@motivation-labs/crosscheck 0.4.0 → 0.4.1-beta.1216e4e.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.
Files changed (90) hide show
  1. package/README.md +26 -9
  2. package/crosscheck.config.example.yml +98 -5
  3. package/dist/__tests__/backtrace.test.d.ts +2 -0
  4. package/dist/__tests__/backtrace.test.d.ts.map +1 -0
  5. package/dist/__tests__/backtrace.test.js +158 -0
  6. package/dist/__tests__/backtrace.test.js.map +1 -0
  7. package/dist/__tests__/loader.test.d.ts +2 -0
  8. package/dist/__tests__/loader.test.d.ts.map +1 -0
  9. package/dist/__tests__/loader.test.js +131 -0
  10. package/dist/__tests__/loader.test.js.map +1 -0
  11. package/dist/__tests__/optimize.test.js +16 -3
  12. package/dist/__tests__/optimize.test.js.map +1 -1
  13. package/dist/ck.d.ts +3 -0
  14. package/dist/ck.d.ts.map +1 -0
  15. package/dist/ck.js +8 -0
  16. package/dist/ck.js.map +1 -0
  17. package/dist/cli.js +12 -4
  18. package/dist/cli.js.map +1 -1
  19. package/dist/commands/diagnose.d.ts +1 -0
  20. package/dist/commands/diagnose.d.ts.map +1 -1
  21. package/dist/commands/diagnose.js +14 -0
  22. package/dist/commands/diagnose.js.map +1 -1
  23. package/dist/commands/init.d.ts.map +1 -1
  24. package/dist/commands/init.js +63 -29
  25. package/dist/commands/init.js.map +1 -1
  26. package/dist/commands/review.d.ts.map +1 -1
  27. package/dist/commands/review.js +12 -6
  28. package/dist/commands/review.js.map +1 -1
  29. package/dist/commands/serve.d.ts +7 -1
  30. package/dist/commands/serve.d.ts.map +1 -1
  31. package/dist/commands/serve.js +152 -34
  32. package/dist/commands/serve.js.map +1 -1
  33. package/dist/commands/status.d.ts.map +1 -1
  34. package/dist/commands/status.js +1 -0
  35. package/dist/commands/status.js.map +1 -1
  36. package/dist/commands/watch.d.ts +7 -1
  37. package/dist/commands/watch.d.ts.map +1 -1
  38. package/dist/commands/watch.js +348 -135
  39. package/dist/commands/watch.js.map +1 -1
  40. package/dist/config/loader.d.ts +10 -0
  41. package/dist/config/loader.d.ts.map +1 -1
  42. package/dist/config/loader.js +196 -0
  43. package/dist/config/loader.js.map +1 -1
  44. package/dist/config/schema.d.ts +461 -35
  45. package/dist/config/schema.d.ts.map +1 -1
  46. package/dist/config/schema.js +72 -5
  47. package/dist/config/schema.js.map +1 -1
  48. package/dist/github/client.d.ts +26 -0
  49. package/dist/github/client.d.ts.map +1 -1
  50. package/dist/github/client.js +159 -2
  51. package/dist/github/client.js.map +1 -1
  52. package/dist/github/detector.d.ts +9 -2
  53. package/dist/github/detector.d.ts.map +1 -1
  54. package/dist/github/detector.js +86 -10
  55. package/dist/github/detector.js.map +1 -1
  56. package/dist/lib/backtrace.d.ts +20 -0
  57. package/dist/lib/backtrace.d.ts.map +1 -0
  58. package/dist/lib/backtrace.js +75 -0
  59. package/dist/lib/backtrace.js.map +1 -0
  60. package/dist/lib/board.d.ts +54 -0
  61. package/dist/lib/board.d.ts.map +1 -0
  62. package/dist/lib/board.js +406 -0
  63. package/dist/lib/board.js.map +1 -0
  64. package/dist/lib/runner.d.ts +10 -1
  65. package/dist/lib/runner.d.ts.map +1 -1
  66. package/dist/lib/runner.js +129 -51
  67. package/dist/lib/runner.js.map +1 -1
  68. package/dist/lib/verdict.d.ts +1 -0
  69. package/dist/lib/verdict.d.ts.map +1 -1
  70. package/dist/lib/verdict.js +27 -7
  71. package/dist/lib/verdict.js.map +1 -1
  72. package/dist/lib/workflow.d.ts +14 -14
  73. package/dist/lib/workflow.d.ts.map +1 -1
  74. package/dist/lib/workflow.js +22 -5
  75. package/dist/lib/workflow.js.map +1 -1
  76. package/dist/reviewers/claude.d.ts +1 -1
  77. package/dist/reviewers/claude.d.ts.map +1 -1
  78. package/dist/reviewers/claude.js +4 -6
  79. package/dist/reviewers/claude.js.map +1 -1
  80. package/dist/reviewers/codex.d.ts +2 -2
  81. package/dist/reviewers/codex.d.ts.map +1 -1
  82. package/dist/reviewers/codex.js +6 -6
  83. package/dist/reviewers/codex.js.map +1 -1
  84. package/dist/reviewers/fix.d.ts +5 -0
  85. package/dist/reviewers/fix.d.ts.map +1 -0
  86. package/dist/reviewers/fix.js +87 -0
  87. package/dist/reviewers/fix.js.map +1 -0
  88. package/get-started.md +202 -23
  89. package/get-started.zh.md +2 -3
  90. package/package.json +4 -3
package/README.md CHANGED
@@ -74,13 +74,15 @@ crosscheck watch
74
74
  │ ├── clone PR branch │
75
75
  │ ├── run reviewer (cross-vendor assignment) │
76
76
  │ ├── post review comment │
77
- │ └── address step (fix issues, push [crosscheck] commit)
77
+ │ └── post-review auto-fix
78
+ │ ├── authoring vendor reads review comment │
79
+ │ └── opens fix PR targeting original branch │
78
80
  └────────────────────────────────────────────────────────────────┘
79
81
  ```
80
82
 
81
- **Routing** reads PR body patterns. `Generated with [Claude Code]` → Codex reviews. `Generated with [OpenAI Codex]` → Claude reviews. `allowed_authors` restricts reviews to your agent accounts.
83
+ **Routing** uses a four-signal chain: PR body patterns → commit `Co-Authored-By:` trailers → branch prefix (`claude/` or `codex/`) → `author_routes` config fallback. `Generated with [Claude Code]` → Codex reviews. `Generated with [OpenAI Codex]` → Claude reviews. `allowed_authors` restricts reviews to your agent accounts.
82
84
 
83
- **The address step** (optional, enabled via workflow config) runs after the review. The author agent reads its own review comment and commits fixes back to the PR branch. Commits are prefixed `[crosscheck]`. Hard cap: 5 address commits per PR.
85
+ **Post-review auto-fix** runs after the review when issues are found. The vendor that authored the PR (`fixer: same-as-author`) reads its own review comment and opens a new fix PR targeting the original branch. You review and merge the fix PR the original PR then updates automatically. Controlled by `post_review.auto_fix` in your config.
84
86
 
85
87
  **The feedback loop** closes via `crosscheck diagnose` → `crosscheck optimize`. Failure patterns and quality signals from `~/.crosscheck/logs/` feed back into improved reviewer instructions — no manual config editing required.
86
88
 
@@ -98,6 +100,7 @@ crosscheck watch
98
100
  repos your-org/your-repo
99
101
  mode cross-vendor
100
102
  quality balanced
103
+ auto-fix on_issues · same-as-author · pull_request
101
104
  config ./crosscheck.config.yml ← edit to change above
102
105
 
103
106
  ✓ tunnel ready: https://abc123.lhr.life
@@ -110,12 +113,14 @@ PR #47 opened: add retry logic for flaky network calls
110
113
  review complete (12s)
111
114
  posted → github.com/your-org/your-repo/pull/47
112
115
  NEEDS WORK
116
+ auto-fix claude fixing...
117
+ fix PR #48 opened → github.com/your-org/your-repo/pull/48
113
118
 
114
- PR #48 opened: implement caching layer
119
+ PR #49 opened: implement caching layer
115
120
  origin=codex reviewer=claude
116
121
  claude reviewing... (18s)
117
122
  review complete (18s)
118
- posted → github.com/your-org/your-repo/pull/48
123
+ posted → github.com/your-org/your-repo/pull/49
119
124
  APPROVE
120
125
  ```
121
126
 
@@ -138,7 +143,7 @@ crosscheck impact [--money] # time saved, issues caught, code quality tr
138
143
 
139
144
  ## Configuration
140
145
 
141
- `crosscheck.config.yml` lives in your project root. Coding agents can read and modify it directly.
146
+ Config lives at `~/.crosscheck/config.yml` by default persistent across all projects, no per-repo file needed. Run `crosscheck init` to generate it. Project-local `crosscheck.config.yml` overrides the global config when present.
142
147
 
143
148
  ```yaml
144
149
  # Which repos/orgs to watch (at least one required)
@@ -146,10 +151,10 @@ orgs:
146
151
  - your-org # covers every repo in the org
147
152
 
148
153
  # Only review PRs from these GitHub accounts
154
+ # Auto-filled with your login by `crosscheck init` or first `crosscheck watch`
149
155
  routing:
150
156
  allowed_authors:
151
- - your-claude-bot-account
152
- - your-codex-bot-account
157
+ - your-github-login # auto-detected from gh auth
153
158
 
154
159
  # Review depth
155
160
  quality:
@@ -165,6 +170,16 @@ budget:
165
170
  # smee — stable channel URL, queues events while offline
166
171
  tunnel:
167
172
  backend: localhost.run
173
+
174
+ # Post-review auto-fix — opens a fix PR when issues are found
175
+ post_review:
176
+ auto_fix:
177
+ enabled: true
178
+ trigger: on_issues # on_issues | always | never
179
+ min_severity: warning # error | warning | info
180
+ fixer: same-as-author # the vendor that wrote the PR also fixes it
181
+ delivery:
182
+ mode: pull_request # pull_request | commit | comment
168
183
  ```
169
184
 
170
185
  Full configuration reference: [get-started.md](./get-started.md)
@@ -254,12 +269,14 @@ npm install -g smee-client
254
269
  ```
255
270
 
256
271
  ```yaml
257
- # crosscheck.config.yml
272
+ # ~/.crosscheck/config.yml
258
273
  tunnel:
259
274
  backend: smee
260
275
  smee_channel: https://smee.io/your-channel-id
261
276
  ```
262
277
 
278
+ crosscheck registers the webhook automatically on first `watch` start — no manual webhook setup needed.
279
+
263
280
  ---
264
281
 
265
282
  ## Requirements
@@ -2,6 +2,13 @@
2
2
  # This file is designed to be easily read and modified by coding agents.
3
3
  # All fields are optional — crosscheck uses sensible defaults.
4
4
 
5
+ # ─── Deployment ──────────────────────────────────────────────────────────────
6
+ # Set automatically on first run of `crosscheck watch` or `crosscheck serve`.
7
+ # personal — monitor your own repos and orgs; review only PRs you author
8
+ # team — monitor org repos only; review all PRs from any author
9
+ # Absent = first-run prompt will ask you to choose.
10
+ # deployment: personal
11
+
5
12
  # ─── Mode ────────────────────────────────────────────────────────────────────
6
13
  # single-vendor: one AI reviews all PRs
7
14
  # cross-vendor: each AI reviews the other's PRs (Claude ↔ Codex)
@@ -49,6 +56,15 @@ orgs:
49
56
  - motivation-labs
50
57
  - codatta
51
58
 
59
+ # ─── Users ───────────────────────────────────────────────────────────────────
60
+ # Monitor all repos owned by personal GitHub accounts (non-org).
61
+ # At startup, crosscheck enumerates each user's repos and registers webhooks.
62
+ # Useful when your AI agents open PRs across many personal repos.
63
+ # Combines with `orgs` and `repos` — all configured sources are additive.
64
+ # users:
65
+ # - beingzy # your personal account
66
+ # - my-agent-login # a bot account that pushes to its own repos
67
+
52
68
  # ─── Repos ───────────────────────────────────────────────────────────────────
53
69
  # Omit entirely to auto-detect from `git remote` (useful in watch mode)
54
70
  # repos: still works for monitoring specific repos only
@@ -62,18 +78,41 @@ repos:
62
78
  # ─── Routing ─────────────────────────────────────────────────────────────────
63
79
  routing:
64
80
  # Restrict reviews to PRs opened by these GitHub logins.
65
- # Recommended: set to the accounts your AI agents use.
81
+ # Auto-filled with your login by `crosscheck init` or first `crosscheck watch`.
66
82
  # Empty list = no author restriction (applies body-pattern check only).
67
83
  # allowed_authors:
68
84
  # - beingzy # your personal account (Claude Code runs as you)
69
85
  # - my-org-bot # a dedicated bot account
70
86
 
71
- # Body-pattern matchingdetermines which reviewer is assigned
87
+ # Author-based routing fallback maps GitHub login vendor origin.
88
+ # Used when no body pattern matches. Lets you skip the attribution footer requirement.
89
+ # beingzy: claude → PRs from beingzy treated as Claude-authored → Codex reviews them
90
+ # my-bot: codex → PRs from my-bot treated as Codex-authored → Claude reviews them
91
+ # author_routes:
92
+ # beingzy: claude
93
+
94
+ # Fallback reviewer when origin cannot be determined (no pattern/branch/route matched).
95
+ # 'auto' = pick whichever vendor is currently authenticated (codex first, then claude).
96
+ # 'codex' / 'claude' = always use that vendor.
97
+ # null = skip the PR (legacy cross-vendor behaviour).
98
+ fallback_reviewer: auto
99
+
100
+ # Branch-prefix routing — checked when body and commit patterns don't match.
101
+ # Agents should branch with these prefixes (claude/* and codex/*) so origin is
102
+ # always detectable even without attribution text in the PR body.
103
+ claude_branch_prefixes:
104
+ - "claude/"
105
+ codex_branch_prefixes:
106
+ - "codex/"
107
+
108
+ # Body and commit pattern matching — checked first, before branch prefix.
109
+ # Patterns are matched against both the PR body and commit messages (Co-Authored-By trailers).
72
110
  codex_reviews_patterns:
73
- - "Generated with \\[Claude Code\\]" # Claude Code attribution footer
111
+ - "Generated with \\[Claude Code\\]" # PR body attribution footer
112
+ - "Co-Authored-By: Claude" # commit trailer added by Claude Code
74
113
  claude_reviews_patterns:
75
- - "Generated with \\[OpenAI Codex\\]" # Codex attribution footer
76
- - "Co-Authored-By: codex"
114
+ - "Generated with \\[OpenAI Codex\\]" # PR body attribution footer
115
+ - "Co-Authored-By: codex" # commit trailer added by Codex
77
116
 
78
117
  # ─── Tunnel (watch mode only) ────────────────────────────────────────────────
79
118
  # Controls how crosscheck watch receives GitHub webhook events on your laptop.
@@ -100,9 +139,63 @@ server:
100
139
  webhook_path: /webhook
101
140
  # pid_file: ~/.crosscheck/crosscheck.pid # for daemon management
102
141
 
142
+ # ─── Post-Review Auto-Fix ────────────────────────────────────────────────────
143
+ # After a CR completes, optionally dispatch the authoring agent to fix the issues.
144
+ #
145
+ # Complete loop:
146
+ # agent opens PR → opposite vendor reviews → authoring vendor fixes
147
+ # ↓
148
+ # fix PR opened targeting original branch
149
+ # ↓
150
+ # human reviews fix PR → merges → original PR updated
151
+ #
152
+ post_review:
153
+ auto_fix:
154
+ enabled: true
155
+ trigger: on_issues # on_issues | always | never
156
+ min_severity: warning # error | warning | info — skip cosmetic findings
157
+
158
+ # same-as-author: the vendor that wrote the PR also applies the fix (recommended)
159
+ # In cross-vendor mode: Claude wrote it → Claude fixes it, Codex wrote it → Codex fixes it
160
+ fixer: same-as-author # same-as-author | same-as-reviewer | codex | claude
161
+
162
+ delivery:
163
+ mode: pull_request # pull_request | commit | comment
164
+ # pull_request → fix PR targets the original branch; human approves before merge
165
+ # commit → fixes pushed directly onto the original PR branch
166
+ # comment → suggested fixes posted as review comments only
167
+ pr_title: "fix: address CR issues in #{original_pr_title}"
168
+ label: cr-autofix # GitHub label applied to the fix PR
169
+
170
+ # ─── Backtrace ───────────────────────────────────────────────────────────────
171
+ # On startup, scan all open PRs in the monitored scope and review any that have
172
+ # never received a [crosscheck] comment. Ensures coverage for PRs opened while
173
+ # crosscheck was offline or before it was first set up.
174
+ # backtrace:
175
+ # enabled: true # set to false to disable the startup scan entirely
176
+
103
177
  # ─── Logs ────────────────────────────────────────────────────────────────────
104
178
  # Structured debug logs written to ~/.crosscheck/logs/YYYY-MM-DD.ndjson
105
179
  # Each line is a JSON object: { ts, level, event, ...context }
106
180
  logs:
107
181
  enabled: true
108
182
  retention_days: 7 # delete files older than N days (min: 1, max: 30)
183
+
184
+ # ─── Display ─────────────────────────────────────────────────────────────────
185
+ # Terminal output theme for crosscheck watch.
186
+ # Override individual colors using chalk color names (red, green, blue, cyan,
187
+ # yellow, magenta, white, gray, dim, bold) or hex values (#rrggbb).
188
+ # display:
189
+ # theme:
190
+ # bar_fill: blue # filled █ segments on the PR size bar
191
+ # bar_empty: dim # empty ░ segments (all bars)
192
+ # cr_approve: green # CR bar + verdict label when APPROVE
193
+ # cr_needs_work: yellow # CR bar + verdict label when NEEDS WORK
194
+ # cr_block: red # CR bar + verdict label when BLOCK
195
+ # fix_fill: cyan # filled segments on the FIX bar
196
+
197
+ # ─── Backtrace ───────────────────────────────────────────────────────────────
198
+ # On startup, scan all open PRs in the monitored scope and review any that have
199
+ # never received a [crosscheck] comment.
200
+ # backtrace:
201
+ # enabled: true # set to false to disable the startup scan entirely
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=backtrace.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"backtrace.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/backtrace.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,158 @@
1
+ import { describe, it, expect, vi, beforeEach } from 'vitest';
2
+ import { scanUnreviewedPRs, buildScopesFromConfig } from '../lib/backtrace.js';
3
+ import { ConfigSchema } from '../config/schema.js';
4
+ vi.mock('../github/client.js', () => ({
5
+ listOpenPRs: vi.fn(),
6
+ listOrgRepos: vi.fn(),
7
+ listUserRepos: vi.fn(),
8
+ prHasCrossCheckComment: vi.fn(),
9
+ }));
10
+ const { listOpenPRs, listOrgRepos, listUserRepos, prHasCrossCheckComment } = await import('../github/client.js');
11
+ const mockListOpenPRs = vi.mocked(listOpenPRs);
12
+ const mockListOrgRepos = vi.mocked(listOrgRepos);
13
+ const mockListUserRepos = vi.mocked(listUserRepos);
14
+ const mockPrHasCrossCheckComment = vi.mocked(prHasCrossCheckComment);
15
+ const defaultConfig = ConfigSchema.parse({});
16
+ function makePR(overrides = {}) {
17
+ return {
18
+ number: overrides.number ?? 1,
19
+ title: 'test PR',
20
+ author: overrides.author ?? 'alice',
21
+ headSha: 'abc123',
22
+ headRef: 'feat/thing',
23
+ headRepo: 'acme/api',
24
+ baseRef: 'main',
25
+ body: null,
26
+ createdAt: overrides.createdAt ?? '2025-01-01T00:00:00Z',
27
+ };
28
+ }
29
+ beforeEach(() => {
30
+ vi.clearAllMocks();
31
+ });
32
+ describe('scanUnreviewedPRs', () => {
33
+ it('returns empty result for empty scopes without calling any API', async () => {
34
+ const result = await scanUnreviewedPRs([], defaultConfig, 'token');
35
+ expect(result).toEqual({ queued: [], alreadyReviewed: 0, skippedAuthor: 0 });
36
+ expect(mockListOpenPRs).not.toHaveBeenCalled();
37
+ });
38
+ it('includes a PR with no [crosscheck] comment', async () => {
39
+ const scopes = [{ owner: 'acme', repo: 'api' }];
40
+ mockListOpenPRs.mockResolvedValue([makePR()]);
41
+ mockPrHasCrossCheckComment.mockResolvedValue(false);
42
+ const result = await scanUnreviewedPRs(scopes, defaultConfig, 'token');
43
+ expect(result.queued).toHaveLength(1);
44
+ expect(result.queued[0].number).toBe(1);
45
+ expect(result.alreadyReviewed).toBe(0);
46
+ expect(result.skippedAuthor).toBe(0);
47
+ });
48
+ it('excludes a PR that already has a [crosscheck] comment', async () => {
49
+ const scopes = [{ owner: 'acme', repo: 'api' }];
50
+ mockListOpenPRs.mockResolvedValue([makePR()]);
51
+ mockPrHasCrossCheckComment.mockResolvedValue(true);
52
+ const result = await scanUnreviewedPRs(scopes, defaultConfig, 'token');
53
+ expect(result.queued).toHaveLength(0);
54
+ expect(result.alreadyReviewed).toBe(1);
55
+ });
56
+ it('excludes a PR whose author is not in allowed_authors', async () => {
57
+ const config = ConfigSchema.parse({ routing: { allowed_authors: ['bob'] } });
58
+ const scopes = [{ owner: 'acme', repo: 'api' }];
59
+ mockListOpenPRs.mockResolvedValue([makePR({ author: 'alice' })]);
60
+ const result = await scanUnreviewedPRs(scopes, config, 'token');
61
+ expect(result.queued).toHaveLength(0);
62
+ expect(result.skippedAuthor).toBe(1);
63
+ expect(mockPrHasCrossCheckComment).not.toHaveBeenCalled();
64
+ });
65
+ it('includes all authors when allowed_authors is empty', async () => {
66
+ const scopes = [{ owner: 'acme', repo: 'api' }];
67
+ mockListOpenPRs.mockResolvedValue([makePR({ author: 'anyone' })]);
68
+ mockPrHasCrossCheckComment.mockResolvedValue(false);
69
+ const result = await scanUnreviewedPRs(scopes, defaultConfig, 'token');
70
+ expect(result.queued).toHaveLength(1);
71
+ });
72
+ it('sorts queued PRs oldest-first', async () => {
73
+ const scopes = [{ owner: 'acme', repo: 'api' }];
74
+ mockListOpenPRs.mockResolvedValue([
75
+ makePR({ number: 3, createdAt: '2025-03-01T00:00:00Z' }),
76
+ makePR({ number: 1, createdAt: '2025-01-01T00:00:00Z' }),
77
+ makePR({ number: 2, createdAt: '2025-02-01T00:00:00Z' }),
78
+ ]);
79
+ mockPrHasCrossCheckComment.mockResolvedValue(false);
80
+ const result = await scanUnreviewedPRs(scopes, defaultConfig, 'token');
81
+ expect(result.queued.map(p => p.number)).toEqual([1, 2, 3]);
82
+ });
83
+ it('expands org scopes to individual repos', async () => {
84
+ const scopes = [{ org: 'acme' }];
85
+ mockListOrgRepos.mockResolvedValue([
86
+ { owner: 'acme', name: 'api' },
87
+ { owner: 'acme', name: 'frontend' },
88
+ ]);
89
+ mockListOpenPRs.mockResolvedValue([]);
90
+ await scanUnreviewedPRs(scopes, defaultConfig, 'token');
91
+ expect(mockListOrgRepos).toHaveBeenCalledWith('acme', 'token');
92
+ expect(mockListOpenPRs).toHaveBeenCalledWith('acme', 'api', 'token');
93
+ expect(mockListOpenPRs).toHaveBeenCalledWith('acme', 'frontend', 'token');
94
+ });
95
+ it('continues scan when listOpenPRs throws for one repo', async () => {
96
+ const scopes = [
97
+ { owner: 'acme', repo: 'broken' },
98
+ { owner: 'acme', repo: 'working' },
99
+ ];
100
+ mockListOpenPRs
101
+ .mockRejectedValueOnce(new Error('API error'))
102
+ .mockResolvedValueOnce([makePR()]);
103
+ mockPrHasCrossCheckComment.mockResolvedValue(false);
104
+ const result = await scanUnreviewedPRs(scopes, defaultConfig, 'token');
105
+ expect(result.queued).toHaveLength(1);
106
+ });
107
+ it('skips a PR when prHasCrossCheckComment throws', async () => {
108
+ const scopes = [{ owner: 'acme', repo: 'api' }];
109
+ mockListOpenPRs.mockResolvedValue([makePR()]);
110
+ mockPrHasCrossCheckComment.mockRejectedValue(new Error('API error'));
111
+ const result = await scanUnreviewedPRs(scopes, defaultConfig, 'token');
112
+ expect(result.queued).toHaveLength(0);
113
+ expect(result.alreadyReviewed).toBe(0);
114
+ });
115
+ it('handles mixed queued / already-reviewed / skipped in one scan', async () => {
116
+ const config = ConfigSchema.parse({ routing: { allowed_authors: ['alice', 'bob'] } });
117
+ const scopes = [{ owner: 'acme', repo: 'api' }];
118
+ mockListOpenPRs.mockResolvedValue([
119
+ makePR({ number: 1, author: 'alice' }), // queued
120
+ makePR({ number: 2, author: 'bob' }), // already reviewed
121
+ makePR({ number: 3, author: 'carol' }), // skipped (author)
122
+ ]);
123
+ mockPrHasCrossCheckComment
124
+ .mockResolvedValueOnce(false) // PR 1
125
+ .mockResolvedValueOnce(true); // PR 2
126
+ const result = await scanUnreviewedPRs(scopes, config, 'token');
127
+ expect(result.queued).toHaveLength(1);
128
+ expect(result.queued[0].number).toBe(1);
129
+ expect(result.alreadyReviewed).toBe(1);
130
+ expect(result.skippedAuthor).toBe(1);
131
+ });
132
+ });
133
+ describe('buildScopesFromConfig', () => {
134
+ it('builds org and repo scopes from config', async () => {
135
+ const config = ConfigSchema.parse({
136
+ orgs: ['acme'],
137
+ repos: [{ owner: 'bob', name: 'myrepo' }],
138
+ });
139
+ const scopes = await buildScopesFromConfig(config, 'token');
140
+ expect(scopes).toContainEqual({ org: 'acme' });
141
+ expect(scopes).toContainEqual({ owner: 'bob', repo: 'myrepo' });
142
+ });
143
+ it('expands users to repo scopes', async () => {
144
+ const config = ConfigSchema.parse({ users: ['alice'] });
145
+ mockListUserRepos.mockResolvedValue([{ owner: 'alice', name: 'proj' }]);
146
+ const scopes = await buildScopesFromConfig(config, 'token');
147
+ expect(scopes).toContainEqual({ owner: 'alice', repo: 'proj' });
148
+ expect(mockListUserRepos).toHaveBeenCalledWith('alice', 'token');
149
+ });
150
+ it('continues when listUserRepos throws', async () => {
151
+ const config = ConfigSchema.parse({ users: ['alice'], orgs: ['acme'] });
152
+ mockListUserRepos.mockRejectedValue(new Error('API error'));
153
+ const scopes = await buildScopesFromConfig(config, 'token');
154
+ expect(scopes).toContainEqual({ org: 'acme' });
155
+ expect(scopes).not.toContainEqual(expect.objectContaining({ owner: 'alice' }));
156
+ });
157
+ });
158
+ //# sourceMappingURL=backtrace.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"backtrace.test.js","sourceRoot":"","sources":["../../src/__tests__/backtrace.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=loader.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"loader.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/loader.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,131 @@
1
+ import { describe, it, expect, beforeEach, afterEach } from 'vitest';
2
+ import { mkdtempSync, writeFileSync, readFileSync, rmSync } from 'fs';
3
+ import { join } from 'path';
4
+ import { tmpdir } from 'os';
5
+ import { patchAllowedAuthors } from '../config/loader.js';
6
+ let tmpDir;
7
+ beforeEach(() => { tmpDir = mkdtempSync(join(tmpdir(), 'crosscheck-loader-test-')); });
8
+ afterEach(() => { rmSync(tmpDir, { recursive: true, force: true }); });
9
+ function write(name, content) {
10
+ const p = join(tmpDir, name);
11
+ writeFileSync(p, content);
12
+ return p;
13
+ }
14
+ function read(p) {
15
+ return readFileSync(p, 'utf8');
16
+ }
17
+ describe('patchAllowedAuthors', () => {
18
+ // ── Case 1: commented-out placeholder ───────────────────────────────────
19
+ it('case 1 — uncomments placeholder block', () => {
20
+ const p = write('c1.yml', [
21
+ 'mode: cross-vendor',
22
+ 'routing:',
23
+ ' # allowed_authors:',
24
+ ' # - beingzy',
25
+ '',
26
+ ].join('\n'));
27
+ expect(patchAllowedAuthors(p, 'alice')).toBe(true);
28
+ const out = read(p);
29
+ expect(out).toContain(' allowed_authors:\n - alice');
30
+ expect(out).not.toContain('# allowed_authors');
31
+ });
32
+ // ── Case 2: multi-line empty (key present, no entries) ──────────────────
33
+ it('case 2 — fills multi-line empty allowed_authors', () => {
34
+ const p = write('c2.yml', [
35
+ 'routing:',
36
+ ' allowed_authors:',
37
+ ' codex_reviews_patterns: []',
38
+ '',
39
+ ].join('\n'));
40
+ expect(patchAllowedAuthors(p, 'alice')).toBe(true);
41
+ expect(read(p)).toContain(' - alice');
42
+ });
43
+ it('case 2 — fills allowed_authors that has only comment lines', () => {
44
+ const p = write('c2b.yml', [
45
+ 'routing:',
46
+ ' allowed_authors:',
47
+ ' # - somebot',
48
+ '',
49
+ ].join('\n'));
50
+ expect(patchAllowedAuthors(p, 'alice')).toBe(true);
51
+ expect(read(p)).toContain(' - alice');
52
+ });
53
+ // ── Case 3: inline empty array ──────────────────────────────────────────
54
+ it('case 3 — fills allowed_authors: []', () => {
55
+ const p = write('c3a.yml', [
56
+ 'routing:',
57
+ ' allowed_authors: []',
58
+ '',
59
+ ].join('\n'));
60
+ expect(patchAllowedAuthors(p, 'alice')).toBe(true);
61
+ const out = read(p);
62
+ expect(out).toContain(' allowed_authors:\n - alice');
63
+ expect(out).not.toContain('[]');
64
+ });
65
+ it('case 3 — fills allowed_authors: [ ] (with space)', () => {
66
+ const p = write('c3b.yml', [
67
+ 'routing:',
68
+ ' allowed_authors: [ ]',
69
+ '',
70
+ ].join('\n'));
71
+ expect(patchAllowedAuthors(p, 'alice')).toBe(true);
72
+ expect(read(p)).toContain(' - alice');
73
+ });
74
+ // ── Case 4: routing: exists but no allowed_authors key ──────────────────
75
+ it('case 4 — appends allowed_authors after routing: when key is absent', () => {
76
+ const p = write('c4.yml', [
77
+ 'mode: cross-vendor',
78
+ 'routing:',
79
+ ' codex_reviews_patterns: []',
80
+ '',
81
+ ].join('\n'));
82
+ expect(patchAllowedAuthors(p, 'alice')).toBe(true);
83
+ const out = read(p);
84
+ expect(out).toContain(' allowed_authors:\n - alice');
85
+ });
86
+ // ── Case 5 (new): no routing: section at all ────────────────────────────
87
+ it('case 5 — appends routing block when no routing: section exists', () => {
88
+ const p = write('c5.yml', 'mode: cross-vendor\n');
89
+ expect(patchAllowedAuthors(p, 'alice')).toBe(true);
90
+ const out = read(p);
91
+ expect(out).toContain('routing:');
92
+ expect(out).toContain(' allowed_authors:');
93
+ expect(out).toContain(' - alice');
94
+ });
95
+ it('case 5 — works for completely minimal config (no trailing newline)', () => {
96
+ const p = write('c5b.yml', 'mode: cross-vendor');
97
+ expect(patchAllowedAuthors(p, 'bob')).toBe(true);
98
+ const out = read(p);
99
+ expect(out).toContain('routing:');
100
+ expect(out).toContain(' - bob');
101
+ });
102
+ it('case 5 — added routing block is valid YAML structure', () => {
103
+ const p = write('c5c.yml', 'mode: cross-vendor\n');
104
+ patchAllowedAuthors(p, 'alice');
105
+ const out = read(p);
106
+ // routing: must be a top-level key (no leading spaces)
107
+ expect(out).toMatch(/^routing:/m);
108
+ // allowed_authors must be indented under routing
109
+ expect(out).toMatch(/^ allowed_authors:/m);
110
+ // entry must be indented under allowed_authors
111
+ expect(out).toMatch(/^ - alice/m);
112
+ });
113
+ // ── Already-populated: should not modify ────────────────────────────────
114
+ it('returns false when allowed_authors already has entries', () => {
115
+ const p = write('populated.yml', [
116
+ 'routing:',
117
+ ' allowed_authors:',
118
+ ' - existingbot',
119
+ '',
120
+ ].join('\n'));
121
+ expect(patchAllowedAuthors(p, 'alice')).toBe(false);
122
+ expect(read(p)).not.toContain('alice');
123
+ });
124
+ // ── Login value ──────────────────────────────────────────────────────────
125
+ it('uses the provided login in the written entry', () => {
126
+ const p = write('login.yml', 'mode: cross-vendor\n');
127
+ patchAllowedAuthors(p, 'mybot');
128
+ expect(read(p)).toContain(' - mybot');
129
+ });
130
+ });
131
+ //# sourceMappingURL=loader.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"loader.test.js","sourceRoot":"","sources":["../../src/__tests__/loader.test.ts"],"names":[],"mappings":"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"}
@@ -4,18 +4,30 @@ function makeConfig(claudeEnabled, codexEnabled) {
4
4
  return {
5
5
  mode: 'cross-vendor',
6
6
  orgs: [],
7
+ users: [],
7
8
  repos: [],
8
- routing: { codex_reviews_patterns: [], claude_reviews_patterns: [], allowed_authors: [] },
9
+ routing: { codex_reviews_patterns: [], claude_reviews_patterns: [], claude_branch_prefixes: [], codex_branch_prefixes: [], allowed_authors: [], author_routes: {}, fallback_reviewer: 'auto' },
9
10
  server: { port: 7892, webhook_path: '/webhook' },
10
11
  quality: { tier: 'balanced', focus: [], custom_prompt: undefined },
11
12
  budget: { codex_monthly_usd: null, per_review_usd: 1 },
12
13
  vendors: {
13
- claude: { enabled: claudeEnabled, auth: 'subscription', effort: 'medium' },
14
- codex: { enabled: codexEnabled, auth: 'subscription', effort: 'medium' },
14
+ claude: { enabled: claudeEnabled, model: null, auth: 'subscription', effort: 'medium' },
15
+ codex: { enabled: codexEnabled, model: null, auth: 'subscription', effort: 'medium', quality: 'medium' },
15
16
  },
16
17
  logs: { enabled: false, retention_days: 7 },
17
18
  tunnel: { backend: 'localhost.run', smee_channel: '' },
18
19
  impact: { assumed_human_review_minutes: 60, hourly_rate_usd: 150, defect_cost_usd: 150 },
20
+ backtrace: { enabled: true },
21
+ display: { theme: { bar_fill: 'blue', bar_empty: 'dim', cr_approve: 'green', cr_needs_work: 'yellow', cr_block: 'red', fix_fill: 'cyan' } },
22
+ post_review: {
23
+ auto_fix: {
24
+ enabled: false,
25
+ trigger: 'on_issues',
26
+ min_severity: 'warning',
27
+ fixer: 'same-as-author',
28
+ delivery: { mode: 'pull_request', pr_title: 'fix: address CR issues in #{original_pr_title}', label: 'cr-autofix' },
29
+ },
30
+ },
19
31
  };
20
32
  }
21
33
  function makeReport(claudeAttempts = 0, claudeSuccesses = 0, codexAttempts = 0, codexSuccesses = 0) {
@@ -39,6 +51,7 @@ function makeReport(claudeAttempts = 0, claudeSuccesses = 0, codexAttempts = 0,
39
51
  summary: { total_reviews: 0, successful: 0, failed: 0, failure_rate: 0 },
40
52
  errors: [],
41
53
  verdict_distribution: { APPROVE: 0, NEEDS_WORK: 0, BLOCK: 0 },
54
+ verdict_parse_failures: 0,
42
55
  repos_seen: [],
43
56
  languages_detected: [],
44
57
  reviewer_performance: performance,
@@ -1 +1 @@
1
- {"version":3,"file":"optimize.test.js","sourceRoot":"","sources":["../../src/__tests__/optimize.test.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"optimize.test.js","sourceRoot":"","sources":["../../src/__tests__/optimize.test.ts"],"names":[],"mappings":"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"}