pi-crew 0.11.24 → 0.11.25

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (105) hide show
  1. package/CHANGELOG.md +61 -0
  2. package/README.md +2 -2
  3. package/agents/designer.md +23 -0
  4. package/agents/orchestrator.md +17 -0
  5. package/dist/index.mjs +568 -559
  6. package/{workflows/chain.workflow.md → docs/chain-workflow.md} +8 -0
  7. package/docs/dynamic-workflows.md +23 -0
  8. package/docs/publishing.md +34 -5
  9. package/docs/troubleshooting.md +50 -2
  10. package/docs/usage.md +7 -2
  11. package/install.mjs +2 -1
  12. package/package.json +4 -2
  13. package/skills/read-only-explorer/SKILL.md +15 -5
  14. package/skills/real-test-pi-crew/SKILL.md +7 -7
  15. package/src/config/config-validation.ts +58 -0
  16. package/src/config/config.ts +90 -5
  17. package/src/config/env-vars.ts +14 -7
  18. package/src/config/role-tools.ts +7 -5
  19. package/src/config/types.ts +4 -1
  20. package/src/extension/crew-cleanup.ts +6 -0
  21. package/src/extension/project-init.ts +19 -1
  22. package/src/extension/register.ts +68 -30
  23. package/src/extension/registration/artifact-cleanup.ts +52 -11
  24. package/src/extension/registration/commands/manage.ts +9 -2
  25. package/src/extension/registration/hook-registration.ts +3 -0
  26. package/src/extension/registration/lazy-configurers.ts +2 -0
  27. package/src/extension/registration/lifecycle-handlers.ts +79 -18
  28. package/src/extension/registration/render-loop.ts +27 -2
  29. package/src/extension/registration/tool-loop-guard.ts +5 -3
  30. package/src/extension/registration/verify-gate-tool.ts +7 -3
  31. package/src/extension/team-tool/api/read.ts +12 -3
  32. package/src/extension/team-tool/cancel.ts +11 -0
  33. package/src/extension/team-tool/config-patch.ts +56 -11
  34. package/src/extension/team-tool/destructive-gate.ts +72 -12
  35. package/src/extension/team-tool/dispatch/manage.ts +9 -1
  36. package/src/extension/team-tool/handle-schedule.ts +38 -4
  37. package/src/extension/team-tool/handle-settings.ts +76 -6
  38. package/src/extension/team-tool/health-monitor.ts +14 -2
  39. package/src/extension/team-tool/lifecycle-actions.ts +28 -10
  40. package/src/extension/team-tool/respond.ts +12 -5
  41. package/src/extension/team-tool/run-deadline.ts +29 -2
  42. package/src/extension/team-tool/run.ts +11 -0
  43. package/src/extension/team-tool/status.ts +64 -8
  44. package/src/extension/team-tool/workflow-manage.ts +5 -1
  45. package/src/extension/team-tool.ts +3 -0
  46. package/src/i18n.ts +18 -14
  47. package/src/prompt/inbox-poll.ts +14 -2
  48. package/src/prompt/message-tool.ts +1 -1
  49. package/src/prompt/prompt-runtime.ts +25 -3
  50. package/src/prompt/scratchpad-lifecycle.ts +14 -13
  51. package/src/prompt/surface-worker.ts +35 -9
  52. package/src/runtime/broker/crew-broker.ts +113 -111
  53. package/src/runtime/broker/delegate/delegate-abort.ts +118 -0
  54. package/src/runtime/broker/delegate/delegate-event.ts +3 -1
  55. package/src/runtime/broker/delegate/delegate-finalize.ts +179 -0
  56. package/src/runtime/broker/protocol/wait-resolve.ts +71 -5
  57. package/src/runtime/budget-enforcement.ts +26 -6
  58. package/src/runtime/chain-parser.ts +5 -3
  59. package/src/runtime/chain-runner.ts +41 -22
  60. package/src/runtime/child-pi/mock-fixtures.ts +12 -5
  61. package/src/runtime/compaction/tool-output-pruner.ts +8 -3
  62. package/src/runtime/delegate-spawn.ts +61 -8
  63. package/src/runtime/dispatch-batch.ts +10 -4
  64. package/src/runtime/effectiveness.ts +31 -3
  65. package/src/runtime/live-session/live-agent-control.ts +89 -6
  66. package/src/runtime/live-session/live-agent-manager.ts +94 -10
  67. package/src/runtime/live-session/live-session-runtime.ts +9 -2
  68. package/src/runtime/model/model-fallback.ts +6 -9
  69. package/src/runtime/model/pi-args.ts +3 -3
  70. package/src/runtime/model/runtime-policy.ts +4 -2
  71. package/src/runtime/model/runtime-resolver.ts +6 -2
  72. package/src/runtime/process/orphan-reaper.ts +143 -0
  73. package/src/runtime/process-status.ts +38 -16
  74. package/src/runtime/resilient-edit.ts +170 -93
  75. package/src/runtime/role-permission.ts +3 -1
  76. package/src/runtime/run-tracker.ts +9 -1
  77. package/src/runtime/sanitize/worker-text.ts +98 -0
  78. package/src/runtime/scheduling/run-coalesced-task-group.ts +82 -28
  79. package/src/runtime/spawn-policy.ts +190 -8
  80. package/src/runtime/stale-reconciler.ts +36 -1
  81. package/src/runtime/subagent-manager.ts +29 -0
  82. package/src/runtime/surface/herdr-provider.ts +31 -1
  83. package/src/runtime/task-output-context.ts +58 -11
  84. package/src/runtime/task-runner/post-execution.ts +144 -0
  85. package/src/runtime/task-runner/prompt-builder.ts +41 -8
  86. package/src/runtime/task-runner.ts +16 -3
  87. package/src/runtime/verification/classifier-pre-gate.ts +27 -6
  88. package/src/runtime/verification/verify-gate.ts +153 -30
  89. package/src/state/atomic-write.ts +73 -4
  90. package/src/state/contracts.ts +7 -0
  91. package/src/state/coordination/mailbox.ts +40 -11
  92. package/src/state/event-log/event-log-rotation.ts +28 -6
  93. package/src/state/event-log/poison-marker.ts +16 -1
  94. package/src/state/stores/artifact-store.ts +181 -6
  95. package/src/state/stores/manifest-io.ts +126 -8
  96. package/src/state/stores/state-store.ts +30 -31
  97. package/src/state/types.ts +13 -0
  98. package/src/state/usage.ts +41 -0
  99. package/src/ui/run-dashboard.ts +3 -3
  100. package/src/ui/settings-overlay.ts +19 -1
  101. package/src/utils/internal-error.ts +5 -1
  102. package/src/utils/timer-limits.ts +30 -0
  103. package/src/utils/token-counter.ts +47 -0
  104. package/src/workflows/discover-workflows.ts +9 -1
  105. package/src/worktree/worktree-manager.ts +128 -18
package/CHANGELOG.md CHANGED
@@ -2,6 +2,63 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ### Fixed (live battery 2026-10-18 — real-test v0.11.24)
6
+
7
+ - **fix(gate): RT24-F1 root-gate config/autonomy read over-block** — `rootGateBlockReason` counted the `args` subcommand carrier as a config patch key, so worker-origin `config {args:"show"}` READS were rejected as "writes user-scope config" (live-caught in the v0.11.24 real-test battery; docblock promised reads open). `args` added to the carrier-key sets for `config` and `autonomy`; unknown junk keys stay write-classified (fail-closed). Pinned: reads with carrier keys open, writes hidden behind the same key still gated (`unset`/patch keys), user config byte-untouched. Live-verified end-to-end (fresh `pi -p`, worker env): read allowed + dumps config; `unset` write gate-blocked verbatim "root-session required".
8
+
9
+ ## [0.11.25] — Fix-wave 6: deep-review R1-R51 close-out (security cluster, state/verification families, doc tail) (2026-10-19)
10
+
11
+ Close-out wave for the 51-round / ~150-finding deep review recorded in
12
+ `pi-crew-deep-review-2026-10-16.md` (R51 evidence-hardening: 24/24 hardest-class
13
+ findings CONFIRMED, 0 refuted). ~100 work items across 4 milestones, each
14
+ gated on `typecheck` + `test:critical` (124/124) with mutation-pins
15
+ (revert-fix → test RED) on every security item.
16
+
17
+ ### Fixed (M1 — HIGH security cluster, 8 items)
18
+
19
+ - **fix(config): R24-H1/H2 proto-pollution at the parse boundary** — drop `__proto__`/`constructor`/`prototype` keys in the shared patch/merge helper (config patch, team-settings set/unset, autonomy, i18n `addTranslations` R46-7) instead of mirroring the handler predicate. `team-settings set __proto__.polluted` → clean; `unset __proto__.toString` → rejected. 5 mutation-pins RED.
20
+ - **fix(security): R11-F1/F2 + R11-S1 scope-carrier + workflow-* root-gate** — gate and handlers read the SAME scope source (carrier-key `scope`) with a gate-vs-handler parity matrix for every scope-carrying action; `workflow-create/update/delete/save/import/export` now route through the root-gate (previously ungated). Planted `~/.pi/agent/agents/evil.md` blocked on both paths; worker-origin `workflow-create` blocked. 5/3/1 RED.
21
+ - **fix(prompt): R38-C1 chain-executor worker-text fencing** — worker text fenced (strip ANSI, mark provenance, escape fence) before entering the privileged session.
22
+ - **fix(security): R43-S1 delegate role-tier containment is now TRANSITIVE** — grandchild admission = min(parent tier, requested role tier) via `roleToolPolicy` threading; a read-only parent can no longer spawn a write-capable `executor` grandchild at depth ≥ 2.
23
+ - **fix(artifacts): R44-N1 prune containment** — `unlink` only when the resolved path is under runDir/artifacts (realpath + prefix), blocking `..`/absolute-outside deletion; regression probe (victim file survives).
24
+ - **fix(verify): R47-1 verify-gate ground truth is host-owned** — check commands come from the frozen manifest spec, never re-derived from the worker-mutable task-cwd `package.json`; mismatch → INCONCLUSIVE → verifier spawns (fail-closed). Replay: tampered `scripts.typecheck` now FAILs the gate instead of deterministic PASS + verifier-skip.
25
+ - **fix(config): R45-S2 run-param tier honours the sensitive-drop + kill-switch** — `params.config` can no longer set `executeWorkers:true` over an explicit user `executeWorkers:false`, nor bypass otlp/webhook/policy drops.
26
+
27
+ ### Fixed (M2 — family epics: one change per member-family)
28
+
29
+ - **fix(prompt/ui/broker): EP1 unfenced-worker-text rollout (≥6 IDs: R4-B1, R11-F2/S3, R31-S1, R38-C1, R46-1, R2-M-B3)** — one `fenceWorkerText()` helper applied at every interpolation of worker text into privileged/orchestrator output (status/dashboard/transcript/tool-result); status-line forgery probe blocked. 5-red/2-red pins.
30
+ - **fix(effectiveness): EP2 provenance-tagged gate-skip (R9-F3 + R47-V7, complementary halves)** — synthesized gate-skip results are tagged `synthetic` and excluded from observable-activity accounting while genuine `delegate.completed` counts; a forged gate PASS is no longer certified "effective".
31
+ - **fix(observability): EP3 read-fault vs corruption + orphan/progress truth (R39-M1/M2/M3, R48-N1/N2/N10a)** — `EACCES`/`EMFILE` no longer quarantine a valid manifest (only SyntaxError does); zombie-sync orphan detection drops the magic-string heuristic; the reaper reclaims PID-1 orphans; transient write errors degrade instead of killing the host. 2-red/1-red/1-red pins.
32
+ - **fix(state): EP4 event-log compaction generation bump (R32-M1 ≡ R4-N1/FS10)** — compaction increments `.gen` so readers cache-miss instead of reading a stale prefix. 3-red pin.
33
+
34
+ ### Fixed (M3 — MED cluster, 28 items)
35
+
36
+ - **fix(trust-chain): R47-2/V3 fence-escape scrub** — failed-check output entering the bash-capable verifier prompt is now scrubbed/escaped (SEC-4 parity at the dependency seam).
37
+ - **fix(verify): R47-3/V4 `PASS:`/`FAIL:` verdict is a real consumer** — a `FAIL:` verdict now fails the task instead of completing green; `PASS:` is required for "verified".
38
+ - **fix(budget/broker): R43-N1..N4 delegate budget made real** — `tokensGranted` seeded at grant time (unbudgeted delegate refused), delegate spend counted broker-side (not self-reported), terminal signals propagate to grandchildren, grandchild results no longer dropped.
39
+ - **fix(scheduler/state): R21-F1 scoped waitState clear · R22-F2 scheduler persist-in-lock · R23-F1 terminal-write clobber guard on every late path · R33-1 group fan-out dedupe by (group, recipient) · R34 one token unit across 3 call sites.**
40
+ - **fix(lifecycle): R41-1 persistent control-request dedupe (no replay after crash/resume) · R41-4 eviction disposes + aborts with a deadline before removing the record.**
41
+ - **fix(config): R45-F1 `updateConfig` preserves unknown on-disk keys (merge, not replace-whitelist) · R46-7 proto-safe `addTranslations` via the shared helper.**
42
+ - **fix(core-api/robustness): R49-F1 resilient-edit reads a real tool seam (phantom keys gone) · R47-V2/V4adj dead classifier-pre-gate mount removed · R36-1/2 worktree untracked-file artifact + add-failure isolation · R37-1 timeout clamp · R14-1 chain `parallel()` partial-fail visible · R15-1 renderTick async/cached tasks read (bench 0.84→0.014 ms) · R13-1/2 init/cleanup safety · R26-1/2 `prepublishOnly` + real env-vars gate (24 env-param sites migrated) · R18-1/2/3 CI-gate fixes (lazy-import depth-1, template emits, `.js` suffix) · R19-M1 macOS reader · R12-* herdr watcher dispose + caps · R31-1 `AgentOverridesSubmenu.onApply` wired · R28-2 phantom `chain.workflow.md` steps relocated.**
43
+
44
+ ### Changed (M4 — LOW/doc tail, ~60 items: docs follow code)
45
+
46
+ - **docs: R27 doc-drift ×8** — trust-model v1.2 (DWF gate scope, cold-verifier isolation, telemetry census), `README`/`ui-samples` Node floor `≥ 22.19.0`, README action count 55→57, `publishing.md` test counts (97/120→124) + current config path, `usage.md` `widgetPlacement` default `bottom`, `/team-cleanup` removed (unregistered), `dynamic-workflows.md` default-deny `PI_CREW_TRUST_PROJECT_DWF=1` documented, `read-only-explorer` SKILL tool-availability note (explorer has no `bash`), `CLAUDE.md` Runtime Modes table gains `auto`.
47
+ - **docs: R45-F2/F3/F4** — `autoRepairIntervalMs` comment 60s→300s; both `EFFECTIVE_DEFAULTS` maps report `telemetry.enabled` default ON; `PI_CREW_DEBUG`/`PI_TEAMS_DEBUG` raw-truthiness documented (`=0` still enables → unset to disable) in the registry + troubleshooting.
48
+ - **docs: R48 doctor false-negative catalogue + telemetry egress census** — anomaly-detection matrix in `failure-mode-inventory.md`, operator guidance + mux (R42) sharp edges in `troubleshooting.md`.
49
+ - **docs: R26/R29 packaging notes** — shipped `skills/**/__pycache__`, dev tooling in the tarball, consumer-side `.githooks` referencing unpublished scripts, unbounded `pi-coding-agent` peer, committed-dist requirement.
50
+ - **docs: R28-2/R28-3** — designer/orchestrator capability contradictions documented in-body (the frontmatter-tools de-advertise vs `WRITE_ROLES` decision is left open, see below).
51
+ - **comments (R49-F3/F4/F6/F7, R17-minor):** phantom `pi-core/i18n/*` channels, `pi.on()` never-throws (real degradation = never-fire), crew-cleanup switch-flag vs reason-gate divergence, phantom `ctx.workspaceFolder`, `task.verifier_pre_gate` registration now landed.
52
+ - **docs: R17 event-types CI note** — `docs/TEST_MATRIX.md` records that the LOCAL `check:event-types` is report-only while `ci.yml` runs `--enforce`.
53
+
54
+ ### Known residuals (declared, not hidden)
55
+
56
+ - `agents/designer.md` / `agents/orchestrator.md` still advertise tool capabilities their permission class denies (`edit`/`write`/`bash` / `delegate`). Both fixes are behaviour-affecting (WRITE_ROLES entry — code — or frontmatter de-advertise — resource metadata); documented in-body pending sign-off.
57
+ - `check:event-types` LOCAL wiring still lacks `--enforce` (one line; verified safe at HEAD).
58
+ - `pi-coding-agent` peer remains unbounded (R49-F2).
59
+
60
+ ## [0.11.24] — Deep review waves 2-4: terminal-escape injection, broker replay/overflow, state read-error wipes (2026-10-18)
61
+
5
62
  ### Fixed (deep review 2026-10-16/17 — Waves 2-4; report `pi-crew-deep-review-2026-10-16.md`, 128/128 cold-verified)
6
63
 
7
64
  - **fix(state): FS2 + R5-M-B4 + family-2 read-error wipes** — readCrewAgents/plan-store/ownership-map/active-run-registry/orphan-worker-registry no longer collapse non-ENOENT read faults to empty feeding a locked full overwrite (sentinel + skip-save + loud log; ST-4-style guards).
@@ -13,6 +70,8 @@
13
70
  - **fix(reliability — Wave 4):** R9-F3 effectiveness counts delegate.completed; R9-OPS2 stale /tmp pc-*.sock sweep (24h); spawnedRunIds pruned; tmux-watcher/BatchBarrier disposal; cron scan hoisted+hopped (no 527k-iteration stall); prune digest head-2/tail-2 fallback for all tools; herdr 10s request timeout + error-ack → mux-dead; oscillation period-2 detection; parked-ask shield derives from clamped deadline (+ broker stamps manifest.waitState.deadline); purgeStaleActiveRunIndex isIntentionalWait + waitState clear; rpcPool overwrite guard; FS6 liveness-before-exactly-once; FS7 fallback attempt enters accounting; FS8 writer-timeout terminates wedged worker; client per-request timer unref'd; steer.push requestId dedupe; missed-cron one-time skipped event.
14
71
  - **fix(hardening one-liners):** TMPDIR in child env allowlists (macOS run-lock dual-guard actually contends); escalate `to` validated; deadletter assertSafePathId; PI_TEAMS_PI_BIN path-separator boundary; submit_result error-result on throw; launch-script env-key regex; quoted `command -v` probe; classifier failureSummary redacted; tool_execution args/supervisor_contact compacted + 64KiB line cap; env-vars numeric defaults route through parsers; mock-seam double-gated; sqlite 0600; theme atomic write + no corrupt-wipe; OTLP CUMULATIVE temporality + header precedence; goal-wrap spawn-failure → blocked (no zombie goal); CI-only concurrency clamp; sweep-test-tmp age guard; watch-bundle recursion; temp-root realpath both sides; agent frontmatter ceilings; non-finite heartbeat = corrupt lock; agent-events cursor seq-sort; settings enums derived from schema (+ drift test); mailbox seam sanitize; R10-C1 duplicate-condition fix.
15
72
 
73
+ ## [0.11.23] — Deep review wave 1: state-loss HIGHs (tasks.json orphan reconcile, coalesced dispatch, agents.json requeue) + broker delegate param validation (2026-10-17)
74
+
16
75
  ### Fixed (deep review 2026-10-16/17 — Wave 1; report `pi-crew-deep-review-2026-10-16.md`, 128/128 cold-verified)
17
76
 
18
77
  - **fix(recovery): R2-H1 — orphan reconcile persisted the FILTERED zombie subset** (tasks.json data loss, runtime-proven). `/tmp` orphan reconcile wrote `result.repairedTasks` (stale-only subset) via full-overwrite — every sibling record destroyed on a timer tick. Now persists `result.persistTasks ?? repairedTasks` (mirror of `crash-recovery.ts`); caller retention pinning test (3-task fixture: alive + zombie + completed all retained).
@@ -24,6 +83,8 @@
24
83
  - **fix(event-log): FS1 + FS3**. FS1: agent-events `.seq` sidecar trusted with no regression guard — crash-window re-mints duplicate seqs that `> sinceSeq` consumers drop forever; now max(sidecar, tail-scan) like EL-1. FS3: cursor `nextSeq=max-delivered` + file-order slice stranded lower-seq events forever on documented non-monotonic files (field-confirmed in 7 runs); `filtered` is now seq-sorted before the limit slice (2 sites); the 2 file-order test pins updated to seq-order contract.
25
84
  - **fix(run): R5-M-B3 — 4 bare `void appendEventAsync` in `run.ts`** (probe-proven host crash via unhandledRejection, exit=7); all now `.catch`-logged (17 other sites already were).
26
85
 
86
+ ## [0.11.22] — DR-01 false-green P0 + rounds 4-7 audit waves (DR-02/DR-03/DR-04) (2026-10-16)
87
+
27
88
  ### Fixed (rounds 4–7 audit waves — DR-02/DR-03/DR-04, reports `docs/reviews/pi-crew-audit-round{4,5,6,7}-*.md`)
28
89
 
29
90
  - **fix(state): DR-03 C1 — disk-terminal status preserved + delegate allocation max-merge** (R6-1 HIGH). `persistSingleTaskUpdate` reloaded disk inside the lock but `updateTask` replaced by id UNCONDITIONALLY — a detached runner's late settle overwrote a crash-recovery `cancelled` task with `completed` and emitted `task.completed` after `crew.run.orphan_cancelled`. New `mergeTaskUpdateWithDisk()` preserves a differing disk-terminal status (result fields still recorded); post-execution derives the persisted status from the persist return (terminal events via `TERMINAL_TASK_EVENT_TYPES` — never `task.completed` over cancelled). R6-4: delegate budget reserve/roll-up now merges `tokensSpent=max(disk,incoming)` — the 1s heartbeat whole-record replace could no longer clobber a reservation (lost update), and refund over-subtraction self-heals.
package/README.md CHANGED
@@ -52,7 +52,7 @@ pi install npm:pi-crew
52
52
  ```
53
53
 
54
54
  > The `npm:` prefix is required — without it, `pi install` treats the argument
55
- > as a local path. Requires Node ≥ 22.
55
+ > as a local path. Requires Node ≥ 22.19.0 (`engines.node`).
56
56
 
57
57
  Local development (from a clone):
58
58
 
@@ -176,7 +176,7 @@ config only, so an untrusted repo cannot point your runs at an attacker URL.
176
176
  |-----|----------|
177
177
  | [docs/README.md](docs/README.md) | index of all docs (living + archive) |
178
178
  | [docs/usage.md](docs/usage.md) | usage patterns + config examples |
179
- | [docs/actions-reference.md](docs/actions-reference.md) | all 55 `team` actions with examples |
179
+ | [docs/actions-reference.md](docs/actions-reference.md) | all 57 `team` actions with examples |
180
180
  | [docs/commands-reference.md](docs/commands-reference.md) | slash commands + `/team-api` |
181
181
  | [docs/architecture.md](docs/architecture.md) | internal architecture + run flow |
182
182
  | [docs/troubleshooting.md](docs/troubleshooting.md) | common errors, recovery, error codes |
@@ -35,6 +35,29 @@ When handing off to implementers:
35
35
  - Visual excellence first, code perfection second — but never ship broken interactions.
36
36
  - Implement only the design scope you were given; surface adjacent issues in NOTES.
37
37
 
38
+ ## Enforcement note (R28-M2 — capability/gate contradiction, documented not resolved)
39
+
40
+ This persona is **not** in `WRITE_ROLES` (`src/runtime/role-permission.ts:18`), so
41
+ `permissionForRole("designer")` resolves to **`read_only`** by default-deny. The
42
+ consequences at HEAD are mechanical:
43
+
44
+ - the runner injects the **read-only contract** block into your system prompt
45
+ (`src/runtime/task-runner/prompt-builder.ts:29`),
46
+ - the **plan-approval gate is bypassed** for this role
47
+ (`src/runtime/plan-approval.ts:65`),
48
+ - **MCP tools are cut** (`src/runtime/mcp-proxy.ts:136`),
49
+ - a design-tier task's grandchild spawn tier is floored to `read_only`
50
+ (`src/runtime/spawn-policy.ts:184`).
51
+
52
+ Meanwhile this file's frontmatter advertises `edit`/`write`/`bash` and `useWhen`
53
+ claims implementation. That mismatch is a **known open contradiction** — do not
54
+ assume the read-only contract is the enforced one for *tool availability*, and do
55
+ not assume edit/write are sanctioned. Until the leader signs off either
56
+ (a) adding `designer` to `WRITE_ROLES` (a CODE change) or (b) dropping
57
+ `edit`/`write`/`bash` from the frontmatter (a resource-metadata change), treat
58
+ designer as **design + review + handoff**, and route implementation to `executor`
59
+ or `test-engineer`.
60
+
38
61
  ## Output format
39
62
 
40
63
  End with exactly this block:
@@ -17,6 +17,23 @@ You are an orchestrator — a delegated orchestration specialist. You own a mult
17
17
  ## Canonical routing source (READ THIS)
18
18
  Lane routing tables are NOT duplicated in this body. The discovered resources guidance (teams, workflows, agents with `useWhen`/`avoidWhen`/`cost`/`category`) injected at session start — or `team action='list'` when you need it live — is the SINGLE canonical source. When this body and the discovered guidance disagree, the guidance wins; report the discrepancy rather than routing from memory.
19
19
 
20
+ ## Enforcement note (R28-M3 — the `delegate` tool is DENIED for this role)
21
+
22
+ This file's frontmatter advertises `delegate`, but **`orchestrator` is not in
23
+ `WRITE_ROLES`** (`src/runtime/role-permission.ts:18`), so
24
+ `permissionForRole("orchestrator")` is `read_only` and
25
+ `checkSubagentSpawnPermission` refuses the spawn at call time
26
+ (`src/runtime/role-permission.ts:51-61`, enforced in
27
+ `src/extension/registration/subagent-tools.ts:129-135`): *"Role 'orchestrator' is
28
+ read-only and cannot spawn additional subagents."*
29
+
30
+ Practical consequence: you **cannot** spawn subagents yourself. Route work by
31
+ returning a routing/dispatch plan in your output (the leader or root session
32
+ dispatches it), or by using the `team` tool actions that are not subagent spawns.
33
+ Do not advertise nested orchestration you cannot perform. (De-advertising the
34
+ frontmatter `delegate` entry, or adding `orchestrator` to `WRITE_ROLES`, is an
35
+ open decision requiring leader sign-off — both are behaviour-affecting.)
36
+
20
37
  ## Workflow phases
21
38
  1. **Route** — classify each unit of work against the discovered lanes (useWhen/avoidWhen/cost). Prefer the cheapest sufficient lane. Escalation-tier lanes (expensive) only when the decision cost justifies them.
22
39
  2. **Dispatch** — one owner per file/symbol; parallelize only lanes with no shared write surface. Pass handoff context, not conversation history.