Skip to content

Commit 458a0ea

Browse files
committed
fix(dag): bind review output into synthesize final gate and drop mode claim from review gate error (issues #304, #305)
1 parent 758e8c1 commit 458a0ea

4 files changed

Lines changed: 33 additions & 2 deletions

File tree

packages/opencode/src/dag/blocks.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -276,6 +276,14 @@ function compileBlock(
276276

277277
const verifyAggregatorIDs = verifyAggregators.get(block.id)
278278
const verifyAggregator = verifyAggregatorIDs && verifyAggregatorIDs.length > 0 ? verifyAggregatorIDs[0] : undefined
279+
// A synthesize that follows a review is the route's final gate: it must map
280+
// the review output so unresolvedReviewOutcomes/finalReviewGates recognize
281+
// an ACCEPTed review as resolved (issue #304) — the same binding contract
282+
// the verify aggregation path already honors.
283+
const synthesizeGateBinding =
284+
block.kind === "synthesize" && reviewDependency
285+
? { [`${reviewDependency.replace(/-/g, "_")}_review`]: `${reviewDependency}.output` }
286+
: undefined
279287
return [
280288
node({
281289
id: block.id,
@@ -293,7 +301,7 @@ function compileBlock(
293301
implementation_changed_files: `${verifyAggregator}.output.changed_files`,
294302
implementation_fingerprint: `${verifyAggregator}.output.fingerprint`,
295303
}
296-
: undefined,
304+
: synthesizeGateBinding,
297305
outputSchema: WRITER_KINDS.has(block.kind)
298306
? IMPLEMENTATION_SCHEMA
299307
: block.kind === "verify"

packages/opencode/src/dag/dag.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ export class ReviewGateError extends Error {
112112
readonly reviewIDs: string[]
113113

114114
constructor(dagID: string, reviewIDs: string[]) {
115-
super(`Cannot complete deep workflow ${dagID}: unresolved review outcome(s): ${reviewIDs.join(", ")}`)
115+
super(`Cannot complete workflow ${dagID}: unresolved review outcome(s): ${reviewIDs.join(", ")}`)
116116
this.name = "ReviewGateError"
117117
this.dagID = dagID
118118
this.reviewIDs = reviewIDs

packages/opencode/test/dag/blocks.test.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,27 @@ describe("workflow blocks", () => {
161161
expect(nodes.find((node) => node.id === "report")?.condition).toBe('decision.output.verdict == "ACCEPT"')
162162
})
163163

164+
// Issue #304: a synthesize following a review is the route's final gate —
165+
// it must map the review output, otherwise an ACCEPTed review stays listed
166+
// as an unresolved review outcome forever.
167+
it("binds the review output into a following synthesize as the final gate", () => {
168+
const nodes = DagBlocks.compileWorkflowBlocks({
169+
objective: "Deliver a reviewed change with a bound final report",
170+
blocks: [
171+
{ id: "implementation", kind: "coding" },
172+
{ id: "verification", kind: "verify", depends_on: ["implementation"] },
173+
{ id: "decision", kind: "review", depends_on: ["verification"] },
174+
{ id: "report", kind: "synthesize", depends_on: ["decision"] },
175+
{ id: "side-note", kind: "synthesize", depends_on: ["verification"] },
176+
],
177+
})
178+
179+
expect(nodes.find((node) => node.id === "report")?.input_mapping).toEqual({
180+
decision_review: "decision.output",
181+
})
182+
expect(nodes.find((node) => node.id === "side-note")?.input_mapping).toBeUndefined()
183+
})
184+
164185
it("keeps every downstream branch behind a reporting scope gate", () => {
165186
const nodes = DagBlocks.compileWorkflowBlocks({
166187
objective: "Deliver only while the bounded route remains valid",

packages/opencode/test/dag/dag-wake-integration.test.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -788,6 +788,8 @@ describe("DagLoop atomic wake integration", () => {
788788
expect(completion).toBeInstanceOf(Error)
789789
if (!(completion instanceof Error)) throw new Error("deep completion unexpectedly succeeded")
790790
expect(completion.message).toContain("unresolved review outcome")
791+
// Issue #305: the gate message must not claim a mode it does not check.
792+
expect(completion.message).not.toContain("deep workflow")
791793
expect((yield* store.getWorkflow(dagID))?.status).toBe("running")
792794
expect((yield* store.getNode(dagID, "review-diff"))?.status).toBe("pending")
793795
}),

0 commit comments

Comments
 (0)