Skip to content
Merged
12 changes: 12 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,15 @@

# Agent-doc, local-only (untracked 2026-06-21)
PLAYBOOK.md

# Repo-local gstack state stays untracked EXCEPT the QA recipe, which the
# qa:browser recipe schema requires committed (see
# qa/skills/browser/references/recipe-schema.md). The leading `!.gstack/`
# keeps the DIRECTORY includable (the machine-global ignore excludes
# `.gstack/` wholesale, and git cannot re-include children of an excluded
# dir); `.gstack/*` then untracks its contents except the recipe path.
!.gstack/
.gstack/*
!.gstack/qa-quincey/
.gstack/qa-quincey/*
!.gstack/qa-quincey/recipe.yml
2 changes: 1 addition & 1 deletion eng/.claude-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "eng",
"version": "2.6.5",
"version": "2.7.0",
"description": "Engineer Ernie, the engineering persona. eng:cr is his master code-review skill and the single local review path the ~/dev merge gate keys on: it risk-tiers depth, runs the pr-review-toolkit lenses, and mints the merge-clearance stamp. It routes to cr-teammate (review someone else's PR and post a comment) and to address-pr-feedback / pr-watcher (respond to review feedback). He also spikes the riskiest unknown before building, generates CodeRabbit config, and builds macOS Shortcuts. The plugin also SHIPS Ernie's PR-lifecycle enforcement hooks (hooks/hooks.json): the ship-PR gate (PRs only via /ship), the merge-clearance gate (no merge without the cleared gauntlet AND a /land-and-deploy sentinel, making /land-and-deploy the single CLI merge path), the /ship and /land-and-deploy sentinels, the review stamp recorder, and an after-ship CodeRabbit-watcher nudge (after a genuine /ship opens a PR, points the agent at /eng:pr-watcher, or when CodeRabbit is rate-limited routes to /land-and-deploy if a current /eng:cr review backstops the head, else to /eng:cr and then /land-and-deploy), active in opted-in repos (.ship-gate.json / .merge-clearance.json) under ~/dev. Skills: eng:cr, eng:cr-teammate, eng:address-pr-feedback, eng:pr-watcher, eng:spike, eng:coderabbit-config, eng:shortcut.",
"author": {
"name": "Mujtaba Badat",
Expand Down
4 changes: 2 additions & 2 deletions eng/hooks/scripts/ship-watch-nudge-lib.sh
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@
# opens a PR in an opted-in ~/dev repo, it decides WHAT to nudge the main agent
# toward and builds the additionalContext string.
#
# The "why": a hook cannot launch a foreground skill (/eng:pr-watcher pairs the
# main agent with a sensor subagent), but it CAN inject additionalContext that the
# The "why": a hook cannot launch a foreground skill (/eng:pr-watcher's dispatcher
# loop runs in the main agent's own turn), but it CAN inject additionalContext that the
# model reads next turn. So the durable, reliable mechanism is an auto-NUDGE, not
# an auto-run. The nudge is rate-limit-aware: it must not push the agent into an
# open-ended watch loop when CodeRabbit will not actually review (rate-limited).
Expand Down
4 changes: 2 additions & 2 deletions eng/hooks/scripts/ship-watch-nudge.sh
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
# shipped PR unwatched.
#
# Why a NUDGE and not an auto-run: a hook cannot launch a foreground skill. The
# canonical watcher /eng:pr-watcher pairs the main agent (dispatcher + fix-applier)
# with a passive sensor subagent, so it can only run in the main turn. A PostToolUse
# canonical watcher /eng:pr-watcher runs its dispatcher loop (and its foreground
# sensor script) in the main agent's own turn, so only the model can start it. A PostToolUse
# hook CAN return additionalContext that the model reads next turn - verified against
# Claude Code 2.1.x: {"hookSpecificOutput":{"hookEventName":"PostToolUse",
# "additionalContext":"..."}} on stdout with exit 0 reliably reaches the model. So
Expand Down
30 changes: 29 additions & 1 deletion eng/skills/pr-watcher/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,34 @@
# eng:pr-watcher changelog

## Unreleased
## v4 - deterministic sensor script (no more sensor subagent)

The sensor is now `scripts/sensor-poll.sh`, a deterministic bash script the
dispatcher runs in FOREGROUND Bash slices (~9 min each, `continue` outcome +
`sensor-state.json` spanning the 30-minute cycle budget), printing exactly one
JSON object per invocation. The general-purpose sensor subagent is removed.

Why: the subagent contract ("block 30 minutes in one agent turn, end with one
JSON") was structurally unsatisfiable. Foreground sleep is blocked for agents,
so the model reached for background tasks and Monitor, both of which END the
agent's turn, which the dispatcher reads as the final answer. Observed live on
email-hero PR 79 (2026-07-20): the sensor parked twice on monitors whose
conditions fired correctly within ~1 minute of CodeRabbit finishing, with no
agent left to consume them, while the dispatcher waited 10+ minutes. A script
that sleeps internally satisfies the one-JSON contract by construction and
removes the prompt-drift surface entirely.

Also in v4: pure decision logic extracted to `scripts/sensor-poll-lib.sh` with
bats coverage (`tests/sensor-poll.bats`); robust gh/jq resolution under Claude
Code's stripped PATH (the incident's first poll script died on a hardcoded
`/opt/homebrew/bin/jq`); persistent-API-failure ticks surface as a new
`outcome: error` with `error_message` instead of hanging. The v3 protocol
semantics (status-primary polling, comment-stream fallback, init-pass
`already_settled` / `cr_failure` / backlog-drain branches, settle conditions)
carry over, with one deliberate broadening: the `Actionable comments posted:`
fallback settle marker now matches any new CR item body, not only review
bodies.

Also shipping with v4 (was pending as Unreleased):

- Dropped the test-command gate from the skill contract. The watcher no longer
asks for or runs a test command before pushing a CR-induced fix. Friction
Expand Down
Loading
Loading