From 66a1f3f663410d939a3611ddaadcfc34da69ea8a Mon Sep 17 00:00:00 2001 From: Griffin Long Date: Wed, 30 Sep 2026 15:15:13 -0400 Subject: [PATCH 1/2] feat: sync 10 skills to agent-afk bundled versions agent-afk loads user plugins before its bundled skills and the first copy of a name wins, so these older copies were overriding the newer bundled ones. Replace each SKILL.md with the agent-afk bundled version (agent-afk 2773cec7, v5.276.18), byte-for-byte: automate, diagnose, fix-pr, ground-state, refactor, research, review, shadow-verify, ship, simplify --- skills/automate/SKILL.md | 21 +++++---- skills/diagnose/SKILL.md | 6 +++ skills/fix-pr/SKILL.md | 87 ++++++++++++++++++++++++++++++----- skills/ground-state/SKILL.md | 38 +++++++++++++-- skills/refactor/SKILL.md | 1 + skills/research/SKILL.md | 4 ++ skills/review/SKILL.md | 45 ++++++++++++++---- skills/shadow-verify/SKILL.md | 1 + skills/ship/SKILL.md | 22 +++++++++ skills/simplify/SKILL.md | 1 + 10 files changed, 192 insertions(+), 34 deletions(-) diff --git a/skills/automate/SKILL.md b/skills/automate/SKILL.md index d5f2726..3adc18a 100644 --- a/skills/automate/SKILL.md +++ b/skills/automate/SKILL.md @@ -1,18 +1,19 @@ --- name: automate -description: "Set up a scheduled headless Claude run that sends summaries to Telegram. Use when the user wants to automate a recurring task via launchd (macOS) with push-notified results." +description: "Set up a scheduled headless afk run that pushes a summary to Telegram. Use when the user wants to automate a recurring task via the afk daemon scheduler (cron) with push-notified results." disable-model-invocation: true +context: load --- -Dispatch two sub-agents in parallel: -1. **Scout** — inspect `~/.claude/automations/` (scripts, plists, existing jobs) and the target project folder to surface conventions, naming, and overlapping jobs. Report any conflicts. -2. **Prompt designer** — draft the headless Claude prompt for the recurring task. Encode the input context explicitly and require a concise Telegram-ready summary as output. +Set up a recurring headless task using afk's **native** scheduler. Do NOT hand-roll launchd plists or shell scripts — afk has first-class scheduling: the `create_schedule` tool writes `~/.afk/config/schedules.json` entries that `afk daemon` runs on cron. -When both return, generate: -- Shell script at `~/.claude/automations/scripts/.sh` that `cd`s into the target folder and runs: - `claude --dangerously-skip-permissions --channels plugin:telegram@claude-plugins-official ""` -- launchd plist at `~/Library/LaunchAgents/com.claude.automate..plist` with the requested `StartCalendarInterval` or `StartInterval`, plus `StandardOutPath`/`StandardErrorPath` pointing at `~/.claude/automations/logs/.log`. +Dispatch two sub-agents in parallel: +1. **Scout** — call `list_schedules` and inspect `~/.afk/config/schedules.json` for existing or overlapping jobs, run `afk service status` to see whether the daemon is installed as a launchd service and running, confirm Telegram is configured (`TELEGRAM_BOT_TOKEN` + `AFK_TELEGRAM_ALLOWED_CHAT_IDS`), and scan the target project folder for conventions. Report conflicts, daemon/service state, and any missing prerequisite. +2. **Prompt designer** — draft the recurring task's `command` string: a self-contained prompt (or `/skill --flags` invocation) sent verbatim into a freshly spawned session each run. The daemon session starts cold, so encode all input context explicitly, and require the run to END by calling the `send_telegram` tool with a concise, push-ready summary. -Load it: `launchctl bootstrap gui/$(id -u) ` and confirm with `launchctl list | grep `. +When both return: +- If Telegram is unconfigured, stop and tell the user to run `/telegram-setup` first — `send_telegram` fails closed without `TELEGRAM_BOT_TOKEN` and `AFK_TELEGRAM_ALLOWED_CHAT_IDS`. +- Create the job with the `create_schedule` tool: `name`, the designed `command`, the requested 5-field `cron`, `trigger: "cron"`, and `notifyOn: "failure"` as a crash safety-net (the per-run summary comes from the agent's own `send_telegram` call, not from `notifyOn`). +- Schedules only fire while the daemon is running, so make it survive reboot/crash: if `afk service status` shows the daemon isn't installed, run `afk service install daemon` (launchd `RunAtLoad` + `KeepAlive` on macOS). -Dispatch a **verification** sub-agent to run the script once manually, confirm the Telegram summary arrives, and tail the log. On failure, diagnose (permissions, path, channel config, prompt shape) and fix before exiting. Report final status, script path, plist path, and next scheduled run time. +Dispatch a **verification** sub-agent to confirm the job registered (`list_schedules`), the daemon is running (`afk service status`), and — by running the task's `command` once as a one-shot (`afk chat ""`) — that the Telegram summary actually arrives. On failure, diagnose (Telegram config, daemon not running, cron syntax, prompt shape) and fix before exiting. Report the schedule id, cron, `notifyOn`, daemon/service status, and the next scheduled run. diff --git a/skills/diagnose/SKILL.md b/skills/diagnose/SKILL.md index fe68403..560e500 100644 --- a/skills/diagnose/SKILL.md +++ b/skills/diagnose/SKILL.md @@ -2,10 +2,16 @@ name: diagnose description: "Parallel root-cause analysis for bugs and failing tests. Use when a test fails, a bug is reported, or behavior is unexplained — dispatches sub-agents to form and validate hypotheses in isolated worktrees." context: fork +category: Debug & fix --- Gather context: read the failing test or bug description, relevant error output, and recent git changes. If no failing test exists yet, write a minimal reproducer test (or identify a concrete verification command) before proceeding — hypotheses need a pass/fail signal to validate against. Dispatch two sub-agents in parallel — one to search the codebase for code paths involved in the failure (`subagent_type: research-agent`, read-only), and one to check recent commits and diffs that could have introduced the regression (`subagent_type: general-purpose` — requires Bash for `git log`/`git diff`/`git show`). When both return, synthesize findings into 2–4 ranked hypotheses, each with a specific code location and proposed cause. +**TUI label convention.** Set `id_prefix` on each Agent dispatch to a short, descriptive label so the live progress view shows the agent's role: +- Codebase search agent: `id_prefix: "codebase search"` +- Git history agent: `id_prefix: "git history"` +- Each hypothesis-testing agent: `id_prefix: "hypothesis: "` (e.g. `"hypothesis: null deref in parser"`) + For each hypothesis, dispatch a sub-agent with `isolation: "worktree"` to apply a minimal speculative fix, run the test or verification command, and then run the broader related test suite to check for regressions. Run all hypothesis-testing agents in parallel. Collect results: which fixes passed, which didn't, and any regressions surfaced by the broader suite. Report the validated root cause (the hypothesis whose fix passed), the speculative fix diff, and regression status from the broader test run. If no hypothesis passes, synthesize what was learned and form a second round of hypotheses. If the user approves the fix, apply it to the main worktree. diff --git a/skills/fix-pr/SKILL.md b/skills/fix-pr/SKILL.md index 708edba..1a81f1d 100644 --- a/skills/fix-pr/SKILL.md +++ b/skills/fix-pr/SKILL.md @@ -1,6 +1,7 @@ --- name: fix-pr -description: "One-verb pipeline for the operator's highest-frequency manual loop: fetch a PR's unresolved reviewer feedback (inline review comments, review-summary bodies, and issue-level conversation comments) and failing CI checks, fix them in an isolated managed worktree via a budget-bounded subagent, verify with the project's test gates, and push the fix back to the PR branch. Replaces the retyped recipe 'send a subagent in a worktree to fix , then push.' Use when a PR has review comments or red CI that needs addressing — e.g. 'fix pr 286', 'address the review on #215', 'CI is red on the worktree-sweep PR'. Never force-pushes, never touches main, fails closed on missing gh auth or un-pushable fork PRs." +description: "One-verb pipeline for the operator's highest-frequency manual loop: fetch a PR's unresolved reviewer feedback (inline review comments, review-summary bodies, and issue-level conversation comments) and failing CI checks, triage each issue via parallel read-only probes, fix the validated items in an isolated managed worktree via a budget-bounded subagent, verify with the project's test gates, and push the fix back to the PR branch. Replaces the retyped recipe 'send a subagent in a worktree to fix , then push.' Use when a PR has review comments or red CI that needs addressing — e.g. 'fix pr 286', 'address the review on #215', 'CI is red on the worktree-sweep PR'. Never force-pushes, never touches main, fails closed on missing gh auth or un-pushable fork PRs." +when-to-use: "A PR has review feedback or red CI that needs fixing." argument-hint: " [--repo ] [--no-push] [--re-review]" surface: "afk" failure_modes: @@ -8,12 +9,13 @@ failure_modes: - nested /review max_depth self-collision - silent partial fix (some comments addressed, done claimed for all) - unmanaged worktree leak + - triage probe misclassifies a valid item as invalid (filtered before write agent sees it) --- ## Sub-agent contract /contract -`fix-pr` turns "review feedback / red CI on PR N" into a pushed fix commit with test evidence, using worktree isolation so the operator's working tree is never disturbed. It is the composition the operator previously chained by hand: `/resolve`-style feedback interpretation + managed worktree + budget-bounded fix subagent + test gate + push. +`fix-pr` turns "review feedback / red CI on PR N" into a pushed fix commit with test evidence, using worktree isolation so the operator's working tree is never disturbed. The pipeline has three stages: **harvest** (collect feedback from all GitHub stores), **triage** (parallel read-only probes validate each issue independently), and **fix** (a single write agent applies pre-validated proposals). The parallel triage wave eliminates false-positive spec items and pre-computes fix proposals before any write begins, reducing cycling. **Skip when:** the fix is a one-line suggestion the operator pointed at directly (apply inline); the PR is already green with all threads resolved (report and stop); or the work is local-only and unpushed (use `/ship`). @@ -66,15 +68,70 @@ If a managed worktree for this PR already exists, reuse it only if clean; otherw --- +### Phase 2.5 — Parallel issue triage (read-only probes) + +This phase validates each spec item independently before any writes begin. The goals: filter false positives, detect already-addressed items, and pre-compute fix proposals so the write agent receives validated work orders instead of raw review comments. + +**Fast-path:** if the numbered spec contains exactly 1 item, skip this phase entirely -- dispatch directly to Phase 3 with the raw spec item. The parallel wave's value is proportional to item count; for a single item the overhead exceeds the benefit. + +**For 2+ spec items:** dispatch N read-only sub-agents in parallel via the `agent` tool (one call per spec item in the same tool-use turn -- pure fan-out). `compose` now supports `agent_type`, `cwd`, `readRoots`, and `writeRoots` per node (so the read-only and worktree-scoping guarantees are NOT silently lost when using compose), but the `agent` fan-out is preferred here: it gives simpler rate-limiting control (see the rate note below) and each probe is truly independent with no DAG dependencies to model. If DAG dependency tracking between probes matters for a future extension, compose is a valid alternative -- just wire `agent_type: "research-agent"` and `cwd: ` on each node. Each probe: + +- **agent_type:** `research-agent` (mechanically read-only -- cannot write files, run bash, or commit). +- **cwd:** `` (so file reads target the PR branch, not the main working tree). +- **model:** `haiku` (sufficient for validation; keeps the fan-out cheap). +- **max_tool_use_iterations:** 8 (read the cited code, check surrounding context, form a verdict -- more than enough). +- **inputs (in prompt):** one numbered spec item (comment text, source URL, file/line if known) and the `head_branch`. +- **goal:** read the cited file(s) and surrounding context in the worktree. Determine whether the reviewer's request is (a) valid and unaddressed, (b) already addressed by existing code, or (c) a false positive / misunderstanding. If valid, propose the minimal fix: exact file, location, and the change to make. +- **deliverable (structured):** + - `item_id`: the spec item number. + - `verdict`: `valid` | `already-addressed` | `invalid` | `unclear`. + - `reasoning`: 2-3 sentences explaining the verdict with file:line citations. + - `proposed_fix` (when verdict is `valid`): exact file path, location description, and a concrete description of the change needed. NOT a diff -- a plain-language description the write agent can execute. + - `risk`: `low` | `medium` | `high` -- whether the fix might have non-obvious side effects. + +**After all probes return,** the orchestrator consolidates: + +1. **Filter:** items with verdict `already-addressed` or `invalid` are removed from the fix spec. Note them in the terminal report as "triaged out" with the probe's reasoning. +2. **Escalate:** items with verdict `unclear` stay in the spec but are flagged -- the write agent sees the probe's reasoning and must make its own determination. +3. **Enrich:** items with verdict `valid` carry the probe's `proposed_fix` and `risk` forward into Phase 3 as pre-validated work orders. + +If ALL items are triaged out (every probe returned `already-addressed` or `invalid`), report "All spec items were triaged out by parallel probes -- no changes needed" with the per-item triage table and stop (Done, no mutation). Clean up the worktree. + +**Rate note:** for PRs with >6 spec items, cap the parallel wave at 6 concurrent `agent` calls per turn (remaining items in a second turn) to respect API rate ceilings. + +--- + ### Phase 3 — Fix dispatch (one subagent, budget-bounded) Size the dispatch per `/right-size-delegation` if available; defaults otherwise: -Dispatch ONE implementation subagent (`agent` tool, `cwd: `, `max_turns: 25`, model right-sized to the diff — `sonnet` default, `haiku` never for code fixes): -- inputs: the numbered fix spec, `head_branch`, repo test/lint commands (inferred from package.json / Makefile / pyproject.toml and passed explicitly). -- goal: address every numbered item with minimal diffs; run the narrowest relevant tests per item; commit locally with a message referencing the PR (`fix(pr-): address review feedback`). -- non_goals: do NOT push, do NOT touch branches other than the checked-out one, do NOT invoke /review or any skill, do NOT expand scope beyond the numbered items. -- deliverable: per-item status table (`fixed` | `out-of-scope `), unified diff summary, targeted test output, local commit SHA. +Dispatch ONE implementation subagent (`agent` tool, `cwd: `, `max_turns: 25`, model right-sized to the diff -- `sonnet` default, `haiku` never for code fixes): +- inputs: the **triaged fix spec** (not raw review comments): each surviving item carries its original numbered ID, the reviewer's request, the triage probe's `proposed_fix` and `risk` assessment, and `unclear` items carry the probe's reasoning. Also: `head_branch`, repo test/lint commands (inferred from package.json / Makefile / pyproject.toml and passed explicitly). +- goal: apply every surviving spec item with minimal diffs, using the triage probe's proposed fixes as a starting point (the probe proposals are guidance, not mandates -- the write agent may deviate if it discovers a better approach). Run the narrowest relevant tests per item. **Do NOT commit yet** -- emit the manifest first (see below). +- non_goals: do NOT push, do NOT touch branches other than the checked-out one, do NOT invoke /review or any skill, do NOT expand scope beyond the surviving spec items, do NOT modify files that are not named in the triaged spec unless a fix structurally requires it (e.g. a shared helper, a test file, a baseline file the gate checks). +- deliverable -- **two-part, manifest-then-commit:** + 1. **Pre-commit manifest** (emit BEFORE running `git commit`): + - `touched_files`: list of every file modified or created. + - `spec_item_map`: `{ file: [spec_item_ids] }` -- which numbered spec items justified touching each file. Every file must map to at least one item; a file not named in the spec must carry an explicit justification (e.g. "shared helper used by the fixed call site", "test file covering the changed behavior", "baseline file that the CI gate requires after the edit"). + - `invariant_per_file`: `{ file: "one-sentence claim about what changed and why it is correct" }` -- a falsifiable statement the orchestrator can verify by re-reading the file. + - `out_of_scope`: `{ [spec_item_id]: "reason" }` -- spec items the subagent determined cannot be addressed in this PR, with a reason for each. + 2. **After orchestrator validates the manifest** (see Phase 3.5): emit the per-item status table (`fixed` | `out-of-scope `), unified diff summary, and targeted test output. + +--- + +### Phase 3.5 — Manifest validation (inline, before commit) + +After the fix subagent emits its pre-commit manifest, the orchestrator validates it **before** allowing the commit: + +Run all three checks below, collecting violations into a single list. Do NOT re-dispatch after each individual check -- aggregate first, re-dispatch once after step 3. + +1. **Scope check:** independently enumerate the worktree's modified and untracked files using the union of: `git diff --name-only HEAD` (unstaged changes), `git diff --name-only --cached` (staged changes), and `git ls-files --others --exclude-standard` (untracked files) — deduplicate the combined list. Require an exact match with the subagent's `touched_files` list. Any file present in the worktree diff but absent from `touched_files` → record violation: "File `` was modified in the worktree but not declared in your `touched_files` manifest. Add it to the manifest or revert the change." Then, for each file in `touched_files`, verify it appears in `spec_item_map` with a valid spec item or an explicit justification. Any unjustified file → record violation: "File `` was modified but is not covered by any spec item. Either remove the change or provide a justification." +2. **Invariant spot-check:** re-read each file in `touched_files` in the worktree. For each, check whether the `invariant_per_file` claim is consistent with the file's actual content. Flag obvious contradictions (e.g. claim says "added try/catch around writeFn" but the file has no try/catch near writeFn). A contradicted invariant → record violation naming the specific discrepancy. +3. **Compound-fix completeness:** cross-reference the triaged spec (surviving items only) against `spec_item_map`. If any surviving spec item is absent from the map AND absent from `out_of_scope`, record violation: "Spec item N is not addressed by any touched file and was not declared out-of-scope." + +**After all three checks:** zero violations → pass. One or more violations → re-dispatch with the full violation list as a single message. + +Pass → the orchestrator commits inline: run `git commit -m 'fix(pr-): address review feedback'` in the worktree. Fail → **≤2 manifest-validation re-dispatch iterations** (all violations from steps 1-3 in a single re-dispatch message per iteration). Cap reached with outstanding **scope violations** (unjustified files in `touched_files`, or missing spec items not declared out-of-scope) → emit **Blocked** naming the unresolved violations, the worktree path, and the branch (keep the worktree for manual follow-up). Cap reached with **unverified invariants only** (invariant claims the orchestrator could not confirm but no scope violation) → for each file whose invariant remains unverified, require the subagent to either restate the invariant in a falsifiable form (concrete line number + observable property) or declare the file `out-of-scope`. If at least one file retains a verifiable invariant, commit only the files with verified or restated-and-verified invariants. If zero files pass, emit **Blocked** naming the unverifiable claims. In all cases, note unverified invariants in the terminal report. --- @@ -83,16 +140,20 @@ Dispatch ONE implementation subagent (`agent` tool, `cwd: `, `max Run the project's full test/lint gates in the worktree yourself — do not trust the subagent's report alone. - All green → Phase 5. -- Failures → iterate: re-dispatch the fix subagent with the failure output as an updated spec (or hand off to `/heal` semantics if that skill is loadable), **≤2 iterations**. Cap reached → keep the worktree, emit **Blocked** naming the branch, the worktree path, the surviving failures, and the per-item status table. +- Failures → iterate: re-dispatch the fix subagent with the failure output as an updated spec (or hand off to `/heal` semantics if that skill is loadable), **≤2 test-failure re-dispatch iterations** (independent of Phase 3.5's manifest-validation counter). Each Phase 4 re-dispatch must also pass through Phase 3.5 manifest validation (with a fresh counter) before the orchestrator commits the resulting changes. Cap reached → keep the worktree, emit **Blocked** naming the branch, the worktree path, the surviving failures, and the per-item status table. **Completeness check:** every numbered spec item must be `fixed` or explicitly `out-of-scope` with a reason. A partially addressed spec is never reported as Done. --- -### Phase 5 — Push (guarded) +### Phase 5 — Push (guarded, with PR state re-check) - `no_push` set → `worktree keep` (reason: "fix-pr --no-push review pending"), emit the diff summary + per-item table, stop (Done, no remote mutation). -- Otherwise: `git push origin ` from the worktree. **Plain push only — never `--force`, never `--force-with-lease`, never any other ref.** If push is rejected (non-fast-forward because the PR moved), fetch + rebase the fix commits onto the new head, re-run Phase 4 gates, push again. If still rejected → Blocked. +- Otherwise, **re-check PR state before pushing** -- the PR may have been merged or closed during the fix work: + 1. `gh pr view --json state,mergedAt,mergeCommit` — if `state === "MERGED"`: STOP. Report "PR #N was merged during fix work (merged at ``, commit ``). Fix commits are on branch `` in worktree `` — cherry-pick to a fresh branch if the fixes are still needed." Keep the worktree, emit Done (no push). + 2. If `state === "CLOSED"` (no `mergedAt`): STOP. Report "PR #N was closed without merge during fix work." Keep the worktree, emit Done (no push). + 3. If `state === "OPEN"`: proceed with push. +- `git push origin ` from the worktree. **Plain push only -- never `--force`, never `--force-with-lease`, never any other ref.** If push is rejected (non-fast-forward because the PR moved), fetch + rebase the fix commits onto the new head, re-run Phase 4 gates, push again. If still rejected → Blocked. --- @@ -100,6 +161,10 @@ Run the project's full test/lint gates in the worktree yourself — do not trust If `re_review`: invoke `/review` **directly from this top-level session only — NEVER from inside a subagent** (known max_depth self-collision: 100+ `delegation.skipped reason:"max_depth" requested_name:"review"` entries in routing-decisions.jsonl). If the current session is itself a subagent (check `get_runtime_state` depth), skip re-review and note it in the terminal report instead. +**Scope the re-review via `--brief`:** pass the numbered fix spec from Phase 1 as the `--brief` argument to `/review`. This anchors the re-review's spec-compliance assessment to "were these specific items addressed?" rather than a full open-ended sweep. The review skill's stated-intent capture and spec-compliance assessment will assess the fix against the original findings as its spec — findings from other review dimensions (security, api-compat, perf, etc.) are reported normally and unaffected by `--brief`; the spec-compliance dimension flags deviations from the original findings as scope creep or unmet intent, so the reviewer can judge whether the original items were resolved. + +**Note:** the manifest gate (Phase 3.5) catches over-broad scope; logic inversions introduced by the fix subagent are caught only when `--re-review` is passed. For high-risk fixes, pass `--re-review` to enable this detection. + --- ### Phase 7 — Cleanup & terminal state @@ -107,5 +172,5 @@ If `re_review`: invoke `/review` **directly from this top-level session only — - Success: `worktree remove` (branch ref is preserved automatically). - Failure/Blocked: keep the worktree, name its path and branch in the report. -**Done** must cite: pushed commit SHA(s), the PR URL, the per-item fix table, and test-gate output location. +**Done** must cite: pushed commit SHA(s), the PR URL, the triage table (per-item verdict from Phase 2.5, including items triaged out), the per-item fix table, and test-gate output location. **Blocked** must cite: exact unblock condition (auth, fork perms, surviving test failures), worktree path, and everything already fixed. diff --git a/skills/ground-state/SKILL.md b/skills/ground-state/SKILL.md index 2b0c1a3..c72e50f 100644 --- a/skills/ground-state/SKILL.md +++ b/skills/ground-state/SKILL.md @@ -18,7 +18,7 @@ If the survey reveals a fix that's tempting to apply, **return it as a recommend ## Inline reconnaissance -Run the three surveys below **directly using your own tools**. Do NOT dispatch any sub-agents via the `agent` or `skill` tools — every lookup in this phase is a deterministic read that you execute yourself using `bash`, `glob`, `read_file`, `grep`, `list_directory`, and `memory_search`. Issue all three surveys in a single batched tool-use round where possible. +Run the four surveys below **directly using your own tools**. Do NOT dispatch any sub-agents via the `agent` or `skill` tools — every lookup in this phase is a deterministic read that you execute yourself using `bash`, `glob`, `read_file`, `grep`, `list_directory`, and `memory_search`. Issue all four surveys in a single batched tool-use round where possible. ### State survey *(bash)* @@ -62,21 +62,51 @@ Call the **`memory_search` tool** with keywords from the user's current request Return: relevant facts with 1-line summaries, **plus the stores actually consulted** — e.g. `memory_search: 3 queries, 0 hits; HOT.md: read; AFK.md: read` — so the orchestrator can tell "no relevant memory exists" from "the fork never looked." If `memory_search` is unavailable on this surface, say so explicitly. +### Spine survey *(read_file)* + +Read `SPINE.md` from the repo root (same directory as `AFK.md`). If the file does not exist or is empty, skip silently -- do not mention it in the snapshot. If it exists and has entries, extract its invariant (INV-*), rejected-pattern (REJ-*), and taste entries (TST-*) and include them in the snapshot so architectural constraints are visible before proposing changes. + +### Eval-pipeline recency survey *(bash — software domain only)* + +For software-domain sessions, check whether the eval-run pipeline is current. This guard catches silent regressions that accumulate when the pipeline goes unrun during high-velocity sprints. + +Run: +``` +tail -1 ~/.afk/agent-framework/improve/eval-runs/.index.jsonl 2>/dev/null +``` + +Parse the `timestamp` field from the output. Then compute the age in days relative to today's date. + +**Threshold**: Read `AFK_EVAL_STALENESS_DAYS` from the environment (default: 7). If the env var is `0`, skip this check silently. + +**Surface as a warning finding when any of these are true:** +- The index file does not exist → `⚠ Eval-pipeline has NEVER run. Run \`afk improve eval-run\` to establish a baseline.` +- The file exists but the timestamp is absent or unparseable → `⚠ Eval-pipeline recency guard: index unreadable.` +- Age in days ≥ threshold → `⚠ Eval-pipeline is STALE: last eval-run was N days ago (exceeds AFK_EVAL_STALENESS_DAYS=N threshold). Run \`afk improve eval-run\`.` + +Skip this check silently when: +- Domain is not `software`, OR +- Working directory has no `package.json` / `pyproject.toml` / `Cargo.toml` / `go.mod` at root level (heuristic that this is not a software project), OR +- `AFK_EVAL_STALENESS_DAYS=0`. + +Include the result (fresh or warning) in the Implementation risks line of the snapshot. + ## Synthesis -Assemble the survey results into a **6-line ground-truth snapshot**: +Assemble the survey results into a ground-truth snapshot (6 lines minimum, 7 when SPINE.md has entries): - Branch: ``, ``, upstream: `` - Recent work: last 3 commits or stash items - Infrastructure: CI present? package scripts? authoritative configs for this task - Memory hits: facts (1-line each) + which stores were consulted, or `none (consulted: …)` -- Implementation risks: e.g. "branch is `main`, don't edit directly"; "CI runs on push"; "memory says prior attempt used approach X" +- Spine constraints *(only when SPINE.md exists with entries)*: INV-*, REJ-*, TST-* entries. Omit this line entirely when SPINE.md is absent or empty. +- Implementation risks: e.g. "branch is `main`, don't edit directly"; "CI runs on push"; "memory says prior attempt used approach X"; include any eval-pipeline staleness warning here. - Epistemic confidence: `` — based on how much state could be verified. Flag if working directory is sparse, if domain is unfamiliar, or if key artifacts may be missing. Surface the snapshot and stop. The orchestrator then uses these verified facts — not assumptions — to decide the next step. This skill never edits files. ## Brief Anchor (auto-runs after synthesis) -After the 6-line snapshot is assembled, construct the **Brief Anchor** — a path-verified grounding preamble the orchestrator pastes verbatim into every subsequent sub-agent brief. +After the snapshot is assembled, construct the **Brief Anchor** — a path-verified grounding preamble the orchestrator pastes verbatim into every subsequent sub-agent brief. **Construction procedure:** diff --git a/skills/refactor/SKILL.md b/skills/refactor/SKILL.md index f899134..6249b0d 100644 --- a/skills/refactor/SKILL.md +++ b/skills/refactor/SKILL.md @@ -1,6 +1,7 @@ --- name: refactor description: "Orchestrates safe, large-scale structural changes across a codebase — symbol renames, API migrations, pattern standardizations, layer restructurings. Enumerates all affected sites, groups them into dependency layers via the DAG executor, applies changes in parallel per layer with worktree isolation, and verifies behavioral preservation at each layer boundary before proceeding. Exits immediately on first regression, surfacing the exact site, diff, and behavioral delta before any commit." +when-to-use: "Large-scope structural refactors across multiple files or layers." argument-hint: " [--scope ]" failure_modes: - bad decomposition diff --git a/skills/research/SKILL.md b/skills/research/SKILL.md index 91466f9..52d0d6b 100644 --- a/skills/research/SKILL.md +++ b/skills/research/SKILL.md @@ -9,6 +9,10 @@ context: load Dispatch two sub-agents in parallel using the Agent tool. Prefer `subagent_type: "research-agent"`; fall back to `subagent_type: "Explore"` with thoroughness "very thorough" if the research-agent is not available. One researches the web for external context relevant to the current task. The other inspects the local working directory for domain-relevant artifacts. Return a concise merged research brief highlighting relevant findings, conflicts, risks, and implications for the task. +**TUI label convention.** Set `id_prefix` on each Agent dispatch to a short, descriptive task label so the live progress view shows the agent's job rather than a generic identifier: +- Web agent: `id_prefix: "web research"` +- Local inspection agent: `id_prefix: "local inspection"` + **Web research agent** — always the same: search for external context, prior art, patterns, APIs, and comparable approaches relevant to the task. Domain-agnostic. **Local inspection agent** — adapt to the domain: diff --git a/skills/review/SKILL.md b/skills/review/SKILL.md index f32b436..f384c7a 100644 --- a/skills/review/SKILL.md +++ b/skills/review/SKILL.md @@ -7,15 +7,17 @@ context: load ## Read-only — hard constraint -This skill **analyzes and reports**; it never mutates the repository, the PR/MR, or anything external. After you emit the merge recommendation, **STOP**. +This skill **analyzes and reports**; it never mutates the repository, the PR/MR, or anything external. After you emit the merge recommendation, **STOP** -- unless the **Merge offer** conditions (below) are met. Never — not for a real bug, not for a blocking defect, not even when there is no human reviewer and "someone has to fix it": - edit, create, or delete files (no `write`/`edit`-style mutations); - `git add` / `commit` / `stash` / `reset`, `git checkout` to discard changes, or `git push`; - `gh pr comment` / `review` / `edit` / `merge` / `create`, or post or edit any PR/MR body, comment, or description; + +**Exception:** `gh pr merge` is permitted after a **MERGE** verdict on a PR review in-which the findings are docs/test-related only, but **only** after the user explicitly confirms via `ask_question` (see **Merge offer** below). - run any other write- or network-mutating shell command. -The only shell permitted is **read-only inspection**: `git diff` / `git show` / `gh pr diff`, `grep` / `rg`, and file reads — plus dispatching the review sub-agents. Resolving findings, fixing bugs, resolving merge conflicts, and "making the branch mergeable" are explicitly **out of scope**: a fixable defect is a finding to report (`file:line` + a one-line fix in the `suggestion` field), never a license to act. +The only shell permitted is **read-only inspection**: `git diff` / `git show` / `gh pr diff` / `gh repo view`, `grep` / `rg`, and file reads — plus dispatching the review sub-agents. Resolving findings, fixing bugs, resolving merge conflicts, and "making the branch mergeable" are explicitly **out of scope**: a fixable defect is a finding to report (`file:line` + a one-line fix in the `suggestion` field), never a license to act. ## Sub-agent contract /contract @@ -42,6 +44,20 @@ The only shell permitted is **read-only inspection**: `git diff` / `git show` / Never fabricate intent. When none is available the value is the literal `(none supplied)`; agents disclose its absence rather than guess. +**Capture prior reviewer feedback (inline, PR targets only).** When the review target is a PR URL or number, fetch existing reviewer feedback from all three GitHub comment stores before dispatching Wave 1: + +1. Inline review comments (anchored to diff lines): `gh api "repos/{owner}/{repo}/pulls//comments?per_page=100&sort=created&direction=desc"`. +2. Review summary bodies (top-level body per review submission): `gh pr view -R {owner}/{repo} --json reviews -q '.reviews[] | {author: .author.login, state: .state, body: .body, submittedAt: .submittedAt}'`. +3. Conversation comments (issue-level PR comments): `gh pr view -R {owner}/{repo} --json comments -q '.comments[] | {author: .author.login, body: .body, createdAt: .createdAt}'`. + +Extract `{owner}/{repo}` from the PR URL argument directly, or for bare-number inputs from `gh pr view --json url -q .url`. Pass `-R {owner}/{repo}` on every `gh pr view` call above so cross-repo PR URLs resolve in the correct repository. + +Normalize each source's timestamp field (`created_at` for inline, `submittedAt` for review summaries, `createdAt` for conversation) to a single `timestamp` ISO string during merge. Filter: drop bot/automation comments (author login contains `[bot]` or body is empty/whitespace). Merge all three stores into a single array, sort by timestamp descending, then cap to the **20 most recent** comments, truncated to a combined **4,096 tokens** to prevent context-window bloat on busy PRs. Identify afk's own prior review comments by the `` marker. + +Bundle surviving comments as a **`prior-reviewer-feedback`** block: `[{ source: "inline"|"review"|"conversation", author, body, timestamp, path?, line? }]`. When the PR has no prior comments, set `prior-reviewer-feedback: none`. + +For non-PR targets (`--staged`, `--head`, working-tree, patch-file, bare commit SHA, branch with no open PR), skip this step and set `prior-reviewer-feedback: not available — non-PR target`. + **Pre-fetch file contents at reviewed ref (inline).** After capturing `reviewed_ref` and the diff, and before dispatching any Wave 1 agent, the orchestrator (which has Bash) pre-reads every changed file at the reviewed ref and bundles the results for injection into sub-agent prompts. This is required because Wave 1 and Wave 1.5 agents are `research-agent` instances with no shell — they cannot run `git show` themselves. 1. Extract the list of changed files from the diff: `git diff --name-only ..` (or parse `--- a/` / `+++ b/` headers from a patch-file diff). @@ -58,10 +74,10 @@ When `reviewed_ref` is `unknown` (patch-file input), skip pre-fetch entirely and **The post-synthesis tail is the conditional half of that budget.** A review that surfaces a `critical`/`high` finding (or one whose `blocking` value departs from the default table — see **Post-synthesis** below) invokes `/shadow-verify`, which dispatches one verifier per claim in parallel, so the whole-run budget is **peak 2–3 concurrent, 4–6 total (1–3 verifiers)**, and it lands on exactly the high-stakes reviews most likely to hit a rate ceiling. Bound it: **at most 3 claims in a single round, no repeat rounds**, and hand the verifiers Wave 1.5's manifest so each re-derives the *claim* instead of re-locating evidence Wave 1.5 already pinned at the ref. Wave 1.5 verifies that a citation is real; shadow-verify re-derives whether the inference drawn from it holds — never substitute one for the other. **Wave 1 — Full review (regime=full, 2 parallel agents, `subagent_type: "research-agent"`).** Dispatch: -- **security · api-compat** — contracts, auth, injection, breaking changes, secret exposure. -- **correctness · spec-compliance · test-coverage · perf-observability** — logic bugs, regressions, whether the change satisfies its **stated intent** (unmet requirement or unrequested scope creep), missing tests, hot-path perf, logging gaps. +- **security · api-compat** — contracts, auth, injection, breaking changes, secret exposure. Set `id_prefix: "security & api-compat"` on this Agent dispatch. +- **correctness · spec-compliance · test-coverage · perf-observability** — logic bugs, regressions, whether the change satisfies its **stated intent** (unmet requirement or unrequested scope creep), missing tests, hot-path perf, logging gaps. Set `id_prefix: "correctness & coverage"` on this Agent dispatch. -Each agent receives: full diff + file tree + triage header + **reviewed ref (SHA)** + the **stated intent** (what the change is meant to accomplish, or `(none supplied)`), the severity rubric, **the `blocking` default table plus its overrides and assignment-order invariant**, and the finding schema. The blocking rules are not optional context: the finding schema mandates a `blocking` value per finding, so an agent that receives the schema without the table is being told to emit a field whose assignment rules it was never given. +Each agent receives: full diff + file tree + triage header + **reviewed ref (SHA)** + the **stated intent** (what the change is meant to accomplish, or `(none supplied)`), the **`prior-reviewer-feedback`** block (when available), the severity rubric, **the `blocking` default table plus its overrides and assignment-order invariant**, and the finding schema. The blocking rules are not optional context: the finding schema mandates a `blocking` value per finding, so an agent that receives the schema without the table is being told to emit a field whose assignment rules it was never given. **Citation requirement (enforced per agent).** Wave 1 agents cite from the diff and from the **`prefetched-files` block injected by the orchestrator**. They do **not** call `read_file` for ref-anchored verification (the working tree may be on a different branch) and do **not** run git. Centralized verification runs in Wave 1.5, which checks every `blocking`/`critical`/`high` citation **and every `file-state` citation at any severity** against the same pre-fetched content, then drops fabricated ones. Each agent must: 1. State the reviewed ref it was given in each finding: `ref: `. @@ -90,7 +106,7 @@ If the `Grep` tool is unavailable, tag the finding `[UNVERIFIED: reachability no This is the agent's first-line self-check; **Wave 1.5 Check B** independently re-verifies any surviving absence claims against the reviewed ref as a backstop. -**Wave 1 — Light review (regime=light, 1 agent, `subagent_type: "research-agent"`).** Single agent covers all dimensions (including spec-compliance). Same `stated-intent` input, rubric, schema, and citation requirement. Through synthesis, the light regime peaks at **1 concurrent sub-agent session** and dispatches **2 in total** (Wave 1 ×1, then Wave 2 ×1, sequential). Its conditional post-synthesis `/shadow-verify` tail dispatches 1–3 verifiers in parallel when qualifying findings surface, making the whole-run budget **peak 1–3 concurrent, 3–5 total (1–3 verifiers)**; the same bound of at most 3 claims in one round with no repeat rounds applies. +**Wave 1 — Light review (regime=light, 1 agent, `subagent_type: "research-agent"`).** Set `id_prefix: "light review"` on this Agent dispatch. Single agent covers all dimensions (including spec-compliance). Same `stated-intent` input, `prior-reviewer-feedback` block, rubric, schema, and citation requirement. Through synthesis, the light regime peaks at **1 concurrent sub-agent session** and dispatches **2 in total** (Wave 1 ×1, then Wave 2 ×1, sequential). Its conditional post-synthesis `/shadow-verify` tail dispatches 1–3 verifiers in parallel when qualifying findings surface, making the whole-run budget **peak 1–3 concurrent, 3–5 total (1–3 verifiers)**; the same bound of at most 3 claims in one round with no repeat rounds applies. **Wave 1.5 — Citation + absence-claim verification (INLINE — run by the orchestrator, dispatches nothing).** Run after Wave 1 returns, before Wave 2 synthesis. The orchestrator already holds exactly the read-only shell this verification needs (`git show` / `git diff` / `gh pr diff` / `grep` / `rg` — see the shell grant above), so running it inline costs **zero** additional sessions and zero nesting. A shell-less sub-agent here would have to nest a `git-investigator` to run the very commands the orchestrator can already run. Two independent checks: @@ -106,7 +122,15 @@ This is the agent's first-line self-check; **Wave 1.5 Check B** independently re Returns a combined verification manifest: `[{type: citation|absence, claim, status, finding_id, evidence?}]`. Findings classified `fabricated` (citation) or `false-absent` (absence) are excluded from Wave 2 input. `diff-only` citations are passed to Wave 2 with a `⚠ diff-only citation — line absent at the reviewed ref` annotation and auto-downgraded one severity tier. `grep-unavailable` absence claims are passed through with their `[UNVERIFIED]` tag intact. -**Wave 2 — Synthesis (1 agent, `subagent_type: "research-agent"`).** Receives: Wave 1 findings **after** citation-verification filtering + manifest of dropped/downgraded citations + **the merge-decision rule and its counts format below**. Wave 2 emits the verdict, so it needs that rule for exactly the reason Wave 1 needs the blocking table: an agent told to produce an output whose format and threshold it was never given will improvise both. Dedup by `(file, line_range, dimension)` — keep highest severity on exact match. Flag cross-agent conflicts as `CONFLICT` blocks (surface both rationales; do not auto-resolve). +**Wave 2 — Synthesis (1 agent, `subagent_type: "research-agent"`).** Set `id_prefix: "synthesis"` on this Agent dispatch. Receives: Wave 1 findings **after** citation-verification filtering + manifest of dropped/downgraded citations + **the `prior-reviewer-feedback` block** (when available) + **the merge-decision rule and its counts format below**. Wave 2 emits the verdict, so it needs that rule for exactly the reason Wave 1 needs the blocking table: an agent told to produce an output whose format and threshold it was never given will improvise both. Dedup by `(file, line_range, dimension)` — keep highest severity on exact match. Flag cross-agent conflicts as `CONFLICT` blocks (surface both rationales; do not auto-resolve). + +**Prior-feedback dedup (cross-run).** After intra-run dedup, compare each surviving finding against the `prior-reviewer-feedback` block. Decision rubric: +- The **diff contains evidence** that the concern was addressed (removed line, added guard, new test) → downgrade finding to `low` severity with `blocking: false` and annotation `[addressed since prior review]`. This is an explicit override of the assignment-order invariant: the pre-downgrade `blocking` value does **not** carry through, because the concern is resolved and surfacing it as blocking defeats the purpose of recognizing the fix. Do not suppress entirely — the reviewer sees the resolution. +- A prior comment (from afk or a human reviewer) **raised the same concern** and the author **acknowledged and deferred** it (e.g. "I'll fix this in a follow-up") → tag finding `[previously raised — author deferred]` and preserve its original severity. Do not re-raise the argument; note the deferral. +- A prior comment **raised the same concern** but the issue **persists unchanged in the current diff** → preserve the finding at its earned severity with annotation `[persists from prior review]`. Never suppress a real issue because a prior comment exists. +- No match in prior feedback → emit the finding unchanged. + +This rubric is concrete, not discretionary. "Same concern" means the finding and prior comment reference the same file, overlapping line range, and the same class of defect. Semantic similarity alone ("error handling" vs. "missing error handler") is not a match unless file and line range also overlap. **Severity sort order within the blocking list:** findings tagged with semantics matching `invariant violation`, `defeats stated purpose`, `defeats refactor goal`, or `breaks stated contract` sort above all other `high` findings, even those with higher mechanical severity (e.g. test/build hygiene). Within that group, sort by tier (critical → high). Mechanical findings (missing test, build hygiene) sort last within their tier. @@ -132,7 +156,7 @@ Severity and disposition are **separate axes**. `severity` answers "how bad is t - A `medium` representing a material data-integrity risk or a likely production failure under normal usage is **never** overridable to `false` — a race that intermittently loses user state stays blocking even when its blast radius keeps it out of `high`. - A `low` or `nit` may be marked `blocking: true` only for a stated external constraint (release gate, compliance requirement). Do not use this to smuggle a preference. -**Invariant — assignment order.** `blocking` is assigned from the **pre-downgrade** severity. A finding later downgraded by **any** downgrade rule in this file — the api-compat reachability rule (which drops straight to `nit`, two tiers in one step), its grep-unavailable fallback, Wave 1's absence-grounding fallback, Wave 1.5's `diff-only` citation rule, Wave 1.5's `grep-unavailable` absence rule, or the confidence rule below — **keeps the `blocking` value its pre-downgrade severity earned**: a downgrade lowers severity, never disposition. Only an explicit, justified override from the list above may flip `blocking`. Without this ordering, a security or data-integrity `medium` would silently become non-blocking by being downgraded rather than waived, defeating the two never-overridable rules above through a path that requires no justification at all. +**Invariant — assignment order.** `blocking` is assigned from the **pre-downgrade** severity. A finding later downgraded by **any** downgrade rule in this file — the api-compat reachability rule (which drops straight to `nit`, two tiers in one step), its grep-unavailable fallback, Wave 1's absence-grounding fallback, Wave 1.5's `diff-only` citation rule, Wave 1.5's `grep-unavailable` absence rule, the confidence rule below, or the prior-feedback addressed-since-prior-review rule (Wave 2) — **keeps the `blocking` value its pre-downgrade severity earned**: a downgrade lowers severity, never disposition. Only an explicit, justified override from the list above may flip `blocking`. Without this ordering, a security or data-integrity `medium` would silently become non-blocking by being downgraded rather than waived, defeating the two never-overridable rules above through a path that requires no justification at all. A `blocking: true` that survives a downgrade this way is **not** an override and needs no justification clause: it carries `· blocking preserved from pre-downgrade ` instead, and the `low`/`nit` external-constraint rule above does not apply to it. Without this exemption the invariant and that rule contradict each other — every downgraded `medium` would land as a `low`/`nit` carrying `blocking: true` with no admissible reason to write, forcing the reviewer to either fabricate an external constraint or emit a schema-violating finding. @@ -140,7 +164,9 @@ Emit **DO NOT MERGE** when one or more findings carry `blocking: true` after Wav State the counts that drove the decision on the same line, **with a dimension breakdown for any blocking medium**, e.g. `Decision: DO NOT MERGE — 1 high, 2 medium blocking (1 security, 1 correctness); 1 medium waived, 3 low.` or `Decision: MERGE — 0 blocking (2 medium waived, 3 low, 1 nit).` If zero findings survived, say `Decision: MERGE — 0 findings.` Never emit a bare verdict with no counts, and never waive a finding silently — a waived medium must appear in the count with its justification. -This is the terminal step — after emitting the decision, STOP. Do not act on any finding: no edits, commits, pushes, or PR/MR mutations. A blocking bug is a finding to report, not a fix to apply. +This is the terminal step. A blocking bug is a finding to report, not a fix to apply -- no edits, commits, pushes, or PR/MR mutations. + +**Merge offer (docs/test-only PR reviews).** When **all four** hold: (1) the decision is **MERGE**, (2) the review target was a **PR** (URL or number), (3) every surviving finding is in the `test-coverage` dimension or is a documentation-only concern (comments, doc strings, README) -- no `security`, `correctness`, `api-compat`, `perf-observability`, or `spec-compliance` findings survived, and (4) every changed file in the PR is a test or documentation file (test files: `*.test.*`, `*.spec.*`, files under `__tests__/`, `/test/`, `/tests/`; doc files: `*.md`, `*.mdx`, doc-only config such as `typedoc.json`) -- no production source files changed -- ask the user: "Would you like me to merge this PR?" via `ask_question` with `type: "confirm"`. On accept (`value: true`), determine the target repo's preferred merge strategy via `gh repo view / --json viewerDefaultMergeMethod` (derive `/` from the PR reference, not from the local checkout) and pass the corresponding flag (`--squash`, `--rebase`, or `--merge`) -- a strategy flag is mandatory because `gh pr merge` errors in non-interactive contexts without one. Run `gh pr merge -- --match-head-commit ` where `` is the SHA captured at triage time, so the merge is pinned to the exact commit that was reviewed. If `gh pr merge` exits non-zero, surface the error output to the user and stop. On decline, cancel, skip, non-PR target, or any condition not met: stop. **Severity rubric (impact axis only — severity measures blast radius and reachability, never category):** - `critical` — data loss, auth bypass, secret exposure, RCE. If it cannot cause unauthorized access or data loss, it is NOT critical. @@ -165,6 +191,7 @@ Confidence `low` → auto-downgrade one tier + append `[low confidence — verif - If any citations could not be verified against a live ref (patch-file input): `Citation verification skipped — no live ref available; diff-context citations only.` - Any topical gaps (e.g. 'did not review Telegram surface', 'did not run tests'). - Whether a **stated intent** was available and spec-compliance was assessed. Example: `Stated intent: PR #123 title+body — spec-compliance assessed.` or `Stated intent: (none supplied) — spec-compliance not assessed.` +- Whether prior-reviewer-feedback was captured (PR targets: list comment count and cap hit if truncated; non-PR targets: 'not available — non-PR target'). **Post-synthesis:** if any `critical` or `high` finding is present, **or any finding whose `blocking` value departs from the default table** (a waived `medium`, an escalated `low`/`nit`), invoke `/shadow-verify` on those findings before surfacing to the user. Shadow-verify independently re-derives each claim against source; fabricated or unsupportable findings drop here before they reach the merge decision. An overridden finding is routed because the agent that found it also set its disposition and wrote its own justification — the waiver is otherwise the only judgement in this pipeline with no second reader. `medium` and below **at their default disposition** go straight through. diff --git a/skills/shadow-verify/SKILL.md b/skills/shadow-verify/SKILL.md index c38c878..a3efc89 100644 --- a/skills/shadow-verify/SKILL.md +++ b/skills/shadow-verify/SKILL.md @@ -30,6 +30,7 @@ Select 2–3 of the highest-stakes normalized claims to send to the verifier wav 2. Dispatch one shadow sub-agent per claim, in parallel. Each receives the normalized claim text + the user's original goal + **the search surface** — the inventory of files, directories, or URLs the original investigation touched. It must NOT receive the original agent's reasoning, verdict, confidence language, or the specific line/region it concluded from. **Withhold the conclusion, not the map.** Withholding the map too does not buy extra independence — the verifier still has to reach the same evidence, it just spends its budget guessing paths to get there. Measured: one verifier denied the inventory spent 70 `grep` + 18 `read_file` calls re-locating files the parent already had paths for, guessed 5 nonexistent paths on the way, and hit its tool-loop ceiling before finishing. The independence that matters is epistemic (re-deriving the verdict), not navigational. - The inventory is a **starting surface, not a boundary**: it does not satisfy the composition-axis guard below, and a verifier that reads only inside it still returns `evidence_base: artifact-internal`. At least one primary source outside that surface is still required for `independent-rederivation`. - **Default to `subagent_type: "research-agent"` (mechanically locked to Read/Grep/Glob/WebFetch/WebSearch — cannot Edit/commit/push).** If the claim requires Bash to verify (running a failing test, `gh pr view`, `git log origin/...`), fall back to a Bash-capable subagent type with `isolation: "worktree"` and prepend this prefix to the prompt: *"Verifier sub-agent — do not Edit, Write, commit, push, `gh pr create`, or `curl`. Return findings only."* + - **Set `id_prefix`** to a short label identifying the claim being verified, e.g. `"verify: unused export"` or `"verify: missing null check"`, so the live TUI shows which claim each verifier is checking. - **Every verifier dispatch carries an explicit budget** — `max_tool_use_iterations` (a wave of 2–3 claim checks needs ~15–25 rounds each, not 50) plus the cheapest sufficient model. An unbudgeted verifier does not fail loudly: it exhausts the default tool-round ceiling, terminates `stopReason: "tool_use_loop_capped"`, and emits its verdict from a tools-stripped wind-down round built on partial evidence. A `CONFIRMED` produced that way is indistinguishable from a real one and silently defeats the entire point of the wave. Check each returned verifier's stop reason before merging its verdict; treat a capped or wind-down verifier as `UNVERIFIABLE`, not as a verdict. 3. Each verifier re-derives the verdict independently using tool calls only — never re-reading the original report's reasoning. Returns `{claim_id, verifier_verdict, evidence_pointer, evidence_base}`, where `verifier_verdict` is one of `CONFIRMED`, `REFUTED`, `STALE`, or `UNVERIFIABLE`, and `evidence_base` is `independent-rederivation` (read primary sources *outside* the cited artifact's boundary) or `artifact-internal` (re-read only the cited file/region). On `REFUTED` or `STALE`, the verifier also emits a corrected or updated finding. diff --git a/skills/ship/SKILL.md b/skills/ship/SKILL.md index 3443a01..e3dd135 100644 --- a/skills/ship/SKILL.md +++ b/skills/ship/SKILL.md @@ -1,6 +1,7 @@ --- name: ship description: "Release pipeline for already-done local work. Dispatches /ground-state pre-flight, runs the project test suite, drafts a commit message, pushes, and opens a PR with a structured verification summary. Use when local changes are ready to hand off to review — e.g. 'ship this', 'push and open a PR', 'release this work'. Add --verify to trigger an adversarial verifier wave on the diff before a human reads the PR." +when-to-use: "Local changes are ready to push and open a PR." context: fork --- @@ -123,6 +124,27 @@ gh pr create \ PR bodies are markdown: they routinely carry backticks (inline code), `$(...)`, and quotes. **Never** inline the body as `--body "$(cat <<'EOF' … EOF)"` — the shell parses backticks/`$(` inside the command substitution before `gh` ever runs, so the call fails (or worse, opens the PR with a truncated/garbled body and you don't notice). `--body-file` reads the file verbatim: no shell quoting, no escaping. Only `--title` stays inline — keep it a single plain line with no backticks. +**Phase 8b — Fix-of-fix detection (runs immediately after the PR URL is returned by Phase 8).** +Scan the PR title and body for cross-references to other PRs. If any referenced PR was merged within the last 7 days, apply the `fix-of-fix` label. + +``` +# 1. Extract referenced PR numbers from the PR body using the patterns: +# #NNNN, fix(#NNNN), regression from #N, follow-up to #N, addresses #N, +# fixes/closes/resolves #N +# +# 2. For each referenced PR number (excluding the current PR): +gh pr view --json mergedAt --jq .mergedAt +# If mergedAt is non-null and within 7 days → it qualifies. +# +# 3. If any qualify: +gh label create fix-of-fix \ + --description "PR that fixes a bug introduced by a recently-merged PR (merged within 7 days)" \ + --color D93F0B --force +gh pr edit --add-label fix-of-fix +``` + +**Skip gracefully** when `gh` is unavailable or any step fails — the PR is already open and that is the deliverable. Never abort Phase 8b failure back to the user as a blocking error; log a one-line note instead (e.g. `fix-of-fix detection skipped: `). + **Phase 9 — Reclaim the worktree.** Skip entirely unless the work you just shipped lives under `.afk-worktrees/` (check the invocation cwd from Phase 1 — if the path has no `.afk-worktrees/` segment, there is nothing to reclaim; go straight to the PR URL). diff --git a/skills/simplify/SKILL.md b/skills/simplify/SKILL.md index c41a304..b41c960 100644 --- a/skills/simplify/SKILL.md +++ b/skills/simplify/SKILL.md @@ -1,6 +1,7 @@ --- name: simplify description: "Discovers incidental complexity, duplication, and dead code in a codebase and produces a ranked, behavior-preserving reduction plan — optionally applying safe changes. Dispatches four parallel read-only discovery lenses (clone detection, dead code, complexity hotspots, wrong abstraction), synthesizes into a prioritized reduction plan, and gates apply mode behind /refactor and a hard test check." +when-to-use: "Reduce complexity, find duplicates, or remove dead code." argument-hint: "[target] [--apply] [--all]" context: load --- From 09d703e77dc174f6e61a11befe6f46702a013c39 Mon Sep 17 00:00:00 2001 From: Griffin Long Date: Wed, 30 Sep 2026 15:18:29 -0400 Subject: [PATCH 2/2] revert(automate): keep the plugin's own automate skill --- skills/automate/SKILL.md | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/skills/automate/SKILL.md b/skills/automate/SKILL.md index 3adc18a..d5f2726 100644 --- a/skills/automate/SKILL.md +++ b/skills/automate/SKILL.md @@ -1,19 +1,18 @@ --- name: automate -description: "Set up a scheduled headless afk run that pushes a summary to Telegram. Use when the user wants to automate a recurring task via the afk daemon scheduler (cron) with push-notified results." +description: "Set up a scheduled headless Claude run that sends summaries to Telegram. Use when the user wants to automate a recurring task via launchd (macOS) with push-notified results." disable-model-invocation: true -context: load --- -Set up a recurring headless task using afk's **native** scheduler. Do NOT hand-roll launchd plists or shell scripts — afk has first-class scheduling: the `create_schedule` tool writes `~/.afk/config/schedules.json` entries that `afk daemon` runs on cron. - Dispatch two sub-agents in parallel: -1. **Scout** — call `list_schedules` and inspect `~/.afk/config/schedules.json` for existing or overlapping jobs, run `afk service status` to see whether the daemon is installed as a launchd service and running, confirm Telegram is configured (`TELEGRAM_BOT_TOKEN` + `AFK_TELEGRAM_ALLOWED_CHAT_IDS`), and scan the target project folder for conventions. Report conflicts, daemon/service state, and any missing prerequisite. -2. **Prompt designer** — draft the recurring task's `command` string: a self-contained prompt (or `/skill --flags` invocation) sent verbatim into a freshly spawned session each run. The daemon session starts cold, so encode all input context explicitly, and require the run to END by calling the `send_telegram` tool with a concise, push-ready summary. +1. **Scout** — inspect `~/.claude/automations/` (scripts, plists, existing jobs) and the target project folder to surface conventions, naming, and overlapping jobs. Report any conflicts. +2. **Prompt designer** — draft the headless Claude prompt for the recurring task. Encode the input context explicitly and require a concise Telegram-ready summary as output. + +When both return, generate: +- Shell script at `~/.claude/automations/scripts/.sh` that `cd`s into the target folder and runs: + `claude --dangerously-skip-permissions --channels plugin:telegram@claude-plugins-official ""` +- launchd plist at `~/Library/LaunchAgents/com.claude.automate..plist` with the requested `StartCalendarInterval` or `StartInterval`, plus `StandardOutPath`/`StandardErrorPath` pointing at `~/.claude/automations/logs/.log`. -When both return: -- If Telegram is unconfigured, stop and tell the user to run `/telegram-setup` first — `send_telegram` fails closed without `TELEGRAM_BOT_TOKEN` and `AFK_TELEGRAM_ALLOWED_CHAT_IDS`. -- Create the job with the `create_schedule` tool: `name`, the designed `command`, the requested 5-field `cron`, `trigger: "cron"`, and `notifyOn: "failure"` as a crash safety-net (the per-run summary comes from the agent's own `send_telegram` call, not from `notifyOn`). -- Schedules only fire while the daemon is running, so make it survive reboot/crash: if `afk service status` shows the daemon isn't installed, run `afk service install daemon` (launchd `RunAtLoad` + `KeepAlive` on macOS). +Load it: `launchctl bootstrap gui/$(id -u) ` and confirm with `launchctl list | grep `. -Dispatch a **verification** sub-agent to confirm the job registered (`list_schedules`), the daemon is running (`afk service status`), and — by running the task's `command` once as a one-shot (`afk chat ""`) — that the Telegram summary actually arrives. On failure, diagnose (Telegram config, daemon not running, cron syntax, prompt shape) and fix before exiting. Report the schedule id, cron, `notifyOn`, daemon/service status, and the next scheduled run. +Dispatch a **verification** sub-agent to run the script once manually, confirm the Telegram summary arrives, and tail the log. On failure, diagnose (permissions, path, channel config, prompt shape) and fix before exiting. Report final status, script path, plist path, and next scheduled run time.