skill-family-harness-node 0.10.0 → 0.12.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 +35 -0
- package/CHANGELOG.zh-CN.md +35 -0
- package/README.md +11 -16
- package/README.zh-CN.md +9 -14
- package/candidate/rename-directory-no-replace/build-prebuild-candidate.mjs +19 -43
- package/package.json +2 -2
- package/release-notes/0.11.0.yaml +21 -0
- package/release-notes/0.12.0.yaml +15 -0
- package/src/bound-read.mjs +45 -9
- package/src/native/NOTICE +2 -3
- package/src/native/assemble-stable-prebuild.mjs +45 -0
- package/src/native/binding.gyp +25 -0
- package/src/native/bound_read.c +96 -20
- package/src/native/prebuild-manifest.json +32 -8
- package/src/native/prebuild-release-receipt.json +32 -4
- package/src/native/prebuild-sbom.json +29 -5
- package/src/native/prebuilds/darwin-arm64/bound_read.darwin-arm64.node +0 -0
- package/src/native/prebuilds/darwin-x64/bound_read.darwin-x64.node +0 -0
- package/src/native/prebuilds/linux-arm64-gnu/bound_read.linux-arm64-gnu.node +0 -0
- package/src/native/prebuilds/linux-x64-gnu/bound_read.linux-x64-gnu.node +0 -0
- package/src/native/receipt-assembler-core.mjs +228 -0
- package/src/supervise-process.mjs +252 -4
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,40 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
<!-- release-skill:changelog:start version=0.12.0 locale=en baseline=sha256:0074df3a947706f6cd6d8593968a348cd4d3f38d8f2bf75f15a04c8183d76618 -->
|
|
4
|
+
## [0.12.0] - 2026-08-26
|
|
5
|
+
|
|
6
|
+
Harness 0.12.0 joins the lockstep Foundation release with its existing process and filesystem mechanisms unchanged.
|
|
7
|
+
|
|
8
|
+
### Changed
|
|
9
|
+
|
|
10
|
+
- Pins skill-family-contracts to 0.12.0 for the five-host verification extension implemented by Engineering Kit.
|
|
11
|
+
|
|
12
|
+
### Upgrade Notes
|
|
13
|
+
|
|
14
|
+
No new Harness API or runtime behavior is introduced. Existing process supervision, raw-byte sinks, bound reads, and digest mechanisms remain the shared implementation; host-specific protocol rules belong to Engineering Kit.
|
|
15
|
+
<!-- release-skill:changelog:end version=0.12.0 locale=en -->
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
<!-- release-skill:changelog:start version=0.11.0 locale=en baseline=sha256:e20f505e42ba45865bfee38a6c83671006bf20148d5d4e421ff7f443bcce260b -->
|
|
19
|
+
## [0.11.0] - 2026-08-25
|
|
20
|
+
|
|
21
|
+
Harness 0.11.0 adds raw-byte subprocess sinks and exposes the bound-read root identity needed by host verification.
|
|
22
|
+
|
|
23
|
+
### Added
|
|
24
|
+
|
|
25
|
+
- Extends superviseProcess with an exclusive, no-follow raw stdout/stderr sink that waits for stream close, queued writes, fsync, and close.
|
|
26
|
+
- Keeps the existing bound-read mechanism as the only root and member read authority.
|
|
27
|
+
|
|
28
|
+
### Changed
|
|
29
|
+
|
|
30
|
+
- Carries the previously prepared host Profile closure into the lockstep 0.11.0 family release.
|
|
31
|
+
|
|
32
|
+
### Upgrade Notes
|
|
33
|
+
|
|
34
|
+
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.
|
|
35
|
+
<!-- release-skill:changelog:end version=0.11.0 locale=en -->
|
|
36
|
+
|
|
37
|
+
|
|
3
38
|
<!-- release-skill:changelog:start version=0.10.0 locale=en baseline=sha256:64a325d3e51604a8436b33df5a7f617f9aab1f1e02adb32ccb58316aa5c5eab1 -->
|
|
4
39
|
## [0.10.0] - 2026-08-24
|
|
5
40
|
|
package/CHANGELOG.zh-CN.md
CHANGED
|
@@ -1,5 +1,40 @@
|
|
|
1
1
|
# 变更日志
|
|
2
2
|
|
|
3
|
+
<!-- release-skill:changelog:start version=0.12.0 locale=zh-CN baseline=sha256:2e7db41a08c30d9e0bdc16b4cd27853104ef79dc02f7e9925a1383a6546f5111 -->
|
|
4
|
+
## [0.12.0] - 2026-08-26
|
|
5
|
+
|
|
6
|
+
Harness 0.12.0 随 Foundation 三包锁步升级,既有进程与文件系统机制保持不变。
|
|
7
|
+
|
|
8
|
+
### 变更
|
|
9
|
+
|
|
10
|
+
- 将 skill-family-contracts 精确依赖更新为 0.12.0,支持 Engineering Kit 的五平台验证扩展。
|
|
11
|
+
|
|
12
|
+
### 升级说明
|
|
13
|
+
|
|
14
|
+
本版本不新增 Harness API,也不改变运行行为。进程监督、原始字节输出、绑定读取和摘要继续复用既有机制;宿主专属的协议规则由 Engineering Kit 实现。
|
|
15
|
+
<!-- release-skill:changelog:end version=0.12.0 locale=zh-CN -->
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
<!-- release-skill:changelog:start version=0.11.0 locale=zh-CN baseline=sha256:51776685eead52118cc98ee47b8ac8990650a493d60567002133ff74e1c779c6 -->
|
|
19
|
+
## [0.11.0] - 2026-08-25
|
|
20
|
+
|
|
21
|
+
Harness 0.11.0 增加原始字节子进程输出 sink,并暴露真实宿主验证所需的受保护根身份。
|
|
22
|
+
|
|
23
|
+
### 新增
|
|
24
|
+
|
|
25
|
+
- 为 superviseProcess 增加独占、禁止跟随符号链接的 stdout/stderr 原始字节 sink,并等待流关闭、排队写入、fsync 与 close。
|
|
26
|
+
- 继续把既有 bound-read 机制作为唯一根目录与成员读取权威。
|
|
27
|
+
|
|
28
|
+
### 变更
|
|
29
|
+
|
|
30
|
+
- 把此前准备好的宿主 Profile 闭包并入 0.11.0 三包锁步交付。
|
|
31
|
+
|
|
32
|
+
### 升级说明
|
|
33
|
+
|
|
34
|
+
原始 sink 只提供机制,不建立第二个进程 runner、收据状态机或宿主专属策略。调用方必须在整个调用期间独占 sink 命名空间;句柄保护不证明 pathname 或根目录身份始终不变。
|
|
35
|
+
<!-- release-skill:changelog:end version=0.11.0 locale=zh-CN -->
|
|
36
|
+
|
|
37
|
+
|
|
3
38
|
<!-- release-skill:changelog:start version=0.10.0 locale=zh-CN baseline=sha256:d8e27cce0c3e97b7d10b6f88fad4df66c8d527d3d6a2fb165f8c909612ff9f19 -->
|
|
4
39
|
## [0.10.0] - 2026-08-24
|
|
5
40
|
|
package/README.md
CHANGED
|
@@ -4,29 +4,22 @@
|
|
|
4
4
|
|
|
5
5
|
# skill-family-harness-node
|
|
6
6
|
|
|
7
|
-
<!-- release-skill:release-version: 0.
|
|
7
|
+
<!-- release-skill:release-version: 0.12.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.
|
|
12
|
+
**0.12.0** (2026-08-26)
|
|
13
13
|
|
|
14
|
-
Harness 0.
|
|
15
|
-
|
|
16
|
-
**Added**
|
|
17
|
-
|
|
18
|
-
- Adds skill-family-harness-node/quickstart-profile and skill-family-harness-node/rename-directory-no-replace canonical exports.
|
|
19
|
-
- Reuses filesystem-root binding, strict no-replace publication, atomic replacement, and existing build digests for the Kit's local host install/update path.
|
|
20
|
-
- Adds `verifyPeerAdapterDirectories`, which re-enumerates peer roots and verifies common closure, byte digests, standard manifests, and complete logical mappings without writing them.
|
|
14
|
+
Harness 0.12.0 joins the lockstep Foundation release with its existing process and filesystem mechanisms unchanged.
|
|
21
15
|
|
|
22
16
|
**Changed**
|
|
23
17
|
|
|
24
|
-
-
|
|
25
|
-
- Keeps validate-many-by-schema-id and its error semantics unchanged while managed Bundles accept historical and canonical Schema IDs through the same validator.
|
|
18
|
+
- Pins skill-family-contracts to 0.12.0 for the five-host verification extension implemented by Engineering Kit.
|
|
26
19
|
|
|
27
20
|
**Upgrade Notes**
|
|
28
21
|
|
|
29
|
-
|
|
22
|
+
No new Harness API or runtime behavior is introduced. Existing process supervision, raw-byte sinks, bound reads, and digest mechanisms remain the shared implementation; host-specific protocol rules belong to Engineering Kit.
|
|
30
23
|
<!-- release-skill:managed:end id=latest-release -->
|
|
31
24
|
|
|
32
25
|
## Problem It Solves
|
|
@@ -35,19 +28,19 @@ Contracts defines "what should be", and the Harness turns that into "can be safe
|
|
|
35
28
|
|
|
36
29
|
## Core Mental Model
|
|
37
30
|
|
|
38
|
-
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`.
|
|
31
|
+
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`.
|
|
39
32
|
|
|
40
33
|
## Installation and Minimal Example
|
|
41
34
|
|
|
42
35
|
```sh
|
|
43
|
-
npm install skill-family-harness-node@0.
|
|
36
|
+
npm install skill-family-harness-node@0.12.0
|
|
44
37
|
npm info skill-family-harness-node --help
|
|
45
38
|
```
|
|
46
39
|
|
|
47
40
|
The minimal example shows validating a contract document inside Node:
|
|
48
41
|
|
|
49
42
|
```js
|
|
50
|
-
// Run from an empty directory: npm install skill-family-harness-node@0.
|
|
43
|
+
// Run from an empty directory: npm install skill-family-harness-node@0.12.0
|
|
51
44
|
import { validateContractDocument } from "skill-family-harness-node";
|
|
52
45
|
|
|
53
46
|
const document = {
|
|
@@ -107,6 +100,7 @@ The capability remains **candidate**. Pin all three Foundation packages exactly
|
|
|
107
100
|
| `writeFileAtomic` | Atomic write: leaves no half-written artifact on failure (temp file + fsync + rename). |
|
|
108
101
|
| `TemporaryWorkspace` / `createTemporaryWorkspace` / `withTemporaryWorkspace` | Auto-cleanup temporary workspace, cleaned up even on exception paths. |
|
|
109
102
|
| `digestBytes` / `computeResourceClosure` / `closureContains` | Resource closure and deterministic sha256 digest. |
|
|
103
|
+
| `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. |
|
|
110
104
|
| `parseRequest` / `processRequest` | Parse `operation-request`, output terminal `operation-result`. |
|
|
111
105
|
| `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. |
|
|
112
106
|
| `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. |
|
|
@@ -144,7 +138,7 @@ Comparison is based on the canonical root after `realpath`, avoiding misjudgment
|
|
|
144
138
|
|
|
145
139
|
## Testing
|
|
146
140
|
|
|
147
|
-
`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.
|
|
141
|
+
`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.
|
|
148
142
|
|
|
149
143
|
## Troubleshooting
|
|
150
144
|
|
|
@@ -178,6 +172,7 @@ Mechanism failures uniformly throw `SFC2004` (EXECUTION_FAILED), with `details.k
|
|
|
178
172
|
- `foundation.harness.atomic-write`: atomic write within contained paths, rolling back on failure.
|
|
179
173
|
- `foundation.harness.temporary-workspace`: auto-cleanup temporary workspace.
|
|
180
174
|
- `foundation.harness.resource-closure`: deterministic resource closure and sha256 digest.
|
|
175
|
+
- `foundation.harness.supervise-process`: one bounded subprocess supervisor; raw evidence capture remains mechanism-only and does not produce a receipt or domain verdict.
|
|
181
176
|
- `foundation.harness.request-processing`: operation-request → terminal operation-result.
|
|
182
177
|
- `foundation.harness.report`: report-model validation/render/binding/check.
|
|
183
178
|
- `foundation.harness.host-adapter`: adapter source closure/build/materialize, version probe, and read-only peer adapter verification.
|
package/README.zh-CN.md
CHANGED
|
@@ -5,29 +5,22 @@
|
|
|
5
5
|
|
|
6
6
|
# skill-family-harness-node
|
|
7
7
|
|
|
8
|
-
<!-- release-skill:release-version: 0.
|
|
8
|
+
<!-- release-skill:release-version: 0.12.0 -->
|
|
9
9
|
|
|
10
10
|
Contracts 机制协议的**唯一默认 Node 实现**。这是一个薄运行时(thin runtime):只实现机制协议,不引入业务语义,不做第二语言实现。
|
|
11
11
|
|
|
12
12
|
<!-- release-skill:managed:start id=latest-release -->
|
|
13
|
-
**0.
|
|
13
|
+
**0.12.0** (2026-08-26)
|
|
14
14
|
|
|
15
|
-
Harness 0.
|
|
16
|
-
|
|
17
|
-
**新增**
|
|
18
|
-
|
|
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,不写入目录。
|
|
15
|
+
Harness 0.12.0 随 Foundation 三包锁步升级,既有进程与文件系统机制保持不变。
|
|
22
16
|
|
|
23
17
|
**变更**
|
|
24
18
|
|
|
25
|
-
-
|
|
26
|
-
- validate-many-by-schema-id 及错误语义不变;受管 Bundle 让历史与规范 Schema ID 共用同一 validator。
|
|
19
|
+
- 将 skill-family-contracts 精确依赖更新为 0.12.0,支持 Engineering Kit 的五平台验证扩展。
|
|
27
20
|
|
|
28
21
|
**升级说明**
|
|
29
22
|
|
|
30
|
-
|
|
23
|
+
本版本不新增 Harness API,也不改变运行行为。进程监督、原始字节输出、绑定读取和摘要继续复用既有机制;宿主专属的协议规则由 Engineering Kit 实现。
|
|
31
24
|
<!-- release-skill:managed:end id=latest-release -->
|
|
32
25
|
|
|
33
26
|
## 解决的问题
|
|
@@ -41,14 +34,14 @@ Harness 消费 `skill-family-contracts`(工作区依赖),复用其方言
|
|
|
41
34
|
## 安装和最小示例
|
|
42
35
|
|
|
43
36
|
```sh
|
|
44
|
-
npm install skill-family-harness-node@0.
|
|
37
|
+
npm install skill-family-harness-node@0.12.0
|
|
45
38
|
npm info skill-family-harness-node --help
|
|
46
39
|
```
|
|
47
40
|
|
|
48
41
|
最小示例演示在 Node 内校验一份契约文档:
|
|
49
42
|
|
|
50
43
|
```js
|
|
51
|
-
// 从空目录运行:npm install skill-family-harness-node@0.
|
|
44
|
+
// 从空目录运行:npm install skill-family-harness-node@0.12.0
|
|
52
45
|
import { validateContractDocument } from "skill-family-harness-node";
|
|
53
46
|
|
|
54
47
|
const document = {
|
|
@@ -108,6 +101,7 @@ v2 机制会重算每个 path-backed output 和 evidence Resource 的真实字
|
|
|
108
101
|
| `writeFileAtomic` | 原子写:失败不留半成品(临时文件 + fsync + rename)。 |
|
|
109
102
|
| `TemporaryWorkspace` / `createTemporaryWorkspace` / `withTemporaryWorkspace` | 自动清理的临时工作区,异常路径也清理。 |
|
|
110
103
|
| `digestBytes` / `computeResourceClosure` / `closureContains` | 资源闭包与确定性 sha256 摘要。 |
|
|
104
|
+
| `superviseProcess` / `validateTimeoutPolicy` | 唯一的受约束子进程监督器。0.11.0 的 `rawSink` 只向 fresh canonical 私有根写原始 stdout/stderr 字节,并等待子进程、流、排队写入、fsync 与句柄全部关闭。调用方必须在整个调用期间独占 sink 命名空间;句柄保护不证明 pathname 或根目录身份始终不变。 |
|
|
111
105
|
| `parseRequest` / `processRequest` | 解析 `operation-request`,输出终态 `operation-result`。 |
|
|
112
106
|
| `validateReportModel` / `renderReportMarkdown` / `buildBinding` / `checkReport` | 消费经 Contracts 验证的 report model,确定性渲染中性 Markdown 并校验来源/结果/报告绑定;不解释业务输出。 |
|
|
113
107
|
| `normalizeAdapterSource` / `buildAdapterClosure` / `verifyAdapterBuildManifest` / `materializeAdapterBuild` | 通用文本 source closure、manifest 全摘要复验和目标集合原子落盘;具体 Profile/driver 不在 Harness。 |
|
|
@@ -179,6 +173,7 @@ v2 机制会重算每个 path-backed output 和 evidence Resource 的真实字
|
|
|
179
173
|
- `foundation.harness.atomic-write`:受收容路径内原子写,失败回滚。
|
|
180
174
|
- `foundation.harness.temporary-workspace`:自动清理的临时工作区。
|
|
181
175
|
- `foundation.harness.resource-closure`:确定性资源闭包与 sha256 摘要。
|
|
176
|
+
- `foundation.harness.supervise-process`:监督一个有界子进程;原始证据收集仍只提供机制,不产生收据或领域结论。
|
|
182
177
|
- `foundation.harness.request-processing`:operation-request → 终态 operation-result。
|
|
183
178
|
- `foundation.harness.report`:report-model 校验/渲染/绑定/检查。
|
|
184
179
|
- `foundation.harness.host-adapter`:adapter source closure/build/materialize 与版本探测。
|
|
@@ -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
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
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
|
-
|
|
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:
|
|
155
|
+
manifestSha256: manifestSha,
|
|
173
156
|
inputs: inputs.map(({ key, receiptSha, binarySha }) => ({ platformKey: key, platformReceiptSha256: receiptSha, binarySha256: binarySha })),
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
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.
|
|
13
|
+
"skill-family-contracts": "0.12.0",
|
|
14
14
|
"yaml": "2.9.0"
|
|
15
15
|
},
|
|
16
16
|
"description": "Thin Node.js mechanism runtime for Skill Family engineering contracts.",
|
|
@@ -48,7 +48,7 @@
|
|
|
48
48
|
"url": "https://github.com/ifoohoo/skill-family-harness-node.git"
|
|
49
49
|
},
|
|
50
50
|
"type": "module",
|
|
51
|
-
"version": "0.
|
|
51
|
+
"version": "0.12.0",
|
|
52
52
|
"scripts": {
|
|
53
53
|
"check": "node --test",
|
|
54
54
|
"test": "node --test"
|
|
@@ -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 或根目录身份始终不变。
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
version: 0.12.0
|
|
2
|
+
date: 2026-08-26
|
|
3
|
+
locales:
|
|
4
|
+
en:
|
|
5
|
+
summary: Harness 0.12.0 joins the lockstep Foundation release with its existing process and filesystem mechanisms unchanged.
|
|
6
|
+
changes:
|
|
7
|
+
changed:
|
|
8
|
+
- Pins skill-family-contracts to 0.12.0 for the five-host verification extension implemented by Engineering Kit.
|
|
9
|
+
upgradeNotes: No new Harness API or runtime behavior is introduced. Existing process supervision, raw-byte sinks, bound reads, and digest mechanisms remain the shared implementation; host-specific protocol rules belong to Engineering Kit.
|
|
10
|
+
zh-CN:
|
|
11
|
+
summary: Harness 0.12.0 随 Foundation 三包锁步升级,既有进程与文件系统机制保持不变。
|
|
12
|
+
changes:
|
|
13
|
+
changed:
|
|
14
|
+
- 将 skill-family-contracts 精确依赖更新为 0.12.0,支持 Engineering Kit 的五平台验证扩展。
|
|
15
|
+
upgradeNotes: 本版本不新增 Harness API,也不改变运行行为。进程监督、原始字节输出、绑定读取和摘要继续复用既有机制;宿主专属的协议规则由 Engineering Kit 实现。
|
package/src/bound-read.mjs
CHANGED
|
@@ -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
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
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
|
-
|
|
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/native/NOTICE
CHANGED
|
@@ -1,3 +1,2 @@
|
|
|
1
|
-
Skill Family Foundation
|
|
2
|
-
|
|
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.
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { createHash } from "node:crypto";
|
|
3
|
+
import { readFileSync } from "node:fs";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import process from "node:process";
|
|
6
|
+
import { fileURLToPath } from "node:url";
|
|
7
|
+
import { assertSha256Hex, runCli } from "./receipt-assembler-core.mjs";
|
|
8
|
+
|
|
9
|
+
const ROOT = path.dirname(fileURLToPath(import.meta.url));
|
|
10
|
+
const SOURCE = path.join(ROOT, "bound_read.c");
|
|
11
|
+
const BINDING_GYP = path.join(ROOT, "binding.gyp");
|
|
12
|
+
const KEYS = Object.freeze(["darwin-arm64", "darwin-x64", "linux-arm64-gnu", "linux-x64-gnu"]);
|
|
13
|
+
const PLATFORMS = Object.freeze({
|
|
14
|
+
"darwin-arm64": { os: "darwin", arch: "arm64", libc: "none" },
|
|
15
|
+
"darwin-x64": { os: "darwin", arch: "x64", libc: "none" },
|
|
16
|
+
"linux-arm64-gnu": { os: "linux", arch: "arm64", libc: "glibc" },
|
|
17
|
+
"linux-x64-gnu": { os: "linux", arch: "x64", libc: "glibc" },
|
|
18
|
+
});
|
|
19
|
+
const EXPORTS = Object.freeze(["closeParentDirectory", "openParentDirectory", "platform", "readFileBoundNative", "renameDirectoryNoReplace"]);
|
|
20
|
+
const sha256 = (bytes) => createHash("sha256").update(bytes).digest("hex");
|
|
21
|
+
const bindingGypSha256 = sha256(readFileSync(BINDING_GYP));
|
|
22
|
+
|
|
23
|
+
function extractFacts(key, receipt, { sourceSha }) {
|
|
24
|
+
const expected = PLATFORMS[key];
|
|
25
|
+
if (receipt?.kind !== "skill-family.filesystem-bound-read-platform-receipt" || receipt.status !== "VERIFIED" ||
|
|
26
|
+
receipt.actualExecution !== true || receipt.platformKey !== key || receipt.node?.version !== "v22.23.2" ||
|
|
27
|
+
Number(receipt.node?.napi) !== 10 || receipt.platform?.os !== expected.os || receipt.platform?.arch !== expected.arch ||
|
|
28
|
+
receipt.platform?.libc !== expected.libc || receipt.source?.sha256 !== sourceSha ||
|
|
29
|
+
receipt.buildRecipe?.sha256 !== bindingGypSha256 || receipt.addon?.mode !== "0644" ||
|
|
30
|
+
receipt.addon?.size === undefined || JSON.stringify(receipt.addon?.exports) !== JSON.stringify(EXPORTS) ||
|
|
31
|
+
!Array.isArray(receipt.oracle?.results) || receipt.oracle.results.length === 0 || !receipt.oracle.results.every((result) => result.status === "PASS")) {
|
|
32
|
+
throw new Error(`${key}: stable receipt does not prove the fixed platform execution contract`);
|
|
33
|
+
}
|
|
34
|
+
return { sourceSha, binarySha: assertSha256Hex(receipt.addon.sha256, `${key} addon sha256`), binarySize: receipt.addon.size, binaryMode: 0o644 };
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
runCli({
|
|
38
|
+
keys: KEYS, platforms: PLATFORMS, sourceFile: SOURCE, mode: 0o644, napi: 10, exports: EXPORTS,
|
|
39
|
+
binaryName: (key) => `bound_read.${key}.node`, extractFacts,
|
|
40
|
+
manifestDoc: (entries) => ({ schemaVersion: 1, kind: "skill-family.filesystem-bound-read-prebuild-manifest", status: "stable", entries }),
|
|
41
|
+
sbomDoc: (sourceSha, entries) => ({ schemaVersion: 1, kind: "skill-family.filesystem-bound-read-prebuild-sbom", status: "stable", source: { path: "packages/skill-family-harness-node/src/native/bound_read.c", sha256: sourceSha }, buildRecipe: { path: "packages/skill-family-harness-node/src/native/binding.gyp", sha256: bindingGypSha256 }, files: entries.map(({ platformKey, binary, sha256: digest }) => ({ platformKey, binary, sha256: digest, license: "Apache-2.0" })) }),
|
|
42
|
+
releaseReceiptDoc: ({ inputs, sourceSha, manifestSha }) => ({ schemaVersion: 1, kind: "skill-family.filesystem-bound-read-prebuild-release-receipt", status: "stable", node: { version: "v22.23.2", napi: 10 }, source: { path: "packages/skill-family-harness-node/src/native/bound_read.c", sha256: sourceSha }, buildRecipe: { path: "packages/skill-family-harness-node/src/native/binding.gyp", sha256: bindingGypSha256 }, manifestSha256: manifestSha, inputs: inputs.map(({ key, receiptSha, binarySha }) => ({ platformKey: key, platformReceiptSha256: receiptSha, binarySha256: binarySha })) }),
|
|
43
|
+
noticeText: "Skill Family Foundation filesystem bound-read stable prebuilds\nGenerated only from four verified platform receipts.\n",
|
|
44
|
+
managedNames: ["prebuilds", "prebuild-manifest.json", "prebuild-sbom.json", "prebuild-release-receipt.json", "NOTICE"],
|
|
45
|
+
}, process.argv.slice(2));
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"targets": [
|
|
3
|
+
{
|
|
4
|
+
"target_name": "bound_read",
|
|
5
|
+
"sources": ["bound_read.c"],
|
|
6
|
+
"cflags": ["-Wall", "-Wextra", "-Werror", "-std=c11"],
|
|
7
|
+
"xcode_settings": {
|
|
8
|
+
"GCC_GENERATE_DEBUGGING_SYMBOLS": "NO",
|
|
9
|
+
"OTHER_CFLAGS": [
|
|
10
|
+
"-Wall", "-Wextra", "-Werror", "-std=c11",
|
|
11
|
+
"-ffile-prefix-map=<(module_root_dir)=.",
|
|
12
|
+
"-fdebug-prefix-map=<(module_root_dir)=."
|
|
13
|
+
]
|
|
14
|
+
},
|
|
15
|
+
"conditions": [
|
|
16
|
+
["OS=='mac'", {
|
|
17
|
+
"defines": ["PLATFORM_DARWIN=1"]
|
|
18
|
+
}],
|
|
19
|
+
["OS=='linux'", {
|
|
20
|
+
"defines": ["PLATFORM_LINUX=1"]
|
|
21
|
+
}]
|
|
22
|
+
]
|
|
23
|
+
}
|
|
24
|
+
]
|
|
25
|
+
}
|