oficina / suite Public

Merge branch 'worktree-s6-placement' into main

claude agent, operated by paul@keymail.dev pushed by paul@keymail.dev 2b78680e72b4f21909449f8f40951833a45f9cf1 parent 7876689parent ba8270e
2 files changed, +45 −15
  • README.md +2 −1
  • docs/superpowers/specs/2026-09-05-suite-sharing-doctrine.md +43 −14
diff --git a/README.md b/README.md
index 5d084f7..9082fdb 100644
--- a/README.md
+++ b/README.md
@@ -48,7 +48,8 @@ c := suite.NewSiblingClient("acme", nil) // refuses another team's label,
Built: S1 the change feed, S2 authorization with its total decoder, S3
content, the typed asker subject, the team-bound client, and gates 1 to 4.
That is the doctrine's §12 step 2, and it does not wait on the home. S6 is
-step 3, with Sheets serving and memoria calling; it is specified and has no
+step 4,
+after memoria's slice 1 and before its slice 2; it is specified and has no
fixture yet.
Not built, deliberately: the server half — that arrives with the first app
diff --git a/docs/superpowers/specs/2026-09-05-suite-sharing-doctrine.md b/docs/superpowers/specs/2026-09-05-suite-sharing-doctrine.md
index 9bcad6a..ac34d76 100644
--- a/docs/superpowers/specs/2026-09-05-suite-sharing-doctrine.md
+++ b/docs/superpowers/specs/2026-09-05-suite-sharing-doctrine.md
@@ -3,7 +3,14 @@
How the apps of the suite share identity, membership and content with
each other, and where the line is that nothing crosses.
-**Status:** design, **revision 6**, 2026-09-05.
+**Status:** design, **revision 7**, 2026-09-05.
+
+**Revision 7 moves S6 to where it is actually needed and records why it has
+the shape it has.** Revision 6 gave it a step before memoria slice 1, which
+was earlier than the dependency requires and implied slice 1 was blocked on
+it. The memoria session supplied the piece neither of the other two had:
+slice 1 is S1, S2, S3 and §4.1 and needs no S6 at all; the numeric and
+aggregate class in its §8.2 router is slice 2, and that is where S6 bites.
**Revision 6 schedules S6, which had a number and no builder.** It is in §6's
contract table and A.4 tells memoria to cite it, and no step of §12 ever
@@ -672,11 +679,29 @@ spec, which owns them, and are cited here for completeness of direction.
was no roster of record; call 8 is that record. A lone instance never
needed it. memoria decision 11 is superseded (Appendix A.4).
-**S6 is a call, and until step 3 it is a specification without a fixture.**
+**S6's shape is a settled decision of Paul's, not an API style.** memoria
+decision 10 — *"memoria never does arithmetic. A number comes from a cell,
+or from Sheets' engine via S6"* — was reached by two models independently
+and forbids one specific failure: a model averaging numeric strings it
+retrieved.
+That failure stacks incomplete recall on wrong operand selection and
+arithmetic done outside the engine that owns it, and it **cites its sources
+correctly while being wrong**, which is the hardest failure in this product
+to catch. So S6 takes an enumerated function — `SUM`, `AVERAGE`, `MIN`,
+`MAX`, `COUNT` — over cell refs, and never expression text, because a call
+that accepted expression text would let a consumer grow into a formula
+author one release at a time.
+
+Recorded here because §6's row said what S6 takes and not why, and a reader
+tidying an enumerated function into a general one would have been removing a
+ruling without knowing there was one. *(memoria decision 10, Paul; surfaced
+by the memoria session, 2026-09-05.)*
+
+**S6 is a call, and until its step it is a specification without a fixture.**
Ruling 7 says fixtures are the interface, so a call with no fixture has no
-interface — it has a description. §12 step 3 builds it, beside memoria
-slice 1, because Sheets is its only server and memoria its only caller,
-and neither is useful to it alone. Until then a consumer cites S6's
+interface — it has a description. §12 step 4 builds it, after memoria slice
+1 and before slice 2, because Sheets is its only server and memoria its only
+caller and neither is useful to it alone. Until then a consumer cites S6's
specification here and not a golden fixture, and A.4 says so rather than
implying one exists.
@@ -1142,15 +1167,19 @@ that cannot be written as stated is a spec bug.
2. **The contract module**, types and fixtures only: S1, S2, S3, the
hostile-hostname fixture, gates 1 to 4. No server half yet. Docs moves
its module path.
-3. **S6, with the same pair.** Sheets serves it, memoria calls it, and it
- ships with slice 1 rather than after it: an enumerated aggregate over
- cell refs, capped at 64, never expression text, with the typed asker and
- the custody intersection S2 carries. It is listed separately from the
- line below because revision 5 left it in the contract table and in A.4
- with no step at all, and a call that is numbered but unscheduled is
- indistinguishable from one that is finished.
-4. **memoria slice 1 against the module**, with Sheets and Docs serving
+3. **memoria slice 1 against the module**, with Sheets and Docs serving
S1 to S3. The second adoption corrects the fixtures.
+4. **S6, before memoria's slice 2 and not before its slice 1.** Sheets
+ serves it, memoria calls it, and nobody else does either: an enumerated
+ aggregate over cell refs, capped at 64, never expression text, carrying
+ the typed asker and the custody intersection S2 carries.
+
+ **Slice 1 does not need it** — that is S1, S2, S3 and memoria §4.1, all
+ of which the module already has. Slice 2 is memoria's model layer, and
+ the numeric and aggregate class of its §8.2 router is the one query class
+ S6 answers. Revision 6 put this step before slice 1, which implied a
+ dependency that does not exist; revision 5 gave it no step at all, which
+ let a numbered call read as a finished one.
5. **The home's identity layer** (home §13 step 4) and, on top of it,
call 8 and the roster (§5). Gates 5 and 8.
6. **Call 7**, the activity pull, once the home can mint assertions
@@ -1434,7 +1463,7 @@ own uploads. *(GPT-6 review, finding 3.)*
§11: memoria's person-ref and home dependency on slice 1 survives A.4 as
written. Slice 1 runs in lone shape against sibling tokens (suite §3, §12
-step 4), so the dependency is on the typed asker subject, not on the
+step 3), so the dependency is on the typed asker subject, not on the
home. Correct it, or slice 1 stays blocked on something that is not
actually in its way.