Discovered by live end-to-end testing on v1.0.21 (harness smoke DAG dag_fe5ff550b327CGm6EzUOUNwBex).
Problem Statement
After a diff review settles ACCEPT and the whole graph completes, workflow(action="status") still reports unresolved_reviews: ["decision"]. The field meant to surface actionable REJECT verdicts becomes false-positive noise exactly on the clean path — the loudness signal #294 added now cries wolf on success.
Root Cause
reviewAccepted (review-lifecycle) requires, besides verdict ACCEPT, a completed final gate from finalReviewGates: a required node that depends on the review, maps its output in input_mapping, and carries a condition accepting the verdict. The block compiler emits the synthesize (report) node without any input_mapping, so no final gate is ever found for block-compiled routes → the ACCEPTed review counts as unresolved forever.
This is the third instance of one pattern: block contracts promise dependency-evidence binding in prose while the compiler emits no mechanical input_mapping (verify before #299; now synthesize).
Deeper Implication
validateFinalReviewGate rejects the same shape in deep mode ("deep diff review must feed a required final gate conditioned on verdict ACCEPT"). A block route with implementation-review → synthesize therefore compiles in standard mode but should fail portable validation in deep mode — a mode-dependent cliff for the same authoring shape.
Proposed Fix (from analysis, not yet implemented)
When a synthesize block depends on a review, bind the review's output into the synthesize node's input_mapping (the synthesize condition is already <review>.output.verdict == "ACCEPT"). That satisfies finalReviewGates on all three axes (required ✓, mapping ✓, verdict-accepting condition ✓), making status clean after ACCEPT and the deep-mode shape valid. Add regression tests: ACCEPTed route → no unresolved_reviews; deep-mode block route with report validates.
Out of Scope
- Changing
unresolvedReviewOutcomes semantics for REJECT/skipped shapes (correct today).
Discovered by live end-to-end testing on v1.0.21 (harness smoke DAG
dag_fe5ff550b327CGm6EzUOUNwBex).Problem Statement
After a diff review settles ACCEPT and the whole graph completes,
workflow(action="status")still reportsunresolved_reviews: ["decision"]. The field meant to surface actionable REJECT verdicts becomes false-positive noise exactly on the clean path — the loudness signal #294 added now cries wolf on success.Root Cause
reviewAccepted(review-lifecycle) requires, besides verdict ACCEPT, a completed final gate fromfinalReviewGates: a required node that depends on the review, maps its output ininput_mapping, and carries a condition accepting the verdict. The block compiler emits thesynthesize(report) node without anyinput_mapping, so no final gate is ever found for block-compiled routes → the ACCEPTed review counts as unresolved forever.This is the third instance of one pattern: block contracts promise dependency-evidence binding in prose while the compiler emits no mechanical
input_mapping(verify before #299; now synthesize).Deeper Implication
validateFinalReviewGaterejects the same shape in deep mode ("deep diff review must feed a required final gate conditioned on verdict ACCEPT"). A block route with implementation-review → synthesize therefore compiles in standard mode but should fail portable validation in deep mode — a mode-dependent cliff for the same authoring shape.Proposed Fix (from analysis, not yet implemented)
When a
synthesizeblock depends on a review, bind the review's output into the synthesize node'sinput_mapping(the synthesize condition is already<review>.output.verdict == "ACCEPT"). That satisfiesfinalReviewGateson all three axes (required ✓, mapping ✓, verdict-accepting condition ✓), making status clean after ACCEPT and the deep-mode shape valid. Add regression tests: ACCEPTed route → no unresolved_reviews; deep-mode block route with report validates.Out of Scope
unresolvedReviewOutcomessemantics for REJECT/skipped shapes (correct today).