DUNIN7 · LOOMWORKS · RECORD
record.dunin7.com
Status Current
Path standing-notes/dunin7-build-list-v0_78.md

DUNIN7 — the build list — v0.78

Version. 0.78 Date. 2026-08-14 Status of this document. The single guide we work to. This is the one page the Operator reads at each sitting. Charter v0.1 ratified 2026-07-30; autonomous regime in effect. Day-to-day status lives in current-status/dunin7-status-brief. Author. Claude.ai; v0.3–v0.4, v0.10, v0.46–v0.78 by Claude Code; the rest by Claude.ai.

Changes from v0.77. The CR-2026-210 walk is VERIFIED and [B-95] CLOSES FULLY (completion v0.2). The Operator tapped Make it official on E0128 — real domain, real passkey: the server's sentence rendered ("Seed has 1 open finding(s)…"), the passkey completed silently with no credential error. The ceremony was never the obstacle, and the surface now says so; two days of "try your passkey again" is closed. The truthfulness trio stands at: [B-96] closed, [B-95] closed, [B-94] the last member open — confirmed unchanged on the same view (the label spliced mid-sentence), its own small item. E0128 remains one answered success_conditions (or one divergent rationale) from commit, behind a banner that tells the truth about exactly that.

Changes from v0.76, carried. CR-2026-210 v0.2 SHIPS — the commit ceremony says what the server said; [B-95] CLOSES pending the Operator's walk (surface a1deb86, tag cr-2026-210-commit-error-truth; run verdict in the completion note). The arc that began with two days of "try your passkey again" ends with the amendment that made bypassUnauthorizedRedirect's name honest: api()'s 401 branch now parses the body exactly as every other status's (a parse and nothing more; the observation sweep held — eleven consumers, live 401s pre+post, no behavior change, the new detail ignored by all). The catch branches on the OBSERVED vocabulary — the three step-up spellings probed live before the branch was written — with the guard both ways: the verbatim 409 body renders the server's message and never the passkey sentence; the passkey sentence renders only in its true family. The not_converged render on E0128 is the Operator's post-deploy walk to see (localhost cannot complete a tap against rp dunin7.com — WebAuthn design); its result appends to the completion record, not assumed. The truthfulness-arc trio [B-94]/[B-95]/[B-96] stands at: B-96 closed, B-95 closed-pending-walk, B-94 open (the sentence splice — its own small item).

Changes from v0.75, carried. CR-2026-211 SHIPS — the converse catches tell the truth; [B-96] CLOSES (surface 8871997, tag cr-2026-211-converse-error-truth; run verdict in the completion note; the true-branch eye-test observed live). The sentence keeps its true branches and loses its false ones — a confinement, not a deletion: the thrown-fetch case is PINNED in tests, not just the false cases removed. The completion note carries the arc's finding, per the Operator: both of [B-96]'s stated mechanisms — the 503, then the 502 — were wrong until observed; the item was filed on one wrong mechanism, re-justified on a second, and only got right facts when a probe killed an engine and looked. The class held every time; every specific claim about how it manifests failed. The general form: HOW A STACK FAILS IS NOT VISIBLE IN THE STACK — the same shape as CR-2026-206's replay finding, from the other direction: some facts only exist under execution. The kill-able relay is recorded as an instrument (completion note §instrument): a real dead-server state against a live page, production untouched. CR-2026-210 is next per the ranking.

Changes from v0.74, carried. B-96 SCOPED (read-only) and CR-2026-211 DRAFTED — with a correction folded in rather than left standing. The scoping (scoping-notes/loomworks-b96-converse-failure-vocabulary-scoping-note-v0_1) enumerated the converse residue: five failure shapes reach the two catch sites; three render falsely as "couldn't reach" — and the in-situ example both prior records gave was WRONG: /operator/converse cannot 503 no_credential (the route composes a 200 around a missing key); the true false-members are the 500-bug and the 422-surface-bug. The [B-96] entry below is corrected accordingly — the class claim and the ranking stand on the corrected members. Found in the same read: the converse route is deliberately ARMORED (exhaustion, keys, rate limits, phase-31 errors all compose 200s) — the residue is small, which makes the fix cheap; the sentence HAS true branches (thrown fetch; 502 engine-down — a real state on this deployment) and the fix confines it to them; no stable codes exist in the residue and none are needed — the branch is status-class, no server prerequisite. The shared helper is ruled shareable without being the fenced mechanism (one route, two callers, three branches; the line: a code→message table or a route parameter would cross it). CR-2026-210 holds behind [B-96] per the ranking.

Changes from v0.73, carried. The B-95 scoping's rulings land: [B-96] is filed and RANKS ABOVE [B-95] — the two "Couldn't reach the Companion" sites are the main artery, assert something specifically false (a 503 no_credential means the server was reached and answered), and nobody has walked into them yet only because they need a server error to surface. Dev-auth's site is ruled NOTHING — not filed. The 401 wrinkle moves INTO the B-95 CR's scope, not a footnote: the one case where "try your passkey again" is true (a failed tap, 401 commit_attestation_failed) is the one case that never renders it — api() redirects all 401s and the ceremony shows nothing — so the CR must distinguish a failed tap from an expired session or it fixes half the defect. CR-2026-210 is DRAFTED against B-95 site 1 with the 401 case in scope, on the FallbackAffordance precedent, fence held: no general error-rendering mechanism. Scoping note: scoping-notes/loomworks-b95-error-cause-sweep-scoping-note-v0_1 (four cause-manufacturing sites of 23; 49 stable server codes — branchable, no prerequisite; the guard absent everywhere).

Changes from v0.72, carried. [B-95] is filed per the Operator — the misattributed commit error, framed as the STRONGEST instance of the truthfulness class, not another one. The server named the cause truthfully on all four attempts and the surface destroyed the message and asserted a different cause; the recorded cost (two days on the wrong problem) is what makes the item worth building. Standing note v0.4 adds the third revision and the layer inversion: server composition necessary-but-not-sufficient (B-85), a composed imperative outliving its affordance (207-v0.3), and now a truthful server message destroyed by the receiving client — every layer has now been the one that broke it.

Changes from v0.71, carried. The E0128 commit blocker is DIAGNOSED FROM LOGS — it was never the passkey. The Operator registered a fresh passkey (clean CREATE) and the commit still failed; the engine log shows all four instantiate attempts across both days went challenge 200 → instantiate 409 — the app-level not_converged mapping: "Seed has 1 open finding(s)" (E0128's induction finding: success_conditions present but empty). The gate order proves the passkey layer PASSED on every attempt (step-up asserts before the convergence check; an attestation failure would have been 401 — the log holds no 401s). The surface's CommitCeremony collapses every non-401 error — including the server's precise not_converged message — into "Couldn't commit — try your passkey again": the server told the truth and the surface replaced it with a false cause, which cost two days of passkey-side investigation. CR-2026-207-completion v0.3 supersedes v0.2's conclusion accordingly. [B-94] is filed below per the Operator (the commit sentence splicing the title mid-clause — noted against the batch in CR-209 §2, now tracked); the misattributed-error finding is reported for ruling, not self-filed.

Changes from v0.70, carried. CR-2026-209 SHIPS — titles, both faces (engine f011c80, tag cr-2026-209-title-both-faces; run verdict in the completion note). Candidates are named the moment their seed lands (name_candidate_from_seed, COALESCE-guarded, at all four seed-landing sites); the derivation produces a clause label, not the description (last clause boundary inside a 60-char word-boundary budget; ellipsis only on a mid-clause cut; deterministic — the settled ruling, not reopened). The backfill boundary held and was confirmed three ways (structural state='candidate' pin, the three-shapes test, live before/after): E0128/E0129 named; E0089/E0127 byte-identical and E0104 still NULL — committed engagements are out of reach by construction, the defect left visible on purpose (renaming a committed engagement is a change to something someone may already refer to by name). The migration-0049-era backfill_engagement_titles.py predates this boundary and walks active rows — do not run it. [B-93] is filed below from the eye-test's own cleanup — the discard 500 on turn-carrying candidates, found only by tidying up properly. E0130's display number is consumed by the eye-test (the sequence gap is by design — the E0060-survives precedent); a sliver of Marvin-Test2's trial credit went to the one live extraction.

Changes from v0.69, carried. CR-2026-208 SHIPS — newest-first with the composer at top, as the default (surface 00ed2f2, tag cr-2026-208-message-order-default; run 31740270876 watched green, 4m16s; production redeployed; eye-tested pre- and post-deploy on a genuinely no-preference account, with the toggle-written row cleaned up both times). ORDER_DEFAULT aligns with the engine default key; the composer moves with the effective order — one JSX value placed structurally, the seam border flipping side; the oldest_first DOM-order test is the fence that the toggle still means something. The deploy-day flip of every no-preference account is the ruling's INTENT, not a side effect — a default, not an option; the toggle staying is what makes it a default rather than a removal. The Operator's own account holds stored oldest_first (written 2026-08-12) and correctly does not flip — named so the post-deploy state is never misread as a failed deploy. [B-91] and [B-92] are filed below at the Operator's direction — filed, not fixed: the ruling survives unapplied on the mobile surface (the exact contradiction ruled against, alive on one surface), and the one-time remount jump on stored-oldest-first accounts (small, but the kind of thing that gets rediscovered as a mystery later). CR-2026-209 (titles) stays a draft.

Changes from v0.68, carried. CR-2026-207 SHIPS — the ceremony's durable home (engine c04afd9, tag cr-2026-207-candidate-ceremony-home; surface b3b01d7; both runs watched green — the engine's on a 34-minute slow runner, watched to the edge of a named cancel-for-logs threshold rather than assumed; redeployed; the banner verified live on E0128 pre-handoff). The E0128 walk's stranding is remedied at both ends: the candidate's page renders the shared ceremony from DERIVED state, and the persisted sentence describes state with a durable pointer (standing note v0.3's member, remedied at origin). Two findings recorded at the Operator's direction: the Step-1 read paid for itself in SCOPE REDUCTION (state already flowed; the projection shrank to one server-computed boolean, the raw creator id never shipping) — premises can be wrong in the favorable direction and reading first collects that too; and the instantiate 409 pin is load-bearing — two commit surfaces coexist only because the state check is a tested guarantee; removing it would look harmless and convert a double-tap into a double-commit. The Operator's passkey walk on E0128 is pending; its result appends to the completion record when reported. The E0128 findings brief's other two rulings hold as drafts: CR-2026-208 (order default), CR-2026-209 (titles — the clause-label ruling settled in advance: deterministic, never model-derived; a name is a claim about what a thing is).

Changes from v0.67, carried. CR-2026-206 SHIPS — the two dead creation doors open (engine cd91176, tag cr-2026-206-creation-terminal-control; surface 7ac3cb2; both runs watched green first attempt; production redeployed; the control eye-tested live). The terminal act stops being a classification: two buttons carry the menu's own words with intent_hint at the exit (the entry turn's instrument, finally used at the terminal — deliberate dual guard, the CR-203 shape); project-less commit_project_draft AND finalize_project fall through to the creation-commit handler; the door-2 closer and the template name only wired acts (the standing note v0.2's first application). A METHOD FINDING is recorded in the completion note at the Operator's direction: replay reproduces the words but not the state that produced them — the live eye-test's terminal sentence routed to finalize_project, a third sibling no replayed cell surfaced; both B-86's re-measurement and the dead-doors scoping used replay and both would have missed it. No replay-based measurement is to be trusted as exhaustive over a confusable set; the live probe is the check the replay cannot be. E0128 awaits the Operator's passkey; E0129 is disposable control-test residue (the brief path drafts before the findings gate holds).

Changes from v0.66, carried. CR-2026-205 SHIPS — the operation outcome is a recorded fact on the turn (engine a367db7, tag cr-2026-205-persist-operation-outcome; run verdict in the completion note). [B-86] CLOSES ENTIRELY — its routing half went with CR-2026-203, its persistence half lands here; the classifier remains untouched, pinned by a fence test. The Step-1 completeness check earned its place twice: the vocabulary was corrected before the migration (save_filter/tune_setting actions, seven error keys), and it caught a wrong premise — add_knowledge's happy path computes no discriminator at all. [B-90] is filed below from that finding. The missing-execution_result derivation is conservative (_then_failed), reasoned in the completion note.

Changes from v0.65, carried. CR-2026-204 SHIPS — the chat-provenance marker, unconditional and structured (engine 6802014, tag cr-2026-204-chat-provenance-marker; surface 6175e45, redeployed; details in the completion note). The push cost two red runs, both instructive: the first (7c4d270) was a REAL break — mypy caught nine arg-type errors whose local gate output this session truncated and misread as at-baseline (the arc's third assumed-vs-observed divergence, again caught only by watching); the second was the dashboard perf budget flaking on a shared runner — [B-89] is filed below, B-84's family: an intermittently-red gate test teaches rerun-instead-of-read. The marker was eye-tested live post-deploy in all three states (model_prose chip; server_composed absent; pre-CR null absent).

Changes from v0.64, carried. CR-2026-203 SHIPS — the grant path stops depending on routing (engine b4a7881, tag cr-2026-203-raw-message-grant-extraction; push run 31555563172 watched green — the ninth consecutive). The rulings that reshaped [B-86]'s remedy, recorded: the Operator's live testing found the ungated chat route (E0127 — the deferral's own trigger fired), and the re-measurement that followed replayed the live history verbatim and found the dependency LARGER than the deferral's premise — one wired cell in ten, deterministic, with the denial's own suggested sentence never routing wired in its natural typed form. Ruled: the 32-intent classifier change does not rise; raw-message extraction ahead of routing instead — strictly better than what was deferred: paraphrase, punctuation, history length, and project context stop mattering. Shipped as CR-2026-203: adjacency+word-boundary matcher (the old laxness was masked by the classifier gate the redirect removes), revoke-first 4a redirect on the orient-redirect precedent, both grant acks reframed tentative ("I think you may have granted…" — a false capture asserts a reading, never the Operator's act; held-not-committed absorbs the residue). Eye-tested live in the misroute condition. [B-88] is filed from the completion note's residual, per the Operator: deferred work must be tracked, not live in comments and records. The disclaimer is RULED (structured marker, unconditional — CR-2026-204 approved executing) and [B-86]'s persist half is drafted as CR-2026-205. Scoping and re-measurement briefs: inspection-briefs/ at 34c27cc and 9fe62ff.

Changes from v0.63, carried. CR-2026-202 SHIPS — [B-77]'s prompt-cache half CLOSES (engine 64610b4, tag cr-2026-202-b77-prompt-cache): the cache now asks the seed's own log, and only when the ref leaves it free to move — pinned refs pay nothing, unpinned refs pay one query. The two accidents that were preventing the live defect are recorded as accidents no longer load-bearing. Both directions pinned by test: the failing direction (a superseded seed served indefinitely) and the inverse (a later seed version leaking into a pinned engagement — the same harm inverted). [B-77]'s second Kind B entry (the Manifestation preview's frozen ordering) stands; [B-69] is narrowed, not settled. Session closes here — see the session handoff in session-handoffs/.

Changes from v0.62, carried. CR-2026-201 SHIPS — [B-87]'s four all-claim replies are server-composed (the approval-card proposal with its forced variant, the executed and executed-then-failed reports, approve_draft's three outcomes, remember's misconfiguration report — all on the denial's precedent, no new machinery; the no-claim elicitations stay model prose, pinned). The Step-4 re-measurement changed two rulings in one sitting. [B-86]'s dependency is measurably smaller: 18 trials, zero misroutes, every condition 3/3 consistent — so [B-86] takes the smaller remedy, ruled: persist the operation outcome on the turn; the 32-intent classifier change is deferred unless a live recurrence demands it. A change moving all 32 intents to fix a dependency now measurably smaller is the wrong ratio — and the persisted outcome earns its place independently of routing, since it is a structural fact currently surviving only inside prose. And the steering finding is recorded as its own result, not a footnote: composed replies route the grant sentence to the wired path where fallback prose routes it to a re-ask — nobody argued for that benefit, and it must be visible when the five-splits CR is weighed.

Changes from v0.61, carried. [B-87] files the overclaim sighting and closes its ruled remedy the same session: the delegation-denied branch's reply is server-composed — the microphone is taken away on the one branch observed to lie. Why it ranks above "one sighting, next reply honest," in the Operator's words: CR-2026-199 established the wording layer as the weakest guarantee and the structure as what holds; [B-85]'s fallback then removed the pin from that exact layer, and a reply disobeyed a correct template within the same session. The prediction was confirmed, not hypothesised — and every claim still living in model prose is now less reliable than when CR-2026-199 assessed it. The item carries the standing question that raises, with the enumeration (report, not sweep) of the seven surfaces where an outcome claim still lives in model prose.

Changes from v0.60, carried. CR-2026-200 CLOSES and [B-81] CLOSES ENTIRELY — the arc that started this cluster was observed end to end in the product: the sentence the Companion itself suggests, typed in a project, produced a held grant scoped to that project; the held card named capability, scope by project name, and mode; Confirm committed the delegation; the same ask then produced the CR-2026-197 approval card. (Engine 469fc22, tag cr-2026-200-b81-grant-pathway, run 31453801230 watched green; eye-test transcript with every turn's classified intent captured before teardown.) Mid-execution the API broke the respondertemperature refused for the resolved model — halted, ruled, and closed same session as [B-85] (retry shape; the fall visible and recorded; a partial surrender of CR-2026-169 D-1, recorded at that CR's v0.2). [B-86] is filed: the grant arc leans on the denial's PROSE as classification context — demonstrated three ways in one night. And one observation awaits a ruling: the first unpinned responder reply composed a completed-action claim against a denied outcome — the CR-2026-199 class, surfacing as model disobedience under unpinned generation rather than a template defect.

Changes from v0.59, carried. CR-2026-199 SHIPS — the Companion no longer claims actions the system did not perform (engine 5e6cd17, tag cr-2026-199-truthful-replies; push run 31449387413 watched green, the third consecutive). [B-81]'s truthfulness half CLOSES, and [B-82] CLOSES with it — the fallback's overclaim was member 4 of the same four-member class. The structural fix outranks the persona rule: a formatter-covered template without its {operation_result} slot — the mechanism that computed and silently discarded a failure instruction — is now unconstructible, validated at startup and in the suite. The persona gains the never-claim-unperformed-actions rule, recorded as necessary and not sufficient. Every test observed failing against pre-fix code first. [B-81] stays open for its grant-pathway half alone — reachability (1a) and paraphrase fragility (1b), its own CR. (completion-records/cr-2026-199-completion-note-v0_1.)

Changes from v0.58, carried. [B-84] CLOSES — the gate is green and now trustworthy (CR-2026-198; engine 4ae5509, tag cr-2026-198-b84-polling-determinism; push run 31430333953 watched green, the second consecutive). The test waits on the property itself — events committed, not specialists called — and the fix exposed a second race of the same kind hidden under the first. A property is named from the pair, per the Operator: a failure of a given kind suppresses detection of others of that kind, so the first fix should expect to find more rather than treat the count as settled — B-83's red-on-red, then CR-2026-198's race-under-race, in consecutive change requests. (completion-records/cr-2026-198-completion-note-v0_1.)

Changes from v0.57, carried. [B-84] files the polling flake with [B-83]'s own reasoning: an intermittently-red gate gets read as noise, and once people learn to rerun rather than read it, it has stopped being a gate — the exact state [B-83] just left. Two Operator corrections are carried into [B-83]'s entry, and the audit the second one ordered is complete: one false record claim found beyond the already-corrected memory — CR-2026-196's completion said "all four push-verified" against a red engine run; corrected at its v0.2. CR-2026-194's record itself was honest (written pre-push; its session memory carried the false claim, corrected); CR-2026-195 claimed only "pushed," which was true; status briefs predate the window; CR-2026-197's record was written after the discovery and told the truth.

Changes from v0.56, carried. [B-83] is filed and ranked above the four from the gate — not for complexity, for what it costs while it sits. And fixing it revealed the redness was six pushes deep, not two, with a second cause layered under the first. Every commit since CR-2026-194 landed with no CI signal; a permanently-red check is worse than none, because nobody reads it and it cannot distinguish a new failure from the standing one — the same property as a check that cannot fail, arriving from the other direction. This run proved that property live: the standing failure was hiding an older one. Both fixes went in the same session; the acceptance is the push run itself observed green.

Changes from v0.55, carried. CR-2026-197 SHIPS — the Companion's first writes, per-action only (engine 46ab350, tag cr-2026-197-b8-companion-drafts; surface 35a49c9). Every load-bearing test was observed failing before being trusted, and the product eye-test observed the whole chain: card → approve → job, the spend refusal named on the failed job, the pre-debit park behind a cap-named card, a companion-produced pending shape carrying its authorizing delegation's id, and a pre-authorized grant still producing a card. One acceptance-gate clause is recorded UNMET as written: "granted conversationally" was satisfied through the service layer because the surface path does not exist — [B-81] names why. Three filings from the eye-test: [B-80] (three approval-card components dead against real wire data — and the empirical fact that hid it: zero approval cards had ever existed in the dev database), [B-81] (the conversational grant unreachable in-engagement, and a reply claiming a personal save that wrote nothing — the serious half), [B-82] (the same sentence routing differently by conversation length, with the reply overclaiming "I'm drafting… now" while structurally nothing could dispatch). An Operator correction is recorded: a first reading of the checkpoint took §4 as the whole card mechanism being inert; it is three specific pre-existing components outside CR-2026-197's fence, and the CR's generic-card fix demonstrably works — 197 was not blocked.

Changes from v0.54, carried. [B-8] slice two is scoped against the code and ruled: Shaping and Rendering drafts first, per-action only, as CR-2026-197 (drafted, awaiting the gate); the Manifestation write splits off as [B-78]. The scoping note's lean inverts on a read: the async rooms inherit the finished cap-named spend consent, and Manifestation — the note's "cheapest first move" — is hard-excluded from the pause machinery and needs consent built by hand. Three new items from the same read: [B-77], filed first and independent of B-8 — the Companion's prompt cache is invalidated by a version the seed's own writes never bump, currently saved by one incidental call; [B-78], the Manifestation write on its own; [B-79] — confirming a shape enqueues zero renders in production, because the dispatch hook is registered only by tests. B-69's inventory gains its first two Kind B entries (both inside B-77) and a Kind C with a boundary note for Rendering.

Changes from v0.53, carried. [B-51] closes, and the live check settled the question that decided its size: scroll position survives the rebuild in both arms; only keyboard focus was lost. So it was a focus fix, and small. The first measurement was vacuous — it read zero before and after because the page was not scrollable at that viewport — which put the observe-the-failure note at v0.6, with a section on what changes when the check is a person looking rather than a test.

Changes from v0.52, carried. [B-68] splits. Its untitled engagement is done — and it was two engagements, not one. Its two type names are not the rename they look like: they are stored values that three lookups match on, and renaming them would pass the suite while breaking every existing system. Filed as [B-76] with the path ruled and two reasonable-looking wrong moves named. The asymmetry is recorded as a fourth instance of green-because-the-test-cannot-reach-the-defect — and the first where it is an entire environment, not a check, that cannot fail.

Changes from v0.51, carried. [B-67] closes — the organized view can be pointed at. It halted first: the citation had been drafted into the one-payload slot that already holds what a turn produced, which is the same distinction applied at one level and dropped at the next. The vocabulary wall failed the build and was right to — the fix was to project at the sanctioned adapter, not to add a component to the allowlist, and the reasoning is recorded because this citation is the first thing to carry that vocabulary to the surface at all.

Changes from v0.50, carried. [B-66] closes without a line of code being written — it was fixed a hundred change requests ago and its symptom was never reachable. The change request written against it halted at Step 1. [B-75] is new: the in-engagement transcript throws away who spoke, which is harmless today and silent the day a shared transcript lands. The architecture specification is corrected at v0.8, with the stale line kept visible rather than overwritten — and a standing note records the pattern, now at three instances: an artefact describing work outliving the work and being acted on as though it were the state.

Changes from v0.49, carried. [B-60] closes — a document's structure reaches Memory. It halted first: the deferral comment had averaged two solved problems and two unsolvable ones into one phrase, and the timestamps it named had never been observed at all. Capture was chosen over relaxing the schema, and filling them from the upload moment was refused outright — that refusal is now a test. [B-74] is new, and it is the same statement's other unfiled follow-on: multi-file upload aggregation, deferred in a code comment that nobody ever turned into an item. A standing note is added on comments that average unlike problems.

Changes from v0.48, carried. [B-72] moves from READY-to-scope to READY-to-build. It has acceptance criteria, and the two files the engine sweep turned up are now its named fixtures rather than prose about it — the check must find [B-71] and stay silent on both, or it is not built. [B-73] is new: the production file named like a test, recorded as its own item because it is cheap now and expensive to meet later.

Changes from v0.47, carried. The engine was checked, and it is clean — [B-71]'s defect does not exist there. 451 files on disk match the runner's own naming pattern; 449 contribute tests; both of the two that do not are explained, and neither is silent. The unknown recorded in v0.47 is now answered. And the check taught [B-72] something it needed: a naive disk-versus-collected comparison would have called both of them findings, and both would have been wrong.

Changes from v0.46, carried. B-71's orphaned file was run, and it is green — three real assertions, passing. The finding does not shrink: a passing test nobody collects still reports nothing. B-72 is new, lifted out of B-71 because it is not about that directory or that repository: a check that compares test files on disk against the files the runner actually collected. And the engine's suite has never been checked for this at all — recorded as unknown rather than assumed fine.

Changes from v0.45, carried. B-47 closes — the credential surface exists and is live. B-71 is new, and it is a vacuity finding one level up from any we have recorded: a whole test directory has never been collected, so the file inside it has reported nothing since the day it was written.

Changes from v0.44, carried. B-54 closes entirely. Three deferrals, each recorded with what it waited on, all discharged.

Changes from v0.43, carried. B-70 closes — the log now holds what the views hold, and a rebuild reproduces them. B-54's database half is unblocked after three deferrals and returns as its own change request.

Changes from v0.42, carried. B-69's taxonomy gains Kind C, checked-no-contact, and the standing instruction now requires an entry even when the answer is no. An inventory that records only hits cannot tell no dependency from never looked.

Changes from v0.41, carried. B-54 closes at its code half. B-70 is new, and it is the largest thing found this run: the organized views hold facts the event log cannot reproduce, so rebuilding one loses them. Nobody caused this today — it has been true since Phase 36.

Changes from v0.40, carried. B-32 closes. B-69's inventory has its first entries — and they turned out to be a kind nobody predicted.

Changes from v0.39, carried. B-69's standing instruction is complete — the heading is named, and the instruction now says what NOT to do as well as what to record.

Changes from v0.38, carried. B-69 is added, and it carries a standing instruction for every change request until it is resolved. Whether a seed can change after an engagement is running, and what that would ripple into.

Changes from v0.37, carried. B-47 is rewritten with its placement settled and the work it actually needs. The change request assumed a credentials screen already existed to add a button to. None does.

Changes from v0.36, carried. B-68 is added: the accounting machinery names things for itself and those names reach the screen.

Changes from v0.35, carried. The phrase everything about B-63 rested on was never written down. B-65 has its scaffold; B-63 is reclassified from a possibly-forgotten commitment to a new one.

Changes from v0.34, carried. Six items said READY and were already done — for six versions, including three written in this session. Every status is now checked against the tag that closed it rather than against the previous version of this list.

> What went wrong, because the shape of it matters more than the six corrections. Nobody follows a status. A pointer gets walked — reading one item sends you to another, and a stale pointer is caught the moment someone arrives. A status is read only when somebody opens the item, and an item that says READY when it is DONE costs nothing until that happens. That is why incidental discovery found two stale pointers and none of the six stale statuses. > > The cost is already paid three times this session. B-13, B-12 and B-17 were each opened for building and each turned out to be already built or already satisfied. That is the same failure arriving from the other direction. > > And the list was contradicting itself. When items closed in v0.30, the closures were recorded in the grouped small-work line and not in the items' own entries — so two places in this document disagreed about the same six items, and either half read as authoritative alone. That contradiction was introduced while fixing a different staleness, which is exactly the walk-audit finding already recorded above: the sweep reported the files containing stale text and never asked whether those files were the current version. The same question went unasked here — whether the entry agreed with the grouped line.

Changes from v0.33, carried. B-61 is fixed for the case that carries note content; the smaller remaining case is separated rather than folded in. B-67 is added.

Changes from v0.32, carried. A question about one missing topic found a hole where four should be. B-65 is added and it outranks the question that found it: the foundation document defers four topics to a document that does not contain them, and that document does not exist here. B-66 is added: a live misattribution that has been sitting in the architecture specification as a known gap without ever reaching this list.

Changes from v0.31, carried. One item turns out to have been built and then quietly unbuilt. B-17 is rewritten against what it actually is now; B-43 comes back untouched with its costing still good. A control that had been offering a choice it could not deliver is gone.

Changes from v0.30, carried. B-14 is done, and one more untracked thing is tracked. Spreadsheets and slide decks are read. B-64 is added: a list of what the system accepts that is wrong and currently unread.

Changes from v0.29, carried. Five items that existed only as comments, notes, or nobody's job are now tracked — and two of the old ones closed by being measured rather than built. B-59 to B-63 are added; B-12 and B-30 close. Nothing waits on you, but B-63 is a seed-level question when you want it.

> Why five at once. Each was produced by a change request in the 175–182 run and filed with a document behind it, but none was on this list. The one that prompted the sweep is B-60: it lived as an in-code comment describing itself as a follow-on, which is not the same as being tracked. The other four were in the same state.

Changes from v0.28, carried. Nothing waits on you. Both open questions are answered, B-12's unread fact was measured, and B-16 is closed — **the first time this list has had an empty Waiting on you section since it was written. Also: the foundation document's two-month-old error is corrected and so are the documents that inherited it. A duplicated block in the previous version's Waiting on you is removed.**

Changes from v0.27, carried. Everything is deployed, and looking at the deployed product found six things. Thirteen change requests went live in one act and were verified one at a time in the running system. Six new items, B-53 to B-58, five of them found by using the product rather than by reading the code.

Changes from v0.26, carried. B-8 scoped and joined B-12 on the Operator's side of the line.

Changes from v0.25, carried. B-12 scoped; the fourteen small items grouped.

Changes from v0.24, carried. B-11, B-50 and the gates are all closed.

Changes from v0.23, carried. One claim about a tag corrected.

Changes from v0.22, carried. B-49 is closed, and it was worse than the item described.

Changes from v0.21, carried. The clean-report cluster is closed and one item opened.

Changes from v0.20, carried. A recommendation corrected and three items added that nobody knew were there.

Changes from v0.19, carried. B-10 is closed.


What to do next

Nothing waits on you. Both questions are answered, the one unread fact is measured, and everything merged is running.

B-8 slice two part one is DONE — CR-2026-197 shipped and eye-tested; what remains of B-8 is the Manifestation write ([B-78]) and the deferred confirm (Q-7, [B-79]). [B-81] is the serious new thing — a reply claimed a personal save that wrote nothing. [B-77] still should be looked at regardless — a filed latent defect, one bootstrap change from live. [B-83] — the red push CI — is filed first, fixed, and awaiting its push run observed green.

Or the small work, grouped below so it can be swept. The single-line group is now entirely closed; what remains is the small-builds group and the scoping items.

B-48 is closed — both checkers now gate every push, one at zero tolerance.

The one thing that will eventually want you: B-65. The foundation document points four topics at a document that does not hold them. B-63 waits behind it and is on hold rather than open. Neither blocks anything being built today.


How this list works

It is written for you first. Technical cross-references sit at the end of each line for the sessions.

The highest-numbered version is always the truth — past nine, sort as numbers, not text.

Some numbers are deliberately absent. Where a count grows every time something is built, this list names the mechanism and says to count it at scoping time. A figure written here is wrong before it is read.

The statuses: READY · IN PROGRESS · NEEDS YOU: decision · NEEDS YOU: act · WAITING · DONE · PARKED.

The finish line, in one sentence: the full live demonstration works with no excuses — a firm's own document becomes a working engagement, information flows in, the summary view appears, a finished report comes out, a question gets a truthful answer with its sources shown, and an outsider can contribute without seeing anything else.


What just closed

The foundation document named the wrong authorization mechanism for two months, and so did everything built on it.

A decision in June moved authorization from OVA to GRANTHA — and the seed, which every session reads first, kept describing the old model. Five places. Anything written against the seed in that window inherited it: a scoping note, two vertical documents, an investigation. A correction note filed in July swept the arc it was written inside and never looked at what was written in parallel.

All of it is now corrected, and the correction took three seed versions rather than one:

v0.13 replaced the superseded model with the current one. v0.14 removed the mechanism entirely — because describing a mechanism is what made the seed go stale in the first place, and swapping one description for another leaves it exposed to the same failure on a slower clock. The seed now names which seat each substrate occupies and nothing more. (Operator direction: while basic functionality is being tested, these are named by placement, not function.)

Two of the four inherited documents were corrected. One needed nothing — its stale text lives in a version two behind the current one, and the version anyone actually reads was already clean. Naming it as a live problem was itself the error, and that is recorded.

> The finding worth keeping. The sweep searched for stale text and reported the files containing it. It never asked whether those files were the current version of their document. A superseded version carrying a superseded claim is the record working; the question is always whether the current version carries it.


Part 1 — The main line

B-1 to B-7. DONE. The four rooms exist: Memory, Manifestation, Shaping, Rendering.

B-8. Teach the Companion the last three rooms. SLICE ONE DONE · SLICE TWO PART ONE DONE — Shaping + Rendering drafts, per-action only, live (CR-2026-197; engine 46ab350, tag cr-2026-197-b8-companion-drafts; surface 35a49c9). The Manifestation write is [B-78]; the confirm stays deferred (Q-7) with [B-79] recording that its auto-dispatch is dead in production.

What part one shipped, verified in the product: ask → approval card → approve → companion-attributed production. The produced shape's provenance carries kind=companion, the authorizing delegation's assertion id as capability_ref, and approval_mode=explicit. Both spend gates bind unchanged: the agent_spend_authorized refusal reaches the job visibly with its exits named, and spend_pause parks the companion-triggered run pre-debit behind a card naming the computed cap. per_action_only is a slice-scoped flag, not a permanent positionpre_authorized stays supported and unused; the reasoning travels with the flag's docstring and the completion record. One gate clause recorded unmet as written — "granted conversationally" ([B-81]). (completion-records/cr-2026-197-completion-note-v0_1.)

Slice one shipped and is live. Three words came off a forbidden list and the Companion can now name and describe all four rooms. Verified in the product. (CR-2026-170; engine 1706248, tag companion-room-reads-v0_1.)

Slice two — the writes — was read against the code 2026-08-10, and the scoping note's lean inverts. The path is fully built and ends in a deliberate hole: delegation, default-deny authorization, conversational grant, approval cards with click-time re-verification, and audit-grade ActorRef attribution all exist — but no dispatcher is registered for any Tier-1 capability, so an approved "draft a spec" card fails with no dispatcher registered. The build is three dispatchers; the decisions were the item, and they are ruled:

Q-6's answer is the prerequisite it was waiting for. Every answer a model writes will show the notes it was given; answers the system assembles will not, because they already show their own material. A write proposed from material the Companion cannot show would be an approval you could not check — that is now handled. (scoping-notes/loomworks-b8-slice-two-scoping-note-v0_1; scoping read 2026-08-10 against engine d85154d.)

B-9. The "show me where this came from" walk. DONE.

B-10. Outside contributors, safely. DONE.

B-11. Make answers truthful. DONE.

B-12. Make the record searchable. DONE — closed as already satisfied. Nothing was built and no constant was raised.

The measurement that closed it also corrected it. Every figure in the previous version of this line is right, and they count the wrong thing. Twenty-three engagements hold notes and the largest holds fifty-eight — but that fifty-eight counts notes in every state, and recall reads only the settled ones. That engagement holds ten settled. The largest anyone actually works in holds thirteen, against a window of fifty.

The window has never bound anything. Raising it would have solved a problem no project has. Every settled note in the entire system would fit in the model's context several times over.

> Why the error was invisible. The counts were all correct. Only the quantity was wrong. (CR-2026-179; completion-records/loomworks-cr-2026-179-b12-completion-note-v0_1.)

One thing is near binding, and it is the one already parked. The universal commons holds thirty-nine settled notes against the window of fifty — eleven of headroom, and it grows with every person who joins rather than with work on any project. The parked question now has a number.

> One thing does grow without bound, and it is not a project. The universal commons — the engagement every account joins automatically — holds the most of anything, and it grows with every person who signs up and contributes. Any window will be exceeded there eventually; raising the limit only moves the date. That is a different question from search, and probably a different answer: a shared space everyone contributes to is not the same thing as your own notes.

What remains before the constant is raised: measure what the model's prompt actually holds against fifty-eight notes, and raise the limit to a figure someone measured rather than a round number that sounds safe. Small. (scoping-notes/loomworks-b12-scoping-note-v0_6.)

B-13. Ask your engagement. READY — and the ask itself is already built. B-61 is closed for the case that blocked it: on a recall turn the model no longer receives notes the answer cannot cite. B-67 remains, but it concerns the organized view rather than notes, and whether it blocks this item is a scoping question rather than a known blocker. A read against the code found the question, the truthful answer, the stated boundary and the sources all in place — the ask was never the gap. (CR-2026-180 halted rather than building what was already there; inspection-briefs/loomworks-cr-2026-180-b13-step-1-findings-v0_1.)

B-14. Read spreadsheets and slide decks. DONE. A cap table, a financial model and a pitch deck can now be taken in. Each file becomes one note, the way every other document type does; a deck's speaker notes come in too, marked as notes — the argument is often there rather than on the slide.

The file chooser needed no widening. It never restricted anything: these files were always selectable and simply failed on the way in. The item's original note anticipated greying-out that the surface does not do. (CR-2026-181; completion-records/loomworks-cr-2026-181-b14-completion-note-v0_1 §3.)


Part 2 — Side work

The clean-report cluster — closed

B-33, B-34, B-42. DONE. Three symptoms of one problem: nobody could read a clean report as clean, because a standing exception had to be held in mind — and a standing exception is how a second failure hides. They were three of six; the other three are below. (CR-2026-166.)

> The inventory matters more than the zero. Lint now reports nothing. A zero standing over an unlabelled exception is the same defect as a green report standing over a known failure — so every exception is categorised, and the ones covering real defects carry the item that will fix them.

The small work, grouped

Twenty items, and grouping them is the point. Each is currently its own change request; taken in one or two passes they are perhaps two days. Ordered by how self-contained they are.

Single-line or near it. (All closed. B-26, B-35, B-44, B-53, B-57, B-58 and B-59 are DONE — see their entries.)

Small builds, one file or two. B-52 (an answer's sources vanish on refresh) · B-32 (no way to rebuild a summary the record has moved past) · B-37 (a failed production looks identical to a running one) · B-47 (done) · B-54 (done) · B-55 (a screen claiming something it cannot check) · B-56 (a development sign-in on the live perimeter) · B-60 (done) · B-64 (the list of what the system accepts is wrong) · B-66 (closed — already fixed). (B-36 and B-38 have since closed.)

Small but needing a decision first. B-45 (wrap the five stand-up commands, or leave them visible) · B-46 (where implementation notes should live) · B-51 (four components rebuilt on every change — and whether anyone loses anything is unread).

Scoping, not building. B-69 (whether a seed can change once an engagement is running — carries a standing instruction for every change request) · B-39 (agent identities invented and recorded) · B-40 (where contributed Markdown should route) · B-67 (done) · B-62 (two windows on the same notes) · B-63 (nothing marks an answer as written by a model — seed-level). All five now have scoping notes or findings filed.

The three nobody had named

B-48. The engine has quality tooling it has never run. DONE. Both checkers now run on every push — one as a ratchet against its existing backlog, one at zero tolerance with its backlog cleared. Every gate was deliberately broken and observed failing before it was trusted. (CR-2026-173, CR-2026-174.) Originally: Two standard checkers are configured and shipped, neither has ever been run as a gate, and both report large backlogs. This is not a cleanup item. The question is what a gate ought to check, and the answer is unlikely to be everything currently reported. A rule that has never run has never been agreed to.

B-49. Nothing was checking anything. DONE. The engine's single check had never passed — not once since June — and the surface had none at all. A permanently-red check is worse than none, because it teaches everyone that red means nothing. Both repositories now check themselves on every push, and every gate was deliberately broken and observed failing before it was trusted. (CR-2026-167.)

> One thing to keep an eye on. The engine's suite takes about a quarter of an hour on a fresh machine. That is a real cost on every push, and if it grows it will start to be paid in people not waiting for it.

B-50. Building the surface is not the same as checking it. DONE. The build only inspects what the running application reaches, so the test tree was invisible to it — and that is where the errors were. All drift in test fixtures; none silenced with an exception.

Everything else

B-16. Update the foundation document. DONE. See What just closed. The seed is at v0.14, committed.

B-17. The portfolio filter. READY to re-scope — and it is no longer the small thing this list has said for twenty versions.

It was built, and then it was unbuilt. A grouping of engagements into workspaces, with the list filtered to the one you were in, shipped in the engagement-navigation fill-in. A later redesign replaced that page with the current home surface and deleted the section that did the filtering, leaving every other part standing: the engine still filters, the data adapter still accepts the parameter, the chooser still exists. Only the thing that used them went. (Built in 194d060; removed in ba99d6e — both recorded so nobody re-derives this.)

"One parameter" is no longer true of either place it could live. The home surface deliberately has no workspace chooser, so filtering there would be a filter nobody can reach. The dashboard has the chooser but its data comes from three cross-project reads, none of which can be scoped to a workspace — that is engine work across three routes and their schemas.

What it needs is a decision about where the portfolio view lives, then a build sized to that answer. (CR-2026-182 halted rather than build to the old description; completion-records/loomworks-cr-2026-182-b17-b43-completion-note-v0_1 §2.)

> The chooser is no longer shown. It offered a choice that changed nothing, on every screen it appeared on, for a full redesign cycle. That is a separate correctness fix and not progress on this item — nothing is filtered that was not filtered before. The component is kept, so putting it back is one line once there is something to filter.

B-18. File the four protocol requirements. READY.

B-19. Keep this list alive. IN PROGRESS — and it now carries a standing check.

> When an item closes, update every place it appears — its own entry, every grouped line that names it, and every other item that points at it. Six items said READY for six versions while being done, because closures were recorded in a grouped line and not in the entries. Check a status against the tag that closed it, never against what the previous version of this list said — copying a version forward carries its errors forward with it. > > And the statuses are not the only thing drifting. Five items opened for building this session turned out to be already built or already satisfied — B-12, B-13, B-17, B-43, and B-37's engine half. A READY item can be wrong not because its status was never updated but because nobody knew the work existed: the capability is there, unreached, and the list has no way to notice. Before building an item, read the code for it first. Every one of the five was found in minutes by looking.

B-25, B-27, B-28, B-29, B-41. DONE.

B-26. Correct the record about door 3. DONE. Two corrections, filed beside the audit rather than edited into it: the finding blamed door 3, but the fault was eight unregistered event types — every door and every upload — and it is now fixed, all eight verified registered. (inspection-briefs/loomworks-walk-audit-w1-door-3-correction-note-v0_1.)

B-30. The trap that caused B-27. DONE — closed as detectability, not as repair. There was nothing to repair: the dependency is correct for the routes that carry the path segment, and a walk of all 228 routes found no live instance. The defect the item names is the suite's blindness, and that is gone — a guard now walks every route's dependency tree and fails if one requires something its own path cannot supply. Measured while proving it: the same broken route a request-based test answered 200 for would have refused every real caller. (CR-2026-178.)

B-31. The post-admission lifecycle investigation. READY to open. An investigation, not a build.

B-32. The re-derive button on the Manifestation screen. DONE. An organized picture Memory had moved past can be rebuilt. You approve it; nothing rebuilds on its own.

The consent does not name a price, and says so. The only figure the system could produce needs the whole prompt assembled — the foundation document plus every settled note — which is most of the work done twice, to estimate the work. So it names what it can: the balance it draws from, and what actually moved.

> And it says when the foundation itself changed. That is a different kind of change from more notes arriving: organizing again will reflect changed commitments, not just new material. The engine had counted this all along and no screen showed it. (CR-2026-189.)

B-35. A missing key looks like a crash. DONE. A missing model key reported a crash instead of saying the service was unavailable. (CR-2026-171, engine tag cr-2026-171-small-work-sweep-engine.)

B-36. A way to retire a render. DONE. (CR-2026-172, surface tag cr-2026-172-small-work-sweep-surface.)

B-37. Show when a shape's production has failed. READY — small (engine).

B-38. Bring the Rendering screen onto the shared contract. DONE. (CR-2026-172.)

B-39. Ephemeral agent identities. READY to scope. B-40. Where contributed Markdown should go. READY to scope.

B-43. Walk the other way. READY — costed, and the costing was re-checked and still holds. From a note, forward to everything built on it. The data is already there — the route exists and answers exactly this question, version-pinned. Nothing on any screen asks it yet, and that is the whole of the work.

The costing stands as written in v0.15 and is not restated here: the wiring is cheap, where it lives is not — its own room, its own presentation, its own tests. A second feature sharing infrastructure, not a ride-along. (Verified both halves during CR-2026-182; it left that change request for being larger than its pairing implied.)

B-44. Things with titles displaying as untitled. DONE. Both places: a Shape's title came back empty on a walk, and every shape in the Shaping room displayed as "Untitled shape." (CR-2026-171.)

B-45. One command instead of five. READY — deliberately deferred.

B-46. Where documents live. READY to scope — small. Implementation notes land in the code repositories; the filing convention says documents live in the record. A session orienting from the record alone cannot find them.

B-47. The issuance screen for contributor credentials. DONE. The screen exists, and the engine pathway is reachable for the first time.

The engine half was already complete. Issue, list and revoke all existed and were Operator-only. Nothing on any screen called them — there was no credentials surface at all, so the earlier framing of "add a reveal and a revoke to the existing list" described adding to something that is not there.

Where it goes, decided. A panel opened from the in-engagement header, the way the seed viewer already opens. That surface is where the Operator lands on opening an engagement and persists across all four rooms, so the panel is reachable wherever the engagement is — which is the requirement, since a credential can be issued at creation or any time after.

> What the survey found. In an engagement but outside the four rooms, three things persist: the header (engagement name, focus chip, home, view-seed), the room tab strip, and the conversation. There is no engagement-level page above the rooms and no per-engagement settings surface. The header-opened panel is an established shape — the seed viewer and the voice-listening panel both use it.

Three placements were rejected, and why matters. A fifth room tab would change the list of rooms, which is the single source of that vocabulary and seed-level — it would assert that credentials are a room. A new route invents one where the address resolves to a single surface. The person-level settings page would need a selector to bridge person scope to engagement scope, which is the mismatch that ruled it out.

> One thing the precedent does not cover, and it is the important part. The seed viewer only reads. This panel acts — it issues and revokes write access to the engagement. The precedent settles where the panel lives and how it opens, and nothing more. The issue and revoke affordances need their own care and do not inherit the seed viewer's weight: a credential is shown once and cannot be shown again, and revoking cannot be undone.

What was built: the list, the issue form, the one-time reveal, and revoke — a surface, not a control added to one. (CR-2026-188 halted at Step 1 rather than build to the wrong premise; CR-2026-193 replaced it and shipped, tag cr-2026-193-b47-credential-surface.)

The issue and revoke affordances did not inherit the seed viewer's weight, which was the part the precedent could not settle. The form states what it grants — unmoderated write access to this engagement's Memory, by someone who is not a member — above its fields, where it is read before anything is filled in rather than discovered after the link exists. Revoke asks in the row and cannot fire on one click. The header control is named for the act rather than the shelf: "Grant access", a verb, beside the passive "Foundation".

> One finding from the wire, and it decided how the list reads. The engine writes status = 'expired' lazily — only as a side effect of somebody attempting a claim. A credential that quietly passed its expiry with nobody trying it still reports itself as awaiting claim. Reading that status would have shown dead access to the Operator as live. The row also carries the expiry date itself, so the surface compares that to now instead. No engine change was needed, and no state is shown that the wire cannot express.

The one-time reveal is a property of the wire, not a convention of the screen. The list query does not select the token column at all, so there is no route back to a token even in principle. The surface adds none: the token lives in a single piece of component state, cleared on dismiss, and reaches no log, no address bar, no query string and no browser store — checked by a test written for that failure specifically, because it is the one that would not surface in a test of ordinary behaviour.

B-51. Four components rebuilt on every change. DONE. What a user was losing was keyboard focus — and nothing else. (Surface 62b2f27, no change request.)

The question that decided the size is answered. Scroll position survives the rebuild — measured live in both arms, sampled two frames after the click as well as after it settled, because a transient clamp during the commit was the specific doubt. Container and document scrollTop both unchanged; the mobile scroll container is the same DOM node before and after. Nothing a user would call data was ever at risk — every hook lives in Home, and the rebuilt subtree holds no state, no text input, no cursor.

So it ranked where Wave 0 said it should: an accessibility defect plus wasted work, below anything that loses data. The fix was hoisting all four above Home, with LensBody mandatory as the cascade root — while it was itself a new type each render, its subtree remounted no matter what the other three did.

> One property is now load-bearing and was not deliberate. The desktop arm has no scroll container — it scrolls the document, so the scroller was never inside the remounted subtree. That is why scroll was safe there. A change that gives that arm its own container, or moves scroll ownership inside LensBody, changes the thing that makes it safe. Recorded at both call sites in the code, not only here, because that is where somebody would be standing when they made it.

And the measurement that answered it was wrong the first time. It read zero before and zero after — because the document was not scrollable at that viewport, so it could not have read anything else. It agreed with the expectation, which is when a result is least likely to be questioned. Corrected by asserting the precondition first. Filed as a new section of the observe-the-failure note (v0.6): every instance in that note had been an automated check, and this one had no artifact, nothing to run it twice, and nobody to review it.

B-52. The sources vanish on reload. READY — small. The answer survives a refresh; its sources do not.

B-53. A render's sequence number never arrives. DONE. The list query never asked for the column, so the reader got nothing back — indistinguishable from a record that genuinely has no number. The same defect was later found in the Shape path and closed as B-59. (CR-2026-171.)

B-54. Nothing requires that number to exist. DONE. The database now requires one.

Every path that creates a record now assigns a number, in production (CR-2026-176) and in the tests (CR-2026-190). Nothing in the system produces an unnumbered record.

Three deferrals, each for a real reason and never because it stopped mattering. The live paths still created unnumbered records (closed by CR-2026-176). Then the test fixtures did (CR-2026-190). Then rebuilding from the log produced them (B-70, CR-2026-191). All discharged. (CR-2026-192.)

What it buys, said plainly: not much today. Every path already assigns a number. The value is that the rule is now enforced rather than maintained — something new that forgets fails at once, where it went wrong, instead of quietly storing a record that misbehaves on a screen weeks later.

> Recording why the distinction was kept. Deferred and unnecessary look identical in a closed item a year later. Writing "deferred, and here is what it waits on" each time is what let this reach the end of the queue instead of quietly becoming a thing nobody required.

Five tests were retired or rewritten, and that was decided rather than absorbed. They created unnumbered records because the unnumbered state was the thing they pinned — a state the rule now makes impossible. Fixture convenience and subject matter are different questions, and each of the eleven affected tests was classified individually before anything was touched. The retired ones are kept where they were, with their reasoning attached, rather than deleted.

B-55. The screen says "of its type" without knowing what type is. DONE. It now names the type — or says nothing at all. The claim is checkable because the name travels with the thing.

Where it could not be named, the phrase disappears rather than trailing off. That instinct was already in the code for a missing number and had simply never been applied to a missing name; this made it uniform rather than adding a rule. (CR-2026-186.)

> The same divergence has now been found twice. A list of things and a single thing are built by different code, and fixing one leaves the other — first in B-44, and again here across four places. A third time and the question changes: not "check both" but "why can they differ at all." (Recorded in the completion note.)

B-56. A development sign-in is reachable on the live perimeter. READY — small. It refuses correctly and nothing is exposed. But it offers something it cannot do.

B-57. A screen that appears to argue with itself. DONE. (CR-2026-172.)

B-58. A superseded document still says it was never done. DONE. Corrected. The proposal was installed the same day it was written; the header was the only thing that disagreed. (Perimeter persistent-run proposal v0.2; v0.1 stands as a sibling.)

B-59. A shape's sequence number never arrives. DONE. The same defect B-53 fixed for renders, in the Shape path — the list query never asked for the column, so the reader got nothing back, which is indistinguishable from a shape that genuinely has no number. Found and deliberately left by CR-2026-171. Two queries carried it, not one; the second fed the downstream-impact screen. (CR-2026-175.)

B-60. Where a document came from is recorded, shown once, and then dropped. DONE. The structure now reaches Memory. When a file is read, the extraction records its structure — a Word document's headings, a PDF's per-page markers, which path the image reader took. That structure reached the screen that uploaded it and was then discarded before Memory; the event stored an empty list where the chain belonged. (CR-2026-194, tag cr-2026-194-b60-provenance-to-memory.)

It was not the small carry-through it looked like, and the reason is now its own standing note. The deferral comment said the executor's dict "doesn't trivially map" to the event model. Step 1 found that two of the three named fields mapped exactly and always had — every one of the ten skills emits skill_name and skill_version — while the other two, the timestamps, had no source anywhere: the executor had never imported datetime. So the CR halted rather than building to a premise that could not hold.

> The decision the halt surfaced. Carrying provenance through required either capturing the timestamps, relaxing the schema to make them optional, or filling them from the upload moment. Capture was chosen, and the reasoning is recorded in CR-2026-194 v0.2 §0: the schema is not wrong — a record of how something entered Memory reasonably includes when each step ran — and relaxing it would lower what provenance claims to be permanently, for every future consumer, to avoid two datetime.now(UTC) calls. Capture adds a real observation; relaxation subtracts a guarantee. > > The third option was refused outright. Filling both timestamps from the upload moment is the shortest path, produces a green suite, and fabricates an observation into an append-only log on every upload from that day forward. The refusal is now a test: it fails if the timestamps are copied rather than measured, because every other test in the CR would pass either way.

Historical uploads gained nothing, deliberately. B-70's backfill ratification does not reach this: extraction provenance was observed at ingestion, from a file that may no longer exist, by an extractor whose version may have changed. Nothing in the log determines it. Old events stay empty because they were empty.

One thing this completes into, and it is known rather than discovered later: nothing downstream reads provenance from Memory today. The field is correct and unread. A reader is a separate item, not a reason to have stopped.

It is not a limitation of the model. The field is deliberately free-form and already carries per-page and per-path detail elsewhere; the writing step simply leaves it empty, and has since it was built. Today it lives as a comment in the code describing itself as a follow-on — which is why it is here now. Whatever lands for spreadsheets and slide decks inherits the same drop on arrival. (uploads.py:956; found during CR-2026-181 Step 1.)

B-61. An answer can be shaped by material it cannot cite. DONE for the case that carries notes — see B-67 for what remains.

The defect was narrower than this line said, and sharper about who it hit. The standing block of recent notes does not reach every turn: it belongs to one of three mutually exclusive tiers and appears only while an engagement has notes but no organized view. That is the early-life state — new engagements, which is the worst place for it.

The fix was free, which is why it was the right one. On a recall turn the standing block was a truncated subset of the notes the answer already retrieves and already cites — same source, same order, fewer of them, cut to 150 characters. It is now omitted on that one turn. The model sees strictly more than before, in fuller form, all of it citable. (CR-2026-183.)

> Two remedies were rejected on their merits. Citing the truncated copy would have produced a source entry that misstates what the model saw. Telling the model not to rely on the block would have been a rule nobody can watch fail.

Originally: Every conversational turn carries a block of recent notes into the model's context. That block never appears in the answer's sources; only the notes fetched by an explicit recall question do. So on the one turn where the product promises every source is shown, the model has been handed material the answer cannot point at.

This is why B-13 is still open — "every source shown" is B-13's own wording. It sits against B-11's guarantee and the posture that claims should be checkable. Not a sizing question: making the two windows match, in either direction, leaves it exactly as it is. (scoping-notes/loomworks-recall-window-tier-2-relationship-scoping-note-v0_2 §3.)

B-62. Two windows on the same notes, and nobody chose the relationship. READY to scope — small. The standing context carries twenty notes; an explicit recall question fetches up to fifty. They are not required to match, but the code claimed they already did and that comment was false for as long as it stood. What relationship they should have is undecided. Measure first: a standing context of fifty would run roughly three times its stated budget, which likely settles it. (Same note, §5.)

B-63. Nothing marks an answer as written by a model. OPEN — and it would be a NEW commitment, not a forgotten one. Blocks nothing.

The thing this rested on does not exist. The foundation document defers to an "AI-as-faithful-clerk posture" and treats it as settled — the rule that you approve state transitions is described as consonant with it. That posture was never written down. It appears in the foundation document as a deferral and nowhere else, and the destination it defers to did not contain it (B-65).

> A correction, recorded rather than smoothed. The Operator confirms he does not recall writing it and considers it a good framing — which is not the same as having decided it. It was nonetheless asserted as settled, in a change request's acceptance criteria and in his own working notes. That assertion was unfounded.

Why this changes the item rather than just annotating it. A forgotten commitment gets recovered; a new one gets decided. Treating this as the first would have skipped the deciding entirely — the marking would have shipped as though someone had already weighed it. Nothing about marking model-written answers has been weighed.

What it would cost is real: a mark on every answer a model writes, non-suppressible, permanently. That is worth a decision, and the decision has not happened.

B-64. The list of what the system accepts is wrong, and nothing reads it. READY — small, and the decision is the point. There is an endpoint that answers "what kinds of file can I contribute?" It names four kinds and the system reads at least eight — Word documents, web pages, spreadsheets and slide decks are all missing from it. It has been wrong since the second pathway was built.

Nothing is broken today because nothing consumes it. The upload screen does not ask; it offers everything and lets the engine answer per file. That is exactly what makes it worth deciding now rather than later — an endpoint that is wrong and unread is harmless until the first thing that trusts it, and whoever wires that up should not be the one to discover it.

The decision is small: correct it, or remove it. Correcting means teaching it about the second pathway; removing means saying the honest answer is per-file rather than a list. Either is defensible; drifting is not. (Found during CR-2026-181 Step 5; affects Word documents identically and predates that change request.)

B-65. The foundation document points four topics at a document that does not hold them. IN PROGRESS — the destination now has four sections and they are deliberately empty. NEEDS YOU: content.

The foundation document defers four things — the AI-as-faithful-clerk posture, the six-stage engagement lifecycle, the badge mechanism, and retirement-with-dignity — to "the philosophy and architecture document." The architecture specification held none of them and no philosophy document exists.

Four sections now exist for them (architecture specification v0.7, Section 16). Each records what the foundation document promises it explains, quoted, and the questions that explanation must answer. None of them says what the four are — none of it is inferable from the record, and writing it would produce the exact failure worth finding here: a governing document that appears to answer a question nobody asked it.

> The lifecycle was deliberately not reverse-engineered. There is a lifecycle in the running system. Whether its stages are the six meant here is unknowable, and reading them off the implementation would record as decided something nobody decided.

The pointer is deliberately still wrong. The foundation document still points at this document, which still holds no content. Correcting it now would move the false pointer rather than remove it — it gets corrected as its own act, once the sections hold something.

What is needed is your content, not a session's. Four sections, each with its questions already written out.

B-66. A contributor who speaks is credited as the Operator. CLOSED — already fixed, and the symptom was never reachable. Nothing was built for this item. (Verified by CR-2026-195 Step 1, which halted rather than build it; architecture specification corrected at v0.8.)

The original entry said: the substrate knows the difference between the Operator and a contributor, the screen does not, and a contributor's words appear over the Operator's name. Two things were wrong with that, each sufficient on its own.

The labelling was wired by CR-2026-094 Step 5, about a hundred change requests before this item was written. The surface resolves a turn's author from the actor fields the engine serves and renders (Operator), (Contributor) or (Domain Expert). Nothing on the screen hardcodes a role — the only occurrences of the string are inside the comments of the file that fixed it.

And a contributor's turn is never shown to anyone else in the first place. Conversation history is scoped to the person asking for it — the route is literally "get my conversation history", and it is the only route that serves turns. A contributor's words could not have appeared over the Operator's name, because the Operator is never sent them. The live data makes this concrete: one engagement does have two speakers, an Operator and a real contributor with sixteen turns. Nobody has ever been shown those sixteen mislabelled.

> Where the item came from, which is the part worth keeping. It reached this list from a line in the architecture specification recording a known gap. The gap closed; the line did not, and the line is what got acted on — a change request was written, approved, and executed against it. That is now a standing note of its own (descriptions of state are not state), and the specification entry is corrected at v0.8 with its original text kept visible rather than overwritten, so a reader in six months sees that the gap existed and when it closed.

Two hundred and two conversation turns carry no author, and they were counted and left alone. Sixteen of them belong to that contributor. They could be labelled — the membership join resolves "contributor" today — and the code deliberately refuses, which is correct and must not be "fixed". Today's designation is not necessarily what someone held when they spoke, so applying it would assert a fact about the past from present state: the move [B-70]'s ratification rules out. The refusal now says so at the code, because a bare actor_kind IS NOT NULL gate reads as an oversight.

B-75. The in-engagement transcript throws away who spoke. READY — small, and it is defence against a change nobody has proposed yet.

What it does today. The in-engagement conversation surface drops the author fields at its adapter — the engine sends actor_kind, the display name, and the resolved designation; the surface's own turn type carries none of them — and renders every human turn in an unlabelled bubble, right-aligned, meaning me.

The trigger, stated plainly so nobody has to rediscover it. This is harmless while conversation history stays person-scoped: the only turns this surface can receive are your own, so me is accurate. It becomes a silent misattribution the day a shared transcript lands — an Operator reviewing what a contributor discussed, or any engagement-scoped rather than person-scoped history. On that day the other conversation surface is fine and this one is wrong, and nothing catches it, because there is no test that could fail today.

> And its type will make the bug look intentional. The surface types a turn's role as "operator" | "companion"the misattribution written into the type system, asserting that every human turn is the Operator's. Whoever reads it next will take that for a deliberate contract rather than an artefact of what the route happened to return, and will build on it. That is the part that makes this worth filing now rather than when it breaks: the cost is not the wrong label, it is that the wrong label looks like a decision.

(Found by CR-2026-195 Step 1 while establishing that [B-66] was already closed.)

B-67. The organized view is shown to the model and cannot be pointed at. DONE. An answer now points at the arrangement it was given — the organized view's own identity and version, plus the engagement version it was derived at, which says what state of Memory it arranged. (CR-2026-196, tag cr-2026-196-b67-organized-view-citation.)

It carries its own field rather than sharing the turn's payload slot, because that slot holds what a turn produced and an arrangement is what the turn was given — and any sharing would have forced a precedence order, making a displaced citation indistinguishable from no arrangement at all. And it cites what was rendered, not a re-read: the engagement context is cached, so a re-read at write time could name an arrangement the model never saw.

> [B-69] is unchanged and is NOT partly closed. The Kind B contact point stands: nothing recomputes when a seed moves. The drift is merely discoverable now, because a cited arrangement carries the seed version it was organised under — a side effect of citing the object, not a remedy.

(Superseded framing below, kept: the item as it read before it was built.) Once an engagement has an organized view, that view — its group headings, how many notes are in each, and the reasoning behind the arrangement — goes into every turn, and an answer cannot point at any of it.

It is not the same shape as B-61. No note content is involved: the individual notes are deliberately left out and only the arrangement is shown. So the model is not reading unsourced material; it is reading a summary it cannot attribute.

And the thing to point at already exists. The organized view is a real object with its own identity and version — a remedy could cite it rather than trying to cite the notes underneath. That likely makes this cheaper than B-61 was, and it is the first thing whoever scopes it should check.

Deliberately not folded into B-61. Different material, different shape, different remedy; B-61's fix does not touch it. (Found during CR-2026-183 Step 1.)

B-68. The accounting machinery names things for itself, and you see those names. SPLIT — the engagement's missing name is DONE; the two type names became [B-76]. The credit-reconciliation path creates objects an Operator can see, and gives them names written for code rather than for a reader.

The untitled engagement is fixed — and it was two engagements, not one. Both Phase 48 system engagements were inserted with an id and nothing else, so every screen showing either read "Untitled project". They are now Accounting and Credit Management. The item named only the first. (Engine d85154d.)

> The fix could not live only at the insert, and that is the part worth keeping. Bootstrap idempotency keys on the engagement's deterministic id, so on any database that already holds these rows the insert never runs again. A change made only there is green on a fresh database and changes nothing on a real one. A healing branch fills the title in when it is missing — guarded on is it absent, never does it differ, so it fills in what was never set and cannot overwrite a name somebody chose.

The two type names turned out to be a different item entirely. They read as a rename and are not one. Filed as [B-76], rewritten against what the scoping read found. (Scoping note: scoping-notes/loomworks-b68-accounting-names-scoping-note-v0_1.)

Three, all from the same bootstrap:

Two of these were found by accident while making a screen name the kind of thing it was showing, and the third turned up on the check for whether the first two were a habit. They are.

> Measured, not assumed. A sweep of every declared kind of work and kind of artifact in the system found exactly these two written as code identifiers — the rest are ordinary prose (Concept reference, Discovery record, Phase change request). So it is not general drift; it is one path that names things the way its code reads.

The fix belongs where the names are declared, not where they are displayed. Renaming them once at the source corrects every screen at once; correcting them at display would install a permanent translation that quietly stops working the next time something is declared this way. The prose fields on those same objects are well written — only the names and the engagement's title were left as they were.

(Found during CR-2026-186; the two type names are in the accounting bootstrap, the missing title is an insert that sets only an id.)

B-69. Whether a seed can change after an engagement is running. NEEDS SCOPING — seed-level, and nothing is decided.

The Operator's position: a seed should be editable. It is unlikely everything about an engagement is known at creation, and a seed that can never change forces people either to get it right up front or to work against a document they have outgrown.

Two cases, with different answers, and they have not been separated:

A candidate seed, before it is inducted. Editing it is a draft revision. Uncontroversial.

A running engagement's seed. Harder. Memory is append-only and corrections are preserved rather than smoothed — and every note contributed under an earlier seed was contributed against different stated commitments. That argues not against editing but for versioning: a new seed version with the prior versions kept as siblings, which is the discipline this project already applies to every other governing document.

Why it is not a small build. It touches what a seed is, which the foundation document defines. It is also adjacent to B-65 — "seed lifecycle" sits close to the six-stage lifecycle deferred to a document that does not hold it.

What comes first is a read, not a build. Can a seed be revised at all today? Is there version history? What happens to a running engagement whose seed changes — what reads it, what caches it, what derives from it and would not notice?

> ### Standing instruction, in force until this item is resolved > > Every change request records its seed-mutability check under a seed-mutability impact heading — including when the answer is that nothing was touched. A null finding is an entry. > > Record anything affected by a seed changing after an engagement is running: anything that reads the seed, caches it, assumes it fixed, or derives from it. And when a build genuinely has no contact, say so and say what was checked. > > Do not design for it. Do not build around it. Just record the contact point. > > The point is an inventory, not a search. When this is eventually scoped, the question "what breaks if a seed changes" should be answerable by reading a list that was written as the code was touched — rather than by a sweep conducted afterwards by someone reconstructing it. > > Why it rides along with every change request instead of being gathered once: what depends on a seed being fixed is visible when you are inside the code touching it, and invisible from outside. A sweep finds the obvious readers; the build finds the assumptions. > > The first entries arrived from CR-2026-189, and they are a kind this item did not anticipate. Not code assuming a seed is fixed — the engine already knowing a seed change matters, and the screen not carrying it. Two separate signals, computed server-side on independent paths, both stopping at the boundary. > > So the inventory has three kinds, and they want different answers. Sessions say which kind each entry is. > > Kind A — awareness that stops at a boundary. The engine computes a seed signal; nothing downstream carries it. Remedy: show what is already there — cheap, and safe before anything is decided, because surfacing an existing signal is not designing for an open question. Closeable as found. > > Kind B — code that assumes a seed cannot move. Caches it, pins it, derives from it without noticing. Remedy: unknown, and waits on this item — it is precisely what has to be settled. Not closeable. > > Kind C — checked, no contact. The build looked and found nothing that depends on a seed being fixed. This is an entry, not a non-entry. An inventory holding only hits cannot distinguish no dependency from never looked, and when this item is finally scoped the question will be what was examined — which a list of hits answers only by accident. Coverage is what makes the inventory usable. > > The worked example, and it is what keeps Kind C honest. ShapeEvent.seed_version_at_production stamps the seed version in force when a shape was produced. It is genuinely seed-aware. It is not seed-dependent — display numbering never reads it, and nothing about it changes if the seed moves. Seed-adjacent is not seed-dependent. Without that line, Kind C inflates with every field whose name contains "seed" until the inventory means nothing. (CR-2026-190.) > > Use the heading verbatim. Entries are only findable as a set if every session writes the same words.


Part 3 — Your track

B-20. The marketing website. READY. B-22. Package Stele. WAITING (background). B-23. The security story. READY. B-24. Investor-visible FORAY. READY.


Part 4 — Deliberately parked

Restricted-visibility slices 2–3 · target-hosted visibility · protocol wire-format changes · OVA's remaining standalone role · cross-fund questions · the stray zip file in the record's working tree — every session reports it and leaves it alone; a one-line decision ends that · how many live records carry a fabricated author · whether playground_dev's schema matches what the chain now produces · whether a held shape whose job failed is ever cleaned up · how the universal commons should be searched — it grows with adoption rather than with work, and it is a different question from B-12.


Waiting on you

Nothing. For the first time since this list was written.

Everything merged is running, deployed 2026-08-06 and verified in the product one item at a time.

Both questions are answered. Sources will appear on every answer a model writes, and not on answers the system assembles, because those already show their own material (Q-6: split). Confirming a Shape stays on the screen for now, and reopens after the Companion's other writes have been used (Q-7: defer).

B-12's unread fact is measured. The largest engagement anyone works in holds fifty-eight notes against a window of fifty. Raising a constant closes it for everything that exists — and the commons, which grows with adoption rather than with work, is parked as its own question.

The seed is current and committed at v0.14.

Two decisions sit on the queue and block nothing, unchanged: per-statement provenance, and whether a walk should use the event identifier. Neither has a deadline and neither stops anything.

Everything else proceeds without you.

B-70. The organized views hold facts the event log cannot reproduce. DONE. The log now holds what the views hold, and rebuilding reproduces them.

Rebuilding an engagement's shapes or renders from the log loses their sequence numbers. Not a risk — a measured fact, true today: 63 of 64 shape events and 27 of 28 render events carry no number in the log. Almost the entire history.

How it happened, and nobody did anything wrong. When numbering was introduced, existing records were filled in by updating the views directly. The log was left alone — correctly, because the log is append-only and rewriting history is not available. But that left the number living only in the derived copy.

Why it matters more than a missing number. Rebuilding from the log is the repair tool — what you reach for when the derived copy is already damaged. The repair silently discards data the damaged copy still held. And the deeper problem is the inversion: the derived thing carries information its source does not, which is the one relationship the whole substrate is built to prevent.

Day to day, nothing is broken. Ordinary operation preserves the numbers; the current views are complete and correct. The loss appears only on a full rebuild — which is to say, only when something has already gone wrong.

The remedy is to put the numbers into the log, as new events recording what was computed. Append-only, invents nothing, and makes rebuilding correct rather than making it something to avoid.

> One remedy is ruled out, firmly. Having the projection assign a number when the log doesn't carry one would make the derived copy a writer of facts rather than a reader of them — two rebuilds could disagree with each other, and the log would stop being the truth. Convenient, and it costs the substrate's central guarantee. (Operator ruling, 2026-08-08.)

What it took first was a decision nobody had made. Three earlier migrations had already filled in an identifier this way, relying on a principle that had been acted on three times and never ratified. It is ratified now, with the boundary that was missing: this covers identifiers the log already determines — where the fill-in writes down what was always true — and not content, state, commitments, attribution, or anything that is a judgment rather than a computation.

The test of it is a counterfactual. Had the number existed from the start, every record would carry exactly the one that was filled in — verified, every one of fifty-four. Nothing new was asserted about the past.

> The alternative was rejected on its own terms. Recording the numbers as new events would have kept the letter of never-editing-the-record while writing fifty-two revisions that never happened into a record whose purpose is truthfulness about what happened. And no honest author existed for them — no one did anything. (CR-2026-191.)

Interim note superseded: standing-notes/loomworks-standing-note-legacy-engagements-cannot-be-rebuilt-from-the-log-v0_1 described the condition while it held. Rebuild is faithful now — proven by a test that rebuilds and compares, not by payloads having changed. [B-54]'s database rule is unblocked. (Found by CR-2026-190, closed by CR-2026-191.)

B-71. A test directory that has never been collected. READY — the fix is small; what it implies is not.

tests/lib/ has never run. The surface's test configuration collects tests/components/ and nothing else. tests/lib/api/grouping-adapters.test.ts sits outside that pattern, so it has reported nothing since the day it was written** — not a pass, not a failure, no output at all. It covers the wire-boundary adapters for workspaces, tags and saved filters: the projections that translate engine field names into the ones the rest of the surface uses, which are exactly the things that break quietly when a name changes.

This is the vacuity mode one level up. Every instance recorded in observe the failure before trusting the check is a check that ran and could not fail — an assertion over an empty list, a guard whose input had quietly emptied. This one never ran. The distinction matters for how it is caught: a vacuous assertion is at least visible in a test report as a passing test, and can be caught by asserting non-vacuity inside the check. A file the collector never opens produces no line to read. Nothing inside the file can defend against that, because nothing inside the file executes.

> How it was found, which is the uncomfortable part. Nobody audited for it. During CR-2026-193 a new adapter test was written into tests/lib/api/ — the obvious home, alongside the existing adapter tests — and the suite reported it as passing. It had not run. The count in the summary was the only thing that gave it away, and only because the run happened to be scoped narrowly enough that a missing file was noticeable. On a full-suite run it would have been invisible. The new test was moved to tests/components/lib/, where the repo's other adapter tests actually live and where the suite does collect it; the pre-existing file was left exactly as found, because deciding its fate is a decision, not a cleanup. > > So the finding is not "one file is stale." It is that the surface has two plausible-looking homes for adapter tests and only one of them is real, with nothing anywhere to say which. The next person will make the same choice for the same reason.

It was run, and it is green. Three assertions, all passing, executed under a throwaway configuration that collected tests/lib/ and was then removed — the repository was not modified and nothing was fixed. What it checks is real and still worth having: that the workspaces, tags and saved-filters adapters each project an engine field name to the Operator one and leave the engine name off the projected object entirely** — engagement_countproject_count, engagement_idproject_id, engagement_idsproject_ids.

> Green does not shrink the finding, and it is worth being clear about why. The value of a test is the report it produces when it fails. A file nobody collects produces no report in either state — its passing today is a fact nobody could have learned from the suite, and had it gone red at any point in its life, nobody would have learned that either. It has been carrying no information for its whole existence. That it happens to be correct now is luck about the adapters, not evidence about the check.

What to do. Either widen the collection pattern or move the file, and whichever is chosen, leave the other home unable to look plausible — the standing hazard is two believable homes for adapter tests with nothing to say which is real. (Found incidentally by CR-2026-193; run and confirmed green 2026-08-09.)

B-72. Nothing anywhere makes a test runner say what it collected. READY to build — it has acceptance criteria and two real fixtures. The general form of [B-71].

The check. Compare the test files on disk against the files the runner actually collected, and fail — or at minimum report — when the two disagree. One line of output, in every repository, run in CI alongside the suite.

Acceptance criteria

1. It finds [B-71]. Pointed at the surface repository as it stands, it reports tests/lib/api/grouping-adapters.test.ts as on disk and never collected. A check that cannot find the defect that produced it is not built.

2. It does not flag either known-good case. These are not hypotheticals — they are the two files the engine sweep actually turned up, and a naive implementation reports both. They are the fixtures:

| Fixture | What it is | Why a naive check flags it | Required verdict | |---|---|---|---| | src/loomworks/agents/test_external_specialist.py | Production source — a stub render specialist, i.e. a test double. Zero test functions. Sits outside testpaths. | Matches the runner's default test_.py pattern, and produces no collected tests. | Silent. (See [B-73] — the file itself is a trap worth removing, but the check must be correct while it exists.)* | | tests/test_phase_36_backfill.py | CR-2026-192's deliberate tombstone. Docstring only, zero test functions, kept inside testpaths so the retirement stays discoverable where the tests used to be. | Matches the pattern, sits in the collected path, and produces no collected tests. | Silent. |

One is production source that merely looks like a test; the other is an intentionally empty marker. Between them they cover both shapes of false positive — outside the collected path, and inside it.

3. It distinguishes the three states, not two. Silently uncollected is the finding. Not actually a test and deliberately empty are not, and the check must be able to say which is which rather than lumping everything that produced no tests into one bucket.

4. Emptiness can be declared. A file that is deliberately empty should be able to say so in a way the check reads, rather than depending on a maintained exclusion list somewhere else. The tombstone already declares itself in prose; the check should not require that prose to be duplicated into a config file that will drift from it.

> Why these criteria exist at all, and why they came before the build. The engine sweep was run to answer whether the engine had B-71's defect. It did not — but it turned up both files above, and a naive comparison of name-matching files against files that produced tests would have called both of them findings. A check that cries wolf twice on its first repository gets switched off, and then it is worse than nothing: it occupies the place where a working check would go. The likely implementation follows directly: match on files that contain test functions, not on files that match a name — which resolves both fixtures on its own, since neither contains one.

Why it is its own item and not a note inside [B-71]. B-71 is one directory in one repository. This is not about that directory. Every repository we have has a collection pattern, and none of them is checked against what is on disk. A test file can be written, reviewed, committed and believed in without ever being opened by the runner, and today nothing in any of our repositories would say so. The remedy generalises exactly; the finding that produced it does not.

It is the same shape as asserting non-vacuity, applied one level up. The standing note's rule is: for a check over a whole surface, make the check state what it inspected, so it cannot silently become a no-op when its input empties. That defends an assertion that runs. This applies the identical move to the collector — make the collector state what it inspected — and defends against the case the standing note's rule cannot reach, because a file that is never opened has no assertion in it to defend anything. (See standing-notes/loomworks-standing-note-observe-the-failure-before-trusting-the-check.)

> The engine's suite has now been checked, read-only, and it is clean. (2026-08-09. This replaces v0.47's entry recording it as unknown — the reason for checking stands, and is why the answer is worth having.) > > The comparison. Configuration is testpaths = ["tests"] with no python_files override, so the runner's own patterns are test_.py and _test.py. 451 files anywhere in the repository match those patterns. 449 contribute collected tests. 3,658 tests collect. Both files in the gap were inspected without being run, and neither is a silently-missed test: > > - src/loomworks/agents/test_external_specialist.pynot a test. It is production source: a stub render specialist, a test double, with zero test functions. It sits outside testpaths, so it is correctly excluded. It is worth knowing about anyway: it matches the runner's default name pattern, so it is a trap primed for the day anyone widens testpaths or runs the collector against src/. > - tests/test_phase_36_backfill.pya deliberate tombstone. CR-2026-192 retired the tests it held and kept the file as a docstring-only marker, explicitly so the retirement stays discoverable from where the tests used to be. Its emptiness is the point and it is documented in the file itself. > > Nothing inside tests/ is missed. Every .py file there that does not match the naming patterns is a conftest, a helpers module, or an __init__, and none of them contains a test function — checked, not assumed. So the engine does not have the surface's defect: no file there has been written, committed and believed in while never being opened.

> Both files are now [B-72]'s acceptance fixtures, not just prose about them — see its criteria above. The check must stay silent on both or it is not built. > > Recorded as a null finding, deliberately — per the [B-69] lesson that an inventory recording only hits cannot tell no problem from never looked.

B-73. A production file named like a test. READY — a rename, and the reason to do it now is timing rather than severity.

src/loomworks/agents/test_external_specialist.py is production source. It is a stub external render specialist — a test double — with zero test functions. The test_ prefix names what it is for, not what it is.

It is harmless today and that is the whole point. It matches pytest's default test_*.py collection pattern, and the only thing keeping it out of the runner is testpaths = ["tests"]. Nothing about the file itself is protecting anything — the protection is a single line of configuration in a different file, holding by coincidence rather than by intent.

What it is primed for. Anyone who widens testpaths, adds a second entry to it, or points the collector at src/ for any reason collects a production module as a test. What happens then is not a clean failure: it imports at collection time, contributes zero tests, and looks exactly like a file that is silently uncollected — which is to say it looks like [B-71], in a repository that does not have B-71.

> Renaming it is cheap; discovering it later is not. Today it is one git mv and its import sites. Later it is a confusing collection error, or worse, a false reading of whatever [B-72] reports — the check would be correct and the repository still misleading. This is the cheapest it will ever be to fix, and it was found by accident while looking for something else.

(Found in passing by the engine collector sweep, 2026-08-09. Not fixed then, deliberately — the sweep was read-only and a rename is a decision.)

B-74. Multi-file upload aggregation is dropped at the same statement, under a follow-on nobody filed. READY to scope — unknown size until somebody looks, which is the point.

The same event construction that dropped [B-60]'s provenance also writes:


parent_upload_event_id=None,
folder_manifest=None,

under a comment reading "None for v1 per-file children; multi-file aggregation persistence is a Step 1 follow-on." That follow-on does not appear on this list, and no change request carries it. Found by CR-2026-194's Step 1, which was asked whether the same drop happened anywhere else.

What is not known, and should not be guessed: whether a folder upload's structure — which files arrived together, and under what parent — is persisted anywhere else, or is simply lost the way provenance was. The response shape carries folder_manifest; the event does not. That is the same signature as B-60 and it may be the same defect, but nobody has checked. Scope it before sizing it.

> This is the second time a code comment has been the only record of deferred work — and the first time was this identical statement. [B-60] itself reached this list in v0.29 only because a sweep went looking; until then it lived as an in-code comment describing itself as a follow-on, which is not the same as being tracked. Both follow-ons were written in the same block, by the same hand, on the same day. One was found by a deliberate sweep sixteen versions ago; the other survived that sweep and was found now, by accident, while fixing its neighbour. > > So the pattern is not "a comment went stale." It is that naming something a follow-on inside the code reliably feels like filing it, and the feeling is indistinguishable from having filed it — the word "follow-on" is doing the work of a tracked item without any of the properties of one. A sweep catches these one at a time and only where it happens to look. The general question, and it is worth an item of its own if it recurs a third time: what would make an in-code deferral impossible to leave untracked?

B-76. The reconciliation type names are a lookup key, not a label. READY — and it is NOT the rename it looks like.

Two objects an Operator can see carry names written for code: a kind of work called ReconciliationCorrection, and a kind of artifact called CorrectiveForayFlow. That much was [B-68]'s framing and it was right about the symptom.

It was wrong about the work, in a way worth recording. These are not display strings generated at write time. They are stored in the objects' payloads and they are the key three lookups match on — two in the proposal applier, one in the specialist bootstrap, each resolving a name to the object's reference. Runtime dispatch keys on the object's id, not its name; but the name is what resolves to that id. And the literal is declared three times, in three modules — so even the trivial half is not the single-place edit the item assumed.

> The trap, and it is the reason this is filed rather than done. Bootstrap idempotency keys on deterministic ids, not names. Rename the constants and: a fresh database gets the new name written and every lookup finds it — green; an existing database keeps the old stored name, because the bootstrap finds the row by id and leaves it alone, and the by-name lookups then find nothing and raise. The change passes the suite and breaks every system that already exists. > > This is the fourth instance of green-because-the-test-cannot-reach-the-defect, and it has a shape the others did not. The earlier three were checks that could not fail. This one is an entire environment that cannot fail: a fresh database never exercises the path an existing one takes, because idempotency means the two run different code. Any test that only runs against a fresh database is blind to that whole class — not to one assertion, to every defect that lives in the difference between creating and encountering. Worth holding onto beyond this item.

How it is to be done, ruled:

  1. Key the three lookups on the deterministic ids, which already exist as constants and which the bootstrap's own idempotency already uses. This makes the names pure display strings — which is what [B-68] wanted in the first place. This is the item; it is the substantive change and it carries the risk.
  2. Then rename, in all three declaring modules. Trivial, and it comes last.

Two paths are refused, and both look reasonable:

(Found by scoping [B-68], 2026-08-10.)

B-77. The Companion's prompt cache watches the wrong log. READY — filed first, independent of [B-8]; latent today, one bootstrap change from live.

The mechanism. The Companion's assembled prompt — which carries the engagement's seed in all three context tiers — is cached process-lifetime with no TTL (orchestration/prompt.py:141), and the cache's only invalidation predicate is engagements.current_engagement_version (prompt.py:469-472). But seed writes do not land on that log. Seeds live under the administrative engagement (seed_lookup.py:10-20); revising one bumps the administrative log, not the working engagement's. The cache is watching a version that the seed's own writes never advance.

The invariant that currently saves it, recorded here because it is unwritten anywhere else and unenforced: seed amendment convergence calls revise_engagement on the working engagement (engagement/seed_amendment.py:146-153creation.py:920-927), which appends an engagement_revised event and so bumps the version the cache checks — incidentally. The cache is correct today because of the amendment orchestrator's write order, not because of anything the cache verifies. A second saving fact rides with it: operator-created engagements pin their seed_ref at a version (creation.py:1058), so a moved seed cannot change what the pinned loader returns.

Where it goes live. The bootstrap paths construct engagements with unpinned seed_refs (engagement/bootstrap.py:151, 253; credit/bootstrap.py:205). For those, get_current_seed resolves latest seed version at read time — so a new seed version changes what the loader returns without touching current_engagement_version, and the cached prompt keeps the old seed indefinitely: a Companion speaking from a superseded governing document, with nothing anywhere that will ever notice. Any future path that creates an unpinned engagement, or any seed write that stops going through convergence, makes this live.

> [B-69] seed-mutability impact — two Kind B entries, the inventory's first. (1) The cache above: pins a seed-derived artifact whose invalidation predicate assumes the seed cannot move without the working engagement's version moving. (2) Found in the same read: Manifestation's preview resolves the seed's content into the organizing prompt, and the resulting arrangement is stored verbatim at derive (manifestation.py:461-463, stored at :570) — seed-derived output frozen at preview time; nothing recomputes or flags it if the seed then moves. The only flag, seed_version_changed, appears on the next preview and has zero code consumers (Kind A). Neither is designed for here, per the standing instruction — recorded as contact points.

The remedy is not decided by this filing. The cheap move — key the cache's validity on the resolved seed version as well — is Kind-A-shaped (surface what is already computed) and safe before [B-69] is settled; documenting the pin-at-creation invariant where the bootstraps would meet it is the other half. What is not acceptable is leaving the invariant implicit now that it is known.

(Found by the B-8 slice-two scoping read, 2026-08-10, engine d85154d.)

B-78. The Manifestation write through conversation — split from [B-8] slice two. NEEDS ITS OWN CHANGE REQUEST — the consent must be built by hand.

Why it split. The scoping note ranked "derive a Manifestation" the cheapest knowable write. The code says the opposite, twice:

And it has no name in the delegation vocabulary. produce_specification, produce_artifact, initiate_render, commit_notes exist; nothing for deriving a Manifestation — so a conversational route to it today would sit entirely outside the delegation layer. The capability must be added before any handler can be gated.

The intended shape, named now so the item carries it: stated-facts-before-the-call, per CR-2026-189. The consent names what is knowable free of charge before the metered call — the balance the spend draws from, the roll-up's change counts, the seed-amendment count — and explicitly does not claim a price. The surface's RederiveConsent panel already says it correctly; the Companion's version states the same facts in conversation and waits for the Operator. The pattern transfers; the component does not.

Also carried from the read: the derive stores the previewed ordering with no re-validation — the surface's approval works because the preview is adjacent to the button. A conversational trigger must reproduce that adjacency (the consent, the preview's outcome, and the confirmation must be bound to each other), or it is an approval of something the Operator has not seen.

(Split from [B-8] by Operator ruling, 2026-08-10.)

B-79. Confirming a shape enqueues zero renders in production — the dispatch hook is registered only by tests. NEEDS A DECISION, not a build — what does "confirm" mean?

The plain facts. shape_confirmed_post_hook (engagement/shape_confirmed_dispatch.py:79) exists to auto-enqueue render production when a shape is confirmed — one job per active declared render-type mapped to the confirmed shape's type. It only fires if register_shape_confirmed_dispatch_hook() has been called, and nothing in src/ or scripts/ ever calls it — only tests do. The FastAPI lifespan registers materializers, dispatchers, and specialists, but no post-append hooks; the same is true of register_shape_produced_dispatch_hook and register_drift_dispatch_hook. Production renders today happen only via explicit POST /renders. Confirming a shape changes the shape's state and records the attestation — and enqueues nothing.

Why this is filed as a decision. Whether confirming a shape should enqueue renders is a question about what "confirm" means — an attestation of the shape, or an authorization of everything declared downstream of it. The Shaping room's design history says the confirm control must name what it will produce before it is pressed; a confirm that silently enqueues nothing and a confirm that silently enqueues everything are both wrong against that standard, in opposite directions. The selector that would name the outputs exists only as a private method with no HTTP surface (render_dispatch.py:568). Settling this is prior to any surface or Companion work that speaks about confirmation consequences — which is exactly why it was kept out of the [B-8] slice-two change request (and why Q-7's defer was the right call: the two-turn conversational confirm would today be confirming a consequence that does not occur).

(Found by the B-8 slice-two scoping read, 2026-08-10. The hook body itself was confirmed present but deliberately not audited line-by-line — the finding is about registration, not correctness.)

B-83. The engine's push CI is red, and every commit lands unsignalled while it stays that way. DONE — observed green: run 31416329840 on f338f3f, suite 20m57s, the first green in seven runs. One residue left open below, awaiting a ruling.

How the count went from two to six, stated for the record because the ruling was written against two. The ruling was made against what was visible: two pushes red at the stand-up failure. Fixing that failure let the suite run — and the suite immediately showed an older failure underneath, red since CR-2026-194's own push. The last green was CR-2026-192 (ae58d34); six pushes landed unsignalled, not two. The undercount was itself the defect's signature: red-on-red is invisible, so the standing failure concealed both the older one and the true duration.

The causes were three, not one:

  1. The stand-up failure (2 pushes, deterministic, masking everything): B-68 item B added engagements.title reads to the credit bootstrap, which the CI stand-up contractually runs at the 0033-point schema — title arrives in 0049. Fixed at d172da3: every title read/write conditional on the column existing; the B-68 heal fills titles at the next head-schema run. Verified locally end-to-end before pushing.
  2. The missing OCR binary (all 6 pushes, deterministic): CR-2026-194's PDF-provenance test has a fixture below the 30-char per-page threshold by construction, so it always takes the OCR fallback — "Tesseract first" — and pytesseract's system binary was never on the runner image. Local machines carry it via homebrew, which is why every local suite was green. Fixed at f338f3f: the workflow installs tesseract-ocr. CR-2026-194's completion record said "push-verified"; its engine run was red — a red gate makes even the records wrong.
  3. A timing flake, intermittent — now filed as [B-84].

> Two corrections, the Operator's own, recorded 2026-08-10. "I ruled on B-83 against one cause and two pushes; it was three and six. The undercount was the defect's own signature — red-on-red is invisible, so the standing failure concealed the older one and the true duration. What I wrote as an argument ('can't distinguish a new failure from the standing one') was already a description of what had happened." And: "CR-2026-194's 'push-verified' was false — its engine run was red." Recorded in the same family as the stale build-list statuses: a broken gate doesn't only stop catching defects, it makes the records assert things that aren't true. The ordered audit of every completion between CR-2026-192 and now found one further false claim — CR-2026-196's "all four push-verified" — corrected at its completion record v0.2; the rest either made no CI claim or made it honestly.

What it costs while it sits, which is why it outranks the others: first read as red-for-two-pushes; the fix revealed six — last green was CR-2026-192 (ae58d34). Local green is a fallback, not the gate. A permanently-red check is worse than none: nobody reads it, and it cannot distinguish a new failure from the standing one. That is the same property as a check that cannot fail, arriving from the other direction — and it was demonstrated live in this very repair: the stand-up failure (two pushes) was masking an older test failure (all six, since CR-2026-194's own push), which nobody saw because red-on-red is invisible. The completion record for CR-2026-194 says "push-verified"; its engine run was red. A red gate makes even the records wrong.

The cause, recorded in the family it belongs to. B-68 item B (d85154d) added a title read (and write) to the credit bootstrap — which the CI stand-up runs at the 0033-point schema, between alembic upgrade 0033 and the walk to head, because migration 0034 depends on the administrative rows existing and no migration creates them. engagements.title arrives in 0049. CI's migration order differs from the local order — an environment running a path the tests never exercise. Fourth instance of green-because-the-environment-cannot-reach-the-defect, after [B-76]'s fresh-vs-existing database and [B-80]'s components never meeting real wire data.

> Noted without blame: item B was the one build in this run done without a change request. The scope was right and the fix was right; what it broke sits outside what a local suite can see. The lesson is not "always write a CR" — it is that a no-CR build should ask: what runs this code in an environment I'm not testing in?

The second cause, found when the first was fixed. test_pdf_structure_reaches_memory (CR-2026-194's own test) has failed in CI since 4f9374f: its fixture text is below the 30-char per-page threshold by construction, so the page always takes the OCR fallback — "Tesseract first" — and the runner image never had the tesseract binary (pytesseract is in the lockfile; the system binary is not a Python dependency). Local machines carry it via homebrew, which is why the suite is green everywhere except CI. Fixed by installing it in the workflow. Fifth instance of the family.

The first fix, and why it is not the reordering it first looks like. Running the bootstrap after the walk to head is unavailable — 0034's dependency is hard. The bootstrap's contract includes the 0033-point schema, so the fix makes it honor that contract: every title read/write is conditional on the column existing (information_schema probe); at the pre-0049 schema the rows are planted in their pre-B-68 shape, and the B-68 heal branch fills the titles in on the next run against a head schema — verified locally end-to-end: fresh DB → 0033 → bootstrap (succeeds) → head → bootstrap again → both titles present. The failure was reproduced locally before the fix was written, per the standing discipline.

B-84. One test can re-redden the gate at random. DONE — CR-2026-198, engine 4ae5509; push run 31430333953 watched green; 20/20 consecutive local passes; the deliberate property-break observed failing 5/5 first. The wait condition is now the property (events committed while the slow poll provably blocks); the bound is liveness-only; the assertion strengthened to exactly two. A second race of the same kind was found hidden under the first and closed by the same condition.

test_external_polling::test_concurrent_polls_for_different_specialists is empirically flaky in CI: failed in 2 of the 6 suite-reaching runs since CR-2026-192 — "Expected ≥2 polled events committed before slow poll returns, got 1" — and passed on rerun with no code change. Green locally throughout. A timing assumption (two concurrent polls commit before a deliberately slow one returns) that shared CI runners do not always honor.

Why it ranks with [B-83] rather than below it: an intermittently-red gate gets read as noise, and once people learn to rerun rather than read it, it has stopped being a gate — the exact state [B-83] just left. [B-83] bought a gate that answers; this item is what keeps the answer meaning something. The remedy is not "rerun on failure" — it is making the test's timing property deterministic (or proving the code's concurrency property some way that does not race the runner's scheduler). The fix must be observed failing first on the recorded assertion, per the standing discipline; a fix that merely widens a timeout should be treated as the suspicious move it is.

(Found by the B-83 repair, 2026-08-10 — visible only once the deterministic reds were cleared.)

B-80. Three approval-card components render dead or hidden controls against real wire data. READY — the generic card is already fixed; these three are what remains.

The engine's wire value for a pending card is pending_approval (notifications/schemas.py:62). ProposalApprovalCard, GrantDecisionApprovalCard, and SpendPauseApprovalCard each initialize local state as (approval_status as "pending"|null) ?? "pending" and compute resolved = localStatus !== "pending"so a genuinely pending card reads as already resolved, and each component hides its own wired button row, including spend-pause's "stop pausing." Since CR-2026-197's generic-card fix, the inner NotificationCard renders Approve/Decline inside these components — with no handlers attached. Observed live 2026-08-10: the spend-pause card's visible Decline was clicked and nothing changed; the same endpoint called directly declined the card at once.

> The empirical fact that made it invisible: 891 notification rows in the development database, zero with a non-null approval_status. No approval card had ever existed, so this render path had never met real wire data. And each component's vitest hands it the value it expects rather than the value the wire sends — green-because-the-environment-cannot-reach-the-defect, the same family as [B-76]'s fresh-vs-existing database: the test environment structurally cannot produce the input production produces.

(Found by CR-2026-197 Step 5's verify, 2026-08-10; the generic card shipped fixed in that CR at surface 35a49c9.)

B-81. The conversational grant is unreachable in-engagement — and the retry claims a save that never happened. DONE — both halves. Truthfulness half: CR-2026-199 (with [B-82]). Grant pathway: CR-2026-200 — extraction reads the Operator's own words on both paths (the raw message was already in scope at the failing line; add_knowledge needed one forwarded parameter); scope defaults to the engagement the grant was typed in (everywhere-markers widen; the personal path keeps "all", the Operator-confirmed extension); and the confirm became readable FIRST: one renderer names capability, scope by project name, and approval mode at the held card, the ack, and personal recall — the card that must be readable is exactly the card that must be pressed. The matcher itself untouched, pinned by negative controls. Verified in the product end to end — the eye-test transcript is in the completion record.

Two defects on one pathway, found together:

First: the sentence the Companion itself suggests does not do what it says. The denial reply for an unauthorized draft tells the Operator to say "you can draft specifications." Said in an engagement conversation, that sentence classifies as add_knowledge and commits as a plain project noteextract_delegation_grant recognizes it perfectly, but the extraction is wired only into the personal remember-about-me path (router.py, the _route_remember_about_me grant branch), which an in-engagement turn does not take. The system's own suggested words, followed exactly, produce a note instead of a grant.

Second, ranked the serious one: a reply claimed a personal save and nothing was written. "Remember about me: you can draft specifications" produced "Saved to your personal memory across all projects: …"and no assertion was written anywhere, verified against every engagement. A system that tells someone it saved something and didn't is the failure [B-11] exists to prevent, on a path [B-11] does not cover. The save-claims-gated-on-real-writes discipline (server-composed acks) evidently has a gap this phrasing walks through.

Consequence already recorded: CR-2026-197's acceptance-gate clause "granted conversationally" is unmet as written — the delegation was created through the service layer because the surface path does not exist. (Found by the CR-2026-197 product eye-test, 2026-08-10.)

B-82. The same sentence routes differently by conversation length, and the unconstrained reply overclaims. DONE — closed by CR-2026-199 as member 4 of the truthfulness class: the fallback now states no engine operation ran and forbids claiming otherwise; the persona carries the general rule. The classification context-sensitivity itself is by construction and was never the item — the overclaiming reply was.

"Draft an application specification for this project." routed to request_draft in a short conversation (correct denial, correct proposal) and to general conversation in a longer one — where the unconstrained reply said "I'm drafting an application specification … now." Structurally nothing could dispatch: the vague path has no code route to any write, and no card or job existed. The guarantee held; the wording overclaimed. The classifier is LLM-based and context-sensitive by construction — the item is not "make classification deterministic" but that the fallback path's reply can claim actions the fallback path cannot take. (Found by the CR-2026-197 product eye-test, 2026-08-10.)

B-85. The API refused the temperature pin, and every prose reply fell. DONE — filed and closed same session; ruled to the retry shape.

The Anthropic API began rejecting temperature for the responder's resolved model (observed live: claude-opus-4-7 via the credit-tier router — the sonnet default was the assumed model; the live one differed), taking down every responder-composed reply in development while the classifier (Haiku 4.5) and every server-composed reply carried on. The Operator's ruling, and it decided everything: unconditional removal fixes one seam at one endpoint and leaves the same failure waiting at all the others; the retry preserves CR-2026-169 D-1 wherever the API still honours it, which is what the decision was for.

The three requirements, all in: (1) the fall is visible, not silent — a WARNING log naming seam and model, plus the fallen_pin_seams registry, so "this seam is no longer reproducible" is a findable fact (live-fired exactly once during the eye-test, naming seam=responder model=claude-opus-4-7); (2) recorded as a partial surrender of D-1, not a workaround — in this item and in CR-2026-169's own record at v0.2, so nobody later reads the pin as still universal; (3) scope: the responder seam only, today — the same refusal message in any other pinned seam's failure path is what would say a second has fallen, and the helper is theirs to adopt then. Retry only on the exact refusal; any other error propagates. Observed failing first; edge tests pin no-retry-on-unrelated-400 and pin-stays-when-accepted. (Engine 722592a; push run 31456014937 watched green — the fifth consecutive.)

B-86. The grant arc leans on the denial's prose as classification context. ROUTING HALF CLOSED BY CR-2026-203 (2026-08-12): the deferral's trigger fired live (E0127), the re-measurement found the dependency larger than the premise, and the ruled remedy became raw-message extraction ahead of routing — the grant path no longer depends on classification at all. The classifier change is RULED OUT (does not rise). The persist-the-outcome half proceeds as its own thing: CR-2026-205, drafted, awaiting execution approval.

Why the smaller remedy: CR-2026-201's re-measurement — 18 trials, zero misroutes, 3/3 consistent per condition — found the dependency measurably smaller than scoped once the all-claim replies were deterministic. A change moving all 32 intents to fix a shrinking dependency is the wrong ratio. The persisted outcome earns its place independently of routing: the operation outcome (denied / approval_card / executed …) is a structural fact that today survives only inside prose — conversation_turns records classified_intent and structured_data, but not what the operation actually did. The steering finding, its own result: with composed denials in history the grant sentence routes to the wired grant path (add_knowledge, 3/3); with fallback prose it routes to a re-ask (request_draft, 3/3). Composed replies don't just stabilize routing — they steer it correctly.

Nobody had named this dependency: whether "You can draft specifications." becomes a grant depends on the previous reply's prose being in the conversation history. With the responder down (fallback turns instead of the denial), the sentence classified general_conversation — twice, including the "Remember about me:" phrasing that probes at 0.82–0.92 in isolation. In a clean conversation with the denial prose present, it classified correctly on the first try. The structural layer held throughout (nothing was written on any misclassified turn) — the dependency costs reachability, not safety: the grant flow silently degrades to conversation whenever the framing turn is missing, malformed, or fell back.

What scoping should establish: whether the classifier's history window should weight the structural outcome of prior turns (the denial's outcome: denied is in the turn record) rather than only their prose — the prose is model-composed and, post-[B-85], unpinned. Adjacent to [B-82]'s closed half (the honesty of the fallback) but a different property: this is about routing robustness, not reply truthfulness.

(Found 2026-08-10/11; the eye-test transcript with per-turn classified intents is the record.)

B-87. A reply disobeyed a correct template the same session the pin left its seam. DENIED BRANCH DONE — server-composed; the standing question remains open for ruling.

The sighting: the first unpinned responder reply said "Got it — drafting a specification now" against a denied outcome — no card, no job, no delegation. Model disobedience of a correct template, which no template fix prevents.

Why it ranks above "one sighting": [CR-2026-199] established the wording layer as the weakest guarantee and the structure as what holds. [B-85]'s fallback then removed the pin from that exact layer, and the disobedience arrived within the same session. The prediction was confirmed, not hypothesised — and every claim still living in model prose is now less reliable than when CR-2026-199 assessed it.

The ruled remedy, done: the denied branch of request_draft / request_revision is server-composed (_denied_reply), the way the held and committed acks already are — deterministic, naming the missing authority and carrying a grant-suggestion sentence the extractor recognizes. A deliberate second effect: the denial's prose is exactly the framing [B-86] says the grant arc leans on — server-composing it stabilizes the fragile turn. Fence pinned by test: the authorized outcomes still voice through the responder. Observed failing first (3 of 4 tests, pre-fix); determinism pinned (same input, same words). (Engine a7e26bc; push run 31493548786 watched green — the sixth consecutive.)

The four all-claim remedies: DONE (CR-2026-201; the scoping's categories held; each observed failing first; determinism pinned; the fence pinned). The five splits remain, per the Operator's sequencing: their own CR after [B-86] is settled, with the mechanism decision (prefix-composition vs structured side-channel) and commit_project_draft's empty-slot prerequisite named as its own step — and weighed WITH the steering finding: composed replies steer routing to the wired path, a benefit nobody argued for.

The original standing question, now answered by the scoping and the four conversions — kept for the record: (1) request_draft/request_revision's approval_card claim ("I've proposed it") and executed/executed_then_failed reports; (2) approve_draft's three outcomes; (3) save_filter's saved-plus-six-failures; (4) tune_setting's confirmations; (5) add_knowledge's error branches (successes are server-composed); (6) commit_project_draft's success voice over an empty result slot (the CR-2026-199 latent smell, still standing); (7) remember_about_me's two non-writing branches. All are Action-first or status-correct at the instruction layer post-CR-2026-199 — the instruction layer is what the sighting shows can be disobeyed. Which of these earn the microphone-removal treatment is the Operator's ruling to make; the denial was the outlier among acks and is done.

B-96. "Couldn't reach the Companion" renders for servers that were reached — the converse path's two catch sites manufacture unreachability. CLOSED 2026-08-14 — CR-2026-211 (surface 8871997). The sentence is confined to its true branches (thrown fetch; dead upstream = the OBSERVED 500-no-body shape); reached-server failures render the server's usable detail or honest ignorance. Filed on one wrong mechanism (503), re-justified on a second (502), fixed on the third — the observed one. Kept below as filed+corrected for the record.

The mechanism (corrected 2026-08-14 by the B-96 scoping — the original example was wrong): useConversation.ts:448 and ChatView.tsx:535 — one sentence, one shape, both wrapping the SAME route — map every non-401 failure of /operator/converse to "Couldn't reach the Companion. Try again in a moment." The false members are the 500 (an engine bug — server reached and crashed, the red-on-red family, rendered as network weather and retried into) and the 422 (a malformed surface request — the server answered precisely, naming the field; a surface defect hidden as weather). NOT a member: 503 no_credential — the converse route composes a 200 around a missing key; that 503 lives on adjacent routes these catches never see. The message asserts UNREACHABILITY — a specific, checkable claim, false in both member cases.

Why it ranks above [B-95]: the converse path is the main artery — every Companion turn crosses it, against the commit ceremony's once-per-engagement tap. Same class (a catch manufacturing a cause the facts deny), more traffic. Nobody has walked into it yet only because it needs a server error to surface — which is a description of luck, not of safety; the first engine bug or malformed request on this path will be reported as the Operator's network, and retried into.

The fix shape (per the scoping — status-class branching, no codes needed, no prerequisite): no response at all (non-ApiError throw) → "couldn't reach", TRUE; 502/503/504 from the proxy layer (engine down) → same sentence, TRUE in substance; any 4xx/5xx WITH a response → honest ignorance, with the FallbackAffordance detail-extraction when a usable string detail exists. Two sites, ONE shared string-returning helper (converseFailureMessage) — ruled not-the-mechanism; the line it must not cross: a code→message table or a route parameter. Its test drives a 500 body and a 422 body through each site and asserts the unreachability sentence does NOT render — and drives a thrown fetch and asserts it DOES.

B-95. The commit ceremony's catch block manufactures a false cause — the strongest instance of the truthfulness class. CLOSED 2026-08-14, walk VERIFIED — CR-2026-210 v0.2 (surface a1deb86). The Operator's live tap on E0128 rendered the server's sentence; the passkey completed silently. Kept below as filed for the record.

Why it is the strongest instance, not another one: every prior member of the class had the truth go missing somewhere before the words — a model overclaimed, a prompt assumed success, a composed promise was unarmed or outlived its affordance. Here the truth arrived and was destroyed at the last step: the server named the cause truthfully on all four attempts — 409 not_converged, "Seed has 1 open finding(s)", in the response body — and CommitCeremony.tsx's catch block collapsed every non-401 ApiError into "Couldn't commit — try your passkey again." Not a model overclaiming; not a stale promise; one catch block asserting a cause the server denied.

The cost, recorded because it is what makes this worth building: two days on the wrong problem. A full deploy/stele/RP-config/credential-storage audit that answered the question asked, not the one that mattered. The Operator re-registered a passkey they did not need. The Operator was told twice that the diagnosis had changed when it had not — the blocker was the same open finding on all four attempts. All of it downstream of one line of error handling.

The general shape — the rule the fix must follow: a client that collapses distinct server errors into one message does not lose information, it MANUFACTURES a false one. "Something went wrong" is honest about ignorance; "your passkey failed" is a claim — and when the server said otherwise, a false claim. Failure messages are composed claims and take the standing note's full gate: composed against the server's stated cause, or confessing ignorance — never specific and wrong.

The fix shape: CommitCeremony's catch branches on the server's error code. not_converged → say what the server said (the open-finding count and message; point at the path — address the findings or commit divergent with a rationale). Passkey-layer failures (the ceremony throwing client-side, or 401 commit_attestation_failed) → the passkey message, which is then true. Anything else → honest ignorance ("Couldn't commit — [the server's message]" or "something went wrong"), never a specific invented cause. Its test drives the 409 body through the component and asserts the passkey sentence does NOT render. Standing note v0.4 (third revision) carries the principle; this item carries the fix.

B-94. The commit sentence swallows the engagement name mid-clause. OPEN — filed 2026-08-14 per the Operator, from the E0128 walk's batch item: tracked here, not only in CR-209 §2's consequences note.

The mechanism: CommitCeremony renders ` Commit ${label} with your passkey to make it official. ` — the title spliced into the sentence as a bare interpolation. With clause labels (CR-2026-209) the splice reads: "Commit A one-page closing procedure checklist for a community tool… with your passkey to make it official." — the label's own ellipsis and capital collide with the sentence around it. CR-209 fixed what the name IS; this is the sentence that carries it. The fix shape: set the name off from the sentence typographically (quotes, or the label on its own line above a constant sentence — the banner's header already shows the title, so the sentence may not need the name at all); a label is a noun to be mounted, not prose to be inlined.

B-93. Conversationally-created candidates are undiscardable — the discard endpoint 500s on any candidate carrying conversation turns. OPEN — filed 2026-08-14 from CR-2026-209's eye-test cleanup, at the Operator's direction.

The mechanism, and why it is a growing pile: discard_candidate_engagement deletes the engagement row without handling conversation_turns rows that reference it — conversation_turns_engagement_id_fkey blocks the delete and the endpoint 500s. Every conversationally-created candidate is undiscardable through the endpoint, because conversational creation attaches turns at draft. And the creation doors now WORK (CR-2026-206 opened them; CR-2026-207 gave candidacy a durable home) — so each dead-end conversation leaves an unremovable candidate, and more get created. E0128 itself would 500 if discarded today. The document-upload door's seedless candidates remain discardable (no turns yet) — the defect is exactly coextensive with the conversational path.

How it was found — named because the finding path is the lesson: CR-2026-209's own eye-test cleanup hit it. It is not reachable by any path that LOOKS for defects — the suites drive discard on turn-less fixtures, and no defect-hunting walk discards candidates — only by trying to tidy up after one. That is why it survived until a session that cleaned up properly: the e2e cleanup rule is itself a detection instrument, and this entry is its second catch (the first being the rule's own reason to exist). A failure of a kind suppresses detection of its kind (the suppression property): an undiscardable candidate teaches people not to discard, which keeps the defect unobserved.

The fix shape: delete (or detach) the candidate's conversation_turns inside discard_candidate_engagement's transaction, alongside the seed-event deletion it already does — the turns are pre-commit conversation about a thing that is being unmade, the same nothing-to-preserve rationale the endpoint's own docstring states. Its test drives discard on a candidate WITH turns (the fixture the suites never had), plus the fence: an active engagement's turns are never touched by any discard path.

B-91. The message-order ruling survives unapplied on the mobile surface. OPEN — filed 2026-08-13 from CR-2026-208's Step-1 scope finding, at the Operator's direction: this is the ruling surviving unapplied on one surface, not a footnote — nobody is to read CR-2026-208 as having closed the ordering question.

The mechanism: ConversationPane — the component CR-2026-208's conditional placement lives in — never renders on mobile. MobileSurface renders ConversationTranscript directly plus ONE engagement-wide bottom bar (hint line, attach, composer, mic) that serves all five slots: the Companion slot and the four rooms. Under the newest-first default, mobile therefore reads newest-at-top with the composer in a shared bottom bar — the exact internally-contradictory state the ruling named and ruled against, alive on one surface.

The design tension, stated plainly: the bar serves five slots and only one of them has an order. A per-slot bar (top on the Companion slot under newest_first, bottom on rooms) would jump as the Operator swipes between slots — trading one contradiction for a moving control. Candidate shapes for its own CR: the bar moves only on the Companion slot with a deliberate transition; the Companion slot gets its own bar and the rooms keep theirs; or the mobile transcript alone defaults differently (which re-opens the split the ruling closed, named here so it is rejected knowingly, not proposed silently). Deliberately NOT decided inside CR-2026-208.

B-92. The composer jumps once on load for stored-oldest-first accounts. OPEN — filed 2026-08-13 from CR-2026-208's build, at the Operator's direction: a one-time layout jump on page load is small, but it is the kind of thing that gets rediscovered as a mystery later. This entry is the discovery, pre-made.

The mechanism: the pane mounts at ORDER_DEFAULT (newest_first, composer top) while the stored preference is still in flight (fetchMeSetting on mount); when a stored oldest_first resolves, the effective order flips and the input zone REMOUNTS from top to bottom — one visible jump, once per page load, only for accounts holding the non-default preference. The draft survives (controlled textarea); focus does not. Two suite tests held DOM references across that remount and were re-idiomed in CR-2026-208 (voicePlaceholder, micTiming) — the same stale-reference shape a future test will hit if it grabs the input zone before the preference settles. The fix shape, if it ever ranks: defer the pane's first paint until the order preference resolves (one deliberate loading beat instead of one jump), or persist the last-known order client-side as the mount default. Until then: a "composer flashed at the top" report from a stored-oldest-first account is THIS, not a failed deploy.

B-90. Operating branches that compute no discriminator persist null — null that lies. OPEN — filed 2026-08-12 from CR-2026-205's Step-1 completeness check, caught before shipping rather than after.

What makes it more than a gap (the Operator's framing, recorded): operation_outcome's null means "the turn carried no operation." An operating branch that computes no discriminator persists null anyway — indistinguishable from a turn where nothing happened. The same null-vs-empty shape CR-2026-185 hit (absent-vs-empty answer sources), arriving in a new column — but caught at the CR's own Step 1, not by a live defect.

Both families, named: (1) add_knowledge's held branches — the happy path (holds an assertion, offers the save) and its update/confirmation siblings carry payload keys only, no action; (2) commit_assertion's five branches — the reserved human commit act, operation_data={"delegated_response": ...} only, no discriminator on any outcome. The fix shape: one action/outcome key per branch plus the matching frozenset entries — handler semantics, deliberately excluded from CR-2026-205's persistence-only fence; its own small CR. The source-scrape completeness test will force the frozenset entries the moment the keys land.

B-89. The dashboard perf test can re-redden the gate at random. OPEN — filed 2026-08-12 from CR-2026-204's push.

The mechanism: test_phase_39_dashboard_auth_perf::test_performance_10_engagements asserts wall-clock budgets (200ms) for the three dashboard endpoints on the shared CI runner; CR-2026-204's fix-commit run measured recent at 426ms and went red on a diff with no dashboard contact (verified: the route reads memberships/engagements/jobs, not conversation_turns; the suite passed locally in 2s on the same code; the rerun on identical code was green). [B-84]'s reasoning applies verbatim: an intermittently-red gate gets read as noise, and once people learn to rerun rather than read it, it has stopped being a gate. The remedy is not a wider budget reflex — it is making the assertion's property deterministic (count queries, or measure against a calibrated runner baseline) or moving the wall-clock check off the merge gate; B-84's fix shape (wait on the property, not the clock) is the precedent. Until then: a red on this test with a non-dashboard diff is evidence of runner weather, but every such red must still be READ before rerun — the suppression property says the flake will eventually be standing in front of a real regression.

B-88. The classifier-routed grant branches run extraction with no revoke check. OPEN — filed 2026-08-12 from CR-2026-203's completion residual, at the Operator's direction: deferred work is tracked here, not only in comments and completion notes.

The mechanism: CR-2026-203's 4a redirect is revoke-first — a sentence matching both grant and revoke prefixes ("I don't think you can draft specifications yet.") is never redirected as a grant. But when the classifier itself routes to remember_about_me or add_knowledge, those branches call extract_delegation_grant on the raw message directly, with no revoke check of their own — a dual-match negation reaching them classifier-first could still mint a held grant. Pre-existing (CR-2026-200 shape), and smaller since CR-2026-203: the tightened matcher kills the apostrophe forms ("you can't render") structurally, so the residual is the spelled-out negations ("cannot", "don't think you can", "no longer") in sentences the classifier reads as memory-writes. Blast radius bounded as always by held-not-committed and the tentative ack. The fix shape is one guard, shared: the same revoke-first check the redirect uses, applied where the two branches extract — small, and its test is the dual-match battery already in test_cr_2026_203_raw_message_grant_extraction.py.

> A method note the Operator ordered kept: the D-1 correction — the refusing model is the tier-routed Opus 4.7, not the sonnet default the halt report assumed — is the second time this arc that a report's assumed fact differed from the observed one (the first: red-for-two-pushes that was six). Both were caught by watching rather than inferring.


DUNIN7 — Done In Seven LLC — Miami, Florida DUNIN7 — the build list — v0.64 — 2026-08-11