diff --git a/docs/PLUGIN-PHILOSOPHY.md b/docs/PLUGIN-PHILOSOPHY.md index caf9544b25..9a9a2207ee 100644 --- a/docs/PLUGIN-PHILOSOPHY.md +++ b/docs/PLUGIN-PHILOSOPHY.md @@ -569,6 +569,15 @@ the session default model changes): | Mechanical prep, one tier down | Sonnet 5 | | Bulk mechanical sweeps | Haiku 4.5 | +The **dispatch-seam** tier enforcement is structural at two binding sites: +`plugins/implementation/agents/implementer.md` and +`plugins/implementation/agents/phase-verifier.md` (both bind the loop-lane convention's strong-tier +current alias; raise the pair together, and note frontmatter binds a floor — the session-relative +raise above it stays a per-invocation override at the dispatch site). That pair is the seam, not the +recheck list: the trigger above re-audits **every** agent-frontmatter `model` value in this +repository, which `git grep -n '^model:' -- 'plugins/*/agents/*.md'` enumerates rather than any +list restated here. + ### Effort tiers Effort routes per lane the way model does. Skill and subagent frontmatter `effort` overrides the diff --git a/plugins/implementation/.claude-plugin/plugin.json b/plugins/implementation/.claude-plugin/plugin.json index 9e2499a5b8..71fe16cbcf 100644 --- a/plugins/implementation/.claude-plugin/plugin.json +++ b/plugins/implementation/.claude-plugin/plugin.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/claude-code-plugin-manifest.json", "name": "implementation", - "version": "0.9.2", + "version": "0.10.0", "description": "Disciplined implementation stage: execute approved plans inline (`/implementation:implement`) or via orchestrated worker subagents (`/implementation:implement-dispatch`) with incremental validation, TDD-by-default cadence, green-checkpoint commits, scope-fence drift detection, and divergence detection that routes back to planning. Build/test/lint, testing, and outcome verification live in the companion `toolchain`, `testing`, and `verification` plugins, invoked when installed.", "author": { "name": "Melodic Software", diff --git a/plugins/implementation/CHANGELOG.md b/plugins/implementation/CHANGELOG.md index afa853991b..4be372925a 100644 --- a/plugins/implementation/CHANGELOG.md +++ b/plugins/implementation/CHANGELOG.md @@ -3,6 +3,45 @@ All notable changes to the `implementation` plugin are documented here. Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/); this plugin uses semantic versioning. +## [0.10.0] + +### Added + +- **Structural capability-tier model bindings at the dispatch seam (`#1649`).** Two new plugin + agents, `implementer` and `phase-verifier`, carry an explicit `model` frontmatter binding of the + strong capability tier's current alias, replacing the default `inherit` that let a fast + orchestrator root silently run implementers (and their verifiers) at orchestrator strength. + `implement-dispatch`'s cadence now dispatches workers as `implementation:implementer` (step 2) + and the phase-boundary verifier as `implementation:phase-verifier`, reserving the per-invocation + `model` parameter for upward (frontier-tier) routing only, with a gotcha documenting that a set + `CLAUDE_CODE_SUBAGENT_MODEL` (any value but `inherit`) outranks the binding. Tier definitions + stay order-defined and family-agnostic per the marketplace's loop-lane convention §3 — the alias + binding lives only at this seam, and the verifier binds never weaker than the implementer it + checks. Frontmatter binds a floor, not a session-relative value, so both the cadence and the + agent bodies record the upward-only override duty when the session's model resolves above the + binding (per the plugin philosophy's session-relative ladder). Each agent also declares an + explicit tool cage — a change from the previously ungoverned generic-subagent tool surface: the + implementer grants file edit, search, shell, web research, skill invocation, and nested dispatch + (that last one conditional — the harness withholds `Agent` from a subagent at the spawn-depth + limit whatever the `tools` list says); the phase-verifier bars Edit/Write and agent spawning, with + Bash retained for inspection (stated as the cage it is, not as "read-only", per the plugin + philosophy's named-agent bar). Both also bind `effort` rather than inheriting the session's level, + matching this marketplace's other named agents — a model binding alone would still let an + orchestrator that lowered effort for its own bookkeeping lower it for the phase work. Neither + agent sets `maxTurns`, unlike every `discovery` and `review` agent in this marketplace (all of + which cap, between 25 and 40). The documented semantics are that the + field is the "maximum number of agentic turns before the subagent stops"; *applied inference,* not + a documented claim: a stop is not a failure signal, so the return an orchestrator receives from a + capped verifier that outran its budget is a truncated report shaped exactly like a verdict. The + phase-verifier is therefore contracted to return INCONCLUSIVE rather than a partial PASS, and + `implement-dispatch`'s phase-boundary clause makes that return re-dispatch a fresh verifier + against the named gap instead of marking the phase `[DONE]`. The implementer is uncapped for the + narrower reason that a phase's length is set by its brief — a real exposure, since its cage grants + edit and shell, and one the brief's scope fence rather than a turn budget is the control for. + (Frontmatter `model` and `effort` values, the env → parameter → frontmatter → inherit resolution + order, the `maxTurns` definition quoted above, and the depth-limit `Agent` withholding verified + against , 2026-07-27.) + ## [0.9.2] ### Changed diff --git a/plugins/implementation/README.md b/plugins/implementation/README.md index 9db6772f52..3a0145a65e 100644 --- a/plugins/implementation/README.md +++ b/plugins/implementation/README.md @@ -11,6 +11,15 @@ verified code. | `/implementation:implement` | Inline execution discipline — mode detection (feature/fix/refactor/config), TDD-by-default cadence, build+test after each logical block, green-checkpoint commits, divergence detection routing back to planning, scope-fence drift detection, phase-boundary handoffs. | | `/implementation:implement-dispatch` | Orchestrated execution variant — composes scope-fenced worker briefs, dispatches subagents, verifies returns against direct evidence, builds main-side, and handles divergence in autonomous runs via a conservative-option deviations log. | +Two plugin agents are the dispatch surface `implement-dispatch` routes through; their `model` +frontmatter structurally binds the capability tier, so workers never silently inherit a fast +orchestrator root's model: + +| Agent | What it does | +|---|---| +| `implementation:implementer` | Scope-fenced worker dispatched per phase; executes exactly one brief in its assigned or self-provisioned worktree. Frontmatter binds the strong tier's current alias. | +| `implementation:phase-verifier` | Fresh-context acceptance verifier dispatched at phase boundaries with the orchestrator's rationale withheld; its tool cage bars Edit/Write and agent spawning (Bash remains for inspection), and it is bound never weaker than the implementer it checks. | + ## Companion stages (separate plugins) Build/test/lint, testing, and outcome verification were split out of this plugin into diff --git a/plugins/implementation/agents/implementer.md b/plugins/implementation/agents/implementer.md new file mode 100644 index 0000000000..336e278121 --- /dev/null +++ b/plugins/implementation/agents/implementer.md @@ -0,0 +1,50 @@ +--- +name: implementer +description: "Scope-fenced implementation worker dispatched per phase by /implementation:implement-dispatch (directly, or chained from callers such as /work-items:work): executes exactly one brief inside its assigned or self-provisioned worktree, commits and pushes early, and returns a verdict plus identifiers. Not intended for direct ad-hoc use." +tools: "Read, Edit, Write, Grep, Glob, Bash, WebFetch, WebSearch, Skill, Agent" +model: opus +effort: high +--- + +You are the implementation worker: a fresh-context subagent an orchestrator dispatches to execute +exactly one scope-fenced brief. You start with no conversation history by design; everything you +need arrives in your dispatch brief, composed per `/implementation:implement-dispatch`'s dispatch +cadence. Refuse to guess anything the brief omits — a missing scope fence, branch name, or +acceptance criterion is a STOP-and-report, never a gap to improvise over. A **worktree path** is +required of an *assigned*-worktree brief only. Under worker-side provisioning the brief carries the +branch name and provisioning instructions in place of a path by design: materializing that worktree +is then your mandated first step, and you discover the path there and return it — never STOP over +its absence. What is never optional is one of the two: a brief that names neither an assigned path +nor provisioning instructions is the omission that STOPs. + +**The brief is the contract.** Its scope fence (ALLOWED/FORBIDDEN files and actions), its +divergence-escalation clause, the project invariants it names, its acceptance criteria, its +worktree/provisioning instructions, and its CI-hygiene clauses govern verbatim. This definition +adds no permissions beyond the brief and never overrides it; when the brief and this file appear to +conflict, STOP and report the conflict. + +The `tools` list above is an explicit cage, stated so it can be audited: file reads and edits, +search, shell, web research (so a consuming project's fresh-docs obligations stay satisfiable), +skill invocation, and nested dispatch for skills that fan out their own workers. Nothing else is +granted — and the nested-dispatch grant is conditional, not absolute: Claude Code withholds `Agent` +from a subagent already at the spawn-depth limit, whatever the `tools` list says, and that subagent +"does its delegated work itself and returns one summary" +(, verified 2026-07-27). So a deeply chained dispatch +fans out nothing; plan the brief's work as your own. + +## Model binding (the dispatch seam) + +The `model` frontmatter above is the structural seam binding of the **strong capability tier** — +the default implementer tier of the order-defined, family-agnostic tier vocabulary owned by the +loop-lane convention (`docs/conventions/loop-lane/README.md` §3 in this plugin's marketplace +repository) — to the current recommended model alias. It exists so a worker never silently inherits +a fast orchestrator root's model. The binding is an alias, never a dated model ID (an alias tracks +the provider's current recommendation; a pinned ID rots), and it is re-audited on any new model +release. Tier *definitions* stay abstract; only this seam binds one to an alias. A dispatching +orchestrator passes a per-invocation `model` only to route a phase **upward** — the frontier tier's +current alias for security-surface work classes, or the session's own model when it resolves above +this binding — never to hand source-editing work to a weaker model than this binding. + +`effort` is bound alongside it for the same reason: it otherwise inherits the session's level, so an +orchestrator that lowered effort for its own bookkeeping would silently lower it for the phase +implementation too. diff --git a/plugins/implementation/agents/phase-verifier.md b/plugins/implementation/agents/phase-verifier.md new file mode 100644 index 0000000000..c30464d961 --- /dev/null +++ b/plugins/implementation/agents/phase-verifier.md @@ -0,0 +1,45 @@ +--- +name: phase-verifier +description: "Fresh-context acceptance verifier dispatched by /implementation:implement-dispatch at phase boundaries: checks a phase's binary acceptance criteria against the actual diff with the orchestrator's rationale withheld, and returns a per-criterion verdict grounded in direct evidence. Its tool cage bars Edit/Write and agent spawning; Bash remains for inspection. Not intended for direct ad-hoc use." +tools: "Read, Grep, Glob, Bash" +model: opus +effort: high +--- + +You are the phase verifier: a fresh-context subagent dispatched at a phase boundary to decide +whether the phase's acceptance criteria are actually satisfied by the diff. You start with no +conversation history, and the orchestrator withholds its rationale **by design** — you audit the +artifact, not the story. Everything you need arrives in your dispatch prompt: the binary acceptance +criteria and how to obtain the diff (a worktree path plus base ref, or the diff itself). Refuse to +guess either. + +Ground every verdict in direct evidence — read the diff, grep the tree, run read-only checks — +never in the plausibility of a claim. Return a per-criterion PASS/FAIL with the evidence for each +FAIL (file, line, observed state), and flag anything in the diff outside the phase's stated scope. +You verify; you never fix. Your tool cage deliberately bars Edit/Write and agent spawning; Bash +remains available for inspection (diffs, greps, read-only checks), and mutating state through it is +outside your contract — a verifier that touches the artifact it grades has voided its verdict. + +**Decide every criterion, or return no verdict.** A return that leaves any criterion undecided is +an INCONCLUSIVE report naming what it could not reach, never a partial PASS — and this definition +deliberately sets no `maxTurns`, because an audit's length is set by the diff, and a turn cap would +stop the verifier mid-audit with no error, leaving a truncated report that reads like a verdict. + +## Model binding (the dispatch seam) + +The `model` frontmatter above is the structural seam binding for this verifier, held to the +loop-lane convention's tier rule (`docs/conventions/loop-lane/README.md` §3 in this plugin's +marketplace repository): **a reviewer or verifier is never weaker than the implementer it checks**. +It therefore binds the same current strong-tier alias as the sibling `implementer` agent — raise +the two together, never independently — as an alias, never a dated model ID, re-audited on any new +model release. Tier *definitions* stay abstract; only this seam binds one to an alias. + +Frontmatter binds a floor-shaped default; it cannot express session-relative raising. The ladder is +relative to the session — a consequential verdict runs at the session-model tier or above, never +below (the marketplace's `docs/PLUGIN-PHILOSOPHY.md` "Model tiers") — so when the dispatching +session's model resolves above this binding, the orchestrator passes a per-invocation `model` at or +above the session tier; that override routes upward only. + +`effort` is bound alongside the model, and for the same reason: it otherwise inherits the session's +level, so an orchestrator that lowered effort for its own bookkeeping would silently lower it for +the acceptance verdict too. diff --git a/plugins/implementation/skills/implement-dispatch/SKILL.md b/plugins/implementation/skills/implement-dispatch/SKILL.md index 73d7b4eccd..d7e39bbb00 100644 --- a/plugins/implementation/skills/implement-dispatch/SKILL.md +++ b/plugins/implementation/skills/implement-dispatch/SKILL.md @@ -42,7 +42,19 @@ Because the orchestrator stays on the default branch, **every source-touching op ## Dispatch cadence (per worker-routed phase) 1. **Compose the brief** — an explicit scope fence (ALLOWED files/actions and FORBIDDEN files/actions, enumerated), a divergence-escalation clause (verbatim in every brief: "if an assumption in this brief proves wrong or the task requires touching anything FORBIDDEN, STOP and report — do not improvise"), the project invariants the task touches (from the consuming project's `CLAUDE.md` / rules), the phase's acceptance criteria, and any model routing the plan specifies. **When the worker edits in a dedicated worktree** (an out-of-tree sibling or any checkout other than the session's default), the brief MUST also give that worktree's absolute path and instruct the worker to never rely on the shell's working directory persisting across separate tool calls — anchor every command that touches the worktree — file edits AND git operations (`status`, `add`, `commit`, `diff`, `log`, everything) — with `git -C ` (or re-`cd` into the path at the start of each call), never a one-time `cd`, since cwd can drift between a read and the next write and silently risks committing into the wrong checkout. **When provisioning is worker-side** (the autonomous work-lane — the orchestrator cannot itself invoke `/source-control:worktree create`, whose `EnterWorktree` terminal would transition the orchestrator's session), the brief instead makes materializing that isolated worktree the worker's **first step** — via `/source-control:worktree`'s non-entering creation seam when installed, or a plain `git worktree add` otherwise — worked via the same `git -C ` anchoring (never entering it). Provisioning happens **once per item, on the first dispatched phase**; the worktree persists across the item's phases, so every **later** phase of the same item is handed that same worktree path and works in it — never re-provisioning the already-checked-out item branch (both `git worktree add -b ` and attaching the branch fail while it is checked out in the persisted worktree). The brief for the first phase also instructs the worker to bring the branch current with the default branch, commit, and push before returning, then **return the worktree's absolute path plus the branch name** so the orchestrator can open the PR against the pushed branch; a worker that cannot provision an isolated worktree STOPs and reports rather than editing the default checkout. The interactive default above — the brief supplies a pre-existing worktree path — is unchanged. The brief MUST also front-load three CI-hygiene clauses: no issue-number back-references in code comments (the `comment-hygiene` check flags them; `TODO(#issue)` is the sanctioned exception); any new regular file with a shebang (never a `120000` symlink — `git update-index --chmod=+x` fails on one) must be marked executable on both the worktree and the index in this order: `chmod +x `, then `git add ` to stage it (a not-yet-tracked path fails `git update-index --chmod=+x` outright — "cannot add to the index" — so the first-time stage MUST happen before the mode override), then `git update-index --chmod=+x ` to force the index mode explicitly since a plain `git add` alone can't be trusted to carry an executable bit across every platform/filesystem (the `exec-bit` check flags a tracked shebang file recorded non-executable); and commit and push as early as practical — before the CI-poll tail — so a mid-flight worker session-limit death never orphans unpushed work; this early commit is a source-only checkpoint, not a substitute for the phase-boundary plan-mark commit, which the orchestrator still runs separately (see Phase boundaries below). PR creation stays out of the brief: it belongs to the orchestrator's post-verification flow (`/implementation:implement` Step 5), invoked only after every worker return is verified and the phase's build/test gate passes -2. **Dispatch** the worker +2. **Dispatch** the worker as this plugin's `implementer` agent (subagent type + `implementation:implementer`). That definition's `model` frontmatter is the structural + capability-tier binding — the strong tier's current alias — so an unqualified dispatch lands on + the intended tier regardless of the orchestrator's own model; never rely on root inheritance, + and never dispatch source-editing work through a generic subagent type. Pass a per-invocation + `model` only to route a phase **upward**: a security-surface work class, or plan-declared + frontier routing, dispatches at the frontier tier's current alias — and a run that cannot + resolve that alias STOPs (autonomously: escalates) rather than dispatching lower — and a session + whose own model resolves above the binding may pass that model. Never pass a `model` that + undercuts the frontmatter binding for source-editing work. (Model resolution order — + `CLAUDE_CODE_SUBAGENT_MODEL` when set to anything but `inherit`, then the per-invocation `model` + parameter, then the definition's `model` frontmatter, then the main conversation's model — per + , verified 2026-07-27.) 3. **Verify the return against direct evidence before accepting edits** — worker returns are synthesis, not ground truth; promote their claims to direct evidence (diff read, grep, file Read) before building on them 4. **Build/test main-side** — invoke `/toolchain:check` from the main window when the `toolchain` plugin is installed, otherwise run the project's own build/test command main-side; never accept a worker's green claim as the build signal. Under worker-side provisioning, run it against the returned worktree (`git -C ` or from that directory), not the orchestrator's default checkout — see the Prerequisites exception 5. **Route worker divergence reports into `/implementation:implement` "Step 3: Divergence Detection"** — a worker STOPping per the divergence-escalation clause is a divergence signal, severity-assessed the same way; the orchestrator revises the brief or routes back to the planning skill (`/planning:plan review` when installed) @@ -55,7 +67,7 @@ In a session with no human to escalate to, stop-and-escalate on Moderate diverge **Ritual unchanged, except the phase-boundary commit's contents.** Every phase boundary runs the `/implementation:implement` "Step 4: Task Tracking and Phase-Boundary Handoff" ritual — plan marks, handoff entry, status summary, mark-then-commit, resume prompt — with one scoped exception: Step 4 item 4 normally combines a phase's source changes and its plan-mark in one commit, but a dispatched worker already committed and pushed its source early (per the push-early clause above) before the orchestrator's acceptance-criteria verdict exists to mark the phase `[DONE]`. In that case the phase-boundary commit is plan-marks-only — the worker's earlier commit already carries the source — rather than the combined single commit inline mode produces. Under worker-side provisioning this plan-mark commit MUST land on the worker's branch, committed in the returned worktree via `git -C ` **and pushed**, never in the orchestrator's default checkout (which would put it on the local default branch, off the PR branch — see the Prerequisites exception). Pushing it is not optional: it keeps the worktree tip in sync with the remote, which `/source-control:pull-request create --pushed`'s HEAD-equals-remote precondition requires, and it keeps tracked plan progress on the PR branch. Orchestration changes who edits and when the source lands, not whether progress gets recorded. -**Fresh-context verifier before marking a phase `[DONE]`:** the Step 4 ritual's acceptance-criteria verdict (item 1) is, in orchestrated runs, *dispatched* rather than rendered inline — send a separate verifier subagent to check the phase's acceptance criteria against the actual diff, handed binary criteria and the diff with your rationale withheld. Where the phase's outcome is high-stakes and correlated blind spots are the risk, prefer a cross-vendor advisor for that verifier **when one is installed and set up** — e.g. the OpenAI Codex plugin, when its documented surface can take this artifact, invoked per its own docs — with the fresh-context same-vendor verifier sub-agent as the stated fallback, never a route to a command that may not resolve. It applies in every mode: autonomous runs MUST; interactive runs MUST for any phase beyond a mechanical, behavior-preserving change. Surface subagent results in the response before ending the turn. +**Fresh-context verifier before marking a phase `[DONE]`:** the Step 4 ritual's acceptance-criteria verdict (item 1) is, in orchestrated runs, *dispatched* rather than rendered inline — dispatch this plugin's `phase-verifier` agent (subagent type `implementation:phase-verifier`; its `model` frontmatter structurally binds the verifier at least as capable as the implementer it checks) to check the phase's acceptance criteria against the actual diff, handed binary criteria and the diff with your rationale withheld. Frontmatter binds a floor, not a session-relative value: a consequential verdict runs at the session-model tier or above, never below (the marketplace's `docs/PLUGIN-PHILOSOPHY.md` "Model tiers"), so when the orchestrating session's model resolves above the binding, pass a per-invocation `model` at or above the session tier — upward only. Where the phase's outcome is high-stakes and correlated blind spots are the risk, prefer a cross-vendor advisor for that verifier **when one is installed and set up** — e.g. the OpenAI Codex plugin, when its documented surface can take this artifact, invoked per its own docs — with the fresh-context same-vendor verifier sub-agent as the stated fallback, never a route to a command that may not resolve. It applies in every mode: autonomous runs MUST; interactive runs MUST for any phase beyond a mechanical, behavior-preserving change. **An `INCONCLUSIVE` return — the `phase-verifier` contract's answer when it could not decide every criterion — is not a verdict:** the phase stays unmarked, and the orchestrator re-dispatches a *fresh* verifier against the gap the return named (narrower criteria, or the specific files it could not reach), never accepting the partial coverage and never marking `[DONE]` on it; a second inconclusive return on the same criteria is an escalation, handled like a divergence report (step 5 above). Surface subagent results in the response before ending the turn. ### Resident-vs-clear at phase boundaries @@ -94,4 +106,5 @@ Any criterion fails → clear + resume from the emitted prompt. **The phase-boun - **New shebang files need `chmod`, then `git add`, then `git update-index --chmod=+x` — in that order.** Brief every worker: `chmod +x `, then `git add ` (a not-yet-tracked file fails `git update-index --chmod=+x` outright — it can't override the index mode of a path that isn't staged yet), then `git update-index --chmod=+x ` to force the index mode explicitly (skip symlinks — staged `120000`, they fail the same command) — a shebang file staged non-executable trips the `exec-bit` check - **Push early, before the CI-poll tail — but never the PR.** Brief every worker to commit and push as early as practical rather than deferring until its fix-and-verify loop is done, so a mid-session death never orphans unpushed work. This is a source-only checkpoint commit — the phase-boundary plan-mark commit (Step 4) still runs separately, orchestrator-side, once the phase's acceptance criteria are verified. PR creation stays out of every worker brief — it happens in the orchestrator's post-verification flow (`/implementation:implement` Step 5) after every return is verified and the build/test gate passes - **Scope-fence drift applies to agent returns.** Every worker return is a decision boundary — classify proposed follow-ups per `/implementation:implement` "Step 3.5: Scope-fence drift detector (run at every decision boundary)" before announcing them +- **The capability-tier binding lives in agent frontmatter — don't undercut it.** Workers dispatch as `implementation:implementer` and phase verifiers as `implementation:phase-verifier`; a generic subagent type inherits the orchestrator's model, which under a fast orchestrator root silently runs implementers at orchestrator strength. A per-invocation `model` routes only upward (frontier-alias for security-surface work, or the session's own higher tier); and a `CLAUDE_CODE_SUBAGENT_MODEL` environment variable set to anything but `inherit` outranks even the frontmatter binding — keep it unset for orchestrated runs - **An omitted `--wave-cap` keeps the internal 3–5 — never coerce an absent value into a number.** Only cap at `N` when the caller passed a real positive integer; a missing, empty, or unresolved-placeholder argument means "use the internal default," not `0` and not a hard `1` diff --git a/plugins/work-items/.claude-plugin/plugin.json b/plugins/work-items/.claude-plugin/plugin.json index 017fe4eb5b..02d3809e44 100644 --- a/plugins/work-items/.claude-plugin/plugin.json +++ b/plugins/work-items/.claude-plugin/plugin.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/claude-code-plugin-manifest.json", "name": "work-items", - "version": "0.29.0", + "version": "0.30.0", "description": "Manages development work items through a provider-neutral tracker seam that ships with the plugin (bundled dispatcher plus github and local-markdown adapters; seam plugin-dir canonical, adapters consumer-local-first): dashboard, taxonomy-labeled creation, a race-safe assignee-plus-lease claim protocol, recurring-schedule checks, TODO scanning, stale-lease auditing, plan decomposition into vertical-slice items, raw-intake triage (issues and unsolicited PRs through raw, verified, briefed, autonomous-eligible states), plus the two work-items loop lanes of the loop-lane convention: a self-paced autonomous work-loop drain (work-class admission gate, adaptive item cap, PR-only) and an attended attend-queue escalation lane. The re-runnable setup skill binds the provider (.work-item-tracker.json), seeds the recurring-schedule seam (.github/recurring-schedule.json), and remaps canonical role labels.", "author": { "name": "Melodic Software", diff --git a/plugins/work-items/CHANGELOG.md b/plugins/work-items/CHANGELOG.md index fbf822091d..b7b8fa56cb 100644 --- a/plugins/work-items/CHANGELOG.md +++ b/plugins/work-items/CHANGELOG.md @@ -3,6 +3,23 @@ All notable changes to the `work-items` plugin are documented here. Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/); this plugin uses semantic versioning. +## [0.30.0] + +### Changed + +- **`work` rides the structural capability-tier binding for every source-touching dispatch + (`#1649`).** The Step 5 execute chain now names the model-tier enforcement carried by + `implementation:implement-dispatch`'s new `implementer` / `phase-verifier` agent frontmatter, and + the branch-owned fix re-dispatches into the persisted worktree dispatch + `implementation:implementer` when the `implementation` plugin is installed (when absent, an + explicit per-invocation strong-tier alias — never inheritance of the orchestrator's model). The + PR-monitor and post-green review-pass dispatches into the persisted worktree likewise carry an + explicit per-invocation `model` now — fast-tier alias for the mechanical watch, no weaker than + the implementer binding for the review pass, since a reviewer is never weaker than the + implementer it checks. A fast-tier lane root no longer silently determines implementer strength, + which is what let a `sonnet` root run every implementer as `sonnet` despite the loop-lane tier + vocabulary. + ## [0.29.0] ### Added diff --git a/plugins/work-items/skills/work/SKILL.md b/plugins/work-items/skills/work/SKILL.md index d753a147ee..e72fe3c5df 100644 --- a/plugins/work-items/skills/work/SKILL.md +++ b/plugins/work-items/skills/work/SKILL.md @@ -217,7 +217,7 @@ On user confirmation ("yes"): 1. **Suggest branch name.** Propose `/-` so `/pull-request create` can auto-inject `Closes #N` from the branch parse. Same protocol as the `/work-items:track start` action's branch-name step ([`${CLAUDE_PLUGIN_ROOT}/skills/track/actions/start.md`](${CLAUDE_PLUGIN_ROOT}/skills/track/actions/start.md) "Suggest branch name") — branch `` vocabulary derived from the item's issue type (native Issue Type preferred, `type:*` label fallback), slug from title (kebab-case, 40-char cap), existing-branch detection, multi-claim 3-option (switch / stay+cover-both / skip). Agent emits `git checkout -b ...` for the user; never executes itself. Under autonomous worker-side provisioning the orchestrator instead carries the resolved name into the dispatch brief and the worker creates the branch when it provisions its worktree (below) — the orchestrator still never creates the branch itself. -1. **Execute — orchestrator-dispatch is the default (`#451`).** For autonomous execution the default posture is orchestrator, not inline editor: this skill picks and claims the item, then **dispatches a scope-fenced implementation subagent** that does the source edits in its **own out-of-tree worktree** (lifecycle owned by `/source-control:worktree`, one per pick), collects the return, verifies it, and does the bookkeeping. **The orchestrator never edits source itself.** All dispatch *mechanics* — worker-brief composition, orchestrator-never-edits, verify-returns-against-evidence, and the concurrent-wave cap — are owned by `/implementation:implement-dispatch`; chain to it rather than re-describing them here. An interactive, all-inline run instead uses `/implementation:implement`. Whichever path runs, the executing surface MUST follow every step of the consuming project's development workflow (a workflow skill, a `CLAUDE.md` workflow section, or team convention) and read the project's rules for the item's domain first — no shortcuts, no skipping research, no surface-level execution; dispatch is only *how* that workflow is carried out. The **lane shape** that execution composes — the fixed lane set, the implementer ≠ reviewer ≠ verifier invariant, and the depth tiers by which an item's lanes are to be scaled — is defined once in [`${CLAUDE_PLUGIN_ROOT}/reference/pipeline-shape.md`](${CLAUDE_PLUGIN_ROOT}/reference/pipeline-shape.md); the dispatched chain runs that shape *within* the consumer's workflow and rules, never in place of them. **Autonomous branch/worktree provisioning is worker-side (`#572`).** An autonomous run reaches a non-default branch/worktree *before* the dispatch preflight by making provisioning the dispatched worker's own first step: the worker materializes an isolated out-of-tree worktree — through `/source-control:worktree`'s non-entering creation seam when the `source-control` plugin is installed (that skill owns naming, placement, and cleanup conventions), or a plain `git worktree add` otherwise — and works against it via `git -C ` **without entering it**, per `/implementation:implement-dispatch`'s worktree-cwd contract. The branch name is the one the *Suggest branch name* sub-step above resolved, carried in the dispatch brief; the worker attaches it to the worktree with `git worktree add -b ` for a **new** branch, or `git worktree add ` (no `-b`) when that sub-step already detected the branch as **existing** — `-b` fails outright on an existing branch, including one the user created by following that sub-step — so the `Closes #N` the name encodes reaches the orchestrator's PR. The orchestrator never invokes `/source-control:worktree create` itself: that action's `EnterWorktree` terminal would transition the orchestrator's own session and end its ability to keep orchestrating. The worker commits, pushes, and brings the branch current with the default branch *before returning*, then returns the worktree's absolute path plus the branch name; a worker that cannot provision an isolated worktree parks the item and escalates for operator-provided branch setup rather than editing the default checkout. PR creation is **not** the worker's — the orchestrator opens it (see the orchestrator-owned PR step below). +1. **Execute — orchestrator-dispatch is the default (`#451`).** For autonomous execution the default posture is orchestrator, not inline editor: this skill picks and claims the item, then **dispatches a scope-fenced implementation subagent** that does the source edits in its **own out-of-tree worktree** (lifecycle owned by `/source-control:worktree`, one per pick), collects the return, verifies it, and does the bookkeeping. **The orchestrator never edits source itself.** All dispatch *mechanics* — worker-brief composition, orchestrator-never-edits, verify-returns-against-evidence, and the concurrent-wave cap — are owned by `/implementation:implement-dispatch`; chain to it rather than re-describing them here. That chain also carries the **capability-tier model binding structurally**: `/implementation:implement-dispatch` dispatches workers and phase verifiers as its plugin's `implementer` / `phase-verifier` agents, whose `model` frontmatter binds the strong tier's current alias — so when the `implementation` plugin is installed, an autonomous lane root running a fast-tier model never silently leaks its own model into implementers, and no per-dispatch model admonition is needed for the tiers that seam already enforces. When that plugin is absent, the seam is absent too: every source-touching or judgment-rendering dispatch then carries an explicit per-invocation `model` resolving the strong tier's current alias, never the lane root's inherited model. An interactive, all-inline run instead uses `/implementation:implement`. Whichever path runs, the executing surface MUST follow every step of the consuming project's development workflow (a workflow skill, a `CLAUDE.md` workflow section, or team convention) and read the project's rules for the item's domain first — no shortcuts, no skipping research, no surface-level execution; dispatch is only *how* that workflow is carried out. The **lane shape** that execution composes — the fixed lane set, the implementer ≠ reviewer ≠ verifier invariant, and the depth tiers by which an item's lanes are to be scaled — is defined once in [`${CLAUDE_PLUGIN_ROOT}/reference/pipeline-shape.md`](${CLAUDE_PLUGIN_ROOT}/reference/pipeline-shape.md); the dispatched chain runs that shape *within* the consumer's workflow and rules, never in place of them. **Autonomous branch/worktree provisioning is worker-side (`#572`).** An autonomous run reaches a non-default branch/worktree *before* the dispatch preflight by making provisioning the dispatched worker's own first step: the worker materializes an isolated out-of-tree worktree — through `/source-control:worktree`'s non-entering creation seam when the `source-control` plugin is installed (that skill owns naming, placement, and cleanup conventions), or a plain `git worktree add` otherwise — and works against it via `git -C ` **without entering it**, per `/implementation:implement-dispatch`'s worktree-cwd contract. The branch name is the one the *Suggest branch name* sub-step above resolved, carried in the dispatch brief; the worker attaches it to the worktree with `git worktree add -b ` for a **new** branch, or `git worktree add ` (no `-b`) when that sub-step already detected the branch as **existing** — `-b` fails outright on an existing branch, including one the user created by following that sub-step — so the `Closes #N` the name encodes reaches the orchestrator's PR. The orchestrator never invokes `/source-control:worktree create` itself: that action's `EnterWorktree` terminal would transition the orchestrator's own session and end its ability to keep orchestrating. The worker commits, pushes, and brings the branch current with the default branch *before returning*, then returns the worktree's absolute path plus the branch name; a worker that cannot provision an isolated worktree parks the item and escalates for operator-provided branch setup rather than editing the default checkout. PR creation is **not** the worker's — the orchestrator opens it (see the orchestrator-owned PR step below). **The dispatch brief carries the PR contract forward (`#462`).** So a worker knows the target up front instead of discovering it through red CI, the brief relays what `/source-control:pull-request` will require at PR time — that skill owns the PR body shape (including its configurable required-section scaffold, `pr_body_required_sections` — see [`config-resolution.md`](https://raw.githubusercontent.com/melodic-software/claude-code-plugins/main/plugins/source-control/reference/config-resolution.md)), the `Closes #N` closing-keyword injection, and merge style; do **not** redefine them here. The brief enumerates the consuming-project obligations the worker must satisfy: per-plugin version bump plus the matching CHANGELOG entry, and the attribution trailer plus session link — alongside the `Closes #N` the branch name carries. A `## Related` entry is not a standing obligation here (`/source-control:pull-request`'s scaffold no longer includes it by default); it becomes one only via the deferred-finding path below, which owns ensuring the section exists. @@ -225,7 +225,7 @@ On user confirmation ("yes"): 1. **High-blast-radius diff gate (pre-PR).** Before a PR is opened, the orchestrator does a **full-diff read** when the diff touches skill frontmatter descriptions or trigger keywords, cross-plugin contracts, or hooks — read against the worker's returned worktree (`git -C `), since the orchestrator's own default-branch checkout does not contain the worker's changes. This complements the worker scope-fence: the scope-fence bounds what a worker *may* touch, this gate is the orchestrator's own read of what the worker *did* touch before the change leaves the lane. -1. **Open the PR — orchestrator-owned (`#572`).** After the worker returns and the pre-PR diff gate passes, the **orchestrator** opens the PR — it is never the worker's to open (opening it from a worker would make the pre-PR gate a no-op, and `/implementation:implement-dispatch` already keeps PR creation out of every worker brief). The worker committed and pushed inside its own worktree, so the orchestrator invokes the **PR-only entry** `/source-control:pull-request create --pushed --worktree ` — passing that path explicitly, since the orchestrator stays in its own (default-branch) checkout and the mode needs the path to resolve the worker's branch and diff rather than the orchestrator's. That mode re-resolves branch and diff from the target worktree and skips the commit/push/rebase steps the normal `create` runs, while `/source-control:pull-request` stays the SSOT for the PR body shape, the `Closes #N` closing-keyword injection, the required-section gate, and merge style. **Detection lives here:** when the consuming project's own development workflow already owns a PR stage, the orchestrator defers to it instead of invoking `create --pushed`; otherwise the orchestrator opens the PR. After the PR is open, monitoring (Phase 3) and the post-green review pass run **from the persisted worktree**, not the orchestrator's default-branch session: `/source-control:pull-request monitor` resolves the PR by bare `gh pr view` and requires its watch to run in the session that owns the PR branch, which the orchestrator's own checkout is not — so the orchestrator dispatches these into the persisted worktree (which stays checked out on the PR branch) and passes the **PR number explicitly**. Any **branch-owned fix** (a failing check or a review finding) is applied by re-dispatching a **fresh scope-fenced subagent into that same persisted worktree** (`git -C `) — never by the orchestrator editing source, and a fresh scoped brief rather than resuming the original worker because the worktree, not the subagent, is the state carrier across dispatches. +1. **Open the PR — orchestrator-owned (`#572`).** After the worker returns and the pre-PR diff gate passes, the **orchestrator** opens the PR — it is never the worker's to open (opening it from a worker would make the pre-PR gate a no-op, and `/implementation:implement-dispatch` already keeps PR creation out of every worker brief). The worker committed and pushed inside its own worktree, so the orchestrator invokes the **PR-only entry** `/source-control:pull-request create --pushed --worktree ` — passing that path explicitly, since the orchestrator stays in its own (default-branch) checkout and the mode needs the path to resolve the worker's branch and diff rather than the orchestrator's. That mode re-resolves branch and diff from the target worktree and skips the commit/push/rebase steps the normal `create` runs, while `/source-control:pull-request` stays the SSOT for the PR body shape, the `Closes #N` closing-keyword injection, the required-section gate, and merge style. **Detection lives here:** when the consuming project's own development workflow already owns a PR stage, the orchestrator defers to it instead of invoking `create --pushed`; otherwise the orchestrator opens the PR. After the PR is open, monitoring (Phase 3) and the post-green review pass run **from the persisted worktree**, not the orchestrator's default-branch session: `/source-control:pull-request monitor` resolves the PR by bare `gh pr view` and requires its watch to run in the session that owns the PR branch, which the orchestrator's own checkout is not — so the orchestrator dispatches these into the persisted worktree (which stays checked out on the PR branch) and passes the **PR number explicitly**. Both of those dispatches carry an explicit per-invocation `model`, never the lane root's inherited one: the monitor watch is mechanical and may run at the fast capability tier's current alias, while the post-green review pass renders judgment on an implementer's diff and so dispatches no weaker than the implementer binding — the strong tier's current alias, resolved at runtime as an alias per the loop-lane convention's tier rules (a reviewer is never weaker than the implementer it checks). Any **branch-owned fix** (a failing check or a review finding) is applied by re-dispatching a **fresh scope-fenced subagent into that same persisted worktree** (`git -C `) — never by the orchestrator editing source, and a fresh scoped brief rather than resuming the original worker because the worktree, not the subagent, is the state carrier across dispatches. Dispatch that fix worker as `implementation:implementer` when the `implementation` plugin is installed — its agent frontmatter carries the strong-tier model binding, so the fix lands on the same tier as the original implementation; when that plugin is absent, pass an explicit per-invocation `model` resolving the strong tier's current alias rather than letting the fix worker inherit the orchestrator's model. 1. **Post-green review pass, then hand off.** After CI is green, run one review pass. The fetch-once → validate → classify → threaded-reply → react → resolve-bot-thread loop is owned by `/source-control:pull-request`; this skill adds only the sequencing and the work-item linkage: fix branch-owned findings via the same **fresh-subagent-into-the-persisted-worktree** re-dispatch the orchestrator-owned PR step defines (the orchestrator still never edits source), and a **VALID-but-deferred finding requires a filed follow-up issue** — file it via `/work-items:track add` following the shared self-observation contract ([`${CLAUDE_PLUGIN_ROOT}/reference/dogfood-filing.md`](${CLAUDE_PLUGIN_ROOT}/reference/dogfood-filing.md): dedupe → categorize → fixed shape → `needs-triage`), then cite that issue **both** in the classification reply **and** in the PR's `## Related` section — **ensure the section exists first**: `/source-control:pull-request`'s scaffold carries `## Related` only when the repo's `pr_body_required_sections` requires it or a genuine reference already populated it at create time, so a deferred finding is frequently the first content that section ever holds. Adding it is a **read-modify-write**, never a bare `--body` replacement — `gh pr edit --body`/`--body-file` REPLACES the whole body (the same identity note the GitHub adapter's [PR closing-keyword mechanics](../../tools/work-item-tracker/adapters/github/README.md) documents for its own body edit), so read the current body first (`gh pr view --json body --jq '.body'`), append the `## Related` section (or its content, if the section already exists) to that read, and write the combined result back via `--body-file -`; a bare `gh pr edit --body "## Related\n..."` would silently drop `Closes #N`, Summary, and Test plan. A deferred finding cannot be resolved without it. Then hand the PR off to `/source-control:babysit-prs` (fleet loop, owned there). diff --git a/prompts/loops/loop-lane-prompts.md b/prompts/loops/loop-lane-prompts.md index 4d57eb411e..861558c071 100644 --- a/prompts/loops/loop-lane-prompts.md +++ b/prompts/loops/loop-lane-prompts.md @@ -342,26 +342,32 @@ IDs — the alias tracks the current recommended model and a pinned ID rots. only: 200k context and an old cutoff, never for a question about current harness behavior. -**A `sonnet` root silently makes every implementer `sonnet` too.** The -`model` frontmatter field defaults to `inherit`, and neither `/work-items:work` -nor `implement-dispatch` sets one, so subagents dispatched from a -`--model sonnet` root run Sonnet — not the strong tier this section promises. -The three tiers above are aspirational unless something overrides that -inheritance. Resolution order is: `CLAUDE_CODE_SUBAGENT_MODEL`, then the -per-invocation `model` parameter, then frontmatter, then the main -conversation's model -(, verified 2026-07-25). - -Two ways to make the tiering real. Prefer the per-dispatch instruction — -`CLAUDE_CODE_SUBAGENT_MODEL` is a blunt floor that would also pull mechanical -greps up off `haiku`: - -- **Per-dispatch (recommended).** The lane bodies below carry a standing - dispatch-model rule. Because `/loop` re-sends the prompt each iteration, - that rule survives compaction — a rule stated once in conversation does not. -- **Environment floor.** Export `CLAUDE_CODE_SUBAGENT_MODEL=opus` for that - lane's shell. It wins over everything, including a deliberate `haiku` - per-dispatch choice, so it costs the fast tier entirely. +**The implementer tier is enforced structurally at the dispatch seam +(#1649).** `/implementation:implement-dispatch` dispatches workers and +phase verifiers as the `implementation` plugin's `implementer` / +`phase-verifier` agents, whose `model` frontmatter binds the strong tier's +current alias — so a `sonnet` worker-lane root no longer makes every +implementer `sonnet`, and `/work-items:work`'s branch-owned fix +re-dispatches ride the same agent surface. Resolution order is: +`CLAUDE_CODE_SUBAGENT_MODEL`, then the per-invocation `model` parameter, +then frontmatter, then the main conversation's model +(, verified 2026-07-27). + +Two consequences of that order: + +- **Overrides remain per-dispatch duties.** The frontmatter binds only the + default: security-surface work classes and conflict workers still take an + explicit per-invocation frontier-alias override, and mechanical greps and + log pulls still take an explicit `haiku`. The worker-lane bodies below + carry only those overrides — not a per-dispatch binding for the tiers the + seam already enforces. The merge-lane bodies additionally keep their + `opus` binding for CI fixes, review-comment work, and judgment calls: + babysit dispatches do not route through `implement-dispatch`, so no + frontmatter seam covers them. +- **Never export `CLAUDE_CODE_SUBAGENT_MODEL` for a lane** (any value other + than `inherit`, which resolution treats as unset). It outranks the + frontmatter bindings and every deliberate per-dispatch override alike, + flattening the fast and frontier tiers onto one model. ```bash claude --model sonnet # worker lane @@ -446,13 +452,17 @@ no shared state, no contention, and the sharding problem disappears. > worktree, where an edit belongs. Posture and process corrections that touch > no file apply normally. > -> **Dispatch model, every dispatch.** Your root runs on the fast tier and -> subagents inherit it by default, so an unqualified dispatch silently runs -> an implementer at orchestrator strength. Pass an explicit per-invocation -> `model` on every dispatch: `opus` for anything that reads or edits source, -> writes a PR, or makes a judgment call; `fable` for conflict resolution and -> any security-surface work class, unconditionally; `haiku` only for -> mechanical greps and log pulls. Never leave it to inherit. +> **Dispatch model — overrides only.** Implementer and phase-verifier +> dispatches land on the strong tier structurally: the `implementation` +> plugin's `implementer` / `phase-verifier` agent definitions carry the +> binding in `model` frontmatter, so pass no `model` for those and never +> one that undercuts the binding. Pass an explicit per-invocation `model` +> only for the exceptions the seam does not carry: `fable` for conflict +> resolution and any security-surface work class, unconditionally; `opus` +> for a judgment-call dispatch that does not ride the implementer surface; +> `haiku` only for mechanical greps and log pulls. Never export +> `CLAUDE_CODE_SUBAGENT_MODEL` — it silently outranks the bindings and +> every deliberate override alike. > > **Return contract, every subagent, every depth.** Return at most two > lines: a verdict token and an identifier or path. Everything else goes @@ -1293,13 +1303,17 @@ machines; neither on the attended box. > worktree, where an edit belongs. Posture and process corrections that touch > no file apply normally. > -> **Dispatch model, every dispatch.** Your root runs on the fast tier and -> subagents inherit it by default, so an unqualified dispatch silently runs -> an implementer at orchestrator strength. Pass an explicit per-invocation -> `model` on every dispatch: `opus` for anything that reads or edits source, -> writes a PR, or makes a judgment call; `fable` for conflict resolution and -> any security-surface work class, unconditionally; `haiku` only for -> mechanical greps and log pulls. Never leave it to inherit. +> **Dispatch model — overrides only.** Implementer and phase-verifier +> dispatches land on the strong tier structurally: the `implementation` +> plugin's `implementer` / `phase-verifier` agent definitions carry the +> binding in `model` frontmatter, so pass no `model` for those and never +> one that undercuts the binding. Pass an explicit per-invocation `model` +> only for the exceptions the seam does not carry: `fable` for conflict +> resolution and any security-surface work class, unconditionally; `opus` +> for a judgment-call dispatch that does not ride the implementer surface; +> `haiku` only for mechanical greps and log pulls. Never export +> `CLAUDE_CODE_SUBAGENT_MODEL` — it silently outranks the bindings and +> every deliberate override alike. > > **Return contract, every subagent, every depth.** Return at most two > lines: a verdict token and an identifier or path. Everything else goes