Round-1 blockers, all three reviewers concurring:
- COMMENTED agreement now counts: agreement_signal recognizes the live bots'
durable markers (Verdict: Approve / I agree with everything / leading ✅) —
the gate to needs-human can actually close. Formal verdicts remain the
contract (CONTRIBUTING), this is the documented transitional workaround.
- Every counting verdict is bound to the head SHA; a stale approval parks the
PR in addressing (agent owes re-request) instead of promoting unreviewed
code. CHANGES_REQUESTED blocks at any head, per GitHub's own semantic.
- reconcile serializes under ONE job-level concurrency group; scope stays
per-PR. No more cron-vs-event race on the request-the-human-once guard.
- Sweep resilience: per-PR subshell (one failure logs and continues), label
edits warn instead of wedging; the self-heal claim now matches reality
(dispatch-only bootstrap).
- The state machine is extracted pure (globals in, state out) and sourceable:
test/labels-reconcile.sh proves 14 fixture transitions — comment-only
agreement, stale approval, comment-without-verdict, human precedence and
human-block — wired into CI.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The machinery LABELS.md promised. labels.yml runs the reconciler on a
15-minute cron plus PR events (pull_request_target — every PR here is from a
fork, where pull_request gets a read-only token; no PR code is ever checked
out). The script derives each open PR's state:* from GitHub's own facts and
converges labels statelessly; stale is judged from real activity (commits,
comments, reviews), never label churn, so the sweep cannot un-stale its own
mark. actions/labeler applies scope:* from changed paths. CONTRIBUTING.md is
the guideline: the PR loop, and who sets which labels. Rehearsed with
DRY_RUN=1 against the live repo; shellcheck-clean.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The db PR only unit-tested arg parsing; this adds executable proof that
dump/restore actually works end to end.
- test/db-integration.sh: stands up two throwaway Postgres containers whose
superusers DIFFER by construction (src_super vs dst_super), seeds a known
checksummable fixture, runs the real `rig db dump`/`rig db restore`, and
reads the rows back out — proving both invariants db.sh cares about: the
code reads the container's OWN $POSTGRES_USER/$POSTGRES_DB (a hardcoded
`postgres` would break on the non-default source superuser), and
--no-owner --no-acl makes the dump portable across differing superusers (a
plain dump would abort under ON_ERROR_STOP=1 on the missing role). Also
asserts default-outfile naming, restore idempotency (--clean --if-exists),
and the named-[db] scratch-database path. Skips cleanly (exit 0) when Docker
is absent/unreachable or root is unobtainable; always cleans up via trap.
- ci.yml: separate `db-integration` job on ubuntu-latest (Docker preinstalled),
kept apart from the fast shellcheck+cli.sh `check` job so an image pull can't
slow lint feedback.
- README: "Verifying a dump/restore actually works" — the safe manual
round-trip against a real Coolify container via a fresh scratch db, echoing
"a backup you have never read back is not yet a backup."
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The hand-listed inputs under-covered silently: commands/*.sh does not glob
into lib/, and the previous fix patched that by adding one more path — which
leaves the same hole open for the next subdirectory. Verified: with a broken
script at commands/deep/nested/bad.sh, the hand-listed invocation still exits 0.
`shopt -s globstar` + bin/* **/*.sh covers every script in the repo, including
the extensionless bin/ entrypoints. The list is printed before it runs, so
coverage is visible in the CI log rather than assumed.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The runner-* commands source commands/lib/runner-config.sh. Without -x,
shellcheck will not follow that source even though the files carry a
`source=SCRIPTDIR/...` directive, so it reported SC1091 on all three —
and shellcheck exits non-zero on any finding, info-level included.
commands/*.sh does not glob into lib/, so runner-config.sh was being
linted by nothing at all. List it explicitly.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>