fix(gsd): remove stale completedUnits refs, fix writeLock callers, add missing imports
- Remove completedUnits from dashboard, context, parallel, guided-flow, merge - Fix writeLock callers to match new (basePath, unitType, unitId, sessionFile?) signature - Add gsdRoot, atomicWriteSync, verifyExpectedArtifact, writeUnitRuntimeRecord imports to phases.ts - Add full_plan_md to workflow-manifest snapshot mapping Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
3a12089355
commit
63dea156c3
9 changed files with 12 additions and 70 deletions
|
|
@ -624,7 +624,7 @@ export async function bootstrapAutoSession(
|
|||
"starting",
|
||||
s.currentMilestoneId ?? "unknown",
|
||||
);
|
||||
writeLock(lockBase(), "starting", s.currentMilestoneId ?? "unknown", 0);
|
||||
writeLock(lockBase(), "starting", s.currentMilestoneId ?? "unknown");
|
||||
|
||||
// Secrets collection gate
|
||||
const mid = state.activeMilestone!.id;
|
||||
|
|
|
|||
|
|
@ -322,7 +322,6 @@ export function getAutoDashboardData(): AutoDashboardData {
|
|||
? (s.autoStartTime > 0 ? Date.now() - s.autoStartTime : 0)
|
||||
: 0,
|
||||
currentUnit: s.currentUnit ? { ...s.currentUnit } : null,
|
||||
completedUnits: [],
|
||||
basePath: s.basePath,
|
||||
totalCost: totals?.cost ?? 0,
|
||||
totalTokens: totals?.tokens.total ?? 0,
|
||||
|
|
@ -1169,7 +1168,6 @@ export async function startAuto(
|
|||
lockBase(),
|
||||
"resuming",
|
||||
s.currentMilestoneId ?? "unknown",
|
||||
0,
|
||||
);
|
||||
logCmuxEvent(loadEffectiveGSDPreferences()?.preferences, s.stepMode ? "Step-mode resumed." : "Auto-mode resumed.", "progress");
|
||||
|
||||
|
|
@ -1391,7 +1389,6 @@ export async function dispatchHookUnit(
|
|||
lockBase(),
|
||||
hookUnitType,
|
||||
triggerUnitId,
|
||||
0,
|
||||
sessionFile,
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -29,6 +29,10 @@ import { MergeConflictError } from "../git-service.js";
|
|||
import { join } from "node:path";
|
||||
import { existsSync, cpSync } from "node:fs";
|
||||
import { logWarning, logError } from "../workflow-logger.js";
|
||||
import { gsdRoot } from "../paths.js";
|
||||
import { atomicWriteSync } from "../atomic-write.js";
|
||||
import { verifyExpectedArtifact } from "../auto-recovery.js";
|
||||
import { writeUnitRuntimeRecord } from "../unit-runtime.js";
|
||||
|
||||
// ─── generateMilestoneReport ──────────────────────────────────────────────────
|
||||
|
||||
|
|
@ -275,11 +279,7 @@ export async function runPreDispatch(
|
|||
.map((m: { id: string }) => m.id);
|
||||
deps.pruneQueueOrder(s.basePath, pendingIds);
|
||||
|
||||
// Reset completed-units tracking for the new milestone — stale entries
|
||||
// from the previous milestone cause the dispatch loop to skip units
|
||||
// that haven't actually been completed in the new milestone's context.
|
||||
// Archive the old completed-units.json instead of wiping it (#2313).
|
||||
s.completedUnits = [];
|
||||
try {
|
||||
const completedKeysPath = join(gsdRoot(s.basePath), "completed-units.json");
|
||||
if (existsSync(completedKeysPath) && s.currentMilestoneId) {
|
||||
|
|
@ -538,7 +538,7 @@ export async function runDispatch(
|
|||
if (loopState.stuckRecoveryAttempts === 0) {
|
||||
// Level 1: try verifying the artifact, then cache invalidation + retry
|
||||
loopState.stuckRecoveryAttempts++;
|
||||
const artifactExists = deps.verifyExpectedArtifact(
|
||||
const artifactExists = verifyExpectedArtifact(
|
||||
unitType,
|
||||
unitId,
|
||||
s.basePath,
|
||||
|
|
@ -847,7 +847,7 @@ export async function runUnitPhase(
|
|||
const unitStartSeq = ic.nextSeq();
|
||||
deps.emitJournalEvent({ ts: new Date().toISOString(), flowId: ic.flowId, seq: unitStartSeq, eventType: "unit-start", data: { unitType, unitId } });
|
||||
deps.captureAvailableSkills();
|
||||
deps.writeUnitRuntimeRecord(
|
||||
writeUnitRuntimeRecord(
|
||||
s.basePath,
|
||||
unitType,
|
||||
unitId,
|
||||
|
|
@ -1116,7 +1116,7 @@ export async function runUnitPhase(
|
|||
const skipArtifactVerification = unitType.startsWith("hook/") || unitType === "custom-step";
|
||||
const artifactVerified =
|
||||
skipArtifactVerification ||
|
||||
deps.verifyExpectedArtifact(unitType, unitId, s.basePath);
|
||||
verifyExpectedArtifact(unitType, unitId, s.basePath);
|
||||
if (artifactVerified) {
|
||||
s.unitDispatchCount.delete(`${unitType}/${unitId}`);
|
||||
s.unitRecoveryCount.delete(`${unitType}/${unitId}`);
|
||||
|
|
|
|||
|
|
@ -47,15 +47,10 @@ export async function guardRemoteSession(
|
|||
return false;
|
||||
}
|
||||
|
||||
const unitsMsg = remote.completedUnits != null
|
||||
? `${remote.completedUnits} units completed`
|
||||
: "";
|
||||
|
||||
const choice = await showNextAction(ctx, {
|
||||
title: `Auto-mode is running in another terminal (PID ${remote.pid})`,
|
||||
summary: [
|
||||
`Currently executing: ${unitLabel}`,
|
||||
...(unitsMsg ? [unitsMsg] : []),
|
||||
...(remote.startedAt ? [`Started: ${remote.startedAt}`] : []),
|
||||
],
|
||||
actions: [
|
||||
|
|
|
|||
|
|
@ -63,7 +63,7 @@ export async function handleParallelCommand(trimmed: string, _ctx: ExtensionComm
|
|||
}
|
||||
const lines = ["# Parallel Workers\n"];
|
||||
for (const worker of workers) {
|
||||
lines.push(`- **${worker.milestoneId}** (${worker.title}) — ${worker.state} — ${worker.completedUnits} units — $${worker.cost.toFixed(2)}`);
|
||||
lines.push(`- **${worker.milestoneId}** (${worker.title}) — ${worker.state} — $${worker.cost.toFixed(2)}`);
|
||||
}
|
||||
const state = getOrchestratorState();
|
||||
if (state) {
|
||||
|
|
|
|||
|
|
@ -99,18 +99,11 @@ export class GSDDashboardOverlay {
|
|||
const currentUnit = dashData.currentUnit
|
||||
? `${dashData.currentUnit.type}:${dashData.currentUnit.id}:${dashData.currentUnit.startedAt}`
|
||||
: "-";
|
||||
const lastCompleted = dashData.completedUnits.length > 0
|
||||
? dashData.completedUnits[dashData.completedUnits.length - 1]
|
||||
: null;
|
||||
const completedKey = lastCompleted
|
||||
? `${dashData.completedUnits.length}:${lastCompleted.type}:${lastCompleted.id}:${lastCompleted.finishedAt}`
|
||||
: "0";
|
||||
return [
|
||||
base,
|
||||
dashData.active ? "1" : "0",
|
||||
dashData.paused ? "1" : "0",
|
||||
currentUnit,
|
||||
completedKey,
|
||||
].join("|");
|
||||
}
|
||||
|
||||
|
|
@ -458,49 +451,6 @@ export class GSDDashboardOverlay {
|
|||
lines.push(centered(th.fg("dim", "No active milestone.")));
|
||||
}
|
||||
|
||||
if (this.dashData.completedUnits.length > 0) {
|
||||
lines.push(blank());
|
||||
lines.push(hr());
|
||||
lines.push(row(th.fg("text", th.bold("Completed"))));
|
||||
lines.push(blank());
|
||||
|
||||
// Build ledger lookup for budget indicators (last entry wins for retries)
|
||||
const ledgerLookup = new Map<string, UnitMetrics>();
|
||||
const currentLedger = getLedger();
|
||||
if (currentLedger) {
|
||||
for (const lu of currentLedger.units) {
|
||||
ledgerLookup.set(`${lu.type}:${lu.id}`, lu);
|
||||
}
|
||||
}
|
||||
|
||||
const recent = [...this.dashData.completedUnits].reverse().slice(0, 10);
|
||||
for (const u of recent) {
|
||||
// Budget indicators from ledger — use warning glyph for pressured units
|
||||
const ledgerEntry = ledgerLookup.get(`${u.type}:${u.id}`);
|
||||
const hadPressure = ledgerEntry?.continueHereFired === true;
|
||||
const hadTruncation = (ledgerEntry?.truncationSections ?? 0) > 0;
|
||||
const unitGlyph = hadPressure
|
||||
? th.fg(STATUS_COLOR.warning, STATUS_GLYPH.warning)
|
||||
: th.fg(STATUS_COLOR.done, STATUS_GLYPH.done);
|
||||
const left = ` ${unitGlyph} ${th.fg("muted", unitLabel(u.type))} ${th.fg("muted", u.id)}`;
|
||||
|
||||
let budgetMarkers = "";
|
||||
if (hadTruncation) {
|
||||
budgetMarkers += th.fg("warning", ` ▼${ledgerEntry!.truncationSections}`);
|
||||
}
|
||||
if (hadPressure) {
|
||||
budgetMarkers += th.fg("error", " → wrap-up");
|
||||
}
|
||||
|
||||
const right = th.fg("dim", formatDuration(u.finishedAt - u.startedAt));
|
||||
lines.push(row(joinColumns(`${left}${budgetMarkers}`, right, contentWidth)));
|
||||
}
|
||||
|
||||
if (this.dashData.completedUnits.length > 10) {
|
||||
lines.push(row(th.fg("dim", ` ...and ${this.dashData.completedUnits.length - 10} more`)));
|
||||
}
|
||||
}
|
||||
|
||||
const ledger = getLedger();
|
||||
if (ledger && ledger.units.length > 0) {
|
||||
const totals = getProjectTotals(ledger.units);
|
||||
|
|
|
|||
|
|
@ -910,8 +910,7 @@ export async function showSmartEntry(
|
|||
// when the user exits during init wizard or discuss phase before any
|
||||
// real auto-mode work begins.
|
||||
const isBootstrapCrash = crashLock.unitType === "starting"
|
||||
&& crashLock.unitId === "bootstrap"
|
||||
&& crashLock.completedUnits === 0;
|
||||
&& crashLock.unitId === "bootstrap";
|
||||
|
||||
if (!isBootstrapCrash) {
|
||||
const resume = await showNextAction(ctx, {
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ export function determineMergeOrder(
|
|||
workers: WorkerInfo[],
|
||||
order: MergeOrder = "sequential",
|
||||
): string[] {
|
||||
const completed = workers.filter(w => w.state === "stopped" && w.completedUnits > 0);
|
||||
const completed = workers.filter(w => w.state === "stopped");
|
||||
if (order === "by-completion") {
|
||||
return completed
|
||||
.sort((a, b) => a.startedAt - b.startedAt) // earliest first
|
||||
|
|
|
|||
|
|
@ -128,6 +128,7 @@ export function snapshotState(): StateManifest {
|
|||
inputs: JSON.parse((r["inputs"] as string) || "[]"),
|
||||
expected_output: JSON.parse((r["expected_output"] as string) || "[]"),
|
||||
observability_impact: (r["observability_impact"] as string) ?? "",
|
||||
full_plan_md: (r["full_plan_md"] as string) ?? "",
|
||||
sequence: (r["sequence"] as number) ?? 0,
|
||||
}));
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue