| index 1a5f0a9..2de0391 100644 |
| --- a/docs/superpowers/specs/2026-09-03-memoria-design.md |
| +++ b/docs/superpowers/specs/2026-09-03-memoria-design.md |
| @@ -108,7 +108,7 @@ embedding is a plaintext cell in a costume — §3.1. |
| | 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 | |
| -| 15 | **The fact box calls no model, ever.** A validated fact — eircode, phone, IBAN, VAT, email — is extracted deterministically and shown above the results with its citation. A kind with no validator is not a kind. §8.8 | A cheap extractive model picking the span (§8.7's retrieval-augmented calculator with a different noun); a model composing the label; postal address and `url` as kinds, neither having anything to validate | **The constraint is Paul's, 2026-09-14** — *"do as much deterministic ordering of the data before we go doing inference on it"*, asking for the box in the first place. **The "ever", the validator rule, the kind list and the postal exclusion are the spec author's reading of it and are unconfirmed — §13.** Decision 14 is the form this follows: record which half was ruled and by whom, rather than letting the interpretation live in a commit message (§1.9) | |
| +| 15 | ~~**The fact box calls no model, ever.**~~ **AMENDED BY PAUL, 2026-09-19.** The rule is **deterministic first, then a model for what deterministic analysis cannot reach**: *"do as much as possible with deterministic analysis, and then attempt to build metadata with the best value models via TensorX that make sense."* A validated fact is still extracted by recogniser and shown with its citation; a kind with **no** validator — a postal address — is no longer excluded, it is the model's half. §8.8, §8.9 | The strict reading, which was the spec author's and not Paul's, and which this row carried for five days: it foreclosed the obvious next request and made "has a checksum" the boundary of the feature rather than the boundary of the deterministic layer. **What is NOT amended:** a model never picks among validated candidates, never composes a label, and never produces a value that is not a verbatim span of a cited passage | **Paul, 2026-09-19**, ruling on the question §13 had recorded as open since 2026-09-14. The strict version was mine; he reversed it, and the reversal is the general rule and not a carve-out for addresses | |
| |
| **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 |
| @@ -2699,7 +2699,11 @@ frontier tier — §8.2's three model-free classes remain the design, and this |
| section's closing sentence still holds. |
| |
| Derived-artefact regeneration (§5.2) adds a synthesis-class call per |
| -invalidated artefact and is unbudgeted until the feature exists. |
| +invalidated artefact and is unbudgeted until the feature exists. **So does |
| +§8.9**, at one cheap-tier call per query that names a valueless kind and |
| +whose recognisers found nothing — a small fraction of a small fraction, |
| +and cheap-tier by rule rather than by choice, since a task checked by |
| +string comparison does not improve with a better model. |
| |
| The money is recurring query tokens by an order of magnitude — the |
| economic argument for §8.2's router and its three model-free classes. |
| @@ -2933,12 +2937,22 @@ class would have given one. The box is attempted, and then §8.2 routes the |
| query exactly as it would have. Paul's constraint is deterministic |
| **first**, not deterministic **instead**. |
| |
| -It does not touch §3.2. No egress, no provider, no embedding — the box |
| -works on an instance with AI switched off, which is decision 5's floor and |
| -therefore the whole product today. It is part of §8.1's floor and gate 15 |
| -covers it. **Nothing on this path is logged**, gate 4's rule applying |
| -unchanged: a fact, its label and its kind are all content, and the query |
| -that named the kind is content too. |
| +**This section's own path touches nothing in §3.2.** No egress, no |
| +provider, no embedding — the deterministic box works on an instance with |
| +AI switched off, which is decision 5's floor and therefore the whole |
| +product today. It is part of §8.1's floor and gate 15 covers it. |
| +**Nothing on this path is logged**, gate 4's rule applying unchanged: a |
| +fact, its label and its kind are all content, and the query that named |
| +the kind is content too. |
| + |
| +**That claim is now load-bearing in a way it was not, because decision 15 |
| +has been amended and §8.9 adds a model.** What survives the amendment |
| +without qualification is everything in this section: recogniser, |
| +validator, label, cap, collision, citation. What §8.9 adds sits *after* |
| +all of it and is switched off with AI. The sentence to keep straight is |
| +**"the deterministic box has no egress"**, not "the fact box has no |
| +egress" — and §8.9 exists as its own section rather than a paragraph here |
| +precisely so that the two cannot be confused by someone reading quickly. |
| |
| #### One extraction, over the ruled results, and why drafts 1 and 2 had two |
| |
| @@ -3396,6 +3410,153 @@ natural anyway. Drafts 1 and 2 both said this gate could run against |
| that is the renamed-test failure §10 opens with, committed twice in the |
| section that cites it. |
| |
| +### 8.9 The model half: metadata deterministic analysis cannot reach |
| + |
| +Paul's ruling, 2026-09-19, amending decision 15: *"do as much as possible |
| +with deterministic analysis, and then attempt to build metadata with the |
| +best value models via TensorX that make sense."* |
| + |
| +The question put to him was narrow — should a postal address, which has |
| +no checksum and therefore no validator, be extractable by a cheap model? |
| +**The answer he gave is general**, and this section records the general |
| +form rather than the carve-out: the deterministic layer is the floor and |
| +not the ceiling, and where it cannot reach, a cheap model may — under |
| +conditions that keep every guarantee §8.8 establishes. |
| + |
| +**Not built. This is slice 2**, because nothing in this binary calls a |
| +provider before slice 2 and gate 31 enforces it. What lands now is the |
| +ruling and its conditions, so that the section is written before the code |
| +rather than after — which is the order §1.9 exists to insist on. |
| + |
| +#### The ordering, which is the whole of the ruling |
| + |
| +1. **Recognisers run first, always.** If a validated candidate exists for |
| + a wanted kind, §8.8 answers and no model is called. The deterministic |
| + answer is never second-guessed, never re-ranked, and never handed to a |
| + model for an opinion. |
| +2. **A model runs only where the deterministic layer produced nothing**, |
| + for a kind that has no validator to produce it with. |
| +3. **With AI off, step 2 does not happen and step 1 is unchanged.** |
| + Decision 5's floor is intact: the search page and §8.8's box are the |
| + product, and this section is a garnish on a garnish. |
| + |
| +**The failure this ordering prevents is a model improving on a checksum.** |
| +A mod-97 check is right or wrong; a model asked to "find the IBAN" in the |
| +same passage can be neither. Letting it near a kind that validates would |
| +trade a provable answer for a plausible one, which is the direction §8.7 |
| +spends a whole section refusing. |
| + |
| +#### The condition that makes it safe: the model proposes, a string |
| +#### comparison disposes |
| + |
| +**Every value the model returns must appear verbatim in one of the |
| +passages that were sent to it, and memoria checks that itself.** Not |
| +"should"; the check is a `strings.Contains` against passages memoria |
| +already holds, and a value that fails it is dropped without being |
| +rendered. |
| + |
| +This is the load-bearing sentence of the section, and it is what makes |
| +the rest of §8.8 survive intact: |
| + |
| +- **A hallucinated address cannot reach the page.** Not because the model |
| + is good, but because a string that is not in the corpus fails a string |
| + comparison. The model's job is reduced to *pointing*, and pointing is |
| + checkable. |
| +- **§8.8's display rule is unchanged**: the span shown is the document's |
| + bytes, because it is located in the document by the check above and |
| + sliced from there. The model's own output text is never rendered — it is |
| + a lookup key and then it is discarded. |
| +- **The label is still the document's**, derived by §8.8's five rules from |
| + the span's position once located. **A model never writes a label**, and |
| + this is not a stylistic preference: a composed label is the one part of |
| + the box that no string comparison can verify. |
| +- **Gate 14 and §8.4 hold without amendment.** Every character of the box |
| + still traces to a cited passage, and the citation is the passage the |
| + span was found in. |
| + |
| +**Ambiguity, the cap and collision are §8.8's, unchanged.** If the model |
| +points at three addresses, three render; at four, none. It never picks, |
| +for the same reason the deterministic layer never picks — rank order |
| +carries no claim about which address is the office one, and a model's |
| +preference carries less. |
| + |
| +#### Egress, and what is not new about it |
| + |
| +**Passages go to TensorX at query time.** That is the same egress §8.3 |
| +already describes for the 🤖 paragraph: the same provider, the same |
| +passages, the same moment in the same request, after §4.1 has ruled. It |
| +is **not a new entry on §3.2's leak list** and should not be written as |
| +one — the leak is "query-time passages reach the named provider when AI |
| +is on", and it exists the moment slice 2 does. |
| + |
| +What *is* new is the trigger. §8.2's router sends passages when a query |
| +needs an answer; this sends them when a query named a kind and |
| +deterministic extraction found nothing. A team with AI on now has a |
| +second reason for a request to leave the instance, and §8.3's disclosure |
| +copy should say so in the same breath as the first. |
| + |
| +**Model and tier:** `model.cheap` (§8.2, §8.3 — `deepseek/deepseek-v4-flash-0731` |
| +at the time of writing), because this is extraction from a handful of |
| +passages and §8.2 already classes that as the cheap tier's work. **Never |
| +the frontier tier**, whatever the kind: a task whose output is checked by |
| +string comparison does not improve with a better model, it only costs |
| +more. |
| + |
| +**Cost** is §8.6's to absorb and is unbudgeted until this is built. The |
| +shape: one cheap call per query that names a valueless kind and finds |
| +nothing, which is a small fraction of a small fraction. |
| + |
| +#### Injection |
| + |
| +§8.5 is unchanged and its mitigation is the same partial one. A poisoned |
| +document can make the model point at an attacker's string, and the verbatim |
| +check will pass — because the string really is in the corpus. What bounds |
| +it is what bounds §8.8: the passage passed §4.1, the citation is openable, |
| +and somebody in this team put the document there. **The check defends |
| +against the model inventing; it does not defend against the corpus lying**, |
| +and those are different problems with different answers. |
| + |
| +#### What "that make sense" leaves open, and it is Paul's |
| + |
| +The ruling names a principle and one instance. **Which metadata is worth |
| +building this way is a product judgement and §13 records it as his**, not |
| +because the spec is being timid but because the answer is a list that will |
| +grow and each entry costs a query-time model call. |
| + |
| +The first entry is **postal address**, which is what he was asked about |
| +and which §12 no longer defers. Plausible next ones, none of them ruled: |
| +opening hours, a person's role or title, a document's effective date, a |
| +company registration number in a jurisdiction whose format memoria does |
| +not know. **The test for admitting one is not "could a model do it" — a |
| +model could do all of them — but "is the answer a verbatim span somebody |
| +would want cited".** Anything that needs summarising is not metadata, it |
| +is an answer, and §8.2's router already owns answers. |
| + |
| +#### Gate |
| + |
| +**39. A value the model returns that is not in a sent passage never |
| +renders** (§8.9). Three fixtures against a stubbed provider, and the |
| +first is the presence half: |
| + |
| +- the stub returns a span that **is** in a passage → the box renders it, |
| + with the document's bytes and the document's label; |
| +- the stub returns a plausible address that is **in no passage** → **no |
| + box**, and nothing is logged that contains it (gate 4, and a provider's |
| + response body is content — gate 31); |
| +- the stub returns a span that is in a passage belonging to a document |
| + **§4.1 withheld from this reader** → no box. The passages sent are the |
| + ruled ones, so this should be unreachable; the fixture exists because |
| + "unreachable" is what §1.9 says to test rather than assume. |
| + |
| +Mutation: render the model's own output text rather than locating it and |
| +slicing the document, and fixture 2 goes red. Send the unruled passages |
| +and fixture 3 goes red. **And the deterministic-first ordering needs its |
| +own:** stub the provider to return a *different* phone number from the |
| +one the recogniser validated, and assert the recogniser's answer is what |
| +renders and **that the provider was never called at all** — a gate that |
| +checks only the rendered value cannot tell "called and ignored" from |
| +"never called", and the first of those is an egress nobody authorised. |
| + |
| --- |
| |
| ## 9. Surfaces |
| @@ -4172,7 +4333,8 @@ 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. *No Docs §11.2 qualification is |
| +answer-above-citations, **§8.9's metadata half and gate 39**, 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**, |
| @@ -4215,7 +4377,7 @@ commit. Fixed 2026-09-05; §1.9's lesson arriving inside the fix for §1.8.* |
| | 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 | |
| | **Reaching a fact the ranked query does not** | Somebody reports a fact they can see in a document the box did not surface. §8.8's candidates are the ruled top-25, so an eircode in a document that never says "eircode" is found only if something else about it ranks. **Whatever this turns out to be, it is not "index the kind words"** — §8.8 measures that collapsing `bm25()` for the injected term from 2.7169 to 0.0000 | |
| -| **Postal address as a fact kind** | Decision 15 keeps it out because there is nothing to validate. The two openings are an anchored one — an eircode certifies the lines above it, deterministically — and §8.2's extractive single-source class, which is a model and therefore a decision-15 amendment | |
| +| ~~**Postal address as a fact kind**~~ | **PROMOTED 2026-09-19 to §8.9, slice 2.** Decision 15's amendment makes "nothing to validate" the reason a model handles it rather than the reason it is excluded. Kept struck rather than deleted, because the row's reasoning is what the amendment overturned | |
| | **A durable, sealed facts table** | The per-query extraction shows up in a profile. §8.8 rejects it today on derived-state grounds and because it would freeze the recognisers at ingest — **not** on leak grounds, which is what draft 1 argued and which attacked an unsealed design nobody proposed. Note the direction: storing facts makes recogniser improvements *harder* to ship, so this row gets triggered by cost and never by correctness | |
| | 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 | |
| @@ -4251,6 +4413,20 @@ the rest is work, measurement or somebody else's repo.** |
| nothing. Defensible, and recorded here for the same reason decision |
| 15's widening is: §1.9's lesson is that the load-bearing half of a |
| decision must not live only in a commit message. |
| + - **~~Decision 15 records more than Paul ruled.~~ CLOSED 2026-09-19, |
| + and he reversed it.** The strict reading — no model, ever — was the |
| + spec author's. Paul ruled the opposite and gave the general rule: |
| + deterministic as far as it goes, then a cheap model for what it |
| + cannot reach. §8.9. **What replaces it as open is narrower and is |
| + his:** *which* metadata is worth a query-time model call. Postal |
| + address is ruled in; opening hours, a role, an effective date, a |
| + foreign company number are all plausible and none is ruled. The spec's |
| + proposed test is "is the answer a verbatim span somebody would want |
| + cited" — anything needing summary is an answer, not metadata, and |
| + §8.2 already owns answers. |
| + - **The struck text is kept below**, because what the amendment |
| + overturned is only legible beside what it replaced. |
| + |
| - **Decision 15 records more than Paul ruled, and says so rather than |
| quietly widening.** What he asked for on 2026-09-14 is the box, and |
| the constraint — *"do as much deterministic ordering of the data |