Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
99 changes: 99 additions & 0 deletions .githooks/commit-msg
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
#!/bin/sh
#
# Enforce the commit message format:
#
# <type>(<scope>): <subject>[ [BOT]]
#
# type feat fix docs refactor perf test build ci chore style content
# scope optional, lowercase letters, digits and . _ / -
# subject imperative, lowercase first letter, no trailing full stop
#
# The subject line must be 72 characters or fewer. A trailing " [BOT]" marks a
# commit written by an agent rather than by hand. It is optional, because this
# hook checks hand-written commits too, but when it is present it must be
# exactly that, at the end, and the commit must carry no attribution trailer.
#
# Enable once per clone:
# git config core.hooksPath .githooks

# Byte-wise matching, so [a-z] means ASCII a to z in every locale.
LC_ALL=C
export LC_ALL

# Git runs this hook before it cleans the message up, so with "git commit -v"
# the file still holds the diff below the scissors line. Check the message
# only, without comments or the leading blank lines git would also drop.
msg=$(sed -e '/^# -* >8 -*$/,$d' -e '/^#/d' "$1" | sed '/./,$!d')
subject=$(printf '%s\n' "$msg" | head -n 1)

# The way out is for people only: an agent's commit is the one this hook
# exists to hold to the format, and the git guard denies it --no-verify anyway.
fail() {
echo "commit-msg: $1" >&2
echo " $subject" >&2
[ -n "$2" ] && printf '%s\n' "$2" >&2
echo "A hand-written commit can keep its message with git commit --no-verify," >&2
echo "which skips this check and pre-commit, for that one commit only." >&2
echo "An agent fixes the message instead." >&2
exit 1
}

# Merges, reverts and fixups are generated by git itself; leave them alone.
case "$subject" in
Merge\ *|Revert\ *|fixup!\ *|squash!\ *|amend!\ *) exit 0 ;;
esac

# dash (the /bin/sh on Debian and Ubuntu) counts bytes, so an accented letter
# would cost two. Dropping UTF-8 continuation bytes leaves one byte per character.
length=$(printf '%s' "$subject" | tr -d '\200-\277' | wc -c | tr -d ' ')
[ "$length" -gt 72 ] && fail "subject line is $length characters, the limit is 72"

case "$subject" in
*[[:space:]]) fail "subject line ends in whitespace" ;;
esac

# The marker, if present, is exactly " [BOT]" and the last token. Anything
# resembling it elsewhere is a mistake, not a variant.
bot=
case "$subject" in
*' [BOT]') bot=1; body=${subject% \[BOT\]} ;;
*) body=$subject ;;
esac
# Two spaces before the marker leave one behind, which the pattern below accepts.
case "$body" in
*[[:space:]]) fail "subject line ends in whitespace" ;;
esac
if printf '%s' "$body" | grep -iq '\[ *bot *\]'; then
fail "the [BOT] marker must be the literal ' [BOT]' at the end of the subject"
fi

pattern='^(feat|fix|docs|refactor|perf|test|build|ci|chore|style|content)(\([a-z0-9._/-]+\))?: [a-z].*[^.]$'
if ! printf '%s' "$body" | grep -Eq "$pattern"; then
fail "subject line does not match <type>(<scope>): <subject>" \
" type feat fix docs refactor perf test build ci chore style content
subject imperative, lowercase first letter, no trailing full stop"
fi

description=${body#*: }
first=${description%% *}
case "$first" in
added|adds|fixed|fixes|updated|updates|removed|removes|changed|changes|\
renamed|renames|moved|moves|improved|improves|refactored|refactors|\
created|creates|deleted|deletes|documented|documents)
fail "'$first' is not imperative; write 'add', not 'added'" ;;
esac
case "$description" in
update|updates|wip|fix|fixes|changes|misc|stuff|tmp|temp|minor)
fail "'$description' is a placeholder; say what changed" ;;
esac

# A body, if any, is separated from the subject by one blank line.
second=$(printf '%s\n' "$msg" | sed -n '2p')
[ -n "$second" ] && fail "leave the second line blank"

if [ -n "$bot" ] && printf '%s\n' "$msg" |
grep -Eiq '^co-authored-by:|generated with \[?claude'; then
fail "agent commits carry [BOT] and no attribution trailer"
fi

exit 0
38 changes: 38 additions & 0 deletions .githooks/pre-commit
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
#!/bin/sh
#
# Refuse a commit that adds a machine-local absolute path: where this clone
# lives, or the committer's home directory. The path exists on one machine
# only, so to everyone else it is a dangling reference, and it publishes the
# account name it contains besides. Write paths relative to the repository
# root instead.
#
# Only added lines are checked, so a file that already carries such a path can
# still be edited. Where content genuinely has to hold one, commit it by hand
# with --no-verify.
#
# Enable once per clone:
# git config core.hooksPath .githooks

root=$(git rev-parse --show-toplevel) || exit 1

# A home of / (some containers) would match every line, so it is skipped.
home=${HOME:-}
[ "$home" = / ] && home=

found=$(git diff --cached --no-color --no-ext-diff -U0 --diff-filter=ACMR |
awk -v root="$root" -v home="$home" '
/^\+\+\+ / { file = substr($0, 7); next }
/^\+/ {
line = substr($0, 2)
if (index(line, root) || (home != "" && index(line, home)))
printf " %s: %s\n", file, substr(line, 1, 100)
}')

if [ -n "$found" ]; then
echo "pre-commit: staged lines add a machine-local absolute path" >&2
printf '%s\n' "$found" >&2
echo "Write it relative to the repository root instead." >&2
exit 1
fi

exit 0
22 changes: 22 additions & 0 deletions .github/pages-stage.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#!/bin/sh
# Stage the site for GitHub Pages: copy the repository into _site/, leaving out
# what .pagesignore lists, then fail if a doc asset from the README's asset
# table would be published anyway.
set -eu

[ -f .pagesignore ] || { echo "pages-stage: no .pagesignore" >&2; exit 1; }
[ -f README.md ] || { echo "pages-stage: no README.md, so no asset table to check" >&2; exit 1; }

rm -rf _site
mkdir _site
rsync -a --exclude-from=.pagesignore ./ _site/

status=0
for path in $(awk -F'|' '$3 ~ /^ *doc *$/ { gsub(/[ `]/, "", $2); print $2 }' README.md); do
if [ -e "_site/$path" ]; then
echo "pages-stage: $path is a doc asset in README.md but would be published;" \
"add it to .pagesignore" >&2
status=1
fi
done
exit "$status"
37 changes: 37 additions & 0 deletions .github/workflows/pages.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# Publishes the site to GitHub Pages on every push to main. The Pages
# source must be set to "GitHub Actions" in the repository settings.
# .github/pages-stage.sh decides what is published: everything .pagesignore
# does not list, hidden files included (.git and .github never are).
name: Pages

on:
push:
branches: [main]
workflow_dispatch:

permissions:
contents: read
pages: write
id-token: write

concurrency:
group: pages
cancel-in-progress: false

jobs:
deploy:
runs-on: ubuntu-latest
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
steps:
- uses: actions/checkout@v7
- uses: actions/configure-pages@v6
- name: Stage the site
run: sh .github/pages-stage.sh
- uses: actions/upload-pages-artifact@v5
with:
path: _site
include-hidden-files: true
- id: deployment
uses: actions/deploy-pages@v5
28 changes: 28 additions & 0 deletions .gitidentity.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# Commit identity for this repository.
#
# Copy this file to .gitidentity, fill in your own details, and wire it up once
# per clone:
#
# cp .gitidentity.example .gitidentity
# git config --local include.path ../.gitidentity
#
# This is a git config file, so git reads it natively; there is no script in
# between. The include path is relative to .git/, which is why it starts with
# ../ and why the include line itself lives in .git/config rather than here.
#
# .gitidentity is gitignored: it is a per-person setting, and cloning the
# repository must not hand you somebody else's address or signing key.
#
# Find your key id with: gpg --list-secret-keys --keyid-format=long
# Inspect the last entry: git log -1 --show-signature

[user]
name = Your Name
email = you@univr.it
signingkey = 0123456789ABCDEF

[commit]
gpgsign = true

[tag]
gpgsign = true
20 changes: 20 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Per-person commit identity; see .gitidentity.example
.gitidentity

# Local agent configuration and scratch space
CLAUDE.md
.claude/
.temp/

# macOS Finder metadata, written into any directory the Finder opens
.DS_Store

# Build output
dist/
.cache/

# Node, when a build step exists
node_modules/

# Build output, and the site as staged for GitHub Pages
_site/
17 changes: 17 additions & 0 deletions .pagesignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Left out of the published site by .github/pages-stage.sh, one rsync pattern
# per line. A leading / anchors a pattern at the repository root.
#
# Every row of kind "doc" in the README's asset table must be matched here: the
# stage script fails the deploy when one would be published anyway.
/.git/
/.github/
/.githooks/
/.agent-defs/
/.temp/
/_site/
/docs/
/README.md
/AGENTS.md
/.gitignore
/.gitidentity.example
/.pagesignore
39 changes: 39 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# AGENTS.md — ARMADA site

Static GitHub Pages website for the ARMADA doctoral network on reliable conversational data exploration, served at armada-dn.eu.

## Core rules

These are binding. They override any general default behaviour.

1. **Never touch anything outside this repository.** Not sibling repositories,
not `$HOME` dotfiles, not system paths, not global installs. Reading outside
it is not allowed either without asking first.
2. **Never `git commit` without explicit permission**, every time. "The change
is finished" is not permission.
3. **Check the branch before starting; never switch without a yes.** If the
work belongs on another branch or a new one, propose the switch and wait.
4. **Never stage in bulk.** No `git add -A`, `git add .`, `git add -u`,
`git commit -a`. Enumerate paths.
5. **One commit per topic**, and every commit an agent authors ends its subject
line with `[BOT]`. Never a commit called "update".
6. **`.temp/` is gitignored scratch space.** Read it, ask before writing, never
overwrite without a named yes, never commit it, and never name its paths in a
committed file.
7. **Size-check before reading any file.** A large generated file saturates the
context window and blocks the user's work.
8. **Never start a long-running or destructive operation on your own
initiative.** Describe the command and let the user run it.
9. **Ask instead of investigating, when asking is cheaper**, and be brief.

## Other instruction files

Coding agents keep their instructions in files of their own. Look for each of
these in the repository root and read every one you find:
`CLAUDE.md`, `.claude/rules/`, `GEMINI.md`, `.cursor/rules/`, `.cursorrules`, `.github/copilot-instructions.md`, `.windsurfrules`, `.agent-defs/`. The rules above are the floor. Where two rules
differ only in how strict they are, follow the stricter one. Where they cannot
both be followed, stop and ask which one applies.

## Project guides

The guides in `docs/` say how the site is built and written: `voice.md` before writing or editing copy, `design.md` before changing markup or styles, `adding-content.md` before adding content.
70 changes: 69 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,69 @@
# ARMADA-DN.github.io
# ARMADA-DN.github.io

Website of the ARMADA doctoral network, Reliable Conversational Data
Exploration, served at [armada-dn.eu](https://armada-dn.eu).

Hand-written HTML and CSS with no build step.

## Preview locally

Pages link their assets with root-relative paths (`/styles.css`), so serve the
repository root rather than opening the files directly:

```sh
python3 -m http.server 8000
```

Then open <http://localhost:8000/>.

## Deployment

**Status (2026-10-01): moving from "Deploy from a branch" to "GitHub Actions".**
Until the switch below is done, `main` is still published as-is, including the
files listed as `doc` in the table below.

Once switched, every push to `main` runs `.github/workflows/pages.yml`, which
stages the site with `.github/pages-stage.sh`: everything is copied into
`_site/` except what `.pagesignore` lists, and the deploy fails if a `doc` asset
below would be published anyway. A push to `main` is a deployment.

### Switching the Pages source

1. Merge the branch that adds `.github/workflows/pages.yml` into `main` only
when ready to do steps 2 and 3 straight after.
2. In the repository on GitHub: **Settings → Pages → Build and deployment →
Source**, choose **GitHub Actions**.
3. On the same page, check that **Custom domain** still reads `armada-dn.eu`
and that **Enforce HTTPS** is on. With Actions the domain comes from this
setting; the `CNAME` file is kept but no longer decides it.
4. **Actions → Pages → Run workflow** on `main`, or push to `main`, and wait
for the deploy to go green.
5. Check that <https://armada-dn.eu/> loads with its styles, and that
<https://armada-dn.eu/AGENTS.md> and <https://armada-dn.eu/docs/voice.md>
now return 404.
6. Update the status line above.

To go back, set the source to **Deploy from a branch**, `main`, `/ (root)`.

## Assets

Every path in the repository root is either published as part of the site or
is a `doc` asset that describes it. Each `doc` row must be matched in
`.pagesignore`.

| Path | Kind | What |
| - | - | - |
| `index.html`, `call.html`, `2026-univr-winterschool.html` | site | Pages |
| `styles.css` | site | The site's own styles |
| `pico.min.css`, `flexboxgrid.min.css` | site | Vendored, replaced, never edited |
| `logos/`, `images/`, `armada-logo.png`, `euflag.png` | site | Images |
| `redirects/` | site | Short paths to external URLs |
| `CNAME`, `LICENSE` | site | Domain record, licence |
| `README.md` | doc | This file |
| `AGENTS.md` | doc | Rules for coding agents |
| `docs/` | doc | Voice, design and content guides |
| `.github/` | doc | Pages workflow and stage script |
| `.githooks/` | doc | Commit hooks |
| `.gitignore` | doc | Git ignore list |
| `.gitidentity.example` | doc | Commit identity template |
| `.pagesignore` | doc | What the deploy leaves out |
Loading
Loading