loomworks-record/scoping-notes/8d79e91; Operator Layer half at record 35efc60. Both complete.B-5 builds the Manifestation room — the surface where an engagement's Memory appears organized at a moment in time. It is item five on the main line and the front of the queue.
Change request A's Step 0 had three items. Item one, the engine contract, ran on 2026-07-31. Items two and three, the Operator Layer reads, ran on 2026-08-01. Both are complete and both are anchored. Nothing in this note is reconstructed from session memory; every fact carries a file:line anchor in one of the two findings documents.
This note settles the build shape and surfaces six decisions. It does not draft the change request.
Prior position — build list v0.10, B-5. "READY — front of the queue, and smaller than we thought. The read found the engine already returns what the screen needs. The staleness marker is a wire, not a build."
That is true of the engine and false of the Operator Layer. Both halves are now read, and they point in opposite directions.
The engine is ready. All four Manifestation routes exist. Walk-audit W-16 was wrong when filed — the version pin exists as a query parameter on route 4, and was already there at the walk-audit engine. GET /engagements/{id}/memory-status returns both halves of the state contract in a single call, so staleness needs no computation built.
The Operator Layer has nothing. No ManifestationRoom.tsx. No src/lib/api/manifestations.ts. Zero occurrences of manifestations anywhere in src/ or tests/. Zero occurrences of memory-status in either tree. The wire exists and nothing is plugged into it.
The corrected framing. B-5 is small because its parts are individually simple and no migration is implicated — not because most of it is already built. It is greenfield client work against a ready engine: two new adapters, one new room component, one new fetch-state representation, one changed line in the dispatcher, one new test file. The build list entry should be amended when it next moves.
| Piece | State | Anchor |
|---|---|---|
| Four Manifestation engine routes | Exists | api/routers/manifestations.py:321, :396, :441, :490 |
| Version pin on route 4 | Exists — query parameter, not path segment | manifestations.py:502; loader engagement/manifestation.py:688-697 |
memory-status engine route |
Exists, returns both state-contract halves | api/routers/memory_status.py:45; schema :6899 and :6908 |
| Room key, label and discriminator | Exists — no change needed | src/lib/room-labels.ts:26 |
| Room dispatcher | Exists — one line changes | RoomView.tsx:56-62 |
| Fetch wrapper and adapter pattern | Exists — 26 hand-written adapters over one 113-line wrapper | src/lib/api.ts; src/lib/api/memory.ts as canonical |
| List fetch-state hook | Exists, three states, list-shaped | src/hooks/usePagedList.ts:38-50, :62 |
| Room-test pattern | Exists, six steps, directly copyable | RenderingRoom.test.tsx:1-60 |
ManifestationRoom.tsx |
New | — |
src/lib/api/manifestations.ts |
New | — |
src/lib/api/memory-status.ts (or folded into the above) |
New | — |
| Single-object fetch state | New — nothing models it | see D4 |
ManifestationRoom.test.tsx |
New | — |
The structural model is the Rendering room, not the Memory room. The Operator Layer findings overturn the brief's own framing here: two rooms have real implementations, not one. RenderingRoom.tsx is 126 lines of read-only list with honest empty state; MemoryRoom.tsx is 645 lines dominated by five mutating Operator actions the Manifestation room does not need. B-5 copies the Rendering room.
Six pieces, in dependency order.
src/lib/api/manifestations.ts — wire-boundary comment, Operator-vocabulary types, Wire* snake_case interfaces, projection functions, fetch functions. Surfaces the version pin deliberately from the first line rather than retrofitting it. Must be added to WIRE_BOUNDARY_FILES at tests/components/vocabulary-wall.test.ts:43-80, or the wall fails the moment a route-path string lands.ManifestationRoom.tsx — reads a Manifestation and, if D1 lands that way, a memory-status. Renders per D3.RoomView.tsx:56-62 — replace the compose case's EmptyRoom with the component. One line of substance.ManifestationRoom.test.tsx — six-step pattern, mocking @/lib/api/manifestations, manifestation-* testids, empty-state coverage modelled on MemoryRoom.test.tsx:511.D1 — Which staleness measure the room displays.
The engine carries two and they can disagree. memory_has_changed is driven by whether any Memory-content category is non-zero, explicitly not by version comparison (engagement/memory_status.py:135-136). The raw version delta counts every event including non-content ones. A Manifestation can be twenty-five versions behind and correctly flagged current.
Claude's lean: show memory_has_changed as the marker and changes_since_derivation as the detail. Do not display the raw version delta at all. The delta counts things that are not Memory content, so it is not an Operator-meaningful number, and showing it is what creates the apparent contradiction the build list warns about. This resolves the problem by not introducing it.
D2 — Per-object Manifestation history.
Assertions have a /history route; Manifestations do not. Building one is small. But the client has no caller for the assertion-side history route either, so there is no existing history-display pattern in the surface to extend — this would be new on both sides.
Claude's lean: out of B-5. Route 3 already lists an engagement's Manifestations filtered by state, newest first, which gives "what came before" at the engagement level. Per-object history is a different axis and no evidence yet shows the room needs it. Deferring costs nothing and it can be added later without rework.
D3 — What the room renders: fan-out, inlining, or organization-only.
organized_groups carries assertion references — identifier and version — not text. Three options:
Claude's lean: C. The room's job per the seed is Memory organized at a moment in time. The organization is the product; assertion text is a drill-down. C ships now, needs no engine work, introduces no unprecedented fetch pattern, and does not block A or B later. If the room proves thin in use, B is the better follow-on than A — it keeps the fetch pattern uniform.
D4 — The fourth state.
usePagedList models three states and is list-shaped. B-5's central value is a single object with a nullable field: organized_groups is null on pre-Phase-19 Manifestations, where only counts survive. Nothing in the Operator Layer models fetched successfully, and the value is legitimately null for a structural reason, and there is no single-object fetch hook at all.
Claude's lean: a new single-object fetch state rather than a fourth flag layered onto usePagedList. Four states — loading, error, organized, unorganized. The last renders an honest explanation that this Manifestation predates organized grouping, with the counts that do exist. It is not an empty state and should not look like one.
D5 — The vocabulary wall's case-sensitivity.
The wall test matches case-sensitively (vocabulary-wall.test.ts:128) against lowercase terms (:29-30). The capitalized forms Manifestation and Shaping — the operator-facing forms — pass in every file. The carve-out declares itself a tight distinction, not a blanket exception; as implemented it binds only the lowercase spellings. Live instances already sit outside the sanctioned file: strings.ts:372, :588, SpendPausePreferenceSection.tsx:12-13.
Claude's lean: split it out as its own small build item and run it before B-5's room lands. B-5 is about to add a room dense with exactly those two words, and shipping it under a guard known to be blind is the wrong moment. Keeping it separate also protects B-5's scope: normalising the comparison will surface the three known instances, and each needs a judgment — legitimate operator-facing label, or leak. That triage is a vocabulary question, not a Manifestation-room question.
Rejected alternative, recorded: fixing it inside B-5 by adding the known instances to the allowlist. That is patching a test green when its premise is false, which the project prohibits.
D6 — Room linkability.
Rooms are useState, not routes (InEngagementSurface.tsx:66). A room is not linkable, not bookmarkable, and not restored on reload; the Operator always lands in Memory.
Claude's lean: out of B-5. Making rooms linkable changes the shared surface for all four rooms and is not a Manifestation-room-local change. Worth a build-list item of its own if a staleness notice elsewhere should ever deep-link into the room.
The shared-gate choice. The Memory room's two lists share one gate: either list's loading or error state governs the whole room (MemoryRoom.tsx:555, :558). If B-5 reads both a Manifestation and a memory-status, the same either/or presents itself. The Manifestation is the room's substance and staleness is an annotation on it, so they should not share a gate — a failed staleness read should not blank the room.
Load-more error collapse. usePagedList sets the same error flag for a first-page failure and a load-more failure (:136), so a failed second page replaces an already-rendered list with an error and the loaded items vanish. B-5 inherits this only if it uses the hook. Under D3 lean C it likely does not, and should not reproduce the behaviour.
Only show what is available. The established idiom is conditional rendering to null with a comment naming the principle — nine sites, zero greyed-out controls in any room component. A Manifestation room with no derivation yet shows an honest empty state, not a disabled Derive button.
W-16 should be struck from the walk-audit record, not scheduled. It was wrong when filed. The break list loses an entry. This is a record correction, not build work, and does not belong inside B-5.
The two disabled attributes at MemoryRoom.tsx:224/:241 are an open seed-interpretation question with the code's counter-reasoning recorded at :126-129. Not B-5's to fix either way; noted so it is not lost.
organized_groups on pre-Phase-19 Manifestations. Not proposed by either read.One open dependency. Change request A was scoped in the completion arc as covering more than B-5. Whether A is now B-5 alone, or B-5 plus B-6 and B-7, is not settled by either findings document and should be confirmed against the completion scoping note before drafting opens.
"Smaller than we thought." Build list v0.10's B-5 entry, written from the engine half alone. True of the engine, false of the Operator Layer, where the room is greenfield. The entry stands; the framing is corrected here and should be amended when the list next moves.
"The one room believed to work." Asserted in the Operator Layer brief §4 V1 as the reason to detail the Memory room. Two rooms have real implementations. The Rendering room at 126 lines is the closer structural model for a read-only room than the Memory room at 645. Corrected by the read.
W-16 — "there is no at-version route for manifestations." Walk-audit report §5.3. Overturned by the engine half: the capability exists as a query parameter and was present at the walk-audit engine. What W-16 got right is its route enumeration; what failed is its method — it read an OpenAPI document for a path segment and concluded absence.
The vocabulary wall carve-out as described. vocabulary-wall.test.ts:82-88 claims a tight restriction. The implementation enforces it only for lowercase spellings. The comment describes an intent the code does not deliver.
Produced 2026-08-01 in the Claude.ai Loomworks project, from the two Step 0 findings documents and build list v0.10. No repository was inspected from this session; every anchor is quoted from a findings document, not read directly.
The trajectory worth preserving: the engine half arrived first and produced an optimistic framing that the build list absorbed. The Operator Layer half, run a day later, showed the optimism was half-correct and localised. Neither read was wrong; the framing error came from generalising one half to the whole before the other half ran. Worth naming as a pattern — a partial Step 0 absorbed into the queue reads as a complete one unless the gap is stated in the entry itself.