oficina / memoria Public

🤖 spec: AHEAD is adopted, not disputed; and §13 stops arguing with itself

Two structural findings from the merge review, plus the citation currency
the third one asked for.

AHEAD WAS HALF-CONVERTED and the section asserted both positions. §4.1's
opening said "unresolved, the merge's one real disagreement" while eight
paragraphs down a paragraph inherited from main stated the four-value
ruling as settled, citing suite §4.3 and GPT-6's finding 11 — correctly,
for revision 7. The situation moved while the review was running: revision
10 adopts AHEAD, on memoria's argument, with the no-row half withdrawn
because it opened an id-existence oracle.

So it is a shipping lag, not a disagreement, and the distinction matters —
nothing here needs redesigning and nothing here can be built until the
module lands. §4.1 now says that once, and the dependents that name the
fifth value (gate 6, §8.1's third breaker, §5.2 step 2, §7.4) are marked as
one dependency with one date rather than five open questions. §8.1's
breaker gets the sharper note: with the wire mapping the case to UNKNOWN it
cannot fire at all, and would be indistinguishable from the outage that
same section says must not trip a breaker.

§13 HAD TWO LISTS ARGUING. An item truncated mid-sentence by the merge; the
same sign-in dial question live in one copy and struck in another; the
decision-1 blocker recorded CLOSED and also live as "slice 1 is blocked on
agreement"; two S7 items outliving S7; a 15-minute TTL for a cache that no
longer exists; and a header counting four NEEDS PAUL items of which two
were dead.

The roster-vs-sharing blocker closed itself, which is the part worth
reading: it named two possible rulings — the siblings adopt a member/guest
distinction, or decision 11 is narrowed so admission does not rest on their
answer — and suite ruling 4 is the second. Struck items keep their
reasoning, because that is only visible if they are still there.

The header now names its four: session TTL, the unit-override switch, the
CI runner's repo list, and the disclosure route for the rastrillo SSRF.

And the doctrine citation says what it is behind by: revisions 8 and 10
have landed, gate 9 fails a spec more than one revision back, and the bump
waits on suite CI being configured at all.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Paul Campbell pushed by paul@keymail.dev 8593a1bfa6626c1e7d0b8d09e88afefc636bfdfe parent 36b89cd
1 file changed, +90 −42
  • docs/superpowers/specs/2026-09-03-memoria-design.md +90 −42
diff --git a/docs/superpowers/specs/2026-09-03-memoria-design.md b/docs/superpowers/specs/2026-09-03-memoria-design.md
index e399f77..60f6ccb 100644
--- a/docs/superpowers/specs/2026-09-03-memoria-design.md
+++ b/docs/superpowers/specs/2026-09-03-memoria-design.md
@@ -10,7 +10,13 @@ draft-5 findings, which are still unfixed and still listed in §13.
**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
+**revision 7, commit `2b78680`** — with two amendments adopted since and
+not yet reflected here as a version bump: revision 8 (the module's
+importability, now closed) and **revision 10, which adds `AHEAD`** (§4.1).
+The module this repository pins is `60b01a8`, which is revision 8's merge.
+Gate 9 fails a spec more than one revision behind, so this citation is
+owed a bump the moment the suite's branches merge; they cannot, because
+suite CI is unconfigured (§13). Rulings 1, 3, 4, 6, 7, 9, 14 and 15 are
cited and never restated (suite ruling 7) — 15 at §5's custody mark, which
is where "only the `team` rung is team-visible, so custody keys on the rung
and never on the link" decides what memoria may hold. Not consumed yet: the hand-off
@@ -952,8 +958,8 @@ 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.
-**`AHEAD` is memoria's fifth value and the contract has four. Unresolved,
-and it is the merge's one real disagreement.**
+**`AHEAD` is adopted and not yet shipped. Settled 2026-09-06 as suite
+doctrine revision 10, on memoria's argument.**
Draft 4 added `AHEAD` for the case where the *sibling* is behind — its log
stops short of the revision memoria holds, or it has no row for the item
@@ -962,14 +968,19 @@ refetching fixes it, `NOT_VISIBLE` is the silent value and would delete
history the sibling still has, and `UNKNOWN` "would report no answer when
the sibling answered". Three wrong homes, so a fourth value.
-The suite doctrine rules the same case **`UNKNOWN`, withheld loudly**
-(suite §4.3), and `amadan.net/oficina/suite` implements exactly four
-values — `suite.Authorization` cannot express `AHEAD` at all. So the wire
-shape above is the contract's four, because that is what the module
-carries and ruling 7 says an app cites the contract rather than restating
-it.
+Suite revision 7 had ruled the case `UNKNOWN`; **revision 10 replaces
+that with `AHEAD`**, in the shape memoria proposed and with one half
+declined — see below. The module carries it on an unmerged branch, so the
+version this repository pins (`60b01a8`) still has four constants.
+
+**So the wire shape above is four values today and five when the module
+ships**, and every reference to `AHEAD` in this spec is written against the
+five-value design. That is a shipping lag, not a disagreement, and the
+distinction matters: nothing here needs redesigning, and nothing here can
+be built until the module lands. Suite CI is unconfigured, which is what
+holds it (§13).
-**That is not agreement, and it should not be minuted as agreement.**
+**How it got settled, because the route matters more than the outcome.**
Record the doctrine's reasoning as the doctrine records it: suite §4.3
attributes the `UNKNOWN` ruling to *its own GPT-6 review, finding 11* —
@@ -987,10 +998,23 @@ question and the doctrine with none of the reasoning — and it is the more
embarrassing way to be wrong, because it makes another repository's
careful work look like an accident of mine.*
-Until the contract change lands, memoria maps the case to `UNKNOWN` and
-**loses the distinction between "the sibling answered, and is behind" and
-"the sibling did not answer"** — which is the collapse `AHEAD` was cut to
-prevent. §13 carries it.
+The argument that carried it was not the original one. The suite's
+objection — that `AHEAD` for an item with **no row at all** opens an
+id-existence oracle — was right and that half is withdrawn (§4.1's no-row
+rule above). What survives is the half that does not leak: **a revision
+beyond the sibling's log, for an item it still admits.**
+
+And the reason that case cannot be handled by the feed instead: a
+**whole-log** restore rewinds the sibling's cursor, and S1a re-announces
+unasked. A **partial** restore — a content table restored without its event
+log — leaves the cursor perfectly valid, so nothing fires and nothing can,
+because from the log's point of view nothing happened. Only a per-item
+answer can see it. The two mechanisms are complementary, not overlapping.
+
+Until the module ships, memoria maps the case to `UNKNOWN` and **loses the
+distinction between "the sibling answered, and is behind" and "the sibling
+did not answer"** — the collapse `AHEAD` was cut to prevent. §13 carries
+it as a shipping dependency rather than an open question.
**The sibling intersects its own answer with custody, and memoria depends
on that** (suite §4.3, and the S2 row of suite §6):
@@ -1208,12 +1232,25 @@ refetches rather than omitting.
`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.)*
+record does not recognise the revision at all. **That answers `AHEAD`, and
+withholds loudly while the source re-announces.** Never `VISIBLE`, which
+would authorise a revision nobody can vouch for; never `NOT_VISIBLE`, the
+silent value and the mistake `STALE` was added to fix; and not `UNKNOWN`
+either, which reports that nobody answered when the app of record answered
+precisely about its own log. Three wrong homes, which is why the values are
+five and not four.
+
+*This paragraph said `UNKNOWN` until 2026-09-06, citing suite §4.3 and
+GPT-6's finding 11 — correctly, for revision 7. Revision 10 replaces that
+ruling with `AHEAD`, so the citation stood while the thing it cited had
+moved. Until the module ships the fifth constant the CODE maps this case
+to `UNKNOWN`; the spec no longer pretends that is the design.*
+
+**Everything below `AHEAD` in this section is specified against the
+five-value design and cannot be built until the module ships it** — gate 6,
+§8.1's third breaker, §5.2's step 2 and §7.4's post-restore reconciliation
+all name a value the pinned `suite.Authorization` does not carry. That is
+one dependency with one date, not five open questions.
**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
@@ -2161,7 +2198,11 @@ a provider — and per §3.2, nothing leaves the instance in this mode.
sources page is rendered, so a reader never waits on them.
- **Wholesale `AHEAD` is a third breaker**, and it reads differently: not
- a mis-scoped predicate but a restore (§4.1). It states a restore, marks
+ a mis-scoped predicate but a restore (§4.1). **Unbuildable until the
+ contract module ships the fifth constant** — with the wire mapping the
+ case to `UNKNOWN`, this breaker cannot fire at all, and worse, it would
+ be indistinguishable from the outage this section says must *not* trip
+ one. §13 carries the dependency. It states a restore, marks
the source for re-announce, **takes that source out of the ranking**,
and purges nothing. It needs no baseline and no asker floor, because a
single `AHEAD` is already a positive statement that the sibling lost
@@ -3382,8 +3423,16 @@ commit. Fixed 2026-09-05; §1.9's lesson arriving inside the fix for §1.8.*
## 13. Open
-**Four items need Paul and are marked `NEEDS PAUL`. Nothing else here is
-a ruling; the rest is work, measurement or somebody else's repo.**
+**Four items need Paul and are marked `NEEDS PAUL`:** the 12-hour session
+TTL, §8.7's unit-override switch, the CI runner's repo list, and the
+disclosure route for the rastrillo SSRF (`SECURITY-DRAFT-rastrillo-signin.md`,
+unfiled). **Nothing else here is a ruling; the rest is work, measurement
+or somebody else's repo.**
+
+*Struck items are kept with their reasoning rather than deleted. Several
+closed on 2026-09-05 and 2026-09-06 when the suite doctrine landed, and in
+more than one case the doctrine chose an option the struck item had itself
+named — which is only visible if the item is still here.*
**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.**
@@ -3479,11 +3528,9 @@ a ruling; the rest is work, measurement or somebody else's repo.**
to keymail's host rather than to `?sent=1`, so the two paths still
differ for keymail-delegated domains. Narrower than the status quo it
replaced, and it closes when the upstream fix lands.
-- ~~**The sign-in dial (§3.2) needs a decision.**~~ *Superseded by the
- item above; the original text follows.* The fetch lives in
-- **`AHEAD` is memoria's and the contract has four values.** §4.1 records
- the disagreement in full. Draft 4 cut `AHEAD` because `STALE`,
+- **`AHEAD` is adopted and not yet shipped**, which is a date rather than
+ a question. §4.1 records how it was settled. Draft 4 cut `AHEAD` because `STALE`,
`NOT_VISIBLE` and `UNKNOWN` are each the wrong home for "the sibling is
behind"; `suite.Authorization` cannot express it, so memoria maps the
case to `UNKNOWN` today and loses exactly the distinction `AHEAD` was cut
@@ -3559,8 +3606,14 @@ on 2026-09-06 when the branches merged.
circuit-breaker fix renamed the problem rather than removing it — the
prose claimed a property of the sibling and the predicate still
measured whoever queried that day (§8.1).
-- **`NEEDS PAUL` — the roster-vs-sharing requirement is a BLOCKER of
- decision 1's class, and draft 4 filed it as a ticket.** §4.3 requires
+- ~~**`NEEDS PAUL` — the roster-vs-sharing requirement.**~~ **CLOSED
+ 2026-09-05, by the second of the two rulings it named.** It asked
+ either that the siblings adopt a member/guest distinction, or that
+ decision 11 be narrowed so admission does not rest on their answer.
+ Suite ruling 4 is the second: the home owns the roster, memoria pulls
+ it, and no sibling is asked about membership at all. The original
+ follows, because the reasoning is why ruling 4 is right and not merely
+ different — *draft 4 filed it as a ticket:* §4.3 requires
`MEMBER` to mean "on this team's roster"; Sheets §8.2 and Docs §8.2
both define sharing *as* membership, in those words. So §4.3 does not
ask the siblings to build a call they have not built — that is S1–S3
@@ -3573,7 +3626,10 @@ on 2026-09-06 when the branches merged.
does not rest on their answer. Until one lands, `S7` is
`INDETERMINATE` everywhere and only the configured owners are
admitted. Same threads as the decision-1 blocker below.
-- **No sibling can answer S7 yet.** The *implementation* is ordinary work
+- ~~**No sibling can answer S7 yet.**~~ **Moot 2026-09-05:** S7 is retired
+ and admission is the roster (§4.3). The original follows because the
+ *shape* of the problem — memoria depending on work in another repo it
+ cannot do — is now call 8's, unchanged. The *implementation* is ordinary work
in their repos, the same shape as S1–S3, once the ruling above settles
what `MEMBER` may mean. The two are separate items because they need
different things: one needs Paul, one needs a branch.
@@ -3612,7 +3668,10 @@ on 2026-09-06 when the branches merged.
reports "no anomaly" while doing so. If the record is unacceptable the
answer is to drop the breaker and say so, not to keep it in a form that
cannot work.
-- **15 minutes is chosen, not measured** (§4.3). It is a bet that one
+- ~~**15 minutes is chosen, not measured** (§4.3).~~ **Moot 2026-09-05:**
+ that was S7's cache TTL and there is no S7 cache. The roster has its own
+ TTL, set by the home, and ruling 3 forbids caching authorization at any
+ scope. The original follows. It is a bet that one
extra sibling call per person per quarter-hour is invisible beside the
S2 calls a query already makes; the latency measurement below settles
it, and if a cold S7 costs seconds the number moves.
@@ -3642,17 +3701,6 @@ on 2026-09-06 when the branches merged.
indexed and would be deciding on half the evidence. Worth noting in the
S1–S3 thread: this is one less thing to ask of Sheets, and the fixed
certification table is memoria's to build and gate 22's to hold.
-- **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.**
-- **`NEEDS PAUL` — the sign-in dial (§3.2).** 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,
- which is small but changes guest sign-in that slice 1 will want; or it
- belongs upstream in rastrillo. Paul's call.
- **Measure S6 and S2 latency against a cold sibling** before slice 1
ships. §6.5's 800 ms deadline is an assumption, and it decides whether
the primary search path degrades to permanent withholding.