Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .claude/agents/.AGENTS_PROVENANCE
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# VENDORED from harmon-devkit — DO NOT EDIT the managed agents here.
# source: https://github.com/evanharmon1/harmon-devkit.git
# ref: v0.29.0 (e6596fb0fa1890a187e26ed9aae170517960d861)
# ref: v0.30.0 (92451b5b419779ef7823ea8acaaad4826ffebd22)
# path: ai/agents
# names: *
# managed: implementer
Expand Down
2 changes: 1 addition & 1 deletion .claude/skills/.SKILLS_PROVENANCE
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# VENDORED from harmon-devkit — DO NOT EDIT the managed skills here.
# source: https://github.com/evanharmon1/harmon-devkit.git
# ref: v0.29.0 (e6596fb0fa1890a187e26ed9aae170517960d861)
# ref: v0.30.0 (92451b5b419779ef7823ea8acaaad4826ffebd22)
# path: ai/skills
# categories: universal, repo
# agents-dest: .claude/agents
Expand Down
32 changes: 20 additions & 12 deletions .claude/skills/shepherd/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -700,8 +700,12 @@ you rather than the bot):
reply to, so no act on GitHub can ever record that you answered it and
`check` returns exit 10 for that head forever — the deadlock the inline
adjudication path was built to end, reappearing on the two surfaces it
cannot see. Answer the finding on the PR as usual (fix it, decline it with
reasoning, or file it), then record the disposition:
cannot see. Answer the finding on the PR as usual — and note that only two
of the three answers end here. **Fixing** it means a push, which moves the
head and starts a fresh cycle that reviews the fix on its own merits;
`settle` neither applies nor accepts that disposition. For the two answers
that leave the code alone — declining with reasoning, or filing it as
follow-up work — record the disposition:

```bash
"$helper" settle --state "$state" --actor-id 199175422 \
Expand Down Expand Up @@ -1262,17 +1266,21 @@ loops indefinitely:
When current-head Codex cloud review is enabled, **Codex Automatic reviews
must be disabled in the external integration before the first promotion**.
Otherwise `gh pr ready` can start a new asynchronous review after the gate
that supposedly completed automated work. Three knobs, all of them:
that supposedly completed automated work. Three knobs carry it:
personal **Auto review** off, the repository's **Auto code review**
preference on **Follow personal**, and the repository's review **Trigger**
on Follow personal — an "On every push" trigger is dormant while Auto
review is off and arms the moment that toggle changes. GitHub does not
expose a reliable
repository API for this setting, so treat it honestly as a human-configured
prerequisite: use the repository setup record or maintainer confirmation,
never claim it was mechanically verified — a passing `readiness-gate.sh`
deliberately does not check it, and says so. If its state is unknown, stop
blocked and ask rather than promote.
preference on **Follow personal**, and the repository's
review **Trigger** on Follow personal — an "On every push" trigger is
dormant while Auto review is off and arms the moment that toggle changes.

**This is settled configuration, not a promotion-time check.** The
consuming repository's `AGENTS.md` carries the maintainer's confirmation
and its setup checklist carries the how-to; nothing in this stage gates on
it, and `readiness-gate.sh` says nothing about it either.
The one thing worth raising is an anomaly you happen to observe: if a
Codex cloud review fires **unsolicited** — after a push or a promotion
that no `@codex review` comment triggered — tell the maintainer, because
that is the signature of the knobs drifting back on. Report it and carry
on; it blocks nothing and there is no state to poll.

Report the ready state honestly rather than over-claiming:
`BLOCKED` or `REVIEW_REQUIRED` mean "ready for review and awaiting the
Expand Down
35 changes: 27 additions & 8 deletions .claude/skills/shepherd/assets/readiness-gate.sh
Original file line number Diff line number Diff line change
Expand Up @@ -55,13 +55,11 @@
# checks-indeterminate, merge-state-unknown, fetch-failed,
# malformed-data, codex-indeterminate, usage (indeterminate)
#
# Three readiness conditions are deliberately NOT verified here, because no
# Two readiness conditions are deliberately NOT verified here, because no
# API answers them — the caller must hold them as prose prerequisites:
# - required automation that reacts only to `pull_request.ready_for_review`
# (a configuration blocker: promotion would notify humans before its
# result exists);
# - the Codex Automatic-review knobs being off/Follow-personal (a
# human-configured prerequisite; GitHub exposes no reliable API for it);
# - a required context that NEVER REGISTERED on this head. The checks
# condition judges every check GitHub reports for the commit; a required
# workflow that failed to trigger appears in no list, and the only state
Expand Down Expand Up @@ -411,9 +409,19 @@ evaluate_checks() {
# suites asynchronously, so a read moments after a push reports nothing
# having run rather than nothing to run. A repo with genuinely no CI
# needs a human to say so — this gate cannot tell the two apart.
# --slurpfile over process substitution, never --argjson: a much-rerun
# head's check-runs payload exceeds the per-argument limit as argv and jq
# dies "Argument list too long", which this gate could only report as
# `malformed-data` — indeterminate for a purely mechanical reason, on
# exactly the heads it matters most for (observed on harmon-init#821's
# gate after three infra reruns, 2026-08-12, where it also masked a real
# merge-state-behind condition). printf is a shell builtin, so no exec
# carries the payload; the fd does. $runs/$statuses are bound below so the
# classification program itself is unchanged.
checks_summary="$(jq -cn \
--argjson runs "$check_runs" \
--argjson statuses "$statuses" '
--slurpfile runs_sf <(printf '%s' "$check_runs") \
--slurpfile statuses_sf <(printf '%s' "$statuses") '
$runs_sf[0] as $runs | $statuses_sf[0] as $statuses |
def run_state:
if .status != "completed" then "pending"
elif (.conclusion == "success" or .conclusion == "neutral"
Expand All @@ -432,10 +440,21 @@ evaluate_checks() {
indeterminate malformed-data "check states could not be classified"
[ "$(jq -r '.total' <<<"$checks_summary")" -gt 0 ] ||
indeterminate checks-indeterminate "GitHub reports no checks for this head — populated asynchronously, so re-poll; if the repo truly has no CI, that is a human call, not a pass"
failing_checks="$(jq -r '.failing | join(", ")' <<<"$checks_summary")"
# Bound the name lists the detail carries: with thousands of failing
# checks the joined names are themselves a multi-megabyte string, and
# emit's `jq --arg detail` puts that back into a single argv entry — the
# same ARG_MAX death the --slurpfile change above just removed, one step
# downstream. Twenty names diagnose as well as twenty thousand.
failing_checks="$(jq -r '
.failing | if length > 20
then (.[0:20] | join(", ")) + " … and \(length - 20) more"
else join(", ") end' <<<"$checks_summary")"
[ -z "$failing_checks" ] ||
fail_condition checks-failing "checks failing: $failing_checks"
pending_checks="$(jq -r '.pending | join(", ")' <<<"$checks_summary")"
pending_checks="$(jq -r '
.pending | if length > 20
then (.[0:20] | join(", ")) + " … and \(length - 20) more"
else join(", ") end' <<<"$checks_summary")"
[ -z "$pending_checks" ] ||
fail_condition checks-pending "checks not yet concluded: $pending_checks"
}
Expand Down Expand Up @@ -691,7 +710,7 @@ esac

if [ "$require_draft" = 1 ]; then
verdict_condition=ready
verdict_detail="every mechanically checkable readiness condition holds; ready_for_review-only automation and the Codex Auto-review knobs remain human-verified prerequisites"
verdict_detail="every mechanically checkable readiness condition holds"
else
verdict_condition=audit
verdict_detail="every mechanically checkable condition except the draft requirement holds; this audits an existing promotion and never authorizes gh pr ready"
Expand Down
2 changes: 1 addition & 1 deletion .skills-sync.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
source:
repo: https://github.com/evanharmon1/harmon-devkit.git
# renovate: datasource=github-releases depName=evanharmon1/harmon-devkit
ref: v0.29.0 # pinned tag — bump deliberately to a released harmon-devkit tag
ref: v0.30.0 # pinned tag — bump deliberately to a released harmon-devkit tag
categories:
- universal
- repo
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
source:
repo: https://github.com/evanharmon1/harmon-devkit.git
# renovate: datasource=github-releases depName=evanharmon1/harmon-devkit
ref: v0.29.0 # pinned tag — bump deliberately to a released harmon-devkit tag
ref: v0.30.0 # pinned tag — bump deliberately to a released harmon-devkit tag
categories:
[% for cat in skill_categories %]
- [[ cat ]]
Expand Down
Loading