Merge pull request #2604 from gsd-build/refine/remove-dead-selfHeal

refactor: remove dead selfHealRuntimeRecords function
This commit is contained in:
TÂCHES 2026-03-25 22:48:32 -06:00 committed by GitHub
commit 03d47fd51a
2 changed files with 0 additions and 125 deletions

View file

@ -10,7 +10,6 @@
import type { ExtensionContext } from "@gsd/pi-coding-agent";
import { parseUnitId } from "./unit-id.js";
import { atomicWriteSync } from "./atomic-write.js";
import { clearUnitRuntimeRecord } from "./unit-runtime.js";
import { clearParseCache } from "./files.js";
import { parseRoadmap as parseLegacyRoadmap, parsePlan as parseLegacyPlan } from "./parsers-legacy.js";
import { isDbAvailable, getTask, getSlice, getSliceTasks } from "./gsd-db.js";
@ -623,50 +622,6 @@ export function reconcileMergeState(
return true;
}
// ─── Self-Heal Runtime Records ────────────────────────────────────────────────
/**
* Self-heal: scan runtime records in .gsd/ and clear stale ones.
* Clears dispatched records older than 1 hour (process crashed before
* completing the unit). deriveState() handles re-derivation no need
* for completion key persistence here.
*/
export async function selfHealRuntimeRecords(
base: string,
ctx: ExtensionContext,
): Promise<void> {
try {
const { listUnitRuntimeRecords } = await import("./unit-runtime.js");
const records = listUnitRuntimeRecords(base);
let healed = 0;
const STALE_THRESHOLD_MS = 60 * 60 * 1000; // 1 hour
const now = Date.now();
for (const record of records) {
const { unitType, unitId } = record;
// Case 0 removed — roadmap checkbox auto-fix is no longer needed.
// With DB-as-truth, stale checkboxes are fixed by repairStaleRenders().
// Clear stale dispatched records (dispatched > 1h ago, process crashed)
const age = now - (record.startedAt ?? 0);
if (record.phase === "dispatched" && age > STALE_THRESHOLD_MS) {
clearUnitRuntimeRecord(base, unitType, unitId);
healed++;
continue;
}
}
if (healed > 0) {
ctx.ui.notify(
`Self-heal: cleared ${healed} stale runtime record(s).`,
"info",
);
}
} catch (e) {
// Non-fatal — self-heal should never block auto-mode start
void e;
}
}
// ─── Loop Remediation ─────────────────────────────────────────────────────────
/**

View file

@ -10,7 +10,6 @@ import {
verifyExpectedArtifact,
diagnoseExpectedArtifact,
buildLoopRemediationSteps,
selfHealRuntimeRecords,
hasImplementationArtifacts,
} from "../auto-recovery.ts";
import { parseRoadmap, parsePlan } from "../parsers-legacy.ts";
@ -572,85 +571,6 @@ test("verifyExpectedArtifact plan-slice fails after deleting a rendered task pla
}
});
// ─── selfHealRuntimeRecords — worktree base path (#769) ──────────────────
test("selfHealRuntimeRecords clears stale dispatched records (#769)", async (t) => {
// selfHealRuntimeRecords now only clears stale dispatched records (>1h).
// No completedKeySet parameter — deriveState is sole authority.
const worktreeBase = makeTmpBase();
const mainBase = makeTmpBase();
t.after(() => {
cleanup(worktreeBase);
cleanup(mainBase);
});
const { writeUnitRuntimeRecord, readUnitRuntimeRecord } = await import("../unit-runtime.ts");
// Write a stale runtime record in the worktree .gsd/runtime/units/
writeUnitRuntimeRecord(worktreeBase, "run-uat", "M001/S01", Date.now() - 7200_000, {
phase: "dispatched",
});
// Verify the runtime record exists before heal
const before = readUnitRuntimeRecord(worktreeBase, "run-uat", "M001/S01");
assert.ok(before, "runtime record should exist before heal");
// Mock ExtensionContext with minimal notify
const notifications: string[] = [];
const mockCtx = {
ui: { notify: (msg: string) => { notifications.push(msg); } },
} as any;
// Call selfHeal with worktreeBase — should clear the stale record
await selfHealRuntimeRecords(worktreeBase, mockCtx);
// The stale record should be cleared
const after = readUnitRuntimeRecord(worktreeBase, "run-uat", "M001/S01");
assert.equal(after, null, "runtime record should be cleared after heal");
assert.ok(notifications.some(n => n.includes("Self-heal")), "should emit self-heal notification");
// Write a stale record at mainBase
writeUnitRuntimeRecord(mainBase, "run-uat", "M001/S01", Date.now() - 7200_000, {
phase: "dispatched",
});
await selfHealRuntimeRecords(mainBase, mockCtx);
// The record at mainBase should also be cleared by the stale timeout (>1h)
const afterMain = readUnitRuntimeRecord(mainBase, "run-uat", "M001/S01");
assert.equal(afterMain, null, "stale record at main base should be cleared by timeout");
});
// ─── #1625: selfHealRuntimeRecords on resume clears paused-session leftovers ──
test("selfHealRuntimeRecords clears recently-paused dispatched records on resume (#1625)", async (t) => {
// When pauseAuto closes out a unit but clearUnitRuntimeRecord silently fails
// (e.g. permission error), selfHealRuntimeRecords on resume should still
// clean up stale dispatched records that are >1h old.
const base = makeTmpBase();
t.after(() => cleanup(base));
const { writeUnitRuntimeRecord, readUnitRuntimeRecord } = await import("../unit-runtime.ts");
// Simulate a record left behind after a pause — aged >1h to be considered stale
writeUnitRuntimeRecord(base, "execute-task", "M001/S01/T01", Date.now() - 3700_000, {
phase: "dispatched",
});
const before = readUnitRuntimeRecord(base, "execute-task", "M001/S01/T01");
assert.ok(before, "dispatched record should exist before resume heal");
assert.equal(before!.phase, "dispatched");
const notifications: string[] = [];
const mockCtx = {
ui: { notify: (msg: string) => { notifications.push(msg); } },
} as any;
await selfHealRuntimeRecords(base, mockCtx);
const after = readUnitRuntimeRecord(base, "execute-task", "M001/S01/T01");
assert.equal(after, null, "stale dispatched record should be cleared on resume (#1625)");
});
// ─── #793: invalidateAllCaches unblocks skip-loop ─────────────────────────
// When the skip-loop breaker fires, it must call invalidateAllCaches() (not
// just invalidateStateCache()) to clear path/parse caches that deriveState