diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 00000000..943307ef --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,167 @@ +# AGENTS.md + +Guidance for AI coding agents working on pybcn.org, the website of Associació +Python Barcelona. + +Read [CONTRIBUTING.md](CONTRIBUTING.md) as well. This file covers only what an +agent gets wrong that a human would not, and what this repository does +differently from a default Hugo site. + +## Build and run + +```sh +bin/install # downloads the pinned Hugo binary, verifies its SHA256 +bin/serve # dev server, Hugo's default :1313 +bin/build # writes to docs/ +bin/hugo --quiet -d /tmp/out # the binary directly +``` + +**The Hugo binary lives at `bin/hugo` and is gitignored.** `bin/helpers` prefers +it over anything on `$PATH`, and checks the version from `.hugo-version`. + +**Do not `pip install hugo`.** That PyPI package now requires a Go toolchain and +fails. `bin/install` fetches the official release binary instead. + +A clean build produces **109 HTML pages**: 53 Hugo-generated and 56 copied +from `static/archives/`. Recount rather than trusting this line: the number +moves with the content. It was 317 until #202 stopped publishing the 206 +person and sponsor pages that nothing linked to, which is how quickly a +figure like this goes stale. + +## Traps that have already cost time + +**TOML key placement.** `config.toml` ends with nested `[params.social_items...]` +tables. A key appended at the end of the file lands **inside the last table** and +silently does nothing. Put top-level keys near `theme =`, and verify by building +and checking the output, not by reading the file. + +**`merge.ff = only`.** Any local merge needs `--no-ff`. A failed merge here reports +"Not possible to fast-forward", which is not a conflict. + +**`unsafe=true` in `[markup.goldmark.renderer]`.** Raw HTML in front matter +and page bodies renders unescaped. Two content files still carry it, +`content/events/pyday_bcn/pyday_bcn_2018.md` and `pyday_bcn_2019.md`, which +are pre-Hugo programmes pasted in as tables. So **assume any content field +can inject HTML**, and never add a field that reaches a template through +`safeHTML`. `bin/check-html-safety` rejects dangerous markup in content and +`bin/check-rendered` checks the built pages; both run on every pull request. + +**`resources/_gen/` and `docs/` are build output and are gitignored.** They +used to be tracked, so a build dirtied the tree and a pull request carried +hundreds of regenerated files. If `git status` shows either of them, you are +on a branch from before that change. + +**`master` is not a source branch.** It is the built site, force-pushed as an +orphan commit by the deploy workflow. The default branch is **`edition`**. + +**Images in `static/` cannot be processed by Hugo.** They must be under `assets/` +for `resources.Get` and `.Resize` to work. This is why the site once shipped 8.97 +MB of photographs on a single page. + +## Rules that are not style preferences + +These come from decisions with reasons behind them. Breaking them creates a +problem somebody else has to find. + +**Never store a role that can be computed.** `speaker` and `host` are derived from +the talk and meetup records. Writing them into a person's front matter guarantees +they will eventually contradict the data. + +**A person attribution requires human verification.** Talk records carry +`source` and `confidence`. **Only a human-verified record may create a person page +or link a speaker name.** A record with `confidence: medium` or `low` renders the +talk with an "unconfirmed" marker and leaves the speaker name as plain text. + +This is not a style rule. Publishing an unverified claim about a named person +fails the accuracy principle of the GDPR, and the confidence field then documents +that we knew. + +**Never merge two people on a first-name match.** The existing ids include bare +first names (`david`, `alberto`, `jordi`, `ricardo`) that already collide with +fuller names on the site. When in doubt, leave both and flag it. + +**Do not promote hosting to a role.** Meetup's `eventHosts` field is a tool +attribute, not a title. Hosting one meetup is not being an organizer. + +**RSVPs are not attendance.** `attendedCount` is zero on 205 of 207 events, so the +only figure available is who said yes on Meetup. Render it as registrations. + +**No personal data beyond what is already published.** Never render email +addresses, phone numbers, or social handles extracted from old event +descriptions, even though they are present in the source data. Never derive a +person's attributes from the event they spoke at. + +**Person ids** are `firstname-surname`, kebab-case, accents stripped. A rename +carries an `aliases` entry **and** updates every reference in `content/events/` in +the same commit. Skipping the second half silently drops the person from those +pages, and that bug has been live on this site. + +## Commits + +**No AI attribution.** No `Co-Authored-By` trailer, no generated-with footer, no +model byline. Commits read as authored by the person who made them. + +**Never use the em dash character (U+2014).** Use a comma, a colon, parentheses, or +restructure. + +Write in plain, factual English. Say what changed, why, and what you verified. +**If part of the work is unfinished, say so in the commit message**, with what +blocks it. A commit that claims more than it did is worse than one that admits a +gap. + +## Verify, do not assert + +This repository has a history of changes that looked right and were not. The +build succeeding proves very little: a missing sponsor page, a person id that does +not match its filename, and a broken image URL all build cleanly. + +So: + +- Build and grep **the generated HTML**, not the templates. +- Give before and after numbers for anything you claim to have fixed. +- When you cannot verify something without a browser or a screen reader, **say + that** rather than asserting it works. + +Two checkers exist and both are cheap to run: + +```sh +bin/check-content # front matter, ids, cross-references (needs pyyaml) +bin/check-html-safety # dangerous raw HTML in content +``` + +## Things you cannot do + +**Do not push, open pull requests, or comment on GitHub.** Leave your work as a +local branch. Opening it is a human decision, taken per item. + +**Do not change DNS, repository settings, or anything outside the working tree.** + +**Do not install software.** If a task genuinely needs a tool that is absent, say +so and stop, rather than working around it with something that produces a +plausible but different result. + +## Where things are + +| Path | What | +|---|---| +| `content/people/` | One markdown file per person, keyed by `id` | +| `content/events/` | PyDay BCN, PyDataBCN, and other events, with their agendas in front matter | +| `content/sponsors/` | One file per sponsor | +| `themes/pybcn_theme/` | The theme, **vendored in-tree**, not a submodule | +| `themes/pybcn_theme/layouts/partials/` | Most of the interesting template logic | +| `static/archives/` | Verbatim mirrors of pre-Hugo sites, copied unprocessed | +| `data/` | Tables the templates read: `roles.yaml` maps a heading to a role, `role_emoji.yaml` a role to a glyph | +| `bin/` | Setup and build scripts, plus the gitignored Hugo binary | +| `.hugo-version` | The pinned Hugo version, single source of truth | + +The checks, which are what a pull request has to pass: + +| Script | What it rejects | +|---|---| +| `bin/check-content` | A broken reference, a photo that is not square, a source far larger than the build asks for, a field that no longer exists | +| `bin/check-html-safety` | Dangerous raw HTML in content, with an allowlist for the legitimate embeds | +| `bin/check-rendered` | The same, on the built pages, plus any `http://` URL. Run it against a directory you built, never against `public/` while a dev server is writing there | + +The join between a person and an event is `where $pages ".Params.id" "eq" $person` +in `partials/people-grid.html`. That pattern is how everything cross-references, +and it fails silently when an id does not match. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 152a533f..e6b672f4 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -37,6 +37,51 @@ the source. `master` holds the built site: the `github-pages` workflow writes it on every merge to `edition` and replaces its history each time, so nothing is edited there, and a change made on `master` is lost on the next deploy. +## Your own card + +**You do not need Git, GitHub, or any of the rest of this file.** + +If you have spoken at one of our events, or you organise with us, you may have +a card on the [organizers page](https://pybcn.org/pybcn_association/organizers/) +or on the page of the event. Clicking it opens what the site holds about you. +To change any of it, or to have it removed, **send an email to + saying what to change**. That is enough. We do not ask +for identity documents to correct or remove your own name. + +If you would rather do it yourself, every card is a file under +`content/people/`, named after the person. Click the pencil icon on GitHub, +change the text, and GitHub walks you through opening a pull request. + +### What is on your card + +Your name, the role you held, and the events you appeared at. The list of +events is computed from the pages that list you, so it is not edited on your +card: if an event is missing or wrong, tell us and we fix the event page. + +Everything else is yours to give or withhold: + +| Field | What it does | +|---|---| +| `photo` | A square image, under `themes/pybcn_theme/assets/images/people/` | +| `linkedin`, `github`, `twitter`, `site` | Links under your name | +| the body of the file | The bio, one paragraph or several | + +Whoever did not want a photo does not have one, and the card works without it. + +### If you would rather not appear + +Write to and we remove the card. We will not argue +about it. + +Be aware of what removal can and cannot do, because this is a public +repository. We remove the card from the site and from the build, and we ask +search engines and the Internet Archive to drop their copies. We cannot remove +it from the Git history, and we cannot remove copies other people have already +made. + +If that is not acceptable to you, tell us **before** we publish. We would +rather not publish a card than publish one we cannot properly withdraw. + ## Add content without writing code Most changes to this site are content: a person, a sponsor, an event. Each one diff --git a/README.md b/README.md index d606f222..909dc6e0 100644 --- a/README.md +++ b/README.md @@ -418,6 +418,37 @@ people_sections: - josep ``` +## Decisions, and where each one is written down + +This repository explains itself in the file that carries the decision, not in +a folder of documents beside the code. That keeps the reason next to the thing +it governs, and it means the reason is in front of whoever is about to change +it. The cost is that the reasons are scattered, so this is the index. + +| Decision | Where it is argued | +|---|---| +| A person file builds no page, and `/people/` is a 404 | The `[[cascade]]` comment in `config.toml`, and issue #201 | +| An appearance line calls a page by `appearances_title`, not by its title | `themes/pybcn_theme/layouts/partials/appearances_index.html` | +| The modal is on its way to being a page per person, so its content cannot depend on the page it was opened from | `themes/pybcn_theme/layouts/partials/person_appearances.html` | +| The menu sections are a disclosure, not an ARIA menu | `themes/pybcn_theme/layouts/partials/nav.html` | +| MIT for the code, CC BY-SA 4.0 for the content, and nothing for the photographs | [LICENSING.md](LICENSING.md) | +| `edition` is the source branch and `master` is the built site | [AGENTS.md](AGENTS.md) | +| Raw HTML in content is gated by three checks rather than banned | [CONTRIBUTING.md](CONTRIBUTING.md) and `bin/check-html-safety` | + +One decision has no file of its own, because it is a decision not to act: + +**The heavy image blobs stay in the Git history.** The repository is 139 MB, +and the largest objects in it are photographs that were replaced long ago, +up to 5.9 MB each. They are all reachable from `edition` and none from +`master`, which the deploy workflow force-pushes as a single orphan commit. +Removing them means `git filter-repo` and a force-push, which changes every +commit hash from the first touched blob onwards: every clone breaks, every +open pull request has to be rebased, and every link to a commit or to a line +of code goes dead, including the ones in our own issues. GitHub starts warning +at 1 GB. The cost is a one-time clone, not a recurring one, so the answer is +not now. If it is ever done, the moment is an empty pull request queue and a +day when everybody can re-clone together. + ## Licence The code of the site (the templates, the stylesheets, the scripts, the