Version. 0.1
Date. 2026-07-31
Author. Claude Code, execution session on DUNIN7-M4.
Charter. standing-notes/dunin7-standing-authorization-charter-v0_1.
Executed from. change-requests/cr-2026-159-provenance-seam-v0_4.md — confirmed the highest version present (v0.1 through v0.4; the kickoff named v0.4, so no kickoff/record mismatch under standing-notes/loomworks-standing-note-executable-document-versions-v0_3).
Status. HALTED AT CHECKPOINT A. Steps 0, 1 and 2 are committed on branch cr-2026-159-provenance-seam. Step 3 not written. Step 4 not written. Nothing merged, nothing tagged, nothing pushed. main stands unmoved at a317051.
Why this halts. One of the four conditions the CR names at Checkpoint A fired: a pre-existing test needs editing. The CR's kickoff names this as an anomaly, not an adjustment, and charter A-2 / C-3 forbid absorbing the correction in flight.
tests/test_consideration_triggers.py:95 — test_open_contributor_initiated — passes at baseline a317051 and fails with the Step 1 seam change. Causation was established by restoring the baseline file and re-running, not inferred.
The assertion is:
fetched = await get_consideration(
engagement_id=eid, consideration_id=episode.id, db=db
)
assert fetched == episode
episode is the object open_consideration returns to its caller. fetched is the same object read back from the database. Before this change the two agreed. After it they cannot: the stored row carries the real event_id and the returned object still carries the caller's fabricated UUID.
The CR's §1 states the risk case as:
> Nothing reads the field. No query filters on it, no projector derives from it, no response schema exposes it, no test asserts its value, and nothing hashes it. The surface is write-side only, so no downstream consumer can break.
The last clause does not hold. Option B corrects the object inside append_event, on a local rebinding. MemoryObject is frozen, so the corrected object is a copy — the caller's object is not and cannot be updated. Every caller that builds an object, passes it to append_event, and then returns that same object to its own caller now returns an object whose provenance disagrees with the record just written.
Sized: 93 append_event call sites in src/, of which roughly 35 return a locally-built memory object within a few lines of the append (return new_version ×7, return new_comp ×5, return record ×3, return engagement ×3, return amended ×3, and ~14 more).
This is invisible today for the reason §1 gives — nothing reads the field. It is not invisible in principle: the object handed back to a caller is no longer a faithful representation of the row that was written. Exactly one test in the tree happens to detect it, and it detects it by whole-object equality rather than by asserting the field, which is why the CR's sweep for value assertions correctly returned zero and still missed this.
The live verification below demonstrates the divergence directly.
D-1, the option A / option B choice. The CR closes it on the reasoning that both options are correct and neither can break a consumer. The second half of that is now in question for option B specifically. Option A — stamping at the 83 call sites — would leave the caller's object and the stored row in agreement, at the cost the CR correctly describes (83 sites, and a line-level collision with B-25 at the three C-1 sites).
This is a drafting-session decision, not mine. Charter C-3: the correction is a CR version bump. Three shapes are visible from here, offered as material for that bump rather than as a recommendation:
append_event hand the corrected object back and have callers return that. Keeps object and row in agreement; touches the ~35 returning call sites, not all 93.
| Check | Expected per §Baseline | Found |
|---|---|---|
| Engine HEAD | a317051 | a317051 ✓ |
| git describe | registry-completeness-v0_1-1-ga317051 | identical ✓ |
| Working tree | M uv.lock only | M uv.lock only ✓ |
| Alembic head | 0102, single, 102 revisions | 0102 (head), 102 files ✓ |
| CR number free | CR-2026-159 | free in both change-requests/ and docs/phase-crs/ ✓ |
All six §6.4 anchors confirmed unmoved — re-read directly, not inherited:
| Module | Builds the dict | Embeds in payload | Serialises for column |
|---|---|---|---|
| wasderivedfrom_backfill.py | :78 ✓ | :97 ✓ | :142 ✓ |
| redirect_edge_backfill.py | :142 ✓ | :164 ✓ | :206 ✓ |
base.py:147 (the declaration), base.py:144 and base.py:161 (the two frozen configs) also confirmed at the lines the CR cites.
The CR asks Step 0 to "confirm 83 sites / 37 files."
wasGeneratedBy=uuid.uuid4() plus 2 × wasGeneratedBy=uuid4() (engagement/manifestation.py:531 and :551, which import the bare name).
The enumeration is complete and closed: 103 total wasGeneratedBy occurrences in src/ = 84 with a uuid4 call (the 83, plus redirect_edge_backfill.py:148, which is a bypass repair rather than a stamping site) + 19 without. There is no multiline or alternate-spacing form hiding a 37th file.
This is reported, not treated as the halt. The tree has not moved; the discrepancy is in a descriptive count of code the CR puts explicitly out of scope (§2, §11), and no step reads it. It is a fifth instance of the pattern the CR's own v0.4 changes-line names — state the anchor, not a count of anchors.
Live verification on a throwaway database (cr159_throwaway, cloned from playground_test as a template, dropped after — see §4). One event appended through append_event with a deliberately wrong caller-supplied value:
row event_id : 956c279c-0243-4250-b997-68ed5cec5295
provenance column wasGeneratedBy : 956c279c-0243-4250-b997-68ed5cec5295
payload.provenance wasGeneratedBy: 956c279c-0243-4250-b997-68ed5cec5295
caller sentinel : deadbeef-dead-beef-dead-beefdeadbeef
1. provenance column == event_id : True
2. payload nested == event_id : True
3. sentinel in neither column : True
Acceptance-gate items 2 and 3 hold. Both columns carry the true identifier; the caller's value reaches neither.
And the same run demonstrates §1.1 directly:
4. the object the CALLER still holds, after append_event returned:
obj.provenance.wasGeneratedBy : deadbeef-dead-beef-dead-beefdeadbeef
matches what was stored? : False
| Run | Result | |---|---| | After Step 1 | 2 failed, 3438 passed, 68 skipped (475s) | | After Step 2 | 2 failed, 3438 passed, 68 skipped (482s) |
Step 2 introduces no new failure. The two failures are:
test_consideration_triggers.py::test_open_contributor_initiated — caused by Step 1. The finding above. Not edited.test_stele_router_mount.py::test_stele_router_mounts_and_begin_resolves_end_to_end — pre-existing at a317051, verified by running it against the restored baseline file. Fails with 401 {"detail":"Authentication required (no valid session)."} where 200 is expected. Unrelated to provenance and untouched by this CR.
Failure 2 is a second queue entry. Memory records the engine baseline as zero failures, no known-failures allowance. That baseline no longer holds at a317051, and this CR did not cause it. It is out of scope here and is filed rather than fixed.
Branch cr-2026-159-provenance-seam, three commits, unmerged:
| Commit | Step |
|---|---|
| e2661bc | Step 0 — pre-flight + CR archival to docs/phase-crs/ |
| 9fbe04a | Step 1 — the seam + the corrected events.py module docstring |
| a794754 | Step 2 — the two backfill repairs |
Step 1 detail. The §6.1 [CC determines] point was taken as rebind the local name object — minimal diff, downstream references stay correct. The comment block the CR specifies is carried verbatim, including the model_copy(update=...)-skips-validation note and the frozen-models/non-erasure note from D-2.
Step 2 detail. One line each at the dict construction, as §6.4's note determines — wasderivedfrom_backfill.py:75 now assigns was_generated_by = event_id; redirect_edge_backfill.py:148 now reads "wasGeneratedBy": event_id. Both carry a short comment citing base.py:147. Because both modules embed the same dict object in the payload and serialise the same object for the column, the one line corrects both destinations in both modules, exactly as the CR's table predicts.
Step 2 survives any resolution of the finding. The backfills are raw-SQL paths that return no object, so they are untouched by the option A / option B question.
Not done: Step 3 (the five regression tests) and Step 4 (implementation notes). Step 3 is deliberately not written — tests 1, 2 and 5 assert the shape of a seam whose design is now the open question, and writing them would bake in the answer.
playground_dev untouched. No connection opened to it at any point.playground_test, its own established database.cr159_throwaway, created by createdb -T playground_test and dropped after. psql -l confirms only playground_dev, playground_test and playground_walkaudit remain..env change, no live process touched, no deployment, no tunnel, no DNS./Users/dunin7/loomworks-engine and this record file.0102; no migration written.
One process note, reported rather than smoothed. The Step 0 commit was first made on main before the session moved it to a branch; main was reset to a317051 and the commit re-homed, with nothing pushed at any point. The git reset --hard in that move discarded the pre-existing unstaged M uv.lock churn. It was regenerated by the next uv run, so the tree now carries M uv.lock exactly as the baseline describes and nothing was lost — but the discard was careless and is recorded rather than passed over.
Q1 — CR-2026-159 D-1, reopened by evidence. Option B leaves ~35 call sites returning an object whose provenance disagrees with the row just written, and one existing test detects it. Which of the three shapes in §1.2 does the CR bump take? Default if unanswered: none — the branch stays unmerged and B-29 stays open. Blocks: B-29's close; and by C-1/C-2/C-3, B-25, FORAY integration and the completion arc's engine rider all stay behind it. Proceeds regardless: everything outside the engine.
Q2 — the engine test baseline is no longer zero. test_stele_router_mount.py::test_stele_router_mounts_and_begin_resolves_end_to_end fails at a317051 with a 401. Not caused by this CR. Own item, or a rider on the next engine CR?
Default if unanswered: stays red and stays recorded.
Blocks: the zero-failure gate on every subsequent engine CR, which cannot be asserted while it stands.
Q3 — the site-count correction. §Step 0 and §1 say "37 files"; the sweep returns 36 across the same exactly-83 sites. Carry the correction into the CR bump, or file a correction note beside it? Default if unanswered: carried in the bump, since a bump is now required anyway. Blocks: nothing.
The branch is cr-2026-159-provenance-seam at a794754, off a317051. Steps 0–2 are done and Step 2 needs no revisiting. The seam is demonstrated working against both JSONB columns. What is open is one design question, and the resuming session should read the CR bump that answers it rather than this report's §1.2, which is material for that bump and not a decision.
DUNIN7 — Done In Seven LLC — Miami, Florida CR-2026-159 — Checkpoint A halt report — v0.1 — 2026-07-31 The seam works. What it does to the object the caller keeps is the open question.