AGENT SKILL

supervise

Launches `node goal-run.ts` with a plan path in the background and watches it to the end. On a non-zero exit it classifies before repairing — plan at fault, implementation at fault, or unknown — never the reverse. A plan fault is repaired within a closed set (declared paths, max_diff, a mistyped path, prose) with plan-guard.ts proving no gate or dod line moved, then relaunched; an implementation fault discards the tree and relaunches unchanged; anything else stops and wakes the developer. One relaunch per iteration, then stop. Unproven — two prior halts are the whole evidence this classifier has ever seen.

适用职业开发工程师
License 许可
MIT
最近更新
2026-10-13
Stars
★ 9
Forks
2

01 前言 Front Matter

name
supervise
description
Launches `node goal-run.ts` with a plan path in the background and watches it to the end. On a non-zero exit it classifies before repairing — plan at fault, implementation at fault, or unknown — never the reverse. A plan fault is repaired within a closed set (declared paths, max_diff, a mistyped path, prose) with plan-guard.ts proving no gate or dod line moved, then relaunched; an implementation fault discards the tree and relaunches unchanged; anything else stops and wakes the developer. One relaunch per iteration, then stop. Unproven — two prior halts are the whole evidence this classifier has ever seen.
disable-model-invocation
true

02 资源文件 1

03 SKILL.md 正文

Outside Claude Code

This skill needs a background shell and an agent tool. Claude Code: background Bash, Agent. Copilot CLI: async bash read with read_bash, task with goal:goal-session-auditor. Codex CLI: exec_command read with write_stdin, no agent tool. A host with neither a shell nor an agent tool cannot launch or audit a run: state that reason and STOP before Phase 0 runs.

The host never sets or unsets GOAL_RUN_PROVIDER or GOAL_RUN_IMPLEMENTER: goal-run.ts is launched with the developer's provider settings untouched.

Host

<root> is Claude Code's substituted ${CLAUDE_PLUGIN_ROOT}. A host that leaves it literal (Copilot CLI) reads <root> as two levels above Base directory for this skill:. A host that injects no base-directory line and reads SKILL.md itself (Codex CLI) takes <root> as two levels above that file's directory. $ARGUMENTS left literal is the text typed after the command (empty when none). Use <root> wherever a command below says ${CLAUDE_PLUGIN_ROOT}.

When <root>/src/artifacts.ts or a shared file this skill names is missing, print cannot locate the goal plugin scripts, the host, and the install commands, run nothing and write nothing: copilot plugin marketplace add <repository> then copilot plugin install goal@fabien-claude-marketplace under Copilot, codex plugin marketplace add <repository> then codex plugin add goal@fabien-claude-marketplace under Codex, /plugin install goal@fabien-claude-marketplace under Claude Code.

Then read <root>/shared/host.md.

/goal:supervise — Watch a run, classify a halt, repair or relaunch once

You launch node goal-run.ts and read what it returns. goal-run.ts orders the iterations and goal-gate.ts decides whether each one passed; nothing here reopens either verdict. Your one job on a non-zero exit is to say which of two very different things happened — the plan's contract was wrong, or the implementation was — because the two halts this command was written after (docs/open-questions.md §8) exited with the same code and the same wall message and needed opposite responses. Getting that call wrong is worse than not calling it: it either burns the implementer's real work for nothing, or ships a plan quietly rewritten to stop refusing it.

This is a hypothesis, not a proven procedure. The classification rule below was written from two cases. Read the report at the end as evidence for or against it, not as a verdict on this run.

Under Codex

Claude stays the implementer: goal-run.ts runs claude as under any host, and the host never sets or unsets GOAL_RUN_PROVIDER or GOAL_RUN_IMPLEMENTER. The background shell is exec_command, read with write_stdin. There is no agent tool.

Before Phase 1 launches anything, run a network probe and a write probe into $HOME/.claude. Each network probe must print a code other than 000:

curl -s -o /dev/null -w '%{http_code}' --max-time 10 https://api.github.com
curl -s -o /dev/null -w '%{http_code}' --max-time 10 "${ANTHROPIC_BASE_URL:-https://api.anthropic.com}"
f="$HOME/.claude/.goal-write-probe-$$" && touch "$f" && rm -f "$f"

When one fails, name the failing probe, print codex -s workspace-write -c sandbox_workspace_write.network_access=true --add-dir "$HOME/.claude" and launch nothing.

Phase 7 runs claude -p --agent goal:goal-session-auditor from the shell, passing the plan's path and $(pwd). A failed audit is reported with its error instead of an audit, and folds no ### Session audit.

The supervising row is read from the rollout: sum token_usage_record.payload.usage once per response_id over the run's window (input = input_tokens minus cached_input_tokens, cache read = cached_input_tokens, cache write = cache_write_input_tokens, output = output_tokens), take model_context_window from the event_msg token_count as the window, and show cost —. Total names which rows its sums cover.

Project artifact root

Before resolving the source, run the existing resolver from the installed goal plugin:

project="$(git rev-parse --show-toplevel)"
node "${CLAUDE_PLUGIN_ROOT}/src/artifacts.ts" "$project"

Then read <root>/shared/artifact-root.md.

Use the effective value without asking for a location. If no value is supplied, use the returned default; location selection belongs to /goal:spec.

Phase 0 — Resolve the plan

Argument: $ARGUMENTS

  • A full path ending in .md → that is the plan; fail clearly if missing or unreadable.
  • An identifier → resolve it as <root>/shared/plan-argument.md words it.
  • Empty → the <plans>/*-spec.md most recently modified, excluding *-cleanup-spec.md. Several equally plausible candidates → list them and ASK. None → ask for the full path and wait.

If the developer confirms there is no existing plan to select, STOP: "No plan exists. Run /goal:plan first."

Phase 1 — Launch in the background

node ${CLAUDE_PLUGIN_ROOT}/scripts/goal-run.ts <plan>

Start it as a background shell. It writes nothing you need to poll for: <runs>/<work-id>/<run-id>/.run.log accumulates the same lines it prints, so the transcript survives you looking away.

Phase 2 — Wait for it to end

Watch the background shell until it exits, without polling in a tight loop. Read its exit code:

Exit Meaning
0 every attempted iteration landed, gate-verified
1 halted — the gate refused one iteration
2 refused — the run never started (a faulty or unknown GOAL_RUN_*, GOAL_CMD_* or GOAL_PROC_* setting, listed all at once, including the retired GOAL_RUN_SHUTDOWN_MAX_RETRIES)
3 paused — a clean boundary (the attempt ceiling, listing each attempt with its class; an implementer that committed on its own; an implementer that wrote nothing; a publication refused under commit+pr: push, pull request, scanner, fixup commit, or a closed or merged pull request, with the iterations that are local only)

The failure report — evidence before diagnosis

Every non-zero exit is reported in one structured block, in this order:

  1. Command — the exact command the run executed, verbatim from the log (make php/qa, the gateN= line the gate refused…).
  2. Output — the failing part of that command's output, verbatim from <runs>/<work-id>/<run-id>/.run.log: the error lines plus enough surrounding context to read them (the failing suites, the assertion diff, the lint error). Trim the green noise, never the failure.
  3. Reproduce — the command the developer can run themselves to see the same thing, what it prints when healthy, and what it printed here.
  4. Diagnosis — what you conclude from 1–3, with your confidence.
  5. Next move — the repair, the relaunch, or the question for the developer.

The same evidence duty covers silent mutations: after any stage that runs project commands (preflight, DoD), check git status --short — a QA command that fixes instead of failing (a mutating cs-fixer, a formatter) leaves a dirty tree nobody reported. Show that diff verbatim and say which command produced it; never fold it into a commit without showing it.

Never deliver 4–5 without 1–3: a solution whose problem the developer never saw is advice, not a report — the failure this section was added after was three refusals reported as "jest OOM, fix Docker" without one line of jest output shown. And the whole block goes in the final message of the turn: text emitted between tool calls is not reliably displayed, so a report printed mid-turn may never reach the developer at all.

Phase 3 — 0 or 3: report, nothing to classify

Neither exit names a fault. 0 → report what landed. 3 → report the boundary the log names and that relaunching resumes at the first unchecked box; do not relaunch it yourself, that is the developer's call, not a repair.

Phase 4 — 2: nothing was attempted

A refusal exit means the preflight stopped the run before any iteration was handed to an implementer — there is no work to classify and nothing to discard. Build the failure report: the STOP line names the command (Command), the log holds its output (Output — for a not-green base, the failing tests/lint lines themselves, not just the summary line). Wake the developer with it: the cause (a lock, a dirty tree, a stale branch, a red base) is almost always theirs to fix, and relaunching blind repeats the same refusal.

Phase 5 — 1: classify before repairing

Read the log tail back to the gate's own HALT block — REASON: and DETAIL:, written by plugins/goal/src/gate/halt.ts. That text is the only evidence there is; do not re-run the gate to get a second opinion; it will say the same thing.

Sort what it names into exactly one bucket:

  • Plan fault — the DETAIL: names a defect in the iteration's own contract: a declared path that does not exist or is misspelled, a max_diff too tight for work the iteration's prose plainly asked for, an impl_files/test_files entry missing a file the goal names, prose that reads one way and was enforced another. The fix stays inside a closed set: an entry in test_files or impl_files, max_diff, a mistyped path, or prose. It may never touch a gateN= or dodN= line, nor empty test_files — plan-guard.ts hashes that field's emptiness, not its content, so it disarms the bite check the same way a rewritten gate line would: not a smaller iteration, a different one, judged by a bar nobody locked.
  • Implementation fault — the same gate line is correct and the code it judged is not: a wrong file touched, a rule half-implemented, a helper the plan never needed guessed into existence. The fix is to try again, not to edit the contract.
  • Unknown — anything that is not confidently one of the above: the DETAIL: names something outside the closed repair set, points at more than one iteration, or you cannot tell which side of the line it falls on. Guessing here is the failure mode this command exists to avoid.

Plan fault → repair, prove it, relaunch

  1. Hash the plan before touching it: node ${CLAUDE_PLUGIN_ROOT}/src/plan-guard.ts <plan> → keep guard_hash.
  2. Make the smallest edit inside the closed set above. Nothing else in the plan moves — no rewording a goal, no reordering iterations, no touching a checkbox.
  3. Prove it: node ${CLAUDE_PLUGIN_ROOT}/src/plan-guard.ts <plan> <guard_hash> must print OK: no gate or dod line moved. and exit 0. If it halts instead, the edit reached a guarded line — revert it (git checkout -- <plan>) and fall through to unknown.
  4. Have the gate re-judge the preserved tree directly: node ${CLAUDE_PLUGIN_ROOT}/scripts/goal-gate.ts commit <plan> <iteration> alone. commit runs the whole verify first and commits only when it passes, so a separate verify before it would replay every gate command twice. Then, on exit 0, goal-gate.ts dod <plan> if it was the last iteration. Do NOT relaunch via goal-run.ts here: its preflight requires a clean tree and refuses (exit 2) the very implementer tree this procedure just decided to keep — observed on ct-5865 iteration 3. The gate subcommands are the run's own judge/commit path, bite check included, so nothing lands unbitten.

Implementation fault → discard the tree, relaunch unchanged

  1. Discard exactly what the halted attempt wrote, and nothing that predates it — the preflight already required a clean tree, so everything git status --short shows now belongs to this attempt: git checkout -- . for tracked changes, git clean -fd for untracked ones. Never -x: that would also sweep ignored planning documents the developer chose to keep local.
  2. Confirm the tree is clean (git status --short prints nothing) before relaunching.
  3. Relaunch the same iteration, plan untouched: node ${CLAUDE_PLUGIN_ROOT}/scripts/goal-run.ts <plan> <iteration>.

Unknown → stop, wake the developer

Build the failure report — the HALT block verbatim is its Command/Output — say plainly that you could not classify it, and name why — which detail put it outside the closed set. Do not guess at a repair and do not discard anything. This is the same halt a human reading the log would have to solve; it stays theirs.

Phase 6 — One relaunch, then stop

Whichever repair ran, watch the relaunch exactly as in Phase 2, then report Phase 3/4/5 against its exit code — but do not repair a second time. A second halt on the same iteration, of either kind, means the classification was wrong, the repair was incomplete, or the halt has a cause this command does not model: build the failure report with everything (both HALT blocks, the repair made in between, git status --short) and stop for the developer. Looping again is exactly the failure the design guards against — an agent that keeps relaunching optimizes for the run continuing, not for the work being right.

Phase 7 — Audit the session, unless nothing was attempted

Skip this phase on exit 2: the preflight refused before any iteration reached an implementer, so there is no implementer transcript and nothing was driven — auditing how work was done when none was is waste by construction. Print the Phase 4 report and stop.

Otherwise — landed, paused, halted, or a relaunch that itself landed or stopped — invoke the goal:goal-session-auditor agent exactly once before you finish, passing it the plan's path and $(pwd). It reads the transcripts a run left behind, including this supervising session's own, and reports findings anchored to a tool-call sequence: an edit reverted, the same command failing twice, a file read five times, a brief that named a convention skill with no Skill call to match. Print what it returns; nothing it finds changes the exit you already reported.

Once the audit returns, fold its highlights into the PR's run report as a ### Session audit section — short bullets, not paragraphs — but only when the plan's policy is commit+pr and a pull request is open: `gh pr edit <branch> --repo <repo> --body "$(gh pr view <branch> --repo <repo> --json body -q .body)

Session audit

<highlights>". Outside commit+pr`, or with no PR open yet, there is nowhere to fold it: the highlights stay in this session's own final message instead.

Write the fold in English, whatever language this session is running in. It lands in a pull request body, and a pull request is English — the git skill's rule, which is not loaded here, so it is restated at the one point in this command that writes to the forge. The trap is that the highlights are already composed, in the reply being written to the developer: pasting that reply into gh pr edit is a new artifact for a different reader, not a copy. A French audit fold under an English run report is what this paragraph exists to prevent, and it happened before it did.

The same fold completes the PR's attribution table: the auditor wrote it before its own tokens existed, so append the Auditor row (off its RUN tokens line) and the Supervising session row, and rewrite the Total row so it covers every row. A PR whose total leaves out two of the sessions that produced it is not a total.

Duration table

Every final report — Phase 3, 4, or the end of Phase 5/6 — that attempted at least one iteration prints the same two tables the auditor writes into report.md, built from the run's own .run.jsonl (one JSON line per RUN stage=<name> duration_ms=<n> exit=<n> event goal-run.ts already emits): extend those tables with this session's own rows, never reshape them into a differently-shaped one of your own. Skip both only on exit 2: Phase 4 already covers that case, and there is no per-iteration row to print when nothing was attempted.

Durations, one row per iteration, in order. Each step's own duration goes under the stage that ran it, push collapsing push and pull-request-update into one publication figure on the row of the iteration that triggered it:

Step Implementer Gate Push
1 <duration> <duration> —
… … … …
Total <sum> <sum> <sum>

preflight (with the base-must-already-be-green sweep) and dod run once, outside any iteration: their durations go in one note line under the table (Preflight: <d> · Dod: <d>), never in all-dash rows. Show every duration in minutes once it passes 60 s (1m 12s, not 72000); below that, milliseconds are fine. Print an exit code only on the rows whose stage failed; a row that landed carries no exit code at all. Total sums the displayed rows, not a re-scan of every duration_ms in the file — a step this table does not show never enters the total.

Attribution, one row per session stage — implementer per iteration, lens, reviewer, auditor — read off that stage's own RUN tokens stage=<name> input_tokens=<n> output_tokens=<n> cache_creation_input_tokens=<n> cache_read_input_tokens=<n> line, right after its stage= line:

Stage Duration Model Context peak Total tokens % cache read Cost (list)
Iteration 1 — Implementer <duration> <model> <pct> <n> <pct> <$>
… … … … … … …
Runner subtotal <sum> — — <sum> — <sum>
Lens <duration> <model> <pct> <n> <pct> <$>
Reviewer <duration> <model> <pct> <n> <pct> <$>
Auditor <duration> <model> <pct> <n> <pct> <$>
Supervising session <duration> <model> <pct> <n> <pct> <$>
Total <sum> — — <sum> — <sum>

Duration is the stage's own duration_ms — this table is the only place the lens, reviewer and auditor durations appear; the supervising session's is the span from this run's goal-run.ts launch to the fold, never the whole transcript — a session that supervised an earlier run or did other work would otherwise report hours for minutes. Total tokens is the four token classes summed for the row. Context peak is the percentage only, never the token count, and never the effective window itself, which is named once in a note under the table (Effective window: <model> <n> tokens) together with the run's own compaction count, 0 stated rather than left blank, rather than repeated cell by cell — a model absent from that note shows its tokens instead of a percentage. % cache read is the row's cache_read_input_tokens divided by its four-class total. Cost (list) is priced at the model's list rate, computed by you, the writer, from the four token classes — never read off the transcript. The rates themselves never appear under the table — the column already carries them applied, and the window-and-compactions line is the attribution table's only note. The Runner subtotal row sums the implementer rows above it — what driving the plan itself cost — and is not summed again into Total, which sums every other displayed row.

A Copilot row (a role or the supervising session that ran under Copilot) shows its duration, its model and what the Copilot session records. Its Cost cell holds the premium-request count or —, never a Claude-priced figure. Total names which rows its sums cover (token sum, cost sum).

A Copilot supervising session is read from ~/.copilot/session-state/<id>/events.jsonl instead: sum session.usage_record events by token class, read cost as premium requests and maxPromptTokens as the effective window.

The supervising session counts itself. Everything above measures what goal-run.ts spawned; it never measures the session reading this skill and watching it. After the run ends, locate this session's own transcript under ~/.claude/projects/<encoded-cwd>/ (the working directory with every / turned into a -). A transcript repeats one message's usage on every content-block entry it produced, so before summing, deduplicate by message id — keep one usage per id — to land on the envelope-exact figure rather than an inflated one; the peak needs no such rule, max is immune to a repeated value. Sum the per-class usage that survives deduplication across the transcript entries timestamped between this run's goal-run.ts launch and the fold — the same window as the row's duration, and for the same reason: the transcript may carry earlier runs and unrelated work — and add the Supervising session row to the attribution table with those four numbers, and — read the same way off that transcript's own events — its served model, its context peak (percentage against the note's effective window, or tokens if the model is unknown), and its compaction count folded into the note alongside the rest. Total includes that row like any other — a token total that leaves out the session that spent them is not a total.

Closing paths

Every closing report names the plan and the run directories it produced, so the developer can open them without hunting: the plan path, then each run directory under <runs>/, one path per line — never a single ·-joined line. A developer copy-pasting a path off a run that used commas or middle dots as separators is the failure this rule exists to avoid.

Rules for THIS command

  • Classify before repairing, always — never patch a plan and never discard a tree without first naming which fault this is and why.
  • A plan fault may only move inside the closed set, proven by plan-guard.ts before every relaunch. A gateN=/dodN= line moving is never a repair, it is the halt disappearing.
  • It never patches implementation code. The tree it may touch is discarded, not edited; the implementer rewrites under the gate on relaunch, so nothing lands that the bite check never bit.
  • Unknown stops. No repair is better than a wrong one.
  • One relaunch per iteration, then stop — no matter which bucket, no matter the second exit.

04 同仓库更多 Skills

FabienSalles
FabienSalles/claude-marketplace

vitest-test-conventions

ACTIVATE when writing Vitest tests, creating test files, using vi.fn()/vi.mock()/vi.spyOn(), or test factories. ACTIVATE for 'Vitest', 'it.each', 'NestJS testing module', 'supertest', 'vi.fn', 'vi.mock', 'spyOn'. Provides Vitest/NestJS-specific testing patterns; cross-language testing principles (DAMP, AAA, spy over mock, what NOT to test) live in craft:testing-principles. DO NOT use for: node:test files (a file importing node:test) or a project whose tests run under node --test (see node-test:node-test-conventions), TDD workflow/iterations (see vitest:vitest-tdd-workflow), PHP/PHPUnit tests (see phpunit:php-test-conventions).

测试与质量
★ 9
FabienSalles
FabienSalles/claude-marketplace

vitest-tdd-workflow

ACTIVATE when building new features, endpoints, or user stories using TDD with Vitest in a TypeScript/NestJS context. ACTIVATE for '/feature-tdd-dev', 'TDD', 'red-green-refactor' in TS context. Provides Vitest/NestJS-specific TDD examples and commands; cross-language TDD workflow (cross-layer iterations, RED-GREEN-REFACTOR, working app, bug-fix-first-test, mocks-hiding-bugs) lives in craft:tdd-workflow-principles. DO NOT use for: node:test files (a file importing node:test) or a project whose tests run under node --test (see node-test:node-test-conventions), test writing conventions (see vitest:vitest-test-conventions), PHP TDD (see phpunit:php-tdd-workflow).

Skill与智能体开发
★ 9
FabienSalles
FabienSalles/claude-marketplace

ts-refactoring

ACTIVATE when refactoring TypeScript code. ACTIVATE for 'refactor', 'extract', 'redesign', 'simplify', or 'clean up' in a TypeScript / Node / NestJS context. This skill provides TS-specific examples for the cross-language refactoring rules defined in craft:refactoring-principles. DO NOT use for: writing new features from scratch, general OOP patterns (see ts-oop).

★ 9
FabienSalles
FabienSalles/claude-marketplace

ts-ports-adapters

ACTIVATE when a TypeScript codebase with no dependency-injection container has to declare a port and wire its adapter — a repository, a clock, an id generator, an event dispatcher the domain depends on. ACTIVATE for 'port', 'adapter', 'SPI', 'dependency inversion' or 'composition root' in a codebase that wires its dependencies by hand. Covers: the port as a type of functions in the domain layer, the adapter as an object literal annotated with it, the handler that takes its dependencies first and its data second, the composition root in the infrastructure layer, and the in-memory stub that substitutes for the adapter in a unit test. DO NOT use for: NestJS, or any container-based DI with decorators and tokens (see nest-ddd-conventions), aggregate operations and smart constructors (see ddd-ts-fp), the Result type itself (see ts-functional).

测试与质量
★ 9
FabienSalles
FabienSalles/claude-marketplace

ts-oop

ACTIVATE when designing TypeScript classes, and the value objects or collections modelled as classes, or when the user asks about object design, encapsulation, Tell Don't Ask, or Symbol.iterator. Provides TS-specific examples for the cross-language OOP rules defined in craft:oop-principles, plus TS-specific patterns (Symbol.iterator, branded types for primitive identifiers). DO NOT use for: a value object or an aggregate modelled as an immutable record rather than a class (see ddd-ts-fp), refactoring methodology (see ts-refactoring).

★ 9
FabienSalles
FabienSalles/claude-marketplace

ts-layer-boundaries

ACTIVATE when wiring a Worker entry point, a Router, a controller, or a domain event consumer in a TypeScript CQRS codebase, or when deciding where a port belongs. ACTIVATE for 'composition root', 'layer boundary', 'ESLint zones', 'domain must not import infrastructure'. Covers: the *Worker.ts composition root, the curried consumer, Router wiring of command endpoints, the five-gesture controller, the domain-to-infrastructure import boundary enforced by ESLint, infrastructure-only ports, in-memory doubles for every port, the read-can-reach-write asymmetry, and read endpoints going through a domain handler. DO NOT use for: the port's own shape and adapter binding (see ts-ports-adapters), the Result type (see ts-functional), domain event structure (see ts-ddd-events).

★ 9