@humanbased/crosscheck 1.3.1-beta.7 → 1.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +16 -1
- package/assets/demo-block-to-approve.gif +0 -0
- package/assets/demo-block-to-approve.mp4 +0 -0
- package/dist/__tests__/base-ref-recovery.test.d.ts +2 -0
- package/dist/__tests__/base-ref-recovery.test.d.ts.map +1 -0
- package/dist/__tests__/base-ref-recovery.test.js +102 -0
- package/dist/__tests__/base-ref-recovery.test.js.map +1 -0
- package/dist/__tests__/codex-env.test.js +4 -0
- package/dist/__tests__/codex-env.test.js.map +1 -1
- package/dist/__tests__/credential-free-origin.test.js +134 -2
- package/dist/__tests__/credential-free-origin.test.js.map +1 -1
- package/dist/__tests__/demo-arc.test.d.ts +2 -0
- package/dist/__tests__/demo-arc.test.d.ts.map +1 -0
- package/dist/__tests__/demo-arc.test.js +73 -0
- package/dist/__tests__/demo-arc.test.js.map +1 -0
- package/dist/__tests__/doc-only-verdict.test.d.ts +2 -0
- package/dist/__tests__/doc-only-verdict.test.d.ts.map +1 -0
- package/dist/__tests__/doc-only-verdict.test.js +79 -0
- package/dist/__tests__/doc-only-verdict.test.js.map +1 -0
- package/dist/__tests__/error-classification.test.js +14 -0
- package/dist/__tests__/error-classification.test.js.map +1 -1
- package/dist/__tests__/fix-requires-recheck.test.d.ts +2 -0
- package/dist/__tests__/fix-requires-recheck.test.d.ts.map +1 -0
- package/dist/__tests__/fix-requires-recheck.test.js +56 -0
- package/dist/__tests__/fix-requires-recheck.test.js.map +1 -0
- package/dist/__tests__/human-feedback.test.d.ts +2 -0
- package/dist/__tests__/human-feedback.test.d.ts.map +1 -0
- package/dist/__tests__/human-feedback.test.js +132 -0
- package/dist/__tests__/human-feedback.test.js.map +1 -0
- package/dist/__tests__/inconclusive-review.test.d.ts +2 -0
- package/dist/__tests__/inconclusive-review.test.d.ts.map +1 -0
- package/dist/__tests__/inconclusive-review.test.js +110 -0
- package/dist/__tests__/inconclusive-review.test.js.map +1 -0
- package/dist/__tests__/merge-gate.test.d.ts +2 -0
- package/dist/__tests__/merge-gate.test.d.ts.map +1 -0
- package/dist/__tests__/merge-gate.test.js +220 -0
- package/dist/__tests__/merge-gate.test.js.map +1 -0
- package/dist/__tests__/no-verdict.test.d.ts +2 -0
- package/dist/__tests__/no-verdict.test.d.ts.map +1 -0
- package/dist/__tests__/no-verdict.test.js +415 -0
- package/dist/__tests__/no-verdict.test.js.map +1 -0
- package/dist/__tests__/pr-spec.test.js +37 -0
- package/dist/__tests__/pr-spec.test.js.map +1 -1
- package/dist/__tests__/run.test.js +50 -1
- package/dist/__tests__/run.test.js.map +1 -1
- package/dist/__tests__/runner.test.js +82 -1
- package/dist/__tests__/runner.test.js.map +1 -1
- package/dist/__tests__/standing-verdict-history.test.d.ts +2 -0
- package/dist/__tests__/standing-verdict-history.test.d.ts.map +1 -0
- package/dist/__tests__/standing-verdict-history.test.js +109 -0
- package/dist/__tests__/standing-verdict-history.test.js.map +1 -0
- package/dist/__tests__/step-plan.test.d.ts +2 -0
- package/dist/__tests__/step-plan.test.d.ts.map +1 -0
- package/dist/__tests__/step-plan.test.js +129 -0
- package/dist/__tests__/step-plan.test.js.map +1 -0
- package/dist/cli.js +12 -0
- package/dist/cli.js.map +1 -1
- package/dist/commands/merge.d.ts +11 -0
- package/dist/commands/merge.d.ts.map +1 -0
- package/dist/commands/merge.js +188 -0
- package/dist/commands/merge.js.map +1 -0
- package/dist/commands/review.d.ts.map +1 -1
- package/dist/commands/review.js +29 -7
- package/dist/commands/review.js.map +1 -1
- package/dist/commands/run.d.ts +53 -0
- package/dist/commands/run.d.ts.map +1 -1
- package/dist/commands/run.js +197 -32
- package/dist/commands/run.js.map +1 -1
- package/dist/commands/watch.d.ts.map +1 -1
- package/dist/commands/watch.js +14 -3
- package/dist/commands/watch.js.map +1 -1
- package/dist/config/loader.d.ts +4 -0
- package/dist/config/loader.d.ts.map +1 -1
- package/dist/config/loader.js +8 -0
- package/dist/config/loader.js.map +1 -1
- package/dist/config/review-strategy.json +5 -5
- package/dist/github/client.d.ts +18 -1
- package/dist/github/client.d.ts.map +1 -1
- package/dist/github/client.js +19 -3
- package/dist/github/client.js.map +1 -1
- package/dist/github/merge.d.ts +28 -0
- package/dist/github/merge.d.ts.map +1 -1
- package/dist/github/merge.js +41 -0
- package/dist/github/merge.js.map +1 -1
- package/dist/github/webhook.d.ts +1 -0
- package/dist/github/webhook.d.ts.map +1 -1
- package/dist/github/webhook.js.map +1 -1
- package/dist/lib/board.d.ts +1 -0
- package/dist/lib/board.d.ts.map +1 -1
- package/dist/lib/board.js +1 -1
- package/dist/lib/board.js.map +1 -1
- package/dist/lib/clone.d.ts +65 -1
- package/dist/lib/clone.d.ts.map +1 -1
- package/dist/lib/clone.js +252 -16
- package/dist/lib/clone.js.map +1 -1
- package/dist/lib/human-feedback.d.ts +9 -0
- package/dist/lib/human-feedback.d.ts.map +1 -0
- package/dist/lib/human-feedback.js +83 -0
- package/dist/lib/human-feedback.js.map +1 -0
- package/dist/lib/logger.d.ts +1 -1
- package/dist/lib/logger.d.ts.map +1 -1
- package/dist/lib/logger.js +6 -0
- package/dist/lib/logger.js.map +1 -1
- package/dist/lib/merge-gate.d.ts +70 -0
- package/dist/lib/merge-gate.d.ts.map +1 -0
- package/dist/lib/merge-gate.js +138 -0
- package/dist/lib/merge-gate.js.map +1 -0
- package/dist/lib/no-verdict.d.ts +104 -0
- package/dist/lib/no-verdict.d.ts.map +1 -0
- package/dist/lib/no-verdict.js +262 -0
- package/dist/lib/no-verdict.js.map +1 -0
- package/dist/lib/pr-spec.d.ts +7 -0
- package/dist/lib/pr-spec.d.ts.map +1 -1
- package/dist/lib/pr-spec.js +12 -1
- package/dist/lib/pr-spec.js.map +1 -1
- package/dist/lib/pr-workflow-state.d.ts +37 -0
- package/dist/lib/pr-workflow-state.d.ts.map +1 -1
- package/dist/lib/pr-workflow-state.js +58 -1
- package/dist/lib/pr-workflow-state.js.map +1 -1
- package/dist/lib/review-strategy.d.ts +14 -0
- package/dist/lib/review-strategy.d.ts.map +1 -1
- package/dist/lib/review-strategy.js +32 -1
- package/dist/lib/review-strategy.js.map +1 -1
- package/dist/lib/runner.d.ts +51 -2
- package/dist/lib/runner.d.ts.map +1 -1
- package/dist/lib/runner.js +211 -105
- package/dist/lib/runner.js.map +1 -1
- package/dist/lib/verdict.d.ts +30 -1
- package/dist/lib/verdict.d.ts.map +1 -1
- package/dist/lib/verdict.js +94 -9
- package/dist/lib/verdict.js.map +1 -1
- package/dist/reviewers/codex-env.d.ts.map +1 -1
- package/dist/reviewers/codex-env.js +2 -0
- package/dist/reviewers/codex-env.js.map +1 -1
- package/dist/reviewers/codex.d.ts.map +1 -1
- package/dist/reviewers/codex.js +3 -1
- package/dist/reviewers/codex.js.map +1 -1
- package/dist/reviewers/fix.d.ts +2 -2
- package/dist/reviewers/fix.d.ts.map +1 -1
- package/dist/reviewers/fix.js +10 -4
- package/dist/reviewers/fix.js.map +1 -1
- package/docs/growth/launch/README.md +59 -0
- package/docs/growth/launch/essay-stop-letting-one-agent-review-its-own-code.md +118 -0
- package/docs/growth/launch/posts.md +175 -0
- package/docs/trust.md +16 -1
- package/package.json +11 -3
package/README.md
CHANGED
|
@@ -17,7 +17,17 @@
|
|
|
17
17
|
</p>
|
|
18
18
|
|
|
19
19
|
<p align="center">
|
|
20
|
-
<img src="./assets/
|
|
20
|
+
<img src="./assets/demo-block-to-approve.gif" alt="crosscheck reviewing a PR: a BLOCK on a dropped ownership filter, an auto-fix, and the verdict on the repair" width="900" />
|
|
21
|
+
</p>
|
|
22
|
+
|
|
23
|
+
<p align="center">
|
|
24
|
+
<strong>The run above is real</strong> — a public fixture PR, every comment permanently linked:<br />
|
|
25
|
+
<a href="https://github.com/humanbased-ai/crosscheck-proof-fixture/pull/2">the whole thread</a>
|
|
26
|
+
· <a href="./demo/README.md">how the recording was made</a>
|
|
27
|
+
</p>
|
|
28
|
+
|
|
29
|
+
<p align="center">
|
|
30
|
+
<sub>A pagination change that passed CI and dropped <code>ownerId</code> from the transaction query — any caller could read another user's transactions. Reviewed by Codex, repaired by Claude, and re-judged against the repair.</sub>
|
|
21
31
|
</p>
|
|
22
32
|
|
|
23
33
|
---
|
|
@@ -173,6 +183,7 @@ Full walkthrough: **[docs/linear-identity.md](./docs/linear-identity.md)**.
|
|
|
173
183
|
| `crosscheck review <pr>` | One-shot review, posts a comment |
|
|
174
184
|
| `crosscheck run <pr>` | Full pipeline for a PR — review, fix, recheck |
|
|
175
185
|
| `crosscheck recheck` / `fix` / `resolve` | Run one step in isolation |
|
|
186
|
+
| `crosscheck merge <pr>` | Merge a PR once the verdict allows it — `--loose` / `--tight` / `--force` |
|
|
176
187
|
| `crosscheck watch` | Listen for PR events and run the pipeline automatically |
|
|
177
188
|
| `crosscheck scan` | Show open PRs with stale crosscheck state |
|
|
178
189
|
| `crosscheck kickass` | Pick a stale PR and drive it to its next step |
|
|
@@ -304,6 +315,10 @@ Every option, annotated: **[crosscheck.config.example.yml](./crosscheck.config.e
|
|
|
304
315
|
crosscheck onboard && crosscheck watch
|
|
305
316
|
```
|
|
306
317
|
|
|
318
|
+
<p align="center">
|
|
319
|
+
<img src="./assets/screenshot-watch.png" alt="crosscheck watch — live pipeline view" width="860" />
|
|
320
|
+
</p>
|
|
321
|
+
|
|
307
322
|
**On a server** — one always-on watcher for a team, with per-repo depth where it matters.
|
|
308
323
|
|
|
309
324
|
```bash
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"base-ref-recovery.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/base-ref-recovery.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
2
|
+
import { execFileSync } from 'child_process';
|
|
3
|
+
import { mkdtempSync, rmSync, writeFileSync } from 'fs';
|
|
4
|
+
import { join } from 'path';
|
|
5
|
+
import { tmpdir } from 'os';
|
|
6
|
+
import { resolveBaseRef, BaseRefUnavailableError, isBaseRefUnavailableError } from '../lib/clone.js';
|
|
7
|
+
// Exercised against a real local origin rather than a mock: the whole point of the
|
|
8
|
+
// recovery chain is which git invocations actually put a ref in place, and a mock
|
|
9
|
+
// that answers "fetched" proves nothing about git.
|
|
10
|
+
describe('resolveBaseRef', () => {
|
|
11
|
+
let originDir;
|
|
12
|
+
let workDir;
|
|
13
|
+
let baseSha;
|
|
14
|
+
const git = (dir, ...args) => execFileSync('git', args, { cwd: dir, encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'] }).trim();
|
|
15
|
+
const commit = (dir, file, message) => {
|
|
16
|
+
writeFileSync(join(dir, file), `${message}\n`);
|
|
17
|
+
git(dir, 'add', '-A');
|
|
18
|
+
git(dir, '-c', 'user.email=t@t.t', '-c', 'user.name=t', 'commit', '-m', message);
|
|
19
|
+
return git(dir, 'rev-parse', 'HEAD');
|
|
20
|
+
};
|
|
21
|
+
beforeEach(() => {
|
|
22
|
+
// origin: a normal repo with a `main` and a `feature-base` the PR targets.
|
|
23
|
+
originDir = mkdtempSync(join(tmpdir(), 'crosscheck-origin-'));
|
|
24
|
+
git(originDir, 'init', '--initial-branch=main', '.');
|
|
25
|
+
commit(originDir, 'a.txt', 'root');
|
|
26
|
+
git(originDir, 'checkout', '-b', 'feature-base');
|
|
27
|
+
baseSha = commit(originDir, 'b.txt', 'base work');
|
|
28
|
+
git(originDir, 'checkout', '-b', 'pr-head');
|
|
29
|
+
commit(originDir, 'c.txt', 'pr work');
|
|
30
|
+
git(originDir, 'checkout', 'main');
|
|
31
|
+
// The consumer clone, with the PR head checked out — never the base branch.
|
|
32
|
+
workDir = mkdtempSync(join(tmpdir(), 'crosscheck-work-'));
|
|
33
|
+
git(workDir, 'clone', originDir, '.');
|
|
34
|
+
git(workDir, 'checkout', '-b', 'pr-1', 'origin/pr-head');
|
|
35
|
+
// Drop the tracking ref the clone created, so each case starts from "absent".
|
|
36
|
+
git(workDir, 'update-ref', '-d', 'refs/remotes/origin/feature-base');
|
|
37
|
+
});
|
|
38
|
+
afterEach(() => {
|
|
39
|
+
rmSync(originDir, { force: true, recursive: true });
|
|
40
|
+
rmSync(workDir, { force: true, recursive: true });
|
|
41
|
+
});
|
|
42
|
+
it('fetches the base by name when the branch exists', async () => {
|
|
43
|
+
const status = await resolveBaseRef({ tmpDir: workDir, baseRef: 'feature-base', prNumber: 1 });
|
|
44
|
+
expect(status).toBe('fetched');
|
|
45
|
+
expect(git(workDir, 'rev-parse', 'refs/remotes/origin/feature-base')).toBe(baseSha);
|
|
46
|
+
});
|
|
47
|
+
it('recovers by commit when the base branch has been deleted', async () => {
|
|
48
|
+
// The incident: PR #3649's base branch was gone by the time the review ran.
|
|
49
|
+
git(originDir, 'branch', '-D', 'feature-base');
|
|
50
|
+
const recovered = [];
|
|
51
|
+
const status = await resolveBaseRef({
|
|
52
|
+
tmpDir: workDir, baseRef: 'feature-base', prNumber: 1, baseSha,
|
|
53
|
+
onBaseRefRecovered: s => recovered.push(s),
|
|
54
|
+
});
|
|
55
|
+
expect(status).toBe('recovered_by_sha');
|
|
56
|
+
expect(recovered).toEqual(['recovered_by_sha']);
|
|
57
|
+
// The ref must actually resolve — the whole failure was trusting an exit code.
|
|
58
|
+
expect(git(workDir, 'rev-parse', 'refs/remotes/origin/feature-base')).toBe(baseSha);
|
|
59
|
+
});
|
|
60
|
+
it("recovers via the PR's merge ref when the base commit is not fetchable by name", async () => {
|
|
61
|
+
// GitHub keeps refs/pull/<n>/merge for an open PR; its first parent is the base.
|
|
62
|
+
const mergeSha = git(originDir, 'commit-tree', `${baseSha}^{tree}`, '-p', baseSha, '-p', git(originDir, 'rev-parse', 'pr-head'), '-m', 'merge');
|
|
63
|
+
git(originDir, 'update-ref', 'refs/pull/1/merge', mergeSha);
|
|
64
|
+
git(originDir, 'branch', '-D', 'feature-base');
|
|
65
|
+
const status = await resolveBaseRef({
|
|
66
|
+
tmpDir: workDir, baseRef: 'feature-base', prNumber: 1,
|
|
67
|
+
// No baseSha, so recovery 1 cannot run and recovery 2 must carry it.
|
|
68
|
+
});
|
|
69
|
+
expect(status).toBe('recovered_by_merge_ref');
|
|
70
|
+
expect(git(workDir, 'rev-parse', 'refs/remotes/origin/feature-base')).toBe(baseSha);
|
|
71
|
+
});
|
|
72
|
+
it('reports unavailable when no recovery path exists', async () => {
|
|
73
|
+
git(originDir, 'branch', '-D', 'feature-base');
|
|
74
|
+
const failed = [];
|
|
75
|
+
const status = await resolveBaseRef({
|
|
76
|
+
tmpDir: workDir, baseRef: 'feature-base', prNumber: 1,
|
|
77
|
+
onBaseFetchFailed: () => failed.push(1),
|
|
78
|
+
});
|
|
79
|
+
expect(status).toBe('unavailable');
|
|
80
|
+
expect(failed.length).toBeGreaterThan(0);
|
|
81
|
+
});
|
|
82
|
+
it('reports unavailable when a stale base sha no longer resolves', async () => {
|
|
83
|
+
git(originDir, 'branch', '-D', 'feature-base');
|
|
84
|
+
const status = await resolveBaseRef({
|
|
85
|
+
tmpDir: workDir, baseRef: 'feature-base', prNumber: 1,
|
|
86
|
+
baseSha: '0'.repeat(40),
|
|
87
|
+
});
|
|
88
|
+
expect(status).toBe('unavailable');
|
|
89
|
+
});
|
|
90
|
+
});
|
|
91
|
+
describe('BaseRefUnavailableError', () => {
|
|
92
|
+
it('names the ref and says what to do', () => {
|
|
93
|
+
const err = new BaseRefUnavailableError('chore/in-4439-registry-machine-ownership');
|
|
94
|
+
expect(err.message).toContain('origin/chore/in-4439-registry-machine-ownership');
|
|
95
|
+
expect(err.message).toContain('Retarget the PR');
|
|
96
|
+
});
|
|
97
|
+
it('is identifiable through the guard', () => {
|
|
98
|
+
expect(isBaseRefUnavailableError(new BaseRefUnavailableError('x'))).toBe(true);
|
|
99
|
+
expect(isBaseRefUnavailableError(new Error('x'))).toBe(false);
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
//# sourceMappingURL=base-ref-recovery.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"base-ref-recovery.test.js","sourceRoot":"","sources":["../../src/__tests__/base-ref-recovery.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -13,6 +13,8 @@ describe('buildCodexEnv', () => {
|
|
|
13
13
|
LC_ALL: 'en_US.UTF-8',
|
|
14
14
|
HTTPS_PROXY: 'http://proxy:8080',
|
|
15
15
|
OPENAI_API_KEY: 'sk-real',
|
|
16
|
+
GIT_CONFIG_NOSYSTEM: '1',
|
|
17
|
+
GIT_CONFIG_GLOBAL: '/dev/null',
|
|
16
18
|
// None of these may survive.
|
|
17
19
|
GITHUB_TOKEN: 'ghp_secret',
|
|
18
20
|
GH_TOKEN: 'gho_secret',
|
|
@@ -29,6 +31,8 @@ describe('buildCodexEnv', () => {
|
|
|
29
31
|
expect(env.OPENAI_API_KEY).toBe('sk-real');
|
|
30
32
|
expect(env.HTTPS_PROXY).toBe('http://proxy:8080');
|
|
31
33
|
expect(env.LC_ALL).toBe('en_US.UTF-8');
|
|
34
|
+
expect(env.GIT_CONFIG_NOSYSTEM).toBe('1');
|
|
35
|
+
expect(env.GIT_CONFIG_GLOBAL).toBe('/dev/null');
|
|
32
36
|
});
|
|
33
37
|
it('drops every credential that is not codex\'s own', () => {
|
|
34
38
|
const env = buildCodexEnv({}, source);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"codex-env.test.js","sourceRoot":"","sources":["../../src/__tests__/codex-env.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAA;AAC7C,OAAO,EAAE,aAAa,EAAE,oBAAoB,EAAE,MAAM,2BAA2B,CAAA;AAE/E,gFAAgF;AAChF,gFAAgF;AAChF,iFAAiF;AACjF,kFAAkF;AAClF,sBAAsB;AACtB,QAAQ,CAAC,eAAe,EAAE,GAAG,EAAE;IAC7B,MAAM,MAAM,GAAG;QACb,IAAI,EAAE,UAAU;QAChB,IAAI,EAAE,WAAW;QACjB,IAAI,EAAE,aAAa;QACnB,MAAM,EAAE,aAAa;QACrB,WAAW,EAAE,mBAAmB;QAChC,cAAc,EAAE,SAAS;QACzB,6BAA6B;QAC7B,YAAY,EAAE,YAAY;QAC1B,QAAQ,EAAE,YAAY;QACtB,iBAAiB,EAAE,eAAe;QAClC,cAAc,EAAE,YAAY;QAC5B,yBAAyB,EAAE,OAAO;QAClC,qBAAqB,EAAE,YAAY;QACnC,SAAS,EAAE,YAAY;KACxB,CAAA;IAED,EAAE,CAAC,gDAAgD,EAAE,GAAG,EAAE;QACxD,MAAM,GAAG,GAAG,aAAa,CAAC,EAAE,EAAE,MAAM,CAAC,CAAA;QACrC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAA;QACjC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,CAAA;QAClC,MAAM,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAA;QAC1C,MAAM,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC,IAAI,CAAC,mBAAmB,CAAC,CAAA;QACjD,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAA;
|
|
1
|
+
{"version":3,"file":"codex-env.test.js","sourceRoot":"","sources":["../../src/__tests__/codex-env.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
2
2
|
import { execFileSync } from 'child_process';
|
|
3
|
-
import { mkdtempSync, rmSync } from 'fs';
|
|
3
|
+
import { appendFileSync, chmodSync, existsSync, mkdirSync, mkdtempSync, rmSync, writeFileSync } from 'fs';
|
|
4
4
|
import { tmpdir } from 'os';
|
|
5
5
|
import { join } from 'path';
|
|
6
|
-
import { withCredentialFreeOrigin } from '../lib/clone.js';
|
|
6
|
+
import { CompromisedCloneError, isolateGitConfig, withCredentialFreeOrigin } from '../lib/clone.js';
|
|
7
7
|
// An HTTPS clone stores https://x-access-token:<token>@github.com/... in
|
|
8
8
|
// .git/config, so the checkout carries a writable GitHub token as a plain file.
|
|
9
9
|
// Once a vendor agent runs shell commands in that checkout — which is what
|
|
@@ -50,10 +50,142 @@ describe('withCredentialFreeOrigin', () => {
|
|
|
50
50
|
expect(seen).toBe(ssh);
|
|
51
51
|
expect(originUrl()).toBe(ssh);
|
|
52
52
|
});
|
|
53
|
+
// A hook planted by the agent would otherwise fire on the runner's own commit
|
|
54
|
+
// and push in this same clone — after the credential is restored, and with
|
|
55
|
+
// crosscheck's environment, which carries GITHUB_TOKEN. Flagged by review on #313.
|
|
56
|
+
describe('hooks planted by the agent', () => {
|
|
57
|
+
const withToken = 'https://x-access-token:ghs_secret@github.com/o/r.git';
|
|
58
|
+
const effectiveHooksPath = () => {
|
|
59
|
+
try {
|
|
60
|
+
return execFileSync('git', ['config', '--get', 'core.hooksPath'], { cwd: dir, encoding: 'utf8' }).trim();
|
|
61
|
+
}
|
|
62
|
+
catch {
|
|
63
|
+
return '';
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
it('disables hooks for the body, so a dropped .git/hooks script never runs', async () => {
|
|
67
|
+
setOrigin(withToken);
|
|
68
|
+
let seen = '';
|
|
69
|
+
await withCredentialFreeOrigin(dir, async () => { seen = effectiveHooksPath(); });
|
|
70
|
+
expect(seen).toBe('/dev/null');
|
|
71
|
+
});
|
|
72
|
+
// The trusted config snapshot removes the re-point after the agent exits.
|
|
73
|
+
it('neutralises a plain core.hooksPath re-point and restores the credential', async () => {
|
|
74
|
+
setOrigin(withToken);
|
|
75
|
+
await withCredentialFreeOrigin(dir, async () => {
|
|
76
|
+
execFileSync('git', ['config', 'core.hooksPath', '.git/hooks'], { cwd: dir });
|
|
77
|
+
});
|
|
78
|
+
expect(effectiveHooksPath()).toBe('/dev/null');
|
|
79
|
+
expect(originUrl()).toBe(withToken);
|
|
80
|
+
});
|
|
81
|
+
it('neutralises a core.hooksPath re-point for an SSH remote', async () => {
|
|
82
|
+
const ssh = 'git@github.com:o/r.git';
|
|
83
|
+
setOrigin(ssh);
|
|
84
|
+
await withCredentialFreeOrigin(dir, async () => {
|
|
85
|
+
execFileSync('git', ['config', 'core.hooksPath', '.git/hooks'], { cwd: dir });
|
|
86
|
+
});
|
|
87
|
+
expect(effectiveHooksPath()).toBe('/dev/null');
|
|
88
|
+
expect(originUrl()).toBe(ssh);
|
|
89
|
+
});
|
|
90
|
+
it('neutralises a core.hooksPath re-point without an origin', async () => {
|
|
91
|
+
await withCredentialFreeOrigin(dir, async () => {
|
|
92
|
+
execFileSync('git', ['config', 'core.hooksPath', '.git/hooks'], { cwd: dir });
|
|
93
|
+
});
|
|
94
|
+
expect(effectiveHooksPath()).toBe('/dev/null');
|
|
95
|
+
});
|
|
96
|
+
it('removes a re-point hidden behind an include', async () => {
|
|
97
|
+
setOrigin(withToken);
|
|
98
|
+
await withCredentialFreeOrigin(dir, async () => {
|
|
99
|
+
writeFileSync(join(dir, 'evil.cfg'), '[core]\n\thooksPath = .git/hooks\n');
|
|
100
|
+
appendFileSync(join(dir, '.git', 'config'), '[include]\n\tpath = ../evil.cfg\n');
|
|
101
|
+
});
|
|
102
|
+
expect(effectiveHooksPath()).toBe('/dev/null');
|
|
103
|
+
expect(originUrl()).toBe(withToken);
|
|
104
|
+
});
|
|
105
|
+
it('restores non-hook config that could execute during later Git commands', async () => {
|
|
106
|
+
const monitor = join(dir, 'fsmonitor.sh');
|
|
107
|
+
const marker = join(dir, 'fsmonitor-ran');
|
|
108
|
+
writeFileSync(monitor, '#!/bin/sh\nprintf ran > fsmonitor-ran\n');
|
|
109
|
+
chmodSync(monitor, 0o755);
|
|
110
|
+
await withCredentialFreeOrigin(dir, async () => {
|
|
111
|
+
execFileSync('git', ['config', 'core.fsmonitor', monitor], { cwd: dir });
|
|
112
|
+
execFileSync('git', ['status', '--short'], { cwd: dir, stdio: 'pipe' });
|
|
113
|
+
expect(existsSync(marker)).toBe(true);
|
|
114
|
+
rmSync(marker);
|
|
115
|
+
});
|
|
116
|
+
execFileSync('git', ['status', '--short'], { cwd: dir, stdio: 'pipe' });
|
|
117
|
+
expect(existsSync(marker)).toBe(false);
|
|
118
|
+
expect(() => execFileSync('git', ['config', '--get', 'core.fsmonitor'], { cwd: dir, stdio: 'pipe' })).toThrow();
|
|
119
|
+
});
|
|
120
|
+
it('leaves the origin scrubbed when a pre-existing include is changed', async () => {
|
|
121
|
+
const included = join(dir, 'trusted.cfg');
|
|
122
|
+
writeFileSync(included, '[user]\n\tname = trusted\n');
|
|
123
|
+
appendFileSync(join(dir, '.git', 'config'), `[include]\n\tpath = ${included}\n`);
|
|
124
|
+
setOrigin(withToken);
|
|
125
|
+
await expect(withCredentialFreeOrigin(dir, async () => {
|
|
126
|
+
writeFileSync(included, '[user]\n\tname = changed\n');
|
|
127
|
+
})).rejects.toBeInstanceOf(CompromisedCloneError);
|
|
128
|
+
expect(originUrl()).toBe('https://github.com/o/r.git');
|
|
129
|
+
});
|
|
130
|
+
it('still restores the credential on the honest path', async () => {
|
|
131
|
+
setOrigin(withToken);
|
|
132
|
+
await withCredentialFreeOrigin(dir, async () => { });
|
|
133
|
+
expect(originUrl()).toBe(withToken);
|
|
134
|
+
});
|
|
135
|
+
});
|
|
53
136
|
it('runs the body when there is no origin at all', async () => {
|
|
54
137
|
let ran = false;
|
|
55
138
|
await withCredentialFreeOrigin(dir, async () => { ran = true; });
|
|
56
139
|
expect(ran).toBe(true);
|
|
57
140
|
});
|
|
58
141
|
});
|
|
142
|
+
describe('isolated Git config', () => {
|
|
143
|
+
const envKeys = [
|
|
144
|
+
'GIT_CONFIG_NOSYSTEM', 'GIT_CONFIG_GLOBAL',
|
|
145
|
+
'GIT_AUTHOR_NAME', 'GIT_AUTHOR_EMAIL', 'GIT_COMMITTER_NAME', 'GIT_COMMITTER_EMAIL',
|
|
146
|
+
];
|
|
147
|
+
let root;
|
|
148
|
+
let savedEnv;
|
|
149
|
+
beforeEach(() => {
|
|
150
|
+
root = mkdtempSync(join(tmpdir(), 'crosscheck-git-config-'));
|
|
151
|
+
savedEnv = {};
|
|
152
|
+
for (const key of envKeys) {
|
|
153
|
+
if (process.env[key] !== undefined)
|
|
154
|
+
savedEnv[key] = process.env[key];
|
|
155
|
+
}
|
|
156
|
+
});
|
|
157
|
+
afterEach(() => {
|
|
158
|
+
for (const key of envKeys) {
|
|
159
|
+
const value = savedEnv[key];
|
|
160
|
+
if (value === undefined)
|
|
161
|
+
delete process.env[key];
|
|
162
|
+
else
|
|
163
|
+
process.env[key] = value;
|
|
164
|
+
}
|
|
165
|
+
rmSync(root, { force: true, recursive: true });
|
|
166
|
+
});
|
|
167
|
+
it('prevents a poisoned global hooksPath from reaching a later clone', () => {
|
|
168
|
+
const source = join(root, 'source');
|
|
169
|
+
const hooks = join(root, 'hooks');
|
|
170
|
+
const marker = join(root, 'hook-ran');
|
|
171
|
+
const poisonedConfig = join(root, 'global.gitconfig');
|
|
172
|
+
execFileSync('git', ['init', '-q', source]);
|
|
173
|
+
writeFileSync(join(source, 'file.txt'), 'content\n');
|
|
174
|
+
execFileSync('git', ['-C', source, 'add', 'file.txt']);
|
|
175
|
+
execFileSync('git', ['-C', source, '-c', 'user.name=Test', '-c', 'user.email=test@example.com', 'commit', '-qm', 'initial']);
|
|
176
|
+
mkdirSync(hooks);
|
|
177
|
+
writeFileSync(join(hooks, 'post-checkout'), `#!/bin/sh\nprintf ran > ${JSON.stringify(marker)}\n`);
|
|
178
|
+
chmodSync(join(hooks, 'post-checkout'), 0o755);
|
|
179
|
+
execFileSync('git', ['config', '--file', poisonedConfig, 'core.hooksPath', hooks]);
|
|
180
|
+
const poisonedEnv = { ...process.env, GIT_CONFIG_NOSYSTEM: '1', GIT_CONFIG_GLOBAL: poisonedConfig };
|
|
181
|
+
execFileSync('git', ['clone', '-q', source, join(root, 'control')], { env: poisonedEnv });
|
|
182
|
+
expect(existsSync(marker)).toBe(true);
|
|
183
|
+
rmSync(marker);
|
|
184
|
+
process.env.GIT_CONFIG_GLOBAL = poisonedConfig;
|
|
185
|
+
isolateGitConfig();
|
|
186
|
+
execFileSync('git', ['clone', '-q', source, join(root, 'isolated')]);
|
|
187
|
+
expect(process.env.GIT_CONFIG_GLOBAL).toBe(process.platform === 'win32' ? 'NUL' : '/dev/null');
|
|
188
|
+
expect(existsSync(marker)).toBe(false);
|
|
189
|
+
});
|
|
190
|
+
});
|
|
59
191
|
//# sourceMappingURL=credential-free-origin.test.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"credential-free-origin.test.js","sourceRoot":"","sources":["../../src/__tests__/credential-free-origin.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAA;AACpE,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAA;AAC5C,OAAO,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,IAAI,CAAA;
|
|
1
|
+
{"version":3,"file":"credential-free-origin.test.js","sourceRoot":"","sources":["../../src/__tests__/credential-free-origin.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"demo-arc.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/demo-arc.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { readFileSync, existsSync } from 'node:fs';
|
|
3
|
+
import { join, dirname } from 'node:path';
|
|
4
|
+
import { fileURLToPath } from 'node:url';
|
|
5
|
+
// The demo's whole claim is that what it shows happened. These guard the two ways
|
|
6
|
+
// that claim rots silently: arc.json drifting away from a real captured run, and a
|
|
7
|
+
// scene file the replay reads going missing so a beat renders empty.
|
|
8
|
+
//
|
|
9
|
+
// `npm run demo:play -- --fast` in CI covers the rendering; this covers the data.
|
|
10
|
+
const demoDir = join(dirname(fileURLToPath(import.meta.url)), '..', '..', 'demo');
|
|
11
|
+
function arc() {
|
|
12
|
+
return JSON.parse(readFileSync(join(demoDir, 'arc.json'), 'utf8'));
|
|
13
|
+
}
|
|
14
|
+
describe('demo/arc.json', () => {
|
|
15
|
+
it('names the public run it was captured from', () => {
|
|
16
|
+
const a = arc();
|
|
17
|
+
// The provenance note in demo/README.md and the README caption both promise a
|
|
18
|
+
// checkable source. A capture that lost it makes both of them lies.
|
|
19
|
+
expect(a.capturedFrom.repo).toBe('humanbased-ai/crosscheck-proof-fixture');
|
|
20
|
+
expect(a.capturedFrom.url).toBe(`https://github.com/${a.capturedFrom.repo}/pull/${a.capturedFrom.pr}`);
|
|
21
|
+
expect(a.capturedAt).toMatch(/^\d{4}-\d{2}-\d{2}$/);
|
|
22
|
+
});
|
|
23
|
+
it('carries the beats the replay narrates', () => {
|
|
24
|
+
const a = arc();
|
|
25
|
+
expect(a.steps.some(s => s.event === 'review_complete')).toBe(true);
|
|
26
|
+
expect(a.steps.some(s => s.event === 'fix_complete')).toBe(true);
|
|
27
|
+
// A demo with no blocking finding has no story; this is the beat everything
|
|
28
|
+
// else hangs off.
|
|
29
|
+
expect(a.comments.some(c => c.verdict === 'BLOCK')).toBe(true);
|
|
30
|
+
});
|
|
31
|
+
it('keeps step timings monotonic, so the replay never waits backwards', () => {
|
|
32
|
+
const times = arc().steps.map(s => s.atMs);
|
|
33
|
+
expect(times).toEqual([...times].sort((x, y) => x - y));
|
|
34
|
+
expect(times[0]).toBeGreaterThanOrEqual(0);
|
|
35
|
+
});
|
|
36
|
+
it('carries a real review body for every verdict it claims', () => {
|
|
37
|
+
for (const c of arc().comments.filter(x => x.verdict !== undefined)) {
|
|
38
|
+
// Verbatim bodies only — a placeholder here would put words in the
|
|
39
|
+
// reviewer's mouth on camera.
|
|
40
|
+
expect(c.body).toMatch(/Code Review by|Recheck by/);
|
|
41
|
+
expect(c.body.length).toBeGreaterThan(200);
|
|
42
|
+
expect(c.id).toBeGreaterThan(0);
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
it('has the Critical Issues section the replay quotes from', () => {
|
|
46
|
+
const blocking = arc().comments.find(c => c.verdict === 'BLOCK');
|
|
47
|
+
expect(blocking?.body).toContain('## Critical Issues');
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
describe('demo/scenes', () => {
|
|
51
|
+
// Kept in step with the scene() calls in replay.ts. A missing file throws at
|
|
52
|
+
// record time, which is a bad moment to find out.
|
|
53
|
+
it.each(['01-diff.txt', '02-tests-pass.txt', '02-tests-pass-short.txt'])('%s exists and is not empty', name => {
|
|
54
|
+
const path = join(demoDir, 'scenes', name);
|
|
55
|
+
expect(existsSync(path)).toBe(true);
|
|
56
|
+
expect(readFileSync(path, 'utf8').trim().length).toBeGreaterThan(0);
|
|
57
|
+
});
|
|
58
|
+
it('the diff scene still shows the ownership filter being removed', () => {
|
|
59
|
+
// This single removed line is the defect the whole demo is about. If a
|
|
60
|
+
// re-capture loses it, the recording no longer demonstrates anything.
|
|
61
|
+
// The capture keeps git's colour codes, so the leading `-` is preceded by an
|
|
62
|
+
// ANSI escape rather than sitting at the start of the line.
|
|
63
|
+
// eslint-disable-next-line no-control-regex -- matching the ESC byte is the point
|
|
64
|
+
const diff = readFileSync(join(demoDir, 'scenes', '01-diff.txt'), 'utf8').replace(/\x1B\[[0-9;]*m/g, '');
|
|
65
|
+
expect(diff).toMatch(/^-\s+ownerId/m);
|
|
66
|
+
});
|
|
67
|
+
it('the passing-test scene shows a green suite', () => {
|
|
68
|
+
const tests = readFileSync(join(demoDir, 'scenes', '02-tests-pass.txt'), 'utf8');
|
|
69
|
+
expect(tests).toMatch(/Tests\s+2 passed/);
|
|
70
|
+
expect(tests).not.toMatch(/failed/);
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
//# sourceMappingURL=demo-arc.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"demo-arc.test.js","sourceRoot":"","sources":["../../src/__tests__/demo-arc.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"doc-only-verdict.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/doc-only-verdict.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { applySeverityGate, DOC_ONLY_GATE_NOTE, SEVERITY_GATE_NOTE } from '../lib/verdict.js';
|
|
3
|
+
import { isDocOnlyChange } from '../lib/review-strategy.js';
|
|
4
|
+
// A doc-only PR is never blocked. A finding about a sentence cannot break a build
|
|
5
|
+
// or a caller, and the `docs` class runs `review` with no fix or recheck step — so
|
|
6
|
+
// a BLOCK there had no path to ever clear, it just sat on the PR.
|
|
7
|
+
const BLOCKING_REVIEW = '## Critical Issues\n\n- [P1] The install command is wrong.\n';
|
|
8
|
+
describe('isDocOnlyChange', () => {
|
|
9
|
+
it.each([
|
|
10
|
+
[['README.md'], true],
|
|
11
|
+
[['docs/trust.md', 'docs/metrics.md'], true],
|
|
12
|
+
[['guide.mdx', 'notes.rst', 'manual.adoc'], true],
|
|
13
|
+
[['README.md', 'src/index.ts'], false],
|
|
14
|
+
[['src/index.ts'], false],
|
|
15
|
+
// An empty list is "unknown", not "doc-only": an unreadable diff must not
|
|
16
|
+
// suppress a verdict.
|
|
17
|
+
[[], false],
|
|
18
|
+
])('%j → %s', (files, expected) => {
|
|
19
|
+
expect(isDocOnlyChange(files)).toBe(expected);
|
|
20
|
+
});
|
|
21
|
+
it('is not fooled by a doc extension inside a source path', () => {
|
|
22
|
+
expect(isDocOnlyChange(['src/md/parser.ts'])).toBe(false);
|
|
23
|
+
});
|
|
24
|
+
it('requires every file to be prose, not merely most of them', () => {
|
|
25
|
+
// Stricter than the `docs` PR class, which matches on a *fraction* of doc
|
|
26
|
+
// churn. One source file in the diff means a finding could describe shipping
|
|
27
|
+
// code, so the verdict must stay free to block.
|
|
28
|
+
expect(isDocOnlyChange(['a.md', 'b.md', 'c.md', 'd.md', 'src/one.ts'])).toBe(false);
|
|
29
|
+
});
|
|
30
|
+
});
|
|
31
|
+
describe('a doc-only PR is never blocked', () => {
|
|
32
|
+
it('turns BLOCK into APPROVE', () => {
|
|
33
|
+
const gate = applySeverityGate('BLOCK', BLOCKING_REVIEW, { docOnly: true });
|
|
34
|
+
expect(gate).toEqual({ verdict: 'APPROVE', downgraded: true, reason: 'doc_only' });
|
|
35
|
+
});
|
|
36
|
+
it('turns NEEDS WORK into APPROVE even with a blocking finding', () => {
|
|
37
|
+
const gate = applySeverityGate('NEEDS WORK', BLOCKING_REVIEW, { docOnly: true });
|
|
38
|
+
expect(gate.verdict).toBe('APPROVE');
|
|
39
|
+
expect(gate.reason).toBe('doc_only');
|
|
40
|
+
});
|
|
41
|
+
it('leaves an APPROVE alone', () => {
|
|
42
|
+
expect(applySeverityGate('APPROVE', BLOCKING_REVIEW, { docOnly: true }))
|
|
43
|
+
.toEqual({ verdict: 'APPROVE', downgraded: false });
|
|
44
|
+
});
|
|
45
|
+
it('leaves a null verdict alone — nothing to cap', () => {
|
|
46
|
+
expect(applySeverityGate(null, BLOCKING_REVIEW, { docOnly: true }))
|
|
47
|
+
.toEqual({ verdict: null, downgraded: false });
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
describe('the cap applies only to doc-only PRs', () => {
|
|
51
|
+
it('a BLOCK on code still blocks', () => {
|
|
52
|
+
const gate = applySeverityGate('BLOCK', BLOCKING_REVIEW, { docOnly: false });
|
|
53
|
+
expect(gate).toEqual({ verdict: 'BLOCK', downgraded: false });
|
|
54
|
+
});
|
|
55
|
+
it('a BLOCK on code still blocks when docOnly is unset', () => {
|
|
56
|
+
expect(applySeverityGate('BLOCK', BLOCKING_REVIEW).verdict).toBe('BLOCK');
|
|
57
|
+
});
|
|
58
|
+
it('the existing severity rule is unchanged for code', () => {
|
|
59
|
+
// P3-only NEEDS WORK still downgrades, and reports the older reason so the
|
|
60
|
+
// caller picks the older note.
|
|
61
|
+
const gate = applySeverityGate('NEEDS WORK', '- [P3] Rename `x`.', { docOnly: false });
|
|
62
|
+
expect(gate).toEqual({ verdict: 'APPROVE', downgraded: true, reason: 'no_blocking_findings' });
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
describe('the two downgrades are distinguishable', () => {
|
|
66
|
+
it('reports a reason the caller can branch on for the note', () => {
|
|
67
|
+
// The notes say different things, and picking the wrong one tells the author
|
|
68
|
+
// there were no blocking findings when in fact there were and they were capped.
|
|
69
|
+
const docs = applySeverityGate('BLOCK', BLOCKING_REVIEW, { docOnly: true });
|
|
70
|
+
const nits = applySeverityGate('NEEDS WORK', '- [P3] nit', {});
|
|
71
|
+
expect(docs.reason).not.toBe(nits.reason);
|
|
72
|
+
});
|
|
73
|
+
it('the doc note reclassifies rather than dismisses the findings', () => {
|
|
74
|
+
expect(DOC_ONLY_GATE_NOTE).toMatch(/advisory/);
|
|
75
|
+
expect(DOC_ONLY_GATE_NOTE).toMatch(/worth reading/);
|
|
76
|
+
expect(DOC_ONLY_GATE_NOTE).not.toBe(SEVERITY_GATE_NOTE);
|
|
77
|
+
});
|
|
78
|
+
});
|
|
79
|
+
//# sourceMappingURL=doc-only-verdict.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"doc-only-verdict.test.js","sourceRoot":"","sources":["../../src/__tests__/doc-only-verdict.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -81,4 +81,18 @@ describe('classifyError — a failing clone is classified by its cause, not its
|
|
|
81
81
|
expect(classifyError(`${cloneCommand}\n\nremote: Repository not found.\nfatal: 403 Forbidden`)).toBe('permission');
|
|
82
82
|
});
|
|
83
83
|
});
|
|
84
|
+
describe('crosscheck fail-closed categories', () => {
|
|
85
|
+
it('classifies an unresolvable base ref', () => {
|
|
86
|
+
expect(classifyError('base ref origin/chore/in-4439-registry-machine-ownership could not be resolved — the branch is deleted or renamed, '
|
|
87
|
+
+ 'so the PR diff cannot be computed. Retarget the PR to a live base branch and re-run.')).toBe('base_ref');
|
|
88
|
+
});
|
|
89
|
+
it('classifies a reviewer that reported it could not review', () => {
|
|
90
|
+
expect(classifyError('codex review inconclusive — reviewer reported it could not perform the review')).toBe('inconclusive');
|
|
91
|
+
});
|
|
92
|
+
it('does not let the base-ref message fall through to auth', () => {
|
|
93
|
+
// The message contains no credential words, but the auth pattern is broad
|
|
94
|
+
// enough that ordering matters — assert the outcome, not the intent.
|
|
95
|
+
expect(classifyError('base ref origin/main could not be resolved')).not.toBe('auth');
|
|
96
|
+
});
|
|
97
|
+
});
|
|
84
98
|
//# sourceMappingURL=error-classification.test.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"error-classification.test.js","sourceRoot":"","sources":["../../src/__tests__/error-classification.test.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"error-classification.test.js","sourceRoot":"","sources":["../../src/__tests__/error-classification.test.ts"],"names":[],"mappings":"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"}
|