From 7e882b56d0b2dcd5b3e54ea365f8616c7d6cc401 Mon Sep 17 00:00:00 2001 From: Mikael Hugo Date: Sat, 16 May 2026 19:35:44 +0200 Subject: [PATCH] fix(engine): auto-remediate reassess-roadmap ASSESSMENT artifact (breaks loop) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The rogue-write detector in auto-post-unit.js:detectRogueFileWrites checks for an `artifacts` table row with artifact_type='ASSESSMENT' after a reassess-roadmap unit writes the assessment file. Other unit types (execute-task, complete-slice) had auto-remediation paths that sync the DB to the filesystem when state is stale. reassess-roadmap did not. Effect: the reassess_roadmap MCP tool writes the assessment file but nothing registers it in the artifacts table. EVERY successful iteration gets flagged rogue post-hoc; SF re-dispatches the same unit; same thing happens; infinite loop until --timeout SIGTERM. Empirically observed today (filed as sf-mpmp8min68-yoy2pa): Run 1: success $0.012, 16709 tokens → rogue → redispatch Run 2: success $0.017, 18925 tokens → rogue → redispatch Run 3: started → SIGTERM at --timeout 480000ms Each iteration is real work product (real assessment content, verdict: roadmap-confirmed) — the model is doing its job correctly, the engine just doesn't recognize completion. Fix: when assessment file exists on disk and artifacts row is missing, INSERT into artifacts table via insertArtifact (parallel to updateTaskStatus / updateSliceStatus auto-remediate in the same function). Falls back to flagging rogue only if the insert fails. Co-Authored-By: Claude Opus 4.7 (1M context) --- src/resources/extensions/sf/auto-post-unit.js | 24 +++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/src/resources/extensions/sf/auto-post-unit.js b/src/resources/extensions/sf/auto-post-unit.js index e9afde4c9..93d259d7a 100644 --- a/src/resources/extensions/sf/auto-post-unit.js +++ b/src/resources/extensions/sf/auto-post-unit.js @@ -162,8 +162,9 @@ const LIFECYCLE_ONLY_UNITS = new Set([ "rewrite-docs", ]); -import { existsSync, unlinkSync } from "node:fs"; +import { existsSync, readFileSync, unlinkSync } from "node:fs"; import { join } from "node:path"; +import { insertArtifact } from "./sf-db/sf-db-artifacts.js"; import { getAutoSession } from "./auto/session.js"; import { describeNextUnit } from "./auto-dashboard.js"; import { _resetHasChangesCache } from "./native-git-bridge.js"; @@ -266,7 +267,26 @@ export function detectRogueFileWrites(unitType, unitId, basePath) { ) .get({ ":pattern": `%${sid}-ASSESSMENT.md` }); if (!row) { - rogues.push({ path: assessPath, unitType, unitId }); + // Auto-remediate: ASSESSMENT exists on disk but no artifacts row. + // Parallel to execute-task / complete-slice patterns above. Without + // this, the reassess-roadmap unit can infinite-loop: the unit's job + // IS to write this file, but no completion path registers the row, + // so every successful iteration immediately gets flagged rogue and + // re-dispatched. Sync the DB to match the filesystem here (#rogue-loop). + try { + const fullContent = readFileSync(assessPath, "utf-8"); + insertArtifact({ + path: assessPath, + artifact_type: "ASSESSMENT", + milestone_id: mid, + slice_id: sid, + task_id: null, + full_content: fullContent, + }); + } catch { + // Insertion failure → fall back to rogue detection so the issue is visible. + rogues.push({ path: assessPath, unitType, unitId }); + } } } } else if (unitType === "plan-task") {