Repository navigation
Commit 55ccbe9
feat(S2): step 8 — the canonical patch bundle (patch + manifest + postimage)
Turns the accepted rewriter's postimage into a deterministic, reviewable, hash-addressed
artifact for the later step-9 self-gates and the 007 gate:
python -m ownlang own-fix subscriptions apply \
--plan <validated-plan.json> --candidates <candidates.json> \
--root <source-root> --out <artifact-dir> [--rewriter <owen-rewrite-command>]
<out>/change.patch <out>/apply-manifest.json <out>/postimage/<canonical-rel-path>
Thin orchestration only — no model, no o7, no analyzer, no gate.toml. It re-runs the
accepted validate_apply_inputs, invokes the accepted Owen.CSharp.Rewriter as an argv
VECTOR (never a shell string, so nothing in a path can be read as syntax), verifies the
transport output, then builds and publishes the bundle.
rewriter-report.json is transport, and is verified rather than believed — a neighbouring
executable produced it, which is not the same as it being true. Checked: exact key set,
version/operation, input_bundle_sha256, validated_plan_sha256, target api, source path,
pre-SHA, a RECOMPUTED post-SHA over the actual postimage bytes, applied/manual as sets
with the expected partition, a postimage for the allowed path ONLY, and no unexpected file
anywhere in the rewriter workdir. The validated plan stays the sole authority for action,
the hash-bound candidates the sole authority for identity; the report contributes nothing
to the manifest.
change.patch is generated in-process (difflib over byte-lines, git headers) rather than
shelled out to `diff`, so it cannot inherit a timestamp column, a temp path, a username or
a cwd. Lines split on b"\n" ONLY — git's own notion of a line — because bytes.splitlines()
also breaks on a lone \r (and \v, \f, \x1c), which would silently reflow a file whose
content carries one. A missing EOF newline gets git's own marker on both sides. The result
is a/<rel> -> b/<rel>, one file, no rename/mode/binary records, and `git apply` reproduces
the postimage byte for byte.
apply-manifest.json is the authority artifact: exact shape, sort_keys + compact separators
+ trailing newline, built ONLY from the validated plan (actions, in candidate order), the
plan's own bytes, and the real postimage/patch bytes. No timestamps, no absolute or temp
paths, no hostname, run id, command line or token counts — a re-run over the same pristine
inputs is byte-identical.
An all-manual_review plan is VALID, not a refusal: postimage == preimage, change.patch is
zero length, patch_sha256 is the sha of no bytes, applied_findings is empty and every id
stays in candidate order under manual_review_findings.
Publication is atomic: everything is built in a sibling work directory and the finished
staging bundle is renamed into place, so <out> never exists until every check has passed.
An existing <out>, an <out> physically inside the source root (symlinks resolved), or any
rewriter/patch/manifest/write/rename failure leaves no <out> at all, no work directory,
and a byte-identical source tree.
Tests: tests/test_patch_bundle.py (52 checks) covers the patch/manifest pure functions and
every transport-tampering case — those live at the pure-function level ON PURPOSE, since
forging a bad report must not require a test-only hook in the production rewriter.
tests/patch_bundle_regressions.sh + a CI step drive the real chain: the three-part bundle,
canonical headers with no temp/absolute path or timestamp, exact manifest shape and SHAs,
`diff -r` determinism, a git apply round-trip on a pristine copy, mixed actions, the empty
patch, and the refusal/no-output cases.
Steps 9-12 are NOT started: no analyzer delta, no own-check re-run, no OWN001/OWN050
assertions, no gate.toml, no 007 evidence, no STS clone, no apply to a real branch.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JxKjqdGEFzq4UzZupw379G1 parent d30c75d commit 55ccbe9
5 files changed
Lines changed: 900 additions & 3 deletions
File tree
- .github/workflows
- ownlang
- tests
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1709 | 1709 | | |
1710 | 1710 | | |
1711 | 1711 | | |
| 1712 | + | |
| 1713 | + | |
| 1714 | + | |
| 1715 | + | |
| 1716 | + | |
| 1717 | + | |
| 1718 | + | |
| 1719 | + | |
| 1720 | + | |
| 1721 | + | |
1712 | 1722 | | |
1713 | 1723 | | |
1714 | 1724 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
559 | 559 | | |
560 | 560 | | |
561 | 561 | | |
| 562 | + | |
| 563 | + | |
| 564 | + | |
| 565 | + | |
| 566 | + | |
| 567 | + | |
| 568 | + | |
| 569 | + | |
| 570 | + | |
| 571 | + | |
| 572 | + | |
| 573 | + | |
| 574 | + | |
| 575 | + | |
| 576 | + | |
| 577 | + | |
| 578 | + | |
| 579 | + | |
| 580 | + | |
| 581 | + | |
| 582 | + | |
| 583 | + | |
| 584 | + | |
| 585 | + | |
| 586 | + | |
| 587 | + | |
| 588 | + | |
| 589 | + | |
| 590 | + | |
| 591 | + | |
| 592 | + | |
| 593 | + | |
| 594 | + | |
| 595 | + | |
| 596 | + | |
562 | 597 | | |
563 | | - | |
| 598 | + | |
564 | 599 | | |
565 | 600 | | |
566 | | - | |
| 601 | + | |
567 | 602 | | |
568 | 603 | | |
569 | 604 | | |
| |||
572 | 607 | | |
573 | 608 | | |
574 | 609 | | |
| 610 | + | |
| 611 | + | |
575 | 612 | | |
576 | | - | |
| 613 | + | |
577 | 614 | | |
578 | 615 | | |
579 | 616 | | |
| |||
0 commit comments