Corrections from the home spec's review
Three fixes, all surfaced by the Fable review of the sibling spec. Section 9.1's call-2 row still said the instance carries HMAC(pepper, address), contradicting section 9.6 of the same document, which had already ruled that instances never hold the pepper. An implementer reading the contract table would have built instance-side hashing — the design 9.6 rejects. That correction exposed a deeper one. The rekey journal was keyed on hashes, and an instance holds no hashes and can compute none, so it would have matched nothing it had. Call 2 now returns a person ref, which is also the assertion's subject — one identifier linking assertion to grant to journal — and journal retention is a per-consumer TTL rather than a slowest-cursor rule that never completes in a hibernating fleet. And the attribute-selector claim was imprecise. Zero bare rst-* attribute selectors is the load-bearing part and unchanged, but tokens.css does carry [data-tone=], [aria-*] and [type=], so "zero attribute selectors" would not have survived being checked. Re-verified post design-system v2 rather than left at a number from an earlier read.
Reviewed-By: Fable (dispatched; see spec status line) Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01APtNStXDZ5H2fAc1ZuGqtS
2 files changed,
+32
−11
AGENTS.md+3 −1docs/superpowers/specs/2026-08-30-sheets-design.md+29 −10
No CI script — run details
Intent
diff --git a/AGENTS.md b/AGENTS.md| index e87df07..6822a46 100644 |
| --- a/AGENTS.md |
| +++ b/AGENTS.md |
| @@ -143,7 +143,9 @@ relying on remembering one, make it a test instead. |
| - No build step. Jspreadsheet CE is vendored as a static asset, not fetched |
| from a CDN and not bundled. |
| - **Write `class="rst-*"` today.** The shipped `ui/tokens.css` has zero |
| - attribute selectors and 382 class selectors; the attribute grammar is |
| + bare `rst-*` attribute selectors against ~528 `.rst-` class selector |
| + occurrences (it does carry `[data-tone=]`, `[aria-*]` and `[type=]`, so |
| + "zero attribute selectors" was imprecise); the attribute grammar is |
| ruled but *not started*, and its staged path ships dual-matching CSS |
| first. Writing `rst-btn="primary compact"` now renders unstyled against a |
| frozen `static/tokens.css`. Run the codemod when the dual-grammar release |
diff --git a/docs/superpowers/specs/2026-08-30-sheets-design.md b/docs/superpowers/specs/2026-08-30-sheets-design.md| index 088b680..825a28f 100644 |
| --- a/docs/superpowers/specs/2026-08-30-sheets-design.md |
| +++ b/docs/superpowers/specs/2026-08-30-sheets-design.md |
| @@ -494,9 +494,16 @@ Sheets depends on. |
| 1. **Sign-in and session minting** (§8.1 — the home is the identity |
| authority, not merely a directory). |
| -2. **Directory upsert/revoke** — instance→home on grant change, carrying |
| - `HMAC(pepper, lowercased email) → (app, team sqid, host, kind)` where |
| - `kind` is member or guest. |
| +2. **Directory upsert/revoke** — instance→home on grant change, sending the |
| + **address and the team's sealed display name**; **the home hashes** and |
| + **returns the person ref**, which we store against our member rows. |
| + Carries `kind` (member or guest) and a team-scoped revoke. |
| + |
| + *Corrected 2026-08-30: this row previously read "carrying |
| + `HMAC(pepper, …)`", contradicting §9.6 of this same document. An |
| + implementer reading the contract table would have built instance-side |
| + hashing — which needs the pepper on every instance, the design §9.6 |
| + rejects.* |
| 3. **Directory list** for a signed-in person — every team across every |
| suite app. |
| 4. **Provisioning enqueue** (§9.4 — and see the doctrine divergence there). |
| @@ -624,10 +631,18 @@ The directory is keyed on `HMAC(pepper, lowercased email)`. |
| a cursor-based rekey journal that instances pull, carrying `old_hmac → |
| new_hmac`. The home learns both addresses transiently and stores neither. |
| -**Open, and the home spec resolves it:** whether instances hold readable |
| -addresses at all. They need one to render "shared with alice@example.com"; |
| -they no longer need one for auth, since §8.1 moved sign-in to the home. That |
| -question decides whether the journal must also carry the new address. |
| +**Resolved by the home spec, 2026-08-30: instances hold readable addresses; |
| +the home never does.** The sharer typed the address, so the team already |
| +knows it — storing it in that team's instance leaks nothing the team lacks, |
| +whereas the home holding every address is the aggregation risk. It also |
| +keeps share notification possible, since nobody can email a hash. |
| + |
| +**The journal keys on person refs, not hashes.** An instance holds no hashes |
| +and can compute none, so a hash-keyed journal would have matched nothing it |
| +had. The ref arrives in call 2's response and is also the assertion's `sub`, |
| +so one identifier links assertion to grant to journal. Rows carry the sealed |
| +new address alongside it, scoped per consumer, with a per-consumer retention |
| +TTL rather than a slowest-cursor rule. |
| **The pepper is a suite-wide secret with per-team blast radius, and that is |
| the wrong shape.** Call 2 has the *instance* compute the HMAC, so every |
| @@ -699,9 +714,13 @@ window. |
| ### 10.1 Markup: classes now, attributes after the dual-grammar release |
| **The first draft got this backwards and it would have shipped an unstyled |
| -app.** Verified against `origin/main`: the shipped `ui/tokens.css` contains |
| -**zero attribute selectors and 382 class selectors**, and the rake line |
| -`::after` is still live. The attribute grammar is "RULED, **not yet |
| +app.** Verified against `origin/main` post-design-system-v2 (2026-08-30): |
| +`ui/tokens.css` contains **zero bare `rst-*` attribute selectors** against |
| +~528 `.rst-` class selector occurrences, and the rake line `::after` is |
| +still live. (It *does* carry attribute selectors for `[data-tone=]`, |
| +`[aria-*]` and `[type=]` — an earlier wording said "zero attribute |
| +selectors", which was imprecise and would not survive being checked. The |
| +conclusion is unchanged.) The attribute grammar is "RULED, **not yet |
| started**", and its staged path is explicitly *ship a `tokens.css` matching |
| both spellings → codemod → drop the class selectors.* |