From 65e0b91d49991f0c4a97f168c12aa5a109204595 Mon Sep 17 00:00:00 2001 From: Li SiYi Date: Fri, 6 Nov 2026 03:24:43 +0800 Subject: [PATCH] chore: remove the shadow governance observer The observer existed to answer one question: whether the repository should switch to the reconciled verifier. That verifier could not converge, both governance repositories returned to the canonical gate, and the question is settled. Its snapshot check also treats any comment or body change during a read as stale evidence, so it fails whenever a review starts while it runs. It is report-only, so the result is a red cross on pull requests that blocks nothing and teaches readers to ignore a red check. Remove the reusable engine, its tests, the policy document it reads and the documentation that describes it. --- .github/governance/policy.json | 10 - .github/workflows/governance-acceptance.yml | 207 ---------------- .github/workflows/governance-shadow-tests.yml | 17 -- PROPOSAL.md | 5 +- README.md | 1 - README.zh-CN.md | 1 - docs/governance-shadow.md | 83 ------- scripts/test-governance-shadow.cjs | 232 ------------------ 8 files changed, 4 insertions(+), 552 deletions(-) delete mode 100644 .github/governance/policy.json delete mode 100644 .github/workflows/governance-acceptance.yml delete mode 100644 .github/workflows/governance-shadow-tests.yml delete mode 100644 docs/governance-shadow.md delete mode 100644 scripts/test-governance-shadow.cjs diff --git a/.github/governance/policy.json b/.github/governance/policy.json deleted file mode 100644 index d346891..0000000 --- a/.github/governance/policy.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "schema_version": 1, - "mode": "SHADOW", - "reviewers": [], - "owners": [], - "codex_app_ids": [1144995], - "max_evidence_age_hours": 24, - "baseline_exceptions": false, - "merge_authorized": false -} diff --git a/.github/workflows/governance-acceptance.yml b/.github/workflows/governance-acceptance.yml deleted file mode 100644 index 5b84d16..0000000 --- a/.github/workflows/governance-acceptance.yml +++ /dev/null @@ -1,207 +0,0 @@ -name: Governance shadow engine - -on: - workflow_call: - inputs: - pr-number: - required: true - type: number - policy-revision: - description: Same immutable central commit as the reusable workflow reference - required: true - type: string - mode: - default: SHADOW - type: string - -permissions: - contents: read - pull-requests: read - issues: read - -jobs: - observe: - name: Governance shadow observation - runs-on: ubuntu-latest - timeout-minutes: 5 - steps: - - name: Observe native candidate evidence - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 - env: - PR_NUMBER: ${{ inputs.pr-number }} - POLICY_REVISION: ${{ inputs.policy-revision }} - MODE: ${{ inputs.mode }} - with: - script: | - // Trusted reusable workflow code only; never load candidate source. - const crypto = require('node:crypto'); - const digest = value => crypto.createHash('sha256').update(JSON.stringify(value)).digest('hex'); - const revision = process.env.POLICY_REVISION; - const number = Number(process.env.PR_NUMBER); - const fail = code => { throw new Error(code); }; - const hash = value => typeof value === 'string' && /^[0-9a-f]{40}$/.test(value); - let report = {schema_version: 1, mode: 'SHADOW', merge_authorized: false, - observation: 'UNKNOWN', reason: 'READ_OR_INTEGRITY_FAILURE'}; - try { - if (process.env.MODE !== 'SHADOW' || !hash(revision) - || !Number.isSafeInteger(number) || number < 1) fail('INVALID_INPUT'); - const {data: content} = await github.rest.repos.getContent({owner: 'QuantStrategyLab', - repo: '.github', path: '.github/governance/policy.json', ref: revision}); - if (content.encoding !== 'base64' || typeof content.content !== 'string') fail('INVALID_POLICY'); - const policy = JSON.parse(Buffer.from(content.content, 'base64').toString('utf8')); - if (policy.schema_version !== 1 || policy.mode !== 'SHADOW' || policy.merge_authorized !== false - || policy.baseline_exceptions !== false || !Array.isArray(policy.reviewers) - || !Array.isArray(policy.owners) || ![...policy.reviewers, ...policy.owners].every(x => typeof x === 'string') - || !Array.isArray(policy.codex_app_ids) - || !policy.codex_app_ids.every(x => Number.isSafeInteger(x) && x > 0) - || !Number.isInteger(policy.max_evidence_age_hours) - || policy.max_evidence_age_hours < 1 || policy.max_evidence_age_hours > 24) fail('INVALID_POLICY'); - const policyDigest = digest(policy); - const args = {...context.repo, pull_number: number}; - const readPR = async () => (await github.rest.pulls.get(args)).data; - const readEvidence = async () => ({ - reviews: await github.paginate(github.rest.pulls.listReviews, {...args, per_page: 100}), - comments: await github.paginate(github.rest.issues.listComments, - {...context.repo, issue_number: number, per_page: 100}) - }); - const binding = pr => [pr.id, pr.state, pr.head?.sha, pr.base?.sha, pr.base?.ref, - pr.base?.repo?.id, pr.head?.repo?.id, pr.user?.login, pr.changed_files, pr.additions, pr.deletions]; - const pr = await readPR(); - if (pr.state !== 'open' || !hash(pr.head?.sha) || !hash(pr.base?.sha) - || !Number.isSafeInteger(pr.changed_files) || pr.changed_files < 1 || pr.changed_files >= 3000 - || !pr.user?.login || pr.base?.repo?.full_name?.toLowerCase() - !== `${context.repo.owner}/${context.repo.repo}`.toLowerCase()) fail('INVALID_CANDIDATE'); - const files = await github.paginate(github.rest.pulls.listFiles, {...args, per_page: 100}); - if (files.length !== pr.changed_files || new Set(files.map(f => f.filename)).size !== files.length) - fail('INCOMPLETE_FILES'); - function complete(file) { - if (!['added', 'modified', 'removed', 'renamed'].includes(file.status) - || typeof file.filename !== 'string' || !file.filename || file.filename.includes('\0') - || (file.status === 'renamed' && !file.previous_filename) - || !Number.isInteger(file.additions) || file.additions < 0 - || !Number.isInteger(file.deletions) || file.deletions < 0 - || typeof file.patch !== 'string' || !file.patch) return false; - let adds = 0, dels = 0, oldLeft = 0, newLeft = 0, hunk = false; - for (const line of file.patch.split('\n')) { - const m = /^@@ -(\d+)(?:,(\d+))? \+(\d+)(?:,(\d+))? @@/.exec(line); - if (m) { - if (oldLeft || newLeft) return false; - oldLeft = m[2] === undefined ? 1 : Number(m[2]); - newLeft = m[4] === undefined ? 1 : Number(m[4]); hunk = true; - } else if (line.startsWith('\\ No newline')) { - if (!hunk) return false; - } else { - if (!hunk) return false; - if (line[0] === '+') { adds++; newLeft--; } - else if (line[0] === '-') { dels++; oldLeft--; } - else if (line[0] === ' ') { oldLeft--; newLeft--; } - else return false; - if (oldLeft < 0 || newLeft < 0) return false; - } - } - return hunk && oldLeft === 0 && newLeft === 0 && adds === file.additions && dels === file.deletions; - } - if (!files.every(complete) - || files.reduce((n,f) => n + f.additions, 0) !== pr.additions - || files.reduce((n,f) => n + f.deletions, 0) !== pr.deletions) fail('INCOMPLETE_PATCH'); - const scope = digest(files.map(f => [f.filename, f.previous_filename || null, f.status, - f.additions, f.deletions, digest(f.patch)]).sort((a,b) => a[0].localeCompare(b[0]))); - const authority = /authoriz|approv|governance|acceptance|permission|secret|credential|token|account|iam|risk|trad(?:e|ing)|kelly|stop.?loss|deploy|publish|baseline|safety|mask|redact|门禁|审批|授权|风控|密钥|止损/i; - const sensitive = /(^|\/)(\.github|tests?|fixtures?|config|deploy|runtime)(\/|$)|(^|\/)(AGENTS\.md|CODEOWNERS|SECURITY\.md)$|lock$|requirements|package\.json|pyproject\.toml/i; - const isR2 = files.some(f => [f.filename, f.previous_filename || ''].some(p => sensitive.test(p) || authority.test(p)) - || authority.test(f.patch) || (f.filename.endsWith('.md') && (f.deletions > 0 || f.status !== 'added'))); - const proseOnly = files.every(f => /^docs\/[a-z0-9/-]+\.md$/.test(f.filename) - && f.status === 'added' && f.deletions === 0 && !/[<`\[\]{}]|https?:|\bmust\b|\bshall\b|\brequir|必须|应当/i.test(f.patch)); - const tier = isR2 ? 'R2' : proseOnly ? 'R0' : 'R1'; - const evidence = await readEvidence(); - function codexObservation(comments) { - if (policy.codex_app_ids.length === 0) return 'NOT_CONFIGURED'; - const candidates = comments.filter(entry => entry.user?.type === 'Bot' - && policy.codex_app_ids.includes(entry.performed_via_github_app?.id) - && typeof entry.body === 'string' && entry.body.includes('codex-review-state:v1')); - if (candidates.some(entry => !Number.isSafeInteger(entry.id) - || !Number.isFinite(Date.parse(entry.updated_at)))) return 'UNKNOWN'; - candidates.sort((a, b) => Date.parse(b.updated_at || '') - Date.parse(a.updated_at || '') || b.id - a.id); - if (!candidates.length) return 'NOT_FOUND'; - if ((candidates[0].body.match(/codex-review-state:v1/g) || []).length !== 1) return 'UNKNOWN'; - const match = //.exec(candidates[0].body); - if (!match) return 'UNKNOWN'; - let review; - try { review = JSON.parse(match[1]).code_review; } catch { return 'UNKNOWN'; } - if (!review || review.commit_sha !== pr.head.sha) return 'STALE_OR_UNKNOWN'; - if (review.status === 'running') return 'IN_PROGRESS'; - if (review.status !== 'completed' || typeof review.has_any_findings !== 'boolean' - || typeof review.has_unresolved_findings !== 'boolean') return 'UNKNOWN'; - return review.has_unresolved_findings ? 'UNRESOLVED_FINDINGS' - : review.has_any_findings ? 'FINDINGS_REPORTED' : 'NO_REPORTED_FINDINGS'; - } - const now = Date.now(); - const findingsClear = findings => Array.isArray(findings) && findings.every(finding => - finding && (finding.kind === 'advisory' || (finding.kind === 'defect' - && ['resolved', 'false_positive', 'obsolete'].includes(finding.disposition)))); - function decision(kind, list, allowed) { - const candidates = []; - for (const entry of list) { - if (entry.user?.type !== 'User' || !allowed.includes(entry.user?.login) - || entry.user.login === pr.user.login) continue; - // No labels, provider completion, executor receipt or shared identity inference. - let record; - try { record = JSON.parse(entry.body); } catch { continue; } - if (record.schema_version !== 1 || record.kind !== kind || record.head !== pr.head.sha - || record.base !== pr.base.sha || record.scope !== scope || record.policy !== policyDigest - || record.repository !== `${context.repo.owner}/${context.repo.repo}` || record.pr !== number) continue; - const created = Date.parse(entry.submitted_at || entry.created_at); - const expires = Date.parse(record.expires_at); - const valid = Number.isFinite(created) && Number.isFinite(expires) && created <= now && expires > now - && expires > created && expires - created <= policy.max_evidence_age_hours * 3600000 - && now - created <= policy.max_evidence_age_hours * 3600000 - && (!entry.updated_at || entry.updated_at === entry.created_at) - && (kind !== 'review' || (entry.state === 'APPROVED' && entry.commit_id === pr.head.sha)) - && findingsClear(record.findings); - // Newer revoked/invalid records must dominate an older approval. - candidates.push({id: entry.id, valid, decision: record.decision}); - } - candidates.sort((a,b) => b.id - a.id); - return candidates[0]?.valid === true && candidates[0]?.decision === 'approve'; - } - const unresolvedByReviewer = new Map(); - for (const review of [...evidence.reviews].sort((a,b) => a.id - b.id)) { - if (review.user?.type !== 'User' || !policy.reviewers.includes(review.user.login) - || review.user.login === pr.user.login || review.commit_id !== pr.head.sha - || review.state === 'DISMISSED') continue; - const reviewer = review.user.login; - if (review.state === 'CHANGES_REQUESTED') { - unresolvedByReviewer.set(reviewer, true); - } else if (review.state === 'APPROVED' && decision('review', [review], policy.reviewers)) { - unresolvedByReviewer.set(reviewer, false); - } else { - try { - const record = JSON.parse(review.body); - if (record.head === pr.head.sha && record.base === pr.base.sha && record.scope === scope - && record.policy === policyDigest && !findingsClear(record.findings)) - unresolvedByReviewer.set(reviewer, true); - } catch { /* A comment without a record cannot clear a change request. */ } - } - } - const unresolved = [...unresolvedByReviewer.values()].some(Boolean); - const reviewed = !unresolved && decision('review', evidence.reviews, policy.reviewers); - const owned = decision('owner', evidence.comments, policy.owners); - const currentEvidence = await readEvidence(); - const current = await readPR(); - if (digest(binding(pr)) !== digest(binding(current)) || digest(evidence) !== digest(currentEvidence)) fail('STALE_SNAPSHOT'); - report = {...report, tier, file_count: files.length, head: pr.head.sha, base: pr.base.sha, - scope, policy: policyDigest, policy_revision: revision, - observation: tier === 'R0' ? 'PROSE_CANDIDATE' : !reviewed ? 'PENDING_REVIEW' - : tier === 'R2' && !owned ? 'PENDING_OWNER' : 'EVIDENCE_PRESENT', - reason: 'SHADOW_ONLY', independent_review: reviewed, owner_approval: owned, - semantic_classification: 'HEURISTIC_NOT_ACCEPTANCE', required_checks: 'NOT_EVALUATED', - event_freshness: 'SNAPSHOT_ONLY', baseline_exception: 'UNSUPPORTED', - codex_observation: codexObservation(evidence.comments)}; - } catch { - // Deliberately omit exception text, paths, patches, user bodies and API response data. - report = {...report, observation: 'UNKNOWN', reason: 'READ_OR_INTEGRITY_FAILURE'}; - core.setFailed('Shadow observation unavailable; existing gates remain authoritative'); - } - await core.summary.addHeading('Governance shadow observation').addCodeBlock(JSON.stringify(report, null, 2), 'json').write(); - core.setOutput('observation', report.observation); - return report; diff --git a/.github/workflows/governance-shadow-tests.yml b/.github/workflows/governance-shadow-tests.yml deleted file mode 100644 index 2057b1a..0000000 --- a/.github/workflows/governance-shadow-tests.yml +++ /dev/null @@ -1,17 +0,0 @@ -name: Governance shadow tests -on: - pull_request: - push: - branches: [main] -permissions: - contents: read -jobs: - test-shadow: - name: Governance shadow tests - runs-on: ubuntu-latest - timeout-minutes: 5 - steps: - - uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6 - with: - persist-credentials: false - - run: node --test scripts/test-governance-shadow.cjs diff --git a/PROPOSAL.md b/PROPOSAL.md index ae99280..bfe95e9 100644 --- a/PROPOSAL.md +++ b/PROPOSAL.md @@ -8,7 +8,10 @@ QuantAIService and could not converge. Its own check, status and workflow-run triggers change the check evidence it rereads, so every evaluation ended in `error` and closed both repositories to merges. Both returned to the canonical `.github/workflows/risk-gate.yml` that the other 23 repositories run. The text -below is the record of the proposed design, not the deployed gate. +below is the record of the proposed design, not the deployed gate. The shadow +observer and its tests were removed on 2026-11-06, so the references below to +`scripts/test-governance-shadow.cjs` and `.github/governance/policy.json` are +historical. ## Decision rule diff --git a/README.md b/README.md index d35f368..fcbee84 100644 --- a/README.md +++ b/README.md @@ -23,4 +23,3 @@ Organization-wide files for QuantStrategyLab repositories. Open a pull request. Describe the problem, the change, and the effect on existing repositories. AI assistants and maintainers follow [AGENTS.md](AGENTS.md). -- [Governance shadow engine](docs/governance-shadow.md) — reusable report-only review observations. diff --git a/README.zh-CN.md b/README.zh-CN.md index 805f71f..0c32ae7 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -23,4 +23,3 @@ QuantStrategyLab 各仓库共用的组织级文件。 提交 pull request,说明要解决的问题、改了什么,以及对现有仓库的影响。AI 助手与维护者遵循 [AGENTS.md](AGENTS.md)。 -- [Governance shadow engine](docs/governance-shadow.md) — reusable report-only review observations. diff --git a/docs/governance-shadow.md b/docs/governance-shadow.md deleted file mode 100644 index 30db82e..0000000 --- a/docs/governance-shadow.md +++ /dev/null @@ -1,83 +0,0 @@ -# Governance shadow engine - -The reusable `governance-acceptance.yml` workflow observes pull requests without -changing merge authorization. It is public and can be called from repositories -under any owner. Both the workflow reference and `policy-revision` must use the -same full, reviewed central commit. There are no inherited secrets, candidate -checkouts, comment writes, status writes or merge operations. - -## Caller contract - -Use `pull_request` with opened, synchronize, reopened, edited and ready-for-review -events. Pass the event pull request number, `mode: SHADOW` and the central policy -revision. Use the dedicated `Governance shadow observation` job name; do not add -it to required checks or replace an existing check. Read-only contents, pull -requests and issues permissions suffice. Existing required checks remain separate. -The public workflow requires no organization access-setting change. Hosting -restrictions or an unavailable pin must be reported rather than bypassed. - -## Observations and evidence - -The engine reads all changed files and verifies file counts, aggregate additions -and deletions, and hunk lengths. Missing or binary patches are unknown. It hashes -the complete scope and central policy. Authority-related paths and prose, test -changes and modified Markdown are conservatively R2; ordinary source changes -are R1. Only narrowly constrained added explanatory prose is an R0 candidate. -These are heuristic observations, never semantic review acceptance. - -Native reviews and owner comments may carry a JSON record containing -`schema_version: 1`, `kind: review` or `kind: owner`, `repository`, numeric `pr`, -full `head` and `base`, `scope`, `policy`, `decision: approve` or `decision: revoke`, -`expires_at` and `findings: []`. Findings may also contain optional -`kind: advisory` advice, or `kind: defect` records with a reviewed disposition of -`resolved`, `false_positive` or `obsolete`; unresolved or malformed defects block -review evidence. Digest values come from the candidate summary. -An independent review also needs an APPROVED native review at that head. -Only human identities listed in the pinned policy qualify; the author cannot -approve their own work. Owner comments edited after creation do not qualify. -Newer invalid or revoked bound records dominate older approvals. No label or -provider availability supplies authorization. Default identity lists are empty, -so R1/R2 observations remain pending until a separately reviewed source policy -establishes identities. No identities or settings are provisioned by this pilot. -An unresolved change request survives comment-only reviews and can be cleared -only by a valid later approval from that reviewer or dismissal of the request. - -An optional `codex_app_ids` list enables a separate observation of the native -Codex summary comment. The source must be a bot posted through a configured -GitHub App and its review state must name the current head. The observation -distinguishes running, stale, malformed and unresolved findings. An empty list -leaves this observation unconfigured. This signal does not establish full -coverage or base binding, does not accept a review, and never authorizes merge. -Multiple receipt blocks in one comment are ambiguous and reported as unknown. - -The final read compares the candidate and evidence with the initial reads. -Failures produce UNKNOWN and a failed, non-required observation job. Reports use -fixed fields, candidate bindings and counts; paths, titles, patches, identities, -comment text and raw API errors are never exported. A green workflow means the -observer ran; it never means the pull request is approved. - -## Limits and promotion - -`merge_authorized` is always false. Required-check acceptance, baseline-red -exceptions, durable writer admission, atomic freshness, event completeness, -merge-queue support and identity independence from execution are not established. -Review/comment and base-only changes do not currently schedule a new observation; -a later pull request event refreshes the snapshot. This is suitable only for -report-only comparison, not enforcement. There is no persistent decision cache: -parallel or restarted observations independently re-read native state and cannot -mutate repository state. No deployment, publication or live operation is added. - -Keep the central PR and pilot callers under review. Callers pin the central -candidate commit; merge the reviewed central source before integrating callers. -The PR check run can exercise the pinned public candidate before that merge, -subject to repository Actions policy. Promote only after native runtime evidence -and separately approved identity, required-check and event-freshness changes. -Rollback consists of reverting only the new caller through a normal reviewed PR; -existing gates stay in place. - -## Local verification - -Run `node --test scripts/test-governance-shadow.cjs`, the existing Python checker -and secret-scan tests, and `python scripts/check_style.py --phase 2`. The new -`Governance shadow tests` workflow exercises the exact inline reusable code with -fake API responses. Offline tests do not prove hosted execution or adoption. diff --git a/scripts/test-governance-shadow.cjs b/scripts/test-governance-shadow.cjs deleted file mode 100644 index dbce45d..0000000 --- a/scripts/test-governance-shadow.cjs +++ /dev/null @@ -1,232 +0,0 @@ -const {test} = require('node:test'); -const assert = require('node:assert/strict'); -const fs = require('node:fs'); -const path = require('node:path'); -const workflowPath = process.env.SHADOW_WORKFLOW || path.join(__dirname, '../.github/workflows/governance-acceptance.yml'); -const workflow = fs.readFileSync(workflowPath, 'utf8'); -const script = workflow.split(' script: |\n')[1]?.split('\n').map(x => x.slice(12)).join('\n'); -assert.ok(script, 'Reusable engine must contain executable observer'); -const AsyncFunction = Object.getPrototypeOf(async function(){}).constructor; -const execute = new AsyncFunction('github', 'context', 'core', 'require', 'process', script); -const H = 'a'.repeat(40), B = 'b'.repeat(40), R = 'c'.repeat(40); -const policy = JSON.parse(fs.readFileSync(path.join(__dirname, '../.github/governance/policy.json'))); -function fixture() { - return {pr: {id: 1, state: 'open', head: {sha: H, repo: {id: 2}}, - base: {sha: B, ref: 'main', repo: {id: 2, full_name: '/sample'}}, - user: {login: '-author'}, changed_files: 1, additions: 1, deletions: 0}, - files: [{filename: 'docs/example.md', status: 'added', additions: 1, deletions: 0, patch: '@@ -0,0 +1 @@\n+Plain explanatory prose.'}], - evidence: {reviews: [], comments: []}, policy: structuredClone(policy)}; -} -async function observe(f = fixture()) { - const state = {failed: false, summary: '', calls: 0}; - const clone = x => structuredClone(x); - const github = {rest: {repos: {getContent: async () => { - if (f.apiError) throw new Error('private token '); - return {data: {encoding: 'base64', content: Buffer.from(JSON.stringify(f.policy)).toString('base64')}}; - }}, pulls: {get: async () => ({data: clone(++state.calls > 1 && f.current || f.pr)}), - listFiles: 'files', listReviews: 'reviews'}, issues: {listComments: 'comments'}}, - paginate: async method => clone(method === 'files' ? f.files : f.evidence[method])}; - const core = {setFailed: () => {state.failed = true;}, setOutput: () => {}, summary: { - addHeading() {return this;}, addCodeBlock(s) {state.summary = s; return this;}, async write() {}}}; - const report = await execute(github, {repo: {owner: '', repo: 'sample'}}, core, require, - {env: {MODE: f.mode || 'SHADOW', POLICY_REVISION: R, PR_NUMBER: '1'}}); - assert.equal(report.merge_authorized, false); - assert.ok(!state.summary.includes('private token')); - return {report, state}; -} -test('Narrow added prose produces an observation, never merge authorization', async () => { - const {report} = await observe(); assert.equal(report.tier, 'R0'); assert.equal(report.observation, 'PROSE_CANDIDATE'); -}); -test('Ordinary code needs independent review', async () => { - const f = fixture(); f.files[0].filename = 'src/example.js'; - assert.equal((await observe(f)).report.observation, 'PENDING_REVIEW'); -}); -function codexFixture() { - const f = fixture(); f.files[0].filename = 'src/example.js'; f.policy.codex_app_ids = [12345]; - const record = {code_review: {commit_sha: H, status: 'completed', - has_any_findings: false, has_unresolved_findings: false}}; - const entry = {id: 11, user: {type: 'Bot'}, performed_via_github_app: {id: 12345}, - updated_at: new Date().toISOString(), body: ``}; - f.evidence.comments = [entry]; - return {f, entry, record}; -} -test('Codex current-head receipt is observation only, never approval', async () => { - const {f} = codexFixture(); const {report} = await observe(f); - assert.equal(report.codex_observation, 'NO_REPORTED_FINDINGS'); - assert.equal(report.observation, 'PENDING_REVIEW'); - assert.equal(report.independent_review, false); -}); -for (const [mutation, expected] of [['stale', 'STALE_OR_UNKNOWN'], ['running', 'IN_PROGRESS'], - ['unresolved', 'UNRESOLVED_FINDINGS'], ['resolved', 'FINDINGS_REPORTED'], - ['malformed', 'UNKNOWN'], ['untrusted', 'NOT_FOUND'], ['user', 'NOT_FOUND'], ['timestamp', 'UNKNOWN']]) { - test(`Codex receipt preserves untrusted or incomplete state: ${mutation}`, async () => { - const {f, entry, record} = codexFixture(); - if (mutation === 'stale') record.code_review.commit_sha = 'd'.repeat(40); - if (mutation === 'running') record.code_review.status = 'running'; - if (mutation === 'unresolved') record.code_review.has_unresolved_findings = true; - if (mutation === 'resolved') record.code_review.has_any_findings = true; - if (mutation === 'untrusted') entry.performed_via_github_app.id = 54321; - if (mutation === 'user') entry.user.type = 'User'; - if (mutation === 'timestamp') entry.updated_at = 'invalid'; - entry.body = mutation === 'malformed' ? '' - : ``; - const {report} = await observe(f); - assert.equal(report.codex_observation, expected); - assert.equal(report.observation, 'PENDING_REVIEW'); - }); -} -test('Unconfigured Codex observer never trusts arbitrary receipts', async () => { - const {f} = codexFixture(); f.policy.codex_app_ids = []; - assert.equal((await observe(f)).report.codex_observation, 'NOT_CONFIGURED'); -}); -test('A malformed newer native receipt cannot revive an older clean receipt', async () => { - const {f, entry} = codexFixture(); - f.evidence.comments.push({...entry, id: 12, updated_at: 'invalid'}); - assert.equal((await observe(f)).report.codex_observation, 'UNKNOWN'); -}); -for (const duplicate of ['unresolved', 'malformed']) { - test(`Duplicate Codex receipt blocks an ambiguous observation: ${duplicate}`, async () => { - const {f, entry, record} = codexFixture(); - record.code_review.has_unresolved_findings = true; - entry.body += duplicate === 'unresolved' - ? `\n` - : '\n'; - assert.equal((await observe(f)).report.codex_observation, 'UNKNOWN'); - }); -} -for (const filename of ['.github/workflows/example.yml', 'tests/example.js', 'docs/acceptance-policy.md', 'AGENTS.md']) { - test(`Sensitive scope is R2: ${filename}`, async () => { - const f = fixture(); f.files[0].filename = filename; - assert.equal((await observe(f)).report.tier, 'R2'); - }); -} -test('Authority-changing Markdown is R2', async () => { - const f = fixture(); f.files[0].patch = '@@ -0,0 +1 @@\n+Approval is optional.'; - assert.equal((await observe(f)).report.tier, 'R2'); -}); -for (const mutation of ['missing', 'truncated', 'count', 'binary', 'duplicate', 'rename', 'api', 'mode', 'policy']) { - test(`Unknown integrity never becomes valid: ${mutation}`, async () => { - const f = fixture(); - if (mutation === 'missing') f.files = []; - if (mutation === 'truncated') f.files[0].patch = '@@ -0,0 +1,2 @@\n+Only one line'; - if (mutation === 'count') f.pr.additions = 2; - if (mutation === 'binary') delete f.files[0].patch; - if (mutation === 'duplicate') {f.files.push(f.files[0]); f.pr.changed_files = 2;} - if (mutation === 'rename') f.files[0].status = 'renamed'; - if (mutation === 'api') f.apiError = true; - if (mutation === 'mode') f.mode = 'ENFORCE'; - if (mutation === 'policy') f.policy.merge_authorized = true; - const {report, state} = await observe(f); - assert.equal(report.observation, 'UNKNOWN'); assert.equal(state.failed, true); - }); -} -for (const drift of ['head', 'base', 'ref', 'files', 'author']) { - test(`Re-read rejects snapshot drift: ${drift}`, async () => { - const f = fixture(); f.current = structuredClone(f.pr); - if (drift === 'head') f.current.head.sha = 'd'.repeat(40); - if (drift === 'base') f.current.base.sha = 'e'.repeat(40); - if (drift === 'ref') f.current.base.ref = 'other'; - if (drift === 'files') f.current.changed_files = 2; - if (drift === 'author') f.current.user.login = '-other'; - assert.equal((await observe(f)).report.observation, 'UNKNOWN'); - }); -} -async function recordFixture() { - const f = fixture(); f.files[0].filename = '.github/example.yml'; - f.policy.reviewers = ['-reviewer', '-other']; f.policy.owners = ['-owner']; - const {report} = await observe(f); - const record = {schema_version: 1, kind: 'review', repository: '/sample', pr: 1, - head: H, base: B, scope: report.scope, policy: report.policy, decision: 'approve', findings: [], - expires_at: new Date(Date.now() + 3600000).toISOString()}; - const created_at = new Date(Date.now() - 60000).toISOString(); - f.evidence.reviews = [{id: 1, user: {login: '-reviewer', type: 'User'}, state: 'APPROVED', commit_id: H, - submitted_at: created_at, body: JSON.stringify(record)}]; - return {f, record, created_at}; -} -test('Configured independent review still requires distinct owner evidence for R2', async () => { - const {f} = await recordFixture(); assert.equal((await observe(f)).report.observation, 'PENDING_OWNER'); -}); -for (const invalid of ['expired', 'base', 'scope', 'policy', 'author', 'bot', 'dismissed', 'defect', 'revoked']) { - test(`Reject invalid independent review: ${invalid}`, async () => { - const {f, record} = await recordFixture(); - if (invalid === 'expired') record.expires_at = new Date(Date.now() - 1000).toISOString(); - if (['base', 'scope', 'policy'].includes(invalid)) record[invalid] = 'f'.repeat(40); - if (invalid === 'author') {f.policy.reviewers.push(f.pr.user.login); f.evidence.reviews[0].user.login = f.pr.user.login;} - if (invalid === 'bot') f.evidence.reviews[0].user.type = 'Bot'; - if (invalid === 'dismissed') f.evidence.reviews[0].state = 'DISMISSED'; - if (invalid === 'defect') record.findings = [{severity: 'defect'}]; - if (invalid === 'revoked') record.decision = 'revoke'; - f.evidence.reviews[0].body = JSON.stringify(record); - assert.equal((await observe(f)).report.observation, 'PENDING_REVIEW'); - }); -} -test('Edited owner comment cannot authorize; subsequent revoke dominates approval', async () => { - const {f, record, created_at} = await recordFixture(); record.kind = 'owner'; - f.evidence.comments = [{id: 2, user: {login: '-owner', type: 'User'}, created_at, updated_at: created_at, - body: JSON.stringify(record)}]; - assert.equal((await observe(f)).report.observation, 'EVIDENCE_PRESENT'); - f.evidence.comments[0].updated_at = new Date().toISOString(); - assert.equal((await observe(f)).report.observation, 'PENDING_OWNER'); - f.evidence.comments[0].updated_at = created_at; - f.evidence.comments.push({...f.evidence.comments[0], id: 3, body: JSON.stringify({...record, decision: 'revoke'})}); - assert.equal((await observe(f)).report.observation, 'PENDING_OWNER'); -}); -test('Workflow has no checkout, secret inheritance, write permission or legacy status publisher', () => { - assert.doesNotMatch(workflow, /checkout|secrets:|: write|createCommitStatus|checks\.create|pull_request_target/); - assert.match(workflow, /timeout-minutes: 5/); -}); - -test('An unresolved concrete defect cannot be erased by another reviewer approval', async () => { - const {f, record} = await recordFixture(); - f.evidence.reviews.push({...f.evidence.reviews[0], id: 2, state: 'CHANGES_REQUESTED', - body: JSON.stringify({...record, findings: [{severity: 'defect'}]})}); - f.evidence.reviews.push({...f.evidence.reviews[0], id: 3, user: {login: '-other', type: 'User'}}); - assert.equal((await observe(f)).report.observation, 'PENDING_REVIEW'); -}); -test('A comment-only review does not clear a prior change request', async () => { - const {f, record} = await recordFixture(); - f.evidence.reviews.push({...f.evidence.reviews[0], id: 2, state: 'CHANGES_REQUESTED'}); - f.evidence.reviews.push({...f.evidence.reviews[0], id: 3, state: 'COMMENTED', body: 'Follow-up comment'}); - f.evidence.reviews.push({...f.evidence.reviews[0], id: 4, user: {login: '-other', type: 'User'}, - body: JSON.stringify(record)}); - const {report} = await observe(f); - assert.equal(report.independent_review, false); - assert.equal(report.observation, 'PENDING_REVIEW'); -}); -test('A valid later approval by the requesting reviewer clears the request', async () => { - const {f} = await recordFixture(); - f.evidence.reviews.push({...f.evidence.reviews[0], id: 2, state: 'CHANGES_REQUESTED'}); - f.evidence.reviews.push({...f.evidence.reviews[0], id: 3, state: 'COMMENTED', body: 'Follow-up'}); - f.evidence.reviews.push({...f.evidence.reviews[0], id: 4}); - const {report} = await observe(f); - assert.equal(report.independent_review, true); - assert.equal(report.observation, 'PENDING_OWNER'); -}); -test('A dismissed change request no longer blocks an independent approval', async () => { - const {f} = await recordFixture(); - f.evidence.reviews.push({...f.evidence.reviews[0], id: 2, state: 'DISMISSED'}); - f.evidence.reviews.push({...f.evidence.reviews[0], id: 3, - user: {login: '-other', type: 'User'}}); - assert.equal((await observe(f)).report.observation, 'PENDING_OWNER'); -}); -test('An expired approval cannot clear a prior change request', async () => { - const {f, record} = await recordFixture(); - f.evidence.reviews.push({...f.evidence.reviews[0], id: 2, state: 'CHANGES_REQUESTED'}); - record.expires_at = new Date(Date.now() - 1000).toISOString(); - f.evidence.reviews.push({...f.evidence.reviews[0], id: 3, body: JSON.stringify(record)}); - f.evidence.reviews.push({...f.evidence.reviews[0], id: 4, - user: {login: '-other', type: 'User'}}); - assert.equal((await observe(f)).report.observation, 'PENDING_REVIEW'); -}); -test('Parallel observations do not share mutable decisions', async () => { - const f = fixture(); f.apiError = true; - const [bad, good] = await Promise.all([observe(f), observe()]); - assert.equal(bad.report.observation, 'UNKNOWN'); - assert.equal(good.report.observation, 'PROSE_CANDIDATE'); -}); - -test('Optional style advice does not require repair', async () => { - const {f, record} = await recordFixture(); - record.findings = [{kind: 'advisory'}]; f.evidence.reviews[0].body = JSON.stringify(record); - assert.equal((await observe(f)).report.observation, 'PENDING_OWNER'); -});