oficina / home Public

Rulings 17 and 18, and three claims the spec got wrong

Ruling 17 settles the springboard's shape. It is one page: a grid of
apps with the team switcher as a dropdown in the header, and which apps
exist comes from a registry rather than from your grants. The registry
is what lets an app you have no team in still render its New button,
and §4's per-app hostname and §7's provisioning both need it anyway.
Mail and Files enter it as `integrated: false` and render as outbound
links — Keymail and Slopbox are separate products with their own auth,
and neither verifies assertions yet.

Ruling 18 defers the activity surface rather than refusing it. Any
version the home renders itself makes it learn document names, which
puts them on §3.2's list and retracts §0. Each app rendering its own
activity on its own origin is the version that survives, and the
question gets revisited against a springboard people actually use.

Three corrections, all found by the review pass on the branches
downstream of this file:

§11's "zero bare `rst-*` attribute selectors against ~528 class
occurrences" was true when verified on 2026-08-30 and false at every
version this repo has pinned — v0.22.0 has 130 and 536. The twins
landed in v0.21.0, which the first go.mod already pinned, so no later
bump falsified it. The rule to write classes survives; its evidence
did not.

"Until `rastrillo doctor` lands" was a trigger that had already fired:
doctor shipped in v0.21.0. There is no vendored `static/` here yet, so
the instruction had no object either.

§3.2 listed session rows as carrying "coarsened timestamps". They
cannot — expiry is compared against them, and rastrillo stores exact
RFC3339. §3.3's coarsening is a rule for publication, not a storage
format.

§15's "a real suite domain" bullet died with ruling 16 and is struck
rather than deleted, since the reasoning is worth keeping visible.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Paul Campbell pushed by paul@keymail.dev ba4461414e5c4144a0538c6c629da1201b8af10d parent 462df84
1 file changed, +61 −14
  • docs/superpowers/specs/2026-08-30-home-design.md +61 −14

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 444fbd9..266d6ae 100644
--- a/docs/superpowers/specs/2026-08-30-home-design.md
+++ b/docs/superpowers/specs/2026-08-30-home-design.md
@@ -56,6 +56,8 @@ blind, replaceable*.
| 14 | **Hand-roll sign-in: subject is an opaque person ref, link state is sealed** — §5.1 | `rastrillo/auth` as shipped (stores readable addresses); blocking on upstream | Paul, 2026-08-30 |
| 15 | **Passkey 2FA is optional per person. Owner-enforceable per-team is NOT in slice one** — §14 | Promising a per-team floor neither spec can build | Paul, 2026-08-30 |
| 16 | **`oficina.works` is the primary domain — RP `id.oficina.works`, directory at the apex, instances at `<team>.<app>.oficina.works`** — §4 | Staying on `oncarlos.com` until passkeys forced the move; keeping the `bds` slot | Paul, 2026-09-03 |
+| 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** — §11 | 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 | Paul, 2026-09-05 |
+| 18 | **No activity surface on the springboard while §0 stands — deferred, not refused** — §15 | Building it now, which requires the home to learn document names and retracts §0; refusing it outright | Paul, 2026-09-05 |
---
@@ -178,7 +180,11 @@ journal's new addresses; **sign-in link state** (§5.1).
- The bipartite graph: person ref ↔ team ↔ app ↔ `kind`.
- Team hostnames and sqids — these appear in URLs and were never secret.
- Passkey credential ids and **public** keys.
-- Session rows keyed by **person ref**, and coarsened timestamps (§3.3).
+- Session rows keyed by **person ref**. Their timestamps are **exact at
+ rest** — expiry is compared against them, so they cannot be coarsened in
+ the table — and §3.3's coarsening is a **rule for publication**, applied
+ when the database is published, never a storage format. An earlier draft
+ listed them as "coarsened timestamps", which no expiring session can be.
- Each person's theme.
**Never at rest readable: an email address.** Sealed and short-lived in link
@@ -660,22 +666,54 @@ restored is a hope, not a backup* — the restore drill runs before launch.
rastrillo `server` shape, zero-JS baseline, `day` theme, and the **class**
vocabulary — `class="rst-*"`.
-**Verified at `origin/main` post-design-system-v2, 2026-08-30:**
-`ui/tokens.css` has **zero bare `rst-*` attribute selectors** against ~528
-`.rst-` class selector occurrences. (It does contain attribute selectors for
-`[data-tone=]`, `[aria-*]`, `[type=]` — an earlier draft said "zero
-attribute selectors", which was imprecise. The conclusion is unchanged:
-write classes.) `Pair`, `Allocate`, `rastrillo doctor` and
-`--rst-header-rule` remain in zero files.
-
-Re-copy `static/tokens.css` deliberately on every rastrillo upgrade until
-`rastrillo doctor` lands.
+**Corrected 2026-09-05, verified against the pinned `rastrillo v0.22.0`.**
+An earlier draft recorded "zero bare `rst-*` attribute selectors against
+~528 `.rst-` class selector occurrences", verified 2026-08-30 against
+`origin/main` post-design-system-v2. That was true when written and is
+false at every version this repo has ever pinned: `ui/tokens.css` at
+v0.22.0 has **130 distinct bare `rst-*` attribute selectors** against
+**536** `.rst-` class occurrences. The twins arrived in **v0.21.0**, which
+is what this repo's first `go.mod` pinned — so the claim was falsified
+before the skeleton existed, not by a later bump.
+
+**The rule survives its evidence: write `class="rst-*"`.** The bare-attribute
+grammar is released but the class vocabulary is what this app uses, and a
+mixed codebase is worse than either. Re-check the counts whenever the pin
+moves, and say which version was checked.
+
+**`rastrillo doctor` landed in v0.21.0** — an earlier draft's "until
+`rastrillo doctor` lands" was a trigger that had already fired. When this
+app vendors `static/`, run `rastrillo doctor --fix` on each upgrade rather
+than re-copying by hand. There is no `static/tokens.css` in the tree today
+(§11's zero-JS page links no stylesheet), so this is an instruction with no
+object until the first vendored asset arrives.
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.
-The directory is the suite's front door: your teams grouped by app, and a
-New button. No template gallery, no upsell chrome, no waffle.
+**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
+dropdown in the header**. You pick a team above; the grid below is that
+team's apps. It is a dropdown rather than a first-level screen because most
+people have one team, and a switcher screen would be a click that earns
+nothing.
+
+**Which apps exist comes from a registry, not from your grants.** The
+registry holds, per app: slug, display name, hostname pattern,
+`provisionable`, `integrated`. The directory renders your teams' instances
+under each entry; an entry you have no instance in still renders, showing
+the New button, which is the affordance a grants-only list cannot have. The
+registry is not invented for this screen — §4's `<team>.<app>.oficina.works`
+has to resolve per app and §7 has to know what can be provisioned, and
+without it that knowledge is scattered across templates.
+
+**Mail and Files are `integrated: false`** — Keymail and Slopbox are
+separate products in separate repositories with their own auth. They render
+as plain outbound links, never as team-scoped tiles, until each verifies
+assertions per §5.3 (§14).
+
+No activity feed (ruling 18, §15), no template gallery, no upsell chrome,
+no waffle.
---
@@ -739,6 +777,7 @@ ships**, because call 2's person ref is what their grant rows key on.
| **Step-up for instances** (`RequireFresh`-grade) | An instance flow needs it; requires a fresh-assertion round trip with a `max_age`-style parameter |
| Any preference beyond theme | It passes §5.4 |
| Billing, plans, quotas | Somebody has to pay |
+| **Mail and Files behind suite SSO** | Keymail or Slopbox verifies assertions per §5.3 in its own repository. Until then they are `integrated: false` registry entries rendered as outbound links (§11, ruling 17) |
| A support console | Never, under §0 |
---
@@ -753,4 +792,12 @@ ships**, because call 2's person ref is what their grant rows key on.
- **`carlos/platform` #4** — the machine credential.
- **`carlos/pegamento` #1** — the shared-instance ruling, and whether §9
protects the credential or the knowledge.
-- **A real suite domain** — triggers §5.7.
+- **A cross-app activity surface** (ruling 18). Deferred, not refused. Any
+ version the home renders itself requires it to learn document names,
+ which puts them on §3.2's list and retracts §0. The version that
+ survives is each app rendering its own activity on its own origin, with
+ the springboard linking there. Revisit against a springboard people
+ actually use, not against a description of one.
+- ~~A real suite domain — triggers §5.7~~ — closed by ruling 16. §5.7 is
+ titled for the case it does not apply to: the `oficina.works` move needs
+ no RP rename drill.