@sema-agent/settings-schema 8.2.0 → 8.3.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 CHANGED
@@ -6,6 +6,45 @@
6
6
  > `@sema-agent/registry-core`(更早还曾是 `@sema-ai/registry-core`)。本档 `0.19.0` 以下各节里的
7
7
  > 包名是**历史记录**,如实保留不追改。
8
8
 
9
+ ## 8.3.0 — 2026-10-08 — fleet 成员所属租户 scope 的 env 名常量 `FLEET_SCOPE_ENV`(additive)
10
+
11
+ > **判档 = minor**(README「发版纪律」:新键 / 新导出 = minor)。新导出 1 名(`FLEET_SCOPE_ENV`,`.` 与 `./fleet` 各 +1)与一个新保留 env 名;
12
+ > 无新运行期行为、无新 wire 形。导出面登记表只多名字行,无删、无改形。
13
+
14
+ ### 一、`FLEET_SCOPE_ENV = "FLEET_SCOPE"`
15
+
16
+ 与 `FLEET_GENERATION_ENV` 同一处登记(`src/fleet.ts`)。配置渲染器把引擎所属租户 scope 渲进这个 env(与该成员 per-worker 令牌绑定的 scope 同值);
17
+ 引擎读它作**自身身份的 scope**:排空口里拿 `DrainRequest.scope` 比对的那一格、续租体 `ServingLeaseRequest.scope`、心跳的 `?scope=`。
18
+ **一个事实一个源**:scope 由渲染器给,引擎**不从令牌的格式反推**(令牌对引擎是不透明串,格式猜测就是会与中心绑定值分歧的第二个源)。
19
+ **缺席 = 默认 scope**:值的唯一正本是 `GLOBAL_SCOPE`(`api/scopes`),与中心「请求里没有任何东西点名 scope」时读的是同一个缺省;常量头注只指向 `GLOBAL_SCOPE`,不复述字面量。
20
+
21
+ 它同时进了保留 env 名表(`RESERVED_WORKER_ENV`):`WorkerSpec.env` / `secretEnv` 设它即被拒,拒因指路「set by the config renderer from the worker's scope — not configurable」;
22
+ 渲染器是唯一写手。两侧都 import 常量,不各自拼字符串(整个 `src/` 里这个字面量只出现一次,有源码文本钉)。
23
+
24
+ ### 用户可见变化(谁受影响 / 怎么改 / 不改会怎样)
25
+
26
+ 1. **只用 schema / 读写 helper、不碰 fleet 的消费方**:零变化。
27
+ 2. **配置渲染器(管理面)**:给 fleet 成员渲 env 时,用 `FLEET_SCOPE_ENV` 写该成员 per-worker 令牌绑定的 scope;不改会怎样:引擎读不到 scope,按上面的缺省读法把自己当 `GLOBAL_SCOPE` ——
28
+ 非 `global` 租户的成员的身份 scope 与令牌绑定的 scope 不一致(续租体「在场必须相等」的规则下会被拒)。
29
+ 3. **引擎(server)**:读 `FLEET_SCOPE_ENV` 作自身身份的 scope,缺席用 `GLOBAL_SCOPE`;不要从令牌格式反推。
30
+ 4. **在 `WorkerSpec.env` / `secretEnv` 里设了 `FLEET_SCOPE` 的**:现在被拒(保留名),`migrateWorkersDomain` 对这样的行进 `refused`(8.2.0 起逐行终判)。
31
+ 这个名字是本版才出现的,本包此前没有它;设过的删掉那一行,由渲染器写。**持有 `RESERVED_WORKER_ENV` 总数 / 名单断言的测试**要 +1(本仓的历史钉就是两例)。
32
+
33
+ ### 红先 / 门
34
+
35
+ - 先写钉、对着改前的树跑红,再实现:`test/fleet-scope-env-8.3.0.test.ts` 七格(名逐字 + 两入口同值 + 字面量类型 / 保留表两直通面 + 拒因指路 + fleet 保留名恰两个 /
36
+ 字面量在 `src/` 里恰一处 / 头注指向 `GLOBAL_SCOPE` 且不复述字面量、写清三件事)+ 两处既有钉的新读数(`test/engine-signal-8.1.0.test.ts` 保留 fleet 名单、
37
+ `test/migration-signpost-7.0.1.test.ts` 7.0.0 之后的增词表)⇒ 8 红;实装后全绿。
38
+ - 变异自证(七个,每个都使预期那一格红):常量值改字 / 删保留表那一行 / `src/` 里多一处字面量 / 头注写进 `"global"` / 头注删掉 `GLOBAL_SCOPE` / 头注删掉「不从令牌格式反推」句 /
39
+ 导出面登记表删一行。
40
+ - 全量: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)。
41
+ - 导出面登记表(`docs/API-SURFACE.md`)重生成:`.` 与 `./fleet` 各 +1 名(`FLEET_SCOPE_ENV` — value),无删无改形。
42
+
43
+ ### 未闭环(如实)
44
+
45
+ - **本包只定名与缺省,不含渲染器与引擎的实现**:「渲染器写、引擎读」的两端落地在各自仓,本包没有取它们源码的通道,两端是否都 import 这只常量没有机器门看。
46
+ - **`FLEET_SCOPE` 与令牌绑定的 scope 同值是渲染器的义务**:本包不判、也判不了(令牌不在本包);不一致时按各口既有的规则表现为拒(续租体 scope 在场必须与令牌绑定的相等)/ 比对不上,而不是静默错归属。
47
+
9
48
  ## 8.2.0 — 2026-10-08 — 迁移器逐行终判(行为改真)· fleet 端点常量与活性路由组 · 心跳三键收型 · 中心能力位的承载键 · 两处注释改真
10
49
 
11
50
  > **判档 = minor**(README「发版纪律」:新键 / 新导出 = minor)。新导出 6 名(`LEASE_STATES` / `LeaseState` / `FLEET_PATHS` / `FLEET_LIVE_PATHS` /
package/README.md CHANGED
@@ -48,6 +48,10 @@ 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.2.0 → 8.3.0(additive,零动作可升)
52
+
53
+ `./fleet`(与主入口)多一个 env 名常量 `FLEET_SCOPE_ENV = "FLEET_SCOPE"`:成员所属租户 scope,渲染器写、引擎读(缺席 = `GLOBAL_SCOPE`),同时进 `RESERVED_WORKER_ENV`(`worker.env` / `secretEnv` 设它被拒)。逐条见 CHANGELOG 8.3.0。
54
+
51
55
  ## 升级指引:8.1.0 → 8.2.0(additive + 迁移器一处行为改真)
52
56
 
53
57
  **行为改真(一处)—— `migrateWorkersDomain` 终判逐行**:每一只有字符串名字的 worker(迁过的、没动的一样)都要读得进「只含它一行的 workers 域」
@@ -344,6 +348,8 @@ therefore shows only `zod`. Node built-ins only (`node:crypto`, `node:fs`).
344
348
  `/api/fleet/live/host-planner/{host}`) and the liveness group `FLEET_LIVE_PATHS` (heartbeat, serving lease, host planner), also
345
349
  shipped as `@sema-agent/settings-schema/fleet-paths.json` for tooling that cannot import TypeScript — and names the one carrier of
346
350
  the center's capability bits: `HEARTBEAT_REPLY_CAPABILITIES_KEY` (`"capabilities"`) on the heartbeat's 2xx reply.
351
+ **8.3.0** (additive) adds the env name `FLEET_SCOPE_ENV` (`"FLEET_SCOPE"`) — the tenant scope a fleet member belongs to, set by the
352
+ config renderer and read by the engine as its own identity's scope (absent ⇒ `GLOBAL_SCOPE`); a reserved worker env name.
347
353
  - **Declared-but-unvalidated keys** (`PARSE_TRANSPARENT_KEYS`, 2.0.0): the ONE registry of keys this package
348
354
  carries verbatim WITHOUT checking their shape, each row naming who does check it (the engine's ingest side or
349
355
  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.0
package/dist/fleet.d.ts CHANGED
@@ -712,6 +712,18 @@ 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: the scope it compares a {@link DrainRequest}'s `scope` against, sends as {@link ServingLeaseRequest}`.scope`, and
719
+ * names on its heartbeat ({@link InstanceAnnouncement}'s `?scope=`). Both sides import THIS constant; neither spells the name.
720
+ * One fact, one source: the scope is GIVEN by the renderer — the engine never derives it from the token's format (a token is
721
+ * opaque to it, and a format guess would be a second source that can disagree with the scope the center binds). Absent from
722
+ * the engine's env ⇒ the default scope, {@link GLOBAL_SCOPE} (`api/scopes`) — the same default the center reads where nothing
723
+ * names a scope; this note does not restate the value. Like {@link FLEET_GENERATION_ENV} it is a reserved worker env name
724
+ * (`WorkerSpec.env` / `secretEnv` cannot set it).
725
+ */
726
+ export declare const FLEET_SCOPE_ENV = "FLEET_SCOPE";
715
727
  /**
716
728
  * The serving lease — the ONE fence between a superseded generation and the traffic it must stop taking. A fleet-member
717
729
  * 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,18 @@ 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: the scope it compares a {@link DrainRequest}'s `scope` against, sends as {@link ServingLeaseRequest}`.scope`, and
481
+ * names on its heartbeat ({@link InstanceAnnouncement}'s `?scope=`). Both sides import THIS constant; neither spells the name.
482
+ * One fact, one source: the scope is GIVEN by the renderer — the engine never derives it from the token's format (a token is
483
+ * opaque to it, and a format guess would be a second source that can disagree with the scope the center binds). Absent from
484
+ * the engine's env ⇒ the default scope, {@link GLOBAL_SCOPE} (`api/scopes`) — the same default the center reads where nothing
485
+ * names a scope; this note does not restate the value. Like {@link FLEET_GENERATION_ENV} it is a reserved worker env name
486
+ * (`WorkerSpec.env` / `secretEnv` cannot set it).
487
+ */
488
+ export const FLEET_SCOPE_ENV = "FLEET_SCOPE";
477
489
  /**
478
490
  * The serving lease — the ONE fence between a superseded generation and the traffic it must stop taking. A fleet-member
479
491
  * 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.0",
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",