Skip to content

feat(conformance): dispatch protected runs against a named merged revision - #314

Merged
BunsDev merged 5 commits into
mainfrom
fix/219-producer-ref-dispatch
Sep 19, 2026
Merged

BunsDev merged 5 commits into
mainfrom
fix/219-producer-ref-dispatch

Conversation

@BunsDev

@BunsDev BunsDev commented Sep 16, 2026

Copy link
Copy Markdown
Member

⚠️ Proposal — this widens what a protected dispatch may validate, and wants a real review

It also pins harnessAuthority.revision to 4246664, 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 main points at when it starts: every job is gated on github.ref == 'refs/heads/main' and the workspace is checked out at github.sha. But a cross-repository binding names one exact producer merge, and scripts/conformance-contract.mjs:1090,1571-1578 requires that merge's tree to equal its reviewed second parent's tree. So a binding describes one commit, and the next commit to main — conformance-related or not — leaves it unable to describe the tip.

Observed today: SDK 4de2edfcb bound Chat 3c6f804 and was stale before its second use; a dispatch against the then-current tip failed on all three platforms at phase1.stage.evidence-authority.checkout with 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_revision dispatch input. A new producer-revision job 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.ts failed seven assertions on the first attempt, and one failure was substantive rather than cosmetic. verifyHardenedWorkflowGraph enumerates 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 of windows-supervisor moved 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. verifyHardenedWorkflowGraph now includes the new job in both the main-ref loop and the privileged-permissions loop, and a new verifyResolvedProducerRevision asserts that the job performs the ancestry check, binds OPENCOVEN_DISPATCH_SHA to github.sha, and that the raw inputs.producer_revision is never checked out directly.

The job is named producer-revision, not producer, because the verifier already uses the label producer for platform-conformance.

Two other assertions moved and both should be read as deliberate spec changes: the exact needs: line for platform-conformance, and the count of full checkouts, which rises from 3 to 4 for the resolution job.

Verification

Suite Result
client-v1-conformance-workflow.test.ts + specification-guards.test.ts 144 passed, 19 skipped
phase1-conformance-lock.test.ts (heavy config) 95 passed, 1 skipped

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_revision for 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, signerDigest and the bootstrap digests.

Refs #219. Context and the full race analysis in OpenCoven/sdk#38.

🤖 Generated with Claude Code

Copilot AI lite review requested due to automatic review settings September 16, 2026 20:51

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 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 with rev-parse HEAD, and the run can fail or produce an identity different from the requested SHA. Check the object type with git cat-file -t "$requested" and accept only commit before 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 harnessAuthority to 4246664d6a8fbbe113b12d504470c1215e9ff1fd leaves the conformance guide's current-lock paragraph at docs/phase1-conformance.md:217-218 claiming b5e0fac1... and tree 1642836d.... 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=...' before git 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: 0 entries, 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.

Comment thread .github/workflows/client-v1-conformance.yml
Comment thread .github/workflows/client-v1-conformance.yml
BunsDev and others added 2 commits September 16, 2026 16:09
…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>
@BunsDev
BunsDev force-pushed the fix/219-producer-ref-dispatch branch from 9ee4661 to 59b18b5 Compare September 16, 2026 21:12
@BunsDev BunsDev added the ci:full Run the macOS and Windows CI jobs on this pull request label Sep 16, 2026
@BunsDev

BunsDev commented Sep 16, 2026

Copy link
Copy Markdown
Member Author

Handoff

Handing this to whoever owns the conformance lane. No further pushes from my side. Current state:

value
branch head 59b18b5 (lock repin)
sources commit / pinned revision bdd0dca
base f21953f, equal to main at the time of writing
labels ci:full (required here — the branch touches the protected workflow)

The earlier Web checks failure was a Biome formatter line-width error in a line I wrote in the spec test; it is fixed and biome check src/ scripts/ is clean. phase1-conformance-lock.test.ts passes (95 passed, 1 skipped), as do the workflow spec and specification guards (144 passed, 19 skipped together).

What a reviewer needs to decide

This 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 resolve-producer-revision is the right one:

git merge-base --is-ancestor "$requested" "$OPENCOVEN_DISPATCH_SHA"

The claim is that this keeps the boundary where it was — only reviewed history that reached main can be validated — while removing the requirement that it be the newest such history. If that reasoning is wrong, the change is wrong, and nothing else in the PR matters.

Two further points worth a reviewer's attention:

  1. verifyHardenedWorkflowGraph and its negative fixtures caught a real gap in my first attempt: inserting a job ahead of windows-supervisor moved the first if: github.ref == 'refs/heads/main' into a job the verifier did not cover, so the mutation tests stopped detecting it. The verifier now covers the new job in both loops, and verifyResolvedProducerRevision asserts the ancestry check, the OPENCOVEN_DISPATCH_SHA binding, and that the raw input is never checked out. Please confirm that closure is complete rather than merely sufficient to make the tests pass.
  2. Two assertions changed shape deliberately and should be read as spec changes, not fixups: the exact needs: line for platform-conformance, and the count of full checkouts rising from 3 to 4.

What has not been tested

No protected run has exercised this. The honest tests are a dispatch supplying producer_revision for a commit that is not the tip, confirming it validates that commit, and a negative dispatch with an unmerged revision, confirming refusal. Both need the protected environment.

Merge constraints

harnessAuthority.revision is pinned to bdd0dca, a commit inside this branch: merge with a real merge commit, never squash, and rebase immediately before merging so the merge tree equals the branch tree. Rebasing invalidates the pin, so the lock and the golden expectations in src/phase1-conformance-lock.test.ts must be regenerated against the new sources SHA each time.

An SDK rebinding is required after merge: the workflow's bytes change, and the binding carries workflow.sha256, signerDigest and the bootstrap digests.

Why hand off rather than land it

Four attempts on the sibling PR #307 each lost the merge window while CI ran; ci:full puts the macOS Rust cross-build in the path, making a cycle roughly 25 minutes, and main moved during every one. The last drift came from #310, a UI change unrelated to conformance — which is the same observation that motivates this PR. Someone sequencing against their own merges lands it in one pass.

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
@BunsDev

BunsDev commented Sep 19, 2026

Copy link
Copy Markdown
Member Author

Rebuilt on current main, which has moved twice since this was last green (#325 raised the Windows supervisor cap, #326 landed the bounded quota retry classification).

Merged rather than rebased, so bdd0dca stays reachable and the pin it carries stays valid. Two commits added:

  • 28821d4 — the merge, carrying every governed digest refreshed against the merged tree: both workflows, the schema-v2 producer, the Windows job supervisor, and the Cargo and keyring production deltas.
  • 96dd7f0 — repin only, advancing the authority to 28821d4.

The merged tree carries all three changes, each checked individually: this branch's producer-revision job and ancestry gate, main's 30-minute cap, and #326's supervisor classification.

Two stale things surfaced while rebuilding, both worth recording:

The original repin in 59b18b5 only touched the lock and its test copy, never the prose, so this branch has been claiming authority b5e0fac1 in docs/phase1-conformance.md while its lock said bdd0dca7. Corrected.

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 28821d4 was amended rather than followed by a third commit.

Re-verified: authority tree matches 28821d4^{tree}, all 25 governed files and 10 production deltas match, heavy suite 909 passed, workflow suite 91 passed, typecheck and lint clean.

The SDK side this was waiting on is OpenCoven/sdk#295.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ci:full Run the macOS and Windows CI jobs on this pull request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants