Skip to content
Open
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
167 changes: 167 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -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.
45 changes: 45 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
<pybcn@googlegroups.com> 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 <pybcn@googlegroups.com> 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
Expand Down
31 changes: 31 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Loading