@mclawnet/scheduler 0.1.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.
Files changed (97) hide show
  1. package/dist/__tests__/executor-oneshot.test.d.ts +2 -0
  2. package/dist/__tests__/executor-oneshot.test.d.ts.map +1 -0
  3. package/dist/__tests__/executor-oneshot.test.js +141 -0
  4. package/dist/__tests__/executor-oneshot.test.js.map +1 -0
  5. package/dist/__tests__/executor-swarm.test.d.ts +2 -0
  6. package/dist/__tests__/executor-swarm.test.d.ts.map +1 -0
  7. package/dist/__tests__/executor-swarm.test.js +142 -0
  8. package/dist/__tests__/executor-swarm.test.js.map +1 -0
  9. package/dist/__tests__/json-schedule-repository-prompt-cap.test.d.ts +2 -0
  10. package/dist/__tests__/json-schedule-repository-prompt-cap.test.d.ts.map +1 -0
  11. package/dist/__tests__/json-schedule-repository-prompt-cap.test.js +64 -0
  12. package/dist/__tests__/json-schedule-repository-prompt-cap.test.js.map +1 -0
  13. package/dist/__tests__/json-schedule-repository.test.d.ts +2 -0
  14. package/dist/__tests__/json-schedule-repository.test.d.ts.map +1 -0
  15. package/dist/__tests__/json-schedule-repository.test.js +178 -0
  16. package/dist/__tests__/json-schedule-repository.test.js.map +1 -0
  17. package/dist/__tests__/paths.test.d.ts +2 -0
  18. package/dist/__tests__/paths.test.d.ts.map +1 -0
  19. package/dist/__tests__/paths.test.js +19 -0
  20. package/dist/__tests__/paths.test.js.map +1 -0
  21. package/dist/__tests__/scheduler-maxrun-cap.test.d.ts +2 -0
  22. package/dist/__tests__/scheduler-maxrun-cap.test.d.ts.map +1 -0
  23. package/dist/__tests__/scheduler-maxrun-cap.test.js +109 -0
  24. package/dist/__tests__/scheduler-maxrun-cap.test.js.map +1 -0
  25. package/dist/__tests__/scheduler-service-events-typed.test.d.ts +2 -0
  26. package/dist/__tests__/scheduler-service-events-typed.test.d.ts.map +1 -0
  27. package/dist/__tests__/scheduler-service-events-typed.test.js +27 -0
  28. package/dist/__tests__/scheduler-service-events-typed.test.js.map +1 -0
  29. package/dist/__tests__/scheduler-service-register.test.d.ts +2 -0
  30. package/dist/__tests__/scheduler-service-register.test.d.ts.map +1 -0
  31. package/dist/__tests__/scheduler-service-register.test.js +54 -0
  32. package/dist/__tests__/scheduler-service-register.test.js.map +1 -0
  33. package/dist/__tests__/scheduler-service-tick-23.test.d.ts +2 -0
  34. package/dist/__tests__/scheduler-service-tick-23.test.d.ts.map +1 -0
  35. package/dist/__tests__/scheduler-service-tick-23.test.js +72 -0
  36. package/dist/__tests__/scheduler-service-tick-23.test.js.map +1 -0
  37. package/dist/__tests__/scheduler-service-tick-24.test.d.ts +2 -0
  38. package/dist/__tests__/scheduler-service-tick-24.test.d.ts.map +1 -0
  39. package/dist/__tests__/scheduler-service-tick-24.test.js +80 -0
  40. package/dist/__tests__/scheduler-service-tick-24.test.js.map +1 -0
  41. package/dist/__tests__/scheduler-service-tick-25.test.d.ts +2 -0
  42. package/dist/__tests__/scheduler-service-tick-25.test.d.ts.map +1 -0
  43. package/dist/__tests__/scheduler-service-tick-25.test.js +111 -0
  44. package/dist/__tests__/scheduler-service-tick-25.test.js.map +1 -0
  45. package/dist/__tests__/scheduler-service-tick-26.test.d.ts +2 -0
  46. package/dist/__tests__/scheduler-service-tick-26.test.d.ts.map +1 -0
  47. package/dist/__tests__/scheduler-service-tick-26.test.js +97 -0
  48. package/dist/__tests__/scheduler-service-tick-26.test.js.map +1 -0
  49. package/dist/__tests__/scheduler-service-tick-27.test.d.ts +2 -0
  50. package/dist/__tests__/scheduler-service-tick-27.test.d.ts.map +1 -0
  51. package/dist/__tests__/scheduler-service-tick-27.test.js +69 -0
  52. package/dist/__tests__/scheduler-service-tick-27.test.js.map +1 -0
  53. package/dist/__tests__/scheduler-service-tick-coalesce.test.d.ts +2 -0
  54. package/dist/__tests__/scheduler-service-tick-coalesce.test.d.ts.map +1 -0
  55. package/dist/__tests__/scheduler-service-tick-coalesce.test.js +119 -0
  56. package/dist/__tests__/scheduler-service-tick-coalesce.test.js.map +1 -0
  57. package/dist/__tests__/scheduler-service-trigger-gating.test.d.ts +2 -0
  58. package/dist/__tests__/scheduler-service-trigger-gating.test.d.ts.map +1 -0
  59. package/dist/__tests__/scheduler-service-trigger-gating.test.js +55 -0
  60. package/dist/__tests__/scheduler-service-trigger-gating.test.js.map +1 -0
  61. package/dist/__tests__/tick-helpers.d.ts +17 -0
  62. package/dist/__tests__/tick-helpers.d.ts.map +1 -0
  63. package/dist/__tests__/tick-helpers.js +97 -0
  64. package/dist/__tests__/tick-helpers.js.map +1 -0
  65. package/dist/executor.d.ts +20 -0
  66. package/dist/executor.d.ts.map +1 -0
  67. package/dist/executor.js +2 -0
  68. package/dist/executor.js.map +1 -0
  69. package/dist/executors/oneshot.d.ts +22 -0
  70. package/dist/executors/oneshot.d.ts.map +1 -0
  71. package/dist/executors/oneshot.js +55 -0
  72. package/dist/executors/oneshot.js.map +1 -0
  73. package/dist/executors/swarm.d.ts +26 -0
  74. package/dist/executors/swarm.d.ts.map +1 -0
  75. package/dist/executors/swarm.js +40 -0
  76. package/dist/executors/swarm.js.map +1 -0
  77. package/dist/index.d.ts +11 -0
  78. package/dist/index.d.ts.map +1 -0
  79. package/dist/index.js +8 -0
  80. package/dist/index.js.map +1 -0
  81. package/dist/json-schedule-repository.d.ts +39 -0
  82. package/dist/json-schedule-repository.d.ts.map +1 -0
  83. package/dist/json-schedule-repository.js +374 -0
  84. package/dist/json-schedule-repository.js.map +1 -0
  85. package/dist/paths.d.ts +7 -0
  86. package/dist/paths.d.ts.map +1 -0
  87. package/dist/paths.js +22 -0
  88. package/dist/paths.js.map +1 -0
  89. package/dist/scheduler-service.d.ts +71 -0
  90. package/dist/scheduler-service.d.ts.map +1 -0
  91. package/dist/scheduler-service.js +395 -0
  92. package/dist/scheduler-service.js.map +1 -0
  93. package/dist/types.d.ts +92 -0
  94. package/dist/types.d.ts.map +1 -0
  95. package/dist/types.js +8 -0
  96. package/dist/types.js.map +1 -0
  97. package/package.json +36 -0
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=scheduler-maxrun-cap.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scheduler-maxrun-cap.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/scheduler-maxrun-cap.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,109 @@
1
+ import { describe, it, expect, beforeEach, afterEach, vi } from "vitest";
2
+ import { mkdtempSync, readFileSync, writeFileSync } from "node:fs";
3
+ import { tmpdir } from "node:os";
4
+ import { join } from "node:path";
5
+ import FakeTimers from "@sinonjs/fake-timers";
6
+ import { JsonScheduleRepository } from "../json-schedule-repository.js";
7
+ import { SchedulerService } from "../scheduler-service.js";
8
+ import { HARD_MAX_RUN_MS } from "../types.js";
9
+ import { makeDeferredExecutor, makeImmediateExecutor, drainFactory, } from "./tick-helpers.js";
10
+ let home;
11
+ let repo;
12
+ const sample = (over = {}) => ({
13
+ agentId: "agent-A",
14
+ workDir: "/tmp/proj-cap-maxrun",
15
+ label: "cap test",
16
+ mode: "oneshot",
17
+ cronExpression: "0 9 * * *",
18
+ timezone: "Asia/Shanghai",
19
+ launchConfig: { prompt: "hi" },
20
+ ...over,
21
+ });
22
+ describe("JsonScheduleRepository — launchConfig.maxRunMs cap (repo)", () => {
23
+ beforeEach(() => {
24
+ home = mkdtempSync(join(tmpdir(), "sched-cap-maxrun-"));
25
+ repo = new JsonScheduleRepository({ home });
26
+ });
27
+ it("create accepts maxRunMs at exactly HARD_MAX_RUN_MS", async () => {
28
+ const s = await repo.create(sample({
29
+ launchConfig: { prompt: "hi", maxRunMs: HARD_MAX_RUN_MS },
30
+ }));
31
+ expect(s.launchConfig.maxRunMs).toBe(HARD_MAX_RUN_MS);
32
+ });
33
+ it("create rejects maxRunMs at HARD_MAX_RUN_MS + 1 with MAX_RUN_MS_TOO_LARGE", async () => {
34
+ await expect(repo.create(sample({
35
+ launchConfig: { prompt: "hi", maxRunMs: HARD_MAX_RUN_MS + 1 },
36
+ }))).rejects.toThrow("MAX_RUN_MS_TOO_LARGE");
37
+ });
38
+ it("update rejects oversize patch.launchConfig.maxRunMs with MAX_RUN_MS_TOO_LARGE", async () => {
39
+ const s = await repo.create(sample());
40
+ await expect(repo.update(s.id, {
41
+ launchConfig: { maxRunMs: HARD_MAX_RUN_MS + 1 },
42
+ })).rejects.toThrow("MAX_RUN_MS_TOO_LARGE");
43
+ });
44
+ it("update without launchConfig.maxRunMs does not validate", async () => {
45
+ const s = await repo.create(sample());
46
+ const updated = await repo.update(s.id, { label: "renamed-2" });
47
+ expect(updated.label).toBe("renamed-2");
48
+ });
49
+ });
50
+ describe("SchedulerService — maxRunMs clamp (defensive against stale data)", () => {
51
+ let clock;
52
+ let drain;
53
+ let setTimeoutSpy;
54
+ beforeEach(() => {
55
+ home = mkdtempSync(join(tmpdir(), "sched-clamp-"));
56
+ repo = new JsonScheduleRepository({ home });
57
+ clock = FakeTimers.install({
58
+ now: new Date("2026-04-30T00:00:00.000Z"),
59
+ shouldAdvanceTime: true,
60
+ advanceTimeDelta: 20,
61
+ });
62
+ drain = drainFactory(clock);
63
+ // Spy AFTER FakeTimers installs the fake setTimeout so we observe the
64
+ // exact delay argument the scheduler asks for, independent of how the
65
+ // clock advances during drain.
66
+ setTimeoutSpy = vi.spyOn(globalThis, "setTimeout");
67
+ });
68
+ afterEach(() => {
69
+ setTimeoutSpy.mockRestore();
70
+ clock.uninstall();
71
+ });
72
+ it("clamps an out-of-bounds on-disk maxRunMs to HARD_MAX_RUN_MS when arming the timeout", async () => {
73
+ // Bypass repo validation by writing the bad value directly to the on-disk
74
+ // schedule file, mimicking a row written before the cap landed (defence in
75
+ // depth for the service path that always re-reads from disk).
76
+ const s = await repo.create(sample({
77
+ workDir: "/tmp/proj-clamp",
78
+ cronExpression: "* * * * *",
79
+ timezone: "UTC",
80
+ launchConfig: { prompt: "hi" },
81
+ }));
82
+ const schedulePath = join(home, ".clawnet/projects/-tmp-proj-clamp/schedules", `${s.id}.json`);
83
+ const onDisk = JSON.parse(readFileSync(schedulePath, "utf-8"));
84
+ onDisk.launchConfig.maxRunMs = HARD_MAX_RUN_MS + 100_000;
85
+ writeFileSync(schedulePath, JSON.stringify(onDisk, null, 2));
86
+ const exec = makeDeferredExecutor();
87
+ const svc = new SchedulerService(repo, {
88
+ oneshot: exec,
89
+ swarm: makeImmediateExecutor({ exitCode: 0 }),
90
+ });
91
+ svc.register(s);
92
+ // Fire one cron tick → executor enters the deferred state with the timeout
93
+ // timer armed.
94
+ await clock.tickAsync(60_000);
95
+ await drain();
96
+ expect(exec.invocations).toHaveLength(1);
97
+ // Find any setTimeout call whose delay equals the raw uncapped value —
98
+ // that is the smoking gun for "no clamp". Conversely, the maximum delay
99
+ // observed across all setTimeout calls must not exceed HARD_MAX_RUN_MS.
100
+ const delays = setTimeoutSpy.mock.calls.map((c) => Number(c[1] ?? 0));
101
+ const maxDelay = delays.reduce((a, b) => Math.max(a, b), 0);
102
+ expect(delays).not.toContain(HARD_MAX_RUN_MS + 100_000);
103
+ expect(maxDelay).toBeLessThanOrEqual(HARD_MAX_RUN_MS);
104
+ exec.resolveNext({ exitCode: 0 });
105
+ await drain();
106
+ await svc.stop();
107
+ });
108
+ });
109
+ //# sourceMappingURL=scheduler-maxrun-cap.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scheduler-maxrun-cap.test.js","sourceRoot":"","sources":["../../src/__tests__/scheduler-maxrun-cap.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=scheduler-service-events-typed.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scheduler-service-events-typed.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/scheduler-service-events-typed.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,27 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { SchedulerService } from "../scheduler-service.js";
3
+ describe("SchedulerService.events typed emitter (M-1)", () => {
4
+ it("on('change') / on('run') accept the right listener shapes (compile-time)", () => {
5
+ const svc = new SchedulerService({
6
+ recoverInterruptedRuns: async () => { },
7
+ listAll: async () => [],
8
+ }, {});
9
+ // These should typecheck cleanly — no manual cast required at the call
10
+ // site. (Compile failures here would surface as `pnpm test`/`pnpm build`
11
+ // failures in CI; this test mostly pins the shape so a future regression
12
+ // is loud.)
13
+ const changeListener = (s) => {
14
+ void s.id;
15
+ };
16
+ const runListener = (r) => {
17
+ void r.id;
18
+ };
19
+ svc.events.on("change", changeListener);
20
+ svc.events.on("run", runListener);
21
+ svc.events.off("change", changeListener);
22
+ svc.events.off("run", runListener);
23
+ // Sanity: emitting still works at runtime.
24
+ expect(typeof svc.events.emit).toBe("function");
25
+ });
26
+ });
27
+ //# sourceMappingURL=scheduler-service-events-typed.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scheduler-service-events-typed.test.js","sourceRoot":"","sources":["../../src/__tests__/scheduler-service-events-typed.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAG3D,QAAQ,CAAC,6CAA6C,EAAE,GAAG,EAAE;IAC3D,EAAE,CAAC,0EAA0E,EAAE,GAAG,EAAE;QAClF,MAAM,GAAG,GAAG,IAAI,gBAAgB,CAC9B;YACE,sBAAsB,EAAE,KAAK,IAAI,EAAE,GAAE,CAAC;YACtC,OAAO,EAAE,KAAK,IAAI,EAAE,CAAC,EAAE;SACf,EACV,EAAW,CACZ,CAAC;QAEF,uEAAuE;QACvE,yEAAyE;QACzE,yEAAyE;QACzE,YAAY;QACZ,MAAM,cAAc,GAAG,CAAC,CAAW,EAAQ,EAAE;YAC3C,KAAK,CAAC,CAAC,EAAE,CAAC;QACZ,CAAC,CAAC;QACF,MAAM,WAAW,GAAG,CAAC,CAAc,EAAQ,EAAE;YAC3C,KAAK,CAAC,CAAC,EAAE,CAAC;QACZ,CAAC,CAAC;QACF,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,QAAQ,EAAE,cAAc,CAAC,CAAC;QACxC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,WAAW,CAAC,CAAC;QAClC,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,QAAQ,EAAE,cAAc,CAAC,CAAC;QACzC,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,EAAE,WAAW,CAAC,CAAC;QAEnC,2CAA2C;QAC3C,MAAM,CAAC,OAAO,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;IAClD,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=scheduler-service-register.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scheduler-service-register.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/scheduler-service-register.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,54 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { SchedulerService } from "../scheduler-service.js";
3
+ const sched = (over = {}) => ({
4
+ id: over.id ?? "s1",
5
+ agentId: "agent-A",
6
+ workDir: "/tmp/proj",
7
+ encodedCwd: "-tmp-proj",
8
+ label: "L",
9
+ mode: "oneshot",
10
+ cronExpression: "0 9 * * *",
11
+ timezone: "UTC",
12
+ status: "active",
13
+ consecutiveFailures: 0,
14
+ maxConsecutiveFailures: 5,
15
+ createdAt: "2026-01-01T00:00:00.000Z",
16
+ updatedAt: "2026-01-01T00:00:00.000Z",
17
+ launchConfig: { prompt: "hi" },
18
+ ...over,
19
+ });
20
+ describe("SchedulerService register lifecycle", () => {
21
+ it("start registers active schedules and skips paused", async () => {
22
+ const fakeRepo = {
23
+ recoverInterruptedRuns: async () => { },
24
+ listAll: async () => [
25
+ sched({ id: "active1" }),
26
+ sched({ id: "active2", cronExpression: "*/5 * * * *" }),
27
+ sched({ id: "paused1", status: "paused" }),
28
+ ],
29
+ };
30
+ const svc = new SchedulerService(fakeRepo, {});
31
+ await svc.start();
32
+ expect(svc.getRegistered()).toEqual(expect.arrayContaining(["active1", "active2"]));
33
+ expect(svc.getRegistered()).not.toContain("paused1");
34
+ expect(svc.getRegistered()).toHaveLength(2);
35
+ await svc.stop();
36
+ expect(svc.getRegistered()).toHaveLength(0);
37
+ });
38
+ it("register replaces an existing job for the same id", async () => {
39
+ const svc = new SchedulerService({ recoverInterruptedRuns: async () => { }, listAll: async () => [] }, {});
40
+ svc.register(sched({ id: "x", cronExpression: "0 9 * * *" }));
41
+ svc.register(sched({ id: "x", cronExpression: "0 10 * * *" })); // re-register
42
+ expect(svc.getRegistered()).toEqual(["x"]);
43
+ await svc.stop();
44
+ });
45
+ it("unregister removes a job", async () => {
46
+ const svc = new SchedulerService({ recoverInterruptedRuns: async () => { }, listAll: async () => [] }, {});
47
+ svc.register(sched({ id: "y" }));
48
+ expect(svc.getRegistered()).toContain("y");
49
+ svc.unregister("y");
50
+ expect(svc.getRegistered()).not.toContain("y");
51
+ await svc.stop();
52
+ });
53
+ });
54
+ //# sourceMappingURL=scheduler-service-register.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scheduler-service-register.test.js","sourceRoot":"","sources":["../../src/__tests__/scheduler-service-register.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=scheduler-service-tick-23.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scheduler-service-tick-23.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/scheduler-service-tick-23.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,72 @@
1
+ import { describe, it, expect, beforeEach, afterEach } from "vitest";
2
+ import { mkdtempSync } from "node:fs";
3
+ import { tmpdir } from "node:os";
4
+ import { join } from "node:path";
5
+ import FakeTimers from "@sinonjs/fake-timers";
6
+ import { SchedulerService } from "../scheduler-service.js";
7
+ import { JsonScheduleRepository } from "../json-schedule-repository.js";
8
+ import { makeDeferredExecutor, makeImmediateExecutor, sample, drainFactory, } from "./tick-helpers.js";
9
+ let home;
10
+ let repo;
11
+ let clock;
12
+ let drain;
13
+ beforeEach(() => {
14
+ home = mkdtempSync(join(tmpdir(), "sched-tick-"));
15
+ repo = new JsonScheduleRepository({ home });
16
+ clock = FakeTimers.install({
17
+ now: new Date("2026-04-30T00:00:00.000Z"),
18
+ shouldAdvanceTime: true,
19
+ advanceTimeDelta: 20,
20
+ });
21
+ drain = drainFactory(clock);
22
+ });
23
+ afterEach(() => {
24
+ clock.uninstall();
25
+ });
26
+ describe("SchedulerService.tick — single-flight (Task 2.3)", () => {
27
+ it("invokes executor exactly once per cron fire and persists a completed run", async () => {
28
+ const s = await repo.create(sample({ workDir: "/tmp/proj-tick-23a" }));
29
+ const exec = makeImmediateExecutor({ exitCode: 0, summary: "done" });
30
+ const svc = new SchedulerService(repo, {
31
+ oneshot: exec,
32
+ swarm: makeImmediateExecutor({ exitCode: 0 }),
33
+ });
34
+ await svc.start();
35
+ expect(svc.getRegistered()).toContain(s.id);
36
+ await clock.tickAsync(60_000);
37
+ await drain();
38
+ expect(exec.invocations).toHaveLength(1);
39
+ const runs = await repo.listRuns(s.id);
40
+ expect(runs).toHaveLength(1);
41
+ expect(runs[0].status).toBe("completed");
42
+ expect(runs[0].exitCode).toBe(0);
43
+ expect(runs[0].summary).toBe("done");
44
+ await svc.stop();
45
+ });
46
+ it("records second tick as cancelled with previous_run_in_progress while first still in flight", async () => {
47
+ const s = await repo.create(sample({ workDir: "/tmp/proj-tick-23b" }));
48
+ const exec = makeDeferredExecutor();
49
+ const svc = new SchedulerService(repo, {
50
+ oneshot: exec,
51
+ swarm: makeImmediateExecutor({ exitCode: 0 }),
52
+ });
53
+ await svc.start();
54
+ await clock.tickAsync(60_000);
55
+ await drain();
56
+ expect(exec.invocations).toHaveLength(1);
57
+ await clock.tickAsync(60_000);
58
+ await drain();
59
+ expect(exec.invocations).toHaveLength(1); // single-flight: no new call
60
+ const runs = await repo.listRuns(s.id);
61
+ expect(runs).toHaveLength(2);
62
+ const cancelled = runs.find(r => r.status === "cancelled");
63
+ expect(cancelled?.error).toBe("previous_run_in_progress");
64
+ exec.resolveNext({ exitCode: 0 });
65
+ await drain();
66
+ const after = await repo.listRuns(s.id);
67
+ const completed = after.find(r => r.status === "completed");
68
+ expect(completed?.exitCode).toBe(0);
69
+ await svc.stop();
70
+ });
71
+ });
72
+ //# sourceMappingURL=scheduler-service-tick-23.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scheduler-service-tick-23.test.js","sourceRoot":"","sources":["../../src/__tests__/scheduler-service-tick-23.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=scheduler-service-tick-24.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scheduler-service-tick-24.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/scheduler-service-tick-24.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,80 @@
1
+ import { describe, it, expect, beforeEach, afterEach } from "vitest";
2
+ import { mkdtempSync } from "node:fs";
3
+ import { tmpdir } from "node:os";
4
+ import { join } from "node:path";
5
+ import FakeTimers from "@sinonjs/fake-timers";
6
+ import { SchedulerService } from "../scheduler-service.js";
7
+ import { JsonScheduleRepository } from "../json-schedule-repository.js";
8
+ import { makeDeferredExecutor, makeImmediateExecutor, sample, drainFactory, } from "./tick-helpers.js";
9
+ let home;
10
+ let repo;
11
+ let clock;
12
+ let drain;
13
+ beforeEach(() => {
14
+ home = mkdtempSync(join(tmpdir(), "sched-tick-"));
15
+ repo = new JsonScheduleRepository({ home });
16
+ clock = FakeTimers.install({
17
+ now: new Date("2026-04-30T00:00:00.000Z"),
18
+ shouldAdvanceTime: true,
19
+ advanceTimeDelta: 20,
20
+ });
21
+ drain = drainFactory(clock);
22
+ });
23
+ afterEach(() => {
24
+ clock.uninstall();
25
+ });
26
+ describe("SchedulerService.tick — cwdLock with retry (Task 2.4)", () => {
27
+ it("retries 60s later and cancels with cwd_busy if still locked", async () => {
28
+ // Two schedules sharing the same workDir; cron fires both at the */5
29
+ // boundary. First one wins the cwd lock; the second must be deferred ~60s
30
+ // and recorded as cancelled with cwd_busy when the lock is still held.
31
+ const s1 = await repo.create(sample({ workDir: "/tmp/proj-tick-24a", cronExpression: "*/5 * * * *" }));
32
+ const s2 = await repo.create(sample({ workDir: "/tmp/proj-tick-24a", cronExpression: "*/5 * * * *" }));
33
+ const exec1 = makeDeferredExecutor();
34
+ const svc = new SchedulerService(repo, {
35
+ oneshot: exec1,
36
+ swarm: makeImmediateExecutor({ exitCode: 0 }),
37
+ });
38
+ await svc.start();
39
+ await clock.tickAsync(5 * 60_000);
40
+ await drain();
41
+ expect(exec1.invocations).toHaveLength(1);
42
+ const inFlightId = exec1.invocations[0].schedule.id;
43
+ const otherId = inFlightId === s1.id ? s2.id : s1.id;
44
+ // Advance ~60s so the cwd retry timer fires; cwd still busy → cancelled.
45
+ await clock.tickAsync(60_000);
46
+ await drain();
47
+ const otherRuns = await repo.listRuns(otherId);
48
+ expect(otherRuns.some(r => r.status === "cancelled" && r.error === "cwd_busy")).toBe(true);
49
+ expect(exec1.invocations).toHaveLength(1);
50
+ exec1.resolveNext({ exitCode: 0 });
51
+ await drain();
52
+ await svc.stop();
53
+ });
54
+ it("retry succeeds when the cwd has been released before the timer fires", async () => {
55
+ const s1 = await repo.create(sample({ workDir: "/tmp/proj-tick-24b", cronExpression: "*/5 * * * *" }));
56
+ const s2 = await repo.create(sample({ workDir: "/tmp/proj-tick-24b", cronExpression: "*/5 * * * *" }));
57
+ const exec = makeDeferredExecutor();
58
+ const svc = new SchedulerService(repo, {
59
+ oneshot: exec,
60
+ swarm: makeImmediateExecutor({ exitCode: 0 }),
61
+ });
62
+ await svc.start();
63
+ await clock.tickAsync(5 * 60_000);
64
+ await drain();
65
+ expect(exec.invocations).toHaveLength(1);
66
+ // Release the first run before the cwd retry timer fires.
67
+ exec.resolveNext({ exitCode: 0 });
68
+ await drain();
69
+ // Retry fires (~60s after second tick was originally enqueued).
70
+ await clock.tickAsync(60_000);
71
+ await drain();
72
+ expect(exec.invocations).toHaveLength(2);
73
+ exec.resolveNext({ exitCode: 0 });
74
+ await drain();
75
+ const totalRuns = (await repo.listRuns(s1.id)).length + (await repo.listRuns(s2.id)).length;
76
+ expect(totalRuns).toBe(2);
77
+ await svc.stop();
78
+ });
79
+ });
80
+ //# sourceMappingURL=scheduler-service-tick-24.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scheduler-service-tick-24.test.js","sourceRoot":"","sources":["../../src/__tests__/scheduler-service-tick-24.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=scheduler-service-tick-25.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scheduler-service-tick-25.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/scheduler-service-tick-25.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,111 @@
1
+ import { describe, it, expect, beforeEach, afterEach } from "vitest";
2
+ import { mkdtempSync } from "node:fs";
3
+ import { tmpdir } from "node:os";
4
+ import { join } from "node:path";
5
+ import FakeTimers from "@sinonjs/fake-timers";
6
+ import { SchedulerService } from "../scheduler-service.js";
7
+ import { JsonScheduleRepository } from "../json-schedule-repository.js";
8
+ import { makeDeferredExecutor, makeImmediateExecutor, sample, drainFactory, drainUntilFactory, } from "./tick-helpers.js";
9
+ let home;
10
+ let repo;
11
+ let clock;
12
+ let drain;
13
+ let drainUntil;
14
+ beforeEach(() => {
15
+ home = mkdtempSync(join(tmpdir(), "sched-tick-"));
16
+ repo = new JsonScheduleRepository({ home });
17
+ clock = FakeTimers.install({
18
+ now: new Date("2026-04-30T00:00:00.000Z"),
19
+ shouldAdvanceTime: true,
20
+ advanceTimeDelta: 20,
21
+ });
22
+ drain = drainFactory(clock);
23
+ drainUntil = drainUntilFactory(clock);
24
+ });
25
+ afterEach(() => {
26
+ clock.uninstall();
27
+ });
28
+ describe("SchedulerService.tick — failure circuit breaker (Task 2.5)", () => {
29
+ it("auto-pauses after maxConsecutiveFailures reached and unregisters cron", async () => {
30
+ const s = await repo.create(sample({ workDir: "/tmp/proj-tick-25a", maxConsecutiveFailures: 3 }));
31
+ const exec = makeImmediateExecutor({ exitCode: 1, error: "boom" });
32
+ const svc = new SchedulerService(repo, {
33
+ oneshot: exec,
34
+ swarm: makeImmediateExecutor({ exitCode: 0 }),
35
+ });
36
+ let lastChange = {};
37
+ let changeCount = 0;
38
+ svc.events.on("change", (sched) => {
39
+ lastChange = sched;
40
+ changeCount += 1;
41
+ });
42
+ await svc.start();
43
+ for (let i = 0; i < 3; i++) {
44
+ await clock.tickAsync(60_000);
45
+ // Wait for THIS tick's change event before advancing the clock again,
46
+ // so we never accidentally cross another 60s cron boundary while a
47
+ // proper-lockfile retry chain is still in flight on a slow runner.
48
+ await drainUntil(() => changeCount >= i + 1);
49
+ }
50
+ const reloaded = await repo.getById(s.id);
51
+ expect(reloaded?.status).toBe("paused");
52
+ expect(reloaded?.consecutiveFailures).toBe(3);
53
+ expect(svc.getRegistered()).not.toContain(s.id);
54
+ expect(lastChange.id).toBe(s.id);
55
+ expect(lastChange.status).toBe("paused");
56
+ await svc.stop();
57
+ });
58
+ it("resets counter to zero after a successful run", async () => {
59
+ const s = await repo.create(sample({ workDir: "/tmp/proj-tick-25b", maxConsecutiveFailures: 5 }));
60
+ const exec = {
61
+ mode: "oneshot",
62
+ calls: 0,
63
+ async execute() {
64
+ this.calls += 1;
65
+ if (this.calls <= 4)
66
+ return { exitCode: 1, error: "fail" };
67
+ return { exitCode: 0 };
68
+ },
69
+ };
70
+ const svc = new SchedulerService(repo, {
71
+ oneshot: exec,
72
+ swarm: makeImmediateExecutor({ exitCode: 0 }),
73
+ });
74
+ let changeCount = 0;
75
+ svc.events.on("change", () => { changeCount += 1; });
76
+ await svc.start();
77
+ for (let i = 0; i < 5; i++) {
78
+ await clock.tickAsync(60_000);
79
+ await drainUntil(() => changeCount >= i + 1);
80
+ }
81
+ const reloaded = await repo.getById(s.id);
82
+ expect(reloaded?.status).toBe("active");
83
+ expect(reloaded?.consecutiveFailures).toBe(0);
84
+ expect(svc.getRegistered()).toContain(s.id);
85
+ await svc.stop();
86
+ });
87
+ it("cancelled runs do not increment consecutiveFailures", async () => {
88
+ const s = await repo.create(sample({ workDir: "/tmp/proj-tick-25c", maxConsecutiveFailures: 2 }));
89
+ const exec = makeDeferredExecutor();
90
+ const svc = new SchedulerService(repo, {
91
+ oneshot: exec,
92
+ swarm: makeImmediateExecutor({ exitCode: 0 }),
93
+ });
94
+ await svc.start();
95
+ // Tick three times: first kicks off the deferred run, the next two are
96
+ // single-flight cancellations. consecutiveFailures must stay at 0.
97
+ await clock.tickAsync(60_000);
98
+ await drain();
99
+ await clock.tickAsync(60_000);
100
+ await drain();
101
+ await clock.tickAsync(60_000);
102
+ await drain();
103
+ const reloaded = await repo.getById(s.id);
104
+ expect(reloaded?.consecutiveFailures).toBe(0);
105
+ expect(reloaded?.status).toBe("active");
106
+ exec.resolveNext({ exitCode: 0 });
107
+ await drain();
108
+ await svc.stop();
109
+ });
110
+ });
111
+ //# sourceMappingURL=scheduler-service-tick-25.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scheduler-service-tick-25.test.js","sourceRoot":"","sources":["../../src/__tests__/scheduler-service-tick-25.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=scheduler-service-tick-26.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scheduler-service-tick-26.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/scheduler-service-tick-26.test.ts"],"names":[],"mappings":""}