fix: reduce stale doctor warnings
This commit is contained in:
parent
e32d620cc5
commit
969b0f3295
5 changed files with 63 additions and 20 deletions
|
|
@ -1,7 +1,12 @@
|
|||
import { existsSync, readdirSync, renameSync, rmSync, statSync } from "node:fs";
|
||||
import { join } from "node:path";
|
||||
import { milestonesDir, resolveMilestoneFile } from "./paths.js";
|
||||
import { _getAdapter, getAllMilestones, isDbAvailable } from "./sf-db.js";
|
||||
import {
|
||||
_getAdapter,
|
||||
getAllMilestones,
|
||||
isDbAvailable,
|
||||
openDatabase,
|
||||
} from "./sf-db.js";
|
||||
import { deriveState } from "./state.js";
|
||||
import { readEvents } from "./workflow-events.js";
|
||||
import { renderAllProjections } from "./workflow-projections.js";
|
||||
|
|
@ -28,18 +33,17 @@ function normalizeLegacyDirectory(
|
|||
const sourcePath = join(parentDir, entry);
|
||||
const targetPath = join(parentDir, bareId);
|
||||
const conflict = existsSync(targetPath);
|
||||
if (conflict) return;
|
||||
issues.push({
|
||||
severity: conflict ? "warning" : "info",
|
||||
severity: "info",
|
||||
code: "legacy_plan_slug_directory",
|
||||
scope: "project",
|
||||
unitId: `${unitPrefix}/${entry}`,
|
||||
message: conflict
|
||||
? `Legacy plan directory ${sourcePath} should be renamed to ${targetPath}, but the target already exists. Merge manually before running doctor fix.`
|
||||
: `Legacy plan directory ${sourcePath} should be renamed to bare ID directory ${targetPath}.`,
|
||||
message: `Legacy plan directory ${sourcePath} should be renamed to bare ID directory ${targetPath}.`,
|
||||
file: sourcePath,
|
||||
fixable: !conflict,
|
||||
fixable: true,
|
||||
});
|
||||
if (conflict || !shouldFix?.("legacy_plan_slug_directory")) return;
|
||||
if (!shouldFix?.("legacy_plan_slug_directory")) return;
|
||||
try {
|
||||
renameSync(sourcePath, targetPath);
|
||||
fixesApplied.push(
|
||||
|
|
@ -105,6 +109,14 @@ export async function checkEngineHealth(
|
|||
shouldFix,
|
||||
) {
|
||||
const dbPath = join(basePath, ".sf", "sf.db");
|
||||
if (!isDbAvailable() && existsSync(dbPath)) {
|
||||
try {
|
||||
openDatabase(dbPath);
|
||||
} catch {
|
||||
// Report below. Doctor must not throw just because the engine DB is
|
||||
// locked, corrupt, or unavailable in this process.
|
||||
}
|
||||
}
|
||||
if (!isDbAvailable() && existsSync(dbPath)) {
|
||||
issues.push({
|
||||
severity: "warning",
|
||||
|
|
|
|||
|
|
@ -543,7 +543,7 @@ function checkGitRemote(basePath) {
|
|||
const remote = tryExec("git remote get-url origin", basePath);
|
||||
if (!remote) return null;
|
||||
// Quick connectivity check with short timeout
|
||||
const result = tryExec("git ls-remote --exit-code -h origin HEAD", basePath);
|
||||
const result = tryExec("git ls-remote --exit-code origin HEAD", basePath);
|
||||
if (result === null) {
|
||||
return {
|
||||
name: "git_remote",
|
||||
|
|
|
|||
|
|
@ -704,8 +704,6 @@ function formatBucketCountParts(counts) {
|
|||
parts.push(`${counts.upgradable} pending upgrade`);
|
||||
if (counts["editing-drift"] && counts["editing-drift"] > 0)
|
||||
parts.push(`${counts["editing-drift"]} editing-drift`);
|
||||
if (counts.untracked && counts.untracked > 0)
|
||||
parts.push(`${counts.untracked} untracked`);
|
||||
const pendingCount = (counts.missing ?? 0) + (counts.upgradable ?? 0);
|
||||
return { parts, pendingCount };
|
||||
}
|
||||
|
|
@ -727,10 +725,7 @@ export function checkScaffoldFreshness(basePath) {
|
|||
}
|
||||
const counts = report.countsByBucket;
|
||||
const actionable =
|
||||
counts.missing +
|
||||
counts.upgradable +
|
||||
counts["editing-drift"] +
|
||||
counts.untracked;
|
||||
counts.missing + counts.upgradable + counts["editing-drift"];
|
||||
if (actionable === 0) return null;
|
||||
const { parts, pendingCount } = formatBucketCountParts(counts);
|
||||
const summary = parts.join(", ");
|
||||
|
|
|
|||
|
|
@ -9,7 +9,11 @@ import {
|
|||
import { tmpdir } from "node:os";
|
||||
import { dirname, join } from "node:path";
|
||||
import { afterEach, test } from "vitest";
|
||||
import { ensureAgenticDocsScaffold } from "../agentic-docs-scaffold.js";
|
||||
import {
|
||||
ensureAgenticDocsScaffold,
|
||||
SCAFFOLD_FILES,
|
||||
} from "../agentic-docs-scaffold.js";
|
||||
import { checkScaffoldFreshness } from "../doctor-runtime-checks.js";
|
||||
import { detectScaffoldDrift } from "../scaffold-drift.js";
|
||||
import { extractMarker, stampScaffoldFile } from "../scaffold-versioning.js";
|
||||
|
||||
|
|
@ -131,3 +135,14 @@ All lines should start with \`OK:\` for the bootstrap spec to pass.
|
|||
assert.equal(existsSync(join(root, "harness")), false);
|
||||
assert.equal(existsSync(join(root, ".sf/harness/specs/bootstrap.md")), true);
|
||||
});
|
||||
|
||||
test("checkScaffoldFreshness_when_only_markerless_scaffold_exists_does_not_warn", () => {
|
||||
const root = makeProject();
|
||||
for (const file of SCAFFOLD_FILES) {
|
||||
const target = join(root, file.path);
|
||||
mkdirSync(dirname(target), { recursive: true });
|
||||
writeFileSync(target, file.content, "utf-8");
|
||||
}
|
||||
|
||||
assert.equal(checkScaffoldFreshness(root), null);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -9,7 +9,11 @@ import {
|
|||
import { tmpdir } from "node:os";
|
||||
import { join } from "node:path";
|
||||
import { afterEach, describe, test } from "vitest";
|
||||
import { normalizeLegacyPlanSlugDirectories } from "../doctor-engine-checks.js";
|
||||
import {
|
||||
checkEngineHealth,
|
||||
normalizeLegacyPlanSlugDirectories,
|
||||
} from "../doctor-engine-checks.js";
|
||||
import { closeDatabase, openDatabase } from "../sf-db.js";
|
||||
|
||||
const tmpDirs = [];
|
||||
|
||||
|
|
@ -21,6 +25,7 @@ function makeProject() {
|
|||
}
|
||||
|
||||
afterEach(() => {
|
||||
closeDatabase();
|
||||
while (tmpDirs.length > 0) {
|
||||
const dir = tmpDirs.pop();
|
||||
if (dir) rmSync(dir, { recursive: true, force: true });
|
||||
|
|
@ -96,15 +101,31 @@ describe("doctor plan directory normalization", () => {
|
|||
(code) => code === "legacy_plan_slug_directory",
|
||||
);
|
||||
|
||||
const issue = issues.find(
|
||||
(candidate) => candidate.code === "legacy_plan_slug_directory",
|
||||
assert.equal(
|
||||
issues.some(
|
||||
(candidate) => candidate.code === "legacy_plan_slug_directory",
|
||||
),
|
||||
false,
|
||||
);
|
||||
assert.equal(issue?.fixable, false);
|
||||
assert.match(issue?.message ?? "", /target already exists/);
|
||||
assert.equal(
|
||||
existsSync(join(project, ".sf", "milestones", "M001-long-name")),
|
||||
true,
|
||||
);
|
||||
assert.deepEqual(fixesApplied, []);
|
||||
});
|
||||
|
||||
test("checkEngineHealth_when_db_file_can_open_does_not_report_unavailable", async () => {
|
||||
const project = makeProject();
|
||||
const dbPath = join(project, ".sf", "sf.db");
|
||||
assert.equal(openDatabase(dbPath), true);
|
||||
closeDatabase();
|
||||
const issues = [];
|
||||
|
||||
await checkEngineHealth(project, issues, [], () => false);
|
||||
|
||||
assert.equal(
|
||||
issues.some((issue) => issue.code === "db_unavailable"),
|
||||
false,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue