Big News: Socket raises $60M Series C at a $1B valuation to secure software supply chains for AI-driven development.Announcement
Sign In

tryassay

Package Overview
Dependencies
Maintainers
1
Versions
44
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

tryassay - npm Package Compare versions

Comparing version
0.29.0
to
0.30.1
+9
dist/commands/harvest.d.ts
interface HarvestOptions {
mode?: string;
limit?: string;
repos?: string;
resume?: boolean;
stats?: boolean;
}
export declare function harvestCommand(options: HarvestOptions): Promise<void>;
export {};
import { resolve, dirname } from 'node:path';
import { fileURLToPath } from 'node:url';
import { readFile } from 'node:fs/promises';
import { harvestRepos } from '../lib/rule-harvester/harvest.js';
import { harvestPRs } from '../lib/rule-harvester/pr-harvest.js';
import { getLearnedRulesSummary } from '../lib/learned-rules/index.js';
import { loadProgress } from '../lib/rule-harvester/progress.js';
const __dirname = dirname(fileURLToPath(import.meta.url));
const CATALOG_PATH = resolve(__dirname, '../..');
const EXPERIMENTS_DIR = resolve(CATALOG_PATH, 'experiments/rule-harvester');
export async function harvestCommand(options) {
// Stats mode: just show catalog state
if (options.stats) {
const summary = await getLearnedRulesSummary(CATALOG_PATH);
const progress = await loadProgress(EXPERIMENTS_DIR);
const repoCount = Object.keys(progress).length;
const completedRepos = Object.values(progress).filter(r => r.status === 'complete').length;
console.log('='.repeat(50));
console.log(' Rule Harvester — Catalog Stats');
console.log('='.repeat(50));
console.log(` Total rules: ${summary.total}`);
console.log(` Promoted: ${summary.promoted}`);
console.log(` Candidates: ${summary.candidates}`);
console.log(` Categories: ${summary.categories.join(', ') || 'none'}`);
console.log(` Total fires: ${summary.totalFires}`);
console.log(` Repos tracked: ${repoCount} (${completedRepos} complete)`);
console.log('='.repeat(50));
return;
}
// Load repo list
const repoListPath = resolve(EXPERIMENTS_DIR, 'curated-repos.json');
let repoList;
try {
const raw = await readFile(repoListPath, 'utf-8');
repoList = JSON.parse(raw).repos;
}
catch {
console.error(`Cannot load repo list from ${repoListPath}`);
process.exit(1);
}
// Parse options
const repoFilter = options.repos?.split(',').map(s => s.trim()) ?? undefined;
const limit = options.limit ? parseInt(options.limit, 10) : undefined;
if (options.mode === 'scan') {
// Legacy scan mode
const config = {
repoList,
catalogPath: CATALOG_PATH,
progressDir: EXPERIMENTS_DIR,
runsDir: resolve(EXPERIMENTS_DIR, 'runs'),
workDir: '/private/tmp/assay-harvester',
model: process.env.ASSAY_OLLAMA_MODEL ?? 'qwen3.5:122b',
limit,
repoFilter,
resume: options.resume ?? false,
};
process.env.ASSAY_LLM_PROVIDER = process.env.ASSAY_LLM_PROVIDER ?? 'ollama';
await harvestRepos(config);
}
else {
// PR mode (default)
const prConfig = {
repoList,
catalogPath: CATALOG_PATH,
progressDir: EXPERIMENTS_DIR,
runsDir: resolve(EXPERIMENTS_DIR, 'runs'),
model: process.env.ASSAY_OLLAMA_MODEL ?? 'qwen3.5:122b',
limit,
repoFilter,
resume: options.resume ?? false,
};
process.env.ASSAY_LLM_PROVIDER = process.env.ASSAY_LLM_PROVIDER ?? 'ollama';
await harvestPRs(prConfig);
}
}
//# sourceMappingURL=harvest.js.map
{"version":3,"file":"harvest.js","sourceRoot":"","sources":["../../src/commands/harvest.ts"],"names":[],"mappings":"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"}
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
import { extractPattern, getSupportedCategories, resetPatternCounter, codifyRule, executeRule, updateRuleStatus, recordRuleFire, resetRuleCounter, validateRule, getValidationThresholds, learnFromFinding, runLearnedRules, getLearnedRulesSummary, STARTER_RULES, } from '../learned-rules/index.js';
import { mkdtemp, rm } from 'node:fs/promises';
import { join } from 'node:path';
import { tmpdir } from 'node:os';
// ── Fixtures ─────────────────────────────────────────────────
const missingErrorHandlingInput = {
claim: {
id: 'claim_001',
category: 'error-handling',
severity: 'high',
description: 'Function processOrder does not handle errors from the database call',
assertion: 'Missing error handling for async database operation',
},
verification: {
verdict: 'FAIL',
reasoning: 'No try/catch or .catch() found around the database query',
},
code: `async function processOrder(orderId: string) {
const result = await db.query('SELECT * FROM orders WHERE id = ?', [orderId]);
return result.rows[0];
}`,
language: 'typescript',
filePath: 'src/services/order.ts',
};
const sqlInjectionInput = {
claim: {
id: 'claim_002',
category: 'security',
severity: 'critical',
description: 'SQL query built with string interpolation, vulnerable to SQL injection',
assertion: 'Uses string concatenation for SQL query construction',
},
verification: {
verdict: 'FAIL',
reasoning: 'Template literal used to build SQL query with user input',
},
code: 'const query = `SELECT * FROM users WHERE id = ${userId}`;',
language: 'typescript',
filePath: 'src/db/users.ts',
};
const hardcodedSecretInput = {
claim: {
id: 'claim_003',
category: 'security',
severity: 'critical',
description: 'API key is hardcoded in source code',
assertion: 'Hardcoded secret found as string literal',
},
verification: {
verdict: 'FAIL',
reasoning: 'API key appears as a string literal in source code',
},
code: `const api_key = "sk_live_abc123def456ghi789jkl012mno345";`,
language: 'typescript',
filePath: 'src/config.ts',
};
// ── Pattern Extractor Tests ──────────────────────────────────
describe('Pattern Extractor', () => {
beforeEach(() => {
resetPatternCounter();
});
it('extracts pattern for missing error handling', () => {
const result = extractPattern(missingErrorHandlingInput);
expect(result.success).toBe(true);
expect(result.pattern).toBeDefined();
expect(result.pattern.kind).toBe('regex');
expect(result.pattern.claimCategory).toBe('error-handling');
expect(result.pattern.severity).toBe('high');
expect(result.pattern.matchBehavior).toBe('presence_is_bad');
});
it('extracts pattern for SQL injection', () => {
const result = extractPattern(sqlInjectionInput);
expect(result.success).toBe(true);
expect(result.pattern).toBeDefined();
expect(result.pattern.severity).toBe('critical');
expect(result.pattern.claimCategory).toBe('security');
});
it('extracts pattern for hardcoded secrets', () => {
const result = extractPattern(hardcodedSecretInput);
expect(result.success).toBe(true);
expect(result.pattern).toBeDefined();
expect(result.pattern.severity).toBe('critical');
});
it('rejects non-FAIL verdicts', () => {
const input = {
...missingErrorHandlingInput,
verification: { verdict: 'PASS', reasoning: 'Looks good' },
};
const result = extractPattern(input);
expect(result.success).toBe(false);
expect(result.failureReason).toContain('PASS');
});
it('returns failure for unsupported categories', () => {
const input = {
claim: {
id: 'claim_999',
category: 'performance',
severity: 'medium',
description: 'Function is slow',
assertion: 'Performance is not optimal',
},
verification: { verdict: 'FAIL', reasoning: 'Too slow' },
code: 'function slow() { for (let i = 0; i < 1000000; i++) {} }',
language: 'typescript',
filePath: 'src/slow.ts',
};
const result = extractPattern(input);
expect(result.success).toBe(false);
});
it('generates unique pattern IDs', () => {
const r1 = extractPattern(missingErrorHandlingInput);
const r2 = extractPattern(sqlInjectionInput);
expect(r1.pattern.id).not.toBe(r2.pattern.id);
});
it('reports supported categories', () => {
const categories = getSupportedCategories();
expect(categories).toContain('error-handling');
expect(categories).toContain('security');
expect(categories).toContain('edge-case');
expect(categories).toContain('correctness');
});
});
// ── Rule Codifier Tests ──────────────────────────────────────
describe('Rule Codifier', () => {
beforeEach(() => {
resetPatternCounter();
resetRuleCounter();
});
it('creates a candidate rule from a pattern', () => {
const extraction = extractPattern(missingErrorHandlingInput);
const rule = codifyRule(extraction.pattern, missingErrorHandlingInput);
expect(rule.id).toMatch(/^lr_\d{4}$/);
expect(rule.status).toBe('candidate');
expect(rule.fireCount).toBe(0);
expect(rule.sourceFindings).toHaveLength(1);
expect(rule.sourceFindings[0].claimId).toBe('claim_001');
});
it('executes a rule against matching code', () => {
const extraction = extractPattern(sqlInjectionInput);
const rule = codifyRule(extraction.pattern, sqlInjectionInput);
// Should fire on SQL injection code
const badCode = 'const q = `SELECT * FROM users WHERE name = ${name}`;';
const { fires: badFires } = executeRule(rule, badCode, 'typescript');
expect(badFires).toBe(true);
// Should NOT fire on parameterized query
const goodCode = "const q = 'SELECT * FROM users WHERE name = ?';";
const { fires: goodFires } = executeRule(rule, goodCode, 'typescript');
expect(goodFires).toBe(false);
});
it('skips rules for wrong language', () => {
const extraction = extractPattern(missingErrorHandlingInput);
const rule = codifyRule(extraction.pattern, missingErrorHandlingInput);
const { fires } = executeRule(rule, 'some python code', 'python');
expect(fires).toBe(false);
});
it('updates rule status', () => {
const extraction = extractPattern(missingErrorHandlingInput);
const rule = codifyRule(extraction.pattern, missingErrorHandlingInput);
const promoted = updateRuleStatus(rule, 'promoted');
expect(promoted.status).toBe('promoted');
expect(promoted.id).toBe(rule.id);
});
it('records rule fires', () => {
const extraction = extractPattern(missingErrorHandlingInput);
const rule = codifyRule(extraction.pattern, missingErrorHandlingInput);
const fired = recordRuleFire(rule, true);
expect(fired.fireCount).toBe(1);
expect(fired.truePositiveCount).toBe(1);
expect(fired.falsePositiveCount).toBe(0);
const firedAgain = recordRuleFire(fired, false);
expect(firedAgain.fireCount).toBe(2);
expect(firedAgain.truePositiveCount).toBe(1);
expect(firedAgain.falsePositiveCount).toBe(1);
});
});
// ── Validation Harness Tests ─────────────────────────────────
describe('Validation Harness', () => {
beforeEach(() => {
resetPatternCounter();
resetRuleCounter();
});
it('validates SQL injection rule successfully', () => {
const extraction = extractPattern(sqlInjectionInput);
const rule = codifyRule(extraction.pattern, sqlInjectionInput);
const result = validateRule(rule);
expect(result.truePositives).toBeGreaterThan(0);
expect(result.precision).toBeGreaterThan(0);
expect(result.testCases.length).toBeGreaterThanOrEqual(3);
});
it('validates hardcoded secrets rule', () => {
const extraction = extractPattern(hardcodedSecretInput);
const rule = codifyRule(extraction.pattern, hardcodedSecretInput);
const result = validateRule(rule);
expect(result.testCases.length).toBeGreaterThanOrEqual(1);
});
it('returns thresholds', () => {
const thresholds = getValidationThresholds();
expect(thresholds.minPrecision).toBe(0.8);
expect(thresholds.minRecall).toBe(0.5);
expect(thresholds.minTestCases).toBe(3);
});
});
// ── Full Pipeline Tests ──────────────────────────────────────
describe('Full Pipeline (learnFromFinding)', () => {
let tempDir;
beforeEach(async () => {
resetPatternCounter();
resetRuleCounter();
tempDir = await mkdtemp(join(tmpdir(), 'assay-learned-test-'));
});
afterEach(async () => {
try {
await rm(tempDir, { recursive: true, force: true, maxRetries: 3, retryDelay: 100 });
}
catch {
// Ignore cleanup failures in temp directories
}
});
it('learns from a SQL injection finding', async () => {
const result = await learnFromFinding(tempDir, sqlInjectionInput);
expect(result.learned).toBe(true);
expect(result.rule).toBeDefined();
expect(result.rule.status).toBe('promoted');
});
it('stores learned rules in the catalog', async () => {
await learnFromFinding(tempDir, sqlInjectionInput);
const summary = await getLearnedRulesSummary(tempDir);
expect(summary.total).toBeGreaterThanOrEqual(1);
expect(summary.promoted).toBeGreaterThanOrEqual(0);
});
it('runs learned rules against code', async () => {
const result = await learnFromFinding(tempDir, sqlInjectionInput);
const findings = await runLearnedRules(tempDir, 'const q = `SELECT * FROM accounts WHERE id = ${accountId}`;', 'typescript');
// If the rule was promoted, find the specific SQL injection finding
// (starter rules may also fire on this code, so filter by rule ID)
if (result.learned && result.rule) {
const sqlFinding = findings.find(f => f.ruleId === result.rule.id);
expect(sqlFinding).toBeDefined();
expect(sqlFinding.severity).toBe('critical');
expect(sqlFinding.category).toBe('security');
}
});
it('does not learn from PASS verdicts', async () => {
const passInput = {
...sqlInjectionInput,
verification: { verdict: 'PASS', reasoning: 'All good' },
};
const result = await learnFromFinding(tempDir, passInput);
expect(result.learned).toBe(false);
});
it('produces a summary including starter rules', async () => {
const summary = await getLearnedRulesSummary(tempDir);
// Fresh directory has no local rules, but starter catalog is always present
expect(summary.total).toBe(STARTER_RULES.length);
expect(summary.promoted).toBe(STARTER_RULES.length);
expect(summary.categories.length).toBeGreaterThan(0);
});
});
//# sourceMappingURL=learned-rules.test.js.map
{"version":3,"file":"learned-rules.test.js","sourceRoot":"","sources":["../../../src/lib/__tests__/learned-rules.test.ts"],"names":[],"mappings":"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"}
import { describe, it, expect } from 'vitest';
describe('PR Harvester Types', () => {
it('DiscoveredPR has required fields', () => {
const pr = {
repo: 'calcom/cal.com',
prNumber: 123,
title: 'fix: null check on booking',
labels: ['bug'],
mergeCommit: 'abc123',
reviewComments: [],
files: ['src/utils/booking.ts'],
};
expect(pr.repo).toBe('calcom/cal.com');
expect(pr.prNumber).toBe(123);
});
it('DiffHunk represents parsed diff output', () => {
const hunk = {
file: 'src/utils/booking.ts',
removedLines: ['const x = obj.prop;'],
addedLines: ['const x = obj?.prop;'],
context: ['function getBooking() {'],
startLine: 42,
};
expect(hunk.removedLines.length).toBe(1);
expect(hunk.addedLines.length).toBe(1);
});
it('GeneralizedRule has detection and fix fields', () => {
const rule = {
category: 'null-safety',
severity: 'medium',
description: 'Check for null before property access',
detection: { pattern: '\\w+\\.\\w+\\.\\w+', language: 'typescript' },
fix: { description: 'Use optional chaining', pattern: '\\w+\\?\\.\\w+' },
fileGlob: '**/*.ts',
matchBehavior: 'presence_is_bad',
evidenceTemplate: 'Found unsafe property access: {match} in {file}',
provenance: { repo: 'calcom/cal.com', pr: 123, file: 'src/utils/booking.ts' },
};
expect(rule.category).toBe('null-safety');
expect(rule.fix.description).toBe('Use optional chaining');
});
});
//# sourceMappingURL=pr-harvester-types.test.js.map
{"version":3,"file":"pr-harvester-types.test.js","sourceRoot":"","sources":["../../../src/lib/__tests__/pr-harvester-types.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAQ9C,QAAQ,CAAC,oBAAoB,EAAE,GAAG,EAAE;IAClC,EAAE,CAAC,kCAAkC,EAAE,GAAG,EAAE;QAC1C,MAAM,EAAE,GAAiB;YACvB,IAAI,EAAE,gBAAgB;YACtB,QAAQ,EAAE,GAAG;YACb,KAAK,EAAE,4BAA4B;YACnC,MAAM,EAAE,CAAC,KAAK,CAAC;YACf,WAAW,EAAE,QAAQ;YACrB,cAAc,EAAE,EAAE;YAClB,KAAK,EAAE,CAAC,sBAAsB,CAAC;SAChC,CAAC;QACF,MAAM,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC;QACvC,MAAM,CAAC,EAAE,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAChC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,wCAAwC,EAAE,GAAG,EAAE;QAChD,MAAM,IAAI,GAAa;YACrB,IAAI,EAAE,sBAAsB;YAC5B,YAAY,EAAE,CAAC,qBAAqB,CAAC;YACrC,UAAU,EAAE,CAAC,sBAAsB,CAAC;YACpC,OAAO,EAAE,CAAC,yBAAyB,CAAC;YACpC,SAAS,EAAE,EAAE;SACd,CAAC;QACF,MAAM,CAAC,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACzC,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACzC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,8CAA8C,EAAE,GAAG,EAAE;QACtD,MAAM,IAAI,GAAoB;YAC5B,QAAQ,EAAE,aAAa;YACvB,QAAQ,EAAE,QAAQ;YAClB,WAAW,EAAE,uCAAuC;YACpD,SAAS,EAAE,EAAE,OAAO,EAAE,oBAAoB,EAAE,QAAQ,EAAE,YAAY,EAAE;YACpE,GAAG,EAAE,EAAE,WAAW,EAAE,uBAAuB,EAAE,OAAO,EAAE,gBAAgB,EAAE;YACxE,QAAQ,EAAE,SAAS;YACnB,aAAa,EAAE,iBAAiB;YAChC,gBAAgB,EAAE,iDAAiD;YACnE,UAAU,EAAE,EAAE,IAAI,EAAE,gBAAgB,EAAE,EAAE,EAAE,GAAG,EAAE,IAAI,EAAE,sBAAsB,EAAE;SAC9E,CAAC;QACF,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;QAC1C,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC,IAAI,CAAC,uBAAuB,CAAC,CAAC;IAC7D,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
import { describe, it, expect } from 'vitest';
import { parsePatch, filterHunks } from '../rule-harvester/diff-parser.js';
describe('Diff Parser', () => {
it('parses a simple unified diff patch', () => {
const patch = `@@ -10,4 +10,4 @@ function getUser(id: string) {
const user = db.query(id);
- return user.name;
+ return user?.name;
// end`;
const hunks = parsePatch('src/utils/user.ts', patch);
expect(hunks).toHaveLength(1);
expect(hunks[0].file).toBe('src/utils/user.ts');
expect(hunks[0].removedLines).toEqual([' return user.name;']);
expect(hunks[0].addedLines).toEqual([' return user?.name;']);
expect(hunks[0].context).toContain(' const user = db.query(id);');
expect(hunks[0].startLine).toBe(10);
});
it('parses multi-hunk patch', () => {
const patch = `@@ -5,3 +5,3 @@ import { db } from './db';
-const x = 1;
+const x = 2;
// mid
@@ -20,3 +20,3 @@ function foo() {
-const y = 3;
+const y = 4;
// end`;
const hunks = parsePatch('src/file.ts', patch);
expect(hunks).toHaveLength(2);
expect(hunks[0].startLine).toBe(5);
expect(hunks[1].startLine).toBe(20);
});
it('returns empty array for null/empty patch', () => {
expect(parsePatch('file.ts', null)).toEqual([]);
expect(parsePatch('file.ts', '')).toEqual([]);
});
describe('filterHunks', () => {
it('filters out import-only changes', () => {
const hunks = [
{
file: 'src/file.ts',
removedLines: ["import { foo } from './foo';", "import { bar } from './bar';"],
addedLines: ["import { foo, bar } from './foo';", "import { baz } from './baz';"],
context: [],
startLine: 1,
},
];
expect(filterHunks(hunks)).toEqual([]);
});
it('filters out comment-only changes', () => {
const hunks = [
{
file: 'src/file.ts',
removedLines: ['// old comment', '// another old'],
addedLines: ['// new comment', '// another new'],
context: [],
startLine: 5,
},
];
expect(filterHunks(hunks)).toEqual([]);
});
it('filters out hunks with <2 meaningful lines', () => {
const hunks = [
{
file: 'src/file.ts',
removedLines: ['const x = 1;'],
addedLines: ['const x = 2;'],
context: [],
startLine: 5,
},
];
expect(filterHunks(hunks)).toEqual([]);
});
it('keeps hunks with meaningful code changes', () => {
const hunks = [
{
file: 'src/file.ts',
removedLines: ['const x = obj.prop;', 'const y = obj.other;'],
addedLines: ['const x = obj?.prop;', 'const y = obj?.other;'],
context: ['function getStuff() {'],
startLine: 10,
},
];
expect(filterHunks(hunks)).toHaveLength(1);
});
it('filters out whitespace-only changes', () => {
const hunks = [
{
file: 'src/file.ts',
removedLines: [' const x = 1;', ' const y = 2;'],
addedLines: [' const x = 1;', ' const y = 2;'],
context: [],
startLine: 5,
},
];
expect(filterHunks(hunks)).toEqual([]);
});
});
});
// ── Rule Generalizer Tests ─────────────────────────────────
import { buildGeneralizationPrompt, mapToExtractedPattern, } from '../rule-harvester/rule-generalizer.js';
describe('Rule Generalizer', () => {
describe('buildGeneralizationPrompt', () => {
it('includes before/after code', () => {
const hunk = {
file: 'src/utils/user.ts',
removedLines: [' return user.name;'],
addedLines: [' return user?.name;'],
context: ['function getUser() {'],
startLine: 10,
};
const prompt = buildGeneralizationPrompt(hunk, 'calcom/cal.com', 123, 'fix: null check on user access');
expect(prompt).toContain('return user.name;');
expect(prompt).toContain('return user?.name;');
expect(prompt).toContain('calcom/cal.com');
expect(prompt).toContain('123');
});
it('includes review comment when provided', () => {
const hunk = {
file: 'src/utils/user.ts',
removedLines: ['x'],
addedLines: ['y'],
context: [],
startLine: 1,
};
const prompt = buildGeneralizationPrompt(hunk, 'repo', 1, 'fix', 'This crashes when user is null');
expect(prompt).toContain('This crashes when user is null');
});
it('omits review comment section when not provided', () => {
const hunk = {
file: 'src/utils/user.ts',
removedLines: ['x'],
addedLines: ['y'],
context: [],
startLine: 1,
};
const prompt = buildGeneralizationPrompt(hunk, 'repo', 1, 'fix');
expect(prompt).not.toContain('Reviewer comment');
});
});
describe('mapToExtractedPattern', () => {
it('maps GeneralizedRule to ExtractedPattern', () => {
const rule = {
category: 'null-safety',
severity: 'high',
description: 'Check for null before property access',
detection: { pattern: '\\w+\\.\\w+\\.\\w+', language: 'typescript' },
fix: { description: 'Use optional chaining', pattern: '\\w+\\?\\.\\w+' },
fileGlob: '**/*.ts',
matchBehavior: 'presence_is_bad',
evidenceTemplate: 'Unsafe access: {match} in {file}',
provenance: { repo: 'calcom/cal.com', pr: 123, file: 'src/utils/user.ts' },
};
const pattern = mapToExtractedPattern(rule, 'lp_0042');
expect(pattern.id).toBe('lp_0042');
expect(pattern.regexPattern).toBe('\\w+\\.\\w+\\.\\w+');
expect(pattern.claimCategory).toBe('error-handling');
expect(pattern.severity).toBe('high');
expect(pattern.fileGlob).toBe('**/*.ts');
expect(pattern.matchBehavior).toBe('presence_is_bad');
expect(pattern.kind).toBe('regex');
expect(pattern.languages).toEqual(['typescript']);
});
it('coerces severity low to medium', () => {
const rule = {
category: 'style',
severity: 'low',
description: 'desc',
detection: { pattern: 'x', language: 'typescript' },
fix: { description: 'fix', pattern: 'y' },
fileGlob: '**/*.ts',
matchBehavior: 'presence_is_bad',
evidenceTemplate: '{match}',
provenance: { repo: 'r', pr: 1, file: 'f' },
};
const pattern = mapToExtractedPattern(rule, 'lp_0001');
expect(pattern.severity).toBe('medium');
});
it('defaults fileGlob when empty', () => {
const rule = {
category: 'test',
severity: 'medium',
description: 'desc',
detection: { pattern: 'x', language: 'typescript' },
fix: { description: 'fix', pattern: 'y' },
fileGlob: '',
matchBehavior: 'presence_is_bad',
evidenceTemplate: '{match}',
provenance: { repo: 'r', pr: 1, file: 'f' },
};
const pattern = mapToExtractedPattern(rule, 'lp_0001');
expect(pattern.fileGlob).toBe('**/*.{ts,tsx,js,jsx}');
});
});
});
// ── PR Discovery Tests ─────────────────────────────────────────
import { matchesBugLabels, matchesBugTitle, isTargetFile, DEFAULT_BUG_LABELS, } from '../rule-harvester/pr-discovery.js';
describe('PR Discovery', () => {
describe('matchesBugLabels', () => {
it('matches standard bug labels', () => {
expect(matchesBugLabels(['bug'])).toBe(true);
expect(matchesBugLabels(['fix'])).toBe(true);
expect(matchesBugLabels(['security'])).toBe(true);
expect(matchesBugLabels(['hotfix'])).toBe(true);
expect(matchesBugLabels(['bugfix'])).toBe(true);
expect(matchesBugLabels(['patch'])).toBe(true);
});
it('matches case-insensitively', () => {
expect(matchesBugLabels(['Bug'])).toBe(true);
expect(matchesBugLabels(['BUG'])).toBe(true);
});
it('matches compound labels', () => {
expect(matchesBugLabels(['type: bug'])).toBe(true);
expect(matchesBugLabels(['kind/bug'])).toBe(true);
});
it('rejects non-bug labels', () => {
expect(matchesBugLabels(['enhancement'])).toBe(false);
expect(matchesBugLabels(['feature'])).toBe(false);
expect(matchesBugLabels(['docs'])).toBe(false);
});
});
describe('matchesBugTitle', () => {
it('matches fix-related titles', () => {
expect(matchesBugTitle('fix: null check on booking input')).toBe(true);
expect(matchesBugTitle('Fix race condition in auth')).toBe(true);
expect(matchesBugTitle('bug: missing validation')).toBe(true);
expect(matchesBugTitle('resolve crash on empty input')).toBe(true);
expect(matchesBugTitle('patch: handle undefined user')).toBe(true);
});
it('rejects non-fix titles', () => {
expect(matchesBugTitle('feat: add new button')).toBe(false);
expect(matchesBugTitle('chore: update deps')).toBe(false);
expect(matchesBugTitle('refactor: clean up utils')).toBe(false);
});
});
describe('isTargetFile', () => {
it('accepts TypeScript/JavaScript files', () => {
expect(isTargetFile('src/utils/booking.ts')).toBe(true);
expect(isTargetFile('src/components/App.tsx')).toBe(true);
expect(isTargetFile('src/utils/helper.js')).toBe(true);
expect(isTargetFile('src/components/Form.jsx')).toBe(true);
});
it('rejects non-target files', () => {
expect(isTargetFile('package.json')).toBe(false);
expect(isTargetFile('README.md')).toBe(false);
expect(isTargetFile('styles.css')).toBe(false);
expect(isTargetFile('src/types.d.ts')).toBe(false);
});
it('rejects test files', () => {
expect(isTargetFile('src/utils/booking.test.ts')).toBe(false);
expect(isTargetFile('src/__tests__/app.ts')).toBe(false);
expect(isTargetFile('src/utils/booking.spec.ts')).toBe(false);
});
});
describe('DEFAULT_BUG_LABELS', () => {
it('contains expected labels', () => {
expect(DEFAULT_BUG_LABELS).toContain('bug');
expect(DEFAULT_BUG_LABELS).toContain('fix');
expect(DEFAULT_BUG_LABELS).toContain('security');
expect(DEFAULT_BUG_LABELS).toContain('regression');
});
});
});
// ── PR Harvest Orchestrator Tests ─────────────────────────────
import { createPRProgress, markPRProcessed, isPRProcessed, getPRProgressStats, diceCoefficient, } from '../rule-harvester/pr-harvest.js';
describe('PR Harvest Orchestrator', () => {
describe('PR Progress Tracking', () => {
it('creates empty progress', () => {
const progress = createPRProgress();
expect(progress).toEqual({});
});
it('marks PRs as processed', () => {
let progress = createPRProgress();
progress = markPRProcessed(progress, 'calcom/cal.com', 123);
expect(isPRProcessed(progress, 'calcom/cal.com', 123)).toBe(true);
expect(isPRProcessed(progress, 'calcom/cal.com', 456)).toBe(false);
});
it('tracks stats per repo', () => {
let progress = createPRProgress();
progress = markPRProcessed(progress, 'calcom/cal.com', 123);
progress = markPRProcessed(progress, 'calcom/cal.com', 456);
const stats = getPRProgressStats(progress, 'calcom/cal.com');
expect(stats.processedPRs).toBe(2);
});
it('does not duplicate PR numbers on re-mark', () => {
let progress = createPRProgress();
progress = markPRProcessed(progress, 'calcom/cal.com', 123);
progress = markPRProcessed(progress, 'calcom/cal.com', 123);
const stats = getPRProgressStats(progress, 'calcom/cal.com');
expect(stats.processedPRs).toBe(1);
});
it('tracks multiple repos independently', () => {
let progress = createPRProgress();
progress = markPRProcessed(progress, 'calcom/cal.com', 1);
progress = markPRProcessed(progress, 'vercel/next.js', 2);
expect(isPRProcessed(progress, 'calcom/cal.com', 1)).toBe(true);
expect(isPRProcessed(progress, 'calcom/cal.com', 2)).toBe(false);
expect(isPRProcessed(progress, 'vercel/next.js', 2)).toBe(true);
});
it('returns 0 stats for unknown repo', () => {
const progress = createPRProgress();
const stats = getPRProgressStats(progress, 'unknown/repo');
expect(stats.processedPRs).toBe(0);
});
});
describe('Fuzzy Deduplication', () => {
it('detects high similarity', () => {
const a = 'Check for null before accessing nested properties';
const b = 'Check for null before accessing nested object properties';
expect(diceCoefficient(a, b)).toBeGreaterThan(0.7);
});
it('detects low similarity', () => {
const a = 'Check for null before accessing nested properties';
const b = 'Use try-catch around async database operations';
expect(diceCoefficient(a, b)).toBeLessThan(0.3);
});
it('returns 1 for identical strings', () => {
expect(diceCoefficient('hello', 'hello')).toBe(1);
});
it('returns 0 for single-char strings', () => {
expect(diceCoefficient('a', 'b')).toBe(0);
});
it('handles empty strings', () => {
expect(diceCoefficient('', '')).toBe(1);
expect(diceCoefficient('', 'hello')).toBe(0);
});
it('is symmetric', () => {
const a = 'Check for null safety';
const b = 'Verify null safety checks';
expect(diceCoefficient(a, b)).toBeCloseTo(diceCoefficient(b, a), 5);
});
});
});
// ── CLI Integration Tests ─────────────────────────────────────────
describe('CLI Integration', () => {
it('harvest command module exports harvestCommand', async () => {
const mod = await import('../../commands/harvest.js');
expect(mod.harvestCommand).toBeDefined();
expect(typeof mod.harvestCommand).toBe('function');
});
});
//# sourceMappingURL=pr-harvester.test.js.map
{"version":3,"file":"pr-harvester.test.js","sourceRoot":"","sources":["../../../src/lib/__tests__/pr-harvester.test.ts"],"names":[],"mappings":"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"}
import { describe, it, expect } from 'vitest';
import { confirmByGrep, parseConfirmation } from '../rule-harvester/ground-truth.js';
import { buildScanResult } from '../rule-harvester/scanner.js';
import { mkdtemp, rm, readFile } from 'node:fs/promises';
import { tmpdir } from 'node:os';
import { join } from 'node:path';
import { selectFiles } from '../rule-harvester/file-selector.js';
import { loadProgress, saveProgress, getFileState, setFileState, getRepoStatus, initRepo, } from '../rule-harvester/progress.js';
import { createRunReport, saveRunReport } from '../rule-harvester/reporter.js';
describe('selectFiles', () => {
it('prioritizes API route files', () => {
const candidates = [
{ path: 'src/lib/utils.ts', lineCount: 50 },
{ path: 'src/api/users.ts', lineCount: 50 },
{ path: 'src/routes/auth.ts', lineCount: 50 },
];
const result = selectFiles(candidates);
expect(result[0].priority).toBe(1);
expect(result[0].path).toMatch(/\/(api|routes)\//);
});
it('skips test files', () => {
const candidates = [
{ path: 'src/lib/utils.test.ts', lineCount: 50 },
{ path: 'src/lib/utils.spec.ts', lineCount: 50 },
{ path: 'src/__tests__/foo.ts', lineCount: 50 },
{ path: 'src/lib/utils.ts', lineCount: 50 },
];
const result = selectFiles(candidates);
expect(result).toHaveLength(1);
expect(result[0].path).toBe('src/lib/utils.ts');
});
it('skips .d.ts files', () => {
const candidates = [
{ path: 'src/types/foo.d.ts', lineCount: 50 },
{ path: 'src/lib/utils.ts', lineCount: 50 },
];
const result = selectFiles(candidates);
expect(result).toHaveLength(1);
expect(result[0].path).toBe('src/lib/utils.ts');
});
it('skips files over 800 lines', () => {
const candidates = [
{ path: 'src/api/huge.ts', lineCount: 801 },
{ path: 'src/api/normal.ts', lineCount: 800 },
{ path: 'src/api/small.ts', lineCount: 100 },
];
const result = selectFiles(candidates);
const paths = result.map((f) => f.path);
expect(paths).not.toContain('src/api/huge.ts');
expect(paths).toContain('src/api/normal.ts');
expect(paths).toContain('src/api/small.ts');
});
it('limits to 20 files', () => {
const candidates = Array.from({ length: 50 }, (_, i) => ({
path: `src/lib/file${i}.ts`,
lineCount: 10,
}));
const result = selectFiles(candidates);
expect(result).toHaveLength(20);
});
it('assigns priority reasons', () => {
const candidates = [
{ path: 'src/api/users.ts', lineCount: 50 },
{ path: 'src/auth/login.ts', lineCount: 50 },
{ path: 'src/db/schema.ts', lineCount: 50 },
{ path: 'src/services/email.ts', lineCount: 50 },
{ path: 'src/lib/helpers.ts', lineCount: 50 },
{ path: 'src/components/button.tsx', lineCount: 50 },
{ path: 'src/misc/other.js', lineCount: 50 },
];
const result = selectFiles(candidates);
const byPath = Object.fromEntries(result.map((f) => [f.path, f]));
expect(byPath['src/api/users.ts'].reason).toMatch(/api|route|middleware/i);
expect(byPath['src/auth/login.ts'].reason).toMatch(/auth/i);
expect(byPath['src/db/schema.ts'].reason).toMatch(/database|model/i);
expect(byPath['src/services/email.ts'].reason).toMatch(/service|handler|controller/i);
expect(byPath['src/lib/helpers.ts'].reason).toMatch(/library|utility|helper/i);
expect(byPath['src/components/button.tsx'].reason).toMatch(/react/i);
expect(byPath['src/misc/other.js'].reason).toMatch(/other/i);
});
it('skips config and generated files', () => {
const candidates = [
{ path: 'tailwind.config.ts', lineCount: 20 },
{ path: 'next.config.js', lineCount: 20 },
{ path: 'vite.config.ts', lineCount: 20 },
{ path: 'jest.config.js', lineCount: 20 },
{ path: 'vitest.config.ts', lineCount: 20 },
{ path: 'eslint.config.js', lineCount: 20 },
{ path: 'prettier.config.js', lineCount: 20 },
{ path: 'tsconfig.json', lineCount: 20 },
{ path: 'package.json', lineCount: 20 },
{ path: 'src/generated/schema.ts', lineCount: 20 },
{ path: 'src/lib/real.ts', lineCount: 20 },
];
const result = selectFiles(candidates);
expect(result).toHaveLength(1);
expect(result[0].path).toBe('src/lib/real.ts');
});
it('prioritizes auth-related paths at priority 2', () => {
const candidates = [
{ path: 'src/auth/session.ts', lineCount: 50 },
{ path: 'src/db/users.ts', lineCount: 50 },
];
const result = selectFiles(candidates);
expect(result[0].path).toBe('src/auth/session.ts');
expect(result[0].priority).toBe(2);
expect(result[1].priority).toBe(3);
});
it('prioritizes database paths at priority 3', () => {
const candidates = [
{ path: 'src/services/payment.ts', lineCount: 50 },
{ path: 'src/models/user.ts', lineCount: 50 },
{ path: 'src/database/migrate.ts', lineCount: 50 },
{ path: 'src/entities/order.ts', lineCount: 50 },
];
const result = selectFiles(candidates);
const dbFiles = result.filter((f) => f.priority === 3);
expect(dbFiles.length).toBeGreaterThan(0);
const serviceFiles = result.filter((f) => f.priority === 4);
expect(serviceFiles.length).toBeGreaterThan(0);
// All db files should rank above service files
const minDbPriority = Math.min(...dbFiles.map((f) => f.priority));
const maxServicePriority = Math.max(...serviceFiles.map((f) => f.priority));
expect(minDbPriority).toBeLessThan(maxServicePriority);
});
});
describe('Progress Tracker', () => {
it('returns empty progress when no file exists', async () => {
const dir = await mkdtemp(join(tmpdir(), 'assay-progress-test-'));
try {
const progress = await loadProgress(dir);
expect(progress).toEqual({});
}
finally {
await rm(dir, { recursive: true });
}
});
it('saves and loads progress roundtrip', async () => {
const dir = await mkdtemp(join(tmpdir(), 'assay-progress-test-'));
try {
const progress = {
'owner/repo': {
status: 'in_progress',
startedAt: '2026-01-01T00:00:00.000Z',
files: {
'src/lib/foo.ts': 'scanned',
'src/lib/bar.ts': 'learned',
},
},
};
await saveProgress(dir, progress);
const loaded = await loadProgress(dir);
expect(loaded).toEqual(progress);
}
finally {
await rm(dir, { recursive: true });
}
});
it('gets file state when present', () => {
const progress = {
'owner/repo': {
status: 'in_progress',
startedAt: '2026-01-01T00:00:00.000Z',
files: { 'src/lib/foo.ts': 'scanned' },
},
};
expect(getFileState(progress, 'owner/repo', 'src/lib/foo.ts')).toBe('scanned');
});
it('returns pending when file not found', () => {
const progress = {};
expect(getFileState(progress, 'owner/repo', 'src/lib/missing.ts')).toBe('pending');
});
it('sets file state immutably (original unchanged)', () => {
const original = {};
const updated = setFileState(original, 'owner/repo', 'src/lib/foo.ts', 'scanned');
expect(original).toEqual({});
expect(updated['owner/repo'].files['src/lib/foo.ts']).toBe('scanned');
});
it('auto-creates repo entry on first setFileState', () => {
const progress = {};
const updated = setFileState(progress, 'owner/repo', 'src/lib/foo.ts', 'scanned');
expect(updated['owner/repo']).toBeDefined();
expect(updated['owner/repo'].startedAt).toBeTruthy();
expect(updated['owner/repo'].status).toBe('in_progress');
});
it('computes repo status: all learned = complete', () => {
expect(getRepoStatus({ 'a.ts': 'learned', 'b.ts': 'learned' })).toBe('complete');
});
it('computes repo status: mixed = in_progress', () => {
expect(getRepoStatus({ 'a.ts': 'learned', 'b.ts': 'scanned' })).toBe('in_progress');
});
it('computes repo status: all pending = pending', () => {
expect(getRepoStatus({ 'a.ts': 'pending', 'b.ts': 'pending' })).toBe('pending');
});
it('computes repo status: empty = pending', () => {
expect(getRepoStatus({})).toBe('pending');
});
it('computes repo status: confirmed + learned = in_progress', () => {
expect(getRepoStatus({ 'a.ts': 'confirmed', 'b.ts': 'learned' })).toBe('in_progress');
});
it('computes repo status: all confirmed = in_progress', () => {
expect(getRepoStatus({ 'a.ts': 'confirmed', 'b.ts': 'confirmed' })).toBe('in_progress');
});
it('initRepo creates entry only if absent', () => {
const empty = {};
const created = initRepo(empty, 'owner/repo');
expect(created['owner/repo']).toBeDefined();
expect(created['owner/repo'].status).toBe('pending');
expect(created['owner/repo'].files).toEqual({});
// Does not overwrite existing
const existing = {
'owner/repo': {
status: 'in_progress',
startedAt: '2026-01-01T00:00:00.000Z',
files: { 'a.ts': 'scanned' },
},
};
const unchanged = initRepo(existing, 'owner/repo');
expect(unchanged).toBe(existing); // same reference
});
});
describe('Reporter', () => {
const catalogSize = { total: 10, promoted: 7, rejected: 3 };
const makeFileResult = (overrides = {}) => ({
repo: 'owner/repo',
filePath: 'src/lib/foo.ts',
claimsExtracted: 5,
confirmed: 2,
rulesLearned: 1,
rulesRejected: 0,
rulesDuplicate: 0,
unmatchedCategories: [],
durationMs: 3000,
...overrides,
});
it('aggregates file results into a run report', () => {
const fileResults = [
makeFileResult({ filePath: 'src/lib/foo.ts', claimsExtracted: 4, confirmed: 2, rulesLearned: 1, rulesRejected: 0, rulesDuplicate: 0, durationMs: 2000 }),
makeFileResult({ filePath: 'src/lib/bar.ts', claimsExtracted: 6, confirmed: 3, rulesLearned: 2, rulesRejected: 1, rulesDuplicate: 1, durationMs: 4000 }),
];
const report = createRunReport(fileResults, 'claude-3-5-sonnet', catalogSize);
expect(report.reposScanned).toBe(1);
expect(report.filesScanned).toBe(2);
expect(report.claimsExtracted).toBe(10);
expect(report.findingsConfirmed).toBe(5);
expect(report.rulesLearned).toBe(3);
expect(report.rulesRejected).toBe(1);
expect(report.rulesDuplicate).toBe(1);
expect(report.model).toBe('claude-3-5-sonnet');
expect(report.catalogSize).toEqual(catalogSize);
expect(report.durationMinutes).toBeCloseTo((2000 + 4000) / 60_000);
});
it('saves run report to a timestamped JSON file', async () => {
const dir = await mkdtemp(join(tmpdir(), 'assay-reporter-test-'));
try {
const report = createRunReport([makeFileResult()], 'claude-3-5-sonnet', catalogSize);
const filePath = await saveRunReport(dir, report);
// File should exist and be parseable JSON
const raw = await readFile(filePath, 'utf-8');
const parsed = JSON.parse(raw);
expect(parsed.model).toBe('claude-3-5-sonnet');
expect(parsed.filesScanned).toBe(1);
expect(parsed.timestamp).toBe(report.timestamp);
// Filename should not contain : or .
const filename = filePath.split('/').pop();
expect(filename).not.toContain(':');
expect(filename).toMatch(/\.json$/);
}
finally {
await rm(dir, { recursive: true });
}
});
it('aggregates unmatchedCategories counts across multiple files', () => {
const fileResults = [
makeFileResult({ unmatchedCategories: ['security', 'performance'] }),
makeFileResult({ unmatchedCategories: ['security', 'correctness'] }),
];
const report = createRunReport(fileResults, 'claude-3-5-sonnet', catalogSize);
expect(report.unmatchedCategories['security']).toBe(2);
expect(report.unmatchedCategories['performance']).toBe(1);
expect(report.unmatchedCategories['correctness']).toBe(1);
});
it('handles empty file results gracefully', () => {
const report = createRunReport([], 'claude-3-5-sonnet', catalogSize);
expect(report.reposScanned).toBe(0);
expect(report.filesScanned).toBe(0);
expect(report.claimsExtracted).toBe(0);
expect(report.findingsConfirmed).toBe(0);
expect(report.rulesLearned).toBe(0);
expect(report.rulesRejected).toBe(0);
expect(report.rulesDuplicate).toBe(0);
expect(report.unmatchedCategories).toEqual({});
expect(report.durationMinutes).toBe(0);
});
});
describe('Ground Truth Validator', () => {
const makeClaim = (overrides = {}) => ({
claimId: 'claim-001',
category: 'error-handling',
description: 'Missing try/catch around async call',
verdict: 'FAIL',
...overrides,
});
it('confirms missing error handling (await without try/catch)', () => {
const code = `
async function fetchData() {
const result = await fetch('https://example.com/api');
return result.json();
}
`;
const claim = makeClaim();
const result = confirmByGrep(code, claim);
expect(result.confirmed).toBe(true);
expect(result.method).toBe('grep');
expect(result.evidence).toMatch(/await without try\/catch/);
});
it('rejects error handling claim when try/catch exists', () => {
const code = `
async function fetchData() {
try {
const result = await fetch('https://example.com/api');
return result.json();
} catch (err) {
console.error(err);
}
}
`;
const claim = makeClaim();
const result = confirmByGrep(code, claim);
expect(result.confirmed).toBe(false);
expect(result.method).toBe('grep');
});
it('confirms SQL injection (template literal with ${})', () => {
const code = `
async function getUser(id: string) {
const query = \`SELECT * FROM users WHERE id = \${id}\`;
return db.query(query);
}
`;
const claim = makeClaim({
claimId: 'claim-002',
category: 'security',
description: 'SQL injection via template literal query construction',
verdict: 'FAIL',
});
const result = confirmByGrep(code, claim);
expect(result.confirmed).toBe(true);
expect(result.method).toBe('grep');
expect(result.evidence).toMatch(/SQL template literal/);
});
it('confirms missing null check (nested access without ?.)', () => {
const code = `
function getCity(user: any) {
return user.address.city.name;
}
`;
const claim = makeClaim({
claimId: 'claim-003',
category: 'edge-case',
description: 'Missing null check on nested property access',
verdict: 'FAIL',
});
const result = confirmByGrep(code, claim);
expect(result.confirmed).toBe(true);
expect(result.method).toBe('grep');
expect(result.evidence).toMatch(/optional chaining/i);
});
it('returns not-confirmed for unknown categories', () => {
const code = `const x = 42;`;
const claim = makeClaim({
claimId: 'claim-004',
category: 'performance',
description: 'Inefficient algorithm in hot path',
verdict: 'FAIL',
});
const result = confirmByGrep(code, claim);
expect(result.confirmed).toBe(false);
expect(result.method).toBe('grep');
expect(result.evidence).toMatch(/No grep strategy for category "performance"/);
});
it('confirms hardcoded secret pattern', () => {
const code = `
const API_KEY = "sk-abc123def456ghi789jkl";
const client = new Client(API_KEY);
`;
const claim = makeClaim({
claimId: 'claim-005',
category: 'hardcoded secret',
description: 'Hardcoded API key in source code',
verdict: 'FAIL',
});
const result = confirmByGrep(code, claim);
expect(result.confirmed).toBe(true);
expect(result.method).toBe('grep');
expect(result.evidence).toMatch(/hardcoded secret/i);
});
it('rejects hardcoded secret when no pattern match', () => {
const code = `
const apiKey = process.env.API_KEY;
const client = new Client(apiKey);
`;
const claim = makeClaim({
claimId: 'claim-006',
category: 'hardcoded secret',
description: 'Hardcoded API key in source code',
verdict: 'FAIL',
});
const result = confirmByGrep(code, claim);
expect(result.confirmed).toBe(false);
expect(result.method).toBe('grep');
});
it('uses compiler method when tsc reports error in file for type-safety claim', () => {
const code = `const x: string = 42;`;
const claim = makeClaim({
claimId: 'claim-007',
category: 'type-safety',
description: 'Type mismatch',
verdict: 'FAIL',
});
const tscOutput = `src/lib/foo.ts(1,7): error TS2322: Type 'number' is not assignable to type 'string'.`;
const result = parseConfirmation(code, claim, tscOutput, 'src/lib/foo.ts');
expect(result.confirmed).toBe(true);
expect(result.method).toBe('compiler');
expect(result.evidence).toMatch(/tsc reported error/);
});
it('falls through to grep when tscOutput is null', () => {
const code = `
async function load() {
const data = await getData();
return data;
}
`;
const claim = makeClaim({
claimId: 'claim-008',
category: 'error-handling',
description: 'Missing try/catch',
verdict: 'FAIL',
});
const result = parseConfirmation(code, claim, null, 'src/lib/foo.ts');
expect(result.method).toBe('grep');
expect(result.confirmed).toBe(true);
});
});
describe('Scanner', () => {
const makeClaim = (overrides = {}) => ({
id: 'claim-001',
category: 'error-handling',
severity: 'high',
description: 'Missing error handling around async call',
assertion: 'All async calls must be wrapped in try/catch',
...overrides,
});
const makeVerification = (overrides = {}) => ({
claimId: 'claim-001',
verdict: 'FAIL',
reasoning: 'No try/catch found around async call',
method: 'llm',
...overrides,
});
it('builds scan result from claims and verifications (3 claims, 2 FAIL, 1 PASS → 2 failures)', () => {
const claims = [
makeClaim({ id: 'c1', category: 'error-handling', severity: 'high' }),
makeClaim({ id: 'c2', category: 'security', severity: 'critical' }),
makeClaim({ id: 'c3', category: 'type-safety', severity: 'medium' }),
];
const verifications = [
makeVerification({ claimId: 'c1', verdict: 'FAIL' }),
makeVerification({ claimId: 'c2', verdict: 'PASS', reasoning: 'Input is validated' }),
makeVerification({ claimId: 'c3', verdict: 'FAIL', reasoning: 'Type mismatch detected' }),
];
const result = buildScanResult(claims, verifications);
expect(result.totalClaims).toBe(3);
expect(result.failures).toHaveLength(2);
expect(result.failures.map((f) => f.claimId)).toEqual(expect.arrayContaining(['c1', 'c3']));
});
it('extracts failure metadata for learning pipeline (all fields populated correctly)', () => {
const claims = [
makeClaim({
id: 'c1',
category: 'security',
severity: 'critical',
description: 'SQL injection vulnerability',
assertion: 'User input must never be interpolated into SQL',
}),
];
const verifications = [
makeVerification({
claimId: 'c1',
verdict: 'FAIL',
reasoning: 'Template literal used with user input in query',
method: 'formal',
}),
];
const result = buildScanResult(claims, verifications);
expect(result.failures).toHaveLength(1);
const failure = result.failures[0];
expect(failure.claimId).toBe('c1');
expect(failure.category).toBe('security');
expect(failure.severity).toBe('critical');
expect(failure.description).toBe('SQL injection vulnerability');
expect(failure.assertion).toBe('User input must never be interpolated into SQL');
expect(failure.reasoning).toBe('Template literal used with user input in query');
expect(failure.method).toBe('formal');
});
it('handles empty verifications (0 claims → 0 failures)', () => {
const result = buildScanResult([], []);
expect(result.totalClaims).toBe(0);
expect(result.failures).toHaveLength(0);
});
it('filters PARTIAL verdicts into failures too', () => {
const claims = [
makeClaim({ id: 'c1', category: 'error-handling', severity: 'medium' }),
makeClaim({ id: 'c2', category: 'performance', severity: 'low' }),
];
const verifications = [
makeVerification({ claimId: 'c1', verdict: 'PARTIAL', reasoning: 'Error partially handled' }),
makeVerification({ claimId: 'c2', verdict: 'PASS', reasoning: 'Performance acceptable' }),
];
const result = buildScanResult(claims, verifications);
expect(result.totalClaims).toBe(2);
expect(result.failures).toHaveLength(1);
expect(result.failures[0].claimId).toBe('c1');
expect(result.failures[0].reasoning).toBe('Error partially handled');
});
});
//# sourceMappingURL=rule-harvester.test.js.map
{"version":3,"file":"rule-harvester.test.js","sourceRoot":"","sources":["../../../src/lib/__tests__/rule-harvester.test.ts"],"names":[],"mappings":"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"}
/**
* Canonical Category Map — normalizes the fragmented claim categories
* (37 variants) down to ~12 canonical categories.
*
* Used in two places:
* 1. Rule creation (harvester + pattern extractor) — new rules get canonical categories
* 2. Rule loading (catalog) — existing rules get mapped at load time
*/
/**
* The canonical set of claim categories for all learned rules.
*/
export declare const CANONICAL_CATEGORIES: readonly ["security", "error-handling", "data-validation", "async", "resource-management", "api-contract", "state-management", "i18n", "performance", "type-safety", "ui-logic", "code-quality"];
export type CanonicalCategory = (typeof CANONICAL_CATEGORIES)[number];
/**
* Maps every known raw category string to its canonical form.
*
* When adding new mappings, keep the keys lowercase and hyphenated.
*/
export declare const CATEGORY_MAP: Readonly<Record<string, CanonicalCategory>>;
/**
* Normalize a raw category string to its canonical form.
*
* Lookup order:
* 1. Exact match in CATEGORY_MAP
* 2. Lowercase + trim exact match
* 3. Fallback to 'code-quality' (the broadest bucket)
*/
export declare function normalizeCategory(raw: string): CanonicalCategory;
/**
* Canonical Category Map — normalizes the fragmented claim categories
* (37 variants) down to ~12 canonical categories.
*
* Used in two places:
* 1. Rule creation (harvester + pattern extractor) — new rules get canonical categories
* 2. Rule loading (catalog) — existing rules get mapped at load time
*/
/**
* The canonical set of claim categories for all learned rules.
*/
export const CANONICAL_CATEGORIES = [
'security',
'error-handling',
'data-validation',
'async',
'resource-management',
'api-contract',
'state-management',
'i18n',
'performance',
'type-safety',
'ui-logic',
'code-quality',
];
/**
* Maps every known raw category string to its canonical form.
*
* When adding new mappings, keep the keys lowercase and hyphenated.
*/
export const CATEGORY_MAP = {
// ── security ────────────────────────────────────────────────
'security': 'security',
// ── error-handling ──────────────────────────────────────────
'error-handling': 'error-handling',
'null-safety': 'error-handling',
'logging': 'error-handling',
// ── data-validation ─────────────────────────────────────────
'data-validation': 'data-validation',
'validation-safety': 'data-validation',
'data-integrity': 'data-validation',
'data-accuracy': 'data-validation',
'data-consistency': 'data-validation',
'database-integrity': 'data-validation',
// ── async ───────────────────────────────────────────────────
'async': 'async',
'concurrency': 'async',
'race-condition': 'async',
'race-conditions': 'async',
// ── resource-management ─────────────────────────────────────
'resource-management': 'resource-management',
// ── api-contract ────────────────────────────────────────────
'api-contract': 'api-contract',
'dependency-injection': 'api-contract',
// ── state-management ────────────────────────────────────────
'state-management': 'state-management',
// ── i18n ────────────────────────────────────────────────────
'i18n': 'i18n',
'i18n-compliance': 'i18n',
'i18n-safety': 'i18n',
'i18n-support': 'i18n',
'internationalization': 'i18n',
'localization': 'i18n',
// ── performance ─────────────────────────────────────────────
'performance': 'performance',
'react-optimization': 'performance',
// ── type-safety ─────────────────────────────────────────────
'type-safety': 'type-safety',
// ── ui-logic ────────────────────────────────────────────────
'ui-logic': 'ui-logic',
'ui-ux': 'ui-logic',
'react-antipatterns': 'ui-logic',
'react-best-practices': 'ui-logic',
'react-patterns': 'ui-logic',
// ── code-quality ────────────────────────────────────────────
'code-quality': 'code-quality',
'code-convention': 'code-quality',
'documentation': 'code-quality',
'test-reliability': 'code-quality',
'test-robustness': 'code-quality',
// ── catch-all for logic variants ────────────────────────────
'logic': 'error-handling',
'logic-error': 'error-handling',
'logic-flow': 'error-handling',
'logic-control-flow': 'error-handling',
'bug-fix': 'error-handling',
'business-logic': 'data-validation',
};
/**
* Normalize a raw category string to its canonical form.
*
* Lookup order:
* 1. Exact match in CATEGORY_MAP
* 2. Lowercase + trim exact match
* 3. Fallback to 'code-quality' (the broadest bucket)
*/
export function normalizeCategory(raw) {
// 1. Exact match
const exact = CATEGORY_MAP[raw];
if (exact)
return exact;
// 2. Normalized match (lowercase, trimmed)
const normalized = raw.toLowerCase().trim();
const normMatch = CATEGORY_MAP[normalized];
if (normMatch)
return normMatch;
// 3. Fallback
return 'code-quality';
}
//# sourceMappingURL=category-map.js.map
{"version":3,"file":"category-map.js","sourceRoot":"","sources":["../../../src/lib/learned-rules/category-map.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH;;GAEG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAAG;IAClC,UAAU;IACV,gBAAgB;IAChB,iBAAiB;IACjB,OAAO;IACP,qBAAqB;IACrB,cAAc;IACd,kBAAkB;IAClB,MAAM;IACN,aAAa;IACb,aAAa;IACb,UAAU;IACV,cAAc;CACN,CAAC;AAIX;;;;GAIG;AACH,MAAM,CAAC,MAAM,YAAY,GAAgD;IACvE,+DAA+D;IAC/D,UAAU,EAAE,UAAU;IAEtB,+DAA+D;IAC/D,gBAAgB,EAAE,gBAAgB;IAClC,aAAa,EAAE,gBAAgB;IAC/B,SAAS,EAAE,gBAAgB;IAE3B,+DAA+D;IAC/D,iBAAiB,EAAE,iBAAiB;IACpC,mBAAmB,EAAE,iBAAiB;IACtC,gBAAgB,EAAE,iBAAiB;IACnC,eAAe,EAAE,iBAAiB;IAClC,kBAAkB,EAAE,iBAAiB;IACrC,oBAAoB,EAAE,iBAAiB;IAEvC,+DAA+D;IAC/D,OAAO,EAAE,OAAO;IAChB,aAAa,EAAE,OAAO;IACtB,gBAAgB,EAAE,OAAO;IACzB,iBAAiB,EAAE,OAAO;IAE1B,+DAA+D;IAC/D,qBAAqB,EAAE,qBAAqB;IAE5C,+DAA+D;IAC/D,cAAc,EAAE,cAAc;IAC9B,sBAAsB,EAAE,cAAc;IAEtC,+DAA+D;IAC/D,kBAAkB,EAAE,kBAAkB;IAEtC,+DAA+D;IAC/D,MAAM,EAAE,MAAM;IACd,iBAAiB,EAAE,MAAM;IACzB,aAAa,EAAE,MAAM;IACrB,cAAc,EAAE,MAAM;IACtB,sBAAsB,EAAE,MAAM;IAC9B,cAAc,EAAE,MAAM;IAEtB,+DAA+D;IAC/D,aAAa,EAAE,aAAa;IAC5B,oBAAoB,EAAE,aAAa;IAEnC,+DAA+D;IAC/D,aAAa,EAAE,aAAa;IAE5B,+DAA+D;IAC/D,UAAU,EAAE,UAAU;IACtB,OAAO,EAAE,UAAU;IACnB,oBAAoB,EAAE,UAAU;IAChC,sBAAsB,EAAE,UAAU;IAClC,gBAAgB,EAAE,UAAU;IAE5B,+DAA+D;IAC/D,cAAc,EAAE,cAAc;IAC9B,iBAAiB,EAAE,cAAc;IACjC,eAAe,EAAE,cAAc;IAC/B,kBAAkB,EAAE,cAAc;IAClC,iBAAiB,EAAE,cAAc;IAEjC,+DAA+D;IAC/D,OAAO,EAAE,gBAAgB;IACzB,aAAa,EAAE,gBAAgB;IAC/B,YAAY,EAAE,gBAAgB;IAC9B,oBAAoB,EAAE,gBAAgB;IACtC,SAAS,EAAE,gBAAgB;IAC3B,gBAAgB,EAAE,iBAAiB;CACpC,CAAC;AAEF;;;;;;;GAOG;AACH,MAAM,UAAU,iBAAiB,CAAC,GAAW;IAC3C,iBAAiB;IACjB,MAAM,KAAK,GAAG,YAAY,CAAC,GAAG,CAAC,CAAC;IAChC,IAAI,KAAK;QAAE,OAAO,KAAK,CAAC;IAExB,2CAA2C;IAC3C,MAAM,UAAU,GAAG,GAAG,CAAC,WAAW,EAAE,CAAC,IAAI,EAAE,CAAC;IAC5C,MAAM,SAAS,GAAG,YAAY,CAAC,UAAU,CAAC,CAAC;IAC3C,IAAI,SAAS;QAAE,OAAO,SAAS,CAAC;IAEhC,cAAc;IACd,OAAO,cAAc,CAAC;AACxB,CAAC"}
/**
* Self-Expanding Formal Verification — Phase 1 Entry Point
*
* Pipeline: LLM finds bug → pattern extractor → rule codifier → validation → catalog
*
* Usage:
* // After a scan confirms a finding:
* const result = await learnFromFinding(projectPath, input);
*
* // During a scan, run learned rules alongside hand-crafted ones:
* const findings = await runLearnedRules(projectPath, code, language);
*/
export { extractPattern, getSupportedCategories, resetPatternCounter } from './pattern-extractor.js';
export { normalizeCategory, CANONICAL_CATEGORIES, CATEGORY_MAP } from './category-map.js';
export type { CanonicalCategory } from './category-map.js';
export { codifyRule, executeRule, updateRuleStatus, recordRuleFire, resetRuleCounter } from './rule-codifier.js';
export { validateRule, getValidationThresholds } from './validation-harness.js';
export { STARTER_RULES } from './starter-catalog.js';
export { loadRules, saveRules, addRule, updateRule, getPromotedRules, getCandidateRules, getRulesByCategory, loadStats, } from './learned-catalog.js';
export type { ExtractedPattern, LearnedRule, LearnedRuleStatus, PatternExtractionInput, PatternExtractionResult, SourceFinding, ValidationResult, TestCase, PatternKind, PRReviewComment, DiscoveredPR, DiffHunk, ParsedPRDiff, GeneralizedRule, } from './types.js';
import type { PatternExtractionInput, LearnedRule } from './types.js';
/**
* Tracks per-rule fire counts across files within a single `assay assess` run.
*
* After a rule fires in >30% of files scanned so far (minimum 5 files),
* it is suppressed for the remainder of the scan.
*
* Create one per scan run and pass it to `runLearnedRules()`.
*/
export declare class ScanSession {
/** Total files processed so far. */
private fileCount;
/** ruleId → number of files where the rule fired. */
private readonly fireCounts;
/** Rules that have been permanently suppressed for this session. */
private readonly suppressed;
/** Call once per file, before running rules on that file. */
recordFile(): void;
/** Record that a rule fired on the current file. */
recordFire(ruleId: string): void;
/** Check whether a rule should be suppressed. */
isSuppressed(ruleId: string): boolean;
/** Snapshot of session stats (useful for diagnostics). */
stats(): ScanSessionStats;
}
/** Diagnostic snapshot of a scan session. */
export interface ScanSessionStats {
readonly filesScanned: number;
readonly suppressedRules: string[];
readonly fireCounts: Record<string, number>;
}
export interface LearnResult {
/** Whether a new rule was created. */
readonly learned: boolean;
/** The rule, if created. */
readonly rule?: LearnedRule;
/** Why learning didn't produce a rule. */
readonly reason?: string;
}
/**
* Full pipeline: extract pattern → codify rule → validate → store.
*
* Call this after confirming an LLM finding is correct.
* The system will try to extract a generalizable pattern and
* create a formal rule that catches future instances.
*
* @param projectPath - Root of the project being scanned.
* @param input - The confirmed finding with code context.
* @returns Whether a rule was learned and stored.
*/
export declare function learnFromFinding(projectPath: string, input: PatternExtractionInput): Promise<LearnResult>;
/**
* Run all promoted learned rules against code.
*
* Returns findings from rules that fire.
* These run alongside the hand-crafted formal checks.
*
* @param session - Optional scan session for noise suppression.
* When provided, rules that fire on >30% of files (min 5 files)
* are suppressed for the remainder of the scan. The caller must
* call `session.recordFile()` before each invocation.
*/
export declare function runLearnedRules(projectPath: string, code: string, language: string, session?: ScanSession): Promise<LearnedRuleFinding[]>;
/** A finding produced by a learned rule. */
export interface LearnedRuleFinding {
readonly ruleId: string;
readonly description: string;
readonly severity: 'critical' | 'high' | 'medium';
readonly category: string;
readonly evidence: string;
readonly matches: string[];
/** Confidence based on the rule's track record. */
readonly confidence: number;
}
/**
* Get a summary of the learned rule system status.
*/
export declare function getLearnedRulesSummary(projectPath: string): Promise<{
total: number;
promoted: number;
candidates: number;
rejected: number;
categories: string[];
totalFires: number;
}>;
/**
* Self-Expanding Formal Verification — Phase 1 Entry Point
*
* Pipeline: LLM finds bug → pattern extractor → rule codifier → validation → catalog
*
* Usage:
* // After a scan confirms a finding:
* const result = await learnFromFinding(projectPath, input);
*
* // During a scan, run learned rules alongside hand-crafted ones:
* const findings = await runLearnedRules(projectPath, code, language);
*/
export { extractPattern, getSupportedCategories, resetPatternCounter } from './pattern-extractor.js';
export { normalizeCategory, CANONICAL_CATEGORIES, CATEGORY_MAP } from './category-map.js';
export { codifyRule, executeRule, updateRuleStatus, recordRuleFire, resetRuleCounter } from './rule-codifier.js';
export { validateRule, getValidationThresholds } from './validation-harness.js';
export { STARTER_RULES } from './starter-catalog.js';
export { loadRules, saveRules, addRule, updateRule, getPromotedRules, getCandidateRules, getRulesByCategory, loadStats, } from './learned-catalog.js';
import { extractPattern } from './pattern-extractor.js';
import { codifyRule, executeRule } from './rule-codifier.js';
import { validateRule } from './validation-harness.js';
import { addRule, getPromotedRules, updateRule } from './learned-catalog.js';
import { updateRuleStatus } from './rule-codifier.js';
// ── Scan Session (Noise Suppression) ────────────────────────
/** Minimum files scanned before noise suppression kicks in. */
const NOISE_MIN_FILES = 5;
/** If a rule fires on more than this fraction of files, suppress it. */
const NOISE_THRESHOLD = 0.3;
/**
* Tracks per-rule fire counts across files within a single `assay assess` run.
*
* After a rule fires in >30% of files scanned so far (minimum 5 files),
* it is suppressed for the remainder of the scan.
*
* Create one per scan run and pass it to `runLearnedRules()`.
*/
export class ScanSession {
/** Total files processed so far. */
fileCount = 0;
/** ruleId → number of files where the rule fired. */
fireCounts = new Map();
/** Rules that have been permanently suppressed for this session. */
suppressed = new Set();
/** Call once per file, before running rules on that file. */
recordFile() {
this.fileCount++;
}
/** Record that a rule fired on the current file. */
recordFire(ruleId) {
this.fireCounts.set(ruleId, (this.fireCounts.get(ruleId) ?? 0) + 1);
}
/** Check whether a rule should be suppressed. */
isSuppressed(ruleId) {
if (this.suppressed.has(ruleId))
return true;
if (this.fileCount >= NOISE_MIN_FILES) {
const fires = this.fireCounts.get(ruleId) ?? 0;
if (fires / this.fileCount > NOISE_THRESHOLD) {
this.suppressed.add(ruleId);
return true;
}
}
return false;
}
/** Snapshot of session stats (useful for diagnostics). */
stats() {
return {
filesScanned: this.fileCount,
suppressedRules: [...this.suppressed],
fireCounts: Object.fromEntries(this.fireCounts),
};
}
}
/**
* Full pipeline: extract pattern → codify rule → validate → store.
*
* Call this after confirming an LLM finding is correct.
* The system will try to extract a generalizable pattern and
* create a formal rule that catches future instances.
*
* @param projectPath - Root of the project being scanned.
* @param input - The confirmed finding with code context.
* @returns Whether a rule was learned and stored.
*/
export async function learnFromFinding(projectPath, input) {
// Step 1: Extract pattern
const extraction = extractPattern(input);
if (!extraction.success || !extraction.pattern) {
return {
learned: false,
reason: extraction.failureReason ?? 'Pattern extraction failed',
};
}
// Step 2: Codify as a rule
const rule = codifyRule(extraction.pattern, input);
// Step 3: Validate against synthetic test cases
const validation = validateRule(rule);
const validatedRule = {
...rule,
validationResults: validation,
};
if (!validation.passed) {
// Store as rejected for record-keeping
const rejectedRule = updateRuleStatus(validatedRule, 'rejected');
await addRule(projectPath, rejectedRule);
return {
learned: false,
rule: rejectedRule,
reason: `Validation failed: precision=${validation.precision.toFixed(2)}, recall=${validation.recall.toFixed(2)}`,
};
}
// Step 4: Promote to validated and store
const promotedRule = updateRuleStatus(validatedRule, 'promoted');
await addRule(projectPath, promotedRule);
return {
learned: true,
rule: promotedRule,
};
}
/**
* Run all promoted learned rules against code.
*
* Returns findings from rules that fire.
* These run alongside the hand-crafted formal checks.
*
* @param session - Optional scan session for noise suppression.
* When provided, rules that fire on >30% of files (min 5 files)
* are suppressed for the remainder of the scan. The caller must
* call `session.recordFile()` before each invocation.
*/
export async function runLearnedRules(projectPath, code, language, session) {
const rules = await getPromotedRules(projectPath);
const findings = [];
for (const rule of rules) {
// Skip rules the session has already flagged as noise
if (session?.isSuppressed(rule.id))
continue;
const { fires, matches } = executeRule(rule, code, language);
if (fires) {
// Record the fire in the session before checking suppression
if (session) {
session.recordFire(rule.id);
// Re-check: this fire may have pushed the rule over the threshold
if (session.isSuppressed(rule.id))
continue;
}
findings.push({
ruleId: rule.id,
description: rule.pattern.description,
severity: rule.pattern.severity,
category: rule.pattern.claimCategory,
evidence: rule.pattern.evidenceTemplate
.replace('{match}', matches[0] ?? 'pattern detected')
.replace('{file}', 'current file'),
matches,
confidence: computeConfidence(rule),
});
// Track the fire (async, don't block)
updateRule(projectPath, rule.id, r => ({
...r,
fireCount: r.fireCount + 1,
updatedAt: new Date().toISOString(),
})).catch(() => { });
}
}
return findings;
}
/**
* Compute confidence for a rule based on its track record.
* New rules start at 0.7, confidence grows with confirmed true positives.
*/
function computeConfidence(rule) {
if (rule.fireCount === 0)
return 0.7; // New rule, moderate confidence
const precision = rule.truePositiveCount + rule.falsePositiveCount > 0
? rule.truePositiveCount / (rule.truePositiveCount + rule.falsePositiveCount)
: 0.7;
// Scale: min 0.5, max 0.95
// More fires with high precision → higher confidence
const fireBoost = Math.min(rule.fireCount / 100, 0.15);
return Math.min(0.95, Math.max(0.5, precision * 0.8 + fireBoost + 0.1));
}
/**
* Get a summary of the learned rule system status.
*/
export async function getLearnedRulesSummary(projectPath) {
const { loadRules } = await import('./learned-catalog.js');
const rules = await loadRules(projectPath);
return {
total: rules.length,
promoted: rules.filter(r => r.status === 'promoted').length,
candidates: rules.filter(r => r.status === 'candidate').length,
rejected: rules.filter(r => r.status === 'rejected').length,
categories: [...new Set(rules.map(r => r.pattern.claimCategory))],
totalFires: rules.reduce((sum, r) => sum + r.fireCount, 0),
};
}
//# sourceMappingURL=index.js.map
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/lib/learned-rules/index.ts"],"names":[],"mappings":"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"}
/**
* Learned Rule Catalog — stores, loads, and queries auto-generated formal rules.
*
* Rules are stored as JSON in the `learned/` subdirectory alongside
* hand-crafted rules in check-catalog.ts. This separation ensures
* auto-generated rules are clearly distinguishable.
*
* Storage structure:
* .assay/learned/
* rules.json — all learned rules
* stats.json — catalog statistics over time
*/
import type { LearnedRule, LearnedRuleStatus } from './types.js';
export interface CatalogStats {
totalRules: number;
byStatus: Record<LearnedRuleStatus, number>;
byCategory: Record<string, number>;
bySeverity: Record<string, number>;
totalFires: number;
totalTruePositives: number;
totalFalsePositives: number;
overallPrecision: number;
lastUpdated: string;
}
/**
* Load all learned rules from the project's catalog,
* merged with the bundled starter catalog.
*
* Local rules (from .assay/learned/rules.json) take precedence over
* starter rules when IDs collide. Starter rules provide out-of-the-box
* coverage even when no local catalog exists.
*/
export declare function loadRules(projectPath: string): Promise<LearnedRule[]>;
/**
* Save all learned rules to the project's catalog.
*/
export declare function saveRules(projectPath: string, rules: LearnedRule[]): Promise<void>;
/**
* Add a new rule to the catalog.
* Returns the updated rule list.
*/
export declare function addRule(projectPath: string, rule: LearnedRule): Promise<LearnedRule[]>;
/**
* Update a rule in the catalog by ID.
*/
export declare function updateRule(projectPath: string, ruleId: string, updater: (rule: LearnedRule) => LearnedRule): Promise<LearnedRule | null>;
/**
* Get all promoted rules (ready to run in the formal verifier).
*/
export declare function getPromotedRules(projectPath: string): Promise<LearnedRule[]>;
/**
* Get all candidate rules (awaiting validation).
*/
export declare function getCandidateRules(projectPath: string): Promise<LearnedRule[]>;
/**
* Get rules by category.
*/
export declare function getRulesByCategory(projectPath: string, category: string): Promise<LearnedRule[]>;
/**
* Load catalog statistics.
*/
export declare function loadStats(projectPath: string): Promise<CatalogStats | null>;
/**
* Learned Rule Catalog — stores, loads, and queries auto-generated formal rules.
*
* Rules are stored as JSON in the `learned/` subdirectory alongside
* hand-crafted rules in check-catalog.ts. This separation ensures
* auto-generated rules are clearly distinguishable.
*
* Storage structure:
* .assay/learned/
* rules.json — all learned rules
* stats.json — catalog statistics over time
*/
import { readFile, writeFile, mkdir } from 'node:fs/promises';
import { join } from 'node:path';
import { normalizeCategory } from './category-map.js';
import { STARTER_RULES } from './starter-catalog.js';
// ── File Paths ───────────────────────────────────────────────
function rulesPath(projectPath) {
return join(projectPath, '.assay', 'learned', 'rules.json');
}
function statsPath(projectPath) {
return join(projectPath, '.assay', 'learned', 'stats.json');
}
// ── CRUD Operations ──────────────────────────────────────────
/**
* Load all learned rules from the project's catalog,
* merged with the bundled starter catalog.
*
* Local rules (from .assay/learned/rules.json) take precedence over
* starter rules when IDs collide. Starter rules provide out-of-the-box
* coverage even when no local catalog exists.
*/
export async function loadRules(projectPath) {
// Load local rules from disk
let localRules = [];
try {
const content = await readFile(rulesPath(projectPath), 'utf-8');
const data = JSON.parse(content);
if (Array.isArray(data)) {
localRules = data.map((rule) => ({
...rule,
pattern: {
...rule.pattern,
claimCategory: normalizeCategory(rule.pattern.claimCategory),
},
}));
}
}
catch {
// No local catalog yet — starter rules will provide base coverage
}
// Merge: local rules take precedence over starter rules with same ID
const localIds = new Set(localRules.map(r => r.id));
const starterRulesFiltered = STARTER_RULES.filter(r => !localIds.has(r.id));
return [...localRules, ...starterRulesFiltered];
}
/**
* Save all learned rules to the project's catalog.
*/
export async function saveRules(projectPath, rules) {
const dir = join(projectPath, '.assay', 'learned');
await mkdir(dir, { recursive: true });
await writeFile(rulesPath(projectPath), JSON.stringify(rules, null, 2));
await saveStats(projectPath, computeStats(rules));
}
/**
* Add a new rule to the catalog.
* Returns the updated rule list.
*/
export async function addRule(projectPath, rule) {
const rules = await loadRules(projectPath);
// Check for duplicate patterns
const isDuplicate = rules.some(r => r.pattern.regexPattern === rule.pattern.regexPattern &&
r.pattern.claimCategory === rule.pattern.claimCategory &&
r.status !== 'rejected' && r.status !== 'deprecated');
if (isDuplicate) {
return rules; // Don't add duplicates
}
rules.push(rule);
await saveRules(projectPath, rules);
return rules;
}
/**
* Update a rule in the catalog by ID.
*/
export async function updateRule(projectPath, ruleId, updater) {
const rules = await loadRules(projectPath);
const index = rules.findIndex(r => r.id === ruleId);
if (index === -1)
return null;
rules[index] = updater(rules[index]);
await saveRules(projectPath, rules);
return rules[index];
}
/**
* Get all promoted rules (ready to run in the formal verifier).
*/
export async function getPromotedRules(projectPath) {
const rules = await loadRules(projectPath);
return rules.filter(r => r.status === 'promoted');
}
/**
* Get all candidate rules (awaiting validation).
*/
export async function getCandidateRules(projectPath) {
const rules = await loadRules(projectPath);
return rules.filter(r => r.status === 'candidate');
}
/**
* Get rules by category.
*/
export async function getRulesByCategory(projectPath, category) {
const rules = await loadRules(projectPath);
return rules.filter(r => r.pattern.claimCategory === category);
}
// ── Statistics ───────────────────────────────────────────────
function computeStats(rules) {
const byStatus = {};
const byCategory = {};
const bySeverity = {};
let totalFires = 0;
let totalTP = 0;
let totalFP = 0;
for (const rule of rules) {
byStatus[rule.status] = (byStatus[rule.status] ?? 0) + 1;
byCategory[rule.pattern.claimCategory] = (byCategory[rule.pattern.claimCategory] ?? 0) + 1;
bySeverity[rule.pattern.severity] = (bySeverity[rule.pattern.severity] ?? 0) + 1;
totalFires += rule.fireCount;
totalTP += rule.truePositiveCount;
totalFP += rule.falsePositiveCount;
}
return {
totalRules: rules.length,
byStatus: byStatus,
byCategory,
bySeverity,
totalFires,
totalTruePositives: totalTP,
totalFalsePositives: totalFP,
overallPrecision: totalTP + totalFP > 0 ? totalTP / (totalTP + totalFP) : 1,
lastUpdated: new Date().toISOString(),
};
}
async function saveStats(projectPath, stats) {
const dir = join(projectPath, '.assay', 'learned');
await mkdir(dir, { recursive: true });
await writeFile(statsPath(projectPath), JSON.stringify(stats, null, 2));
}
/**
* Load catalog statistics.
*/
export async function loadStats(projectPath) {
try {
const content = await readFile(statsPath(projectPath), 'utf-8');
return JSON.parse(content);
}
catch {
return null;
}
}
//# sourceMappingURL=learned-catalog.js.map
{"version":3,"file":"learned-catalog.js","sourceRoot":"","sources":["../../../src/lib/learned-rules/learned-catalog.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AAC9D,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,OAAO,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AACtD,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAgBrD,gEAAgE;AAEhE,SAAS,SAAS,CAAC,WAAmB;IACpC,OAAO,IAAI,CAAC,WAAW,EAAE,QAAQ,EAAE,SAAS,EAAE,YAAY,CAAC,CAAC;AAC9D,CAAC;AAED,SAAS,SAAS,CAAC,WAAmB;IACpC,OAAO,IAAI,CAAC,WAAW,EAAE,QAAQ,EAAE,SAAS,EAAE,YAAY,CAAC,CAAC;AAC9D,CAAC;AAED,gEAAgE;AAEhE;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,WAAmB;IACjD,6BAA6B;IAC7B,IAAI,UAAU,GAAkB,EAAE,CAAC;IACnC,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,SAAS,CAAC,WAAW,CAAC,EAAE,OAAO,CAAC,CAAC;QAChE,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QACjC,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;YACxB,UAAU,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,IAAiB,EAAE,EAAE,CAAC,CAAC;gBAC5C,GAAG,IAAI;gBACP,OAAO,EAAE;oBACP,GAAG,IAAI,CAAC,OAAO;oBACf,aAAa,EAAE,iBAAiB,CAAC,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC;iBAC7D;aACF,CAAC,CAAC,CAAC;QACN,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,kEAAkE;IACpE,CAAC;IAED,qEAAqE;IACrE,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IACpD,MAAM,oBAAoB,GAAG,aAAa,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IAE5E,OAAO,CAAC,GAAG,UAAU,EAAE,GAAG,oBAAoB,CAAC,CAAC;AAClD,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,SAAS,CAC7B,WAAmB,EACnB,KAAoB;IAEpB,MAAM,GAAG,GAAG,IAAI,CAAC,WAAW,EAAE,QAAQ,EAAE,SAAS,CAAC,CAAC;IACnD,MAAM,KAAK,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACtC,MAAM,SAAS,CAAC,SAAS,CAAC,WAAW,CAAC,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;IACxE,MAAM,SAAS,CAAC,WAAW,EAAE,YAAY,CAAC,KAAK,CAAC,CAAC,CAAC;AACpD,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,OAAO,CAC3B,WAAmB,EACnB,IAAiB;IAEjB,MAAM,KAAK,GAAG,MAAM,SAAS,CAAC,WAAW,CAAC,CAAC;IAE3C,+BAA+B;IAC/B,MAAM,WAAW,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CACjC,CAAC,CAAC,OAAO,CAAC,YAAY,KAAK,IAAI,CAAC,OAAO,CAAC,YAAY;QACpD,CAAC,CAAC,OAAO,CAAC,aAAa,KAAK,IAAI,CAAC,OAAO,CAAC,aAAa;QACtD,CAAC,CAAC,MAAM,KAAK,UAAU,IAAI,CAAC,CAAC,MAAM,KAAK,YAAY,CACrD,CAAC;IAEF,IAAI,WAAW,EAAE,CAAC;QAChB,OAAO,KAAK,CAAC,CAAC,uBAAuB;IACvC,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACjB,MAAM,SAAS,CAAC,WAAW,EAAE,KAAK,CAAC,CAAC;IACpC,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,UAAU,CAC9B,WAAmB,EACnB,MAAc,EACd,OAA2C;IAE3C,MAAM,KAAK,GAAG,MAAM,SAAS,CAAC,WAAW,CAAC,CAAC;IAC3C,MAAM,KAAK,GAAG,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,MAAM,CAAC,CAAC;IACpD,IAAI,KAAK,KAAK,CAAC,CAAC;QAAE,OAAO,IAAI,CAAC;IAE9B,KAAK,CAAC,KAAK,CAAC,GAAG,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC;IACrC,MAAM,SAAS,CAAC,WAAW,EAAE,KAAK,CAAC,CAAC;IACpC,OAAO,KAAK,CAAC,KAAK,CAAC,CAAC;AACtB,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAC,WAAmB;IACxD,MAAM,KAAK,GAAG,MAAM,SAAS,CAAC,WAAW,CAAC,CAAC;IAC3C,OAAO,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,UAAU,CAAC,CAAC;AACpD,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB,CAAC,WAAmB;IACzD,MAAM,KAAK,GAAG,MAAM,SAAS,CAAC,WAAW,CAAC,CAAC;IAC3C,OAAO,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,WAAW,CAAC,CAAC;AACrD,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACtC,WAAmB,EACnB,QAAgB;IAEhB,MAAM,KAAK,GAAG,MAAM,SAAS,CAAC,WAAW,CAAC,CAAC;IAC3C,OAAO,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,aAAa,KAAK,QAAQ,CAAC,CAAC;AACjE,CAAC;AAED,gEAAgE;AAEhE,SAAS,YAAY,CAAC,KAAoB;IACxC,MAAM,QAAQ,GAA2B,EAAE,CAAC;IAC5C,MAAM,UAAU,GAA2B,EAAE,CAAC;IAC9C,MAAM,UAAU,GAA2B,EAAE,CAAC;IAC9C,IAAI,UAAU,GAAG,CAAC,CAAC;IACnB,IAAI,OAAO,GAAG,CAAC,CAAC;IAChB,IAAI,OAAO,GAAG,CAAC,CAAC;IAEhB,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC;QACzD,UAAU,CAAC,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,GAAG,CAAC,UAAU,CAAC,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC;QAC3F,UAAU,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC,UAAU,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC;QACjF,UAAU,IAAI,IAAI,CAAC,SAAS,CAAC;QAC7B,OAAO,IAAI,IAAI,CAAC,iBAAiB,CAAC;QAClC,OAAO,IAAI,IAAI,CAAC,kBAAkB,CAAC;IACrC,CAAC;IAED,OAAO;QACL,UAAU,EAAE,KAAK,CAAC,MAAM;QACxB,QAAQ,EAAE,QAA6C;QACvD,UAAU;QACV,UAAU;QACV,UAAU;QACV,kBAAkB,EAAE,OAAO;QAC3B,mBAAmB,EAAE,OAAO;QAC5B,gBAAgB,EAAE,OAAO,GAAG,OAAO,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,GAAG,CAAC,OAAO,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC;QAC3E,WAAW,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;KACtC,CAAC;AACJ,CAAC;AAED,KAAK,UAAU,SAAS,CAAC,WAAmB,EAAE,KAAmB;IAC/D,MAAM,GAAG,GAAG,IAAI,CAAC,WAAW,EAAE,QAAQ,EAAE,SAAS,CAAC,CAAC;IACnD,MAAM,KAAK,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACtC,MAAM,SAAS,CAAC,SAAS,CAAC,WAAW,CAAC,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;AAC1E,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,WAAmB;IACjD,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,SAAS,CAAC,WAAW,CAAC,EAAE,OAAO,CAAC,CAAC;QAChE,OAAO,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IAC7B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC"}
/**
* Pattern Extractor — extracts generalizable detection patterns from confirmed LLM findings.
*
* This is the core of Phase 1: Self-Expanding Formal Verification.
* When the LLM finds a real bug and it's confirmed, this module
* analyzes the code and finding to extract a regex pattern that
* would catch the same class of bug deterministically.
*
* The extracted pattern becomes a formal rule — no LLM needed for
* future instances of the same bug class.
*/
import type { PatternExtractionInput, PatternExtractionResult } from './types.js';
/** Reset counter (for testing). */
export declare function resetPatternCounter(start?: number): void;
/**
* Extract a generalizable detection pattern from a confirmed LLM finding.
*
* @param input - The confirmed finding with code context.
* @returns The extraction result with pattern or failure reason.
*/
export declare function extractPattern(input: PatternExtractionInput): PatternExtractionResult;
/**
* Get the list of claim categories that have extraction strategies.
*/
export declare function getSupportedCategories(): string[];
/**
* Pattern Extractor — extracts generalizable detection patterns from confirmed LLM findings.
*
* This is the core of Phase 1: Self-Expanding Formal Verification.
* When the LLM finds a real bug and it's confirmed, this module
* analyzes the code and finding to extract a regex pattern that
* would catch the same class of bug deterministically.
*
* The extracted pattern becomes a formal rule — no LLM needed for
* future instances of the same bug class.
*/
// ── Language Helpers ──────────────────────────────────────────
function langGroup(language) {
const lang = language.toLowerCase();
if (['typescript', 'ts', 'tsx'].includes(lang))
return ['ts', 'tsx', 'typescript'];
if (['javascript', 'js', 'jsx'].includes(lang))
return ['js', 'jsx', 'javascript'];
if (['python', 'py'].includes(lang))
return ['py', 'python'];
if (['go', 'golang'].includes(lang))
return ['go', 'golang'];
return [lang];
}
function fileGlobForLang(language) {
const lang = language.toLowerCase();
if (['typescript', 'ts', 'tsx', 'javascript', 'js', 'jsx'].includes(lang)) {
return '**/*.{ts,tsx,js,jsx}';
}
if (['python', 'py'].includes(lang))
return '**/*.py';
if (['go', 'golang'].includes(lang))
return '**/*.go';
if (['rust', 'rs'].includes(lang))
return '**/*.rs';
if (['java'].includes(lang))
return '**/*.java';
return '**/*';
}
// ── ID Generation ────────────────────────────────────────────
let patternCounter = 0;
function nextPatternId() {
patternCounter++;
return `lp_${String(patternCounter).padStart(4, '0')}`;
}
/** Reset counter (for testing). */
export function resetPatternCounter(start = 0) {
patternCounter = start;
}
// ── Extraction Strategies ────────────────────────────────────
/**
* Strategy: Missing error handling.
* If the LLM found that error handling is missing, extract a pattern
* that checks for try/catch, .catch(), or error callbacks near
* async operations.
*/
const missingErrorHandling = {
categories: ['error-handling'],
extract(input) {
const { claim, code, language } = input;
const text = `${claim.description} ${claim.assertion}`.toLowerCase();
// Only handle "missing error handling" findings
if (!/(?:missing|no|lacks?|without|absent)\s+(?:error|exception)\s+(?:handling|catching|recovery)/i.test(text) &&
!/(?:does\s+not|doesn't)\s+(?:handle|catch)\s+(?:errors?|exceptions?|failures?)/i.test(text)) {
return null;
}
const lang = language.toLowerCase();
// Extract the specific operation that needs error handling
const asyncOpMatch = code.match(/(?:await\s+(\w+(?:\.\w+)*)\s*\()|(?:(\w+(?:\.\w+)*)\s*\(\s*\)\.then)/);
let pattern;
let description;
if (asyncOpMatch) {
const op = asyncOpMatch[1] || asyncOpMatch[2];
// Pattern: this specific async call without surrounding try/catch
pattern = `(?:await\\s+${escapeRegex(op)}\\s*\\()`;
description = `Async call to '${op}' without error handling`;
}
else if (['ts', 'tsx', 'typescript', 'js', 'jsx', 'javascript'].includes(lang)) {
// Generic: any fetch/API call without error handling nearby
pattern = '\\bfetch\\s*\\([^)]*\\)(?![\\s\\S]{0,200}(?:\\.catch|try\\s*\\{|catch\\s*\\())';
description = 'Fetch call without error handling within 200 characters';
}
else if (['py', 'python'].includes(lang)) {
pattern = '(?:requests\\.(?:get|post|put|delete|patch)\\s*\\()(?![\\s\\S]{0,200}(?:except|try\\s*:))';
description = 'HTTP request without error handling within 200 characters';
}
else {
return null; // Can't generalize for this language
}
return {
id: nextPatternId(),
description,
kind: 'regex',
languages: langGroup(language),
regexPattern: pattern,
fileGlob: fileGlobForLang(language),
matchBehavior: 'presence_is_bad',
claimCategory: 'error-handling',
severity: claim.severity === 'low' ? 'medium' : claim.severity,
evidenceTemplate: `Missing error handling: {match} in {file}`,
};
},
};
/**
* Strategy: SQL injection via string concatenation.
* Extracts patterns for detecting SQL built from string concatenation.
*/
const sqlInjection = {
categories: ['security'],
extract(input) {
const { claim, language } = input;
const text = `${claim.description} ${claim.assertion}`.toLowerCase();
if (!/sql\s+injection|string\s+concatenat.*sql|unsanitized.*sql|sql.*interpolat/i.test(text)) {
return null;
}
const lang = language.toLowerCase();
let pattern;
if (['ts', 'tsx', 'typescript', 'js', 'jsx', 'javascript'].includes(lang)) {
// Template literals with SQL keywords and interpolation
pattern = '`[^`]*(?:SELECT|INSERT|UPDATE|DELETE|DROP|ALTER)\\s[^`]*\\$\\{(?!\\d)';
}
else if (['py', 'python'].includes(lang)) {
// f-strings or .format() with SQL
pattern = '(?:f[\'"][^\'"]*(?:SELECT|INSERT|UPDATE|DELETE)|\\.format\\s*\\([^)]*\\).*(?:SELECT|INSERT|UPDATE|DELETE))';
}
else {
// Generic string concat with SQL
pattern = '(?:SELECT|INSERT|UPDATE|DELETE|DROP)\\s[^"\']*\\+\\s*\\w+';
}
return {
id: nextPatternId(),
description: 'SQL query built with string interpolation/concatenation',
kind: 'regex',
languages: langGroup(language),
regexPattern: pattern,
fileGlob: fileGlobForLang(language),
matchBehavior: 'presence_is_bad',
claimCategory: 'security',
severity: 'critical',
evidenceTemplate: `SQL injection risk: {match} in {file}`,
};
},
};
/**
* Strategy: Missing null/undefined check.
* When LLM finds that a function doesn't validate its inputs for null.
*/
const missingNullCheck = {
categories: ['edge-case', 'correctness'],
extract(input) {
const { claim, code, language } = input;
const text = `${claim.description} ${claim.assertion}`.toLowerCase();
if (!/(?:null|undefined|nil|none)\s+(?:check|guard|validation|handling)/i.test(text) &&
!/(?:does\s+not|doesn't)\s+(?:check|validate|guard|handle)\s+(?:null|undefined|nil|none)/i.test(text)) {
return null;
}
// Try to extract the specific function/variable name
const funcMatch = code.match(/(?:function|const|let|var)\s+(\w+)\s*(?:=\s*(?:async\s+)?)?(?:\([^)]*\)|=>|\{)/);
if (!funcMatch)
return null;
const funcName = funcMatch[1];
const lang = language.toLowerCase();
let pattern;
if (['ts', 'tsx', 'typescript', 'js', 'jsx', 'javascript'].includes(lang)) {
// Function that takes params but doesn't check for null/undefined
pattern = `(?:function\\s+${escapeRegex(funcName)}|(?:const|let|var)\\s+${escapeRegex(funcName)}\\s*=)\\s*(?:async\\s+)?\\([^)]+\\)[^{]*\\{(?![\\s\\S]{0,300}(?:!==?\\s*(?:null|undefined)|===?\\s*(?:null|undefined)|\\?\\.))`;
}
else {
return null;
}
return {
id: nextPatternId(),
description: `Function '${funcName}' does not validate inputs for null/undefined`,
kind: 'regex',
languages: langGroup(language),
regexPattern: pattern,
fileGlob: fileGlobForLang(language),
matchBehavior: 'presence_is_bad',
claimCategory: claim.category,
severity: claim.severity === 'low' ? 'medium' : claim.severity,
evidenceTemplate: `Missing null check in function '{match}' in {file}`,
};
},
};
/**
* Strategy: Hardcoded secrets/credentials.
* When LLM finds API keys, tokens, or passwords hardcoded in source.
*/
const hardcodedSecrets = {
categories: ['security'],
extract(input) {
const { claim } = input;
const text = `${claim.description} ${claim.assertion}`.toLowerCase();
if (!/(?:hardcod|embed|literal)\w*\s+(?:secret|key|token|password|credential|api.?key)/i.test(text) &&
!/(?:secret|key|token|password|credential|api.?key)\s+(?:hardcod|embed|literal)/i.test(text)) {
return null;
}
return {
id: nextPatternId(),
description: 'Hardcoded secret, API key, token, or password in source code',
kind: 'regex',
languages: [], // All languages
regexPattern: "(?:api[_-]?key|secret|password|token|auth)\\s*[:=]\\s*['\"`][A-Za-z0-9+/=_-]{20,}['\"`]",
fileGlob: '**/*.{ts,tsx,js,jsx,py,go,java,rs}',
matchBehavior: 'presence_is_bad',
claimCategory: 'security',
severity: 'critical',
evidenceTemplate: `Hardcoded secret found: {match} in {file}`,
};
},
};
/**
* Strategy: Missing input validation.
* When LLM finds that user input isn't validated before use.
*/
const missingInputValidation = {
categories: ['security', 'correctness'],
extract(input) {
const { claim, code, language } = input;
const text = `${claim.description} ${claim.assertion}`.toLowerCase();
if (!/(?:missing|no|lacks?|without)\s+(?:input|user|request)\s+validation/i.test(text) &&
!/(?:does\s+not|doesn't)\s+(?:validate|sanitize|check)\s+(?:input|user|request)/i.test(text)) {
return null;
}
const lang = language.toLowerCase();
let pattern;
if (['ts', 'tsx', 'typescript', 'js', 'jsx', 'javascript'].includes(lang)) {
// Express/Node: req.body used without validation
if (code.includes('req.body') || code.includes('req.params') || code.includes('req.query')) {
pattern = '(?:req\\.(?:body|params|query))(?![\\s\\S]{0,100}(?:validate|parse|schema|zod|joi|yup))';
}
else {
return null;
}
}
else if (['py', 'python'].includes(lang)) {
// Flask/Django: request data used without validation
pattern = '(?:request\\.(?:json|form|args|data))(?![\\s\\S]{0,100}(?:validate|schema|marshmallow|pydantic))';
}
else {
return null;
}
return {
id: nextPatternId(),
description: 'User input used without validation or sanitization',
kind: 'regex',
languages: langGroup(language),
regexPattern: pattern,
fileGlob: fileGlobForLang(language),
matchBehavior: 'presence_is_bad',
claimCategory: 'security',
severity: 'high',
evidenceTemplate: `Unvalidated input: {match} in {file}`,
};
},
};
/**
* Strategy: Unhandled promise rejection.
* When LLM finds async operations without await or .catch().
*/
const unhandledPromise = {
categories: ['error-handling', 'correctness'],
extract(input) {
const { claim, language } = input;
const text = `${claim.description} ${claim.assertion}`.toLowerCase();
if (!/(?:unhandled|floating|detached)\s+promise/i.test(text) &&
!/promise\s+(?:not\s+)?(?:awaited|handled|caught)/i.test(text)) {
return null;
}
const lang = language.toLowerCase();
if (!['ts', 'tsx', 'typescript', 'js', 'jsx', 'javascript'].includes(lang)) {
return null;
}
return {
id: nextPatternId(),
description: 'Promise-returning call without await or .catch()',
kind: 'regex',
languages: langGroup(language),
// Match function calls that likely return promises but aren't awaited
// Look for common async patterns without await
regexPattern: '(?<!await\\s)(?<!return\\s)\\b(?:fetch|axios\\.\\w+|\\w+\\.save|\\w+\\.delete|\\w+\\.update)\\s*\\([^)]*\\)(?!\\s*\\.(?:then|catch))',
fileGlob: fileGlobForLang(language),
matchBehavior: 'presence_is_bad',
claimCategory: 'error-handling',
severity: 'high',
evidenceTemplate: `Unhandled promise: {match} in {file}`,
};
},
};
// ── Registry ─────────────────────────────────────────────────
const STRATEGIES = [
missingErrorHandling,
sqlInjection,
missingNullCheck,
hardcodedSecrets,
missingInputValidation,
unhandledPromise,
];
// ── Helpers ──────────────────────────────────────────────────
function escapeRegex(str) {
return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
}
// ── Public API ───────────────────────────────────────────────
/**
* Extract a generalizable detection pattern from a confirmed LLM finding.
*
* @param input - The confirmed finding with code context.
* @returns The extraction result with pattern or failure reason.
*/
export function extractPattern(input) {
// Only process confirmed failures
if (input.verification.verdict !== 'FAIL') {
return {
success: false,
failureReason: `Verdict is '${input.verification.verdict}', not 'FAIL'. Only confirmed failures can generate rules.`,
};
}
// Try each strategy in order
for (const strategy of STRATEGIES) {
if (!strategy.categories.includes(input.claim.category))
continue;
const pattern = strategy.extract(input);
if (pattern) {
// Validate the regex compiles
try {
new RegExp(pattern.regexPattern);
}
catch {
continue; // Skip patterns that don't compile
}
return { success: true, pattern };
}
}
return {
success: false,
failureReason: `No extraction strategy matched claim category '${input.claim.category}' with description: ${input.claim.description}`,
};
}
/**
* Get the list of claim categories that have extraction strategies.
*/
export function getSupportedCategories() {
const categories = new Set();
for (const strategy of STRATEGIES) {
for (const cat of strategy.categories) {
categories.add(cat);
}
}
return [...categories];
}
//# sourceMappingURL=pattern-extractor.js.map
{"version":3,"file":"pattern-extractor.js","sourceRoot":"","sources":["../../../src/lib/learned-rules/pattern-extractor.ts"],"names":[],"mappings":"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"}
/**
* Rule Codifier — converts extracted patterns into executable formal checks.
*
* Takes an ExtractedPattern and produces a LearnedRule that can run
* alongside hand-crafted rules in the formal verifier.
*
* Current implementation: regex-based rules only.
* Future: AST patterns (tree-sitter), type constraints (ts-morph).
*/
import type { ExtractedPattern, LearnedRule, PatternExtractionInput } from './types.js';
/** Reset counter (for testing). */
export declare function resetRuleCounter(start?: number): void;
/**
* Create a new learned rule from an extracted pattern and its source finding.
*
* The rule starts in 'candidate' status and must pass validation
* before being promoted to 'validated' and then 'promoted'.
*/
export declare function codifyRule(pattern: ExtractedPattern, input: PatternExtractionInput): LearnedRule;
/**
* Execute a learned rule against a code string.
*
* Returns true if the rule fires (detects a potential issue).
*/
export declare function executeRule(rule: LearnedRule, code: string, language: string): {
fires: boolean;
matches: string[];
};
/**
* Merge a new source finding into an existing rule.
* This happens when the same pattern is extracted from a different finding.
*/
export declare function mergeSourceFinding(rule: LearnedRule, input: PatternExtractionInput): LearnedRule;
/**
* Update rule status through the lifecycle.
*/
export declare function updateRuleStatus(rule: LearnedRule, status: LearnedRule['status']): LearnedRule;
/**
* Record that a rule fired and was confirmed/rejected by a human or auto-confirmation.
*/
export declare function recordRuleFire(rule: LearnedRule, wasCorrect: boolean): LearnedRule;
/**
* Rule Codifier — converts extracted patterns into executable formal checks.
*
* Takes an ExtractedPattern and produces a LearnedRule that can run
* alongside hand-crafted rules in the formal verifier.
*
* Current implementation: regex-based rules only.
* Future: AST patterns (tree-sitter), type constraints (ts-morph).
*/
// ── Rule ID Generation ───────────────────────────────────────
let ruleCounter = 0;
function nextRuleId() {
ruleCounter++;
return `lr_${String(ruleCounter).padStart(4, '0')}`;
}
/** Reset counter (for testing). */
export function resetRuleCounter(start = 0) {
ruleCounter = start;
}
// ── Rule Creation ────────────────────────────────────────────
/**
* Create a new learned rule from an extracted pattern and its source finding.
*
* The rule starts in 'candidate' status and must pass validation
* before being promoted to 'validated' and then 'promoted'.
*/
export function codifyRule(pattern, input) {
const now = new Date().toISOString();
const sourceFinding = {
claimId: input.claim.id,
claimDescription: input.claim.description,
codeSnippet: truncateCode(input.code, 500),
filePath: input.filePath,
language: input.language,
timestamp: now,
};
return {
id: nextRuleId(),
pattern,
status: 'candidate',
createdAt: now,
updatedAt: now,
fireCount: 0,
truePositiveCount: 0,
falsePositiveCount: 0,
sourceFindings: [sourceFinding],
};
}
/**
* Execute a learned rule against a code string.
*
* Returns true if the rule fires (detects a potential issue).
*/
export function executeRule(rule, code, language) {
const pattern = rule.pattern;
// Check language applicability
if (pattern.languages.length > 0) {
const langLower = language.toLowerCase();
if (!pattern.languages.some(l => l.toLowerCase() === langLower)) {
return { fires: false, matches: [] };
}
}
// Currently only regex patterns are supported
if (pattern.kind !== 'regex' || !pattern.regexPattern) {
return { fires: false, matches: [] };
}
try {
const regex = new RegExp(pattern.regexPattern, 'gi');
const matches = [];
let match;
while ((match = regex.exec(code)) !== null) {
matches.push(match[0]);
// Prevent infinite loops on zero-length matches
if (match[0].length === 0)
regex.lastIndex++;
}
if (pattern.matchBehavior === 'presence_is_bad') {
return { fires: matches.length > 0, matches };
}
else {
// absence_is_bad: fires when pattern is NOT found
return { fires: matches.length === 0, matches: [] };
}
}
catch {
// Invalid regex — rule should be rejected
return { fires: false, matches: [] };
}
}
/**
* Merge a new source finding into an existing rule.
* This happens when the same pattern is extracted from a different finding.
*/
export function mergeSourceFinding(rule, input) {
const now = new Date().toISOString();
const newFinding = {
claimId: input.claim.id,
claimDescription: input.claim.description,
codeSnippet: truncateCode(input.code, 500),
filePath: input.filePath,
language: input.language,
timestamp: now,
};
return {
...rule,
updatedAt: now,
sourceFindings: [...rule.sourceFindings, newFinding],
};
}
/**
* Update rule status through the lifecycle.
*/
export function updateRuleStatus(rule, status) {
return {
...rule,
status,
updatedAt: new Date().toISOString(),
};
}
/**
* Record that a rule fired and was confirmed/rejected by a human or auto-confirmation.
*/
export function recordRuleFire(rule, wasCorrect) {
return {
...rule,
fireCount: rule.fireCount + 1,
truePositiveCount: rule.truePositiveCount + (wasCorrect ? 1 : 0),
falsePositiveCount: rule.falsePositiveCount + (wasCorrect ? 0 : 1),
updatedAt: new Date().toISOString(),
};
}
// ── Helpers ──────────────────────────────────────────────────
function truncateCode(code, maxLength) {
if (code.length <= maxLength)
return code;
return code.slice(0, maxLength) + '\n// ... truncated';
}
//# sourceMappingURL=rule-codifier.js.map
{"version":3,"file":"rule-codifier.js","sourceRoot":"","sources":["../../../src/lib/learned-rules/rule-codifier.ts"],"names":[],"mappings":"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"}
/**
* Starter Catalog — ~15 high-signal learned rules that ship with the npm package.
*
* These rules were curated from 214 rules harvested from real PR diffs in
* popular open-source TypeScript projects. Selection criteria:
* - Valid, compilable regex
* - High or critical severity preferred
* - General-purpose (not tied to a specific project)
* - Good coverage across categories (security, error handling, etc.)
* - Useful fixDescription
*
* All starter rules have `source: "bundled"` to distinguish them from
* user-harvested rules. Local rules with the same ID take precedence.
*/
import type { LearnedRule } from './types.js';
export declare const STARTER_RULES: readonly LearnedRule[];
/**
* Starter Catalog — ~15 high-signal learned rules that ship with the npm package.
*
* These rules were curated from 214 rules harvested from real PR diffs in
* popular open-source TypeScript projects. Selection criteria:
* - Valid, compilable regex
* - High or critical severity preferred
* - General-purpose (not tied to a specific project)
* - Good coverage across categories (security, error handling, etc.)
* - Useful fixDescription
*
* All starter rules have `source: "bundled"` to distinguish them from
* user-harvested rules. Local rules with the same ID take precedence.
*/
const NOW = '2026-03-14T00:00:00.000Z';
export const STARTER_RULES = [
// ─── SECURITY ───────────────────────────────────────────────
{
id: 'starter_security_token_logging',
pattern: {
id: 'starter_security_token_logging',
description: 'Avoid logging sensitive data (like JWT tokens, access tokens, or API keys) directly to console. They can be intercepted by browser extensions, leaked in log aggregators, or exposed in CI output.',
kind: 'regex',
languages: ['typescript', 'javascript'],
regexPattern: '(?i)console\\.log.*\\{token|\\[access_token\\]|\\[JWT\\]',
fileGlob: '**/*.{ts,tsx,js,jsx}',
matchBehavior: 'presence_is_bad',
claimCategory: 'security',
severity: 'critical',
evidenceTemplate: '{file}:{line} logs sensitive token data: \'{match}\'. Remove or redact before shipping.',
},
status: 'promoted',
createdAt: NOW,
updatedAt: NOW,
fireCount: 0,
truePositiveCount: 0,
falsePositiveCount: 0,
sourceFindings: [],
source: 'bundled',
fixDescription: 'Remove console.log statements that print raw token values, or replace sensitive values with a placeholder like "[REDACTED]".',
confirmationCount: 4,
},
{
id: 'starter_security_webhook_ssrf',
pattern: {
id: 'starter_security_webhook_ssrf',
description: 'Webhook URLs should be validated against private/internal IP ranges before processing to prevent SSRF and data leakage through internal network access.',
kind: 'regex',
languages: ['typescript', 'javascript'],
regexPattern: '(?:await\\s+)?(?:\\w+\\.)*(?:create|update|send).*webhook',
fileGlob: '**/*.{ts,tsx,js,jsx}',
matchBehavior: 'presence_is_bad',
claimCategory: 'security',
severity: 'high',
evidenceTemplate: '{file}:{line} performs webhook operation without visible private-URL validation: {match}',
},
status: 'promoted',
createdAt: NOW,
updatedAt: NOW,
fireCount: 0,
truePositiveCount: 0,
falsePositiveCount: 0,
sourceFindings: [],
source: 'bundled',
fixDescription: 'Add a validation check after URL extraction to ensure the target is not a private/loopback/link-local IP before performing webhook operations.',
confirmationCount: 1,
},
{
id: 'starter_security_hardcoded_credentials',
pattern: {
id: 'starter_security_hardcoded_credentials',
description: 'Hardcoded service account emails or credentials in source code should be replaced by environment variables to support multi-tenant deployments and prevent credential leakage.',
kind: 'regex',
languages: ['typescript', 'javascript'],
regexPattern: "^const [A-Z][A-Z_]*(EMAIL|PASSWORD|SECRET|TOKEN|KEY)\\s*=\\s*['\"][^'\"]+['\"];?$",
fileGlob: '**/*.{ts,tsx,js,jsx}',
matchBehavior: 'presence_is_bad',
claimCategory: 'security',
severity: 'high',
evidenceTemplate: '{file}:{line} contains hardcoded credential constant: {match}. Use environment variables instead.',
},
status: 'promoted',
createdAt: NOW,
updatedAt: NOW,
fireCount: 0,
truePositiveCount: 0,
falsePositiveCount: 0,
sourceFindings: [],
source: 'bundled',
fixDescription: 'Replace hardcoded credential constants with process.env lookups (e.g., process.env.SERVICE_ACCOUNT_EMAIL) with appropriate fallback handling.',
confirmationCount: 2,
},
// ─── CONCURRENCY ────────────────────────────────────────────
{
id: 'starter_concurrency_version_increment',
pattern: {
id: 'starter_concurrency_version_increment',
description: 'Application-side version increments (read-modify-write) introduce TOCTOU race conditions. Version counters should be managed atomically within the database query.',
kind: 'regex',
languages: ['typescript', 'javascript'],
regexPattern: 'version:\\s+.*\\.version\\s*\\+\\s*1',
fileGlob: '**/*.{ts,tsx,js,jsx}',
matchBehavior: 'presence_is_bad',
claimCategory: 'concurrency',
severity: 'high',
evidenceTemplate: '{file}:{line} has non-atomic version increment: {match}. Use db.raw() or .increment() inside the SQL query instead.',
},
status: 'promoted',
createdAt: NOW,
updatedAt: NOW,
fireCount: 0,
truePositiveCount: 0,
falsePositiveCount: 0,
sourceFindings: [],
source: 'bundled',
fixDescription: 'Replace application-side version arithmetic with an atomic database operation (e.g., db.raw("?? + 1", [...]) or .increment()) inside the SQL query.',
confirmationCount: 1,
},
// ─── ERROR HANDLING ─────────────────────────────────────────
{
id: 'starter_error_dynamic_import_unhandled',
pattern: {
id: 'starter_error_dynamic_import_unhandled',
description: 'Dynamic imports with .then() but no .catch() will silently fail, leaving the component in a null/undefined state that causes missing UI elements or runtime errors.',
kind: 'regex',
languages: ['typescript', 'javascript'],
regexPattern: '(import\\(.*\\)\\.then\\()\\s*\\([^)]+\\)',
fileGlob: '**/*.{ts,tsx,js,jsx}',
matchBehavior: 'presence_is_bad',
claimCategory: 'error-handling',
severity: 'high',
evidenceTemplate: '{file}:{line} has dynamic import without error handling: {match}. Add .catch() to handle module load failures.',
},
status: 'promoted',
createdAt: NOW,
updatedAt: NOW,
fireCount: 0,
truePositiveCount: 0,
falsePositiveCount: 0,
sourceFindings: [],
source: 'bundled',
fixDescription: 'Add a .catch() handler to the dynamic import that sets an error state and logs the failure. Ensure dependent logic handles the error state gracefully.',
confirmationCount: 1,
},
{
id: 'starter_error_void_import_no_catch',
pattern: {
id: 'starter_error_void_import_no_catch',
description: 'Fire-and-forget dynamic imports (void import("...").then()) silently swallow rejections, corrupting component state when the module fails to load.',
kind: 'regex',
languages: ['typescript', 'javascript'],
regexPattern: 'void import\\("([^"]+)"\\).then\\([^)]+\\);',
fileGlob: '**/*.{ts,tsx,js,jsx}',
matchBehavior: 'presence_is_bad',
claimCategory: 'error-handling',
severity: 'high',
evidenceTemplate: '{file}:{line} uses fire-and-forget dynamic import without error handling: {match}. If this rejects, the app enters a silent error state.',
},
status: 'promoted',
createdAt: NOW,
updatedAt: NOW,
fireCount: 0,
truePositiveCount: 0,
falsePositiveCount: 0,
sourceFindings: [],
source: 'bundled',
fixDescription: 'Wrap the dynamic import to catch errors, set a failure state flag, and ensure dependent logic falls back to raw data or a sensible default.',
confirmationCount: 1,
},
{
id: 'starter_error_fragile_nth_child_selector',
pattern: {
id: 'starter_error_fragile_nth_child_selector',
description: 'CSS selectors relying on DOM structure (like nth-child) in E2E tests break when UI layout changes. Use data-testid attributes for stable element targeting.',
kind: 'regex',
languages: ['typescript', 'javascript'],
regexPattern: 'page\\.locator\\([^)]*nth-child[^)]*\\)',
fileGlob: '**/*.{ts,tsx,js,jsx}',
matchBehavior: 'presence_is_bad',
claimCategory: 'test-robustness',
severity: 'high',
evidenceTemplate: '{file}:{line} uses fragile nth-child selector: {match}. Replace with getByTestId() for stable targeting.',
},
status: 'promoted',
createdAt: NOW,
updatedAt: NOW,
fireCount: 0,
truePositiveCount: 0,
falsePositiveCount: 0,
sourceFindings: [],
source: 'bundled',
fixDescription: 'Replace nth-child or text-based locators with getByTestId() targeting explicit data-testid attributes on the DOM elements.',
confirmationCount: 2,
},
{
id: 'starter_error_nested_transactions',
pattern: {
id: 'starter_error_nested_transactions',
description: 'Nested database transactions waste resources and can cause deadlocks. When an external transaction is already provided, use it directly instead of opening a new one.',
kind: 'regex',
languages: ['typescript', 'javascript'],
regexPattern: '(await\\s+[a-zA-Z_]+\\.transaction\\(\\)|\\.transaction\\(\\))\\s*;?\\s*(?!;)',
fileGlob: '**/*.{ts,tsx,js,jsx}',
matchBehavior: 'presence_is_bad',
claimCategory: 'error-handling',
severity: 'medium',
evidenceTemplate: '{file}:{line} opens a transaction that may be nested: {match}. Check if an external transaction is already in scope.',
},
status: 'promoted',
createdAt: NOW,
updatedAt: NOW,
fireCount: 0,
truePositiveCount: 0,
falsePositiveCount: 0,
sourceFindings: [],
source: 'bundled',
fixDescription: 'Check whether an external transaction (trx) is already available before opening a new one. Pass the existing transaction through instead of nesting.',
confirmationCount: 2,
},
{
id: 'starter_error_usestate_true_default',
pattern: {
id: 'starter_error_usestate_true_default',
description: 'Initializing a boolean useState to true without checking if dependent data exists causes unintended side effects (e.g., enabling a feature before its prerequisites are loaded).',
kind: 'regex',
languages: ['typescript', 'javascript'],
regexPattern: '^\\s*const \\[([^,]+,\\s*[^\\]]+)\\]\\s*=\\s*useState\\((?:(?!&&|&&&).)*true\\);',
fileGlob: '**/*.{ts,tsx,js,jsx}',
matchBehavior: 'presence_is_bad',
claimCategory: 'error-handling',
severity: 'high',
evidenceTemplate: '{file}:{line} initializes state to true without verifying dependent data: {match}. Consider defaulting to false and setting true only when prerequisites are confirmed.',
},
status: 'promoted',
createdAt: NOW,
updatedAt: NOW,
fireCount: 0,
truePositiveCount: 0,
falsePositiveCount: 0,
sourceFindings: [],
source: 'bundled',
fixDescription: 'Default the useState flag to false and set it to true only after confirming the dependent value (e.g., email, config) exists. Use a useEffect or derive the initial value from props.',
confirmationCount: 1,
},
// ─── TYPE SAFETY ────────────────────────────────────────────
{
id: 'starter_typesafety_truthy_array_guard',
pattern: {
id: 'starter_typesafety_truthy_array_guard',
description: 'Using `|| []` to guard against non-array inputs fails for empty objects `{}` (which are truthy). Use Array.isArray() for correct type narrowing before calling .map().',
kind: 'regex',
languages: ['typescript', 'javascript'],
regexPattern: '(\\w+)\\.map\\(.*\\)\\s*=>\\s*\\((\\w+)\\.map.*|\\1||\\[\\]\\)',
fileGlob: '**/*.{ts,tsx,js,jsx}',
matchBehavior: 'presence_is_bad',
claimCategory: 'type-safety',
severity: 'high',
evidenceTemplate: '{file}:{line} uses truthy guard instead of Array.isArray(): {match}. Empty objects {} will pass the truthy check and throw on .map().',
},
status: 'promoted',
createdAt: NOW,
updatedAt: NOW,
fireCount: 0,
truePositiveCount: 0,
falsePositiveCount: 0,
sourceFindings: [],
source: 'bundled',
fixDescription: 'Replace `value || []` with `Array.isArray(value) ? value : []` before calling .map() to correctly handle non-array truthy values like empty objects.',
confirmationCount: 1,
},
// ─── REACT PATTERNS ────────────────────────────────────────
{
id: 'starter_react_ref_mutation_during_render',
pattern: {
id: 'starter_react_ref_mutation_during_render',
description: 'Mutating Refs (.current.set(), .current.push()) directly during render violates React rules and causes inconsistent UI. Move mutations to useEffect hooks.',
kind: 'regex',
languages: ['typescript', 'javascript'],
regexPattern: '(\\s*const\\s+\\w+\\s*=\\s*[\\[\\(]\\w+\\.forEach.*?\\{[^}]*Ref\\.|\\.current\\.set\\(|\\.current\\.push\\()',
fileGlob: '**/*.{ts,tsx,js,jsx}',
matchBehavior: 'presence_is_bad',
claimCategory: 'react-best-practices',
severity: 'high',
evidenceTemplate: '{file}:{line} mutates a Ref during render: {match}. Wrap in useEffect to avoid inconsistent state.',
},
status: 'promoted',
createdAt: NOW,
updatedAt: NOW,
fireCount: 0,
truePositiveCount: 0,
falsePositiveCount: 0,
sourceFindings: [],
source: 'bundled',
fixDescription: 'Move Ref mutations (.current.set(), .current.push()) into a useEffect hook with appropriate dependency arrays.',
confirmationCount: 1,
},
// ─── VALIDATION ─────────────────────────────────────────────
{
id: 'starter_validation_form_submit_no_check',
pattern: {
id: 'starter_validation_form_submit_no_check',
description: 'Calling formRef.current.submit() or .reset() without checking form validity bypasses HTML5 validation constraints, allowing invalid data to be submitted.',
kind: 'regex',
languages: ['typescript', 'javascript'],
regexPattern: '\\s+formRef\\.current\\?\\.(submit|reset)\\(',
fileGlob: '**/*.{ts,tsx,js,jsx}',
matchBehavior: 'presence_is_bad',
claimCategory: 'validation',
severity: 'medium',
evidenceTemplate: '{file}:{line} submits form without validity check: {match}. Call formRef.current.checkValidity() first.',
},
status: 'promoted',
createdAt: NOW,
updatedAt: NOW,
fireCount: 0,
truePositiveCount: 0,
falsePositiveCount: 0,
sourceFindings: [],
source: 'bundled',
fixDescription: 'Guard manual form submission with a validity check: if (formRef.current?.checkValidity()) { formRef.current.submit(); }',
confirmationCount: 1,
},
// ─── LOGGING ────────────────────────────────────────────────
{
id: 'starter_logging_console_with_objects',
pattern: {
id: 'starter_logging_console_with_objects',
description: 'Direct console.error/log/warn calls with non-string arguments (objects, errors) bypass centralized logging infrastructure, causing inconsistent log formatting and missing observability.',
kind: 'regex',
languages: ['typescript', 'javascript'],
regexPattern: "(\\s)(?:console\\.error|console\\.log|console\\.info|console\\.warn)\\((?![\"'`])",
fileGlob: '**/*.{ts,tsx,js,jsx}',
matchBehavior: 'presence_is_bad',
claimCategory: 'logging',
severity: 'medium',
evidenceTemplate: '{file}:{line} uses direct console call with non-string arg: {match}. Use a structured logger instead.',
},
status: 'promoted',
createdAt: NOW,
updatedAt: NOW,
fireCount: 0,
truePositiveCount: 0,
falsePositiveCount: 0,
sourceFindings: [],
source: 'bundled',
fixDescription: 'Replace direct console.error/log/warn calls with your application\'s structured logger (e.g., logger.error(), winston, pino) for consistent formatting and observability.',
confirmationCount: 1,
},
// ─── NULL SAFETY ────────────────────────────────────────────
{
id: 'starter_null_promise_all_conditional',
pattern: {
id: 'starter_null_promise_all_conditional',
description: 'Promise.all() with conditional expressions that may resolve to null can silently pass null values to subsequent function calls. Assign results to intermediate variables and check for null before use.',
kind: 'regex',
languages: ['typescript', 'javascript'],
regexPattern: 'await Promise\\.all\\(\\[([\\w\\s:<>]+)\\?.*?:.*?null,',
fileGlob: '**/*.{ts,tsx,js,jsx}',
matchBehavior: 'presence_is_bad',
claimCategory: 'null-safety',
severity: 'high',
evidenceTemplate: '{file}:{line} has Promise.all with conditional null: {match}. Destructure results and null-check before use.',
},
status: 'promoted',
createdAt: NOW,
updatedAt: NOW,
fireCount: 0,
truePositiveCount: 0,
falsePositiveCount: 0,
sourceFindings: [],
source: 'bundled',
fixDescription: 'Destructure Promise.all results into named variables and explicitly check for null before passing to subsequent function calls.',
confirmationCount: 1,
},
// ─── INTERNATIONALIZATION ───────────────────────────────────
{
id: 'starter_i18n_suspense_hardcoded_text',
pattern: {
id: 'starter_i18n_suspense_hardcoded_text',
description: 'String literals inside Suspense fallback components or other UI elements should be wrapped with i18n translation functions (Trans, t()) to support internationalization.',
kind: 'regex',
languages: ['typescript', 'javascript'],
regexPattern: '<Suspense[^>]*fallback=[^>]*><div[^>]*>([^<]+)</div>',
fileGlob: '**/*.{ts,tsx,js,jsx}',
matchBehavior: 'presence_is_bad',
claimCategory: 'i18n',
severity: 'medium',
evidenceTemplate: '{file}:{line} has hardcoded text in Suspense fallback: {match}. Wrap with <Trans> or t() for i18n support.',
},
status: 'promoted',
createdAt: NOW,
updatedAt: NOW,
fireCount: 0,
truePositiveCount: 0,
falsePositiveCount: 0,
sourceFindings: [],
source: 'bundled',
fixDescription: 'Wrap hardcoded string literals in Suspense fallbacks and other UI elements with the i18n translation component (e.g., <Trans>) or function (e.g., t()).',
confirmationCount: 1,
},
];
//# sourceMappingURL=starter-catalog.js.map
{"version":3,"file":"starter-catalog.js","sourceRoot":"","sources":["../../../src/lib/learned-rules/starter-catalog.ts"],"names":[],"mappings":"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"}
/**
* Types for the self-expanding formal verification system (Phase 1).
*
* When the LLM verifier finds a bug and it's confirmed correct,
* the system extracts the pattern and codifies it as a deterministic rule.
* Future scans use the formal rule instead of the LLM.
*/
/** The kind of detection pattern extracted from a confirmed finding. */
export type PatternKind = 'regex' | 'ast_pattern' | 'type_constraint';
/** A generalizable pattern extracted from a confirmed LLM finding. */
export interface ExtractedPattern {
/** Unique pattern ID (e.g., "lp_001"). */
readonly id: string;
/** Human-readable description of what this pattern catches. */
readonly description: string;
/** The kind of detection used. */
readonly kind: PatternKind;
/** Languages this pattern applies to. */
readonly languages: readonly string[];
/** The regex pattern string for detection (kind='regex'). */
readonly regexPattern?: string;
/** Glob for which files to scan. */
readonly fileGlob: string;
/** Whether the pattern should be PRESENT (and it's bad) or ABSENT (and it's bad if present). */
readonly matchBehavior: 'presence_is_bad' | 'absence_is_bad';
/** The original claim category that triggered this pattern. */
readonly claimCategory: string;
/** Severity when this pattern fires. */
readonly severity: 'critical' | 'high' | 'medium';
/** Evidence template using {match}, {file}, etc. */
readonly evidenceTemplate: string;
}
/** Status lifecycle of a learned rule. */
export type LearnedRuleStatus = 'candidate' | 'validated' | 'promoted' | 'rejected' | 'deprecated';
/** A rule generated from a confirmed LLM finding. */
export interface LearnedRule {
/** Unique rule ID (e.g., "lr_001"). */
readonly id: string;
/** The pattern this rule detects. */
readonly pattern: ExtractedPattern;
/** Current lifecycle status. */
readonly status: LearnedRuleStatus;
/** When the rule was first created. */
readonly createdAt: string;
/** When the rule was last updated. */
readonly updatedAt: string;
/** How many times this rule has fired across all scans. */
readonly fireCount: number;
/** How many times a human confirmed a fire was correct. */
readonly truePositiveCount: number;
/** How many times a human marked a fire as false positive. */
readonly falsePositiveCount: number;
/** The original finding that spawned this rule. */
readonly sourceFindings: readonly SourceFinding[];
/** Validation results from the harness. */
readonly validationResults?: ValidationResult;
/** Whether this rule was discovered via code scanning, PR review mining, or ships bundled. */
readonly source?: 'scan' | 'pr' | 'bundled';
/** Human-readable description of how to fix the detected issue. */
readonly fixDescription?: string;
/** Regex replacement pattern for auto-fix suggestions. */
readonly fixPattern?: string;
/** Number of distinct PRs/repos where this pattern was independently confirmed. */
readonly confirmationCount?: number;
}
/** Reference to the original LLM finding that spawned a learned rule. */
export interface SourceFinding {
/** The claim ID from the original verification. */
readonly claimId: string;
/** Description of the claim. */
readonly claimDescription: string;
/** The code snippet that contained the bug. */
readonly codeSnippet: string;
/** The file path where the bug was found. */
readonly filePath: string;
/** The language of the code. */
readonly language: string;
/** When this finding was recorded. */
readonly timestamp: string;
}
/** Results from running a rule through the validation harness. */
export interface ValidationResult {
/** Whether the rule passed validation. */
readonly passed: boolean;
/** True positives: correctly flagged known-bad code. */
readonly truePositives: number;
/** False positives: incorrectly flagged known-good code. */
readonly falsePositives: number;
/** True negatives: correctly passed known-good code. */
readonly trueNegatives: number;
/** False negatives: missed known-bad code. */
readonly falseNegatives: number;
/** Precision = TP / (TP + FP). */
readonly precision: number;
/** Recall = TP / (TP + FN). */
readonly recall: number;
/** Validation timestamp. */
readonly validatedAt: string;
/** Synthetic test cases used. */
readonly testCases: readonly TestCase[];
}
/** A test case for validating a learned rule. */
export interface TestCase {
/** Description of what this test case checks. */
readonly description: string;
/** The code to test. */
readonly code: string;
/** Whether this code SHOULD trigger the rule. */
readonly shouldMatch: boolean;
/** Whether the rule DID match. */
readonly didMatch: boolean;
}
/** Input to the pattern extractor. */
export interface PatternExtractionInput {
/** The verified claim (LLM found a real bug). */
readonly claim: {
readonly id: string;
readonly category: string;
readonly severity: 'critical' | 'high' | 'medium' | 'low';
readonly description: string;
readonly assertion: string;
};
/** The verification result (confirmed FAIL). */
readonly verification: {
readonly verdict: 'FAIL';
readonly reasoning: string;
readonly evidence?: string;
};
/** The code that was verified. */
readonly code: string;
/** The language of the code. */
readonly language: string;
/** The file path of the code. */
readonly filePath: string;
}
/** Output of the pattern extractor. */
export interface PatternExtractionResult {
/** Whether extraction succeeded. */
readonly success: boolean;
/** The extracted pattern, if successful. */
readonly pattern?: ExtractedPattern;
/** Why extraction failed, if it did. */
readonly failureReason?: string;
}
/** A review comment from a PR code review. */
export interface PRReviewComment {
readonly body: string;
readonly path: string;
readonly line: number | null;
}
/** A merged PR discovered for rule mining. */
export interface DiscoveredPR {
readonly repo: string;
readonly prNumber: number;
readonly title: string;
readonly labels: string[];
readonly mergeCommit: string;
readonly reviewComments: PRReviewComment[];
readonly files: string[];
}
/** A single hunk from a parsed PR diff. */
export interface DiffHunk {
readonly file: string;
readonly removedLines: string[];
readonly addedLines: string[];
readonly context: string[];
readonly startLine: number;
}
/** A PR with its parsed diff hunks. */
export interface ParsedPRDiff {
readonly pr: DiscoveredPR;
readonly hunks: DiffHunk[];
}
/** A generalized rule extracted from PR review patterns. */
export interface GeneralizedRule {
readonly category: string;
readonly severity: 'low' | 'medium' | 'high' | 'critical';
readonly description: string;
readonly detection: {
readonly pattern: string;
readonly language: string;
};
readonly fix: {
readonly description: string;
readonly pattern: string;
};
readonly fileGlob: string;
readonly matchBehavior: 'presence_is_bad' | 'absence_is_bad';
readonly evidenceTemplate: string;
readonly provenance: {
readonly repo: string;
readonly pr: number;
readonly file: string;
readonly reviewComment?: string;
};
}
/**
* Types for the self-expanding formal verification system (Phase 1).
*
* When the LLM verifier finds a bug and it's confirmed correct,
* the system extracts the pattern and codifies it as a deterministic rule.
* Future scans use the formal rule instead of the LLM.
*/
export {};
//# sourceMappingURL=types.js.map
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/lib/learned-rules/types.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG"}
/**
* Validation Harness — tests learned rules before promotion to the formal catalog.
*
* Every auto-generated rule must pass validation:
* 1. Fires correctly on the ORIGINAL code that triggered it
* 2. Fires correctly on synthetic "known-bad" variations
* 3. Does NOT fire on synthetic "known-good" variations
*
* Only rules with precision >= 0.8 and recall >= 0.5 are promoted.
*/
import type { LearnedRule, ValidationResult } from './types.js';
/**
* Validate a learned rule against synthetic test cases.
*
* @param rule - The candidate rule to validate.
* @returns The validation result with precision/recall metrics.
*/
export declare function validateRule(rule: LearnedRule): ValidationResult;
/**
* Get the validation thresholds.
*/
export declare function getValidationThresholds(): {
minPrecision: number;
minRecall: number;
minTestCases: number;
};
/**
* Validation Harness — tests learned rules before promotion to the formal catalog.
*
* Every auto-generated rule must pass validation:
* 1. Fires correctly on the ORIGINAL code that triggered it
* 2. Fires correctly on synthetic "known-bad" variations
* 3. Does NOT fire on synthetic "known-good" variations
*
* Only rules with precision >= 0.8 and recall >= 0.5 are promoted.
*/
import { executeRule } from './rule-codifier.js';
// ── Thresholds ───────────────────────────────────────────────
const MIN_PRECISION = 0.8; // At most 20% false positive rate
const MIN_RECALL = 0.5; // Catch at least half the real bugs
const MIN_TEST_CASES = 3; // At least 3 test cases required
// ── Synthetic Test Case Generation ───────────────────────────
/**
* Generate synthetic test cases from the original source finding.
* Creates variations of the original code that should and shouldn't
* trigger the rule.
*/
function generateTestCases(rule) {
const cases = [];
const pattern = rule.pattern;
// Test 1: Original code should trigger the rule
for (const finding of rule.sourceFindings) {
cases.push({
description: `Original finding: ${finding.claimDescription}`,
code: finding.codeSnippet,
shouldMatch: pattern.matchBehavior === 'presence_is_bad',
didMatch: false, // Will be filled during validation
});
}
// Test 2: Generate "fixed" version that should NOT trigger
for (const finding of rule.sourceFindings) {
const fixedCode = generateFixedCode(finding.codeSnippet, pattern, finding.language);
if (fixedCode) {
cases.push({
description: `Fixed version of: ${finding.claimDescription}`,
code: fixedCode,
shouldMatch: false,
didMatch: false,
});
}
}
// Test 3: Generate additional "bad" variations
for (const finding of rule.sourceFindings) {
const variation = generateBadVariation(finding.codeSnippet, pattern, finding.language);
if (variation) {
cases.push({
description: `Bad variation of: ${finding.claimDescription}`,
code: variation,
shouldMatch: pattern.matchBehavior === 'presence_is_bad',
didMatch: false,
});
}
}
// Test 4: Known-good code that should never trigger
const safeCode = generateSafeCode(pattern, rule.sourceFindings[0]?.language ?? 'typescript');
if (safeCode) {
cases.push({
description: 'Known-good code (should not trigger)',
code: safeCode,
shouldMatch: false,
didMatch: false,
});
}
return cases;
}
/**
* Generate a "fixed" version of buggy code.
* Applies the obvious fix based on the pattern category.
*/
function generateFixedCode(code, pattern, language) {
const lang = language.toLowerCase();
const category = pattern.claimCategory;
if (category === 'error-handling') {
// Wrap in try/catch
if (['ts', 'tsx', 'typescript', 'js', 'jsx', 'javascript'].includes(lang)) {
return `try {\n${code}\n} catch (error) {\n console.error('Operation failed:', error);\n throw error;\n}`;
}
if (['py', 'python'].includes(lang)) {
const indented = code.split('\n').map(l => ' ' + l).join('\n');
return `try:\n${indented}\nexcept Exception as e:\n raise`;
}
}
if (category === 'security' && pattern.regexPattern?.includes('SELECT|INSERT|UPDATE|DELETE')) {
// Replace string interpolation with parameterized query
if (['ts', 'tsx', 'typescript', 'js', 'jsx', 'javascript'].includes(lang)) {
return code
.replace(/`([^`]*)\$\{(\w+)\}([^`]*)`/g, "'$1?' + ',$3'")
.replace(/(['"])([^'"]*)\1\s*\+\s*(\w+)/g, "'$2?'")
+ '\n// Parameters passed separately: [userId]';
}
}
if (category === 'edge-case' || category === 'correctness') {
// Add null check before the function body
if (['ts', 'tsx', 'typescript', 'js', 'jsx', 'javascript'].includes(lang)) {
const funcMatch = code.match(/(function\s+\w+\s*\([^)]*\)\s*\{)/);
if (funcMatch) {
return code.replace(funcMatch[1], funcMatch[1] + '\n if (arguments[0] === null || arguments[0] === undefined) throw new Error("Invalid input");');
}
}
}
return null;
}
/**
* Generate a "bad" variation of buggy code.
* Creates a similar but different instance of the same bug class.
*/
function generateBadVariation(code, pattern, language) {
const lang = language.toLowerCase();
if (pattern.claimCategory === 'error-handling') {
if (['ts', 'tsx', 'typescript', 'js', 'jsx', 'javascript'].includes(lang)) {
// Generate another async call without error handling
return `async function processData(input: unknown) {\n const result = await fetch('/api/data');\n return result.json();\n}`;
}
}
if (pattern.claimCategory === 'security') {
if (['ts', 'tsx', 'typescript', 'js', 'jsx', 'javascript'].includes(lang)) {
return 'const query = `SELECT * FROM users WHERE id = ${userId}`;';
}
}
return null;
}
/**
* Generate known-good code that should never trigger the rule.
*/
function generateSafeCode(pattern, language) {
const lang = language.toLowerCase();
if (pattern.claimCategory === 'error-handling') {
if (['ts', 'tsx', 'typescript', 'js', 'jsx', 'javascript'].includes(lang)) {
return `async function safeOp() {\n try {\n const result = await fetch('/api/data');\n return await result.json();\n } catch (error) {\n console.error('Failed:', error);\n throw error;\n }\n}`;
}
}
if (pattern.claimCategory === 'security') {
if (['ts', 'tsx', 'typescript', 'js', 'jsx', 'javascript'].includes(lang)) {
return "const result = await db.query('SELECT * FROM users WHERE id = ?', [userId]);";
}
}
if (pattern.claimCategory === 'edge-case' || pattern.claimCategory === 'correctness') {
if (['ts', 'tsx', 'typescript', 'js', 'jsx', 'javascript'].includes(lang)) {
return `function safeProcess(input: string | null) {\n if (input === null || input === undefined) {\n throw new Error('Input required');\n }\n return input.trim();\n}`;
}
}
return null;
}
// ── Validation Execution ─────────────────────────────────────
/**
* Validate a learned rule against synthetic test cases.
*
* @param rule - The candidate rule to validate.
* @returns The validation result with precision/recall metrics.
*/
export function validateRule(rule) {
const testCases = generateTestCases(rule);
// PR-sourced rules: if we have fewer synthetic cases but the regex fires on
// the original buggy code, accept it. The merged PR is the human confirmation.
if (testCases.length < MIN_TEST_CASES && rule.source === 'pr') {
return validatePRRule(rule, testCases);
}
if (testCases.length < MIN_TEST_CASES) {
return {
passed: false,
truePositives: 0,
falsePositives: 0,
trueNegatives: 0,
falseNegatives: 0,
precision: 0,
recall: 0,
validatedAt: new Date().toISOString(),
testCases,
};
}
// Run each test case
const language = rule.sourceFindings[0]?.language ?? 'typescript';
const executedCases = [];
let tp = 0, fp = 0, tn = 0, fn = 0;
for (const testCase of testCases) {
const { fires } = executeRule(rule, testCase.code, language);
const executed = {
...testCase,
didMatch: fires,
};
executedCases.push(executed);
if (testCase.shouldMatch && fires)
tp++;
else if (testCase.shouldMatch && !fires)
fn++;
else if (!testCase.shouldMatch && fires)
fp++;
else
tn++;
}
const precision = tp + fp > 0 ? tp / (tp + fp) : 0;
const recall = tp + fn > 0 ? tp / (tp + fn) : 0;
const passed = precision >= MIN_PRECISION && recall >= MIN_RECALL;
return {
passed,
truePositives: tp,
falsePositives: fp,
trueNegatives: tn,
falseNegatives: fn,
precision,
recall,
validatedAt: new Date().toISOString(),
testCases: executedCases,
};
}
/**
* Lightweight validation for PR-sourced rules.
* The merged PR is the human confirmation — we just need to verify
* the regex actually fires on the buggy code from the diff.
*/
function validatePRRule(rule, testCases) {
const language = rule.sourceFindings[0]?.language ?? 'typescript';
let firesOnOriginal = false;
// Test: does the regex fire on any source finding's code snippet?
for (const finding of rule.sourceFindings) {
if (!finding.codeSnippet || finding.codeSnippet.startsWith('PR #'))
continue;
const { fires } = executeRule(rule, finding.codeSnippet, language);
if (fires) {
firesOnOriginal = true;
break;
}
}
// Also check: does it compile as valid regex?
let regexValid = false;
try {
if (rule.pattern.regexPattern) {
new RegExp(rule.pattern.regexPattern, 'gi');
regexValid = true;
}
}
catch { /* invalid */ }
const passed = firesOnOriginal && regexValid;
return {
passed,
truePositives: firesOnOriginal ? 1 : 0,
falsePositives: 0,
trueNegatives: 0,
falseNegatives: firesOnOriginal ? 0 : 1,
precision: firesOnOriginal ? 1.0 : 0,
recall: firesOnOriginal ? 1.0 : 0,
validatedAt: new Date().toISOString(),
testCases,
};
}
/**
* Get the validation thresholds.
*/
export function getValidationThresholds() {
return {
minPrecision: MIN_PRECISION,
minRecall: MIN_RECALL,
minTestCases: MIN_TEST_CASES,
};
}
//# sourceMappingURL=validation-harness.js.map
{"version":3,"file":"validation-harness.js","sourceRoot":"","sources":["../../../src/lib/learned-rules/validation-harness.ts"],"names":[],"mappings":"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"}
import type { DiffHunk } from '../learned-rules/types.js';
/**
* Parse a GitHub PR file `patch` string (unified diff format) into typed hunks.
*/
export declare function parsePatch(filePath: string, patch: string): DiffHunk[];
/**
* Filter out noise hunks: import-only, comment-only, whitespace-only, <2 meaningful lines.
*/
export declare function filterHunks(hunks: DiffHunk[]): DiffHunk[];
/**
* Parse a GitHub PR file `patch` string (unified diff format) into typed hunks.
*/
export function parsePatch(filePath, patch) {
if (!patch)
return [];
const hunks = [];
const lines = patch.split('\n');
let currentHunk = null;
for (const line of lines) {
const headerMatch = line.match(/^@@ -(\d+)/);
if (headerMatch) {
if (currentHunk) {
hunks.push(makeHunk(filePath, currentHunk));
}
currentHunk = {
removed: [],
added: [],
context: [],
startLine: parseInt(headerMatch[1], 10),
};
continue;
}
if (!currentHunk)
continue;
if (line.startsWith('-')) {
currentHunk.removed.push(line.slice(1));
}
else if (line.startsWith('+')) {
currentHunk.added.push(line.slice(1));
}
else if (line.startsWith(' ')) {
currentHunk.context.push(line);
}
}
if (currentHunk) {
hunks.push(makeHunk(filePath, currentHunk));
}
return hunks;
}
function makeHunk(file, raw) {
return {
file,
removedLines: raw.removed,
addedLines: raw.added,
context: raw.context,
startLine: raw.startLine,
};
}
/**
* Filter out noise hunks: import-only, comment-only, whitespace-only, <2 meaningful lines.
*/
export function filterHunks(hunks) {
return hunks.filter((hunk) => {
const removed = hunk.removedLines;
const added = hunk.addedLines;
if (Math.max(removed.length, added.length) < 2)
return false;
const allImports = [...removed, ...added].every((l) => l.trim().startsWith('import ') || (l.trim().startsWith('export ') && l.includes('from')));
if (allImports)
return false;
const allComments = [...removed, ...added].every((l) => {
const trimmed = l.trim();
return trimmed.startsWith('//') || trimmed.startsWith('/*') || trimmed.startsWith('*') || trimmed === '';
});
if (allComments)
return false;
const removedNormalized = removed.map((l) => l.replace(/\s+/g, ' ').trim());
const addedNormalized = added.map((l) => l.replace(/\s+/g, ' ').trim());
if (removedNormalized.length === addedNormalized.length &&
removedNormalized.every((l, i) => l === addedNormalized[i])) {
return false;
}
return true;
});
}
//# sourceMappingURL=diff-parser.js.map
{"version":3,"file":"diff-parser.js","sourceRoot":"","sources":["../../../src/lib/rule-harvester/diff-parser.ts"],"names":[],"mappings":"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"}
export type FileCandidate = {
path: string;
lineCount: number;
};
export type SelectedFile = {
path: string;
reason: string;
priority: number;
};
export declare function selectFiles(candidates: FileCandidate[]): SelectedFile[];
const MAX_FILES = 20;
const MAX_LINE_COUNT = 800;
const SKIP_PATTERNS = [
/\.test\./,
/\.spec\./,
/__tests__\//,
/\.d\.ts$/,
/\.config\.(js|ts)$/,
/tsconfig/,
/package\.json$/,
/\/generated\//,
/node_modules/,
/tailwind\.config/,
/next\.config/,
/vite\.config/,
/jest\.config/,
/vitest\.config/,
/eslint\.config/,
/prettier\.config/,
/\.eslintrc/,
/\.prettierrc/,
];
function shouldSkip(path) {
return SKIP_PATTERNS.some((pattern) => pattern.test(path));
}
function assignPriority(path) {
if (/\/api\/|\/routes\/|\/middleware\//.test(path)) {
return { priority: 1, reason: 'API/route/middleware file' };
}
if (/\/auth/.test(path)) {
return { priority: 2, reason: 'Auth-related file' };
}
if (/\/db\/|\/database\/|\/models\/|\/entities\//.test(path)) {
return { priority: 3, reason: 'Database/model file' };
}
if (/\/services\/|\/handlers\/|\/controllers\//.test(path)) {
return { priority: 4, reason: 'Service/handler/controller file' };
}
if (/\/lib\/|\/utils\/|\/helpers\//.test(path)) {
return { priority: 5, reason: 'Library/utility/helper file' };
}
if (/\.ts$/.test(path)) {
return { priority: 6, reason: 'TypeScript file' };
}
if (/\.tsx$/.test(path)) {
return { priority: 7, reason: 'TypeScript React file' };
}
return { priority: 99, reason: 'Other file' };
}
export function selectFiles(candidates) {
const filtered = candidates.filter((c) => !shouldSkip(c.path) && c.lineCount <= MAX_LINE_COUNT);
const scored = filtered.map((c) => {
const { priority, reason } = assignPriority(c.path);
return { path: c.path, reason, priority };
});
scored.sort((a, b) => a.priority - b.priority);
return scored.slice(0, MAX_FILES);
}
//# sourceMappingURL=file-selector.js.map
{"version":3,"file":"file-selector.js","sourceRoot":"","sources":["../../../src/lib/rule-harvester/file-selector.ts"],"names":[],"mappings":"AAWA,MAAM,SAAS,GAAG,EAAE,CAAC;AACrB,MAAM,cAAc,GAAG,GAAG,CAAC;AAE3B,MAAM,aAAa,GAAa;IAC9B,UAAU;IACV,UAAU;IACV,aAAa;IACb,UAAU;IACV,oBAAoB;IACpB,UAAU;IACV,gBAAgB;IAChB,eAAe;IACf,cAAc;IACd,kBAAkB;IAClB,cAAc;IACd,cAAc;IACd,cAAc;IACd,gBAAgB;IAChB,gBAAgB;IAChB,kBAAkB;IAClB,YAAY;IACZ,cAAc;CACf,CAAC;AAEF,SAAS,UAAU,CAAC,IAAY;IAC9B,OAAO,aAAa,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;AAC7D,CAAC;AAED,SAAS,cAAc,CAAC,IAAY;IAClC,IAAI,mCAAmC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QACnD,OAAO,EAAE,QAAQ,EAAE,CAAC,EAAE,MAAM,EAAE,2BAA2B,EAAE,CAAC;IAC9D,CAAC;IACD,IAAI,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QACxB,OAAO,EAAE,QAAQ,EAAE,CAAC,EAAE,MAAM,EAAE,mBAAmB,EAAE,CAAC;IACtD,CAAC;IACD,IAAI,6CAA6C,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QAC7D,OAAO,EAAE,QAAQ,EAAE,CAAC,EAAE,MAAM,EAAE,qBAAqB,EAAE,CAAC;IACxD,CAAC;IACD,IAAI,2CAA2C,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QAC3D,OAAO,EAAE,QAAQ,EAAE,CAAC,EAAE,MAAM,EAAE,iCAAiC,EAAE,CAAC;IACpE,CAAC;IACD,IAAI,+BAA+B,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QAC/C,OAAO,EAAE,QAAQ,EAAE,CAAC,EAAE,MAAM,EAAE,6BAA6B,EAAE,CAAC;IAChE,CAAC;IACD,IAAI,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QACvB,OAAO,EAAE,QAAQ,EAAE,CAAC,EAAE,MAAM,EAAE,iBAAiB,EAAE,CAAC;IACpD,CAAC;IACD,IAAI,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QACxB,OAAO,EAAE,QAAQ,EAAE,CAAC,EAAE,MAAM,EAAE,uBAAuB,EAAE,CAAC;IAC1D,CAAC;IACD,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,YAAY,EAAE,CAAC;AAChD,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,UAA2B;IACrD,MAAM,QAAQ,GAAG,UAAU,CAAC,MAAM,CAChC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,SAAS,IAAI,cAAc,CAC5D,CAAC;IAEF,MAAM,MAAM,GAAmB,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE;QAChD,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,cAAc,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;QACpD,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC;IAC5C,CAAC,CAAC,CAAC;IAEH,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,GAAG,CAAC,CAAC,QAAQ,CAAC,CAAC;IAE/C,OAAO,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,SAAS,CAAC,CAAC;AACpC,CAAC"}
export interface ClaimForConfirmation {
claimId: string;
category: string;
description: string;
verdict: string;
}
export interface GroundTruthResult {
claimId: string;
confirmed: boolean;
method: 'compiler' | 'test' | 'grep';
evidence: string;
}
export declare function runTscCheck(repoDir: string): {
errors: string;
exitCode: number;
} | null;
export declare function tscReportsErrorInFile(tscOutput: string, filePath: string): boolean;
export declare function confirmByGrep(code: string, claim: ClaimForConfirmation): GroundTruthResult;
export declare function parseConfirmation(code: string, claim: ClaimForConfirmation, tscOutput: string | null, filePath: string): GroundTruthResult;
import { execSync } from 'node:child_process';
export function runTscCheck(repoDir) {
try {
execSync('npx tsc --noEmit 2>&1', {
cwd: repoDir,
timeout: 120_000,
encoding: 'utf-8',
});
return { errors: '', exitCode: 0 };
}
catch (err) {
const e = err;
if (e.status === undefined) {
return null;
}
const combined = ((e.stdout ?? '') + (e.stderr ?? '')).trim();
return { errors: combined, exitCode: e.status };
}
}
export function tscReportsErrorInFile(tscOutput, filePath) {
return tscOutput.includes(filePath);
}
export function confirmByGrep(code, claim) {
const cat = claim.category.toLowerCase();
const desc = claim.description.toLowerCase();
// error-handling: await without surrounding try/catch or .catch()
if (cat.includes('error-handling') ||
desc.includes('error handling') ||
desc.includes('try/catch')) {
const hasAwait = /\bawait\b/.test(code);
const hasTryCatch = /\btry\s*\{/.test(code);
const hasCatchChain = /\.catch\s*\(/.test(code);
const confirmed = hasAwait && !hasTryCatch && !hasCatchChain;
return {
claimId: claim.claimId,
confirmed,
method: 'grep',
evidence: confirmed
? 'Found await without try/catch or .catch()'
: 'await is wrapped in try/catch or .catch()',
};
}
// security + SQL injection: template literals with SQL keywords and ${
if ((cat.includes('security') &&
(desc.includes('sql') ||
desc.includes('injection') ||
desc.includes('query'))) ||
cat.includes('injection')) {
const sqlTemplatePattern = /`[^`]*(SELECT|INSERT|UPDATE|DELETE|FROM|WHERE)[^`]*\$\{/i;
const confirmed = sqlTemplatePattern.test(code);
return {
claimId: claim.claimId,
confirmed,
method: 'grep',
evidence: confirmed
? 'Found SQL template literal with ${} interpolation'
: 'No SQL injection pattern found',
};
}
// hardcoded secrets: key|secret|token|password|api_key = "long_string"
if (cat.includes('hardcoded') ||
cat.includes('secret') ||
desc.includes('hardcoded') ||
desc.includes('secret') ||
desc.includes('api key') ||
desc.includes('api_key')) {
const secretPattern = /\b(?:key|secret|token|password|api_key)\s*=\s*["'][^"']{8,}["']/i;
const confirmed = secretPattern.test(code);
return {
claimId: claim.claimId,
confirmed,
method: 'grep',
evidence: confirmed
? 'Found hardcoded secret assignment'
: 'No hardcoded secret pattern found',
};
}
// edge-case / null/undefined: nested property access without optional chaining or null checks
if (cat.includes('edge-case') ||
cat.includes('null') ||
cat.includes('undefined') ||
desc.includes('null') ||
desc.includes('undefined') ||
desc.includes('optional')) {
// Match a.b.c patterns where there's no ?. in the chain
const unsafeAccessPattern = /\b\w+\.\w+\.\w+/;
const safeAccessPattern = /\b\w+\??\.\w+\??\.\w+/;
const hasUnsafe = unsafeAccessPattern.test(code);
const hasSafe = /\?\.\w+/.test(code);
const confirmed = hasUnsafe && !hasSafe;
return {
claimId: claim.claimId,
confirmed,
method: 'grep',
evidence: confirmed
? 'Found nested property access without optional chaining'
: 'Optional chaining or null checks present',
};
}
// input validation: req.body/params/query without validation
if (cat.includes('input validation') ||
cat.includes('validation') ||
desc.includes('input validation') ||
desc.includes('req.body') ||
desc.includes('req.params') ||
desc.includes('req.query')) {
const hasDirectAccess = /req\.(body|params|query)/.test(code);
const hasValidation = /validate|schema|zod|yup|joi|\.parse\(|\.safeParse\(/.test(code);
const confirmed = hasDirectAccess && !hasValidation;
return {
claimId: claim.claimId,
confirmed,
method: 'grep',
evidence: confirmed
? 'Found req.body/params/query without validation'
: 'Validation present',
};
}
// unhandled promise: floating promise without await
if (cat.includes('unhandled promise') ||
cat.includes('promise') ||
desc.includes('unhandled promise') ||
desc.includes('floating promise')) {
// Look for function calls that return promises but are not awaited
const floatingPattern = /^\s+(?!.*await\s)\w+\s*\(.*\)\s*;/m;
const confirmed = floatingPattern.test(code);
return {
claimId: claim.claimId,
confirmed,
method: 'grep',
evidence: confirmed
? 'Found potential floating promise (unawaited call)'
: 'No floating promise pattern found',
};
}
return {
claimId: claim.claimId,
confirmed: false,
method: 'grep',
evidence: `No grep strategy for category "${claim.category}"`,
};
}
export function parseConfirmation(code, claim, tscOutput, filePath) {
if (tscOutput !== null &&
claim.category.toLowerCase().includes('type-safety') &&
tscReportsErrorInFile(tscOutput, filePath)) {
return {
claimId: claim.claimId,
confirmed: true,
method: 'compiler',
evidence: `tsc reported error in ${filePath}`,
};
}
return confirmByGrep(code, claim);
}
//# sourceMappingURL=ground-truth.js.map
{"version":3,"file":"ground-truth.js","sourceRoot":"","sources":["../../../src/lib/rule-harvester/ground-truth.ts"],"names":[],"mappings":"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"}
export interface HarvestConfig {
repoList: Array<{
owner: string;
name: string;
category: string;
}>;
/** Assay project root — where rules go */
catalogPath: string;
/** Where progress.json lives */
progressDir: string;
/** Where run reports go */
runsDir: string;
/** Temp dir for clones (e.g. /private/tmp/assay-harvester) */
workDir: string;
/** Model name for reporting */
model: string;
/** Max repos to scan */
limit?: number;
/** Only scan these repos (owner/name format) */
repoFilter?: string[];
/** Skip completed repos */
resume?: boolean;
/** Logging callback */
onLog?: (msg: string) => void;
}
export declare function harvestRepos(config: HarvestConfig): Promise<void>;
import { execSync } from 'node:child_process';
import { readFile, rm } from 'node:fs/promises';
import { readdirSync, readFileSync, statSync } from 'node:fs';
import { join, relative } from 'node:path';
import { selectFiles } from './file-selector.js';
import { scanFile } from './scanner.js';
import { parseConfirmation, runTscCheck } from './ground-truth.js';
import { loadProgress, saveProgress, getFileState, setFileState, initRepo, } from './progress.js';
import { createRunReport, saveRunReport } from './reporter.js';
import { learnFromFinding, getLearnedRulesSummary } from '../learned-rules/index.js';
// ── Helpers ───────────────────────────────────────────────────
function repoKey(repo) {
return `${repo.owner}/${repo.name}`;
}
function log(config, msg) {
if (config.onLog) {
config.onLog(msg);
}
else {
console.log(msg);
}
}
function cloneRepo(repo, targetDir) {
const url = `https://github.com/${repo.owner}/${repo.name}.git`;
try {
execSync(`git clone --depth 1 "${url}" "${targetDir}"`, {
timeout: 120_000,
stdio: 'pipe',
});
return true;
}
catch {
return false;
}
}
function installDeps(repoDir) {
try {
execSync('npm install --ignore-scripts', {
cwd: repoDir,
timeout: 180_000,
stdio: 'pipe',
});
return true;
}
catch {
return false;
}
}
function listTsFiles(dir, base) {
const root = base ?? dir;
const results = [];
const TS_EXTENSIONS = new Set(['.ts', '.tsx', '.js', '.jsx']);
const SKIP_DIRS = new Set(['node_modules', '.git', 'dist']);
let entries;
try {
entries = readdirSync(dir);
}
catch {
return results;
}
for (const entry of entries) {
if (SKIP_DIRS.has(entry))
continue;
const fullPath = join(dir, entry);
let stat;
try {
stat = statSync(fullPath);
}
catch {
continue;
}
if (stat.isDirectory()) {
results.push(...listTsFiles(fullPath, root));
}
else if (stat.isFile()) {
const ext = entry.slice(entry.lastIndexOf('.'));
if (!TS_EXTENSIONS.has(ext))
continue;
let content = '';
try {
content = readFileSync(fullPath, 'utf-8');
}
catch {
// If unreadable, estimate 0 lines
}
const lineCount = content ? content.split('\n').length : 0;
const relPath = relative(root, fullPath);
results.push({ path: relPath, lineCount });
}
}
return results;
}
// ── Main Pipeline ─────────────────────────────────────────────
export async function harvestRepos(config) {
const startTime = Date.now();
// 1. Load progress if resume=true
let progress = {};
if (config.resume) {
progress = await loadProgress(config.progressDir);
log(config, `Resumed progress: ${Object.keys(progress).length} repos tracked`);
}
// 2. Filter repos by repoFilter and limit
let repos = config.repoList;
if (config.repoFilter && config.repoFilter.length > 0) {
const filterSet = new Set(config.repoFilter);
repos = repos.filter((r) => filterSet.has(repoKey(r)));
}
if (config.limit !== undefined && config.limit > 0) {
repos = repos.slice(0, config.limit);
}
log(config, `Processing ${repos.length} repos`);
const allFileResults = [];
for (const repo of repos) {
const key = repoKey(repo);
// 3. Skip completed repos on resume
if (config.resume) {
const repoProgress = progress[key];
if (repoProgress?.status === 'complete') {
log(config, `[SKIP] ${key} — already complete`);
continue;
}
}
log(config, `[START] ${key}`);
const repoDir = join(config.workDir, repo.name);
// 4a. Clone
const cloneOk = cloneRepo(repo, repoDir);
if (!cloneOk) {
log(config, `[FAIL] ${key} — clone failed`);
continue;
}
log(config, `[CLONED] ${key}`);
// 4b. Install deps (best-effort)
const installOk = installDeps(repoDir);
if (!installOk) {
log(config, `[WARN] ${key} — npm install failed (continuing)`);
}
// 4c. Run tsc baseline
const tscResult = runTscCheck(repoDir);
const tscOutput = tscResult?.errors ?? null;
log(config, `[TSC] ${key} — exit ${tscResult?.exitCode ?? 'null'}, ${tscOutput?.length ?? 0} chars`);
// 4d. List all .ts/.tsx/.js/.jsx files
const allFiles = listTsFiles(repoDir);
log(config, `[FILES] ${key} — ${allFiles.length} files found`);
// 4e. Select files
const selectedFiles = selectFiles(allFiles);
log(config, `[SELECTED] ${key} — ${selectedFiles.length} files to scan`);
// Initialize repo in progress
progress = initRepo(progress, key);
// 4f. Scan each selected file
for (const selectedFile of selectedFiles) {
const absFilePath = join(repoDir, selectedFile.path);
const fileState = getFileState(progress, key, selectedFile.path);
// Skip already-learned/confirmed files on resume
if (config.resume && (fileState === 'learned' || fileState === 'confirmed')) {
log(config, ` [SKIP] ${selectedFile.path} — ${fileState}`);
continue;
}
const fileStart = Date.now();
const result = {
repo: key,
filePath: selectedFile.path,
claimsExtracted: 0,
confirmed: 0,
rulesLearned: 0,
rulesRejected: 0,
rulesDuplicate: 0,
unmatchedCategories: [],
durationMs: 0,
};
// Read file content
let code;
try {
code = await readFile(absFilePath, 'utf-8');
}
catch (err) {
log(config, ` [WARN] ${selectedFile.path} — unreadable: ${String(err)}`);
continue;
}
// Scan with timeout (clear timer on success to avoid accumulating pending timers)
let scanOutput = null;
let timer;
try {
const scanPromise = scanFile(code, 'typescript');
const timeoutPromise = new Promise((_, reject) => {
timer = setTimeout(() => reject(new Error('scanFile timeout')), 1_200_000);
});
scanOutput = await Promise.race([scanPromise, timeoutPromise]);
}
catch (err) {
log(config, ` [SCAN_ERR] ${selectedFile.path} — ${String(err)}`);
progress = setFileState(progress, key, selectedFile.path, 'scanned');
await saveProgress(config.progressDir, progress);
result.durationMs = Date.now() - fileStart;
allFileResults.push(result);
continue;
}
finally {
if (timer)
clearTimeout(timer);
}
result.claimsExtracted = scanOutput.result.totalClaims;
// Update progress to scanned
progress = setFileState(progress, key, selectedFile.path, 'scanned');
// For each FAIL/PARTIAL finding, confirm and learn
for (const failure of scanOutput.result.failures) {
const claimForConfirmation = {
claimId: failure.claimId,
category: failure.category,
description: failure.description,
verdict: 'FAIL',
};
const groundTruth = parseConfirmation(code, claimForConfirmation, tscOutput, selectedFile.path);
if (!groundTruth.confirmed)
continue;
result.confirmed += 1;
progress = setFileState(progress, key, selectedFile.path, 'confirmed');
// Coerce severity: low → medium (ExtractedPattern.severity doesn't include 'low')
const rawSeverity = failure.severity;
const coercedSeverity = rawSeverity === 'critical' || rawSeverity === 'high' || rawSeverity === 'medium'
? rawSeverity
: 'medium';
const extractionInput = {
claim: {
id: failure.claimId,
category: failure.category,
severity: coercedSeverity,
description: failure.description,
assertion: failure.assertion,
},
verification: {
verdict: 'FAIL',
reasoning: failure.reasoning,
},
code,
language: 'typescript',
filePath: selectedFile.path,
};
let learnResult;
try {
learnResult = await learnFromFinding(config.catalogPath, extractionInput);
}
catch (err) {
log(config, ` [LEARN_ERR] ${selectedFile.path}:${failure.claimId} — ${String(err)}`);
result.rulesRejected += 1;
continue;
}
if (learnResult.learned) {
result.rulesLearned += 1;
progress = setFileState(progress, key, selectedFile.path, 'learned');
}
else {
const reason = learnResult.reason ?? '';
if (reason.includes('No extraction strategy')) {
result.unmatchedCategories.push(failure.category);
}
else if (reason.includes('duplicate')) {
result.rulesDuplicate += 1;
}
else {
result.rulesRejected += 1;
}
}
}
result.durationMs = Date.now() - fileStart;
allFileResults.push(result);
// Save after each file (Ctrl+C safe)
await saveProgress(config.progressDir, progress);
log(config, ` [FILE] ${selectedFile.path} — ${result.claimsExtracted} claims, ${result.confirmed} confirmed, ${result.rulesLearned} learned (${result.durationMs}ms)`);
}
// 4g. Delete clone
try {
await rm(repoDir, { recursive: true, force: true });
log(config, `[CLEANED] ${key}`);
}
catch (err) {
log(config, `[WARN] ${key} — rm failed: ${String(err)}`);
}
log(config, `[DONE] ${key}`);
}
// 5. Generate and save run report
const catalogSummary = await getLearnedRulesSummary(config.catalogPath);
const catalogSize = {
total: catalogSummary.total,
promoted: catalogSummary.promoted,
rejected: catalogSummary.rejected,
};
const report = createRunReport(allFileResults, config.model, catalogSize);
const reportPath = await saveRunReport(config.runsDir, report);
// 6. Print summary
const totalDurationMin = ((Date.now() - startTime) / 60_000).toFixed(1);
log(config, '');
log(config, '══════════════════════════════════════');
log(config, ' HARVEST COMPLETE');
log(config, '══════════════════════════════════════');
log(config, ` Repos scanned: ${report.reposScanned}`);
log(config, ` Files scanned: ${report.filesScanned}`);
log(config, ` Claims extracted: ${report.claimsExtracted}`);
log(config, ` Findings confirmed: ${report.findingsConfirmed}`);
log(config, ` Rules learned: ${report.rulesLearned}`);
log(config, ` Rules rejected: ${report.rulesRejected}`);
log(config, ` Rules duplicate: ${report.rulesDuplicate}`);
log(config, ` Catalog total: ${catalogSize.total} (${catalogSize.promoted} promoted)`);
log(config, ` Duration: ${totalDurationMin}m`);
log(config, ` Report: ${reportPath}`);
log(config, '══════════════════════════════════════');
}
//# sourceMappingURL=harvest.js.map
{"version":3,"file":"harvest.js","sourceRoot":"","sources":["../../../src/lib/rule-harvester/harvest.ts"],"names":[],"mappings":"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"}
import type { DiscoveredPR, ParsedPRDiff } from '../learned-rules/types.js';
/** Default labels that indicate a bug-fix PR. Matched case-insensitively as substrings. */
export declare const DEFAULT_BUG_LABELS: readonly string[];
/**
* Returns true if any label in the array contains a bug-related keyword.
* Matches case-insensitively. Accepts optional custom bug label list.
*/
export declare function matchesBugLabels(labels: string[], bugLabels?: readonly string[]): boolean;
/**
* Returns true if the PR title matches common bug-fix prefixes.
* Looks for fix/bug/resolve/patch at the start or after a conventional commit prefix.
*/
export declare function matchesBugTitle(title: string): boolean;
/**
* Returns true if the file path is a TypeScript/JavaScript source file
* that is NOT a test file or declaration file.
*/
export declare function isTargetFile(path: string): boolean;
interface DiscoveryConfig {
/** Max PRs to fetch per page (GitHub max: 100). */
readonly perPage?: number;
/** Max pages to paginate through. */
readonly maxPages?: number;
/** Custom bug labels to match. Defaults to DEFAULT_BUG_LABELS. */
readonly bugLabels?: readonly string[];
/** Whether to fall back to title matching if no labels match. */
readonly titleFallback?: boolean;
/** Whether to fetch review comments for each PR. */
readonly fetchComments?: boolean;
}
/**
* Discover merged bug-fix PRs from a GitHub repo.
*
* Flow:
* 1. Fetch closed PRs (paginated)
* 2. Filter to merged-only (merged_at !== null)
* 3. Filter by bug labels (with optional title fallback)
* 4. Optionally enrich with review comments
* 5. Collect changed file paths
*/
export declare function discoverBugFixPRs(owner: string, name: string, config?: DiscoveryConfig): DiscoveredPR[];
/**
* Fetch and parse diffs for a discovered PR.
*
* Calls the files endpoint, filters to target files,
* and parses each file's patch into typed DiffHunks.
*/
export declare function fetchPRDiffs(owner: string, name: string, pr: DiscoveredPR): ParsedPRDiff;
export {};
import { execSync } from 'node:child_process';
import { parsePatch } from './diff-parser.js';
// ── Constants ──────────────────────────────────────────────────
/** Default labels that indicate a bug-fix PR. Matched case-insensitively as substrings. */
export const DEFAULT_BUG_LABELS = [
'bug',
'fix',
'bugfix',
'hotfix',
'security',
'patch',
'defect',
'regression',
];
/** File extensions we mine patterns from. */
const TARGET_EXTENSIONS = /\.(tsx?|jsx?|py)$/;
/** Paths that indicate test files — excluded from mining. */
const TEST_PATH_PATTERNS = /\.(test|spec)\.[tj]sx?$|__tests__[/\\]|tests?[/\\]|_test\.py$|test_[^/\\]+\.py$/;
/** Declaration files — excluded from mining. */
const DECLARATION_PATTERN = /\.d\.ts$|\.pyi$/;
// ── Pure Filter Functions (unit-testable) ──────────────────────
/**
* Returns true if any label in the array contains a bug-related keyword.
* Matches case-insensitively. Accepts optional custom bug label list.
*/
export function matchesBugLabels(labels, bugLabels = DEFAULT_BUG_LABELS) {
const lower = labels.map((l) => l.toLowerCase());
return lower.some((label) => bugLabels.some((keyword) => label.includes(keyword)));
}
/**
* Returns true if the PR title matches common bug-fix prefixes.
* Looks for fix/bug/resolve/patch at the start or after a conventional commit prefix.
*/
export function matchesBugTitle(title) {
return /\b(fix|bug|resolve|hotfix|patch)\b/i.test(title);
}
/**
* Returns true if the file path is a TypeScript/JavaScript source file
* that is NOT a test file or declaration file.
*/
export function isTargetFile(path) {
if (!TARGET_EXTENSIONS.test(path))
return false;
if (TEST_PATH_PATTERNS.test(path))
return false;
if (DECLARATION_PATTERN.test(path))
return false;
return true;
}
/**
* Call the `gh api` CLI and return parsed JSON.
* Relies on gh's built-in auth and rate-limit handling.
*/
function ghApi(endpoint) {
const raw = execSync(`gh api "${endpoint}" --paginate`, {
encoding: 'utf-8',
maxBuffer: 50 * 1024 * 1024, // 50 MB — large repos can have big diffs
timeout: 60_000,
});
return JSON.parse(raw);
}
/**
* Fetch a single page from the GitHub API (no --paginate).
*/
function ghApiPage(endpoint) {
const raw = execSync(`gh api "${endpoint}"`, {
encoding: 'utf-8',
maxBuffer: 50 * 1024 * 1024,
timeout: 60_000,
});
return JSON.parse(raw);
}
/**
* Discover merged bug-fix PRs from a GitHub repo.
*
* Flow:
* 1. Fetch closed PRs (paginated)
* 2. Filter to merged-only (merged_at !== null)
* 3. Filter by bug labels (with optional title fallback)
* 4. Optionally enrich with review comments
* 5. Collect changed file paths
*/
export function discoverBugFixPRs(owner, name, config = {}) {
const { perPage = 30, maxPages = 3, bugLabels = DEFAULT_BUG_LABELS, titleFallback = true, fetchComments = true, } = config;
const discovered = [];
for (let page = 1; page <= maxPages; page++) {
const endpoint = `/repos/${owner}/${name}/pulls?state=closed&sort=updated&direction=desc&per_page=${perPage}&page=${page}`;
let prs;
try {
prs = ghApiPage(endpoint);
}
catch {
// If the API call fails (auth, network, etc.), stop pagination
break;
}
if (!prs || prs.length === 0)
break;
for (const pr of prs) {
// Only merged PRs
if (!pr.merged_at || !pr.merge_commit_sha)
continue;
const labelNames = pr.labels.map((l) => l.name);
const hasBugLabel = matchesBugLabels(labelNames, bugLabels);
const hasBugTitle = titleFallback && matchesBugTitle(pr.title);
if (!hasBugLabel && !hasBugTitle)
continue;
// Fetch file list for this PR
let files = [];
try {
const prFiles = ghApi(`/repos/${owner}/${name}/pulls/${pr.number}/files`);
files = prFiles.map((f) => f.filename);
}
catch {
// If we can't get files, still include the PR with empty files
}
// Fetch review comments if requested
let reviewComments = [];
if (fetchComments) {
try {
const comments = ghApi(`/repos/${owner}/${name}/pulls/${pr.number}/comments`);
reviewComments = comments.map((c) => ({
body: c.body,
path: c.path,
line: c.line ?? c.original_line ?? null,
}));
}
catch {
// Review comments are optional enrichment
}
}
discovered.push({
repo: `${owner}/${name}`,
prNumber: pr.number,
title: pr.title,
labels: labelNames,
mergeCommit: pr.merge_commit_sha,
reviewComments,
files,
});
}
}
return discovered;
}
/**
* Fetch and parse diffs for a discovered PR.
*
* Calls the files endpoint, filters to target files,
* and parses each file's patch into typed DiffHunks.
*/
export function fetchPRDiffs(owner, name, pr) {
let allHunks = [];
try {
const files = ghApi(`/repos/${owner}/${name}/pulls/${pr.prNumber}/files`);
for (const file of files) {
if (!isTargetFile(file.filename))
continue;
if (!file.patch)
continue;
const hunks = parsePatch(file.filename, file.patch);
allHunks = allHunks.concat(hunks);
}
}
catch {
// Return empty hunks if API call fails
}
return { pr, hunks: allHunks };
}
//# sourceMappingURL=pr-discovery.js.map
{"version":3,"file":"pr-discovery.js","sourceRoot":"","sources":["../../../src/lib/rule-harvester/pr-discovery.ts"],"names":[],"mappings":"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"}
/**
* PR Harvest Orchestrator — ties discovery -> extraction -> generalization -> catalog
* into a complete pipeline for mining bug-fix PRs from public GitHub repos.
*
* PR-sourced rules are validated through the validation harness before promotion.
* Fuzzy deduplication via Dice coefficient prevents near-duplicate rules.
*/
import type { LearnedRule } from '../learned-rules/types.js';
/** Per-repo set of processed PR numbers. */
export type PRProgress = Record<string, number[]>;
export declare function createPRProgress(): PRProgress;
export declare function markPRProcessed(progress: PRProgress, repo: string, prNumber: number): PRProgress;
export declare function isPRProcessed(progress: PRProgress, repo: string, prNumber: number): boolean;
export declare function getPRProgressStats(progress: PRProgress, repo: string): {
processedPRs: number;
};
export declare function loadPRProgress(dir: string): Promise<PRProgress>;
export declare function savePRProgress(dir: string, progress: PRProgress): Promise<void>;
/**
* Dice coefficient (bigram overlap) for fuzzy string similarity.
* Returns a value between 0 (no overlap) and 1 (identical).
*/
export declare function diceCoefficient(a: string, b: string): number;
/**
* Find the most similar existing rule by description, if above threshold.
*/
export declare function findFuzzyDuplicate(existingRules: LearnedRule[], description: string, category: string): LearnedRule | null;
export interface PRHarvestConfig {
/** List of repos to mine PRs from. */
repoList: Array<{
owner: string;
name: string;
}>;
/** Assay project root where rules go. */
catalogPath: string;
/** Where pr-progress.json lives. */
progressDir: string;
/** Where run reports go. */
runsDir: string;
/** Model name for reporting. */
model: string;
/** Max repos to process. */
limit?: number;
/** Only scan these repos (owner/name format). */
repoFilter?: string[];
/** Skip already-processed PRs. */
resume?: boolean;
/** Max PRs per repo. */
maxPRsPerRepo?: number;
/** Logging callback. */
onLog?: (msg: string) => void;
}
export declare function harvestPRs(config: PRHarvestConfig): Promise<void>;
/**
* PR Harvest Orchestrator — ties discovery -> extraction -> generalization -> catalog
* into a complete pipeline for mining bug-fix PRs from public GitHub repos.
*
* PR-sourced rules are validated through the validation harness before promotion.
* Fuzzy deduplication via Dice coefficient prevents near-duplicate rules.
*/
import { readFile, writeFile, mkdir } from 'node:fs/promises';
import { join } from 'node:path';
import { discoverBugFixPRs, fetchPRDiffs } from './pr-discovery.js';
import { filterHunks } from './diff-parser.js';
import { generalizeHunk, mapToExtractedPattern } from './rule-generalizer.js';
import { addRule, updateRule, loadRules, loadStats } from '../learned-rules/learned-catalog.js';
import { validateRule } from '../learned-rules/validation-harness.js';
import { saveRunReport } from './reporter.js';
export function createPRProgress() {
return {};
}
export function markPRProcessed(progress, repo, prNumber) {
const existing = progress[repo] ?? [];
if (existing.includes(prNumber))
return progress;
return { ...progress, [repo]: [...existing, prNumber] };
}
export function isPRProcessed(progress, repo, prNumber) {
return (progress[repo] ?? []).includes(prNumber);
}
export function getPRProgressStats(progress, repo) {
return { processedPRs: (progress[repo] ?? []).length };
}
export async function loadPRProgress(dir) {
const filePath = join(dir, 'pr-progress.json');
try {
const content = await readFile(filePath, 'utf-8');
return JSON.parse(content);
}
catch {
return {};
}
}
export async function savePRProgress(dir, progress) {
await mkdir(dir, { recursive: true });
const filePath = join(dir, 'pr-progress.json');
await writeFile(filePath, JSON.stringify(progress, null, 2), 'utf-8');
}
// ── Fuzzy Deduplication ─────────────────────────────────────
/**
* Dice coefficient (bigram overlap) for fuzzy string similarity.
* Returns a value between 0 (no overlap) and 1 (identical).
*/
export function diceCoefficient(a, b) {
if (a === b)
return 1;
if (a.length < 2 || b.length < 2)
return 0;
const bigramsA = new Map();
for (let i = 0; i < a.length - 1; i++) {
const bigram = a.substring(i, i + 2);
bigramsA.set(bigram, (bigramsA.get(bigram) ?? 0) + 1);
}
const bigramsB = new Map();
for (let i = 0; i < b.length - 1; i++) {
const bigram = b.substring(i, i + 2);
bigramsB.set(bigram, (bigramsB.get(bigram) ?? 0) + 1);
}
let intersectionSize = 0;
for (const [bigram, countA] of bigramsA) {
const countB = bigramsB.get(bigram) ?? 0;
intersectionSize += Math.min(countA, countB);
}
return (2 * intersectionSize) / (a.length - 1 + (b.length - 1));
}
/** Similarity threshold above which two descriptions are considered duplicates. */
const DEDUP_THRESHOLD = 0.7;
/**
* Find the most similar existing rule by description, if above threshold.
*/
export function findFuzzyDuplicate(existingRules, description, category) {
let bestMatch = null;
let bestScore = 0;
for (const rule of existingRules) {
// Only compare within the same category
if (rule.pattern.claimCategory !== category)
continue;
if (rule.status === 'rejected' || rule.status === 'deprecated')
continue;
const score = diceCoefficient(rule.pattern.description.toLowerCase(), description.toLowerCase());
if (score > bestScore) {
bestScore = score;
bestMatch = rule;
}
}
return bestScore >= DEDUP_THRESHOLD ? bestMatch : null;
}
// ── Rule Construction ───────────────────────────────────────
function buildLearnedRule(generalizedRule, patternId, repo, prNumber, hunkFile, buggyCode) {
const now = new Date().toISOString();
const pattern = mapToExtractedPattern(generalizedRule, patternId);
// Use actual buggy code from the diff hunk so validation has real test material
const codeSnippet = buggyCode && buggyCode.trim().length > 10
? buggyCode
: `PR #${prNumber}: ${generalizedRule.fix.description}`;
const sourceFinding = {
claimId: `pr_${repo.replace('/', '_')}_${prNumber}`,
claimDescription: generalizedRule.description,
codeSnippet,
filePath: hunkFile,
language: generalizedRule.detection.language,
timestamp: now,
};
return {
id: patternId,
pattern,
status: 'promoted', // Default; overridden by validation harness result
createdAt: now,
updatedAt: now,
fireCount: 0,
truePositiveCount: 0,
falsePositiveCount: 0,
sourceFindings: [sourceFinding],
source: 'pr',
fixDescription: generalizedRule.fix.description,
fixPattern: generalizedRule.fix.pattern,
confirmationCount: 1,
};
}
// ── Main Orchestrator ───────────────────────────────────────
function log(config, msg) {
if (config.onLog) {
config.onLog(msg);
}
else {
console.log(msg);
}
}
export async function harvestPRs(config) {
const startTime = Date.now();
// 1. Load progress if resuming
let progress = config.resume
? await loadPRProgress(config.progressDir)
: createPRProgress();
if (config.resume) {
const totalTracked = Object.values(progress).reduce((sum, prs) => sum + prs.length, 0);
log(config, `Resumed PR progress: ${totalTracked} PRs tracked`);
}
// 2. Filter repos
let repos = config.repoList;
if (config.repoFilter && config.repoFilter.length > 0) {
const filterSet = new Set(config.repoFilter);
repos = repos.filter((r) => filterSet.has(`${r.owner}/${r.name}`));
}
if (config.limit !== undefined && config.limit > 0) {
repos = repos.slice(0, config.limit);
}
log(config, `Processing ${repos.length} repos in PR mode`);
// Tracking for the run report
let totalPRsDiscovered = 0;
let totalHunksProcessed = 0;
let rulesLearned = 0;
let rulesRejected = 0;
let rulesDuplicate = 0;
const unmatchedCategories = {};
for (const repo of repos) {
const key = `${repo.owner}/${repo.name}`;
log(config, `[PR-DISCOVER] ${key}`);
// 3. Discover bug-fix PRs
let discoveredPRs;
try {
const maxPRs = config.maxPRsPerRepo ?? 100;
const allDiscoveredPRs = discoverBugFixPRs(repo.owner, repo.name, {
perPage: 30,
maxPages: Math.ceil(maxPRs / 30),
titleFallback: true,
fetchComments: true,
});
discoveredPRs = allDiscoveredPRs.slice(0, maxPRs);
}
catch (err) {
log(config, `[FAIL] ${key} — discovery failed: ${String(err)}`);
continue;
}
log(config, `[FOUND] ${key} — ${discoveredPRs.length} bug-fix PRs`);
totalPRsDiscovered += discoveredPRs.length;
for (const pr of discoveredPRs) {
// Skip already-processed PRs on resume
if (config.resume && isPRProcessed(progress, key, pr.prNumber)) {
log(config, ` [SKIP] PR #${pr.prNumber} — already processed`);
continue;
}
log(config, ` [PR] #${pr.prNumber}: ${pr.title}`);
// 4. Fetch and parse diffs
let parsedDiff;
try {
parsedDiff = fetchPRDiffs(repo.owner, repo.name, pr);
}
catch (err) {
log(config, ` [DIFF_ERR] PR #${pr.prNumber} — ${String(err)}`);
progress = markPRProcessed(progress, key, pr.prNumber);
await savePRProgress(config.progressDir, progress);
continue;
}
// Filter to meaningful hunks
const meaningfulHunks = filterHunks(parsedDiff.hunks);
if (meaningfulHunks.length === 0) {
log(config, ` [SKIP] PR #${pr.prNumber} — no meaningful hunks`);
progress = markPRProcessed(progress, key, pr.prNumber);
await savePRProgress(config.progressDir, progress);
continue;
}
log(config, ` [HUNKS] ${meaningfulHunks.length} meaningful (of ${parsedDiff.hunks.length} total)`);
totalHunksProcessed += meaningfulHunks.length;
// Load catalog once before processing hunks (refreshed after writes)
let existingRules = await loadRules(config.catalogPath);
// 5. Generalize each hunk
for (const hunk of meaningfulHunks) {
// Find matching review comment for this hunk's file
const matchingComment = pr.reviewComments.find((c) => c.path === hunk.file);
let generalizedRule;
try {
generalizedRule = await generalizeHunk(hunk, key, pr.prNumber, pr.title, matchingComment?.body);
}
catch (err) {
log(config, ` [GEN_ERR] ${hunk.file}:${hunk.startLine} — ${String(err)}`);
rulesRejected++;
continue;
}
if (!generalizedRule) {
log(config, ` [SKIP] ${hunk.file}:${hunk.startLine} — too context-specific`);
rulesRejected++;
continue;
}
// 6. Check for fuzzy duplicates in catalog
const duplicate = findFuzzyDuplicate(existingRules, generalizedRule.description, generalizedRule.category);
if (duplicate) {
// Merge provenance into existing rule
log(config, ` [DEDUP] "${generalizedRule.description}" → merging into ${duplicate.id}`);
await updateRule(config.catalogPath, duplicate.id, (existing) => ({
...existing,
confirmationCount: (existing.confirmationCount ?? 1) + 1,
updatedAt: new Date().toISOString(),
sourceFindings: [
...existing.sourceFindings,
{
claimId: `pr_${key.replace('/', '_')}_${pr.prNumber}`,
claimDescription: generalizedRule.description,
codeSnippet: `PR #${pr.prNumber}: ${generalizedRule.fix.description}`,
filePath: hunk.file,
language: generalizedRule.detection.language,
timestamp: new Date().toISOString(),
},
],
}));
rulesDuplicate++;
existingRules = await loadRules(config.catalogPath);
continue;
}
// 7. Build candidate rule and validate before promotion
const patternId = `lp_pr_${Date.now()}`;
// Pass the buggy code (removed lines + context) so validation has real test material
const buggyCode = [...hunk.context, ...hunk.removedLines].join('\n');
const candidateRule = buildLearnedRule(generalizedRule, patternId, key, pr.prNumber, hunk.file, buggyCode);
// 7b. Run through validation harness
const validationResult = validateRule(candidateRule);
const finalRule = {
...candidateRule,
status: validationResult.passed ? 'promoted' : 'rejected',
validationResults: validationResult,
};
await addRule(config.catalogPath, finalRule);
existingRules = await loadRules(config.catalogPath);
if (validationResult.passed) {
rulesLearned++;
log(config, ` [LEARNED] ${patternId}: ${generalizedRule.description} (precision=${validationResult.precision.toFixed(2)} recall=${validationResult.recall.toFixed(2)})`);
}
else {
rulesRejected++;
log(config, ` [REJECTED] ${patternId}: ${generalizedRule.description} — validation failed (precision=${validationResult.precision.toFixed(2)} recall=${validationResult.recall.toFixed(2)})`);
}
}
// Mark PR as processed and persist after each PR
progress = markPRProcessed(progress, key, pr.prNumber);
await savePRProgress(config.progressDir, progress);
}
log(config, `[DONE] ${key}`);
}
// 8. Generate and save run report
const stats = await loadStats(config.catalogPath);
const allRules = await loadRules(config.catalogPath);
const catalogSize = {
total: allRules.length,
promoted: allRules.filter((r) => r.status === 'promoted').length,
rejected: allRules.filter((r) => r.status === 'rejected').length,
};
const durationMinutes = (Date.now() - startTime) / 60_000;
const report = {
timestamp: new Date().toISOString(),
model: config.model,
reposScanned: repos.length,
filesScanned: totalHunksProcessed, // hunks are the PR-mode equivalent of files
claimsExtracted: totalPRsDiscovered,
findingsConfirmed: totalHunksProcessed,
rulesLearned,
rulesRejected,
rulesDuplicate,
unmatchedCategories,
durationMinutes,
catalogSize,
};
const reportPath = await saveRunReport(config.runsDir, report);
// 9. Print summary
log(config, '');
log(config, '══════════════════════════════════════');
log(config, ' PR HARVEST COMPLETE');
log(config, '══════════════════════════════════════');
log(config, ` Repos processed: ${repos.length}`);
log(config, ` PRs discovered: ${totalPRsDiscovered}`);
log(config, ` Hunks processed: ${totalHunksProcessed}`);
log(config, ` Rules learned: ${rulesLearned}`);
log(config, ` Rules duplicate: ${rulesDuplicate}`);
log(config, ` Rules rejected: ${rulesRejected}`);
log(config, ` Catalog total: ${catalogSize.total} (${catalogSize.promoted} promoted)`);
log(config, ` Duration: ${durationMinutes.toFixed(1)}m`);
log(config, ` Report: ${reportPath}`);
log(config, '══════════════════════════════════════');
}
//# sourceMappingURL=pr-harvest.js.map
{"version":3,"file":"pr-harvest.js","sourceRoot":"","sources":["../../../src/lib/rule-harvester/pr-harvest.ts"],"names":[],"mappings":"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"}
export type FileState = 'pending' | 'scanned' | 'confirmed' | 'learned';
export interface RepoProgress {
status: 'pending' | 'in_progress' | 'complete';
startedAt: string;
files: Record<string, FileState>;
}
export type HarvestProgress = Record<string, RepoProgress>;
export declare function loadProgress(dir: string): Promise<HarvestProgress>;
export declare function saveProgress(dir: string, progress: HarvestProgress): Promise<void>;
export declare function getFileState(progress: HarvestProgress, repoKey: string, filePath: string): FileState;
export declare function getRepoStatus(files: Record<string, FileState>): 'pending' | 'in_progress' | 'complete';
export declare function initRepo(progress: HarvestProgress, repoKey: string): HarvestProgress;
export declare function setFileState(progress: HarvestProgress, repoKey: string, filePath: string, state: FileState): HarvestProgress;
import { readFile, writeFile, mkdir } from 'node:fs/promises';
import { join } from 'node:path';
export async function loadProgress(dir) {
const filePath = join(dir, 'progress.json');
try {
const content = await readFile(filePath, 'utf-8');
return JSON.parse(content);
}
catch {
return {};
}
}
export async function saveProgress(dir, progress) {
await mkdir(dir, { recursive: true });
const filePath = join(dir, 'progress.json');
await writeFile(filePath, JSON.stringify(progress, null, 2), 'utf-8');
}
export function getFileState(progress, repoKey, filePath) {
return progress[repoKey]?.files[filePath] ?? 'pending';
}
export function getRepoStatus(files) {
const states = Object.values(files);
if (states.length === 0)
return 'pending';
if (states.every((s) => s === 'learned'))
return 'complete';
if (states.every((s) => s === 'pending'))
return 'pending';
return 'in_progress';
}
export function initRepo(progress, repoKey) {
if (progress[repoKey])
return progress;
return {
...progress,
[repoKey]: { status: 'pending', startedAt: new Date().toISOString(), files: {} },
};
}
export function setFileState(progress, repoKey, filePath, state) {
const existingRepo = progress[repoKey];
const existingFiles = existingRepo?.files ?? {};
const updatedFiles = { ...existingFiles, [filePath]: state };
const updatedRepo = {
status: getRepoStatus(updatedFiles),
startedAt: existingRepo?.startedAt ?? new Date().toISOString(),
files: updatedFiles,
};
return { ...progress, [repoKey]: updatedRepo };
}
//# sourceMappingURL=progress.js.map
{"version":3,"file":"progress.js","sourceRoot":"","sources":["../../../src/lib/rule-harvester/progress.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AAC9D,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAYjC,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,GAAW;IAC5C,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,eAAe,CAAC,CAAC;IAC5C,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;QAClD,OAAO,IAAI,CAAC,KAAK,CAAC,OAAO,CAAoB,CAAC;IAChD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,GAAW,EAAE,QAAyB;IACvE,MAAM,KAAK,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACtC,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,eAAe,CAAC,CAAC;IAC5C,MAAM,SAAS,CAAC,QAAQ,EAAE,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC;AACxE,CAAC;AAED,MAAM,UAAU,YAAY,CAC1B,QAAyB,EACzB,OAAe,EACf,QAAgB;IAEhB,OAAO,QAAQ,CAAC,OAAO,CAAC,EAAE,KAAK,CAAC,QAAQ,CAAC,IAAI,SAAS,CAAC;AACzD,CAAC;AAED,MAAM,UAAU,aAAa,CAC3B,KAAgC;IAEhC,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IACpC,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,SAAS,CAAC;IAC1C,IAAI,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,SAAS,CAAC;QAAE,OAAO,UAAU,CAAC;IAC5D,IAAI,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,SAAS,CAAC;QAAE,OAAO,SAAS,CAAC;IAC3D,OAAO,aAAa,CAAC;AACvB,CAAC;AAED,MAAM,UAAU,QAAQ,CACtB,QAAyB,EACzB,OAAe;IAEf,IAAI,QAAQ,CAAC,OAAO,CAAC;QAAE,OAAO,QAAQ,CAAC;IACvC,OAAO;QACL,GAAG,QAAQ;QACX,CAAC,OAAO,CAAC,EAAE,EAAE,MAAM,EAAE,SAAS,EAAE,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE;KACjF,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,YAAY,CAC1B,QAAyB,EACzB,OAAe,EACf,QAAgB,EAChB,KAAgB;IAEhB,MAAM,YAAY,GAAG,QAAQ,CAAC,OAAO,CAAC,CAAC;IACvC,MAAM,aAAa,GAAG,YAAY,EAAE,KAAK,IAAI,EAAE,CAAC;IAChD,MAAM,YAAY,GAA8B,EAAE,GAAG,aAAa,EAAE,CAAC,QAAQ,CAAC,EAAE,KAAK,EAAE,CAAC;IAExF,MAAM,WAAW,GAAiB;QAChC,MAAM,EAAE,aAAa,CAAC,YAAY,CAAC;QACnC,SAAS,EAAE,YAAY,EAAE,SAAS,IAAI,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;QAC9D,KAAK,EAAE,YAAY;KACpB,CAAC;IAEF,OAAO,EAAE,GAAG,QAAQ,EAAE,CAAC,OAAO,CAAC,EAAE,WAAW,EAAE,CAAC;AACjD,CAAC"}
export interface ScanFileResult {
repo: string;
filePath: string;
claimsExtracted: number;
confirmed: number;
rulesLearned: number;
rulesRejected: number;
rulesDuplicate: number;
unmatchedCategories: string[];
durationMs: number;
}
export interface RunReport {
timestamp: string;
model: string;
reposScanned: number;
filesScanned: number;
claimsExtracted: number;
findingsConfirmed: number;
rulesLearned: number;
rulesRejected: number;
rulesDuplicate: number;
unmatchedCategories: Record<string, number>;
durationMinutes: number;
catalogSize: {
total: number;
promoted: number;
rejected: number;
};
}
export declare function createRunReport(fileResults: ScanFileResult[], model: string, catalogSize: {
total: number;
promoted: number;
rejected: number;
}): RunReport;
export declare function saveRunReport(runsDir: string, report: RunReport): Promise<string>;
import { mkdir, writeFile } from 'node:fs/promises';
import { join } from 'node:path';
export function createRunReport(fileResults, model, catalogSize) {
const uniqueRepos = new Set(fileResults.map((r) => r.repo));
let claimsExtracted = 0;
let findingsConfirmed = 0;
let rulesLearned = 0;
let rulesRejected = 0;
let rulesDuplicate = 0;
let totalDurationMs = 0;
const unmatchedCategories = {};
for (const result of fileResults) {
claimsExtracted += result.claimsExtracted;
findingsConfirmed += result.confirmed;
rulesLearned += result.rulesLearned;
rulesRejected += result.rulesRejected;
rulesDuplicate += result.rulesDuplicate;
totalDurationMs += result.durationMs;
for (const category of result.unmatchedCategories) {
unmatchedCategories[category] = (unmatchedCategories[category] ?? 0) + 1;
}
}
return {
timestamp: new Date().toISOString(),
model,
reposScanned: uniqueRepos.size,
filesScanned: fileResults.length,
claimsExtracted,
findingsConfirmed,
rulesLearned,
rulesRejected,
rulesDuplicate,
unmatchedCategories,
durationMinutes: totalDurationMs / 60_000,
catalogSize,
};
}
export async function saveRunReport(runsDir, report) {
await mkdir(runsDir, { recursive: true });
// Replace : and . with - to make a safe filename
const safeName = report.timestamp.replace(/[:.]/g, '-');
const filePath = join(runsDir, `${safeName}.json`);
await writeFile(filePath, JSON.stringify(report, null, 2), 'utf-8');
return filePath;
}
//# sourceMappingURL=reporter.js.map
{"version":3,"file":"reporter.js","sourceRoot":"","sources":["../../../src/lib/rule-harvester/reporter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AA6BjC,MAAM,UAAU,eAAe,CAC7B,WAA6B,EAC7B,KAAa,EACb,WAAkE;IAElE,MAAM,WAAW,GAAG,IAAI,GAAG,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;IAE5D,IAAI,eAAe,GAAG,CAAC,CAAC;IACxB,IAAI,iBAAiB,GAAG,CAAC,CAAC;IAC1B,IAAI,YAAY,GAAG,CAAC,CAAC;IACrB,IAAI,aAAa,GAAG,CAAC,CAAC;IACtB,IAAI,cAAc,GAAG,CAAC,CAAC;IACvB,IAAI,eAAe,GAAG,CAAC,CAAC;IACxB,MAAM,mBAAmB,GAA2B,EAAE,CAAC;IAEvD,KAAK,MAAM,MAAM,IAAI,WAAW,EAAE,CAAC;QACjC,eAAe,IAAI,MAAM,CAAC,eAAe,CAAC;QAC1C,iBAAiB,IAAI,MAAM,CAAC,SAAS,CAAC;QACtC,YAAY,IAAI,MAAM,CAAC,YAAY,CAAC;QACpC,aAAa,IAAI,MAAM,CAAC,aAAa,CAAC;QACtC,cAAc,IAAI,MAAM,CAAC,cAAc,CAAC;QACxC,eAAe,IAAI,MAAM,CAAC,UAAU,CAAC;QAErC,KAAK,MAAM,QAAQ,IAAI,MAAM,CAAC,mBAAmB,EAAE,CAAC;YAClD,mBAAmB,CAAC,QAAQ,CAAC,GAAG,CAAC,mBAAmB,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC;QAC3E,CAAC;IACH,CAAC;IAED,OAAO;QACL,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;QACnC,KAAK;QACL,YAAY,EAAE,WAAW,CAAC,IAAI;QAC9B,YAAY,EAAE,WAAW,CAAC,MAAM;QAChC,eAAe;QACf,iBAAiB;QACjB,YAAY;QACZ,aAAa;QACb,cAAc;QACd,mBAAmB;QACnB,eAAe,EAAE,eAAe,GAAG,MAAM;QACzC,WAAW;KACZ,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,OAAe,EAAE,MAAiB;IACpE,MAAM,KAAK,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAE1C,iDAAiD;IACjD,MAAM,QAAQ,GAAG,MAAM,CAAC,SAAS,CAAC,OAAO,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC;IACxD,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,EAAE,GAAG,QAAQ,OAAO,CAAC,CAAC;IAEnD,MAAM,SAAS,CAAC,QAAQ,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC;IAEpE,OAAO,QAAQ,CAAC;AAClB,CAAC"}
/**
* Rule Generalizer — Uses LLM to generalize PR diff hunks into reusable
* code quality rules for the Assay catalog.
*
* Given a before/after diff hunk (and optional reviewer comment), the LLM
* produces a GeneralizedRule with detection regex, fix description, severity,
* and provenance. The rule is then mapped to an ExtractedPattern for catalog
* ingestion.
*/
import type { DiffHunk, GeneralizedRule } from '../learned-rules/types.js';
import type { ExtractedPattern } from '../learned-rules/types.js';
/**
* Build the user prompt for the LLM generalization call.
*/
export declare function buildGeneralizationPrompt(hunk: DiffHunk, repo: string, prNumber: number, prTitle: string, reviewComment?: string): string;
/**
* Call the LLM to generalize a diff hunk into a reusable rule.
* Returns null if the hunk is too context-specific, parse fails,
* or all retries are exhausted.
*/
export declare function generalizeHunk(hunk: DiffHunk, repo: string, prNumber: number, prTitle: string, reviewComment?: string): Promise<GeneralizedRule | null>;
/**
* Map a GeneralizedRule to an ExtractedPattern for catalog ingestion.
*/
export declare function mapToExtractedPattern(rule: GeneralizedRule, patternId: string): ExtractedPattern;
/**
* Rule Generalizer — Uses LLM to generalize PR diff hunks into reusable
* code quality rules for the Assay catalog.
*
* Given a before/after diff hunk (and optional reviewer comment), the LLM
* produces a GeneralizedRule with detection regex, fix description, severity,
* and provenance. The rule is then mapped to an ExtractedPattern for catalog
* ingestion.
*/
import { getProvider } from '../llm-provider.js';
import { normalizeCategory } from '../learned-rules/category-map.js';
const SYSTEM_PROMPT = `You are a code quality pattern extractor. Given a before/after code diff from a merged PR, generalize the fix into a reusable detection rule.
Respond with a JSON object (no markdown, no explanation) with these fields:
- category: string — bug category (e.g., "null-safety", "error-handling", "type-safety", "security", "performance")
- severity: "low" | "medium" | "high" | "critical"
- description: string — human-readable description of the pattern
- detection: { pattern: string (regex), language: string }
- fix: { description: string, pattern: string (regex replacement) }
- fileGlob: string — glob for files to scan (e.g., "**/*.ts")
- matchBehavior: "presence_is_bad" | "absence_is_bad"
- evidenceTemplate: string — template using {match}, {file}, {line}
If the change is too context-specific to generalize (e.g., a variable rename, config tweak, or purely cosmetic), respond with: {"skip": true}
Respond with JSON only.`;
/**
* Build the user prompt for the LLM generalization call.
*/
export function buildGeneralizationPrompt(hunk, repo, prNumber, prTitle, reviewComment) {
const before = hunk.removedLines.join('\n');
const after = hunk.addedLines.join('\n');
const ctx = hunk.context.length > 0
? `\nSurrounding context:\n${hunk.context.join('\n')}`
: '';
let prompt = `Repository: ${repo}
PR #${prNumber}: ${prTitle}
File: ${hunk.file} (line ${hunk.startLine})
Before:
${before}
After:
${after}${ctx}`;
if (reviewComment) {
prompt += `\n\nReviewer comment:\n${reviewComment}`;
}
return prompt;
}
/**
* Call the LLM to generalize a diff hunk into a reusable rule.
* Returns null if the hunk is too context-specific, parse fails,
* or all retries are exhausted.
*/
export async function generalizeHunk(hunk, repo, prNumber, prTitle, reviewComment) {
const provider = getProvider();
const model = process.env.ASSAY_OLLAMA_MODEL ?? 'qwen3.5:122b';
const userPrompt = buildGeneralizationPrompt(hunk, repo, prNumber, prTitle, reviewComment);
const MAX_RETRIES = 3;
const BASE_DELAY_MS = 2000;
for (let attempt = 0; attempt < MAX_RETRIES; attempt++) {
try {
const result = await Promise.race([
provider.complete({
model,
systemPrompt: SYSTEM_PROMPT,
userPrompt,
maxTokens: 2000,
}),
new Promise((_, reject) => setTimeout(() => reject(new Error('LLM timeout')), 60_000)),
]);
const parsed = JSON.parse(result.content);
// Skip response — hunk is too context-specific
if (parsed.skip)
return null;
// Validate required fields
if (!parsed.category ||
!parsed.description ||
!parsed.detection?.pattern ||
!parsed.detection?.language) {
throw new Error('Missing required fields in LLM response');
}
const rule = {
category: parsed.category,
severity: parsed.severity ?? 'medium',
description: parsed.description,
detection: {
pattern: parsed.detection.pattern,
language: parsed.detection.language,
},
fix: {
description: parsed.fix?.description ?? '',
pattern: parsed.fix?.pattern ?? '',
},
fileGlob: parsed.fileGlob ?? '',
matchBehavior: parsed.matchBehavior ?? 'presence_is_bad',
evidenceTemplate: parsed.evidenceTemplate ?? '{match} in {file}',
provenance: {
repo,
pr: prNumber,
file: hunk.file,
...(reviewComment ? { reviewComment } : {}),
},
};
return rule;
}
catch {
// Exponential backoff before retry
if (attempt < MAX_RETRIES - 1) {
await new Promise((resolve) => setTimeout(resolve, BASE_DELAY_MS * Math.pow(2, attempt)));
}
}
}
return null;
}
/**
* Map a GeneralizedRule to an ExtractedPattern for catalog ingestion.
*/
export function mapToExtractedPattern(rule, patternId) {
// Coerce 'low' to 'medium' — ExtractedPattern doesn't support 'low'
const severity = rule.severity === 'low' ? 'medium' : rule.severity;
return {
id: patternId,
description: rule.description,
kind: 'regex',
languages: [rule.detection.language],
regexPattern: rule.detection.pattern,
fileGlob: rule.fileGlob || '**/*.{ts,tsx,js,jsx}',
matchBehavior: rule.matchBehavior,
claimCategory: normalizeCategory(rule.category),
severity,
evidenceTemplate: rule.evidenceTemplate,
};
}
//# sourceMappingURL=rule-generalizer.js.map
{"version":3,"file":"rule-generalizer.js","sourceRoot":"","sources":["../../../src/lib/rule-harvester/rule-generalizer.ts"],"names":[],"mappings":"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"}
import type { CodeClaim, ClaimVerification } from '../../sdk/types.js';
export interface ScanFailure {
claimId: string;
category: string;
severity: string;
description: string;
assertion: string;
reasoning: string;
method: 'formal' | 'llm';
}
export interface ScanResult {
totalClaims: number;
failures: ScanFailure[];
}
export declare function buildScanResult(claims: CodeClaim[], verifications: ClaimVerification[]): ScanResult;
export declare function scanFile(code: string, language: string): Promise<{
result: ScanResult;
claims: CodeClaim[];
verifications: ClaimVerification[];
}>;
import { extractCodeClaims, verifyCodeClaims, applyFormalVerification, } from '../../sdk/forward-verify.js';
export function buildScanResult(claims, verifications) {
const claimById = new Map(claims.map((c) => [c.id, c]));
const failures = verifications
.filter((v) => v.verdict === 'FAIL' || v.verdict === 'PARTIAL')
.map((v) => {
const claim = claimById.get(v.claimId);
return {
claimId: v.claimId,
category: claim?.category ?? 'unknown',
severity: claim?.severity ?? 'unknown',
description: claim?.description ?? '',
assertion: claim?.assertion ?? '',
reasoning: v.reasoning,
method: v.method,
};
});
return { totalClaims: claims.length, failures };
}
export async function scanFile(code, language) {
const { claims } = await extractCodeClaims(code, language);
if (claims.length === 0) {
return {
result: { totalClaims: 0, failures: [] },
claims: [],
verifications: [],
};
}
const { verifications: llmVerifications } = await verifyCodeClaims(code, language, claims);
const { verifications } = applyFormalVerification(code, language, claims, llmVerifications);
return {
result: buildScanResult(claims, verifications),
claims,
verifications,
};
}
//# sourceMappingURL=scanner.js.map
{"version":3,"file":"scanner.js","sourceRoot":"","sources":["../../../src/lib/rule-harvester/scanner.ts"],"names":[],"mappings":"AACA,OAAO,EACL,iBAAiB,EACjB,gBAAgB,EAChB,uBAAuB,GACxB,MAAM,6BAA6B,CAAC;AAiBrC,MAAM,UAAU,eAAe,CAC7B,MAAmB,EACnB,aAAkC;IAElC,MAAM,SAAS,GAAG,IAAI,GAAG,CAAoB,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;IAE3E,MAAM,QAAQ,GAAkB,aAAa;SAC1C,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,KAAK,MAAM,IAAI,CAAC,CAAC,OAAO,KAAK,SAAS,CAAC;SAC9D,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE;QACT,MAAM,KAAK,GAAG,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC;QACvC,OAAO;YACL,OAAO,EAAE,CAAC,CAAC,OAAO;YAClB,QAAQ,EAAE,KAAK,EAAE,QAAQ,IAAI,SAAS;YACtC,QAAQ,EAAE,KAAK,EAAE,QAAQ,IAAI,SAAS;YACtC,WAAW,EAAE,KAAK,EAAE,WAAW,IAAI,EAAE;YACrC,SAAS,EAAE,KAAK,EAAE,SAAS,IAAI,EAAE;YACjC,SAAS,EAAE,CAAC,CAAC,SAAS;YACtB,MAAM,EAAE,CAAC,CAAC,MAAM;SACjB,CAAC;IACJ,CAAC,CAAC,CAAC;IAEL,OAAO,EAAE,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,QAAQ,EAAE,CAAC;AAClD,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,QAAQ,CAC5B,IAAY,EACZ,QAAgB;IAMhB,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,iBAAiB,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;IAE3D,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACxB,OAAO;YACL,MAAM,EAAE,EAAE,WAAW,EAAE,CAAC,EAAE,QAAQ,EAAE,EAAE,EAAE;YACxC,MAAM,EAAE,EAAE;YACV,aAAa,EAAE,EAAE;SAClB,CAAC;IACJ,CAAC;IAED,MAAM,EAAE,aAAa,EAAE,gBAAgB,EAAE,GAAG,MAAM,gBAAgB,CAAC,IAAI,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAC;IAE3F,MAAM,EAAE,aAAa,EAAE,GAAG,uBAAuB,CAAC,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,gBAAgB,CAAC,CAAC;IAE5F,OAAO;QACL,MAAM,EAAE,eAAe,CAAC,MAAM,EAAE,aAAa,CAAC;QAC9C,MAAM;QACN,aAAa;KACd,CAAC;AACJ,CAAC"}
+10
-0

@@ -20,2 +20,3 @@ #!/usr/bin/env node

import { configCommand } from './commands/config-cmd.js';
import { harvestCommand } from './commands/harvest.js';
const program = new Command();

@@ -100,2 +101,11 @@ program

.action(assessCommand);
program
.command('harvest')
.description('Harvest formal verification rules from public TypeScript repos')
.option('-m, --mode <mode>', 'Harvest mode: pr (default) or scan', 'pr')
.option('-l, --limit <n>', 'Max repos to scan')
.option('-r, --repos <list>', 'Comma-separated owner/name list')
.option('--resume', 'Resume from progress.json')
.option('--stats', 'Show catalog growth stats')
.action(harvestCommand);
// ── Config (User Settings) ───────────────────────────────────

@@ -102,0 +112,0 @@ program

+1
-1

@@ -1,1 +0,1 @@

{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}

@@ -88,2 +88,3 @@ import { readFile, writeFile, mkdir } from 'node:fs/promises';

}
console.error(`│ Coverage: ${v.formalCoverage.formalPercent}% formal (${v.formalCoverage.formalClaimsVerified}/${v.formalCoverage.totalClaims} claims) — target: 85%`);
console.error(`│ Specs: ${result.specs}`);

@@ -90,0 +91,0 @@ console.error(`│ Constraints: ${result.constraints}`);

@@ -1,1 +0,1 @@

{"version":3,"file":"generate.js","sourceRoot":"","sources":["../../src/commands/generate.ts"],"names":[],"mappings":"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"}
{"version":3,"file":"generate.js","sourceRoot":"","sources":["../../src/commands/generate.ts"],"names":[],"mappings":"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"}

@@ -0,1 +1,2 @@

import { ScanSession } from '../lib/learned-rules/index.js';
import type { CodeClaim, ClaimVerification, FormalStats, VerificationResult } from './types.js';

@@ -16,3 +17,4 @@ export declare function extractCodeClaims(code: string, language: string, context?: string): Promise<{

};
export declare function forwardVerify(code: string, language: string, context?: string): Promise<{
export { ScanSession, type ScanSessionStats } from '../lib/learned-rules/index.js';
export declare function forwardVerify(code: string, language: string, context?: string, session?: ScanSession): Promise<{
result: VerificationResult;

@@ -19,0 +21,0 @@ inputTokens: number;

import { MODEL } from '../lib/anthropic.js';
import { getProvider } from '../lib/llm-provider.js';
import { runFormalVerification, } from '../lib/formal-verifier.js';
import { runLearnedRules, learnFromFinding, } from '../lib/learned-rules/index.js';
// ── System Prompts (ported from api/v1/forward.ts) ───────────

@@ -194,3 +195,5 @@ const EXTRACTION_SYSTEM = `You are a code quality auditor. Given source code, extract every testable claim the code makes about its behavior.

// ── Orchestrator ─────────────────────────────────────────────
export async function forwardVerify(code, language, context) {
// Re-export ScanSession so SDK callers can create one across multiple verify calls
export { ScanSession } from '../lib/learned-rules/index.js';
export async function forwardVerify(code, language, context, session) {
let totalIn = 0;

@@ -208,14 +211,74 @@ let totalOut = 0;

const { verifications, formalStats } = applyFormalVerification(code, language, claims, llmVerifications);
// Phase 2.75: Run learned rules (self-expanding formal verification)
// If a ScanSession is provided, record this file and pass it through
// so noisy rules (>30% fire rate after 5+ files) get suppressed.
if (session)
session.recordFile();
let learnedRuleFindings = [];
try {
learnedRuleFindings = await runLearnedRules(process.cwd(), code, language, session);
}
catch {
// Learned rules are additive — failures don't block the pipeline
}
// Phase 2.8: Feed confirmed failures back to the learning system
// (async, non-blocking — doesn't slow down the current scan)
const confirmedFailures = verifications.filter(v => v.verdict === 'FAIL' && v.method === 'formal');
if (confirmedFailures.length > 0) {
for (const failure of confirmedFailures) {
const matchingClaim = claims.find(c => c.id === failure.claimId);
if (!matchingClaim)
continue;
learnFromFinding(process.cwd(), {
claim: {
id: matchingClaim.id,
category: matchingClaim.category,
severity: matchingClaim.severity,
description: matchingClaim.description,
assertion: matchingClaim.assertion,
},
verification: {
verdict: 'FAIL',
reasoning: failure.reasoning,
},
code,
language,
filePath: 'unknown', // File path not available at this level
}).catch(() => { });
}
}
const passed = verifications.filter(v => v.verdict === 'PASS').length;
const failed = verifications.filter(v => v.verdict === 'FAIL').length;
const partial = verifications.filter(v => v.verdict === 'PARTIAL').length;
// Include learned rule findings in the total count
const learnedFailed = learnedRuleFindings.length;
// Compute formal coverage ratio — the key 10x roadmap metric.
// "Formal" = deterministic formal verifier (method: 'formal') + learned rules.
// "LLM" = everything else (method: 'llm' without formal override).
const formalFromVerifier = verifications.filter(v => v.method === 'formal').length;
const formalClaimsVerified = formalFromVerifier + learnedFailed;
const totalClaimsProcessed = verifications.length + learnedFailed;
const llmClaimsVerified = totalClaimsProcessed - formalClaimsVerified;
const formalCoverage = {
formalClaimsVerified,
llmClaimsVerified,
totalClaims: totalClaimsProcessed,
formalPercent: totalClaimsProcessed > 0
? Math.round((formalClaimsVerified / totalClaimsProcessed) * 1000) / 10
: 0,
};
const result = {
claims,
verifications,
formalStats,
formalStats: {
...formalStats,
learnedRuleFindings: learnedFailed,
...(session ? { noiseFilter: session.stats() } : {}),
},
formalCoverage,
passed,
failed,
failed: failed + learnedFailed,
partial,
total: verifications.length,
allPassed: failed === 0 && partial === 0,
total: verifications.length + learnedFailed,
allPassed: failed === 0 && partial === 0 && learnedFailed === 0,
};

@@ -222,0 +285,0 @@ return { result, inputTokens: totalIn, outputTokens: totalOut };

@@ -1,1 +0,1 @@

{"version":3,"file":"forward-verify.js","sourceRoot":"","sources":["../../src/sdk/forward-verify.ts"],"names":[],"mappings":"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"}
{"version":3,"file":"forward-verify.js","sourceRoot":"","sources":["../../src/sdk/forward-verify.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,qBAAqB,CAAC;AAC5C,OAAO,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AACrD,OAAO,EACL,qBAAqB,GAGtB,MAAM,2BAA2B,CAAC;AACnC,OAAO,EACL,eAAe,EACf,gBAAgB,GAIjB,MAAM,+BAA+B,CAAC;AAUvC,gEAAgE;AAEhE,MAAM,iBAAiB,GAAG;;;;;;;;;;;;;;;;;;;;;8CAqBoB,CAAC;AAE/C,MAAM,mBAAmB,GAAG;;;;;;;;;;;;oBAYR,CAAC;AAErB,gEAAgE;AAEhE,SAAS,eAAe,CAAC,IAAY;IACnC,IAAI,MAAM,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;IACzB,IAAI,MAAM,CAAC,UAAU,CAAC,KAAK,CAAC,EAAE,CAAC;QAC7B,MAAM,YAAY,GAAG,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QAC1C,IAAI,YAAY,KAAK,CAAC,CAAC;YAAE,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,YAAY,GAAG,CAAC,CAAC,CAAC;IACnE,CAAC;IACD,MAAM,SAAS,GAAG,MAAM,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC;IAC5C,IAAI,SAAS,KAAK,CAAC,CAAC;QAAE,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,SAAS,CAAC,CAAC;IAC1D,OAAO,MAAM,CAAC,IAAI,EAAE,CAAC;AACvB,CAAC;AAED,SAAS,oBAAoB,CAAC,IAAY;IACxC,IAAI,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC;QAAE,OAAO,IAAI,CAAC;IACpC,MAAM,SAAS,GAAG,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC;IACxC,IAAI,SAAS,KAAK,CAAC,CAAC;QAAE,OAAO,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,SAAS,GAAG,CAAC,CAAC,GAAG,KAAK,CAAC;IAClE,OAAO,IAAI,CAAC;AACd,CAAC;AAED,SAAS,cAAc,CAAC,IAAY;IAClC,IAAI,QAAQ,GAAG,eAAe,CAAC,IAAI,CAAC,CAAC;IACrC,QAAQ,GAAG,oBAAoB,CAAC,QAAQ,CAAC,CAAC;IAC1C,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;QACpC,OAAO,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC;IAC7C,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC;AAED,gEAAgE;AAEhE,SAAS,kBAAkB,CAAC,KAAgB;IAC1C,OAAO;QACL,EAAE,EAAE,KAAK,CAAC,EAAE;QACZ,QAAQ,EAAE,KAAK,CAAC,QAAQ;QACxB,QAAQ,EAAE,KAAK,CAAC,QAAQ;QACxB,WAAW,EAAE,KAAK,CAAC,WAAW;QAC9B,SAAS,EAAE,KAAK,CAAC,SAAS;QAC1B,QAAQ,EAAE,IAAI;KACf,CAAC;AACJ,CAAC;AAED,SAAS,cAAc,CAAC,GAA4B,EAAE,KAAa;IACjE,IAAI,CAAC,GAAG,CAAC,WAAW,IAAI,OAAO,GAAG,CAAC,WAAW,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAC;IACzE,IAAI,CAAC,GAAG,CAAC,SAAS,IAAI,OAAO,GAAG,CAAC,SAAS,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAC;IAErE,MAAM,eAAe,GAAG,CAAC,aAAa,EAAE,UAAU,EAAE,aAAa,EAAE,gBAAgB,EAAE,WAAW,EAAE,aAAa,CAAU,CAAC;IAC1H,MAAM,eAAe,GAAG,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,CAAU,CAAC;IAEvE,MAAM,QAAQ,GAAG,eAAe,CAAC,QAAQ,CAAC,GAAG,CAAC,QAA0C,CAAC;QACvF,CAAC,CAAE,GAAG,CAAC,QAAkC;QACzC,CAAC,CAAC,aAAa,CAAC;IAElB,MAAM,QAAQ,GAAG,eAAe,CAAC,QAAQ,CAAC,GAAG,CAAC,QAA0C,CAAC;QACvF,CAAC,CAAE,GAAG,CAAC,QAAkC;QACzC,CAAC,CAAC,QAAQ,CAAC;IAEb,OAAO;QACL,EAAE,EAAE,OAAO,GAAG,CAAC,EAAE,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,SAAS,MAAM,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE;QACvF,QAAQ;QACR,QAAQ;QACR,WAAW,EAAG,GAAG,CAAC,WAAsB,CAAC,IAAI,EAAE;QAC/C,SAAS,EAAG,GAAG,CAAC,SAAoB,CAAC,IAAI,EAAE;KAC5C,CAAC;AACJ,CAAC;AAED,SAAS,sCAAsC,CAC7C,CAAsB;IAEtB,OAAO;QACL,OAAO,EAAE,CAAC,CAAC,OAAO;QAClB,OAAO,EAAE,CAAC,CAAC,OAAuB;QAClC,SAAS,EAAE,CAAC,CAAC,SAAS;QACtB,MAAM,EAAE,CAAC,CAAC,CAAC,mBAAmB,IAAI,KAAK,CAAqB;QAC5D,cAAc,EAAE,CAAC,CAAC,eAAe;YAC/B,CAAC,CAAC;gBACE,kBAAkB,EAAE,CAAC,CAAC,eAAe,CAAC,oBAAoC;gBAC1E,aAAa,EAAE,CAAC,CAAC,eAAe,CAAC,cAAc;gBAC/C,MAAM,EAAE,CAAC,CAAC,eAAe,CAAC,MAAM;aACjC;YACH,CAAC,CAAC,SAAS;KACd,CAAC;AACJ,CAAC;AAED,gEAAgE;AAEhE,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACrC,IAAY,EACZ,QAAgB,EAChB,OAAgB;IAEhB,MAAM,YAAY,GAAG,OAAO,CAAC,CAAC,CAAC,4BAA4B,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;IAC1E,MAAM,QAAQ,GAAG,MAAM,WAAW,EAAE,CAAC,QAAQ,CAAC;QAC5C,KAAK,EAAE,KAAK;QACZ,SAAS,EAAE,MAAM;QACjB,YAAY,EAAE,iBAAiB;QAC/B,UAAU,EAAE,aAAa,QAAQ,cAAc,IAAI,GAAG,YAAY,EAAE;KACrE,CAAC,CAAC;IAEH,MAAM,IAAI,GAAG,QAAQ,CAAC,OAAO,CAAC;IAE9B,MAAM,SAAS,GAAG,cAAc,CAAC,IAAI,CAAC,CAAC;IACvC,MAAM,MAAM,GAAgB,EAAE,CAAC;IAC/B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,SAAS,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QAC1C,MAAM,KAAK,GAAG,cAAc,CAAC,SAAS,CAAC,CAAC,CAA4B,EAAE,CAAC,CAAC,CAAC;QACzE,IAAI,KAAK;YAAE,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAChC,CAAC;IAED,OAAO;QACL,MAAM;QACN,WAAW,EAAE,QAAQ,CAAC,WAAW,IAAI,CAAC;QACtC,YAAY,EAAE,QAAQ,CAAC,YAAY,IAAI,CAAC;KACzC,CAAC;AACJ,CAAC;AAED,gEAAgE;AAEhE,MAAM,CAAC,KAAK,UAAU,gBAAgB,CACpC,IAAY,EACZ,QAAgB,EAChB,MAAmB;IAEnB,MAAM,SAAS,GAAG,MAAM;SACrB,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,QAAQ,KAAK,CAAC,CAAC,QAAQ,KAAK,CAAC,CAAC,WAAW,kBAAkB,CAAC,CAAC,SAAS,EAAE,CAAC;SAClG,IAAI,CAAC,MAAM,CAAC,CAAC;IAEhB,MAAM,QAAQ,GAAG,MAAM,WAAW,EAAE,CAAC,QAAQ,CAAC;QAC5C,KAAK,EAAE,KAAK;QACZ,SAAS,EAAE,MAAM;QACjB,YAAY,EAAE,mBAAmB;QACjC,UAAU,EAAE,aAAa,QAAQ,cAAc,IAAI,0BAA0B,SAAS,EAAE;KACzF,CAAC,CAAC;IAEH,MAAM,IAAI,GAAG,QAAQ,CAAC,OAAO,CAAC;IAE9B,MAAM,gBAAgB,GAAG,cAAc,CAAC,IAAI,CAAmC,CAAC;IAChF,MAAM,aAAa,GAAG,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,KAAK,CAAC,CAAC;IAEzD,MAAM,aAAa,GAAwB,gBAAgB;SACxD,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,OAAO,CAAC,CAAC,OAAO,KAAK,QAAQ,IAAI,OAAO,CAAC,CAAC,SAAS,KAAK,QAAQ,CAAC;SAC7E,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,OAAO,CAAC,CAAC;SACjD,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;QACT,OAAO,EAAE,CAAC,CAAC,OAAiB;QAC5B,OAAO,EAAE,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC,CAAC,OAAiB,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAiB;QAC1F,SAAS,EAAG,CAAC,CAAC,SAAoB,CAAC,IAAI,EAAE;QACzC,MAAM,EAAE,KAAc;KACvB,CAAC,CAAC,CAAC;IAEN,OAAO;QACL,aAAa;QACb,WAAW,EAAE,QAAQ,CAAC,WAAW,IAAI,CAAC;QACtC,YAAY,EAAE,QAAQ,CAAC,YAAY,IAAI,CAAC;KACzC,CAAC;AACJ,CAAC;AAED,gEAAgE;AAEhE,MAAM,UAAU,uBAAuB,CACrC,IAAY,EACZ,QAAgB,EAChB,MAAmB,EACnB,gBAAqC;IAErC,+BAA+B;IAC/B,MAAM,aAAa,GAAmB,MAAM,CAAC,GAAG,CAAC,kBAAkB,CAAC,CAAC;IACrE,MAAM,oBAAoB,GAA0B,gBAAgB,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;QAC7E,OAAO,EAAE,CAAC,CAAC,OAAO;QAClB,OAAO,EAAE,CAAC,CAAC,OAAO;QAClB,SAAS,EAAE,CAAC,CAAC,SAAS;QACtB,mBAAmB,EAAE,CAAC,CAAC,MAAM;KAC9B,CAAC,CAAC,CAAC;IAEJ,0BAA0B;IAC1B,MAAM,MAAM,GAAG,qBAAqB,CAAC,IAAI,EAAE,QAAQ,EAAE,aAAa,EAAE,oBAAoB,CAAC,CAAC;IAE1F,+BAA+B;IAC/B,MAAM,aAAa,GAAG,MAAM,CAAC,aAAa,CAAC,GAAG,CAAC,sCAAsC,CAAC,CAAC;IACvF,MAAM,WAAW,GAAgB;QAC/B,gBAAgB,EAAE,MAAM,CAAC,KAAK,CAAC,iBAAiB;QAChD,WAAW,EAAE,MAAM,CAAC,KAAK,CAAC,YAAY;QACtC,aAAa,EAAE,MAAM,CAAC,KAAK,CAAC,aAAa;QACzC,eAAe,EAAE,MAAM,CAAC,KAAK,CAAC,gBAAgB;KAC/C,CAAC;IAEF,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,CAAC;AACxC,CAAC;AAED,gEAAgE;AAEhE,mFAAmF;AACnF,OAAO,EAAE,WAAW,EAAyB,MAAM,+BAA+B,CAAC;AAEnF,MAAM,CAAC,KAAK,UAAU,aAAa,CACjC,IAAY,EACZ,QAAgB,EAChB,OAAgB,EAChB,OAAqB;IAErB,IAAI,OAAO,GAAG,CAAC,CAAC;IAChB,IAAI,QAAQ,GAAG,CAAC,CAAC;IAEjB,0BAA0B;IAC1B,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,SAAS,EAAE,YAAY,EAAE,UAAU,EAAE,GAChE,MAAM,iBAAiB,CAAC,IAAI,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAC;IACnD,OAAO,IAAI,SAAS,CAAC;IACrB,QAAQ,IAAI,UAAU,CAAC;IAEvB,4BAA4B;IAC5B,MAAM,EAAE,aAAa,EAAE,gBAAgB,EAAE,WAAW,EAAE,QAAQ,EAAE,YAAY,EAAE,SAAS,EAAE,GACvF,MAAM,gBAAgB,CAAC,IAAI,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAC;IACjD,OAAO,IAAI,QAAQ,CAAC;IACpB,QAAQ,IAAI,SAAS,CAAC;IAEtB,+CAA+C;IAC/C,MAAM,EAAE,aAAa,EAAE,WAAW,EAAE,GAClC,uBAAuB,CAAC,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,gBAAgB,CAAC,CAAC;IAEpE,qEAAqE;IACrE,qEAAqE;IACrE,iEAAiE;IACjE,IAAI,OAAO;QAAE,OAAO,CAAC,UAAU,EAAE,CAAC;IAClC,IAAI,mBAAmB,GAAyB,EAAE,CAAC;IACnD,IAAI,CAAC;QACH,mBAAmB,GAAG,MAAM,eAAe,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAC;IACtF,CAAC;IAAC,MAAM,CAAC;QACP,iEAAiE;IACnE,CAAC;IAED,iEAAiE;IACjE,6DAA6D;IAC7D,MAAM,iBAAiB,GAAG,aAAa,CAAC,MAAM,CAC5C,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,OAAO,KAAK,MAAM,IAAI,CAAC,CAAC,MAAM,KAAK,QAAQ,CACnD,CAAC;IACF,IAAI,iBAAiB,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACjC,KAAK,MAAM,OAAO,IAAI,iBAAiB,EAAE,CAAC;YACxC,MAAM,aAAa,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;YACjE,IAAI,CAAC,aAAa;gBAAE,SAAS;YAC7B,gBAAgB,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE;gBAC9B,KAAK,EAAE;oBACL,EAAE,EAAE,aAAa,CAAC,EAAE;oBACpB,QAAQ,EAAE,aAAa,CAAC,QAAQ;oBAChC,QAAQ,EAAE,aAAa,CAAC,QAAQ;oBAChC,WAAW,EAAE,aAAa,CAAC,WAAW;oBACtC,SAAS,EAAE,aAAa,CAAC,SAAS;iBACnC;gBACD,YAAY,EAAE;oBACZ,OAAO,EAAE,MAAM;oBACf,SAAS,EAAE,OAAO,CAAC,SAAS;iBAC7B;gBACD,IAAI;gBACJ,QAAQ;gBACR,QAAQ,EAAE,SAAS,EAAE,wCAAwC;aAC9D,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,GAA+B,CAAC,CAAC,CAAC;QAClD,CAAC;IACH,CAAC;IAED,MAAM,MAAM,GAAG,aAAa,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,OAAO,KAAK,MAAM,CAAC,CAAC,MAAM,CAAC;IACtE,MAAM,MAAM,GAAG,aAAa,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,OAAO,KAAK,MAAM,CAAC,CAAC,MAAM,CAAC;IACtE,MAAM,OAAO,GAAG,aAAa,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,OAAO,KAAK,SAAS,CAAC,CAAC,MAAM,CAAC;IAE1E,mDAAmD;IACnD,MAAM,aAAa,GAAG,mBAAmB,CAAC,MAAM,CAAC;IAEjD,8DAA8D;IAC9D,+EAA+E;IAC/E,mEAAmE;IACnE,MAAM,kBAAkB,GAAG,aAAa,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,QAAQ,CAAC,CAAC,MAAM,CAAC;IACnF,MAAM,oBAAoB,GAAG,kBAAkB,GAAG,aAAa,CAAC;IAChE,MAAM,oBAAoB,GAAG,aAAa,CAAC,MAAM,GAAG,aAAa,CAAC;IAClE,MAAM,iBAAiB,GAAG,oBAAoB,GAAG,oBAAoB,CAAC;IACtE,MAAM,cAAc,GAAmB;QACrC,oBAAoB;QACpB,iBAAiB;QACjB,WAAW,EAAE,oBAAoB;QACjC,aAAa,EAAE,oBAAoB,GAAG,CAAC;YACrC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,oBAAoB,GAAG,oBAAoB,CAAC,GAAG,IAAI,CAAC,GAAG,EAAE;YACvE,CAAC,CAAC,CAAC;KACN,CAAC;IAEF,MAAM,MAAM,GAAuB;QACjC,MAAM;QACN,aAAa;QACb,WAAW,EAAE;YACX,GAAG,WAAW;YACd,mBAAmB,EAAE,aAAa;YAClC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SACrD;QACD,cAAc;QACd,MAAM;QACN,MAAM,EAAE,MAAM,GAAG,aAAa;QAC9B,OAAO;QACP,KAAK,EAAE,aAAa,CAAC,MAAM,GAAG,aAAa;QAC3C,SAAS,EAAE,MAAM,KAAK,CAAC,IAAI,OAAO,KAAK,CAAC,IAAI,aAAa,KAAK,CAAC;KAChE,CAAC;IAEF,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,OAAO,EAAE,YAAY,EAAE,QAAQ,EAAE,CAAC;AAClE,CAAC"}

@@ -8,2 +8,2 @@ import type { AssaySDKConfig, GenerateOptions, VerifyOptions, VerifiedCode, VerificationResult } from './types.js';

}
export type { AssaySDKConfig, GenerateOptions, VerifyOptions, VerifiedCode, VerificationResult, CodeClaim, ClaimVerdict, ClaimVerification, FormalStats, ProgressEvent, } from './types.js';
export type { AssaySDKConfig, GenerateOptions, VerifyOptions, VerifiedCode, VerificationResult, CodeClaim, ClaimVerdict, ClaimVerification, FormalStats, FormalCoverage, ProgressEvent, } from './types.js';

@@ -48,3 +48,22 @@ export interface AssaySDKConfig {

formalOverrides: number;
/** Findings from auto-generated learned rules (Phase 1 self-expanding). */
learnedRuleFindings?: number;
/** Noise filter stats from the ScanSession, if one was provided. */
noiseFilter?: {
filesScanned: number;
suppressedRules: string[];
fireCounts: Record<string, number>;
};
}
/** Tracks the LLM/formal verification ratio — the key metric for the 10x roadmap. */
export interface FormalCoverage {
/** Claims verified by formal verifier or learned rules (deterministic). */
formalClaimsVerified: number;
/** Claims verified only by LLM (non-deterministic). */
llmClaimsVerified: number;
/** Total claims processed. */
totalClaims: number;
/** Percentage of claims verified formally: (formalClaimsVerified / totalClaims) * 100. */
formalPercent: number;
}
export interface VerificationResult {

@@ -54,2 +73,4 @@ claims: CodeClaim[];

formalStats: FormalStats;
/** LLM/formal ratio tracking — measures progress toward 15/85 target. */
formalCoverage: FormalCoverage;
passed: number;

@@ -56,0 +77,0 @@ failed: number;

{
"name": "tryassay",
"version": "0.29.0",
"version": "0.30.1",
"description": "AI code verification CLI — find bugs that tests miss, linters ignore, and code review overlooks",

@@ -5,0 +5,0 @@ "type": "module",