@urateam/core 0.1.32 → 0.1.34

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 (132) hide show
  1. package/dist/__tests__/audit-immutability.test.js +50 -1
  2. package/dist/__tests__/audit-immutability.test.js.map +1 -1
  3. package/dist/__tests__/auth-monitor.test.d.ts +2 -0
  4. package/dist/__tests__/auth-monitor.test.d.ts.map +1 -0
  5. package/dist/__tests__/auth-monitor.test.js +253 -0
  6. package/dist/__tests__/auth-monitor.test.js.map +1 -0
  7. package/dist/__tests__/bec-186-repro.test.d.ts +16 -0
  8. package/dist/__tests__/bec-186-repro.test.d.ts.map +1 -0
  9. package/dist/__tests__/bec-186-repro.test.js +223 -0
  10. package/dist/__tests__/bec-186-repro.test.js.map +1 -0
  11. package/dist/__tests__/db-migrations.test.d.ts +2 -0
  12. package/dist/__tests__/db-migrations.test.d.ts.map +1 -0
  13. package/dist/__tests__/db-migrations.test.js +237 -0
  14. package/dist/__tests__/db-migrations.test.js.map +1 -0
  15. package/dist/__tests__/executor-issue-id.test.js +2 -0
  16. package/dist/__tests__/executor-issue-id.test.js.map +1 -1
  17. package/dist/__tests__/pm-scheduler.test.js +59 -0
  18. package/dist/__tests__/pm-scheduler.test.js.map +1 -1
  19. package/dist/__tests__/post-fanout-comments.test.js +36 -0
  20. package/dist/__tests__/post-fanout-comments.test.js.map +1 -1
  21. package/dist/__tests__/preflight-claude-auth.test.d.ts +2 -0
  22. package/dist/__tests__/preflight-claude-auth.test.d.ts.map +1 -0
  23. package/dist/__tests__/preflight-claude-auth.test.js +36 -0
  24. package/dist/__tests__/preflight-claude-auth.test.js.map +1 -0
  25. package/dist/__tests__/resolve-claude-auth.test.d.ts +2 -0
  26. package/dist/__tests__/resolve-claude-auth.test.d.ts.map +1 -0
  27. package/dist/__tests__/resolve-claude-auth.test.js +129 -0
  28. package/dist/__tests__/resolve-claude-auth.test.js.map +1 -0
  29. package/dist/__tests__/runner-retry-strategies.test.d.ts +23 -0
  30. package/dist/__tests__/runner-retry-strategies.test.d.ts.map +1 -0
  31. package/dist/__tests__/runner-retry-strategies.test.js +274 -0
  32. package/dist/__tests__/runner-retry-strategies.test.js.map +1 -0
  33. package/dist/__tests__/scratch-file-guard.test.d.ts +2 -0
  34. package/dist/__tests__/scratch-file-guard.test.d.ts.map +1 -0
  35. package/dist/__tests__/scratch-file-guard.test.js +144 -0
  36. package/dist/__tests__/scratch-file-guard.test.js.map +1 -0
  37. package/dist/__tests__/spec-vs-impl-gate.test.d.ts +2 -0
  38. package/dist/__tests__/spec-vs-impl-gate.test.d.ts.map +1 -0
  39. package/dist/__tests__/spec-vs-impl-gate.test.js +222 -0
  40. package/dist/__tests__/spec-vs-impl-gate.test.js.map +1 -0
  41. package/dist/__tests__/stage-models.test.js +4 -0
  42. package/dist/__tests__/stage-models.test.js.map +1 -1
  43. package/dist/__tests__/typecheck-gate.test.d.ts +2 -0
  44. package/dist/__tests__/typecheck-gate.test.d.ts.map +1 -0
  45. package/dist/__tests__/typecheck-gate.test.js +196 -0
  46. package/dist/__tests__/typecheck-gate.test.js.map +1 -0
  47. package/dist/audit/events.d.ts +52 -0
  48. package/dist/audit/events.d.ts.map +1 -1
  49. package/dist/audit/events.js +81 -0
  50. package/dist/audit/events.js.map +1 -1
  51. package/dist/db/client.d.ts.map +1 -1
  52. package/dist/db/client.js +8 -0
  53. package/dist/db/client.js.map +1 -1
  54. package/dist/db/migrations/postgres/014_missing_indexes.sql +28 -0
  55. package/dist/db/migrations/sqlite/013_missing_indexes.sql +28 -0
  56. package/dist/executor/auth-check.d.ts +39 -0
  57. package/dist/executor/auth-check.d.ts.map +1 -1
  58. package/dist/executor/auth-check.js +31 -0
  59. package/dist/executor/auth-check.js.map +1 -1
  60. package/dist/executor/auth-monitor.d.ts +40 -0
  61. package/dist/executor/auth-monitor.d.ts.map +1 -0
  62. package/dist/executor/auth-monitor.js +114 -0
  63. package/dist/executor/auth-monitor.js.map +1 -0
  64. package/dist/executor/executor.d.ts.map +1 -1
  65. package/dist/executor/executor.js +12 -26
  66. package/dist/executor/executor.js.map +1 -1
  67. package/dist/executor/index.d.ts +2 -0
  68. package/dist/executor/index.d.ts.map +1 -1
  69. package/dist/executor/index.js +2 -0
  70. package/dist/executor/index.js.map +1 -1
  71. package/dist/executor/review/post-fanout-comments.d.ts +8 -0
  72. package/dist/executor/review/post-fanout-comments.d.ts.map +1 -1
  73. package/dist/executor/review/post-fanout-comments.js +23 -3
  74. package/dist/executor/review/post-fanout-comments.js.map +1 -1
  75. package/dist/index.d.ts +2 -1
  76. package/dist/index.d.ts.map +1 -1
  77. package/dist/index.js +2 -1
  78. package/dist/index.js.map +1 -1
  79. package/dist/notifier/composite.d.ts +1 -0
  80. package/dist/notifier/composite.d.ts.map +1 -1
  81. package/dist/notifier/composite.js +3 -0
  82. package/dist/notifier/composite.js.map +1 -1
  83. package/dist/notifier/linear.d.ts +2 -0
  84. package/dist/notifier/linear.d.ts.map +1 -1
  85. package/dist/notifier/linear.js +12 -2
  86. package/dist/notifier/linear.js.map +1 -1
  87. package/dist/pipeline/runner.d.ts.map +1 -1
  88. package/dist/pipeline/runner.js +152 -2
  89. package/dist/pipeline/runner.js.map +1 -1
  90. package/dist/pipeline/scratch-file-guard.d.ts +21 -0
  91. package/dist/pipeline/scratch-file-guard.d.ts.map +1 -0
  92. package/dist/pipeline/scratch-file-guard.js +155 -0
  93. package/dist/pipeline/scratch-file-guard.js.map +1 -0
  94. package/dist/pipeline/spec-vs-impl-gate.d.ts +49 -0
  95. package/dist/pipeline/spec-vs-impl-gate.d.ts.map +1 -0
  96. package/dist/pipeline/spec-vs-impl-gate.js +177 -0
  97. package/dist/pipeline/spec-vs-impl-gate.js.map +1 -0
  98. package/dist/pipeline/typecheck-gate.d.ts +34 -0
  99. package/dist/pipeline/typecheck-gate.d.ts.map +1 -0
  100. package/dist/pipeline/typecheck-gate.js +89 -0
  101. package/dist/pipeline/typecheck-gate.js.map +1 -0
  102. package/dist/pm/scheduler.d.ts.map +1 -1
  103. package/dist/pm/scheduler.js +19 -0
  104. package/dist/pm/scheduler.js.map +1 -1
  105. package/dist/release-manager/index.d.ts +2 -0
  106. package/dist/release-manager/index.d.ts.map +1 -1
  107. package/dist/release-manager/index.js +2 -0
  108. package/dist/release-manager/index.js.map +1 -1
  109. package/dist/release-manager/release-helpers.d.ts +112 -0
  110. package/dist/release-manager/release-helpers.d.ts.map +1 -0
  111. package/dist/release-manager/release-helpers.js +164 -0
  112. package/dist/release-manager/release-helpers.js.map +1 -0
  113. package/dist/release-manager/release-tick.d.ts +101 -0
  114. package/dist/release-manager/release-tick.d.ts.map +1 -0
  115. package/dist/release-manager/release-tick.js +374 -0
  116. package/dist/release-manager/release-tick.js.map +1 -0
  117. package/dist/release-manager/scheduler.d.ts +28 -3
  118. package/dist/release-manager/scheduler.d.ts.map +1 -1
  119. package/dist/release-manager/scheduler.js +41 -417
  120. package/dist/release-manager/scheduler.js.map +1 -1
  121. package/dist/server.d.ts.map +1 -1
  122. package/dist/server.js +3 -0
  123. package/dist/server.js.map +1 -1
  124. package/dist/types.d.ts +15 -0
  125. package/dist/types.d.ts.map +1 -1
  126. package/dist/types.js +17 -0
  127. package/dist/types.js.map +1 -1
  128. package/dist/webhook/github-handler.d.ts +7 -1
  129. package/dist/webhook/github-handler.d.ts.map +1 -1
  130. package/dist/webhook/github-handler.js +82 -38
  131. package/dist/webhook/github-handler.js.map +1 -1
  132. package/package.json +1 -1
@@ -0,0 +1,129 @@
1
+ /**
2
+ * Tests for resolveClaudeAuth() and the updated isClaudeAuthValid() env-var
3
+ * short-circuit (BEC-207).
4
+ */
5
+ import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
6
+ import { resolveClaudeAuth, isClaudeAuthValid, resetAuthCheckCache } from "../executor/auth-check.js";
7
+ // Mock child_process.execFile to control subprocess behaviour
8
+ vi.mock("node:child_process", () => ({
9
+ execFile: vi.fn(),
10
+ }));
11
+ import { execFile } from "node:child_process";
12
+ const mockExecFile = vi.mocked(execFile);
13
+ function simulateCliSuccess() {
14
+ mockExecFile.mockImplementation((_cmd, _args, _opts, cb) => {
15
+ cb(null);
16
+ return {};
17
+ });
18
+ }
19
+ function simulateCliFailure() {
20
+ mockExecFile.mockImplementation((_cmd, _args, _opts, cb) => {
21
+ cb(new Error("auth failed"));
22
+ return {};
23
+ });
24
+ }
25
+ // --- resolveClaudeAuth ---
26
+ describe("resolveClaudeAuth", () => {
27
+ const origEnv = { ...process.env };
28
+ afterEach(() => {
29
+ // Restore env between tests
30
+ for (const k of ["CLAUDE_CODE_OAUTH_TOKEN", "ANTHROPIC_API_KEY"]) {
31
+ if (origEnv[k] === undefined) {
32
+ delete process.env[k];
33
+ }
34
+ else {
35
+ process.env[k] = origEnv[k];
36
+ }
37
+ }
38
+ });
39
+ it("returns oauth-token method when CLAUDE_CODE_OAUTH_TOKEN is set", () => {
40
+ process.env.CLAUDE_CODE_OAUTH_TOKEN = "sk-ant-oat-testtoken";
41
+ delete process.env.ANTHROPIC_API_KEY;
42
+ const result = resolveClaudeAuth();
43
+ expect(result.method).toBe("oauth-token");
44
+ // The credential value is intentionally NOT exposed in the result struct
45
+ // (see ClaudeAuthCredentials JSDoc for rationale).
46
+ expect(result.oauthToken).toBeUndefined();
47
+ });
48
+ it("returns api-key method when ANTHROPIC_API_KEY is set (and no oauth token)", () => {
49
+ delete process.env.CLAUDE_CODE_OAUTH_TOKEN;
50
+ process.env.ANTHROPIC_API_KEY = "sk-ant-api03-testkey";
51
+ const result = resolveClaudeAuth();
52
+ expect(result.method).toBe("api-key");
53
+ });
54
+ it("CLAUDE_CODE_OAUTH_TOKEN takes precedence over ANTHROPIC_API_KEY", () => {
55
+ process.env.CLAUDE_CODE_OAUTH_TOKEN = "sk-ant-oat-higher-priority";
56
+ process.env.ANTHROPIC_API_KEY = "sk-ant-api03-lower-priority";
57
+ const result = resolveClaudeAuth();
58
+ expect(result.method).toBe("oauth-token");
59
+ });
60
+ it("returns session method when neither env var is set", () => {
61
+ delete process.env.CLAUDE_CODE_OAUTH_TOKEN;
62
+ delete process.env.ANTHROPIC_API_KEY;
63
+ const result = resolveClaudeAuth();
64
+ expect(result.method).toBe("session");
65
+ });
66
+ it("precedence order: CLAUDE_CODE_OAUTH_TOKEN > ANTHROPIC_API_KEY > session", () => {
67
+ // All set — oauth token wins
68
+ process.env.CLAUDE_CODE_OAUTH_TOKEN = "sk-ant-oat-x";
69
+ process.env.ANTHROPIC_API_KEY = "sk-ant-api03-y";
70
+ expect(resolveClaudeAuth().method).toBe("oauth-token");
71
+ // Only API key — api-key wins
72
+ delete process.env.CLAUDE_CODE_OAUTH_TOKEN;
73
+ expect(resolveClaudeAuth().method).toBe("api-key");
74
+ // Neither — session
75
+ delete process.env.ANTHROPIC_API_KEY;
76
+ expect(resolveClaudeAuth().method).toBe("session");
77
+ });
78
+ });
79
+ // --- isClaudeAuthValid env-var short-circuit ---
80
+ describe("isClaudeAuthValid env-var short-circuit", () => {
81
+ const origEnv = { ...process.env };
82
+ beforeEach(() => {
83
+ resetAuthCheckCache();
84
+ mockExecFile.mockReset();
85
+ });
86
+ afterEach(() => {
87
+ for (const k of ["CLAUDE_CODE_OAUTH_TOKEN", "ANTHROPIC_API_KEY"]) {
88
+ if (origEnv[k] === undefined) {
89
+ delete process.env[k];
90
+ }
91
+ else {
92
+ process.env[k] = origEnv[k];
93
+ }
94
+ }
95
+ });
96
+ it("returns true immediately when CLAUDE_CODE_OAUTH_TOKEN is set — no subprocess", async () => {
97
+ process.env.CLAUDE_CODE_OAUTH_TOKEN = "sk-ant-oat-test";
98
+ delete process.env.ANTHROPIC_API_KEY;
99
+ const result = await isClaudeAuthValid();
100
+ expect(result).toBe(true);
101
+ // Must NOT have spawned a subprocess
102
+ expect(mockExecFile).not.toHaveBeenCalled();
103
+ });
104
+ it("returns true immediately when ANTHROPIC_API_KEY is set — no subprocess", async () => {
105
+ delete process.env.CLAUDE_CODE_OAUTH_TOKEN;
106
+ process.env.ANTHROPIC_API_KEY = "sk-ant-api03-test";
107
+ const result = await isClaudeAuthValid();
108
+ expect(result).toBe(true);
109
+ expect(mockExecFile).not.toHaveBeenCalled();
110
+ });
111
+ it("runs subprocess only when both env vars are absent", async () => {
112
+ delete process.env.CLAUDE_CODE_OAUTH_TOKEN;
113
+ delete process.env.ANTHROPIC_API_KEY;
114
+ simulateCliSuccess();
115
+ const result = await isClaudeAuthValid();
116
+ expect(result).toBe(true);
117
+ expect(mockExecFile).toHaveBeenCalledTimes(1);
118
+ expect(mockExecFile).toHaveBeenCalledWith("claude", ["auth", "status"], { timeout: 10_000 }, expect.any(Function));
119
+ });
120
+ it("returns false and runs subprocess when session is invalid and no env vars set", async () => {
121
+ delete process.env.CLAUDE_CODE_OAUTH_TOKEN;
122
+ delete process.env.ANTHROPIC_API_KEY;
123
+ simulateCliFailure();
124
+ const result = await isClaudeAuthValid();
125
+ expect(result).toBe(false);
126
+ expect(mockExecFile).toHaveBeenCalledTimes(1);
127
+ });
128
+ });
129
+ //# sourceMappingURL=resolve-claude-auth.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolve-claude-auth.test.js","sourceRoot":"","sources":["../../src/__tests__/resolve-claude-auth.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,23 @@
1
+ /**
2
+ * BEC-200 — runner retry-strategy tests
3
+ *
4
+ * Verifies the three documented retry strategies in `PipelineConfig.retry`:
5
+ *
6
+ * 1. `fix-and-retry` — on a failed stage, re-execute up to `maxAttempts`
7
+ * times. `stageRetries[stage]` increments on each retry. If a retry
8
+ * succeeds, the pipeline proceeds; if all retries fail, the run is
9
+ * marked retriable (per failPipeline's `retriable=true` branch).
10
+ *
11
+ * 2. `escalate` — on a failed stage, NO retries are attempted. The run
12
+ * fails immediately, marked retriable.
13
+ *
14
+ * 3. `fail-fast` — on a failed stage, NO retries. The run fails
15
+ * immediately, marked NON-retriable (no automatic recovery).
16
+ *
17
+ * Mock strategy: hijack `executeStage` to return failed/completed based on a
18
+ * per-test counter, then call `runner.start()` directly and inspect the DB.
19
+ * Avoids the webhook-driven scaffolding in ralph-gate.test.ts since retry
20
+ * logic doesn't depend on it.
21
+ */
22
+ export {};
23
+ //# sourceMappingURL=runner-retry-strategies.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runner-retry-strategies.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/runner-retry-strategies.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG"}
@@ -0,0 +1,274 @@
1
+ /**
2
+ * BEC-200 — runner retry-strategy tests
3
+ *
4
+ * Verifies the three documented retry strategies in `PipelineConfig.retry`:
5
+ *
6
+ * 1. `fix-and-retry` — on a failed stage, re-execute up to `maxAttempts`
7
+ * times. `stageRetries[stage]` increments on each retry. If a retry
8
+ * succeeds, the pipeline proceeds; if all retries fail, the run is
9
+ * marked retriable (per failPipeline's `retriable=true` branch).
10
+ *
11
+ * 2. `escalate` — on a failed stage, NO retries are attempted. The run
12
+ * fails immediately, marked retriable.
13
+ *
14
+ * 3. `fail-fast` — on a failed stage, NO retries. The run fails
15
+ * immediately, marked NON-retriable (no automatic recovery).
16
+ *
17
+ * Mock strategy: hijack `executeStage` to return failed/completed based on a
18
+ * per-test counter, then call `runner.start()` directly and inspect the DB.
19
+ * Avoids the webhook-driven scaffolding in ralph-gate.test.ts since retry
20
+ * logic doesn't depend on it.
21
+ */
22
+ import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
23
+ import { createDb } from "../db/client.js";
24
+ import { PipelineRunner } from "../pipeline/runner.js";
25
+ import { pipelineRuns } from "../db/schema.js";
26
+ import { eq } from "drizzle-orm";
27
+ import { installTestProLicense, restoreLicense } from "./helpers/license.js";
28
+ // ---------------------------------------------------------------------------
29
+ // Mock surface
30
+ // ---------------------------------------------------------------------------
31
+ const { mockCreatePRViaCli, mockExecFile } = vi.hoisted(() => ({
32
+ mockCreatePRViaCli: vi
33
+ .fn()
34
+ .mockResolvedValue("https://github.com/test/repo/pull/99"),
35
+ mockExecFile: vi.fn().mockImplementation((...args) => {
36
+ const cb = args.find((a) => typeof a === "function");
37
+ if (cb)
38
+ cb(null, "", "");
39
+ }),
40
+ }));
41
+ let stageResultsQueue = [];
42
+ let executeStageCalls = [];
43
+ function handoffFor(runId, issueId, stage) {
44
+ return {
45
+ runId,
46
+ issueId,
47
+ stage,
48
+ timestamp: new Date().toISOString(),
49
+ summary: `Stage ${stage} done`,
50
+ filesChanged: ["src/feature.ts"],
51
+ approach: `approach for ${stage}`,
52
+ context: {
53
+ issueIntent: "test retry strategies",
54
+ constraints: [],
55
+ assumptions: [],
56
+ },
57
+ tokenBudget: { contextTokensUsed: 100, recommendedMaxTurns: 3 },
58
+ };
59
+ }
60
+ vi.mock("../executor/executor.js", () => ({
61
+ executeStage: vi.fn().mockImplementation(async ({ runId, issueId, stage, db }) => {
62
+ const { nanoid } = await import("nanoid");
63
+ const { stageRuns: stageRunsTable } = await import("../db/schema.js");
64
+ const { eq: drizzleEq } = await import("drizzle-orm");
65
+ const attempt = executeStageCalls.filter((c) => c.stage === stage).length;
66
+ executeStageCalls.push({ stage, attempt });
67
+ const verdict = stageResultsQueue.shift() ?? "completed";
68
+ const stageRunId = nanoid();
69
+ await db.insert(stageRunsTable).values({
70
+ id: stageRunId,
71
+ pipelineRunId: runId,
72
+ stage,
73
+ status: "running",
74
+ });
75
+ await db
76
+ .update(stageRunsTable)
77
+ .set({
78
+ status: verdict,
79
+ completedAt: new Date(),
80
+ inputTokens: 100,
81
+ outputTokens: 50,
82
+ turns: 1,
83
+ handoffArtifact: JSON.stringify(handoffFor(runId, issueId, stage)),
84
+ errorMessage: verdict === "failed" ? "simulated stage failure" : null,
85
+ })
86
+ .where(drizzleEq(stageRunsTable.id, stageRunId));
87
+ return {
88
+ status: verdict,
89
+ inputTokens: 100,
90
+ outputTokens: 50,
91
+ turns: 1,
92
+ ...(verdict === "completed"
93
+ ? {
94
+ handoffArtifact: handoffFor(runId, issueId, stage),
95
+ handoffIsStructured: true,
96
+ }
97
+ : { errorMessage: "simulated stage failure" }),
98
+ stageRunId,
99
+ };
100
+ }),
101
+ }));
102
+ vi.mock("../repo/git.js", () => ({
103
+ cloneRepo: vi.fn().mockResolvedValue(undefined),
104
+ createWorktree: vi.fn().mockResolvedValue("/tmp/retry-strategies-worktree"),
105
+ deleteWorktree: vi.fn().mockResolvedValue(undefined),
106
+ pushBranch: vi.fn().mockResolvedValue(undefined),
107
+ pushBranchForce: vi.fn().mockResolvedValue(undefined),
108
+ rebaseBranch: vi.fn().mockResolvedValue({ success: true, hasConflicts: false }),
109
+ abortRebase: vi.fn().mockResolvedValue(undefined),
110
+ autoCommitChanges: vi.fn().mockResolvedValue(false),
111
+ createPRViaCli: mockCreatePRViaCli,
112
+ mergePRViaCli: vi.fn().mockResolvedValue(true),
113
+ getDiffLineCount: vi.fn().mockResolvedValue(50),
114
+ getChangedFiles: vi.fn().mockResolvedValue(["src/feature.ts"]),
115
+ checkDuplicateBranch: vi.fn().mockResolvedValue(null),
116
+ cleanupWorktrees: vi.fn().mockResolvedValue([]),
117
+ branchName: vi.fn().mockImplementation((id, slug) => `agent/${id}-${slug}`),
118
+ gitExecSafe: vi.fn().mockResolvedValue(""),
119
+ gitExecRaw: vi.fn().mockResolvedValue(""),
120
+ gitExec: vi.fn().mockResolvedValue(""),
121
+ choosePushStrategy: vi.fn().mockReturnValue("standard"),
122
+ getAgentCommits: vi.fn().mockResolvedValue([]),
123
+ installPrePushHook: vi.fn().mockResolvedValue(undefined),
124
+ }));
125
+ vi.mock("../executor/test-quality.js", () => ({
126
+ checkTestQuality: vi.fn().mockResolvedValue({ violations: [] }),
127
+ }));
128
+ vi.mock("../repo/tech-stack.js", () => ({
129
+ detectTechStack: vi.fn().mockResolvedValue({
130
+ languages: ["typescript"],
131
+ frameworks: [],
132
+ buildSystems: ["pnpm"],
133
+ }),
134
+ }));
135
+ vi.mock("../repo/devcontainer.js", () => ({
136
+ shouldUseDevcontainer: vi.fn().mockResolvedValue(false),
137
+ devcontainerUp: vi.fn().mockResolvedValue(undefined),
138
+ devcontainerDown: vi.fn().mockResolvedValue(undefined),
139
+ }));
140
+ vi.mock("../pm/coordination.js", () => ({
141
+ upsertActiveWork: vi.fn().mockResolvedValue(undefined),
142
+ removeActiveWork: vi.fn().mockResolvedValue(undefined),
143
+ checkFileOverlap: vi.fn().mockResolvedValue({
144
+ hasOverlap: false,
145
+ overlappingFiles: [],
146
+ conflictingRunIds: [],
147
+ }),
148
+ getModifiedFiles: vi.fn().mockResolvedValue([]),
149
+ }));
150
+ vi.mock("node:child_process", () => ({
151
+ execFile: mockExecFile,
152
+ }));
153
+ // ---------------------------------------------------------------------------
154
+ // Fixtures
155
+ // ---------------------------------------------------------------------------
156
+ const REPO_CONFIG = {
157
+ url: "https://github.com/test/test-repo.git",
158
+ defaultBranch: "main",
159
+ testCommand: "pnpm test",
160
+ buildCommand: "pnpm build",
161
+ };
162
+ function makePipelineConfig(retry, stages = ["implement"]) {
163
+ return {
164
+ name: "Auto Implement",
165
+ stages,
166
+ retry,
167
+ review: { requiredApprovals: 0 },
168
+ prStrategy: "ready",
169
+ validateHandoffs: false,
170
+ ralphIterations: 0, // disable RALPH so the retry-loop is the only re-exec path
171
+ reviewFixIterations: 0,
172
+ deepReviewPasses: 0,
173
+ };
174
+ }
175
+ const ISSUE = {
176
+ id: "issue-rs-1",
177
+ identifier: "RS-1",
178
+ title: "retry-strategies test issue",
179
+ description: "Test retry strategies",
180
+ priority: 2,
181
+ };
182
+ async function waitForRunComplete(db, issueId, timeoutMs = 5_000) {
183
+ const deadline = Date.now() + timeoutMs;
184
+ while (Date.now() < deadline) {
185
+ const rows = await db.select().from(pipelineRuns).where(eq(pipelineRuns.issueId, issueId));
186
+ if (rows.length > 0) {
187
+ const run = rows[0];
188
+ if (["completed", "failed", "retriable", "aborted"].includes(run.status)) {
189
+ return run;
190
+ }
191
+ }
192
+ await new Promise((r) => setTimeout(r, 25));
193
+ }
194
+ throw new Error(`Pipeline for ${issueId} did not finish within ${timeoutMs}ms`);
195
+ }
196
+ // ---------------------------------------------------------------------------
197
+ // Tests
198
+ // ---------------------------------------------------------------------------
199
+ describe("PipelineRunner retry strategies (BEC-200)", () => {
200
+ let db;
201
+ let notifier;
202
+ beforeEach(async () => {
203
+ await installTestProLicense();
204
+ stageResultsQueue = [];
205
+ executeStageCalls = [];
206
+ mockCreatePRViaCli.mockClear();
207
+ db = await createDb({ driver: "sqlite", connectionString: ":memory:" });
208
+ notifier = {
209
+ onPipelineStart: vi.fn(async () => { }),
210
+ onStageComplete: vi.fn(async () => { }),
211
+ onPipelineComplete: vi.fn(async () => { }),
212
+ onPipelineFailed: vi.fn(async () => { }),
213
+ };
214
+ });
215
+ afterEach(async () => {
216
+ await restoreLicense();
217
+ });
218
+ function makeRunner() {
219
+ return new PipelineRunner({
220
+ db,
221
+ notifier,
222
+ concurrency: 1,
223
+ agentRunDir: "/tmp/retry-strategies-runs",
224
+ repoCloneDir: "/tmp/retry-strategies-repos",
225
+ });
226
+ }
227
+ it("fix-and-retry: stage succeeds after one retry → stageRetries[stage] = 1, run completes", async () => {
228
+ // First implement call fails, second (retry attempt 1) succeeds.
229
+ stageResultsQueue = ["failed", "completed"];
230
+ const config = makePipelineConfig({ maxAttempts: 2, strategy: "fix-and-retry" });
231
+ const runner = makeRunner();
232
+ const issue = { ...ISSUE, identifier: "RS-1" };
233
+ await runner.start(issue, "auto-implement", config, REPO_CONFIG, issue);
234
+ const run = await waitForRunComplete(db, "RS-1");
235
+ expect(run.status).toBe("completed");
236
+ expect(executeStageCalls.filter((c) => c.stage === "implement")).toHaveLength(2);
237
+ });
238
+ it("fix-and-retry: exhausts maxAttempts → run marked retriable, stageRetries shows N retries", async () => {
239
+ // initial + 2 retries all fail
240
+ stageResultsQueue = ["failed", "failed", "failed"];
241
+ const config = makePipelineConfig({ maxAttempts: 2, strategy: "fix-and-retry" });
242
+ const runner = makeRunner();
243
+ const issue = { ...ISSUE, identifier: "RS-2" };
244
+ await runner.start(issue, "auto-implement", config, REPO_CONFIG, issue);
245
+ const run = await waitForRunComplete(db, "RS-2");
246
+ // failPipeline with retriable=true sets status="retriable" (auto-recovery candidate)
247
+ expect(["retriable", "failed"]).toContain(run.status);
248
+ // 1 initial + 2 retries
249
+ expect(executeStageCalls.filter((c) => c.stage === "implement")).toHaveLength(3);
250
+ });
251
+ it("escalate: NO retries on failure — fails immediately", async () => {
252
+ stageResultsQueue = ["failed"];
253
+ const config = makePipelineConfig({ maxAttempts: 5, strategy: "escalate" });
254
+ const runner = makeRunner();
255
+ const issue = { ...ISSUE, identifier: "RS-3" };
256
+ await runner.start(issue, "auto-implement", config, REPO_CONFIG, issue);
257
+ const run = await waitForRunComplete(db, "RS-3");
258
+ expect(["retriable", "failed"]).toContain(run.status);
259
+ // Strategy "escalate" enters the retry loop but breaks on the first
260
+ // iteration — so executeStage is called exactly once.
261
+ expect(executeStageCalls.filter((c) => c.stage === "implement")).toHaveLength(1);
262
+ });
263
+ it("fail-fast: NO retries, marks run as failed (non-retriable)", async () => {
264
+ stageResultsQueue = ["failed"];
265
+ const config = makePipelineConfig({ maxAttempts: 5, strategy: "fail-fast" });
266
+ const runner = makeRunner();
267
+ const issue = { ...ISSUE, identifier: "RS-4" };
268
+ await runner.start(issue, "auto-implement", config, REPO_CONFIG, issue);
269
+ const run = await waitForRunComplete(db, "RS-4");
270
+ expect(run.status).toBe("failed");
271
+ expect(executeStageCalls.filter((c) => c.stage === "implement")).toHaveLength(1);
272
+ });
273
+ });
274
+ //# sourceMappingURL=runner-retry-strategies.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runner-retry-strategies.test.js","sourceRoot":"","sources":["../../src/__tests__/runner-retry-strategies.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=scratch-file-guard.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scratch-file-guard.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/scratch-file-guard.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,144 @@
1
+ /**
2
+ * Tier 1a — scratch-file denylist gate.
3
+ *
4
+ * `findScratchFiles` is wired into the pipeline runner just before push and
5
+ * forces draft + a `category: "scratch-files"` blocking finding when any of
6
+ * the agent's added files match the denylist (FINAL_CHECKLIST.md,
7
+ * commit-test-changes.sh, TESTING_COMPLETE.md, *.bak, untracked *.log, etc).
8
+ *
9
+ * The matcher is a pure function so it can be tested without git. The git
10
+ * layer (`enumerateAddedFiles`) is exercised by the runner integration.
11
+ */
12
+ import { describe, it, expect, beforeEach, afterEach } from "vitest";
13
+ import { matchScratchPatterns } from "../pipeline/scratch-file-guard.js";
14
+ describe("matchScratchPatterns — fires on bad cases", () => {
15
+ it("matches FINAL_CHECKLIST.md at repo root", () => {
16
+ expect(matchScratchPatterns(["FINAL_CHECKLIST.md"])).toEqual([
17
+ "FINAL_CHECKLIST.md",
18
+ ]);
19
+ });
20
+ it("matches TESTING_COMPLETE.md at repo root (TESTING_* pattern)", () => {
21
+ expect(matchScratchPatterns(["TESTING_COMPLETE.md"])).toEqual([
22
+ "TESTING_COMPLETE.md",
23
+ ]);
24
+ });
25
+ it("matches *_REPORT.md (case-insensitive)", () => {
26
+ expect(matchScratchPatterns(["VERIFICATION_REPORT.md"])).toEqual([
27
+ "VERIFICATION_REPORT.md",
28
+ ]);
29
+ expect(matchScratchPatterns(["test_report.md"])).toEqual(["test_report.md"]);
30
+ });
31
+ it("matches TEST_*.md at repo root", () => {
32
+ expect(matchScratchPatterns(["TEST_PLAN.md"])).toEqual(["TEST_PLAN.md"]);
33
+ });
34
+ it("matches FINAL_*.md at repo root", () => {
35
+ expect(matchScratchPatterns(["FINAL_NOTES.md"])).toEqual(["FINAL_NOTES.md"]);
36
+ });
37
+ it("matches *.bak and *.bak.* anywhere", () => {
38
+ expect(matchScratchPatterns(["foo.bak"])).toEqual(["foo.bak"]);
39
+ expect(matchScratchPatterns(["src/utils.ts.bak"])).toEqual(["src/utils.ts.bak"]);
40
+ expect(matchScratchPatterns(["config.bak.20260511"])).toEqual([
41
+ "config.bak.20260511",
42
+ ]);
43
+ });
44
+ it("matches root-level commit-*.sh and run-*.sh", () => {
45
+ expect(matchScratchPatterns(["commit-test-changes.sh"])).toEqual([
46
+ "commit-test-changes.sh",
47
+ ]);
48
+ expect(matchScratchPatterns(["run-verification.sh"])).toEqual([
49
+ "run-verification.sh",
50
+ ]);
51
+ });
52
+ it("matches *.tmp anywhere", () => {
53
+ expect(matchScratchPatterns(["scratch.tmp"])).toEqual(["scratch.tmp"]);
54
+ expect(matchScratchPatterns(["src/foo.tmp"])).toEqual(["src/foo.tmp"]);
55
+ });
56
+ it("matches untracked *.log anywhere", () => {
57
+ expect(matchScratchPatterns(["debug.log"])).toEqual(["debug.log"]);
58
+ expect(matchScratchPatterns(["logs/run.log"])).toEqual(["logs/run.log"]);
59
+ });
60
+ it("matches any new repo-root *.md NOT in the exemption set", () => {
61
+ expect(matchScratchPatterns(["NOTES.md"])).toEqual(["NOTES.md"]);
62
+ expect(matchScratchPatterns(["scratch.md"])).toEqual(["scratch.md"]);
63
+ expect(matchScratchPatterns(["SUMMARY.md"])).toEqual(["SUMMARY.md"]);
64
+ });
65
+ it("returns all matches in input order, deduped", () => {
66
+ const matches = matchScratchPatterns([
67
+ "FINAL_CHECKLIST.md",
68
+ "src/foo.ts",
69
+ "commit-test.sh",
70
+ "debug.log",
71
+ ]);
72
+ expect(matches).toEqual([
73
+ "FINAL_CHECKLIST.md",
74
+ "commit-test.sh",
75
+ "debug.log",
76
+ ]);
77
+ });
78
+ });
79
+ describe("matchScratchPatterns — does NOT fire on clean diffs", () => {
80
+ it("does not match repo-root README.md (tracked exemption)", () => {
81
+ expect(matchScratchPatterns(["README.md"])).toEqual([]);
82
+ });
83
+ it("does not match repo-root CLAUDE.md (tracked exemption)", () => {
84
+ expect(matchScratchPatterns(["CLAUDE.md"])).toEqual([]);
85
+ });
86
+ it("does not match repo-root CHANGELOG.md, CONTRIBUTING.md, SECURITY.md, CODE_OF_CONDUCT.md, LICENSE.md, AUTHORS.md", () => {
87
+ expect(matchScratchPatterns([
88
+ "CHANGELOG.md",
89
+ "CONTRIBUTING.md",
90
+ "SECURITY.md",
91
+ "CODE_OF_CONDUCT.md",
92
+ "LICENSE.md",
93
+ "AUTHORS.md",
94
+ ])).toEqual([]);
95
+ });
96
+ it("does not match exemptions case-insensitively (Readme.md, Changelog.md)", () => {
97
+ expect(matchScratchPatterns(["Readme.md", "Changelog.md"])).toEqual([]);
98
+ });
99
+ it("does not match docs in subdirectories (docs/foo.md, .github/PULL_REQUEST_TEMPLATE.md)", () => {
100
+ expect(matchScratchPatterns([
101
+ "docs/feature.md",
102
+ ".github/PULL_REQUEST_TEMPLATE.md",
103
+ "packages/core/README.md",
104
+ ])).toEqual([]);
105
+ });
106
+ it("does not match real source files (src/foo.ts, packages/core/src/runner.ts, scripts/sync.ts)", () => {
107
+ expect(matchScratchPatterns([
108
+ "src/foo.ts",
109
+ "packages/core/src/pipeline/runner.ts",
110
+ "scripts/gh-linear-sync.ts",
111
+ "tests/foo.test.ts",
112
+ ])).toEqual([]);
113
+ });
114
+ it("does not match nested .sh scripts (scripts/setup.sh) — only root commit-*.sh / run-*.sh", () => {
115
+ expect(matchScratchPatterns(["scripts/setup.sh", "deploy/restart.sh"])).toEqual([]);
116
+ });
117
+ it("returns empty array for an empty input", () => {
118
+ expect(matchScratchPatterns([])).toEqual([]);
119
+ });
120
+ });
121
+ describe("findScratchFiles env-var escape hatch", () => {
122
+ const ENV_KEY = "URATEAM_DISABLE_SCRATCH_GUARD";
123
+ beforeEach(() => {
124
+ delete process.env[ENV_KEY];
125
+ });
126
+ afterEach(() => {
127
+ delete process.env[ENV_KEY];
128
+ });
129
+ it("when URATEAM_DISABLE_SCRATCH_GUARD=true, returns skipped=true and empty files (no git work)", async () => {
130
+ process.env[ENV_KEY] = "true";
131
+ const { findScratchFiles } = await import("../pipeline/scratch-file-guard.js");
132
+ const result = await findScratchFiles("/nonexistent/path", "main");
133
+ expect(result).toEqual({ files: [], skipped: true });
134
+ });
135
+ it("when URATEAM_DISABLE_SCRATCH_GUARD is unset, gate is active (returns skipped=false)", async () => {
136
+ // We don't have a real worktree here, so we just confirm skipped is false.
137
+ // The git layer is exercised by the runner integration tests.
138
+ const { findScratchFiles } = await import("../pipeline/scratch-file-guard.js");
139
+ // Use a path that exists but is not a git repo so git commands return [].
140
+ const result = await findScratchFiles("/tmp", "main");
141
+ expect(result.skipped).toBe(false);
142
+ });
143
+ });
144
+ //# sourceMappingURL=scratch-file-guard.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scratch-file-guard.test.js","sourceRoot":"","sources":["../../src/__tests__/scratch-file-guard.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=spec-vs-impl-gate.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"spec-vs-impl-gate.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/spec-vs-impl-gate.test.ts"],"names":[],"mappings":""}