Skip to content

Commit f96e067

Browse files
feat(engine): add isPlanEmpty (#3447)
Expose a pure plan-DAG emptiness check for miner and dashboard flows that gate on whether a plan has steps. Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent f6b9872 commit f96e067

3 files changed

Lines changed: 50 additions & 0 deletions

File tree

packages/gittensory-engine/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,7 @@ export {
138138
export * from "./plan-export.js";
139139
export { countPlanStepsByStatus } from "./plan-step-stats.js";
140140
export { countPlanSteps } from "./plan-step-count.js";
141+
export { isPlanEmpty } from "./plan-empty.js";
141142
export { isPlanFullyCompleted } from "./plan-completion.js";
142143
export { hasPlanFailedSteps } from "./plan-failure.js";
143144
export { hasPlanPendingSteps } from "./plan-pending.js";
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import type { PlanDag } from "./plan-export.js";
2+
3+
/**
4+
* Return whether the plan has no steps. Pure — reads the plan DAG only.
5+
*/
6+
export function isPlanEmpty(plan: PlanDag): boolean {
7+
return plan.steps.length === 0;
8+
}

test/unit/plan-empty.test.ts

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import { describe, expect, it } from "vitest";
2+
3+
import { isPlanEmpty } from "../../packages/gittensory-engine/src/plan-empty";
4+
import type { PlanStep } from "../../packages/gittensory-engine/src/plan-export";
5+
6+
function step(over: Partial<PlanStep> & { id: string; title: string }): PlanStep {
7+
return {
8+
actionClass: undefined,
9+
dependsOn: [],
10+
status: "pending",
11+
attempts: 0,
12+
maxAttempts: 3,
13+
lastError: null,
14+
...over,
15+
};
16+
}
17+
18+
describe("isPlanEmpty", () => {
19+
it("returns true when the plan has no steps", () => {
20+
expect(isPlanEmpty({ steps: [] })).toBe(true);
21+
});
22+
23+
it("returns false when the plan has at least one step", () => {
24+
expect(isPlanEmpty({ steps: [step({ id: "a", title: "Build" })] })).toBe(false);
25+
expect(
26+
isPlanEmpty({
27+
steps: [
28+
step({ id: "a", title: "Build", status: "completed" }),
29+
step({ id: "b", title: "Test", status: "pending" }),
30+
],
31+
}),
32+
).toBe(false);
33+
});
34+
35+
it("is exported from the package barrel", async () => {
36+
const barrel = await import("../../packages/gittensory-engine/src/index");
37+
expect(typeof barrel.isPlanEmpty).toBe("function");
38+
expect(barrel.isPlanEmpty({ steps: [] })).toBe(true);
39+
expect(barrel.isPlanEmpty({ steps: [step({ id: "a", title: "A" })] })).toBe(false);
40+
});
41+
});

0 commit comments

Comments
 (0)