Ruling 21: one named carve-out for the passkey ceremony
§11 said "no surface here has any reason to require script, and the day one does is the day this section is wrong". That day arrived: a WebAuthn ceremony is navigator.credentials, script by construction, and §5.2 requires passkeys. The sentence was right to be written as a tripwire and right to fire. Paul ruled the carve-out rather than either quietly widening the rule or dropping the second factor. rastrillo ships the client half already — webauthn.JS() embeds js/webauthn.mjs — so nothing needs writing and the only question was whether one screen may require script. Scoped to enrolment and ceremony. Magic-link sign-in, recovery-code redemption and the springboard stay scriptless, so script-off keeps a working sign-in and loses only an optional second factor, which ruling 15 already made optional. The no-script test gets an explicit allowlist rather than an exemption: a rule with a list is checkable.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
1 file changed,
+18
−3
docs/superpowers/specs/2026-08-30-home-design.md+18 −3
CI passed — run details
diff --git a/docs/superpowers/specs/2026-08-30-home-design.md b/docs/superpowers/specs/2026-08-30-home-design.md| index cbeb7e7..f9a6b69 100644 |
| --- a/docs/superpowers/specs/2026-08-30-home-design.md |
| +++ b/docs/superpowers/specs/2026-08-30-home-design.md |
| @@ -1,6 +1,6 @@ |
| # 🤖 The Oficina home — design |
| -**Status:** design, revised **2026-09-05** (rulings 17-20, and four claims |
| +**Status:** design, revised **2026-09-05** (rulings 17-21, and four claims |
| corrected). Originally 2026-08-30. Drafted by the Sheets |
| session with Docs (`oficina/docs`) as a declared consumer. |
| **Review passes:** consumer review by docs-e6 (5 findings, all applied); |
| @@ -61,6 +61,7 @@ blind, replaceable*. |
| | 17 | **The springboard is one page: an app grid, with the team switcher as a dropdown in the header. Which apps exist comes from a registry, not from your grants. A team is a suite-wide entity that instances belong to** — §11, §10 | A team-switcher screen as a first level; deriving tiles from grants alone, which hides an app you have no team in and leaves nowhere to press New; grouping by hostname label alone, with no row that exists before the first instance and outlives any one of them | Paul, 2026-09-05 | |
| | 18 | **Activity is approved in principle, pulled at render, and sequenced last** — the front door shows a real feed with titles, the home holds none of it at rest, and its slot is a step 10 after step 4 — `design-activity-pull` | Push into the home; a contentless heat map; refusing the feature | Paul, 2026-09-05 | |
| | 19 | **A team name is reserved suite-wide. Both paths end in the drain — an existing instance settles the label question for the operator, it does not bypass them. The home answers only facts about its own records, never "may this person"** — §7 | Per-app first-come, which lets a stranger squat `acme.sheets` behind your `acme.docs` on a domain that reads as one suite | Paul, 2026-09-05 | |
| +| 21 | **§11's zero-JS rule gains one named carve-out: passkey enrolment and ceremony screens may load script. Everything else stays scriptless** — §11 | Passkeys leaving slice one; a blanket exemption; a progressive-enhancement version, since the ceremony itself cannot happen without script either way | Paul, 2026-09-05 | |
| | 20 | **Write the attribute spelling — `rst-btn`, not `class="rst-btn"`. The seven unpaired utilities keep `class`** — §11 | The class vocabulary, which this section ruled on 2026-08-30 and whose every stated reason has since died; "follow whatever the pin's partials use", which changes this app's markup on a dependency bump | Paul, 2026-09-05 | |
| --- |
| @@ -831,8 +832,22 @@ than re-copying by hand. There is no `static/tokens.css` in the tree today |
| object until the first vendored asset arrives. (The unstyled page is |
| `internal/home/page.go`; this section defines no page of its own.) |
| -Every screen works with scripts off. **No surface here has any reason to |
| -require script**, and the day one does is the day this section is wrong. |
| +Every screen works with scripts off — **with one named exception, and the |
| +day it arrived is the day this sentence was tested** (ruling 21). This |
| +section used to say "no surface here has any reason to require script, and |
| +the day one does is the day this section is wrong". That day came with |
| +passkeys: a WebAuthn ceremony is `navigator.credentials`, script by |
| +construction. rastrillo ships the client half (`webauthn.JS()` embeds |
| +`js/webauthn.mjs`), so nothing has to be written — the only question was |
| +whether one screen may require it, and the answer is yes. |
| + |
| +**The carve-out is exactly the passkey enrolment and ceremony screens.** |
| +Magic-link sign-in, recovery-code redemption and the whole springboard stay |
| +scriptless, so a person with script off keeps a working sign-in and loses |
| +only the optional second factor (ruling 15 already makes it optional). The |
| +no-`<script>` test gets an **explicit allowlist of those screens**, never a |
| +blanket exemption — a rule with a list is checkable; a rule with an escape |
| +hatch is not. |
| **The springboard** (ruling 17). The directory origin is the suite's front |
| door, and it is **one page**: a grid of apps, with a **team switcher as a |