pi-legion 4.0.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.
- package/CHANGELOG.md +168 -1
- package/README.md +101 -44
- package/agents/centurion.md +20 -16
- package/agents/scout.md +3 -1
- package/agents/worker.md +5 -7
- package/docs/contracts/declarative-phase-machine-v5.md +128 -0
- package/docs/contracts/subagent-renewal-v1.md +260 -0
- package/docs/headless-child-contract.md +6 -3
- package/docs/inprocess-supervisor.md +70 -24
- package/docs/legion-v2-contract.md +9 -0
- package/docs/spawn-mode-visibility-v1.md +4 -1
- package/index.ts +598 -44
- package/package.json +2 -2
- package/src/agents/agents.ts +293 -36
- package/src/agents/overrides.ts +3 -1
- package/src/agents/settings.ts +6 -18
- package/src/extension/child-guard.ts +25 -1
- package/src/extension/legion-tools.ts +66 -21
- package/src/extension/notify.ts +136 -4
- package/src/extension/playbook.ts +2 -2
- package/src/extension/runtime.ts +56 -1
- package/src/extension/summary.ts +13 -4
- package/src/extension/timing.ts +8 -4
- package/src/legion/phase-conditions.ts +164 -0
- package/src/legion/phase-machine.ts +403 -0
- package/src/legion/state-machine.ts +219 -115
- package/src/runs/args.ts +32 -18
- package/src/runs/extension-inheritance.ts +220 -0
- package/src/runs/orchestrator.ts +137 -44
- package/src/runs/progress-signal.ts +137 -0
- package/src/runs/store-lookup.ts +270 -0
- package/src/shared/types.ts +88 -3
- package/src/supervisor/child-context.ts +19 -0
- package/src/supervisor/inprocess-supervisor.ts +29 -5
- package/src/supervisor/routing-supervisor.ts +2 -1
- package/src/supervisor/rpc-supervisor.ts +2 -2
- package/src/tui/tree-view.ts +56 -0
- package/src/tui/viz/palette.ts +0 -3
- package/agents/advisor.md +0 -110
- package/agents/designer.md +0 -87
- package/agents/planner.md +0 -53
- package/agents/prototype.md +0 -72
- package/agents/reviewer.md +0 -113
package/CHANGELOG.md
CHANGED
|
@@ -11,8 +11,174 @@ summary so you can see **2.x vs 0.16** without leaving the tree.
|
|
|
11
11
|
|
|
12
12
|
## [Unreleased]
|
|
13
13
|
|
|
14
|
+
## [5.0.0] — 2026-10-26
|
|
15
|
+
|
|
16
|
+
Major because upgrading **can break an existing role card**: unknown frontmatter
|
|
17
|
+
is now a hard error, and `spawnMode` / `placement` are gone. A card carrying
|
|
18
|
+
either fails to load with a message naming the offending field. That is the point
|
|
19
|
+
— the previous behaviour was to silently ignore them, so a setting that looked
|
|
20
|
+
active did nothing.
|
|
21
|
+
|
|
22
|
+
**Before upgrading**, remove `spawnMode:` and `placement:` from every card under
|
|
23
|
+
`~/.pi/agent/agents/` and `.pi/agents/`. Nothing else about a card needs to
|
|
24
|
+
change.
|
|
25
|
+
|
|
26
|
+
### Breaking
|
|
27
|
+
|
|
28
|
+
- **`spawnMode` is gone from role cards (#290).** The backend is a property of
|
|
29
|
+
the launch, not of a role: a launch that needs a worktree runs over rpc, and
|
|
30
|
+
everything else runs in-process. Since only a `centurion` may hold a worktree
|
|
31
|
+
(#214), a centurion is the only role that can reach rpc. There is no global
|
|
32
|
+
default to configure because there is no longer a choice to make per role.
|
|
33
|
+
- **Unknown frontmatter is refused (#290).** A card with a field the runtime does
|
|
34
|
+
not honour is rejected at load, listing the bad keys and the full legal set. The
|
|
35
|
+
retiral of `placement` was previously "tolerated"; fourteen bundled and local
|
|
36
|
+
cards still carried it while it did nothing.
|
|
37
|
+
- **Five bundled roles removed (#301).** `planner`, `reviewer`, `designer`,
|
|
38
|
+
`prototype`, and `advisor` are gone; `centurion`, `worker`, and `scout` remain.
|
|
39
|
+
They existed because the phase machine keyed its rules on role *names* — a role
|
|
40
|
+
that merely existed carried workflow meaning. See the declarative machine below.
|
|
41
|
+
- **Children inherit every host extension by default (#280).** The old default was
|
|
42
|
+
`--no-extensions` plus whatever a card named, which meant a role silently lost
|
|
43
|
+
host capabilities (a provider extension, a guard) it never knew to name. Use
|
|
44
|
+
`excludeExtensions: [pi-lens]` on a card, or
|
|
45
|
+
`PI_LEGION_CHILD_INHERIT_EXTENSIONS=0` globally, to get the old isolation.
|
|
46
|
+
`inheritExtensions: false` loads only the card's own list.
|
|
47
|
+
|
|
48
|
+
### Added
|
|
49
|
+
|
|
50
|
+
- **Declarative phase machine (#298).** A team declares its own phases on its
|
|
51
|
+
lead's card:
|
|
52
|
+
```yaml
|
|
53
|
+
phases:
|
|
54
|
+
- id: recon
|
|
55
|
+
next:
|
|
56
|
+
- to: build
|
|
57
|
+
require: [contract_file]
|
|
58
|
+
- id: build
|
|
59
|
+
next:
|
|
60
|
+
- to: done
|
|
61
|
+
require: [children_settled]
|
|
62
|
+
```
|
|
63
|
+
Omitted means the built-in `planning → implementing → reviewing → verifying`.
|
|
64
|
+
Conditions are a closed vocabulary (`contract_file`, `children_present`,
|
|
65
|
+
`children_settled`, `children_none_failed`, `verdict_ok`, `verdict_not_ok`,
|
|
66
|
+
`verification_passed`, `verification_failed`, `rework_available`,
|
|
67
|
+
`children_all_failed`, `always`) and **none of them names a role**, which is
|
|
68
|
+
what lets a team reshape its workflow without touching source.
|
|
69
|
+
- Conditions live on the **edge**, not the phase: `planning → implementing`
|
|
70
|
+
needs a contract while `planning → failed` must not, or a team could not fail
|
|
71
|
+
before it had one.
|
|
72
|
+
- `done` / `failed` / `aborted` are injected when a card omits them. A team that
|
|
73
|
+
cannot be aborted is a safety hole. Reaching them is still gated: `failed`
|
|
74
|
+
needs a terminal reason, `aborted` needs a parent retire, `done` stays
|
|
75
|
+
supervisor-only. `aborted` is deliberately exempt from phase-level
|
|
76
|
+
requirements — an emergency stop must not wait for "everything settled".
|
|
77
|
+
- An invalid machine is rejected at load, reporting every problem at once
|
|
78
|
+
(unknown condition, duplicate id, unknown target, self-edge, unreachable
|
|
79
|
+
phase, terminal start).
|
|
80
|
+
- **The tree shows a team's machine (#302).** Selecting a team root unfolds its
|
|
81
|
+
phases, the current one, and every transition with its conditions.
|
|
82
|
+
- **`legion_phase` refusals name the legal exits (#299).** A refused transition
|
|
83
|
+
reports `legal next phases from 'recon': build, done, failed, aborted`. Measured:
|
|
84
|
+
without it a model invented plausible-but-nonexistent transitions and burned a
|
|
85
|
+
turn being refused.
|
|
86
|
+
|
|
87
|
+
### Fixed
|
|
88
|
+
|
|
89
|
+
- **In-process children enforce `toolBudget` / `turnBudget` (#295).** The parent
|
|
90
|
+
computed budgets into `SpawnInput.env`, which an rpc child receives as its real
|
|
91
|
+
environment — but an in-process child shares the *host's* `process.env`, so the
|
|
92
|
+
guard read the parent's (absent) values. A card with `toolBudget: {maxToolCalls:
|
|
93
|
+
3}` made 12 tool calls and finished normally. Budgets now travel in the
|
|
94
|
+
async-local child context, the same channel already used for lineage identity,
|
|
95
|
+
with `process.env` kept as the rpc fallback.
|
|
96
|
+
- **A malformed budget is an error, not "no limit" (#293).** `{not json}` parsed to
|
|
97
|
+
`undefined` and `{"maxToolCalls": -3}` was accepted, so the guard would have
|
|
98
|
+
blocked the first call. Both now fail at load naming the file, field, and value.
|
|
99
|
+
- **rpc is reserved for a centurion, and in-process keeps its ledger budget
|
|
100
|
+
(#279).** In-process launches had been exempted from every legion budget
|
|
101
|
+
(`Number.MAX_SAFE_INTEGER`), which made "run it in-process" a way to escape
|
|
102
|
+
depth/spawn/ledger caps. Admission and reservation now share one code path, so a
|
|
103
|
+
launch cannot be admitted and then rejected.
|
|
104
|
+
- **Runtime handles are keyed by (ledger path, node id) (#283, #285, #287).** Keyed
|
|
105
|
+
by path alone, a second session sharing the ledger could close the parent's
|
|
106
|
+
database; the identity is now adopted per node.
|
|
107
|
+
- **Extension inheritance discovery (#280).** `pi list` reports package
|
|
108
|
+
*directories*, resolved the way pi's own loader does (`package.json`'s
|
|
109
|
+
`pi.extensions`, else `index.ts` / `index.js`). Single-file `--extension`
|
|
110
|
+
arguments and builtins cannot be discovered this way — name them explicitly.
|
|
111
|
+
- **One spelling for "unknown child" (#288).** The old message advised "pass the
|
|
112
|
+
same cwd used for launch", which stopped being actionable once cross-store
|
|
113
|
+
lookup landed.
|
|
114
|
+
- **Prompt surfaces match the code again (#303, #304).** The parent playbook told a
|
|
115
|
+
centurion to "dispatch N centurions" — an instruction the runtime refuses
|
|
116
|
+
(only the root may dispatch one). The roster also listed a centurion to
|
|
117
|
+
itself. `worker` kept `subagent` in its tool list while its text said not to
|
|
118
|
+
spawn, and the README described every child as an rpc process.
|
|
119
|
+
|
|
120
|
+
### Notes
|
|
121
|
+
|
|
122
|
+
- `toolBudget` / `turnBudget` remain **optional and unused by the bundled cards**.
|
|
123
|
+
Time, depth, per-node child counts, active-node counts, and the memory floor are
|
|
124
|
+
the intended bounds; a tool budget is a scalpel for one stubborn role, not a
|
|
125
|
+
default.
|
|
126
|
+
- Child extension inheritance includes heavy extensions. pi-lens starts an LSP
|
|
127
|
+
stack (~1 GB per child, #43); add `excludeExtensions: [pi-lens]` where a fleet
|
|
128
|
+
does not need it.
|
|
129
|
+
|
|
14
130
|
## [4.0.0] — 2026-10-24
|
|
15
131
|
|
|
132
|
+
- **Renewal for a budget casualty (#270).** An agent whose turn budget (`timeoutMs`)
|
|
133
|
+
runs out no longer just reports `failed`. When it measurably progressed and its
|
|
134
|
+
role allows renewal, the completion notice ends with a ready-to-paste
|
|
135
|
+
`subagent({ action: "continue", … })` plus the evidence (`progressed: 47 tool
|
|
136
|
+
results, 12 assistant messages, 6 files changed`) and the remaining budget
|
|
137
|
+
(`renewals: 1/2`). Renewal reuses the existing `continue` path, so the child
|
|
138
|
+
resumes from its own session file with its context intact. Two dispatches in one
|
|
139
|
+
day motivated this: one worker was cut off with nothing on disk, another finished
|
|
140
|
+
its code but ran out during verification.
|
|
141
|
+
- **Never automatic.** An auto-renewed stuck child would hold a concurrency slot
|
|
142
|
+
indefinitely; the notice hands over the decision instead.
|
|
143
|
+
- "Progressed" = ≥ 3 new `toolResult` rows or ≥ 1 changed file since the last
|
|
144
|
+
baseline. `assistant` messages are shown but never count — a model looping on
|
|
145
|
+
the same sentence grows that number without doing work.
|
|
146
|
+
- New agent frontmatter: `renewable` (default **true**; a timed-out `scout` means
|
|
147
|
+
the task was mis-scoped) and `maxRenewals` (default **2**; `0` disables, each
|
|
148
|
+
renewal is a fresh full budget). Both are validated in the team-override path
|
|
149
|
+
like every other numeric/boolean field.
|
|
150
|
+
- `wait`/`collect` timeouts never propose a renewal: those return while the child
|
|
151
|
+
is still alive. Only a real termination does, judged by `stopReason: "toolUse"`
|
|
152
|
+
/ missing assistant row (F29) / the host's `terminated` wording — a clean
|
|
153
|
+
`stop` from an ordinary retry does not spend the budget.
|
|
154
|
+
|
|
155
|
+
- **Launch admission queue (#266).** A launch that exceeds a *concurrency* gate
|
|
156
|
+
(`maxChildrenPerNode`, `maxActive`, ledger `max_children_per_node` /
|
|
157
|
+
`max_active_nodes`) no longer fails with `BUDGET_EXCEEDED`. It is enqueued,
|
|
158
|
+
the caller gets `▶ scout-7 (scout) supervisor=inprocess queued(3)` and treats
|
|
159
|
+
the work as dispatched, and the child starts automatically when a slot frees.
|
|
160
|
+
Previously a 12-step fan-out against `maxChildrenPerNode: 8` ran 8 children and
|
|
161
|
+
reported 4 errors — and nothing ever retried them, because the parent was
|
|
162
|
+
already inside `wait` and the freed slot had no subscriber.
|
|
163
|
+
- New settings: `subagents.inprocess.maxQueuedLaunches` (default **32**) and
|
|
164
|
+
`subagents.inprocess.launchQueueTimeoutMs` (default **900000**). Both are
|
|
165
|
+
shared by the in-process and RPC paths — one scheduler, not two.
|
|
166
|
+
- Depth, spawn-per-session, memory, name-collision and config errors are still
|
|
167
|
+
refused synchronously, byte-identically to before: only gates that a slot
|
|
168
|
+
release can satisfy are queueable. `maxChildrenPerNode: 0` is a hard refusal
|
|
169
|
+
(0 means "no children", so waiting can never satisfy it).
|
|
170
|
+
- A queued launch reserves nothing: no ledger row, no child run directory, no
|
|
171
|
+
worktree, no session file. `timeout`/`overflow`/`stop` are reported by name;
|
|
172
|
+
`stop` dequeues without ever spawning, and a parent-turn abort flushes the
|
|
173
|
+
queue and says how many launches it dropped.
|
|
174
|
+
- `subagent({action:"status"})` shows `state=queued`, `collect`/`wait` block
|
|
175
|
+
until admission instead of reading the placeholder session path, and
|
|
176
|
+
`async: false` reports `↳ <name> waited Ns in the launch queue`.
|
|
177
|
+
- Two new process-only events (`launch_queued`, `launch_admitted`) and a
|
|
178
|
+
`legion-queue: N queued` status pill.
|
|
179
|
+
|
|
180
|
+
## [4.0.0] — 2026-10-19
|
|
181
|
+
|
|
16
182
|
A major line — but not only the pi-only collapse. Relative to 3.1.0 this release
|
|
17
183
|
removes every non-`pi` agent kind and the archived Herdr backend behind it, along
|
|
18
184
|
with the settings and error codes that only its transport read. It also carries
|
|
@@ -651,7 +817,8 @@ branch — pin `pi-legion@0.16.x` if you still need that model. See
|
|
|
651
817
|
Earlier 0.11–0.15 history (including the `pi-herdr-swarm` /
|
|
652
818
|
`@zzjcool/pi-herdr-subagents` renames) lives in git tags and older Releases.
|
|
653
819
|
|
|
654
|
-
[Unreleased]: https://github.com/zzjcool/pi-legion/compare/
|
|
820
|
+
[Unreleased]: https://github.com/zzjcool/pi-legion/compare/v5.0.0...HEAD
|
|
821
|
+
[5.0.0]: https://github.com/zzjcool/pi-legion/compare/v4.0.0...v5.0.0
|
|
655
822
|
[4.0.0]: https://github.com/zzjcool/pi-legion/compare/v3.1.0...v4.0.0
|
|
656
823
|
[3.1.0]: https://github.com/zzjcool/pi-legion/compare/v3.0.3...v3.1.0
|
|
657
824
|
[3.0.3]: https://github.com/zzjcool/pi-legion/compare/v3.0.2...v3.0.3
|
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
|
|
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
|
|
@@ -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
|
|
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:**
|
|
59
|
-
|
|
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,36 +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
|
-
##
|
|
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
|
-
`
|
|
71
|
-
|
|
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/
|
|
75
|
-
|
|
76
|
-
|
|
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
|
-
|
|
81
|
-
`
|
|
82
|
-
|
|
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.
|
|
83
108
|
|
|
84
|
-
|
|
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
|
|
88
|
-
[Delivery and resume](#delivery-and-resume)).
|
|
109
|
+
## Spawn mode
|
|
89
110
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
different transport behind its back. Non-centurion requests for `worktree: true`
|
|
94
|
-
are demoted to `false` with a once-per-run warning instead of throwing.
|
|
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:
|
|
95
114
|
|
|
96
|
-
|
|
97
|
-
|
|
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
|
|
98
134
|
[docs/inprocess-supervisor.md](docs/inprocess-supervisor.md).
|
|
99
135
|
|
|
100
136
|
## Headless child contract
|
|
@@ -129,7 +165,7 @@ subagent({
|
|
|
129
165
|
```
|
|
130
166
|
|
|
131
167
|
The centurion plans the work, writes the squad contract, dispatches ordinary
|
|
132
|
-
roles such as `worker
|
|
168
|
+
roles such as `worker` and `scout`, and coordinates the squad
|
|
133
169
|
through its phases. For a single leaf task, dispatch an ordinary role directly:
|
|
134
170
|
|
|
135
171
|
```text
|
|
@@ -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
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
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
|
-
|
|
432
|
-
|
|
433
|
-
extensions
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
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
|
-
|
|
|
441
|
-
| `
|
|
442
|
-
| `
|
|
443
|
-
|
|
444
|
-
|
|
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
|
|
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
|
package/agents/centurion.md
CHANGED
|
@@ -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
|
-
|
|
24
|
+
阶段由本队的 `phases:` 声明(省略则是内置四阶段:planning → implementing → reviewing → verifying → done)。**不要假定阶段名**——先看系统提示里展开的状态机,或直接用 `legion_phase` 试,被拒时拒绝信息会列出当前合法的下一步。
|
|
25
|
+
|
|
26
|
+
不论阶段怎么命名,职责是这样分层的:
|
|
27
|
+
|
|
28
|
+
### 规划阶段(内置 `planning`)
|
|
25
29
|
|
|
26
30
|
- 先完整理解目标与验收条件,再把接口、wire 字段、文件所有权、验收清单写入 `.pi-legion/contract.md`。
|
|
27
|
-
-
|
|
31
|
+
- 契约未落盘且非空,不得请求进入实现阶段;发现契约矛盾时停止派工并上报,不得自行解释或扩大文件归属。
|
|
28
32
|
- 契约中的每个写入文件只能有一个 owner;共享文件必须串行分配。
|
|
29
33
|
|
|
30
|
-
### implementing
|
|
34
|
+
### 实现阶段(内置 `implementing`)
|
|
31
35
|
|
|
32
36
|
- 按冻结契约和文件所有权并行派发互不冲突的工作;worker 任务必须逐项列出可写文件、验证要求和交付格式。
|
|
33
37
|
- 子任务只用 `subagent` 工具派发;工具的并行任务必须各自有明确边界。实现过程中监控 `legion_tree`,通过邮件解决必要的协作,不以邮件取代共享工件。
|
|
34
|
-
- 组员声明完成后,按结果收集 session/verdict;未收齐所有 worker
|
|
38
|
+
- 组员声明完成后,按结果收集 session/verdict;未收齐所有 worker 结果不得请求离开本阶段。
|
|
35
39
|
|
|
36
|
-
### reviewing
|
|
40
|
+
### 审查阶段(内置 `reviewing`)
|
|
37
41
|
|
|
38
|
-
-
|
|
39
|
-
- `
|
|
40
|
-
- 每个 `failed`
|
|
41
|
-
- 被 `legion_phase`
|
|
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
|
-
-
|
|
46
|
-
- done
|
|
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
|
|
52
|
-
-
|
|
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,
|
|
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
9
|
timeoutMs: 2700000
|
|
10
|
-
allowNestedSubagents: true
|
|
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:
|
|
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
|
-
-
|
|
40
|
-
|
|
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
|
|
48
|
+
- 全部步骤完成、交活前才跑一次项目的**全量档**(完整测试套件/CI 等效命令);交活后插件会自动再验证一遍判定验收,不需要中途反复跑全量。全量失败按「失败止损」处理:重试不超过 2 次,仍失败则 `ok: false` 上报。
|
|
51
49
|
- 项目找不到任何验证入口时,在报告里写明依据(查过哪些地方),不要自己编命令冒充。
|
|
52
50
|
3. 把最终那次 full 验证输出**原样**(含命令与结果)放进最终报告。
|
|
53
51
|
4. 交活前逐文件重读完整 diff:检查遗留的调试代码、console.log/print、注释掉的老逻辑、无关改动混入;发现问题先修。
|