@humanbased/crosscheck 0.15.0-beta.154 → 0.15.0-beta.159
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/__tests__/comment-bodies.test.js +58 -1
- package/dist/__tests__/comment-bodies.test.js.map +1 -1
- package/dist/__tests__/reviewer-error.test.d.ts +2 -0
- package/dist/__tests__/reviewer-error.test.d.ts.map +1 -0
- package/dist/__tests__/reviewer-error.test.js +89 -0
- package/dist/__tests__/reviewer-error.test.js.map +1 -0
- package/dist/__tests__/verdict-gate.test.d.ts +2 -0
- package/dist/__tests__/verdict-gate.test.d.ts.map +1 -0
- package/dist/__tests__/verdict-gate.test.js +81 -0
- package/dist/__tests__/verdict-gate.test.js.map +1 -0
- package/dist/commands/review.d.ts.map +1 -1
- package/dist/commands/review.js +12 -4
- package/dist/commands/review.js.map +1 -1
- package/dist/commands/watch.d.ts.map +1 -1
- package/dist/commands/watch.js +26 -0
- package/dist/commands/watch.js.map +1 -1
- package/dist/lib/comment-bodies.d.ts +8 -0
- package/dist/lib/comment-bodies.d.ts.map +1 -1
- package/dist/lib/comment-bodies.js +25 -0
- package/dist/lib/comment-bodies.js.map +1 -1
- package/dist/lib/reviewer-error.d.ts +8 -0
- package/dist/lib/reviewer-error.d.ts.map +1 -0
- package/dist/lib/reviewer-error.js +65 -0
- package/dist/lib/reviewer-error.js.map +1 -0
- package/dist/lib/runner.d.ts.map +1 -1
- package/dist/lib/runner.js +12 -4
- package/dist/lib/runner.js.map +1 -1
- package/dist/lib/verdict.d.ts +7 -0
- package/dist/lib/verdict.d.ts.map +1 -1
- package/dist/lib/verdict.js +45 -0
- package/dist/lib/verdict.js.map +1 -1
- package/package.json +1 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { describe, it, expect } from 'vitest';
|
|
2
|
-
import { buildFixAppliedCommentBody, buildConflictResolvedCommentBody, buildRetriedReviewBanner, } from '../lib/comment-bodies.js';
|
|
2
|
+
import { buildFixAppliedCommentBody, buildConflictResolvedCommentBody, buildRetriedReviewBanner, buildReviewFailedCommentBody, } from '../lib/comment-bodies.js';
|
|
3
3
|
describe('buildFixAppliedCommentBody', () => {
|
|
4
4
|
it('includes a 7-char sha link, applied count, and the fix_applied annotation tag', () => {
|
|
5
5
|
const body = buildFixAppliedCommentBody({
|
|
@@ -88,4 +88,61 @@ describe('buildRetriedReviewBanner', () => {
|
|
|
88
88
|
expect(banner).toContain('`timeout_sec`');
|
|
89
89
|
});
|
|
90
90
|
});
|
|
91
|
+
describe('buildReviewFailedCommentBody', () => {
|
|
92
|
+
const PR_URL = 'https://github.com/o/r/pull/42';
|
|
93
|
+
it('renders the timeout reason with summary, retry hint, and the non-review marker', () => {
|
|
94
|
+
const body = buildReviewFailedCommentBody({
|
|
95
|
+
prUrl: PR_URL,
|
|
96
|
+
reason: 'timeout',
|
|
97
|
+
summary: 'claude reviewer subprocess timed out after 180s',
|
|
98
|
+
details: 'Vendor: `claude`\nConfigured timeout: 180s',
|
|
99
|
+
});
|
|
100
|
+
expect(body).toContain('### ⏱️ Review failed — timed out');
|
|
101
|
+
expect(body).toContain('**Reason**: claude reviewer subprocess timed out after 180s');
|
|
102
|
+
expect(body).toContain('crosscheck run https://github.com/o/r/pull/42');
|
|
103
|
+
expect(body).toContain('<!-- crosscheck: review_failed -->');
|
|
104
|
+
// Distinct from a review annotation so Phase 1 detection ignores it.
|
|
105
|
+
expect(body).not.toContain('origin=');
|
|
106
|
+
});
|
|
107
|
+
it('uses the usage_limit title and renders the summary verbatim', () => {
|
|
108
|
+
const body = buildReviewFailedCommentBody({
|
|
109
|
+
prUrl: PR_URL,
|
|
110
|
+
reason: 'usage_limit',
|
|
111
|
+
summary: 'claude reviewer hit a usage / rate limit',
|
|
112
|
+
});
|
|
113
|
+
expect(body).toContain('### 🚫 Review failed — reviewer hit usage limit');
|
|
114
|
+
expect(body).toContain('**Reason**: claude reviewer hit a usage / rate limit');
|
|
115
|
+
});
|
|
116
|
+
it('uses the generic subprocess_error title for unspecified failures', () => {
|
|
117
|
+
const body = buildReviewFailedCommentBody({
|
|
118
|
+
prUrl: PR_URL,
|
|
119
|
+
reason: 'subprocess_error',
|
|
120
|
+
summary: 'codex reviewer subprocess failed',
|
|
121
|
+
});
|
|
122
|
+
expect(body).toContain('### ❌ Review failed');
|
|
123
|
+
expect(body).not.toContain('timed out');
|
|
124
|
+
});
|
|
125
|
+
it('wraps non-empty details in a collapsible <details> block', () => {
|
|
126
|
+
const body = buildReviewFailedCommentBody({
|
|
127
|
+
prUrl: PR_URL,
|
|
128
|
+
reason: 'timeout',
|
|
129
|
+
summary: 's',
|
|
130
|
+
details: 'Vendor: `claude`\nConfigured timeout: 180s',
|
|
131
|
+
});
|
|
132
|
+
expect(body).toContain('<details>');
|
|
133
|
+
expect(body).toContain('<summary>Details</summary>');
|
|
134
|
+
expect(body).toContain('Vendor: `claude`');
|
|
135
|
+
expect(body).toContain('</details>');
|
|
136
|
+
});
|
|
137
|
+
it('omits the <details> block entirely when details is missing or blank', () => {
|
|
138
|
+
const noDetails = buildReviewFailedCommentBody({
|
|
139
|
+
prUrl: PR_URL, reason: 'subprocess_error', summary: 's',
|
|
140
|
+
});
|
|
141
|
+
const blank = buildReviewFailedCommentBody({
|
|
142
|
+
prUrl: PR_URL, reason: 'subprocess_error', summary: 's', details: ' \n ',
|
|
143
|
+
});
|
|
144
|
+
expect(noDetails).not.toContain('<details>');
|
|
145
|
+
expect(blank).not.toContain('<details>');
|
|
146
|
+
});
|
|
147
|
+
});
|
|
91
148
|
//# sourceMappingURL=comment-bodies.test.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"comment-bodies.test.js","sourceRoot":"","sources":["../../src/__tests__/comment-bodies.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAA;AAC7C,OAAO,EACL,0BAA0B,EAC1B,gCAAgC,EAChC,wBAAwB,
|
|
1
|
+
{"version":3,"file":"comment-bodies.test.js","sourceRoot":"","sources":["../../src/__tests__/comment-bodies.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAA;AAC7C,OAAO,EACL,0BAA0B,EAC1B,gCAAgC,EAChC,wBAAwB,EACxB,4BAA4B,GAC7B,MAAM,0BAA0B,CAAA;AAEjC,QAAQ,CAAC,4BAA4B,EAAE,GAAG,EAAE;IAC1C,EAAE,CAAC,+EAA+E,EAAE,GAAG,EAAE;QACvF,MAAM,IAAI,GAAG,0BAA0B,CAAC;YACtC,KAAK,EAAE,SAAS;YAChB,IAAI,EAAE,qBAAqB;YAC3B,GAAG,EAAE,kBAAkB;YACvB,YAAY,EAAE,CAAC;YACf,eAAe,EAAE,UAAU;SAC5B,CAAC,CAAA;QACF,MAAM,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,qFAAqF,CAAC,CAAA;QAC7G,MAAM,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,uBAAuB,CAAC,CAAA;QAC/C,MAAM,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,4BAA4B,CAAC,CAAA;QACpD,MAAM,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,kCAAkC,CAAC,CAAA;IAC5D,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,gDAAgD,EAAE,GAAG,EAAE;QACxD,MAAM,GAAG,GAAG,0BAA0B,CAAC;YACrC,KAAK,EAAE,GAAG,EAAE,IAAI,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,EAAE,YAAY,EAAE,CAAC,EAAE,eAAe,EAAE,CAAC;SAChF,CAAC,CAAA;QACF,MAAM,IAAI,GAAG,0BAA0B,CAAC;YACtC,KAAK,EAAE,GAAG,EAAE,IAAI,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,EAAE,YAAY,EAAE,CAAC,EAAE,eAAe,EAAE,CAAC;SAChF,CAAC,CAAA;QACF,MAAM,CAAC,GAAG,CAAC,CAAC,SAAS,CAAC,sBAAsB,CAAC,CAAA;QAC7C,MAAM,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,uBAAuB,CAAC,CAAA;IACjD,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,yDAAyD,EAAE,GAAG,EAAE;QACjE,MAAM,IAAI,GAAG,0BAA0B,CAAC;YACtC,KAAK,EAAE,GAAG,EAAE,IAAI,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,EAAE,YAAY,EAAE,CAAC;SAC5D,CAAC,CAAA;QACF,MAAM,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,eAAe,CAAC,CAAA;QAC3C,MAAM,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,gBAAgB,CAAC,CAAA;IAC9C,CAAC,CAAC,CAAA;AACJ,CAAC,CAAC,CAAA;AAEF,QAAQ,CAAC,kCAAkC,EAAE,GAAG,EAAE;IAChD,EAAE,CAAC,kFAAkF,EAAE,GAAG,EAAE;QAC1F,MAAM,IAAI,GAAG,gCAAgC,CAAC;YAC5C,KAAK,EAAE,SAAS;YAChB,IAAI,EAAE,qBAAqB;YAC3B,GAAG,EAAE,kBAAkB;YACvB,aAAa,EAAE,CAAC;YAChB,KAAK,EAAE,CAAC,UAAU,EAAE,UAAU,CAAC;SAChC,CAAC,CAAA;QACF,MAAM,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,0BAA0B,CAAC,CAAA;QAClD,MAAM,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,cAAc,CAAC,CAAA;QACtC,MAAM,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,cAAc,CAAC,CAAA;QACtC,MAAM,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,aAAa,CAAC,CAAA;QACrC,MAAM,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,wCAAwC,CAAC,CAAA;IAClE,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,yCAAyC,EAAE,GAAG,EAAE;QACjD,MAAM,GAAG,GAAG,gCAAgC,CAAC;YAC3C,KAAK,EAAE,GAAG,EAAE,IAAI,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,EAAE,aAAa,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,MAAM,CAAC;SAC9E,CAAC,CAAA;QACF,MAAM,CAAC,GAAG,CAAC,CAAC,SAAS,CAAC,yBAAyB,CAAC,CAAA;IAClD,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,wEAAwE,EAAE,GAAG,EAAE;QAChF,MAAM,KAAK,GAAG,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,YAAY,CAAC,KAAK,CAAC,CAAA;QACtE,MAAM,IAAI,GAAG,gCAAgC,CAAC;YAC5C,KAAK,EAAE,GAAG,EAAE,IAAI,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,EAAE,aAAa,EAAE,EAAE,EAAE,KAAK;SACrE,CAAC,CAAA;QACF,MAAM,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,mBAAmB,CAAC,CAAA;QAC3C,MAAM,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,oBAAoB,CAAC,CAAA;QAC5C,MAAM,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,oBAAoB,CAAC,CAAA;QAChD,MAAM,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,mBAAmB,CAAC,CAAA;IAC7C,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,mDAAmD,EAAE,GAAG,EAAE;QAC3D,MAAM,IAAI,GAAG,gCAAgC,CAAC;YAC5C,KAAK,EAAE,GAAG,EAAE,IAAI,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,EAAE,aAAa,EAAE,CAAC;YAC5D,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC;SAChC,CAAC,CAAA;QACF,MAAM,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAA;QACpC,MAAM,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,OAAO,CAAC,CAAA;IACrC,CAAC,CAAC,CAAA;AACJ,CAAC,CAAC,CAAA;AAEF,QAAQ,CAAC,0BAA0B,EAAE,GAAG,EAAE;IACxC,EAAE,CAAC,6DAA6D,EAAE,GAAG,EAAE;QACrE,MAAM,MAAM,GAAG,wBAAwB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAA;QACzD,MAAM,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,eAAe,CAAC,CAAA;QACzC,MAAM,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,mBAAmB,CAAC,CAAA;QAC7C,MAAM,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,WAAW,CAAC,CAAA;QACrC,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;IAC5C,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,6BAA6B,EAAE,GAAG,EAAE;QACrC,MAAM,CAAC,wBAAwB,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC,iBAAiB,CAAC,CAAA;IAC7E,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,kEAAkE,EAAE,GAAG,EAAE;QAC1E,MAAM,MAAM,GAAG,wBAAwB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAA;QACzD,MAAM,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,eAAe,CAAC,CAAA;IAC3C,CAAC,CAAC,CAAA;AACJ,CAAC,CAAC,CAAA;AAEF,QAAQ,CAAC,8BAA8B,EAAE,GAAG,EAAE;IAC5C,MAAM,MAAM,GAAG,gCAAgC,CAAA;IAE/C,EAAE,CAAC,gFAAgF,EAAE,GAAG,EAAE;QACxF,MAAM,IAAI,GAAG,4BAA4B,CAAC;YACxC,KAAK,EAAE,MAAM;YACb,MAAM,EAAE,SAAS;YACjB,OAAO,EAAE,iDAAiD;YAC1D,OAAO,EAAE,4CAA4C;SACtD,CAAC,CAAA;QACF,MAAM,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,kCAAkC,CAAC,CAAA;QAC1D,MAAM,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,6DAA6D,CAAC,CAAA;QACrF,MAAM,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,+CAA+C,CAAC,CAAA;QACvE,MAAM,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,oCAAoC,CAAC,CAAA;QAC5D,qEAAqE;QACrE,MAAM,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,SAAS,CAAC,CAAA;IACvC,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,6DAA6D,EAAE,GAAG,EAAE;QACrE,MAAM,IAAI,GAAG,4BAA4B,CAAC;YACxC,KAAK,EAAE,MAAM;YACb,MAAM,EAAE,aAAa;YACrB,OAAO,EAAE,0CAA0C;SACpD,CAAC,CAAA;QACF,MAAM,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,iDAAiD,CAAC,CAAA;QACzE,MAAM,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,sDAAsD,CAAC,CAAA;IAChF,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,kEAAkE,EAAE,GAAG,EAAE;QAC1E,MAAM,IAAI,GAAG,4BAA4B,CAAC;YACxC,KAAK,EAAE,MAAM;YACb,MAAM,EAAE,kBAAkB;YAC1B,OAAO,EAAE,kCAAkC;SAC5C,CAAC,CAAA;QACF,MAAM,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,qBAAqB,CAAC,CAAA;QAC7C,MAAM,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,WAAW,CAAC,CAAA;IACzC,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,0DAA0D,EAAE,GAAG,EAAE;QAClE,MAAM,IAAI,GAAG,4BAA4B,CAAC;YACxC,KAAK,EAAE,MAAM;YACb,MAAM,EAAE,SAAS;YACjB,OAAO,EAAE,GAAG;YACZ,OAAO,EAAE,4CAA4C;SACtD,CAAC,CAAA;QACF,MAAM,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,WAAW,CAAC,CAAA;QACnC,MAAM,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,4BAA4B,CAAC,CAAA;QACpD,MAAM,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,kBAAkB,CAAC,CAAA;QAC1C,MAAM,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,YAAY,CAAC,CAAA;IACtC,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,qEAAqE,EAAE,GAAG,EAAE;QAC7E,MAAM,SAAS,GAAG,4BAA4B,CAAC;YAC7C,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,kBAAkB,EAAE,OAAO,EAAE,GAAG;SACxD,CAAC,CAAA;QACF,MAAM,KAAK,GAAG,4BAA4B,CAAC;YACzC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,kBAAkB,EAAE,OAAO,EAAE,GAAG,EAAE,OAAO,EAAE,SAAS;SAC5E,CAAC,CAAA;QACF,MAAM,CAAC,SAAS,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,WAAW,CAAC,CAAA;QAC5C,MAAM,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,WAAW,CAAC,CAAA;IAC1C,CAAC,CAAC,CAAA;AACJ,CAAC,CAAC,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reviewer-error.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/reviewer-error.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { classifyReviewerError } from '../lib/reviewer-error.js';
|
|
3
|
+
function reviewerErr(message, ann = {}) {
|
|
4
|
+
return Object.assign(new Error(message), ann);
|
|
5
|
+
}
|
|
6
|
+
describe('classifyReviewerError', () => {
|
|
7
|
+
describe('non-reviewer errors → returns null', () => {
|
|
8
|
+
it('returns null for plain strings / non-Error values', () => {
|
|
9
|
+
expect(classifyReviewerError('boom')).toBeNull();
|
|
10
|
+
expect(classifyReviewerError(undefined)).toBeNull();
|
|
11
|
+
expect(classifyReviewerError({ message: 'claude: x' })).toBeNull();
|
|
12
|
+
});
|
|
13
|
+
it('returns null for Errors without a vendor message prefix', () => {
|
|
14
|
+
expect(classifyReviewerError(new Error('git push failed'))).toBeNull();
|
|
15
|
+
expect(classifyReviewerError(new Error('something blew up'))).toBeNull();
|
|
16
|
+
});
|
|
17
|
+
it('does not match prefixes embedded mid-message', () => {
|
|
18
|
+
// The prefix must anchor at the start, otherwise normal log messages
|
|
19
|
+
// mentioning "codex:" or "claude:" would be misclassified.
|
|
20
|
+
expect(classifyReviewerError(new Error('upstream returned codex: timed out'))).toBeNull();
|
|
21
|
+
});
|
|
22
|
+
});
|
|
23
|
+
describe('timeout', () => {
|
|
24
|
+
it('classifies a tagged timeout with the effective cap rendered in seconds', () => {
|
|
25
|
+
const err = reviewerErr('claude: timed out after 180s (retried once) — PR diff may be too large', {
|
|
26
|
+
timedOut: true,
|
|
27
|
+
effectiveTimeoutMs: 180_000,
|
|
28
|
+
retryDelayMs: 120_000,
|
|
29
|
+
});
|
|
30
|
+
const out = classifyReviewerError(err);
|
|
31
|
+
expect(out?.reason).toBe('timeout');
|
|
32
|
+
expect(out?.summary).toContain('claude reviewer subprocess timed out after 180s');
|
|
33
|
+
expect(out?.details).toContain('Vendor: `claude`');
|
|
34
|
+
expect(out?.details).toContain('Configured timeout: 180s');
|
|
35
|
+
expect(out?.details).toContain('120s wait');
|
|
36
|
+
expect(out?.details).toContain('timeout_sec');
|
|
37
|
+
});
|
|
38
|
+
it('omits the timeout-seconds figure when the annotation is missing', () => {
|
|
39
|
+
const err = reviewerErr('codex: timed out — PR diff may be too large', { timedOut: true });
|
|
40
|
+
const out = classifyReviewerError(err);
|
|
41
|
+
expect(out?.reason).toBe('timeout');
|
|
42
|
+
expect(out?.summary).toBe('codex reviewer subprocess timed out');
|
|
43
|
+
expect(out?.details).not.toMatch(/Configured timeout/);
|
|
44
|
+
});
|
|
45
|
+
it('rounds sub-second precision', () => {
|
|
46
|
+
const err = reviewerErr('claude: timed out', { timedOut: true, effectiveTimeoutMs: 1_500 });
|
|
47
|
+
expect(classifyReviewerError(err)?.summary).toContain('after 2s');
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
describe('usage_limit', () => {
|
|
51
|
+
it('classifies a subscription-limit error', () => {
|
|
52
|
+
// isSubscriptionLimitError matches patterns like /rate.?limit|quota|429/i in the message
|
|
53
|
+
const err = reviewerErr('claude: rate limit exceeded');
|
|
54
|
+
const out = classifyReviewerError(err);
|
|
55
|
+
expect(out?.reason).toBe('usage_limit');
|
|
56
|
+
expect(out?.summary).toContain('claude reviewer hit a usage / rate limit');
|
|
57
|
+
expect(out?.details).toContain('Vendor: `claude`');
|
|
58
|
+
expect(out?.details).toContain('rate limit exceeded'); // vendor prefix stripped
|
|
59
|
+
expect(out?.details).not.toContain('claude: rate limit'); // prefix gone
|
|
60
|
+
});
|
|
61
|
+
it('takes precedence over subprocess_error when message matches', () => {
|
|
62
|
+
const err = reviewerErr('codex: usage limit reached for this period', { exitCode: 1 });
|
|
63
|
+
expect(classifyReviewerError(err)?.reason).toBe('usage_limit');
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
describe('subprocess_error', () => {
|
|
67
|
+
it('catches non-timeout / non-limit failures with the generic reason', () => {
|
|
68
|
+
const err = reviewerErr('claude: not logged in — run claude auth', { exitCode: 1, stderr: 'auth error\nnot logged in' });
|
|
69
|
+
const out = classifyReviewerError(err);
|
|
70
|
+
expect(out?.reason).toBe('subprocess_error');
|
|
71
|
+
expect(out?.summary).toBe('claude reviewer subprocess failed');
|
|
72
|
+
expect(out?.details).toContain('Vendor: `claude`');
|
|
73
|
+
expect(out?.details).toContain('Exit code: 1');
|
|
74
|
+
expect(out?.details).toContain('not logged in');
|
|
75
|
+
});
|
|
76
|
+
it('renders the stderr tail (last 5 lines) inside a fenced block when present', () => {
|
|
77
|
+
const stderr = 'a\nb\nc\nd\ne\nf\ng';
|
|
78
|
+
const err = reviewerErr('codex: command failed', { exitCode: 127, stderr });
|
|
79
|
+
const details = classifyReviewerError(err)?.details ?? '';
|
|
80
|
+
expect(details).toMatch(/```\nc\nd\ne\nf\ng\n```/);
|
|
81
|
+
expect(details).not.toContain('a\nb\nc\nd\ne\nf\ng'); // not the full thing
|
|
82
|
+
});
|
|
83
|
+
it('skips the stderr fenced block when stderr is empty or blank', () => {
|
|
84
|
+
const err = reviewerErr('claude: oops', { exitCode: 1, stderr: ' \n ' });
|
|
85
|
+
expect(classifyReviewerError(err)?.details ?? '').not.toContain('```');
|
|
86
|
+
});
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
//# sourceMappingURL=reviewer-error.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reviewer-error.test.js","sourceRoot":"","sources":["../../src/__tests__/reviewer-error.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"verdict-gate.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/verdict-gate.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { applySeverityGate, hasBlockingFindings, parseVerdict } from '../lib/verdict.js';
|
|
3
|
+
// Reproduces the IN-628 non-convergence pattern: the IN-625 PR #28 review at 11:49
|
|
4
|
+
// had "Critical Issues: None" yet returned NEEDS WORK on three warnings, so the
|
|
5
|
+
// fix/recheck loop never terminated. The severity gate must downgrade that to APPROVE.
|
|
6
|
+
const WARNINGS_ONLY_REVIEW = [
|
|
7
|
+
'## Summary',
|
|
8
|
+
'Adds inspection commands. Looks solid overall.',
|
|
9
|
+
'## Critical Issues',
|
|
10
|
+
'None.',
|
|
11
|
+
'## Warnings',
|
|
12
|
+
'1. isValidRepoArg false negative on ../foo',
|
|
13
|
+
'2. isUnsafeRunId("") returns false',
|
|
14
|
+
'3. confirm() blocks on non-TTY stdin',
|
|
15
|
+
'## Suggestions',
|
|
16
|
+
'- Move the constant above its first use.',
|
|
17
|
+
'',
|
|
18
|
+
'VERDICT: NEEDS WORK',
|
|
19
|
+
].join('\n');
|
|
20
|
+
const REAL_CRITICAL_REVIEW = [
|
|
21
|
+
'## Summary',
|
|
22
|
+
'A correctness bug slipped in.',
|
|
23
|
+
'## Critical Issues',
|
|
24
|
+
'- src/auth.ts:12 — token comparison is non-constant-time, auth bypass.',
|
|
25
|
+
'## Warnings',
|
|
26
|
+
'- minor: rename a variable',
|
|
27
|
+
'',
|
|
28
|
+
'VERDICT: NEEDS WORK',
|
|
29
|
+
].join('\n');
|
|
30
|
+
describe('hasBlockingFindings', () => {
|
|
31
|
+
it('is false when Critical Issues section is an explicit None', () => {
|
|
32
|
+
expect(hasBlockingFindings(WARNINGS_ONLY_REVIEW)).toBe(false);
|
|
33
|
+
});
|
|
34
|
+
it('treats "- None", "N/A", "None found" as empty', () => {
|
|
35
|
+
for (const none of ['- None', 'N/A', 'None found.', '_None identified_', 'no critical issues']) {
|
|
36
|
+
const text = `## Critical Issues\n${none}\n## Warnings\n- something`;
|
|
37
|
+
expect(hasBlockingFindings(text)).toBe(false);
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
it('is true when the Critical Issues section lists a real finding', () => {
|
|
41
|
+
expect(hasBlockingFindings(REAL_CRITICAL_REVIEW)).toBe(true);
|
|
42
|
+
});
|
|
43
|
+
it('is true for Codex P0/P1 markers and false for P2/P3 only', () => {
|
|
44
|
+
expect(hasBlockingFindings('- [P0] data loss')).toBe(true);
|
|
45
|
+
expect(hasBlockingFindings('- [P1] broken auth')).toBe(true);
|
|
46
|
+
expect(hasBlockingFindings('- [P2] medium\n- [P3] minor')).toBe(false);
|
|
47
|
+
});
|
|
48
|
+
it('is false when there is no recognizable Critical section (NEEDS WORK is non-blocking by definition)', () => {
|
|
49
|
+
expect(hasBlockingFindings('## Summary\nSome nits.\n## Suggestions\n- tidy up')).toBe(false);
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
describe('applySeverityGate', () => {
|
|
53
|
+
it('downgrades the IN-625 warnings-only NEEDS WORK to APPROVE', () => {
|
|
54
|
+
const { verdict } = parseVerdict(WARNINGS_ONLY_REVIEW);
|
|
55
|
+
expect(verdict).toBe('NEEDS WORK');
|
|
56
|
+
const gated = applySeverityGate(verdict, WARNINGS_ONLY_REVIEW);
|
|
57
|
+
expect(gated.verdict).toBe('APPROVE');
|
|
58
|
+
expect(gated.downgraded).toBe(true);
|
|
59
|
+
});
|
|
60
|
+
it('keeps NEEDS WORK when a real Critical finding is present', () => {
|
|
61
|
+
const gated = applySeverityGate('NEEDS WORK', REAL_CRITICAL_REVIEW);
|
|
62
|
+
expect(gated.verdict).toBe('NEEDS WORK');
|
|
63
|
+
expect(gated.downgraded).toBe(false);
|
|
64
|
+
});
|
|
65
|
+
it('never alters BLOCK', () => {
|
|
66
|
+
const gated = applySeverityGate('BLOCK', WARNINGS_ONLY_REVIEW);
|
|
67
|
+
expect(gated.verdict).toBe('BLOCK');
|
|
68
|
+
expect(gated.downgraded).toBe(false);
|
|
69
|
+
});
|
|
70
|
+
it('never alters APPROVE', () => {
|
|
71
|
+
const gated = applySeverityGate('APPROVE', WARNINGS_ONLY_REVIEW);
|
|
72
|
+
expect(gated.verdict).toBe('APPROVE');
|
|
73
|
+
expect(gated.downgraded).toBe(false);
|
|
74
|
+
});
|
|
75
|
+
it('leaves a null verdict untouched', () => {
|
|
76
|
+
const gated = applySeverityGate(null, WARNINGS_ONLY_REVIEW);
|
|
77
|
+
expect(gated.verdict).toBe(null);
|
|
78
|
+
expect(gated.downgraded).toBe(false);
|
|
79
|
+
});
|
|
80
|
+
});
|
|
81
|
+
//# sourceMappingURL=verdict-gate.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"verdict-gate.test.js","sourceRoot":"","sources":["../../src/__tests__/verdict-gate.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"review.d.ts","sourceRoot":"","sources":["../../src/commands/review.ts"],"names":[],"mappings":"AAqBA,wBAAsB,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,EAAE,aAAa,CAAC,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"review.d.ts","sourceRoot":"","sources":["../../src/commands/review.ts"],"names":[],"mappings":"AAqBA,wBAAsB,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,EAAE,aAAa,CAAC,EAAE,MAAM,iBAmJzF"}
|
package/dist/commands/review.js
CHANGED
|
@@ -10,7 +10,7 @@ import { runClaudeReview } from '../reviewers/claude.js';
|
|
|
10
10
|
import { loadConfig, getGithubToken } from '../config/loader.js';
|
|
11
11
|
import { normalizeVendor, VENDOR_ALIAS_HINT } from '../lib/vendor.js';
|
|
12
12
|
import { initLogger, log as fileLog, logError } from '../lib/logger.js';
|
|
13
|
-
import { parseVerdict, formatVerdict, prependVerdictToComment, NULL_VERDICT_WARNING } from '../lib/verdict.js';
|
|
13
|
+
import { parseVerdict, formatVerdict, prependVerdictToComment, NULL_VERDICT_WARNING, applySeverityGate, SEVERITY_GATE_NOTE } from '../lib/verdict.js';
|
|
14
14
|
import { clonePRForReview } from '../lib/clone.js';
|
|
15
15
|
function parsePRUrl(url) {
|
|
16
16
|
const m = url.match(/github\.com\/([^/]+)\/([^/]+)\/pull\/(\d+)/);
|
|
@@ -99,15 +99,23 @@ export async function runReview(prUrl, configPath, forceReviewer) {
|
|
|
99
99
|
clearInterval(elapsedTimer);
|
|
100
100
|
}
|
|
101
101
|
reviewSpinner.succeed(`Review complete (${elapsed}s)`);
|
|
102
|
-
const
|
|
103
|
-
|
|
102
|
+
const parsed = parseVerdict(reviewText);
|
|
103
|
+
const { clean } = parsed;
|
|
104
|
+
if (parsed.verdict === null) {
|
|
104
105
|
fileLog({ level: 'warn', event: 'verdict_parse_failed', repo: `${owner}/${repo}`, pr: number, reviewer, output_length: reviewText.length });
|
|
105
106
|
}
|
|
107
|
+
// Severity gate: a NEEDS WORK review with no blocking (Critical/High) finding is
|
|
108
|
+
// approved-with-comments (matches the runner's gating so both paths converge).
|
|
109
|
+
const gate = applySeverityGate(parsed.verdict, clean);
|
|
110
|
+
const verdict = gate.verdict;
|
|
111
|
+
if (gate.downgraded) {
|
|
112
|
+
fileLog({ level: 'info', event: 'verdict_severity_gated', repo: `${owner}/${repo}`, pr: number, reviewer, raw_verdict: parsed.verdict, gated_verdict: verdict });
|
|
113
|
+
}
|
|
106
114
|
fileLog({ level: 'info', event: 'review_complete', repo: `${owner}/${repo}`, pr: number, reviewer, model, verdict: verdict ?? undefined, duration_ms: Date.now() - reviewStart, tokens_used: tokensUsed });
|
|
107
115
|
console.log(` ${formatVerdict(verdict)}`);
|
|
108
116
|
const commentBody = verdict === null
|
|
109
117
|
? `${NULL_VERDICT_WARNING}\n\n${clean}`
|
|
110
|
-
: prependVerdictToComment(clean, verdict);
|
|
118
|
+
: prependVerdictToComment(gate.downgraded ? `${SEVERITY_GATE_NOTE}\n\n${clean}` : clean, verdict);
|
|
111
119
|
await postReviewComment(octokit, owner, repo, number, commentBody, reviewer, config.brand, origin, verdict ?? undefined, undefined, false, model, 'review', 1, pr.head.sha);
|
|
112
120
|
fileLog({ level: 'info', event: 'comment_posted', repo: `${owner}/${repo}`, pr: number, url: prUrl });
|
|
113
121
|
console.log(chalk.green(`\n✓ Review posted to ${prUrl}\n`));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"review.js","sourceRoot":"","sources":["../../src/commands/review.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,IAAI,CAAA;AACxC,OAAO,EAAE,MAAM,EAAE,MAAM,IAAI,CAAA;AAC3B,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAA;AAC3B,OAAO,KAAK,MAAM,OAAO,CAAA;AACzB,OAAO,GAAG,MAAM,KAAK,CAAA;AACrB,OAAO,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAA;AAC3E,OAAO,EAAE,gBAAgB,EAAE,cAAc,EAAiB,MAAM,uBAAuB,CAAA;AACvF,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAA;AACtD,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAA;AACxD,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAA;AAChE,OAAO,EAAE,eAAe,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAA;AACrE,OAAO,EAAE,UAAU,EAAE,GAAG,IAAI,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAA;AACvE,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,uBAAuB,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAA;
|
|
1
|
+
{"version":3,"file":"review.js","sourceRoot":"","sources":["../../src/commands/review.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"watch.d.ts","sourceRoot":"","sources":["../../src/commands/watch.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"watch.d.ts","sourceRoot":"","sources":["../../src/commands/watch.ts"],"names":[],"mappings":"AAuKA,MAAM,WAAW,SAAS;IACxB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,IAAI,CAAC,EAAE,OAAO,CAAA;IACd,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB,SAAS,CAAC,EAAE,OAAO,CAAA;CACpB;AAED,wBAAsB,QAAQ,CAAC,IAAI,GAAE,SAAc,iBA+hClD"}
|
package/dist/commands/watch.js
CHANGED
|
@@ -15,6 +15,8 @@ import { parseAnnotation } from '../lib/annotation.js';
|
|
|
15
15
|
import { PRBoard, fmtTime, FMT_TIME_WIDTH } from '../lib/board.js';
|
|
16
16
|
import { clonePRForReview } from '../lib/clone.js';
|
|
17
17
|
import { getSmartSwitch, isSubscriptionLimitError, detectFailedVendor, triggerSwitch, notifyReviewSuccess, stopSmartSwitch, } from '../lib/smart-switch.js';
|
|
18
|
+
import { buildReviewFailedCommentBody } from '../lib/comment-bodies.js';
|
|
19
|
+
import { classifyReviewerError } from '../lib/reviewer-error.js';
|
|
18
20
|
import { mkdtempSync, rmSync } from 'fs';
|
|
19
21
|
import { tmpdir } from 'os';
|
|
20
22
|
import { join } from 'path';
|
|
@@ -400,6 +402,30 @@ export async function runWatch(opts = {}) {
|
|
|
400
402
|
if (boardAdded)
|
|
401
403
|
board.failPR(key, message);
|
|
402
404
|
logError({ repo: `${owner}/${repoName}`, pr: prNumber, phase: 'review' }, err);
|
|
405
|
+
// Post a PR-visible failure comment when the error came from a reviewer
|
|
406
|
+
// subprocess (timeout / usage limit / subprocess crash). Non-reviewer
|
|
407
|
+
// errors (fix, conflict-resolve, GitHub API) return `null` from the
|
|
408
|
+
// classifier and skip the comment — those have their own handling.
|
|
409
|
+
// The comment uses a bareword marker so Phase 1 detection ignores it
|
|
410
|
+
// and the next push still triggers a fresh review.
|
|
411
|
+
const classified = classifyReviewerError(err);
|
|
412
|
+
if (classified) {
|
|
413
|
+
try {
|
|
414
|
+
await lockOctokit.rest.issues.createComment({
|
|
415
|
+
owner, repo: repoName, issue_number: prNumber,
|
|
416
|
+
body: buildReviewFailedCommentBody({
|
|
417
|
+
prUrl: `https://github.com/${owner}/${repoName}/pull/${prNumber}`,
|
|
418
|
+
reason: classified.reason,
|
|
419
|
+
summary: classified.summary,
|
|
420
|
+
...(classified.details !== undefined && { details: classified.details }),
|
|
421
|
+
}),
|
|
422
|
+
});
|
|
423
|
+
fileLog({ level: 'info', event: 'review_failed_comment_posted', repo: `${owner}/${repoName}`, pr: prNumber, reason: classified.reason });
|
|
424
|
+
}
|
|
425
|
+
catch (postErr) {
|
|
426
|
+
fileLog({ level: 'warn', event: 'review_failed_comment_failed', repo: `${owner}/${repoName}`, pr: prNumber, error: postErr instanceof Error ? postErr.message : String(postErr) });
|
|
427
|
+
}
|
|
428
|
+
}
|
|
403
429
|
await releaseRemoteLock(lockOctokit, owner, repoName, params.headSha, 'failure');
|
|
404
430
|
// Smart-switch: when a reviewer hits a subscription limit in cross-vendor mode,
|
|
405
431
|
// degrade to single-vendor with the healthy vendor for the next 30 minutes.
|