Version. 0.2 · Date. 2026-08-08 · Author. Claude.ai (v0.1) / Claude Code (v0.2 amendment) / Marvin Percival (approving).
Supersedes. v0.1 at record 5711fd3, which stands as a sibling.
Target. /Users/dunin7/loomworks-engine. Engine only.
What changed from v0.1. Step 3 found that the constraint cannot land, for a reason that has nothing to do with fixtures. The fixtures land; the migration does not. v0.1's title said "fixtures, then the constraint"; the ordering discipline survives intact and its conclusion moved.
Migration 0054 backfilled display_number into the projection and never into the event log. The backfill was an UPDATE against shape_events_view and render_events_view; memory_events payloads were untouched — correctly, because the log is append-only.
apply_event_to__events_view reads payload.get("display_number"). For a pre-Phase-36 event that key does not exist. rebuild__events_view deletes the engagement's rows and replays from the log, so a rebuild of a legacy engagement returns display_number NULL.
| playground_dev, 2026-08-08 | payload has no display_number | total |
|---|---|---|
| shape_event | 63 | 64 |
| render_event | 27 | 28 |
Incremental projection is safe. The upsert is COALESCE(existing, incoming), so replaying onto a row that already holds a number preserves it. Only rebuild-from-scratch loses it — the operational-recovery path, reached for when a projection is already damaged.
> Operator, 2026-08-08: "This is not a NOT NULL problem. Migration 0054 wrote numbers into the projection and never into the event log, so the view holds facts the log cannot reproduce. Rebuild-from-scratch is ALREADY broken for legacy engagements, today, constraint or no constraint. The constraint doesn't cause it — it exposes it."
Record it that way everywhere. "The constraint breaks rebuild" leads to the wrong remedy — drop the constraint, call it fixed — and leaves a lossy recovery path in place.
| | | |
|---|---|---|
| (a) Projector assigns a number when the payload lacks one | RULED OUT | Makes the projector a writer of facts rather than a reader; two rebuilds could disagree with each other; inverts the log-is-truth / view-is-derived relationship the substrate rests on. |
| (b) Accept that legacy engagements cannot be rebuilt | INTERIM — written down, not tacitly accepted | standing-notes/loomworks-standing-note-legacy-engagements-cannot-be-rebuilt-from-the-log-v0_1, plus the limitation in both rebuild docstrings, where someone reaching for the recovery path will meet it. |
| (c) Compensating events carrying 0054's numbers | CORRECT — its own CR | Filed as B-70. Append-only, invents nothing, makes rebuild correct rather than making it safe to avoid. |
| (d) Never add the constraint | not chosen | The constraint is deferred behind B-70, not abandoned. |
Lands: the fixture work (§2 of v0.1, Step 2), the two rebuild docstring limitations, the standing note, B-70.
Does not land: the migration, and the constraint-holds test that would prove it. The thing that would have failed goes last — the same discipline that produced v0.1's ordering, applied one step further out than v0.1 could see.
B-54 closes at its code-level guarantee. Every production path assigns a number (CR-2026-176); every fixture now does too. The database half is deferred pending B-70, not judged unnecessary.
Step 1 applied the constraint, ran the suite, and asked: is any failure a production path rather than a fixture? 226 failed, all the same violation; every production construction site was read directly; the answer was no, all fixtures. That answer was correct.
Several failing tests exercised rebuild — production code — but failed for a fixture reason. They were classified as fixtures and their fixtures were fixed, which made them green and removed the only signal that the production path underneath them was also broken. What survived was the one test whose fixture was not a defect: a faithful pre-Phase-36 payload, with nothing in it to fix.
> A check that was sound and incomplete. It could fail, did fail, failed loudly, and named the invariant. Fixing the defect it did cover masked the defect it did not.
Recorded as a third mode in standing-notes/loomworks-standing-note-observe-the-failure-before-trusting-the-check-v0_3.
v0.1 said to revert the local constraint before proceeding. It was left applied to playground_test while the fixtures were fixed, because it is the only feedback signal for 226 sites, and reverted before the final verification run.
The instruction's purpose held: nobody could mistake an uncommitted ALTER TABLE for the migration. The change existed only in playground_test, never in the repository, and playground_dev was never touched. Approved by the Operator on report.
v0.1 §1 flagged that CR-2026-171's 230 and Wave 0.5's ~73/42 count different things. The re-measurement came back 226 / 46 files — close enough to 230 to be worth explaining rather than accepting.
It is not coincidence, and not evidence that CR-2026-176 did nothing. The fixture debt and the production bug were separate populations. CR-2026-176's three paths are exercised by tests that go through the fixed code and now pass a number; the fixtures construct their objects directly and never touched those paths. The two counts were never going to move together.
display_number is NOT NULL~~ — deferred to B-70.DUNIN7 — Done In Seven LLC — Miami, Florida CR-2026-190 — B-54 remaining half — v0.2 — 2026-08-08