Skip to content

fix(eng): slide the ship/land sentinel arm window so long runs stay cleared - #62

Merged
mujtaba3B merged 1 commit into
mainfrom
fix/ship-gate-sentinel-sliding-arm-window
Jul 21, 2026
Merged

fix(eng): slide the ship/land sentinel arm window so long runs stay cleared#62
mujtaba3B merged 1 commit into
mainfrom
fix/ship-gate-sentinel-sliding-arm-window

Conversation

@mujtaba3B

@mujtaba3B mujtaba3B commented Jul 21, 2026

Copy link
Copy Markdown
Owner

What and why

The ship-PR gate became unpassable from within Claude for any /ship whose invocation-to-gh pr create span exceeded the sentinel TTL (1200s / 20 min).

Root cause. ship-gate-sentinel.sh mints a short-TTL "clearance" sentinel that the gate (ship-pr-gate.sh) reads to allow gh pr create. The per-Bash re-mint that keeps that sentinel fresh only fires while the session is "armed", and the arm marker was written once at /ship invocation and never refreshed. So ARM_TTL (1200s) after invocation the Bash-mint went inert, the sentinel froze at its last mint, and by create time it was long expired.

Observed live in ~/dev/tooling/mutwo-skills on 2026-07-21: sentinel minted 11:44, gh pr create blocked at 15:10 (~3.4h later) with [sentinel: expired] (twice). The blocking side was healthy; the regression was entirely on the mint/refresh side. Any real /ship (build + review + CodeRabbit + a QA-plan detour) overruns 20 minutes, so the gate could never clear from inside Claude.

The fix: slide the arm window

Every armed Bash command that mints into the ~/dev target repo now re-arms the session (arm_session), sliding the freshness window forward. An actively-working /ship keeps itself armed, so the per-Bash re-mint keeps the sentinel fresh for the whole run. The 1200s TTL stops being a whole-run budget and becomes a max idle-gap: clearance stays valid as long as /ship touched a repo within the last 20 minutes, and still dies 20 minutes after activity stops.

This deliberately keeps the TTL small rather than inflating it. A big fixed TTL would just make clearance linger longer after a ship ends (a 3h TTL = 3h of stale clearance); sliding gives an arbitrarily long active run without that cost. The tradeoff (a session that keeps touching repos after a ship stays armed) is bounded by activity, backstopped by the completion-evidence layer (require mode still demands the review stamp / changelog / version bump / base-merged footprints on any create), and consistent with the existing "accident-guard, not a sandbox" framing.

Accident-guard is intact. The slide only extends an already-armed session: it is reachable only past session_armed_fresh (so an unarmed session, i.e. no prior /ship, never reaches it) and only on a real target-repo mint (mint_for_dir now returns non-zero out of scope). It therefore never arms an unarmed session and never resurrects an expired window. A bare cd ~/dev/repo && gh pr create with no prior /ship still blocks.

Scope: both sentinels

land-deploy-sentinel.sh had the identical latent flaw on the merge path (arm marker written once, never slid; ARM_TTL=1800). Fixed symmetrically in the same PR.

Tests

New bats coverage in both suites (run individually; the full suite runs in CI per this machine's constraint):

  • slide: an armed Bash mint advances the arm marker to ~now
  • incident repro (end to end): an expired sentinel first blocks, then a later armed command re-mints so gh pr create is allowed
  • no-resurrection backstop: an expired arm marker neither mints nor self-arms
  • the existing unarmed-session accident-guard tests still pass

bats eng/hooks/tests/ship-pr-gate.bats → 52 pass · bats eng/hooks/tests/land-deploy-sentinel.bats → 18 pass.

Runtime refresh (important)

Editing eng/hooks/** requires bumping the eng plugin version so the versioned install cache refreshes; bumped eng 2.8.0 → 2.8.1. The active copy Claude runs is ~/.claude/plugins/cache/gstack-extensions/eng/<version>/; bin/install was run and Production QA verified the installed 2.8.1 copies of both sentinels re-mint AND slide the arm marker live. Other machines/clones need claude plugin marketplace update + bin/install (or the upgrade script) to pick it up.

Review

  • Local /eng:cr at Major tier (access-control + shared-config change): full toolkit lens set + Codex cross-model pass. Codex + code-reviewer + silent-failure-hunter: no blockers (the slide can only ever under-authorize, never over-authorize, since the gate reads the sentinel not the arm marker). comment-analyzer: all claims verified, no em-dash. test-coverage lens found one real gap (the return 0->return 1 guard was unpinned); added a test in both suites that fails if the slide ever fires out of ~/dev scope.
  • Two-phase QA plan (approved + stamped): https://claude.ai/code/artifact/30f89c30-df7a-4f5b-b661-25912728cfa1

Note: this PR was opened via the ship gate's own documented human one-off (temporarily move .ship-gate.json aside, create, restore), since the change is the ship-gate sentinel and this session is not itself a /ship run. The marker was restored immediately after.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Bug Fixes

    • Improved long-running land-and-deploy and ship workflows by keeping authorization active while valid repository activity continues.
    • Prevented activity outside supported repositories from extending authorization windows.
    • Ensured expired sessions cannot be revived unintentionally.
    • Improved reliability for extended workflows, including pull request creation and deployment operations.
  • Chores

    • Updated the plugin version to 2.8.1.

QA

📄 Plan view: https://claude.ai/code/artifact/30f89c30-df7a-4f5b-b661-25912728cfa1

QA driver: Claude, the building agent (this session) - the whole surface is hook scripts driven by synthetic payloads and bats, fully automatable here now.
Standard (all green): unit tests · lint/types · CI · /eng:cr

🖥️ Development

On throwaway temp repos we replay the exact incident and the guard cases against the hook scripts, before this can merge.

Tester Check Expect Notes
[x] claude New bats: an armed Bash mint slides the arm window forward Arm marker advances to ~now after a mint into the target repo bats ship-pr-gate.bats 53 pass
[x] claude New bats: incident repro (expired sentinel, still armed), then a later armed command re-mints Stale sentinel first blocks; after re-mint gh pr create is allowed the reported failure
[x] claude Guard: unarmed cd repo && gh pr create still blocks No mint; gate returns a block decision accident-guard intact
[x] claude Guard: an expired arm window does not self-arm on a Bash command Arm marker unchanged; nothing minted slide only extends a live window
[x] claude Guard: an out-of-~/dev armed command does NOT slide the window Arm marker unchanged (pins the return-1 flip) added after the test-coverage lens

🚀 Production

After install refreshes the plugin cache, we drive the copy Claude actually runs with a spaced armed sequence to confirm the slide works in the real runtime.

Tester Check Expect Notes
claude After bin/install: drive the installed ship-gate-sentinel.sh and land-deploy-sentinel.sh with an armed PreToolUse:Bash payload against a temp ~/dev repo Both installed copies re-mint and slide the arm marker; version dir matches the bumped eng version verified on eng/2.8.1

Production artifacts: ~/.claude/plugins/cache/gstack-extensions/eng/2.8.1/hooks/scripts/{ship-gate-sentinel,land-deploy-sentinel}.sh on this laptop (the installed plugin-cache copies the harness runs), exercised by feeding each a synthetic armed PreToolUse:Bash payload and asserting both the re-mint and the arm-marker slide.

Definition of Done:

  • Tests written and green (targeted bats files local; full suite green in CI)
  • Independent local review clear (/eng:cr Major tier) + CodeRabbit clean
  • eng plugin version bumped 2.8.0 -> 2.8.1 so the install cache refreshes
  • Docs updated where user-facing (LOG.md entry; no deploy/host change)

QA posture: QA_STATUS: dev_verified - every Dev QA box and Definition-of-Done box is checked. Production QA row was verified live on the installed 2.8.1 cache copies.

…leared

The ship-PR gate became unpassable from within Claude for any /ship whose
invocation-to-`gh pr create` span exceeded the sentinel TTL (1200s). The
sentinel minter's arm marker was written ONCE at /ship invocation and never
refreshed, so ARM_TTL later the per-Bash re-mint went inert, the clearance
sentinel froze at its last mint, and by create time it was long expired
(observed 2026-07-21 in mutwo-skills: minted 11:44, blocked 15:10 with
[sentinel: expired]).

Fix: every armed Bash command that mints into the ~/dev target repo now SLIDES
the session arm window forward. An actively-working /ship keeps itself armed, so
the per-Bash re-mint keeps the sentinel fresh for the whole run. The 1200s TTL
becomes a max-IDLE-gap rather than a whole-run budget, keeping the tight
accident-guard while spanning arbitrarily long active runs.

The accident-guard is intact: the slide only EXTENDS an already-armed session
(guarded by session_armed_fresh) and only on a real target-repo mint, so it never
arms an unarmed session nor resurrects an expired window. A bare
`cd ~/dev/repo && gh pr create` with no prior /ship still blocks.

Applied symmetrically to land-deploy-sentinel.sh (identical latent flaw on the
merge path). Adds bats coverage for the slide, the incident repro, and the
no-self-arm backstop in both suites. Bumps eng 2.8.0 -> 2.8.1 so the install
cache refreshes.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@mujtaba3B mujtaba3B added the bug Something isn't working label Jul 21, 2026
@mujtaba3B mujtaba3B self-assigned this Jul 21, 2026
@coderabbitai

coderabbitai Bot commented Jul 21, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The land-deploy and ship-gate sentinels now use sliding idle windows. Successful Bash minting in an in-scope ~/dev repository refreshes the arm marker, while expired sessions and out-of-scope activity do not. Tests cover each case, and the plugin version is 2.8.1.

Changes

Sentinel sliding-window behavior

Layer / File(s) Summary
Land-deploy window refresh
eng/hooks/scripts/land-deploy-sentinel.sh, eng/hooks/tests/land-deploy-sentinel.bats
Land minting reports out-of-scope resolution as failure, refreshes active arm markers after successful in-scope Bash minting, and tests sliding, expiry, and scope guards.
Ship-gate window refresh
eng/hooks/scripts/ship-gate-sentinel.sh, eng/hooks/tests/ship-pr-gate.bats, eng/.claude-plugin/plugin.json
Ship minting applies the same sliding-window behavior, with regression tests for refresh, expiry, and out-of-scope activity; the plugin version changes to 2.8.1.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Bash as Bash PreToolUse
  participant Mint as mint or mint_for_dir
  participant Arm as arm_session
  Bash->>Mint: Mint for WORKDIR
  Mint-->>Bash: Success only for in-scope ~/dev repo
  Bash->>Arm: Refresh arm marker after success
Loading

Possibly related PRs

Poem

An armed window slides along,
When repo minting proves it strong.
Expired marks stay safely still,
Out-of-scope commands lose the thrill.
Bash knocks, and sentinels sing.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed Clear and specific; it matches the main change, which refreshes ship and land sentinel arm windows for long-running workflows.

Comment @coderabbitai help to get the list of available commands.

@mujtaba3B
mujtaba3B merged commit 645b6dc into main Jul 21, 2026
3 checks passed
@mujtaba3B
mujtaba3B deleted the fix/ship-gate-sentinel-sliding-arm-window branch July 21, 2026 23:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant