@feng0531/dsh-code-review 0.77.0 → 0.78.0

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 CHANGED
@@ -2,9 +2,13 @@
2
2
 
3
3
  本文件介绍对使用者有意义的变化。安装与参数见 [README](README.md),本次发布正文见 [发布说明](RELEASE-NOTES.md)。历史提交和安装包保留在 [GitHub Releases](https://github.com/f-e-n-g-0531/dsh-code-review/releases);本文不把开发日志当作功能介绍。
4
4
 
5
+ ## 0.78.0 — SVN 标准关键字限定调用方
6
+
7
+ 限定调用方复用已验证的标准关键字来源捕获,只有命中原文进入上下文,未命中扫描及BASE核验仍计入共享预算。正文/属性/祖先复核保留,不支持配置或读取失败明确失败,不冒充完整调用覆盖。
8
+
5
9
  ## 0.77.0 — SVN 标准关键字自动依赖
6
10
 
7
- 自动依赖和显式链入口复用标准关键字BASE核验,保留原始展开证据,全部来源计入共享预算/截止;预览披露映射。不支持配置明确阻断,共享耗尽失败,限定调用方关键字扫描仍未开放。
11
+ 自动依赖和显式链入口复用标准关键字BASE核验,保留原始展开证据,全部来源计入共享预算/截止;预览披露映射。不支持配置明确阻断,共享耗尽失败,限定调用方关键字扫描仍未开放。518项串行回归、10项显式/依赖/预算组合与离线链专项、宿主/隔离安装及四矩阵CI通过;原始Actions/npm/Release包151,247字节逐字节一致,双哈希/清单/冻结说明通过。
8
12
 
9
13
  ## 0.76.3 — 修复 SVN 本地导航组合共享预算
10
14
 
@@ -32,7 +32,8 @@
32
32
 
33
33
  ## 当前验证结论
34
34
 
35
- - 最近完整回归:517项通过;v0.76.3 预算/关键字/EOL组合4项通过。
35
+ - 最近完整回归:518项通过;v0.77.0 显式/依赖/预算组合10项及离线链专项通过。
36
+ - v0.77.0 已发布自动标准关键字依赖及显式入口,复用BASE核验/身份复核,全部正文共享来源预算,原展开证据保留,不支持配置明确阻断;宿主/隔离安装和四矩阵CI通过,原始Actions/npm/Release包151,247字节逐字节一致,双哈希/清单/冻结说明通过。调用方关键字与其余全面SVN缺口继续。
36
37
  - v0.76.3 已发布本地导航共享1MiB来源/30秒截止BUG修复,显式/依赖/调用方扫描及关键字BASE核验共同扣费,共享耗尽传播失败;宿主/隔离安装和四矩阵CI通过,原始Actions/npm/Release包150,607字节逐字节一致,双哈希/清单/冻结正文通过。全面SVN其余缺口继续。
37
38
  - v0.76.2 已发布普通SVN变更来源URL/UUID/revision冻结和切换祖先阻断,预览execute来源变化拒绝且零发送;v0.76.1测试编码断言失败未发布、原标签保留,修正后四矩阵CI通过。原始Actions/npm/Release包149,274字节逐字节一致,双哈希/清单/冻结正文通过。共享预算超限独立BUG仍待修复。
38
39
  - v0.76.0 已发布独立显式标准关键字上下文,BASE展开/未展开核验计入原来源预算,原始展开正文为证据,预览元数据披露;宿主/隔离安装及四矩阵CI通过,原始Actions/npm/Release包147,614字节逐字节一致,双哈希/清单/冻结说明通过。自动依赖/调用方关键字与全面SVN其余缺口继续。
package/README.md CHANGED
@@ -158,7 +158,7 @@ dsh plugin add https://github.com/f-e-n-g-0531/dsh-code-review.git
158
158
 
159
159
  - `contextPaths`:手动加入辅助文件,不代表该文件本身完成了主审。SVN工作副本的未变更文件须通过离线正常节点/属性及来源检查;标准关键字在显式上下文和autoContext依赖中经过BASE核验后支持;自定义/固定宽度关键字、特殊或不可确认来源仍明确拒绝,已选择的可审查变更复用快照。
160
160
  - `autoContext:true`:自动补充最多三层文本依赖,不是全仓扫描或编译器分析。
161
- - `callerScopePaths`:在明确指定的非根目录中寻找字面引用。预览可能扫描该目录内不匹配的候选正文;只有捕获的匹配上下文随执行发送模型。不保证找到全部调用方。
161
+ - `callerScopePaths`:在明确指定的非根目录中寻找字面引用。预览可能扫描该目录内不匹配的候选正文;只有捕获的匹配上下文随执行发送模型。SVN标准关键字候选经BASE核验后支持,扫描及核验正文共同计费;不支持配置明确失败,不保证找到全部调用方。
162
162
  - `rulePaths`:最多四个 UTF-8 Markdown 或文本规则文件,不能同时列为上下文。
163
163
  - `businessRequirement`:帮助确定审查目标,本身不是缺陷证据。
164
164
 
package/RELEASE-NOTES.md CHANGED
@@ -1,14 +1,14 @@
1
- # v0.77.0 发布说明
1
+ # v0.78.0 发布说明
2
2
 
3
- ## SVN 自动依赖支持标准关键字文件
3
+ ## SVN 限定调用方支持标准关键字
4
4
 
5
- 工作副本使用 `autoContext:true` 时,可沿既有三层字面依赖捕获带标准 `svn:keywords` 的正常文件,手动上下文也可作为链入口。
5
+ `callerScopePaths` 扫描指定目录时,标准svn:keywords正常文件现在复用原始正文与BASE核验流程。只有命中侧的原始展开上下文进入模型,未命中正文不作为批准上下文保留。
6
6
 
7
- - 复用独立显式上下文的节点/祖先身份和BASE收缩核验;原始展开正文、字节哈希和行号不替换。
8
- - 工作正文、BASE展开及未展开核验正文计入同一1MiB来源预算,沿用共享30秒截止、20上下文名额和512精确探测。
9
- - 预览披露关键字映射元数据,执行仍需批准;模型输入使用原文,不把收缩视图当行为等价证明。
10
- - 自定义/固定宽度/不完整展开以候选阻断说明,不继续读取其依赖;共享预算耗尽仍直接失败。
7
+ - 预览披露已捕获关键字映射,不显示正文;仍须确认执行。
8
+ - 工作及BASE展开/未展开正文全部进入共享1MiB来源/30秒预算;扫描正文与总来源分别报告,核验不另开额度。
9
+ - 所有已扫描文件的节点/祖先指纹及正文哈希均复核,包含未命中候选。
10
+ - 自定义、固定宽度、不完整展开或读取失败明确失败,不把失败当作无引用;不保证完整调用覆盖。
11
11
 
12
- 用法:预览参数 `{"autoContext":true}`,或 `{"contextPaths":["src/version.h"],"autoContext":true}`。先检查捕获文件、阻断项和模型,再确认执行。限定调用方的关键字扫描本批未开放,特殊状态和分支来源等仍见 [支持矩阵](SVN-SUPPORT-MATRIX.md)。
12
+ 用法:先预览 `{"callerScopePaths":["src/clients"]}`,检查扫描范围、上下文和模型后确认。源码只读,不更新/修改工作副本,也不扩大网络范围。其余特殊状态、分支来源等见 [支持矩阵](SVN-SUPPORT-MATRIX.md)。
13
13
 
14
14
  完整操作见 [README](README.md)。
@@ -31,11 +31,19 @@
31
31
  | 历史两侧上下文与调用方 | v0.63.0 已发布显式 contextPaths 固定双侧来源及目标 rulePaths,UUID/URL/peg/operative revision/原字节与正文 hash 验证,禁止脏工作树与排除项回流。477 项串行回归、宿主、隔离安装、四矩阵 CI 与原包/npm/Release 127,355 字节、双哈希/清单/发布说明通过。v0.64.0 已发布 oldContextPaths 旧侧独有正文、SVN 来源专用校验、预览披露与选中删除身份复用;479 项串行回归、宿主、隔离安装、四矩阵 CI 与原包/npm/Release 127,879 字节、双哈希/清单/发布说明通过;本地开发了注入固定版本 lookup/read 能力的三层历史依赖导航核心,逐侧共享512探测/20候选/1MiB来源与上下文名额;排除项、旧侧限定和规则隔离测试通过。v0.68.0 已发布三层逐侧历史 autoContext,固定父目录有界清单 lookup,清单成功证明缺失、访问失败直接传播,真实旧/新版本链和脏工作树隔离通过。496 项串行回归、宿主、隔离安装、四矩阵 CI 与原包/npm/Release 134,056 字节、双哈希/清单/发布说明通过;本地新增逐侧固定 revision 调用方核心能力接口:共享256索引/128扫描/1MiB正文/512侧探测,命中侧才捕获正文,规则/变更排除与old-only限制保留,同侧命名调用声明导航测试通过;本地已接入固定 SVN 显式目录递归清单/同侧正文、统一30秒/512命令/元数据/正文预算,caller 与依赖共享精确探测及来源上限,真实集成专项13项通过;v0.69.0 已发布历史限定调用方和autoContext组合;502项串行全量回归、组合模型输入隔离、宿主/隔离安装、四矩阵 CI 通过。原包/npm/Release 136,030 字节、双哈希/清单/发布说明通过;字面发现仍不证明完整调用覆盖 | 第六批:逐侧固定版本,不能回退至工作副本 |
32
32
  | 真正 r7314→r7315 样本 | 用户未提供隔离副本路径;本地类似复现不是该样本验收 | 获得路径后只读生成预览,不自动发送模型 |
33
33
 
34
- ## 下一批:自动依赖标准关键字(v0.77.0 待发布)
34
+ ## 下一批:标准关键字限定调用方(v0.78.0 待发布)
35
+
36
+ - 调用方扫描复用captureSvnExplicitContext标准关键字核验,捕获原展开上下文,只保留命中正文;所有扫描身份/哈希复核。BASE两份核验进入原共享来源账本,sourceBytes与scannedBytes分别披露。
37
+ - `test/svn-caller-keywords.test.mjs` 离线真实复现:命中展开原文、非命中正文不进入快照/模型、来源计费、revision保留、预览0发送与稳定ID;既有调用方/预算/EOL组合4项通过,首次全量518通过/1失败:未改动Git历史链测试超时;保持原限制独立复跑5/5通过,重新全量519/519通过,原失败记录保留,尚未发布。
38
+ - 自定义/固定宽度/不完整展开或读取失败仍不能当作负搜索证据。特殊状态、有效继承mergeinfo与跨URL缺口继续。
39
+
40
+ ## 自动依赖标准关键字(v0.77.0 已发布)
41
+
42
+ 冻结提交 `fc482b55f81f44b354eb91f829f12a37fb9c89b0`;标签CI `38728355698`,唯一发布运行 `38729326841`,原工件 `11969183899`。原始Actions/npm/Release包151,247字节逐字节一致;SHA-256 `1a654d83860f74cf0709169e0a348e72a79c9ae46969e0639678f870008cb2cb`,SHA-512/清单/冻结发布说明核验通过。
35
43
 
36
44
  - 本地依赖链复用已验证的显式来源捕获,工作/BASE展开/未展开全部计费,共享1MiB/30秒;原始展开为唯一正文证据,三层/20名额/512探测保持。
37
45
  - 捕获后按同一允许关键字策略复核身份;显式入口元数据复用,不把节点允许策略泄漏给调用方扫描。
38
- - `test/svn-auto-keywords.test.mjs` 真实离线链复现:展开原文、BASE计费、后续依赖、unsupported候选阻断、预览0发送与模型原文/稳定ID;既有显式/依赖/预算10项通过。518项串行全量回归、10项显式/依赖/预算组合、离线链专项及宿主/隔离安装通过,尚未发布。
46
+ - `test/svn-auto-keywords.test.mjs` 真实离线链复现:展开原文、BASE计费、后续依赖、unsupported候选阻断、预览0发送与模型原文/稳定ID;既有显式/依赖/预算10项通过。518项串行全量回归、10项显式/依赖/预算组合、离线链专项及宿主/隔离安装、四矩阵标签及发布CI通过,正式发布工件核验完成。
39
47
  - 限定调用方标准关键字、自定义/固定宽度与其余SVN缺口仍未完成。
40
48
 
41
49
  ## 审查发现修复:本地组合共享预算(v0.76.3 已发布)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@feng0531/dsh-code-review",
3
- "version": "0.77.0",
3
+ "version": "0.78.0",
4
4
  "private": false,
5
5
  "license": "SEE LICENSE IN LICENSE",
6
6
  "publishConfig": { "access": "public", "registry": "https://registry.npmjs.org/" },
@@ -4,28 +4,29 @@ import {svnCallerNode,svnCallerProbes} from './svn-caller-node.mjs';
4
4
  import {callerCandidates} from './caller-candidates.mjs';
5
5
  import {readLocal} from './content.mjs';
6
6
  import {isSecretPath} from './secret-path.mjs';
7
- import {svnEolMapping} from './svn-eol-mapping.mjs';
7
+ import {captureSvnExplicitContext} from './svn-explicit-context.mjs';
8
+ import {createSvnNavigationBudget} from './svn-navigation-budget.mjs';
8
9
 
9
10
  // Called only by preview capture, never by a model retrieval request.
10
11
  export async function captureSvnCallers(root, files, scopes, options={}) {
11
12
  const deadline=AbortSignal.timeout(30000);
12
13
  const signal=options.signal?AbortSignal.any([options.signal,deadline]):deadline;
13
- const bounded={...options,signal,maxFileBytes:Math.min(options.maxFileBytes??256*1024,256*1024)};
14
+ const bounded={...options,signal,svnNavigationBudget:options.svnNavigationBudget??createSvnNavigationBudget(signal),maxFileBytes:Math.min(options.maxFileBytes??256*1024,256*1024)};
14
15
  const index=await svnCallerIndex(root,scopes,bounded);
15
16
  const changed=new Set(files.flatMap(f=>[f.path,...(f.oldPath?[f.oldPath]:[])]));
16
17
  const rules=new Set(options.rulePaths??[]),scanned=[],skipped=[];
17
- let scannedBytes=0;
18
+ let scannedBytes=0,sourceBytes=0;
18
19
  for(const name of index.paths){
19
20
  signal.throwIfAborted();
20
21
  if(changed.has(name)||rules.has(name)||isSecretPath(name)) {skipped.push({path:name,reason:'Excluded from caller discovery'});continue;}
21
22
  if(!/\.(mjs|cjs|js|jsx|ts|tsx|c|cc|cpp|cxx|h|hh|hpp|hxx|shader|hlsl|glsl|cginc|hlsli|compute)$/i.test(name)){skipped.push({path:name,reason:'Unsupported caller language'});continue;}
22
23
  if(scanned.length>=128)throw new Error('Caller scan file limit exceeded');
23
24
  // Read failures are fatal: unread bytes are not negative search evidence.
24
- const identity=await svnCallerNode(root,name,bounded);
25
- const content=await readLocal(root,name,bounded);
26
- bounded.svnNavigationBudget?.charge(content.bytes);scannedBytes+=content.bytes;
25
+ const captured=await captureSvnExplicitContext(root,[name],bounded);
26
+ const identity=captured.identities[0],content=captured.context[0];sourceBytes+=captured.sourceBytes;
27
+ scannedBytes+=content.bytes;
27
28
  if(scannedBytes>1024*1024)throw new Error('Caller scan byte limit exceeded');
28
- scanned.push({path:name,...content,...(identity.eolStyle?{eolMapping:svnEolMapping(content.text,identity.eolStyle)}:{}),nodeFingerprint:identity.fingerprint,revision:identity.revision});
29
+ scanned.push({...content,nodeFingerprint:identity.fingerprint,revision:identity.revision});
29
30
  }
30
31
  const requested=callerCandidates(files,scanned,[]).probePaths;
31
32
  const probes=await svnCallerProbes(root,requested,bounded);
@@ -33,7 +34,7 @@ export async function captureSvnCallers(root, files, scopes, options={}) {
33
34
  let verifiedBytes=0;
34
35
  const verify=async()=>{
35
36
  for(const source of scanned){
36
- const identity=await svnCallerNode(root,source.path,bounded);
37
+ const identity=await svnCallerNode(root,source.path,{...bounded,allowExplicitKeywords:true});
37
38
  if(identity.fingerprint!==source.nodeFingerprint)throw new Error('SVN caller node changed during capture');
38
39
  const again=await readLocal(root,source.path,bounded);verifiedBytes+=again.bytes;
39
40
  if(verifiedBytes>2*1024*1024)throw new Error('Caller verification byte limit exceeded');
@@ -45,5 +46,5 @@ export async function captureSvnCallers(root, files, scopes, options={}) {
45
46
  };
46
47
  await verify();
47
48
  const paths=new Set(plan.candidates.map(c=>c.fromPath));
48
- return {verify,context:scanned.filter(s=>paths.has(s.path)),discovery:{scopes:index.scopes,indexFingerprint:index.fingerprint,probeFingerprint:probes.fingerprint,scanned:scanned.map(({path,hash,bytes,nodeFingerprint,revision})=>({path,hash,bytes,nodeFingerprint,revision})),scannedBytes,skipped,candidates:plan.candidates,notice:plan.notice,incomplete:true,limitations:['Only supported literal references inside approved scopes were searched; no complete caller or semantic coverage.']}};
49
+ return {verify,context:scanned.filter(s=>paths.has(s.path)),discovery:{scopes:index.scopes,indexFingerprint:index.fingerprint,probeFingerprint:probes.fingerprint,scanned:scanned.map(({path,hash,bytes,nodeFingerprint,revision})=>({path,hash,bytes,nodeFingerprint,revision})),scannedBytes,sourceBytes,skipped,candidates:plan.candidates,notice:plan.notice,incomplete:true,limitations:['Only supported literal references inside approved scopes were searched; no complete caller or semantic coverage.']}};
49
50
  }
@@ -17,7 +17,7 @@ export async function captureSvnExplicitContext(root,names,options={}){
17
17
  if(base.text!==repository.text)throw new Error('SVN explicit BASE keyword mapping differs from repository text');
18
18
  const {text,...metadata}=current;keywordMapping={...metadata,status:'verified-line-view',notice:'Raw working text remains evidence; BASE contraction independently verified. Keyword view is not behavioral equivalence.'};
19
19
  }
20
- context.push({path:name,...content,...(keywordMapping?{keywordMapping}:{}),...(node.eolStyle?{eolMapping:svnEolMapping(content.text,node.eolStyle)}:{})});identities.push({path:name,fingerprint:node.fingerprint});
20
+ context.push({path:name,...content,...(keywordMapping?{keywordMapping}:{}),...(node.eolStyle?{eolMapping:svnEolMapping(content.text,node.eolStyle)}:{})});identities.push({path:name,fingerprint:node.fingerprint,revision:node.revision});
21
21
  }
22
22
  const verify=async()=>{for(const node of identities)if((await svnCallerNode(root,node.path,bounded)).fingerprint!==node.fingerprint)throw new Error('SVN explicit context identity changed during capture');signal.throwIfAborted();};
23
23
  return {context,verify,options:bounded,sourceBytes:bytes,identities};