diff --git a/.github/scripts/check-workflow-policy.mjs b/.github/scripts/check-workflow-policy.mjs index ceded0134..7b383e0a9 100644 --- a/.github/scripts/check-workflow-policy.mjs +++ b/.github/scripts/check-workflow-policy.mjs @@ -993,11 +993,13 @@ const packagedPlatformWorkflowDigest = // made advisory, parked in dead code, or followed by a payload substitution // while leaving the expected tokens in place. const packagedPlatformCoordinatorWorkflowDigest = - "29fdda15a93e6cf4526588bad2c746ef8e13afb58e74a7b2b44d9b1a656eb549"; + "797fa9e2be359f83eacd45b78722829d1f277efd2e721de1c9bf8b590b73dc58"; +const releaseSourceProofSentinelDigest = + "91ee8bc1a6a055e9297e81747c37d167b123d0a2e5dc60d5c6e2bdcfbef9c351"; const frozenCandidateQualityWorkflowDigest = "92d0a7ab0e0df63dacd5cc3ef0b58500a6578036494c329aa35279048734f173"; const macosMetalWorkflowDigest = - "05b69d48238284b47b40c13bf15eb1f31370dea55bb77169553d41b46fda1a7f"; + "55581330f6a035b84e1224dbd5469d812ab2fa444914157e22a39cccc64f4627"; const windowsVulkanWorkflowDigest = "c2272dbf4c550ba4a21372e772a87f6df3307f5f4f709b216473f85958157ffe"; const linuxVulkanWorkflowDigest = @@ -2890,9 +2892,33 @@ function validateReleaseCoordinator(workflows, violations, graph) { ); const source = requireJob(violations, releaseFile, release, "source-proof"); - add(violations, source.uses === "./.github/workflows/source-proof.yml", `${releaseFile} must call exact source proof`); + add( + violations, + createHash("sha256").update(JSON.stringify(source)).digest("hex") + === releaseSourceProofSentinelDigest, + `${releaseFile} source proof placeholder must match the reviewed fail-closed sentinel`, + ); + add( + violations, + source.uses === undefined + && source["runs-on"] === "ubuntu-latest" + && source["timeout-minutes"] === 1 + && permissionMapMatches(source.permissions, {}) + && object(source.env).SOURCE_SHA === "${{ github.sha }}", + `${releaseFile} source proof placeholder must fail closed without calling the broad source workflow`, + ); add(violations, sameMembers(needs(source), releaseChain.dependencies["source-proof"]), `${releaseFile} source proof dependencies must match the release claim graph`); - add(violations, object(source.with).ref === "${{ github.sha }}", `${releaseFile} source proof must receive the exact release SHA`); + requireStepRun( + violations, + releaseFile, + source, + "Refuse a second source proof", + [ + 'test "$SOURCE_SHA" = "$GITHUB_SHA"', + "Preflight did not resolve reusable exact-head source proof", + "exit 1", + ], + ); // Reuse is admissible only through the authenticated closeout binding, never by simply // dropping the gate: the job may be skipped, and only when preflight resolved reusable // evidence for this exact tree. @@ -2935,10 +2961,10 @@ function validateReleaseCoordinator(workflows, violations, graph) { ); add( violations, - object(source.with).version === "${{ needs.preflight.outputs.version }}" - && object(source.with).freeze_receipt_digest === "" - && object(source.with).emit_release_cells === undefined, - `${releaseFile} unreachable source fallback must fail closed without a post-calibration freeze`, + source.with === undefined + && source.uses === undefined + && list(source.steps).length === 1, + `${releaseFile} unreachable source fallback must remain a one-step fail-closed sentinel`, ); const packaged = requireJob(violations, releaseFile, release, "packaged-proof"); @@ -6414,9 +6440,24 @@ function validateRemainingWorkflows(workflows, violations) { job, "${{ !inputs.calibration_mode && !inputs.server_behavior_only }}", ); + const calibrationPreflightName = "Validate unfrozen Metal calibration source"; + const calibrationPreflight = namedStep(job, calibrationPreflightName); + add( + violations, + calibrationPreflight?.if === "inputs.calibration_mode" + && calibrationPreflight?.shell === "bash" + && calibrationPreflight?.["continue-on-error"] === undefined + && stepRun(job, calibrationPreflightName).trim() === [ + "set -euo pipefail", + 'test "$(jq -r .status crates/codestory-llama-sys/per-user-embedding-server-constant-set.json)" = unfrozen', + 'test "$(jq -r .freeze_record crates/codestory-llama-sys/per-user-embedding-server-constant-set.json)" = null', + ].join("\n") + && stepIndex(job, calibrationPreflightName) + === stepIndex(job, "Checkout") + 1, + `${metalFile} must reject a frozen or stale calibration source immediately after checkout and before setup or compilation`, + ); const calibrationStepName = "Collect three independent Metal constant calibration runs"; requireStepRun(violations, metalFile, job, calibrationStepName, [ - 'test "$(jq -r .status crates/codestory-llama-sys/per-user-embedding-server-constant-set.json)" = unfrozen', "--proof-tier calibration", "--engine-policy accelerated", "--expected-backend Metal", @@ -7951,6 +7992,60 @@ function permissionMapMatches(actualValue, expectedValue) { && Object.entries(expected).every(([key, value]) => actual[key] === value); } +function reusableWorkflowPermissionViolations(workflows) { + const violations = []; + const permissionRank = value => ( + value === "write" ? 2 : value === "read" ? 1 : 0 + ); + const permissionRequests = value => { + if (value === "write-all") return [["*", "write"]]; + if (value === "read-all") return [["*", "read"]]; + return Object.entries(object(value)); + }; + const permissionGrant = (value, scope) => { + if (value === "write-all") return { rank: 2, label: "write-all" }; + if (value === "read-all") return { rank: 1, label: "read-all" }; + const granted = object(value)[scope]; + return { rank: permissionRank(granted), label: granted ?? "none" }; + }; + const localWorkflow = /^\.\/\.github\/workflows\/([^/]+\.ya?ml)$/u; + + for (const [callerFile, callerWorkflow] of workflows) { + for (const [jobName, jobValue] of Object.entries(object(callerWorkflow.jobs))) { + const job = object(jobValue); + const match = String(job.uses ?? "").match(localWorkflow); + if (!match) continue; + const calleeFile = match[1]; + const callee = workflows.get(calleeFile); + if (!callee) { + violations.push( + `[reusable_permissions] ${callerFile} job ${jobName} calls missing local workflow ${calleeFile}`, + ); + continue; + } + const callerPermissions = job.permissions === undefined + ? callerWorkflow.permissions + : job.permissions; + for (const [calleeJobName, calleeJobValue] of Object.entries(object(callee.jobs))) { + const calleeJob = object(calleeJobValue); + const requestedPermissions = calleeJob.permissions === undefined + ? callee.permissions + : calleeJob.permissions; + for (const [scope, requested] of permissionRequests(requestedPermissions)) { + const granted = permissionGrant(callerPermissions, scope); + add( + violations, + granted.rank >= permissionRank(requested), + `[reusable_permissions] ${callerFile} job ${jobName} grants ${scope}: ${granted.label} but ${calleeFile} job ${calleeJobName} requests ${requested}`, + ); + } + } + } + } + + return violations; +} + function findNamedStep(workflow, name) { for (const job of Object.values(object(workflow.jobs))) { const found = namedStep(job, name); @@ -8527,7 +8622,7 @@ export function releaseFreezeBarrierWorkflowViolations( add( violations, object(workflow.permissions).statuses === "read", - "[freeze_barrier] packaged-platform-pr.yml must authenticate the exact-head freeze status", + "[freeze_barrier] packaged-platform-pr.yml must authenticate the exact-head freeze status without broad workflow authority", ); } add( @@ -8735,13 +8830,16 @@ export function releaseFreezeBarrierWorkflowViolations( sourceWorkflow, acceptance.windows_job, ); + const windowsProbePowerShell + = `powershell -NoLogo -NoProfile -NonInteractive -ExecutionPolicy Bypass -Command ". '{0}'"`; add( violations, windowsJob.if === "inputs.acceptance_only" && sameMembers(needs(windowsJob), ["resolve"]) && sameMembers(list(windowsJob["runs-on"]), list(acceptance.windows_runner)) && windowsJob["timeout-minutes"] === 5 - && namedStep(windowsJob, acceptance.windows_step)?.shell === "pwsh" + && namedStep(windowsJob, acceptance.windows_step)?.shell + === windowsProbePowerShell && namedStep(windowsJob, acceptance.windows_step)?.["continue-on-error"] !== true, "[freeze_barrier] source acceptance must execute the protected blocking Windows native probe", ); @@ -8754,14 +8852,28 @@ export function releaseFreezeBarrierWorkflowViolations( "cargo new --quiet --bin", "cargo build --release --quiet", "node --test .github/scripts/cargo-build-artifacts.test.mjs", + "const [root, deps] = process.argv.slice(2);", "left.dev !== right.dev", "left.ino !== right.ino", "left.nlink !== 2n", "right.nlink !== 2n", + '$identityScriptPath = Join-Path $probeRoot "verify-hardlink-identity.cjs"', + "Set-Content -LiteralPath $identityScriptPath -Value $identityScript -Encoding UTF8", + "node $identityScriptPath $rootExe $depsExe", "Elapsed.TotalSeconds -ge 90", "Remove-Item -LiteralPath $probeRoot -Recurse -Force", ], ); + forbidStepRun( + violations, + "source-proof.yml", + windowsJob, + acceptance.windows_step, + [ + "node -e $identityScript", + "process.argv.slice(1)", + ], + ); const publisherJob = requireJob( violations, @@ -8894,6 +9006,22 @@ export function releaseFreezeBarrierWorkflowViolations( '.name == "full-source-gate" and .conclusion == "success"', ], ); + const packagedSourceJob = requireJob( + violations, + "packaged-platform-pr.yml", + coordinator, + "source-proof", + ); + add( + violations, + permissionMapMatches(packagedSourceJob.permissions, { + actions: "write", + contents: "read", + "pull-requests": "read", + statuses: "write", + }), + "[freeze_barrier] packaged source-proof call must grant exactly the reusable workflow permissions", + ); const release = workflows.get("release.yml"); const auto = workflows.get("auto-release.yml"); @@ -8942,7 +9070,9 @@ export function releaseFreezeBarrierWorkflowViolations( sourceJob.if === "needs.preflight.outputs.source_proof_reused != 'true'" && object(preflight.outputs).source_proof_reused === "${{ steps.reuse.outputs.source_proof_reused }}" - && object(sourceJob.with).freeze_receipt_digest === "", + && sourceJob.uses === undefined + && sourceJob.with === undefined + && namedStep(sourceJob, "Refuse a second source proof") !== undefined, "[freeze_barrier] release must make the post-calibration source-proof fallback unreachable", ); @@ -9038,9 +9168,13 @@ export function releaseWorkflowContractViolations( const release = workflows.get("release.yml"); for (const jobName of policy.release_chain.exact_sha_jobs) { + const job = object(at(release, "jobs", jobName)); + const exactSha = jobName === "source-proof" && job.uses === undefined + ? object(job.env).SOURCE_SHA + : object(job.with).ref; add( violations, - object(at(release, "jobs", jobName, "with")).ref === policy.promotion.exact_sha_expression, + exactSha === policy.promotion.exact_sha_expression, `[exact_sha] release.yml job ${jobName} must receive ${policy.promotion.exact_sha_expression}`, ); } @@ -10242,6 +10376,7 @@ export function validateWorkflows(workflows, graph = loadReleaseClaimGraph(repos for (const [file, workflow] of workflows) { violations.push(...basicWorkflowViolations(file, workflow)); } + violations.push(...reusableWorkflowPermissionViolations(workflows)); validateCargoTestFilters(workflows, violations); validatePluginRelease(workflows, violations, graph); validateMarketplaceSync(workflows, violations); diff --git a/.github/scripts/check-workflow-policy.test.mjs b/.github/scripts/check-workflow-policy.test.mjs index aee6de8ff..494567a4f 100644 --- a/.github/scripts/check-workflow-policy.test.mjs +++ b/.github/scripts/check-workflow-policy.test.mjs @@ -673,6 +673,10 @@ test("constant calibration structure rejects qualification, 3x3 sampling, repeat workflow.jobs["packaged-metal"], "Collect three independent Metal constant calibration runs", ); + const metalPreflight = workflow => draftStep( + workflow.jobs["packaged-metal"], + "Validate unfrozen Metal calibration source", + ); const nativeBuild = workflow => draftStep( workflow.jobs["packaged-metal"], "Build and package native CLI", @@ -696,6 +700,33 @@ test("constant calibration structure rejects qualification, 3x3 sampling, repeat ); const mutations = [ + ["frozen calibration source is checked after compilation", metalFile, workflow => { + const job = workflow.jobs["packaged-metal"]; + const [preflight] = job.steps.splice( + job.steps.findIndex(step => step.name === "Validate unfrozen Metal calibration source"), + 1, + ); + job.steps.splice( + job.steps.findIndex(step => step.name === "Build and package native CLI") + 1, + 0, + preflight, + ); + }, /reject a frozen or stale calibration source immediately after checkout/u], + ["frozen calibration source preflight becomes advisory", metalFile, workflow => { + metalPreflight(workflow)["continue-on-error"] = true; + }, /reject a frozen or stale calibration source immediately after checkout/u], + ["frozen calibration source preflight checks a copy", metalFile, workflow => { + metalPreflight(workflow).run = metalPreflight(workflow).run.replace( + "crates/codestory-llama-sys/per-user-embedding-server-constant-set.json", + "target/per-user-embedding-server-constant-set.json", + ); + }, /reject a frozen or stale calibration source immediately after checkout/u], + ["stale freeze record preflight is removed", metalFile, workflow => { + metalPreflight(workflow).run = metalPreflight(workflow).run + .split("\n") + .filter(line => !line.includes(".freeze_record")) + .join("\n"); + }, /reject a frozen or stale calibration source immediately after checkout/u], ["qualification scenario enters calibration", metalFile, workflow => { collector(workflow).run += "\n--qualification-scenario lifecycle"; }, /without full qualification or nested sampling/u], @@ -2211,6 +2242,43 @@ jobs: ); }); +test("local reusable workflow callers grant every permission requested by the callee", async (t) => { + assert.deepEqual(validateWorkflows(loadWorkflows()), []); + const cases = [ + ["packaged source caller downgrades status authority", workflows => { + workflows.get("packaged-platform-pr.yml").jobs["source-proof"] + .permissions.statuses = "read"; + }, /packaged-platform-pr\.yml job source-proof grants statuses: read but source-proof\.yml job .+ requests write/u], + ["packaged source caller drops its job permission boundary", workflows => { + delete workflows.get("packaged-platform-pr.yml").jobs["source-proof"].permissions; + }, /packaged-platform-pr\.yml job source-proof grants statuses: read but source-proof\.yml job .+ requests write/u], + ["automatic release caller downgrades Actions authority", workflows => { + workflows.get("auto-release.yml").jobs.release.permissions.actions = "read"; + }, /auto-release\.yml job release grants actions: read but release\.yml job .+ requests write/u], + ["plugin release caller cannot fund its publish job", workflows => { + workflows.get("auto-release.yml").jobs["plugin-release"].permissions.contents = "read"; + }, /auto-release\.yml job plugin-release grants contents: read but plugin-release\.yml job publish requests write/u], + ["release restores the invalid broad source call", workflows => { + workflows.get("release.yml").jobs["source-proof"].uses + = "./.github/workflows/source-proof.yml"; + }, /release\.yml job source-proof grants statuses: none but source-proof\.yml job .+ requests write/u], + ["caller scalar read-all cannot fund a write job", workflows => { + workflows.get("auto-release.yml").jobs["plugin-release"].permissions = "read-all"; + }, /auto-release\.yml job plugin-release grants contents: read-all but plugin-release\.yml job publish requests write/u], + ["callee scalar write-all cannot hide from the caller check", workflows => { + workflows.get("plugin-release.yml").jobs.publish.permissions = "write-all"; + }, /auto-release\.yml job plugin-release grants \*: none but plugin-release\.yml job publish requests write/u], + ]; + + for (const [name, mutate, expected] of cases) { + await t.test(name, () => { + const workflows = loadWorkflows(); + mutate(workflows); + assert.match(validateWorkflows(workflows).join("\n"), expected); + }); + } +}); + test("cargo test filters must select at least one real test", () => { const identifiers = new Map([["demo-crate", "/unused"]]); const known = new Set(["tests", "demo_tests", "full_publication_survives_restart"]); @@ -2842,13 +2910,13 @@ test("exact proof policy rejects trigger and identity downgrades", async (t) => ["Metal calibration reads the calibration contract from an unpinned location", metalProofFile, workflow => { const step = draftStep( workflow.jobs["packaged-metal"], - "Collect three independent Metal constant calibration runs", + "Validate unfrozen Metal calibration source", ); step.run = step.run.replaceAll( "crates/codestory-llama-sys/per-user-embedding-server-constant-set.json", "per-user-embedding-server-constant-set.json", ); - }, /Collect three independent Metal constant calibration runs must run test "\$\(jq -r \.status crates\/codestory-llama-sys\/per-user-embedding-server-constant-set\.json\)"/u], + }, /reject a frozen or stale calibration source immediately after checkout/u], ["Vulkan model preparation drops the bypass shell", windowsVulkanFile, workflow => { delete draftStep(workflow.jobs["packaged-vulkan"], "Prepare checksum-pinned embedded model").shell; }, /Prepare checksum-pinned embedded model must declare the bypass shell/u], @@ -3207,6 +3275,60 @@ test("release freeze barrier rejects every broad-proof bypass", async (t) => { workflows.get("source-proof.yml").jobs["freeze-windows-native-probe"]["runs-on"] = ["self-hosted", "Windows", "X64"]; }, /protected blocking Windows native probe/u], + ["Windows probe restores unavailable PowerShell Core", workflows => { + const step = draftStep( + workflows.get("source-proof.yml").jobs["freeze-windows-native-probe"], + "Run exact-head Windows native probe", + ); + step.shell = "pwsh"; + }, /protected blocking Windows native probe/u], + ["Windows probe restores inline JavaScript", workflows => { + const step = draftStep( + workflows.get("source-proof.yml").jobs["freeze-windows-native-probe"], + "Run exact-head Windows native probe", + ); + step.run = step.run.replace( + "node $identityScriptPath $rootExe $depsExe", + "node -e $identityScript $rootExe $depsExe", + ); + }, /Run exact-head Windows native probe/u], + ["Windows probe loses the literal owned path write", workflows => { + const step = draftStep( + workflows.get("source-proof.yml").jobs["freeze-windows-native-probe"], + "Run exact-head Windows native probe", + ); + step.run = step.run.replace( + "Set-Content -LiteralPath $identityScriptPath", + "Set-Content -Path $identityScriptPath", + ); + }, /Run exact-head Windows native probe/u], + ["Windows probe loses explicit UTF-8 encoding", workflows => { + const step = draftStep( + workflows.get("source-proof.yml").jobs["freeze-windows-native-probe"], + "Run exact-head Windows native probe", + ); + step.run = step.run.replace(" -Encoding UTF8", ""); + }, /Run exact-head Windows native probe/u], + ["Windows probe writes the script to a stale fixed path", workflows => { + const step = draftStep( + workflows.get("source-proof.yml").jobs["freeze-windows-native-probe"], + "Run exact-head Windows native probe", + ); + step.run = step.run.replace( + '$identityScriptPath = Join-Path $probeRoot "verify-hardlink-identity.cjs"', + '$identityScriptPath = "C:\\Temp\\verify-hardlink-identity.cjs"', + ); + }, /Run exact-head Windows native probe/u], + ["Windows probe restores inline argv indexing", workflows => { + const step = draftStep( + workflows.get("source-proof.yml").jobs["freeze-windows-native-probe"], + "Run exact-head Windows native probe", + ); + step.run = step.run.replace( + "process.argv.slice(2)", + "process.argv.slice(1)", + ); + }, /Run exact-head Windows native probe/u], ["Windows probe restores a full build", workflows => { const step = draftStep( workflows.get("source-proof.yml").jobs["freeze-windows-native-probe"], @@ -3283,9 +3405,10 @@ test("release freeze barrier rejects every broad-proof bypass", async (t) => { workflows.get("packaged-platform-pr.yml").on.workflow_dispatch .inputs.freeze_receipt_digest.default = ""; }, /packaged proof must require an exact-head freeze digest/u], - ["platform cannot read freeze status", workflows => { - delete workflows.get("packaged-platform-pr.yml").permissions.statuses; - }, /must authenticate the exact-head freeze status/u], + ["platform downgrades reusable source status authority", workflows => { + workflows.get("packaged-platform-pr.yml").jobs["source-proof"] + .permissions.statuses = "read"; + }, /packaged source-proof call must grant exactly the reusable workflow permissions/u], ["qualification bypasses its exact-head freeze status", workflows => { draftStep( workflows.get("packaged-platform-pr.yml").jobs.route, @@ -3359,10 +3482,43 @@ test("release freeze barrier rejects every broad-proof bypass", async (t) => { ); step.run += "\nnode .github/scripts/release-freeze-barrier.mjs verify-status\n"; }, /Resolve reusable prior evidence must not run release-freeze-barrier\.mjs verify-status/u], - ["release placeholder propagates a post-calibration receipt", workflows => { - workflows.get("release.yml").jobs["source-proof"].with.freeze_receipt_digest - = "${{ inputs.freeze_receipt_digest }}"; - }, /unreachable source fallback must fail closed without a post-calibration freeze/u], + ["release placeholder calls the broad source workflow", workflows => { + const job = workflows.get("release.yml").jobs["source-proof"]; + delete job["runs-on"]; + delete job["timeout-minutes"]; + delete job.permissions; + delete job.env; + delete job.steps; + job.uses = "./.github/workflows/source-proof.yml"; + job.with = { + ref: "${{ github.sha }}", + proof_key: "release-${{ needs.preflight.outputs.version }}", + version: "${{ needs.preflight.outputs.version }}", + freeze_receipt_digest: "", + }; + }, /source proof placeholder must fail closed without calling the broad source workflow/u], + ["release placeholder loses its hard failure", workflows => { + draftStep( + workflows.get("release.yml").jobs["source-proof"], + "Refuse a second source proof", + ).run = "exit 0"; + }, /Refuse a second source proof/u], + ["release placeholder parks its failure behind a false step", workflows => { + draftStep( + workflows.get("release.yml").jobs["source-proof"], + "Refuse a second source proof", + ).if = "${{ false }}"; + }, /source proof placeholder must match the reviewed fail-closed sentinel/u], + ["release placeholder exits successfully before its failure", workflows => { + const step = draftStep( + workflows.get("release.yml").jobs["source-proof"], + "Refuse a second source proof", + ); + step.run = `exit 0\n${step.run}`; + }, /source proof placeholder must match the reviewed fail-closed sentinel/u], + ["release placeholder makes job failure advisory", workflows => { + workflows.get("release.yml").jobs["source-proof"]["continue-on-error"] = true; + }, /source proof placeholder must match the reviewed fail-closed sentinel/u], ["release stops cancelling superseded work", workflows => { workflows.get("release.yml").concurrency["cancel-in-progress"] = false; }, /release and auto-release must cancel superseded work/u], diff --git a/.github/scripts/fixtures/workflow-policy-invalid.json b/.github/scripts/fixtures/workflow-policy-invalid.json index 35685c8ea..222bb4791 100644 --- a/.github/scripts/fixtures/workflow-policy-invalid.json +++ b/.github/scripts/fixtures/workflow-policy-invalid.json @@ -64,8 +64,8 @@ "workflow": "release.yml", "job": "source-proof", "field": [ - "with", - "ref" + "env", + "SOURCE_SHA" ], "value": "${{ github.ref }}" }, diff --git a/.github/scripts/release-freeze-acceptance-jobs.json b/.github/scripts/release-freeze-acceptance-jobs.json index e99d402a7..a2c0c005b 100644 --- a/.github/scripts/release-freeze-acceptance-jobs.json +++ b/.github/scripts/release-freeze-acceptance-jobs.json @@ -5,7 +5,7 @@ "jobs": { "resolve": "da6c955c944644cd714728bf67d43aabd4ad049d5fde943ce7b1739f3d7cd8e5", "freeze-hostile-mutations": "ebc27d28a1c087f848be090d2a2a458acee0177f06048c4d357e0724cf38be1a", - "freeze-windows-native-probe": "252f90a48322275128f47c58f48895a0be25909e323ae7e049ddaff015bf2299", + "freeze-windows-native-probe": "03fc4c0ae9758564ea2d8588d6661c393e88939cf3a7b229448bbfcfc5e11457", "freeze-acceptance": "544688894a77c9f95ef5799e3070ec3bea4e199b5ed650f9aa62d35a54e83ca4" } } diff --git a/.github/workflows/macos-metal-proof.yml b/.github/workflows/macos-metal-proof.yml index 6a865193b..aa01ba7fe 100644 --- a/.github/workflows/macos-metal-proof.yml +++ b/.github/workflows/macos-metal-proof.yml @@ -79,6 +79,14 @@ jobs: ref: ${{ inputs.ref || github.sha }} fetch-depth: 0 + - name: Validate unfrozen Metal calibration source + if: inputs.calibration_mode + shell: bash + run: | + set -euo pipefail + test "$(jq -r .status crates/codestory-llama-sys/per-user-embedding-server-constant-set.json)" = unfrozen + test "$(jq -r .freeze_record crates/codestory-llama-sys/per-user-embedding-server-constant-set.json)" = null + - name: Capture host evidence shell: bash run: | @@ -504,7 +512,6 @@ jobs: CODESTORY_EMBED_ALLOW_CPU: "0" run: | set -euo pipefail - test "$(jq -r .status crates/codestory-llama-sys/per-user-embedding-server-constant-set.json)" = unfrozen version="${VERSION#v}" source_sha="$(git rev-parse HEAD)" source_tree="$(git rev-parse 'HEAD^{tree}')" diff --git a/.github/workflows/packaged-platform-pr.yml b/.github/workflows/packaged-platform-pr.yml index 3247d71fd..630bcad78 100644 --- a/.github/workflows/packaged-platform-pr.yml +++ b/.github/workflows/packaged-platform-pr.yml @@ -420,6 +420,11 @@ jobs: source-proof: if: needs.route.outputs.mode == 'integration' needs: route + permissions: + actions: write + contents: read + pull-requests: read + statuses: write uses: ./.github/workflows/source-proof.yml with: ref: ${{ needs.route.outputs.head_sha }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 7145cd62b..43524d429 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -311,17 +311,22 @@ jobs: source-proof: needs: preflight - # Preflight fails unless the one frozen-candidate source proof is reusable. This job is a - # structural DAG placeholder for closeout compatibility and must remain unreachable. + # Preflight fails unless the one frozen-candidate source proof is reusable. Keep this + # structural DAG placeholder fail-closed without calling the broad source workflow again. if: needs.preflight.outputs.source_proof_reused != 'true' - uses: ./.github/workflows/source-proof.yml - with: - ref: ${{ github.sha }} - proof_key: release-${{ needs.preflight.outputs.version }} - version: ${{ needs.preflight.outputs.version }} - # This job is deliberately unreachable. An empty digest makes any policy - # regression that reaches it fail before starting a second broad proof. - freeze_receipt_digest: "" + runs-on: ubuntu-latest + timeout-minutes: 1 + permissions: {} + env: + SOURCE_SHA: ${{ github.sha }} + steps: + - name: Refuse a second source proof + shell: bash + run: | + set -euo pipefail + test "$SOURCE_SHA" = "$GITHUB_SHA" + echo "::error::Preflight did not resolve reusable exact-head source proof; refusing to start a second broad proof." + exit 1 packaged-proof: needs: preflight diff --git a/.github/workflows/source-proof.yml b/.github/workflows/source-proof.yml index e764b798f..c2d988bd9 100644 --- a/.github/workflows/source-proof.yml +++ b/.github/workflows/source-proof.yml @@ -325,7 +325,7 @@ jobs: ref: ${{ needs.resolve.outputs.ref }} - name: Run exact-head Windows native probe - shell: pwsh + shell: powershell -NoLogo -NoProfile -NonInteractive -ExecutionPolicy Bypass -Command ". '{0}'" run: | $ErrorActionPreference = "Stop" $probeRoot = Join-Path $env:RUNNER_TEMP ( @@ -363,7 +363,7 @@ jobs: $depsExe = Join-Path $probeRoot "target/release/deps/cargo_hardlink_probe.exe" $identityScript = @' const fs = require("node:fs"); - const [root, deps] = process.argv.slice(1); + const [root, deps] = process.argv.slice(2); const left = fs.statSync(root, { bigint: true }); const right = fs.statSync(deps, { bigint: true }); if ( @@ -380,7 +380,9 @@ jobs: nlink: String(left.nlink), })); '@ - node -e $identityScript $rootExe $depsExe + $identityScriptPath = Join-Path $probeRoot "verify-hardlink-identity.cjs" + Set-Content -LiteralPath $identityScriptPath -Value $identityScript -Encoding UTF8 + node $identityScriptPath $rootExe $depsExe if ($LASTEXITCODE -ne 0) { throw "Cargo native hardlink identity probe failed" } diff --git a/benchmarks/release-evidence/fixtures/candidate.json b/benchmarks/release-evidence/fixtures/candidate.json index 28f6da4e3..2eb0115f1 100644 --- a/benchmarks/release-evidence/fixtures/candidate.json +++ b/benchmarks/release-evidence/fixtures/candidate.json @@ -62,7 +62,7 @@ }, "release_claims": { "graph_schema": "codestory.release-claims/v1", - "graph_sha256": "9df8f932d66a60baa104bf61252ab318b0c1325740a0edf193463618e0946348", + "graph_sha256": "269b011b112506297d565d734baa2a08e0f31a2bce79037c6c61708abf9a52c9", "observed_at": "2026-07-21T02:13:20.738Z", "expires_at": "2026-07-22T02:13:20.738Z", "requested_claims": [ @@ -85,7 +85,7 @@ "type": "performance", "tier": "live_behavior", "status": "measured", - "graph_sha256": "9df8f932d66a60baa104bf61252ab318b0c1325740a0edf193463618e0946348", + "graph_sha256": "269b011b112506297d565d734baa2a08e0f31a2bce79037c6c61708abf9a52c9", "observed_at": "2026-07-21T02:13:20.738Z", "expires_at": "2026-07-22T02:13:20.738Z", "identity": { @@ -106,7 +106,7 @@ "type": "answer_quality", "tier": "answer_quality", "status": "pass", - "graph_sha256": "9df8f932d66a60baa104bf61252ab318b0c1325740a0edf193463618e0946348", + "graph_sha256": "269b011b112506297d565d734baa2a08e0f31a2bce79037c6c61708abf9a52c9", "observed_at": "2026-07-21T02:13:20.738Z", "expires_at": "2026-07-22T02:13:20.738Z", "identity": { diff --git a/benchmarks/release-evidence/fixtures/report.json b/benchmarks/release-evidence/fixtures/report.json index ccd2f3014..cb65506bd 100644 --- a/benchmarks/release-evidence/fixtures/report.json +++ b/benchmarks/release-evidence/fixtures/report.json @@ -7,7 +7,7 @@ "baseline_id": "ci-contract-v1@1111111111111111111111111111111111111111", "baseline_sha256": "0bbbe6dd8b4000151edf7b1270959d08e94e08db876e7f2372b25613e0f237c1", "candidate_path": "benchmarks/release-evidence/fixtures/candidate.json", - "candidate_sha256": "5302ac5856891447183a0c0e7c7df09cfc5ad425efb1b834b2b439364870f438", + "candidate_sha256": "35837d94ed1f96016acfc82ad4dfcf7003ba7b840736de06001fc16138b518ab", "artifact_paths": [ { "path": "candidate-stats.json", @@ -26,7 +26,7 @@ "type": "performance", "tier": "live_behavior", "status": "pass", - "graph_sha256": "9df8f932d66a60baa104bf61252ab318b0c1325740a0edf193463618e0946348", + "graph_sha256": "269b011b112506297d565d734baa2a08e0f31a2bce79037c6c61708abf9a52c9", "observed_at": "2026-07-21T02:13:20.738Z", "expires_at": "2026-07-22T02:13:20.738Z", "identity": { @@ -47,7 +47,7 @@ "type": "answer_quality", "tier": "answer_quality", "status": "pass", - "graph_sha256": "9df8f932d66a60baa104bf61252ab318b0c1325740a0edf193463618e0946348", + "graph_sha256": "269b011b112506297d565d734baa2a08e0f31a2bce79037c6c61708abf9a52c9", "observed_at": "2026-07-21T02:13:20.738Z", "expires_at": "2026-07-22T02:13:20.738Z", "identity": { @@ -69,7 +69,7 @@ "schema": "codestory.release-claim-evaluation/v1", "status": "pass", "graph_schema": "codestory.release-claims/v1", - "graph_sha256": "9df8f932d66a60baa104bf61252ab318b0c1325740a0edf193463618e0946348", + "graph_sha256": "269b011b112506297d565d734baa2a08e0f31a2bce79037c6c61708abf9a52c9", "evidence_selection": "all_matching_rows_must_pass", "expected_commit": "2222222222222222222222222222222222222222", "evaluated_at": "2026-07-21T02:13:20.738Z", diff --git a/crates/codestory-llama-sys/per-user-embedding-server-constant-set.json b/crates/codestory-llama-sys/per-user-embedding-server-constant-set.json index 33c1aed5a..359094289 100644 --- a/crates/codestory-llama-sys/per-user-embedding-server-constant-set.json +++ b/crates/codestory-llama-sys/per-user-embedding-server-constant-set.json @@ -1,15 +1,15 @@ { "calibration_required_values": { "capacity_retry_policy": { - "retry_after_ms": 42, + "retry_after_ms": 66, "retry_class": "after_capacity_change", "retry_condition_source": "named_condition_from_typed_capacity_response" }, "connect_timeout_ms": 2000, "election_backoff_policy": { - "initial_backoff_ms": 7, + "initial_backoff_ms": 8, "jitter": "sha256(process_start_id||attempt) modulo inclusive [initial_backoff_ms,maximum_backoff_ms]", - "maximum_backoff_ms": 104 + "maximum_backoff_ms": 106 }, "hard_native_no_progress_ms": 385431, "request_deadlines_ms": { @@ -44,20 +44,20 @@ "true_idle_observation_grace_ms": 2500 }, "freeze_record": { - "calibration_bundle_sha256": "2adaaab974814cf890609bac0f1b6be54fb04cda4812aea33f06dff63a954ed0", - "calibration_freeze_digest": "511ec0e9018d73c1cfb4669de2e1b4bd722ef650cd9e6d38d7179d568913f2d5", + "calibration_bundle_sha256": "9c9476f3098c7836394d84089a7856858c1c9eda7b6a8c0bc482796adc72f683", + "calibration_freeze_digest": "6ccda709725bfa3397e1595db0e866e94c1507e7ee370000b37e99ab803cdba2", "input_constant_set_sha256": "ea58d298473ddf320469d15dc7c32176a1109d615a689c15ff76b67fb337e109", "measurement_protocol_sha256": "d1bb9b2c7eb354fe98990aa32eedc0e165b0cf804212966e0a2ee362a2f5bf8e", "protocol_sha256": "f4a3fa4afb4d5bcd8e707a5e21b687cdd023dc3398b28ff6891a2318e89c5ec7", "run_artifact_sha256s": [ - "2332db8dfdf81057263a7db45d184e0847f576d1cb2047f2704998d0f20848c8", - "7a0a63f344b0ba0f88cc8e3a7118d8c8d3ec044ee1ea0b71ad3fbc75421ee44c", - "7bb038f4503c333d200f5f532fded590ce74acdb2cabc99cfaf7643b6930767f" + "1e3bd8da8bb21fd6196a71101d17a49e70c2a57bc8baf6f6d8a7e16463259a2b", + "b38c009d4190c360bcf7790239d2cf22beea6bfb64c2920a1cd48019b0851dbb", + "dd204de79a20f6347e2c83e00876ba62840d3d3b619befa5fb1712f85b6c1ba8" ], - "selected_at": "github-actions-run:30562970311:1", + "selected_at": "github-actions-run:30590903374:1", "selection_rule": "constant_only_three_fresh_generations_one_sample_each+slow_host_floors_v2", - "selection_source_commit": "681ca99098b86006e7dc1f51c27158c757ac05c9", - "selection_source_tree": "1a5494c9b82bcddc030438452285154e9d5b3e2b" + "selection_source_commit": "ac2d788bd1de84ad7cc241f8d2a7efdabf8c9d35", + "selection_source_tree": "88583ad49a26f38cfb5f00aee6aee0236a70c833" }, "qualification_thresholds": { "backend_observed_accelerator_residency": 1, diff --git a/release-claims.json b/release-claims.json index 068d2e6f0..e5b60359a 100644 --- a/release-claims.json +++ b/release-claims.json @@ -1523,7 +1523,7 @@ "publisher_step": "Publish executable release freeze", "status_creator": "github-actions[bot]", "job_manifest": ".github/scripts/release-freeze-acceptance-jobs.json", - "job_manifest_sha256": "2df6fb76f1ac19acb98e530381ef456f38d517ded6356e61892b75b5fe6f3c79", + "job_manifest_sha256": "bb0a23ed7f74528fc3d4e4962c1b34f98758a628f48dd943b9d1356149e453c5", "phases": { "calibration_source": { "known_future_source_changes": [ diff --git a/scripts/tests/codestory-release-claims.test.mjs b/scripts/tests/codestory-release-claims.test.mjs index 147772278..9b59a0175 100644 --- a/scripts/tests/codestory-release-claims.test.mjs +++ b/scripts/tests/codestory-release-claims.test.mjs @@ -180,7 +180,7 @@ test("versioned claim graph has one deterministic digest and all declared contro status_creator: "github-actions[bot]", job_manifest: ".github/scripts/release-freeze-acceptance-jobs.json", job_manifest_sha256: - "2df6fb76f1ac19acb98e530381ef456f38d517ded6356e61892b75b5fe6f3c79", + "bb0a23ed7f74528fc3d4e4962c1b34f98758a628f48dd943b9d1356149e453c5", phases: { calibration_source: { known_future_source_changes: [ diff --git a/scripts/tests/fixtures/release-claims/positive.json b/scripts/tests/fixtures/release-claims/positive.json index 57f4a96fe..d7023033d 100644 --- a/scripts/tests/fixtures/release-claims/positive.json +++ b/scripts/tests/fixtures/release-claims/positive.json @@ -17,7 +17,7 @@ "type": "source_behavior", "tier": "source", "status": "pass", - "graph_sha256": "9df8f932d66a60baa104bf61252ab318b0c1325740a0edf193463618e0946348", + "graph_sha256": "269b011b112506297d565d734baa2a08e0f31a2bce79037c6c61708abf9a52c9", "observed_at": "2026-07-16T11:00:00.000Z", "expires_at": "2026-07-17T11:00:00.000Z", "identity": {