opencode-plugin-flow 4.3.3 → 4.3.5
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 +37 -0
- package/README.md +13 -5
- package/dist/adapters/opencode/tools.d.ts +14 -0
- package/dist/cli.js +313 -113
- package/dist/cli.js.map +3 -3
- package/dist/index.js +573 -119
- package/dist/index.js.map +7 -7
- package/dist/runtime/api.d.ts +11 -0
- package/dist/runtime/schema.d.ts +119 -0
- package/dist/runtime/transitions.d.ts +43 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -169,7 +169,7 @@ stay separate:
|
|
|
169
169
|
| --- | --- | --- |
|
|
170
170
|
| Repo shape is unclear before planning | Discovery pass | Requirements, decisions, targets, validation entries, or a review-first feature |
|
|
171
171
|
| A broad finding set needs refutation | Audit pass | Surviving findings with guards checked and gaps named |
|
|
172
|
-
| Changed files or risk lenses are too broad for one review pass | Review pass | One
|
|
172
|
+
| Changed files or risk lenses are too broad for one review pass | Review pass | One feature review packet or \`finalReview\` payload owned by the manager |
|
|
173
173
|
| Test strategy or route coverage is unclear | Validation pass | Candidate commands or authorized raw command evidence |
|
|
174
174
|
| A claim is single-source, surprising, high-stakes, or payload-bound | Verification pass | Per-claim keep, narrow, rewrite, or remove decisions |
|
|
175
175
|
| Multiple implementation paths are plausible | Candidate pass | Candidate patches inspected and validated by the manager before use |
|
|
@@ -403,7 +403,7 @@ Where accepted evidence goes:
|
|
|
403
403
|
- Validation evidence may become \`validationRun\` only when the worker was
|
|
404
404
|
explicitly authorized to run the command and reported the exact command,
|
|
405
405
|
status, and raw outcome summary.
|
|
406
|
-
- Review evidence informs \`
|
|
406
|
+
- Review evidence informs \`featureReviewDepth\` plus \`featureReview\`, or \`finalReview\`, but the manager
|
|
407
407
|
owns the pass/fail verdict and must resolve blockers, contradictions, and
|
|
408
408
|
coverage gaps before returning the payload.
|
|
409
409
|
- Audit evidence becomes findings only after refutation; blocking findings need
|
|
@@ -530,10 +530,10 @@ suggestions do not move into the next pass or user-facing answer.
|
|
|
530
530
|
`;
|
|
531
531
|
|
|
532
532
|
// skills/flow/references/recovery-playbook.md
|
|
533
|
-
var recovery_playbook_default = '# Recovery playbook\n\nUse this when a Flow tool returns `status: "error"`, a blocker, or a `nextAction` that conflicts with memory.\n\n## First response\n\n1. Re-anchor with `flow_status`.\n2. Read the returned `summary`, `recovery`, `lastError`, and active feature.\n3. Fix the cause, then retry the smallest valid Flow action.\n\n## Common cases\n\n- `missing_session`: start with `flow_plan_save` using the user\'s goal.\n- `missing_goal`: ask for a concrete goal before planning.\n- `Approved plans cannot be changed`: use `flow_feature_reset` when only affected features need another pass; otherwise close and start a new goal.\n- `No feature is currently running`: call `flow_run_start` before completing.\n- `already in progress`: finish, reset, or block the active feature before starting another.\n- `Completion requires recorded validation evidence`: run real validation and include at least one passing `validationRun`.\n- `Completion requires all recorded validation to pass`: fix failures and rerun. Do not relabel failed checks as passed.\n- `Non-final feature completion requires targeted validation`: use `validationScope: "targeted"` for ordinary features.\n- `Final feature completion requires broad validation`: run the project-level gate and use `validationScope: "broad"`.\n- `Completion requires a passing featureReview`: run or request a real review and include a passing `featureReview` only when there are no blocking findings.\n- `Final feature completion requires a finalReview`: perform final review and include `finalReview`.\n- `Final review depth must match the plan policy`: use `reviewDepth` equal to the approved plan\'s `finalReviewPolicy`; valid final-review values are `broad` and `detailed`.\n- `Cannot close ... unfinished features`: complete, reset, defer, or abandon honestly. Do not mark completed while work remains.\n\n## Reset guidance\n\nUse `flow_feature_reset` when the active or completed work was built on the wrong assumption, validation revealed a design issue, dependencies need to be rerun, or dependent features must be invalidated. Resetting a feature also resets its dependents.\n\n## Closure guidance\n\nUse `flow_session_close`:\n\n- `completed`: only after all planned features are complete.\n- `deferred`: the user intentionally postpones unfinished work.\n- `abandoned`: the session should be archived without claiming delivery.\n\nAfter closure, the active `.flow/session.json` is removed and the archived JSON is stored under `.flow/history/`.\n';
|
|
533
|
+
var recovery_playbook_default = '# Recovery playbook\n\nUse this when a Flow tool returns `status: "error"`, a blocker, or a `nextAction` that conflicts with memory.\n\n## First response\n\n1. Re-anchor with `flow_status`.\n2. Read the returned `summary`, `recovery`, `lastError`, and active feature.\n3. Fix the cause, then retry the smallest valid Flow action.\n\n## Common cases\n\n- `missing_session`: start with `flow_plan_save` using the user\'s goal.\n- `missing_goal`: ask for a concrete goal before planning.\n- `Approved plans cannot be changed`: use `flow_feature_reset` when only affected features need another pass; otherwise close and start a new goal.\n- `No feature is currently running`: call `flow_run_start` before completing.\n- `already in progress`: finish, reset, or block the active feature before starting another.\n- `Completion requires recorded validation evidence`: run real validation and include at least one passing `validationRun`.\n- `Completion requires all recorded validation to pass`: fix failures and rerun. Do not relabel failed checks as passed.\n- `Non-final feature completion requires targeted validation`: use `validationScope: "targeted"` for ordinary features.\n- `Final feature completion requires broad validation`: run the project-level gate and use `validationScope: "broad"`.\n- `Feature review depth ... does not meet the plan requirement`: rerun review\n at the feature\'s planned depth or reset/replan if the depth was chosen\n incorrectly.\n- `Completion requires a passing featureReview`: run or request a real review and include a passing `featureReview` only when there are no blocking findings.\n- `Review retry budget exhausted`: stop and report the remaining blocker. Do\n not keep patching; reset or replan only after explicit user direction.\n- `Final feature completion requires a finalReview`: perform final review and include `finalReview`.\n- `Final review depth must match the plan policy`: use `reviewDepth` equal to the approved plan\'s `finalReviewPolicy`; valid final-review values are `broad` and `detailed`.\n- `Completed ... features since the last Flow checkpoint`: stop the current\n root session and resume from `.flow/session.json` in a fresh OpenCode session.\n- `Cannot close ... unfinished features`: complete, reset, defer, or abandon honestly. Do not mark completed while work remains.\n\n## Reset guidance\n\nUse `flow_feature_reset` when the active or completed work was built on the wrong assumption, validation revealed a design issue, dependencies need to be rerun, or dependent features must be invalidated. Resetting a feature also resets its dependents.\n\n## Closure guidance\n\nUse `flow_session_close`:\n\n- `completed`: only after all planned features are complete.\n- `deferred`: the user intentionally postpones unfinished work.\n- `abandoned`: the session should be archived without claiming delivery.\n\nAfter closure, the active `.flow/session.json` is removed and the archived JSON is stored under `.flow/history/`.\n';
|
|
534
534
|
|
|
535
535
|
// skills/flow/SKILL.md
|
|
536
|
-
var SKILL_default = "---\nname: flow\ndescription: Manage the end-to-end Flow loop for skills-first OpenCode work. Use when a user asks for Flow-guided delivery from goal to completion, resumable autonomous delivery, or resuming or closing a Flow session. For plan-only work use flow-plan; for executing one approved feature use flow-run.\n---\n\n# Flow\n\nUse Flow as a minimal state ledger, not as a framework. Skills provide judgment; the runtime only records the approved plan, active feature, validation evidence, review evidence, and closure.\n\nRouting: this manager skill owns the whole loop and every state-changing `flow_*` call. Load `flow-plan` alone for plan-only requests and `flow-run` alone when an approved plan needs one feature executed. Answer status-only questions with `flow_status`; no skill load is needed. `flow-test`, `flow-deslop`, and `flow-ui-quality` are optional helpers loaded from inside the loop; `flow-commit` is user-triggered only and never part of the autonomous loop.\n\n## Loop\n\n1. Call `flow_status` first. Trust its active session and next action over conversation memory.\n If the result includes `setup.skills`, follow the Skill Availability rules\n below before loading any Flow skill.\n2. If there is no active session and the user gave a goal, load `flow-plan`, save a plan with `flow_plan_save`, then approve it with `flow_plan_approve` only after explicit user approval or prior authorization for autonomous implementation. If there is no goal, ask for one.\n3. Load `flow-run`, call `flow_run_start`, implement exactly one feature, validate it, and prepare a `flow_feature_complete` payload. For validation-heavy, regression-sensitive, browser QA, route QA, or failure-prone work, use `flow-test` to choose and summarize evidence before completion.\n4. Load `flow-review` for the required feature review. The reviewer reports
|
|
536
|
+
var SKILL_default = "---\nname: flow\ndescription: Manage the end-to-end Flow loop for skills-first OpenCode work. Use when a user asks for Flow-guided delivery from goal to completion, resumable autonomous delivery, or resuming or closing a Flow session. For plan-only work use flow-plan; for executing one approved feature use flow-run.\n---\n\n# Flow\n\nUse Flow as a minimal state ledger, not as a framework. Skills provide judgment; the runtime only records the approved plan, active feature, validation evidence, review evidence, and closure.\n\nRouting: this manager skill owns the whole loop and every state-changing `flow_*` call. Load `flow-plan` alone for plan-only requests and `flow-run` alone when an approved plan needs one feature executed. Answer status-only questions with `flow_status`; no skill load is needed. `flow-test`, `flow-deslop`, and `flow-ui-quality` are optional helpers loaded from inside the loop; `flow-commit` is user-triggered only and never part of the autonomous loop.\n\n## Loop\n\n1. Call `flow_status` first. Trust its active session and next action over conversation memory.\n If the result includes `setup.skills`, follow the Skill Availability rules\n below before loading any Flow skill.\n If it includes `session.resumePacket` or `session.budget.phaseBoundary`, stop\n and report the resume instructions unless this is a fresh user invocation\n explicitly resuming the session. Only then may the next `flow_run_start` use\n `phaseBoundaryAck: true`.\n2. If there is no active session and the user gave a goal, load `flow-plan`, save a plan with `flow_plan_save`, then approve it with `flow_plan_approve` only after explicit user approval or prior authorization for autonomous implementation. If there is no goal, ask for one.\n3. Load `flow-run`, call `flow_run_start`, implement exactly one feature, validate it, and prepare a `flow_feature_complete` payload. For validation-heavy, regression-sensitive, browser QA, route QA, or failure-prone work, use `flow-test` to choose and summarize evidence before completion.\n4. Load `flow-review` for the required feature review. Send a compact review\n packet, not the accumulated root transcript. The reviewer reports\n `featureReviewDepth` and `featureReview`; the manager records both inside\n `flow_feature_complete`.\n5. On the final feature, run broad validation and include `finalReview` in the same `flow_feature_complete` call. Its `reviewDepth` must match the plan's `finalReviewPolicy`.\n6. After all features are complete, archive the session with `flow_session_close` using `kind: \"completed\"`.\n\nUse `references/parallel-orchestration.md` for broad read-only discovery, audit, validation, review, verification, or candidate implementation passes. It is the whole pass playbook — orient, slice, manifest, fan out, account, verify, synthesize, extend or stop — and its `references/handoff-format.md` companion holds the worker report templates the manager pastes into prompts. Hidden Flow workers are injected by plugin config; invoke the named worker when it is available. The manager owns every `flow_*` state change.\n\nDo not commit, push, amend, rebase, publish, or mutate releases during the\nautonomous Flow loop. Load `flow-commit` only when the user explicitly asks for\ncommit preparation or commit creation.\n\n## Skill Availability\n\nIf `flow_status` returns `setup.skills`, report that setup status and stop\nnative-loading Flow skills in the current OpenCode startup. Missing, incomplete,\nor outdated managed skills require a sync/restart cycle before their native skill\ninstructions can be trusted by the running process. Public command bundles are\nself-contained and may continue when the command prompt already embeds the\nrequired Flow instructions.\n\nIf optional helper skills such as `flow-test`, `flow-deslop`, or\n`flow-ui-quality` are unavailable, continue only with explicit coverage gaps. Do\nnot copy their rubrics into another skill and do not claim their quality checks\nwere completed.\n\n## Runtime Surface\n\n- `flow_status`: read the active session.\n- `flow_plan_save`: create a session and/or save a draft plan.\n- `flow_plan_approve`: lock the draft plan.\n- `flow_run_start`: start one runnable feature.\n- `flow_feature_complete`: record completion or a real blocker with validation and review evidence.\n- `flow_feature_reset`: reset one feature and its dependents.\n- `flow_session_close`: archive the active session as `completed`, `deferred`, or `abandoned`.\n\nThere is no `flow_context`, no separate review-record tool, and no multi-session activation surface. The single active source of truth is `.flow/session.json`; closed sessions are archived under `.flow/history/`.\n\nPlanning and running require loaded Flow tools; do not simulate plan approval or feature completion when the runtime is unavailable. Review may still return advisory output when tools, skills, or references are stale or unavailable, but the manager must not record it as Flow-gated evidence.\n\n## Hard Gates\n\n- Approved plans are immutable. To change direction, reset affected features or close the session and start a new goal.\n- Only one feature can be active at a time.\n- Each feature's planned `reviewDepth` is the minimum accepted\n `featureReviewDepth` for completion.\n- Completion requires at least one passing `validationRun` entry.\n- Non-final completion requires `validationScope: \"targeted\"`.\n- Final completion requires `validationScope: \"broad\"` and a passing `finalReview`.\n- Every completed feature requires a passing `featureReview` with no blocking findings.\n- Failed reviews pause the loop by default. Autonomous repair may make at most\n one repair plus one retry review before stopping.\n- Phase boundaries stop the current root session; resume from\n `.flow/session.json` in a fresh OpenCode session.\n- `flow_session_close` accepts `kind: \"completed\"` only after an approved plan has passed final completion.\n\n## Budget And Retry Boundaries\n\nFlow can enforce review retry counts and feature-count phase boundaries in the\nruntime ledger. The current OpenCode plugin surface does not expose per-turn\ntoken usage to Flow; when usage is visible in the host UI or logs, stop the\ncurrent autonomous loop once the root session is large enough to threaten\nlatency or cost and report a compact handoff instead of continuing. Treat high\nvisible tokens, high non-cache tokens, or repeated reviewer fan-out as a reason\nto finish the current feature, emit the resume packet, and continue in a fresh\nsession.\n\n## Recovery\n\n- Confused state: call `flow_status` and follow `nextAction`.\n- Wrong assumption or failed implementation path: use `flow_feature_reset` for the feature and dependents, then rerun from the corrected plan.\n- Missing validation or review evidence: gather real evidence, then call `flow_feature_complete`.\n- Approved plan is materially wrong: reset the affected features, save a revised plan if the session is back in planning; otherwise close and start a new goal.\n- Unknown runtime error: read `summary` and `recovery`; see `references/recovery-playbook.md` for common cases.\n\nNever fabricate validation output, backfill review approval you did not perform, or close as `deferred`/`abandoned` merely to avoid an unfinished-work blocker.\n";
|
|
537
537
|
|
|
538
538
|
// skills/flow-commit/SKILL.md
|
|
539
539
|
var SKILL_default2 = `---
|
|
@@ -836,6 +836,60 @@ Apply the manager synthesis barrier from
|
|
|
836
836
|
evidence-backed claims become plan fields.
|
|
837
837
|
`;
|
|
838
838
|
|
|
839
|
+
// skills/flow-plan/references/plan-quality-checklist.md
|
|
840
|
+
var plan_quality_checklist_default = `# Plan quality checklist
|
|
841
|
+
|
|
842
|
+
Use this checklist before \`flow_plan_save\` and again before approval if the plan
|
|
843
|
+
changed during discussion. The goal is not a long planning artifact; it is a
|
|
844
|
+
compact plan another agent can execute without rediscovering the work.
|
|
845
|
+
|
|
846
|
+
## Must pass
|
|
847
|
+
|
|
848
|
+
- Outcome: \`summary\` names the user-visible result, not an internal activity.
|
|
849
|
+
- Requirements: acceptance criteria, constraints, and non-goals that affect
|
|
850
|
+
implementation are captured in \`requirements\`.
|
|
851
|
+
- Decisions: assumptions, scope choices, and architecture choices already made
|
|
852
|
+
are captured in \`decisions\`.
|
|
853
|
+
- Uncertainty: specification uncertainty is resolved by a decision or a user
|
|
854
|
+
question; environment uncertainty is resolved by inspection, discovery, or a
|
|
855
|
+
first evidence-producing feature.
|
|
856
|
+
- Feature shape: each feature has one coherent outcome and can be reviewed on
|
|
857
|
+
its own.
|
|
858
|
+
- Targets: each feature names bounded files, modules, routes, commands, docs, or
|
|
859
|
+
generated surfaces. Whole-repo targets are allowed only for explicit broad
|
|
860
|
+
audits or final validation.
|
|
861
|
+
- Validation: each feature names expected check levels, such as targeted unit,
|
|
862
|
+
integration, browser/e2e, package/build, docs/static, cleanup preservation, or
|
|
863
|
+
broad project gate.
|
|
864
|
+
- Dependencies: \`dependsOn\` captures true ordering and avoids hidden dependency
|
|
865
|
+
chains.
|
|
866
|
+
- Review policy: \`finalReviewPolicy\` is \`detailed\` when the work changes
|
|
867
|
+
behavior, persistence, public contracts, security posture, release surfaces,
|
|
868
|
+
or multiple modules.
|
|
869
|
+
|
|
870
|
+
## Revise when you see this
|
|
871
|
+
|
|
872
|
+
- A feature title describes a step like "update files" instead of a result.
|
|
873
|
+
- A validation entry says only "manual testing" or "run tests".
|
|
874
|
+
- A feature has targets but no behavior or artifact that can be judged.
|
|
875
|
+
- A feature claims cleanup or simplification without an evidence-producing
|
|
876
|
+
audit or cited smell.
|
|
877
|
+
- A docs feature depends on behavior that is not yet implemented but lacks
|
|
878
|
+
\`dependsOn\`.
|
|
879
|
+
- A low-risk \`finalReviewPolicy: "broad"\` is used while the plan crosses runtime,
|
|
880
|
+
schema, persistence, security, or release boundaries.
|
|
881
|
+
|
|
882
|
+
## Approval summary
|
|
883
|
+
|
|
884
|
+
When presenting the plan for approval, include:
|
|
885
|
+
|
|
886
|
+
- The promised outcome.
|
|
887
|
+
- The feature order and any dependencies that matter.
|
|
888
|
+
- The main validation levels.
|
|
889
|
+
- Material assumptions in \`decisions\`.
|
|
890
|
+
- Any known gaps that remain intentional.
|
|
891
|
+
`;
|
|
892
|
+
|
|
839
893
|
// skills/flow-plan/references/planning-examples.md
|
|
840
894
|
var planning_examples_default = `# Planning examples
|
|
841
895
|
|
|
@@ -914,6 +968,164 @@ Better plan:
|
|
|
914
968
|
3. Add behavior-preservation tests for the changed service paths.
|
|
915
969
|
\`\`\`
|
|
916
970
|
|
|
971
|
+
## Bugfix plan
|
|
972
|
+
|
|
973
|
+
Human summary:
|
|
974
|
+
|
|
975
|
+
1. Reproduce and localize the failed password reset redirect.
|
|
976
|
+
2. Fix the redirect state handling and cover the regression.
|
|
977
|
+
3. Update release notes only if user-facing behavior changed.
|
|
978
|
+
|
|
979
|
+
Payload:
|
|
980
|
+
|
|
981
|
+
\`\`\`json
|
|
982
|
+
{
|
|
983
|
+
"goal": "Fix password reset links landing users on the wrong page",
|
|
984
|
+
"plan": {
|
|
985
|
+
"summary": "Password reset links land users on the intended reset confirmation flow.",
|
|
986
|
+
"overview": "Start with a focused reproduction, then fix the redirect state and update user-facing notes only if the behavior change needs documentation.",
|
|
987
|
+
"requirements": [
|
|
988
|
+
"Preserve existing token validation and expiry behavior.",
|
|
989
|
+
"Users with valid reset links should not be sent to the generic sign-in page before completing the reset."
|
|
990
|
+
],
|
|
991
|
+
"decisions": [
|
|
992
|
+
"Treat the current redirect mismatch as a regression until reproduction proves otherwise."
|
|
993
|
+
],
|
|
994
|
+
"finalReviewPolicy": "detailed",
|
|
995
|
+
"features": [
|
|
996
|
+
{
|
|
997
|
+
"id": "reset-redirect-repro",
|
|
998
|
+
"title": "Redirect reproduction",
|
|
999
|
+
"summary": "Produce a failing focused check or trace that identifies where the reset redirect is lost.",
|
|
1000
|
+
"targets": ["src/auth/reset", "tests/auth"],
|
|
1001
|
+
"validation": ["targeted unit or integration reproduction for reset redirect behavior"],
|
|
1002
|
+
"dependsOn": []
|
|
1003
|
+
},
|
|
1004
|
+
{
|
|
1005
|
+
"id": "reset-redirect-fix",
|
|
1006
|
+
"title": "Redirect fix",
|
|
1007
|
+
"summary": "Preserve reset redirect state through token validation and completion.",
|
|
1008
|
+
"targets": ["src/auth/reset", "tests/auth"],
|
|
1009
|
+
"validation": ["targeted regression test passes", "auth package/build check if available"],
|
|
1010
|
+
"dependsOn": ["reset-redirect-repro"]
|
|
1011
|
+
},
|
|
1012
|
+
{
|
|
1013
|
+
"id": "reset-redirect-notes",
|
|
1014
|
+
"title": "User-facing notes",
|
|
1015
|
+
"summary": "Document the corrected reset-link behavior if release notes or help text mention the flow.",
|
|
1016
|
+
"targets": ["CHANGELOG.md", "docs/auth.md"],
|
|
1017
|
+
"validation": ["docs/static check if available", "review docs against implemented behavior"],
|
|
1018
|
+
"dependsOn": ["reset-redirect-fix"]
|
|
1019
|
+
}
|
|
1020
|
+
]
|
|
1021
|
+
}
|
|
1022
|
+
}
|
|
1023
|
+
\`\`\`
|
|
1024
|
+
|
|
1025
|
+
## UI/frontend plan
|
|
1026
|
+
|
|
1027
|
+
Human summary:
|
|
1028
|
+
|
|
1029
|
+
1. Map the current checkout empty state and responsive constraints.
|
|
1030
|
+
2. Implement the empty state with accessible controls and mobile layout.
|
|
1031
|
+
3. Verify the visual states with screenshots or browser evidence.
|
|
1032
|
+
|
|
1033
|
+
Good feature outline:
|
|
1034
|
+
|
|
1035
|
+
\`\`\`text
|
|
1036
|
+
1. Empty-state discovery - inspect the route, component boundaries, design tokens, existing empty states, and likely responsive breakpoints.
|
|
1037
|
+
2. Empty-state implementation - add the checkout empty state, action wiring, focus order, and loading/error boundaries in the existing component style.
|
|
1038
|
+
3. Visual and interaction verification - capture desktop and mobile evidence, run available route/component checks, and fix overlap or accessibility regressions.
|
|
1039
|
+
\`\`\`
|
|
1040
|
+
|
|
1041
|
+
Why this is better than one "build UI" feature: the plan names the uncertain
|
|
1042
|
+
surface first, keeps implementation scoped to the route/components, and makes
|
|
1043
|
+
visual evidence part of completion rather than an afterthought.
|
|
1044
|
+
|
|
1045
|
+
## Runtime or schema plan
|
|
1046
|
+
|
|
1047
|
+
Human summary:
|
|
1048
|
+
|
|
1049
|
+
1. Introduce the schema change behind a backward-compatible parser.
|
|
1050
|
+
2. Migrate callers and persistence writes.
|
|
1051
|
+
3. Add compatibility validation and docs.
|
|
1052
|
+
|
|
1053
|
+
Good feature outline:
|
|
1054
|
+
|
|
1055
|
+
\`\`\`text
|
|
1056
|
+
1. Compatible schema reader - accept old and new session payloads, with targeted parser tests for both.
|
|
1057
|
+
2. New writer path - emit the new field from runtime transitions and update affected callers.
|
|
1058
|
+
3. Compatibility sweep - run persistence/workspace tests, update docs, and verify old sessions still recover.
|
|
1059
|
+
\`\`\`
|
|
1060
|
+
|
|
1061
|
+
Use \`finalReviewPolicy: "detailed"\` for this shape. Persistence and schema work
|
|
1062
|
+
usually has hidden downstream contracts, so feature validation should name both
|
|
1063
|
+
targeted parser checks and broader workspace/runtime gates.
|
|
1064
|
+
|
|
1065
|
+
## Docs-only plan
|
|
1066
|
+
|
|
1067
|
+
Docs-only work can use \`finalReviewPolicy: "broad"\` when it does not change
|
|
1068
|
+
commands, configuration, generated files, or release metadata.
|
|
1069
|
+
|
|
1070
|
+
Good feature outline:
|
|
1071
|
+
|
|
1072
|
+
\`\`\`text
|
|
1073
|
+
1. Align installation docs - update README and troubleshooting steps for the current setup flow.
|
|
1074
|
+
2. Verify commands and links - check documented commands against package scripts and make sure links/paths resolve.
|
|
1075
|
+
\`\`\`
|
|
1076
|
+
|
|
1077
|
+
Bad validation:
|
|
1078
|
+
|
|
1079
|
+
\`\`\`text
|
|
1080
|
+
validation: ["manual review"]
|
|
1081
|
+
\`\`\`
|
|
1082
|
+
|
|
1083
|
+
Better validation:
|
|
1084
|
+
|
|
1085
|
+
\`\`\`text
|
|
1086
|
+
validation: ["docs/static link and path review", "command examples checked against package scripts"]
|
|
1087
|
+
\`\`\`
|
|
1088
|
+
|
|
1089
|
+
## Audit-first and review-first plans
|
|
1090
|
+
|
|
1091
|
+
Use an evidence-producing first feature when the request asks to "review",
|
|
1092
|
+
"audit", "clean up", "modernize", or "improve" a broad area.
|
|
1093
|
+
|
|
1094
|
+
Good feature outline:
|
|
1095
|
+
|
|
1096
|
+
\`\`\`text
|
|
1097
|
+
1. Audit checkout state management - cite concrete findings with file:line evidence, refutation checks, severity, and recommended fix order.
|
|
1098
|
+
2. Fix confirmed high-impact state leak - only for findings that survived the audit.
|
|
1099
|
+
3. Regression validation - add or run checks covering the changed state paths.
|
|
1100
|
+
\`\`\`
|
|
1101
|
+
|
|
1102
|
+
Do not plan fixes for guessed findings. If the audit might find no actionable
|
|
1103
|
+
issue, say that in the first feature summary and make later features conditional
|
|
1104
|
+
on evidence.
|
|
1105
|
+
|
|
1106
|
+
## Validation examples
|
|
1107
|
+
|
|
1108
|
+
Weak:
|
|
1109
|
+
|
|
1110
|
+
\`\`\`text
|
|
1111
|
+
validation: ["run tests", "manual testing"]
|
|
1112
|
+
\`\`\`
|
|
1113
|
+
|
|
1114
|
+
Stronger:
|
|
1115
|
+
|
|
1116
|
+
\`\`\`text
|
|
1117
|
+
validation: [
|
|
1118
|
+
"targeted unit tests for empty and invalid input",
|
|
1119
|
+
"integration test for persisted session recovery",
|
|
1120
|
+
"package/build gate for changed TypeScript exports",
|
|
1121
|
+
"browser screenshot at desktop and mobile widths for layout-sensitive UI",
|
|
1122
|
+
"docs/static review for changed command examples"
|
|
1123
|
+
]
|
|
1124
|
+
\`\`\`
|
|
1125
|
+
|
|
1126
|
+
The stronger version says what level of evidence is expected and which behavior
|
|
1127
|
+
or surface it covers.
|
|
1128
|
+
|
|
917
1129
|
## Decomposition anti-patterns
|
|
918
1130
|
|
|
919
1131
|
- Feature per file when behavior crosses files.
|
|
@@ -986,6 +1198,7 @@ Call \`flow_plan_save\` with:
|
|
|
986
1198
|
"id": "lowercase-kebab-case",
|
|
987
1199
|
"title": "Short title",
|
|
988
1200
|
"summary": "Outcome this feature delivers",
|
|
1201
|
+
"reviewDepth": "standard",
|
|
989
1202
|
"targets": ["files, modules, routes, commands, or docs in scope"],
|
|
990
1203
|
"validation": ["focused checks expected before completion"],
|
|
991
1204
|
"dependsOn": []
|
|
@@ -997,6 +1210,23 @@ Call \`flow_plan_save\` with:
|
|
|
997
1210
|
|
|
998
1211
|
Use only \`finalReviewPolicy: "broad"\` or \`"detailed"\`. These are the canonical final-review policy and \`reviewDepth\` enum values. Use \`"broad"\` only for low-risk, narrow work. Use \`"detailed"\` for behavioral changes, cross-module edits, migrations, releases, security-sensitive code, or large refactors.
|
|
999
1212
|
|
|
1213
|
+
Set each feature's \`reviewDepth\` to one of:
|
|
1214
|
+
|
|
1215
|
+
- \`quick\`: docs, comments, config-only changes, generated output, or mechanical changes fully covered by tooling.
|
|
1216
|
+
- \`standard\`: the default for ordinary implementation slices. The review reads every changed file and relevant tests.
|
|
1217
|
+
- \`detailed\`: persistence, migrations, concurrency, security, cross-module behavior, release/package surfaces, large refactors, weak validation, or any work where a missed edge case would be expensive.
|
|
1218
|
+
|
|
1219
|
+
Do not make reviews shallower to save tokens. Reduce token use by splitting features, keeping \`targets\` precise, and using scoped review packets during execution.
|
|
1220
|
+
|
|
1221
|
+
## Plan quality gate
|
|
1222
|
+
|
|
1223
|
+
Before saving or asking for approval, load
|
|
1224
|
+
\`references/plan-quality-checklist.md\` and check the draft against it. Revise the
|
|
1225
|
+
plan until it passes, or record the remaining gap in \`decisions\` when the gap is
|
|
1226
|
+
an intentional assumption. Do not approve a plan whose outcome, requirements,
|
|
1227
|
+
targets, validation, or dependency order are still too vague for another agent
|
|
1228
|
+
to execute.
|
|
1229
|
+
|
|
1000
1230
|
## Feature sizing
|
|
1001
1231
|
|
|
1002
1232
|
- Each feature should have one owner, one coherent outcome, and a validation story.
|
|
@@ -1007,12 +1237,17 @@ Use only \`finalReviewPolicy: "broad"\` or \`"detailed"\`. These are the canonic
|
|
|
1007
1237
|
validation entry should name the expected test level, such as targeted unit,
|
|
1008
1238
|
integration, browser/e2e, package/build, docs/static, cleanup preservation, or
|
|
1009
1239
|
broad project gate.
|
|
1240
|
+
- Assign \`reviewDepth\` from risk. Use \`detailed\` for persistence, migration,
|
|
1241
|
+
concurrency, security, final-delivery-adjacent, or cross-module slices; use
|
|
1242
|
+
\`standard\` for normal code changes; reserve \`quick\` for low-risk non-behavioral
|
|
1243
|
+
work.
|
|
1010
1244
|
|
|
1011
1245
|
## Approval
|
|
1012
1246
|
|
|
1013
1247
|
After saving, summarize the plan to the user. Call \`flow_plan_approve\` only after explicit user approval, unless the user already authorized autonomous implementation. Approved plans are immutable; changing them later requires reset/closure rather than silent edits.
|
|
1014
1248
|
|
|
1015
|
-
See \`references/planning-examples.md\` for payload examples and decomposition
|
|
1249
|
+
See \`references/planning-examples.md\` for payload examples and decomposition
|
|
1250
|
+
anti-patterns.
|
|
1016
1251
|
`;
|
|
1017
1252
|
|
|
1018
1253
|
// skills/flow-review/references/review-rubric.md
|
|
@@ -1048,22 +1283,54 @@ If unsure whether a finding is real, read more or downgrade it. Do not promote g
|
|
|
1048
1283
|
|
|
1049
1284
|
## Final review checklist
|
|
1050
1285
|
|
|
1051
|
-
-
|
|
1052
|
-
-
|
|
1286
|
+
- The original goal is satisfied by the delivered behavior or artifacts.
|
|
1287
|
+
- Every approved requirement is either met or explicitly accounted for by an
|
|
1288
|
+
accepted gap.
|
|
1289
|
+
- Plan decisions and scope boundaries still match the implementation.
|
|
1290
|
+
- Every planned feature is complete, has recorded validation evidence, and
|
|
1291
|
+
contributes to the final outcome.
|
|
1292
|
+
- Feature dependencies were completed in an order that makes the evidence
|
|
1293
|
+
trustworthy.
|
|
1294
|
+
- Changed artifacts match the plan's \`targets\`; extra changed surfaces are
|
|
1295
|
+
explained and reviewed.
|
|
1296
|
+
- Broad validation ran and passed, or any skipped broad check is justified as a
|
|
1297
|
+
non-blocking gap.
|
|
1053
1298
|
- The final \`reviewDepth\` equals the approved \`finalReviewPolicy\`; the only final-review enum values are \`broad\` and \`detailed\`.
|
|
1054
1299
|
- Feature-level reviews have no unresolved blocking findings.
|
|
1055
1300
|
- Docs, commands, package metadata, and release surfaces match the delivered behavior.
|
|
1056
1301
|
- Remaining gaps are explicit and do not contradict \`kind: "completed"\`.
|
|
1057
1302
|
|
|
1303
|
+
## Final convergence scan
|
|
1304
|
+
|
|
1305
|
+
Run this scan before returning a passing \`finalReview\`:
|
|
1306
|
+
|
|
1307
|
+
1. Restate the original goal and the approved plan summary in your own words.
|
|
1308
|
+
2. Map each requirement to delivered evidence, validation output, or an explicit
|
|
1309
|
+
accepted gap.
|
|
1310
|
+
3. Walk every planned feature and confirm its completion evidence, review
|
|
1311
|
+
result, and validation level.
|
|
1312
|
+
4. Compare the changed files, docs, commands, generated surfaces, and package
|
|
1313
|
+
metadata to the planned targets and requirements.
|
|
1314
|
+
5. Check whether the validation evidence would have caught the main failure
|
|
1315
|
+
modes introduced by the work.
|
|
1316
|
+
6. Decide whether remaining gaps are advisory or blocking before setting
|
|
1317
|
+
\`status\`.
|
|
1318
|
+
|
|
1319
|
+
Fail the final review when the delivered work cannot be traced back to the
|
|
1320
|
+
approved goal and requirements, even if each individual feature review passed.
|
|
1321
|
+
|
|
1058
1322
|
## Payloads
|
|
1059
1323
|
|
|
1060
1324
|
Feature review:
|
|
1061
1325
|
|
|
1062
1326
|
\`\`\`json
|
|
1063
1327
|
{
|
|
1064
|
-
"
|
|
1065
|
-
"
|
|
1066
|
-
|
|
1328
|
+
"featureReviewDepth": "standard",
|
|
1329
|
+
"featureReview": {
|
|
1330
|
+
"status": "passed",
|
|
1331
|
+
"summary": "Reviewed changed runtime files and focused tests; validation covers the new gate.",
|
|
1332
|
+
"blockingFindings": []
|
|
1333
|
+
}
|
|
1067
1334
|
}
|
|
1068
1335
|
\`\`\`
|
|
1069
1336
|
|
|
@@ -1071,14 +1338,17 @@ Failed feature review:
|
|
|
1071
1338
|
|
|
1072
1339
|
\`\`\`json
|
|
1073
1340
|
{
|
|
1074
|
-
"
|
|
1075
|
-
"
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1341
|
+
"featureReviewDepth": "detailed",
|
|
1342
|
+
"featureReview": {
|
|
1343
|
+
"status": "failed",
|
|
1344
|
+
"summary": "Validation does not exercise the changed persistence path.",
|
|
1345
|
+
"blockingFindings": [
|
|
1346
|
+
{
|
|
1347
|
+
"summary": "No test covers archive removal of .flow/session.json after close.",
|
|
1348
|
+
"severity": "blocking"
|
|
1349
|
+
}
|
|
1350
|
+
]
|
|
1351
|
+
}
|
|
1082
1352
|
}
|
|
1083
1353
|
\`\`\`
|
|
1084
1354
|
|
|
@@ -1138,7 +1408,15 @@ These instructions run in two contexts, and only one of them can load helpers:
|
|
|
1138
1408
|
|
|
1139
1409
|
- Call \`flow_status\` when available.
|
|
1140
1410
|
- Identify whether this is a feature review or final review.
|
|
1411
|
+
- Prefer the manager's compact review packet over parent-session memory. The
|
|
1412
|
+
packet should name the active feature, minimum \`reviewDepth\`, changed files,
|
|
1413
|
+
diff summary, validation evidence, and targeted paths or risk lenses. If the
|
|
1414
|
+
packet is missing important scope or evidence, record that as a coverage gap
|
|
1415
|
+
or blocker instead of searching the full conversation transcript.
|
|
1141
1416
|
- Read the approved plan fields relevant to the work: \`requirements\`, \`decisions\`, feature \`targets\`, feature \`validation\`, and dependencies.
|
|
1417
|
+
- For final review, also compare the original goal, full feature list, completed
|
|
1418
|
+
feature evidence, changed artifacts, and final validation against the
|
|
1419
|
+
convergence checklist in \`references/review-rubric.md\`.
|
|
1142
1420
|
- Inspect the actual diff, changed files, tests, and validation output. Do not review only the completion summary.
|
|
1143
1421
|
- In manager context, load \`flow-test\` for validation-heavy,
|
|
1144
1422
|
regression-sensitive, browser QA, or unclear coverage reviews. If it is
|
|
@@ -1156,16 +1434,23 @@ These instructions run in two contexts, and only one of them can load helpers:
|
|
|
1156
1434
|
|
|
1157
1435
|
## Output
|
|
1158
1436
|
|
|
1159
|
-
For a feature review, return
|
|
1437
|
+
For a feature review, return a packet the manager can copy into
|
|
1438
|
+
\`flow_feature_complete\`:
|
|
1160
1439
|
|
|
1161
1440
|
\`\`\`json
|
|
1162
1441
|
{
|
|
1163
|
-
"
|
|
1164
|
-
"
|
|
1165
|
-
|
|
1442
|
+
"featureReviewDepth": "standard",
|
|
1443
|
+
"featureReview": {
|
|
1444
|
+
"status": "passed",
|
|
1445
|
+
"summary": "what was reviewed and why it is acceptable",
|
|
1446
|
+
"blockingFindings": []
|
|
1447
|
+
}
|
|
1166
1448
|
}
|
|
1167
1449
|
\`\`\`
|
|
1168
1450
|
|
|
1451
|
+
\`featureReviewDepth\` must be at least the feature's planned \`reviewDepth\`.
|
|
1452
|
+
Use the actual depth performed: \`quick\`, \`standard\`, or \`detailed\`.
|
|
1453
|
+
|
|
1169
1454
|
For a final review, return:
|
|
1170
1455
|
|
|
1171
1456
|
\`\`\`json
|
|
@@ -1317,96 +1602,7 @@ Never trim failing output, relabel a failed command as passed, or use "not run"
|
|
|
1317
1602
|
`;
|
|
1318
1603
|
|
|
1319
1604
|
// skills/flow-run/SKILL.md
|
|
1320
|
-
var SKILL_default6 =
|
|
1321
|
-
name: flow-run
|
|
1322
|
-
description: "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."
|
|
1323
|
-
---
|
|
1324
|
-
|
|
1325
|
-
# Flow Run
|
|
1326
|
-
|
|
1327
|
-
Use this skill for implementation after a Flow plan is approved. Work one feature at a time.
|
|
1328
|
-
|
|
1329
|
-
If \`flow_run_start\` is unavailable, stop and tell the user to check that \`opencode-plugin-flow\` is loaded in OpenCode.
|
|
1330
|
-
|
|
1331
|
-
## Start
|
|
1332
|
-
|
|
1333
|
-
- Call \`flow_status\`.
|
|
1334
|
-
- Call \`flow_run_start\` with no \`featureId\` unless the user or plan requires a specific runnable feature.
|
|
1335
|
-
- Treat the returned feature as the sole scope until it is completed, blocked, or reset.
|
|
1336
|
-
- Helper rule: when a named helper skill is unavailable, record the gap and
|
|
1337
|
-
keep the corresponding claims conservative instead of simulating its checks.
|
|
1338
|
-
- Load \`flow-deslop\` for cleanup/refactor features.
|
|
1339
|
-
- Load \`flow-ui-quality\` for frontend, UX, responsive, accessibility, or visual work.
|
|
1340
|
-
|
|
1341
|
-
## Implement
|
|
1342
|
-
|
|
1343
|
-
- Read the feature \`targets\`, \`summary\`, \`validation\`, dependencies, and plan \`requirements\`/\`decisions\`.
|
|
1344
|
-
- Keep edits scoped to the active feature. If new scope appears, stop and replan or defer it to another feature.
|
|
1345
|
-
- Preserve unrelated user changes in the worktree.
|
|
1346
|
-
- When a wrong assumption invalidates the feature, use \`flow_feature_reset\`; do not pile patches onto a bad path.
|
|
1347
|
-
- Do not stage, commit, push, amend, rebase, publish, or mutate releases as part
|
|
1348
|
-
of feature execution. If the user explicitly asks for commit preparation, load
|
|
1349
|
-
\`flow-commit\` only after \`flow_feature_complete\` has been recorded, unless the
|
|
1350
|
-
user explicitly asks for a WIP commit path. Keep Git boundaries separate from
|
|
1351
|
-
Flow state recording.
|
|
1352
|
-
|
|
1353
|
-
## Validate
|
|
1354
|
-
|
|
1355
|
-
- For complex validation, regression-sensitive changes, browser QA, route QA,
|
|
1356
|
-
failure-prone checks, unclear coverage, exploratory QA, or
|
|
1357
|
-
\`validationRun\` summarization, load \`flow-test\` (helper rule applies).
|
|
1358
|
-
- Read \`references/validation-rubric.md\` before completing.
|
|
1359
|
-
- Run the strongest practical checks for the changed behavior.
|
|
1360
|
-
- Record concrete command names, status, and observed results. "Tests pass" is not evidence.
|
|
1361
|
-
- Non-final features complete with \`validationScope: "targeted"\`.
|
|
1362
|
-
- The final feature must run a broad project-level gate and use \`validationScope: "broad"\`.
|
|
1363
|
-
|
|
1364
|
-
For broad validation research, risky changes, or unclear coverage, use
|
|
1365
|
-
\`../flow/references/parallel-orchestration.md\` to fan out named Flow workers.
|
|
1366
|
-
Use the mode-to-agent mapping in that reference instead of generic subagents.
|
|
1367
|
-
Write its pass manifest before fan-out, paste the matching handoff template
|
|
1368
|
-
from \`../flow/references/handoff-format.md\` into every worker prompt, and
|
|
1369
|
-
apply its verification tiers to the handoffs that come back.
|
|
1370
|
-
They may report command output they actually ran or propose focused checks; the
|
|
1371
|
-
manager decides what is strong enough to record.
|
|
1372
|
-
|
|
1373
|
-
For independent implementation attempts, use candidate workers only with
|
|
1374
|
-
explicit user authorization plus isolated worktrees or exact non-overlapping
|
|
1375
|
-
path ownership. Treat their output as candidate patches. The manager inspects,
|
|
1376
|
-
merges, validates, and records Flow state serially.
|
|
1377
|
-
|
|
1378
|
-
## Review and complete
|
|
1379
|
-
|
|
1380
|
-
Before \`flow_feature_complete\`, obtain a \`featureReview\` payload. Load
|
|
1381
|
-
\`flow-review\`; for read-only subagent reviews, the manager receives the payload
|
|
1382
|
-
and records it.
|
|
1383
|
-
|
|
1384
|
-
If \`flow_status\` reports \`setup.skills\` or \`flow-review\` cannot be loaded, do
|
|
1385
|
-
not record a Flow-gated \`featureReview\` or \`finalReview\`. You may perform an
|
|
1386
|
-
advisory review using available context or the bundled review fallback provided
|
|
1387
|
-
by plugin config, then complete with \`status: "needs_input"\` if review evidence
|
|
1388
|
-
is required to proceed.
|
|
1389
|
-
|
|
1390
|
-
For the final feature, also obtain a \`finalReview\` payload whose \`reviewDepth\` equals the approved plan's \`finalReviewPolicy\`.
|
|
1391
|
-
|
|
1392
|
-
Complete with:
|
|
1393
|
-
|
|
1394
|
-
\`\`\`json
|
|
1395
|
-
{
|
|
1396
|
-
"status": "ok",
|
|
1397
|
-
"featureId": "active-feature-id",
|
|
1398
|
-
"summary": "what changed",
|
|
1399
|
-
"artifactsChanged": [{ "path": "src/file.ts" }],
|
|
1400
|
-
"validationRun": [
|
|
1401
|
-
{ "command": "bun test tests/foo.test.ts", "status": "passed", "summary": "3 pass, exercised foo behavior" }
|
|
1402
|
-
],
|
|
1403
|
-
"validationScope": "targeted",
|
|
1404
|
-
"featureReview": { "status": "passed", "summary": "review summary", "blockingFindings": [] }
|
|
1405
|
-
}
|
|
1406
|
-
\`\`\`
|
|
1407
|
-
|
|
1408
|
-
If genuinely blocked, call \`flow_feature_complete\` with \`status: "needs_input"\` and an \`outcome\` that explains the blocker and next step. Never fabricate validation or review evidence to force progress.
|
|
1409
|
-
`;
|
|
1605
|
+
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';
|
|
1410
1606
|
|
|
1411
1607
|
// skills/flow-test/SKILL.md
|
|
1412
1608
|
var SKILL_default7 = `---
|
|
@@ -1710,6 +1906,10 @@ var FLOW_SKILL_DEFINITIONS = [
|
|
|
1710
1906
|
relativePath: "references/planning-examples.md",
|
|
1711
1907
|
content: planning_examples_default
|
|
1712
1908
|
},
|
|
1909
|
+
{
|
|
1910
|
+
relativePath: "references/plan-quality-checklist.md",
|
|
1911
|
+
content: plan_quality_checklist_default
|
|
1912
|
+
},
|
|
1713
1913
|
{
|
|
1714
1914
|
relativePath: "references/parallel-discovery.md",
|
|
1715
1915
|
content: parallel_discovery_default
|
|
@@ -1802,6 +2002,7 @@ var FLOW_REVIEW_BUNDLED_INSTRUCTIONS = bundledFlowInstructions([
|
|
|
1802
2002
|
var FLOW_PLAN_BUNDLED_INSTRUCTIONS = bundledFlowInstructions([
|
|
1803
2003
|
["flow-plan", "SKILL.md"],
|
|
1804
2004
|
["flow-plan", "references/planning-examples.md"],
|
|
2005
|
+
["flow-plan", "references/plan-quality-checklist.md"],
|
|
1805
2006
|
["flow-plan", "references/parallel-discovery.md"],
|
|
1806
2007
|
["flow", "references/parallel-orchestration.md"],
|
|
1807
2008
|
["flow", "references/handoff-format.md"]
|
|
@@ -1822,6 +2023,7 @@ var FLOW_AUTO_BUNDLED_INSTRUCTIONS = bundledFlowInstructions([
|
|
|
1822
2023
|
["flow", "references/handoff-format.md"],
|
|
1823
2024
|
["flow-plan", "SKILL.md"],
|
|
1824
2025
|
["flow-plan", "references/planning-examples.md"],
|
|
2026
|
+
["flow-plan", "references/plan-quality-checklist.md"],
|
|
1825
2027
|
["flow-plan", "references/parallel-discovery.md"],
|
|
1826
2028
|
["flow-run", "SKILL.md"],
|
|
1827
2029
|
["flow-run", "references/validation-rubric.md"],
|
|
@@ -1831,6 +2033,7 @@ var FLOW_AUTO_BUNDLED_INSTRUCTIONS = bundledFlowInstructions([
|
|
|
1831
2033
|
]);
|
|
1832
2034
|
var FLOW_SELF_CONTAINED_COMMAND_PREFLIGHT = [
|
|
1833
2035
|
"Call `flow_status` first. If the result includes `setup.skills`, report the setup status and continue with the bundled public Flow command instructions below.",
|
|
2036
|
+
"If `flow_status` includes `session.resumePacket` or `session.budget.phaseBoundary`, stop the current autonomous loop and report the resume instructions unless this is a fresh user invocation explicitly resuming the session; only then may `flow_run_start` use `phaseBoundaryAck: true`.",
|
|
1834
2037
|
"After `flow_status`, briefly state which bundled Flow command is running and for what goal, then continue.",
|
|
1835
2038
|
"Do not call native Flow skills for `flow`, `flow-plan`, `flow-run`, or `flow-review` from public Flow commands. In bundled sections, `load` means read and use the corresponding bundled section in this command, and missing native public Flow skills are not blockers.",
|
|
1836
2039
|
"Optional helper skills (`flow-test`, `flow-deslop`, `flow-ui-quality`, and user-triggered `flow-commit`) are not bundled fallbacks. If one is unavailable, record the coverage gap exactly as the bundled instructions require."
|
|
@@ -1851,6 +2054,7 @@ var FLOW_RUN_COMMAND_TEMPLATE = flowBundledCommandTemplate("Flow run", "Execute
|
|
|
1851
2054
|
var FLOW_REVIEW_COMMAND_TEMPLATE = flowBundledCommandTemplate("Flow review", "Review: $ARGUMENTS", FLOW_REVIEW_BUNDLED_INSTRUCTIONS);
|
|
1852
2055
|
var FLOW_REVIEW_AGENT_INSTRUCTIONS = [
|
|
1853
2056
|
"Use Flow review mode. Call `flow_status` first. Do not call the native skill tool for `flow-review`; the canonical Flow review instructions and rubric are already embedded below. If Flow setup reports stale/unavailable skills, continue as advisory review only and do not present advisory review as Flow-gated `featureReview` or `finalReview` evidence.",
|
|
2057
|
+
"Prefer the manager's compact review packet over the accumulated root transcript. Return feature review packets with `featureReviewDepth` plus `featureReview`; final reviews still return `finalReview` with `reviewDepth`.",
|
|
1854
2058
|
"When the manager assigns a parallel review slice instead of a direct Flow review command, cite or drop every claim, label single-source, inferred, and unsettled claims, and return only the assigned Flow handoff. Report blocked if the assigned scope, expected coverage, or handoff shape is missing.",
|
|
1855
2059
|
"",
|
|
1856
2060
|
"## Bundled Flow review instructions",
|
|
@@ -2397,6 +2601,11 @@ var SessionStatusSchema = z.enum([
|
|
|
2397
2601
|
var ReviewStatusSchema = z.enum(["passed", "failed"]);
|
|
2398
2602
|
var ValidationStatusSchema = z.enum(["passed", "failed"]);
|
|
2399
2603
|
var ValidationScopeSchema = z.enum(["targeted", "broad"]);
|
|
2604
|
+
var FeatureReviewDepthSchema = z.enum([
|
|
2605
|
+
"quick",
|
|
2606
|
+
"standard",
|
|
2607
|
+
"detailed"
|
|
2608
|
+
]);
|
|
2400
2609
|
var FinalReviewPolicySchema = z.enum(["broad", "detailed"]);
|
|
2401
2610
|
var ReviewFindingSchema = z.object({
|
|
2402
2611
|
summary: z.string().min(1),
|
|
@@ -2423,6 +2632,7 @@ var FeatureSchema = z.object({
|
|
|
2423
2632
|
title: z.string().min(1),
|
|
2424
2633
|
summary: z.string().min(1),
|
|
2425
2634
|
status: FeatureStatusSchema.default("pending"),
|
|
2635
|
+
reviewDepth: FeatureReviewDepthSchema.default("standard"),
|
|
2426
2636
|
targets: z.array(z.string().min(1)).default([]),
|
|
2427
2637
|
validation: z.array(z.string().min(1)).default([]),
|
|
2428
2638
|
dependsOn: z.array(z.string().regex(FEATURE_ID_PATTERN)).default([])
|
|
@@ -2439,6 +2649,7 @@ var PlanInputSchema = PlanSchema.omit({ features: true }).extend({
|
|
|
2439
2649
|
finalReviewPolicy: FinalReviewPolicySchema.optional(),
|
|
2440
2650
|
features: z.array(FeatureSchema.omit({ status: true }).extend({
|
|
2441
2651
|
status: FeatureStatusSchema.optional(),
|
|
2652
|
+
reviewDepth: FeatureReviewDepthSchema.optional(),
|
|
2442
2653
|
targets: z.array(z.string().min(1)).optional(),
|
|
2443
2654
|
validation: z.array(z.string().min(1)).optional(),
|
|
2444
2655
|
dependsOn: z.array(z.string().regex(FEATURE_ID_PATTERN)).optional()
|
|
@@ -2462,6 +2673,7 @@ var WorkerResultSchema = z.discriminatedUnion("status", [
|
|
|
2462
2673
|
artifactsChanged: z.array(ArtifactSchema).default([]),
|
|
2463
2674
|
validationRun: z.array(ValidationRunSchema).default([]),
|
|
2464
2675
|
validationScope: ValidationScopeSchema,
|
|
2676
|
+
featureReviewDepth: FeatureReviewDepthSchema,
|
|
2465
2677
|
featureReview: ReviewSchema,
|
|
2466
2678
|
finalReview: FinalReviewSchema.optional(),
|
|
2467
2679
|
outcome: WorkerOutcomeSchema.optional()
|
|
@@ -2473,6 +2685,7 @@ var WorkerResultSchema = z.discriminatedUnion("status", [
|
|
|
2473
2685
|
artifactsChanged: z.array(ArtifactSchema).default([]),
|
|
2474
2686
|
validationRun: z.array(ValidationRunSchema).default([]),
|
|
2475
2687
|
validationScope: ValidationScopeSchema.optional(),
|
|
2688
|
+
featureReviewDepth: FeatureReviewDepthSchema.optional(),
|
|
2476
2689
|
featureReview: ReviewSchema.optional(),
|
|
2477
2690
|
finalReview: FinalReviewSchema.optional(),
|
|
2478
2691
|
outcome: NeedsInputOutcomeSchema
|
|
@@ -2494,10 +2707,37 @@ var ExecutionHistoryEntrySchema = z.object({
|
|
|
2494
2707
|
artifactsChanged: z.array(ArtifactSchema).default([]),
|
|
2495
2708
|
validationRun: z.array(ValidationRunSchema).default([]),
|
|
2496
2709
|
validationScope: ValidationScopeSchema.optional(),
|
|
2710
|
+
featureReviewDepth: FeatureReviewDepthSchema.optional(),
|
|
2497
2711
|
featureReview: ReviewSchema.optional(),
|
|
2498
2712
|
finalReview: FinalReviewSchema.optional(),
|
|
2499
2713
|
outcome: WorkerOutcomeSchema.optional()
|
|
2500
2714
|
}).strict();
|
|
2715
|
+
var TokenTelemetrySchema = z.object({
|
|
2716
|
+
source: z.enum(["host_unavailable", "reported"]).default("host_unavailable"),
|
|
2717
|
+
visibleTokens: z.number().int().nonnegative().nullable().default(null),
|
|
2718
|
+
cacheReadTokens: z.number().int().nonnegative().nullable().default(null),
|
|
2719
|
+
nonCacheTokens: z.number().int().nonnegative().nullable().default(null)
|
|
2720
|
+
}).strict();
|
|
2721
|
+
var PhaseBoundarySchema = z.object({
|
|
2722
|
+
reason: z.enum(["feature_limit", "token_limit", "review_failure_limit"]),
|
|
2723
|
+
summary: z.string().min(1),
|
|
2724
|
+
resumeInstructions: z.string().min(1),
|
|
2725
|
+
recordedAt: z.string().min(1)
|
|
2726
|
+
}).strict();
|
|
2727
|
+
var BudgetTelemetrySchema = z.object({
|
|
2728
|
+
phaseStartedAt: z.string().min(1).default("unknown"),
|
|
2729
|
+
completedFeaturesSinceBoundary: z.number().int().nonnegative().default(0),
|
|
2730
|
+
reviewCount: z.number().int().nonnegative().default(0),
|
|
2731
|
+
failedReviewCount: z.number().int().nonnegative().default(0),
|
|
2732
|
+
failedReviewAttemptsByFeature: z.record(z.string().regex(FEATURE_ID_PATTERN, FEATURE_ID_MESSAGE), z.number().int().nonnegative()).default({}),
|
|
2733
|
+
tokenTelemetry: TokenTelemetrySchema.default({
|
|
2734
|
+
source: "host_unavailable",
|
|
2735
|
+
visibleTokens: null,
|
|
2736
|
+
cacheReadTokens: null,
|
|
2737
|
+
nonCacheTokens: null
|
|
2738
|
+
}),
|
|
2739
|
+
phaseBoundary: PhaseBoundarySchema.nullable().default(null)
|
|
2740
|
+
}).strict();
|
|
2501
2741
|
var SessionSchema = z.object({
|
|
2502
2742
|
version: z.literal(2),
|
|
2503
2743
|
id: z.string().regex(/^[a-zA-Z0-9_-]+$/, "Invalid session id."),
|
|
@@ -2507,6 +2747,20 @@ var SessionSchema = z.object({
|
|
|
2507
2747
|
plan: PlanSchema.nullable(),
|
|
2508
2748
|
activeFeatureId: z.string().regex(FEATURE_ID_PATTERN, FEATURE_ID_MESSAGE).nullable(),
|
|
2509
2749
|
history: z.array(ExecutionHistoryEntrySchema).default([]),
|
|
2750
|
+
budget: BudgetTelemetrySchema.default({
|
|
2751
|
+
phaseStartedAt: "unknown",
|
|
2752
|
+
completedFeaturesSinceBoundary: 0,
|
|
2753
|
+
reviewCount: 0,
|
|
2754
|
+
failedReviewCount: 0,
|
|
2755
|
+
failedReviewAttemptsByFeature: {},
|
|
2756
|
+
tokenTelemetry: {
|
|
2757
|
+
source: "host_unavailable",
|
|
2758
|
+
visibleTokens: null,
|
|
2759
|
+
cacheReadTokens: null,
|
|
2760
|
+
nonCacheTokens: null
|
|
2761
|
+
},
|
|
2762
|
+
phaseBoundary: null
|
|
2763
|
+
}),
|
|
2510
2764
|
closure: z.object({
|
|
2511
2765
|
kind: z.enum(["completed", "deferred", "abandoned"]),
|
|
2512
2766
|
summary: z.string().min(1),
|
|
@@ -2944,6 +3198,13 @@ function nowIso() {
|
|
|
2944
3198
|
|
|
2945
3199
|
// src/runtime/transitions.ts
|
|
2946
3200
|
var MAX_HISTORY_ENTRIES = 500;
|
|
3201
|
+
var MAX_FEATURES_PER_PHASE = 3;
|
|
3202
|
+
var MAX_FAILED_REVIEW_ATTEMPTS_PER_FEATURE = 2;
|
|
3203
|
+
var FEATURE_REVIEW_DEPTH_RANK = {
|
|
3204
|
+
quick: 0,
|
|
3205
|
+
standard: 1,
|
|
3206
|
+
detailed: 2
|
|
3207
|
+
};
|
|
2947
3208
|
function appendHistory(history, entry) {
|
|
2948
3209
|
const next = [...history, entry];
|
|
2949
3210
|
return next.length > MAX_HISTORY_ENTRIES ? next.slice(next.length - MAX_HISTORY_ENTRIES) : next;
|
|
@@ -2957,11 +3218,42 @@ function historyEntryFor(worker, status) {
|
|
|
2957
3218
|
artifactsChanged: worker.artifactsChanged,
|
|
2958
3219
|
validationRun: worker.validationRun,
|
|
2959
3220
|
validationScope: worker.validationScope,
|
|
3221
|
+
featureReviewDepth: worker.featureReviewDepth,
|
|
2960
3222
|
featureReview: worker.featureReview,
|
|
2961
3223
|
finalReview: worker.finalReview,
|
|
2962
3224
|
outcome: worker.outcome
|
|
2963
3225
|
};
|
|
2964
3226
|
}
|
|
3227
|
+
function initialBudgetTelemetry() {
|
|
3228
|
+
return {
|
|
3229
|
+
phaseStartedAt: nowIso(),
|
|
3230
|
+
completedFeaturesSinceBoundary: 0,
|
|
3231
|
+
reviewCount: 0,
|
|
3232
|
+
failedReviewCount: 0,
|
|
3233
|
+
failedReviewAttemptsByFeature: {},
|
|
3234
|
+
tokenTelemetry: {
|
|
3235
|
+
source: "host_unavailable",
|
|
3236
|
+
visibleTokens: null,
|
|
3237
|
+
cacheReadTokens: null,
|
|
3238
|
+
nonCacheTokens: null
|
|
3239
|
+
},
|
|
3240
|
+
phaseBoundary: null
|
|
3241
|
+
};
|
|
3242
|
+
}
|
|
3243
|
+
function normalizeBudgetTelemetry(session) {
|
|
3244
|
+
const defaults = initialBudgetTelemetry();
|
|
3245
|
+
return {
|
|
3246
|
+
...defaults,
|
|
3247
|
+
...session.budget,
|
|
3248
|
+
failedReviewAttemptsByFeature: {
|
|
3249
|
+
...session.budget.failedReviewAttemptsByFeature
|
|
3250
|
+
},
|
|
3251
|
+
tokenTelemetry: {
|
|
3252
|
+
...defaults.tokenTelemetry,
|
|
3253
|
+
...session.budget.tokenTelemetry
|
|
3254
|
+
}
|
|
3255
|
+
};
|
|
3256
|
+
}
|
|
2965
3257
|
function ok(value) {
|
|
2966
3258
|
return { ok: true, value };
|
|
2967
3259
|
}
|
|
@@ -2986,6 +3278,7 @@ function clonePlan(input) {
|
|
|
2986
3278
|
title: feature.title,
|
|
2987
3279
|
summary: feature.summary,
|
|
2988
3280
|
status: "pending",
|
|
3281
|
+
reviewDepth: feature.reviewDepth ?? "standard",
|
|
2989
3282
|
targets: feature.targets ?? [],
|
|
2990
3283
|
validation: feature.validation ?? [],
|
|
2991
3284
|
dependsOn: feature.dependsOn ?? []
|
|
@@ -3039,6 +3332,7 @@ function createSession(goal) {
|
|
|
3039
3332
|
plan: null,
|
|
3040
3333
|
activeFeatureId: null,
|
|
3041
3334
|
history: [],
|
|
3335
|
+
budget: initialBudgetTelemetry(),
|
|
3042
3336
|
closure: null,
|
|
3043
3337
|
lastError: null,
|
|
3044
3338
|
timestamps: {
|
|
@@ -3069,6 +3363,7 @@ function applyPlan(session, planInput) {
|
|
|
3069
3363
|
plan,
|
|
3070
3364
|
activeFeatureId: null,
|
|
3071
3365
|
history: [],
|
|
3366
|
+
budget: initialBudgetTelemetry(),
|
|
3072
3367
|
closure: null,
|
|
3073
3368
|
lastError: null,
|
|
3074
3369
|
timestamps: { ...session.timestamps, completedAt: null }
|
|
@@ -3112,7 +3407,7 @@ function nextRunnableFeature(features, requestedId) {
|
|
|
3112
3407
|
function updateFeature(features, featureId, status) {
|
|
3113
3408
|
return features.map((feature) => feature.id === featureId ? { ...feature, status } : feature.status === "in_progress" && status === "in_progress" ? { ...feature, status: "pending" } : feature);
|
|
3114
3409
|
}
|
|
3115
|
-
function startRun(session, featureId) {
|
|
3410
|
+
function startRun(session, featureId, options) {
|
|
3116
3411
|
if (session.status === "completed") {
|
|
3117
3412
|
return fail("This Flow session is already completed.");
|
|
3118
3413
|
}
|
|
@@ -3122,6 +3417,10 @@ function startRun(session, featureId) {
|
|
|
3122
3417
|
if (session.status === "blocked") {
|
|
3123
3418
|
return fail("Blocked features must be reset before rerun.", "Call flow_feature_reset for the blocked feature, then start it again.");
|
|
3124
3419
|
}
|
|
3420
|
+
const budget = normalizeBudgetTelemetry(session);
|
|
3421
|
+
if (budget.phaseBoundary && !options?.phaseBoundaryAck) {
|
|
3422
|
+
return fail(budget.phaseBoundary.summary, budget.phaseBoundary.resumeInstructions);
|
|
3423
|
+
}
|
|
3125
3424
|
if (session.activeFeatureId) {
|
|
3126
3425
|
if (!featureId || featureId === session.activeFeatureId) {
|
|
3127
3426
|
const active = session.plan.features.find((feature) => feature.id === session.activeFeatureId);
|
|
@@ -3141,6 +3440,12 @@ function startRun(session, featureId) {
|
|
|
3141
3440
|
...session,
|
|
3142
3441
|
status: "running",
|
|
3143
3442
|
plan: nextPlan,
|
|
3443
|
+
budget: budget.phaseBoundary ? {
|
|
3444
|
+
...budget,
|
|
3445
|
+
phaseStartedAt: nowIso(),
|
|
3446
|
+
completedFeaturesSinceBoundary: 0,
|
|
3447
|
+
phaseBoundary: null
|
|
3448
|
+
} : budget,
|
|
3144
3449
|
activeFeatureId: selected.value.id,
|
|
3145
3450
|
lastError: null
|
|
3146
3451
|
});
|
|
@@ -3157,6 +3462,12 @@ function finalFeature(session, featureId) {
|
|
|
3157
3462
|
return false;
|
|
3158
3463
|
return session.plan.features.every((feature) => feature.id === featureId || feature.status === "completed");
|
|
3159
3464
|
}
|
|
3465
|
+
function activeFeature(session, featureId) {
|
|
3466
|
+
return session.plan?.features.find((feature) => feature.id === featureId) ?? null;
|
|
3467
|
+
}
|
|
3468
|
+
function reviewDepthMeetsRequirement(actual, required) {
|
|
3469
|
+
return FEATURE_REVIEW_DEPTH_RANK[actual] >= FEATURE_REVIEW_DEPTH_RANK[required];
|
|
3470
|
+
}
|
|
3160
3471
|
function completionFailure(session, tool, message, recovery) {
|
|
3161
3472
|
return fail(message, recovery, {
|
|
3162
3473
|
...session,
|
|
@@ -3165,6 +3476,8 @@ function completionFailure(session, tool, message, recovery) {
|
|
|
3165
3476
|
}
|
|
3166
3477
|
function validateCompletion(session, worker) {
|
|
3167
3478
|
const wasFinal = finalFeature(session, worker.featureId);
|
|
3479
|
+
const feature = activeFeature(session, worker.featureId);
|
|
3480
|
+
const requiredReviewDepth = feature?.reviewDepth ?? "standard";
|
|
3168
3481
|
if (worker.validationRun.length === 0) {
|
|
3169
3482
|
return completionFailure(session, "flow_feature_complete", "Completion requires recorded validation evidence.", "Run the targeted or broad validation command and record the result.");
|
|
3170
3483
|
}
|
|
@@ -3174,6 +3487,9 @@ function validateCompletion(session, worker) {
|
|
|
3174
3487
|
if (!wasFinal && worker.validationScope !== "targeted") {
|
|
3175
3488
|
return completionFailure(session, "flow_feature_complete", "Non-final feature completion requires targeted validation.", "Record validationScope: targeted for ordinary feature completion.");
|
|
3176
3489
|
}
|
|
3490
|
+
if (!reviewDepthMeetsRequirement(worker.featureReviewDepth, requiredReviewDepth)) {
|
|
3491
|
+
return completionFailure(session, "flow_feature_complete", `Feature review depth '${worker.featureReviewDepth}' does not meet the plan requirement '${requiredReviewDepth}'.`, "Run the feature review at the planned depth or reset/replan if the depth is wrong.");
|
|
3492
|
+
}
|
|
3177
3493
|
if (wasFinal && worker.validationScope !== "broad") {
|
|
3178
3494
|
return completionFailure(session, "flow_feature_complete", "Final feature completion requires broad validation.", "Run the project-level gate and record validationScope: broad.");
|
|
3179
3495
|
}
|
|
@@ -3194,6 +3510,85 @@ function validateCompletion(session, worker) {
|
|
|
3194
3510
|
}
|
|
3195
3511
|
return ok(undefined);
|
|
3196
3512
|
}
|
|
3513
|
+
function incrementFailedReviewAttempt(session, worker, review, reviewKind) {
|
|
3514
|
+
const budget = normalizeBudgetTelemetry(session);
|
|
3515
|
+
const attempts = (budget.failedReviewAttemptsByFeature[worker.featureId] ?? 0) + 1;
|
|
3516
|
+
const exhausted = attempts >= MAX_FAILED_REVIEW_ATTEMPTS_PER_FEATURE;
|
|
3517
|
+
const nextBudget = {
|
|
3518
|
+
...budget,
|
|
3519
|
+
failedReviewCount: budget.failedReviewCount + 1,
|
|
3520
|
+
failedReviewAttemptsByFeature: {
|
|
3521
|
+
...budget.failedReviewAttemptsByFeature,
|
|
3522
|
+
[worker.featureId]: attempts
|
|
3523
|
+
},
|
|
3524
|
+
phaseBoundary: exhausted ? {
|
|
3525
|
+
reason: "review_failure_limit",
|
|
3526
|
+
summary: "Review retry budget exhausted. Stop and report the blocker before making more changes.",
|
|
3527
|
+
resumeInstructions: "Ask the user how to proceed, or reset the feature after an explicit decision. Do not keep auto-repairing this review failure.",
|
|
3528
|
+
recordedAt: nowIso()
|
|
3529
|
+
} : budget.phaseBoundary
|
|
3530
|
+
};
|
|
3531
|
+
if (!exhausted) {
|
|
3532
|
+
return {
|
|
3533
|
+
session: { ...session, budget: nextBudget },
|
|
3534
|
+
attempts,
|
|
3535
|
+
exhausted
|
|
3536
|
+
};
|
|
3537
|
+
}
|
|
3538
|
+
const entry = historyEntryFor({
|
|
3539
|
+
...worker,
|
|
3540
|
+
summary: `${reviewKind === "final" ? "Final review" : "Feature review"} failed after ${attempts} attempts: ${review.summary}`,
|
|
3541
|
+
outcome: {
|
|
3542
|
+
kind: "blocked",
|
|
3543
|
+
summary: review.summary,
|
|
3544
|
+
resolutionHint: "Report the review blocker and wait for explicit reset, replan, or repair approval."
|
|
3545
|
+
}
|
|
3546
|
+
}, "blocked");
|
|
3547
|
+
return {
|
|
3548
|
+
session: touch({
|
|
3549
|
+
...session,
|
|
3550
|
+
status: "blocked",
|
|
3551
|
+
activeFeatureId: null,
|
|
3552
|
+
plan: session.plan ? {
|
|
3553
|
+
...session.plan,
|
|
3554
|
+
features: updateFeature(session.plan.features, worker.featureId, "blocked")
|
|
3555
|
+
} : session.plan,
|
|
3556
|
+
history: appendHistory(session.history, entry),
|
|
3557
|
+
budget: nextBudget
|
|
3558
|
+
}),
|
|
3559
|
+
attempts,
|
|
3560
|
+
exhausted
|
|
3561
|
+
};
|
|
3562
|
+
}
|
|
3563
|
+
function failedReviewCompletion(session, worker, review, reviewKind) {
|
|
3564
|
+
const failedReview = incrementFailedReviewAttempt(session, worker, review, reviewKind);
|
|
3565
|
+
const reviewName = reviewKind === "final" ? "finalReview" : "featureReview";
|
|
3566
|
+
return completionFailure(failedReview.session, "flow_feature_complete", failedReview.exhausted ? "Review retry budget exhausted for this feature." : `Completion requires a passing ${reviewName} with no blocking findings.`, failedReview.exhausted ? "Stop and report the remaining review blocker. Reset or replan only after explicit user direction." : `Pause and report the review blocker. If autonomous repair was explicitly authorized, make at most one repair and retry once; this was failed review attempt ${failedReview.attempts}/${MAX_FAILED_REVIEW_ATTEMPTS_PER_FEATURE}.`);
|
|
3567
|
+
}
|
|
3568
|
+
function clearFailedReviewAttempts(budget, featureId) {
|
|
3569
|
+
const { [featureId]: _cleared, ...remainingAttempts } = budget.failedReviewAttemptsByFeature;
|
|
3570
|
+
return {
|
|
3571
|
+
...budget,
|
|
3572
|
+
failedReviewAttemptsByFeature: remainingAttempts
|
|
3573
|
+
};
|
|
3574
|
+
}
|
|
3575
|
+
function completionBudget(session, worker, allComplete) {
|
|
3576
|
+
const budget = clearFailedReviewAttempts(normalizeBudgetTelemetry(session), worker.featureId);
|
|
3577
|
+
const completedFeaturesSinceBoundary = budget.completedFeaturesSinceBoundary + 1;
|
|
3578
|
+
const reviewCount = budget.reviewCount + (worker.finalReview ? 2 : 1);
|
|
3579
|
+
const reachedFeatureLimit = !allComplete && completedFeaturesSinceBoundary >= MAX_FEATURES_PER_PHASE;
|
|
3580
|
+
return {
|
|
3581
|
+
...budget,
|
|
3582
|
+
completedFeaturesSinceBoundary,
|
|
3583
|
+
reviewCount,
|
|
3584
|
+
phaseBoundary: reachedFeatureLimit ? {
|
|
3585
|
+
reason: "feature_limit",
|
|
3586
|
+
summary: `Completed ${completedFeaturesSinceBoundary} features since the last Flow checkpoint. Stop this OpenCode session and resume from .flow/session.json in a fresh session.`,
|
|
3587
|
+
resumeInstructions: "Start a fresh OpenCode session in this workspace, call flow_status, then call flow_run_start with phaseBoundaryAck: true to begin the next phase.",
|
|
3588
|
+
recordedAt: nowIso()
|
|
3589
|
+
} : budget.phaseBoundary
|
|
3590
|
+
};
|
|
3591
|
+
}
|
|
3197
3592
|
function completeFeature(session, input) {
|
|
3198
3593
|
if (!session.plan || session.status !== "running" || !session.activeFeatureId) {
|
|
3199
3594
|
return fail("No feature is currently running.");
|
|
@@ -3201,7 +3596,7 @@ function completeFeature(session, input) {
|
|
|
3201
3596
|
const parsed = WorkerResultSchema.safeParse(input);
|
|
3202
3597
|
if (!parsed.success) {
|
|
3203
3598
|
const issues = parsed.error.issues.slice(0, 3).map((issue) => `${issue.path.join(".") || "payload"}: ${issue.message}`).join("; ");
|
|
3204
|
-
return fail(`flow_feature_complete payload is invalid: ${issues}.`, 'Provide status, featureId, and summary. Results with status "ok" also need validationScope, at least one validationRun entry, and a featureReview; final features add a finalReview.');
|
|
3599
|
+
return fail(`flow_feature_complete payload is invalid: ${issues}.`, 'Provide status, featureId, and summary. Results with status "ok" also need validationScope, at least one validationRun entry, featureReviewDepth, and a featureReview; final features add a finalReview.');
|
|
3205
3600
|
}
|
|
3206
3601
|
const worker = parsed.data;
|
|
3207
3602
|
if (worker.featureId !== session.activeFeatureId) {
|
|
@@ -3209,6 +3604,7 @@ function completeFeature(session, input) {
|
|
|
3209
3604
|
}
|
|
3210
3605
|
if (worker.status === "needs_input") {
|
|
3211
3606
|
const entry2 = historyEntryFor(worker, "needs_input");
|
|
3607
|
+
const budget2 = normalizeBudgetTelemetry(session);
|
|
3212
3608
|
return ok(touch({
|
|
3213
3609
|
...session,
|
|
3214
3610
|
status: "blocked",
|
|
@@ -3218,9 +3614,16 @@ function completeFeature(session, input) {
|
|
|
3218
3614
|
features: updateFeature(session.plan.features, worker.featureId, "blocked")
|
|
3219
3615
|
},
|
|
3220
3616
|
history: appendHistory(session.history, entry2),
|
|
3617
|
+
budget: budget2,
|
|
3221
3618
|
lastError: null
|
|
3222
3619
|
}));
|
|
3223
3620
|
}
|
|
3621
|
+
if (!isPassingReview(worker.featureReview)) {
|
|
3622
|
+
return failedReviewCompletion(session, worker, worker.featureReview, "feature");
|
|
3623
|
+
}
|
|
3624
|
+
if (finalFeature(session, worker.featureId) && worker.finalReview && !isPassingReview(worker.finalReview)) {
|
|
3625
|
+
return failedReviewCompletion(session, worker, worker.finalReview, "final");
|
|
3626
|
+
}
|
|
3224
3627
|
const validation = validateCompletion(session, worker);
|
|
3225
3628
|
if (!validation.ok)
|
|
3226
3629
|
return validation;
|
|
@@ -3228,12 +3631,14 @@ function completeFeature(session, input) {
|
|
|
3228
3631
|
const features = updateFeature(session.plan.features, worker.featureId, "completed");
|
|
3229
3632
|
const allComplete = features.every((feature) => feature.status === "completed");
|
|
3230
3633
|
const now = nowIso();
|
|
3634
|
+
const budget = completionBudget(session, worker, allComplete);
|
|
3231
3635
|
return ok(touch({
|
|
3232
3636
|
...session,
|
|
3233
3637
|
status: allComplete ? "completed" : "ready",
|
|
3234
3638
|
activeFeatureId: null,
|
|
3235
3639
|
plan: { ...session.plan, features },
|
|
3236
3640
|
history: appendHistory(session.history, entry),
|
|
3641
|
+
budget,
|
|
3237
3642
|
closure: allComplete ? { kind: "completed", summary: worker.summary, recordedAt: now } : null,
|
|
3238
3643
|
lastError: null,
|
|
3239
3644
|
timestamps: {
|
|
@@ -3267,6 +3672,13 @@ function resetFeature(session, featureId) {
|
|
|
3267
3672
|
const affected = dependentFeatureIds(session.plan.features, featureId);
|
|
3268
3673
|
const activeFeatureId = session.activeFeatureId && affected.has(session.activeFeatureId) ? null : session.activeFeatureId;
|
|
3269
3674
|
const nextFeatures = session.plan.features.map((feature) => affected.has(feature.id) ? { ...feature, status: "pending" } : feature);
|
|
3675
|
+
const budget = normalizeBudgetTelemetry(session);
|
|
3676
|
+
const failedReviewAttemptsByFeature = {
|
|
3677
|
+
...budget.failedReviewAttemptsByFeature
|
|
3678
|
+
};
|
|
3679
|
+
for (const featureIdToClear of affected) {
|
|
3680
|
+
delete failedReviewAttemptsByFeature[featureIdToClear];
|
|
3681
|
+
}
|
|
3270
3682
|
const nextStatus = session.approval !== "approved" ? "planning" : activeFeatureId ? "running" : nextFeatures.some((feature) => feature.status === "blocked") ? "blocked" : "ready";
|
|
3271
3683
|
return ok(touch({
|
|
3272
3684
|
...session,
|
|
@@ -3276,6 +3688,11 @@ function resetFeature(session, featureId) {
|
|
|
3276
3688
|
...session.plan,
|
|
3277
3689
|
features: nextFeatures
|
|
3278
3690
|
},
|
|
3691
|
+
budget: {
|
|
3692
|
+
...budget,
|
|
3693
|
+
failedReviewAttemptsByFeature,
|
|
3694
|
+
phaseBoundary: budget.phaseBoundary?.reason === "review_failure_limit" ? null : budget.phaseBoundary
|
|
3695
|
+
},
|
|
3279
3696
|
closure: null,
|
|
3280
3697
|
lastError: null,
|
|
3281
3698
|
timestamps: { ...session.timestamps, completedAt: null }
|
|
@@ -3323,6 +3740,7 @@ function summarizeSession(session) {
|
|
|
3323
3740
|
const latestHistoryEntry = session.history.at(-1) ?? null;
|
|
3324
3741
|
const blockedEntry = session.status === "blocked" ? latestHistoryEntry : null;
|
|
3325
3742
|
const active = session.activeFeatureId ? features.find((feature) => feature.id === session.activeFeatureId) : null;
|
|
3743
|
+
const budget = normalizeBudgetTelemetry(session);
|
|
3326
3744
|
return {
|
|
3327
3745
|
status: session.status,
|
|
3328
3746
|
summary: session.closure?.summary ?? session.lastError?.summary ?? blockedEntry?.summary ?? session.plan?.summary ?? "Flow session is active.",
|
|
@@ -3336,6 +3754,30 @@ function summarizeSession(session) {
|
|
|
3336
3754
|
activeFeature: active ?? null,
|
|
3337
3755
|
progress: { completed: completed.length, total: features.length },
|
|
3338
3756
|
features,
|
|
3757
|
+
budget: {
|
|
3758
|
+
phaseStartedAt: budget.phaseStartedAt,
|
|
3759
|
+
completedFeaturesSinceBoundary: budget.completedFeaturesSinceBoundary,
|
|
3760
|
+
reviewCount: budget.reviewCount,
|
|
3761
|
+
failedReviewCount: budget.failedReviewCount,
|
|
3762
|
+
failedReviewAttemptsByFeature: budget.failedReviewAttemptsByFeature,
|
|
3763
|
+
tokenTelemetry: {
|
|
3764
|
+
...budget.tokenTelemetry,
|
|
3765
|
+
note: budget.tokenTelemetry.source === "host_unavailable" ? "OpenCode does not expose per-turn usage to this plugin surface; Flow can enforce feature/review checkpoints, but token thresholds remain manager-observed." : undefined
|
|
3766
|
+
},
|
|
3767
|
+
phaseBoundary: budget.phaseBoundary
|
|
3768
|
+
},
|
|
3769
|
+
resumePacket: budget.phaseBoundary ? {
|
|
3770
|
+
sessionId: session.id,
|
|
3771
|
+
goal: session.goal,
|
|
3772
|
+
status: session.status,
|
|
3773
|
+
activeFeatureId: session.activeFeatureId,
|
|
3774
|
+
progress: {
|
|
3775
|
+
completed: completed.length,
|
|
3776
|
+
total: features.length
|
|
3777
|
+
},
|
|
3778
|
+
phaseBoundary: budget.phaseBoundary,
|
|
3779
|
+
nextAction: "Start a fresh OpenCode session in this workspace, call flow_status, then call flow_run_start with phaseBoundaryAck: true."
|
|
3780
|
+
} : null,
|
|
3339
3781
|
closure: session.closure,
|
|
3340
3782
|
lastError: session.lastError,
|
|
3341
3783
|
latestHistoryEntry,
|
|
@@ -3349,6 +3791,9 @@ function nextAction(session) {
|
|
|
3349
3791
|
return "Save a plan with flow_plan_save.";
|
|
3350
3792
|
if (session.approval !== "approved")
|
|
3351
3793
|
return "Approve the plan.";
|
|
3794
|
+
if (session.budget.phaseBoundary) {
|
|
3795
|
+
return "Start a fresh OpenCode session, call flow_status, then acknowledge the phase boundary with flow_run_start.";
|
|
3796
|
+
}
|
|
3352
3797
|
if (session.status === "ready")
|
|
3353
3798
|
return "Start the next feature.";
|
|
3354
3799
|
if (session.status === "running")
|
|
@@ -3366,7 +3811,8 @@ var FlowPlanSaveSchema = z2.object({
|
|
|
3366
3811
|
plan: PlanInputSchema.optional()
|
|
3367
3812
|
}).strict();
|
|
3368
3813
|
var FlowRunStartSchema = z2.object({
|
|
3369
|
-
featureId: z2.string().min(1).optional()
|
|
3814
|
+
featureId: z2.string().min(1).optional(),
|
|
3815
|
+
phaseBoundaryAck: z2.boolean().optional()
|
|
3370
3816
|
}).strict();
|
|
3371
3817
|
var FlowFeatureResetSchema = z2.object({
|
|
3372
3818
|
featureId: z2.string().min(1)
|
|
@@ -3382,6 +3828,7 @@ var FlowFeatureCompleteToolSchema = z2.object({
|
|
|
3382
3828
|
artifactsChanged: z2.array(ArtifactSchema).optional(),
|
|
3383
3829
|
validationRun: z2.array(ValidationRunSchema).optional(),
|
|
3384
3830
|
validationScope: ValidationScopeSchema.optional(),
|
|
3831
|
+
featureReviewDepth: FeatureReviewDepthSchema.optional(),
|
|
3385
3832
|
featureReview: ReviewSchema.optional(),
|
|
3386
3833
|
finalReview: FinalReviewSchema.optional(),
|
|
3387
3834
|
outcome: z2.union([WorkerOutcomeSchema, NeedsInputOutcomeSchema]).optional()
|
|
@@ -3496,7 +3943,7 @@ async function flowRunStart(worktree, input) {
|
|
|
3496
3943
|
if (!session) {
|
|
3497
3944
|
return missingSessionResponse();
|
|
3498
3945
|
}
|
|
3499
|
-
const result = startRun(session, args.featureId);
|
|
3946
|
+
const result = startRun(session, args.featureId, args.phaseBoundaryAck === undefined ? undefined : { phaseBoundaryAck: args.phaseBoundaryAck });
|
|
3500
3947
|
if (!result.ok)
|
|
3501
3948
|
return responseFromFailure(result);
|
|
3502
3949
|
const saved = await saveSession(worktree, result.value.session);
|
|
@@ -3516,8 +3963,15 @@ async function flowFeatureComplete(worktree, input) {
|
|
|
3516
3963
|
}
|
|
3517
3964
|
const result = completeFeature(session, worker);
|
|
3518
3965
|
if (!result.ok) {
|
|
3519
|
-
if (result.session)
|
|
3520
|
-
await saveSession(worktree, result.session);
|
|
3966
|
+
if (result.session) {
|
|
3967
|
+
const saved2 = await saveSession(worktree, result.session);
|
|
3968
|
+
return {
|
|
3969
|
+
...summarizeSession(saved2),
|
|
3970
|
+
status: "error",
|
|
3971
|
+
summary: result.message,
|
|
3972
|
+
...result.recovery ? { recovery: result.recovery } : {}
|
|
3973
|
+
};
|
|
3974
|
+
}
|
|
3521
3975
|
return responseFromFailure(result);
|
|
3522
3976
|
}
|
|
3523
3977
|
const saved = await saveSession(worktree, result.value);
|
|
@@ -3744,4 +4198,4 @@ export {
|
|
|
3744
4198
|
plugin_default as default
|
|
3745
4199
|
};
|
|
3746
4200
|
|
|
3747
|
-
//# debugId=
|
|
4201
|
+
//# debugId=519CC894E2A4E68464756E2164756E21
|