feat(conformance): dispatch protected runs against a named merged revision - #314
Conversation
There was a problem hiding this comment.
🔵 Needs a closer look
The critical attestation mismatch and unresolved validation coverage require further changes and protected-environment verification.
Pull request overview
Adds optional producer_revision support for protected conformance runs against a verified ancestor commit instead of only main’s tip.
Changes:
- Adds ancestry validation and propagates the selected revision across conformance jobs.
- Updates workflow tests, authority locks, documentation, and expected metadata.
File summaries
| File | Summary |
|---|---|
.github/workflows/client-v1-conformance.yml |
Adds revision resolution and selected-commit checkouts. Critical (1 vote): attestation provenance still reflects the dispatch tip. Moderate (1 vote): annotated tag objects are accepted. Moderate (2 votes): protected dispatch behavior lacks execution-level coverage. |
src/client-v1-conformance-workflow.test.ts |
Extends workflow validation. Moderate (1 vote): assert ancestry validation precedes output publication. Nit (1 vote): correct the checkout-count comment. |
phase1-conformance.lock.json |
Repins harness authority metadata. |
docs/phase1-conformance.md |
Documents targeted dispatches. Nit (1 vote): update the stale lock and tree values. |
src/phase1-conformance-lock.test.ts |
Updates expected authority values. |
Review details
Suppressed comments (4)
.github/workflows/client-v1-conformance.yml:55
^{commit}peels annotated tag objects, so a 40-hex annotated-tag SHA whose target is an ancestor passes this check even though the requested value is not itself a commit. The raw tag SHA is then propagated to the Windows bootstrap, where it is compared withrev-parse HEAD, and the run can fail or produce an identity different from the requested SHA. Check the object type withgit cat-file -t "$requested"and accept onlycommitbefore publishing it.
if ! git cat-file -e "$requested^{commit}" 2>/dev/null; then
echo 'Producer revision is not a commit in this repository.' >&2
exit 1
phase1-conformance.lock.json:25
- Repinning
harnessAuthorityto4246664d6a8fbbe113b12d504470c1215e9ff1fdleaves the conformance guide's current-lock paragraph atdocs/phase1-conformance.md:217-218claimingb5e0fac1...and tree1642836d.... The lock, lock test, and documentation now disagree; update that paragraph with this repin.
"revision": "4246664d6a8fbbe113b12d504470c1215e9ff1fd",
"tree": "415d360d5bc111fb3eba5d7d5769534e86f859a9",
src/client-v1-conformance-workflow.test.ts:260
- This guard only checks that the ancestry command and the output expression appear somewhere in the job. It would still pass if a future edit moved
printf 'revision=...'beforegit merge-base --is-ancestor, allowing an unchecked value to reach downstream jobs while the regression test stays green. Assert the relative order of the ancestry check and output (as the validator-input checks already do).
function verifyResolvedProducerRevision(job: string, workflow: string): void {
if (
!job.includes('git merge-base --is-ancestor "$requested" "$OPENCOVEN_DISPATCH_SHA"') ||
!job.includes('OPENCOVEN_DISPATCH_SHA: ' + githubShaExpression) ||
!job.includes('revision: ' + resolvedProducerExpression)
) {
throw new Error('producer revision job does not verify ancestry of the dispatch ref');
src/client-v1-conformance-workflow.test.ts:493
- This assertion counts four
fetch-depth: 0entries, but the comment names the Windows bootstrap as one of them even though that workspace is fetched manually; the fourth match is the Cave authority checkout. Please describe the actual four checkouts so this guard does not mislead future workflow changes.
// four full checkouts: producer-revision resolution, the supervisor build,
// the Windows bootstrap workspace and the Unix workspace.
expect(workflow.match(/ {10}fetch-depth: 0/gu)).toHaveLength(4);
- Files reviewed: 5/5 changed files
- Comments generated: 2
- Review effort level: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
…ision A protected run validated whatever main pointed at when it started. Because a cross-repository binding names one exact producer merge, and the contract requires that merge's tree to equal its reviewed parent's tree, any later commit to main left the binding unable to describe the tip. Unrelated changes closed the window as effectively as conformance ones. Add the optional producer_revision input. The producer-revision job requires an exact lowercase 40-hex commit that exists here and is an ancestor of the dispatch ref, then publishes it to the supervisor build, the Windows bootstrap and the Unix workspace checkout. Omitting it preserves today's behaviour. The ancestry check keeps the trust boundary where it was: unmerged, unrelated and fork revisions are refused, so only reviewed history that reached main can be validated. It simply no longer has to be the newest such history. verifyHardenedWorkflowGraph now covers the new job in its main-ref and permissions loops, and verifyResolvedProducerRevision asserts the ancestry check, the dispatch-sha binding, and that the raw input is never checked out. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…input The conformance workflow's bytes changed, so harness.revision, harnessAuthority.revision/tree and the workflow's files entry move to the sources commit on this branch, with the golden expectations in phase1-conformance-lock.test.ts following. Merge with a real merge commit, never squash; the pinned revision must stay an ancestor of main. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
9ee4661 to
59b18b5
Compare
HandoffHanding this to whoever owns the conformance lane. No further pushes from my side. Current state:
The earlier What a reviewer needs to decideThis changes what a protected dispatch is permitted to validate, so it is a trust-boundary review, not a mechanical one. The specific question is whether the ancestry constraint in The claim is that this keeps the boundary where it was — only reviewed history that reached Two further points worth a reviewer's attention:
What has not been testedNo protected run has exercised this. The honest tests are a dispatch supplying Merge constraints
An SDK rebinding is required after merge: the workflow's bytes change, and the binding carries Why hand off rather than land itFour attempts on the sibling PR #307 each lost the merge window while CI ran; Refs #219. Full analysis in OpenCoven/sdk#38. |
…sitory The gate that selects which commit all three native lanes execute was only checked by matching strings in the workflow file, so nothing proved it accepts or refuses anything. Extract the shipped shell from the workflow and run it against a temporary repository. Covers the default tip, an explicitly named tip, a merged ancestor, a commit on an unmerged branch, a descendant of the dispatch tip, an abbreviated revision, an uppercase revision, a ref name, an empty value, and a well-formed revision absent from the repository. The uppercase case uses a fixed value rather than upper-casing the fixture SHA, which would be a no-op for an all-digit SHA and would stop exercising the lowercase rule.
Brings in the raised Windows supervisor timeout and the bounded quota retry classification, and refreshes every governed digest against the merged tree: the two workflows, the schema-v2 producer, the Windows job supervisor, and the Cargo and keyring production deltas. Digests land with the merge that changes the files. The authority revision and tree advance in the repin that follows.
Repin only: harness revision, harness authority revision and tree, and the prose and test copies of them. The digests they describe landed with the merge. Refs #324
|
Rebuilt on current Merged rather than rebased, so
The merged tree carries all three changes, each checked individually: this branch's Two stale things surfaced while rebuilding, both worth recording: The original repin in Six rows of the documented pin table had drifted from the files they describe — the conformance workflow, the schema-v2 producer, the job supervisor, and three Windows quota test scripts. A test caught the first two; I checked the whole table rather than fixing them one at a time. Those are digests, so they belong with the merge and not the repin, which is why Re-verified: authority tree matches The SDK side this was waiting on is OpenCoven/sdk#295. |
It also pins
harnessAuthority.revisionto4246664, a commit inside this branch, so it must merge with a real merge commit, never a squash, and be rebased immediately before merging.The problem
A protected run validates whatever
mainpoints at when it starts: every job is gated ongithub.ref == 'refs/heads/main'and the workspace is checked out atgithub.sha. But a cross-repository binding names one exact producer merge, andscripts/conformance-contract.mjs:1090,1571-1578requires that merge's tree to equal its reviewed second parent's tree. So a binding describes one commit, and the next commit tomain— conformance-related or not — leaves it unable to describe the tip.Observed today: SDK
4de2edfcbbound Chat3c6f804and was stale before its second use; a dispatch against the then-current tip failed on all three platforms atphase1.stage.evidence-authority.checkoutwith no platform reaching native scenarios. The most recent window closed because of #310, a familiar-identity UI change with no relationship to conformance.The change
An optional
producer_revisiondispatch input. A newproducer-revisionjob resolves it, requires an exact lowercase 40-hex commit that exists in this repository and is an ancestor of the dispatch ref, and publishes it to the supervisor build, the Windows bootstrap (OPENCOVEN_CHAT_SHA) and the Unix workspace checkout. Omitting the input keeps today's behaviour exactly.The ancestry check is the part that matters for review. It is what keeps the trust boundary where it was: an unmerged branch, an unrelated commit, or a fork revision is refused, so a protected run still only validates reviewed history that reached
main. What changes is that it no longer has to be the newest such history.The spec test caught a real gap, twice
src/client-v1-conformance-workflow.test.tsfailed seven assertions on the first attempt, and one failure was substantive rather than cosmetic.verifyHardenedWorkflowGraphenumerates the protected jobs and requires each to carry the exact main-ref constraint; the negative fixtures mutate the first occurrence of that constraint and expect a throw. Inserting a new job ahead ofwindows-supervisormoved the first occurrence into a job the verifier did not know about, so the mutation stopped being detected. That is exactly the gap the negatives exist to catch, and my change had introduced it.The fix is not to relax those tests.
verifyHardenedWorkflowGraphnow includes the new job in both the main-ref loop and the privileged-permissions loop, and a newverifyResolvedProducerRevisionasserts that the job performs the ancestry check, bindsOPENCOVEN_DISPATCH_SHAtogithub.sha, and that the rawinputs.producer_revisionis never checked out directly.The job is named
producer-revision, notproducer, because the verifier already uses the labelproducerforplatform-conformance.Two other assertions moved and both should be read as deliberate spec changes: the exact
needs:line forplatform-conformance, and the count of full checkouts, which rises from 3 to 4 for the resolution job.Verification
client-v1-conformance-workflow.test.ts+specification-guards.test.tsphase1-conformance-lock.test.ts(heavy config)The workflow parses as YAML and the job graph is
producer-revision → windows-supervisor → platform-conformance → validate → attest → aggregate.What I have not done
No protected run has exercised this. The honest test is a dispatch supplying
producer_revisionfor a commit that is not the tip, confirming it validates that commit, plus a negative dispatch with an unmerged revision confirming it is refused. Both need the protected environment.An SDK rebinding will be required after merge, since the workflow's bytes change and the binding carries
workflow.sha256,signerDigestand the bootstrap digests.Refs #219. Context and the full race analysis in OpenCoven/sdk#38.
🤖 Generated with Claude Code