oficina / sheets Public

Consume the suite doctrine, and keep the local rule that was never the problem

Appendix A.3 of the suite sharing doctrine (oficina/suite, revision 4,
commit 210312f).

PRODUCT.md principle 2 and spec §8.3 both said the boundary is the
instance. It is the team (suite ruling 1): a team's instances across every
app are one trust domain, so a same-team memoria may pull team-visible
sheets. Across a team label, nothing moves, and that has not changed.

§8.2 says where team standing comes from — the home's roster in suite shape,
$SHEETS_OWNERS and the local member rows in lone shape — and that per-sheet
roles stay here. The doctrine deliberately does not unify them: a role
vocabulary designed from three specs at once would fit none, and it moves to
the contract module only when two apps have shipped the same one. Sheets has
commenter, calendar has no such role, Docs has none. That moment is not now.

§9.1's five-call contract is cited rather than restated. This section had
already caught itself drifting once, in its own words — "the first draft
called this the four-call contract and then admitted a fifth in a
subsection" — and then drifted again from the other side, because the home
now has six calls with a seventh and eighth designed. The note was right
about the mechanism and could not fix it: the fix is not to count more
carefully, it is to stop keeping a second copy. What is Sheets' own stays,
including the correction about the home hashing rather than the instance.

AGENTS.md keeps its same-commit rule. A draft of the doctrine proposed
replacing it, reading it as a cross-repository claim — but it says that where
*this file* disagrees with *its own design spec*, the spec wins and both are
fixed together, and both files live here. It is a local obligation that
works and was never what broke. A second rule is added beside it for the
doctrine, which genuinely cannot be one commit.

New §9a lists the consumed rulings with the doctrine's commit and revision,
names S6 as Sheets' alone, and says S8 and the hand-off are not consumed yet.

It also records the one thing that travelled the other way: ruling 12, link
sharing is viewer-or-commenter never editor, is now suite ruling 15's
read-only half. A draft of the doctrine proposed an anonymous link editor
and had to be corrected against this spec — and the reason Sheets gave is
the reason the suite took it, that every version row carries an actor and an
anonymous editor writes unattributed rows into history.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Paul Campbell pushed by paul@keymail.dev f18adaa5a83decba3bb49f4507b691cadc4389a9 parent 8ed3926
3 files changed, +96 −25
  • AGENTS.md +8 −1
  • PRODUCT.md +6 −3
  • docs/superpowers/specs/2026-08-30-sheets-design.md +82 −21

CI passed — run details

diff --git a/AGENTS.md b/AGENTS.md
index 8d9691d..9d25ff7 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -4,7 +4,14 @@ A CARLOS spreadsheet. One instance per team.
This file is binding. Where it disagrees with a general skill, this file
wins. Where it disagrees with the design spec, the spec wins and this file
-is wrong — fix it in the same commit.
+is wrong — fix it in the same commit. Both files live here, so that is a
+rule this repository can actually keep.
+
+Where the design spec disagrees with the suite sharing doctrine
+(`oficina/suite`), the doctrine wins and **the spec is wrong — fix it by
+citing the ruling, not by restating it**. That cannot be one commit, because
+the doctrine is another repository; a cross-repository request goes through
+`amadan discuss` on the repo whose text must change.
**The design:** `docs/superpowers/specs/2026-08-30-sheets-design.md`. Its
§1 is the settled-decisions table: dated, attributed, with the rejected
diff --git a/PRODUCT.md b/PRODUCT.md
index 1a05d29..a19993d 100644
--- a/PRODUCT.md
+++ b/PRODUCT.md
@@ -44,9 +44,12 @@ confirmed binding by Paul on 2026-09-01:
compromised live host *can* read an open sheet — and the README says
so. **We never claim "we can't read your data."** The UI must never
imply otherwise, in copy, in iconography, or by omission.
-2. **One instance per team.** Sharing is membership: the sheet never
- moves, and an outside person becomes a scoped member of the instance
- that owns it. There is no federation story and there will not be one.
+2. **One instance per team.** Sharing is membership across teams: the
+ sheet never moves, and an outside person becomes a scoped member of the
+ team that owns it. There is no federation story across teams and there
+ will not be one. Within a team, Sheets is one of several apps sharing
+ one trust domain (suite ruling 1), and a same-team sibling may pull
+ team-visible sheets under the suite contract.
3. **No affordance is script-required.** Sign-in, the sheet list, sharing
and membership, settings, version history, comments, import and export
all work scriptless, as does a paginated table view of any sheet with
diff --git a/docs/superpowers/specs/2026-08-30-sheets-design.md b/docs/superpowers/specs/2026-08-30-sheets-design.md
index 5f6d9b7..709e42c 100644
--- a/docs/superpowers/specs/2026-08-30-sheets-design.md
+++ b/docs/superpowers/specs/2026-08-30-sheets-design.md
@@ -453,6 +453,14 @@ Team: owner, member, guest. Per sheet: owner, editor, commenter, viewer. A
guest holds no team-wide rights — they see exactly the sheets granted to
them and learn nothing about the team beyond §9.5's disclosure.
+**Team standing comes from the roster the home holds** (suite §5) in suite
+shape, and from `$SHEETS_OWNERS` and the local member rows in lone shape.
+**Per-sheet roles stay here.** The doctrine deliberately does not unify
+them: a shared role vocabulary designed from three specs at once would fit
+none of them, and it may move to the contract module when two apps have
+shipped the same one (suite §5.6). Sheets has `commenter`; calendar has no
+such role and Docs has none yet, so that moment has not arrived.
+
This is Pegamento's *"sharing is membership"* taken literally: the sheet
never moves, and the outside person becomes a scoped member of the instance
that owns it. There is no federation story and there will not be one.
@@ -460,9 +468,21 @@ that owns it. There is no federation story and there will not be one.
### 8.3 Link sharing
**"Sharing is membership" does not forbid link sharing.** It forbids data
-crossing an instance boundary, and a link does not move data. This sentence
-exists because the rule reads like a prohibition and someone will later
-think it was one. (docs-e6, 2026-08-30.)
+crossing a *team* boundary (suite ruling 2), and a link does not move data —
+whoever holds it reads the sheet here. This sentence exists because the rule
+reads like a prohibition and someone will later think it was one. (docs-e6,
+2026-08-30.)
+
+**A link is not a rung on the sharing ladder** (suite ruling 15). A sheet is
+private, granted to named people, or shared with the team, and only the team
+rung is team-visible — so a sheet that is link-shared but not team-shared is
+one no sibling may hold a copy of. Making a link team-visible would turn
+"whoever I sent this URL to" into "everyone on the team, findable through
+memoria's search", which is a different grant than the one the sharer made.
+
+**Ruling 12 below is now the suite's rule**, adopted verbatim as suite
+ruling 15's read-only half. The doctrine cites it; it stays here as the
+original, and the reason it gives is the reason the suite took it.
Off by default, high-entropy, revocable, **viewer-or-commenter only, never
editor** (ruling 12) — every version row carries an actor, and an anonymous
@@ -491,27 +511,30 @@ The home is a **separate repo and spec** (ruling 10), drafted by this
session with Docs as a declared consumer. This section records only what
Sheets depends on.
-### 9.1 The five-call contract
+### 9.1 The contract, cited rather than restated
+
+**Sheets consumes the home contract as the home spec states it, and the
+suite contract as `oficina/suite` states it.** Neither is copied here.
-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, 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.
+This section used to enumerate five calls. It had already caught itself
+drifting once — *"the first draft called this the four-call contract and
+then admitted a fifth in a subsection; an implementer reading the heading
+builds four"* — and the same thing happened again from the other side: the
+home now has six calls, with a seventh and eighth designed, and nothing made
+this copy notice. The note was right about the mechanism and could not fix
+it, because the fix is not to count more carefully. It is to stop keeping a
+second copy (suite §10).
- *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).
-5. **Rekey journal pull** (§9.6).
+What is Sheets' own, and stays:
-*(The first draft called this "the four-call contract" and then admitted a
-fifth in a subsection. An implementer reading the heading builds four.)*
+- **The home hashes, and returns the person ref**, which we store against
+ our member rows. An earlier revision of this section said the instance
+ sends `HMAC(pepper, …)`, contradicting §9.6 of this same document; an
+ implementer reading it would have built instance-side hashing, which needs
+ the pepper on every instance.
+- **A directory row never grants team standing** (suite ruling 12), whatever
+ its `kind` says. Membership is the home's own record, written only by an
+ owner.
Instance→home auth follows Pegamento's service contract: per-consumer
bearer token stored hashed, consumer identity is its origin, origins are an
@@ -722,6 +745,44 @@ window.
---
+## 9a. Consumed suite rulings
+
+The suite sharing doctrine (`oficina/suite`) binds this spec. Read against
+**revision 4, commit `210312f`**.
+
+| # | What it settles for Sheets |
+|---|---|
+| 1 | The team is the boundary. A team's instances across every app are one trust domain, so a same-team memoria may hold team-visible sheets. |
+| 2 | Sharing is membership across teams. Nothing crosses a team label. |
+| 3 | Custody and authorization are two rules, and authorization is never cached at any scope. |
+| 5 | Two shapes, one switch. The sibling-token switch is independent of whether a home is present. |
+| 6 | The egress sentence, suite-wide. |
+| 7 | One contract, one module, fixtures as the interface. |
+| 8 | Two credentials. A consumer token proves a same-team sibling; an assertion proves a person. |
+| 9 | A write names its real actor — the rule §8.3's ruling 12 already served. |
+| 12 | Membership is the home's own record. A directory row never grants team standing. |
+| 14 | Custody is bounded at acquisition and retired by the feed. |
+| 15 | Three rungs and a read-only link. Sheets ruling 12 is the original this adopts. |
+
+Sheets serves **S1, S2, S3 and S6** wherever sibling consumer tokens are in
+its platform config. S6 is Sheets' alone: an enumerated aggregate over cell
+refs, capped, never expression text, so memoria can total a column without
+holding the sheet.
+
+**Not yet consumed:** S8 write-as-self (suite §7.2) needs a designated-area
+grant in the share UI, and the hand-off (suite §7.1) needs a receipt route
+with pending state. Neither is specified here.
+
+**One thing the doctrine took from Sheets rather than the other way round.**
+Ruling 12 — link sharing is viewer-or-commenter, never editor — is now suite
+ruling 15's read-only half, and the reason Sheets gave for it is the reason
+the suite took it: every version row carries an actor, and an anonymous
+editor writes unattributed rows into history. A draft of the doctrine
+proposed an anonymous link editor and had to be corrected against this
+spec.
+
+---
+
## 10. Design system
`day` theme (ruling 7). rastrillo tokens.