# S1 SPIKE 报告 — 宿主数据契约验证(决策门) > 日期:2026-08-20 · 依据源码:`deepseek-ai/deepseek-harness` @ master(`0.1.0-rc.8`) > 结论:**通过** —— 三个验证点中两个源码级确证,一个需自建映射后可达。产品 v1 可开工。 ## 0. 首先一个重要事实:版本漂移已发生 我们脚手架当前离线链锁定了 `0.1.0-rc.7`;仓库主干当前已是 `0.1.0-rc.8`(root `package.json` 的 `version` 字段)。SPIKE 警告成立:**必须先把脚手架钉到 rc.8 再开发**,否则采集器按过时契约写会直接踩雷。 ## 1. 验证点 ①:运行时/遥测事件是否带插件级归因 ### 结论:**会话级免费,插件级需自建映射(有条件通过,产品做降级设计)** | 数据 | 来源(文件/行) | 是否带插件归因 | | --- | --- | --- | | 会话级聚合统计 | `packages/session/session-stats/src/types.ts` — `SessionStatsProjection`:`turns/steps/llmMs/toolMs/ttftMs/decodeMs/decodeTokens`,其中 `toolMs` 由 `tool/call`→`tool/result` 按 callId 配对累加 | 否(会话级,不含插件维度) | | 会话事件流(完整载荷) | `packages/session/session-telemetry/src/index.ts` — `SessionTelemetryRecord.body` = 会话事件的完整 payload 深拷贝;`attributes` 故意不重复 body(含 **`isError`** 标志) | 否;`attributes` 只有 `session.id/event.type/event.seq/cwd/parent_id`(`index.ts:71-78`) | | 工具执行对象 | `packages/core/tools/src/index.ts` — `ToolExecution` 只有 `callId/name/agent/token/rootCallId`,**没有 provider/plugin 字段**;`ToolDefinition` 无插件字段 | 否 | | 工具注册/注销信号 | `tools/change` 事件(`tools.ts` codis surface);`ctx.tools.register()` 返回 disposer;注册发生在插件自身 ctx/scope 内 | 注册侧可获取(插件 context 即知是谁注册的) | ### 可行方案(自建映射,约 1–2 天胶水工作) 在插件挂载期订阅 `tools/change`(或包一层 `ctx.tools.register`),记录 `toolName → pluginId` 映射;再在 `session/event` 流里把 `tool/call` / `tool/result` 按名称及 callId 配对,聚合出**每插件的调用次数、耗时、错误/超时数、token 占比**。`tool/result` 的 `isError` 直接给出错误率;`session-stats` 的配对逻辑(tool/call→tool/result)可直接复用思路。 ### 对产品的影响 v1 运行时维度以**会话级统计为主**(零成本可得),**插件级归因列为 v1.1 feature**(依赖自建映射,作为首个 roadmap item 挂钩官方讨论区)。这样 SPIKE 即便在 rc 后续变更中再次漂移,核心评分也不受牵连。 ## 2. 验证点 ②:`--dump-config` 能否还原完整插件树 ### 结论:**通过(源码确证,无需联网/无模型 key)** - 实现:`apps/cli/src/dump-config.ts` — `dsh --profile --dump-config` 通过 include 的 patch 算法**不启动插件**地合成 profile 全部分层(各 bundle 层 + 用户 `cordis.patch.yml` + 家目录 patch + `--patch` 覆盖层),并以 `renderConfigDump` 打印整棵条目树(含每条目的 id/name/config/disabled)。 - 关键依据:`packages/boot/app-boot/src/index.ts`:`assertEntriesLoaded` / `assertEntriesActivated`(`index.ts:658-706`)会枚举 `ctx.loader.entries()` 并区分 `disabled` 与 `fiber === undefined` —— 这暴露了与 dump 输出一致的加载后事实,可交叉校验静态 dump。 - 用途:无需启动即可得到「总体插件清单、启用/禁用、依赖结构、配置项」,用于硬健康与治理健康大部分信号;`!!js` 表达式不会求值(documented),扫描时以文本层保留。 ## 3. 验证点 ③:patch 写入 + HMR 热生效闭环 ### 结论:**完全确证(源码 + 测试双重证据)** - 热更新机制:`packages/boot/app-boot/src/index.ts` 的 `watchUserPatches()`(`index.ts:232-265`)用 Cordis HMR 服务的 `registerConfig(filename, async () => { loadOptionalPatches → 重编 patch → entry.update() })` 监听 profile 的 `cordis.patch.yml` 文件,改动即热重载(dsh-market 宣称的 ~1s 与此一致)。 - 禁用语义:loader 条目原生支持 `disabled`,且支持 `!!js` 表达式;`app-boot` 在结算时跳过 `disabled` 条目(`index.ts:659, 698`);测试 `packages/boot/app-boot/tests/config-reload.spec.ts` 覆盖了「禁用→恢复」往返(`disabled: true/false` 写入后实时生效)与 `user-patches.spec.ts:202-262` 的 `disabled` 插值。 - 写入方式:`dsh-market` 的实践与上述机制一致——向 profile 的 `cordis.patch.yml` 写 `- id: <插件>` + `disabled: true` 即完成「禁用」,撤销=改回 `false` 或删行。**由此我们的一键修复/撤销的标准动作有了官方语义支撑。** ## 4. 决策门决议 | 项 | 结果 | | --- | --- | | 方向 | 继续健康评分产品(无反对证据) | | 版本 | 升级脚手架锁定 `0.1.0-rc.8`(改动后重跑 typecheck/test/build 已通过) | | v1 范围 | 硬健康 + 治理健康 = 全部依赖 dump-config 与配置校验;运行时健康 = 会话级(session-stats) | | 后续 roadmap | 插件级运行时归因(自建工具名→插件映射)列为 v1.1;安全静态扫描仍划给 DShScan 边界外 | | 剩余风险 | rc 继续漂移;仓库文档指出 HMR 在部分桌面端(自带旧 dsh)可能缺失——发布说明需再次核对目标宿主版本 | ## 5. 环境备注 - 本次验证在 Windows/Node v24.14.0/pnpm 11.21 完成;`pnpm install` 会报 `ERR_PNPM_IGNORED_BUILDS`(node-pty/koffi/esbuild 等原生/构建脚本默认忽略),用 `pnpm approve-builds` 一次性确认即可;`.npmrc` 已设 `verify-deps-before-run=false` 规避 pnpm 11 的预校验误报。 - 未做全量 `pnpm run build`(monorepo 全构建耗时大且含原生依赖,非本 SPIKE 必需);数据契约结论以源码 + 单元测试为准,可在后续开发期用 `dsh web` 冒烟复核。