opencode-plugin-flow 7.3.2 → 8.1.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 +40 -0
- package/README.md +2 -2
- package/dist/index.js +368 -345
- package/dist/index.js.map +16 -15
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -109,7 +109,8 @@ If a Flow tool is absent, report an incomplete plugin load; never simulate state
|
|
|
109
109
|
## Recovery
|
|
110
110
|
|
|
111
111
|
On interruption, read compact status; load \`flow-run\` for an active or blocked feature
|
|
112
|
-
and use execution or reviewer status, never prose, for lifecycle truth.
|
|
112
|
+
and use execution or reviewer status, never prose, for lifecycle truth. At a
|
|
113
|
+
checkpoint or lease stop, print compact \`findingsDigest\` before any delivery map.
|
|
113
114
|
|
|
114
115
|
Summaries keep plan/source IDs \`verified\` or \`incomplete\`. A prior finding is
|
|
115
116
|
terminally \`fixed\` only when review passes with current evidence. A failed
|
|
@@ -187,21 +188,19 @@ Save one plan with:
|
|
|
187
188
|
- \`overview\`: the implementation approach and important boundaries.
|
|
188
189
|
- \`requirements\`: acceptance criteria, constraints, and non-goals.
|
|
189
190
|
- \`decisions\`: assumptions and architecture or scope choices already made.
|
|
190
|
-
- \`
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
satisfies it: final review and completed closure stay refused until then. A skipped
|
|
201
|
-
case exits zero and satisfies nothing, and recording the gap as a \`requirements\`
|
|
202
|
-
non-goal declares nothing.
|
|
191
|
+
- \`evidence\`: one \`scope: "gate"\` entry for the canonical whole-repository
|
|
192
|
+
command, plus \`scope: "extra"\` entries for observations this host may lack.
|
|
193
|
+
Each entry names \`requirement\`, \`environment\`, \`command\`, \`platform\`
|
|
194
|
+
(\`win32\`, \`darwin\`, \`linux\`, or \`other\`), and \`assertions\` (empty when the
|
|
195
|
+
evidence is not a test result). Broad observations run the gate command
|
|
196
|
+
byte-for-byte. Extra entries may be omitted when the goal is fully observable
|
|
197
|
+
here. Final review and completed closure stay refused until every extra
|
|
198
|
+
entry is satisfied on its declared platform with named cases passing. The
|
|
199
|
+
gate is the command every broad observation must run; a failed or
|
|
200
|
+
claimed-broad gate still vetoes review.
|
|
203
201
|
- \`features\`: ordered outcome slices, each with a stable \`id\`, \`title\`,
|
|
204
|
-
\`summary\`, bounded \`targets\`, concrete \`validation\`,
|
|
202
|
+
\`summary\`, bounded \`targets\`, concrete \`validation\`, \`dependsOn\` ids, and
|
|
203
|
+
optional \`kind\`.
|
|
205
204
|
|
|
206
205
|
Each feature needs one observable outcome judgeable from bounded evidence and
|
|
207
206
|
focused validation. Split only independent failures or true dependencies; file
|
|
@@ -216,6 +215,13 @@ outcome and its evidence.
|
|
|
216
215
|
A \`validation\` entry naming a command is recorded byte-for-byte; prose there stays
|
|
217
216
|
reviewer judgment, never a fabricated result.
|
|
218
217
|
|
|
218
|
+
If the request is inspect-only (review, audit, survey, no promised edit), invent
|
|
219
|
+
no repair features. Save at most a small set of inspect features with
|
|
220
|
+
\`kind: "inspect"\`, whose \`validation\` is reviewer inspection, whose \`targets\`
|
|
221
|
+
are existing paths, and whose \`decisions\` state that no source edit is
|
|
222
|
+
authorized. The gate may be the repo's existing check. Ask before turning an
|
|
223
|
+
inspect request into repairs.
|
|
224
|
+
|
|
219
225
|
Before saving, confirm:
|
|
220
226
|
|
|
221
227
|
- every requirement maps to a feature or an explicit non-goal;
|
|
@@ -280,17 +286,15 @@ the manager's summary. Check that:
|
|
|
280
286
|
- changed behavior is correct at public and downstream call sites;
|
|
281
287
|
- validation is strong enough for the behavior and main failure modes;
|
|
282
288
|
- scope did not drift and unrelated user work was preserved;
|
|
283
|
-
- relevant adjacent states, failure/cleanup ordering, repetition, retry,
|
|
284
|
-
interruption, reentrancy, concurrency, and overlapping invariants still work;
|
|
285
289
|
- the feature's actual base diff includes no unexplained deletion, rename, file
|
|
286
290
|
type, generated artifact, or executable/file-mode change; and
|
|
287
291
|
- persistence, concurrency, security, migration, compatibility, package, UI,
|
|
288
292
|
and recovery risks were examined when relevant.
|
|
289
293
|
|
|
290
|
-
Finish the supplied feature-specific risk checklist
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
+
Finish the supplied feature-specific risk checklist. Continue a concurrency or
|
|
295
|
+
state-machine matrix after one blocker only when \`assignment.packet.riskLenses\`
|
|
296
|
+
is non-empty or the packet summary includes a matrix. Otherwise inspect the
|
|
297
|
+
changed artifacts, the supplied validation, and live \`priorFindings\`.
|
|
294
298
|
|
|
295
299
|
Scope plan/source IDs by assignment kind. An ordinary feature review records
|
|
296
300
|
dispositions only for IDs mapped to the active feature or explicitly supplied
|
|
@@ -314,8 +318,8 @@ not a failure; a missing or conflicting baseline fact, or a material mode,
|
|
|
314
318
|
platform, race, or failure-path claim without proof, is.
|
|
315
319
|
|
|
316
320
|
Flow deliberately projects no raw command output; use the durable command, exit
|
|
317
|
-
code, completeness, digest, source binding, and your workspace inspection. A weak
|
|
318
|
-
|
|
321
|
+
code, completeness, digest, source binding, and your workspace inspection. A weak
|
|
322
|
+
coverage claim is an evidence gap.
|
|
319
323
|
|
|
320
324
|
For a final assignment, also inspect broad validation and confirm docs,
|
|
321
325
|
commands, package surfaces, and remaining gaps are consistent with completion.
|
|
@@ -329,8 +333,8 @@ IDs in summary or evidence.
|
|
|
329
333
|
|
|
330
334
|
Report every problem you find. Severity is a routing decision the runtime acts
|
|
331
335
|
on, not a filter on what to mention: \`blocking\` when the issue invalidates the
|
|
332
|
-
approved outcome, \`advisory\` otherwise.
|
|
333
|
-
\`advisory\` rather than omitting it.
|
|
336
|
+
approved outcome, \`advisory\` otherwise. If unsure, report it as
|
|
337
|
+
\`advisory\` rather than omitting it. Inspect kind completes with blockers.
|
|
334
338
|
|
|
335
339
|
Set \`scopeBlocker: true\` on a blocking finding whose repair requires material
|
|
336
340
|
work outside the approved plan, and identify the boundary in \`evidence\`. The
|
|
@@ -406,14 +410,14 @@ description: Implement, validate, independently review, and record one approved
|
|
|
406
410
|
|
|
407
411
|
Work on exactly one approved feature.
|
|
408
412
|
|
|
409
|
-
## Start
|
|
413
|
+
## Start
|
|
410
414
|
|
|
411
415
|
1. Call \`flow_status { request: { view: "compact" } }\` first. Treat
|
|
412
|
-
\`nextAction\` as the durable default
|
|
416
|
+
\`nextAction\` as the durable default, not as permission.
|
|
413
417
|
2. If the top-level response status is \`error\`, report its exact summary and
|
|
414
418
|
recovery when present and, if \`workflowData.delivery\` exists, the handoff
|
|
415
|
-
below.
|
|
416
|
-
|
|
419
|
+
below. This read made no lifecycle, Git, or release mutation. Stop. Do not
|
|
420
|
+
route its \`nextAction\`.
|
|
417
421
|
3. If compact status contains \`archiveRetry\`, call \`flow_session_close\` once
|
|
418
422
|
with the projected request byte-for-byte. Report delivery under the contract
|
|
419
423
|
below. Refresh only if publication is unconfirmed. Stop after this cleanup
|
|
@@ -421,9 +425,9 @@ Work on exactly one approved feature.
|
|
|
421
425
|
4. When the projection contains an active goal, align it with the current
|
|
422
426
|
\`/flow-run\` request before another manager lifecycle mutation. Continue only
|
|
423
427
|
for the same goal or a method/emphasis narrowing that preserves all outcomes;
|
|
424
|
-
close completed work. Unless step
|
|
425
|
-
mutation: report that it has not started and offer continue, defer,
|
|
426
|
-
abandon.
|
|
428
|
+
close completed work. Unless the next step applies, new or expanded work
|
|
429
|
+
makes no mutation: report that it has not started and offer continue, defer,
|
|
430
|
+
or abandon.
|
|
427
431
|
5. If the aligned request explicitly chooses deferred or abandoned closure for
|
|
428
432
|
a non-completed session, call \`flow_session_close\` with compact session id and
|
|
429
433
|
revision, fresh operation id, that kind, and optional summary. Report delivery
|
|
@@ -432,226 +436,113 @@ Work on exactly one approved feature.
|
|
|
432
436
|
explain that \`/flow-run\` requires an approved feature, and stop without
|
|
433
437
|
mutation.
|
|
434
438
|
|
|
435
|
-
|
|
439
|
+
Delivery handoff: report \`workflowData.delivery.report\` verbatim. Map IDs only
|
|
440
|
+
from delivery \`outcomeSummary\`/\`terminalFindings\`. Requirements are \`verified\`,
|
|
441
|
+
\`incomplete\`, or explicitly \`deferred\`, and \`abandoned\` remains the kind. If
|
|
442
|
+
delivery is absent, report exact recovery and no map. On revision conflict,
|
|
443
|
+
refresh compact; retry only for the same session and goal while status still
|
|
444
|
+
permits the selected closure kind; never close a replacement.
|
|
445
|
+
|
|
446
|
+
## Route
|
|
447
|
+
|
|
448
|
+
Follow compact \`nextAction\` in this order:
|
|
436
449
|
|
|
437
450
|
- \`flow_session_close\`: close completed work with its projected session
|
|
438
|
-
id/revision, fresh operation id, and \`kind: "completed"\`. Report delivery
|
|
439
|
-
|
|
440
|
-
New work may enter planning afterward; do not fabricate a run.
|
|
451
|
+
id/revision, fresh operation id, and \`kind: "completed"\`. Report delivery,
|
|
452
|
+
follow one exact \`archiveRetry\` if needed, and stop.
|
|
441
453
|
- \`await-user-direction\` or blocked \`flow_feature_reset\`: call
|
|
442
|
-
\`flow_status { request: { view: "detail" } }\` exactly once, then
|
|
443
|
-
|
|
444
|
-
- Ready \`await-user-direction\` has no blocked run left to reset. Identify the
|
|
445
|
-
planned feature whose latest relevant reviewed outcome remains failed and
|
|
446
|
-
checkpoint unless the current aligned request explicitly authorizes its
|
|
447
|
-
retry. When authorized, call \`flow_run_start\` with that exact \`featureId\`;
|
|
448
|
-
never call \`flow_feature_reset\` from ready status or rely on default
|
|
449
|
-
selection.
|
|
450
|
-
- For blocked status, apply **Blocked review** below. If it permits another
|
|
451
|
-
feature run, pass that exact choice as \`nextFeatureId\` to
|
|
452
|
-
\`flow_feature_reset\` so reset and run start are atomic, then route its
|
|
453
|
-
returned projection; otherwise report the checkpoint and stop.
|
|
454
|
+
\`flow_status { request: { view: "detail" } }\` exactly once, then apply
|
|
455
|
+
**Blocked review**.
|
|
454
456
|
- Running \`flow_feature_reset\`: the pending review is source-stale. Reset with
|
|
455
|
-
the same feature as \`nextFeatureId\` when continuing it
|
|
456
|
-
|
|
457
|
+
the same feature as \`nextFeatureId\` when continuing it. Never redispatch that
|
|
458
|
+
assignment.
|
|
457
459
|
- \`dispatch-flow-reviewer\`: read execution status. If that read errors, report
|
|
458
|
-
its exact summary and recovery when present and stop
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
\`flow_feature_reset\`, follow the source-stale reset route and never dispatch
|
|
463
|
-
that assignment. Skip run start, implementation, and validation.
|
|
460
|
+
its exact summary and recovery when present and stop. Otherwise route that
|
|
461
|
+
refreshed projection. Dispatch under **Review** only if \`nextAction\` is still
|
|
462
|
+
\`dispatch-flow-reviewer\`. If it is now running \`flow_feature_reset\`, follow
|
|
463
|
+
the source-stale reset route.
|
|
464
464
|
- \`flow_run_start\`: start the ready feature, refresh compact status, and read
|
|
465
465
|
execution status.
|
|
466
|
-
- \`flow_validation_start\`: read execution status and resume
|
|
467
|
-
|
|
468
|
-
- \`flow_review_start\`: read execution status and continue at **Review
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
feature summary, targets, validation, dependencies, requirements, and decisions.
|
|
475
|
-
If a projected action fails an environment-sensitive guard, refresh compact and
|
|
476
|
-
handle that rejection; never force it.
|
|
477
|
-
|
|
478
|
-
Summaries keep plan/source IDs \`verified\` or \`incomplete\`.
|
|
479
|
-
Delivery handoff: report \`workflowData.delivery.report\` verbatim. Map IDs only from
|
|
480
|
-
delivery \`outcomeSummary\`/\`terminalFindings\`; requirements are \`verified\`,
|
|
481
|
-
\`incomplete\`, or explicitly \`deferred\`, and \`abandoned\` remains the kind.
|
|
482
|
-
If delivery is absent, report exact recovery and no map; never invent or read
|
|
483
|
-
detail solely for closure. On revision conflict, refresh compact; retry only for
|
|
484
|
-
the same session and goal while status still permits the selected closure kind;
|
|
485
|
-
never close a replacement.
|
|
486
|
-
|
|
487
|
-
Preserve unrelated work and stay inside the feature. Out-of-plan work stops;
|
|
488
|
-
finish or obtain explicit deferred/abandoned closure before a new plan. Never
|
|
489
|
-
replan in place. Use \`flow_feature_reset\` for a wrong design or assumption; do
|
|
490
|
-
not layer retries.
|
|
491
|
-
|
|
492
|
-
## Evidence and risk preflight
|
|
493
|
-
|
|
494
|
-
Before editing or dispatching a worker, perform one preflight from the approved
|
|
495
|
-
feature and current worktree:
|
|
496
|
-
|
|
497
|
-
- Preserve every named finding/requirement; map each to an observable acceptance
|
|
498
|
-
outcome.
|
|
499
|
-
- Inventory each \`externalEvidence\` command, its platform, and where it writes a
|
|
500
|
-
JUnit report: arm it with \`resultsPath\` naming that file, or its declared cases go
|
|
501
|
-
unobserved and the entry stays open on exit zero.
|
|
502
|
-
- Reuse one conversational run baseline of unrelated work, deletions, renames,
|
|
503
|
-
file types, and executable modes. Refresh changed facts; give each review only
|
|
504
|
-
facts the feature changes or depends on, and give final review the full
|
|
505
|
-
inventory.
|
|
506
|
-
- Write one concise adversarial checklist covering failure and cleanup ordering,
|
|
507
|
-
adjacent states, repetition, retry, interruption, concurrency, overlapping
|
|
508
|
-
invariants, and relevant platform or persistence risks. For concurrency or
|
|
509
|
-
state-machine work, express it as a compact matrix with \`state/interleaving\`,
|
|
510
|
-
\`event\`, \`expected outcome\`, \`cleanup/invariant\`, and \`evidence\` columns.
|
|
511
|
-
|
|
512
|
-
Carry the checklist/IDs through workers and review. Evidence needing user or
|
|
513
|
-
external authority stops before implementation.
|
|
466
|
+
- \`flow_validation_start\`: read execution status and resume from the current
|
|
467
|
+
worktree.
|
|
468
|
+
- \`flow_review_start\`: read execution status and continue at **Review**.
|
|
469
|
+
- Any other action: report it and stop.
|
|
470
|
+
|
|
471
|
+
Use execution status for active scope and revision. Stay inside the feature.
|
|
472
|
+
Out-of-plan work stops. Use \`flow_feature_reset\` for a wrong design; do not
|
|
473
|
+
layer retries.
|
|
514
474
|
|
|
515
475
|
## Implement
|
|
516
476
|
|
|
517
|
-
Make the smallest change
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
requires an explicit request.
|
|
477
|
+
Make the smallest change that satisfies the approved outcome. Create no
|
|
478
|
+
lifecycle or handoff sidecars. Do not stage, commit, push, publish, or mutate
|
|
479
|
+
releases unless asked separately.
|
|
521
480
|
|
|
522
|
-
|
|
481
|
+
Work serially. After a feature run is active, dispatch \`flow-worker\` only for
|
|
482
|
+
two or three genuinely independent slices with clear benefit. Workers call no
|
|
483
|
+
Flow tools, spawn no children, and run no Bash. Integrate and inspect the
|
|
484
|
+
combined diff before validation.
|
|
523
485
|
|
|
524
|
-
##
|
|
486
|
+
## Validate
|
|
525
487
|
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
host runs concurrent tasks, otherwise sequentially; report serial either way. Each
|
|
530
|
-
assignment names a stable id, exact outcome/read-write scope, coverage, manager
|
|
531
|
-
checks, dependencies, stop condition, and preflight risk checklist. The worker
|
|
532
|
-
must receive the checklist before it codes. Shared contracts, lockfiles, and generated
|
|
533
|
-
output stay manager-owned unless wholly assigned to one worker.
|
|
488
|
+
Arm each evidence Bash command with \`flow_validation_start\` (current revision,
|
|
489
|
+
feature id, exact command, \`scope\`) immediately before running it byte-for-byte.
|
|
490
|
+
Flow records the host observation; copy no host-observed fields.
|
|
534
491
|
|
|
535
|
-
|
|
536
|
-
status, scope/coverage, evidence/changed paths, manager checks, gaps/risks, and
|
|
537
|
-
integration notes; missing or blocked output is a coverage gap.
|
|
492
|
+
\`scope: "broad"\` runs the plan's gate evidence command and nothing else.
|
|
538
493
|
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
coordination ledger/sidecar. After interruption, inspect status/worktree and
|
|
543
|
-
treat partial worker edits as untrusted.
|
|
494
|
+
A failed, incomplete, or source-drifted observation of a plan-listed command or
|
|
495
|
+
of the declared gate command blocks review until that same command passes for
|
|
496
|
+
current source.
|
|
544
497
|
|
|
545
|
-
|
|
498
|
+
Every host-observed validation advances revision. The \`[flow-validation]\`
|
|
499
|
+
marker reports \`passed\`, \`recordedRevision\`, and declared \`assertions\`. Use
|
|
500
|
+
\`recordedRevision\` for the next \`flow_validation_start\`, or for
|
|
501
|
+
\`flow_review_start\` only when \`passed: true\`. If the marker is absent, refresh
|
|
502
|
+
compact status before mutating.
|
|
546
503
|
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
sufficient alone only for genuinely mechanical or documentation-only work.
|
|
555
|
-
- UI claims need visual inspection when a runnable target is available.
|
|
556
|
-
- Cleanup claims need behavior-preservation evidence, not formatting alone.
|
|
557
|
-
- \`scope: "broad"\` runs the plan's declared \`gate\` and nothing else; the runtime
|
|
558
|
-
refuses any other command at that scope.
|
|
559
|
-
|
|
560
|
-
Immediately before each evidence Bash command, call \`flow_validation_start\`
|
|
561
|
-
with current revision, feature id, exact command, and \`scope\` (\`focused\` or
|
|
562
|
-
\`broad\`). Run it byte-for-byte next and inspect the complete outcome. Flow
|
|
563
|
-
records the host observation; copy no host-observed fields into a later request.
|
|
564
|
-
The command is durable, so include no secrets.
|
|
565
|
-
|
|
566
|
-
A failed, incomplete, or source-drifted observation of a plan-listed command or of
|
|
567
|
-
the declared \`gate\` blocks review until that same command passes for current
|
|
568
|
-
source. No substitute discharges it; reverting the source revives nothing.
|
|
569
|
-
|
|
570
|
-
A gate or \`externalEvidence\` command that cannot pass withholds completed closure,
|
|
571
|
-
and reporting the blocker is half the move. Reach the passing command, or ask the
|
|
572
|
-
user to choose deferred or abandoned closure. Ending the turn having done neither
|
|
573
|
-
leaves the workflow with no next step, whatever the blocker was.
|
|
574
|
-
|
|
575
|
-
Every host-observed validation advances the session revision, so the revision
|
|
576
|
-
that armed a completed command is stale. The \`[flow-validation]\` marker reports
|
|
577
|
-
\`passed\`, \`recordedRevision\`, and any declared \`assertions\` with what the report
|
|
578
|
-
said about each. Use \`recordedRevision\` for the next
|
|
579
|
-
\`flow_validation_start\`, or for \`flow_review_start\` only when \`passed: true\`. If
|
|
580
|
-
the marker is absent or malformed, refresh compact status before mutating.
|
|
581
|
-
|
|
582
|
-
Use focused validation for ordinary features. For the final feature, run the
|
|
583
|
-
plan's declared \`gate\` at broad scope after the last relevant edit. A source edit
|
|
584
|
-
invalidates earlier applicability. Failed or unavailable checks are blockers, not
|
|
585
|
-
passing evidence.
|
|
586
|
-
|
|
587
|
-
Immediately before review admission, reconcile the preflight inventory against the
|
|
588
|
-
recorded current-source observations.
|
|
589
|
-
|
|
590
|
-
## Review and record
|
|
504
|
+
For the final feature, run the plan's gate command at broad scope after the
|
|
505
|
+
last relevant edit.
|
|
506
|
+
|
|
507
|
+
An evidence command that cannot pass withholds completed closure. Reach the
|
|
508
|
+
passing command, or ask the user to choose deferred or abandoned closure.
|
|
509
|
+
|
|
510
|
+
## Review
|
|
591
511
|
|
|
592
512
|
After successful applicable validation, call \`flow_review_start\` with a fresh
|
|
593
|
-
operation id, current revision, feature id, \`artifactsChanged
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
Keep the packet bounded. Map IDs to current-source commands or scenarios,
|
|
598
|
-
environment, and results. Put the feature-specific risk checklist under
|
|
599
|
-
\`Risks/Matrix:\`, representing it as a transition matrix for concurrency or
|
|
600
|
-
state-machine work. Include \`Baseline:\` facts only when this feature changes or
|
|
601
|
-
depends on them, except that final review receives the full inventory.
|
|
602
|
-
Ordinary-review plan/source IDs are limited to active-feature mappings or IDs
|
|
603
|
-
explicitly supplied for its packet; final review includes every approved
|
|
604
|
-
requirement/feature ID. Omit empty optional sections; state \`none\` only for a relevant
|
|
605
|
-
inspected absence. Never hide a gap.
|
|
606
|
-
|
|
607
|
-
Dispatch only to reserved \`flow-reviewer\`; never review, copy, or submit its
|
|
608
|
-
verdict in manager context. It reads the assignment/workspace and calls
|
|
609
|
-
\`flow_feature_complete\` directly; runtime verifies the caller. It stays
|
|
610
|
-
workspace-read-only, with that result submission as its sole lifecycle mutation.
|
|
513
|
+
operation id, current revision, feature id, \`artifactsChanged\`, and a bounded
|
|
514
|
+
packet. Dispatch only reserved \`flow-reviewer\`. Never review or submit its
|
|
515
|
+
verdict in manager context.
|
|
611
516
|
|
|
612
517
|
After dispatch, read compact status. On top-level error, report exact
|
|
613
|
-
summary/recovery
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
\`dispatch-flow-reviewer\` or running \`flow_feature_reset\` route above. If status
|
|
618
|
-
is blocked, load detail through the single blocked route above. A recorded pass
|
|
619
|
-
completes the feature.
|
|
518
|
+
summary/recovery and stop without further mutation. If status remains running,
|
|
519
|
+
apply the \`dispatch-flow-reviewer\` or running \`flow_feature_reset\` route. If
|
|
520
|
+
status is blocked, apply **Blocked review**. A recorded pass completes the
|
|
521
|
+
feature.
|
|
620
522
|
|
|
621
523
|
### Blocked review
|
|
622
524
|
|
|
623
|
-
Follow \`nextAction\` with the one detail projection
|
|
525
|
+
Follow \`nextAction\` with the one detail projection for routing, then print
|
|
526
|
+
compact \`findingsDigest\` as the user-facing list. The runtime already weighs
|
|
624
527
|
\`failedReviewCount\` and \`blockedFeature.scopeBlocker\`.
|
|
625
528
|
|
|
626
|
-
- \`await-user-direction\`
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
529
|
+
- Ready \`await-user-direction\` has no blocked run left to reset. Identify the
|
|
530
|
+
planned feature whose latest relevant reviewed outcome remains failed and
|
|
531
|
+
checkpoint unless the current aligned request explicitly authorizes its
|
|
532
|
+
retry. When authorized, call \`flow_run_start\` with that exact \`featureId\`.
|
|
533
|
+
Never call \`flow_feature_reset\` from ready status.
|
|
534
|
+
- For blocked \`await-user-direction\`, checkpoint. Do not reset.
|
|
535
|
+
- For blocked \`flow_feature_reset\`, one automatic reset is allowed under
|
|
536
|
+
existing implementation authority. Pass the blocked \`featureId\` as
|
|
537
|
+
\`nextFeatureId\` so reset and run start are atomic. Fix only its blocking
|
|
630
538
|
findings, then run full validation and full independent review.
|
|
631
|
-
- A feature whose latest relevant reviewed outcome remains failed is never
|
|
632
|
-
selected implicitly. \`/flow-auto\` may still continue an untouched,
|
|
633
|
-
dependency-independent feature. When every runnable candidate requires a
|
|
634
|
-
retry, compact status is \`ready\` with \`await-user-direction\`, handled by the
|
|
635
|
-
ready route above.
|
|
636
539
|
- When \`failedReviewCount >= 2\`, retry only when the current aligned request
|
|
637
|
-
explicitly authorizes one additional attempt.
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
attempts and starts that exact run in one transaction. Do not reset first,
|
|
644
|
-
call \`flow_run_start\` separately, or rely on default selection.
|
|
645
|
-
|
|
646
|
-
When stopping blocked, label overall incomplete. Report the latest repair proved
|
|
647
|
-
pending a passing review; recurring and new blockers; goal/progress; blocked
|
|
648
|
-
feature, attempt, and failure count; completed/untouched features; latest
|
|
649
|
-
validations and \`artifactsChanged\` as Flow-reported artifact evidence; Git/release
|
|
650
|
-
mutation status; whether this request started and matched the goal; exact
|
|
651
|
-
\`nextAction\`; and whether another attempt requires explicit authorization.
|
|
652
|
-
|
|
653
|
-
Use that already-loaded compact status after every recorded outcome. Direct
|
|
654
|
-
\`/flow-run\` reports this one feature's cumulative outcome and \`nextAction\`, then
|
|
540
|
+
explicitly authorizes one additional attempt.
|
|
541
|
+
- If explicit direction selects another planned, dependency-independent
|
|
542
|
+
feature, pass that exact \`featureId\` as \`nextFeatureId\` on
|
|
543
|
+
\`flow_feature_reset\`.
|
|
544
|
+
|
|
545
|
+
Direct \`/flow-run\` reports compact \`findingsDigest\` and \`nextAction\`, then
|
|
655
546
|
stops. Under \`/flow-auto\`, return to its lifecycle loop.
|
|
656
547
|
`;
|
|
657
548
|
|
|
@@ -677,7 +568,9 @@ var FLOW_MANAGER_KERNEL = [
|
|
|
677
568
|
].join(" "),
|
|
678
569
|
[
|
|
679
570
|
"- Make one automatic fresh full retry only when the projected `nextAction`",
|
|
680
|
-
"is `flow_feature_reset`; otherwise checkpoint."
|
|
571
|
+
"is `flow_feature_reset`; otherwise checkpoint.",
|
|
572
|
+
"On `await-user-direction` or a lease stop, print compact `findingsDigest`",
|
|
573
|
+
"without inventing ids."
|
|
681
574
|
].join(" "),
|
|
682
575
|
[
|
|
683
576
|
"- Before review, require current-source evidence appropriate to the changed outcome,",
|
|
@@ -725,9 +618,8 @@ var FLOW_WORKER_PROMPT = [
|
|
|
725
618
|
"- Never edit .flow or .git metadata paths; the host denies those paths.",
|
|
726
619
|
"- A read-only evidence slice must not edit files.",
|
|
727
620
|
[
|
|
728
|
-
"-
|
|
729
|
-
"
|
|
730
|
-
"If it is missing, stop without editing and report the gap."
|
|
621
|
+
"- If the assignment includes a risk checklist or matrix, apply it before coding.",
|
|
622
|
+
"If required work would escape the assigned paths, stop and report the gap."
|
|
731
623
|
].join(" "),
|
|
732
624
|
[
|
|
733
625
|
"- An implementation slice may edit only the exact, non-overlapping write paths explicitly assigned by the manager.",
|
|
@@ -738,13 +630,7 @@ var FLOW_WORKER_PROMPT = [
|
|
|
738
630
|
"- The manager owns integration, focused checks, and authoritative combined validation",
|
|
739
631
|
"after all workers have stopped."
|
|
740
632
|
].join(" "),
|
|
741
|
-
|
|
742
|
-
"Before editing, apply the supplied risk coverage through its matrix rows when present:",
|
|
743
|
-
"primary behavior, failure and cleanup ordering, adjacent state transitions,",
|
|
744
|
-
"repeated or interrupted operation, overlapping invariants, and relevant persistence,",
|
|
745
|
-
"concurrency, security, compatibility, or file-metadata risks.",
|
|
746
|
-
"Preserve every named finding, requirement, or prior review ID in your handoff."
|
|
747
|
-
].join(" "),
|
|
633
|
+
"- Preserve every named finding, requirement, or prior review ID in your handoff.",
|
|
748
634
|
"## Handoff",
|
|
749
635
|
[
|
|
750
636
|
"Return exactly one concise handoff with `Status` (success, partial, or blocked),",
|
|
@@ -1089,6 +975,54 @@ function isFeatureId(value) {
|
|
|
1089
975
|
return FEATURE_ID_PATTERN.test(value);
|
|
1090
976
|
}
|
|
1091
977
|
|
|
978
|
+
// src/domain/session.ts
|
|
979
|
+
function planEvidence(plan) {
|
|
980
|
+
return plan?.evidence ?? [];
|
|
981
|
+
}
|
|
982
|
+
function planGate(plan) {
|
|
983
|
+
return planEvidence(plan).find((entry) => entry.scope === "gate")?.command;
|
|
984
|
+
}
|
|
985
|
+
function featureKind(feature) {
|
|
986
|
+
return feature?.kind === "inspect" ? "inspect" : "change";
|
|
987
|
+
}
|
|
988
|
+
function reviewResultSemanticIssues(result) {
|
|
989
|
+
const issues = [];
|
|
990
|
+
const blocking = result.findings.some((finding) => finding.severity === "blocking");
|
|
991
|
+
for (const [index, finding] of result.findings.entries()) {
|
|
992
|
+
if (finding.severity === "blocking" && !finding.evidence?.trim()) {
|
|
993
|
+
issues.push({
|
|
994
|
+
path: ["findings", index, "evidence"],
|
|
995
|
+
message: "A blocking finding requires concrete evidence."
|
|
996
|
+
});
|
|
997
|
+
}
|
|
998
|
+
if (finding.scopeBlocker && finding.severity !== "blocking") {
|
|
999
|
+
issues.push({
|
|
1000
|
+
path: ["findings", index, "scopeBlocker"],
|
|
1001
|
+
message: "Only a blocking finding can be a scope blocker."
|
|
1002
|
+
});
|
|
1003
|
+
}
|
|
1004
|
+
}
|
|
1005
|
+
if (result.verdict === "failed" && !blocking) {
|
|
1006
|
+
issues.push({
|
|
1007
|
+
path: ["findings"],
|
|
1008
|
+
message: "A failed review requires a blocking finding."
|
|
1009
|
+
});
|
|
1010
|
+
}
|
|
1011
|
+
if (result.verdict === "passed" && blocking) {
|
|
1012
|
+
issues.push({
|
|
1013
|
+
path: ["findings"],
|
|
1014
|
+
message: "A passed review cannot contain blocking findings."
|
|
1015
|
+
});
|
|
1016
|
+
}
|
|
1017
|
+
if (result.terminalDisposition === "observed_unsubmitted" && result.verdict !== "failed") {
|
|
1018
|
+
issues.push({
|
|
1019
|
+
path: ["terminalDisposition"],
|
|
1020
|
+
message: "Observed-but-unsubmitted review work must fail closed."
|
|
1021
|
+
});
|
|
1022
|
+
}
|
|
1023
|
+
return issues;
|
|
1024
|
+
}
|
|
1025
|
+
|
|
1092
1026
|
// src/domain/test-results.ts
|
|
1093
1027
|
var TESTCASE = /<testcase\b([^>]*?)(\/>|>([\s\S]*?)<\/testcase\s*>)/g;
|
|
1094
1028
|
var ATTRIBUTE = /([\w:.-]+)\s*=\s*"([^"]*)"/g;
|
|
@@ -1202,7 +1136,7 @@ function narrowingArguments(command) {
|
|
|
1202
1136
|
}
|
|
1203
1137
|
function declaredAssertions(session, command) {
|
|
1204
1138
|
return [
|
|
1205
|
-
...new Set((session.plan
|
|
1139
|
+
...new Set(planEvidence(session.plan).filter((entry) => entry.command === command).flatMap((entry) => entry.assertions ?? []))
|
|
1206
1140
|
];
|
|
1207
1141
|
}
|
|
1208
1142
|
function sameAssertions(left, right) {
|
|
@@ -1241,7 +1175,7 @@ function recordValidation(session, input) {
|
|
|
1241
1175
|
if (narrowing.length > 0) {
|
|
1242
1176
|
throw new FlowTransitionError(`A broad observation cannot select which tests it runs (${narrowing.join(", ")}). Arm the repository's canonical gate, or record this command as focused.`);
|
|
1243
1177
|
}
|
|
1244
|
-
const gate = session.plan
|
|
1178
|
+
const gate = planGate(session.plan);
|
|
1245
1179
|
if (gate !== undefined && input.command !== gate) {
|
|
1246
1180
|
throw new FlowTransitionError(`A broad observation must run the plan-declared canonical gate (${gate}). Arm that exact command, or record this one as focused.`);
|
|
1247
1181
|
}
|
|
@@ -1282,7 +1216,7 @@ function isObservedOnDeclaredPlatform(entry, observation) {
|
|
|
1282
1216
|
return true;
|
|
1283
1217
|
return observation.hostPlatform === entry.platform;
|
|
1284
1218
|
}
|
|
1285
|
-
function
|
|
1219
|
+
function evidenceRefusal(session, entry, sourceDigest) {
|
|
1286
1220
|
const eligible = session.runs.flatMap((run) => run.validations).filter((observation) => observation.command === entry.command && isValidationEligible(observation, sourceDigest));
|
|
1287
1221
|
const wrongHosts = [
|
|
1288
1222
|
...new Set(eligible.filter((observation) => !isObservedOnDeclaredPlatform(entry, observation)).map((observation) => observation.hostPlatform ?? "an unrecorded host"))
|
|
@@ -1292,18 +1226,21 @@ function externalEvidenceRefusal(session, entry, sourceDigest) {
|
|
|
1292
1226
|
const detail = wrongHosts.length > 0 ? `passed on ${wrongHosts.join(", ")} but this entry declares ${entry.platform}, so that run observed something else — a skipped case exits zero too` : unmet ? `passed on ${entry.platform ?? "the declared host"} but reported no passing result for ${unmet.join(", ")}; arm it again with \`resultsPath\` naming the report the command writes, and make those cases run` : `needs ${needs}`;
|
|
1293
1227
|
return `${JSON.stringify(entry.command)} (${detail}, for ${entry.requirement})`;
|
|
1294
1228
|
}
|
|
1295
|
-
function
|
|
1296
|
-
const declared = session.plan
|
|
1229
|
+
function unsatisfiedEvidence(session, sourceDigest) {
|
|
1230
|
+
const declared = planEvidence(session.plan);
|
|
1297
1231
|
if (declared.length === 0)
|
|
1298
1232
|
return [];
|
|
1299
1233
|
const observed = session.runs.flatMap((run) => run.validations);
|
|
1300
1234
|
return declared.filter((entry) => !observed.some((observation) => observation.command === entry.command && isObservedOnDeclaredPlatform(entry, observation) && assertionsSatisfied(entry.assertions ?? [], observation.observedAssertions) && isValidationEligible(observation, sourceDigest)));
|
|
1301
1235
|
}
|
|
1236
|
+
function unsatisfiedExtraEvidence(session, sourceDigest) {
|
|
1237
|
+
return unsatisfiedEvidence(session, sourceDigest).filter((entry) => entry.scope === "extra");
|
|
1238
|
+
}
|
|
1302
1239
|
function isValidationFresh(session, run, observation) {
|
|
1303
1240
|
return session.runs.filter((candidate) => candidate.featureId === run.featureId).flatMap((candidate) => candidate.validations).every((candidate) => candidate.command !== observation.command || isValidationEligible(candidate) || candidate.recordedRevision < observation.recordedRevision);
|
|
1304
1241
|
}
|
|
1305
1242
|
function unresolvedVetoedCommands(session, run, sourceDigest) {
|
|
1306
|
-
const gate = session.plan
|
|
1243
|
+
const gate = planGate(session.plan);
|
|
1307
1244
|
const planned = session.approval === "approved" ? [
|
|
1308
1245
|
...session.plan?.features.find((candidate) => candidate.id === run.featureId)?.validation ?? [],
|
|
1309
1246
|
...gate === undefined ? [] : [gate]
|
|
@@ -1321,8 +1258,9 @@ function gateIssue(gate) {
|
|
|
1321
1258
|
return narrowing.length === 0 ? null : `The plan's canonical gate cannot select which tests it runs (${narrowing.join(", ")}).`;
|
|
1322
1259
|
}
|
|
1323
1260
|
function planIssue(plan) {
|
|
1324
|
-
|
|
1325
|
-
|
|
1261
|
+
const gate = planGate(plan);
|
|
1262
|
+
if (gate !== undefined) {
|
|
1263
|
+
const issue = gateIssue(gate);
|
|
1326
1264
|
if (issue)
|
|
1327
1265
|
return issue;
|
|
1328
1266
|
}
|
|
@@ -1436,45 +1374,6 @@ function droppedFindingIds(session, featureId, findings) {
|
|
|
1436
1374
|
return liveFindingIds(session, featureId).filter((id) => !submitted.has(id));
|
|
1437
1375
|
}
|
|
1438
1376
|
|
|
1439
|
-
// src/domain/session.ts
|
|
1440
|
-
function reviewResultSemanticIssues(result) {
|
|
1441
|
-
const issues = [];
|
|
1442
|
-
const blocking = result.findings.some((finding) => finding.severity === "blocking");
|
|
1443
|
-
for (const [index, finding] of result.findings.entries()) {
|
|
1444
|
-
if (finding.severity === "blocking" && !finding.evidence?.trim()) {
|
|
1445
|
-
issues.push({
|
|
1446
|
-
path: ["findings", index, "evidence"],
|
|
1447
|
-
message: "A blocking finding requires concrete evidence."
|
|
1448
|
-
});
|
|
1449
|
-
}
|
|
1450
|
-
if (finding.scopeBlocker && finding.severity !== "blocking") {
|
|
1451
|
-
issues.push({
|
|
1452
|
-
path: ["findings", index, "scopeBlocker"],
|
|
1453
|
-
message: "Only a blocking finding can be a scope blocker."
|
|
1454
|
-
});
|
|
1455
|
-
}
|
|
1456
|
-
}
|
|
1457
|
-
if (result.verdict === "failed" && !blocking) {
|
|
1458
|
-
issues.push({
|
|
1459
|
-
path: ["findings"],
|
|
1460
|
-
message: "A failed review requires a blocking finding."
|
|
1461
|
-
});
|
|
1462
|
-
}
|
|
1463
|
-
if (result.verdict === "passed" && blocking) {
|
|
1464
|
-
issues.push({
|
|
1465
|
-
path: ["findings"],
|
|
1466
|
-
message: "A passed review cannot contain blocking findings."
|
|
1467
|
-
});
|
|
1468
|
-
}
|
|
1469
|
-
if (result.terminalDisposition === "observed_unsubmitted" && result.verdict !== "failed") {
|
|
1470
|
-
issues.push({
|
|
1471
|
-
path: ["terminalDisposition"],
|
|
1472
|
-
message: "Observed-but-unsubmitted review work must fail closed."
|
|
1473
|
-
});
|
|
1474
|
-
}
|
|
1475
|
-
return issues;
|
|
1476
|
-
}
|
|
1477
|
-
|
|
1478
1377
|
// src/domain/transitions.ts
|
|
1479
1378
|
function fail(message) {
|
|
1480
1379
|
throw new FlowTransitionError(message);
|
|
@@ -1530,20 +1429,19 @@ function assertPlan(plan) {
|
|
|
1530
1429
|
if (issue)
|
|
1531
1430
|
fail(issue);
|
|
1532
1431
|
}
|
|
1533
|
-
function
|
|
1534
|
-
if (plan.
|
|
1535
|
-
fail(
|
|
1432
|
+
function assertDeclaredEvidence(plan) {
|
|
1433
|
+
if (plan.evidence === undefined) {
|
|
1434
|
+
fail('A saved plan must declare `evidence`: one `scope: "gate"` command that validates the whole repository, plus every extra observation this host may be unable to produce. Extra entries may be an empty list when the goal is fully observable here.');
|
|
1536
1435
|
}
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
fail("A saved plan must declare `externalEvidence`: every acceptance observation needing an environment this host may not be, each with the exact command whose passing is that observation. Declare an empty list when the goal is fully observable here.");
|
|
1436
|
+
const gates = planEvidence(plan).filter((entry) => entry.scope === "gate");
|
|
1437
|
+
if (gates.length !== 1) {
|
|
1438
|
+
fail('A saved plan must declare exactly one `evidence` entry with `scope: "gate"`: the exact canonical command every broad observation then has to run.');
|
|
1541
1439
|
}
|
|
1542
|
-
if (plan.
|
|
1543
|
-
fail("Every `
|
|
1440
|
+
if (plan.evidence.some((entry) => entry.platform === undefined)) {
|
|
1441
|
+
fail("Every `evidence` entry must declare `platform`: the operating system that can observe it (`win32`, `darwin`, or `linux`), or `other` when the missing environment is a service, credential, setting, or device rather than an OS.");
|
|
1544
1442
|
}
|
|
1545
|
-
if (plan.
|
|
1546
|
-
fail("Every `
|
|
1443
|
+
if (plan.evidence.some((entry) => entry.assertions === undefined)) {
|
|
1444
|
+
fail("Every `evidence` entry must declare `assertions`: the test case names whose passing is that observation. Declare an empty list when the evidence is not a test result.");
|
|
1547
1445
|
}
|
|
1548
1446
|
}
|
|
1549
1447
|
function assertArtifacts(artifacts) {
|
|
@@ -1578,8 +1476,7 @@ function sessionStatus(session) {
|
|
|
1578
1476
|
function savePlan(session, input, environment) {
|
|
1579
1477
|
assertPlan(input.plan);
|
|
1580
1478
|
if (!session) {
|
|
1581
|
-
|
|
1582
|
-
assertDeclaredExternalEvidence(input.plan);
|
|
1479
|
+
assertDeclaredEvidence(input.plan);
|
|
1583
1480
|
if (input.expectedRevision !== 0) {
|
|
1584
1481
|
fail("A new Flow session must start from expectedRevision 0.");
|
|
1585
1482
|
}
|
|
@@ -1609,8 +1506,7 @@ function savePlan(session, input, environment) {
|
|
|
1609
1506
|
const replay = existingOperation(session, "plan-save", input.operationId, input);
|
|
1610
1507
|
if (replay)
|
|
1611
1508
|
return { session, value: null, replayed: true };
|
|
1612
|
-
|
|
1613
|
-
assertDeclaredExternalEvidence(input.plan);
|
|
1509
|
+
assertDeclaredEvidence(input.plan);
|
|
1614
1510
|
assertRevision(session, input.expectedRevision);
|
|
1615
1511
|
assertMutable(session);
|
|
1616
1512
|
if (session.approval === "approved")
|
|
@@ -1753,9 +1649,9 @@ function startReview(session, input, environment) {
|
|
|
1753
1649
|
}
|
|
1754
1650
|
const kind = isFinalFeatureRun(session, run) ? "final" : "feature";
|
|
1755
1651
|
if (kind === "final") {
|
|
1756
|
-
const unsatisfied =
|
|
1652
|
+
const unsatisfied = unsatisfiedExtraEvidence(session, input.sourceDigest);
|
|
1757
1653
|
if (unsatisfied.length > 0) {
|
|
1758
|
-
fail(`Final review requires the plan's declared
|
|
1654
|
+
fail(`Final review requires the plan's declared evidence to pass for the current workspace content: ${unsatisfied.map((entry) => evidenceRefusal(session, entry, input.sourceDigest)).join(", ")}. A substitute observation cannot discharge it. If the environment is unavailable, ask the user to choose deferred or abandoned closure.`);
|
|
1759
1655
|
}
|
|
1760
1656
|
}
|
|
1761
1657
|
const applicable = run.validations.filter((validation) => isValidationEligible(validation, input.sourceDigest) && isValidationFresh(session, run, validation));
|
|
@@ -1830,6 +1726,7 @@ function completeFeature(session, input) {
|
|
|
1830
1726
|
}
|
|
1831
1727
|
}
|
|
1832
1728
|
const findings = assignFindingIds(input.result.findings, findingIdPrefix(run.featureId, assignment.createdRevision));
|
|
1729
|
+
const inspect = featureKind(session.plan?.features.find((feature) => feature.id === run.featureId)) === "inspect";
|
|
1833
1730
|
const next = commit(session, "feature-complete", input.operationId, input, (draft, revision) => ({
|
|
1834
1731
|
...draft,
|
|
1835
1732
|
runs: draft.runs.map((item) => {
|
|
@@ -1837,7 +1734,7 @@ function completeFeature(session, input) {
|
|
|
1837
1734
|
return item;
|
|
1838
1735
|
return {
|
|
1839
1736
|
...item,
|
|
1840
|
-
state: input.result.verdict === "passed" ? "completed" : "blocked",
|
|
1737
|
+
state: input.result.verdict === "passed" || inspect ? "completed" : "blocked",
|
|
1841
1738
|
summary: input.summary,
|
|
1842
1739
|
reviews: item.reviews.map((review) => review.id === assignment.id ? {
|
|
1843
1740
|
...review,
|
|
@@ -1932,12 +1829,12 @@ function closeSession(session, input) {
|
|
|
1932
1829
|
fail("sessionId does not match active state.");
|
|
1933
1830
|
assertMutable(session);
|
|
1934
1831
|
if (input.kind === "completed" && sessionStatus(session) !== "completed") {
|
|
1935
|
-
fail("A completed close requires every planned feature to
|
|
1832
|
+
fail("A completed close requires every planned feature to be complete.");
|
|
1936
1833
|
}
|
|
1937
1834
|
if (input.kind === "completed") {
|
|
1938
|
-
const unsatisfied =
|
|
1835
|
+
const unsatisfied = unsatisfiedExtraEvidence(session);
|
|
1939
1836
|
if (unsatisfied.length > 0) {
|
|
1940
|
-
fail(`A completed close requires the plan's declared
|
|
1837
|
+
fail(`A completed close requires the plan's declared evidence to have passed: ${unsatisfied.map((entry) => evidenceRefusal(session, entry)).join(", ")}. Close deferred or abandoned instead.`);
|
|
1941
1838
|
}
|
|
1942
1839
|
}
|
|
1943
1840
|
let closure = null;
|
|
@@ -1992,8 +1889,14 @@ class ArchiveCollisionError extends Error {
|
|
|
1992
1889
|
import { z } from "zod";
|
|
1993
1890
|
|
|
1994
1891
|
// src/domain/session-invariants.ts
|
|
1995
|
-
function
|
|
1996
|
-
|
|
1892
|
+
function featureSettledBefore(session, featureId, revision) {
|
|
1893
|
+
const inspect = featureKind(session.plan?.features.find((feature) => feature.id === featureId)) === "inspect";
|
|
1894
|
+
return session.runs.some((run) => run.featureId === featureId && run.reviews.some((review) => {
|
|
1895
|
+
const result = review.result;
|
|
1896
|
+
if (!result || result.recordedRevision >= revision)
|
|
1897
|
+
return false;
|
|
1898
|
+
return result.verdict === "passed" || inspect;
|
|
1899
|
+
}));
|
|
1997
1900
|
}
|
|
1998
1901
|
function sessionInvariantIssues(session) {
|
|
1999
1902
|
const issues = [];
|
|
@@ -2110,7 +2013,7 @@ function sessionInvariantIssues(session) {
|
|
|
2110
2013
|
if (referenced.some((validation) => validation.recordedRevision >= review.createdRevision)) {
|
|
2111
2014
|
issues.push(`Review '${review.id}' references later validation.`);
|
|
2112
2015
|
}
|
|
2113
|
-
const expectedKind = session.plan.features.every((feature) => feature.id === run.featureId ||
|
|
2016
|
+
const expectedKind = session.plan.features.every((feature) => feature.id === run.featureId || featureSettledBefore(session, feature.id, review.createdRevision)) ? "final" : "feature";
|
|
2114
2017
|
if (review.kind !== expectedKind) {
|
|
2115
2018
|
issues.push(`Review '${review.id}' has the wrong derived kind.`);
|
|
2116
2019
|
}
|
|
@@ -2128,7 +2031,10 @@ function sessionInvariantIssues(session) {
|
|
|
2128
2031
|
issues.push(`Active run '${run.id}' contains a recorded outcome.`);
|
|
2129
2032
|
}
|
|
2130
2033
|
if (run.state === "completed" && last?.result?.verdict !== "passed") {
|
|
2131
|
-
|
|
2034
|
+
const inspect = featureKind(session.plan?.features.find((feature) => feature.id === run.featureId)) === "inspect";
|
|
2035
|
+
if (!(inspect && last?.result?.verdict === "failed")) {
|
|
2036
|
+
issues.push(`Completed run '${run.id}' lacks a passing review.`);
|
|
2037
|
+
}
|
|
2132
2038
|
}
|
|
2133
2039
|
if (run.state === "blocked" && last?.result?.verdict !== "failed") {
|
|
2134
2040
|
issues.push(`Blocked run '${run.id}' lacks a failed review.`);
|
|
@@ -2167,14 +2073,16 @@ var PlanFeatureSchema = z.object({
|
|
|
2167
2073
|
summary: boundedText("Feature summary"),
|
|
2168
2074
|
targets: z.array(boundedText("Feature target")).max(MAX_PLAN_FEATURES).default([]),
|
|
2169
2075
|
validation: z.array(boundedText("Feature validation")).max(MAX_PLAN_FEATURES).default([]),
|
|
2170
|
-
dependsOn: z.array(FeatureIdSchema).max(MAX_PLAN_FEATURES).default([])
|
|
2076
|
+
dependsOn: z.array(FeatureIdSchema).max(MAX_PLAN_FEATURES).default([]),
|
|
2077
|
+
kind: z.enum(["change", "inspect"]).optional()
|
|
2171
2078
|
}).strict();
|
|
2172
|
-
var
|
|
2173
|
-
requirement: boundedText("
|
|
2174
|
-
environment: boundedText("
|
|
2175
|
-
command: boundedText("
|
|
2079
|
+
var EvidenceEntrySchema = z.object({
|
|
2080
|
+
requirement: boundedText("Evidence requirement"),
|
|
2081
|
+
environment: boundedText("Evidence environment"),
|
|
2082
|
+
command: boundedText("Evidence command"),
|
|
2083
|
+
scope: z.enum(["gate", "extra"]),
|
|
2176
2084
|
platform: z.enum(EVIDENCE_PLATFORMS).optional(),
|
|
2177
|
-
assertions: z.array(boundedText("
|
|
2085
|
+
assertions: z.array(boundedText("Evidence assertion")).max(MAX_DECLARED_ASSERTIONS).optional()
|
|
2178
2086
|
}).strict();
|
|
2179
2087
|
var ObservedAssertionSchema = z.object({
|
|
2180
2088
|
name: boundedText("Observed assertion name"),
|
|
@@ -2186,8 +2094,7 @@ var PlanSchema = z.object({
|
|
|
2186
2094
|
requirements: z.array(boundedText("Plan requirement")).max(MAX_PLAN_FEATURES).default([]),
|
|
2187
2095
|
decisions: z.array(boundedText("Plan decision")).max(MAX_PLAN_FEATURES).default([]),
|
|
2188
2096
|
features: z.array(PlanFeatureSchema).min(1).max(MAX_PLAN_FEATURES),
|
|
2189
|
-
|
|
2190
|
-
externalEvidence: z.array(ExternalEvidenceSchema).max(MAX_PLAN_FEATURES).optional()
|
|
2097
|
+
evidence: z.array(EvidenceEntrySchema).max(MAX_PLAN_FEATURES).optional()
|
|
2191
2098
|
}).strict().superRefine((plan, context) => {
|
|
2192
2099
|
if (encoder.encode(JSON.stringify(plan)).byteLength > MAX_PLAN_BYTES) {
|
|
2193
2100
|
context.addIssue({
|
|
@@ -2377,6 +2284,69 @@ var StatusInputSchema = z.object({
|
|
|
2377
2284
|
])
|
|
2378
2285
|
}).strict();
|
|
2379
2286
|
|
|
2287
|
+
// src/application/findings-digest.ts
|
|
2288
|
+
function plannedFeatureIds(session) {
|
|
2289
|
+
if (session.plan)
|
|
2290
|
+
return session.plan.features.map((feature) => feature.id);
|
|
2291
|
+
const ids = [];
|
|
2292
|
+
for (const run of session.runs) {
|
|
2293
|
+
if (ids.includes(run.featureId))
|
|
2294
|
+
continue;
|
|
2295
|
+
ids.push(run.featureId);
|
|
2296
|
+
}
|
|
2297
|
+
return ids;
|
|
2298
|
+
}
|
|
2299
|
+
function findingsDigest(session) {
|
|
2300
|
+
const rows = [];
|
|
2301
|
+
const indexById = new Map;
|
|
2302
|
+
for (const featureId of plannedFeatureIds(session)) {
|
|
2303
|
+
for (const run of session.runs) {
|
|
2304
|
+
if (run.featureId !== featureId)
|
|
2305
|
+
continue;
|
|
2306
|
+
for (const review of run.reviews) {
|
|
2307
|
+
const result = review.result;
|
|
2308
|
+
if (!result)
|
|
2309
|
+
continue;
|
|
2310
|
+
for (const finding of result.findings) {
|
|
2311
|
+
if (!finding.findingId)
|
|
2312
|
+
continue;
|
|
2313
|
+
const row = {
|
|
2314
|
+
featureId,
|
|
2315
|
+
findingId: finding.findingId,
|
|
2316
|
+
severity: finding.severity,
|
|
2317
|
+
summary: finding.summary,
|
|
2318
|
+
attempt: run.attempt,
|
|
2319
|
+
verdict: result.verdict,
|
|
2320
|
+
live: false,
|
|
2321
|
+
...finding.evidence === undefined ? {} : { evidence: finding.evidence }
|
|
2322
|
+
};
|
|
2323
|
+
const existing = indexById.get(finding.findingId);
|
|
2324
|
+
if (existing === undefined) {
|
|
2325
|
+
indexById.set(finding.findingId, rows.length);
|
|
2326
|
+
rows.push(row);
|
|
2327
|
+
} else {
|
|
2328
|
+
rows[existing] = row;
|
|
2329
|
+
}
|
|
2330
|
+
}
|
|
2331
|
+
}
|
|
2332
|
+
}
|
|
2333
|
+
}
|
|
2334
|
+
return rows.map((row) => ({
|
|
2335
|
+
...row,
|
|
2336
|
+
live: liveFindingIds(session, row.featureId).includes(row.findingId)
|
|
2337
|
+
}));
|
|
2338
|
+
}
|
|
2339
|
+
function digestReportLines(digest) {
|
|
2340
|
+
if (digest.length === 0)
|
|
2341
|
+
return ["Findings digest: none"];
|
|
2342
|
+
const line = (row, kind) => `- ${kind} ${row.featureId} ${row.findingId} ${row.severity}: ${row.summary}`;
|
|
2343
|
+
return [
|
|
2344
|
+
"Findings digest:",
|
|
2345
|
+
...digest.filter((row) => row.live).map((row) => line(row, "live")),
|
|
2346
|
+
...digest.filter((row) => !row.live).map((row) => line(row, "historical"))
|
|
2347
|
+
];
|
|
2348
|
+
}
|
|
2349
|
+
|
|
2380
2350
|
// src/application/delivery.ts
|
|
2381
2351
|
var LIMITATIONS = [
|
|
2382
2352
|
"Artifact paths and the canonical gate are caller declarations; Flow validates binding, not completeness or fitness.",
|
|
@@ -2413,23 +2383,23 @@ function assuranceProjection(session) {
|
|
|
2413
2383
|
check("recorded-completion", "Recorded completion", "ts-enforced", structural, `${completed}/${features.length} features and ${passing}/${features.length} independent reviews pass, including a final review with no terminal blocker.`),
|
|
2414
2384
|
check("accepted-validation", "Accepted validation", "host-attested", runs.length === features.length && runs.every((run) => accepted.some((observation) => observation.runId === run.id)), `${runs.filter((run) => accepted.some((item) => item.runId === run.id)).length}/${features.length} terminal runs have eligible host evidence accepted by review.`)
|
|
2415
2385
|
];
|
|
2416
|
-
const gate = session.plan
|
|
2386
|
+
const gate = planGate(session.plan);
|
|
2417
2387
|
checks.push(gate === undefined ? {
|
|
2418
2388
|
id: "canonical-gate",
|
|
2419
2389
|
label: "Canonical gate",
|
|
2420
2390
|
tier: "caller-declared",
|
|
2421
2391
|
status: "not-applicable",
|
|
2422
|
-
explanation: "This
|
|
2392
|
+
explanation: "This plan declared no canonical gate."
|
|
2423
2393
|
} : check("canonical-gate", "Canonical gate", "host-attested", accepted.some((observation) => observation.command === gate && observation.scope === "broad"), `${JSON.stringify(gate)} must have eligible broad evidence accepted by review.`));
|
|
2424
|
-
const declared = session.plan?.
|
|
2425
|
-
const missing =
|
|
2394
|
+
const declared = session.plan?.evidence;
|
|
2395
|
+
const missing = unsatisfiedEvidence(session).length;
|
|
2426
2396
|
checks.push(declared === undefined ? {
|
|
2427
|
-
id: "
|
|
2428
|
-
label: "Declared
|
|
2397
|
+
id: "declared-evidence",
|
|
2398
|
+
label: "Declared evidence",
|
|
2429
2399
|
tier: "caller-declared",
|
|
2430
2400
|
status: "not-applicable",
|
|
2431
|
-
explanation: "This
|
|
2432
|
-
} : check("
|
|
2401
|
+
explanation: "This plan declared no evidence obligations."
|
|
2402
|
+
} : check("declared-evidence", "Declared evidence", declared.length === 0 ? "caller-declared" : "host-attested", missing === 0, `${declared.length - missing}/${declared.length} declared obligations have eligible evidence on their declared host with named cases passing.`));
|
|
2433
2403
|
return {
|
|
2434
2404
|
conclusion: !complete ? "completion-not-claimed" : checks.some((item) => item.status === "unsatisfied") ? "completion-unsupported" : "completion-supported",
|
|
2435
2405
|
checks,
|
|
@@ -2457,6 +2427,7 @@ function formatReport(delivery) {
|
|
|
2457
2427
|
`Progress: ${delivery.progress.completed} of ${delivery.progress.total} features complete`,
|
|
2458
2428
|
"Features:",
|
|
2459
2429
|
...lines,
|
|
2430
|
+
...digestReportLines(delivery.findingsDigest),
|
|
2460
2431
|
`Assurance: ${delivery.assurance.conclusion.replaceAll("-", " ")}`,
|
|
2461
2432
|
"Assurance checks:",
|
|
2462
2433
|
...delivery.assurance.checks.map((item) => `- ${item.status} [${TIER_LABELS[item.tier]}] ${item.label}: ${item.explanation}`),
|
|
@@ -2478,6 +2449,7 @@ function deliveryProjection(session) {
|
|
|
2478
2449
|
const latest = grouped.flatMap(({ runs }) => runs.slice(-1));
|
|
2479
2450
|
const latestArtifacts = new Set(latest.flatMap((run) => run.artifactsChanged.map((item) => item.path)));
|
|
2480
2451
|
const allArtifacts = new Set(session.runs.flatMap((run) => run.artifactsChanged.map((item) => item.path)));
|
|
2452
|
+
const digest = findingsDigest(session);
|
|
2481
2453
|
const delivery = {
|
|
2482
2454
|
goal: session.goal,
|
|
2483
2455
|
closure: { kind: session.closure.kind, summary: session.closure.summary },
|
|
@@ -2493,17 +2465,15 @@ function deliveryProjection(session) {
|
|
|
2493
2465
|
attempts: runs.length,
|
|
2494
2466
|
latestState: run?.state ?? "not-started",
|
|
2495
2467
|
outcomeSummary: run?.summary ?? null,
|
|
2496
|
-
terminalFindings:
|
|
2497
|
-
severity,
|
|
2498
|
-
summary
|
|
2499
|
-
})) ?? []
|
|
2468
|
+
terminalFindings: digest.filter((row) => row.featureId === feature.id && row.live).map(({ severity, summary }) => ({ severity, summary }))
|
|
2500
2469
|
};
|
|
2501
2470
|
}),
|
|
2502
2471
|
reportedArtifacts: {
|
|
2503
2472
|
latestAttempts: [...latestArtifacts].sort(),
|
|
2504
2473
|
supersededAttemptsOnly: [...allArtifacts].filter((path) => !latestArtifacts.has(path)).sort()
|
|
2505
2474
|
},
|
|
2506
|
-
assurance: assuranceProjection(session)
|
|
2475
|
+
assurance: assuranceProjection(session),
|
|
2476
|
+
findingsDigest: digest
|
|
2507
2477
|
};
|
|
2508
2478
|
return { ...delivery, report: formatReport(delivery) };
|
|
2509
2479
|
}
|
|
@@ -2579,7 +2549,8 @@ function compactProjection(session, pendingReviewSourceStale = false) {
|
|
|
2579
2549
|
blockedFeature,
|
|
2580
2550
|
progress: featureProgress(session),
|
|
2581
2551
|
nextAction: nextAction(session, pendingReviewSourceStale, blockedFeature),
|
|
2582
|
-
archiveRetry: retryRequest ? { request: retryRequest } : null
|
|
2552
|
+
archiveRetry: retryRequest ? { request: retryRequest } : null,
|
|
2553
|
+
findingsDigest: findingsDigest(session)
|
|
2583
2554
|
};
|
|
2584
2555
|
}
|
|
2585
2556
|
function archivedProjection(session) {
|
|
@@ -2637,9 +2608,8 @@ function reviewerProjection(session, assignmentId) {
|
|
|
2637
2608
|
validation: [...candidate.validation],
|
|
2638
2609
|
dependsOn: [...candidate.dependsOn]
|
|
2639
2610
|
})),
|
|
2640
|
-
...plan.
|
|
2641
|
-
|
|
2642
|
-
externalEvidence: plan.externalEvidence.map((entry) => ({
|
|
2611
|
+
...plan.evidence === undefined ? {} : {
|
|
2612
|
+
evidence: plan.evidence.map((entry) => ({
|
|
2643
2613
|
...entry
|
|
2644
2614
|
}))
|
|
2645
2615
|
}
|
|
@@ -2668,7 +2638,8 @@ function idleProjection(view) {
|
|
|
2668
2638
|
view,
|
|
2669
2639
|
status: "idle",
|
|
2670
2640
|
revision: 0,
|
|
2671
|
-
nextAction: "flow_plan_save"
|
|
2641
|
+
nextAction: "flow_plan_save",
|
|
2642
|
+
findingsDigest: []
|
|
2672
2643
|
};
|
|
2673
2644
|
}
|
|
2674
2645
|
function project(session, request, pendingReviewSourceStale = false) {
|
|
@@ -3919,6 +3890,10 @@ var CONTINUATION_ROUTE = [
|
|
|
3919
3890
|
"for a fresh close use compact session id/revision plus a fresh operation id,",
|
|
3920
3891
|
"and replay archiveRetry exactly from its projected request."
|
|
3921
3892
|
].join(" ");
|
|
3893
|
+
var HANDBACK_ROUTE = [
|
|
3894
|
+
"Call flow_status with the compact view first.",
|
|
3895
|
+
"Print findingsDigest as the user-facing list. Do not invent ids."
|
|
3896
|
+
].join(" ");
|
|
3922
3897
|
function inspectMessage(parts) {
|
|
3923
3898
|
let token = null;
|
|
3924
3899
|
let text = "";
|
|
@@ -3944,6 +3919,9 @@ function isCheckpoint(projection) {
|
|
|
3944
3919
|
function isPendingReviewer(projection) {
|
|
3945
3920
|
return projection.status === "running" && projection.nextAction === "dispatch-flow-reviewer";
|
|
3946
3921
|
}
|
|
3922
|
+
function isHandback(projection) {
|
|
3923
|
+
return projection.status === "blocked" || projection.nextAction === "flow_feature_reset" || projection.nextAction === "dispatch-flow-reviewer";
|
|
3924
|
+
}
|
|
3947
3925
|
|
|
3948
3926
|
class AutoDriveCoordinator {
|
|
3949
3927
|
#lease = null;
|
|
@@ -3993,6 +3971,31 @@ class AutoDriveCoordinator {
|
|
|
3993
3971
|
lease.lastPromptedRevision = null;
|
|
3994
3972
|
this.#setTiming("waiting-for-user");
|
|
3995
3973
|
}
|
|
3974
|
+
async#promptHandback(lease, projection) {
|
|
3975
|
+
if (!isHandback(projection))
|
|
3976
|
+
return;
|
|
3977
|
+
if (lease.handbackPromptedRevision === projection.revision)
|
|
3978
|
+
return;
|
|
3979
|
+
if (!lease.delivery)
|
|
3980
|
+
return;
|
|
3981
|
+
lease.handbackPromptedRevision = projection.revision;
|
|
3982
|
+
lease.messageId = null;
|
|
3983
|
+
this.#setTiming("active");
|
|
3984
|
+
lease.inFlight = "prompt";
|
|
3985
|
+
try {
|
|
3986
|
+
const handback = [
|
|
3987
|
+
`Flow is handing control back at compact revision ${projection.revision}.`,
|
|
3988
|
+
HANDBACK_ROUTE,
|
|
3989
|
+
`Then follow ${projection.nextAction} or stop at await-user-direction.`,
|
|
3990
|
+
"Do not expand the approved goal."
|
|
3991
|
+
].join(" ");
|
|
3992
|
+
await this.#options.prompt(lease.hostSessionId, `${handback}
|
|
3993
|
+
|
|
3994
|
+
${FLOW_MANAGER_KERNEL}`, lease.delivery, { [FLOW_AUTO_METADATA_KEY]: lease.token });
|
|
3995
|
+
} catch (error) {
|
|
3996
|
+
this.#stop(lease, `Flow auto prompt failed: ${String(error)}`);
|
|
3997
|
+
}
|
|
3998
|
+
}
|
|
3996
3999
|
async#read(lease) {
|
|
3997
4000
|
try {
|
|
3998
4001
|
return await this.#options.readProjection();
|
|
@@ -4015,6 +4018,7 @@ class AutoDriveCoordinator {
|
|
|
4015
4018
|
baseline: null,
|
|
4016
4019
|
delivery: null,
|
|
4017
4020
|
lastPromptedRevision: null,
|
|
4021
|
+
handbackPromptedRevision: null,
|
|
4018
4022
|
checkpoint: null,
|
|
4019
4023
|
pendingReply: false,
|
|
4020
4024
|
inFlight: null,
|
|
@@ -4211,8 +4215,12 @@ ${FLOW_MANAGER_KERNEL}`;
|
|
|
4211
4215
|
const mutationAdvanced = advance !== undefined && projection.revision === advance && isMechanical(projection);
|
|
4212
4216
|
if (lease.pendingReply) {
|
|
4213
4217
|
lease.pendingReply = false;
|
|
4214
|
-
if (boundary && (!checkpoint || projection.revision > checkpoint.revision))
|
|
4218
|
+
if (boundary && (!checkpoint || projection.revision > checkpoint.revision)) {
|
|
4219
|
+
await this.#promptHandback(lease, projection);
|
|
4220
|
+
if (this.#lease !== lease)
|
|
4221
|
+
return;
|
|
4215
4222
|
return void this.#waitAt(lease, projection.revision);
|
|
4223
|
+
}
|
|
4216
4224
|
if (!checkpoint || !boundary && !mutationAdvanced)
|
|
4217
4225
|
return void this.deactivate(hostSessionId);
|
|
4218
4226
|
checkpoint.answered = true;
|
|
@@ -4222,15 +4230,27 @@ ${FLOW_MANAGER_KERNEL}`;
|
|
|
4222
4230
|
if (boundary) {
|
|
4223
4231
|
if (checkpoint && projection.revision < checkpoint.revision)
|
|
4224
4232
|
return void this.deactivate(hostSessionId);
|
|
4233
|
+
await this.#promptHandback(lease, projection);
|
|
4234
|
+
if (this.#lease !== lease)
|
|
4235
|
+
return;
|
|
4225
4236
|
return void this.#waitAt(lease, projection.revision);
|
|
4226
4237
|
}
|
|
4238
|
+
if (!isMechanical(projection)) {
|
|
4239
|
+
const already = lease.handbackPromptedRevision === projection.revision;
|
|
4240
|
+
await this.#promptHandback(lease, projection);
|
|
4241
|
+
if (this.#lease !== lease)
|
|
4242
|
+
return;
|
|
4243
|
+
if (!already && lease.handbackPromptedRevision === projection.revision) {
|
|
4244
|
+
this.#setTiming("paused");
|
|
4245
|
+
return;
|
|
4246
|
+
}
|
|
4247
|
+
return void this.deactivate(hostSessionId);
|
|
4248
|
+
}
|
|
4227
4249
|
if (checkpoint) {
|
|
4228
4250
|
if (projection.revision <= checkpoint.revision || !mutationAdvanced)
|
|
4229
4251
|
return void this.deactivate(hostSessionId);
|
|
4230
4252
|
lease.checkpoint = null;
|
|
4231
4253
|
}
|
|
4232
|
-
if (!isMechanical(projection))
|
|
4233
|
-
return void this.deactivate(hostSessionId);
|
|
4234
4254
|
if (lease.lastPromptedRevision === projection.revision) {
|
|
4235
4255
|
this.#setTiming("paused");
|
|
4236
4256
|
return this.#warn(`Flow auto-drive paused after revision ${projection.revision} made no lifecycle progress.`);
|
|
@@ -4498,7 +4518,8 @@ var planFeature = host.object({
|
|
|
4498
4518
|
summary: text,
|
|
4499
4519
|
targets: host.array(text).max(MAX_PLAN_FEATURES).default([]),
|
|
4500
4520
|
validation: host.array(text).max(MAX_PLAN_FEATURES).default([]),
|
|
4501
|
-
dependsOn: host.array(featureId).max(MAX_PLAN_FEATURES).default([])
|
|
4521
|
+
dependsOn: host.array(featureId).max(MAX_PLAN_FEATURES).default([]),
|
|
4522
|
+
kind: host.enum(["change", "inspect"]).optional()
|
|
4502
4523
|
}).strict();
|
|
4503
4524
|
var plan = host.object({
|
|
4504
4525
|
summary: text,
|
|
@@ -4506,11 +4527,11 @@ var plan = host.object({
|
|
|
4506
4527
|
requirements: host.array(text).max(MAX_PLAN_FEATURES).default([]),
|
|
4507
4528
|
decisions: host.array(text).max(MAX_PLAN_FEATURES).default([]),
|
|
4508
4529
|
features: host.array(planFeature).min(1).max(MAX_PLAN_FEATURES),
|
|
4509
|
-
|
|
4510
|
-
externalEvidence: host.array(host.object({
|
|
4530
|
+
evidence: host.array(host.object({
|
|
4511
4531
|
requirement: text,
|
|
4512
4532
|
environment: text,
|
|
4513
4533
|
command: text,
|
|
4534
|
+
scope: host.enum(["gate", "extra"]),
|
|
4514
4535
|
platform: host.enum(EVIDENCE_PLATFORMS).optional(),
|
|
4515
4536
|
assertions: host.array(text).max(MAX_DECLARED_ASSERTIONS).optional()
|
|
4516
4537
|
}).strict()).max(MAX_PLAN_FEATURES).optional()
|
|
@@ -4617,12 +4638,14 @@ function toolError(error) {
|
|
|
4617
4638
|
}
|
|
4618
4639
|
});
|
|
4619
4640
|
}
|
|
4620
|
-
function withAutoContext(response, options) {
|
|
4641
|
+
function withAutoContext(response, options, view) {
|
|
4621
4642
|
let workflowData = response.workflowData;
|
|
4622
4643
|
try {
|
|
4623
|
-
|
|
4624
|
-
|
|
4625
|
-
|
|
4644
|
+
if (view === "detail") {
|
|
4645
|
+
const timing = options.autoTimingSnapshot?.();
|
|
4646
|
+
if (timing)
|
|
4647
|
+
workflowData = { ...workflowData, autoTiming: timing };
|
|
4648
|
+
}
|
|
4626
4649
|
} catch {}
|
|
4627
4650
|
try {
|
|
4628
4651
|
const support = options.autoContinuationSupport?.();
|
|
@@ -4669,7 +4692,7 @@ function createTools(_ctx, options) {
|
|
|
4669
4692
|
flow_status: tool({
|
|
4670
4693
|
description: "Read compact, execution, detail, or reviewer Flow state.",
|
|
4671
4694
|
args: StatusArgs,
|
|
4672
|
-
execute: (args, context) => execute(context, async (workspace) => withAutoContext(await flowStatus(workspace, args), options))
|
|
4695
|
+
execute: (args, context) => execute(context, async (workspace) => withAutoContext(await flowStatus(workspace, args), options, args.request.view))
|
|
4673
4696
|
}),
|
|
4674
4697
|
flow_plan_save: tool({
|
|
4675
4698
|
description: "Create or replace the active draft plan.",
|
|
@@ -5139,4 +5162,4 @@ export {
|
|
|
5139
5162
|
plugin_default as default
|
|
5140
5163
|
};
|
|
5141
5164
|
|
|
5142
|
-
//# debugId=
|
|
5165
|
+
//# debugId=4CB19287BD614D3B64756E2164756E21
|