@rigour-labs/core 6.5.2 → 6.5.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/context/automatic-index.d.ts +5 -0
- package/dist/context/automatic-index.js +24 -3
- package/dist/context/index-status.test.d.ts +1 -0
- package/dist/context/index-status.test.js +26 -0
- package/dist/gates/dedupe-failures.test.d.ts +1 -0
- package/dist/gates/dedupe-failures.test.js +12 -0
- package/dist/gates/deprecated-dependencies.d.ts +27 -0
- package/dist/gates/deprecated-dependencies.js +104 -0
- package/dist/gates/deprecated-dependencies.test.d.ts +1 -0
- package/dist/gates/deprecated-dependencies.test.js +55 -0
- package/dist/gates/runner.d.ts +7 -1
- package/dist/gates/runner.js +25 -11
- package/dist/gates/security-patterns-data.js +2 -1
- package/dist/gates/security-patterns-owasp.test.js +15 -0
- package/dist/gates/semantic-bugs.js +1 -1
- package/dist/gates/unindexed-reads/coverage.d.ts +15 -0
- package/dist/gates/unindexed-reads/coverage.js +24 -0
- package/dist/gates/unindexed-reads/index.d.ts +20 -0
- package/dist/gates/unindexed-reads/index.js +98 -0
- package/dist/gates/unindexed-reads/queries.d.ts +17 -0
- package/dist/gates/unindexed-reads/queries.js +147 -0
- package/dist/gates/unindexed-reads/queries.test.d.ts +1 -0
- package/dist/gates/unindexed-reads/queries.test.js +54 -0
- package/dist/gates/unindexed-reads/schema.d.ts +38 -0
- package/dist/gates/unindexed-reads/schema.js +256 -0
- package/dist/gates/unindexed-reads/schema.test.d.ts +1 -0
- package/dist/gates/unindexed-reads/schema.test.js +81 -0
- package/dist/gates/unindexed-reads/sql.d.ts +21 -0
- package/dist/gates/unindexed-reads/sql.js +131 -0
- package/dist/gates/unindexed-reads/unindexed-reads.test.d.ts +1 -0
- package/dist/gates/unindexed-reads/unindexed-reads.test.js +90 -0
- package/dist/index.d.ts +4 -1
- package/dist/index.js +4 -1
- package/dist/inference/brain-placeholder.test.d.ts +1 -0
- package/dist/inference/brain-placeholder.test.js +28 -0
- package/dist/inference/llama-engine-checksum.test.d.ts +1 -0
- package/dist/inference/llama-engine-checksum.test.js +27 -0
- package/dist/inference/llama-engine.d.ts +4 -1
- package/dist/inference/llama-engine.js +15 -8
- package/dist/memory/recall.d.ts +37 -0
- package/dist/memory/recall.js +93 -0
- package/dist/memory/recall.test.d.ts +1 -0
- package/dist/memory/recall.test.js +36 -0
- package/dist/pattern-index/assess.d.ts +16 -0
- package/dist/pattern-index/assess.js +37 -0
- package/dist/pattern-index/embeddings.d.ts +19 -0
- package/dist/pattern-index/embeddings.js +16 -0
- package/dist/pattern-index/index.d.ts +3 -2
- package/dist/pattern-index/index.js +4 -2
- package/dist/pattern-index/indexer.js +15 -7
- package/dist/pattern-index/matcher.d.ts +6 -0
- package/dist/pattern-index/matcher.js +13 -3
- package/dist/pattern-index/pattern-reuse.test.d.ts +1 -0
- package/dist/pattern-index/pattern-reuse.test.js +76 -0
- package/dist/pattern-index/staleness.d.ts +2 -15
- package/dist/pattern-index/staleness.js +1 -78
- package/dist/pattern-index/types.d.ts +2 -0
- package/dist/review/agent-fixes.d.ts +4 -0
- package/dist/review/agent-fixes.js +10 -7
- package/dist/review/agent-fixes.test.js +4 -4
- package/dist/review/changed-lines.d.ts +2 -1
- package/dist/review/changed-lines.js +12 -2
- package/dist/review/check-outcomes.d.ts +31 -0
- package/dist/review/check-outcomes.js +81 -0
- package/dist/review/check-outcomes.test.d.ts +1 -0
- package/dist/review/check-outcomes.test.js +56 -0
- package/dist/review/migration-order.d.ts +5 -0
- package/dist/review/migration-order.js +70 -0
- package/dist/review/migration-order.test.d.ts +1 -0
- package/dist/review/migration-order.test.js +62 -0
- package/dist/review/quiet.d.ts +2 -0
- package/dist/review/quiet.js +12 -3
- package/dist/review/review.d.ts +2 -0
- package/dist/review/review.js +7 -2
- package/dist/review/review.test.js +11 -0
- package/dist/semantic/ast.d.ts +1 -0
- package/dist/semantic/ast.js +4 -0
- package/dist/semantic/learn/compile.js +3 -1
- package/dist/semantic/learn/learn.js +40 -5
- package/dist/semantic/types.d.ts +2 -0
- package/dist/services/context-scope-summary.test.d.ts +1 -0
- package/dist/services/context-scope-summary.test.js +17 -0
- package/dist/services/context-telemetry-service.js +2 -1
- package/dist/storage/context-telemetry.js +33 -19
- package/dist/storage/db.js +52 -1
- package/dist/storage/findings.js +1 -1
- package/dist/storage/fix-lessons.d.ts +13 -0
- package/dist/storage/fix-lessons.js +84 -0
- package/dist/storage/fix-lessons.test.d.ts +1 -0
- package/dist/storage/fix-lessons.test.js +61 -0
- package/dist/storage/index.d.ts +2 -1
- package/dist/storage/index.js +2 -1
- package/dist/storage/lessons.d.ts +16 -2
- package/dist/storage/lessons.js +27 -61
- package/dist/storage/lessons.test.js +1 -15
- package/dist/storage/local-memory.js +39 -5
- package/dist/storage/local-memory.test.d.ts +1 -0
- package/dist/storage/local-memory.test.js +55 -0
- package/dist/storage/patterns.d.ts +1 -1
- package/dist/storage/patterns.js +1 -1
- package/dist/storage/share-memory.test.d.ts +1 -0
- package/dist/storage/share-memory.test.js +34 -0
- package/dist/storage/team-vector-store.js +2 -1
- package/dist/storage/telemetry-scope.test.d.ts +1 -0
- package/dist/storage/telemetry-scope.test.js +38 -0
- package/dist/templates/universal-config.js +11 -0
- package/dist/types/index.d.ts +132 -0
- package/dist/types/index.js +15 -0
- package/package.json +7 -7
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Before deep scan: checks local SQLite for known patterns in this project.
|
|
5
5
|
* After any scan: stores verified findings and reinforces patterns.
|
|
6
|
-
*
|
|
6
|
+
* Counts are per repository and stay on the machine; replayed findings are limited to files
|
|
7
|
+
* unchanged since the scan that found them.
|
|
7
8
|
*/
|
|
8
9
|
import path from 'path';
|
|
9
10
|
import { openDatabase } from './db.js';
|
|
@@ -11,6 +12,8 @@ import { insertScan, getRecentScans } from './scans.js';
|
|
|
11
12
|
import { insertFindings, getDeepFindings } from './findings.js';
|
|
12
13
|
import { reinforcePattern, getStrongPatterns, getHardRules, decayPatterns } from './patterns.js';
|
|
13
14
|
import { Logger } from '../utils/logger.js';
|
|
15
|
+
import { getRepositoryId } from './lessons.js';
|
|
16
|
+
import fs from 'fs-extra';
|
|
14
17
|
/** Minimum pattern strength to produce instant findings (skip LLM). */
|
|
15
18
|
const INSTANT_MATCH_THRESHOLD = 0.7;
|
|
16
19
|
/** Max local findings to inject per scan (avoid flooding). */
|
|
@@ -20,6 +23,14 @@ const MIN_REUSE_CONFIDENCE = 0.7;
|
|
|
20
23
|
/**
|
|
21
24
|
* Build a map of relative file path → known issues from past findings.
|
|
22
25
|
*/
|
|
26
|
+
async function fileModifiedAt(cwd, relPath) {
|
|
27
|
+
try {
|
|
28
|
+
return (await fs.stat(path.join(cwd, relPath))).mtimeMs;
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
return undefined;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
23
34
|
function buildFileIssueMap(recentFindings) {
|
|
24
35
|
const map = new Map();
|
|
25
36
|
for (const f of recentFindings) {
|
|
@@ -40,7 +51,9 @@ export async function checkLocalPatterns(cwd, fileList) {
|
|
|
40
51
|
const db = await openDatabase();
|
|
41
52
|
if (!db)
|
|
42
53
|
return [];
|
|
43
|
-
|
|
54
|
+
// The repository's identity (its remote, else its path), never the folder name: two
|
|
55
|
+
// checkouts called `api` are different projects.
|
|
56
|
+
const repoName = await getRepositoryId(cwd);
|
|
44
57
|
const findings = [];
|
|
45
58
|
try {
|
|
46
59
|
const patterns = await getStrongPatterns(db, repoName, INSTANT_MATCH_THRESHOLD);
|
|
@@ -52,13 +65,23 @@ export async function checkLocalPatterns(cwd, fileList) {
|
|
|
52
65
|
return [];
|
|
53
66
|
const fileIssueMap = buildFileIssueMap(recentFindings);
|
|
54
67
|
// fileList contains relative paths — match directly against DB (also relative)
|
|
68
|
+
const seen = new Set();
|
|
55
69
|
for (const relPath of fileList) {
|
|
56
70
|
const known = fileIssueMap.get(relPath);
|
|
57
71
|
if (!known)
|
|
58
72
|
continue;
|
|
73
|
+
const modifiedAt = await fileModifiedAt(cwd, relPath);
|
|
59
74
|
for (const issue of known) {
|
|
60
75
|
if ((issue.confidence ?? 0) < MIN_REUSE_CONFIDENCE)
|
|
61
76
|
continue;
|
|
77
|
+
// A finding still holds only while its file is the one it was found in: an edit
|
|
78
|
+
// since that scan may be the fix, and replaying it would report fixed code.
|
|
79
|
+
if (modifiedAt === undefined || modifiedAt > Number(issue.scan_time))
|
|
80
|
+
continue;
|
|
81
|
+
const key = `${issue.line}:${issue.category}:${issue.description}`;
|
|
82
|
+
if (seen.has(`${relPath}:${key}`))
|
|
83
|
+
continue;
|
|
84
|
+
seen.add(`${relPath}:${key}`);
|
|
62
85
|
const matchingPattern = patterns.find((p) => p.pattern === issue.category && p.strength >= INSTANT_MATCH_THRESHOLD);
|
|
63
86
|
if (!matchingPattern)
|
|
64
87
|
continue;
|
|
@@ -95,20 +118,29 @@ export async function persistAndReinforce(cwd, report, meta) {
|
|
|
95
118
|
const db = await openDatabase();
|
|
96
119
|
if (!db)
|
|
97
120
|
return;
|
|
98
|
-
|
|
121
|
+
// The repository's identity (its remote, else its path), never the folder name: two
|
|
122
|
+
// checkouts called `api` are different projects.
|
|
123
|
+
const repoName = await getRepositoryId(cwd);
|
|
99
124
|
try {
|
|
100
125
|
const scanId = await insertScan(db, repoName, report, meta);
|
|
101
126
|
if (report.failures.length > 0) {
|
|
102
127
|
await insertFindings(db, scanId, report.failures);
|
|
103
128
|
}
|
|
129
|
+
// One reinforcement per category per scan: five findings of one kind in one scan are
|
|
130
|
+
// one observation of that kind, not five.
|
|
131
|
+
const byCategory = new Map();
|
|
104
132
|
for (const f of report.failures) {
|
|
105
133
|
const category = f.category || f.id;
|
|
134
|
+
if (!byCategory.has(category))
|
|
135
|
+
byCategory.set(category, f);
|
|
136
|
+
}
|
|
137
|
+
for (const [category, f] of byCategory) {
|
|
106
138
|
const source = f.source === 'llm' ? 'llm' : 'ast';
|
|
107
139
|
await reinforcePattern(db, repoName, category, `${f.title}: ${f.details?.substring(0, 120)}`, source);
|
|
108
140
|
}
|
|
109
141
|
await decayPatterns(db, 30);
|
|
110
142
|
Logger.info(`Local memory: stored ${report.failures.length} findings, ` +
|
|
111
|
-
`reinforced ${
|
|
143
|
+
`reinforced ${byCategory.size} patterns`);
|
|
112
144
|
}
|
|
113
145
|
catch (error) {
|
|
114
146
|
Logger.warn(`Local memory persist failed: ${error}`);
|
|
@@ -124,7 +156,9 @@ export async function getProjectStats(cwd) {
|
|
|
124
156
|
const db = await openDatabase();
|
|
125
157
|
if (!db)
|
|
126
158
|
return null;
|
|
127
|
-
|
|
159
|
+
// The repository's identity (its remote, else its path), never the folder name: two
|
|
160
|
+
// checkouts called `api` are different projects.
|
|
161
|
+
const repoName = await getRepositoryId(cwd);
|
|
128
162
|
try {
|
|
129
163
|
const scans = await getRecentScans(db, repoName, 100);
|
|
130
164
|
const patterns = await getStrongPatterns(db, repoName, 0.3);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import os from 'os';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
5
|
+
/** An in-memory stand-in for the local SQLite store: records writes, answers the two reads under test. */
|
|
6
|
+
const db = vi.hoisted(() => ({
|
|
7
|
+
writes: [],
|
|
8
|
+
patterns: [],
|
|
9
|
+
findings: [],
|
|
10
|
+
reset() { this.writes = []; this.patterns = []; this.findings = []; },
|
|
11
|
+
}));
|
|
12
|
+
vi.mock('./db.js', () => {
|
|
13
|
+
const store = {
|
|
14
|
+
run: async (sql) => { db.writes.push(sql.trim().split(/\s+/).slice(0, 3).join(' ')); return { changes: 1, lastID: 0 }; },
|
|
15
|
+
get: async () => undefined,
|
|
16
|
+
all: async (sql) => (sql.includes('FROM patterns') ? db.patterns : sql.includes('FROM findings') ? db.findings : []),
|
|
17
|
+
exec: async () => undefined,
|
|
18
|
+
transaction: async (work) => work(store),
|
|
19
|
+
close: async () => undefined,
|
|
20
|
+
};
|
|
21
|
+
return { openDatabase: async () => store };
|
|
22
|
+
});
|
|
23
|
+
const { checkLocalPatterns, persistAndReinforce } = await import('./local-memory.js');
|
|
24
|
+
let repo;
|
|
25
|
+
beforeEach(() => {
|
|
26
|
+
db.reset();
|
|
27
|
+
repo = fs.mkdtempSync(path.join(os.tmpdir(), 'local-memory-'));
|
|
28
|
+
fs.mkdirSync(path.join(repo, 'src'));
|
|
29
|
+
});
|
|
30
|
+
afterEach(() => fs.rmSync(repo, { recursive: true, force: true }));
|
|
31
|
+
const failure = (id, line) => ({ id, title: id, details: 'x', severity: 'medium', provenance: 'traditional', files: ['src/a.ts'], line });
|
|
32
|
+
describe('persistAndReinforce', () => {
|
|
33
|
+
it('reinforces each category once per scan, however many findings it has', async () => {
|
|
34
|
+
await persistAndReinforce(repo, { status: 'FAIL', failures: [failure('content-check', 1), failure('content-check', 2), failure('content-check', 3), failure('ast', 4)], stats: {} });
|
|
35
|
+
expect(db.writes.filter(w => w.startsWith('INSERT INTO patterns'))).toHaveLength(2);
|
|
36
|
+
});
|
|
37
|
+
});
|
|
38
|
+
describe('checkLocalPatterns', () => {
|
|
39
|
+
const remembered = (scanTime) => ({ file: 'src/a.ts', line: 3, category: 'security', description: 'token logged', confidence: 0.9, scan_time: scanTime });
|
|
40
|
+
it('replays a remembered finding while its file is unchanged since that scan, once', async () => {
|
|
41
|
+
fs.writeFileSync(path.join(repo, 'src/a.ts'), 'log(token)\n');
|
|
42
|
+
const after = fs.statSync(path.join(repo, 'src/a.ts')).mtimeMs + 1000;
|
|
43
|
+
db.patterns = [{ pattern: 'security', strength: 0.9 }];
|
|
44
|
+
db.findings = [remembered(after), remembered(after)];
|
|
45
|
+
const findings = await checkLocalPatterns(repo, ['src/a.ts']);
|
|
46
|
+
expect(findings.map(f => f.description)).toEqual(['[Local Memory] token logged']);
|
|
47
|
+
});
|
|
48
|
+
it('never replays a finding for a file edited since, which may be the fix', async () => {
|
|
49
|
+
fs.writeFileSync(path.join(repo, 'src/a.ts'), 'log(redact(token))\n');
|
|
50
|
+
const before = fs.statSync(path.join(repo, 'src/a.ts')).mtimeMs - 1000;
|
|
51
|
+
db.patterns = [{ pattern: 'security', strength: 0.9 }];
|
|
52
|
+
db.findings = [remembered(before)];
|
|
53
|
+
expect(await checkLocalPatterns(repo, ['src/a.ts'])).toEqual([]);
|
|
54
|
+
});
|
|
55
|
+
});
|
|
@@ -30,6 +30,6 @@ export declare function getStrongPatterns(store: RigourDB, repo: string, thresho
|
|
|
30
30
|
export declare function getPatterns(store: RigourDB, repo: string): Promise<PatternRecord[]>;
|
|
31
31
|
/**
|
|
32
32
|
* Get patterns promoted to hard rules (strength > 0.9).
|
|
33
|
-
*
|
|
33
|
+
* Categories seen this often; shown by `rigour brain`. They are counts, not checks.
|
|
34
34
|
*/
|
|
35
35
|
export declare function getHardRules(store: RigourDB, repo: string): Promise<PatternRecord[]>;
|
package/dist/storage/patterns.js
CHANGED
|
@@ -49,7 +49,7 @@ export async function getPatterns(store, repo) {
|
|
|
49
49
|
}
|
|
50
50
|
/**
|
|
51
51
|
* Get patterns promoted to hard rules (strength > 0.9).
|
|
52
|
-
*
|
|
52
|
+
* Categories seen this often; shown by `rigour brain`. They are counts, not checks.
|
|
53
53
|
*/
|
|
54
54
|
export async function getHardRules(store, repo) {
|
|
55
55
|
return store.all('SELECT * FROM patterns WHERE repo = ? AND strength >= 0.9 ORDER BY times_seen DESC', repo);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
2
|
+
const db = vi.hoisted(() => ({ rows: [], outbox: 0, reset() { this.rows = []; this.outbox = 0; } }));
|
|
3
|
+
vi.mock('./db.js', () => {
|
|
4
|
+
const store = {
|
|
5
|
+
run: async (sql, ...params) => {
|
|
6
|
+
if (sql.includes('INSERT INTO lessons'))
|
|
7
|
+
db.rows.push({ id: params[0], visibility: 'team', state: 'candidate', kind: 'memory', subject: params[4] });
|
|
8
|
+
if (sql.includes('INSERT INTO sync_outbox'))
|
|
9
|
+
db.outbox++;
|
|
10
|
+
return { changes: 1, lastID: 0 };
|
|
11
|
+
},
|
|
12
|
+
get: async (sql, ...params) => (sql.includes("kind = 'memory'") ? db.rows.find(r => r.subject === params[1]) : undefined),
|
|
13
|
+
all: async () => [],
|
|
14
|
+
exec: async () => undefined,
|
|
15
|
+
close: async () => undefined,
|
|
16
|
+
};
|
|
17
|
+
return { openDatabase: async () => store };
|
|
18
|
+
});
|
|
19
|
+
vi.mock('./local-encryption.js', () => ({ encryptLocalPayload: async (v) => JSON.stringify(v), decryptLocalPayload: async (v) => JSON.parse(v) }));
|
|
20
|
+
vi.mock('./team-store.js', () => ({ loadTeamConfiguration: async () => null }));
|
|
21
|
+
const { shareMemoryLesson } = await import('./lessons.js');
|
|
22
|
+
beforeEach(() => db.reset());
|
|
23
|
+
describe('shareMemoryLesson', () => {
|
|
24
|
+
it('stores a team candidate of kind memory, once per memory', async () => {
|
|
25
|
+
const first = await shareMemoryLesson(process.cwd(), 'retries', 'Use withRetry from lib/net.');
|
|
26
|
+
const again = await shareMemoryLesson(process.cwd(), 'retries', 'Use withRetry from lib/net.');
|
|
27
|
+
expect(first).toBe(again);
|
|
28
|
+
expect(db.rows).toEqual([{ id: first, visibility: 'team', state: 'candidate', kind: 'memory', subject: 'retries: Use withRetry from lib/net.' }]);
|
|
29
|
+
});
|
|
30
|
+
it('queues nothing for sync until team mode is configured', async () => {
|
|
31
|
+
await shareMemoryLesson(process.cwd(), 'naming', 'Hooks are named useX.');
|
|
32
|
+
expect(db.outbox).toBe(0);
|
|
33
|
+
});
|
|
34
|
+
});
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { createHash } from 'node:crypto';
|
|
2
|
+
import { SEMANTIC_FLOOR } from '../memory/recall.js';
|
|
2
3
|
export const TEAM_VECTOR_SCHEMA = `
|
|
3
4
|
CREATE EXTENSION IF NOT EXISTS vector;
|
|
4
5
|
CREATE TABLE IF NOT EXISTS rigour.lesson_embeddings (
|
|
@@ -176,7 +177,7 @@ export async function searchConfiguredTeamKnowledge(config, query, limit) {
|
|
|
176
177
|
OR (lesson.visibility = 'team' AND lesson.state = 'promoted'))
|
|
177
178
|
AND 1 - (embedding.embedding <=> $1::vector) >= $6
|
|
178
179
|
ORDER BY embedding.embedding <=> $1::vector LIMIT $5`, [vectorLiteral(embedding), config.organizationId, config.teamId, config.semantic.model,
|
|
179
|
-
Math.max(1, Math.min(25, limit)),
|
|
180
|
+
Math.max(1, Math.min(25, limit)), SEMANTIC_FLOOR, config.actorId]);
|
|
180
181
|
return {
|
|
181
182
|
status: 'ready', provider: 'pgvector', model: config.semantic.model,
|
|
182
183
|
candidates: result.rows.map((row) => ({
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import os from 'os';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
import { afterAll, describe, expect, it, vi } from 'vitest';
|
|
5
|
+
/** Every open goes to a scratch database, never ~/.rigour/rigour.db. */
|
|
6
|
+
const dbFile = vi.hoisted(() => require('path').join(require('os').tmpdir(), `rigour-telemetry-${process.pid}.db`));
|
|
7
|
+
vi.mock('./db.js', async (importOriginal) => {
|
|
8
|
+
const real = await importOriginal();
|
|
9
|
+
return { ...real, openDatabase: (p) => real.openDatabase(p ?? dbFile) };
|
|
10
|
+
});
|
|
11
|
+
const { openDatabase, isSQLiteAvailable } = await import('./db.js');
|
|
12
|
+
const { getCheckpointMetrics, getContextEvents, recordCheckpointMetric, recordContextEvent } = await import('./context-telemetry.js');
|
|
13
|
+
const repoA = fs.mkdtempSync(path.join(os.tmpdir(), 'telemetry-a-'));
|
|
14
|
+
const repoB = fs.mkdtempSync(path.join(os.tmpdir(), 'telemetry-b-'));
|
|
15
|
+
afterAll(() => {
|
|
16
|
+
for (const p of [dbFile, `${dbFile}-wal`, `${dbFile}-shm`])
|
|
17
|
+
fs.rmSync(p, { force: true });
|
|
18
|
+
for (const dir of [repoA, repoB])
|
|
19
|
+
fs.rmSync(dir, { recursive: true, force: true });
|
|
20
|
+
});
|
|
21
|
+
describe.runIf(isSQLiteAvailable())('telemetry scoped to its repository', () => {
|
|
22
|
+
it('migrates idempotently when two processes open the database at once', async () => {
|
|
23
|
+
const [a, b] = await Promise.all([openDatabase(), openDatabase()]);
|
|
24
|
+
const columns = (await a.all('PRAGMA table_info(checkpoint_metrics)')).map((c) => c.name);
|
|
25
|
+
expect(columns).toContain('repository_id');
|
|
26
|
+
await a.close();
|
|
27
|
+
await b.close();
|
|
28
|
+
});
|
|
29
|
+
it("shows a repository its own checkpoints and context events, never another's", async () => {
|
|
30
|
+
await recordCheckpointMetric({ checkpointId: 'cp-a', taskId: 't', agentId: 'agent', checkpointTokens: 10, rawStateTokens: 0, replayTokensAvoided: 0 }, repoA);
|
|
31
|
+
await recordCheckpointMetric({ checkpointId: 'cp-b', taskId: 't', agentId: 'agent', checkpointTokens: 10, rawStateTokens: 0, replayTokensAvoided: 0 }, repoB);
|
|
32
|
+
await recordContextEvent({ toolName: 'rigour_recall', cacheStatus: 'miss', candidateTokens: 0, returnedTokens: 0 }, repoA);
|
|
33
|
+
expect((await getCheckpointMetrics(undefined, repoA)).map(m => m.checkpointId)).toEqual(['cp-a']);
|
|
34
|
+
expect((await getCheckpointMetrics(undefined, repoB)).map(m => m.checkpointId)).toEqual(['cp-b']);
|
|
35
|
+
expect(await getContextEvents(undefined, repoB)).toEqual([]);
|
|
36
|
+
expect((await getContextEvents(undefined, repoA)).map(e => e.toolName)).toEqual(['rigour_recall']);
|
|
37
|
+
});
|
|
38
|
+
});
|
|
@@ -234,6 +234,17 @@ export const UNIVERSAL_CONFIG = {
|
|
|
234
234
|
track_branches: false,
|
|
235
235
|
track_returns: false,
|
|
236
236
|
},
|
|
237
|
+
unindexed_reads: {
|
|
238
|
+
enabled: false,
|
|
239
|
+
migrations: [],
|
|
240
|
+
},
|
|
241
|
+
deprecated_dependencies: {
|
|
242
|
+
enabled: false,
|
|
243
|
+
},
|
|
244
|
+
migration_order: {
|
|
245
|
+
enabled: false,
|
|
246
|
+
dirs: ['**/supabase/migrations'],
|
|
247
|
+
},
|
|
237
248
|
semantic_bugs: {
|
|
238
249
|
enabled: true,
|
|
239
250
|
},
|
package/dist/types/index.d.ts
CHANGED
|
@@ -498,6 +498,36 @@ export declare const GatesSchema: z.ZodObject<{
|
|
|
498
498
|
custom_patterns?: string[] | undefined;
|
|
499
499
|
audit_log?: boolean | undefined;
|
|
500
500
|
}>>>;
|
|
501
|
+
unindexed_reads: z.ZodDefault<z.ZodOptional<z.ZodObject<{
|
|
502
|
+
enabled: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
|
|
503
|
+
migrations: z.ZodDefault<z.ZodOptional<z.ZodArray<z.ZodString, "many">>>;
|
|
504
|
+
}, "strip", z.ZodTypeAny, {
|
|
505
|
+
enabled: boolean;
|
|
506
|
+
migrations: string[];
|
|
507
|
+
}, {
|
|
508
|
+
enabled?: boolean | undefined;
|
|
509
|
+
migrations?: string[] | undefined;
|
|
510
|
+
}>>>;
|
|
511
|
+
deprecated_dependencies: z.ZodDefault<z.ZodOptional<z.ZodObject<{
|
|
512
|
+
enabled: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
|
|
513
|
+
registry: z.ZodOptional<z.ZodString>;
|
|
514
|
+
}, "strip", z.ZodTypeAny, {
|
|
515
|
+
enabled: boolean;
|
|
516
|
+
registry?: string | undefined;
|
|
517
|
+
}, {
|
|
518
|
+
enabled?: boolean | undefined;
|
|
519
|
+
registry?: string | undefined;
|
|
520
|
+
}>>>;
|
|
521
|
+
migration_order: z.ZodDefault<z.ZodOptional<z.ZodObject<{
|
|
522
|
+
enabled: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
|
|
523
|
+
dirs: z.ZodDefault<z.ZodOptional<z.ZodArray<z.ZodString, "many">>>;
|
|
524
|
+
}, "strip", z.ZodTypeAny, {
|
|
525
|
+
enabled: boolean;
|
|
526
|
+
dirs: string[];
|
|
527
|
+
}, {
|
|
528
|
+
enabled?: boolean | undefined;
|
|
529
|
+
dirs?: string[] | undefined;
|
|
530
|
+
}>>>;
|
|
501
531
|
semantic_bugs: z.ZodDefault<z.ZodOptional<z.ZodObject<{
|
|
502
532
|
enabled: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
|
|
503
533
|
rules: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
@@ -917,6 +947,18 @@ export declare const GatesSchema: z.ZodObject<{
|
|
|
917
947
|
custom_patterns: string[];
|
|
918
948
|
audit_log: boolean;
|
|
919
949
|
};
|
|
950
|
+
unindexed_reads: {
|
|
951
|
+
enabled: boolean;
|
|
952
|
+
migrations: string[];
|
|
953
|
+
};
|
|
954
|
+
deprecated_dependencies: {
|
|
955
|
+
enabled: boolean;
|
|
956
|
+
registry?: string | undefined;
|
|
957
|
+
};
|
|
958
|
+
migration_order: {
|
|
959
|
+
enabled: boolean;
|
|
960
|
+
dirs: string[];
|
|
961
|
+
};
|
|
920
962
|
semantic_bugs: {
|
|
921
963
|
enabled: boolean;
|
|
922
964
|
rules?: string[] | undefined;
|
|
@@ -1160,6 +1202,18 @@ export declare const GatesSchema: z.ZodObject<{
|
|
|
1160
1202
|
custom_patterns?: string[] | undefined;
|
|
1161
1203
|
audit_log?: boolean | undefined;
|
|
1162
1204
|
} | undefined;
|
|
1205
|
+
unindexed_reads?: {
|
|
1206
|
+
enabled?: boolean | undefined;
|
|
1207
|
+
migrations?: string[] | undefined;
|
|
1208
|
+
} | undefined;
|
|
1209
|
+
deprecated_dependencies?: {
|
|
1210
|
+
enabled?: boolean | undefined;
|
|
1211
|
+
registry?: string | undefined;
|
|
1212
|
+
} | undefined;
|
|
1213
|
+
migration_order?: {
|
|
1214
|
+
enabled?: boolean | undefined;
|
|
1215
|
+
dirs?: string[] | undefined;
|
|
1216
|
+
} | undefined;
|
|
1163
1217
|
semantic_bugs?: {
|
|
1164
1218
|
enabled?: boolean | undefined;
|
|
1165
1219
|
rules?: string[] | undefined;
|
|
@@ -1752,6 +1806,36 @@ export declare const ConfigSchema: z.ZodObject<{
|
|
|
1752
1806
|
custom_patterns?: string[] | undefined;
|
|
1753
1807
|
audit_log?: boolean | undefined;
|
|
1754
1808
|
}>>>;
|
|
1809
|
+
unindexed_reads: z.ZodDefault<z.ZodOptional<z.ZodObject<{
|
|
1810
|
+
enabled: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
|
|
1811
|
+
migrations: z.ZodDefault<z.ZodOptional<z.ZodArray<z.ZodString, "many">>>;
|
|
1812
|
+
}, "strip", z.ZodTypeAny, {
|
|
1813
|
+
enabled: boolean;
|
|
1814
|
+
migrations: string[];
|
|
1815
|
+
}, {
|
|
1816
|
+
enabled?: boolean | undefined;
|
|
1817
|
+
migrations?: string[] | undefined;
|
|
1818
|
+
}>>>;
|
|
1819
|
+
deprecated_dependencies: z.ZodDefault<z.ZodOptional<z.ZodObject<{
|
|
1820
|
+
enabled: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
|
|
1821
|
+
registry: z.ZodOptional<z.ZodString>;
|
|
1822
|
+
}, "strip", z.ZodTypeAny, {
|
|
1823
|
+
enabled: boolean;
|
|
1824
|
+
registry?: string | undefined;
|
|
1825
|
+
}, {
|
|
1826
|
+
enabled?: boolean | undefined;
|
|
1827
|
+
registry?: string | undefined;
|
|
1828
|
+
}>>>;
|
|
1829
|
+
migration_order: z.ZodDefault<z.ZodOptional<z.ZodObject<{
|
|
1830
|
+
enabled: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
|
|
1831
|
+
dirs: z.ZodDefault<z.ZodOptional<z.ZodArray<z.ZodString, "many">>>;
|
|
1832
|
+
}, "strip", z.ZodTypeAny, {
|
|
1833
|
+
enabled: boolean;
|
|
1834
|
+
dirs: string[];
|
|
1835
|
+
}, {
|
|
1836
|
+
enabled?: boolean | undefined;
|
|
1837
|
+
dirs?: string[] | undefined;
|
|
1838
|
+
}>>>;
|
|
1755
1839
|
semantic_bugs: z.ZodDefault<z.ZodOptional<z.ZodObject<{
|
|
1756
1840
|
enabled: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
|
|
1757
1841
|
rules: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
@@ -2171,6 +2255,18 @@ export declare const ConfigSchema: z.ZodObject<{
|
|
|
2171
2255
|
custom_patterns: string[];
|
|
2172
2256
|
audit_log: boolean;
|
|
2173
2257
|
};
|
|
2258
|
+
unindexed_reads: {
|
|
2259
|
+
enabled: boolean;
|
|
2260
|
+
migrations: string[];
|
|
2261
|
+
};
|
|
2262
|
+
deprecated_dependencies: {
|
|
2263
|
+
enabled: boolean;
|
|
2264
|
+
registry?: string | undefined;
|
|
2265
|
+
};
|
|
2266
|
+
migration_order: {
|
|
2267
|
+
enabled: boolean;
|
|
2268
|
+
dirs: string[];
|
|
2269
|
+
};
|
|
2174
2270
|
semantic_bugs: {
|
|
2175
2271
|
enabled: boolean;
|
|
2176
2272
|
rules?: string[] | undefined;
|
|
@@ -2414,6 +2510,18 @@ export declare const ConfigSchema: z.ZodObject<{
|
|
|
2414
2510
|
custom_patterns?: string[] | undefined;
|
|
2415
2511
|
audit_log?: boolean | undefined;
|
|
2416
2512
|
} | undefined;
|
|
2513
|
+
unindexed_reads?: {
|
|
2514
|
+
enabled?: boolean | undefined;
|
|
2515
|
+
migrations?: string[] | undefined;
|
|
2516
|
+
} | undefined;
|
|
2517
|
+
deprecated_dependencies?: {
|
|
2518
|
+
enabled?: boolean | undefined;
|
|
2519
|
+
registry?: string | undefined;
|
|
2520
|
+
} | undefined;
|
|
2521
|
+
migration_order?: {
|
|
2522
|
+
enabled?: boolean | undefined;
|
|
2523
|
+
dirs?: string[] | undefined;
|
|
2524
|
+
} | undefined;
|
|
2417
2525
|
semantic_bugs?: {
|
|
2418
2526
|
enabled?: boolean | undefined;
|
|
2419
2527
|
rules?: string[] | undefined;
|
|
@@ -2712,6 +2820,18 @@ export declare const ConfigSchema: z.ZodObject<{
|
|
|
2712
2820
|
custom_patterns: string[];
|
|
2713
2821
|
audit_log: boolean;
|
|
2714
2822
|
};
|
|
2823
|
+
unindexed_reads: {
|
|
2824
|
+
enabled: boolean;
|
|
2825
|
+
migrations: string[];
|
|
2826
|
+
};
|
|
2827
|
+
deprecated_dependencies: {
|
|
2828
|
+
enabled: boolean;
|
|
2829
|
+
registry?: string | undefined;
|
|
2830
|
+
};
|
|
2831
|
+
migration_order: {
|
|
2832
|
+
enabled: boolean;
|
|
2833
|
+
dirs: string[];
|
|
2834
|
+
};
|
|
2715
2835
|
semantic_bugs: {
|
|
2716
2836
|
enabled: boolean;
|
|
2717
2837
|
rules?: string[] | undefined;
|
|
@@ -2985,6 +3105,18 @@ export declare const ConfigSchema: z.ZodObject<{
|
|
|
2985
3105
|
custom_patterns?: string[] | undefined;
|
|
2986
3106
|
audit_log?: boolean | undefined;
|
|
2987
3107
|
} | undefined;
|
|
3108
|
+
unindexed_reads?: {
|
|
3109
|
+
enabled?: boolean | undefined;
|
|
3110
|
+
migrations?: string[] | undefined;
|
|
3111
|
+
} | undefined;
|
|
3112
|
+
deprecated_dependencies?: {
|
|
3113
|
+
enabled?: boolean | undefined;
|
|
3114
|
+
registry?: string | undefined;
|
|
3115
|
+
} | undefined;
|
|
3116
|
+
migration_order?: {
|
|
3117
|
+
enabled?: boolean | undefined;
|
|
3118
|
+
dirs?: string[] | undefined;
|
|
3119
|
+
} | undefined;
|
|
2988
3120
|
semantic_bugs?: {
|
|
2989
3121
|
enabled?: boolean | undefined;
|
|
2990
3122
|
rules?: string[] | undefined;
|
package/dist/types/index.js
CHANGED
|
@@ -270,6 +270,21 @@ export const GatesSchema = z.object({
|
|
|
270
270
|
}).optional().default({}),
|
|
271
271
|
// v4.3+ Side-Effect Safety Analysis
|
|
272
272
|
// On by default: full scans of three real repositories reported no finding that was not a real defect.
|
|
273
|
+
// A supabase-js read no index can serve, proved from the repository's own migrations (advisory, off by default).
|
|
274
|
+
unindexed_reads: z.object({
|
|
275
|
+
enabled: z.boolean().optional().default(false),
|
|
276
|
+
migrations: z.array(z.string()).optional().default([]),
|
|
277
|
+
}).optional().default({}),
|
|
278
|
+
// A dependency whose installed version npm marks deprecated (advisory, off by default: it asks the registry).
|
|
279
|
+
deprecated_dependencies: z.object({
|
|
280
|
+
enabled: z.boolean().optional().default(false),
|
|
281
|
+
registry: z.string().optional(),
|
|
282
|
+
}).optional().default({}),
|
|
283
|
+
// A migration added on a branch that sorts before the newest one on the base (advisory, off by default).
|
|
284
|
+
migration_order: z.object({
|
|
285
|
+
enabled: z.boolean().optional().default(false),
|
|
286
|
+
dirs: z.array(z.string()).optional().default(['**/supabase/migrations']),
|
|
287
|
+
}).optional().default({}),
|
|
273
288
|
semantic_bugs: z.object({
|
|
274
289
|
enabled: z.boolean().optional().default(true),
|
|
275
290
|
rules: z.array(z.string()).optional(),
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rigour-labs/core",
|
|
3
|
-
"version": "6.5.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "6.5.3",
|
|
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"
|
|
7
7
|
},
|
|
@@ -70,11 +70,11 @@
|
|
|
70
70
|
"pg": "^8.16.3",
|
|
71
71
|
"sqlite3": "^5.1.7",
|
|
72
72
|
"openai": "^4.104.0",
|
|
73
|
-
"@rigour-labs/brain-darwin-arm64": "6.5.
|
|
74
|
-
"@rigour-labs/brain-
|
|
75
|
-
"@rigour-labs/brain-
|
|
76
|
-
"@rigour-labs/brain-
|
|
77
|
-
"@rigour-labs/brain-
|
|
73
|
+
"@rigour-labs/brain-darwin-arm64": "6.5.3",
|
|
74
|
+
"@rigour-labs/brain-darwin-x64": "6.5.3",
|
|
75
|
+
"@rigour-labs/brain-linux-x64": "6.5.3",
|
|
76
|
+
"@rigour-labs/brain-linux-arm64": "6.5.3",
|
|
77
|
+
"@rigour-labs/brain-win-x64": "6.5.3"
|
|
78
78
|
},
|
|
79
79
|
"devDependencies": {
|
|
80
80
|
"@types/fs-extra": "^11.0.4",
|