@rigour-labs/core 4.2.3 → 4.3.1

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.
@@ -11,6 +11,7 @@
11
11
  import { Gate } from './base.js';
12
12
  import { createProvider } from '../inference/index.js';
13
13
  import { extractFacts, factsToPromptString, chunkFacts, buildAnalysisPrompt, buildCrossFilePrompt, verifyFindings } from '../deep/index.js';
14
+ import { checkLocalPatterns } from '../storage/local-memory.js';
14
15
  import { Logger } from '../utils/logger.js';
15
16
  /** Max files to analyze before truncating (prevents OOM on huge repos) */
16
17
  const MAX_ANALYZABLE_FILES = 500;
@@ -62,9 +63,15 @@ export class DeepAnalysisGate extends Gate {
62
63
  const agentCount = this.config.options.agents || 1;
63
64
  const isCloud = !!this.config.options.apiKey;
64
65
  onProgress?.(` Found ${allFacts.length} files to analyze${agentCount > 1 ? ` with ${agentCount} parallel agents` : ''}.`);
66
+ // Step 1.5: Check local project memory for known patterns (instant, no LLM)
67
+ const fileList = allFacts.map(f => f.path).filter(Boolean);
68
+ const localFindings = checkLocalPatterns(context.cwd, fileList);
69
+ if (localFindings.length > 0) {
70
+ onProgress?.(` 🧠 Local memory: ${localFindings.length} known pattern(s) matched instantly.`);
71
+ }
65
72
  // Step 2: LLM interprets facts (in chunks)
66
73
  const chunks = chunkFacts(allFacts);
67
- const allFindings = [];
74
+ const allFindings = [...localFindings];
68
75
  let failedChunks = 0;
69
76
  if (agentCount > 1 && isCloud) {
70
77
  // ── Multi-agent mode: partition chunks across N agents, analyze in parallel ──
@@ -1,5 +1,6 @@
1
1
  import { SEVERITY_WEIGHTS } from '../types/index.js';
2
2
  import { DeepAnalysisGate } from './deep-analysis.js';
3
+ import { persistAndReinforce } from '../storage/local-memory.js';
3
4
  import { FileGate } from './file.js';
4
5
  import { ContentGate } from './content.js';
5
6
  import { StructureGate } from './structure.js';
@@ -22,6 +23,7 @@ import { PromiseSafetyGate } from './promise-safety.js';
22
23
  import { PhantomApisGate } from './phantom-apis.js';
23
24
  import { DeprecatedApisGate } from './deprecated-apis.js';
24
25
  import { TestQualityGate } from './test-quality.js';
26
+ import { SideEffectAnalysisGate } from './side-effect-analysis.js';
25
27
  import { execa } from 'execa';
26
28
  import { Logger } from '../utils/logger.js';
27
29
  export class GateRunner {
@@ -92,6 +94,10 @@ export class GateRunner {
92
94
  if (this.config.gates.test_quality?.enabled !== false) {
93
95
  this.gates.push(new TestQualityGate(this.config.gates.test_quality));
94
96
  }
97
+ // v4.3+ Side-Effect Safety Analysis (enabled by default)
98
+ if (this.config.gates.side_effect_analysis?.enabled !== false) {
99
+ this.gates.push(new SideEffectAnalysisGate(this.config.gates.side_effect_analysis));
100
+ }
95
101
  // Environment Alignment Gate (Should be prioritized)
96
102
  if (this.config.gates.environment?.enabled) {
97
103
  this.gates.unshift(new EnvironmentGate(this.config.gates));
@@ -254,7 +260,8 @@ export class GateRunner {
254
260
  break;
255
261
  }
256
262
  }
257
- return {
263
+ // Persist findings + reinforce patterns in local SQLite (fire-and-forget)
264
+ const report = {
258
265
  status,
259
266
  summary,
260
267
  failures,
@@ -269,5 +276,16 @@ export class GateRunner {
269
276
  ...(deepStats ? { deep: deepStats } : {}),
270
277
  },
271
278
  };
279
+ // Store findings + reinforce patterns in local SQLite (non-blocking)
280
+ try {
281
+ persistAndReinforce(cwd, report, deepStats ? {
282
+ deepTier: deepStats.tier,
283
+ deepModel: deepStats.model,
284
+ } : undefined);
285
+ }
286
+ catch {
287
+ // Silent — local memory is advisory, never blocks scans
288
+ }
289
+ return report;
272
290
  }
273
291
  }
@@ -0,0 +1,67 @@
1
+ /**
2
+ * Side-Effect Analysis Gate
3
+ *
4
+ * Context-aware detection of code patterns with real-world consequences:
5
+ * unbounded process spawns, runaway timers, missing circuit breakers,
6
+ * circular file watchers, resource leaks, and auto-restart bombs.
7
+ *
8
+ * SMART DETECTION APPROACH (not hardcoded regex):
9
+ * ───────────────────────────────────────────────
10
+ * 1. VARIABLE BINDING TRACKING: Tracks `const id = setInterval(...)` and
11
+ * verifies `clearInterval(id)` exists — not just "does clearInterval
12
+ * appear somewhere in the file?"
13
+ *
14
+ * 2. SCOPE-AWARE ANALYSIS: Checks cleanup is in the same function scope
15
+ * as creation. A clearInterval in a completely different function doesn't
16
+ * help the one that created the timer.
17
+ *
18
+ * 3. FRAMEWORK AWARENESS: Understands React useEffect cleanup returns,
19
+ * Go `defer f.Close()` idiom, Python `with open()` context managers,
20
+ * Java try-with-resources, C# using statements, Ruby block form.
21
+ *
22
+ * 4. PATH OVERLAP ANALYSIS: For circular triggers, extracts actual paths
23
+ * from watch() and writeFile() calls and checks if they overlap.
24
+ *
25
+ * 5. BASE CASE ORDERING: For recursion, checks that the base case
26
+ * (return/break) comes BEFORE the recursive call, not just that
27
+ * both exist somewhere in the function.
28
+ *
29
+ * Follows the architectural patterns of:
30
+ * - hallucinated-imports (build context → scan → resolve → report)
31
+ * - promise-safety (scope-aware helpers, extractBraceBody, isInsideTryBlock)
32
+ *
33
+ * This is a CORE gate (enabled by default, provenance: ai-drift).
34
+ * Supports: JS/TS, Python, Go, Rust, C#, Java, Ruby
35
+ *
36
+ * @since v4.3.0
37
+ */
38
+ import { Gate, GateContext } from './base.js';
39
+ import { Failure, Provenance } from '../types/index.js';
40
+ export interface SideEffectAnalysisConfig {
41
+ enabled?: boolean;
42
+ check_unbounded_timers?: boolean;
43
+ check_unbounded_loops?: boolean;
44
+ check_process_lifecycle?: boolean;
45
+ check_recursive_depth?: boolean;
46
+ check_resource_lifecycle?: boolean;
47
+ check_retry_without_limit?: boolean;
48
+ check_circular_triggers?: boolean;
49
+ check_auto_restart?: boolean;
50
+ ignore_patterns?: string[];
51
+ }
52
+ export declare class SideEffectAnalysisGate extends Gate {
53
+ private cfg;
54
+ constructor(config?: SideEffectAnalysisConfig);
55
+ protected get provenance(): Provenance;
56
+ run(context: GateContext): Promise<Failure[]>;
57
+ private scanFile;
58
+ private checkUnboundedTimers;
59
+ private checkProcessLifecycle;
60
+ private checkUnboundedLoops;
61
+ private checkRetryWithoutLimit;
62
+ private checkCircularTriggers;
63
+ private checkResourceLifecycle;
64
+ private checkRecursiveDepth;
65
+ private checkAutoRestart;
66
+ private buildFailures;
67
+ }