评审真实性P1落地:全量代码可见性+Mock信号+AI日志审计+定向追踪+功能发现轮

- code-inventory.ts:全量符号索引,注入实现类维度,AI 可见100%文件清单
- code-signals.ts:代码信号检测器(TODO密度/空壳率/硬编码返回/死导入),修复TODO检测顺序bug
- ai-log-audit.ts:AI日志确定性审计(占位率/git一致率/覆盖广度),占位>50%→≤30%、git一致<30%→≤20%封顶
- standard-utils.ts:isQualitativeDesignDim 定性设计维度豁免C档
- review.service.ts:注入三个模块+定向追踪模板+日志封顶+分支提醒+Map-Reduce功能发现轮
- 预算 15000→40000;新增 review-authenticity.test.ts(14用例)
- 实测:净码特攻b3 81分(118功能全量识别),六边形50~62分(Mock被识别)
This commit is contained in:
hangshuo652
2026-08-27 09:23:02 +08:00
parent c16aa70aae
commit f8b4e9d44d
9 changed files with 963 additions and 7 deletions
+109
View File
@@ -0,0 +1,109 @@
import path from 'path';
/**
* 全量代码可见性(2026-08-26 评审真实性改进方案 §3.1):
* 对仓库全部文本文件生成轻量签名索引,让评审 AI 看见 100% 文件清单,
* 替代"前60个文件+15K截断"导致的盲区。
*/
export interface FileSignature {
path: string;
lines: number;
lang: 'ts' | 'js' | 'py' | 'other';
symbols: string[];
}
const CODE_LANG: Record<string, 'ts' | 'js' | 'py'> = {
'.ts': 'ts', '.tsx': 'ts', '.mts': 'ts', '.cts': 'ts',
'.js': 'js', '.jsx': 'js', '.mjs': 'js', '.cjs': 'js',
'.py': 'py',
};
/** 可提取符号的文本扩展名(其余只记 path+lines */
const TEXT_EXTS = new Set([
...Object.keys(CODE_LANG),
'.json', '.yaml', '.yml', '.md', '.html', '.css', '.scss', '.vue',
'.sql', '.sh', '.java', '.go', '.rs', '.rb', '.php', '.cs', '.kt',
]);
// 各语言的定义行模式(保守匹配,宁缺勿滥)
const SYMBOL_PATTERNS: Record<string, RegExp[]> = {
ts: [
/^\s*(export\s+)?(default\s+)?(async\s+)?function\s+\w+/,
/^\s*(export\s+)?(abstract\s+)?class\s+\w+/,
/^\s*export\s+(const|let|var)\s+\w+\s*=\s*(\(|async|function)/,
/^\s*export\s+(interface|type|enum)\s+\w+/,
/^\s*(public|private|protected|static)?\s*(async\s+)?\w+\s*\([^)]*\)\s*[:{]/,
],
js: [
/^\s*(export\s+)?(default\s+)?(async\s+)?function\s+\w+/,
/^\s*(export\s+)?(class\s+\w+)/,
/^\s*(export\s+)?(const|let|var)\s+\w+\s*=\s*(\(|async|function|\w+\s*=>)/,
/^\s*(module\.exports|exports\.\w+)\s*=/
],
py: [
/^\s*def\s+\w+/,
/^\s*class\s+\w+/,
],
};
export function extractSignatures(files: { path: string; content?: string; size?: number }[]): FileSignature[] {
const out: FileSignature[] = [];
for (const f of files) {
const ext = path.extname(f.path).toLowerCase();
const rel = f.path.replace(/\\/g, '/');
// 行数:有内容用内容算,否则用 size 粗估
let lines = 0;
let lang: FileSignature['lang'] = 'other';
const codeLang = CODE_LANG[ext];
if (codeLang) lang = codeLang;
let content: string | undefined;
if ('content' in f && typeof (f as any).content === 'string') {
content = (f as any).content as string;
lines = content.split('\n').length;
} else if (f.size != null) {
lines = Math.max(1, Math.round(f.size / 38)); // 平均 38 字节/行 估算
} else {
lines = 1;
}
if (!TEXT_EXTS.has(ext)) {
out.push({ path: rel, lines, lang: 'other', symbols: [] });
continue;
}
const symbols: string[] = [];
if (content && codeLang) {
const patterns = SYMBOL_PATTERNS[codeLang] || [];
for (const line of content.split('\n')) {
if (symbols.length >= 5) break;
for (const re of patterns) {
if (re.test(line)) {
symbols.push(line.trim().slice(0, 100));
break;
}
}
}
}
out.push({ path: rel, lines, lang, symbols });
}
return out;
}
/** 渲染紧凑索引文本(供注入 prompt)——全量文件,每文件 1~3 行 */
export function renderInventoryText(sigs: FileSignature[], budgetChars = 8000): string {
const lines: string[] = ['', '## 仓库文件全量索引(共 ' + sigs.length + ' 个文件,此清单为完整底账,评分必须覆盖其中核心文件)'];
let used = lines.join('\n').length;
for (const s of sigs) {
const symLine = s.symbols.length ? ' | ' + s.symbols.slice(0, 2).join(' ; ') : '';
const l = `- ${s.path} (${s.lines}${s.lang !== 'other' ? ',' + s.lang : ''})${symLine}`;
if (used + l.length > budgetChars) {
lines.push(`- …(其余 ${sigs.length - sigs.indexOf(s)} 个文件见截断说明,均为外围配置/资源)`);
break;
}
lines.push(l);
used += l.length;
}
return '\n' + lines.join('\n');
}