From f76fe8ec1ebf0f0b3b291de8053093669819d474 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?T=C3=82CHES?= Date: Mon, 23 Mar 2026 13:09:37 -0600 Subject: [PATCH] =?UTF-8?q?feat(S06/T02):=20Strip=20all=2016=20lazy=20crea?= =?UTF-8?q?teRequire=20fallback=20paths=20from=20migr=E2=80=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - src/resources/extensions/gsd/dispatch-guard.ts - src/resources/extensions/gsd/auto-dispatch.ts - src/resources/extensions/gsd/auto-verification.ts - src/resources/extensions/gsd/parallel-eligibility.ts - src/resources/extensions/gsd/doctor.ts - src/resources/extensions/gsd/doctor-checks.ts - src/resources/extensions/gsd/visualizer-data.ts - src/resources/extensions/gsd/workspace-index.ts --- .gsd/milestones/M001/slices/S06/S06-PLAN.md | 2 +- .../extensions/gsd/auto-dashboard.ts | 28 +--- .../extensions/gsd/auto-direct-dispatch.ts | 30 +--- src/resources/extensions/gsd/auto-dispatch.ts | 38 +---- src/resources/extensions/gsd/auto-prompts.ts | 130 ++---------------- src/resources/extensions/gsd/auto-recovery.ts | 18 +-- .../extensions/gsd/auto-verification.ts | 20 +-- src/resources/extensions/gsd/auto-worktree.ts | 9 +- .../extensions/gsd/dashboard-overlay.ts | 36 +---- .../extensions/gsd/dispatch-guard.ts | 66 ++------- src/resources/extensions/gsd/doctor-checks.ts | 17 +-- src/resources/extensions/gsd/doctor.ts | 27 +--- src/resources/extensions/gsd/guided-flow.ts | 16 +-- .../extensions/gsd/parallel-eligibility.ts | 36 +---- .../extensions/gsd/reactive-graph.ts | 12 +- .../extensions/gsd/visualizer-data.ts | 33 +---- .../extensions/gsd/workspace-index.ts | 50 ++----- 17 files changed, 67 insertions(+), 501 deletions(-) diff --git a/.gsd/milestones/M001/slices/S06/S06-PLAN.md b/.gsd/milestones/M001/slices/S06/S06-PLAN.md index 9d6d939d5..109202b87 100644 --- a/.gsd/milestones/M001/slices/S06/S06-PLAN.md +++ b/.gsd/milestones/M001/slices/S06/S06-PLAN.md @@ -85,7 +85,7 @@ node --import ./src/resources/extensions/gsd/tests/resolve-ts.mjs --experimental - Verify: `node --import ./src/resources/extensions/gsd/tests/resolve-ts.mjs --experimental-strip-types --test src/resources/extensions/gsd/tests/parsers.test.ts src/resources/extensions/gsd/tests/roadmap-slices.test.ts src/resources/extensions/gsd/tests/planning-crossval.test.ts src/resources/extensions/gsd/tests/markdown-renderer.test.ts src/resources/extensions/gsd/tests/auto-recovery.test.ts src/resources/extensions/gsd/tests/migrate-writer.test.ts src/resources/extensions/gsd/tests/migrate-writer-integration.test.ts src/resources/extensions/gsd/tests/complete-milestone.test.ts` — all pass - Done when: `parseRoadmap` and `parsePlan` no longer exported from `files.ts`, all consumers import from `parsers-legacy.ts`, all parser/crossval/renderer tests pass -- [ ] **T02: Strip all 16 lazy createRequire fallback paths from migrated callers** `est:35m` +- [x] **T02: Strip all 16 lazy createRequire fallback paths from migrated callers** `est:35m` - Why: With parsers relocated, the lazy fallback singletons in all 16 migrated callers are dead code — they imported from `files.ts` which no longer exports parsers. Strip them to complete the parser deprecation. - Files: `src/resources/extensions/gsd/dispatch-guard.ts`, `src/resources/extensions/gsd/auto-dispatch.ts`, `src/resources/extensions/gsd/auto-verification.ts`, `src/resources/extensions/gsd/parallel-eligibility.ts`, `src/resources/extensions/gsd/doctor.ts`, `src/resources/extensions/gsd/doctor-checks.ts`, `src/resources/extensions/gsd/visualizer-data.ts`, `src/resources/extensions/gsd/workspace-index.ts`, `src/resources/extensions/gsd/dashboard-overlay.ts`, `src/resources/extensions/gsd/auto-dashboard.ts`, `src/resources/extensions/gsd/guided-flow.ts`, `src/resources/extensions/gsd/auto-prompts.ts`, `src/resources/extensions/gsd/auto-recovery.ts`, `src/resources/extensions/gsd/auto-direct-dispatch.ts`, `src/resources/extensions/gsd/auto-worktree.ts`, `src/resources/extensions/gsd/reactive-graph.ts` - Do: For each of the 16 files: (1) remove `import { createRequire } from "node:module"`, (2) remove the lazy parser singleton declaration and function, (3) replace `if (isDbAvailable()) { ...DB path... } else { ...parser fallback... }` with just the DB path body — when DB unavailable, return early with empty/null/skip. Special cases: `workspace-index.ts` `titleFromRoadmapHeader` was parser-only with no DB equivalent — remove it or return null when DB unavailable. `auto-prompts.ts` has async `lazyParseRoadmap`/`lazyParsePlan` helpers wrapping 6 call sites — remove the helpers entirely and inline the DB-only path. `auto-recovery.ts` has `import { createRequire }` at top and 2 inline `createRequire` usages — remove all. Remove `import { createRequire }` from files that imported it only for parser fallback (check if any remaining non-parser `createRequire` usage exists before removing). diff --git a/src/resources/extensions/gsd/auto-dashboard.ts b/src/resources/extensions/gsd/auto-dashboard.ts index 4cb7fb712..4db561cd5 100644 --- a/src/resources/extensions/gsd/auto-dashboard.ts +++ b/src/resources/extensions/gsd/auto-dashboard.ts @@ -26,18 +26,6 @@ import { getActiveWorktreeName } from "./worktree-command.js"; import { loadEffectiveGSDPreferences, getGlobalGSDPreferencesPath } from "./preferences.js"; import { resolveServiceTierIcon, getEffectiveServiceTier } from "./service-tier.js"; -// Lazy-loaded parsers — only resolved when DB is unavailable (fallback path) -import { createRequire } from "node:module"; -let _lazyParsers: { parseRoadmap: (c: string) => { slices: Array<{ id: string; done: boolean; title: string }> }; parsePlan: (c: string) => { tasks: Array<{ id: string; done: boolean; title: string }> } } | null = null; -function getLazyParsers() { - if (!_lazyParsers) { - const req = createRequire(import.meta.url); - try { const mod = req("./files.ts"); _lazyParsers = { parseRoadmap: mod.parseRoadmap, parsePlan: mod.parsePlan }; } - catch { const mod = req("./files.js"); _lazyParsers = { parseRoadmap: mod.parseRoadmap, parsePlan: mod.parsePlan }; } - } - return _lazyParsers!; -} - // ─── UAT Slice Extraction ───────────────────────────────────────────────────── /** @@ -266,10 +254,7 @@ export function updateSliceProgressCache(base: string, mid: string, activeSid?: if (isDbAvailable()) { normSlices = getMilestoneSlices(mid).map(s => ({ id: s.id, done: s.status === "complete", title: s.title })); } else { - const roadmapFile = resolveMilestoneFile(base, mid, "ROADMAP"); - if (!roadmapFile) return; - const content = readFileSync(roadmapFile, "utf-8"); - normSlices = getLazyParsers().parseRoadmap(content).slices; + normSlices = []; } let activeSliceTasks: { done: number; total: number } | null = null; @@ -285,17 +270,6 @@ export function updateSliceProgressCache(base: string, mid: string, activeSid?: }; taskDetails = dbTasks.map(t => ({ id: t.id, title: t.title, done: t.status === "complete" || t.status === "done" })); } - } else { - const planFile = resolveSliceFile(base, mid, activeSid, "PLAN"); - if (planFile && existsSync(planFile)) { - const planContent = readFileSync(planFile, "utf-8"); - const plan = getLazyParsers().parsePlan(planContent); - activeSliceTasks = { - done: plan.tasks.filter(t => t.done).length, - total: plan.tasks.length, - }; - taskDetails = plan.tasks.map(t => ({ id: t.id, title: t.title, done: t.done })); - } } } catch { // Non-fatal — just omit task count diff --git a/src/resources/extensions/gsd/auto-direct-dispatch.ts b/src/resources/extensions/gsd/auto-direct-dispatch.ts index 358edaf73..bddd5801c 100644 --- a/src/resources/extensions/gsd/auto-direct-dispatch.ts +++ b/src/resources/extensions/gsd/auto-direct-dispatch.ts @@ -157,19 +157,8 @@ export async function dispatchDirectPhase( if (isDbAvailable()) { completedSliceIds = getMilestoneSlices(mid).filter(s => s.status === "complete").map(s => s.id); } else { - const roadmapFile = resolveMilestoneFile(base, mid, "ROADMAP"); - const roadmapContent = roadmapFile ? await loadFile(roadmapFile) : null; - if (!roadmapContent) { - ctx.ui.notify("Cannot dispatch reassess-roadmap: no roadmap found.", "warning"); - return; - } - const { createRequire } = await import("node:module"); - const _require = createRequire(import.meta.url); - let parseRoadmap: Function; - try { parseRoadmap = _require("./files.ts").parseRoadmap; } - catch { parseRoadmap = _require("./files.js").parseRoadmap; } - const roadmap = parseRoadmap(roadmapContent); - completedSliceIds = roadmap.slices.filter((s: { done: boolean }) => s.done).map((s: { id: string }) => s.id); + ctx.ui.notify("Cannot dispatch reassess-roadmap: DB unavailable.", "warning"); + return; } if (completedSliceIds.length === 0) { ctx.ui.notify("Cannot dispatch reassess-roadmap: no completed slices.", "warning"); @@ -192,19 +181,8 @@ export async function dispatchDirectPhase( if (isDbAvailable()) { uatCompletedSliceIds = getMilestoneSlices(mid).filter(s => s.status === "complete").map(s => s.id); } else { - const roadmapFile = resolveMilestoneFile(base, mid, "ROADMAP"); - const roadmapContent = roadmapFile ? await loadFile(roadmapFile) : null; - if (!roadmapContent) { - ctx.ui.notify("Cannot dispatch run-uat: no roadmap found.", "warning"); - return; - } - const { createRequire } = await import("node:module"); - const _require = createRequire(import.meta.url); - let parseRoadmap: Function; - try { parseRoadmap = _require("./files.ts").parseRoadmap; } - catch { parseRoadmap = _require("./files.js").parseRoadmap; } - const roadmap = parseRoadmap(roadmapContent); - uatCompletedSliceIds = roadmap.slices.filter((s: { done: boolean }) => s.done).map((s: { id: string }) => s.id); + ctx.ui.notify("Cannot dispatch run-uat: DB unavailable.", "warning"); + return; } if (uatCompletedSliceIds.length === 0) { ctx.ui.notify("Cannot dispatch run-uat: no completed slices.", "warning"); diff --git a/src/resources/extensions/gsd/auto-dispatch.ts b/src/resources/extensions/gsd/auto-dispatch.ts index 179d3ae5d..f71fd71ad 100644 --- a/src/resources/extensions/gsd/auto-dispatch.ts +++ b/src/resources/extensions/gsd/auto-dispatch.ts @@ -14,21 +14,7 @@ import type { GSDPreferences } from "./preferences.js"; import type { UatType } from "./files.js"; import { loadFile, extractUatType, loadActiveOverrides } from "./files.js"; import { isDbAvailable, getMilestoneSlices } from "./gsd-db.js"; -import { createRequire } from "node:module"; -// Lazy-loaded parseRoadmap — only resolved when DB is unavailable (fallback path). -let _lazyParseRoadmap: ((content: string) => { slices: { id: string; done: boolean }[] }) | null = null; -function lazyParseRoadmap(content: string) { - if (!_lazyParseRoadmap) { - const req = createRequire(import.meta.url); - try { - _lazyParseRoadmap = req("./files.ts").parseRoadmap; - } catch { - _lazyParseRoadmap = req("./files.js").parseRoadmap; - } - } - return _lazyParseRoadmap!(content); -} import { resolveMilestoneFile, resolveMilestonePath, @@ -194,11 +180,7 @@ export const DISPATCH_RULES: DispatchRule[] = [ .filter(s => s.status === "complete") .map(s => s.id); } else { - // Disk fallback - const roadmapContent = roadmapFile ? await loadFile(roadmapFile) : null; - if (!roadmapContent) return null; - const roadmap = lazyParseRoadmap(roadmapContent); - completedSliceIds = roadmap.slices.filter(s => s.done).map(s => s.id); + return null; } for (const sliceId of completedSliceIds) { @@ -532,14 +514,7 @@ export const DISPATCH_RULES: DispatchRule[] = [ if (isDbAvailable()) { sliceIds = getMilestoneSlices(mid).map(s => s.id); } else { - const roadmapFile = resolveMilestoneFile(basePath, mid, "ROADMAP"); - const roadmapContent = roadmapFile ? await loadFile(roadmapFile) : null; - if (roadmapContent) { - const roadmap = lazyParseRoadmap(roadmapContent); - sliceIds = roadmap.slices.map(s => s.id); - } else { - sliceIds = []; - } + sliceIds = []; } if (sliceIds.length > 0) { @@ -600,14 +575,7 @@ export const DISPATCH_RULES: DispatchRule[] = [ if (isDbAvailable()) { sliceIds = getMilestoneSlices(mid).map(s => s.id); } else { - const roadmapFile = resolveMilestoneFile(basePath, mid, "ROADMAP"); - const roadmapContent = roadmapFile ? await loadFile(roadmapFile) : null; - if (roadmapContent) { - const roadmap = lazyParseRoadmap(roadmapContent); - sliceIds = roadmap.slices.map(s => s.id); - } else { - sliceIds = []; - } + sliceIds = []; } if (sliceIds.length > 0) { diff --git a/src/resources/extensions/gsd/auto-prompts.ts b/src/resources/extensions/gsd/auto-prompts.ts index 25778e84f..d8a64e218 100644 --- a/src/resources/extensions/gsd/auto-prompts.ts +++ b/src/resources/extensions/gsd/auto-prompts.ts @@ -28,27 +28,6 @@ import { formatDecisionsCompact, formatRequirementsCompact } from "./structured- const MAX_PREAMBLE_CHARS = 30_000; -// ─── Lazy parser helpers ────────────────────────────────────────────────────── -// Centralize createRequire fallback for callers that need parser as a last resort. -async function lazyParseRoadmap(content: string) { - const { createRequire } = await import("node:module"); - const _require = createRequire(import.meta.url); - let parseRoadmap: Function; - try { parseRoadmap = _require("./files.ts").parseRoadmap; } - catch { parseRoadmap = _require("./files.js").parseRoadmap; } - return parseRoadmap(content) as { slices: { id: string; done: boolean; depends: string[] }[] }; -} - -async function lazyParsePlan(content: string) { - const { createRequire } = await import("node:module"); - const _require = createRequire(import.meta.url); - let parsePlan: Function; - try { parsePlan = _require("./files.ts").parsePlan; } - catch { parsePlan = _require("./files.js").parsePlan; } - return parsePlan(content) as { tasks: { id: string; title: string; done: boolean; files: string[] }[]; filesLikelyTouched: string[] }; -} -// ────────────────────────────────────────────────────────────────────────────── - function capPreamble(preamble: string): string { if (preamble.length <= MAX_PREAMBLE_CHARS) return preamble; return truncateAtSectionBoundary(preamble, MAX_PREAMBLE_CHARS).content; @@ -207,17 +186,11 @@ export async function inlineDependencySummaries( if (!slice || slice.depends.length === 0) return "- (no dependencies)"; depends = slice.depends as string[]; } - } catch { /* fall through to parser */ } + } catch { /* fall through */ } - // Parser fallback — load roadmap and parse for depends + // If DB didn't provide depends, we can't determine them without parsers if (!depends) { - const roadmapFile = resolveMilestoneFile(base, mid, "ROADMAP"); - const roadmapContent = roadmapFile ? await loadFile(roadmapFile) : null; - if (!roadmapContent) return "- (no dependencies)"; - const roadmap = await lazyParseRoadmap(roadmapContent); - const sliceEntry = roadmap.slices.find(s => s.id === sid); - if (!sliceEntry || sliceEntry.depends.length === 0) return "- (no dependencies)"; - depends = sliceEntry.depends; + return "- (no dependencies)"; } const sections: string[] = []; @@ -738,34 +711,10 @@ export async function checkNeedsReassessment( if (!hasSummary) return null; return { sliceId: lastCompleted }; } - } catch { /* fall through to parser */ } + } catch { /* fall through */ } - // Parser fallback - const roadmapFile = resolveMilestoneFile(base, mid, "ROADMAP"); - const roadmapContent = roadmapFile ? await loadFile(roadmapFile) : null; - if (!roadmapContent) return null; - - const roadmap = await lazyParseRoadmap(roadmapContent); - const completedSlices = roadmap.slices.filter(s => s.done); - const incompleteSlices = roadmap.slices.filter(s => !s.done); - - // No completed slices or all slices done — skip - if (completedSlices.length === 0 || incompleteSlices.length === 0) return null; - - // Check the last completed slice - const lastCompleted = completedSlices[completedSlices.length - 1]; - const assessmentFile = resolveSliceFile(base, mid, lastCompleted.id, "ASSESSMENT"); - const hasAssessment = !!(assessmentFile && await loadFile(assessmentFile)); - - if (hasAssessment) return null; - - // Also need a summary to reassess against - const summaryFile = resolveSliceFile(base, mid, lastCompleted.id, "SUMMARY"); - const hasSummary = !!(summaryFile && await loadFile(summaryFile)); - - if (!hasSummary) return null; - - return { sliceId: lastCompleted.id }; + // DB unavailable — cannot determine assessment needs + return null; } /** @@ -806,47 +755,10 @@ export async function checkNeedsRunUat( const uatType = extractUatType(uatContent) ?? "artifact-driven"; return { sliceId: sid, uatType }; } - } catch { /* fall through to parser */ } + } catch { /* fall through */ } - // Parser fallback - const roadmapFile = resolveMilestoneFile(base, mid, "ROADMAP"); - const roadmapContent = roadmapFile ? await loadFile(roadmapFile) : null; - if (!roadmapContent) return null; - - const roadmap = await lazyParseRoadmap(roadmapContent); - const completedSlices = roadmap.slices.filter(s => s.done); - const incompleteSlices = roadmap.slices.filter(s => !s.done); - - // No completed slices — nothing to UAT yet - if (completedSlices.length === 0) return null; - - // All slices done — milestone complete path, skip (reassessment handles) - if (incompleteSlices.length === 0) return null; - - // uat_dispatch must be opted in - if (!prefs?.uat_dispatch) return null; - - // Take the last completed slice - const lastCompleted = completedSlices[completedSlices.length - 1]; - const sid = lastCompleted.id; - - // UAT file must exist - const uatFile = resolveSliceFile(base, mid, sid, "UAT"); - if (!uatFile) return null; - const uatContent = await loadFile(uatFile); - if (!uatContent) return null; - - // If UAT result already exists, skip (idempotent) - const uatResultFile = resolveSliceFile(base, mid, sid, "UAT-RESULT"); - if (uatResultFile) { - const hasResult = !!(await loadFile(uatResultFile)); - if (hasResult) return null; - } - - // Classify UAT type; default to artifact-driven (LLM-executed UATs are always artifact-driven) - const uatType = extractUatType(uatContent) ?? "artifact-driven"; - - return { sliceId: sid, uatType }; + // DB unavailable — cannot determine UAT needs + return null; } // ─── Prompt Builders ────────────────────────────────────────────────────── @@ -1307,13 +1219,7 @@ export async function buildCompleteMilestonePrompt( sliceIds = getMilestoneSlices(mid).map(s => s.id); } } catch { /* fall through */ } - if (sliceIds.length === 0) { - const roadmapContent = roadmapPath ? await loadFile(roadmapPath) : null; - if (roadmapContent) { - const roadmap = await lazyParseRoadmap(roadmapContent); - sliceIds = roadmap.slices.map(s => s.id); - } - } + // If DB didn't provide slice IDs, sliceIds stays empty — no summaries to inline const seenSlices = new Set(); for (const sid of sliceIds) { if (seenSlices.has(sid)) continue; @@ -1373,13 +1279,7 @@ export async function buildValidateMilestonePrompt( valSliceIds = getMilestoneSlices(mid).map(s => s.id); } } catch { /* fall through */ } - if (valSliceIds.length === 0) { - const roadmapContent = roadmapPath ? await loadFile(roadmapPath) : null; - if (roadmapContent) { - const roadmap = await lazyParseRoadmap(roadmapContent); - valSliceIds = roadmap.slices.map(s => s.id); - } - } + // If DB didn't provide slice IDs, valSliceIds stays empty const seenValSlices = new Set(); for (const sid of valSliceIds) { if (seenValSlices.has(sid)) continue; @@ -1714,12 +1614,8 @@ export async function buildRewriteDocsPrompt( } catch { /* fall through */ } if (!incompleteTasks) { - // Parser fallback - const planContent = await loadFile(slicePlanPath); - if (planContent) { - const plan = await lazyParsePlan(planContent); - incompleteTasks = plan.tasks.filter(t => !t.done).map(t => ({ id: t.id })); - } + // DB unavailable — no task data to inline + incompleteTasks = []; } if (incompleteTasks) { diff --git a/src/resources/extensions/gsd/auto-recovery.ts b/src/resources/extensions/gsd/auto-recovery.ts index de5fd6c65..81600cf86 100644 --- a/src/resources/extensions/gsd/auto-recovery.ts +++ b/src/resources/extensions/gsd/auto-recovery.ts @@ -10,9 +10,9 @@ import type { ExtensionContext } from "@gsd/pi-coding-agent"; import { parseUnitId } from "./unit-id.js"; import { atomicWriteSync } from "./atomic-write.js"; -import { createRequire } from "node:module"; 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"; import { isValidationTerminal } from "./state.js"; import { @@ -375,13 +375,9 @@ export function verifyExpectedArtifact( } if (!taskIds) { - // Parser fallback + // DB unavailable or no tasks in DB — parse plan file for task IDs const planContent = readFileSync(absPath, "utf-8"); - const _require = createRequire(import.meta.url); - let parsePlan: Function; - try { parsePlan = _require("./parsers-legacy.ts").parsePlan; } - catch { parsePlan = _require("./parsers-legacy.js").parsePlan; } - const plan = parsePlan(planContent); + const plan = parseLegacyPlan(planContent); if (plan.tasks.length > 0) taskIds = plan.tasks.map((t: { id: string }) => t.id); } @@ -418,16 +414,12 @@ export function verifyExpectedArtifact( // DB available — trust it if (dbSlice.status !== "complete") return false; } else if (!isDbAvailable()) { - // DB unavailable — fall back to roadmap checkbox check + // DB unavailable — fall back to roadmap checkbox check via parsers-legacy const roadmapFile = resolveMilestoneFile(base, mid, "ROADMAP"); if (roadmapFile && existsSync(roadmapFile)) { try { const roadmapContent = readFileSync(roadmapFile, "utf-8"); - const _require = createRequire(import.meta.url); - let parseRoadmap: Function; - try { parseRoadmap = _require("./parsers-legacy.ts").parseRoadmap; } - catch { parseRoadmap = _require("./parsers-legacy.js").parseRoadmap; } - const roadmap = parseRoadmap(roadmapContent); + const roadmap = parseLegacyRoadmap(roadmapContent); const slice = roadmap.slices.find((s) => s.id === sid); if (slice && !slice.done) return false; } catch { diff --git a/src/resources/extensions/gsd/auto-verification.ts b/src/resources/extensions/gsd/auto-verification.ts index 758bcd9d1..8a0c6ca55 100644 --- a/src/resources/extensions/gsd/auto-verification.ts +++ b/src/resources/extensions/gsd/auto-verification.ts @@ -13,7 +13,6 @@ import type { ExtensionContext, ExtensionAPI } from "@gsd/pi-coding-agent"; import { resolveSliceFile, resolveSlicePath } from "./paths.js"; import { isDbAvailable, getTask } from "./gsd-db.js"; -import { createRequire } from "node:module"; import { loadEffectiveGSDPreferences } from "./preferences.js"; import { runVerificationGate, @@ -67,25 +66,8 @@ export async function runPostUnitVerification( const [mid, sid, tid] = parts; if (isDbAvailable()) { taskPlanVerify = getTask(mid, sid, tid)?.verify; - } else { - // Disk fallback: lazy-load parsePlan + loadFile - const planFile = resolveSliceFile(s.basePath, mid, sid, "PLAN"); - if (planFile) { - const req = createRequire(import.meta.url); - let filesModule: { loadFile: (p: string) => Promise; parsePlan: (c: string) => { tasks?: { id: string; verify?: string }[] } }; - try { - filesModule = req("./files.ts"); - } catch { - filesModule = req("./files.js"); - } - const planContent = await filesModule.loadFile(planFile); - if (planContent) { - const slicePlan = filesModule.parsePlan(planContent); - const taskEntry = slicePlan?.tasks?.find((t) => t.id === tid); - taskPlanVerify = taskEntry?.verify; - } - } } + // When DB unavailable, taskPlanVerify stays undefined — gate runs without task-specific checks } const result = runVerificationGate({ diff --git a/src/resources/extensions/gsd/auto-worktree.ts b/src/resources/extensions/gsd/auto-worktree.ts index 6abc37a2c..930444604 100644 --- a/src/resources/extensions/gsd/auto-worktree.ts +++ b/src/resources/extensions/gsd/auto-worktree.ts @@ -18,7 +18,6 @@ import { lstatSync as lstatSyncFn, } from "node:fs"; import { isAbsolute, join } from "node:path"; -import { createRequire } from "node:module"; import { GSDError, GSD_IO_ERROR, GSD_GIT_ERROR } from "./errors.js"; import { reconcileWorktreeDb, @@ -1005,14 +1004,8 @@ export function mergeMilestoneToMain( completedSlices = getMilestoneSlices(milestoneId) .filter(s => s.status === "complete") .map(s => ({ id: s.id, title: s.title })); - } else { - const _require = createRequire(import.meta.url); - let parseRoadmap: Function; - try { parseRoadmap = _require("./files.ts").parseRoadmap; } - catch { parseRoadmap = _require("./files.js").parseRoadmap; } - const roadmap = parseRoadmap(roadmapContent); - completedSlices = roadmap.slices.filter((s: { done: boolean }) => s.done).map((s: { id: string; title: string }) => ({ id: s.id, title: s.title })); } + // When DB unavailable, completedSlices stays empty — commit message will omit slice details // 3. chdir to original base const previousCwd = process.cwd(); diff --git a/src/resources/extensions/gsd/dashboard-overlay.ts b/src/resources/extensions/gsd/dashboard-overlay.ts index 94e8922fe..ed0e69a51 100644 --- a/src/resources/extensions/gsd/dashboard-overlay.ts +++ b/src/resources/extensions/gsd/dashboard-overlay.ts @@ -27,18 +27,6 @@ import { estimateTimeRemaining } from "./auto-dashboard.js"; import { computeProgressScore, formatProgressLine } from "./progress-score.js"; import { runEnvironmentChecks, type EnvironmentCheckResult } from "./doctor-environment.js"; -// Lazy-loaded parsers — only resolved when DB is unavailable (fallback path) -import { createRequire } from "node:module"; -let _lazyParsers: { parseRoadmap: (c: string) => { slices: Array<{ id: string; done: boolean; title: string; risk: string }> }; parsePlan: (c: string) => { tasks: Array<{ id: string; done: boolean; title: string }> } } | null = null; -function getLazyParsers() { - if (!_lazyParsers) { - const req = createRequire(import.meta.url); - try { const mod = req("./files.ts"); _lazyParsers = { parseRoadmap: mod.parseRoadmap, parsePlan: mod.parsePlan }; } - catch { const mod = req("./files.js"); _lazyParsers = { parseRoadmap: mod.parseRoadmap, parsePlan: mod.parsePlan }; } - } - return _lazyParsers!; -} - function unitLabel(type: string): string { switch (type) { case "research-milestone": return "Research"; @@ -172,13 +160,11 @@ export class GSDDashboardOverlay { const roadmapFile = resolveMilestoneFile(base, mid, "ROADMAP"); const roadmapContent = roadmapFile ? await loadFile(roadmapFile) : null; - // Normalize slices: prefer DB, fall back to parser + // Normalize slices from DB type NormSlice = { id: string; done: boolean; title: string; risk: string }; let normSlices: NormSlice[] = []; if (isDbAvailable()) { normSlices = getMilestoneSlices(mid).map(s => ({ id: s.id, done: s.status === "complete", title: s.title, risk: s.risk || "medium" })); - } else if (roadmapContent) { - normSlices = getLazyParsers().parseRoadmap(roadmapContent).slices; } for (const s of normSlices) { @@ -192,7 +178,7 @@ export class GSDDashboardOverlay { }; if (sliceView.active) { - // Normalize tasks: prefer DB, fall back to parser + // Normalize tasks from DB if (isDbAvailable()) { const dbTasks = getSliceTasks(mid, s.id); sliceView.taskProgress = { @@ -207,24 +193,6 @@ export class GSDDashboardOverlay { active: state.activeTask?.id === t.id, }); } - } else { - const planFile = resolveSliceFile(base, mid, s.id, "PLAN"); - const planContent = planFile ? await loadFile(planFile) : null; - if (planContent) { - const plan = getLazyParsers().parsePlan(planContent); - sliceView.taskProgress = { - done: plan.tasks.filter(t => t.done).length, - total: plan.tasks.length, - }; - for (const t of plan.tasks) { - sliceView.tasks.push({ - id: t.id, - title: t.title, - done: t.done, - active: state.activeTask?.id === t.id, - }); - } - } } } diff --git a/src/resources/extensions/gsd/dispatch-guard.ts b/src/resources/extensions/gsd/dispatch-guard.ts index acc7c7783..78a061185 100644 --- a/src/resources/extensions/gsd/dispatch-guard.ts +++ b/src/resources/extensions/gsd/dispatch-guard.ts @@ -1,27 +1,9 @@ // GSD Dispatch Guard — prevents out-of-order slice dispatch -import { readFileSync } from "node:fs"; -import { createRequire } from "node:module"; import { resolveMilestoneFile } from "./paths.js"; import { findMilestoneIds } from "./guided-flow.js"; import { isDbAvailable, getMilestoneSlices } from "./gsd-db.js"; -// Lazy-loaded parser — only resolved when DB is unavailable (fallback path). -// Uses createRequire so the function stays synchronous. Tries .ts first (strip-types dev) -// then .js (compiled production). -let _lazyParser: ((content: string) => { id: string; done: boolean; depends: string[] }[]) | null = null; -function lazyParseRoadmapSlices(content: string) { - if (!_lazyParser) { - const req = createRequire(import.meta.url); - try { - _lazyParser = req("./roadmap-slices.ts").parseRoadmapSlices; - } catch { - _lazyParser = req("./roadmap-slices.js").parseRoadmapSlices; - } - } - return _lazyParser!(content); -} - const SLICE_DISPATCH_TYPES = new Set([ "research-slice", "plan-slice", @@ -30,28 +12,6 @@ const SLICE_DISPATCH_TYPES = new Set([ "complete-slice", ]); -/** - * Read a roadmap file from disk (working tree) rather than from a git branch. - * - * Prior implementation used `git show :` which read committed - * state on a specific branch. This caused false-positive blockers when work - * was committed on a milestone/worktree branch but the integration branch - * (main) hadn't been updated yet — the guard would see prior slices as - * incomplete on main even though they were done in the working tree (#530). - * - * Reading from disk always reflects the latest state, regardless of which - * branch is checked out or whether changes have been committed. - */ -function readRoadmapFromDisk(base: string, milestoneId: string): string | null { - try { - const absPath = resolveMilestoneFile(base, milestoneId, "ROADMAP"); - if (!absPath) return null; - return readFileSync(absPath, "utf-8").trim(); - } catch { - return null; - } -} - export function getPriorSliceCompletionBlocker( base: string, _mainBranch: string, @@ -74,24 +34,18 @@ export function getPriorSliceCompletionBlocker( if (resolveMilestoneFile(base, mid, "PARKED")) continue; if (resolveMilestoneFile(base, mid, "SUMMARY")) continue; - // Normalised slice list: prefer DB, fall back to disk parsing + // Normalised slice list from DB type NormSlice = { id: string; done: boolean; depends: string[] }; - let slices: NormSlice[]; - if (isDbAvailable()) { - const rows = getMilestoneSlices(mid); - if (rows.length === 0) continue; - slices = rows.map((r) => ({ - id: r.id, - done: r.status === "complete", - depends: r.depends ?? [], - })); - } else { - // Fallback: disk parsing when DB is not yet initialised - const roadmapContent = readRoadmapFromDisk(base, mid); - if (!roadmapContent) continue; - slices = lazyParseRoadmapSlices(roadmapContent); - } + if (!isDbAvailable()) continue; + + const rows = getMilestoneSlices(mid); + if (rows.length === 0) continue; + const slices: NormSlice[] = rows.map((r) => ({ + id: r.id, + done: r.status === "complete", + depends: r.depends ?? [], + })); if (mid !== targetMid) { const incomplete = slices.find((slice) => !slice.done); diff --git a/src/resources/extensions/gsd/doctor-checks.ts b/src/resources/extensions/gsd/doctor-checks.ts index 9618651fd..862ec3c0a 100644 --- a/src/resources/extensions/gsd/doctor-checks.ts +++ b/src/resources/extensions/gsd/doctor-checks.ts @@ -4,6 +4,7 @@ import { basename, dirname, join, sep } from "node:path"; import type { DoctorIssue, DoctorIssueCode } from "./doctor-types.js"; import { readRepoMeta, externalProjectsRoot } from "./repo-identity.js"; import { loadFile } from "./files.js"; +import { parseRoadmap as parseLegacyRoadmap } from "./parsers-legacy.js"; import { isDbAvailable, getMilestoneSlices } from "./gsd-db.js"; import { resolveMilestoneFile, milestonesDir, gsdRoot, resolveGsdRootFile, relGsdRootFile } from "./paths.js"; import { deriveState, isMilestoneComplete } from "./state.js"; @@ -19,17 +20,6 @@ import { readAllSessionStatuses, isSessionStale, removeSessionStatus } from "./s import { recoverFailedMigration } from "./migrate-external.js"; import { loadEffectiveGSDPreferences } from "./preferences.js"; -// Lazy-loaded parser — only resolved when DB is unavailable (fallback path) -import { createRequire } from "node:module"; -let _lazyParseRoadmap: ((c: string) => { title: string; slices: Array<{ id: string; done: boolean; title: string; risk: string; depends: string[]; demo: string }> }) | null = null; -function lazyParseRoadmap(content: string) { - if (!_lazyParseRoadmap) { - const req = createRequire(import.meta.url); - try { _lazyParseRoadmap = req("./files.ts").parseRoadmap; } - catch { _lazyParseRoadmap = req("./files.js").parseRoadmap; } - } - return _lazyParseRoadmap!(content); -} export async function checkGitHealth( basePath: string, issues: DoctorIssue[], @@ -70,10 +60,11 @@ export async function checkGitHealth( const roadmapPath = resolveMilestoneFile(basePath, milestoneId, "ROADMAP"); const roadmapContent = roadmapPath ? await loadFile(roadmapPath) : null; if (roadmapContent) { - const roadmap = lazyParseRoadmap(roadmapContent); + const roadmap = parseLegacyRoadmap(roadmapContent); isComplete = isMilestoneComplete(roadmap); } } + // When DB unavailable and no roadmap, isComplete stays false } if (isComplete) { @@ -122,7 +113,7 @@ export async function checkGitHealth( } else { const roadmapContent = roadmapPath ? await loadFile(roadmapPath) : null; if (!roadmapContent) continue; - const roadmap = lazyParseRoadmap(roadmapContent); + const roadmap = parseLegacyRoadmap(roadmapContent); branchMilestoneComplete = isMilestoneComplete(roadmap); } if (branchMilestoneComplete) { diff --git a/src/resources/extensions/gsd/doctor.ts b/src/resources/extensions/gsd/doctor.ts index b39fb140f..5cc52282d 100644 --- a/src/resources/extensions/gsd/doctor.ts +++ b/src/resources/extensions/gsd/doctor.ts @@ -2,6 +2,7 @@ import { existsSync, mkdirSync, lstatSync, readdirSync, readFileSync } from "nod import { join } from "node:path"; import { loadFile, parseSummary, saveFile, parseTaskPlanMustHaves, countMustHavesMentionedInSummary } from "./files.js"; +import { parseRoadmap as parseLegacyRoadmap, parsePlan as parseLegacyPlan } from "./parsers-legacy.js"; import { isDbAvailable, getMilestoneSlices, getSliceTasks } from "./gsd-db.js"; import { resolveMilestoneFile, resolveMilestonePath, resolveSliceFile, resolveSlicePath, resolveTaskFile, resolveTasksDir, milestonesDir, gsdRoot, relMilestoneFile, relSliceFile, relTaskFile, relSlicePath, relGsdRootFile, resolveGsdRootFile, relMilestonePath } from "./paths.js"; import { deriveState, isMilestoneComplete } from "./state.js"; @@ -15,23 +16,6 @@ import { checkGitHealth, checkRuntimeHealth, checkGlobalHealth } from "./doctor- import { checkEnvironmentHealth } from "./doctor-environment.js"; import { runProviderChecks } from "./doctor-providers.js"; -// ── Lazy-loaded parsers — only resolved when DB is unavailable (fallback path) ── -import { createRequire } from "node:module"; -let _lazyParsers: { parseRoadmap: (c: string) => { title: string; slices: RoadmapSliceEntry[] }; parsePlan: (c: string) => { title: string; goal: string; tasks: Array<{ id: string; done: boolean; title: string; estimate?: string; files?: string[]; verify?: string }> } } | null = null; -function getLazyParsers() { - if (!_lazyParsers) { - const req = createRequire(import.meta.url); - try { - const mod = req("./files.ts"); - _lazyParsers = { parseRoadmap: mod.parseRoadmap, parsePlan: mod.parsePlan }; - } catch { - const mod = req("./files.js"); - _lazyParsers = { parseRoadmap: mod.parseRoadmap, parsePlan: mod.parsePlan }; - } - } - return _lazyParsers!; -} - // ── Re-exports ───────────────────────────────────────────────────────────── // All public types and functions from extracted modules are re-exported here // so that existing imports from "./doctor.js" continue to work unchanged. @@ -231,13 +215,12 @@ export async function selectDoctorScope(basePath: string, requestedScope?: strin const roadmapPath = resolveMilestoneFile(basePath, milestone.id, "ROADMAP"); const roadmapContent = roadmapPath ? await loadFile(roadmapPath) : null; if (!roadmapContent) continue; - // DB primary path — check slice statuses directly from DB if (isDbAvailable()) { const dbSlices = getMilestoneSlices(milestone.id); const allDone = dbSlices.length > 0 && dbSlices.every(s => s.status === "complete"); if (!allDone) return milestone.id; } else { - const roadmap = getLazyParsers().parseRoadmap(roadmapContent); + const roadmap = parseLegacyRoadmap(roadmapContent); if (!isMilestoneComplete(roadmap)) return milestone.id; } } @@ -500,7 +483,7 @@ export async function runGSDDoctor(basePath: string, options?: { fix?: boolean; demo: s.demo, })); } else { - slices = getLazyParsers().parseRoadmap(roadmapContent).slices; + slices = parseLegacyRoadmap(roadmapContent).slices; } // Wrap in Roadmap-compatible shape for detectCircularDependencies const roadmap = { slices }; @@ -622,7 +605,7 @@ export async function runGSDDoctor(basePath: string, options?: { fix?: boolean; const planPath = resolveSliceFile(basePath, milestoneId, slice.id, "PLAN"); const planContent = planPath ? await loadFile(planPath) : null; - // Normalize plan tasks: prefer DB, fall back to parser + // Normalize plan tasks: prefer DB, fall back to parsers-legacy let plan: { tasks: Array<{ id: string; done: boolean; title: string; estimate?: string }> } | null = null; if (isDbAvailable()) { const dbTasks = getSliceTasks(milestoneId, slice.id); @@ -631,7 +614,7 @@ export async function runGSDDoctor(basePath: string, options?: { fix?: boolean; } } if (!plan && planContent) { - plan = getLazyParsers().parsePlan(planContent); + plan = parseLegacyPlan(planContent); } if (!plan) { if (!slice.done) { diff --git a/src/resources/extensions/gsd/guided-flow.ts b/src/resources/extensions/gsd/guided-flow.ts index 3a19e58d9..a0479b68d 100644 --- a/src/resources/extensions/gsd/guided-flow.ts +++ b/src/resources/extensions/gsd/guided-flow.ts @@ -39,18 +39,6 @@ import { findMilestoneIds, nextMilestoneId, reserveMilestoneId, getReservedMiles import { parkMilestone, discardMilestone } from "./milestone-actions.js"; import { resolveModelWithFallbacksForUnit } from "./preferences-models.js"; -// Lazy-loaded parseRoadmap — only resolved when DB is unavailable (fallback path) -import { createRequire } from "node:module"; -let _lazyParseRoadmap: ((c: string) => { slices: Array<{ id: string; done: boolean; title: string; risk: string; depends: string[]; demo: string }> }) | null = null; -function lazyParseRoadmap(content: string) { - if (!_lazyParseRoadmap) { - const req = createRequire(import.meta.url); - try { _lazyParseRoadmap = req("./files.ts").parseRoadmap; } - catch { _lazyParseRoadmap = req("./files.js").parseRoadmap; } - } - return _lazyParseRoadmap!(content); -} - // ─── Re-exports (preserve public API for existing importers) ──────────────── export { MILESTONE_ID_RE, generateMilestoneSuffix, nextMilestoneId, @@ -464,8 +452,6 @@ async function buildDiscussSlicePrompt( let normSlices: NormSlice[] = []; if (isDbAvailable()) { normSlices = getMilestoneSlices(mid).map(s => ({ id: s.id, done: s.status === "complete" })); - } else if (roadmapContent) { - normSlices = lazyParseRoadmap(roadmapContent).slices; } for (const s of normSlices) { if (!s.done || s.id === sid) continue; @@ -608,7 +594,7 @@ export async function showDiscuss( if (isDbAvailable()) { normSlices = getMilestoneSlices(mid).map(s => ({ id: s.id, done: s.status === "complete", title: s.title })); } else { - normSlices = lazyParseRoadmap(roadmapContent!).slices; + normSlices = []; } const pendingSlices = normSlices.filter(s => !s.done); diff --git a/src/resources/extensions/gsd/parallel-eligibility.ts b/src/resources/extensions/gsd/parallel-eligibility.ts index c36eaab65..20e4a2327 100644 --- a/src/resources/extensions/gsd/parallel-eligibility.ts +++ b/src/resources/extensions/gsd/parallel-eligibility.ts @@ -9,7 +9,6 @@ import { deriveState } from "./state.js"; import { resolveMilestoneFile, resolveSliceFile } from "./paths.js"; import { findMilestoneIds } from "./guided-flow.js"; import { isDbAvailable, getMilestoneSlices, getSliceTasks } from "./gsd-db.js"; -import { createRequire } from "node:module"; import type { MilestoneRegistryEntry } from "./types.js"; // ─── Types ─────────────────────────────────────────────────────────────────── @@ -52,41 +51,8 @@ async function collectTouchedFiles( } } } - } else { - // Disk fallback: lazy-load parsers - const req = createRequire(import.meta.url); - let filesModule: { - loadFile: (p: string) => Promise; - parseRoadmap: (c: string) => { slices: { id: string }[] }; - parsePlan: (c: string) => { filesLikelyTouched: string[] }; - }; - try { - filesModule = req("./files.ts"); - } catch { - filesModule = req("./files.js"); - } - - const roadmapPath = resolveMilestoneFile(basePath, milestoneId, "ROADMAP"); - if (!roadmapPath) return []; - - const roadmapContent = await filesModule.loadFile(roadmapPath); - if (!roadmapContent) return []; - - const roadmap = filesModule.parseRoadmap(roadmapContent); - - for (const slice of roadmap.slices) { - const planPath = resolveSliceFile(basePath, milestoneId, slice.id, "PLAN"); - if (!planPath) continue; - - const planContent = await filesModule.loadFile(planPath); - if (!planContent) continue; - - const plan = filesModule.parsePlan(planContent); - for (const f of plan.filesLikelyTouched) { - files.add(f); - } - } } + // When DB unavailable, return empty file set — parallel eligibility cannot be determined return [...files]; } diff --git a/src/resources/extensions/gsd/reactive-graph.ts b/src/resources/extensions/gsd/reactive-graph.ts index 66f88df94..c36ca29f9 100644 --- a/src/resources/extensions/gsd/reactive-graph.ts +++ b/src/resources/extensions/gsd/reactive-graph.ts @@ -205,16 +205,8 @@ export async function loadSliceTaskIO( } catch { /* fall through */ } if (!taskEntries) { - // Parser fallback - if (!planContent) return []; - const { createRequire } = await import("node:module"); - const _require = createRequire(import.meta.url); - let parsePlan: Function; - try { parsePlan = _require("./files.ts").parsePlan; } - catch { parsePlan = _require("./files.js").parsePlan; } - const plan = parsePlan(planContent); - taskEntries = plan.tasks; - if (!taskEntries || taskEntries.length === 0) return []; + // DB unavailable — cannot determine task graph + return []; } const tDir = resolveTasksDir(basePath, mid, sid); diff --git a/src/resources/extensions/gsd/visualizer-data.ts b/src/resources/extensions/gsd/visualizer-data.ts index 9342dd3a2..cac910392 100644 --- a/src/resources/extensions/gsd/visualizer-data.ts +++ b/src/resources/extensions/gsd/visualizer-data.ts @@ -37,18 +37,6 @@ import type { UnitMetrics, } from './metrics.js'; -// Lazy-loaded parsers — only resolved when DB is unavailable (fallback path) -import { createRequire } from 'node:module'; -let _lazyParsers: { parseRoadmap: (c: string) => { slices: Array<{ id: string; done: boolean; title: string; risk: string; depends: string[]; demo: string }> }; parsePlan: (c: string) => { tasks: Array<{ id: string; done: boolean; title: string; estimate?: string }> } } | null = null; -function getLazyParsers() { - if (!_lazyParsers) { - const req = createRequire(import.meta.url); - try { const mod = req('./files.ts'); _lazyParsers = { parseRoadmap: mod.parseRoadmap, parsePlan: mod.parsePlan }; } - catch { const mod = req('./files.js'); _lazyParsers = { parseRoadmap: mod.parseRoadmap, parsePlan: mod.parsePlan }; } - } - return _lazyParsers!; -} - // ─── Visualizer Types ───────────────────────────────────────────────────────── export interface VisualizerMilestone { @@ -810,13 +798,13 @@ export async function loadVisualizerData(basePath: string): Promise ({ id: s.id, done: s.status === 'complete', title: s.title, risk: s.risk || 'medium', depends: s.depends, demo: s.demo })); } else { - normSlices = getLazyParsers().parseRoadmap(roadmapContent!).slices; + normSlices = []; } for (const s of normSlices) { @@ -827,7 +815,7 @@ export async function loadVisualizerData(basePath: string): Promise { title: string; slices: Array<{ id: string; done: boolean; title: string; risk: string; depends: string[]; demo: string }> }; parsePlan: (c: string) => { title: string; tasks: Array<{ id: string; done: boolean; title: string; estimate?: string }> } } | null = null; -function getLazyParsers() { - if (!_lazyParsers) { - const req = createRequire(import.meta.url); - try { const mod = req("./files.ts"); _lazyParsers = { parseRoadmap: mod.parseRoadmap, parsePlan: mod.parsePlan }; } - catch { const mod = req("./files.js"); _lazyParsers = { parseRoadmap: mod.parseRoadmap, parsePlan: mod.parsePlan }; } - } - return _lazyParsers!; -} - export interface WorkspaceTaskTarget { id: string; title: string; @@ -75,10 +63,12 @@ export interface GSDWorkspaceIndex { validationIssues: ValidationIssue[]; } - +// Extract milestone title from roadmap header without using parsers. +// Falls back to the milestone ID if no title line found. function titleFromRoadmapHeader(content: string, fallbackId: string): string { - const roadmap = getLazyParsers().parseRoadmap(content); - return roadmap.title.replace(/^M\d+(?:-[a-z0-9]{6})?[^:]*:\s*/, "") || fallbackId; + // Parse the "# M001: Title" header directly + const match = content.match(/^#\s+M\d+(?:-[a-z0-9]{6})?[^:]*:\s*(.+)/m); + return match?.[1]?.trim() || fallbackId; } async function indexSlice(basePath: string, milestoneId: string, sliceId: string, fallbackTitle: string, done: boolean, roadmapMeta?: { risk?: RiskLevel; depends?: string[]; demo?: string }): Promise { @@ -90,7 +80,7 @@ async function indexSlice(basePath: string, milestoneId: string, sliceId: string const tasks: WorkspaceTaskTarget[] = []; let title = fallbackTitle; - // Prefer DB for task data, fall back to parser + // Prefer DB for task data if (isDbAvailable()) { const dbTasks = getSliceTasks(milestoneId, sliceId); for (const task of dbTasks) { @@ -103,22 +93,8 @@ async function indexSlice(basePath: string, milestoneId: string, sliceId: string summaryPath: resolveTaskFile(basePath, milestoneId, sliceId, task.id, "SUMMARY") ?? undefined, }); } - } else if (planPath) { - const content = await loadFile(planPath); - if (content) { - const plan = getLazyParsers().parsePlan(content); - title = plan.title || fallbackTitle; - for (const task of plan.tasks) { - tasks.push({ - id: task.id, - title: task.title, - done: task.done, - planPath: resolveTaskFile(basePath, milestoneId, sliceId, task.id, "PLAN") ?? undefined, - summaryPath: resolveTaskFile(basePath, milestoneId, sliceId, task.id, "SUMMARY") ?? undefined, - }); - } - } } + // When DB unavailable, tasks stays empty return { id: sliceId, @@ -158,24 +134,18 @@ export async function indexWorkspace(basePath: string, opts: IndexWorkspaceOptio const slices: WorkspaceSliceTarget[] = []; if (roadmapPath || isDbAvailable()) { - // Normalize slices: prefer DB, fall back to parser + // Normalize slices from DB type NormSlice = { id: string; done: boolean; title: string; risk: string; depends: string[]; demo: string }; let normSlices: NormSlice[]; if (isDbAvailable()) { normSlices = getMilestoneSlices(milestoneId).map(s => ({ id: s.id, done: s.status === "complete", title: s.title, risk: s.risk || "medium", depends: s.depends, demo: s.demo })); - // Get title from DB milestone or roadmap header + // Get title from roadmap header if (roadmapPath) { const roadmapContent = await loadFile(roadmapPath); if (roadmapContent) title = titleFromRoadmapHeader(roadmapContent, milestoneId); } } else { - const roadmapContent = await loadFile(roadmapPath!); - if (roadmapContent) { - normSlices = getLazyParsers().parseRoadmap(roadmapContent).slices; - title = titleFromRoadmapHeader(roadmapContent, milestoneId); - } else { - normSlices = []; - } + normSlices = []; } if (normSlices!.length > 0) {