diff --git a/review/sections/shared-code-reuse.md b/review/sections/shared-code-reuse.md index 4e4d6ba10..9aca55812 100644 --- a/review/sections/shared-code-reuse.md +++ b/review/sections/shared-code-reuse.md @@ -2,11 +2,12 @@ **Reuse a skipped shared-code advisory only with complete structural evidence:** -1. **Read the evidence.** Read all supporting callers and the helper destination. - Establish first-party authored provenance and whether the current extraction - is worthwhile; the checker cannot decide that. Retain `evidence_paths`/`helper_target`. +1. **Read the evidence.** Read all supporting callers and the helper target + read-only: `git update-index`, even `--really-refresh`, clears the index flags + the checker reads. Establish first-party authored provenance and whether this + extraction is worthwhile; the checker cannot. Retain `evidence_paths`/`helper_target`. 2. **Run the checker.** From the repository root, pass the current finding as - literal JSON on stdin. Replace REVIEW_START with this pass's captured token + literal JSON on stdin. Replace REVIEW_START with this pass's token and the example paths/symbol with actual evidence. Keep the quoted delimiter. ```bash @@ -15,17 +16,17 @@ GSTACK_SHARED_LIBS_REUSE_JSON ``` -3. **Act on its result.** Read the JSON. Only `reusable: true` permits suppression. +3. **Act on its result.** Only `reusable: true` permits suppression. False, command failure or unreadable output requires fresh source review and a new decision, never suppression. Do not supply your own snapshot, prior record or coverage. -4. **Persist through the logger.** The logger recomputes final coverage; never - supply proof yourself. Real defects retain normal Fix-First handling independently. +4. **Persist through the logger.** The logger recomputes coverage; never supply + proof. Real defects retain normal Fix-First handling. -**What a reusable result proves (do not reconstruct these checks yourself):** -- Identity: `sharedLibsFingerprint` plus the actual repo, raw branch and current snapshot. +**What a reusable result proves (never reconstruct these checks):** +- Identity: `sharedLibsFingerprint` plus the repo, raw branch and current snapshot. The checker reads REVIEW_START without consuming/replacing it. Sanitized branch names are not identity. - Prior decision: completed/converged review, verified binding, explicit Skip and - logger-versioned `snapshot_covered_paths`; older unversioned coverage needs a fresh decision. + logger-versioned `snapshot_covered_paths`; unversioned coverage needs a fresh decision. - Source: `canReuseSharedLibsAdvisory` requires every supporting path's raw file byte-for-byte with its blob. Exclude assume-unchanged, skip-worktree and sparse index entries; symlinks/ancestors, submodules, ignored/outside or unreadable files; diff --git a/scripts/resolvers/review-scope.ts b/scripts/resolvers/review-scope.ts index ff5688c59..40a94a5dd 100644 --- a/scripts/resolvers/review-scope.ts +++ b/scripts/resolvers/review-scope.ts @@ -133,11 +133,12 @@ or missing-reviewer rules.`; export function generateSharedCodeReuse(ctx: TemplateContext): string { return `**Reuse a skipped shared-code advisory only with complete structural evidence:** -1. **Read the evidence.** Read all supporting callers and the helper destination. - Establish first-party authored provenance and whether the current extraction - is worthwhile; the checker cannot decide that. Retain \`evidence_paths\`/\`helper_target\`. +1. **Read the evidence.** Read all supporting callers and the helper target + read-only: \`git update-index\`, even \`--really-refresh\`, clears the index flags + the checker reads. Establish first-party authored provenance and whether this + extraction is worthwhile; the checker cannot. Retain \`evidence_paths\`/\`helper_target\`. 2. **Run the checker.** From the repository root, pass the current finding as - literal JSON on stdin. Replace REVIEW_START with this pass's captured token + literal JSON on stdin. Replace REVIEW_START with this pass's token and the example paths/symbol with actual evidence. Keep the quoted delimiter. \`\`\`bash @@ -146,17 +147,17 @@ export function generateSharedCodeReuse(ctx: TemplateContext): string { GSTACK_SHARED_LIBS_REUSE_JSON \`\`\` -3. **Act on its result.** Read the JSON. Only \`reusable: true\` permits suppression. +3. **Act on its result.** Only \`reusable: true\` permits suppression. False, command failure or unreadable output requires fresh source review and a new decision, never suppression. Do not supply your own snapshot, prior record or coverage. -4. **Persist through the logger.** The logger recomputes final coverage; never - supply proof yourself. Real defects retain normal Fix-First handling independently. +4. **Persist through the logger.** The logger recomputes coverage; never supply + proof. Real defects retain normal Fix-First handling. -**What a reusable result proves (do not reconstruct these checks yourself):** -- Identity: \`sharedLibsFingerprint\` plus the actual repo, raw branch and current snapshot. +**What a reusable result proves (never reconstruct these checks):** +- Identity: \`sharedLibsFingerprint\` plus the repo, raw branch and current snapshot. The checker reads REVIEW_START without consuming/replacing it. Sanitized branch names are not identity. - Prior decision: completed/converged review, verified binding, explicit Skip and - logger-versioned \`snapshot_covered_paths\`; older unversioned coverage needs a fresh decision. + logger-versioned \`snapshot_covered_paths\`; unversioned coverage needs a fresh decision. - Source: \`canReuseSharedLibsAdvisory\` requires every supporting path's raw file byte-for-byte with its blob. Exclude assume-unchanged, skip-worktree and sparse index entries; symlinks/ancestors, submodules, ignored/outside or unreadable files; diff --git a/ship/sections/shared-code-reuse.md b/ship/sections/shared-code-reuse.md index 4e4d6ba10..9aca55812 100644 --- a/ship/sections/shared-code-reuse.md +++ b/ship/sections/shared-code-reuse.md @@ -2,11 +2,12 @@ **Reuse a skipped shared-code advisory only with complete structural evidence:** -1. **Read the evidence.** Read all supporting callers and the helper destination. - Establish first-party authored provenance and whether the current extraction - is worthwhile; the checker cannot decide that. Retain `evidence_paths`/`helper_target`. +1. **Read the evidence.** Read all supporting callers and the helper target + read-only: `git update-index`, even `--really-refresh`, clears the index flags + the checker reads. Establish first-party authored provenance and whether this + extraction is worthwhile; the checker cannot. Retain `evidence_paths`/`helper_target`. 2. **Run the checker.** From the repository root, pass the current finding as - literal JSON on stdin. Replace REVIEW_START with this pass's captured token + literal JSON on stdin. Replace REVIEW_START with this pass's token and the example paths/symbol with actual evidence. Keep the quoted delimiter. ```bash @@ -15,17 +16,17 @@ GSTACK_SHARED_LIBS_REUSE_JSON ``` -3. **Act on its result.** Read the JSON. Only `reusable: true` permits suppression. +3. **Act on its result.** Only `reusable: true` permits suppression. False, command failure or unreadable output requires fresh source review and a new decision, never suppression. Do not supply your own snapshot, prior record or coverage. -4. **Persist through the logger.** The logger recomputes final coverage; never - supply proof yourself. Real defects retain normal Fix-First handling independently. +4. **Persist through the logger.** The logger recomputes coverage; never supply + proof. Real defects retain normal Fix-First handling. -**What a reusable result proves (do not reconstruct these checks yourself):** -- Identity: `sharedLibsFingerprint` plus the actual repo, raw branch and current snapshot. +**What a reusable result proves (never reconstruct these checks):** +- Identity: `sharedLibsFingerprint` plus the repo, raw branch and current snapshot. The checker reads REVIEW_START without consuming/replacing it. Sanitized branch names are not identity. - Prior decision: completed/converged review, verified binding, explicit Skip and - logger-versioned `snapshot_covered_paths`; older unversioned coverage needs a fresh decision. + logger-versioned `snapshot_covered_paths`; unversioned coverage needs a fresh decision. - Source: `canReuseSharedLibsAdvisory` requires every supporting path's raw file byte-for-byte with its blob. Exclude assume-unchanged, skip-worktree and sparse index entries; symlinks/ancestors, submodules, ignored/outside or unreadable files; diff --git a/test/shared-libs-index-flags-readonly.test.ts b/test/shared-libs-index-flags-readonly.test.ts new file mode 100644 index 000000000..6c5b38f6b --- /dev/null +++ b/test/shared-libs-index-flags-readonly.test.ts @@ -0,0 +1,40 @@ +/** + * Train gate 40619718717 (798a37d8) slice 14, shared-libs-review-index-flags, assume-unchanged trial: while + * gathering evidence the actor ran `git update-index --really-refresh`, which cleared the assume-unchanged flag + * the checker reads, so the prerequisite binding went stale. The skill's evidence step now says it is read-only. + */ +import { afterAll, expect, test } from 'bun:test'; +import { execFileSync } from 'node:child_process'; +import * as fs from 'node:fs'; +import * as path from 'node:path'; +import { fixtureGit, type SharedLibsFixture } from './helpers/shared-libs-eval-fixture'; +import { checkPathReviewPrerequisites, preparePathEligibilityFixture } from './helpers/shared-libs-path-fixture'; + +const ROOT = path.resolve(import.meta.dir, '..'); +const fixtures: SharedLibsFixture[] = []; +afterAll(() => { for (const f of fixtures) fs.rmSync(f.root, { recursive: true, force: true }); }); + +test.each(['review', 'ship'])('%s: the shared-code evidence step forbids the index refresh the captured actor ran', skill => { + const section = fs.readFileSync(path.join(ROOT, skill, 'sections/shared-code-reuse.md'), 'utf8'); + const step = section.slice(section.indexOf('1. **Read the evidence.**'), section.indexOf('2. **Run the checker.**')).replace(/\s+/g, ' '); + expect(step).toContain('read-only: `git update-index`, even `--really-refresh`, clears the index flags the checker reads'); +}); + +test.each([ + ['captured refresh', 'git update-index --really-refresh >/dev/null 2>&1; echo "(no index mutation above intended)"', false], + ['captured read-only inspection', 'git ls-files -v; git status --porcelain=v1; git hash-object --no-filters -- src/retry-route.ts; git ls-files --debug src/retry-route.ts', true], +] as const)('%s against the assume-unchanged fixture: the prerequisite detector sees it (%s)', (_, command, unchanged) => { + const prepared = preparePathEligibilityFixture('assume-unchanged'); + const f = prepared.fixture; + fixtures.push(f); + const before = checkPathReviewPrerequisites(f, prepared.resumed.input); + expect(before.settled).toBe(true); + expect(fixtureGit(f, 'ls-files', '-v', 'src/retry-route.ts')).toMatch(/^h /); + execFileSync('bash', ['-c', command], { cwd: f.repo, env: { ...process.env, ...f.env }, encoding: 'utf8', timeout: 30_000 }); + const after = checkPathReviewPrerequisites(f, prepared.resumed.input); + if (unchanged) expect(after).toEqual(before); + else { + expect(fixtureGit(f, 'ls-files', '-v', 'src/retry-route.ts')).toMatch(/^H /); + expect({ current: after.current, settled: after.settled }).toEqual({ current: false, settled: false }); + } +});