Version. 0.1
Date. 2026-08-07
Author. Claude Code (Wave 0 session). Operator: Marvin Percival.
Charter. standing-notes/dunin7-standing-authorization-charter-v0_1.
Scope. Wave 0 investigation 0.5 of standing-notes/loomworks-development-schedule-v0_1. Read-only. No code changed, no test suite run, no database touched.
Read against. Engine loomworks-engine at afdeff5, clean tree.
Both — and the code half is the part that matters. The ~230 failures are overwhelmingly test-fixture debt, but the investigation also found three production (src/) call sites that create shapes or renders without ever assigning a display number. The build-list premise that only a rule is missing — that "the only rule prevents two from sharing one; nothing requires one at all" — is true, but incomplete: code that would violate the rule already exists and runs today. The eventual fix cannot be a fixture sweep alone, and the NOT NULL migration must not land before the code fix.
B-54 concerns two real INTEGER columns added by migrations/versions/0054_phase_36_display_numbers_and_lineage.py:
shape_events_view.display_number (0054:53), render_events_view.display_number (0054:59) — both NULL-ableuq_shape_events_view_display_number (0054:140) and uq_render_events_view_display_number (0054:152), each scoped (engagement_id, declared_*_type_object_id, display_number), NULLS NOT DISTINCT, and — this is the hole — WHERE display_number IS NOT NULL
So NULL rows are exempt from the only constraint that exists. A backfill in the same migration (0054:86–134, ROW_NUMBER() OVER (PARTITION BY …)) numbered every pre-existing row before the indexes were created, which is why CR-2026-171's production-safety count came back zero.
> Do not conflate this with the assertion display_number, which is a JSONB payload key backed by engagements.next_display_number (migration 0041, backfill 0094). Different mechanism, not B-54.
The assignment helpers are assign_shape_display_number (src/loomworks/engagement/display_numbers.py:44) and assign_render_display_number (:141). Every construction of a ShapeEvent/RenderEvent in src/ was enumerated and checked for whether a number is supplied.
Independently verified: grep -c "display_number" returns 0 for both src/loomworks/credit/proposal_applier.py and src/loomworks/agents/composition_specialists.py, against 4 and 16 occurrences in the known-good agents/shaping.py and agents/render_specialist.py.
credit/proposal_applier.py (live)
apply_reconciliation_correction builds a ShapeEvent (:357, appended as shape_produced at :388) and a RenderEvent (:438, appended as render_produced at :463). Neither supplies a display number.
This is wired live, not dead code — verified end to end: credit/dispatchers.py:25 imports it, and api/app.py:111 calls register_reconciliation_dispatchers() at FastAPI lifespan. Every Operator-approved reconciliation correction writes a NULL-numbered shape and render into the Accounting engagement today.
agents/composition_specialists.py:109
_append_composition_step_render builds and appends a render_produced RenderEvent with no display number, and is called from five sites (:503, :567, :614, :657, :700). Its own docstring says it mirrors RenderSpecialist._append_render_produced — and in mirroring it, dropped the Phase 36 assignment.
The orchestrator confirms the asymmetry: assign_render_display_number appears exactly once in engagement/composition_orchestrator.py (:1008, in the revision re-dispatch path). The produce-step branch assigns; the review and simple branches (_dispatch_review_step:488, _dispatch_simple_step:531) route to the non-assigning helper. Every non-produce composition step — review, translate, transform, validate — produces a NULL-numbered render.
One honest caveat, recorded not smoothed. Today only Stub specialist classes implement those categories; the four base classes are contracts. One could argue Gap 3 is "stub-only." The counter, which this document adopts: the helper is production-module code and is the reference implementation any real specialist will copy, and the failure is in the orchestrator's own dispatch branches. It is precisely the "a future path could skip it and nothing would object" scenario the build-list item describes — except it is not future.
The nullable regime was deliberate, and its rationale names tests explicitly. agents/render_specialist.py:253–261 documents the None default as intentional so that direct callers bypassing the engine path "don't all collide on the same value and trip the partial unique constraint." That is the design rationale for the whole hole, and it is why the fixtures look the way they do.
Not one shared helper. Independently verified counts: 81 ShapeEvent(/RenderEvent( constructions across 42 test files; roughly 73 of them supply no display number. Causes, in descending weight:
_fresh_shape_event / _append_confirmed_shape at module scope. One edit per file.tests/test_render_content_kinds.py:95 _base_render_event_kwargs, feeding 23 sites in that file. A single-line fix.tests/helpers/phase12.py:505 and :575, used 25 times from 4 files. A two-line fix.
A sequencing trap worth naming before anyone sweeps. ~19 files build multiple shapes/renders in the same engagement and declared-type scope. Because the unique index is NULLS NOT DISTINCT, stamping display_number=1 everywhere will trip the constraint on those files. The sweep is mechanical but not blind — it needs per-scope counters.
tests/test_cr_2026_171_shape_title_derivation.py:88–92 carries the comment:
> display_number is NOT NULL as of migration 0104 (B-54, CR-2026-171)
Migration 0104 does not exist — migrations/versions/ ends at 0103_contribution_credentials.py (verified). The migration was reverted when the 230 fixtures broke; the comment asserting it was not. Functionally harmless (the supplied value is valid), but it is a false claim sitting in the codebase that will mislead the next reader. The follow-on CR should remove or rewrite it deliberately.
The schedule gates CR-2026-191 on this finding. Scope it as two workstreams, not one sweep, and order them:
(b) code fix — must land first. Three call sites: credit/proposal_applier.py:357 and :438, and agents/composition_specialists.py:109 (which needs the value plumbed from the orchestrator's review and simple dispatch branches through the specialist method signatures). This is a real design touch, not a mechanical edit — non-produce composition steps have no obvious declared_render_type_ref scope story, and choosing one is a decision the CR must make explicitly rather than infer.
(a) fixture sweep — mechanical, ~35 targeted edits, of which three edits (two helpers plus one factory) cover roughly 48 of the ~73 sites. Needs per-scope counters per §3.
Then, and only then, the NOT NULL migration. If it lands before (b), it will break Operator-approved reconciliation corrections and every non-produce composition step in production at runtime, not merely in tests.
One uncertainty carried forward honestly. The enumeration was a paren-balanced scan for literal ShapeEvent(/RenderEvent( constructions. A row created via model_validate from a raw dict would evade it. None was found, but their absence was not exhaustively proven — worth one confirming pass at CR Step 0.
DUNIN7 — Done In Seven LLC — Miami, Florida Loomworks — B-54 residue findings — v0.1 — 2026-08-07