oficina / sheets Public

Record real Sheets sibling producer gaps and conformance gates

Paul Campbell pushed by paul@keymail.dev ac5868df20b2298ca078d0e67c61504a7b7f2806 parent 4aea8f4
1 file changed, +43 −0
  • docs/superpowers/specs/2026-09-07-sheets-sibling-producer-gap.md +43 −0

CI passed — run details

diff --git a/docs/superpowers/specs/2026-09-07-sheets-sibling-producer-gap.md b/docs/superpowers/specs/2026-09-07-sheets-sibling-producer-gap.md
new file mode 100644
index 0000000..e3ca110
--- /dev/null
+++ b/docs/superpowers/specs/2026-09-07-sheets-sibling-producer-gap.md
@@ -0,0 +1,43 @@
+# Sheets sibling producer: implementation boundary
+
+Status: inspection and proposed design, not approved. Fable review pending quota reset. No production code change is authorized by this document alone.
+
+## Verified source
+
+- Sheets main `4aea8f4`, design §9a: S1/S2/S3 accepted wherever sibling tokens are configured; S6 separately deferred by suite slice order.
+- Suite main `1ac4323a7317a2bf4def3829896d9895a6ddc464`: doctrine §3, §4.2–4.3, §6.2; `fixtures.go`, `subject.go`, `s1.go`, `s2.go`, `s3.go`, `conformance/conformance.go`.
+- Sheets `internal/sheets/app.go`: no `/suite` routes. `cmd/sheets` and app configuration have no sibling token input. `go.mod` has no suite dependency. Existing browser-session routes are not sibling endpoints.
+- `internal/sheets/grants.go`: every completed, undeleted sheet is reachable through team standing. `store/models.go` has no custody rung. Explicit grants and links do not remove team access.
+- Cell input/cache versions are per tab. The approved isolated durable-version branch `d35cf94` fixes tab freshness, but it is not a workbook revision, does not include every title/format/metadata mutation, and is not on main. The existing input/cache history is not a consumer change feed.
+
+## Settled requirements to implement
+
+The actual paths are GET `/suite/s1?cursor=`, POST `/suite/s2`, and GET `/suite/s3/{item_id}?revision=`. S2 receives suite.S2Request, including a typed asker and at most 256 pairs. S3 has no asker. Use the shared module, not copied structs.
+
+Authenticate each call with a configured per-consumer bearer credential. Store hashes, support two acceptance epochs and three-phase rotation, and never let the credential act as a browser user. The receiver's consumer configuration identifies the same-team origin; an arbitrary Origin header is not independent proof. Disable sibling access when no consumer is configured. CSRF protection must still cover browser writes; S2's bearer-authenticated read must not inherit browser-only Origin requirements.
+
+Custody means team standing alone reaches the item. S3 refuses nonexistent, deleted, importing and non-team items identically with 404. S2 intersects the person's current grant join with custody on every request. Unreadable is NOT_VISIBLE; readable at the current revision is VISIBLE; source ahead is STALE; source behind, unsupported subject kind or inability to determine authority is UNKNOWN. No authorization cache and no account creation from an asker. Lone shape uses normalized addresses; person_ref cannot silently become an address.
+
+S1 carries only item_id, revision, gone, touched_at, with an opaque nonempty cursor. Never announce items that have never entered custody. A departure is gone, indistinguishable from deletion. Per-consumer bounded retention, cursor expiry and restoration rewind require full re-announcement. Read content at the revision named; never attach an old revision to current bytes. No title, cell, tab name, comment or bearer value in replicated plaintext/logs.
+
+## Design decisions still required
+
+1. Item and content mapping: recommend one item per workbook, with a deterministic versioned UTF-8 projection of ordered tabs and displayed cells. Decide whether titles, tab names, formulas, hidden/filtered cells, comments and formats belong in that projection. The shared S3 decoder deliberately accepts bytes; it does not settle this product contract. Memoria expects source display strings, so raw doubles alone are insufficient. XLSX export is not a settled search projection.
+2. Custody representation: decide the migration/default for explicit private/granted/team rungs and how existing sheet owners change it. Current team-readable behavior must not be silently tightened or broadened. Alternatively approve a clearly limited team-only producer first, with no claim that the unavailable private/granted transitions conform. The generic fixture cannot be satisfied by manufacturing states the application cannot represent.
+3. Revision boundary: introduce a persisted workbook revision covering exactly the chosen projection and custody changes. Mutation and feed record must commit together. Include import completion, creation, rename, tabs, input/clear, recalculation, structural edits, format/view changes if projected, restore and deletion. Read projection/revision from one consistent snapshot. Do not derive it from MAX surviving cell versions, timestamps, or event count.
+4. Consumer/feed persistence: specify configuration grammar and validation, token epoch identity, cursor binding, acknowledgement boundary, pagination, bounded re-announcement snapshot, TTL value supplied by consumer, and rewind/reset response understood by Memoria. Suite types define neither reset wire shape nor acknowledgement route. Do not invent a private protocol in Sheets.
+5. Conformance adapter: shared fixture IDs are strings such as doc-* and its team content is literal fixture bytes. Sheets IDs are numeric and cells are sealed. Define a harness seam that can seed real models and translate fixture identity/projection without replacing the production handler or fabricating responses. Add the seam in suite if needed, with review and a second adopter.
+
+## Required executable gates after design approval
+
+| Boundary | Real HTTP/storage assertion |
+|---|---|
+| Routes/auth | Exercise App.Router over an HTTP test server; missing/wrong/retired token refused, both rotation epochs accepted, wrong consumer/team config rejected, browser session alone insufficient, sibling bearer cannot call browser writes. |
+| Custody | Seed actual persisted states; run shared conformance against production handlers. Private/granted/link-only/deleted/importing/nonexistent bodies identical; team plus link unchanged. Downgrade after ingestion while retaining an individual grant gives S2 NOT_VISIBLE and S3 404 before feed delivery. |
+| Identity | Two members and stranger; grant/team changes take effect on next request; unsupported person_ref returns UNKNOWN in lone shape; malformed/duplicate JSON and over-cap requests fail closed without truncated answers. |
+| Freshness | Current/ahead/behind revisions; blank-last-cell and restart; all projection mutations advance revision; failed transactions produce neither new revision nor feed event. Concurrent S3 cannot mix bytes from one revision with another. |
+| Feed | Initial snapshot, multiple pages, empty page with cursor, repeated cursor, separate consumers, TTL expiry, restored DB ahead/behind cursors, deletion/downgrade/re-entry; no private-only IDs, content or visibility flags. |
+| Sealing | Unique synthetic title/tab/formula/cell/comment strings absent from raw DB/WAL and captured logs; only expected S3 response exposes approved projection. |
+| Consumer integration | Real Sheets producer called through suite transport by Memoria; index, edit, withhold stale, refetch and revoke. No fake fixture responder counted as app conformance. |
+
+Mutation checks must make the gate fail when custody is removed, revision comparison is bypassed, a failed transaction publishes an event, or a consumer is allowed to reuse another consumer's cursor. Run repository checks, exact-head CI, independent Fable spec/branch review, and a deployed isolated canary before merge. Production also requires preserving the already-deployed routes through the separate reconciliation branch. No approved candidate or canary is modified by this inspection.