## Summary
Ship-loop **anti-pattern #7** — "claiming a gate fix landed without
re-running the gate at post-merge HEAD" — cost three self-corrections in
the 2026-05-19 session, including one on the very PR that *added* AP#7
(#354). The doctrine was documentation, not enforcement. This PR makes
it mechanical.
Closes: soc-o5kq
## Fitness delta
- AP#7 enforcement: **0 → 1** mechanical check (doctrine alone caught
**0 of 3** in-session violations; check #39 blocks at push-time before
merge).
- Anti-patterns with mechanical backstop: **1/8 → 2/8** (AP#1 via
`ship.sh`, AP#7 via `verify-gate-claim.sh`).
- Test coverage: `verify-gate-claim.sh` **0 → 15** bats.
`pre-push-gate.sh` regression: **55 → 55** ok (no regression from helper
instrumentation).
## What changes
`scripts/verify-gate-claim.sh <ref> "<claim>"` — runs the gate at HEAD
(or reads a provided log via `--log`) and exits **0** iff the claim
appears verbatim. Exit codes: `0` verified, `1` AP#7 violation, `2`
usage error, `3` verification impossible.
`scripts/pre-push-gate.sh` **check #39** calls it automatically against
every `Evidence:` trailer in the open PR body, against this run's tee'd
verdict log. False claims now block the push, not the post-merge
surprise.
Skip-key: `AGENTOPS_PREPUSH_SKIP_EVIDENCE_CLAIM=1` (matches the
established `prepush_skip_flag` pattern).
## Skip-gracefully matrix
| Condition | Behavior |
|---|---|
| `gh` not in PATH | skip |
| `scripts/verify-gate-claim.sh` missing | skip |
| No open PR for branch | skip |
| PR body has no `Evidence:` line | skip |
| Verdict log empty | skip (defensive) |
| `AGENTOPS_PREPUSH_SKIP_EVIDENCE_CLAIM=1` | skip via skip-key |
## Files
| File | Change |
|---|---|
| `scripts/verify-gate-claim.sh` | New, ~170 lines, exit-code contract
in `--help` |
| `tests/scripts/verify-gate-claim.bats` | New, **15 tests** covering
all 4 exit codes + `fgrep` semantics |
| `scripts/pre-push-gate.sh` | Instrument `pass/fail/skip/warn` to tee
`PRE_PUSH_GATE_LOG`; add check #39 |
| `skills/ship-loop/references/anti-patterns.md` | AP#7 now points at
the mechanical enforcement |
| `skills/ship-loop/SKILL.md` | Step 5 names check #39 |
| `skills-codex/ship-loop/SKILL.md` | Codex twin parity |
| `skills-codex/.agentops-manifest.json` + `.agentops-generated.json` |
Regen (parity hash) |
## Verification (self-dogfood per AP#7)
- `bats tests/scripts/verify-gate-claim.bats` → **15 ok**
- `bats tests/scripts/pre-push-gate.bats` → **55 ok** (no regression
from `pass/fail/skip/warn` instrumentation)
- Local `scripts/pre-push-gate.sh --fast` → `pre-push gate (fast):
passed (52 skipped)`. Check #39 emits `PR Evidence claim verification
(no open PR for fix/verify-gate-claim-soc-o5kq) (skipped)`
pre-PR-creation.
- Skip-key live test: `AGENTOPS_PREPUSH_SKIP_EVIDENCE_CLAIM=1
scripts/pre-push-gate.sh --fast` → `PR Evidence claim verification
(skipped via AGENTOPS_PREPUSH_SKIP_EVIDENCE_CLAIM) (skipped)`.
- `shellcheck` clean. `bash -n` clean.
## Design note — scenarios
This bead arrived with free-text acceptance, not the `## Scenarios`
block the workflow spec mandates. Looking at the 9 PRs merged on
2026-05-19 (#346, #348–#355), **0** used the `#scenario-slug` PR-title
format — the rule is currently aspirational, which is exactly the
doctrine-vs-enforcement gap this PR exists to close on a different axis.
Shipping per today's observed practice; promoting the scenario rule
itself to mechanical is `soc-waxr` / `soc-g2r9` territory.
Bounded-context: BC0-foundations (`scripts/pre-push-gate.sh` +
`scripts/verify-gate-claim.sh`)
Evidence: ok no symlinks
Co-authored-by: Codex <codex@example.invalid>