oficina / memoria Public

🤖 spec: the search-page ruling survives, its argument does not

Fable's second pass, REQUEST CHANGES, 5 MAJOR / 4 MINOR / 1 NIT, all
applied. §1.6 records it. The ruling in decision 14 stands; almost
everything I said to justify it was wrong, and two of the errors were
misreadings of this spec.

The latency argument was false. I wrote that re-authorizing a transcript
each turn would breach §6.5's deadline. §4.1 caps a batch at 256 pairs and
§6.5 makes a page one round of concurrent calls with the floor at the
slowest sibling, not their sum — a four-turn transcript of eight passages
is 32 pairs, one batch, one round, the same floor as a fresh query. A
correct chat is affordable here. The sentence also conceded the point while
denying it: "the only correct version" means a correct version exists.

The staleness argument proved too much. "A chat transcript is a cache of
authorized passages" is equally true of a results tab left open, and of
/documents/{id}, which renders a whole document indefinitely. That is a
fact about rendered pages, so it moves to §3.2's leak list where it applies
to all of them.

What survives is narrower and enforceable: no passage crosses a request, in
a response or a model prompt, without §4.1 in the request that carries it.
Gate 26 asserts it against the prompt builder's input rather than against a
schema — a grep for "transcript" would be a gate written to pass, since the
leak needs no table. Gate 17 gains a literal route allowlist so a chat
route goes red on the way in.

The ruling is now recorded as the choice it is. Decision 14 rests on
decision 5, not decision 3, and is attributed to me with Paul unconfirmed —
QUESTIONS #2. A conversational surface that keeps the invariant is parked
in §12 to be argued, not forbidden by fiat.

Also corrected: "what slice 0 shipped" claimed §8.1's floor when it ships a
subset (no marker, no date, no pagination, and the route is / not /query);
decision 13's "two model ids" is three, and the embedding one costs a
corpus re-embed to swap; and "Paul ruled it need not block" put the
un-owing of the synthesis measurement in his mouth when he only accepted
the tier.

The lesson, and it is the third coat on one failure across §1.4–§1.6: a
preference will always find a rule that seems to compel it. Check the
constraint against the text, not against your memory of the text.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Paul Campbell pushed by paul@keymail.dev f80944a7feb0e0bca8fa5bb3b8799610a6b99ad7 parent c0acc9b
3 files changed, +211 −67
  • AGENTS.md +11 −6
  • QUESTIONS-FOR-PAUL.md +38 −8
  • docs/superpowers/specs/2026-09-03-memoria-design.md +162 −53
diff --git a/AGENTS.md b/AGENTS.md
index 758bce6..e7dd273 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -7,7 +7,7 @@ wins. Where it disagrees with the design spec, the spec wins and this file
is wrong — fix it in the same commit.
**The design:** `docs/superpowers/specs/2026-09-03-memoria-design.md`. Its
-§1 is the settled-decisions table, §1.2–§1.5 record what six independent
+§1 is the settled-decisions table, §1.2–§1.6 record what seven independent
reviews changed. Don't relitigate a settled decision in a drive-by.
---
@@ -118,11 +118,16 @@ so; it never renders as a confident zero.
Every spec and every branch is reviewed by a model that did not write it,
before it is approved or merged, and the reviewing model is recorded.
-Six passes so far: Fable and GPT Sol on spec drafts 1–3, Fable on the code,
-Fable on the §8.3 provider amendment. Every one returned REQUEST CHANGES.
-The code review was the most useful; the provider review found a rule
-broken in the paragraph that stated it, twice, and a fourth gate that could
-not have gone red (§1.5).
+Seven passes so far: Fable and GPT Sol on spec drafts 1–3, Fable on the
+code, Fable twice on the §8.3–§8.4 amendments. Every one returned REQUEST
+CHANGES. The code review was the most useful; the provider review found a
+rule broken in the paragraph that stated it, twice, and a fourth gate that
+could not have gone red (§1.5); the review after it found a product ruling
+argued from two misread sections of this same spec (§1.6).
+
+**The pattern across §1.4–§1.6 is one failure wearing three coats:** the
+writer checks their claim against their memory of the document rather than
+the document. Open the section you are citing.
---
diff --git a/QUESTIONS-FOR-PAUL.md b/QUESTIONS-FOR-PAUL.md
index 496adf3..8d01c03 100644
--- a/QUESTIONS-FOR-PAUL.md
+++ b/QUESTIONS-FOR-PAUL.md
@@ -74,9 +74,12 @@ can't be printed on an answer, then picked `kimi-k3` and
**Both questions this section asked are now answered** (2026-09-04):
- **Exactly one provider, TensorX** — *"if we want to swap that later we
- can."* Decision 13 records both halves. Swapping stays cheap in code (one
- client, one base URL, two model ids) and goes through §Review, which is
- where the two-parties question gets asked again.
+ can."* Decision 13 records both halves. Swapping *generation* is cheap —
+ one client, one base URL, two model ids — through §Review, where the
+ two-parties question gets asked again. Swapping *embeddings* is not: a
+ host serving the same open weights costs a migration, anything else costs
+ a re-embed of the whole corpus. Fable caught me writing "two model ids"
+ when the third is the expensive one.
- **Open-weight frontier is fine** — *"this is lightweight LLM inference
based on categorisation and memory. It's not complex reasoning."* That is
the right reading of §8.2 and I have put your words in §13. It also
@@ -100,7 +103,34 @@ probably a subprocessor, which is a DPA question and the DPA is unread.
Still does not bite until slice 2.
-## 2. Is routine partial withholding acceptable? (§6.5)
+## 2. Confirm: search page, not a chat (decision 14, §8.4)
+
+You asked whether we were building a chat UI or a hybrid. I answered
+*hybrid, search-first* — one GET search form, one query in, one page out,
+with an optional 🤖 paragraph above the citation list. §8.4 already implied
+it; it now says it.
+
+**But I first argued it as forced when it is chosen, and Fable caught me.**
+I claimed decision 3 forbade a chat outright — that re-authorizing a
+transcript would blow §6.5's latency budget. It would not: §4.1 batches 256
+pairs a call and §6.5 makes a page one round, so a four-turn transcript is
+one batch. A correct chat is affordable here. My other argument, that a
+transcript is a cache of authorized passages, is true of any page left open
+in a tab and so distinguishes nothing.
+
+**So the honest version:** the invariant decision 3 actually requires is
+that no passage crosses a request — not in a response, not in a model
+prompt — and gate 26 enforces it. The *shape* is decision 5's call: the
+search floor is the product, so the product is a search page. That is
+**decision 14, attributed to me and not to you.** A conversational surface
+that keeps the invariant is now parked in §12 as a thing to argue rather
+than a thing forbidden.
+
+**What I need:** confirm decision 14, or tell me you want the conversational
+version considered properly. Either is cheap today and neither is cheap
+after slice 2 ships an answer surface.
+
+## 3. Is routine partial withholding acceptable? (§6.5)
The genuinely new product question, and the one I would most like your
view on.
@@ -124,7 +154,7 @@ the source, never to cache its authorization. Tell me if you would rather
trade differently, because the alternative is a short-TTL cache and that
is a stated leak window rather than a correctness fix.
-## 3. Deploy it, or leave it local?
+## 4. Deploy it, or leave it local?
It has never run anywhere but localhost. Deploying creates a new app on
your `bds` CARLOS account, which is outward-facing provisioning I did not
@@ -133,7 +163,7 @@ want to do unattended. One command when you want it, in the README.
Local is genuinely usable — keymail accepts the loopback `client_id`, which
I verified.
-## 4. Your rastrillo CLI is about six releases stale, and it affects every app
+## 5. Your rastrillo CLI is about six releases stale, and it affects every app
`rastrillo` on PATH predates v0.22.0: it has no `doctor`, and
`rastrillo new` scaffolds a **v0.17.0** pin. Two things follow, and the
@@ -153,7 +183,7 @@ second is the one that matters beyond memoria:
would fix it, but it is a shared binary other sessions are using, so I
left it alone.
-## 5. An anonymous visitor can make the instance dial any domain
+## 6. An anonymous visitor can make the instance dial any domain
Found by the code review, and it is a real hole rather than a wording
problem. rastrillo's sign-in classifier fetches
@@ -175,7 +205,7 @@ before rastrillo sees it. The second is small and I can do it, but it
changes sign-in behaviour for guests, which slice 1 will want. Say which
you'd prefer, or whether it belongs upstream in rastrillo.
-## 6. Not a question, but you should know
+## 7. Not a question, but you should know
`gpt-5.6-sol` via Codex **reported its own model id as `gpt-5`** when
asked. Codex's own banner says `gpt-5.6-sol`. I recorded the discrepancy
diff --git a/docs/superpowers/specs/2026-09-03-memoria-design.md b/docs/superpowers/specs/2026-09-03-memoria-design.md
index f6e0556..c49bda9 100644
--- a/docs/superpowers/specs/2026-09-03-memoria-design.md
+++ b/docs/superpowers/specs/2026-09-03-memoria-design.md
@@ -3,7 +3,8 @@
The Oficina suite's memory. One instance per team, at
`<team>.memoria.oficina.works`.
-**Status:** draft 3, 2026-09-03; amended 2026-09-04 (§8.3 provider).
+**Status:** draft 3, 2026-09-03; amended 2026-09-04 (§8.3 provider, §8.4
+shape, decisions 13–14).
Review history, per AGENTS.md §Review. None of these models wrote the
draft they reviewed; all drafts were written by Opus.
@@ -14,6 +15,7 @@ draft they reviewed; all drafts were written by Opus.
| 2 | GPT Sol via Codex on a ChatGPT subscription — **it reported its own model id as `gpt-5`, which disagrees with the `gpt-5.6-sol` Codex labelled it; recorded as the discrepancy it is rather than resolved** | REQUEST CHANGES — 4 ANSWERED, 1 REWORDED, 6 PARTIAL, 1 blocker and 6 new findings |
| 3 | Fable | REQUEST CHANGES — 22 findings. (This row read "none yet" while §13 recorded the review as done; §13 was right.) |
| 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 |
§1.2 records what the draft-1 reviews changed; §1.3 what the draft-2
review changed. **Draft 3 has not been reviewed.**
@@ -68,7 +70,8 @@ embedding is a plaintext cell in a costume — §3.1.
| 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 |
| 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 is a code change and a review, not a setting |
+| 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. Paul has not confirmed it** — QUESTIONS #3 |
### 1.1 The proposal review, and what it killed
@@ -270,6 +273,49 @@ application should be reviewed by different people.** Whoever writes the
rule has just persuaded themselves, and is the worst-placed reader to
notice that the very next paragraph fails it.
+### 1.6 What the second provider review changed
+
+The §8.4 amendment — *memoria is a search page, not a chat* — was reviewed
+by Fable, which returned REQUEST CHANGES with five MAJOR findings. The
+ruling survived; the argument for it did not, and the way it failed is
+worth more than the ruling.
+
+**The case was built on a misreading of this spec.** §8.4 claimed a chat
+could not be built correctly, because re-authorizing a transcript each turn
+would breach §6.5's deadline. §4.1 caps a batch at **256 pairs per call**
+and §6.5 says **one page is one round of concurrent calls, the floor is the
+slowest sibling and not their sum**. A four-turn transcript of eight
+passages is 32 pairs: one batch, one round, the same floor as a fresh
+query. A correct chat is affordable. The draft had also conceded the point
+in the act of denying it — *"re-authorizing the whole transcript on every
+turn is the only correct version"* says a correct version exists.
+
+**Its other argument proved too much.** *A chat transcript is a cache of
+authorized passages* — true, and equally true of a search results page left
+open in a tab, and of `/documents/{id}`, which renders a whole document and
+stays open indefinitely. That is a fact about rendered pages, not about
+chat; it now sits in §3.2's leak list where it applies to everything.
+
+**What survived is narrower and enforceable.** The real rule is about
+**re-speaking**: no passage crosses a request, in a response or in a model
+prompt, without §4.1 in the request that carries it. That is decision 3
+applied to rendering, it is what §8.5 already said about the model, and
+gate 26 asserts it against the prompt builder. Carrying query *text*
+forward breaks nothing.
+
+**And the shape is a choice, so it is recorded as one.** Decision 14 rests
+on decision 5 — the search floor is the product — not on decision 3. A
+conversational surface that keeps the invariant is deferred to §12, to be
+argued on its merits. The row is attributed to the spec author with Paul
+unconfirmed, because unlike decisions 13's two halves it is not his ruling.
+
+The lesson, distinct from §1.4's unfailable gates and §1.5's rule broken in
+the paragraph that states it: **a preference will always find a rule that
+seems to compel it.** The search page was the right answer for
+decision 5's reasons. Reaching past those for decision 3 made a choice look
+forced, and cost two misread sections to do it. When a constraint arrives
+conveniently, check it against the text rather than the memory of the text.
+
---
## 2. What memoria is
@@ -345,6 +391,15 @@ is a truncation of a native 4096. Either way the stored width names a
choice of ours. It narrows the candidate models; it does not name one. Do
not lean on that.
+**From a rendered page.** A response body is a copy in someone's browser,
+and no revocation reaches it. A results tab left open on Tuesday still
+shows Tuesday's authorized snippets on Friday; `/documents/{id}` shows a
+whole document for as long as it stays open. This is true of every surface
+memoria has or could have, so it is not an argument against any particular
+one (§8.4 tried to use it as one and was wrong). memoria's guarantee ends
+at the response body, and §8.4's invariant is what keeps the *next*
+response honest.
+
**From a live host — the largest entry, and new.** A compromised memoria
host reaches the team's corpus across every app at once, plus §5.1's
connector credentials. §3.3 works through how this compares to Sheets;
@@ -1190,9 +1245,16 @@ is narrower than decision 4's "fixed by the team owner", and deliberately:
a configurable base URL on an OpenAI-compatible client would let an owner
point memoria at a proxy while the answer still printed "one named
provider", which is the failure this whole section is about. Decision 13
-records it. Swapping providers later stays cheap — one client, one base
-URL, two model ids — but it goes through code and §Review, where the
-two-parties question gets asked again.
+records it.
+
+**Swapping later is cheap on one side and not the other.** Generation is
+one client, one base URL and two model ids, through code and §Review, where
+the two-parties question gets asked again. **Embeddings are a third id and
+the expensive one:** a new host that serves the same open `qwen3-embedding-8b`
+weights yields compatible vectors and costs a migration, and any other host
+costs a re-embed of the whole corpus — 24M tokens at an unmeasured
+wall-clock (§13). Saying "swapping stays cheap" without that split would
+contradict §13 two pages later.
**Why this one, honestly stated.** Not because it beats OpenRouter and
OpenAI on decision 4's own words — on one half of them it loses:
@@ -1261,40 +1323,71 @@ list; every sentence carries a passage id from it, and a sentence citing
nothing is dropped before render. **If every sentence is dropped, say
so** — an empty paragraph is indistinguishable from AI being off.
-**This is a search page, not a chat.** The spec implied it everywhere and
-stated it nowhere, which is how a chat box gets added by someone reading
-§8.2's "frontier model" and reaching for the familiar shape. Written down:
-one GET form, `?q=`, one query in and one page out. No turns, no history,
-no thread, no follow-up that refers to "it". §8.1's floor is already this
-page, and it is what slice 0 shipped.
-
-**The reason is decision 3, not taste.** Authorization is asked of the
-owning sibling per person *per query*, live, never cached, because caching
-authorization is what makes a stale answer a leak. **A chat transcript is a
-cache of authorized passages.** Turn 1's snippets sit on screen while turn
-4 is asked; their grants were evaluated minutes ago; a revocation in
-between is invisible, and the page is now doing exactly what decision 3
-forbids — with the added twist that the passages are on the reader's screen
-rather than in a database, so no purge reaches them. Re-authorizing the
-whole transcript on every turn is the only correct version, and it makes
-each turn cost the previous turns' S2 calls again, against §6.5's 800 ms
-deadline. The shape does not survive its own rule.
-
-Two consequences worth stating, since both are places a chat UI would have
-been the easy answer:
-
-- **A refined search is a new query, not a follow-up.** The affordance is
- editing `?q=` and asking again. Nothing carries over, and nothing needs
- to, because retrieval is free (§8.2).
-- **The 🤖 paragraph is disposable.** It is one paragraph above one list,
- regenerated per query and stored nowhere. Nothing accumulates that §5.2's
- provenance rule would then have to expire, and no runtime record of
- prompts exists to fall under §8.3's ledger rule.
-
-Hybrid, then, in the only sense that matters: **the search page is the
-product and the answer is a garnish on it** (decision 5). Not a chat with
-citations bolted on, which is the same components in the order that breaks
-the authorization rule.
+#### The invariant, which is the part that binds
+
+> **Every passage in a response, and every passage in a model prompt,
+> passed §4.1 in the request that produced it. Nothing memoria holds, and
+> nothing it accepts from the client, carries a passage across requests.**
+
+This is decision 3 applied to rendering, and §8.5 already states its other
+half — *"authorization runs before the model exists"*. It is a rule about
+what may enter a response or a prompt **per request**. It says nothing
+about interface shape, and it is enforceable: gates 17 and 26.
+
+What it forbids is **re-speaking**. A surface that puts previously
+retrieved passages back into a model prompt, re-renders them server-side,
+or stores them in a transcript row is memoria speaking those passages
+again, at a moment when nobody has asked whether this person may still see
+them. Carrying *query text* forward is not re-speaking and is not
+forbidden: retrieval and S2 then run from scratch, which is the whole point
+of retrieval being free (§8.2).
+
+**An earlier draft of this section argued the case badly, and the argument
+is worth recording as wrong.** It said a chat transcript is a cache of
+authorized passages because turn 1's snippets sit on screen while turn 4 is
+asked. That proves too much: it is equally true of a search results page
+left open in a tab, and of `/documents/{id}`, which renders a whole
+document's passages and stays open as long as the reader likes. **A
+rendered page is a copy in a browser and memoria's revocation never reaches
+it** — true of every page here, chat or not, and now recorded as such in
+§3.2 rather than argued from here. The draft also claimed re-authorizing a
+transcript would breach §6.5's deadline. It would not: §4.1 batches 256
+pairs per call and §6.5 makes a page one round of concurrent calls, so a
+four-turn transcript of eight passages is 32 pairs — one batch, one round,
+the same floor as a fresh query. **A correct chat is affordable under this
+spec.** The invariant, not the latency, is what does the work.
+
+#### The shape, which is a product ruling and not a consequence
+
+**memoria is a search page: one GET search form, `?q=`, one query in and
+one page out**, with `/documents/{id}` as the only other passage surface.
+That follows from decision 5 — the search floor *is* the product — and not
+from decision 3, which a conversational surface could satisfy. Recorded as
+decision 14. **It is the spec author's ruling, in answer to a question Paul
+asked on 2026-09-04, and Paul has not confirmed it** — QUESTIONS #3. A
+conversational surface that keeps the invariant above is not forbidden by
+this section; it is deferred by §12, where it can be argued on its merits
+rather than smuggled in as a corollary.
+
+Two things this settles for now:
+
+- **A refined search is a new query.** Edit `?q=` and ask again; nothing
+ carries over, and nothing needs to.
+- **The 🤖 paragraph is disposable — today.** One paragraph above one list,
+ regenerated per query, stored nowhere; it is not a §5.2 derived artefact
+ and no runtime prompt record exists to fall under §8.3's ledger rule.
+ **Both halves expire if §12's cached-answers or model-call-ledger rows
+ trigger**, and this paragraph changes with them.
+
+**What slice 0 actually shipped is a subset of §8.1's floor**, not the
+floor: `index.html` renders source, title, ordinal, snippet and deep link,
+but **no 👨/🤖 marker, no date, and no pagination** — `Search(ctx, q, 25)`
+is a hard cap and nothing renders a second page. §9.1 also specifies
+`GET /query?q=` where the app serves `GET /?q=`. Three gaps and a path,
+listed rather than glossed.
+
+Hybrid, then, in the sense that matters: **the search page is the product
+and the answer is a garnish on it** (decision 5).
### 8.5 Prompt injection
@@ -1510,6 +1603,12 @@ renders on its own origin. The home never sees `q`.
17. **No sibling-facing endpoint exists.** Enumerate memoria's routes and
fail any inbound route not on the person-facing or connector list —
the regression gate for the push surface draft 1 shipped by accident.
+ **The list is literal, not descriptive:** `GET /`, `GET /documents/{id}`,
+ `POST /documents`, `POST /documents/{id}/delete`, plus the auth routes.
+ Written that way so that adding a thread or chat route goes red on the
+ way in (decision 14) rather than being argued about afterwards.
+ Mutation: register `POST /chat`, expect red. **Not built** — nothing in
+ `internal/memoriatest` walks the router yet.
18. **External-source authorization and disclosure.** A memoria-owned
document is governed by memoria's grants, an upstream revocation does
*not* remove it, and the copy the README promises is deletable is
@@ -1546,6 +1645,13 @@ renders on its own origin. The home never sees `q`.
4xx can echo the input that caused it, so a provider error path is a
content path. Gate 4 covers our own log lines; this covers the bytes
the provider hands back.
+26. **Every passage id in a model prompt is one S2 returned `VISIBLE` for
+ in this same request.** Asserted on the prompt builder's input, not on
+ a table name or a schema grep — a grep for "transcript" would be a gate
+ written to pass, since the leak does not need a table. This is §8.4's
+ invariant and the only thing that stops a future surface re-speaking a
+ revoked passage. Mutation: hand the prompt builder one passage from a
+ previous request's result set, expect red. Slice 2.
---
@@ -1595,7 +1701,8 @@ rows.
| A feed surface on memoria | Something needs to consume memoria. A new decision, with its own leak-list entry |
| 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 |
+| Cached answers | Query cost becomes visible. Requires §5.2's provenance invalidation first. **Triggering this expires §8.4's "stored nowhere"** |
+| A conversational surface | Someone wants one, and it keeps §8.4's invariant: no passage crosses a request, in a prompt or a response. Decision 14 chose the shape on decision 5's grounds, not decision 3's, so this is a product argument to have — not a rule to break. §6.5 affords it: a four-turn transcript re-authorizes in one batch, one round |
| A runtime model-call ledger | Someone needs to audit routing. Sealed and provenance-stamped when it lands (§8.3) |
| A local embedding model | Would remove ingest egress entirely, which §3.2 makes the largest single leak. **Not the cgo-free in-process runtime this row first imagined:** §8.3's `qwen3-embedding-8b` is open-weight, so self-hosting is possible in principle, but 8B parameters means a second box to run and keep, not a library linked into the one binary |
| Cross-team anything | Never, under §6.4 |
@@ -1719,19 +1826,21 @@ rows.
- **§7.2's 24M tokens is now an estimate under a different tokeniser.** It
came from an OpenAI-style ~1.34 tokens/word; billing is Qwen's. Probably
close, unverified, and §8.6 inherits it.
-- **Answer quality for the synthesis class is unmeasured, and Paul has
- ruled it need not block.** *"Frontier open weight is fine. This is
- lightweight LLM inference based on categorisation and memory. It's not
- complex reasoning."* (2026-09-04.) That is the right reading of §8.2: the
- three model-free classes carry the navigational, numeric and
- who-knows-about-X work, §8.7 sends arithmetic to Sheets, and what is left
- for a model is extractive summary over passages that have already been
- retrieved, filtered and authorized. **It also raises a question this spec
- should answer rather than inherit:** if the work is not complex
- reasoning, does §8.2 need two tiers at all, or is the frontier tier a
- habit? Measure the cheap tier on the synthesis class before building the
- expensive one — the router's cost argument (§8.6) gets stronger, not
- weaker, if the answer is one tier.
+- **Answer quality for the synthesis class is unmeasured, and the
+ measurement is still owed before slice 2** (§11). What Paul settled on
+ 2026-09-04 is the *product call* — *"Frontier open weight is fine. This
+ is lightweight LLM inference based on categorisation and memory. It's not
+ complex reasoning."* — not the obligation to measure it, which came from
+ the code review and stands. It reads §8.2 right: three of five classes
+ call no model, and §8.7 sends arithmetic to Sheets, so a model sees only
+ what survives retrieval, filtering and authorization.
+- **Does §8.2 need two tiers at all?** The spec's own question, not Paul's.
+ If the residual work is as light as decision 14's shape suggests, measure
+ the cheap tier on the synthesis class *before* building the expensive
+ one; §8.6's cost argument gets stronger, not weaker, if the answer is one
+ tier. Note the premise is not free: §8.2 calls the single-source class
+ extractive but synthesis spans ≥3 documents, which is the one place the
+ work is genuinely more than extraction.
- **Docs §11.2's qualification** must be recorded in the Docs repo by
`docs-e6`, or the two specs contradict.
- **A `carlos/platform` discussion** noting the app boundary is the