From 498614c95f49bdcb221ffb1387c63be1d5d1a4e5 Mon Sep 17 00:00:00 2001 From: Lex Christopherson Date: Fri, 13 Mar 2026 09:20:19 -0600 Subject: [PATCH] fix: inject observability warnings into agent prompt for enforcement (#174) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit emitObservabilityWarnings only called ctx.ui.notify — the agent never saw the warnings and ignored them entirely. Validator caught real issues (missing observability sections, placeholder diagnostics) but had zero enforcement. Rename to collectObservabilityWarnings (returns issues), add buildObservabilityRepairBlock to format issues as actionable prompt instructions. Appended to the unit prompt so the agent reads flagged files and fixes gaps before proceeding with the unit. Co-Authored-By: Claude Opus 4.6 --- src/resources/extensions/gsd/auto.ts | 51 +++++++++++++++++++++++----- 1 file changed, 42 insertions(+), 9 deletions(-) diff --git a/src/resources/extensions/gsd/auto.ts b/src/resources/extensions/gsd/auto.ts index 24ab30b34..264738921 100644 --- a/src/resources/extensions/gsd/auto.ts +++ b/src/resources/extensions/gsd/auto.ts @@ -1262,7 +1262,7 @@ async function dispatchNextUnit( return; } - await emitObservabilityWarnings(ctx, unitType, unitId); + const observabilityIssues = await collectObservabilityWarnings(ctx, unitType, unitId); // Idempotency: skip units already completed in a prior session. const idempotencyKey = `${unitType}/${unitId}`; @@ -1402,6 +1402,13 @@ async function dispatchNextUnit( } } + // Inject observability repair instructions so the agent fixes gaps before + // proceeding with the unit (see #174). + const repairBlock = buildObservabilityRepairBlock(observabilityIssues); + if (repairBlock) { + finalPrompt = `${finalPrompt}${repairBlock}`; + } + // Switch model if preferences specify one for this unit type // Try primary model, then fallbacks in order if setting fails const modelConfig = resolveModelWithFallbacksForUnit(unitType); @@ -2365,17 +2372,17 @@ function ensurePreconditions( // ─── Diagnostics ────────────────────────────────────────────────────────────── -async function emitObservabilityWarnings( +async function collectObservabilityWarnings( ctx: ExtensionContext, unitType: string, unitId: string, -): Promise { +): Promise { const parts = unitId.split("/"); const mid = parts[0]; const sid = parts[1]; const tid = parts[2]; - if (!mid || !sid) return; + if (!mid || !sid) return []; let issues = [] as Awaited>; @@ -2387,12 +2394,38 @@ async function emitObservabilityWarnings( issues = await validateCompleteBoundary(basePath, mid, sid); } - if (issues.length === 0) return; + if (issues.length > 0) { + ctx.ui.notify( + `Observability check (${unitType}) found ${issues.length} warning${issues.length === 1 ? "" : "s"}:\n${formatValidationIssues(issues)}`, + "warning", + ); + } - ctx.ui.notify( - `Observability check (${unitType}) found ${issues.length} warning${issues.length === 1 ? "" : "s"}:\n${formatValidationIssues(issues)}`, - "warning", - ); + return issues; +} + +function buildObservabilityRepairBlock(issues: import("./observability-validator.ts").ValidationIssue[]): string { + if (issues.length === 0) return ""; + const items = issues.map(issue => { + const fileName = issue.file.split("/").pop() || issue.file; + let line = `- **${fileName}**: ${issue.message}`; + if (issue.suggestion) line += ` → ${issue.suggestion}`; + return line; + }); + return [ + "", + "---", + "", + "## Pre-flight: Observability gaps to fix FIRST", + "", + "The following issues were detected in plan/summary files for this unit.", + "**Read each flagged file, apply the fix described, then proceed with the unit.**", + "", + ...items, + "", + "---", + "", + ].join("\n"); } async function recoverTimedOutUnit(