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

DUNIN7 — the build list — v0.50

Version. 0.50 Date. 2026-08-09 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.50 by Claude Code; the rest by Claude.ai.

Changes from v0.49. [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.

The largest thing available: B-8 slice two — teaching the Companion to do things in the other three rooms rather than only describe them. Q-6's answer unblocked it. It has a scoping note and needs a change request.

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 READY, unblocked by Q-6.

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. One test governs it: can you know what you are approving from the sentence you typed? Deriving a summary passes. Asking for a draft passes in kind but not in cost — you would not know the price. Confirming a Shape fails outright, which is why its control stays on a screen that shows the outputs first (Q-7, answered: defer).

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.)

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 (a contributor who speaks is credited as the Operator). (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 (the organized view cannot be pointed at) · 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. READY to scope. What is unread decides the priority: whether any of the four holds something a user would notice losing — a scroll position, a selection, where the cursor is.

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. READY — small, and it has been known and untracked. The substrate knows the difference between the Operator and a contributor. The screen does not — every turn is labelled as though the engagement's Operator said it, so a contributor's words appear over the Operator's name.

This is attribution being wrong, not missing, in a product whose whole posture is that claims should be checkable. It has been sitting in the architecture specification as a known gap — "role-aware author labels need wiring"without ever reaching this list, which is the same failure B-60 was added for. (Architecture specification v0.6, §613.)

B-67. The organized view is shown to the model and cannot be pointed at. READY to scope — small, and narrower than B-61 was. 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. READY — small, and fixable in one place rather than at each screen. The credit-reconciliation path creates objects an Operator can see, and gives them names written for code rather than for a reader.

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?


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