drive
Automatically cycles through implementation, PR creation, self-review, and fixes from an Issue or an instruction to produce a merge-ready PR. Supports single/multiple Issues/PRs and explicit dependency notation. Can optionally run through to merge. In orchestration, detects and self-resolves cross-cutting concerns before merge, leaving no follow-up within a single run.
- License 许可
- MIT
- 所属仓库
- ozzy-labs/skills
- 最近更新
- 2026-07-13
- Stars
- ★ 0
- Forks
- 0
01 前言 Front Matter
- description
- Automatically cycles through implementation, PR creation, self-review, and fixes from an Issue or an instruction to produce a merge-ready PR. Supports single/multiple Issues/PRs and explicit dependency notation. Can optionally run through to merge. In orchestration, detects and self-resolves cross-cutting concerns before merge, leaving no follow-up within a single run.
- allowed-tools
- Read, Write, Edit, Bash, Grep, Glob, WebSearch, WebFetch, AskUserQuestion, Agent, Workflow
- argument-hint
- <#N |
- disable-model-invocation
- true
02 资源文件 3
- 脚本drive-plan.mjs19.1 KB
- 脚本drive-report.mjs12.0 KB
- 参考文档worktree-safety.claude-code.md9.2 KB
03 SKILL.md 正文
drive
Read .agents/skills/drive/SKILL.md and follow its workflow steps.
Important: for each phase, Read the corresponding skill's SKILL.md and execute only the workflow steps. Ignore entirely the "next action suggestions" section and the "completion report" section within the SKILL.md you read. Transitions between phases are controlled by this skill.
Claude Code-specific additions
Input parsing
Parse $ARGUMENTS and identify the target list (Issue/PR/instruction), dependency notation, and options (--merge, --concurrency N, --review=<mode>, --no-usage-guard). Deterministic expansion, DAG/wave construction, and mode branching are delegated to drive-plan.mjs (~/.claude/skills/drive/drive-plan.mjs; for dogfooding, <repo>/.claude/skills/drive/drive-plan.mjs), the same as the canonical, and aggregate-report formatting is delegated to drive-report.mjs.
- 1 target with no dependency notation (
->) → single mode - 2 or more targets, or dependency notation present → orchestration mode
Handling of --review:
- Default is
quick - Single mode: accepts all of
quick/final-deep/deep - Orchestration mode: forces
--review=quick; iffinal-deep/deepis specified, shows a warning and falls back toquick(cost management)
Handling of usage-guard:
- Enabled by default (opt-out). A plain drive with no checkpoints inserted runs only when
--no-usage-guardis explicitly added. - If
--no-usage-guardis not specified, the usage-guard engine is called at the checkpoints described later in "usage-guard wiring (default ON, disabled via--no-usage-guard)"; on Usage Limit overage, it waits until the budget recovers before re-entering itself. --usage-guardis accepted as a backward-compatible deprecated no-op alias (it doesn't need to be explicit since it's ON by default; behavior is identical to the default). It is never force-added to the continuation command.- The original argument list is saved at parse time (used to assemble the continuation command
/drive <元の引数>and the resume line in the failure-time report再開: /drive <元の引数>— canonical's Phase 5 / Phase Final-6, only when there's a leftoverfailed/merge-ready/skipped—).--no-usage-guardis included in what's saved, and carried over to the continuation command, only if the user specified it.--usage-guardis a no-op alias, so it's neither saved nor added.
Autonomous execution
AskUserQuestion is not used at all — including for plan approval — up through the merge step (or merge confirmation) (fully autonomous execution).
usage-guard wiring (default ON, disabled via --no-usage-guard)
Enabled by default. Pauses work before the Claude Code Usage Limit (5-hour = Current / weekly = Weekly) reaches 100%, and automatically resumes once the budget recovers. Only when --no-usage-guard is specified does this section's processing not run at all, leaving drive's core behavior unchanged (since pause/resume is Claude-specific, the wiring is confined to this overlay; precedent: review --deep). --usage-guard is accepted as a deprecated no-op alias, but since it's enabled by default, behavior doesn't change.
Claude-only: the usage-guard engine depends on the OAuth usage-rate endpoint and
ScheduleWakeup, so it only works on Claude Code (theusage-guardskill gated byadapters: claude-code= #121). The base SKILL.md is ON by default, but since this overlay isn't included in the build output for other adapters (codex/gemini/copilot), it effectively remains a no-op there.
Graceful degrade (skill absent)
Since it's ON by default, drive is not stopped with an error even in an environment where the usage-guard skill / usage-check.mjs doesn't exist (e.g., ~/.claude/skills/usage-guard/ not installed). At the start of each checkpoint, check for the existence of the usage-guard skill (.claude/skills/usage-guard/SKILL.md; in user-scope, ~/.claude/skills/usage-guard/SKILL.md), and if its absence is detected, emit a one-line warning (e.g., "⚠️ usage-guard degraded: skill not installed, proceeding normally without monitoring") and proceed normally as-is (treated as fail-open — subsequent checkpoints are also skipped). This is a mandatory requirement of being ON by default: the guard's own absence must never hard-stop drive.
checkpoint trigger points
When --no-usage-guard is not specified (= default ON), usage-guard is called only at the following entry points to a resumable unit:
| Mode | checkpoint |
|---|---|
| Single mode | before each target's Phase 1 (implement) begins |
| Single mode | before each review-loop iteration (before each pass in Phase 3 begins) |
| Orchestration | before each wave begins (at the start of the wave loop in Phase 1..N) |
| Orchestration | before each worker dispatch (immediately before launching a worker within the same wave) |
Checkpoints are always placed at boundaries where re-entry can happen cleanly. It does not pause mid-implement (partway through implementation, before a PR exists), partway through a review pass, or partway through a commit/push — pausing there risks losing track of progress on re-entry. Since drive has idempotent resume (detecting an existing PR and resuming from Phase 3), any of the boundaries above can safely resume from where they left off on re-run.
Steps at a checkpoint
At each checkpoint, run the following:
- Read the
usage-guardengine (.claude/skills/usage-guard/SKILL.md; in user-scope,~/.claude/skills/usage-guard/SKILL.md) and run its "lightweight wait-loop" (= runusage-check.mjsin the same directory via Bash to get JSON).- At the wave / worker-dispatch checkpoint (orchestration), gate headroom-aware (#141): wave dispatch definitively launches N uninterruptible workers, and they consume budget while running. Judging by the current value alone can't account for the projected consumption of a whole wave, and the threshold can be overshot mid-wave (observed in practice:
five_hour86% → 98% after 1 wave, with 3-worker concurrency). To prevent this, the dispatch checkpoint passesusage-check.mjs --headroom <pct>and gates on the projected post-dispatch value (util + reserve(N) >= threshold).reserve(N)is a reservation proportional to the concurrency levelN(--concurrency, defaultmin(4, tasks in wave)). A rough guideline isreserve = N × per_worker_pct(per_worker_pctis the projected consumption per heavy worker; take the observed86→98 / 3-worker ≈ 4pt/workeras the initial value, overridable via theUSAGE_GUARD_DISPATCH_HEADROOMenv var). Example:concurrency=3,per_worker≈4pt→--headroom 12→ pause before dispatch ifthreshold(95) − 12 = 83%is exceeded. The larger--concurrencyis, the larger the reserve grows. - The single-mode Phase 1 / review-loop checkpoint doesn't pass headroom (= default 0, gating on the current value, as before). Since the consumption of 1 unit can be absorbed at an interruptible boundary, no reserve is needed.
- At the wave / worker-dispatch checkpoint (orchestration), gate headroom-aware (#141): wave dispatch definitively launches N uninterruptible workers, and they consume budget while running. Judging by the current value alone can't account for the projected consumption of a whole wave, and the threshold can be overshot mid-wave (observed in practice:
ok: true(both budgets are below threshold; at the dispatch checkpoint,util + reserveis below threshold) → proceed normally. Move straight on to the next phase / wave / worker dispatch.ok: false(either budget exceeds the threshold) → defer to usage-guard's wait-loop. A post-reset buffer (resume_buffer_seconds, default +300 seconds) is folded intowait_seconds, and the wait extends toresets_at + buffer(avoiding a re-bounce from re-entering right at the reset boundary). Execute the engine'sresume_plan(#212) rather than re-deriving the trigger by hand:usage-check.mjsemitsresume_plan { trigger, wait_seconds, fire_at }, and orchestration checkpoints pass--context orchestrationso the plan prefers a durable one-shot (mapping in usage-guard SKILL.md §"Resume plan"). Thetriggercases:schedule-wakeup(in-session, wait ≤1h) → set up a heartbeat withScheduleWakeup(min(wait_seconds, 3600))and wait (no re-entry and no budget consumption while waiting). Ifwait_secondsexceeds 3600, re-check in multiple installments.cron-oneshot(non-/looporchestration, wait >1h, needing restart resilience) → set aCronCreate(recurring: false, durable) tofire_at(= resets_at + resume_buffer_seconds), and re-submit the continuation command when it fires (a single wall-clock shot, restart-resilient; the one-shot auto-deletes after firing). Since this being ON by default makes this path (>1h, non-/loop) easy to hit, this is preferred overScheduleWakeup. See usage-guard SKILL.md §Lightweight wait-loop, "Choosing the resume trigger", for details.cron-routine(durable, survives a 100% hard-interrupt — #213) → arm a cloud Routine atfire_at. See "Durable pre-arm" below; validatefiresAfterReset(plan, resets_at)first, and fall back tocron-oneshot+ a warning where/scheduleis unavailable.- When a reflection lag is suspected (
suspected_reflection_lag: true), don't set a long-duration CronCreate at the boundary — instead re-check withScheduleWakeupat a short interval (wait_seconds≈ 180 seconds) (this catches a false 100% caused by the residual image of the prior window right after the boundary, once recovery is confirmed, avoiding a false negative from a ~5h stall; see usage-guard SKILL.md §Behavior: reflection-lag detection). - On waking, self-re-enter via the continuation command
/drive <元の引数>(since it's ON by default,--usage-guardis never force-added;--no-usage-guardis carried over only if the user specified it — though since this section doesn't run at all when--no-usage-guardis set, the actual continuation command can simply pass the original arguments as-is). drive's idempotent resume detects the existing PR / branch / completed workers and resumes from where it left off (no duplicate side effects, even across a wait). - Repeat steps 3–4 until
usage-check.mjsreturnsok: true.
Pass the original argument list as-is to the continuation command (since it's ON by default, adding
--usage-guardis unnecessary — the guard keeps taking effect by default after resume too).
Durable pre-arm (hard-interrupt resilience, #213/#217)
A reactive pause (steps above) keeps the session alive so an in-session trigger (ScheduleWakeup / a session-scoped CronCreate) can fire. But if the boundary checkpoint is bypassed and a worker burns all the way to 100%, the session hard-interrupts and those in-session triggers die with it — the exact "stopped and never resumed" failure. Only a cloud Routine (scheduled deployment) survives a hard-interrupt.
For orchestration / long runs with hard-interrupt risk, pre-arm a durable resume at run start (not reactively):
- Before launching the first wave, run
usage-check.mjs --context durableto obtain aresume_planwithtrigger: "cron-routine"andfire_at. - Assert
firesAfterReset(plan, resets_at)(a Routine firing at/before the reset is rejected withsession_rate_limited_error;fire_at = resets_at + bufferalready satisfies this — the guard catches aresume_buffer=0/ clock-skew edge). - Arm a cloud Routine at
fire_atwhose prompt is the continuation command/drive <元の引数>(idempotent — drive resumes from the existing PR/branch/cached workers, so a spurious fire is a harmless no-op). - On a clean finish (limit never hit), the one-shot Routine either no-ops on its idempotent continuation or is cancelled.
- Fallback: where the host
/schedule(Routines) is unavailable, fall back to the in-sessioncron-oneshotpath + a degradation warning — a durable path that cannot be armed must not silently drop the resume.
This composes with the two existing layers (headroom-aware boundary pause + the #123 PreToolUse ceiling): the pre-armed Routine is the last-resort net that recovers even a genuine hard-interrupt.
Granularity and duplication (two-layer defense against in-wave overshoot, #141)
Orchestration pauses at the granularity of wave boundaries / worker-dispatch boundaries. There's a structural failure mode here:
- Failure mode (in-wave overshoot): even if the boundary checkpoint dispatches with
ok:true, the N launched workers consume budget while running. Since the boundary checkpoint has no effect while things are running, the threshold can be crossed and 100% reached mid-wave.ok:falseis only detected after the fact at the next boundary, and can't prevent overshoot within that wave (observed in practice: 86% pre-dispatch, threshold 95 → 3-worker concurrency → 98% while running). Lowering the threshold is a stopgap that can't account for concurrency and is not a substitute.
This is closed off with a two-layer defense:
- A resumable pause at the boundary (headroom-aware, preventive): at the wave / worker-dispatch checkpoint, pass
usage-check.mjs --headroom <pct>(withreserve(N)computed from--concurrency) and gate on the projected post-dispatch value, stopping the dispatch itself preemptively (step 1 of "Steps at a checkpoint" above). On a headroom trip, the wait is until the budget edge + buffer (headroom recovers when the budget resets). - The mid-unit ceiling (#123 PreToolUse hook, a reliable ceiling): a mid-unit overage in an already-launched, running worker can't be stopped at a boundary. The #123 hook — which takes effect before every tool call and also reaches inside subagents — is the only reliable ceiling. In environments running
/drive's wave parallelism, it's recommended to wire up thematcher:"*"hook by default (#141). Previously, due to the risk of the pre-#139 hook hard-stopping on a transient outlier, it remained only a "recommended addition," but since #139 (file kill-switch / reflection-lag ALLOW / debounce / spike rejection) resolved the hard-stop risk, wiring it by default has become safe. In the unlikely case of a false deny, it can be released immediately withtouch ~/.claude/usage-guard/DISABLE(usage-guard SKILL.md §Disabling). See usage-guard SKILL.md §Enabling the PreToolUse hook for wiring steps.
The boundary pause (headroom-aware) and the mid-unit hook are each insufficient on their own. headroom is the "stop dispatch preemptively" prevention; the hook is the "reliable ceiling during execution." Only when both are in place is overshoot practically closed off.
- The prompt itself passed to the worker (subagent) can remain unchanged. Since the worker runs single mode, the parent inserting a headroom-aware checkpoint before worker dispatch (ON by default) is what provides wave-granularity budget handling.
fail-open (surfacing degradation)
If usage-check's signal acquisition fails completely (both the endpoint → JSONL fallback fail), usage-guard returns ok: true (fail-open). drive proceeds normally as-is — a bug in the guard itself must never hard-stop drive.
However, fail-open means the guard is effectively OFF. When the source of the JSON obtained at a checkpoint is anything other than endpoint / cache (especially fail-open), the drive caller leaves the degradation explicitly in the report (e.g., "⚠️ usage-guard degraded: source=fail-open, not actually monitoring"). See usage-guard SKILL.md §Environment requirements for the causes of the endpoint path being unusable (api.anthropic.com egress / permission to read ~/.claude/.credentials.json) and how to recover. The PreToolUse hook on a running worker likewise emits a degradation warning to stderr.
Choosing the orchestration execution mechanism
For orchestration worker parallel execution, the Workflow method is canonical (default). It rides natively on the harness via Claude Code's Dynamic Workflows (a deterministic script + journal resume + parallel agent() launches), delegating wave execution, worker distribution, progress observation, and interrupt/resume to the runtime (ADR-0028 R2). It falls back to the conventional Agent tool method (the "subagent dispatch" section) only in environments where the Workflow tool is unavailable (Dynamic Workflows disabled — disableWorkflows — / an older version).
| Workflow method (canonical) | Agent tool method (fallback) | |
|---|---|---|
| Concurrency control | runtime manages the cap (max 16) and queueing | manual semaphore |
| worktree isolation | agent({ isolation: 'worktree' }) |
Agent({ isolation: "worktree" }) |
| Return-value validation | structured validation via schema (mismatches auto-retry) |
parent parses free-form JSON |
| Progress monitoring | /workflows UI + log() |
gh pr list 30-second polling |
| Interrupt/resume | journal resume (resumeFromRunId; completed workers restored from cache) |
manual re-run (PR detection via drive's idempotent resume) |
Orchestration via the Workflow method (canonical)
Phase 0 (DAG / wave construction) and the plan display are done on the conversation side, before launching the Workflow (since a workflow can't accept mid-run user input, all approval-related steps are placed before/after launch). Pass the wave configuration via args and assemble a script of the following form:
export const meta = {
name: 'drive-orchestration',
description: 'drive: wave 単位で worker を並列実行し merge-ready PR 群を作る',
// pure literal(args を参照できない)。起動時に drive-plan.mjs の wave 数へ合わせて書く
phases: [{ title: 'Wave 1' }, { title: 'Wave 2' }],
}
// worker 戻り値 contract。canonical(.agents/skills/drive/SKILL.md)の戻り値 JSON を Schema 化した
// もので、フィールドは canonical と 1:1。runtime が schema 不一致を検出して自動リトライする。
// status は worker では最大 merge-ready(self-merge しないため merged は現れない)。
const WORKER_SCHEMA = {
type: 'object',
required: ['target', 'status', 'final_head_state'],
properties: {
target: { type: 'string' }, // "#N"
title: { type: 'string' },
branch: { type: 'string' },
pr_url: { type: 'string' },
pr_number: { type: 'number' },
status: { enum: ['merge-ready', 'failed'] }, // worker は self-merge しない
review: { type: 'object' }, // { mode, axes_applied, by_axis, total, iterations }
cross_cutting_gaps: { type: 'array', items: { type: 'string' } },
final_head_state: { type: 'object' }, // { symbolic_ref, rev_parse_HEAD, status_short }
error: { type: 'string' },
},
}
// worker prompt。Agent tool 方式「subagent dispatch」節の制約と同一(runtime の worktree 隔離は
// cleanup を肩代わりするが worker の git 操作自体は防がないため制約は省略不可)。
const workerPrompt = (target) => [
`.agents/skills/drive/SKILL.md を Read し、target ${target} について単一モード Phase 1-3 を実行せよ。`,
'- Phase 4(マージ)は実行しない。merge-ready で停止し gh pr merge を一切呼ばない(self-merge 禁止)。',
'- main / 親 ref への書き込み禁止: git checkout main / switch main / symbolic-ref HEAD refs/heads/main /',
' update-ref refs/heads/main / reset --hard origin/main(main を指す状態で)/ branch -m / push origin main。',
'- Edit/Write の file_path は自 worktree path(.claude/worktrees/agent-<id>/)配下の絶対パス限定。まず pwd で確認。',
'- 依存元 wave がある target は依存元 PR の headRefName をベースに stacked PR を作る。',
'- 戻り値は WORKER_SCHEMA の JSON(status は最大 merge-ready、final_head_state / cross_cutting_gaps 含む)。',
].join('\n')
// args は drive-plan.mjs --json の出力そのもの(会話側で Phase 0 に実行して渡す):
// { waves: string[][], deps: { "#N": string[] }, concurrency, review, options: { merge }, ... }
// waves は target 文字列の二次元配列、deps は target → 依存 target[] の map(canonical と同形)。
const results = []
const failed = new Set()
for (const [i, wave] of args.waves.entries()) {
// 依存元が failed の target は dispatch せず skipped(canonical の失敗 semantics)。deps は
// drive-plan.mjs の deps map をそのまま参照する(wave 要素に依存情報を再掲しない)。
const runnable = wave.filter((t) => !(args.deps[t] ?? []).some((d) => failed.has(d)))
for (const t of wave) {
if (runnable.includes(t)) continue
const upstream = (args.deps[t] ?? []).filter((d) => failed.has(d))
results.push({ target: t, status: 'skipped', error: `upstream failed: ${upstream.join(',')}` })
log(`${t} skipped (upstream failed: ${upstream.join(',')})`)
}
// 同一 wave 内の独立 worker は parallel() で並列(バリア = wave 完了)。wave 列の for-await が
// 依存バリア。pipeline() は stage 間バリアがないため依存 wave には使わない(意図的に parallel)。
// runtime が concurrent cap(最大 16)を管理。--concurrency がそれより小さければ、会話側で
// runnable を args.concurrency 件ずつのスライスに整形して直列に流す。
const waveResults = await parallel(
runnable.map((t) => () =>
agent(workerPrompt(t), {
label: t,
phase: `Wave ${i + 1}`,
isolation: 'worktree',
schema: WORKER_SCHEMA,
}),
),
)
for (const r of waveResults) {
if (!r) continue
results.push(r)
if (r.status === 'failed') failed.add(r.target)
log(`${r.target} → ${r.pr_url ?? '-'} (${r.status})`)
}
}
return { results }
The workerPrompt(target) in the script must always include the following (the skeleton is already embedded in the script above; identical constraints to the Agent tool method's "subagent dispatch". The runtime's worktree isolation takes care of cleanup, but doesn't prevent the worker's git operations themselves, so the prompt constraints can't be omitted):
- Instructions to Read the canonical SKILL.md and run single-mode Phase 1-3 (do not run Phase 4 merge; stop at
merge-ready) - A list of commands prohibited for writing to main / the parent-side ref
- The Edit / Write tool's
file_pathconstraint (limited to its own worktree path) - Merge prohibition (never call
gh pr merge) - Base-branch rule (a target with an upstream wave dependency is stacked on headRefName; since it doesn't self-merge, it's stacked regardless of
--merge) - The return-value JSON contract (
statuscapped atmerge-ready/ includesfinal_head_state/cross_cutting_gaps; matches theWORKER_SCHEMAabove)
Notes specific to the Workflow method:
Date.now()/Math.random()/ argument-lessnew Date()cannot be used within the script (the runtime throws, for resume determinism). If a timestamp is needed, pass it viaargs- Observability is handled by the
/workflowsUI andlog()(pto pause /xto stop / per-agent token monitoring). The Agent tool method'sgh pr list30-second polling is unnecessary (the Workflow method doesn't poll for PR detection) - The
awaitbetween waves is an intentional barrier from the dependency relationship (don't turn it into a pipeline) - Workers within the workflow are fixed to
acceptEditsand inherit the session's allowlist. To avoid permission prompts during a long-running run, confirm before launch that the necessary commands are on the allowlist - Interrupt/resume is the Workflow's journal resume: re-entering via
Workflow({scriptPath, resumeFromRunId})restores unchangedagent()calls (completed workers) from the journal/cache and continues from the incomplete wave. This is the primary mechanism replacing the conventional "detect a PR via 30-secondgh pr listpolling and re-run manually." drive's idempotent resume (detecting an existing PR) remains as a secondary safety net (it also takes effect inside workers) - Phase Final-1 through Final-6 are run on the conversation side after the workflow ends. Since a worker's worktree contains changes, it's not subject to the runtime's automatic deletion; Final-2 audit / Final-3 reconciliation are performed within this surviving worktree, and cleanup (the Final-5 section) is done last. The worktree path convention (
.claude/worktrees/agent-<id>/) is the same - The folding for Final-3 reconciliation is also done on the conversation side after the workflow returns (group the gaps by
source_pr, and fold them in parallel per PR withparallel(); within the same PR it's sequential). Alternatively, a separate small workflow dedicated to reconciliation may be launched - The Final-4 merge (parent-centralized, dependency-order merge when
--mergeis specified) and the bulk-merge confirmation when--mergeis not specified (the AskUserQuestion in the "After completion" section) are performed after the workflow returns - The wave checkpoint is inserted on the conversation side (default ON; omitted when
--no-usage-guardis specified). Since the workflow script runs deterministically and can't call either Read on the SKILL.md orScheduleWakeup, launch the workflow per wave, and before each wave's launch, run the "usage-guard wiring" section's checkpoint on the conversation side headroom-aware (deriving--headroomfrom--concurrency; #141) (ifok, launch the next wave's workflow; if over, wait → re-enter via/drive <元の引数>, restoring completed workers from cache viaresumeFromRunIdand continuing). While the workflow is running (a mid-unit overage in an already-dispatched worker), the #123 PreToolUse hook serves as the ceiling (§Granularity and duplication)
subagent dispatch (orchestration mode, Agent tool method fallback)
In orchestration mode, each target is run in parallel with the Agent tool:
- isolation:
"worktree"(mandatory) - subagent_type:
general-purpose - prompt: since a subagent can't call a slash command, have it Read
.agents/skills/drive/SKILL.mdand instruct it to run the single-mode workflow (Phase 1-3) for target #N. The worker does not run Phase 4 (merge) — after review passes, it stops atmerge-readyand returns JSON without ever callinggh pr merge(the same even when--mergeis specified; the parent centrally manages the merge in Phase Final-4). Have it return the final result as JSON - Prohibition on writing to main / the parent-side ref (must be stated explicitly in the prompt): the subagent is self-contained within its own worktree branch. The following commands are all prohibited — they pollute the parent worktree's
HEAD/index/refs/heads/mainvia the shared git directory (Issue #66 / Issue #89). Since the worktree is deleted on the parent side, there's no need to switch back to main:git checkout main/git switch main/git checkout HEAD~(moving HEAD)git symbolic-ref HEAD refs/heads/main(symbolically switching HEAD to main)git update-ref refs/heads/main <sha>(directly rewriting the main ref)git reset --hard origin/main(if run while its own branch points to main, this propagates indirectly to the parent)git branch -m <new-name>(breaks the worktree-branch binding)git push origin main/git push origin HEAD:main
- Require
final_head_statein the return-value JSON (must be stated explicitly in the prompt): on completion, the subagent must include the output of its own worktree'sgit symbolic-ref HEAD/git rev-parse HEAD/git status --shortin thefinal_head_statefield of the return-value JSON. Ifsymbolic_refisrefs/heads/mainor empty (detached), the parent-side Phase Final-1 raises a warning. This is a countermeasure against the observed discrepancy between the self-reported "no main checkout" and reality (Issue #89), making the self-attestation verifiable - The Edit / Write tool's
file_pathconstraint (must be stated explicitly in the prompt): thefile_pathpassed to a subagent's Edit / Write tool must always be limited to an absolute path starting with its own worktree path (.claude/worktrees/agent-<id>/). A parent worktree path (a path directly under the repo root that doesn't include.claude/worktrees/) must never be passed. The pollution observed in Phase 20 (opshub) occurred via the absolute path argument of Edit/Write, not viacd, which is why this constraint is decisive. It's safe to check its own worktree path withpwdbefore execution and then pass it to the tool (Issue #77) - Merge prohibition (must be stated explicitly in the prompt): the subagent never calls
gh pr mergeat all (neither--autonor--delete-branch). If it were to self-merge, it would already be merged, cross-cutting couldn't be folded in, and follow-up would remain. In addition,--delete-branchfails to delete a branch held by its own worktree (fatal: '<branch>' is already used by worktree at ...). All merging and cleanup of local branches / worktrees is handled on the parent side in Phase Final (Final-4 merge / Final-5 cleanup) (Issue #69 / #166) - Out-of-scope impact check (must be stated explicitly in the prompt): if the subagent adds an enum / field / CLI flag, it greps the whole repo for the corresponding help strings / error messages / samples/docs and checks whether they're in sync. If not in sync, it includes the fix in its own PR if feasible. If it clearly exceeds its own scope, it records it in the return-value JSON's
cross_cutting_gaps: string[]field in the form<file>:<line> — <symbol> not synced, to be aggregated into the parent's Phase Final-2 audit (Issue #70) - The base branch when there's an upstream wave dependency: since the worker doesn't self-merge, the upstream also remains unmerged during the run. So regardless of
--merge, it creates a stacked PR based on the upstream PR's headRefName (the parent reassigns the downstream's base to main when merging in dependency order in Phase Final-4) - Parallel launch: independent subagents within the same wave are launched in parallel via multiple tool calls in a single message
- Concurrency:
min(4, tasks in wave), overridable with--concurrency N; exceeding 8 only warns - When the number of tasks in a wave exceeds the concurrency level: wait for a free slot via a semaphore approach (wait for a previously launched subagent to complete before launching the next)
Observability and interrupt/resume
When Phase 0 completes (before launching the Workflow, on the conversation side), display the wave configuration and target list. Output the aggregate report in Phase Final. The mechanisms for progress observation and interrupt/resume differ by method:
Workflow method (canonical):
- Progress is handled by each worker's single-line
log()output and the/workflowsUI (pto pause /xto stop / per-agent token monitoring). Nogh pr listpolling is done - Interrupt/resume is the Workflow's journal resume: re-entering via
Workflow({scriptPath, resumeFromRunId})restores completed workers (unchangedagent()calls) from the journal/cache and continues from the incomplete wave. This replaces the conventional "pollgh pr listevery 30 seconds from the wave-launch time and resume via PR detection" - State finalization has the worker return-value JSON (
schema-validated) as the SSOT
Agent tool method (fallback):
- Since the
Agenttool returns only the final result, streaming intermediate reports aren't possible. The parent records the wave-launch time<T>in ISO 8601 and pollsgh pr list --author @me --state open --search "created:>=<T>" --json number,url,headRefName,titleat 30-second intervals. It detects new PRs from the diff against known PRs and displays the URL immediately - Interrupt/resume is manual re-run (drive's idempotent resume detects the existing PR / branch and continues from Phase 3)
Phase Final-1: parent worktree consistency check
A fail-safe for the case where a subagent pollutes the parent's HEAD / index / refs/heads/main via the shared git directory (originating from Issue #66 / Issue #77 / Issue #89). For the execution details of the 7 detection axes (+ cross-checking the return value's final_head_state) and the recovery sequence, see the "7 contamination-detection axes" and "Recovery sequence" sections of worktree-safety.claude-code.md. If any of them don't match, output a warning + recovery steps at the end of the aggregate report.
Phase Final-2: cross-cutting audit (pre-merge, parallel within worker worktrees)
Follows the detection content of the canonical's (.agents/skills/drive/SKILL.md) Phase Final-2 (worker-report aggregation / enum-flag sync / stale wording / lockfile drift / docs-code). Claude Code-specific execution:
- Run grep within each worker's worktree (
.claude/worktrees/agent-<id>/, which persists until Final-5). Fetch the PR-specific diff withgh pr diff <N>andrgthe extracted symbols within that worktree - Run in parallel per PR (with the Agent tool method, multiple tool calls in a single message; with the Workflow method,
parallel()on the conversation side after return). Attribute each gap tosource_prand pass it to Final-3
Phase Final-3: reconciliation (folding into the originating PR, parallel per PR)
Following the canonical's Phase Final-3, group the gaps by source_pr and fix/push in the originating PR's worktree. Claude Code-specific execution:
- In the worker worktree (
.claude/worktrees/agent-<id>/) corresponding to each gap'ssource_pr, Edit the target file (file_pathlimited to its own worktree path) → lint → commit (fix(sync): ...) →git push - Parallel per PR (no collision, since each uses a separate worktree; multiple tool calls for the Agent tool method,
parallel(gaps.groupBy(pr))for the Workflow method). Multiple gaps within the same PR are sequential (they share the same worktree) - No full review loop is run (lint pass only). A gap where folding conflicts / that spans multiple PRs falls back to a dedicated reconciliation PR, which is merged at the tail of Phase Final-4 with main as the base after all content PRs have merged (see canonical Final-3). A folding failure is left as a warning via fail-soft
Phase Final-4: dependency-order merge (centralized by the parent)
Following the canonical's Phase Final-4, the parent merges in topological order when --merge is specified. Claude Code-specific notes:
POLICY_GUARD_PROCEED=merge gh pr merge --squashdoes not add--delete-branch(since the worker worktree still holds that branch and remains, deleting the local branch would fail due to the worktree lock. Deletion of the branch / worktree is consolidated into Final-5 cleanup).POLICY_GUARD_PROCEED=mergeconveys--merge's proceed override to the PreToolUsepolicy-hook.mjs(wired viahooks add policy), avoiding a deny stemming from gate=ask(canonical's "Merge and the autonomy policy" / #195)- If a phantom conflict appears after reassigning the downstream PR's base (
gh pr edit <下流> --base main), rungit rebase origin/main→git push --force-with-leasewithin the downstream's worktree (limited to its own worktree branch; the parent ref is left untouched) before merging the next one - The parent worktree's HEAD / main ref is not touched. Since the merge goes through the
ghAPI (a remote operation), the parent's local git state proceeds unchanged
Phase Final-5: subagent worktree cleanup
Runs after Final-4's merge. The per-status cleanup policy (merged is deleted / merge-ready and failed are left in place) follows the canonical's Phase Final-5. For the Claude Code worktree-mechanism-specific execution steps (avoiding cwd loss via a subshell, releasing the -f -f lock, checking for leftover worktree-agent-* synthetic branches), see the "Cleanup execution steps" section of worktree-safety.claude-code.md (originating from Issue #69 / Issue #90). If anything other than merged is left over, or cleanup itself fails, output a list of leftovers and manual cleanup steps as a warning at the end of the aggregate report.
On interruption
If interrupted at any phase, confirm the next action with AskUserQuestion:
- 「エラーを修正して再開する」 → resume from the interrupted phase
- 「中断する」 → end
In orchestration mode, if only some tasks fail, after outputting the Phase Final report, confirm the resume targets with AskUserQuestion.
After completion
Single mode
- When
--mergeis specified: run the merge following the Phase 4 procedure, report the result, and end - When
--mergeis not specified: call AskUserQuestion (don't set theanswersparameter)- 「PR をマージする」 → run the merge with
POLICY_GUARD_PROCEED=merge gh pr merge --squash --delete-branchand report the result (since AskUserQuestion's explicit approval satisfies theaskgate,POLICY_GUARD_PROCEED=mergeis prefixed so it passes through the PreToolUsepolicy-hook.mjs; see the canonical's "Merge and the autonomy policy") - 「追加の変更を行う」 → end
- 「PR をマージする」 → run the merge with
Orchestration mode
Phase Final runs Final-1 (consistency) → Final-2 (audit) → Final-3 (reconciliation), regardless of whether --merge is present (to finalize the PRs gap-free). The only branch point is the Final-4 merge:
- When
--mergeis specified: the parent merges in dependency order in Phase Final-4, followed by Final-5 cleanup (cleaning up merged workers) → outputting the Final-6 aggregate report, and ending - When
--mergeis not specified: after completing through Final-3, call AskUserQuestion (don't set theanswersparameter)- 「全 PR を一括マージする」 → run Phase Final-4's dependency-order merge, followed by Final-5 cleanup → output the Final-6 report
- 「個別に対応する」 → output the Final-6 report and end.
merge-readyworktrees remain left in place. The user cleans them up after merging, either via/healtharea #7 or manually
04 同仓库更多 Skills
verify
An integrated skill that runs the combined validation of build / typecheck / test / lint in one shot. The `verify.mjs` engine auto-discovers validation commands via a discovery chain (AGENTS.md's 「検証」 section → package.json scripts → justfile/Makefile/lefthook → language heuristics), executes them serially with their source attached, and returns a result summary. If a higher-priority stage produces a hit, only that stage is executed (no crossing stages).
usage-guard
Monitors Claude Code's Usage Limit (5-hour = Current / weekly = Weekly), pauses work when it exceeds 95%, and automatically resumes once the reset restores headroom. Bundles an engine form that a caller such as drive Reads at a checkpoint, and a standalone form, `/usage-guard "<continuation command>"`, that guards arbitrary work. Claude-only (depends on the OAuth usage-rate endpoint + ScheduleWakeup).
topics
Selects GitHub topic candidates by having the `topics.mjs` engine perform constraint validation, popularity measurement, broad+narrow / singular-plural comparison, and hardcoded ozzy-labs conventions, then applies them via `gh repo edit --add-topic` following the policy's `externally-visible` gate (default batch-confirm). Scope is limited to use within ozzy-labs.
skill-observability
Defines the skill observability event contract (event.schema.json) and emit substrate (obs-emit.mjs). The SSOT for the measurement layer of the skill improvement loop. Referenced by other skills and hooks.
skill-metrics
Aggregates the local observability event log (~/.agents/observability/events.jsonl) and presents, read-only, per-skill invocation counts and notable events (fallback / HITL rejection / loop cap reached / abort). Fires on "show me the skill metrics," "aggregate the observability results," "which skills get used the most?" Does not send anything.
ship
Runs verification, commit, and PR creation all at once. An integrated pipeline that runs verify (build + type + test + lint) → commit → PR creation in sequence against the changes.