Skip to content

Commit 1884e10

Browse files
committed
Require review-first discovery before remediation
Add a concrete planning.reviewFindings prerequisite so broad review-and-fix requests cannot enter remediation mode before an audit produces findings. Keep real review_and_fix completion strict and document the v2.0.14 release contract. Constraint: Add a narrow planning-context/tool-schema contract without adding commands, tools, runtime modes, state paths, package exports, or dependencies Constraint: Keep zod aligned with @opencode-ai/plugin; dependency contract verified unchanged at zod 4.1.8 Rejected: Loosen reviewFindingClosures, reviewScopeLedger, or final-review gates | that would make shallow no-change completion easier instead of forcing discovery first Rejected: Raise the bundle sanity budget | prompt wording was trimmed so the bundle remains under the existing 720896-byte ceiling Confidence: high Scope-risk: moderate Directive: Use goalMode: review_and_fix only after concrete findings are recorded in planning.reviewFindings; no-findings review-and-fix starts as goalMode: review Tested: bun test tests/config/prompt-contracts.test.ts tests/plan-graph-validation.test.ts tests/prompt-mode-behavior-eval.test.ts tests/prompt-mode-capture.test.ts tests/runtime/plan-and-tool-schema-contracts.test.ts tests/completion-gates.test.ts tests/runtime/evidence-packets.test.ts; bun test tests/config/tool-schemas.test.ts; bun run build plus bundle sanity; bun run typecheck; bun run lint; bun run check Not-tested: Live GitHub-hosted CI/release workflow for tag v2.0.14 before push
1 parent 72362ea commit 1884e10

20 files changed

Lines changed: 318 additions & 27 deletions

CHANGELOG.md

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,32 @@
22

33
## [Unreleased]
44

5+
## [2.0.14] - 2026-05-07
6+
7+
Route no-findings review-and-fix work through review-first discovery
8+
9+
Flow 2.0.14 fixes the review-and-fix quality regression where broad codebase review requests with no concrete findings could be planned as a single `review_and_fix` feature and then degrade into repeated completion-payload retries. Planning now has an explicit `planning.reviewFindings` context ledger for concrete existing review findings, and `review_and_fix` plan application fails fast when that ledger is empty.
10+
11+
No-findings review-and-fix requests now stay in `goalMode: review` for audit/discovery first. Once a review produces concrete findings, a remediation replan can use `goalMode: review_and_fix` with those findings recorded in `planning.reviewFindings`, preserving the strict finding-to-fix-to-validation chain.
12+
13+
The release deliberately keeps the existing completion gates strict. Real `review_and_fix` remediation still requires closure evidence, review-scope accounting, final-review evidence, and reviewer approval; this patch changes when remediation mode may start, not what it must prove before completion.
14+
15+
Prompt contracts, planner/auto/planning-researcher guidance, and prompt-mode calibration fixtures now mirror the runtime rule: no findings means review-first discovery, known findings means strict remediation. Regression coverage locks both paths, including inline-only `planning.reviewFindings` acceptance and audit-only no-findings calibration.
16+
17+
Constraint: Add only a narrow planning-context contract for concrete review findings; do not add commands, tools, runtime modes, state paths, package exports, or dependencies
18+
Constraint: Accept a narrow raw tool-schema budget increase for `planning.reviewFindings` while keeping the bundle sanity budget unchanged
19+
Constraint: Preserve strict `review_and_fix` completion gates for actual remediation with known findings
20+
Constraint: Keep `zod` aligned with `@opencode-ai/plugin`; no dependency-version changes in this patch
21+
Rejected: Loosen `reviewFindingClosures`, `reviewScopeLedger`, or final-review requirements for no-change completions | that would make shallow review-and-fix completion easier instead of forcing discovery first
22+
Rejected: Keep broad no-findings review-and-fix as a single remediation feature | it frames the agent around completion accounting before findings exist
23+
Rejected: Infer known findings from natural-language goals alone | `planning.reviewFindings` gives the runtime and prompts a concrete, auditable prerequisite
24+
Confidence: high
25+
Scope-risk: moderate
26+
Reversibility: clean
27+
Directive: Use `goalMode: review_and_fix` only after concrete findings are recorded in `planning.reviewFindings`; broad review-and-fix/codebase-review requests without findings must start as `goalMode: review`
28+
Tested: `bun test tests/config/prompt-contracts.test.ts tests/plan-graph-validation.test.ts tests/prompt-mode-behavior-eval.test.ts tests/prompt-mode-capture.test.ts tests/runtime/plan-and-tool-schema-contracts.test.ts tests/completion-gates.test.ts tests/runtime/evidence-packets.test.ts` (91 pass, 1262 expect calls); `bun test tests/config/tool-schemas.test.ts`; `bun run build` plus bundle sanity at 720706 bytes; `bun run typecheck`; `bun run lint`; Oracle review found no blockers and P2 follow-ups were applied; `bun run check`
29+
Not-tested: Live GitHub-hosted CI/release workflow runs for tag `v2.0.14` before push
30+
531
## [2.0.13] - 2026-05-07
632

733
Dedupe final-completion tool guidance in subagent prompts

docs/releases/v2.0.14.md

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# v2.0.14
2+
3+
Route no-findings review-and-fix work through review-first discovery
4+
5+
Flow 2.0.14 fixes the review-and-fix quality regression where broad codebase review requests with no concrete findings could be planned as a single `review_and_fix` feature and then degrade into repeated completion-payload retries. Planning now has an explicit `planning.reviewFindings` context ledger for concrete existing review findings, and `review_and_fix` plan application fails fast when that ledger is empty.
6+
7+
No-findings review-and-fix requests now stay in `goalMode: review` for audit/discovery first. Once a review produces concrete findings, a remediation replan can use `goalMode: review_and_fix` with those findings recorded in `planning.reviewFindings`, preserving the strict finding-to-fix-to-validation chain.
8+
9+
The release deliberately keeps the existing completion gates strict. Real `review_and_fix` remediation still requires closure evidence, review-scope accounting, final-review evidence, and reviewer approval; this patch changes when remediation mode may start, not what it must prove before completion.
10+
11+
Prompt contracts, planner/auto/planning-researcher guidance, and prompt-mode calibration fixtures now mirror the runtime rule: no findings means review-first discovery, known findings means strict remediation. Regression coverage locks both paths, including inline-only `planning.reviewFindings` acceptance and audit-only no-findings calibration.
12+
13+
Constraint: Add only a narrow planning-context contract for concrete review findings; do not add commands, tools, runtime modes, state paths, package exports, or dependencies
14+
Constraint: Accept a narrow raw tool-schema budget increase for `planning.reviewFindings` while keeping the bundle sanity budget unchanged
15+
Constraint: Preserve strict `review_and_fix` completion gates for actual remediation with known findings
16+
Constraint: Keep `zod` aligned with `@opencode-ai/plugin`; no dependency-version changes in this patch
17+
Rejected: Loosen `reviewFindingClosures`, `reviewScopeLedger`, or final-review requirements for no-change completions | that would make shallow review-and-fix completion easier instead of forcing discovery first
18+
Rejected: Keep broad no-findings review-and-fix as a single remediation feature | it frames the agent around completion accounting before findings exist
19+
Rejected: Infer known findings from natural-language goals alone | `planning.reviewFindings` gives the runtime and prompts a concrete, auditable prerequisite
20+
Confidence: high
21+
Scope-risk: moderate
22+
Reversibility: clean
23+
Directive: Use `goalMode: review_and_fix` only after concrete findings are recorded in `planning.reviewFindings`; broad review-and-fix/codebase-review requests without findings must start as `goalMode: review`
24+
Tested: `bun test tests/config/prompt-contracts.test.ts tests/plan-graph-validation.test.ts tests/prompt-mode-behavior-eval.test.ts tests/prompt-mode-capture.test.ts tests/runtime/plan-and-tool-schema-contracts.test.ts tests/completion-gates.test.ts tests/runtime/evidence-packets.test.ts` (91 pass, 1262 expect calls); `bun test tests/config/tool-schemas.test.ts`; `bun run build` plus bundle sanity at 720706 bytes; `bun run typecheck`; `bun run lint`; Oracle review found no blockers and P2 follow-ups were applied; `bun run check`
25+
Not-tested: Live GitHub-hosted CI/release workflow runs for tag `v2.0.14` before push

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "opencode-plugin-flow",
3-
"version": "2.0.13",
3+
"version": "2.0.14",
44
"description": "Stateful planning and execution workflow plugin for OpenCode",
55
"type": "module",
66
"main": "dist/index.js",

src/prompts/contracts.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ const FLOW_PLAN_CONTRACT_BASE = `Persist a plan with:
2626
2727
Plan rules:
2828
- review/review_and_fix plans must declare review scope through reviewScope or fileTargets for every target/domain the runtime must account.
29+
- Use goalMode: review_and_fix only when concrete findings already exist and are recorded in planning.reviewFindings; broad review-and-fix/codebase-review goals with no findings must start as goalMode: review for audit/discovery, then replan review_and_fix after findings are recorded.
2930
3031
Record planning context separately via flow_plan_context_record or flow_plan_apply({ plan, planning: ... }) when needed — not inside \`plan\`.
3132
- planning.repoProfile?: string[]
@@ -36,6 +37,7 @@ Record planning context separately via flow_plan_context_record or flow_plan_app
3637
- planning.research?: string[]
3738
- planning.implementationApproach?: { chosenDirection: string, keyConstraints: string[], validationSignals: string[], sources: string[] }
3839
- planning.decisionLog?: { question: string, decisionMode?: autonomous_choice | recommend_confirm | human_required, decisionDomain?: architecture | product | quality | scope | delivery, options: { label: string, tradeoffs: string[] }[], recommendation: string, rationale: string[] }[]
40+
- planning.reviewFindings?: { findingRef: string, summary: string, sourceRefs: string[] }[] — concrete existing findings from a user-provided finding, audit report, issue, failing test, or prior review ledger; sourceRefs must be non-empty concrete refs. Missing/empty means no remediation findings are known yet.
3941
- planning.evidencePackets?: { id: string, purpose?: planning | review | audit | validation | general, contextLane?: planning | auto_planning | execution | review | status | history | session | reset | doctor | control, summary: string, sourceRefs?: string[], highlights?: string[], selectedContext?: string[], excludedContext?: string[], codemapSummaries?: string[], sliceSummaries?: string[], relationshipHypotheses?: string[], ambiguities?: string[], knownExclusions?: string[], alreadyCoveredFindings?: string[], validationEvidence?: { command, status, summary }[] }[]`;
4042

4143
export const FLOW_PLAN_CONTRACT = `${FLOW_PLAN_CONTRACT_BASE}

src/prompts/fragments.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ export const FLOW_COORDINATOR_BOUNDARY_RULE =
2020
"- Stay at the coordinator layer: decide whether planning, execution, review, reset, or recovery happens next, and rely on the specialized Flow roles for their detailed contracts.";
2121
export const FLOW_REVIEW_FINDINGS_LOOP_RULE =
2222
"- Do not complete a feature while review findings remain. Fix them, record a finding-by-finding closure ledger with code/test/validation evidence and residual risk, rerun validation, and rereview until the feature is clean or a real blocker remains.";
23+
export const FLOW_REVIEW_FIRST_WITHOUT_FINDINGS_PLAN_RULE =
24+
"- Broad review-and-fix/codebase-review goals with no concrete existing findings in planning.reviewFindings must start as goalMode: review for audit/discovery; replan to goalMode: review_and_fix only after concrete findings are recorded in planning.reviewFindings.";
2325
export const FLOW_FEATURE_REVIEW_APPROVAL_RULE =
2426
"- Before persisting success, get flow-reviewer approval and record it through flow_review_record_feature.";
2527
export const FLOW_FINAL_COMPLETION_PATH_RULE =

src/prompts/generated/role-prompts.ts

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ import {
3232
FLOW_RESUME_ONLY_RULE,
3333
FLOW_REVIEW_CONTEXT_DISCOVERY_RULE,
3434
FLOW_REVIEW_FINDINGS_LOOP_RULE,
35+
FLOW_REVIEW_FIRST_WITHOUT_FINDINGS_PLAN_RULE,
3536
FLOW_RUNTIME_TOOLS_AUTHORITATIVE_RULE,
3637
FLOW_RUNTIME_TOOLS_AUTHORITATIVE_WORKFLOW_RULE,
3738
FLOW_STACK_STANDARDS_PROFILE_READONLY_RULE,
@@ -77,7 +78,7 @@ const control = getCoreRoleProtocol("control");
7778
const FLOW_PLANNING_RESEARCHER_EXAMPLES = renderExampleBlocks([
7879
{
7980
name: "review-first-codebase-review",
80-
body: "If the goal asks for a full codebase review and fixes, recommend a review-first plan shape: first run a read-only codebase review/audit, then fix confirmed findings only after the audit ledger exists. Do not invent findings during research.",
81+
body: "No findings: recommend goalMode: review for audit, requiresReplanAfterAudit: true. goalMode: review_and_fix only after concrete findings are recorded in planning.reviewFindings. Do not invent findings.",
8182
},
8283
{
8384
name: "not-runtime-planner",
@@ -89,16 +90,17 @@ export const FLOW_PLANNING_RESEARCHER_AGENT_PROMPT = renderPromptSections([
8990
{ title: "Role", body: "You are the Flow planning researcher." },
9091
{
9192
title: "Objective",
92-
body: "Produce a read-only evidence packet that helps Flow planning stay phase-correct, especially for broad review-and-fix goals where findings do not exist yet.",
93+
body: "Produce read-only evidence that keeps broad no-findings review-and-fix planning review-first.",
9394
},
9495
{
9596
title: "Rules",
9697
body: `${FLOW_NEVER_WRITE_FLOW_FILES_RULE}
9798
${FLOW_CONTEXT_GATHERING_READONLY_RULE}
9899
- Stay read-only: do not write repository code, do not call Flow runtime tools, do not apply or approve plans, and do not claim execution success.
99100
- You are not flow-planner. Produce research for flow-planner or flow-auto to consume through normal runtime-owned planning.
100-
- For full codebase review and fix goals, recommend an audit/review-first plan shape before any fix feature. Findings belong in the audit/review ledger, not planning research.
101-
- Do not invent findings, severity, or closure evidence. If findings are not already provided, say the fix phase must wait for a concrete review ledger.
101+
${FLOW_REVIEW_FIRST_WITHOUT_FINDINGS_PLAN_RULE}
102+
- For codebase review/fix with no findings, recommend an audit/review-first plan shape before any fix feature; copy concrete findings into planning.reviewFindings after audit.
103+
- Do not invent findings. If none are provided, set recommendedPlanShape.goalMode to "review", requiresReplanAfterAudit: true; fixes wait for a concrete review ledger.
102104
${FLOW_STACK_STANDARDS_PROFILE_READONLY_RULE}
103105
${FLOW_PACKAGE_MANAGER_PRIMARY_CONTRACT_RULE}
104106
${FLOW_PACKAGE_MANAGER_AMBIGUITY_PLAN_RULE}
@@ -109,7 +111,7 @@ ${FLOW_ENGINEERING_QUALITY_RULE}
109111
title: "Workflow",
110112
body: `1. Normalize the request into goal, constraints, evidence requirements, and done-when.
111113
2. Inspect only enough local evidence to identify package manager, stack, validation scripts, local standards, and the major review surfaces.
112-
3. For review-and-fix goals without existing findings, recommend a review-first or replan-after-audit shape instead of a fake all-in-one fix feature.
114+
3. For review-and-fix goals without findings, recommend goalMode: "review" and requiresReplanAfterAudit: true; use goalMode: "review_and_fix" only after planning.reviewFindings has concrete findings.
113115
4. Return exactly one JSON object with no markdown fences or commentary:
114116
115117
{
@@ -120,6 +122,7 @@ ${FLOW_ENGINEERING_QUALITY_RULE}
120122
"stackProfile": { "languages": unknown[], "frameworks": unknown[], "runtimes": unknown[], "packageManagers": unknown[], "tools": unknown[] },
121123
"standardsProfile": { "localGuidelines": unknown[], "externalGuidance": unknown[], "rules": unknown[], "gaps": unknown[], "precedence": string[] },
122124
"research": string[],
125+
"reviewFindings": { "findingRef": string, "summary": string, "sourceRefs": string[] }[],
123126
"evidencePackets": unknown[]
124127
},
125128
"recommendedPlanShape": {
@@ -153,15 +156,17 @@ ${FLOW_ENGINEERING_QUALITY_RULE}
153156
${FLOW_OPERATOR_PROGRESS_RULE}
154157
- Keep plans short, concrete, and ready to execute.
155158
- Broad goals are valid.
156-
- For broad review-and-fix goals where findings do not exist yet, use a review-first plan shape or a flow-planning-researcher handoff; do not create a fake all-in-one fix feature.
159+
${FLOW_REVIEW_FIRST_WITHOUT_FINDINGS_PLAN_RULE}
160+
- For broad review-and-fix goals without findings, use goalMode: review or a flow-planning-researcher handoff; do not create a fake all-in-one fix feature.
161+
- Use goalMode: review_and_fix only after concrete findings exist in planning.reviewFindings.
157162
- Do not start implementation after drafting a plan.`,
158163
},
159164
{
160165
title: "Workflow",
161166
body: `${renderWorkflowProtocol(planner)}
162167
1. Call flow_plan_start.
163168
2. Read enough repo context to justify the plan. When the goal is a broad review-and-fix request and Task handoff is available, ask flow-planning-researcher for a read-only planning research packet before finalizing decomposition.
164-
3. Persist repoProfile, packageManager, stackProfile, standardsProfile, research, implementationApproach, evidencePackets, or decisionLog with flow_plan_context_record.
169+
3. Persist repoProfile, packageManager, stackProfile, standardsProfile, research, implementationApproach, evidencePackets, decisionLog, or concrete reviewFindings with flow_plan_context_record.
165170
4. Return plan content matching:
166171
167172
${FLOW_PLAN_CONTRACT}
@@ -244,7 +249,7 @@ ${FLOW_STRUCTURED_RECOVERY_RULE}`,
244249
body: `${renderWorkflowProtocol(auto)}
245250
1. Call flow_auto_prepare with the raw command argument string before planning or repo inspection.
246251
2. If flow_auto_prepare returns missing_goal, render that result clearly and stop.
247-
3. If planning is needed for a broad review-and-fix/codebase-review request, prefer a Task-tool handoff to flow-planning-researcher first so review discovery and fix execution stay phase-correct.
252+
3. If planning is needed for broad review-and-fix/codebase-review, use flow-planning-researcher first; without concrete planning.reviewFindings, start with goalMode: review, then goalMode: review_and_fix only after findings are recorded.
248253
4. If planning is needed, prefer a Task-tool handoff to flow-planner; the planning pass records stackProfile, standardsProfile, and useful evidencePackets with flow_plan_context_record, persists the plan with flow_plan_apply, and approves it with flow_plan_approve.
249254
5. If repo evidence and research still leave a meaningful architecture, product, or quality decision still remains, record the options and recommendation with flow_plan_context_record so the runtime summary exposes a decision gate. If any Flow tool response includes session.decisionGate with status recommend_confirm or human_required, present that recommendation clearly and stop for user confirmation.
250255
6. Start the next feature with flow_run_start and keep that feature active until it is clean or truly blocked.

src/runtime/domain/index.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,11 @@ export {
2323
type ReviewIncludedContext,
2424
type ReviewValidationEvidence,
2525
} from "./final-review-coverage";
26-
export { validatePlanGraph } from "./plan-graph-validation";
26+
export {
27+
REVIEW_AND_FIX_FINDINGS_REQUIRED_MESSAGE,
28+
validatePlanGraph,
29+
validateReviewAndFixFindingPrerequisite,
30+
} from "./plan-graph-validation";
2731
export { selectProjectedFeatureSubset } from "./plan-projection";
2832
export {
2933
mergeEvidencePackets,

src/runtime/domain/plan-graph-validation.ts

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,20 @@
1-
import type { Plan } from "../schema";
1+
import type { Plan, PlanningContext } from "../schema";
2+
3+
export const REVIEW_AND_FIX_FINDINGS_REQUIRED_MESSAGE =
4+
"review_and_fix plans require concrete existing findings in planning.reviewFindings. For broad review/codebase-review goals without findings, apply a review-first plan with goalMode: review, run discovery/audit, then replan remediation after findings exist.";
5+
6+
export function validateReviewAndFixFindingPrerequisite(
7+
plan: Plan,
8+
planning: PlanningContext,
9+
): string | null {
10+
if (plan.goalMode !== "review_and_fix") {
11+
return null;
12+
}
13+
if (planning.reviewFindings.length > 0) {
14+
return null;
15+
}
16+
return REVIEW_AND_FIX_FINDINGS_REQUIRED_MESSAGE;
17+
}
218

319
export function validatePlanGraph(plan: Plan): string | null {
420
const ids = new Set<string>();

src/runtime/domain/planning-context.ts

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import type { PlanningContext } from "../schema";
1+
import type { PlanningContext, ReviewFindingPlanningContext } from "../schema";
22
import type { EvidencePacket } from "../schema-evidence-packets";
33

44
function mergeUniqueStrings(
@@ -45,6 +45,20 @@ export function mergeEvidencePackets(
4545
return [...byId.values()];
4646
}
4747

48+
function mergeReviewFindings(
49+
current: readonly ReviewFindingPlanningContext[] = [],
50+
next?: readonly ReviewFindingPlanningContext[],
51+
): ReviewFindingPlanningContext[] {
52+
const byRef = new Map<string, ReviewFindingPlanningContext>();
53+
for (const finding of current) {
54+
byRef.set(finding.findingRef, finding);
55+
}
56+
for (const finding of next ?? []) {
57+
byRef.set(finding.findingRef, finding);
58+
}
59+
return [...byRef.values()];
60+
}
61+
4862
export function mergePlanningContext(
4963
current: PlanningContext,
5064
next: Partial<PlanningContext> = {},
@@ -61,6 +75,10 @@ export function mergePlanningContext(
6175
next.implementationApproach ?? current.implementationApproach,
6276
decisionLog: next.decisionLog ?? current.decisionLog,
6377
replanLog: mergeUniqueBySerialized(current.replanLog, next.replanLog),
78+
reviewFindings: mergeReviewFindings(
79+
current.reviewFindings,
80+
next.reviewFindings,
81+
),
6482
evidencePackets: mergeEvidencePackets(
6583
current.evidencePackets,
6684
next.evidencePackets,

0 commit comments

Comments
 (0)