@urateam/core 0.1.25 → 0.1.27

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 (146) hide show
  1. package/dist/__tests__/agent-stream-cache-tokens.test.d.ts +2 -0
  2. package/dist/__tests__/agent-stream-cache-tokens.test.d.ts.map +1 -0
  3. package/dist/__tests__/agent-stream-cache-tokens.test.js +25 -0
  4. package/dist/__tests__/agent-stream-cache-tokens.test.js.map +1 -0
  5. package/dist/__tests__/audit/pm-skipped-circuit-breaker-event.test.d.ts +2 -0
  6. package/dist/__tests__/audit/pm-skipped-circuit-breaker-event.test.d.ts.map +1 -0
  7. package/dist/__tests__/audit/pm-skipped-circuit-breaker-event.test.js +32 -0
  8. package/dist/__tests__/audit/pm-skipped-circuit-breaker-event.test.js.map +1 -0
  9. package/dist/__tests__/audit/review-model-low-output-ratio-event.test.d.ts +2 -0
  10. package/dist/__tests__/audit/review-model-low-output-ratio-event.test.d.ts.map +1 -0
  11. package/dist/__tests__/audit/review-model-low-output-ratio-event.test.js +23 -0
  12. package/dist/__tests__/audit/review-model-low-output-ratio-event.test.js.map +1 -0
  13. package/dist/__tests__/audit-immutability.test.js +2 -0
  14. package/dist/__tests__/audit-immutability.test.js.map +1 -1
  15. package/dist/__tests__/bec-181-circuit-breaker-audit-gap.test.d.ts +24 -0
  16. package/dist/__tests__/bec-181-circuit-breaker-audit-gap.test.d.ts.map +1 -0
  17. package/dist/__tests__/bec-181-circuit-breaker-audit-gap.test.js +253 -0
  18. package/dist/__tests__/bec-181-circuit-breaker-audit-gap.test.js.map +1 -0
  19. package/dist/__tests__/executor/review-feedback-profile-override.test.d.ts +2 -0
  20. package/dist/__tests__/executor/review-feedback-profile-override.test.d.ts.map +1 -0
  21. package/dist/__tests__/executor/review-feedback-profile-override.test.js +32 -0
  22. package/dist/__tests__/executor/review-feedback-profile-override.test.js.map +1 -0
  23. package/dist/__tests__/executor/review-feedback-prompt.test.d.ts +2 -0
  24. package/dist/__tests__/executor/review-feedback-prompt.test.d.ts.map +1 -0
  25. package/dist/__tests__/executor/review-feedback-prompt.test.js +29 -0
  26. package/dist/__tests__/executor/review-feedback-prompt.test.js.map +1 -0
  27. package/dist/__tests__/extract-handoff.test.js +74 -0
  28. package/dist/__tests__/extract-handoff.test.js.map +1 -1
  29. package/dist/__tests__/pipeline/pr-cost-summary.test.js +23 -0
  30. package/dist/__tests__/pipeline/pr-cost-summary.test.js.map +1 -1
  31. package/dist/__tests__/pm-circuit-breaker.test.js +86 -15
  32. package/dist/__tests__/pm-circuit-breaker.test.js.map +1 -1
  33. package/dist/__tests__/pm-slack-interface.test.js +40 -1
  34. package/dist/__tests__/pm-slack-interface.test.js.map +1 -1
  35. package/dist/__tests__/post-fanout-comments.test.js +52 -0
  36. package/dist/__tests__/post-fanout-comments.test.js.map +1 -1
  37. package/dist/__tests__/prune-worktrees-in-repo-dirs.test.d.ts +2 -0
  38. package/dist/__tests__/prune-worktrees-in-repo-dirs.test.d.ts.map +1 -0
  39. package/dist/__tests__/prune-worktrees-in-repo-dirs.test.js +64 -0
  40. package/dist/__tests__/prune-worktrees-in-repo-dirs.test.js.map +1 -0
  41. package/dist/__tests__/review/model-health.test.d.ts +2 -0
  42. package/dist/__tests__/review/model-health.test.d.ts.map +1 -0
  43. package/dist/__tests__/review/model-health.test.js +122 -0
  44. package/dist/__tests__/review/model-health.test.js.map +1 -0
  45. package/dist/__tests__/review-feedback.test.js +3 -2
  46. package/dist/__tests__/review-feedback.test.js.map +1 -1
  47. package/dist/__tests__/runner-ralph-feedback-skip.test.d.ts +2 -0
  48. package/dist/__tests__/runner-ralph-feedback-skip.test.d.ts.map +1 -0
  49. package/dist/__tests__/runner-ralph-feedback-skip.test.js +23 -0
  50. package/dist/__tests__/runner-ralph-feedback-skip.test.js.map +1 -0
  51. package/dist/__tests__/templates.test.js +4 -2
  52. package/dist/__tests__/templates.test.js.map +1 -1
  53. package/dist/__tests__/validate-review-models.test.d.ts +2 -0
  54. package/dist/__tests__/validate-review-models.test.d.ts.map +1 -0
  55. package/dist/__tests__/validate-review-models.test.js +155 -0
  56. package/dist/__tests__/validate-review-models.test.js.map +1 -0
  57. package/dist/__tests__/worktree-stale-recovery.test.js +11 -1
  58. package/dist/__tests__/worktree-stale-recovery.test.js.map +1 -1
  59. package/dist/audit/events.d.ts +12 -0
  60. package/dist/audit/events.d.ts.map +1 -1
  61. package/dist/audit/events.js +26 -0
  62. package/dist/audit/events.js.map +1 -1
  63. package/dist/db/migrations/postgres/013_stage_runs_cache_tokens.sql +6 -0
  64. package/dist/db/migrations/sqlite/012_stage_runs_cache_tokens.sql +6 -0
  65. package/dist/db/schema.d.ts +34 -0
  66. package/dist/db/schema.d.ts.map +1 -1
  67. package/dist/db/schema.js +2 -0
  68. package/dist/db/schema.js.map +1 -1
  69. package/dist/executor/agent-stream.d.ts +4 -0
  70. package/dist/executor/agent-stream.d.ts.map +1 -1
  71. package/dist/executor/agent-stream.js +5 -1
  72. package/dist/executor/agent-stream.js.map +1 -1
  73. package/dist/executor/executor.d.ts +9 -1
  74. package/dist/executor/executor.d.ts.map +1 -1
  75. package/dist/executor/executor.js +26 -4
  76. package/dist/executor/executor.js.map +1 -1
  77. package/dist/executor/prompt/templates.d.ts.map +1 -1
  78. package/dist/executor/prompt/templates.js +15 -15
  79. package/dist/executor/prompt/templates.js.map +1 -1
  80. package/dist/executor/review/model-health.d.ts +28 -0
  81. package/dist/executor/review/model-health.d.ts.map +1 -0
  82. package/dist/executor/review/model-health.js +64 -0
  83. package/dist/executor/review/model-health.js.map +1 -0
  84. package/dist/executor/review/post-fanout-comments.d.ts +7 -0
  85. package/dist/executor/review/post-fanout-comments.d.ts.map +1 -1
  86. package/dist/executor/review/post-fanout-comments.js +17 -3
  87. package/dist/executor/review/post-fanout-comments.js.map +1 -1
  88. package/dist/executor/review/review-provider.d.ts +15 -0
  89. package/dist/executor/review/review-provider.d.ts.map +1 -1
  90. package/dist/executor/review/review-provider.js +78 -1
  91. package/dist/executor/review/review-provider.js.map +1 -1
  92. package/dist/index.d.ts +1 -0
  93. package/dist/index.d.ts.map +1 -1
  94. package/dist/index.js +1 -0
  95. package/dist/index.js.map +1 -1
  96. package/dist/pipeline/cost-summary.d.ts +2 -0
  97. package/dist/pipeline/cost-summary.d.ts.map +1 -1
  98. package/dist/pipeline/cost-summary.js +18 -7
  99. package/dist/pipeline/cost-summary.js.map +1 -1
  100. package/dist/pipeline/review-providers-runner.d.ts.map +1 -1
  101. package/dist/pipeline/review-providers-runner.js +49 -0
  102. package/dist/pipeline/review-providers-runner.js.map +1 -1
  103. package/dist/pipeline/runner-ralph-helpers.d.ts +8 -0
  104. package/dist/pipeline/runner-ralph-helpers.d.ts.map +1 -0
  105. package/dist/pipeline/runner-ralph-helpers.js +15 -0
  106. package/dist/pipeline/runner-ralph-helpers.js.map +1 -0
  107. package/dist/pipeline/runner.d.ts +2 -2
  108. package/dist/pipeline/runner.d.ts.map +1 -1
  109. package/dist/pipeline/runner.js +11 -19
  110. package/dist/pipeline/runner.js.map +1 -1
  111. package/dist/pm/actions/db-queries.d.ts +11 -0
  112. package/dist/pm/actions/db-queries.d.ts.map +1 -1
  113. package/dist/pm/actions/db-queries.js +46 -0
  114. package/dist/pm/actions/db-queries.js.map +1 -1
  115. package/dist/pm/actions/promote.d.ts +5 -4
  116. package/dist/pm/actions/promote.d.ts.map +1 -1
  117. package/dist/pm/actions/promote.js +24 -4
  118. package/dist/pm/actions/promote.js.map +1 -1
  119. package/dist/pm/actions/start-todo.d.ts +4 -3
  120. package/dist/pm/actions/start-todo.d.ts.map +1 -1
  121. package/dist/pm/actions/start-todo.js +29 -8
  122. package/dist/pm/actions/start-todo.js.map +1 -1
  123. package/dist/pm/linear-helpers.d.ts +14 -0
  124. package/dist/pm/linear-helpers.d.ts.map +1 -1
  125. package/dist/pm/linear-helpers.js +23 -0
  126. package/dist/pm/linear-helpers.js.map +1 -1
  127. package/dist/pm/scheduler.d.ts.map +1 -1
  128. package/dist/pm/scheduler.js +22 -23
  129. package/dist/pm/scheduler.js.map +1 -1
  130. package/dist/pm/slack-interface.d.ts +12 -0
  131. package/dist/pm/slack-interface.d.ts.map +1 -1
  132. package/dist/pm/slack-interface.js +16 -10
  133. package/dist/pm/slack-interface.js.map +1 -1
  134. package/dist/repo/git.d.ts +27 -2
  135. package/dist/repo/git.d.ts.map +1 -1
  136. package/dist/repo/git.js +67 -8
  137. package/dist/repo/git.js.map +1 -1
  138. package/dist/security/review-checklist.d.ts +8 -2
  139. package/dist/security/review-checklist.d.ts.map +1 -1
  140. package/dist/security/review-checklist.js +39 -9
  141. package/dist/security/review-checklist.js.map +1 -1
  142. package/dist/types.d.ts +10 -0
  143. package/dist/types.d.ts.map +1 -1
  144. package/dist/types.js +2 -1
  145. package/dist/types.js.map +1 -1
  146. package/package.json +1 -1
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=agent-stream-cache-tokens.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agent-stream-cache-tokens.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/agent-stream-cache-tokens.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,25 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { consumeAgentStream } from "../executor/agent-stream.js";
3
+ describe("consumeAgentStream — cache tokens (BEC: cache telemetry)", () => {
4
+ it("accumulates cache_creation_input_tokens and cache_read_input_tokens from message.usage", async () => {
5
+ async function* fakeStream() {
6
+ yield { type: "assistant", usage: { input_tokens: 100, cache_creation_input_tokens: 5000, cache_read_input_tokens: 0, output_tokens: 200 }, content: [{ type: "text", text: "" }] };
7
+ yield { type: "assistant", usage: { input_tokens: 50, cache_creation_input_tokens: 0, cache_read_input_tokens: 5000, output_tokens: 300 }, content: [{ type: "text", text: "" }] };
8
+ yield { type: "assistant", usage: { input_tokens: 50, cache_creation_input_tokens: 0, cache_read_input_tokens: 5000, output_tokens: 250 }, content: [{ type: "text", text: "done" }] };
9
+ }
10
+ const result = await consumeAgentStream(fakeStream());
11
+ expect(result.inputTokens).toBe(200);
12
+ expect(result.outputTokens).toBe(750);
13
+ expect(result.cacheCreationInputTokens).toBe(5000);
14
+ expect(result.cacheReadInputTokens).toBe(10000);
15
+ });
16
+ it("treats missing cache fields as 0 (backward compat with non-cache responses)", async () => {
17
+ async function* fakeStream() {
18
+ yield { type: "assistant", usage: { input_tokens: 100, output_tokens: 200 }, content: [{ type: "text", text: "" }] };
19
+ }
20
+ const result = await consumeAgentStream(fakeStream());
21
+ expect(result.cacheCreationInputTokens).toBe(0);
22
+ expect(result.cacheReadInputTokens).toBe(0);
23
+ });
24
+ });
25
+ //# sourceMappingURL=agent-stream-cache-tokens.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agent-stream-cache-tokens.test.js","sourceRoot":"","sources":["../../src/__tests__/agent-stream-cache-tokens.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,kBAAkB,EAAE,MAAM,6BAA6B,CAAC;AAEjE,QAAQ,CAAC,0DAA0D,EAAE,GAAG,EAAE;IACxE,EAAE,CAAC,wFAAwF,EAAE,KAAK,IAAI,EAAE;QACtG,KAAK,SAAS,CAAC,CAAC,UAAU;YACxB,MAAM,EAAE,IAAI,EAAE,WAAW,EAAE,KAAK,EAAE,EAAE,YAAY,EAAE,GAAG,EAAE,2BAA2B,EAAE,IAAI,EAAE,uBAAuB,EAAE,CAAC,EAAE,aAAa,EAAE,GAAG,EAAE,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC;YACpL,MAAM,EAAE,IAAI,EAAE,WAAW,EAAE,KAAK,EAAE,EAAE,YAAY,EAAE,EAAE,EAAE,2BAA2B,EAAE,CAAC,EAAE,uBAAuB,EAAE,IAAI,EAAE,aAAa,EAAE,GAAG,EAAE,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC;YACnL,MAAM,EAAE,IAAI,EAAE,WAAW,EAAE,KAAK,EAAE,EAAE,YAAY,EAAE,EAAE,EAAE,2BAA2B,EAAE,CAAC,EAAE,uBAAuB,EAAE,IAAI,EAAE,aAAa,EAAE,GAAG,EAAE,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC;QACzL,CAAC;QACD,MAAM,MAAM,GAAG,MAAM,kBAAkB,CAAC,UAAU,EAAE,CAAC,CAAC;QACtD,MAAM,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACrC,MAAM,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACtC,MAAM,CAAC,MAAM,CAAC,wBAAwB,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACnD,MAAM,CAAC,MAAM,CAAC,oBAAoB,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAClD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,6EAA6E,EAAE,KAAK,IAAI,EAAE;QAC3F,KAAK,SAAS,CAAC,CAAC,UAAU;YACxB,MAAM,EAAE,IAAI,EAAE,WAAW,EAAE,KAAK,EAAE,EAAE,YAAY,EAAE,GAAG,EAAE,aAAa,EAAE,GAAG,EAAE,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC;QACvH,CAAC;QACD,MAAM,MAAM,GAAG,MAAM,kBAAkB,CAAC,UAAU,EAAE,CAAC,CAAC;QACtD,MAAM,CAAC,MAAM,CAAC,wBAAwB,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAChD,MAAM,CAAC,MAAM,CAAC,oBAAoB,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAC9C,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=pm-skipped-circuit-breaker-event.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pm-skipped-circuit-breaker-event.test.d.ts","sourceRoot":"","sources":["../../../src/__tests__/audit/pm-skipped-circuit-breaker-event.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,32 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { pmSkippedCircuitBreakerEvent } from "../../audit/events.js";
3
+ describe("pmSkippedCircuitBreakerEvent (BEC-181)", () => {
4
+ it("returns an audit event with the documented shape", () => {
5
+ const event = pmSkippedCircuitBreakerEvent({
6
+ issueId: "BEC-157",
7
+ failureCount: 4,
8
+ threshold: 3,
9
+ source: "promote",
10
+ });
11
+ expect(event.eventType).toBe("pm.skipped_circuit_breaker");
12
+ expect(event.actor).toBe("pm-agent");
13
+ expect(event.actorType).toBe("pm-agent");
14
+ expect(event.issueId).toBe("BEC-157");
15
+ expect(event.payload).toEqual({
16
+ failureCount: 4,
17
+ threshold: 3,
18
+ source: "promote",
19
+ });
20
+ expect(event.id).toMatch(/^evt_/);
21
+ });
22
+ it("captures source as 'start-todo' when fired from start-todo path", () => {
23
+ const event = pmSkippedCircuitBreakerEvent({
24
+ issueId: "BEC-999",
25
+ failureCount: 5,
26
+ threshold: 3,
27
+ source: "start-todo",
28
+ });
29
+ expect(event.payload.source).toBe("start-todo");
30
+ });
31
+ });
32
+ //# sourceMappingURL=pm-skipped-circuit-breaker-event.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pm-skipped-circuit-breaker-event.test.js","sourceRoot":"","sources":["../../../src/__tests__/audit/pm-skipped-circuit-breaker-event.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,4BAA4B,EAAE,MAAM,uBAAuB,CAAC;AAErE,QAAQ,CAAC,wCAAwC,EAAE,GAAG,EAAE;IACtD,EAAE,CAAC,kDAAkD,EAAE,GAAG,EAAE;QAC1D,MAAM,KAAK,GAAG,4BAA4B,CAAC;YACzC,OAAO,EAAE,SAAS;YAClB,YAAY,EAAE,CAAC;YACf,SAAS,EAAE,CAAC;YACZ,MAAM,EAAE,SAAS;SAClB,CAAC,CAAC;QACH,MAAM,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,IAAI,CAAC,4BAA4B,CAAC,CAAC;QAC3D,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QACrC,MAAM,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QACzC,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QACtC,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,OAAO,CAAC;YAC5B,YAAY,EAAE,CAAC;YACf,SAAS,EAAE,CAAC;YACZ,MAAM,EAAE,SAAS;SAClB,CAAC,CAAC;QACH,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;IACpC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,iEAAiE,EAAE,GAAG,EAAE;QACzE,MAAM,KAAK,GAAG,4BAA4B,CAAC;YACzC,OAAO,EAAE,SAAS;YAClB,YAAY,EAAE,CAAC;YACf,SAAS,EAAE,CAAC;YACZ,MAAM,EAAE,YAAY;SACrB,CAAC,CAAC;QACH,MAAM,CAAE,KAAK,CAAC,OAA8B,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;IAC1E,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=review-model-low-output-ratio-event.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"review-model-low-output-ratio-event.test.d.ts","sourceRoot":"","sources":["../../../src/__tests__/audit/review-model-low-output-ratio-event.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,23 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { reviewModelLowOutputRatioEvent } from "../../audit/events.js";
3
+ describe("reviewModelLowOutputRatioEvent", () => {
4
+ it("returns an audit event with the documented shape", () => {
5
+ const event = reviewModelLowOutputRatioEvent({
6
+ modelId: "gpt-oss-120b:free",
7
+ outputRatio: 0.011,
8
+ runs: 10,
9
+ threshold: 0.05,
10
+ });
11
+ expect(event.eventType).toBe("review.model_low_output_ratio");
12
+ expect(event.actor).toBe("system");
13
+ expect(event.actorType).toBe("system");
14
+ expect(event.payload).toEqual({
15
+ modelId: "gpt-oss-120b:free",
16
+ outputRatio: 0.011,
17
+ runs: 10,
18
+ threshold: 0.05,
19
+ });
20
+ expect(event.id).toMatch(/^evt_/);
21
+ });
22
+ });
23
+ //# sourceMappingURL=review-model-low-output-ratio-event.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"review-model-low-output-ratio-event.test.js","sourceRoot":"","sources":["../../../src/__tests__/audit/review-model-low-output-ratio-event.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,8BAA8B,EAAE,MAAM,uBAAuB,CAAC;AAEvE,QAAQ,CAAC,gCAAgC,EAAE,GAAG,EAAE;IAC9C,EAAE,CAAC,kDAAkD,EAAE,GAAG,EAAE;QAC1D,MAAM,KAAK,GAAG,8BAA8B,CAAC;YAC3C,OAAO,EAAE,mBAAmB;YAC5B,WAAW,EAAE,KAAK;YAClB,IAAI,EAAE,EAAE;YACR,SAAS,EAAE,IAAI;SAChB,CAAC,CAAC;QACH,MAAM,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,IAAI,CAAC,+BAA+B,CAAC,CAAC;QAC9D,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QACnC,MAAM,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QACvC,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,OAAO,CAAC;YAC5B,OAAO,EAAE,mBAAmB;YAC5B,WAAW,EAAE,KAAK;YAClB,IAAI,EAAE,EAAE;YACR,SAAS,EAAE,IAAI;SAChB,CAAC,CAAC;QACH,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;IACpC,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -52,7 +52,9 @@ describe("audit_events immutability", () => {
52
52
  "packages/core/src/pm/scheduler.ts",
53
53
  "packages/core/src/pm/actions/triage.ts",
54
54
  "packages/core/src/pm/actions/promote.ts",
55
+ "packages/core/src/pm/actions/start-todo.ts",
55
56
  "packages/core/src/pm/actions/resolve-approvals.ts",
57
+ "packages/core/src/pipeline/review-providers-runner.ts",
56
58
  "packages/core/src/release-manager/scheduler.ts",
57
59
  "packages/core/src/release-manager/slack-handler.ts",
58
60
  "packages/core/src/repo/agent-branch-sweep-runner.ts",
@@ -1 +1 @@
1
- {"version":3,"file":"audit-immutability.test.js","sourceRoot":"","sources":["../../src/__tests__/audit-immutability.test.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"audit-immutability.test.js","sourceRoot":"","sources":["../../src/__tests__/audit-immutability.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,QAAQ,CAAC,2BAA2B,EAAE,GAAG,EAAE;IACzC,EAAE,CAAC,gEAAgE,EAAE,GAAG,EAAE;QACxE,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,aAAa,CAAC,CAAC;QACxD,MAAM,OAAO,GAAG;YACd,sCAAsC;YACtC,qDAAqD;YACrD,wDAAwD;SACzD,CAAC;QAEF,MAAM,QAAQ,GAAG;YACf,iCAAiC;YACjC,iCAAiC;SAClC,CAAC;QAEF,MAAM,OAAO,GAAa,EAAE,CAAC;QAC7B,KAAK,MAAM,GAAG,IAAI,QAAQ,EAAE,CAAC;YAC3B,IAAI,CAAC;gBACH,MAAM,GAAG,GAAG,YAAY,CACtB,KAAK,EACL,CAAC,MAAM,EAAE,KAAK,EAAE,GAAG,EAAE,IAAI,EAAE,kBAAkB,CAAC,EAC9C,EAAE,GAAG,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,EAAE,CACpC,CAAC;gBACF,OAAO,CAAC,IAAI,CAAC,GAAG,GAAG,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC;YAC1D,CAAC;YAAC,MAAM,CAAC;gBACP,0DAA0D;YAC5D,CAAC;QACH,CAAC;QAED,MAAM,SAAS,GAAG,OAAO;aACtB,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAE,CAAC;aAClC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,IAAI,KAAK,CAAC,CAAC,CAAC,CAAC;QAE1E,MAAM,CACJ,SAAS,EACT,2CAA2C,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAClE,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;IAChB,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,mFAAmF,EAAE,GAAG,EAAE;QAC3F,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,aAAa,CAAC,CAAC;QACxD,wCAAwC;QACxC,sCAAsC;QACtC,oDAAoD;QACpD,iFAAiF;QACjF,4EAA4E;QAC5E,yEAAyE;QACzE,sBAAsB;QACtB,MAAM,OAAO,GAAG;YACd,mCAAmC;YACnC,8BAA8B;YAC9B,mCAAmC;YACnC,wCAAwC;YACxC,yCAAyC;YACzC,4CAA4C;YAC5C,mDAAmD;YACnD,uDAAuD;YACvD,gDAAgD;YAChD,oDAAoD;YACpD,qDAAqD;YACrD,gCAAgC;YAChC,6BAA6B;YAC7B,wDAAwD;SACzD,CAAC;QAEF,IAAI,OAAO,GAAa,EAAE,CAAC;QAC3B,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,YAAY,CACtB,KAAK,EACL,CAAC,MAAM,EAAE,KAAK,EAAE,wBAAwB,EAAE,IAAI,EAAE,kBAAkB,CAAC,EACnE,EAAE,GAAG,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,EAAE,CACpC,CAAC;YACF,OAAO,GAAG,GAAG,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;QACnD,CAAC;QAAC,MAAM,CAAC;YACP,aAAa;QACf,CAAC;QAED,MAAM,SAAS,GAAG,OAAO;aACtB,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAE,CAAC;aAClC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,IAAI,KAAK,CAAC,CAAC,CAAC,CAAC;QAE1E,MAAM,CACJ,SAAS,EACT,kDAAkD,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CACzE,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;IAChB,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -0,0 +1,24 @@
1
+ /**
2
+ * BEC-181 — Fix: circuit breaker now emits pm.skipped_circuit_breaker audit events.
3
+ *
4
+ * This file verifies:
5
+ *
6
+ * 1. WORKING: The circuit breaker correctly skips issues in both promoteReadyIssues
7
+ * and startTodoIssues when an issue has ≥ maxConsecutiveFailures failed runs.
8
+ * countConsecutiveFailures returns 4 for 4 consecutive failed runs with no
9
+ * intervening completion.
10
+ *
11
+ * 2. FIXED: When the breaker fires in promote or start-todo, a `pm.skipped_circuit_breaker`
12
+ * audit event is now written to the database. Operators can distinguish
13
+ * "breaker prevented a re-promotion" from "issue was never a candidate"
14
+ * by querying audit_events.
15
+ *
16
+ * Acceptance criteria from BEC-181 that this test covers:
17
+ * - countConsecutiveFailures is verified to return 4 for 4 consecutive failures
18
+ * - promote and start-todo skip issues at/above the threshold (end-to-end)
19
+ * - audit_events table has 1 row with eventType "pm.skipped_circuit_breaker" after a skip
20
+ * - AuditEventTypeSchema contains "pm.skipped_circuit_breaker"
21
+ * - audit/events.ts exports pmSkippedCircuitBreakerEvent builder
22
+ */
23
+ export {};
24
+ //# sourceMappingURL=bec-181-circuit-breaker-audit-gap.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bec-181-circuit-breaker-audit-gap.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/bec-181-circuit-breaker-audit-gap.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG"}
@@ -0,0 +1,253 @@
1
+ /**
2
+ * BEC-181 — Fix: circuit breaker now emits pm.skipped_circuit_breaker audit events.
3
+ *
4
+ * This file verifies:
5
+ *
6
+ * 1. WORKING: The circuit breaker correctly skips issues in both promoteReadyIssues
7
+ * and startTodoIssues when an issue has ≥ maxConsecutiveFailures failed runs.
8
+ * countConsecutiveFailures returns 4 for 4 consecutive failed runs with no
9
+ * intervening completion.
10
+ *
11
+ * 2. FIXED: When the breaker fires in promote or start-todo, a `pm.skipped_circuit_breaker`
12
+ * audit event is now written to the database. Operators can distinguish
13
+ * "breaker prevented a re-promotion" from "issue was never a candidate"
14
+ * by querying audit_events.
15
+ *
16
+ * Acceptance criteria from BEC-181 that this test covers:
17
+ * - countConsecutiveFailures is verified to return 4 for 4 consecutive failures
18
+ * - promote and start-todo skip issues at/above the threshold (end-to-end)
19
+ * - audit_events table has 1 row with eventType "pm.skipped_circuit_breaker" after a skip
20
+ * - AuditEventTypeSchema contains "pm.skipped_circuit_breaker"
21
+ * - audit/events.ts exports pmSkippedCircuitBreakerEvent builder
22
+ */
23
+ import { describe, it, expect, vi, afterEach } from "vitest";
24
+ import { randomBytes } from "node:crypto";
25
+ import { unlinkSync } from "node:fs";
26
+ import { createDb } from "../db/index.js";
27
+ import { pipelineRuns, auditEvents } from "../db/schema.js";
28
+ import { countConsecutiveFailures } from "../pm/actions/db-queries.js";
29
+ import { promoteReadyIssues } from "../pm/actions/promote.js";
30
+ import { startTodoIssues } from "../pm/actions/start-todo.js";
31
+ // ---------------------------------------------------------------------------
32
+ // Helpers
33
+ // ---------------------------------------------------------------------------
34
+ function tmpDbPath() {
35
+ return `/tmp/bec-181-repro-${randomBytes(8).toString("hex")}.sqlite`;
36
+ }
37
+ const paths = [];
38
+ afterEach(() => {
39
+ for (const p of paths) {
40
+ for (const suffix of ["", "-wal", "-shm"]) {
41
+ try {
42
+ unlinkSync(p + suffix);
43
+ }
44
+ catch { /* ignore */ }
45
+ }
46
+ }
47
+ paths.length = 0;
48
+ });
49
+ async function makeDb() {
50
+ const path = tmpDbPath();
51
+ paths.push(path);
52
+ return createDb({ driver: "sqlite", connectionString: path });
53
+ }
54
+ /**
55
+ * Seed N consecutive failed runs for issueId, spaced 5 seconds apart.
56
+ * All are terminal (status="failed") with no completion between them.
57
+ */
58
+ async function seedConsecutiveFailures(db, issueId, count) {
59
+ const t0 = new Date(Date.now() - 60 * 60_000); // 1 hour ago so not "recent"
60
+ const rows = Array.from({ length: count }, (_, i) => ({
61
+ id: `run-${issueId}-${i}`,
62
+ issueId,
63
+ issueTitle: `Issue ${issueId}`,
64
+ pipelineKey: "auto-implement",
65
+ repoUrl: "https://github.com/org/repo",
66
+ status: "failed",
67
+ startedAt: new Date(t0.getTime() + i * 5_000),
68
+ completedAt: new Date(t0.getTime() + i * 5_000 + 1_000),
69
+ }));
70
+ await db.insert(pipelineRuns).values(rows);
71
+ }
72
+ // ---------------------------------------------------------------------------
73
+ // PART 1: Verify countConsecutiveFailures returns 4 for 4 consecutive failures
74
+ // ---------------------------------------------------------------------------
75
+ describe("BEC-181 Part 1: countConsecutiveFailures (working correctly)", () => {
76
+ it("returns 4 when issue has 4 consecutive failed runs and no completed run", async () => {
77
+ const db = await makeDb();
78
+ await seedConsecutiveFailures(db, "BEC-147", 4);
79
+ const count = await countConsecutiveFailures(db, "BEC-147");
80
+ // EXPECTED: 4 — the circuit breaker should engage at threshold 3
81
+ expect(count).toBe(4);
82
+ });
83
+ it("returns 3 when issue has exactly 3 consecutive failed runs (at default threshold)", async () => {
84
+ const db = await makeDb();
85
+ await seedConsecutiveFailures(db, "BEC-157", 3);
86
+ const count = await countConsecutiveFailures(db, "BEC-157");
87
+ // EXPECTED: 3 — at the default threshold of 3, breaker should engage
88
+ expect(count).toBe(3);
89
+ });
90
+ });
91
+ // ---------------------------------------------------------------------------
92
+ // PART 2: Verify promoteReadyIssues skips the issue (circuit breaker fires)
93
+ // ---------------------------------------------------------------------------
94
+ /** Allow fire-and-forget audit DB writes to complete before asserting. */
95
+ async function flushFireAndForget() {
96
+ await new Promise((r) => setImmediate(r));
97
+ await new Promise((r) => setImmediate(r));
98
+ }
99
+ /**
100
+ * Shared helper: runs promoteReadyIssues with the circuit breaker enabled
101
+ * for a single pre-seeded issue and asserts both the skip and the audit event.
102
+ */
103
+ async function assertPromoteBreakerSkip(opts) {
104
+ const db = await makeDb();
105
+ await seedConsecutiveFailures(db, opts.issueId, opts.failureCount);
106
+ const issues = [
107
+ {
108
+ id: opts.linearUuid,
109
+ identifier: opts.issueId,
110
+ title: opts.issueTitle,
111
+ description: "Some description",
112
+ priority: 2,
113
+ labels: vi.fn().mockResolvedValue({ nodes: [{ name: "auto-implement" }] }),
114
+ team: Promise.resolve({ id: "team-1" }),
115
+ url: `https://linear.app/beckerspace/issue/${opts.issueId}`,
116
+ },
117
+ ];
118
+ const client = {
119
+ issues: vi.fn().mockResolvedValue({ nodes: issues }),
120
+ updateIssue: vi.fn(),
121
+ createComment: vi.fn(),
122
+ };
123
+ const stateMap = new Map([["team-1:Todo", "state-todo"]]);
124
+ const conflictChecker = vi.fn();
125
+ // Omit getFailureCount to exercise the batchCountConsecutiveFailures path.
126
+ // The production scheduler also omits getFailureCount after BEC-181 so
127
+ // both promote and start-todo benefit from the batch optimization.
128
+ const results = await promoteReadyIssues({
129
+ linearClient: client,
130
+ teamIds: ["team-1"],
131
+ slotsAvailable: 1,
132
+ checkConflict: conflictChecker,
133
+ stateMap,
134
+ db,
135
+ maxConsecutiveFailures: opts.threshold,
136
+ });
137
+ // Circuit breaker fires and prevents promotion
138
+ expect(results).toHaveLength(1);
139
+ expect(results[0].promoted).toBe(false);
140
+ expect(results[0].reason).toMatch(/circuit.breaker/i);
141
+ expect(results[0].reason).toContain(`${opts.failureCount} consecutive failed runs`);
142
+ expect(client.updateIssue).not.toHaveBeenCalled();
143
+ // Breaker fires before conflict detection (saves tokens)
144
+ expect(conflictChecker).not.toHaveBeenCalled();
145
+ await flushFireAndForget();
146
+ // Audit event is now written when the breaker fires
147
+ const auditRows = await db.select().from(auditEvents);
148
+ expect(auditRows).toHaveLength(1);
149
+ expect(auditRows[0].eventType).toBe("pm.skipped_circuit_breaker");
150
+ expect(auditRows[0].issueId).toBe(opts.issueId);
151
+ expect(JSON.parse(auditRows[0].payload).failureCount).toBe(opts.failureCount);
152
+ expect(JSON.parse(auditRows[0].payload).threshold).toBe(opts.threshold);
153
+ expect(JSON.parse(auditRows[0].payload).source).toBe("promote");
154
+ }
155
+ describe("BEC-181 Part 2: promoteReadyIssues circuit-breaker skip (working correctly)", () => {
156
+ it("skips promotion for issue with 4 consecutive failures and writes a pm.skipped_circuit_breaker audit event (fix verified)", () => assertPromoteBreakerSkip({
157
+ issueId: "BEC-147",
158
+ issueTitle: "Tech debt: changelog UNRELEASED section stale",
159
+ linearUuid: "uuid-bec147",
160
+ failureCount: 4,
161
+ threshold: 3,
162
+ }));
163
+ it("skips promotion for BEC-157 with exactly 3 consecutive failures (at threshold)", () => assertPromoteBreakerSkip({
164
+ issueId: "BEC-157",
165
+ issueTitle: "Pipeline: filter agent scratchpad files",
166
+ linearUuid: "uuid-bec157",
167
+ failureCount: 3,
168
+ threshold: 3,
169
+ }));
170
+ });
171
+ // ---------------------------------------------------------------------------
172
+ // PART 3: Verify startTodoIssues skips the issue (circuit breaker fires)
173
+ // ---------------------------------------------------------------------------
174
+ describe("BEC-181 Part 3: startTodoIssues circuit-breaker skip (working correctly)", () => {
175
+ const pipelineConfigs = {
176
+ "auto-implement": {
177
+ stages: ["triage", "implement", "review"],
178
+ },
179
+ };
180
+ const repoConfigs = {
181
+ "team-1": {
182
+ url: "https://github.com/org/repo",
183
+ defaultBranch: "main",
184
+ },
185
+ };
186
+ it("skips start for issue with 4 consecutive failures WITHOUT touching Linear SDK, and writes an audit event (fix verified)", async () => {
187
+ const db = await makeDb();
188
+ await seedConsecutiveFailures(db, "BEC-147", 4);
189
+ // Spy on Linear SDK lazy relations to prove they are NOT called
190
+ const labelsSpy = vi.fn().mockResolvedValue({ nodes: [{ name: "auto-implement" }] });
191
+ const issue = {
192
+ id: "uuid-bec147",
193
+ identifier: "BEC-147",
194
+ title: "Tech debt: changelog UNRELEASED section stale",
195
+ description: "",
196
+ priority: 2,
197
+ team: Promise.resolve({ id: "team-1" }),
198
+ project: Promise.resolve({ id: "proj-1" }),
199
+ labels: labelsSpy,
200
+ };
201
+ // Use the real DB — seeded rows are 1 hour old, outside the 30-minute recent window,
202
+ // and all are "failed" (not "queued"/"running"), so getActiveAndRecentIssueIds
203
+ // correctly returns empty sets, making BEC-147 appear as an orphaned Todo issue.
204
+ const runner = { start: vi.fn() };
205
+ // Omit getFailureCount to exercise the batchCountConsecutiveFailures path
206
+ // (single DB round-trip). The production scheduler also omits getFailureCount
207
+ // after BEC-181 so it benefits from the batch optimization.
208
+ const input = {
209
+ linearClient: { issues: vi.fn().mockResolvedValue({ nodes: [issue] }) },
210
+ db,
211
+ teamIds: ["team-1"],
212
+ runner: runner,
213
+ pipelineConfigs,
214
+ repoConfigs,
215
+ maxPerTick: 5,
216
+ maxConsecutiveFailures: 3,
217
+ };
218
+ const results = await startTodoIssues(input);
219
+ // VERIFIED WORKING: breaker skips the issue
220
+ expect(results).toHaveLength(1);
221
+ expect(results[0].started).toBe(false);
222
+ expect(results[0].reason).toMatch(/circuit.breaker/i);
223
+ expect(results[0].reason).toContain("4 consecutive failed runs");
224
+ expect(runner.start).not.toHaveBeenCalled();
225
+ // Breaker fires BEFORE Linear SDK calls (saves 3 round-trips per tick per doom-looping issue)
226
+ expect(labelsSpy).not.toHaveBeenCalled();
227
+ // Allow fire-and-forget audit DB writes to complete before asserting.
228
+ await flushFireAndForget();
229
+ // VERIFIED FIX: audit event is now written when the circuit breaker fires in start-todo
230
+ const auditRows = await db.select().from(auditEvents);
231
+ expect(auditRows).toHaveLength(1);
232
+ expect(auditRows[0].eventType).toBe("pm.skipped_circuit_breaker");
233
+ expect(auditRows[0].issueId).toBe("BEC-147");
234
+ expect(JSON.parse(auditRows[0].payload).failureCount).toBe(4);
235
+ expect(JSON.parse(auditRows[0].payload).threshold).toBe(3);
236
+ expect(JSON.parse(auditRows[0].payload).source).toBe("start-todo");
237
+ });
238
+ });
239
+ // ---------------------------------------------------------------------------
240
+ // PART 4: Confirm pm.skipped_circuit_breaker is present in AuditEventTypeSchema (fix verified)
241
+ // ---------------------------------------------------------------------------
242
+ describe("BEC-181 Part 4: pm.skipped_circuit_breaker event type is now defined (gap closed)", () => {
243
+ it("AuditEventTypeSchema contains pm.skipped_circuit_breaker", async () => {
244
+ const { AuditEventTypeSchema } = await import("../types.js");
245
+ const types = AuditEventTypeSchema.options;
246
+ expect(types).toContain("pm.skipped_circuit_breaker");
247
+ });
248
+ it("audit/events.ts exports pmSkippedCircuitBreakerEvent builder", async () => {
249
+ const auditModule = await import("../audit/events.js");
250
+ expect(typeof auditModule.pmSkippedCircuitBreakerEvent).toBe("function");
251
+ });
252
+ });
253
+ //# sourceMappingURL=bec-181-circuit-breaker-audit-gap.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bec-181-circuit-breaker-audit-gap.test.js","sourceRoot":"","sources":["../../src/__tests__/bec-181-circuit-breaker-audit-gap.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=review-feedback-profile-override.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"review-feedback-profile-override.test.d.ts","sourceRoot":"","sources":["../../../src/__tests__/executor/review-feedback-profile-override.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,32 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { applyReviewFeedbackProfileOverride } from "../../executor/executor.js";
3
+ const baseProfile = {
4
+ tools: ["Read", "Write", "Edit"],
5
+ maxInputTokens: 100_000,
6
+ maxTurns: 100,
7
+ model: "claude-sonnet-4-6",
8
+ };
9
+ describe("applyReviewFeedbackProfileOverride (BEC-182)", () => {
10
+ it("caps maxTurns + maxInputTokens for implement stage when review-feedback context is present", () => {
11
+ const out = applyReviewFeedbackProfileOverride(baseProfile, "implement", true);
12
+ expect(out.maxTurns).toBe(30);
13
+ expect(out.maxInputTokens).toBe(60_000);
14
+ // Other fields preserved
15
+ expect(out.tools).toEqual(baseProfile.tools);
16
+ expect(out.model).toBe(baseProfile.model);
17
+ });
18
+ it("returns the profile unchanged on non-implement stages even with review-feedback context", () => {
19
+ const out = applyReviewFeedbackProfileOverride(baseProfile, "test", true);
20
+ expect(out).toEqual(baseProfile);
21
+ });
22
+ it("returns the profile unchanged when review-feedback context is absent", () => {
23
+ const out = applyReviewFeedbackProfileOverride(baseProfile, "implement", false);
24
+ expect(out).toEqual(baseProfile);
25
+ });
26
+ it("does not mutate the input profile", () => {
27
+ const before = JSON.parse(JSON.stringify(baseProfile));
28
+ applyReviewFeedbackProfileOverride(baseProfile, "implement", true);
29
+ expect(baseProfile).toEqual(before);
30
+ });
31
+ });
32
+ //# sourceMappingURL=review-feedback-profile-override.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"review-feedback-profile-override.test.js","sourceRoot":"","sources":["../../../src/__tests__/executor/review-feedback-profile-override.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,kCAAkC,EAAE,MAAM,4BAA4B,CAAC;AAEhF,MAAM,WAAW,GAAG;IAClB,KAAK,EAAE,CAAC,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC;IAChC,cAAc,EAAE,OAAO;IACvB,QAAQ,EAAE,GAAG;IACb,KAAK,EAAE,mBAAmB;CAC3B,CAAC;AAEF,QAAQ,CAAC,8CAA8C,EAAE,GAAG,EAAE;IAC5D,EAAE,CAAC,4FAA4F,EAAE,GAAG,EAAE;QACpG,MAAM,GAAG,GAAG,kCAAkC,CAAC,WAAW,EAAE,WAAW,EAAE,IAAI,CAAC,CAAC;QAC/E,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAC9B,MAAM,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACxC,yBAAyB;QACzB,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC;QAC7C,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC;IAC5C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,yFAAyF,EAAE,GAAG,EAAE;QACjG,MAAM,GAAG,GAAG,kCAAkC,CAAC,WAAW,EAAE,MAAM,EAAE,IAAI,CAAC,CAAC;QAC1E,MAAM,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC;IACnC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,sEAAsE,EAAE,GAAG,EAAE;QAC9E,MAAM,GAAG,GAAG,kCAAkC,CAAC,WAAW,EAAE,WAAW,EAAE,KAAK,CAAC,CAAC;QAChF,MAAM,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC;IACnC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,mCAAmC,EAAE,GAAG,EAAE;QAC3C,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,WAAW,CAAC,CAAC,CAAC;QACvD,kCAAkC,CAAC,WAAW,EAAE,WAAW,EAAE,IAAI,CAAC,CAAC;QACnE,MAAM,CAAC,WAAW,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;IACtC,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=review-feedback-prompt.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"review-feedback-prompt.test.d.ts","sourceRoot":"","sources":["../../../src/__tests__/executor/review-feedback-prompt.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,29 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { implementTemplate } from "../../executor/prompt/templates.js";
3
+ const baseIssue = { id: "BEC-999", identifier: "BEC-999", title: "T", description: "D", priority: 3, labels: [], slug: "bec-999-t" };
4
+ const baseRepo = { url: "https://github.com/x/y", defaultBranch: "main", testCommand: "pnpm test", buildCommand: "pnpm build" };
5
+ describe("implementTemplate — review-feedback prompt (BEC-182)", () => {
6
+ const feedback = {
7
+ prBranch: "agent/BEC-999-foo",
8
+ prUrl: "https://github.com/x/y/pull/42",
9
+ comments: [{ author: "reviewer", body: "rename foo to bar", file: "src/x.ts", line: 12, createdAt: "2026-05-08T00:00:00Z" }],
10
+ };
11
+ it("includes the bounded-scope language", () => {
12
+ const out = implementTemplate(baseIssue, baseRepo, undefined, feedback);
13
+ expect(out).toMatch(/Address ONLY the listed comments/);
14
+ expect(out).toMatch(/Do NOT refactor adjacent code/);
15
+ });
16
+ it("instructs the agent to read the diff first", () => {
17
+ const out = implementTemplate(baseIssue, baseRepo, undefined, feedback);
18
+ expect(out).toMatch(/git diff origin\/main\.\.\.HEAD/);
19
+ });
20
+ it("makes build/test conditional on text-only changes", () => {
21
+ const out = implementTemplate(baseIssue, baseRepo, undefined, feedback);
22
+ expect(out).toMatch(/Skip build\/test ONLY if every change is text-only/);
23
+ });
24
+ it("instructs the agent to stop and report rather than spelunk on failure", () => {
25
+ const out = implementTemplate(baseIssue, baseRepo, undefined, feedback);
26
+ expect(out).toMatch(/stop and report what blocks the resolution/);
27
+ });
28
+ });
29
+ //# sourceMappingURL=review-feedback-prompt.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"review-feedback-prompt.test.js","sourceRoot":"","sources":["../../../src/__tests__/executor/review-feedback-prompt.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,iBAAiB,EAAE,MAAM,oCAAoC,CAAC;AAEvE,MAAM,SAAS,GAAG,EAAE,EAAE,EAAE,SAAS,EAAE,UAAU,EAAE,SAAS,EAAE,KAAK,EAAE,GAAG,EAAE,WAAW,EAAE,GAAG,EAAE,QAAQ,EAAE,CAAC,EAAE,MAAM,EAAE,EAAE,EAAE,IAAI,EAAE,WAAW,EAAS,CAAC;AAC5I,MAAM,QAAQ,GAAG,EAAE,GAAG,EAAE,wBAAwB,EAAE,aAAa,EAAE,MAAM,EAAE,WAAW,EAAE,WAAW,EAAE,YAAY,EAAE,YAAY,EAAS,CAAC;AAEvI,QAAQ,CAAC,sDAAsD,EAAE,GAAG,EAAE;IACpE,MAAM,QAAQ,GAAG;QACf,QAAQ,EAAE,mBAAmB;QAC7B,KAAK,EAAE,gCAAgC;QACvC,QAAQ,EAAE,CAAC,EAAE,MAAM,EAAE,UAAU,EAAE,IAAI,EAAE,mBAAmB,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,EAAE,EAAE,SAAS,EAAE,sBAAsB,EAAE,CAAC;KACtH,CAAC;IAET,EAAE,CAAC,qCAAqC,EAAE,GAAG,EAAE;QAC7C,MAAM,GAAG,GAAG,iBAAiB,CAAC,SAAS,EAAE,QAAQ,EAAE,SAAS,EAAE,QAAQ,CAAC,CAAC;QACxE,MAAM,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,kCAAkC,CAAC,CAAC;QACxD,MAAM,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,+BAA+B,CAAC,CAAC;IACvD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,4CAA4C,EAAE,GAAG,EAAE;QACpD,MAAM,GAAG,GAAG,iBAAiB,CAAC,SAAS,EAAE,QAAQ,EAAE,SAAS,EAAE,QAAQ,CAAC,CAAC;QACxE,MAAM,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,iCAAiC,CAAC,CAAC;IACzD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,mDAAmD,EAAE,GAAG,EAAE;QAC3D,MAAM,GAAG,GAAG,iBAAiB,CAAC,SAAS,EAAE,QAAQ,EAAE,SAAS,EAAE,QAAQ,CAAC,CAAC;QACxE,MAAM,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,oDAAoD,CAAC,CAAC;IAC5E,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,uEAAuE,EAAE,GAAG,EAAE;QAC/E,MAAM,GAAG,GAAG,iBAAiB,CAAC,SAAS,EAAE,QAAQ,EAAE,SAAS,EAAE,QAAQ,CAAC,CAAC;QACxE,MAAM,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,4CAA4C,CAAC,CAAC;IACpE,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -441,6 +441,80 @@ describe("extractHandoff", () => {
441
441
  await rm(dir, { recursive: true, force: true });
442
442
  }
443
443
  });
444
+ // ---------------------------------------------------------------------------
445
+ // BEC-167: Review stage HandoffArtifact envelope — no soup-gate placeholder
446
+ // ---------------------------------------------------------------------------
447
+ it("review stage HandoffArtifact with reviewFindings parses as structured (BEC-167)", async () => {
448
+ // Reproduces the fix for BEC-167: the review agent now emits a full
449
+ // HandoffArtifact envelope. This must parse via the fast path
450
+ // (structured: true) and produce a real prose summary — not the
451
+ // "not parseable prose" soup-gate placeholder.
452
+ const reviewArtifact = {
453
+ stage: "review",
454
+ summary: "The implementation looks correct. No blocking issues found.",
455
+ filesChanged: ["packages/core/src/security/review-checklist.ts"],
456
+ approach: "Updated the output format to emit a HandoffArtifact envelope.",
457
+ context: {
458
+ issueIntent: "Fix review stage placeholder output",
459
+ constraints: [],
460
+ assumptions: [],
461
+ reviewFindings: [],
462
+ },
463
+ tokenBudget: {
464
+ contextTokensUsed: 500,
465
+ recommendedMaxTurns: 10,
466
+ },
467
+ };
468
+ const output = wrapInJsonBlock(reviewArtifact);
469
+ const result = await extractHandoff(output, "run-1", "ISS-1", "review", "/tmp");
470
+ // Must take the fast path — structured agent output, not git fallback
471
+ expect(result.structured).toBe(true);
472
+ // Summary must be real prose, NOT the soup-gate placeholder
473
+ expect(result.artifact.summary).toBe("The implementation looks correct. No blocking issues found.");
474
+ expect(result.artifact.summary).not.toContain("not parseable prose");
475
+ expect(result.artifact.summary).not.toContain("Stage review completed");
476
+ // reviewFindings accessible via context
477
+ expect(result.artifact.context.reviewFindings).toEqual([]);
478
+ });
479
+ it("review stage HandoffArtifact with blocking findings parses correctly (BEC-167)", async () => {
480
+ // Verifies that reviewFindings inside context round-trips through
481
+ // parseHandoffArtifact — downstream stages (review-fix loop, fanout)
482
+ // read handoff.context.reviewFindings to detect blocking issues.
483
+ const reviewArtifact = {
484
+ stage: "review",
485
+ summary: "Found one blocking security issue: SQL injection in user query.",
486
+ filesChanged: ["src/db/user-query.ts"],
487
+ approach: "Reviewed user query builder for injection vulnerabilities.",
488
+ context: {
489
+ issueIntent: "Add user search endpoint",
490
+ constraints: [],
491
+ assumptions: [],
492
+ reviewFindings: [
493
+ {
494
+ severity: "blocking",
495
+ file: "src/db/user-query.ts",
496
+ line: 42,
497
+ category: "SQL Injection",
498
+ description: "Unparameterized query allows SQL injection.",
499
+ fix: "Use parameterized statements via the ORM.",
500
+ },
501
+ ],
502
+ },
503
+ tokenBudget: {
504
+ contextTokensUsed: 800,
505
+ recommendedMaxTurns: 10,
506
+ },
507
+ };
508
+ const output = wrapInJsonBlock(reviewArtifact);
509
+ const result = await extractHandoff(output, "run-1", "ISS-1", "review", "/tmp");
510
+ expect(result.structured).toBe(true);
511
+ expect(result.artifact.summary).toContain("SQL injection");
512
+ expect(result.artifact.summary).not.toContain("not parseable prose");
513
+ // reviewFindings must be accessible for the review-fix loop in runner.ts
514
+ expect(result.artifact.context.reviewFindings).toHaveLength(1);
515
+ expect(result.artifact.context.reviewFindings[0].severity).toBe("blocking");
516
+ expect(result.artifact.context.reviewFindings[0].category).toBe("SQL Injection");
517
+ });
444
518
  it("returns structured: false when git diff fails (not a git repo)", async () => {
445
519
  const dir = await mkdtemp(join(tmpdir(), "extract-test-"));
446
520
  // Not a git repo — no .git directory