Version. v0.6
Date. 2026-07-31
Author. Claude.ai (drafting session). Approving: Marvin Percival.
Charter. standing-notes/dunin7-standing-authorization-charter-v0_1. No session executes a change request it drafted (§1).
Target. /Users/dunin7/loomworks-engine. No work in loomworks, loomworks-ui, or stele.
Baseline. Engine main a317051. See §3 — the baseline suite is not green, and v0.1–v0.4 assumed it was.
Build-list item. B-29.
Supersedes. v0.4 at record 05ba36b, v0.3 at 9c1766f, v0.2 at d697187, v0.1 at b36a676. All stand as siblings, unaltered.
Status. Pre-execution. This version changes how the build runs, not only how the document reads — per standing-notes/loomworks-standing-note-executable-document-versions-v0_3, do not run from a sibling.
Supersedes. v0.5 at record 6165bf6, and v0.1–v0.4. All stand as siblings, unaltered.
Changes from v0.5. This version does not change the design — it corrects a premise and unblocks the last thirteen sites. v0.5 §2.2 said every site takes the same mechanical shape; that held for sixty-six of ninety-three. Three shapes are now named, all following one rule — mint where the object is constructed. Shape two, the shared-helper conversion, ratifies what the build session established and needs no rework. Shape three resolves the creation.py:501 / seed_induction.py case, where a dynamic count makes pre-minting impossible: the constructor mints, the append passes through. Acceptance item 8 is refined so it cannot be read as forbidding that pass-through, and two proof obligations are added for the shapes a mechanical test would miss.
> Work in flight is not invalidated. Branch cr-2026-159-option-a carries Step 0, Step 1 and eighty of ninety-three Step 2 sites. Nothing on it is revised by this version. Resume at the thirteen remaining.
Changes from v0.4, carried. The seam decision was reversed at v0.5: v0.1–v0.4 built option B — append_event stamps the field itself — and Checkpoint A proved it unsound. The build is option A: the caller mints and passes in. §1's safety claim was corrected, D-3's deferred sweep disappeared because it became the work, and the file count went to 36.
The v0.4 build completed Steps 0 through 2 on branch cr-2026-159-provenance-seam and halted at Checkpoint A on the CR's own named condition: a pre-existing test needed editing. tests/test_consideration_triggers.py:95 passes at baseline and fails with the change, established by causation rather than inference.
The test is the symptom. The defect is in option B itself.
MemoryObject is frozen, so append_event corrects a copy. The caller keeps the object it built, still carrying the fabricated value. append_event returns MemoryEventRow — the row, not the corrected object — so the caller has no path to the true value even if it wanted one. Roughly 35 of the 93 call sites return their locally-built object immediately after the append. Every one of them now hands back provenance that disagrees with the row just written.
v0.1–v0.4 §1 asserted the opposite: "Nothing reads the field… the surface is write-side only, so no downstream consumer can break." That is false, and the sweep that produced it asked the wrong question. It swept for what reads wasGeneratedBy. The exposure is what returns the object — a different set, which nothing in the inspection looked for. Recorded rather than smoothed: the error was in the drafting session's framing of the search, not in the search.
Why the divergence is not tolerable even if no client currently sees the field. The row would be right and the object in hand would be wrong, in the same call. A system whose governing rule is that a surface states only what it has read back cannot hold an object that contradicts the record it just wrote and call it harmless because nobody is looking. The broken test is proof that something already looks.
The caller mints the event identifier and passes it to append_event. The object the caller holds and the row that is written carry the same value by construction, so no correction, copy or back-patch is needed anywhere.
This is the pattern already proven in-tree. The three sites in persons/personal_engagement.py pre-mint, stamp, and pass the same identifier to their insert. Option A generalises what those three already do.
src/loomworks/memory/events.py
append_event gains a required keyword parameter event_id: UUID, used in place of the internal mint. Required, not optional with a fallback — an optional parameter leaves a silent path back to the fabricated value and would let a new call site reintroduce the defect without any signal.
Remove entirely the v0.4 stamping block: the mint, the nested model_copy, and its comment. Under option A the object arrives correct and must not be rewritten.
The module docstring correction from v0.4 stands as already applied — it is on the discarded branch, so it is re-applied here. It names all four write paths and replaces the false claim that append_event is the only one.
Sixty-six sites take the mechanical shape. Mint above the Provenance(...) construction, use the variable in both places.
Twenty-seven do not, and v0.5 was wrong to say they did. (Corrected at v0.6. v0.5 §2.2 said "Each site takes the same mechanical shape," which held for 71% of the work. The premise was asserted from the sweep's count of sites rather than from reading their shapes.) The three shapes, all with the same rule behind them — mint where the object is constructed:
Shape one — the mechanical site. Mint, construct, pass. Sixty-six sites.
Shape two — the shared helper. Four helpers construct provenance for thirteen appends between them: _bump_composition, _bump_shape_event, _build_next_version, _advance_render_state. Each takes a required event_id parameter; the caller mints and passes it to both the helper and the append. The construction guarantee is preserved rather than degraded to reading the value back off the object. (Established by the v0.6 build session and ratified here — the branch needs no rework.)
Shape three — construction and append separated across a function or module boundary, with a count not known until runtime. The hard case is creation.py:501, which appends Finding objects built in seed_induction.py inside a loop whose length depends on a parsed model response. The caller cannot pre-mint because it does not know how many identifiers to mint.
The rule: the constructor mints; the append site passes the value through from the object.
event_id=finding.provenance.wasGeneratedBy
Why this is a pass-through and not a read-back. Under option B the object held a fabricated value and append_event corrected a copy, so reading from the object would have propagated the fabrication. Under option A the object holds the true value, minted at construction. One value reaches two destinations, which is the whole of the guarantee — the same guarantee the sixty-six mechanical sites give, reached the same way. Shape two applies this across a function boundary; shape three applies it across a module boundary. There is no third principle.
What this forbids, so the rule cannot be stretched. The append site may pass a value through from an object whose provenance was minted at construction. It may not mint one itself, and it may not pass a value through from an object it did not itself receive from a constructor that mints. [EXECUTING SESSION: for each shape-three site, name the constructor that mints. If none can be named, that site halts.]
event_id = uuid.uuid4()
...
provenance=Provenance(
wasAttributedTo=actor,
wasGeneratedBy=event_id,
...
),
...
await append_event(..., event_id=event_id, ...)
Where a site appends more than one object, each object needs its own identifier — one event per append, and two objects appended separately are two events. [EXECUTING SESSION: report any site where one minted value would otherwise be reused across two appends. That is a correctness question, not a mechanical one, and it halts.]
The two backfill repairs from v0.4 §6.3 and §6.4 stand unchanged. Neither calls append_event, so neither is reached by the signature change. The anchors are in v0.4 §6.4's table and re-verified unmoved at the v0.4 Step 0.
D-3's deferred sweep no longer exists. Option B left 83 sites minting a discarded value and deferred their removal behind B-25. Option A visits all 83 as the work. There is nothing left over.
The declaration change disappears. wasGeneratedBy stays required at memory/base.py:147. Option B would eventually have needed it optional to delete the dead arguments; option A never does.
A fact v0.1 through v0.4 assumed and none of them checked. tests/test_stele_router_mount fails at a317051 with a 401, unrelated to this work.
Every prior version's acceptance criterion read "suite green." Against this baseline that criterion can never be met, and a session holding to it literally would halt forever or, worse, "fix" an unrelated failure to satisfy it.
Corrected criterion: no new failures. Record the baseline failure set at Step 0 by name, and compare against it at each step. Any failure not in the Step 0 set is an anomaly and halts.
The 401 is not in scope. It is recorded here and belongs on the build list as its own item, not folded into this one.
83 sites across 36 files. The v0.4 Step 0 sweep re-ran it at a317051 and returned 36.
Corrections preserved. The original sizing said 36. inspection-briefs/loomworks-inspection-corrections-note-v0_1 C-4 "corrected" it to 37, and manifest v0.77 absorbed that. C-4 was wrong and the original sizing was right. Two independent sweeps now say 36 against C-4's one. The corrections note and the manifest both owe an amendment; the site count of 83 was never in dispute.
Unchanged in substance, and option A tightens one of them.
Before B-25. Under option B this was a caution; under option A it is strict. B-25 edits wasAttributedTo and this CR edits wasGeneratedBy — the two keyword arguments of one Provenance(...) expression on consecutive lines — at api/routers/seed_extraction.py, api/routers/seed_commit_from_brief.py, and api/routers/me_create_engagement.py. B-25's change request must be drafted against post-A code, not pre-A code.
Before FORAY integration opens at foray/reserved_emit.py. Unchanged.
Before the completion arc's engine rider. Unchanged.
Step 0 — pre-flight, and dispose of the v0.4 branch.
Verify HEAD, working tree, alembic head 0102. Record the baseline failure set by name (§3). Re-run the site sweep and report sites and files. Confirm the two backfill anchors unmoved.
Delete branch cr-2026-159-provenance-seam from the v0.4 run. It builds an abandoned design. Report its head SHAs in the Step 0 commit message so the discarded work is locatable in the reflog, then delete the branch. Nothing from it is cherry-picked except the docstring correction, which is re-applied at Step 1.
Commit: CR-2026-159 step 0: pre-flight, baseline failure set, discard the option-B branch
Step 1 — the signature and the docstring.
§2.1. append_event takes a required event_id. Remove the option-B stamping block. Re-apply the docstring correction. The tree will not pass at this step — every call site is now missing a required argument. That is expected; do not stop.
Commit: CR-2026-159 step 1: append_event takes the event identifier from its caller
Step 2 — the call sites, in tranches.
§2.2, across 36 files. Commit per tranche, not per file and not all at once — group by the heaviest clusters first: engagement creation, assertions, bootstrap, shaping process, channels, boundary, credit bootstrap, seed conversation. Run the suite at each tranche and compare against the Step 0 baseline set.
Commit per tranche: CR-2026-159 step 2/N: <cluster> call sites
Step 3 — the two backfills. Unchanged from v0.4.
Commit: CR-2026-159 step 3: repair the two backfill provenance fabrications
Step 4 — tests.
The five from v0.4 §8, with two changed by option A: the equality now holds on the caller's object as well as both stored columns, and the caller's-value-is-discarded test becomes its inverse — the caller's value is honoured, because under option A the caller's value is the true one.
Commit: CR-2026-159 step 4: provenance equality regression tests
CHECKPOINT A — report, then proceed. Failure set versus the Step 0 baseline; the five tests; live verification on a throwaway database showing the row, both JSONB columns, and the returned object all agreeing. No Operator confirmation. Halt and queue only if: a pre-existing test outside the baseline set fails; the live verification does not show three-way agreement; or any charter §6 anomaly.
Step 5 — implementation notes, carrying D-4's recorded statement unchanged.
CHECKPOINT B — merge and tag. --no-ff to main, tag provenance-seam-v0_1, push. Authorized under R-2 including the push. Deployment is never autonomous (F-1).
append_event cannot be called without an event identifier.provenance.wasGeneratedBy equals the row's event_id, in both the provenance column and the nested payload.personal_engagement.py sites still satisfy it.memory/events.py's docstring names all four write paths.uuid.uuid4() appears as a direct argument to wasGeneratedBy anywhere in the tree. Minting into a named variable and using that variable satisfies this at all three shapes. (Refined at v0.6 — v0.5's wording read as though it forbade the shape-three pass-through, which it does not: a pass-through introduces no uuid4() call at the append site.)0102.loomworks-engine; no database touched except a throwaway.test_stele_router_mount 401. Recorded in §3; belongs on the build list separately.wasGeneratedBy argument, never wasAttributedTo.
CR-2026-159 — B-29, the provenance seam. Execution session.
CR: loomworks-record/change-requests/cr-2026-159-provenance-seam-v0_6.md
Confirm v0_6 is the highest version present before starting. v0_1 through v0_5 are
superseded and v0_5 REVERSED the seam decision — running from a sibling builds the
abandoned design.
Read the CR in full. Charter dunin7-standing-authorization-charter-v0_1 governs.
Target: /Users/dunin7/loomworks-engine at main a317051 or later.
The engine belongs to this CR for its duration.
playground_dev is the live production database and is not touched. Verification runs
on the walk-audit dev pattern: spare port, inline environment, throwaway database,
live processes and .env untouched, teardown after.
The baseline suite is NOT green — test_stele_router_mount fails at a317051 with a 401.
Record the baseline failure set at Step 0 and compare against it. "Suite green" is not
the criterion; "no new failures" is.
Step 0 deletes the branch cr-2026-159-provenance-seam from the abandoned v0.4 run.
Report its head SHAs before deleting.
Step 1 leaves the tree failing to import — every call site is missing a required
argument. That is expected. Step 2 repairs it in tranches.
Steps 0 through 4 auto. CHECKPOINT A reports and proceeds on a clean report.
Step 5 auto. CHECKPOINT B merges, tags provenance-seam-v0_1, pushes.
Deployment is never yours.
Halt and queue on: any failure outside the Step 0 baseline set; any site where one
minted identifier would be reused across two appends; any charter section 6 anomaly.
Append the outcome to current-status/dunin7-status-brief at close.
DUNIN7 — Done In Seven LLC — Miami, Florida CR-2026-159 — B-29: the provenance seam — v0.6 — 2026-07-31 Option B abandoned at Checkpoint A. Eighty-three sites, deliberately, because the cheap fix was not correct.