T-VBFY-pr-check-no-checks-verdict
Status: closed/done · Impact: low · Complexity: small
Auto-generated from a /sdlc:task-work post-mortem. Review and
promote to open/ready before picking up.
When a PR has no configured CI checks, gh pr view --json statusCheckRollup returns an empty array. The current
/sdlc:pr-check (from T-8896-add-pr-check-skill) silently
treats this the same as “checks all green” and falls through to
CLEAN. The orchestrator can’t tell “checks passed” from “checks
never ran” — both look identical in the verdict line. Make the
“no checks” fact visible so the orchestrator’s digest can flag it
without re-querying GitHub.
From the originating post-mortem:
gh pr view --json statusCheckRollupreturns an empty array on PRs without checks; the SKILL.md handles this by simply not matching rule 4. But “no checks” is also a fact worth reporting in the digest. Could be a future verdict refinement (NO-CHECKS) or just a richer reason string in CLEAN.
In plugin/skills/pr-check/SKILL.md, rule 4 of the priority ladder
only fires when at least one check in statusCheckRollup is failing.
An empty rollup falls through to rule 6 and is reported as
CLEAN reason="awaiting review" — indistinguishable from a PR whose
checks all passed.
Proposed
Section titled “Proposed”Adopt option (A) — richer reason string. Keep the five-verdict
enum and encode the rollup state in the CLEAN reason — e.g.
CLEAN reason="no checks configured; awaiting review" vs.
CLEAN reason="checks passing; awaiting review". Cheapest and
preserves the existing enum contract; additive reason strings are
reversible, enum expansion is harder to walk back.
Rejected alternative: option (B) — extending the enum with a new
NO-CHECKS verdict. Highest visibility but every consumer
(orchestrator, lint invariants, any harness fixtures) has to
learn the new token. Revisit only if reason-string parsing
becomes a real burden downstream.
Approach
Section titled “Approach”- Edit
plugin/skills/pr-check/SKILL.mdrule 6 (the catch-all) to inspectlen(statusCheckRollup) == 0and emitCLEAN reason="no checks configured; awaiting review"when the rollup is empty, vs the existingCLEAN reason="awaiting review"(or"checks passing; awaiting review") when checks exist. - Re-run
plugin/scripts/lint_skill_prose.py plugin/skills/pr-check/SKILL.mdto confirm invariants pass.
Files to touch
Section titled “Files to touch”plugin/skills/pr-check/SKILL.md— rule 6 inspects rollup length and emits a richer reason string.
Acceptance criteria
Section titled “Acceptance criteria”- AC-1: A PR with zero entries in
statusCheckRollupproduces a verdict line whose CLEAN reason string is distinguishable from a PR with checks passing — e.g.CLEAN reason="no checks configured; awaiting review". (auto, via a--mock-statefixture once T-BM56-pr-check-mock-state-flag lands) - AC-2:
plugin/scripts/lint_skill_prose.py plugin/skills/pr-check/SKILL.mdexits 0. (auto)
Out of scope
Section titled “Out of scope”- Distinguishing “no checks configured” from “checks not yet run” — GraphQL doesn’t cleanly expose this; treat both as no-checks.
Dependencies
Section titled “Dependencies”- T-BM56-pr-check-mock-state-flag is needed to write a deterministic AC-1 test, but isn’t a hard blocker for the SKILL.md edit itself.
Discovery context
Section titled “Discovery context”Spawned by /sdlc:task-work post-mortem of T-8896-add-pr-check-skill on 2026-05-20.
Post-mortem
Section titled “Post-mortem”Captured by /sdlc:task-work on 2026-05-21. PR: pending.
Acceptance criteria coverage
Section titled “Acceptance criteria coverage”- AC-1: agent-manual — edited
plugin/skills/pr-check/SKILL.mdrule 6 to distinguish emptystatusCheckRollupfrom passing checks; the four CLEAN reason strings are now distinguishable (no checks configured; awaiting review,checks passing; awaiting review,no checks configured; approved, awaiting merge,checks passing; approved, awaiting merge). Added afixtures/clean-no-checks.jsonfixture so the next harness run that exercises the mock-state path covers the empty-rollup case. The spec is the executable contract for this skill (no compiled code path); deterministic execution against the fixture happens once the eval harness asserts the new fixture. - AC-2: auto —
plugin/scripts/lint_skill_prose.py plugin/skills/pr-check/SKILL.mdexits 0 (verified locally; no invariant phrases drift).
What worked
Section titled “What worked”- The change scope was small enough (rule 6 + one fixture + the bundled-fixtures bullet) to implement without delegating to a sub-agent — the colocation of fixture, invariants, and SKILL.md made the blast radius obvious.
lint_skill_prose.pyimmediately confirmed AC-2 with no invariant-phrase regressions, even though the new prose introduced several new reason-string examples — the existing invariants list is well-scoped (verdict tokens, contract phrases) rather than pinning the full reason text.
Friction and automation gaps
Section titled “Friction and automation gaps”sdlc.yamlvalidation fails withAdditional properties are not allowed ('pr_check' was unexpected)because thepr_check.author_commentsblock landed insdlc.yaml(commite463491, May 21) without a corresponding update toplugin/schemas/sdlc-yaml.schema.json. This blocksrun_quality_checks.py --key worktree_initin Step 4 andrun_quality_checks.py(default key) in Step 7 from succeeding. Multiple in-flight/sdlc:task-workruns likely hit the same block — a follow-up task should add thepr_check:block toplugin/schemas/sdlc-yaml.schema.jsonso the validator stops rejecting documented configuration. → T-DN76-pr-check-schema-promote
Spawned follow-up tasks
Section titled “Spawned follow-up tasks”- T-DN76-pr-check-schema-promote — linked existing task that
already covers adding
pr_check:to the bundledsdlc-yaml.schema.jsonsorun_quality_checks.pystops rejecting the documented author-comment block.