Merge pull request #3555 from Tibsfox/fix/captures-executed-timestamp
fix(gsd): stamp defer and milestone captures as executed after triage
This commit is contained in:
commit
b38db63c96
2 changed files with 49 additions and 10 deletions
|
|
@ -0,0 +1,30 @@
|
|||
/**
|
||||
* Regression test for #3542: defer and milestone captures must be stamped
|
||||
* as executed after triage resolution, regardless of directory state.
|
||||
*/
|
||||
import { test } from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
import { mkdtempSync, mkdirSync, writeFileSync, readFileSync, rmSync } from "node:fs";
|
||||
import { join } from "node:path";
|
||||
import { tmpdir } from "node:os";
|
||||
import { executeTriageResolutions } from "../triage-resolution.ts";
|
||||
import { appendCapture, markCaptureResolved, loadAllCaptures } from "../captures.ts";
|
||||
|
||||
test("defer captures without milestone ID are stamped as executed (#3542)", async () => {
|
||||
const base = mkdtempSync(join(tmpdir(), "gsd-stamp-"));
|
||||
mkdirSync(join(base, ".gsd", "milestones", "M001"), { recursive: true });
|
||||
try {
|
||||
appendCapture(base, "Improve error messages");
|
||||
const captures = loadAllCaptures(base);
|
||||
const id = captures[0].id;
|
||||
markCaptureResolved(base, id, "defer", "Deferred to a future UX-polish milestone", "Not urgent");
|
||||
|
||||
executeTriageResolutions(base, "M001", "S01");
|
||||
|
||||
const after = loadAllCaptures(base);
|
||||
const cap = after.find(c => c.id === id);
|
||||
assert.ok(cap?.executed, "Defer capture should be stamped as executed");
|
||||
} finally {
|
||||
rmSync(base, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
|
|
@ -479,15 +479,18 @@ export function executeTriageResolutions(
|
|||
}
|
||||
}
|
||||
|
||||
// Also process deferred captures that target milestone IDs — create
|
||||
// milestone directories so deriveState() discovers them.
|
||||
const deferred = loadAllCaptures(basePath).filter(
|
||||
c => c.status === "resolved" && !c.executed && c.classification === "defer",
|
||||
// Also process deferred and milestone-class captures (#3542).
|
||||
// A defer/milestone capture's "action" is the triage decision itself —
|
||||
// once classified and resolved, the capture is done. The target milestone
|
||||
// picks up the work naturally from its planning context.
|
||||
const deferrable = loadAllCaptures(basePath).filter(
|
||||
c => c.status === "resolved" && !c.executed &&
|
||||
(c.classification === "defer" || (c.classification as string) === "milestone"),
|
||||
);
|
||||
if (deferred.length > 0) {
|
||||
// Group deferred captures by target milestone
|
||||
if (deferrable.length > 0) {
|
||||
// Group captures that reference a specific milestone — create dirs as needed.
|
||||
const byMilestone = new Map<string, CaptureEntry[]>();
|
||||
for (const cap of deferred) {
|
||||
for (const cap of deferrable) {
|
||||
const target = cap.resolution?.match(/\b(M\d{3}(?:-[a-z0-9]{6})?)\b/)?.[1];
|
||||
if (target) {
|
||||
const list = byMilestone.get(target) ?? [];
|
||||
|
|
@ -502,12 +505,18 @@ export function executeTriageResolutions(
|
|||
if (created) {
|
||||
result.deferredMilestones++;
|
||||
result.actions.push(`Created milestone ${milestoneId} for ${captures.length} deferred capture(s)`);
|
||||
for (const cap of captures) {
|
||||
markCaptureExecuted(basePath, cap.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Stamp ALL defer/milestone captures as executed (#3542 gaps 1-3).
|
||||
// Previously only captures that triggered dir creation were stamped.
|
||||
// Captures without a milestone ID in resolution text, or targeting an
|
||||
// existing directory, were silently dropped — never stamped.
|
||||
for (const cap of deferrable) {
|
||||
if (!cap.executed) {
|
||||
markCaptureExecuted(basePath, cap.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Mark note captures as executed — they're informational only, no action
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue