dsh-scout 0.9.0 → 0.9.2

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 ADDED
@@ -0,0 +1,91 @@
1
+ # Changelog
2
+
3
+ 本文件记录 `dsh-scout` 的显著变更。格式参考 [Keep a Changelog](https://keepachangelog.com/zh-CN/1.1.0/),版本号遵循 [Semantic Versioning](https://semver.org/lang/zh-CN/)。
4
+
5
+ 本文件从 0.9.1 开始记录;0.9.0 及更早版本的变更见仓库的 git tag。
6
+
7
+ ## [0.9.2] - 2026-10-13
8
+
9
+ 0.9.2 是 0.9.1 的收口版本:把 0.9.1 验证报告点名的最大盲区(**U11**)补成真实宿主门禁,清掉三轮独立验证留下的文档税,并关闭 0.9.1 的两个已知边界(**D10**、**O10**)。
10
+
11
+ ### Added
12
+
13
+ - **真实宿主端到端门禁(关闭 U11)**:`scripts/profile-smoke.mjs` 从「单进程、两个工具调用」扩展为**六步场景链**,在最多六个真实宿主进程里依次跑:创建(`scout_start` + `scout_add_source` + `scout_add_claim`)→ 导出(绝对 `targetDir`、6 个文件、完成标记 `complete: true`)→ 冷重启(同一 `DSH_HOME`/profile 的第二个进程)→ 惰性回填(不 `scout_import`,断言决策不变并落 `case_rehydrated`)→ 跨语言导入(`en` profile 导入 zh 导出,报告与 `report.md` 的 CJK = 0)→ 卸载(无 registry 残留)。另加两条**完成标记负向对照**:删掉 `export.complete.json`、把标记改成 `complete: false`,两者都必须拒绝回填——证明第 4 步真的在读标记,而不是恰好命中别的东西。全量约 16 秒(M 系列 Mac、pnpm store 已热),可用 `DSH_SCOUT_SMOKE_SKIP_CROSS_LANGUAGE` / `DSH_SCOUT_SMOKE_SKIP_REHYDRATION_CONTROL` 走 PR 快速路径,跳过会在 PASS 行如实报为**覆盖降级**、不静默。**四版本矩阵在扩展后的门禁下 4/4 通过**(`0.1.0-rc.6` / `0.1.2-rc.1` / `0.2.0-rc.2` / `0.2.1-alpha.2`);「已覆盖 / 仍未覆盖」两栏写在脚本头部注释里(仍未覆盖:真实 LLM / agent loop 与 approval 策略、相对路径、`autoPersist`、真实进程中断、其余 6 个工具仅断言注册)。
14
+
15
+ ### Changed
16
+
17
+ - **精选面试问题改为按下标逐条对齐**(关闭 0.9.1 的「整组 all-or-nothing」边界,即 D10):长度与出厂数组相同时,逐个下标比较——该下标仍是出厂默认的条目替换为本部署语言的同下标默认值,**用户改过的那条保持原样**;长度与出厂数组不同时,整组视为用户自撰、原样保留。判定依据是「是否等于该下标的出厂默认」,不是编辑历史:把某条改回出厂原文,那条会被对齐。**默认行为对未编辑场景零变化**——整组未被编辑时,对齐结果与 0.9.1 逐条相同;`zh-CN` 部署对既有 case 仍是 no-op(`case.json` 的 `decisionObjective` / `interviewQuestions` / `role.fitQuestion` 三个字段逐字段不变)。
18
+ - **`@deepseek-ai/schemastery` 由 `^3.18.2` 对齐到 `~3.18.4`**:宿主系列锁 `~3.18.4`,而 `^` 允许解析到 `3.19.x`,存在静默行为漂移;`Config` 的身份识别走全局符号,跨版本不会认错,这次只是把版本面收齐。`peerDependencies` 与 `engines.dsh` 范围**没有变化**。
19
+
20
+ ### Fixed
21
+
22
+ - **render 整串上限改为由省略号长度推导**(原 O10):0.9.1 的裁切预算是固定余量 `MAX_RENDER_BYTES - 8`,再追加 3 B 省略号,含省略号的实际上界约 1019 B(实测最坏 1018 B),距 1024 B 预算只剩约 5 B 余量——把余量改到 3 以内就会越界。现在省略号自身的 UTF-8 长度先从预算里扣掉、再按码元裁切,整串(含省略号)**不超过 1024 B**;对抗性测试把 ASCII 输入推到整整 1024 B,并对 CJK 与 emoji(代理对、UTF-8 变长)断言不超过预算。
23
+
24
+ ### Docs
25
+
26
+ - **`case.json.language` 的语义**:它是**本部署的渲染语言**,不是文件内容的语言;手工改写的外来文件不做自洽性校正。
27
+ - **`alignCaseDefaults` 的生效范围**:只在 `decide` 路径生效——重算决策的工具调用与缓存未命中时的惰性回填经过它(回填由**第一个访问该 case 的工具调用**触发,所以冷启动后即使只调 `scout_report` 也可能带上一次对齐),只创建案例的 `scout_start` 不经过;直接读磁盘 `case.json`、从不经过插件的第三方工具看不到对齐。
28
+ - **导出标记的两处细节**:`eventCount` 在导出中断时描述的是**失败的那次尝试**期望的事件数,与磁盘 `events.jsonl` 行数可能不同,读取方以 `complete` 为准;标记里的 `files` 只列 **5 个数据文件**,而 `scout_export` 返回值里的 `files` 是 **6 个名字**(五个数据文件 + 标记自身)——同名不同义,两者都正确。
29
+ - **新增门禁发现记录**:`docs/dsh-scout-0.9.2-gate-findings.md` 记录 F2 暴露的 G1(见下),并附最小复现脚本 `scripts/repro-decision-reason-drift.mjs`——两者都是仓库内文档 / 诊断件,**不在 npm 包内**,也不进测试套件。
30
+
31
+ ### Known boundaries
32
+
33
+ - **G1 · 冷启动回填会重写决策理由的措辞(0.9.2 有意不修,推迟 0.10.0)**:对「`scout_start` 之后从未发生会重算决策的调用」就导出、冷启动(或 `scout_import`)的案例,回填路径会重跑决策,把 `decisionReason` 由初始文案 `defaults.initialDecisionReason` 换成 `decisionReasons.identity`,并补上 `decisionReasonCode`;**`decision`(`VERIFY`)不变**,证据账本 / 覆盖率 / 面试问题 / `caseId` 都不受影响。触发窗口比「一次写操作都没有」略大:会重算决策的是 `scout_add_claim` / `scout_ingest` / `scout_search` / `scout_verify_identity` / `scout_verify_claim` / `scout_import`;只登记来源的 `scout_add_source` 在同一进程内不重算决策,但冷启动后第一次访问该 case 的工具调用(含 `scout_report` / `scout_add_source`)会先走惰性回填 → `decide`,所以「创建后只登记过来源、然后导出并重启」的案例同样会看到措辞变化。0.9.2 不修的两个理由:正确修法会改动 0.9.0/0.9.1 冻结的全新案例首屏文案基线;且把「重启前后理由必须一致」写成断言会违反本项目「测试套件里断言缺陷存在的用例数为 0」的硬规矩。复现步骤、代码路径与 0.10.0 的候选修法见 [0.9.2 门禁发现](docs/dsh-scout-0.9.2-gate-findings.md)。
34
+
35
+ ## [0.9.1] - 2026-10-12
36
+
37
+ 0.9.1 是本地迭代版本(不发 npm、不推远端、不打 tag)。主题不是加功能,而是把 0.9.0 已经写好的能力接到 DSH 正确的接缝上:**插件在真实 DSH 里要表现正确、便宜、可解释**。
38
+
39
+ ### Added
40
+
41
+ - **`/scout` 命令**:注册 `ctx.commands.register({ name: 'scout', … })`,提供 `status` / `questions` / `report` / `export` 四个子动作(用法:`/scout status <caseId> | /scout questions <caseId> | /scout report <caseId> | /scout export <caseId> [targetDir]`);宿主在处理前后自动落 `command/run` + `command/done` 事件(这两个名字都在宿主已知事件词表内,因此会话可重建)。
42
+ - **`scout` Skill**:由插件直接注册(`ctx.skills.register`,不需要落文件、不需要 client bundle),内含标准尽调工作流(start → search/ingest → claim → verify_identity → report → questions)与四条证据纪律。插件卸载时条目随 disposer 消失。
43
+ - **`language: 'zh-CN' | 'en'` 配置**(默认 `zh-CN`):控制报告、面试问题、对比报告,以及 `scout_ingest` 采集草稿自动写入的说明文字(`confidenceNote` / `nextAction`)的语言;它还会对齐案例自带、**仍是出厂默认**的文案,但判定粒度不同——`decisionObjective` / `role.fitQuestion` 是**逐字段**(该字段仍是出厂默认即对齐),精选面试问题在 0.9.1 是**整组 all-or-nothing**(整组逐项未被编辑才对齐;改过任意一条则整组保留创建语言,`en` 报告此时仍含约 79 个 CJK 字符),**0.9.2 起改为按下标逐条**:长度与出厂数组相同时逐条替换,用户改过的那条保持原样,长度不同则整组原样保留(见 `[0.9.2]`)。**用户编辑过的文案保持原样**;与案例自带语言相同时是 no-op,默认值对既有用户零变化。
44
+ - **报告新增 `## Coverage` 调查覆盖度区块**:对全部 11 个调查维度给出「已核验 / 待核验 / 未调查」计数,并单列未调查维度;`scout_questions` 会优先为未调查维度生成问题。
45
+ - **导出完成标记 `export.complete.json`**:内容形如 `{ schemaVersion, caseId, complete, files, eventCount }`;写入时**先置 `complete: false`,五个数据文件全部落盘后再改写为 `complete: true`**——所以陈旧标记不会为半份导出宣称完整(覆盖写场景下,上一次成功导出的标记会在新文件写入前先被降级)。**`eventCount` 只有在 `complete: true` 时才与 `events.jsonl` 的行数一致**;导出中断时它记录的是那次失败尝试期望的事件数,请以 `complete` 为准(读取方不要只比较 `eventCount`)。标记里的 `files` 只列**五个数据文件**,而 `scout_export` 返回值里的 `files` 是这次调用写出的**六个名字**(五个数据文件 + 标记自身)——同名不同义,两者都正确。
46
+ - **惰性回填仅限完整导出**:缓存未命中时,只有当 `<scoutDir>/<caseId>/` 的完成标记为 `complete: true` 时才读回案例并追加 `case_rehydrated` 事件;**0.9.0 的旧导出没有完成标记,不会被自动回填**,需用 `scout_import` 显式恢复;错误信息会提示可用的 caseId / 建议 `scout_import`。
47
+ - **分发契约字段**:`engines.dsh`(`>=0.1.0-rc.6 <0.3.0-0`)、`dsh.manifestVersion: 1`、`locale/en.json` + `locale/zh.json`(已写进 `exports`,否则会被 ESM resolver 静默忽略)、包内 `icon`。
48
+ - **本 CHANGELOG**。
49
+
50
+ ### Changed
51
+
52
+ - **工具输出契约**:变更类工具(8 个,含 `scout_import`)不再回吐整份 case JSON。每次调用返回结构化对象 `value`(决策、计数、本次增量、下一步,以及变更类工具在 `autoPersist` 路径真正尝试过落盘时的 `persist`)加上 **< 1KB 的紧凑 `render`**;全量数据仍由 `scout_report`(Markdown 报告)与 `scout_export`(五文件)提供。`scout_export` 不带 `persist` 键,用顶层 `persisted` / `complete` / `error` 表达落盘结果。工具名与语义不变。
53
+ - **`scout_export` 返回解析后的绝对路径**(`targetDir`):此前返回的是相对字符串,用户无从知道文件写到了哪。
54
+ - **重复 URL 不再产生重复来源**:`scout_ingest` / `scout_search` 按规范化 URL 复用已登记来源,避免同一 URL 搜两次就稀释报告里的「关键证据」配额。
55
+
56
+ ### Fixed
57
+
58
+ - **`autoPersist` 失败不再静默**:返回值带 `persist.persisted = false` + `persist.error`,模型与用户能直接看到这次没有落盘;落盘失败时**不产生** `case_exported` 事件,磁盘上的 `events.jsonl` 不会声称一次并未完成的导出。
59
+ - **导出顺序统一为「先记事件、后写文件」**:`scout_export` 此前先写文件后记事件,导致磁盘上的 `events.jsonl` 永远不含本次导出的事件,与 `autoPersist` 路径产出不同的事件流;现在两条路径一致,「导出 → 导入 → 再导出」的事件序列稳定。
60
+ - **`fs` / `web` 改为调用时解析**:不再在 `apply()` 时用 `ctx.get()` 快照服务。此前能工作只是因为真实 profile 里 `dsh-scout` 恰好排在 `fs` / `web` 之后;行序变化、只装部分 bundle、会话级服务覆盖时,服务其实存在而插件会一直报 "service unavailable"。
61
+ - **写盘转发 `exec.signal`**:`scout_export` 的写盘可被取消。
62
+ - **`scout_import` 如实报告导出完整性**:它读完成标记的 `complete` **字段**(不是「标记文件是否存在」)。没有标记的旧导出、或标记为 `complete: false` 的导出,只要五个数据文件都已落盘仍可导入,但返回 `complete: false`,由调用方决定是否信任该快照(五个数据文件缺任何一个,例如在写 `claims.json` 时中断的全新导出,仍是硬失败)。
63
+ - **导出路径以会话 cwd 为基准**:`scoutDir` 的相对路径不再落到 DSH 进程的当前目录。
64
+ - **`scout` Skill 补上必填的 `source`**:`SkillRegistration` 只把 `invocation` / `provider` 变成可选,`source` 仍是非可选字段,而注册表只为 `provider` 补默认值。此前注册**不会报错**(`validateRuntimeSkill` 不校验 `source`),模型侧技能目录也不受影响(只含 name/description),但 `ctx.skills.list()` 返回的 `SkillSummary` 违反其自身声明的非可选字段——`source` 读回 `undefined`。现注册为 `source: 'runtime'`;真实宿主实测由 `{"name":"scout","provider":"runtime"}` 变为 `{"name":"scout","source":"runtime","provider":"runtime"}`。
65
+ - **`@deepseek-ai/dsh-tools` 不再标为 optional peer**:它是构建产物 `dist/index.js` 里真实的运行时 `import`(`@deepseek-ai/cordis` 只有类型导入、会被擦除,故保持 optional)。这**不影响** DSH 的兼容门禁(`evaluatePluginCompatibility` 只读 `peerDependencies`,无视 `peerDependenciesMeta`),只是让安装期信号与真实依赖关系一致。
66
+
67
+ ### Note
68
+
69
+ - **四版本真实矩阵已跑通**:0.9.1 候选 tarball 在 `0.1.0-rc.6` / `0.1.2-rc.1` / `0.2.0-rc.2` / `0.2.1-alpha.2` 四个**精确版本**上通过安装、冷启动、工具挂载、真实工具调用门禁(含 `signal` 字段与 `output.schema` 校验)与卸载;四个 binary 均从 npm 真实取用(非本机 DSH),`npm run check:release` 实测输出 `dsh-scout release gate: PASS (4 DSH versions, >=0.1.0-rc.6 <0.3.0-0)`。超出声明范围(如 `0.3.0-alpha.1`)仍按预期不满足。
70
+ - **核对版本范围请带 `includePrerelease`**:`engines.dsh` / peer 的 `>=0.1.0-rc.6 <0.3.0-0` 在**默认 semver 语义**下会被判为**不满足** `0.1.2-rc.1`、`0.2.0-rc.2`、`0.2.1-alpha.2`——这是 node-semver 的预发布规则:预发布版本只被「同 major.minor.patch 元组且带预发布」的比较器接受。DSH 的检查带 `{ includePrerelease: true }`(`dsh-app-boot/lib/index.js:300`),在该标志下四个实测版本全部满足,`0.3.0-alpha.1` 正确不满足。semver 7.8.5 实测:`0.1.0-rc.6` plain=true / incl=true;`0.1.2-rc.1`、`0.2.0-rc.2`、`0.2.1-alpha.2` plain=false / incl=true;`0.3.0-alpha.1` plain=false / incl=false。**以 DSH 的判定为准。**
71
+
72
+ ### Not in this release(本轮明确不做)
73
+
74
+ 诚实列出边界,避免把「没做」读成「已支持」:
75
+
76
+ - **client 面板**(case 看板 / 浮层卡片):要注册 client 的 `tool.call.toolview` / `shell.overlay` slot,需要 `dsh.client` + client bundle + 双 tsc program + HMR 验证——这是一个完整 feature release 的工作量,不是 patch。
77
+ - **自造 session event**(把 case 状态写进会话日志):在 DSH 0.2.0-rc.2 上走不通,是红线。`Session.append()` 无法写入 `ignorable`,而持久化读路径拒绝任何不在 `KNOWN_SESSION_EVENT_TYPES` 内且非 ignorable 的事件类型,自造事件会导致**插件自己写的会话在重启 / 恢复时无法重建**。详见[产品契约 §5.1](docs/dsh-scout-product-contract.md)。
78
+ - **`ctx.storageDomain` 持久化重写**:这是 DSH 原生的正确接缝,但要引入 domain schema、生命周期与迁移策略,并改变现在的 owner 语义(按 `SessionId` 分桶)。0.9.1 先用「文件惰性回填」解决大部分痛感,重写排到 0.10.0。
79
+ - **评分 / 排名 / 批量对比扩展**:产品契约 §6 非目标,保持边界。
80
+ - **`@deepseek-ai/schemastery` 版本对齐**(`^3.18.2` → 宿主系列的 `~3.18.4`):本轮明确不做。
81
+ - **`presentationMeta` 载荷**:没有 client 消费者时没有可见收益,与 client 面板一起在 0.10.0 落地。
82
+ - **npm 发布 / git tag**:本轮只做本地交付。
83
+
84
+ ### Known boundaries
85
+
86
+ 刻意保留的行为边界(不是待修缺陷,但读文档的人需要知道):
87
+
88
+ - **精选面试问题整组对齐**(0.9.1 的边界;0.9.2 起改为**按下标逐条**对齐,见 `[0.9.2]` Changed):0.9.1 采用 **all-or-nothing** 判定——整组逐项都仍是出厂默认时才对齐到部署语言;用户改过其中任意一条,整组保留创建语言(其余未编辑的条目也不会逐条转换),`en` 部署的报告此时仍含约 79 个 CJK 字符。这是当时有意保守(不猜用户意图)。
89
+ - **惰性回填只认完整导出**:只有完成标记 `complete: true` 的导出才会在缓存未命中时自动回填;0.9.0 的旧导出没有该标记,不会被自动恢复,需用 `scout_import` 显式导入。
90
+ - **`case.json.language` 是渲染语言、不是内容语言**:案例对齐只按这个标签判定,**不校验标签与内容是否自洽**;手工把 `language` 改成与内容不符的值再导入时,插件会按「语言相同」提前返回、不做校正。正常导出 / 导入 / 升级路径不受影响(0.9.0 旧导出的 `language` 恒为 `zh-CN`,与其内容一致)。
91
+ - **对齐只在 `decide` 路径生效**:它随决策重算运行(`scout_add_claim` / `scout_ingest` / `scout_search` / `scout_verify_identity` / `scout_verify_claim` / `scout_import`)与缓存未命中时的惰性回填运行(回填由第一个访问该 case 的工具调用触发,所以冷启动后即使只调 `scout_report` 也可能带上一次对齐),对齐结果随之落到内存 case 与之后的导出;只创建案例的 `scout_start` 不经过它。直接读磁盘 `case.json`、从不经过插件的第三方工具看不到对齐。
package/README.en.md CHANGED
@@ -4,76 +4,221 @@
4
4
  [![license MIT](https://img.shields.io/badge/license-MIT-blue)](LICENSE)
5
5
  [![node >=22.19](https://img.shields.io/badge/node-%3E%3D22.19-brightgreen)](package.json)
6
6
  [![DSH nightly compatibility](https://github.com/MaxHou-infinity/dsh-scout/actions/workflows/compatibility-nightly.yml/badge.svg)](https://github.com/MaxHou-infinity/dsh-scout/actions/workflows/compatibility-nightly.yml)
7
- [![tests 34 passing](https://img.shields.io/badge/tests-34%20passing-green)](tests/model.test.mjs)
7
+ [![tests passing](https://img.shields.io/badge/tests-passing-green)](tests/model.test.mjs)
8
8
  [![中文 README](https://img.shields.io/badge/README-%E4%B8%AD%E6%96%87-2ea44f)](README.md)
9
9
 
10
- **司察(Scout)** — evidence-driven company & job due-diligence plugin for [DeepSeek Harness](https://github.com/deepseek-ai/DeepSeek-Harness).
10
+ **司察 (Scout)** — an **evidence-driven company & job due-diligence plugin** for [DeepSeek Harness](https://github.com/deepseek-ai/DeepSeek-Harness).
11
11
 
12
- `dsh-scout` helps an agent answer a concrete question:
12
+ In one sentence: **it helps an agent answer — "Is this company and this role worth taking to the next round, and what must I verify in the interview?"** Every conclusion is tied to a real source; company self-description and funding news are never treated as verified facts.
13
13
 
14
- > Is this company and role worth taking to the next round, and what must I verify in the interview?
14
+ ## Naming
15
15
 
16
- The plugin keeps facts, reported information, inference, unknowns, sources, and next actions separate. It starts conservatively at `VERIFY` until the company identity and high-impact claims are supported.
16
+ - **Scout** — a scout is sent ahead to reconnoiter a company and a role before you commit: background check, due diligence, evidence gathering, interview prep. The package and repository name stays `dsh-scout` so install references remain stable.
17
+ - **司察** (sī-chá) — the Chinese name: "司" echoes *scout*, "察" means to examine and reconnoiter, capturing the core function of evidence-driven company & role due diligence.
17
18
 
18
- ## Naming
19
+ ---
19
20
 
20
- - **Scout** — a scout is sent ahead to reconnoiter a company and a role before you commit: background check, due diligence, evidence gathering, interview prep. The English package and repository name stays `dsh-scout` for stable install references.
21
- - **司察** (sī-chá) — Chinese name: "司" echoes *scout*, "察" means to examine and reconnoiter, capturing the core function of evidence-driven company & role due diligence.
21
+ ## Why it exists (the pain it removes)
22
22
 
23
- ## Current scope
23
+ Say you receive an interview invitation or an offer:
24
24
 
25
- This repository contains the first runnable, session-isolated slice:
25
+ - The job posting says the company is "the global leader, valued at tens of billions" — **that is marketing, not fact**;
26
+ - Media reports "raised $2B" — **that may be second-hand reporting, possibly already denied by the company**;
27
+ - Before the interview you need to judge: is the legal entity real? Are the reporting line, mandate, and compensation credible? **What should I ask?**
26
28
 
27
- - `scout_start`: create an in-memory diligence case.
28
- - `scout_add_source`: register a source.
29
- - `scout_ingest`: batch-register collected research results (auto-infers source type and evidence level per URL, optionally drafting a claim per item; individual failures or rejected claims are isolated without aborting the batch).
30
- - `scout_search`: run a web search through the DSH web provider and auto-register the result URLs as sources (type and evidence level inferred per URL).
31
- - `scout_add_claim`: attach an evidence-bounded claim.
32
- - `scout_verify_identity`: confirm the legal entity from an `E3` source.
33
- - `scout_verify_claim`: promote a claim while retaining its prior evidence state.
34
- - `scout_report`: render the current Markdown report (evidence summary counts, impact-sorted key evidence/risks/role hypotheses, a **verification checklist**, URL-linked source list, and interview questions).
35
- - `scout_questions`: derive a deduplicated, prioritized interview question list from the case (up to 12 items).
36
- - `scout_compare`: render a side-by-side comparison report for two to five cases (decisions, identity status, verified conclusions, open risks, and merged interview questions).
37
- - `scout_export`: persist a case as the durable **five-file export** (`case.json`, `sources.json`, `claims.json`, `events.jsonl`, `report.md`) into a target directory (`targetDir` is optional; defaults to `<scoutDir>/<caseId>`).
38
- - `scout_import`: restore a case from a five-file export directory and recompute its decision.
29
+ `dsh-scout` turns this into a reproducible workflow: **collect sources → register evidence → add claims → verify identity → render a report with explicit evidence boundaries**. The report separates "verified", "needs verification", and "unknown", and ends with an interview action list.
39
30
 
40
- The first case fixture is [Snapmaker HR Head](docs/fixtures/dsh-scout/snapmaker-hr-head.json). Its historical material is deliberately marked as `E1` and is not treated as current verification.
31
+ ---
41
32
 
42
- Case state lives in memory by default and is isolated by DSH agent/session identity; `scout_export` / `scout_import` make a case durable across sessions through the five-file format with a replayable `events.jsonl`. Configurable storage is available via plugin config (`scoutDir` / `autoPersist`); `scout_ingest` handles batch source registration from collected search/page results and can draft claims from each item; `scout_search` runs the DSH web provider directly and auto-registers result sources; `scout_compare` renders side-by-side case comparisons. This repository does not yet claim the full product contract is complete.
33
+ ## Mental model: evidence levels (the core concept)
43
34
 
44
- ## Development
35
+ Every claim is bounded by **evidence levels**; the ceiling comes from the sources it cites:
45
36
 
46
- ```sh
47
- pnpm install
48
- pnpm test
49
- pnpm run check:release
37
+ | Level | Meaning | Examples |
38
+ |---|---|---|
39
+ | **E0** | No source / model inference | Unsupported guesses — may only be `unknown` or `needs_verification` |
40
+ | **E1** | User-provided | Information you obtained from an internal channel |
41
+ | **E2** | Authoritative third party | Independent media, recruiting platforms (BOSS/Liepin), business-registry aggregators (Tianyancha/Aiqicha), Wikipedia |
42
+ | **E3** | Official first-hand | National Enterprise Credit Information Publicity System (gsxt.gov.cn), regulatory notices, official filings |
43
+
44
+ **Four disciplines (enforced by the plugin, not bypassable):**
45
+
46
+ 1. A claim's `evidenceLevel` **cannot exceed** its strongest cited source (an E1 source cannot support an E3 conclusion);
47
+ 2. A high-impact conclusion **without a source** may only be `unknown` or `needs_verification`;
48
+ 3. **Identity verification** (`scout_verify_identity`) requires an E3 official registration source, or it refuses to run;
49
+ 4. Decisions default to **conservative**: while the entity is unverified or a blocking claim is unresolved, the decision stays `VERIFY` — it never pretends the case passed.
50
+
51
+ > The guarantee: **when evidence is thin, the report honestly says "not verified yet" instead of overstating a conclusion.**
52
+
53
+ ---
54
+
55
+ ## The lifecycle of a case
56
+
57
+ ```
58
+ scout_start create the case (decision defaults to VERIFY)
59
+ │
60
+ scout_search run a Web Provider search; result URLs register as sources
61
+ │
62
+ scout_ingest batch-register collected results (optionally drafting claims)
63
+ │
64
+ scout_add_source register a source (sourceType + evidenceLevel)
65
+ │
66
+ scout_add_claim add a claim (bounded by the evidence policy)
67
+ │
68
+ scout_verify_identity verify the legal entity from an E3 official source
69
+ │
70
+ scout_verify_claim promote a claim to verified (prior evidence state retained)
71
+ │
72
+ scout_report / scout_questions render the report / derive interview questions
73
+ │
74
+ scout_compare compare several cases side by side
75
+ │
76
+ scout_export / scout_import persist (five files + completion marker), resumable across sessions
77
+ ```
78
+
79
+ You can also drive it explicitly: `/scout status|questions|report|export` (see "Commands & Skill" below).
80
+
81
+ ## Quick start (a complete example)
82
+
83
+ To judge whether a company's HR role is worth pursuing, the agent works like this:
84
+
85
+ ```text
86
+ ① scout_start(caseId=acme-hr, companyName=Acme, roleTitle=HRBP, location=Shenzhen)
87
+ ② Search the registry → scout_add_source(sourceId=s1, sourceType=company_registry/…, evidenceLevel=E2)
88
+ ③ Fetch the official job posting → scout_add_source(sourceId=s2, sourceType=company_official, evidenceLevel=E2)
89
+ ④ Extract duties/qualifications/comp → scout_add_claim(claimId=c1, status=verified, evidenceLevel=E2, …)
90
+ Unsupported high-impact items (e.g. the reporting line) → status=unknown / needs_verification
91
+ ⑤ Try gsxt official verification → scout_verify_identity(…)
92
+ ⑥ scout_report → render the report
50
93
  ```
51
94
 
52
- The 34 tests cover the conservative decision default, evidence-level constraints, identity verification, session isolation, report rendering, export/import round-trips, auto-persist, question generation, and tool cleanup on unload. `check:release` additionally packs the plugin, installs it into isolated temporary DSH profiles for the four exact versions in the table below, verifies `--dump-config`, checks the mounted tools, observes the Cordis unload disposer, and checks package contents. `test:profile` uses `DSH_BIN` when explicitly set; otherwise it downloads the exact `DSH_VERSION` (default `0.2.0-rc.2`) through `npx`.
95
+ The generated report contains:
96
+
97
+ - **Decision**: `PROCEED` / `VERIFY` / `STOP` (with reasons)
98
+ - **Key supporting evidence**: the strongest verified claims
99
+ - **Blocking or unresolved risks**: risks and unknowns that block progress
100
+ - **Verification checklist**: every blocking/important item awaiting verification plus its next action (use it directly as an interview action list)
101
+ - **Coverage (new in 0.9.1)**: per-dimension counts of verified / open / untouched across all 11 investigation dimensions, calling out **untouched dimensions** — the report becomes due-diligence progress, not just an evidence list
102
+ - **Claim ledger**: every claim (status / evidence level / impact / sources / confidence note)
103
+ - **Sources**: URL-linked source list
104
+ - **Interview questions**: suggested questions (untouched dimensions come first)
105
+
106
+ ## Tools (12)
107
+
108
+ | Tool | Purpose |
109
+ |---|---|
110
+ | `scout_start` | Create a due-diligence case |
111
+ | `scout_add_source` | Register a source (with evidence level) |
112
+ | `scout_ingest` | Batch-register collected results (infers source type/evidence level; can draft one claim per item) |
113
+ | `scout_search` | Search through the DSH Web Provider and auto-register result sources (type/evidence inferred per URL) |
114
+ | `scout_add_claim` | Add an evidence-bounded claim |
115
+ | `scout_verify_identity` | Verify the legal entity from an E3 official source |
116
+ | `scout_verify_claim` | Promote a claim with stronger evidence |
117
+ | `scout_report` | Render the current Markdown report |
118
+ | `scout_questions` | Derive a deduplicated, prioritized interview question list (up to 12; case-curated questions keep their quota) |
119
+ | `scout_compare` | Side-by-side comparison of 2–5 cases (decisions/identity/claims/risks/questions) |
120
+ | `scout_export` | Persist the five-file export + completion marker |
121
+ | `scout_import` | Restore a case from an export directory and recompute its decision (tolerates legacy exports, reports `complete`) |
122
+
123
+ ### Output contract (since 0.9.1)
124
+
125
+ Mutating tools (`scout_start` / `scout_add_source` / `scout_ingest` / `scout_search` / `scout_add_claim` / `scout_verify_identity` / `scout_verify_claim` / `scout_import`, 8 in total) **no longer echo the whole case JSON back to the model**:
126
+
127
+ - `value` is a **structured object**: `{ ok, caseId, decision, identity, counts: { sources, claims, verified, open }, changed: { kind, ids }, nextActions, persist? }`;
128
+ - `render` is a **compact summary that never exceeds 1 KB as a whole**: decision + this call's delta + unresolved blockers + next steps. The cap is the render budget itself (`MAX_RENDER_BYTES = 1024` B): the ellipsis's own UTF-8 length is subtracted from the budget before cutting, so **the final string including the ellipsis never exceeds 1024 B**;
129
+ - `persist: { persisted, targetDir, error? }` appears **only on the `autoPersist` path of mutating tools** (the key is omitted when no persist was attempted). **A failed persist is no longer silent**: `persist.persisted === false` plus `persist.error`, and no `case_exported` event is recorded. `scout_export` carries **no `persist` key** — it expresses the same thing through **top-level** `persisted` / `complete` / `error`;
130
+ - **Full data still comes from `scout_report` (Markdown) and `scout_export` (files)** — the model sees a summary, not the whole ledger.
131
+
132
+ Read-class tools keep their own shapes: `scout_report` → `{ caseId, decision, report, coverage }`; `scout_questions` → `{ caseId, questions }`; `scout_compare` → `{ caseIds, report }`; `scout_export` → `{ caseId, targetDir, files, persisted, complete, error? }`, where `targetDir` is the **resolved absolute path**. `scout_import` reuses the mutating envelope and adds `sourceDir` (the directory it restored from) and `complete` (whether the export was complete).
133
+
134
+ ### Commands & Skill (new in 0.9.1)
135
+
136
+ | Entry point | Purpose |
137
+ |---|---|
138
+ | `/scout` command | `status` / `questions` / `report` / `export` sub-actions (reusing the tool logic). The host automatically logs `command/run` + `command/done` around the handler — both names are in the host's known event vocabulary, so the session log stays reconstructable |
139
+ | `scout` Skill | The due-diligence workflow registered by the plugin itself (`ctx.skills.register`, no files, no client bundle): start → search/ingest → claim → verify_identity → report → questions, plus the four evidence disciplines; it disappears on unload |
140
+
141
+ Usage (identical to the implementation):
142
+
143
+ ```text
144
+ /scout status <caseId> | /scout questions <caseId> | /scout report <caseId> | /scout export <caseId> [targetDir]
145
+ ```
146
+
147
+ Both are **optional bindings**: when the host exposes no `commands` / `skills` service they are skipped silently, and plugin loading plus tools are unaffected.
53
148
 
54
149
  ## Install into a DSH profile
55
150
 
56
- The package is an installable DSH bundle:
151
+ Two verified paths:
57
152
 
58
153
  ```sh
154
+ # Path A: git install (fetches source, runs prepare)
59
155
  dsh plugin --profile scout-demo add github:MaxHou-infinity/dsh-scout#<commit>
156
+
157
+ # Path B: local tarball (offline, reproducible; this is the exact form the gate runs)
158
+ pnpm pack # produces dsh-scout-0.9.2.tgz
159
+ dsh plugin --profile scout-demo add "file:$PWD/dsh-scout-0.9.2.tgz"
160
+
161
+ # Verify the mounted tools
60
162
  dsh --profile scout-demo --dump-config
61
163
  ```
62
164
 
63
- Git installs fetch source and run `prepare`. pnpm may require an explicit `allowBuilds` entry for `dsh-scout`; only allow a pinned source you have reviewed. The `@deepseek-ai/dsh-tools` peer range is `>=0.1.0-rc.6 <0.3.0-0`; the `@deepseek-ai/cordis` peer range is `>=4.0.2 <5`.
165
+ Requirements: Node ≥ 22.19. A git install runs `prepare`; pnpm may require an explicit `allowBuilds` entry for `dsh-scout` (only allow a pinned source you have reviewed).
64
166
 
65
167
  ### DSH compatibility
66
168
 
67
- DSH checks a plugin's `@deepseek-ai/dsh-*` peers at install and startup, including prereleases. The `<0.3.0-0` upper bound excludes `0.3.0-alpha` and later releases. **Verified on 2026-10-12** with the 0.9.0 candidate tarball in isolated profiles, covering installation, cold startup, tool mounting, and unload:
169
+ DSH compatibility has **three layers** — do not conflate them:
170
+
171
+ | Layer | Where | Who enforces it | What happens on mismatch |
172
+ |---|---|---|---|
173
+ | **Declaration** | `package.json` → `engines.dsh` (`>=0.1.0-rc.6 <0.3.0-0`) | **Nothing reads it today** (the DSH 0.2.0-rc.2 runtime never reads `engines.dsh`; the official manifest comment says *"DSH compatibility is declarative until a reader enforces it"*) | Expresses author intent only; blocks nothing |
174
+ | **Enforced** | The ranges in `peerDependencies` whose names are `@deepseek-ai/dsh` / `@deepseek-ai/dsh-*` (this repo has exactly one: `@deepseek-ai/dsh-tools: ">=0.1.0-rc.6 <0.3.0-0"`) | DSH install checks + the profile startup preflight `evaluatePluginCompatibility()` | Install is refused, or the plugin row is disabled at startup |
175
+ | **Not enforced** | `@deepseek-ai/cordis: ">=4.0.2 <5"` (and `@deepseek-ai/schemastery`) | DSH's peer name filter only accepts `@deepseek-ai/dsh` / `@deepseek-ai/dsh-*` | Resolved by pnpm; DSH makes no compatibility judgement |
68
176
 
69
- | DSH version | npm channel on that date | Result |
177
+ Three details that matter:
178
+
179
+ 1. **Prereleases participate in matching.** The check is `semver.satisfies(runtime, range, { includePrerelease: true })`. The `<0.3.0-0` upper bound exists precisely to exclude `0.3.0-alpha` and later *while* prereleases are included; conversely, **the range depends on that flag** — without it, `0.2.0-rc.2` would be judged incompatible. **When checking this range with other tooling, pass `includePrerelease`**: under default semver semantics (node-semver's prerelease rule accepts a prerelease only through a comparator with the same major.minor.patch tuple), `0.1.2-rc.1` / `0.2.0-rc.2` / `0.2.1-alpha.2` are all judged **unsatisfied** (measured with semver 7.8.5: `0.1.0-rc.6` is true under both semantics; the other three are plain=false / incl=true; `0.3.0-alpha.1` is false under both). **DSH's verdict is the one that counts.**
180
+ 2. **The failure mode is "silently not loaded", not an error.** Specs that can be judged up front (adding a registry package) are refused before pnpm runs, with the exemption command printed. Git / tarball specs are judged after installation, and a failure rolls back the profile manifest and lockfile. But a plugin **already in the profile whose mismatch is only discovered at startup** has its row disabled: the process writes exactly one stderr line, `dsh: disabling profile plugin <row>: <reason>`. What the user sees is "the plugin isn't working" (no tools, no `/scout`) — not a loud red error.
181
+ 3. **That single `@deepseek-ai/dsh-tools` peer carries the entire gate.** Changing the peer range changes the compatibility promise.
182
+
183
+ #### Escape hatch: profile-level exact-version exemptions
184
+
185
+ If only the **declared range has expired** (say DSH publishes `0.3.0-alpha.1` while the plugin still runs fine), you can exempt it in the profile instead of shipping a new version:
186
+
187
+ ```sh
188
+ dsh plugin --profile <name> version-exemptions # inspect current exemptions and the runtime version
189
+ dsh plugin --profile <name> allow-version <package>@<version> --dsh-version <exact-dsh-version> --accept-risk
190
+ dsh plugin --profile <name> revoke-version <package>@<version> --dsh-version <exact-dsh-version>
191
+ ```
192
+
193
+ The exemption lives in the profile's `compatibility.json`, beside `package.json` and `cordis.patch.yml` (exact `package@version` → list of exact DSH versions). Four boundaries:
194
+
195
+ - **Exact versions only** — ranges are rejected;
196
+ - it authorizes **one package version paired with one DSH version**, and is **not inherited** by plugin or DSH upgrades;
197
+ - it does **not** authorize dependency install scripts;
198
+ - it takes effect on the **next composition**: a live profile recomposes and mounts it, a startup-only profile needs a restart.
199
+
200
+ Granting requires explicit risk acceptance (CLI `--accept-risk`, plugin manager `acceptRisk: true`); DSH prints a risk warning before writing — an incompatible plugin may crash or corrupt data.
201
+
202
+ #### Verified version matrix
203
+
204
+ **The 0.9.2 candidate tarball passed on four exact DSH versions** (2026-10-13, isolated profiles, all four binaries fetched from npm rather than the local DSH): covering installation, cold startup, tool mounting, the **extended six-step real-host gate** (`scout_start` → export → cold restart → lazy rehydration → cross-language import → uninstall, plus the "marker missing / `complete:false`" negative controls), and unload. The full gate runs through `npm run check:release`; measured output:
205
+
206
+ ```text
207
+ dsh-scout release gate: PASS (4 DSH versions, >=0.1.0-rc.6 <0.3.0-0)
208
+ ```
209
+
210
+ | DSH version | npm channel (checked 2026-10-12) | 0.9.2 candidate tarball |
70
211
  | --- | --- | --- |
71
212
  | `0.1.0-rc.6` | Declared lower bound | Pass |
72
213
  | `0.1.2-rc.1` | Older release regression | Pass |
73
214
  | `0.2.0-rc.2` | `latest` / `next` | Pass |
74
215
  | `0.2.1-alpha.2` | `alpha` | Pass |
75
216
 
76
- Future `0.2.x` versions still require runtime verification. The [nightly compatibility workflow](https://github.com/MaxHou-infinity/dsh-scout/actions/workflows/compatibility-nightly.yml) resolves npm's `latest`, `next`, and `alpha` tags to exact versions before testing each one. The badge shows the latest workflow result; this table records versions already verified.
217
+ This table previously recorded the **0.9.0** and **0.9.1** candidate tarball results; 0.9.2 keeps the identical `peerDependencies` and `engines.dsh` ranges (**unchanged**), aligns only `@deepseek-ai/schemastery` from `^3.18.2` to `~3.18.4`, and passed the matrix again on the same four exact versions under the extended gate (4/4), so it is updated to this version. The npm-channel column is still the 2026-10-12 check; this re-run did not re-check it. The `/scout` command and Skill added in 0.9.1 are optional bindings, skipped silently on hosts without the corresponding services, so they do not affect loading.
218
+
219
+ Future `0.2.x` versions still require runtime verification. The [nightly compatibility workflow](https://github.com/MaxHou-infinity/dsh-scout/actions/workflows/compatibility-nightly.yml) resolves npm's `latest`, `next`, and `alpha` tags to exact versions before running tests and the isolated profile check. The badge shows the latest workflow result; this table records versions already verified.
220
+
221
+ > **A red nightly does not mean the plugin is broken**: it may simply mean the declared range expired (e.g. DSH published `0.3.0-alpha.1`). `check:release` reports "out of declared range (expected)" separately from "in range but failing (regression)" — the former calls for updating the range or using the exact-version exemption above, not for a regression fix.
77
222
 
78
223
  ## Configuration (optional)
79
224
 
@@ -82,12 +227,36 @@ Configure `dsh-scout` in the DSH profile's `cordis.patch.yml`:
82
227
  ```yaml
83
228
  - id: dsh-scout
84
229
  config:
85
- scoutDir: /path/to/scout-cases # default export dir; cases land in <scoutDir>/<caseId>/
230
+ scoutDir: /path/to/scout-cases # default export dir; cases land in <scoutDir>/<caseId>/ (default 'dsh-scout')
86
231
  autoPersist: true # write the five files after every mutation (default false)
232
+ language: en # 'zh-CN' (default) or 'en'
233
+ authorityHostSuffixes: # E3 trusted-host suffixes (defaults cover government/registry/exchange domains)
234
+ - gov.cn
87
235
  ```
88
236
 
89
- - Without `scoutDir`, `scout_export` with no `targetDir` writes to `./dsh-scout/<caseId>/`;
90
- - With `autoPersist: true`, every `scout_start` / `scout_add_source` / `scout_ingest` / `scout_search` / `scout_add_claim` / `scout_verify_*` persists automatically; a failed write does not break the main flow.
237
+ - A relative `scoutDir` resolves against the **session cwd**, not the DSH process cwd;
238
+ - With `autoPersist: true`, every mutation persists automatically; **failures are visible**: the return value carries `persist.persisted=false` plus `persist.error`, and no `case_exported` event is written;
239
+ - `language` affects the report, interview questions, the comparison report, and the explanatory text that `scout_ingest` writes into auto-drafted claims (`confidenceNote` / `nextAction`); it also aligns case-owned text that is **still at its factory default**, at two different granularities: `decisionObjective` / `role.fitQuestion` are decided **per field** (a field still at its factory default is aligned); curated interview questions are decided **entry by entry, by index** — when the set has the factory length, an entry still equal to its factory slot is replaced by this deployment's default at the same index and **an entry the user edited is kept verbatim**; a set whose length differs from the factory array is taken as fully user-authored and preserved as a whole. The test is "does this entry equal its factory slot", not edit history (editing an entry back to the factory text makes it align). It is a no-op when it matches the case's own language, so the `zh-CN` default changes nothing for existing users. Separately, `language` in `case.json` is the **rendering language of this deployment**, not the language of the file's contents: alignment reads only that label and **never checks whether the label is consistent with the content**, so a hand-edited file whose `language` disagrees with its prose is taken at its word and skipped by the "same language" early return (the normal export / import / upgrade paths are unaffected: a 0.9.0 export always says `zh-CN`, matching its content).
240
+
241
+ ## Data persistence
242
+
243
+ Cases live in memory by default, isolated per session; `scout_export` writes one copyable directory:
244
+
245
+ ```
246
+ case.json case body (including the decision)
247
+ sources.json source list
248
+ claims.json claim ledger
249
+ events.jsonl replayable event stream (case_started / source_added / claim_added /
250
+ identity_verified / claim_verified / case_exported /
251
+ case_imported / case_rehydrated)
252
+ report.md current report snapshot
253
+ export.complete.json since 0.9.1: completion marker (written as complete:false first, rewritten to complete:true once the five files land)
254
+ ```
255
+
256
+ - The export order is "**record the `case_exported` event first, then write files**", so the on-disk `events.jsonl` includes that export; "export → import → export again" yields a stable event sequence;
257
+ - The completion marker `export.complete.json` holds `{ schemaVersion, caseId, complete, files, eventCount }`: it is written as **`complete: false` before any file is written** and rewritten to `complete: true` only after all five data files have landed — so **a stale marker never claims completeness for a half-written export** (when overwriting, the previous successful marker is downgraded before the new files are written). `scout_import` reads the marker's **`complete` field**, not merely whether the marker file exists; **`eventCount` matches the line count of `events.jsonl` only when `complete: true`** (after an interrupted export it records the event count that failed attempt expected) — read `complete`, do not compare `eventCount` alone; the marker's `files` and the `files` returned by `scout_export` are **the same key with different meanings** — the marker lists only the **five data files**, while `scout_export` returns the **six names** it wrote (the five data files plus the marker itself), and both are correct;
258
+ - `scout_import` accepts a **legacy export with no completion marker**; when the marker is missing or says `complete: false`, the import still proceeds **as long as all five data files are present**, but the return value reports `complete: false` so the caller can decide whether to trust the snapshot (a brand-new export missing any of the five files — e.g. interrupted while writing `claims.json` — still fails hard);
259
+ - **Whether a session restore rehydrates automatically depends on export completeness**: on a cache miss, **only a complete export** (completion marker `complete: true`) is lazily rehydrated, appending a `case_rehydrated` event, keeping a "case that appeared from nowhere" auditable; **a 0.9.0 export has no completion marker and is never auto-rehydrated** — restore it explicitly with `scout_import`. Alignment itself only runs on the `decide` path — it executes on the tools that recompute the decision (`scout_add_claim` / `scout_ingest` / `scout_search` / `scout_verify_identity` / `scout_verify_claim` / `scout_import`) and on lazy rehydration after a cache miss; rehydration is triggered by the **first tool call that touches the case** (`requireCase`), so after a cold start even a plain `scout_report` can carry one alignment, while `scout_start`, which only creates the case, does not reach it. The aligned values land in the in-memory case and in later exports on the first `decide`; **a third-party tool that reads `case.json` straight from disk, never passing through the plugin, sees no alignment**.
91
260
 
92
261
  ## Design boundaries
93
262
 
@@ -95,9 +264,38 @@ Configure `dsh-scout` in the DSH profile's `cordis.patch.yml`:
95
264
  - It does not send applications, emails, or personal identity data to third parties.
96
265
  - It does not turn funding, company self-description, or a job posting into verified success claims.
97
266
  - It is not legal, investment, or medical advice.
267
+ - Identity verification depends on E3 official sources; when gsxt and friends are unreachable, the report marks the item as unverified.
268
+ - **Explicitly not in 0.9.1 or 0.9.2**: the client panel, custom session events, the `ctx.storageDomain` persistence rewrite, and scoring/ranking. See [CHANGELOG](CHANGELOG.md) for the reasons and boundaries; "custom session events would make the plugin's own sessions unreconstructable" is a hard constraint — see [product contract §5.1](docs/dsh-scout-product-contract.md).
269
+
270
+ See [the product contract](docs/dsh-scout-product-contract.md) for the full MVP boundary and acceptance criteria. The first fixture is [Snapmaker HR Head](docs/fixtures/dsh-scout/snapmaker-hr-head.json).
271
+
272
+ ## Development
273
+
274
+ ```sh
275
+ pnpm install
276
+ pnpm test # build + unit tests (decision defaults/evidence constraints/identity/session isolation/report rendering/export-import round-trip/auto-persist/questions/ingest-to-claim/multi-language/package contract)
277
+ pnpm run check:release # profile gate on four exact DSH versions + package-content check
278
+ ```
279
+
280
+ `test:profile` runs a **six-step real-host scenario chain** inside isolated profiles: create (`scout_start` + `scout_add_source` + `scout_add_claim`) → export (absolute `targetDir`, 6 files, completion marker `complete: true`) → cold restart (a second process on the same `DSH_HOME`/profile) → lazy rehydration (no `scout_import`; decision unchanged and `case_rehydrated` persisted) → cross-language import (an `en` profile imports the zh export; report and `report.md` CJK = 0) → uninstall (no registry residue), plus three negative controls: `output.schema` sensitivity, missing marker, and marker `complete:false`. A full run takes ~16 s and can be trimmed with `DSH_SCOUT_SMOKE_SKIP_CROSS_LANGUAGE` / `DSH_SCOUT_SMOKE_SKIP_REHYDRATION_CONTROL` / `DSH_SCOUT_SMOKE_SKIP_NEGATIVE_CONTROL`; every skip is reported in the PASS line as degraded coverage. **Still not covered** (spelled out in the script comments): the real LLM / agent loop and approval policy, relative paths (`agent` unset leaves `cwd` undefined, so the gate uses absolute paths only), `autoPersist`, a real process interruption (the torn export is reproduced by rewriting the marker), and the other 6 tools, which are asserted by registration only. `check:release` then re-runs the same gate on four exact DSH versions (reporting "out of declared range (expected)" separately from "in range but failing (regression)") and finally checks the packed contents.
98
281
 
99
- See [the product contract](docs/dsh-scout-product-contract.md) for the full MVP boundary and acceptance criteria.
282
+ ## Roadmap
283
+
284
+ - ✅ v0.1: evidence discipline, three-state decisions, structured report, session isolation
285
+ - ✅ v0.2: five-file durable export + event stream + import/restore
286
+ - ✅ v0.3: configurable storage (`scoutDir` / `autoPersist`) + interview question generation (`scout_questions`)
287
+ - ✅ v0.4: research ingestion (`scout_ingest`: batch-register search/page results, inferring source type and evidence level)
288
+ - ✅ v0.5: company/role comparison (`scout_compare`: multi-case side-by-side report with merged questions)
289
+ - ✅ v0.6: ingest-to-claim flow (`scout_ingest` claim drafts: register a source and add a claim in one call)
290
+ - ✅ v0.7: deeper provider integration (`scout_search` through the DSH Web Provider, result sources auto-registered)
291
+ - ✅ v0.8: first npm release (0.8.0); 0.8.1 declared the Schemastery `Config`, added the `exports` map, and fixed optional-service injection; 0.8.2 aligned with the DSH 0.2 rc channel
292
+ - ✅ v0.9.0: adapted to the DSH 0.2 runtime; four-version compatibility matrix + nightly compatibility check + compatibility issue template
293
+ - ✅ v0.9.1: output contract (structured value + compact summary), persistence trustworthiness, call-site service binding with lazy rehydration, `language` and `## Coverage`, `/scout` and the `scout` Skill, distribution contract (`engines.dsh` / `manifestVersion` / locale / icon)
294
+ - ✅ v0.9.2: real-host end-to-end gate (six-step scenario chain + completion-marker negative controls, closing the U11 blind spot in the 0.9.1 verification report), curated interview questions aligned **entry by entry, by index** (closing D10), the render whole-string cap, and `@deepseek-ai/schemastery` aligned to `~3.18.4`
295
+ - ⏭ v0.10.0 (planned): client panel, `ctx.storageDomain` persistence rewrite, `presentationMeta` payloads, comparison-matrix upgrade and extensible evidence policy (**scoring still out of scope**)
100
296
 
101
297
  ## Community
102
298
 
103
299
  This is an independent community plugin for DeepSeek Harness. The repository uses the `dsh-plugin`, `deepseek-harness`, `due-diligence`, `company-research`, `job-research`, `hr-tech`, and `evidence-based` topics for discovery. For install or startup failures, use the [compatibility issue template](https://github.com/MaxHou-infinity/dsh-scout/issues/new?template=compatibility.yml) with exact versions and redacted logs.
300
+
301
+ Version history lives in the [CHANGELOG](CHANGELOG.md).