From bdd0dca7bf954ef3433315e3c2a5a06f3f09e967 Mon Sep 17 00:00:00 2001 From: Val Alexander Date: Wed, 16 Sep 2026 16:09:24 -0500 Subject: [PATCH 1/4] feat(conformance): dispatch protected runs against a named merged revision 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) --- .github/workflows/client-v1-conformance.yml | 61 +++++++++++++++++++-- docs/phase1-conformance.md | 23 +++++++- src/client-v1-conformance-workflow.test.ts | 34 ++++++++++-- 3 files changed, 109 insertions(+), 9 deletions(-) diff --git a/.github/workflows/client-v1-conformance.yml b/.github/workflows/client-v1-conformance.yml index 4e693a7c..7d8a92fc 100644 --- a/.github/workflows/client-v1-conformance.yml +++ b/.github/workflows/client-v1-conformance.yml @@ -5,12 +5,65 @@ on: validator_revision: required: true type: string + producer_revision: + description: >- + Exact merged Chat commit to validate. Defaults to the dispatch ref + tip. It must already be an ancestor of that tip: an unmerged or + unrelated revision is refused, so this decouples a protected run from + whatever happens to be at the tip without widening what may be + validated. + required: false + type: string permissions: contents: read jobs: + producer-revision: + name: resolve-producer-revision + if: github.ref == 'refs/heads/main' + runs-on: ubuntu-24.04 + timeout-minutes: 5 + permissions: + contents: read + outputs: + revision: ${{ steps['resolve'].outputs.revision }} + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 + with: + fetch-depth: 0 + persist-credentials: false + ref: ${{ github.sha }} + - id: resolve + name: Resolve and verify the producer revision + env: + OPENCOVEN_PRODUCER_REVISION_INPUT: ${{ inputs.producer_revision }} + OPENCOVEN_DISPATCH_SHA: ${{ github.sha }} + run: | + set -euo pipefail + requested="${OPENCOVEN_PRODUCER_REVISION_INPUT:-}" + if [ -z "$requested" ]; then + requested="$OPENCOVEN_DISPATCH_SHA" + fi + case "$requested" in + *[!0-9a-f]* | "" ) echo 'Producer revision must be an exact lowercase 40-hex commit.' >&2; exit 1 ;; + esac + if [ "${#requested}" -ne 40 ]; then + echo 'Producer revision must be an exact lowercase 40-hex commit.' >&2 + exit 1 + fi + if ! git cat-file -e "$requested^{commit}" 2>/dev/null; then + echo 'Producer revision is not a commit in this repository.' >&2 + exit 1 + fi + if ! git merge-base --is-ancestor "$requested" "$OPENCOVEN_DISPATCH_SHA"; then + echo 'Producer revision is not an ancestor of the dispatch ref; only merged revisions may be validated.' >&2 + exit 1 + fi + printf 'revision=%s\n' "$requested" >> "$GITHUB_OUTPUT" + echo "Validating producer revision $requested" windows-supervisor: name: build-windows-supervisor if: github.ref == 'refs/heads/main' + needs: producer-revision runs-on: macos-latest timeout-minutes: 30 permissions: @@ -22,7 +75,7 @@ jobs: with: fetch-depth: 0 persist-credentials: false - ref: ${{ github.sha }} + ref: ${{ needs['producer-revision'].outputs.revision }} - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 with: node-version: 24.18.1 @@ -42,7 +95,7 @@ jobs: platform-conformance: name: platform-conformance (${{ matrix.platform }}) if: github.ref == 'refs/heads/main' - needs: windows-supervisor + needs: [producer-revision, windows-supervisor] timeout-minutes: 60 strategy: fail-fast: false @@ -71,7 +124,7 @@ jobs: OPENCOVEN_VALIDATOR_REVISION_INPUT: ${{ inputs.validator_revision }} OPENCOVEN_PROTECTED_VALIDATOR_REVISION: ${{ vars.CLIENT_V1_CONFORMANCE_VALIDATOR_REVISION }} OPENCOVEN_CHAT_REPOSITORY: ${{ github.repository }} - OPENCOVEN_CHAT_SHA: ${{ github.sha }} + OPENCOVEN_CHAT_SHA: ${{ needs['producer-revision'].outputs.revision }} OPENCOVEN_WINDOWS_IMAGE_OS: 'win25-vs2026' OPENCOVEN_WINDOWS_IMAGE_VERSION: '20260907.229.1' OPENCOVEN_WINDOWS_PREVIOUS_IMAGE_VERSION: '20260824.214.3' @@ -1819,7 +1872,7 @@ jobs: with: fetch-depth: 0 persist-credentials: false - ref: ${{ github.sha }} + ref: ${{ needs['producer-revision'].outputs.revision }} - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 if: matrix.platform != 'win32-x64' with: diff --git a/docs/phase1-conformance.md b/docs/phase1-conformance.md index 5f932897..40164b7b 100644 --- a/docs/phase1-conformance.md +++ b/docs/phase1-conformance.md @@ -2020,7 +2020,7 @@ revision authorities can therefore have different workflow hashes: | File | Bytes | SHA-256 | | --- | ---: | --- | -| `.github/workflows/client-v1-conformance.yml` | 177,934 | `79ba921d0bb49c2e88ac5253928e44489462fc80b2ac98b53ab9ed13533fba87` | +| `.github/workflows/client-v1-conformance.yml` | 180,234 | `a22c99529f760247a6590c3ca4f9b205a9ac003a0536bf7a711b5a99df9c6574` | | `scripts/contract-canary.mjs` | 40,618 | `a4c2fe0a5eb6a5ff4653de5374c34c0fb46907c6806a5d23b86d8b37206ef958` | | `scripts/executable-resolution.mjs` | 9,154 | `31e3c412ff8c835f14522f36a59e91f4a4ba82913210ae8e3b4455217503f430` | | `scripts/owned-temp-directory.mjs` | 7,762 | `95f546ef9ed614f2a0f55d356ddfc54c943fc53b595b4eebebfcbd4db68e5c0b` | @@ -2788,6 +2788,27 @@ Fresh reviewed source binding, SDK rebinding, and protected validation are required before attributing the Windows failure or claiming a repaired run. +### Dispatching a protected run against a specific merged revision + +`workflow_dispatch` previously validated whatever `main` pointed at when the +run started. The cross-repository contract requires the evidence producer to be +a merge whose tree equals its reviewed second parent's tree, so a binding names +one exact merge; any later commit to `main` — conformance-related or not — +leaves that binding unable to describe the tip. Protected runs were therefore +only usable inside the window between a binding landing and the next merge. + +The optional `producer_revision` input names the commit to validate. The +`resolve-producer-revision` job requires an exact lowercase 40-hex commit that +exists in this repository and is an **ancestor of the dispatch ref**, then +publishes it for the supervisor build, the Windows bootstrap and the Unix +workspace checkout. Omitting it keeps the previous behaviour of validating the +dispatch ref tip. + +The ancestry requirement is what keeps this from widening the trust boundary: +an unmerged branch, an unrelated commit, or a revision from a fork is refused, +so a protected run still only ever validates reviewed history that reached +`main`. What changes is that it no longer has to be the newest such history. + ### Unexpected installation RPC failures Protected run `34964120550` used Chat `d84195c61b86598e691ccd47163e46a15b154417` diff --git a/src/client-v1-conformance-workflow.test.ts b/src/client-v1-conformance-workflow.test.ts index ede63190..d42d8b68 100644 --- a/src/client-v1-conformance-workflow.test.ts +++ b/src/client-v1-conformance-workflow.test.ts @@ -35,6 +35,8 @@ const validatorInputExpression = '${' + '{ inputs.validator_revision }}'; const protectedValidatorExpression = '${' + '{ vars.CLIENT_V1_CONFORMANCE_VALIDATOR_REVISION }}'; const githubRepositoryExpression = '${' + '{ github.repository }}'; const githubShaExpression = '${' + '{ github.sha }}'; +const resolvedProducerExpression = '${' + "{ steps['resolve'].outputs.revision }}"; +const producerRevisionExpression = '${' + "{ needs['producer-revision'].outputs.revision }}"; const expressionOpening = '${' + '{'; const uploadedSupervisorArtifactIdExpression = '${' + "{ steps['upload-supervisor'].outputs['artifact-id'] }}"; @@ -247,7 +249,25 @@ function verifyExactMainRefConstraint(label: string, job: string): void { } } +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'); + } + if ( + workflow.includes( + 'ref: ' + githubShaExpression.replace('github.sha', 'inputs.producer_revision'), + ) + ) { + throw new Error('producer revision input is checked out without verification'); + } +} + function verifyHardenedWorkflowGraph(workflow: string): void { + const producerRevision = workflowJob(workflow, 'producer-revision'); const windowsSupervisor = workflowJob(workflow, 'windows-supervisor'); const producer = workflowJob(workflow, 'platform-conformance'); const validation = workflowJob(workflow, 'validate-conformance-artifacts'); @@ -255,6 +275,7 @@ function verifyHardenedWorkflowGraph(workflow: string): void { const aggregate = workflowJob(workflow, 'aggregate-conformance'); for (const [label, job] of [ + ['producer revision', producerRevision], ['windows supervisor', windowsSupervisor], ['producer', producer], ['validator', validation], @@ -264,7 +285,10 @@ function verifyHardenedWorkflowGraph(workflow: string): void { verifyExactMainRefConstraint(label, job); } + verifyResolvedProducerRevision(producerRevision, workflow); + for (const [label, job] of [ + ['producer revision', producerRevision], ['windows supervisor', windowsSupervisor], ['producer', producer], ['validator', validation], @@ -466,7 +490,9 @@ describe('client-v1 conformance workflow bootstrap', () => { const workflow = readFileSync(workflowPath, 'utf8'); const unixProducerCommand = readFileSync(unixProducerCommandPath, 'utf8'); - expect(workflow.match(/ {10}fetch-depth: 0/gu)).toHaveLength(3); + // 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); expect(workflow).toContain('scripts/executable-resolution.mjs'); expect(workflow).toContain('resolveExecutableInvocation'); expect(workflow).toContain(" GIT_CONFIG_COUNT: '1'"); @@ -530,10 +556,10 @@ describe('client-v1 conformance workflow bootstrap', () => { expect(workflow).toContain(' windows-supervisor:'); expect(workflow).toContain( - " windows-supervisor:\n name: build-windows-supervisor\n if: github.ref == 'refs/heads/main'", + " windows-supervisor:\n name: build-windows-supervisor\n if: github.ref == 'refs/heads/main'\n needs: producer-revision", ); expect(workflow).toContain(' runs-on: macos-latest'); - expect(workflow).toContain(' needs: windows-supervisor'); + expect(workflow).toContain(' needs: [producer-revision, windows-supervisor]'); expect(workflow).toContain(' run: bash scripts/phase1-windows-supervisor-build.sh'); expect(workflow).toContain(' name: phase1-process-supervisor-win32-x64'); expect(workflow).toContain(`artifact_id: ${uploadedSupervisorArtifactIdExpression}`); @@ -1025,7 +1051,7 @@ ${source.slice(start, end)} `OPENCOVEN_VALIDATOR_REVISION_INPUT: ${validatorInputExpression}`, ); expect(environment).toContain(`OPENCOVEN_CHAT_REPOSITORY: ${githubRepositoryExpression}`); - expect(environment).toContain(`OPENCOVEN_CHAT_SHA: ${githubShaExpression}`); + expect(environment).toContain(`OPENCOVEN_CHAT_SHA: ${producerRevisionExpression}`); expect(runBody).not.toContain(validatorInputExpression); expect(runBody).not.toContain(expressionOpening); expect(runBody).not.toMatch(/\$\{\{\s*inputs\./u); From 59b18b54a1b40bbb1170ebb3f38db118ea1f7649 Mon Sep 17 00:00:00 2001 From: Val Alexander Date: Wed, 16 Sep 2026 16:09:58 -0500 Subject: [PATCH 2/4] chore(conformance): repin the harness authority for the producer-ref 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) --- phase1-conformance.lock.json | 10 +++++----- src/phase1-conformance-lock.test.ts | 8 ++++---- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/phase1-conformance.lock.json b/phase1-conformance.lock.json index df3593b3..23d451cb 100644 --- a/phase1-conformance.lock.json +++ b/phase1-conformance.lock.json @@ -18,11 +18,11 @@ }, "harness": { "repository": "OpenCoven/chat", - "revision": "b5e0fac1d56ee2188f839e7da8fbddd3a3c2b8c2" + "revision": "bdd0dca7bf954ef3433315e3c2a5a06f3f09e967" }, "harnessAuthority": { - "revision": "b5e0fac1d56ee2188f839e7da8fbddd3a3c2b8c2", - "tree": "1642836d4bfaab3b54f93ef58aaabeb930af3d9e", + "revision": "bdd0dca7bf954ef3433315e3c2a5a06f3f09e967", + "tree": "51631f2d9906e11136beb0955d17dabf60afbb94", "files": [ { "path": "scripts/phase1-conformance.mjs", @@ -146,8 +146,8 @@ }, { "path": ".github/workflows/client-v1-conformance.yml", - "blob": "4e693a7c780265823bd041ec9f2972006bfd00b5", - "sha256": "79ba921d0bb49c2e88ac5253928e44489462fc80b2ac98b53ab9ed13533fba87" + "blob": "7d8a92fcaf07f57cff2a0dd12a4a0b04d787225c", + "sha256": "a22c99529f760247a6590c3ca4f9b205a9ac003a0536bf7a711b5a99df9c6574" } ], "productionDeltas": [ diff --git a/src/phase1-conformance-lock.test.ts b/src/phase1-conformance-lock.test.ts index a07af066..1b6d9b01 100644 --- a/src/phase1-conformance-lock.test.ts +++ b/src/phase1-conformance-lock.test.ts @@ -80,8 +80,8 @@ const committedHarnessAuthority = JSON.parse( readFileSync(resolve(projectRoot, 'phase1-conformance.lock.json'), 'utf8'), ).harnessAuthority; const expectedBehaviorAuthority = { - revision: 'b5e0fac1d56ee2188f839e7da8fbddd3a3c2b8c2', - tree: '1642836d4bfaab3b54f93ef58aaabeb930af3d9e', + revision: 'bdd0dca7bf954ef3433315e3c2a5a06f3f09e967', + tree: '51631f2d9906e11136beb0955d17dabf60afbb94', files: [ { path: 'scripts/owned-temp-directory.mjs', @@ -135,8 +135,8 @@ const expectedBehaviorAuthority = { }, { path: '.github/workflows/client-v1-conformance.yml', - blob: '4e693a7c780265823bd041ec9f2972006bfd00b5', - sha256: '79ba921d0bb49c2e88ac5253928e44489462fc80b2ac98b53ab9ed13533fba87', + blob: '7d8a92fcaf07f57cff2a0dd12a4a0b04d787225c', + sha256: 'a22c99529f760247a6590c3ca4f9b205a9ac003a0536bf7a711b5a99df9c6574', }, { path: 'scripts/process-owned-artifact-root.mjs', From 24eb2bc1aaa1ac336167d96537c8964ce81baf97 Mon Sep 17 00:00:00 2001 From: Val Alexander Date: Fri, 18 Sep 2026 00:17:54 -0500 Subject: [PATCH 3/4] test(conformance): run the producer ancestry gate against a real repository 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. --- src/client-v1-conformance-workflow.test.ts | 135 ++++++++++++++++++++- 1 file changed, 133 insertions(+), 2 deletions(-) diff --git a/src/client-v1-conformance-workflow.test.ts b/src/client-v1-conformance-workflow.test.ts index d42d8b68..0c732b2f 100644 --- a/src/client-v1-conformance-workflow.test.ts +++ b/src/client-v1-conformance-workflow.test.ts @@ -1,11 +1,12 @@ import { execFileSync, spawnSync } from 'node:child_process'; import { createHash } from 'node:crypto'; -import { existsSync, readFileSync } from 'node:fs'; +import { existsSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs'; import { createRequire } from 'node:module'; +import { tmpdir } from 'node:os'; import { dirname, relative, resolve } from 'node:path'; import { fileURLToPath } from 'node:url'; import * as ts from 'typescript'; -import { describe, expect, test } from 'vitest'; +import { afterAll, describe, expect, test } from 'vitest'; import { decodeWindowsSupervisorSource } from '../scripts/windows-supervisor-source.mjs'; const projectRoot = resolve(dirname(fileURLToPath(import.meta.url)), '..'); @@ -680,6 +681,136 @@ ${childBootstrap.slice(validationStart, validationEnd)} ); }); +function producerRevisionScript(workflow: string): string { + const job = workflowJob(workflow, 'producer-revision'); + const marker = ' run: |\n'; + const start = job.indexOf(marker); + if (start < 0) { + throw new Error('producer-revision job does not define a resolve script'); + } + const lines: string[] = []; + for (const line of job.slice(start + marker.length).split('\n')) { + if (line.trim() !== '' && !line.startsWith(' ')) break; + lines.push(line.slice(10)); + } + return lines.join('\n'); +} + +type ResolveOutcome = { status: number; revision: string; stderr: string }; + +function runProducerResolve( + script: string, + repository: string, + dispatchSha: string, + requested: string | undefined, +): ResolveOutcome { + const outputPath = resolve(repository, 'github-output'); + writeFileSync(outputPath, ''); + const result = spawnSync('bash', ['-c', script], { + cwd: repository, + encoding: 'utf8', + timeout: 20_000, + env: { + ...process.env, + GITHUB_OUTPUT: outputPath, + OPENCOVEN_DISPATCH_SHA: dispatchSha, + ...(requested === undefined ? {} : { OPENCOVEN_PRODUCER_REVISION_INPUT: requested }), + }, + }); + const emitted = readFileSync(outputPath, 'utf8'); + const match = /^revision=([0-9a-f]{40})$/mu.exec(emitted); + return { + status: result.status ?? -1, + revision: match?.[1] ?? '', + stderr: result.stderr ?? '', + }; +} + +describe('producer revision ancestry gate', () => { + const script = producerRevisionScript(readFileSync(workflowPath, 'utf8')); + const repository = mkdtempSync(resolve(tmpdir(), 'opencoven-producer-ref-')); + const git = (...args: string[]) => + execFileSync('git', args, { + cwd: repository, + encoding: 'utf8', + timeout: 20_000, + env: { + ...process.env, + GIT_AUTHOR_NAME: 'Conformance', + GIT_AUTHOR_EMAIL: 'conformance@example.invalid', + GIT_COMMITTER_NAME: 'Conformance', + GIT_COMMITTER_EMAIL: 'conformance@example.invalid', + }, + }).trim(); + + git('init', '--quiet', '--initial-branch=main', '.'); + git('-c', 'commit.gpgsign=false', 'commit', '--quiet', '--allow-empty', '-m', 'base'); + const merged = git('rev-parse', 'HEAD'); + git('-c', 'commit.gpgsign=false', 'commit', '--quiet', '--allow-empty', '-m', 'tip'); + const tip = git('rev-parse', 'HEAD'); + git('checkout', '--quiet', '-b', 'unmerged', merged); + git('-c', 'commit.gpgsign=false', 'commit', '--quiet', '--allow-empty', '-m', 'unmerged'); + const unmerged = git('rev-parse', 'HEAD'); + git('checkout', '--quiet', 'main'); + + afterAll(() => { + rmSync(repository, { force: true, recursive: true }); + }); + + test('defaults to the dispatch tip when no revision is requested', () => { + const outcome = runProducerResolve(script, repository, tip, undefined); + expect(outcome.status).toBe(0); + expect(outcome.revision).toBe(tip); + }); + + test('accepts an exact merged ancestor of the dispatch tip', () => { + const outcome = runProducerResolve(script, repository, tip, merged); + expect(outcome.status).toBe(0); + expect(outcome.revision).toBe(merged); + }); + + test('accepts the dispatch tip named explicitly', () => { + const outcome = runProducerResolve(script, repository, tip, tip); + expect(outcome.status).toBe(0); + expect(outcome.revision).toBe(tip); + }); + + test('refuses a commit that is not merged into the dispatch tip', () => { + const outcome = runProducerResolve(script, repository, tip, unmerged); + expect(outcome.status).toBe(1); + expect(outcome.revision).toBe(''); + expect(outcome.stderr).toContain('only merged revisions may be validated'); + }); + + test('refuses a descendant of the dispatch tip', () => { + const outcome = runProducerResolve(script, repository, merged, tip); + expect(outcome.status).toBe(1); + expect(outcome.revision).toBe(''); + expect(outcome.stderr).toContain('only merged revisions may be validated'); + }); + + test.each([ + ['an abbreviated revision', (value: string) => value.slice(0, 12)], + // Fixed rather than derived: an all-digit fixture SHA would upper-case to + // itself and silently stop exercising the lowercase rule. + ['an uppercase revision', () => 'ABCDEF' + '0'.repeat(34)], + ['a ref name rather than a commit', () => 'main'], + ['an empty revision', () => ' '], + ])('refuses %s', (_label, transform) => { + const outcome = runProducerResolve(script, repository, tip, transform(merged)); + expect(outcome.status).toBe(1); + expect(outcome.revision).toBe(''); + expect(outcome.stderr).toContain('exact lowercase 40-hex commit'); + }); + + test('refuses a well-formed revision that is absent from the repository', () => { + const outcome = runProducerResolve(script, repository, tip, 'b'.repeat(40)); + expect(outcome.status).toBe(1); + expect(outcome.revision).toBe(''); + expect(outcome.stderr).toContain('not a commit in this repository'); + }); +}); + describe.skipIf(!validatorAvailable)('protected client-v1 conformance workflow', () => { test('accepts the committed protected workflow with the current SDK validator', async () => { const fixture = await workflowFixture(); From 96dd7f0dcb9bb034fe5c0b3f4612e00a50084fa4 Mon Sep 17 00:00:00 2001 From: Val Alexander Date: Fri, 18 Sep 2026 22:11:07 -0500 Subject: [PATCH 4/4] chore(conformance): bind the merged producer-ref dispatch source 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 --- docs/phase1-conformance.md | 4 ++-- phase1-conformance.lock.json | 6 +++--- src/phase1-conformance-lock.test.ts | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/phase1-conformance.md b/docs/phase1-conformance.md index 06a8e8ac..db962baf 100644 --- a/docs/phase1-conformance.md +++ b/docs/phase1-conformance.md @@ -214,8 +214,8 @@ assignment, RPC decoder and primary/secondary exception pipeline. This fixture correction does not establish the protected installation failure's cause or relax the round-trip assertions. -The lock now selects reviewed source `b5e0fac1d56ee2188f839e7da8fbddd3a3c2b8c2`, -tree `1642836d4bfaab3b54f93ef58aaabeb930af3d9e`, including all 25 governed files +The lock now selects reviewed source `28821d4d035d7815df396b756c2e30ecb0d54f5e`, +tree `f893c71be4fd0eec23c8739716441bd2f533d764`, including all 25 governed files and ten production deltas. The checkout regression exercises all five labels from that immutable revision. SDK rebinding, both scope rotations and fresh protected validation remain required; this binding alone is not acceptance. diff --git a/phase1-conformance.lock.json b/phase1-conformance.lock.json index 49844909..18f9551b 100644 --- a/phase1-conformance.lock.json +++ b/phase1-conformance.lock.json @@ -18,11 +18,11 @@ }, "harness": { "repository": "OpenCoven/chat", - "revision": "bdd0dca7bf954ef3433315e3c2a5a06f3f09e967" + "revision": "28821d4d035d7815df396b756c2e30ecb0d54f5e" }, "harnessAuthority": { - "revision": "bdd0dca7bf954ef3433315e3c2a5a06f3f09e967", - "tree": "51631f2d9906e11136beb0955d17dabf60afbb94", + "revision": "28821d4d035d7815df396b756c2e30ecb0d54f5e", + "tree": "f893c71be4fd0eec23c8739716441bd2f533d764", "files": [ { "path": "scripts/phase1-conformance.mjs", diff --git a/src/phase1-conformance-lock.test.ts b/src/phase1-conformance-lock.test.ts index 81df94bc..28ad40b2 100644 --- a/src/phase1-conformance-lock.test.ts +++ b/src/phase1-conformance-lock.test.ts @@ -80,8 +80,8 @@ const committedHarnessAuthority = JSON.parse( readFileSync(resolve(projectRoot, 'phase1-conformance.lock.json'), 'utf8'), ).harnessAuthority; const expectedBehaviorAuthority = { - revision: 'bdd0dca7bf954ef3433315e3c2a5a06f3f09e967', - tree: '51631f2d9906e11136beb0955d17dabf60afbb94', + revision: '28821d4d035d7815df396b756c2e30ecb0d54f5e', + tree: 'f893c71be4fd0eec23c8739716441bd2f533d764', files: [ { path: 'scripts/owned-temp-directory.mjs',