feat(gsd): add post-hook bookkeeping after each auto-mode unit
Run doctor (fix mode) and rebuild STATE.md after each unit completes in handleAgentEnd. Catches missed checkboxes and stub summaries the LLM may have skipped, and keeps STATE.md in sync with disk state. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
5bb3229a85
commit
b4ccbadd09
2 changed files with 31 additions and 1 deletions
|
|
@ -18,7 +18,7 @@ import type {
|
|||
|
||||
import { deriveState } from "./state.js";
|
||||
import type { GSDState } from "./types.js";
|
||||
import { loadFile, parseContinue, parseRoadmap, parseSummary, extractUatType, inlinePriorMilestoneSummary } from "./files.js";
|
||||
import { loadFile, parseContinue, parsePlan, parseRoadmap, parseSummary, extractUatType, inlinePriorMilestoneSummary } from "./files.js";
|
||||
export { inlinePriorMilestoneSummary };
|
||||
import type { UatType } from "./files.js";
|
||||
import { loadPrompt } from "./prompt-loader.js";
|
||||
|
|
@ -48,6 +48,7 @@ import {
|
|||
formatValidationIssues,
|
||||
} from "./observability-validator.js";
|
||||
import { ensureGitignore } from "./gitignore.js";
|
||||
import { runGSDDoctor, rebuildState } from "./doctor.js";
|
||||
import { snapshotSkills, clearSkillSnapshot } from "./skill-discovery.js";
|
||||
import {
|
||||
initMetrics, resetMetrics, snapshotUnitMetrics, getLedger,
|
||||
|
|
@ -376,6 +377,28 @@ export async function handleAgentEnd(
|
|||
} catch {
|
||||
// Non-fatal
|
||||
}
|
||||
|
||||
// Post-hook: fix mechanical bookkeeping the LLM may have skipped.
|
||||
// 1. Doctor handles: checkbox marking, stub summaries/UATs.
|
||||
// 2. STATE.md is always rebuilt from disk state (purely derived, no LLM needed).
|
||||
// This is more reliable than prompt instructions for mechanical tasks.
|
||||
// Scope to slice level (M001/S01) so doctor checks all tasks within the slice.
|
||||
try {
|
||||
const scopeParts = currentUnit.id.split("/").slice(0, 2);
|
||||
const doctorScope = scopeParts.join("/");
|
||||
const report = await runGSDDoctor(basePath, { fix: true, scope: doctorScope });
|
||||
if (report.fixesApplied.length > 0) {
|
||||
ctx.ui.notify(`Post-hook: applied ${report.fixesApplied.length} fix(es).`, "info");
|
||||
}
|
||||
} catch {
|
||||
// Non-fatal — doctor failure should never block dispatch
|
||||
}
|
||||
try {
|
||||
await rebuildState(basePath);
|
||||
autoCommitCurrentBranch(basePath, currentUnit.type, currentUnit.id);
|
||||
} catch {
|
||||
// Non-fatal
|
||||
}
|
||||
}
|
||||
|
||||
// In step mode, pause and show a wizard instead of immediately dispatching
|
||||
|
|
|
|||
|
|
@ -147,6 +147,13 @@ async function updateStateFile(basePath: string, fixesApplied: string[]): Promis
|
|||
fixesApplied.push(`updated ${path}`);
|
||||
}
|
||||
|
||||
/** Rebuild STATE.md from current disk state. Exported for auto-mode post-hooks. */
|
||||
export async function rebuildState(basePath: string): Promise<void> {
|
||||
const state = await deriveState(basePath);
|
||||
const path = resolveGsdRootFile(basePath, "STATE");
|
||||
await saveFile(path, buildStateMarkdown(state));
|
||||
}
|
||||
|
||||
async function ensureSliceSummaryStub(basePath: string, milestoneId: string, sliceId: string, fixesApplied: string[]): Promise<void> {
|
||||
const path = join(resolveSlicePath(basePath, milestoneId, sliceId) ?? relSlicePath(basePath, milestoneId, sliceId), `${sliceId}-SUMMARY.md`);
|
||||
const absolute = resolveSliceFile(basePath, milestoneId, sliceId, "SUMMARY") ?? join(resolveSlicePath(basePath, milestoneId, sliceId)!, `${sliceId}-SUMMARY.md`);
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue