@sema-agent/settings-schema 7.0.0 → 8.0.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.
Files changed (106) hide show
  1. package/CHANGELOG.md +173 -1
  2. package/README.md +47 -5
  3. package/conformance/VERSION +1 -1
  4. package/conformance/cases/c01-entry-salvage-then-typo/expected.json +5 -0
  5. package/conformance/cases/c02-hosts-clamp-stacked/expected.json +5 -0
  6. package/conformance/cases/c03-literal-dotted-top-key/expected.json +41 -0
  7. package/conformance/cases/c04-all-bad-collection-not-salvaged/expected.json +5 -0
  8. package/conformance/cases/c05-gate-domain-bad-entry-throws/expected.json +5 -0
  9. package/conformance/cases/c06-legacy-governance-lift/expected.json +12 -0
  10. package/conformance/cases/c07-hooks-is-not-a-config-domain/expected.json +5 -0
  11. package/conformance/cases/c08-limits-unknown-carried-64/expected.json +77 -0
  12. package/conformance/cases/c09-limits-unknown-carried-65/expected.json +78 -0
  13. package/conformance/cases/c10-record-key-secret-redacted-twice/expected.json +5 -0
  14. package/conformance/cases/c11-unicode-unknown-keys/expected.json +14 -0
  15. package/conformance/cases/c12-prototype-name-top-keys/expected.json +5 -0
  16. package/conformance/cases/c13-models-field-null/expected.json +5 -0
  17. package/conformance/cases/c14-models-field-empty-array/expected.json +16 -0
  18. package/conformance/cases/c15-models-field-number/expected.json +5 -0
  19. package/conformance/cases/c16-deep-nesting-205/expected.json +14 -0
  20. package/conformance/cases/c17-empty-domain-file/expected.json +10 -0
  21. package/conformance/cases/c18-wire-envelope-not-an-object/expected.json +10 -0
  22. package/conformance/cases/c19-wire-envelope-no-version/expected.json +10 -0
  23. package/conformance/cases/c20-governance-absent-lifts/expected.json +12 -0
  24. package/conformance/cases/c21-governance-empty-object-wins/expected.json +13 -0
  25. package/conformance/cases/c22-governance-null-throws/expected.json +5 -0
  26. package/conformance/cases/c23-mixed-validity-models/expected.json +5 -0
  27. package/conformance/cases/c24-valid-entries-with-unknown-keys/expected.json +29 -0
  28. package/conformance/cases/c25-published-roundtrip-zero-warnings/expected.json +33 -0
  29. package/conformance/cases/c26-hosts-array-1024/expected.json +7183 -0
  30. package/conformance/cases/c27-hosts-array-1025/expected.json +5 -0
  31. package/conformance/cases/c28-large-document-over-1mb/expected.json +14 -0
  32. package/conformance/cases/c29-salvage-then-domain-refine-fails/expected.json +5 -0
  33. package/conformance/cases/c30-version-triple-a-first/expected.json +29 -0
  34. package/conformance/cases/c31-version-triple-b-changed/expected.json +29 -0
  35. package/conformance/cases/c32-version-triple-a-restored/expected.json +29 -0
  36. package/conformance/cases/c33-limits-nested-carried-key/expected.json +17 -0
  37. package/conformance/cases/c34-skills-content-vs-manifest/expected.json +23 -0
  38. package/conformance/cases/c35-portable-domain-file-is-null/expected.json +5 -0
  39. package/conformance/cases/c36-skills-entry-without-content/expected.json +23 -0
  40. package/conformance/cases/c37-skills-field-null/expected.json +5 -0
  41. package/conformance/cases/c38-remote-exec-is-a-non-domain-file/expected.json +5 -0
  42. package/conformance/cases/c39-hidden-file-in-config-d/expected.json +5 -0
  43. package/conformance/cases/c40-s712-identity-only-what-reads-out/expected.json +5 -0
  44. package/conformance/cases/c41-s792-encoded-pem-identity/expected.json +5 -0
  45. package/conformance/cases/c42-s792-unknown-key-secret-redacted/expected.json +33 -0
  46. package/conformance/cases/c43-s792-form-and-deep-encoded-pem/expected.json +5 -0
  47. package/conformance/cases/c44-s792-utf8-escape-glued-token/expected.json +5 -0
  48. package/conformance/cases/c45-s792-identity-bounds-and-decode-depth/expected.json +5 -0
  49. package/conformance/cases/c46-s811-host-name-url-userinfo-characterization/expected.json +5 -0
  50. package/conformance/cases/c47-s811-roles-key-url-userinfo-characterization/expected.json +5 -0
  51. package/conformance/cases/c48-s935-model-headers-transported/expected.json +59 -0
  52. package/conformance/cases/c49-s935-model-headers-secret-entry-dropped/expected.json +5 -0
  53. package/conformance/cases/c50-s784-lane-host-legal/expected.json +44 -0
  54. package/conformance/cases/c51-s784-lane-e2b-legal/expected.json +46 -0
  55. package/conformance/cases/c52-s784-lane-k8s-legal/expected.json +48 -0
  56. package/conformance/cases/c53-s784-lane-ssh-legal/expected.json +48 -0
  57. package/conformance/cases/c54-s784-lane-adb-legal/expected.json +44 -0
  58. package/conformance/cases/c55-s784-lane-local-docker-legal/expected.json +45 -0
  59. package/conformance/cases/c56-s784-lane-device-legal/expected.json +51 -0
  60. package/conformance/cases/c57-s784-g1-enabled-no-lane/expected.json +5 -0
  61. package/conformance/cases/c58-s784-g2-multitenant-host/expected.json +5 -0
  62. package/conformance/cases/c59-s784-g3-k8s-missing-image/expected.json +5 -0
  63. package/conformance/cases/c60-s784-g3-e2b-missing-key/expected.json +5 -0
  64. package/conformance/cases/c61-s784-g3-other-lane-key/expected.json +5 -0
  65. package/conformance/cases/c62-s784-g7a-session-tidb/expected.json +5 -0
  66. package/conformance/cases/c63-s784-host-two-replicas/expected.json +5 -0
  67. package/conformance/cases/c64-s784-multitenant-local-store/expected.json +5 -0
  68. package/conformance/cases/c65-s784-pg-without-db/expected.json +5 -0
  69. package/conformance/cases/c66-s784-memory-backend-mismatch/expected.json +5 -0
  70. package/conformance/cases/c67-s784-retired-field-loud/expected.json +5 -0
  71. package/conformance/cases/c67-s784-retired-field-loud/meta.json +1 -1
  72. package/conformance/cases/c68-s784-device-local-store/expected.json +5 -0
  73. package/conformance/cases/cc01-mcp-toolfaces-non-object/expected.json +54 -0
  74. package/conformance/cases/cc02-mcp-source-key-is-transported/expected.json +27 -0
  75. package/conformance/cases/cc03-model-compat-unknown-subkey/expected.json +33 -0
  76. package/conformance/cases/cli01-model-name-nfc-vs-nfd/expected.json +5 -0
  77. package/conformance/cases/cli02-control-and-zero-width-chars/expected.json +5 -0
  78. package/conformance/cases/cli03-credential-pasted-into-unknown-key/expected.json +22 -0
  79. package/conformance/cases/cli04-env-value-with-newline/expected.json +46 -0
  80. package/conformance/cases/cli05-long-and-prefix-colliding-names/expected.json +53 -0
  81. package/conformance/cases/t01-absent-domain-key-not-null/expected.json +5 -0
  82. package/conformance/cases/t02-entry-salvage-vs-whole-domain-refusal/expected.json +5 -0
  83. package/conformance/cases/t03-prototype-name-key-not-swallowed/expected.json +29 -0
  84. package/conformance/cases/t04-secret-literal-never-in-warning-value/expected.json +5 -0
  85. package/conformance/cases/z01-every-domain-empty-object/expected.json +64 -0
  86. package/conformance/cases/z02-every-domain-unknown-key/expected.json +66 -0
  87. package/dist/cli.js +97 -25
  88. package/dist/config-fns.js +7 -1
  89. package/dist/conformance/contract-adapter.d.ts +4 -2
  90. package/dist/conformance/contract-adapter.js +21 -7
  91. package/dist/conformance/corpus.js +12 -7
  92. package/dist/conformance/derive.d.ts +26 -0
  93. package/dist/conformance/derive.js +25 -2
  94. package/dist/conformance/index.d.ts +4 -3
  95. package/dist/conformance/index.js +4 -3
  96. package/dist/conformance/run.d.ts +3 -3
  97. package/dist/conformance/run.js +87 -17
  98. package/dist/conformance/types.d.ts +57 -17
  99. package/dist/conformance/types.js +4 -2
  100. package/dist/execution-lane-index.d.ts +20 -1
  101. package/dist/execution-lane-index.js +24 -1
  102. package/dist/hooks.d.ts +4 -4
  103. package/dist/migrate-workers.d.ts +1 -1
  104. package/dist/migrate-workers.js +3 -7
  105. package/dist/types.js +13 -4
  106. package/package.json +2 -1
package/CHANGELOG.md CHANGED
@@ -6,7 +6,179 @@
6
6
  > `@sema-agent/registry-core`(更早还曾是 `@sema-ai/registry-core`)。本档 `0.19.0` 以下各节里的
7
7
  > 包名是**历史记录**,如实保留不追改。
8
8
 
9
- ## Unreleased
9
+ ## 8.0.0 — 2026-10-04 — 一致性门第四条腿 `strictWrite`(严写受理)+ 每条腿都要表态(BREAKING,kit 类型面与 release 律)· `migrate-workers --check` 与真跑过同一只判定(BREAKING,CLI 退出码 / `refused[]`)· 导出面登记表机器门
10
+
11
+ > **判档 = major**(README「发版纪律」):kit 的闭集 `LegName` 加一员、`ConformanceAdapter.legs` 从 `Partial<Record<…>>` 改成全量
12
+ > `Record<LegName, LegStance>`(实现了 kit 适配器的消费方 tsc 红,即通知);release 律收紧(没表态的腿从「跳过、不算违例」变成违例);
13
+ > CLI 机读契约字段 `outcome` / `refused` 在一种文档上改了值(`--check` 对整域会被拒的文档从 `pending` / 退出 3 变成 `refused` / 退出 1,
14
+ > `refused[]` 从空变成逐条)。契约 schema、域默认值、三条读腿的读数**一字未动**:conformance 全语料重加冕,`domains` / `wire` / `localRoot`
15
+ > 三腿金样、`baseline.json`、迁移金样 `m01–m04` 零 diff(`expected.json` 只新增 `strictWrite` 一格,82 个文件 0 行删改)。
16
+
17
+ ### 一、一致性门第四条腿 `strictWrite`(严写受理)
18
+
19
+ **病**:写入席(管理面)判一份文档收不收,读侧(worker / CLI)逐条容错 —— 两侧此前没有任何一格放在同一份文档上对过。kit 的三条腿都是读语义:
20
+ 读腿抢救的地方(丢坏条目、整域降级、主机名夹紧)写入席**应该拒**,而这一点没有金样、没有门。
21
+
22
+ **修**:
23
+
24
+ - `LegName` 闭集 +1 `"strictWrite"`(`LEG_NAMES` 同步)。入参与 `domains` 腿**同一份字段表**(新导出类型 `DomainDocsInput`,两臂共用,
25
+ 不抄第二份)。结局只用既有两类(`LegOutcome` 不动):全部受理 ⇒ `effective` = 提交的每个键 → 受理后的域文档(**只含提交的键、不补缺席域**;
26
+ 空集受理成 `{}`),告警面为空,元数据回报 harness 注入的那一份;任何一份不受理 ⇒ `rejected{ stage: "validate" }`(写是一个动作,不逐份抢救)。
27
+ - 契约仓自己的适配器用 `parseDomain` 接这条腿(`ContractModule` 加 `parseDomain`,`createContractAdapter` 的 `entries` 要第四个入口)。
28
+ - **期望从哪来是一张表**:`derive.ts` 新导出 `LEG_EXPECTATION`(`Record<LegName, { source: "explicit" | "derivable"; why }>`,新开一条腿不表态
29
+ 即编译红)。`strictWrite` 入参恒等、**期望不派生**(严写结局不是读结局的表示变换;从 `domains` 期望派生会让一份金样的错同时改写两条腿)。
30
+ 语料加载器只按这张表判「能不能写 `{derive:true}`」—— 原来对 `domains` 的特判并进同一条规则。
31
+ - **语料**:82 条 case 逐条加 `strictWrite` 显式金样,每条带 `why`(为什么受理 / 为什么拒):40 受理、42 拒(全在 `validate` 层)。
32
+ 拒的 42 条 = 读腿抢救过的 22 条(全在逐条容错档的域上:models / skills / mcp / hosts)+ 读腿整条拒的 15 条 + 带非域名键的 5 条。
33
+ - **金样逐条核**(`test/conformance-strict-write-8.0.0.test.ts`):一条**不调 `parseDomain`** 的律 ——「严写拒 ⇔ 文档集里有非域名键 ∨ 读腿整条拒 ∨
34
+ 读腿在提交的某个域上抢救过(`entry-dropped` / `domain-defaulted` / `hosts-grandfathered`);否则受理,受理形 = 读腿 effective 里那几格」——
35
+ 对着已冻结的 `domains` 金样逐条推导,与入库的严写金样 82/82 一致。两份金样从此互相压着。
36
+ - **负控**(`test/conformance-mutation.test.ts` 第十一靶):把 `parseDomain` 换成读腿的宽松解析 ⇒ 严写腿**恰好**红在那 22 条读腿抢救 case 上
37
+ (别的腿、别的 case 一格不动);把非域名键静默跳过 ⇒ 恰好红在那 5 条上。
38
+ - **`parseDomain` 对非域名响亮拒**:`"x" is not a config domain`(名字出门前过与告警坐标同一道脱敏)。此前 `DOMAIN_SCHEMAS[x]` 是 undefined,
39
+ TypeError 被 getter 守卫吞掉、说成 `invalid "x" config (a value could not be read during validation)` —— 抛还是抛,原因说错了。
40
+ 严写腿把文档集的每个键都交给它,所以这句话现在是用户看得见的拒因。真域名的行为一字不变。
41
+
42
+ ### 二、每条腿都要表态:实现,或显式声明不承担(一条规则,四腿同律)
43
+
44
+ **病**:3.0.0 起「未登记的腿 ⇒ `skipped: no adapter`,不算违例」。闭集加一条腿时,这条律让每个没接新腿的消费方**静默跳过、默认当通过** ——
45
+ 「这个消费方结构上没有这条写法」与「这个消费方忘了接」在报告里长一个样。
46
+
47
+ **修**:
48
+
49
+ - `ConformanceAdapter.legs` 改为全量 `Record<LegName, LegStance>`;`LegStance = ConformanceLeg | LegDeclined`,
50
+ `LegDeclined = { declined: LegDeclineReason; why: string }`。两臂**互斥**(`ConformanceLeg.declined?: never`、`LegDeclined.entry?` / `run?: never`),
51
+ 运行期 release 门同一条律(带 `declined` 又带 `entry` 或 `run` = 形不对)—— 编译过了就证明表态形合法。收窄用 `stance.declined !== undefined`
52
+ (`"run" in stance` 收不窄)。理由词是闭集(`LEG_DECLINE_REASONS`),今天一个词 `"no-production-path"`
53
+ = 你的生产代码里结构上就没有这种读法 / 写法。刻意没有「有路径、还没接」的词:有路径就接,结局上的已知差走既有账本(带台账号、翻转条件、stale 律)。
54
+ - release 律:**每条腿都有表态**(缺表态 / 形不对 / 理由词不在闭集 / `why` 为空 / 既实现又声明 = 违例)· 至少**实现**一条腿 · **实现的腿必须跑**。
55
+ 声明的腿逐格记 `skipped`(detail = `consumer declines this leg (<理由词>): <why>`),报告新增 `adapter.declined` 逐条列出。historical 模式不跑这些律。
56
+ - 新导出(`./conformance`):`LEG_DECLINE_REASONS`、`LEG_EXPECTATION`、类型 `LegStance` / `LegDeclined` / `LegDeclineReason` / `DomainDocsInput`。
57
+
58
+ ### 三、`migrate-workers --check` 与真跑过同一只判定
59
+
60
+ **病**(7.0.0 既有,7.0.1 披露):`--check` 在整域终判**之前**就返回 —— 整域会被拒的文档(例:两只同名 worker)`--check` 报
61
+ `outcome=pending` / 退出 3,真跑报 `outcome=refused` / 退出 1,按 `--check` 做闸的脚本会放行一次必被拒的真跑;整域终判拒时 stdout 的
62
+ `refused[]` 为空,原因只在 stderr。
63
+
64
+ **修**:写之前只有**一只**判定 —— 逐只 refused → 无改动 ⇒ noop → 整域终判 → 有改动;`--check` 不参与判定,只决定「有改动」那一支写不写
65
+ (`pending` / `applied`)。整域终判拒时 `refused[]` 逐条带出被拒项,形与逐只 refused 同一套(`name` / `field` / `reason`;`field` 相对那只 worker;
66
+ 条目本身没有字符串名字时 `name` 为 `null`,不编名字)。stderr 两句文案、退出码的含义表、`outcome` 闭集都不变。`migrateWorkersDomain` 的输出不变。
67
+
68
+ ### 四、导出面登记表(API 面卫生的机器门,零新依赖)
69
+
70
+ - `scripts/api-surface.mjs`(`npm run api:surface [-- --check]`)用 TypeScript 检查器(本来就在 devDependencies)列出 exports 表里 22 条模块子路径的
71
+ 全部导出名(`export *` 展开,`value` / `type` / `value+type` 三形),写成 `docs/API-SURFACE.md`(不随包发)。
72
+ - `test/api-surface.test.ts`:登记表逐名逐形 ≡ 真实导出面;子路径集合与次序 = exports 表;登记表 = 生成器原样输出;判据自证(每条子路径「值」形的名字
73
+ ≡ 运行期模块对象的键)。加 / 删 / 改形一个导出而不重生成登记表 ⇒ 红 —— 导出面的每次变动从此以登记表 diff 出现在评审里。
74
+ - 编译器层的死代码门早已在位:`tsconfig.json` 的 `noUnusedLocals` / `noUnusedParameters`(`npm run typecheck` 覆盖 src + test),本版不重复造。
75
+ - **`./migrate` 本版不删**:五个消费仓 `origin/main` 普查,管理面仍有三处生产 import(hosts grandfather 路由、hosts status 路由、
76
+ workers migrate-model 路由:`grandfatherHosts` / `planWorkerModelMigration` / `applyModelMigrationPlan` / `migrationDiffViolations` /
77
+ `ApplyMigrationDocs` / `LegacyWorkerModel`);server / cli / client-core / sdk 零 import。零外部消费的前提不成立 ⇒ 不删(ROADMAP 条目已按普查改写)。
78
+ `./api/wire` 早在 `3.0.0` 删除;`createSiblingResolver` 的工厂命名在 `2.0.0` 已改(本版核过,无动作)。
79
+
80
+ ### 用户可见变化(谁受影响 / 怎么改 / 不改会怎样)
81
+
82
+ 1. **实现了 kit 适配器的消费方**(server、管理面)—— `legs` 少任何一条腿的表态 ⇒ **tsc 红**(`Partial<Record<LegName, ConformanceLeg>>` 不再可赋给
83
+ `legs`)。改:每条没有的腿写 `{ declined: "no-production-path", why: "<你仓的事实>" }`。不改(用 `as` 绕过类型)⇒ release 门违例
84
+ 「leg `<腿名>` has no stance」,`report.ok = false`。
85
+ 2. **持 `Record<LegName, …>` / 对 `LegName` 穷举 `switch` 的消费方** —— 编译红,要为 `strictWrite` 表态(实现或声明)。
86
+ 3. **读 `report.adapter.legs` 的** —— 语义从「登记的腿」变成「**实现**的腿」(声明的腿在新字段 `adapter.declined` 里);读 `skipped` 格 detail
87
+ 文案做判断的 —— `no adapter: …` 那句不再出现,换成 `consumer declines this leg (…)` 或 `no stance: …`。
88
+ 4. **按 `migrate-workers --check` 做闸的脚本** —— 整域会被拒的文档(同名 / 同路由 / replicas 与 autoscaling 并存)现在 `--check` 也是
89
+ `outcome=refused` / 退出 1(以前 `pending` / 3)。退出码 3 仍只表示「会改、且写得进去」。读 `refused[]` 的 —— 这种文档上它不再是空数组,
90
+ 项里 `name` 可以是 `null`(条目本身没有名字)。
91
+ 5. **捕 `parseDomain` 抛错文案的**(只在传入非域名时)—— 文案从 `invalid "x" config (a value could not be read during validation)` 变成
92
+ `"x" is not a config domain`;抛的时机不变。
93
+ 6. **装了包、只用 schema / 读写 helper 的消费方**:零变化(契约 schema、默认值、读腿读数一字未动)。
94
+
95
+ ### 红先 / 门
96
+
97
+ - A:新腿前置格(`conformance-strict-write-8.0.0`)与表态律格(`conformance-legs-and-ledger` ①)。表态律的改前读数 = 7.0.1 同一构造
98
+ `violations=[]`、`ok=true`、detail `no adapter: this consumer registered no such read leg`(旧格「适配器缺一条腿 ⇒ 不算违例」钉的正是它,
99
+ 本版按意图翻向);`parseDomain("hooks")` 改前抛 `a value could not be read`。
100
+ - C:先只改测试、对 7.0.1 的 `cli.ts` 跑 —— 4 格红:四态表新行 `--check` 整域拒 `expected 3 to be 1`;翻钉格
101
+ `[3, 'outcome=pending written=false']` ≠ `[1, 'outcome=refused written=false']`;`refused[]` `expected 0 to be greater than 0`;`--check` 的 stderr 为空。
102
+ 修后 25/25。原 🚧 特征钉(`--check` 先于整域终判)按它自己的翻转句翻成新读数。
103
+ - B:`test/api-surface.test.ts`(五格)。
104
+ - 全量:见收车读数(`npm test` / `npm run build` / `npm pack --dry-run`)。
105
+
106
+ ### 消费方(迁移句)
107
+
108
+ - **server**(今天精确钉 6.4.0;一致性门实现 `wire` + `localRoot`):提货 8.x 时 `serverAdapter` 的 `legs` 改成
109
+ `{ domains: { declined: "no-production-path", why: "…" }, strictWrite: { declined: "no-production-path", why: "worker 只读不写:写入只经中心" },
110
+ localRoot: …, wire: … }` —— `Partial<Record<LegName, ConformanceLeg>>` 那一行换成 `ConformanceAdapter["legs"]`。语料新增的 82 格严写读数在你这边
111
+ 记 `skipped`(声明不承担),既有账本不受影响。`./migrate`:零读点(你的提货门已钉零 import),删不删都不碰你。
112
+ - **管理面(config-center 产品,今天钉 `^6.4.0`)**:提货 8.x 时 `legs` 四条表态 —— `domains` = 现在那条往返腿;**`strictWrite` = 域写受理那一段**
113
+ (对文档集的每个键跑写受理,任一份不受理 ⇒ `{ outcome: "rejected", stage: "validate", diagnostics }`,全部受理 ⇒
114
+ `{ outcome: "effective", effective: { <键>: <受理后的域文档> }, warnings: [], version: input.version, updatedAt: input.updatedAt }`);
115
+ `wire` / `localRoot` 声明 `no-production-path`(你生产 wire、不消费它;不读 `config.d/`)。两处已知差先看清再接:写受理剥未知键时回报的那份
116
+ 名单在 kit 里没有位置(严写腿告警面恒空,按 `warnings: []` 交);写受理里的跨域引用完整性 `parseDomain` 不做(今天 40 条受理 case 跑过
117
+ `refIntegrityIssues` 零命中,所以暂不分叉)。`./migrate` 三处 import 不受影响(本版不删)。
118
+ - **cli / client-core**(钉 `^7`,脱字号不跨 major):不提货就零动作。提货时:不实现 kit 适配器(cli 已显式拒接 kit 读腿;client-core 零 import)⇒ 零改动;
119
+ cli 那一格「`conformance.projectWarning === configFns.projectWarning`」同一只函数,不受影响;`./migrate` 零 import。
120
+ - **sdk**:不依赖本包,零动作。
121
+
122
+ ### 订正 7.0.1 披露(test d327-schema701 验收 32 格 PASS 31 / INFO 1 / FAIL 0 后补)
123
+
124
+ - **用户可见变化补一形(比字段改名更硬)**:7.0.1 起 `migrate-workers` 的 stdout **首行是结论行**(`outcome=… written=…`),整段 `JSON.parse(stdout)` 的旧脚本会抛错(test 实测 25/25 抛;7.0.0 为 0/24)—— 机读请取**最后一行**解析,或改读 `outcome` / `written` / `refused` 三个契约字段。
125
+ - 7.0.1 披露的「已知未修」两形(`--check` 先于整域终判、整域终判拒时 `refused[]` 为空)本版已修,见上第三节。
126
+
127
+ ### 未闭环(如实)
128
+
129
+ - 严写腿没有 historical 读数(`conformance/historical/` 是三腿时代跑的,53 条 × 三条读腿);重跑会改写一批 `firstRedOn`,本版没做。
130
+ - 严写腿告警面恒空(`parseDomain` 不铸告警),写受理回报的剥键名单在 kit 里没有位置;严写不含跨域引用完整性。两样都是契约层的下一步,候裁。
131
+ - 语料 c06 / c20 的严写金样是 🚧 特征格(S-1020,已定性为缺陷、下一版源头修):写入席受理 runtime 旧文档时三闸旧键被 runtime schema 静默剥掉(读腿是抬升),管理面若按受理形落库,读侧抬升此后找不到它们 = 运维护栏静默消失。修掉(写侧对这三个旧键响亮拒)时两格翻 `rejected{validate}`;在那之前,一个**正确拒收**的管理面会在这两格与金样分歧,请登记进 `knownDivergences`。
132
+
133
+ ## 7.0.1 — 2026-10-03 — 7.0.0 验收三件(文案 / 报告形):拒因补迁移指路、CLI 报告「没写」机读不可误读、指路写 `npx` 全名形(patch)
134
+
135
+ > **判档 = patch**(README「发版纪律」:文案 / 报告形;**判据语义不变** —— 写闸的拒 / 放集合与每条 issue 的 `path` / `code` 同 7.0.0,`RESERVED_WORKER_ENV`
136
+ > 键集不变(90 个),`migrateWorkersDomain` 输出不变;变的只有**拒因文案**与 **CLI 报告形**。conformance 重加冕:`expected.json` / `baseline.json` 零 diff,只有
137
+ > `VERSION` 随版、c67 的 `meta.json` claim 文字改命令拼写)。来源 = test `d327-schema700` 验收的 ②③④(OBS-2 / OBS-3 与冲突 2)。
138
+ > 判据 G7b / G7c 的拆分见 design/327 v1.2(`sessionBackend:tidb` 带全坐标 ⇒ 迁 mysql;无坐标 / 无口令引用 ⇒ refused),**实现不变**。
139
+
140
+ ### 病(7.0.0)
141
+
142
+ - **② 车道旧形拒因不指路**:6.x 存量里最典型的一形 —— 车道只靠 `env.REMOTE_EXEC` / `K8S_*` 携带 —— 被保留名闸拒,拒因点名键与新字段,却**不提迁移命令**;
143
+ 只有被删结构键(`sessionBackend` 等)那几形的拒因指路。运维看到「env cannot set reserved key」,不知道有一条命令能整份搬过去。
144
+ - **③ CLI 报告歧义**:一个文件里有 worker 被 refused ⇒ rc 1、文件一字未写,但 stdout 的 JSON 把其余 worker 列在 `changed` 下;只解析 stdout 的运维脚本
145
+ 会以为它们已迁(真案:`evidence/l1/runs.json` g5-mixed,`fx-good-k8s` 列在 `changed`、盘上 sha256 与跑前相同)。
146
+ - **④ 指路写裸 bin 名**:拒因 / 文档里是 `settings-schema migrate-workers <file>`,没装本包的运维粘贴即 `command not found`(design/327 §3.4 的原句是 `npx` 全名形)。
147
+
148
+ ### 修
149
+
150
+ - **② 一条规则、一个谓词**:拒因带不带迁移指路只看**这个键「旧形 ⇒ 迁移能搬」**。「迁移能搬」= 迁移自己识别的键集 —— 车道键(车道表全部 env 名 +
151
+ `REMOTE_EXEC` / `REQUIRE_EXECUTION_LANE`)∪ 店键(`DB_BACKEND` / `MEMORY_ENGINE` / `MEMORY_ENGINE_BACKEND`)—— 写闸读**同一只谓词**
152
+ (`isMigratableEnvKey`,包内私有,在 `execution-lane-index.ts`;迁移的 `laneKeysOf` / `storeKeysOf` 改读同一处),不是第二张手抄的表。命中的键,
153
+ `env` / `secretEnv` 两口的拒因都在键名后带指路;迁移搬不了的保留名(`PORT` / `MODEL_*` / `MYSQL_*` / `PG_*` / `SESSION_BACKEND` …)不带。句尾的
154
+ `— set it via <归属字段>` 一字不动(下游按它抽归属字段)。
155
+ - **④ 指路句是一个常量**:`MIGRATION_SIGNPOST`(内含 `MIGRATE_WORKERS_COMMAND` = `npx @sema-agent/settings-schema migrate-workers`),保留名闸与删除项预处理(七个被删旧键)
156
+ 引用同一个;迁移 / CLI 的 usage / 文档里的命令拼写同一处。全仓扫描零裸 bin 名残留(`CHANGELOG` 的 7.0.0 及更早段落是历史记录,原句不追改)。
157
+ - **③ 报告形**:CLI 对退出码 0 / 1 / 3 的 stdout 恰两行 —— 第 1 行**唯一结论行** `outcome=<refused|applied|noop|pending> written=<true|false>`,
158
+ 第 2 行 JSON(加 `outcome` / `written`)。`written` 由 `outcome` 派生(只有 `applied` 写盘,写盘成功之后才报;`pending` = `--check` 有改动、没写)。
159
+ 名单键名**带时态**:`changed` / `dropped` 仅在 `written=true` 时存在,没写时是 `wouldChange` / `wouldDrop`。退出码 0 / 1 / 2 / 3 与 stderr 文案不变。
160
+
161
+ ### 用户可见变化(逐条;对只解析旧字段的运维脚本是**行为变化**)
162
+
163
+ 1. **`changed` 在没写盘时不再出现**:7.0.0 的 stdout JSON 恒有 `changed`;7.0.1 起仅 `written=true`(`outcome=applied`)时有。refused / noop / pending 三态下它换名 `wouldChange`,
164
+ 只读 `changed` 的脚本读到 `undefined` —— 这正是要的结果(以前读到一份没落盘的「已迁」名单)。`dropped` 同理换名 `wouldDrop`。`applied` 的 `changed` / `dropped` 形与 7.0.0 相同(有正控格)。
165
+ 2. **stdout 从一行 JSON 变成两行**:第 1 行是结论行,JSON 在第 2 行(**取最后一行解析**);整段 `JSON.parse(stdout)` 的脚本要改成只解析最后一行。JSON 新增 `outcome` / `written`。
166
+ 3. **`--check` 有改动时 `outcome=pending written=false`**(退出 3,同前):结论词的第四个值,是 `--check` 专有的「有改动、没写」。
167
+ 4. **整域终判拒(迁出的文档整域读不进 7.0.0)现在也出报告**:7.0.0 此时 stdout 先打了含 `changed` 的 JSON、再在 stderr 说 nothing was written(同一个歧义);7.0.1 报 `outcome=refused written=false`,
168
+ `refused[]` 为空、原因仍在 stderr(那条文案不变)。
169
+ 5. **拒因文案变长**:车道键 / 店键的保留名拒因在键名后多一段指路;被删旧键的拒因把「run the one-time migration before reading this document: …」换成同一个常量的句子
170
+ (仍含 `migrateWorkersDomain` 与 `migrate-workers`)。读拒因文案做字符串匹配的下游:`set it via <字段>` 句尾不变。
171
+ 6. **已知(7.0.0 既有,披露、不在本 patch 改)**:`--check` 先于整域终判 —— 迁出的文档整域读不进 7.0.0(例:两只同名 worker)时,`--check` 报 `outcome=pending`(退出 3),
172
+ 真跑才报 `outcome=refused`(退出 1)。`pending` 的意思是「迁移产出了改动、`--check` 没写」,**不保证**真跑一定写盘;要改它会动 `--check` 的退出码,另案(`test/cli-report-7.0.1.test.ts` 有一格 🚧 特征钉,修掉时翻成 refused / rc 1)。
173
+
174
+ ### 红先 / 门
175
+
176
+ - 红先提交只含两个新测试文件 —— `test/migration-signpost-7.0.1.test.ts`(②④)、`test/cli-report-7.0.1.test.ts`(③)。修前读数:两文件 44 格 **29 红** / 15 绿(绿 = 判据语义不变的 P6 两格 +
177
+ 不带指路的保留名 8 格 + P3「两侧非空」+ 句尾不变 + 另 3 格正控:报告 `event` / `file`、用法错 rc 2 无 stdout、refused 的 stderr 文案);P3 对拍逐个保留名:迁移会动而拒因不带指路的 57 个名(车道 52 + `REMOTE_EXEC` / `REQUIRE_EXECUTION_LANE` + 店键 3,× `env` / `secretEnv` 两口)全红,反向(拒因带指路而迁移不动)0 个。
178
+ - **差分(车内一次性,脚本不入库)**:同一批 379 例(每个保留名 × `env` / `secretEnv` × enabled / 停用 = 360,七个删除项 × 2 = 14,非保留名 4 与合法新形 1 = 5)过 7.0.0 发布树与本树的
179
+ `WorkerSpec.safeParse`:`(success, issue.path, issue.code)` 零差异(374 例两边都拒、5 例两边都放);同批过 `migrateWorkersDomain`:输出逐字节相同。
180
+ - **codex 一轮**(`adversarial-review`,`gpt-6.1-sol`,`--base 1db8e3f --scope branch`):Verdict approve、No material findings;它复现的「`--check` 跳过整域终判」即上面「用户可见变化」第 6 条(基线既有)。
181
+ - 旧测试按意图改:`migrate-workers-7.0.0.test.ts` 一格(CLI 可迁:解析 stdout 最后一行,加结论行断言)+ describe 标题的命令拼写。
10
182
 
11
183
  ## 7.0.0 — 2026-10-03 — M-R2 WorkerSpec 结构块:执行车道 `runtime.execution` + 存储 `runtime.store`,词表单正本 `EXECUTION_LANES` 等六个导出,一次性迁移 `migrateWorkersDomain`(BREAKING)
12
184
 
package/README.md CHANGED
@@ -48,6 +48,19 @@ therefore shows only `zod`. Node built-ins only (`node:crypto`, `node:fs`).
48
48
  > a zod-3 host (today the config-center product) still dedups to one instance. Moving this package to zod 4 is a major
49
49
  > of its own (S-777); 6.2.0 does not touch the dependency or peer range.
50
50
 
51
+ ## 升级指引:7.x → 8.0.0
52
+
53
+ 8.0.0 的破坏面只在**一致性门 kit**(`./conformance`)的类型与 release 律、以及 CLI `migrate-workers --check` 的一种结局上;契约 schema、域默认值、
54
+ 读写 helper 的读数一字未动。逐条(谁受影响 / 怎么改 / 不改会怎样)见 CHANGELOG 8.0.0「用户可见变化」,要点:
55
+
56
+ - **实现了 kit 适配器的**:`legs` 是全量 `Record<LegName, LegStance>` —— 四条腿(`domains` / `strictWrite` / `wire` / `localRoot`)每条要么实现、
57
+ 要么写 `{ declined: "no-production-path", why: "<你仓的事实>" }`。少一条 ⇒ tsc 红;用 `as` 绕过 ⇒ release 门违例 `has no stance`。
58
+ 只读的 worker:`domains` 与 `strictWrite` 声明不承担;写入席:实现 `strictWrite`(域写受理,任一份不受理 ⇒ `rejected{validate}`)。
59
+ - **持 `Record<LegName, …>` / 对 `LegName` 穷举的**:为新腿 `strictWrite` 表态。
60
+ - **按 `migrate-workers --check` 做闸的脚本**:整域会被拒的文档 `--check` 现在报 `outcome=refused` / 退出 1(以前 `pending` / 3),`refused[]`
61
+ 逐条带出被拒项(`name` 可为 `null`)。
62
+ - **只用 schema / helper 的**:零动作。`./migrate` 本版**没有**删(管理面仍在用)。
63
+
51
64
  ## What's in it
52
65
 
53
66
  - **The zod contract** (`types.ts`): `DOMAIN_SCHEMAS` (17 domains — 0.10.0 adds `projects`, the project
@@ -98,6 +111,30 @@ therefore shows only `zod`. Node built-ins only (`node:crypto`, `node:fs`).
98
111
  `env.REMOTE_EXEC` — the migration never guesses a lane) come back in `refused` with the field to fix and the
99
112
  `original` row verbatim — they are NOT in the migrated `doc`, so a caller that writes `doc` back must put each
100
113
  `refused[].original` back at its `index` (or store it aside), or that worker's spec disappears.
114
+ - **`migrate-workers` CLI: report shape + rejection signposts (7.0.1)** (`bin/settings-schema.mjs`, core in `cli.ts`):
115
+ `npx @sema-agent/settings-schema migrate-workers <workers.json> [--check]` prints exactly two lines on stdout (exit 0 / 1 / 3): line 1 is the
116
+ ONE conclusion line `outcome=<refused|applied|noop|pending> written=<true|false>`, line 2 is the JSON report (`event: "workers_domain_migrated"`,
117
+ `outcome`, `written`, `file`, the worker lists, `refused[]`). Only `applied` writes the file (`written` is derived from `outcome`; `pending` =
118
+ `--check` with changes). The list keys carry tense — `changed` / `dropped` exist **only when `written=true`**; when nothing was written
119
+ (refused / noop / pending) the same lists are `wouldChange` / `wouldDrop`, so a script that reads only stdout can never take a worker that was
120
+ not migrated for migrated. Exit codes are unchanged (0 applied or noop · 1 refused or the migrated document does not read as a 7.0.0 workers
121
+ domain, nothing written · 2 usage / unreadable / not JSON, empty stdout · 3 `--check` with changes).
122
+
123
+ | 7.0.0 stdout | 7.0.1 stdout |
124
+ |---|---|
125
+ | one JSON line | the conclusion line, then the JSON line (parse the **last** line) |
126
+ | `changed` (always) | `changed` when `written=true`, else `wouldChange` |
127
+ | `dropped` (always) | `dropped` when `written=true`, else `wouldDrop` |
128
+ | — | `outcome`, `written` |
129
+
130
+ **8.0.0**: `--check` and a real run go through ONE verdict (per-worker refusals → nothing to change ⇒ `noop` → the whole-domain final judgment →
131
+ changes); `--check` only decides whether the "changes" branch writes (`pending`) or not. A document the whole-domain judgment refuses (two workers
132
+ with one name …) is `outcome=refused` / exit 1 under `--check` too (7.x: `pending` / exit 3), and `refused[]` names the refused items in the
133
+ per-worker shape (`name` / `field` / `reason`; `name` is `null` for an entry that carries no string name) instead of being empty.
134
+
135
+ Rejections that a migration can lift carry ONE signpost sentence (a constant, `MIGRATION_SIGNPOST`): the seven deleted 6.x fields, and an
136
+ `env` / `secretEnv` key the migration moves or drops (the execution-lane keys incl. `REMOTE_EXEC` / `REQUIRE_EXECUTION_LANE`, and the store keys
137
+ `DB_BACKEND` / `MEMORY_ENGINE` / `MEMORY_ENGINE_BACKEND`). A reserved name the migration cannot move (`PORT`, `MODEL_*`, `MYSQL_*` …) carries none.
101
138
  - **Safety merge-spec** (`safety-merge-spec.ts`, subpath `./safety-merge-spec`): `SAFETY_MERGE_SPEC`, the ONE
102
139
  per-category merge/enforcement table every settings resolver reads (`mergeShape` / `minTrust` / `tighten` /
103
140
  `enforcement` / `fail`) — no consumer re-implements the merge law. 1.1.0 adds **member-level admission**:
@@ -250,9 +287,11 @@ therefore shows only `zod`. Node built-ins only (`node:crypto`, `node:fs`).
250
287
 
251
288
  ## Cross-repo conformance gate (`./conformance`, 3.0.0)
252
289
 
253
- 契约仓自带**金样语料 + 比对器**,随包发(`conformance/cases/**`)。每个消费方把**自己的生产读腿**塞给
290
+ 契约仓自带**金样语料 + 比对器**,随包发(`conformance/cases/**`)。每个消费方把**自己的生产入口**塞给
254
291
  kit 跑一遍 ⇒ 同一份 settings 语料在配置中心、worker、管理面、契约仓自身读出的 effective 逐字节一致、
255
- 告警多重集一致。
292
+ 告警多重集一致;写入席对同一份文档收不收也一致。**四条腿**(8.0.0):`domains`(各域原始文档 → effective)、
293
+ `strictWrite`(同一份文档 → 严写受理;契约仓 = `parseDomain`,任一份不受理 ⇒ `rejected{validate}`,读腿抢救处严写拒)、
294
+ `wire`、`localRoot`。
256
295
 
257
296
  ```ts
258
297
  import { runConformance } from "@sema-agent/settings-schema/conformance";
@@ -264,13 +303,15 @@ if (!report.ok) throw new Error(report.violations.join("\n"));
264
303
  (**生产代码**的入口 —— 在测试里把契约包函数本身当读腿,门证明的只是「包等于包」)。4.0.0 起这句
265
304
  **有牙**:入口落在**本包自己的 `dist/`**(拿自己比自己)⇒ `createContractAdapter` **当场拒**;落在
266
305
  **另一份**装好的契约包里(`historical` 模式的正当目标)⇒ 工厂不拒,由 `release` 门点名。
267
- - **两种模式**:`release`(版本三方对齐 + 生产入口核验 + 禁 `only`/归一器 + 至少登记一条腿 + **登记的腿
268
- 必须跑**;未登记的腿只记 `skipped: no adapter`,不算违例)与 `historical`(对旧版包跑,只记行为 diff)。
306
+ - **每条腿都要表态**(8.0.0):实现它,或 `{ declined: "no-production-path", why }`(你的生产代码结构上没有这种读法 / 写法)。
307
+ 缺表态 = release 违例;声明的腿逐格 `skipped`(带理由),报告 `adapter.declined` 列出。
308
+ - **两种模式**:`release`(版本三方对齐 + 每条腿有表态 + 生产入口核验 + 禁 `only`/归一器 + 至少实现一条腿 + **实现的腿
309
+ 必须跑**)与 `historical`(对旧版包跑,只记行为 diff)。
269
310
  - **四律**:对抗律 / 变异律 / 覆盖律 / 来源律 —— 缺一不算「门」。
270
311
  - **已知分歧账本** `knownDivergences`:消费方对金样的**合法**差异(临时欠账 / 成文语义)登记成机器可核的
271
312
  一行,命中记 `diff-ledgered` 不阻塞;**告警差与元数据差永不可入账**,而**从未命中的一条登记本身就是
272
313
  违例**(棘轮只降不升,逼着到期翻转)。契约仓自证不许挂账本。
273
- - 契约形、报告形、语料贡献形、以及**三条如实登记的边界**见 `docs/CONFORMANCE.md`。
314
+ - 契约形、报告形、语料贡献形、以及**如实登记的边界**见 `docs/CONFORMANCE.md`。
274
315
  - Node-only(语料在磁盘上,local-root 腿要落盘),不进主 barrel。
275
316
 
276
317
  ## Secret boundary
@@ -298,6 +339,7 @@ defaults. Each `config.d/<domain>.json` is exactly that domain's zod object.
298
339
  自 `1.0.0`(改名)起本包按**标准 semver** 发版;`0.x` 时代的「破坏性变更必 bump minor」规矩**已撤**
299
340
  (末条仅作历史注记)。判档看**消费方可见性**,不看 diff 大小:
300
341
 
342
+ - **CLI 报告形的契约边界(7.0.1 起)**:`migrate-workers` 的 stdout = 结论行 + JSON;其中 `outcome` / `written` / `refused` 三字段是机读契约(再改按 minor),其余键与行形是诊断面,patch 可变(test 认定口径 2026-10-03:CLI 报告形不在 C-R197 包导出面)。机读请取最后一行解析。
301
343
  - **破坏性变更必 bump major**:字段/导出改名、改形(retype)、删除、默认值变更、校验收紧或放宽
302
344
  (例:`.passthrough()` → 默认 strip、宽读 → 严判)、枚举**重排**——凡是能让一个按上一版契约写的
303
345
  消费方在**运行期**变红、或让同一份配置解析出不同结果的改动;编译期同判,**唯一裁定例外** = 下一条的
@@ -1 +1 @@
1
- 7.0.0
1
+ 8.0.0
@@ -98,6 +98,11 @@
98
98
  "updatedAt": "2026-09-16T00:00:00.000Z"
99
99
  }
100
100
  },
101
+ "strictWrite": {
102
+ "outcome": "rejected",
103
+ "stage": "validate",
104
+ "why": "拒:读腿逐条抢救 models(丢掉缺 id 的那一条 + entry-dropped);写入席不替运维删条目 —— 同一份文档里有一条坏条目,整次不受理。"
105
+ },
101
106
  "wire": {
102
107
  "derive": true
103
108
  },
@@ -89,6 +89,11 @@
89
89
  "updatedAt": "2026-09-16T00:00:00.000Z"
90
90
  }
91
91
  },
92
+ "strictWrite": {
93
+ "outcome": "rejected",
94
+ "stage": "validate",
95
+ "why": "拒:读腿对 hosts 先做存量夹紧(超长名剔除,hosts-grandfathered)再逐条抢救(entry-dropped);两样都只活在读路径,严写按现行 schema 判:名长 > 63 与名字形不合法都拒。"
96
+ },
92
97
  "wire": {
93
98
  "derive": true
94
99
  },
@@ -102,6 +102,47 @@
102
102
  "updatedAt": "2026-09-16T00:00:00.000Z"
103
103
  }
104
104
  },
105
+ "strictWrite": {
106
+ "outcome": "effective",
107
+ "effective": {
108
+ "models": {
109
+ "models": [
110
+ {
111
+ "api": "openai-completions",
112
+ "apiKeyEnv": "OPENAI_API_KEY",
113
+ "enabled": true,
114
+ "id": "m0-id",
115
+ "name": "m0",
116
+ "provider": "openai",
117
+ "quotaWeight": 1,
118
+ "reasoning": false,
119
+ "tier": "standard",
120
+ "vision": false
121
+ },
122
+ {
123
+ "api": "openai-completions",
124
+ "apiKeyEnv": "OPENAI_API_KEY",
125
+ "enabled": true,
126
+ "id": "m1-id",
127
+ "name": "m1",
128
+ "provider": "openai",
129
+ "quotaWeight": 1,
130
+ "reasoning": false,
131
+ "tier": "standard",
132
+ "vision": false
133
+ }
134
+ ],
135
+ "roles": {},
136
+ "tierGroups": []
137
+ }
138
+ },
139
+ "warnings": [],
140
+ "metadata": {
141
+ "version": 7,
142
+ "updatedAt": "2026-09-16T00:00:00.000Z"
143
+ },
144
+ "why": "受理:models 文档里字面叫 `models.1.k` 的键与条目里的未知键 `k` 都被 schema 剥掉(读腿报 unknown-keys-dropped);parseDomain 同样剥、不铸告警 —— 受理形逐字等于读腿 models 那一格,告警面为空。"
145
+ },
105
146
  "wire": {
106
147
  "derive": true
107
148
  },
@@ -67,6 +67,11 @@
67
67
  "updatedAt": "2026-09-16T00:00:00.000Z"
68
68
  }
69
69
  },
70
+ "strictWrite": {
71
+ "outcome": "rejected",
72
+ "stage": "validate",
73
+ "why": "拒:全坏集合,读腿救不出一条幸存者、整域降级成默认(domain-defaulted);写入席不会把坏文档换成空默认存下。"
74
+ },
70
75
  "wire": {
71
76
  "derive": true
72
77
  },
@@ -3,6 +3,11 @@
3
3
  "outcome": "rejected",
4
4
  "stage": "validate"
5
5
  },
6
+ "strictWrite": {
7
+ "outcome": "rejected",
8
+ "stage": "validate",
9
+ "why": "拒:rosters 是 gate 域,读腿对坏条目(name 是数字)本就整域拒;严写同一份文档同样过不了 rosters 的 schema。"
10
+ },
6
11
  "wire": {
7
12
  "outcome": "rejected",
8
13
  "stage": "validate",
@@ -89,6 +89,18 @@
89
89
  "updatedAt": "2026-09-16T00:00:00.000Z"
90
90
  }
91
91
  },
92
+ "strictWrite": {
93
+ "outcome": "effective",
94
+ "effective": {
95
+ "runtime": {}
96
+ },
97
+ "warnings": [],
98
+ "metadata": {
99
+ "version": 7,
100
+ "updatedAt": "2026-09-16T00:00:00.000Z"
101
+ },
102
+ "why": "🚧 S-1020 特征格(已裁 = 缺陷,源头修排下一版):受理,受理形 = { runtime: {} }。读腿从 runtime 旧文档抬升 governance 三闸(legacy-governance-lifted);写入席不抬升 —— 受理的只有提交的 runtime 一份,三闸旧键被 runtime schema 静默剥掉(不拒、不告警,读腿的剥键扫描本就豁免这三个键)。写入席若按受理形落库,读侧抬升此后找不到它们。S-1020 修掉(写侧拒 runtime 上的三闸旧键)时本格翻 rejected{validate}。"
103
+ },
92
104
  "wire": {
93
105
  "derive": true
94
106
  },
@@ -62,6 +62,11 @@
62
62
  "updatedAt": "2026-09-16T00:00:00.000Z"
63
63
  }
64
64
  },
65
+ "strictWrite": {
66
+ "outcome": "rejected",
67
+ "stage": "validate",
68
+ "why": "拒:`hooks` 不是域名(它是 settings.json 的键,不是配置域)—— 写入席没有这个域可受理;读腿各按各的处置(domains 腿按域名取文档故无声、wire 逐字承运、本地报 not-a-domain),严写不替运维把它丢掉。"
69
+ },
65
70
  "wire": {
66
71
  "derive": true
67
72
  },
@@ -326,6 +326,83 @@
326
326
  "updatedAt": "2026-09-16T00:00:00.000Z"
327
327
  }
328
328
  },
329
+ "strictWrite": {
330
+ "outcome": "effective",
331
+ "effective": {
332
+ "limits": {
333
+ "zzzUnknown000": 0,
334
+ "zzzUnknown001": 1,
335
+ "zzzUnknown002": 2,
336
+ "zzzUnknown003": 3,
337
+ "zzzUnknown004": 4,
338
+ "zzzUnknown005": 5,
339
+ "zzzUnknown006": 6,
340
+ "zzzUnknown007": 7,
341
+ "zzzUnknown008": 8,
342
+ "zzzUnknown009": 9,
343
+ "zzzUnknown010": 10,
344
+ "zzzUnknown011": 11,
345
+ "zzzUnknown012": 12,
346
+ "zzzUnknown013": 13,
347
+ "zzzUnknown014": 14,
348
+ "zzzUnknown015": 15,
349
+ "zzzUnknown016": 16,
350
+ "zzzUnknown017": 17,
351
+ "zzzUnknown018": 18,
352
+ "zzzUnknown019": 19,
353
+ "zzzUnknown020": 20,
354
+ "zzzUnknown021": 21,
355
+ "zzzUnknown022": 22,
356
+ "zzzUnknown023": 23,
357
+ "zzzUnknown024": 24,
358
+ "zzzUnknown025": 25,
359
+ "zzzUnknown026": 26,
360
+ "zzzUnknown027": 27,
361
+ "zzzUnknown028": 28,
362
+ "zzzUnknown029": 29,
363
+ "zzzUnknown030": 30,
364
+ "zzzUnknown031": 31,
365
+ "zzzUnknown032": 32,
366
+ "zzzUnknown033": 33,
367
+ "zzzUnknown034": 34,
368
+ "zzzUnknown035": 35,
369
+ "zzzUnknown036": 36,
370
+ "zzzUnknown037": 37,
371
+ "zzzUnknown038": 38,
372
+ "zzzUnknown039": 39,
373
+ "zzzUnknown040": 40,
374
+ "zzzUnknown041": 41,
375
+ "zzzUnknown042": 42,
376
+ "zzzUnknown043": 43,
377
+ "zzzUnknown044": 44,
378
+ "zzzUnknown045": 45,
379
+ "zzzUnknown046": 46,
380
+ "zzzUnknown047": 47,
381
+ "zzzUnknown048": 48,
382
+ "zzzUnknown049": 49,
383
+ "zzzUnknown050": 50,
384
+ "zzzUnknown051": 51,
385
+ "zzzUnknown052": 52,
386
+ "zzzUnknown053": 53,
387
+ "zzzUnknown054": 54,
388
+ "zzzUnknown055": 55,
389
+ "zzzUnknown056": 56,
390
+ "zzzUnknown057": 57,
391
+ "zzzUnknown058": 58,
392
+ "zzzUnknown059": 59,
393
+ "zzzUnknown060": 60,
394
+ "zzzUnknown061": 61,
395
+ "zzzUnknown062": 62,
396
+ "zzzUnknown063": 63
397
+ }
398
+ },
399
+ "warnings": [],
400
+ "metadata": {
401
+ "version": 7,
402
+ "updatedAt": "2026-09-16T00:00:00.000Z"
403
+ },
404
+ "why": "受理:limits 是 open-world,64 个未知键原样承运(读腿报 unknown-keys-carried);严写同样承运,受理形等于读腿 limits 那一格,告警面为空。"
405
+ },
329
406
  "wire": {
330
407
  "derive": true
331
408
  },
@@ -328,6 +328,84 @@
328
328
  "updatedAt": "2026-09-16T00:00:00.000Z"
329
329
  }
330
330
  },
331
+ "strictWrite": {
332
+ "outcome": "effective",
333
+ "effective": {
334
+ "limits": {
335
+ "zzzUnknown000": 0,
336
+ "zzzUnknown001": 1,
337
+ "zzzUnknown002": 2,
338
+ "zzzUnknown003": 3,
339
+ "zzzUnknown004": 4,
340
+ "zzzUnknown005": 5,
341
+ "zzzUnknown006": 6,
342
+ "zzzUnknown007": 7,
343
+ "zzzUnknown008": 8,
344
+ "zzzUnknown009": 9,
345
+ "zzzUnknown010": 10,
346
+ "zzzUnknown011": 11,
347
+ "zzzUnknown012": 12,
348
+ "zzzUnknown013": 13,
349
+ "zzzUnknown014": 14,
350
+ "zzzUnknown015": 15,
351
+ "zzzUnknown016": 16,
352
+ "zzzUnknown017": 17,
353
+ "zzzUnknown018": 18,
354
+ "zzzUnknown019": 19,
355
+ "zzzUnknown020": 20,
356
+ "zzzUnknown021": 21,
357
+ "zzzUnknown022": 22,
358
+ "zzzUnknown023": 23,
359
+ "zzzUnknown024": 24,
360
+ "zzzUnknown025": 25,
361
+ "zzzUnknown026": 26,
362
+ "zzzUnknown027": 27,
363
+ "zzzUnknown028": 28,
364
+ "zzzUnknown029": 29,
365
+ "zzzUnknown030": 30,
366
+ "zzzUnknown031": 31,
367
+ "zzzUnknown032": 32,
368
+ "zzzUnknown033": 33,
369
+ "zzzUnknown034": 34,
370
+ "zzzUnknown035": 35,
371
+ "zzzUnknown036": 36,
372
+ "zzzUnknown037": 37,
373
+ "zzzUnknown038": 38,
374
+ "zzzUnknown039": 39,
375
+ "zzzUnknown040": 40,
376
+ "zzzUnknown041": 41,
377
+ "zzzUnknown042": 42,
378
+ "zzzUnknown043": 43,
379
+ "zzzUnknown044": 44,
380
+ "zzzUnknown045": 45,
381
+ "zzzUnknown046": 46,
382
+ "zzzUnknown047": 47,
383
+ "zzzUnknown048": 48,
384
+ "zzzUnknown049": 49,
385
+ "zzzUnknown050": 50,
386
+ "zzzUnknown051": 51,
387
+ "zzzUnknown052": 52,
388
+ "zzzUnknown053": 53,
389
+ "zzzUnknown054": 54,
390
+ "zzzUnknown055": 55,
391
+ "zzzUnknown056": 56,
392
+ "zzzUnknown057": 57,
393
+ "zzzUnknown058": 58,
394
+ "zzzUnknown059": 59,
395
+ "zzzUnknown060": 60,
396
+ "zzzUnknown061": 61,
397
+ "zzzUnknown062": 62,
398
+ "zzzUnknown063": 63,
399
+ "zzzUnknown064": 64
400
+ }
401
+ },
402
+ "warnings": [],
403
+ "metadata": {
404
+ "version": 7,
405
+ "updatedAt": "2026-09-16T00:00:00.000Z"
406
+ },
407
+ "why": "受理:同上一条,65 个未知键(读腿告警截断到 64 条坐标 + truncatedFrom,那是告警面的事);严写逐字承运全部 65 个,告警面为空。"
408
+ },
331
409
  "wire": {
332
410
  "derive": true
333
411
  },
@@ -96,6 +96,11 @@
96
96
  "updatedAt": "2026-09-16T00:00:00.000Z"
97
97
  }
98
98
  },
99
+ "strictWrite": {
100
+ "outcome": "rejected",
101
+ "stage": "validate",
102
+ "why": "拒:models.roles 里有一个密钥形的坏键,读腿把它逐条丢掉(entry-dropped,坐标脱敏);严写不删条目 ⇒ 整次不受理。"
103
+ },
99
104
  "wire": {
100
105
  "derive": true
101
106
  },
@@ -78,6 +78,20 @@
78
78
  "updatedAt": "2026-09-16T00:00:00.000Z"
79
79
  }
80
80
  },
81
+ "strictWrite": {
82
+ "outcome": "effective",
83
+ "effective": {
84
+ "scenarios": {
85
+ "scenarios": []
86
+ }
87
+ },
88
+ "warnings": [],
89
+ "metadata": {
90
+ "version": 7,
91
+ "updatedAt": "2026-09-16T00:00:00.000Z"
92
+ },
93
+ "why": "受理:scenarios 里 CJK / emoji / 组合字符的未知键被 schema 剥掉(读腿报 unknown-keys-dropped);严写同样剥、不铸告警,受理形等于读腿 scenarios 那一格。"
94
+ },
81
95
  "wire": {
82
96
  "derive": true
83
97
  },
@@ -62,6 +62,11 @@
62
62
  "updatedAt": "2026-09-16T00:00:00.000Z"
63
63
  }
64
64
  },
65
+ "strictWrite": {
66
+ "outcome": "rejected",
67
+ "stage": "validate",
68
+ "why": "拒:`constructor` / `toString` / `hasOwnProperty` / `__proto__` 都不是域名 —— 原型链上的名字在严写这里同样只是「不是域的键」,写入席没有这些域可受理(读腿:domains 无声、wire 逐字承运、本地报 not-a-domain)。"
69
+ },
65
70
  "wire": {
66
71
  "derive": true
67
72
  },