🤖 spec: adopt the suite doctrine, Appendix A.4
Against oficina/suite revision 7, commit 2b78680, now that all three sibling amendments have merged — home c1784e8, docs 6797bbf, sheets 4aea8f4. §13's standing BLOCKER closes with them. Decisions 1, 3 and 4 became suite rulings 1, 3 and 6 and are now cited rather than restated, per ruling 7: every app spec keeping its own copy of the contract is what drifted. Decisions 13 and 14 are memoria's own and stand — suite §8 adopts 13, and ruling 3's "never cached, at any scope" is the invariant 14 rests on. Decision 11 is superseded by ruling 4. Admission is the home's roster in suite shape and the owners list in lone shape; S7 is retired. The old rule was wrong twice: it cached membership for a session, which is the exact conflation of admission with authorization that the roster split removes — the doctrine's own loose "never cached past a session" came from this rule — and MEMBER was undefined for a guest, which memoria's §13 had recorded as an open question it could not answer from inside decision 11. The substantive fix is in §4.1, and it was a live leak. Sheets §4.5's grants join answers "rows this team can see, PLUS ROWS GRANTED TO ME INDIVIDUALLY". An individually-granted item is not team-visible, so custody forbids memoria holding it — and S2 as specified answered VISIBLE for it anyway. §6.1 closed that hole on S3 and on the S1 visibility flag, and §4.1 reopened it one call along. VISIBLE is now necessary and not sufficient: a sibling passage renders only if S2 says VISIBLE and the item was team-visible when acquired. §4.1 also gains the source-behind case. STALE means the sibling moved past our revision; a sibling restored from backup sits behind it, and a revision the app of record does not recognise is UNKNOWN, withheld loudly. Never VISIBLE, which authorises a revision nobody can vouch for; never NOT_VISIBLE, which is the silent value STALE was added to fix. §1.3 corrected: it claimed the S3 fix "carries the person ref too" while §6.1 said S3 takes no person. The fix deleted the false comment; it did not add an asker. Third time a §1 summary and its own body have disagreed. Slice 1 is not gated on the home and this spec said it was. The dependency is the typed asker subject, which in lone shape is a lowercased address. memoria had recorded itself blocked on something not in its way. Also: §6.4's blanket write ban becomes ruling 9 — a person's own hand-off, and S8 as memoria into its designated area, on-behalf-of writes rejected for good. §8.3 cites suite §8 instead of qualifying Docs §11.2, which is how discussion 33 closed. Decision 4 widens to derived artefacts and §5.2 records the producing model, so a regenerated artefact cannot inherit the wrong provider name. §9.1 records that a hand-off built on memoria-owned material is refused until a decision opens that surface — a real product cost, stated rather than discovered. Gate 21 stopped citing S7. Retiring a call leaves a gate asserting against something that no longer exists: green forever, for the worst reason. Not reviewed. A.4 was reviewed three times in the suite repo; memoria's application of it has had no pass, and the review table says so.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01H3VKtXnq6fSmM2F11Bjcsy
1 file changed,
+246
−64
docs/superpowers/specs/2026-09-03-memoria-design.md+246 −64
diff --git a/docs/superpowers/specs/2026-09-03-memoria-design.md b/docs/superpowers/specs/2026-09-03-memoria-design.md| index 12d7b8d..3ad36a6 100644 |
| --- a/docs/superpowers/specs/2026-09-03-memoria-design.md |
| +++ b/docs/superpowers/specs/2026-09-03-memoria-design.md |
| @@ -4,7 +4,16 @@ The Oficina suite's memory. One instance per team, at |
| `<team>.memoria.oficina.works`. |
| **Status:** draft 3, 2026-09-03; amended 2026-09-04 and 2026-09-05 (§8.3 |
| -provider, §8.4 shape, decisions 13–14, live-key verification, gate 17). |
| +provider, §8.4 shape, decisions 13–14, live-key verification, gate 17, |
| +**suite doctrine Appendix A.4**). |
| + |
| +**Consumed suite rulings.** This spec consumes `oficina/suite` |
| +`docs/superpowers/specs/2026-09-05-suite-sharing-doctrine.md` at |
| +**revision 7, commit `2b78680`**. Rulings 1, 3, 4, 6, 7, 9, 14 and 15 are |
| +cited and never restated (suite ruling 7). Not consumed yet: the hand-off |
| +(suite §7) — §9.1 records why memoria refuses one built on its own |
| +material — and S8, which needs a designated-area grant memoria has no UI |
| +for. S6 is cited as a specification; suite §12 step 4 builds it. |
| Review history, per AGENTS.md §Review. None of these models wrote the |
| draft they reviewed; all drafts were written by Opus. |
| @@ -17,6 +26,7 @@ draft they reviewed; all drafts were written by Opus. |
| | 3, §8.3 provider amendment | Fable | REQUEST CHANGES — 4 MAJOR, 4 MINOR, 2 NIT. All applied; §1.5 records what it changed | |
| | 3, §8.4 and decisions 13–14 | Fable | REQUEST CHANGES — 5 MAJOR, 4 MINOR, 1 NIT. All applied. It found §8.4's central argument resting on a misreading of §6.5 and §4.1; §1.6 records what it changed | |
| | 3, gate 17 + live-key verification | Fable | REQUEST CHANGES — 3 MAJOR, 4 MINOR, 3 NIT. All applied. **It mutation-tested gate 17 itself** and found five idioms the gate blessed, including one that served a declared path outside the session gate; §1.7 records what it changed | |
| +| 3, suite doctrine adoption (A.4) | — **not yet reviewed** | The adoption commit is unreviewed. A.4 itself was written by the suite session and reviewed three times there (Opus 5, GPT-6, GPT-6 + Fable), but memoria's *application* of it has had no pass | |
| §1.2 records what the draft-1 reviews changed; §1.3 what the draft-2 |
| review changed. **Draft 3 has not been reviewed.** |
| @@ -63,17 +73,31 @@ embedding is a plaintext cell in a costume — §3.1. |
| | 1 | **The suite is the app; the team is the boundary; the implementer declares the process set.** A team's instances of sheets, docs, calendar and memoria are one trust domain. | Four boundaries, one per process — memoria could then hold only person-initiated snapshots, stale the moment taken | Paul, 2026-09-03 | |
| | 2 | **Not a doctrine change.** CARLOS apps to date have been single processes, so process and app coincided. Oficina's app is a suite. The doctrine is that the implementer chooses the boundary; this is applying it, not amending it. | Filing it as a family ruling at `carlos/pegamento`, the way home §2.3 filed its divergence | Paul, 2026-09-03 | |
| | 3 | **Team-visibility is a CUSTODY rule; the per-asker check is the AUTHORIZATION rule.** Nothing enters memoria from a sibling unless it is team-visible there, which bounds what memoria holds. Who may *read* it is asked of the owning app, per person, per query. | Draft 1's "one ACL: is a member of this team", which conflated the two and left membership drifting across four tables | Paul, 2026-09-03, after review | |
| -| 4 | **Model egress is permitted to one named provider**, per-team opt-in, fixed by the team owner, named on every answer, disclosed in the README's first paragraph. | No egress at all; OpenRouter choosing per question; a local model only | Paul, 2026-09-03 | |
| +| 4 | **Model egress is permitted to one named provider**, per-team opt-in, fixed by the team owner, named on every answer **and on every derived artefact** (widened 2026-09-05 under suite ruling 6, which says "every artefact it produced" and means it — an answer is transient, a §5.2 artefact outlives the query and is regenerated when a source changes, so it can carry a different provider from the one that first made it), disclosed in the README's first paragraph. | No egress at all; OpenRouter choosing per question; a local model only | Paul, 2026-09-03 | |
| | 5 | **The search floor works with AI switched off**, and is the product. **With AI off, nothing leaves the instance at all** — which §3.2 shows is a larger difference than draft 1 realised. | An answer-first product with search as a fallback | AGENTS.md §Authorship, applied | |
| | 6 | **Embeddings and FTS5 postings are content.** They never reach replicated SQLite or the bucket unsealed. | Treating a vector as a derived artefact outside §0's scope, on the grounds that no plaintext bytes appear | Fable trust pass, adopted 2026-09-03 | |
| | 7 | **Every SIBLING call is a pull. memoria exposes no sibling-facing surface.** Its ingest endpoint serves external connectors only, which are memoria-owned content, not a sibling push. | An outbox per sibling; and draft 1's §9, which specified the push surface this decision forbids | Fable contract pass; corrected after review | |
| | 8 | **Provenance or deletion, at `(id, revision)` granularity.** A derived artefact whose provenance stops resolving *or whose source changed* is deleted or regenerated. memoria never fine-tunes. | Draft 1's id-level rule, which left "the Q3 target is X" standing after the source said Y | Fable ingestion pass; corrected after review | |
| | 10 | **memoria never does arithmetic. A number comes from a cell, or from Sheets' engine via S6.** S6 takes an enumerated function over cell refs — never expression text — so memoria is never a formula author. | memoria computing and showing its working (a second calculator, arrived at one `mean` at a time); writing a formula into a scratch sheet (a write, forbidden by §6.4) | Paul asked for numeric questions, 2026-09-03; shape ruled by Fable and GPT Sol independently, converging | |
| -| 11 | **Admission is derived from the siblings, not from a members table memoria maintains.** A person may enter memoria if at least one sibling says they are a member of this team. | Draft 2's own members table, which left a person removed everywhere else still able to read memoria-owned content | after the draft-2 review, 2026-09-03 | |
| +| 11 | ~~**Admission is derived from the siblings.**~~ **SUPERSEDED 2026-09-05 by suite ruling 4** — the home owns identity, the team and the membership roster; instances pull it (suite call 8) and memoria admits from that roster in suite shape, from the owners list in lone shape. S7 is retired. The rule below was right that memoria must hold no members table of its own and wrong about where the answer comes from — and it made a guest a member, since "at least one sibling says MEMBER" has no value for someone who is not on any roster. §4.3. Original text: a person may enter memoria if at least one sibling says they are a member of this team. | Draft 2's own members table, which left a person removed everywhere else still able to read memoria-owned content | after the draft-2 review, 2026-09-03 | |
| | 12 | **Name: `memoria`.** House convention is plain Spanish office nouns. A *memoria* is both a memory and, in Spanish office usage, a written record. | `archivo`; `cerebro`; `base`; anything English | Paul, 2026-09-03 | |
| | 13 | **The provider is TensorX, and memoria supports exactly one.** A team owner's choice is on or off, not which — a configurable base URL would let an owner reinstate a proxy while the answer still printed "one named provider". Chosen for EU jurisdiction and open weights, criteria decision 4 did not state, at a known cost in id-pinning and frontier quality (§8.3). | OpenRouter (a proxy: two parties see content); OpenAI direct (better id-pinning, worse jurisdiction and cost) | Paul, 2026-09-04, both halves — *"we go with exactly one for now: TensorX. If we want to swap that later we can."* Swapping the *generation* provider is a code change and a review, not a setting; swapping the *embedding* provider is that plus a full corpus re-embed unless the new host serves the same open weights (§8.3, §13) | |
| | 14 | **memoria is a search page, not a chat.** One GET search form, `?q=`, one query in and one page out, with `/documents/{id}` as the only other passage surface. This follows from decision 5, not from decision 3 — §8.4's invariant is what decision 3 requires, and a conversational surface could satisfy it. | A chat UI with citations; a threaded follow-up. Deferred to §12, not forbidden — the invariant forbids the leak, this row chooses the shape | Spec author, 2026-09-04, answering a question Paul asked; **confirmed by Paul 2026-09-05**, parking the conversational surface in §12 rather than forbidding it | |
| +**Three of these are now suite rulings, and this spec cites them rather |
| +than restating them.** Decision 1 is **suite ruling 1**, decision 3 is |
| +**ruling 3**, decision 4 is **ruling 6**. They are unchanged in substance; |
| +what changed is where they live. Suite ruling 7 is why: every app spec |
| +restating the contract in its own words is what drifted, so an app spec |
| +cites the doctrine and never restates one. Where a row below still carries |
| +the reasoning, that is history and not a second copy — the binding text is |
| +the doctrine's, at `oficina/suite`, revision 7, commit `2b78680`. |
| + |
| +**Decision 11 is superseded** by ruling 4, and decisions 13 and 14 are |
| +memoria's own and stand: suite §8 adopts 13's "the recipient is the app's, |
| +never the team's", and ruling 3's "never cached, at any scope" is the |
| +invariant 14 rests on. |
| + |
| ### 1.1 The proposal review, and what it killed |
| Before draft 1 existed, the *proposal* — ingest everything, store a |
| @@ -91,10 +115,13 @@ per-viewer copy*. They remain live traps for anyone widening this design. |
| never cached, never pushed. |
| - **The home cannot query memoria.** Home §0, home §2.2. **Accepted, not |
| worked around** — §9.2. |
| -- **Blocked on person refs** (home §13 step 4, not started). **Accepted, |
| - and now load-bearing**: decision 3's per-asker S2 needs a person ref, |
| - so memoria cannot ship before step 4. Same gate as Sheets and Docs, but |
| - memoria depends on it more deeply. |
| +- ~~**Blocked on person refs** (home §13 step 4).~~ **No longer true, and |
| + it was the more expensive kind of wrong.** S2 needs a *typed asker |
| + subject* (suite §3), which in lone shape is a lowercased address; sibling |
| + tokens are a switch independent of the home token. So slice 1 runs |
| + against Sheets and Docs before any home exists — suite §12 steps 2 and 3. |
| + memoria had recorded itself blocked on something that was not in its way. |
| + Corrected 2026-09-05 via A.4; §11. |
| **A citation note.** That review is recorded in session memory and in |
| this section; it is **not** in any repo file, so a reader cannot open it. |
| @@ -181,8 +208,16 @@ plausible-empty-list failure the same draft claimed to have removed. |
| **S3 could not do what it promised.** It took `(id, revision)` with no |
| asker, yet claimed the sibling "re-runs its grants join at fetch time". |
| -There was no person in the call. **Fixed:** S3 carries the person ref too |
| -(§6.1). |
| +There was no person in the call. **Fixed by deleting the claim, not by |
| +adding a person:** S3 takes no asker, and §6.1 spells out why — custody is |
| +person-less, so the call that fills the corpus can be made at all, and a |
| +per-person S3 would have made ingest impossible. |
| + |
| +*This paragraph said the opposite until 2026-09-05 — "S3 carries the |
| +person ref too" — while §6.1 three sections later said it does not. Both |
| +could not be right, and the summary was the wrong one. Caught by the suite |
| +doctrine's review of memoria (A.4), which is the third time in this |
| +document that a §1 summary and its own body have disagreed.* |
| **Derived artefacts had no algorithm for external or mixed provenance** — |
| S2 exists only at siblings. **Fixed** in §5.2. |
| @@ -619,14 +654,38 @@ Every path that would speak a passage originating in a sibling app calls |
| **S2** first, carrying the asker: |
| ``` |
| -S2(person_ref, [(item_id, revision), …]) |
| +S2(subject, [(item_id, revision), …]) |
| → per item: VISIBLE | NOT_VISIBLE | STALE | UNKNOWN |
| ``` |
| +**The asker is a typed subject, not a bare string** (suite §3): a person |
| +ref in suite shape, a lowercased address in lone shape. Two instances that |
| +talk were provisioned together and share a shape, so the type is fixed at |
| +configuration rather than negotiated per call. |
| + |
| The sibling answers from the grants join it already maintains — Sheets |
| §4.5, "rows this team can see, plus rows granted to me individually, |
| minus rows I have been removed from". memoria evaluates none of it. |
| +**But memoria intersects that answer with custody, and this is a leak the |
| +spec had reopened without noticing** (suite §4.3, ruling 14; found by the |
| +doctrine's review of memoria, 2026-09-05). Read Sheets §4.5 again: *"plus |
| +rows granted to me individually"*. An item granted to one person and to |
| +nobody else is **not team-visible**, so custody says memoria may never |
| +hold it — and yet S2 as specified above answers `VISIBLE` for it, because |
| +the asker really may read it at the source. §6.1 closed exactly this hole |
| +on S3 and on the S1 visibility flag, and §4.1 quietly reopened it one call |
| +along. |
| + |
| +**So `VISIBLE` is necessary and not sufficient.** memoria renders a |
| +sibling passage only when S2 says `VISIBLE` **and** the item was |
| +team-visible when acquired. The second half is memoria's own record, taken |
| +at ingest, not a second question to the sibling — asking again would be a |
| +custody question answered per person, which is the conflation ruling 3 |
| +exists to prevent. An item that passes the first and fails the second is a |
| +custody violation on our side: withhold it, and say so loudly, because a |
| +silent omission here would hide our own bug. |
| + |
| **Four-valued, and each value exists because collapsing it into another |
| produces a plausible empty list — the failure shape sheets-core AGENTS.md |
| names as the worst available.** |
| @@ -663,6 +722,17 @@ revision removed a sentence must not have its stale chunk spoken: a |
| revision behind the sibling's current one answers **`STALE`**, and memoria |
| refetches rather than omitting. |
| +**A source can be *behind* the revision we hold, and that is not `STALE`.** |
| +`STALE` means the sibling has moved *past* the revision memoria asked |
| +about — an ingestion delay, and memoria refetches. The opposite happens |
| +too: a sibling restored from backup can sit *behind* it, so the app of |
| +record does not recognise the revision at all. **That answers `UNKNOWN` |
| +and withholds loudly.** Never `VISIBLE`, which would authorise a revision |
| +nobody can vouch for; and never `NOT_VISIBLE`, which is the silent value |
| +and the exact mistake `STALE` was added to fix. The two directions look |
| +alike and behave differently, which is why the four values are not three. |
| +*(Suite §4.3; GPT-6's review of the doctrine, finding 11.)* |
| + |
| **Batch cap: 256 pairs per call**, multiple calls permitted. Draft 1's 64 |
| was tied to answer top-k and did not survive paginated search or |
| derived-artefact expansion (§5.2). The number's reason: one page of |
| @@ -683,36 +753,50 @@ memoria*. §5.1's enumeration diff is a convenience that eventually |
| notices upstream deletions, not an authorization mechanism, and §3.6's |
| claim does not pretend otherwise. |
| -### 4.3 Admission is derived from the siblings |
| +### 4.3 Admission comes from the roster |
| -A person reaches memoria with a home assertion whose `aud` is memoria's |
| -host. **Whether they are admitted is asked of the siblings, not answered |
| -from a table memoria maintains** (decision 11). |
| +**Admission is the home's roster — suite §5.3, ruling 4.** memoria holds no |
| +members table of its own and asks no sibling. In **suite shape** it pulls |
| +the roster (suite call 8, consumer token, roster epoch in every response) |
| +and admits from it. In **lone shape**, where there is no home, admission is |
| +the owners list, exactly as slice 0 does today. |
| Draft 2 had memoria keep its own members table, and the draft-2 review |
| -showed why that fails. S2 protected sibling content, but admission and |
| -§4.2's memoria-owned content both used memoria's own list — so a person |
| -removed from the team in Sheets and Docs, and not removed in memoria, |
| -could still sign in and read the team's Notion pages, Drive files, |
| -forwarded mail and uploads. The membership drift decision 3 was written to |
| -eliminate survived in exactly the half nobody was looking at. |
| - |
| -**The rule: a person is admitted if at least one sibling answers that they |
| -are a member of this team.** That is one more call, `S7(person_ref) → |
| -MEMBER | NOT_MEMBER | UNKNOWN`, and it is cached for the life of a session |
| -rather than per request, because it changes on the timescale of employment |
| -rather than of editing. |
| - |
| -Three consequences worth stating: |
| - |
| -- **`UNKNOWN` from every sibling refuses the session.** Fail closed: an |
| - unreachable suite means nobody new signs in, which is an outage, where |
| - the alternative is admitting someone nobody can vouch for. |
| -- **A session already minted survives a sibling outage** for its TTL, |
| - exactly as home §2.1 accepts for the suite as a whole. |
| -- **memoria still holds no authorization model.** It holds no answer of |
| - its own — it relays one, and §4.1 still governs every passage after the |
| - door. |
| +showed why that fails: S2 protected sibling content, but admission and |
| +§4.2's memoria-owned content both used memoria's own list, so a person |
| +removed everywhere else could still sign in and read the team's Notion |
| +pages, Drive files, forwarded mail and uploads. That failure is real and |
| +the roster fixes it. What draft 3 got wrong was the *replacement*. |
| + |
| +**What this replaces, and why it was broken.** Decision 11 admitted a |
| +person if **at least one sibling answered `MEMBER`** to a call `S7`. Two |
| +things were wrong with it. The small one: it cached membership for the life |
| +of a session, and ruling 3 now says authorization is never cached at any |
| +scope — the word "session" in the doctrine's first draft came from *this |
| +rule*, and folding admission into authorization is precisely the |
| +conflation the roster split exists to remove. **Custody and admission are |
| +person-less or roster-shaped; authorization is per asker, per query, never |
| +cached. They are three different things and S7 blurred two of them.** |
| + |
| +The large one: **`MEMBER` has no value for a guest.** A guest is not on any |
| +roster, so "at least one sibling says MEMBER" is undefined for exactly the |
| +person the rule most needed to exclude — and memoria's own §13 carried |
| +that as an open question it could not answer from inside decision 11. |
| +Under ruling 4 it answers itself: guests are never on the roster. **S7 is |
| +retired.** |
| + |
| +Three consequences worth keeping: |
| + |
| +- **No roster, no admission.** Fail closed: an unreachable home means |
| + nobody new signs in, which is an outage, where the alternative is |
| + admitting someone nobody can vouch for. In lone shape the owners list is |
| + local and this cannot arise. |
| +- **A session already minted survives an outage** for its TTL, exactly as |
| + home §2.1 accepts for the suite as a whole. That is admission surviving, |
| + not authorization — §4.1 still runs live on every passage. |
| +- **memoria still holds no authorization model.** It holds no answer of its |
| + own; it reads a roster and relays it, and §4.1 governs every passage |
| + after the door. |
| No guests, no anonymous link viewers. Admission is not authorization: an |
| admitted person still sees only what §4.1 and §4.2 allow. |
| @@ -766,7 +850,15 @@ The hard case is derived content, because a fact outlives its source. |
| **Ruled (decision 8): every derived artefact carries provenance as |
| `(source_id, revision)` pairs, and is deleted or regenerated when any of |
| -them stops resolving *or moves to a new revision*.** Draft 1's rule was |
| +them stops resolving *or moves to a new revision*.** |
| + |
| +**And it records the model that produced it, beside that provenance** |
| +(decision 4, widened 2026-09-05). Not for attribution's sake: an artefact |
| +is *regenerated* when a source moves, so the model that made the row a |
| +reader is looking at is often not the one that made it first. Without the |
| +producing model stored alongside, a regenerated artefact silently inherits |
| +the name of its predecessor — a naming requirement that reports the wrong |
| +name is worse than none, because it is believed. Draft 1's rule was |
| id-level, which left "the Q3 target is X" standing after the source |
| changed to Y — provenance that resolves and is wrong. |
| @@ -888,6 +980,29 @@ comment, not to add a person to the call. |
| So: **S3 is custody-scoped and person-less. S2 is the only per-person |
| gate, and nothing is spoken without it.** |
| +**The definitions above are no longer memoria's to state.** Suite ruling 7: |
| +one contract, one Go module, fixtures as the interface — every app imports |
| +`amadan.net/oficina/suite`, serves its half, and passes the conformance |
| +suite in CI. An app spec cites the doctrine and never restates it, because |
| +each spec restating the contract in its own words is exactly what drifted. |
| +So **S1, S2, S3 and S6 are the module's**, and what survives here is |
| +memoria's own reasoning about them: why S3 carries no person, why the four |
| +S2 values are four, what a slow sibling costs. The wire shapes are the |
| +module's and the fixtures are the interface. |
| + |
| +**S6 is cited as a specification, not as a fixture, and that is |
| +deliberate.** The module on `main` is S1, S2 and S3; there is no S6 fixture |
| +to conform to yet. Suite §12 **step 4** builds it — after memoria's slice 1 |
| +and before slice 2 — so §8.7's numeric path cites a call that is scheduled |
| +rather than one that exists, and picks up the fixture when it lands. |
| +Nothing else waits on it: **slice 1 needs S1, S2, S3 and §4.1 only.** |
| + |
| +*Suite revision 5 left S6 in the contract table with no step at all, which |
| +let a numbered call read as a finished one; revision 6 gave it a step and |
| +put it before slice 1, implying a dependency that does not exist; revision |
| +7 placed it at step 4 and said so in the step. Recorded because "cite S6" |
| +meant three different things in one evening.* |
| + |
| ### 6.2 Authentication, and rotation |
| **Per-consumer bearer tokens, minted by the operator drain, delivered as |
| @@ -952,9 +1067,23 @@ which is how home §2.2 survives decision 1. |
| ### 6.4 What decision 1 does not license |
| Any hop across a `<team>` label. Any instance presenting an assertion. |
| -Any write. Any sibling item outside the team-visible custody rule. The |
| -home in the loop. Content leaving the four hosts, except §8.3's named |
| -provider, which is a disclosed exception and not a sibling. |
| +**Any write except suite §7: a person's own hand-off, and S8 as memoria |
| +itself into its designated area.** Any sibling item outside the |
| +team-visible custody rule. The home in the loop. Content leaving the four |
| +hosts, except §8.3's named provider, which is a disclosed exception and not |
| +a sibling. |
| + |
| +**The write exception is narrow and it is suite ruling 9's shape: a write |
| +names its real actor, and no sibling ever acts as a person.** Two forms and |
| +no third. *A person's own hand-off* — memoria renders a form that POSTs to |
| +the receiving app's create endpoint carrying that person's own session, |
| +same-site cookie, scriptless; memoria is not in the write at all, it only |
| +drew the button. *S8, memoria as itself* — under the consumer token, only |
| +into an area the team owner designated for memoria, only items memoria |
| +authored. **On-behalf-of writes are rejected for good**, because a sibling |
| +token that can write as a person is a write capability over every member's |
| +identity. This spec previously forbade all writes, which was simpler and |
| +also forbade a person clicking "save this to Docs" with their own hands. |
| ### 6.5 Availability: the deadline, and what a slow sibling costs |
| @@ -1397,17 +1526,28 @@ reduce what is sent, and a retention term is a promise we cannot verify |
| (§3.6 says so to the reader and should keep saying it). Decision 5's AI-off |
| floor remains the only configuration in which nothing leaves. |
| -**This qualifies Docs §11.2**, which rejected third-party conversion APIs |
| -for shipping "customer documents to a third party, contradicting §2". The |
| -comparison must be like with like: Docs rejected whole-document egress, |
| -and per §3.2 **memoria's embedding ingest is also whole-corpus egress**, |
| -not merely answer-time top-k. Draft 1 compared Docs' whole documents |
| -against memoria's retrieved passages, which flattered memoria. The real |
| -difference is opt-in, naming, and the AI-off floor. **It is not a |
| -difference in what the provider sees, nor now in how much.** |
| - |
| -Because Docs is a separate repo, the qualification must be recorded there |
| -too, or the two specs contradict — §13. |
| +**The rule is suite §8's, not memoria's**, and this spec cites it rather |
| +than qualifying another app's: |
| + |
| +> Egress that is a property of the product is forbidden. Egress that is |
| +> per-team opt-in, to one named recipient, disclosed in the README's first |
| +> paragraph and on every artefact it produced, with a working floor when it |
| +> is off, is admissible. |
| + |
| +That one sentence replaces what stood here — a qualification of Docs §11.2 |
| +written from memoria's side — and Docs cites the same sentence rather than |
| +its own wording (A.2, merged 2026-09-05 as `6797bbf`). **This is how |
| +`oficina/docs` discussion 33 closed:** it asked for the boundary of Docs' |
| +rejection to be recorded rather than the rejection reversed, and a shared |
| +sentence records it once instead of twice in two repos that then drift. |
| + |
| +**What memoria still has to say for itself**, because the doctrine states |
| +the rule and not our position under it: per §3.2, **memoria's embedding |
| +ingest is whole-corpus egress**, not merely answer-time top-k. Draft 1 |
| +compared Docs' whole documents against memoria's retrieved passages, which |
| +flattered memoria. Under suite §8 the difference is opt-in, naming, and the |
| +AI-off floor. **It is not a difference in what the provider sees, nor in |
| +how much.** |
| **A "model call ledger" is content.** Any runtime record of prompts holds |
| retrieved passages, so it falls under §3.2's log rule and §5.2's |
| @@ -1646,6 +1786,25 @@ memoria exposes exactly two surfaces: |
| path**, and a sibling presenting a connector token is refused: the two |
| credential sets are disjoint and separately scoped. |
| +**A hand-off whose sources include memoria-owned material is refused, and |
| +that is a real product cost.** Suite §7 lets a person hand an artefact to |
| +another app through their own session — "save this answer as a Doc" — but |
| +it requires the *receiver* to check every source with its app of record. |
| +For a passage from Docs or Sheets that works. For memoria's own uploads, |
| +Notion pages and forwarded mail (§4.2), **memoria is the app of record and |
| +serves nobody to ask**, because decision 7 gives it no sibling-facing |
| +surface. So the receiver cannot check, and the hand-off cannot complete. |
| + |
| +**Refused until a decision opens that surface**, rather than opened here by |
| +implication. Opening it means a new decision with its own §3.2 leak entry |
| +and its own gate, exactly as §12 requires of any consumer of memoria — and |
| +decision 7 is settled, so widening it is Paul's and not a corollary. The |
| +cost is concrete and belongs in front of him: **an answer built on the |
| +team's own uploads cannot be saved into Docs**, while one built purely on |
| +sibling passages can. That asymmetry will look like a bug to a reader long |
| +before it looks like a ruling. *(Raised by GPT-6's review of the doctrine, |
| +finding 3, via A.4.)* |
| + |
| **No feed. Nothing consumes memoria.** If something ever does, that is a |
| new decision with its own leak-list entry, not an endpoint left lying |
| around for it. |
| @@ -1750,9 +1909,14 @@ renders on its own origin. The home never sees `q`. |
| rather than asserted, failing when the measured footprint at the |
| stated chunk count exceeds the budget. §7's numbers were wrong twice |
| with no gate to notice. |
| -21. **Admission is relayed, not held.** A person no sibling calls a member |
| - is refused, and `UNKNOWN` from every sibling refuses rather than |
| - admits. |
| +21. **Admission is read, not held.** A person absent from the roster is |
| + refused, and an unreadable roster refuses rather than admits. **The |
| + fixtures change with the prose:** this gate cited `S7` until |
| + 2026-09-05, and retiring a call leaves a gate asserting against |
| + something that no longer exists — green forever, for the worst reason. |
| + Suite shape asserts against the pulled roster and its epoch; lone shape |
| + against the owners list. Mutation: admit a subject absent from the |
| + roster, expect red. |
| 22. **Every stored vector has L2 norm 1 ± ε.** Deterministic, and the |
| mutation is named: *skip the renormalise after §7.2's 4096→512 |
| truncation, expect red.* Written this way on purpose — a gate phrased |
| @@ -1795,9 +1959,17 @@ egress at all.** This is the design Docs §15 item 2 deferred for want of |
| one, so proving it unblocks Docs regardless of memoria's fate. |
| **Slice 1 — the sibling contract (§6) and §4.1.** One source app first — |
| -Docs, since it has bodies. S1–S4, the per-asker gate, loud withholding, |
| -the circuit breaker. Needs person refs, so gated on home §13 step 4. |
| -Needs the drain to grow multi-host provisioning and three-phase rotation. |
| +Docs, since it has bodies. S1–S3 from the contract module, the per-asker |
| +gate, loud withholding, the circuit breaker. Needs the drain to grow |
| +multi-host provisioning and three-phase rotation. |
| + |
| +**It is not gated on the home, and this spec said it was.** The dependency |
| +is on the **typed asker subject** (suite §3), not on person refs existing: |
| +in lone shape the subject is a lowercased address, and sibling tokens are a |
| +switch independent of the home token, so suite §12 steps 2 and 3 run |
| +against Sheets and Docs before any home exists. Corrected 2026-09-05 — |
| +memoria had been recording itself as blocked on something that was not in |
| +its way, which is a more expensive mistake than being blocked. |
| **Slice 2 — the model layer (§8).** Named provider, router, |
| answer-above-citations, the §3.6 README, the Docs §11.2 qualification |
| @@ -1891,12 +2063,22 @@ rows. |
| - **§Review pass on draft 3 itself.** Done: Fable, 22 findings, REQUEST |
| CHANGES. The blocker it found was mine — see §6.1 on S3. |
| -- **BLOCKER, and not mine to close: the source apps must adopt decision |
| - 1.** home §2.2, Sheets PRODUCT.md principle 3 and Docs §8.2 each |
| - currently forbid content crossing an instance boundary. Paul ruled the |
| - suite is one trust domain, and memoria cannot enact that in its own |
| - table. `oficina/docs` discussion 33 is open; Sheets and home need the |
| - same. **Slice 1 is blocked on agreement, not on code.** |
| +- ~~**BLOCKER: the source apps must adopt decision 1.**~~ **CLOSED |
| + 2026-09-05.** Decision 1 became suite ruling 1 and all three siblings |
| + adopted it: home `c1784e8` (ruling 23, review record recovered as |
| + discussion 8), Docs `6797bbf` (A.2, discussion 37, closing discussion |
| + 33), Sheets `4aea8f4` (A.3, discussion 16, new §9a naming S6 as Sheets' |
| + alone). Each replaced "instance boundary" with "team boundary" and cites |
| + the doctrine rather than restating it. **Slice 1 is no longer blocked on |
| + agreement.** What it now waits on is Sheets and Docs actually serving |
| + S1–S3 — code, in their repos, suite §12 step 3. |
| +- **The three amendments cite doctrine revision 4 and the doctrine is at |
| + 7.** Gate 9 fails a spec more than one revision behind, so a citation |
| + bump is owed in all three. It is a debt and not a red gate, because gate |
| + 9 is not built — §12 step 2 schedules gates 1 to 4 only. Recorded here |
| + because memoria depends on those specs being current, and because |
| + "nobody has built the gate yet" is exactly the sentence that lets a |
| + currency rule rot. |
| - **The sign-in dial (§3.2) needs a decision.** The fetch lives in |
| `rastrillo/auth` and `keymaildev/signin`, not here. memoria could gate |
| `/auth/begin` on the owners list before rastrillo sees the address, |