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") {