| index 01c931a..ea2559f 100644 |
| --- a/docs/superpowers/specs/2026-09-03-memoria-design.md |
| +++ b/docs/superpowers/specs/2026-09-03-memoria-design.md |
| @@ -9,8 +9,13 @@ provider, §8.4 shape, decisions 13–14, live-key verification, gate 17, |
| |
| **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 |
| +**revision 7, commit `2b78680`**. Rulings 1, 3, 4, 6, 7, 9 and 14 are |
| +cited and never restated (suite ruling 7). **Ruling 15 (three rungs and a |
| +read-only link) is depended on and not yet cited in the body** — its |
| +"only the `team` rung is team-visible, so custody keys on the rung and |
| +never on the link" is what §5's custody mark should say, and does not. |
| +Owed; §13. Listing a ruling as consumed without a citation is the drift |
| +ruling 7 exists to prevent, one level up. 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. |
| @@ -26,7 +31,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 | |
| +| 3, suite doctrine adoption (A.4) | Fable | **REQUEST CHANGES — 1 BLOCKER, 9 MAJOR, 3 MINOR, 1 NIT.** All applied. The blocker: §4.1 adopted a custody intersection that was not the doctrine's, could not fail, and left the leak it claimed to close. §1.8 records what it changed | |
| |
| §1.2 records what the draft-1 reviews changed; §1.3 what the draft-2 |
| review changed. **Draft 3 has not been reviewed.** |
| @@ -73,7 +78,7 @@ 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 **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 | |
| +| 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 **§8's binding sentence**, which says "every artefact it produced" and means it — ruling 6's own row says "disclosed", so the phrase is §8's and the citation should not borrow the ruling's number for 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. **The derived-artefact widening is not his**: raised by the memoria session and adopted into A.4 on 2026-09-05, and he has not ruled on it — §13 | |
| | 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 | |
| @@ -395,12 +400,70 @@ being in neither §1 nor §13, with the interpretation of the load-bearing |
| one living only in a commit message. That is §1.6's failure by omission: |
| the strong reading asserted where reviewers do not look. |
| |
| +### 1.8 What the adoption review changed, and where an adoption goes wrong |
| + |
| +A.4 was reviewed three times in the suite repo before memoria adopted it. |
| +memoria's *application* of it was then reviewed once, by Fable, and came |
| +back REQUEST CHANGES with a blocker and nine majors. The distribution is |
| +the lesson. |
| + |
| +**The blocker was a rule invented while citing the section that forbade |
| +it.** §4.1 needed the custody intersection. The doctrine puts it in the |
| +**app of record, inside S2**: the sibling runs its grants join for the |
| +named asker and then answers `NOT_VISIBLE` for anything not team-visible. |
| +memoria wrote instead that it would render a passage when S2 says |
| +`VISIBLE` **and** the item was team-visible *when acquired* — a rule that |
| +is **vacuous** (everything memoria holds came through S3, which serves |
| +only team-visible items, so the second half is true of every held item), |
| +that **does not close the leak** (a document downgraded from `team` to |
| +`granted` with the asker's grant intact passes both halves), and that |
| +**argued against the doctrine's own mechanism** as a ruling-3 violation. |
| +The doctrine had explicitly retracted that reasoning — *"a later |
| +visibility change is exactly what the defect turns on"* — in the section |
| +memoria cited for it. |
| + |
| +**The other eight majors are one shape: the commit checked what it |
| +opened.** S7 was retired in §1 and §4.3 and left alive in the §6.1 |
| +contract table, in §6.5's warm-up, in §4.1's "this subsumes membership", |
| +in §2's members table and in five §13 items still listed as open after |
| +this commit closed them. §11 corrected the home dependency in one |
| +paragraph and reasserted it twelve lines below, in the same commit. The |
| +Docs §11.2 qualification was deleted from §8.3 and still owed in §11 and |
| +§13. The blanket write ban went from §6.4 and stayed load-bearing in §3.3, |
| +§8.5 and decision 10's rejected column. |
| + |
| +**The rule, and it is specific to adopting someone else's document:** the |
| +risk in an adoption commit is concentrated in the sections you did *not* |
| +open. A rewritten section has been read twice; an untouched one that |
| +depended on the old rule has been read never, and it now contradicts the |
| +new one while looking untouched and therefore fine. **Grep for the retired |
| +thing by name before claiming it is retired** — `S7`, `person_ref`, |
| +`members table`, `forbidden by §6.4` — and read every hit. |
| + |
| +Two smaller ones worth keeping. §4.1 had inverted *why* the asker subject |
| +is typed, saying the type is fixed at configuration because instances are |
| +provisioned together — which is the reasoning suite §3 names and rejects, |
| +"an operational habit, not an enforced property". And gate 21 claimed its |
| +fixtures had been replaced; there were never any fixtures, the gate does |
| +not exist, and the entry now says so. |
| + |
| +**One finding was wrong, and how it was wrong is worth as much.** The |
| +review could not resolve `6797bbf` or `4aea8f4` and said §13's blocker was |
| +therefore not shown closed. The hashes are correct — they are |
| +`refs/heads/main` on the hub, confirmed by `git ls-remote` — but every |
| +local checkout on this box is behind, because `amadan branch merge` lands |
| +on the remote and touches no working tree. The reviewer was right that the |
| +claim was unverifiable *as written*, and §13 now says which ref the hashes |
| +are on and how they were checked. A citation that only resolves for |
| +whoever wrote it is not a citation. |
| + |
| --- |
| |
| ## 2. What memoria is |
| |
| A fourth per-team instance app, beside sheets, docs and calendar, with |
| -its own DEK, its own members table and its own origin. It holds the |
| +its own DEK, its own roster copy — the home's in suite shape, the owners |
| +list in lone shape (§4.3) — and its own origin. It holds the |
| team's content in a sealed index and answers questions over it with |
| citations. |
| |
| @@ -580,8 +643,13 @@ backwards.** |
| ever held.** §3.5 mitigates it. |
| |
| **What memoria does not do is weaken its siblings' sealing.** It holds no |
| -sibling's DEK, gains no write path (§6.4), and cannot read a sibling's |
| -individually-granted content, because S3 404s it. |
| +sibling's DEK, cannot read a sibling's individually-granted content |
| +because S3 404s it, and **gains no write path except suite §7's two, which |
| +are narrower than "no writes" and differently shaped** (§6.4): a person's |
| +own hand-off, carried by their browser under their own session, and S8 as |
| +memoria itself into an area the team owner designated for it. Neither |
| +gives memoria a capability over a sibling's content; the first is not |
| +memoria writing at all. |
| |
| ### 3.4 A citation is exactly as trustworthy as the app it cites |
| |
| @@ -658,33 +726,66 @@ 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 asker is a typed subject, and the type travels on the wire** |
| +(suite §3): `{kind: person_ref | address, value}` — a person ref in suite |
| +shape, a lowercased address in lone shape. *On the wire* is the point, and |
| +this spec first got it backwards by saying the type is fixed at |
| +configuration because two instances that talk were provisioned together. |
| +The doctrine names that exact reasoning and rejects it: **"provisioned |
| +together" is an operational habit, not an enforced property.** With the |
| +kind on the call, a receiver that keys on the other one answers `UNKNOWN` |
| +for the whole call rather than guessing — which is a loud failure instead |
| +of a silent mismatch between an address and a person ref that happen to |
| +collide. Suite gate 2 tests it, and memoria's gate 6 gains the case. |
| |
| 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. |
| +**The sibling intersects its own answer with custody, and memoria depends |
| +on that** (suite §4.3, and the S2 row of suite §6): |
| + |
| +> S2's answer is intersected with custody. The app of record runs its |
| +> grants join for the named asker, and then answers `NOT_VISIBLE` for any |
| +> item that is not team-visible, **whatever the asker's individual grants |
| +> say**. |
| + |
| +Read Sheets §4.5 again — *"plus rows granted to me individually"* — and the |
| +reason is immediate. An item that leaves team-visibility appears in S1 as |
| +`gone`, which §4.2 requires to be **indistinguishable from deletion**. An |
| +unintersected S2 distinguishes them perfectly: `VISIBLE` for the one that |
| +went private, when a grantee is the asker, and `NOT_VISIBLE` for the one |
| +that was deleted. That is the leak the S1 visibility flag was removed to |
| +prevent and the leak S3's "404, never 403" was designed to prevent, |
| +arriving on the third call. |
| + |
| +**The cost is real and it is memoria's to disclose.** A person who still |
| +holds an individual grant loses that passage here, and any answer built on |
| +it, while the document stays open to them in the app of record. That is |
| +the price of §4.2's indistinguishability and it cannot be avoided while |
| +that promise stands. memoria's honest move is §6.5's: name the app the |
| +answer went to, rather than quietly return a shorter one. |
| + |
| +**What memoria must do about it**, since the mechanism is the sibling's: |
| +depend on it, and refuse to trust an S2 that has not demonstrated it. |
| +Before memoria consumes a sibling's S2, that sibling passes suite gate 3 — |
| +*the same item id answers `NOT_VISIBLE` in each state* — including the |
| +transition fixture: ingested while `team`, downgraded to `granted` with |
| +the asker's grant intact, asked before `gone` arrives. |
| + |
| +*An earlier version of this paragraph put the intersection on memoria's |
| +side, as "S2 says VISIBLE **and** the item was team-visible when |
| +acquired". Three things were wrong with it and a review found all three. |
| +It was **vacuous** — everything memoria holds arrived through S3, which |
| +serves only team-visible items, so the second half is true of every held |
| +item and false of none: a guard written to pass. It **did not close the |
| +leak**, because the downgrade-with-grant case passes both halves. And it |
| +**argued against the doctrine's actual mechanism** as a ruling-3 |
| +violation, when a sibling applying its own two rules to its own answer is |
| +not authorization cached or asked per person. The doctrine had already |
| +retracted exactly that reasoning — "a later visibility change is exactly |
| +what the defect turns on" — and this spec re-adopted it while citing the |
| +section that retracted it.* |
| |
| **Four-valued, and each value exists because collapsing it into another |
| produces a plausible empty list — the failure shape sheets-core AGENTS.md |
| @@ -712,10 +813,17 @@ pair gets exactly one result or the whole call is `UNKNOWN`. Without that |
| rule a missing entry falls through to omission, which is the silent path |
| again. Gate 6. |
| |
| -**This subsumes membership.** There is no separate "is P a member?" |
| -question and no membership synchronisation, because a non-member's items |
| -all come back `NOT_VISIBLE` from the app that knows. The four |
| -unreconciled tables of §1.2 are simply not consulted. |
| +**Membership is not S2's question, and it is no longer memoria's either.** |
| +This paragraph used to say S2 "subsumes membership" — that there is no |
| +separate *is P a member?* because a non-member's items all come back |
| +`NOT_VISIBLE`. That was true of decision 11's world and is not true now: |
| +**admission is the home's roster** (§4.3, suite ruling 4), and it is a |
| +different question asked at a different moment. Authorization is per |
| +asker, per query, never cached; admission is a pulled copy refreshed by |
| +epoch. Keeping them apart is the whole point of the roster split. What |
| +survives from the old paragraph is its good half — the four unreconciled |
| +tables of §1.2 are still not consulted, and memoria still maintains no |
| +membership state of its own. |
| |
| **Revision matters.** A document that stays team-visible but whose newer |
| revision removed a sentence must not have its stale chunk spoken: a |
| @@ -742,8 +850,11 @@ on it — measured before the constant is committed. |
| ### 4.2 memoria-owned content: memoria is the app of record |
| |
| Notion, Drive, forwarded mail and uploads have no sibling to ask. For |
| -this content **memoria's own members and grants are authoritative** — not |
| -a copy, so there is nothing to drift from. |
| +this content **memoria's own grants are authoritative** — not a copy, so |
| +there is nothing to drift from. *Membership is not among them:* that is |
| +the roster's, and §4.3 is where it is read. An earlier version said "own |
| +members and grants", which was decision 11's world and is the sentence |
| +that let a members table survive its own rejection. |
| |
| This is the honest resolution of gap 3, and it costs something that must |
| be said plainly rather than implied: **revoking a Notion page from the |
| @@ -758,8 +869,11 @@ claim does not pretend otherwise. |
| **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. |
| +and admits from it. The epoch rides the home's own responses — calls 2, 5 |
| +and 8 and the assertion — and **sibling calls carry none, deliberately** |
| +(suite §5.3), so nothing in S1, S2 or S3 grows a membership field. 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 |
| @@ -791,9 +905,19 @@ Three consequences worth keeping: |
| 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. |
| +- **A stale roster stops team standing for everybody, sessions included.** |
| + Within the TTL the pulled copy is enforced as it stands. Past the TTL |
| + with no successful fetch it is stale, and team standing stops being |
| + established for anyone — **including people already in a session** — so |
| + S2 answers `UNKNOWN` and the page withholds loudly. An unfamiliar roster |
| + generation holds the stale copy until an owner confirms it. Suite §5.3, |
| + gate 5. |
| + *This spec said the opposite until 2026-09-05: "a session already minted |
| + survives an outage for its TTL". That is the doctrine's revision-3 |
| + position, which it retracted — on a 30-day sliding session it is an |
| + outage-length revocation tail, the very thing the paragraph claimed to |
| + reject, and it is the same tail §13 had been carrying as an open defect |
| + of S7.* |
| - **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. |
| @@ -932,12 +1056,12 @@ memoria pulls on its own wake and on each question. It runs no timers. |
| | # | Call | Notes | |
| |---|---|---| |
| | S1 | Change feed | Cursor is an opaque position in the sibling's event log, issued by the sibling, stored per consumer. A row is `(item_id, revision, gone, touched_at)` — **no title, no content, no visibility flag** — so it passes the plaintext-grep gate unaided. Retention is a **per-consumer TTL** (home §9.1: "any 'prune when all consumers have caught up' is unbounded in a hibernating fleet"). Past TTL, or a restore-rewound cursor, takes a full re-announce. | |
| -| S2 | Authorization re-check | `(person_ref, [(item_id, revision)]) → VISIBLE / NOT_VISIBLE / STALE / UNKNOWN` per item. §4.1 specifies it, including the total decoder. Cap 256 pairs. | |
| +| S2 | Authorization re-check | `(subject, [(item_id, revision)]) → VISIBLE / NOT_VISIBLE / STALE / UNKNOWN` per item. Asker is a **typed subject** with its kind on the wire (suite §3); **the sibling intersects its answer with custody** (suite §4.3) — §4.1 explains why memoria depends on that and cannot do it itself. Total decoder, cap 256 pairs. **Never cached, at any scope** (ruling 3). | |
| | S3 | Content fetch | **`(id, revision)`, no person** → plaintext body over TLS into memoria's RAM only. The sibling answers only for items that are **team-visible** — the custody predicate, not the grants join — and **404s, never 403s**. This is an ingest call and there is no asker on the ingest path. | |
| -| S4 | Tombstone | *a feed row.* `gone` covers deletion and departure from team-visibility, indistinguishably. | |
| -| S5 | Consumer expiry | *no call.* TTL expiry is the decommission verb. | |
| -| S6 | Aggregate | `(person_ref, fn, [(sheet, tab, row, col, revision)…]) → {value, display, format_code}` or `STALE(cell)` / `NOT_VISIBLE` / `UNKNOWN` / `NON_NUMERIC(cell)` / `MIXED_UNITS`. §8.7. Cap 64 cells. Never cached. | |
| -| S7 | Membership | `(person_ref) → MEMBER / NOT_MEMBER / UNKNOWN`. §4.3's admission relay. Cached per session, not per request. | |
| +| ~~S4~~ | Tombstone | *a feed row, and no number is reserved for it* (suite §6). `gone` covers deletion and departure from team-visibility, indistinguishably. Numbering a non-call is how a table starts reading as a contract. | |
| +| ~~S5~~ | Consumer expiry | *no call, and no number.* TTL expiry is the decommission verb. | |
| +| S6 | Aggregate | `(subject, fn, [(sheet, tab, row, col, revision)…]) → {value, display, format_code}` or `STALE(cell)` / `NOT_VISIBLE` / `UNKNOWN` / `NON_NUMERIC(cell)` / `MIXED_UNITS`. §8.7. Cap 64 cells. Typed subject and the custody intersection, as S2. Never cached. **Specified, not built** — suite §12 step 4. | |
| +| ~~S7~~ | ~~Membership~~ | **RETIRED 2026-09-05.** It existed because there was no roster of record; suite call 8 is that record, and admission is §4.3's. Its per-session cache was the conflation of admission with authorization that ruling 3 now forbids outright. | |
| |
| **Draft 1's S1 row carried a `visible-to-team` field, and that was a |
| leak.** With it, a `visible=false, gone=false` row tells memoria that a |
| @@ -1073,17 +1197,32 @@ 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. |
| +**The write exception is narrow and it is suite ruling 9's: a write names |
| +its real actor, and no sibling ever acts as a person.** Two forms and no |
| +third — the hand-off (suite §7.1) and S8 (suite §7.2) — and **the wire |
| +shapes are the doctrine's, cited here and not restated.** An earlier |
| +version of this paragraph described the hand-off as "memoria renders a |
| +form that POSTs to the receiving app's create endpoint; memoria is not in |
| +the write at all, it only drew the button", which is the drifted |
| +restatement ruling 7 exists to prevent, and it drifted in the direction |
| +that made memoria's own obligations disappear. |
| + |
| +**memoria's own obligations, which are the part that belongs here:** |
| + |
| +- **The destination is validated against memoria's own `<team>` label |
| + before the form is rendered.** memoria is in the write to that extent — |
| + it chooses where the button points. |
| +- **A `form-action` CSP allowance** for the receiving origin, in memoria's |
| + rastrillo config. Gate 4's log rule runs on memoria's side of this, and |
| + the receiving app's create endpoint commits on receipt only after its own |
| + confirmation step (suite §7.1: "a create endpoint that commits on receipt |
| + is a drive-by"), which is the receiver's obligation and not ours. |
| +- **On-behalf-of writes are refused 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 |
| |
| @@ -1104,9 +1243,10 @@ is declared. |
| which app. |
| - **A cold sibling is a wake, and a wake is slower than a deadline.** The |
| honest consequence: the first search after a quiet period reliably |
| - withholds. So memoria **fires S7 and a warm-up S2 on its own wake**, |
| - before anyone has searched, to spend that latency where nobody is |
| - waiting. |
| + withholds. So memoria **pulls the roster (suite call 8) and fires a |
| + warm-up S2 on its own wake**, before anyone has searched, to spend that |
| + latency where nobody is waiting. *This said "fires S7" until 2026-09-05, |
| + which would have warmed a call that no longer exists.* |
| - **A reliably slow sibling produces permanent withholding**, which is |
| worse than it sounds: the product's primary path degrades to "Sheets did |
| not answer" forever, and a reader learns to ignore the notice. There is |
| @@ -1633,8 +1773,12 @@ and the answer is a garnish on it** (decision 5). |
| |
| ### 8.5 Prompt injection |
| |
| -- **No tools.** memoria retrieves and answers. It never writes to a |
| - sibling and never fetches a URL at answer time. |
| +- **No tools.** memoria retrieves and answers. It **never writes to a |
| + sibling at answer time** and never fetches a URL at answer time. Suite |
| + §7's writes are not tools and are not on this path: the hand-off is a |
| + person clicking a button after an answer is rendered, and S8 is memoria |
| + filing its own material. Neither is reachable from a prompt, which is |
| + what this bullet is about. |
| - **No remote loads in rendered answers.** `` is |
| exfiltration without a tool. Strip remote images; refuse links to |
| non-suite hosts. |
| @@ -1706,8 +1850,12 @@ second formatter as the same bug class one layer down. |
| `MAX`, `COUNT` — and never expression text, so memoria is not a formula |
| author and cannot grow into one. Rejected alternatives: 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, |
| -and a citation that cites itself). |
| +time); writing a formula into a scratch sheet — **and note this rejection |
| +now stands on decision 10's own ground rather than on a write ban.** Suite |
| +§7's S8 permits memoria to write into a designated area, and a scratch tab |
| +is exactly that shape, so "forbidden by §6.4" is no longer the reason. The |
| +reason is decision 10 itself: a formula memoria authored is a calculation |
| +memoria owns, and a citation that cites itself. |
| |
| **Chunking a spreadsheet**, which §13 carried as unsolved. Two chunk kinds |
| per tab: |
| @@ -1910,13 +2058,19 @@ renders on its own origin. The home never sees `q`. |
| stated chunk count exceeds the budget. §7's numbers were wrong twice |
| with no gate to notice. |
| 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. |
| + refused, and an unreadable roster refuses rather than admits. |
| + **NOT BUILT, and the honest version of that matters here.** This gate |
| + cited `S7` until 2026-09-05; retiring a call leaves a gate asserting |
| + against something that no longer exists, which is green forever for |
| + the worst reason. But nothing in `internal/memoriatest` ever asserted |
| + admission at all — there were no S7 fixtures to replace, and there are |
| + no roster fixtures now, so "the fixtures change with the prose" (which |
| + this entry said for one commit) described work that had not happened. |
| + **Owed, in two halves:** the *lone* half is buildable today against |
| + `Admits` — an address absent from `MEMORIA_OWNERS` is refused — and |
| + the *suite* half waits for the roster at slice 1. No mutation is named |
| + here, because per §1.7 an author-chosen mutation on a test that does |
| + not exist yet is twice removed from evidence. |
| 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 |
| @@ -1988,8 +2142,10 @@ 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 |
| -recorded in Docs. **This is the slice that turns on whole-corpus egress**, |
| +answer-above-citations, the §3.6 README. *No Docs §11.2 qualification is |
| +owed any more: §8.3 cites suite §8 and Docs cites the same sentence, which |
| +is how discussion 33 closed.* **This is the slice that turns on |
| +whole-corpus egress**, |
| so it does not land before its disclosure copy does. |
| |
| Four things enter with it rather than after, because each is cheap now and |
| @@ -2003,10 +2159,17 @@ only thing standing between a stable model id and a silent weight swap. |
| **Slice 3 — external connectors (§5.1)** under §4.2, with least-privilege |
| credentials (§3.5) and gate 7. |
| |
| -**Ship gate throughout:** home §13 step 4 before anything reaches a |
| -shared host. memoria depends on it more deeply than Sheets or Docs, |
| -because §4.1 needs a person ref on every query rather than only on grant |
| -rows. |
| +**Ship gate throughout, and it is shape-dependent.** In **lone shape** |
| +there is no home and none is needed: the asker subject is a lowercased |
| +address, sibling tokens are their own switch, and slices 0 to 2 run |
| +against Sheets and Docs without one. In **suite shape**, home §13 step 4 |
| +gates **admission only** — call 8 and the roster (§4.3) — and never §4.1, |
| +which needs a typed subject and not specifically a person ref. |
| + |
| +*This paragraph said "home §13 step 4 before anything reaches a shared |
| +host … because §4.1 needs a person ref on every query", twelve lines below |
| +the correction that says the opposite. Both were written in the same |
| +commit. Fixed 2026-09-05; §1.7's lesson arriving inside the fix for §1.6.* |
| |
| --- |
| |
| @@ -2018,6 +2181,7 @@ rows. |
| | ANN index (`coder/hnsw`) | Query latency exceeds ~200 ms, i.e. ~600k chunks at 512 dims — which §7.3 shows is *later* than the wake ceiling, not earlier | |
| | MCP adapter | The first consumer that speaks it | |
| | A feed surface on memoria | Something needs to consume memoria. A new decision, with its own leak-list entry | |
| +| **A source-of-record surface, so a hand-off can include memoria's own material** | Someone wants "save this answer as a Doc" to work for answers built on the team's own uploads. Suite §7 makes the *receiver* check every source with its app of record, and memoria serves nobody to ask (decision 7), so such a hand-off is refused today — §9.1. Opening it needs its own decision, its own §3.2 leak entry and its own gate, and it is Paul's: decision 7 is settled | |
| | Calendar as a source | Calendar has a spec and content; bodies off by default even then | |
| | Mail beyond forward-or-label | Never by sweep. A mail repo exists **and** a per-thread audience maps onto a team | |
| | Cached answers | Query cost becomes visible. Requires §5.2's provenance invalidation first. **Triggering this expires §8.4's "stored nowhere"** | |
| @@ -2037,30 +2201,32 @@ rows. |
| before this one found two fixes that renamed a problem instead of |
| removing it. |
| |
| - - **`MEMBER` is undefined for a guest.** Sheets §8.2 and Docs §8.2 both |
| - call a guest a member. §4.3 admits on "at least one sibling says |
| - MEMBER", so a person granted one Sheets sheet may be admitted — and |
| - §5.2 step 3 then gives them everything memoria owns. The predicate |
| - S7 asks needs to be "member of this team", not "known to this app". |
| - - **The S7 cache has no stated TTL, and the framework's default is 30 |
| - days.** `rastrillo/auth` sets `DefaultSessionTTL = 30 * 24h` and |
| - memoria overrides nothing, so a person removed from every sibling |
| - reads memoria-owned content for up to a month. The number was never |
| - chosen. It also sits against §6.5's "never to cache its |
| - authorization", which for memoria-owned content is what the cache is. |
| - - **§2 and §4.2 still specify the members table decision 11 rejected**, |
| - word for word from draft 2. A reader of §2 builds it; a reader of |
| - decision 11 does not. |
| - - **§4.1 still says there is "no separate 'is P a member?' question"**, |
| - which §4.3's S7 is. |
| - - **No S2 value covers a sibling that is BEHIND memoria** — restored |
| - from backup, holding revision 5 of an item memoria indexed at 7. |
| - `STALE` is defined as the sibling having moved *past* the revision, so |
| - the natural answer is `NOT_VISIBLE`, which is the one silent value. |
| - The plausible-empty-list failure returns through the restore door. |
| - - **The circuit breaker fires on a routine case.** Under "admit on at |
| - least one sibling", a Docs-only member gets wholesale `NOT_VISIBLE` |
| - from Sheets on every query, which §8.1 calls an anomaly. |
| + **Six of these closed on 2026-09-05 when A.4 landed, and they closed |
| + together because they were one defect wearing six coats: decision 11.** |
| + |
| + - ~~**`MEMBER` is undefined for a guest.**~~ **Closed.** Guests are |
| + never on the roster (§4.3), so the question a guest broke is no |
| + longer asked. Suite ruling 4. |
| + - ~~**The S7 cache has no stated TTL, and the framework's default is 30 |
| + days.**~~ **Closed.** There is no S7 and no cache. Ruling 3 is "never |
| + cached, at any scope", and §4.3's roster is a pulled copy with its |
| + own TTL whose expiry stales team standing for everybody rather than |
| + silently extending it. |
| + - ~~**§2 and §4.2 still specify the members table decision 11 |
| + rejected.**~~ **Closed.** §2 now says roster copy; §4.2 says grants |
| + and points membership at §4.3. |
| + - ~~**§4.1 still says there is "no separate 'is P a member?' |
| + question".**~~ **Closed.** §4.1 now says the opposite and says why: |
| + admission and authorization are different questions at different |
| + moments, which is the whole point of the roster split. |
| + - ~~**No S2 value covers a sibling that is BEHIND memoria.**~~ |
| + **Closed.** §4.1 defines it: a revision the app of record does not |
| + recognise answers `UNKNOWN` and withholds loudly, never `NOT_VISIBLE`. |
| + Suite §4.3; GPT-6's finding 11 on the doctrine. |
| + - ~~**The circuit breaker fires on a routine case.**~~ **Closed.** It |
| + was routine only under "admit on at least one sibling"; with |
| + admission on the roster, a Docs-only member is not admitted by Docs |
| + and then refused wholesale by Sheets. |
| - **Derived-artefact provenance is not transitive.** An artefact built |
| from another artefact partitions as memoria-owned and never reaches |
| S2 for the first artefact's sibling sources. Two hops launder what |
| @@ -2084,7 +2250,14 @@ rows. |
| 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 |
| + alone). |
| + **Those are `refs/heads/main` on the remote, verified by `git ls-remote` |
| + against each hub repo, and a checkout on any developer box will not |
| + resolve them** — `amadan branch merge` lands on the remote and touches |
| + no working tree, which is suite AGENTS.md's "the checkout on disk can |
| + lie". A reviewer of this section looked in local checkouts, found none of |
| + the three, and was right to say the claim was unverifiable as written. |
| + It is the fetch that is missing, not the merge. 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. |
| @@ -2226,8 +2399,25 @@ rows. |
| single-source class extractive, but synthesis spans ≥3 documents, which |
| is the one place the work is more than extraction and the one place a |
| second tier could earn itself. |
| -- **Docs §11.2's qualification** must be recorded in the Docs repo by |
| - `docs-e6`, or the two specs contradict. |
| +- ~~**Docs §11.2's qualification** must be recorded in the Docs repo.~~ |
| + **CLOSED 2026-09-05.** It is not recorded, it is *replaced*: §8.3 and |
| + Docs both cite suite §8's binding sentence, so there is one rule in one |
| + place rather than two qualifications in two repos that then drift. That |
| + is what `oficina/docs` discussion 33 asked for. |
| +- **A hand-off cannot include memoria-owned material, and that will read |
| + as a bug.** An answer built purely on sibling passages can be saved into |
| + Docs; one built on the team's own uploads cannot, because suite §7 makes |
| + the receiver check every source with its app of record and decision 7 |
| + gives memoria no surface to be asked on. **The asymmetry is invisible to |
| + a reader and looks like a defect long before it looks like a ruling.** |
| + Recorded in §9.1 and as a §12 row; the decision is Paul's. *(GPT-6's |
| + review of the doctrine, finding 3, via A.4.)* |
| +- **Ruling 15 is consumed and not cited.** §5's custody mark should say |
| + what it says — only the `team` rung is team-visible, so custody keys on |
| + the rung and never on the read-only link — and does not. One paragraph, |
| + owed. Note the doctrine's own §14 still records Paul as not having ruled |
| + on 15 while its §1 says he has; cite it as §1 has it and do not resolve |
| + their disagreement from here. |
| - **A `carlos/platform` discussion** noting the app boundary is the |
| implementer's to declare, and Oficina declares it at the suite — |
| informational, not a doctrine proposal (decision 2). |