@clipboard-health/ai-rules 2.29.9 → 2.30.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -98,7 +98,7 @@ Stop before continuing if:
98
98
 
99
99
  ## Helper Script
100
100
 
101
- Script paths below are written as `scripts/...`, relative to this `SKILL.md`, matching the other core skills such as `babysit-pr`.
101
+ Script paths below are written as `scripts/...`, relative to this `SKILL.md`, matching the other core skills such as `cb-babysit`.
102
102
 
103
103
  Use the helper before visual work to make the verification surface deterministic:
104
104
 
@@ -131,7 +131,7 @@ Every finding **must** include a `failure_mode`: one sentence on the concrete us
131
131
 
132
132
  ### Do-not-raise list (binding)
133
133
 
134
- - Speculative defensiveness at trusted internal boundaries.
134
+ - Speculative defensiveness at trusted internal boundaries (only where the boundary's guarantee is actually _enforced_ — a declared or cast type is not enforcement; see AntiSlop).
135
135
  - Restating the obvious ("consider a comment explaining what this does").
136
136
  - Hypothetical future-caller scenarios with no current caller.
137
137
  - Style/formatting a linter or formatter covers.
@@ -150,6 +150,8 @@ For every candidate finding, run the litmus test before keeping it: _"What is th
150
150
 
151
151
  For each change name a realistic input or condition that would expose a bug. If you cannot, do not raise it.
152
152
 
153
+ - **Declared ≠ enforced — check what actually guarantees a precondition.** When new code performs an operation that faults on a missing or malformed input (destructuring, property/array access, non-null assertion, iteration, parsing, arithmetic), do not accept a declared type, function signature, cast, or `as` as proof the input is safe — those _label_ a value, they don't _check_ it. Ask what enforces the precondition on this path: a runtime validator, a preceding explicit check, or a constructor/factory invariant. If nothing does, trace the value to its origin — it can fault on real data even though it compiles. Look at the operation that would actually throw, not only the named field beside it.
154
+ - **Asymmetric handling across sibling call sites is a likely bug, not a style nit.** When the diff guards, validates, converts, or error-wraps a value in one place but consumes the same value or shape bare elsewhere, exactly one side is usually right. Compare the call sites against each other instead of reviewing each in isolation, and resolve the inconsistency: guard missing where it's absent → bug; guard unnecessary everywhere → slop to remove.
153
155
  - Edge cases, error paths, observability of real failure modes.
154
156
  - Tests cover real risk, not lines.
155
157
  - Concurrency, performance at real scale, data integrity.
@@ -212,7 +214,7 @@ Tag every convention finding with `[CONVENTION]` in the title. Cap severity at M
212
214
 
213
215
  This PR may have been written or assisted by an LLM. For each addition, ask: _"Is this line earning its keep, or is it pattern-matching what code is supposed to look like?"_ Push back on what other lenses are too polite to flag. Apply to additions **inside the diff itself**.
214
216
 
215
- - **Defensive code at trusted internal boundaries.** Null guards on private helpers whose callers' types guarantee non-null; `try`/`catch` wrapping a single non-throwing call, or that re-throws unchanged, or that "logs and swallows" without naming what to do next; optional-chaining through types that don't include optionality.
217
+ - **Defensive code at trusted internal boundaries.** Null guards on private helpers whose callers' types guarantee non-null; `try`/`catch` wrapping a single non-throwing call, or that re-throws unchanged, or that "logs and swallows" without naming what to do next; optional-chaining through types that don't include optionality. **But "trusted" means the guarantee is _enforced_** — by a validator on this path, a constructor/factory invariant, or a preceding check you can point to. A type, signature, cast, or `as`/non-null-assertion only _asserts_ the guarantee; a guard backing a merely-asserted guarantee is load-bearing, not slop. Confirm what enforces the type at the call site before flagging the guard.
216
218
  - **Defensiveness against unrealistic product scenarios.** Litmus: _"In the real product flow this code participates in, what user action / system event / upstream call could land us in this branch?"_ If the answer is "none" or "I had to invent one to justify the guard", it's slop. Concrete shapes:
217
219
  - Null/undefined guard on an ID immediately after that ID was used to load (and find) the entity.
218
220
  - A `null`/`undefined`/`""`/`0` branch on a field whose TypeScript or Zod/class-validator already rejects those.
@@ -282,7 +284,7 @@ Does this follow our FE conventions, and will it behave correctly under realisti
282
284
  After walking the checklist, apply these filters to your candidate findings:
283
285
 
284
286
  1. **Drop findings with empty or hypothetical `failure_mode`.** "A future caller might…", "in case someone…" → drop.
285
- 2. **Self-audit for slop.** For every finding you wrote, ask: does it match a slop pattern (asks-for-defensive-guard on already-narrowed value; hypothetical future caller; restating-obvious comment request; abstract refactor with no concrete cost-of-keeping; observability without named failure mode; test for trivially-verifiable code; defends against a state the product cannot produce)? If yes and you can't write a concrete, product-specific cost in one sentence — drop it. Being your own AntiSlop reviewer is the main lever for keeping this skill honest.
287
+ 2. **Self-audit for slop.** For every finding you wrote, ask: does it match a slop pattern (asks-for-defensive-guard on an already-narrowed value — but only if an _enforced_ check narrows it, not merely a declared type or cast (see Engineering's declared ≠ enforced); hypothetical future caller; restating-obvious comment request; abstract refactor with no concrete cost-of-keeping; observability without named failure mode; test for trivially-verifiable code; defends against a state the product cannot produce)? If yes and you can't write a concrete, product-specific cost in one sentence — drop it. Being your own AntiSlop reviewer is the main lever for keeping this skill honest.
286
288
  3. **Cross-repo audit.** For every finding, ask: does the failure_mode reference a downstream actor (FE, mobile, consumer service, rolling deploy, external library user) or a contract/schema/public-artifact boundary? If yes, did you actually read the relevant external file(s) to confirm the claim, or are you reasoning from priors about "how FEs usually work"? If you didn't read it, the finding is cross-repo — route to the Cross-repo evidence policy (verify, ask for access, or downgrade to a clearly-labeled "speculative" MINOR). Do not ship a "consumer will break" finding sourced from imagination.
287
289
  4. **Drop do-not-raise items** that slipped through.
288
290
  5. **Apply the NIT gate.** NITs that don't meet (a)/(b)/(c) → drop. Kept internally but hidden by default in synthesis.
@@ -1,392 +0,0 @@
1
- ---
2
- name: babysit-pr
3
- description: "Watch a PR through CI and review feedback: commit/push, wait for CI, auto-fix high-confidence failures, reply to active review threads, address top-level Conversation-tab comments, and summarize automated review-body content with sentinel-tagged comments. Runs one pass against the current branch's PR; pass a PR number or URL to `gh pr checkout` that PR first. Use when the user says 'babysit my PR', 'babysit PR 482', 'watch my PR', 'keep my PR moving', or 'respond to comments'."
4
- argument-hint: "[pr-number-or-url]"
5
- ---
6
-
7
- # Babysit PR
8
-
9
- Watch one PR through CI, auto-fix high-confidence failures, and leave a paper-trail reply on every active review thread and automated review-body comment. Threads stay open for human resolution — this skill only posts replies, it never resolves.
10
-
11
- This skill is self-contained: it does not invoke other skills. It works in Claude Code and Codex — no subagents, no `Skill` tool calls, no `!` command interpolation, no `$CLAUDE_PLUGIN_ROOT`.
12
-
13
- ## Inputs
14
-
15
- Parse an optional PR number or URL from the invocation arguments if the host exposes them; otherwise read the user's request text. Parse in **this priority order** and stop at the first match — do not fall back to a generic "first integer in prose" regex, which would grab unrelated numbers (issue refs, quoted counts, etc.):
16
-
17
- 1. **Full PR URL** — if `$ARGUMENTS` or the user's text contains `https?://github\.com/[^/\s]+/[^/\s]+/pull/\d+`, capture the URL and pass it to `gh pr checkout` as-is.
18
- 2. **Explicit PR token** — match `(?:PR|pr|pull request)\s*#?(\d+)` or a bare `#(\d+)` in the user's text. Capture the numeric group.
19
- 3. **Bare numeric argument** — only when the entire `$ARGUMENTS` string is a positive integer (no surrounding prose).
20
- 4. **None of the above** — operate on the PR for the current branch (existing Step 2 behavior).
21
-
22
- When a match is found, the checkout happens in Preflight before Step 2.
23
-
24
- This skill always runs exactly one pass. It never waits or repeats internally. For recurring execution, wrap the call with `/loop <cadence> /babysit-pr` or an external shell `while` loop.
25
-
26
- ## Sentinels
27
-
28
- The skill uses two sentinels. Each is a visible footer line wrapped in `<sub>` (a 🤖 mark plus the token in `<code>`).
29
-
30
- **Addressed sentinel**: `<sub>🤖 <code>babysit-pr:addressed v1 core@3.9.0</code></sub>`. Appended on its own line at the end of every reply the skill posts (both thread replies and the review-body summary); this is how re-runs know which threads and review-body comments are already handled. Dedupe matches the version-agnostic substring `babysit-pr:addressed v1` followed by a space (also matches legacy `<!-- babysit-pr:addressed v1 ... -->` sentinels). Grep `babysit-pr:addressed v1` for any version; add `core@3.9.0` for a specific one.
31
-
32
- **Follow-up sentinel**: `<sub>🤖 <code>babysit-pr:followup v1 core@3.9.0</code></sub>`. Attached to replies that defer an out-of-scope comment as a tracked follow-up (see the Scope subsection and the Defer verdict in step 6). Grep `babysit-pr:followup` across PR conversation JSON to enumerate deferred items. This sentinel is additive — the post-reply scripts still append the `addressed` sentinel at the end, so a deferred thread is correctly machine-classified as addressed (the skill _has_ handled it — by deferring). Human reviewers and future sweeps distinguish deferred from resolved by looking for the follow-up sentinel.
33
-
34
- **Sentinel recency rules.** The script emits a per-thread `activityState` with three values:
35
-
36
- - **`active`** — no sentinel yet, OR at least one human commented after the last sentinel. Always handle this thread.
37
- - **`uncertain`** — a sentinel exists AND one or more bot comments appeared after it. The thread carries a `postSentinelBotComments` array listing EVERY such comment. You MUST read every entry in that array (not just the most recent — a later ack must not hide an earlier actionable finding), then decide:
38
- - **Every** post-sentinel bot comment is a non-actionable acknowledgement (`"Thanks, resolved"`, `"LGTM"`, `"Learnings added"`, etc.) → mark the thread **Skip-reply**; do not post a new reply. (See step 6a — Skip-reply is a distinct classification from the `addressed` activityState value.)
39
- - **Any** post-sentinel bot comment carries new actionable content (new nit, new finding, corrected diagnosis) → treat as **active**; reply again AND mention in the final summary that you reactivated an "uncertain" thread and why.
40
- - If you cannot confidently classify every entry → default to **active** and flag it. Silence is the failure mode we are trying to avoid.
41
- - **`addressed`** — the sentinel is the newest relevant activity on the thread. Skip it.
42
-
43
- **Bot detection** uses two signals (union): GraphQL `author.__typename == "Bot"` (primary — catches every GitHub-tagged bot, including ones not on our allowlist), plus a name allowlist (for bots that post via a User-type service account). An unknown bot never falls through to human classification, so a new review bot won't cause an infinite re-reply loop.
44
-
45
- The bot detection exists ONLY to downgrade the default for post-sentinel bot activity from `"active"` to `"uncertain"`. It NEVER suppresses bot comments or marks a thread `"addressed"` on its own — review-bot content would be lost if it did.
46
-
47
- For automated review bodies, the script emits a stable `fingerprint` per review (sha256 of the whole normalized body — collapsed whitespace, no timestamp, no author). It covers every review from a known automated reviewer (CodeRabbit, Mendral, Dependabot, etc.); the agent reads each body directly and extracts findings as part of its scope/verdict assessment, instead of relying on a fragile pre-parser. For top-level Conversation-tab comments, the script emits the same kind of `fingerprint` per comment. Dedupe happens against the `priorBabysitSentinels` array returned in the same JSON document: if a current `reviewBodyComments[].fingerprint` or `activeIssueComments[].fingerprint` already appears in any prior sentinel body, skip posting / treat it as addressed.
48
-
49
- ## One iteration
50
-
51
- Each iteration is a procedure you execute as ordinary tool calls in the same agent turn. No subagents, no `Skill` tool, no `!` prefix.
52
-
53
- ### 1. Preflight
54
-
55
- Script paths in this procedure are written as `scripts/...`, relative to this SKILL.md. Each host (Claude Code, Codex, …) resolves them from wherever it has the skill installed — do not try to derive an absolute path yourself; it will be wrong under at least one host.
56
-
57
- ```bash
58
- git status --short
59
- ```
60
-
61
- If non-empty, stop and report the dirty files. The skill refuses to start with uncommitted changes so it never sweeps up unrelated work.
62
-
63
- ```bash
64
- gh auth status
65
- ```
66
-
67
- If this fails, stop and tell the user to run `gh auth login`.
68
-
69
- If a PR number or URL was parsed from Inputs, check out that PR now:
70
-
71
- ```bash
72
- gh pr checkout <pr-number-or-url>
73
- ```
74
-
75
- This switches the local worktree to the PR's head branch (and handles forks automatically). If the command fails (PR not found, conflicting local branch, etc.), stop and report. The `git status --short` check above runs first so we never check out over dirty work.
76
-
77
- ### 2. Locate the PR
78
-
79
- ```bash
80
- gh pr view --json number,url,headRefName,statusCheckRollup,mergeable,mergeStateStatus 2>/dev/null
81
- ```
82
-
83
- If Preflight checked out a PR explicitly, `gh pr view` will find it by construction and the fallback below does not apply. The fallback only fires when no PR number was supplied and the current branch has no open PR.
84
-
85
- If no PR exists for the current branch:
86
-
87
- - Verify there are commits ahead of the base branch: `git log --oneline @{u}..HEAD 2>/dev/null || git log --oneline origin/HEAD..HEAD`. If nothing is ahead, stop and report "no commits to push".
88
- - Push the branch and open a PR:
89
-
90
- ```bash
91
- git push -u origin HEAD
92
- gh pr create --fill
93
- ```
94
-
95
- - Re-fetch `gh pr view --json number,url,statusCheckRollup,mergeable,mergeStateStatus`.
96
-
97
- #### Resolve merge conflicts (if any)
98
-
99
- If `mergeable == "CONFLICTING"` (or `mergeStateStatus == "DIRTY"`), merge the base into the PR branch locally:
100
-
101
- ```bash
102
- BASE=$(gh pr view --json baseRefName --jq .baseRefName)
103
- git fetch origin "$BASE"
104
- git merge --no-edit "origin/$BASE"
105
- ```
106
-
107
- Apply the same conservative bar as step 5: resolve directly only for lockfile/generated regenerations, additive non-overlapping edits, or trivial textual conflicts in PR-touched files. Anything semantic, ambiguous, or outside the PR's intentional surface — `git merge --abort` and skip to step 10 to exit **stuck** with a diagnosis. After a clean resolution, commit the merge and `git push origin HEAD`.
108
-
109
- ### 3. Wait for CI
110
-
111
- Wrap the watch call with a timeout so a hung check doesn't wedge the turn:
112
-
113
- ```bash
114
- rc=0
115
- if command -v gtimeout >/dev/null 2>&1; then
116
- gtimeout 600 gh pr checks --watch || rc=$?
117
- elif command -v timeout >/dev/null 2>&1; then
118
- timeout 600 gh pr checks --watch || rc=$?
119
- else
120
- gh pr checks --watch || rc=$?
121
- fi
122
- case $rc in 0|1|8|124) ;; *) exit $rc;; esac
123
- ```
124
-
125
- Exit codes 0 (pass), 1 (fail), 8 (pending), and 124 (timeout) are expected and handled next. Other codes (auth errors, etc.) re-raise.
126
-
127
- ### 4. Fetch review data
128
-
129
- ```bash
130
- bash scripts/unresolvedPrComments.sh
131
- ```
132
-
133
- The output JSON has:
134
-
135
- - `threads`: every unresolved review thread, with `threadId`, `replyToCommentDatabaseId`, `comments[]`, `lastBabysitSentinelAt`, `lastHumanCommentAt`, `lastBotCommentAt`, `postSentinelBotComments[]`, `postSentinelHumanComments[]`, and `activityState` (`"active"` / `"uncertain"` / `"addressed"`).
136
- - `activeThreads`: threads where `activityState != "addressed"` — these need attention this iteration (active AND uncertain).
137
- - `uncertainThreads`: just the uncertain subset. For each, read EVERY entry in `postSentinelBotComments` before deciding.
138
- - `reviewBodyComments`: every review from a known automated reviewer (CodeRabbit, Mendral, Dependabot, etc.), with the raw body and a stable per-review `fingerprint`. The agent reads each body directly to extract findings.
139
- - `issueComments`: every top-level Conversation-tab comment, each with `isBabysitSentinel`, `isKnownBot`, and a per-comment `fingerprint`.
140
- - `activeIssueComments`: the subset of `issueComments` that are NOT babysit-pr sentinels, NOT from a known bot, and whose `fingerprint` is NOT already listed in any prior babysit-pr summary. These are the human Conversation-tab comments still needing a reply.
141
- - `priorBabysitSentinels`: prior babysit-pr summary comments posted as PR issue-comments. The script does the dedupe lookup for `activeIssueComments` automatically; the agent uses this array for `reviewBodyComments` dedupe.
142
- - `truncated`: array naming any GraphQL connection that hit GitHub's 100-item cap (`reviewThreads`, `thread-comments`, `reviews`, `issueComments`). Non-empty means some comments may not be in this JSON — surface this in the final summary.
143
- - `totalActiveThreads`, `totalUncertainThreads`, `totalActiveIssueComments`, `totalReviewBodyComments`, `totalUnresolvedComments` for quick checks.
144
-
145
- ### Scope
146
-
147
- This PR's review-feedback scope is strict by default. Steps 6a (threads), 6b (top-level conversation comments), and 7 (automated review bodies) classify each comment as in-scope or out-of-scope using this rule before choosing a verdict. Step 5 (CI) uses the broader CI-scope rule in that step, not this one — CI can legitimately fail on unchanged lines because the PR changed a contract or dependency path.
148
-
149
- Build the changed-line set from `gh pr diff` once per iteration. Count changed diff lines on both sides: added lines in the new version, removed lines in the old version, and modified code represented by adjacent remove/add pairs. Do not count diff context lines. A reviewer comment or automated review-body comment is **in scope** when its anchor falls on a changed diff line on either side of the hunk. Deleted-line comments like "why remove this?" or "please add this back" are in scope by definition. For a range like `12-14`, any overlap with a changed diff line is in scope.
150
-
151
- When matching review comments to hunks, use the anchor line provided by `unresolvedPrComments.sh`; it may be the current `line` or the script's fallback to `originalLine`. Compare that anchor against both new-side added ranges and old-side removed ranges.
152
-
153
- Comments on unchanged/context lines, touched files outside changed lines, or untouched files are **out of scope by default**.
154
-
155
- Narrow escape hatch: treat an unchanged/context-line comment as in scope only when there is an explicit external signal that this PR caused or requires the issue. Acceptable signals:
156
-
157
- - The reviewer explicitly ties the concern to this PR's change.
158
- - The comment points to an unchanged line directly used by a changed diff line, and you can name the changed `file:line` that creates the coupling.
159
- - CI, test, or typecheck output proves the PR changed the contract or behavior for the symbol, API, or execution path named by the comment.
160
-
161
- If you cannot name one of those signals, classify the comment as out of scope. Do not use broad judgment phrases like "related", "nearby", "maintainability", or "review confidence" to widen scope.
162
-
163
- Default posture: focus on in-scope feedback. For out-of-scope feedback, apply the fix **only** if it meets the out-of-scope bar below. Otherwise defer with a follow-up reply.
164
-
165
- **Out-of-scope fix bar** (apply the fix even though it's out of scope):
166
-
167
- - Security vulnerability, data loss, or crash in the PR's execution path.
168
- - Obvious correctness bug (wrong output, broken invariant) confirmed by reading the referenced code.
169
- - One-line or trivial change that obviously cannot regress anything (typo, missing null check matching surrounding style, etc.).
170
-
171
- **Everything else → Defer** (for out-of-scope fix requests that miss the bar): post a Defer reply tagged with the follow-up sentinel (see step 9). Do not expand the PR. Disagree and Already-fixed still apply to out-of-scope comments when the reviewer is wrong or the concern is already handled elsewhere; Defer is specifically for "this is a real but out-of-scope ask we are choosing not to act on here."
172
-
173
- ### 5. Handle CI failures (conservative)
174
-
175
- Run `bash scripts/fetchFailedLogs.sh` to stream failed output for every failing check on the PR. The first line is either:
176
-
177
- - `# babysit-pr: no failing checks` → skip to step 6a.
178
- - `# babysit-pr: failing checks` → followed by one delimited block per failing job or external check:
179
- - `# --- run=<id> job=<id> ---` blocks carry the job's `--log-failed` output (GitHub Actions).
180
- - `# --- external check: <name> (<url>) ---` blocks carry no logs — the check isn't a GitHub Actions run (CircleCI, Nx Cloud, semgrep, CodeRabbit, Devin, etc.). Treat these like "External checks with no inspectable logs" in the diagnosis-only list below: stop and report, don't guess a fix.
181
-
182
- Read the logs and diagnose: **build/type errors first** (they cause cascading test failures), then lint/format, then tests.
183
-
184
- **Apply a fix directly** only when the cause is high-confidence and inside the PR's changed surface:
185
-
186
- - Compile/type errors in files the PR touched.
187
- - Deterministic lint/format violations (auto-fixable).
188
- - Tests that the PR broke by renaming/removing symbols they reference.
189
- - Missing test updates for intentional behavior changes.
190
-
191
- **Stop and report a diagnosis** (do not guess a fix) for:
192
-
193
- - Flaky / intermittent failures.
194
- - Infrastructure or provider outages.
195
- - Permission / auth / missing-secret failures.
196
- - Unrelated failures (touching code this PR didn't modify).
197
- - Ambiguous test intent.
198
- - External checks with no inspectable logs.
199
-
200
- Scope check for CI: scope is the PR's changed files plus failures directly caused by those changes in the PR's execution path. Use `gh pr diff --name-only` as the first signal — this is PR-authoritative and works even if the local base ref is missing or stale (e.g., in fresh clones or CI sandboxes). Allow fixes outside changed files only when the logs and code make causality clear (e.g., the PR renamed a symbol that a sibling test references). CI failures outside that surface are out of scope — report the diagnosis, don't apply speculative fixes. CI fixes are never Deferred as follow-ups: CI needs to pass on this PR.
201
-
202
- ### 6a. Assess active review threads
203
-
204
- For every thread in `activeThreads` (this includes both `"active"` and `"uncertain"`):
205
-
206
- - Group comments by file; read each file once (not per comment).
207
- - If the referenced file no longer exists, record "comment may be outdated" and classify as **Already fixed**.
208
- - If `activityState == "uncertain"`, read EVERY entry in `postSentinelBotComments` (not just the newest):
209
- - If EVERY entry is a non-actionable acknowledgement → mark the thread **Skip-reply** (the existing sentinel already covers the thread; posting again would be noise). Do not classify it Agree/Disagree/Already-fixed. Record this in the final summary so the skip is visible.
210
- - If ANY entry carries new actionable content → treat the thread as new feedback and proceed below. Note in the final summary that an uncertain thread was reactivated, citing the specific comment.
211
- - Each remaining thread (i.e., NOT marked Skip-reply) gets a scope classification first. Use the Scope subsection to label it in-scope or out-of-scope. For comments on deleted lines, record that the anchor is on the removed side of the diff. For any unchanged/context-line comment classified in scope via the narrow escape hatch, record the external signal and the changed `file:line` when applicable.
212
- - Then pick one verdict — each of these (except Skip-reply) will get a reply posted in step 9:
213
- - **In-scope** threads use the original three verdicts:
214
- - **Agree** — the comment identifies a real issue. Apply the fix. Record the thread ID and a one-line what-changed.
215
- - **Disagree** — the current code is acceptable. Record a short reasoning.
216
- - **Already fixed** — a prior commit addresses the concern. Record a pointer (commit SHA, file:line).
217
- - **Out-of-scope** threads apply the out-of-scope fix bar from the Scope subsection:
218
- - Meets the bar → **Agree** (apply the fix, and note in the reply that it was fixed despite being out of scope because it met the bar).
219
- - Does not meet the bar → **Defer** (new verdict). Record a one-line rationale and, if relevant, a pointer to where the concern lives.
220
- - Disagree and Already-fixed can still apply to out-of-scope comments (e.g., reviewer asks for a refactor that's already landed on main, or misreads the code).
221
-
222
- ### 6b. Assess top-level Conversation-tab comments
223
-
224
- For every entry in `activeIssueComments` — humans commenting on the PR Conversation tab without anchoring to a file/line:
225
-
226
- - Apply the **Scope** subsection's rules. A top-level comment is in scope when the reviewer explicitly ties it to a changed file/line, behavior the PR introduced, or a contract the PR altered. Otherwise out of scope by default.
227
- - Pick a verdict the same way as a thread: Agree / Disagree / Already fixed (in-scope), or Agree-meets-bar / Defer (out-of-scope). Apply fixes for Agree verdicts.
228
- - Replies are NOT posted as individual top-level comments — that would clutter the conversation. Instead, every issue-comment verdict goes into the **same step-9 PR-level summary** as the review-body findings, under its own `## Conversation-tab comments` heading. Per-comment fingerprints join the fenced fingerprint block so future runs dedupe.
229
- - If `activeIssueComments` is empty AND `reviewBodyComments` is empty (or all dedupe), skip the PR-level summary comment entirely in step 9.
230
-
231
- ### 7. Assess automated review bodies
232
-
233
- For every entry in `reviewBodyComments`:
234
-
235
- - Dedupe first: if its `fingerprint` already appears in any `priorBabysitSentinels[].body`, skip — already covered.
236
- - Otherwise, READ THE BODY IN FULL. Automated reviewers (CodeRabbit, Mendral, etc.) pack findings into nested `<details>/<blockquote>` HTML with file paths, line ranges, and titles inline. Identify each individual finding the body contains.
237
- - For each finding, **classify scope** (in / out) using the Scope subsection. For ranges like `12-14`, any overlap with changed diff lines on either side of the hunk is in scope; no overlap is out of scope unless one of the explicit escape-hatch signals applies.
238
- - Pick a verdict per finding:
239
- - In-scope → Agree / Disagree / Already fixed (as with threads). If Agree, apply the fix.
240
- - Out-of-scope → apply the out-of-scope fix bar. Meets the bar → Agree and apply the fix, noting in the summary that it was fixed despite being out of scope. Does not meet the bar → **Defer**. A Deferred finding does not get its own top-level comment; it goes into the summary under the **Deferred (out of scope)** heading (see step 9).
241
-
242
- The whole-body `fingerprint` (not per-finding) goes in the fenced fingerprint block at the end of the summary. If the review body later changes (new findings, edits), the fingerprint changes and the next pass will post the summary again — slightly noisier but never silently drops a new finding. Trivial whitespace/version-tag changes are absorbed by body normalization before hashing, so identical content doesn't churn.
243
-
244
- If `reviewBodyComments` is empty (or all entries dedupe), skip ONLY the review-body section of the summary in step 9. Still post thread replies for every non-Skip-reply thread from step 6a and handle issue comments per step 6b.
245
-
246
- ### 8. Commit and push (if any edits)
247
-
248
- If steps 5, 6, or 7 modified any files, decide:
249
-
250
- - **Which files are yours this iteration.** The worktree may contain unrelated in-progress work. Only stage files this iteration touched — if in doubt, run `git diff --name-only` and pick from that list deliberately.
251
- - **A focused commit message.** Prefer something like `babysit-pr: <one-line what-changed>`; the project's commitlint expects conventional-commit form, so a `fix(core): ...` or `docs(core): ...` prefix is usually right.
252
-
253
- Then run:
254
-
255
- ```bash
256
- bash scripts/commitAndPush.sh "<message>" <file1> [<file2> ...]
257
- ```
258
-
259
- The script enforces explicit staging (never `git add -A`), never skips hooks, and prints:
260
-
261
- ```text
262
- sha=<commit-sha>
263
- url=https://github.com/<owner>/<repo>/commit/<sha>
264
- ```
265
-
266
- Capture the `url=` line for the reply templates in step 9.
267
-
268
- ### 9. Post replies
269
-
270
- For every thread assessed in step 6a that was NOT marked **Skip-reply** (i.e., one of Agree / Disagree / Already fixed / Defer):
271
-
272
- ```bash
273
- bash scripts/postSentinelReply.sh "$THREAD_ID" "$BODY"
274
- ```
275
-
276
- Skip-reply threads (uncertain threads where every post-sentinel bot comment was a non-actionable ack) are left alone — the existing sentinel already covers them.
277
-
278
- Body templates (the script appends the `addressed` sentinel if missing):
279
-
280
- - **Agree**: `Addressed in <commit-url>. <one-line what-changed>.`
281
- - **Disagree**: `Leaving current behavior. <reasoning>.`
282
- - **Already fixed**: `Already handled by <commit-url-or-file:line>. <brief pointer>.`
283
- - **Defer**: `Out of scope for this PR; this looks like follow-up work rather than something introduced or required by this change. <one-line rationale or pointer if useful>.\n\n<sub>🤖 <code>babysit-pr:followup v1 core@3.9.0</code></sub>`
284
-
285
- For Defer replies, include the follow-up sentinel on its own line as shown. The script will append the `addressed` sentinel after it on its own line, so the final body ends with the follow-up sentinel followed by a blank line followed by the `addressed` sentinel — `grep babysit-pr:followup` finds the deferral and `grep babysit-pr:addressed` still marks the thread handled for dedupe.
286
-
287
- The script uses the `addPullRequestReviewThreadReply` GraphQL mutation. It does NOT resolve the thread.
288
-
289
- If any automated review bodies were assessed in step 7 OR any active issue comments were assessed in step 6b, post ONE top-level PR comment summarizing all of them:
290
-
291
- ```bash
292
- bash scripts/postSentinelPrComment.sh "$PR_NUMBER" "$BODY"
293
- ```
294
-
295
- The PR-level summary should:
296
-
297
- - Group by source. Use `## Review-body findings` for step-7 work and `## Conversation-tab comments` for step-6b work. Omit a section if its list is empty.
298
- - Inside each section, group verdicts under **Agree / Disagree / Already fixed / Deferred (out of scope)** subheadings. Omit a subheading if its list is empty.
299
- - Under **Deferred (out of scope)**, list each deferred item as a bullet, followed on its own line by `<sub>🤖 <code>babysit-pr:followup v1 core@3.9.0</code></sub>` so grep catches them individually.
300
- - Include the commit URL for fixes.
301
- - End with a fenced fingerprint block listing every current fingerprint — addressed and deferred — one per line. Include both `reviewBodyComments[].fingerprint` (whole-body, one per automated review) and `activeIssueComments[].fingerprint` (per Conversation-tab comment). Future runs dedupe by matching these against `priorBabysitSentinels`.
302
-
303
- ### 10. Summarize
304
-
305
- Report:
306
-
307
- - Commits made (with URLs).
308
- - Merge conflict status if relevant (resolved or aborted with reason).
309
- - CI checks fixed / still failing / skipped-with-diagnosis.
310
- - Review threads replied to, grouped by verdict (including any Defer count: "X threads deferred as follow-ups").
311
- - Conversation-tab comments addressed, grouped by verdict (e.g. "Z conversation comments deferred as follow-ups").
312
- - Review-body findings summarized (or skipped because already covered), including the Deferred count: "Y review-body findings deferred as follow-ups".
313
- - Threads left active because of bot-acknowledgement uncertainty (flag by thread URL).
314
- - If `truncated` is non-empty: explicitly call out which connection hit GitHub's 100-item GraphQL cap (e.g. "`truncated: ['thread-comments']` — at least one review thread has more than 100 comments; this pass may have missed the tail. Investigate before relying on it for completeness.").
315
- - The stop condition triggered for this pass (clean / progressing / stuck).
316
-
317
- When the report mentions any deferrals, include a one-liner the user can run later to enumerate them, e.g.:
318
-
319
- ```bash
320
- gh api graphql -f query='query($o:String!,$r:String!,$n:Int!){repository(owner:$o,name:$r){pullRequest(number:$n){reviewThreads(first:100){nodes{comments(first:50){nodes{body url}}}}comments(first:100){nodes{body url}}}}}' -F o=<owner> -F r=<repo> -F n=<pr> | grep -B1 babysit-pr:followup
321
- ```
322
-
323
- Do not rely only on `gh pr view --json comments,reviews` — that view can miss inline review-thread replies, which is where most Defer replies live.
324
-
325
- ## Loop control
326
-
327
- After the single pass completes, pick exactly one outcome:
328
-
329
- - **Exit clean** — all CI checks passed AND every thread in `activeThreads` was either marked Skip-reply during step 6a's inspection or has already received a fresh sentinel reply in this pass (Agree / Disagree / Already-fixed / **Defer** all count — a Defer reply is a sentinel reply), AND every entry in `activeIssueComments` is covered by this pass's PR-level summary, AND every current review-body fingerprint is covered by an existing sentinel comment (deferred review-body and conversation-comment fingerprints count; they're in the summary's fenced block). Do not use raw `totalActiveThreads` / `totalActiveIssueComments` from the script output — they're pre-inspection and will stay non-zero for Skip-reply or post-summary cases. A PR with Deferred items is still clean from babysit's perspective: the skill has done what it can without widening scope. Report success and stop.
330
- - **Exit progressing** — pass made commits, posted new replies, or both, and the PR is not yet clean (CI is still pending, a new CI run was triggered by this pass's commits, or more work remains). There is real work still in flight that another run would pick up. Report what was done and what is pending, and tell the user to re-run `/babysit-pr` once CI settles, or to wrap the call with `/loop <cadence> /babysit-pr` (or a shell `while true; do ...; done`) for automatic re-runs.
331
- - **Exit stuck** — pass made no commits and posted no new replies, and the PR is still not clean. Nothing actionable happened this pass. Use this whenever progress is blocked on something outside the skill's scope, including:
332
- - Merge conflict in step 2 that exceeded the high-confidence resolution bar.
333
- - CI still running (`gh pr checks --watch` timed out with pending checks).
334
- - CI failing with a diagnosis-only verdict from Step 5 (flaky / infra / auth / external check / ambiguous / out-of-scope failure).
335
- - Only Skip-reply threads remained AND CI was already red or pending.
336
-
337
- Report the specific blocker (pending vs. diagnosed-failure, with the diagnosis text) and tell the user to investigate or re-run once state may have changed.
338
-
339
- This skill never waits or repeats internally.
340
-
341
- ## Portability notes
342
-
343
- - No `Task` subagent spawning — run everything inline.
344
- - No `Skill` tool calls — this skill never invokes `core:commit-push-pr`, `core:fix-ci`, or `core:unresolved-pr-comments`.
345
- - No `!` slash-command prefix in code fences — the agent runs these as ordinary bash.
346
- - No `$CLAUDE_PLUGIN_ROOT` and no host-specific path discovery. Script paths are relative bundled-resource paths like `scripts/unresolvedPrComments.sh`, resolved relative to this SKILL.md.
347
- - `argument-hint` is the only intentional nonstandard frontmatter key (included for Claude Code UX). If a strict Codex validator rejects it, move the key into a host-specific wrapper and keep this file's frontmatter to `name` + `description` only.
348
-
349
- ## Examples
350
-
351
- ### Example 1: single pass, exits stuck awaiting CI
352
-
353
- User: `babysit my PR`
354
-
355
- - No PR arg → operate on the current branch.
356
- - Preflight OK, PR #482 found.
357
- - `gh pr checks --watch` times out at 600s — two checks still pending.
358
- - `unresolvedPrComments.sh` returns 0 active threads, 0 review-body comments, 0 active issue comments.
359
- - No commits, no replies posted, CI state unchanged vs. start.
360
- - Outcome: **stuck**. Report: "CI still running after 10 min; no comments to address. Re-run `/babysit-pr` once CI settles, or wrap with `/loop 2m /babysit-pr`."
361
-
362
- ### Example 2: explicit PR number checks out and babysits that PR
363
-
364
- User: `babysit PR 482`
365
-
366
- - Preflight OK. Input parser matches the explicit-token rule and captures `482`.
367
- - `gh pr checkout 482` switches the worktree to PR #482's head branch (say, `feat/xyz`).
368
- - Step 2's `gh pr view` confirms PR #482 on the now-current branch; the new-PR fallback does not fire.
369
- - Remainder proceeds as a normal single pass (CI watch, thread / conversation-comment / review-body assessment, replies).
370
- - Report final state on exit.
371
-
372
- ### Example 3: out-of-scope review-body finding gets deferred
373
-
374
- User: `babysit my PR`
375
-
376
- - Preflight OK, PR #612 found, CI green.
377
- - `unresolvedPrComments.sh` returns 1 active thread, 1 active issue comment, and 1 CodeRabbit review body containing two findings:
378
- - Thread on `src/users.ts:82` (unchanged, not touched by diff) — reviewer: "while you're here, this helper could be memoized".
379
- - Active issue comment from a teammate on the Conversation tab: "general nit — can you rename the new module to `payments-core`?". Touches a changed file (`src/payments/index.ts`).
380
- - CodeRabbit review body — agent reads it and identifies two findings: (a) on `src/orders.ts:45-47`, anchor overlaps a changed line, error message should use backticks (in scope); (b) on `src/unrelated.ts:10`, file not touched by the PR (out of scope, no escape-hatch signal).
381
- - Scope classification:
382
- - Thread is on an unchanged line; reviewer doesn't tie it to this PR's changes; doesn't meet the fix bar. → **Defer**.
383
- - Conversation-tab comment ties to a changed file and is a trivial rename. → **Agree**, apply rename.
384
- - Finding (a) is in-scope → **Agree**, apply backtick fix.
385
- - Finding (b) is out-of-scope, not a correctness bug, not a one-liner → **Defer**.
386
- - Commit `f00dbabe` covers the rename and the backtick fix. Post Defer reply on the thread with the `babysit-pr:followup v1` sentinel above the `addressed` sentinel. Post one PR-level summary with `## Review-body findings` (Agree 1, Deferred 1) and `## Conversation-tab comments` (Agree 1); the fenced block lists the CodeRabbit review body's whole-body fingerprint AND the conversation comment's per-comment fingerprint.
387
- - Summary reports: "1 thread deferred as follow-up, 1 review-body finding deferred as follow-up, 0 conversation comments deferred" plus the `gh api graphql ... | grep babysit-pr:followup` one-liner.
388
- - **Exit clean** — Defer replies count as fresh sentinel replies; all fingerprints are covered.
389
-
390
- ## Input
391
-
392
- PR number or URL: $ARGUMENTS
@@ -1,53 +0,0 @@
1
- ---
2
- allowed-tools: Bash(git checkout --branch:*), Bash(git add:*), Bash(git status:*), Bash(git push:*), Bash(git commit:*), Bash(gh pr view:*), Bash(gh pr create:*), Bash(gh pr edit:*), Bash(git diff:*), Bash(git merge-base:*)
3
- description: Commit, push, and open a PR. Use when the user wants to ship changes, create a pull request, or says things like 'commit and push', 'open a PR', 'ship it', 'send it', 'create a PR for this', or 'push this up'.
4
- ---
5
-
6
- ## Context
7
-
8
- - Current branch: !`git branch --show-current`
9
- - Git status: !`git status --short`
10
- - Commits ahead of default branch: !`git log --oneline origin/HEAD..HEAD 2>/dev/null || echo "(unknown)"`
11
- - Existing PR: !`gh pr view --json url --jq .url 2>/dev/null || echo "none"`
12
- - Diff summary: !`git diff HEAD --stat`
13
- - Full diff: !`git diff HEAD`
14
-
15
- ## Your task
16
-
17
- If `Commits ahead of default branch` is `(unknown)`, `origin/HEAD` couldn't be resolved — stop and tell the user to run `git remote set-head origin -a` (or otherwise set the default branch) before retrying, since the simplify step also depends on it. Otherwise, if `Git status`, `Commits ahead of default branch`, and `Existing PR` are all empty/none, stop and reply `nothing to ship.`. Otherwise:
18
-
19
- Before doing any step, output the full 7-step checklist below in your first response so it stays in recent context across sub-skill calls. Do not skip this — it's what keeps you from stopping after `simplify`.
20
-
21
- Use this PR body shape when creating or refreshing descriptions:
22
-
23
- ```md
24
- ## Summary
25
-
26
- Briefly explain the user intent from session history and the meaningful behavior/system change. If intent cannot be determined from the session or diff, ask the user before creating or refreshing the PR. Do not write a file-by-file changelog.
27
-
28
- ## Validation
29
-
30
- - List proof of validation: commands, screenshots, telemetry, Loom, or `Not run: <reason>`.
31
-
32
- ## Notes
33
-
34
- Optional: ticket links, rollout plan, residual risk, or areas for reviewers to focus on.
35
- ```
36
-
37
- - Omit `## Notes` when there are no useful notes.
38
- - Do not invent ticket links, validation evidence, rollout plans, or risks. Use `Not run: <reason>` when validation was not run.
39
-
40
- Script paths in this procedure are written as `scripts/...`, relative to this SKILL.md. When executing a bundled script, run it from this skill directory or resolve it to this skill's installed directory; do not look for it at the repository root.
41
-
42
- 1. Create a new branch if on main (e.g., `feat/add-user-validation`, `fix/null-check-in-parser`).
43
- 2. Run the `simplify` skill on the full PR diff — `git diff $(git merge-base HEAD origin/HEAD)..HEAD` plus any uncommitted changes. When it returns, your very next action is to restate the remaining steps (3–7) and continue with step 3 in the same turn. Do not stop, do not end the turn with a simplify summary.
44
- 3. If `git status --short` shows changes, create a single conventional commit with `git commit --no-gpg-sign`.
45
- 4. Push the branch to origin.
46
- 5. Look up the current agent session ID by running this skill's bundled script: `bash scripts/find-session-id.sh '<phrase>'`. Pass a distinctive verbatim chunk (≥10 words) from the most recent user message; see the script header for quoting constraints. If the script prints `codex <id>`, use ``Agent session: `codex resume <id>` ``. If it prints `claude-code <id>`, use ``Agent session: `claude --resume <id>` ``. Keep the resume command in backticks so it renders as code in the PR description. If empty, there is no session footer line.
47
- 6. Check for an existing PR with `gh pr view`.
48
-
49
- PR title format: conventional-commit type + description, with no scope, plus the Linear ticket in parentheses at the end when one applies (e.g., `feat: add resume command (STAFF-123)`). This differs from the commit subject, which keeps its scope. Derive the ticket from the branch name, commit body, or session context; omit the parenthetical when no ticket applies.
50
- - No PR: create with `gh pr create` using the PR title format above. Description = the PR body shape above, followed by the session footer line if known and the agent footer `<sub>🤖 <code>commit-push-pr:created v1 core@3.9.0</code></sub>` on its own line.
51
- - PR exists: if the title doesn't match the format above, correct it with `gh pr edit --title`. Refresh the body via `gh pr edit --body` so (a) the new commit's changes are reflected in the prose while existing `## Summary`, `## Validation`, and `## Notes` sections are preserved unless clearly stale, (b) any known session footer line is appended if missing, never removing or rewriting existing `Agent session: ...` or `Agent session ID: ...` lines, and (c) any existing footer carrying the substring `commit-push-pr:created v1` is preserved verbatim, appending `<sub>🤖 <code>commit-push-pr:created v1 core@3.9.0</code></sub>` only if absent. Then report the URL.
52
-
53
- 7. End with one short text response: branch name and the full PR URL (e.g., `https://github.com/clipboardhealth/core-utils/pull/123`). Never use shorthand like `repo#123` — always output the complete URL.
@@ -1,39 +0,0 @@
1
- #!/usr/bin/env bash
2
- # find-session-id.sh — print the current agent session ID, or nothing.
3
- # Output format on success: "<agent> <id>" (e.g. "codex 0193..." or "claude-code c8fb7000-...").
4
- # Exits 0 either way; callers should treat empty output as "unknown, skip the line".
5
- #
6
- # Codex: reads $CODEX_THREAD_ID directly.
7
- # Claude Code: greps the project's JSONL transcripts for a phrase the caller
8
- # passes as $1. The phrase MUST come from a recent user message in the
9
- # current session and MUST avoid: " ' \ tab newline (those are JSON-escaped
10
- # on disk or break shell quoting). Non-ASCII is fine — Claude Code stores
11
- # user messages verbatim, not as \uXXXX escapes.
12
- #
13
- # Usage: find-session-id.sh "<distinctive phrase>"
14
-
15
- set -euo pipefail
16
-
17
- if [ -n "${CODEX_THREAD_ID:-}" ]; then
18
- echo "codex $CODEX_THREAD_ID"
19
- exit 0
20
- fi
21
-
22
- phrase="${1:-}"
23
- [ -n "$phrase" ] || exit 0
24
-
25
- # Claude Code encodes both / and . as - in the project-dir name
26
- # (e.g. /Users/x/.claude → -Users-x--claude).
27
- project_dir="$HOME/.claude/projects/$(pwd | sed -e 's|/|-|g' -e 's|\.|-|g')"
28
- [ -d "$project_dir" ] || exit 0
29
-
30
- # -- guards against a phrase that starts with -. -m 1 stops scanning each
31
- # transcript at the first hit (transcripts can be hundreds of MB).
32
- matches=$(grep -lFm 1 -- "$phrase" "$project_dir"/*.jsonl 2>/dev/null || true)
33
- [ -n "$matches" ] || exit 0
34
-
35
- # Require exactly one match. Falling back to recency under multi-match would
36
- # risk a wrong ID when concurrent sessions exist in the same project.
37
- [ "$(printf '%s\n' "$matches" | wc -l)" -eq 1 ] || exit 0
38
-
39
- echo "claude-code $(basename "$matches" .jsonl)"
@@ -1,46 +0,0 @@
1
- ---
2
- description: Implement a plan file or direct request end-to-end, then hand off to commit-push-pr to ship it. Use when the user says 'go', 'execute the plan', 'implement the plan', or gives an implementation request.
3
- ---
4
-
5
- # Go: Execute Work and Ship It
6
-
7
- Given a plan file or direct implementation request, implement the requested work, then invoke the `commit-push-pr` skill to create a PR.
8
-
9
- This skill is the bridge between intent and shipping. It does not re-plan or re-design unless the user asked for that. If the request is wrong or cannot be implemented safely, surface that; do not silently improvise.
10
-
11
- ## Phase 1: Resolve the Input
12
-
13
- The user invokes this skill with either a plan file path/identifier or a direct request like "add a login button."
14
-
15
- - **Path-like input** (absolute, relative, or bare name): try to read it. For bare names, check the host's plans directory first. Resolve the path first; if it lies outside the workspace/repo or the host's plans directory, ask the user to confirm before reading it (applies to both absolute and relative inputs — `..` escapes count). If the input doesn't resolve to a readable file, stop and tell the user — do not reinterpret a missing path as a direct request.
16
- - **Natural-language request**: treat as the implementation task. There may be no plan file.
17
- - **No argument**: ask for either a plan path or the implementation request.
18
-
19
- When using a plan, read it fully before starting. Note **Critical files**, **Approach**, and **Verification** sections (or equivalents).
20
-
21
- ## Phase 2: Implement
22
-
23
- The plan or request is the source of truth for scope:
24
-
25
- - Make exactly the changes requested — no extra refactors, tests, or cleanup.
26
- - If the plan references files or utilities that no longer exist, stop and report.
27
- - If an assumption is invalid or a constraint was missed, stop and report. Do not silently rewrite the approach.
28
- - Do not modify the plan file itself.
29
-
30
- ## Phase 3: Validate
31
-
32
- - Read `AGENTS.md`, `CLAUDE.md`, `CONTRIBUTING.md`, or equivalent contributor instructions for the mandated pre-PR command (e.g. `node --run verify`). That wins over everything.
33
- - If the repo relies on pre-commit/pre-push hooks and mandates no manual command, don't invent one — let the hooks run during the `commit-push-pr` handoff.
34
- - If the plan names specific checks, run them when practical. Ask before running anything that's clearly a slow CI-only suite.
35
-
36
- Fix any failures before handing off. Do not hand off with known failing checks unless the user explicitly accepts a pre-existing or unrelated failure.
37
-
38
- ## Phase 4: Hand Off
39
-
40
- Invoke the `commit-push-pr` skill. Do not commit, push, or open the PR yourself.
41
-
42
- If `commit-push-pr` reports `nothing to ship.`, surface that.
43
-
44
- ## Phase 5: Final Output
45
-
46
- Ensure the user sees the branch name and full PR URL.