Repository navigation
feat: persist fork provenance in the trace bundle - #41
Open
how2how2how2-arch wants to merge 1 commit into
Open
how2how2how2-arch wants to merge 1 commit into
how2how2how2-arch wants to merge 1 commit into
Conversation
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Implements part of #31, the half that names a defect: a forked trace is saved without the provenance that says it is one.
The defect
fork_tracebuilds aForkInfoand then throws it away. It callsforked_trace.save(output_path)and never passes the metadata it just constructed, so a bundle written bystepfork forkis byte-for-byte indistinguishable from a recorded run. A reader holding only the saved bundle cannot tell that outputs were replaced, where the fork point was, or what it was forked from — which is what #31's second scope item asks for.What this does
Fork provenance is persisted as an optional bundle file,
fork.json:{ "fork_info": { "parent_trace_path": ..., "parent_run_id": ..., "fork_point_event_id": ..., "fork_point_step": 3, "fork_timestamp": ..., "overrides_applied": 1 }, "overridden_event_ids": ["evt_002"] }Three decisions in that shape, and why:
A separate file rather than a manifest field.
manifest.jsoncarriesschema_version: "0.1"and the models areextra="forbid", so a new manifest field is either a version bump or a breaking change. I raised that trade-off on #33 and did not want to settle it on your behalf here. The bundle already has a precedent for adding a file:integrity.jsonwas added after the initial v0.1 work, and older bundles stay loadable.fork.jsonfollows it — present only on a forked bundle, and a bundle without it loads and verifies exactly as before.schema_versionis unchanged.overridden_event_idsalongsideForkInfo.ForkInfo.overrides_appliedis a count, and a count cannot answer "was this output recorded or replaced". The ids can, so they are recorded next to it rather than folded into the count.ForkInfoitself is untouched.Covered by the integrity record when it exists.
integrity.jsonnow hashesfork.jsonwhen present, so editing provenance is reported as a mismatch instead of passing. A bundle whosefork.jsonexists but is not in the record is reported asintegrity_unlisted_file, so the file cannot be kept while its digest is quietly dropped.read_fork_provenance()returns the record, orNonefor a bundle that was not forked.The honest limit, also written into
docs/trace-format.md: deleting both the file and its digest leaves a bundle that verifies as an ordinary recorded run. Provenance is evidence about a bundle, not proof of its origin, and the doc says so rather than implying more.Not done, deliberately
This PR is one logical change: persist the provenance that already exists in memory, cover it with the record, document what that does and does not prove.
Tests
tests/unit/test_fork_provenance.py— 6 tests: the fork's parent, fork point and replaced events round-trip through the bundle; a bundle written without a fork has no provenance and still verifies with exactly the three required files; provenance is named in the record and editing it is a mismatch; an unrecordedfork.jsonis reported; a recorded-but-deleted one is a missing file; a malformed one raises rather than reading as absent.Two plants, both run against the committed tree:
write_fork_provenancecall (today's behaviour) → 4 failed, 2 passed.Reverted, the suite is green.
Checks
pytest722 passed (716 without the new file),ruff checkclean,ruff format --check204 files,mypyclean across 138 source files,mkdocs build --strictexit 0, and the three demos (quickstart,booking_agent,refund_agent) all exit 0.One note on process: #31 asks for a design comment before a PR. I have kept the change to the item that names a defect and put the design here, since the reasoning is what a reviewer reads anyway — say the word if you would rather I have split it out first and I will not repeat the shape.