DUNIN7 · LOOMWORKS · RECORD
record.dunin7.com
Status Current
Path change-requests/cr-2026-192-loomworks-b54-display-number-not-null-v0_1.md

DUNIN7-M4 — INFRASTRUCTURE CHANGE REQUEST

CR-2026-192 — B-54's database half: the constraint, finally (v0.1)

Version. 0.1 · Date. 2026-08-09 · Author. Claude Code (drafting) / Marvin Percival (approving). Target. /Users/dunin7/loomworks-engine, branch main. Engine only. Baseline. Engine c895863, tag cr-2026-191-b70-log-reconstruction. CC confirms at Step 0. Companion to. CR-2026-171 §2.2 (first deferral); CR-2026-176 (the code half); CR-2026-190 v0.2 (second deferral, and the finding); CR-2026-191 (B-70, the third blocker, now closed). Charter. Autonomous regime. Status. Ready for execution, with one conditional halt.


1. Summary

display_number should be NOT NULL on shape_events_view and render_events_view. Every blocker is now closed.

| Deferral | Blocker | Closed by | |---|---|---| | CR-2026-171 §2.2 | live paths created unnumbered records | CR-2026-176 | | CR-2026-190 | ~226 test fixtures created unnumbered records | CR-2026-190 | | CR-2026-190 v0.2 | rebuilding from the log produced unnumbered rows | CR-2026-191 |

Each deferral was recorded with what it waited on. That is why this item reached the end of the queue rather than becoming something nobody required.

2. What this CR is, in one line

One migration and one test. The scope is deliberately small; the work that made it small was done in the three change requests above.

3. Step 1 — verify the ground before adding the constraint, and halt if it has moved

Do not trust this document's premise. CR-2026-190's Step 1 was sound and incomplete, and that is a live lesson rather than a historical one. Establish and report:

  1. Zero NULL display_number rows in both views, in playground_dev. A count query.
  2. Zero shape/render payloads missing display_number in memory_events and current_memory_objects.
  3. A rebuild is faithful — run rebuild_*_events_view against an engagement in a scratch copy or the test database and confirm the numbers survive. CR-2026-191's tests assert this; run it against real data rather than only fixtures.
  4. Apply the constraint locally and run the full suite, as CR-2026-190 did, and report what fails.

HALT and report if anything fails that is not a fixture — and this time, be specific about the failure mode that got past the same check before: a test that fails for a fixture reason may be sitting in front of a production path that would also fail. Before classifying a failure as a fixture, ask what production code the test exercises, not only what its fixture constructs.

> The check to run is not "is this failure a fixture?" but "what would still be broken if I fixed this fixture?"

4. Step 2 — the migration

ALTER COLUMN display_number SET NOT NULL on both views. Downgrade drops it.

Ordering, unchanged from CR-2026-190 v0.1 and still the point: the constraint goes last. Nothing else in this CR precedes it because nothing else is needed.

5. Step 3 — prove the constraint holds

A test asserting an insert with a null display_number fails at the database level. Observe it failing for the right reason before trusting it, and make the message name the invariant rather than report a rejected insert.

Note the shape of this test: it is not a defect pin (observe-the-failure v0.4's fourth pattern). Its green means the constraint is present and working. Say so, so that nobody later reads it as the pinned-defect kind and inverts it.

6. What the constraint buys, stated honestly

Not much today, and that is fine. Every production path assigns a number, every fixture assigns one, and the log carries them. The constraint's value is that it makes the invariant enforced rather than maintained — a new call site that forgets fails immediately and locally, instead of writing a row that only misbehaves on a screen much later.

It also closes the loop opened by B-53 and B-59, where a missing number was indistinguishable from a fetch failure. With the column non-nullable, that ambiguity cannot recur at the storage layer at all.

7. Out of scope

8. Seed-mutability impact

B-69's instruction applies. Expected Kind C — a nullability constraint on a sequence number has no plausible seed contact, and CR-2026-190 §7 already established that numbering never reads the seed. Record it as a null finding with what was checked, per the standing instruction: an entry either way.

9. Build steps

| Step | What | Mode | |---|---|---| | 0 | Pre-flight, engine. | Auto | | 1 | Verify per §3. Halt if anything fails that is not a fixture. | Auto, conditional halt | | 2 | Migration, with downgrade. | Auto | | 3 | Constraint-holds test, observed failing first. | Auto | | 4 | Apply to playground_dev. | Auto | | 5 | Full suite; ruff 0; mypy at/below baseline. | Auto | | A | Checkpoint. | Checkpoint | | 6 | Tag cr-2026-192-b54-not-null. Push. | Auto |

10. Acceptance gate

  1. display_number is NOT NULL on both views, and a null insert fails at the database level — proven by observation.
  2. The migration has a working downgrade.
  3. playground_dev is at the new head, with the constraint applied and no data loss.
  4. Full suite green; both gates clean.
  5. Step 1's four verifications recorded with their numbers.
  6. Seed-mutability recorded — expected Kind C, and recorded as an entry either way.
  7. B-54 closes entirely, and the build list says so in both the item and the grouped line.

DUNIN7 — Done In Seven LLC — Miami, Florida CR-2026-192 — B-54's database half — v0.1 — 2026-08-09