@feng0531/dsh-code-review 0.46.0 → 0.48.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,17 @@
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.48.0 — 剩余语言与格式的保守缺陷提示
6
+
7
+ 为 F#/R/Elm/Haskell/Julia/Nim/OCaml、Rego/Jsonnet/Nix、Solidity/Vyper、Verilog/SystemVerilog/VHDL、Zig、PO/POT、MyBatis 映射和 JS/TS/React 补充证据门控提示;关注资源求值、重入、位宽、分配器、占位符和渲染副作用,不照搬风格或错误绝对规则。有歧义后缀(裸 .v、.pot)不分类;.sol 与 F# 仅在命中语言签名时提示。
8
+
9
+ ## 0.47.0 — 命名空间成员调用导航
10
+
11
+ 批准的 JS/TS 命名空间导入可把独立成员调用指向唯一命名声明;历史逐侧隔离。写入、遮蔽、逃逸、间接调用与歧义拒绝提示;不是实例推导或完整调用图。不新增读取,超预算省略可选线索。424 项完整回归、实际宿主、17 项独立安装专项与四组 CI 通过;npm、Release 与原产物 98,883 字节一致,双哈希和清单通过。
12
+
5
13
  ## 0.46.0 — 历史调用方的同版本导航
6
14
 
7
- Git 历史调用方可分别对应 baseline 与 target 的唯一声明。批准正文、侧与版本必须对应,缺侧不回退;支持删除目标的旧侧导航,新增文件不产生旧侧声明线索。仍为保守语法导航,不新增读取或证明完整调用覆盖。
15
+ Git 历史调用方可分别对应 baseline 与 target 的唯一声明。批准正文、侧与版本必须对应,缺侧不回退;支持删除目标的旧侧导航,新增文件不产生旧侧声明线索。仍为保守语法导航,不新增读取或证明完整调用覆盖。416 项完整回归、实际宿主、13 项独立安装专项与四组 CI 通过;npm、Release 与原产物 97,482 字节一致,双哈希和清单通过。
8
16
 
9
17
  ## 0.45.1 — 调用方绑定的歧义与历史隔离
10
18
 
@@ -14,27 +14,29 @@
14
14
  | 范围保护 | 预览确认、版本与内容指纹、执行前复核、凭据路径排除 | 普通源码中一定没有秘密 |
15
15
  | 审查流程 | 风险规划、分组与回退、窗口分析、跨文件综合、候选反证 | 模型一定找到所有问题 |
16
16
  | 检索 | 只在批准文件中查找、搜索和读行段,记录来源与证据 | 找到名称就证明符号绑定 |
17
- | 引用方发现 | Git 工作区、历史与 SVN 的指定目录字面引用导航 | 完整函数调用图或动态分派 |
17
+ | 引用方发现 | Git 工作区、历史与 SVN 的指定目录字面引用导航;命名空间成员调用导航 | 完整函数调用图、类实例或动态分派 |
18
18
  | 历史上下文 | 显式旧版本文件;复用已批准删除依赖的旧正文 | 自动批准对应的新正文 |
19
19
  | 审查次数 | 默认一次,可选 1~3 次,各阶段共享预算 | 次数增加就保证效果提高 |
20
- | 文件指导 | 常用语言大类与五类协议指导 | 全部语言专用检查已验收 |
20
+ | 文件指导 | 常用语言大类、五类协议与剩余函数式/HDL/智能合约/格式的保守提示 | 全部语言专用检查已验收;提示不代替语义解析 |
21
21
 
22
22
  ## 剩余工作与优先级
23
23
 
24
24
  | 优先级 | 未完成项 | 验收重点 |
25
25
  |---|---|---|
26
- | 1 | 必要调用方、实例与成员级上下文 | 命名与默认定义位置、保守调用方线索已交付;实例、成员级与历史调用绑定仍待验收 |
26
+ | 1 | 实例与成员级上下文 | 命名/默认定义、保守调用方线索、历史同版本线索、命名空间成员导航已交付;类实例与继承仍待验收 |
27
27
  | 2 | 剩余历史来源与旧侧组合 | Git 历史与 SVN 三层链已交付;其他旧侧来源仍待验收 |
28
28
  | 3 | 其他旧侧独有来源 | 来源、版本、权限与删除场景保持一致 |
29
- | 4 | 剩余语言与路径专用指导 | 只提示具体缺陷和反证;不照搬风格禁令 |
29
+ | 4 | 剩余语言与路径专用指导 | 剩余主流函数式/HDL/智能合约语言已加入保守提示;Go/Python/Rust 与部分生命周期细化仍待验收 |
30
30
  | 5 | 剩余预算组合与端到端矩阵 | 分组、窗口、检索、多次审查的终态与覆盖一致 |
31
31
  | 后置 | 真实模型质量评测 | 保存真实输出,人工裁决,分开统计失败和未完成 |
32
32
 
33
- Git 工作区、Git 历史两侧与 SVN 当前侧支持安全的三层字面依赖链;手动批准上下文可以作为入口。命名与默认导入支持批准正文中的唯一声明位置。这些能力不是完整语义绑定。调用方语法绑定的 v0.45.0 存在逐条导入歧义与历史发现元数据混侧错误,不计作安全对齐;v0.45.1 已修正并正式交付;历史调用方的同版本语法导航已实现,待完整复验与正式交付;缺侧不用另一侧替代。
33
+ Git 工作区、Git 历史两侧与 SVN 当前侧支持安全的三层字面依赖链;手动批准上下文可以作为入口。命名与默认导入支持批准正文中的唯一声明位置。这些能力不是完整语义绑定。调用方语法绑定的 v0.45.0 存在逐条导入歧义与历史发现元数据混侧错误,不计作安全对齐;v0.45.1 已修正并正式交付;历史调用方的同版本语法导航已随 v0.46.0 正式交付;命名空间成员调用导航已随 v0.47.0 正式交付;写入、遮蔽、逃逸、复合/间接调用、转义名与 JSX 文本不猜测,可选线索超预算时记录省略而不阻断源码审查。
34
34
 
35
35
  ## 当前验证结论
36
36
 
37
- - 最近完整回归:413 项通过。
37
+ - 最近完整回归:424 项通过。
38
+ - v0.47.0 已正式交付命名空间成员调用导航。424 项回归、实际宿主、17 项独立安装专项和四组 CI 通过;npm、Release 与原包 98,883 字节一致,SHA-256、SHA-512 与清单核验通过。
39
+ - v0.46.0 已正式交付历史同版本调用方导航与可选线索预算回退。416 项回归、实际宿主、13 项独立安装专项和四组 CI 通过;npm、Release 与原包 97,482 字节一致,SHA-256、SHA-512 与清单核验通过。
38
40
  - v0.45.1 已正式交付保守的调用方语法线索及两项边界修正。413 项回归、实际宿主、10 项独立安装专项和四组 CI 通过;npm、Release 与原包的 96,700 字节一致,SHA-256、SHA-512 与清单核验通过。v0.45.0 已发布但存在逐条导入歧义与历史元数据混侧错误,由 0.45.1 替代,不计作安全对齐。
39
41
  - 实际 DSH 工具运行时检查通过;模型响应受控,不是质量成绩。
40
42
  - v0.39.0 独立安装包的真实 Git、服务与预算测试:5 项通过。
@@ -26,22 +26,19 @@
26
26
  - 历史重命名按删除和新增表达;区间比较使用明确端点,不自动计算合并基点。
27
27
  - 参考中的风格偏好、无条件性能建议和错误绝对规则不作为缺陷标准。
28
28
 
29
- ## 语言指导的剩余工作
29
+ ## 语言指导的现状与剩余细化
30
30
 
31
- | 类型 | 剩余重点 |
32
- |---|---|
33
- | F#、R、Elm、Haskell、Julia、Nim、OCaml | 仍以通用指导为主;需处理各语言的资源、求值、数据与运行契约 |
34
- | Rego、Jsonnet、Nix | 策略未定义值、对象合并、构建依赖和具体运行版本 |
35
- | Solidity、Vyper | 部署版本、存储布局、重入、外部调用与实际消费者 |
36
- | Verilog/SystemVerilog、VHDL | 调度、位宽、有符号值、时钟跨域和目标工具支持 |
37
- | Zig | 分配器、异常清理、借用寿命和构建模式 |
38
- | PO/POT、MyBatis 映射文件 | 文本身份、占位符、复数规则、绑定值与动态 SQL |
39
- | JS/TS/React | hook、异步清理、渲染副作用的实际缺陷与反证 |
40
- | Go、Python、Rust | 版本前提、并发、宏求值和调用契约细化 |
41
- | Objective-C、Swift、MATLAB、ArkTS、PHP、Kotlin、Java、C/C++ | 大类指导已有;部分生命周期、并发、版本和调用场景需具体测试 |
42
- | 模板、依赖清单、基础设施 | 转义方式、引擎版本、消费者与部署约束,不按文件名推定行为 |
31
+ 已加入保守缺陷提示(只提示具体缺陷和反证,不照搬风格偏好、无条件性能建议或错误绝对规则):
43
32
 
44
- Protobuf、Thrift、Cap’n Proto、GraphQL、Prisma 的专用指导已交付。不把协议检查提示当作解析器、迁移执行结果或缺陷证据。
33
+ - F#、R、Elm、Haskell、Julia、Nim、OCaml:资源、求值、数据与运行契约提示。
34
+ - Rego、Jsonnet、Nix:未定义值、合并、构建依赖与版本提示。
35
+ - Solidity、Vyper:重入、外部调用、存储布局与部署版本提示。
36
+ - Verilog/SystemVerilog、VHDL:调度、位宽、有符号、跨时钟域与工具支持提示。
37
+ - Zig:分配器、错误清理、溢出/对齐与构建模式提示。
38
+ - PO/POT、MyBatis 映射:文本身份、复数、占位符、绑定值与动态 SQL 提示。
39
+ - JS/TS/React:hook、异步清理与渲染副作用提示。
40
+
41
+ Protobuf、Thrift、Cap’n Proto、GraphQL、Prisma 的专用指导已交付。以下仍以通用或大类指导为主,需进一步细化:Go/Python/Rust 的版本前提与调用契约,Objective-C/Swift/Kotlin/Java/C/C++ 的具体生命周期与并发场景,以及模板、依赖清单与基础设施的引擎版本和消费者约束。全部提示不是语义解析、迁移执行结果或缺陷证据。
45
42
 
46
43
  ## 固定参考文件索引
47
44
 
package/README.md CHANGED
@@ -67,7 +67,7 @@ DSH 的只读代码审查插件。它比较 Git 或 SVN 中的代码变更,使
67
67
 
68
68
  commit 与 baseRevision/targetRevision 不能同时使用。oldContextPaths 只能用于 Git 历史审查。
69
69
 
70
- 自动上下文和引用方发现是保守的文本导航,不是编译器分析。动态导入、宏、重载、继承和实例方法可能无法解析;找到引用不等于证明函数实际被调用。历史调用方的命名导入线索逐侧对应相同版本声明,标明 side/revision;缺少该版本的批准正文不回退另一侧。Git 历史可沿修改前后两侧分别捕获最多三层依赖;SVN 可沿安全的当前工作副本节点取得最多三层依赖,不读取 SVN 历史。已批准正文中的唯一命名声明和唯一 default 导出会标出位置;同名、重导出或跨侧内容不猜测。
70
+ 自动上下文和引用方发现是保守的文本导航,不是编译器分析。动态导入、宏、重载、继承和实例方法可能无法解析;找到引用不等于证明函数实际被调用。历史调用方的命名导入线索逐侧对应相同版本声明,标明 side/revision;缺少该版本的批准正文不回退另一侧。Git 历史可沿修改前后两侧分别捕获最多三层依赖;SVN 可沿安全的当前工作副本节点取得最多三层依赖,不读取 SVN 历史。已批准正文中的唯一命名声明和唯一 default 导出会标出位置;同名、重导出或跨侧内容不猜测。命名空间导入后的独立成员调用可导航至唯一命名声明;写入、逃逸或间接调用不推定绑定,不是实例类型分析。
71
71
 
72
72
  ## 如何读报告
73
73
 
package/RELEASE-NOTES.md CHANGED
@@ -1,20 +1,16 @@
1
- # v0.46.0 发布说明
1
+ # v0.48.0 发布说明
2
2
 
3
- ## Git 历史调用方的同版本导航
3
+ ## 剩余语言与格式的保守缺陷提示
4
4
 
5
- callerScopePaths 捕获的 JS/TS 命名导入与独立调用位置,可以分别对应 baseline 与 target 中的唯一声明。审查输入标明 side、revision、targetRevision 与 contextSide,调用方正文和声明必须来自同一版本。
5
+ 为 F#、R、Elm、Haskell、Julia、Nim、OCaml、Rego、Jsonnet、Nix、Solidity、Vyper、Verilog/SystemVerilog/VHDL、Zig、PO/POT、MyBatis 映射和 JS/TS/React 补充了审查提示。提示只针对具体缺陷和反证,不照搬风格偏好、无条件性能建议或错误绝对规则。
6
6
 
7
- 旧侧只使用已批准 oldText 与 oldRevision;新侧只使用已批准 text 与 revision。缺失或版本不符时不提供线索,不回退另一侧。删除目标的旧声明仍可用于旧侧导航;新增文件不产生旧侧声明线索。
7
+ Rego、Jsonnet、Nix 关注未定义值、对象合并和构建依赖;Solidity/Vyper 关注重入、外部调用和存储布局;硬件描述语言关注调度、位宽和跨时钟域;Zig 关注分配器与错误清理;PO/MyBatis 关注文本身份、占位符与绑定值;React 关注 hook 清理和渲染副作用。
8
8
 
9
- ## 保持的限制
9
+ ## 限制
10
10
 
11
- - 不新增扫描或读取权限,两侧继续共享原有路径、正文、数量与时间预算。
12
- - 每条导入须独立唯一解析;排除、歧义、动态调用和未批准正文不提供绑定。
13
- - 仅为语法导航,不证明执行可达、动态分派、因果或完整调用覆盖。窗口初始输入仍须按相应版本 catalog 读取原文,线索不是证据。
14
- - 可选调用线索超出输入预算时会省略并记录回退原因和数量,不挤掉批准正文、不提高预算,也不阻断原本可审查的窗口。
15
-
16
- ## 验证范围
17
-
18
- 覆盖真实 Git 两侧声明位置、删除目标、缺失旧正文、错误版本、添加文件和脏工作区隔离;完整回归、实际宿主、独立安装与四矩阵 CI 作为正式发布条件。模型响应受控,不是效果成绩。
11
+ - 提示不是语义解析、迁移执行结果或缺陷证据,也不授予读取权限。
12
+ - MyBatis 只在 XML 头部有限范围的 mapper 签名命中时选择该提示,其余 XML 仍是通用配置提示。
13
+ - 有歧义的后缀不猜测:裸 .v、.pot 不分类;.sol 仅在命中 Solidity 签名、.fs/.fsi/.fsx 仅在命中 F# 签名时给出对应提示,否则保持通用。
14
+ - Go、Python、Rust 及部分语言的生命周期与并发细化仍保留后续验收。
19
15
 
20
16
  使用方法见 [README](README.md)。
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@feng0531/dsh-code-review",
3
- "version": "0.46.0",
3
+ "version": "0.48.0",
4
4
  "private": false,
5
5
  "license": "SEE LICENSE IN LICENSE",
6
6
  "publishConfig": { "access": "public", "registry": "https://registry.npmjs.org/" },
@@ -1,5 +1,6 @@
1
1
  import { contextCandidates } from './context-candidates.mjs';
2
2
  import { directImportBindings } from './import-bindings.mjs';
3
+ import { namespaceImportBindings } from './namespace-bindings.mjs';
3
4
  import { importedCallSites, indexStandaloneCalls } from './import-call-sites.mjs';
4
5
  import { relativePath } from './content.mjs';
5
6
  import { isSecretPath } from './secret-path.mjs';
@@ -17,7 +18,7 @@ function callerBindings(fromPath, targetPath, scannedText, targetText, resolutio
17
18
  const candidates = path.posix.extname(target) ? [target] : [target, ...importExtensions.map(ext => target + ext), ...importExtensions.map(ext => target + '/index' + ext)];
18
19
  const matches = candidates.filter(candidate => resolutionPaths.has(candidate));
19
20
  if (matches.length !== 1 || matches[0] !== targetPath) continue;
20
- result.push(...directImportBindings(lines, i + 1, importedCallSites(lines, i + 1, callIndex), targetText));
21
+ result.push(...directImportBindings(lines, i + 1, importedCallSites(lines, i + 1, callIndex), targetText), ...namespaceImportBindings(lines, i + 1, targetText));
21
22
  }
22
23
  return result.slice(0, 20);
23
24
  }
@@ -15,17 +15,36 @@ const checklists = {
15
15
  cpp: ['Check ownership, dangling references, double release and allocator/object lifetime across all exits.', 'Check stop publication, synchronization, join completion and remaining users before teardown; source order alone is not a happens-before proof.', 'Check exception/early-return resource cleanup, iterator invalidation, bounds, integer conversion and undefined behavior.', 'Do not assume macro expansion, overload binding, build flags or virtual dispatch targets without evidence.'],
16
16
  csharp: ['Check IDisposable/async disposal, cancellation propagation and completion before releasing shared state.', 'Check nullability and actual runtime guards, event subscription lifetime, deferred enumeration and captured mutable state.', 'Check async exception observation, synchronization and thread-affinity; do not assume await establishes unrelated ordering.'],
17
17
  javascript: ['Check null/undefined contract changes, promise rejection handling and missing await or cleanup on failure.', 'Check event/timer/listener lifecycle, closure state, cancellation races and concurrent updates.', 'Check untrusted input at injection/authentication boundaries; do not report style or infer TypeScript runtime checks.'],
18
- configuration: ['Check changed configuration keys against consumer/schema evidence, type/unit/default mismatches and compatibility.', 'Check exposed secrets, privilege expansion and unsafe interpolation only with concrete supported evidence.']
18
+ configuration: ['Check changed configuration keys against consumer/schema evidence, type/unit/default mismatches and compatibility.', 'Check exposed secrets, privilege expansion and unsafe interpolation only with concrete supported evidence.'],
19
+ hdl: ['Check scheduling/sensitivity lists, blocking versus nonblocking assignment, bit width, sign extension and truncation against actual values and consumers.', 'Check clock/reset domain crossings, metastability handling and combinational loops with concrete timing and tool-support evidence.', 'Check case exhaustiveness, inferred latches, x/z propagation and parameter/define overrides; do not assume simulator or synthesizer outcomes.'],
20
+ solidity: ['Check state update ordering, reentrancy before external calls, balance/allowance accounting and storage layout across upgrades.', 'Check msg.sender/msg.value trust, delegatecall/selfdestruct exposure, overflow/underflow and access control against actual callers.', 'Check compiler/pragma version and deployed bytecode consumers; do not invent chain-specific behavior.'],
21
+ vyper: ['Check reentrancy, external contract calls, integer bounds and payable/value handling against the actual call graph.', 'Check storage layout and compiler/version compatibility with deployed consumers; do not assume ABI or gas semantics.'],
22
+ rego: ['Check undefined values, iteration over undefined, negation under open-world semantics and complete rules against actual inputs.', 'Check conflict resolution, package/import bindings and data-document mismatches; do not invent the document schema.'],
23
+ jsonnet: ['Check self/object inheritance and lazy evaluation changes, array/object merges, and error paths for missing fields.', 'Check runtime/stdlib version and import paths against captured build evidence; do not assume evaluation order.'],
24
+ nix: ['Check fetched/pinned sources (hash, revision), sandbox breaks, impurity and build-time network/download against actual derivations.', 'Check attribute/override precedence, fixed-output hashes and eval/builder versions; do not invent Nixpkgs defaults.'],
25
+ zig: ['Check allocator ownership and leak paths, error cleanup with defer/errdefer, and pointers invalidated after reallocation.', 'Check integer overflow/wrapping intent, alignment, sentinel slices and build-mode differences tied to actual callers.'],
26
+ fsharp: ['Check laziness, sequence/async resource cleanup, tail-call assumptions and partial application against actual evaluation.', 'Check null/option/Result handling, pattern-match exhaustiveness and disposal across all exits of the changed path.'],
27
+ rlang: ['Check vector recycling/length, NA/NaN propagation, type coercion and factor levels against actual input contracts.', 'Check lazy evaluation, environment capture, connection/file cleanup and package or seed version effects.'],
28
+ elm: ['Check model update handling, Decoder failures, impossible patterns and command/task cancellation or ordering.', 'Check state transitions and side-effect boundaries against the changed ports/flags contract.'],
29
+ haskell: ['Check laziness, bottoms/exceptions, partial functions and strictness-driven space/time changes against callers.', 'Check IO/resource cleanup with bracket, async exception safety and pure/mutable boundaries in the changed transaction.'],
30
+ julia: ['Do not report type instability without a measurement; check allocation, closure capture and dispatch changes only against evidence.', 'Check broadcast/scalar semantics, bounds, shared mutable state and task/coroutine cleanup with concrete callers.'],
31
+ nim: ['Check nil/exception paths, gc versus manual memory ownership, generics/compile-time evaluation and proc mutation side effects.', 'Check integer overflow, indexing and FFI ownership against actual callers and backend; do not infer runtime checks from declarations.'],
32
+ ocaml: ['Check option/result/exception paths, tail-recursion assumptions, physical versus structural equality and resource cleanup.', 'Check GADT/exhaustiveness and FFI lifetime; do not invent a module signature that is not captured.'],
33
+ po: ['Check msgid/msgstr identity, plural-forms rules, format specifiers and their order, and encoding/NFC identity.', 'Check untranslated/fuzzy markers and placeholder mismatch against the actual extraction catalog.'],
34
+ mybatis: ['Check parameter bound type, #{} versus ${}, collection iteration and NULL semantics in dynamic SQL against the mapper interface.', 'Check result map column/type, statement id uniqueness and cache invalidation; do not infer database behavior that is not captured.'],
35
+ react: ['Check effect cleanup, stale closures over dependencies, and async setState after unmount or on cancelled requests.', 'Check list keys, event-handler reuse and render-phase side effects; do not classify a memo re-render as a defect without evidence.']
19
36
  };
20
37
  export function defectGuidance(filePath, sourceText = '') {
21
38
  const lower = filePath.toLowerCase(), protocol = protocolGuidance(filePath);
22
- const first = typeof sourceText === 'string' ? sourceText.slice(0,8192).split('\n').map(s=>s.trim()).find(Boolean) ?? '' : '';
23
- const sniffed = lower.endsWith('.m') ? (/^(?:#(?:import|include|pragma|if|define)\b|@(?:import|interface|implementation|class|protocol)\b|\/\/|\/\*)/.test(first) ? 'objc' : 'matlab') : null;
24
- const specialized = /\.(shader|hlsl|glsl|cginc|hlsli|compute)$/.test(lower) ? 'shader' : sniffed ?? (/\.mm$/.test(lower) ? 'objc' : /\.swift$/.test(lower) ? 'swift' : null) ?? (/\.java$/.test(lower) ? 'java' : /\.go$/.test(lower) ? 'go' : /\.(py|pyi)$/.test(lower) ? 'python' : /\.rs$/.test(lower) ? 'rust' : /\.(kt|kts)$/.test(lower) ? 'kotlin' : /\.(php|phtml)$/.test(lower) ? 'php' : null);
39
+ const head = typeof sourceText === 'string' ? sourceText.slice(0,8192) : '';
40
+ const first = head.split('\n').map(s=>s.trim()).find(Boolean) ?? '';
41
+ const sniffed = lower.endsWith('.m') ? (/^(?:#(?:import|include|pragma|if|define)\b|@(?:import|interface|implementation|class|protocol)\b|\/\/|\/\*)/.test(first) ? 'objc' : 'matlab') : lower.endsWith('.xml') && /<mapper\s+namespace\s*=|<!DOCTYPE\s+mapper\b|<(?:select|insert|update|delete)\s+id\s*=/.test(head) ? 'mybatis' : lower.endsWith('.sol') && /\/\/\s*SPDX-License-Identifier|pragma\s+solidity|\b(?:contract|interface|library)\s+[A-Za-z_$][A-Za-z0-9_$]*|\b(?:function|constructor|fallback|receive|modifier)\s*\(/.test(head) ? 'solidity' : /\.(fs|fsi|fsx)$/.test(lower) && /\blet\b|\bmodule\b|\bnamespace\b|\bopen\b|\btype\b|\[</.test(head) ? 'fsharp' : null;
42
+ const specialized = /\.(shader|hlsl|glsl|cginc|hlsli|compute)$/.test(lower) ? 'shader' : sniffed ?? (/\.mm$/.test(lower) ? 'objc' : /\.swift$/.test(lower) ? 'swift' : /\.(vh|sv|svh|vhd|vhdl)$/.test(lower) ? 'hdl' : /\.vy$/.test(lower) ? 'vyper' : /\.rego$/.test(lower) ? 'rego' : /\.(jsonnet|libsonnet)$/.test(lower) ? 'jsonnet' : /\.nix$/.test(lower) ? 'nix' : /\.zig$/.test(lower) ? 'zig' : /\.r$/.test(lower) ? 'rlang' : /\.elm$/.test(lower) ? 'elm' : /\.(hs|lhs)$/.test(lower) ? 'haskell' : /\.jl$/.test(lower) ? 'julia' : /\.nim$/.test(lower) ? 'nim' : /\.(ml|mli)$/.test(lower) ? 'ocaml' : /\.po$/.test(lower) ? 'po' : null) ?? (/\.java$/.test(lower) ? 'java' : /\.go$/.test(lower) ? 'go' : /\.(py|pyi)$/.test(lower) ? 'python' : /\.rs$/.test(lower) ? 'rust' : /\.(kt|kts)$/.test(lower) ? 'kotlin' : /\.(php|phtml)$/.test(lower) ? 'php' : null);
25
43
  const language = protocol?.language ?? specialized ?? (/\.(c|cc|cpp|cxx|h|hh|hpp|hxx)$/.test(lower) ? 'cpp' : lower.endsWith('.cs') ? 'csharp' : /\.(js|jsx|mjs|cjs|ts|tsx)$/.test(lower) ? 'javascript' : /\.(json|yaml|yml|toml|ini|xml|properties)$/.test(lower) ? 'configuration' : 'generic');
26
44
  const fileType = /^\.github\/workflows\/.+\.(yaml|yml)$/.test(lower) ? 'github-workflow' : /(?:^|\/)(package\.json|cargo\.toml|pom\.xml|build\.gradle|composer\.json)$/.test(lower) ? 'dependency-manifest' : undefined;
27
45
  const purpose = fileType ?? (/\.(ftl|ftlh|ftlx|hbs|mustache|jinja2|j2|pug|astro)$/.test(lower) ? 'template' : /\.(tf|hcl|tfvars|bicep|nix)$/.test(lower) ? 'infrastructure' : /\.(proto|thrift|capnp|graphql|gql|prisma)$/.test(lower) ? 'protocol-schema' : undefined);
28
46
  const purposeChecks = purpose === 'template' ? ['Check escaping for the actual HTML/attribute/URL/script context, untrusted raw output and template execution boundaries.', 'Check missing/null variables and changed conditional branches against captured callers; do not invent engine defaults.'] : purpose === 'infrastructure' ? ['Check privilege/network exposure, secret/state handling and untrusted interpolation against actual provider semantics.', 'Check replacement/deletion, dependency order and changed defaults for concrete data loss or service disruption; configuration is not an applied plan.'] : purpose === 'protocol-schema' ? protocol.checks : [];
47
+ const reactChecks = /\.(jsx|tsx)$/.test(lower) ? checklists.react : [];
29
48
  const typeChecks = fileType === 'github-workflow' ? ['Check event trust boundaries, token permissions and fork/PR-controlled inputs before privileged commands or secret exposure.', 'Check expression interpolation into shell commands, checkout/ref provenance, artifact/cache trust and required job dependency gates.'] : fileType === 'dependency-manifest' ? ['Check changed dependency versions/features against consumers, lock/resolution evidence and runtime compatibility; do not invent a vulnerability from a package name.', 'Check executable install/build hooks, publish/export entry points and platform constraints for concrete changed behavior.'] : [];
30
- return { version:5, language, ...(sniffed ? {selection:'bounded-first-nonblank-line-hint'} : {}), ...(purpose ? {fileType:purpose} : {}), checks:[...common,...(checklists[language] ?? []),...typeChecks,...purposeChecks], notice:'Guidance is not evidence, a semantic parser, a style rule or permission to read outside the snapshot.' };
49
+ return { version:5, language, ...(sniffed ? {selection:'bounded-first-nonblank-line-hint'} : {}), ...(purpose ? {fileType:purpose} : {}), checks:[...common,...(checklists[language] ?? []),...reactChecks,...typeChecks,...purposeChecks], notice:'Guidance is not evidence, a semantic parser, a style rule or permission to read outside the snapshot.' };
31
50
  }
@@ -1,4 +1,5 @@
1
1
  import path from 'node:path';
2
+ import { namespaceImportBindings } from './namespace-bindings.mjs';
2
3
  import { directImportBindings, namedImportDefinitions, defaultImportDefinition } from './import-bindings.mjs';
3
4
  import { importedCallSites, indexStandaloneCalls } from './import-call-sites.mjs';
4
5
 
@@ -38,7 +39,7 @@ export function inferImportRelations(files) {
38
39
  if (matches.length !== 1 || matches[0].eligibility !== 'reviewable' || matches[0].id === file.id) continue;
39
40
  const callSites = importedCallSites(lines, i + 1, callIndex);
40
41
  const targetText = (side === 'old' ? matches[0].left : matches[0].right)?.text;
41
- const bindingHints = directImportBindings(lines, i + 1, callSites, targetText);
42
+ const bindingHints = [...directImportBindings(lines, i + 1, callSites, targetText), ...namespaceImportBindings(lines, i + 1, targetText)].slice(0,20);
42
43
  const definitionHints = [...namedImportDefinitions(lines[i], targetText), ...defaultImportDefinition(lines[i], targetText)];
43
44
  edges.push({ from: file.id, to: matches[0].id, reason: 'relative-import:' + side + ':L' + (i + 1), side, line: i + 1, specifier: match[2], callSites, bindingHints, definitionHints });
44
45
  if (callSites.length && edges.length < 2000) edges.push({ from: file.id, to: matches[0].id, reason: 'imported-call-syntax:' + side + ':L' + callSites.map(c => c.line).join(','), side, callSites });
@@ -17,7 +17,15 @@ export function prepareInteractionInput(snapshot, group, measure) {
17
17
  if (group.fileIds.filter(id => { const file = snapshot.files.find(f => f.id === id); return file.left.text !== file.right.text; }).length < 2) return { status: 'not-applicable', fileIds: [...group.fileIds] };
18
18
  if (files.some(f => f.unavailable)) return { status: 'blocked', reason: 'exact-edit-analysis-unavailable', fileIds: [...group.fileIds] };
19
19
  const contextRelations = group.fileIds.flatMap(id=>inferContextRelations(snapshot,snapshot.files.find(f=>f.id===id)).map(relation=>({primaryFileId:id,...relation})));
20
- const payload = JSON.stringify({ ...(snapshot.history ? {contextSideNotice:CONTEXT_SIDE_NOTICE} : {}), ...(snapshot.businessRequirement!==undefined?{businessRequirement:snapshot.businessRequirement,requirementNotice:REQUIREMENT_NOTICE}:{}), contextRelations, contextRelationNotice:'Approved-context import/include and call binding hints only; not a call graph, complete caller coverage, semantic binding or causal evidence. Historical contextSide/contextVersion must not be interchanged.', snapshotId: snapshot.id, sourceMode: 'file-interaction', groupId: group.id, files, context: snapshot.context ?? [], rules: snapshot.rules ?? [], notice: 'Only combined defects across at least two listed files; directories are navigation not evidence. Read referenced edit source sides from approved catalog in this generation. Return ordinary findings with fileId of one listed primary and exact original line anchor; attribution.editIds references only that primary. Add interaction:{editRefs:[{fileId,editId}]} including all involved edits across at least two files. This sourceMode overrides single-primary input instructions. Retain original primary anchor and file-qualified edit references. Unsupported conclusions become limitations; no repeated single-file issues. Relations and read coverage are not causal proof.' });
21
- const budget = measure(payload);
22
- return { status: budget.fits ? 'ready' : 'blocked', reason: budget.fits ? undefined : 'input-budget', fileIds: [...group.fileIds], payload, budget };
20
+ let payload = JSON.stringify({ ...(snapshot.history ? {contextSideNotice:CONTEXT_SIDE_NOTICE} : {}), ...(snapshot.businessRequirement!==undefined?{businessRequirement:snapshot.businessRequirement,requirementNotice:REQUIREMENT_NOTICE}:{}), contextRelations, contextRelationNotice:'Approved-context import/include and call binding hints only; not a call graph, complete caller coverage, semantic binding or causal evidence. Historical contextSide/contextVersion must not be interchanged.', snapshotId: snapshot.id, sourceMode: 'file-interaction', groupId: group.id, files, context: snapshot.context ?? [], rules: snapshot.rules ?? [], notice: 'Only combined defects across at least two listed files; directories are navigation not evidence. Read referenced edit source sides from approved catalog in this generation. Return ordinary findings with fileId of one listed primary and exact original line anchor; attribution.editIds references only that primary. Add interaction:{editRefs:[{fileId,editId}]} including all involved edits across at least two files. This sourceMode overrides single-primary input instructions. Retain original primary anchor and file-qualified edit references. Unsupported conclusions become limitations; no repeated single-file issues. Relations and read coverage are not causal proof.' });
21
+ let budget = measure(payload);
22
+ const metadata={};
23
+ const omitted=contextRelations.reduce((n,e)=>n+(e.bindingHints??[]).filter(h=>h.confidence==='namespace-member-syntax-only').length,0);
24
+ if(!budget.fits&&omitted){
25
+ const envelope=JSON.parse(payload);
26
+ envelope.contextRelations=envelope.contextRelations.map(e=>({...e,bindingHints:(e.bindingHints??[]).filter(h=>h.confidence!=='namespace-member-syntax-only')}));
27
+ payload=JSON.stringify(envelope);budget=measure(payload);
28
+ metadata.namespaceBindingFallback='input-budget';metadata.omittedNamespaceBindingCount=omitted;
29
+ }
30
+ return { status: budget.fits ? 'ready' : 'blocked', reason: budget.fits ? undefined : 'input-budget', fileIds: [...group.fileIds], payload, budget, ...metadata };
23
31
  }
@@ -0,0 +1,26 @@
1
+ import {uniqueApprovedDeclarations} from './import-bindings.mjs';
2
+ // Pure syntax navigation over already approved text; no inferred instance type.
3
+ export function namespaceImportBindings(lines, importLine, targetText) {
4
+ if(!Array.isArray(lines)||lines.length>20000||typeof targetText!=='string')return [];
5
+ const text=lines.join('\n');
6
+ // Reject escape-normalized names and JSX-like lexical regions without a parser.
7
+ if (/[\\<>]/.test(text) || /[\\<>]/.test(targetText)) return [];
8
+ if(Buffer.byteLength(text)>256*1024||text.includes('/*')||text.includes(String.fromCharCode(96))||/\\\r?\n/.test(text))return [];
9
+ const match=/^\s*import\s+\*\s+as\s+([A-Za-z_$][A-Za-z0-9_$]*)\s+from\s+(['"])[^'"\\]+\2\s*;?\s*(?:\/\/.*)?$/.exec(lines[importLine-1]);
10
+ if(!match)return [];
11
+ const local=match[1],escaped=local.replaceAll('$','\\$'),token=new RegExp('(^|[^A-Za-z0-9_$])'+escaped+'(?=[^A-Za-z0-9_$]|$)','g');
12
+ const pattern=new RegExp('^\\s*(?:await\\s+)?'+escaped+'\\.([A-Za-z_$][A-Za-z0-9_$]*)\\s*\\([^();"\x27/]*\\)\\s*;?\\s*$');
13
+ const calls=[];
14
+ for(let i=0;i<lines.length;i++){
15
+ if(i+1===importLine)continue;
16
+ const occurrences=[...lines[i].matchAll(token)];if(!occurrences.length)continue;
17
+ const call=pattern.exec(lines[i]);
18
+ // Other occurrences may mutate, shadow, pass, destructure or escape the namespace.
19
+ if(!call||occurrences.length!==1)return [];
20
+ if(calls.length>=20)return [];
21
+ calls.push({member:call[1],callLine:i+1});
22
+ }
23
+ const targets=targetText.split('\n'),result=[];
24
+ for(const call of calls){const declarations=uniqueApprovedDeclarations(targets,call.member);if(declarations.length===1)result.push({imported:call.member,local,member:call.member,importLine,callLine:call.callLine,declarationLine:declarations[0].number,declarationKind:declarations[0].kind,confidence:'namespace-member-syntax-only'});}
25
+ return result;
26
+ }
@@ -34,6 +34,13 @@ export function preparePrimaryInput(input, file, { instructions, scope, grouping
34
34
  let payload = JSON.stringify(base);
35
35
  const measure = value => initialInputBudget({ instructions, input: value }, scope, maxInputBytes);
36
36
  const metadata = {};
37
+ // Newly added member navigation is optional, like caller annotations.
38
+ if (base.contextRelations?.some(e=>e.bindingHints?.some(h=>h.confidence==='namespace-member-syntax-only')) && !measure(payload).fits) {
39
+ metadata.namespaceBindingFallback = 'input-budget';
40
+ metadata.omittedNamespaceBindingCount = base.contextRelations.reduce((n,e)=>n+(e.bindingHints??[]).filter(h=>h.confidence==='namespace-member-syntax-only').length,0);
41
+ base.contextRelations = base.contextRelations.map(e=>({...e,bindingHints:(e.bindingHints??[]).filter(h=>h.confidence!=='namespace-member-syntax-only')}));
42
+ payload = JSON.stringify(base);
43
+ }
37
44
  // Optional navigation must not block source review or force extra windows.
38
45
  if (base.callerBindings?.length && !measure(payload).fits) {
39
46
  metadata.callerBindingFallback = 'input-budget';
@@ -51,7 +58,14 @@ export function preparePrimaryInput(input, file, { instructions, scope, grouping
51
58
  const bindingRelations = inferImportRelations(input.files).filter(edge => (edge.bindingHints?.length || edge.definitionHints?.length) && (edge.from === file.id || edge.to === file.id) && group.fileIds.includes(edge.from) && group.fileIds.includes(edge.to)).map(({ from, to, side, bindingHints, definitionHints }) => ({ from, to, side, bindingHints, ...(definitionHints?.length ? { definitionHints } : {}) }));
52
59
  const grouped = JSON.stringify({ ...base, relatedFiles, relations, bindingRelations, relationNotice: '命名和相对导入关系仅为源码线索,可能被同名局部变量遮蔽,不证明绑定、调用可达或因果;只报告主file的问题,不为relatedFiles重复生成发现。' });
53
60
  if (measure(grouped).fits) { payload = grouped; metadata.relatedFileIds = relatedFiles.map(f => f.id); }
54
- else metadata.groupFallback = 'input-budget';
61
+ else {
62
+ // Optional member hints must not cost the grouped related sources either.
63
+ const groupedOmitted = bindingRelations.reduce((n, e) => n + (e.bindingHints ?? []).filter(h => h.confidence === 'namespace-member-syntax-only').length, 0);
64
+ const trimmedRelations = bindingRelations.map(e => ({ ...e, bindingHints: (e.bindingHints ?? []).filter(h => h.confidence !== 'namespace-member-syntax-only') })).filter(e => e.bindingHints.length || e.definitionHints?.length);
65
+ const trimmed = JSON.stringify({ ...base, relatedFiles, relations, bindingRelations: trimmedRelations, relationNotice: '命名和相对导入关系仅为源码线索,可能被同名局部变量遮蔽,不证明绑定、调用可达或因果;只报告主file的问题,不为relatedFiles重复生成发现。' });
66
+ if (groupedOmitted && measure(trimmed).fits) { payload = trimmed; metadata.relatedFileIds = relatedFiles.map(f => f.id); metadata.namespaceBindingFallback = 'input-budget'; metadata.omittedNamespaceBindingCount = (metadata.omittedNamespaceBindingCount ?? 0) + groupedOmitted; }
67
+ else metadata.groupFallback = 'input-budget';
68
+ }
55
69
  }
56
70
  }
57
71
  if (!measure(payload).fits && scope) {