@sema-agent/settings-schema 3.0.0 → 4.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 +327 -1
- package/README.md +16 -8
- package/conformance/VERSION +1 -1
- package/conformance/cases/t01-absent-domain-key-not-null/expected.json +93 -0
- package/conformance/cases/t01-absent-domain-key-not-null/input.json +29 -0
- package/conformance/cases/t01-absent-domain-key-not-null/meta.json +6 -0
- package/conformance/cases/t02-entry-salvage-vs-whole-domain-refusal/expected.json +16 -0
- package/conformance/cases/t02-entry-salvage-vs-whole-domain-refusal/input.json +32 -0
- package/conformance/cases/t02-entry-salvage-vs-whole-domain-refusal/meta.json +6 -0
- package/conformance/cases/t03-prototype-name-key-not-swallowed/expected.json +107 -0
- package/conformance/cases/t03-prototype-name-key-not-swallowed/input.json +25 -0
- package/conformance/cases/t03-prototype-name-key-not-swallowed/meta.json +6 -0
- package/conformance/cases/t04-secret-literal-never-in-warning-value/expected.json +105 -0
- package/conformance/cases/t04-secret-literal-never-in-warning-value/input.json +25 -0
- package/conformance/cases/t04-secret-literal-never-in-warning-value/meta.json +14 -0
- package/conformance/historical/settings-schema@1.0.0.json +84 -3
- package/conformance/historical/settings-schema@1.10.0.json +84 -3
- package/conformance/historical/settings-schema@1.5.0.json +84 -3
- package/conformance/historical/settings-schema@2.0.0.json +83 -2
- package/conformance/historical/settings-schema@2.1.0.json +83 -2
- package/dist/api/auth.d.ts +21 -0
- package/dist/api/auth.js +21 -0
- package/dist/config-fns.d.ts +76 -11
- package/dist/config-fns.js +81 -26
- package/dist/conformance/contract-adapter.js +37 -0
- package/dist/conformance/entry-origin.d.ts +24 -0
- package/dist/conformance/entry-origin.js +86 -0
- package/dist/conformance/run.js +6 -6
- package/dist/conformance/types.d.ts +5 -1
- package/dist/conformance/warnings.d.ts +7 -48
- package/dist/conformance/warnings.js +17 -40
- package/dist/cross-domain.d.ts +27 -7
- package/dist/cross-domain.js +31 -11
- package/dist/hooks.d.ts +2 -2
- package/dist/hooks.js +2 -2
- package/dist/node.d.ts +1 -0
- package/dist/node.js +1 -0
- package/dist/prompts-digest.d.ts +16 -0
- package/dist/prompts-digest.js +48 -0
- package/dist/scheduler-store-node.d.ts +7 -19
- package/dist/scheduler-store-node.js +8 -26
- package/dist/scheduler-store.d.ts +1 -1
- package/dist/scheduler-store.js +1 -1
- package/dist/types.d.ts +2 -2
- package/dist/types.js +3 -3
- package/package.json +3 -1
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,332 @@
|
|
|
6
6
|
> `@sema-agent/registry-core`(更早还曾是 `@sema-ai/registry-core`)。本档 `0.19.0` 以下各节里的
|
|
7
7
|
> 包名是**历史记录**,如实保留不追改。
|
|
8
8
|
|
|
9
|
+
## 4.0.0 — 2026-09-22 — 引用完整性的**域闭集发成运行期值**(additive)+ `RefIssue.path` 六臂坐标**统一成以域键起手**(BREAKING,值形)+ 诊断面回**真实总数**(BREAKING,返回形)+ 告警投影改**机器派生**并搬回属主(BREAKING,模块面)+ `fileSchedulerStore` → `createFileSchedulerStore`(BREAKING,重命名,零别名)+ 一致性门来源律改**运行期拒**+ prompts 摘要搬出调度存储并合并铸点 + 四道机器门
|
|
10
|
+
|
|
11
|
+
> **版号裁定(发布方)**:本批两件里有一件是**值形**破坏性改动 —— `refIntegrityIssues` 返回的
|
|
12
|
+
> `RefIssue.path` 在 `collab` / `entitlement` 两臂上换了首段。照本档下方「发版纪律」的「**改形 = major**」
|
|
13
|
+
> 判 **4.0.0**。**零别名、零双读、零过渡方法**:值形改动不会让消费方 tsc 红(`path` 的类型一字未变),
|
|
14
|
+
> 所以下方迁移句给出**可 grep 的旧形特征**与逐处坐标——这是本批唯一需要人看一眼的地方。
|
|
15
|
+
> 持久数据面**零影响**:不碰任何存储形,`config.d/*.json` 与 `/effective` 载荷一字未变。
|
|
16
|
+
|
|
17
|
+
**三问**:
|
|
18
|
+
- **谁需要** = ① 任何在**运行期**问「这个域要不要过引用完整性闸」的消费方:闭集以前只活在类型里
|
|
19
|
+
(`RefCheckedDomain` 是一条手写联合,运行期没有值),于是每个消费方只能自己抄一份域名清单,而手抄的
|
|
20
|
+
清单会静默漂 —— 管理台的两条回滚路由各抄了一份**少 `entitlement`** 的五员集合
|
|
21
|
+
(`sema-web-admin` `src/app/api/config/rollback/route.ts:18` 的 `REF_CHECKED` Set 与
|
|
22
|
+
`src/app/api/v1/scopes/[scope]/rollback/route.ts:89` 的行内联合),`entitlement` 域的回滚因此整条**绕过**
|
|
23
|
+
引用完整性闸,而全舰队没有一格红;② 拿 `path` 拼坐标的写腿:六臂两形,`path[0]` 读不出是哪个域。
|
|
24
|
+
- **谁受伤** = 两个消费方:管理台(`sema-web-admin`)与 worker 服务(`sema-server`)。逐只亲读实测
|
|
25
|
+
(`refIntegrityIssues`:**管理台 17**(`src/`;另 `test/` 5)、**worker 服务 2**、CLI 0、client SDK 0、
|
|
26
|
+
SDK / 桌面 / web 客户端 / 测试仓各 0;`RefIssue` 与 `RefCheckedDomain` 各**只**管理台 1 处 ——
|
|
27
|
+
`src/lib/config/cross-domain.ts:8` 的再导出)。
|
|
28
|
+
worker 服务那 2 处是**真生产调用**,不是注释:`src/config-provider.ts:45` 的 import 与 `:272` 的调用,
|
|
29
|
+
本地读腿每次 `fetchEffective` 都会跑,并且它 ①`:271` 自己手抄了一份六员域名清单(第三份手抄闭集,
|
|
30
|
+
改调 `REF_CHECKED_DOMAINS` 即可退役)、②`:274` 用 `i.path.join(".")` 拼运维可见的
|
|
31
|
+
`domainErrors[].error`。所以 4.0.0 之后它那条串对 `collab` / `entitlement` 两域会多一段域键前缀
|
|
32
|
+
(`tiers.0.roster` → `entitlement.tiers.0.roster`);它现有的钉只覆盖 `rosters` 臂
|
|
33
|
+
(`test/config-provider.test.ts:473` 与 `:509` 各钉一次 `"rosters.0.models.0"`,该臂本批不变)⇒
|
|
34
|
+
**提货不会红**,但那段串是运维面,值得知道它对那两个域变长了一段。
|
|
35
|
+
- **什么补偿** = 闭集给的是**编译期 + 运行期**双可用的替代品(常量 + 判别式),不是「文档里说一声」;
|
|
36
|
+
值形改动给可 grep 的旧形特征 + 逐处坐标 + 一行改法。
|
|
37
|
+
|
|
38
|
+
### ① 域闭集发成运行期值(additive,不 breaking)
|
|
39
|
+
|
|
40
|
+
`./cross-domain` 新增两个导出,`RefCheckedDomain` 改为**从值派生**:
|
|
41
|
+
|
|
42
|
+
- `REF_CHECKED_DOMAINS`:六员闭集常量,`as const satisfies readonly (keyof DomainConfig)[]` —— 每一员都
|
|
43
|
+
被编译期钉成真域键。
|
|
44
|
+
- `type RefCheckedDomain = (typeof REF_CHECKED_DOMAINS)[number]`:同一个集合**只写一处**;此前它是常量
|
|
45
|
+
旁边的一条手写联合,两份可以各说各话。
|
|
46
|
+
- `isRefCheckedDomain(d: unknown): d is RefCheckedDomain`:运行期判别式(与 `isDomainName` 同款),
|
|
47
|
+
消费方据此**删掉自己那份手抄清单**。
|
|
48
|
+
|
|
49
|
+
`refIntegrityIssues` 尾部那道穷举守卫(`const unhandled: never = domain`)原样保留,并复核仍咬得住:
|
|
50
|
+
闭集加第七员而不加分派支 ⇒ `tsc` 报 `error TS2322: Type '"<新域>"' is not assignable to type 'never'`。
|
|
51
|
+
|
|
52
|
+
### ② `RefIssue.path` 六臂统一(BREAKING,值形)
|
|
53
|
+
|
|
54
|
+
`refIntegrityIssues` 的头注一直写着「Paths start with the domain key」,而其中两臂**不是**:
|
|
55
|
+
|
|
56
|
+
| 域 | 4.0.0 之前 | 4.0.0 |
|
|
57
|
+
| --- | --- | --- |
|
|
58
|
+
| `rosters` / `workers` / `models` / `systems` | `["rosters", …]` / `["workers", …]` / `["models", …]` / `["systems", …]` | 不变 |
|
|
59
|
+
| `collab` | `["templates", i, "slots", j, "members", k, "modelRef"]` | `["collab", "templates", i, …]` |
|
|
60
|
+
| `entitlement` | `["tiers", i, …]`(`roster` / `models` / `visibleSkills` / `visibleMcp` / `visiblePlugins` 五处) | `["entitlement", "tiers", i, …]` |
|
|
61
|
+
|
|
62
|
+
于是 `path[0]` **恒等于**被校验的那个域,那句头注第一次成为真话。
|
|
63
|
+
|
|
64
|
+
**迁移说明(管理台)**:
|
|
65
|
+
|
|
66
|
+
1. **批量写腿:本批把它从「两源两形」修回一致,不需要改。** `src/lib/config/apply-batch.ts` 对 zod 问题
|
|
67
|
+
拼的是 `[index, c.domain, ...iss.path]`(:63),对引用完整性问题拼的是 `[index, ...ri.path]`(:83)——
|
|
68
|
+
同一个 409 响应里两种坐标空间,而缺域键的恰好是后者。4.0.0 起两者逐字同形。
|
|
69
|
+
2. **单域 PUT → UI:逐路亲读的结论是不用改。** `src/lib/config/domain-write.ts:108` 把 issues 原样回给
|
|
70
|
+
路由,UI 侧有两种渲染:① `src/components/domains/issue-map.ts:42` 的字段级定位用
|
|
71
|
+
`path[0] === entriesKey` —— 但它只经 `DESCRIPTORS`(`src/components/domains/descriptors.tsx:419`)服务
|
|
72
|
+
**五个**域(`models` / `mcp` / `skills` / `rosters` / `plugins`),其中受引用完整性校验的 `models` 与
|
|
73
|
+
`rosters` 的域键**恰好等于**其条目键,`collab` / `entitlement` 根本不走这条路;② `collab` /
|
|
74
|
+
`entitlement` 的页面(`entitlement-page` / `tiers-page` / `generic-domain-editor`)只渲染
|
|
75
|
+
`issue.message` 或整条 JSON dump,不读 `path[0]`。**所以 UI 没有字段定位降级。**
|
|
76
|
+
(若日后把 `collab` / `entitlement` 接进 ① 那条路:在边界处剥掉域键前缀
|
|
77
|
+
`path[0] === domain ? path.slice(1) : path` 即与该域 zod 问题的坐标空间一致 —— zod 的 `superRefine`
|
|
78
|
+
仍从条目键起手。)
|
|
79
|
+
3. **顺带提一句(本批不动别仓)**:管理台自己那份反向引用告警 `danglingRefWarnings`
|
|
80
|
+
(`src/lib/config/cross-domain.ts`)的 `DanglingRef.path` 仍从 `["tiers", …]` / `["rosters", …]` 起手。
|
|
81
|
+
它是管理台的面,属主在管理台;要不要跟着统一由它自己定。
|
|
82
|
+
4. **五处手抄闭集请改调 `REF_CHECKED_DOMAINS`**(这才是那个少一员漏闸的正解,而不是把五员补成六员):
|
|
83
|
+
管理台 `src/app/api/config/rollback/route.ts:18`、`src/app/api/v1/scopes/[scope]/rollback/route.ts:89`
|
|
84
|
+
(各少 `entitlement` ⇒ 该域回滚漏闸)、`src/lib/config/apply-batch.ts:81` 与
|
|
85
|
+
`src/lib/config/domain-write.ts:107` 的六员 `if` 串(齐全,但仍是手抄);worker 服务
|
|
86
|
+
`src/config-provider.ts:271` 的六员 `for (const d of [...] as const)`(齐全,同上)。
|
|
87
|
+
|
|
88
|
+
**旧形的可 grep 特征**(在消费方仓里跑,命中即是需要人看一眼的点):
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
# a. 把引用完整性坐标当「从条目键起手」用的地方(旧形假设)
|
|
92
|
+
grep -rn '"templates"\|"tiers"' --include='*.ts' --include='*.tsx' src | grep -i 'path\|issue'
|
|
93
|
+
# b. 自己给坐标补域键前缀的地方 —— 4.0.0 后会补成两遍
|
|
94
|
+
grep -rn '\.\.\.ri\.path\|\.\.\.issue\.path\|\.\.\.i\.path\|path\.unshift' --include='*.ts' src
|
|
95
|
+
# c. 手抄的域闭集(五员或六员,Set 或行内联合)—— 改调 REF_CHECKED_DOMAINS
|
|
96
|
+
grep -rn '"rosters".*"workers".*"models"' --include='*.ts' src
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
### ③ 两道机器门(`test/ref-domain-closed-set-4.0.0.test.ts`,16 格)
|
|
100
|
+
|
|
101
|
+
- **闭集门**:`REF_CHECKED_DOMAINS` 与 `refIntegrityIssues` **真实分派**到的域集合逐字相等(双向:少一支
|
|
102
|
+
= 某域声明要查却没人查;多一支 = 闭集没登记的域被偷偷查了)。分派集合从**源码 AST 机器解出**
|
|
103
|
+
(形参名也从 AST 读),门里**不留第二份域名清单** —— 手抄清单正是本批要治的病,不能把它搬进门里。
|
|
104
|
+
另钉「一域一支」(两支同域 / 一支 `||` 串两域,都是那道穷举守卫咬不住的形态)与「解不出分派形就判红」
|
|
105
|
+
(fail-closed:分派改成第三种结构时门必须吵,不许静默放行)。三条变异自证它有牙。
|
|
106
|
+
- **path 形门**:六个域各造一条**真悬空引用**(五个走真 `parseDomain`;`models` 臂用未过 schema 的裸值
|
|
107
|
+
——`ModelsConfig` 自己的 `superRefine` 就拒悬空自引用,过了 parse 的 models 值在结构上造不出这条),
|
|
108
|
+
断言每条 issue 的 `path[0]` ∈ `REF_CHECKED_DOMAINS` 且 == 被查的域键,并先钉「反例真的红了」防假绿。
|
|
109
|
+
反例表的形是 `satisfies Record<RefCheckedDomain, …>`:闭集加一员而没补反例 ⇒ **编译期**红。
|
|
110
|
+
**红先读数**(本节 ② 落地前、① 已在):16 格里 4 格红 —— `collab` 臂
|
|
111
|
+
(`path[0]="templates" 不在 REF_CHECKED_DOMAINS 里`)、`entitlement` 臂(`path[0]="tiers"`)、六臂合集
|
|
112
|
+
(得到 `templates` / `tiers`,期望 `collab` / `entitlement`)、坐标细节钉;其余四臂与闭集门 8 格全绿。
|
|
113
|
+
|
|
114
|
+
### ③ 诊断面 `droppedKeys()` / `carriedUnknownKeys()` 回 `{ keys, total }`(BREAKING,返回形)
|
|
115
|
+
|
|
116
|
+
```diff
|
|
117
|
+
-export function droppedKeys(domain, raw, parsed): ConfigPath[]
|
|
118
|
+
-export function carriedUnknownKeys(domain, parsed): ConfigPath[]
|
|
119
|
+
+export interface UnknownKeyScan { keys: ConfigPath[]; total: number }
|
|
120
|
+
+export function droppedKeys(domain, raw, parsed): UnknownKeyScan
|
|
121
|
+
+export function carriedUnknownKeys(domain, parsed): UnknownKeyScan
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
**谁需要**:自装配读腿的消费方(不走 `parseDomainLoud` 的 `onWarning`、自己拿这两只函数做诊断)。此前
|
|
125
|
+
点名列表的上界(`DROP_SCAN_MAX_KEYS`)与真实总数**都是私有的** ⇒ 它拿到 64 条,既不知道是不是刚好 64 条,
|
|
126
|
+
也没有第二个公开面可以问「一共多少」,**结构上铸不出** `truncatedFrom`;而本包自己的告警面从 1.5.0 起就带
|
|
127
|
+
着这个数 —— 同一件事在两个公开面上一个说、一个不说。把有界诊断当穷举决策输入,正是服务端消费方在配置门上
|
|
128
|
+
三次被复审打穿的根因。
|
|
129
|
+
|
|
130
|
+
**谁受伤**(九个仓逐条命中**逐行亲读**,2026-09-22 —— 不是拿 grep 计数下的结论):
|
|
131
|
+
- **生产码零受伤**:worker 服务 / CLI / 管理台 / client SDK / 桌面 / web 客户端的 `src` 对这两只**包函数**
|
|
132
|
+
零 import。同名命中逐条判过,没有一条是它:worker 服务 `src/spec-fields.ts:443,468,525` 是它自己留痕事件
|
|
133
|
+
`task_limits_legacy_key_dropped` 的字段名;CLI `src/cli/handlers/cloudResources.ts:617-618` 读的是
|
|
134
|
+
registry PUT 响应里的 wire 字段 `json.droppedKeys`;SDK `packages/sdk/src/control-types.ts:61,68` 同一个
|
|
135
|
+
wire 字段;管理台 `src/lib/config/domain-write.ts:47` 是**它自己另写的一只同名函数**(算的是「本次写入被
|
|
136
|
+
schema 丢掉的键」,本包算的是「读回来时未知被丢的键」——**同名不同物**),其余管理台命中全是那个 wire 字段。
|
|
137
|
+
⚠️ 请管理台把那只改名(语义是 `strippedKeys`),否则 4.0.0 之后两边的文档会互相污染。
|
|
138
|
+
- **有一个带名字的非生产消费方**:评测线 sema-test 的 ss300 黑盒半场 ——
|
|
139
|
+
`repro/ss300-corpus-20260919/min-reader.mjs:34` 从 dist 解构了这两个名字(名字还在、解构照旧成立),
|
|
140
|
+
`c09-parsedomainloud-probe.mjs:17-20` 真调用它们并**打印返回形**:那两行的读数会从 `array[64]` 变成
|
|
141
|
+
`object`(不抛、不红,是探针的输出变了)。顺带说一句:B3 要补的缺口正是评测线自己量出来的 ——
|
|
142
|
+
它在 `min-reader.mjs:42-46` 与 `verify.mjs:265-269` 白纸黑字写着「那两个只回数组的导出才是零消费者的
|
|
143
|
+
**半成品**」「读数对(那两个导出确实不给 total)、结论错(整个公开面并非没有出路)」;4.0.0 之后那句
|
|
144
|
+
「半成品」不再成立,自接装配层的消费方可以直接从这两只函数拿到总数。
|
|
145
|
+
|
|
146
|
+
**补偿**:`total` 就是消费方要的那个数(`total > keys.length` ⇔ 被截断,自铸 `truncatedFrom = total`);
|
|
147
|
+
上界 `DROP_SCAN_MAX_KEYS` **仍然私有** —— 它是告警载荷的上界,不是契约,改它不该让任何消费方红。
|
|
148
|
+
顺带把两只私有扫描包装(`scanDropped` / `scanCarried`)删了:公开面自己就是那一次扫描,`parseDomainLoud`
|
|
149
|
+
铸告警时调的也是它 ⇒ 消费方自铸的数与告警里那个**同一个铸点**。
|
|
150
|
+
|
|
151
|
+
**红先读数**(B3 落地前,跑在本节 ①② 已在的树上,`test/diagnostics-scan-total-4.0.0.test.ts`):
|
|
152
|
+
`Tests 5 failed | 4 passed (9)`。绿的四格 = 三格「改前形」钉(`droppedKeys` 回的就是数组本身、`.total`
|
|
153
|
+
恒 `undefined`、公开面上没有第二个入口可以问真实总数)+ 一格「上界仍私有」;红的五格里最要紧的那条是
|
|
154
|
+
**消费方视角**的自铸:`expected {} to deeply equal { truncatedFrom: 70 }` —— 70 个未知键、列表截到 64,
|
|
155
|
+
消费方算不出那个 70。改后「改前形」三格已按新形改写,全文件 6 格绿。
|
|
156
|
+
|
|
157
|
+
### ④ 告警投影 `WarningProjection` 改**机器派生**,属主搬回 `./config-fns`(BREAKING,模块面)
|
|
158
|
+
|
|
159
|
+
`WARNING_KINDS` / `WarningKind` / `WarningProjection` / `projectWarning` 的属主从 `./conformance` 搬到
|
|
160
|
+
**告警联合自己的模块** `./config-fns`;`./conformance` 只**再导出**,kit 的进口一字不用改
|
|
161
|
+
(`import { projectWarning } from "@sema-agent/settings-schema/conformance"` 照旧可解析、照旧是同一只函数)。
|
|
162
|
+
投影的**形**从一张手写字段表改成从联合**逐臂派生**:
|
|
163
|
+
|
|
164
|
+
```ts
|
|
165
|
+
type PerArmOmit<W, K extends string> = W extends unknown ? Omit<W, K> : never; // 裸类型参数 ⇒ 逐员分配
|
|
166
|
+
export type WarningProjection = PerArmOmit<EffectiveReadWarning, "error">;
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
**病**:手写投影与它投影的联合之间没有任何字段级约束 ⇒ 给已有 kind 加一个字段,投影不动、`projectWarning`
|
|
170
|
+
不动、跨仓一致性门照绿,新字段一路穿过 kit,两边比的是一份**少一个字段**的告警(而都显示全绿)。
|
|
171
|
+
**注意分配律**:直接写 `Omit<EffectiveReadWarning, "error">` 会先把联合塌成交集再剥,`keys` / `path` /
|
|
172
|
+
`file` 这些只属于某几臂的字段会整片消失 —— 那是这条派生唯一的坑。
|
|
173
|
+
|
|
174
|
+
**消费方影响**:派生出来的形比手写那份**更紧**(`domain: string` → `DomainName`;`why: string` →
|
|
175
|
+
`"not-a-domain" | "non-portable-domain"`;`keys` / `droppedNames` 从 `readonly` 数组变可变数组)。本仓内
|
|
176
|
+
零处需要改;kit 的运行期行为一字未变(语料、报告、比对键全同形;`./conformance` 与 `./config-fns` 两条路
|
|
177
|
+
取到的 `projectWarning` 是**同一只函数**,实测 `===`)。**顺带一个正效果**:属主在 `./config-fns` ⇒ 投影也
|
|
178
|
+
出现在**主 barrel**(浏览器安全那一半),消费方不必为一只投影函数去装 Node-only 的 kit。
|
|
179
|
+
|
|
180
|
+
> 🔴 **订正一句抽象盘点稿里的话**(本批亲读,2026-09-22):盘点稿说管理台
|
|
181
|
+
> `src/lib/config/effective-warnings.ts` 的 `warningFacts` 是「`projectWarning` 的另一份实现」——**不是**。
|
|
182
|
+
> 逐行读它(`:78-99`):它出的是**管理面 wire 形** —— 坐标 `names(w.keys)` / `name(w.path)` 已经 stringify
|
|
183
|
+
> 成串、`error` 换成 `REDACTED_WIRE_ERROR` 占位、真 `error` 另路返回给服务端日志;而 `projectWarning` 保
|
|
184
|
+
> **结构化段坐标**、`error` 整个剔掉。两者共用的只是「逐 kind 列字段 + 一个 `never` 穷举 switch」这个骨架。
|
|
185
|
+
> 所以它**不能**直接改调本包的投影(那会丢掉 stringify 与脱敏两层);正解是**架在**派生投影之上(投影出门
|
|
186
|
+
> 后再过一遍坐标 stringify 与 error 脱敏),那样它的字段枚举才跟着契约走。这条留给管理台自己定。
|
|
187
|
+
|
|
188
|
+
### ⑤ prompts 摘要搬出调度存储模块,两个 sha256 铸点合成一只(不 breaking)
|
|
189
|
+
|
|
190
|
+
`promptsContentDigest` / `sectionContentHash` 从 `src/scheduler-store-node.ts` 搬进新模块
|
|
191
|
+
`src/prompts-digest.ts`,**仍挂在 `./node` 这条 subpath 上** —— 消费方的 import 一个字不用改
|
|
192
|
+
(管理台 `src/lib/config/resolve-prompts.ts:17` 与 `test/resolve-prompts.test.ts:8` 真在用
|
|
193
|
+
`@sema-agent/settings-schema/node`;逐行亲读 2026-09-22)。原处**删干净**、不留转出、零别名。
|
|
194
|
+
|
|
195
|
+
两条理由:①它们与调度存储(`scheduled_tasks.json` 的原子读写 + 跨进程变更原语)零关系;②搬家顺手把
|
|
196
|
+
**两个** sha256 铸点合成一只 —— 此前整包 digest 与逐 section contentHash 各 `createHash('sha256')` 一次、
|
|
197
|
+
各拼一次 `sha256:` 前缀,而这是跨仓对账锚(center 产、server 透传、core manifest 归因三方独立复算),
|
|
198
|
+
哪天有人只改一处就成了两个刻度,**而没有一格会红**。摘要改用本包**纯实现**的 `sha256Hex`(与
|
|
199
|
+
`createHash("sha256")` 逐字节相同,`test/sha256.test.ts` 逐例压着)⇒ 整个包的 sha256 只剩**一份实现**。
|
|
200
|
+
**值一个字节没变**:`test/prompts-digest-4.0.0.test.ts` 拿 `node:crypto` 当参照逐例压 `sectionContentHash`
|
|
201
|
+
与 `contentDigest` / `packId` / `sectionHashes`。
|
|
202
|
+
|
|
203
|
+
### ⑥ `fileSchedulerStore` → `createFileSchedulerStore`(BREAKING,重命名,零别名)
|
|
204
|
+
|
|
205
|
+
`./node` 的 `fileSchedulerStore(path)` 返回一只带 `load` / `save` 的**活对象**,按本组工厂命名法该是
|
|
206
|
+
`create*`;`2.0.0` 那次照人眼改,只改到了同族的 `siblingResolver`,这处漏了 —— 人眼扫一遍的门,漏掉的
|
|
207
|
+
那个永远不会自己红,所以这一版连同**门④** 一起落地(以后由机器看着)。
|
|
208
|
+
|
|
209
|
+
**零别名、零双读、零过渡方法**:旧名**已删**。消费方的 `tsc` 红(具名 import 找不到导出)或运行期
|
|
210
|
+
`undefined` 就是通知本身。迁移 = 把 `fileSchedulerStore(` 一律改成 `createFileSchedulerStore(`;
|
|
211
|
+
`loadSchedulerStore` / `saveSchedulerStore` / `mutateSchedulerStore` 三只**一字未动**。
|
|
212
|
+
红先读数:改名前放一格引用旧名的临时文件 ⇒ `tsc` rc=0(探针今天编译得过);改名后同一格 ⇒
|
|
213
|
+
`error TS2724: '"../src/scheduler-store-node.js"' has no exported member named 'fileSchedulerStore'`(rc=2)。
|
|
214
|
+
|
|
215
|
+
`materializeLocalRoot` **不改名**:见门④ 那条射程裁定 —— 它返回 `{ root, cleanup }`,属**资源获取**族。
|
|
216
|
+
|
|
217
|
+
### ⑦ 一致性门的来源律改**运行期拒**(fail-closed)
|
|
218
|
+
|
|
219
|
+
`createContractAdapter` 的头注一直自陈「它不是给消费方抄的」,而那句话**机器证不了**:把契约包自己的
|
|
220
|
+
函数注册成读腿,门证明的只是「包等于包」,而且看起来全绿。4.0.0 起这句自陈**有牙**:
|
|
221
|
+
|
|
222
|
+
- **判别式单铸**在 `src/conformance/entry-origin.ts`,两形闭集 —— `own-build-output`(落在**正在跑这段码
|
|
223
|
+
的那份**契约包的 `<包根>/dist/` 下 = 拿自己比自己)与 `another-installed-copy`(落在**另一份**装好的
|
|
224
|
+
契约包里)。**先判「我自己」**:消费方装好的那份 kit 自己就住在 `node_modules` 里,它的 dist 同时命中
|
|
225
|
+
两形,顺序反了最该拒的那一形反而被放行(本批自查出的承重位,已钉一格)。咬 `dist/` 而不是整个包根,
|
|
226
|
+
是因为契约仓**自证**时腿的入口是本仓 `src/*.ts`,而 `src/` 不随包发。
|
|
227
|
+
- **两处执法,同一只判别式,哪一形归哪一处有判据**:工厂看不见 `mode`,所以它只拒**在任何模式下都错**的
|
|
228
|
+
那一形(`own-build-output`)—— **当场拒**(抛,不是告警:一条告警在 CI 日志里活不过第二天,而这道门一旦
|
|
229
|
+
被同义反复接进去,它报的全绿是**假绿**);`another-installed-copy` 在 `historical` 下是**正当**的(那个
|
|
230
|
+
模式就是拿旧版包比行为,而旧版包常常正好装在 `node_modules/<包名>/` 布局下 —— codex 对抗复审 [medium]
|
|
231
|
+
点出这条,成立并已改),归看得见 mode 的 `release` 门。手写的适配器字面量(接入文档的默认配方)走不到
|
|
232
|
+
工厂 ⇒ 那道门**两形都判**。**拒绝句与违例句同源**,都说清「你落在哪儿 / 合法的是哪儿」;消费方那侧靠
|
|
233
|
+
`sits inside the contract package` 这段文案点名的钉子**一字未动**。
|
|
234
|
+
- **判据咬的是文件,不是字符串**:判别式两侧都先归一(`resolve` 抹平点段 / 双斜杠,再尽力 `realpath` 解一次
|
|
235
|
+
软链 —— `npm link` 出来的树就是软链)。`release` 门本来就先解软链再判,但 `historical` 模式根本不跑那道门,
|
|
236
|
+
工厂这一侧是那条路上**唯一的闸** ⇒ 归一必须做在**判别式里**,两处执法才咬同一个判据(codex 对抗复审
|
|
237
|
+
第二轮 [medium] 点出 `<包根>/./dist/x.js` 能绕过,成立并已改;顺带把 `run.ts` 里那次重复的解软链删掉)。
|
|
238
|
+
- **对照臂(不许误伤)**:消费方自己树里的生产入口(评测线自写的最小 reader 就是这形)、契约仓自证的
|
|
239
|
+
`src/*.ts`、historical 模式的旧版包(解包的、或装在 `node_modules` 布局下的)—— 一条都不拒,
|
|
240
|
+
红先的两次读数里它们恒绿。
|
|
241
|
+
- 红先读数:把拒关掉 ⇒ `Tests 5 failed | 9 passed`(工厂三臂 + 缺字段臂 + 门那一臂全红,对照臂不动);
|
|
242
|
+
装回来 ⇒ 全绿。归一那一条也是红先落的:先写点段 / 双斜杠 / 软链三格 ⇒ `1 failed`(`expected [Function]
|
|
243
|
+
to throw`),归一进判别式后同格转绿,`来源律` 整组 17 格全绿。
|
|
244
|
+
|
|
245
|
+
### ⑧ `./api/auth` 取「迁移」不取「删」
|
|
246
|
+
|
|
247
|
+
整面**没有任何消费仓 import 子路径 `./api/auth`**,也没有人从本包桶里取这 18 个名(尺子:同一把 grep 数
|
|
248
|
+
各仓的 `@sema-agent/settings-schema` import 行,读数几十到上百 —— 零是真零)。**但零消费不等于这面死了**,
|
|
249
|
+
它是被**各抄了一份**:管理台 `src/lib/auth/device-flow.ts:57-63` 的 `normalizeUserCode` 与本包
|
|
250
|
+
`src/api/auth.ts:89-95` **逐字节相同**(diff 实测),另有自己的 `DEVICE_GRANT_TYPE`(`:35`)/
|
|
251
|
+
`DEVICE_POLL_INTERVAL_SECONDS`(`:31`);壳 `src/sema/cloudAuth.ts:41` 自声明 `DeviceCodeResponse`
|
|
252
|
+
(与本包同名不同物)、`:50` 自声明 `TokenResponse`;SDK 在 `packages/sdk/src/registry/auth.ts:16` 对路径表与
|
|
253
|
+
grant_type 做**值拷贝**,并在**生产码** `:11` 从**改名前的旧包名**把 `DeviceCodeResponse` /
|
|
254
|
+
`TokenGrantResponse` 两只当类型 import,靠自己的同源锚测试(`test/registry-auth.test.ts:26,28`)对账 ——
|
|
255
|
+
而那道锚钉的是旧包(devDependency 停在 `0.17.1`)⇒ 本包改名之后的任何改动它一格也看不见。
|
|
256
|
+
(「零 import」那句话的**精确射程**:没有任何仓从 `@sema-agent/settings-schema` 这个名字下取这 18 个名;
|
|
257
|
+
从**旧包名**取的有 SDK 那 3 行。)
|
|
258
|
+
|
|
259
|
+
⇒ 删掉等于把三份手抄就地合法化。凭据轴该有单属主,所以本版**保留该出口**,理由 / 谁该改调 / 什么条件下删
|
|
260
|
+
逐字写进 `src/api/auth.ts` 的文件头注。**下一个 major** 若三方都已改调(或各自明确表态不改并认账)且那时
|
|
261
|
+
仍是零消费,「删」的理由才成立。
|
|
262
|
+
|
|
263
|
+
### 两道随本批落地的机器门(门③ / 门④)+ 一格闭集补全
|
|
264
|
+
|
|
265
|
+
- **门③ 投影派生门**(`test/warning-projection-derived-4.0.0.test.ts`):从 AST 取 `EffectiveReadWarning`
|
|
266
|
+
的**每一臂声明**(不是字段清单 —— 锚在手抄清单上,真键一加门就断),各插一个必填字段,用
|
|
267
|
+
LanguageService 重编译整棵 `src`,断言诊断落在 `projectWarning` 的**函数段内**(段也是 AST 取的)。
|
|
268
|
+
**两个读数**:
|
|
269
|
+
· **改前**(投影还是手写的那棵树):七臂变异,`projectWarning` 段内诊断恒 **0** ——「今天确实不红」
|
|
270
|
+
是实测,不是读码推演;同一次变异在**段外**有 1–2 条(`buildEffective` / `parseDomainLoud` /
|
|
271
|
+
`salvageEntries` / `file-store` 这些**铸造点**少铸一个必填字段),与投影一格关系也没有 ⇒ 门只认段内。
|
|
272
|
+
整文件读数 `Tests 7 failed | 3 passed (10)`。
|
|
273
|
+
· **改后**:七臂变异,段内诊断各 ≥1(逐臂都红),整文件 `Tests 10 passed (10)`。
|
|
274
|
+
另两格是承重的:未变异时全 `src` 零诊断(基线不红,否则「必红」是假读数)、变异还原后仍零诊断。
|
|
275
|
+
- **闭集补全格**(同文件):`WARNING_KINDS` == 从 AST 解出的联合臂集(逐字同集同序,双向)。**为什么要它**:
|
|
276
|
+
这张表的旧注写着「新 kind 不登记 ⇒ `satisfies` 编译红」,**实测是假的** —— 给联合加一条新臂、不登记进表,
|
|
277
|
+
`satisfies` 那行零诊断,红的是三处 `never` 穷举臂(`projectWarning` / `rebaseWarningPaths` / kit 的
|
|
278
|
+
`normalizeProjection` 各一条)。`satisfies` 只咬「多一个联合里没有的词」,咬不住「少登记一员」;
|
|
279
|
+
那半条缝现在由这一格咬。带负控(拿一张故意少一员/多一员的表喂比对函数,必须报差 —— 否则就是
|
|
280
|
+
「两边都从同一处来、永远相等」的假绿)。
|
|
281
|
+
- **门④ 工厂命名门**(`test/factory-naming-4.0.0.test.ts`):从**类型**判「活对象」——返回类型剥掉
|
|
282
|
+
`Promise`/`undefined`/`null` 后,自己可调用、或逐层下探(≤3 层)摸得到一个可调用的属性,**且那个调用
|
|
283
|
+
签名是本仓声明的**(`string[]` 的 `.map`、zod 的方法都声明在 lib / node_modules 里,数组与第三方值不该
|
|
284
|
+
因为「有方法」被判成活对象)。
|
|
285
|
+
**射程裁定(发布方)**:命名法只管**工厂**,**不管**返回「一份必须释放的把手」的**资源获取**族。于是门是
|
|
286
|
+
**一只判别式分两族,每族一条律**(闭集 —— 每只返回活对象的导出函数恰好落在一族里):
|
|
287
|
+
· **工厂族**(返回活对象、返回形第一层**没有**释放把手)⇒ 名字必须 `create*`;
|
|
288
|
+
· **资源获取族**(返回形第一层有 `cleanup` / `dispose` / `close` / `release` / `Symbol.[async]dispose`
|
|
289
|
+
这么一只**本仓声明的可调用**属性)⇒ 名字必须 `acquire*` / `open*` / `materialize*`。
|
|
290
|
+
第二条是第一条的**反向咬合**:随手挂一只 `cleanup: () => {}` 躲开 `create*` 的路在这里堵死 ——
|
|
291
|
+
**门按类写,不做逐名白名单**(白名单会把「今天这两个名字」冻成律,下一个同类还是漏)。本仓当下读数:
|
|
292
|
+
125 只导出函数 = 工厂族 3(`createContractAdapter` / `createSiblingResolver` / `createFileSchedulerStore`)
|
|
293
|
+
+ 资源获取族 1(`materializeLocalRoot`)+ 纯数据 121;**两族违例各 0**,已知违例登记表随之**删空**。
|
|
294
|
+
判别力自证**两条,各咬一族**:① 把 `createContractAdapter` 临时改名成 `makeContractAdapter`
|
|
295
|
+
(AST 取声明位、内存覆盖、不落盘)⇒ 立刻进工厂族违例名单;② 把 `materializeLocalRoot` 返回形里的
|
|
296
|
+
**释放把手改个名**(函数名一个字不动)⇒ 它掉回工厂族并被判违例(证明豁免咬的是**返回形**不是名字)。
|
|
297
|
+
**反向那一半(`create*` 必须返回活对象)今天是绿的,但刻意没立门** —— 一个返回 `void` 的过程名会被判成
|
|
298
|
+
假红,那条判据要先说清「工厂」与「过程」怎么分。**已知会翻红的反例(诚实登记,不预先豁免)**:哪天出现
|
|
299
|
+
一只真·工厂、它造的活对象自己带 `close()`(例:连接池客户端),门会按第二条要求它改名 —— 那时候要的是
|
|
300
|
+
给命名法补一句「资源 vs 客户端」怎么分的裁定,而不是在门里加一行特例。
|
|
301
|
+
|
|
302
|
+
### 本版**没做**的(免得读成「抽象批已全收」)
|
|
303
|
+
|
|
304
|
+
同一轮抽象盘点开出九件,本版收了八件(上面 ①–⑧ 与四道门);**剩下一件未动**,按它自己的批次发:
|
|
305
|
+
|
|
306
|
+
1. cross-session 两张词表与引擎包**双属主**:本包这 10 名生产面零痕迹,但消费方设了锁步门。候属主表态,
|
|
307
|
+
认领后随下一个 major 删。
|
|
308
|
+
|
|
309
|
+
另有两件**只记不做**(理由在各自小节里):`./api/auth` 的「删」分支要等下一个 major 才有理由(见 ⑧);
|
|
310
|
+
`./types` 那批零痕迹名与门面转出名未逐条判「预留 / 腐烂」,本版不以它们为据删任何东西。
|
|
311
|
+
|
|
312
|
+
## 3.0.1 — 2026-09-22 — 纯文档 patch:订正幽灵版号、重写消费接入档到 3.0.0、README 真话、待裁账并入 ROADMAP、产品名统一、一致性门新增 4 条对抗语料
|
|
313
|
+
|
|
314
|
+
**零行为改动、零类型改动、零 wire 面变化。**
|
|
315
|
+
|
|
316
|
+
- 全仓订正开发期遗留的幽灵版号 `1.11.0`:这一批开发时叫 `1.11.0`,发布定版时改叫 `2.0.0`,但改名没
|
|
317
|
+
传导进源码注释与文档(npm 上从未有过 `1.11.0`)。本档给 config-center 产品的迁移句 `^1.11.0` 是一个
|
|
318
|
+
永远解析不到任何版本的提货范围,已订正为 `^2.0.0`。
|
|
319
|
+
- 新增一格门:文档 / `src/` 里出现的 `x.y.z` 必须是本档真有的一节(或落在有理由的外部产品版本白名单
|
|
320
|
+
里)——不许再有第二个幽灵版号。
|
|
321
|
+
- `docs/CONSUMER-INTEGRATION.md` 重写到 3.0.0(此前停在改名定版前一节,2.0.0/2.1.0/3.0.0 三版未跟):
|
|
322
|
+
删除两条今天会带消费方接错线的过期指路 —— 老 env 名 `CONFIG_CENTER_URL` 在真实部署里是**拒启键**
|
|
323
|
+
(已改名 `SEMA_REGISTRY_URL`);「service 与 client 都调 `loadLocalConfig`」的说法与四仓生产码
|
|
324
|
+
(零 import)不符,真实接线是 `FileConfigStore` 直连。补齐 `ConfigPath`/`formatConfigPath`、
|
|
325
|
+
`./conformance`、已删 `./api/wire` 四节。
|
|
326
|
+
- README 改正一条过期的「待迁移」声明:cli 的 trust-rank 消费批早已兑现(不再是 pending)。
|
|
327
|
+
- 三条仍然有效的历史待裁事项(GC 工具的 `new Date()`、`refIntegrityIssues` 无级联校验、
|
|
328
|
+
`atModelAllowlist` 空语义二义)从一份标着「历史存档」却记着未拍板项的文档,搬进 `ROADMAP.md`
|
|
329
|
+
「立案」(坐标复核到 3.0.0),原文档删除 —— 待裁事项只留一份真源。
|
|
330
|
+
- 产品旧名(`sema-registry` / `sema-web`)统一为现名 `sema-web-admin`。
|
|
331
|
+
- 一致性门语料新增 4 条对抗用例(`contributor: test`),覆盖:告警对象上 `domain` 缺席必须原样传成
|
|
332
|
+
缺席、逐条容错域与闸门域的结论必须隔离、域内条目键上的原型名(`__proto__`/`constructor`/`toString`)
|
|
333
|
+
不被剥键扫描吞掉、坏条目值里的秘密字面永不上线四个面。
|
|
334
|
+
|
|
9
335
|
## 3.0.0 — 2026-09-16 — 告警坐标改铸**结构化段** `ConfigPath`(BREAKING)+ 删死出口 `./api/wire`(BREAKING)+ wire 透传判据修原型链漏洞 + 跨仓一致性门 kit(新 subpath `./conformance`)
|
|
10
336
|
|
|
11
337
|
> **版号裁定(发布方)**:本批三件里有两件是**纯 API 面**的破坏性改动 —— ①`EffectiveReadWarning` 的
|
|
@@ -445,7 +771,7 @@ parse 失败** —— 一份在上游合法的 `settings.json` 只因带了 `Pre
|
|
|
445
771
|
两边可以直接对拍(派生 vs 登记),不必各自反射。
|
|
446
772
|
- **config-center 产品(舰队管理台)**:①写侧仍走 `parseDomain`(严判,一字未变)—— 逐条容错只在**读**
|
|
447
773
|
路径;②读面同 worker 服务:坏条目不再连坐;③它今天装的是改名前、冻结在 `0.15.0` 的旧名包,本版之前的
|
|
448
|
-
全部契约它都不认,提货 `^
|
|
774
|
+
全部契约它都不认,提货 `^2.0.0` 是它自己的车。
|
|
449
775
|
|
|
450
776
|
## 1.10.0 — 2026-09-09 — `models.models[].compat` 引擎兼容声明席(镜像 `OpenAICompletionsCompat`;新键=minor)
|
|
451
777
|
|
package/README.md
CHANGED
|
@@ -22,12 +22,12 @@
|
|
|
22
22
|
> deprecate 标指向新名。
|
|
23
23
|
|
|
24
24
|
Engine-agnostic, zero-dependency contract layer for the sema stack — **one source of truth** for the
|
|
25
|
-
config domain across the center (the config-center product — today `sema-web` — the fleet admin), the
|
|
25
|
+
config domain across the center (the config-center product — today `sema-web-admin` — the fleet admin), the
|
|
26
26
|
server (the workers), and the client (desktop/CLI) seams. No two copies of the schema.
|
|
27
27
|
|
|
28
|
-
> 命名说明:config-center 这个**产品**曾叫 `sema-registry`,现在是 `sema-web`
|
|
29
|
-
>
|
|
30
|
-
> 下文凡提「center / config-center 产品」即指该产品的当期实现。
|
|
28
|
+
> 命名说明:config-center 这个**产品**曾叫 `sema-registry`,又改成 `sema-web`,现在是 `sema-web-admin`
|
|
29
|
+
> (clay 令 2026-08-26 仓改名;后续仍可能再改)。本**包**名与产品名解耦,**不随产品改名而动**(1.0.0 这次
|
|
30
|
+
> 改名是本包自身的正名,与产品名无关)—— 下文凡提「center / config-center 产品」即指该产品的当期实现。
|
|
31
31
|
|
|
32
32
|
- **Source of truth**: `github.com/sema-agent/sema-settings-schema`, branch `main` (private org repo; renamed from `sema-registry-core` on 2026-08-26, old URLs redirect).
|
|
33
33
|
仓名本步不动(只改包名)。
|
|
@@ -51,7 +51,9 @@ sound). `npm ls --prod` therefore shows only `zod`. Node built-ins only (`node:c
|
|
|
51
51
|
shapes (`User`, `PublishedSnapshot`, …).
|
|
52
52
|
- **Roster resolution** (`resolve-roster.ts`): `resolveEffectiveForWorker` → the per-worker wire payload.
|
|
53
53
|
- **Ref integrity** (`cross-domain.ts`): `refIntegrityIssues` + `createSiblingResolver` (the pure half — fleet
|
|
54
|
-
auto-placement stays in the config-center 产品).
|
|
54
|
+
auto-placement stays in the config-center 产品). The ref-checked domains are a RUNTIME closed set —
|
|
55
|
+
`REF_CHECKED_DOMAINS` + `isRefCheckedDomain` (`RefCheckedDomain` is derived from the value) — so a consumer
|
|
56
|
+
asks the contract instead of hand-copying the list; every `RefIssue.path` starts with the domain key.
|
|
55
57
|
- **Hashing** (`hash.ts`): `stableHash` (FNV-1a 64) for ETags / version detection.
|
|
56
58
|
- **Skills manifest** (`skills-manifest.ts`): content-addressing for the lazy-pull skill bodies.
|
|
57
59
|
- **Secret inventory** (`secret-refs.ts`): `collectEnvRefs` — every referenced env-NAME.
|
|
@@ -71,8 +73,12 @@ sound). `npm ls --prod` therefore shows only `zod`. Node built-ins only (`node:c
|
|
|
71
73
|
step — those live in the consumers (engine import door, shell resolver), each on its own release schedule.
|
|
72
74
|
1.2.0 adds the **trust ordering primitives**: `trustRank(layer)` (v2-design §1 verbatim, `managed 6 > global 5 >
|
|
73
75
|
project 4 > local 3 > session 2 > task 1`) and `isAtLeast(layer, minTrust)` — the ladder every resolver is to
|
|
74
|
-
compare floors against; consumers retire their own rank tables onto it
|
|
75
|
-
|
|
76
|
+
compare floors against; consumers retire their own rank tables onto it. **3.0.1 correction**: this used to say
|
|
77
|
+
the CLI's `TRUST_RANK` migration was still pending (board [5807] ③) — it shipped in the CLI's own 1.2.0
|
|
78
|
+
consumption batch (board [5815], 2026-08-31): the CLI's third literal copy of the ladder is deleted
|
|
79
|
+
(`src/sema/settings/keyContracts.ts` self-documents the retirement) and its resolver now really imports
|
|
80
|
+
`isAtLeast` from this package (`src/sema/settings/resolver.ts`). Exporting the primitive DID make it the
|
|
81
|
+
single source, for this consumer.
|
|
76
82
|
`MinTrust` gains the `task` word (the runtime task overlay, the loosest floor; appended, declaration order
|
|
77
83
|
otherwise unchanged — the declaration order is not the ladder, `trustRank` is). The published table does not
|
|
78
84
|
use `task`; the widened vocabulary is visible to consumers that hold a `Record<MinTrust, …>` / exhaustive
|
|
@@ -158,7 +164,9 @@ if (!report.ok) throw new Error(report.violations.join("\n"));
|
|
|
158
164
|
```
|
|
159
165
|
|
|
160
166
|
- **适配器**带 `provenance`(你**实际加载到**的那份契约包)与每条腿的 `entry{module,export}`
|
|
161
|
-
(**生产代码**的入口 —— 在测试里把契约包函数本身当读腿,门证明的只是「包等于包」)。
|
|
167
|
+
(**生产代码**的入口 —— 在测试里把契约包函数本身当读腿,门证明的只是「包等于包」)。4.0.0 起这句
|
|
168
|
+
**有牙**:入口落在**本包自己的 `dist/`**(拿自己比自己)⇒ `createContractAdapter` **当场拒**;落在
|
|
169
|
+
**另一份**装好的契约包里(`historical` 模式的正当目标)⇒ 工厂不拒,由 `release` 门点名。
|
|
162
170
|
- **两种模式**:`release`(版本三方对齐 + 生产入口核验 + 禁 `only`/归一器 + 至少登记一条腿 + **登记的腿
|
|
163
171
|
必须跑**;未登记的腿只记 `skipped: no adapter`,不算违例)与 `historical`(对旧版包跑,只记行为 diff)。
|
|
164
172
|
- **四律**:对抗律 / 变异律 / 覆盖律 / 来源律 —— 缺一不算「门」。
|
package/conformance/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
4.0.0
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
{
|
|
2
|
+
"domains": {
|
|
3
|
+
"outcome": "effective",
|
|
4
|
+
"effective": {
|
|
5
|
+
"collab": {
|
|
6
|
+
"templates": []
|
|
7
|
+
},
|
|
8
|
+
"entitlement": {
|
|
9
|
+
"budgetGroups": [],
|
|
10
|
+
"groupBindings": [],
|
|
11
|
+
"subjects": [],
|
|
12
|
+
"tiers": []
|
|
13
|
+
},
|
|
14
|
+
"execution": {
|
|
15
|
+
"allowedLanes": [
|
|
16
|
+
"host"
|
|
17
|
+
],
|
|
18
|
+
"required": false
|
|
19
|
+
},
|
|
20
|
+
"governance": {},
|
|
21
|
+
"hosts": {
|
|
22
|
+
"hosts": [
|
|
23
|
+
{
|
|
24
|
+
"approved": true,
|
|
25
|
+
"drain": false,
|
|
26
|
+
"enabled": true,
|
|
27
|
+
"labels": [],
|
|
28
|
+
"name": "box1"
|
|
29
|
+
}
|
|
30
|
+
]
|
|
31
|
+
},
|
|
32
|
+
"limits": {},
|
|
33
|
+
"mcp": {
|
|
34
|
+
"servers": []
|
|
35
|
+
},
|
|
36
|
+
"models": {
|
|
37
|
+
"models": [
|
|
38
|
+
{
|
|
39
|
+
"api": "openai-completions",
|
|
40
|
+
"apiKeyEnv": "OPENAI_API_KEY",
|
|
41
|
+
"enabled": true,
|
|
42
|
+
"id": "m0-id",
|
|
43
|
+
"name": "m0",
|
|
44
|
+
"provider": "openai",
|
|
45
|
+
"quotaWeight": 1,
|
|
46
|
+
"reasoning": false,
|
|
47
|
+
"tier": "standard",
|
|
48
|
+
"vision": false
|
|
49
|
+
}
|
|
50
|
+
],
|
|
51
|
+
"roles": {},
|
|
52
|
+
"tierGroups": []
|
|
53
|
+
},
|
|
54
|
+
"plugins": {
|
|
55
|
+
"plugins": []
|
|
56
|
+
},
|
|
57
|
+
"projects": {
|
|
58
|
+
"projects": {}
|
|
59
|
+
},
|
|
60
|
+
"prompts": {
|
|
61
|
+
"bindings": []
|
|
62
|
+
},
|
|
63
|
+
"rosters": {
|
|
64
|
+
"rosters": []
|
|
65
|
+
},
|
|
66
|
+
"runtime": {},
|
|
67
|
+
"scenarios": {
|
|
68
|
+
"scenarios": []
|
|
69
|
+
},
|
|
70
|
+
"skills": {
|
|
71
|
+
"skills": []
|
|
72
|
+
},
|
|
73
|
+
"systems": {
|
|
74
|
+
"systems": []
|
|
75
|
+
},
|
|
76
|
+
"workers": {
|
|
77
|
+
"workers": []
|
|
78
|
+
}
|
|
79
|
+
},
|
|
80
|
+
"warnings": [],
|
|
81
|
+
"metadata": {
|
|
82
|
+
"version": 7,
|
|
83
|
+
"updatedAt": "2026-09-16T00:00:00.000Z"
|
|
84
|
+
}
|
|
85
|
+
},
|
|
86
|
+
"wire": {
|
|
87
|
+
"derive": true
|
|
88
|
+
},
|
|
89
|
+
"localRoot": {
|
|
90
|
+
"derive": true,
|
|
91
|
+
"localVersion": 2247688933988744
|
|
92
|
+
}
|
|
93
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
{
|
|
2
|
+
"meta": {
|
|
3
|
+
"version": 7,
|
|
4
|
+
"updatedAt": "2026-09-16T00:00:00.000Z"
|
|
5
|
+
},
|
|
6
|
+
"domains": {
|
|
7
|
+
"models": {
|
|
8
|
+
"models": [
|
|
9
|
+
{
|
|
10
|
+
"name": "m0",
|
|
11
|
+
"provider": "openai",
|
|
12
|
+
"id": "m0-id",
|
|
13
|
+
"apiKeyEnv": "OPENAI_API_KEY"
|
|
14
|
+
}
|
|
15
|
+
]
|
|
16
|
+
},
|
|
17
|
+
"hosts": {
|
|
18
|
+
"hosts": [
|
|
19
|
+
{
|
|
20
|
+
"name": "box1",
|
|
21
|
+
"approved": true
|
|
22
|
+
}
|
|
23
|
+
]
|
|
24
|
+
},
|
|
25
|
+
"web-admin-ui": {
|
|
26
|
+
"theme": "dark"
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
{
|
|
2
|
+
"contributor": "test",
|
|
3
|
+
"claim": "告警对象上 `domain` **缺席**(不是 `null`、不是空串)时,三腿必须原样传成缺席。消费方普遍写 `{domain: w.domain, …}` 这种投影(web-admin [7285] ③ 实测 `unread-config-file` 无必填 `domain`),谁把缺席折成 `null` 或 `\"\"`,谁就让下游多红一处而自己看不见。",
|
|
4
|
+
"firstRedOn": [],
|
|
5
|
+
"note": "一条输入里同时放两条 `unread-config-file`:`hosts.json`(非便携域 ⇒ **带** `domain`)与 `web-admin-ui.json`(非域名 ⇒ **不带** `domain`)。两种形并排,所以「把缺席一律折成 null」与「把 domain 整个丢掉」两种错法都会红,而不是只挡住其中一种。诚实边界:`unread-config-file` 是 `config.d/` 扫描**独有**的 kind(闭集里唯一 `domain` 可选的那条),所以「缺席传成缺席」这件事**结构上只在 local-root 腿上可观察**;domains / wire 两腿在本条上是零告警的正控(它们不该凭空造出这条告警)。firstRedOn 空是**实跑**出来的:五个历史版本(1.0.0/1.5.0/1.10.0/2.0.0/2.1.0)在本条上全绿 ⇒ 护栏,不是回归凭证。"
|
|
6
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"domains": {
|
|
3
|
+
"outcome": "rejected",
|
|
4
|
+
"stage": "validate"
|
|
5
|
+
},
|
|
6
|
+
"wire": {
|
|
7
|
+
"outcome": "rejected",
|
|
8
|
+
"stage": "validate",
|
|
9
|
+
"why": "domains 腿是拒绝态,不派生;wire 腿把同一份 rosters 文档交给同一个 buildEffective(skills/prompts 之外零改道),闸门域的 throw 穿过信封层 ⇒ 拒绝层与 domains 腿相同"
|
|
10
|
+
},
|
|
11
|
+
"localRoot": {
|
|
12
|
+
"outcome": "rejected",
|
|
13
|
+
"stage": "validate",
|
|
14
|
+
"why": "models 与 rosters 都是便携域,两份文件层都过得去(都是 JSON 对象),拒绝发生在域校验层而不是 parse 层"
|
|
15
|
+
}
|
|
16
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
{
|
|
2
|
+
"meta": {
|
|
3
|
+
"version": 7,
|
|
4
|
+
"updatedAt": "2026-09-16T00:00:00.000Z"
|
|
5
|
+
},
|
|
6
|
+
"domains": {
|
|
7
|
+
"models": {
|
|
8
|
+
"models": [
|
|
9
|
+
{
|
|
10
|
+
"name": "keep",
|
|
11
|
+
"provider": "openai",
|
|
12
|
+
"id": "keep-id",
|
|
13
|
+
"apiKeyEnv": "OPENAI_API_KEY"
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
"name": 5,
|
|
17
|
+
"id": "bad-id"
|
|
18
|
+
}
|
|
19
|
+
]
|
|
20
|
+
},
|
|
21
|
+
"rosters": {
|
|
22
|
+
"rosters": [
|
|
23
|
+
{
|
|
24
|
+
"name": "r1"
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
"name": 5
|
|
28
|
+
}
|
|
29
|
+
]
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
{
|
|
2
|
+
"contributor": "test",
|
|
3
|
+
"claim": "同一份坏条目,放进逐条容错的域(`models`,`DOMAIN_READ_FALLBACK=\"default\"`)与放进 fail-loud 的闸门域(`rosters`,`=\"throw\"`),结论必须不同:前者丢那一条并恰报一条 `entry-dropped`、**不得同时**报 `domain-defaulted`(一域一个结论);后者**整份读在 validate 层被拒**(不是回落到 schema 默认 —— 对闸门域回落就是放宽闸门)。server [6782] 与 web-admin [7285] G2/G4 是同一件事的两半,跨腿读错会把『逐条容错』顺手放宽到闸门域,把这条设计为拒的输入读成绿(带两条 `entry-dropped`)。",
|
|
4
|
+
"firstRedOn": [],
|
|
5
|
+
"note": "【claim 措辞订正 2026-09-19,test 自纠】初稿写的是「后者整域回落」—— **是本方写错**:「回落」是 `DOMAIN_READ_FALLBACK=\"default\"` 那一族的动作,闸门域是 `\"throw\"` = fail-loud,整份读被拒。出处:契约自述 `config-fns.js` 的 `DOMAIN_READ_FALLBACK` 头注(defaulting would LOOSEN gates)+ 契约自带 `c05-gate-domain-bad-entry-throws` 金样(rejected/validate)+ 本仓 historical 实跑五版一致。claim 已按实测改写,对抗意图不变。两域放**同形**坏条目(`name` 是数字):`models`(DOMAIN_READ_FALLBACK=\"default\")与 `rosters`(=\"throw\",且是唯一一个便携的数组形闸门域)。3.0.0 实测:闸门域坏条目使**整份读**在 `validate` 层拒,三腿同形 —— 因此本条的期望是 `rejected/validate`,`models` 那半场在同一份输入里**结构上观察不到**。这正是本条的判别力所在:一个把逐条容错顺手放宽到闸门域的消费方,会把这条设计为拒的 case 读成绿(带两条 `entry-dropped`)。claim 前半场(`models` 恰一条 `entry-dropped`、零 `domain-defaulted`)由 verify.mjs 的配对正控臂(同一份输入去掉 `rosters` 域)钉住,读数记在 _SUMMARY.json 的 t02.pairedControl。claim 里「闸门域整域回落」的『回落』一词与契约自述的 `\"throw\"` = fail-loud 不符 —— 见 _SUMMARY.json 的 claimVsObserved。firstRedOn 空是**实跑**出来的:1.0.0/1.5.0/1.10.0/2.0.0/2.1.0 五版在本条上都是 expected-rejected(闸门域坏条目整份拒这条律从 1.0.0 起一字未动)⇒ 护栏,不是回归凭证。"
|
|
6
|
+
}
|