oficina / home Public

Re-review: the replacement reason was false at the pin too

Third attempt at one bullet. The first reason died at v0.21.0, the
second at v0.22.0, and the second was written by the commit that was
looking for exactly this class of error.

"Write classes because that is what the partials we render alongside
write" is false at the version go.mod pins. The shipped partials
flipped to the attribute spelling at v0.22.0 — rastrillo's design
system §6-v3 stage 2 — and the class spelling in ui/partials/ falls
from 124 occurrences to 6. callout.html at the pin is
`<div rst-callout rst-tone="…">`. The evidence the last pass quoted,
tokens.css's own header saying "the class spelling is what every app
and every shipped partial writes today", is stale upstream: the header
was not updated when the partials flipped, and this file repeated it
as a verified fact.

So the bullet stops giving reasons and rests on §11, which rules the
class vocabulary. Both dead reasons are recorded rather than deleted,
because each looked sound when written and each will be proposed
again. And the re-check instruction now says to count ui/partials/ as
well as ui/tokens.css — checking one and not the other is precisely
how the second reason survived.

The underlying ruling is flagged for Paul rather than decided here.
rastrillo §6-v3 rules one spelling and drops the class selectors
before 1.0; this app has no rst- markup at all yet; and an app writing
classes beside v0.22.0's attribute partials is the mixed codebase §11
wanted to avoid. That is a spec decision, and it is cheap now and
expensive after the first template.

Also: the RP ID bullet named the wrong first door — passkey.Config has
no RP ID field at all, the RP ID is Origin's hostname, so widening
means widening Origin or bypassing the package. LegacyRPID is accepted
for assertions only, never registration. The gate-9 sentence claimed
§13 sequences gate 9 beside the skeleton "which is why" it needs no
database; §13 gives no reason and lists gates 1 and 2 there too.
auth_sessions is no longer written at v0.22.0 — its rows migrated into
the shared sessions table — so the readable address at rest is
auth_links.address and sessions.subject: the leak moved rather than
closed. And this repo was hand-written rather than scaffolded, so
TestVendoredAssetsMatchTheLibrary is a test to add, not to keep.

Merge order: this branch must land with or after
spec/springboard-and-corrections. Landing it first leaves AGENTS.md
disagreeing with main's uncorrected §11, which by this file's own
opening rule makes this file wrong.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Paul Campbell pushed by paul@keymail.dev 04df7693ba2c76ac99b75f0425e73be7edb21b06 parent f9f1d24
1 file changed, +52 −28
  • AGENTS.md +52 −28

CI passed — run details

diff --git a/AGENTS.md b/AGENTS.md
index 9313d5d..4ca8862 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -38,8 +38,7 @@ directory upsert **plus rekey** leaves no readable address anywhere, **WAL
included**; and **no-content-in-logs** (gate 9). **Gates 1 and 2 need a
database and the skeleton has none. Gate 9 needs no database** — it scans
strings, and `cmd/home/main.go` already emits log lines — **so it is not
-sequenced behind one**, which is why §13 step 1 puts it beside the
-skeleton. A guard written after the code it guards is a guard written to
+sequenced behind one**. §13 step 1 lists all three beside the skeleton. A guard written after the code it guards is a guard written to
pass. (Stated as the rule rather than as a status: which gates exist today
is a question for `git log`, and a status line in a binding file is a
sentence that goes stale without anyone noticing.)
@@ -117,11 +116,17 @@ quietly ignored).
is a suffix of the origin's hostname, so `oficina.works` would be
accepted; it is also valid at every host in the zone, the directory and
every team instance included, which collapses the very split the two
- origins exist to make (§4, rulings 2 and 13). The widening has a second
- door: `passkey.Config.LegacyRPID` is passed straight through to
- `webauthn.Config` as a second accepted RP ID, so it must stay empty
- unless a rename drill (§5.7) is actually running, and then hold a full
- host. Both halves need the test, because both are accepted silently.
+ origins exist to make (§4, rulings 2 and 13; the host itself is ruling
+ 16). Note where the first door actually is: `passkey.Config` has no RP ID
+ field — the RP ID is always `Origin`'s hostname — so the way to widen it
+ is to widen `Origin`, or to bypass `passkey` and build a
+ `webauthn.Config` directly. The second door is
+ `passkey.Config.LegacyRPID`, passed straight through as an RP ID accepted
+ **for assertions only** (registration never takes it), so it must stay
+ empty unless a rename drill (§5.7) is actually running, and then hold a
+ full host. The test asserts both: `Origin`'s hostname is exactly
+ `id.oficina.works`, and `LegacyRPID` is empty or a full host. Both are
+ accepted silently, which is why neither can rest on care.
- Assertions carry **identity and display preferences, never
authorization**. The boundary test (§5.4) is **two-sided**: a claim may
ride the assertion only if **(a)** the instance makes no trust decision
@@ -164,7 +169,10 @@ quietly ignored).
any delete. `PRAGMA secure_delete` is on.
- **The session Subject is an opaque person ref, never an address.**
`rastrillo/auth` as shipped sets `Subject: id.Address` and stores readable
- addresses in `auth_links` and `auth_sessions` — verified at source, filed
+ addresses in `auth_links.address` and, via `Subject: id.Address`,
+ in `sessions.subject` — `auth_sessions` is no longer written at v0.22.0,
+ its rows having been migrated into the shared `sessions` table, so the
+ leak moved rather than closed. Verified at source, filed
upstream as `rastrillo/rastrillo` #2. We hand-roll session minting until
that lands, and use `auth` only for address classification and the Keymail
upgrade, which hold nothing.
@@ -177,30 +185,46 @@ quietly ignored).
cannot grep.
**Frontend**
-- Write `class="rst-*"` — but **not for the reason this file used to
- give**. The claim was that the shipped `ui/tokens.css` has zero bare
- `rst-*` attribute selectors. At v0.22.0, the version `go.mod` pins, it has
- **130 distinct ones**, deliberately. tokens.css says why in its own header:
- "TWO SPELLINGS, ON PURPOSE, FOR ONE RELEASE. Every component rule below
- names its class and its attribute twin: `.rst-btn, [rst-btn]` … The
- attribute spelling is the vocabulary this library is moving to (design spec
- §6-v3); the class spelling is what every app and every shipped partial
- writes today." So the rule stands and the justification does not: write
- classes because that is what the partials we render alongside write, not
- because the attribute grammar is unreleased. **Re-check this when the pin
- moves, and name the version you checked.** Note what actually falsified
- the old claim, because this file got that wrong too: the twins arrived in
- **v0.21.0**, which is the version this repo's *first* `go.mod` pinned. The
- claim was verified on 2026-08-30 against a rastrillo that had 0 bare
- selectors and 528 class occurrences — true then, and already false by the
- time the skeleton existed. The later bump to v0.22.0 changed 130 to 130
- and falsified nothing.
+- Write `class="rst-*"`, **because spec §11 rules it — and for no other
+ reason, because this file has now given two and buried both.** Record the
+ graveyard, so nobody revives either:
+ - *"The attribute grammar is ruled but not released."* Dead since
+ **v0.21.0**, this repo's *first* pin. tokens.css there pairs every
+ class with an attribute twin and says so in its own header: "TWO
+ SPELLINGS, ON PURPOSE, FOR ONE RELEASE." The original claim — zero bare
+ `rst-*` selectors against ~528 class occurrences — was verified on
+ 2026-08-30 against v0.20.x, was true then, and was already false by the
+ time the skeleton existed. No later bump falsified it.
+ - *"The partials we render alongside write classes."* Dead at
+ **v0.22.0**, the current pin, where the shipped partials flipped to the
+ attribute spelling — rastrillo's design-system §6-v3 stage 2. Counted
+ in `ui/partials/` with template comments stripped, the class spelling
+ falls from **124 occurrences at v0.21.0 to 6 at v0.22.0**, replaced by
+ the attribute spelling. (Count the class spelling, not the attribute
+ one: `class="rst-…"` is unambiguous, while attribute-spelling totals
+ move with how you treat valued attributes like `rst-tone="…"`, and two
+ careful counts of it disagreed.) `callout.html` at the pin
+ is `<div rst-callout rst-tone="…">`. tokens.css's header still claims
+ "the class spelling is what every app and every shipped partial writes
+ today" — **that sentence is stale upstream**, and this file quoted it
+ as evidence until it was checked.
+ So the rule now rests on §11 alone, and §11's own reason is due a
+ re-ruling: rastrillo §6-v3 rules "one spelling, not two" and drops the
+ class selectors before 1.0, this app has no `rst-` markup yet, and an app
+ writing classes beside v0.22.0's partials *is* the mixed codebase §11
+ wanted to avoid. Flagged for Paul; until he rules, §11 stands.
+ **Re-check on every pin move, name the version you checked, and count
+ both `ui/tokens.css` and `ui/partials/`** — checking one and not the
+ other is how the second reason survived a pass that was looking for
+ exactly this.
- **`rastrillo doctor` has landed** — in v0.21.0, the first version this
repo pinned. "Adopt it the day it lands" was a trigger that had already
fired when it was written. When this app vendors `static/`, run
`rastrillo doctor --fix` on each upgrade rather than re-copying by hand,
- and keep the `TestVendoredAssetsMatchTheLibrary` that `rastrillo new`
- scaffolds as the standing gate. There is no vendored `static/` in the
+ and add the `TestVendoredAssetsMatchTheLibrary` that `rastrillo new`
+ scaffolds as the standing gate — "add", not "keep": this repo was
+ hand-written, not scaffolded, so it has no `internal/hometest/` and no
+ copy of that test. There is no vendored `static/` in the
tree today — `internal/home/page.go` links no stylesheet, deliberately —
so this is an instruction with no object until the first asset arrives.
- Zero JS. **No surface here has any reason to require script**, and the day