pi-legion 3.1.0 → 5.0.0

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 (85) hide show
  1. package/CHANGELOG.md +382 -2
  2. package/CONTRIBUTING.md +4 -17
  3. package/README.md +127 -66
  4. package/agents/centurion.md +20 -16
  5. package/agents/scout.md +3 -1
  6. package/agents/worker.md +6 -8
  7. package/docs/contracts/declarative-phase-machine-v5.md +128 -0
  8. package/docs/contracts/display-liveness-v1.md +373 -0
  9. package/docs/contracts/drop-non-pi-kinds-v4.md +198 -0
  10. package/docs/contracts/esc-abort-v1.md +4 -4
  11. package/docs/contracts/esc-no-retire-v3.md +2 -2
  12. package/docs/contracts/launch-admission-queue-v1.md +839 -0
  13. package/docs/contracts/status-panel-v1.md +174 -0
  14. package/docs/contracts/subagent-renewal-v1.md +260 -0
  15. package/docs/headless-child-contract.md +6 -3
  16. package/docs/inprocess-supervisor.md +106 -27
  17. package/docs/legion-v2-contract.md +27 -2
  18. package/docs/performance-2026-10-19.md +199 -0
  19. package/docs/spawn-mode-visibility-v1.md +4 -1
  20. package/docs/vexillatio-contract.md +3 -1
  21. package/index.ts +1155 -120
  22. package/package.json +5 -3
  23. package/skills/pi-legion/SKILL.md +2 -3
  24. package/src/agents/agents.ts +310 -46
  25. package/src/agents/overrides.ts +3 -2
  26. package/src/agents/presets.ts +14 -40
  27. package/src/agents/settings.ts +32 -95
  28. package/src/agents/step-model.ts +7 -22
  29. package/src/api.ts +1 -35
  30. package/src/extension/child-guard.ts +25 -1
  31. package/src/extension/event-bus.ts +80 -8
  32. package/src/extension/legion-commands.ts +41 -17
  33. package/src/extension/legion-doctor.ts +1 -1
  34. package/src/extension/legion-stop.ts +39 -13
  35. package/src/extension/legion-tools.ts +87 -25
  36. package/src/extension/liveness.ts +200 -0
  37. package/src/extension/notify.ts +136 -4
  38. package/src/extension/playbook.ts +4 -4
  39. package/src/extension/recycle.ts +4 -0
  40. package/src/extension/runtime.ts +435 -16
  41. package/src/extension/slash.ts +0 -21
  42. package/src/extension/summary.ts +14 -5
  43. package/src/extension/timing.ts +8 -4
  44. package/src/legion/db.ts +165 -1
  45. package/src/legion/events.ts +81 -16
  46. package/src/legion/nodes.ts +13 -2
  47. package/src/legion/phase-conditions.ts +164 -0
  48. package/src/legion/phase-machine.ts +403 -0
  49. package/src/legion/rebuild.ts +7 -1
  50. package/src/legion/state-machine.ts +219 -115
  51. package/src/legion/usage.ts +58 -4
  52. package/src/runs/args.ts +32 -18
  53. package/src/runs/extension-inheritance.ts +220 -0
  54. package/src/runs/orchestrator.ts +1292 -96
  55. package/src/runs/progress-signal.ts +137 -0
  56. package/src/runs/store-lookup.ts +270 -0
  57. package/src/runs/worktree.ts +190 -21
  58. package/src/shared/ignored-kind.ts +57 -0
  59. package/src/shared/progress.ts +11 -0
  60. package/src/shared/session.ts +199 -19
  61. package/src/shared/types.ts +138 -69
  62. package/src/supervisor/admission-queue.ts +516 -0
  63. package/src/supervisor/child-context.ts +19 -0
  64. package/src/supervisor/inprocess-supervisor.ts +137 -38
  65. package/src/supervisor/proc-memory.ts +67 -0
  66. package/src/supervisor/routing-supervisor.ts +60 -2
  67. package/src/supervisor/rpc-supervisor.ts +2 -2
  68. package/src/tui/session-messages-file.ts +100 -0
  69. package/src/tui/status.ts +91 -18
  70. package/src/tui/tree-view.ts +84 -3
  71. package/src/tui/viz/layout.ts +54 -3
  72. package/src/tui/viz/model.ts +67 -14
  73. package/src/tui/viz/palette.ts +11 -3
  74. package/src/tui/viz/panels.ts +222 -8
  75. package/src/tui/viz/view.ts +110 -23
  76. package/agents/advisor.md +0 -110
  77. package/agents/designer.md +0 -87
  78. package/agents/planner.md +0 -53
  79. package/agents/prototype.md +0 -72
  80. package/agents/reviewer.md +0 -113
  81. package/src/backends/legacy/client.ts +0 -638
  82. package/src/backends/legacy/cursor-chat.ts +0 -348
  83. package/src/backends/legacy/runner.ts +0 -101
  84. package/src/backends/legacy/types.ts +0 -138
  85. package/src/runs/kind.ts +0 -260
package/README.md CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: pi-legion
3
3
  npm: "pi-legion"
4
- description: "Pi Legion v2: recursive centurion trees on headless Pi RPC; shared SQLite WAL legion.db. Four-stage planning/implementing/reviewing/verifying state machine, parent/child/sibling legion_mail, three-layer /legion UI, upward MR trees, squad resume."
4
+ description: "Pi Legion v2: recursive centurion teams on Pi. Children run in-process by default (rpc only for a worktree-owning centurion); shared SQLite WAL legion.db. Declarative per-team phase machine (built-in default: planning/implementing/reviewing/verifying), parent/child/sibling legion_mail, three-layer /legion UI, upward MR trees, squad resume."
5
5
  ---
6
6
 
7
7
  # pi-legion
@@ -30,7 +30,7 @@ shared SQLite ledger records the whole tree.
30
30
  - [Team 配置](#team-配置)
31
31
  - [Parent/child context conventions](#parentchild-context-conventions)
32
32
  - [Delivery and resume](#delivery-and-resume)
33
- - [Herdr transition and role migration](#herdr-transition-and-role-migration)
33
+ - [Supported kinds](#supported-kinds)
34
34
  - [Changelog](CHANGELOG.md)
35
35
  - [Development](#development)
36
36
 
@@ -49,14 +49,16 @@ User (CEO) — root Pi session (TUI)
49
49
  └─ pi-legion in child mode
50
50
  ├─ LegionSupervisor for this squad
51
51
  ├─ `legion_mail` / `legion_tree` / `legion_phase` tools
52
- ├─ worker / scout / reviewer … children (`worktree: false`)
52
+ ├─ worker / scout … children (`worktree: false`)
53
53
  └─ nested centurions → recursive squads
54
54
  ```
55
55
 
56
56
  Transport, state, decision-making, and display are separate layers:
57
57
 
58
- - **Transport:** each child is a headless Pi RPC process managed by a
59
- `LegionSupervisor`; a centurion can supervise another squad recursively.
58
+ - **Transport:** a child is an `AgentSession` inside the parent process by
59
+ default; only a worktree-owning centurion runs as a separate `pi --mode rpc`
60
+ process. A `LegionSupervisor` owns both backends, and a centurion can lead
61
+ another squad recursively.
60
62
  - **State:** all nodes in the tree share the `legion.db` SQLite ledger in WAL
61
63
  mode. Per-run `run.json` records and session files remain the resume source
62
64
  for each child.
@@ -65,37 +67,70 @@ Transport, state, decision-making, and display are separate layers:
65
67
  - **Display:** the event bus feeds the resident tree, the `/legion` full view,
66
68
  and an on-demand live overlay.
67
69
 
68
- ## Spawn mode
70
+ ## Bundled roles
71
+
72
+ Three roles ship with the package:
73
+
74
+ | Role | Purpose | Tools | Default timeout |
75
+ | --- | --- | --- | --- |
76
+ | `centurion` | Team lead: freeze the contract, dispatch children, drive the phase machine, deliver a reviewable result. Holds the worktree. | pi defaults | 30 min |
77
+ | `worker` | Implementation: write code and run tests against a frozen plan, report a verdict. | read/edit/write/grep/find/ls/bash/codemode | 45 min |
78
+ | `scout` | Read-only recon: produce a fact list, no plan, no implementation. | read/grep/find/ls/bash/codemode | 10 min |
69
79
 
70
- `spawnMode` picks the backend that runs one child. Per role it comes from agent
71
- frontmatter → `subagents.agentOverrides.<role>` → team member override:
80
+ `worker` cannot dispatch nested agents (`maxSubagentDepth: 1`). Anything a worker
81
+ needs to escalate is a `verdict ok:false`, not another layer of children — one
82
+ level keeps the tree's cost and blast radius bounded.
83
+
84
+ Roles are not a fixed pipeline. Since the phase machine became declarative
85
+ (#298), nothing in the runtime keys on a role being called "worker" or
86
+ "reviewer"; a team shapes its workflow by declaring phases on its lead's card.
72
87
 
73
88
  ```yaml
74
- # agents/scout.md
75
- ---
76
- spawnMode: inprocess # or: rpc
77
- ---
89
+ # agents/centurion.md
90
+ phases:
91
+ - id: recon
92
+ next:
93
+ - to: build
94
+ require: [contract_file]
95
+ - id: build
96
+ next:
97
+ - to: done
98
+ require: [children_settled]
78
99
  ```
79
100
 
80
- **Default:** omitting `spawnMode` means `inprocess` — the child runs as an
81
- `AgentSession` inside the parent Node process (`createAgentSession`), which
82
- trades process isolation for spawn latency.
83
-
84
- **Automatic fallback to `rpc`:** when `spawnMode` is omitted *and* the resolved
85
- spawn cannot be in-process, Legion launches it over `pi --mode rpc` instead.
86
- That happens when the role needs an OS worktree (`worktree: true` — only the
87
- `centurion` role may hold this; see Isolation below). A `kind !== "pi"` role is
88
- rejected earlier with `INVALID_PARAMS` (only `kind: pi` children are supported
89
- at all), so it never reaches this fallback.
90
-
91
- **Explicit is strict:** `spawnMode: "inprocess"` with `worktree: true` is
92
- refused with `INVALID_PARAMS` (message prefers `set worktree: false`) rather
93
- than silently downgraded, so a role that asks for in-process never gets a
94
- different transport behind its back. Non-centurion requests for `worktree: true`
95
- are demoted to `false` with a once-per-run warning instead of throwing.
96
-
97
- Limits for the in-process backend are separate (`subagents.inprocess.*`) and are
98
- not shared with the `legion.*` tree budgets — see
101
+ Omitted means the built-in four-phase pipeline
102
+ (`planning → implementing → reviewing → verifying → done`). Conditions are a
103
+ closed vocabulary — `contract_file`, `children_present`, `children_settled`,
104
+ `children_none_failed`, `verdict_ok`, `verdict_not_ok`, `verification_passed`,
105
+ `verification_failed`, `rework_available`, `children_all_failed`, `always` — and
106
+ `done` / `failed` / `aborted` are always available terminals so a team can
107
+ never lose the ability to complete or abort.
108
+
109
+ ## Spawn mode
110
+
111
+ The backend is a property of the **launch**, not of a role. There is no
112
+ `spawnMode` frontmatter field — a card cannot pin itself to a process model —
113
+ and the rule is simply:
114
+
115
+ | Launch needs a worktree? | Backend |
116
+ | --- | --- |
117
+ | yes | `rpc` — a separate `pi --mode rpc` process |
118
+ | no | in-process `AgentSession` inside the parent Node process |
119
+
120
+ **Why rpc still exists:** it is the only backend that can host a worktree
121
+ child. Only the `centurion` role may hold a worktree (see
122
+ [Delivery and resume](#delivery-and-resume)), so a centurion is the only role
123
+ that can ever run on rpc — every other role runs in-process, where spawn latency
124
+ is lower and no second process is involved.
125
+
126
+ **A non-centurion asking for a worktree** is demoted to `worktree: false` with a
127
+ once-per-run warning rather than throwing, so a stale card does not break every
128
+ launch in the tree.
129
+
130
+ **Both backends share the same budgets.** The legion tree limits
131
+ (`maxDepth` / `maxChildrenPerNode` / `maxActiveNodes`) apply to every launch; the
132
+ in-process pools (`subagents.inprocess.*`) apply on top, and the stricter gate
133
+ wins. Avoiding rpc is never a way to escape a cap. See
99
134
  [docs/inprocess-supervisor.md](docs/inprocess-supervisor.md).
100
135
 
101
136
  ## Headless child contract
@@ -130,7 +165,7 @@ subagent({
130
165
  ```
131
166
 
132
167
  The centurion plans the work, writes the squad contract, dispatches ordinary
133
- roles such as `worker`, `scout`, and `reviewer`, and coordinates the squad
168
+ roles such as `worker` and `scout`, and coordinates the squad
134
169
  through its phases. For a single leaf task, dispatch an ordinary role directly:
135
170
 
136
171
  ```text
@@ -167,16 +202,17 @@ controls run graceful `stop` (a stopped parent retires its own subtree on
167
202
  shutdown); process exit / session shutdown also retires:
168
203
 
169
204
  - **`/legion stop`** stops every live subagent; **`/legion stop <name>`** stops
170
- one (`all` is an alias for no name). Names complete from the live children;
205
+ one (`all` is an alias for no name). Add **`--force`** / **`-f`** for hard
206
+ retire (same as tool `force: true`). Names complete from the live children;
171
207
  nested agents complete as `parent/name` and are reported as owned by their
172
208
  parent (stop the parent to retire them).
173
209
  - **`x`** on the selected live agent in the `/legion` view or its live
174
- overlay asks `y` to confirm in place, then stops it.
210
+ overlay asks `y` to confirm in place, then stops it (graceful; use
211
+ `/legion stop --force <name>` for hard retire).
175
212
  - **Tool**: `subagent({ action: "stop", name })` is graceful; pass
176
213
  `force: true` for hard retire (SIGTERM → SIGKILL + descendant sweep per
177
214
  `docs/retire-hardening-contract.md`). Example:
178
215
  `subagent({ action: "stop", name: "worker-0", force: true })`.
179
- TUI `/legion stop --force` is an optional follow-up (same `force: true`).
180
216
 
181
217
  **`wait` ≠ stop.** A `wait` timeout (or still-running result) does **not**
182
218
  retire children. The tool result includes live child names, elapsed ms, and
@@ -184,8 +220,8 @@ suggested decisions (`extend` / `steer_again` / `stop` / `force_stop`); the
184
220
  parent chooses the next tool call. See `docs/contracts/wait-not-stop.md`.
185
221
 
186
222
  While subagents are live, the railway header can show a dim
187
- `/legion stop [name|all]` hint (shortened on narrow terminals; idle gating /
188
- tree-strip coverage is a TUI follow-up).
223
+ `/legion stop [--force] [name|all]` hint (shortened on narrow terminals; idle
224
+ gating / tree-strip coverage is a TUI follow-up).
189
225
 
190
226
  ### Status (`/legion status`)
191
227
 
@@ -230,11 +266,17 @@ legion_phase({ to, note?, evidence? })
230
266
  - **`legion_phase`** is available to centurions. It requests a state transition;
231
267
  the supervisor checks the transition's invariants and records the result.
232
268
 
233
- A squad moves through **planning → implementing → reviewing → verifying**.
234
- Planning produces a non-empty `.pi-legion/contract.md` with file ownership
235
- and acceptance commands. During reviewing, an independent reviewer supplies
236
- the judgment; in verifying, the supervisor runs the contract's `legion-verify`
237
- commands without an LLM. Passing verification advances the squad to `done`.
269
+ A squad moves through whatever phases its lead declares. With no declaration
270
+ that is the built-in **planning → implementing → reviewing → verifying**:
271
+ planning produces a non-empty `.pi-legion/contract.md` with file ownership and
272
+ acceptance commands; reviewing needs a settled child that returns `ok:true`; in
273
+ verifying the supervisor runs the contract's `legion-verify` commands without an
274
+ LLM. Passing verification advances the squad to `done`.
275
+
276
+ Because the machine is data, no rule names a role: "the children are settled" and
277
+ "a verdict came back ok" are conditions, not "the worker" and "the reviewer".
278
+ That is what lets a team rename, reorder, or drop phases without touching code —
279
+ and it is why the bundled roster is just `centurion`, `worker`, and `scout`.
238
280
 
239
281
  ## Three-layer visualization
240
282
 
@@ -428,20 +470,35 @@ Coverage:
428
470
 
429
471
  ### Child extensions
430
472
 
431
- Children start with `--no-extensions`, so they do not load the host's
432
- discovered extensions (settings `packages`, `~/.pi/agent/extensions`, project
433
- extensions). Each extension a child loads is a memory cost multiplied by every
434
- concurrent child; an LSP stack is ~1 GB (#43). A child still loads pi-legion
435
- itself (guard and nested spawning) and every path listed in the role's
436
- `extensions` / `subagentOnlyExtensions`.
473
+ **A child inherits every extension the parent has**, minus anything it excludes.
474
+ This is the opposite of the pre-4.0.1 default: children used to start with
475
+ `--no-extensions` and load only what a card named, which meant a role silently
476
+ lost host capabilities (a provider extension, a guard) it never knew to name.
477
+ Children now run in-process by default, so "one more extension" no longer means
478
+ another process's worth of memory.
437
479
 
438
480
  | Knob | Effect |
439
481
  | --- | --- |
440
- | role frontmatter `inheritExtensions: true` | That role loads all host extensions, as before. |
441
- | `PI_LEGION_CHILD_INHERIT_EXTENSIONS=1` | Every child loads all host extensions, as before. |
442
- | `PI_LEGION_CHILD_EXTENSIONS=/a.ts,/b.ts` | Extra extension paths every child loads explicitly, e.g. a provider extension the child's model needs. |
443
-
444
- Bundled planner, reviewer, and worker do not list `lens_*` tools. pi-lens starts an LSP stack (~1 GB per child, #43) and is not part of the default `--no-extensions` child. To give a role those tools, add the tool names to its `tools:` list and point `extensions:` / `subagentOnlyExtensions` (or `PI_LEGION_CHILD_EXTENSIONS`) at the pi-lens extension file. `/legion doctor` reports `ROLE_MISSING_LENS` when a role lists the tools without that path.
482
+ | *(default)* | Inherit every host extension. |
483
+ | role frontmatter `excludeExtensions: [pi-lens]` | Subtract specific ones. Accepts a package **name** (`pi-lens`, matches the package dir or a loose `pi-lens.ts`) or an exact **path** (`/abs/.../index.ts`, `~` expands). |
484
+ | role frontmatter `inheritExtensions: false` | Load ONLY the card's own `extensions` / `subagentOnlyExtensions`. Nothing is inherited, so `excludeExtensions` has nothing to subtract. |
485
+ | `PI_LEGION_CHILD_INHERIT_EXTENSIONS=0` | Same as `inheritExtensions: false`, for every child. `1` forces inheritance back on. |
486
+ | `PI_LEGION_CHILD_EXTENSIONS=/a.ts,/b.ts` | Extra paths every child loads explicitly. |
487
+
488
+ The child-guard is appended unconditionally and is not subject to either knob —
489
+ a child without it breaks the prompt/format contract no matter what the card
490
+ asked for.
491
+
492
+ **Discovery limit.** Host extensions are found by reading `pi list` (which
493
+ reports package *directories*) and resolving each one the way pi's own loader
494
+ does: `package.json`'s `pi.extensions`, else `index.ts`/`index.js`. Single-file
495
+ `--extension` arguments and builtins cannot be discovered this way; name them
496
+ explicitly if a child needs them.
497
+
498
+ **Cost note.** Inheriting everything includes heavy extensions. pi-lens starts an
499
+ LSP stack (~1 GB per child, #43). If a fleet of children does not need it, add
500
+ `excludeExtensions: [pi-lens]` to those cards. The bundled `worker` does not list
501
+ `lens_*` tools; give a role those tools by adding the names to its `tools:` list.
445
502
 
446
503
  ### Extra agent directories
447
504
 
@@ -492,7 +549,7 @@ another connection holds the db.
492
549
 
493
550
  ## Delivery and resume
494
551
 
495
- A centurion owns a Git worktree for its subtree; its worker/scout/reviewer
552
+ A centurion owns a Git worktree for its subtree; its worker/scout
496
553
  children work in that subtree (`worktree: false`, typically in-process), and a
497
554
  nested centurion gets a nested worktree. **Parallel isolated writers:** dispatch
498
555
  N centurions from the root (each owns one worktree); do not give every worker
@@ -505,17 +562,13 @@ process ends, its child processes do not become detached workers; resuming the
505
562
  parent restores the squad, while already-settled children are not restarted.
506
563
  Keep the run records and session files while the tree may need to resume.
507
564
 
508
- ## Herdr transition and role migration
565
+ ## Supported kinds
509
566
 
510
- Version **0.16.x is the final Herdr-era line**. Pi Legion 2.x is RPC-only:
511
- child execution uses headless `pi --mode rpc` processes, and the legacy backend
512
- is archived under `src/backends/legacy/`, not offered as a v2 runtime option.
513
- Pin to `pi-legion` 0.16.x if a workflow must stay on the former Herdr-based
514
- execution model. A short breaking-change summary lives in
515
- [CHANGELOG.md](CHANGELOG.md#200---2026-10-14).
516
-
517
- Pi Legion 2.x supports `kind: pi` only. Migrate a custom role that uses another
518
- CLI kind by changing its frontmatter, for example:
567
+ Pi Legion runs `kind: pi` roles only. `kind` is still a legal frontmatter key,
568
+ but `pi` is the only accepted value: a non-`pi` value is ignored (one note on
569
+ stderr) and the role launches as `kind: pi`. The same holds for
570
+ `subagents.presets.<role>.kind` and `subagents.agentOverrides.<role>.kind` —
571
+ those three spots are the only places a role's `kind` can come from.
519
572
 
520
573
  ```yaml
521
574
  ---
@@ -524,8 +577,16 @@ kind: pi
524
577
  ---
525
578
  ```
526
579
 
527
- A non-Pi kind is rejected by v2. If that role must continue to run through
528
- another CLI, keep it on the 0.16.x line or replace it with a Pi role.
580
+ Child execution is Pi either way: in-process by default, with an automatic
581
+ `rpc` fallback when the resolved spawn needs an OS worktree (see
582
+ [Spawn mode](#spawn-mode)).
583
+
584
+ Version **0.16.x was the final Herdr-era line** — it launched children in Herdr
585
+ panes instead of Pi processes. That backend was archived under
586
+ `src/backends/legacy/` for 2.x and 3.x, and is gone in v4. Pin to
587
+ `pi-legion@0.16.x` if a workflow must stay on that former execution model; a
588
+ short breaking-change summary lives in
589
+ [CHANGELOG.md](CHANGELOG.md#200---2026-10-14).
529
590
 
530
591
  ## Development
531
592
 
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: centurion
3
- description: 百夫长/组长:携带一支团队递归派生的指挥角色,负责冻结契约、调度组员、推进四阶段状态机并整队交付。
3
+ description: 百夫长/组长:带领一支子团队,负责冻结契约、调度组员、推进阶段状态机并整队交付。(仅根会话可派发)
4
4
  kind: pi
5
5
  allowNestedSubagents: true
6
6
  maxSubagentDepth: 3
@@ -19,35 +19,39 @@ acceptance:
19
19
 
20
20
  你是 centurion,一支子树团队的负责人。只负责自己的队伍;不得越级指挥、收割或绕过其他 centurion 管理其组员。你必须通过 `legion_phase` 请求阶段迁移,通过 `legion_mail` 沟通,通过 `legion_tree` 观察状态,不得把口头声明当作机器事实。
21
21
 
22
- ## 四阶段职责
22
+ ## 阶段职责
23
23
 
24
- ### planning
24
+ 阶段由本队的 `phases:` 声明(省略则是内置四阶段:planning → implementing → reviewing → verifying → done)。**不要假定阶段名**——先看系统提示里展开的状态机,或直接用 `legion_phase` 试,被拒时拒绝信息会列出当前合法的下一步。
25
+
26
+ 不论阶段怎么命名,职责是这样分层的:
27
+
28
+ ### 规划阶段(内置 `planning`)
25
29
 
26
30
  - 先完整理解目标与验收条件,再把接口、wire 字段、文件所有权、验收清单写入 `.pi-legion/contract.md`。
27
- - 契约未落盘且非空,不得请求进入 implementing;发现契约矛盾时停止派工并上报,不得自行解释或扩大文件归属。
31
+ - 契约未落盘且非空,不得请求进入实现阶段;发现契约矛盾时停止派工并上报,不得自行解释或扩大文件归属。
28
32
  - 契约中的每个写入文件只能有一个 owner;共享文件必须串行分配。
29
33
 
30
- ### implementing
34
+ ### 实现阶段(内置 `implementing`)
31
35
 
32
36
  - 按冻结契约和文件所有权并行派发互不冲突的工作;worker 任务必须逐项列出可写文件、验证要求和交付格式。
33
37
  - 子任务只用 `subagent` 工具派发;工具的并行任务必须各自有明确边界。实现过程中监控 `legion_tree`,通过邮件解决必要的协作,不以邮件取代共享工件。
34
- - 组员声明完成后,按结果收集 session/verdict;未收齐所有 worker 结果不得请求进入 reviewing。
38
+ - 组员声明完成后,按结果收集 session/verdict;未收齐所有 worker 结果不得请求离开本阶段。
35
39
 
36
- ### reviewing
40
+ ### 审查阶段(内置 `reviewing`)
37
41
 
38
- - 对实现做多角度、对抗式 review,至少覆盖正确性/回归、测试覆盖和简洁性;reviewer 必须独立于实现者。
39
- - `reviewer` verdict `ok:true` 才能请求进入 verifying。major finding 必须退回 implementing 修复并复审;若是计划本身错误,可退回 planning,但必须通过 `legion_phase` 记录并邮件告知上级。
40
- - 每个 `failed` worker 必须先 rework 重派或由父级明确收队;不得把失败 worker 声明为已处理来解锁 reviewing。
41
- - 被 `legion_phase` 拒绝时,先按拒绝理由核实机器事实,修正条件后重试;这不是 rework,不得伪造证据或绕过状态机。
42
+ - 对抗式审查,至少覆盖正确性/回归、测试覆盖和简洁性,且必须由**未参与实现**的独立子节点做。
43
+ - 通过条件看阶段声明里的 `verdict_ok`:需要一个 settled 子节点回报 `ok:true`。major finding 退回实现阶段修复并复审;若是计划本身错误,可退回规划阶段,但必须通过 `legion_phase` 记录并邮件告知上级。
44
+ - 每个 `failed` 子节点必须先 rework 重派或由父级明确收队;不得把失败节点声明为已处理来解锁审查阶段。
45
+ - 被 `legion_phase` 拒绝时,按拒绝理由核实机器事实,修正条件后重试;这不是 rework,不得伪造证据或绕过状态机。
42
46
 
43
- ### verifying 与收队
47
+ ### 验证与收队(内置 `verifying` / `done`)
44
48
 
45
- - verifying 由 supervisor 自动运行契约中的 `legion-verify` 命令。全绿后状态机自动进入 done;不要自行宣称验证成功,也不要手动改数据库。
46
- - done 后汇总全部 worker/reviewer verdict、验证结果和未决问题;把子树工作 squash 成一个 MR/PR,开给父节点/目标分支审查,并把链接写入最终 verdict。绝不在本级自行合并。
49
+ - 验证由 supervisor 自动运行契约中的 `legion-verify` 命令。全绿后状态机自动进入 done;不要自行宣称验证成功,也不要手动改数据库。
50
+ - done 后汇总全部子节点 verdict、验证结果和未决问题;把子树工作 squash 成一个 MR/PR,开给父节点/目标分支审查,并把链接写入最终 verdict。绝不在本级自行合并。
47
51
  - 收队时保证组员结果已落盘;由父级 collect 本节点结果后 retire。worktree/session 是 resume 凭据,未获根级清理授权前不得删除。
48
52
 
49
53
  ## 止损与安全
50
54
 
51
- - 同一问题重试不超过 2 次;两次仍失败,立即通过 `advisor` 咨询止损。advisor 必须是 `kind: pi`。咨询要附已尝试方案及失败输出、当前假设、涉及文件,并按 advisor 裁决执行。
52
- - advisor 裁决回滚时回到上一个检查点并记录原因;不得硬扛第三次,不得为了绿灯绕过测试或修改冻结契约。
55
+ - 同一问题重试不超过 2 次;两次仍失败,停止并向上报告(附已尝试方案、失败输出、当前假设、涉及文件),不要硬扛第三次,也不得为了绿灯绕过测试或修改冻结契约。
56
+ - 回滚时回到上一个检查点并记录原因。
53
57
  - 只使用允许的工具和团队边界;不读写其他队的文件,不直接访问 legion.db,不使用越级邮件。
package/agents/scout.md CHANGED
@@ -8,6 +8,9 @@ inheritProjectContext: true
8
8
  inheritSkills: false
9
9
  async: true
10
10
  timeoutMs: 600000
11
+ # A timed-out scout means the question was too broad, not that work needs
12
+ # finishing — renewing it would just re-run an over-scoped search (#270 D5).
13
+ renewable: false
11
14
  acceptance:
12
15
  level: attested
13
16
  role: read-only
@@ -17,7 +20,6 @@ acceptance:
17
20
  evidence: [facts, open-questions]
18
21
  severity: required
19
22
  kind: pi
20
- placement: split-down
21
23
  steer: true
22
24
  maxSubagentDepth: 1
23
25
  ---
package/agents/worker.md CHANGED
@@ -1,13 +1,12 @@
1
1
  ---
2
2
  name: worker
3
3
  description: 实现者:按冻结计划写代码、跑测试,完成后自报 verdict
4
- tools: read, edit, write, grep, find, ls, bash, subagent, codemode
4
+ tools: read, edit, write, grep, find, ls, bash, codemode
5
5
  systemPromptMode: replace
6
6
  inheritProjectContext: true
7
7
  inheritSkills: false
8
8
  async: true
9
- timeoutMs: 1800000
10
- allowNestedSubagents: true
9
+ timeoutMs: 2700000
11
10
  acceptance:
12
11
  level: verified
13
12
  role: writer
@@ -17,10 +16,9 @@ acceptance:
17
16
  evidence: [verification-output]
18
17
  severity: required
19
18
  kind: pi
20
- placement: split-down
21
19
  steer: true
22
20
  worktree: false
23
- maxSubagentDepth: 3
21
+ maxSubagentDepth: 1
24
22
  ---
25
23
 
26
24
  你是 worker,一个实现 agent。你按已冻结的计划把代码写出来并证明它能跑。
@@ -36,8 +34,8 @@ maxSubagentDepth: 3
36
34
  - **严格按计划**:接口/签名已被冻结,不要擅自更改。发现计划有错时,**停下来报告**(verdict `ok: false`,reason 写明冲突),不要自行"顺手修正"。
37
35
  - **只碰计划授权的文件**。其他模块的文件一律不动。
38
36
  - **跑不动的命令**(交互式、需要确认)走默认放行,不要挂起等待。
39
- - **禁止开新的 worker/写操作子 agent**。你唯一允许的嵌套派生是只读的 advisor,
40
- 且仅限失败升级场景(见 AGENTS.md「失败止损」:重试 2 次仍失败时咨询,按裁决执行)。
37
+ - **不派任何子 agent**。失败止损靠自己重试与向上报告(verdict `ok: false` 写明冲突),
38
+ 不要为了"咨询"再开一层。
41
39
  - **工作区**:在 centurion 子树 worktree(或父会话 cwd)**原地**改代码;隔离与 MR 由 centurion(或根会话)收队。不要本地 merge,不要 push 到 main/master。
42
40
  - **bugfix 先红后绿**:修 bug 类任务,第一步先写一个能复现该 bug 的失败测试并跑出失败,修复后同一测试必须转绿;两次输出都原样贴进报告。不复现就修复的,视为未验收。
43
41
 
@@ -47,7 +45,7 @@ maxSubagentDepth: 3
47
45
  2. **用项目自己的方式验证,分两档,别每轮都跑全量**:
48
46
  - 先发现项目怎么验证:看 `.pi/verify.sh`、Makefile/justfile target、CI 配置(.github/workflows 等)、package.json scripts、项目 README/CONTRIBUTING。项目明确的验证入口就是唯一权威,不要自己发明替代命令。
49
47
  - 每轮改动结束:跑项目的**轻量档**(typecheck/lint/vet 级别,如 `make lint`、`npm run typecheck`、`go vet ./...`);秒级反馈,失败直接修,不计入重试升级。
50
- - 全部步骤完成、交活前才跑一次项目的**全量档**(完整测试套件/CI 等效命令);交活后插件会自动再验证一遍判定验收,不需要中途反复跑全量。全量失败按「失败止损」处理:重试不超过 2 次,仍失败升级咨询 advisor。
48
+ - 全部步骤完成、交活前才跑一次项目的**全量档**(完整测试套件/CI 等效命令);交活后插件会自动再验证一遍判定验收,不需要中途反复跑全量。全量失败按「失败止损」处理:重试不超过 2 次,仍失败则 `ok: false` 上报。
51
49
  - 项目找不到任何验证入口时,在报告里写明依据(查过哪些地方),不要自己编命令冒充。
52
50
  3. 把最终那次 full 验证输出**原样**(含命令与结果)放进最终报告。
53
51
  4. 交活前逐文件重读完整 diff:检查遗留的调试代码、console.log/print、注释掉的老逻辑、无关改动混入;发现问题先修。
@@ -0,0 +1,128 @@
1
+ # Declarative phase machine (v5)
2
+
3
+ Status: draft, layer 3 (#298).
4
+
5
+ ## Problem
6
+
7
+ The legion phase machine is hard-coded in three places that must agree:
8
+
9
+ | Place | Hard-codes |
10
+ | --- | --- |
11
+ | `src/legion/state-machine.ts:1` | The seven phase names and every transition rule |
12
+ | `src/agents/settings.ts:117` | A duplicate copy of the phase names, for `phaseTimeouts` validation |
13
+ | `src/extension/legion-tools.ts:47` | The `legion_phase` tool's `to` enum |
14
+
15
+ Worse, the *rules inside a phase* are keyed on **role names**:
16
+
17
+ ```text
18
+ implementing → reviewing requires every `role === "worker"` to be settled
19
+ reviewing → verifying requires a settled `role === "reviewer"` with verdict ok:true
20
+ planning → implementing requires no `role === "planner"` still running
21
+ ```
22
+
23
+ So a team cannot re-shape its workflow without editing source, and a role that
24
+ merely *exists* acquires workflow meaning. "worker" is not "a worker" — it is a
25
+ specific slot in a fixed pipeline.
26
+
27
+ ## Goal
28
+
29
+ A centurion declares its team's phases and the conditions for leaving them.
30
+ Defaults preserve today's behaviour exactly, so an undeclared machine is the
31
+ four-phase pipeline above.
32
+
33
+ ## Non-goals
34
+
35
+ - Arbitrary scripting. Conditions are a closed vocabulary of predicates, not code.
36
+ - Per-node machines. One machine per team root; children inherit it read-only.
37
+ - Changing how children are spawned. Phases gate *progress*, not dispatch.
38
+
39
+ ## Where the declaration lives
40
+
41
+ Role frontmatter, on the team root card:
42
+
43
+ ```yaml
44
+ ---
45
+ name: centurion
46
+ phases:
47
+ - id: planning
48
+ require: [contract_file]
49
+ next: [implementing]
50
+ timeoutMs: 900000
51
+ - id: implementing
52
+ require: [children_settled]
53
+ next: [reviewing, failed]
54
+ - id: reviewing
55
+ require: [verdict_ok]
56
+ next: [verifying, implementing]
57
+ - id: verifying
58
+ require: [verification_passed]
59
+ next: [done]
60
+ ---
61
+ ```text
62
+
63
+ Kept on the card rather than in `settings` because the machine is a property of
64
+ *this team*, like its roster — two centurions in one settings file may run
65
+ different workflows.
66
+
67
+ Resolution order: card `phases` → `settings.legion.phases` → built-in default.
68
+
69
+ ## Condition vocabulary
70
+
71
+ A condition answers "may this node leave phase X for phase Y?" over a snapshot of
72
+ the node and its children. Closed set, each with one meaning:
73
+
74
+ | Condition | True when |
75
+ | --- | --- |
76
+ | `contract_file` | `contract_path` is set and the file exists and is non-empty |
77
+ | `children_settled` | every child of this node is terminal (settled/failed/retired) |
78
+ | `children_none_failed` | no child is `failed`, or every failed child is listed in `handled` |
79
+ | `verdict_ok` | some settled child's last turn parsed to `{ok: true}` |
80
+ | `verdict_not_ok` | some settled child's last turn parsed to `{ok: false}` |
81
+ | `verification_passed` | the node's stored verification result is `pass` |
82
+ | `verification_failed` | the node's stored verification result is `fail` |
83
+ | `rework_available` | `rework_count < maxRework` |
84
+ | `children_all_failed` | there is ≥1 child and all are `failed` |
85
+ | `always` | unconditional |
86
+
87
+ `require` is a conjunction: all listed conditions must hold.
88
+ `next` is the allowed successor set; a transition to anything outside it is
89
+ refused with `invalid_transition`.
90
+
91
+ **Role names do not appear anywhere.** `verdict_ok` asks "did any settled child
92
+ return ok:true", not "did the reviewer". That is what lets `worker` become a
93
+ general worker.
94
+
95
+ ## Terminal phases
96
+
97
+ Exactly three are terminal and are always available regardless of `next`:
98
+
99
+ - `done` — only when the phase's `require` holds
100
+ - `failed` — parent decision, all children failed, or rework exhausted
101
+ - `aborted` — parent retire only
102
+
103
+ ## Safety rails (unchanged)
104
+
105
+ These are invariants, not configuration:
106
+
107
+ - A new team starts in the machine's **first** phase.
108
+ - `rework_count` still caps at `maxRework` (default 2).
109
+ - `centurion`-only gates stay: a child cannot advance the team.
110
+ - Phase timeouts still only *report*; they never kill work.
111
+ - A declared machine is validated at load time. Unknown condition, empty `next`,
112
+ duplicate id, unreachable phase, or a terminal phase used as a start → the card
113
+ is rejected (same posture as unknown frontmatter).
114
+
115
+ ## Migration
116
+
117
+ The default machine reproduces today's table, so no behaviour change without a
118
+ declaration. The role-name predicates become condition predicates:
119
+
120
+ | Old rule | New condition |
121
+ | --- | --- |
122
+ | all `worker` settled before reviewing | `children_settled` |
123
+ | settled `reviewer` with ok:true | `verdict_ok` |
124
+ | no `planner` still running | covered by `children_settled` |
125
+ | verification pass before done | `verification_passed` |
126
+
127
+ Once this lands, `planner` and `reviewer` stop being load-bearing and their
128
+ bundled cards can be deleted.