| index afbe6b8..0e66473 100644 |
| --- a/docs/superpowers/specs/2026-09-03-memoria-design.md |
| +++ b/docs/superpowers/specs/2026-09-03-memoria-design.md |
| @@ -3,7 +3,7 @@ |
| The Oficina suite's memory. One instance per team, at |
| `<team>.memoria.oficina.works`. |
| |
| -**Status:** draft 3, 2026-09-03. |
| +**Status:** draft 3, 2026-09-03; amended 2026-09-04 (§8.3 provider). |
| |
| Review history, per AGENTS.md §Review. None of these models wrote the |
| draft they reviewed; all drafts were written by Opus. |
| @@ -12,7 +12,8 @@ draft they reviewed; all drafts were written by Opus. |
| |---|---|---| |
| | 1 | GPT Sol (commandcode), Fable | REQUEST CHANGES ×2 — 12 and 21 findings, three structural gaps found by both | |
| | 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 | none yet | — | |
| +| 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 | |
| |
| §1.2 records what the draft-1 reviews changed; §1.3 what the draft-2 |
| review changed. **Draft 3 has not been reviewed.** |
| @@ -67,6 +68,7 @@ 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. **The "supports exactly one" half is the spec's reading of decision 4's "fixed by the team owner" and needs Paul's confirmation** — §13 | |
| |
| ### 1.1 The proposal review, and what it killed |
| |
| @@ -224,6 +226,50 @@ not fail for the thing they named.** A gate is a claim about the world, |
| and an unmutated gate is an unverified claim — including, twice on this |
| branch, a gate whose subject had been deleted while it stayed green. |
| |
| +### 1.5 What the provider review changed |
| + |
| +The §8.3 amendment — OpenRouter out, TensorX in — was written by Opus and |
| +reviewed by Fable, which returned REQUEST CHANGES with four MAJOR findings. |
| +Two of them are the same mistake in different clothes, and it is the |
| +mistake §1.4 is about: **stating a rule and then breaking it in the |
| +paragraph that states it.** |
| + |
| +**§8.2 wrote a pinning rule its own selections failed.** The rule — a model |
| +id that can move under a team without the team changing it cannot be |
| +printed on an answer — is right, and generalises `openrouter/auto`'s |
| +disqualification properly. Then it selected `moonshotai/kimi-k3` and |
| +`z-ai/glm-5.3-flash`, neither datestamped, both plausibly aliases over |
| +dated checkpoints. The fix is a datestamped id where one exists, gate 24's |
| +canary where none does, and §13 recording that the vendor's id-immutability |
| +policy is unread. The draft had even quoted the one datestamped id in the |
| +catalogue as evidence — and it was the model it did not pick. |
| + |
| +**§8.3 claimed as established what §13 called untested**, three paragraphs |
| +apart. Worse, the verification it proposed could not have established it: |
| +the live call settles whether one model *name* is proxied, not whose GPUs |
| +run the weights. "One contractual counterparty" is not "one organisation |
| +sees the corpus" — a subprocessor sees content in §3.2's sense — so the |
| +claim is now written as narrowed rather than closed. |
| + |
| +**§7.2 specified a gate that could not fail.** It said a mutation of the |
| +4096→512 renormalise would be caught by watching recall fall. But cosine |
| +similarity is scale-invariant: against a true cosine, skipping the |
| +renormalise changes no ranking, and the gate stays green forever. The spec |
| +had never said whether search computes cosine or a raw dot product. It now |
| +says dot product — which is what §7.3's flop count always assumed — and |
| +gate 22 asserts unit norm deterministically instead of inferring it from |
| +quality. This is the fourth gate in this project to have been written in a |
| +shape that could not go red, and the first caught before it was built. |
| + |
| +**Decision 13 exists because of this review.** The amendment named a |
| +provider in §8.3 without recording the ruling in §1, which AGENTS.md says |
| +is how a settled decision gets relitigated in a drive-by. |
| + |
| +The general lesson, distinct from §1.4's: **a rule and its first |
| +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. |
| + |
| --- |
| |
| ## 2. What memoria is |
| @@ -292,7 +338,12 @@ edges alone reveal which passages resemble each other. |
| **From the bucket:** document and chunk counts; sealed blob sizes; source |
| app and coordinates per chunk, plaintext exactly as Sheets §2.2 keeps |
| coordinates; ingest and update timestamps, therefore which sources are |
| -busy and when; embedding dimension, therefore the model. |
| +busy and when; embedding dimension — though "therefore the model", as this |
| +line read until draft 3, overstated it even then: the design has always |
| +stored 512, which under OpenAI was a requested `dimensions` and under §8.3 |
| +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 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 |
| @@ -878,8 +929,43 @@ wrong by about 4×. The ceiling is a chunk count and only a chunk count. |
| The token figure follows from the measured chunk (202 words ≈ 270 tokens), |
| not the 400-token target: **24M, not the 36M draft 3 carried into §8.6.** |
| |
| -**Vectors.** 512-dim Matryoshka embeddings (OpenAI's `-3` models accept a |
| -`dimensions` parameter), stored **int8-quantised**: |
| +**Vectors.** 512-dim Matryoshka embeddings, stored **int8-quantised**. |
| + |
| +**512 is a client-side truncation, not a requested dimension.** The |
| +parenthetical this replaced — *"OpenAI's `-3` models accept a `dimensions` |
| +parameter"* — is true of OpenAI and false of the provider §8.3 selects. |
| +`qwen/qwen3-embedding-8b` emits **4096 fixed** and *fails the request* if |
| +`dimensions` is sent. The pipeline therefore keeps the first 512 values and |
| +**re-normalises to unit L2**. That is valid because the model is trained |
| +Matryoshka. |
| + |
| +**Search computes a raw dot product over vectors stored unit-normalised**, |
| +not a cosine that divides by norms at query time. §7.3's `2 × N × D` flop |
| +figure already assumed this and the spec never said so. It matters here: |
| +**cosine is scale-invariant, so against a true cosine a skipped renormalise |
| +changes no ranking at all** — a gate written over cosine could not fail, |
| +which is the shape AGENTS.md says this codebase has already shipped three |
| +times. Over a raw dot product the same omission silently reorders results. |
| +So the earlier draft of this paragraph, which said "cosine similarity then |
| +degrades smoothly", was wrong twice: cosine does not degrade, and the thing |
| +that does degrade is the form the spec had not committed to. |
| + |
| +Gate 22 is therefore deterministic and cannot be argued past — *every |
| +stored vector has L2 norm 1 ± ε; mutation: skip the renormalise, expect |
| +red* — and gate 23 carries the measurement it cannot do. |
| + |
| +**It costs ingest bandwidth, not storage.** Truncation happens before |
| +storing, so the table below is unchanged — 90k chunks × 4096 f32 is |
| +**~1.4 GB decoded to store 46 MB**, a ratio of about 32×, not the 8× you |
| +get by mis-reading the kept width as the f32@512 intermediate (184 MB is |
| +what truncation yields; int8 quantisation is what is stored). On the wire |
| +it is worse than decoded: **~2 GB base64, ~4 GB as JSON floats**, and |
| +whether TensorX honours `encoding_format: "base64"` is unverified (§13). |
| + |
| +This is ingest-time, not wake-time, so §7.3's ceiling is untouched. It is |
| +not free either: neither the first embed's wall-clock nor the batch size |
| +the endpoint accepts is written down anywhere, and §13 asks for the |
| +re-embed figure while the first embed's is equally absent. |
| |
| | chunks | int8 @512 | f32 @512 | f32 @768 | |
| |---|---|---|---| |
| @@ -1055,10 +1141,36 @@ alongside it. The passages have already passed the gate. |
| Two configuration names — `model.cheap`, `model.frontier` — with the |
| route printed on the answer. |
| |
| -**`openrouter/auto` is disqualified.** It selects by trailing-7-day |
| -community spend, changes weekly, and cannot be attributed — failing |
| -AGENTS.md §Review's "record which model" and decision 4's naming |
| -requirement. |
| +**The pinning rule bit the first two ids this section chose.** They were |
| +`z-ai/glm-5.3-flash` and `moonshotai/kimi-k3`; neither is datestamped, and |
| +Kimi's own history (`k2-0711`, `k2-0905`) says a bare family name is an |
| +alias over dated checkpoints. By the rule below they are floating aliases, |
| +so **prefer a datestamped id wherever the catalogue offers one** — |
| +`deepseek/deepseek-v4-pro-0813` for the frontier tier — and where it does |
| +not, the id is provisional and §13 carries it. Whether TensorX holds a |
| +stable id to stable weights is **unread**, not established. |
| + |
| +**Where no datestamped id exists, a canary substitutes for one.** At first |
| +configuration, embed a fixed set of public, non-content sentences — they |
| +are not team content, so §3.2 is untouched — seal the vectors, re-embed on |
| +wake, and **state an anomaly when cosine drift exceeds a stated ε**. That |
| +detects a silent weight swap under a stable id, which is the exact failure |
| +the rule names, and it is the same instrument §13's single-model item |
| +needs. Gate 24. |
| + |
| +**`openrouter/auto` is disqualified**, and so is anything shaped like it. |
| +It selects by trailing-7-day community spend, changes weekly, and cannot be |
| +attributed — failing AGENTS.md §Review's "record which model" and decision |
| +4's naming requirement. State the rule generally, because a router is not |
| +the only thing that breaks it: **a model id that can change under a team |
| +without that team changing it cannot be printed on an answer.** A floating |
| +alias is disqualified on identical grounds, which is why the two config |
| +names hold ids and not tiers — pinned where the catalogue permits it, and |
| +canaried where it does not. |
| + |
| +**Noted against ourselves:** OpenAI's dated snapshots satisfy this rule |
| +more cleanly than the catalogue §8.3 selects. That is a real cost of the |
| +provider choice, and §8.3 records it as one rather than talking past it. |
| |
| ### 8.3 Egress |
| |
| @@ -1066,6 +1178,61 @@ Per decision 4: one provider, fixed by the team owner, named on every |
| answer, disclosed in the README's first paragraph, opt-in per team, with |
| §8.1's floor working when it is off. |
| |
| +**The provider memoria supports is TensorX** (`api.tensorx.ai`) — |
| +open-weight models served on EU infrastructure in Dublin, Helsinki and |
| +Paris, with **advertised** zero retention. Advertised: no account exists, |
| +no terms have been read, and §3.6 tells the reader we cannot verify a |
| +retention promise. That sentence keeps saying so. |
| + |
| +**The provider is not team-configurable.** Supporting one is a code change, |
| +and a team owner's choice is on or off. Decision 4 says "fixed by the team |
| +owner", which reads as picking one, and these are different products: a |
| +configurable base URL on an OpenAI-compatible client lets an owner point |
| +memoria at a proxy and still have the answer print "one named provider", |
| +which is the failure this whole section is about. The spec takes the |
| +narrower reading. **It is Paul's to confirm and decision 4's wording needs |
| +it** — QUESTIONS #1, §13. |
| + |
| +**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: |
| + |
| +- **Fewer parties, not one party.** OpenRouter is a proxy, so the proxy and |
| + the upstream that runs the weights both see content, and "one named |
| + provider" would be naming one of two. TensorX runs the weights it serves. |
| + But **one contractual counterparty is not one organisation seeing the |
| + corpus**: a vendor this young on infrastructure in three cities is |
| + plausibly renting it, and a subprocessor sees content in exactly the |
| + sense §3.2 means. That is a DPA question and the DPA is unread (§13). |
| + The claim is *narrowed*, not *closed*, and §13's item says so. |
| +- **Named weights — partially.** §8.2's requirement, met by a namespaced |
| + catalogue, but only `deepseek/deepseek-v4-pro-0813` and its like are |
| + datestamped. On id-pinning **OpenAI's dated snapshots are strictly |
| + better**, and §8.2 records that against us. |
| +- **Open weights, which decision 4 never asked for.** Any host serving the |
| + same `qwen3-embedding-8b` produces compatible vectors, so the vendor |
| + failing is a *re-host*, not a re-embed of the corpus — see §13. Self- |
| + hosting stays reachable (§12). This is the strongest argument here and it |
| + is not a decision-4 argument at all. |
| +- **EU jurisdiction**, likewise not a decision-4 criterion. |
| + |
| +**So the honest record is:** TensorX was chosen for sovereignty and open |
| +weights — criteria Paul added on 2026-09-04, not criteria decision 4 stated |
| +— at a known cost in id-pinning and in frontier answer quality (§8.2, §13). |
| +Going direct to OpenAI satisfies decision 4's letter at least as well: one |
| +counterparty, pinnable dated ids, zero-retention obtainable on request, and |
| +the subprocessor point cuts both ways since it runs on Azure. It loses on |
| +jurisdiction, on open weights, and on cost. Written that way the choice |
| +stands; written as "TensorX wins on decision 4" it would be false. |
| + |
| +**What it does not change.** This is still whole-corpus egress at ingest, |
| +and §3.2's leak list is unaltered: a provider that sees the corpus sees the |
| +corpus, whatever its jurisdiction. EU jurisdiction and zero retention |
| +constrain what may lawfully be done with the content *after* it arrives — |
| +the first thing on this page that constrains anything — but they do not |
| +reduce what is sent, and a retention term is a promise we cannot verify |
| +(§3.6 says so to the reader and should keep saying it). Decision 5's AI-off |
| +floor remains the only configuration in which nothing leaves. |
| + |
| **This qualifies Docs §11.2**, which rejected third-party conversion APIs |
| for shipping "customer documents to a third party, contradicting §2". The |
| comparison must be like with like: Docs rejected whole-document egress, |
| @@ -1111,8 +1278,10 @@ so** — an empty paragraph is indistinguishable from AI being off. |
| |
| On §7.2's measured corpus — 90k chunks, **24M tokens**: |
| |
| -**Ingest:** 24M × $0.02/M (`text-embedding-3-small`) = **$0.48 once**; |
| -$3.12 on `-large`. Re-embed only changed chunks. |
| +**Ingest:** 24M × $0.02/M (`qwen/qwen3-embedding-8b`) = **$0.48 once**. |
| +Re-embed only changed chunks. The figure survives the move off |
| +`text-embedding-3-small` unchanged because the two list prices happen to |
| +match — a coincidence, not a reason to stop rechecking it. |
| |
| This has been wrong twice, in opposite directions. Draft 1 priced 60M |
| tokens against a 90k-chunk corpus; draft 3 corrected to 36M by keeping the |
| @@ -1120,9 +1289,17 @@ tokens against a 90k-chunk corpus; draft 3 corrected to 36M by keeping the |
| from §7.2's measurement, and if that measurement moves so does this. |
| |
| **Query:** retrieval is free. The generative layer sends ~8 passages plus |
| -instructions — ~5k tokens in, ~500 out. Cheap model ~$0.001–0.003; |
| -frontier ~$0.02–0.10. Five people × 20 queries/day × 22 days = 2,200 |
| -queries: **~$5/month if most route cheap, ~$100 if most route frontier.** |
| +instructions — ~5k tokens in, ~500 out. At §8.3's list prices |
| +`z-ai/glm-5.3-flash` ($0.20/$0.50 per M) is **~$0.0013 a query** and |
| +`moonshotai/kimi-k3` ($3.00/$15.00) is **~$0.023**. Five people × 20 |
| +queries/day × 22 days = 2,200 queries: **~$3/month if most route cheap, |
| +~$50 if most route frontier** — or ~$26 with |
| +`deepseek/deepseek-v4-pro-0813` as the frontier tier. Read that carefully: |
| +both figures sit *inside* the $0.001–0.003 and $0.02–0.10 per-query ranges |
| +this section already carried, near the bottom. **The ceilings fell; the |
| +estimates did not move.** Nothing here is a reason to route more queries to |
| +the frontier tier — §8.2's three model-free classes are still the design, |
| +and §8.6'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. |
| |
| @@ -1311,6 +1488,28 @@ renders on its own origin. The home never sees `q`. |
| 21. **Admission is relayed, not held.** A person no sibling calls a member |
| is refused, and `UNKNOWN` from every sibling refuses rather than |
| admits. |
| +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 |
| + over cosine similarity **could not fail**, because cosine divides by |
| + the norms and is scale-invariant. §7.2 commits the search to a raw dot |
| + product so that this gate bites. |
| +23. **Recall at 512 against recall at 4096**, on a named labelled query |
| + set, above a stated fraction. **This gate cannot be written yet**: no |
| + labelled set exists in the repo and `docs/BENCHMARKS.md` measures |
| + latency only. Recorded here as owed, not as present — §13. Writing it |
| + as a threshold over an unlabelled corpus would be gate 2's and gate 8's |
| + mistake a third time. |
| +24. **Embedding-model canary.** A fixed set of public, non-content |
| + sentences embedded at first configuration and re-embedded on wake; |
| + cosine drift beyond a stated ε **states an anomaly** and does not |
| + silently continue. This is what stands in for a datestamped id where |
| + the catalogue offers none (§8.2), and the detector for §13's |
| + single-model risk. The mutation is a substituted model id. |
| +25. **No provider response body is logged verbatim.** An OpenAI-compatible |
| + 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. |
| |
| --- |
| |
| @@ -1333,6 +1532,13 @@ 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**, |
| so it does not land before its disclosure copy does. |
| |
| +Four things enter with it rather than after, because each is cheap now and |
| +a silent wrong answer later: **the live-call verification** §13 asks for, |
| +against a real key, before anything is signed; **gate 22**, which is |
| +deterministic and costs nothing; **a labelled query set**, without which |
| +gate 23 cannot be written at all; and **gate 24's canary**, which is the |
| +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. |
| |
| @@ -1355,7 +1561,7 @@ rows. |
| | 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 | |
| | A runtime model-call ledger | Someone needs to audit routing. Sealed and provenance-stamped when it lands (§8.3) | |
| -| A local embedding model | A pure-Go or cgo-free runtime exists. Would remove ingest egress entirely, which §3.2 makes the largest single leak | |
| +| 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 | |
| | Fine-tuning on the corpus | Never, under decision 8 | |
| |
| @@ -1436,11 +1642,52 @@ rows. |
| the rest of a cold start: opening SQLite, allocating the vector matrix, |
| parsing the snapshot and building id maps are all outside the 5-second |
| budget as computed. |
| -- **OpenRouter's embeddings endpoint** is reported OpenAI-compatible at |
| - `/api/v1/embeddings`, but the documentation page 404'd when checked. If |
| - embeddings and chat both route through OpenRouter, **two parties see |
| - content** — the proxy and the upstream provider — and decision 4's "one |
| - named provider" then names one of two. Resolve before slice 2. |
| +- **The two-parties problem is narrowed, not closed.** §8.3 selects a |
| + provider that runs its own weights rather than a proxy, which removes |
| + OpenRouter's proxy-plus-upstream shape. It does not establish that one |
| + organisation sees the corpus: **whether a subprocessor sits under TensorX |
| + is a DPA question and the DPA is unread**, as are the retention terms |
| + §8.3 calls "advertised". Both before slice 2. |
| +- **One live call, before anything is signed.** TensorX's *LlamaIndex* page |
| + configures embeddings as `text-embedding-3-small` against |
| + `api.tensorx.ai` — a closed-weight OpenAI model an open-weight EU |
| + provider cannot be serving. It reads as a copy-paste of LlamaIndex's |
| + OpenAI documentation with the base URL swapped, and it is not a source; |
| + the API reference is, and says `/v1/embeddings` serves |
| + `qwen/qwen3-embedding-8b`. **Untested: no API key.** Be clear what the |
| + call can settle: a 400 proves only that TensorX does not proxy *that |
| + model name*. It says nothing about whose GPUs run `qwen3-embedding-8b`, |
| + which is the item above. While there, check `encoding_format: "base64"` |
| + (§7.2) and the accepted batch size. |
| +- **Is the model id stable?** §8.2 selects `moonshotai/kimi-k3` and |
| + `z-ai/glm-5.3-flash`, neither datestamped. **Whether TensorX holds a |
| + stable id to stable weights is unread.** Ask; and until answered, gate |
| + 24's canary is what detects the answer being no. |
| +- **One embedding model, and the risk is a re-host not a re-embed.** The |
| + catalogue offers exactly one. But the weights are open, so any host |
| + serving the same `qwen3-embedding-8b` yields compatible vectors — closely |
| + rather than bit-identically, since serving stacks and quantisations |
| + differ, which is a thing gate 24 would measure. **The vendor failing |
| + costs a migration; the model being retired costs the corpus**, and that |
| + second exposure is identical to the one the OpenAI design carried. What |
| + is written down nowhere is the wall-clock and search-quality gap of a |
| + full re-embed, first or subsequent. |
| +- **The 4096→512 truncation is unmeasured.** §7.2 argues it is sound |
| + because the model is trained Matryoshka, which is an argument and not a |
| + measurement. Gate 23 is owed and cannot be written until a labelled query |
| + set exists; `docs/BENCHMARKS.md` measures latency only. |
| +- **Qwen3-Embedding is instruction-tuned, and §7–§8 are silent on it.** |
| + Queries take an instruction prefix, documents do not. OpenAI's models had |
| + no such asymmetry, which is why nothing in the spec mentions one. Getting |
| + it wrong costs recall and says nothing while it does. |
| +- **§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, for any model.** |
| + §8.2's frontier tier moved from "Claude or GPT" to an open-weight model |
| + with no measurement either side of the change. QUESTIONS #1 puts the |
| + product call to Paul, correctly; the measurement is still owed. The spec |
| + measures everything else. |
| - **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 |