@sema-agent/settings-schema 8.2.0 → 8.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -6,6 +6,92 @@
6
6
  > `@sema-agent/registry-core`(更早还曾是 `@sema-ai/registry-core`)。本档 `0.19.0` 以下各节里的
7
7
  > 包名是**历史记录**,如实保留不追改。
8
8
 
9
+ ## 8.3.1 — 2026-10-08 — FLEET_SCOPE_ENV 注改真(patch)
10
+
11
+ > **判档 = patch**(README「发版纪律」:文案 / 注释 = patch)。只改 `src/fleet.ts` 里 `FLEET_SCOPE_ENV` 的头注(它随 `.d.ts` 发出);常量值不变,导出面登记表零 diff,零运行期行为,
12
+ > 零 wire 形。**8.3.0 已发的那一节一字未动**(按包规则,对已发节的更正写进当前节)—— 它里面作废的说法在下面逐句点名。
13
+
14
+ ### 一、病:8.3.0 的头注把续租体与心跳说成引擎用这只 env 填的东西
15
+
16
+ 8.3.0 的头注(与同节第一节)写「引擎读它作自身身份的 scope:比对 `DrainRequest.scope`、作 `ServingLeaseRequest.scope` 发出、在心跳上点名(`?scope=`)」。
17
+ 后两处与引擎的实现相反:引擎读这只 env **只**用来比对 `DrainRequest.scope`(请求不带 scope ⇒ 读成默认 scope,与引擎自己缺席时读的是同一只缺省);
18
+ **续租体与心跳都不另送 scope** —— 那两处的 scope 由凭证绑定决定。这条规则本包一直写在 `ServingLeaseRequest` 注(续租只收 per-worker 令牌,缺席 = 令牌的 scope,在场必须相等)与
19
+ `InstanceAnnouncement` 注(心跳体没有 `scope` 键,行落在凭证点名的 scope)上,8.3.0 的 `FLEET_SCOPE_ENV` 头注与它们自相矛盾。
20
+
21
+ ### 二、8.3.0 一节里作废的三句(同一个说法)
22
+
23
+ 1. 第一节「引擎读它作**自身身份的 scope**:排空口里拿 `DrainRequest.scope` 比对的那一格、续租体 `ServingLeaseRequest.scope`、心跳的 `?scope=`」—— 后两项作废,只剩第一项。
24
+ 2. 用户可见变化第 2 条的括注「(续租体「在场必须相等」的规则下会被拒)」—— 作废;没渲 `FLEET_SCOPE` 的后果见下面第四节。
25
+ 3. 未闭环第二条「不一致时按各口既有的规则表现为拒(续租体 scope 在场必须与令牌绑定的相等)/ 比对不上」里的「续租体 … 拒」—— 作废,只剩「比对不上」。
26
+
27
+ ### 三、现读法(一个事实一个源)
28
+
29
+ - **续租 / 心跳的 scope = 凭证**:续租只收 per-worker 令牌,`ServingLeaseRequest.scope` 缺席 = 令牌绑定的 scope、在场必须相等(令牌与体字段之间的规则,不涉 `FLEET_SCOPE`,未变);
30
+ 心跳体没有 `scope` 键,行落在凭证点名的 scope。引擎不用 `FLEET_SCOPE` 填这两处。
31
+ - **引擎自身身份的 scope = 渲染器给的 `FLEET_SCOPE`**(缺席 = `GLOBAL_SCOPE`;不从令牌格式反推),唯一用途 = 比对 `DrainRequest.scope`(请求不带 scope ⇒ 读成默认 scope)。
32
+ - **两者同值是渲染器的义务**:本包不判、也判不了(令牌不在本包)。
33
+
34
+ 头注改成这一读法;文本钉见第五节。README 的 8.3.0 条目与 llms 事实版没有复述作废的那句;`llms-full.txt` 的 `FLEET_SCOPE_ENV` 事实句补写了「只用来比对 `DrainRequest.scope`、续租 / 心跳的 scope 由凭证定」。
35
+
36
+ ### 四、用户可见变化(谁受影响 / 怎么改 / 不改会怎样)
37
+
38
+ 1. **只用 schema / 读写 helper、不碰 fleet 的消费方**:零变化(注释改动,导出面零 diff)。
39
+ 2. **配置渲染器(管理面)**:渲 `FLEET_SCOPE` 的义务不变,值 = 该成员 per-worker 令牌绑定的 scope。现读法下它**只**影响引擎对排空请求 scope 的比对:
40
+ 不渲 ⇒ 引擎身份按 `GLOBAL_SCOPE`,非 `global` 租户的排空请求(带该租户 scope)与它比对不上;**续租与心跳不受影响**(它们的 scope 来自凭证,不来自这只 env)。
41
+ 3. **引擎(server)**:不要把 `FLEET_SCOPE` 填进续租体或心跳;引擎现有的读法(只比对 `DrainRequest.scope`)不变,本版只是把本包的注改成与它一致。
42
+
43
+ ### 五、红先 / 门
44
+
45
+ - 先写钉、对着改前的注跑红,再改注:`test/fleet-scope-env-8.3.1.test.ts` 五格(常量不动 / 头注不再说「sends as」「names on its heartbeat」「?scope=」,且凡提到续租 / 心跳的句子同句带「credential」/
46
+ 头注把用途钉在比对 `DrainRequest.scope` 并写明请求不带 scope 读成默认 scope / 头注一句话说清「续租 / 心跳的 scope = 凭证;引擎自身身份的 scope = 渲染器给的 `FLEET_SCOPE`;同值是渲染器的义务」/
47
+ 8.3.0 既有三件仍在)⇒ 3 红 2 绿(2 绿是反向格:常量值不动、既有三件不丢);改注后全绿;`test/fleet-scope-env-8.3.0.test.ts` 不动、仍绿。
48
+ - 变异自证(每个都使预期那一格红):头注改回「sends as … ServingLeaseRequest.scope」/ 头注删「the credential's」句 / 头注删「renderer's obligation」句 / 头注删「carries no scope reads as the default scope」/ 头注写回 `?scope=`。
49
+ - 全量读数见发版链(本节不复述一份会过期的数)。
50
+
51
+ ### 未闭环(如实)
52
+
53
+ - **现读法里引擎侧的事实**(只比对 `DrainRequest.scope`、请求不带 scope 读缺省、续租体与心跳不另送 scope)出自引擎实现;本包没有取引擎源码的通道,没有机器门看「引擎改了读法而本注没跟」。
54
+ - **8.3.0 已发节里作废的三句仍留在那一节**(已发节字节不动):读者以本节为准。
55
+
56
+ ## 8.3.0 — 2026-10-08 — fleet 成员所属租户 scope 的 env 名常量 `FLEET_SCOPE_ENV`(additive)
57
+
58
+ > **判档 = minor**(README「发版纪律」:新键 / 新导出 = minor)。新导出 1 名(`FLEET_SCOPE_ENV`,`.` 与 `./fleet` 各 +1)与一个新保留 env 名;
59
+ > 无新运行期行为、无新 wire 形。导出面登记表只多名字行,无删、无改形。
60
+
61
+ ### 一、`FLEET_SCOPE_ENV = "FLEET_SCOPE"`
62
+
63
+ 与 `FLEET_GENERATION_ENV` 同一处登记(`src/fleet.ts`)。配置渲染器把引擎所属租户 scope 渲进这个 env(与该成员 per-worker 令牌绑定的 scope 同值);
64
+ 引擎读它作**自身身份的 scope**:排空口里拿 `DrainRequest.scope` 比对的那一格、续租体 `ServingLeaseRequest.scope`、心跳的 `?scope=`。
65
+ **一个事实一个源**:scope 由渲染器给,引擎**不从令牌的格式反推**(令牌对引擎是不透明串,格式猜测就是会与中心绑定值分歧的第二个源)。
66
+ **缺席 = 默认 scope**:值的唯一正本是 `GLOBAL_SCOPE`(`api/scopes`),与中心「请求里没有任何东西点名 scope」时读的是同一个缺省;常量头注只指向 `GLOBAL_SCOPE`,不复述字面量。
67
+
68
+ 它同时进了保留 env 名表(`RESERVED_WORKER_ENV`):`WorkerSpec.env` / `secretEnv` 设它即被拒,拒因指路「set by the config renderer from the worker's scope — not configurable」;
69
+ 渲染器是唯一写手。两侧都 import 常量,不各自拼字符串(整个 `src/` 里这个字面量只出现一次,有源码文本钉)。
70
+
71
+ ### 用户可见变化(谁受影响 / 怎么改 / 不改会怎样)
72
+
73
+ 1. **只用 schema / 读写 helper、不碰 fleet 的消费方**:零变化。
74
+ 2. **配置渲染器(管理面)**:给 fleet 成员渲 env 时,用 `FLEET_SCOPE_ENV` 写该成员 per-worker 令牌绑定的 scope;不改会怎样:引擎读不到 scope,按上面的缺省读法把自己当 `GLOBAL_SCOPE` ——
75
+ 非 `global` 租户的成员的身份 scope 与令牌绑定的 scope 不一致(续租体「在场必须相等」的规则下会被拒)。
76
+ 3. **引擎(server)**:读 `FLEET_SCOPE_ENV` 作自身身份的 scope,缺席用 `GLOBAL_SCOPE`;不要从令牌格式反推。
77
+ 4. **在 `WorkerSpec.env` / `secretEnv` 里设了 `FLEET_SCOPE` 的**:现在被拒(保留名),`migrateWorkersDomain` 对这样的行进 `refused`(8.2.0 起逐行终判)。
78
+ 这个名字是本版才出现的,本包此前没有它;设过的删掉那一行,由渲染器写。**持有 `RESERVED_WORKER_ENV` 总数 / 名单断言的测试**要 +1(本仓的历史钉就是两例)。
79
+
80
+ ### 红先 / 门
81
+
82
+ - 先写钉、对着改前的树跑红,再实现:`test/fleet-scope-env-8.3.0.test.ts` 七格(名逐字 + 两入口同值 + 字面量类型 / 保留表两直通面 + 拒因指路 + fleet 保留名恰两个 /
83
+ 字面量在 `src/` 里恰一处 / 头注指向 `GLOBAL_SCOPE` 且不复述字面量、写清三件事)+ 两处既有钉的新读数(`test/engine-signal-8.1.0.test.ts` 保留 fleet 名单、
84
+ `test/migration-signpost-7.0.1.test.ts` 7.0.0 之后的增词表)⇒ 8 红;实装后全绿。
85
+ - 变异自证(七个,每个都使预期那一格红):常量值改字 / 删保留表那一行 / `src/` 里多一处字面量 / 头注写进 `"global"` / 头注删掉 `GLOBAL_SCOPE` / 头注删掉「不从令牌格式反推」句 /
86
+ 导出面登记表删一行。
87
+ - 全量:build rc 0 · typecheck rc 0 · vitest 85 files / 1508 tests rc 0 · e2e rc 0 · `npm pack --dry-run` rc 0(360 文件,含 `dist/fleet-paths.json`,无 docs / scripts / test / src)。
88
+ - 导出面登记表(`docs/API-SURFACE.md`)重生成:`.` 与 `./fleet` 各 +1 名(`FLEET_SCOPE_ENV` — value),无删无改形。
89
+
90
+ ### 未闭环(如实)
91
+
92
+ - **本包只定名与缺省,不含渲染器与引擎的实现**:「渲染器写、引擎读」的两端落地在各自仓,本包没有取它们源码的通道,两端是否都 import 这只常量没有机器门看。
93
+ - **`FLEET_SCOPE` 与令牌绑定的 scope 同值是渲染器的义务**:本包不判、也判不了(令牌不在本包);不一致时按各口既有的规则表现为拒(续租体 scope 在场必须与令牌绑定的相等)/ 比对不上,而不是静默错归属。
94
+
9
95
  ## 8.2.0 — 2026-10-08 — 迁移器逐行终判(行为改真)· fleet 端点常量与活性路由组 · 心跳三键收型 · 中心能力位的承载键 · 两处注释改真
10
96
 
11
97
  > **判档 = minor**(README「发版纪律」:新键 / 新导出 = minor)。新导出 6 名(`LEASE_STATES` / `LeaseState` / `FLEET_PATHS` / `FLEET_LIVE_PATHS` /
package/README.md CHANGED
@@ -48,6 +48,14 @@ 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
+ ## 升级指引:8.3.0 → 8.3.1(注释改真,零动作可升)
52
+
53
+ `FLEET_SCOPE_ENV` 的头注改真:引擎读这只 env 只用来比对 `DrainRequest.scope`;续租体与心跳的 scope 由凭证定、不取这只 env。导出面与行为零变化,逐条见 CHANGELOG 8.3.1。
54
+
55
+ ## 升级指引:8.2.0 → 8.3.0(additive,零动作可升)
56
+
57
+ `./fleet`(与主入口)多一个 env 名常量 `FLEET_SCOPE_ENV = "FLEET_SCOPE"`:成员所属租户 scope,渲染器写、引擎读(缺席 = `GLOBAL_SCOPE`),同时进 `RESERVED_WORKER_ENV`(`worker.env` / `secretEnv` 设它被拒)。逐条见 CHANGELOG 8.3.0。
58
+
51
59
  ## 升级指引:8.1.0 → 8.2.0(additive + 迁移器一处行为改真)
52
60
 
53
61
  **行为改真(一处)—— `migrateWorkersDomain` 终判逐行**:每一只有字符串名字的 worker(迁过的、没动的一样)都要读得进「只含它一行的 workers 域」
@@ -344,6 +352,8 @@ therefore shows only `zod`. Node built-ins only (`node:crypto`, `node:fs`).
344
352
  `/api/fleet/live/host-planner/{host}`) and the liveness group `FLEET_LIVE_PATHS` (heartbeat, serving lease, host planner), also
345
353
  shipped as `@sema-agent/settings-schema/fleet-paths.json` for tooling that cannot import TypeScript — and names the one carrier of
346
354
  the center's capability bits: `HEARTBEAT_REPLY_CAPABILITIES_KEY` (`"capabilities"`) on the heartbeat's 2xx reply.
355
+ **8.3.0** (additive) adds the env name `FLEET_SCOPE_ENV` (`"FLEET_SCOPE"`) — the tenant scope a fleet member belongs to, set by the
356
+ config renderer and read by the engine as its own identity's scope (absent ⇒ `GLOBAL_SCOPE`); a reserved worker env name.
347
357
  - **Declared-but-unvalidated keys** (`PARSE_TRANSPARENT_KEYS`, 2.0.0): the ONE registry of keys this package
348
358
  carries verbatim WITHOUT checking their shape, each row naming who does check it (the engine's ingest side or
349
359
  a consumer's enforcement point). Today: `models[].compat` (1.10.0), `models[].headers` (6.4.0; the header-name
@@ -1 +1 @@
1
- 8.2.0
1
+ 8.3.1
package/dist/fleet.d.ts CHANGED
@@ -712,6 +712,20 @@ export declare function readEngineSignal(health: unknown): EngineSignal | undefi
712
712
  * ({@link EngineSignal}`.instanceId`), and env cannot carry it (see there).
713
713
  */
714
714
  export declare const FLEET_GENERATION_ENV = "FLEET_GENERATION";
715
+ /**
716
+ * The env name a fleet member's tenant scope travels under — the config renderer sets it to the scope this member's per-worker
717
+ * token binds (the same value, taken from the same place the token is issued for), and the engine reads it as the scope of its
718
+ * OWN identity, for one use: the scope it compares a {@link DrainRequest}'s `scope` against (a request that carries no scope
719
+ * reads as the default scope — the same default the engine's own absence reads as). Both sides import THIS constant; neither
720
+ * spells the name. One fact, one source: a lease's and the heartbeat's scope is the credential's (the credential decides it, see
721
+ * {@link ServingLeaseRequest} and {@link InstanceAnnouncement}; nothing in the engine's env does); the engine's own identity
722
+ * scope is the one the renderer gave in this env; that the two are the same value is the renderer's obligation.
723
+ * The engine never derives it from the token's format (a token is opaque to it, and a format guess would be a second source
724
+ * that can disagree with the scope the center binds). Absent from the engine's env ⇒ the default scope, {@link GLOBAL_SCOPE} (`api/scopes`) —
725
+ * the same default the center reads where nothing names a scope; this note does not restate the value. Like
726
+ * {@link FLEET_GENERATION_ENV} it is a reserved worker env name (`WorkerSpec.env` / `secretEnv` cannot set it).
727
+ */
728
+ export declare const FLEET_SCOPE_ENV = "FLEET_SCOPE";
715
729
  /**
716
730
  * The serving lease — the ONE fence between a superseded generation and the traffic it must stop taking. A fleet-member
717
731
  * engine renews it with the center; the center answers per the lifecycle object it holds for that instance. Three constants
package/dist/fleet.js CHANGED
@@ -474,6 +474,20 @@ export function readEngineSignal(health) {
474
474
  * ({@link EngineSignal}`.instanceId`), and env cannot carry it (see there).
475
475
  */
476
476
  export const FLEET_GENERATION_ENV = "FLEET_GENERATION";
477
+ /**
478
+ * The env name a fleet member's tenant scope travels under — the config renderer sets it to the scope this member's per-worker
479
+ * token binds (the same value, taken from the same place the token is issued for), and the engine reads it as the scope of its
480
+ * OWN identity, for one use: the scope it compares a {@link DrainRequest}'s `scope` against (a request that carries no scope
481
+ * reads as the default scope — the same default the engine's own absence reads as). Both sides import THIS constant; neither
482
+ * spells the name. One fact, one source: a lease's and the heartbeat's scope is the credential's (the credential decides it, see
483
+ * {@link ServingLeaseRequest} and {@link InstanceAnnouncement}; nothing in the engine's env does); the engine's own identity
484
+ * scope is the one the renderer gave in this env; that the two are the same value is the renderer's obligation.
485
+ * The engine never derives it from the token's format (a token is opaque to it, and a format guess would be a second source
486
+ * that can disagree with the scope the center binds). Absent from the engine's env ⇒ the default scope, {@link GLOBAL_SCOPE} (`api/scopes`) —
487
+ * the same default the center reads where nothing names a scope; this note does not restate the value. Like
488
+ * {@link FLEET_GENERATION_ENV} it is a reserved worker env name (`WorkerSpec.env` / `secretEnv` cannot set it).
489
+ */
490
+ export const FLEET_SCOPE_ENV = "FLEET_SCOPE";
477
491
  /**
478
492
  * The serving lease — the ONE fence between a superseded generation and the traffic it must stop taking. A fleet-member
479
493
  * engine renews it with the center; the center answers per the lifecycle object it holds for that instance. Three constants
package/dist/types.js CHANGED
@@ -17,7 +17,7 @@
17
17
  */
18
18
  import { z } from "zod";
19
19
  import { MAX_EXTRA_BODY_DEPTH } from "./hash.js"; // reconcile the extraBody depth cap with the hash cap (R18)
20
- import { DispatchPolicy, FLEET_GENERATION_ENV } from "./fleet.js"; // WorkerSpec.fleet.dispatch (scheduling framework D3)
20
+ import { DispatchPolicy, FLEET_GENERATION_ENV, FLEET_SCOPE_ENV } from "./fleet.js"; // WorkerSpec.fleet.dispatch (scheduling framework D3)
21
21
  import { carriesUserinfo, percentLayers } from "./secret-scan.js"; // package-private normalization + the userinfo judgement (not in the barrel)
22
22
  import { EXECUTION_LANES, EXECUTION_LANE_WORDS, MEMORY_ENGINE_BACKENDS, SESSION_BACKENDS, STORE_BACKENDS } from "./execution-lanes.js"; // 7.0.0 M-R2: the lane / store vocabulary (one exported table)
23
23
  import { LANE_ENV_OWNER, MIGRATION_SIGNPOST, isMigratableEnvKey, laneEnvSeat, laneRowOf } from "./execution-lane-index.js"; // package-private indexes derived from that table (not in the barrel)
@@ -1569,8 +1569,9 @@ const RESERVED_ENV_FIELD = Object.freeze({
1569
1569
  PG_DATABASE: "runtime.store.db.database", PG_PASSWORD: "secretRefs.dbPasswordEnv",
1570
1570
  // ── runtime.execution(7.0.0):车道词两键 + 车道表里每个名(派生,不手抄)
1571
1571
  REMOTE_EXEC: "runtime.execution.lane",
1572
- // ── fleet 成员身份:渲染器按该 worker 的代际写它,引擎读回并回声成 EngineSignal.generation —— 没有 WorkerSpec 字段能写它
1572
+ // ── fleet 成员身份两键:渲染器按该 worker 的代际写 generation(引擎读回并回声成 EngineSignal.generation)、按该 worker 令牌绑定的 scope 写 scope(引擎读作自身身份的 scope)—— 没有 WorkerSpec 字段能写它们
1573
1573
  [FLEET_GENERATION_ENV]: "(set by the config renderer from the worker's generation — not configurable)",
1574
+ [FLEET_SCOPE_ENV]: "(set by the config renderer from the worker's scope — not configurable)",
1574
1575
  REQUIRE_EXECUTION_LANE: "runtime.execution.lane (the renderer emits REQUIRE_EXECUTION_LANE=true for every declared lane)",
1575
1576
  ...Object.fromEntries([...LANE_ENV_OWNER].map(([name, owner]) => [name, laneEnvSeat(name, owner)])),
1576
1577
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sema-agent/settings-schema",
3
- "version": "8.2.0",
3
+ "version": "8.3.1",
4
4
  "description": "Sema settings schema — pure config contract (zod domains + effective-config + roster resolution + ref-integrity + remoteExec) shared by sema-registry, sema-server, and the TOC desktop/CLI. One source of truth. (Renamed at 1.0.0 from @sema-agent/registry-core — name and docs only, no behavior change; note the range moves 0.19.0 -> 1.0.0, so a dependency must become ^1.0.0. The old names @sema-agent/registry-core <=0.19.0 and @sema-ai/registry-core remain published, frozen, for migration.)",
5
5
  "type": "module",
6
6
  "license": "BUSL-1.1",