@sema-agent/settings-schema 7.0.1 → 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.
- package/CHANGELOG.md +123 -1
- package/README.md +28 -5
- package/conformance/VERSION +1 -1
- package/conformance/cases/c01-entry-salvage-then-typo/expected.json +5 -0
- package/conformance/cases/c02-hosts-clamp-stacked/expected.json +5 -0
- package/conformance/cases/c03-literal-dotted-top-key/expected.json +41 -0
- package/conformance/cases/c04-all-bad-collection-not-salvaged/expected.json +5 -0
- package/conformance/cases/c05-gate-domain-bad-entry-throws/expected.json +5 -0
- package/conformance/cases/c06-legacy-governance-lift/expected.json +12 -0
- package/conformance/cases/c07-hooks-is-not-a-config-domain/expected.json +5 -0
- package/conformance/cases/c08-limits-unknown-carried-64/expected.json +77 -0
- package/conformance/cases/c09-limits-unknown-carried-65/expected.json +78 -0
- package/conformance/cases/c10-record-key-secret-redacted-twice/expected.json +5 -0
- package/conformance/cases/c11-unicode-unknown-keys/expected.json +14 -0
- package/conformance/cases/c12-prototype-name-top-keys/expected.json +5 -0
- package/conformance/cases/c13-models-field-null/expected.json +5 -0
- package/conformance/cases/c14-models-field-empty-array/expected.json +16 -0
- package/conformance/cases/c15-models-field-number/expected.json +5 -0
- package/conformance/cases/c16-deep-nesting-205/expected.json +14 -0
- package/conformance/cases/c17-empty-domain-file/expected.json +10 -0
- package/conformance/cases/c18-wire-envelope-not-an-object/expected.json +10 -0
- package/conformance/cases/c19-wire-envelope-no-version/expected.json +10 -0
- package/conformance/cases/c20-governance-absent-lifts/expected.json +12 -0
- package/conformance/cases/c21-governance-empty-object-wins/expected.json +13 -0
- package/conformance/cases/c22-governance-null-throws/expected.json +5 -0
- package/conformance/cases/c23-mixed-validity-models/expected.json +5 -0
- package/conformance/cases/c24-valid-entries-with-unknown-keys/expected.json +29 -0
- package/conformance/cases/c25-published-roundtrip-zero-warnings/expected.json +33 -0
- package/conformance/cases/c26-hosts-array-1024/expected.json +7183 -0
- package/conformance/cases/c27-hosts-array-1025/expected.json +5 -0
- package/conformance/cases/c28-large-document-over-1mb/expected.json +14 -0
- package/conformance/cases/c29-salvage-then-domain-refine-fails/expected.json +5 -0
- package/conformance/cases/c30-version-triple-a-first/expected.json +29 -0
- package/conformance/cases/c31-version-triple-b-changed/expected.json +29 -0
- package/conformance/cases/c32-version-triple-a-restored/expected.json +29 -0
- package/conformance/cases/c33-limits-nested-carried-key/expected.json +17 -0
- package/conformance/cases/c34-skills-content-vs-manifest/expected.json +23 -0
- package/conformance/cases/c35-portable-domain-file-is-null/expected.json +5 -0
- package/conformance/cases/c36-skills-entry-without-content/expected.json +23 -0
- package/conformance/cases/c37-skills-field-null/expected.json +5 -0
- package/conformance/cases/c38-remote-exec-is-a-non-domain-file/expected.json +5 -0
- package/conformance/cases/c39-hidden-file-in-config-d/expected.json +5 -0
- package/conformance/cases/c40-s712-identity-only-what-reads-out/expected.json +5 -0
- package/conformance/cases/c41-s792-encoded-pem-identity/expected.json +5 -0
- package/conformance/cases/c42-s792-unknown-key-secret-redacted/expected.json +33 -0
- package/conformance/cases/c43-s792-form-and-deep-encoded-pem/expected.json +5 -0
- package/conformance/cases/c44-s792-utf8-escape-glued-token/expected.json +5 -0
- package/conformance/cases/c45-s792-identity-bounds-and-decode-depth/expected.json +5 -0
- package/conformance/cases/c46-s811-host-name-url-userinfo-characterization/expected.json +5 -0
- package/conformance/cases/c47-s811-roles-key-url-userinfo-characterization/expected.json +5 -0
- package/conformance/cases/c48-s935-model-headers-transported/expected.json +59 -0
- package/conformance/cases/c49-s935-model-headers-secret-entry-dropped/expected.json +5 -0
- package/conformance/cases/c50-s784-lane-host-legal/expected.json +44 -0
- package/conformance/cases/c51-s784-lane-e2b-legal/expected.json +46 -0
- package/conformance/cases/c52-s784-lane-k8s-legal/expected.json +48 -0
- package/conformance/cases/c53-s784-lane-ssh-legal/expected.json +48 -0
- package/conformance/cases/c54-s784-lane-adb-legal/expected.json +44 -0
- package/conformance/cases/c55-s784-lane-local-docker-legal/expected.json +45 -0
- package/conformance/cases/c56-s784-lane-device-legal/expected.json +51 -0
- package/conformance/cases/c57-s784-g1-enabled-no-lane/expected.json +5 -0
- package/conformance/cases/c58-s784-g2-multitenant-host/expected.json +5 -0
- package/conformance/cases/c59-s784-g3-k8s-missing-image/expected.json +5 -0
- package/conformance/cases/c60-s784-g3-e2b-missing-key/expected.json +5 -0
- package/conformance/cases/c61-s784-g3-other-lane-key/expected.json +5 -0
- package/conformance/cases/c62-s784-g7a-session-tidb/expected.json +5 -0
- package/conformance/cases/c63-s784-host-two-replicas/expected.json +5 -0
- package/conformance/cases/c64-s784-multitenant-local-store/expected.json +5 -0
- package/conformance/cases/c65-s784-pg-without-db/expected.json +5 -0
- package/conformance/cases/c66-s784-memory-backend-mismatch/expected.json +5 -0
- package/conformance/cases/c67-s784-retired-field-loud/expected.json +5 -0
- package/conformance/cases/c68-s784-device-local-store/expected.json +5 -0
- package/conformance/cases/cc01-mcp-toolfaces-non-object/expected.json +54 -0
- package/conformance/cases/cc02-mcp-source-key-is-transported/expected.json +27 -0
- package/conformance/cases/cc03-model-compat-unknown-subkey/expected.json +33 -0
- package/conformance/cases/cli01-model-name-nfc-vs-nfd/expected.json +5 -0
- package/conformance/cases/cli02-control-and-zero-width-chars/expected.json +5 -0
- package/conformance/cases/cli03-credential-pasted-into-unknown-key/expected.json +22 -0
- package/conformance/cases/cli04-env-value-with-newline/expected.json +46 -0
- package/conformance/cases/cli05-long-and-prefix-colliding-names/expected.json +53 -0
- package/conformance/cases/t01-absent-domain-key-not-null/expected.json +5 -0
- package/conformance/cases/t02-entry-salvage-vs-whole-domain-refusal/expected.json +5 -0
- package/conformance/cases/t03-prototype-name-key-not-swallowed/expected.json +29 -0
- package/conformance/cases/t04-secret-literal-never-in-warning-value/expected.json +5 -0
- package/conformance/cases/z01-every-domain-empty-object/expected.json +64 -0
- package/conformance/cases/z02-every-domain-unknown-key/expected.json +66 -0
- package/dist/cli.js +58 -26
- package/dist/config-fns.js +7 -1
- package/dist/conformance/contract-adapter.d.ts +4 -2
- package/dist/conformance/contract-adapter.js +21 -7
- package/dist/conformance/corpus.js +12 -7
- package/dist/conformance/derive.d.ts +26 -0
- package/dist/conformance/derive.js +25 -2
- package/dist/conformance/index.d.ts +4 -3
- package/dist/conformance/index.js +4 -3
- package/dist/conformance/run.d.ts +3 -3
- package/dist/conformance/run.js +87 -17
- package/dist/conformance/types.d.ts +57 -17
- package/dist/conformance/types.js +4 -2
- package/dist/hooks.d.ts +4 -4
- package/package.json +2 -1
|
@@ -99,6 +99,59 @@
|
|
|
99
99
|
"updatedAt": "2026-09-16T00:00:00.000Z"
|
|
100
100
|
}
|
|
101
101
|
},
|
|
102
|
+
"strictWrite": {
|
|
103
|
+
"outcome": "effective",
|
|
104
|
+
"effective": {
|
|
105
|
+
"models": {
|
|
106
|
+
"models": [
|
|
107
|
+
{
|
|
108
|
+
"api": "openai-completions",
|
|
109
|
+
"apiKeyEnv": "OPENAI_API_KEY",
|
|
110
|
+
"enabled": true,
|
|
111
|
+
"id": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx-id",
|
|
112
|
+
"name": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
|
|
113
|
+
"provider": "openai",
|
|
114
|
+
"quotaWeight": 1,
|
|
115
|
+
"reasoning": false,
|
|
116
|
+
"tier": "standard",
|
|
117
|
+
"vision": false
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
"api": "openai-completions",
|
|
121
|
+
"apiKeyEnv": "OPENAI_API_KEY",
|
|
122
|
+
"enabled": true,
|
|
123
|
+
"id": "prefix-shared-name-aaaa-id",
|
|
124
|
+
"name": "prefix-shared-name-aaaa",
|
|
125
|
+
"provider": "openai",
|
|
126
|
+
"quotaWeight": 1,
|
|
127
|
+
"reasoning": false,
|
|
128
|
+
"tier": "standard",
|
|
129
|
+
"vision": false
|
|
130
|
+
},
|
|
131
|
+
{
|
|
132
|
+
"api": "openai-completions",
|
|
133
|
+
"apiKeyEnv": "OPENAI_API_KEY",
|
|
134
|
+
"enabled": true,
|
|
135
|
+
"id": "prefix-shared-name-aaab-id",
|
|
136
|
+
"name": "prefix-shared-name-aaab",
|
|
137
|
+
"provider": "openai",
|
|
138
|
+
"quotaWeight": 1,
|
|
139
|
+
"reasoning": false,
|
|
140
|
+
"tier": "standard",
|
|
141
|
+
"vision": false
|
|
142
|
+
}
|
|
143
|
+
],
|
|
144
|
+
"roles": {},
|
|
145
|
+
"tierGroups": []
|
|
146
|
+
}
|
|
147
|
+
},
|
|
148
|
+
"warnings": [],
|
|
149
|
+
"metadata": {
|
|
150
|
+
"version": 7,
|
|
151
|
+
"updatedAt": "2026-09-16T00:00:00.000Z"
|
|
152
|
+
},
|
|
153
|
+
"why": "受理:300 字节的模型名与一组同前缀名,读腿不截断不去重;严写同样逐字收下,受理形等于读腿 models 那一格。"
|
|
154
|
+
},
|
|
102
155
|
"wire": {
|
|
103
156
|
"derive": true
|
|
104
157
|
},
|
|
@@ -83,6 +83,11 @@
|
|
|
83
83
|
"updatedAt": "2026-09-16T00:00:00.000Z"
|
|
84
84
|
}
|
|
85
85
|
},
|
|
86
|
+
"strictWrite": {
|
|
87
|
+
"outcome": "rejected",
|
|
88
|
+
"stage": "validate",
|
|
89
|
+
"why": "拒:文档集里有不是域名的键 `web-admin-ui` —— 写入席没有这个域可受理(models / hosts 两份本身是好的,但写是一个动作,一份不受理就整次不受理)。"
|
|
90
|
+
},
|
|
86
91
|
"wire": {
|
|
87
92
|
"derive": true
|
|
88
93
|
},
|
|
@@ -3,6 +3,11 @@
|
|
|
3
3
|
"outcome": "rejected",
|
|
4
4
|
"stage": "validate"
|
|
5
5
|
},
|
|
6
|
+
"strictWrite": {
|
|
7
|
+
"outcome": "rejected",
|
|
8
|
+
"stage": "validate",
|
|
9
|
+
"why": "拒:同一份坏条目在 models(读腿逐条丢)与 rosters(gate 域,读腿整域拒)两处;读腿已整域拒,严写同样不受理 —— 两处任一都够拒。"
|
|
10
|
+
},
|
|
6
11
|
"wire": {
|
|
7
12
|
"outcome": "rejected",
|
|
8
13
|
"stage": "validate",
|
|
@@ -97,6 +97,35 @@
|
|
|
97
97
|
"updatedAt": "2026-09-16T00:00:00.000Z"
|
|
98
98
|
}
|
|
99
99
|
},
|
|
100
|
+
"strictWrite": {
|
|
101
|
+
"outcome": "effective",
|
|
102
|
+
"effective": {
|
|
103
|
+
"models": {
|
|
104
|
+
"models": [
|
|
105
|
+
{
|
|
106
|
+
"api": "openai-completions",
|
|
107
|
+
"apiKeyEnv": "OPENAI_API_KEY",
|
|
108
|
+
"enabled": true,
|
|
109
|
+
"id": "m0-id",
|
|
110
|
+
"name": "m0",
|
|
111
|
+
"provider": "openai",
|
|
112
|
+
"quotaWeight": 1,
|
|
113
|
+
"reasoning": false,
|
|
114
|
+
"tier": "standard",
|
|
115
|
+
"vision": false
|
|
116
|
+
}
|
|
117
|
+
],
|
|
118
|
+
"roles": {},
|
|
119
|
+
"tierGroups": []
|
|
120
|
+
}
|
|
121
|
+
},
|
|
122
|
+
"warnings": [],
|
|
123
|
+
"metadata": {
|
|
124
|
+
"version": 7,
|
|
125
|
+
"updatedAt": "2026-09-16T00:00:00.000Z"
|
|
126
|
+
},
|
|
127
|
+
"why": "受理:`__proto__` / `constructor` / `toString` 作条目内的未知键被 schema 剥掉(读腿报 unknown-keys-dropped);严写同样剥,受理形等于读腿 models 那一格,原型不被污染。"
|
|
128
|
+
},
|
|
100
129
|
"wire": {
|
|
101
130
|
"derive": true
|
|
102
131
|
},
|
|
@@ -99,6 +99,11 @@
|
|
|
99
99
|
"updatedAt": "2026-09-16T00:00:00.000Z"
|
|
100
100
|
}
|
|
101
101
|
},
|
|
102
|
+
"strictWrite": {
|
|
103
|
+
"outcome": "rejected",
|
|
104
|
+
"stage": "validate",
|
|
105
|
+
"why": "拒:models 第 2 条的 api 是一把密钥字面(不在闭集里),读腿逐条丢(entry-dropped)并剥未知键;严写 ⇒ 整次不受理。"
|
|
106
|
+
},
|
|
102
107
|
"wire": {
|
|
103
108
|
"derive": true
|
|
104
109
|
},
|
|
@@ -62,6 +62,70 @@
|
|
|
62
62
|
"updatedAt": "2026-09-16T00:00:00.000Z"
|
|
63
63
|
}
|
|
64
64
|
},
|
|
65
|
+
"strictWrite": {
|
|
66
|
+
"outcome": "effective",
|
|
67
|
+
"effective": {
|
|
68
|
+
"collab": {
|
|
69
|
+
"templates": []
|
|
70
|
+
},
|
|
71
|
+
"entitlement": {
|
|
72
|
+
"budgetGroups": [],
|
|
73
|
+
"groupBindings": [],
|
|
74
|
+
"subjects": [],
|
|
75
|
+
"tiers": []
|
|
76
|
+
},
|
|
77
|
+
"execution": {
|
|
78
|
+
"allowedLanes": [
|
|
79
|
+
"host"
|
|
80
|
+
],
|
|
81
|
+
"required": false
|
|
82
|
+
},
|
|
83
|
+
"governance": {},
|
|
84
|
+
"hosts": {
|
|
85
|
+
"hosts": []
|
|
86
|
+
},
|
|
87
|
+
"limits": {},
|
|
88
|
+
"mcp": {
|
|
89
|
+
"servers": []
|
|
90
|
+
},
|
|
91
|
+
"models": {
|
|
92
|
+
"models": [],
|
|
93
|
+
"roles": {},
|
|
94
|
+
"tierGroups": []
|
|
95
|
+
},
|
|
96
|
+
"plugins": {
|
|
97
|
+
"plugins": []
|
|
98
|
+
},
|
|
99
|
+
"projects": {
|
|
100
|
+
"projects": {}
|
|
101
|
+
},
|
|
102
|
+
"prompts": {
|
|
103
|
+
"bindings": []
|
|
104
|
+
},
|
|
105
|
+
"rosters": {
|
|
106
|
+
"rosters": []
|
|
107
|
+
},
|
|
108
|
+
"runtime": {},
|
|
109
|
+
"scenarios": {
|
|
110
|
+
"scenarios": []
|
|
111
|
+
},
|
|
112
|
+
"skills": {
|
|
113
|
+
"skills": []
|
|
114
|
+
},
|
|
115
|
+
"systems": {
|
|
116
|
+
"systems": []
|
|
117
|
+
},
|
|
118
|
+
"workers": {
|
|
119
|
+
"workers": []
|
|
120
|
+
}
|
|
121
|
+
},
|
|
122
|
+
"warnings": [],
|
|
123
|
+
"metadata": {
|
|
124
|
+
"version": 7,
|
|
125
|
+
"updatedAt": "2026-09-16T00:00:00.000Z"
|
|
126
|
+
},
|
|
127
|
+
"why": "受理:十七个域各一份空对象,读腿零告警;严写逐份受理,受理形 = 十七个域的 schema 默认(只含提交的键 —— 这里恰好十七个都在)。"
|
|
128
|
+
},
|
|
65
129
|
"wire": {
|
|
66
130
|
"derive": true
|
|
67
131
|
},
|
|
@@ -218,6 +218,72 @@
|
|
|
218
218
|
"updatedAt": "2026-09-16T00:00:00.000Z"
|
|
219
219
|
}
|
|
220
220
|
},
|
|
221
|
+
"strictWrite": {
|
|
222
|
+
"outcome": "effective",
|
|
223
|
+
"effective": {
|
|
224
|
+
"collab": {
|
|
225
|
+
"templates": []
|
|
226
|
+
},
|
|
227
|
+
"entitlement": {
|
|
228
|
+
"budgetGroups": [],
|
|
229
|
+
"groupBindings": [],
|
|
230
|
+
"subjects": [],
|
|
231
|
+
"tiers": []
|
|
232
|
+
},
|
|
233
|
+
"execution": {
|
|
234
|
+
"allowedLanes": [
|
|
235
|
+
"host"
|
|
236
|
+
],
|
|
237
|
+
"required": false
|
|
238
|
+
},
|
|
239
|
+
"governance": {},
|
|
240
|
+
"hosts": {
|
|
241
|
+
"hosts": []
|
|
242
|
+
},
|
|
243
|
+
"limits": {
|
|
244
|
+
"zzzUnknown": 1
|
|
245
|
+
},
|
|
246
|
+
"mcp": {
|
|
247
|
+
"servers": []
|
|
248
|
+
},
|
|
249
|
+
"models": {
|
|
250
|
+
"models": [],
|
|
251
|
+
"roles": {},
|
|
252
|
+
"tierGroups": []
|
|
253
|
+
},
|
|
254
|
+
"plugins": {
|
|
255
|
+
"plugins": []
|
|
256
|
+
},
|
|
257
|
+
"projects": {
|
|
258
|
+
"projects": {}
|
|
259
|
+
},
|
|
260
|
+
"prompts": {
|
|
261
|
+
"bindings": []
|
|
262
|
+
},
|
|
263
|
+
"rosters": {
|
|
264
|
+
"rosters": []
|
|
265
|
+
},
|
|
266
|
+
"runtime": {},
|
|
267
|
+
"scenarios": {
|
|
268
|
+
"scenarios": []
|
|
269
|
+
},
|
|
270
|
+
"skills": {
|
|
271
|
+
"skills": []
|
|
272
|
+
},
|
|
273
|
+
"systems": {
|
|
274
|
+
"systems": []
|
|
275
|
+
},
|
|
276
|
+
"workers": {
|
|
277
|
+
"workers": []
|
|
278
|
+
}
|
|
279
|
+
},
|
|
280
|
+
"warnings": [],
|
|
281
|
+
"metadata": {
|
|
282
|
+
"version": 7,
|
|
283
|
+
"updatedAt": "2026-09-16T00:00:00.000Z"
|
|
284
|
+
},
|
|
285
|
+
"why": "受理:十七个域各带一个未知键 —— 十六个被 schema 剥掉(读腿报 unknown-keys-dropped)、limits 承运(unknown-keys-carried);严写同样剥 / 承运、不铸告警,受理形等于读腿十七格。"
|
|
286
|
+
},
|
|
221
287
|
"wire": {
|
|
222
288
|
"derive": true
|
|
223
289
|
},
|
package/dist/cli.js
CHANGED
|
@@ -6,7 +6,8 @@
|
|
|
6
6
|
* 不进主桶 —— 它是 bin 的实现,不是契约面。
|
|
7
7
|
*
|
|
8
8
|
* 退出码:0 = 已写回 / 无事可做;1 = 有 worker 迁不了(refused)或迁出的文档整域读不进 7.0.0,**一字不写**;2 = 用法 / 读文件 /
|
|
9
|
-
* JSON 错(此时 stdout 为空);3 = `--check` 且会有改动(不写)
|
|
9
|
+
* JSON 错(此时 stdout 为空);3 = `--check` 且会有改动(不写)。**8.0.0**:`--check` 与真跑过同一只判定({@link verdictOf}),
|
|
10
|
+
* 只在「有改动、且整域判得过」那一支上分开(pending vs applied)—— 整域会被拒的文档 `--check` 也报 refused / rc 1。
|
|
10
11
|
*
|
|
11
12
|
* ## 报告形(7.0.1:让「没写」在机读上不可能被读成「写了」)
|
|
12
13
|
* 退出码 0 / 1 / 3 时 stdout 恰两行:
|
|
@@ -43,8 +44,41 @@ function writtenOf(outcome) {
|
|
|
43
44
|
}
|
|
44
45
|
}
|
|
45
46
|
}
|
|
47
|
+
/**
|
|
48
|
+
* 迁移结果 → 结论。顺序:逐只 refused → 无改动 ⇒ noop → **整域终判**(迁出来的文档读不读得进 7.0.0:名字 / 路由唯一 …)→ 有改动。
|
|
49
|
+
* 7.0.1 把整域终判放在 `--check` 之后:同一份文档 `--check` 报 pending(rc 3)、真跑报 refused(rc 1),按 `--check` 做闸的脚本会放行
|
|
50
|
+
* 一次必被拒的真跑;而整域终判拒时 `refused[]` 为空、拒因只在 stderr。现在两种跑法过同一只判定,`refused[]` 逐条带出被拒项。
|
|
51
|
+
*/
|
|
52
|
+
function verdictOf(m) {
|
|
53
|
+
if (m.refused.length > 0) {
|
|
54
|
+
return {
|
|
55
|
+
kind: "refused",
|
|
56
|
+
refused: m.refused.map((r) => ({ name: r.name, field: r.field, reason: r.reason })),
|
|
57
|
+
why: `${m.refused.length} worker(s) cannot be migrated (see "refused") — fix them by hand and re-run`,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
if (m.changed.length === 0)
|
|
61
|
+
return { kind: "noop" };
|
|
62
|
+
const whole = DOMAIN_SCHEMAS.workers.safeParse(m.doc);
|
|
63
|
+
if (whole.success)
|
|
64
|
+
return { kind: "changes" };
|
|
65
|
+
const issues = sanitizeZodError(whole.error).issues;
|
|
66
|
+
const workers = isRecord(m.doc) && Array.isArray(m.doc.workers) ? m.doc.workers : [];
|
|
67
|
+
return {
|
|
68
|
+
kind: "refused",
|
|
69
|
+
refused: issues.map((i) => {
|
|
70
|
+
const [head, index, ...rest] = i.path;
|
|
71
|
+
if (head !== "workers" || typeof index !== "number")
|
|
72
|
+
return { name: null, field: i.path, reason: i.message };
|
|
73
|
+
const entry = workers[index];
|
|
74
|
+
return { name: isRecord(entry) && typeof entry.name === "string" ? entry.name : null, field: rest, reason: i.message };
|
|
75
|
+
}),
|
|
76
|
+
why: `the migrated document does not read as a 7.0.0 workers domain — ${issues.map((i) => `${formatConfigPath(i.path)}: ${i.message}`).join("; ")}`,
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
const isRecord = (v) => v !== null && typeof v === "object" && !Array.isArray(v);
|
|
46
80
|
/** 一次发出报告:结论行 + JSON 行(一次 `stdout` 调用)。键名带时态:写了 ⇒ `changed` / `dropped`,没写 ⇒ `wouldChange` / `wouldDrop`。 */
|
|
47
|
-
function reportOf(outcome, file, m) {
|
|
81
|
+
function reportOf(outcome, file, m, refused) {
|
|
48
82
|
const written = writtenOf(outcome);
|
|
49
83
|
const dropped = m.dropped.map((d) => ({ name: d.name, field: formatConfigPath(d.field) }));
|
|
50
84
|
const json = JSON.stringify({
|
|
@@ -53,7 +87,7 @@ function reportOf(outcome, file, m) {
|
|
|
53
87
|
written,
|
|
54
88
|
file,
|
|
55
89
|
...(written ? { changed: m.changed, dropped } : { wouldChange: m.changed, wouldDrop: dropped }),
|
|
56
|
-
refused:
|
|
90
|
+
refused: refused.map((r) => ({ name: r.name, field: formatConfigPath(r.field), reason: r.reason })),
|
|
57
91
|
});
|
|
58
92
|
return `outcome=${outcome} written=${written}\n${json}\n`;
|
|
59
93
|
}
|
|
@@ -83,28 +117,26 @@ export async function runSettingsSchemaCli(argv, io) {
|
|
|
83
117
|
return 2;
|
|
84
118
|
}
|
|
85
119
|
const m = migrateWorkersDomain(doc);
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
120
|
+
const verdict = verdictOf(m);
|
|
121
|
+
switch (verdict.kind) {
|
|
122
|
+
case "refused":
|
|
123
|
+
io.stdout(reportOf("refused", file, m, verdict.refused));
|
|
124
|
+
io.stderr(`settings-schema: ${verdict.why}; nothing was written\n`);
|
|
125
|
+
return 1;
|
|
126
|
+
case "noop":
|
|
127
|
+
io.stdout(reportOf("noop", file, m, []));
|
|
128
|
+
return 0;
|
|
129
|
+
case "changes":
|
|
130
|
+
if (check) {
|
|
131
|
+
io.stdout(reportOf("pending", file, m, []));
|
|
132
|
+
return 3;
|
|
133
|
+
}
|
|
134
|
+
await io.writeFileAtomic(file, `${JSON.stringify(m.doc, null, 2)}\n`);
|
|
135
|
+
io.stdout(reportOf("applied", file, m, [])); // 写盘成功之后才报 applied
|
|
136
|
+
return 0;
|
|
137
|
+
default: {
|
|
138
|
+
const unhandled = verdict;
|
|
139
|
+
throw new Error(`settings-schema: unhandled verdict ${JSON.stringify(unhandled)}`);
|
|
140
|
+
}
|
|
106
141
|
}
|
|
107
|
-
await io.writeFileAtomic(file, `${JSON.stringify(m.doc, null, 2)}\n`);
|
|
108
|
-
io.stdout(reportOf("applied", file, m)); // 写盘成功之后才报 applied
|
|
109
|
-
return 0;
|
|
110
142
|
}
|
package/dist/config-fns.js
CHANGED
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
import { z } from "zod";
|
|
19
19
|
import { MODEL_ENTRY_REFUSAL_CODES, ModelRefusalsWire } from "./fleet.js";
|
|
20
20
|
import { carriesUserinfo, secretScanForms } from "./secret-scan.js"; // package-private display-side normalization + the userinfo judgement (not in the barrel)
|
|
21
|
-
import { DOMAIN_SCHEMAS, GOVERNANCE_MIRROR_KEYS, HOSTS_ARRAY_MAX, HOST_NAME_MAX, InfraCostRates, LimitsConfig, PRIVATE_KEY_PEM_HEADER, SkillsManifestSchema, containsSecretToken } from "./types.js";
|
|
21
|
+
import { DOMAIN_SCHEMAS, GOVERNANCE_MIRROR_KEYS, HOSTS_ARRAY_MAX, HOST_NAME_MAX, InfraCostRates, LimitsConfig, PRIVATE_KEY_PEM_HEADER, SkillsManifestSchema, containsSecretToken, isDomainName } from "./types.js";
|
|
22
22
|
/** Redact secret-shaped tokens from a validation MESSAGE — a zod error echoes the offending value (e.g.
|
|
23
23
|
* `received 'sk-live-…'`), so a secret pasted into the WRONG field would leak via the error string. Redacts
|
|
24
24
|
* space-spanning auth credentials (`Bearer <token>`) FIRST (the token pass can't see across the space), then
|
|
@@ -134,6 +134,12 @@ export function emptyEffective() {
|
|
|
134
134
|
}
|
|
135
135
|
/** Validate + fill defaults for a domain payload (or the schema's empty default when absent/invalid). */
|
|
136
136
|
export function parseDomain(domain, raw) {
|
|
137
|
+
// A name that is not a domain is refused for what it is — before, `DOMAIN_SCHEMAS[name]` was undefined and the
|
|
138
|
+
// TypeError surfaced through the guard below as "a value could not be read", which names the wrong failure. The
|
|
139
|
+
// strict-write face hands this function every key of a submitted document set, so the name is user text: it leaves
|
|
140
|
+
// through the same outgoing redaction as a warning coordinate segment.
|
|
141
|
+
if (!isDomainName(domain))
|
|
142
|
+
throw new Error(`"${outgoingSegment(String(domain))}" is not a config domain`);
|
|
137
143
|
// ONLY true absence (undefined) defaults; an explicit `null` (e.g. a `config.d/<d>.json` containing JSON
|
|
138
144
|
// `null`) is corrupt input → let zod reject it ("expected object, received null"), not silently default. R4.
|
|
139
145
|
let r;
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import type { ConformanceAdapter, ConformanceProvenance, LegEntry, LegName } from "./types.js";
|
|
2
|
-
/** 一份契约模块(新旧版同形 ——
|
|
2
|
+
/** 一份契约模块(新旧版同形 —— 四条腿的签名自 0.19.0 起没动过)。 */
|
|
3
3
|
export interface ContractModule {
|
|
4
4
|
buildEffective(version: number, getRaw: (d: string) => unknown, updatedAt: string, onWarning?: (w: unknown) => void): unknown;
|
|
5
|
+
/** 严写受理的生产入口:一个域名 + 一份原始文档 → 受理后的域文档,不受理就抛。 */
|
|
6
|
+
parseDomain(domain: string, raw: unknown): unknown;
|
|
5
7
|
readEffectiveWire(raw: unknown, onWarning?: (w: unknown) => void): unknown;
|
|
6
8
|
loadLocalConfig(root: string, opts?: {
|
|
7
9
|
onWarning?: (w: unknown) => void;
|
|
@@ -14,7 +16,7 @@ export interface ContractAdapterOptions {
|
|
|
14
16
|
name: string;
|
|
15
17
|
provenance: ConformanceProvenance;
|
|
16
18
|
mod: ContractModule;
|
|
17
|
-
/**
|
|
19
|
+
/** 四条腿各自的生产入口坐标(绝对路径)。 */
|
|
18
20
|
entries: Record<LegName, LegEntry>;
|
|
19
21
|
}
|
|
20
22
|
export declare function createContractAdapter(opts: ContractAdapterOptions): ConformanceAdapter;
|
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* 契约仓自己的四腿适配器 —— 也是 `historical` 模式拿旧版包跑时用的同一只工厂。
|
|
3
3
|
*
|
|
4
4
|
* **它不是给消费方抄的**:消费方登记的腿必须是**自己生产代码**的入口(来源律,§2d-2);把契约包的函数
|
|
5
|
-
*
|
|
6
|
-
* `buildEffective` / `readEffectiveWire` / `loadLocalConfig` **就是**它的生产入口);②historical 模式要对
|
|
7
|
-
* 一份旧版 dist
|
|
5
|
+
* 注册成腿,门证明的就只是「包等于包」。本工厂存在的理由只有两个:①契约仓自证四腿(对契约仓而言
|
|
6
|
+
* `buildEffective` / `parseDomain` / `readEffectiveWire` / `loadLocalConfig` **就是**它的生产入口);②historical 模式要对
|
|
7
|
+
* 一份旧版 dist 跑同样的四腿。
|
|
8
|
+
*
|
|
9
|
+
* `strictWrite`(严写受理)= 文档集里**每一个**键各自过 `parseDomain`(写入席收下的那份);任何一份不受理 ⇒ 整次
|
|
10
|
+
* `rejected{stage:"validate"}`(写是一个动作,不是逐份抢救);全部受理 ⇒ `effective` = 键 → 受理后的域文档(只含入参里的
|
|
11
|
+
* 键,不补缺席域)。`parseDomain` 不铸告警,所以这条腿的告警面恒空。
|
|
8
12
|
*
|
|
9
13
|
* 4.0.0 起这句自陈**有牙**:入口落在契约包自己身上 ⇒ 造适配器那一刻就**拒**(抛,不是告警 —— 一条告警
|
|
10
14
|
* 在 CI 日志里活不过第二天,而这道门一旦被同义反复接进去,它报的全绿是**假绿**)。判别式单铸在
|
|
@@ -13,7 +17,7 @@
|
|
|
13
17
|
*
|
|
14
18
|
* 拒绝分层的判据(一条,不是一串特例):**ZodError 形的错 = `validate`**(某个域没通过它的 schema);
|
|
15
19
|
* 其余按腿各自的默认层 —— wire 腿是 `envelope`(它在解析任何域之前先判信封),local-root 腿是 `parse`
|
|
16
|
-
* (文件层先判:空文件 / 非 JSON / 非对象),domains
|
|
20
|
+
* (文件层先判:空文件 / 非 JSON / 非对象),domains 与 strictWrite 两腿本来就只有校验这一层。
|
|
17
21
|
*/
|
|
18
22
|
import { materializeLocalRoot } from "./run.js";
|
|
19
23
|
import { contractOwnedEntry, contractOwnedEntryWhy } from "./entry-origin.js";
|
|
@@ -29,7 +33,7 @@ const reject = (e, fallback) => {
|
|
|
29
33
|
return { outcome: "rejected", stage, diagnostics };
|
|
30
34
|
};
|
|
31
35
|
/**
|
|
32
|
-
* 入口的**运行期拒**(fail-closed)
|
|
36
|
+
* 入口的**运行期拒**(fail-closed):四条腿的入口一条都不许落在**这份正在跑的**契约包自己的构建产物里,
|
|
33
37
|
* 缺席也不行。判别式与拒绝句来自 `./entry-origin.js`,这里只负责「拒」这个动作 —— 以及**按形分派**:
|
|
34
38
|
* 工厂看不见 `mode`,所以它只拒在任何模式下都错的那一形(`own-build-output` = 拿自己比自己);
|
|
35
39
|
* `another-installed-copy` 在 `historical` 模式下是正当的(那个模式就是拿旧版包比行为,而旧版包常常正好
|
|
@@ -40,7 +44,7 @@ function refuseContractOwnedEntries(entries) {
|
|
|
40
44
|
for (const leg of LEG_NAMES) {
|
|
41
45
|
const entry = entries[leg];
|
|
42
46
|
if (entry === undefined || typeof entry.module !== "string" || entry.module.length === 0) {
|
|
43
|
-
throw new Error(`conformance: leg \`${leg}\` 缺 entry.module ——
|
|
47
|
+
throw new Error(`conformance: leg \`${leg}\` 缺 entry.module —— 每条腿各自的**生产入口**坐标是必填的(来源律 §2d-2)`);
|
|
44
48
|
}
|
|
45
49
|
const owned = contractOwnedEntry(entry.module);
|
|
46
50
|
if (owned === undefined)
|
|
@@ -72,6 +76,15 @@ export function createContractAdapter(opts) {
|
|
|
72
76
|
catch (e) {
|
|
73
77
|
return reject(e, "validate");
|
|
74
78
|
}
|
|
79
|
+
case "strictWrite":
|
|
80
|
+
try {
|
|
81
|
+
// fromEntries(不是逐键赋值):文档集的键是用户写的,`__proto__` 作键时逐键赋值会走原型 setter。
|
|
82
|
+
const accepted = Object.fromEntries(Object.entries(input.docs).map(([d, raw]) => [d, mod.parseDomain(d, raw)]));
|
|
83
|
+
return { outcome: "effective", effective: accepted, warnings: [], version: input.version, updatedAt: input.updatedAt };
|
|
84
|
+
}
|
|
85
|
+
catch (e) {
|
|
86
|
+
return reject(e, "validate");
|
|
87
|
+
}
|
|
75
88
|
case "wire":
|
|
76
89
|
try {
|
|
77
90
|
const effective = mod.readEffectiveWire(input.payload, onWarning);
|
|
@@ -104,6 +117,7 @@ export function createContractAdapter(opts) {
|
|
|
104
117
|
provenance: opts.provenance,
|
|
105
118
|
legs: {
|
|
106
119
|
domains: { entry: opts.entries.domains, run },
|
|
120
|
+
strictWrite: { entry: opts.entries.strictWrite, run },
|
|
107
121
|
wire: { entry: opts.entries.wire, run },
|
|
108
122
|
localRoot: { entry: opts.entries.localRoot, run },
|
|
109
123
|
},
|
|
@@ -6,8 +6,9 @@
|
|
|
6
6
|
* · `input.json` —— **真源是 `domains`**(各域原始文档);`wire` / `local-root` 两腿的入参由
|
|
7
7
|
* `derive.ts` 的变换表派生。个别腿的入参派生不出来(坏 wire 信封、空文件)时,
|
|
8
8
|
* 用 `wirePayload` / `localFiles` 显式给。
|
|
9
|
-
* · `expected.json` ——
|
|
10
|
-
*
|
|
9
|
+
* · `expected.json` —— 每条腿一格(四条腿一条不少)。`domains` 与 `strictWrite` 两腿的期望恒是**显式 golden**
|
|
10
|
+
* (能不能派生只看 `derive.ts` 的 `LEG_EXPECTATION`);`wire` / `local-root` 默认写 `{"derive":true}`,
|
|
11
|
+
* 由变换表派生;派生解释不了的腿写显式期望并带 `why`(那是明示声明,不是暗门)。
|
|
11
12
|
* · `meta.json` —— `contributor`(**只写真交了的仓**)、`claim`(这条想打谁、为什么会读错)、
|
|
12
13
|
* `secrets`(语料里埋的秘密字面,告警面零命中门用)、`firstRedOn`(**由 historical
|
|
13
14
|
* 模式对旧版真跑填**,不许手写)。
|
|
@@ -17,7 +18,8 @@
|
|
|
17
18
|
import { readFile, readdir } from "node:fs/promises";
|
|
18
19
|
import { dirname, join } from "node:path";
|
|
19
20
|
import { fileURLToPath } from "node:url";
|
|
20
|
-
import { REJECT_STAGES } from "./types.js";
|
|
21
|
+
import { LEG_NAMES, REJECT_STAGES } from "./types.js";
|
|
22
|
+
import { LEG_EXPECTATION } from "./derive.js";
|
|
21
23
|
/** 本包的根目录 —— `src/conformance/*.ts` 与 `dist/conformance/*.js` 距根同深,两处求值同一个地方。 */
|
|
22
24
|
export const PACKAGE_ROOT = join(dirname(fileURLToPath(import.meta.url)), "..", "..");
|
|
23
25
|
/** 语料目录(默认 = 本包的 `conformance/`)。 */
|
|
@@ -72,13 +74,16 @@ export async function loadCase(dir, id) {
|
|
|
72
74
|
if (!Array.isArray(meta.firstRedOn))
|
|
73
75
|
throw new Error(`conformance corpus: ${id}/meta.json#firstRedOn must be an array`);
|
|
74
76
|
const legs = {};
|
|
75
|
-
for (const leg of
|
|
77
|
+
for (const leg of LEG_NAMES) {
|
|
76
78
|
if (expected[leg] === undefined)
|
|
77
79
|
throw new Error(`conformance corpus: ${id}/expected.json has no \`${leg}\` — 每腿都要表态(派生 / 显式 / 不适用)`);
|
|
78
|
-
|
|
80
|
+
const spec = checkLegExpectation(expected[leg], `${id}/expected.json#${leg}`);
|
|
81
|
+
// 能不能派生只看一张表(derive.ts 的 LEG_EXPECTATION),不按腿名特判。
|
|
82
|
+
if ("derive" in spec && LEG_EXPECTATION[leg].source === "explicit") {
|
|
83
|
+
throw new Error(`conformance corpus: ${id}/expected.json#${leg} cannot be derived — ${LEG_EXPECTATION[leg].why}`);
|
|
84
|
+
}
|
|
85
|
+
legs[leg] = spec;
|
|
79
86
|
}
|
|
80
|
-
if ("derive" in legs.domains)
|
|
81
|
-
throw new Error(`conformance corpus: ${id}/expected.json#domains cannot be derived — domains 腿是真源`);
|
|
82
87
|
return { id, dir, input: input, meta: meta, expected: legs };
|
|
83
88
|
}
|
|
84
89
|
/** 整套语料。 */
|
|
@@ -9,6 +9,32 @@ export interface DerivationTransform {
|
|
|
9
9
|
}
|
|
10
10
|
/** 变换表(本文件头注的机器形;`docs/CONFORMANCE.md` 与 `test/conformance-coverage.test.ts` 都读它)。 */
|
|
11
11
|
export declare const DERIVATION_TRANSFORMS: readonly DerivationTransform[];
|
|
12
|
+
/**
|
|
13
|
+
* 每条腿的期望从哪来 —— 闭集表态(`Record<LegName, …>`:新开一条腿不在这里表态 ⇒ 编译红)。
|
|
14
|
+
*
|
|
15
|
+
* · `derivable`:语料可以写 `{"derive":true}`,由上面的变换表从 `domains` 期望派生(也可写显式期望并带 `why`)。
|
|
16
|
+
* · `explicit`:只收显式金样(`{"derive":true}` 加载即拒)。
|
|
17
|
+
*
|
|
18
|
+
* 语料加载器与驱动都读这张表(一条规则),不再各自特判某个腿名。
|
|
19
|
+
*/
|
|
20
|
+
export declare const LEG_EXPECTATION: {
|
|
21
|
+
readonly domains: {
|
|
22
|
+
readonly source: "explicit";
|
|
23
|
+
readonly why: "真源:domains 腿的期望就是金样本身";
|
|
24
|
+
};
|
|
25
|
+
readonly strictWrite: {
|
|
26
|
+
readonly source: "explicit";
|
|
27
|
+
readonly why: "入参与 domains 恒等,期望不派生:严写结局不是读结局的表示变换(读腿抢救处严写拒);从 domains 期望派生会让一份金样的错同时改写两条腿";
|
|
28
|
+
};
|
|
29
|
+
readonly wire: {
|
|
30
|
+
readonly source: "derivable";
|
|
31
|
+
readonly why: "T1 / T2 / T4";
|
|
32
|
+
};
|
|
33
|
+
readonly localRoot: {
|
|
34
|
+
readonly source: "derivable";
|
|
35
|
+
readonly why: "T3 / T4 / T5 / T6";
|
|
36
|
+
};
|
|
37
|
+
};
|
|
12
38
|
/** 非便携域 —— 由 {@link PORTABLE_DOMAINS} 派生,零手抄。 */
|
|
13
39
|
export declare const NON_PORTABLE_DOMAINS: readonly DomainName[];
|
|
14
40
|
/** 一份 `domains` 文档集里,**不是域名**的那些顶层键(T4 的触发面)。 */
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
* 与**期望**都由本文件派生 ——
|
|
2
|
+
* **读腿等价先定义再比**:语料以 `domains`(各域原始文档)为真源,`wire` 与 `local-root` 两腿的**入参**
|
|
3
|
+
* 与**期望**都由本文件派生 —— 派生规则就是三条读腿之间那几处**表示差异**的完整登记表(第四条腿
|
|
4
|
+
* `strictWrite` 的立场见下文与 {@link LEG_EXPECTATION})。
|
|
4
5
|
*
|
|
5
6
|
* 为什么派生期望而不是每腿各写一份 golden:三份手写 golden 之间的关系是**隐含**的,谁也说不清某一处
|
|
6
7
|
* 不同是「设计如此」还是「某一腿读错了」。派生把那层关系变成代码:凡不能由登记的变换解释的差异,一定
|
|
@@ -27,6 +28,11 @@
|
|
|
27
28
|
*
|
|
28
29
|
* 语料可以对某一腿写**显式期望**(带 `why`)来覆盖派生 —— 那是「这条 case 的这条腿不由变换解释」的
|
|
29
30
|
* 明示声明,不是一条暗门。
|
|
31
|
+
*
|
|
32
|
+
* **每条腿的期望从哪来**是一张闭集表({@link LEG_EXPECTATION},`Record<LegName, …>` —— 新开一条腿不来这里
|
|
33
|
+
* 表态就编译红)。`strictWrite`(严写受理)的**入参**与 `domains` 恒等(同一份各域原始文档),**期望不派生**:
|
|
34
|
+
* 严写的结局不是读结局的一种表示变换,而是另一个判断(读腿抢救的地方严写拒)。把它写成「从 domains 期望派生」
|
|
35
|
+
* 就等于让 domains 金样的一处错同时改写两条腿的期望 —— 两条腿同错、门照绿。它的金样逐条显式入库、各自冻结。
|
|
30
36
|
*/
|
|
31
37
|
import { NON_DOMAIN_CONFIG_FILES, PORTABLE_DOMAINS } from "../file-store.js";
|
|
32
38
|
import { skillContentHash } from "../skills-manifest.js";
|
|
@@ -40,6 +46,23 @@ export const DERIVATION_TRANSFORMS = [
|
|
|
40
46
|
{ id: "T5-file-layer-first", legs: ["localRoot"], what: "config.d/<域>.json 必须是 JSON 对象(空/null/数组/标量 throw),这一层在域解析之前" },
|
|
41
47
|
{ id: "T6-file-name-classification", legs: ["localRoot"], what: "config.d/ 的文件分三类:域文件 / NON_DOMAIN_CONFIG_FILES(remote-exec.json 自带读取与校验路径)/ 隐藏文件与非 .json(忽略);只有剩下的才报 not-a-domain" },
|
|
42
48
|
];
|
|
49
|
+
/**
|
|
50
|
+
* 每条腿的期望从哪来 —— 闭集表态(`Record<LegName, …>`:新开一条腿不在这里表态 ⇒ 编译红)。
|
|
51
|
+
*
|
|
52
|
+
* · `derivable`:语料可以写 `{"derive":true}`,由上面的变换表从 `domains` 期望派生(也可写显式期望并带 `why`)。
|
|
53
|
+
* · `explicit`:只收显式金样(`{"derive":true}` 加载即拒)。
|
|
54
|
+
*
|
|
55
|
+
* 语料加载器与驱动都读这张表(一条规则),不再各自特判某个腿名。
|
|
56
|
+
*/
|
|
57
|
+
export const LEG_EXPECTATION = {
|
|
58
|
+
domains: { source: "explicit", why: "真源:domains 腿的期望就是金样本身" },
|
|
59
|
+
strictWrite: {
|
|
60
|
+
source: "explicit",
|
|
61
|
+
why: "入参与 domains 恒等,期望不派生:严写结局不是读结局的表示变换(读腿抢救处严写拒);从 domains 期望派生会让一份金样的错同时改写两条腿",
|
|
62
|
+
},
|
|
63
|
+
wire: { source: "derivable", why: "T1 / T2 / T4" },
|
|
64
|
+
localRoot: { source: "derivable", why: "T3 / T4 / T5 / T6" },
|
|
65
|
+
};
|
|
43
66
|
/** 非便携域 —— 由 {@link PORTABLE_DOMAINS} 派生,零手抄。 */
|
|
44
67
|
export const NON_PORTABLE_DOMAINS = DOMAINS.filter((d) => !PORTABLE_DOMAINS.includes(d));
|
|
45
68
|
const isPlainObject = (v) => v !== null && typeof v === "object" && !Array.isArray(v);
|
|
@@ -6,7 +6,8 @@
|
|
|
6
6
|
*
|
|
7
7
|
* 三步接线(消费方侧):
|
|
8
8
|
* 1. 造一个 {@link ConformanceAdapter}:`provenance` = 你**实际加载到**的那份契约包(package.json 路径、
|
|
9
|
-
* 版号、完整性串);`legs` =
|
|
9
|
+
* 版号、完整性串);`legs` = **四条腿逐条表态**:你自己的生产入口(每条带 `entry{module,export}`,生产入口的
|
|
10
|
+
* 绝对路径),或 `{ declined: "no-production-path", why }`(你的生产代码结构上没有这种读法 / 写法)。
|
|
10
11
|
* 2. `const report = await runConformance(adapter, { mode: "release" })`。
|
|
11
12
|
* 3. 断言 `report.ok`;不绿就读 `report.violations` 与每格的 `diff`(effective 的 JSON patch、告警多重集差、
|
|
12
13
|
* 元数据差三面分开)。
|
|
@@ -16,7 +17,7 @@
|
|
|
16
17
|
export { runConformance, materializeLocalRoot, resolveLegExpectation } from "./run.js";
|
|
17
18
|
export { createContractAdapter, type ContractAdapterOptions, type ContractModule } from "./contract-adapter.js";
|
|
18
19
|
export { loadCorpus, loadCase, kitIdentity, DEFAULT_CORPUS_DIR, PACKAGE_ROOT, type Corpus, type ConformanceCase, type CaseInput, type CaseMeta, type LegExpectationJson } from "./corpus.js";
|
|
19
|
-
export { DERIVATION_TRANSFORMS, NON_PORTABLE_DOMAINS, EPOCH_ISO, deriveWireInput, deriveLocalInput, deriveWireExpectation, deriveLocalExpectation, foreignKeysOf, toManifestDoc, type DerivationTransform, type LegExpectation, } from "./derive.js";
|
|
20
|
+
export { DERIVATION_TRANSFORMS, LEG_EXPECTATION, NON_PORTABLE_DOMAINS, EPOCH_ISO, deriveWireInput, deriveLocalInput, deriveWireExpectation, deriveLocalExpectation, foreignKeysOf, toManifestDoc, type DerivationTransform, type LegExpectation, } from "./derive.js";
|
|
20
21
|
export { canonicalize, deepEqual, diffJson, stableKey, type JsonPatchOp } from "./canonical.js";
|
|
21
22
|
export { WARNING_KINDS, projectWarning, normalizeProjection, projectionKey, projectionValue, diffWarningMultiset, findSecretLeaks, type WarningKind, type WarningProjection, } from "./warnings.js";
|
|
22
|
-
export { LEG_NAMES, REJECT_STAGES, type ConformanceAdapter, type ConformanceLeg, type ConformanceMode, type ConformanceNormalizer, type ConformanceOptions, type ConformanceProvenance, type ConformanceReport, type ConformanceResult, type ConformanceStatus, type KnownDivergence, type OutcomeDivergence, type PathDivergence, type LedgerRow, type LegEntry, type LegInput, type LegName, type LegOutcome, type RejectStage, } from "./types.js";
|
|
23
|
+
export { LEG_DECLINE_REASONS, LEG_NAMES, REJECT_STAGES, type ConformanceAdapter, type ConformanceLeg, type DomainDocsInput, type LegDeclineReason, type LegDeclined, type LegStance, type ConformanceMode, type ConformanceNormalizer, type ConformanceOptions, type ConformanceProvenance, type ConformanceReport, type ConformanceResult, type ConformanceStatus, type KnownDivergence, type OutcomeDivergence, type PathDivergence, type LedgerRow, type LegEntry, type LegInput, type LegName, type LegOutcome, type RejectStage, } from "./types.js";
|