From dde93d5eeca5fdd93f4e1db5a3c271cae4757f13 Mon Sep 17 00:00:00 2001 From: kiannidev <156195510+kiannidev@users.noreply.github.com> Date: Sun, 5 Jul 2026 09:09:29 +0200 Subject: [PATCH] feat(engine): add countPlanSteps Expose a pure plan-DAG step total for miner and dashboard progress summaries. Co-authored-by: Cursor --- packages/gittensory-engine/src/index.ts | 1 + .../gittensory-engine/src/plan-step-count.ts | 8 ++++ test/unit/plan-step-count.test.ts | 44 +++++++++++++++++++ 3 files changed, 53 insertions(+) create mode 100644 packages/gittensory-engine/src/plan-step-count.ts create mode 100644 test/unit/plan-step-count.test.ts diff --git a/packages/gittensory-engine/src/index.ts b/packages/gittensory-engine/src/index.ts index 91c489f44b..feef006714 100644 --- a/packages/gittensory-engine/src/index.ts +++ b/packages/gittensory-engine/src/index.ts @@ -137,6 +137,7 @@ export { } from "./governor-ledger.js"; export * from "./plan-export.js"; export { countPlanStepsByStatus } from "./plan-step-stats.js"; +export { countPlanSteps } from "./plan-step-count.js"; export { isPlanFullyCompleted } from "./plan-completion.js"; export { hasPlanFailedSteps } from "./plan-failure.js"; export { hasPlanPendingSteps } from "./plan-pending.js"; diff --git a/packages/gittensory-engine/src/plan-step-count.ts b/packages/gittensory-engine/src/plan-step-count.ts new file mode 100644 index 0000000000..d19aeeacc4 --- /dev/null +++ b/packages/gittensory-engine/src/plan-step-count.ts @@ -0,0 +1,8 @@ +import type { PlanDag } from "./plan-export.js"; + +/** + * Return the total number of steps in the plan. Pure — reads the plan DAG only. + */ +export function countPlanSteps(plan: PlanDag): number { + return plan.steps.length; +} diff --git a/test/unit/plan-step-count.test.ts b/test/unit/plan-step-count.test.ts new file mode 100644 index 0000000000..e56e6b4228 --- /dev/null +++ b/test/unit/plan-step-count.test.ts @@ -0,0 +1,44 @@ +import { describe, expect, it } from "vitest"; + +import { countPlanSteps } from "../../packages/gittensory-engine/src/plan-step-count"; +import type { PlanStep } from "../../packages/gittensory-engine/src/plan-export"; + +function step(over: Partial & { id: string; title: string }): PlanStep { + return { + actionClass: undefined, + dependsOn: [], + status: "pending", + attempts: 0, + maxAttempts: 3, + lastError: null, + ...over, + }; +} + +describe("countPlanSteps", () => { + it("returns zero for an empty plan", () => { + expect(countPlanSteps({ steps: [] })).toBe(0); + }); + + it("returns the total number of steps regardless of status", () => { + expect( + countPlanSteps({ + steps: [ + step({ id: "a", title: "Build", status: "completed" }), + step({ id: "b", title: "Test", status: "pending" }), + step({ id: "c", title: "Deploy", status: "failed" }), + ], + }), + ).toBe(3); + }); + + it("is exported from the package barrel", async () => { + const barrel = await import("../../packages/gittensory-engine/src/index"); + expect(typeof barrel.countPlanSteps).toBe("function"); + expect( + barrel.countPlanSteps({ + steps: [step({ id: "a", title: "A" }), step({ id: "b", title: "B" })], + }), + ).toBe(2); + }); +});