diff --git a/src/resources/extensions/gsd/auto-post-unit.ts b/src/resources/extensions/gsd/auto-post-unit.ts index 12444750e..ab6e5e9a6 100644 --- a/src/resources/extensions/gsd/auto-post-unit.ts +++ b/src/resources/extensions/gsd/auto-post-unit.ts @@ -35,6 +35,7 @@ import { import { writeUnitRuntimeRecord, clearUnitRuntimeRecord } from "./unit-runtime.js"; import { resolveAutoSupervisorConfig, loadEffectiveGSDPreferences } from "./preferences.js"; import { runGSDDoctor, rebuildState, summarizeDoctorIssues } from "./doctor.js"; +import { COMPLETION_TRANSITION_CODES } from "./doctor-types.js"; import { recordHealthSnapshot, checkHealEscalation } from "./doctor-proactive.js"; import { syncStateToProjectRoot } from "./auto-worktree-sync.js"; import { resetRewriteCircuitBreaker } from "./auto-dispatch.js"; @@ -154,13 +155,17 @@ export async function postUnitPreVerification(pctx: PostUnitContext): Promise<"d ctx.ui.notify(`Post-hook: applied ${report.fixesApplied.length} fix(es).`, "info"); } - // Proactive health tracking - const summary = summarizeDoctorIssues(report.issues); + // Proactive health tracking — exclude completion-transition codes at task level + // since they are expected after the last task and resolved by complete-slice + const issuesForHealth = effectiveFixLevel === "task" + ? report.issues.filter(i => !COMPLETION_TRANSITION_CODES.has(i.code)) + : report.issues; + const summary = summarizeDoctorIssues(issuesForHealth); recordHealthSnapshot(summary.errors, summary.warnings, report.fixesApplied.length); // Check if we should escalate to LLM-assisted heal if (summary.errors > 0) { - const unresolvedErrors = report.issues + const unresolvedErrors = issuesForHealth .filter(i => i.severity === "error" && !i.fixable) .map(i => ({ code: i.code, message: i.message, unitId: i.unitId })); const escalation = checkHealEscalation(summary.errors, unresolvedErrors); diff --git a/src/resources/extensions/gsd/doctor-types.ts b/src/resources/extensions/gsd/doctor-types.ts index 1e76fc266..ae580c553 100644 --- a/src/resources/extensions/gsd/doctor-types.ts +++ b/src/resources/extensions/gsd/doctor-types.ts @@ -32,6 +32,19 @@ export type DoctorIssueCode = | "gitignore_missing_patterns" | "unresolvable_dependency"; +/** + * Issue codes that represent expected completion-transition states. + * These are detected by the doctor but should NOT be auto-fixed at task level — + * they are resolved by the complete-slice/complete-milestone dispatch units. + * Consumers (e.g. auto-post-unit health tracking) should exclude these from + * error counts when running at task fixLevel to avoid false escalation. + */ +export const COMPLETION_TRANSITION_CODES = new Set([ + "all_tasks_done_missing_slice_summary", + "all_tasks_done_missing_slice_uat", + "all_tasks_done_roadmap_not_checked", +]); + export interface DoctorIssue { severity: DoctorSeverity; code: DoctorIssueCode; diff --git a/src/resources/extensions/gsd/doctor.ts b/src/resources/extensions/gsd/doctor.ts index c83c6e51e..9fd0e0055 100644 --- a/src/resources/extensions/gsd/doctor.ts +++ b/src/resources/extensions/gsd/doctor.ts @@ -8,6 +8,7 @@ import { invalidateAllCaches } from "./cache.js"; import { loadEffectiveGSDPreferences, type GSDPreferences } from "./preferences.js"; import type { DoctorIssue, DoctorIssueCode } from "./doctor-types.js"; +import { COMPLETION_TRANSITION_CODES } from "./doctor-types.js"; import { checkGitHealth, checkRuntimeHealth } from "./doctor-checks.js"; // ── Re-exports ───────────────────────────────────────────────────────────── @@ -356,16 +357,11 @@ export async function runGSDDoctor(basePath: string, options?: { fix?: boolean; // dispatch lifecycle (complete-slice, complete-milestone units), not to // mechanical post-hook bookkeeping. When fixLevel is "task", these are // detected and reported but never auto-fixed. - const completionTransitionCodes = new Set([ - "all_tasks_done_missing_slice_summary", - "all_tasks_done_missing_slice_uat", - "all_tasks_done_roadmap_not_checked", - ]); /** Whether a given issue code should be auto-fixed at the current fixLevel. */ const shouldFix = (code: DoctorIssueCode): boolean => { if (!fix) return false; - if (fixLevel === "task" && completionTransitionCodes.has(code)) return false; + if (fixLevel === "task" && COMPLETION_TRANSITION_CODES.has(code)) return false; return true; };