Skip to content

chore(tooling): merge-train skill + pre-merge gh-pr-merge verification hook#193

Merged
DocGerd merged 3 commits into
developfrom
chore/176-177-merge-train-tooling
Jul 24, 2026
Merged

chore(tooling): merge-train skill + pre-merge gh-pr-merge verification hook#193
DocGerd merged 3 commits into
developfrom
chore/176-177-merge-train-tooling

Conversation

@DocGerd

@DocGerd DocGerd commented Jul 24, 2026

Copy link
Copy Markdown
Owner

Codifies the hand-driven merge lifecycle into repo-committed automation. Both are .claude/ tooling only — no app code, no user-visible behaviour, so no CHANGELOG entry.

#177 — pre-merge verification hook

.claude/hooks/premerge-verify.sh, wired as a third PreToolUse hook in the Bash matcher of .claude/settings.json. Mechanises the memorised #119 LAW on every gh pr merge (mine and the user's):

  • deny when the PR object's head.sha != the live branch-ref tip (GitHub dropped a synchronize webhook → the green checks describe a stale commit — the exact fix(pwa): dodge Pages CDN gzip-of-range on the basemap archive (#118) #119 failure) or when zero check-runs exist for that exact SHA.
  • ask (never a hard block) on anything it cannot verify: fork/deleted branch (ref 404), API/auth failure, unparseable command, no resolvable PR. Hook fragility can never block a legitimate merge.
  • silently passes through every non-gh pr merge command.

Decision logic is a pure decide() function with an offline --selftest matrix (6 cases, green). Poll commands were foreground-tested against the real API before arming (CLAUDE.md poll-loop gotcha). Live production-path tests: non-merge → pass; gh pr view → not triggered; compound git push && gh pr merge N → PR# parsed; merged-PR/deleted-branch → graceful ask.

#176 — merge-train skill

.claude/skills/merge-train/SKILL.md — the main-session serial develop merge loop (a skill, not an agent: the watcher agent is what oversleeps). Encodes: arm a check-runs Monitor → pre-merge head.sha/check-runs LAW (now backstopped by the #177 hook) → merge-commit → update-branch re-sync of the next serial PR, plus the #94 504-reconcile and #119 stale-SHA rerun procedures, and the Projects-classic REST fallbacks. Validated with skill-creator quick_validate.pySkill is valid!. Model-invocable (assistant drives develop merges); the user-only develop→main release path stays in the separate release skill.

Closes #176
Closes #177

🤖 Generated with Claude Code

…n hook

Two coupled merge-workflow tooling deliverables:

#177 — a PreToolUse guard (`.claude/hooks/premerge-verify.sh`, wired into the
Bash matcher in `.claude/settings.json`) that mechanises the #119 near-miss LAW:
before a `gh pr merge` it DENIES when the PR head.sha != the live branch-ref tip
(dropped `synchronize` webhook → stale green checks) or when zero check-runs
exist for that exact SHA, and ASKS (never hard-blocks) on any case it cannot
verify (fork/deleted branch, API/auth failure, unparseable command). Decision
logic is a pure `decide()` function with an offline `--selftest` matrix;
poll commands foreground-tested before arming (CLAUDE.md poll-loop gotcha).

#176 — `.claude/skills/merge-train/SKILL.md`, the main-session serial develop
merge loop: arm a check-runs Monitor, run the pre-merge LAW, merge, re-sync the
next PR via update-branch, plus the #94 504-reconcile and #119 stale-SHA rerun
procedures. Validated with skill-creator quick_validate.py ("Skill is valid!").

Neither is user-visible app behaviour → no CHANGELOG entry.

Closes #176
Closes #177

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Comment thread .claude/hooks/premerge-verify.sh Outdated
Comment thread .claude/hooks/premerge-verify.sh Outdated
Patrick Kuhn and others added 2 commits July 24, 2026 14:02
…runs API failure

Self-review findings on PR #193:

- Chained `gh pr merge A && gh pr merge B`: the greedy sed extracted only the
  last PR, so a fresh+green B silently vouched for an unverified A. Now: >1
  `gh pr merge` in the command -> emit_ask (merges must be serial anyway).
- The check-runs API call collapsed a failed call into COUNT=0 -> hard deny with
  a false "no check-runs exist" reason, contradicting the script's own
  ask-on-API-failure contract. Now a non-zero `gh api` exit -> emit_ask; only a
  successful 0 denies (matching the TIP_SHA lookup's degrade-to-ask behavior).

Verified: --selftest OK; chained -> ask; single live merge -> allow; failed
check-runs call -> ask branch; successful zero -> deny path.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… string as data

The substring match fired the guard whenever a command merely CONTAINED the
phrase "gh pr merge" — so commit messages, printf/echo, and grep patterns that
mention it (including this feature's own commits and tests) spammed false
approval prompts.

Now the hook anchors on the command actually BEGINNING with `gh pr merge` (after
leading whitespace / env-var / sudo / time prefixes). Data-carrying commands
begin with git/printf/grep and pass through untouched. The chained-merge `ask`
now counts only segments that themselves start with the invocation, so a
trailing `&& git commit -m '... gh pr merge ...'` is ignored. PR number is
extracted from the invocation segment (up to the first separator), not greedily
from the whole string.

Tradeoff: a compound `git push && gh pr merge N` is no longer guarded (first
token is git) — acceptable since the merge workflow runs the merge as its own
command, and false-positive spam erodes the guard's usefulness.

Verified via regression matrix: heredoc/printf/grep/echo -> pass-through; real
single + env-prefixed merge -> verify; chained -> ask; selftest OK.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@DocGerd
DocGerd merged commit 60e0f88 into develop Jul 24, 2026
5 checks passed
@DocGerd
DocGerd deleted the chore/176-177-merge-train-tooling branch July 24, 2026 12:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add a pre-merge verification hook on gh pr merge Add a merge-train skill for the main-session merge loop

1 participant