sf snapshot: pre-dispatch, uncommitted changes after 42m inactivity

This commit is contained in:
Mikael Hugo 2026-05-04 02:34:07 +02:00
parent 061985b226
commit bffd6c22fc

View file

@ -2,14 +2,18 @@ import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { describe, expect, test } from "vitest";
import { formatFailureContext } from "../verification-gate.js";
import { checkCrossTaskSignatures } from "../post-execution-checks.js";
import { getPriorSliceCompletionBlocker } from "../dispatch-guard.js";
import { extractPackageReferences } from "../pre-execution-checks.js";
import {
evaluateRunawayGuard,
resetRunawayGuardState,
} from "../auto-runaway-guard.js";
import { runCustomVerification } from "../custom-verification.js";
import { getPriorSliceCompletionBlocker } from "../dispatch-guard.js";
import { checkCrossTaskSignatures } from "../post-execution-checks.js";
import {
extractPackageReferences,
normalizeFilePath,
} from "../pre-execution-checks.js";
import { formatFailureContext, isLikelyCommand } from "../verification-gate.js";
// ─── Bug 1: formatFailureContext double-truncation ─────────────────────────
@ -44,8 +48,20 @@ describe("formatFailureContext", () => {
const result = {
passed: false,
checks: [
{ command: "npm run lint", exitCode: 1, stdout: "", stderr: stderrA, durationMs: 100 },
{ command: "npm run test", exitCode: 1, stdout: "", stderr: stderrB, durationMs: 100 },
{
command: "npm run lint",
exitCode: 1,
stdout: "",
stderr: stderrA,
durationMs: 100,
},
{
command: "npm run test",
exitCode: 1,
stdout: "",
stderr: stderrB,
durationMs: 100,
},
],
discoverySource: "preference" as const,
timestamp: Date.now(),
@ -135,10 +151,10 @@ describe("getPriorSliceCompletionBlocker", () => {
writeFileSync(
join(base, ".sf", "milestones", "M001", "ROADMAP.md"),
"# M001\n\n" +
"- [ ] S01 — first slice\n" +
" - depends: S02\n" +
"- [ ] S02 — second slice\n" +
"- [ ] S03 — third slice\n",
"- [ ] S01 — first slice\n" +
" - depends: S02\n" +
"- [ ] S02 — second slice\n" +
"- [ ] S03 — third slice\n",
);
const blocker = getPriorSliceCompletionBlocker(
@ -299,3 +315,86 @@ describe("evaluateRunawayGuard", () => {
expect(decision.action).toBe("warn");
});
});
// ─── Bug 6: isLikelyCommand prose guard gaps ───────────────────────────────
describe("isLikelyCommand", () => {
test("treats single non-command token as prose", () => {
expect(isLikelyCommand("hello")).toBe(false);
expect(isLikelyCommand("world")).toBe(false);
});
test("treats single known command token as command", () => {
expect(isLikelyCommand("npm")).toBe(true);
expect(isLikelyCommand("node")).toBe(true);
});
test("treats single-letter first token with 2+ words as prose", () => {
expect(isLikelyCommand("a quick test")).toBe(false);
expect(isLikelyCommand("it works now")).toBe(false);
expect(isLikelyCommand("i am here")).toBe(false);
});
test("still treats short command-like strings as commands", () => {
expect(isLikelyCommand("npm test")).toBe(true);
expect(isLikelyCommand("./script.sh")).toBe(true);
});
});
// ─── Bug 7: custom-verification standalone shell operators ─────────────────
describe("runCustomVerification shell-command policy", () => {
test("pauses on standalone pipe operator", () => {
const base = mkdtempSync(join(tmpdir(), "sf-cv-pipe-"));
try {
writeFileSync(
join(base, "DEFINITION.yaml"),
`steps:\n - id: step-1\n produces: []\n verify:\n policy: shell-command\n command: echo hello | cat\n`,
);
const result = runCustomVerification(base, "step-1");
expect(result).toBe("pause");
} finally {
rmSync(base, { recursive: true, force: true });
}
});
test("pauses on background operator", () => {
const base = mkdtempSync(join(tmpdir(), "sf-cv-bg-"));
try {
writeFileSync(
join(base, "DEFINITION.yaml"),
`steps:\n - id: step-1\n produces: []\n verify:\n policy: shell-command\n command: echo hello \u0026\n`,
);
const result = runCustomVerification(base, "step-1");
expect(result).toBe("pause");
} finally {
rmSync(base, { recursive: true, force: true });
}
});
test("allows safe command without pipe or background", () => {
const base = mkdtempSync(join(tmpdir(), "sf-cv-safe-"));
try {
writeFileSync(
join(base, "DEFINITION.yaml"),
`steps:\n - id: step-1\n produces: []\n verify:\n policy: shell-command\n command: echo hello\n`,
);
const result = runCustomVerification(base, "step-1");
expect(result).toBe("continue");
} finally {
rmSync(base, { recursive: true, force: true });
}
});
});
// ─── Bug 8: normalizeFilePath JSDoc filesystem warning ─────────────────────
describe("normalizeFilePath", () => {
test("does NOT make path safe for direct filesystem use", () => {
// The function normalizes for comparison only; traversal sequences remain
const normalized = normalizeFilePath("../../etc/passwd");
expect(normalized).toBe("../../etc/passwd");
// A consumer that passed this straight to readFileSync would escape
// the intended directory. The JSDoc warns about this explicitly.
});
});