diff --git a/plugins/implementation/.claude-plugin/plugin.json b/plugins/implementation/.claude-plugin/plugin.json index fd451bddac..98b0a89409 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.19.0", + "version": "0.19.1", "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 95a59fcb3b..3a0c796abc 100644 --- a/plugins/implementation/CHANGELOG.md +++ b/plugins/implementation/CHANGELOG.md @@ -3,6 +3,20 @@ 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.19.1] - 2026-09-27 + +### Fixed + +- **`implement-dispatch` defines the wave, and one git writer per worktree binds under both + commit authorities.** A wave is a batch of one phase's worker rows, each an independent brief + with its own disjoint fence, and `--wave-cap` bounds the rows in flight at once; waves never + span phases. 0.18.0 added commit authority `orchestrator` with its single-committer + Concurrency rule (#4511), but that rule bound only under `orchestrator`. Under the default + `worker` authority, rows that share a worktree now run one at a time whatever the cap, since a + worktree has one index and one HEAD; concurrent rows in a shared worktree need `orchestrator`. + The wave-cap eval now runs its rows under `orchestrator`, and a new eval covers two `worker` + rows sharing a worktree (#4262). + ## [0.19.0] - 2026-09-27 ### Changed diff --git a/plugins/implementation/skills/implement-dispatch/SKILL.md b/plugins/implementation/skills/implement-dispatch/SKILL.md index 507157e55e..8bde3b2316 100644 --- a/plugins/implementation/skills/implement-dispatch/SKILL.md +++ b/plugins/implementation/skills/implement-dispatch/SKILL.md @@ -16,7 +16,7 @@ Structural variant of `/implementation:implement` for orchestrated execution: th **Orchestration mode detection**. Infer autonomous vs interactive from the session shape: a goal/loop harness driving turns with no human in the cycle, a plan that declares itself autonomous-ready, or an explicit orchestration instruction means **autonomous**; a human reviewing each turn means **interactive**. -**Autonomous:** the main window is orchestrator only. Dispatch workers per phase; orchestrated cadence is the **default** even when the plan's routing is all-main-window (synthesize per-phase worker rows from the plan). Cap concurrent dispatch waves at 3–5 workers by default; when the caller passes `--wave-cap ` (see Arguments), for example `/work-items:work` threading its `work_dispatch_concurrency_cap`, cap at that `N` instead of the internal 3–5. The parameter is the single enforcement point for a caller-configured concurrency ceiling; omitting it keeps the internal default, so existing callers are unaffected. +**Autonomous:** the main window is orchestrator only. Dispatch workers per phase; orchestrated cadence is the **default** even when the plan's routing is all-main-window (synthesize per-phase worker rows from the plan). A **wave** is a batch of one phase's worker rows dispatched together: each row is an independent brief with its own disjoint fence, waves never span phases (see Arguments), and a phase with more rows than the cap runs them as sequential waves. Cap each wave at 3–5 concurrent worker rows by default; when the caller passes `--wave-cap ` (see Arguments), for example `/work-items:work` threading its `work_dispatch_concurrency_cap`, cap at that `N` instead of the internal 3–5. The parameter is the single enforcement point for a caller-configured concurrency ceiling; omitting it keeps the internal default, so existing callers are unaffected. Whatever the cap, one git writer per worktree binds under both commit authorities (see Commit authority), so under the default `worker` authority rows that share a worktree run one at a time. **Interactive:** read the plan's execution-shape/routing table. Worker rows present (any surface other than main-window) → this skill's dispatch cadence for those phases. Routing table absent or all main-window → `/implementation:implement` classic inline cadence instead. @@ -28,7 +28,7 @@ Structural variant of `/implementation:implement` for orchestrated execution: th The **phase selector** (e.g. `phase-2`) scopes the dispatch cadence to that plan phase only. Otherwise walk the remaining plan phases strictly in order. Never dispatch a later worker-routed phase past an incomplete earlier phase: dispatch each worker-routed phase as it becomes current; in interactive mode, at the first inline-routed phase hand back by invoking `/implementation:implement` via the Skill tool (classic cadence) and re-enter here when a later worker-routed phase becomes current. Under autonomous mode every remaining phase dispatches in order, synthesizing worker rows per the Autonomous rule above when the routing table lacks them. -`--wave-cap `. An optional positive-integer ceiling on **concurrent dispatch waves**. When passed, it replaces the internal 3–5 default (see the Autonomous rule); when omitted, the internal default stands. This is how a chaining caller threads a configured concurrency cap in. `/work-items:work` passes its resolved `${user_config.work_dispatch_concurrency_cap}` here, and passes nothing when that key is unset so the internal default applies. Waves are discrete: floor a fractional argument to `⌊N⌋` (e.g. `2.5` → `2`) and treat `< 1` as `1`, so a stray non-integer never produces a fractional or zero cap. +`--wave-cap `. An optional positive-integer ceiling on **worker rows in flight at once within one phase**, the size of a wave. When passed, it replaces the internal 3–5 default (see the Autonomous rule); when omitted, the internal default stands. This is how a chaining caller threads a configured concurrency cap in. `/work-items:work` passes its resolved `${user_config.work_dispatch_concurrency_cap}` here, and passes nothing when that key is unset so the internal default applies. Rows are discrete: floor a fractional argument to `⌊N⌋` (e.g. `2.5` → `2`) and treat `< 1` as `1`, so a stray non-integer never produces a fractional or zero cap. ## Prerequisites (before any dispatch) @@ -66,8 +66,11 @@ Because the orchestrator stays on the default branch, **every source-touching op `CLAUDE_CODE_SUBAGENT_MODEL` when set to a model alias or id, then the main conversation's model, per , verified 2026-09-11. Recheck when a release note touches subagent model selection.) - Dispatch a wave and keep working while it runs: verify returns from the same phase as they - arrive, compose the next brief, and run the build/test gate on accepted returns. Intervene when + Dispatch a wave, up to the cap's worker rows from the current phase, and keep working while it + runs: verify returns from the same phase as they arrive, compose the next brief, and run the + build/test gate on accepted returns. Under commit authority `worker` each row stages and + commits, so a wave in a shared worktree is one row; more than one concurrent row in a shared + worktree needs commit authority `orchestrator` and its Concurrency rule. Intervene when a worker goes off track or is missing context. Do not block on the slowest worker before starting orchestrator-side work that does not depend on it. 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. Under commit authority `orchestrator` the return is an uncommitted tree; read it as Commit authority describes @@ -78,13 +81,15 @@ Because the orchestrator stays on the default branch, **every source-touching op Every brief states **commit authority**: `worker` (the default, and what an absent field means, so existing callers are unchanged) or `orchestrator`. Declare `orchestrator` when the plan's worker fence forbids staging, committing, or pushing, when the orchestrator owns a commit-subject gate, or when the plan has a push-once rule. Write the field into the brief; the worker never infers it from a fence. A worker handed a fence that forbids those writes with no declared mode STOPs and reports the conflict, and a fence that forbids only a narrow action (a force-push, opening the PR) leaves the mode at `worker`. A no-commit plan therefore needs no fenced generic subagent: the implementer honors the mode and keeps its tier binding. +**One git writer per worktree, under either authority.** A worktree has one index and one HEAD, and git writes the index under an exclusive lock, so two workers staging or committing in one worktree at once either fail on the lock or commit each other's paths. Under `worker`, where every worker stages and commits, run at most one worker per worktree at a time: rows that share the item's worktree dispatch one per wave, whatever `--wave-cap` allows. Concurrent rows in one shared worktree need `orchestrator`, where the orchestrator is the only git writer (see Concurrency below). This skill provisions no per-row worktrees; a phase whose rows must run concurrently under `worker` is a plan question, not a dispatch-time split. (A linked worktree is linked to its repository "sharing everything except per-worktree files such as HEAD, index", per , verified 2026-09-27. Recheck when a git release note changes per-worktree state.) + Under `orchestrator`: - **Worktree.** The brief carries an assigned worktree path. Worker-side provisioning cannot combine with this mode, because a provisioning worker must commit and push before returning; a brief asking for both makes the worker STOP. The orchestrator creates the worktree itself (a non-entering `git worktree add`, or the project's own tool) and hands over the path. - **Brief.** Omit the commit-and-push-early clause, and shrink the exec-bit clause to `chmod +x ` plus listing the file in the return. The worker never runs `git add`, `git commit`, `git push`, `git stash`, or any other index or ref write; it returns `git -C status --porcelain --untracked-files=all` output in place of a commit sha. - **Verification.** Return verification (step 3) reads the uncommitted tree with `git -C status --porcelain --untracked-files=all`, `git -C diff HEAD`, and `git -C ls-files --others --exclude-standard`; a plain `git diff` misses untracked files. The build/test gate (step 4) runs on that tree. The phase-verifier gets the worktree path plus the base ref and is told the changes are uncommitted, so it reads untracked files with `status --porcelain --untracked-files=all` or `ls-files --others --exclude-standard` as well as `git diff `, or gets the diff itself: `diff HEAD` output plus the content of every file `ls-files --others --exclude-standard` lists (plain `status --porcelain` collapses a new directory to one entry). - **Commit.** The orchestrator commits in the assigned worktree via `git -C `, never in its own checkout, at the phase boundary: source and plan marks in one commit (see Phase boundaries), under the project's commit convention and gate, staging each listed shebang file in the order the Gotchas bullet "New shebang files need `chmod`" gives. It pushes per the plan's push rule, and as under `worker` when the plan states none. Commit as soon as the phase is accepted: until then the work exists only on local disk. When the commit gate is one only the user can pass, follow `/implementation:implement` Step 4 item 4: complete the plan marks and handoff first, then hand the commit to the user. -- **Concurrency.** Run one worker per worktree at a time. When several must share a worktree, give them disjoint fences, let none stage, attribute returned paths by fence, and run the build/test gate only after the wave settles. +- **Concurrency.** The orchestrator is the only git writer, so this is the one mode where several rows may share a worktree. Prefer one worker per worktree at a time. When several must share one, up to the wave cap, give them disjoint fences, let none stage, attribute returned paths by fence, and run the build/test gate only after the wave settles. ## Divergence in non-interactive runs @@ -146,7 +151,7 @@ Which way the boundary goes decides its ritual (see Phase boundaries): a clear g - **No issue-number back-references in code comments.** Brief every worker that a comment citing an issue number (`# Issue #NNN ...`, `(issue #NNN obs #N)`) trips the `comment-hygiene` check; `TODO(#issue)` is the sanctioned exception - **New shebang files need `chmod`, then `git add`, then `git update-index --chmod=+x`. In that order.** Brief every `worker`-authority worker (for `orchestrator`, see Commit authority): `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, since a plain `git add` alone can't be trusted to carry an executable bit across every platform and filesystem (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`-authority 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. Commit authority `orchestrator` forgoes the early push: uncommitted work lives only on local disk until the orchestrator commits. -- **Shared-worktree workers under `orchestrator` need disjoint fences.** See Commit authority +- **One git writer per worktree, under both commit authorities.** Under `worker`, rows that share a worktree run one at a time even when `--wave-cap` allows more; concurrent rows in a shared worktree need `orchestrator`, with disjoint fences. See Commit authority - **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). `CLAUDE_CODE_SUBAGENT_MODEL` ranks below both the per-invocation parameter and the frontmatter, so it cannot undercut the binding; it decides only where neither is set, which is the generic-subagent case this bullet already rules out - **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/implementation/skills/implement-dispatch/evals/evals.json b/plugins/implementation/skills/implement-dispatch/evals/evals.json index 1f424b67f2..2c7a59eb21 100644 --- a/plugins/implementation/skills/implement-dispatch/evals/evals.json +++ b/plugins/implementation/skills/implement-dispatch/evals/evals.json @@ -80,14 +80,15 @@ { "id": 7, "name": "wave-cap-argument-overrides-internal-default", - "prompt": "/implementation:implement-dispatch --wave-cap 2 — autonomous run, execute the approved plan; a phase routes eight parallelizable worker rows.", - "expected_output": "The passed --wave-cap 2 replaces the internal 3–5 default: concurrent dispatch waves are capped at 2 workers, so the eight rows run in waves of at most 2 rather than 3–5. With no --wave-cap the internal 3–5 default would stand instead; the argument is the single enforcement point for a caller-configured concurrency ceiling and does not otherwise change the dispatch cadence. A fractional cap (e.g. --wave-cap 2.5) is floored to whole waves (2), since a wave is discrete, and a value below 1 is treated as 1 — never a fractional or zero cap.", + "prompt": "/implementation:implement-dispatch --wave-cap 2 — autonomous run, execute the approved plan; a phase routes eight parallelizable worker rows with disjoint fences, and the plan declares commit authority orchestrator in one assigned worktree.", + "expected_output": "The passed --wave-cap 2 replaces the internal 3–5 default: a wave is a batch of that phase's worker rows, so at most 2 rows are in flight at once and the eight rows run as sequential waves of at most 2 rather than 3–5. The rows can share the assigned worktree only because the orchestrator is its only git writer; they keep disjoint fences, none stages, and the build/test gate runs after each wave settles. With no --wave-cap the internal 3–5 default would stand instead; the argument is the single enforcement point for a caller-configured concurrency ceiling and does not otherwise change the dispatch cadence. A fractional cap (e.g. --wave-cap 2.5) is floored to whole rows (2), and a value below 1 is treated as 1 — never a fractional or zero cap.", "files": [], "expectations": [ - "Honors --wave-cap 2 as the concurrent-wave ceiling, capping at 2 workers rather than the internal 3–5", - "Runs the eight rows in waves bounded by the passed cap instead of the internal default", + "Honors --wave-cap 2 as the ceiling on worker rows in flight at once within the phase, capping at 2 rather than the internal 3–5", + "Runs the eight rows as sequential waves of at most 2 within the one phase, never starting a later phase's rows alongside them", + "Shares the assigned worktree only under the declared orchestrator authority: disjoint fences, no worker stages, and the build/test gate runs after each wave settles", "Treats the argument as the only change — the rest of the dispatch cadence (scope-fenced briefs, verify-returns, main-side build) is unchanged", - "Floors a fractional cap to whole waves and treats a value below 1 as 1 — never a fractional or zero concurrent-wave cap" + "Floors a fractional cap to whole rows and treats a value below 1 as 1 — never a fractional or zero cap" ] }, { @@ -103,6 +104,19 @@ "The orchestrator commits source and plan marks in one phase-boundary commit under its commit-subject gate and does not push before the plan's single push", "Does NOT treat a fence forbidding only a narrow action (for example force-push or opening the PR) as switching commit authority to orchestrator" ] + }, + { + "id": 9, + "name": "worker-authority-rows-sharing-a-worktree-run-one-at-a-time", + "prompt": "/implementation:implement-dispatch phase-2 --wave-cap 3 — autonomous run; phase 2 routes two independent worker rows with disjoint fences, both editing in the item's one persisted worktree, and no brief declares a commit authority.", + "expected_output": "Commit authority is worker, the default for an absent field, so each row stages, commits, and pushes early in the shared worktree. One git writer per worktree binds under both authorities: the two rows dispatch one at a time (a wave of one in that worktree) even though --wave-cap 3 would allow three, with the second dispatched after the first returns. Running them concurrently would need commit authority orchestrator declared in both briefs, where the orchestrator is the only git writer. The skill does not split the worktree or provision a second one to get parallelism.", + "files": [], + "expectations": [ + "Treats the absent commit-authority field as worker, and does not dispatch the two rows concurrently into the one shared worktree", + "Explains that --wave-cap 3 does not lift the one-writer-per-worktree rule: under worker, rows sharing a worktree run one per wave", + "Names commit authority orchestrator, declared in the briefs rather than inferred, as the only way to run both rows at once in that worktree", + "Does NOT provision a per-row worktree or split the phase's briefs to manufacture parallelism" + ] } ] } diff --git a/plugins/work-items/.claude-plugin/plugin.json b/plugins/work-items/.claude-plugin/plugin.json index c8aa9cf037..939b3c6a16 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.40.31", + "version": "0.40.32", "description": "Manages development work items through a provider-neutral tracker seam that ships with the plugin (bundled dispatcher plus github, local-markdown, jira, gitea, and linear 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, a macro-journey router over spec containers (rollup, per-container execution shape, next-step routing), 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", @@ -32,7 +32,7 @@ "work_dispatch_concurrency_cap": { "type": "number", "title": "Autonomous dispatch concurrency cap", - "description": "Maximum concurrent dispatch waves /work-items:work's autonomous execute step allows per invocation (it runs exactly one item per invocation). Give a whole number of waves; a fractional value is floored to whole waves since a wave is discrete. When set, /work-items:work threads it into /implementation:implement-dispatch as that skill's --wave-cap ceiling. Leave unset to let implement-dispatch apply its own internal 3-5 wave default. This key declares no default, so an unset value stays distinguishable from a configured one (which a declared default would collapse into a hard cap).", + "description": "Maximum worker rows /work-items:work's autonomous execute step lets /implementation:implement-dispatch run at once within one plan phase, the size of one dispatch wave (it runs exactly one item per invocation). Give a whole number of rows; a fractional value is floored since a row is discrete. Rows that share a worktree under the default worker commit authority still run one at a time. When set, /work-items:work threads it into /implementation:implement-dispatch as that skill's --wave-cap ceiling. Leave unset to let implement-dispatch apply its own internal 3-5 wave default. This key declares no default, so an unset value stays distinguishable from a configured one (which a declared default would collapse into a hard cap).", "min": 1 }, "work_loop_item_cap_start": { diff --git a/plugins/work-items/CHANGELOG.md b/plugins/work-items/CHANGELOG.md index 94b2e11c37..8eb89f96a7 100644 --- a/plugins/work-items/CHANGELOG.md +++ b/plugins/work-items/CHANGELOG.md @@ -3,6 +3,15 @@ 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.40.32] - 2026-09-27 + +### Changed + +- The `work_dispatch_concurrency_cap` description, the README, and `work` name what the cap bounds: + the worker rows of one plan phase that `/implementation:implement-dispatch` runs at once, one + dispatch wave, in place of "concurrent dispatch waves". Rows that share a worktree under the + default `worker` commit authority still run one at a time, per implementation 0.19.1 (#4262). + ## [0.40.31] - 2026-09-27 ### Fixed diff --git a/plugins/work-items/README.md b/plugins/work-items/README.md index ed25e85e06..917fc3f1cf 100644 --- a/plugins/work-items/README.md +++ b/plugins/work-items/README.md @@ -138,8 +138,8 @@ enough that one skill no longer predicts its contents. ## Configuration -`work_dispatch_concurrency_cap` caps the concurrent dispatch waves autonomous -`/work-items:work` allows per item. When set, `/work-items:work` threads it into +`work_dispatch_concurrency_cap` caps how many worker rows of one plan phase, one +dispatch wave, autonomous `/work-items:work` runs at once per item. When set, `/work-items:work` threads it into `/implementation:implement-dispatch` as that skill's `--wave-cap` ceiling; left unset (its default state, the key declares no manifest default), it lets `/implementation:implement-dispatch` apply its own internal 3–5 wave default. @@ -180,7 +180,7 @@ reads it from. | --- | --- | --- | --- | --- | | `lane_instance` | string | *(none)* | `CLAUDE_PLUGIN_OPTION_LANE_INSTANCE` | Writer identity for this machine's loop-lane telemetry, per the loop-lane convention's lane-instance identity rule. It becomes the suffix of the lane's telemetry sentinel marker (`work-items:work-loop@`), so each concurrently running lane instance owns its own comment and none can overwrite another's durable state, including first_drain_complete, whose loss would end one machine's earn-trust ratification gate because a different machine finished a drain. Must match ^\[a-z0-9\]\[a-z0-9-\]{0,31}$, be stable across restarts, and be distinct across concurrent instances; two lanes on one machine each need an explicit value. Absent: the sanitized lowercased hostname. The value appears verbatim in tracker comments. Set an opaque id if a machine name should not be published in a public tracker. | | `decompose_container_publish` | boolean | *(none)* | `CLAUDE_PLUGIN_OPTION_DECOMPOSE_CONTAINER_PUBLISH` | When true, /work-items:decompose pre-selects the spec-container offer in its approval round for multi-session breakdowns (the Brief published as a container item carrying the binding-resolved container label, default work-map, with slices as native sub-items). The approval gate itself is unchanged and mandatory. This key changes the offered default answer, never bypasses approval. Leave unset (or false) for the default plain ask with a default answer of no; this key declares no default so an unset value stays distinguishable from a configured one. | -| `work_dispatch_concurrency_cap` | number
*min 1* | *(none)* | `CLAUDE_PLUGIN_OPTION_WORK_DISPATCH_CONCURRENCY_CAP` | Maximum concurrent dispatch waves /work-items:work's autonomous execute step allows per invocation (it runs exactly one item per invocation). Give a whole number of waves; a fractional value is floored to whole waves since a wave is discrete. When set, /work-items:work threads it into /implementation:implement-dispatch as that skill's --wave-cap ceiling. Leave unset to let implement-dispatch apply its own internal 3-5 wave default. This key declares no default, so an unset value stays distinguishable from a configured one (which a declared default would collapse into a hard cap). | +| `work_dispatch_concurrency_cap` | number
*min 1* | *(none)* | `CLAUDE_PLUGIN_OPTION_WORK_DISPATCH_CONCURRENCY_CAP` | Maximum worker rows /work-items:work's autonomous execute step lets /implementation:implement-dispatch run at once within one plan phase, the size of one dispatch wave (it runs exactly one item per invocation). Give a whole number of rows; a fractional value is floored since a row is discrete. Rows that share a worktree under the default worker commit authority still run one at a time. When set, /work-items:work threads it into /implementation:implement-dispatch as that skill's --wave-cap ceiling. Leave unset to let implement-dispatch apply its own internal 3-5 wave default. This key declares no default, so an unset value stays distinguishable from a configured one (which a declared default would collapse into a hard cap). | | `work_loop_item_cap_start` | number
*min 1* | `2` | `CLAUDE_PLUGIN_OPTION_WORK_LOOP_ITEM_CAP_START` | Where the work-loop lane's adaptive per-cycle item cap starts. The cap ramps up by one after three consecutive clean items (never while a rate-limit warning is latched) and drops by one on any dirty item; enforcement is the loop body's own arithmetic. | | `work_loop_item_cap_ceiling` | number
*min 1* | `3` | `CLAUDE_PLUGIN_OPTION_WORK_LOOP_ITEM_CAP_CEILING` | Upper bound the work-loop lane's adaptive item cap can ramp to for non-frontier-tier items. Frontier-tier items are bounded separately by work_loop_frontier_item_cap_ceiling. | | `work_loop_item_cap_floor` | number
*min 1* | `1` | `CLAUDE_PLUGIN_OPTION_WORK_LOOP_ITEM_CAP_FLOOR` | Lower bound the work-loop lane's adaptive item cap can drop to on dirty items. | diff --git a/plugins/work-items/skills/work/SKILL.md b/plugins/work-items/skills/work/SKILL.md index 9b85e8ec39..64eae75cbd 100644 --- a/plugins/work-items/skills/work/SKILL.md +++ b/plugins/work-items/skills/work/SKILL.md @@ -206,7 +206,7 @@ On user confirmation ("yes"): **The dispatch brief carries the PR contract forward.** 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, **and** the live `lease_comment_id` plus the mid-flight `renew-lease` duty above. A `## Related` entry is not a standing obligation here (`/source-control:pull-request`'s scaffold carries it only when the repo requires it); it becomes one only via the deferred-finding path below, which owns ensuring the section exists. - **The dispatch concurrency cap is configured via `userConfig`, never a hardcoded literal.** `${user_config.work_dispatch_concurrency_cap}` resolves to the operator's value when set; when the key is unset (it declares no manifest default) it renders as a literal `${user_config.…}` placeholder, the same unset render the sibling `work_loop_item_cap_*` keys rely on, or, defensively, an empty value. When it resolves to a positive number, the orchestrator threads it into the delegated `/implementation:implement-dispatch` dispatch as that skill's `--wave-cap `, capping concurrent dispatch waves at that value; any other render, a surviving placeholder or an empty value, both meaning unset, passes **no** `--wave-cap`, so `/implementation:implement-dispatch` applies its own internal 3–5 wave default (that skill owns the wave-cap mechanics; chain to it rather than re-describing them here). That single parameter is the cap's enforcement, so never coerce an unset placeholder or empty value into a number. **Waves are discrete, so a fractional cap is floored to a whole number before it becomes the argument**. Pass `⌊value⌋` (e.g. `1.5` → `1`), never below the manifest's `min` of `1`: the manifest `type` is `number` (the userConfig schema has no integer type), so a non-whole value is possible, and flooring keeps the operator's ceiling conservative rather than rounding up past their intent. Never fall back to the internal 3–5 default on a fractional value, that would silently *raise* concurrency above the operator's lower ceiling. **A per-cycle item budget is not this skill's concern:** `work` selects and executes exactly one item per invocation, so it has no cycle to bound; the autonomous per-cycle item budget lives in the driving loop, the `work-loop` lane's adaptive item cap (`work_loop_item_cap_*`), enforced by the loop body's own arithmetic. **Same-plugin serialization is not enforced.** Treat two in-flight items in the same plugin as an awareness note: prefer not to dispatch a second concurrently, since their diffs and version/CHANGELOG bumps can collide. + **The dispatch concurrency cap is configured via `userConfig`, never a hardcoded literal.** `${user_config.work_dispatch_concurrency_cap}` resolves to the operator's value when set; when the key is unset (it declares no manifest default) it renders as a literal `${user_config.…}` placeholder, the same unset render the sibling `work_loop_item_cap_*` keys rely on, or, defensively, an empty value. When it resolves to a positive number, the orchestrator threads it into the delegated `/implementation:implement-dispatch` dispatch as that skill's `--wave-cap `, capping the worker rows one dispatch wave runs at once at that value; any other render, a surviving placeholder or an empty value, both meaning unset, passes **no** `--wave-cap`, so `/implementation:implement-dispatch` applies its own internal 3–5 wave default (that skill owns the wave-cap mechanics; chain to it rather than re-describing them here). That single parameter is the cap's enforcement, so never coerce an unset placeholder or empty value into a number. **Rows are discrete, so a fractional cap is floored to a whole number before it becomes the argument**. Pass `⌊value⌋` (e.g. `1.5` → `1`), never below the manifest's `min` of `1`: the manifest `type` is `number` (the userConfig schema has no integer type), so a non-whole value is possible, and flooring keeps the operator's ceiling conservative rather than rounding up past their intent. Never fall back to the internal 3–5 default on a fractional value, that would silently *raise* concurrency above the operator's lower ceiling. **A per-cycle item budget is not this skill's concern:** `work` selects and executes exactly one item per invocation, so it has no cycle to bound; the autonomous per-cycle item budget lives in the driving loop, the `work-loop` lane's adaptive item cap (`work_loop_item_cap_*`), enforced by the loop body's own arithmetic. **Same-plugin serialization is not enforced.** Treat two in-flight items in the same plugin as an awareness note: prefer not to dispatch a second concurrently, since their diffs and version/CHANGELOG bumps can collide. 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.