@feng0531/dsh-code-review 0.29.2 → 0.30.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +11 -0
- package/README.md +5 -1
- package/package.json +1 -1
- package/src/review.mjs +6 -0
- package/src/risk-read-coverage.mjs +6 -0
- package/src/snapshot.mjs +4 -3
- package/src/tracked-context.mjs +6 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
# 变更记录
|
|
2
2
|
|
|
3
|
+
## 0.30.1
|
|
4
|
+
|
|
5
|
+
- 风险请求来源sourceReadCoverage记录本批生成阶段requested/read/unread;未读来源不能以无发现冒充完整核查。
|
|
6
|
+
- search/find与旧批次不算片段读取,报告partial限制,无新调用/权限,285测试通过。
|
|
7
|
+
- 完成主loop核心退出/定位/预算调度复核;agent/templates剩余核查仍待。
|
|
8
|
+
|
|
9
|
+
## 0.30.0
|
|
10
|
+
|
|
11
|
+
- Git自动直接上下文改512字面候选index探针,不枚举全tracked;普通tracked有效性及同probe竞态重核保持。
|
|
12
|
+
- 累计metadata1MiB/每probe64KiB、目录扩张拒绝;11000大树auto及指纹相关性回归,283测试通过。SVN索引未改。
|
|
13
|
+
|
|
3
14
|
## 0.29.2
|
|
4
15
|
|
|
5
16
|
- Git工作副本基线捕获不再全HEAD枚举,仅可审查变更基线路径literal非递归lookup,改名使用oldPath,删除/子模块边界不变。
|
package/README.md
CHANGED
|
@@ -4,7 +4,11 @@
|
|
|
4
4
|
|
|
5
5
|
## 当前状态
|
|
6
6
|
|
|
7
|
-
当前代码版本为 0.
|
|
7
|
+
当前代码版本为 0.30.1
|
|
8
|
+
|
|
9
|
+
风险计划sourceIds会记录本批生成读取片段/未读状态;未读产生partial覆盖限制。search/find不算读,读过片段也不证明条件已核查或缺陷成立,不自动添加调用。
|
|
10
|
+
|
|
11
|
+
Git自动直接依赖上下文仅查询从已捕获源码导出的最多512路径索引探针,不列整个tracked树;累计索引1MiB并在结束核对同探针指纹。SVN沿用原索引限额,此项不冒充SVN等价。
|
|
8
12
|
|
|
9
13
|
凭据路径硬排除:.env及其后缀(.env.example/.env.sample/.env.template例外)、.ssh目录、SSH私钥及.netrc/_netrc/.npmrc/.pypirc/.dockercfg,大小写无关;显式选择不可绕过,上下文/规则同样拒绝。仅路径策略,不保证普通文件无秘密。
|
|
10
14
|
|
package/package.json
CHANGED
package/src/review.mjs
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { correctAnchor } from './anchor-correction.mjs';
|
|
2
|
+
import { riskReadCoverage } from './risk-read-coverage.mjs';
|
|
2
3
|
import { BUSINESS_GROUP_INSTRUCTIONS, prepareBusinessGroups, validateBusinessGroups } from './business-groups.mjs';
|
|
3
4
|
import { RISK_PLAN_INSTRUCTIONS, validateRiskPlan } from './risk-plan.mjs';
|
|
4
5
|
import { prepareInteractionInput } from './interaction-input.mjs';
|
|
@@ -186,6 +187,11 @@ export async function reviewSnapshot(snapshot, model, options = {}) {
|
|
|
186
187
|
response = decoded;
|
|
187
188
|
}
|
|
188
189
|
}
|
|
190
|
+
if (options.enableRiskPlanning === true && !batch.synthesis) {
|
|
191
|
+
const entry=state.riskPlans.at(-1);
|
|
192
|
+
entry.sourceReadCoverage=riskReadCoverage(entry.risks,(state.retrievalAudit??[]).slice(auditStart),scope?.catalog()??[],input.id);
|
|
193
|
+
if(entry.sourceReadCoverage.some(c=>c.unreadSourceIds.length)) report.limitations.push({fileId:file.id,text:'风险计划请求的部分来源未在本次生成阶段读取;不能视为已核查风险条件或排除问题'});
|
|
194
|
+
}
|
|
189
195
|
const generated = batch.interaction ? validateInteractionFindings(response, input, batch.interaction, (state.retrievalAudit ?? []).slice(auditStart), scope.catalog()) : validateResponse(response, file, changes);
|
|
190
196
|
if (batch.synthesis) validateSynthesisReads(generated.findings, changes, (state.retrievalAudit ?? []).slice(auditStart), scope.catalog(), file.id, input.id);
|
|
191
197
|
if (!batch.interaction && state.sourceMode === 'change-windows') validateWindowFindings(generated.findings, batch.synthesis ? prepared.synthesis.windows : JSON.parse(batch.payload).windows, batch.synthesis === true);
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
// Read occurrence only, never complete source coverage or semantic verification.
|
|
2
|
+
export function riskReadCoverage(risks, audit, catalog, snapshotId){
|
|
3
|
+
const allowed=new Set(catalog.map(s=>s.id));
|
|
4
|
+
const read=new Set(audit.filter(r=>r.kind==='read'&&r.snapshotId===snapshotId&&r.stage==='retrieved-locally'&&Number.isSafeInteger(r.count)&&r.count>0&&allowed.has(r.sourceId)).map(r=>r.sourceId));
|
|
5
|
+
return risks.map(r=>({riskId:r.id,requestedSourceIds:[...r.sourceIds],readSourceIds:r.sourceIds.filter(id=>read.has(id)),unreadSourceIds:r.sourceIds.filter(id=>!read.has(id)),status:r.sourceIds.some(id=>!read.has(id))?'unread-sources':r.sourceIds.length?'fragments-read':'no-sources-requested',conditions:'unverified'}));
|
|
6
|
+
}
|
package/src/snapshot.mjs
CHANGED
|
@@ -39,9 +39,10 @@ export async function captureSnapshot(cwd, options = {}) {
|
|
|
39
39
|
if (!Array.isArray(rulePaths) || rulePaths.some(name => contextPaths.includes(name))) throw new Error('Rule paths must be separate from context paths');
|
|
40
40
|
const rules = await captureProjectRules(snapshot.root, rulePaths, { signal: options.signal, files: snapshot.files });
|
|
41
41
|
const context = [];
|
|
42
|
-
let autoContext, index;
|
|
42
|
+
let autoContext, index, indexOptions = options;
|
|
43
43
|
if (options.autoContext === true) {
|
|
44
|
-
|
|
44
|
+
if(type==='git'){const probes=new Set();contextCandidates(snapshot.files,[],{onProbe:paths=>{for(const p of paths)probes.add(p);if(probes.size>512)throw new Error('Git context lookup limit exceeded');}});indexOptions={...options,probePaths:[...probes]};}
|
|
45
|
+
index = await (type === 'git' ? gitContextIndex : svnContextIndex)(snapshot.root, indexOptions);
|
|
45
46
|
const plan = contextCandidates(snapshot.files, index.paths);
|
|
46
47
|
autoContext = { ...plan, candidates: plan.candidates.filter(c => !contextPaths.includes(c.path) && !rulePaths.includes(c.path)), capturedPaths: [] };
|
|
47
48
|
for (const candidate of autoContext.candidates) {
|
|
@@ -63,7 +64,7 @@ export async function captureSnapshot(cwd, options = {}) {
|
|
|
63
64
|
// Re-capture verifies both selected sides while collecting explicit context.
|
|
64
65
|
if ((await capture(cwd, options)).id !== snapshot.id) throw new Error('Snapshot changed while collecting context');
|
|
65
66
|
for (const item of context) if ((await readLocal(snapshot.root, item.path, options)).hash !== item.hash) throw new Error('Context changed during capture');
|
|
66
|
-
if (index && (await (type === 'git' ? gitContextIndex : svnContextIndex)(snapshot.root,
|
|
67
|
+
if (index && (await (type === 'git' ? gitContextIndex : svnContextIndex)(snapshot.root, indexOptions)).fingerprint !== index.fingerprint) throw new Error('Tracked context index changed during capture');
|
|
67
68
|
const verifiedRules = await captureProjectRules(snapshot.root, rulePaths, { signal: options.signal, files: snapshot.files });
|
|
68
69
|
if (JSON.stringify(verifiedRules) !== JSON.stringify(rules)) throw new Error('Rules changed during capture');
|
|
69
70
|
const result = { ...snapshot, context, ...(autoContext ? { autoContext } : {}), ...(rules.length ? { rules } : {}) };
|
package/src/tracked-context.mjs
CHANGED
|
@@ -13,7 +13,12 @@ export async function svnContextIndex(root, options = {}) {
|
|
|
13
13
|
}
|
|
14
14
|
// Index metadata only; never invokes network, diff drivers or source reads.
|
|
15
15
|
export async function gitContextIndex(root, options = {}) {
|
|
16
|
-
|
|
16
|
+
let bytes;
|
|
17
|
+
if(options.probePaths !== undefined){
|
|
18
|
+
if(!Array.isArray(options.probePaths)||options.probePaths.length>512||new Set(options.probePaths).size!==options.probePaths.length)throw new Error('Invalid Git context probes');
|
|
19
|
+
const chunks=[];let totalBytes=0;for(const name of [...options.probePaths].sort()){relativePath(name);const chunk=await checked('git',['--no-optional-locks','--literal-pathspecs','-c','core.fsmonitor=false','-c','core.untrackedCache=false','ls-files','--stage','-z','--',name],{...options,cwd:root,maxBytes:64*1024});const records=new TextDecoder('utf-8',{fatal:true}).decode(chunk).split('\0').filter(Boolean);for(const record of records)if(record.slice(record.indexOf('\t')+1)!==name)throw new Error('Git context probe expanded beyond literal path');totalBytes+=chunk.length;if(totalBytes>1024*1024)throw new Error('Tracked context index limit exceeded');chunks.push(chunk);}
|
|
20
|
+
bytes=Buffer.concat(chunks);if(bytes.length>1024*1024)throw new Error('Tracked context index limit exceeded');
|
|
21
|
+
}else bytes = await checked('git', ['--no-optional-locks','-c','core.fsmonitor=false','-c','core.untrackedCache=false','ls-files','--stage','-z'], {...options,cwd:root,maxBytes:1024*1024});
|
|
17
22
|
const records = new TextDecoder('utf-8',{fatal:true}).decode(bytes).split('\0').filter(Boolean);
|
|
18
23
|
if(records.length>10000)throw new Error('Tracked context index limit exceeded');
|
|
19
24
|
const entries=new Map();
|