fix(engine): auto-remediate reassess-roadmap ASSESSMENT artifact (breaks loop)

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) <noreply@anthropic.com>
This commit is contained in:
Mikael Hugo 2026-05-16 19:35:44 +02:00
parent 7217a24c28
commit 7e882b56d0

View file

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