dsh-data-cleaning-agent 0.3.0 → 0.4.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
@@ -4,6 +4,39 @@
4
4
 
5
5
  ## [Unreleased]
6
6
 
7
+ > 目标版本:`0.4.0`。源码版本、token 到期刷新、401/429/配额故障门与 Linux/Windows 远端 CI
8
+ > 均已通过;尚未创建 `v0.4.0` tag、GitHub Release 或 npm 发布。
9
+
10
+ ### Added
11
+ - 0.4.0 二期第一切片:新增可测的 QCC 工商全景契约(16 个工商工具 + 4 个历史工商工具);
12
+ `enterprise-enrichment` 按 `panorama` / `ownership` / `governance` / `history` 组按需调用,
13
+ 强制来源标记、历史权限降级与付费批次约束。
14
+ - 0.4.0 验收评估器与默认关闭的 `e2e:phase2` Runner:强制 20 企业 / 每企业 ≥15 维、
15
+ 源工具匹配、字段原值一致、历史账号门,并显式拒绝合成证据充当真实 E2E。
16
+ - 只读 `/data-cleaning/api/phase2/capabilities` 预检:报告 16+4 工具注册状态,
17
+ 不调用 QCC,并将历史工具可用性与账号权限验证明确分开。
18
+ - G5-1 QCC Host Bridge(`lib/qcc.js`):通过公共 `ctx.tools.execute()` 程序化调用动态 MCP 工具,
19
+ 支持允许列表、超时/取消、OAuth 重注册窗口、企业去重批处理、多候选暂停与部分失败隔离。
20
+ - 同源 Web 端点 `/data-cleaning/api/g5/capabilities` 与 `/data-cleaning/api/g5/enrich`;
21
+ 计费调用前强制 `confirmPaidCalls:true`,单批最多 100 行。
22
+ - G5 Mock/Contract 测试已覆盖主路径;真实 OAuth/QCC 主路径、token 自然到期刷新与故障注入均已验收。
23
+ - G5-2 安全闭环:默认关闭且仅允许回环 Host 的 E2E Runner、日志/报告脱敏、请求幂等、
24
+ Host 内存 run 状态、多候选人工确认续跑、retryable 失败人工重试和安全调用审计。
25
+ - 上游错误细分为授权、权限、限流、配额、超时、工具刷新、服务不可用和契约拒绝;
26
+ 错误响应不再复述可能包含敏感内容的上游原始 message。
27
+
28
+ ### Changed
29
+ - QCC Host Bridge 兼容 `qcc-dsh-mcp-oauth@0.1.7` 实测注册的
30
+ `mcp__company__*` / `mcp__history__*` legacy serverName,同时保留
31
+ `mcp__qcc-company__*` / `mcp__qcc-history__*` 作为规范名称;capabilities 同时报告规范名与实际运行时名。
32
+
33
+ ### Verified
34
+ - 在隔离 DSH `0.1.1-rc.2` Host 完成真实 OAuth、授权跨重启恢复与 20 家公开企业的 400 次 QCC 调用;
35
+ 严格历史域验收通过:20/20 主体已解析、每企业当前工商最低 15 维、历史工商 4 维。
36
+ - 原始证据与脱敏报告仅保存在 Git 忽略的 `.phase2-e2e/`,权限为 `0600`;未触碰生产端口。
37
+ - 自然过期 access token 的真实 refresh、动态工具恢复与续期后最小真实调用已通过;
38
+ 401/429/配额耗尽故障注入确认无自动重试、人工重试门正确且审计不泄密。
39
+
7
40
  ## [0.3.0] - 2026-09-01
8
41
 
9
42
  > 企查查 MCP 接入 · 方案 A(模型中介式企业名单补全)首个版本。
package/README.en.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  > A data cleaning & completion agent plugin for DeepSeek Harness: local CSV/XLSX/JSON engine plus optional Qichacha (QCC) MCP enterprise-data enrichment. Initiated and maintained by the Qichacha (QCC) team.
4
4
  >
5
- > Current version / 当前版本: **0.3.0**
5
+ > Current source version / 当前源码版本: **0.4.0** (release candidate; npm `latest` remains 0.3.0)
6
6
 
7
7
  [![CI](https://github.com/duhu2000/dsh-data-cleaning-agent/actions/workflows/ci.yml/badge.svg)](https://github.com/duhu2000/dsh-data-cleaning-agent/actions/workflows/ci.yml)
8
8
  [![npm](https://img.shields.io/npm/v/dsh-data-cleaning-agent)](https://www.npmjs.com/package/dsh-data-cleaning-agent)
@@ -55,19 +55,31 @@ Or let an agent install it for you:
55
55
  | Async jobs | web `/data-cleaning/api/mvp/jobs` | job state machine + persistent storage |
56
56
  | UI | web `/data-cleaning/` | upload → clean/complete → export |
57
57
  | Skill | `data-cleaning` | guides the model through the workflow |
58
- | QCC enrichment | (planned, see below) | backfill list with Qichacha MCP enterprise data |
58
+ | QCC Skill enrichment | `enterprise-enrichment` | 0.4.0 release candidate: company panorama, ownership, governance, and historical registration |
59
+ | 0.4.0 preflight | web `/data-cleaning/api/phase2/capabilities` | Read-only 16+4 dynamic-tool check; makes no QCC or paid calls |
60
+ | QCC Host Bridge | web `/data-cleaning/api/g5/*` | 0.4.0 release candidate: real OAuth/QCC path, natural-expiry refresh, and fault injection verified |
59
61
 
60
- ## Qichacha MCP enrichment (roadmap)
62
+ ## Qichacha MCP enrichment (status and roadmap)
61
63
 
62
- Besides local deterministic completion, the plugin will (in later releases) enrich lists with
63
- Qichacha MCP enterprise data:
64
+ Besides local deterministic completion, the plugin supports Qichacha MCP enterprise-data enrichment:
64
65
 
65
66
  - **Plan A (model-mediated, first)**: after the user connects Qichacha with
66
67
  `qcc-dsh-mcp-oauth`, the Skill guides the model to call
67
68
  `mcp__qcc-company__get_company_by_query` / `mcp__qcc-company__get_company_registration_info`
68
69
  per company name and feed the fresh registration data back into the completion tool.
69
- - **Plan B (programmatic, later)**: `lib/qcc.js` calls Qichacha MCP tools directly in the host
70
- half and batch-completes; the model sees only the final summary.
70
+ - **Plan B (programmatic, 0.4.0 release candidate)**: the Host Bridge now supports batch enrichment,
71
+ idempotency, explicit candidate-resolution resume, manual retry of retryable failures, and
72
+ metadata-only auditing through the public `ctx.tools.execute()` runtime. Paid endpoints require
73
+ both `confirmPaidCalls:true` and a unique `idempotencyKey`; ambiguous candidates are never
74
+ auto-selected. A loopback-only, fail-closed E2E runner is ready. On 2026-09-01 an isolated rc.2
75
+ Host passed real OAuth, restart recovery, and 400 QCC calls across 20 public companies; expiry-time
76
+ token refresh and rate-limit/quota fault injection remain release-blocking gates.
77
+
78
+ The Bridge accepts both the documented `mcp__qcc-company__*` names and the legacy
79
+ `mcp__company__*` names observed from `qcc-dsh-mcp-oauth@0.1.7`. A fresh rc.2 profile must also
80
+ install the matching `@deepseek-ai/dsh-mcp-client` explicitly; see the compatibility guide.
81
+
82
+ See [the 0.4.0 release checklist](docs/RELEASE-0.4.0.md) for scope, blocking gates, and rollback steps.
71
83
 
72
84
  See [docs/PLAN-OSS.md](docs/PLAN-OSS.md) for details.
73
85
 
@@ -84,6 +96,8 @@ npm run check
84
96
 
85
97
  `npm run check` runs lint, documentation version consistency, pack whitelist verification and
86
98
  unit tests.
99
+ Real G5 validation must be enabled explicitly according to
100
+ [the E2E runbook](docs/G5-E2E-RUNBOOK.md); `npm run e2e:g5` refuses to run by default.
87
101
 
88
102
  ## Configuration
89
103
 
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  > 在 DeepSeek Harness 中清洗、补全、画像企业名单数据的智能体插件:本地 CSV/XLSX/JSON 引擎 + 可选企查查(Qichacha/QCC)MCP 企业数据补全,由企查查(Qichacha/QCC)团队发起并维护。
4
4
  >
5
- > 当前版本 / Current version: **0.3.0**
5
+ > 当前源码版本 / Current source version: **0.4.0**(发布候选;npm `latest` 仍为 0.3.0
6
6
 
7
7
  [![CI](https://github.com/duhu2000/dsh-data-cleaning-agent/actions/workflows/ci.yml/badge.svg)](https://github.com/duhu2000/dsh-data-cleaning-agent/actions/workflows/ci.yml)
8
8
  [![npm](https://img.shields.io/npm/v/dsh-data-cleaning-agent)](https://www.npmjs.com/package/dsh-data-cleaning-agent)
@@ -52,20 +52,36 @@ bash <(curl -fsSL https://raw.githubusercontent.com/duhu2000/dsh-data-cleaning-a
52
52
  | 异步任务 | web `/data-cleaning/api/mvp/jobs` | 任务状态机 + 持久化存储 |
53
53
  | UI | web `/data-cleaning/` | 上传 → 清洗/补全 → 导出 |
54
54
  | Skill | `data-cleaning` | 引导模型按工作流调度上述工具 |
55
- | 企查查补全 | (规划中,见下文) | 用企查查 MCP 企业数据回填名单 |
55
+ | 企查查 Skill 补全 | `enterprise-enrichment` | 0.4.0 发布候选:工商全景、股权穿透与历史工商 |
56
+ | 0.4.0 工具预检 | web `/data-cleaning/api/phase2/capabilities` | 只读检查 16+4 动态工具,不发起 QCC/付费调用 |
57
+ | QCC Host Bridge | web `/data-cleaning/api/g5/*` | 0.4.0 发布候选:后台批量基础层;真实 OAuth/QCC 主路径、自然到期刷新与故障注入均已验收 |
56
58
 
57
- ## 企查查 MCP 补全(路线图)
59
+ ## 企查查 MCP 补全(状态与路线图)
58
60
 
59
- 插件同时提供本地确定性补全,以及(后续版本)接入企查查 MCP 企业数据的能力:
61
+ 插件同时提供本地确定性补全和企查查 MCP 企业数据补全:
60
62
 
61
63
  - **方案 A(模型中介,优先)**:用户已用 `qcc-dsh-mcp-oauth` 连接企查查后,
62
64
  Skill 引导模型对名单中每个企业名调用 `mcp__qcc-company__get_company_by_query` /
63
65
  `mcp__qcc-company__get_company_registration_info`,把返回的最新工商信息回填到补全工具。
64
- - **方案 B(后台程序化,后续)**:`lib/qcc.js` host 半区直接调用企查查 MCP 工具,
65
- 批量补全,模型只见最终摘要。
66
+ `main` 上的 0.4.0 开发切片已将 16 个工商工具和 4 个历史工商工具固化为可测契约,
67
+ 按 `panorama` / `ownership` / `governance` / `history` 维度组按需调用;未显式选择时不会默认打满全部付费工具。
68
+ - **方案 B(后台程序化,0.4.0 发布候选)**:Host Bridge 已通过公共 `ctx.tools.execute()` 实现
69
+ 批量补全、请求幂等、多候选人工确认续跑、retryable 失败人工重试与安全审计。
70
+ 批量 Web 端点同时要求 `confirmPaidCalls:true` 和唯一 `idempotencyKey`;多候选绝不自动选择。
71
+ 默认关闭的本机 E2E Runner 已就绪;2026-09-01 已在隔离 rc.2 Host 完成真实 OAuth、跨重启恢复和
72
+ 20 家公开企业的 400 次 QCC 调用;自然过期 token 的真实刷新、动态工具恢复及 1 行续期后调用也已通过。
73
+ 401 / 429 / 配额耗尽使用 Web→Bridge→ToolRuntime 故障注入验证,不额外消耗真实付费批次。
74
+
75
+ `qcc-dsh-mcp-oauth@0.1.7` 在 rc.2 实测注册为 `mcp__company__*` / `mcp__history__*`;
76
+ 本插件的兼容 Bridge 会自动映射到文档规范名 `mcp__qcc-company__*` / `mcp__qcc-history__*`。
77
+ 隔离 Profile 还需显式安装与 Host 同版本的 `@deepseek-ai/dsh-mcp-client`,详见兼容性文档。
66
78
 
67
79
  详见 [docs/PLAN-OSS.md](docs/PLAN-OSS.md)。
68
80
 
81
+ 0.4.0 真实账号验收的证据格式、安全门和命令见
82
+ [docs/PHASE2-ACCEPTANCE.md](docs/PHASE2-ACCEPTANCE.md)。
83
+ 发布范围、阻断门和回滚步骤见 [docs/RELEASE-0.4.0.md](docs/RELEASE-0.4.0.md)。
84
+
69
85
  ## 本地开发
70
86
 
71
87
  要求 Node.js 20 或更高。DSH 运行期服务(`ctx.tools` / `ctx.skills` / `ctx.jobs` /
@@ -77,6 +93,7 @@ npm run check
77
93
  ```
78
94
 
79
95
  `npm run check` 会依次执行 lint、文档版本一致性、发布包白名单校验与单元测试。
96
+ 真实 G5 验收必须按 [E2E 手册](docs/G5-E2E-RUNBOOK.md) 显式开启;默认执行 `npm run e2e:g5` 会安全拒绝。
80
97
 
81
98
  ## 配置
82
99
 
@@ -11,12 +11,15 @@
11
11
 
12
12
  > 生产 GUI(`http://127.0.0.1:43120`)不用于验证,验证一律使用隔离 `DSH_HOME` + 专用端口。
13
13
 
14
+ 2026-09-01 的 0.4.0 发布候选已分别在 rc.2(43153)和 alpha.2(43154)
15
+ 隔离 Host 完成 tarball 加载冒烟,两者均返回 `enrichSkillRegistered:true`;测试进程已停止。
16
+
14
17
  ## 2. Node 运行时
15
18
 
16
19
  - 本包 `engines.node` 声明 `>=20`。
17
20
  - CI 矩阵按 ADR-0001 收敛为 **Node 22 / 24**(本机 Desktop `engines` 为 `^22.19.0 || >=24.0.0`)。
18
21
 
19
- ## 3. 契约面(Spike #1–#6 已实测)
22
+ ## 3. 契约面(Spike #1–#7 已实测)
20
23
 
21
24
  | 契约 | 用法 | 备注 |
22
25
  | --- | --- | --- |
@@ -29,12 +32,13 @@
29
32
  | 存储 | `ctx.storageDomain` | `open({name,version,tables})` → `table('jobs')` |
30
33
  | web 路由 | `webServer.register({kind:'prefix', path, handler})` | 最长前缀匹配;前缀需以 `/` 结尾且匹配 `pathname.startsWith(prefix + '/')` |
31
34
  | 同源守卫 | `isTrusted(req)` | `sec-fetch-site !== 'cross-site'` 且 origin 为 127.0.0.1/localhost |
35
+ | 程序化工具调用 | `ctx.tools.get()` + `ctx.tools.execute()` | S7 双基线验证;每次调用重新解析,不缓存动态 MCP 工具 |
32
36
 
33
37
  ## 4. 与企查查 MCP OAuth 插件的共存
34
38
 
35
39
  | | `qcc-dsh-mcp-oauth` | 本插件 |
36
40
  | --- | --- | --- |
37
- | 工具名前缀 | `qcc_oauth_*` + `mcp__qcc-*` | `data_clean_rows` / `data_complete_rows` / `data_profile` |
41
+ | 工具名前缀 | `qcc_oauth_*` + 规范 `mcp__qcc-*`;0.1.7 实测为 legacy `mcp__company__*` 等 | `data_clean_rows` / `data_complete_rows` / `data_profile` |
38
42
  | Skill | — | `data-cleaning`、`enterprise-enrichment` |
39
43
  | 存储域 | 自有 grant store | `dc_tasks_v1` |
40
44
  | 能否共存 | ✅ | ✅(工具名 / Skill 名 / 存储域 / 条目 id 全独立) |
@@ -44,9 +48,25 @@
44
48
  `mcp__qcc-company__*` / `mcp__qcc-risk__*`(授权成功后由 mcp-client 动态提供)。
45
49
  - 若 qcc-dsh-mcp-oauth 未安装或未授权,`enterprise-enrichment` Skill 的第一步
46
50
  `qcc_oauth_status` 即会中断并引导用户先连接,不会假装补全。
51
+ - G5 Host Bridge 不读取 grant/token,也不访问 mcp-client 私有 client;只经共享 `ctx.tools`
52
+ 调用动态注册的 `mcp__qcc-*` 工具。G5-2 增加幂等、候选续跑、人工重试与安全审计;
53
+ run 明细仅驻留 Host 内存。Bridge 会把 OAuth 0.1.7 的 legacy `mcp__company__*` / `mcp__history__*`
54
+ 映射到规范名称,并在 capabilities 中同时报告两者。
55
+
56
+ ### 4.1 2026-09-01 rc.2 实测结论
57
+
58
+ - fresh Profile 必须显式安装与 Host 同版本的 `@deepseek-ai/dsh-mcp-client@0.1.1-rc.2`;
59
+ 仅依赖 DSH CLI 全局副本时,OAuth grant 可恢复但动态工具不会进入 Profile 的可调用工具面。
60
+ - `qcc-dsh-mcp-oauth@0.1.7` 的 `serverName` 实际为 `company/history/...`,注册名因此不带 `qcc-`。
61
+ 当前 Bridge 已兼容;上游修复后无需迁移证据或 Skill 规范名。
62
+ - 真实 OAuth、跨重启恢复、16+4 工具预检、20 企业/400 调用及自然到期 refresh 已通过;
63
+ refresh 后 16+4 工具恢复,并以 1 行真实 enrich 验证新 token 可用。
47
64
 
48
65
  ## 5. 已知限制
49
66
 
50
67
  - alpha.2 的 `@Remote` 契约仍可能变动,本包不对其作稳定 API 承诺。
51
68
  - web 半区仅 web 组合可用;headless 组合自动跳过(工具与 Skill 仍注册)。
52
69
  - XLSX 解析依赖 `xlsx`(懒加载),缺失时返回 `XLSX_UNAVAILABLE` 而非崩溃。
70
+ - `/data-cleaning/api/g5/enrich` 当前为 0.4.0 发布候选能力,单批上限 100 行、并发上限 4,
71
+ 且必须显式 `confirmPaidCalls:true` 和唯一 `idempotencyKey`;token 到期刷新与 401/429/配额故障门已通过,
72
+ 正式可用版本仍以 npm/GitHub Release 为准。
@@ -0,0 +1,110 @@
1
+ # G5 真实 E2E 验收手册
2
+
3
+ - 日期:2026-09-02
4
+ - 当前状态:**真实 OAuth、跨重启恢复、token 自然到期刷新、QCC 主调用路径与 401/429/配额故障注入均已执行**
5
+ - 适用脚本:`npm run e2e:g5`
6
+ - 示例夹具:`test/fixtures/g5-e2e.example.json`(仅虚构数据)
7
+
8
+ ## 安全前提
9
+
10
+ Runner 默认关闭,并同时执行以下硬门:
11
+
12
+ 1. 必须显式设置 `G5_E2E=1`。
13
+ 2. `G5_BASE_URL` 只允许 `http://127.0.0.1:*` 或 `http://localhost:*`,拒绝远端地址。
14
+ 3. `G5_E2E_MODE=enrich` 还必须显式设置 `G5_E2E_CONFIRM_PAID_CALLS=YES`。
15
+ 4. enrich 模式要求 capabilities 已为 `ready:true`;Runner 不代替用户发起 OAuth 连接。
16
+ 5. 报告仅保留状态、计数、错误码和安全审计数量,不写原始行、候选详情或 QCC 原始响应。
17
+ 6. 报告文件以 `0600` 权限创建;默认写入系统临时目录。
18
+
19
+ 不要把真实 Token、企业名单或真实 E2E 报告提交到 Git。仓库已忽略 `.env.g5-e2e` 与 `.g5-e2e/`。
20
+
21
+ > 2026-09-01 补充:在隔离 rc.2 Profile 中,`qcc-dsh-mcp-oauth@0.1.7` 还需要显式安装
22
+ > `@deepseek-ai/dsh-mcp-client@0.1.1-rc.2`;其实际工具名不带 `qcc-` 前缀,当前 Bridge 已兼容。
23
+ > 20 家公开企业的 400 次当前/历史工商调用已通过 `e2e:phase2` 严格验收。
24
+
25
+ > 2026-09-02 补充:复用同一隔离 Profile 的自然过期 grant,在端口 `43159` 启动 0.4.0 候选后,
26
+ > OAuth 插件自动刷新 access token,16+4 动态工具恢复;随后以 1 行批准夹具执行真实 enrich,
27
+ > 1/1 成功、0 失败、2 条安全审计。输入与报告均为 Git 忽略的 `0600` 文件,测试 Host 已停止,
28
+ > 生产端口 `43120` 未触碰。
29
+ >
30
+ > 401、429 与配额耗尽采用 Web→Bridge→Mock ToolRuntime 故障注入,避免伪造真实账号故障或重复付费批次。
31
+ > 三类错误均只派发一次失败调用;401/429 仅在显式 `/retry` 后重新派发,配额耗尽拒绝重试;
32
+ > 审计仅含工具名、callId、attempt、稳定错误码与耗时。
33
+
34
+ ## 1. 被动 preflight
35
+
36
+ 此步骤只读取 capabilities,不调用 OAuth 或计费 QCC 工具:
37
+
38
+ ```bash
39
+ G5_E2E=1 \
40
+ G5_E2E_MODE=preflight \
41
+ G5_BASE_URL=http://127.0.0.1:43150 \
42
+ npm run e2e:g5
43
+ ```
44
+
45
+ 预期:生成临时脱敏报告;Host 未连接时显示 `not-connected-or-refreshing` 或 `oauth-plugin-missing`。
46
+
47
+ ## 2. 准备本机夹具
48
+
49
+ 把示例复制到仓库外或已忽略目录,再替换为经过批准的脱敏名单:
50
+
51
+ ```bash
52
+ cp test/fixtures/g5-e2e.example.json /private/tmp/g5-e2e-input.json
53
+ chmod 600 /private/tmp/g5-e2e-input.json
54
+ ```
55
+
56
+ 夹具结构:
57
+
58
+ ```json
59
+ {
60
+ "headers": ["name"],
61
+ "nameField": "name",
62
+ "includeRisk": false,
63
+ "concurrency": 1,
64
+ "rows": [{ "name": "批准用于测试的企业" }],
65
+ "selections": [
66
+ {
67
+ "companyName": "需要人工消歧的输入名",
68
+ "selectedCreditNo": "人工确认的候选信用代码"
69
+ }
70
+ ],
71
+ "retryCompanyNames": []
72
+ }
73
+ ```
74
+
75
+ `selections` 只能填写 enrich 返回的候选;Host 会再次校验信用代码是否属于待复核列表。`retryCompanyNames` 只能填写错误队列中 `retryable:true` 的企业。
76
+
77
+ ## 3. 真实 enrich Gate
78
+
79
+ 先由用户在隔离 DSH Profile 内完成 QCC OAuth,再确认测试调用额度,最后运行:
80
+
81
+ ```bash
82
+ G5_E2E=1 \
83
+ G5_E2E_MODE=enrich \
84
+ G5_E2E_CONFIRM_PAID_CALLS=YES \
85
+ G5_BASE_URL=http://127.0.0.1:43150 \
86
+ G5_FIXTURE_PATH=/private/tmp/g5-e2e-input.json \
87
+ G5_E2E_REPORT=/private/tmp/g5-e2e-report.json \
88
+ npm run e2e:g5
89
+ ```
90
+
91
+ Runner 为 enrich、每次候选确认和人工重试生成稳定幂等键。同一 Host 内重复执行相同输入时应得到 `idempotencyReplayed:true`,不得再次调用计费工具。
92
+
93
+ ## 4. 必验场景
94
+
95
+ 1. 未授权:capabilities 非 ready,Runner 在 enrich 前关闭。
96
+ 2. 首次授权:用户完成 OAuth 后,capabilities 变为 ready。
97
+ 3. 唯一匹配:完成工商补全。
98
+ 4. 多候选:初次只进入 `awaiting-review`;未确认前不调用工商详情。
99
+ 5. 候选确认:只调用工商详情及可选风险,不重复实体检索。
100
+ 6. 未匹配:保持 unresolved。
101
+ 7. token 刷新:工具短暂消失后恢复;仅 `UNKNOWN_TOOL` 竞态允许一次内部安全重解析。✅ 自然到期刷新已验
102
+ 8. 401、403、429、配额不足、超时和 5xx:映射为稳定错误码,且只能由用户显式重试。✅ 401/429/配额故障注入已验,其余有契约测试
103
+ 9. 混合批次:单企业失败不影响其他企业。
104
+ 10. 报告、日志和审计中无 Token、企业原名、信用代码、邮箱、手机号或原始工具响应。
105
+
106
+ ## 5. 当前限制
107
+
108
+ - G5 run 与幂等记录只保存在 Host 内存,默认 TTL 30 分钟、最多 50 个 run;Host 重启或过期后必须新建 run。
109
+ - 当前不持久化原始/补全行,这是刻意的隐私边界;后续如需跨重启恢复,应先完成加密存储与保留期设计评审。
110
+ - Runner 不自动调用 `qcc_oauth_connect`,真实 OAuth 始终由用户在隔离环境显式完成。
@@ -0,0 +1,136 @@
1
+ # G5 Host Bridge:方案 B 批量补全基础层
2
+
3
+ - 日期:2026-09-02
4
+ - 状态:**G5-2.1~G5-2.5、真实 OAuth/QCC 主路径、token 自然到期刷新与 401/429/配额故障注入已验收**
5
+ - 发布状态:0.4.0 发布候选,尚未创建 tag 或发布 npm 新版本
6
+ - 决策依据:`docs/adr/0002-programmatic-mcp-tool-execution.md`
7
+
8
+ ## 本阶段交付
9
+
10
+ `lib/qcc.js` 基于 DSH 公共 `ctx.tools.get()` / `ctx.tools.execute()` 实现 Host Bridge:
11
+
12
+ 1. 仅允许 `qcc_oauth_*`、规范 `mcp__qcc-*__*` 与 OAuth 0.1.7 已验证 legacy serverName,拒绝任意工具代理。
13
+ 2. 每次调用重新解析工具,兼容 OAuth 刷新造成的注销/重注册窗口;只对 `UNKNOWN_TOOL` 做一次安全重试,其他失败不自动重试,避免重复计费。
14
+ 3. 统一 call ID、AbortSignal、超时和 ToolRuntime `isError`,错误响应不携带工具原始业务数据。
15
+ 4. 解析 MCP `structuredContent` 或 QCC 文本 JSON,复用一期字段契约。
16
+ 5. 批量输入按企业名去重调用;唯一精确主体才继续工商/风险补全,多候选进入 `reviewQueue`,未匹配保留为 `unresolved`。
17
+ 6. 单企业失败隔离,不中断其他企业;原始/补全明细只在 Host/Web 同源边界内流转。
18
+
19
+ ## 数据流
20
+
21
+ ```text
22
+ 同源 Web 请求(显式确认计费)
23
+ → QccHostBridge.enrichRows
24
+ → 企业名去重 + 受控并发(1–4)
25
+ → ctx.tools.get(每次重新解析)
26
+ → ctx.tools.execute(mcp__qcc-company__get_company_by_query)
27
+ ├─ 唯一精确 → 锁定信用代码 → 工商详情 → 可选风险扫描
28
+ ├─ 多候选 → reviewQueue,停止该主体下游调用
29
+ └─ 未匹配 → unresolved
30
+ → 摘要 + 同源明细 + CSV 下载
31
+ ```
32
+
33
+ ## Web 契约
34
+
35
+ ### 被动能力探测
36
+
37
+ `GET /data-cleaning/api/g5/capabilities`
38
+
39
+ 只检查工具是否注册,不调用 OAuth 或任何计费 QCC 工具。返回 Bridge marker、连接态推断和批量限制。
40
+ 同时声明 `idempotencyRequired / candidateResume / manualRetry`,run 状态仅为 `host-memory`。
41
+
42
+ ### 批量补全
43
+
44
+ `POST /data-cleaning/api/g5/enrich`
45
+
46
+ ```json
47
+ {
48
+ "idempotencyKey": "client-generated-unique-key",
49
+ "confirmPaidCalls": true,
50
+ "rows": [{ "name": "示例企业" }],
51
+ "headers": ["name"],
52
+ "nameField": "name",
53
+ "includeRisk": false,
54
+ "concurrency": 2
55
+ }
56
+ ```
57
+
58
+ 约束:
59
+
60
+ - `confirmPaidCalls` 必须严格为 `true`,否则在任何工具调用前返回 `QCC_CONFIRM_REQUIRED`。
61
+ - `idempotencyKey` 必填;相同键与相同请求复用首个结果,不重复调用工具;同键不同请求返回冲突。
62
+ - 单批最多 100 行,并发范围 1–4;重复企业只检索一次。
63
+ - 响应包含 `summary / reviewQueue / errors / rows / csv`;完整明细不得转发给模型。
64
+ - 多候选不会自动取第一项;响应提供 `runId`,状态为 `awaiting-review`。
65
+
66
+ ### 候选确认、人工重试与 run 查询
67
+
68
+ - `POST /data-cleaning/api/g5/resolve`:传入 `runId / companyName / selectedCreditNo / idempotencyKey / confirmPaidCalls:true`。信用代码必须存在于该公司的待复核候选列表;成功后直接调用工商详情和可选风险,不重复实体检索。
69
+ - `POST /data-cleaning/api/g5/retry`:传入 `runId / companyNames / idempotencyKey / confirmPaidCalls:true`。只允许重试错误队列中 `retryable:true` 的企业,且不会自动触发。
70
+ - `GET /data-cleaning/api/g5/run/<runId>`:读取当前同源 run 状态,不调用 QCC 工具。
71
+
72
+ run 状态为 `awaiting-review / needs-retry / completed-with-errors / completed`。明细、候选和幂等结果仅保存在 Host 内存,默认 TTL 30 分钟、最多 50 个 run;Host 重启后失效。
73
+
74
+ ## 错误分类与安全审计
75
+
76
+ Host Bridge 把上游错误归一为稳定错误码:
77
+
78
+ - 401/Token 失效 → `QCC_AUTH_REQUIRED`
79
+ - 403/资源域未授权 → `QCC_PERMISSION_DENIED`
80
+ - 429 → `QCC_RATE_LIMITED`
81
+ - 配额不足 → `QCC_QUOTA_EXHAUSTED`
82
+ - 超时 → `QCC_TIMEOUT`
83
+ - 工具刷新消失 → `QCC_TOOL_UNAVAILABLE`
84
+ - 5xx/连接故障 → `QCC_UPSTREAM_UNAVAILABLE`
85
+ - 参数契约拒绝 → `QCC_UPSTREAM_REJECTED`
86
+
87
+ 错误响应不复述上游原始 message。每次物理工具调用只记录 toolName、callId、attempt、结果、稳定错误码和耗时;不记录参数、企业名或工具响应。
88
+
89
+ ## E2E Runner
90
+
91
+ `scripts/g5-e2e.mjs` 默认关闭、仅允许回环 DSH Host;真实 enrich 还要求独立的付费确认变量。报告只包含脱敏摘要,详见 `docs/G5-E2E-RUNBOOK.md`。
92
+
93
+ ## 已通过的 Mock/Contract 门
94
+
95
+ - Bridge 单元测试覆盖允许列表、每次解析、唯一 call ID、动态工具恢复、取消、超时、细分错误归一化、安全审计、响应解码、消歧、字段映射、锁定候选、去重批量、部分失败、未连接和批量上限。
96
+ - Run/Web 测试覆盖并发幂等、同键冲突、候选合法性、续跑、人工重试、状态过期、capabilities、确认门和 CSV。
97
+ - Runner/脱敏测试覆盖默认关闭、回环限制、付费确认、摘要报告及凭据/企业标识清洗。
98
+ - Web 路由级故障注入覆盖 401、429、配额耗尽:确认不自动重试、人工重试门与安全审计语义。
99
+ - 测试夹具只使用虚构企业与虚构信用代码,不含真实 token 或业务数据。
100
+
101
+ ## DSH rc.2 隔离 Host 冒烟
102
+
103
+ 2026-09-01 将当前工作树打包后安装到临时 `DSH_HOME`,在隔离端口 `43140` 启动 DSH `0.1.1-rc.2`;为规避本机文件监听器的 `EMFILE`,仅在该测试进程设置 `CHOKIDAR_USEPOLLING=1`。结果:
104
+
105
+ - Host 成功执行插件 `apply()`;MVP seam 返回 `qccBridgeMounted:true`、`enrichSkillRegistered:true`,原有三工具与两个 Skill 均保持注册。
106
+ - `GET /data-cleaning/api/g5/capabilities` 返回 `200`、marker `g5-host-bridge`、`ready:false`、`state:oauth-plugin-missing`,未调用 OAuth 或 QCC 工具。
107
+ - 未传 `confirmPaidCalls:true` 的补全请求返回 `409 QCC_CONFIRM_REQUIRED`,证明计费确认门在工具调用前生效。
108
+ - 显式确认后,因隔离 Host 未安装 QCC 动态工具而返回 `503 QCC_NOT_CONNECTED`,没有真实 OAuth、token 刷新或 QCC 请求。
109
+ - 测试 Host 已停止;生产 GUI 端口 `43120` 未触碰。
110
+
111
+ G5-2 在同日将更新后的 27 文件 tarball 安装到隔离 Profile,并在端口 `43141` 追加验证:
112
+
113
+ - capabilities 返回 `idempotencyRequired:true / candidateResume:true / manualRetry:true / runPersistence:host-memory`。
114
+ - 已确认计费但缺少幂等键时返回 `400 QCC_IDEMPOTENCY_REQUIRED`,零 QCC 工具调用。
115
+ - 带合法幂等键时,由于隔离 Host 未安装 OAuth/QCC 工具,安全返回 `503 QCC_NOT_CONNECTED`。
116
+ - `G5_E2E_MODE=preflight` Runner 成功生成权限 `0600` 的脱敏报告,只含 capabilities 摘要。
117
+ - 测试 Host 已停止;没有安装 QCC OAuth、没有真实 QCC 调用,生产端口 `43120` 未触碰。
118
+
119
+ ## 真实 E2E 验收门
120
+
121
+ 2026-09-01 已通过:OAuth PKCE 首连、授权跨重启恢复、真实 company/history 工具调用、
122
+ 20 家公开企业/400 次调用、每企业当前最低 15 维与历史 4 维、证据脱敏边界。
123
+
124
+ 发布前验收状态:
125
+
126
+ 1. 未授权 host:返回 `QCC_NOT_CONNECTED` 并正确引导 `qcc_oauth_connect`。
127
+ 2. 已授权 host:用脱敏名单跑真实 `get_company_by_query` 与 `get_company_registration_info`。✅
128
+ 3. 多候选真实响应:不发生下游工商/风险调用。
129
+ 4. token 临期刷新:刷新期间工具短暂消失后恢复,且无重复计费调用。✅ 自然到期真实 refresh + 续期后 1 行调用
130
+ 5. 401/限流/配额不足/超时:错误分类、部分失败和人工重试符合契约。✅ 401/429/配额故障注入 + 其余契约测试
131
+ 6. includeRisk:风险因子计数逐字引用,不自行加总或推断。
132
+ 7. 审计:日志、响应错误、测试证据均不泄露 token 或未脱敏原始名单。✅ 主路径与故障注入均已验证
133
+
134
+ 2026-09-02 的刷新验收使用隔离 DSH `0.1.1-rc.2`、端口 `43159`:启动前 access token 已自然过期;
135
+ 启动后持久 grant 更新时间与到期时间均前移,16+4 工具恢复为 ready,随后 1 行真实 enrich 1/1 成功。
136
+ 测试 Host 已停止,生产 `43120` 未触碰。错误注入只走本地 Mock ToolRuntime,没有新增真实计费故障调用。
@@ -0,0 +1,133 @@
1
+ # 0.4.0 工商全景验收手册
2
+
3
+ ## 1. 目的与边界
4
+
5
+ `scripts/phase2-acceptance.mjs` 是 0.4.0 源码仓库的本地验收 Runner(不进 npm 运行时包)。
6
+ 它只读取从真实 DSH/QCC
7
+ 工具轨迹整理出的 JSON 证据,不主动联网、不发起付费调用、不读取 OAuth token。
8
+
9
+ Runner 验证:
10
+
11
+ - 不少于 20 条企业记录;
12
+ - 每条的主体消歧已完成,不允许多候选或未解析;
13
+ - `resolveEntity` 和 `registration` 必须是含非空原值对照的 `resolved`,不能以 `no_data` 充数;
14
+ - 每条不少于 15 个当前工商维度已交付;
15
+ - 维度使用的 `sourceTool` 必须与 `lib/qcc-phase2.js` 中的已验证契约一致;
16
+ - `resolved` 字段的 `value` 与 `sourceValue` 必须深度全等,防止金额、比例、计数或股权链被二次计算;
17
+ - 启用历史域门时,必须标记企业认证账号,且每条 4 个历史工商维度都已交付。
18
+
19
+ Runner 是「证据结构和结果契约」的自动检查,不是 QCC 调用器。真实性还需与同次
20
+ DSH session/tool transcript 的时间和调用记录对应;Mock 或人工编造的数据不能作为发布证据。
21
+
22
+ `sourceTool` 同时接受规范名 `mcp__qcc-company__*` / `mcp__qcc-history__*` 与
23
+ `qcc-dsh-mcp-oauth@0.1.7` 实测 legacy 名 `mcp__company__*` / `mcp__history__*`;
24
+ 除此之外的别名仍会以 `SOURCE_TOOL_MISMATCH` 拒绝。
25
+
26
+ ### 1.1 真实调用前的只读预检
27
+
28
+ 在隔离 DSH Host 完成 OAuth 后,先请求:
29
+
30
+ ```bash
31
+ curl -fsS -H 'sec-fetch-site: same-origin' \
32
+ http://127.0.0.1:<隔离端口>/data-cleaning/api/phase2/capabilities
33
+ ```
34
+
35
+ 该端点只读取 ToolRuntime 注册表,不调用 QCC,返回 `paidCalls:false` 和
36
+ `executesTools:false`。进入当前工商 E2E 前应有 `companyRegistered:16` / `companyReady:true`;
37
+ 历史域应有 `historyRegistered:4` / `historyToolsReady:true`。
38
+
39
+ `historyAuthorizationVerified:false` 在预检中始终为 false:工具已注册不等于企业认证账号已获权,
40
+ 账号权限只能在用户明确同意后通过真实历史工具调用验证。
41
+
42
+ ## 2. 证据文件契约
43
+
44
+ 真实证据不进 Git,建议放在已忽略的 `.phase2-e2e/`。企业名及信用代码不写入
45
+ `reference`,只使用 `row-001` 这类不透明行号。
46
+
47
+ ```json
48
+ {
49
+ "schemaVersion": 1,
50
+ "evidenceKind": "qcc-phase2-real-tool-transcript",
51
+ "synthetic": false,
52
+ "historyAccess": "enterprise-certified",
53
+ "records": [
54
+ {
55
+ "reference": "row-001",
56
+ "entityStatus": "resolved",
57
+ "dimensions": [
58
+ {
59
+ "domain": "company",
60
+ "id": "registration",
61
+ "status": "resolved",
62
+ "sourceTool": "mcp__qcc-company__get_company_registration_info",
63
+ "fields": [
64
+ {
65
+ "key": "reg_capital",
66
+ "value": "<实际输出值>",
67
+ "sourceValue": "<同次工具返回原值>"
68
+ }
69
+ ]
70
+ },
71
+ {
72
+ "domain": "company",
73
+ "id": "listing",
74
+ "status": "no_data",
75
+ "sourceTool": "mcp__qcc-company__get_listing_info",
76
+ "fields": []
77
+ }
78
+ ]
79
+ }
80
+ ]
81
+ }
82
+ ```
83
+
84
+ 维度状态:
85
+
86
+ - `resolved`:工具成功返回数据;必须至少有一个非空 `fields` 条目,且输出值与源值完全一致。
87
+ - `no_data`:工具调用成功但该主体无此数据;`fields` 必须为空。该维度计入覆盖。
88
+ - `permission_required` / `not_available` / `error`:显式降级,不计入覆盖数。
89
+
90
+ 公司域 `id` 和历史域 `id` 必须使用 [qcc-phase2.js](../lib/qcc-phase2.js) 的对象键。
91
+
92
+ ## 3. 执行命令
93
+
94
+ 只验收当前工商全景:
95
+
96
+ ```bash
97
+ QCC_PHASE2_ACCEPTANCE=1 \
98
+ QCC_PHASE2_EVIDENCE="$PWD/.phase2-e2e/evidence.json" \
99
+ QCC_PHASE2_REPORT="$PWD/.phase2-e2e/report.json" \
100
+ npm run e2e:phase2
101
+ ```
102
+
103
+ 同时强制验收企业认证历史域:
104
+
105
+ ```bash
106
+ QCC_PHASE2_ACCEPTANCE=1 \
107
+ QCC_PHASE2_REQUIRE_HISTORY=YES \
108
+ QCC_PHASE2_EVIDENCE="$PWD/.phase2-e2e/evidence.json" \
109
+ QCC_PHASE2_REPORT="$PWD/.phase2-e2e/report-history.json" \
110
+ npm run e2e:phase2
111
+ ```
112
+
113
+ 退出码:
114
+
115
+ - `0`:全部验收门通过;
116
+ - `1`:证据可读,但记录数、维度、消歧、来源或原值一致性未达标;
117
+ - `2`:Runner 未显式启用、文件缺失或 JSON 无法读取。
118
+
119
+ ## 4. 安全要求
120
+
121
+ - 报告文件以 `0600` 权限写入,只包含计数、失败代码和 `row-xxx` 引用。
122
+ - 不将 evidence、session transcript、OAuth 参数、token、企业名单或信用代码提交到 Git。
123
+ - `synthetic:true` 的证据会被 Runner 显式拒绝,不能用单元测试夹具代替真实 E2E。
124
+ - 若需付费调用,必须先在隔离 DSH Profile 内由用户明确确认额度和企业名单。
125
+
126
+ ## 5. 2026-09-01 真实验收记录
127
+
128
+ - 环境:隔离 DSH `0.1.1-rc.2`,未触碰生产 `43120`。
129
+ - 授权:OAuth PKCE 成功,6 个 Server 挂载,授权跨多次 Host 重启恢复成功。
130
+ - 样本:20 家公开知名企业;顺序执行 400 次工具调用。
131
+ - 结果:20/20 主体解析;每企业当前工商最低 15 维、历史工商 4 维;严格历史门通过。
132
+ - `verifyIdentity` 因输入只含企业名而统一不交付,符合 Skill 的按需调用规则,15 维门不受影响。
133
+ - 原始证据与报告位于 Git 忽略的 `.phase2-e2e/` 且为 `0600`;不进入 npm 包、Git、Issue 或日志。
@@ -208,8 +208,11 @@ content:
208
208
  ## 9. 与方案 B 的边界与预留
209
209
 
210
210
  - 方案 A 不改 `lib/`(最多可选加 `data_rows_to_csv` 工具)。
211
- - 方案 B 新增 `lib/qcc.js`(host 半区程序化调用 mcp-client),依赖 Spike #7 验证
212
- `ctx.loader` 条目能否被插件代码直接 `tools/call`。
211
+ - 方案 B 已新增 `lib/qcc.js`:Spike #7 双基线证明公共 `ctx.tools.execute()` 可程序化调度动态
212
+ MCP 工具;G5-2 幂等、候选续跑、人工重试与安全 Runner 的 Mock/Contract 已通过。
213
+ 禁止访问 mcp-client 私有 client;真实 OAuth/QCC 主路径已验收,token 到期刷新与
214
+ 2026-09-02 已完成自然过期 token refresh 与 401/429/配额故障注入验收;
215
+ 故障注入使用本地 ToolRuntime,不重复真实付费批次。
213
216
  - 两者**共享**:§5 字段契约、§6 消歧策略、§7 未连接引导。方案 B 落地时直接复用,
214
217
  不重定义契约。
215
218