@ronkovic/aad 0.3.9 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +332 -14
- package/package.json +6 -1
- package/src/__tests__/e2e/cleanup-e2e.test.ts +186 -0
- package/src/__tests__/e2e/dashboard-api-e2e.test.ts +87 -0
- package/src/__tests__/e2e/pipeline-e2e.test.ts +10 -68
- package/src/__tests__/e2e/resume-e2e.test.ts +9 -11
- package/src/__tests__/e2e/retry-e2e.test.ts +285 -0
- package/src/__tests__/e2e/status-e2e.test.ts +227 -0
- package/src/__tests__/e2e/tdd-pipeline-e2e.test.ts +360 -0
- package/src/__tests__/helpers/index.ts +6 -0
- package/src/__tests__/helpers/mock-claude-provider.ts +53 -0
- package/src/__tests__/helpers/mock-logger.ts +36 -0
- package/src/__tests__/helpers/wait-helpers.ts +34 -0
- package/src/__tests__/integration/pipeline.test.ts +3 -0
- package/src/modules/claude-provider/__tests__/claude-sdk-real-env.test.ts +1 -1
- package/src/modules/claude-provider/__tests__/claude-sdk.adapter.test.ts +6 -0
- package/src/modules/claude-provider/__tests__/provider-registry.test.ts +3 -0
- package/src/modules/cli/__tests__/cleanup.test.ts +73 -0
- package/src/modules/cli/__tests__/resume.test.ts +4 -0
- package/src/modules/cli/__tests__/run.test.ts +37 -0
- package/src/modules/cli/__tests__/status.test.ts +1 -0
- package/src/modules/cli/app.ts +2 -0
- package/src/modules/cli/commands/__tests__/task-dispatch-handler.test.ts +145 -0
- package/src/modules/cli/commands/cleanup.ts +26 -11
- package/src/modules/cli/commands/resume.ts +14 -8
- package/src/modules/cli/commands/run.ts +70 -8
- package/src/modules/cli/commands/task-dispatch-handler.ts +73 -3
- package/src/modules/dashboard/__tests__/api-graph.test.ts +332 -0
- package/src/modules/dashboard/__tests__/api-timeline.test.ts +461 -0
- package/src/modules/dashboard/routes/sse.ts +3 -2
- package/src/modules/dashboard/server.ts +1 -0
- package/src/modules/dashboard/services/sse-broadcaster.ts +29 -0
- package/src/modules/dashboard/ui/dashboard.html +640 -349
- package/src/modules/git-workspace/__tests__/branch-manager.test.ts +52 -0
- package/src/modules/git-workspace/__tests__/dependency-installer.test.ts +77 -0
- package/src/modules/git-workspace/__tests__/git-exec.test.ts +26 -0
- package/src/modules/git-workspace/__tests__/merge-service.test.ts +19 -0
- package/src/modules/git-workspace/__tests__/pr-manager.test.ts +80 -0
- package/src/modules/git-workspace/__tests__/template-copy.test.ts +189 -0
- package/src/modules/git-workspace/__tests__/worktree-cleanup.test.ts +29 -2
- package/src/modules/git-workspace/__tests__/worktree-manager.test.ts +64 -4
- package/src/modules/git-workspace/branch-manager.ts +24 -3
- package/src/modules/git-workspace/dependency-installer.ts +113 -0
- package/src/modules/git-workspace/git-exec.ts +3 -2
- package/src/modules/git-workspace/index.ts +10 -1
- package/src/modules/git-workspace/merge-service.ts +36 -2
- package/src/modules/git-workspace/pr-manager.ts +278 -0
- package/src/modules/git-workspace/template-copy.ts +302 -0
- package/src/modules/git-workspace/worktree-manager.ts +37 -11
- package/src/modules/planning/__tests__/planning-service.test.ts +3 -0
- package/src/modules/planning/__tests__/planning.service.test.ts +149 -0
- package/src/modules/planning/__tests__/project-detection.test.ts +7 -1
- package/src/modules/planning/planning.service.ts +16 -2
- package/src/modules/planning/project-detection.ts +4 -1
- package/src/modules/process-manager/__tests__/process-manager.test.ts +3 -0
- package/src/modules/process-manager/process-manager.ts +2 -1
- package/src/modules/task-execution/__tests__/executor.test.ts +496 -0
- package/src/modules/task-execution/__tests__/tester-verify.test.ts +4 -3
- package/src/modules/task-execution/executor.ts +163 -4
- package/src/modules/task-execution/phases/implementer-green.ts +22 -5
- package/src/modules/task-execution/phases/merge.ts +44 -2
- package/src/modules/task-execution/phases/tester-red.ts +22 -5
- package/src/modules/task-execution/phases/tester-verify.ts +22 -6
- package/src/modules/task-queue/dispatcher.ts +96 -3
- package/src/shared/__tests__/config.test.ts +30 -0
- package/src/shared/__tests__/events.test.ts +42 -16
- package/src/shared/__tests__/prerequisites.test.ts +176 -0
- package/src/shared/__tests__/shutdown-handler.test.ts +96 -0
- package/src/shared/config.ts +10 -0
- package/src/shared/events.ts +5 -0
- package/src/shared/memory-check.ts +2 -2
- package/src/shared/prerequisites.ts +190 -0
- package/src/shared/shutdown-handler.ts +12 -5
- package/src/shared/types.ts +25 -0
- package/templates/CLAUDE.md +122 -0
- package/templates/settings.json +117 -0
- package/src/modules/persistence/__tests__/.tmp-stores-test-81991/progress.json +0 -10
- package/src/modules/persistence/__tests__/.tmp-stores-test-81991/queue/completed/task-getall-2.json +0 -10
- package/src/modules/persistence/__tests__/.tmp-stores-test-81991/queue/pending/task-1.json +0 -13
- package/src/modules/persistence/__tests__/.tmp-stores-test-81991/queue/pending/task-getall-1.json +0 -10
- package/src/modules/persistence/__tests__/.tmp-stores-test-81991/queue/pending/task-status-change.json +0 -10
- package/src/modules/persistence/__tests__/.tmp-stores-test-81991/workers/worker-1.json +0 -5
- package/src/modules/persistence/__tests__/.tmp-stores-test-81991/workers/worker-2.json +0 -5
- package/src/modules/persistence/__tests__/.tmp-stores-test-82469/progress.json +0 -10
- package/src/modules/persistence/__tests__/.tmp-stores-test-82469/queue/completed/task-getall-2.json +0 -10
- package/src/modules/persistence/__tests__/.tmp-stores-test-82469/queue/pending/task-1.json +0 -13
- package/src/modules/persistence/__tests__/.tmp-stores-test-82469/queue/pending/task-getall-1.json +0 -10
- package/src/modules/persistence/__tests__/.tmp-stores-test-82469/queue/pending/task-status-change.json +0 -10
- package/src/modules/persistence/__tests__/.tmp-stores-test-82469/workers/worker-1.json +0 -5
- package/src/modules/persistence/__tests__/.tmp-stores-test-82469/workers/worker-2.json +0 -5
- package/src/modules/persistence/__tests__/.tmp-stores-test-85301/progress.json +0 -10
- package/src/modules/persistence/__tests__/.tmp-stores-test-85301/queue/completed/task-getall-2.json +0 -10
- package/src/modules/persistence/__tests__/.tmp-stores-test-85301/queue/pending/task-1.json +0 -13
- package/src/modules/persistence/__tests__/.tmp-stores-test-85301/queue/pending/task-getall-1.json +0 -10
- package/src/modules/persistence/__tests__/.tmp-stores-test-85301/queue/pending/task-status-change.json +0 -10
- package/src/modules/persistence/__tests__/.tmp-stores-test-85301/workers/worker-1.json +0 -5
- package/src/modules/persistence/__tests__/.tmp-stores-test-85301/workers/worker-2.json +0 -5
- package/src/modules/persistence/__tests__/.tmp-stores-test-85759/progress.json +0 -10
- package/src/modules/persistence/__tests__/.tmp-stores-test-85759/queue/completed/task-getall-2.json +0 -10
- package/src/modules/persistence/__tests__/.tmp-stores-test-85759/queue/pending/task-1.json +0 -13
- package/src/modules/persistence/__tests__/.tmp-stores-test-85759/queue/pending/task-getall-1.json +0 -10
- package/src/modules/persistence/__tests__/.tmp-stores-test-85759/queue/pending/task-status-change.json +0 -10
- package/src/modules/persistence/__tests__/.tmp-stores-test-85759/workers/worker-1.json +0 -5
- package/src/modules/persistence/__tests__/.tmp-stores-test-85759/workers/worker-2.json +0 -5
- package/src/modules/persistence/__tests__/.tmp-stores-test-86184/progress.json +0 -10
- package/src/modules/persistence/__tests__/.tmp-stores-test-86184/queue/completed/task-getall-2.json +0 -10
- package/src/modules/persistence/__tests__/.tmp-stores-test-86184/queue/pending/task-1.json +0 -13
- package/src/modules/persistence/__tests__/.tmp-stores-test-86184/queue/pending/task-getall-1.json +0 -10
- package/src/modules/persistence/__tests__/.tmp-stores-test-86184/queue/pending/task-status-change.json +0 -10
- package/src/modules/persistence/__tests__/.tmp-stores-test-86184/workers/worker-1.json +0 -5
- package/src/modules/persistence/__tests__/.tmp-stores-test-86184/workers/worker-2.json +0 -5
- package/src/modules/persistence/__tests__/.tmp-stores-test-88026/progress.json +0 -10
- package/src/modules/persistence/__tests__/.tmp-stores-test-88026/queue/completed/task-getall-2.json +0 -10
- package/src/modules/persistence/__tests__/.tmp-stores-test-88026/queue/pending/task-1.json +0 -13
- package/src/modules/persistence/__tests__/.tmp-stores-test-88026/queue/pending/task-getall-1.json +0 -10
- package/src/modules/persistence/__tests__/.tmp-stores-test-88026/queue/pending/task-status-change.json +0 -10
- package/src/modules/persistence/__tests__/.tmp-stores-test-88026/workers/worker-1.json +0 -5
- package/src/modules/persistence/__tests__/.tmp-stores-test-88026/workers/worker-2.json +0 -5
- package/src/modules/persistence/__tests__/.tmp-stores-test-89475/progress.json +0 -10
- package/src/modules/persistence/__tests__/.tmp-stores-test-89475/queue/completed/task-getall-2.json +0 -10
- package/src/modules/persistence/__tests__/.tmp-stores-test-89475/queue/pending/task-1.json +0 -13
- package/src/modules/persistence/__tests__/.tmp-stores-test-89475/queue/pending/task-getall-1.json +0 -10
- package/src/modules/persistence/__tests__/.tmp-stores-test-89475/queue/pending/task-status-change.json +0 -10
- package/src/modules/persistence/__tests__/.tmp-stores-test-89475/workers/worker-1.json +0 -5
- package/src/modules/persistence/__tests__/.tmp-stores-test-89475/workers/worker-2.json +0 -5
- package/src/modules/persistence/__tests__/.tmp-stores-test-89924/progress.json +0 -10
- package/src/modules/persistence/__tests__/.tmp-stores-test-89924/queue/completed/task-getall-2.json +0 -10
- package/src/modules/persistence/__tests__/.tmp-stores-test-89924/queue/pending/task-1.json +0 -13
- package/src/modules/persistence/__tests__/.tmp-stores-test-89924/queue/pending/task-getall-1.json +0 -10
- package/src/modules/persistence/__tests__/.tmp-stores-test-89924/queue/pending/task-status-change.json +0 -10
- package/src/modules/persistence/__tests__/.tmp-stores-test-89924/workers/worker-1.json +0 -5
- package/src/modules/persistence/__tests__/.tmp-stores-test-89924/workers/worker-2.json +0 -5
|
@@ -43,6 +43,9 @@ describe("executeTddPipeline", () => {
|
|
|
43
43
|
teams: { splitter: false, reviewer: false },
|
|
44
44
|
memorySync: true,
|
|
45
45
|
dashboard: { enabled: true, port: 7333, host: "localhost" },
|
|
46
|
+
git: { autoPush: false },
|
|
47
|
+
skipCompleted: true,
|
|
48
|
+
strictTdd: true,
|
|
46
49
|
};
|
|
47
50
|
|
|
48
51
|
const mockProvider: ClaudeProvider = {
|
|
@@ -139,6 +142,9 @@ describe("executeTddPipeline", () => {
|
|
|
139
142
|
teams: { splitter: false, reviewer: false },
|
|
140
143
|
memorySync: true,
|
|
141
144
|
dashboard: { enabled: true, port: 7333, host: "localhost" },
|
|
145
|
+
git: { autoPush: false },
|
|
146
|
+
skipCompleted: true,
|
|
147
|
+
strictTdd: true,
|
|
142
148
|
};
|
|
143
149
|
|
|
144
150
|
let callCount = 0;
|
|
@@ -225,6 +231,9 @@ describe("executeTddPipeline", () => {
|
|
|
225
231
|
teams: { splitter: false, reviewer: false },
|
|
226
232
|
memorySync: true,
|
|
227
233
|
dashboard: { enabled: true, port: 7333, host: "localhost" },
|
|
234
|
+
git: { autoPush: false },
|
|
235
|
+
skipCompleted: true,
|
|
236
|
+
strictTdd: true,
|
|
228
237
|
};
|
|
229
238
|
|
|
230
239
|
const mockProvider: ClaudeProvider = {
|
|
@@ -315,6 +324,9 @@ describe("executeTddPipeline", () => {
|
|
|
315
324
|
teams: { splitter: false, reviewer: false },
|
|
316
325
|
memorySync: true,
|
|
317
326
|
dashboard: { enabled: true, port: 7333, host: "localhost" },
|
|
327
|
+
git: { autoPush: false },
|
|
328
|
+
skipCompleted: true,
|
|
329
|
+
strictTdd: true,
|
|
318
330
|
};
|
|
319
331
|
|
|
320
332
|
let phaseCount = 0;
|
|
@@ -416,6 +428,9 @@ describe("executeTddPipeline", () => {
|
|
|
416
428
|
teams: { splitter: false, reviewer: false },
|
|
417
429
|
memorySync: true,
|
|
418
430
|
dashboard: { enabled: true, port: 7333, host: "localhost" },
|
|
431
|
+
git: { autoPush: false },
|
|
432
|
+
skipCompleted: true,
|
|
433
|
+
strictTdd: true,
|
|
419
434
|
};
|
|
420
435
|
|
|
421
436
|
const mockProvider: ClaudeProvider = {
|
|
@@ -512,6 +527,9 @@ describe("executeTddPipeline", () => {
|
|
|
512
527
|
teams: { splitter: false, reviewer: false },
|
|
513
528
|
memorySync: true,
|
|
514
529
|
dashboard: { enabled: true, port: 7333, host: "localhost" },
|
|
530
|
+
git: { autoPush: false },
|
|
531
|
+
skipCompleted: true,
|
|
532
|
+
strictTdd: true,
|
|
515
533
|
};
|
|
516
534
|
|
|
517
535
|
let callCount = 0;
|
|
@@ -572,6 +590,9 @@ describe("executeTddPipeline", () => {
|
|
|
572
590
|
teams: { splitter: false, reviewer: false },
|
|
573
591
|
memorySync: true,
|
|
574
592
|
dashboard: { enabled: true, port: 7333, host: "localhost" },
|
|
593
|
+
git: { autoPush: false },
|
|
594
|
+
skipCompleted: true,
|
|
595
|
+
strictTdd: true,
|
|
575
596
|
};
|
|
576
597
|
|
|
577
598
|
const mockProvider: ClaudeProvider = {
|
|
@@ -629,6 +650,9 @@ describe("executeTddPipeline", () => {
|
|
|
629
650
|
teams: { splitter: false, reviewer: false },
|
|
630
651
|
memorySync: true,
|
|
631
652
|
dashboard: { enabled: true, port: 7333, host: "localhost" },
|
|
653
|
+
git: { autoPush: false },
|
|
654
|
+
skipCompleted: true,
|
|
655
|
+
strictTdd: true,
|
|
632
656
|
};
|
|
633
657
|
|
|
634
658
|
const mockProvider: ClaudeProvider = {
|
|
@@ -687,6 +711,9 @@ describe("executeTddPipeline", () => {
|
|
|
687
711
|
teams: { splitter: false, reviewer: false },
|
|
688
712
|
memorySync: true,
|
|
689
713
|
dashboard: { enabled: true, port: 7333, host: "localhost" },
|
|
714
|
+
git: { autoPush: false },
|
|
715
|
+
skipCompleted: true,
|
|
716
|
+
strictTdd: true,
|
|
690
717
|
};
|
|
691
718
|
|
|
692
719
|
const mockProvider: ClaudeProvider = {
|
|
@@ -738,6 +765,9 @@ describe("executeTddPipeline", () => {
|
|
|
738
765
|
teams: { splitter: false, reviewer: false },
|
|
739
766
|
memorySync: true,
|
|
740
767
|
dashboard: { enabled: true, port: 7333, host: "localhost" },
|
|
768
|
+
git: { autoPush: false },
|
|
769
|
+
skipCompleted: true,
|
|
770
|
+
strictTdd: true,
|
|
741
771
|
};
|
|
742
772
|
|
|
743
773
|
const mockProvider: ClaudeProvider = {
|
|
@@ -757,4 +787,470 @@ describe("executeTddPipeline", () => {
|
|
|
757
787
|
expect(result.status).toBe("failed");
|
|
758
788
|
expect(result.error).toBe("Unknown error");
|
|
759
789
|
});
|
|
790
|
+
|
|
791
|
+
test("emits warn log events when commit fails (invalid workspace)", async () => {
|
|
792
|
+
const task: Task = {
|
|
793
|
+
taskId: createTaskId("task-commit-warn"),
|
|
794
|
+
title: "Commit warning test",
|
|
795
|
+
description: "Test warn logs on commit failure",
|
|
796
|
+
filesToModify: [],
|
|
797
|
+
dependsOn: [],
|
|
798
|
+
priority: 1,
|
|
799
|
+
status: "running",
|
|
800
|
+
retryCount: 0,
|
|
801
|
+
};
|
|
802
|
+
|
|
803
|
+
// workspace.path が無効 → git add/commit は全て失敗する想定
|
|
804
|
+
const workspace: WorkspaceInfo = {
|
|
805
|
+
path: "/nonexistent-invalid-workspace",
|
|
806
|
+
language: "typescript",
|
|
807
|
+
packageManager: "bun",
|
|
808
|
+
framework: "hono",
|
|
809
|
+
testFramework: "bun-test",
|
|
810
|
+
};
|
|
811
|
+
|
|
812
|
+
const config: Config = {
|
|
813
|
+
workers: { num: 2, max: 8 },
|
|
814
|
+
models: {},
|
|
815
|
+
timeouts: { claude: 1200, test: 600, staleTask: 5400 },
|
|
816
|
+
retry: { maxRetries: 2 },
|
|
817
|
+
debug: false,
|
|
818
|
+
adaptiveEffort: false,
|
|
819
|
+
teams: { splitter: false, reviewer: false },
|
|
820
|
+
memorySync: true,
|
|
821
|
+
dashboard: { enabled: true, port: 7333, host: "localhost" },
|
|
822
|
+
git: { autoPush: false },
|
|
823
|
+
skipCompleted: true,
|
|
824
|
+
strictTdd: true,
|
|
825
|
+
};
|
|
826
|
+
|
|
827
|
+
const mockProvider: ClaudeProvider = {
|
|
828
|
+
async call(): Promise<ClaudeResponse> {
|
|
829
|
+
return { result: "OK", exitCode: 0, model: "claude-sonnet-4-5", effortLevel: "medium", duration: 1000 };
|
|
830
|
+
},
|
|
831
|
+
};
|
|
832
|
+
|
|
833
|
+
const mockMergeService = {
|
|
834
|
+
async mergeToParent(): Promise<MergeResult> {
|
|
835
|
+
return { success: true, message: "Merged" };
|
|
836
|
+
},
|
|
837
|
+
} as unknown as MergeService;
|
|
838
|
+
|
|
839
|
+
const events: Array<{ type: string; entry?: { level?: string; message?: string } }> = [];
|
|
840
|
+
const mockEventBus = {
|
|
841
|
+
on() {},
|
|
842
|
+
off() {},
|
|
843
|
+
emit(event: any) {
|
|
844
|
+
events.push(event);
|
|
845
|
+
},
|
|
846
|
+
} as unknown as EventBus;
|
|
847
|
+
|
|
848
|
+
const mockSpawner = {
|
|
849
|
+
async spawn() {
|
|
850
|
+
return { exitCode: 0, stdout: "OK", stderr: "" };
|
|
851
|
+
},
|
|
852
|
+
};
|
|
853
|
+
|
|
854
|
+
await executeTddPipeline(
|
|
855
|
+
task, workspace, "branch", "main", "/parent", createRunId("run-cw"), config,
|
|
856
|
+
mockProvider, mockMergeService, mockEventBus, mockSpawner
|
|
857
|
+
);
|
|
858
|
+
|
|
859
|
+
const warnEvents = events.filter(
|
|
860
|
+
(e) => e.type === "log:entry" && e.entry?.level === "warn"
|
|
861
|
+
);
|
|
862
|
+
|
|
863
|
+
// Red/Green 両フェーズのコミット失敗 warn が発火
|
|
864
|
+
expect(warnEvents.length).toBeGreaterThanOrEqual(2);
|
|
865
|
+
expect(warnEvents.some((e) => e.entry?.message?.includes("Red phase"))).toBe(true);
|
|
866
|
+
expect(warnEvents.some((e) => e.entry?.message?.includes("Green phase"))).toBe(true);
|
|
867
|
+
});
|
|
868
|
+
|
|
869
|
+
test("skips pipeline when tests already pass (pre-check succeeds)", async () => {
|
|
870
|
+
const task: Task = {
|
|
871
|
+
taskId: createTaskId("task-skip-001"),
|
|
872
|
+
title: "Already passing task",
|
|
873
|
+
description: "Tests already pass",
|
|
874
|
+
filesToModify: ["src/feature.ts"],
|
|
875
|
+
dependsOn: [],
|
|
876
|
+
priority: 1,
|
|
877
|
+
status: "running",
|
|
878
|
+
retryCount: 0,
|
|
879
|
+
};
|
|
880
|
+
|
|
881
|
+
const workspace: WorkspaceInfo = {
|
|
882
|
+
path: "/workspace",
|
|
883
|
+
language: "typescript",
|
|
884
|
+
packageManager: "bun",
|
|
885
|
+
framework: "hono",
|
|
886
|
+
testFramework: "bun-test",
|
|
887
|
+
};
|
|
888
|
+
|
|
889
|
+
const config: Config = {
|
|
890
|
+
workers: { num: 2, max: 8 },
|
|
891
|
+
models: {},
|
|
892
|
+
timeouts: { claude: 1200, test: 600, staleTask: 5400 },
|
|
893
|
+
retry: { maxRetries: 2 },
|
|
894
|
+
debug: false,
|
|
895
|
+
adaptiveEffort: false,
|
|
896
|
+
teams: { splitter: false, reviewer: false },
|
|
897
|
+
memorySync: true,
|
|
898
|
+
dashboard: { enabled: true, port: 7333, host: "localhost" },
|
|
899
|
+
git: { autoPush: false },
|
|
900
|
+
skipCompleted: true,
|
|
901
|
+
strictTdd: false,
|
|
902
|
+
};
|
|
903
|
+
|
|
904
|
+
let claudeCallCount = 0;
|
|
905
|
+
const mockProvider: ClaudeProvider = {
|
|
906
|
+
async call(): Promise<ClaudeResponse> {
|
|
907
|
+
claudeCallCount++;
|
|
908
|
+
return {
|
|
909
|
+
result: "Should not be called",
|
|
910
|
+
exitCode: 0,
|
|
911
|
+
model: "claude-sonnet-4-5",
|
|
912
|
+
effortLevel: "medium",
|
|
913
|
+
duration: 1000,
|
|
914
|
+
};
|
|
915
|
+
},
|
|
916
|
+
};
|
|
917
|
+
|
|
918
|
+
const mockMergeService = {
|
|
919
|
+
async mergeToParent(): Promise<MergeResult> {
|
|
920
|
+
throw new Error("Should not reach merge");
|
|
921
|
+
},
|
|
922
|
+
} as unknown as MergeService;
|
|
923
|
+
|
|
924
|
+
const events: string[] = [];
|
|
925
|
+
const mockEventBus = {
|
|
926
|
+
on() {},
|
|
927
|
+
off() {},
|
|
928
|
+
emit(event: any) {
|
|
929
|
+
events.push(event.type);
|
|
930
|
+
},
|
|
931
|
+
} as unknown as EventBus;
|
|
932
|
+
|
|
933
|
+
// Pre-check always passes
|
|
934
|
+
const mockSpawner = {
|
|
935
|
+
async spawn() {
|
|
936
|
+
return { exitCode: 0, stdout: "All tests passed", stderr: "" };
|
|
937
|
+
},
|
|
938
|
+
};
|
|
939
|
+
|
|
940
|
+
const result = await executeTddPipeline(
|
|
941
|
+
task, workspace, "branch", "main", "/parent", createRunId("run-skip"), config,
|
|
942
|
+
mockProvider, mockMergeService, mockEventBus, mockSpawner
|
|
943
|
+
);
|
|
944
|
+
|
|
945
|
+
expect(result.status).toBe("completed");
|
|
946
|
+
expect(result.skipped).toBe(true);
|
|
947
|
+
expect(result.phasesExecuted).toEqual(["pre-check"]);
|
|
948
|
+
expect(claudeCallCount).toBe(0); // No Claude calls made
|
|
949
|
+
expect(events).toContain("execution:skipped");
|
|
950
|
+
});
|
|
951
|
+
|
|
952
|
+
test("runs full pipeline when tests fail pre-check", async () => {
|
|
953
|
+
const task: Task = {
|
|
954
|
+
taskId: createTaskId("task-run-full"),
|
|
955
|
+
title: "Tests need work",
|
|
956
|
+
description: "Pre-check fails, run full pipeline",
|
|
957
|
+
filesToModify: [],
|
|
958
|
+
dependsOn: [],
|
|
959
|
+
priority: 1,
|
|
960
|
+
status: "running",
|
|
961
|
+
retryCount: 0,
|
|
962
|
+
};
|
|
963
|
+
|
|
964
|
+
const workspace: WorkspaceInfo = {
|
|
965
|
+
path: "/workspace",
|
|
966
|
+
language: "typescript",
|
|
967
|
+
packageManager: "bun",
|
|
968
|
+
framework: "hono",
|
|
969
|
+
testFramework: "bun-test",
|
|
970
|
+
};
|
|
971
|
+
|
|
972
|
+
const config: Config = {
|
|
973
|
+
workers: { num: 2, max: 8 },
|
|
974
|
+
models: {},
|
|
975
|
+
timeouts: { claude: 1200, test: 600, staleTask: 5400 },
|
|
976
|
+
retry: { maxRetries: 2 },
|
|
977
|
+
debug: false,
|
|
978
|
+
adaptiveEffort: false,
|
|
979
|
+
teams: { splitter: false, reviewer: false },
|
|
980
|
+
memorySync: true,
|
|
981
|
+
dashboard: { enabled: true, port: 7333, host: "localhost" },
|
|
982
|
+
git: { autoPush: false },
|
|
983
|
+
skipCompleted: true,
|
|
984
|
+
strictTdd: false,
|
|
985
|
+
};
|
|
986
|
+
|
|
987
|
+
const mockProvider: ClaudeProvider = {
|
|
988
|
+
async call(): Promise<ClaudeResponse> {
|
|
989
|
+
return {
|
|
990
|
+
result: "OK",
|
|
991
|
+
exitCode: 0,
|
|
992
|
+
model: "claude-sonnet-4-5",
|
|
993
|
+
effortLevel: "medium",
|
|
994
|
+
duration: 1000,
|
|
995
|
+
};
|
|
996
|
+
},
|
|
997
|
+
};
|
|
998
|
+
|
|
999
|
+
const mockMergeService = {
|
|
1000
|
+
async mergeToParent(): Promise<MergeResult> {
|
|
1001
|
+
return { success: true };
|
|
1002
|
+
},
|
|
1003
|
+
} as unknown as MergeService;
|
|
1004
|
+
|
|
1005
|
+
const events: string[] = [];
|
|
1006
|
+
const mockEventBus = {
|
|
1007
|
+
on() {},
|
|
1008
|
+
off() {},
|
|
1009
|
+
emit(event: any) {
|
|
1010
|
+
events.push(event.type);
|
|
1011
|
+
},
|
|
1012
|
+
} as unknown as EventBus;
|
|
1013
|
+
|
|
1014
|
+
let precheckDone = false;
|
|
1015
|
+
const mockSpawner = {
|
|
1016
|
+
async spawn() {
|
|
1017
|
+
if (!precheckDone) {
|
|
1018
|
+
precheckDone = true;
|
|
1019
|
+
return { exitCode: 1, stdout: "", stderr: "Tests failed" };
|
|
1020
|
+
}
|
|
1021
|
+
return { exitCode: 0, stdout: "OK", stderr: "" };
|
|
1022
|
+
},
|
|
1023
|
+
};
|
|
1024
|
+
|
|
1025
|
+
const result = await executeTddPipeline(
|
|
1026
|
+
task, workspace, "branch", "main", "/parent", createRunId("run-full"), config,
|
|
1027
|
+
mockProvider, mockMergeService, mockEventBus, mockSpawner
|
|
1028
|
+
);
|
|
1029
|
+
|
|
1030
|
+
expect(result.status).toBe("completed");
|
|
1031
|
+
expect(result.skipped).toBeUndefined();
|
|
1032
|
+
expect(events).toContain("execution:phase:started");
|
|
1033
|
+
expect(events).not.toContain("execution:skipped");
|
|
1034
|
+
});
|
|
1035
|
+
|
|
1036
|
+
test("respects strictTdd flag to skip pre-check", async () => {
|
|
1037
|
+
const task: Task = {
|
|
1038
|
+
taskId: createTaskId("task-strict"),
|
|
1039
|
+
title: "Strict TDD mode",
|
|
1040
|
+
description: "Always run full pipeline",
|
|
1041
|
+
filesToModify: [],
|
|
1042
|
+
dependsOn: [],
|
|
1043
|
+
priority: 1,
|
|
1044
|
+
status: "running",
|
|
1045
|
+
retryCount: 0,
|
|
1046
|
+
};
|
|
1047
|
+
|
|
1048
|
+
const workspace: WorkspaceInfo = {
|
|
1049
|
+
path: "/workspace",
|
|
1050
|
+
language: "typescript",
|
|
1051
|
+
packageManager: "bun",
|
|
1052
|
+
framework: "hono",
|
|
1053
|
+
testFramework: "bun-test",
|
|
1054
|
+
};
|
|
1055
|
+
|
|
1056
|
+
const config: Config = {
|
|
1057
|
+
workers: { num: 2, max: 8 },
|
|
1058
|
+
models: {},
|
|
1059
|
+
timeouts: { claude: 1200, test: 600, staleTask: 5400 },
|
|
1060
|
+
retry: { maxRetries: 2 },
|
|
1061
|
+
debug: false,
|
|
1062
|
+
adaptiveEffort: false,
|
|
1063
|
+
teams: { splitter: false, reviewer: false },
|
|
1064
|
+
memorySync: true,
|
|
1065
|
+
dashboard: { enabled: true, port: 7333, host: "localhost" },
|
|
1066
|
+
git: { autoPush: false },
|
|
1067
|
+
skipCompleted: true,
|
|
1068
|
+
strictTdd: true,
|
|
1069
|
+
};
|
|
1070
|
+
|
|
1071
|
+
const mockProvider: ClaudeProvider = {
|
|
1072
|
+
async call(): Promise<ClaudeResponse> {
|
|
1073
|
+
return {
|
|
1074
|
+
result: "OK",
|
|
1075
|
+
exitCode: 0,
|
|
1076
|
+
model: "claude-sonnet-4-5",
|
|
1077
|
+
effortLevel: "medium",
|
|
1078
|
+
duration: 1000,
|
|
1079
|
+
};
|
|
1080
|
+
},
|
|
1081
|
+
};
|
|
1082
|
+
|
|
1083
|
+
const mockMergeService = {
|
|
1084
|
+
async mergeToParent(): Promise<MergeResult> {
|
|
1085
|
+
return { success: true };
|
|
1086
|
+
},
|
|
1087
|
+
} as unknown as MergeService;
|
|
1088
|
+
|
|
1089
|
+
const events: string[] = [];
|
|
1090
|
+
const mockEventBus = {
|
|
1091
|
+
on() {},
|
|
1092
|
+
off() {},
|
|
1093
|
+
emit(event: any) {
|
|
1094
|
+
events.push(event.type);
|
|
1095
|
+
},
|
|
1096
|
+
} as unknown as EventBus;
|
|
1097
|
+
|
|
1098
|
+
const mockSpawner = {
|
|
1099
|
+
async spawn() {
|
|
1100
|
+
return { exitCode: 0, stdout: "OK", stderr: "" };
|
|
1101
|
+
},
|
|
1102
|
+
};
|
|
1103
|
+
|
|
1104
|
+
const result = await executeTddPipeline(
|
|
1105
|
+
task, workspace, "branch", "main", "/parent", createRunId("run-strict"), config,
|
|
1106
|
+
mockProvider, mockMergeService, mockEventBus, mockSpawner
|
|
1107
|
+
);
|
|
1108
|
+
|
|
1109
|
+
expect(result.status).toBe("completed");
|
|
1110
|
+
expect(result.skipped).toBeUndefined();
|
|
1111
|
+
expect(events).toContain("execution:phase:started");
|
|
1112
|
+
expect(events).not.toContain("execution:skipped");
|
|
1113
|
+
});
|
|
1114
|
+
|
|
1115
|
+
test("emits execution:skipped event when skipping", async () => {
|
|
1116
|
+
const task: Task = {
|
|
1117
|
+
taskId: createTaskId("task-event-skip"),
|
|
1118
|
+
title: "Event test",
|
|
1119
|
+
description: "Test skip event",
|
|
1120
|
+
filesToModify: [],
|
|
1121
|
+
dependsOn: [],
|
|
1122
|
+
priority: 1,
|
|
1123
|
+
status: "running",
|
|
1124
|
+
retryCount: 0,
|
|
1125
|
+
};
|
|
1126
|
+
|
|
1127
|
+
const workspace: WorkspaceInfo = {
|
|
1128
|
+
path: "/workspace",
|
|
1129
|
+
language: "typescript",
|
|
1130
|
+
packageManager: "bun",
|
|
1131
|
+
framework: "hono",
|
|
1132
|
+
testFramework: "bun-test",
|
|
1133
|
+
};
|
|
1134
|
+
|
|
1135
|
+
const config: Config = {
|
|
1136
|
+
workers: { num: 2, max: 8 },
|
|
1137
|
+
models: {},
|
|
1138
|
+
timeouts: { claude: 1200, test: 600, staleTask: 5400 },
|
|
1139
|
+
retry: { maxRetries: 2 },
|
|
1140
|
+
debug: false,
|
|
1141
|
+
adaptiveEffort: false,
|
|
1142
|
+
teams: { splitter: false, reviewer: false },
|
|
1143
|
+
memorySync: true,
|
|
1144
|
+
dashboard: { enabled: true, port: 7333, host: "localhost" },
|
|
1145
|
+
git: { autoPush: false },
|
|
1146
|
+
skipCompleted: true,
|
|
1147
|
+
strictTdd: false,
|
|
1148
|
+
};
|
|
1149
|
+
|
|
1150
|
+
const mockProvider: ClaudeProvider = {
|
|
1151
|
+
async call(): Promise<ClaudeResponse> {
|
|
1152
|
+
throw new Error("Should not be called");
|
|
1153
|
+
},
|
|
1154
|
+
};
|
|
1155
|
+
|
|
1156
|
+
const mockMergeService = {
|
|
1157
|
+
async mergeToParent(): Promise<MergeResult> {
|
|
1158
|
+
throw new Error("Should not reach merge");
|
|
1159
|
+
},
|
|
1160
|
+
} as unknown as MergeService;
|
|
1161
|
+
|
|
1162
|
+
const events: Array<{ type: string; taskId?: string }> = [];
|
|
1163
|
+
const mockEventBus = {
|
|
1164
|
+
on() {},
|
|
1165
|
+
off() {},
|
|
1166
|
+
emit(event: any) {
|
|
1167
|
+
events.push(event);
|
|
1168
|
+
},
|
|
1169
|
+
} as unknown as EventBus;
|
|
1170
|
+
|
|
1171
|
+
const mockSpawner = {
|
|
1172
|
+
async spawn() {
|
|
1173
|
+
return { exitCode: 0, stdout: "Tests pass", stderr: "" };
|
|
1174
|
+
},
|
|
1175
|
+
};
|
|
1176
|
+
|
|
1177
|
+
await executeTddPipeline(
|
|
1178
|
+
task, workspace, "branch", "main", "/parent", createRunId("run-evt"), config,
|
|
1179
|
+
mockProvider, mockMergeService, mockEventBus, mockSpawner
|
|
1180
|
+
);
|
|
1181
|
+
|
|
1182
|
+
const skipEvent = events.find((e) => e.type === "execution:skipped");
|
|
1183
|
+
expect(skipEvent).toBeDefined();
|
|
1184
|
+
expect(skipEvent?.taskId).toBe(task.taskId);
|
|
1185
|
+
});
|
|
1186
|
+
|
|
1187
|
+
test("includes skipped flag in result when skipped", async () => {
|
|
1188
|
+
const task: Task = {
|
|
1189
|
+
taskId: createTaskId("task-result-flag"),
|
|
1190
|
+
title: "Result flag test",
|
|
1191
|
+
description: "Test result structure",
|
|
1192
|
+
filesToModify: [],
|
|
1193
|
+
dependsOn: [],
|
|
1194
|
+
priority: 1,
|
|
1195
|
+
status: "running",
|
|
1196
|
+
retryCount: 0,
|
|
1197
|
+
};
|
|
1198
|
+
|
|
1199
|
+
const workspace: WorkspaceInfo = {
|
|
1200
|
+
path: "/workspace",
|
|
1201
|
+
language: "typescript",
|
|
1202
|
+
packageManager: "bun",
|
|
1203
|
+
framework: "hono",
|
|
1204
|
+
testFramework: "bun-test",
|
|
1205
|
+
};
|
|
1206
|
+
|
|
1207
|
+
const config: Config = {
|
|
1208
|
+
workers: { num: 2, max: 8 },
|
|
1209
|
+
models: {},
|
|
1210
|
+
timeouts: { claude: 1200, test: 600, staleTask: 5400 },
|
|
1211
|
+
retry: { maxRetries: 2 },
|
|
1212
|
+
debug: false,
|
|
1213
|
+
adaptiveEffort: false,
|
|
1214
|
+
teams: { splitter: false, reviewer: false },
|
|
1215
|
+
memorySync: true,
|
|
1216
|
+
dashboard: { enabled: true, port: 7333, host: "localhost" },
|
|
1217
|
+
git: { autoPush: false },
|
|
1218
|
+
skipCompleted: true,
|
|
1219
|
+
strictTdd: false,
|
|
1220
|
+
};
|
|
1221
|
+
|
|
1222
|
+
const mockProvider: ClaudeProvider = {
|
|
1223
|
+
async call(): Promise<ClaudeResponse> {
|
|
1224
|
+
throw new Error("Should not be called");
|
|
1225
|
+
},
|
|
1226
|
+
};
|
|
1227
|
+
|
|
1228
|
+
const mockMergeService = {
|
|
1229
|
+
async mergeToParent(): Promise<MergeResult> {
|
|
1230
|
+
throw new Error("Should not reach merge");
|
|
1231
|
+
},
|
|
1232
|
+
} as unknown as MergeService;
|
|
1233
|
+
|
|
1234
|
+
const mockEventBus = {
|
|
1235
|
+
on() {},
|
|
1236
|
+
off() {},
|
|
1237
|
+
emit() {},
|
|
1238
|
+
} as unknown as EventBus;
|
|
1239
|
+
|
|
1240
|
+
const mockSpawner = {
|
|
1241
|
+
async spawn() {
|
|
1242
|
+
return { exitCode: 0, stdout: "Pass", stderr: "" };
|
|
1243
|
+
},
|
|
1244
|
+
};
|
|
1245
|
+
|
|
1246
|
+
const result = await executeTddPipeline(
|
|
1247
|
+
task, workspace, "branch", "main", "/parent", createRunId("run-flag"), config,
|
|
1248
|
+
mockProvider, mockMergeService, mockEventBus, mockSpawner
|
|
1249
|
+
);
|
|
1250
|
+
|
|
1251
|
+
expect(result.status).toBe("completed");
|
|
1252
|
+
expect(result.skipped).toBe(true);
|
|
1253
|
+
expect(result.phasesExecuted).toEqual(["pre-check"]);
|
|
1254
|
+
expect(result.duration).toBeGreaterThanOrEqual(0);
|
|
1255
|
+
});
|
|
760
1256
|
});
|
|
@@ -200,16 +200,17 @@ describe("buildTestCommand", () => {
|
|
|
200
200
|
expect(buildTestCommand(workspace)).toEqual(["./gradlew", "test"]);
|
|
201
201
|
});
|
|
202
202
|
|
|
203
|
-
test("
|
|
203
|
+
test("returns fallback for unknown test framework", () => {
|
|
204
204
|
const workspace: WorkspaceInfo = {
|
|
205
205
|
path: "/path/to/workspace",
|
|
206
206
|
language: "unknown",
|
|
207
|
-
packageManager: "
|
|
207
|
+
packageManager: "npm",
|
|
208
208
|
framework: "unknown",
|
|
209
209
|
testFramework: "unknown",
|
|
210
210
|
};
|
|
211
211
|
|
|
212
|
-
|
|
212
|
+
// After fallback implementation, unknown should return npm test
|
|
213
|
+
expect(buildTestCommand(workspace)).toEqual(["npm", "test"]);
|
|
213
214
|
});
|
|
214
215
|
});
|
|
215
216
|
|