Skip to content

feat: add bounded version preparation delivery contract - #105

Merged
pcvantol merged 24 commits into
mainfrom
codex/ep-version-preparation-delivery
Sep 8, 2026
Merged

feat: add bounded version preparation delivery contract#105
pcvantol merged 24 commits into
mainfrom
codex/ep-version-preparation-delivery

Conversation

@pcvantol

@pcvantol pcvantol commented Sep 8, 2026

Copy link
Copy Markdown
Owner

Implements the EP-owned bounded version-preparation execution seam. The strict v1 request rejects unknown fields, arbitrary paths, non-exact sources, non-canonical stable SemVer and malformed SHA-256 identities. Preparation runs in a fresh isolated worktree, verifies clean source/candidate state, requires the product declaration to bind identity/policy/projection paths, and validates the shared facts in the product-owned prepared receipt. The NUL-safe allowlisted inventory includes both tracked changes and the operation receipt.

Candidate publication uses deterministic ep/version-preparation/<operation-id> branches, preflights the configured GitHub writer’s actor/repository/push scope without exposing credentials, rejects stale target branches and post-push PR-head races, and uses create-or-recover PR semantics for acknowledgement loss. Qualification and immutable local delivery evidence bind exact candidate commit/tree identities and retain PENDING_PROTECTED_MERGE; this adapter never merges.

Closure repairs

The prepared-operation digest is now canonical, content-bound evidence: it covers every resulting allowlisted projection and receipt byte digest plus the admitted operation facts. The candidate is fully re-inventoried and re-hashed immediately before staging, so a changed path, receipt, or byte content cannot be published from a stale preparation. Receipts bind the admitted identity, policy digest, source-event facts, classification/rationale, source/target revisions, versions, paths, authorization reference and delivery mode. NO_BUMP is explicitly rejected by this mutating adapter. Qualification reads the protected base branch's actual required check collection and rejects empty, incomplete, failed or missing required checks. A disposable external fixture-product process exercises the declared receipt contract without relying only on an in-memory helper.

This PR does not grant merge authority, create credentials, alter #100, publish product artifacts, or claim installed qualification. It does not yet wire an EP authorization/grant, CENTRAL-backed durable operation lifecycle, trusted source-event routing/deduplication, a production product-helper process adapter, or a disposable remote GitHub/installed-artifact canary. Those remain explicit follow-up integration work.

Bootstrap release cadence V2

BOOTSTRAP_RELEASE_CADENCE_V2: the protected-delivery request binds release_class and rationale to increment lineage and rejects mismatch. This is not a native Forge planner, installed writer, grant or merge authority.

pcvantol commented Sep 8, 2026

Copy link
Copy Markdown
Owner Author

Scoped architecture/code review of d1ae0de — source blockers despite green CI

Head reviewed: d1ae0de2b8cf1f2c8eb22756f85aaa716080510c. Hosted workflow results and separate Owner Authorization are green. This review inspected version_preparation_delivery.py and the added execution_repository.py adapter methods. It is not a complete repository audit or installed qualification. No production repo/host/grant/credential/status/merge mutation was performed.

I reproduced the cases below using copied method bodies in isolated temporary files, a fake GitHub response adapter, and (for the publication case) real Git with a disposable local bare remote only. These are bounded source correctness findings, not a request for a new releaseplanner, IAM system or publication authority.

VP-D1 — prepared operation digest is a path-list digest, and version/class parity is incomplete

prepare() currently computes:

digest = "sha256:" + hashlib.sha256("\n".join(changed).encode()).hexdigest()

This hashes only filenames, not prepared receipt/content or candidate diff bytes. Arbitrarily different contents at the same paths produce the same value. Recording a candidate tree afterwards identifies the eventual result but does not prove that it is the content previously admitted/prepared.

_validate_prepared_receipt() checks operation/product/policy revision/source/allowed paths, but not the requested baseline, determined target, release class/rationale or increment/event semantics. Isolated probe: request 2.3.0 -> 2.3.1 / PATCH, receipt determined_version=9.0.0, release_class=MAJOR, with the currently checked identities unchanged, is accepted. Changing the receipt bytes leaves the path-list digest unchanged.

Required fix: define and implement distinct digests for the admitted operation, prepared receipt/content and resulting candidate tree as needed. Keep product-owned schemas but validate all shared version/class/lineage facts against the admitted request. Bind the exact prepared bytes/tree, not just its path inventory. No self-referential tracked receipt/amend loop. If a path-list digest is useful, name it honestly and do not treat it as content-integrity evidence.

Negative proofs: altered target/baseline/class/rationale/lineage, changed receipt or projection bytes under unchanged paths, and unchanged honest fixture. This must be checked at the actual prepare-to-publish boundary.

VP-D2 — publish_candidate can commit/push an unexpected staged file after preparation

publish_candidate() trusts prepared['changed_paths'], stages those paths and invokes an ordinary git commit, without checking the final staged inventory/tree against the prepared snapshot. Existing staged changes are included by that commit.

Reproduction with a disposable local Git repo: a prepared path tuple contains only product-version.json and .version-operations/review-operation-001.json. Stage a modification to unrelated.txt after preparation, then invoke the copied publish_candidate() with a fake authorized GitHub adapter and a local bare remote. The resulting committed/pushed diff includes all three paths, including unrelated.txt, and the method returns success.

Required fix: enforce candidate ownership/locking and validate the entire index/candidate delta, modes and content snapshot immediately at commit/publication, including parent/head/branch bindings; recheck the actual committed candidate before any remote push. Reject out-of-scope or post-preparation changes rather than resetting someone else's work. Do not assume git add -- allowed_paths excludes entries already in the index. Prefer immutable candidate/tree publication.

Negative proofs: pre-staged extra file, same allowed path modified after preparation, changed parent/head, and clean positive publication. No remote side effect before rejection.

VP-D3 — exact-head qualification silently ignores commit StatusContext failures

GhCliClient.qualification_for_exact_head() filters statusCheckRollup down to objects with a string status, excluding status-context entries represented with context/state. Isolated fake CLI response: one successful CheckRun plus {'__typename':'StatusContext','context':'Owner Authorization','state':'FAILURE'} produces conclusion PASS, with only the CheckRun retained.

This does not mean this PR has bypassed GitHub merge protection: the adapter does not merge, and this head's own Owner Authorization is green. It means its emitted qualification evidence can falsely claim success for another candidate. The contract must not hide a known failing status.

Required fix: handle CheckRun and StatusContext evidence explicitly, evaluate the required control set from trusted repository/policy authority, reject missing or unsatisfied mandatory controls, preserve their identities/results, and bind qualification to the actual head/base context. Advisory/non-blocking checks remain distinct; don't blanket-promote skipped/neutral controls or infer complete qualification from any single passing check.

Negative proofs: failing/pending Owner Authorization beside green CheckRuns, missing required check, wrong-head evidence, and valid complete positive evidence.

Delivery/architecture scope remains unchanged

Product helpers own version semantics; EP owns execution; repository/GitHub owns protected merge. The approved bootstrap V2 policy remains PATCH per bounded increment, NO_BUMP where applicable, explicit MINOR, no second merge/retry bump. Existing V1 receipts remain historical.

Keep the already-declared omissions visible: real operation/grant admission, CENTRAL-backed lifecycle/recovery, trusted increment identity, concrete product-helper process adapter and installed/remote qualification are not delivered merely by installing this wheel. They must be composed/qualified before claiming automatic runtime versioning. These omissions are separate from the three source bugs above; don't use future wiring to waive defects in the current methods.

Recommendation: retain the current source-foundation scope, close VP-D1/D2/D3 with focused regression evidence on this PR, then requalify exact head. No automatic merge or runtime activation is authorized by this comment.

@pcvantol
pcvantol merged commit ad5f990 into main Sep 8, 2026
17 checks passed
@pcvantol
pcvantol deleted the codex/ep-version-preparation-delivery branch September 8, 2026 18:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant