skill-family-contracts 0.2.1 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +21 -0
- package/CHANGELOG.zh-CN.md +21 -0
- package/README.md +13 -11
- package/README.zh-CN.md +13 -11
- package/candidate/quickstart-profile/fixtures/resource-negative-absolute-path.json +13 -0
- package/candidate/quickstart-profile/fixtures/resource-negative-bad-digest.json +2 -2
- package/candidate/quickstart-profile/fixtures/resource-positive-uri.json +13 -0
- package/candidate/quickstart-profile/fixtures/resource-positive.json +2 -2
- package/candidate/quickstart-profile/index.mjs +229 -30
- package/candidate/quickstart-profile/protocol.json +64 -0
- package/candidate/quickstart-profile/resource.schema.json +6 -4
- package/candidate/quickstart-profile/result.schema.json +84 -8
- package/candidate/quickstart-profile/task.schema.json +7 -5
- package/package.json +3 -2
- package/release-notes/0.3.0.yaml +23 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,26 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
<!-- release-skill:changelog:start version=0.3.0 locale=en baseline=sha256:32a8df58662f9dbb64e142cb8a2b329556b4bd59872e48fcbc317cc971cbdd7c -->
|
|
4
|
+
## [0.3.0] - 2026-08-12
|
|
5
|
+
|
|
6
|
+
This source candidate replaces the Quickstart Profile candidate with v2 while preserving the stable Contracts registry and kernel protocol.
|
|
7
|
+
|
|
8
|
+
### Added
|
|
9
|
+
|
|
10
|
+
- Adds the v2 protocol definition with the business-neutral execute-method operation and a real $id-indexed Resource, Task, and Result schema collection.
|
|
11
|
+
- Enforces JSON-safe Task and Result boundaries, one path-backed observation, terminal output shapes, and exact evidence bindings.
|
|
12
|
+
|
|
13
|
+
### Changed
|
|
14
|
+
|
|
15
|
+
- Replaces the incompatible 0.2.1 candidate surface; consumers that still require v1 must remain pinned to exactly 0.2.1.
|
|
16
|
+
- Keeps method identifiers, parameter schemas, and domain result semantics under consumer ownership.
|
|
17
|
+
|
|
18
|
+
### Upgrade Notes
|
|
19
|
+
|
|
20
|
+
Version 0.3.0 is a local, unpublished source candidate. Pin all candidate imports to an exact package version and migrate v1 integrations before selecting this version.
|
|
21
|
+
<!-- release-skill:changelog:end version=0.3.0 locale=en -->
|
|
22
|
+
|
|
23
|
+
|
|
3
24
|
<!-- release-skill:changelog:start version=0.2.1 locale=en baseline=sha256:b759650e4d52969bd907bccea937c64622b00b0ff97851da9c6c4aee8adb4888 -->
|
|
4
25
|
## [0.2.1] - 2026-08-10
|
|
5
26
|
|
package/CHANGELOG.zh-CN.md
CHANGED
|
@@ -1,5 +1,26 @@
|
|
|
1
1
|
# 变更日志
|
|
2
2
|
|
|
3
|
+
<!-- release-skill:changelog:start version=0.3.0 locale=zh-CN baseline=sha256:5f1c4cf56cc336279cbc6f11bc6cf8ce0a7524f454ff6589e067e828707a7cc7 -->
|
|
4
|
+
## [0.3.0] - 2026-08-12
|
|
5
|
+
|
|
6
|
+
本源码候选版以 Quickstart Profile v2 替换原 candidate,同时保持稳定 Contracts 登记表和内核协议不变。
|
|
7
|
+
|
|
8
|
+
### 新增
|
|
9
|
+
|
|
10
|
+
- 新增 v2 协议定义,以业务中立的 execute-method 作为唯一操作,并按真实 $id 登记 Resource、Task、Result Schema 集合。
|
|
11
|
+
- 收紧 Task 与 Result 的 JSON-safe 边界、单一 path-backed observation、终态输出形状及 evidence 精确回指。
|
|
12
|
+
|
|
13
|
+
### 变更
|
|
14
|
+
|
|
15
|
+
- 替换与 0.2.1 不兼容的 candidate 面;仍依赖 v1 的消费者必须继续精确锁定 0.2.1。
|
|
16
|
+
- 方法标识、参数 Schema 与领域结果语义继续归消费者所有。
|
|
17
|
+
|
|
18
|
+
### 升级说明
|
|
19
|
+
|
|
20
|
+
0.3.0 当前只是本地、未发布的源码候选。candidate 导入必须精确锁定包版本,v1 接入完成迁移后才能选用本版本。
|
|
21
|
+
<!-- release-skill:changelog:end version=0.3.0 locale=zh-CN -->
|
|
22
|
+
|
|
23
|
+
|
|
3
24
|
<!-- release-skill:changelog:start version=0.2.1 locale=zh-CN baseline=sha256:1f9b53564843c024415fda87d41abf96bc22db2ec5b90d12d7a8cd7c897a24fc -->
|
|
4
25
|
## [0.2.1] - 2026-08-10
|
|
5
26
|
|
package/README.md
CHANGED
|
@@ -4,28 +4,28 @@
|
|
|
4
4
|
|
|
5
5
|
# skill-family-contracts
|
|
6
6
|
|
|
7
|
-
<!-- release-skill:release-version: 0.
|
|
7
|
+
<!-- release-skill:release-version: 0.3.0 -->
|
|
8
8
|
|
|
9
9
|
The single authoritative package of machine-executable engineering structure and mechanism protocols (Contracts 1.4.0, frozen).
|
|
10
10
|
|
|
11
11
|
<!-- release-skill:managed:start id=latest-release -->
|
|
12
|
-
**0.
|
|
12
|
+
**0.3.0** (2026-08-12)
|
|
13
13
|
|
|
14
|
-
This
|
|
14
|
+
This source candidate replaces the Quickstart Profile candidate with v2 while preserving the stable Contracts registry and kernel protocol.
|
|
15
15
|
|
|
16
16
|
**Added**
|
|
17
17
|
|
|
18
|
-
- Adds
|
|
19
|
-
-
|
|
18
|
+
- Adds the v2 protocol definition with the business-neutral execute-method operation and a real $id-indexed Resource, Task, and Result schema collection.
|
|
19
|
+
- Enforces JSON-safe Task and Result boundaries, one path-backed observation, terminal output shapes, and exact evidence bindings.
|
|
20
20
|
|
|
21
21
|
**Changed**
|
|
22
22
|
|
|
23
|
-
-
|
|
24
|
-
-
|
|
23
|
+
- Replaces the incompatible 0.2.1 candidate surface; consumers that still require v1 must remain pinned to exactly 0.2.1.
|
|
24
|
+
- Keeps method identifiers, parameter schemas, and domain result semantics under consumer ownership.
|
|
25
25
|
|
|
26
26
|
**Upgrade Notes**
|
|
27
27
|
|
|
28
|
-
|
|
28
|
+
Version 0.3.0 is a local, unpublished source candidate. Pin all candidate imports to an exact package version and migrate v1 integrations before selecting this version.
|
|
29
29
|
<!-- release-skill:managed:end id=latest-release -->
|
|
30
30
|
|
|
31
31
|
## Problem It Solves
|
|
@@ -41,14 +41,14 @@ Schema validation is based entirely on [Ajv](https://ajv.js.org/) (exact version
|
|
|
41
41
|
## Installation and Minimal Example
|
|
42
42
|
|
|
43
43
|
```sh
|
|
44
|
-
npm install skill-family-contracts@0.
|
|
44
|
+
npm install skill-family-contracts@0.3.0
|
|
45
45
|
npm info skill-family-contracts --help
|
|
46
46
|
```
|
|
47
47
|
|
|
48
48
|
The minimal example starts from an empty directory and demonstrates how to validate a registered contract object:
|
|
49
49
|
|
|
50
50
|
```js
|
|
51
|
-
// Run from an empty directory: npm install skill-family-contracts@0.
|
|
51
|
+
// Run from an empty directory: npm install skill-family-contracts@0.3.0
|
|
52
52
|
import { validateDocument } from "skill-family-contracts";
|
|
53
53
|
|
|
54
54
|
const document = {
|
|
@@ -81,7 +81,9 @@ import {
|
|
|
81
81
|
} from "skill-family-contracts/candidate/quickstart-profile";
|
|
82
82
|
```
|
|
83
83
|
|
|
84
|
-
|
|
84
|
+
Version 0.3.0 carries Quickstart Profile v2. Its protocol fixes the business-neutral operation to `execute-method`; Resource, Task, and Result schemas resolve one another through their real v2 `$id` values. Foundation validates the JSON-safe exchange shape, while the consumer owns method identifiers, parameter schemas, and domain results.
|
|
85
|
+
|
|
86
|
+
The subpath is public but **not stable**. Its schemas remain absent from `src/registry.json`, do not expand the eighteen stable object classes, and may change or be removed in a later minor release. Pin exactly `0.3.0` when evaluating v2. Integrations that still require candidate v1 must stay pinned to exactly `0.2.1`.
|
|
85
87
|
|
|
86
88
|
## Typical Use Cases
|
|
87
89
|
|
package/README.zh-CN.md
CHANGED
|
@@ -5,28 +5,28 @@
|
|
|
5
5
|
|
|
6
6
|
# skill-family-contracts
|
|
7
7
|
|
|
8
|
-
<!-- release-skill:release-version: 0.
|
|
8
|
+
<!-- release-skill:release-version: 0.3.0 -->
|
|
9
9
|
|
|
10
10
|
机器可执行工程结构和机制协议的唯一权威包(Contracts 1.4.0,冻结)。
|
|
11
11
|
|
|
12
12
|
<!-- release-skill:managed:start id=latest-release -->
|
|
13
|
-
**0.
|
|
13
|
+
**0.3.0** (2026-08-12)
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
本源码候选版以 Quickstart Profile v2 替换原 candidate,同时保持稳定 Contracts 登记表和内核协议不变。
|
|
16
16
|
|
|
17
17
|
**新增**
|
|
18
18
|
|
|
19
|
-
-
|
|
20
|
-
-
|
|
19
|
+
- 新增 v2 协议定义,以业务中立的 execute-method 作为唯一操作,并按真实 $id 登记 Resource、Task、Result Schema 集合。
|
|
20
|
+
- 收紧 Task 与 Result 的 JSON-safe 边界、单一 path-backed observation、终态输出形状及 evidence 精确回指。
|
|
21
21
|
|
|
22
22
|
**变更**
|
|
23
23
|
|
|
24
|
-
-
|
|
25
|
-
-
|
|
24
|
+
- 替换与 0.2.1 不兼容的 candidate 面;仍依赖 v1 的消费者必须继续精确锁定 0.2.1。
|
|
25
|
+
- 方法标识、参数 Schema 与领域结果语义继续归消费者所有。
|
|
26
26
|
|
|
27
27
|
**升级说明**
|
|
28
28
|
|
|
29
|
-
|
|
29
|
+
0.3.0 当前只是本地、未发布的源码候选。candidate 导入必须精确锁定包版本,v1 接入完成迁移后才能选用本版本。
|
|
30
30
|
<!-- release-skill:managed:end id=latest-release -->
|
|
31
31
|
|
|
32
32
|
## 解决的问题
|
|
@@ -42,14 +42,14 @@ Schema 验证完全基于 [Ajv](https://ajv.js.org/)(精确版本见 `package.
|
|
|
42
42
|
## 安装和最小示例
|
|
43
43
|
|
|
44
44
|
```sh
|
|
45
|
-
npm install skill-family-contracts@0.
|
|
45
|
+
npm install skill-family-contracts@0.3.0
|
|
46
46
|
npm info skill-family-contracts --help
|
|
47
47
|
```
|
|
48
48
|
|
|
49
49
|
最小示例从空目录开始,演示如何校验一份已登记契约对象:
|
|
50
50
|
|
|
51
51
|
```js
|
|
52
|
-
// 从空目录运行:npm install skill-family-contracts@0.
|
|
52
|
+
// 从空目录运行:npm install skill-family-contracts@0.3.0
|
|
53
53
|
import { validateDocument } from "skill-family-contracts";
|
|
54
54
|
|
|
55
55
|
const document = {
|
|
@@ -82,7 +82,9 @@ import {
|
|
|
82
82
|
} from "skill-family-contracts/candidate/quickstart-profile";
|
|
83
83
|
```
|
|
84
84
|
|
|
85
|
-
|
|
85
|
+
0.3.0 携带 Quickstart Profile v2。协议把业务中立的操作固定为 `execute-method`,Resource、Task、Result Schema 通过真实的 v2 `$id` 互相解析。Foundation 只校验 JSON-safe 交换结构;方法标识、参数 Schema 与领域结果继续归消费者所有。
|
|
86
|
+
|
|
87
|
+
以上子路径公开但**不稳定**。这些 Schema 不进入 `src/registry.json`,也不扩张十八类稳定对象,后续小版本可以修改或移除。评估 v2 时应精确锁定 `0.3.0`;仍依赖 candidate v1 的接入必须继续精确锁定 `0.2.1`。
|
|
86
88
|
|
|
87
89
|
## 典型使用场景
|
|
88
90
|
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schemaVersion": 1,
|
|
3
|
+
"kind": "skill-family.resource",
|
|
4
|
+
"id": "acme-review-observation",
|
|
5
|
+
"location": {
|
|
6
|
+
"path": "/etc/acme-review-observation.json"
|
|
7
|
+
},
|
|
8
|
+
"role": "observation",
|
|
9
|
+
"digest": {
|
|
10
|
+
"algorithm": "sha256",
|
|
11
|
+
"value": "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
|
|
12
|
+
}
|
|
13
|
+
}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": 1,
|
|
3
3
|
"kind": "skill-family.resource",
|
|
4
|
-
"id": "observation
|
|
4
|
+
"id": "acme-review-observation",
|
|
5
5
|
"location": {
|
|
6
|
-
"path": "inputs/observation.json"
|
|
6
|
+
"path": "inputs/acme-review-observation.json"
|
|
7
7
|
},
|
|
8
8
|
"role": "observation",
|
|
9
9
|
"digest": {
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schemaVersion": 1,
|
|
3
|
+
"kind": "skill-family.resource",
|
|
4
|
+
"id": "acme-review-evidence-uri",
|
|
5
|
+
"location": {
|
|
6
|
+
"uri": "urn:example:acme-review:evidence:0001"
|
|
7
|
+
},
|
|
8
|
+
"role": "evidence",
|
|
9
|
+
"digest": {
|
|
10
|
+
"algorithm": "sha256",
|
|
11
|
+
"value": "fedcba9876543210fedcba9876543210fedcba9876543210fedcba9876543210"
|
|
12
|
+
}
|
|
13
|
+
}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": 1,
|
|
3
3
|
"kind": "skill-family.resource",
|
|
4
|
-
"id": "observation
|
|
4
|
+
"id": "acme-review-observation",
|
|
5
5
|
"location": {
|
|
6
|
-
"path": "inputs/observation.json"
|
|
6
|
+
"path": "inputs/acme-review-observation.json"
|
|
7
7
|
},
|
|
8
8
|
"role": "observation",
|
|
9
9
|
"digest": {
|
|
@@ -1,12 +1,31 @@
|
|
|
1
1
|
import { readFileSync } from "node:fs";
|
|
2
|
-
import
|
|
2
|
+
import Ajv2020 from "ajv/dist/2020.js";
|
|
3
|
+
import { findSchemaRegistration } from "../../src/registry.mjs";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Candidate quickstart profile v2 (unstable).
|
|
7
|
+
*
|
|
8
|
+
* Unlike v1, the collection is registered under its real $ids in one
|
|
9
|
+
* dialect-matching Ajv instance: task and result reference the v2 Resource
|
|
10
|
+
* schema and the stable operation-request/operation-result envelopes by
|
|
11
|
+
* $ref, and no $ref is rewritten into a local $def. The candidate $ids stay
|
|
12
|
+
* absent from the stable registry on purpose.
|
|
13
|
+
*/
|
|
3
14
|
|
|
4
15
|
const documents = Object.freeze({
|
|
16
|
+
protocol: load("protocol.json"),
|
|
5
17
|
resource: load("resource.schema.json"),
|
|
6
18
|
task: load("task.schema.json"),
|
|
7
19
|
result: load("result.schema.json"),
|
|
8
20
|
});
|
|
9
21
|
|
|
22
|
+
const VALIDATE_KINDS = Object.freeze(["resource", "task", "result"]);
|
|
23
|
+
|
|
24
|
+
const STABLE_ENVELOPE_IDS = Object.freeze([
|
|
25
|
+
"https://contracts.skill-family.example/v1/operation-request.json",
|
|
26
|
+
"https://contracts.skill-family.example/v1/operation-result.json",
|
|
27
|
+
]);
|
|
28
|
+
|
|
10
29
|
function load(name) {
|
|
11
30
|
return Object.freeze(
|
|
12
31
|
JSON.parse(readFileSync(new URL(name, import.meta.url), "utf8")),
|
|
@@ -14,46 +33,226 @@ function load(name) {
|
|
|
14
33
|
}
|
|
15
34
|
|
|
16
35
|
export const QUICKSTART_PROFILE_ID = "quickstart-profile";
|
|
17
|
-
export const QUICKSTART_PROFILE_VERSION =
|
|
36
|
+
export const QUICKSTART_PROFILE_VERSION = 2;
|
|
18
37
|
export const QUICKSTART_PROTOCOL = Object.freeze({
|
|
19
|
-
name:
|
|
20
|
-
version:
|
|
38
|
+
name: documents.protocol.name,
|
|
39
|
+
version: documents.protocol.version,
|
|
21
40
|
});
|
|
41
|
+
export const QUICKSTART_OPERATION = Object.freeze(
|
|
42
|
+
documents.protocol.operations.map((operation) => operation.name),
|
|
43
|
+
);
|
|
44
|
+
|
|
45
|
+
/** The frozen candidate protocol definition document (not a JSON Schema). */
|
|
46
|
+
export function loadQuickstartProtocol() {
|
|
47
|
+
return structuredClone(documents.protocol);
|
|
48
|
+
}
|
|
22
49
|
|
|
23
50
|
/** Candidate-only schemas. They are deliberately absent from registry.json. */
|
|
24
51
|
export function quickstartProfileSchemas() {
|
|
25
|
-
return structuredClone(
|
|
52
|
+
return structuredClone({
|
|
53
|
+
resource: documents.resource,
|
|
54
|
+
task: documents.task,
|
|
55
|
+
result: documents.result,
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/** Enumerates the collection as registry-style entries ($id + document). */
|
|
60
|
+
export function listQuickstartProfileSchemas() {
|
|
61
|
+
return VALIDATE_KINDS.map((kind) => ({
|
|
62
|
+
kind,
|
|
63
|
+
$id: documents[kind].$id,
|
|
64
|
+
document: structuredClone(documents[kind]),
|
|
65
|
+
}));
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// The stable envelopes declare a date-time format; the candidate instance
|
|
69
|
+
// must define it identically so the envelopes compile under validateFormats.
|
|
70
|
+
const DATE_TIME_PATTERN =
|
|
71
|
+
/^\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])[Tt]([01]\d|2[0-3]):[0-5]\d:[0-5]\d(\.\d+)?([Zz]|[+-]([01]\d|2[0-3]):[0-5]\d)$/;
|
|
72
|
+
|
|
73
|
+
function isValidDateTime(value) {
|
|
74
|
+
if (typeof value !== "string" || !DATE_TIME_PATTERN.test(value)) return false;
|
|
75
|
+
const [datePart] = value.split(/[Tt]/, 1);
|
|
76
|
+
const [year, month, day] = datePart.split("-").map(Number);
|
|
77
|
+
const roundTrip = new Date(Date.UTC(year, month - 1, day));
|
|
78
|
+
return (
|
|
79
|
+
roundTrip.getUTCFullYear() === year &&
|
|
80
|
+
roundTrip.getUTCMonth() === month - 1 &&
|
|
81
|
+
roundTrip.getUTCDate() === day
|
|
82
|
+
);
|
|
26
83
|
}
|
|
27
84
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
85
|
+
let collection = null;
|
|
86
|
+
|
|
87
|
+
function getCollection() {
|
|
88
|
+
if (collection) return collection;
|
|
89
|
+
const ajv = new Ajv2020({
|
|
90
|
+
coerceTypes: false,
|
|
91
|
+
useDefaults: false,
|
|
92
|
+
allErrors: true,
|
|
93
|
+
validateFormats: true,
|
|
94
|
+
strict: true,
|
|
95
|
+
});
|
|
96
|
+
ajv.addFormat("date-time", { type: "string", validate: isValidDateTime });
|
|
97
|
+
for (const schemaId of STABLE_ENVELOPE_IDS) {
|
|
98
|
+
const registration = findSchemaRegistration(schemaId);
|
|
99
|
+
if (!registration) {
|
|
100
|
+
throw new Error(`stable envelope schema is not registered: ${schemaId}`);
|
|
35
101
|
}
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
102
|
+
ajv.addSchema(
|
|
103
|
+
JSON.parse(readFileSync(new URL(`../../${registration.file}`, import.meta.url), "utf8")),
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
for (const kind of VALIDATE_KINDS) {
|
|
107
|
+
ajv.addSchema(documents[kind]);
|
|
108
|
+
}
|
|
109
|
+
collection = Object.freeze(
|
|
110
|
+
Object.fromEntries(
|
|
111
|
+
VALIDATE_KINDS.map((kind) => [kind, ajv.getSchema(documents[kind].$id)]),
|
|
112
|
+
),
|
|
113
|
+
);
|
|
114
|
+
return collection;
|
|
46
115
|
}
|
|
47
116
|
|
|
48
|
-
|
|
117
|
+
function normalizeErrors(ajvErrors) {
|
|
118
|
+
if (!ajvErrors) return [];
|
|
119
|
+
return ajvErrors.map((error) => ({
|
|
120
|
+
keyword: error.keyword,
|
|
121
|
+
instancePath: error.instancePath,
|
|
122
|
+
schemaPath: error.schemaPath,
|
|
123
|
+
message: error.message,
|
|
124
|
+
params: error.params,
|
|
125
|
+
}));
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function escapePointerSegment(segment) {
|
|
129
|
+
return String(segment).replaceAll("~", "~0").replaceAll("/", "~1");
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const ARRAY_INDEX_PATTERN = /^(0|[1-9]\d*)$/;
|
|
133
|
+
|
|
134
|
+
// Inspects own properties through descriptors only, so accessors are refused
|
|
135
|
+
// without ever being invoked. JSON would drop symbol-keyed and non-enumerable
|
|
136
|
+
// properties silently and execute accessors, so all three classes fail closed.
|
|
137
|
+
function probeObjectMembers(value, instancePath, ancestors) {
|
|
138
|
+
const symbolKeys = Object.getOwnPropertySymbols(value);
|
|
139
|
+
if (symbolKeys.length > 0) {
|
|
140
|
+
return {
|
|
141
|
+
instancePath: `${instancePath}/${escapePointerSegment(String(symbolKeys[0]))}`,
|
|
142
|
+
reason: "symbol-keyed property",
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
const isArray = Array.isArray(value);
|
|
146
|
+
let arrayLength = 0;
|
|
147
|
+
if (isArray) {
|
|
148
|
+
const lengthDescriptor = Object.getOwnPropertyDescriptor(value, "length");
|
|
149
|
+
if (!lengthDescriptor || lengthDescriptor.get || lengthDescriptor.set) {
|
|
150
|
+
return { instancePath: `${instancePath}/length`, reason: "accessor property" };
|
|
151
|
+
}
|
|
152
|
+
arrayLength = lengthDescriptor.value;
|
|
153
|
+
}
|
|
154
|
+
for (const key of Object.getOwnPropertyNames(value)) {
|
|
155
|
+
if (isArray && key === "length") continue;
|
|
156
|
+
const memberPath = `${instancePath}/${escapePointerSegment(key)}`;
|
|
157
|
+
if (isArray && (!ARRAY_INDEX_PATTERN.test(key) || Number(key) >= arrayLength)) {
|
|
158
|
+
return { instancePath: memberPath, reason: "non-index array property" };
|
|
159
|
+
}
|
|
160
|
+
const descriptor = Object.getOwnPropertyDescriptor(value, key);
|
|
161
|
+
if (!descriptor) continue;
|
|
162
|
+
if (descriptor.get || descriptor.set) {
|
|
163
|
+
return { instancePath: memberPath, reason: "accessor property" };
|
|
164
|
+
}
|
|
165
|
+
if (!descriptor.enumerable) {
|
|
166
|
+
return { instancePath: memberPath, reason: "non-enumerable property" };
|
|
167
|
+
}
|
|
168
|
+
const issue = probeJsonValue(descriptor.value, memberPath, ancestors);
|
|
169
|
+
if (issue) return issue;
|
|
170
|
+
}
|
|
171
|
+
return null;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
function probeJsonValue(value, instancePath, ancestors) {
|
|
175
|
+
if (value === null) return null;
|
|
176
|
+
const type = typeof value;
|
|
177
|
+
if (type === "boolean" || type === "string") return null;
|
|
178
|
+
if (type === "number") {
|
|
179
|
+
return Number.isFinite(value) ? null : { instancePath, reason: "non-finite number" };
|
|
180
|
+
}
|
|
181
|
+
if (type === "bigint") return { instancePath, reason: "bigint" };
|
|
182
|
+
if (type === "undefined") return { instancePath, reason: "undefined" };
|
|
183
|
+
if (type === "function" || type === "symbol") return { instancePath, reason: type };
|
|
184
|
+
if (type !== "object") return { instancePath, reason: type };
|
|
185
|
+
const isArray = Array.isArray(value);
|
|
186
|
+
if (!isArray) {
|
|
187
|
+
const prototype = Object.getPrototypeOf(value);
|
|
188
|
+
if (prototype !== Object.prototype && prototype !== null) {
|
|
189
|
+
return {
|
|
190
|
+
instancePath,
|
|
191
|
+
reason: `non-plain object (${Object.prototype.toString.call(value)})`,
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
if (ancestors.has(value)) return { instancePath, reason: "circular reference" };
|
|
196
|
+
ancestors.add(value);
|
|
197
|
+
const issue = probeObjectMembers(value, instancePath, ancestors);
|
|
198
|
+
ancestors.delete(value);
|
|
199
|
+
return issue;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Deep JSON-safety probe for candidate documents and caller-owned values.
|
|
204
|
+
* Pure JSON data is null, booleans, finite numbers, strings, arrays, and
|
|
205
|
+
* plain objects. Anything else (bigint, undefined, function, symbol,
|
|
206
|
+
* non-finite number, non-plain object, circular reference) is reported as
|
|
207
|
+
* { instancePath, reason } at the first offending location, instead of
|
|
208
|
+
* surviving into structuredClone, digestDocument, or JSON.stringify where it
|
|
209
|
+
* would throw or silently drift. Own properties JSON would ignore or execute
|
|
210
|
+
* are refused the same way: symbol-keyed and non-enumerable properties are
|
|
211
|
+
* dropped by serialization and accessors would be executed, so the probe
|
|
212
|
+
* rejects them through descriptors without invoking any accessor. Repeated
|
|
213
|
+
* references to the same JSON-safe object stay accepted when acyclic; only
|
|
214
|
+
* true cycles fail closed. Returns null when the value is pure JSON.
|
|
215
|
+
*/
|
|
216
|
+
export function findNonJsonValue(value) {
|
|
217
|
+
return probeJsonValue(value, "", new Set());
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Validates one candidate profile document against the collection registered
|
|
222
|
+
* under real $ids. Returns { valid, errorCode, errors, data } with the same
|
|
223
|
+
* shape as the stable validateDocument; data is a normalized deep copy and
|
|
224
|
+
* caller input is never mutated. Documents containing non-JSON values are
|
|
225
|
+
* refused deterministically before any clone or serialization.
|
|
226
|
+
*/
|
|
49
227
|
export function validateQuickstartProfileDocument(kind, document) {
|
|
50
|
-
if (!
|
|
228
|
+
if (!VALIDATE_KINDS.includes(kind)) {
|
|
51
229
|
throw new TypeError(`unknown quickstart profile document kind: ${String(kind)}`);
|
|
52
230
|
}
|
|
53
|
-
const
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
231
|
+
const target = document === undefined ? null : document;
|
|
232
|
+
const jsonIssue = findNonJsonValue(target);
|
|
233
|
+
if (jsonIssue) {
|
|
234
|
+
return {
|
|
235
|
+
valid: false,
|
|
236
|
+
errorCode: "SFC1001",
|
|
237
|
+
errors: [
|
|
238
|
+
{
|
|
239
|
+
keyword: "json-value",
|
|
240
|
+
instancePath: jsonIssue.instancePath,
|
|
241
|
+
schemaPath: "#",
|
|
242
|
+
message: `value is not representable as JSON: ${jsonIssue.reason}`,
|
|
243
|
+
params: { reason: jsonIssue.reason },
|
|
244
|
+
},
|
|
245
|
+
],
|
|
246
|
+
data: null,
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
const validate = getCollection()[kind];
|
|
250
|
+
const data = structuredClone(target);
|
|
251
|
+
const valid = validate(data) === true;
|
|
252
|
+
return {
|
|
253
|
+
valid,
|
|
254
|
+
errorCode: valid ? null : "SFC1001",
|
|
255
|
+
errors: valid ? [] : normalizeErrors(validate.errors),
|
|
256
|
+
data,
|
|
257
|
+
};
|
|
59
258
|
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schemaVersion": 1,
|
|
3
|
+
"kind": "skill-family.protocol-definition",
|
|
4
|
+
"name": "skill-family.quickstart-profile",
|
|
5
|
+
"version": 2,
|
|
6
|
+
"status": "candidate",
|
|
7
|
+
"description": "Candidate v2 quickstart profile protocol. One business-neutral operation (execute-method) wraps a caller-owned method around exactly one path-backed observation Resource and binds the terminal Result back to the complete canonical Task. Foundation never registers or interprets the caller method vocabulary; this definition deliberately stays outside the stable protocol registry.",
|
|
8
|
+
"terminalStates": ["succeeded", "failed", "rejected"],
|
|
9
|
+
"operations": [
|
|
10
|
+
{
|
|
11
|
+
"name": "execute-method",
|
|
12
|
+
"status": "candidate",
|
|
13
|
+
"description": "Executes one caller-owned method. params.method identifies the method and params.parameters carries caller-owned parameters; Foundation does not interpret either. params.inputs holds exactly one path-backed observation Resource; params.correlation carries run, stage, and attempt (attempt counts from 1). succeeded means the mechanism execution completed, not that a domain conclusion passed.",
|
|
14
|
+
"params": {
|
|
15
|
+
"type": "object",
|
|
16
|
+
"additionalProperties": false,
|
|
17
|
+
"required": ["method", "parameters", "inputs", "correlation"],
|
|
18
|
+
"properties": {
|
|
19
|
+
"method": {
|
|
20
|
+
"type": "string",
|
|
21
|
+
"maxLength": 128,
|
|
22
|
+
"pattern": "^[a-z][a-z0-9]*(-[a-z0-9]+)*$",
|
|
23
|
+
"description": "Caller-owned method identifier; no Foundation method vocabulary exists."
|
|
24
|
+
},
|
|
25
|
+
"parameters": {
|
|
26
|
+
"type": "object",
|
|
27
|
+
"description": "Caller-owned parameter object; Foundation never reads its fields."
|
|
28
|
+
},
|
|
29
|
+
"inputs": {
|
|
30
|
+
"type": "array",
|
|
31
|
+
"minItems": 1,
|
|
32
|
+
"maxItems": 1,
|
|
33
|
+
"description": "Exactly one path-backed observation Resource."
|
|
34
|
+
},
|
|
35
|
+
"correlation": {
|
|
36
|
+
"type": "object",
|
|
37
|
+
"additionalProperties": false,
|
|
38
|
+
"required": ["run", "stage", "attempt"],
|
|
39
|
+
"properties": {
|
|
40
|
+
"run": {
|
|
41
|
+
"type": "string",
|
|
42
|
+
"pattern": "^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$"
|
|
43
|
+
},
|
|
44
|
+
"stage": {
|
|
45
|
+
"type": "string",
|
|
46
|
+
"pattern": "^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$"
|
|
47
|
+
},
|
|
48
|
+
"attempt": {
|
|
49
|
+
"type": "integer",
|
|
50
|
+
"minimum": 1
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
],
|
|
58
|
+
"resultContract": {
|
|
59
|
+
"outputs": {
|
|
60
|
+
"execute-method": "On succeeded, outputs is mandatory and carries summary, outputs (role=output Resources), evidence (role=evidence Resources), the caller-owned domainResult, and a taskBinding that echoes operationId, the complete canonical Task digest, the observation id/digest, and the exact run/stage/attempt correlation. taskBinding.evidenceBindings carries exactly one mechanism back-reference per evidence Resource, each fixing resourceId, operationId, observationId, and the run/stage/attempt correlation; the array only proves the evidence belongs to this Task exchange and never interprets which domain conclusion the evidence supports. On failed and rejected, outputs is mandatory and null."
|
|
61
|
+
},
|
|
62
|
+
"errors": "Every error entry carries a code registered in the frozen contracts error registry; a format-conforming but unregistered code is refused. failed and rejected results carry at least one."
|
|
63
|
+
}
|
|
64
|
+
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
-
"$id": "https://contracts.skill-family.example/candidate/quickstart-profile/
|
|
3
|
+
"$id": "https://contracts.skill-family.example/candidate/quickstart-profile/v2/resource.json",
|
|
4
4
|
"title": "QuickstartResource",
|
|
5
|
-
"description": "Candidate Resource profile for one content-addressed observation, output, or evidence item.",
|
|
5
|
+
"description": "Candidate Resource profile (v2) for one content-addressed observation, output, or evidence item. location.path and location.uri are mutually exclusive; URIs are only structurally checked and never fetched.",
|
|
6
6
|
"type": "object",
|
|
7
7
|
"additionalProperties": false,
|
|
8
8
|
"required": ["schemaVersion", "kind", "id", "location", "role", "digest"],
|
|
@@ -23,11 +23,13 @@
|
|
|
23
23
|
"properties": {
|
|
24
24
|
"path": {
|
|
25
25
|
"type": "string",
|
|
26
|
-
"
|
|
26
|
+
"maxLength": 1024,
|
|
27
|
+
"pattern": "^[A-Za-z0-9][A-Za-z0-9._-]*(?:/[A-Za-z0-9][A-Za-z0-9._-]*)*$"
|
|
27
28
|
},
|
|
28
29
|
"uri": {
|
|
29
30
|
"type": "string",
|
|
30
|
-
"
|
|
31
|
+
"maxLength": 2048,
|
|
32
|
+
"pattern": "^[A-Za-z][A-Za-z0-9+.-]*:[^\\s]+$"
|
|
31
33
|
}
|
|
32
34
|
},
|
|
33
35
|
"oneOf": [
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
-
"$id": "https://contracts.skill-family.example/candidate/quickstart-profile/
|
|
3
|
+
"$id": "https://contracts.skill-family.example/candidate/quickstart-profile/v2/result.json",
|
|
4
4
|
"title": "QuickstartResult",
|
|
5
|
-
"description": "Candidate narrow profile over the stable terminal operation-result envelope.",
|
|
5
|
+
"description": "Candidate narrow profile (v2) over the stable terminal operation-result envelope. Every terminal state fixes outputs explicitly: succeeded requires the outputs object, failed and rejected require null outputs plus at least one error whose code is registered in the frozen contracts error registry. succeeded.outputs fixes summary, outputs, evidence, domainResult, and a taskBinding whose evidenceBindings carries exactly one mechanism back-reference per evidence Resource. domainResult is an arbitrary caller-owned JSON value that Foundation never interprets.",
|
|
6
6
|
"allOf": [
|
|
7
7
|
{
|
|
8
8
|
"$ref": "https://contracts.skill-family.example/v1/operation-result.json"
|
|
@@ -13,12 +13,13 @@
|
|
|
13
13
|
"protocol": {
|
|
14
14
|
"const": {
|
|
15
15
|
"name": "skill-family.quickstart-profile",
|
|
16
|
-
"version":
|
|
16
|
+
"version": 2
|
|
17
17
|
}
|
|
18
18
|
},
|
|
19
19
|
"operation": {
|
|
20
|
-
"const": "
|
|
20
|
+
"const": "execute-method"
|
|
21
21
|
},
|
|
22
|
+
"completedAt": false,
|
|
22
23
|
"outputs": {
|
|
23
24
|
"oneOf": [
|
|
24
25
|
{
|
|
@@ -28,6 +29,36 @@
|
|
|
28
29
|
"$ref": "#/$defs/outputs"
|
|
29
30
|
}
|
|
30
31
|
]
|
|
32
|
+
},
|
|
33
|
+
"errors": {
|
|
34
|
+
"type": "array",
|
|
35
|
+
"items": {
|
|
36
|
+
"type": "object",
|
|
37
|
+
"properties": {
|
|
38
|
+
"code": {
|
|
39
|
+
"enum": [
|
|
40
|
+
"SFC1001",
|
|
41
|
+
"SFC1002",
|
|
42
|
+
"SFC1003",
|
|
43
|
+
"SFC1004",
|
|
44
|
+
"SFC1005",
|
|
45
|
+
"SFC1006",
|
|
46
|
+
"SFC1007",
|
|
47
|
+
"SFC1008",
|
|
48
|
+
"SFC1009",
|
|
49
|
+
"SFC1010",
|
|
50
|
+
"SFC1011",
|
|
51
|
+
"SFC1012",
|
|
52
|
+
"SFC2002",
|
|
53
|
+
"SFC2003",
|
|
54
|
+
"SFC2004",
|
|
55
|
+
"SFC3001",
|
|
56
|
+
"SFC3002",
|
|
57
|
+
"SFC3003"
|
|
58
|
+
]
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
31
62
|
}
|
|
32
63
|
},
|
|
33
64
|
"allOf": [
|
|
@@ -41,7 +72,6 @@
|
|
|
41
72
|
"required": ["state"]
|
|
42
73
|
},
|
|
43
74
|
"then": {
|
|
44
|
-
"type": "object",
|
|
45
75
|
"required": ["outputs"],
|
|
46
76
|
"properties": {
|
|
47
77
|
"outputs": {
|
|
@@ -49,6 +79,24 @@
|
|
|
49
79
|
}
|
|
50
80
|
}
|
|
51
81
|
}
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
"if": {
|
|
85
|
+
"properties": {
|
|
86
|
+
"state": {
|
|
87
|
+
"enum": ["failed", "rejected"]
|
|
88
|
+
}
|
|
89
|
+
},
|
|
90
|
+
"required": ["state"]
|
|
91
|
+
},
|
|
92
|
+
"then": {
|
|
93
|
+
"required": ["outputs"],
|
|
94
|
+
"properties": {
|
|
95
|
+
"outputs": {
|
|
96
|
+
"const": null
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
52
100
|
}
|
|
53
101
|
]
|
|
54
102
|
}
|
|
@@ -73,10 +121,32 @@
|
|
|
73
121
|
}
|
|
74
122
|
}
|
|
75
123
|
},
|
|
124
|
+
"evidenceBinding": {
|
|
125
|
+
"type": "object",
|
|
126
|
+
"additionalProperties": false,
|
|
127
|
+
"required": ["resourceId", "operationId", "observationId", "correlation"],
|
|
128
|
+
"properties": {
|
|
129
|
+
"resourceId": {
|
|
130
|
+
"type": "string",
|
|
131
|
+
"pattern": "^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$"
|
|
132
|
+
},
|
|
133
|
+
"operationId": {
|
|
134
|
+
"type": "string",
|
|
135
|
+
"pattern": "^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$"
|
|
136
|
+
},
|
|
137
|
+
"observationId": {
|
|
138
|
+
"type": "string",
|
|
139
|
+
"pattern": "^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$"
|
|
140
|
+
},
|
|
141
|
+
"correlation": {
|
|
142
|
+
"$ref": "#/$defs/correlation"
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
},
|
|
76
146
|
"taskBinding": {
|
|
77
147
|
"type": "object",
|
|
78
148
|
"additionalProperties": false,
|
|
79
|
-
"required": ["operationId", "taskDigest", "observationId", "observationDigest", "correlation"],
|
|
149
|
+
"required": ["operationId", "taskDigest", "observationId", "observationDigest", "correlation", "evidenceBindings"],
|
|
80
150
|
"properties": {
|
|
81
151
|
"operationId": {
|
|
82
152
|
"type": "string",
|
|
@@ -96,6 +166,12 @@
|
|
|
96
166
|
},
|
|
97
167
|
"correlation": {
|
|
98
168
|
"$ref": "#/$defs/correlation"
|
|
169
|
+
},
|
|
170
|
+
"evidenceBindings": {
|
|
171
|
+
"type": "array",
|
|
172
|
+
"items": {
|
|
173
|
+
"$ref": "#/$defs/evidenceBinding"
|
|
174
|
+
}
|
|
99
175
|
}
|
|
100
176
|
}
|
|
101
177
|
},
|
|
@@ -113,7 +189,7 @@
|
|
|
113
189
|
"items": {
|
|
114
190
|
"allOf": [
|
|
115
191
|
{
|
|
116
|
-
"$ref": "https://contracts.skill-family.example/candidate/quickstart-profile/
|
|
192
|
+
"$ref": "https://contracts.skill-family.example/candidate/quickstart-profile/v2/resource.json"
|
|
117
193
|
},
|
|
118
194
|
{
|
|
119
195
|
"type": "object",
|
|
@@ -131,7 +207,7 @@
|
|
|
131
207
|
"items": {
|
|
132
208
|
"allOf": [
|
|
133
209
|
{
|
|
134
|
-
"$ref": "https://contracts.skill-family.example/candidate/quickstart-profile/
|
|
210
|
+
"$ref": "https://contracts.skill-family.example/candidate/quickstart-profile/v2/resource.json"
|
|
135
211
|
},
|
|
136
212
|
{
|
|
137
213
|
"type": "object",
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
-
"$id": "https://contracts.skill-family.example/candidate/quickstart-profile/
|
|
3
|
+
"$id": "https://contracts.skill-family.example/candidate/quickstart-profile/v2/task.json",
|
|
4
4
|
"title": "QuickstartTask",
|
|
5
|
-
"description": "Candidate narrow profile over the stable operation-request envelope.",
|
|
5
|
+
"description": "Candidate narrow profile (v2) over the stable operation-request envelope. The single operation is the business-neutral execute-method; params.method and params.parameters are caller-owned and Foundation never interprets them. The Task digest binds the complete canonical Task, so the optional requestedAt timestamp is excluded from this profile.",
|
|
6
6
|
"allOf": [
|
|
7
7
|
{
|
|
8
8
|
"$ref": "https://contracts.skill-family.example/v1/operation-request.json"
|
|
@@ -13,12 +13,13 @@
|
|
|
13
13
|
"protocol": {
|
|
14
14
|
"const": {
|
|
15
15
|
"name": "skill-family.quickstart-profile",
|
|
16
|
-
"version":
|
|
16
|
+
"version": 2
|
|
17
17
|
}
|
|
18
18
|
},
|
|
19
19
|
"operation": {
|
|
20
|
-
"const": "
|
|
20
|
+
"const": "execute-method"
|
|
21
21
|
},
|
|
22
|
+
"requestedAt": false,
|
|
22
23
|
"params": {
|
|
23
24
|
"type": "object",
|
|
24
25
|
"additionalProperties": false,
|
|
@@ -26,6 +27,7 @@
|
|
|
26
27
|
"properties": {
|
|
27
28
|
"method": {
|
|
28
29
|
"type": "string",
|
|
30
|
+
"maxLength": 128,
|
|
29
31
|
"pattern": "^[a-z][a-z0-9]*(-[a-z0-9]+)*$"
|
|
30
32
|
},
|
|
31
33
|
"parameters": {
|
|
@@ -38,7 +40,7 @@
|
|
|
38
40
|
"items": {
|
|
39
41
|
"allOf": [
|
|
40
42
|
{
|
|
41
|
-
"$ref": "https://contracts.skill-family.example/candidate/quickstart-profile/
|
|
43
|
+
"$ref": "https://contracts.skill-family.example/candidate/quickstart-profile/v2/resource.json"
|
|
42
44
|
},
|
|
43
45
|
{
|
|
44
46
|
"type": "object",
|
package/package.json
CHANGED
|
@@ -9,7 +9,8 @@
|
|
|
9
9
|
},
|
|
10
10
|
"description": "Machine-readable contracts for Skill Family engineering.",
|
|
11
11
|
"engines": {
|
|
12
|
-
"node": ">=22.22.2 <23"
|
|
12
|
+
"node": ">=22.22.2 <23",
|
|
13
|
+
"pnpm": "10.30.0"
|
|
13
14
|
},
|
|
14
15
|
"exports": {
|
|
15
16
|
".": "./src/index.mjs",
|
|
@@ -37,7 +38,7 @@
|
|
|
37
38
|
"url": "https://github.com/ifoohoo/skill-family-contracts.git"
|
|
38
39
|
},
|
|
39
40
|
"type": "module",
|
|
40
|
-
"version": "0.
|
|
41
|
+
"version": "0.3.0",
|
|
41
42
|
"scripts": {
|
|
42
43
|
"check": "node --test",
|
|
43
44
|
"test": "node --test"
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
version: 0.3.0
|
|
2
|
+
date: 2026-08-12
|
|
3
|
+
locales:
|
|
4
|
+
en:
|
|
5
|
+
summary: This source candidate replaces the Quickstart Profile candidate with v2 while preserving the stable Contracts registry and kernel protocol.
|
|
6
|
+
changes:
|
|
7
|
+
added:
|
|
8
|
+
- Adds the v2 protocol definition with the business-neutral execute-method operation and a real $id-indexed Resource, Task, and Result schema collection.
|
|
9
|
+
- Enforces JSON-safe Task and Result boundaries, one path-backed observation, terminal output shapes, and exact evidence bindings.
|
|
10
|
+
changed:
|
|
11
|
+
- Replaces the incompatible 0.2.1 candidate surface; consumers that still require v1 must remain pinned to exactly 0.2.1.
|
|
12
|
+
- Keeps method identifiers, parameter schemas, and domain result semantics under consumer ownership.
|
|
13
|
+
upgradeNotes: Version 0.3.0 is a local, unpublished source candidate. Pin all candidate imports to an exact package version and migrate v1 integrations before selecting this version.
|
|
14
|
+
zh-CN:
|
|
15
|
+
summary: 本源码候选版以 Quickstart Profile v2 替换原 candidate,同时保持稳定 Contracts 登记表和内核协议不变。
|
|
16
|
+
changes:
|
|
17
|
+
added:
|
|
18
|
+
- 新增 v2 协议定义,以业务中立的 execute-method 作为唯一操作,并按真实 $id 登记 Resource、Task、Result Schema 集合。
|
|
19
|
+
- 收紧 Task 与 Result 的 JSON-safe 边界、单一 path-backed observation、终态输出形状及 evidence 精确回指。
|
|
20
|
+
changed:
|
|
21
|
+
- 替换与 0.2.1 不兼容的 candidate 面;仍依赖 v1 的消费者必须继续精确锁定 0.2.1。
|
|
22
|
+
- 方法标识、参数 Schema 与领域结果语义继续归消费者所有。
|
|
23
|
+
upgradeNotes: 0.3.0 当前只是本地、未发布的源码候选。candidate 导入必须精确锁定包版本,v1 接入完成迁移后才能选用本版本。
|