@rigour-labs/core 6.6.5 → 6.7.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 (154) hide show
  1. package/README.md +16 -92
  2. package/dist/environment.test.js +0 -4
  3. package/dist/gates/agent-team.d.ts +0 -2
  4. package/dist/gates/agent-team.js +0 -8
  5. package/dist/gates/checkpoint.d.ts +0 -1
  6. package/dist/gates/checkpoint.js +0 -1
  7. package/dist/gates/context.d.ts +0 -4
  8. package/dist/gates/context.js +7 -17
  9. package/dist/gates/deep-analysis.d.ts +0 -1
  10. package/dist/gates/deep-runner.js +0 -1
  11. package/dist/gates/frontend-secret-exposure.d.ts +1 -1
  12. package/dist/gates/frontend-secret-exposure.js +9 -3
  13. package/dist/gates/frontend-secret-exposure.test.js +6 -3
  14. package/dist/gates/hallucinated-imports/index.d.ts +0 -2
  15. package/dist/gates/hallucinated-imports/index.js +2 -2
  16. package/dist/gates/hallucinated-imports/js-resolver.js +1 -0
  17. package/dist/gates/hallucinated-imports.test.js +10 -0
  18. package/dist/gates/runner.js +3 -4
  19. package/dist/gates/test-quality-matchers.d.ts +0 -4
  20. package/dist/gates/test-quality-matchers.js +0 -7
  21. package/dist/gates/test-quality.d.ts +0 -2
  22. package/dist/gates/test-quality.js +1 -10
  23. package/dist/hooks/checker.js +1 -1
  24. package/dist/hooks/index.d.ts +2 -3
  25. package/dist/hooks/index.js +1 -2
  26. package/dist/hooks/stop-review.d.ts +6 -9
  27. package/dist/hooks/stop-review.js +5 -10
  28. package/dist/hooks/types.d.ts +0 -14
  29. package/dist/hooks/types.js +1 -15
  30. package/dist/index.d.ts +11 -3
  31. package/dist/index.js +11 -3
  32. package/dist/inference/cloud-provider.d.ts +2 -0
  33. package/dist/inference/cloud-provider.js +16 -6
  34. package/dist/inference/cloud-provider.test.js +21 -0
  35. package/dist/pattern-index/embeddings.d.ts +0 -5
  36. package/dist/pattern-index/embeddings.js +11 -4
  37. package/dist/pattern-index/semantic-runtime.d.ts +10 -0
  38. package/dist/pattern-index/semantic-runtime.js +46 -0
  39. package/dist/pattern-index/semantic-runtime.test.d.ts +1 -0
  40. package/dist/pattern-index/semantic-runtime.test.js +31 -0
  41. package/dist/review/backtest-init.d.ts +8 -0
  42. package/dist/review/backtest-init.js +87 -0
  43. package/dist/review/backtest-init.test.d.ts +1 -0
  44. package/dist/review/backtest-init.test.js +74 -0
  45. package/dist/review/backtest-judges.d.ts +38 -0
  46. package/dist/review/backtest-judges.js +63 -0
  47. package/dist/review/backtest-judges.test.d.ts +1 -0
  48. package/dist/review/backtest-judges.test.js +32 -0
  49. package/dist/review/backtest.d.ts +320 -0
  50. package/dist/review/backtest.js +207 -0
  51. package/dist/review/backtest.test.d.ts +1 -0
  52. package/dist/review/backtest.test.js +129 -0
  53. package/dist/review/baseline.js +2 -1
  54. package/dist/review/dead-code.test.js +15 -0
  55. package/dist/review/fix-scope.d.ts +21 -0
  56. package/dist/review/fix-scope.js +94 -0
  57. package/dist/review/fix-scope.test.d.ts +1 -0
  58. package/dist/review/fix-scope.test.js +104 -0
  59. package/dist/review/lint-overlay.d.ts +21 -0
  60. package/dist/review/lint-overlay.js +89 -0
  61. package/dist/review/orphan-files.js +3 -2
  62. package/dist/review/partial-wiring.js +5 -3
  63. package/dist/review/query-patterns.js +3 -2
  64. package/dist/review/quiet.d.ts +6 -0
  65. package/dist/review/quiet.js +12 -2
  66. package/dist/review/quiet.test.js +11 -0
  67. package/dist/review/review.d.ts +6 -0
  68. package/dist/review/review.js +10 -2
  69. package/dist/review/reviewer/adapters.d.ts +51 -0
  70. package/dist/review/reviewer/adapters.js +181 -0
  71. package/dist/review/reviewer/adapters.test.d.ts +1 -0
  72. package/dist/review/reviewer/adapters.test.js +23 -0
  73. package/dist/review/reviewer/background.d.ts +41 -0
  74. package/dist/review/reviewer/background.js +153 -0
  75. package/dist/review/reviewer/background.test.d.ts +1 -0
  76. package/dist/review/reviewer/background.test.js +118 -0
  77. package/dist/review/reviewer/consensus.d.ts +36 -0
  78. package/dist/review/reviewer/consensus.js +144 -0
  79. package/dist/review/reviewer/context.d.ts +75 -0
  80. package/dist/review/reviewer/context.js +140 -0
  81. package/dist/review/reviewer/context.test.d.ts +1 -0
  82. package/dist/review/reviewer/context.test.js +18 -0
  83. package/dist/review/reviewer/exec.d.ts +17 -0
  84. package/dist/review/reviewer/exec.js +27 -0
  85. package/dist/review/reviewer/exec.test.d.ts +1 -0
  86. package/dist/review/reviewer/exec.test.js +23 -0
  87. package/dist/review/reviewer/inputs.d.ts +39 -0
  88. package/dist/review/reviewer/inputs.js +107 -0
  89. package/dist/review/reviewer/merge-impact.d.ts +2 -0
  90. package/dist/review/reviewer/merge-impact.js +64 -0
  91. package/dist/review/reviewer/panel.d.ts +67 -0
  92. package/dist/review/reviewer/panel.js +101 -0
  93. package/dist/review/reviewer/panel.test.d.ts +1 -0
  94. package/dist/review/reviewer/panel.test.js +110 -0
  95. package/dist/review/reviewer/prompt.d.ts +32 -0
  96. package/dist/review/reviewer/prompt.js +145 -0
  97. package/dist/review/reviewer/settings.d.ts +58 -0
  98. package/dist/review/reviewer/settings.js +152 -0
  99. package/dist/review/reviewer/settings.test.d.ts +1 -0
  100. package/dist/review/reviewer/settings.test.js +57 -0
  101. package/dist/review/reviewer/store.d.ts +49 -0
  102. package/dist/review/reviewer/store.js +99 -0
  103. package/dist/review/reviewer/usage.d.ts +2 -0
  104. package/dist/review/reviewer/usage.js +28 -0
  105. package/dist/review/reviewer/usage.test.d.ts +1 -0
  106. package/dist/review/reviewer/usage.test.js +14 -0
  107. package/dist/review/reviewer/verdict.d.ts +141 -0
  108. package/dist/review/reviewer/verdict.js +166 -0
  109. package/dist/review/reviewer.d.ts +84 -49
  110. package/dist/review/reviewer.js +364 -195
  111. package/dist/review/reviewer.test.js +316 -50
  112. package/dist/review/test-files.d.ts +5 -1
  113. package/dist/review/test-files.js +6 -2
  114. package/dist/review/toolchain.d.ts +5 -2
  115. package/dist/review/toolchain.js +89 -7
  116. package/dist/review/toolchain.test.js +45 -4
  117. package/dist/review/typed/program.d.ts +26 -0
  118. package/dist/review/typed/program.js +90 -0
  119. package/dist/review/typed/redundancy.d.ts +10 -0
  120. package/dist/review/typed/redundancy.js +378 -0
  121. package/dist/review/typed/redundancy.test.d.ts +1 -0
  122. package/dist/review/typed/redundancy.test.js +179 -0
  123. package/dist/review/typed/schema-nullability.d.ts +6 -0
  124. package/dist/review/typed/schema-nullability.js +218 -0
  125. package/dist/review/typed/schema-nullability.test.d.ts +1 -0
  126. package/dist/review/typed/schema-nullability.test.js +63 -0
  127. package/dist/review/unused-exports.js +10 -7
  128. package/dist/semantic/learn/types.d.ts +2 -2
  129. package/dist/services/adaptive-thresholds.d.ts +2 -52
  130. package/dist/services/adaptive-thresholds.js +2 -147
  131. package/dist/services/adaptive-thresholds.test.js +1 -81
  132. package/dist/settings.d.ts +14 -6
  133. package/dist/settings.js +0 -12
  134. package/dist/storage/team-diagnostics.js +1 -1
  135. package/dist/storage/team-schema.d.ts +1 -1
  136. package/dist/storage/team-schema.js +2 -0
  137. package/dist/storage/team-store.js +26 -3
  138. package/dist/storage/team-sync-scope.test.js +35 -2
  139. package/dist/telemetry/telemetry.d.ts +5 -0
  140. package/dist/telemetry/telemetry.js +11 -1
  141. package/dist/templates/paradigms.js +0 -8
  142. package/dist/templates/presets.d.ts +0 -1
  143. package/dist/templates/presets.js +0 -23
  144. package/dist/templates/universal-config.js +16 -46
  145. package/dist/types/index.d.ts +202 -574
  146. package/dist/types/index.js +64 -67
  147. package/dist/types/index.test.d.ts +1 -0
  148. package/dist/types/index.test.js +33 -0
  149. package/dist/utils/command-line.d.ts +8 -0
  150. package/dist/utils/command-line.js +8 -0
  151. package/dist/utils/command-line.test.d.ts +1 -0
  152. package/dist/utils/command-line.test.js +8 -0
  153. package/dist/utils/profile.js +1 -1
  154. package/package.json +6 -7
@@ -1,9 +1,10 @@
1
1
  import { z } from 'zod';
2
+ /** A model name handed to an agent CLI as an argument: one that starts with "-" would be read as a flag. */
3
+ const ModelName = z.string().regex(/^[\w.:/@][\w.:/@-]*$/, 'a model name (letters, digits and . : / @ -), not starting with -');
2
4
  export const GatesSchema = z.object({
3
5
  max_file_lines: z.number().optional().default(500),
4
6
  forbid_todos: z.boolean().optional().default(true),
5
7
  forbid_fixme: z.boolean().optional().default(true),
6
- forbid_paths: z.array(z.string()).optional().default([]),
7
8
  required_files: z.array(z.string()).optional().default([
8
9
  'docs/SPEC.md',
9
10
  'docs/ARCH.md',
@@ -14,10 +15,6 @@ export const GatesSchema = z.object({
14
15
  complexity: z.number().optional().default(10),
15
16
  max_methods: z.number().optional().default(10),
16
17
  max_params: z.number().optional().default(5),
17
- max_nesting: z.number().optional().default(4),
18
- max_inheritance_depth: z.number().optional().default(3),
19
- max_class_dependencies: z.number().optional().default(5),
20
- max_function_lines: z.number().optional().default(50),
21
18
  }).optional().default({}),
22
19
  staleness: z.object({
23
20
  enabled: z.boolean().optional().default(false),
@@ -35,7 +32,6 @@ export const GatesSchema = z.object({
35
32
  }).optional().default({}),
36
33
  dependencies: z.object({
37
34
  forbid: z.array(z.string()).optional().default([]),
38
- trusted_registry: z.string().optional(),
39
35
  detect_unused: z.boolean().optional().default(true),
40
36
  detect_heavy_alternatives: z.boolean().optional().default(true),
41
37
  detect_duplicate_purpose: z.boolean().optional().default(true),
@@ -56,12 +52,6 @@ export const GatesSchema = z.object({
56
52
  enabled: z.boolean().optional().default(true),
57
53
  sensitivity: z.number().min(0).max(1).optional().default(0.8), // 0.8 correlation threshold
58
54
  mining_depth: z.number().optional().default(100), // Number of files to sample
59
- ignored_patterns: z.array(z.string()).optional().default([]),
60
- // v2.14+ Extended Context for frontier models
61
- cross_file_patterns: z.boolean().optional().default(true),
62
- naming_consistency: z.boolean().optional().default(true),
63
- import_relationships: z.boolean().optional().default(true),
64
- max_cross_file_depth: z.number().optional().default(50),
65
55
  }).optional().default({}),
66
56
  environment: z.object({
67
57
  enabled: z.boolean().optional().default(true),
@@ -72,14 +62,11 @@ export const GatesSchema = z.object({
72
62
  retry_loop_breaker: z.object({
73
63
  enabled: z.boolean().optional().default(true),
74
64
  max_retries: z.number().optional().default(3), // Fail after 3 consecutive failures in same category
75
- auto_classify: z.boolean().optional().default(true), // Auto-detect failure category from error message
76
65
  doc_sources: z.record(z.string()).optional().default({}), // Custom doc URLs per category
77
66
  }).optional().default({}),
78
67
  agent_team: z.object({
79
68
  enabled: z.boolean().optional().default(false),
80
69
  max_concurrent_agents: z.number().optional().default(3),
81
- cross_agent_pattern_check: z.boolean().optional().default(true),
82
- handoff_verification: z.boolean().optional().default(true),
83
70
  task_ownership: z.enum(['strict', 'collaborative']).optional().default('strict'),
84
71
  }).optional().default({}),
85
72
  checkpoint: z.object({
@@ -87,7 +74,6 @@ export const GatesSchema = z.object({
87
74
  interval_minutes: z.number().optional().default(15),
88
75
  quality_threshold: z.number().optional().default(80),
89
76
  drift_detection: z.boolean().optional().default(true),
90
- auto_save_on_failure: z.boolean().optional().default(true),
91
77
  }).optional().default({}),
92
78
  security: z.object({
93
79
  enabled: z.boolean().optional().default(true),
@@ -135,13 +121,6 @@ export const GatesSchema = z.object({
135
121
  ]),
136
122
  allowlist_env_names: z.array(z.string()).optional().default([]),
137
123
  }).optional().default({}),
138
- adaptive: z.object({
139
- enabled: z.boolean().optional().default(false),
140
- base_coverage_threshold: z.number().optional().default(80),
141
- base_quality_threshold: z.number().optional().default(80),
142
- auto_detect_tier: z.boolean().optional().default(true),
143
- forced_tier: z.enum(['hobby', 'startup', 'enterprise']).optional(),
144
- }).optional().default({}),
145
124
  // v2.16+ AI-Native Drift Detection Gates
146
125
  duplication_drift: z.object({
147
126
  enabled: z.boolean().optional().default(true),
@@ -150,8 +129,6 @@ export const GatesSchema = z.object({
150
129
  }).optional().default({}),
151
130
  hallucinated_imports: z.object({
152
131
  enabled: z.boolean().optional().default(true),
153
- check_relative: z.boolean().optional().default(true),
154
- check_packages: z.boolean().optional().default(true),
155
132
  ignore_patterns: z.array(z.string()).optional().default([
156
133
  '\\.css$', '\\.scss$', '\\.less$', '\\.svg$', '\\.png$', '\\.jpg$',
157
134
  '\\.json$', '\\.wasm$', '\\.graphql$', '\\.gql$',
@@ -203,10 +180,8 @@ export const GatesSchema = z.object({
203
180
  check_empty_tests: z.boolean().optional().default(true),
204
181
  check_tautological: z.boolean().optional().default(true),
205
182
  check_mock_heavy: z.boolean().optional().default(true),
206
- check_snapshot_abuse: z.boolean().optional().default(true),
207
183
  check_assertion_free_async: z.boolean().optional().default(true),
208
184
  max_mocks_per_test: z.number().optional().default(5),
209
- ignore_patterns: z.array(z.string()).optional().default([]),
210
185
  }).optional().default({}),
211
186
  // v4.2+ Memory & Skills Governance
212
187
  governance: z.object({
@@ -259,15 +234,6 @@ export const GatesSchema = z.object({
259
234
  '.windsurf/hooks.json', // Rigour's own hook config
260
235
  ]),
261
236
  }).optional().default({}),
262
- // v4.2+ AI Agent DLP (Data Loss Prevention)
263
- input_validation: z.object({
264
- enabled: z.boolean().optional().default(true),
265
- block_on_detection: z.boolean().optional().default(true),
266
- min_secret_length: z.number().optional().default(8),
267
- custom_patterns: z.array(z.string()).optional().default([]),
268
- ignore_patterns: z.array(z.string()).optional().default([]),
269
- audit_log: z.boolean().optional().default(true),
270
- }).optional().default({}),
271
237
  // v4.3+ Side-Effect Safety Analysis
272
238
  // On by default: full scans of three real repositories reported no finding that was not a real defect.
273
239
  // A supabase-js read no index can serve, proved from the repository's own migrations (advisory, off by default).
@@ -290,6 +256,19 @@ export const GatesSchema = z.object({
290
256
  query_patterns: z.object({ enabled: z.boolean().optional().default(true) }).optional().default({}),
291
257
  /** What a fix leaves half done: the narrower condition still used elsewhere, a prop wired into some sibling mounts only, an accumulator copied every step (review/partial-fixes.ts, partial-wiring.ts, loop-copies.ts). */
292
258
  change_sweep: z.object({ enabled: z.boolean().optional().default(true) }).optional().default({}),
259
+ /**
260
+ * What a change made redundant, from the project's own TypeScript (review/typed/redundancy.ts): a null filter beside a
261
+ * range on the same column, a nullable row type the query filters non-null, an optional member every host supplies, a
262
+ * property written and never read. Runs at push, in `rigour review` and in a backtest (the program takes seconds to build).
263
+ * `wire_contracts`: files whose types another service reads, so their members are never write-only here.
264
+ * `schema_migrations`: folders of SQL migrations (relative, absolute, or `~/`; another repository's is fine, read only)
265
+ * replayed to learn which columns are NOT NULL, for the nullable-not-null-column note. Missing folders are skipped.
266
+ */
267
+ redundancy: z.object({
268
+ enabled: z.boolean().optional().default(true),
269
+ wire_contracts: z.array(z.string()).optional().default([]),
270
+ schema_migrations: z.array(z.string()).optional().default(['supabase/migrations']),
271
+ }).optional().default({}),
293
272
  /** A parameter the change adds as optional that only tests omit (review/optional-params.ts). */
294
273
  optional_params: z.object({ enabled: z.boolean().optional().default(true) }).optional().default({}),
295
274
  /** A changed function whose body duplicates another in the files the change touched (review/duplicate-functions.ts). */
@@ -334,20 +313,11 @@ export const GatesSchema = z.object({
334
313
  track_branches: z.boolean().optional().default(false), // opt-in: counts change on purpose in most edits
335
314
  track_returns: z.boolean().optional().default(false), // opt-in: same
336
315
  }).optional().default({}),
337
- // v4.0+ Deep Analysis (LLM-powered)
316
+ // Model review. Whether it runs, the tier, the provider and its key come from flags and the person's settings, never from a committed file.
338
317
  deep: z.object({
339
- enabled: z.boolean().optional().default(false),
340
- pro: z.boolean().optional().default(false),
341
- max: z.boolean().optional().default(false), // local Qwen2.5-Coder-7B (4.7GB); needs ~8GB free memory
342
- provider: z.string().optional().default('local'), // 'local' for sidecar, or any cloud: 'claude', 'openai', 'gemini', 'groq', 'mistral', 'together', etc.
343
- api_key: z.string().optional(),
344
- api_base_url: z.string().optional(), // custom API base URL (for self-hosted, proxies, any OpenAI-compatible endpoint)
345
- model_name: z.string().optional(), // cloud model name override (e.g. 'gpt-4o', 'claude-sonnet-4-5-20250929', 'gemini-pro')
346
- model_path: z.string().optional(), // custom local GGUF model path override
347
- threads: z.number().optional().default(4),
348
318
  max_tokens: z.number().optional(), // default per provider: local 1024, cloud 4096
349
319
  temperature: z.number().optional().default(0.1),
350
- timeout_ms: z.number().optional(), // per inference call; default per provider: local 60s, cloud 120s
320
+ timeout_ms: z.number().optional(), // per model call; default: cloud 120s, local 60s, local --max 240s
351
321
  budget_ms: z.number().optional(), // whole deep run; files not started in time are reported as skipped
352
322
  agentic: z.boolean().optional(), // cloud tier: the model may read the repository while it reviews (default true)
353
323
  repo_rules: z.boolean().optional(), // show the reviewer the rules in AGENTS.md / CLAUDE.md / Cursor rules that name what the change touches (default false)
@@ -379,26 +349,10 @@ export const CommandsSchema = z.object({
379
349
  test: z.string().optional(),
380
350
  });
381
351
  export const HooksSchema = z.object({
382
- enabled: z.boolean().optional().default(false),
383
- tools: z.array(z.enum(['claude', 'cursor', 'cline', 'windsurf'])).optional().default([]),
384
- fast_gates: z.array(z.string()).optional().default([
385
- 'hallucinated-imports',
386
- 'phantom-apis',
387
- 'deprecated-apis',
388
- 'promise-safety',
389
- 'security-patterns',
390
- 'side-effect-analysis',
391
- 'file-size',
392
- ]),
393
- timeout_ms: z.number().optional().default(5000),
394
- block_on_failure: z.boolean().optional().default(false),
395
352
  /** Stop hook: hold "done" until the agent acknowledges each risky changed function (rigour_review_ack). */
396
353
  require_review_ack: z.boolean().optional().default(false),
397
- /** Enable DLP (Data Loss Prevention) pre-input hooks — default ON for security */
398
- dlp: z.boolean().optional().default(true),
399
354
  }).optional().default({});
400
355
  export const ConfigSchema = z.object({
401
- version: z.number().default(1),
402
356
  preset: z.string().optional(),
403
357
  paradigm: z.string().optional(),
404
358
  commands: CommandsSchema.optional().default({}),
@@ -415,15 +369,58 @@ export const ConfigSchema = z.object({
415
369
  show_preexisting: z.boolean().optional().default(false),
416
370
  /** The GitHub account whose token fetches the pull request's previous review (`gh auth token --user`). */
417
371
  github_account: z.string().optional(),
418
- /** The fresh reviewer (review/reviewer.ts): the person's own coding agent CLI, headless. `enabled` runs it at every push; `rigour review --reviewer` runs it on request. */
372
+ /** The reviewer (review/reviewer.ts): the person's own coding-agent CLIs, headless and read-only. `enabled` runs it at push; `rigour review --reviewer` runs it on request. */
419
373
  reviewer: z.object({
420
374
  enabled: z.boolean().optional().default(false),
421
- command: z.string().optional().default('claude'),
422
- model: z.string().optional(),
375
+ /**
376
+ * At push, once the deterministic gates pass: `background` (default) starts the model review of the pushed
377
+ * commit without holding the push; `wait` holds the push for it; `off` reviews only on request. Either way
378
+ * a model is asked only when the branch has an open, non-draft pull request (someone will read the push).
379
+ */
380
+ on_push: z.enum(['background', 'wait', 'off']).optional().default('background'),
381
+ /** The adapters, in order: claude, cursor, codex. */
382
+ reviewers: z.array(z.string()).optional().default(['claude']),
383
+ /** single: the first installed reviewer. cross: prefer a vendor not on the commits' trailers. full: two vendors, verdicts merged. */
384
+ mode: z.enum(['single', 'cross', 'full']).optional().default('single'),
385
+ /** The model for the claude reviewer. */
386
+ model: ModelName.optional(),
387
+ /** A model per reviewer name, e.g. { cursor: "auto" }. */
388
+ models: z.record(ModelName).optional().default({}),
423
389
  timeout_ms: z.number().optional().default(15 * 60_000),
390
+ /**
391
+ * With two vendors: match their findings, cross-examine only what one raised, and block only on what is
392
+ * confirmed (review/reviewer/panel.ts). `on` implies mode full. `required`: no user or run may turn it off,
393
+ * and a run without two vendors is unavailable instead of falling back to one.
394
+ */
395
+ panel: z.enum(['off', 'on', 'required']).optional().default('off'),
396
+ /** No user or run may review with fewer than the team's mode (a protected branch, CI). */
397
+ mode_required: z.boolean().optional().default(false),
398
+ /**
399
+ * Whether people may dismiss a reviewer finding as not a bug (`rigour dismiss <id>`, Studio). Off by default:
400
+ * a wrong finding is fixed by improving the reviewer, a right one by fixing the code. A team decision only.
401
+ */
402
+ dismissals: z.boolean().optional().default(false),
403
+ /** Findings cross-examined per review at most; the rest are shown as disputed. */
404
+ panel_max_items: z.number().int().positive().optional().default(20),
405
+ /**
406
+ * Spending caps per repository and local day, unset by default. Runs are checked before any judge starts
407
+ * (a cross-examination counts too); dollars are the ones the CLIs reported, so a cap stops new reviews once
408
+ * reached. Past a cap a review is skipped, or unavailable when the team requires the reviewer.
409
+ */
410
+ max_runs_per_day: z.number().int().positive().optional(),
411
+ max_usd_per_day: z.number().positive().optional(),
412
+ /** Judges in a full or panel review, each from a different vendor; capped by the vendors installed. */
413
+ judges: z.union([z.literal(2), z.literal(3)]).optional().default(2),
414
+ /**
415
+ * When a full or panel review adds judges. `always`: every review. `risk`: only when the change has a
416
+ * risky function (the router's score), a human review exists, or the run is the --full hard stop; any
417
+ * other change gets one judge. Measured against `always` by the backtest before you rely on it.
418
+ */
419
+ escalate: z.enum(['always', 'risk']).optional().default('always'),
420
+ /** A model per reviewer name for cross-examination (a narrow verification task), e.g. { claude: "haiku" }. */
421
+ cross_models: z.record(ModelName).optional().default({}),
424
422
  }).optional().default({}),
425
423
  }).optional().default({}),
426
- planned: z.array(z.string()).optional().default([]),
427
424
  ignore: z.array(z.string()).optional().default([]),
428
425
  });
429
426
  export const StatusSchema = z.enum(['PASS', 'FAIL', 'SKIP', 'ERROR']);
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,33 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import yaml from 'yaml';
3
+ import { ConfigSchema } from './index.js';
4
+ describe('rigour.yml', () => {
5
+ it('still loads a file that sets the settings no check ever read, and keeps everything else', () => {
6
+ const old = yaml.parse(`
7
+ version: 1
8
+ planned: [Layer boundaries]
9
+ hooks: { enabled: true, tools: [claude], fast_gates: [file-size], timeout_ms: 5000, block_on_failure: true, dlp: false, require_review_ack: true }
10
+ gates:
11
+ max_file_lines: 300
12
+ forbid_paths: [secret/]
13
+ ast: { complexity: 7, max_nesting: 3, max_function_lines: 40 }
14
+ adaptive: { enabled: true, forced_tier: enterprise }
15
+ input_validation: { enabled: true, min_secret_length: 12 }
16
+ deep: { enabled: true, provider: claude, api_key: sk-test, model_name: x, threads: 8, temperature: 0.2 }
17
+ test_quality: { check_snapshot_abuse: false, ignore_patterns: [x], max_mocks_per_test: 3 }
18
+ `);
19
+ const config = ConfigSchema.parse(old);
20
+ expect(config.gates.max_file_lines).toBe(300);
21
+ expect(config.gates.ast.complexity).toBe(7);
22
+ expect(config.gates.deep.temperature).toBe(0.2);
23
+ expect(config.gates.test_quality.max_mocks_per_test).toBe(3);
24
+ expect(config.hooks.require_review_ack).toBe(true);
25
+ // What nothing read is dropped, so no code can start depending on it by accident.
26
+ expect(config).not.toHaveProperty('version');
27
+ expect(config).not.toHaveProperty('planned');
28
+ expect(config.gates).not.toHaveProperty('adaptive');
29
+ expect(config.gates).not.toHaveProperty('input_validation');
30
+ expect(config.gates.deep).not.toHaveProperty('api_key');
31
+ expect(config.hooks).not.toHaveProperty('block_on_failure');
32
+ });
33
+ });
@@ -0,0 +1,8 @@
1
+ /**
2
+ * A `commands:` entry from rigour.yml as a program and its arguments. Run without a shell, so pipes,
3
+ * `&&` and variable assignments are not interpreted: a team that needs them points at a script.
4
+ */
5
+ export declare function splitCommand(command: string): {
6
+ bin: string;
7
+ args: string[];
8
+ };
@@ -0,0 +1,8 @@
1
+ /**
2
+ * A `commands:` entry from rigour.yml as a program and its arguments. Run without a shell, so pipes,
3
+ * `&&` and variable assignments are not interpreted: a team that needs them points at a script.
4
+ */
5
+ export function splitCommand(command) {
6
+ const parts = command.match(/(?:[^\s"']+|"[^"]*"|'[^']*')+/g) ?? [command];
7
+ return { bin: parts[0], args: parts.slice(1).map(arg => arg.replace(/^["']|["']$/g, '')) };
8
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,8 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { splitCommand } from './command-line.js';
3
+ describe('splitCommand', () => {
4
+ it('splits on spaces and keeps a quoted argument whole, without its quotes', () => {
5
+ expect(splitCommand('npm run lint')).toEqual({ bin: 'npm', args: ['run', 'lint'] });
6
+ expect(splitCommand(`vitest run "src/a b.test.ts" --reporter 'dot'`)).toEqual({ bin: 'vitest', args: ['run', 'src/a b.test.ts', '--reporter', 'dot'] });
7
+ });
8
+ });
@@ -16,7 +16,7 @@ import { spawnSync } from 'child_process';
16
16
  import fs from 'fs';
17
17
  import os from 'os';
18
18
  import path from 'path';
19
- const TEAM_ENV = ['RIGOUR_ORGANIZATION_ID', 'RIGOUR_TEAM_ID', 'RIGOUR_ACTOR_ID', 'RIGOUR_TEAM_REPOSITORIES', 'RIGOUR_TEAM_DATABASE_URL', 'RIGOUR_TEAM_DATABASE_URL_COMMAND', 'RIGOUR_TEAM_SEMANTIC'];
19
+ const TEAM_ENV = ['RIGOUR_ORGANIZATION_ID', 'RIGOUR_TEAM_ID', 'RIGOUR_ACTOR_ID', 'RIGOUR_TEAM_REPOSITORIES', 'RIGOUR_TEAM_DATABASE_URL', 'RIGOUR_TEAM_DATABASE_URL_COMMAND', 'RIGOUR_TEAM_SEMANTIC', 'RIGOUR_TEAM_SYNC_PERSONAL'];
20
20
  /** Where profiles are listed: always the person's real home, never a profile's. */
21
21
  export function profilesPath() {
22
22
  return process.env.RIGOUR_PROFILES || path.join(os.homedir(), '.rigour', 'profiles.json');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rigour-labs/core",
3
- "version": "6.6.5",
3
+ "version": "6.7.0",
4
4
  "description": "Rigour's review engine: deterministic gates on changed lines, rules and lessons learned from your team's fixes, and per-check precision from what you fix versus dismiss, across TypeScript, JavaScript, Python, Go, Ruby and C#.",
5
5
  "engines": {
6
6
  "node": ">=22"
@@ -70,15 +70,14 @@
70
70
  },
71
71
  "optionalDependencies": {
72
72
  "@anthropic-ai/sdk": "^0.30.1",
73
- "@xenova/transformers": "^2.17.2",
74
73
  "pg": "^8.16.3",
75
74
  "sqlite3": "^5.1.7",
76
75
  "openai": "^4.104.0",
77
- "@rigour-labs/brain-darwin-arm64": "6.6.5",
78
- "@rigour-labs/brain-darwin-x64": "6.6.5",
79
- "@rigour-labs/brain-linux-arm64": "6.6.5",
80
- "@rigour-labs/brain-linux-x64": "6.6.5",
81
- "@rigour-labs/brain-win-x64": "6.6.5"
76
+ "@rigour-labs/brain-darwin-x64": "6.7.0",
77
+ "@rigour-labs/brain-linux-arm64": "6.7.0",
78
+ "@rigour-labs/brain-darwin-arm64": "6.7.0",
79
+ "@rigour-labs/brain-win-x64": "6.7.0",
80
+ "@rigour-labs/brain-linux-x64": "6.7.0"
82
81
  },
83
82
  "devDependencies": {
84
83
  "@types/fs-extra": "^11.0.4",