Background
PR #131 added 7 safety checks to the PR-review agent. It was closed unfixable
(28 merge-conflict sentinels) because its supporting script changes were
restructured out from under it by the dev-lead refactor on main. The check
semantics are still worth landing; reference PR #131 branch
claude/issue-43-20260511-2113, commit ed7d462.
The 7 checks: (1) CI-weakening detection, (2) prompt-injection scanning in
workflows, (3) large-PR gating, (4) PR-description quality scoring,
(5) critical-path tracing, (6) duplication search, (7) dependency-risk
assessment.
Revised implementation plan (determinism-first)
Correction to the original premise. The original issue said the checks are
"purely prompt-level additions to agents/pr-reviewer.md, no script changes
needed." That is inaccurate for the operational pipeline. agents/pr-reviewer.md
is only the manually-invoked Copilot profile. The automated cascade
that reviews every PR runs prompts/triage.md (Tier 1, no tools) →
prompts/deep-review.md (Tier 2, has search) → prompts/security-audit.md
(Tier 3), driven by scripts/review-one-pr.sh. To make these checks actually
fire on real PRs they must live in the cascade, not only the Copilot profile.
Design principle (required): deterministic wherever possible, LLM only where
unavoidable. Mechanical, pattern-matchable checks must NOT be left to the LLM
to "eyeball" — they must be computed in shell so they are reproducible and
unit-testable. The LLM is reserved for genuinely semantic judgment.
Determinism split
| # |
Check |
Mechanism |
Where |
| 1 |
CI weakening |
Deterministic — grep diff for skipped/deleted tests (skip, only, xit, it.skip, test.skip, .todo, @Ignore), if: false, continue-on-error: true, commented-out CI steps, and lowered numeric thresholds (compare -/+ lines in coverage/CI config). Hard-stop. |
new scripts/lib/safety-checks.sh |
| 2 |
Prompt injection |
Deterministic — scan changed .github/workflows/*.yml for ${{ github.event.* }} used inside a run: step, LLM output interpolated into shell, over-broad token perms, pull_request_target + PR-head checkout without trust gate. Hard-stop. |
scripts/lib/safety-checks.sh |
| 3 |
Large-PR gate |
Deterministic — numeric from PR_METADATA (changedFiles, additions, deletions) + regex for an implementation-plan/breakdown section in the body. |
scripts/lib/safety-checks.sh |
| 4 |
Description quality |
Deterministic — regex presence of the 5 required sections (problem statement, risk category, test plan, rollback, monitoring). Count missing; ≥3 missing → escalate. |
scripts/lib/safety-checks.sh |
| 7 |
Dependency risk |
Hybrid — deterministic parse of lockfile/manifest diff hunks (added deps, unpinned/^/~/latest ranges) in the lib; LLM narrative / CVE interpretation in deep-review. |
lib + prompts/deep-review.md |
| 5 |
Critical-path trace |
LLM (semantic) — data-flow tracing, permission checks on all auth branches, boundary-condition coverage. |
prompts/deep-review.md |
| 6 |
Duplication search |
Hybrid — Tier 2 has the search tool: deterministic candidate gathering, LLM adjudicates true duplication. |
prompts/deep-review.md |
Architecture (mirror the existing downstream-impact feature)
There is already a proven pattern for injecting a deterministic, pre-computed
block into the LLM cascade. Follow it exactly:
scripts/lib/downstream-impact.sh → assemble_downstream_impact() writes a
structured block; review-one-pr.sh inlines it into the triage prompt via
downstream_impact_triage_section; gated behind DOWNSTREAM_IMPACT_ENABLED
so that when the flag is off the triage prompt is byte-identical to
pre-feature behavior. Tests: tests/test_downstream_impact_*.bats,
tests/test_downstream_impact_triage_inline.bats.
Do the same for safety checks:
- New
scripts/lib/safety-checks.sh — pure functions that take PR_METADATA
(JSON from gh pr view) and PR_DIFF and emit a structured SAFETY_CHECKS
block: per-check findings with severity, file:line, and two hard-stop
flags CI_WEAKENING_DETECTED / PROMPT_INJECTION_DETECTED. No network, no
gh calls — deterministic and side-effect-free so it is unit-testable.
- Wire into
scripts/review-one-pr.sh — compute the block before the Tier 1
prompt is assembled and inline it into TRIAGE_PROMPT_FILE (next to the
DOWNSTREAM_IMPACT / ADVISORY_BOT_FEEDBACK sections). Also write it to a
file (mirror ADVISORY_BOT_FEEDBACK_FILE) so Tier 2/3 can read it. Gate
behind a SAFETY_CHECKS_ENABLED flag; default it ON (these are
safety-critical), but keep the flag for rollback and holdout-eval stability
(scripts/lib/holdout-guard.sh). When off, the prompt must be byte-identical.
prompts/triage.md — consume the SAFETY_CHECKS block: treat
CI_WEAKENING_DETECTED and PROMPT_INJECTION_DETECTED as forced
escalate:true and never-approve; large-PR gate and description-missing≥3
→ escalate. The prompt must NOT re-derive these mechanically (no tools in
Tier 1) — it consumes the pre-computed verdicts.
prompts/deep-review.md — add the LLM-only checks: critical-path trace
(5), duplication adjudication using the search tool (6), dependency-risk
narrative (7). Reference the deterministic hard-stops as blocking.
agents/pr-reviewer.md — document all 7 checks in the human-facing
Copilot profile so the manually-invoked agent stays consistent with the
cascade (this satisfies the literal original ask; the operational teeth are
in the lib + prompts).
Testing (required — the whole point of determinism)
tests/test_safety_checks.bats — unit-test each deterministic check with
fixture diffs/metadata: CI-weakening true/false (incl. threshold lowering),
prompt-injection true/false, large-PR boundary (just under/over), description
scoring (0/3/5 sections present), dependency parsing (pinned vs unpinned).
- Triage-inlining test mirroring
tests/test_downstream_impact_triage_inline.bats:
block present when enabled, and prompt byte-identical when the flag is off.
- Register new test files in the
bats job list / scripts/run-bats.sh
invocation so lint CI runs them. shellcheck must pass on the new lib.
Guardrails
- Do NOT modify thin caller-stub workflows (
dev-lead.yml, pr-review.yml
header contract, etc.) beyond what AGENTS.md allows. Changes are confined to
scripts/lib/safety-checks.sh (new), scripts/review-one-pr.sh,
prompts/triage.md, prompts/deep-review.md, agents/pr-reviewer.md, and
tests/.
- Flag-off path must be byte-identical to current triage output (protects
holdout evals).
- Keep the lib free of
gh/network calls so it stays deterministic and testable.
Acceptance criteria
🤖 Generated with Claude Code
Background
PR #131 added 7 safety checks to the PR-review agent. It was closed unfixable
(28 merge-conflict sentinels) because its supporting script changes were
restructured out from under it by the dev-lead refactor on main. The check
semantics are still worth landing; reference PR #131 branch
claude/issue-43-20260511-2113, commited7d462.The 7 checks: (1) CI-weakening detection, (2) prompt-injection scanning in
workflows, (3) large-PR gating, (4) PR-description quality scoring,
(5) critical-path tracing, (6) duplication search, (7) dependency-risk
assessment.
Revised implementation plan (determinism-first)
Design principle (required): deterministic wherever possible, LLM only where
unavoidable. Mechanical, pattern-matchable checks must NOT be left to the LLM
to "eyeball" — they must be computed in shell so they are reproducible and
unit-testable. The LLM is reserved for genuinely semantic judgment.
Determinism split
skip,only,xit,it.skip,test.skip,.todo,@Ignore),if: false,continue-on-error: true, commented-out CI steps, and lowered numeric thresholds (compare-/+lines in coverage/CI config). Hard-stop.scripts/lib/safety-checks.sh.github/workflows/*.ymlfor${{ github.event.* }}used inside arun:step, LLM output interpolated into shell, over-broad token perms,pull_request_target+ PR-head checkout without trust gate. Hard-stop.scripts/lib/safety-checks.shPR_METADATA(changedFiles,additions,deletions) + regex for an implementation-plan/breakdown section in the body.scripts/lib/safety-checks.shscripts/lib/safety-checks.sh^/~/latestranges) in the lib; LLM narrative / CVE interpretation in deep-review.prompts/deep-review.mdprompts/deep-review.mdsearchtool: deterministic candidate gathering, LLM adjudicates true duplication.prompts/deep-review.mdArchitecture (mirror the existing
downstream-impactfeature)There is already a proven pattern for injecting a deterministic, pre-computed
block into the LLM cascade. Follow it exactly:
scripts/lib/downstream-impact.sh→assemble_downstream_impact()writes astructured block;
review-one-pr.shinlines it into the triage prompt viadownstream_impact_triage_section; gated behindDOWNSTREAM_IMPACT_ENABLEDso that when the flag is off the triage prompt is byte-identical to
pre-feature behavior. Tests:
tests/test_downstream_impact_*.bats,tests/test_downstream_impact_triage_inline.bats.Do the same for safety checks:
scripts/lib/safety-checks.sh— pure functions that takePR_METADATA(JSON from
gh pr view) andPR_DIFFand emit a structuredSAFETY_CHECKSblock: per-check findings with
severity,file:line, and two hard-stopflags
CI_WEAKENING_DETECTED/PROMPT_INJECTION_DETECTED. No network, noghcalls — deterministic and side-effect-free so it is unit-testable.scripts/review-one-pr.sh— compute the block before the Tier 1prompt is assembled and inline it into
TRIAGE_PROMPT_FILE(next to theDOWNSTREAM_IMPACT/ADVISORY_BOT_FEEDBACKsections). Also write it to afile (mirror
ADVISORY_BOT_FEEDBACK_FILE) so Tier 2/3 can read it. Gatebehind a
SAFETY_CHECKS_ENABLEDflag; default it ON (these aresafety-critical), but keep the flag for rollback and holdout-eval stability
(
scripts/lib/holdout-guard.sh). When off, the prompt must be byte-identical.prompts/triage.md— consume theSAFETY_CHECKSblock: treatCI_WEAKENING_DETECTEDandPROMPT_INJECTION_DETECTEDas forcedescalate:trueand never-approve; large-PR gate and description-missing≥3→ escalate. The prompt must NOT re-derive these mechanically (no tools in
Tier 1) — it consumes the pre-computed verdicts.
prompts/deep-review.md— add the LLM-only checks: critical-path trace(5), duplication adjudication using the
searchtool (6), dependency-risknarrative (7). Reference the deterministic hard-stops as blocking.
agents/pr-reviewer.md— document all 7 checks in the human-facingCopilot profile so the manually-invoked agent stays consistent with the
cascade (this satisfies the literal original ask; the operational teeth are
in the lib + prompts).
Testing (required — the whole point of determinism)
tests/test_safety_checks.bats— unit-test each deterministic check withfixture diffs/metadata: CI-weakening true/false (incl. threshold lowering),
prompt-injection true/false, large-PR boundary (just under/over), description
scoring (0/3/5 sections present), dependency parsing (pinned vs unpinned).
tests/test_downstream_impact_triage_inline.bats:block present when enabled, and prompt byte-identical when the flag is off.
batsjob list /scripts/run-bats.shinvocation so lint CI runs them.
shellcheckmust pass on the new lib.Guardrails
dev-lead.yml,pr-review.ymlheader contract, etc.) beyond what AGENTS.md allows. Changes are confined to
scripts/lib/safety-checks.sh(new),scripts/review-one-pr.sh,prompts/triage.md,prompts/deep-review.md,agents/pr-reviewer.md, andtests/.holdout evals).
gh/network calls so it stays deterministic and testable.Acceptance criteria
scripts/lib/safety-checks.shcomputes all deterministic checks with no network calls;shellcheckclean.SAFETY_CHECKSblock inlined into Tier 1 prompt whenSAFETY_CHECKS_ENABLED; byte-identical when off.prompts/triage.mdforces escalate/never-approve on the two hard-stops and consumes (does not re-derive) the deterministic verdicts.prompts/deep-review.mdcovers critical-path tracing, duplication adjudication, dependency-risk narrative.agents/pr-reviewer.mddocuments all 7 checks for the manual Copilot profile.tests/test_safety_checks.bats+ triage-inlining test added and wired into thebatsCI job; all green.🤖 Generated with Claude Code