@sjawhar/pi-legion-envoy 1.42.0 → 1.42.2
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/dist/envoy.js
CHANGED
|
@@ -30175,7 +30175,7 @@ var HANDOFF_SCHEMA_VERSION = 1;
|
|
|
30175
30175
|
var HANDOFF_PHASES = ["architect", "plan", "implement", "test", "review"];
|
|
30176
30176
|
var isoTimestamp = string2().regex(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}/);
|
|
30177
30177
|
var handoffPhase = _enum2(HANDOFF_PHASES);
|
|
30178
|
-
var nonEmpty = string2().min(1);
|
|
30178
|
+
var nonEmpty = string2().trim().min(1);
|
|
30179
30179
|
var proofSchema = object({
|
|
30180
30180
|
criterion: nonEmpty,
|
|
30181
30181
|
surface: nonEmpty,
|
|
@@ -30195,7 +30195,7 @@ var baseHandoffSchema = object({
|
|
|
30195
30195
|
completed: isoTimestamp,
|
|
30196
30196
|
learningsInjected: array(string2()).optional(),
|
|
30197
30197
|
learningsHelpful: array(string2()).optional()
|
|
30198
|
-
});
|
|
30198
|
+
}).passthrough();
|
|
30199
30199
|
var architectSchema = baseHandoffSchema.extend({
|
|
30200
30200
|
phase: literal("architect"),
|
|
30201
30201
|
scope: _enum2(["trivial", "small", "medium", "large"]).optional(),
|
|
@@ -30203,7 +30203,7 @@ var architectSchema = baseHandoffSchema.extend({
|
|
|
30203
30203
|
subIssues: array(string2()).optional(),
|
|
30204
30204
|
routingHints: routingHintsSchema,
|
|
30205
30205
|
concerns: array(string2()).optional()
|
|
30206
|
-
})
|
|
30206
|
+
});
|
|
30207
30207
|
var requiredSkillsSchema = object({
|
|
30208
30208
|
implement: array(string2()).optional(),
|
|
30209
30209
|
test: array(string2()).optional(),
|
|
@@ -30217,7 +30217,7 @@ var planSchema = baseHandoffSchema.extend({
|
|
|
30217
30217
|
concerns: array(string2()).optional(),
|
|
30218
30218
|
workflowRecommendation: string2().optional(),
|
|
30219
30219
|
requiredSkills: requiredSkillsSchema
|
|
30220
|
-
})
|
|
30220
|
+
});
|
|
30221
30221
|
var implementSchema = baseHandoffSchema.extend({
|
|
30222
30222
|
phase: literal("implement"),
|
|
30223
30223
|
filesChanged: array(string2()).optional(),
|
|
@@ -30228,7 +30228,7 @@ var implementSchema = baseHandoffSchema.extend({
|
|
|
30228
30228
|
subPlanningNeeded: boolean2().optional(),
|
|
30229
30229
|
discoveredComplexity: array(string2()).optional(),
|
|
30230
30230
|
suggestedSubWorkers: number2().optional()
|
|
30231
|
-
})
|
|
30231
|
+
});
|
|
30232
30232
|
var testSchema = baseHandoffSchema.extend({
|
|
30233
30233
|
phase: literal("test"),
|
|
30234
30234
|
passed: number2().optional(),
|
|
@@ -30238,9 +30238,15 @@ var testSchema = baseHandoffSchema.extend({
|
|
|
30238
30238
|
proof: array(proofSchema).min(1).optional(),
|
|
30239
30239
|
documentationFeedback: string2().optional(),
|
|
30240
30240
|
observations: array(string2()).optional()
|
|
30241
|
-
}).
|
|
30241
|
+
}).refine((handoff) => (handoff.failures?.length ?? 0) > 0 || (handoff.failed ?? 0) > 0 || (handoff.proof?.length ?? 0) > 0, {
|
|
30242
30242
|
path: ["proof"],
|
|
30243
30243
|
message: "a passing test handoff needs the tester's own production-like proof"
|
|
30244
|
+
}).refine((handoff) => (handoff.failed ?? 0) === 0 || (handoff.failures?.length ?? 0) > 0, {
|
|
30245
|
+
path: ["failures"],
|
|
30246
|
+
message: "a test handoff that reports failed > 0 records at least one failure"
|
|
30247
|
+
}).refine((handoff) => handoff.implementerProof.verdict !== "rejected" || (handoff.failures?.length ?? 0) > 0, {
|
|
30248
|
+
path: ["failures"],
|
|
30249
|
+
message: "a rejected implementer proof is a recorded failure"
|
|
30244
30250
|
});
|
|
30245
30251
|
var reviewSchema = baseHandoffSchema.extend({
|
|
30246
30252
|
phase: literal("review"),
|
|
@@ -30249,7 +30255,7 @@ var reviewSchema = baseHandoffSchema.extend({
|
|
|
30249
30255
|
minor: number2().optional(),
|
|
30250
30256
|
verdict: _enum2(["approved", "changes_requested"]).optional(),
|
|
30251
30257
|
keyFindings: array(object({ severity: string2(), file: string2(), description: string2() }).passthrough()).optional()
|
|
30252
|
-
})
|
|
30258
|
+
});
|
|
30253
30259
|
var phaseHandoffSchema = discriminatedUnion("phase", [
|
|
30254
30260
|
architectSchema,
|
|
30255
30261
|
planSchema,
|
package/dist/legion.js
CHANGED
|
@@ -29449,7 +29449,7 @@ var HANDOFF_SCHEMA_VERSION = 1;
|
|
|
29449
29449
|
var HANDOFF_PHASES = ["architect", "plan", "implement", "test", "review"];
|
|
29450
29450
|
var isoTimestamp = string2().regex(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}/);
|
|
29451
29451
|
var handoffPhase = _enum2(HANDOFF_PHASES);
|
|
29452
|
-
var nonEmpty = string2().min(1);
|
|
29452
|
+
var nonEmpty = string2().trim().min(1);
|
|
29453
29453
|
var proofSchema = object({
|
|
29454
29454
|
criterion: nonEmpty,
|
|
29455
29455
|
surface: nonEmpty,
|
|
@@ -29469,7 +29469,7 @@ var baseHandoffSchema = object({
|
|
|
29469
29469
|
completed: isoTimestamp,
|
|
29470
29470
|
learningsInjected: array(string2()).optional(),
|
|
29471
29471
|
learningsHelpful: array(string2()).optional()
|
|
29472
|
-
});
|
|
29472
|
+
}).passthrough();
|
|
29473
29473
|
var architectSchema = baseHandoffSchema.extend({
|
|
29474
29474
|
phase: literal("architect"),
|
|
29475
29475
|
scope: _enum2(["trivial", "small", "medium", "large"]).optional(),
|
|
@@ -29477,7 +29477,7 @@ var architectSchema = baseHandoffSchema.extend({
|
|
|
29477
29477
|
subIssues: array(string2()).optional(),
|
|
29478
29478
|
routingHints: routingHintsSchema,
|
|
29479
29479
|
concerns: array(string2()).optional()
|
|
29480
|
-
})
|
|
29480
|
+
});
|
|
29481
29481
|
var requiredSkillsSchema = object({
|
|
29482
29482
|
implement: array(string2()).optional(),
|
|
29483
29483
|
test: array(string2()).optional(),
|
|
@@ -29491,7 +29491,7 @@ var planSchema = baseHandoffSchema.extend({
|
|
|
29491
29491
|
concerns: array(string2()).optional(),
|
|
29492
29492
|
workflowRecommendation: string2().optional(),
|
|
29493
29493
|
requiredSkills: requiredSkillsSchema
|
|
29494
|
-
})
|
|
29494
|
+
});
|
|
29495
29495
|
var implementSchema = baseHandoffSchema.extend({
|
|
29496
29496
|
phase: literal("implement"),
|
|
29497
29497
|
filesChanged: array(string2()).optional(),
|
|
@@ -29502,7 +29502,7 @@ var implementSchema = baseHandoffSchema.extend({
|
|
|
29502
29502
|
subPlanningNeeded: boolean2().optional(),
|
|
29503
29503
|
discoveredComplexity: array(string2()).optional(),
|
|
29504
29504
|
suggestedSubWorkers: number2().optional()
|
|
29505
|
-
})
|
|
29505
|
+
});
|
|
29506
29506
|
var testSchema = baseHandoffSchema.extend({
|
|
29507
29507
|
phase: literal("test"),
|
|
29508
29508
|
passed: number2().optional(),
|
|
@@ -29512,9 +29512,15 @@ var testSchema = baseHandoffSchema.extend({
|
|
|
29512
29512
|
proof: array(proofSchema).min(1).optional(),
|
|
29513
29513
|
documentationFeedback: string2().optional(),
|
|
29514
29514
|
observations: array(string2()).optional()
|
|
29515
|
-
}).
|
|
29515
|
+
}).refine((handoff) => (handoff.failures?.length ?? 0) > 0 || (handoff.failed ?? 0) > 0 || (handoff.proof?.length ?? 0) > 0, {
|
|
29516
29516
|
path: ["proof"],
|
|
29517
29517
|
message: "a passing test handoff needs the tester's own production-like proof"
|
|
29518
|
+
}).refine((handoff) => (handoff.failed ?? 0) === 0 || (handoff.failures?.length ?? 0) > 0, {
|
|
29519
|
+
path: ["failures"],
|
|
29520
|
+
message: "a test handoff that reports failed > 0 records at least one failure"
|
|
29521
|
+
}).refine((handoff) => handoff.implementerProof.verdict !== "rejected" || (handoff.failures?.length ?? 0) > 0, {
|
|
29522
|
+
path: ["failures"],
|
|
29523
|
+
message: "a rejected implementer proof is a recorded failure"
|
|
29518
29524
|
});
|
|
29519
29525
|
var reviewSchema = baseHandoffSchema.extend({
|
|
29520
29526
|
phase: literal("review"),
|
|
@@ -29523,7 +29529,7 @@ var reviewSchema = baseHandoffSchema.extend({
|
|
|
29523
29529
|
minor: number2().optional(),
|
|
29524
29530
|
verdict: _enum2(["approved", "changes_requested"]).optional(),
|
|
29525
29531
|
keyFindings: array(object({ severity: string2(), file: string2(), description: string2() }).passthrough()).optional()
|
|
29526
|
-
})
|
|
29532
|
+
});
|
|
29527
29533
|
var phaseHandoffSchema = discriminatedUnion("phase", [
|
|
29528
29534
|
architectSchema,
|
|
29529
29535
|
planSchema,
|
|
@@ -485,7 +485,10 @@ dispatch_message({
|
|
|
485
485
|
|
|
486
486
|
`in_reply_to` correlates the answer under the asker's message in its Conversation card. A BTW
|
|
487
487
|
delivery can post its answer automatically; use this call when the frame asks the primary agent to
|
|
488
|
-
reply.
|
|
488
|
+
reply. A human may reply to your message in turn — the follow-up arrives as a targeted frame whose
|
|
489
|
+
`in_reply_to` names your message and whose `reply_body` quotes it; answer it the same way,
|
|
490
|
+
`dispatch_message({ issue, in_reply_to: "<their reply id>", body })`, so the exchange reads as one
|
|
491
|
+
thread. `dispatch_message` itself never carries `target` or `delivery`: agent-to-agent traffic goes
|
|
489
492
|
through Envoy or the hub. A bearer that targets over HTTP names its own session in `actor`
|
|
490
493
|
(`{kind: "session", id}`), and the card shows that session as the author. `GET /api/v1/agents`
|
|
491
494
|
(any authenticated caller) lists live sessions with their capabilities (`aside`, `btw`, `steer`);
|
|
@@ -99,7 +99,7 @@ dispatch_message({
|
|
|
99
99
|
|
|
100
100
|
**Proofs read:** implementer <surface/command>, tester <surface/command>.
|
|
101
101
|
|
|
102
|
-
**Production check:** <what the implementer will drive after the merge, or the
|
|
102
|
+
**Production check:** <what the implementer will drive after the merge, or the deploy/restart step a human will have to perform first>
|
|
103
103
|
|
|
104
104
|
<!-- legion: {"session":"<session-id>","phase":"retro"} -->`,
|
|
105
105
|
})
|
|
@@ -280,6 +280,25 @@ Verified the implementer's proof by <re-running its command | driving the same s
|
|
|
280
280
|
**Retarget:** Retargeting a pull request to a new base does not re-run Tests; after a retarget, rebase onto the new base and push — the new head runs Tests against the new merge result — and cite that run in the PR body.
|
|
281
281
|
```
|
|
282
282
|
|
|
283
|
+
**A proof** is the changed behaviour exercised on the surface a user reaches it through, recorded
|
|
284
|
+
as the exact command or run id, what was observed, the head SHA, and one negative control —
|
|
285
|
+
a deliberately broken input and the refusal or failure it produced. The surface is
|
|
286
|
+
**production-like** — the repository's real-process test harness and fixtures, a sandbox
|
|
287
|
+
repository, a real browser, a devN stack, staging, or a local stack with real migrations, one that
|
|
288
|
+
has the resource the change touches — and each `E2E` line carries a **link** to that run,
|
|
289
|
+
screenshot, or e2e; the merge queue does not approve a user-facing change without it, and a
|
|
290
|
+
green unit suite is not it. A unit or integration test is a regression lock, never proof of a
|
|
291
|
+
criterion. Sami, 2026-09-13, verbatim: "They need to test everything in a production-like
|
|
292
|
+
environment before merging, and it is the agent that develops the feature that is responsible
|
|
293
|
+
for doing that. If there's anything blocking that, we need to fix it: if it's infrastructure, we
|
|
294
|
+
need to fix it; if it's tooling, we need to develop it; if it's skills, we need to fix the skills
|
|
295
|
+
... it should not require deploying to production to realize your feature doesn't work."
|
|
296
|
+
Evidence for the rule: in the week of 2026-09-08 three surfaces merged green and were wrong on
|
|
297
|
+
inspection (the Astrolabe IPI stack, Dispatch on ECS, the candidate flow), and on 2026-09-12 six
|
|
298
|
+
deploy slots died on code first executed after merge, including a production-only ECS bootstrap
|
|
299
|
+
the whole staging gate never ran. The implementer's proof and the tester's proof below are both
|
|
300
|
+
this proof.
|
|
301
|
+
|
|
283
302
|
- **Threads are dispositioned individually, never resolved in bulk.** Every open review
|
|
284
303
|
thread gets its own line naming the fixing commit or the reason it isn't a defect. The
|
|
285
304
|
reviewer answers each thread it opened with exactly one of `Accepted: fixed in <commit> — <one line>`,
|
|
@@ -319,40 +338,23 @@ Verified the implementer's proof by <re-running its command | driving the same s
|
|
|
319
338
|
field names naming, duplication, or wording cleanup only; anything that changes behaviour,
|
|
320
339
|
hides an error, or breaks a gate lands in this PR.
|
|
321
340
|
- **The implementer proves the change before its phase completes, and writes the `E2E (implementer)` line when the pull request opens.**
|
|
322
|
-
The proof is the
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
one negative control. The same proof goes into `.legion/implement.json` as its required `proof`
|
|
327
|
-
array (`legion handoff write --phase implement` refuses a payload without one and names the
|
|
328
|
-
field), and into the PR body, because the reviewer and the merger verify facts on GitHub and
|
|
329
|
-
never from a handoff. A unit or integration test is a regression lock, never proof of a
|
|
330
|
-
criterion.
|
|
341
|
+
The proof is the one defined above. It goes into `.legion/implement.json` as the required `proof`
|
|
342
|
+
array (`legion handoff write --phase implement` refuses a payload without one, or with a blank or
|
|
343
|
+
whitespace-only field, and names the field), and into the PR body, because the reviewer and the
|
|
344
|
+
merger verify facts on GitHub and never from a handoff.
|
|
331
345
|
- **The tester verifies the implementer's proof and adds its own `E2E (tester)` line.** It re-runs
|
|
332
|
-
the implementer's command or drives the same surface independently, records the verdict in
|
|
333
|
-
`.legion/test.json` as `implementerProof` (`{verdict, how}`)
|
|
334
|
-
|
|
346
|
+
the implementer's command or drives the same surface independently, and records the verdict in
|
|
347
|
+
`.legion/test.json` as `implementerProof` (`{verdict, how}`).
|
|
348
|
+
A test handoff whose predecessor carried no proof is a test failure, not a gap for the tester to fill:
|
|
335
349
|
record it in `failures` with `implementerProof.verdict: "rejected"`, complete the phase, and let
|
|
336
350
|
the architect return the issue to the implementer — the agent that developed the change owns
|
|
337
|
-
proving it
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
has the resource the change touches — and each `E2E` line carries a **link** to that run,
|
|
342
|
-
screenshot, or e2e; the merge queue does not approve a user-facing change without it, and a
|
|
343
|
-
green unit suite is not it. Sami, 2026-09-13, verbatim: "They need to test everything in a
|
|
344
|
-
production-like environment before merging, and it is the agent that develops the feature
|
|
345
|
-
that is responsible for doing that. If there's anything blocking that, we need to fix it: if
|
|
346
|
-
it's infrastructure, we need to fix it; if it's tooling, we need to develop it; if it's
|
|
347
|
-
skills, we need to fix the skills ... it should not require deploying to production to
|
|
348
|
-
realize your feature doesn't work." A code path whose first execution is after merge — a
|
|
351
|
+
proving it (`legion handoff write --phase test` refuses a rejected verdict, or `failed > 0`,
|
|
352
|
+
with no recorded failure). Otherwise, add your own proof before completing — a proof as defined
|
|
353
|
+
above — as the `E2E (tester)` line and the `proof` array `legion handoff write --phase test`
|
|
354
|
+
requires whenever you report no failure. A code path whose first execution is after merge — a
|
|
349
355
|
deploy workflow's inline step, a post-merge helper, a production-only resource — is untested
|
|
350
356
|
until the implementer has executed it against a devN stack; if no surface can reach it, the
|
|
351
|
-
tester names that missing surface as the blocker instead of passing the phase.
|
|
352
|
-
the rule: in the week of 2026-09-08 three surfaces merged green and were wrong on inspection
|
|
353
|
-
(the Astrolabe IPI stack, Dispatch on ECS, the candidate flow), and on 2026-09-12 six deploy
|
|
354
|
-
slots died on code first executed after merge, including a production-only ECS bootstrap the
|
|
355
|
-
whole staging gate never ran. Environment or
|
|
357
|
+
tester names that missing surface as the blocker instead of passing the phase. Environment or
|
|
356
358
|
secret-scrub evidence (e.g. "`LEGION_*`/`DISPATCH_*`/`ENVOY_*` unset") is recorded once, in
|
|
357
359
|
`.legion/test.json`, and only when the issue's acceptance criteria call for it — never
|
|
358
360
|
re-pasted into the PR body each round. After a conflict-forced rebase, compute the
|
|
@@ -536,10 +538,10 @@ and, for the implementer, until the push has too. This is the committed copy the
|
|
|
536
538
|
reads after revival. It is removed once, at the end of a clean review: the implementer pushes
|
|
537
539
|
that deletion at the reviewer's direction. No other phase removes it — and once it is gone
|
|
538
540
|
(`jj -R "$LEGION_WORKSPACE" file list -r @- .legion` prints nothing on stdout; jj warns on
|
|
539
|
-
stderr), this gate no longer applies: a later rebase, bare-gate re-check, confirmation, or
|
|
540
|
-
writes no `.legion/<phase>.json`, commits no handoff, and reports
|
|
541
|
-
alone (below). Recreating `.legion/` after its deletion changes the
|
|
542
|
-
the review loop this rule exists to end.
|
|
541
|
+
stderr), this gate no longer applies: a later rebase, bare-gate re-check, confirmation, retro, or
|
|
542
|
+
the post-merge production check writes no `.legion/<phase>.json`, commits no handoff, and reports
|
|
543
|
+
with `legion handoff complete` alone (below). Recreating `.legion/` after its deletion changes the
|
|
544
|
+
approved head and restarts the review loop this rule exists to end.
|
|
543
545
|
|
|
544
546
|
## Completion: report to the architect, then stay
|
|
545
547
|
|