@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
package/README.md CHANGED
@@ -1,104 +1,28 @@
1
1
  # @rigour-labs/core
2
2
 
3
- [![npm version](https://img.shields.io/npm/v/@rigour-labs/core?color=cyan)](https://www.npmjs.com/package/@rigour-labs/core)
4
- [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
3
+ [![npm version](https://img.shields.io/npm/v/@rigour-labs/core?color=4f46e5)](https://www.npmjs.com/package/@rigour-labs/core)
4
+ [![License: MIT](https://img.shields.io/badge/license-MIT-facc15.svg)](https://opensource.org/licenses/MIT)
5
5
 
6
- **AI Agent Governance Engine — deterministic quality gates, drift detection, and LLM-powered deep analysis.**
6
+ The engine behind [Rigour](https://github.com/rigour-labs/rigour): the checks, the review of a change, the
7
+ reviewer that runs coding agents' own CLIs as judges, model review, local learning and the team database.
7
8
 
8
- The core library powering [Rigour](https://rigour.run) — 27+ quality gates, five-signal deep analysis pipeline, temporal drift engine, and AI agent DLP across TypeScript, JavaScript, Python, Go, Ruby, and C#/.NET.
9
+ Most people want the command, [@rigour-labs/cli](https://www.npmjs.com/package/@rigour-labs/cli), or the MCP
10
+ server, [@rigour-labs/mcp](https://www.npmjs.com/package/@rigour-labs/mcp). This package is for building on
11
+ the engine.
9
12
 
10
- > This package is the engine. For the CLI, use [`@rigour-labs/cli`](https://www.npmjs.com/package/@rigour-labs/cli). For MCP integration, use [`@rigour-labs/mcp`](https://www.npmjs.com/package/@rigour-labs/mcp).
11
-
12
- ## What's Inside
13
-
14
- ### 27+ Deterministic Quality Gates
15
-
16
- **Structural:** File size, cyclomatic complexity, method count, parameter count, nesting depth, required docs, content hygiene.
17
-
18
- **Security:** Hardcoded secrets, SQL injection, XSS, command injection, path traversal, frontend secret exposure.
19
-
20
- **AI Drift Detection:**
21
- - **Three-pass duplication drift** — MD5 exact → AST Jaccard (tree-sitter) → semantic embedding (all-MiniLM-L6-v2, 384D cosine). Catches `.find()` vs `.filter()[0]` — same intent, different implementation.
22
- - **Hallucinated imports** — language-aware resolution for relative + package imports.
23
- - **Phantom APIs** — non-existent stdlib/framework methods the LLM invented.
24
- - **Style drift** — fingerprints naming, error handling, import style, quote preferences against project baseline.
25
- - **Logic drift** — tracks comparison operators (>= → >), branch counts, return statements per function across scans.
26
- - **Dependency bloat** — unused deps, heavy alternatives (moment→dayjs), duplicate purpose packages.
27
- - **Context-window artifacts**, inconsistent error handling, promise safety, deprecated APIs.
28
-
29
- **Semantic Bugs (type-aware, on by default):** a TypeScript program with the checker and cross-file value tracing proves a defect before reporting it: credentials forwarded on redirect, paged reads aggregated in memory, cached responses that can carry a failure fallback. Rules learned from fixes (`rigour learn`) run alongside them from `.rigour/rules/`.
30
-
31
- **Agent Governance:** Multi-agent scope isolation, EWMA-based checkpoint supervision, context drift, retry loop breaker, memory & skills governance with DLP scanning.
32
-
33
- ### Real-Time Hook Engine
34
-
35
- Sub-200ms per-file-write checker with 5 fast gates (governance, hallucinated imports, promise safety, security patterns, file size). Generates native hook configs for Claude Code, Cursor, Cline, and Windsurf.
36
-
37
- ### AI Agent DLP (Data Loss Prevention)
38
-
39
- 29 credential patterns with anti-evasion hardening (unicode normalization, zero-width char removal, bidi control stripping, Shannon entropy detection >4.5 bits). Compliance-mapped to SOC2-CC6.1, HIPAA-164.312, PCI-DSS-3.4/3.5/6.5, OWASP-A2, CWE-798.
40
-
41
- ### Five-Signal Deep Analysis Pipeline
42
-
43
- Rigour's deep analysis is not a wrapper around a generic LLM. The model operates within a cage of deterministic facts:
44
-
45
- 1. **Extract** — five independent signal streams (AST facts, semantic embeddings, style fingerprints, logic baselines, dependency graphs) computed deterministically before the LLM sees anything.
46
- 2. **Interpret** — the model receives structured facts (not raw source), focuses on SOLID, design patterns, language idioms, architecture. Constrained input prevents hallucination.
47
- 3. **Verify** — every LLM finding is cross-referenced against all five signal streams. Wrong line numbers, phantom patterns, non-existent functions → discarded. Only verified findings with confidence scores reach the report.
48
-
49
- Deep review is built for frontier models: your own key, or the coding agent's own model. The small local models (lite and deep tiers fine-tuned via the [DriftBench RLAIF pipeline](https://github.com/rigour-labs/driftbench); max is the stock Qwen2.5-Coder-7B) remain available offline but are no longer developed.
50
-
51
- ### Temporal Drift Engine (v5.1)
52
-
53
- Cross-session trend analysis powered by EWMA and Z-score anomaly detection. Tracks three independent provenance streams (AI drift, structural, security) with separate trend directions. Reads from the SQLite brain for month-over-month analysis.
54
-
55
- Key capabilities: per-provenance EWMA streams (alpha=0.3), Z-score anomaly detection (|Z| > 2.0), monthly/weekly rollups, semantic duplicate tracking, style + logic baseline evolution, human-readable narrative generation.
56
-
57
- ### Multi-Language Support
58
-
59
- Hallucinated import detection supports 8 languages with stdlib whitelists and dependency manifest parsing: TypeScript, JavaScript, Python, Go, Ruby, C#/.NET, Rust, Java, and Kotlin. Core structural gates support all languages via AST analysis.
60
-
61
- ### Two-Score System
62
-
63
- Every failure carries a **provenance tag** (`ai-drift`, `traditional`, `security`, `governance`) and contributes to two sub-scores:
64
-
65
- - **AI Health Score** (0–100) — AI-specific failures
66
- - **Structural Score** (0–100) — Traditional code quality
67
-
68
- ### Fix Packets (v2)
69
-
70
- Machine-readable JSON diagnostics with severity, provenance, file, line number, and step-by-step remediation instructions that AI agents can consume directly.
71
-
72
- ## Usage
73
-
74
- ```typescript
75
- import { GateRunner } from '@rigour-labs/core';
76
-
77
- const runner = new GateRunner(config);
78
- const report = await runner.run(projectRoot);
79
-
80
- console.log(report.status); // 'PASS' or 'FAIL'
81
- console.log(report.stats.score); // 0-100
82
- console.log(report.failures); // Failure[]
13
+ ```bash
14
+ npm install @rigour-labs/core
83
15
  ```
84
16
 
85
- ### With Deep Analysis
86
-
87
- ```typescript
88
- import { GateRunner } from '@rigour-labs/core';
17
+ ```ts
18
+ import { ConfigSchema, reviewChange } from '@rigour-labs/core';
89
19
 
90
- const runner = new GateRunner(config);
91
- const report = await runner.run(projectRoot, undefined, {
92
- enabled: true,
93
- pro: false, // true for full-power model
94
- provider: 'local', // or 'claude', 'openai', etc.
95
- });
20
+ const config = ConfigSchema.parse({}); // or the parsed rigour.yml
21
+ const result = await reviewChange({ cwd: process.cwd(), config, source: { mode: 'working' } });
22
+ console.log(result.status, result.findings);
96
23
  ```
97
24
 
98
- ## Documentation
99
-
100
- **[Full docs at docs.rigour.run](https://docs.rigour.run)**
101
-
102
- ## License
25
+ What each check finds and whether it blocks: [What Rigour checks](https://github.com/rigour-labs/rigour/blob/main/docs/CHECKS.md). Every setting:
26
+ [Configuration reference](https://github.com/rigour-labs/rigour/blob/main/docs/CONFIG_REFERENCE.md). Node 22 or later.
103
27
 
104
28
  MIT © [Rigour Labs](https://github.com/rigour-labs)
@@ -14,7 +14,6 @@ describe('Environment Alignment Gate', () => {
14
14
  });
15
15
  it('should detect tool version mismatch (Explicit)', async () => {
16
16
  const rawConfig = {
17
- version: 1,
18
17
  gates: {
19
18
  environment: {
20
19
  enabled: true,
@@ -36,7 +35,6 @@ describe('Environment Alignment Gate', () => {
36
35
  });
37
36
  it('should detect missing environment variables', async () => {
38
37
  const rawConfig = {
39
- version: 1,
40
38
  gates: {
41
39
  environment: {
42
40
  enabled: true,
@@ -57,7 +55,6 @@ describe('Environment Alignment Gate', () => {
57
55
  ruff = ">=99.14.0"
58
56
  `);
59
57
  const rawConfig = {
60
- version: 1,
61
58
  gates: {
62
59
  environment: {
63
60
  enabled: true,
@@ -77,7 +74,6 @@ ruff = ">=99.14.0"
77
74
  });
78
75
  it('should prioritize environment gate and run it first', async () => {
79
76
  const rawConfig = {
80
- version: 1,
81
77
  gates: {
82
78
  max_file_lines: 1,
83
79
  environment: {
@@ -26,8 +26,6 @@ export interface AgentTeamSession {
26
26
  export interface AgentTeamConfig {
27
27
  enabled?: boolean;
28
28
  max_concurrent_agents?: number;
29
- cross_agent_pattern_check?: boolean;
30
- handoff_verification?: boolean;
31
29
  task_ownership?: 'strict' | 'collaborative';
32
30
  }
33
31
  /**
@@ -114,8 +114,6 @@ export class AgentTeamGate extends Gate {
114
114
  this.config = {
115
115
  enabled: config.enabled ?? false,
116
116
  max_concurrent_agents: config.max_concurrent_agents ?? 3,
117
- cross_agent_pattern_check: config.cross_agent_pattern_check ?? true,
118
- handoff_verification: config.handoff_verification ?? true,
119
117
  task_ownership: config.task_ownership ?? 'strict',
120
118
  };
121
119
  }
@@ -148,12 +146,6 @@ export class AgentTeamGate extends Gate {
148
146
  }
149
147
  }
150
148
  }
151
- // Check 3: Cross-agent pattern detection (if enabled)
152
- if (this.config.cross_agent_pattern_check && context.record) {
153
- // This would integrate with the Pattern Index to detect conflicting patterns
154
- // For now, we log that we would do this check
155
- Logger.debug('Cross-agent pattern check: would analyze patterns across agent scopes');
156
- }
157
149
  return failures;
158
150
  }
159
151
  }
@@ -44,7 +44,6 @@ export interface CheckpointConfig {
44
44
  interval_minutes?: number;
45
45
  quality_threshold?: number;
46
46
  drift_detection?: boolean;
47
- auto_save_on_failure?: boolean;
48
47
  /** EWMA smoothing factor. Higher = more weight on recent data. Default 0.3 */
49
48
  ewma_alpha?: number;
50
49
  /** Drop from EWMA that triggers drift warning. Default 15 */
@@ -256,7 +256,6 @@ export class CheckpointGate extends Gate {
256
256
  interval_minutes: config.interval_minutes ?? 15,
257
257
  quality_threshold: config.quality_threshold ?? 80,
258
258
  drift_detection: config.drift_detection ?? true,
259
- auto_save_on_failure: config.auto_save_on_failure ?? true,
260
259
  ewma_alpha: config.ewma_alpha ?? 0.3,
261
260
  drift_drop_threshold: config.drift_drop_threshold ?? 15,
262
261
  };
@@ -8,10 +8,6 @@ export interface ExtendedContextConfig {
8
8
  enabled?: boolean;
9
9
  sensitivity?: number;
10
10
  mining_depth?: number;
11
- cross_file_patterns?: boolean;
12
- naming_consistency?: boolean;
13
- import_relationships?: boolean;
14
- max_cross_file_depth?: number;
15
11
  }
16
12
  export declare class ContextGate extends Gate {
17
13
  private config;
@@ -13,10 +13,6 @@ export class ContextGate extends Gate {
13
13
  enabled: config.context?.enabled ?? false,
14
14
  sensitivity: config.context?.sensitivity ?? 0.8,
15
15
  mining_depth: config.context?.mining_depth ?? 100,
16
- cross_file_patterns: true, // Default ON for frontier model support
17
- naming_consistency: true,
18
- import_relationships: true,
19
- max_cross_file_depth: 50,
20
16
  };
21
17
  }
22
18
  get provenance() { return 'ai-drift'; }
@@ -42,22 +38,16 @@ export class ContextGate extends Gate {
42
38
  const codeContent = this.stripComments(content);
43
39
  // 1. Original: Detect Redundant Suffixes (The Golden Example)
44
40
  this.checkEnvDrift(codeContent, file, envAnchors, failures);
45
- // 2. NEW: Cross-file pattern collection
46
- if (this.extendedConfig.cross_file_patterns) {
47
- this.collectNamingPatterns(codeContent, file, namingPatterns);
48
- this.collectImportPatterns(codeContent, file, importPatterns);
49
- }
41
+ // 2. Cross-file pattern collection
42
+ this.collectNamingPatterns(codeContent, file, namingPatterns);
43
+ this.collectImportPatterns(codeContent, file, importPatterns);
50
44
  }
51
45
  catch (e) { }
52
46
  }
53
- // 3. NEW: Analyze naming consistency across files
54
- if (this.extendedConfig.naming_consistency) {
55
- this.analyzeNamingConsistency(namingPatterns, failures);
56
- }
57
- // 4. NEW: Analyze import relationship patterns
58
- if (this.extendedConfig.import_relationships) {
59
- this.analyzeImportPatterns(importPatterns, failures);
60
- }
47
+ // 3. Naming consistency across files
48
+ this.analyzeNamingConsistency(namingPatterns, failures);
49
+ // 4. Import relationship patterns
50
+ this.analyzeImportPatterns(importPatterns, failures);
61
51
  // Cross-file patterns need the whole repo, but a scoped run reports
62
52
  // only findings that cite a file in scope.
63
53
  if (isScoped(context.patterns)) {
@@ -20,7 +20,6 @@ import { type LessonMode } from '../review-learning/team-lessons.js';
20
20
  export interface DeepGateConfig {
21
21
  options: DeepOptions;
22
22
  checks?: Record<string, boolean>;
23
- threads?: number;
24
23
  maxTokens?: number;
25
24
  temperature?: number;
26
25
  timeoutMs?: number;
@@ -16,7 +16,6 @@ export async function runDeepAnalysis(config, context, deepOptions) {
16
16
  const deepGate = new DeepAnalysisGate({
17
17
  options: deepOptions,
18
18
  checks: config.gates.deep?.checks,
19
- threads: config.gates.deep?.threads,
20
19
  maxTokens: config.gates.deep?.max_tokens,
21
20
  temperature: config.gates.deep?.temperature,
22
21
  timeoutMs: config.gates.deep?.timeout_ms,
@@ -54,5 +54,5 @@ export declare class FrontendSecretExposureGate extends Gate {
54
54
  private isDummyValue;
55
55
  private toFailures;
56
56
  }
57
- /** Imports a Node-only module: server code, whatever its path. */
57
+ /** Imports a Node-only module (a built-in, or a package that spawns processes): server code, whatever its path. */
58
58
  export declare function importsNodeBuiltins(content: string): boolean;
@@ -66,8 +66,14 @@ export class FrontendSecretExposureGate extends Gate {
66
66
  // Any tool config at the repository root runs in Node, never in a browser bundle.
67
67
  '^[^/]+\\.config\\.[cm]?[jt]s$',
68
68
  '\\.server\\.(?:ts|tsx|js|jsx|mjs|cjs)$',
69
- // SvelteKit endpoints (`+server.ts`) are server-only.
69
+ // SvelteKit endpoints (`+server.ts`) and `src/lib/server/` are server-only.
70
70
  '(^|/)\\+server\\.[cm]?[jt]s$',
71
+ '(^|/)src/lib/server/',
72
+ // Serverless functions and workers run on the platform, never in a browser bundle.
73
+ '(^|/)netlify/(?:functions|edge-functions)/',
74
+ '(^|/)supabase/functions/',
75
+ '^api/',
76
+ '(^|/)(?:functions|lambdas?|workers?|cron|jobs)/',
71
77
  ...(config.server_path_patterns ?? []),
72
78
  ],
73
79
  allowlist_env_names: config.allowlist_env_names ?? [],
@@ -254,8 +260,8 @@ export class FrontendSecretExposureGate extends Gate {
254
260
  return failures;
255
261
  }
256
262
  }
257
- const NODE_BUILTINS = /(?:from\s+|require\(\s*|import\(\s*)['"](?:node:[\w/]+|fs|fs\/promises|fs-extra|child_process|os|net|tls|worker_threads|cluster|module)['"]/;
258
- /** Imports a Node-only module: server code, whatever its path. */
263
+ const NODE_BUILTINS = /(?:from\s+|require\(\s*|import\(\s*)['"](?:node:[\w/]+|fs|fs\/promises|fs-extra|child_process|os|net|tls|worker_threads|cluster|module|execa|cross-spawn)['"]/;
264
+ /** Imports a Node-only module (a built-in, or a package that spawns processes): server code, whatever its path. */
259
265
  export function importsNodeBuiltins(content) {
260
266
  return NODE_BUILTINS.test(content);
261
267
  }
@@ -40,15 +40,18 @@ describe('FrontendSecretExposureGate', () => {
40
40
  });
41
41
  it('treats a file outside frontend paths that imports Node built-ins as server code', async () => {
42
42
  const cli = path.join(testDir, 'packages/cli/src/commands/post.ts');
43
+ const spawner = path.join(testDir, 'packages/core/src/exec.ts');
43
44
  const shared = path.join(testDir, 'packages/shared/src/config.ts');
44
45
  const component = path.join(testDir, 'src/components/Admin.tsx');
45
- for (const file of [cli, shared, component])
46
+ for (const file of [cli, spawner, shared, component])
46
47
  fs.mkdirSync(path.dirname(file), { recursive: true });
47
48
  fs.writeFileSync(cli, "import { execFileSync } from 'child_process';\nexport const token = process.env.GITHUB_TOKEN;\n");
49
+ fs.writeFileSync(spawner, "import { execa } from 'execa';\nexport const env = { GH_TOKEN: process.env.GH_TOKEN };\n");
48
50
  fs.writeFileSync(shared, 'export const token = process.env.GITHUB_TOKEN;\n');
49
51
  fs.writeFileSync(component, "import fs from 'node:fs';\nexport const key = process.env.STRIPE_SECRET_KEY;\n");
50
52
  const files = (await new FrontendSecretExposureGate().run({ cwd: testDir })).flatMap(f => f.files ?? []);
51
53
  expect(files).not.toContain('packages/cli/src/commands/post.ts'); // Node-only: never bundled
54
+ expect(files).not.toContain('packages/core/src/exec.ts'); // a package that spawns processes is Node-only too
52
55
  expect(files).toContain('packages/shared/src/config.ts'); // could be bundled: still flagged
53
56
  expect(files).toContain('src/components/Admin.tsx'); // a frontend path stays frontend
54
57
  });
@@ -94,9 +97,9 @@ describe('FrontendSecretExposureGate', () => {
94
97
  expect(await gate.run({ cwd: testDir })).toHaveLength(0);
95
98
  });
96
99
  it('ignores shell files even when a broad project scan passes them in', async () => {
97
- const shellPath = path.join(testDir, 'scripts/rigour-nmc.sh');
100
+ const shellPath = path.join(testDir, 'scripts/rigour-team.sh');
98
101
  fs.mkdirSync(path.dirname(shellPath), { recursive: true });
99
- fs.writeFileSync(shellPath, 'node -e "process.env.RIGOUR_NMC_DATABASE_URL"');
102
+ fs.writeFileSync(shellPath, 'node -e "process.env.APP_DATABASE_URL"');
100
103
  const gate = new FrontendSecretExposureGate();
101
104
  expect(await gate.run({ cwd: testDir, patterns: ['**/*'] })).toHaveLength(0);
102
105
  });
@@ -27,8 +27,6 @@ export interface HallucinatedImport {
27
27
  }
28
28
  export interface HallucinatedImportsConfig {
29
29
  enabled?: boolean;
30
- check_relative?: boolean;
31
- check_packages?: boolean;
32
30
  ignore_patterns?: string[];
33
31
  }
34
32
  export declare class HallucinatedImportsGate extends Gate {
@@ -33,8 +33,6 @@ export class HallucinatedImportsGate extends Gate {
33
33
  super('hallucinated-imports', 'Hallucinated Import Detection');
34
34
  this.config = {
35
35
  enabled: config.enabled ?? true,
36
- check_relative: config.check_relative ?? true,
37
- check_packages: config.check_packages ?? true,
38
36
  ignore_patterns: config.ignore_patterns ?? [
39
37
  '\\.css$', '\\.scss$', '\\.less$', '\\.svg$', '\\.png$', '\\.jpg$',
40
38
  '\\.json$', '\\.wasm$', '\\.graphql$', '\\.gql$',
@@ -70,6 +68,8 @@ export class HallucinatedImportsGate extends Gate {
70
68
  const allProjectFiles = projectFiles;
71
69
  const packageJson = await loadPackageJson(context.cwd);
72
70
  const rootDeps = new Set([
71
+ // A package importing itself by its own name (type tests, examples) is Node's self-reference.
72
+ ...(typeof packageJson?.name === 'string' ? [packageJson.name] : []),
73
73
  ...Object.keys(packageJson?.dependencies || {}),
74
74
  ...Object.keys(packageJson?.devDependencies || {}),
75
75
  ...Object.keys(packageJson?.peerDependencies || {}),
@@ -180,6 +180,7 @@ export async function resolveJSDepsForFile(file, cwd, rootDeps, depCacheByDir) {
180
180
  const packageJson = await fs.readJson(packageJsonPath);
181
181
  const deps = new Set([
182
182
  ...rootDeps,
183
+ ...(typeof packageJson?.name === 'string' ? [packageJson.name] : []),
183
184
  ...Object.keys(packageJson?.dependencies || {}),
184
185
  ...Object.keys(packageJson?.devDependencies || {}),
185
186
  ...Object.keys(packageJson?.peerDependencies || {}),
@@ -423,6 +423,16 @@ import { cfg } from '~shared/config';
423
423
  expect(details.includes("Path alias '@/utils/missing' does not resolve to a project file")
424
424
  || details.includes("Package '@/utils' not in package.json dependencies")).toBe(true);
425
425
  });
426
+ it('should NOT flag a package importing itself by its own name (type tests, examples), and still flag an undeclared one', async () => {
427
+ FileScanner.findFiles.mockResolvedValue(['test-d/types.ts']);
428
+ mockReadFile.mockResolvedValue(`import ky from 'ky';\nimport left from 'left-pad';\n`);
429
+ mockPathExists.mockImplementation(async (p) => normalizePath(p) === `${testCwdNormalized}/package.json`);
430
+ mockReadJson.mockResolvedValue({ name: 'ky', dependencies: {}, devDependencies: {} });
431
+ const failures = await gate.run(context);
432
+ const details = failures.map(f => f.details).join('\n');
433
+ expect(details).not.toContain("Package 'ky'");
434
+ expect(details).toContain("Package 'left-pad' not in package.json dependencies");
435
+ });
426
436
  it('should resolve configured aliases to hidden generated files', async () => {
427
437
  const jsContent = `import { docs } from '@/.source';`;
428
438
  const tsconfigContent = `{
@@ -1,6 +1,7 @@
1
1
  import { SEVERITY_WEIGHTS } from '../types/index.js';
2
2
  import { runDeepAnalysis } from './deep-runner.js';
3
3
  import { persistAndReinforce } from '../storage/local-memory.js';
4
+ import { splitCommand } from '../utils/command-line.js';
4
5
  import { recordGateRun } from '../services/adaptive-thresholds.js';
5
6
  import { FileGate } from './file.js';
6
7
  import { ContentGate } from './content.js';
@@ -71,7 +72,7 @@ export class GateRunner {
71
72
  if (this.config.gates.checkpoint?.enabled) {
72
73
  this.gates.push(new CheckpointGate(this.config.gates.checkpoint));
73
74
  }
74
- // Semantic Bugs Gate (type-aware, proven findings) — opt-in until field-validated
75
+ // Semantic Bugs Gate (type-aware, proven findings); on by default, `enabled: false` turns it off
75
76
  if (this.config.gates.semantic_bugs?.enabled) {
76
77
  this.gates.push(new SemanticBugsGate(this.config.gates.semantic_bugs));
77
78
  }
@@ -192,9 +193,7 @@ export class GateRunner {
192
193
  }
193
194
  try {
194
195
  Logger.info(`Running command gate: ${key} (${cmd})`);
195
- const parts = cmd.match(/(?:[^\s"']+|"[^"]*"|'[^']*')+/g) || [cmd];
196
- const bin = parts[0];
197
- const args = parts.slice(1).map(a => a.replace(/^["']|["']$/g, ''));
196
+ const { bin, args } = splitCommand(cmd);
198
197
  await execa(bin, args, { cwd });
199
198
  summary[key] = 'PASS';
200
199
  }
@@ -35,10 +35,6 @@ export declare const JS_TAUTOLOGICAL_PATTERNS: RegExp[];
35
35
  * Variable tautology pattern for JavaScript
36
36
  */
37
37
  export declare const JS_VAR_TAUTOLOGY_PATTERN: RegExp;
38
- /**
39
- * Snapshot test patterns
40
- */
41
- export declare const SNAPSHOT_PATTERNS: RegExp[];
42
38
  /**
43
39
  * Python tautological patterns
44
40
  */
@@ -64,13 +64,6 @@ export const JS_TAUTOLOGICAL_PATTERNS = [
64
64
  * Variable tautology pattern for JavaScript
65
65
  */
66
66
  export const JS_VAR_TAUTOLOGY_PATTERN = /expect\s*\(\s*(\w+)\s*\)\s*\.(?:toBe|toEqual|toStrictEqual)\s*\(\s*(\w+)\s*\)/;
67
- /**
68
- * Snapshot test patterns
69
- */
70
- export const SNAPSHOT_PATTERNS = [
71
- /\.toMatchSnapshot\s*\(/,
72
- /\.toMatchInlineSnapshot\s*\(/,
73
- ];
74
67
  /**
75
68
  * Python tautological patterns
76
69
  */
@@ -33,10 +33,8 @@ export interface TestQualityConfig {
33
33
  check_empty_tests?: boolean;
34
34
  check_tautological?: boolean;
35
35
  check_mock_heavy?: boolean;
36
- check_snapshot_abuse?: boolean;
37
36
  check_assertion_free_async?: boolean;
38
37
  max_mocks_per_test?: number;
39
- ignore_patterns?: string[];
40
38
  }
41
39
  export declare class TestQualityGate extends Gate {
42
40
  private config;
@@ -25,7 +25,7 @@ import { FileScanner } from '../utils/scanner.js';
25
25
  import { Logger } from '../utils/logger.js';
26
26
  import { languageAdapters } from './language-adapters/index.js';
27
27
  import { checkGoTestQuality, checkJavaKotlinTestQuality } from './test-quality-lang.js';
28
- import { JS_TEST_START_PATTERN, JS_ASSERTION_PATTERNS, JS_MOCK_PATTERNS, JS_TAUTOLOGICAL_PATTERNS, JS_VAR_TAUTOLOGY_PATTERN, SNAPSHOT_PATTERNS, PYTHON_TEST_FUNC_PATTERN, PYTHON_ASSERTION_PATTERNS, PYTHON_MOCK_PATTERNS, PYTHON_TAUTOLOGICAL_PATTERNS, PYTHON_FIXTURE_PATTERN, PYTHON_CONFTEST_NAME, } from './test-quality-matchers.js';
28
+ import { JS_TEST_START_PATTERN, JS_ASSERTION_PATTERNS, JS_MOCK_PATTERNS, JS_TAUTOLOGICAL_PATTERNS, JS_VAR_TAUTOLOGY_PATTERN, PYTHON_TEST_FUNC_PATTERN, PYTHON_ASSERTION_PATTERNS, PYTHON_MOCK_PATTERNS, PYTHON_TAUTOLOGICAL_PATTERNS, PYTHON_FIXTURE_PATTERN, PYTHON_CONFTEST_NAME, } from './test-quality-matchers.js';
29
29
  import fs from 'fs-extra';
30
30
  import path from 'path';
31
31
  export class TestQualityGate extends Gate {
@@ -37,10 +37,8 @@ export class TestQualityGate extends Gate {
37
37
  check_empty_tests: config.check_empty_tests ?? true,
38
38
  check_tautological: config.check_tautological ?? true,
39
39
  check_mock_heavy: config.check_mock_heavy ?? true,
40
- check_snapshot_abuse: config.check_snapshot_abuse ?? true,
41
40
  check_assertion_free_async: config.check_assertion_free_async ?? true,
42
41
  max_mocks_per_test: config.max_mocks_per_test ?? 5,
43
- ignore_patterns: config.ignore_patterns ?? [],
44
42
  };
45
43
  }
46
44
  get provenance() { return 'ai-drift'; }
@@ -200,13 +198,6 @@ export class TestQualityGate extends Gate {
200
198
  });
201
199
  }
202
200
  }
203
- // Check for snapshot-only tests
204
- if (this.config.check_snapshot_abuse) {
205
- if (SNAPSHOT_PATTERNS.some(p => p.test(line))) {
206
- // This is fine IF there are also semantic assertions
207
- // We'll check when the block ends
208
- }
209
- }
210
201
  // End of test block
211
202
  if (braceDepth === 0) {
212
203
  this.analyzeJSTestBlock(testBlockContent, file, testStartLine, mockCount, hasAssertion, hasAwait, isAsync, issues);
@@ -40,7 +40,7 @@ async function loadConfig(cwd) {
40
40
  const raw = yaml.parse(await fs.readFile(configPath, 'utf-8'));
41
41
  return ConfigSchema.parse(raw);
42
42
  }
43
- return ConfigSchema.parse({ version: 1 });
43
+ return ConfigSchema.parse({});
44
44
  }
45
45
  /**
46
46
  * Resolve a file path to absolute, read its content, and return metadata.
@@ -6,14 +6,13 @@ export { runHookChecker } from './checker.js';
6
6
  export type { CheckerOptions } from './checker.js';
7
7
  export { generateHookFiles, pushGateShell } from './templates.js';
8
8
  export type { GeneratedHookFile } from './templates.js';
9
- export type { HookTool, HookConfig, HookCheckerResult } from './types.js';
10
- export { DEFAULT_HOOK_CONFIG, FAST_GATE_IDS } from './types.js';
9
+ export type { HookTool, HookCheckerResult } from './types.js';
11
10
  export { scanInputForCredentials, formatDLPAlert, createDLPAuditEntry } from './input-validator.js';
12
11
  export type { CredentialDetection, InputValidationResult, InputValidationConfig } from './input-validator.js';
13
12
  export { recordDLPFeedback, allowLastDLPBlock, writeDLPBlockManifest, getLearnedAllowCount, loadDLPFeedbackStore, loadDLPFeedbackStoreSync, fingerprintDetection, } from './dlp-feedback.js';
14
13
  export type { DLPFeedbackEntry, DLPFeedbackStore, DLPBlockManifest } from './dlp-feedback.js';
15
14
  export { generateDLPHookFiles } from './dlp-templates.js';
16
15
  export type { GeneratedDLPHookFile } from './dlp-templates.js';
17
- export { stopReview, stopMessage, blocksStop, STOP_MAX_ATTEMPTS } from './stop-review.js';
16
+ export { stopReview, stopMessage, STOP_MAX_ATTEMPTS } from './stop-review.js';
18
17
  export type { StopDecision } from './stop-review.js';
19
18
  export { recordSessionBaseline, sessionBaseline, nextStopAttempt, clearStopAttempts, workFingerprint, alreadyReviewed, recordReviewed } from './session-state.js';
@@ -4,11 +4,10 @@
4
4
  */
5
5
  export { runHookChecker } from './checker.js';
6
6
  export { generateHookFiles, pushGateShell } from './templates.js';
7
- export { DEFAULT_HOOK_CONFIG, FAST_GATE_IDS } from './types.js';
8
7
  // DLP (Data Loss Prevention) — v4.2.0
9
8
  export { scanInputForCredentials, formatDLPAlert, createDLPAuditEntry } from './input-validator.js';
10
9
  export { recordDLPFeedback, allowLastDLPBlock, writeDLPBlockManifest, getLearnedAllowCount, loadDLPFeedbackStore, loadDLPFeedbackStoreSync, fingerprintDetection, } from './dlp-feedback.js';
11
10
  export { generateDLPHookFiles } from './dlp-templates.js';
12
11
  // Stop review ("before you say done")
13
- export { stopReview, stopMessage, blocksStop, STOP_MAX_ATTEMPTS } from './stop-review.js';
12
+ export { stopReview, stopMessage, STOP_MAX_ATTEMPTS } from './stop-review.js';
14
13
  export { recordSessionBaseline, sessionBaseline, nextStopAttempt, clearStopAttempts, workFingerprint, alreadyReviewed, recordReviewed } from './session-state.js';
@@ -6,13 +6,12 @@
6
6
  * letting it finish. A review with nothing to look at says so; it is never
7
7
  * reported as a clean pass.
8
8
  *
9
- * Only findings that deserve it block: critical ones, high ones that are proven
10
- * (the semantic engine traced them) or security findings, findings the change added that are
11
- * certain from the code alone (MUST_FIX: dead code, offset paging, an unbounded window, a
12
- * duplicate function), and on a branch a merge conflict with main or a mention of a file the
13
- * branch deleted (review/branch-checks.ts).
14
- * A high heuristic (a regex that sees `fetch` without `.catch`) is not enough to
15
- * hold an agent back; callers also cap the number of attempts.
9
+ * What blocks is what the review itself reports (review/quiet.ts `mustFix`: proven, critical, or
10
+ * high and verified or from a security gate), plus on a branch a merge conflict with main or a
11
+ * mention of a file the branch deleted (review/branch-checks.ts). The same rule decides the push
12
+ * gate, so a stop, a review and a push never disagree about a finding. A high heuristic (a regex
13
+ * that sees `fetch` without `.catch`) is not enough to hold an agent back; callers also cap the
14
+ * number of attempts.
16
15
  */
17
16
  import type { Config, Failure } from '../types/index.js';
18
17
  import { type ReviewTaskItem } from '../review/review-task.js';
@@ -31,6 +30,4 @@ export interface StopDecision {
31
30
  /** `sessionBaseline`: the commit the session started from, used on main (session-state.ts). */
32
31
  export declare function stopReview(cwd: string, config: Config, attempt: number, sessionBaseline?: string): Promise<StopDecision>;
33
32
  export declare function reviewAckMessage(items: ReviewTaskItem[], attempt: number): string;
34
- /** Critical; high and either proven by the semantic engine or a security finding; or a certain, local finding the change added (MUST_FIX). */
35
- export declare function blocksStop(finding: Failure): boolean;
36
33
  export declare function stopMessage(findings: Failure[], attempt: number): string;
@@ -22,7 +22,11 @@ export async function stopReview(cwd, config, attempt, sessionBaseline) {
22
22
  const result = await reviewChange({ cwd, config, diff, source });
23
23
  const branch = branchBase(cwd);
24
24
  const whole = branch && !branch.onMain ? branchFailures(cwd, branch.base, branch.mainRef, config) : [];
25
- const blocking = [...result.findings.filter(blocksStop), ...whole];
25
+ // A check that could not run is never a pass; the attempt cap keeps a broken environment from looping forever.
26
+ const crashed = result.status === 'ERROR'
27
+ ? result.gateErrors.map(id => ({ id, title: 'A check could not run', details: id === 'typed-checks-unavailable' && result.typedError ? result.typedError : `${id} crashed instead of running`, severity: 'high', files: [], hint: 'Fix the environment (dependencies, generated config), then try again.' }))
28
+ : [];
29
+ const blocking = [...result.findings, ...whole, ...crashed];
26
30
  const unreviewed = config.hooks?.require_review_ack ? buildReviewTask(cwd, diff, config.gates.deep?.router).items : [];
27
31
  const reviewed = { findings: result.findings, reviewedFiles: Object.keys(result.changedLines), against };
28
32
  if (blocking.length === 0 && unreviewed.length === 0)
@@ -41,15 +45,6 @@ export function reviewAckMessage(items, attempt) {
41
45
  ...listed,
42
46
  ].join('\n');
43
47
  }
44
- /** Certain from the code alone, and quick to fix (each measured with no false alarms before it blocks). */
45
- const MUST_FIX = new Set(['unused-export', 'orphan-file', 'offset-paging', 'unbounded-window', 'duplicate-function', 'partial-fix', 'partial-wiring']);
46
- /** Critical; high and either proven by the semantic engine or a security finding; or a certain, local finding the change added (MUST_FIX). */
47
- export function blocksStop(finding) {
48
- const severity = (finding.severity || 'medium');
49
- if (severity === 'critical' || MUST_FIX.has(finding.id))
50
- return true;
51
- return severity === 'high' && (finding.verified === true || finding.provenance === 'security');
52
- }
53
48
  export function stopMessage(findings, attempt) {
54
49
  const listed = findings.slice(0, MAX_LISTED).map(f => {
55
50
  const where = `${f.files?.[0] ?? '?'}:${f.line ?? '?'}`;
@@ -6,20 +6,6 @@
6
6
  *
7
7
  */
8
8
  export type HookTool = 'claude' | 'cursor' | 'cline' | 'windsurf';
9
- export interface HookConfig {
10
- /** Which tools to generate hooks for */
11
- tools: HookTool[];
12
- /** Gates to run in the hook checker (fast subset) */
13
- fast_gates: string[];
14
- /** Max execution time in ms before the checker aborts */
15
- timeout_ms: number;
16
- /** Whether to block the tool on failure (exit 2) or just warn */
17
- block_on_failure: boolean;
18
- }
19
- /** The fast gates that can run per-file in <200ms */
20
- export declare const FAST_GATE_IDS: readonly ["file-guard", "governance", "hallucinated-imports", "promise-safety", "security-patterns", "file-size"];
21
- export declare const DEFAULT_HOOK_CONFIG: HookConfig;
22
- export type FastGateId = typeof FAST_GATE_IDS[number];
23
9
  export interface HookCheckerResult {
24
10
  status: 'pass' | 'fail' | 'error';
25
11
  failures: Array<{