opencode-plugin-flow 4.3.7 → 4.3.9
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 +43 -0
- package/README.md +14 -6
- package/dist/adapters/opencode/tools.d.ts +97 -0
- package/dist/cli.js +332 -15
- package/dist/cli.js.map +2 -2
- package/dist/index.js +614 -60
- package/dist/index.js.map +5 -5
- package/dist/runtime/api.d.ts +77 -0
- package/dist/runtime/schema.d.ts +729 -1
- package/dist/runtime/transitions.d.ts +51 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -35,6 +35,12 @@ success | partial | blocked
|
|
|
35
35
|
## Scope
|
|
36
36
|
<owned slice: path set, module, command, risk lens, route, data range, or question set>
|
|
37
37
|
|
|
38
|
+
## Pass metadata
|
|
39
|
+
- Pass id: <stable pass id from the manifest>
|
|
40
|
+
- Manifest row id: <row id from the manifest>
|
|
41
|
+
- Depends on: <upstream row ids or "none">
|
|
42
|
+
- Write scope: <none | manager-serial | exact-path | isolated-worktree | mixed>
|
|
43
|
+
|
|
38
44
|
## Coverage
|
|
39
45
|
- Expected: <files, ranges, questions, commands, or findings assigned>
|
|
40
46
|
- Checked: <actual coverage, for example "12/12 files" or "command not run">
|
|
@@ -89,6 +95,11 @@ success | partial | blocked
|
|
|
89
95
|
## Scope
|
|
90
96
|
<claim ids, sources or commands checked, and the acceptance question>
|
|
91
97
|
|
|
98
|
+
## Pass metadata
|
|
99
|
+
- Pass id: <stable pass id from the manifest>
|
|
100
|
+
- Manifest row id: <row id from the manifest>
|
|
101
|
+
- Depends on: <upstream row ids or "none">
|
|
102
|
+
|
|
92
103
|
## Verdict per claim
|
|
93
104
|
- <claim id>: verdict=<supported | partly-supported | unsupported | source-not-found>
|
|
94
105
|
- claim: <claim text>
|
|
@@ -119,6 +130,12 @@ success | partial | blocked
|
|
|
119
130
|
## Scope
|
|
120
131
|
<isolated worktree or exact path-owned slice>
|
|
121
132
|
|
|
133
|
+
## Pass metadata
|
|
134
|
+
- Pass id: <stable pass id from the manifest>
|
|
135
|
+
- Manifest row id: <row id from the manifest>
|
|
136
|
+
- Depends on: <upstream row ids or "none">
|
|
137
|
+
- Write scope: <exact-path | isolated-worktree>
|
|
138
|
+
|
|
122
139
|
## Changed or proposed patch
|
|
123
140
|
- <path>: <what changed and why>
|
|
124
141
|
|
|
@@ -145,6 +162,48 @@ live-verified | test-verified | type-check-only | not-verified
|
|
|
145
162
|
|
|
146
163
|
The manager must inspect and validate any candidate patch before recording Flow
|
|
147
164
|
completion.
|
|
165
|
+
|
|
166
|
+
## Manager pass accounting record
|
|
167
|
+
|
|
168
|
+
The manager, not the worker, may carry compact records into
|
|
169
|
+
\`flow_feature_complete.orchestrationPasses\`. Use one record per material pass or
|
|
170
|
+
implementation decision; keep handoffs and long artifacts outside \`.flow/**\`.
|
|
171
|
+
The candidate accounting rules — which \`candidateEligibility\`,
|
|
172
|
+
\`candidateDecision\`, and \`decision\` combinations validate, and what counts as
|
|
173
|
+
candidate execution evidence — live in
|
|
174
|
+
[parallel-orchestration.md](parallel-orchestration.md) under "Implementation
|
|
175
|
+
pass decision"; note \`decision: "parallel"\` is not valid on
|
|
176
|
+
\`implementation-decision\` records.
|
|
177
|
+
|
|
178
|
+
\`\`\`json
|
|
179
|
+
{
|
|
180
|
+
"id": "stable-pass-id",
|
|
181
|
+
"kind": "discovery | audit | review | validation | verification | candidate | implementation-decision",
|
|
182
|
+
"decision": "serial | parallel | candidate-exact-path | candidate-worktree | tournament | skipped",
|
|
183
|
+
"decisionReason": "why this pass shape was chosen",
|
|
184
|
+
"candidateEligibility": "eligible | not_eligible | unknown",
|
|
185
|
+
"candidateDecision": "used | skipped | serial_required",
|
|
186
|
+
"decisionFactors": [
|
|
187
|
+
"shared_state",
|
|
188
|
+
"overlapping_files",
|
|
189
|
+
"small_slice",
|
|
190
|
+
"needs_manager_judgment",
|
|
191
|
+
"independent_surface",
|
|
192
|
+
"validation_available"
|
|
193
|
+
],
|
|
194
|
+
"modes": ["evidence"],
|
|
195
|
+
"workerCount": 1,
|
|
196
|
+
"candidateWorkerCount": 0,
|
|
197
|
+
"verifierWorkerCount": 0,
|
|
198
|
+
"sliceIds": ["manifest-row-id"],
|
|
199
|
+
"dependsOn": [],
|
|
200
|
+
"writeScope": "none | manager-serial | exact-path | isolated-worktree | mixed",
|
|
201
|
+
"handoffRefs": ["/tmp/flow-handoff.md"],
|
|
202
|
+
"verificationStatus": "not-needed | pending | passed | failed | mixed | downgraded",
|
|
203
|
+
"outcome": "accepted | modified | rejected | partial | not-covered | superseded",
|
|
204
|
+
"synthesisRef": "/tmp/flow-synthesis.md"
|
|
205
|
+
}
|
|
206
|
+
\`\`\`
|
|
148
207
|
`;
|
|
149
208
|
|
|
150
209
|
// skills/flow/references/parallel-orchestration.md
|
|
@@ -197,6 +256,99 @@ Pass notes:
|
|
|
197
256
|
or exact non-overlapping path ownership. Patches stay proposals until the
|
|
198
257
|
manager inspects, merges or rejects, and validates.
|
|
199
258
|
|
|
259
|
+
## Implementation pass decision
|
|
260
|
+
|
|
261
|
+
Before implementing a broad, risky, or multi-target feature, record one manager
|
|
262
|
+
decision. This is required even when the answer is "stay serial"; the point is
|
|
263
|
+
to make the skipped parallelism visible instead of relying on memory.
|
|
264
|
+
|
|
265
|
+
First classify candidate eligibility:
|
|
266
|
+
|
|
267
|
+
- \`eligible\`: at least one slice is independent enough for a candidate worker.
|
|
268
|
+
- \`not_eligible\`: worker isolation would not make the implementation safer or
|
|
269
|
+
cheaper because the slice shares state, files, tests, or one mental model.
|
|
270
|
+
- \`unknown\`: orientation did not produce enough evidence to classify; use this
|
|
271
|
+
only for non-decision pass rows or legacy low-signal records, not for
|
|
272
|
+
\`implementation-decision\` records and not as a substitute for judgment.
|
|
273
|
+
|
|
274
|
+
Then record the candidate decision:
|
|
275
|
+
|
|
276
|
+
- \`used\`: candidate workers were used or a candidate pass carried the work.
|
|
277
|
+
- \`skipped\`: candidates were eligible, but the manager chose serial anyway.
|
|
278
|
+
This is the underused-parallelism signal counted by Flow status; use it only
|
|
279
|
+
on \`kind: "implementation-decision"\` records.
|
|
280
|
+
- \`serial_required\`: candidates were not eligible, so serial work was the
|
|
281
|
+
correct implementation shape. Use it only on \`kind: "implementation-decision"\`
|
|
282
|
+
records.
|
|
283
|
+
|
|
284
|
+
Use one of these decisions:
|
|
285
|
+
|
|
286
|
+
- \`serial\`: the manager implements directly because slices overlap, the next
|
|
287
|
+
edit depends on one shared contract, or prompt/merge overhead would exceed the
|
|
288
|
+
value. Pair unsafe or not-useful worker cases with
|
|
289
|
+
\`candidateEligibility: "not_eligible"\` and
|
|
290
|
+
\`candidateDecision: "serial_required"\`.
|
|
291
|
+
- \`candidate-exact-path\`: one or more candidate workers may edit exact
|
|
292
|
+
non-overlapping paths or modules named by the manager.
|
|
293
|
+
- \`candidate-worktree\`: one or more candidate workers may edit in isolated
|
|
294
|
+
worktrees, then the manager inspects and merges or rejects.
|
|
295
|
+
- \`tournament\`: several isolated candidate implementations compete for the same
|
|
296
|
+
outcome; the manager filters by tests, review, and source inspection before
|
|
297
|
+
accepting one.
|
|
298
|
+
- Candidate-shaped decisions (\`candidate-exact-path\`, \`candidate-worktree\`,
|
|
299
|
+
\`tournament\`) require candidate execution evidence on the same record:
|
|
300
|
+
\`kind: "candidate"\`, \`modes\` includes \`candidate-implementation\`, or
|
|
301
|
+
\`candidateWorkerCount > 0\`. The same evidence rule applies to
|
|
302
|
+
\`candidateDecision: "used"\`. Non-decision candidate rows (for example
|
|
303
|
+
\`kind: "candidate"\`) may omit \`decision\`; \`implementation-decision\` rows must
|
|
304
|
+
always set one, and when \`candidateDecision\` is \`"used"\` that decision must
|
|
305
|
+
be candidate-shaped — never \`serial\`, \`parallel\`, or \`skipped\`.
|
|
306
|
+
- \`parallel\` describes multi-worker read or audit passes (discovery, audit,
|
|
307
|
+
review); it is not a valid \`implementation-decision\` value. Implementation
|
|
308
|
+
decisions use \`serial\`, \`skipped\`, or a candidate-shaped decision.
|
|
309
|
+
- \`skipped\`: candidate workers were eligible but the manager chose serial
|
|
310
|
+
anyway; pair this with \`candidateEligibility: "eligible"\` and
|
|
311
|
+
\`candidateDecision: "skipped"\`. Do not use \`skipped\` for shared fixtures,
|
|
312
|
+
shared API contracts, unclear ownership, or other unsafe worker cases; use
|
|
313
|
+
\`serial\` plus \`serial_required\` for those.
|
|
314
|
+
|
|
315
|
+
Use structured \`decisionFactors\` alongside prose \`decisionReason\`:
|
|
316
|
+
\`shared_state\`, \`overlapping_files\`, \`small_slice\`,
|
|
317
|
+
\`needs_manager_judgment\`, \`independent_surface\`, and
|
|
318
|
+
\`validation_available\`. Serial-required records usually cite
|
|
319
|
+
\`shared_state\`, \`overlapping_files\`, or \`needs_manager_judgment\`; eligible
|
|
320
|
+
records usually cite \`independent_surface\` and \`validation_available\`, with
|
|
321
|
+
\`small_slice\` explaining an eligible-but-skipped choice.
|
|
322
|
+
|
|
323
|
+
### Worker decision rubric
|
|
324
|
+
|
|
325
|
+
Default to considering candidate workers when:
|
|
326
|
+
|
|
327
|
+
- the plan has three or more features.
|
|
328
|
+
- features touch separate surfaces such as frontend, core, docs, release
|
|
329
|
+
scripts, tests, or bindings.
|
|
330
|
+
- validation can run per slice.
|
|
331
|
+
- the work is mostly additive or localized.
|
|
332
|
+
- the final manager can review, apply, adapt, or reject the result safely.
|
|
333
|
+
|
|
334
|
+
Prefer serial when:
|
|
335
|
+
|
|
336
|
+
- one tight invariant crosses shared files.
|
|
337
|
+
- migrations, persistence, storage, or lifecycle semantics require one mental
|
|
338
|
+
model.
|
|
339
|
+
- tests require iterative local debugging in one checkout.
|
|
340
|
+
- multiple slices would edit the same files or fixtures.
|
|
341
|
+
- the slice is so small that prompt, handoff, merge, and verification overhead
|
|
342
|
+
costs more than direct work.
|
|
343
|
+
|
|
344
|
+
Record the decision in the pass manifest with a stable pass id,
|
|
345
|
+
\`candidateEligibility\`, \`candidateDecision\`, \`decisionFactors\`,
|
|
346
|
+
\`decisionReason\`, \`writeScope\`, expected verification, and where any handoff or
|
|
347
|
+
synthesis artifact will live. If the feature completes, include the compact
|
|
348
|
+
record in the \`orchestrationPasses\` array of the \`flow_feature_complete\`
|
|
349
|
+
payload. The runtime stores only compact accounting; full worker handoffs stay
|
|
350
|
+
in manager-owned scratch files or the conversation.
|
|
351
|
+
|
|
200
352
|
## When to stay serial
|
|
201
353
|
|
|
202
354
|
- One file, command, or design question determines the next step.
|
|
@@ -272,6 +424,12 @@ schemas, docs, tests, commands, or artifacts to identify real slices. Keep the
|
|
|
272
424
|
immediate blocker local: do not delegate the question that determines whether
|
|
273
425
|
fan-out is even valid.
|
|
274
426
|
|
|
427
|
+
Treat orientation as uncertainty reduction. Resolve environment uncertainty by
|
|
428
|
+
inspecting the repo, running cheap commands, or assigning evidence workers; ask
|
|
429
|
+
the user only when the remaining specification uncertainty would make a wrong
|
|
430
|
+
slice expensive to undo. Do not split a vague goal into workers until the
|
|
431
|
+
candidate slices have concrete targets, dependencies, and verification signals.
|
|
432
|
+
|
|
275
433
|
## Stage 2 — Slice
|
|
276
434
|
|
|
277
435
|
Split along whichever axis keeps slices independent: modules or path sets,
|
|
@@ -279,14 +437,29 @@ route or endpoint groups, risk lenses, command surfaces, data ranges, or claim
|
|
|
279
437
|
sets. Each slice needs a one-line scope, expected coverage, and a defined
|
|
280
438
|
output the manager can check.
|
|
281
439
|
|
|
440
|
+
For implementation slices, also name dependencies and write ownership before
|
|
441
|
+
spawning. A real dependency edge means the later slice waits for a verified
|
|
442
|
+
handoff or manager synthesis from the earlier slice; a shared file, fixture,
|
|
443
|
+
schema, or public contract usually means the work should stay serial unless an
|
|
444
|
+
isolated worktree is used. The manifest owns those edges through \`dependsOn\`
|
|
445
|
+
and \`writeScope\`.
|
|
446
|
+
|
|
282
447
|
## Stage 3 — Manifest (the pre-fan-out coverage gate)
|
|
283
448
|
|
|
284
449
|
Before spawning, write a pass manifest: one row per slice, plus a totals check.
|
|
450
|
+
Give the pass a stable id so later handoffs, verifier claims, and completion
|
|
451
|
+
payloads can refer to the same work without replaying chat.
|
|
285
452
|
|
|
286
|
-
|
|
|
287
|
-
| --- | --- | --- | --- | --- |
|
|
288
|
-
|
|
|
289
|
-
|
|
|
453
|
+
| Row id | Slice scope | Expected coverage | Mode | Depends on | Write scope | Verification tier | Handoff ref | Verification status | Synthesis ref |
|
|
454
|
+
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
|
|
455
|
+
| \`runtime-read\` | \`src/core/**\` plus its tests | 14 files | \`evidence\` | none | none | accept locally | pending | pending | pending |
|
|
456
|
+
| \`release-read\` | release contract: CI workflows, \`package.json\`, changelog | 6 files | \`evidence\` | none | none | verify once | pending | pending | pending |
|
|
457
|
+
|
|
458
|
+
\`writeScope\` values in runtime accounting are \`none\`, \`manager-serial\`,
|
|
459
|
+
\`exact-path\`, \`isolated-worktree\`, or \`mixed\`. Use \`manager-serial\` for a
|
|
460
|
+
recorded serial implementation decision, \`exact-path\` for disjoint candidate
|
|
461
|
+
edits in one checkout, and \`isolated-worktree\` for candidate work that must be
|
|
462
|
+
merged back by the manager.
|
|
290
463
|
|
|
291
464
|
- Count the total work items when countable: files, modules, routes, commands,
|
|
292
465
|
rows, findings, screenshots, or claims. Confirm slice counts add back to the
|
|
@@ -295,11 +468,30 @@ Before spawning, write a pass manifest: one row per slice, plus a totals check.
|
|
|
295
468
|
"all changed files plus callers" or "all public commands plus release docs."
|
|
296
469
|
- Assign each slice's verification tier now (see Stage 6). Deciding where a
|
|
297
470
|
wrong claim is expensive belongs before handoffs arrive, not after.
|
|
471
|
+
- Record dependency edges now. A row may be spawned only after every \`depends on\`
|
|
472
|
+
row it names has returned a verified handoff or a manager synthesis that
|
|
473
|
+
explicitly settles the dependency.
|
|
298
474
|
- Fix the slice map centrally before spawning if the gate does not reconcile.
|
|
299
475
|
|
|
300
476
|
The manifest is also the accounting contract for the pass: N rows spawned means
|
|
301
477
|
N handoffs collected and checked in Stage 5 before anything is synthesized.
|
|
302
478
|
|
|
479
|
+
For implementation decisions, add a manifest row even when no worker is spawned:
|
|
480
|
+
\`kind=implementation-decision\`, \`decision=serial\` or \`decision=skipped\`,
|
|
481
|
+
\`candidateEligibility\`, \`candidateDecision\`, \`decisionFactors\`,
|
|
482
|
+
\`workerCount=0\`, \`writeScope=manager-serial\`, and a concrete \`decisionReason\`.
|
|
483
|
+
Use \`decision=serial\` with \`candidateDecision=serial_required\` for ineligible
|
|
484
|
+
worker cases; reserve \`decision=skipped\` for eligible candidate work that the
|
|
485
|
+
manager chose not to delegate. When an implementation-decision row uses
|
|
486
|
+
\`candidateDecision=used\`, it must also record actual candidate execution
|
|
487
|
+
evidence: either \`modes=candidate-implementation\`, or \`candidateWorkerCount > 0\`
|
|
488
|
+
with \`workerCount\` raised to cover it — a \`workerCount=0\` row cannot carry a
|
|
489
|
+
positive \`candidateWorkerCount\`. Neither subtype count may exceed the total:
|
|
490
|
+
\`candidateWorkerCount <= workerCount\` and \`verifierWorkerCount <= workerCount\`
|
|
491
|
+
(a single worker may fill both roles).
|
|
492
|
+
This is how Flow distinguishes deliberate serial work from forgotten candidate
|
|
493
|
+
or verifier passes.
|
|
494
|
+
|
|
303
495
|
Write the manifest where it survives the pass: the conversation is enough for a
|
|
304
496
|
single bounded pass, but when a follow-up pass or a session resume is
|
|
305
497
|
plausible, persist it with the synthesis (Stage 7) so the accounting can be
|
|
@@ -312,8 +504,10 @@ Every worker prompt includes:
|
|
|
312
504
|
\`\`\`text
|
|
313
505
|
Overall goal, context only: <goal>
|
|
314
506
|
Mode: evidence | review | validation | audit | verifier | candidate-implementation
|
|
507
|
+
Pass id and manifest row id: <stable ids from the manifest>
|
|
315
508
|
Your exact slice: <paths, modules, command, claim ids, risk lens, or worktree>
|
|
316
509
|
Expected coverage: <count, paths, range, or complete question set>
|
|
510
|
+
Dependencies and write scope: <verified dependencies, if any; none | manager-serial | exact-path | isolated-worktree>
|
|
317
511
|
Do: <bounded actions>
|
|
318
512
|
Do not: call state-changing Flow tools, edit .flow/**, own sibling slices, or make the final Flow verdict.
|
|
319
513
|
Return only the Flow handoff in this exact shape:
|
|
@@ -339,6 +533,21 @@ worker that never returns, errors out, returns empty or unstructured output, or
|
|
|
339
533
|
reports \`partial\` or \`blocked\` is a hole in the pass, and synthesizing around it
|
|
340
534
|
silently drops a slice.
|
|
341
535
|
|
|
536
|
+
For each row, fill in:
|
|
537
|
+
|
|
538
|
+
- \`handoffRefs\`: worker ids, handoff file paths, command output artifacts, or
|
|
539
|
+
review packet location that the manager can re-open.
|
|
540
|
+
- \`verificationStatus\`: \`not-needed\`, \`pending\`, \`passed\`, \`failed\`, \`mixed\`,
|
|
541
|
+
or \`downgraded\`.
|
|
542
|
+
- \`outcome\`: \`accepted\`, \`modified\`, \`rejected\`, \`partial\`, \`not-covered\`, or
|
|
543
|
+
\`superseded\`.
|
|
544
|
+
- \`synthesisRef\`: the manager-owned synthesis file or plan field that carries
|
|
545
|
+
the accepted result forward.
|
|
546
|
+
|
|
547
|
+
Rows with no worker, such as serial or skipped implementation decisions, still
|
|
548
|
+
need a row id, decision, reason, and outcome. They are not handoffs, but they
|
|
549
|
+
are accounting.
|
|
550
|
+
|
|
342
551
|
Worker failure ladder:
|
|
343
552
|
|
|
344
553
|
1. Re-spawn once with a narrower slice and a note about what the first attempt
|
|
@@ -360,6 +569,12 @@ handoff only after a cheap manager-side pass:
|
|
|
360
569
|
- The evidence supports the claim, not just the topic.
|
|
361
570
|
- Findings stay inside the worker's slice.
|
|
362
571
|
- Headline counts can be recounted or traced.
|
|
572
|
+
- Dependency claims cite the verified upstream handoff, synthesis, or source
|
|
573
|
+
artifact they depend on.
|
|
574
|
+
- Candidate implementation claims identify whether they came from exact path
|
|
575
|
+
ownership or an isolated worktree, and whether the manager inspected the
|
|
576
|
+
resulting patch. Record the manager result as \`accepted\`, \`modified\`, or
|
|
577
|
+
\`rejected\` where that is the most precise candidate outcome.
|
|
363
578
|
- Contradictions between workers are either resolved or explicitly marked as
|
|
364
579
|
contested.
|
|
365
580
|
|
|
@@ -400,6 +615,11 @@ Apply the manager synthesis barrier before presenting or recording anything:
|
|
|
400
615
|
- When workers disagree, inspect the cited artifact or rerun the cited command
|
|
401
616
|
instead of arbitrating from summaries. Do not average conflicting claims.
|
|
402
617
|
- Run the strongest practical local check for the deliverable.
|
|
618
|
+
- For broad implementation sessions, use one verifier worker after manager
|
|
619
|
+
synthesis when the risk is medium or high. Ask it whether every planned
|
|
620
|
+
feature landed, worker validation claims are supported, final code matches
|
|
621
|
+
the audit finding, generated bindings/docs/version metadata stayed
|
|
622
|
+
consistent, and changed files have plausible test coverage.
|
|
403
623
|
- Re-read critical files or docs that will be cited in the final decision.
|
|
404
624
|
- Move only distilled, evidence-backed claims forward; raw handoffs remain
|
|
405
625
|
candidate evidence, not a plan, review, completion payload, or final answer.
|
|
@@ -424,6 +644,56 @@ Where accepted evidence goes:
|
|
|
424
644
|
- Candidate patches are not Flow evidence until the manager inspects, merges or
|
|
425
645
|
rejects them, and validates the main Flow-managed workspace.
|
|
426
646
|
|
|
647
|
+
When completing a feature, include compact pass accounting in
|
|
648
|
+
\`flow_feature_complete.orchestrationPasses\` for any pass or implementation
|
|
649
|
+
decision that materially affected the feature:
|
|
650
|
+
|
|
651
|
+
\`\`\`json
|
|
652
|
+
{
|
|
653
|
+
"id": "feature-id-implementation-decision",
|
|
654
|
+
"kind": "implementation-decision",
|
|
655
|
+
"decision": "serial",
|
|
656
|
+
"decisionReason": "Shared schema and tests made exact path ownership unsafe.",
|
|
657
|
+
"candidateEligibility": "not_eligible",
|
|
658
|
+
"candidateDecision": "serial_required",
|
|
659
|
+
"decisionFactors": ["shared_state", "overlapping_files"],
|
|
660
|
+
"modes": [],
|
|
661
|
+
"workerCount": 0,
|
|
662
|
+
"candidateWorkerCount": 0,
|
|
663
|
+
"verifierWorkerCount": 0,
|
|
664
|
+
"sliceIds": ["manager-implementation"],
|
|
665
|
+
"dependsOn": [],
|
|
666
|
+
"writeScope": "manager-serial",
|
|
667
|
+
"handoffRefs": [],
|
|
668
|
+
"verificationStatus": "not-needed",
|
|
669
|
+
"outcome": "accepted",
|
|
670
|
+
"synthesisRef": "/tmp/flow-pass-synthesis.md"
|
|
671
|
+
}
|
|
672
|
+
\`\`\`
|
|
673
|
+
|
|
674
|
+
For candidate and verifier passes, use \`kind: "candidate"\` or
|
|
675
|
+
\`kind: "verification"\`, list the worker modes used, worker counts, slice ids,
|
|
676
|
+
handoff refs, dependency ids, verification status, and whether the manager
|
|
677
|
+
accepted, rejected, downgraded, or superseded the pass. The runtime aggregates
|
|
678
|
+
these compact records into \`session.budget.orchestration\` and stores them on
|
|
679
|
+
the feature history entry. Do not store full handoffs, long logs, or scratch
|
|
680
|
+
tables in \`.flow/session.json\`.
|
|
681
|
+
|
|
682
|
+
Status accounting distinguishes three cases: \`candidateDecision: "used"\` means
|
|
683
|
+
candidate execution evidence was recorded, \`candidateDecision:
|
|
684
|
+
"serial_required"\` means workers were not safe or useful, and
|
|
685
|
+
\`candidateEligibility: "eligible"\` plus \`candidateDecision: "skipped"\`
|
|
686
|
+
increments \`skippedCandidateDecisionCount\`. These candidate decision counters
|
|
687
|
+
come from \`kind: "implementation-decision"\` records; \`skipped\` and
|
|
688
|
+
\`serial_required\` are not valid on discovery, audit, review, validation,
|
|
689
|
+
verification, or candidate pass rows. \`candidatePassCount\`
|
|
690
|
+
counts actual candidate pass or worker evidence (\`kind: "candidate"\`,
|
|
691
|
+
\`modes\` includes \`candidate-implementation\`, or \`candidateWorkerCount > 0\`) —
|
|
692
|
+
a candidate-shaped decision label without that evidence is rejected, so decision
|
|
693
|
+
labels alone never count. \`verifierPassCount\` similarly counts
|
|
694
|
+
actual verifier pass or worker evidence (\`kind: "verification"\`, \`modes\`
|
|
695
|
+
includes \`verifier\`, or \`verifierWorkerCount > 0\`).
|
|
696
|
+
|
|
427
697
|
Persist the manifest and the synthesis when another pass may follow or the
|
|
428
698
|
session is long enough to be compacted or resumed: write the distilled result —
|
|
429
699
|
the accounted manifest, accepted claims with evidence and confidence, dropped
|
|
@@ -442,6 +712,11 @@ Stop after a pass when:
|
|
|
442
712
|
- accepted claims are evidenced, scoped, and confidence-labeled.
|
|
443
713
|
- material single-source, contested, high-stakes, or payload-bound claims have
|
|
444
714
|
been verified or downgraded.
|
|
715
|
+
- every dependency edge named in the manifest has either a verified upstream
|
|
716
|
+
result or an explicit not-covered outcome.
|
|
717
|
+
- implementation pass decisions are recorded, including skipped candidate
|
|
718
|
+
workers, candidate eligibility, candidate decision, structured factors, and
|
|
719
|
+
the reason eligible workers were skipped.
|
|
445
720
|
- remaining gaps are explicit and do not block the Flow artifact being produced.
|
|
446
721
|
|
|
447
722
|
Start a bounded follow-up pass only when:
|
|
@@ -449,6 +724,9 @@ Start a bounded follow-up pass only when:
|
|
|
449
724
|
- the original slice map missed material scope.
|
|
450
725
|
- workers disagree on a claim that affects the Flow decision.
|
|
451
726
|
- a high-stakes or payload-bound claim needs verification.
|
|
727
|
+
- a dependency has just become verified and now unlocks a dependent slice.
|
|
728
|
+
- a candidate patch was rejected and an isolated alternative is still cheaper
|
|
729
|
+
than serial repair.
|
|
452
730
|
- a first pass exposes a narrower implementation or validation slice worth
|
|
453
731
|
isolating.
|
|
454
732
|
|
|
@@ -457,6 +735,20 @@ reason, such as a high-stakes verifier check or a newly discovered bounded
|
|
|
457
735
|
slice. Do not recurse by default: if a worker says it needs another worker, the
|
|
458
736
|
manager decides whether that is a follow-up pass and writes the next bounded
|
|
459
737
|
prompt, starting again from the manifest.
|
|
738
|
+
|
|
739
|
+
## Worker count defaults
|
|
740
|
+
|
|
741
|
+
Use caps, not a fixed feature limit:
|
|
742
|
+
|
|
743
|
+
- small implementation: zero or one worker.
|
|
744
|
+
- medium independent implementation: two workers.
|
|
745
|
+
- broad audit: three to five workers.
|
|
746
|
+
- broad implementation: two to four candidate workers, only for
|
|
747
|
+
non-overlapping slices.
|
|
748
|
+
- final verifier: one worker when risk is medium or high.
|
|
749
|
+
|
|
750
|
+
The target is not "more workers." The target is explicit accounting: Flow must
|
|
751
|
+
justify not using workers when the work was eligible.
|
|
460
752
|
`;
|
|
461
753
|
|
|
462
754
|
// skills/flow/references/parallel-pass-example.md
|
|
@@ -475,21 +767,24 @@ directory. The manager keeps the middleware local because it is one file and
|
|
|
475
767
|
anchors every other judgment.
|
|
476
768
|
|
|
477
769
|
Pass manifest: twelve countable route modules remain after the local check, and
|
|
478
|
-
4 + 3 + 5 adds back to 12 with no overlaps or gaps.
|
|
770
|
+
4 + 3 + 5 adds back to 12 with no overlaps or gaps. The pass id is
|
|
771
|
+
\`api-error-handling-read\`.
|
|
479
772
|
|
|
480
|
-
|
|
|
481
|
-
| --- | --- | --- | --- | --- |
|
|
482
|
-
|
|
|
483
|
-
|
|
|
484
|
-
|
|
|
773
|
+
| Row id | Slice scope | Expected coverage | Mode | Depends on | Write scope | Verification tier | Handoff ref | Verification status | Synthesis ref |
|
|
774
|
+
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
|
|
775
|
+
| \`routes-auth\` | auth and account routes | 4/12 modules | \`evidence\` | none | none | accept locally | pending | pending | pending |
|
|
776
|
+
| \`routes-billing\` | billing and subscription routes | 3/12 modules | \`review\` | none | none | verify once | pending | pending | pending |
|
|
777
|
+
| \`routes-admin\` | remaining content and admin routes | 5/12 modules | \`audit\` | none | none | verify once | pending | pending | pending |
|
|
485
778
|
|
|
486
779
|
Worker prompts:
|
|
487
780
|
|
|
488
781
|
\`\`\`text
|
|
489
782
|
Overall goal, context only: confirm API error handling is consistent.
|
|
490
783
|
Mode: evidence
|
|
784
|
+
Pass id and manifest row id: api-error-handling-read / routes-auth
|
|
491
785
|
Your exact slice: the four auth and account route modules under src/routes/.
|
|
492
786
|
Expected coverage: 4/4 modules.
|
|
787
|
+
Dependencies and write scope: none; none.
|
|
493
788
|
Do: report each route's error paths, status codes, and middleware usage with file:line evidence.
|
|
494
789
|
Do not: call state-changing Flow tools, edit .flow/**, own sibling slices, or make the final Flow verdict.
|
|
495
790
|
Return only the Flow handoff in this exact shape:
|
|
@@ -499,8 +794,10 @@ Return only the Flow handoff in this exact shape:
|
|
|
499
794
|
\`\`\`text
|
|
500
795
|
Overall goal, context only: confirm API error handling is consistent.
|
|
501
796
|
Mode: review
|
|
797
|
+
Pass id and manifest row id: api-error-handling-read / routes-billing
|
|
502
798
|
Your exact slice: the three billing and subscription route modules under src/routes/.
|
|
503
799
|
Expected coverage: 3/3 modules.
|
|
800
|
+
Dependencies and write scope: none; none.
|
|
504
801
|
Do: separate blocking findings from advisory notes and cite file:line evidence.
|
|
505
802
|
Do not: call state-changing Flow tools, edit .flow/**, own sibling slices, or make the final Flow verdict.
|
|
506
803
|
Return only the Flow handoff in this exact shape:
|
|
@@ -510,8 +807,10 @@ Return only the Flow handoff in this exact shape:
|
|
|
510
807
|
\`\`\`text
|
|
511
808
|
Overall goal, context only: confirm API error handling is consistent.
|
|
512
809
|
Mode: audit
|
|
810
|
+
Pass id and manifest row id: api-error-handling-read / routes-admin
|
|
513
811
|
Your exact slice: the five content and admin route modules under src/routes/.
|
|
514
812
|
Expected coverage: 5/5 modules.
|
|
813
|
+
Dependencies and write scope: none; none.
|
|
515
814
|
Do: check each claimed error path against the shared middleware contract and report divergences with evidence.
|
|
516
815
|
Do not: call state-changing Flow tools, edit .flow/**, own sibling slices, or make the final Flow verdict.
|
|
517
816
|
Return only the Flow handoff in this exact shape:
|
|
@@ -522,6 +821,9 @@ Accounting: three manifest rows spawned means three handoffs collected before
|
|
|
522
821
|
synthesis. If slice B returned \`partial\`, the manager would re-spawn it once
|
|
523
822
|
with a narrower scope, then cover it directly, and as a last resort carry it
|
|
524
823
|
into the synthesis explicitly as not-covered.
|
|
824
|
+
The manager fills \`handoffRefs\`, \`verificationStatus\`, \`outcome\`, and
|
|
825
|
+
\`synthesisRef\` for each row before any claim becomes a plan decision or review
|
|
826
|
+
payload.
|
|
525
827
|
|
|
526
828
|
Handoff checks: the manager accepts only reports with terminal status, matching
|
|
527
829
|
coverage counts, concrete file:line evidence, confidence tags, and claims inside
|
|
@@ -539,6 +841,12 @@ Final synthesis: the manager re-reads the relevant route and middleware lines,
|
|
|
539
841
|
keeps only verified or clearly labeled claims, and records one artifact such as
|
|
540
842
|
a plan decision, review payload, or docs patch. Raw handoffs and unverified
|
|
541
843
|
suggestions do not move into the next pass or user-facing answer.
|
|
844
|
+
|
|
845
|
+
If the pass shaped feature execution, the manager records compact accounting in
|
|
846
|
+
\`flow_feature_complete.orchestrationPasses\`, such as pass id
|
|
847
|
+
\`api-error-handling-read\`, kind \`review\`, worker count \`3\`, slice ids
|
|
848
|
+
\`routes-auth\`, \`routes-billing\`, and \`routes-admin\`, verification status
|
|
849
|
+
\`mixed\` or \`passed\`, and a synthesis ref pointing to the manager-owned summary.
|
|
542
850
|
`;
|
|
543
851
|
|
|
544
852
|
// skills/flow/references/recovery-playbook.md
|
|
@@ -822,15 +1130,19 @@ config, or release surfaces in the pass manifest.
|
|
|
822
1130
|
|
|
823
1131
|
Write the pass manifest and the worker prompts exactly as Stages 3 and 4 of
|
|
824
1132
|
\`../../flow/references/parallel-orchestration.md\` define them: one manifest row
|
|
825
|
-
per slice with expected coverage
|
|
826
|
-
prompt per worker naming the mode (usually
|
|
827
|
-
the expected coverage. Discovery-specific
|
|
1133
|
+
per slice with expected coverage, dependencies, write scope, and a verification
|
|
1134
|
+
tier, and a self-contained prompt per worker naming the mode (usually
|
|
1135
|
+
\`evidence\`), the exact slice, and the expected coverage. Discovery-specific
|
|
1136
|
+
rules:
|
|
828
1137
|
|
|
829
1138
|
- Workers are read-only. For validation-oriented discovery, workers may report
|
|
830
1139
|
commands that should be run, and include raw output only for commands they
|
|
831
1140
|
actually ran.
|
|
832
1141
|
- Workers cannot read reference files themselves; paste the matching handoff
|
|
833
1142
|
template from \`../../flow/references/handoff-format.md\` into the prompt.
|
|
1143
|
+
- If discovery finds later features with disjoint path ownership, preserve that
|
|
1144
|
+
fact in feature \`targets\` and \`dependsOn\` so execution can make an explicit
|
|
1145
|
+
serial or candidate-pass decision instead of rediscovering ownership.
|
|
834
1146
|
|
|
835
1147
|
## Synthesis
|
|
836
1148
|
|
|
@@ -1249,6 +1561,11 @@ to execute.
|
|
|
1249
1561
|
validation entry should name the expected test level, such as targeted unit,
|
|
1250
1562
|
integration, browser/e2e, package/build, docs/static, cleanup preservation, or
|
|
1251
1563
|
broad project gate.
|
|
1564
|
+
- When a feature may benefit from parallel implementation, make \`targets\`
|
|
1565
|
+
precise enough for later ownership decisions: name exact modules, docs,
|
|
1566
|
+
commands, or route groups, and use \`dependsOn\` to preserve prerequisite order.
|
|
1567
|
+
Broad shared-contract work should stay in one feature or an earlier foundation
|
|
1568
|
+
feature so later candidate passes can own disjoint paths safely.
|
|
1252
1569
|
- Assign \`reviewDepth\` from risk. Use \`detailed\` for persistence, migration,
|
|
1253
1570
|
concurrency, security, final-delivery-adjacent, or cross-module slices; use
|
|
1254
1571
|
\`standard\` for normal code changes; reserve \`quick\` for low-risk non-behavioral
|
|
@@ -1614,7 +1931,7 @@ Never trim failing output, relabel a failed command as passed, or use "not run"
|
|
|
1614
1931
|
`;
|
|
1615
1932
|
|
|
1616
1933
|
// skills/flow-run/SKILL.md
|
|
1617
|
-
var SKILL_default6 = '---\nname: flow-run\ndescription: "Use when an approved Flow plan has a feature to implement, validate, or complete in the v4 runtime, and the work is scoped to one active feature. For planning a goal first use flow-plan; for the full goal-to-completion loop or resuming a session use flow."\n---\n\n# Flow Run\n\nUse this skill for implementation after a Flow plan is approved. Work one feature at a time.\n\nIf `flow_run_start` is unavailable, stop and tell the user to check that `opencode-plugin-flow` is loaded in OpenCode.\n\n## Start\n\n- Call `flow_status`.\n- If `flow_status` returns a `session.resumePacket` or\n `session.budget.phaseBoundary`, stop the current autonomous loop and report\n the resume instructions. Only call `flow_run_start` with\n `phaseBoundaryAck: true` at the start of a fresh user invocation that is\n explicitly resuming the Flow session; do not acknowledge a boundary inside\n the same uninterrupted loop that created it.\n- Call `flow_run_start` with no `featureId` unless the user or plan requires a specific runnable feature.\n- Treat the returned feature as the sole scope until it is completed, blocked, or reset.\n- Helper rule: when a named helper skill is unavailable, record the gap and\n keep the corresponding claims conservative instead of simulating its checks.\n- Load `flow-deslop` for cleanup/refactor features.\n- Load `flow-ui-quality` for frontend, UX, responsive, accessibility, or visual work.\n\n## Implement\n\n- Read the feature `targets`, `summary`, `validation`, dependencies, and plan `requirements`/`decisions`.\n- Treat the feature\'s `reviewDepth` as the minimum feature-review depth that\n must be recorded in `flow_feature_complete`.\n- Keep edits scoped to the active feature. If new scope appears, stop and replan or defer it to another feature.\n- Preserve unrelated user changes in the worktree.\n- When a wrong assumption invalidates the feature, use `flow_feature_reset`; do not pile patches onto a bad path.\n- Do not stage, commit, push, amend, rebase, publish, or mutate releases as part\n of feature execution. If the user explicitly asks for commit preparation, load\n `flow-commit` only after `flow_feature_complete` has been recorded, unless the\n user explicitly asks for a WIP commit path. Keep Git boundaries separate from\n Flow state recording.\n\n## Validate\n\n- For complex validation, regression-sensitive changes, browser QA, route QA,\n failure-prone checks, unclear coverage, exploratory QA, or\n `validationRun` summarization, load `flow-test` (helper rule applies).\n- Read `references/validation-rubric.md` before completing.\n- Run the strongest practical checks for the changed behavior.\n- Record concrete command names, status, and observed results. "Tests pass" is not evidence.\n- Non-final features complete with `validationScope: "targeted"`.\n- The final feature must run a broad project-level gate and use `validationScope: "broad"`.\n\nFor broad validation research, risky changes, or unclear coverage, use\n`../flow/references/parallel-orchestration.md` to fan out named Flow workers.\nUse the mode-to-agent mapping in that reference instead of generic subagents.\nWrite its pass manifest before fan-out, paste the matching handoff template\nfrom `../flow/references/handoff-format.md` into every worker prompt, and\napply its verification tiers to the handoffs that come back.\nThey may report command output they actually ran or propose focused checks; the\nmanager decides what is strong enough to record.\n\nFor independent implementation attempts, use candidate workers only with\nexplicit user authorization plus isolated worktrees or exact non-overlapping\npath ownership. Treat their output as candidate patches. The manager inspects,\nmerges, validates, and records Flow state serially.\n\n## Review and complete\n\nBefore `flow_feature_complete`, obtain a `featureReview` payload. Load\n`flow-review`; for read-only subagent reviews, the manager receives the review\npacket and records both `featureReviewDepth` and `featureReview`.\n\nSend reviewers a compact review packet. Do not rely on the accumulated parent\nconversation. Include only:\n\n- active feature id, title, summary, `reviewDepth`, targets, validation, and dependencies\n- relevant plan requirements, decisions, and final review policy\n- changed files and a short diff summary\n- validation evidence with exact commands, status, and observed result\n- targeted paths or risk lenses the reviewer must inspect\n\nIf the review returns `status: "failed"`, do not fix inside the review pass.\nRecord the failed attempt by calling `flow_feature_complete` with the otherwise\nprepared completion payload, the failed `featureReview`, and the attempted\n`featureReviewDepth`; the runtime will reject completion and update the retry\nbudget. Default to stopping and reporting the blocker. When the user already\nauthorized autonomous implementation, make at most one repair and run one retry\nreview. If the retry fails or the runtime reports review retry budget\nexhausted, stop with the blocker.\n\nIf `flow_status` reports `setup.skills` or `flow-review` cannot be loaded, do\nnot record a Flow-gated `featureReview` or `finalReview`. You may perform an\nadvisory review using available context or the bundled review fallback provided\nby plugin config, then complete with `status: "needs_input"` if review evidence\nis required to proceed.\n\nFor the final feature, also obtain a `finalReview` payload whose `reviewDepth` equals the approved plan\'s `finalReviewPolicy`.\n\nComplete with:\n\n```json\n{\n "status": "ok",\n "featureId": "active-feature-id",\n "summary": "what changed",\n "artifactsChanged": [{ "path": "src/file.ts" }],\n "validationRun": [\n { "command": "bun test tests/foo.test.ts", "status": "passed", "summary": "3 pass, exercised foo behavior" }\n ],\n "validationScope": "targeted",\n "featureReviewDepth": "standard",\n "featureReview": { "status": "passed", "summary": "review summary", "blockingFindings": [] }\n}\n```\n\nIf `flow_feature_complete` returns a `session.resumePacket` or\n`session.budget.phaseBoundary`, stop after reporting the compact handoff. If\ngenuinely blocked, call `flow_feature_complete` with `status: "needs_input"` and\nan `outcome` that explains the blocker and next step. Never fabricate validation\nor review evidence to force progress.\n';
|
|
1934
|
+
var SKILL_default6 = '---\nname: flow-run\ndescription: "Use when an approved Flow plan has a feature to implement, validate, or complete in the v4 runtime, and the work is scoped to one active feature. For planning a goal first use flow-plan; for the full goal-to-completion loop or resuming a session use flow."\n---\n\n# Flow Run\n\nUse this skill for implementation after a Flow plan is approved. Work one feature at a time.\n\nIf `flow_run_start` is unavailable, stop and tell the user to check that `opencode-plugin-flow` is loaded in OpenCode.\n\n## Start\n\n- Call `flow_status`.\n- If `flow_status` returns a `session.resumePacket` or\n `session.budget.phaseBoundary`, stop the current autonomous loop and report\n the resume instructions. Only call `flow_run_start` with\n `phaseBoundaryAck: true` at the start of a fresh user invocation that is\n explicitly resuming the Flow session; do not acknowledge a boundary inside\n the same uninterrupted loop that created it.\n- Call `flow_run_start` with no `featureId` unless the user or plan requires a specific runnable feature.\n- Treat the returned feature as the sole scope until it is completed, blocked, or reset.\n- Helper rule: when a named helper skill is unavailable, record the gap and\n keep the corresponding claims conservative instead of simulating its checks.\n- Load `flow-deslop` for cleanup/refactor features.\n- Load `flow-ui-quality` for frontend, UX, responsive, accessibility, or visual work.\n\n## Implement\n\n- Read the feature `targets`, `summary`, `validation`, dependencies, and plan `requirements`/`decisions`.\n- Treat the feature\'s `reviewDepth` as the minimum feature-review depth that\n must be recorded in `flow_feature_complete`.\n- For broad, risky, or multi-target work, record an implementation pass\n decision before editing: `serial`, `candidate-exact-path`,\n `candidate-worktree`, `tournament`, or `skipped`. Use\n `../flow/references/parallel-orchestration.md` for the decision rules,\n manifest fields, and compact `orchestrationPasses` record.\n- Classify `candidateEligibility` (`eligible`, `not_eligible`, or `unknown`)\n and `candidateDecision` (`used`, `skipped`, or `serial_required`) separately;\n implementation decisions must use `eligible` or `not_eligible` and always set\n an explicit `decision`. The valid pairings and the candidate execution\n evidence rules are in `../flow/references/parallel-orchestration.md` under\n "Implementation pass decision" — follow that reference when composing the\n record.\n- Record structured `decisionFactors`: `shared_state`, `overlapping_files`,\n `small_slice`, `needs_manager_judgment`, `independent_surface`, and\n `validation_available`.\n- Keep edits scoped to the active feature. If new scope appears, stop and replan or defer it to another feature.\n- Preserve unrelated user changes in the worktree.\n- When a wrong assumption invalidates the feature, use `flow_feature_reset`; do not pile patches onto a bad path.\n- Do not stage, commit, push, amend, rebase, publish, or mutate releases as part\n of feature execution. If the user explicitly asks for commit preparation, load\n `flow-commit` only after `flow_feature_complete` has been recorded, unless the\n user explicitly asks for a WIP commit path. Keep Git boundaries separate from\n Flow state recording.\n\n## Validate\n\n- For complex validation, regression-sensitive changes, browser QA, route QA,\n failure-prone checks, unclear coverage, exploratory QA, or\n `validationRun` summarization, load `flow-test` (helper rule applies).\n- Read `references/validation-rubric.md` before completing.\n- Run the strongest practical checks for the changed behavior.\n- Record concrete command names, status, and observed results. "Tests pass" is not evidence.\n- Non-final features complete with `validationScope: "targeted"`.\n- The final feature must run a broad project-level gate and use `validationScope: "broad"`.\n\nFor broad validation research, risky changes, or unclear coverage, use\n`../flow/references/parallel-orchestration.md` to fan out named Flow workers.\nUse the mode-to-agent mapping in that reference instead of generic subagents.\nWrite its pass manifest before fan-out, paste the matching handoff template\nfrom `../flow/references/handoff-format.md` into every worker prompt, and\napply its verification tiers to the handoffs that come back.\nThey may report command output they actually ran or propose focused checks; the\nmanager decides what is strong enough to record.\n\nFor independent implementation attempts, use candidate workers only with\nexplicit user authorization plus isolated worktrees or exact non-overlapping\npath ownership. Treat their output as candidate patches. The manager inspects,\nmerges or rejects, validates, and records Flow state serially. Record whether a\ncandidate was `accepted`, `modified`, or `rejected`.\nWhen a candidate pass or serial/skipped implementation decision materially\nshaped the feature, include its compact record in\n`flow_feature_complete.orchestrationPasses`. Do not paste full worker handoffs\nor long logs into the runtime payload.\n\n## Review and complete\n\nBefore `flow_feature_complete`, obtain a `featureReview` payload. Load\n`flow-review`; for read-only subagent reviews, the manager receives the review\npacket and records both `featureReviewDepth` and `featureReview`.\n\nSend reviewers a compact review packet. Do not rely on the accumulated parent\nconversation. Include only:\n\n- active feature id, title, summary, `reviewDepth`, targets, validation, and dependencies\n- relevant plan requirements, decisions, and final review policy\n- changed files and a short diff summary\n- validation evidence with exact commands, status, and observed result\n- targeted paths or risk lenses the reviewer must inspect\n\nIf the review returns `status: "failed"`, do not fix inside the review pass.\nRecord the failed attempt by calling `flow_feature_complete` with the otherwise\nprepared completion payload, the failed `featureReview`, and the attempted\n`featureReviewDepth`; the runtime will reject completion and update the retry\nbudget. Default to stopping and reporting the blocker. When the user already\nauthorized autonomous implementation, make at most one repair and run one retry\nreview. If the retry fails or the runtime reports review retry budget\nexhausted, stop with the blocker.\n\nIf `flow_status` reports `setup.skills` or `flow-review` cannot be loaded, do\nnot record a Flow-gated `featureReview` or `finalReview`. You may perform an\nadvisory review using available context or the bundled review fallback provided\nby plugin config, then complete with `status: "needs_input"` if review evidence\nis required to proceed.\n\nFor the final feature, also obtain a `finalReview` payload whose `reviewDepth` equals the approved plan\'s `finalReviewPolicy`.\n\nComplete with:\n\n```json\n{\n "status": "ok",\n "featureId": "active-feature-id",\n "summary": "what changed",\n "artifactsChanged": [{ "path": "src/file.ts" }],\n "validationRun": [\n { "command": "bun test tests/foo.test.ts", "status": "passed", "summary": "3 pass, exercised foo behavior" }\n ],\n "validationScope": "targeted",\n "featureReviewDepth": "standard",\n "featureReview": { "status": "passed", "summary": "review summary", "blockingFindings": [] },\n "orchestrationPasses": [\n {\n "id": "active-feature-id-implementation-decision",\n "kind": "implementation-decision",\n "decision": "serial",\n "decisionReason": "Shared contract edits made worker ownership unsafe.",\n "candidateEligibility": "not_eligible",\n "candidateDecision": "serial_required",\n "decisionFactors": ["shared_state", "overlapping_files"],\n "writeScope": "manager-serial",\n "verificationStatus": "not-needed",\n "outcome": "accepted"\n }\n ]\n}\n```\n\nIf `flow_feature_complete` returns a `session.resumePacket` or\n`session.budget.phaseBoundary`, stop after reporting the compact handoff. If\ngenuinely blocked, call `flow_feature_complete` with `status: "needs_input"` and\nan `outcome` that explains the blocker and next step. Never fabricate validation\nor review evidence to force progress.\n';
|
|
1618
1935
|
|
|
1619
1936
|
// skills/flow-test/SKILL.md
|
|
1620
1937
|
var SKILL_default7 = `---
|
|
@@ -2635,6 +2952,182 @@ var FeatureReviewDepthSchema = z.enum([
|
|
|
2635
2952
|
"detailed"
|
|
2636
2953
|
]);
|
|
2637
2954
|
var FinalReviewPolicySchema = z.enum(["broad", "detailed"]);
|
|
2955
|
+
var OrchestrationPassKindSchema = z.enum([
|
|
2956
|
+
"discovery",
|
|
2957
|
+
"audit",
|
|
2958
|
+
"review",
|
|
2959
|
+
"validation",
|
|
2960
|
+
"verification",
|
|
2961
|
+
"candidate",
|
|
2962
|
+
"implementation-decision"
|
|
2963
|
+
]);
|
|
2964
|
+
var OrchestrationModeSchema = z.enum([
|
|
2965
|
+
"evidence",
|
|
2966
|
+
"review",
|
|
2967
|
+
"validation",
|
|
2968
|
+
"audit",
|
|
2969
|
+
"verifier",
|
|
2970
|
+
"candidate-implementation"
|
|
2971
|
+
]);
|
|
2972
|
+
var OrchestrationDecisionSchema = z.enum([
|
|
2973
|
+
"serial",
|
|
2974
|
+
"parallel",
|
|
2975
|
+
"candidate-exact-path",
|
|
2976
|
+
"candidate-worktree",
|
|
2977
|
+
"tournament",
|
|
2978
|
+
"skipped"
|
|
2979
|
+
]);
|
|
2980
|
+
var OrchestrationCandidateEligibilitySchema = z.enum([
|
|
2981
|
+
"eligible",
|
|
2982
|
+
"not_eligible",
|
|
2983
|
+
"unknown"
|
|
2984
|
+
]);
|
|
2985
|
+
var OrchestrationCandidateDecisionSchema = z.enum([
|
|
2986
|
+
"used",
|
|
2987
|
+
"skipped",
|
|
2988
|
+
"serial_required"
|
|
2989
|
+
]);
|
|
2990
|
+
var OrchestrationDecisionFactorSchema = z.enum([
|
|
2991
|
+
"shared_state",
|
|
2992
|
+
"overlapping_files",
|
|
2993
|
+
"small_slice",
|
|
2994
|
+
"needs_manager_judgment",
|
|
2995
|
+
"independent_surface",
|
|
2996
|
+
"validation_available"
|
|
2997
|
+
]);
|
|
2998
|
+
var OrchestrationWriteScopeSchema = z.enum([
|
|
2999
|
+
"none",
|
|
3000
|
+
"manager-serial",
|
|
3001
|
+
"exact-path",
|
|
3002
|
+
"isolated-worktree",
|
|
3003
|
+
"mixed"
|
|
3004
|
+
]);
|
|
3005
|
+
var OrchestrationVerificationStatusSchema = z.enum([
|
|
3006
|
+
"not-needed",
|
|
3007
|
+
"pending",
|
|
3008
|
+
"passed",
|
|
3009
|
+
"failed",
|
|
3010
|
+
"mixed",
|
|
3011
|
+
"downgraded"
|
|
3012
|
+
]);
|
|
3013
|
+
var OrchestrationOutcomeSchema = z.enum([
|
|
3014
|
+
"accepted",
|
|
3015
|
+
"modified",
|
|
3016
|
+
"rejected",
|
|
3017
|
+
"partial",
|
|
3018
|
+
"not-covered",
|
|
3019
|
+
"superseded"
|
|
3020
|
+
]);
|
|
3021
|
+
var CANDIDATE_SHAPED_DECISIONS = new Set([
|
|
3022
|
+
"candidate-exact-path",
|
|
3023
|
+
"candidate-worktree",
|
|
3024
|
+
"tournament"
|
|
3025
|
+
]);
|
|
3026
|
+
function isCandidateShapedDecision(decision) {
|
|
3027
|
+
return decision !== undefined && CANDIDATE_SHAPED_DECISIONS.has(decision);
|
|
3028
|
+
}
|
|
3029
|
+
function hasCandidateExecutionEvidence(pass) {
|
|
3030
|
+
return pass.kind === "candidate" || pass.modes.includes("candidate-implementation") || pass.candidateWorkerCount > 0;
|
|
3031
|
+
}
|
|
3032
|
+
function hasVerifierExecutionEvidence(pass) {
|
|
3033
|
+
return pass.kind === "verification" || pass.modes.includes("verifier") || pass.verifierWorkerCount > 0;
|
|
3034
|
+
}
|
|
3035
|
+
var OrchestrationPassRecordSchema = z.object({
|
|
3036
|
+
id: z.string().min(1),
|
|
3037
|
+
kind: OrchestrationPassKindSchema,
|
|
3038
|
+
decision: OrchestrationDecisionSchema.optional(),
|
|
3039
|
+
decisionReason: z.string().min(1).optional(),
|
|
3040
|
+
candidateEligibility: OrchestrationCandidateEligibilitySchema.default("unknown"),
|
|
3041
|
+
candidateDecision: OrchestrationCandidateDecisionSchema.optional(),
|
|
3042
|
+
decisionFactors: z.array(OrchestrationDecisionFactorSchema).default([]),
|
|
3043
|
+
modes: z.array(OrchestrationModeSchema).default([]),
|
|
3044
|
+
workerCount: z.number().int().nonnegative().default(0),
|
|
3045
|
+
candidateWorkerCount: z.number().int().nonnegative().default(0),
|
|
3046
|
+
verifierWorkerCount: z.number().int().nonnegative().default(0),
|
|
3047
|
+
sliceIds: z.array(z.string().min(1)).default([]),
|
|
3048
|
+
dependsOn: z.array(z.string().min(1)).default([]),
|
|
3049
|
+
writeScope: OrchestrationWriteScopeSchema.default("none"),
|
|
3050
|
+
handoffRefs: z.array(z.string().min(1)).default([]),
|
|
3051
|
+
verificationStatus: OrchestrationVerificationStatusSchema.default("not-needed"),
|
|
3052
|
+
outcome: OrchestrationOutcomeSchema.default("accepted"),
|
|
3053
|
+
synthesisRef: z.string().min(1).optional()
|
|
3054
|
+
}).strict().superRefine((value, ctx) => {
|
|
3055
|
+
const issue = (path, message) => ctx.addIssue({ code: "custom", path: [path], message });
|
|
3056
|
+
const isImplementationDecision = value.kind === "implementation-decision";
|
|
3057
|
+
const candidateEligibilityIsUnknown = value.candidateEligibility === "unknown";
|
|
3058
|
+
if (value.candidateWorkerCount > value.workerCount) {
|
|
3059
|
+
issue("candidateWorkerCount", "candidateWorkerCount cannot exceed total workerCount.");
|
|
3060
|
+
}
|
|
3061
|
+
if (value.verifierWorkerCount > value.workerCount) {
|
|
3062
|
+
issue("verifierWorkerCount", "verifierWorkerCount cannot exceed total workerCount.");
|
|
3063
|
+
}
|
|
3064
|
+
if (isCandidateShapedDecision(value.decision) && !hasCandidateExecutionEvidence(value)) {
|
|
3065
|
+
issue("decision", "Candidate-shaped decisions require candidate execution evidence: a candidate pass, candidate-implementation mode, or candidateWorkerCount > 0.");
|
|
3066
|
+
}
|
|
3067
|
+
if (isImplementationDecision) {
|
|
3068
|
+
if (value.decision === "parallel") {
|
|
3069
|
+
issue("decision", "Implementation decisions cannot use decision 'parallel'; use 'serial', 'skipped', or a candidate-shaped decision.");
|
|
3070
|
+
}
|
|
3071
|
+
if (candidateEligibilityIsUnknown) {
|
|
3072
|
+
issue("candidateEligibility", "Implementation decisions must include explicit candidateEligibility.");
|
|
3073
|
+
}
|
|
3074
|
+
if (!value.candidateDecision) {
|
|
3075
|
+
issue("candidateDecision", "Implementation decisions must include explicit candidateDecision.");
|
|
3076
|
+
}
|
|
3077
|
+
if (!value.decision) {
|
|
3078
|
+
issue("decision", "Implementation decisions must include explicit decision.");
|
|
3079
|
+
}
|
|
3080
|
+
if (value.decisionFactors.length === 0) {
|
|
3081
|
+
issue("decisionFactors", "Implementation decisions must include at least one decisionFactor.");
|
|
3082
|
+
}
|
|
3083
|
+
}
|
|
3084
|
+
if (!value.candidateDecision)
|
|
3085
|
+
return;
|
|
3086
|
+
if (!isImplementationDecision && candidateEligibilityIsUnknown) {
|
|
3087
|
+
issue("candidateEligibility", "Candidate eligibility must be explicit when candidateDecision is set.");
|
|
3088
|
+
}
|
|
3089
|
+
if (!isImplementationDecision && (value.candidateDecision === "skipped" || value.candidateDecision === "serial_required")) {
|
|
3090
|
+
issue("candidateDecision", "Candidate decisions 'skipped' and 'serial_required' are only valid on implementation-decision records.");
|
|
3091
|
+
}
|
|
3092
|
+
if (value.candidateEligibility === "not_eligible" && value.candidateDecision === "used") {
|
|
3093
|
+
issue("candidateDecision", "Candidate decision 'used' requires eligible candidate work.");
|
|
3094
|
+
}
|
|
3095
|
+
if (value.candidateEligibility === "eligible" && value.candidateDecision === "serial_required") {
|
|
3096
|
+
issue("candidateDecision", "Candidate decision 'serial_required' requires not_eligible candidate work.");
|
|
3097
|
+
}
|
|
3098
|
+
if (value.candidateDecision === "skipped" && value.candidateEligibility !== "eligible") {
|
|
3099
|
+
issue("candidateDecision", "Candidate decision 'skipped' requires eligible candidate work.");
|
|
3100
|
+
}
|
|
3101
|
+
if (isImplementationDecision && value.decision === "skipped" && value.candidateDecision !== "skipped") {
|
|
3102
|
+
issue("decision", "Implementation decision 'skipped' requires candidateDecision 'skipped'.");
|
|
3103
|
+
}
|
|
3104
|
+
if (isImplementationDecision && value.candidateDecision === "skipped" && value.decision && value.decision !== "skipped") {
|
|
3105
|
+
issue("candidateDecision", "Candidate decision 'skipped' requires implementation decision 'skipped'.");
|
|
3106
|
+
}
|
|
3107
|
+
if (isImplementationDecision && value.candidateDecision === "serial_required" && value.decision && value.decision !== "serial") {
|
|
3108
|
+
issue("candidateDecision", "Candidate decision 'serial_required' requires implementation decision 'serial'.");
|
|
3109
|
+
}
|
|
3110
|
+
if (value.candidateDecision === "used") {
|
|
3111
|
+
if (!hasCandidateExecutionEvidence(value)) {
|
|
3112
|
+
issue("candidateDecision", "Candidate decision 'used' requires a candidate pass, candidate mode, or candidate worker count.");
|
|
3113
|
+
}
|
|
3114
|
+
if (value.decision && !isCandidateShapedDecision(value.decision)) {
|
|
3115
|
+
issue("decision", "Candidate decision 'used' requires an omitted or candidate-shaped decision.");
|
|
3116
|
+
}
|
|
3117
|
+
}
|
|
3118
|
+
});
|
|
3119
|
+
var OrchestrationTelemetrySchema = z.object({
|
|
3120
|
+
passCount: z.number().int().nonnegative().default(0),
|
|
3121
|
+
workerCount: z.number().int().nonnegative().default(0),
|
|
3122
|
+
candidatePassCount: z.number().int().nonnegative().default(0),
|
|
3123
|
+
verifierPassCount: z.number().int().nonnegative().default(0),
|
|
3124
|
+
candidateEligibleCount: z.number().int().nonnegative().default(0),
|
|
3125
|
+
candidateUsedDecisionCount: z.number().int().nonnegative().default(0),
|
|
3126
|
+
candidateSerialRequiredDecisionCount: z.number().int().nonnegative().default(0),
|
|
3127
|
+
skippedCandidateDecisionCount: z.number().int().nonnegative().default(0),
|
|
3128
|
+
recordedPassIds: z.array(z.string().min(1)).default([]),
|
|
3129
|
+
latestPasses: z.array(OrchestrationPassRecordSchema).default([])
|
|
3130
|
+
}).strict();
|
|
2638
3131
|
var ReviewFindingSchema = z.object({
|
|
2639
3132
|
summary: z.string().min(1),
|
|
2640
3133
|
severity: z.enum(["blocking", "advisory"]).default("blocking")
|
|
@@ -2704,7 +3197,8 @@ var WorkerResultSchema = z.discriminatedUnion("status", [
|
|
|
2704
3197
|
featureReviewDepth: FeatureReviewDepthSchema,
|
|
2705
3198
|
featureReview: ReviewSchema,
|
|
2706
3199
|
finalReview: FinalReviewSchema.optional(),
|
|
2707
|
-
outcome: WorkerOutcomeSchema.optional()
|
|
3200
|
+
outcome: WorkerOutcomeSchema.optional(),
|
|
3201
|
+
orchestrationPasses: z.array(OrchestrationPassRecordSchema).default([])
|
|
2708
3202
|
}).strict(),
|
|
2709
3203
|
z.object({
|
|
2710
3204
|
status: z.literal("needs_input"),
|
|
@@ -2716,7 +3210,8 @@ var WorkerResultSchema = z.discriminatedUnion("status", [
|
|
|
2716
3210
|
featureReviewDepth: FeatureReviewDepthSchema.optional(),
|
|
2717
3211
|
featureReview: ReviewSchema.optional(),
|
|
2718
3212
|
finalReview: FinalReviewSchema.optional(),
|
|
2719
|
-
outcome: NeedsInputOutcomeSchema
|
|
3213
|
+
outcome: NeedsInputOutcomeSchema,
|
|
3214
|
+
orchestrationPasses: z.array(OrchestrationPassRecordSchema).default([])
|
|
2720
3215
|
}).strict()
|
|
2721
3216
|
]).superRefine((value, ctx) => {
|
|
2722
3217
|
if (value.status === "ok" && value.outcome?.kind && value.outcome.kind !== "completed") {
|
|
@@ -2738,7 +3233,8 @@ var ExecutionHistoryEntrySchema = z.object({
|
|
|
2738
3233
|
featureReviewDepth: FeatureReviewDepthSchema.optional(),
|
|
2739
3234
|
featureReview: ReviewSchema.optional(),
|
|
2740
3235
|
finalReview: FinalReviewSchema.optional(),
|
|
2741
|
-
outcome: WorkerOutcomeSchema.optional()
|
|
3236
|
+
outcome: WorkerOutcomeSchema.optional(),
|
|
3237
|
+
orchestrationPasses: z.array(OrchestrationPassRecordSchema).default([])
|
|
2742
3238
|
}).strict();
|
|
2743
3239
|
var TokenTelemetrySchema = z.object({
|
|
2744
3240
|
source: z.enum(["host_unavailable", "reported"]).default("host_unavailable"),
|
|
@@ -2764,6 +3260,7 @@ var BudgetTelemetrySchema = z.object({
|
|
|
2764
3260
|
cacheReadTokens: null,
|
|
2765
3261
|
nonCacheTokens: null
|
|
2766
3262
|
}),
|
|
3263
|
+
orchestration: OrchestrationTelemetrySchema.prefault({}),
|
|
2767
3264
|
phaseBoundary: PhaseBoundarySchema.nullable().default(null)
|
|
2768
3265
|
}).strict();
|
|
2769
3266
|
var SessionSchema = z.object({
|
|
@@ -2775,20 +3272,7 @@ var SessionSchema = z.object({
|
|
|
2775
3272
|
plan: PlanSchema.nullable(),
|
|
2776
3273
|
activeFeatureId: z.string().regex(FEATURE_ID_PATTERN, FEATURE_ID_MESSAGE).nullable(),
|
|
2777
3274
|
history: z.array(ExecutionHistoryEntrySchema).default([]),
|
|
2778
|
-
budget: BudgetTelemetrySchema.
|
|
2779
|
-
phaseStartedAt: "unknown",
|
|
2780
|
-
completedFeaturesSinceBoundary: 0,
|
|
2781
|
-
reviewCount: 0,
|
|
2782
|
-
failedReviewCount: 0,
|
|
2783
|
-
failedReviewAttemptsByFeature: {},
|
|
2784
|
-
tokenTelemetry: {
|
|
2785
|
-
source: "host_unavailable",
|
|
2786
|
-
visibleTokens: null,
|
|
2787
|
-
cacheReadTokens: null,
|
|
2788
|
-
nonCacheTokens: null
|
|
2789
|
-
},
|
|
2790
|
-
phaseBoundary: null
|
|
2791
|
-
}),
|
|
3275
|
+
budget: BudgetTelemetrySchema.prefault({}),
|
|
2792
3276
|
closure: z.object({
|
|
2793
3277
|
kind: z.enum(["completed", "deferred", "abandoned"]),
|
|
2794
3278
|
summary: z.string().min(1),
|
|
@@ -3227,6 +3711,7 @@ function nowIso() {
|
|
|
3227
3711
|
// src/runtime/transitions.ts
|
|
3228
3712
|
var MAX_HISTORY_ENTRIES = 500;
|
|
3229
3713
|
var MAX_FAILED_REVIEW_ATTEMPTS_PER_FEATURE = 2;
|
|
3714
|
+
var MAX_LATEST_ORCHESTRATION_PASSES = 50;
|
|
3230
3715
|
var FEATURE_REVIEW_DEPTH_RANK = {
|
|
3231
3716
|
quick: 0,
|
|
3232
3717
|
standard: 1,
|
|
@@ -3248,24 +3733,12 @@ function historyEntryFor(worker, status) {
|
|
|
3248
3733
|
featureReviewDepth: worker.featureReviewDepth,
|
|
3249
3734
|
featureReview: worker.featureReview,
|
|
3250
3735
|
finalReview: worker.finalReview,
|
|
3251
|
-
outcome: worker.outcome
|
|
3736
|
+
outcome: worker.outcome,
|
|
3737
|
+
orchestrationPasses: worker.orchestrationPasses
|
|
3252
3738
|
};
|
|
3253
3739
|
}
|
|
3254
3740
|
function initialBudgetTelemetry() {
|
|
3255
|
-
return {
|
|
3256
|
-
phaseStartedAt: nowIso(),
|
|
3257
|
-
completedFeaturesSinceBoundary: 0,
|
|
3258
|
-
reviewCount: 0,
|
|
3259
|
-
failedReviewCount: 0,
|
|
3260
|
-
failedReviewAttemptsByFeature: {},
|
|
3261
|
-
tokenTelemetry: {
|
|
3262
|
-
source: "host_unavailable",
|
|
3263
|
-
visibleTokens: null,
|
|
3264
|
-
cacheReadTokens: null,
|
|
3265
|
-
nonCacheTokens: null
|
|
3266
|
-
},
|
|
3267
|
-
phaseBoundary: null
|
|
3268
|
-
};
|
|
3741
|
+
return { ...BudgetTelemetrySchema.parse({}), phaseStartedAt: nowIso() };
|
|
3269
3742
|
}
|
|
3270
3743
|
function normalizeBudgetTelemetry(session) {
|
|
3271
3744
|
const defaults = initialBudgetTelemetry();
|
|
@@ -3278,9 +3751,87 @@ function normalizeBudgetTelemetry(session) {
|
|
|
3278
3751
|
tokenTelemetry: {
|
|
3279
3752
|
...defaults.tokenTelemetry,
|
|
3280
3753
|
...session.budget.tokenTelemetry
|
|
3754
|
+
},
|
|
3755
|
+
orchestration: {
|
|
3756
|
+
...defaults.orchestration,
|
|
3757
|
+
...session.budget.orchestration,
|
|
3758
|
+
recordedPassIds: [
|
|
3759
|
+
...session.budget.orchestration?.recordedPassIds ?? []
|
|
3760
|
+
],
|
|
3761
|
+
latestPasses: [...session.budget.orchestration?.latestPasses ?? []]
|
|
3281
3762
|
}
|
|
3282
3763
|
};
|
|
3283
3764
|
}
|
|
3765
|
+
function recordOrchestrationPasses(budget, passes) {
|
|
3766
|
+
if (passes.length === 0)
|
|
3767
|
+
return budget;
|
|
3768
|
+
const seenPassIds = new Set([
|
|
3769
|
+
...budget.orchestration.recordedPassIds,
|
|
3770
|
+
...budget.orchestration.latestPasses.map((pass) => pass.id)
|
|
3771
|
+
]);
|
|
3772
|
+
const newPasses = [];
|
|
3773
|
+
for (const pass of passes) {
|
|
3774
|
+
if (seenPassIds.has(pass.id))
|
|
3775
|
+
continue;
|
|
3776
|
+
seenPassIds.add(pass.id);
|
|
3777
|
+
newPasses.push(pass);
|
|
3778
|
+
}
|
|
3779
|
+
if (newPasses.length === 0)
|
|
3780
|
+
return budget;
|
|
3781
|
+
const tally = {
|
|
3782
|
+
workerCount: 0,
|
|
3783
|
+
candidatePassCount: 0,
|
|
3784
|
+
verifierPassCount: 0,
|
|
3785
|
+
candidateEligibleCount: 0,
|
|
3786
|
+
candidateUsedDecisionCount: 0,
|
|
3787
|
+
candidateSerialRequiredDecisionCount: 0,
|
|
3788
|
+
skippedCandidateDecisionCount: 0
|
|
3789
|
+
};
|
|
3790
|
+
for (const pass of newPasses) {
|
|
3791
|
+
tally.workerCount += pass.workerCount;
|
|
3792
|
+
if (hasCandidateExecutionEvidence(pass))
|
|
3793
|
+
tally.candidatePassCount += 1;
|
|
3794
|
+
if (hasVerifierExecutionEvidence(pass))
|
|
3795
|
+
tally.verifierPassCount += 1;
|
|
3796
|
+
if (pass.kind !== "implementation-decision")
|
|
3797
|
+
continue;
|
|
3798
|
+
if (pass.candidateEligibility === "eligible") {
|
|
3799
|
+
tally.candidateEligibleCount += 1;
|
|
3800
|
+
}
|
|
3801
|
+
if (pass.candidateDecision === "used") {
|
|
3802
|
+
tally.candidateUsedDecisionCount += 1;
|
|
3803
|
+
}
|
|
3804
|
+
if (pass.candidateDecision === "serial_required") {
|
|
3805
|
+
tally.candidateSerialRequiredDecisionCount += 1;
|
|
3806
|
+
}
|
|
3807
|
+
if (pass.candidateDecision === "skipped") {
|
|
3808
|
+
tally.skippedCandidateDecisionCount += 1;
|
|
3809
|
+
}
|
|
3810
|
+
}
|
|
3811
|
+
const latestPasses = [...budget.orchestration.latestPasses, ...newPasses];
|
|
3812
|
+
return {
|
|
3813
|
+
...budget,
|
|
3814
|
+
orchestration: {
|
|
3815
|
+
passCount: budget.orchestration.passCount + newPasses.length,
|
|
3816
|
+
workerCount: budget.orchestration.workerCount + tally.workerCount,
|
|
3817
|
+
candidatePassCount: budget.orchestration.candidatePassCount + tally.candidatePassCount,
|
|
3818
|
+
verifierPassCount: budget.orchestration.verifierPassCount + tally.verifierPassCount,
|
|
3819
|
+
candidateEligibleCount: budget.orchestration.candidateEligibleCount + tally.candidateEligibleCount,
|
|
3820
|
+
candidateUsedDecisionCount: budget.orchestration.candidateUsedDecisionCount + tally.candidateUsedDecisionCount,
|
|
3821
|
+
candidateSerialRequiredDecisionCount: budget.orchestration.candidateSerialRequiredDecisionCount + tally.candidateSerialRequiredDecisionCount,
|
|
3822
|
+
skippedCandidateDecisionCount: budget.orchestration.skippedCandidateDecisionCount + tally.skippedCandidateDecisionCount,
|
|
3823
|
+
recordedPassIds: [
|
|
3824
|
+
...budget.orchestration.recordedPassIds,
|
|
3825
|
+
...newPasses.map((pass) => pass.id)
|
|
3826
|
+
],
|
|
3827
|
+
latestPasses: latestPasses.length > MAX_LATEST_ORCHESTRATION_PASSES ? latestPasses.slice(latestPasses.length - MAX_LATEST_ORCHESTRATION_PASSES) : latestPasses
|
|
3828
|
+
}
|
|
3829
|
+
};
|
|
3830
|
+
}
|
|
3831
|
+
function sessionWithOrchestrationPasses(session, passes) {
|
|
3832
|
+
const budget = recordOrchestrationPasses(normalizeBudgetTelemetry(session), passes);
|
|
3833
|
+
return budget === session.budget ? session : { ...session, budget };
|
|
3834
|
+
}
|
|
3284
3835
|
function ok(value) {
|
|
3285
3836
|
return { ok: true, value };
|
|
3286
3837
|
}
|
|
@@ -3640,48 +4191,49 @@ function completeFeature(session, input) {
|
|
|
3640
4191
|
if (worker.featureId !== session.activeFeatureId) {
|
|
3641
4192
|
return fail(`Worker result feature '${worker.featureId}' does not match active feature '${session.activeFeatureId}'.`);
|
|
3642
4193
|
}
|
|
4194
|
+
const sessionWithPasses = sessionWithOrchestrationPasses(session, worker.orchestrationPasses);
|
|
3643
4195
|
if (worker.status === "needs_input") {
|
|
3644
4196
|
const entry2 = historyEntryFor(worker, "needs_input");
|
|
3645
|
-
const budget2 = normalizeBudgetTelemetry(
|
|
4197
|
+
const budget2 = normalizeBudgetTelemetry(sessionWithPasses);
|
|
3646
4198
|
return ok(touch({
|
|
3647
|
-
...
|
|
4199
|
+
...sessionWithPasses,
|
|
3648
4200
|
status: "blocked",
|
|
3649
4201
|
activeFeatureId: null,
|
|
3650
4202
|
plan: {
|
|
3651
4203
|
...session.plan,
|
|
3652
4204
|
features: updateFeature(session.plan.features, worker.featureId, "blocked")
|
|
3653
4205
|
},
|
|
3654
|
-
history: appendHistory(
|
|
4206
|
+
history: appendHistory(sessionWithPasses.history, entry2),
|
|
3655
4207
|
budget: budget2,
|
|
3656
4208
|
lastError: null
|
|
3657
4209
|
}));
|
|
3658
4210
|
}
|
|
3659
4211
|
if (!isPassingReview(worker.featureReview)) {
|
|
3660
|
-
return failedReviewCompletion(
|
|
4212
|
+
return failedReviewCompletion(sessionWithPasses, worker, worker.featureReview, "feature");
|
|
3661
4213
|
}
|
|
3662
|
-
if (finalFeature(
|
|
3663
|
-
return failedReviewCompletion(
|
|
4214
|
+
if (finalFeature(sessionWithPasses, worker.featureId) && worker.finalReview && !isPassingReview(worker.finalReview)) {
|
|
4215
|
+
return failedReviewCompletion(sessionWithPasses, worker, worker.finalReview, "final");
|
|
3664
4216
|
}
|
|
3665
|
-
const validation = validateCompletion(
|
|
4217
|
+
const validation = validateCompletion(sessionWithPasses, worker);
|
|
3666
4218
|
if (!validation.ok)
|
|
3667
4219
|
return validation;
|
|
3668
4220
|
const entry = historyEntryFor(worker, "completed");
|
|
3669
4221
|
const features = updateFeature(session.plan.features, worker.featureId, "completed");
|
|
3670
4222
|
const allComplete = features.every((feature) => feature.status === "completed");
|
|
3671
4223
|
const now = nowIso();
|
|
3672
|
-
const budget = completionBudget(
|
|
4224
|
+
const budget = completionBudget(sessionWithPasses, worker);
|
|
3673
4225
|
return ok(touch({
|
|
3674
|
-
...
|
|
4226
|
+
...sessionWithPasses,
|
|
3675
4227
|
status: allComplete ? "completed" : "ready",
|
|
3676
4228
|
activeFeatureId: null,
|
|
3677
4229
|
plan: { ...session.plan, features },
|
|
3678
|
-
history: appendHistory(
|
|
4230
|
+
history: appendHistory(sessionWithPasses.history, entry),
|
|
3679
4231
|
budget,
|
|
3680
4232
|
closure: allComplete ? { kind: "completed", summary: worker.summary, recordedAt: now } : null,
|
|
3681
4233
|
lastError: null,
|
|
3682
4234
|
timestamps: {
|
|
3683
|
-
...
|
|
3684
|
-
completedAt: allComplete ? now :
|
|
4235
|
+
...sessionWithPasses.timestamps,
|
|
4236
|
+
completedAt: allComplete ? now : sessionWithPasses.timestamps.completedAt
|
|
3685
4237
|
}
|
|
3686
4238
|
}));
|
|
3687
4239
|
}
|
|
@@ -3808,6 +4360,7 @@ function summarizeSession(session) {
|
|
|
3808
4360
|
reviewCount: budget.reviewCount,
|
|
3809
4361
|
failedReviewCount: budget.failedReviewCount,
|
|
3810
4362
|
failedReviewAttemptsByFeature: budget.failedReviewAttemptsByFeature,
|
|
4363
|
+
orchestration: budget.orchestration,
|
|
3811
4364
|
tokenTelemetry: {
|
|
3812
4365
|
...budget.tokenTelemetry,
|
|
3813
4366
|
note: budget.tokenTelemetry.source === "host_unavailable" ? "OpenCode does not expose per-turn usage to this plugin surface; Flow can enforce review checkpoints, but token thresholds remain manager-observed." : undefined
|
|
@@ -3882,7 +4435,8 @@ var FlowFeatureCompleteToolSchema = z2.object({
|
|
|
3882
4435
|
featureReviewDepth: FeatureReviewDepthSchema.optional(),
|
|
3883
4436
|
featureReview: ReviewSchema.optional(),
|
|
3884
4437
|
finalReview: FinalReviewSchema.optional(),
|
|
3885
|
-
outcome: z2.union([WorkerOutcomeSchema, NeedsInputOutcomeSchema]).optional()
|
|
4438
|
+
outcome: z2.union([WorkerOutcomeSchema, NeedsInputOutcomeSchema]).optional(),
|
|
4439
|
+
orchestrationPasses: z2.array(OrchestrationPassRecordSchema).optional()
|
|
3886
4440
|
}).strict();
|
|
3887
4441
|
function missingSessionResponse() {
|
|
3888
4442
|
return {
|
|
@@ -4249,4 +4803,4 @@ export {
|
|
|
4249
4803
|
plugin_default as default
|
|
4250
4804
|
};
|
|
4251
4805
|
|
|
4252
|
-
//# debugId=
|
|
4806
|
+
//# debugId=C77708A78E8740C664756E2164756E21
|