skill-family-harness-node 0.2.0 → 0.2.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 ADDED
@@ -0,0 +1,21 @@
1
+ # Changelog
2
+
3
+ <!-- release-skill:changelog:start version=0.2.1 locale=en baseline=sha256:acd7d4e02eb309b149a31f4b88a8163c69ae094a53591f173c20c407e8ff4ed0 -->
4
+ ## [0.2.1] - 2026-08-10
5
+
6
+ This release adds candidate Quickstart Profile exchange helpers and makes the package release documentation available in English and Simplified Chinese.
7
+
8
+ ### Added
9
+
10
+ - Adds candidate helpers that create and revalidate observation Resources, build Tasks, wrap Results, and fail closed when a Result does not bind the exact Task and correlation fields.
11
+ - Adds complete English and Simplified Chinese package documentation, including an agent quick-reference section.
12
+
13
+ ### Changed
14
+
15
+ - Manages the current README and CHANGELOG release sections from one bilingual, versioned notes source.
16
+ - Distributes the project NOTICE separately from the Apache-2.0 LICENSE.
17
+
18
+ ### Upgrade Notes
19
+
20
+ The candidate helpers do not alter the stable Harness API or add lifecycle, retry, orchestration, model-call, network, or Git-write semantics.
21
+ <!-- release-skill:changelog:end version=0.2.1 locale=en -->
@@ -0,0 +1,21 @@
1
+ # 变更日志
2
+
3
+ <!-- release-skill:changelog:start version=0.2.1 locale=zh-CN baseline=sha256:2f2c74ab9dcf0f1a84872743bf203eb43d15c5e722e823e670e8d81ca5f7de65 -->
4
+ ## [0.2.1] - 2026-08-10
5
+
6
+ 本版新增 Quickstart Profile 候选交换辅助函数,并为包发布文档提供完整英文版与简体中文版。
7
+
8
+ ### 新增
9
+
10
+ - 新增候选辅助函数,用于创建并复验 observation Resource、构造 Task、封装 Result,并在 Result 未精确绑定 Task 与关联字段时失败关闭。
11
+ - 新增完整的英文与简体中文包文档,并补充智能体快速参考章节。
12
+
13
+ ### 变更
14
+
15
+ - 使用同一份双语版本化说明源管理当前 README 与 CHANGELOG 的发布区域。
16
+ - 项目 NOTICE 与 Apache-2.0 LICENSE 分开分发。
17
+
18
+ ### 升级说明
19
+
20
+ 候选辅助函数不改变稳定 Harness API,也不引入生命周期、重试、编排、模型调用、网络或 Git 写入语义。
21
+ <!-- release-skill:changelog:end version=0.2.1 locale=zh-CN -->
package/NOTICE ADDED
@@ -0,0 +1,10 @@
1
+ Skill Family Foundation
2
+ =======================
3
+
4
+ Copyright 2026 广州市风荷科技有限公司
5
+
6
+ Licensed under the Apache License, Version 2.0.
7
+
8
+ This NOTICE contains project attribution for Skill Family Foundation. Third-party
9
+ attribution and license texts distributed with skill-family-engineering-kit are
10
+ recorded separately in THIRD_PARTY_NOTICES and the corresponding license files.
package/README.md CHANGED
@@ -1,77 +1,52 @@
1
1
  <!-- release-skill:safe-first-command -->
2
2
  <!-- release-skill:external-write-boundary -->
3
+ > 简体中文版:[README.zh-CN.md](./README.zh-CN.md)
3
4
 
4
5
  # skill-family-harness-node
5
6
 
6
- Contracts 机制协议的**唯一默认 Node 实现**。这是一个薄运行时(thin runtime):只实现机制协议,不引入业务语义,不做第二语言实现。
7
+ <!-- release-skill:release-version: 0.2.1 -->
7
8
 
8
- ## 边界
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.
9
10
 
10
- - 消费 `skill-family-contracts`(工作区依赖),复用其方言路由的 Ajv validator、Kernel Protocol、冻结错误码与 fixture;不复制协议定义,不重新解释 Schema。
11
- - 只实现机制:Schema 校验、原子写、路径收容、临时工作区、资源闭包、operation-request → operation-result 管道、业务中立的事件日志与派生快照。
12
- - 明确排除:业务语义、任务编排、Git 写入、模型调用、远程网络、发布状态。见 `HARNESS_EXCLUSIONS`。
11
+ <!-- release-skill:managed:start id=latest-release -->
12
+ **0.2.1** (2026-08-10)
13
13
 
14
- ## 公共 API
14
+ This release adds candidate Quickstart Profile exchange helpers and makes the package release documentation available in English and Simplified Chinese.
15
15
 
16
- | 导出 | 职责 |
17
- | --- | --- |
18
- | `HARNESS_CAPABILITIES` / `HARNESS_EXCLUSIONS` | 能力与排除清单(冻结常量)。 |
19
- | `HarnessError` / `HARNESS_ERROR_KINDS` / `mechanismError` | 机制失败统一携带注册错误码 `SFC2004`,`details.kind` 给出稳定细分。 |
20
- | `validateContractDocument` / `getValidator` / `resolveSchemaContext` / `validatorCacheSize` | 按 Schema 方言路由并缓存 validator;复用 Contracts 的 Ajv 实例与 dialect/policy 语义。 |
21
- | `classifyPathInput` / `resolveContained` / `readFileContained` | 路径收容:拦截路径越界、符号链接逃逸、真实路径逃逸。 |
22
- | `writeFileAtomic` | 原子写:失败不留半成品(临时文件 + fsync + rename)。 |
23
- | `TemporaryWorkspace` / `createTemporaryWorkspace` / `withTemporaryWorkspace` | 自动清理的临时工作区,异常路径也清理。 |
24
- | `digestBytes` / `computeResourceClosure` / `closureContains` | 资源闭包与确定性 sha256 摘要。 |
25
- | `parseRequest` / `processRequest` | 解析 `operation-request`,输出终态 `operation-result`。 |
26
- | `validateReportModel` / `renderReportMarkdown` / `buildBinding` / `checkReport` | 消费经 Contracts 验证的 report model,确定性渲染中性 Markdown 并校验来源/结果/报告绑定;不解释业务输出。 |
27
- | `normalizeAdapterSource` / `buildAdapterClosure` / `verifyAdapterBuildManifest` / `materializeAdapterBuild` | 通用文本 source closure、manifest 全摘要复验和目标集合原子落盘;具体 Profile/driver 不在 Harness。 |
28
- | `probeVersionVector` | 默认禁用 spawn 的版本探测机制;显式启用时只执行绝对、无 symlink 的受审计向量,不使用 PATH/shell。 |
29
- | `openStateStore` / `appendEvent` / `readEvents` / `verifyStateStore` / `closeStateStore` | 严格单写者的 append-only 事件存储;事件目录是唯一状态权威,`chain-head.json` 只是缓存。 |
30
- | `readSnapshot` / `writeSnapshot` / `rebuildSnapshot` | 原子派生快照与完整事件重建;坏事件不能被旧快照掩盖,坏快照可被重建忽略。 |
31
- | `inspectStateStoreLock` / `recoverStateStoreLock` | 只读锁诊断与显式恢复;恢复必须对观测到的 owner + fencing 做精确匹配。 |
32
-
33
- ## 状态存储的锁与恢复边界
16
+ **Added**
34
17
 
35
- - 锁使用 exclusive create,第二写者立即收到 `store-locked`;不排队,也不按时间、PID 或租约过期偷锁。
36
- - `inspectStateStoreLock` 不创建任何文件,只返回 `owner`、单调 `fencing`、`ageMs` 和恢复中标记。`ageMs` 仅供诊断,从不参与正确性判断。
37
- - 崩溃遗留锁只能由调用方在 Foundation 之外确认旧写者已经终止后,调用 `recoverStateStoreLock`,同时提交精确匹配的 `expectedOwner`、`expectedFencing` 与 `confirmOwnerTerminated: true`。不匹配或缺少确认均失败关闭。
38
- - 恢复产生更大的 fencing。旧 handle 每次 append 都重新核对 owner、fencing 和 acquisition id;事件最终发布使用同目录临时普通文件、fsync 和 exclusive link,绝不覆盖既有 sequence。
39
- - append、snapshot、close 与 recovery 由短期 `writer-mutation.lock` 串行化;恢复不能越过已经持有 mutation guard 的权威写入。
40
- - 如果恢复进程自身在持有 `writer-recovery.lock` 时崩溃,系统保持可诊断的锁死状态,不自动删除该 guard。它需要新的外部取证与人工处置;当前 API 不声称解决不可信调用方谎报“旧写者已终止”的场景。
41
- - state root、`events/`、`snapshots/`、事件和快照拒绝 symlink、硬链接、FIFO、设备与其它非普通条目。payload 必须是纯 JSON,且 `eventType + payloadSchemaVersion` 必须命中调用方在 open/recover 时冻结的 Schema 对。
18
+ - Adds candidate helpers that create and revalidate observation Resources, build Tasks, wrap Results, and fail closed when a Result does not bind the exact Task and correlation fields.
19
+ - Adds complete English and Simplified Chinese package documentation, including an agent quick-reference section.
42
20
 
43
- ## 稳定错误码
21
+ **Changed**
44
22
 
45
- 全部复用 Contracts 冻结登记表,不新增未登记码。机制失败统一为 `SFC2004`(EXECUTION_FAILED),`details.kind` `HARNESS_ERROR_KINDS` 中的稳定值,例如 `path-traversal`、`symlink-escape`、`realpath-escape`、`atomic-write-failed`、`missing-resource`、`workspace-disposed`。
23
+ - Manages the current README and CHANGELOG release sections from one bilingual, versioned notes source.
24
+ - Distributes the project NOTICE separately from the Apache-2.0 LICENSE.
46
25
 
47
- 新增一个全新的 SFC 码属于 Contracts 变更(登记表在 contracts 包内),超出本包写集;因此用「`SFC2004` + 稳定 `details.kind`」组合保持对外语义稳定。
26
+ **Upgrade Notes**
48
27
 
49
- ## 路径收容模型
28
+ The candidate helpers do not alter the stable Harness API or add lifecycle, retry, orchestration, model-call, network, or Git-write semantics.
29
+ <!-- release-skill:managed:end id=latest-release -->
50
30
 
51
- `resolveContained(root, rel)` 是所有文件系统访问的唯一入口,按序拒绝:
31
+ ## Problem It Solves
52
32
 
53
- 1. 输入分级(`classifyPathInput`,纯函数可测):拒绝绝对路径、Windows 盘符/UNC 路径、POSIX 上的反斜杠路径、空输入、NUL 字节。
54
- 2. 词法收容:`path.resolve` 后离开根 → `path-traversal`。
55
- 3. 符号链接逃逸:末位组件是指向根外的符号链接(或断链)→ `symlink-escape`。
56
- 4. 真实路径逃逸:任一中间符号链接链的规范化结果离开根 → `realpath-escape`。
33
+ Contracts defines "what should be", and the Harness turns that into "can be safely reused" mechanisms at the Node runtime. If multiple skill-family projects each implement path containment, atomic writes, resource closure, report rendering, host integration, and the state base independently, you get inconsistent security boundaries and behavioral drift. The Harness consolidates these business-neutral mechanisms into one default implementation, which callers pick up as needed.
57
34
 
58
- 比较是基于 `realpath` 之后的规范根,避免 macOS `/var → /private/var` 一类系统级符号链接造成误判。
35
+ ## Core Mental Model
59
36
 
60
- ## 测试
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`.
61
38
 
62
- `node --test` 覆盖:Contracts fixture 全量回放、安全反例、原子性失败路径、临时工作区、闭包确定性、报告事实绑定与 Markdown 注入、宿主 manifest/路径/命令信任,以及状态存储的崩溃、并发、损坏、fencing、显式恢复、symlink、硬链接与 FIFO 反例。
63
-
64
- ## 安装
39
+ ## Installation and Minimal Example
65
40
 
66
41
  ```sh
67
- npm install skill-family-harness-node@0.2.0
42
+ npm install skill-family-harness-node@0.2.1
68
43
  npm info skill-family-harness-node --help
69
44
  ```
70
45
 
71
- ## 最小示例
46
+ The minimal example shows validating a contract document inside Node:
72
47
 
73
48
  ```js
74
- // 从空目录运行:npm install skill-family-harness-node@0.2.0
49
+ // Run from an empty directory: npm install skill-family-harness-node@0.2.1
75
50
  import { validateContractDocument } from "skill-family-harness-node";
76
51
 
77
52
  const document = {
@@ -89,6 +64,158 @@ const result = validateContractDocument(document, {
89
64
  if (!result.valid) console.error(result.errorCode);
90
65
  ```
91
66
 
92
- ## 故障诊断
67
+ The code above shows the basic `validateContractDocument` call; it reuses the Contracts validator and caches instances keyed by schema, without recompiling.
68
+
69
+ ## Candidate Quickstart Profile
70
+
71
+ Use the candidate subpath to construct an observation-backed Task, wrap its terminal Result, and verify that both documents bind the exact observation bytes and correlation fields:
72
+
73
+ ```js
74
+ import {
75
+ createQuickstartTask,
76
+ wrapQuickstartResult,
77
+ verifyQuickstartExchange,
78
+ } from "skill-family-harness-node/candidate/quickstart-profile";
79
+ ```
80
+
81
+ This mechanism is intended for early integration trials of the candidate Contracts profile. It does not perform a domain audit, choose a method, retry work, or own lifecycle state. The subpath is public but **not stable** and may change or be removed in a later minor release; pin the exact package version and do not expose it through a stable consumer API.
82
+
83
+ ## Typical Use Cases
84
+
85
+ - Need to safely read/write contained paths inside Node: use path containment and atomic write.
86
+ - Need to normalize resources into a recomputable closure or generate a digest: use resource closure.
87
+ - Need to generate a human report from a machine result: use report model/render/binding/check.
88
+ - Need to persist an event log with derived snapshots: use state-store (event meaning is owned by the caller).
89
+
90
+ ## Boundaries
91
+
92
+ - Consumes `skill-family-contracts`, reusing its dialect-routed Ajv validator, Kernel Protocol, frozen error codes, and fixtures; does not copy protocol definitions or re-interpret the Schema.
93
+ - 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.
94
+ - Explicitly excluded: business semantics, task orchestration, Git writes, model calls, remote networking, and publish state. See `HARNESS_EXCLUSIONS`.
95
+
96
+ ## Public API
97
+
98
+ | Export | Responsibility |
99
+ | --- | --- |
100
+ | `HARNESS_CAPABILITIES` / `HARNESS_EXCLUSIONS` | Capability and exclusion lists (frozen constants). |
101
+ | `HarnessError` / `HARNESS_ERROR_KINDS` / `mechanismError` | Mechanism failures uniformly carry the registered error code `SFC2004`; `details.kind` gives a stable subcategory. |
102
+ | `validateContractDocument` / `getValidator` / `resolveSchemaContext` / `validatorCacheSize` | Routes and caches validators by Schema dialect; reuses Contracts' Ajv instances and dialect/policy semantics. |
103
+ | `classifyPathInput` / `resolveContained` / `readFileContained` | Path containment: intercepts path overruns, symlink escapes, and realpath escapes. |
104
+ | `writeFileAtomic` | Atomic write: leaves no half-written artifact on failure (temp file + fsync + rename). |
105
+ | `TemporaryWorkspace` / `createTemporaryWorkspace` / `withTemporaryWorkspace` | Auto-cleanup temporary workspace, cleaned up even on exception paths. |
106
+ | `digestBytes` / `computeResourceClosure` / `closureContains` | Resource closure and deterministic sha256 digest. |
107
+ | `parseRequest` / `processRequest` | Parse `operation-request`, output terminal `operation-result`. |
108
+ | `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. |
109
+ | `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. |
110
+ | `probeVersionVector` | A version-probe mechanism that disables spawn by default; when explicitly enabled, executes only absolute, symlink-free, audited vectors, using no PATH/shell. |
111
+ | `openStateStore` / `appendEvent` / `readEvents` / `verifyStateStore` / `closeStateStore` | Strict single-writer append-only event store; the event directory is the sole state authority, `chain-head.json` is only a cache. |
112
+ | `readSnapshot` / `writeSnapshot` / `rebuildSnapshot` | Atomic derived snapshots and full-event rebuild; a bad event cannot be masked by an old snapshot, and a bad snapshot can be ignored by rebuild. |
113
+ | `inspectStateStoreLock` / `recoverStateStoreLock` | Read-only lock diagnostics and explicit recovery; recovery must precisely match the observed owner + fencing. |
114
+
115
+ ## State Store Lock and Recovery Boundaries
116
+
117
+ - The lock uses exclusive create; a second writer immediately receives `store-locked`; it does not queue, nor steals the lock by time, PID, or lease expiry.
118
+ - `inspectStateStoreLock` creates no file, only returns `owner`, monotonic `fencing`, `ageMs`, and an in-recovery flag. `ageMs` is for diagnostics only and never participates in correctness decisions.
119
+ - A crash-left lock can only be recovered by the caller, after confirming outside Foundation that the old writer has terminated, by calling `recoverStateStoreLock` while submitting the precisely matching `expectedOwner`, `expectedFencing`, and `confirmOwnerTerminated: true`. A mismatch or missing confirmation fails closed.
120
+ - Recovery produces a larger fencing. The old handle re-checks owner, fencing, and acquisition id on every append; final event publication uses a same-directory temporary regular file, fsync, and exclusive link, never overwriting an existing sequence.
121
+ - Append, snapshot, close, and recovery are serialized by a short-lived `writer-mutation.lock`; recovery cannot cross an authoritative write that already holds the mutation guard.
122
+ - If the recovering process itself crashes while holding `writer-recovery.lock`, the system stays in a diagnosable deadlock state and does not auto-delete that guard. It requires fresh external forensics and manual handling; the current API does not claim to solve the scenario where an untrusted caller falsely reports "old writer terminated".
123
+ - The state root, `events/`, `snapshots/`, events, and snapshots reject symlinks, hard links, FIFOs, devices, and other non-regular entries. Payload must be pure JSON, and `eventType + payloadSchemaVersion` must hit the Schema pair frozen by the caller at open/recover.
124
+
125
+ ## Stable Error Codes
126
+
127
+ All reuse the Contracts frozen registry; no unregistered codes are added. Mechanism failures are uniformly `SFC2004` (EXECUTION_FAILED), and `details.kind` takes a stable value from `HARNESS_ERROR_KINDS`, such as `path-traversal`, `symlink-escape`, `realpath-escape`, `atomic-write-failed`, `missing-resource`, `workspace-disposed`.
128
+
129
+ Adding an entirely new SFC code is a Contracts change (the registry is inside the contracts package) and is outside this package's write set; therefore the combination "`SFC2004` + stable `details.kind`" keeps external semantics stable.
130
+
131
+ ## Path Containment Model
132
+
133
+ `resolveContained(root, rel)` is the single entry point for all filesystem access, rejecting in order:
134
+
135
+ 1. Input classification (`classifyPathInput`, a pure testable function): rejects absolute paths, Windows drive/UNC paths, backslash paths on POSIX, empty input, and NUL bytes.
136
+ 2. Lexical containment: after `path.resolve`, leaving the root → `path-traversal`.
137
+ 3. Symlink escape: the final component is a symlink (or dangling link) pointing outside the root → `symlink-escape`.
138
+ 4. Realpath escape: any intermediate symlink chain's normalized result leaves the root → `realpath-escape`.
139
+
140
+ Comparison is based on the canonical root after `realpath`, avoiding misjudgment from system-level symlinks such as macOS `/var → /private/var`.
141
+
142
+ ## Testing
143
+
144
+ `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.
145
+
146
+ ## Troubleshooting
147
+
148
+ Mechanism failures uniformly throw `SFC2004` (EXECUTION_FAILED), with `details.kind` giving a stable subcategory (e.g., `path-traversal`, `atomic-write-failed`). If it fails, check that the root path is correct and the target file is not locked.
149
+
150
+ ## Further Documentation
151
+
152
+ - Architecture boundaries and routing: [Architecture](https://ifoohoo.github.io/skill-family-engineering-kit/architecture/), [Agent architecture routing](https://ifoohoo.github.io/skill-family-engineering-kit/agents/architecture-routing/)
153
+ - Capability catalog: [capability-catalog.json](https://ifoohoo.github.io/skill-family-engineering-kit/agents/capability-catalog.json)
154
+ - Side-effect matrix: [Failure and side-effect matrix](https://ifoohoo.github.io/skill-family-engineering-kit/reference/failure-and-side-effect-matrix/)
155
+
156
+ <!-- agent-quick-reference:start -->
157
+ ## Agent Quick Reference
158
+
159
+ ### Use when
160
+
161
+ - You need to validate contracts inside Node, safely read/write contained paths, compute resource closures, or render deterministic reports.
162
+ - You need to persist an event log with derived snapshots, or normalize host adapter sources.
163
+ - You need to trial the non-stable Quickstart exchange and verify its observation/task/result binding.
164
+
165
+ ### Do not use when
166
+
167
+ - You need to put file-selection business rules into the Foundation (business rules are owned by the caller).
168
+ - You need host apply/install/update/uninstall, a full Qoder driver, or binary adapter source (explicitly unsupported).
169
+ - You need domain audit semantics, retry orchestration, or a compatibility-frozen Quickstart API.
170
+
171
+ ### Capability selection
172
+
173
+ - `foundation.harness.contract-validation`: contract validation and validator caching inside Node.
174
+ - `foundation.harness.path-containment`: path classification and contained resolution, rejecting the three escape types.
175
+ - `foundation.harness.atomic-write`: atomic write within contained paths, rolling back on failure.
176
+ - `foundation.harness.temporary-workspace`: auto-cleanup temporary workspace.
177
+ - `foundation.harness.resource-closure`: deterministic resource closure and sha256 digest.
178
+ - `foundation.harness.request-processing`: operation-request → terminal operation-result.
179
+ - `foundation.harness.report`: report-model validation/render/binding/check.
180
+ - `foundation.harness.host-adapter`: adapter source closure/build/materialize and version probe.
181
+ - `foundation.harness.state-store`: append-only events, hash chain, snapshots, and lock recovery.
182
+ - `foundation.harness.errors`: mechanism error types and stable error classes.
183
+ - `foundation.harness.quickstart-profile-candidate`: exact-version observation/task/result construction and binding verification.
184
+
185
+ ### Required inputs
186
+
187
+ - The contained root directory (the boundary for path containment).
188
+ - The document, resource, or event payload to validate/write.
189
+
190
+ ### Outputs and evidence
191
+
192
+ - Validation result, contained absolute path, atomically written file, closure digest, terminal result, report text, events/snapshots.
193
+ - Evidence: `packages/skill-family-harness-node/test/validation.test.mjs`, `atomic.test.mjs`, `containment.test.mjs`, `closure.test.mjs`, `report.test.mjs`, `state-store.test.mjs`.
194
+
195
+ ### Side effects
196
+
197
+ - Read-only filesystem access (path/atomic/workspace/state-store read and write within contained paths).
198
+ - `HARNESS_EXCLUSIONS` explicitly excludes release-state, remote-network-access, business-semantics, workflow-orchestration, model-calls, git-writes.
199
+
200
+ ### Failure semantics
201
+
202
+ - Mechanism failures are uniformly `SFC2004`, with `details.kind` as a stable subcategory (e.g., `path-traversal`, `atomic-write-failed`).
203
+ - Residual state after failure: atomic write rolls back the temp file; a broken state-store hash chain throws, and old snapshots can be ignored by rebuild.
204
+
205
+ ### Architectural invariants
206
+
207
+ - Event meaning and reducer transitions remain consumer-owned; state-store only provides the base.
208
+ - Only text adapter source (utf8) is supported; binary projection is not supported.
209
+
210
+ ### Route elsewhere when
211
+
212
+ - Business state machine / terminal states: route to loop-agent.
213
+ - Host apply: explicitly unsupported.
214
+ - Domain audit semantics: route to a standalone audit consumer.
215
+
216
+ ### Machine-readable sources
93
217
 
94
- 机制失败统一抛出 `SFC2004`(EXECUTION_FAILED),`details.kind` 给出稳定细分(如 `path-traversal`、`atomic-write-failed`)。如失败,检查 root 路径是否正确且目标文件未被锁定。
218
+ - Public capability catalog: [`capability-catalog.json`](https://ifoohoo.github.io/skill-family-engineering-kit/agents/capability-catalog.json) (`foundation.harness.*` entries).
219
+ - Package-local source: `src/*.mjs`.
220
+ - Package-local candidate source: `candidate/quickstart-profile.mjs`; public import: `skill-family-harness-node/candidate/quickstart-profile`.
221
+ <!-- agent-quick-reference:end -->
@@ -0,0 +1,222 @@
1
+ <!-- release-skill:safe-first-command -->
2
+ <!-- release-skill:external-write-boundary -->
3
+
4
+ > English version: [README.md](./README.md)
5
+
6
+ # skill-family-harness-node
7
+
8
+ <!-- release-skill:release-version: 0.2.1 -->
9
+
10
+ Contracts 机制协议的**唯一默认 Node 实现**。这是一个薄运行时(thin runtime):只实现机制协议,不引入业务语义,不做第二语言实现。
11
+
12
+ <!-- release-skill:managed:start id=latest-release -->
13
+ **0.2.1** (2026-08-10)
14
+
15
+ 本版新增 Quickstart Profile 候选交换辅助函数,并为包发布文档提供完整英文版与简体中文版。
16
+
17
+ **新增**
18
+
19
+ - 新增候选辅助函数,用于创建并复验 observation Resource、构造 Task、封装 Result,并在 Result 未精确绑定 Task 与关联字段时失败关闭。
20
+ - 新增完整的英文与简体中文包文档,并补充智能体快速参考章节。
21
+
22
+ **变更**
23
+
24
+ - 使用同一份双语版本化说明源管理当前 README 与 CHANGELOG 的发布区域。
25
+ - 项目 NOTICE 与 Apache-2.0 LICENSE 分开分发。
26
+
27
+ **升级说明**
28
+
29
+ 候选辅助函数不改变稳定 Harness API,也不引入生命周期、重试、编排、模型调用、网络或 Git 写入语义。
30
+ <!-- release-skill:managed:end id=latest-release -->
31
+
32
+ ## 解决的问题
33
+
34
+ Contracts 定义了「应当如何」,Harness 把它在 Node 运行时变成「可以安全复用」的机制。多个技能族项目如果各自实现路径收容、原子写、资源闭包、报告渲染、宿主接入与状态底座,会出现安全边界不一致、行为漂移。Harness 把这些业务中立机制收口成一份默认实现,调用方按需取用。
35
+
36
+ ## 核心心智模型
37
+
38
+ Harness 消费 `skill-family-contracts`(工作区依赖),复用其方言路由的 Ajv validator、Kernel Protocol、冻结错误码与 fixture;不复制协议定义,不重新解释 Schema。它只实现机制:Schema 校验、原子写、路径收容、临时工作区、资源闭包、operation-request → operation-result 管道、业务中立的事件日志与派生快照。明确排除:业务语义、任务编排、Git 写入、模型调用、远程网络、发布状态。见 `HARNESS_EXCLUSIONS`。
39
+
40
+ ## 安装和最小示例
41
+
42
+ ```sh
43
+ npm install skill-family-harness-node@0.2.1
44
+ npm info skill-family-harness-node --help
45
+ ```
46
+
47
+ 最小示例演示在 Node 内校验一份契约文档:
48
+
49
+ ```js
50
+ // 从空目录运行:npm install skill-family-harness-node@0.2.1
51
+ import { validateContractDocument } from "skill-family-harness-node";
52
+
53
+ const document = {
54
+ schemaVersion: 1,
55
+ kind: "skill-family.project-manifest",
56
+ project: { id: "my-project", name: "My Project", description: "Example" },
57
+ contracts: { version: "1.0.0", profile: "generic" },
58
+ managedFiles: ["package.json"],
59
+ updatedAt: "2026-01-01T00:00:00Z",
60
+ };
61
+
62
+ const result = validateContractDocument(document, {
63
+ schemaId: "https://contracts.skill-family.example/v1/project-manifest.json",
64
+ });
65
+ if (!result.valid) console.error(result.errorCode);
66
+ ```
67
+
68
+ 以上代码展示了 `validateContractDocument` 的基本调用;它复用 Contracts 的校验器并以 schema 为键缓存实例,不重新编译。
69
+
70
+ ## Candidate Quickstart Profile
71
+
72
+ 需要构造带 observation 绑定的 Task、封装终态 Result,并复验两个文档是否绑定精确 observation 字节与 correlation 字段时,使用 candidate 子路径:
73
+
74
+ ```js
75
+ import {
76
+ createQuickstartTask,
77
+ wrapQuickstartResult,
78
+ verifyQuickstartExchange,
79
+ } from "skill-family-harness-node/candidate/quickstart-profile";
80
+ ```
81
+
82
+ 以上机制用于 candidate Contracts Profile 的早期接入试验。它不执行领域审计,不选择 method,不编排重试,也不拥有生命周期状态。该子路径公开但**不稳定**,后续小版本可以修改或移除;调用方需要锁定精确包版本,并避免通过自身稳定 API 再导出。
83
+
84
+ ## 典型使用场景
85
+
86
+ - 需要在 Node 内安全地读/写受收容路径:用 path containment 与 atomic write。
87
+ - 需要把资源归一成可复算闭包或生成摘要:用 resource closure。
88
+ - 需要从机器结果生成人类报告:用 report model/render/binding/check。
89
+ - 需要持久化事件日志与派生快照:用 state-store(事件含义由调用方拥有)。
90
+
91
+ ## 边界
92
+
93
+ - 消费 `skill-family-contracts`,复用其方言路由的 Ajv validator、Kernel Protocol、冻结错误码与 fixture;不复制协议定义,不重新解释 Schema。
94
+ - 只实现机制:Schema 校验、原子写、路径收容、临时工作区、资源闭包、operation-request → operation-result 管道、业务中立的事件日志与派生快照。
95
+ - 明确排除:业务语义、任务编排、Git 写入、模型调用、远程网络、发布状态。见 `HARNESS_EXCLUSIONS`。
96
+
97
+ ## 公共 API
98
+
99
+ | 导出 | 职责 |
100
+ | --- | --- |
101
+ | `HARNESS_CAPABILITIES` / `HARNESS_EXCLUSIONS` | 能力与排除清单(冻结常量)。 |
102
+ | `HarnessError` / `HARNESS_ERROR_KINDS` / `mechanismError` | 机制失败统一携带注册错误码 `SFC2004`,`details.kind` 给出稳定细分。 |
103
+ | `validateContractDocument` / `getValidator` / `resolveSchemaContext` / `validatorCacheSize` | 按 Schema 方言路由并缓存 validator;复用 Contracts 的 Ajv 实例与 dialect/policy 语义。 |
104
+ | `classifyPathInput` / `resolveContained` / `readFileContained` | 路径收容:拦截路径越界、符号链接逃逸、真实路径逃逸。 |
105
+ | `writeFileAtomic` | 原子写:失败不留半成品(临时文件 + fsync + rename)。 |
106
+ | `TemporaryWorkspace` / `createTemporaryWorkspace` / `withTemporaryWorkspace` | 自动清理的临时工作区,异常路径也清理。 |
107
+ | `digestBytes` / `computeResourceClosure` / `closureContains` | 资源闭包与确定性 sha256 摘要。 |
108
+ | `parseRequest` / `processRequest` | 解析 `operation-request`,输出终态 `operation-result`。 |
109
+ | `validateReportModel` / `renderReportMarkdown` / `buildBinding` / `checkReport` | 消费经 Contracts 验证的 report model,确定性渲染中性 Markdown 并校验来源/结果/报告绑定;不解释业务输出。 |
110
+ | `normalizeAdapterSource` / `buildAdapterClosure` / `verifyAdapterBuildManifest` / `materializeAdapterBuild` | 通用文本 source closure、manifest 全摘要复验和目标集合原子落盘;具体 Profile/driver 不在 Harness。 |
111
+ | `probeVersionVector` | 默认禁用 spawn 的版本探测机制;显式启用时只执行绝对、无 symlink 的受审计向量,不使用 PATH/shell。 |
112
+ | `openStateStore` / `appendEvent` / `readEvents` / `verifyStateStore` / `closeStateStore` | 严格单写者的 append-only 事件存储;事件目录是唯一状态权威,`chain-head.json` 只是缓存。 |
113
+ | `readSnapshot` / `writeSnapshot` / `rebuildSnapshot` | 原子派生快照与完整事件重建;坏事件不能被旧快照掩盖,坏快照可被重建忽略。 |
114
+ | `inspectStateStoreLock` / `recoverStateStoreLock` | 只读锁诊断与显式恢复;恢复必须对观测到的 owner + fencing 做精确匹配。 |
115
+
116
+ ## 状态存储的锁与恢复边界
117
+
118
+ - 锁使用 exclusive create,第二写者立即收到 `store-locked`;不排队,也不按时间、PID 或租约过期偷锁。
119
+ - `inspectStateStoreLock` 不创建任何文件,只返回 `owner`、单调 `fencing`、`ageMs` 和恢复中标记。`ageMs` 仅供诊断,从不参与正确性判断。
120
+ - 崩溃遗留锁只能由调用方在 Foundation 之外确认旧写者已经终止后,调用 `recoverStateStoreLock`,同时提交精确匹配的 `expectedOwner`、`expectedFencing` 与 `confirmOwnerTerminated: true`。不匹配或缺少确认均失败关闭。
121
+ - 恢复产生更大的 fencing。旧 handle 每次 append 都重新核对 owner、fencing 和 acquisition id;事件最终发布使用同目录临时普通文件、fsync 和 exclusive link,绝不覆盖既有 sequence。
122
+ - append、snapshot、close 与 recovery 由短期 `writer-mutation.lock` 串行化;恢复不能越过已经持有 mutation guard 的权威写入。
123
+ - 如果恢复进程自身在持有 `writer-recovery.lock` 时崩溃,系统保持可诊断的锁死状态,不自动删除该 guard。它需要新的外部取证与人工处置;当前 API 不声称解决不可信调用方谎报“旧写者已终止”的场景。
124
+ - state root、`events/`、`snapshots/`、事件和快照拒绝 symlink、硬链接、FIFO、设备与其它非普通条目。payload 必须是纯 JSON,且 `eventType + payloadSchemaVersion` 必须命中调用方在 open/recover 时冻结的 Schema 对。
125
+
126
+ ## 稳定错误码
127
+
128
+ 全部复用 Contracts 冻结登记表,不新增未登记码。机制失败统一为 `SFC2004`(EXECUTION_FAILED),`details.kind` 取 `HARNESS_ERROR_KINDS` 中的稳定值,例如 `path-traversal`、`symlink-escape`、`realpath-escape`、`atomic-write-failed`、`missing-resource`、`workspace-disposed`。
129
+
130
+ 新增一个全新的 SFC 码属于 Contracts 变更(登记表在 contracts 包内),超出本包写集;因此用「`SFC2004` + 稳定 `details.kind`」组合保持对外语义稳定。
131
+
132
+ ## 路径收容模型
133
+
134
+ `resolveContained(root, rel)` 是所有文件系统访问的唯一入口,按序拒绝:
135
+
136
+ 1. 输入分级(`classifyPathInput`,纯函数可测):拒绝绝对路径、Windows 盘符/UNC 路径、POSIX 上的反斜杠路径、空输入、NUL 字节。
137
+ 2. 词法收容:`path.resolve` 后离开根 → `path-traversal`。
138
+ 3. 符号链接逃逸:末位组件是指向根外的符号链接(或断链)→ `symlink-escape`。
139
+ 4. 真实路径逃逸:任一中间符号链接链的规范化结果离开根 → `realpath-escape`。
140
+
141
+ 比较是基于 `realpath` 之后的规范根,避免 macOS `/var → /private/var` 一类系统级符号链接造成误判。
142
+
143
+ ## 测试
144
+
145
+ `node --test` 覆盖:Contracts fixture 全量回放、安全反例、原子性失败路径、临时工作区、闭包确定性、报告事实绑定与 Markdown 注入、宿主 manifest/路径/命令信任,以及状态存储的崩溃、并发、损坏、fencing、显式恢复、symlink、硬链接与 FIFO 反例。
146
+
147
+ ## 故障诊断
148
+
149
+ 机制失败统一抛出 `SFC2004`(EXECUTION_FAILED),`details.kind` 给出稳定细分(如 `path-traversal`、`atomic-write-failed`)。如失败,检查 root 路径是否正确且目标文件未被锁定。
150
+
151
+ ## 深入文档入口
152
+
153
+ - 架构边界与路由:[架构说明](https://ifoohoo.github.io/skill-family-engineering-kit/architecture/)、[智能体架构路由](https://ifoohoo.github.io/skill-family-engineering-kit/agents/architecture-routing/)
154
+ - 能力目录:[capability-catalog.json](https://ifoohoo.github.io/skill-family-engineering-kit/agents/capability-catalog.json)
155
+ - 副作用矩阵:[失败与副作用矩阵](https://ifoohoo.github.io/skill-family-engineering-kit/reference/failure-and-side-effect-matrix/)
156
+
157
+ <!-- agent-quick-reference:start -->
158
+ ## Agent Quick Reference
159
+
160
+ ### Use when
161
+
162
+ - 需要在 Node 内校验契约、安全读写受收容路径、计算资源闭包、渲染确定性报告。
163
+ - 需要持久化事件日志与派生快照,或归一化宿主适配源。
164
+ - 需要试用非稳定 Quickstart 交换,并复验 observation/task/result 绑定。
165
+
166
+ ### Do not use when
167
+
168
+ - 需要把文件选择的业务规则放入 Foundation(业务规则由调用方拥有)。
169
+ - 需要 host apply/install/update/uninstall、Qoder 完整 driver 或二进制 adapter source(明确 unsupported)。
170
+ - 需要领域审计语义、重试编排或兼容性已冻结的 Quickstart API。
171
+
172
+ ### Capability selection
173
+
174
+ - `foundation.harness.contract-validation`:Node 内契约校验与校验器缓存。
175
+ - `foundation.harness.path-containment`:路径分类与受收容解析,拒绝三类逃逸。
176
+ - `foundation.harness.atomic-write`:受收容路径内原子写,失败回滚。
177
+ - `foundation.harness.temporary-workspace`:自动清理的临时工作区。
178
+ - `foundation.harness.resource-closure`:确定性资源闭包与 sha256 摘要。
179
+ - `foundation.harness.request-processing`:operation-request → 终态 operation-result。
180
+ - `foundation.harness.report`:report-model 校验/渲染/绑定/检查。
181
+ - `foundation.harness.host-adapter`:adapter source closure/build/materialize 与版本探测。
182
+ - `foundation.harness.state-store`:append-only 事件、hash chain、快照与锁恢复。
183
+ - `foundation.harness.errors`:机制错误类型与稳定错误类。
184
+ - `foundation.harness.quickstart-profile-candidate`:锁定精确版本后构造 observation/task/result 并复验绑定。
185
+
186
+ ### Required inputs
187
+
188
+ - 受收容根目录(路径收容的边界)。
189
+ - 待校验/待写入的文档、资源或事件负载。
190
+
191
+ ### Outputs and evidence
192
+
193
+ - 校验结果、受收容绝对路径、原子写文件、闭包摘要、终态结果、报告文本、事件/快照。
194
+ - 证据:`packages/skill-family-harness-node/test/validation.test.mjs`、`atomic.test.mjs`、`containment.test.mjs`、`closure.test.mjs`、`report.test.mjs`、`state-store.test.mjs`。
195
+
196
+ ### Side effects
197
+
198
+ - 只读文件系统访问(path/atomic/workspace/state-store 在受收容路径内读写)。
199
+ - `HARNESS_EXCLUSIONS` 明确排除 release-state、remote-network-access、business-semantics、workflow-orchestration、model-calls、git-writes。
200
+
201
+ ### Failure semantics
202
+
203
+ - 机制失败统一 `SFC2004`,`details.kind` 为稳定细分(如 `path-traversal`、`atomic-write-failed`)。
204
+ - 失败后残余状态:原子写回滚临时文件;状态存储链断裂抛错,旧快照可被重建忽略。
205
+
206
+ ### Architectural invariants
207
+
208
+ - Event meaning and reducer transitions remain consumer-owned;state-store 只提供底座。
209
+ - 仅支持文本 adapter source(utf8),不支持二进制投影。
210
+
211
+ ### Route elsewhere when
212
+
213
+ - 业务状态机/终态:转 loop-agent。
214
+ - 宿主 apply:明确 unsupported。
215
+ - 领域审计语义:转独立审计消费者。
216
+
217
+ ### Machine-readable sources
218
+
219
+ - 公开能力目录:[`capability-catalog.json`](https://ifoohoo.github.io/skill-family-engineering-kit/agents/capability-catalog.json)(`foundation.harness.*` 条目)。
220
+ - 包内源:`src/*.mjs`。
221
+ - 包内 Candidate 源:`candidate/quickstart-profile.mjs`;公共导入:`skill-family-harness-node/candidate/quickstart-profile`。
222
+ <!-- agent-quick-reference:end -->
@@ -0,0 +1,41 @@
1
+ #!/usr/bin/env node
2
+ import { stdin, stdout, stderr } from "node:process";
3
+ import {
4
+ canonicalJson,
5
+ computeResourceClosure,
6
+ digestDocument,
7
+ } from "./quickstart-profile.mjs";
8
+
9
+ async function readRequest() {
10
+ const chunks = [];
11
+ for await (const chunk of stdin) chunks.push(chunk);
12
+ return JSON.parse(Buffer.concat(chunks).toString("utf8"));
13
+ }
14
+
15
+ export async function runMechanismCli() {
16
+ try {
17
+ const request = await readRequest();
18
+ let result;
19
+ if (request.operation === "canonical-json") {
20
+ result = { text: canonicalJson(request.document) };
21
+ } else if (request.operation === "digest-document") {
22
+ result = { digest: digestDocument(request.document) };
23
+ } else if (request.operation === "resource-closure") {
24
+ result = await computeResourceClosure({
25
+ root: request.root,
26
+ resources: request.resources,
27
+ });
28
+ } else {
29
+ throw new TypeError(`unknown Foundation mechanism: ${String(request.operation)}`);
30
+ }
31
+ stdout.write(`${JSON.stringify(result)}\n`);
32
+ return 0;
33
+ } catch (cause) {
34
+ stderr.write(`${cause?.message ?? String(cause)}\n`);
35
+ return 2;
36
+ }
37
+ }
38
+
39
+ if (import.meta.url === `file://${process.argv[1]}`) {
40
+ process.exitCode = await runMechanismCli();
41
+ }
@@ -0,0 +1,211 @@
1
+ import { canonicalJson, digestDocument } from "skill-family-contracts";
2
+ import {
3
+ QUICKSTART_PROTOCOL,
4
+ validateQuickstartProfileDocument,
5
+ } from "skill-family-contracts/candidate/quickstart-profile";
6
+ import { computeResourceClosure, digestBytes } from "../src/closure.mjs";
7
+ import { HARNESS_ERROR_KINDS, mechanismError } from "../src/errors.mjs";
8
+
9
+ function invalidProfile(kind, outcome) {
10
+ return mechanismError(
11
+ HARNESS_ERROR_KINDS.INVALID_RESULT,
12
+ `quickstart ${kind} violates the Foundation candidate profile`,
13
+ { profileKind: kind, findings: outcome.errors },
14
+ );
15
+ }
16
+
17
+ function assertProfile(kind, document) {
18
+ const outcome = validateQuickstartProfileDocument(kind, document);
19
+ if (!outcome.valid) throw invalidProfile(kind, outcome);
20
+ return outcome.data;
21
+ }
22
+
23
+ function resourcePath(resource) {
24
+ const relPath = resource?.location?.path;
25
+ if (typeof relPath !== "string") {
26
+ throw mechanismError(
27
+ HARNESS_ERROR_KINDS.INVALID_RESULT,
28
+ "the quickstart observation Resource must use a contained relative path",
29
+ );
30
+ }
31
+ return relPath;
32
+ }
33
+
34
+ /** Create one observation Resource from the actual contained file bytes. */
35
+ export async function createObservationResource({ root, path, id = "observation" } = {}) {
36
+ const closure = await computeResourceClosure({
37
+ root,
38
+ resources: [{ path, role: "input" }],
39
+ });
40
+ const record = closure.resources[0];
41
+ const resource = {
42
+ schemaVersion: 1,
43
+ kind: "skill-family.resource",
44
+ id,
45
+ location: { path: record.path },
46
+ role: "observation",
47
+ digest: { algorithm: "sha256", value: record.sha256 },
48
+ };
49
+ return assertProfile("resource", resource);
50
+ }
51
+
52
+ /** Recompute and verify the single observation Resource digest. */
53
+ export async function verifyObservationResource({ root, resource } = {}) {
54
+ const normalized = assertProfile("resource", resource);
55
+ if (normalized.role !== "observation") {
56
+ throw mechanismError(
57
+ HARNESS_ERROR_KINDS.INVALID_RESULT,
58
+ "quickstart task input must have the observation role",
59
+ );
60
+ }
61
+ const closure = await computeResourceClosure({
62
+ root,
63
+ resources: [{ path: resourcePath(normalized), role: "input" }],
64
+ });
65
+ const actual = closure.resources[0].sha256;
66
+ if (actual !== normalized.digest.value) {
67
+ throw mechanismError(
68
+ HARNESS_ERROR_KINDS.INVALID_RESULT,
69
+ "observation Resource digest does not match its current bytes",
70
+ { resourceId: normalized.id, expected: normalized.digest.value, actual },
71
+ );
72
+ }
73
+ return { resource: normalized, closureDigest: closure.digest };
74
+ }
75
+
76
+ /** Build a candidate Task inside the existing operation-request envelope. */
77
+ export async function createQuickstartTask({
78
+ root,
79
+ observationPath,
80
+ observationId = "observation",
81
+ operationId,
82
+ method,
83
+ parameters = {},
84
+ run,
85
+ stage,
86
+ attempt,
87
+ } = {}) {
88
+ const observation = await createObservationResource({
89
+ root,
90
+ path: observationPath,
91
+ id: observationId,
92
+ });
93
+ const task = {
94
+ schemaVersion: 1,
95
+ kind: "skill-family.operation-request",
96
+ protocol: { ...QUICKSTART_PROTOCOL },
97
+ operationId,
98
+ operation: "audit",
99
+ params: {
100
+ method,
101
+ parameters: structuredClone(parameters),
102
+ inputs: [observation],
103
+ correlation: { run, stage, attempt },
104
+ },
105
+ };
106
+ return assertProfile("task", task);
107
+ }
108
+
109
+ /** Wrap a domain result inside the existing terminal operation-result envelope. */
110
+ export function wrapQuickstartResult({
111
+ task,
112
+ state = "succeeded",
113
+ summary,
114
+ outputs = [],
115
+ evidence = [],
116
+ domainResult,
117
+ errors = [],
118
+ } = {}) {
119
+ const normalizedTask = assertProfile("task", task);
120
+ const observation = normalizedTask.params.inputs[0];
121
+ const result = {
122
+ schemaVersion: 1,
123
+ kind: "skill-family.operation-result",
124
+ protocol: structuredClone(normalizedTask.protocol),
125
+ operationId: normalizedTask.operationId,
126
+ operation: normalizedTask.operation,
127
+ state,
128
+ outputs: {
129
+ summary,
130
+ outputs: structuredClone(outputs),
131
+ evidence: structuredClone(evidence),
132
+ domainResult: structuredClone(domainResult),
133
+ taskBinding: {
134
+ operationId: normalizedTask.operationId,
135
+ taskDigest: digestDocument(normalizedTask),
136
+ observationId: observation.id,
137
+ observationDigest: observation.digest.value,
138
+ correlation: structuredClone(normalizedTask.params.correlation),
139
+ },
140
+ },
141
+ errors: structuredClone(errors),
142
+ };
143
+ return assertProfile("result", result);
144
+ }
145
+
146
+ /**
147
+ * Fail-closed exchange assertion. It validates both candidate profiles,
148
+ * recomputes the observation, and proves the result echoes and binds the
149
+ * exact Task/correlation fields. No retry or lifecycle state is introduced.
150
+ */
151
+ export async function assertQuickstartExchange({ root, task, result } = {}) {
152
+ const normalizedTask = assertProfile("task", task);
153
+ const normalizedResult = assertProfile("result", result);
154
+ const observation = normalizedTask.params.inputs[0];
155
+ await verifyObservationResource({ root, resource: observation });
156
+
157
+ const mismatches = [];
158
+ if (canonicalJson(normalizedResult.protocol) !== canonicalJson(normalizedTask.protocol)) {
159
+ mismatches.push("protocol");
160
+ }
161
+ if (normalizedResult.operationId !== normalizedTask.operationId) mismatches.push("operationId");
162
+ if (normalizedResult.operation !== normalizedTask.operation) mismatches.push("operation");
163
+
164
+ const binding = normalizedResult.outputs?.taskBinding;
165
+ if (!binding) {
166
+ mismatches.push("taskBinding");
167
+ } else {
168
+ if (binding.operationId !== normalizedTask.operationId) mismatches.push("binding.operationId");
169
+ if (binding.taskDigest !== digestDocument(normalizedTask)) mismatches.push("binding.taskDigest");
170
+ if (binding.observationId !== observation.id) mismatches.push("binding.observationId");
171
+ if (binding.observationDigest !== observation.digest.value) {
172
+ mismatches.push("binding.observationDigest");
173
+ }
174
+ if (
175
+ canonicalJson(binding.correlation) !==
176
+ canonicalJson(normalizedTask.params.correlation)
177
+ ) {
178
+ mismatches.push("binding.correlation");
179
+ }
180
+ }
181
+ if (mismatches.length > 0) {
182
+ throw mechanismError(
183
+ HARNESS_ERROR_KINDS.INVALID_RESULT,
184
+ `quickstart result does not bind the exact task: ${mismatches.join(", ")}`,
185
+ { mismatches },
186
+ );
187
+ }
188
+ return {
189
+ valid: true,
190
+ taskDigest: digestDocument(normalizedTask),
191
+ observationDigest: observation.digest.value,
192
+ };
193
+ }
194
+
195
+ /** Non-throwing form for callers that need a structured finding. */
196
+ export async function verifyQuickstartExchange(input) {
197
+ try {
198
+ return await assertQuickstartExchange(input);
199
+ } catch (cause) {
200
+ return {
201
+ valid: false,
202
+ code: cause?.code ?? "SFC2004",
203
+ message: cause?.message ?? String(cause),
204
+ details: cause?.details,
205
+ };
206
+ }
207
+ }
208
+
209
+ // Candidate consumers may use these generic Foundation mechanisms directly.
210
+ // They are thin exports of the stable implementations, not parallel algorithms.
211
+ export { canonicalJson, computeResourceClosure, digestBytes, digestDocument };
package/package.json CHANGED
@@ -5,15 +5,25 @@
5
5
  "url": "https://github.com/ifoohoo/skill-family-harness-node/issues"
6
6
  },
7
7
  "dependencies": {
8
- "skill-family-contracts": "0.2.0"
8
+ "skill-family-contracts": "0.2.1"
9
9
  },
10
10
  "description": "Thin Node.js mechanism runtime for Skill Family engineering contracts.",
11
11
  "engines": {
12
12
  "node": ">=22.22.2 <23"
13
13
  },
14
- "exports": "./src/index.mjs",
14
+ "exports": {
15
+ ".": "./src/index.mjs",
16
+ "./candidate/quickstart-profile": "./candidate/quickstart-profile.mjs"
17
+ },
15
18
  "files": [
16
19
  "src",
20
+ "candidate",
21
+ "release-notes",
22
+ "README.md",
23
+ "README.zh-CN.md",
24
+ "CHANGELOG.md",
25
+ "CHANGELOG.zh-CN.md",
26
+ "NOTICE",
17
27
  "SECURITY.md",
18
28
  "CONTRIBUTING.md",
19
29
  "CODE_OF_CONDUCT.md"
@@ -27,7 +37,7 @@
27
37
  "url": "https://github.com/ifoohoo/skill-family-harness-node.git"
28
38
  },
29
39
  "type": "module",
30
- "version": "0.2.0",
40
+ "version": "0.2.1",
31
41
  "scripts": {
32
42
  "check": "node --test",
33
43
  "test": "node --test"
@@ -0,0 +1,23 @@
1
+ version: 0.2.1
2
+ date: 2026-08-10
3
+ locales:
4
+ en:
5
+ summary: This release adds candidate Quickstart Profile exchange helpers and makes the package release documentation available in English and Simplified Chinese.
6
+ changes:
7
+ added:
8
+ - Adds candidate helpers that create and revalidate observation Resources, build Tasks, wrap Results, and fail closed when a Result does not bind the exact Task and correlation fields.
9
+ - Adds complete English and Simplified Chinese package documentation, including an agent quick-reference section.
10
+ changed:
11
+ - Manages the current README and CHANGELOG release sections from one bilingual, versioned notes source.
12
+ - Distributes the project NOTICE separately from the Apache-2.0 LICENSE.
13
+ upgradeNotes: The candidate helpers do not alter the stable Harness API or add lifecycle, retry, orchestration, model-call, network, or Git-write semantics.
14
+ zh-CN:
15
+ summary: 本版新增 Quickstart Profile 候选交换辅助函数,并为包发布文档提供完整英文版与简体中文版。
16
+ changes:
17
+ added:
18
+ - 新增候选辅助函数,用于创建并复验 observation Resource、构造 Task、封装 Result,并在 Result 未精确绑定 Task 与关联字段时失败关闭。
19
+ - 新增完整的英文与简体中文包文档,并补充智能体快速参考章节。
20
+ changed:
21
+ - 使用同一份双语版本化说明源管理当前 README 与 CHANGELOG 的发布区域。
22
+ - 项目 NOTICE 与 Apache-2.0 LICENSE 分开分发。
23
+ upgradeNotes: 候选辅助函数不改变稳定 Harness API,也不引入生命周期、重试、编排、模型调用、网络或 Git 写入语义。