oficina / sheets Public

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
Paul Campbell 811f51cd9bda9f7b5274c3ebaf1765df22194bbb parent 511bad5
2 files changed, +32 −11
  • AGENTS.md +3 −1
  • docs/superpowers/specs/2026-08-30-sheets-design.md +29 −10

No CI script — run details

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.*