From ffdb3547043ac9a9a6845a0b1c2d72d55082cbe9 Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 4 Jul 2026 00:09:44 +0000 Subject: [PATCH] improve(router/workflow): Nemotron COMPLEX review cycle (hour 1 UTC) - Add upfront dep guard in runWorkflow for early, clear failure - Emit modelTier as "SIMPLE" fallback when task.nemotronTier is unset - Normalize validator return value; fail closed on malformed results - Document that pre-revision in complex workflow does not consume loop budget --- src/router/workflow.js | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/src/router/workflow.js b/src/router/workflow.js index be3b1fd..807a29f 100644 --- a/src/router/workflow.js +++ b/src/router/workflow.js @@ -171,7 +171,7 @@ async function runBoundedReviewLoop(draft, task, deps, opts = {}) { phase: "reviewed", ...modelAttribution({ model: activeNemotronModel, - modelTier: escalation.escalate ? "REASONING" : task.nemotronTier, + modelTier: escalation.escalate ? "REASONING" : (task.nemotronTier || "SIMPLE"), modelRole: "review", }), verdict, @@ -182,7 +182,12 @@ async function runBoundedReviewLoop(draft, task, deps, opts = {}) { if (verdict === "PASS") { if (validate) { emit({ phase: "validation-requested" }); - const validationResult = await validate({ draft, task }); + const rawValidation = await validate({ draft, task }); + // Fail closed on malformed validator returns — treat as failed validation. + const validationResult = + rawValidation && typeof rawValidation === "object" + ? rawValidation + : { passed: false, failureKind: "validator-malformed" }; if (!validationResult.passed) { // Validation failed — budget check, then revise with failure context. @@ -336,6 +341,8 @@ async function runComplexWorkflow(task, deps) { // If either pre-review flagged issues, apply a targeted revision before the shared loop // so findings are incorporated regardless of whether the standard review also returns REVISE. + // Note: this pre-revision does not consume the loop's revision budget — runBoundedReviewLoop + // starts revisionCount at 0, giving the loop its full maxRevisions budget after pre-reviews. let resolvedDraft = draft; if (archVerdict === "REVISE" || deepVerdict === "REVISE") { emit({ @@ -417,6 +424,12 @@ async function runHighRiskWorkflow(task, deps) { * @returns {Promise<{ output: string, verdict: 'PASS'|'REVISE', revisionCount: number }>} */ async function runWorkflow(task, deps) { + if (!deps || typeof deps.emit !== "function" || !deps.codingClient || !deps.nemotronClient) { + throw new Error( + "runWorkflow: deps must include codingClient, nemotronClient, and emit" + ); + } + const workflow = task.workflow || "standard"; switch (workflow) {