Increment 5 of #668 (Layer 0 of the design) — pure, unit-tested decision cores + the codified standard. This is where most correctness bugs get caught before a version exists — cheaper and broader than any canary-time signal.
Context / what's already done
#680/#682 set the precedent: criterion #2 of pr-auto-review's readiness gate (all required checks passing) was extracted into .github/scripts/pr-auto-review/lib/ready-check.sh (pr_auto_review_checks_ready), with bats in test/workflows/pr-auto-review/ gated by the existing PR Auto-Review Tests workflow. Increment 5 finishes the job for pr-auto-review and writes the standard.
Scope
1. Extract the remaining readiness criteria into the pure lib
Today the check-and-dispatch job in .github/workflows/pr-auto-review-reusable.yml still has criteria #1 (PR open + not draft), #3 (effective review decision ≠ CHANGES_REQUESTED), and #4 (no unresolved review threads) as inline bash. Move their decision logic into pure functions in ready-check.sh (e.g. pr_auto_review_ready <state> <is_draft> <checks_json> <required_json> <self_check> <review_decision> <unresolved_count> returning the ready/skip verdict and the decision class), so the workflow only does I/O (gather PR state, checks, review decision, unresolved-thread count via gh/GraphQL) and calls the pure core.
2. Decision-matrix bats (test/workflows/pr-auto-review/)
Cover the full matrix, one point per branch, plus combinations that exercise precedence (e.g. draft + CHANGES_REQUESTED → whichever the gate checks first):
3. Codify the standard
Add to AGENTS.md (and/or standards/agent-standards.md) a short standard: "A decision-making reusable keeps its decision logic in a pure, side-effect-free, unit-tested script (scripts/**), with the workflow as thin I/O glue. Gate the script with bats in CI on any PR that changes the reusable." Cite the canary engine (scripts/lib/canary-rollout.sh + tests/canary_rollout.bats, now CI-gated via #685) and pr-auto-review's ready-check.sh as the exemplars. Note the payoff: correctness bugs caught pre-merge, whole decision matrix covered vs one fixture point, zero canary-time cost.
Out of scope
- Extending the pattern to other agents' reusables (auto-rebase already follows it; dependabot-automerge/dev-lead are follow-ups, not this issue).
- Increment 6 (optional SUSPECT→PRE_EXISTING auto-downgrade) — deferred per design until SUSPECT toil appears.
Acceptance
Increment 5 of #668 (Layer 0 of the design) — pure, unit-tested decision cores + the codified standard. This is where most correctness bugs get caught before a version exists — cheaper and broader than any canary-time signal.
Context / what's already done
#680/#682 set the precedent: criterion #2 of pr-auto-review's readiness gate (all required checks passing) was extracted into
.github/scripts/pr-auto-review/lib/ready-check.sh(pr_auto_review_checks_ready), with bats intest/workflows/pr-auto-review/gated by the existing PR Auto-Review Tests workflow. Increment 5 finishes the job for pr-auto-review and writes the standard.Scope
1. Extract the remaining readiness criteria into the pure lib
Today the
check-and-dispatchjob in.github/workflows/pr-auto-review-reusable.ymlstill has criteria #1 (PR open + not draft), #3 (effective review decision ≠ CHANGES_REQUESTED), and #4 (no unresolved review threads) as inline bash. Move their decision logic into pure functions inready-check.sh(e.g.pr_auto_review_ready <state> <is_draft> <checks_json> <required_json> <self_check> <review_decision> <unresolved_count>returning the ready/skip verdict and the decision class), so the workflow only does I/O (gather PR state, checks, review decision, unresolved-thread count via gh/GraphQL) and calls the pure core.decision=<class>outputs the job emits (skip-draft,skip-checks-pending,skip-changes-requested,skip-unresolved-threads,dispatched) — Layer 2 decision-telemetry (increment 4) consumes them. The pure function should RETURN the class; the glue echoes it to$GITHUB_OUTPUT.pr_auto_review_checks_ready(from feat: implement issue #680 — pr-auto-review ready-check counts non-required/cancelled checks → false 'not passing', blocks auto-dispatch #682) as-is or fold it into the unified core — no behaviour change to the required-checks gate.2. Decision-matrix bats (
test/workflows/pr-auto-review/)Cover the full matrix, one point per branch, plus combinations that exercise precedence (e.g. draft + CHANGES_REQUESTED → whichever the gate checks first):
3. Codify the standard
Add to
AGENTS.md(and/orstandards/agent-standards.md) a short standard: "A decision-making reusable keeps its decision logic in a pure, side-effect-free, unit-tested script (scripts/**), with the workflow as thin I/O glue. Gate the script with bats in CI on any PR that changes the reusable." Cite the canary engine (scripts/lib/canary-rollout.sh+tests/canary_rollout.bats, now CI-gated via #685) and pr-auto-review'sready-check.shas the exemplars. Note the payoff: correctness bugs caught pre-merge, whole decision matrix covered vs one fixture point, zero canary-time cost.Out of scope
Acceptance
ready-check.shpure functions;check-and-dispatchis I/O glue that calls them.decision=<class>outputs unchanged (Layer 2 telemetry still works); required-vs-non-required gate (pr-auto-review ready-check counts non-required/cancelled checks → false 'not passing', blocks auto-dispatch #680) behaviour unchanged.