refine: extract enqueueSidecar helper in auto-post-unit

Consolidate three near-identical sidecar enqueue blocks (hook, triage,
quick-task) into a shared enqueueSidecar() helper that handles the
push + debugLog + optional UI notification + return "continue" pattern.

Update static-analysis tests to accept the helper call pattern.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Lex Christopherson 2026-03-26 09:09:12 -06:00
parent 43b1de6d59
commit 174aa33231
2 changed files with 46 additions and 67 deletions

View file

@ -48,8 +48,28 @@ import {
import { hasPendingCaptures, loadPendingCaptures } from "./captures.js";
import { debugLog } from "./debug-logger.js";
import { runSafely } from "./auto-utils.js";
import type { AutoSession } from "./auto/session.js";
import type { AutoSession, SidecarItem } from "./auto/session.js";
/** Enqueue a sidecar item (hook, triage, or quick-task) for the main loop to
* drain via runUnit. Logs the enqueue event and notifies the UI. */
function enqueueSidecar(
s: AutoSession,
ctx: ExtensionContext,
entry: SidecarItem,
debugExtra: Record<string, unknown>,
notification?: string,
): "continue" {
s.sidecarQueue.push(entry);
debugLog("postUnitPostVerification", {
phase: "sidecar-enqueue",
kind: entry.kind,
unitId: entry.unitId,
...debugExtra,
});
if (notification) ctx.ui.notify(notification, "info");
return "continue";
}
/** Unit types that only touch `.gsd/` internal state files (no code changes).
* Auto-commit is skipped for these their state files are picked up by the
* next actual task commit via `smartStage()`. */
@ -492,23 +512,11 @@ export async function postUnitPostVerification(pctx: PostUnitContext): Promise<"
}
persistHookState(s.basePath);
s.sidecarQueue.push({
kind: "hook",
unitType: hookUnit.unitType,
unitId: hookUnit.unitId,
prompt: hookUnit.prompt,
model: hookUnit.model,
});
debugLog("postUnitPostVerification", {
phase: "sidecar-enqueue",
kind: "hook",
unitType: hookUnit.unitType,
unitId: hookUnit.unitId,
hookName: hookUnit.hookName,
});
return "continue";
return enqueueSidecar(
s, ctx,
{ kind: "hook", unitType: hookUnit.unitType, unitId: hookUnit.unitId, prompt: hookUnit.prompt, model: hookUnit.model },
{ hookName: hookUnit.hookName },
);
}
// Check if a hook requested a retry of the trigger unit
@ -607,26 +615,12 @@ export async function postUnitPostVerification(pctx: PostUnitContext): Promise<"
}
const triageUnitId = `${mid}/${sid}/triage`;
s.sidecarQueue.push({
kind: "triage",
unitType: "triage-captures",
unitId: triageUnitId,
prompt,
});
debugLog("postUnitPostVerification", {
phase: "sidecar-enqueue",
kind: "triage",
unitId: triageUnitId,
pendingCount: pending.length,
});
ctx.ui.notify(
return enqueueSidecar(
s, ctx,
{ kind: "triage", unitType: "triage-captures", unitId: triageUnitId, prompt },
{ pendingCount: pending.length },
`Triaging ${pending.length} pending capture${pending.length === 1 ? "" : "s"}...`,
"info",
);
return "continue";
}
}
}
@ -655,27 +649,12 @@ export async function postUnitPostVerification(pctx: PostUnitContext): Promise<"
markCaptureExecuted(s.basePath, capture.id);
const qtUnitId = `${s.currentMilestoneId}/${capture.id}`;
s.sidecarQueue.push({
kind: "quick-task",
unitType: "quick-task",
unitId: qtUnitId,
prompt,
captureId: capture.id,
});
debugLog("postUnitPostVerification", {
phase: "sidecar-enqueue",
kind: "quick-task",
unitId: qtUnitId,
captureId: capture.id,
});
ctx.ui.notify(
return enqueueSidecar(
s, ctx,
{ kind: "quick-task", unitType: "quick-task", unitId: qtUnitId, prompt, captureId: capture.id },
{ captureId: capture.id },
`Executing quick-task: ${capture.id} — "${capture.text}"`,
"info",
);
return "continue";
} catch (e) {
debugLog("postUnit", { phase: "quick-task-dispatch", error: String(e) });
}

View file

@ -113,12 +113,12 @@ test("postUnitPostVerification pushes to sidecarQueue for hooks", () => {
assert.ok(triageSectionStart > -1, "auto-post-unit.ts must have a triage check section");
const hookSection = source.slice(hookSectionStart, triageSectionStart);
assert.ok(
hookSection.includes("s.sidecarQueue.push("),
"hook section must push to s.sidecarQueue",
hookSection.includes("enqueueSidecar(") || hookSection.includes("s.sidecarQueue.push("),
"hook section must enqueue to sidecarQueue (via enqueueSidecar or direct push)",
);
assert.ok(
hookSection.includes('kind: "hook"'),
"hook sidecar item must have kind: 'hook'",
hookSection.includes('"hook"'),
"hook sidecar item must reference kind 'hook'",
);
});
@ -132,12 +132,12 @@ test("postUnitPostVerification pushes to sidecarQueue for triage", () => {
assert.ok(quickTaskSectionStart > -1, "auto-post-unit.ts must have a quick-task dispatch section");
const triageSection = source.slice(triageSectionStart, quickTaskSectionStart);
assert.ok(
triageSection.includes("s.sidecarQueue.push("),
"triage section must push to s.sidecarQueue",
triageSection.includes("enqueueSidecar(") || triageSection.includes("s.sidecarQueue.push("),
"triage section must enqueue to sidecarQueue (via enqueueSidecar or direct push)",
);
assert.ok(
triageSection.includes('kind: "triage"'),
"triage sidecar item must have kind: 'triage'",
triageSection.includes('"triage"'),
"triage sidecar item must reference kind 'triage'",
);
});
@ -149,12 +149,12 @@ test("postUnitPostVerification pushes to sidecarQueue for quick-tasks", () => {
assert.ok(quickTaskSectionStart > -1, "auto-post-unit.ts must have a quick-task dispatch section");
const quickTaskSection = source.slice(quickTaskSectionStart);
assert.ok(
quickTaskSection.includes("s.sidecarQueue.push("),
"quick-task section must push to s.sidecarQueue",
quickTaskSection.includes("enqueueSidecar(") || quickTaskSection.includes("s.sidecarQueue.push("),
"quick-task section must enqueue to sidecarQueue (via enqueueSidecar or direct push)",
);
assert.ok(
quickTaskSection.includes('kind: "quick-task"'),
"quick-task sidecar item must have kind: 'quick-task'",
quickTaskSection.includes('"quick-task"'),
"quick-task sidecar item must reference kind 'quick-task'",
);
});