From 2f70693276fb08b15de4a79d95756de531118b30 Mon Sep 17 00:00:00 2001 From: Bhagya Mudgal Date: Tue, 10 Nov 2026 05:23:57 +0530 Subject: [PATCH 1/8] fix(skills): match real request phrasing and remove contradictions - Restore the fix-ts-errors shared-package rule that baac935 inverted, and the possessives it dropped - Widen five trigger descriptions to the phrasing people type, with an eval case for each - Close silent paths: unpushed fixes no longer resolve threads, a reviewer that cannot run is reported, and side findings are filed, asked about, or listed - Fix stale references and replace judgment words with fixed thresholds - Move the fix-pr-review push procedure into a reference so runs that never push load less --- skills/audit-ticket/SKILL.md | 26 +++-- skills/backend-perf/SKILL.md | 4 +- skills/browser-qa/SKILL.md | 38 ++++-- skills/bulk-edit/SKILL.md | 10 +- skills/converge-reviews/SKILL.md | 32 ++--- skills/design-director/SKILL.md | 8 +- skills/design-director/modes/color.md | 2 +- skills/design-director/modes/critique.md | 2 +- skills/design-director/modes/layout.md | 2 +- skills/design-director/modes/type.md | 2 +- skills/discover-product-domain/SKILL.md | 12 +- skills/done/SKILL.md | 16 +-- skills/done/references/readiness-card.md | 4 +- .../executing-tickets-with-subagents/SKILL.md | 10 +- .../references/unattended-scheduler.md | 13 ++- skills/file-issue/SKILL.md | 50 ++++---- skills/file-pr/SKILL.md | 46 +++++--- skills/fix-pr-review/SKILL.md | 109 ++++++++---------- .../fix-pr-review/references/final-report.md | 13 +++ .../references/github-reply-resolve.md | 2 +- .../references/push-publication.md | 20 ++++ .../fix-pr-review/references/triage-prompt.md | 3 +- .../fix-pr-review/references/triage-rubric.md | 2 +- skills/fix-ts-errors/SKILL.md | 16 +-- skills/git-commit/SKILL.md | 8 +- skills/grill-me/SKILL.md | 9 +- skills/manage-report-lifecycle/SKILL.md | 18 +-- skills/openclaw-backup/SKILL.md | 6 +- skills/openclaw-backup/agents/openai.yaml | 7 ++ skills/parallel-review/SKILL.md | 16 ++- skills/preflight-mutations/SKILL.md | 30 +++-- skills/project-discovery/SKILL.md | 2 +- .../references/coding-standards.md | 26 ++--- .../references/output-templates.md | 10 +- .../references/stack-menu.md | 28 ++--- skills/resolving-merge-conflicts/SKILL.md | 8 +- skills/reuse-first/SKILL.md | 2 +- .../reuse-first/references/artifact-guide.md | 2 +- skills/reuse-first/references/sweep-mode.md | 2 + skills/review-pr/SKILL.md | 94 +++++++-------- skills/review-pr/references/batch-mode.md | 9 +- .../references/q6-reusability-search.md | 2 +- .../references/schema-design-checks.md | 8 +- skills/simplify/SKILL.md | 4 +- skills/standup/SKILL.md | 32 ++--- skills/sync-agent-setups/SKILL.md | 12 +- skills/systematic-debugging/SKILL.md | 6 +- skills/systematic-debugging/TECHNIQUES.md | 8 +- skills/triage-board/SKILL.md | 34 +++--- skills/verify-claims/SKILL.md | 4 +- tools/eval/triggers.json | 22 +++- 51 files changed, 475 insertions(+), 376 deletions(-) create mode 100644 skills/fix-pr-review/references/push-publication.md create mode 100644 skills/openclaw-backup/agents/openai.yaml diff --git a/skills/audit-ticket/SKILL.md b/skills/audit-ticket/SKILL.md index 3aea0b7..13a9484 100644 --- a/skills/audit-ticket/SKILL.md +++ b/skills/audit-ticket/SKILL.md @@ -1,11 +1,11 @@ --- name: audit-ticket -description: Audit a stale GitHub issue against the current codebase, then update or sunset it. Use when the user says "audit this ticket", or asks whether an old issue is still needed or should be sunset. +description: Audit a stale GitHub issue against the current codebase, then update or sunset it. Use when the user says "audit this ticket", asks whether an old issue is still needed or should be sunset, asks "do we still have this issue" or "is this already done or sorted", says "verify the gap is still there and close it", or hands over a batch of tickets to check. --- # /audit-ticket: audit a stale issue against current code -I take a GitHub issue written weeks or months ago and check every requirement in it against the codebase as it is today. Old tickets rot. Half the items ship in unrelated PRs, some turn obsolete after a refactor, and the rest silently block planning because nobody trusts the ticket anymore. I produce a per-item verdict with evidence, then the user decides the ticket fate. I would rather do this audit once, properly, than argue about the ticket for another quarter. +I take a GitHub issue written weeks or months ago and check every requirement in it against the codebase as it is today. Old tickets rot. Half the items ship in unrelated PRs, some turn obsolete after a refactor, and the rest silently block planning because nobody trusts the ticket anymore. I produce a per-item verdict with evidence, then the user decides the ticket fate. ## Usage @@ -40,7 +40,7 @@ I extract attachment URLs from the body AND every comment. - `https://github.com/user-attachments/assets/` - `https://user-images.githubusercontent.com/...` -For each, I download with auth, since private-repo attachments 404 without it, then Read the file to actually look at it. +For each, I download with auth, since private-repo attachments 404 without it, then Read the file. ```bash curl -fsSL -H "Authorization: token $(gh auth token)" -o /tmp/audit-ticket--img.png "" @@ -87,7 +87,7 @@ verdict grounded in file:line evidence, not a guess. Issue: #, (opened <createdAt>) Goal (1 sentence): <from body> HEAD: <full Phase 1 SHA> -Content snapshot: <verified_content_snapshot tree hash> +Content snapshot: <tree ID from Phase 1> ## Requirement to verify <Rn>: <requirement text> @@ -127,11 +127,11 @@ A failed or empty subagent does not stop the audit. I mark that requirement `unv ## Phase 3: Report (main) -I verify each returned `file:line` exists in the recorded content snapshot before printing, by reading the cited range from `git show <verified_content_snapshot>:<path>`. I drop fabricated citations and downgrade that verdict confidence to `low`. +I verify each returned `file:line` exists in the recorded content snapshot before printing, by reading the cited range from `git show <tree ID from Phase 1>:<path>`. I drop fabricated citations and downgrade that verdict confidence to `low`. Every `Rn` from Phase 1 appears exactly once in the table. N equals d + p + nd + o + u. A requirement with no returned verdict is `unverified`, not omitted. I put only evidence IDs in table cells, then render every complete `Rn → E<n>` edge in a block-form source map. Code citations remain separate verdict evidence. -The audit is read by whoever inherits this ticket, and Phase 5 may post it to GitHub unchanged. I write it with no em or en dashes. `unslop` carries the rest of the rules where it is installed. +Whoever inherits this ticket reads the audit, and Phase 5 may post it to GitHub unchanged. I write it with no em or en dashes and follow `unslop` where it is installed. Then I print this. @@ -145,10 +145,10 @@ Then I print this. | # | Requirement | Source evidence | Verdict | Code evidence | |---|-------------|-----------------|---------|---------------| -| R1 | <short text> | E1 | ✅ done | `<file:line>` | -| R2 | <short text> | E2, E4 | 🟡 partial | `<file:line>`: <gap, short> | -| R3 | <short text> | E3 | ❌ not done | none | -| R4 | <short text> | E5 | 🪦 obsolete | <reason, short> | +| R1 | <short text> | E1 | done | `<file:line>` | +| R2 | <short text> | E2, E4 | partial | `<file:line>`: <gap, short> | +| R3 | <short text> | E3 | not done | none | +| R4 | <short text> | E5 | obsolete | <reason, short> | ## Source evidence map @@ -207,7 +207,11 @@ First I reconcile every `attempting`, `reconcile-required`, or `landed` entry. I For each mutation card, I record every payload path and SHA-256 digest, exact title, label, and command options, fresh target guards, and per-step expected guard transition. I pass the exact targets, audit commit and tree IDs, ordered actions, Phase 4 approval, and split metadata to `preflight-mutations` immediately before that card first write. Before each later write, I advance guards only from the prior write authoritative read-back. -After the successor lands, I apply the source-currency gate, then render and freeze the predecessor comment with the real successor URL. I obtain fresh explicit approval for its exact bytes and digest, the current predecessor and successor guards, and the ordered comment to close actions with the expected comment guard transition. After authoritative read-back proves the exact comment landed, I advance to the returned expected guard and permit close without reapproval. Any unexpected transition or changed external guard, source evidence, payload byte, scope, verdict, recommendation, or fate invalidates approval. I reconcile the landed successor through an explicit retain, edit, or close plan and return to Phase 4 when semantics changed. I never restart successor creation. +After the successor lands, I apply the source-currency gate, then render and freeze the predecessor comment with the real successor URL. I obtain fresh explicit approval for its exact bytes and digest, the current predecessor and successor guards, and the ordered comment to close actions with the expected comment guard transition. + +After authoritative read-back proves the exact comment landed, I advance to the returned expected guard and permit close without reapproval. + +Any unexpected transition or changed external guard, source evidence, payload byte, scope, verdict, recommendation, or fate invalidates approval. I reconcile the landed successor through an explicit retain, edit, or close plan and return to Phase 4 when semantics changed. I never restart successor creation. --- diff --git a/skills/backend-perf/SKILL.md b/skills/backend-perf/SKILL.md index fdeccac..d3ea7b6 100644 --- a/skills/backend-perf/SKILL.md +++ b/skills/backend-perf/SKILL.md @@ -5,7 +5,7 @@ description: Backend and database performance checklist. Use when writing or rev # Backend performance checklist -I walk every check below against the code in hand and state a verdict on each one. Applied, clean, or not applicable and why. A check I did not name is a check I did not run. +I walk every check below against the code in hand and state a verdict on each one: applied, clean, or not applicable and why. A check I did not name is a check I did not run. Checks marked `[pg]` are PostgreSQL-specific. I skip them on other stores rather than translating them. @@ -17,4 +17,4 @@ Checks marked `[pg]` are PostgreSQL-specific. I skip them on other stores rather - **Select only needed columns**. List endpoints name their columns instead of selecting the whole row. - **Reuse fetched rows**. A row fetched for an ownership check is the row the handler returns. No second fetch by id. - **EXPLAIN before rewriting** `[pg]`. Any query restructure, batching, joining, indexing, or replacement of parallel-N with batched-1 ships `EXPLAIN (ANALYZE, BUFFERS)` numbers for current versus proposed shape on representative data. Keep the faster shape. A rewrite that looks faster in the diff can run 10-100× slower on real data. -- **Race conditions**. When 2+ writers can touch one row, use a transaction. Async completion order is not source order. Order explicitly with `await` or `Promise.all` instead of relying on accidental serialization. A check plus a write that must stay atomic uses `SELECT ... FOR UPDATE` on `[pg]` or a unique constraint, not application-level locking. +- **Race conditions**. When 2+ writers can touch one row, use a transaction. Async completion order is not source order. Order explicitly with `await` or `Promise.all` instead of relying on accidental serialization. A check plus a write that must stay atomic uses `SELECT ... FOR UPDATE` `[pg]` or a unique constraint, not application-level locking. diff --git a/skills/browser-qa/SKILL.md b/skills/browser-qa/SKILL.md index c76891f..d6ac4af 100644 --- a/skills/browser-qa/SKILL.md +++ b/skills/browser-qa/SKILL.md @@ -1,28 +1,30 @@ --- name: browser-qa -description: Drive a real browser through a UI flow, or run API and endpoint QA and report structured test results. Use when the user names a flow or an endpoint to test against a URL, when verifying filters, pagination, error handling, auth or counts on an API, after a UI or backend change lands, or when a PR needs before and after screenshots. +description: Drive a real browser through a UI flow or QA an endpoint, and report structured results. Use when the user names a flow or endpoint to test at a URL, when verifying filters, pagination, error handling, auth or counts on an API, after a UI or backend change, or when a PR or PR URL needs before and after screenshots, browser QA, or a visual-regression check. --- I drive a real browser through the flow and I do not call it done until I watched it work. Every step gets a screenshot, the full run gets a recording, API-triggering actions get a network check, and I read the console for new errors. A change headed for a PR also runs the same flow on the base code, so the PR shows before next to after. ## Driver priority -1. When `t3-code_preview_snapshot` is in the tool list, I use the T3 preview tools only. I open with `t3-code_preview_open`, go to the URL with `t3-code_preview_navigate`, then loop `t3-code_preview_snapshot` followed by `t3-code_preview_click` or `t3-code_preview_type`. I re-snapshot before every interaction because refs go stale. I keep `open=true` so the user sees the run. +1. When `t3-code_preview_snapshot` is in the tool list, I use the T3 preview tools only. I open with `t3-code_preview_open`, go to the URL with `t3-code_preview_navigate`, then loop `t3-code_preview_snapshot` followed by `t3-code_preview_act` with `action: "click"` or `action: "type"` and a `locator` of `aria-ref=<ref>` from that snapshot. I re-snapshot before every interaction because refs go stale. I keep `open=true` so the user sees the run. 2. Else, when Playwright MCP `browser_navigate` exists, I use the Playwright flow unchanged. 3. Else, I use the `agent-browser` CLI (`open`, `snapshot -i`, `click @eN`, `fill @eN`). I report any evidence the CLI cannot produce as missing, never assume it. -Playwright to T3 mapping: `browser_navigate` becomes `t3-code_preview_navigate`, `browser_snapshot` becomes `t3-code_preview_snapshot`, `browser_click` becomes `t3-code_preview_click`, `browser_type` becomes `t3-code_preview_type`, `browser_press` becomes `t3-code_preview_press`, `browser_wait_for` becomes `t3-code_preview_wait_for`. Screenshots come from `t3-code_preview_snapshot` with `save=true`, which returns a `screenshotPath` to move into `.qa/`. +Playwright to T3 mapping: `browser_navigate` becomes `t3-code_preview_navigate`, `browser_snapshot` becomes `t3-code_preview_snapshot`, `browser_click` becomes `t3-code_preview_act` with `action: "click"`, `browser_type` becomes `t3-code_preview_act` with `action: "type"`, `browser_press` becomes `t3-code_preview_act` with `action: "press"`, `browser_wait_for` becomes `t3-code_preview_wait_for`. Every click and type call targets a `locator` of `aria-ref=<ref>`, with the ref taken from `t3-code_preview_snapshot`. A press goes to the focused element, so I click the field first. Screenshots come from `t3-code_preview_snapshot` with `save=true`, which returns a `screenshotPath` to move into `.qa/`. ## Recording -I record every test flow run, not just the screenshots. On T3 I bracket the flow with `t3-code_preview_recording_start` before the first step and `t3-code_preview_recording_stop` after the last one, which saves a local evidence artifact. On the `agent-browser` CLI I bracket with `record start <head repository root>/.qa/<flow-name>.webm`, given as an absolute path, and `record stop`, because its daemon resolves a relative path against its own working directory and the recording fails to save. Playwright MCP has no video tool, so there the screenshot sequence is the record and I report video as unavailable, never faked. +Every test flow run gets a recording as well as screenshots. On T3 I bracket the flow with `t3-code_preview_recording_start` before the first step and `t3-code_preview_recording_stop` after the last one, which saves a local evidence artifact. On the `agent-browser` CLI I bracket with `record start <head repository root>/.qa/<flow-name>.webm`, given as an absolute path, and `record stop`, because its daemon resolves a relative path against its own working directory and the recording fails to save. Playwright MCP has no video tool, so there the screenshot sequence is the record and I report video as unavailable, never faked. -Record at minimum 1920x1080, and hold 30 fps minimum on drivers that expose frame-rate control. On T3 I set the viewport with `t3-code_preview_resize` to at least that size before recording starts. On the CLI the recording keeps the size the page had at `record start` (observed on `agent-browser` 0.31.1), so I run `set viewport 1920 1080` before it. After `record stop` I still check the artifact dimensions and report when the file misses the floor instead of claiming it. Neither driver exposes a frame-rate control, so I take the recorder default and report it when the file lands. +Record at minimum 1920x1080. On T3 I set the viewport with `t3-code_preview_resize` to at least that size before recording starts. On the CLI the recording keeps the size the page had at `record start` (observed on `agent-browser` 0.31.1), so I run `set viewport 1920 1080` before it. After `record stop` I still check the artifact dimensions and report when the file misses the floor instead of claiming it. Neither driver exposes a frame-rate control, so I take the recorder default and report it when the file lands. ## Before and after When the run verifies a change headed for a PR, as `done` runs it before publishing, I load `${CLAUDE_SKILL_DIR}/references/before-after.md` before Step 1. It picks where the before side runs, fixes what both sides share, and holds the `.qa/pr-evidence.md` template. A UI change runs the flow on both sides. An API change runs, on the before side, only the cases whose expected response the change alters, and reports them in the Changed behavior table below. The before side's server is its own origin, so those cases need their own origin approval under the endpoint rules below. A run with no PR ahead of it, such as a user asking to click through a flow, skips this section. +PR before and after captures come from the app's own routes, with its real layout and stylesheet. A component harness or fixture page may support a finding when I label it as one, and I never use it as PR evidence. + ## Input - A natural language flow, for example "Create a recipe with 3 ingredients, verify nutrition calculates". @@ -48,14 +50,14 @@ fi I check whether the dev server is running with `curl -s -o /dev/null -w "%{http_code}" <url>`. When it is not reachable, I ask the user to start it. -## Step 2: Auth Check +## Step 2: Auth check 1. I open the URL with the driver from the priority above and take a snapshot of the page. 2. I look for login form indicators, things like input[type=password], /login in the URL, or auth-related text. -3. When I find them, the session is not logged in. I tell the user to log in manually in their browser, then say "done". I re-check after. +3. When I find them, the session is not logged in. Before I ask the user for a login, a login code, or an OTP, I check project memory and the project's CLAUDE.md for a standing login method or standing approval. I use one only when it names the same normalized origin and role as this session, and I record its source on the report's Auth line. When it fails, I ask the user once and never retry it in a loop. When no standing login applies, I tell the user to log in manually in their browser, then say "done". I re-check after. 4. When I find none, that alone proves nothing. A logged-out page can render without a visible form. I confirm a post-login marker first, an account name, a logout or profile control, or user-specific content. Only with a marker do I proceed to test execution. Without one I treat the session as unknown and ask the user to log in and confirm. -## Step 3: Execute Test Flow +## Step 3: Execute test flow I parse the user flow into numbered steps and launch a single subagent. The subagent uses the driver I picked in Step 2 and follows the driver priority above. @@ -101,14 +103,24 @@ When the run tests an API or endpoint and not only a click path, I build a test Endpoint execution and auth. Steps 2 and 3 authenticate and drive only the browser, so an endpoint-only run needs its own executor and authenticated context. -Before any endpoint case runs I record the session origin, the scheme, host, and port Step 2 authenticated against. Every target is checked against that one value, and Test Environment reports it as Session origin rather than defining it. I compute the triple rather than read the URL. I accept only `http` and `https` targets and reject every other scheme, `file`, `ftp` and `gopher` among them, before any approval or dispatch and whether or not the case carries credentials, so the runner never reads a local file or opens a non-HTTP connection on my say-so. I reject any URL whose authority carries userinfo, a percent-escape, or a backslash, then lowercase the scheme and host, convert the host to ASCII punycode, drop one trailing dot, and fill in the default port, 443 for HTTPS and 80 for HTTP. Any other origin needs its own approval, an API on a separate host from the browser app included. Naming or pasting an endpoint is not approval. I show the user the normalized origin and name which credentials I would send, the session cookie and the anti-CSRF token by name and never by value, get an answer in that turn, and record the origin they approved in Test Environment. Without it the case is BLOCKED and I send no request at all, credentialed or not, so a typo or a pasted link receives neither a live session nor a seeded record ID. +Before any endpoint case runs, I take these steps in order: + +1. I record the session origin, the scheme, host, and port Step 2 authenticated against. Every target is checked against that one value, and Test Environment reports it as Session origin rather than defining it. I compute the triple rather than read the URL. +2. I accept only `http` and `https` targets and reject every other scheme, `file`, `ftp` and `gopher` among them, before any approval or dispatch and whether or not the case carries credentials, so the runner never reads a local file or opens a non-HTTP connection on my say-so. +3. I reject any URL whose authority carries userinfo, a percent-escape, or a backslash. +4. I lowercase the scheme and host, convert the host to ASCII punycode, drop one trailing dot, and fill in the default port, 443 for HTTPS and 80 for HTTP. +5. Any other origin needs its own approval, an API on a separate host from the browser app included. Naming or pasting an endpoint is not approval. I show the user the normalized origin and name which credentials I would send, the session cookie and the anti-CSRF token by name and never by value, get an answer in that turn, and record the origin they approved in Test Environment. Without it the case is BLOCKED and I send no request at all, credentialed or not, so a typo or a pasted link receives neither a live session nor a seeded record ID. A credentialed request in either lane must be HTTPS unless its host is written as a loopback literal, an address in `127.0.0.0/8`, `::1`, or `::ffff:127.0.0.0/104`, or the bare name `localhost`. I read that literal out of the URL and never accept a name I resolved myself, because curl and the browser both resolve again before they connect, so a rebind or a mixed A and AAAA answer can put the request on a public address after my check passed. A host on plain HTTP that is not one of those literals carries no credentials in either lane, so its cases are BLOCKED rather than tested in cleartext. I get that context one of two ways, and both run those checks first: - `curl` with the Step 2 session exported into it, its cookies plus the anti-CSRF token. I never pass `-L`, credentialed or not, so every request goes only to the origin I checked. A 3xx is the case's observed result: I record the status and `Location`, score them against the expected response, and never issue the next hop. I carry the session in a host-scoped cookie jar with `-b jar.txt`; when the jar's cookie domain does not cover an approved endpoint origin I set an explicit `Cookie` header for that one origin instead. Plain unauthenticated `curl` is enough only when the endpoint needs no session. -- An in-page request through the driver's evaluate hook. The page the hook runs in has to clear the origin check too, because a `fetch` is same-origin to whatever page is open and the browser attaches the session cookie on its own. A same-origin `fetch` adds no anti-CSRF header, so I either call the app's own request helper, the client it already uses for API calls, which attaches the token, or read the anti-CSRF token from the cookie or meta tag and set it in `RequestInit.headers`. A cross-origin request also needs `credentials: "include"` and matching CORS. `fetch` follows redirects on its own and carries the anti-CSRF header and cookies to the next hop, so I set `redirect: "error"` on every evaluate-hook request. A rejection alone proves neither a redirect nor dispatch: I record the observed error and use available network evidence to distinguish proven no-dispatch (BLOCKED) from dispatched without a response (FAIL). When dispatch remains unknown I report FAIL with that uncertainty, never an assumed redirect or pass, and do not rerun through curl because the server may already have applied the request. A case that needs redirect status or `Location` runs through the curl lane from the start. +- An in-page request through the driver's evaluate hook. The page the hook runs in has to clear the origin check too, because a `fetch` is same-origin to whatever page is open and the browser attaches the session cookie on its own. A same-origin `fetch` adds no anti-CSRF header, so I either call the app's own request helper, the client it already uses for API calls, which attaches the token, or read the anti-CSRF token from the cookie or meta tag and set it in `RequestInit.headers`. + + A cross-origin request also needs `credentials: "include"` and matching CORS. `fetch` follows redirects on its own and carries the anti-CSRF header and cookies to the next hop, so I set `redirect: "error"` on every evaluate-hook request. + + A rejection alone proves neither a redirect nor dispatch: I record the observed error and use available network evidence to distinguish proven no-dispatch (BLOCKED) from dispatched without a response (FAIL). When dispatch remains unknown I report FAIL with that uncertainty, never an assumed redirect or pass, and do not rerun through curl because the server may already have applied the request. A case that needs redirect status or `Location` runs through the curl lane from the start. I never accept an unauthenticated response as a case result; when I cannot obtain the authenticated context I record those cases BLOCKED instead of testing the wrong thing. An endpoint case that mutates shared data goes through the same mutation preflight as Step 3 before I dispatch it. Screenshots and the recording do not apply when no UI is exercised, so the captured request and response are the evidence for each case. @@ -128,7 +140,7 @@ I capture the environment once so the run is reproducible, and every case row re - After: <HEAD SHA>, plus the uncommitted working tree when there is one, at <URL> - Widths: <px, comma separated> - Test data: <seeded records and their IDs> -- Auth: <account, role, IDs> +- Auth: <account, role, IDs, and the memory file or CLAUDE.md that supplied a standing login method, if one did> ### UI flow: <flow description> @@ -167,4 +179,6 @@ I capture the environment once so the run is reproducible, and every case row re VERDICT: <ALL PASS | PARTIAL | FAIL | BLOCKED> (<passed>/<executed> steps and cases, <blocked> blocked) ``` -I add only the sections the run has: the UI flow table for a click path, one table per category the endpoint has, and the Before, After, and Widths lines and the Changed behavior table only on a run headed for a PR. Rows number continuously across tables, so Findings can cite a case by number, and a Changed behavior row reuses the number of the case it restates. The copy in `.qa/pr-evidence.md` is the report's PR form. Its Test Environment keeps Date, the Before revision and source without a URL, `After: this PR's head`, Widths, Test data, and Auth reduced to the role, and drops origins, backend URLs, account names, and record IDs, which stay in the chat report. Case rows stay as written, and `file-pr`'s data approval covers them. Its Browser UI Testing section gives the console result and counts, for example `Screenshots: 6 and recordings: 2, embedded in the Before and after section`, names no paths or tools, and is left out when no UI ran. A case whose request I never dispatched is BLOCKED, never PASS or FAIL, and so is a case the unauthenticated-response rule above blocks. A request counts as dispatched once its bytes left the client, so a refused connection or a failed DNS lookup is not dispatched. When dispatch cannot be determined after a fetch rejection, I count the case as executed with FAIL and state that dispatch is unknown; I never claim it was dispatched or retry it. I list a blocked case with its blocker, keep it out of the executed count, and cap the verdict at PARTIAL when other cases executed, or BLOCKED when none did. The Browser UI Testing section reuses the screenshots and recording from the browser flow above, so a mixed run reports the click path and the endpoints behind it in one place. Findings state failures and by-design behavior explicitly; a silent omission reads as a pass it never earned. +I add only the sections the run has: the UI flow table for a click path, one table per category the endpoint has, and the Before, After, and Widths lines and the Changed behavior table only on a run headed for a PR. Rows number continuously across tables, so Findings can cite a case by number, and a Changed behavior row reuses the number of the case it restates. The copy in `.qa/pr-evidence.md` is the report's PR form. Its Test Environment keeps Date, the Before revision and source without a URL, `After: this PR's head`, Widths, Test data, and Auth reduced to the role, and drops origins, backend URLs, account names, and record IDs, which stay in the chat report. Case rows stay as written, and `file-pr`'s data approval covers them. Its Browser UI Testing section gives the console result and counts, for example `Screenshots: 6 and recordings: 2, embedded in the Before and after section`, names no paths or tools, and is left out when no UI ran. + +A case whose request I never dispatched is BLOCKED, never PASS or FAIL, and so is a case the unauthenticated-response rule above blocks. A request counts as dispatched once its bytes left the client, so a refused connection or a failed DNS lookup is not dispatched. When dispatch cannot be determined after a fetch rejection, I count the case as executed with FAIL and state that dispatch is unknown; I never claim it was dispatched or retry it. I list a blocked case with its blocker, keep it out of the executed count, and cap the verdict at PARTIAL when other cases executed, or BLOCKED when none did. The Browser UI Testing section reuses the screenshots and recording from the browser flow above, so a mixed run reports the click path and the endpoints behind it in one place. Findings state failures and by-design behavior explicitly; a silent omission reads as a pass it never earned. diff --git a/skills/bulk-edit/SKILL.md b/skills/bulk-edit/SKILL.md index 68738a0..769f96d 100644 --- a/skills/bulk-edit/SKILL.md +++ b/skills/bulk-edit/SKILL.md @@ -7,9 +7,9 @@ description: Plan and run one mechanical edit across a whole population of recor I turn "change this field on all of them" into a transform that refuses what it cannot prove, a run that survives interruption, and a verification that also covers the records I was never meant to touch. `preflight-mutations` authorizes the writes; I own everything around them. -The failure I exist to prevent is not a crash. It is a run that reports success while having silently flattened a distinction the population depended on. Every gate below exists because that failure is quiet. +The failure I prevent is a run that reports success after silently flattening a distinction the population depended on. Every gate below exists because that failure makes no noise. -`${CLAUDE_SKILL_DIR}/references/worked-example.md` walks one real 638-record run, including the four near-misses that put these gates here. Read it when a gate below looks like overhead. +`${CLAUDE_SKILL_DIR}/references/worked-example.md` walks one real 638-record run, including the four near-misses that put these gates here. Read it before skipping or shortening any gate. ## 1. Fix the population and the field @@ -23,7 +23,7 @@ I count the population and the candidate subset separately. Most requests name t The example in the request is one variant, never the specification. I dump the whole population locally, once, and enumerate the real shapes by frequency. I do not sample, and I do not eyeball. -I count the separators, delimiters, prefixes and joiners that actually occur, then read the long tail. The frequent shapes are the ones I already knew about. **The rare shapes are the ones that break the transform**, and they are invisible in a sample: a joiner appearing four times in three thousand records is exactly the case whose meaning differs from the common one. +I count the separators, delimiters, prefixes and joiners that actually occur, then read the long tail. The frequent shapes are the ones I already knew about. The rare shapes are the ones that break the transform, and they are invisible in a sample: a joiner appearing four times in three thousand records is exactly the case whose meaning differs from the common one. For each distinct shape I ask what it means, not what it looks like. Two shapes that render similarly and mean differently are the trap. A chain arrow and an ampersand can sit in the same position and read as "then" and "and", and collapsing both the same way destroys the second. @@ -45,7 +45,7 @@ I take a fresh snapshot of the whole population's current value immediately befo I dry run against the live system, not against the dump. The dry run's job is to prove the transform still applies to what is actually there. -Whitespace and encoding drift will show up here as false mismatches. When a record fails an equality check against the dump, I re-derive the transform from the live value and compare the *result*. If the result matches the plan, the record is fine and my guard was too strict. That check is better than equality anyway: it validates against what exists rather than against what I remember. +Whitespace and encoding drift will show up here as false mismatches. When a record fails an equality check against the dump, I re-derive the transform from the live value and compare the *result*. If the result matches the plan, the record is fine and my guard was too strict. That check beats equality because it validates against what exists, not what I remember. **Gate.** The snapshot is timestamped, stored outside the working tree, and covers the whole population. The dry run ran against live values and its counts reconcile with section 3. @@ -101,7 +101,7 @@ The account behind the writes tells me a job exists, not where it runs. A machin When someone names the job's location, I verify the location separately from the schedule. Those two claims fail independently, and the schedule being right is not evidence that the path is. A recalled path from an unavailable memory system is a hypothesis. -**Sequencing matters more than speed here.** If the producer rewrites records whose field disagrees with some source of truth, then an edit that makes *every* record disagree hands that job the largest possible batch. The cleanup and the producer change have an order, and doing them backwards costs more churn than doing nothing. I say so plainly rather than proceeding. +**Sequencing matters more than speed here.** If the producer rewrites records whose field disagrees with some source of truth, then an edit that makes *every* record disagree hands that job the largest possible batch. The cleanup and the producer change have an order, and doing them backwards costs more churn than doing nothing. I say so instead of proceeding. **Gate.** I have named the producer with evidence, or stated that I looked and found none. If a producer exists and is still live, I report the sequencing constraint before recommending any follow-up edit. diff --git a/skills/converge-reviews/SKILL.md b/skills/converge-reviews/SKILL.md index 5074199..07a69c9 100644 --- a/skills/converge-reviews/SKILL.md +++ b/skills/converge-reviews/SKILL.md @@ -5,7 +5,7 @@ description: Reconcile repeated code-review rounds against one scope and finding # Converge reviews -I reuse valid review evidence, invalidate only changed coverage, and stop a stable scope after three review/fix rounds. I find this cap is what breaks the loop. Without it, every round finds something new to say about unchanged code and the review never lands. This skill coordinates review state. It does not review code, apply fixes, or create external issues. +I reuse valid review evidence, invalidate only changed coverage, and stop a stable scope after three review/fix rounds. Without that cap, every round finds something new to say about unchanged code and the review never lands. This skill coordinates review state. It does not review code, apply fixes, or create external issues. ## 1. Accept one review packet @@ -18,15 +18,15 @@ I require all of these before I start. - Findings with stable IDs, severity, source reviewer, evidence, and disposition. - Any prior convergence ledger. -When the caller already owns richer finding state, I reference its artifact and finding IDs instead of copying finding bodies. The `review-pr` finding-state file remains authoritative for PR finding status. +When the caller already owns richer finding state, I reference its artifact and finding IDs instead of copying finding bodies. The `review-pr` finding-state file stays authoritative for PR finding status. -**Gate.** Every reviewer is accounted for and every finding is traceable to one scope, reviewer, and disposition. +**Gate.** Every reviewer is accounted for and every finding traces to one scope, reviewer, and disposition. ## 2. Identify scope and coverage A stable scope is the canonical hash of the originating request, review baseline, sorted paths, reviewer roster, and lenses. I record the current content/diff hash as the reviewed snapshot and on each coverage entry. -I keep convergence state in the caller existing authorized review artifact. When none exists and the workflow is authorized to maintain local review state, I use `.claude/review-state/local-<scope-id>.yml` and keep that directory ignored. Otherwise I render the block inline without creating a file. I store only this. +I keep convergence state in the caller's existing authorized review artifact. When none exists and the workflow is authorized to maintain local review state, I use `.claude/review-state/local-<scope-id>.yml` and keep that directory ignored. Otherwise I render the block inline without creating a file. The block holds only these fields: ```yaml convergence: @@ -44,29 +44,31 @@ convergence: next_action: <exact next action> ``` -The same snapshot and contract reuses the recorded result without incrementing the round. Fixes for recorded findings advance the same scope. A new requirement, new review lens, or materially new component creates a child scope at round 1. It inherits active blockers on unchanged parent coverage and cannot reset a capped parent. Cosmetic, generated, and unrelated changes neither reset the round nor invalidate coverage. +The same snapshot and contract reuses the recorded result without incrementing the round. Fixes for recorded findings advance the same scope. A new requirement, new review lens, or materially new component creates a child scope at round 1. The child inherits active blockers on unchanged parent coverage and cannot reset a capped parent. Cosmetic, generated, and unrelated changes neither reset the round nor invalidate coverage. -I invalidate only coverage whose reviewed content, path, or lens changed. I keep unaffected reviewer evidence until its covered content changes. +I invalidate only coverage whose reviewed content, path, or lens changed, and keep unaffected reviewer evidence until its covered content changes. **Gate.** Each coverage entry is either reused with a matching hash and contract or marked for re-review. ## 3. Reconcile the round -I merge findings by stable ID. I preserve source reviewers and disposition history. The caller finding status remains authoritative. I derive `normalized_status` at reconciliation time without writing it back as caller status. Map `active`, `regression`, and caller-specific open states to `open`; `resolved`, `dismissed`, and `wontfix` to `closed`; and `follow-up` to `deferred` outside the current scope. +I merge findings by stable ID and preserve source reviewers and disposition history. The caller's finding status stays authoritative. I derive `normalized_status` at reconciliation time and never write it back as caller status. Map `active`, `regression`, and caller-specific open states to `open`; `resolved`, `dismissed`, and `wontfix` to `closed`; `follow-up` to `deferred` outside the current scope; and the filing outcomes `filed <issue URL>` and `declined` to `closed`. -I count one round only after the planned roster returned or was explicitly recorded unavailable and the caller reconciled every finding. I record fixes, dismissals, regressions, and newly invalidated coverage before selecting the result. +I count a round only after every planned reviewer returned or was explicitly recorded unavailable and the caller reconciled every finding. I record fixes, dismissals, regressions, and newly invalidated coverage before selecting the result. -**Gate.** The ledger explains what changed since the prior round and why every prior finding or coverage entry remains valid, changed, or closed. +**Gate.** The ledger explains what changed since the prior round and why every prior finding or coverage entry stays valid, changed, or closed. ## 4. Return one result -- `continue`: fewer than three rounds are complete and the exact next fix or affected-coverage review is named. -- `converged`: no Critical or Serious finding is open, no worthwhile Moderate or Minor follow-up remains, and all required coverage is current. -- `blocked-at-cap`: round three is complete and at least one Critical or Serious finding remains open. Stop review churn and name the exact blocker fix. After that fix, allow one targeted closure check over those blocker IDs and changed sites; it updates coverage and dispositions but performs no new-finding sweep and does not count as round four. If the check fails, remain blocked pending an explicit new plan or scope decision. -- `follow-up-proposed`: no Critical or Serious finding is open, and worthwhile Moderate or Minor findings are listed as proposed follow-up work. Keep the proposal local until the user approves external issue creation; then invoke `preflight-mutations` before creating issues. +- `continue`: fewer than three rounds are complete and the result names the exact next fix or affected-coverage review. +- `converged`: no Critical or Serious finding is `open`, no Moderate or Minor finding is `open`, no finding of any severity carries disposition `follow-up`, and all required coverage is current. +- `blocked-at-cap`: round three is complete and at least one Critical or Serious finding remains open. Stop review churn and name the exact blocker fix. After that fix, allow one targeted closure check over those blocker IDs and changed sites. It updates coverage and dispositions, runs no new-finding sweep, and does not count as round four. If the check fails, stay blocked until an explicit new plan or scope decision. +- `follow-up-proposed`: no Critical or Serious finding is `open`, and at least one finding remains that blocks `converged`: a Moderate or Minor finding still `open`, or a finding of any severity with disposition `follow-up`. The result lists every such finding as a proposed follow-up issue, so nothing drops silently. -Round three is a hard cap for the stable scope. I treat reworded findings, another reviewer over unchanged coverage, or a cosmetic diff as more of the same round, never round four. The one targeted blocker closure check exists only to verify named fixes. It cannot widen coverage or emit fresh findings. +Round three is a hard cap for the stable scope. Reworded findings, another reviewer over unchanged coverage, or a cosmetic diff count as the same round, never round four. The one targeted blocker closure check only verifies named fixes. It cannot widen coverage or emit fresh findings. -The caller applies this contract. It reruns affected review coverage only for `continue`, proceeds for `converged`, stops for `blocked-at-cap`, and presents the proposal and approval boundary for `follow-up-proposed`. After a named blocker fix at the cap, `closure_check: available` is the dispatch signal for the one targeted check. The caller passes only those blocker IDs and changed sites, leaves `round` unchanged, and rejects any new-finding sweep. +The caller files a proposed issue automatically, after `preflight-mutations`, when its finding is Critical, Serious, or an important user-facing problem, one a user of the shipped product can hit. It asks the user about every other proposed issue. An unattended run files only that automatic class. Every proposed issue that ends unfiled, whether held by an unattended run, declined by the user, or blocked by `preflight-mutations`, goes under "Not filed" in the PR body or report with its reason. Once an issue is filed or declined, I record its disposition as `filed <issue URL>` or `declined`, so later rounds never propose it again. + +The caller applies this contract. It reruns affected review coverage only for `continue`, proceeds for `converged`, stops for `blocked-at-cap`, and applies the filing rule above for `follow-up-proposed`. After a named blocker fix at the cap, `closure_check: available` is the dispatch signal for the one targeted check. The caller passes only those blocker IDs and changed sites, leaves `round` unchanged, and rejects any new-finding sweep. **Done.** The ledger is current, unchanged evidence was reused, affected coverage is explicit, the stable-scope round is at most three, and the caller received exactly one result and next action. diff --git a/skills/design-director/SKILL.md b/skills/design-director/SKILL.md index 701538b..d897f0e 100644 --- a/skills/design-director/SKILL.md +++ b/skills/design-director/SKILL.md @@ -48,7 +48,7 @@ Phase 0 ends when you set the baseline and either route the delta to a mode or c 5. Acknowledge the chosen mode in one short sentence, for example "Running Logo Concepts.". -**Phase 1 ends** by setting `MODE`. +Phase 1 ends when it sets `MODE`. ## Mode handoff @@ -68,7 +68,7 @@ Phase 2 ends when every material field for the active route has a value. Echo th Run the mode's Output spec. On a continuation, produce the requested fields, every dependent output, and every invalidated check while you preserve unrelated values. Modes other than `finalize` print rich markdown to chat first. `finalize` follows its build lifecycle and prints the handoff summary only after its blocking checks pass. -I hold every mode to one bar. +Hold every mode to one bar. - Every recommendation is a spec. Name the element and the value: typeface, px, ratio, hex. "Use a serif for headlines" is not a spec. "Pair Söhne for UI with GT Sectra Display for headlines, 1.6 ratio between H1 and H2" is one. - Write in the imperative. Pair the typefaces. Cut the H1 to 48px. Make the call. @@ -94,6 +94,6 @@ Print the mode's `Next step` verbatim, prefixed with `Next:`, on exactly one lin ## Out of scope - Marketing copy goes to `copywriting`. -- Figma implementation goes to `figma-generate-library` or `figma-generate-design`. -- Brand implementation in application UI code goes to `figma:figma-implement-design` or `frontend-design:frontend-design`. +- Figma implementation goes to `figma:figma-generate-library` or `figma:figma-generate-design`. +- Brand implementation in application UI code goes to `figma:figma-design-to-code` or `frontend-design:frontend-design`. - Naming, taglines, mood boards, and iconography systems sit outside this skill. diff --git a/skills/design-director/modes/color.md b/skills/design-director/modes/color.md index 4eaadbc..158e0b5 100644 --- a/skills/design-director/modes/color.md +++ b/skills/design-director/modes/color.md @@ -14,4 +14,4 @@ - **Contrast proof**: classify each intended use, then apply the WCAG 2.2 criteria in the main skill to measured mark, text, and accent pairings in both themes. Label presentation-only colors instead of treating them as accessible text colors. - **Usage ratio**: a one-line 60/30/10 or similar split. It shows how the palette balances in real layouts - **File save**: yes -- **Next step**: → `/figma-generate-library` to add as color variables +- **Next step**: → `/figma:figma-generate-library` to add as color variables diff --git a/skills/design-director/modes/critique.md b/skills/design-director/modes/critique.md index 442036c..558df4b 100644 --- a/skills/design-director/modes/critique.md +++ b/skills/design-director/modes/critique.md @@ -16,4 +16,4 @@ - End every critique with a one-line verdict: **Ship**, **Hold (fix top item first)**, or **Kill (start over)**. - **File save**: no -- **Next step**: if user wants to act on the fixes → `/figma:figma-implement-design` (if Figma) or `/frontend-design:frontend-design` (if code) +- **Next step**: if user wants to act on the fixes → `/figma:figma-design-to-code` (if Figma) or `/frontend-design:frontend-design` (if code) diff --git a/skills/design-director/modes/layout.md b/skills/design-director/modes/layout.md index 2eedcab..d5b9d77 100644 --- a/skills/design-director/modes/layout.md +++ b/skills/design-director/modes/layout.md @@ -10,4 +10,4 @@ - **Spacing & rhythm**, notes on whitespace, density, and the alignment grid - **Visual balance**, where the weight sits and what counterweights it - **File save**: no -- **Next step**: → `/figma-generate-design` +- **Next step**: → `/figma:figma-generate-design` diff --git a/skills/design-director/modes/type.md b/skills/design-director/modes/type.md index 6483fab..a4673c6 100644 --- a/skills/design-director/modes/type.md +++ b/skills/design-director/modes/type.md @@ -8,4 +8,4 @@ - **Hierarchy**: a type scale table, H1 / H2 / H3 / Body / Small / Caption, with size in px, weight, line-height, tracking. Use a 1.25 or 1.333 scale by default. - **Usage guidelines**: 4-6 bullet rules (e.g., "Headlines never wrap below 80% line-length", "Body uses 1.5 line-height; UI elements use 1.2"). - **File save**: yes -- **Next step**: → `/figma-generate-library` to add as text styles / variables +- **Next step**: → `/figma:figma-generate-library` to add as text styles / variables diff --git a/skills/discover-product-domain/SKILL.md b/skills/discover-product-domain/SKILL.md index b1b9b14..fe2a6aa 100644 --- a/skills/discover-product-domain/SKILL.md +++ b/skills/discover-product-domain/SKILL.md @@ -5,7 +5,7 @@ description: Generate, remix, and rank product or app names, then verify their e # Discover product domains -Turn a product idea into memorable names whose exact `.com` domains are confirmed at a registrar. I treat human combinations such as `Vids` + `Jar` as a core discovery mechanism, not an afterthought. +Turn a product idea into memorable names whose exact `.com` domains are confirmed at a registrar. User-made combinations such as `Vids` + `Jar` drive discovery. ## Mode @@ -40,7 +40,13 @@ Capture these fields from the conversation before asking anything: - whether the name should describe the product or work as an abstract mark; - other languages the audience reads, the field whose jargon it speaks, and any existing brand this name must not collide with. -Default unresolved preferences to warm, clear, credible, globally pronounceable, 5-10 characters, 2 syllables where natural, and no hyphens or digits. Default the describe-or-abstract field to no requirement to describe, and read `${CLAUDE_SKILL_DIR}/references/naming-lenses.md`, section "Description is optional", before generating. When the audience's other languages, jargon field, or brand-collision targets go unstated, default to the market language plus English, the audience's own field as the jargon source, and every brand the user has named in this conversation. Record those defaults, because the "Wart check" in `${CLAUDE_SKILL_DIR}/references/naming-lenses.md` runs on them. In guided mode, ask only when a different answer would materially change the candidate set. +Apply these defaults to anything the user left unresolved: + +- Default unresolved preferences to warm, clear, credible, globally pronounceable, 5-10 characters, 2 syllables where natural, and no hyphens or digits. +- Default the describe-or-abstract field to no requirement to describe, and read `${CLAUDE_SKILL_DIR}/references/naming-lenses.md`, section "Description is optional", before generating. +- When the audience's other languages, jargon field, or brand-collision targets go unstated, default to the market language plus English, the audience's own field as the jargon source, and every brand the user has named in this conversation. + +Record those defaults, because the "Wart check" in `${CLAUDE_SKILL_DIR}/references/naming-lenses.md` runs on them. In guided mode, ask only when a different answer would materially change the candidate set. The step is complete when every field is answered or carries an explicit default. @@ -86,7 +92,7 @@ The step is complete when eight non-cosmetic remixes have a recorded screened st ### 6. Control expansion -A provisional name holds `candidate_available_rdap`. That means screened, not confirmed. That status permits registrar confirmation but does not prove anyone can buy the domain. +A provisional name holds `candidate_available_rdap`: screened, not confirmed. It permits registrar confirmation and proves nothing about whether anyone can buy the domain. Use these transitions. Do not wait in step 6 for evidence that only step 7 can produce: diff --git a/skills/done/SKILL.md b/skills/done/SKILL.md index 60c5abe..354043e 100644 --- a/skills/done/SKILL.md +++ b/skills/done/SKILL.md @@ -3,7 +3,7 @@ name: done description: MANDATORY post-task acceptance verification. Fire before reporting ANY task complete. Route code, UI, documentation, global configuration or skills, external metadata or data, and publication or deployment to their user-facing boundaries. --- -# Post-Task Verification (/done) +# Post-task verification (/done) `done` is the single place a task closes. Send each check to the surface the user will touch and use repo checks only as evidence for surfaces they cover. @@ -30,12 +30,14 @@ git merge-base HEAD "refs/remotes/origin/<base-ref>" For external-only work, bind currency to the authoritative targets instead. Record each stable target ID or URL, environment, version or revision when the system exposes one, and the exact read-back ledger. Git fields and commits are not applicable. Mixed Git and external work records both forms of currency. +With the request summary and currency bound, take the no-lane exit only when all of these hold for the task, not the turn: no request item asks for a change, `git status --porcelain` is empty, the diff against the recorded base is empty when a base is recorded, no readiness card is open for this task, and the task made no external write or publication. Then record `no acceptance lane applies` in one line and stop. A post-publication run always resumes its card and never takes this exit. + Work out the required lanes from the original request, the changed artifacts, and the actions already taken. Show the six-lane selection before you run checks, with one reason for every required or not-applicable lane. Keep going without asking for routine confirmation and apply any user correction before the final verdict. | Lane | Select when | |---|---| | Code | Source, tests, scripts, build behavior, or runtime logic changed | -| UI | A user-visible interface or interaction changed | +| UI | A user-visible interface or interaction changed, or the diff against the recorded base changes any `*.tsx`, `*.jsx`, `*.css`, `*.scss`, `*.less`, `*.vue`, or `*.svelte` file other than `*.test.*`, `*.spec.*`, and `*.stories.*` files | | Documentation | A document, generated artifact, link, asset, or navigation path changed | | Global configuration or skills | Agent rules, configuration, registration, discovery, or invocation changed | | External metadata or data | A board, issue, PR, database, remote record, or computed external result changed | @@ -43,7 +45,7 @@ Work out the required lanes from the original request, the changed artifacts, an An unselected lane is `not-applicable`, with its exclusion reason. A selected lane stays required even when you cannot reach its boundary. -**Gate.** request summary and applicable currency are exact, all six lanes carry a selection and reason, and every user-requested outcome maps to every lane that applies to it. Do not require unrelated lanes. +**Gate.** Request summary and applicable currency are exact, all six lanes carry a selection and reason, and every user-requested outcome maps to every lane that applies to it. Do not require unrelated lanes. ## 2. Verify each required lane @@ -53,20 +55,20 @@ When Code or Global configuration or skills is required, run `simplify` once aft | Lane | Minimum boundary evidence | |---|---| -| Code | Run the affected repository-native type, lint, build, and test checks. Use `fix-ts-errors` when TypeScript applies; a scoped first pass is allowed, but always run the full workspace check at least once. Run `parallel-review` with the recorded merge-base SHA as its comparison base, apply its `converge-reviews` result, and account for the request against the diff. `simplify`'s added-comment scan is blocking here: any comment it prints that is still in the diff leaves this lane unverified. Run `reuse-first` in sweep mode and record what it returned, including nothing: `simplify` is scoped by its own gate to duplication *introduced by the change*, so duplication that already existed is invisible to every other check in this lane. PR-bound work that changes API or endpoint behavior also runs `browser-qa` endpoint QA, so the PR carries its QA Test Results. | +| Code | Run the affected repository-native type, lint, build, and test checks. Use `fix-ts-errors` when TypeScript applies; a scoped first pass is allowed, but always run the full workspace check at least once. Run `parallel-review` with the recorded merge-base SHA as its comparison base, apply its `converge-reviews` result, and account for the request against the diff. `simplify`'s added-comment scan is blocking here: any comment it prints that is still in the diff leaves this lane unverified. Run `reuse-first` in sweep mode and record what it returned, including nothing. The lane stays `pending` until `parallel-review`, `simplify`, and the `reuse-first` sweep each have a recorded result line. The `parallel-review` line names every member it reported `unavailable(<reason>)`. PR-bound work that changes API or endpoint behavior also runs `browser-qa` endpoint QA, so the PR carries its QA Test Results. | | UI | Run the affected flow through `browser-qa`; require every affected step to pass and record browser output, screenshots, network results, and console state. For PR-bound work it also runs the base side and writes `.qa/pr-evidence.md`. | | Documentation | Inspect the rendered or generated final artifact and exercise affected links, assets, and navigation. Source text alone does not verify rendered output. | | Global configuration or skills | Parse the final configuration, verify registration and discovery, then check picker visibility, manual invocation, or actual consumer behavior wherever the change affects them. | | External metadata or data | Freshly re-fetch every changed target from the authoritative system and compare exact IDs, fields, counts, or totals with the request and mutation ledger. | | Publication or deployment | Inspect the published consumer or live target at the exact version and environment; a successful upload or deploy command alone is insufficient. | -For the code lane, fix Critical and Serious review findings and apply `converge-reviews`: continue only on `continue`, proceed on `converged`, stop on `blocked-at-cap`, and present any `follow-up-proposed` approval boundary. Re-run only checks invalidated by a fix. +For the code lane, fix Critical and Serious review findings and apply `converge-reviews`: continue only on `continue`, proceed on `converged`, stop on `blocked-at-cap`, and apply the `follow-up-proposed` filing rule from `converge-reviews`. Re-run only checks invalidated by a fix. For a completed shared-state mutation, reuse the exact authoritative read-back plan and landed-item ledger its execution workflow produced. When inference rather than direct observation supports a lane conclusion, run `verify-claims` and keep its evidence ceiling. Fix a failed check and rerun only the evidence it invalidates. If it remains unresolved, assign the lane `blocked`. -**Gate.** every required lane has direct boundary evidence or a concrete gap, and every check an applicable lane names has a recorded result. +**Gate.** Every required lane has direct boundary evidence or a concrete gap, and every check an applicable lane names has a recorded result. ## 3. Assign states @@ -81,7 +83,7 @@ Use this vocabulary for every request item, lane, and evidence facet: Missing boundary evidence creates an evidence ceiling. It never becomes `verified` because another lane passed. -**Gate.** each state follows from recorded evidence, and every non-verified required row names its exact gap and next action. +**Gate.** Each state follows from recorded evidence, and every non-verified required row names its exact gap and next action. ## 4. Build the readiness card diff --git a/skills/done/references/readiness-card.md b/skills/done/references/readiness-card.md index 8144b66..b779e3c 100644 --- a/skills/done/references/readiness-card.md +++ b/skills/done/references/readiness-card.md @@ -73,7 +73,7 @@ fi Record `$verified_content_snapshot` only after the reverse scope check passes for every declared path and every changed hunk or logical change within it. Every index operation uses the isolated temporary index, so the real index stays untouched. `GIT_INDEX_FILE` redirects the index but not the object database, which is shared. Only the declared paths are ever hashed with `git hash-object -w`, so undeclared worktree content is never written into the repository. The command fails closed if setup, hashing, staging, tree creation, or cleanup fails. Its exit trap removes the temporary index on both success and failure. The snapshot is `HEAD` plus exactly the declared paths. It carries their current bytes where they exist and their removal where they were deleted. External-only work uses its target currency and read-back ledger instead of a Git snapshot. -Every prose cell below is read by someone deciding what to do next. Write it with no em or en dashes. `unslop` carries the rest of the rules where it is installed. +Someone deciding what to do next reads every prose cell below. Write it with no em or en dashes. `unslop` carries the rest of the rules where it is installed. Render the drafted mapping with every originating request item exactly once, in request order, and name all lanes that apply to it: @@ -166,6 +166,8 @@ git rev-list --first-parent --reverse <pre-verification-head>..HEAD The first command must succeed, the merge list must be empty, and the ordered first-parent list must exactly equal **Expected append-only commits**. A failed ancestry check is a rebase; a merge or unrecorded commit makes the card stale. For PR-bound work, refresh the recorded base again and require its ref, base-tip SHA, and merge-base SHA to match the card. Freshly re-fetch every mixed external target and compare its recorded version and read-back. Consume `file-pr`'s authoritative publication evidence, verify required CI and review evidence, and re-evaluate every row. Only PR-bound work uses this post-publication run before final `ready`. +New unresolved review threads or review-body findings on the PR, including ones a PR-watch update reports, go through `fix-pr-review` with the review or discussion URL (`#pullrequestreview-` or `#discussion_r`), not the bare PR URL. This post-publication handoff authorizes `fix-pr-review` to execute. Never edit, reply to, or resolve them inline. A thread left open as NEEDS-INPUT keeps the card from final `ready`. + Before final `ready`, repeat the post-commit content seal: ```bash diff --git a/skills/executing-tickets-with-subagents/SKILL.md b/skills/executing-tickets-with-subagents/SKILL.md index 9b43079..3ec5ed2 100644 --- a/skills/executing-tickets-with-subagents/SKILL.md +++ b/skills/executing-tickets-with-subagents/SKILL.md @@ -1,6 +1,6 @@ --- name: executing-tickets-with-subagents -description: Orchestrate ledger-tracked work through subagents. Use for a bundled GitHub ticket with 3+ sub-issues, a request for subagent-driven execution, resuming such a run after compaction, or explicit away/keep-going delegation even when only one task remains. +description: Orchestrate ledger-tracked work through subagents. Use for a bundled GitHub ticket with 3+ sub-issues, a request for subagent-driven execution such as "use subagents", "you be the orchestrator", or "one subagent per PR or ticket", resuming such a run after compaction, or explicit away/keep-going delegation even when only one task remains. --- # Executing tickets with subagents @@ -14,7 +14,7 @@ The main conversation only orchestrates. Every edit, review, and QA pass runs in - **Unattended.** When the user explicitly says they are stepping away and asks work to continue, read `${CLAUDE_SKILL_DIR}/references/unattended-scheduler.md` in full before the first dispatch. Its scheduling, transition, update, and handoff contract applies even to one task. Use the phases below only when the work is also a bundled GitHub ticket. - **Bundled ticket.** Use the phases below for the ticket's intake, waves, and endgame. -Record the selected branch in the ledger before dispatching work. That is the gate. +**Gate.** The selected branch is in the ledger before any dispatch. ## Phase 0: Intake @@ -25,13 +25,13 @@ Record the selected branch in the ledger before dispatching work. That is the ga ## Phase 1: Lock, plan, instrument 1. **Explore** the code with parallel read-only agents. Produce a per-sub-issue verdict of already-done, partially-done, or not-started, each with file:line evidence. -2. **Grill the user.** Run `grill-me` on task order, PR strategy, done-bar per task, tracking method, and anything user-owned such as migrations and commit policy. Grilling ends when every item above has a locked answer written into the ledger. Locked answers are law for every later dispatch. +2. **Grill the user.** Run `grill-me` on task order, PR strategy, done-bar per task, tracking method, and anything user-owned such as migrations and commit policy. Grilling ends when every item above has a locked answer written into the ledger. Every later dispatch follows the locked answers. 3. **Spec then plan.** Write one brief per sub-issue and cover every sub-issue, each with exact code and 2-3 named risks the reviewer must check. Briefs locate by symbol, because line numbers drift as tasks land. Then run the `grill-me` verification pass against the brief set before task 1. 4. **Instrument tracking** before task 1: - Keep a ledger, for example `docs/<ticket>-progress.md`. It is the recovery map, and the contract below defines it. - Keep the `tasks/todo.md` checklist. - Keep one editable GitHub checklist comment on the issue. It carries status plus root cause per item. Save its comment id in the ledger and update it via `gh api ... -X PATCH -F body=@file`. Render the complete initial comment to a file and freeze its SHA-256. Immediately before creating it, refresh the issue's URL, `updatedAt`, body, state, assignees, and comments. Confirm the intended checklist is still absent, then invoke `preflight-mutations` with those guards, the exact frozen file and digest, the locked tracking decision, the expected resulting comment body, and the authoritative comment read-back. Apply its result contract before continuing. Read back the created comment by ID and require its complete body to equal the frozen content. Retry only when authoritative read-back proves no effect. Empty stdout is not failure. - - Keep the harness todo list with `TodoWrite`. + - Keep the todo list with the harness todo tool. ## Ledger contract @@ -50,6 +50,8 @@ Write the ledger for a reader with zero context. Assume the next writer remember 5. **`browser-qa`** for UI tasks. It records original data, restores it, and proves the restoration. Its artifacts go to scratchpad only. Run it between waves, with no implementer active. Hot reload contaminates the session mid-test. 6. **Bookkeeping.** Tick `tasks/todo.md`. Immediately before each wave's checklist PATCH, invoke `preflight-mutations` with the exact issue and comment IDs, the replacement body, the current comment version and body, the locked tracking permission, and the read-back, then apply its result contract. Update the harness todo list and the ledger before the next task. +During any wait longer than 10 minutes, for subagents or API pacing, post a one-line status, attended or not. + Retire a wedged agent instead of re-nudging it. Record its terminal status or successful interrupt, preserve its worktree and diff location, then move the task from active to ready and dispatch a fresh owner. When you cannot prove inactivity, mark the task blocked instead of assigning a second owner. ## Phase 3: Endgame diff --git a/skills/executing-tickets-with-subagents/references/unattended-scheduler.md b/skills/executing-tickets-with-subagents/references/unattended-scheduler.md index 6c939c7..74e2257 100644 --- a/skills/executing-tickets-with-subagents/references/unattended-scheduler.md +++ b/skills/executing-tickets-with-subagents/references/unattended-scheduler.md @@ -12,7 +12,7 @@ Apply this branch when the user explicitly steps away and asks work to continue. 4. Give a task a mutation owner only after dispatch success or authoritative worker-state read-back proves placement. Record stable task and agent IDs exposed by the harness; otherwise retain the ledger's existing task ID without inventing a provider ownership token. Independent reviewers may inspect that owner's result read-only. Apply the parent skill's wedged-owner retirement contract before `active → ready`; a replacement becomes the owner only after terminal or interrupt evidence proves the prior owner inactive and its worktree/diff location is preserved. 5. Send the kickoff update with the fixed pool size and first runnable tasks, or the failed-initialization blocker with both attempts, plus authority boundaries and the next action. -**Gate:** the ledger can reconstruct every task, dependency, state, active owner, per-task pool initialization attempt, authority boundary, and next action without conversation context; `worker_pool` remains unset until nonzero capacity fixes it, and each task receives at most one initialization retry. +**Gate.** The ledger can reconstruct every task, dependency, state, active owner, per-task pool initialization attempt, authority boundary, and next action without conversation context; `worker_pool` remains unset until nonzero capacity fixes it, and each task receives at most one initialization retry. ## Run the scheduler @@ -28,7 +28,7 @@ Repeat until no runnable task remains: Apply the governing global unattended authority contract to commits, pushes, PRs, merges, destructive actions, irreversible actions, and schema mutations. Queue any action outside that authority with exact ready-to-run instructions. -**Gate:** every task transition is in the ledger, every active task has exactly one owner, every waiting-capacity task has none, every reconcile-required task is excluded from dispatch, and no dispatch exceeds the kickoff-fixed pool. +**Gate.** Every task transition is in the ledger, every active task has exactly one owner, every waiting-capacity task has none, every reconcile-required task is excluded from dispatch, and no dispatch exceeds the kickoff-fixed pool. ## Bound capacity failures @@ -40,7 +40,7 @@ A dispatcher capacity rejection consumes one of two attempts for that task. Pool A capacity retry is event-driven: do not poll, nudge the rejected dispatch, or create another retry. Only an explicit dispatcher capacity rejection consumes this path; an ambiguous result follows the reconcile-required contract instead. A worker's task failure is not a capacity rejection and follows the task's ordinary recovery policy. -**Gate:** no task has more than two capacity attempts across its initialization or dispatch path, every capacity wait has no active owner, and every capacity-blocked record names both attempts. +**Gate.** No task has more than two capacity attempts across its initialization or dispatch path, every capacity wait has no active owner, and every capacity-blocked record names both attempts. ## Surface material updates @@ -49,10 +49,11 @@ Update the ledger after every transition. Message the user only for: - kickoff; - a task completion and its verification result; - a new blocker or authority boundary; -- a materially changed plan; or +- a materially changed plan; +- a one-line status during any wait longer than 10 minutes; or - the final handoff. -Do not send a user-facing update for routine dispatches, unchanged waits, or ledger-only bookkeeping. +Do not send a user-facing update for routine dispatches, unchanged waits of 10 minutes or less, or ledger-only bookkeeping. ## Maintain the morning handoff @@ -72,4 +73,4 @@ Keep one current `Morning handoff` section in the ledger: Refresh this section after every transition so interruption never leaves a stale handoff. The final user update summarizes it; the ledger remains authoritative. -**Done:** every task is verified or explicitly blocked for the run, no runnable task remains, and the morning handoff matches the ledger's task records. +**Done.** Every task is verified or explicitly blocked for the run, no runnable task remains, and the morning handoff matches the ledger's task records. diff --git a/skills/file-issue/SKILL.md b/skills/file-issue/SKILL.md index 52772eb..be44d1c 100644 --- a/skills/file-issue/SKILL.md +++ b/skills/file-issue/SKILL.md @@ -5,15 +5,17 @@ description: File one GitHub issue an assignee can act on unaided. Use when repo # File an issue -An issue is read by someone who was not there when the problem was found, often months later. Every bar below exists so that reader can act on it without coming back to ask what it meant. +The reader of an issue was not there when the problem surfaced, often months ago. Every bar below lets that reader act without coming back to ask what it meant. ## 1. Confirm this is one issue One issue carries one observable problem and one acceptance condition. -Work that splits into several tickets with blocking edges between them belongs to `to-tickets`. A spec synthesised from the current conversation belongs to `to-spec`. Hand off rather than filing a container ticket that hides its real scope. +Hand work that splits into several tickets with blocking edges to `to-tickets`, and a spec built from the current conversation to `to-spec`. Never file a container ticket that hides its real scope. -The gate is simple. The scope is one problem with one acceptance condition, or the work is handed off by name. +File an issue nobody asked for without asking only when it is Critical, Serious, or an important user-facing problem, one a user of the shipped product can hit. For anything else, ask the user first. An unattended run files none of the ask-first issues and lists each in its report. A filing that a calling skill's documented step authorizes counts as requested, for example `review-pr`'s `github-posting.md` step 8c or `executing-tickets-with-subagents` Phase 3 endgame step 5, so neither the ask-first rule nor the unattended rule applies to it. + +**Gate.** One problem with one acceptance condition, or a handoff by name, and an unrequested issue clears the filing bar above. ## 2. Search before filing @@ -22,53 +24,51 @@ gh issue list --repo "$repository" --search "<the symptom in your words>" --stat gh issue list --repo "$repository" --search "<the symptom in the reporter's words>" --state all --limit 20 ``` -Search twice with different vocabulary, because the existing ticket was filed by someone who described it differently. `--state all` matters. A closed near-match is either the thing to reopen or the context the new issue needs. +Search twice with different vocabulary, because whoever filed the existing ticket described it differently. Keep `--state all`. A closed near-match is either the thing to reopen or context the new issue needs. -Resolve every near match before filing. Each one ends duplicate, related, or genuinely new. Link a related one with `Refs #N` and one line saying what is different about this one. +Resolve every near match as duplicate, related, or new. Link a related one with `Refs #N` and one line saying how this one differs. -The gate is that both searches ran and every near match has a resolution. +**Gate.** Both searches ran and every near match has a resolution. ## 3. Compose the title -Prefix the title under the issue-title rule in `CLAUDE.md`: conventional-commit type plus the module the work actually lives in, named the way the board reads it rather than the way the directory spells it. +Prefix the title under the issue-title rule in `CLAUDE.md`: conventional-commit type plus the module the work lives in, named the way the board reads it, not the way the directory spells it. -The description clears the same bar as the title step in `file-pr`. Name what you observe, never the mechanism you suspect, and never a generic verb standing alone as the description. +The description clears the same bar as the title step in `file-pr`. Name what you observe, never the mechanism you suspect, and never a generic verb alone. -A good bug title names the wrong behaviour, not the suspected cause. `fix(auth): session drops on tab switch` survives being wrong about the cause. `fix(auth): cookie expiry miscalculated` becomes a lie the moment the cause turns out to be something else. +A bug title names the wrong behaviour, not the suspected cause. `fix(auth): session drops on tab switch` survives a wrong guess about the cause. `fix(auth): cookie expiry miscalculated` becomes a lie the moment the cause turns out to be something else. -The gate is that the title names an observation that stays true independent of the diagnosis. +**Gate.** The title names an observation that stays true whatever the diagnosis. ## 4. Compose the body -Four parts, in this order, each one prose rather than a fragment: +Four parts, in this order, each in prose rather than fragments: - **What happens.** The observed behaviour, opening the body with no heading above it. - **How to see it.** Exact steps, command, URL, or `file:line` evidence. Someone with repo access and nothing else reaches the same observation from this alone. -- **What should happen instead.** Stated separately from the observation, because the gap between them is the actual request. +- **What should happen instead.** Kept apart from the observation, because the gap between them is the request. - **Done looks like.** One condition a reviewer can check. "Works properly" is not one. -Then add only what carries content. That means `Refs #N` links and the environment when it matters. Take the environment from evidence. The URL bar in a screenshot tells you whether this was production or dev. Never assume it. +Then add only what carries content: `Refs #N` links, and the environment when it matters. Take the environment from evidence, such as the URL bar in a screenshot showing production or dev. Never assume it. -Attach screenshots rather than describing them. A described screenshot loses everything the describer did not notice, and the person fixing this needs the image itself. +Attach screenshots instead of describing them. A description loses everything the describer did not notice, and whoever fixes this needs the image. -The gate is that the four parts are present and the reproduction stands on its own without the session that produced it. +**Gate.** All four parts are present and the reproduction works without the session that produced it. ## 5. Cold-read the result -Run the cold-read bars from `file-pr` against the composed title and body, then two more that only an issue has: +Run the cold-read bars from `file-pr` against the composed title and body, plus two that only an issue has: - **Reproduction.** Someone with repo access and no other context reaches the observation. -- **Acceptance.** The done condition is checkable by someone who did not write it. - -A bar you did not name is a bar you did not check. +- **Acceptance.** Someone who did not write the done condition can check it. -The gate is that every bar has a named result and none is failing. +**Gate.** Every bar has a named result and none fails. A bar you did not name is a bar you did not check. ## 6. File it Render the final body to a file and record its SHA-256 digest. Resolve the repository's stable identity and current duplicate-search results, then invoke `preflight-mutations` with the exact repository, title, body path and digest, create options including `--label ai-created`, ownership boundary, and authoritative read-back query. A changed title, option, label, body path, digest, repository, or duplicate result invalidates the card. -Ensure the `ai-created` provenance label exists before creating. It marks every issue filed through this skill so agent-filed issues stay distinguishable from human-filed ones. When it is absent, raise a separate label-creation card through `preflight-mutations` naming the repository, then look the label up by exact name rather than a capped listing, tolerate only the already-exists race on create, and prove the label reads back: +Make sure the `ai-created` provenance label exists before creating. It marks every issue this skill files, so agent-filed issues stay distinguishable from human-filed ones. When it is absent, raise a separate label-creation card through `preflight-mutations` naming the repository. Look the label up by exact name, never through a capped listing, tolerate only the already-exists race on create, and prove the label reads back: ```bash if ! gh api "repos/$repository/labels/ai-created" >/dev/null 2>&1; then @@ -84,10 +84,10 @@ gh issue create --repo "$repository" --title "$title" --body-file "$body_path" - gh issue view <issue-url> --repo "$repository" --json number,title,body,state,author,assignees,labels,url ``` -Set assignee, estimate, or priority only within the project-board ownership boundary in `CLAUDE.md`. Filing an issue does not make those fields yours to set. Never drop the `ai-created` label to set another label. Triage labels go on alongside it. +Set assignee, estimate, or priority only within the project-board ownership boundary in `CLAUDE.md`. Filing an issue does not make those fields yours. Never drop `ai-created` to set another label. Triage labels go on beside it. -Require one created URL, then compare the fetched repository, title, body, labels, metadata, and URL with the approved payload. Require `ai-created` present in the fetched labels. When the command result is missing or ambiguous, search the target repository for exact-title candidates and compare their bodies and metadata. Mark the attempt `reconcile-required` and do not retry until that authoritative search proves whether the issue exists. +Require one created URL, then compare the fetched repository, title, body, labels, metadata, and URL with the approved payload. Require `ai-created` in the fetched labels. When the command result is missing or ambiguous, search the target repository for exact-title candidates and compare their bodies and metadata. Mark the attempt `reconcile-required` and do not retry until that authoritative search proves whether the issue exists. -Print the URL only after the read-back identifies one matching issue. +Print the URL only after read-back identifies one matching issue. -The run is done when the scope is one issue, both duplicate searches ran and resolved, the title names an observation that survives a wrong diagnosis, the body carries all four parts, every cold-read bar is named and clean, and authoritative read-back identifies the printed issue URL carrying the `ai-created` label or the attempt remains `reconcile-required` without retry. +The run is done when every section gate above passes and authoritative read-back identifies the printed issue URL carrying the `ai-created` label, or the attempt stays `reconcile-required` without retry. diff --git a/skills/file-pr/SKILL.md b/skills/file-pr/SKILL.md index 847ffe4..9a2a420 100644 --- a/skills/file-pr/SKILL.md +++ b/skills/file-pr/SKILL.md @@ -5,7 +5,7 @@ description: Open a GitHub pull request whose title and body a reviewer understa # File a pull request -A PR is read by someone who has not seen the session, the diff, or the ticket. Every bar below exists so that reader understands the change in one pass. +The PR's reader has not seen the session, the diff, or the ticket. Every bar below exists so that reader understands the change in one pass. ## 1. Check preconditions @@ -15,7 +15,7 @@ Read the branch and the current `done` readiness card. - **Readiness card.** Require the current Git card defined by `done`, including its request, currency, coverage, lane, evidence, verdict, and next-action fields. Its verdict must be `ready-to-publish`, with this skill as the exact next action. A missing field or another verdict returns to `done`. Do not reconstruct it here. - **Publication mode.** Bind the expected PR state and draft mode to the user's publication request. When the request does not name draft publication, use `OPEN` and `isDraft: false`. -The gate is that branch, card presence, expected state, and draft mode each have a recorded result, and a failed one stops the PR. +**Gate.** Branch, card presence, expected state, and draft mode each have a recorded result, and a failed one stops the PR. ## 2. Validate the base and resolve the issue link @@ -29,7 +29,11 @@ Choose exactly one issue link: Never `Closes` an issue this diff does not finish. -The gate is that the base ref, remote base tip, and merge base match the refreshed remote state, and the issue link comes from issue output rather than assumption. +When the user asked to batch several issues into one PR, each issue already arrives as its own commit, verified by its own `done` run before the next issue starts. The body carries one link line per issue: `Closes #N` for every issue the diff finishes, and the list above for any other. + +When you choose `Refs #N` over a closing keyword, the final report says the issue will not show as linked in GitHub's Development panel, which lists only closing-keyword links, and names the reason from the list above. + +**Gate.** The base ref, remote base tip, and merge base match the refreshed remote state, and the issue link comes from issue output, not assumption. ### Revalidate card currency and handoff eligibility @@ -51,7 +55,7 @@ Require the snapshot to match **Verified content snapshot**. Account every chang For an initial publication card whose **Existing PR URL** is `not-applicable`, require `HEAD` to equal **Pre-verification head** and **Expected append-only commits** to be `none`. For a superseding existing-PR card, require the recorded append-only transition and post-commit content seal from `done` to match current `HEAD` and **Verified content snapshot**. Another commit is forbidden. A changed request, base, unexpected head, or content snapshot makes the card stale. Return to `done` on any mismatch. -The gate is that the recomputed request, branch, base ref and SHAs, head, snapshot, external currency, candidate diff, and `ready-to-publish` verdict all match the card. +**Gate.** The recomputed request, branch, base ref and SHAs, head, snapshot, external currency, candidate diff, and `ready-to-publish` verdict all match the card. ## 3. Compose the title @@ -68,21 +72,21 @@ Banned as the whole description: `improve`, `update`, `handle`, `refactor`, `fix Name who observes the thing in the title. When the only honest answer is "someone reading the diff", the title names a mechanism, so rewrite it. -The gate is that the observer is named and no banned word stands as the description. +**Gate.** The observer is named and no banned word stands as the description. ## 4. Compose the body Open with two to four sentences of plain prose answering what was wrong, then what changed. Write it to make sense to someone who never opens the diff. No heading above it, never a bullet list, never optional. -Then include only the sections that carry real content: +Then include only the sections that have content: - **What changed.** Derive one outcome-focused bullet from each verified request row. Do not infer additions from the diff. -- **Before and after, and QA test results.** When the change alters UI or API behavior, the card's Browser facet names `.qa/pr-evidence.md` from `browser-qa`. Paste it here verbatim, except for embed lines left out on the user's first choice when push access is missing. Its SHA-256 must equal the recorded digest; a missing fragment or another digest returns to `done`, and so does a cold-read bar that fails inside the fragment, named in the return, since `done` regenerates it through `browser-qa`. -- **How to verify.** Derive only from the evidence index's exact observations. Include applicable `not-applicable` reasons and do not invent evidence. A PR without the fragment says here in one line why browser QA does not apply, for example `No UI or API behavior changed, so there is no browser QA.` -- **Risk or scope notes.** Only when something is genuinely uncertain, migrated, or deliberately deferred. +- **Before and after, and QA test results.** When the change alters UI or API behavior, the card's Browser facet names `.qa/pr-evidence.md` from `browser-qa`. Paste it here verbatim, except for embed lines left out on the user's first choice when push access is missing. Its SHA-256 must equal the recorded digest. A missing fragment, another digest, or a cold-read bar that fails inside the fragment returns to `done` with the failure named, because `done` regenerates the fragment through `browser-qa`. +- **How to verify.** Derive only from the evidence index's exact observations. Include applicable `not-applicable` reasons and do not invent evidence. A PR without the fragment says here in one line why browser QA does not apply, for example `No UI or API behavior changed, so there is no browser QA.` When the card's Code lane records a `parallel-review` member as `unavailable(<reason>)`, say here which member and why. It does not block the PR. +- **Risk or scope notes.** Only when something is uncertain, migrated, or deferred on purpose. - The issue link from section 2. -The attach list is the target of every `![...](...)` embed in the fragment outside code, each once, in fragment order, and each file's SHA-256 must equal the digest the Browser facet recorded. A PR with no fragment attaches nothing. The data approval covers the whole fragment, its text as well as its files: paste and attach only when it holds synthetic or redacted data or the user explicitly approved publishing it. Without that approval, stop before any push, create, or body-edit side effect and ask the user to approve the listed files and text, or return to `done` to regenerate on synthetic data. Attaching requires gh v2.99.0 or later; confirm the floor with `gh --version` before any push or create side effect. When the install is older, stop back to `done` for a gh upgrade instead of publishing a UI or API PR without its evidence. Publish bare only on explicit user decision, then upload the fragment's files through section 6's `gh pr edit --attach` step after upgrading; `done` holds Browser `deferred` until they land. +The attach list is the target of every `![...](...)` embed in the fragment outside code, each once, in fragment order, and each file's SHA-256 must equal the digest the Browser facet recorded. A PR with no fragment attaches nothing. The data approval covers the whole fragment, its text as well as its files: paste and attach only when it holds synthetic or redacted data or the user explicitly approved publishing it. Without that approval, stop before any push, create, or body-edit side effect and ask the user to approve the listed files and text, or return to `done` to regenerate on synthetic data. Attaching requires gh v2.99.0 or later; confirm the floor with `gh --version` before any push or create side effect. When the install is older, return to `done` for a gh upgrade instead of publishing a UI or API PR without its evidence. Publish bare only on explicit user decision, then upload the fragment's files through section 6's `gh pr edit --attach` step after upgrading; `done` holds Browser `deferred` until they land. Uploading also needs push access to the base repository. With a non-empty attach list, resolve the base repository from the card's base remote through `gh repo view --json nameWithOwner`, as section 6 does, and read `gh api repos/<nameWithOwner> --jq .permissions.push` before any push, create, or body-edit side effect. When it is not `true`, no upload from this account can replace a local path in the body, so stop before any side effect and ask the user to choose: @@ -95,7 +99,7 @@ A heading with nothing under it comes out. A small PR is the lead paragraph, how The human-authored rule in `CLAUDE.md` governs the body's voice and its ban on agent, pipeline, and session references. -The gate is that the lead paragraph stands alone without the diff and every heading present carries content. +**Gate.** The lead paragraph stands alone without the diff and every heading present carries content. ## 5. Cold-read the result @@ -111,15 +115,15 @@ Read the composed title and body once as someone with no session context. Name a A bar you did not name is a bar you did not check. Rewrite a failed bar and re-read it. Never ship one as a noted exception. -The gate is that all seven bars have a named result and none is failing. +**Gate.** All seven bars have a named result and none is failing. ## 6. Commit and open it Resolve `$repository` through authenticated `gh repo view ... --json id,nameWithOwner` and record it as the base repository. A valid remote has ordered complete fetch and push sets from `git remote get-url --all <remote>` and `git remote get-url --push --all <remote>` whose credential-free normalized endpoints all resolve through authenticated `gh repo view ... --json id,nameWithOwner` to one common repository ID and `nameWithOwner`. An endpoint whose scheme is not HTTPS or SSH is invalid even when it resolves, since credentials would cross the wire in cleartext. When resolution follows a redirect, the final host, owner, and repository must equal the intended target or the remote is invalid. When push output mentions a redirect, resolve that final URL the same way and require HTTPS with the intended host, owner, and repository before recording the push as landed. Retain only ordered normalized sets or their digests and resolved IDs, never credentials. -When **Existing PR URL** is present, skip commit, push, and create, and load `${CLAUDE_SKILL_DIR}/references/existing-pr-reuse.md` before enumerating remotes. It holds that branch's `gh pr view` read of the existing URL, the head-identity remote match with its multiple- and zero-match dispositions, the freeze, revalidation and reconciliation requirements, what supplies the remote-head evidence without another push, and how the paginated candidate search below and any title or body edit behave on a superseding card. +When **Existing PR URL** is present, skip commit, push, and create, and load `${CLAUDE_SKILL_DIR}/references/existing-pr-reuse.md` before enumerating remotes. It owns that branch's remote selection, revalidation, candidate search, and title or body edits. -Without **Existing PR URL**, inspect the current branch's configured upstream before selecting a remote. A fully configured, resolvable upstream whose branch ref matches the publication branch selects its named remote only when that remote is valid. A malformed, partial, or unresolvable configured upstream is `reconcile-required`. When upstream is authoritatively absent, enumerate valid remotes: auto-select one, use AskUserQuestion with concrete `<remote>: <nameWithOwner> (<id>)` options and pagination when multiple remain, paginating disjoint option sets when needed, and stop with `Configure one valid publication remote or bind the branch upstream, then rerun file-pr.` when none remain. The selected valid remote establishes the head repository identity and `<head-owner>`. It may equal or differ from the base repository. +Without **Existing PR URL**, inspect the current branch's configured upstream before selecting a remote. A fully configured, resolvable upstream whose branch ref matches the publication branch selects its named remote only when that remote is valid. A malformed, partial, or unresolvable configured upstream is `reconcile-required`. When upstream is authoritatively absent, enumerate valid remotes. Auto-select a single one. When several remain, use AskUserQuestion with concrete `<remote>: <nameWithOwner> (<id>)` options, paginating disjoint option sets when needed. Stop with `Configure one valid publication remote or bind the branch upstream, then rerun file-pr.` when none remain. The selected valid remote establishes the head repository identity and `<head-owner>`. It may equal or differ from the base repository. Record the selected remote's ordered complete endpoint sets plus separate base and head identities as guards and invalidators. Re-enumerate immediately before each push and authoritative read-back. Any addition, removal, reorder, resolution failure, or head-repository mismatch is `reconcile-required`. The named-remote push may target its configured complete push set only while that frozen set still matches. @@ -136,7 +140,7 @@ git ls-remote --heads "<push-remote>" "refs/heads/<card-branch>" After every push attempt, including a nonzero or ambiguous command result, recheck the selected-remote identity and run the exact `git ls-remote` read-back once. Record **landed-push SHA** only when that authoritative SHA equals **push-attempt SHA**. An old or unexpected SHA, unavailable read-back, or changed remote identity is `reconcile-required`: stop publication and never retry from the push command result alone. -On an initial publication, recheck the active symbolic ref, branch-ref SHA, and `HEAD` against the frozen attempt. Local movement invalidates the remaining publication even when the remote read-back succeeded. Then load `${CLAUDE_SKILL_DIR}/references/upstream-binding.md`. Reached only on this initial-publication branch, it holds the two `git config` probes that resolve the branch's upstream state, the `ABSENT`, `reconcile-required`, and authoritative-no-op classification, the guarded binding command with its read-back, and the `remote landed / upstream not bound` disposition when binding fails after the push landed. +On an initial publication, recheck the active symbolic ref, branch-ref SHA, and `HEAD` against the frozen attempt. Local movement invalidates the remaining publication even when the remote read-back succeeded. Then load `${CLAUDE_SKILL_DIR}/references/upstream-binding.md` and bind the branch upstream as it directs. Write the final body to a file and record its SHA-256 digest. Before deciding whether an earlier attempt exists, fetch every PR in the base repository through an authoritative paginated REST query: @@ -148,7 +152,7 @@ Require every page to return successfully and parse completely. Any page error, Without **Existing PR URL**, recheck the active symbolic ref, branch-ref SHA, and `HEAD` against **landed-push SHA**, then reconcile every retained candidate across all states. Any `OPEN` candidate with a wrong head SHA or multiple open candidates is `reconcile-required`, and creation stops. One valid open candidate either matches the requested base, state, and draft mode or enters the explicit base-rebind or publication-decision path. Record its URL and return to `done` to bind it before any edit. A `CLOSED` or `MERGED` candidate at a different head SHA is historical and does not block branch reuse. A closed or merged candidate at **landed-push SHA** requires an explicit reopen, new-branch, or no-publication decision. Create only when the complete all-state result contains no open candidate and no closed or merged candidate for the current attempt. Use the same full paginated query and exact local filter when a create attempt returns no usable URL. -Require authoritative repository metadata to show that the selected head is the base repository or belongs to its fork network. An unrelated head blocks creation. Freeze one creation lane before preflight: same-repository heads use `gh pr create --head <branch>`. When the selected head repository is not the base repository, load `${CLAUDE_SKILL_DIR}/references/fork-creation-lanes.md` before freezing the lane. It holds the authenticated-user-owned and organization-owned fork lanes, the stable-ID owner-type and ownership determination that blocks any other relation, and the REST `head_repo` create form. Immediately before creating the PR, invoke `preflight-mutations` with a new inline, single-item card. Its target is the exact base repository and selected head repository. Its action records the frozen creation lane, base, head repository and branch, **landed-push SHA**, title, body path and digest, the attach list with its data approval and each path's SHA-256 digest and byte size when one is frozen, expected `OPEN` state, and expected draft mode. Its guards include both complete endpoint sets and their repository IDs, separate base and head repository IDs, the active symbolic ref, branch-ref SHA and `HEAD` still at **landed-push SHA**, the verified remote branch, recorded base tip, the complete paginated candidate result and exact filter, and confirmed absence of an open candidate or same-head-SHA current attempt. Its read-back is the exact `gh pr view` query below. Apply this result independently under the same result contract. A changed local ref, `HEAD`, endpoint set, base or head identity, creation lane, title, body path, digest, attach list, approval, any attached file's digest or size, state, or draft mode invalidates the card. A non-ready PR card does not erase the landed push evidence. +Require authoritative repository metadata to show that the selected head is the base repository or belongs to its fork network. An unrelated head blocks creation. Freeze one creation lane before preflight: same-repository heads use `gh pr create --head <branch>`. When the selected head repository is not the base repository, load `${CLAUDE_SKILL_DIR}/references/fork-creation-lanes.md` before freezing the lane. It holds both fork lanes and the ownership check that blocks any other relation. Immediately before creating the PR, invoke `preflight-mutations` with a new inline, single-item card. Its target is the exact base repository and selected head repository. Its action records the frozen creation lane, base, head repository and branch, **landed-push SHA**, title, body path and digest, the attach list with its data approval and each path's SHA-256 digest and byte size when one is frozen, expected `OPEN` state, and expected draft mode. Its guards include both complete endpoint sets and their repository IDs, separate base and head repository IDs, the active symbolic ref, branch-ref SHA and `HEAD` still at **landed-push SHA**, the verified remote branch, recorded base tip, the complete paginated candidate result and exact filter, and confirmed absence of an open candidate or same-head-SHA current attempt. Its read-back is the exact `gh pr view` query below. Apply this result independently under the same result contract. A changed local ref, `HEAD`, endpoint set, base or head identity, creation lane, title, body path, digest, attach list, approval, any attached file's digest or size, state, or draft mode invalidates the card. A non-ready PR card does not erase the landed push evidence. Append `--draft` to the create command exactly when the bound draft mode is `isDraft: true`. @@ -160,7 +164,7 @@ gh pr create --repo "$repository" --base "$base" --head "$head_branch" --title " `--attach` uploads each file to GitHub, WebM included, at most 50 per command. It replaces every body reference that resolves to the same absolute path as an argument with that file's URL, so run it from the repository root, where the fragment's paths are relative. Video cap is 10 MB on Free plans, 100 MB on paid, and the plan is often not readable, so use 10 MB unless the user confirmed a paid plan. Immediately before creating with `--attach`, recompute every attach-list SHA-256 and byte size, require each to match the frozen card values, and require each video to be within the cap. An over-cap video returns to `done`. -Read back every lane identically with the ten-field query below. This list is the single home; `references/existing-pr-reuse.md` and `references/fork-creation-lanes.md` cite it, never restate it: +Read back every lane identically with this ten-field query: ```bash gh pr view <pr-url> --repo "$repository" --json url,title,body,baseRefName,baseRefOid,headRefName,headRefOid,headRepository,state,isDraft @@ -168,7 +172,13 @@ gh pr view <pr-url> --repo "$repository" --json url,title,body,baseRefName,baseR Execute only the frozen lane. The other examples are not fallbacks after an ambiguous result. -Require the PR `title` to equal the frozen value. When nothing was attached, require `body` to equal the frozen body exactly. When files were attached, require the frozen body with each attach-list reference rewritten to a `https://github.com/user-attachments/assets/` URL and no other change: an image keeps its `![alt](...)` with only the path swapped, and a video embed alone in its paragraph becomes the bare URL. Preserve those URLs through any reconciliation instead of editing the body back to the frozen bytes. When an upload fails gh still creates the PR and exits nonzero. The paths left to upload are exactly the attach-list paths whose local embed still appears in the read-back body. Upload each through a preflighted `gh pr edit <pr-url> --attach <path>`, once per path, read the body back the same way, and never issue another create. A path that still fails is `reconcile-required`. Require `headRepository.id` to equal the selected head repository ID, `headRefName` to equal **Branch**, `headRefOid` to equal **landed-push SHA**, and `baseRefOid` to equal **Remote base-tip commit**. Require its URL, base name, `state`, and `isDraft` to match the publication request. Recheck the active symbolic ref, branch-ref SHA, and `HEAD` against **landed-push SHA** before accepting the publication. Record the created URL as `landed` as soon as one PR is authoritatively identified. When only title or body differs while repository, base, head, state, and draft mode still match, preflight an exact edit of that existing PR and read it back. A base mismatch enters `done`'s existing-PR rebind path with that URL and its observed values. Never issue another create. A state or draft mismatch stops for an explicit publication decision. Never silently reopen, close, convert, or create another PR. Record both single-step cards, the landed push, the PR observations, the ordered commit list, and the exact commands as publication evidence for the next `done` run. When authoritative search cannot identify whether a PR was created, mark the create `reconcile-required` and do not retry. +Require the PR `title` to equal the frozen value. When nothing was attached, require `body` to equal the frozen body exactly. When files were attached, require the frozen body with each attach-list reference rewritten to a `https://github.com/user-attachments/assets/` URL and no other change: an image keeps its `![alt](...)` with only the path swapped, and a video embed alone in its paragraph becomes the bare URL. Preserve those URLs through any reconciliation instead of editing the body back to the frozen bytes. + +When an upload fails gh still creates the PR and exits nonzero. The paths left to upload are exactly the attach-list paths whose local embed still appears in the read-back body. Upload each through a preflighted `gh pr edit <pr-url> --attach <path>`, once per path, read the body back the same way, and never issue another create. A path that still fails is `reconcile-required`. + +Require `headRepository.id` to equal the selected head repository ID, `headRefName` to equal **Branch**, `headRefOid` to equal **landed-push SHA**, and `baseRefOid` to equal **Remote base-tip commit**. Require its URL, base name, `state`, and `isDraft` to match the publication request. Recheck the active symbolic ref, branch-ref SHA, and `HEAD` against **landed-push SHA** before accepting the publication. Record the created URL as `landed` as soon as one PR is authoritatively identified. + +When only title or body differs while repository, base, head, state, and draft mode still match, preflight an exact edit of that existing PR and read it back. A base mismatch enters `done`'s existing-PR rebind path with that URL and its observed values. Never issue another create. A state or draft mismatch stops for an explicit publication decision. Never silently reopen, close, convert, or create another PR. Record both single-step cards, the landed push, the PR observations, the ordered commit list, and the exact commands as publication evidence for the next `done` run. When authoritative search cannot identify whether a PR was created, mark the create `reconcile-required` and do not retry. Print the URL and return to `done` for CI, review, publication-lane, and final row evaluation. Never merge. Opening the PR is a publication transition, not overall task completion. diff --git a/skills/fix-pr-review/SKILL.md b/skills/fix-pr-review/SKILL.md index da77fd3..247c2b4 100644 --- a/skills/fix-pr-review/SKILL.md +++ b/skills/fix-pr-review/SKILL.md @@ -1,13 +1,13 @@ --- name: fix-pr-review -description: Triage and fix review findings that already exist on a GitHub PR, then reply and resolve the threads. Use on a CodeRabbit review URL, a PR whose review comments still need working through, or a local /review-pr findings file. When the PR has no findings yet, /review-pr produces them first. +description: Triage, fix, and resolve review findings. Use on a CodeRabbit or any reviewer's URL (#pullrequestreview-/#discussion_r), "check <review URL>", "check/fix review comments", "check ai comments", a PR or PR-watch update whose review comments still need working through, or a local /review-pr findings file. When the PR has no findings yet, use /review-pr first. --- # /fix-pr-review: Triage, Fix, and Resolve PR Review Comments Takes a PR review from CodeRabbit, `/review-pr`, or pasted text. It triages each finding, applies validated fixes, runs `/done`, then replies to and resolves the GitHub conversations in one flow. -**Use AskUserQuestion only when the run still needs a user decision**: branch safety, stash confirmation, contested-item confirmation, `--interactive` per-fix confirmations, type-check failure triage, and post-completion next actions. Invoking `/fix-pr-review` or imperatively asking to fix review findings authorizes execution of the validated FIX plan. Do not ask for separate plan approval. Mark every required option as concrete and considered, strongest first with "(Recommended)". +Use AskUserQuestion only for these user decisions: branch safety, stash confirmation, contested-item confirmation, `--interactive` per-fix confirmations, type-check failure triage, and post-completion next actions. Invoking `/fix-pr-review` or imperatively asking to fix review findings authorizes execution of the validated FIX plan. Do not ask for separate plan approval. An unattended PR-watch run asks none of these (Phase 1). Write every option as a concrete choice, strongest first, marked "(Recommended)". ## Quick Reference @@ -40,17 +40,7 @@ Takes a PR review from CodeRabbit, `/review-pr`, or pasted text. It triages each | R8 | DISAGREE: legitimate technical disagreement | `disagree_rationale` | | R9 | NEEDS-INPUT: ambiguous/needs user knowledge | `why_unclear` | -*R4/R5 sit above R3 by design, not by accident, and R10 sits above R6 so a technically correct finding never reaches the fix loop on the strength of being correct. The full rubric and the reason for that order live in `references/triage-rubric.md`.* - -### Key Cross-References - -- **R-rubric definition**: `references/triage-rubric.md`, loaded by the triage subagent at Phase 3 STEP 4, not by main -- **Plan validation rules**: Phase 4 (validates fields required by R-rubric) -- **Fix execution routing**: Phase 5 (executes R6 FIX items) -- **Convergence**: Phase 5.5 (consumes `class_completeness` + `inverse_risk` from the plan; feeds `fix_status` and the per-fix `convergence:` report line) -- **Reply validator**: `references/github-reply-resolve.md` Step 7b (format rules for GitHub replies) -- **Report grouping**: Phase 8 (groups by R-classification) -- **Suppressions**: Phase 3 "Load review suppressions" (main agent reads, before dispatch) + `references/triage-prompt.md` (subagent applies them before classifying) + Phase 8 (write) +R4 and R5 rank above R3 on purpose, and R10 ranks above R6 so a technically correct finding never reaches the fix loop on correctness alone. `references/triage-rubric.md` holds the full rubric and the reason for that order. ### Reference files @@ -64,6 +54,7 @@ Each carries its firing condition in the pointer at the point of use. Load it th - `references/branch-safety.md`: repo plus branch landing with detached-HEAD and different-branch questions. Loaded by main in Phase 1 for GitHub inputs; skipped for local files. - `references/per-fix-loop.md`: per-fix loop, interactive confirmations, narrow type-check plus baseline compare, retry/skip/abort. Loaded by main at the Phase 5 loop. - `references/needs-input-triage.md`: NEEDS-INPUT settle, per-item triage, Phase 8 fix plus verify, preflight before GitHub replies. Loaded by main in Phase 8 step 1, only when the count is nonzero. +- `references/push-publication.md`: publication target, remote endpoint validation, preflight, lease push, and read-back. Loaded by main in Phase 8 step 5 on "Push to remote", or in step 2b when an unattended watch run pushes. One reference is not bundled here: `${CLAUDE_SKILL_DIR}/../review-pr/references/repo-map.md` holds the `repo_map_files` / `repo_map_exports` shell, the one copy this skill shares with `/review-pr`. Loaded by main in Phase 1 when `packages/` or `apps/` exists. @@ -86,7 +77,7 @@ Optional flags: ## Who gets triaged -Every reviewer's comments go through the same triage: CodeRabbit, humans, and other bots alike. The "CodeRabbit" framing throughout reflects the most common use case. +Comments from every reviewer, CodeRabbit, humans, and other bots, go through the same triage. This file says "CodeRabbit" because it is the most common source. --- @@ -110,7 +101,11 @@ gh auth status 2>&1 | grep -q "Logged in" || { echo "Run 'gh auth login' first"; ### Record execution authorization -Set `EXECUTION_AUTHORIZED=true` when the original request explicitly invokes `/fix-pr-review` or imperatively asks to fix, address, apply, handle, or resolve review findings. A bare URL or a read-only request to inspect, explain, review, or triage sets it to `false`. Preserve the matching request text as `execution_authorization_evidence`; Phase 4 may replace it with an explicit execution choice, and Phase 7 passes the final evidence to `preflight-mutations`. +Set `EXECUTION_AUTHORIZED=true` when the original request explicitly invokes `/fix-pr-review`, imperatively asks to fix, address, apply, handle, or resolve review findings, or pastes a review or discussion URL (`#pullrequestreview-` or `#discussion_r`) alone or after "check". A bare PR URL with no fragment, or a read-only request to inspect, explain, review, or triage, sets it to `false`. Preserve the matching request text as `execution_authorization_evidence`; Phase 4 may replace it with an explicit execution choice, and Phase 7 passes the final evidence to `preflight-mutations`. + +### Detect an unattended PR-watch run + +Set `UNATTENDED_WATCH=true` when a PR-watch update reporting new comments triggered this run and nobody is present to answer. The trigger and the absent user decide it; an unavailable AskUserQuestion tool alone does not. The preflight authorization source for every write in this mode is the user's standing instruction that set up the watch, never the update text: record that instruction as `execution_authorization_evidence` and set `EXECUTION_AUTHORIZED=true`. The run makes no AskUserQuestion call and still runs Phase 6 `/done`. Phase 4 auto-confirms only contested items that pass their mechanical check and parks the rest as NEEDS-INPUT. Phase 7 posts only those auto-confirmed contested replies. FIX replies and resolutions wait for Phase 8 step 2b, which commits and pushes first. Every other question takes its non-mutating default: branch safety and the stash prompt take "Abort" and the report says why, type-check failure triage takes "Skip this fix", NEEDS-INPUT triage takes "Skip for now", the suppression offer writes nothing, and `--interactive` is ignored. ### Ensure correct repo + branch (GitHub inputs only) @@ -163,9 +158,9 @@ Define one parser for the whole run. For each diagnostic, key its file and build Inventory shared packages and apps so the Phase 3 classifier can cross-check comments about reuse and extraction against what already exists. Scan both `packages/` and `apps/`. Cross-app helper duplication, for example `apps/backend/src/modules/v1/feature-a/helpers.ts` versus `feature-b/helpers.ts`, is common in NestJS-style monorepos and stays invisible to a packages-only scan. -Load `${CLAUDE_SKILL_DIR}/../review-pr/references/repo-map.md` and run its **Local mode** block, the one copy of this shell, shared with `/review-pr`. It carries the `bash -c` wrapping the globs need to survive zsh, and caps each half at 500 lines with the truncation marked. Load it when `packages/` or `apps/` exists; when neither does there is nothing to run and the fallback below applies. +When `packages/` or `apps/` exists, load `${CLAUDE_SKILL_DIR}/../review-pr/references/repo-map.md` and run its Local mode block unchanged; its `bash -c` wrapper keeps the globs working under zsh. -Stash both outputs as `repo_map_files` and `repo_map_exports` for the Phase 3 subagent prompt. If neither `packages/` nor `apps/` exists (non-monorepo), set both to `N/A (not a monorepo)` and flag `IS_MONOREPO=false`. The classifier prompt uses this to reroute greps to `src/` and the repo root. +Stash both outputs as `repo_map_files` and `repo_map_exports` for the Phase 3 subagent prompt. If neither `packages/` nor `apps/` exists (non-monorepo), set both to `N/A (not a monorepo)` and flag `IS_MONOREPO=false`. The classifier prompt uses this to reroute greps to the changed files' directories, then the repo root. Never assume `src/`. --- @@ -173,9 +168,7 @@ Stash both outputs as `repo_map_files` and `repo_map_exports` for the Phase 3 su ### Dual-path input for /review-pr findings -`/review-pr` now posts findings as **individual inline comments**, one per finding on a specific code line. These create standard `PullRequestReviewThread`s on GitHub, identical to CodeRabbit threads. The existing GraphQL fetch below handles them with zero special parsing. - -Manually exported or legacy `/review-pr` findings files use the existing local-file path. Phase 7 skips GitHub operations for those inputs. +`/review-pr` posts each finding as its own review thread, the same `PullRequestReviewThread` shape CodeRabbit uses, so the GraphQL fetch below reads them unchanged. Exported or legacy `/review-pr` findings files take the local-file path, and Phase 7 skips GitHub for them. ### Fetch from GitHub (PR URL / review URL / discussion URL) @@ -185,11 +178,11 @@ A fetch that errors surfaces the error and exits, so triage never runs on a part ### For local files (`./review.md`, `/tmp/review-pr-*-findings.md`, etc.) -Parse the `/review-pr` output format. Extract findings from the `## Findings` section, preserving `Severity / File / Category / Issue / Why it matters / Suggested fix` **plus `Rule-class` / `Enclosing-symbol` / `Inverse risk` / `Class-sites`** when present. `/review-pr` emits these per finding and dropping them forces this skill to re-derive work the reviewer already did. +Parse the `/review-pr` output format. Extract findings from the `## Findings` section, preserving `Severity / File / Category / Issue / Why it matters / Suggested fix`, plus `Rule-class` / `Enclosing-symbol` / `Inverse risk` / `Class-sites` when present. `/review-pr` emits these per finding and dropping them forces this skill to re-derive work the reviewer already did. -**Seed, don't re-derive**: when `Inverse risk:` is present, seed STEP 5's `inverse_risk` from it and VERIFY it against the code (confirm the named failure mode is real and still applies) rather than deriving a new one from scratch. When `Class-sites: <A>/<N>` is present, seed STEP 1.5's `class_completeness` with those `N` sites and verify the count against your own search. Re-run the search only to catch sites the reviewer missed, not to rebuild the list. `Rule-class` and `Enclosing-symbol` seed the class sweep's `signature`. If a seeded value contradicts what you read in the code, the code wins. Record the discrepancy in the field. +**Seed, don't re-derive.** When `Inverse risk:` is present, seed STEP 5's `inverse_risk` from it and VERIFY it against the code (confirm the named failure mode is real and still applies) rather than deriving a new one from scratch. When `Class-sites: <A>/<N>` is present, seed STEP 1.5's `class_completeness` with those `N` sites and verify the count against your own search. Re-run the search only to catch sites the reviewer missed, not to rebuild the list. `Rule-class` and `Enclosing-symbol` seed the class sweep's `signature`. If a seeded value contradicts what you read in the code, the code wins. Record the discrepancy in the field. -**Severity mapping**: `/review-pr` uses `Critical | Serious | Moderate | Minor` while CodeRabbit uses `Critical | Major | Minor | Refactor | Nitpick`. Both are valid. Normalize to the internal `Comment` schema which accepts either convention. Map for triage priority: `Critical` = highest, `Serious`/`Major` = high, `Moderate` = medium, `Minor`/`Refactor` = low, `Nitpick` = default-dismiss. +**Severity mapping.** `/review-pr` uses `Critical | Serious | Moderate | Minor` while CodeRabbit uses `Critical | Major | Minor | Refactor | Nitpick`. Both are valid. Normalize to the internal `Comment` schema which accepts either convention. Map for triage priority: `Critical` = highest, `Serious`/`Major` = high, `Moderate` = medium, `Minor`/`Refactor` = low, `Nitpick` = default-dismiss. ### Normalize to internal `Comment` list @@ -241,7 +234,7 @@ Pass loaded suppressions into the subagent prompt as a `## Review suppressions` Dispatch **one** `general-purpose` subagent with `Read`, `Grep`, and `Bash` tools. The triage plan comes from this subagent alone. If it fails outright, abort the run and say so. Classifying inline skips the grounding and class-sweep passes the whole plan is built on. -**Important**: The Bash allowlist (`git log/diff/blame/show/merge-base/rev-parse`, `grep`, `rg`) is a **prompt-level instruction**. Claude Code's Agent tool doesn't sandbox Bash per-command. The subagent is trusted not to run other commands, not mechanically prevented from doing so. +The Bash allowlist (`git log/diff/blame/show/merge-base/rev-parse`, `grep`, `rg`) lives only in the prompt. The Agent tool does not sandbox Bash per command, so nothing mechanically stops the subagent from running other commands. ### Prompt template @@ -253,8 +246,6 @@ Its STEP 4 sends the subagent to `references/triage-rubric.md` on its own. You d ## Phase 4: Plan execution gate (main) -*Validates against the R-rubric: summary table above, full detail in `references/triage-rubric.md`. Required fields per classification are defined there.* - ### Plan validation (before display) Before anything is shown to the user, mechanically validate the classifier's output: @@ -293,14 +284,24 @@ If any DISMISS has `rubric: R5`, print a **separate highlighted section BEFORE** CLAUDE.md rule: "<verbatim quote>" Reply will be: "<reply>" If this rule has a legitimate exception in this case, select D<n> - in the contested-item confirmation to change it to FIX or NEEDS-INPUT. + in the contested-item confirmation when it is asked, to change it to + FIX or NEEDS-INPUT; otherwise reply on the thread or rerun with --interactive. ``` ### Contested-item confirmation (multiSelect) Contested items are the ones that will post a reply and resolve a thread WITHOUT any code change: every DISMISS, DEFER, and DISAGREE. A wrong classification here silently closes a reviewer's conversation. Confirm the triage before Phase 5/7 can act on it. -Skip this step if there are zero contested items. Otherwise, use AskUserQuestion: +Skip this step if there are zero contested items. + +**Evidence auto-confirm.** Four rules are mechanically decidable. Main re-checks each in this run: + +- R5: the `claude_md_quote` appears verbatim in CLAUDE.md, which main re-read. +- R2: a re-anchor search for the cited symbol finds nothing. +- R4: the diff of `prior_commit_sha` removes the cited pattern. +- R7: the tracking issue is open and references the finding. + +R1, R3, and R8 items always go to the question below. Record each check as an auto-confirm record: rule, re-checked field, and result. When `EXECUTION_AUTHORIZED=true`, `--interactive` is not set, and every contested item passes its check, skip both questions below. Those items pass this confirmation, Phase 7 posts their replies under their records without asking, and the final report lists each one. When `UNATTENDED_WATCH=true`, items that pass their check pass the same way. Every other contested item, R1, R3, and R8 included, moves to NEEDS-INPUT with `why_unclear: "unattended run, <classification> not mechanically confirmed"` and its `reusability_context` unchanged, so its thread stays open. Otherwise, use AskUserQuestion: Question: header: "Triage" @@ -325,7 +326,7 @@ For each selected item, use a follow-up AskUserQuestion: On "FIX": re-dispatch the classifier scoped to just this item to produce the full FIX field set: `fix_plan`, `change_class`, `test_scenario`, `inverse_risk`, `class_completeness` (class sweep included; a reclassified item has never been swept), and `reusability_context` (carry the contested item's own value through unless the sweep changed it). Then re-run plan validation on the changed item. Producing a partial field set here fails validation and burns the single retry. On "NEEDS-INPUT": move to NEEDS-INPUT with `why_unclear: "user contested the <classification> classification"`, carrying the item's `reusability_context` through unchanged. On "Keep as-is": no change. On "Other": treat the freeform text as the reclassification instruction. -Nothing is posted or resolved during this step. Phase 7 remains the only place GitHub is touched, and it acts **only** on items that survived this confirmation. Resolving a thread is irreversible noise in the reviewer's conversation: a DISMISS, DEFER, or DISAGREE that skipped this gate stays open and unanswered until it has been through it. +This step posts and resolves nothing. Only Phase 7 touches GitHub, and it acts **only** on items that passed this confirmation, asked or evidence-confirmed. A DISMISS, DEFER, or DISAGREE that has not passed this gate stays open and unanswered. ### Execution @@ -350,8 +351,6 @@ On "Execute plan": set `EXECUTION_AUTHORIZED=true`, record the choice as `execut ## Phase 5: Execute fixes (main, sequential) -*Executes R6 FIX items from the validated plan (see the R-Rubric Summary table for R6 criteria and Phase 4 for validation).* - ### Dependency resolution Build an execution order from `dependencies:` fields with a simple topological sort. On a **cycle** (A→B→A): abort with `Cyclic fix dependencies detected. Correct the dependency fields and rerun /fix-pr-review <original-input>.` Restore the stash under the guard. Exit non-zero. @@ -442,8 +441,8 @@ evidence across the complete component ledger rather than its ordinary default. Handling: - `class_complete: no` → apply the missing sites now, then re-verify ONCE. If the second pass still reports `no`, stop: mark `fix_status[idx] = partial`, record the still-unfixed - sites, and surface them in Phase 8. Do not loop a third time; like the narrow - type-check retry (max 2) and the self-heal loop (max 2), this loop is capped. + sites, and surface them in Phase 8. Do not loop a third time. This cap matches + the narrow type-check retry (max 2) and the self-heal loop (max 2). For a Phase 8 remediation, correct only the missing removal or replacement evidence; do not apply an original affected site merely because it is absent from the diff. - Treat each corrective class-completeness or new-sibling edit as part of the fix it completes. @@ -485,11 +484,10 @@ Do not take `/done` §4's handoffs. This skill owns publication: `git-commit` an ``` self_heal_iter = 0 -done_remaining = [] -while self_heal_iter < 2: +while True: findings = run_parallel_review(diff="git diff HEAD") blockers = [f for f in findings if f.severity in ("Critical", "Serious")] - if not blockers: + if not blockers or self_heal_iter == 2: break # Dispatch fresh subagent per blocker with the finding + the diff for f in blockers: @@ -497,13 +495,13 @@ while self_heal_iter < 2: run_fix_ts_errors() self_heal_iter += 1 -# After loop: record whatever blockers remain (if any) done_remaining = blockers +done_moderate_minor = [f for f in findings if f.severity in ("Moderate", "Minor")] ``` -If `done_remaining` is non-empty after 2 iterations, record it for the final report and continue to Phase 7. The user sees the remaining issues in Phase 8 and decides there. +The loop makes at most two edit rounds and always reviews the final state, so `done_remaining` holds the Critical and Serious findings of that last review. If it is non-empty, record it for the final report and continue to Phase 7. The user sees the remaining issues in Phase 8 and decides there. -**Moderate/Minor findings** are recorded in `done_remaining` without self-heal. User decides at commit time. +That last review's Moderate and Minor findings go to `done_moderate_minor` without self-heal, and the final report lists them. User decides at commit time. When every required lane is verified, materialize the exact fix content as the `done_verified_snapshot` required by `git-commit`'s Verified content snapshot contract. Build it with `/done` §4's path-scoped snapshot procedure (`create_verified_snapshot`): read `HEAD` into an isolated `GIT_INDEX_FILE`, hash only the declared paths with `git hash-object -w`, stage each with `git update-index --add --cacheinfo <mode>,<blob>,<path>`, `--force-remove` each declared deletion, then `git write-tree`. The declared-path set here is the union of every landed fix's declared paths; the auto-stashed WIP and any other dirty path stays out of it and out of the object database. Record the snapshot tree and included path manifest while the run-level stash is still untouched. A Phase 8 fix may update only its declared paths in this snapshot after that item's verification lands cleanly; rebuild the tree by reading the prior snapshot instead of `HEAD` into the isolated index and applying only those verified bytes through the same `update-index --cacheinfo` mechanism, never by recapturing the live worktree. If no valid `done_verified_snapshot` exists, Commit and Push are unavailable. @@ -511,10 +509,12 @@ When every required lane is verified, materialize the exact fix content as the ` ## Phase 7: Reply + resolve on GitHub (main) -*Reply format rules live in `references/github-reply-resolve.md` and are referenced by Phase 4 validation. This skill does NOT read or write `/review-pr`'s cache (`~/.claude/skills/review-pr/cache/`). Thread resolution happens on GitHub. `/review-pr`'s re-review picks up resolved threads via its GraphQL prior-review timeline fetch.* +This skill never reads or writes `/review-pr`'s cache (`~/.claude/skills/review-pr/cache/`). It resolves threads on GitHub, and `/review-pr`'s next run sees them through its GraphQL prior-review timeline fetch. Replying and resolving threads is this phase's entire GitHub footprint. The review's own `CHANGES_REQUESTED` state stays exactly as CodeRabbit left it. CodeRabbit clears it itself on its next auto-re-review, once the user pushes. +When `UNATTENDED_WATCH=true`, post and resolve only the auto-confirmed contested items here. Record every FIX item's reply and resolution as `skipped` with the reason `held until push`; Phase 8 step 2b posts them only after the push lands. + Define `has_github_surface[idx] = thread_id != null AND (can_reply OR can_resolve)`. Initialize every item without that surface to paired `not-applicable` states, regardless of input source. For `./review.md` or any other local-file input, all items meet that condition; initialize them, then skip the rest of this phase. Phase 8 still requires deterministic per-item state for its final report. ### Posting mechanics @@ -538,8 +538,6 @@ Derive `reply_ok=true` from `reply_state ∈ {landed, verified-existing, not-app ## Phase 8: Finalize (main) -*Report groups by R-classification (see the R-Rubric Summary table). Includes suppressions write (learning loop).* - ### 1. Settle NEEDS-INPUT Keep the run-level stash untouched throughout this step. Build `needs_input_items` from current workflow state: every item still classified `NEEDS-INPUT` and every item Phase 5 or 5.5 routed there. Do not derive this count from a rendered report. Triage every NEEDS-INPUT item per `${CLAUDE_SKILL_DIR}/references/needs-input-triage.md`, loaded only when the count is nonzero. Leave this step only when no entry is still pending. @@ -565,13 +563,17 @@ fi On `STASH_TOP_MISMATCH`, record `stash_restored: foreign-top` and continue to the final report: another stash now sits on top of ours, so nothing was applied and nothing was dropped. On `STASH_CONFLICT`, leave every conflict marker exactly as the apply left it and skip the drop: the entry stays stashed as the recovery source. Record `stash_restored: conflict` for the final report; resolving the user's WIP is the user's call. On `STASH_APPLY_FAILED`, the apply errored without conflict markers, so the worktree state is unknown: record `stash_restored: failed`, leave the worktree and every entry untouched, and continue to the report. When the drop reports `STASH_CHANGED_MID_RESTORE`, the content applied cleanly but the stack moved first: record `stash_restored: foreign-top`, leave the recorded entry for the user to drop by hand, and continue to the report. After a `conflict` or `failed` restoration, no edit, type-check, convergence check, Phase 6 check, commit, or push may run. Expose stash recovery as the only dependency-ready next action. +### 2b. Publish an unattended watch run + +Runs only when `UNATTENDED_WATCH=true`, before the report. First compute `inverse_risk_blockers` exactly as step 5 defines it. Commit and push only when Phase 6 verified every required lane, `done_remaining` is empty, and step 5's Commit and Push requirements hold. Commit through `git-commit` in sealed-index mode as step 5 describes, then push per `${CLAUDE_SKILL_DIR}/references/push-publication.md` under `preflight-mutations`, with the watch's standing instruction as the authorization source. A question that reference would ask counts as a blocked push. Only after the push read-back records it landed, post the held FIX replies and resolve their threads through Phase 7's posting mechanics. When `/done` is not clean or the push is blocked, post nothing for FIX items and report each as `fixed locally, not pushed`. + ### 3. Print the final report once Recompute every count from the final classification, `fix_status`, `convergence`, `needs_input_status`, and `gh_status` state. Load `${CLAUDE_SKILL_DIR}/references/final-report.md` now and render it exactly once. Render `inverse_risk_applied` under **Skipped / not landed clean**, count it as not converged, and state `risky fix remains applied`. Its dependency-ready action is `Remove the risky owned components or apply a replacement, then rerun Phase 5.5 and Phase 6 to rebuild done_verified_snapshot.` Interpret any legacy no-safe-revert rule for `reverted_inverse_risk` as `inverse_risk_applied`; the reverted status always means the fix was removed. The report's failure-first ordering includes every Phase 7 and Phase 8 GitHub outcome; its rendering rules decide which fix subsection each `[F<n>]` occupies and how the `Test:` line is emitted. ### 4. Offer to write suppressions (learning loop) -After the final report, collect all current DISMISS and DISAGREE items. If any exist, offer to persist them as suppressions for future reviews. +After the final report, collect all current DISMISS and DISAGREE items. If any exist, offer to persist them as suppressions for future reviews. When `UNATTENDED_WATCH=true`, skip the offer and write nothing. Use AskUserQuestion: @@ -606,7 +608,7 @@ Skip this step entirely if: ### 5. Post-completion next actions -After printing the final report and completing the suppression step when applicable, compute `inverse_risk_blockers` from every item whose current `fix_status` is `inverse_risk_applied`, independent of final classification or `needs_input_status`. When `stash_restored=conflict`, suppress the prompt and report `Resolve stash conflicts` as the sole dependency-ready next action; Commit and Push remain unavailable. When `stash_restored=foreign-top`, suppress the prompt and report `Reconcile the stash stack: the recorded auto-stash entry is still stashed` as the sole dependency-ready next action; Commit and Push remain unavailable. When `stash_restored=failed`, suppress the prompt and report `Recover the worktree and stash by hand` as the sole dependency-ready next action; Commit and Push remain unavailable. Otherwise, if `inverse_risk_blockers` is non-empty, suppress the prompt and report the dependency-ready removal/replacement action from Step 3; Commit and Push remain unavailable even when `done_verified_snapshot` exists. Skip the prompt if all fixes were aborted (nothing was applied); otherwise use AskUserQuestion. +After printing the final report and completing the suppression step when applicable, compute `inverse_risk_blockers` from every item whose current `fix_status` is `inverse_risk_applied`, independent of final classification or `needs_input_status`. When `stash_restored=conflict`, suppress the prompt and report `Resolve stash conflicts` as the sole dependency-ready next action; Commit and Push remain unavailable. When `stash_restored=foreign-top`, suppress the prompt and report `Reconcile the stash stack: the recorded auto-stash entry is still stashed` as the sole dependency-ready next action; Commit and Push remain unavailable. When `stash_restored=failed`, suppress the prompt and report `Recover the worktree and stash by hand` as the sole dependency-ready next action; Commit and Push remain unavailable. Otherwise, if `inverse_risk_blockers` is non-empty, suppress the prompt and report the dependency-ready removal/replacement action from Step 3; Commit and Push remain unavailable even when `done_verified_snapshot` exists. Skip the prompt if all fixes were aborted (nothing was applied) or `UNATTENDED_WATCH=true`; otherwise use AskUserQuestion. Question: header: "Next" @@ -623,23 +625,10 @@ After printing the final report and completing the suppression step when applica On "Commit changes" or "Push to remote", require `inverse_risk_blockers` to be empty, `stash_restored` to be none of `conflict`, `foreign-top`, or `failed`, and a valid `done_verified_snapshot`. Recompute the blocker set instead of trusting the rendered report; a non-empty set stops the action. Invoke `git-commit` in Verified content snapshot sealed-index mode with that snapshot; never stage or restage the live worktree. Require the created commit tree to equal the snapshot tree. Ordinary restored WIP remains in the worktree and outside the candidate commit. -On "Push to remote", commit first, then require the commit tree to still equal the snapshot before freezing the publication branch and push-attempt SHA. For GitHub PR input, re-read the current PR's `headRefName` plus `headRepository.id` and `nameWithOwner`; freeze those authoritative values, require the Phase 1 expected PR branch and active branch to equal the frozen head name, and set `<exact-ref>` to `refs/heads/<frozen-headRefName>`. That head repository identity is the intended publication target and remains independent of the input/base repository. For local-file or other no-current-PR input, freeze the active branch and derive `<exact-ref>` as `refs/heads/<active-branch>`. Freeze the push-attempt SHA only when the active symbolic ref equals `<exact-ref>` and that branch ref and `HEAD` resolve to the same SHA. Resolve the intended publication target before binding `<preflighted-remote>`. For GitHub PR input, enumerate configured remotes and validate every endpoint in each ordered complete fetch/push set. Auto-select the only remote whose every endpoint matches the PR head identity; when multiple match, immediately use AskUserQuestion with concrete `<remote>: <nameWithOwner> (<id>)` options and pagination when needed; when none match, stop with `Configure a remote whose complete endpoint sets resolve to the current PR head repository, then choose Push to remote again.` The selected match becomes `<preflighted-remote>`. - -For local-file or other no-current-PR input, immediately follow the "Push to remote" choice with AskUserQuestion. Inventory every configured remote; capture without printing its ordered complete fetch and push endpoint sets, normalize and resolve every endpoint independently through authenticated `gh repo view ... --json id,nameWithOwner`, and retain a remote only when every endpoint resolves to the same repository ID and `nameWithOwner`. Show each retained remote as a concrete option labelled `<remote>: <nameWithOwner> (<id>)`; list rejected remotes with the failing endpoint or set mismatch but never make them selectable. If the option limit cannot hold every retained remote, paginate disjoint sets of concrete options with a `Show next remotes` choice until the user selects one. That selection freezes both complete sets, sets `<preflighted-remote>` and the explicit intended publication target, and joins the preflight authorization and guards. If no remote is valid, stop with: `Configure a remote whose complete fetch and push endpoint sets resolve to one authenticated GitHub repository, then choose Push to remote again.` - -Capture without printing the ordered complete sets from `git remote get-url --all <preflighted-remote>` and `git remote get-url --push --all <preflighted-remote>`, then normalize every endpoint to a credential-free URL identity. Preserve transport class, host, port, and owner/repository while treating equivalent spellings such as scp-like versus `ssh://`, default ports, trailing slash, and `.git` suffix alike. Independently resolve every normalized endpoint through authenticated `gh repo view ... --json id,nameWithOwner`; require every endpoint ID and `nameWithOwner` to equal the intended publication target. Retain only the ordered normalized sets or their digests and resolved IDs, never credentials. Record the remote name, both complete endpoint sets, and intended target; any addition, removal, reorder, resolution failure, or repository mismatch is `reconcile-required`. The named-remote push may target its configured complete push set only while that frozen set still matches. - -Query `<exact-ref>` and record its remote SHA or authoritative absence; when present, require it to be an ancestor of the frozen SHA. Immediately before `git push`, invoke `preflight-mutations` with the exact remote, both ordered complete endpoint sets and repository IDs, intended publication target, `<exact-ref>`, frozen local SHA, expected remote SHA or absence, upstream state, commit range, dependent refs, and the user's choice. Include the PR base, frozen `headRefName`, `<exact-ref>`, and head repository ID in its guards and invalidators when a PR exists. Include the active symbolic ref, branch-ref SHA, `HEAD`, frozen publication branch and exact ref, and complete selected-remote identity in those guards and invalidators, then re-enumerate and compare both endpoint sets before running the exact push: - -```bash -git push --force-with-lease="<exact-ref>:<expected-sha-or-empty>" "<preflighted-remote>" "<frozen-local-sha>:<exact-ref>" -git ls-remote --heads "<preflighted-remote>" "<exact-ref>" -``` - -After every attempted push, including a nonzero or ambiguous exit, establish remote state first: re-enumerate both complete endpoint sets and run the exact `git ls-remote` read-back for `<exact-ref>` once. Record the push as landed only when the authoritative remote SHA equals the frozen SHA, the ordered sets are unchanged, and every endpoint still equals the intended publication target. An old or unexpected SHA, unavailable read-back, added, removed, reordered, unresolved, or mismatched endpoint is `reconcile-required`; stop and never retry from the command result alone. +On "Push to remote", load `${CLAUDE_SKILL_DIR}/references/push-publication.md` now and follow it: commit first, then freeze the publication target, run `preflight-mutations`, push with a lease, and read back remote and local state. -After preserving the remote result, re-read the active symbolic ref, frozen publication branch-ref SHA, and `HEAD`. Continue only when the symbolic ref equals `<exact-ref>` and both local SHAs equal the frozen SHA. When the remote landed but any local value moved, record `remote landed / local publication moved`, set overall publication to `reconcile-required`, and stop without retrying the push. When the remote did not land, preserve its observed state and stop as `reconcile-required` regardless of local state. Only after both remote and local state pass may a GitHub PR input read `headRefName`, `headRefOid`, and `headRepository.id`; require them to equal the frozen head name, frozen SHA, and intended publication target ID. On "Re-run on remaining": if the original input was a local file, invoke `/fix-pr-review <original-file-path>` scoped to skipped/needs-input items; otherwise invoke `/fix-pr-review <url>` scoped to remaining items. On "Done": exit. +On "Re-run on remaining": if the original input was a local file, invoke `/fix-pr-review <original-file-path>` scoped to skipped/needs-input items; otherwise invoke `/fix-pr-review <url>` scoped to remaining items. On "Done": exit. ### 6. Exit -Committing happens only on an explicit "Commit changes" or "Push to remote" choice in the post-completion prompt above. Otherwise leave the working tree as it stands. The report's suggested commit message is the hand-off. +Committing happens only on an explicit "Commit changes" or "Push to remote" choice in the post-completion prompt above, or in step 2b of an unattended watch run. Otherwise leave the working tree as it stands. The report's suggested commit message is the hand-off. diff --git a/skills/fix-pr-review/references/final-report.md b/skills/fix-pr-review/references/final-report.md index 7a0e6fa..6af03b4 100644 --- a/skills/fix-pr-review/references/final-report.md +++ b/skills/fix-pr-review/references/final-report.md @@ -29,6 +29,11 @@ and its exact settling query. Hold every retry until that query settles. ``` # Fix PR Review, PR #<num>: <title> +<Only when UNATTENDED_WATCH=true:> +Unattended watch run: committed <yes, <sha> | no, <reason>>; pushed <yes, <sha> | no, <reason>>; + posted <n> auto-confirmed contested replies and <n> FIX replies; + held <n> FIX threads as fixed locally, not pushed; parked <n> items as NEEDS-INPUT + Convergence: converged. All <F> fixes class-complete, inverse risk absent, no new siblings | NOT converged. <n> of <F> fixes partial or reverted (see below) @@ -121,6 +126,14 @@ Convergence: converged. All <F> fixes class-complete, inverse risk absent, no ne Other lanes: <lane: state, then evidence, or "none required"> Readiness: <ready | ready-to-publish | not ready> with the evidence ceiling Remaining after self-heal: <list or "none"> + Moderate/Minor after self-heal: <done_moderate_minor list or "none"> + +## Contested replies posted without asking (<count>) + [D2] src/auth/util.ts:12: DISMISS (R5), auto-confirm: claude_md_quote, found verbatim + +# Render only for items that passed Phase 4 by evidence auto-confirm, with the +# auto-confirm record (rule, re-checked field, result). Omit the section when +# the user answered the question. ## GitHub reply/resolve (<count>) [1] posted ✓ resolved ✓ src/db/schema/meal-menu-portions.ts:53 diff --git a/skills/fix-pr-review/references/github-reply-resolve.md b/skills/fix-pr-review/references/github-reply-resolve.md index b6bb68c..e9e7220 100644 --- a/skills/fix-pr-review/references/github-reply-resolve.md +++ b/skills/fix-pr-review/references/github-reply-resolve.md @@ -87,7 +87,7 @@ On failure: dispatch a 1-off `general-purpose` subagent with the original commen ## Step 7c: Post loop -Immediately before the first reply in this authorized batch, invoke `preflight-mutations`. Pass the exact PR URL, base and current head SHA, every target thread ID, ordered reply/resolve actions, final validated reply text and classification per item, and the authorization evidence that covers each item. FIX items use `execution_authorization_evidence`, plus their per-fix confirmation when `--interactive` was set. DISMISS, DEFER, and DISAGREE items use their contested-item confirmation. Apply the result contract before continuing. +Immediately before the first reply in this authorized batch, invoke `preflight-mutations`. Pass the exact PR URL, base and current head SHA, every target thread ID, ordered reply/resolve actions, final validated reply text and classification per item, and the authorization evidence that covers each item. FIX items use `execution_authorization_evidence`, plus their per-fix confirmation when `--interactive` was set. DISMISS, DEFER, and DISAGREE items use their contested-item confirmation. An auto-confirmed contested item uses `execution_authorization_evidence` plus its Phase 4 auto-confirm record: rule, re-checked field, and result. Apply the result contract before continuing. For each item with a non-null `thread_id` (actionables only, NOT nitpicks, NOT NEEDS-INPUT, NOT `reply_invalid`, and NOT any FIX item Step 7a left without a `reply_final`, i.e. `fix_status ∈ {skipped, aborted, partial, reverted_inverse_risk, inverse_risk_applied}`): diff --git a/skills/fix-pr-review/references/push-publication.md b/skills/fix-pr-review/references/push-publication.md new file mode 100644 index 0000000..ed33ea1 --- /dev/null +++ b/skills/fix-pr-review/references/push-publication.md @@ -0,0 +1,20 @@ +# Push to remote + +Loaded by main in Phase 8 step 5, only when the user picks "Push to remote". The commit prerequisites in SKILL.md step 5 still apply. + +On "Push to remote", commit first, then require the commit tree to still equal the snapshot before freezing the publication branch and push-attempt SHA. For GitHub PR input, re-read the current PR's `headRefName` plus `headRepository.id` and `nameWithOwner`; freeze those authoritative values, require the Phase 1 expected PR branch and active branch to equal the frozen head name, and set `<exact-ref>` to `refs/heads/<frozen-headRefName>`. That head repository identity is the intended publication target and remains independent of the input/base repository. For local-file or other no-current-PR input, freeze the active branch and derive `<exact-ref>` as `refs/heads/<active-branch>`. Freeze the push-attempt SHA only when the active symbolic ref equals `<exact-ref>` and that branch ref and `HEAD` resolve to the same SHA. Resolve the intended publication target before binding `<preflighted-remote>`. For GitHub PR input, enumerate configured remotes and validate every endpoint in each ordered complete fetch/push set. Auto-select the only remote whose every endpoint matches the PR head identity; when multiple match, immediately use AskUserQuestion with concrete `<remote>: <nameWithOwner> (<id>)` options and pagination when needed; when none match, stop with `Configure a remote whose complete endpoint sets resolve to the current PR head repository, then choose Push to remote again.` The selected match becomes `<preflighted-remote>`. + +For local-file or other no-current-PR input, immediately follow the "Push to remote" choice with AskUserQuestion. Inventory every configured remote; capture without printing its ordered complete fetch and push endpoint sets, normalize and resolve every endpoint independently through authenticated `gh repo view ... --json id,nameWithOwner`, and retain a remote only when every endpoint resolves to the same repository ID and `nameWithOwner`. Show each retained remote as a concrete option labelled `<remote>: <nameWithOwner> (<id>)`; list rejected remotes with the failing endpoint or set mismatch but never make them selectable. If the option limit cannot hold every retained remote, paginate disjoint sets of concrete options with a `Show next remotes` choice until the user selects one. That selection freezes both complete sets, sets `<preflighted-remote>` and the explicit intended publication target, and joins the preflight authorization and guards. If no remote is valid, stop with: `Configure a remote whose complete fetch and push endpoint sets resolve to one authenticated GitHub repository, then choose Push to remote again.` + +Capture without printing the ordered complete sets from `git remote get-url --all <preflighted-remote>` and `git remote get-url --push --all <preflighted-remote>`, then normalize every endpoint to a credential-free URL identity. Preserve transport class, host, port, and owner/repository while treating equivalent spellings such as scp-like versus `ssh://`, default ports, trailing slash, and `.git` suffix alike. Independently resolve every normalized endpoint through authenticated `gh repo view ... --json id,nameWithOwner`; require every endpoint ID and `nameWithOwner` to equal the intended publication target. Retain only the ordered normalized sets or their digests and resolved IDs, never credentials. Record the remote name, both complete endpoint sets, and intended target; any addition, removal, reorder, resolution failure, or repository mismatch is `reconcile-required`. The named-remote push may target its configured complete push set only while that frozen set still matches. + +Query `<exact-ref>` and record its remote SHA or authoritative absence; when present, require it to be an ancestor of the frozen SHA. Immediately before `git push`, invoke `preflight-mutations` with the exact remote, both ordered complete endpoint sets and repository IDs, intended publication target, `<exact-ref>`, frozen local SHA, expected remote SHA or absence, upstream state, commit range, dependent refs, and the user's choice. Include the PR base, frozen `headRefName`, `<exact-ref>`, and head repository ID in its guards and invalidators when a PR exists. Include the active symbolic ref, branch-ref SHA, `HEAD`, frozen publication branch and exact ref, and complete selected-remote identity in those guards and invalidators, then re-enumerate and compare both endpoint sets before running the exact push: + +```bash +git push --force-with-lease="<exact-ref>:<expected-sha-or-empty>" "<preflighted-remote>" "<frozen-local-sha>:<exact-ref>" +git ls-remote --heads "<preflighted-remote>" "<exact-ref>" +``` + +After every attempted push, including a nonzero or ambiguous exit, establish remote state first: re-enumerate both complete endpoint sets and run the exact `git ls-remote` read-back for `<exact-ref>` once. Record the push as landed only when the authoritative remote SHA equals the frozen SHA, the ordered sets are unchanged, and every endpoint still equals the intended publication target. An old or unexpected SHA, unavailable read-back, added, removed, reordered, unresolved, or mismatched endpoint is `reconcile-required`; stop and never retry from the command result alone. + +After preserving the remote result, re-read the active symbolic ref, frozen publication branch-ref SHA, and `HEAD`. Continue only when the symbolic ref equals `<exact-ref>` and both local SHAs equal the frozen SHA. When the remote landed but any local value moved, record `remote landed / local publication moved`, set overall publication to `reconcile-required`, and stop without retrying the push. When the remote did not land, preserve its observed state and stop as `reconcile-required` regardless of local state. Only after both remote and local state pass may a GitHub PR input read `headRefName`, `headRefOid`, and `headRepository.id`; require them to equal the frozen head name, frozen SHA, and intended publication target ID. diff --git a/skills/fix-pr-review/references/triage-prompt.md b/skills/fix-pr-review/references/triage-prompt.md index df51dc9..913cdc0 100644 --- a/skills/fix-pr-review/references/triage-prompt.md +++ b/skills/fix-pr-review/references/triage-prompt.md @@ -152,8 +152,9 @@ If reusability-flagged, run these searches using the repo map + your tools searching for `format` does not automatically mean duplication) Non-monorepo mode (`repo_map_files == "N/A (not a monorepo)"`): - - Grep("<new-symbol-name>", "src/"): primary source root + - Grep("<new-symbol-name>", "<changed-file-dir>"): each changed file's directory - Grep("<new-symbol-name>", "."): repo root fallback + - Never assume `src/` exists - Read candidate matches to confirm Store the findings as `reusability_context:` on the comment. Use diff --git a/skills/fix-pr-review/references/triage-rubric.md b/skills/fix-pr-review/references/triage-rubric.md index 8f55c34..e20cd13 100644 --- a/skills/fix-pr-review/references/triage-rubric.md +++ b/skills/fix-pr-review/references/triage-rubric.md @@ -123,7 +123,7 @@ domain knowledge. Concrete examples: Anti-examples (these are NOT NEEDS-INPUT): - "Rename variable for clarity" → R3 style (or R6 if genuinely confusing) - "Missing null check" → you CAN verify by reading the file → R6 or R1 - - "Consider extracting helper" → R3 or R6 based on readability impact + - "Consider extracting helper" → R6 when `reusability_context.flagged` is true, otherwise R3 Err toward FIX for mechanical low-risk changes you can verify. Err toward NEEDS-INPUT when the comment hinges on domain knowledge or runtime behavior. diff --git a/skills/fix-ts-errors/SKILL.md b/skills/fix-ts-errors/SKILL.md index b9bae1f..1f8388c 100644 --- a/skills/fix-ts-errors/SKILL.md +++ b/skills/fix-ts-errors/SKILL.md @@ -9,7 +9,7 @@ I fix the types at their source and loop the workspace check until it exits 0. I ## Workflow -### Step 1: Identify Target Files +### Step 1: Identify target files - If the user specifies files, I use those. - If the user says "changed files", I run `git diff --name-only HEAD` for staged plus unstaged `.ts` and `.tsx` files, plus `git ls-files --others --exclude-standard` for untracked ones. @@ -21,13 +21,13 @@ I run the workspace type-check, `pnpm type-check` or `turbo type-check`, or `bun I use IDE diagnostics, the LSP tool or `mcp__ide__getDiagnostics`, only to localize an error the check already reported. IDE diagnostics are per-file and unreliable across workspace package boundaries, so they narrow. They never decide. -### Step 3: Fix Errors +### Step 3: Fix errors For each error I find, I read the file around the error location, understand the root cause, then fix at the source with a proper type-safe pattern. I fix in this order. Missing imports get the import added. Type mismatches get the declaration corrected at its source. Missing properties get aligned with the source type definition. Null or undefined gets narrowed with a type guard or an early return. Generic type issues get explicit type parameters. -Every error exits through a real type. I add the missing import, narrow the union, widen the signature at its source, or derive it from the schema with `z.infer`. An `as` survives only with a comment naming the third-party gap. +Every error exits through a real type. I add the missing import, narrow the union, widen the signature at its source, or derive it from the schema with `z.infer`. An `as` survives only with the one-line citation comment that Step 5 requires. ### Step 4: Re-verify @@ -35,7 +35,7 @@ I re-run the same workspace check. Green means it exits 0. A file whose squiggles cleared is not green. A type change breaks its importers, so I chase the cascade to the workspace edge before I report. When new errors appeared, I go back to Step 3. -### Step 5: Escape-Hatch Audit +### Step 5: Escape-hatch audit Once green, I grep the changed lines for all four escape hatches, `as` assertions excluding `as const`, `@ts-ignore`, `@ts-expect-error`, and non-null assertions. @@ -46,17 +46,17 @@ git ls-files --others --exclude-standard -- '*.ts' '*.tsx' | tr '\n' '\0' | xarg The `[!]` groups match a postfix assertion: a value character, closing quote, bracket, or paren before the bang and no equals after it, so `value!`, `value!.x`, `value![0]`, `value!()`, `"value"!`, and backtick-quoted receivers all hit while `!=`, `!==`, and prefix negation like `!ready` stay out. The trailing `sed` neutralizes only the `as const` idiom into a marker instead of dropping whole lines, so co-occurring hatches on the same line survive. A line whose only match is the `__AS_CONST__` marker needs no action. -I skip import aliases like `import { x as y }`. For every remaining hit I attempt removal with proper typing, meaning inference, narrowing, type guards, generics, or schema-derived types with `z.infer`. I re-run the check after each removal, and when errors appear I go back to Step 3. A hatch survives only when genuinely unavoidable, for example a third-party library type gap, and it must carry a one-line comment citing the upstream issue or type gap. +I skip import aliases like `import { x as y }`. For every remaining hit I attempt removal with proper typing, meaning inference, narrowing, type guards, generics, or schema-derived types with `z.infer`. I re-run the check after each removal, and when errors appear I go back to Step 3. A hatch survives only with a one-line comment citing the upstream issue as a URL or `#N`. When no upstream issue exists, the comment cites a permalink URL to the line in the library's type definition that causes the gap. ### Step 6: Report -Once green, I briefly report how many errors I found, what I fixed, and the surviving escape hatches, `as` casts, `@ts-ignore`, `@ts-expect-error`, and non-null assertions, with the count of each and a one-line justification per survivor. My target is 0. +Once green, I briefly report how many errors I found, what I fixed, and the surviving escape hatches, `as` casts, `@ts-ignore`, `@ts-expect-error`, and non-null assertions, with the count of each and the citation for each survivor. My target is 0. ## Loop breaker When errors persist after 3 fix-verify cycles, I stop and explain the root cause, show the remaining errors, suggest whether the issue sits in the file itself or in a dependency, and ask the user how to proceed. -## Important rules +## Rules -- I fix a wrong type in a shared workspace package in that package. I report it and stop. +- A wrong type in a shared workspace package gets its fix in that package, not here. I report it and stop. - I check whether the error sits in generated code or authored code. When a codegen step is stale, I suggest the command and do not run it. diff --git a/skills/git-commit/SKILL.md b/skills/git-commit/SKILL.md index 6341c89..5b66cb1 100644 --- a/skills/git-commit/SKILL.md +++ b/skills/git-commit/SKILL.md @@ -10,7 +10,7 @@ I read the actual diff to find the type, scope, and message. I never write the m ## Message-only mode -When I get asked for a message, phrases like "give me a commit message" or "commit message suggestion", instead of getting asked to commit, I analyze the diff as usual and print two variants to run, a one-liner and a detailed one. +When the user asks for a commit message, such as "give me a commit message" or "commit message suggestion", and not for a commit, I analyze the diff as usual and print two variants to run: a one-liner and a detailed one. ## Workflow @@ -28,9 +28,9 @@ I classify every file in `git status --porcelain` into exactly one commit before I stage only files I have read in the diff. I leave `.env`, `credentials.json`, and key files unstaged. -When the caller supplies a Verified content snapshot, I record the branch, old head, and original index tree, then build the candidate commit in a disposable detached worktree at that head. I load the verified tree into that worktree index and files, require `git write-tree` to equal the snapshot, and run the ordinary commit there so every hook still runs. I require the candidate commit `HEAD^{tree}` to equal the snapshot. A hook failure or mismatch leaves the real branch untouched. +When the caller supplies a Verified content snapshot, I record the branch, old head, and original index tree, then build the candidate commit in a disposable detached worktree at that head. I load the verified tree into that worktree's index and files, require `git write-tree` to equal the snapshot, and run the ordinary commit there so every hook still runs. I require the candidate commit's `HEAD^{tree}` to equal the snapshot. A hook failure or mismatch leaves the real branch untouched. -Before I touch the real branch, I prepare an isolated replacement index from the candidate commit and require its tree to equal the snapshot. Only after the candidate commit and replacement index are complete do I acquire the real index lock exclusively. While I hold it, I copy the real index to an isolated path and require its tree to equal the recorded original index tree. A mismatch releases the lock and leaves the branch untouched. I atomically advance the recorded branch from the recorded old head to the candidate commit, write the prepared index bytes into the held lock, require that locked index tree to equal the snapshot, and atomically install it as the real index without changing the worktree. I release the lock on every path and remove the disposable worktree. If index installation or read-back fails after the branch advances, I report `branch advanced / index not refreshed` with the branch, candidate commit, recorded index tree, and recovery command. I never retry the branch transition or hide the partial state. I never acquire the real index lock while hooks or another Git command still need it, create the candidate commit on the real branch, or restage its live worktree. +Before I touch the real branch, I prepare an isolated replacement index from the candidate commit and require its tree to equal the snapshot. Only after the candidate commit and replacement index are complete do I acquire the real index lock exclusively. While I hold it, I copy the real index to an isolated path and require its tree to equal the recorded original index tree. A mismatch releases the lock and leaves the branch untouched. I atomically advance the recorded branch from the recorded old head to the candidate commit, write the prepared index bytes into the held lock, require that locked index's tree to equal the snapshot, and atomically install it as the real index without changing the worktree. I release the lock on every path and remove the disposable worktree. If index installation or read-back fails after the branch advances, I report `branch advanced / index not refreshed` with the branch, candidate commit, recorded index tree, and recovery command. I never retry the branch transition or hide the partial state. I never acquire the real index lock while hooks or another Git command still need it, create the candidate commit on the real branch, or restage its live worktree. ### 3. Generate commit message @@ -58,4 +58,4 @@ I land one atomic change per commit. Every git operation here stays append-only. I stage, commit, and when a hook fails I fix it and land a new commit on top instead of amending. -Four things sit outside that word and I say them plainly. I leave `git config` alone, I let hooks run with no `--no-verify`, I keep `--force` and hard resets behind an explicit request, and I never force-push `main`. +Four rules sit outside append-only: I leave `git config` alone, I let hooks run with no `--no-verify`, I keep `--force` and hard resets behind an explicit request, and I never force-push `main`. diff --git a/skills/grill-me/SKILL.md b/skills/grill-me/SKILL.md index 7bd990e..4884378 100644 --- a/skills/grill-me/SKILL.md +++ b/skills/grill-me/SKILL.md @@ -9,11 +9,13 @@ If I invoke you with a file path argument, read that file first and grill its co ## Rules -Always use the AskUserQuestion tool for every question you present to me. Every decision reaches me as a cursor-selectable option. +Ask every question through the AskUserQuestion tool, so each decision reaches me as a selectable option. - Before the first question, enumerate the open decisions in the plan as a numbered list and print it. +- When the open list holds more than 10 decisions, group them by area and offer to settle a whole group in one answer. - Ask decision zero first: is this approach even right. Offer one credible alternative drawn from the codebase or the issue, and grill it before the rest. - Ask one question at a time. Make one AskUserQuestion call per turn, then wait for my answer. +- A chat reply I type while a question is pending is my answer to it. Record it and move on. - Give 2-4 options per question. Put your recommended answer first with "(Recommended)" in the label. - Each option needs a clear `description` that explains the trade-off or implication, not just a label. - State each question in plain words with one line of context, then give one concrete example before the options. Never ask a bare technical question. @@ -22,6 +24,7 @@ Always use the AskUserQuestion tool for every question you present to me. Every - When an answer contradicts an earlier one or leaves a dependency unresolved, say which one and re-ask before moving on. - If you can answer a question by exploring the codebase, explore it instead of asking me. Use Grep, Glob, Read, or Agent to verify assumptions before grilling me on them. - If I say "enough" or "done" or "stop" or "skip the rest", append per the file rule and post per the issue rule below with the decisions captured so far, then announce "Grill complete." and exit. +- Every "Grill complete." announcement ends with a `Pending on you:` line that names each decision or action still waiting on me, or reads `Pending on you: nothing`. - When every numbered decision has a recorded answer and no answer has opened a new one, move to the verification pass below instead of closing out. If an answer opens a new decision, append it to the list and say so. ## Verification pass @@ -30,8 +33,8 @@ The decisions are settled. Now prove the plan is true before I code. - List every factual claim in the plan: file paths, symbol names, numbers, commands, flags, CI job names. - Check each claim yourself with Grep, Glob, Read, or Agent. Fix what you can verify without asking, and say what you changed in one line each. -- Ask about only what you cannot resolve on your own. Ask it exactly like a decision question: plain words, one line of context, one concrete example, 2-4 options with your recommended answer first. +- Ask about only what you cannot resolve on your own. Ask it under the same rules as a decision question. - Never ask me to confirm a fact you could have checked. Never batch questions. - If I invoked you with a file path, append the numbered decisions and the verified corrections under a `## Decisions` heading in that file before announcing "Grill complete." -- If a GitHub issue was provided in context as a URL, number, or issue body, post an issue comment for tracking before announcing "Grill complete." The body holds the numbered decisions and the verified corrections followed by the full plan, or whatever is captured so far on early exit. Comment on the exact issue from context with `gh issue comment <url> --body`. When context holds only a number, resolve its repository first instead of assuming the checkout. +- If the context holds a GitHub issue as a URL, number, or issue body, post an issue comment for tracking before announcing "Grill complete." The body holds the numbered decisions and the verified corrections followed by the full plan, or whatever is captured so far on early exit. Comment on the exact issue from context with `gh issue comment <url> --body`. When context holds only a number, resolve its repository first instead of assuming the checkout. - When every claim is verified or answered, announce "Grill complete." with the numbered decisions and the corrections. diff --git a/skills/manage-report-lifecycle/SKILL.md b/skills/manage-report-lifecycle/SKILL.md index e4c0b25..b104bf8 100644 --- a/skills/manage-report-lifecycle/SKILL.md +++ b/skills/manage-report-lifecycle/SKILL.md @@ -6,7 +6,7 @@ disable-model-invocation: true # Manage report lifecycle -Establish one authoritative hosted report while preserving every source item and predecessor. This skill coordinates lifecycle semantics here, shared-state authorization through `preflight-mutations`, and final acceptance through `done`. +Establish one authoritative hosted report while preserving every source item and predecessor. This skill owns lifecycle semantics. `preflight-mutations` authorizes shared-state writes, and `done` owns final acceptance. ## 1. Inventory the report set @@ -16,7 +16,7 @@ Fetch the rendered content and stable URL of every source and canonical candidat Keep this working record inline during read-only inventory and staging. Before the multi-write publication begins, persist it in an existing durable ledger that the user authorized this workflow to write. Record its stable URL or path and require it to remain outside every canonical and predecessor publication target for the full run. When no such ledger exists, stop and name authorization of a suitable existing artifact as the next action. Create no lifecycle registry. -The gate is that the exact discovery query and every classified match are recorded, every source, canonical candidate, and source item appears once, write authority is known, and competing authority claims are explicit. +**Gate.** The exact discovery query and every classified match are recorded, every source, canonical candidate, and source item appears once, write authority is known, and competing authority claims are explicit. ## 2. Stage the canonical record @@ -47,13 +47,13 @@ Select one disposition for every source item: |---|---|---|---| | <ID> | <URL> | carried \| merged \| dismissed \| retained-by-reference | <canonical item/anchor ID or dismissal rationale> | -`carried` and `merged` name an existing canonical destination ID. `dismissed` records a reason. `retained-by-reference` names the canonical anchor that keeps the original evidence reachable. Merge only genuinely equivalent items. Preserve unique findings as distinct destinations or references rather than copying entire source reports. +`carried` and `merged` name an existing canonical destination ID. `dismissed` records a reason. `retained-by-reference` names the canonical anchor that keeps the original evidence reachable. Merge only items with the same claim and the same evidence URLs. Preserve unique findings as distinct destinations or references rather than copying entire source reports. Finalize and validate the disposition map before rendering any publication payload. Require its source-item IDs to equal the inventory exactly, with no duplicate rows, and require every named canonical destination or anchor to exist in the staged canonical record. Render the complete canonical payload and every writable predecessor payload to separate immutable local staging paths, treating each path as write-once. Record each path and SHA-256 digest in the lifecycle ledger. Never overwrite or regenerate a frozen file. Changed bytes require a new path, digest, preview, and preflight. -The gate is that an existing hosted writable canonical candidate and guard are known, every discovery match has its final classification, every noncanonical authority is bound as a predecessor, and the canonical payload, predecessor report-plus-marker payloads, and item map are fully staged with every source item assigned exactly one valid disposition. +**Gate.** An existing hosted writable canonical candidate and guard are known, every discovery match has its final classification, every noncanonical authority is bound as a predecessor, and the canonical payload, predecessor report-plus-marker payloads, and item map are fully staged with every source item assigned exactly one valid disposition. ## 3. Preflight the publication plan @@ -70,17 +70,19 @@ Partition that plan into one or more mutation cards exactly where `preflight-mut Apply each card's `preflight-mutations` result contract in the previewed dependency order. Preserve blocked or confirmation-required cards and their dependent writes as pending. A new target, marker, or content change returns the complete plan to preview. Re-preflight only invalidated cards. -The gate is that the complete preview accounts for every lifecycle write, each write belongs to exactly one domain-correct card, and no unpreviewed or dependency-violating write can execute. +**Gate.** The complete preview accounts for every lifecycle write, each write belongs to exactly one domain-correct card, and no unpreviewed or dependency-violating write can execute. ## 4. Publish and reconcile -Apply the staged canonical payload and predecessor report-plus-marker payloads without regenerating them. Immediately before each write, rehash its frozen file and require the digest to match the ledger and current mutation card. A mismatch invalidates that card before the write. Atomically transition that item from `pending` to `attempting` in the durable ledger and continue only when the transition succeeds from the expected state. Follow `preflight-mutations`' execution and partial-state contract for each card while preserving the publication plan's dependency order and combined item ledger. After each attempted write, authoritatively read back the body and guard, then atomically persist that item's result, read-back guard, and observed body digest in the durable ledger before the next write. An unavailable or ambiguous ledger transition stops the dependent remainder. Mark an item `landed` only when its complete read-back body matches the frozen bytes. Require every predecessor's fetched content and attributed evidence to remain intact before recording its marker write as `landed`, and record the read-back guard as that target's expected post-write guard. +Apply the staged canonical payload and predecessor report-plus-marker payloads without regenerating them. Immediately before each write, rehash its frozen file and require the digest to match the ledger and current mutation card. A mismatch invalidates that card before the write. Atomically transition that item from `pending` to `attempting` in the durable ledger and continue only when the transition succeeds from the expected state. Follow `preflight-mutations`' execution and partial-state contract for each card while preserving the publication plan's dependency order and combined item ledger. -The gate is that every publication-plan item has an authoritative result, every landed payload matches its staged content, and the canonical report plus each writable predecessor has an expected post-write guard from authoritative read-back. +After each attempted write, authoritatively read back the body and guard, then atomically persist that item's result, read-back guard, and observed body digest in the durable ledger before the next write. An unavailable or ambiguous ledger transition stops the dependent remainder. Mark an item `landed` only when its complete read-back body matches the frozen bytes. Require every predecessor's fetched content and attributed evidence to remain intact before recording its marker write as `landed`, and record the read-back guard as that target's expected post-write guard. + +**Gate.** Every publication-plan item has an authoritative result, every landed payload matches its staged content, and the canonical report plus each writable predecessor has an expected post-write guard from authoritative read-back. ## 5. Verify authority and evidence -Re-fetch and reread the rendered canonical report and every predecessor. Exercise every canonical, source, predecessor, and evidence link. Repeat the exact Phase 1 discovery query and compare its stable host IDs and guards with the expected set. Use Phase 4's authoritative post-write guards for landed targets and Phase 1 guards for untouched matches. An expected landed guard is not drift. A missing stable ID, a new match, or any guard outside that expected set returns to inventory, staging, and preflight. Fetch every unexpected match before continuing. +Re-fetch and reread the rendered canonical report and every predecessor. Exercise every canonical, source, predecessor, and evidence link. Repeat the exact section 1 discovery query and compare its stable host IDs and guards with the expected set. Use section 4's authoritative post-write guards for landed targets and section 1 guards for untouched matches. An expected landed guard is not drift. A missing stable ID, a new match, or any guard outside that expected set returns to inventory, staging, and preflight. Fetch every unexpected match before continuing. Completion requires: diff --git a/skills/openclaw-backup/SKILL.md b/skills/openclaw-backup/SKILL.md index 405967a..dcc0f8e 100644 --- a/skills/openclaw-backup/SKILL.md +++ b/skills/openclaw-backup/SKILL.md @@ -41,7 +41,7 @@ openclaw backup --help openclaw backup create --help ``` -`--help` is the source of truth for this install. `docs.openclaw.ai` documents a `backup sqlite` subcommand that shipped builds may lack, so the docs site describes a capability the binary may not have. +`--help` is the source of truth for this install. `docs.openclaw.ai` documents a `backup sqlite` subcommand that shipped builds may lack. Pass this step only when you recorded the subcommand list. If `sqlite` is present, prefer it over Step 3 and say so in the report. @@ -88,7 +88,7 @@ tar --use-compress-program="zstd -3 -T0" \ echo "EXIT=$?" ``` -`node_modules` is excluded as reinstallable and often a third of the total. Everything else stays. The volatile files Step 4 dropped are the point of this step. +`node_modules` is reinstallable and often a third of the total, so the archive excludes it. Everything else stays. The volatile files Step 4 dropped are the point of this step. Then produce the evidence: @@ -184,7 +184,7 @@ Re-read the receiving system's encryption and effective access controls after th Pass this step only when every checked file reports `OK`, encryption at rest remains enabled, effective access stays restricted to the approved recipient accounts, and the retention and deletion path remains available. A mismatched file clears the gate only through the fresh-card retry contract above. A failed security control blocks completion while the sensitive partial state is preserved or separately deleted. `--partial` makes the new transfer resume. -Omitting the official archive from the pull is reasonable. Its contents are a subset of the raw archive. State that omission in the report. +You may omit the official archive from the pull, since the raw archive holds all of its contents. State the omission in the report. ## Step 9: Report diff --git a/skills/openclaw-backup/agents/openai.yaml b/skills/openclaw-backup/agents/openai.yaml new file mode 100644 index 0000000..3cdc40b --- /dev/null +++ b/skills/openclaw-backup/agents/openai.yaml @@ -0,0 +1,7 @@ +interface: + display_name: "OpenClaw Backup" + short_description: "Verified, restorable OpenClaw backup with a restore runbook" + default_prompt: "Use $openclaw-backup to back up my OpenClaw install and verify the archive." + +policy: + allow_implicit_invocation: false diff --git a/skills/parallel-review/SKILL.md b/skills/parallel-review/SKILL.md index 55d6ff7..60cc9bc 100644 --- a/skills/parallel-review/SKILL.md +++ b/skills/parallel-review/SKILL.md @@ -5,11 +5,11 @@ description: Run every code reviewer in parallel over a local diff and merge the # Parallel code review -I run every reviewer at once over the local diff and merge what they find into one ranked list. Fast, and no reviewer sees another one work, so the findings stay independent. +I run every reviewer at once over the local diff and merge what they find into one ranked list. No reviewer sees another's work, so the findings stay independent. ## Workflow -### Step 1: Determine Scope +### Step 1: Determine scope - When the user specifies files, I review those files. - Otherwise I review all unstaged and staged changes through `git diff HEAD`. @@ -20,11 +20,15 @@ I run every reviewer at once over the local diff and merge what they find into o I name the roster first, then launch every member in parallel with the Agent tool. -Members 1-3 below are `subagent_type` values, and I pass them to the Agent tool as-is. Member 4 is a skill-running agent. `/web-interface-guidelines` and `/rams` are skills, not agent types, so they cannot go as `subagent_type`. I dispatch a `general-purpose` agent that invokes them instead. +Every member is an Agent tool `subagent_type`. `/web-interface-guidelines` and `/rams` are skills, not agent types, so member 4 is a `general-purpose` agent prompted to invoke them. + +On a host without Claude Code agent types, such as Codex or OpenCode, I dispatch each member as the host's general agent (Codex `spawn_agent`, OpenCode `general`) with that member's lens in the prompt. The CodeRabbit member runs `coderabbit review --agent --uncommitted --include-untracked`, which reviews tracked uncommitted changes plus untracked files, and returns its findings. When Step 1 named specific files or a fix delta, I keep only its findings on those paths. It never passes `--base-commit`, which would widen the review past that scope. + +On every host, a member whose run exits non-zero, hits an auth or rate limit, times out, or returns empty output is `unavailable(<reason>)`. A missing CodeRabbit CLI counts the same way. I never fill an unavailable member's place with a stand-in. I size the roster to the diff before I name it. `done` runs this skill after every task, so an unsized roster charges a one-line fix what a rewrite costs. I measure the Step 1 scope with `git diff --shortstat HEAD -- <paths>` when files were specified, or bare `git diff --shortstat HEAD` when they were not. -- One file and under roughly 50 changed lines, or the user asked for a quick review, then the roster is `pr-review-toolkit:code-reviewer` alone and the rest of this step does not apply. +- One file and under 50 changed lines, or the user asked for a quick review, then the roster is `pr-review-toolkit:code-reviewer` alone and the rest of this step does not apply. - Anything larger builds the roster from the members below. A re-review after fixing findings covers the fix delta, never the whole diff again, and I size it by that delta. @@ -37,7 +41,7 @@ Always on the roster: I add to the roster when the condition holds: 3. Silent Failure Hunter with `subagent_type: "pr-review-toolkit:silent-failure-hunter"`. I add it when the diff touches error handling, try-catch, or fallback logic. -4. UI Review Agent with `subagent_type: "general-purpose"`. I add it when the diff touches frontend or UI code. I prompt it to invoke `/web-interface-guidelines` and `/rams` against the diff and return their merged findings. +4. UI Review Agent with `subagent_type: "general-purpose"`. I add it when the diff touches frontend or UI code. I prompt it to invoke `/web-interface-guidelines` and `/rams` against the diff and return their merged findings. When either is not loadable as a skill, it reads `~/.claude/commands/web-interface-guidelines.md` or `~/.claude/commands/rams.md` by path instead. When neither the skill nor that file exists, member 4 is `unavailable(<reason>)`, never skipped silently. The shared prompt, plus the per-agent lens, reads "Review these changed files for bugs, logic errors, and adherence to project CLAUDE.md conventions: [files]. The review target is [scope diff command] only, the Step 1 scope or the fix delta. The comparison base, [merge-base SHA], is context for the product-intent check alone: `git show [merge-base SHA]:<file>` is what the base branch has." @@ -47,7 +51,7 @@ I state the roster before dispatching. It is the checklist Step 3 merges against ### Step 3: Merge -The merge is not done while any reviewer on the roster is outstanding. I account for every member by name, reported or failed and re-dispatched. +The merge is not done while any reviewer on the roster is outstanding. I account for every member by name as reported, failed and re-dispatched, or `unavailable(<reason>)`, and I emit one roster coverage line that lists each member as reported or unavailable with its reason. 1. I merge all findings, collapsing duplicates across reviewers. A `product-intent` tag on any source finding survives the collapse, and its evidence carries into the merged finding. 2. I assign each merged finding a stable ID from its file, enclosing symbol, normalized defect class, and defect-instance fingerprint. I derive the fingerprint from the smallest stable semantic code anchor, such as a callee, accessed field, branch label, or data-flow endpoints, plus the violated invariant. When two defects still share an anchor, I extend it with the nearest distinct semantic parent or operand. I normalize incidental formatting, literals, and reviewer wording, and I exclude raw line numbers. I merge only when all four parts match, and I preserve every source reviewer. The caller owns these IDs and their dispositions. I do not create a shared finding-ID authority. diff --git a/skills/preflight-mutations/SKILL.md b/skills/preflight-mutations/SKILL.md index bf55b75..26c9cf6 100644 --- a/skills/preflight-mutations/SKILL.md +++ b/skills/preflight-mutations/SKILL.md @@ -5,7 +5,7 @@ description: Prepare or block a shared-state write. Use immediately before chang # Preflight mutations -I resolve permission, current state, dependencies, and recovery before a shared-state write. I produce a decision record here. I never perform the mutation itself. That separation is the point. +Before a shared-state write I resolve permission, current state, dependencies, and recovery, and produce a decision record. I never perform the mutation itself. ## 1. Define the mutation batch @@ -33,19 +33,27 @@ I render a simple card inline. For a multi-step or multi-batch run, I persist it - **Next action:** <one action permitted by the verdict> ``` -I keep the action, targets, environment, and batch-item IDs exact enough that another agent cannot select a broader resource by inference. A multi-step or multi-batch card needs an authorized durable home, and I state that as the gate. **Gate.** both: nothing selectable by inference, every multi-step card homed. +I keep the action, targets, environment, and batch-item IDs exact enough that another agent cannot select a broader resource by inference. I name every target and guarded dependency by stable ID in **Read-back plan**. I never collapse them into "all targets" or "consumers" or another referential phrase. +**Gate.** Nothing is selectable by inference, and every multi-step or multi-batch card has an authorized durable home. + ## 2. Resolve authority and ownership -I apply the target surface governing ownership policy. Technical write access does not expand the user requested scope and does not override assignee, author, resource-owner, or environment boundaries. +I apply the target surface's ownership policy. Technical write access never widens the requested scope or overrides assignee, author, resource-owner, or environment boundaries. I state that access-versus-authority distinction in **Ownership policy** whenever the evidence shows technical access beyond the authorized boundary. -A specific user request authorizes ordinary `reversible` writes only within its named action and targets. I require fresh explicit confirmation for restructuring like splitting, grouping, closing-with-successor, or rebasing a dependency, for production data or operations, service downtime, and off-box recipients, for resources outside the governing ownership boundary, for `compensating-only` or `irreversible` actions, and for rewriting or deleting published history. +A specific user request authorizes ordinary `reversible` writes only within its named action and targets. These need fresh explicit confirmation: + +- restructuring, such as splitting, grouping, closing-with-successor, or rebasing a dependency; +- production data or operations, service downtime, and off-box recipients; +- resources outside the governing ownership boundary; +- `compensating-only` or `irreversible` actions; +- rewriting or deleting published history. -I do not treat approval for one action class as approval for another. I record the exact authorization source in the card. +Approval for one action class never covers another. I record the exact authorization source in the card. **Gate.** Ownership is resolved and authorization covers the exact batch, or the verdict cannot be `ready`. @@ -56,12 +64,12 @@ I read current state from the authoritative system immediately before the batch. - For ordinary writes I inspect direct dependencies and current compare-before-write guards. - For deletion, closure, base changes, and history rewrites I traverse transitive consumers such as stacked PRs, dependent branches, linked issues, workflows, releases, umbrella records, and downstream services. - For Git history I compare local refs with their upstreams and consumers. When a commit or ref is published or consumed, I put the append-only corrective commit or successor-ref alternative in **Preview / exclusions** and surface that choice in **Next action**. I classify its rewrite or deletion as `compensating-only` at best because restoring a ref cannot undo publication or downstream consumption. It stays `confirmation-required`. -- For production-like UI or operations I verify environment, record ownership, snapshot affected records when possible, and name the captured prior values plus restoration or compensation steps. When downstream billing, entitlement, notification, or audit effects persist after restoring the primary record, I classify the action as `compensating-only`. +- For production-like UI or operations I verify environment, record ownership, and name the captured prior values plus restoration or compensation steps. I snapshot affected records, or state in **Recovery plan** why no snapshot exists. When downstream billing, entitlement, notification, or audit effects persist after restoring the primary record, I classify the action as `compensating-only`. - For off-box copies I resolve the recipient, included sensitive material, retention, and revocation or deletion path. I put every target stable ID plus every mutable fact that protects its write in **Current guards** and **Invalidators**. -**Gate.** Required dependencies and consumers are enumerated from current evidence. An unknown dependency on a destructive action yields `blocked`. +**Gate.** I enumerate required dependencies and consumers from current evidence. An unknown dependency on a destructive action yields `blocked`. ## 4. Preview blast radius and recovery @@ -91,14 +99,14 @@ I pass the `ready` card to the workflow that owns the mutation. Immediately befo Every caller follows the same result contract. It continues only when **Verdict** is `ready` and **Invalidators** still match. On `confirmation-required` it presents the card and waits for the named confirmation. On `blocked` it stops the mutation and reports **Next action**. -The execution handoff must use the surface native conditional-write mechanism when one exists, meaning expected versions, compare-and-swap tokens, exact-SHA leases, or idempotency keys. A recovery push for published Git history must also stay lease-guarded against the re-read remote head. +The execution handoff must use the surface's native conditional write when one exists: expected versions, compare-and-swap tokens, exact-SHA leases, or idempotency keys. A recovery push for published Git history must also stay lease-guarded against the re-read remote head. After each attempted item, I record `landed`, `failed`, `skipped`, or `reconcile-required`. I use `reconcile-required` when the command result and authoritative read-back cannot establish whether the write landed. I stop that item. I do not retry it or classify it as `failed` until an authoritative query resolves its state. -On interruption, invalidation, or `reconcile-required`, I preserve the item ledger, authoritatively re-read every `landed` item, record the observed partial external state, and create a new card whose **Targets** cover only the safe `pending` remainder, while **Batch items** carries the authoritative `landed`, `failed`, `skipped`, and unresolved `reconcile-required` history alongside those pending items. +On interruption, invalidation, or `reconcile-required`, I preserve the item ledger, authoritatively re-read every `landed` item, and record the observed partial external state. Then I create a new card. Its **Targets** cover only the safe `pending` remainder. Its **Batch items** carry those pending items plus the authoritative `landed`, `failed`, `skipped`, and unresolved `reconcile-required` history. -I exclude every `reconcile-required` item from the new card write targets. Its **Next action** is the exact authoritative query needed to resolve that item. Only the observed result may move it to `landed`, `failed`, or `pending`. +I exclude every `reconcile-required` item from the new card's write targets. Its **Next action** is the exact authoritative query needed to resolve that item. Only the observed result may move it to `landed`, `failed`, or `pending`. I never restart from an assumed zero state. The execution workflow owns writes. `verify-claims` may validate consequential post-write claims, and the final completion workflow owns its acceptance-surface verdict. -**Done.** The card is populated, its verdict matches the gates, and no shared-state mutation has been performed by this skill. +**Done.** The card is populated, its verdict matches the gates, and this skill has performed no shared-state mutation. diff --git a/skills/project-discovery/SKILL.md b/skills/project-discovery/SKILL.md index ba4b4db..35d6a6b 100644 --- a/skills/project-discovery/SKILL.md +++ b/skills/project-discovery/SKILL.md @@ -34,7 +34,7 @@ When the interview reaches a layer the user has not already decided, read `${CLA ### Opening ```text -"Before we write any code, I want to understand what we're building and make sure we make good foundational decisions. This might take 15-30 minutes but will save hours later. Let's start with the big picture. What are we building and who is it for?" +"Before we write code, I want to pin down what we're building and the decisions that are expensive to reverse. Expect 15-30 minutes. Let's start with the big picture. What are we building and who is it for?" ``` ### During discovery diff --git a/skills/project-discovery/references/coding-standards.md b/skills/project-discovery/references/coding-standards.md index 5574eff..006dfde 100644 --- a/skills/project-discovery/references/coding-standards.md +++ b/skills/project-discovery/references/coding-standards.md @@ -1,4 +1,4 @@ -# Code Patterns +# Code patterns These are the project's laws. They hold in every file, in every session. @@ -88,7 +88,7 @@ inferred from the DB schema (`type User = typeof users.$inferSelect`). **Run the project's type-check command and fix every error before reporting the task complete.** Run `/done` after completing a task. -## 2. Functions & Files +## 2. Functions & files **Functions are single-responsibility; compose them into operations.** 5-20 lines ideally, 30-40 at most. If you need comments to separate sections inside a @@ -137,7 +137,7 @@ const label = STATUS_LABELS[status] ?? 'Unknown'; - No comments in code. The only exceptions are a one-line docstring on an exported symbol and a one-line citation (URL, spec section, ADR or `docs/` path, issue number) for a constraint the code cannot express. - No emoji in logs or code. -## 3. Error Handling +## 3. Error handling Pick one pattern and hold it across the entire codebase. @@ -201,7 +201,7 @@ in `lib/errors.ts`. (`/api/v1/medical-records`), verb suffix for actions (`POST /api/v1/appointments/:id/confirm`). -## 5. Imports & Folder Structure +## 5. Imports & folder structure **Path aliases, always**: `"paths": { "@/*": ["./src/*"] }` in tsconfig. @@ -325,7 +325,7 @@ async function createAppointmentWithNotification(data: CreateAppointmentInput) { } ``` -## 8. React Components +## 8. React components **`useEffect` synchronizes with an external system**: subscriptions, DOM, analytics, non-React libraries. Derived state is computed during render; state @@ -448,19 +448,17 @@ const appointments = pgTable('appointments', { }); ``` -## 13. Git & Documentation +## 13. Git & documentation -**Conventional commits**, imperative mood, 50-char subject: `feat:`, `fix:`, +**Conventional commits**, imperative mood, 72-char subject: `feat:`, `fix:`, `refactor:`, `perf:`, `chore:`, `docs:`, `test:`, `style:`, with an optional scope, `feat(auth): add OAuth2 support`. -**Document non-obvious behaviour**, and give each feature folder a README with -overview, key files, domain rules and endpoints. +**Document non-obvious behaviour** in a test name, the PR body or an ADR, never +in a code comment, and give each feature folder a README with overview, key +files, domain rules and endpoints. ```typescript -/** - * Returns appointments for the next 7 days. - * Excludes cancelled appointments. Results are cached for 5 minutes. - */ -async function getUpcomingAppointments(patientId: string) {} +it('excludes cancelled appointments from the next 7 days', async () => {}); +it('serves repeat requests from a 5-minute cache', async () => {}); ``` diff --git a/skills/project-discovery/references/output-templates.md b/skills/project-discovery/references/output-templates.md index 66284b3..0d2ed31 100644 --- a/skills/project-discovery/references/output-templates.md +++ b/skills/project-discovery/references/output-templates.md @@ -1,4 +1,4 @@ -# Output Templates +# Output templates ## 1. CLAUDE.md (or PROJECT.md) @@ -9,11 +9,11 @@ [2-3 sentences on what this is] ## Tech stack -- Frontend: Next.js 15 (App Router) +- Frontend: Next.js 16 (App Router) - Backend: Next.js API Routes - Database: PostgreSQL (Supabase) - ORM: Drizzle -- Auth: Lucia + GitHub OAuth +- Auth: Better Auth + GitHub OAuth - Styling: Tailwind + shadcn/ui - Hosting: Vercel @@ -35,7 +35,7 @@ Every category in `stack-menu.md` gets a line here, including the ones decided as "not needed" and why. -## 2. Initial Folder Structure +## 2. Initial folder structure ``` src/ @@ -52,7 +52,7 @@ src/ types/ ``` -## 3. Config Files +## 3. Config files - `tsconfig.json` with strict mode + path aliases - `.env.example` with all required vars diff --git a/skills/project-discovery/references/stack-menu.md b/skills/project-discovery/references/stack-menu.md index d206204..e61fc4a 100644 --- a/skills/project-discovery/references/stack-menu.md +++ b/skills/project-discovery/references/stack-menu.md @@ -1,14 +1,14 @@ -# Stack Menu: Discovery Categories 2-10 +# Stack menu: discovery categories 2-10 -Category 1 (PROJECT OVERVIEW) is in `SKILL.md`. Use the option list and the +Category 1 (Project overview) is in `SKILL.md`. Use the option list and the separating questions for whichever layer the interview has reached. -### 2. TECH STACK +### 2. Tech stack Open each category with a recommendation fitted to their constraints; let them push back. **Frontend Framework** -- Next.js (App Router): Full-stack, great DX, Vercel-optimized +- Next.js (App Router): full-stack React with server components, deploys natively on Vercel - Remix: Better data loading patterns, more control - SvelteKit: Simpler mental model, less boilerplate - Astro: Content-heavy sites, partial hydration @@ -38,7 +38,7 @@ Open each category with a recommendation fitted to their constraints; let them p - TypeScript relaxed: Faster iteration, gradual adoption - Multiple languages: Right tool for each job -### 3. DATABASE +### 3. Database This decision affects everything downstream. @@ -73,7 +73,7 @@ This decision affects everything downstream. - In-memory: Simple apps, single instance - None: Start without, add when needed -### 4. AUTH & SECURITY +### 4. Auth & security Get this wrong and you're rebuilding later. @@ -85,7 +85,7 @@ Get this wrong and you're rebuilding later. - API keys: Service-to-service, developer platforms **Auth Library** -- Lucia: Flexible, self-hosted, good DX +- Better Auth: self-hosted TypeScript library that stores users and sessions in your own database, with built-in OAuth providers and plugins for 2FA and organizations - NextAuth/Auth.js: Quick setup, many providers - Clerk: Managed, beautiful UI, costs money - Supabase Auth: If using Supabase already @@ -120,7 +120,7 @@ Get this wrong and you're rebuilding later. - Data retention policies? - Encryption at rest/in transit? -### 5. API DESIGN +### 5. API design How frontend talks to backend. @@ -128,7 +128,7 @@ How frontend talks to backend. - REST: Universal, cacheable, well understood - tRPC: End-to-end type safety, Next.js native - GraphQL: Flexible queries, higher complexity -- Server Actions: Next.js 14+, simplest for forms +- Server Actions: Next.js 16+, simplest for forms **Validation** - Zod: TypeScript-first, great inference @@ -146,7 +146,7 @@ How frontend talks to backend. - Rate limiting requirements? - Documentation approach? -### 6. FRONTEND ARCHITECTURE +### 6. Frontend architecture **Styling** - Tailwind: Utility-first, consistent, great DX @@ -178,7 +178,7 @@ How frontend talks to backend. - Accessibility requirements? - i18n needed? -### 7. INFRASTRUCTURE +### 7. Infrastructure **Hosting** - Vercel: Best Next.js DX, preview deploys, costs scale @@ -210,7 +210,7 @@ How frontend talks to backend. - API subdomain? (api.example.com) - Separate marketing site? -### 8. INTEGRATIONS +### 8. Integrations **Email** - Resend: Best DX, React Email @@ -229,7 +229,7 @@ How frontend talks to backend. - AI/LLM (OpenAI, Anthropic, local models) - Analytics (PostHog, Mixpanel, Plausible) -### 9. CODE PATTERNS +### 9. Code patterns Establish conventions early. @@ -274,7 +274,7 @@ Decide here; the type is defined there, once. - Feature branches: `feature/add-auth` - Trunk-based: direct to main with feature flags -### 10. TESTING +### 10. Testing **Strategy** - Unit + Integration: Test business logic, API contracts diff --git a/skills/resolving-merge-conflicts/SKILL.md b/skills/resolving-merge-conflicts/SKILL.md index 0ca0fe9..489c205 100644 --- a/skills/resolving-merge-conflicts/SKILL.md +++ b/skills/resolving-merge-conflicts/SKILL.md @@ -1,19 +1,19 @@ --- name: resolving-merge-conflicts -description: "Resolve an in-progress git conflict without a silent drop. Use when the worktree is mid-merge, mid-rebase, mid-cherry-pick or mid-stash-pop: `CONFLICT (content)`, `<<<<<<<` markers, `Unmerged paths`, or `fix conflicts and then commit the result`." +description: "Resolve an in-progress git conflict without a silent drop. Use when the worktree is mid-merge, mid-rebase, mid-cherry-pick or mid-stash-pop: `CONFLICT (content)`, `<<<<<<<` markers, `Unmerged paths`, or `fix conflicts and then commit the result`. Also use when asked to resolve or fix merge conflicts on a PR or branch before any merge has started." --- -1. Orient before you touch anything. Establish from `git status` whether you are in a merge, a rebase, or a cherry-pick. Under a rebase `ours` is the upstream you are replaying onto and `theirs` is your own commit, the inverse of a merge, and getting this backwards is the most common wrong resolution. +1. Orient before you touch anything. Establish from `git status` whether you are in a merge, a rebase, a cherry-pick, or a stash pop. When none is in progress, merge the base branch into the head branch, then orient. Rebase only when the user asked for one, because rebasing a published branch rewrites its history. Under a rebase `ours` is the upstream you are replaying onto and `theirs` is your own commit, the inverse of a merge, and getting this backwards is the most common wrong resolution. Then split the conflicted files. Regenerate generated artifacts and lockfiles from source, never hand-merge them. That means `bun.lock`, `pnpm-lock.yaml`, `_generated/`, and snapshots. Only hand-written files go through steps 2-3. -2. Find the primary sources for each conflict. Understand why each change was made and what the original intent was. Read the commit messages, check the PRs, check original issues and tickets. +2. Find the primary sources for each conflict. Learn why each side made its change. Read the commit messages, check the PRs, check original issues and tickets. 3. Resolve each hunk against the primary sources, not against the markers. Preserve both intents. Where incompatible, pick the one matching the merge's stated goal and note the trade-off. Every line in the resolution traces to one side's original or to a mechanical combination of both. Before staging, diff your resolution against both parents for every conflicted file. Run `git diff --cc <file>`, or `git diff :2:<file> <file>` and `git diff :3:<file> <file>`. Place every hunk from both sides in exactly one bucket. The buckets are kept, superseded with a note naming what replaced it, or dropped with a note naming why. A hunk you cannot place is a silent drop, an unresolved conflict wearing a resolution. State the ledger before you commit. - Always resolve. When the two intents are genuinely incompatible and no stated goal decides between them, stop and put both candidate resolutions to the user rather than guessing. + Never abort the merge to dodge a hard hunk. When the two intents are genuinely incompatible and no stated goal decides between them, stop and put both candidate resolutions to the user rather than guessing. 4. Run the project's checks. `/done` owns this pipeline. Let it select the acceptance lanes, verify each one, assign states, and build its readiness card. Stop at that card and take none of the handoffs its final section ends in. Committing mid-merge concludes the merge with a generated conventional subject instead of the merge message, so `git-commit` and `file-pr` both wait for step 5. Fix anything the merge broke. diff --git a/skills/reuse-first/SKILL.md b/skills/reuse-first/SKILL.md index cde81e7..2f8ba47 100644 --- a/skills/reuse-first/SKILL.md +++ b/skills/reuse-first/SKILL.md @@ -7,7 +7,7 @@ description: "Search before you write, sweep before you finish. Use BEFORE creat Every duplicate is a future divergence. A bug fixed in one copy stays broken in the other. -Greenfield with no codebase to search yet. Run project-discovery first, then come back here. +With no codebase to search yet, run `project-discovery` first, then come back here. ## 3-layer search diff --git a/skills/reuse-first/references/artifact-guide.md b/skills/reuse-first/references/artifact-guide.md index c8692a3..6069735 100644 --- a/skills/reuse-first/references/artifact-guide.md +++ b/skills/reuse-first/references/artifact-guide.md @@ -28,5 +28,5 @@ Reach this section only after all three layers have returned. - **Two unrelated domains** that happen to look similar. DRY is about shared knowledge, not shared appearance. A `userId` validator and a `productId` validator can both be `z.string().uuid()` independently. Coupling them creates fake polymorphism -- **Premature abstraction.** Three similar lines are fine. Abstract on the fourth occurrence, not the second +- **Premature abstraction.** Three similar occurrences are fine. Abstract on the fourth. - **Tests.** Explicit setup in each test often beats shared fixtures that hide what is being tested diff --git a/skills/reuse-first/references/sweep-mode.md b/skills/reuse-first/references/sweep-mode.md index 4c7a6e6..4fd7dba 100644 --- a/skills/reuse-first/references/sweep-mode.md +++ b/skills/reuse-first/references/sweep-mode.md @@ -39,6 +39,8 @@ git diff -U0 "$BASE" \ Do not hand-roll a check for near-identical function bodies. A grep over declaration lines dedupes on text that contains the name, so two helpers with different names, the case worth finding, can never collide, and the check quietly reports nothing while looking like it ran. Structural similarity needs a clone detector. That is what `jscpd` above is for. +`jscpd` also skips repeated sequences shorter than its minimum clone size. So for every function or API the diff calls at 3 or more sites, read the lines around each call with `printf '%s\n' "$FILES" | tr '\n' '\0' | xargs -0 grep -n -C3 -F '<callee>('` and compare them across sites. The same 3-line sequence at every site, for example a set-state call followed by a notify call at 7 call sites, is one helper the sweep should report. + Commands here assume GNU-compatible `grep`. `ugrep`, `busybox` and BSD `grep` differ on bracket classes and `--exclude` ordering, so if a check returns suspiciously zero, verify it finds a planted duplicate before trusting it. Then ask, per hit: diff --git a/skills/review-pr/SKILL.md b/skills/review-pr/SKILL.md index baf43a5..1d70652 100644 --- a/skills/review-pr/SKILL.md +++ b/skills/review-pr/SKILL.md @@ -5,40 +5,38 @@ description: Review a GitHub PR, then automatically request changes for any find # /review-pr: Deep GitHub PR Review -Reviews a remote GitHub PR with anti-slop filtering. It takes a PR URL and nothing else. +Reviews a remote GitHub PR from its URL, drops noise (style nitpicks, hallucinated references, duplicates, generic advice), and submits the review to GitHub. Another author's PR gets `REQUEST_CHANGES` or `APPROVE`; a self-review gets `COMMENT` because GitHub forbids authors from approving their own PRs. -Goal: produce an accurate, critical, actionable PR review, filter out noise (style nitpicks, hallucinated references, duplicates, generic advice), and submit the result to GitHub. Reviews of another author's PR use `REQUEST_CHANGES` or `APPROVE`; self-reviews use `COMMENT` because GitHub forbids authors from approving their own PRs. - -**Cascade** is the failure this review is built to prevent: a fix shipped for round N's finding becomes round N+1's finding. Two things feed it: the suggested fix carries a defect of its own, and the fix lands on the cited site while identical sibling sites go untouched. So every finding proposing a code change carries an `Inverse risk:` and a `Class-sites:` count, one field per feeder. Phase 3 measures the result as `cascade_share` at step 7.5, the verdict at step 8 reads it to say whether the PR is converging, and Phase 4 prints it. +**Cascade** means a fix shipped for round N's finding becomes round N+1's finding. It has two causes: the suggested fix carries its own defect, or the fix lands on the cited site and misses identical sibling sites. Every finding that proposes a code change therefore carries one field per cause, `Inverse risk:` and a `Class-sites:` count. Phase 3 measures the result as `cascade_share` at step 7.5, step 8's verdict reads it to say whether the PR is converging, and Phase 4 prints it. This skill expects CodeRabbit on the repo via `.coderabbit.yaml`. CodeRabbit handles style and convention findings first. `/review-pr` handles the deep semantic and codebase-wide review only it can do. -**Use AskUserQuestion for user-facing decisions that still require judgment**: stop-and-ask and post-failure recovery. Posting is not a decision point. Invoking `/review-pr` authorizes submission of the complete review. Large PRs always proceed with chunked review without asking. Any sentence that offers the user 2+ labeled paths is an AskUserQuestion call. Options are cursor-selectable, concrete, and considered. Put the strongest first and mark it "(Recommended)". In an unattended run (the AskUserQuestion tool is unavailable, or the invoking prompt says nobody can answer), take the first option of each question this skill defines and print which one you took. Step 7's pending-review recovery is the exception: it follows its own unattended rule, which never deletes a review. +Use AskUserQuestion for two decisions only: stop-and-ask and post-failure recovery. Posting is not a decision point. Invoking `/review-pr` authorizes submission of the complete review. Large PRs always proceed with chunked review without asking. Any sentence that offers the user 2+ labeled paths is an AskUserQuestion call. Options are cursor-selectable, concrete, and considered. Put the strongest first and mark it "(Recommended)". In an unattended run (the AskUserQuestion tool is unavailable, or the invoking prompt says nobody can answer), take the first option of each question this skill defines and print which one you took. The pending-review recovery in Step 7 of `github-posting-recovery.md` is the exception: it follows its own unattended rule, which never deletes a review. ## Reference files Main or a subagent loads each file only on the branch that reaches it. Loader and firing condition: -- `references/batch-mode.md`: orchestration rules, "don't stop" semantics, consolidated-report template, and automatic posting sequence. Loaded by **main** at Phase 1 when the user gives 2+ PR URLs or asks for all open PRs. -- `references/reviewer-prompt.md`: the whole Subagent 1 prompt, the anti-slop rules it works under, and the note on why the finding shape is not restated inside it. Loaded by **main** at the Phase 2 dispatch on every `SIZE_MODE` branch. -- `references/cross-cutting-prompt.md`: the whole Subagent 3 prompt. Loaded by **main** at the Phase 2 dispatch when `SIZE_MODE` is `parallel-chunked`; the unchunked modes never dispatch Subagent 3. -- `references/security-prompt.md`: the whole Subagent 4 prompt. Loaded by **main** at the Phase 2 dispatch when `INCLUDE_SECURITY_REVIEWER = true`, in every `SIZE_MODE`. -- `references/q5-type-coercion.md`: the Q5 type-coercion scan: coercion methods, how to decide a field is numeric, severity. Loaded by **Subagent 1** while answering Q5, and by **Subagent 4**, when the diff contains a DB insert/update or an API payload construction. -- `references/class-sweep-and-inverse-risk.md`: reviewer-prompt steps 5 and 6: blast-radius search order, the `class_completeness:` and `Inverse risk:` field rules, the worked inverse-risk examples. Loaded by **Subagent 1** as soon as any finding proposes a code change. -- `references/repo-map.md`: the `repo_map_files` / `repo_map_exports` shell, local and cross-repo modes. The one copy in the repo; `/fix-pr-review` loads it from here too. Loaded by **main** in Phase 1 when `packages/` or `apps/` exists. -- `references/q6-reusability-search.md`: STEP A enumeration + STEP B search algorithm + Q6 control-flow gap. Loaded by **Subagent 1** when the diff has 1+ new definitions of any STEP A kind (not top-level only). The cross-repo search block lives in `references/q6-cross-repo.md`, loaded only when `repo_map_exports` is the cross-repo `N/A` marker. -- `references/finding-output-format.md`: the per-finding field block, the `class_completeness:` audit shape, and the run-level closing block. The one copy of the finding shape. Loaded by **Subagent 1**, **Subagent 3**, **Subagent 4** and **V3** before they write any finding. -- `references/schema-design-checks.md`: Q7 (overlap), Q8 (1:1 consolidation), Q9 (cross-table FK) checks. Loaded by **Subagent 1** when `INCLUDE_SCHEMA_CHECKS = true`, and by **V3** when the gap check covers Q7-Q9. -- `references/verification-subagents.md`: V1/V2/V3 dispatch conditions + the exact prompt each is given. Loaded by **main** in Phase 3 at the first of steps 4.55 / 4.9 / 6 that fires. -- `references/false-positive-rules.md`: the four-rule YAML table (`wrapped-coercion`, `intent-alignment`, `library-behavior-citation`, `default-fallback`) each surviving finding is run through. Loaded by **main** at Phase 3 step 4.6 when any finding survives step 4.5. -- `references/finding-state-schema.md`: both persistence files: `.claude/review-state/<pr>.yml` (schema, finding-ID strategy, state machine, Phase 1 read path) and the run-over-run cache (schema + the three replay branches). Loaded by **main** in Phase 1 before the review-state read and the cache check, and held through Phase 3. The Phase 4 write-back and sweep live in `${CLAUDE_SKILL_DIR}/references/finding-state-phase4.md`, loaded in Phase 4 after posting, plus on Phase 1 startup when the state directory is non-empty. -- `references/github-posting.md`: fresh-review REST/GraphQL posting flow: rendering every body with `scripts/render_review.py`, Phases A through C, and the write-back. Loaded by **main** in Phase 4 for every completed review. Re-runs with a prior review or cache entry also load `${CLAUDE_SKILL_DIR}/references/github-posting-rerun.md` (Steps 0, 0b, 0c, 4-rolling, 8d); a posting failure also loads `${CLAUDE_SKILL_DIR}/references/github-posting-recovery.md` (Step 7). -- `references/phase1-timeline-state.md`: prior-review thread query plus shaping, the review-state read plus round seeding, and the run-over-run cache branches. Loaded by **main** in Phase 1 at the timeline, state, and cache steps. -- `references/dispatch-prompts.md`: the `<SKILL_DIR>` derivation, the `<PROMPT_PREAMBLE>` and `<GROUND_TRUTH>` blocks, and the silent-failure hunter context packet. Loaded by **main** at the Phase 2 dispatch. -- `references/critic-verify.md`: critic steps 1 through 4.57, dedupe through origin attribution. Loaded by **main** at Phase 3 step 1, kept through step 4.57, reloaded at step 6 for routed findings. -- `references/critic-round2.md`: critic steps 4.9, 4.95 and 4.96, regression sweep plus suppression plus lineage. Loaded by **main** at Phase 3 step 4.9, only from round 2 on. - -Four closures, so the accounting is deterministic. Main never loads `finding-output-format.md`: only Subagent 1, Subagent 3, Subagent 4, and V3 load it, before writing findings. V1 and V2 load no references: they report compact verdicts from finding text. Main never loads `schema-design-checks.md` or `q5-type-coercion.md`: Subagent 1 loads each under its own condition, Subagent 4 loads the coercion scan under the same condition, and V3 loads the schema checks only when the gap check covers Q7-Q9. The anti-slop rules live in `reviewer-prompt.md` alone: no dispatch pastes a second copy into any prompt. +- `references/batch-mode.md`: orchestration rules, "don't stop" semantics, consolidated-report template, and automatic posting sequence. Loaded by main at Phase 1 when the user gives 2+ PR URLs or asks for all open PRs. +- `references/reviewer-prompt.md`: the whole Subagent 1 prompt, the anti-slop rules it works under, and the note on why the finding shape is not restated inside it. Loaded by main at the Phase 2 dispatch on every `SIZE_MODE` branch. +- `references/cross-cutting-prompt.md`: the whole Subagent 3 prompt. Loaded by main at the Phase 2 dispatch when `SIZE_MODE` is `parallel-chunked`; the unchunked modes never dispatch Subagent 3. +- `references/security-prompt.md`: the whole Subagent 4 prompt. Loaded by main at the Phase 2 dispatch when `INCLUDE_SECURITY_REVIEWER = true`, in every `SIZE_MODE`. +- `references/q5-type-coercion.md`: the Q5 type-coercion scan: coercion methods, how to decide a field is numeric, severity. Loaded by Subagent 1 while answering Q5, and by Subagent 4, when the diff contains a DB insert/update or an API payload construction. +- `references/class-sweep-and-inverse-risk.md`: reviewer-prompt steps 5 and 6: blast-radius search order, the `class_completeness:` and `Inverse risk:` field rules, the worked inverse-risk examples. Loaded by Subagent 1 as soon as any finding proposes a code change. +- `references/repo-map.md`: the `repo_map_files` / `repo_map_exports` shell, local and cross-repo modes. The one copy in the repo; `/fix-pr-review` loads it from here too. Loaded by main in Phase 1 when `packages/` or `apps/` exists. +- `references/q6-reusability-search.md`: STEP A enumeration + STEP B search algorithm + Q6 control-flow gap. Loaded by Subagent 1 when the diff has 1+ new definitions of any STEP A kind (not top-level only). The cross-repo search block lives in `references/q6-cross-repo.md`, loaded only when `repo_map_exports` is the cross-repo `N/A` marker. +- `references/finding-output-format.md`: the per-finding field block, the `class_completeness:` audit shape, and the run-level closing block. The one copy of the finding shape. Loaded by Subagent 1, Subagent 3, Subagent 4 and V3 before they write any finding. +- `references/schema-design-checks.md`: Q7 (overlap), Q8 (1:1 consolidation), Q9 (cross-table FK) checks. Loaded by Subagent 1 when `INCLUDE_SCHEMA_CHECKS = true`, and by V3 when the gap check covers Q7-Q9. +- `references/verification-subagents.md`: V1/V2/V3 dispatch conditions + the exact prompt each is given. Loaded by main in Phase 3 at the first of steps 4.55 / 4.9 / 6 that fires. +- `references/false-positive-rules.md`: the four-rule YAML table (`wrapped-coercion`, `intent-alignment`, `library-behavior-citation`, `default-fallback`) each surviving finding is run through. Loaded by main at Phase 3 step 4.6 when any finding survives step 4.5. +- `references/finding-state-schema.md`: both persistence files: `.claude/review-state/<pr>.yml` (schema, finding-ID strategy, state machine, Phase 1 read path) and the run-over-run cache (schema + the three replay branches). Loaded by main in Phase 1 before the review-state read and the cache check, and held through Phase 3. The Phase 4 write-back and sweep live in `${CLAUDE_SKILL_DIR}/references/finding-state-phase4.md`, loaded in Phase 4 after posting, plus on Phase 1 startup when the state directory is non-empty. +- `references/github-posting.md`: fresh-review REST/GraphQL posting flow: rendering every body with `scripts/render_review.py`, Phases A through C, and the write-back. Loaded by main in Phase 4 for every completed review. Re-runs with a prior review or cache entry also load `${CLAUDE_SKILL_DIR}/references/github-posting-rerun.md` (Steps 0, 0b, 0c, 4-rolling, 8d); a posting failure also loads `${CLAUDE_SKILL_DIR}/references/github-posting-recovery.md` (Step 7). +- `references/phase1-timeline-state.md`: prior-review thread query plus shaping, the review-state read plus round seeding, and the run-over-run cache branches. Loaded by main in Phase 1 at the timeline, state, and cache steps. +- `references/dispatch-prompts.md`: the `<SKILL_DIR>` derivation, the `<PROMPT_PREAMBLE>` and `<GROUND_TRUTH>` blocks, and the silent-failure hunter context packet. Loaded by main at the Phase 2 dispatch. +- `references/critic-verify.md`: critic steps 1 through 4.57, dedupe through origin attribution. Loaded by main at Phase 3 step 1, kept through step 4.57, reloaded at step 6 for routed findings. +- `references/critic-round2.md`: critic steps 4.9, 4.95 and 4.96, regression sweep plus suppression plus lineage. Loaded by main at Phase 3 step 4.9, only from round 2 on. + +Main never loads `finding-output-format.md`, `schema-design-checks.md`, or `q5-type-coercion.md`; the loaders named above do. V1 and V2 load no references: they report compact verdicts from finding text. The anti-slop rules live in `reviewer-prompt.md` alone, and no dispatch pastes a second copy into any prompt. ## Planning-doc grounding (optional pre-review context) @@ -54,13 +52,13 @@ If no URL is provided, ask the user for one. Bare `gh` commands infer a PR from ## Batch mode (multiple PRs) -Fires when the user provides **2+ PR URLs** or asks to review **all open PRs**. A single-PR run skips this entirely and drops straight into Phase 1. On that branch, load `${CLAUDE_SKILL_DIR}/references/batch-mode.md` before doing anything else: it holds the PR enumeration, the orchestration rules (one subagent per PR, main never reviews inline, subagents never post or ask), the "don't stop" semantics for review-only checkpoints, the consolidated-report template, and the automatic posting sequence. +Fires when the user gives 2+ PR URLs or asks to review all open PRs; a single-PR run goes straight to Phase 1. On that branch, load `${CLAUDE_SKILL_DIR}/references/batch-mode.md` before anything else. Its core rule: one subagent per PR, main never reviews inline, and subagents never post or ask. --- ## Phase 1: Gather context (main) -Run these as **two separate Bash tool calls in a single assistant message**. True parallelism needs separate tool_use blocks, not `&&` chained. +Run these as two separate Bash tool calls in one assistant message, not one `&&` chain, so they run in parallel. ```bash gh pr view <url> --json number,title,body,author,baseRefName,headRefName,headRefOid,baseRefOid,additions,deletions,changedFiles,files,closingIssuesReferences,reviews,comments,state,isDraft @@ -71,7 +69,7 @@ Record `CURRENT_HEAD` from `headRefOid`, `PINNED_BASE_OID` from `baseRefOid`, an Issue the independent reads below as parallel Bash tool calls in a single assistant message: viewer login, linked-issue bodies, review threads, cwd repo, CodeRabbit config, and suppressions. They depend on nothing but the PR URL and owner/repo, so sequencing them is pure wall time. -Phase 1 fetches the **full diff**. Stash it in main context. The error-handling content scan and the Phase 3 critic reference check both need it. +Phase 1 fetches the full diff. Stash it in main context. The error-handling content scan and the Phase 3 critic reference check both need it. ### Empty-diff short-circuit @@ -121,11 +119,6 @@ Draft: <yes|no> Pull every review thread and read the saved state per `${CLAUDE_SKILL_DIR}/references/phase1-timeline-state.md`. Load it now. It holds the thread query, the prior-findings shape, the state-file read plus round seeding, and the cache branches. What it builds feeds Phase 3 dedupe and the round-2 regression sweep. -### Load review-state (multi-round dedup) - -Covered by the reference loaded above. - - ### Stop-and-ask fallback Trigger if **no linked issue** AND the PR description **lacks all** of these: @@ -211,7 +204,7 @@ If `PRIOR_STATE.convergence` exists, invoke `converge-reviews` with the current ### Compute shared-package repo map (for Q6) -If `CROSS_REPO_MODE=true`, load `${CLAUDE_SKILL_DIR}/references/repo-map.md` and run the cross-repo block unconditionally: the target repository's layout decides, not the local cwd's. Otherwise, if `packages/` or `apps/` exists, load `${CLAUDE_SKILL_DIR}/references/repo-map.md` and run the block for the mode you are in: it holds both shell blocks (the cross-repo `gh api` tree fetch and the local `bash -c` find/grep pair, each truncating at 500 lines) and writes `repo_map_files` + `repo_map_exports` to the staged `repo-map.md` for the subagent prompts. It is the one copy of that shell, shared with `/fix-pr-review`. +If `CROSS_REPO_MODE=true`, load `${CLAUDE_SKILL_DIR}/references/repo-map.md` and run the cross-repo block unconditionally: the target repository's layout decides, not the local cwd's. Otherwise, if `packages/` or `apps/` exists, load `${CLAUDE_SKILL_DIR}/references/repo-map.md` and run its local block. Either block writes `repo_map_files` + `repo_map_exports` to the staged `repo-map.md` for the subagent prompts. If neither directory exists in local mode, skip the shell. Set both to `N/A (not a monorepo)` and flag `IS_MONOREPO=false`. Subagent 1 reroutes greps to the changed files' directories, or the repository root when those reveal nothing. Never assume `src/`. @@ -309,7 +302,7 @@ Launch in a **single message with multiple Agent tool calls** based on `SIZE_MOD If any subagent errors out or returns empty, continue with the remaining ones and note `<reviewer> unavailable` in the Phase 4 output header. Abort only if all fail. -**Note on CodeRabbit.** CodeRabbit findings arrive through the prior-review timeline in Phase 1, pulled from the PR existing comments when the repo has `.coderabbit.yaml`. See the one-time hint above. No CodeRabbit subagent goes out. If the PR latest commit has no CodeRabbit comment yet, Subagent 1 carries the load and the next round picks up the CodeRabbit input. +**CodeRabbit input.** CodeRabbit findings reach this review only through the Phase 1 prior-review timeline, which reads the PR's existing comments when the repo has `.coderabbit.yaml`. See the one-time hint above. No CodeRabbit subagent runs. If CodeRabbit has not commented on the latest commit yet, Subagent 1 reviews without that input and the next round picks it up. ### Subagent 1: Claude reviewer (`general-purpose`) @@ -365,7 +358,7 @@ Everything else runs inline. A verification subagent reports evidence in a compa structured verdict; main rules on severity, on drops, and on the state file. Execute in order: -Steps 1 through 4.56 run per `${CLAUDE_SKILL_DIR}/references/critic-verify.md`. Load it at step 1 and keep it through step 4.56, then reload it at step 6 for findings routed back through 4.55 and 4.56. Steps 4.9, 4.95 and 4.96 run per `${CLAUDE_SKILL_DIR}/references/critic-round2.md`, loaded only from round 2 on. +Steps 1 through 4.57 run per `${CLAUDE_SKILL_DIR}/references/critic-verify.md`. Load it at step 1 and keep it through step 4.57, then reload it at step 6 for findings routed back through 4.55, 4.56 and 4.57. Steps 4.9, 4.95 and 4.96 run per `${CLAUDE_SKILL_DIR}/references/critic-round2.md`, loaded only from round 2 on. ### 1. Dedupe @@ -410,7 +403,7 @@ A unified iterator over a rules table. Each rule has: `id`, `trigger` (regex mat Apply each rule in order. Log each fire to Filtered Out with the rule `id` + reason. -The rules themselves, the four-rule YAML table with every `trigger` regex and `evidence_check` body, live in `references/false-positive-rules.md`. Load it here whenever at least one finding survives step 4.5; skip it when the finding list is empty. That table is the single source of truth for false-positive filtering: adding a new false-positive class is a one-row YAML edit there, not a new prose section here. +The rules themselves, the four-rule YAML table with every `trigger` regex and `evidence_check` body, live in `references/false-positive-rules.md`. Load it here whenever at least one finding survives step 4.5; skip it when the finding list is empty. ### 4.9. Proactive regression sweep (runs before prior-state suppression, 4.95) @@ -439,18 +432,16 @@ If `SUPPRESSIONS` was loaded in Phase 1, match each remaining finding: If ALL specified conditions match: DROP, log `suppressed by .claude/review-suppressions.yml: "<reason>" (pattern: "<pattern>")`. -**Critical/Serious override**: suppressions drop findings at any severity. A team that explicitly decided a pattern is acceptable outranks the review, and `reason` keeps the drop auditable. +Suppressions drop findings at every severity, Critical and Serious included. A team that explicitly accepted a pattern outranks the review, and `reason` keeps the drop auditable. ### 6. Gap check (Q1-Q6, Q7-Q9 if schema PR) -For any question category where Subagent 1 said nothing, briefly think about whether the diff has anything in that category. Add findings if you spot misses. Include Q7-Q9 only if `INCLUDE_SCHEMA_CHECKS = true`. +For each question category where Subagent 1 reported nothing, check the diff for that category and add any finding it shows. Include Q7-Q9 only if `INCLUDE_SCHEMA_CHECKS = true`. **Large-PR routing.** If `additions + deletions >= 500` and main lacks the full diff, -route this check to **V3: Deep gap check** and fold its findings in here. V3 has the -context budget to answer from the diff itself, where main would be guessing from a -file list. Pass V3 `INCLUDE_SCHEMA_CHECKS` and `SCHEMA_DIR`. It is dispatched precisely -on the large PRs where schema changes live, so dropping the flag drops Q7-Q9 exactly -where they are most likely to fire. +route this check to V3 (deep gap check) and fold its findings in here. Pass V3 +`INCLUDE_SCHEMA_CHECKS` and `SCHEMA_DIR`; without them V3 skips Q7-Q9 on the large PRs +most likely to change schemas. **Re-run the cascade gates on everything this step adds.** Reload `${CLAUDE_SKILL_DIR}/references/critic-verify.md` for this routing. Findings created here, main's own and V3's alike, arrive after steps 4.55, 4.56, 4.57 and 4.96 have already run, so they @@ -475,9 +466,8 @@ Critical > Serious > Moderate > Minor. ### 7.5. Compute `cascade_share` -The ONE place this ratio is computed. Ranking is done, the finding set is final, and every -finding carries a `caused_by` from step 4.9 or 4.96, so this is the first point where the -number is both computable and stable. +Compute this ratio here and nowhere else. After ranking, the finding set is final, and every +finding carries a `caused_by` from step 4.9 or 4.96. At `CURRENT_ROUND == 1` there is no prior round to attribute to: set `cascade_share = 0`, skip the trend sentence, and move on. @@ -490,12 +480,12 @@ cascade_share: <count of active introduced findings with a resolvable caused_by> Pre-existing findings stay out of both counts. No fix in this PR wrote their lines. -A `caused_by` counts only when its target finding is still present. Dangling ids are excluded, per `references/finding-state-schema.md` (`caused_by` cardinality), which owns that rule. +A `caused_by` counts only when its target finding is still present (`references/finding-state-schema.md`, `caused_by` cardinality). Zero active introduced findings → `cascade_share = 0`, not a division by zero. -Step 8 below reads this value for the verdict prefix, and Phase 4's **Cascade check** -prints it. Neither recomputes it: one number, one definition, one round. +Step 8 reads this value for the verdict prefix and Phase 4's Cascade check prints it. +Neither recomputes it. ### 8. Decide verdict @@ -593,7 +583,7 @@ Threaded findings over surviving findings, printed after the posting/recovery ou ### Cascade check -Mandatory from round 2. PRINT the step 7.5 value. Do not recompute it here: Step 8 already read that same number for the verdict prefix, and a second computation on a different finding set is how the two disagree. +Mandatory from round 2. Print the step 7.5 value unchanged; never recompute it here. Emit exactly one trend sentence, picked from what the numbers say: - `cascade_share > 0.5` → `Not converging, because the fixes are generating the findings.` @@ -626,7 +616,7 @@ Total: <s> ### Post to GitHub -An explicit `/review-pr <PR URL>` invocation is fresh authorization to submit the complete review to that exact PR. The authorization remains valid only while the target PR, head SHA, semantic verdict, GitHub event, and frozen payload match the later mutation card. Apply `preflight-mutations` normally and block on any non-ready verdict; do not bypass it or ask the user to select findings, confirm posting, keep the review local, edit the body, or choose a next action. +An explicit `/review-pr <PR URL>` invocation is fresh authorization to submit the complete review to that exact PR. The authorization remains valid only while the target PR, head SHA, semantic verdict, GitHub event, and frozen payload match the later mutation card. Apply `preflight-mutations` and block on any non-ready verdict; do not bypass it or ask the user to select findings, confirm posting, keep the review local, edit the body, or choose a next action. - When `IS_SELF_REVIEW=false`, submit every surviving finding as an individual review comment with `REQUEST_CHANGES`; submit a clean review with no open blockers with `APPROVE` and no review comments; submit a clean review with open blockers on the blocked-by-open-threads path (Phase 3 step 8). A review whose only surviving findings are pre-existing counts as clean, and its `APPROVE` carries those findings as threads. - When `IS_SELF_REVIEW=true`, submit the same complete finding set or clean summary with `COMMENT`. Keep the semantic verdict in the body and terminal output. diff --git a/skills/review-pr/references/batch-mode.md b/skills/review-pr/references/batch-mode.md index c551487..64276e7 100644 --- a/skills/review-pr/references/batch-mode.md +++ b/skills/review-pr/references/batch-mode.md @@ -11,6 +11,7 @@ For "all open PRs", enumerate via `gh pr list --json number,url,title --limit 50 - Main context is the **orchestrator**: oversight only. It never reviews a PR inline, regardless of `SIZE_MODE` (size routing applies inside each subagent, not in main). - Spawn **ONE `general-purpose` subagent PER PR**. Each subagent runs the single-PR flow (Phases 1-3) independently against its own PR and returns its Phase 4 terminal block as its result. Dispatch in parallel batches of 3-4. - Subagents NEVER post to GitHub and NEVER ask questions. They return the complete surviving finding set, semantic verdict, and `IS_SELF_REVIEW` value to the orchestrator, which posts each review after every subagent has returned. +- When any nested dispatch from a per-PR subagent fails, it runs inline, in order, every lens its `SIZE_MODE` and flags would have dispatched: Subagent 1, Subagent 2 when `INCLUDE_SILENT_FAILURE_HUNTER = true`, Subagent 3 only in `parallel-chunked`, Subagent 4 when `INCLUDE_SECURITY_REVIEWER = true`, then the Phase 3 verifier checks. Its Timing block writes `Reviewers: inline (host blocks nested dispatch)` and marks each lens `run` or `skipped (<reason>)`. When a required lens could not run, the orchestrator posts `COMMENT` instead of `APPROVE` and names that lens and the reason in the body. ### `<SKILL_DIR>` on the batch branch @@ -48,7 +49,7 @@ The run continues unattended through the WHOLE list. The user may be away in bat ## Automatic posting -After every subagent returns, post each completed review in list order through the single-PR GitHub posting flow. For another author's PR, post a review with surviving introduced findings with `REQUEST_CHANGES`, a clean review with no open blockers with `APPROVE` (its pre-existing findings ride along as threads), and a clean review with open blockers on the blocked-by-open-threads path (Phase 3 step 8). For a self-review, post the complete result with `COMMENT`. Invoke `preflight-mutations` separately for each PR immediately before its first mutation, using the batch `/review-pr` request as the authorization source. Reconcile and record each result before moving to the next PR. Record each PR's reconciled `https://github.com/<owner>/<repo>/pull/<num>#pullrequestreview-<REVIEW_DB_ID>` (or `not posted (<reason>)` when nothing landed) and carry it into that PR's `**Review URL**` line and the consolidated posting-status entry. +After every subagent returns, post each completed review in list order through the single-PR GitHub posting flow. For another author's PR, post a review with surviving introduced findings with `REQUEST_CHANGES`, a clean review with no open blockers with `APPROVE` (its pre-existing findings ride along as threads; `COMMENT` when a required lens did not run), and a clean review with open blockers on the blocked-by-open-threads path (Phase 3 step 8). For a self-review, post the complete result with `COMMENT`. Invoke `preflight-mutations` separately for each PR immediately before its first mutation, using the batch `/review-pr` request as the authorization source. Reconcile and record each result before moving to the next PR. Record each PR's reconciled `https://github.com/<owner>/<repo>/pull/<num>#pullrequestreview-<REVIEW_DB_ID>` (or `not posted (<reason>)` when nothing landed) and carry it into that PR's `**Review URL**` line, its table row, and the consolidated posting-status entry. A posting failure never asks immediately, and the `SKILL.md` unattended rule applies here only when its own trigger fires. Record the exact partial GitHub state and the recovery choices from `${CLAUDE_SKILL_DIR}/references/github-posting-recovery.md` as `recovery-pending`, then continue with every untouched PR. @@ -61,9 +62,9 @@ After automatic posting has attempted every completed review and recorded each c ``` # Batch PR Review: <N> PRs (<date>) -| PR | Title | Approval | Verdict | C | S | M | m | -|----|-------|----------|---------|---|---|---|---| -<one row per PR; "review failed" rows included> +| PR | Title | Approval | Verdict | Reviewers | Review URL | C | S | M | m | +|----|-------|----------|---------|-----------|------------|---|---|---|---| +<one row per PR; "review failed" rows included; Reviewers is `dispatched` or `inline`, plus any skipped lens> ## Posting status <one entry per PR: posted request-changes + review URL, posted approve + review URL, posted self-review comment + review URL, review failed, posting failed, or recovery-pending> diff --git a/skills/review-pr/references/q6-reusability-search.md b/skills/review-pr/references/q6-reusability-search.md index b2ac6bb..2386c65 100644 --- a/skills/review-pr/references/q6-reusability-search.md +++ b/skills/review-pr/references/q6-reusability-search.md @@ -1,6 +1,6 @@ # Q6a Reusability: search algorithm and audit format -**Subagent 1** loads this when the diff has 1+ new definitions of any STEP A kind below, for STEP A onward. The main SKILL.md keeps only the Q6a header and reporting format. This reference explains HOW to search. +Subagent 1 loads this when the diff has 1+ new definitions of any STEP A kind below, for STEP A onward. STEP B searches against `repo_map_files` / `repo_map_exports`, which main computes in Phase 1. That shell is in `${CLAUDE_SKILL_DIR}/references/repo-map.md`, the copy `/fix-pr-review` shares. diff --git a/skills/review-pr/references/schema-design-checks.md b/skills/review-pr/references/schema-design-checks.md index 4d261c0..0fbc962 100644 --- a/skills/review-pr/references/schema-design-checks.md +++ b/skills/review-pr/references/schema-design-checks.md @@ -2,15 +2,13 @@ Loaded by Subagent 1 when `INCLUDE_SCHEMA_CHECKS = true`, and by V3 when the Phase 3 gap check covers Q7-Q9 (PR adds new database tables, detected in Phase 1 by grepping the diff for `pgTable\(`, `createTable\(`, `CREATE TABLE`, `knex.schema.createTable`, etc.). -The main SKILL.md keeps a 5-line conditional that loads this file. Loading it inline on every run wastes context on non-schema PRs. - `SCHEMA_DIR` is set in Phase 1, typically `db/schema/`, `drizzle/schema/`, `src/schema/`, or `migrations/`. If unset, defaults to `.` (repo root) and limits searches to files matching the detected table-definition pattern. --- ## Q7. Schema Overlap -**Question**. Does a new table duplicate an existing table's domain? +Question: does a new table duplicate an existing table's domain? For each new `pgTable()` (or equivalent) definition in the diff: @@ -34,7 +32,7 @@ Substring matches alone (e.g., `item` matching many unrelated tables) are NOT su ## Q8. Table Consolidation -**Question**. Could a simple 1:1 table be a column on an existing table? +Question: could a simple 1:1 table be a column on an existing table? For each new table definition in the diff, detect where: @@ -58,7 +56,7 @@ d. **Flag ONLY** if a candidate settings/config table EXISTS. Do NOT suggest "cr ## Q9. Cross-Table Field Consistency -**Question**. Are entity reference columns complete? +Question: are entity reference columns complete? When a new table has entity reference FKs (to `orderTable`, `productTable`, `invoiceTable`, `customerTable`, etc.): diff --git a/skills/simplify/SKILL.md b/skills/simplify/SKILL.md index c2b9748..47af1f5 100644 --- a/skills/simplify/SKILL.md +++ b/skills/simplify/SKILL.md @@ -5,13 +5,13 @@ description: Reduce unnecessary complexity in a completed code or workflow diff # Simplify -I make the smallest behavior-preserving cleanup that leaves the finished change easier to maintain. Less code, same behavior. That is the whole job. +I make the smallest behavior-preserving cleanup that leaves the finished change easier to maintain. Less code, same behavior. ## 1. Bind the change I record the originating request, review baseline, changed paths, and current diff or content hash. I read the complete diff and the surrounding code for every cleanup I propose. -The cleanup scope is the current task diff. Unrelated pre-existing code stays untouched, and I say so as the gate. I do not wander. +The cleanup scope is the current task diff. I leave unrelated pre-existing code untouched and state that as the gate. ## 2. Find removable complexity diff --git a/skills/standup/SKILL.md b/skills/standup/SKILL.md index f9c3793..c98900a 100644 --- a/skills/standup/SKILL.md +++ b/skills/standup/SKILL.md @@ -5,13 +5,13 @@ description: "Generate the user's daily standup update from real activity: GitHu # Standup -Produce a short, spoken-ready standup update from real activity. Never invent items. A gathering script collects the raw material. The script only gathers. The summary is the skill's job. +Produce a short standup update, ready to read aloud, from real activity. Never invent items. A script gathers the raw material and does nothing else. You write the summary. ## Step 0: Confirm the day scope -Propose the smart default, then ask the user to confirm. One question, three choices: today, previous workday, or both. Monday reaches back to Friday. A weekend run covers since Friday. Late-night work past midnight belongs to the standup that has not happened yet. +Propose the default, then ask the user to confirm. One question, three choices: today, previous workday, or both. Monday reaches back to Friday. A weekend run covers since Friday. Work past midnight belongs to the standup that has not happened yet. -Translate the confirmed scope into explicit bounds. A single named day means that day's full cycle, 10:00 that day to 10:00 the next, so its late-night work stays attached. +Turn the confirmed scope into explicit bounds. A single named day means its full cycle, 10:00 that day to 10:00 the next, so its late-night work stays attached. ## Step 1: Gather @@ -24,33 +24,33 @@ python3 "${CLAUDE_SKILL_DIR}/scripts/gather.py" --start 2026-09-04 python3 "${CLAUDE_SKILL_DIR}/scripts/gather.py" --org myorg --repo-root /path/to/repo ``` -A bare `YYYY-MM-DD` means 10:00 local that day. Use full ISO only for sub-day precision. `--org` overrides the auto-detected org from the git remote. `--repo-root` defaults to the current directory. The local scan covers that repo and all its git worktrees. `--no-github`, `--no-local`, and `--no-sessions` drop a source when asked. The `window` object in the output states exactly what was covered. Echo that range in the header. +A bare `YYYY-MM-DD` means 10:00 local that day. Use full ISO only for sub-day precision. `--org` overrides the org the script detects from the git remote. `--repo-root` defaults to the current directory, and the local scan covers that repo and all its git worktrees. `--no-github`, `--no-local`, and `--no-sessions` drop a source when asked. The `window` object in the output states the exact range covered. Echo that range in the header. -Session history is best-effort across Claude, Codex, and OpenCode. Missing stores are skipped silently. Present stores never block the other sources. +The script reads Claude, Codex, and OpenCode session history on a best-effort basis. It skips a missing store without comment, and no store blocks the other sources. ## Step 2: Translate each item into plain words -Write for a **non-technical listener**: a manager, a product owner, a client. Every line answers two questions in everyday words. What changed for the people using the product, and why does that help them? +Write for a non-technical listener: a manager, a product owner, a client. Every line answers two questions in everyday words. What changed for the people using the product, and why does that help them? -Before writing a line for an item you authored that has a PR (Done and In progress), read its PR body: `gh pr view <number> --repo <org>/<repo> --json title,body`. The body holds what the title compresses, namely the effect a user sees and any measured numbers. Carry one number across when the body states one, as a before and after a listener can feel: "opens in 0.04 s instead of 0.75 s", "about 17 times faster". Use only numbers the body states. +Before writing a line for an item you authored that has a PR (Done and In progress), read its PR body: `gh pr view <number> --repo <org>/<repo> --json title,body`. The body holds what the title compresses: the effect a user sees and any measured numbers. Carry one number across when the body states one, as a before and after a listener can feel: "opens in 0.04 s instead of 0.75 s", "about 17 times faster". Use only numbers the body states. -Work without a PR (pushed commits, unpushed commits, uncommitted changes, stashes, session-only tickets) gets its plain line from the gathered commit messages, branch names and session titles. Describe the effect those sources support and leave the number out. +Work without a PR (pushed commits, unpushed commits, uncommitted changes, stashes, session-only tickets) gets its line from the gathered commit messages, branch names and session titles. Describe the effect those sources support and leave the number out. Name the screen or task the user knows, then the effect. Translate every engineering term into what it does for the user: "fanout 1,013 to 14 queries" becomes "the Alternatives tab opens about 17 times faster on large orders"; "cached currency formatter" becomes "price screens load faster". When an effect has no plain description, use a short everyday comparison. -Teammates' items (Merged, Reviewed) get one plain phrase each, written from the title. Read the body only when the title alone is engineering shorthand. +Teammates' items (Merged, Reviewed) get one plain phrase each, written from the title. Read the body only when the title is engineering shorthand. ## Step 3: Write the standup -Keep it short and flat. One ticket is one line. A whole standup reads aloud in about a minute. Never list individual commits or SHAs. Never dump file lists. Never quote session prompts verbatim. +Keep it short and flat. One ticket is one line. A whole standup takes about a minute to read aloud. Never list individual commits or SHAs. Never dump file lists. Never quote session prompts verbatim. -Collapse every commit, PR, and local change for the same ticket into a single line. Branch names and PR titles carry the ticket number. +Collapse every commit, PR, and local change for the same ticket into one line. Branch names and PR titles carry the ticket number. Four buckets, in this order. Omit any empty bucket. - **Done.** PRs you authored that merged, plus your pushed commits. Say what landed, not that a PR merged. - **Merged (N).** Teammates' PRs you merged, whether or not you reviewed them in the window. The heading carries the count. -- **In progress.** Open PRs and unpushed local work such as uncommitted changes, unpushed commits, and stashes. Use sessions only to name a ticket that had real work but left no commit yet. Skip anything already covered above. +- **In progress.** Open PRs and unpushed local work: uncommitted changes, unpushed commits, and stashes. Use sessions only to name a ticket that had real work but no commit yet. Skip anything already covered above. - **Reviewed.** Teammates' PRs you reviewed but did not merge. One line total when few. ## Output format @@ -58,7 +58,7 @@ Four buckets, in this order. Omit any empty bucket. Always output the standup in markdown, using this structure. ```markdown -## Standup — <window.start_readable> to <window.end_readable> +## Standup, <window.start_readable> to <window.end_readable> ### Done @@ -77,12 +77,12 @@ Always output the standup in markdown, using this structure. - <teammate>'s <plain phrase for the change> (#<pr>) ``` -Write each Done and In progress line as one or two short sentences. Lead with the screen or feature the listener knows, then the change, then the benefit. Merge small related items onto one line rather than spending a bullet each. +Write each Done and In progress line as one or two short sentences. Lead with the screen or feature the listener knows, then the change, then the benefit. Put small related items on one line instead of a bullet each. One line, written for the listener: ```markdown -- **Procurement, Alternatives tab**: opens about 17 times faster on large orders (0.04 s instead of 0.75 s) and no longer slows the system down for other users. Prices and totals are unchanged. (#7738) +- **Procurement, Alternatives tab**: opens about 17 times faster on large orders (0.04 s instead of 0.75 s) and no longer slows the system down for other users. Prices and totals stay the same. (#7738) ``` -When any of `github.errors`, `github.error`, `local.errors`, or `sessions.errors` is present, or a source was skipped, add one short note at the end so the user knows the picture is partial. Otherwise say nothing about plumbing. When everything is empty, say so in one line and suggest checking the org or repo root. +When the output holds any of `github.errors`, `github.error`, `local.errors`, or `sessions.errors`, or a source did not run, add one short note at the end so the user knows the picture is partial. Otherwise say nothing about plumbing. When everything is empty, say so in one line and suggest checking the org or repo root. diff --git a/skills/sync-agent-setups/SKILL.md b/skills/sync-agent-setups/SKILL.md index 7b06fa8..7e8fed2 100644 --- a/skills/sync-agent-setups/SKILL.md +++ b/skills/sync-agent-setups/SKILL.md @@ -4,7 +4,7 @@ description: Preview and sync Claude Code's user-authored behavioral setup outwa disable-model-invocation: true --- -# Sync Agent Setups +# Sync agent setups Claude Code is the only source of truth. This workflow lists every detected non-Claude agent, but it writes only to targets the user confirms during this invocation. It never starts automatically. @@ -22,7 +22,7 @@ Exclude credentials, conversation history, caches, telemetry, generated runtime Resolve each Claude-visible source through any symlink and calculate its checksum. Read the target's current path, file kind, link target, and checksum without following a downstream difference back into Claude. -**Gate.** every source and detected target has a stable path, ownership classification, and explicit in-scope or excluded reason. +**Gate.** Every source and detected target has a stable path, ownership classification, and explicit in-scope or excluded reason. ## 2. Produce the dry-run manifest @@ -30,15 +30,15 @@ Classify every in-scope pair per `${CLAUDE_SKILL_DIR}/references/manifest.md`. L ## 3. Confirm the write set -Ask the user to select or confirm the target agents from the preview. Detection is not write authorization. A confirmation covers only the named targets, `ready` item IDs, backup path, exact-copy link targets, staged adaptation paths, checksums, and intended modes, semantic deltas, and proposed actions shown in the current manifest. +Ask the user to select or confirm the target agents from the preview. Detection is not write authorization. A confirmation covers only the named targets, `ready` item IDs, backup path, exact-copy link targets, staged adaptation paths, checksums, intended modes, semantic deltas, and proposed actions shown in the current manifest. Re-inventory after confirmation, re-resolve the backup path, and rehash every staged adaptation. Any changed source checksum, target state, classification, path, selected target, resolved backup path, staged bytes, staged checksum, or intended mode invalidates that item and returns it to preview. Change confirmed `ready` items to `pending`; retain every `blocked`, `preserved-unsupported`, and `preserved-orphaned` row in the ledger. -**Gate.** every `pending` item belongs to an explicitly confirmed target and still matches its preview, while every non-ready item remains outside the mutation batch with its status and reason intact. +**Gate.** Every `pending` item belongs to an explicitly confirmed target and still matches its preview, while every non-ready item remains outside the mutation batch with its status and reason intact. ## 4. Preflight and back up -Immediately before the first backup or target write, invoke `preflight-mutations` for only the selected `pending` items. Include their target agents, source and target paths, current checksums and link targets, staged adaptation paths, checksums, and intended modes, semantic deltas, confirmation source, shared-symlink risk, resolved timestamped backup path, invalidators, per-item recovery, and post-write read-back. Reference the complete ledger for context, but do not put `blocked`, `preserved-unsupported`, or `preserved-orphaned` items into the mutation card's targets or batch items. Continue only when this independently ready mutation batch returns `ready` with unchanged guards. +Immediately before the first backup or target write, invoke `preflight-mutations` for only the selected `pending` items. Include their target agents, source and target paths, current checksums and link targets, staged adaptation paths, checksums, intended modes, semantic deltas, confirmation source, shared-symlink risk, resolved timestamped backup path, invalidators, per-item recovery, and post-write read-back. Reference the complete ledger for context, but do not put `blocked`, `preserved-unsupported`, or `preserved-orphaned` items into the mutation card's targets or batch items. Continue only when this independently ready mutation batch returns `ready` with unchanged guards. Create the confirmed timestamped backup at the recorded resolved physical path before changing a target. Preserve for every affected path: @@ -49,7 +49,7 @@ Create the confirmed timestamped backup at the recorded resolved physical path b Write a backup manifest mapping each item ID to its recovery action. Verify the backup inventory and checksums before the first target mutation. -**Gate.** every pending target path has a verified recovery record, including preserved link metadata and resolved content evidence. +**Gate.** Every pending target path has a verified recovery record, including preserved link metadata and resolved content evidence. ## 5. Apply confirmed items diff --git a/skills/systematic-debugging/SKILL.md b/skills/systematic-debugging/SKILL.md index 5a70da3..6566fb4 100644 --- a/skills/systematic-debugging/SKILL.md +++ b/skills/systematic-debugging/SKILL.md @@ -26,9 +26,9 @@ Complete each phase before the next. Before attempting ANY fix: 1. Read the error message. This is step zero. The stack trace tells you which line, and the message tells you what invariant broke. Read every error and warning in the output. They often contain the exact answer. -2. Reproduce consistently. `/diagnosing-bugs` Phase 2 is the playbook for building the repro loop. +2. Reproduce consistently. `/diagnosing-bugs` Phases 1 and 2 are the playbook for building the repro loop and minimising it. 3. Check recent changes. Look at the git diff, recent commits, new dependencies, config changes, and environment differences. -4. Instrument the boundaries. In a multi-component system, log what enters and exits each boundary and verify env and config propagation. Redact secrets, tokens, credentials, and personal data before anything lands in a log. Keep metadata like names and sizes, never values. Two examples are CI to build to signing, and API to service to database. Run once and read the evidence. It shows where the chain breaks. For example, the evidence may show secrets reaching the workflow but not the build. Then investigate that component. Full technique: [TECHNIQUES.md](${CLAUDE_SKILL_DIR}/TECHNIQUES.md#root-cause-tracing). +4. Instrument the boundaries. In a multi-component system, such as CI to build to signing or API to service to database, log what enters and exits each boundary and verify env and config propagation. Redact secrets, tokens, credentials, and personal data before anything lands in a log. Keep metadata like names and sizes, never values. Run once and read where the chain breaks, for example secrets that reach the workflow but not the build. Then investigate that component. Full technique: [TECHNIQUES.md](${CLAUDE_SKILL_DIR}/TECHNIQUES.md#root-cause-tracing). 5. Trace the data flow backward. When the error is deep in the call stack, the crash site is a symptom. Where does the bad value originate? Keep tracing up until you find the source, and fix it there. Phase 1 is done when you can name the line that produces the bad value and the input that made it bad. If you can only name where it crashed, you are not done. @@ -53,8 +53,6 @@ Phase 2 is done when you can name the working example you compared against and a ### Phase 3: Hypothesis and testing -Scientific method: - 1. Rank 3-5 falsifiable hypotheses, then test the top one. Each states its prediction. For example: "If X is the cause, changing Y makes the bug disappear." Write them down. A prediction can fail; an explanation cannot. 2. Test minimally. Make the smallest change that tests the hypothesis. Change one variable per run so the result isolates the cause. 3. Verify before continuing. A confirmed hypothesis moves to Phase 4. Otherwise revert the failed fix before testing the next hypothesis. diff --git a/skills/systematic-debugging/TECHNIQUES.md b/skills/systematic-debugging/TECHNIQUES.md index d789a34..dda1f1e 100644 --- a/skills/systematic-debugging/TECHNIQUES.md +++ b/skills/systematic-debugging/TECHNIQUES.md @@ -1,8 +1,8 @@ -# Supporting Techniques +# Supporting techniques Reached from `SKILL.md` at the point each one fires. -## Root-Cause Tracing +## Root-cause tracing Bugs often manifest deep in the call stack (file created in the wrong directory, database opened with the wrong path). The instinct is to fix where the error appears. That's the symptom. Trace backward until you find the original trigger: @@ -20,7 +20,7 @@ When you can't trace by reading, instrument: log directory, cwd, relevant env, a For test pollution where you don't know which test is the culprit, bisect: run the suite one file at a time and stop at the first polluter. -## Defense in Depth +## Defense in depth After fixing at the source, validate at EVERY layer the data passes through. A single check gets bypassed by other code paths, refactors, or mocks: @@ -31,7 +31,7 @@ After fixing at the source, validate at EVERY layer the data passes through. A s Different layers catch different bypasses. -## Condition-Based Waiting +## Condition-based waiting Flaky, timing-dependent test failures usually come from arbitrary sleeps, guesses that pass on a fast machine and fail under CI load. Wait for the actual condition instead: diff --git a/skills/triage-board/SKILL.md b/skills/triage-board/SKILL.md index 150955b..acb91a3 100644 --- a/skills/triage-board/SKILL.md +++ b/skills/triage-board/SKILL.md @@ -8,7 +8,7 @@ disable-model-invocation: true I fill the board fields that decide what gets worked on next: whether a coding agent can close a ticket unaided, how urgent it is, what kind of work it is, and how much of the owner's own time it will cost. I read, judge, and reconcile. `preflight-mutations` authorizes every write. -I work five concepts. I resolve each against the live board rather than assuming an ID, so I run on any project with this shape: +I resolve five concepts against the live board instead of assuming IDs, so I run on any project with this shape: | Concept | Usual shape | Missing? | |---|---|---| @@ -18,27 +18,27 @@ I work five concepts. I resolve each against the live board rather than assuming | Type | An org-level issue type, not a board field | I stop and name it | | Release | An iteration board field that bounds the scope | I require an explicit scope instead | -Ordering matters. Scope, then calibration, then judgment, then authorization, then writes, then reconciliation. A judgment made before the calibration is approved is thrown away. +The order is scope, calibration, judgment, authorization, writes, reconciliation. I discard any judgment made before the calibration is approved. ## 1. Resolve the board and the ledger's home -**First I settle which board this is, because nothing else in section 1 can start without it.** A board is an owner plus a number: project numbers restart per owner, so a bare number names a different board under every account. An argument decides it only when it carries both, or is a URL I parse into both. With no argument I list the boards linked to the repository I am running in and keep the open ones: +I settle which board this is first, because nothing else in section 1 can start without it. A board is an owner plus a number. Project numbers restart per owner, so a bare number names a different board under every account. An argument decides the board only when it carries both or is a URL I parse into both. With no argument I list the boards linked to the repository I am running in and keep the open ones: - exactly one open board, I take it and say which; - more than one, I ask, showing each number and title; - none, I ask for the owner and project number, since the board may be owned by the organization and linked to no repository. -I never guess from the working directory or from a board I used before. Two open boards on one repository is the ordinary case, not the edge case, so the ask is a normal outcome rather than a failure. +I never guess from the working directory or from a board I used before. A repository with two open boards is common, so asking is a normal outcome, not a failure. -I then read the project's fields, the repository's labels, the organization's issue types, and the login of the credential I am authenticated as, and record the stable ID of each. That login is the token's principal rather than the requester by definition, so I confirm the two are the same person before the ownership boundary keys on it; a bot or shared credential stops the run. I never carry an ID between runs; a board that gained a field or renamed an option would make a cached ID write to the wrong place. +I then read the project's fields, the repository's labels, the organization's issue types, and the authenticated login, and record each stable ID. The login belongs to the token, not necessarily the requester, so I confirm they are the same person before the ownership boundary keys on it. A bot or shared credential stops the run. I never carry an ID between runs, because a board that gained a field or renamed an option would send a cached ID's write to the wrong place. Load `${CLAUDE_SKILL_DIR}/references/board-queries.md` now. It holds the exact queries, each with the assertion that proves it read everything. -**A missing label is bootstrapped through its own card, not created on a bare yes.** Creating a repository label is a shared-state write, so it cannot happen ahead of the authorization step by asking politely. When either label is absent I raise a label-creation card through `preflight-mutations` naming both labels, their colors and every repository in scope, create only on a `ready` verdict, then re-read the repository's labels and record the resulting IDs. It is a separate card from the label edits in section 5: the target is the repository rather than an issue, and the reversibility differs. +**A missing label gets its own card.** Creating a repository label is a shared-state write, so a bare yes from the requester does not authorize it. When either label is absent I raise a label-creation card through `preflight-mutations` naming both labels, their colors and every repository in scope, create only on a `ready` verdict, then re-read the repository's labels and record the resulting IDs. It is a separate card from the label edits in section 5: the target is the repository rather than an issue, and the reversibility differs. -I also settle **where the ledger lives** before anything else, because `preflight-mutations` blocks a multi-batch run that has no already-authorized durable home and is forbidden from creating one. I name an existing artifact the requester has authorized this run to write, or I ask for one. I do not invent a file to clear the gate. +I also settle where the ledger lives before anything else. `preflight-mutations` blocks a multi-batch run with no already-authorized durable home and may not create one. I name an existing artifact the requester has authorized this run to write, or I ask for one. I never invent a file to clear the gate. -The ledger carries one row per candidate **field**, not per candidate, and is the run's memory across interruption. Each candidate takes four writes across three independently failing action classes, so a single status per candidate would either hide a failed write or drop a landed estimate out of the landed-only totals. `Field` is the row discriminator, and each row carries its own current value, proposed value, basis and status. A partial run therefore keeps its landed field rows and re-cards only the pending ones, which is the resume shape `preflight-mutations` expects: +The ledger is the run's memory across interruption. It has one row per candidate field, keyed by `Field`, not one per candidate. Each candidate takes four writes across three action classes that fail independently, so one status per candidate would hide a failed write or drop a landed estimate from the landed-only totals. Each row carries its own current value, proposed value, basis, and status. A partial run keeps its landed rows and re-cards only the pending ones, the resume shape `preflight-mutations` expects: ```markdown | Item ID | Issue | Owner | Kind | Field | Current | Proposed | Basis | Status | @@ -46,17 +46,17 @@ The ledger carries one row per candidate **field**, not per candidate, and is th | <item ID> | #<n> | <login> | leaf / umbrella | label / priority / estimate / type | <value> | <value> | <evidence> | candidate / pending / landed / failed / skipped / reconcile-required / conflicted / excluded | ``` -An excluded item is a ledger row too, and it is the one shape that does not carry a field. It takes `Field: none`, `Current` and `Proposed` empty, the exclusion reason in `Basis`, and `Status: excluded`, exactly one row per excluded item rather than four. Without that shape an interrupted run loses the reasons section 2 requires and reports a different exclusion count on resume than it did on the first pass. +An excluded item gets exactly one row, not four: `Field: none`, empty `Current` and `Proposed`, the exclusion reason in `Basis`, and `Status: excluded`. Without that row an interrupted run loses the reasons section 2 requires and reports a different exclusion count on resume than on the first pass. **Gate.** The board is named rather than inferred, every concept the run needs has a resolved ID, the requester's identity is known, and the ledger has an authorized home. Otherwise the run stops naming what is missing. ## 2. Fix the scope -With no argument I take every open issue where the requester is the **sole** assignee, on any iteration in the board's live `iterations` list. That list holds the iteration currently running as well as every future one, so work already underway is triaged rather than skipped. A completed iteration sits in a separate list and is reached only by naming it. An argument narrows that to one release, one status, or an explicit list of numbers. +With no argument I take every open issue where the requester is the **sole** assignee, on any iteration in the board's live `iterations` list. That list holds the running iteration and every future one, so I triage work already underway too. A completed iteration sits in a separate list, and I reach it only when the requester names it. An argument narrows that to one release, one status, or an explicit list of numbers. I drop pull requests and draft items, which share the board with issues and carry none of the fields being set. -I apply the board's ownership policy without being asked. A co-assigned, unassigned, or ambiguously owned ticket is read-only, however wide my technical access is. I record every exclusion with its reason rather than dropping it silently, because a total that quietly omits items is worse than a smaller total that says what it left out. +I apply the board's ownership policy unprompted. A co-assigned, unassigned, or ambiguously owned ticket is read-only whatever my technical access. I record every exclusion with its reason and never drop one silently. **Gate.** The candidate set is exact, every excluded item carries a reason, and each candidate is a ledger row at `candidate`. @@ -64,9 +64,9 @@ I apply the board's ownership policy without being asked. A co-assigned, unassig I ask **once**, in a single question, before judging the batch. `${CLAUDE_SKILL_DIR}/references/calibration.md` carries the defaults I propose: the estimate unit, scale, rounding and cap; worked anchors spanning the range; the priority rubric; and any project-specific priority lift the requester wants for this run. -The estimate unit defaults to **human active time**, which `${CLAUDE_SKILL_DIR}/references/calibration.md` defines and anchors. +The estimate unit defaults to human active time, which `calibration.md` defines and anchors. -Approving a rubric is not the same as approving a number. So I then estimate **three to five real tickets** from this scope, covering the smallest, the typical, the largest, and any umbrella, and show those with their reasoning and their subtotal. A scope holding fewer than three candidates previews every one of them instead, since the point is a number the requester has seen rather than a quota. A scope holding none skips the calibration, the judgement and every write, and goes straight to a report with zero totals and its exclusion list. Only after the requester approves that preview do ledger rows move from `candidate` to `pending`. Without it the first applied number they ever see arrives after every write has landed. +An approved rubric is not an approved number. So I then estimate three to five real tickets from this scope, covering the smallest, the typical, the largest, and any umbrella, and show them with their reasoning and subtotal. A scope with fewer than three candidates previews all of them. A scope with none skips calibration, judgment, and every write, and goes straight to a report with zero totals and its exclusion list. Ledger rows move from `candidate` to `pending` only after the requester approves the preview. Without it, the first applied number they see arrives after every write has landed. A changed unit, scale, anchor, rubric, ownership boundary, or umbrella treatment invalidates every row not yet written. @@ -78,7 +78,9 @@ I read each ticket's full body and record four values: the triage label, the pri The triage label answers one question: **is the correct behavior already decided?** `${CLAUDE_SKILL_DIR}/references/triage-rubric.md` carries the five conditions that force `need-human` however well written a ticket is, and the wording tells that settle it cheaply. Difficulty never enters. A tedious ticket that touches three hundred files is `agent-ready` when its outcome is pinned; a one-line fix is `need-human` when nobody has said which line. -**Fan-out is by threshold, not by default.** Under about forty candidates I read inline. Above that I write the rubric to one file and give batches of about thirty to parallel readers, each returning only its verdicts. I re-use those same readers for any second pass rather than re-reading the bodies, since they already hold them. +**Fan-out is by threshold, not by default.** Under 40 candidates I read inline. At 40 or more I write the rubric to one file and give batches of 30 to parallel readers, each returning only its verdicts. I reuse those readers for any second pass, since they already hold the bodies. + +During any wait longer than 10 minutes anywhere in the run, such as parallel readers or API pacing, I post a one-line status, attended or not. Every batch gets a coverage diff against the list it was given. A reader that returns twenty-nine verdicts for thirty tickets has dropped one silently, and only the diff catches it. @@ -96,7 +98,7 @@ I invoke `preflight-mutations` with the board and field IDs, the item IDs, curre I perform only the authorized writes, tee every response to a log, then re-read every touched field from the authoritative source and compare it field by field against the ledger. -Two failure modes make this mandatory rather than tidy, and `${CLAUDE_SKILL_DIR}/references/board-queries.md` carries the evidence for both: a board mutation can report success without persisting, and the issue search index both lags and truncates. So a zero exit code is never evidence, and labels are verified by reading the issue object rather than any list. +A board mutation can report success without persisting, and the issue search index lags and truncates. `${CLAUDE_SKILL_DIR}/references/board-queries.md` carries the evidence for both. So a zero exit code is never evidence, and I verify labels by reading the issue object, never a list. I grep the whole write log for failures rather than reading its tail, because a malformed value fails one row in the middle of a run that otherwise looks clean. @@ -113,7 +115,7 @@ Every row then takes a terminal status from observed state: I give the requester: - counts by triage label, by priority, and by type; -- the estimate total **split by triage label**, because the two are not interchangeable. Hours sitting in `need-human` tickets have to be spent before an agent can start, so only the `agent-ready` subtotal predicts throughput; +- the estimate total split by triage label, because only the `agent-ready` subtotal predicts throughput. `need-human` hours must be spent before an agent can start; - every ticket whose estimate hit the cap, named, since a cap hides its real size and usually means the ticket wants splitting; - every `conflicted`, `reconcile-required`, `failed`, and `excluded` row; and - any judgment I found genuinely close, so the requester can overturn it. diff --git a/skills/verify-claims/SKILL.md b/skills/verify-claims/SKILL.md index 6c40b17..d969539 100644 --- a/skills/verify-claims/SKILL.md +++ b/skills/verify-claims/SKILL.md @@ -49,7 +49,7 @@ I choose the lane matching the consequence. I inspect the current target. Prior I use observations from distinct surfaces. I run the distinguishing observation from step 2 when its surface is available. I record commands, targets, timestamps, file locations, or result identifiers precisely enough for another agent to recheck them. -**Gate.** Each evidence field contains an observation or a concrete reason it is missing or inconclusive, and the distinguishing-observation field contains either its result or the concrete reason it was unavailable. +**Gate.** Each evidence field holds an observation or a concrete reason it is missing or inconclusive. The distinguishing-observation field holds its result or the concrete reason it was unavailable. ## 4. Assign exactly one state @@ -71,7 +71,7 @@ For a material reversal, I dispatch a fresh reviewer who did not produce the ori When no fresh reviewer is available, I set the replacement claim to `blocked` and name the missing recheck. -**Gate.** A non-reversal is recorded as not applicable. Otherwise the independent card is reconciled or reliance on the replacement conclusion is blocked. +**Gate.** I record a non-reversal as `not applicable`. Otherwise the independent card is reconciled or reliance on the replacement conclusion is blocked. ## 6. Persist before reliance diff --git a/tools/eval/triggers.json b/tools/eval/triggers.json index 6aa7c35..366bb01 100644 --- a/tools/eval/triggers.json +++ b/tools/eval/triggers.json @@ -251,6 +251,26 @@ { "utterance": "I'm starting a new inventory app and need to plan its requirements and technical architecture before we write code.", "expect": "project-discovery", - "why": "collision: a bare new-app utterance asking for requirements and architecture stays with project-discovery rather than routing to naming" } + "why": "collision: a bare new-app utterance asking for requirements and architecture stays with project-discovery rather than routing to naming" }, + + { "utterance": "check https://github.com/acme/shop/pull/42#pullrequestreview-2318845", + "expect": "fix-pr-review", + "why": "the user's most common review request: a bare review URL after check, from any reviewer, routes to fix-pr-review" }, + + { "utterance": "resolve conflicts on https://github.com/acme/shop/pull/42, it can't merge into dev", + "expect": "resolving-merge-conflicts", + "why": "a conflict request before any merge has started still routes here; the skill starts the merge itself" }, + + { "utterance": "i think we sorted this out already, do we still have this issue? https://github.com/acme/shop/issues/7", + "expect": "audit-ticket", + "why": "the user's own phrasing for an audit: asking whether an old issue still holds" }, + + { "utterance": "implement issues 12, 13 and 14 and raise a pr for each using subagents, you be the orchestrator", + "expect": "executing-tickets-with-subagents", + "why": "attended multi-ticket work requested by its subagent phrasing rather than a bundled parent ticket" }, + + { "utterance": "this pr needs proper browser qa https://github.com/acme/shop/pull/42", + "expect": "browser-qa", + "why": "COLLISION: a PR URL with a browser QA ask belongs to browser-qa, not review-pr" } ] } From 8aadb8accc35869d4a7a3a3fc0495699ad75637b Mon Sep 17 00:00:00 2001 From: Bhagya Mudgal <bhagyamudgal@gmail.com> Date: Tue, 10 Nov 2026 14:20:16 +0530 Subject: [PATCH 2/8] fix(skills): close the gaps review found in the new skill rules - Hold fix-pr-review FIX replies until a push lands, so no thread closes on unpushed code - Support both T3 tool generations and keep keyboard focus intact in browser-qa - Stop CodeRabbit from uploading changes outside the task and record incomplete review coverage - Make resolving-merge-conflicts fetch the real base and refuse a dirty worktree - Pull the official OpenClaw archive unless a listing proves the raw archive covers it --- skills/browser-qa/SKILL.md | 4 ++-- skills/file-pr/SKILL.md | 7 ++++--- skills/fix-pr-review/SKILL.md | 20 ++++++++++--------- .../fix-pr-review/references/final-report.md | 6 +++++- .../references/github-reply-resolve.md | 4 +++- .../references/needs-input-triage.md | 4 ++-- .../references/push-publication.md | 8 ++++---- skills/fix-ts-errors/SKILL.md | 4 ++-- skills/openclaw-backup/SKILL.md | 9 +++++---- skills/parallel-review/SKILL.md | 2 +- .../references/stack-menu.md | 2 +- skills/resolving-merge-conflicts/SKILL.md | 2 +- skills/review-pr/SKILL.md | 2 +- skills/review-pr/references/batch-mode.md | 8 ++++---- .../references/finding-state-schema.md | 4 +++- .../references/github-posting-rerun.md | 5 ++++- skills/review-pr/references/github-posting.md | 9 +++++++-- .../references/phase1-timeline-state.md | 2 +- skills/simplify/SKILL.md | 2 +- skills/triage-board/SKILL.md | 2 +- 20 files changed, 63 insertions(+), 43 deletions(-) diff --git a/skills/browser-qa/SKILL.md b/skills/browser-qa/SKILL.md index d6ac4af..1c2af6e 100644 --- a/skills/browser-qa/SKILL.md +++ b/skills/browser-qa/SKILL.md @@ -7,11 +7,11 @@ I drive a real browser through the flow and I do not call it done until I watche ## Driver priority -1. When `t3-code_preview_snapshot` is in the tool list, I use the T3 preview tools only. I open with `t3-code_preview_open`, go to the URL with `t3-code_preview_navigate`, then loop `t3-code_preview_snapshot` followed by `t3-code_preview_act` with `action: "click"` or `action: "type"` and a `locator` of `aria-ref=<ref>` from that snapshot. I re-snapshot before every interaction because refs go stale. I keep `open=true` so the user sees the run. +1. When `t3-code_preview_snapshot` is in the tool list, I use the T3 preview tools only. I open with `t3-code_preview_open`, go to the URL with `t3-code_preview_navigate`, then loop `t3-code_preview_snapshot` followed by a click or type on a ref from that snapshot. When `t3-code_preview_act` is in the tool list, that call is `t3-code_preview_act` with `action: "click"` or `action: "type"` and a `locator` of `aria-ref=<ref>`; otherwise it is `t3-code_preview_click` or `t3-code_preview_type`. I re-snapshot before every interaction because refs go stale. I keep `open=true` so the user sees the run. 2. Else, when Playwright MCP `browser_navigate` exists, I use the Playwright flow unchanged. 3. Else, I use the `agent-browser` CLI (`open`, `snapshot -i`, `click @eN`, `fill @eN`). I report any evidence the CLI cannot produce as missing, never assume it. -Playwright to T3 mapping: `browser_navigate` becomes `t3-code_preview_navigate`, `browser_snapshot` becomes `t3-code_preview_snapshot`, `browser_click` becomes `t3-code_preview_act` with `action: "click"`, `browser_type` becomes `t3-code_preview_act` with `action: "type"`, `browser_press` becomes `t3-code_preview_act` with `action: "press"`, `browser_wait_for` becomes `t3-code_preview_wait_for`. Every click and type call targets a `locator` of `aria-ref=<ref>`, with the ref taken from `t3-code_preview_snapshot`. A press goes to the focused element, so I click the field first. Screenshots come from `t3-code_preview_snapshot` with `save=true`, which returns a `screenshotPath` to move into `.qa/`. +Playwright to T3 mapping: `browser_navigate` becomes `t3-code_preview_navigate`, `browser_snapshot` becomes `t3-code_preview_snapshot`, `browser_wait_for` becomes `t3-code_preview_wait_for`. When `t3-code_preview_act` is in the tool list, `browser_click`, `browser_type` and `browser_press` become `t3-code_preview_act` with `action: "click"`, `"type"` or `"press"`; otherwise they become `t3-code_preview_click`, `t3-code_preview_type` and `t3-code_preview_press`. Every click and type call targets the ref taken from `t3-code_preview_snapshot`, passed to `t3-code_preview_act` as a `locator` of `aria-ref=<ref>`. A press goes to the focused element. When the step targets a specific field, I click it first; a keyboard-navigation step such as Tab, Shift+Tab or an arrow key presses without a click, so the focus order under test is the page's own. Screenshots come from `t3-code_preview_snapshot` with `save=true`, which returns a `screenshotPath` to move into `.qa/`. ## Recording diff --git a/skills/file-pr/SKILL.md b/skills/file-pr/SKILL.md index 9a2a420..6c90077 100644 --- a/skills/file-pr/SKILL.md +++ b/skills/file-pr/SKILL.md @@ -29,11 +29,11 @@ Choose exactly one issue link: Never `Closes` an issue this diff does not finish. -When the user asked to batch several issues into one PR, each issue already arrives as its own commit, verified by its own `done` run before the next issue starts. The body carries one link line per issue: `Closes #N` for every issue the diff finishes, and the list above for any other. +When the user asked to batch several issues into one PR, each issue arrives as its own commit, verified by its own `done` run before the next issue starts. Before writing more than one `Closes #N`, confirm for each issue that the card holds a `verified` request row for it and that it has its own commit in `git log <merge-base>..HEAD` whose message contains `#N`. At most one batched issue may instead rely on the sealed commit section 6 will create for its row. An issue missing either gets `Refs #N`, and the final report names which is missing. The body carries one link line per issue: `Closes #N` for every issue the diff finishes that passed this check, and the list above for any other. When you choose `Refs #N` over a closing keyword, the final report says the issue will not show as linked in GitHub's Development panel, which lists only closing-keyword links, and names the reason from the list above. -**Gate.** The base ref, remote base tip, and merge base match the refreshed remote state, and the issue link comes from issue output, not assumption. +**Gate.** The base ref, remote base tip, and merge base match the refreshed remote state, the issue link comes from issue output, not assumption, and every batched `Closes #N` has a `verified` card row and its own commit, with at most one sealed in section 6. ### Revalidate card currency and handoff eligibility @@ -82,8 +82,9 @@ Then include only the sections that have content: - **What changed.** Derive one outcome-focused bullet from each verified request row. Do not infer additions from the diff. - **Before and after, and QA test results.** When the change alters UI or API behavior, the card's Browser facet names `.qa/pr-evidence.md` from `browser-qa`. Paste it here verbatim, except for embed lines left out on the user's first choice when push access is missing. Its SHA-256 must equal the recorded digest. A missing fragment, another digest, or a cold-read bar that fails inside the fragment returns to `done` with the failure named, because `done` regenerates the fragment through `browser-qa`. -- **How to verify.** Derive only from the evidence index's exact observations. Include applicable `not-applicable` reasons and do not invent evidence. A PR without the fragment says here in one line why browser QA does not apply, for example `No UI or API behavior changed, so there is no browser QA.` When the card's Code lane records a `parallel-review` member as `unavailable(<reason>)`, say here which member and why. It does not block the PR. +- **How to verify.** Derive only from the evidence index's exact observations. Include applicable `not-applicable` reasons and do not invent evidence. A PR without the fragment says here in one line why browser QA does not apply, for example `No UI or API behavior changed, so there is no browser QA.` When the card's Code lane records a `parallel-review` member as `unavailable(<reason>)`, say here which review coverage did not run and why, named by the work it covers, for example `No accessibility review ran on this diff because the review tool was rate limited.` Name no agent, tool, or member. It does not block the PR. The final report names the member and its reason. - **Risk or scope notes.** Only when something is uncertain, migrated, or deferred on purpose. +- **Not filed.** Only when `converge-reviews` left proposed follow-up issues unfiled: one line each with the finding and why it was not filed. - The issue link from section 2. The attach list is the target of every `![...](...)` embed in the fragment outside code, each once, in fragment order, and each file's SHA-256 must equal the digest the Browser facet recorded. A PR with no fragment attaches nothing. The data approval covers the whole fragment, its text as well as its files: paste and attach only when it holds synthetic or redacted data or the user explicitly approved publishing it. Without that approval, stop before any push, create, or body-edit side effect and ask the user to approve the listed files and text, or return to `done` to regenerate on synthetic data. Attaching requires gh v2.99.0 or later; confirm the floor with `gh --version` before any push or create side effect. When the install is older, return to `done` for a gh upgrade instead of publishing a UI or API PR without its evidence. Publish bare only on explicit user decision, then upload the fragment's files through section 6's `gh pr edit --attach` step after upgrading; `done` holds Browser `deferred` until they land. diff --git a/skills/fix-pr-review/SKILL.md b/skills/fix-pr-review/SKILL.md index 247c2b4..c11d7ff 100644 --- a/skills/fix-pr-review/SKILL.md +++ b/skills/fix-pr-review/SKILL.md @@ -22,7 +22,7 @@ Use AskUserQuestion only for these user decisions: branch safety, stash confirma | 5 | Execute fixes: sequential edits + per-file narrow type-check | Modified files, `fix_status` per item | | 5.5 | Convergence subagent: class completeness, inverse risk, new siblings | Per-fix verdicts; missing sites applied | | 6 | `/done` acceptance verification scoped to the fix diff, no commit or publish handoff | Verified fix diff, `done_verified_snapshot` | -| 7 | Reply + resolve on GitHub (skipped for local files) | Threads resolved | +| 7 | Reply + resolve contested items on GitHub, FIX replies held until a push lands (skipped for local files) | Contested threads resolved, FIX replies frozen | | 8 | Finalize: settle NEEDS-INPUT, restore stash, report, suppressions write, next actions | Final report | ### R-Rubric Summary (Phase 3 STEP 4; first match wins) @@ -49,7 +49,7 @@ Each carries its firing condition in the pointer at the point of use. Load it th - `references/fetch-review-data.md`: per-input-type GraphQL/REST fetch, CodeRabbit review-body anatomy, `Comment` schema. Loaded by main in Phase 2, at the GitHub fetch step, and again at the `Comment`-schema normalisation step if the local-file path meant it was not read there. - `references/triage-prompt.md`: the whole Phase 3 subagent prompt (STEP 0 → STEP 6 + output format). Read by main in Phase 3, placeholder-substituted, passed verbatim. - `references/triage-rubric.md`: R1-R10 detail, NEEDS-INPUT calibration, `change_class` worked examples, reply formats. Loaded by the triage SUBAGENT at STEP 4. -- `references/github-reply-resolve.md`: Steps 7a-7d posting/resolving mechanics. Loaded by main in Phase 7; never loaded for local-file input. +- `references/github-reply-resolve.md`: Steps 7a-7d posting/resolving mechanics. Loaded by main in Phase 7, and its Step 7c runs again after a landed push in Phase 8 step 5 or step 2b; never loaded for local-file input. - `references/final-report.md`: the Phase 8 report template and its rendering rules. Loaded by main in Phase 8 before printing. - `references/branch-safety.md`: repo plus branch landing with detached-HEAD and different-branch questions. Loaded by main in Phase 1 for GitHub inputs; skipped for local files. - `references/per-fix-loop.md`: per-fix loop, interactive confirmations, narrow type-check plus baseline compare, retry/skip/abort. Loaded by main at the Phase 5 loop. @@ -298,7 +298,7 @@ Skip this step if there are zero contested items. - R5: the `claude_md_quote` appears verbatim in CLAUDE.md, which main re-read. - R2: a re-anchor search for the cited symbol finds nothing. -- R4: the diff of `prior_commit_sha` removes the cited pattern. +- R4: `prior_commit_sha` is an ancestor of HEAD (`git merge-base --is-ancestor <prior_commit_sha> HEAD`), its diff removes the cited pattern, `git cat-file -e HEAD:<path>` succeeds for the cited file, and that file at HEAD no longer contains the pattern. Any git exit above 1 fails the check. - R7: the tracking issue is open and references the finding. R1, R3, and R8 items always go to the question below. Record each check as an auto-confirm record: rule, re-checked field, and result. When `EXECUTION_AUTHORIZED=true`, `--interactive` is not set, and every contested item passes its check, skip both questions below. Those items pass this confirmation, Phase 7 posts their replies under their records without asking, and the final report lists each one. When `UNATTENDED_WATCH=true`, items that pass their check pass the same way. Every other contested item, R1, R3, and R8 included, moves to NEEDS-INPUT with `why_unclear: "unattended run, <classification> not mechanically confirmed"` and its `reusability_context` unchanged, so its thread stays open. Otherwise, use AskUserQuestion: @@ -326,7 +326,7 @@ For each selected item, use a follow-up AskUserQuestion: On "FIX": re-dispatch the classifier scoped to just this item to produce the full FIX field set: `fix_plan`, `change_class`, `test_scenario`, `inverse_risk`, `class_completeness` (class sweep included; a reclassified item has never been swept), and `reusability_context` (carry the contested item's own value through unless the sweep changed it). Then re-run plan validation on the changed item. Producing a partial field set here fails validation and burns the single retry. On "NEEDS-INPUT": move to NEEDS-INPUT with `why_unclear: "user contested the <classification> classification"`, carrying the item's `reusability_context` through unchanged. On "Keep as-is": no change. On "Other": treat the freeform text as the reclassification instruction. -This step posts and resolves nothing. Only Phase 7 touches GitHub, and it acts **only** on items that passed this confirmation, asked or evidence-confirmed. A DISMISS, DEFER, or DISAGREE that has not passed this gate stays open and unanswered. +This step posts and resolves nothing. Only Phase 7 and the held FIX posting after a landed push touch GitHub, and contested items post **only** when they passed this confirmation, asked or evidence-confirmed. A DISMISS, DEFER, or DISAGREE that has not passed this gate stays open and unanswered. ### Execution @@ -513,7 +513,7 @@ This skill never reads or writes `/review-pr`'s cache (`~/.claude/skills/review- Replying and resolving threads is this phase's entire GitHub footprint. The review's own `CHANGES_REQUESTED` state stays exactly as CodeRabbit left it. CodeRabbit clears it itself on its next auto-re-review, once the user pushes. -When `UNATTENDED_WATCH=true`, post and resolve only the auto-confirmed contested items here. Record every FIX item's reply and resolution as `skipped` with the reason `held until push`; Phase 8 step 2b posts them only after the push lands. +In every run, post and resolve only contested items here; when `UNATTENDED_WATCH=true`, only the auto-confirmed ones. Steps 7a and 7b still run for every FIX item now, while the fix is uncommitted, and freeze its validated reply. Record every FIX item's reply and resolution as `skipped` with the reason `held until push`; Phase 8 step 1 adds each NEEDS-INPUT item it fixes to the same held set. Held FIX replies post, and their threads resolve, only after a push read-back records the push landed: in Phase 8 step 5 after "Push to remote", or in step 2b of an unattended watch run. Define `has_github_surface[idx] = thread_id != null AND (can_reply OR can_resolve)`. Initialize every item without that surface to paired `not-applicable` states, regardless of input source. For `./review.md` or any other local-file input, all items meet that condition; initialize them, then skip the rest of this phase. Phase 8 still requires deterministic per-item state for its final report. @@ -615,9 +615,9 @@ After printing the final report and completing the suppression step when applica text: "Fix run complete. <F> fixes applied, <S> skipped, <N> needs-input. What next?" options: - label: "Commit changes" - description: "Stage and commit using the suggested commit message" + description: "Stage and commit using the suggested commit message; FIX threads stay open until a push" - label: "Push to remote" - description: "Commit and push to update the PR" + description: "Commit and push to update the PR, then post the held FIX replies" - label: "Re-run on remaining" description: "Run /fix-pr-review again for skipped/needs-input items" - label: "Done" @@ -625,10 +625,12 @@ After printing the final report and completing the suppression step when applica On "Commit changes" or "Push to remote", require `inverse_risk_blockers` to be empty, `stash_restored` to be none of `conflict`, `foreign-top`, or `failed`, and a valid `done_verified_snapshot`. Recompute the blocker set instead of trusting the rendered report; a non-empty set stops the action. Invoke `git-commit` in Verified content snapshot sealed-index mode with that snapshot; never stage or restage the live worktree. Require the created commit tree to equal the snapshot tree. Ordinary restored WIP remains in the worktree and outside the candidate commit. -On "Push to remote", load `${CLAUDE_SKILL_DIR}/references/push-publication.md` now and follow it: commit first, then freeze the publication target, run `preflight-mutations`, push with a lease, and read back remote and local state. +On "Push to remote", load `${CLAUDE_SKILL_DIR}/references/push-publication.md` now and follow it: commit first, then freeze the publication target, run `preflight-mutations`, push with a lease, and read back remote and local state. When that read-back records the push landed, post the held FIX replies and resolve their threads through Phase 7's posting mechanics, then print each FIX item's reply and resolution state, failures first in the final report's failure-section format. When the push does not land, post nothing for FIX items. + +On "Commit changes", "Re-run on remaining", or "Done", held FIX threads stay open; the final report already says to rerun posting after pushing. On "Re-run on remaining": if the original input was a local file, invoke `/fix-pr-review <original-file-path>` scoped to skipped/needs-input items; otherwise invoke `/fix-pr-review <url>` scoped to remaining items. On "Done": exit. ### 6. Exit -Committing happens only on an explicit "Commit changes" or "Push to remote" choice in the post-completion prompt above, or in step 2b of an unattended watch run. Otherwise leave the working tree as it stands. The report's suggested commit message is the hand-off. +Committing happens only on an explicit "Commit changes" or "Push to remote" choice in the post-completion prompt above, or in step 2b of an unattended watch run. Otherwise leave the working tree as it stands. Held FIX replies post only after a landed push in step 5 or step 2b; until then their threads stay open. The report's suggested commit message is the hand-off. diff --git a/skills/fix-pr-review/references/final-report.md b/skills/fix-pr-review/references/final-report.md index 6af03b4..67a8550 100644 --- a/skills/fix-pr-review/references/final-report.md +++ b/skills/fix-pr-review/references/final-report.md @@ -6,7 +6,7 @@ Loaded by main in Phase 8 only after every NEEDS-INPUT item has a non-pending st ## Failure section (TOP, only if any Phase 7 or Phase 8 GitHub op failed) -If any applicable `gh_status[idx]` has `reply_ok == false` OR `resolve_ok == false`, print this section at the **top** of the report. Exclude entries whose reply and resolution states are both `not-applicable`: +If any applicable `gh_status[idx]` has `reply_ok == false` OR `resolve_ok == false`, print this section at the **top** of the report. Exclude entries whose reply and resolution states are both `not-applicable`, and FIX entries `skipped` with the reason `held until push`, which the main body's held line covers: ``` ## ⚠ GitHub operations needing attention (<count>) @@ -34,6 +34,10 @@ Unattended watch run: committed <yes, <sha> | no, <reason>>; pushed <yes, <sha> posted <n> auto-confirmed contested replies and <n> FIX replies; held <n> FIX threads as fixed locally, not pushed; parked <n> items as NEEDS-INPUT +<Only when UNATTENDED_WATCH is not true and any FIX reply is held:> +Held <n> FIX replies until push. "Push to remote" below posts them once the push lands; + after pushing any other way, rerun `/fix-pr-review <url>` to reply to and resolve those threads. + Convergence: converged. All <F> fixes class-complete, inverse risk absent, no new siblings | NOT converged. <n> of <F> fixes partial or reverted (see below) diff --git a/skills/fix-pr-review/references/github-reply-resolve.md b/skills/fix-pr-review/references/github-reply-resolve.md index e9e7220..86a03da 100644 --- a/skills/fix-pr-review/references/github-reply-resolve.md +++ b/skills/fix-pr-review/references/github-reply-resolve.md @@ -1,6 +1,6 @@ # GitHub reply + resolve mechanics (Phase 7) -Main loads this in Phase 7 after the local-file skip check passes. SKILL.md keeps the skip condition and the batch-failure rule. This file holds the four steps. None of it runs for local-file input. +Main loads this in Phase 7 after the local-file skip check passes, and runs Step 7c again after a landed push to post held FIX replies. SKILL.md keeps the skip condition and the batch-failure rule. This file holds the four steps. None of it runs for local-file input. --- @@ -87,6 +87,8 @@ On failure: dispatch a 1-off `general-purpose` subagent with the original commen ## Step 7c: Post loop +In Phase 7 this loop posts contested items only and holds every FIX item. After a push read-back records the push landed, Phase 8 step 5 or step 2b runs it again over the held FIX items with their frozen `reply_final`, under a new `preflight-mutations` card. + Immediately before the first reply in this authorized batch, invoke `preflight-mutations`. Pass the exact PR URL, base and current head SHA, every target thread ID, ordered reply/resolve actions, final validated reply text and classification per item, and the authorization evidence that covers each item. FIX items use `execution_authorization_evidence`, plus their per-fix confirmation when `--interactive` was set. DISMISS, DEFER, and DISAGREE items use their contested-item confirmation. An auto-confirmed contested item uses `execution_authorization_evidence` plus its Phase 4 auto-confirm record: rule, re-checked field, and result. Apply the result contract before continuing. For each item with a non-null `thread_id` (actionables only, NOT nitpicks, NOT NEEDS-INPUT, NOT `reply_invalid`, and NOT any FIX item Step 7a left without a `reply_final`, i.e. `fix_status ∈ {skipped, aborted, partial, reverted_inverse_risk, inverse_risk_applied}`): diff --git a/skills/fix-pr-review/references/needs-input-triage.md b/skills/fix-pr-review/references/needs-input-triage.md index 0924232..0de8af1 100644 --- a/skills/fix-pr-review/references/needs-input-triage.md +++ b/skills/fix-pr-review/references/needs-input-triage.md @@ -49,9 +49,9 @@ On "Fix it", use a follow-up AskUserQuestion to collect guidance: A Critical or Serious blocker, validation abort, edit failure, exhausted retry, or Phase 6 failure aborts only this item. Before restoring, record the exact triggering error or blocker as `phase8_failure_reason[idx]`. Restore every declared path from `phase8_item_snapshot[idx]`, including removing a path whose snapshot recorded authoritative absence, through the state-preserving restore rule with fallback `restored_failed`; set `needs_input_status[idx]=failed`. Preserve existing convergence evidence and append `restored item snapshot after <phase8_failure_reason>`; when convergence never ran, set `convergence[idx]=not-run, <phase8_failure_reason>; item snapshot restored`. Set paired `gh_status` states to `not-applicable` when `has_github_surface=false` or to `skipped` with `no landed fix, <phase8_failure_reason>; item restored` otherwise. Clear the context and continue with the next independent item without restoring the run-level stash, exiting the skill, or bypassing the renderer. Ordinary Phase 5 and Phase 6 behavior remains unchanged when `phase8_triage_context` is false. - When the fix and verification land cleanly, record current content risk as clear without changing the immutable snapshot state, then update `done_verified_snapshot` from the prior snapshot plus this item's verified declared-path bytes. If `phase8_snapshot_fix_state[idx]=inverse_risk_applied` and `phase8_remediation_kind[idx]=removal`, set `fix_status[idx]=reverted_inverse_risk`, set `needs_input_status[idx]=failed`, set paired `gh_status` states to `not-applicable` when `has_github_surface=false` or to `skipped` with `original finding not accepted, risky fix removed` otherwise, clear `phase8_triage_context`, and rejoin Phase 8 without GitHub reply or resolution. A verified replacement retains its applicable landed status and may proceed to the Phase 7 reply/resolve mechanics. Set `needs_input_status=fixed` only when `fix_status ∈ landed_fix_statuses` and both GitHub operations are successful or not applicable; set it to `reconcile-required` if either operation has that state, otherwise set it to `failed`. Preserve the final classification, `fix_status`, convergence, current-content risk, and `gh_status` before continuing. + When the fix and verification land cleanly, record current content risk as clear without changing the immutable snapshot state, then update `done_verified_snapshot` from the prior snapshot plus this item's verified declared-path bytes. If `phase8_snapshot_fix_state[idx]=inverse_risk_applied` and `phase8_remediation_kind[idx]=removal`, set `fix_status[idx]=reverted_inverse_risk`, set `needs_input_status[idx]=failed`, set paired `gh_status` states to `not-applicable` when `has_github_surface=false` or to `skipped` with `original finding not accepted, risky fix removed` otherwise, clear `phase8_triage_context`, and rejoin Phase 8 without GitHub reply or resolution. A verified replacement retains its applicable landed status. For a landed fix, run Steps 7a and 7b to freeze its `reply_final`, record its reply and resolution as `skipped` with the reason `held until push`, or `not-applicable` when `has_github_surface=false`, and add it to the held FIX set that Phase 8 step 5 and step 2b post after the push lands. Set `needs_input_status` from `fix_status` alone: `fixed` when it is in `landed_fix_statuses`, otherwise `failed`. Preserve the final classification, `fix_status`, convergence, current-content risk, and `gh_status` before continuing. -Immediately before any chosen Fix, Defer, or Dismiss reply mutates GitHub, invoke `preflight-mutations` for that item's reply/resolve batch with the exact PR and current head SHA, target thread ID, final reply text, classification, and the per-item choice above. Name the target thread as the batch's durable home, with the per-item choice as its authorization: thread ID plus expected reply/resolve states, re-readable through the API after every write. Apply its result contract before continuing. Record a blocked or confirmed failure as `needs_input_status=failed`, an indeterminate operation as `reconcile-required`, and the exact Phase 7 `gh_status`; then continue to the next independent item. Every branch rejoins report rendering. +Immediately before a chosen Defer or Dismiss reply mutates GitHub, invoke `preflight-mutations` for that item's reply/resolve batch with the exact PR and current head SHA, target thread ID, final reply text, classification, and the per-item choice above. Name the target thread as the batch's durable home, with the per-item choice as its authorization: thread ID plus expected reply/resolve states, re-readable through the API after every write. Apply its result contract before continuing. Record a blocked or confirmed failure as `needs_input_status=failed`, an indeterminate operation as `reconcile-required`, and the exact Phase 7 `gh_status`; then continue to the next independent item. Every branch rejoins report rendering. On "Defer": set the classification to `DEFER` and run the Phase 7 reply/resolve mechanics. On "Dismiss": do the same with classification `DISMISS`. When `has_github_surface=false`, set both GitHub states to `not-applicable` without entering any GitHub report section. Map the final result for either branch: both required operations successful or not applicable → `needs_input_status` "deferred" or "dismissed"; either operation `reconcile-required` → `reconcile-required`; every other non-success or confirmed failure → `failed`. Preserve every authoritative outcome in `gh_status`, then continue. diff --git a/skills/fix-pr-review/references/push-publication.md b/skills/fix-pr-review/references/push-publication.md index ed33ea1..bea6c58 100644 --- a/skills/fix-pr-review/references/push-publication.md +++ b/skills/fix-pr-review/references/push-publication.md @@ -1,14 +1,14 @@ # Push to remote -Loaded by main in Phase 8 step 5, only when the user picks "Push to remote". The commit prerequisites in SKILL.md step 5 still apply. +Loaded by main in Phase 8 step 5 when the user picks "Push to remote", or in step 2b when an unattended watch run pushes. The commit prerequisites in SKILL.md step 5 still apply. -On "Push to remote", commit first, then require the commit tree to still equal the snapshot before freezing the publication branch and push-attempt SHA. For GitHub PR input, re-read the current PR's `headRefName` plus `headRepository.id` and `nameWithOwner`; freeze those authoritative values, require the Phase 1 expected PR branch and active branch to equal the frozen head name, and set `<exact-ref>` to `refs/heads/<frozen-headRefName>`. That head repository identity is the intended publication target and remains independent of the input/base repository. For local-file or other no-current-PR input, freeze the active branch and derive `<exact-ref>` as `refs/heads/<active-branch>`. Freeze the push-attempt SHA only when the active symbolic ref equals `<exact-ref>` and that branch ref and `HEAD` resolve to the same SHA. Resolve the intended publication target before binding `<preflighted-remote>`. For GitHub PR input, enumerate configured remotes and validate every endpoint in each ordered complete fetch/push set. Auto-select the only remote whose every endpoint matches the PR head identity; when multiple match, immediately use AskUserQuestion with concrete `<remote>: <nameWithOwner> (<id>)` options and pagination when needed; when none match, stop with `Configure a remote whose complete endpoint sets resolve to the current PR head repository, then choose Push to remote again.` The selected match becomes `<preflighted-remote>`. +On "Push to remote", commit first. Step 2b arrives already committed in sealed-index mode. Then require the commit tree to still equal the snapshot before freezing the publication branch and push-attempt SHA. For GitHub PR input, re-read the current PR's `headRefName` plus `headRepository.id` and `nameWithOwner`; freeze those authoritative values, require the Phase 1 expected PR branch and active branch to equal the frozen head name, and set `<exact-ref>` to `refs/heads/<frozen-headRefName>`. That head repository identity is the intended publication target and remains independent of the input/base repository. For local-file or other no-current-PR input, freeze the active branch and derive `<exact-ref>` as `refs/heads/<active-branch>`. Freeze the push-attempt SHA only when the active symbolic ref equals `<exact-ref>` and that branch ref and `HEAD` resolve to the same SHA. Resolve the intended publication target before binding `<preflighted-remote>`. For GitHub PR input, enumerate configured remotes and validate every endpoint in each ordered complete fetch/push set. Auto-select the only remote whose every endpoint matches the PR head identity; when multiple match, immediately use AskUserQuestion with concrete `<remote>: <nameWithOwner> (<id>)` options and pagination when needed; when none match, stop with `Configure a remote whose complete endpoint sets resolve to the current PR head repository, then choose Push to remote again.` The selected match becomes `<preflighted-remote>`. For local-file or other no-current-PR input, immediately follow the "Push to remote" choice with AskUserQuestion. Inventory every configured remote; capture without printing its ordered complete fetch and push endpoint sets, normalize and resolve every endpoint independently through authenticated `gh repo view ... --json id,nameWithOwner`, and retain a remote only when every endpoint resolves to the same repository ID and `nameWithOwner`. Show each retained remote as a concrete option labelled `<remote>: <nameWithOwner> (<id>)`; list rejected remotes with the failing endpoint or set mismatch but never make them selectable. If the option limit cannot hold every retained remote, paginate disjoint sets of concrete options with a `Show next remotes` choice until the user selects one. That selection freezes both complete sets, sets `<preflighted-remote>` and the explicit intended publication target, and joins the preflight authorization and guards. If no remote is valid, stop with: `Configure a remote whose complete fetch and push endpoint sets resolve to one authenticated GitHub repository, then choose Push to remote again.` Capture without printing the ordered complete sets from `git remote get-url --all <preflighted-remote>` and `git remote get-url --push --all <preflighted-remote>`, then normalize every endpoint to a credential-free URL identity. Preserve transport class, host, port, and owner/repository while treating equivalent spellings such as scp-like versus `ssh://`, default ports, trailing slash, and `.git` suffix alike. Independently resolve every normalized endpoint through authenticated `gh repo view ... --json id,nameWithOwner`; require every endpoint ID and `nameWithOwner` to equal the intended publication target. Retain only the ordered normalized sets or their digests and resolved IDs, never credentials. Record the remote name, both complete endpoint sets, and intended target; any addition, removal, reorder, resolution failure, or repository mismatch is `reconcile-required`. The named-remote push may target its configured complete push set only while that frozen set still matches. -Query `<exact-ref>` and record its remote SHA or authoritative absence; when present, require it to be an ancestor of the frozen SHA. Immediately before `git push`, invoke `preflight-mutations` with the exact remote, both ordered complete endpoint sets and repository IDs, intended publication target, `<exact-ref>`, frozen local SHA, expected remote SHA or absence, upstream state, commit range, dependent refs, and the user's choice. Include the PR base, frozen `headRefName`, `<exact-ref>`, and head repository ID in its guards and invalidators when a PR exists. Include the active symbolic ref, branch-ref SHA, `HEAD`, frozen publication branch and exact ref, and complete selected-remote identity in those guards and invalidators, then re-enumerate and compare both endpoint sets before running the exact push: +Query `<exact-ref>` and record its remote SHA or authoritative absence; when present, require it to be an ancestor of the frozen SHA. Immediately before `git push`, invoke `preflight-mutations` with the exact remote, both ordered complete endpoint sets and repository IDs, intended publication target, `<exact-ref>`, frozen local SHA, expected remote SHA or absence, upstream state, commit range, dependent refs, and the authorization source: the user's choice, or in step 2b the watch's standing instruction. Include the PR base, frozen `headRefName`, `<exact-ref>`, and head repository ID in its guards and invalidators when a PR exists. Include the active symbolic ref, branch-ref SHA, `HEAD`, frozen publication branch and exact ref, and complete selected-remote identity in those guards and invalidators, then re-enumerate and compare both endpoint sets before running the exact push: ```bash git push --force-with-lease="<exact-ref>:<expected-sha-or-empty>" "<preflighted-remote>" "<frozen-local-sha>:<exact-ref>" @@ -17,4 +17,4 @@ git ls-remote --heads "<preflighted-remote>" "<exact-ref>" After every attempted push, including a nonzero or ambiguous exit, establish remote state first: re-enumerate both complete endpoint sets and run the exact `git ls-remote` read-back for `<exact-ref>` once. Record the push as landed only when the authoritative remote SHA equals the frozen SHA, the ordered sets are unchanged, and every endpoint still equals the intended publication target. An old or unexpected SHA, unavailable read-back, added, removed, reordered, unresolved, or mismatched endpoint is `reconcile-required`; stop and never retry from the command result alone. -After preserving the remote result, re-read the active symbolic ref, frozen publication branch-ref SHA, and `HEAD`. Continue only when the symbolic ref equals `<exact-ref>` and both local SHAs equal the frozen SHA. When the remote landed but any local value moved, record `remote landed / local publication moved`, set overall publication to `reconcile-required`, and stop without retrying the push. When the remote did not land, preserve its observed state and stop as `reconcile-required` regardless of local state. Only after both remote and local state pass may a GitHub PR input read `headRefName`, `headRefOid`, and `headRepository.id`; require them to equal the frozen head name, frozen SHA, and intended publication target ID. +After preserving the remote result, re-read the active symbolic ref, frozen publication branch-ref SHA, and `HEAD`. Continue only when the symbolic ref equals `<exact-ref>` and both local SHAs equal the frozen SHA. When the remote landed but any local value moved, record `remote landed / local publication moved`, set overall publication to `reconcile-required`, and stop without retrying the push. When the remote did not land, preserve its observed state and stop as `reconcile-required` regardless of local state. Only after both remote and local state pass may a GitHub PR input read `headRefName`, `headRefOid`, and `headRepository.id`; require them to equal the frozen head name, frozen SHA, and intended publication target ID. Only a push that passes all of these counts as landed and releases the held FIX replies. diff --git a/skills/fix-ts-errors/SKILL.md b/skills/fix-ts-errors/SKILL.md index 1f8388c..d4774be 100644 --- a/skills/fix-ts-errors/SKILL.md +++ b/skills/fix-ts-errors/SKILL.md @@ -46,11 +46,11 @@ git ls-files --others --exclude-standard -- '*.ts' '*.tsx' | tr '\n' '\0' | xarg The `[!]` groups match a postfix assertion: a value character, closing quote, bracket, or paren before the bang and no equals after it, so `value!`, `value!.x`, `value![0]`, `value!()`, `"value"!`, and backtick-quoted receivers all hit while `!=`, `!==`, and prefix negation like `!ready` stay out. The trailing `sed` neutralizes only the `as const` idiom into a marker instead of dropping whole lines, so co-occurring hatches on the same line survive. A line whose only match is the `__AS_CONST__` marker needs no action. -I skip import aliases like `import { x as y }`. For every remaining hit I attempt removal with proper typing, meaning inference, narrowing, type guards, generics, or schema-derived types with `z.infer`. I re-run the check after each removal, and when errors appear I go back to Step 3. A hatch survives only with a one-line comment citing the upstream issue as a URL or `#N`. When no upstream issue exists, the comment cites a permalink URL to the line in the library's type definition that causes the gap. +I skip import aliases like `import { x as y }`. For every remaining hit I attempt removal with proper typing, meaning inference, narrowing, type guards, generics, or schema-derived types with `z.infer`. I re-run the check after each removal, and when errors appear I go back to Step 3. A hatch survives only with a one-line comment citing the upstream issue as a URL or `#N`. When no upstream issue exists, the comment cites a permalink URL to the line in the library's type definition that causes the gap. A `@ts-expect-error` in a test file (`*.test.*`, `*.spec.*`, `*.test-d.ts`) whose enclosing test's name states that the compiler rejects that line is a type test, not a hatch. The directive is code, not a comment, and stays with no added comment. ### Step 6: Report -Once green, I briefly report how many errors I found, what I fixed, and the surviving escape hatches, `as` casts, `@ts-ignore`, `@ts-expect-error`, and non-null assertions, with the count of each and the citation for each survivor. My target is 0. +Once green, I briefly report how many errors I found, what I fixed, and the surviving escape hatches, `as` casts, `@ts-ignore`, `@ts-expect-error`, and non-null assertions, with the count of each and the citation for each survivor. My target is 0. I report type tests separately, as "type tests" with their count. ## Loop breaker diff --git a/skills/openclaw-backup/SKILL.md b/skills/openclaw-backup/SKILL.md index dcc0f8e..adb898c 100644 --- a/skills/openclaw-backup/SKILL.md +++ b/skills/openclaw-backup/SKILL.md @@ -163,7 +163,9 @@ A backup on the same disk as the thing it protects survives config mistakes, not Before `rsync`, verify from the receiving system that the destination is encrypted at rest, accessible only to the intended recipient accounts, and governed by a known retention period and tested deletion path. Confirm the destination host key against a known-good value before the first transfer, so a mistyped host or a first-connect interception cannot divert the copy. A destination missing any of those controls is blocked. -Immediately before `rsync`, invoke `preflight-mutations` with that evidence, the exact source and destination host and path, included artifact inventory and credential sensitivity, recipient, retention and deletion path, current destination state, checksum and access-control read-back, and the user's off-box-copy approval. Apply its result contract before copying. +Decide before the preflight whether the official archive goes off-box. Pull it unless this run lists its contents with `tar -tzf` and confirms every path in it appears in a fresh listing of the raw archive. When omitted, add `--exclude='official/'` to the `rsync` below. Record in the report which applied. + +Immediately before `rsync`, invoke `preflight-mutations` with the destination evidence, the exact source and destination host and path, included artifact inventory with `official/` in or out per that decision, credential sensitivity, recipient, retention and deletion path, current destination state, checksum and access-control read-back, and the user's off-box-copy approval. Apply its result contract before copying. ```bash rsync -avh --partial -e ssh <source> <destination> @@ -172,7 +174,7 @@ rsync -avh --partial -e ssh <source> <destination> Verify on the receiving side against the manifest: ```bash -grep -v ' \./official/' MANIFEST.sha256 > /tmp/check.sha256 # if official/ was not pulled +grep -v ' \./official/' MANIFEST.sha256 > /tmp/check.sha256 # only when official/ was omitted above sha256sum -c /tmp/check.sha256 # macOS: shasum -a 256 -c ``` @@ -184,8 +186,6 @@ Re-read the receiving system's encryption and effective access controls after th Pass this step only when every checked file reports `OK`, encryption at rest remains enabled, effective access stays restricted to the approved recipient accounts, and the retention and deletion path remains available. A mismatched file clears the gate only through the fresh-card retry contract above. A failed security control blocks completion while the sensitive partial state is preserved or separately deleted. `--partial` makes the new transfer resume. -You may omit the official archive from the pull, since the raw archive holds all of its contents. State the omission in the report. - ## Step 9: Report ``` @@ -204,6 +204,7 @@ Artifacts (<total size>, mode 0700): MANIFEST.sha256 + RESTORE.md Official archive skipped <N> volatile files, covered by the raw archive. +Official archive off-box: <pulled | omitted, every listed path found in the raw archive listing> (only when Step 8 ran) VERIFIED: <what was run, and the evidence each produced> ASSUMED: <what was inspected but not executed> diff --git a/skills/parallel-review/SKILL.md b/skills/parallel-review/SKILL.md index 60cc9bc..ca8c73c 100644 --- a/skills/parallel-review/SKILL.md +++ b/skills/parallel-review/SKILL.md @@ -22,7 +22,7 @@ I name the roster first, then launch every member in parallel with the Agent too Every member is an Agent tool `subagent_type`. `/web-interface-guidelines` and `/rams` are skills, not agent types, so member 4 is a `general-purpose` agent prompted to invoke them. -On a host without Claude Code agent types, such as Codex or OpenCode, I dispatch each member as the host's general agent (Codex `spawn_agent`, OpenCode `general`) with that member's lens in the prompt. The CodeRabbit member runs `coderabbit review --agent --uncommitted --include-untracked`, which reviews tracked uncommitted changes plus untracked files, and returns its findings. When Step 1 named specific files or a fix delta, I keep only its findings on those paths. It never passes `--base-commit`, which would widen the review past that scope. +On a host without Claude Code agent types, such as Codex or OpenCode, I dispatch each member as the host's general agent (Codex `spawn_agent`, OpenCode `general`) with that member's lens in the prompt. The CodeRabbit member runs `coderabbit review --agent --uncommitted --include-untracked`, which reviews tracked uncommitted changes plus untracked files, and returns its findings. When Step 1 named specific files or a fix delta, I check what the run would upload. `git status --porcelain` lists files, not hunks, so I read the diff of every changed or untracked file it lists. When every change lies inside the scoped delta, I run as above and keep only its findings on those paths. When the out-of-scope changes all sit outside one directory that holds every scoped file, I add `--dir <dir>`. When any file the run would upload holds a change outside the scoped delta, the member is `unavailable(scope cannot be restricted)` and does not run, so nothing outside the scope leaves the machine. It never passes `--base-commit`, which would widen the review past that scope. On every host, a member whose run exits non-zero, hits an auth or rate limit, times out, or returns empty output is `unavailable(<reason>)`. A missing CodeRabbit CLI counts the same way. I never fill an unavailable member's place with a stand-in. diff --git a/skills/project-discovery/references/stack-menu.md b/skills/project-discovery/references/stack-menu.md index e61fc4a..440ab32 100644 --- a/skills/project-discovery/references/stack-menu.md +++ b/skills/project-discovery/references/stack-menu.md @@ -128,7 +128,7 @@ How frontend talks to backend. - REST: Universal, cacheable, well understood - tRPC: End-to-end type safety, Next.js native - GraphQL: Flexible queries, higher complexity -- Server Actions: Next.js 16+, simplest for forms +- Server Actions: Next.js 14+, simplest for forms **Validation** - Zod: TypeScript-first, great inference diff --git a/skills/resolving-merge-conflicts/SKILL.md b/skills/resolving-merge-conflicts/SKILL.md index 489c205..7848504 100644 --- a/skills/resolving-merge-conflicts/SKILL.md +++ b/skills/resolving-merge-conflicts/SKILL.md @@ -3,7 +3,7 @@ name: resolving-merge-conflicts description: "Resolve an in-progress git conflict without a silent drop. Use when the worktree is mid-merge, mid-rebase, mid-cherry-pick or mid-stash-pop: `CONFLICT (content)`, `<<<<<<<` markers, `Unmerged paths`, or `fix conflicts and then commit the result`. Also use when asked to resolve or fix merge conflicts on a PR or branch before any merge has started." --- -1. Orient before you touch anything. Establish from `git status` whether you are in a merge, a rebase, a cherry-pick, or a stash pop. When none is in progress, merge the base branch into the head branch, then orient. Rebase only when the user asked for one, because rebasing a published branch rewrites its history. Under a rebase `ours` is the upstream you are replaying onto and `theirs` is your own commit, the inverse of a merge, and getting this backwards is the most common wrong resolution. +1. Orient before you touch anything. Establish from `git status` whether you are in a merge, a rebase, a cherry-pick, a revert, a `git am`, or a stash pop. Unmerged paths with none of the other five in progress mean a conflicting stash pop; confirm it with `git stash list` and the request, and ask the user when their origin is unclear. When none is in progress and nothing is unmerged, start a merge only from a clean worktree. When `git status --porcelain` lists anything, stop and ask the user to commit it, stash it, or move it to a separate worktree; never stash it yourself. For a PR, the base repository is the PR URL's `<owner>/<repo>`; read the base ref, its tip, the head branch, and the head repository with `gh pr view <url> --json baseRefName,baseRefOid,headRefName,headRepository`. For a named branch with no PR, the base is the default branch from `gh repo view --json defaultBranchRef`. When the head branch, `headRefName` for a PR, is not checked out, stop and ask. Fetch from the remote that resolves to the base repository, require the fetched base ref to equal `baseRefOid` when a PR was named, merge that remote's base ref into the head branch, and orient. Rebase only when the user asked for one, because rebasing a published branch rewrites its history. Under a rebase `ours` is the upstream you are replaying onto and `theirs` is your own commit, the inverse of a merge, and getting this backwards is the most common wrong resolution. Then split the conflicted files. Regenerate generated artifacts and lockfiles from source, never hand-merge them. That means `bun.lock`, `pnpm-lock.yaml`, `_generated/`, and snapshots. Only hand-written files go through steps 2-3. diff --git a/skills/review-pr/SKILL.md b/skills/review-pr/SKILL.md index 1d70652..581b3ab 100644 --- a/skills/review-pr/SKILL.md +++ b/skills/review-pr/SKILL.md @@ -625,7 +625,7 @@ An explicit `/review-pr <PR URL>` invocation is fresh authorization to submit th Load `${CLAUDE_SKILL_DIR}/references/github-posting.md` now: rendering every body with `scripts/render_review.py`, Phases A through C, and the write-back. Also load `${CLAUDE_SKILL_DIR}/references/github-posting-rerun.md` when a prior `/review-pr` review or cache entry exists for this PR, and `${CLAUDE_SKILL_DIR}/references/github-posting-recovery.md` if any posting phase fails. -Pass into the reference: `<owner>`, `<repo>`, `<pr-num>`, `<head_sha>`, `CURRENT_ROUND`, `IS_SELF_REVIEW`, a new render-directory path inside an existing directory (the renderer creates it in Step 1 of `github-posting.md` and refuses a path that already exists), the complete surviving finding list (line-level + file-level), `PRIOR_STATE` (Step 0c in the rerun file compares against it), `$CACHE_FILE` path, `$STATE_FILE` path, and the `/review-pr` invocation as the posting authorization source. +Pass into the reference: `<owner>`, `<repo>`, `<pr-num>`, `<head_sha>`, `CURRENT_ROUND`, `IS_SELF_REVIEW`, `INCOMPLETE_COVERAGE` (`false` outside batch mode), a new render-directory path inside an existing directory (the renderer creates it in Step 1 of `github-posting.md` and refuses a path that already exists), the complete surviving finding list (line-level + file-level), `PRIOR_STATE` (Step 0c in the rerun file compares against it), `$CACHE_FILE` path, `$STATE_FILE` path, and the `/review-pr` invocation as the posting authorization source. ### Convergence handoff diff --git a/skills/review-pr/references/batch-mode.md b/skills/review-pr/references/batch-mode.md index 64276e7..291d134 100644 --- a/skills/review-pr/references/batch-mode.md +++ b/skills/review-pr/references/batch-mode.md @@ -11,7 +11,7 @@ For "all open PRs", enumerate via `gh pr list --json number,url,title --limit 50 - Main context is the **orchestrator**: oversight only. It never reviews a PR inline, regardless of `SIZE_MODE` (size routing applies inside each subagent, not in main). - Spawn **ONE `general-purpose` subagent PER PR**. Each subagent runs the single-PR flow (Phases 1-3) independently against its own PR and returns its Phase 4 terminal block as its result. Dispatch in parallel batches of 3-4. - Subagents NEVER post to GitHub and NEVER ask questions. They return the complete surviving finding set, semantic verdict, and `IS_SELF_REVIEW` value to the orchestrator, which posts each review after every subagent has returned. -- When any nested dispatch from a per-PR subagent fails, it runs inline, in order, every lens its `SIZE_MODE` and flags would have dispatched: Subagent 1, Subagent 2 when `INCLUDE_SILENT_FAILURE_HUNTER = true`, Subagent 3 only in `parallel-chunked`, Subagent 4 when `INCLUDE_SECURITY_REVIEWER = true`, then the Phase 3 verifier checks. Its Timing block writes `Reviewers: inline (host blocks nested dispatch)` and marks each lens `run` or `skipped (<reason>)`. When a required lens could not run, the orchestrator posts `COMMENT` instead of `APPROVE` and names that lens and the reason in the body. +- When any nested dispatch from a per-PR subagent fails, it runs inline, in order, every lens its `SIZE_MODE` and flags would have dispatched: Subagent 1, Subagent 2 when `INCLUDE_SILENT_FAILURE_HUNTER = true`, Subagent 3 only in `parallel-chunked`, Subagent 4 when `INCLUDE_SECURITY_REVIEWER = true`, then the Phase 3 verifier checks. Its Timing block writes `Reviewers: inline (host blocks nested dispatch)` and marks each lens `run` or `skipped (<reason>)`. The orchestrator sets `INCOMPLETE_COVERAGE=true` for a PR whose required lens could not run, `false` for every other PR, and passes it to the posting flow, which posts `COMMENT` instead of `APPROVE` when it is true. The orchestrator writes that lens and the reason into the review JSON's `summary`, since `render_review.py` has no lens field. ### `<SKILL_DIR>` on the batch branch @@ -42,14 +42,14 @@ The run continues unattended through the WHOLE list. The user may be away in bat - Stop-and-ask intent gap → review with just the diff; tag that PR's report `intent not grounded, findings may be generic`. - A PR over 2000 lines gets a chunked review. Note the size in that PR report header. -- Completed review → queue every surviving finding for automatic posting. Queue a clean review with no open blockers for automatic approval, or for a comment when `IS_SELF_REVIEW=true`. A review whose only surviving findings are pre-existing counts as clean. Queue a clean review with open blockers for automatic `REQUEST_CHANGES` on the blocked-by-open-threads path (Phase 3 step 8). +- Completed review → queue every surviving finding for automatic posting. Queue a clean review with no open blockers for automatic approval, or for a comment when `IS_SELF_REVIEW=true` or `INCOMPLETE_COVERAGE=true`. A review whose only surviving findings are pre-existing counts as clean. Queue a clean review with open blockers for automatic `REQUEST_CHANGES` on the blocked-by-open-threads path (Phase 3 step 8). - A failed subagent doesn't stop the batch: record `<pr>: review failed (<reason>)` in the consolidated report and continue with the rest. --- ## Automatic posting -After every subagent returns, post each completed review in list order through the single-PR GitHub posting flow. For another author's PR, post a review with surviving introduced findings with `REQUEST_CHANGES`, a clean review with no open blockers with `APPROVE` (its pre-existing findings ride along as threads; `COMMENT` when a required lens did not run), and a clean review with open blockers on the blocked-by-open-threads path (Phase 3 step 8). For a self-review, post the complete result with `COMMENT`. Invoke `preflight-mutations` separately for each PR immediately before its first mutation, using the batch `/review-pr` request as the authorization source. Reconcile and record each result before moving to the next PR. Record each PR's reconciled `https://github.com/<owner>/<repo>/pull/<num>#pullrequestreview-<REVIEW_DB_ID>` (or `not posted (<reason>)` when nothing landed) and carry it into that PR's `**Review URL**` line, its table row, and the consolidated posting-status entry. +After every subagent returns, post each completed review in list order through the single-PR GitHub posting flow. For another author's PR, post a review with surviving introduced findings with `REQUEST_CHANGES`, a clean review with no open blockers with `APPROVE` (its pre-existing findings ride along as threads; `COMMENT` when `INCOMPLETE_COVERAGE=true`), and a clean review with open blockers on the blocked-by-open-threads path (Phase 3 step 8). For a self-review, post the complete result with `COMMENT`. Invoke `preflight-mutations` separately for each PR immediately before its first mutation, using the batch `/review-pr` request as the authorization source. Reconcile and record each result before moving to the next PR. Record each PR's reconciled `https://github.com/<owner>/<repo>/pull/<num>#pullrequestreview-<REVIEW_DB_ID>` (or `not posted (<reason>)` when nothing landed) and carry it into that PR's `**Review URL**` line, its table row, and the consolidated posting-status entry. A posting failure never asks immediately, and the `SKILL.md` unattended rule applies here only when its own trigger fires. Record the exact partial GitHub state and the recovery choices from `${CLAUDE_SKILL_DIR}/references/github-posting-recovery.md` as `recovery-pending`, then continue with every untouched PR. @@ -67,7 +67,7 @@ After automatic posting has attempted every completed review and recorded each c <one row per PR; "review failed" rows included; Reviewers is `dispatched` or `inline`, plus any skipped lens> ## Posting status -<one entry per PR: posted request-changes + review URL, posted approve + review URL, posted self-review comment + review URL, review failed, posting failed, or recovery-pending> +<one entry per PR: posted request-changes + review URL, posted approve + review URL, posted self-review comment + review URL, posted incomplete-coverage comment + review URL, review failed, posting failed, or recovery-pending> ## Pending review context (<count>) <one entry per review-only checkpoint that could not be resolved, such as: diff --git a/skills/review-pr/references/finding-state-schema.md b/skills/review-pr/references/finding-state-schema.md index 8234e33..131d07c 100644 --- a/skills/review-pr/references/finding-state-schema.md +++ b/skills/review-pr/references/finding-state-schema.md @@ -229,6 +229,8 @@ REVIEW_CACHE_CONTRACT_VERSION=3 "base_sha": "def456...", "last_run_timestamp": "2026-04-11T13:29:50Z", "last_run_verdict": "request-changes", + "incomplete_coverage": false, + "skipped_lenses": [], "findings": [...], "filtered_out": [...], "last_posted_review_id": 12345678, @@ -253,7 +255,7 @@ REVIEW_CACHE_CONTRACT_VERSION=3 ### Three replay branches -These branches apply only after the contract-version check succeeds. +These branches apply only after the contract-version check succeeds. A cache recording `incomplete_coverage: true` skips branches 1 and 2 for a full fresh review. 1. **`last_run_sha == CURRENT_HEAD` with stored `base_sha` equal to the pinned base OID**: no new commits on either side. Reuse the cached review result, subject to the blocker check below. If authoritative cache evidence confirms the same review body, head SHA, required GitHub state, and threaded publication ownership are already posted, print the cached result and exit. Otherwise continue directly to Phase 4 and post the cached complete finding set; do not ask whether to replay or post. diff --git a/skills/review-pr/references/github-posting-rerun.md b/skills/review-pr/references/github-posting-rerun.md index 27ff8d3..e1a6d48 100644 --- a/skills/review-pr/references/github-posting-rerun.md +++ b/skills/review-pr/references/github-posting-rerun.md @@ -65,7 +65,7 @@ The state and ownership checks keep body-only re-reviews compact without droppin ## Step 0b: Verdict-body sync check (re-runs) -If `last_posted_review_id` exists in cache, map the last-posted body verdict through the current self-review status before comparing it with GitHub state: +If `last_posted_review_id` exists in cache, map the last-posted body verdict through the current self-review status and the cache's `incomplete_coverage` before comparing it with GitHub state: ```bash LAST_POSTED_REVIEW_ID=$(jq -r '.last_posted_review_id // empty' "$CACHE_FILE") @@ -83,6 +83,9 @@ if [ -n "$LAST_POSTED_REVIEW_ID" ]; then if [ "$IS_SELF_REVIEW" = "true" ] && [ "$LAST_POSTED_BODY_REQUIRED_STATE" != "UNKNOWN" ]; then LAST_POSTED_BODY_REQUIRED_STATE=COMMENTED fi + if [ "$(jq -r '.incomplete_coverage // false' "$CACHE_FILE")" = "true" ] && [ "$LAST_POSTED_BODY_REQUIRED_STATE" = "APPROVED" ]; then + LAST_POSTED_BODY_REQUIRED_STATE=COMMENTED + fi fi ``` diff --git a/skills/review-pr/references/github-posting.md b/skills/review-pr/references/github-posting.md index 07533bf..1c5d497 100644 --- a/skills/review-pr/references/github-posting.md +++ b/skills/review-pr/references/github-posting.md @@ -6,7 +6,7 @@ Main loads this in Phase 4 for every completed review. It owns the fresh-review ## Event mapping -Before posting, map the semantic verdict to its normal event and required GitHub state, then override both for a self-review: +Before posting, map the semantic verdict to its normal event and required GitHub state, then override both for a self-review and for an approval with incomplete coverage. Batch mode sets `INCOMPLETE_COVERAGE` for every PR; a single-PR run sets it to `false`: ```bash case "$verdict" in @@ -20,6 +20,11 @@ if [ "$IS_SELF_REVIEW" = "true" ]; then REQUIRED_REVIEW_STATE=COMMENTED fi +if [ "${INCOMPLETE_COVERAGE:?}" = "true" ] && [ "$REVIEW_EVENT" = "APPROVE" ]; then + REVIEW_EVENT=COMMENT + REQUIRED_REVIEW_STATE=COMMENTED +fi + case "$REVIEW_EVENT" in APPROVE) REVIEW_FLAG=--approve ;; REQUEST_CHANGES) REVIEW_FLAG=--request-changes ;; @@ -200,7 +205,7 @@ elif [ "$SUBMIT_RESPONSE_HAS_ERRORS" = "true" ] \ fi ``` -**Event mapping**: another author's `approve` verdict uses `APPROVE`, and `request-changes` uses `REQUEST_CHANGES`. A self-review uses `COMMENT` for either semantic verdict because GitHub forbids authors from approving their own pull requests. +**Event mapping**: another author's `approve` verdict uses `APPROVE`, and `request-changes` uses `REQUEST_CHANGES`. A self-review uses `COMMENT` for either semantic verdict because GitHub forbids authors from approving their own pull requests. An `approve` with `INCOMPLETE_COVERAGE=true`, which batch mode sets when a required lens did not run, also uses `COMMENT`. A Phase C failure is the worst case: pending review has all threads but is never submitted, lingering as a draft. diff --git a/skills/review-pr/references/phase1-timeline-state.md b/skills/review-pr/references/phase1-timeline-state.md index 2e8fdd3..ab5de1c 100644 --- a/skills/review-pr/references/phase1-timeline-state.md +++ b/skills/review-pr/references/phase1-timeline-state.md @@ -135,4 +135,4 @@ Use the `CURRENT_HEAD` pinned in Phase 1. Do not re-read it here: a fresh read c Use `REVIEW_CACHE_CONTRACT_VERSION` from `references/finding-state-schema.md`, already loaded for the review-state read. Validate `contract_version` before reading any cache field. A missing or mismatched version invalidates the complete cache and starts a full fresh review. For a current cache, comparing `last_run_sha` to `CURRENT_HEAD` and the stored `base_sha` to the pinned base OID selects one of three branches: replay the cached run unchanged, re-review only the new commits, or invalidate and start fresh. The cache schema and the full body of each branch live in that reference under "Run-over-run cache". -After a successful run, write `contract_version: REVIEW_CACHE_CONTRACT_VERSION` with the result plus `last_run_sha` and `base_sha` (the pinned base OID) in `$CACHE_FILE` at the end of Phase 4. The cache stays local and never touches GitHub state. +After a successful run, write `contract_version: REVIEW_CACHE_CONTRACT_VERSION` with the result plus `last_run_sha`, `base_sha` (the pinned base OID), and `incomplete_coverage` with `skipped_lenses` (`true` and each skipped lens with its reason when `INCOMPLETE_COVERAGE=true`, else `false` and `[]`) in `$CACHE_FILE` at the end of Phase 4. The cache stays local and never touches GitHub state. diff --git a/skills/simplify/SKILL.md b/skills/simplify/SKILL.md index 47af1f5..efc692a 100644 --- a/skills/simplify/SKILL.md +++ b/skills/simplify/SKILL.md @@ -27,7 +27,7 @@ I prefer deletion and direct code over a new helper. I reuse an existing local p ### Added-comment scan -I inspect every comment the change adds under the global comment rule. A comment stays only when it is a one-line `/** */` contract on an exported symbol, or a one-line citation carrying a URL, a spec section, an ADR or `docs/` path, or an issue number. I delete everything else on sight, including any comment that explains what the code does, why it has its shape, or what would break. I run the rule's grep over the diff and delete every comment it prints. A project rule may tighten this scan, never loosen it. +I inspect every comment the change adds under the global comment rule. A comment stays only when it is a one-line `/** */` contract on an exported symbol, or a one-line citation carrying a URL, a spec section, an ADR or `docs/` path, or an issue number. I delete everything else on sight, including any comment that explains what the code does, why it has its shape, or what would break. I run the rule's grep over the diff and delete every comment it prints, except a bare `// @ts-expect-error` in a test file whose enclosing test's name states that the compiler rejects that line, which is a `fix-ts-errors` type test and code, not a comment. A project rule may tighten this scan, never loosen it. Every proposed edit names what turns simpler and why observable behavior stays equivalent, and no uncited comment the change adds remains. This scan blocks. The caller cannot report the work complete while one remains in the diff. diff --git a/skills/triage-board/SKILL.md b/skills/triage-board/SKILL.md index acb91a3..99e074b 100644 --- a/skills/triage-board/SKILL.md +++ b/skills/triage-board/SKILL.md @@ -64,7 +64,7 @@ I apply the board's ownership policy unprompted. A co-assigned, unassigned, or a I ask **once**, in a single question, before judging the batch. `${CLAUDE_SKILL_DIR}/references/calibration.md` carries the defaults I propose: the estimate unit, scale, rounding and cap; worked anchors spanning the range; the priority rubric; and any project-specific priority lift the requester wants for this run. -The estimate unit defaults to human active time, which `calibration.md` defines and anchors. +The estimate unit defaults to human active time, which `${CLAUDE_SKILL_DIR}/references/calibration.md` defines and anchors. An approved rubric is not an approved number. So I then estimate three to five real tickets from this scope, covering the smallest, the typical, the largest, and any umbrella, and show them with their reasoning and subtotal. A scope with fewer than three candidates previews all of them. A scope with none skips calibration, judgment, and every write, and goes straight to a report with zero totals and its exclusion list. Ledger rows move from `candidate` to `pending` only after the requester approves the preview. Without it, the first applied number they see arrives after every write has landed. From 1729bd1e77aa136067ec3f56a58d36f61989be9f Mon Sep 17 00:00:00 2001 From: Bhagya Mudgal <bhagyamudgal@gmail.com> Date: Tue, 10 Nov 2026 14:38:23 +0530 Subject: [PATCH 3/8] fix(skills): correct five rules the review-fix commit got wrong - Write the full checksum list when the official OpenClaw archive is pulled - Let an explicitly named base branch win when resolving merge conflicts - Make simplify and fix-ts-errors accept the same type-test directive forms - Identify batched issue commits from the card, not a message convention - Post held fix-pr-review replies only while the PR head still equals the landed push --- skills/file-pr/SKILL.md | 2 +- skills/fix-pr-review/references/github-reply-resolve.md | 2 +- skills/fix-pr-review/references/needs-input-triage.md | 2 +- skills/fix-pr-review/references/push-publication.md | 2 +- skills/fix-ts-errors/SKILL.md | 2 +- skills/openclaw-backup/SKILL.md | 5 +++-- skills/resolving-merge-conflicts/SKILL.md | 2 +- skills/simplify/SKILL.md | 2 +- 8 files changed, 10 insertions(+), 9 deletions(-) diff --git a/skills/file-pr/SKILL.md b/skills/file-pr/SKILL.md index 6c90077..7b654b9 100644 --- a/skills/file-pr/SKILL.md +++ b/skills/file-pr/SKILL.md @@ -29,7 +29,7 @@ Choose exactly one issue link: Never `Closes` an issue this diff does not finish. -When the user asked to batch several issues into one PR, each issue arrives as its own commit, verified by its own `done` run before the next issue starts. Before writing more than one `Closes #N`, confirm for each issue that the card holds a `verified` request row for it and that it has its own commit in `git log <merge-base>..HEAD` whose message contains `#N`. At most one batched issue may instead rely on the sealed commit section 6 will create for its row. An issue missing either gets `Refs #N`, and the final report names which is missing. The body carries one link line per issue: `Closes #N` for every issue the diff finishes that passed this check, and the list above for any other. +When the user asked to batch several issues into one PR, each issue arrives as its own commit, verified by its own `done` run before the next issue starts. Before writing more than one `Closes #N`, confirm for each issue that the card holds a `verified` request row for it and that the row's evidence names its own commit: a SHA in `git log <merge-base>..HEAD` that no other batched issue's row names. At most one batched issue may instead rely on the sealed commit section 6 will create for its row. An issue missing either gets `Refs #N`, and the final report names which is missing. The body carries one link line per issue: `Closes #N` for every issue the diff finishes that passed this check, and the list above for any other. When you choose `Refs #N` over a closing keyword, the final report says the issue will not show as linked in GitHub's Development panel, which lists only closing-keyword links, and names the reason from the list above. diff --git a/skills/fix-pr-review/references/github-reply-resolve.md b/skills/fix-pr-review/references/github-reply-resolve.md index 86a03da..8529e27 100644 --- a/skills/fix-pr-review/references/github-reply-resolve.md +++ b/skills/fix-pr-review/references/github-reply-resolve.md @@ -87,7 +87,7 @@ On failure: dispatch a 1-off `general-purpose` subagent with the original commen ## Step 7c: Post loop -In Phase 7 this loop posts contested items only and holds every FIX item. After a push read-back records the push landed, Phase 8 step 5 or step 2b runs it again over the held FIX items with their frozen `reply_final`, under a new `preflight-mutations` card. +In Phase 7 this loop posts contested items only and holds every FIX item. After a push read-back records the push landed, Phase 8 step 5 or step 2b runs it again over the held FIX items with their frozen `reply_final`, under a new `preflight-mutations` card that guards on the landed push SHA. Immediately before each reply and each resolve, require the PR head to equal that SHA. When it moved, post nothing more, record each remaining held item as `skipped` with the reason `PR head moved after push`, and report them. Immediately before the first reply in this authorized batch, invoke `preflight-mutations`. Pass the exact PR URL, base and current head SHA, every target thread ID, ordered reply/resolve actions, final validated reply text and classification per item, and the authorization evidence that covers each item. FIX items use `execution_authorization_evidence`, plus their per-fix confirmation when `--interactive` was set. DISMISS, DEFER, and DISAGREE items use their contested-item confirmation. An auto-confirmed contested item uses `execution_authorization_evidence` plus its Phase 4 auto-confirm record: rule, re-checked field, and result. Apply the result contract before continuing. diff --git a/skills/fix-pr-review/references/needs-input-triage.md b/skills/fix-pr-review/references/needs-input-triage.md index 0de8af1..69cac4d 100644 --- a/skills/fix-pr-review/references/needs-input-triage.md +++ b/skills/fix-pr-review/references/needs-input-triage.md @@ -49,7 +49,7 @@ On "Fix it", use a follow-up AskUserQuestion to collect guidance: A Critical or Serious blocker, validation abort, edit failure, exhausted retry, or Phase 6 failure aborts only this item. Before restoring, record the exact triggering error or blocker as `phase8_failure_reason[idx]`. Restore every declared path from `phase8_item_snapshot[idx]`, including removing a path whose snapshot recorded authoritative absence, through the state-preserving restore rule with fallback `restored_failed`; set `needs_input_status[idx]=failed`. Preserve existing convergence evidence and append `restored item snapshot after <phase8_failure_reason>`; when convergence never ran, set `convergence[idx]=not-run, <phase8_failure_reason>; item snapshot restored`. Set paired `gh_status` states to `not-applicable` when `has_github_surface=false` or to `skipped` with `no landed fix, <phase8_failure_reason>; item restored` otherwise. Clear the context and continue with the next independent item without restoring the run-level stash, exiting the skill, or bypassing the renderer. Ordinary Phase 5 and Phase 6 behavior remains unchanged when `phase8_triage_context` is false. - When the fix and verification land cleanly, record current content risk as clear without changing the immutable snapshot state, then update `done_verified_snapshot` from the prior snapshot plus this item's verified declared-path bytes. If `phase8_snapshot_fix_state[idx]=inverse_risk_applied` and `phase8_remediation_kind[idx]=removal`, set `fix_status[idx]=reverted_inverse_risk`, set `needs_input_status[idx]=failed`, set paired `gh_status` states to `not-applicable` when `has_github_surface=false` or to `skipped` with `original finding not accepted, risky fix removed` otherwise, clear `phase8_triage_context`, and rejoin Phase 8 without GitHub reply or resolution. A verified replacement retains its applicable landed status. For a landed fix, run Steps 7a and 7b to freeze its `reply_final`, record its reply and resolution as `skipped` with the reason `held until push`, or `not-applicable` when `has_github_surface=false`, and add it to the held FIX set that Phase 8 step 5 and step 2b post after the push lands. Set `needs_input_status` from `fix_status` alone: `fixed` when it is in `landed_fix_statuses`, otherwise `failed`. Preserve the final classification, `fix_status`, convergence, current-content risk, and `gh_status` before continuing. + When the fix and verification land cleanly, record current content risk as clear without changing the immutable snapshot state, then update `done_verified_snapshot` from the prior snapshot plus this item's verified declared-path bytes. If `phase8_snapshot_fix_state[idx]=inverse_risk_applied` and `phase8_remediation_kind[idx]=removal`, set `fix_status[idx]=reverted_inverse_risk`, set `needs_input_status[idx]=failed`, set paired `gh_status` states to `not-applicable` when `has_github_surface=false` or to `skipped` with `original finding not accepted, risky fix removed` otherwise, clear `phase8_triage_context`, and rejoin Phase 8 without GitHub reply or resolution. A verified replacement retains its applicable landed status. For a landed fix, set its classification to `FIX`, then run Steps 7a and 7b to freeze its `reply_final`, record its reply and resolution as `skipped` with the reason `held until push`, or `not-applicable` when `has_github_surface=false`, and add it to the held FIX set that Phase 8 step 5 and step 2b post after the push lands. Set `needs_input_status` from `fix_status` alone: `fixed` when it is in `landed_fix_statuses`, otherwise `failed`. Preserve the final classification, `fix_status`, convergence, current-content risk, and `gh_status` before continuing. Immediately before a chosen Defer or Dismiss reply mutates GitHub, invoke `preflight-mutations` for that item's reply/resolve batch with the exact PR and current head SHA, target thread ID, final reply text, classification, and the per-item choice above. Name the target thread as the batch's durable home, with the per-item choice as its authorization: thread ID plus expected reply/resolve states, re-readable through the API after every write. Apply its result contract before continuing. Record a blocked or confirmed failure as `needs_input_status=failed`, an indeterminate operation as `reconcile-required`, and the exact Phase 7 `gh_status`; then continue to the next independent item. Every branch rejoins report rendering. diff --git a/skills/fix-pr-review/references/push-publication.md b/skills/fix-pr-review/references/push-publication.md index bea6c58..4b4b83d 100644 --- a/skills/fix-pr-review/references/push-publication.md +++ b/skills/fix-pr-review/references/push-publication.md @@ -17,4 +17,4 @@ git ls-remote --heads "<preflighted-remote>" "<exact-ref>" After every attempted push, including a nonzero or ambiguous exit, establish remote state first: re-enumerate both complete endpoint sets and run the exact `git ls-remote` read-back for `<exact-ref>` once. Record the push as landed only when the authoritative remote SHA equals the frozen SHA, the ordered sets are unchanged, and every endpoint still equals the intended publication target. An old or unexpected SHA, unavailable read-back, added, removed, reordered, unresolved, or mismatched endpoint is `reconcile-required`; stop and never retry from the command result alone. -After preserving the remote result, re-read the active symbolic ref, frozen publication branch-ref SHA, and `HEAD`. Continue only when the symbolic ref equals `<exact-ref>` and both local SHAs equal the frozen SHA. When the remote landed but any local value moved, record `remote landed / local publication moved`, set overall publication to `reconcile-required`, and stop without retrying the push. When the remote did not land, preserve its observed state and stop as `reconcile-required` regardless of local state. Only after both remote and local state pass may a GitHub PR input read `headRefName`, `headRefOid`, and `headRepository.id`; require them to equal the frozen head name, frozen SHA, and intended publication target ID. Only a push that passes all of these counts as landed and releases the held FIX replies. +After preserving the remote result, re-read the active symbolic ref, frozen publication branch-ref SHA, and `HEAD`. Continue only when the symbolic ref equals `<exact-ref>` and both local SHAs equal the frozen SHA. When the remote landed but any local value moved, record `remote landed / local publication moved`, set overall publication to `reconcile-required`, and stop without retrying the push. When the remote did not land, preserve its observed state and stop as `reconcile-required` regardless of local state. Only after both remote and local state pass may a GitHub PR input read `headRefName`, `headRefOid`, and `headRepository.id`; require them to equal the frozen head name, frozen SHA, and intended publication target ID. Only a push that passes all of these counts as landed and releases the held FIX replies, with its frozen SHA as their landed push SHA. diff --git a/skills/fix-ts-errors/SKILL.md b/skills/fix-ts-errors/SKILL.md index d4774be..92994d3 100644 --- a/skills/fix-ts-errors/SKILL.md +++ b/skills/fix-ts-errors/SKILL.md @@ -46,7 +46,7 @@ git ls-files --others --exclude-standard -- '*.ts' '*.tsx' | tr '\n' '\0' | xarg The `[!]` groups match a postfix assertion: a value character, closing quote, bracket, or paren before the bang and no equals after it, so `value!`, `value!.x`, `value![0]`, `value!()`, `"value"!`, and backtick-quoted receivers all hit while `!=`, `!==`, and prefix negation like `!ready` stay out. The trailing `sed` neutralizes only the `as const` idiom into a marker instead of dropping whole lines, so co-occurring hatches on the same line survive. A line whose only match is the `__AS_CONST__` marker needs no action. -I skip import aliases like `import { x as y }`. For every remaining hit I attempt removal with proper typing, meaning inference, narrowing, type guards, generics, or schema-derived types with `z.infer`. I re-run the check after each removal, and when errors appear I go back to Step 3. A hatch survives only with a one-line comment citing the upstream issue as a URL or `#N`. When no upstream issue exists, the comment cites a permalink URL to the line in the library's type definition that causes the gap. A `@ts-expect-error` in a test file (`*.test.*`, `*.spec.*`, `*.test-d.ts`) whose enclosing test's name states that the compiler rejects that line is a type test, not a hatch. The directive is code, not a comment, and stays with no added comment. +I skip import aliases like `import { x as y }`. For every remaining hit I attempt removal with proper typing, meaning inference, narrowing, type guards, generics, or schema-derived types with `z.infer`. I re-run the check after each removal, and when errors appear I go back to Step 3. A hatch survives only with a one-line comment citing the upstream issue as a URL or `#N`. When no upstream issue exists, the comment cites a permalink URL to the line in the library's type definition that causes the gap. A `// @ts-expect-error`, bare or with a `-- <reason>` description, in a test file (`*.test.*`, `*.spec.*`, `*.test-d.ts`) whose enclosing test's name states that the compiler rejects that line is a type test, not a hatch. The directive is code, not a comment, and stays with no added comment. ### Step 6: Report diff --git a/skills/openclaw-backup/SKILL.md b/skills/openclaw-backup/SKILL.md index adb898c..2299975 100644 --- a/skills/openclaw-backup/SKILL.md +++ b/skills/openclaw-backup/SKILL.md @@ -171,10 +171,11 @@ Immediately before `rsync`, invoke `preflight-mutations` with the destination ev rsync -avh --partial -e ssh <source> <destination> ``` -Verify on the receiving side against the manifest: +Verify on the receiving side against the manifest. Run the `cp` line when `official/` was pulled and the `grep -v` line when it was omitted above, never both: ```bash -grep -v ' \./official/' MANIFEST.sha256 > /tmp/check.sha256 # only when official/ was omitted above +cp MANIFEST.sha256 /tmp/check.sha256 +grep -v ' \./official/' MANIFEST.sha256 > /tmp/check.sha256 sha256sum -c /tmp/check.sha256 # macOS: shasum -a 256 -c ``` diff --git a/skills/resolving-merge-conflicts/SKILL.md b/skills/resolving-merge-conflicts/SKILL.md index 7848504..805ce2f 100644 --- a/skills/resolving-merge-conflicts/SKILL.md +++ b/skills/resolving-merge-conflicts/SKILL.md @@ -3,7 +3,7 @@ name: resolving-merge-conflicts description: "Resolve an in-progress git conflict without a silent drop. Use when the worktree is mid-merge, mid-rebase, mid-cherry-pick or mid-stash-pop: `CONFLICT (content)`, `<<<<<<<` markers, `Unmerged paths`, or `fix conflicts and then commit the result`. Also use when asked to resolve or fix merge conflicts on a PR or branch before any merge has started." --- -1. Orient before you touch anything. Establish from `git status` whether you are in a merge, a rebase, a cherry-pick, a revert, a `git am`, or a stash pop. Unmerged paths with none of the other five in progress mean a conflicting stash pop; confirm it with `git stash list` and the request, and ask the user when their origin is unclear. When none is in progress and nothing is unmerged, start a merge only from a clean worktree. When `git status --porcelain` lists anything, stop and ask the user to commit it, stash it, or move it to a separate worktree; never stash it yourself. For a PR, the base repository is the PR URL's `<owner>/<repo>`; read the base ref, its tip, the head branch, and the head repository with `gh pr view <url> --json baseRefName,baseRefOid,headRefName,headRepository`. For a named branch with no PR, the base is the default branch from `gh repo view --json defaultBranchRef`. When the head branch, `headRefName` for a PR, is not checked out, stop and ask. Fetch from the remote that resolves to the base repository, require the fetched base ref to equal `baseRefOid` when a PR was named, merge that remote's base ref into the head branch, and orient. Rebase only when the user asked for one, because rebasing a published branch rewrites its history. Under a rebase `ours` is the upstream you are replaying onto and `theirs` is your own commit, the inverse of a merge, and getting this backwards is the most common wrong resolution. +1. Orient before you touch anything. Establish from `git status` whether you are in a merge, a rebase, a cherry-pick, a revert, a `git am`, or a stash pop. Unmerged paths with none of the other five in progress mean a conflicting stash pop; confirm it with `git stash list` and the request, and ask the user when their origin is unclear. When none is in progress and nothing is unmerged, start a merge only from a clean worktree. When `git status --porcelain` lists anything, stop and ask the user to commit it, stash it, or move it to a separate worktree; never stash it yourself. For a PR, the base repository is the PR URL's `<owner>/<repo>`; read the base ref, its tip, the head branch, and the head repository with `gh pr view <url> --json baseRefName,baseRefOid,headRefName,headRepository`. The base branch is the one the request names, else the PR's `baseRefName`, else the default branch from `gh repo view --json defaultBranchRef`. When the head branch, `headRefName` for a PR, is not checked out, stop and ask. Fetch from the remote that resolves to the base repository, require the fetched base ref to equal `baseRefOid` when the base is the PR's, merge that remote's base ref into the head branch, and orient. Rebase only when the user asked for one, because rebasing a published branch rewrites its history. Under a rebase `ours` is the upstream you are replaying onto and `theirs` is your own commit, the inverse of a merge, and getting this backwards is the most common wrong resolution. Then split the conflicted files. Regenerate generated artifacts and lockfiles from source, never hand-merge them. That means `bun.lock`, `pnpm-lock.yaml`, `_generated/`, and snapshots. Only hand-written files go through steps 2-3. diff --git a/skills/simplify/SKILL.md b/skills/simplify/SKILL.md index efc692a..b103ca2 100644 --- a/skills/simplify/SKILL.md +++ b/skills/simplify/SKILL.md @@ -27,7 +27,7 @@ I prefer deletion and direct code over a new helper. I reuse an existing local p ### Added-comment scan -I inspect every comment the change adds under the global comment rule. A comment stays only when it is a one-line `/** */` contract on an exported symbol, or a one-line citation carrying a URL, a spec section, an ADR or `docs/` path, or an issue number. I delete everything else on sight, including any comment that explains what the code does, why it has its shape, or what would break. I run the rule's grep over the diff and delete every comment it prints, except a bare `// @ts-expect-error` in a test file whose enclosing test's name states that the compiler rejects that line, which is a `fix-ts-errors` type test and code, not a comment. A project rule may tighten this scan, never loosen it. +I inspect every comment the change adds under the global comment rule. A comment stays only when it is a one-line `/** */` contract on an exported symbol, or a one-line citation carrying a URL, a spec section, an ADR or `docs/` path, or an issue number. I delete everything else on sight, including any comment that explains what the code does, why it has its shape, or what would break. I run the rule's grep over the diff and delete every comment it prints, except a `// @ts-expect-error`, bare or with a `-- <reason>` description, in a test file (`*.test.*`, `*.spec.*`, `*.test-d.ts`) whose enclosing test's name states that the compiler rejects that line, which is a `fix-ts-errors` type test and code, not a comment. A project rule may tighten this scan, never loosen it. Every proposed edit names what turns simpler and why observable behavior stays equivalent, and no uncited comment the change adds remains. This scan blocks. The caller cannot report the work complete while one remains in the diff. From a321adcaaca3177ac8b8f4fc9ba4ee6caf488c81 Mon Sep 17 00:00:00 2001 From: Bhagya Mudgal <bhagyamudgal@gmail.com> Date: Tue, 10 Nov 2026 14:49:30 +0530 Subject: [PATCH 4/8] fix(openclaw-backup): check exactly the copied archives in one step - Each case branch runs its own checksum check, so a typo can no longer verify against a stale list - Tell the receiving side to set OFFICIAL_PULLED, since it runs in a different shell --- skills/openclaw-backup/SKILL.md | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/skills/openclaw-backup/SKILL.md b/skills/openclaw-backup/SKILL.md index 2299975..805ec9a 100644 --- a/skills/openclaw-backup/SKILL.md +++ b/skills/openclaw-backup/SKILL.md @@ -163,7 +163,7 @@ A backup on the same disk as the thing it protects survives config mistakes, not Before `rsync`, verify from the receiving system that the destination is encrypted at rest, accessible only to the intended recipient accounts, and governed by a known retention period and tested deletion path. Confirm the destination host key against a known-good value before the first transfer, so a mistyped host or a first-connect interception cannot divert the copy. A destination missing any of those controls is blocked. -Decide before the preflight whether the official archive goes off-box. Pull it unless this run lists its contents with `tar -tzf` and confirms every path in it appears in a fresh listing of the raw archive. When omitted, add `--exclude='official/'` to the `rsync` below. Record in the report which applied. +Decide before the preflight whether the official archive goes off-box. Pull it unless this run lists its contents with `tar -tzf` and confirms every path in it appears in a fresh listing of the raw archive. Set `OFFICIAL_PULLED=yes` when it goes, or `OFFICIAL_PULLED=no` and add `--exclude='official/'` to the `rsync` below when it is omitted. Record in the report which applied. Immediately before `rsync`, invoke `preflight-mutations` with the destination evidence, the exact source and destination host and path, included artifact inventory with `official/` in or out per that decision, credential sensitivity, recipient, retention and deletion path, current destination state, checksum and access-control read-back, and the user's off-box-copy approval. Apply its result contract before copying. @@ -171,12 +171,14 @@ Immediately before `rsync`, invoke `preflight-mutations` with the destination ev rsync -avh --partial -e ssh <source> <destination> ``` -Verify on the receiving side against the manifest. Run the `cp` line when `official/` was pulled and the `grep -v` line when it was omitted above, never both: +Verify on the receiving side against the manifest. Set `OFFICIAL_PULLED` there to the value recorded at the pull-or-omit decision, since that shell is a different one. Each branch checks exactly the entries that were copied, and any other value fails without checking anything. On macOS, use `shasum -a 256 -c` in place of `sha256sum -c`: ```bash -cp MANIFEST.sha256 /tmp/check.sha256 -grep -v ' \./official/' MANIFEST.sha256 > /tmp/check.sha256 -sha256sum -c /tmp/check.sha256 # macOS: shasum -a 256 -c +case "${OFFICIAL_PULLED:?set to the pull-or-omit decision}" in + yes) sha256sum -c MANIFEST.sha256 ;; + no) grep -v ' \./official/' MANIFEST.sha256 | sha256sum -c - ;; + *) echo "OFFICIAL_PULLED must be yes or no" >&2; false ;; +esac ``` Confirm the checksum output covers a non-empty file list before reading it as a pass. A verifier fed an empty list reports success having checked nothing. From b2b9c37f828564d8ffa542df1369923912a88290 Mon Sep 17 00:00:00 2001 From: Bhagya Mudgal <bhagyamudgal@gmail.com> Date: Tue, 10 Nov 2026 15:09:18 +0530 Subject: [PATCH 5/8] fix(skills): give review follow-ups a reporting path and one duplication threshold - review-pr files nothing on follow-up-proposed, since its findings are already threads, and lists them under Not filed - fix-pr-review applies the converge-reviews filing rule after its report, since its local findings have no thread - Scope reuse-first's rule of three to single lines so the sweep and the guide agree at three sites - Re-pin the review-pr corpus ceiling for the new rule, per the test's documented policy --- skills/converge-reviews/SKILL.md | 2 +- skills/fix-pr-review/SKILL.md | 4 ++-- skills/fix-pr-review/references/final-report.md | 1 + skills/reuse-first/references/artifact-guide.md | 2 +- skills/review-pr/SKILL.md | 2 +- skills/review-pr/references/batch-mode.md | 3 +++ tools/eval/test_review_pr_load.py | 2 +- 7 files changed, 10 insertions(+), 6 deletions(-) diff --git a/skills/converge-reviews/SKILL.md b/skills/converge-reviews/SKILL.md index 07a69c9..9926b26 100644 --- a/skills/converge-reviews/SKILL.md +++ b/skills/converge-reviews/SKILL.md @@ -67,7 +67,7 @@ I count a round only after every planned reviewer returned or was explicitly rec Round three is a hard cap for the stable scope. Reworded findings, another reviewer over unchanged coverage, or a cosmetic diff count as the same round, never round four. The one targeted blocker closure check only verifies named fixes. It cannot widen coverage or emit fresh findings. -The caller files a proposed issue automatically, after `preflight-mutations`, when its finding is Critical, Serious, or an important user-facing problem, one a user of the shipped product can hit. It asks the user about every other proposed issue. An unattended run files only that automatic class. Every proposed issue that ends unfiled, whether held by an unattended run, declined by the user, or blocked by `preflight-mutations`, goes under "Not filed" in the PR body or report with its reason. Once an issue is filed or declined, I record its disposition as `filed <issue URL>` or `declined`, so later rounds never propose it again. +The caller files a proposed issue automatically, after `preflight-mutations`, when its finding is Critical, Serious, or an important user-facing problem, one a user of the shipped product can hit. It asks the user about every other proposed issue. An unattended run files only that automatic class. Every proposed issue that ends unfiled, whether held by an unattended run, declined by the user, or blocked by `preflight-mutations`, goes under "Not filed" in the PR body or report with its reason. A caller whose findings already live as PR review threads, such as `review-pr`, files nothing and lists each proposal under "Not filed" with its reason. Once an issue is filed or declined, I record its disposition as `filed <issue URL>` or `declined`, so later rounds never propose it again. The caller applies this contract. It reruns affected review coverage only for `continue`, proceeds for `converged`, stops for `blocked-at-cap`, and applies the filing rule above for `follow-up-proposed`. After a named blocker fix at the cap, `closure_check: available` is the dispatch signal for the one targeted check. The caller passes only those blocker IDs and changed sites, leaves `round` unchanged, and rejects any new-finding sweep. diff --git a/skills/fix-pr-review/SKILL.md b/skills/fix-pr-review/SKILL.md index c11d7ff..6353840 100644 --- a/skills/fix-pr-review/SKILL.md +++ b/skills/fix-pr-review/SKILL.md @@ -326,7 +326,7 @@ For each selected item, use a follow-up AskUserQuestion: On "FIX": re-dispatch the classifier scoped to just this item to produce the full FIX field set: `fix_plan`, `change_class`, `test_scenario`, `inverse_risk`, `class_completeness` (class sweep included; a reclassified item has never been swept), and `reusability_context` (carry the contested item's own value through unless the sweep changed it). Then re-run plan validation on the changed item. Producing a partial field set here fails validation and burns the single retry. On "NEEDS-INPUT": move to NEEDS-INPUT with `why_unclear: "user contested the <classification> classification"`, carrying the item's `reusability_context` through unchanged. On "Keep as-is": no change. On "Other": treat the freeform text as the reclassification instruction. -This step posts and resolves nothing. Only Phase 7 and the held FIX posting after a landed push touch GitHub, and contested items post **only** when they passed this confirmation, asked or evidence-confirmed. A DISMISS, DEFER, or DISAGREE that has not passed this gate stays open and unanswered. +This step posts and resolves nothing. Only Phase 7, the held FIX posting after a landed push, and Phase 6's follow-up filing after the final report touch GitHub, and contested items post **only** when they passed this confirmation, asked or evidence-confirmed. A DISMISS, DEFER, or DISAGREE that has not passed this gate stays open and unanswered. ### Execution @@ -476,7 +476,7 @@ note `convergence checked inline`. After all fixes are applied, run `/done` on the pending fix diff (`git diff HEAD`). `/done` is a four-section acceptance workflow, not a fixed three-command pipeline: §1 binds the run and selects the acceptance lanes, §2 verifies each required lane at its boundary, §3 assigns a state to every request item, lane, and evidence facet, and §4 builds the readiness card. Bind the originating request to the validated fix plan and scope every check to the fix diff, not the entire PR. -The Code lane always applies here. It runs `/fix-ts-errors` to green (catching the cross-file errors a per-file narrow check cannot see, and running the full workspace check at least once), runs `/parallel-review` over the fix diff and applies its `converge-reviews` result, runs `/simplify` including its blocking added-comment scan, and accounts for each fix against the diff. Using the rule `/done` §1 states, select every other lane the fixes actually touched: UI, documentation, global configuration or skills, external metadata or data, publication or deployment. Record the rest as `not-applicable` with their exclusion reason. +The Code lane always applies here. It runs `/fix-ts-errors` to green (catching the cross-file errors a per-file narrow check cannot see, and running the full workspace check at least once), runs `/parallel-review` over the fix diff and applies its `converge-reviews` result (on `follow-up-proposed` it applies that skill's filing rule after the final report prints, since these local findings have no review thread, and the report lists every unfiled proposal under Not filed), runs `/simplify` including its blocking added-comment scan, and accounts for each fix against the diff. Using the rule `/done` §1 states, select every other lane the fixes actually touched: UI, documentation, global configuration or skills, external metadata or data, publication or deployment. Record the rest as `not-applicable` with their exclusion reason. Do not take `/done` §4's handoffs. This skill owns publication: `git-commit` and `file-pr` are unavailable in Phase 6, and Phase 8's post-completion prompt is the only place a commit or push may start. diff --git a/skills/fix-pr-review/references/final-report.md b/skills/fix-pr-review/references/final-report.md index 67a8550..db1431d 100644 --- a/skills/fix-pr-review/references/final-report.md +++ b/skills/fix-pr-review/references/final-report.md @@ -131,6 +131,7 @@ Convergence: converged. All <F> fixes class-complete, inverse risk absent, no ne Readiness: <ready | ready-to-publish | not ready> with the evidence ceiling Remaining after self-heal: <list or "none"> Moderate/Minor after self-heal: <done_moderate_minor list or "none"> + Not filed: <follow-up-proposed items with their reasons, or "none"> ## Contested replies posted without asking (<count>) [D2] src/auth/util.ts:12: DISMISS (R5), auto-confirm: claude_md_quote, found verbatim diff --git a/skills/reuse-first/references/artifact-guide.md b/skills/reuse-first/references/artifact-guide.md index 6069735..2b047cf 100644 --- a/skills/reuse-first/references/artifact-guide.md +++ b/skills/reuse-first/references/artifact-guide.md @@ -28,5 +28,5 @@ Reach this section only after all three layers have returned. - **Two unrelated domains** that happen to look similar. DRY is about shared knowledge, not shared appearance. A `userId` validator and a `productId` validator can both be `z.string().uuid()` independently. Coupling them creates fake polymorphism -- **Premature abstraction.** Three similar occurrences are fine. Abstract on the fourth. +- **Premature abstraction.** Three similar single lines are fine. Abstract on the fourth. A multi-line call sequence repeated at 3 or more sites is different: the sweep reports it as one helper. - **Tests.** Explicit setup in each test often beats shared fixtures that hide what is being tested diff --git a/skills/review-pr/SKILL.md b/skills/review-pr/SKILL.md index 581b3ab..226b901 100644 --- a/skills/review-pr/SKILL.md +++ b/skills/review-pr/SKILL.md @@ -629,7 +629,7 @@ Pass into the reference: `<owner>`, `<repo>`, `<pr-num>`, `<head_sha>`, `CURRENT ### Convergence handoff -After authoritative posting and write-back, invoke `converge-reviews` (it ships in the same skills repository; report it missing and skip this handoff only after invoking it fails) with the PR request, base/current head and diff hash, reviewed paths, reviewer roster and lenses, current findings and dispositions, and `$STATE_FILE`. Store the resulting `convergence` block in that existing state file without replacing `review-pr`'s finding state. Apply its result contract before recommending another review round or declaring the review converged. +After authoritative posting and write-back, invoke `converge-reviews` (it ships in the same skills repository; report it missing and skip this handoff only after invoking it fails) with the PR request, base/current head and diff hash, reviewed paths, reviewer roster and lenses, current findings and dispositions, and `$STATE_FILE`. Store the resulting `convergence` block in that existing state file without replacing `review-pr`'s finding state. Apply its result contract before recommending another review round or declaring the review converged. On `follow-up-proposed`, file nothing, since each finding is already a thread and step 8c files pre-existing ones; print each proposal and its reason under `Not filed` after the Phase 4 output. --- diff --git a/skills/review-pr/references/batch-mode.md b/skills/review-pr/references/batch-mode.md index 291d134..64f9f22 100644 --- a/skills/review-pr/references/batch-mode.md +++ b/skills/review-pr/references/batch-mode.md @@ -69,6 +69,9 @@ After automatic posting has attempted every completed review and recorded each c ## Posting status <one entry per PR: posted request-changes + review URL, posted approve + review URL, posted self-review comment + review URL, posted incomplete-coverage comment + review URL, review failed, posting failed, or recovery-pending> +## Not filed (<count>) +<one line per follow-up proposal: PR, finding and reason; omit when none> + ## Pending review context (<count>) <one entry per review-only checkpoint that could not be resolved, such as: PENDING #<num>: intent was not grounded, re-run with intent text?> diff --git a/tools/eval/test_review_pr_load.py b/tools/eval/test_review_pr_load.py index 358eb63..e77d2f3 100644 --- a/tools/eval/test_review_pr_load.py +++ b/tools/eval/test_review_pr_load.py @@ -21,7 +21,7 @@ import review_pr_load import review_pr_tokens -CORPUS_CEILING = 216270 +CORPUS_CEILING = 216555 CEILINGS = { "parallel-standard/round-1": (152261, 152261, 15), "parallel-chunked/round-1": (153796, 153796, 15), From ba3f0f71d814601ac92da2ae500429b11cbf2142 Mon Sep 17 00:00:00 2001 From: Bhagya Mudgal <bhagyamudgal@gmail.com> Date: Tue, 10 Nov 2026 18:17:23 +0530 Subject: [PATCH 6/8] fix(skills): file review follow-ups before the report that lists them fix-pr-review credited Phase 6 with filing follow-up proposals after the final report, but that report already lists the unfiled ones and no later step did the filing, so qualifying follow-ups were never filed. Filing now runs as Phase 8 step 2c ahead of the report, the report shows filed issue URLs beside unfiled ones, and the filing question joins the list of allowed questions. review-pr's single-PR output gains the Not filed section its convergence handoff writes to, and prints only after that handoff returns. The review-pr corpus ceiling rises by the 138 bytes these lines add. --- skills/fix-pr-review/SKILL.md | 12 ++++++++---- skills/fix-pr-review/references/final-report.md | 1 + skills/review-pr/SKILL.md | 5 ++++- tools/eval/test_review_pr_load.py | 2 +- 4 files changed, 14 insertions(+), 6 deletions(-) diff --git a/skills/fix-pr-review/SKILL.md b/skills/fix-pr-review/SKILL.md index 6353840..0610652 100644 --- a/skills/fix-pr-review/SKILL.md +++ b/skills/fix-pr-review/SKILL.md @@ -7,7 +7,7 @@ description: Triage, fix, and resolve review findings. Use on a CodeRabbit or an Takes a PR review from CodeRabbit, `/review-pr`, or pasted text. It triages each finding, applies validated fixes, runs `/done`, then replies to and resolves the GitHub conversations in one flow. -Use AskUserQuestion only for these user decisions: branch safety, stash confirmation, contested-item confirmation, `--interactive` per-fix confirmations, type-check failure triage, and post-completion next actions. Invoking `/fix-pr-review` or imperatively asking to fix review findings authorizes execution of the validated FIX plan. Do not ask for separate plan approval. An unattended PR-watch run asks none of these (Phase 1). Write every option as a concrete choice, strongest first, marked "(Recommended)". +Use AskUserQuestion only for these user decisions: branch safety, stash confirmation, contested-item confirmation, `--interactive` per-fix confirmations, type-check failure triage, follow-up filing (Phase 8 step 2c), and post-completion next actions. Invoking `/fix-pr-review` or imperatively asking to fix review findings authorizes execution of the validated FIX plan. Do not ask for separate plan approval. An unattended PR-watch run asks none of these (Phase 1). Write every option as a concrete choice, strongest first, marked "(Recommended)". ## Quick Reference @@ -23,7 +23,7 @@ Use AskUserQuestion only for these user decisions: branch safety, stash confirma | 5.5 | Convergence subagent: class completeness, inverse risk, new siblings | Per-fix verdicts; missing sites applied | | 6 | `/done` acceptance verification scoped to the fix diff, no commit or publish handoff | Verified fix diff, `done_verified_snapshot` | | 7 | Reply + resolve contested items on GitHub, FIX replies held until a push lands (skipped for local files) | Contested threads resolved, FIX replies frozen | -| 8 | Finalize: settle NEEDS-INPUT, restore stash, report, suppressions write, next actions | Final report | +| 8 | Finalize: settle NEEDS-INPUT, restore stash, file follow-ups, report, suppressions write, next actions | Final report | ### R-Rubric Summary (Phase 3 STEP 4; first match wins) @@ -326,7 +326,7 @@ For each selected item, use a follow-up AskUserQuestion: On "FIX": re-dispatch the classifier scoped to just this item to produce the full FIX field set: `fix_plan`, `change_class`, `test_scenario`, `inverse_risk`, `class_completeness` (class sweep included; a reclassified item has never been swept), and `reusability_context` (carry the contested item's own value through unless the sweep changed it). Then re-run plan validation on the changed item. Producing a partial field set here fails validation and burns the single retry. On "NEEDS-INPUT": move to NEEDS-INPUT with `why_unclear: "user contested the <classification> classification"`, carrying the item's `reusability_context` through unchanged. On "Keep as-is": no change. On "Other": treat the freeform text as the reclassification instruction. -This step posts and resolves nothing. Only Phase 7, the held FIX posting after a landed push, and Phase 6's follow-up filing after the final report touch GitHub, and contested items post **only** when they passed this confirmation, asked or evidence-confirmed. A DISMISS, DEFER, or DISAGREE that has not passed this gate stays open and unanswered. +This step posts and resolves nothing. Only Phase 7, the held FIX posting after a landed push, and Phase 8 step 2c's follow-up filing touch GitHub, and contested items post **only** when they passed this confirmation, asked or evidence-confirmed. A DISMISS, DEFER, or DISAGREE that has not passed this gate stays open and unanswered. ### Execution @@ -476,7 +476,7 @@ note `convergence checked inline`. After all fixes are applied, run `/done` on the pending fix diff (`git diff HEAD`). `/done` is a four-section acceptance workflow, not a fixed three-command pipeline: §1 binds the run and selects the acceptance lanes, §2 verifies each required lane at its boundary, §3 assigns a state to every request item, lane, and evidence facet, and §4 builds the readiness card. Bind the originating request to the validated fix plan and scope every check to the fix diff, not the entire PR. -The Code lane always applies here. It runs `/fix-ts-errors` to green (catching the cross-file errors a per-file narrow check cannot see, and running the full workspace check at least once), runs `/parallel-review` over the fix diff and applies its `converge-reviews` result (on `follow-up-proposed` it applies that skill's filing rule after the final report prints, since these local findings have no review thread, and the report lists every unfiled proposal under Not filed), runs `/simplify` including its blocking added-comment scan, and accounts for each fix against the diff. Using the rule `/done` §1 states, select every other lane the fixes actually touched: UI, documentation, global configuration or skills, external metadata or data, publication or deployment. Record the rest as `not-applicable` with their exclusion reason. +The Code lane always applies here. It runs `/fix-ts-errors` to green (catching the cross-file errors a per-file narrow check cannot see, and running the full workspace check at least once), runs `/parallel-review` over the fix diff and applies its `converge-reviews` result (on `follow-up-proposed` it records the proposals for Phase 8 step 2c, since these local findings have no review thread), runs `/simplify` including its blocking added-comment scan, and accounts for each fix against the diff. Using the rule `/done` §1 states, select every other lane the fixes actually touched: UI, documentation, global configuration or skills, external metadata or data, publication or deployment. Record the rest as `not-applicable` with their exclusion reason. Do not take `/done` §4's handoffs. This skill owns publication: `git-commit` and `file-pr` are unavailable in Phase 6, and Phase 8's post-completion prompt is the only place a commit or push may start. @@ -567,6 +567,10 @@ On `STASH_TOP_MISMATCH`, record `stash_restored: foreign-top` and continue to th Runs only when `UNATTENDED_WATCH=true`, before the report. First compute `inverse_risk_blockers` exactly as step 5 defines it. Commit and push only when Phase 6 verified every required lane, `done_remaining` is empty, and step 5's Commit and Push requirements hold. Commit through `git-commit` in sealed-index mode as step 5 describes, then push per `${CLAUDE_SKILL_DIR}/references/push-publication.md` under `preflight-mutations`, with the watch's standing instruction as the authorization source. A question that reference would ask counts as a blocked push. Only after the push read-back records it landed, post the held FIX replies and resolve their threads through Phase 7's posting mechanics. When `/done` is not clean or the push is blocked, post nothing for FIX items and report each as `fixed locally, not pushed`. +### 2c. File follow-up proposals + +Runs before the report when Phase 6 recorded `follow-up-proposed`. Apply `converge-reviews`' filing rule to each proposal: file the automatic class through `file-issue` after `preflight-mutations`, ask about the rest, and in an unattended watch run file only the automatic class. Record each outcome as `filed <issue URL>` or the reason it stays unfiled; the report's Not filed line lists the unfiled ones. + ### 3. Print the final report once Recompute every count from the final classification, `fix_status`, `convergence`, `needs_input_status`, and `gh_status` state. Load `${CLAUDE_SKILL_DIR}/references/final-report.md` now and render it exactly once. Render `inverse_risk_applied` under **Skipped / not landed clean**, count it as not converged, and state `risky fix remains applied`. Its dependency-ready action is `Remove the risky owned components or apply a replacement, then rerun Phase 5.5 and Phase 6 to rebuild done_verified_snapshot.` Interpret any legacy no-safe-revert rule for `reverted_inverse_risk` as `inverse_risk_applied`; the reverted status always means the fix was removed. The report's failure-first ordering includes every Phase 7 and Phase 8 GitHub outcome; its rendering rules decide which fix subsection each `[F<n>]` occupies and how the `Test:` line is emitted. diff --git a/skills/fix-pr-review/references/final-report.md b/skills/fix-pr-review/references/final-report.md index db1431d..12c99cd 100644 --- a/skills/fix-pr-review/references/final-report.md +++ b/skills/fix-pr-review/references/final-report.md @@ -131,6 +131,7 @@ Convergence: converged. All <F> fixes class-complete, inverse risk absent, no ne Readiness: <ready | ready-to-publish | not ready> with the evidence ceiling Remaining after self-heal: <list or "none"> Moderate/Minor after self-heal: <done_moderate_minor list or "none"> + Filed: <follow-up issue URLs step 2c filed, or "none"> Not filed: <follow-up-proposed items with their reasons, or "none"> ## Contested replies posted without asking (<count>) diff --git a/skills/review-pr/SKILL.md b/skills/review-pr/SKILL.md index 226b901..56d338e 100644 --- a/skills/review-pr/SKILL.md +++ b/skills/review-pr/SKILL.md @@ -555,6 +555,9 @@ Every Phase 4 path reaches **Convergence handoff** after main submits the GitHub ## Filtered out (<count>) <dropped findings with reasons, for auditability> +## Not filed (<count>) +<one line per follow-up proposal: finding and reason; omit when none> + ## Multi-round status <for each finding in PRIOR_STATE: id, file, status, round_resolved/dismissed, dismissal_reason. Useful for "did I really ship M3 in round 5?" scanning.> ``` @@ -629,7 +632,7 @@ Pass into the reference: `<owner>`, `<repo>`, `<pr-num>`, `<head_sha>`, `CURRENT ### Convergence handoff -After authoritative posting and write-back, invoke `converge-reviews` (it ships in the same skills repository; report it missing and skip this handoff only after invoking it fails) with the PR request, base/current head and diff hash, reviewed paths, reviewer roster and lenses, current findings and dispositions, and `$STATE_FILE`. Store the resulting `convergence` block in that existing state file without replacing `review-pr`'s finding state. Apply its result contract before recommending another review round or declaring the review converged. On `follow-up-proposed`, file nothing, since each finding is already a thread and step 8c files pre-existing ones; print each proposal and its reason under `Not filed` after the Phase 4 output. +After authoritative posting and write-back, invoke `converge-reviews` (it ships in the same skills repository; report it missing and skip this handoff only after invoking it fails) with the PR request, base/current head and diff hash, reviewed paths, reviewer roster and lenses, current findings and dispositions, and `$STATE_FILE`. Store the resulting `convergence` block in that existing state file without replacing `review-pr`'s finding state. Apply its result contract before recommending another review round or declaring the review converged. On `follow-up-proposed`, file nothing, since each finding is already a thread and step 8c files pre-existing ones; print the Phase 4 terminal block only after this handoff returns, listing each proposal with its reason under `Not filed`. --- diff --git a/tools/eval/test_review_pr_load.py b/tools/eval/test_review_pr_load.py index e77d2f3..0cff385 100644 --- a/tools/eval/test_review_pr_load.py +++ b/tools/eval/test_review_pr_load.py @@ -21,7 +21,7 @@ import review_pr_load import review_pr_tokens -CORPUS_CEILING = 216555 +CORPUS_CEILING = 216693 CEILINGS = { "parallel-standard/round-1": (152261, 152261, 15), "parallel-chunked/round-1": (153796, 153796, 15), From 02c2c89901fcee4079ec0ec1c27cd9317766124b Mon Sep 17 00:00:00 2001 From: Bhagya Mudgal <bhagyamudgal@gmail.com> Date: Tue, 10 Nov 2026 18:48:17 +0530 Subject: [PATCH 7/8] fix(skills): close gaps in follow-up, reply and conflict handling A declined follow-up filing normalized its finding to closed, so a later round could report converged with an untracked finding; the decline is now its own disposition, follow-up-declined, which still blocks converged without re-asking. A fix whose reply failed validation was marked held until push, hiding the failure; it now keeps reply_invalid in both reply and resolution state. Proposals resolved by a Phase 8 fix are dropped before filing, and the list of steps that touch GitHub names the push and Phase 8 Defer and Dismiss replies. A requested rebase no longer merges the base, the off-box RESTORE.md says when official/ was not copied, and a top-level @ts-expect-error in a *.test-d.ts file counts as a type test in both simplify and fix-ts-errors. --- skills/converge-reviews/SKILL.md | 8 ++++---- skills/fix-pr-review/SKILL.md | 6 +++--- skills/fix-pr-review/references/needs-input-triage.md | 2 +- skills/fix-ts-errors/SKILL.md | 2 +- skills/openclaw-backup/SKILL.md | 2 +- skills/resolving-merge-conflicts/SKILL.md | 2 +- skills/simplify/SKILL.md | 2 +- 7 files changed, 12 insertions(+), 12 deletions(-) diff --git a/skills/converge-reviews/SKILL.md b/skills/converge-reviews/SKILL.md index 9926b26..4c56a00 100644 --- a/skills/converge-reviews/SKILL.md +++ b/skills/converge-reviews/SKILL.md @@ -52,7 +52,7 @@ I invalidate only coverage whose reviewed content, path, or lens changed, and ke ## 3. Reconcile the round -I merge findings by stable ID and preserve source reviewers and disposition history. The caller's finding status stays authoritative. I derive `normalized_status` at reconciliation time and never write it back as caller status. Map `active`, `regression`, and caller-specific open states to `open`; `resolved`, `dismissed`, and `wontfix` to `closed`; `follow-up` to `deferred` outside the current scope; and the filing outcomes `filed <issue URL>` and `declined` to `closed`. +I merge findings by stable ID and preserve source reviewers and disposition history. The caller's finding status stays authoritative. I derive `normalized_status` at reconciliation time and never write it back as caller status. Map `active`, `regression`, and caller-specific open states to `open`; `resolved`, `dismissed`, and `wontfix` to `closed`; `follow-up` and `follow-up-declined` to `deferred` outside the current scope; and the filing outcome `filed <issue URL>` to `closed`. I count a round only after every planned reviewer returned or was explicitly recorded unavailable and the caller reconciled every finding. I record fixes, dismissals, regressions, and newly invalidated coverage before selecting the result. @@ -61,13 +61,13 @@ I count a round only after every planned reviewer returned or was explicitly rec ## 4. Return one result - `continue`: fewer than three rounds are complete and the result names the exact next fix or affected-coverage review. -- `converged`: no Critical or Serious finding is `open`, no Moderate or Minor finding is `open`, no finding of any severity carries disposition `follow-up`, and all required coverage is current. +- `converged`: no Critical or Serious finding is `open`, no Moderate or Minor finding is `open`, no finding of any severity carries disposition `follow-up` or `follow-up-declined`, and all required coverage is current. - `blocked-at-cap`: round three is complete and at least one Critical or Serious finding remains open. Stop review churn and name the exact blocker fix. After that fix, allow one targeted closure check over those blocker IDs and changed sites. It updates coverage and dispositions, runs no new-finding sweep, and does not count as round four. If the check fails, stay blocked until an explicit new plan or scope decision. -- `follow-up-proposed`: no Critical or Serious finding is `open`, and at least one finding remains that blocks `converged`: a Moderate or Minor finding still `open`, or a finding of any severity with disposition `follow-up`. The result lists every such finding as a proposed follow-up issue, so nothing drops silently. +- `follow-up-proposed`: no Critical or Serious finding is `open`, and at least one finding remains that blocks `converged`: a Moderate or Minor finding still `open`, or a finding of any severity with disposition `follow-up` or `follow-up-declined`. The result lists every such finding as a proposed follow-up issue, so nothing drops silently. Round three is a hard cap for the stable scope. Reworded findings, another reviewer over unchanged coverage, or a cosmetic diff count as the same round, never round four. The one targeted blocker closure check only verifies named fixes. It cannot widen coverage or emit fresh findings. -The caller files a proposed issue automatically, after `preflight-mutations`, when its finding is Critical, Serious, or an important user-facing problem, one a user of the shipped product can hit. It asks the user about every other proposed issue. An unattended run files only that automatic class. Every proposed issue that ends unfiled, whether held by an unattended run, declined by the user, or blocked by `preflight-mutations`, goes under "Not filed" in the PR body or report with its reason. A caller whose findings already live as PR review threads, such as `review-pr`, files nothing and lists each proposal under "Not filed" with its reason. Once an issue is filed or declined, I record its disposition as `filed <issue URL>` or `declined`, so later rounds never propose it again. +The caller files a proposed issue automatically, after `preflight-mutations`, when its finding is Critical, Serious, or an important user-facing problem, one a user of the shipped product can hit. It asks the user about every other proposed issue. An unattended run files only that automatic class. Every proposed issue that ends unfiled, whether held by an unattended run, declined by the user, or blocked by `preflight-mutations`, goes under "Not filed" in the PR body or report with its reason. A caller whose findings already live as PR review threads, such as `review-pr`, files nothing and lists each proposal under "Not filed" with its reason. Once an issue is filed, I record its disposition as `filed <issue URL>`. When the user declines, I record its disposition as `follow-up-declined`, so the finding still blocks `converged` and later rounds list it under "Not filed" as declined without asking again. The caller applies this contract. It reruns affected review coverage only for `continue`, proceeds for `converged`, stops for `blocked-at-cap`, and applies the filing rule above for `follow-up-proposed`. After a named blocker fix at the cap, `closure_check: available` is the dispatch signal for the one targeted check. The caller passes only those blocker IDs and changed sites, leaves `round` unchanged, and rejects any new-finding sweep. diff --git a/skills/fix-pr-review/SKILL.md b/skills/fix-pr-review/SKILL.md index 0610652..bd5a736 100644 --- a/skills/fix-pr-review/SKILL.md +++ b/skills/fix-pr-review/SKILL.md @@ -326,7 +326,7 @@ For each selected item, use a follow-up AskUserQuestion: On "FIX": re-dispatch the classifier scoped to just this item to produce the full FIX field set: `fix_plan`, `change_class`, `test_scenario`, `inverse_risk`, `class_completeness` (class sweep included; a reclassified item has never been swept), and `reusability_context` (carry the contested item's own value through unless the sweep changed it). Then re-run plan validation on the changed item. Producing a partial field set here fails validation and burns the single retry. On "NEEDS-INPUT": move to NEEDS-INPUT with `why_unclear: "user contested the <classification> classification"`, carrying the item's `reusability_context` through unchanged. On "Keep as-is": no change. On "Other": treat the freeform text as the reclassification instruction. -This step posts and resolves nothing. Only Phase 7, the held FIX posting after a landed push, and Phase 8 step 2c's follow-up filing touch GitHub, and contested items post **only** when they passed this confirmation, asked or evidence-confirmed. A DISMISS, DEFER, or DISAGREE that has not passed this gate stays open and unanswered. +This step posts and resolves nothing. Only Phase 7, Phase 8 step 1's Defer and Dismiss replies, the Phase 8 push, the held FIX posting after it lands, and Phase 8 step 2c's follow-up filing touch GitHub, and contested items post **only** when they passed this confirmation, asked or evidence-confirmed. A DISMISS, DEFER, or DISAGREE that has not passed this gate stays open and unanswered. ### Execution @@ -513,7 +513,7 @@ This skill never reads or writes `/review-pr`'s cache (`~/.claude/skills/review- Replying and resolving threads is this phase's entire GitHub footprint. The review's own `CHANGES_REQUESTED` state stays exactly as CodeRabbit left it. CodeRabbit clears it itself on its next auto-re-review, once the user pushes. -In every run, post and resolve only contested items here; when `UNATTENDED_WATCH=true`, only the auto-confirmed ones. Steps 7a and 7b still run for every FIX item now, while the fix is uncommitted, and freeze its validated reply. Record every FIX item's reply and resolution as `skipped` with the reason `held until push`; Phase 8 step 1 adds each NEEDS-INPUT item it fixes to the same held set. Held FIX replies post, and their threads resolve, only after a push read-back records the push landed: in Phase 8 step 5 after "Push to remote", or in step 2b of an unattended watch run. +In every run, post and resolve only contested items here; when `UNATTENDED_WATCH=true`, only the auto-confirmed ones. Steps 7a and 7b still run for every FIX item now, while the fix is uncommitted, and freeze its validated reply. Record every FIX item's reply and resolution as `skipped` with the reason `held until push`, except a `has_github_surface` item Step 7b logs as `reply_invalid`, whose reply and resolution are recorded `skipped` with `reply_invalid` as `reply_err` and `resolve_err`, and which stays out of the held set; Phase 8 step 1 adds each NEEDS-INPUT item it fixes to the same held set. Held FIX replies post, and their threads resolve, only after a push read-back records the push landed: in Phase 8 step 5 after "Push to remote", or in step 2b of an unattended watch run. Define `has_github_surface[idx] = thread_id != null AND (can_reply OR can_resolve)`. Initialize every item without that surface to paired `not-applicable` states, regardless of input source. For `./review.md` or any other local-file input, all items meet that condition; initialize them, then skip the rest of this phase. Phase 8 still requires deterministic per-item state for its final report. @@ -569,7 +569,7 @@ Runs only when `UNATTENDED_WATCH=true`, before the report. First compute `invers ### 2c. File follow-up proposals -Runs before the report when Phase 6 recorded `follow-up-proposed`. Apply `converge-reviews`' filing rule to each proposal: file the automatic class through `file-issue` after `preflight-mutations`, ask about the rest, and in an unattended watch run file only the automatic class. Record each outcome as `filed <issue URL>` or the reason it stays unfiled; the report's Not filed line lists the unfiled ones. +Runs before the report when Phase 6 recorded `follow-up-proposed`. First drop any proposal whose finding a Phase 8 fix resolved, checked against the final `done_verified_snapshot`. Apply `converge-reviews`' filing rule to each proposal: file the automatic class through `file-issue` after `preflight-mutations`, ask about the rest, and in an unattended watch run file only the automatic class. Record each outcome as `filed <issue URL>` or the reason it stays unfiled; the report's Not filed line lists the unfiled ones. ### 3. Print the final report once diff --git a/skills/fix-pr-review/references/needs-input-triage.md b/skills/fix-pr-review/references/needs-input-triage.md index 69cac4d..e48974b 100644 --- a/skills/fix-pr-review/references/needs-input-triage.md +++ b/skills/fix-pr-review/references/needs-input-triage.md @@ -49,7 +49,7 @@ On "Fix it", use a follow-up AskUserQuestion to collect guidance: A Critical or Serious blocker, validation abort, edit failure, exhausted retry, or Phase 6 failure aborts only this item. Before restoring, record the exact triggering error or blocker as `phase8_failure_reason[idx]`. Restore every declared path from `phase8_item_snapshot[idx]`, including removing a path whose snapshot recorded authoritative absence, through the state-preserving restore rule with fallback `restored_failed`; set `needs_input_status[idx]=failed`. Preserve existing convergence evidence and append `restored item snapshot after <phase8_failure_reason>`; when convergence never ran, set `convergence[idx]=not-run, <phase8_failure_reason>; item snapshot restored`. Set paired `gh_status` states to `not-applicable` when `has_github_surface=false` or to `skipped` with `no landed fix, <phase8_failure_reason>; item restored` otherwise. Clear the context and continue with the next independent item without restoring the run-level stash, exiting the skill, or bypassing the renderer. Ordinary Phase 5 and Phase 6 behavior remains unchanged when `phase8_triage_context` is false. - When the fix and verification land cleanly, record current content risk as clear without changing the immutable snapshot state, then update `done_verified_snapshot` from the prior snapshot plus this item's verified declared-path bytes. If `phase8_snapshot_fix_state[idx]=inverse_risk_applied` and `phase8_remediation_kind[idx]=removal`, set `fix_status[idx]=reverted_inverse_risk`, set `needs_input_status[idx]=failed`, set paired `gh_status` states to `not-applicable` when `has_github_surface=false` or to `skipped` with `original finding not accepted, risky fix removed` otherwise, clear `phase8_triage_context`, and rejoin Phase 8 without GitHub reply or resolution. A verified replacement retains its applicable landed status. For a landed fix, set its classification to `FIX`, then run Steps 7a and 7b to freeze its `reply_final`, record its reply and resolution as `skipped` with the reason `held until push`, or `not-applicable` when `has_github_surface=false`, and add it to the held FIX set that Phase 8 step 5 and step 2b post after the push lands. Set `needs_input_status` from `fix_status` alone: `fixed` when it is in `landed_fix_statuses`, otherwise `failed`. Preserve the final classification, `fix_status`, convergence, current-content risk, and `gh_status` before continuing. + When the fix and verification land cleanly, record current content risk as clear without changing the immutable snapshot state, then update `done_verified_snapshot` from the prior snapshot plus this item's verified declared-path bytes. If `phase8_snapshot_fix_state[idx]=inverse_risk_applied` and `phase8_remediation_kind[idx]=removal`, set `fix_status[idx]=reverted_inverse_risk`, set `needs_input_status[idx]=failed`, set paired `gh_status` states to `not-applicable` when `has_github_surface=false` or to `skipped` with `original finding not accepted, risky fix removed` otherwise, clear `phase8_triage_context`, and rejoin Phase 8 without GitHub reply or resolution. A verified replacement retains its applicable landed status. For a landed fix, set its classification to `FIX`, then run Steps 7a and 7b to freeze its `reply_final`. When `has_github_surface=false`, record its reply and resolution as `not-applicable`. Otherwise, when Step 7b logged it `reply_invalid`, record both as `skipped` with `reply_invalid` as `reply_err` and `resolve_err`, and keep it out of the held FIX set. Otherwise record both as `skipped` with the reason `held until push` and add it to the held FIX set that Phase 8 step 5 and step 2b post after the push lands. Set `needs_input_status` from `fix_status` alone: `fixed` when it is in `landed_fix_statuses`, otherwise `failed`. Preserve the final classification, `fix_status`, convergence, current-content risk, and `gh_status` before continuing. Immediately before a chosen Defer or Dismiss reply mutates GitHub, invoke `preflight-mutations` for that item's reply/resolve batch with the exact PR and current head SHA, target thread ID, final reply text, classification, and the per-item choice above. Name the target thread as the batch's durable home, with the per-item choice as its authorization: thread ID plus expected reply/resolve states, re-readable through the API after every write. Apply its result contract before continuing. Record a blocked or confirmed failure as `needs_input_status=failed`, an indeterminate operation as `reconcile-required`, and the exact Phase 7 `gh_status`; then continue to the next independent item. Every branch rejoins report rendering. diff --git a/skills/fix-ts-errors/SKILL.md b/skills/fix-ts-errors/SKILL.md index 92994d3..dc1f58d 100644 --- a/skills/fix-ts-errors/SKILL.md +++ b/skills/fix-ts-errors/SKILL.md @@ -46,7 +46,7 @@ git ls-files --others --exclude-standard -- '*.ts' '*.tsx' | tr '\n' '\0' | xarg The `[!]` groups match a postfix assertion: a value character, closing quote, bracket, or paren before the bang and no equals after it, so `value!`, `value!.x`, `value![0]`, `value!()`, `"value"!`, and backtick-quoted receivers all hit while `!=`, `!==`, and prefix negation like `!ready` stay out. The trailing `sed` neutralizes only the `as const` idiom into a marker instead of dropping whole lines, so co-occurring hatches on the same line survive. A line whose only match is the `__AS_CONST__` marker needs no action. -I skip import aliases like `import { x as y }`. For every remaining hit I attempt removal with proper typing, meaning inference, narrowing, type guards, generics, or schema-derived types with `z.infer`. I re-run the check after each removal, and when errors appear I go back to Step 3. A hatch survives only with a one-line comment citing the upstream issue as a URL or `#N`. When no upstream issue exists, the comment cites a permalink URL to the line in the library's type definition that causes the gap. A `// @ts-expect-error`, bare or with a `-- <reason>` description, in a test file (`*.test.*`, `*.spec.*`, `*.test-d.ts`) whose enclosing test's name states that the compiler rejects that line is a type test, not a hatch. The directive is code, not a comment, and stays with no added comment. +I skip import aliases like `import { x as y }`. For every remaining hit I attempt removal with proper typing, meaning inference, narrowing, type guards, generics, or schema-derived types with `z.infer`. I re-run the check after each removal, and when errors appear I go back to Step 3. A hatch survives only with a one-line comment citing the upstream issue as a URL or `#N`. When no upstream issue exists, the comment cites a permalink URL to the line in the library's type definition that causes the gap. A `// @ts-expect-error`, bare or with a `-- <reason>` description, in a test file (`*.test.*`, `*.spec.*`, `*.test-d.ts`) whose enclosing test's name states that the compiler rejects that line, or that sits at the top level of a `*.test-d.ts` file, is a type test, not a hatch. The directive is code, not a comment, and stays with no added comment. ### Step 6: Report diff --git a/skills/openclaw-backup/SKILL.md b/skills/openclaw-backup/SKILL.md index 805ec9a..6bd4602 100644 --- a/skills/openclaw-backup/SKILL.md +++ b/skills/openclaw-backup/SKILL.md @@ -163,7 +163,7 @@ A backup on the same disk as the thing it protects survives config mistakes, not Before `rsync`, verify from the receiving system that the destination is encrypted at rest, accessible only to the intended recipient accounts, and governed by a known retention period and tested deletion path. Confirm the destination host key against a known-good value before the first transfer, so a mistyped host or a first-connect interception cannot divert the copy. A destination missing any of those controls is blocked. -Decide before the preflight whether the official archive goes off-box. Pull it unless this run lists its contents with `tar -tzf` and confirms every path in it appears in a fresh listing of the raw archive. Set `OFFICIAL_PULLED=yes` when it goes, or `OFFICIAL_PULLED=no` and add `--exclude='official/'` to the `rsync` below when it is omitted. Record in the report which applied. +Decide before the preflight whether the official archive goes off-box. Pull it unless this run lists its contents with `tar -tzf` and confirms every path in it appears in a fresh listing of the raw archive. Set `OFFICIAL_PULLED=yes` when it goes, or `OFFICIAL_PULLED=no` and add `--exclude='official/'` to the `rsync` below when it is omitted. When omitting, append a line to `RESTORE.md`, which the manifest does not hash, saying the off-box copy has no `official/`, so its manifest entry and the official-archive fallback apply only on the source host at `$BACKUP_DIR`. Record in the report which applied. Immediately before `rsync`, invoke `preflight-mutations` with the destination evidence, the exact source and destination host and path, included artifact inventory with `official/` in or out per that decision, credential sensitivity, recipient, retention and deletion path, current destination state, checksum and access-control read-back, and the user's off-box-copy approval. Apply its result contract before copying. diff --git a/skills/resolving-merge-conflicts/SKILL.md b/skills/resolving-merge-conflicts/SKILL.md index 805ce2f..7921257 100644 --- a/skills/resolving-merge-conflicts/SKILL.md +++ b/skills/resolving-merge-conflicts/SKILL.md @@ -3,7 +3,7 @@ name: resolving-merge-conflicts description: "Resolve an in-progress git conflict without a silent drop. Use when the worktree is mid-merge, mid-rebase, mid-cherry-pick or mid-stash-pop: `CONFLICT (content)`, `<<<<<<<` markers, `Unmerged paths`, or `fix conflicts and then commit the result`. Also use when asked to resolve or fix merge conflicts on a PR or branch before any merge has started." --- -1. Orient before you touch anything. Establish from `git status` whether you are in a merge, a rebase, a cherry-pick, a revert, a `git am`, or a stash pop. Unmerged paths with none of the other five in progress mean a conflicting stash pop; confirm it with `git stash list` and the request, and ask the user when their origin is unclear. When none is in progress and nothing is unmerged, start a merge only from a clean worktree. When `git status --porcelain` lists anything, stop and ask the user to commit it, stash it, or move it to a separate worktree; never stash it yourself. For a PR, the base repository is the PR URL's `<owner>/<repo>`; read the base ref, its tip, the head branch, and the head repository with `gh pr view <url> --json baseRefName,baseRefOid,headRefName,headRepository`. The base branch is the one the request names, else the PR's `baseRefName`, else the default branch from `gh repo view --json defaultBranchRef`. When the head branch, `headRefName` for a PR, is not checked out, stop and ask. Fetch from the remote that resolves to the base repository, require the fetched base ref to equal `baseRefOid` when the base is the PR's, merge that remote's base ref into the head branch, and orient. Rebase only when the user asked for one, because rebasing a published branch rewrites its history. Under a rebase `ours` is the upstream you are replaying onto and `theirs` is your own commit, the inverse of a merge, and getting this backwards is the most common wrong resolution. +1. Orient before you touch anything. Establish from `git status` whether you are in a merge, a rebase, a cherry-pick, a revert, a `git am`, or a stash pop. Unmerged paths with none of the other five in progress mean a conflicting stash pop; confirm it with `git stash list` and the request, and ask the user when their origin is unclear. When none is in progress and nothing is unmerged, start a merge only from a clean worktree. When `git status --porcelain` lists anything, stop and ask the user to commit it, stash it, or move it to a separate worktree; never stash it yourself. For a PR, the base repository is the PR URL's `<owner>/<repo>`; read the base ref, its tip, the head branch, and the head repository with `gh pr view <url> --json baseRefName,baseRefOid,headRefName,headRepository`. The base branch is the one the request names, else the PR's `baseRefName`, else the default branch from `gh repo view --json defaultBranchRef`. When the head branch, `headRefName` for a PR, is not checked out, stop and ask. Fetch from the remote that resolves to the base repository, require the fetched base ref to equal `baseRefOid` when the base is the PR's, merge that remote's base ref into the head branch, or rebase onto it when the user asked for a rebase, and orient. Rebase only when the user asked for one, because rebasing a published branch rewrites its history; step 5's `preflight-mutations` gate covers its push. Under a rebase `ours` is the upstream you are replaying onto and `theirs` is your own commit, the inverse of a merge, and getting this backwards is the most common wrong resolution. Then split the conflicted files. Regenerate generated artifacts and lockfiles from source, never hand-merge them. That means `bun.lock`, `pnpm-lock.yaml`, `_generated/`, and snapshots. Only hand-written files go through steps 2-3. diff --git a/skills/simplify/SKILL.md b/skills/simplify/SKILL.md index b103ca2..68d47c4 100644 --- a/skills/simplify/SKILL.md +++ b/skills/simplify/SKILL.md @@ -27,7 +27,7 @@ I prefer deletion and direct code over a new helper. I reuse an existing local p ### Added-comment scan -I inspect every comment the change adds under the global comment rule. A comment stays only when it is a one-line `/** */` contract on an exported symbol, or a one-line citation carrying a URL, a spec section, an ADR or `docs/` path, or an issue number. I delete everything else on sight, including any comment that explains what the code does, why it has its shape, or what would break. I run the rule's grep over the diff and delete every comment it prints, except a `// @ts-expect-error`, bare or with a `-- <reason>` description, in a test file (`*.test.*`, `*.spec.*`, `*.test-d.ts`) whose enclosing test's name states that the compiler rejects that line, which is a `fix-ts-errors` type test and code, not a comment. A project rule may tighten this scan, never loosen it. +I inspect every comment the change adds under the global comment rule. A comment stays only when it is a one-line `/** */` contract on an exported symbol, or a one-line citation carrying a URL, a spec section, an ADR or `docs/` path, or an issue number. I delete everything else on sight, including any comment that explains what the code does, why it has its shape, or what would break. I run the rule's grep over the diff and delete every comment it prints, except a `// @ts-expect-error`, bare or with a `-- <reason>` description, in a test file (`*.test.*`, `*.spec.*`, `*.test-d.ts`) whose enclosing test's name states that the compiler rejects that line, or that sits at the top level of a `*.test-d.ts` file, which is a `fix-ts-errors` type test and code, not a comment. A project rule may tighten this scan, never loosen it. Every proposed edit names what turns simpler and why observable behavior stays equivalent, and no uncited comment the change adds remains. This scan blocks. The caller cannot report the work complete while one remains in the diff. From b12bd2d2ece3b86c6e1bda8715e970dab20498e7 Mon Sep 17 00:00:00 2001 From: Bhagya Mudgal <bhagyamudgal@gmail.com> Date: Tue, 10 Nov 2026 19:12:58 +0530 Subject: [PATCH 8/8] fix(converge-reviews): let a resolved or legacy-declined finding settle The follow-up-declined disposition from 02c2c89 had three gaps: a finding resolved after a decline was proposed forever because the result rules keyed on disposition alone, ledgers written before it still carried a bare declined with no mapping, and no caller was told to write the decline back, so it was asked again next round. A status that maps to closed now wins over any disposition, a bare declined reads as follow-up-declined, and the caller records both filing outcomes in the dispositions it passes on, with fix-pr-review step 2c naming the decline. fix-pr-review's Phase 7 summary now carries the reply_invalid exception the reference already applies. --- skills/converge-reviews/SKILL.md | 8 ++++---- skills/fix-pr-review/SKILL.md | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/skills/converge-reviews/SKILL.md b/skills/converge-reviews/SKILL.md index 4c56a00..89a2bcf 100644 --- a/skills/converge-reviews/SKILL.md +++ b/skills/converge-reviews/SKILL.md @@ -52,7 +52,7 @@ I invalidate only coverage whose reviewed content, path, or lens changed, and ke ## 3. Reconcile the round -I merge findings by stable ID and preserve source reviewers and disposition history. The caller's finding status stays authoritative. I derive `normalized_status` at reconciliation time and never write it back as caller status. Map `active`, `regression`, and caller-specific open states to `open`; `resolved`, `dismissed`, and `wontfix` to `closed`; `follow-up` and `follow-up-declined` to `deferred` outside the current scope; and the filing outcome `filed <issue URL>` to `closed`. +I merge findings by stable ID and preserve source reviewers and disposition history. The caller's finding status stays authoritative. I derive `normalized_status` at reconciliation time and never write it back as caller status. Map `active`, `regression`, and caller-specific open states to `open`; `resolved`, `dismissed`, and `wontfix` to `closed`; `follow-up` and `follow-up-declined` to `deferred` outside the current scope; and the filing outcome `filed <issue URL>` to `closed`. A legacy bare `declined` reads as `follow-up-declined`. A caller status that maps to `closed` wins over any disposition, so a finding resolved after a decline stops being proposed. I count a round only after every planned reviewer returned or was explicitly recorded unavailable and the caller reconciled every finding. I record fixes, dismissals, regressions, and newly invalidated coverage before selecting the result. @@ -61,13 +61,13 @@ I count a round only after every planned reviewer returned or was explicitly rec ## 4. Return one result - `continue`: fewer than three rounds are complete and the result names the exact next fix or affected-coverage review. -- `converged`: no Critical or Serious finding is `open`, no Moderate or Minor finding is `open`, no finding of any severity carries disposition `follow-up` or `follow-up-declined`, and all required coverage is current. +- `converged`: no Critical or Serious finding is `open`, no Moderate or Minor finding is `open`, no finding that is not `closed` carries disposition `follow-up` or `follow-up-declined`, and all required coverage is current. - `blocked-at-cap`: round three is complete and at least one Critical or Serious finding remains open. Stop review churn and name the exact blocker fix. After that fix, allow one targeted closure check over those blocker IDs and changed sites. It updates coverage and dispositions, runs no new-finding sweep, and does not count as round four. If the check fails, stay blocked until an explicit new plan or scope decision. -- `follow-up-proposed`: no Critical or Serious finding is `open`, and at least one finding remains that blocks `converged`: a Moderate or Minor finding still `open`, or a finding of any severity with disposition `follow-up` or `follow-up-declined`. The result lists every such finding as a proposed follow-up issue, so nothing drops silently. +- `follow-up-proposed`: no Critical or Serious finding is `open`, and at least one finding remains that blocks `converged`: a Moderate or Minor finding still `open`, or a finding that is not `closed` with disposition `follow-up` or `follow-up-declined`. The result lists every such finding as a proposed follow-up issue, so nothing drops silently. Round three is a hard cap for the stable scope. Reworded findings, another reviewer over unchanged coverage, or a cosmetic diff count as the same round, never round four. The one targeted blocker closure check only verifies named fixes. It cannot widen coverage or emit fresh findings. -The caller files a proposed issue automatically, after `preflight-mutations`, when its finding is Critical, Serious, or an important user-facing problem, one a user of the shipped product can hit. It asks the user about every other proposed issue. An unattended run files only that automatic class. Every proposed issue that ends unfiled, whether held by an unattended run, declined by the user, or blocked by `preflight-mutations`, goes under "Not filed" in the PR body or report with its reason. A caller whose findings already live as PR review threads, such as `review-pr`, files nothing and lists each proposal under "Not filed" with its reason. Once an issue is filed, I record its disposition as `filed <issue URL>`. When the user declines, I record its disposition as `follow-up-declined`, so the finding still blocks `converged` and later rounds list it under "Not filed" as declined without asking again. +The caller files a proposed issue automatically, after `preflight-mutations`, when its finding is Critical, Serious, or an important user-facing problem, one a user of the shipped product can hit. It asks the user about every other proposed issue. An unattended run files only that automatic class. Every proposed issue that ends unfiled, whether held by an unattended run, declined by the user, or blocked by `preflight-mutations`, goes under "Not filed" in the PR body or report with its reason. A caller whose findings already live as PR review threads, such as `review-pr`, files nothing and lists each proposal under "Not filed" with its reason. In the dispositions it passes to the next round, the caller records a filed issue as `filed <issue URL>`, which closes the finding, and a user decline as `follow-up-declined`, which keeps blocking `converged` while later rounds list it under "Not filed" as declined without asking again. The caller applies this contract. It reruns affected review coverage only for `continue`, proceeds for `converged`, stops for `blocked-at-cap`, and applies the filing rule above for `follow-up-proposed`. After a named blocker fix at the cap, `closure_check: available` is the dispatch signal for the one targeted check. The caller passes only those blocker IDs and changed sites, leaves `round` unchanged, and rejects any new-finding sweep. diff --git a/skills/fix-pr-review/SKILL.md b/skills/fix-pr-review/SKILL.md index bd5a736..939d2a8 100644 --- a/skills/fix-pr-review/SKILL.md +++ b/skills/fix-pr-review/SKILL.md @@ -513,7 +513,7 @@ This skill never reads or writes `/review-pr`'s cache (`~/.claude/skills/review- Replying and resolving threads is this phase's entire GitHub footprint. The review's own `CHANGES_REQUESTED` state stays exactly as CodeRabbit left it. CodeRabbit clears it itself on its next auto-re-review, once the user pushes. -In every run, post and resolve only contested items here; when `UNATTENDED_WATCH=true`, only the auto-confirmed ones. Steps 7a and 7b still run for every FIX item now, while the fix is uncommitted, and freeze its validated reply. Record every FIX item's reply and resolution as `skipped` with the reason `held until push`, except a `has_github_surface` item Step 7b logs as `reply_invalid`, whose reply and resolution are recorded `skipped` with `reply_invalid` as `reply_err` and `resolve_err`, and which stays out of the held set; Phase 8 step 1 adds each NEEDS-INPUT item it fixes to the same held set. Held FIX replies post, and their threads resolve, only after a push read-back records the push landed: in Phase 8 step 5 after "Push to remote", or in step 2b of an unattended watch run. +In every run, post and resolve only contested items here; when `UNATTENDED_WATCH=true`, only the auto-confirmed ones. Steps 7a and 7b still run for every FIX item now, while the fix is uncommitted, and freeze its validated reply. Record every FIX item's reply and resolution as `skipped` with the reason `held until push`, except a `has_github_surface` item Step 7b logs as `reply_invalid`, whose reply and resolution are recorded `skipped` with `reply_invalid` as `reply_err` and `resolve_err`, and which stays out of the held set; Phase 8 step 1 adds each NEEDS-INPUT item it fixes to the same held set, except one Step 7b logs as `reply_invalid`. Held FIX replies post, and their threads resolve, only after a push read-back records the push landed: in Phase 8 step 5 after "Push to remote", or in step 2b of an unattended watch run. Define `has_github_surface[idx] = thread_id != null AND (can_reply OR can_resolve)`. Initialize every item without that surface to paired `not-applicable` states, regardless of input source. For `./review.md` or any other local-file input, all items meet that condition; initialize them, then skip the rest of this phase. Phase 8 still requires deterministic per-item state for its final report. @@ -569,7 +569,7 @@ Runs only when `UNATTENDED_WATCH=true`, before the report. First compute `invers ### 2c. File follow-up proposals -Runs before the report when Phase 6 recorded `follow-up-proposed`. First drop any proposal whose finding a Phase 8 fix resolved, checked against the final `done_verified_snapshot`. Apply `converge-reviews`' filing rule to each proposal: file the automatic class through `file-issue` after `preflight-mutations`, ask about the rest, and in an unattended watch run file only the automatic class. Record each outcome as `filed <issue URL>` or the reason it stays unfiled; the report's Not filed line lists the unfiled ones. +Runs before the report when Phase 6 recorded `follow-up-proposed`. First drop any proposal whose finding a Phase 8 fix resolved, checked against the final `done_verified_snapshot`. Apply `converge-reviews`' filing rule to each proposal: file the automatic class through `file-issue` after `preflight-mutations`, ask about the rest, and in an unattended watch run file only the automatic class. Record each outcome as `filed <issue URL>`, as disposition `follow-up-declined` when the user declines, or as the reason it stays unfiled; the report's Not filed line lists the unfiled ones. ### 3. Print the final report once