diff --git a/.githooks/commit-msg b/.githooks/commit-msg new file mode 100755 index 0000000..83a7d65 --- /dev/null +++ b/.githooks/commit-msg @@ -0,0 +1,99 @@ +#!/bin/sh +# +# Enforce the commit message format: +# +# (): [ [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 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 diff --git a/.githooks/pre-commit b/.githooks/pre-commit new file mode 100755 index 0000000..b926b1e --- /dev/null +++ b/.githooks/pre-commit @@ -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 diff --git a/.github/pages-stage.sh b/.github/pages-stage.sh new file mode 100755 index 0000000..25dd631 --- /dev/null +++ b/.github/pages-stage.sh @@ -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" diff --git a/.github/workflows/pages.yml b/.github/workflows/pages.yml new file mode 100644 index 0000000..0822b6f --- /dev/null +++ b/.github/workflows/pages.yml @@ -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 diff --git a/.gitidentity.example b/.gitidentity.example new file mode 100644 index 0000000..d1e73a6 --- /dev/null +++ b/.gitidentity.example @@ -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 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..77508b9 --- /dev/null +++ b/.gitignore @@ -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/ diff --git a/.pagesignore b/.pagesignore new file mode 100644 index 0000000..01020c2 --- /dev/null +++ b/.pagesignore @@ -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 diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..f0ca220 --- /dev/null +++ b/AGENTS.md @@ -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. diff --git a/README.md b/README.md index d4fc292..a456c65 100644 --- a/README.md +++ b/README.md @@ -1 +1,69 @@ -# ARMADA-DN.github.io \ No newline at end of file +# 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 . + +## 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 loads with its styles, and that + and + 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 | diff --git a/docs/adding-content.md b/docs/adding-content.md new file mode 100644 index 0000000..750cb0b --- /dev/null +++ b/docs/adding-content.md @@ -0,0 +1,44 @@ +# Adding content + +How to add to the site without changing its design. Read this before adding a +page or an entry. What each component is, is in [design.md](design.md); how the +copy should read, in [voice.md](voice.md). + +Sections marked (unverified) were found in the site as it stood when this file +was generated: confirm or correct them, then drop the mark. + +## Adding a page + +The pages today: `2026-univr-winterschool.html`, `call.html`, `index.html`. + +_To fill in: which page to copy as the starting point, what to change in its +`` (title, description, metadata), and where to link the new page from._ + +## Adding a `.task` (unverified) + +Repeated 15 times in `call.html`. _To fill in: which elements it needs, in what order, and which are optional._ + +## Adding a `.task-name` (unverified) + +Repeated 15 times in `call.html`. _To fill in: which elements it needs, in what order, and which are optional._ + +## Adding a `.task-leader` (unverified) + +Repeated 15 times in `call.html`. _To fill in: which elements it needs, in what order, and which are optional._ + +## Adding a `.wp-container` (unverified) + +Repeated 5 times in `call.html`. _To fill in: which elements it needs, in what order, and which are optional._ + +## Adding a `.row` (unverified) + +Repeated 5 times in `index.html`. _To fill in: which elements it needs, in what order, and which are optional._ + +## Adding a `.wide` (unverified) + +Repeated 4 times in `index.html`. _To fill in: which elements it needs, in what order, and which are optional._ + +## Adding a `.intro` (unverified) + +Repeated 3 times in `index.html`. _To fill in: which elements it needs, in what order, and which are optional._ + diff --git a/docs/design.md b/docs/design.md new file mode 100644 index 0000000..f53c83b --- /dev/null +++ b/docs/design.md @@ -0,0 +1,71 @@ +# Design + +The site's visual system first, then the components built on it. Read this +before changing markup or styles. + +Lines marked (unverified) were filled in from the site as it stood when this +file was generated: confirm or correct them, then drop the mark. + +## Style guide + +### Stylesheets + +- `styles.css`: the site's own +- `flexboxgrid.min.css`: vendored, replace it and never edit it (unverified) +- `pico.min.css`: vendored, replace it and never edit it (unverified) + +A vendored stylesheet is replaced with a newer release, never edited. Changes go +in the site's own. + +### Custom properties + +- `--space-indigo: #272649ff` in `styles.css` +- `--vintage-grape: #52506dff` in `styles.css` +- `--dusty-grape: #6f58a1ff` in `styles.css` +- `--pacific-blue: #6ab0b7ff` in `styles.css` +- `--spicy-orange: #d74e09ff` in `styles.css` +- `--bright-lemon: #ffeb3bff` in `styles.css` +- `--azure-mist: #e1eff1ff` in `styles.css` +- `--platinum: #f7f9fcff` in `styles.css` +- `--primary-color: var(--space-indigo)` in `styles.css` +- `--primary-color-light: var(--pacific-blue)` in `styles.css` +- `--secondary-color: var(--azure-mist)` in `styles.css` +- `--accent-color: var(--spicy-orange)` in `styles.css` +- `--accent-color2: var(--bright-lemon)` in `styles.css` +- `--accent-color3: var(--platinum)` in `styles.css` +- `--white-color: var(--platinum)` in `styles.css` +- `--text-color: var(--space-indigo)` in `styles.css` +- `--text-color2: var(--dusty-grape)` in `styles.css` +- `--dark-color: var(--space-indigo)` in `styles.css` +- `--dark-color2: var(--vintage-grape)` in `styles.css` + +_To fill in: the colours, type scale and spacing, by the custom property that +holds each one._ + +### Typography + +- `"Montserrat", "Space Grotesk", sans-serif` +- `"Montserrat", "Archivo Black", sans-serif` + +## Components + +Each component: where its markup is used, where it is styled, and what it +exposes as custom properties so a new use can retune it without copying it. + +- `.bottom`: on 3 pages, no styles found (unverified) +- `.col-md-2`: on 3 pages, no styles found (unverified) +- `.col-md-4`: on 3 pages, no styles found (unverified) +- `.col-md-8`: on 3 pages, no styles found (unverified) +- `.col-md-offset-2`: on 3 pages, no styles found (unverified) +- `.col-xs-12`: on 3 pages, no styles found (unverified) +- `.container`: on 3 pages, no styles found (unverified) +- `.eu-logo`: on 3 pages, styled in `styles.css` (unverified) +- `.fas`: on 3 pages, no styles found (unverified) +- `.hero`: on 3 pages, styled in `styles.css` (unverified) +- `.hero-content`: on 3 pages, styled in `styles.css` (unverified) +- `.intro`: on 3 pages, styled in `styles.css` (unverified) +- `.logo`: on 3 pages, styled in `styles.css` (unverified) +- `.row`: on 3 pages, no styles found (unverified) +- `.side`: on 3 pages, styled in `styles.css` (unverified) + +_To fill in: for each component, what it is for and what may be changed._ diff --git a/docs/voice.md b/docs/voice.md new file mode 100644 index 0000000..d15aec0 --- /dev/null +++ b/docs/voice.md @@ -0,0 +1,30 @@ +# Voice + +Who the site is for, and how it speaks to them. Read this before writing or +editing any copy. It covers the words the site shows its readers, not how the +repository's own documentation is written. + +Lines marked (unverified) were filled in from the site as it stood when this +file was generated: confirm or correct them, then drop the mark. + +## Audience + +_To fill in: who reads the site, what they come to do, and what they already +know. The main audience first, then any other._ + +## Tone + +_To fill in: how formal and how technical the copy is, whether the site speaks +as "we", and how it addresses the reader._ + +## Language + +- `en`: 3 page(s) (unverified) + +_To fill in: which language is primary, which pages are translated, and which +spelling the site follows._ + +## Terminology + +_To fill in: the names the site always uses for its own projects, people and +groups, and the terms it avoids._