skill-family-harness-node 0.9.0 → 0.11.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
@@ -1,5 +1,47 @@
1
1
  # Changelog
2
2
 
3
+ <!-- release-skill:changelog:start version=0.11.0 locale=en baseline=sha256:e20f505e42ba45865bfee38a6c83671006bf20148d5d4e421ff7f443bcce260b -->
4
+ ## [0.11.0] - 2026-08-25
5
+
6
+ Harness 0.11.0 adds raw-byte subprocess sinks and exposes the bound-read root identity needed by host verification.
7
+
8
+ ### Added
9
+
10
+ - Extends superviseProcess with an exclusive, no-follow raw stdout/stderr sink that waits for stream close, queued writes, fsync, and close.
11
+ - Keeps the existing bound-read mechanism as the only root and member read authority.
12
+
13
+ ### Changed
14
+
15
+ - Carries the previously prepared host Profile closure into the lockstep 0.11.0 family release.
16
+
17
+ ### Upgrade Notes
18
+
19
+ The raw sink is mechanism-only; it does not create a second process runner, receipt state machine, or host-specific policy. The caller must exclusively control the sink namespace for the whole call; handle protection does not prove stable pathname or root identity.
20
+ <!-- release-skill:changelog:end version=0.11.0 locale=en -->
21
+
22
+
23
+ <!-- release-skill:changelog:start version=0.10.0 locale=en baseline=sha256:64a325d3e51604a8436b33df5a7f617f9aab1f1e02adb32ccb58316aa5c5eab1 -->
24
+ ## [0.10.0] - 2026-08-24
25
+
26
+ Harness 0.10.0 adds canonical entrypoints, reuses existing host mechanisms, and adds read-only peer adapter verification from real directories.
27
+
28
+ ### Added
29
+
30
+ - Adds skill-family-harness-node/quickstart-profile and skill-family-harness-node/rename-directory-no-replace canonical exports.
31
+ - Reuses filesystem-root binding, strict no-replace publication, atomic replacement, and existing build digests for the Kit's local host install/update path.
32
+ - Adds `verifyPeerAdapterDirectories`, which re-enumerates peer roots and verifies common closure, byte digests, standard manifests, and complete logical mappings without writing them.
33
+
34
+ ### Changed
35
+
36
+ - Keeps each historical candidate export as a same-source migration alias and leaves the mechanism registry unchanged.
37
+ - Keeps validate-many-by-schema-id and its error semantics unchanged while managed Bundles accept historical and canonical Schema IDs through the same validator.
38
+
39
+ ### Upgrade Notes
40
+
41
+ Update all three exact pins to 0.10.0 and migrate historical candidate imports and Schema IDs once to canonical identities. The low-level no-replace primitive remains distinct from the stable fixed-set-publication API; choose the contract that matches the use case.
42
+ <!-- release-skill:changelog:end version=0.10.0 locale=en -->
43
+
44
+
3
45
  <!-- release-skill:changelog:start version=0.9.0 locale=en baseline=sha256:e5f4e3bb6ab343e4ccfb7dcdbba8b9ab4576b4cf285c30bb3597d577be5eb391 -->
4
46
  ## [0.9.0] - 2026-08-24
5
47
 
@@ -1,5 +1,47 @@
1
1
  # 变更日志
2
2
 
3
+ <!-- release-skill:changelog:start version=0.11.0 locale=zh-CN baseline=sha256:51776685eead52118cc98ee47b8ac8990650a493d60567002133ff74e1c779c6 -->
4
+ ## [0.11.0] - 2026-08-25
5
+
6
+ Harness 0.11.0 增加原始字节子进程输出 sink,并暴露真实宿主验证所需的受保护根身份。
7
+
8
+ ### 新增
9
+
10
+ - 为 superviseProcess 增加独占、禁止跟随符号链接的 stdout/stderr 原始字节 sink,并等待流关闭、排队写入、fsync 与 close。
11
+ - 继续把既有 bound-read 机制作为唯一根目录与成员读取权威。
12
+
13
+ ### 变更
14
+
15
+ - 把此前准备好的宿主 Profile 闭包并入 0.11.0 三包锁步交付。
16
+
17
+ ### 升级说明
18
+
19
+ 原始 sink 只提供机制,不建立第二个进程 runner、收据状态机或宿主专属策略。调用方必须在整个调用期间独占 sink 命名空间;句柄保护不证明 pathname 或根目录身份始终不变。
20
+ <!-- release-skill:changelog:end version=0.11.0 locale=zh-CN -->
21
+
22
+
23
+ <!-- release-skill:changelog:start version=0.10.0 locale=zh-CN baseline=sha256:d8e27cce0c3e97b7d10b6f88fad4df66c8d527d3d6a2fb165f8c909612ff9f19 -->
24
+ ## [0.10.0] - 2026-08-24
25
+
26
+ Harness 0.10.0 增加规范入口,复用既有宿主机制,并从真实目录提供同级适配器只读验证。
27
+
28
+ ### 新增
29
+
30
+ - 新增 skill-family-harness-node/quickstart-profile 与 skill-family-harness-node/rename-directory-no-replace 规范导出。
31
+ - 复用 filesystem-root binding、严格不替换发布、原子替换和既有构建摘要,支持 Kit 的本地宿主 install/update。
32
+ - 新增 `verifyPeerAdapterDirectories`,重新枚举 peer 根目录,验证共同闭包、逐字节摘要、标准 manifest 和完整 logicalMappings,不写入目录。
33
+
34
+ ### 变更
35
+
36
+ - 历史 candidate 导出继续作为同源迁移别名,机制登记表不变。
37
+ - validate-many-by-schema-id 及错误语义不变;受管 Bundle 让历史与规范 Schema ID 共用同一 validator。
38
+
39
+ ### 升级说明
40
+
41
+ 消费者应把三个包的精确 pin 更新到 0.10.0,并把历史 candidate 导入和 Schema ID 一次迁移到规范身份。低层不替换原语仍不同于稳定 fixed-set-publication API,消费者按所需合同选择。
42
+ <!-- release-skill:changelog:end version=0.10.0 locale=zh-CN -->
43
+
44
+
3
45
  <!-- release-skill:changelog:start version=0.9.0 locale=zh-CN baseline=sha256:7f3ff503831b8fb52f20293a4af4003ff116b7f9d820a0c2da1bd34a0b261248 -->
4
46
  ## [0.9.0] - 2026-08-24
5
47
 
package/README.md CHANGED
@@ -4,28 +4,27 @@
4
4
 
5
5
  # skill-family-harness-node
6
6
 
7
- <!-- release-skill:release-version: 0.9.0 -->
7
+ <!-- release-skill:release-version: 0.11.0 -->
8
8
 
9
9
  The **single default Node implementation** of the Contracts mechanism protocol. This is a thin runtime: it only implements the mechanism protocol, introduces no business semantics, and does not provide a second-language implementation.
10
10
 
11
11
  <!-- release-skill:managed:start id=latest-release -->
12
- **0.9.0** (2026-08-24)
12
+ **0.11.0** (2026-08-25)
13
13
 
14
- Harness 0.9.0 adds stable identity-bound reads and fixed-set no-replace publication with a fixed four-platform native closure.
14
+ Harness 0.11.0 adds raw-byte subprocess sinks and exposes the bound-read root identity needed by host verification.
15
15
 
16
16
  **Added**
17
17
 
18
- - Adds createFilesystemRootBinding and readFileBound with handle-relative no-follow acquisition and optional byte digest guarding.
19
- - Adds the stable fixed-set-publication subpath with native no-replace publication and terminal indeterminate receipts.
20
- - Adds the candidate validate-many-by-schema-id mechanism to the existing Quickstart dispatcher.
18
+ - Extends superviseProcess with an exclusive, no-follow raw stdout/stderr sink that waits for stream close, queued writes, fsync, and close.
19
+ - Keeps the existing bound-read mechanism as the only root and member read authority.
21
20
 
22
21
  **Changed**
23
22
 
24
- - Keeps protected deletion excluded and preserves the existing 21-item capability registry.
23
+ - Carries the previously prepared host Profile closure into the lockstep 0.11.0 family release.
25
24
 
26
25
  **Upgrade Notes**
27
26
 
28
- Pin Contracts, Harness, and Engineering Kit to exactly 0.9.0. Batch validation and the Quickstart Bundle remain candidate; filesystem binding and fixed-set publication are stable.
27
+ The raw sink is mechanism-only; it does not create a second process runner, receipt state machine, or host-specific policy. The caller must exclusively control the sink namespace for the whole call; handle protection does not prove stable pathname or root identity.
29
28
  <!-- release-skill:managed:end id=latest-release -->
30
29
 
31
30
  ## Problem It Solves
@@ -34,19 +33,19 @@ Contracts defines "what should be", and the Harness turns that into "can be safe
34
33
 
35
34
  ## Core Mental Model
36
35
 
37
- The Harness consumes `skill-family-contracts` (a workspace dependency), reusing its dialect-routed Ajv validator, Kernel Protocol, frozen error codes, and fixtures; it does not copy protocol definitions or re-interpret the Schema. It only implements mechanisms: Schema validation, atomic writes, path containment, temporary workspaces, resource closure, the operation-request → operation-result pipeline, and business-neutral event logging with derived snapshots. Explicitly excluded: business semantics, task orchestration, Git writes, model calls, remote networking, and publish state. See `HARNESS_EXCLUSIONS`.
36
+ The Harness consumes `skill-family-contracts` (a workspace dependency), reusing its dialect-routed Ajv validator, Kernel Protocol, frozen error codes, and fixtures; it does not copy protocol definitions or re-interpret the Schema. It only implements mechanisms: Schema validation, atomic writes, path containment, temporary workspaces, resource closure, bounded process supervision, the operation-request → operation-result pipeline, and business-neutral event logging with derived snapshots. Explicitly excluded: business semantics, task orchestration, Git writes, model calls, remote networking, and publish state. See `HARNESS_EXCLUSIONS`.
38
37
 
39
38
  ## Installation and Minimal Example
40
39
 
41
40
  ```sh
42
- npm install skill-family-harness-node@0.9.0
41
+ npm install skill-family-harness-node@0.11.0
43
42
  npm info skill-family-harness-node --help
44
43
  ```
45
44
 
46
45
  The minimal example shows validating a contract document inside Node:
47
46
 
48
47
  ```js
49
- // Run from an empty directory: npm install skill-family-harness-node@0.9.0
48
+ // Run from an empty directory: npm install skill-family-harness-node@0.11.0
50
49
  import { validateContractDocument } from "skill-family-harness-node";
51
50
 
52
51
  const document = {
@@ -75,12 +74,12 @@ import {
75
74
  createQuickstartTask,
76
75
  wrapQuickstartResult,
77
76
  verifyQuickstartExchange,
78
- } from "skill-family-harness-node/candidate/quickstart-profile";
77
+ } from "skill-family-harness-node/quickstart-profile";
79
78
  ```
80
79
 
81
80
  The v2 mechanism recomputes the bytes of every path-backed output and evidence Resource. It also rejects duplicate Resource ids, correlation drift, a changed Task digest, and incomplete or mismatched evidence bindings. It does not perform a domain audit, choose a method, retry work, or own lifecycle state.
82
81
 
83
- The subpath is public but **not stable** and may change or be removed in a later minor release. Pin exactly `0.4.0` for v2; integrations that still produce candidate v1 exchanges must stay pinned to exactly `0.2.1`.
82
+ The capability remains **candidate**. Pin all three Foundation packages exactly while evaluating it. Version 0.10.0 adds the canonical path above; the historical `/candidate/quickstart-profile` path remains a same-source migration alias. Migrate once to the canonical path. A later stable promotion will not require another import or same-byte Bundle rebuild. Integrations that still produce candidate v1 exchanges must stay pinned to exactly `0.2.1`.
84
83
 
85
84
  ## Typical Use Cases
86
85
 
@@ -106,6 +105,7 @@ The subpath is public but **not stable** and may change or be removed in a later
106
105
  | `writeFileAtomic` | Atomic write: leaves no half-written artifact on failure (temp file + fsync + rename). |
107
106
  | `TemporaryWorkspace` / `createTemporaryWorkspace` / `withTemporaryWorkspace` | Auto-cleanup temporary workspace, cleaned up even on exception paths. |
108
107
  | `digestBytes` / `computeResourceClosure` / `closureContains` | Resource closure and deterministic sha256 digest. |
108
+ | `superviseProcess` / `validateTimeoutPolicy` | The single bounded subprocess supervisor. Its 0.11.0 `rawSink` option writes raw stdout/stderr bytes only to a fresh canonical private root, then waits for child/stream close, queued writes, fsync, and handle close. The caller must exclusively control the sink namespace for the entire call; handle protection does not prove stable pathname/root identity. |
109
109
  | `parseRequest` / `processRequest` | Parse `operation-request`, output terminal `operation-result`. |
110
110
  | `validateReportModel` / `renderReportMarkdown` / `buildBinding` / `checkReport` | Consume a Contracts-validated report model, deterministically render neutral Markdown, and verify source/result/report binding; does not interpret business output. |
111
111
  | `normalizeAdapterSource` / `buildAdapterClosure` / `verifyAdapterBuildManifest` / `materializeAdapterBuild` | Generic text-source closure, full-manifest digest re-verification, and atomic landing of the target set; specific Profile/driver is not in the Harness. |
@@ -143,7 +143,7 @@ Comparison is based on the canonical root after `realpath`, avoiding misjudgment
143
143
 
144
144
  ## Testing
145
145
 
146
- `node --test` covers: full Contracts fixture replay, security negative cases, atomic-failure paths, temporary workspaces, closure determinism, report fact binding and Markdown injection, host manifest/path/command trust, and state-store crashes, concurrency, corruption, fencing, explicit recovery, symlinks, hard links, and FIFO negative cases.
146
+ `node --test` covers: full Contracts fixture replay, security negative cases, atomic-failure paths, temporary workspaces, closure determinism, raw sink delayed-stream and failure paths, report fact binding and Markdown injection, host manifest/path/command trust, and state-store crashes, concurrency, corruption, fencing, explicit recovery, symlinks, hard links, and FIFO negative cases.
147
147
 
148
148
  ## Troubleshooting
149
149
 
@@ -167,7 +167,7 @@ Mechanism failures uniformly throw `SFC2004` (EXECUTION_FAILED), with `details.k
167
167
  ### Do not use when
168
168
 
169
169
  - You need to put file-selection business rules into the Foundation (business rules are owned by the caller).
170
- - You need host apply/install/update/uninstall, a full Qoder driver, or binary adapter source (explicitly unsupported).
170
+ - You need host identity policy, host drivers, remote publication, uninstall deletion, a full Qoder driver, or binary adapter source (explicitly unsupported).
171
171
  - You need domain audit semantics, retry orchestration, or a compatibility-frozen Quickstart API.
172
172
 
173
173
  ### Capability selection
@@ -177,9 +177,10 @@ Mechanism failures uniformly throw `SFC2004` (EXECUTION_FAILED), with `details.k
177
177
  - `foundation.harness.atomic-write`: atomic write within contained paths, rolling back on failure.
178
178
  - `foundation.harness.temporary-workspace`: auto-cleanup temporary workspace.
179
179
  - `foundation.harness.resource-closure`: deterministic resource closure and sha256 digest.
180
+ - `foundation.harness.supervise-process`: one bounded subprocess supervisor; raw evidence capture remains mechanism-only and does not produce a receipt or domain verdict.
180
181
  - `foundation.harness.request-processing`: operation-request → terminal operation-result.
181
182
  - `foundation.harness.report`: report-model validation/render/binding/check.
182
- - `foundation.harness.host-adapter`: adapter source closure/build/materialize and version probe.
183
+ - `foundation.harness.host-adapter`: adapter source closure/build/materialize, version probe, and read-only peer adapter verification.
183
184
  - `foundation.harness.state-store`: append-only events, hash chain, snapshots, and lock recovery.
184
185
  - `foundation.harness.errors`: mechanism error types and stable error classes.
185
186
  - `foundation.harness.quickstart-profile-candidate`: exact-version observation/task/result construction and binding verification.
@@ -208,16 +209,17 @@ Mechanism failures uniformly throw `SFC2004` (EXECUTION_FAILED), with `details.k
208
209
 
209
210
  - Event meaning and reducer transitions remain consumer-owned; state-store only provides the base.
210
211
  - Only text adapter source (utf8) is supported; binary projection is not supported.
212
+ - `verifyPeerAdapterDirectories` enumerates and reads two or more real peer roots, reuses bound-read/path containment/closure/manifest primitives, and fails closed on symlinks, escapes, byte drift, member drift, or incomplete mappings.
211
213
 
212
214
  ### Route elsewhere when
213
215
 
214
216
  - Business state machine / terminal states: route to loop-agent.
215
- - Host apply: explicitly unsupported.
217
+ - Host identity policy, host drivers, and lifecycle authorization belong to Engineering Kit; Harness supplies only the reusable bound-read, strict-publication, atomic-write, closure, and probe mechanisms.
216
218
  - Domain audit semantics: route to a standalone audit consumer.
217
219
 
218
220
  ### Machine-readable sources
219
221
 
220
222
  - Public capability catalog: [`capability-catalog.json`](https://ifoohoo.github.io/skill-family-engineering-kit/agents/capability-catalog.json) (`foundation.harness.*` entries).
221
223
  - Package-local source: `src/*.mjs`.
222
- - Package-local candidate source: `candidate/quickstart-profile.mjs`; public import: `skill-family-harness-node/candidate/quickstart-profile`.
224
+ - Package-local candidate source: `candidate/quickstart-profile.mjs`; canonical public import: `skill-family-harness-node/quickstart-profile`; historical migration alias: `skill-family-harness-node/candidate/quickstart-profile`.
223
225
  <!-- agent-quick-reference:end -->
package/README.zh-CN.md CHANGED
@@ -5,28 +5,27 @@
5
5
 
6
6
  # skill-family-harness-node
7
7
 
8
- <!-- release-skill:release-version: 0.9.0 -->
8
+ <!-- release-skill:release-version: 0.11.0 -->
9
9
 
10
10
  Contracts 机制协议的**唯一默认 Node 实现**。这是一个薄运行时(thin runtime):只实现机制协议,不引入业务语义,不做第二语言实现。
11
11
 
12
12
  <!-- release-skill:managed:start id=latest-release -->
13
- **0.9.0** (2026-08-24)
13
+ **0.11.0** (2026-08-25)
14
14
 
15
- Harness 0.9.0 新增稳定身份绑定读取与固定集合不替换发布,并携带固定四平台原生闭包。
15
+ Harness 0.11.0 增加原始字节子进程输出 sink,并暴露真实宿主验证所需的受保护根身份。
16
16
 
17
17
  **新增**
18
18
 
19
- - 新增 createFilesystemRootBindingreadFileBound,使用句柄相对不跟随符号链接获取,并支持可选字节摘要保护。
20
- - 新增稳定 fixed-set-publication 子路径,使用原生不替换发布并保留终态 indeterminate 回执。
21
- - 在既有 Quickstart dispatcher 中增加 validate-many-by-schema-id candidate 机制。
19
+ - superviseProcess 增加独占、禁止跟随符号链接的 stdout/stderr 原始字节 sink,并等待流关闭、排队写入、fsync close。
20
+ - 继续把既有 bound-read 机制作为唯一根目录与成员读取权威。
22
21
 
23
22
  **变更**
24
23
 
25
- - 身份保护删除继续排除,既有 21 项能力登记保持不变。
24
+ - 把此前准备好的宿主 Profile 闭包并入 0.11.0 三包锁步交付。
26
25
 
27
26
  **升级说明**
28
27
 
29
- 三个 Foundation 包必须精确锁定 0.9.0。批量校验与 Quickstart Bundle 仍为 candidate;文件系统绑定与固定集合发布为 stable。
28
+ 原始 sink 只提供机制,不建立第二个进程 runner、收据状态机或宿主专属策略。调用方必须在整个调用期间独占 sink 命名空间;句柄保护不证明 pathname 或根目录身份始终不变。
30
29
  <!-- release-skill:managed:end id=latest-release -->
31
30
 
32
31
  ## 解决的问题
@@ -40,14 +39,14 @@ Harness 消费 `skill-family-contracts`(工作区依赖),复用其方言
40
39
  ## 安装和最小示例
41
40
 
42
41
  ```sh
43
- npm install skill-family-harness-node@0.9.0
42
+ npm install skill-family-harness-node@0.11.0
44
43
  npm info skill-family-harness-node --help
45
44
  ```
46
45
 
47
46
  最小示例演示在 Node 内校验一份契约文档:
48
47
 
49
48
  ```js
50
- // 从空目录运行:npm install skill-family-harness-node@0.9.0
49
+ // 从空目录运行:npm install skill-family-harness-node@0.11.0
51
50
  import { validateContractDocument } from "skill-family-harness-node";
52
51
 
53
52
  const document = {
@@ -76,12 +75,12 @@ import {
76
75
  createQuickstartTask,
77
76
  wrapQuickstartResult,
78
77
  verifyQuickstartExchange,
79
- } from "skill-family-harness-node/candidate/quickstart-profile";
78
+ } from "skill-family-harness-node/quickstart-profile";
80
79
  ```
81
80
 
82
81
  v2 机制会重算每个 path-backed output 和 evidence Resource 的真实字节摘要,并拒绝重复 Resource id、correlation 漂移、Task digest 变化,以及缺失或错配的 evidence binding。它不执行领域审计,不选择 method,不编排重试,也不拥有生命周期状态。
83
82
 
84
- 该子路径公开但**不稳定**,后续小版本可以修改或移除。使用 v2 时应精确锁定 `0.4.0`;仍生产 candidate v1 交换的接入必须继续精确锁定 `0.2.1`。
83
+ 该能力仍是 **candidate**,评估时必须精确锁定三个 Foundation 包。0.10.0 新增上面的规范入口;历史 `/candidate/quickstart-profile` 入口作为同源迁移别名继续可用。消费者迁移一次后,未来晋升 stable 不再切入口或重建相同 Bundle。仍生产 candidate v1 交换的接入必须继续精确锁定 `0.2.1`。
85
84
 
86
85
  ## 典型使用场景
87
86
 
@@ -107,6 +106,7 @@ v2 机制会重算每个 path-backed output 和 evidence Resource 的真实字
107
106
  | `writeFileAtomic` | 原子写:失败不留半成品(临时文件 + fsync + rename)。 |
108
107
  | `TemporaryWorkspace` / `createTemporaryWorkspace` / `withTemporaryWorkspace` | 自动清理的临时工作区,异常路径也清理。 |
109
108
  | `digestBytes` / `computeResourceClosure` / `closureContains` | 资源闭包与确定性 sha256 摘要。 |
109
+ | `superviseProcess` / `validateTimeoutPolicy` | 唯一的受约束子进程监督器。0.11.0 的 `rawSink` 只向 fresh canonical 私有根写原始 stdout/stderr 字节,并等待子进程、流、排队写入、fsync 与句柄全部关闭。调用方必须在整个调用期间独占 sink 命名空间;句柄保护不证明 pathname 或根目录身份始终不变。 |
110
110
  | `parseRequest` / `processRequest` | 解析 `operation-request`,输出终态 `operation-result`。 |
111
111
  | `validateReportModel` / `renderReportMarkdown` / `buildBinding` / `checkReport` | 消费经 Contracts 验证的 report model,确定性渲染中性 Markdown 并校验来源/结果/报告绑定;不解释业务输出。 |
112
112
  | `normalizeAdapterSource` / `buildAdapterClosure` / `verifyAdapterBuildManifest` / `materializeAdapterBuild` | 通用文本 source closure、manifest 全摘要复验和目标集合原子落盘;具体 Profile/driver 不在 Harness。 |
@@ -168,7 +168,7 @@ v2 机制会重算每个 path-backed output 和 evidence Resource 的真实字
168
168
  ### Do not use when
169
169
 
170
170
  - 需要把文件选择的业务规则放入 Foundation(业务规则由调用方拥有)。
171
- - 需要 host apply/install/update/uninstall、Qoder 完整 driver 或二进制 adapter source(明确 unsupported)。
171
+ - 需要宿主身份策略、宿主 driver、远端发布、删除式 uninstall、Qoder 完整 driver 或二进制 adapter source(明确 unsupported)。
172
172
  - 需要领域审计语义、重试编排或兼容性已冻结的 Quickstart API。
173
173
 
174
174
  ### Capability selection
@@ -178,6 +178,7 @@ v2 机制会重算每个 path-backed output 和 evidence Resource 的真实字
178
178
  - `foundation.harness.atomic-write`:受收容路径内原子写,失败回滚。
179
179
  - `foundation.harness.temporary-workspace`:自动清理的临时工作区。
180
180
  - `foundation.harness.resource-closure`:确定性资源闭包与 sha256 摘要。
181
+ - `foundation.harness.supervise-process`:监督一个有界子进程;原始证据收集仍只提供机制,不产生收据或领域结论。
181
182
  - `foundation.harness.request-processing`:operation-request → 终态 operation-result。
182
183
  - `foundation.harness.report`:report-model 校验/渲染/绑定/检查。
183
184
  - `foundation.harness.host-adapter`:adapter source closure/build/materialize 与版本探测。
@@ -213,12 +214,12 @@ v2 机制会重算每个 path-backed output 和 evidence Resource 的真实字
213
214
  ### Route elsewhere when
214
215
 
215
216
  - 业务状态机/终态:转 loop-agent。
216
- - 宿主 apply:明确 unsupported。
217
+ - 宿主身份策略、宿主 driver 和生命周期授权归 Engineering Kit;Harness 只提供可复用的绑定读取、严格发布、原子写、闭包和探测机制。
217
218
  - 领域审计语义:转独立审计消费者。
218
219
 
219
220
  ### Machine-readable sources
220
221
 
221
222
  - 公开能力目录:[`capability-catalog.json`](https://ifoohoo.github.io/skill-family-engineering-kit/agents/capability-catalog.json)(`foundation.harness.*` 条目)。
222
223
  - 包内源:`src/*.mjs`。
223
- - 包内 Candidate 源:`candidate/quickstart-profile.mjs`;公共导入:`skill-family-harness-node/candidate/quickstart-profile`。
224
+ - 包内 Candidate 源:`candidate/quickstart-profile.mjs`;规范公共导入:`skill-family-harness-node/quickstart-profile`;历史迁移别名:`skill-family-harness-node/candidate/quickstart-profile`。
224
225
  <!-- agent-quick-reference:end -->
@@ -6,7 +6,7 @@ import {
6
6
  validateHarnessSurfaceDetectorDocument,
7
7
  validateHarnessSurfaceInventoryDocument,
8
8
  validateQuickstartProfileDocument,
9
- } from "skill-family-contracts/candidate/quickstart-profile";
9
+ } from "skill-family-contracts/quickstart-profile";
10
10
  import { lstat, readdir, readFile, realpath } from "node:fs/promises";
11
11
  import path from "node:path";
12
12
  import { fileURLToPath } from "node:url";
@@ -16,6 +16,7 @@ import {
16
16
  import path from "node:path";
17
17
  import process from "node:process";
18
18
  import { fileURLToPath } from "node:url";
19
+ import { run as runReceiptAssembler } from "../../src/native/receipt-assembler-core.mjs";
19
20
 
20
21
  const ROOT = path.dirname(fileURLToPath(import.meta.url));
21
22
  const SOURCE = path.join(ROOT, "addon", "rename_directory_no_replace.c");
@@ -128,60 +129,35 @@ function replaceManaged(outputRoot, stage, names) {
128
129
  }
129
130
 
130
131
  export function assemble(argv) {
131
- const args = parseArgs(argv);
132
- const sourceSha = sha256(readFileSync(SOURCE));
133
- const inputs = [];
134
- for (const key of KEYS) {
135
- const receiptPath = path.resolve(args.get(`${key}-receipt`));
136
- const binaryPath = path.resolve(args.get(`${key}-binary`));
137
- const receipt = readJson(receiptPath);
138
- const facts = receiptFacts(key, receipt.document);
139
- if (facts.sourceSha !== sourceSha) throw new Error(`${key}: receipt source SHA与current native source不匹配`);
140
- verifyBinary(key, binaryPath, facts);
141
- inputs.push({ key, receiptSha: receipt.sha256, binaryPath, ...facts });
142
- }
143
- if (new Set(inputs.map((entry) => entry.sourceSha)).size !== 1) throw new Error("四份receipt的native source SHA不一致");
144
-
145
- const outputRoot = path.resolve(args.get("output-root"));
146
- const stage = mkdtempSync(path.join(path.dirname(outputRoot), ".native-prebuild-stage-"));
147
- const entries = [];
148
- try {
149
- for (const input of inputs) {
150
- const filename = `rename_directory_no_replace.${input.key}.node`;
151
- const relative = `prebuilds/${input.key}/${filename}`;
152
- const destination = path.join(stage, relative);
153
- mkdirSync(path.dirname(destination), { recursive: true });
154
- copyFileSync(input.binaryPath, destination);
155
- chmodSync(destination, MODE);
156
- entries.push({ platformKey: input.key, ...CONFIG[input.key], binary: relative, mode: MODE, sha256: input.binarySha, napi: Number(NAPI), exports: EXPORTS });
157
- }
158
- const manifest = { schemaVersion: 2, kind: "skill-family.rename-directory-no-replace-prebuild-manifest", status: "candidate", entries };
159
- const sbom = {
132
+ return runReceiptAssembler({
133
+ keys: KEYS,
134
+ platforms: CONFIG,
135
+ sourceFile: SOURCE,
136
+ mode: MODE,
137
+ napi: NAPI,
138
+ exports: EXPORTS,
139
+ binaryName: (key) => `rename_directory_no_replace.${key}.node`,
140
+ extractFacts: (key, receipt) => receiptFacts(key, receipt),
141
+ manifestDoc: (entries) => ({ schemaVersion: 2, kind: "skill-family.rename-directory-no-replace-prebuild-manifest", status: "candidate", entries }),
142
+ sbomDoc: (sourceSha, entries) => ({
160
143
  schemaVersion: 1,
161
144
  kind: "skill-family.rename-directory-no-replace-prebuild-sbom",
162
145
  status: "candidate",
163
146
  source: { path: EXPECTED_SOURCE, sha256: sourceSha },
164
147
  files: entries.map(({ platformKey, binary, sha256: digest }) => ({ platformKey, binary, sha256: digest, license: "Apache-2.0" })),
165
- };
166
- const releaseReceipt = {
148
+ }),
149
+ releaseReceiptDoc: ({ inputs, sourceSha, manifestSha }) => ({
167
150
  schemaVersion: 1,
168
151
  kind: "skill-family.rename-directory-no-replace-prebuild-release-receipt",
169
152
  status: "candidate",
170
153
  node: { version: NODE_VERSION, napi: Number(NAPI) },
171
154
  source: { path: EXPECTED_SOURCE, sha256: sourceSha },
172
- manifestSha256: sha256(Buffer.from(canonical(manifest))),
155
+ manifestSha256: manifestSha,
173
156
  inputs: inputs.map(({ key, receiptSha, binarySha }) => ({ platformKey: key, platformReceiptSha256: receiptSha, binarySha256: binarySha })),
174
- };
175
- writeFileSync(path.join(stage, "prebuild-manifest.json"), canonical(manifest));
176
- writeFileSync(path.join(stage, "prebuild-sbom.json"), canonical(sbom));
177
- writeFileSync(path.join(stage, "prebuild-release-receipt.json"), canonical(releaseReceipt));
178
- writeFileSync(path.join(stage, "NOTICE"), "Skill Family Foundation rename-directory-no-replace candidate prebuilds\nCompiled from the Apache-2.0 source identified in prebuild-sbom.json.\nNo fallback implementation or stable-registry activation is included.\n");
179
- const managed = ["prebuilds", "prebuild-manifest.json", "prebuild-sbom.json", "prebuild-release-receipt.json", "NOTICE"];
180
- replaceManaged(outputRoot, stage, managed);
181
- return releaseReceipt;
182
- } finally {
183
- rmSync(stage, { recursive: true, force: true });
184
- }
157
+ }),
158
+ noticeText: "Skill Family Foundation rename-directory-no-replace candidate prebuilds\nCompiled from the Apache-2.0 source identified in prebuild-sbom.json.\nNo fallback implementation or stable-registry activation is included.\n",
159
+ managedNames: ["prebuilds", "prebuild-manifest.json", "prebuild-sbom.json", "prebuild-release-receipt.json", "NOTICE"],
160
+ }, argv);
185
161
  }
186
162
 
187
163
  if (process.argv[1] === fileURLToPath(import.meta.url)) {
package/package.json CHANGED
@@ -10,7 +10,7 @@
10
10
  "dependencies": {
11
11
  "@pnpm/lockfile.fs": "1001.1.35",
12
12
  "ipaddr.js": "2.5.0",
13
- "skill-family-contracts": "0.9.0",
13
+ "skill-family-contracts": "0.11.0",
14
14
  "yaml": "2.9.0"
15
15
  },
16
16
  "description": "Thin Node.js mechanism runtime for Skill Family engineering contracts.",
@@ -22,7 +22,9 @@
22
22
  ".": "./src/index.mjs",
23
23
  "./candidate/quickstart-profile": "./candidate/quickstart-profile.mjs",
24
24
  "./candidate/rename-directory-no-replace": "./candidate/rename-directory-no-replace/rename-directory-no-replace.mjs",
25
- "./fixed-set-publication": "./src/fixed-set-publication.mjs"
25
+ "./fixed-set-publication": "./src/fixed-set-publication.mjs",
26
+ "./quickstart-profile": "./candidate/quickstart-profile.mjs",
27
+ "./rename-directory-no-replace": "./candidate/rename-directory-no-replace/rename-directory-no-replace.mjs"
26
28
  },
27
29
  "files": [
28
30
  "src",
@@ -46,7 +48,7 @@
46
48
  "url": "https://github.com/ifoohoo/skill-family-harness-node.git"
47
49
  },
48
50
  "type": "module",
49
- "version": "0.9.0",
51
+ "version": "0.11.0",
50
52
  "scripts": {
51
53
  "check": "node --test",
52
54
  "test": "node --test"
@@ -0,0 +1,25 @@
1
+ version: 0.10.0
2
+ date: 2026-08-24
3
+ locales:
4
+ en:
5
+ summary: Harness 0.10.0 adds canonical entrypoints, reuses existing host mechanisms, and adds read-only peer adapter verification from real directories.
6
+ changes:
7
+ added:
8
+ - Adds skill-family-harness-node/quickstart-profile and skill-family-harness-node/rename-directory-no-replace canonical exports.
9
+ - Reuses filesystem-root binding, strict no-replace publication, atomic replacement, and existing build digests for the Kit's local host install/update path.
10
+ - Adds `verifyPeerAdapterDirectories`, which re-enumerates peer roots and verifies common closure, byte digests, standard manifests, and complete logical mappings without writing them.
11
+ changed:
12
+ - Keeps each historical candidate export as a same-source migration alias and leaves the mechanism registry unchanged.
13
+ - Keeps validate-many-by-schema-id and its error semantics unchanged while managed Bundles accept historical and canonical Schema IDs through the same validator.
14
+ upgradeNotes: Update all three exact pins to 0.10.0 and migrate historical candidate imports and Schema IDs once to canonical identities. The low-level no-replace primitive remains distinct from the stable fixed-set-publication API; choose the contract that matches the use case.
15
+ zh-CN:
16
+ summary: Harness 0.10.0 增加规范入口,复用既有宿主机制,并从真实目录提供同级适配器只读验证。
17
+ changes:
18
+ added:
19
+ - 新增 skill-family-harness-node/quickstart-profile 与 skill-family-harness-node/rename-directory-no-replace 规范导出。
20
+ - 复用 filesystem-root binding、严格不替换发布、原子替换和既有构建摘要,支持 Kit 的本地宿主 install/update。
21
+ - 新增 `verifyPeerAdapterDirectories`,重新枚举 peer 根目录,验证共同闭包、逐字节摘要、标准 manifest 和完整 logicalMappings,不写入目录。
22
+ changed:
23
+ - 历史 candidate 导出继续作为同源迁移别名,机制登记表不变。
24
+ - validate-many-by-schema-id 及错误语义不变;受管 Bundle 让历史与规范 Schema ID 共用同一 validator。
25
+ upgradeNotes: 消费者应把三个包的精确 pin 更新到 0.10.0,并把历史 candidate 导入和 Schema ID 一次迁移到规范身份。低层不替换原语仍不同于稳定 fixed-set-publication API,消费者按所需合同选择。
@@ -0,0 +1,21 @@
1
+ version: 0.11.0
2
+ date: 2026-08-25
3
+ locales:
4
+ en:
5
+ summary: Harness 0.11.0 adds raw-byte subprocess sinks and exposes the bound-read root identity needed by host verification.
6
+ changes:
7
+ added:
8
+ - Extends superviseProcess with an exclusive, no-follow raw stdout/stderr sink that waits for stream close, queued writes, fsync, and close.
9
+ - Keeps the existing bound-read mechanism as the only root and member read authority.
10
+ changed:
11
+ - Carries the previously prepared host Profile closure into the lockstep 0.11.0 family release.
12
+ upgradeNotes: The raw sink is mechanism-only; it does not create a second process runner, receipt state machine, or host-specific policy. The caller must exclusively control the sink namespace for the whole call; handle protection does not prove stable pathname or root identity.
13
+ zh-CN:
14
+ summary: Harness 0.11.0 增加原始字节子进程输出 sink,并暴露真实宿主验证所需的受保护根身份。
15
+ changes:
16
+ added:
17
+ - 为 superviseProcess 增加独占、禁止跟随符号链接的 stdout/stderr 原始字节 sink,并等待流关闭、排队写入、fsync 与 close。
18
+ - 继续把既有 bound-read 机制作为唯一根目录与成员读取权威。
19
+ changed:
20
+ - 把此前准备好的宿主 Profile 闭包并入 0.11.0 三包锁步交付。
21
+ upgradeNotes: 原始 sink 只提供机制,不建立第二个进程 runner、收据状态机或宿主专属策略。调用方必须在整个调用期间独占 sink 命名空间;句柄保护不证明 pathname 或根目录身份始终不变。
@@ -176,6 +176,31 @@ function assertRelativePath(relPath) {
176
176
  return segments;
177
177
  }
178
178
 
179
+ // The only closed set of native failure reasons that can prove a static
180
+ // member policy violation. Anything else — unknown reasons, missing root
181
+ // identity, root identity drift, or "native-io" — stays
182
+ // boundary-indeterminate. This set is the stable branching fact between the
183
+ // harness and the Kit; it must never be derived from errno values or error
184
+ // messages.
185
+ const MEMBER_POLICY_FAILURE_REASONS = Object.freeze([
186
+ "member-missing",
187
+ "intermediate-not-real-directory",
188
+ "leaf-symbolic-link",
189
+ "leaf-not-regular",
190
+ "leaf-multiple-links",
191
+ ]);
192
+ const MEMBER_POLICY_FAILURE_REASON_SET = new Set(MEMBER_POLICY_FAILURE_REASONS);
193
+
194
+ function nativeBoundReadDisposition(native, captured) {
195
+ if (native?.rootDevice !== captured.identity.device || native?.rootInode !== captured.identity.inode ||
196
+ Number(native?.rootMode) !== captured.identity.mode) {
197
+ return "boundary-indeterminate";
198
+ }
199
+ return MEMBER_POLICY_FAILURE_REASON_SET.has(native?.failureReason)
200
+ ? "member-policy-violation"
201
+ : "boundary-indeterminate";
202
+ }
203
+
179
204
  export async function readFileBound(root, relPath, { rootBinding, encoding, expectedSha256 } = {}) {
180
205
  assertSupportedPlatform();
181
206
  assertCanonicalAbsolute(root, "root");
@@ -197,26 +222,36 @@ export async function readFileBound(root, relPath, { rootBinding, encoding, expe
197
222
  throw mechanismError(
198
223
  HARNESS_ERROR_KINDS.UNSAFE_STATE_ENTRY,
199
224
  "approved root binding does not match the current root",
225
+ { boundReadDisposition: "boundary-indeterminate" },
200
226
  );
201
227
  }
202
228
  const { addon } = await loadNativeBoundReadAddon();
203
229
  const native = addon.readFileBoundNative(captured.canonical, segments);
204
230
  if (!native?.ok) {
205
- const cause = {
206
- code: native?.errorCode === (process.platform === "darwin" ? 62 : 40) ? "ELOOP" : native?.errorCode === 2 ? "ENOENT" :
207
- native?.errorCode === 20 ? "ENOTDIR" : `errno-${native?.errorCode ?? "unknown"}`,
208
- };
209
- if (cause.code === "ENOTDIR" && segments.length > 1) {
210
- throw mechanismError(HARNESS_ERROR_KINDS.UNSAFE_STATE_ENTRY, "an intermediate component is not a real directory", {
211
- input: relPath,
212
- });
231
+ // Disposition is decided only by the two documented conditions: the
232
+ // native root identity must equal the captured binding, and the native
233
+ // failureReason must be one of the five closed-set member policy
234
+ // reasons. The reason also selects the stable details.kind; it is never
235
+ // derived from errno values or error messages.
236
+ const boundReadDisposition = nativeBoundReadDisposition(native, captured);
237
+ const details = { input: relPath, boundReadDisposition };
238
+ const reason = native?.failureReason;
239
+ if (reason === "intermediate-not-real-directory") {
240
+ throw mechanismError(HARNESS_ERROR_KINDS.UNSAFE_STATE_ENTRY, "an intermediate component is not a real directory", details);
241
+ }
242
+ if (reason === "leaf-symbolic-link") {
243
+ throw mechanismError(HARNESS_ERROR_KINDS.UNSAFE_STATE_ENTRY, "bound path became a symbolic link during read", details);
244
+ }
245
+ if (reason === "member-missing") {
246
+ throw mechanismError(HARNESS_ERROR_KINDS.MISSING_RESOURCE, "bound path does not exist during read", details);
213
247
  }
214
- mapOpenFailure(cause, relPath, segments.length > 1 ? "bound path" : "leaf");
248
+ throw mechanismError(HARNESS_ERROR_KINDS.READ_FAILED, `bound path could not be read: ${reason ?? "native-io"}`, details);
215
249
  }
216
250
  if (native.rootDevice !== captured.identity.device || native.rootInode !== captured.identity.inode ||
217
251
  Number(native.rootMode) !== captured.identity.mode) {
218
252
  throw mechanismError(HARNESS_ERROR_KINDS.UNSAFE_STATE_ENTRY, "root identity changed during bound read", {
219
253
  input: relPath,
254
+ boundReadDisposition: "boundary-indeterminate",
220
255
  });
221
256
  }
222
257
  const bytes = Buffer.from(native.bytes);
@@ -234,6 +269,7 @@ export async function readFileBound(root, relPath, { rootBinding, encoding, expe
234
269
  sha256,
235
270
  bytes: bytes.length,
236
271
  mode: Number(native.leafMode),
272
+ rootMode: captured.identity.mode,
237
273
  });
238
274
  } finally {
239
275
  await captured.handle.close().catch(() => {});
package/src/index.mjs CHANGED
@@ -148,6 +148,7 @@ export {
148
148
  materializeAdapterBuild,
149
149
  probeVersionVector,
150
150
  } from "./host.mjs";
151
+ export { verifyPeerAdapterDirectories } from "./peer-adapter.mjs";
151
152
 
152
153
  // Deterministic report layer (FND-ADR-005): validate -> render -> bind -> check.
153
154
  // Pure functions only: no clock, no environment, no network, no model calls.
package/src/native/NOTICE CHANGED
@@ -1,3 +1,2 @@
1
- Skill Family Foundation stable filesystem native prebuilds (bound-read and fixed-set publication).
2
- Compiled from the Apache-2.0 source identified in prebuild-sbom.json.
3
- The closure contains no JavaScript fallback and no candidate entry-point dependency.
1
+ Skill Family Foundation filesystem bound-read stable prebuilds
2
+ Generated only from four verified platform receipts.