@twinsearth/dsh-master 3.3.1 → 3.3.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/README.md CHANGED
@@ -92,6 +92,7 @@ dsh plugin add <package> # 加载 dist-host/dist-worker
92
92
 
93
93
  ## 状态
94
94
 
95
+ - **3.3.2 发布线**:与真实瓮城(dsh-Fang 3.1.35)完成回流实测——探针 `--peer fang` 用瓮城发布物当对端(取其 `VENDOR.json` 校验、`peerLabel` 取其 orchestrator),四项 M5 预期全部成立;同批修掉 contract 1.1.0 的 `rejected` 负数计数并同步 1.1.1。详见 [M5 回流实测](docs/EXPERIMENT-M5-fang-relay.md)。
95
96
  - **3.3.1 发布线**:M5 收口 —— 瓮城回流接口升级到 `@twinsearth/dsh-fang-contract` **1.1.0**(`fang.relay.publish` / `fang.relay.status`),房主侧 `src/cost/fang-relay.ts` 做真实探测与投递(只有对端回执才算送达,不可用给具名原因);里程碑 **M1–M5 全部 done**。详见 [M5 回流实测](docs/EXPERIMENT-M5-fang-relay.md)。
96
97
  - **3.3.0 发布线**:成本优化系统层(ledger / budget / policy / audit)+ Worker 四个成本切点 + A1 紧凑化(默认 shadow)+ A2–A3 / B1–B4;真实 A/B 实测 tokens/task 6263 → 3906.75(−37.6%),成功率 0pp 差,详见 [3.3.0 架构设计](docs/DESIGN-v3.3.0-cost-optimization.md)。
97
98
  - 3.2.10 发布线:角色矩阵放宽——Router 进入母体(essence/mother),Worker 与 Worker Agent Loop 进入原身(fork/essence);母体仍不接纳执行体,fork 仍不注册 Router。
package/client.js CHANGED
@@ -275,7 +275,7 @@ window.__ModuleLoader__.load({
275
275
  return h('div', { style: S.card, 'data-dsh-master': 'cost-block' },
276
276
  h('div', { style: S.row },
277
277
  h('span', { style: S.name }, '成本(只读)'),
278
- tag(block ? '3.3.1' : '未启用', block ? undefined : '#B45309')),
278
+ tag(block ? '3.3.2' : '未启用', block ? undefined : '#B45309')),
279
279
  failed ? h('div', { style: S.error }, '成本接口不可达') : null,
280
280
  !block && !failed ? h('div', { style: S.desc }, '成本系统未装配(cost-optimizer 未启用或角色不匹配)') : null,
281
281
  columns ? h('div', { style: S.desc },
@@ -9,7 +9,7 @@
9
9
  * * 删除/改义必须升 MAJOR,并保留至少一个 MINOR 周期的双写(method@1 与 method@2)。
10
10
  */
11
11
  export const CONTRACT_PACKAGE = '@twinsearth/dsh-fang-contract';
12
- export const CONTRACT_VERSION = '1.1.0';
12
+ export const CONTRACT_VERSION = '1.1.1';
13
13
  export const PROTOCOL = 'fang-master';
14
14
  export const PROTOCOL_VERSION = 1;
15
15
  export const MIN_COMPATIBLE_MAJOR = 1;
@@ -135,10 +135,12 @@ export function createRelayStore(options = {}) {
135
135
  }
136
136
  counters.batches += 1;
137
137
  const acceptedIds = [];
138
- let duplicates = 0;
138
+ let alreadySeen = 0;
139
+ let inBatchDuplicates = 0;
140
+ let rejectedCount = 0;
139
141
  for (const event of validated.accepted) {
140
142
  if (seen.has(event.id)) {
141
- duplicates += 1;
143
+ alreadySeen += 1;
142
144
  continue;
143
145
  }
144
146
  seen.set(event.id, { at: nowMs(now), cursor: cursor + 1 });
@@ -148,18 +150,21 @@ export function createRelayStore(options = {}) {
148
150
  remember({ id: event.id, type: event.type, at: event.at, cursor });
149
151
  lastAt = nowMs(now);
150
152
  }
151
- counters.duplicates += duplicates;
152
153
  for (const item of validated.rejected) {
153
154
  // 批内重复计入 duplicates;其余非法条目计入 rejected 并留原因(有界)。
154
155
  if (item.reason === 'duplicate-in-batch') {
155
- duplicates += 1;
156
- counters.duplicates += 1;
156
+ inBatchDuplicates += 1;
157
157
  continue;
158
158
  }
159
- counters.rejected += 1;
159
+ rejectedCount += 1;
160
160
  if (lastErrors.length < 50)
161
161
  lastErrors.push({ id: item.id, reason: item.reason });
162
162
  }
163
+ // 两个计数分开累计:duplicates = 已存在 + 批内重复;rejected 只数真正非法的条目。
164
+ // (1.1.0 曾用 `validated.rejected.length - duplicates` 计算 rejected,重投时会得到负数。)
165
+ const duplicates = alreadySeen + inBatchDuplicates;
166
+ counters.duplicates += duplicates;
167
+ counters.rejected += rejectedCount;
163
168
  const batchId = String(params?.batchId ?? `batch-${counters.batches}`);
164
169
  const receipt = Object.freeze({
165
170
  ok: true,
@@ -167,7 +172,7 @@ export function createRelayStore(options = {}) {
167
172
  taskId: params?.taskId === undefined ? null : String(params.taskId),
168
173
  accepted: acceptedIds.length,
169
174
  duplicates,
170
- rejected: validated.rejected.length - duplicates,
175
+ rejected: rejectedCount,
171
176
  cursor,
172
177
  acceptedIds,
173
178
  peer: peerLabel,
@@ -7,7 +7,7 @@
7
7
  * 与瓮城 dsh-Fang 的协作只走 DSH 服务键,不 import fang 代码。
8
8
  */
9
9
  import crypto from 'node:crypto';
10
- export const MASTER_VERSION = '3.3.1';
10
+ export const MASTER_VERSION = '3.3.2';
11
11
  export const FANG_CONTRACT_MIN = '3.1.14';
12
12
  export const SERVICE = Object.freeze({
13
13
  master: 'dshMaster',
@@ -108,7 +108,7 @@ export function apply(ctx, config = {}) {
108
108
  };
109
109
  const service = {
110
110
  id: SERVICE.cost,
111
- version: '3.3.1',
111
+ version: '3.3.2',
112
112
  role,
113
113
  get mode() {
114
114
  return system.policy.describe().mode;
@@ -354,7 +354,7 @@ export function apply(ctx, config = {}) {
354
354
  return {
355
355
  ok: true,
356
356
  id: 'cost-optimizer',
357
- version: '3.3.1',
357
+ version: '3.3.2',
358
358
  role,
359
359
  mode: current.mode,
360
360
  requestedMode: current.requestedMode,
@@ -396,7 +396,7 @@ export function apply(ctx, config = {}) {
396
396
  doubleCountGuard: report.doubleCountGuard,
397
397
  };
398
398
  })(),
399
- note: '3.3.1:系统层记账/预算/灰度/审计 + A1 工具输出紧凑化(默认 shadow)+ 瓮城回流出口(contract 1.1 真实投递);工具面 A2–A3 与决策复用 B1–B4 已接入(shadow 默认)。',
399
+ note: '3.3.2:系统层记账/预算/灰度/审计 + A1 工具输出紧凑化(默认 shadow)+ 瓮城回流出口(contract 1.1.1 真实投递,回执计数修复);工具面 A2–A3 与决策复用 B1–B4 已接入(shadow 默认)。',
400
400
  };
401
401
  },
402
402
  };
@@ -132,7 +132,7 @@ master-cost-optimizer(行,仅 fork)= 编排器 + 服务 dshMasterCost
132
132
  Agent Loop **不认识具体模块**;模块异常 fail-open 并留痕,因此新增模块无需改 `agent-loop.ts`。
133
133
  - 四切点:`worker/cost-hooks.ts`,事件经 `sink.cost` 到宿主(HTTP sink 的 `cost` 事件 / Cordis `superagent:cost`)。
134
134
  - A1 默认 **shadow**:计算「本可节省」但返回原文;只有 `enforce` 且紧凑结果更短才替换,且归因单独入账(不重复计消耗)。
135
- - 瓮城回流(3.3.1):`src/cost/fang-relay.ts` 走 `@twinsearth/dsh-fang-contract` 1.1 的
135
+ - 瓮城回流(3.3.1 起):`src/cost/fang-relay.ts` 走 `@twinsearth/dsh-fang-contract` 1.1.x 的
136
136
  `fang.relay.publish` / `fang.relay.status`;只有对端回执才算送达,不可用给具名原因。
137
137
  - 详见 `docs/DESIGN-v3.3.0-cost-optimization.md`(§0 总体架构、§17 实施进度)、
138
138
  `docs/EXPERIMENT-M5-real-ab.md`(真实 A/B)、`docs/EXPERIMENT-M5-fang-relay.md`(回流实测)。
package/docs/CHANGELOG.md CHANGED
@@ -1,3 +1,18 @@
1
+ ## 3.3.2(2026-11-13 已发布)
2
+
3
+ > 三方发布:annotated tag `v3.3.2` + GitHub Release + npm `@twinsearth/dsh-master@3.3.2`。
4
+ > 同批发布接口包:`@twinsearth/dsh-fang-contract@1.1.1`(修回执计数缺陷)。
5
+
6
+ **主题:与真实瓮城(dsh-Fang 3.1.35)完成回流实测,并修掉回执计数缺陷。**
7
+
8
+ - **瓮城侧状态(只读核对)**:dsh-Fang **3.1.35** 已内嵌 contract **1.1.0**,`src/contract/` 8 个文件与发布包逐字节相同,并附 `VENDOR.json`(`version` / `fileCount` / 逐文件 sha256);`src/plugins/orchestrator.mjs` 声明 `peerLabel: 'dsh-fang-bundle'` 且注册 `...relayHandlers(relayStore)`。
9
+ - **探针新增 `--peer fang` 模式**:把**瓮城自己的发布物**当对端 —— 用它 vendored 的契约服务端,`peerLabel` 从它自己的 orchestrator 源码读取(不硬编码),并用它的 `VENDOR.json` 逐文件校验 sha256(8/8 通过)。实测:`peer=dsh-fang-bundle`、`events=6`、`sameProcess=false`、`failed=0`、`transport=http`、`contractVersion=1.1.0`;幂等重投 `accepted:0 / duplicates:3 / cursor` 不变。四项 M5 预期全部成立。
10
+ - **实测发现的缺陷(contract 1.1.0)**:relay 回执把 `rejected` 算成 `validated.rejected.length - duplicates`,重投时会得到**负数**(实测 `-3`)。影响仅限回执字段(事件去重、`accepted`、`cursor` 一直正确)。contract **1.1.1** 已修正为「`duplicates`(已存在 + 批内)与 `rejected`(仅非法条目)分开累计」,并补回归测试(契约侧 16/16)。
11
+ - **房主内嵌副本同步 1.1.1**:`src/contract/` 与已发布包逐字节相同(conformance 测试守卫);新增「重投回执非负且 cursor 不倒退」回归测试。
12
+ - **里程碑**:M5 证据由 `contract-server` 升级为 `dsh-fang-bundle · 6 条`,结果行 `notes` 里的残余缺口(“待 bundle 采用 1.1”)自动清空;**M1–M5 全部 done**。
13
+ - **仍未做**:宿主级 co-load(守则 §二 禁止把瓮城装进任何 profile);瓮城 UI/信息栏是否消费回流事件属瓮城产品面。
14
+ - 测试:全量 **449 条**(`npm test` 447 通过 + 2 条 Windows 并行跳过;`npm run test:clean` 449/449)。
15
+
1
16
  ## 3.3.1(2026-11-13 已发布)
2
17
 
3
18
  > 三方发布:annotated tag `v3.3.1` + GitHub Release + npm `@twinsearth/dsh-master@3.3.1`。
@@ -1054,5 +1054,6 @@ node scripts/milestones.mjs --input evidence.json # 追加 M3 守卫准确率 /
1054
1054
  | **M2 收口** 系统层 attribution / bus + 模块 SPI | ✅ 已实施(3.3.0-dev,未发布) | `src/cost/attribution.ts`(按模块/任务聚合,enforce 才入账)、`src/cost/bus.ts`(事件总线 + 回流出口,订阅者异常隔离)、`worker/cost-spi.ts`(ToolModule / DecisionModule 契约)、`worker/cost-modules.ts`(注册表 + A1 适配,异常 fail-open)、`test/worker-cost-spi.test.ts`;Agent Loop 已改为只依赖注册表 |
1055
1055
  | **M2 余项 / M3–M4** | ✅ 已发布(3.3.0) | A2 渐进纰漏、A3 批量、B1–B4、真实 fork 对照实验、面板成本块(见 §14) |
1056
1056
  | **M5 回流收口** | ✅ 已发布(3.3.1) | contract 1.1.0(`fang.relay.publish`/`status`)+ `src/cost/fang-relay.ts` + `scripts/fang-relay-probe.mjs`;M5 由 `fang-relay-out-of-scope` 改为对端回执判定 |
1057
+ | **M5 真实瓮城实测** | ✅ 已发布(3.3.2) | 瓮城 3.1.35 内嵌 1.1.0 并自报 `dsh-fang-bundle`;探针 `--peer fang` 用瓮城发布物当对端(VENDOR.json 校验 8/8、peerLabel 取其 orchestrator);实测 6 条回执、幂等重投通过;同批修 contract 1.1.1 回执计数缺陷并同步 |
1057
1058
 
1058
- *3.3.0 / 3.3.1 均已三方发布(annotated tag + GitHub Release + npm)。*
1059
+ *3.3.0 / 3.3.1 / 3.3.2 均已三方发布(annotated tag + GitHub Release + npm);contract 1.0.0 / 1.1.0 / 1.1.1 已发 npm。*
@@ -103,15 +103,78 @@ M5 │ 实现 ✔ │ 依赖 ✔ │ 效果 verified │ 状态 done │ 回流
103
103
 
104
104
  **不能证明**
105
105
 
106
- - 不能说“已与真实 dsh-Fang bundle 完成对接”:bundle 尚未依赖 contract 1.1,也尚未挂
107
- `relayHandlers`。等 bundle 采用 1.1 后,`peer` 自报值会变成 `dsh-fang-bundle`,
108
- M5 的 `notes` 会自动消失(代码里写死的是白名单,不需要改里程碑表);
106
+ - 不能说“两个插件在同一个 DSH 宿主里同时加载跑通了”:按本会话守则 §二,**不得把瓮城安装/升级到任何 profile**,
107
+ 所以本轮没有做宿主级 co-load;做的是「瓮城**已发布包**的 vendored 契约服务端 × 房主内嵌副本客户端」的跨进程实测;
109
108
  - 不能证明生产环境的网络/鉴权拓扑:本轮是 127.0.0.1 + Bearer token 的本机双进程。
110
109
 
111
110
  ## 6. 复现注意事项
112
111
 
113
112
  - 必须 `npm run build` 后跑:脚本 import 的是 `dist-host/`;
114
- - 子进程用的是 `node_modules` 里**已发布**的包,父进程用 `src/contract/`,两侧故意不同源,
115
- 才能真正测出“已发布包 ↔ 内嵌副本”的互操作;
113
+ - 两种对端:默认 `--peer contract` 用已发布的契约包(自报 `contract-server`,仅协议自测);
114
+ `--peer fang --fang-dir <解包目录>` 用**瓮城自己的包**当对端(自报 `dsh-fang-bundle`);
116
115
  - 证据文件不要提交进仓库(G6 零残留门禁会检查 `rsi-report.json` 一类产物);
117
116
  - Windows 上子进程退出由 `SIGTERM` 处理,若端口被占用会直接报 ready 超时。
117
+
118
+ ## 7. 瓮城 3.1.35 复核(2026-11-13 晚)
119
+
120
+ 瓮城已发 **3.1.35**(commit `feat(v3.1.35): 适配 contract 1.1.0 回流接口 (#100)`),房主侧按下面的方式复核:
121
+
122
+ ```bash
123
+ npm run build
124
+ node scripts/fang-relay-probe.mjs --peer fang --fang-dir <解包后的 @twinsearth/dsh-fang> --events 6
125
+ ```
126
+
127
+ **静态核对(对端是瓮城发布物本身)**
128
+
129
+ | 项 | 结果 |
130
+ |---|---|
131
+ | 瓮城 `src/contract/` 8 个文件 vs contract 1.1.0 | **8/8 逐字节相同** |
132
+ | 瓮城 `src/contract/VENDOR.json` | `package=@twinsearth/dsh-fang-contract, version=1.1.0, fileCount=8`,逐文件 sha256 与实物一致 |
133
+ | `src/plugins/orchestrator.mjs` | `createRelayStore({ peerLabel: 'dsh-fang-bundle' })` + handlers 里 `...relayHandlers(relayStore)` |
134
+
135
+ 探针不硬编码 `peerLabel`:它从**瓮城自己的 orchestrator 源码**里读出该值,并用瓮城的 `VENDOR.json` 校验文件哈希,
136
+ 所以 `peer` 是瓮城自报的,不是我们假设的。
137
+
138
+ **实测结果(`--peer fang`)**
139
+
140
+ ```json
141
+ {
142
+ "ok": true,
143
+ "peerMode": "fang",
144
+ "expectation": { "peer": "dsh-fang-bundle", "matched": true },
145
+ "fangRelay": { "available": true, "peer": "dsh-fang-bundle", "events": 6,
146
+ "sameProcess": false, "transport": "http", "contractVersion": "1.1.0",
147
+ "cursor": 6, "failed": 0, "dropped": 0 },
148
+ "provenance": { "fangVersion": "3.1.35", "vendorVersion": "1.1.0",
149
+ "vendoredFilesChecked": 8, "vendoredFilesOk": true,
150
+ "peerLabelSource": "src/plugins/orchestrator.mjs",
151
+ "relayHandlersRegistered": true },
152
+ "detail": {
153
+ "probePid": 10632, "peerPid": 12972, "sameProcess": false,
154
+ "idempotency": { "ok": true, "events": 3,
155
+ "first": { "accepted": 3, "duplicates": 0, "cursor": 9 },
156
+ "second": { "accepted": 0, "duplicates": 3, "cursor": 9 } }
157
+ }
158
+ }
159
+ ```
160
+
161
+ 四项预期全部成立:`peer === 'dsh-fang-bundle'`、`events ≥ 3`、`sameProcess === false`、`failed === 0`;
162
+ 额外核对幂等重投:同批事件第二次 `accepted:0 / duplicates:3 / cursor` 不倒退。
163
+
164
+ **这次实测发现 contract 1.1.0 的一个回执缺陷(已在 1.1.1 修复并发布)**
165
+
166
+ `createRelayStore().publish()` 用 `validated.rejected.length - duplicates` 计算 `rejected`,
167
+ 而 `duplicates` 同时包含「已存在重复」和「批内重复」,重投时会得到**负数**(实测 `-3`)。
168
+ 影响面:只影响回执里的 `rejected` 字段(事件去重、`cursor`、`accepted` 都正确),房主侧不读该字段;
169
+ 但负数回执是错的数据。已加回归测试并修正为「`rejected` 只数真正非法条目,`duplicates` 单独累计」,
170
+ 契约仓库测试 16/16 通过;**contract 1.1.1 已发布**(npm `latest=1.1.1`,字节核验通过),
171
+ 房主 **3.3.2** 已同步 1.1.1 并重新三方发布。
172
+
173
+ > 注意:瓮城 3.1.35 内嵌的仍是 1.1.0(含该回执缺陷)。这是 PATCH 级差异、只影响 `rejected` 显示,
174
+ > 不影响去重与投递;瓮城下次 vendor 同步时带上 1.1.1 即可,无需协议改动。
175
+
176
+ **仍未证明**
177
+
178
+ - 宿主级 co-load(守则 §二禁止往 profile 装瓮城);
179
+ - 瓮城侧 UI/信息栏是否消费了回流事件(属瓮城产品面,房主只需协议可用)。
180
+
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@twinsearth/dsh-master",
3
- "version": "3.3.1",
3
+ "version": "3.3.2",
4
4
  "private": false,
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -75,7 +75,7 @@
75
75
  "build": "npm run build:host && npm run build:worker"
76
76
  },
77
77
  "devDependencies": {
78
- "@twinsearth/dsh-fang-contract": "^1.1.0",
78
+ "@twinsearth/dsh-fang-contract": "^1.1.1",
79
79
  "@types/node": "^24.0.0",
80
80
  "typescript": "^5.9.0"
81
81
  },
@@ -11,7 +11,7 @@
11
11
  */
12
12
 
13
13
  export const CONTRACT_PACKAGE = '@twinsearth/dsh-fang-contract'
14
- export const CONTRACT_VERSION = '1.1.0'
14
+ export const CONTRACT_VERSION = '1.1.1'
15
15
  export const PROTOCOL = 'fang-master'
16
16
  export const PROTOCOL_VERSION = 1
17
17
  export const MIN_COMPATIBLE_MAJOR = 1
@@ -123,9 +123,11 @@ export function createRelayStore(options = {}) {
123
123
  }
124
124
  counters.batches += 1
125
125
  const acceptedIds = []
126
- let duplicates = 0
126
+ let alreadySeen = 0
127
+ let inBatchDuplicates = 0
128
+ let rejectedCount = 0
127
129
  for (const event of validated.accepted) {
128
- if (seen.has(event.id)) { duplicates += 1; continue }
130
+ if (seen.has(event.id)) { alreadySeen += 1; continue }
129
131
  seen.set(event.id, { at: nowMs(now), cursor: cursor + 1 })
130
132
  cursor += 1
131
133
  counters.accepted += 1
@@ -133,13 +135,17 @@ export function createRelayStore(options = {}) {
133
135
  remember({ id: event.id, type: event.type, at: event.at, cursor })
134
136
  lastAt = nowMs(now)
135
137
  }
136
- counters.duplicates += duplicates
137
138
  for (const item of validated.rejected) {
138
139
  // 批内重复计入 duplicates;其余非法条目计入 rejected 并留原因(有界)。
139
- if (item.reason === 'duplicate-in-batch') { duplicates += 1; counters.duplicates += 1; continue }
140
- counters.rejected += 1
140
+ if (item.reason === 'duplicate-in-batch') { inBatchDuplicates += 1; continue }
141
+ rejectedCount += 1
141
142
  if (lastErrors.length < 50) lastErrors.push({ id: item.id, reason: item.reason })
142
143
  }
144
+ // 两个计数分开累计:duplicates = 已存在 + 批内重复;rejected 只数真正非法的条目。
145
+ // (1.1.0 曾用 `validated.rejected.length - duplicates` 计算 rejected,重投时会得到负数。)
146
+ const duplicates = alreadySeen + inBatchDuplicates
147
+ counters.duplicates += duplicates
148
+ counters.rejected += rejectedCount
143
149
  const batchId = String(params?.batchId ?? `batch-${counters.batches}`)
144
150
  const receipt = Object.freeze({
145
151
  ok: true,
@@ -147,7 +153,7 @@ export function createRelayStore(options = {}) {
147
153
  taskId: params?.taskId === undefined ? null : String(params.taskId),
148
154
  accepted: acceptedIds.length,
149
155
  duplicates,
150
- rejected: validated.rejected.length - duplicates,
156
+ rejected: rejectedCount,
151
157
  cursor,
152
158
  acceptedIds,
153
159
  peer: peerLabel,
package/src/kernel.ts CHANGED
@@ -10,7 +10,7 @@
10
10
 
11
11
  import crypto from 'node:crypto'
12
12
 
13
- export const MASTER_VERSION = '3.3.1'
13
+ export const MASTER_VERSION = '3.3.2'
14
14
  export const FANG_CONTRACT_MIN = '3.1.14'
15
15
 
16
16
  export const SERVICE = Object.freeze({
@@ -117,7 +117,7 @@ export function apply(ctx, config = {}) {
117
117
 
118
118
  const service = {
119
119
  id: SERVICE.cost,
120
- version: '3.3.1',
120
+ version: '3.3.2',
121
121
  role,
122
122
  get mode() {
123
123
  return system.policy.describe().mode
@@ -354,7 +354,7 @@ export function apply(ctx, config = {}) {
354
354
  return {
355
355
  ok: true,
356
356
  id: 'cost-optimizer',
357
- version: '3.3.1',
357
+ version: '3.3.2',
358
358
  role,
359
359
  mode: current.mode,
360
360
  requestedMode: current.requestedMode,
@@ -396,7 +396,7 @@ export function apply(ctx, config = {}) {
396
396
  doubleCountGuard: report.doubleCountGuard,
397
397
  }
398
398
  })(),
399
- note: '3.3.1:系统层记账/预算/灰度/审计 + A1 工具输出紧凑化(默认 shadow)+ 瓮城回流出口(contract 1.1 真实投递);工具面 A2–A3 与决策复用 B1–B4 已接入(shadow 默认)。',
399
+ note: '3.3.2:系统层记账/预算/灰度/审计 + A1 工具输出紧凑化(默认 shadow)+ 瓮城回流出口(contract 1.1.1 真实投递,回执计数修复);工具面 A2–A3 与决策复用 B1–B4 已接入(shadow 默认)。',
400
400
  }
401
401
  },
402
402
  }