@urateam/core 0.1.42 → 0.1.44

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 (108) hide show
  1. package/dist/__tests__/agent-stream.test.js +40 -0
  2. package/dist/__tests__/agent-stream.test.js.map +1 -1
  3. package/dist/__tests__/bitbucket-webhook.test.d.ts +13 -0
  4. package/dist/__tests__/bitbucket-webhook.test.d.ts.map +1 -0
  5. package/dist/__tests__/bitbucket-webhook.test.js +379 -0
  6. package/dist/__tests__/bitbucket-webhook.test.js.map +1 -0
  7. package/dist/__tests__/bitbucket.test.d.ts +15 -0
  8. package/dist/__tests__/bitbucket.test.d.ts.map +1 -0
  9. package/dist/__tests__/bitbucket.test.js +237 -0
  10. package/dist/__tests__/bitbucket.test.js.map +1 -0
  11. package/dist/__tests__/gitlab-webhook.test.d.ts +13 -0
  12. package/dist/__tests__/gitlab-webhook.test.d.ts.map +1 -0
  13. package/dist/__tests__/gitlab-webhook.test.js +388 -0
  14. package/dist/__tests__/gitlab-webhook.test.js.map +1 -0
  15. package/dist/__tests__/pm-triage.test.js +47 -0
  16. package/dist/__tests__/pm-triage.test.js.map +1 -1
  17. package/dist/__tests__/runner-multi-vcs.test.d.ts +19 -0
  18. package/dist/__tests__/runner-multi-vcs.test.d.ts.map +1 -0
  19. package/dist/__tests__/runner-multi-vcs.test.js +346 -0
  20. package/dist/__tests__/runner-multi-vcs.test.js.map +1 -0
  21. package/dist/__tests__/triage-v2-prediction.test.d.ts +2 -0
  22. package/dist/__tests__/triage-v2-prediction.test.d.ts.map +1 -0
  23. package/dist/__tests__/triage-v2-prediction.test.js +70 -0
  24. package/dist/__tests__/triage-v2-prediction.test.js.map +1 -0
  25. package/dist/__tests__/triage-v2-prompt.test.d.ts +2 -0
  26. package/dist/__tests__/triage-v2-prompt.test.d.ts.map +1 -0
  27. package/dist/__tests__/triage-v2-prompt.test.js +127 -0
  28. package/dist/__tests__/triage-v2-prompt.test.js.map +1 -0
  29. package/dist/__tests__/triage-v2-render.test.d.ts +2 -0
  30. package/dist/__tests__/triage-v2-render.test.d.ts.map +1 -0
  31. package/dist/__tests__/triage-v2-render.test.js +200 -0
  32. package/dist/__tests__/triage-v2-render.test.js.map +1 -0
  33. package/dist/__tests__/triage-v2-schema.test.d.ts +2 -0
  34. package/dist/__tests__/triage-v2-schema.test.d.ts.map +1 -0
  35. package/dist/__tests__/triage-v2-schema.test.js +115 -0
  36. package/dist/__tests__/triage-v2-schema.test.js.map +1 -0
  37. package/dist/executor/agent-stream.d.ts +11 -8
  38. package/dist/executor/agent-stream.d.ts.map +1 -1
  39. package/dist/executor/agent-stream.js +54 -9
  40. package/dist/executor/agent-stream.js.map +1 -1
  41. package/dist/index.d.ts +1 -0
  42. package/dist/index.d.ts.map +1 -1
  43. package/dist/index.js.map +1 -1
  44. package/dist/pipeline/feedback-pipeline.d.ts +2 -0
  45. package/dist/pipeline/feedback-pipeline.d.ts.map +1 -1
  46. package/dist/pipeline/feedback-pipeline.js +4 -1
  47. package/dist/pipeline/feedback-pipeline.js.map +1 -1
  48. package/dist/pipeline/runner.d.ts +11 -0
  49. package/dist/pipeline/runner.d.ts.map +1 -1
  50. package/dist/pipeline/runner.js +314 -114
  51. package/dist/pipeline/runner.js.map +1 -1
  52. package/dist/pm/actions/triage-prompt.d.ts +42 -0
  53. package/dist/pm/actions/triage-prompt.d.ts.map +1 -0
  54. package/dist/pm/actions/triage-prompt.js +192 -0
  55. package/dist/pm/actions/triage-prompt.js.map +1 -0
  56. package/dist/pm/actions/triage-render.d.ts +39 -0
  57. package/dist/pm/actions/triage-render.d.ts.map +1 -0
  58. package/dist/pm/actions/triage-render.js +158 -0
  59. package/dist/pm/actions/triage-render.js.map +1 -0
  60. package/dist/pm/actions/triage.d.ts +2 -1
  61. package/dist/pm/actions/triage.d.ts.map +1 -1
  62. package/dist/pm/actions/triage.js +52 -58
  63. package/dist/pm/actions/triage.js.map +1 -1
  64. package/dist/pm/triage-prediction-quality.d.ts +26 -0
  65. package/dist/pm/triage-prediction-quality.d.ts.map +1 -0
  66. package/dist/pm/triage-prediction-quality.js +41 -0
  67. package/dist/pm/triage-prediction-quality.js.map +1 -0
  68. package/dist/pm/types.d.ts +60 -0
  69. package/dist/pm/types.d.ts.map +1 -1
  70. package/dist/pm/types.js +119 -0
  71. package/dist/pm/types.js.map +1 -1
  72. package/dist/repo/bitbucket.d.ts +136 -0
  73. package/dist/repo/bitbucket.d.ts.map +1 -0
  74. package/dist/repo/bitbucket.js +237 -0
  75. package/dist/repo/bitbucket.js.map +1 -0
  76. package/dist/repo/gitlab.d.ts +11 -0
  77. package/dist/repo/gitlab.d.ts.map +1 -1
  78. package/dist/repo/gitlab.js +37 -0
  79. package/dist/repo/gitlab.js.map +1 -1
  80. package/dist/repo/index.d.ts +3 -1
  81. package/dist/repo/index.d.ts.map +1 -1
  82. package/dist/repo/index.js +2 -1
  83. package/dist/repo/index.js.map +1 -1
  84. package/dist/server.d.ts +14 -0
  85. package/dist/server.d.ts.map +1 -1
  86. package/dist/server.js +32 -0
  87. package/dist/server.js.map +1 -1
  88. package/dist/types.d.ts +1 -0
  89. package/dist/types.d.ts.map +1 -1
  90. package/dist/types.js +2 -2
  91. package/dist/types.js.map +1 -1
  92. package/dist/webhook/bitbucket-handler.d.ts +65 -0
  93. package/dist/webhook/bitbucket-handler.d.ts.map +1 -0
  94. package/dist/webhook/bitbucket-handler.js +153 -0
  95. package/dist/webhook/bitbucket-handler.js.map +1 -0
  96. package/dist/webhook/gitlab-handler.d.ts +66 -0
  97. package/dist/webhook/gitlab-handler.d.ts.map +1 -0
  98. package/dist/webhook/gitlab-handler.js +159 -0
  99. package/dist/webhook/gitlab-handler.js.map +1 -0
  100. package/dist/webhook/index.d.ts +3 -0
  101. package/dist/webhook/index.d.ts.map +1 -1
  102. package/dist/webhook/index.js +3 -0
  103. package/dist/webhook/index.js.map +1 -1
  104. package/dist/webhook/shared-handlers.d.ts +110 -0
  105. package/dist/webhook/shared-handlers.d.ts.map +1 -0
  106. package/dist/webhook/shared-handlers.js +251 -0
  107. package/dist/webhook/shared-handlers.js.map +1 -0
  108. package/package.json +1 -1
@@ -0,0 +1,127 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { buildTriageV1Prompt, buildTriageV2Prompt, isV2Disabled, } from "../pm/actions/triage-prompt.js";
3
+ const FIXTURE_ISSUE = {
4
+ identifier: "BEC-100",
5
+ title: "Login throws 500 on empty email",
6
+ description: "When the email field is empty, POST /auth/login returns 500 instead of 400.",
7
+ };
8
+ const identitySanitize = (s) => s;
9
+ describe("buildTriageV1Prompt — pure refactor of the v1 inline prompt", () => {
10
+ it("contains the v1 instruction preamble", () => {
11
+ const prompt = buildTriageV1Prompt(FIXTURE_ISSUE, identitySanitize);
12
+ expect(prompt).toContain("Classify this software issue, generate acceptance criteria");
13
+ });
14
+ it("includes issue identifier, title, and sanitized description", () => {
15
+ const prompt = buildTriageV1Prompt(FIXTURE_ISSUE, identitySanitize);
16
+ expect(prompt).toContain("Issue: BEC-100");
17
+ expect(prompt).toContain("Title: Login throws 500 on empty email");
18
+ expect(prompt).toContain("Description: When the email field is empty");
19
+ });
20
+ it("includes the Tier 4 DESIGN DOC instructions (regression — must still ship)", () => {
21
+ const prompt = buildTriageV1Prompt(FIXTURE_ISSUE, identitySanitize);
22
+ expect(prompt).toContain("Tier 4 — DESIGN DOC fields:");
23
+ expect(prompt).toContain("approachSummary:");
24
+ expect(prompt).toContain("openQuestions:");
25
+ expect(prompt).toContain("antiAcceptanceCriteria:");
26
+ });
27
+ it("matches a canonical snapshot", () => {
28
+ expect(buildTriageV1Prompt(FIXTURE_ISSUE, identitySanitize)).toMatchSnapshot();
29
+ });
30
+ });
31
+ describe("buildTriageV2Prompt — Tier 6a structured prompt", () => {
32
+ it("opens with role + audience priming inside <role> tags", () => {
33
+ const prompt = buildTriageV2Prompt(FIXTURE_ISSUE, identitySanitize);
34
+ expect(prompt).toMatch(/<role>[\s\S]*senior engineer[\s\S]*<\/role>/);
35
+ expect(prompt).toContain("downstream Claude coding agent");
36
+ });
37
+ it("contains XML-delineated sections for role, output_format, examples, issue, reasoning", () => {
38
+ const prompt = buildTriageV2Prompt(FIXTURE_ISSUE, identitySanitize);
39
+ expect(prompt).toMatch(/<role>/);
40
+ expect(prompt).toMatch(/<output_format>/);
41
+ expect(prompt).toMatch(/<examples>/);
42
+ expect(prompt).toMatch(/<issue>/);
43
+ expect(prompt).toMatch(/<reasoning>/);
44
+ });
45
+ it("includes multishot examples for each pipeline label (auto-implement, bug, quick-fix, needs-design)", () => {
46
+ const prompt = buildTriageV2Prompt(FIXTURE_ISSUE, identitySanitize);
47
+ expect(prompt).toMatch(/auto-implement/);
48
+ expect(prompt).toMatch(/bug/);
49
+ expect(prompt).toMatch(/quick-fix/);
50
+ expect(prompt).toMatch(/needs-design/);
51
+ });
52
+ it("includes at least one anti-example (type=\"anti-example\")", () => {
53
+ const prompt = buildTriageV2Prompt(FIXTURE_ISSUE, identitySanitize);
54
+ expect(prompt).toMatch(/type="anti-example"/);
55
+ });
56
+ it("instructs the model that examples are reference, not output", () => {
57
+ const prompt = buildTriageV2Prompt(FIXTURE_ISSUE, identitySanitize);
58
+ expect(prompt.toLowerCase()).toContain("reference examples");
59
+ expect(prompt.toLowerCase()).toContain("not your output");
60
+ });
61
+ it("ends with the JSON prefill anchor (single open brace)", () => {
62
+ const prompt = buildTriageV2Prompt(FIXTURE_ISSUE, identitySanitize);
63
+ expect(prompt.trimEnd().endsWith("{")).toBe(true);
64
+ });
65
+ it("documents the five new Tier 6b output fields in <output_format>", () => {
66
+ const prompt = buildTriageV2Prompt(FIXTURE_ISSUE, identitySanitize);
67
+ expect(prompt).toMatch(/assumptions/);
68
+ expect(prompt).toMatch(/examples/);
69
+ expect(prompt).toMatch(/affectedFiles/);
70
+ expect(prompt).toMatch(/testStrategy/);
71
+ expect(prompt).toMatch(/riskAssessment/);
72
+ });
73
+ it("includes issue identifier, title, and sanitized description inside the real <issue> block (after <examples>)", () => {
74
+ const prompt = buildTriageV2Prompt(FIXTURE_ISSUE, identitySanitize);
75
+ // The multishot examples block also contains <issue> tags — match the
76
+ // LAST occurrence to get the real issue block.
77
+ const matches = [...prompt.matchAll(/<issue>([\s\S]*?)<\/issue>/g)];
78
+ expect(matches.length).toBeGreaterThanOrEqual(1);
79
+ const realIssueBlock = matches[matches.length - 1][1];
80
+ expect(realIssueBlock).toContain("BEC-100");
81
+ expect(realIssueBlock).toContain("Login throws 500 on empty email");
82
+ expect(realIssueBlock).toContain("empty");
83
+ });
84
+ it("prefix (everything before <issue>) stays under 15000 characters", () => {
85
+ const prompt = buildTriageV2Prompt(FIXTURE_ISSUE, identitySanitize);
86
+ const issueIdx = prompt.indexOf("<issue>");
87
+ expect(issueIdx).toBeGreaterThan(0);
88
+ const prefix = prompt.slice(0, issueIdx);
89
+ expect(prefix.length).toBeLessThan(15000);
90
+ });
91
+ it("matches a canonical snapshot", () => {
92
+ expect(buildTriageV2Prompt(FIXTURE_ISSUE, identitySanitize)).toMatchSnapshot();
93
+ });
94
+ });
95
+ describe("isV2Disabled — env-var escape hatch", () => {
96
+ it("returns false when env var is unset", () => {
97
+ expect(isV2Disabled({})).toBe(false);
98
+ });
99
+ it('returns true only on strict equality with "true"', () => {
100
+ expect(isV2Disabled({ URATEAM_DISABLE_TRIAGE_V2: "true" })).toBe(true);
101
+ });
102
+ it("returns false on any other truthy-looking value", () => {
103
+ expect(isV2Disabled({ URATEAM_DISABLE_TRIAGE_V2: "1" })).toBe(false);
104
+ expect(isV2Disabled({ URATEAM_DISABLE_TRIAGE_V2: "yes" })).toBe(false);
105
+ expect(isV2Disabled({ URATEAM_DISABLE_TRIAGE_V2: "TRUE" })).toBe(false);
106
+ expect(isV2Disabled({ URATEAM_DISABLE_TRIAGE_V2: "" })).toBe(false);
107
+ expect(isV2Disabled({ URATEAM_DISABLE_TRIAGE_V2: "false" })).toBe(false);
108
+ });
109
+ it("reads from process.env by default", () => {
110
+ const orig = process.env.URATEAM_DISABLE_TRIAGE_V2;
111
+ try {
112
+ process.env.URATEAM_DISABLE_TRIAGE_V2 = "true";
113
+ expect(isV2Disabled()).toBe(true);
114
+ process.env.URATEAM_DISABLE_TRIAGE_V2 = "false";
115
+ expect(isV2Disabled()).toBe(false);
116
+ }
117
+ finally {
118
+ if (orig === undefined) {
119
+ delete process.env.URATEAM_DISABLE_TRIAGE_V2;
120
+ }
121
+ else {
122
+ process.env.URATEAM_DISABLE_TRIAGE_V2 = orig;
123
+ }
124
+ }
125
+ });
126
+ });
127
+ //# sourceMappingURL=triage-v2-prompt.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"triage-v2-prompt.test.js","sourceRoot":"","sources":["../../src/__tests__/triage-v2-prompt.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=triage-v2-render.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"triage-v2-render.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/triage-v2-render.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,200 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { renderTriageComment, appendTriageSectionsToDescription, } from "../pm/actions/triage-render.js";
3
+ const V1_RESULT = {
4
+ issueId: "BEC-100",
5
+ priority: 2,
6
+ labels: ["bug", "backend", "bug"],
7
+ complexity: "small",
8
+ rationale: "Server-side validation gap on empty email",
9
+ acceptanceCriteria: [
10
+ "validator/email.ts:validate() called from /auth/login",
11
+ "Returns 400 on empty email instead of 500",
12
+ ],
13
+ approachSummary: "Add validator + 400-error path; cover with one test.",
14
+ };
15
+ const V2_RESULT = {
16
+ ...V1_RESULT,
17
+ assumptions: [
18
+ "Empty email is the only 500 case",
19
+ "Other auth endpoints already validate",
20
+ ],
21
+ examples: [
22
+ {
23
+ scenario: "POST /auth/login {email:'', pw:'x'}",
24
+ expected: "HTTP 400, code EMPTY_EMAIL",
25
+ },
26
+ ],
27
+ affectedFiles: [
28
+ "packages/api/src/routes/auth.ts",
29
+ "packages/api/src/validators/email.ts",
30
+ ],
31
+ testStrategy: {
32
+ unit: "packages/api/src/__tests__/email-validator.test.ts",
33
+ integration: "packages/api/src/__tests__/auth.test.ts",
34
+ },
35
+ riskAssessment: { severity: "low", areas: ["auth", "api"] },
36
+ };
37
+ describe("renderTriageComment — v1 + v2 markdown contract", () => {
38
+ it("renders the v1 sections unchanged when no v2 fields are present", () => {
39
+ const comment = renderTriageComment(V1_RESULT, {
40
+ forceNeedsDesign: false,
41
+ pipelineLabel: "bug",
42
+ });
43
+ expect(comment).toContain("**PM Agent — Triaged**");
44
+ expect(comment).toContain("**Priority:** 2");
45
+ expect(comment).toContain("**Complexity:** small");
46
+ expect(comment).toContain("**Pipeline:** bug");
47
+ expect(comment).toContain("**Rationale:** Server-side validation gap");
48
+ expect(comment).toContain("**Generated Acceptance Criteria:**");
49
+ expect(comment).toContain("**Approach (Tier 4):**");
50
+ });
51
+ it("renders all 5 new headed sections when v2 fields are present", () => {
52
+ const comment = renderTriageComment(V2_RESULT, {
53
+ forceNeedsDesign: false,
54
+ pipelineLabel: "bug",
55
+ });
56
+ expect(comment).toMatch(/^### Assumptions$/m);
57
+ expect(comment).toMatch(/^### Examples$/m);
58
+ expect(comment).toMatch(/^### Affected Files$/m);
59
+ expect(comment).toMatch(/^### Test Strategy$/m);
60
+ expect(comment).toMatch(/^### Risk Assessment$/m);
61
+ });
62
+ it("renders (none) placeholders for absent v2 fields when at least one v2 field is present", () => {
63
+ const partial = {
64
+ ...V1_RESULT,
65
+ assumptions: ["only assumption"],
66
+ // examples, affectedFiles, testStrategy, riskAssessment all absent
67
+ };
68
+ const comment = renderTriageComment(partial, {
69
+ forceNeedsDesign: false,
70
+ pipelineLabel: "bug",
71
+ });
72
+ expect(comment).toMatch(/^### Examples\n\(none\)$/m);
73
+ expect(comment).toMatch(/^### Affected Files\n\(none\)$/m);
74
+ expect(comment).toMatch(/^### Test Strategy\n\(none\)$/m);
75
+ expect(comment).toMatch(/^### Risk Assessment\n\(none\)$/m);
76
+ });
77
+ it("omits the entire Tier-6b block when no v2 fields are present", () => {
78
+ const comment = renderTriageComment(V1_RESULT, {
79
+ forceNeedsDesign: false,
80
+ pipelineLabel: "bug",
81
+ });
82
+ expect(comment).not.toContain("### Assumptions");
83
+ expect(comment).not.toContain("### Examples");
84
+ expect(comment).not.toContain("### Affected Files");
85
+ expect(comment).not.toContain("### Test Strategy");
86
+ expect(comment).not.toContain("### Risk Assessment");
87
+ });
88
+ it("omits the entire Tier-6b block when v2 fields are present but all empty (Sonnet W1 fix)", () => {
89
+ const allEmpty = {
90
+ ...V1_RESULT,
91
+ assumptions: [],
92
+ examples: [],
93
+ affectedFiles: [],
94
+ };
95
+ const comment = renderTriageComment(allEmpty, {
96
+ forceNeedsDesign: false,
97
+ pipelineLabel: "bug",
98
+ });
99
+ expect(comment).not.toContain("### Assumptions");
100
+ expect(comment).not.toContain("### Examples");
101
+ expect(comment).not.toContain("### Affected Files");
102
+ expect(comment).not.toContain("### Test Strategy");
103
+ expect(comment).not.toContain("### Risk Assessment");
104
+ });
105
+ it("prefixes the title with `(routed to needs-design)` when forceNeedsDesign", () => {
106
+ const comment = renderTriageComment(V1_RESULT, {
107
+ forceNeedsDesign: true,
108
+ pipelineLabel: "needs-design",
109
+ });
110
+ expect(comment).toContain("(routed to needs-design)");
111
+ });
112
+ it("renders examples as numbered list with Scenario/Expected sub-fields", () => {
113
+ const comment = renderTriageComment(V2_RESULT, {
114
+ forceNeedsDesign: false,
115
+ pipelineLabel: "bug",
116
+ });
117
+ expect(comment).toContain("1. **Scenario:**");
118
+ expect(comment).toContain("**Expected:**");
119
+ });
120
+ it("renders risk assessment as Severity + Areas", () => {
121
+ const comment = renderTriageComment(V2_RESULT, {
122
+ forceNeedsDesign: false,
123
+ pipelineLabel: "bug",
124
+ });
125
+ expect(comment).toContain("**Severity:** low");
126
+ expect(comment).toContain("**Areas:** auth, api");
127
+ });
128
+ it("renders test strategy with unit + integration", () => {
129
+ const comment = renderTriageComment(V2_RESULT, {
130
+ forceNeedsDesign: false,
131
+ pipelineLabel: "bug",
132
+ });
133
+ expect(comment).toContain("- **Unit:** packages/api/src/__tests__/email-validator.test.ts");
134
+ expect(comment).toContain("- **Integration:** packages/api/src/__tests__/auth.test.ts");
135
+ });
136
+ it("matches a canonical snapshot for v2 result", () => {
137
+ expect(renderTriageComment(V2_RESULT, {
138
+ forceNeedsDesign: false,
139
+ pipelineLabel: "bug",
140
+ })).toMatchSnapshot();
141
+ });
142
+ it("matches a canonical snapshot for v1-only result", () => {
143
+ expect(renderTriageComment(V1_RESULT, {
144
+ forceNeedsDesign: false,
145
+ pipelineLabel: "bug",
146
+ })).toMatchSnapshot();
147
+ });
148
+ });
149
+ describe("appendTriageSectionsToDescription — idempotent description appender", () => {
150
+ it("appends acceptance criteria when not already present (v1 behavior preserved)", () => {
151
+ const out = appendTriageSectionsToDescription("Original description.", V1_RESULT);
152
+ expect(out).toContain("Original description.");
153
+ expect(out).toContain("**Acceptance Criteria:**");
154
+ expect(out).toContain("- [ ] validator/email.ts:validate() called from /auth/login");
155
+ });
156
+ it("appends all 5 v2 sections when v2 fields are present", () => {
157
+ const out = appendTriageSectionsToDescription("Original.", V2_RESULT);
158
+ expect(out).toContain("**Examples:**");
159
+ expect(out).toContain("**Affected Files:**");
160
+ expect(out).toContain("**Test Strategy:**");
161
+ expect(out).toContain("**Risk Assessment:**");
162
+ });
163
+ it("skips sections whose marker already appears (idempotent)", () => {
164
+ const existing = "Original.\n\n**Acceptance Criteria:**\n- [ ] one\n\n**Examples:**\n- some example";
165
+ const out = appendTriageSectionsToDescription(existing, V2_RESULT);
166
+ // AC marker present → not re-appended
167
+ expect(out.match(/\*\*Acceptance Criteria:\*\*/g)?.length).toBe(1);
168
+ // Examples marker present → not re-appended
169
+ expect(out.match(/\*\*Examples:\*\*/g)?.length).toBe(1);
170
+ // Affected Files marker not present → appended
171
+ expect(out).toContain("**Affected Files:**");
172
+ });
173
+ it("renders sections to the description in the same order they appear in the comment", () => {
174
+ const out = appendTriageSectionsToDescription("Original.", V2_RESULT);
175
+ const acIdx = out.indexOf("**Acceptance Criteria:**");
176
+ const examplesIdx = out.indexOf("**Examples:**");
177
+ const affectedIdx = out.indexOf("**Affected Files:**");
178
+ const testStrategyIdx = out.indexOf("**Test Strategy:**");
179
+ const riskIdx = out.indexOf("**Risk Assessment:**");
180
+ expect(acIdx).toBeGreaterThan(0);
181
+ expect(examplesIdx).toBeGreaterThan(acIdx);
182
+ expect(affectedIdx).toBeGreaterThan(examplesIdx);
183
+ expect(testStrategyIdx).toBeGreaterThan(affectedIdx);
184
+ expect(riskIdx).toBeGreaterThan(testStrategyIdx);
185
+ });
186
+ it("does not append any v2 section when no v2 fields are present (v1 result)", () => {
187
+ const out = appendTriageSectionsToDescription("Original.", V1_RESULT);
188
+ expect(out).not.toContain("**Examples:**");
189
+ expect(out).not.toContain("**Affected Files:**");
190
+ expect(out).not.toContain("**Test Strategy:**");
191
+ expect(out).not.toContain("**Risk Assessment:**");
192
+ });
193
+ it("handles null/undefined description gracefully", () => {
194
+ const outA = appendTriageSectionsToDescription("", V1_RESULT);
195
+ expect(outA).toContain("**Acceptance Criteria:**");
196
+ // The function expects string input; null/undefined handling is the
197
+ // caller's responsibility (matches existing v1 contract).
198
+ });
199
+ });
200
+ //# sourceMappingURL=triage-v2-render.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"triage-v2-render.test.js","sourceRoot":"","sources":["../../src/__tests__/triage-v2-render.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=triage-v2-schema.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"triage-v2-schema.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/triage-v2-schema.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,115 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { TriageV2ExtensionsSchema, parseTriageV2Extensions, } from "../pm/types.js";
3
+ describe("TriageV2ExtensionsSchema", () => {
4
+ it("accepts a fully-populated v2 result", () => {
5
+ const result = TriageV2ExtensionsSchema.safeParse({
6
+ assumptions: ["Empty email is the only 500 case"],
7
+ examples: [
8
+ { scenario: "POST /auth/login {email:''}", expected: "HTTP 400" },
9
+ ],
10
+ affectedFiles: ["src/routes/auth.ts"],
11
+ testStrategy: {
12
+ unit: "src/__tests__/email.test.ts",
13
+ integration: "src/__tests__/auth.test.ts",
14
+ },
15
+ riskAssessment: { severity: "low", areas: ["auth"] },
16
+ });
17
+ expect(result.success).toBe(true);
18
+ });
19
+ it("accepts an empty object (all fields optional)", () => {
20
+ expect(TriageV2ExtensionsSchema.safeParse({}).success).toBe(true);
21
+ });
22
+ it("rejects severity not in the enum", () => {
23
+ const result = TriageV2ExtensionsSchema.safeParse({
24
+ riskAssessment: { severity: "critical", areas: ["auth"] },
25
+ });
26
+ expect(result.success).toBe(false);
27
+ });
28
+ it("rejects empty strings inside string arrays", () => {
29
+ const result = TriageV2ExtensionsSchema.safeParse({
30
+ assumptions: ["a", ""],
31
+ });
32
+ expect(result.success).toBe(false);
33
+ });
34
+ });
35
+ describe("parseTriageV2Extensions — pre-zod truncation + filter", () => {
36
+ it("returns an empty object when input is not a plain object", () => {
37
+ expect(parseTriageV2Extensions(null)).toEqual({});
38
+ expect(parseTriageV2Extensions("not an object")).toEqual({});
39
+ expect(parseTriageV2Extensions(42)).toEqual({});
40
+ });
41
+ it("silently truncates excess assumptions (>10 → 10)", () => {
42
+ const tooMany = Array.from({ length: 15 }, (_, i) => `assumption ${i}`);
43
+ const parsed = parseTriageV2Extensions({ assumptions: tooMany });
44
+ expect(parsed.assumptions).toHaveLength(10);
45
+ expect(parsed.assumptions?.[0]).toBe("assumption 0");
46
+ expect(parsed.assumptions?.[9]).toBe("assumption 9");
47
+ });
48
+ it("silently truncates excess examples (>3 → 3)", () => {
49
+ const tooMany = Array.from({ length: 5 }, (_, i) => ({
50
+ scenario: `scenario ${i}`,
51
+ expected: `expected ${i}`,
52
+ }));
53
+ const parsed = parseTriageV2Extensions({ examples: tooMany });
54
+ expect(parsed.examples).toHaveLength(3);
55
+ });
56
+ it("silently truncates excess affectedFiles (>20 → 20)", () => {
57
+ const tooMany = Array.from({ length: 30 }, (_, i) => `file${i}.ts`);
58
+ const parsed = parseTriageV2Extensions({ affectedFiles: tooMany });
59
+ expect(parsed.affectedFiles).toHaveLength(20);
60
+ });
61
+ it("silently truncates excess riskAssessment.areas (>5 → 5)", () => {
62
+ const parsed = parseTriageV2Extensions({
63
+ riskAssessment: {
64
+ severity: "high",
65
+ areas: ["a", "b", "c", "d", "e", "f", "g"],
66
+ },
67
+ });
68
+ expect(parsed.riskAssessment?.areas).toHaveLength(5);
69
+ });
70
+ it("filters non-string elements from string arrays", () => {
71
+ const parsed = parseTriageV2Extensions({
72
+ assumptions: ["valid", 42, null, "also valid"],
73
+ affectedFiles: ["a.ts", { wrong: "type" }, "b.ts"],
74
+ });
75
+ expect(parsed.assumptions).toEqual(["valid", "also valid"]);
76
+ expect(parsed.affectedFiles).toEqual(["a.ts", "b.ts"]);
77
+ });
78
+ it("filters examples missing scenario or expected", () => {
79
+ const parsed = parseTriageV2Extensions({
80
+ examples: [
81
+ { scenario: "ok", expected: "ok" },
82
+ { scenario: "missing expected" },
83
+ { expected: "missing scenario" },
84
+ { scenario: "", expected: "" },
85
+ null,
86
+ "string instead of object",
87
+ ],
88
+ });
89
+ expect(parsed.examples).toEqual([{ scenario: "ok", expected: "ok" }]);
90
+ });
91
+ it("drops riskAssessment entirely on unknown severity", () => {
92
+ const parsed = parseTriageV2Extensions({
93
+ riskAssessment: { severity: "critical", areas: ["auth"] },
94
+ });
95
+ expect(parsed.riskAssessment).toBeUndefined();
96
+ });
97
+ it("preserves valid riskAssessment when areas is missing (defaults to [])", () => {
98
+ const parsed = parseTriageV2Extensions({
99
+ riskAssessment: { severity: "medium" },
100
+ });
101
+ expect(parsed.riskAssessment).toEqual({ severity: "medium", areas: [] });
102
+ });
103
+ it("trims whitespace from string fields", () => {
104
+ const parsed = parseTriageV2Extensions({
105
+ assumptions: [" spaced "],
106
+ affectedFiles: [" src/foo.ts\n"],
107
+ });
108
+ expect(parsed.assumptions).toEqual(["spaced"]);
109
+ expect(parsed.affectedFiles).toEqual(["src/foo.ts"]);
110
+ });
111
+ it("returns an empty object when none of the v2 fields are present", () => {
112
+ expect(parseTriageV2Extensions({ priority: 1, labels: [] })).toEqual({});
113
+ });
114
+ });
115
+ //# sourceMappingURL=triage-v2-schema.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"triage-v2-schema.test.js","sourceRoot":"","sources":["../../src/__tests__/triage-v2-schema.test.ts"],"names":[],"mappings":"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"}
@@ -123,15 +123,18 @@ export declare function extractText(content: Array<{
123
123
  text?: string;
124
124
  }> | string): string;
125
125
  /**
126
- * Extract and parse a bare JSON object from text (finds first `{...}` match).
127
- * Returns null if no object is found or if parsing fails.
126
+ * Extract and parse a bare JSON object from text. Returns null if no object
127
+ * is found or if parsing fails.
128
128
  *
129
- * Uses non-greedy matching so that if the text contains multiple top-level
130
- * JSON-like blocks separated by non-JSON content (e.g. log lines), the first
131
- * parseable object is returned rather than a greedy span that crosses all of
132
- * them and fails to parse. Trade-off: non-greedy does not correctly handle
133
- * deeply nested objects — for those, prefer `parseJsonBlock` (fenced block).
134
- * In practice, PM-agent Haiku responses are flat JSON, so non-greedy is safe.
129
+ * Strategy:
130
+ * 1. Try `JSON.parse(text.trim())` first covers clean JSON-only responses
131
+ * (the prefill-anchored v2 triage prompt elicits this shape).
132
+ * 2. Fall back to bracket-counted extraction: find the first `{`, then walk
133
+ * forward tracking nesting depth (string-aware, escape-aware) until the
134
+ * matching closing `}`. Handles nested objects/arrays correctly the
135
+ * previous non-greedy regex (`\{[\s\S]*?\}`) truncated at the first inner
136
+ * `}` and silently failed to parse v2 Haiku responses with `riskAssessment`
137
+ * / `examples` / `testStrategy` objects.
135
138
  */
136
139
  export declare function parseJsonObject(text: string): any | null;
137
140
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"agent-stream.d.ts","sourceRoot":"","sources":["../../src/executor/agent-stream.ts"],"names":[],"mappings":"AAQA,MAAM,WAAW,aAAa;IAC5B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE;QACN,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,aAAa,CAAC,EAAE,MAAM,CAAC;QACvB,2BAA2B,CAAC,EAAE,MAAM,CAAC;QACrC,uBAAuB,CAAC,EAAE,MAAM,CAAC;KAClC,CAAC;IACF,OAAO,CAAC,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,GAAG,MAAM,CAAC;IAC1D,0EAA0E;IAC1E,OAAO,CAAC,EAAE;QAAE,OAAO,CAAC,EAAE,KAAK,CAAC;YAAE,IAAI,EAAE,MAAM,CAAC;YAAC,IAAI,CAAC,EAAE,MAAM,CAAA;SAAE,CAAC,GAAG,MAAM,CAAA;KAAE,GAAG,MAAM,CAAC;CAClF;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,wBAAwB,EAAE,MAAM,CAAC;IACjC,oBAAoB,EAAE,MAAM,CAAC;IAC7B,KAAK,EAAE,MAAM,CAAC;CACf;AAED;;;;;GAKG;AACH,qBAAa,iBAAkB,SAAQ,KAAK;aAExB,YAAY,EAAE,MAAM;aACpB,SAAS,EAAE;QAAE,YAAY,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,MAAM,CAAA;KAAE;gBAD7F,YAAY,EAAE,MAAM,EACpB,SAAS,EAAE;QAAE,YAAY,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,MAAM,CAAA;KAAE;CAQhH;AAED;;;;;;;;GAQG;AACH,qBAAa,0BAA2B,SAAQ,KAAK;aACvB,SAAS,EAAE,MAAM;gBAAjB,SAAS,EAAE,MAAM;CAM9C;AAED;;;;;GAKG;AACH,qBAAa,mBAAoB,SAAQ,KAAK;;CAK7C;AAED;;;;;;;GAOG;AACH,wBAAsB,kBAAkB,CACtC,QAAQ,EAAE,aAAa,CAAC,OAAO,CAAC,EAChC,OAAO,CAAC,EAAE;IACR,aAAa,CAAC,EAAE,CAAC,GAAG,EAAE,aAAa,KAAK,IAAI,CAAC;IAC7C,wEAAwE;IACxE,UAAU,CAAC,EAAE,CAAC,KAAK,EAAE;QAAE,YAAY,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;IACjH,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B;;;;;;;;;OASG;IACH,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B;;;;;;;OAOG;IACH,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B;;;;;OAKG;IACH,WAAW,CAAC,EAAE,WAAW,CAAC;CAC3B,GACA,OAAO,CAAC,aAAa,CAAC,CAmKxB;AAED;;GAEG;AACH,wBAAgB,WAAW,CACzB,OAAO,EAAE,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,GAAG,MAAM,GACvD,MAAM,CAOR;AAkBD;;;;;;;;;;GAUG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,GAAG,GAAG,IAAI,CAExD;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,GAAG,IAAI,CAE3D"}
1
+ {"version":3,"file":"agent-stream.d.ts","sourceRoot":"","sources":["../../src/executor/agent-stream.ts"],"names":[],"mappings":"AAQA,MAAM,WAAW,aAAa;IAC5B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE;QACN,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,aAAa,CAAC,EAAE,MAAM,CAAC;QACvB,2BAA2B,CAAC,EAAE,MAAM,CAAC;QACrC,uBAAuB,CAAC,EAAE,MAAM,CAAC;KAClC,CAAC;IACF,OAAO,CAAC,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,GAAG,MAAM,CAAC;IAC1D,0EAA0E;IAC1E,OAAO,CAAC,EAAE;QAAE,OAAO,CAAC,EAAE,KAAK,CAAC;YAAE,IAAI,EAAE,MAAM,CAAC;YAAC,IAAI,CAAC,EAAE,MAAM,CAAA;SAAE,CAAC,GAAG,MAAM,CAAA;KAAE,GAAG,MAAM,CAAC;CAClF;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,wBAAwB,EAAE,MAAM,CAAC;IACjC,oBAAoB,EAAE,MAAM,CAAC;IAC7B,KAAK,EAAE,MAAM,CAAC;CACf;AAED;;;;;GAKG;AACH,qBAAa,iBAAkB,SAAQ,KAAK;aAExB,YAAY,EAAE,MAAM;aACpB,SAAS,EAAE;QAAE,YAAY,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,MAAM,CAAA;KAAE;gBAD7F,YAAY,EAAE,MAAM,EACpB,SAAS,EAAE;QAAE,YAAY,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,MAAM,CAAA;KAAE;CAQhH;AAED;;;;;;;;GAQG;AACH,qBAAa,0BAA2B,SAAQ,KAAK;aACvB,SAAS,EAAE,MAAM;gBAAjB,SAAS,EAAE,MAAM;CAM9C;AAED;;;;;GAKG;AACH,qBAAa,mBAAoB,SAAQ,KAAK;;CAK7C;AAED;;;;;;;GAOG;AACH,wBAAsB,kBAAkB,CACtC,QAAQ,EAAE,aAAa,CAAC,OAAO,CAAC,EAChC,OAAO,CAAC,EAAE;IACR,aAAa,CAAC,EAAE,CAAC,GAAG,EAAE,aAAa,KAAK,IAAI,CAAC;IAC7C,wEAAwE;IACxE,UAAU,CAAC,EAAE,CAAC,KAAK,EAAE;QAAE,YAAY,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;IACjH,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B;;;;;;;;;OASG;IACH,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B;;;;;;;OAOG;IACH,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B;;;;;OAKG;IACH,WAAW,CAAC,EAAE,WAAW,CAAC;CAC3B,GACA,OAAO,CAAC,aAAa,CAAC,CAmKxB;AAED;;GAEG;AACH,wBAAgB,WAAW,CACzB,OAAO,EAAE,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,GAAG,MAAM,GACvD,MAAM,CAOR;AAkBD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,GAAG,GAAG,IAAI,CAoCxD;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,GAAG,IAAI,CAE3D"}
@@ -237,18 +237,63 @@ function parseJsonWithRegex(text, regex, groupIndex = 0) {
237
237
  }
238
238
  }
239
239
  /**
240
- * Extract and parse a bare JSON object from text (finds first `{...}` match).
241
- * Returns null if no object is found or if parsing fails.
240
+ * Extract and parse a bare JSON object from text. Returns null if no object
241
+ * is found or if parsing fails.
242
242
  *
243
- * Uses non-greedy matching so that if the text contains multiple top-level
244
- * JSON-like blocks separated by non-JSON content (e.g. log lines), the first
245
- * parseable object is returned rather than a greedy span that crosses all of
246
- * them and fails to parse. Trade-off: non-greedy does not correctly handle
247
- * deeply nested objects — for those, prefer `parseJsonBlock` (fenced block).
248
- * In practice, PM-agent Haiku responses are flat JSON, so non-greedy is safe.
243
+ * Strategy:
244
+ * 1. Try `JSON.parse(text.trim())` first covers clean JSON-only responses
245
+ * (the prefill-anchored v2 triage prompt elicits this shape).
246
+ * 2. Fall back to bracket-counted extraction: find the first `{`, then walk
247
+ * forward tracking nesting depth (string-aware, escape-aware) until the
248
+ * matching closing `}`. Handles nested objects/arrays correctly the
249
+ * previous non-greedy regex (`\{[\s\S]*?\}`) truncated at the first inner
250
+ * `}` and silently failed to parse v2 Haiku responses with `riskAssessment`
251
+ * / `examples` / `testStrategy` objects.
249
252
  */
250
253
  export function parseJsonObject(text) {
251
- return parseJsonWithRegex(text, /\{[\s\S]*?\}/, 0);
254
+ const trimmed = text.trim();
255
+ if (trimmed.startsWith("{")) {
256
+ try {
257
+ return JSON.parse(trimmed);
258
+ }
259
+ catch {
260
+ // fall through to extraction
261
+ }
262
+ }
263
+ const start = text.indexOf("{");
264
+ if (start < 0)
265
+ return null;
266
+ let depth = 0;
267
+ let inString = false;
268
+ let escaped = false;
269
+ for (let i = start; i < text.length; i++) {
270
+ const ch = text[i];
271
+ if (inString) {
272
+ if (escaped)
273
+ escaped = false;
274
+ else if (ch === "\\")
275
+ escaped = true;
276
+ else if (ch === '"')
277
+ inString = false;
278
+ continue;
279
+ }
280
+ if (ch === '"')
281
+ inString = true;
282
+ else if (ch === "{")
283
+ depth++;
284
+ else if (ch === "}") {
285
+ depth--;
286
+ if (depth === 0) {
287
+ try {
288
+ return JSON.parse(text.slice(start, i + 1));
289
+ }
290
+ catch {
291
+ return null;
292
+ }
293
+ }
294
+ }
295
+ }
296
+ return null;
252
297
  }
253
298
  /**
254
299
  * Extract and parse a ```json fenced code block from agent output text.