@aikdna/kdna-cli 0.21.1 → 0.22.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/README.md CHANGED
@@ -2,17 +2,17 @@
2
2
 
3
3
  [![npm](https://img.shields.io/npm/v/@aikdna/kdna-cli)](https://www.npmjs.com/package/@aikdna/kdna-cli) [![CI](https://github.com/aikdna/kdna-cli/actions/workflows/ci.yml/badge.svg)](https://github.com/aikdna/kdna-cli/actions/workflows/ci.yml) [![License](https://img.shields.io/badge/license-Apache%202.0-blue)](LICENSE)
4
4
 
5
- > **KDNA Ecosystem:** [`kdna`](https://github.com/aikdna/kdna) the protocol. [KDNAChat](https://github.com/aikdna/kdnachat) the consumption client. [KDNA Studio](https://github.com/aikdna/kdnastudio) the authoring tool. [KDNAWork](https://github.com/aikdna/kdnawork) the workbench. **You are here → kdna-cli** the toolchain. [Registry](https://github.com/aikdna/kdna-registry) — the catalog.
5
+ > **KDNA Core v1 is the official KDNA judgment-asset format and runtime loading contract.** `.kdna` assets are created, inspected, packed, unpacked, and validated through the **official KDNA toolchain**.
6
6
 
7
- **Role**: kdna-cli is the **runtime control plane** — the official reference implementation for asset validation, loading, installation, comparison, publishing, and agent-facing runtime workflows. It bridges Studio output to Chat/Work consumption.
7
+ **Role**: kdna-cli is the **official KDNA runtime CLI** — the official command-line entry point of the KDNA toolchain for inspecting, validating, packing, unpacking, and loading `.kdna` assets.
8
8
 
9
- **KDNA CLI is the official open-source reference implementation for KDNA verification, loading, installation, comparison, registry access, publishing, and agent-facing runtime workflows.**
9
+ **KDNA CLI is the official CLI entry of the KDNA toolchain the official command-line surface for `.kdna` asset operations.**
10
10
 
11
- It is the runtime control plane for loading, validating, composing, testing, and governing domain judgment for AI agents.
11
+ Third-party products integrate KDNA through the official SDK, CLI, Loader, or API.
12
12
 
13
- KDNA CLI 是 KDNA 验证、加载、安装、比较、注册表访问、发布和 Agent 运行时工作流的官方开源参考实现,也是 AI Agent 加载、验证、组合、测试和治理领域判断的运行控制平面。
13
+ KDNA CLI 是 KDNA 官方工具链的 CLI 入口,是 `.kdna` 资产操作的官方命令行界面。第三方产品通过官方 SDK、CLI、Loader 或 API 接入 KDNA。
14
14
 
15
- The CLI is how a KDNA domain judgment package becomes usable by agents. It installs KDNA domains, verifies their structure and trust metadata, loads them into agent-readable form, compares judgment paths with and without KDNA, and records traces for audit.
15
+ The CLI is how a `.kdna` judgment asset becomes usable by agents. It inspects, validates, packs, unpacks, and loads KDNA assets, and records traces for audit.
16
16
 
17
17
  KDNA CLI 让一个领域判断资产真正被 Agent 使用。它负责安装 KDNA、验证结构与信任信息、把 KDNA 转换成 Agent 可加载的形式、对比加载前后的判断路径,并记录可审计的使用痕迹。
18
18
 
@@ -161,108 +161,44 @@ and CLI is tracked via cross-language test vectors in the
161
161
 
162
162
  ## SPEC Compatibility
163
163
 
164
- KDNA CLI follows the canonical KDNA asset structure defined in [`aikdna/kdna`](https://github.com/aikdna/kdna).
164
+ KDNA CLI follows the canonical KDNA Container format defined in [`aikdna/kdna`](https://github.com/aikdna/kdna).
165
165
 
166
- A valid KDNA domain is a `.kdna` asset. The internal tree of that asset may include up to six standard KDNA judgment files:
166
+ A valid KDNA asset is a `.kdna` container with:
167
+ - `kdna.json` — public manifest and metadata (no judgment content)
168
+ - `payload.kdnab` — CBOR-encoded judgment payload
169
+ - `signature.kdsig` — Ed25519 signature
167
170
 
168
- - `KDNA_Core.json`
169
- - `KDNA_Patterns.json`
170
- - `KDNA_Scenarios.json`
171
- - `KDNA_Cases.json`
172
- - `KDNA_Reasoning.json`
173
- - `KDNA_Evolution.json`
171
+ The authoring source tree uses standard JSON files (KDNA_Core.json, KDNA_Patterns.json, etc.) for human editing and Git review. These files belong to the source tree only and MUST NOT appear as top-level entries in a distribution `.kdna` asset.
174
172
 
175
- The minimum valid `.kdna` asset requires these internal entries:
176
-
177
- - `KDNA_Core.json`
178
- - `KDNA_Patterns.json`
179
-
180
- Each KDNA judgment file must include `meta.version`, `meta.domain`, `meta.created`, `meta.purpose`, and `meta.load_condition`.
181
-
182
- Source directories are dev-only authoring workspaces. Public install, inspect, verify, load, compare, publish, and agent-facing commands consume `.kdna` assets or installed asset names, not source directories. To create a trusted `.kdna`, use KDNA Studio or a Studio-compatible compiler that records authoring provenance, Human Lock evidence, compiler metadata, and asset digest.
173
+ To create a trusted `.kdna`, use `kdna-studio migrate <source-dir> --out <file.kdna>` or a Studio-compatible compiler that records authoring provenance, Human Lock evidence, compiler metadata, and asset digest.
183
174
 
184
175
  ---
185
176
 
186
- ## Default Registry
177
+ ## Legacy Registry (deprecated)
187
178
 
188
- By default, KDNA CLI uses the official KDNA registry. Registry schema v3 is asset-first: installable entries must publish `asset_url`, `asset_digest`, signature metadata, trust snapshot/timestamp metadata, and revocations. Expired, yanked, revoked, or digest-mismatched assets are rejected. Users may override the registry with `KDNA_REGISTRY_URL`.
179
+ KDNA Core v1 is the **official KDNA judgment-asset format**. KDNA Core v1 does not define a registry, marketplace, or quality-badge system. Assets are loaded by path or by the official CLI, not by registry name.
189
180
 
190
- ```bash
191
- # Use the official registry (default)
192
- kdna install @aikdna/writing
181
+ The legacy `kdna install <name>` command and the `KDNA_REGISTRY_URL` env var are preserved for backward compatibility with the legacy `kdna-registry` repository (marked as a legacy experiment). New KDNA Core v1 integrations should use the official CLI route:
193
182
 
194
- # Use a custom registry
195
- export KDNA_REGISTRY_URL="https://my-registry.example.com/domains.json"
196
- kdna install @myorg/internal
183
+ ```bash
184
+ kdna inspect <path>
185
+ kdna validate <path>
186
+ kdna pack <source-dir> <output.kdna>
187
+ kdna unpack <input.kdna> <output-dir>
197
188
  ```
198
189
 
199
190
  ---
200
191
 
201
- ## Open Source and Commercial Boundary
202
-
203
- KDNA keeps the protocol, schemas, validator, core CLI, benchmark tools, and reference examples open source.
204
-
205
- Commercial or hosted layers may include:
206
-
207
- - Managed registry services
208
- - Quality badge review workflows
209
- - Hosted runtime guard
210
- - Enterprise private registry
211
- - Team collaboration in KDNA Studio
212
- - Licensed/private judgment asset distribution
213
-
214
- KDNA supports both open judgment assets and licensed/private judgment assets. Open assets remain the default path for community adoption. Licensed assets still use the `.kdna` extension; protected entries are decrypted only in memory after local license activation.
215
-
216
- KDNA 同时支持开放判断资产和授权/私有判断资产。开放资产是社区采用的默认路径;授权资产仍然使用 `.kdna` 后缀,受保护条目只会在本地 license activation 通过后以内存方式解密。
217
-
218
- ---
219
-
220
- ## Environment Variables
221
-
222
- | Variable | Purpose |
223
- | ------------------- | --------------------------------------------------------------------------- |
224
- | `KDNA_AGENT` | Override agent name in trace logs (e.g. `claude_code`, `codex`, `opencode`) |
225
- | `KDNA_REGISTRY_URL` | Override canonical registry URL |
226
- | `KDNA_IDENTITY_DIR` | Override identity key directory |
227
-
228
- ## Exit Codes
229
-
230
- | Code | Name | Meaning |
231
- | ---- | ------------------------- | -------------------------------------------------- |
232
- | 0 | `OK` | Success |
233
- | 1 | `VALIDATION_FAILED` | Structure or schema validation failed |
234
- | 2 | `INPUT_ERROR` | Invalid input, missing argument, not found |
235
- | 3 | `TRUST_FAILED` | Signature or trust verification failed |
236
- | 4 | `JUDGMENT_QUALITY_FAILED` | Judgment governance fields missing or insufficient |
237
- | 5 | `REGISTRY_ERROR` | Registry lookup or network error |
238
- | 6 | `PROVIDER_ERROR` | LLM provider (API key, rate limit) error |
239
- | 7 | `POLICY_VIOLATION` | Publishing or governance policy violation |
240
- | 8 | `HUMAN_LOCK_REQUIRED` | Human lock required but not present |
241
-
242
- ## JSON Output
243
-
244
- Machine-consumable commands support `--json` for structured output:
245
-
246
- ```bash
247
- kdna verify @aikdna/writing --json
248
- kdna available --json
249
- kdna doctor --agents --json
250
- kdna trace --json
251
- kdna history --json
252
- kdna license verify --json <file>
253
- ```
192
+ ## Official toolchain components
254
193
 
255
- ## Product Matrix
194
+ KDNA Core v1 is the **official KDNA judgment-asset format**. `.kdna` assets are created, inspected, protected, loaded, and consumed through the official KDNA toolchain. Third-party products integrate KDNA through the official SDK, CLI, Loader, or API.
256
195
 
257
- | Layer | Product | Responsibility |
258
- | ------------ | ----------------------- | ----------------------------------------------------------------------------- |
259
- | Protocol | KDNA SPEC | Define judgment asset format |
260
- | Core Library | @aikdna/kdna-core | load / validate / compose / render |
261
- | Runtime | @aikdna/kdna-cli | install / verify / load / compare / publish existing assets / license / trace |
262
- | Authoring | KDNA Studio | author / lock / compile / export / sign / encrypt |
263
- | Consumption | KDNAChat | Load, use, compare |
264
- | Governance | KDNA Governance Console | Approve, release, audit |
265
- | Distribution | Registry | Discover, install, license, distribute |
196
+ | Layer | Component | Responsibility |
197
+ | ------ | -------------------------- | --------------- |
198
+ | Format | KDNA Core | Official KDNA judgment-asset format and runtime loading contract |
199
+ | Core Library | @aikdna/kdna-core | Official loader SDK |
200
+ | Runtime | @aikdna/kdna-cli | Official CLI: inspect, validate, pack, unpack, load |
201
+ | Authoring | KDNA Studio Core | Official authoring kernel |
266
202
 
267
203
  ## Development
268
204
 
@@ -275,9 +211,8 @@ npm test
275
211
 
276
212
  ## Related
277
213
 
278
- - [@aikdna/kdna-core](https://github.com/aikdna/kdna/tree/main/packages/kdna-core) — Pure logic library
279
- - [KDNA Registry](https://github.com/aikdna/kdna-registry) — Domain catalog
280
- - [KDNA SPEC](https://github.com/aikdna/kdna) — Protocol specification
214
+ - [@aikdna/kdna-core](https://github.com/aikdna/kdna/tree/main/packages/kdna-core) — Official loader SDK
215
+ - [KDNA SPEC](https://github.com/aikdna/kdna) — Official KDNA Core format
281
216
  - [aikdna.com](https://aikdna.com) — Website
282
217
 
283
218
  ## License
@@ -0,0 +1,6 @@
1
+ {
2
+ "algorithm": "sha256",
3
+ "manifest_digest": "sha256:placeholder",
4
+ "payload_digest": "sha256:placeholder",
5
+ "asset_digest": "sha256:placeholder"
6
+ }
@@ -0,0 +1,42 @@
1
+ {
2
+ "kdna_version": "1.0",
3
+ "asset_id": "kdna:example:atomspeak-core",
4
+ "asset_uid": "urn:uuid:00000000-0000-4000-8000-000000000001",
5
+ "asset_type": "domain",
6
+ "title": "Atomspeak Core",
7
+ "version": "1.0.0",
8
+ "judgment_version": "1.0.0",
9
+ "created_at": "2026-06-16T00:00:00Z",
10
+ "updated_at": "2026-06-16T00:00:00Z",
11
+ "creator": {
12
+ "name": "Example Author",
13
+ "id": "example-author"
14
+ },
15
+ "compatibility": {
16
+ "min_loader_version": "1.0.0",
17
+ "profile": "judgment-profile-v1"
18
+ },
19
+ "payload": {
20
+ "path": "payload.kdnab",
21
+ "encoding": "json",
22
+ "encrypted": false
23
+ },
24
+ "summary": "Phase 1 minimal example. The smallest valid KDNA v1 asset.",
25
+ "language": "en",
26
+ "license": "Apache-2.0",
27
+ "keywords": ["example", "minimal", "phase-1"],
28
+ "lineage": {
29
+ "type": "original",
30
+ "fork_of": null,
31
+ "derived_from": null
32
+ },
33
+ "load_contract": {
34
+ "default_profile": "compact",
35
+ "profiles": {
36
+ "index": { "requires_decryption": false, "max_tokens_hint": 200 },
37
+ "compact": { "requires_decryption": false, "max_tokens_hint": 500 },
38
+ "scenario": { "requires_decryption": false, "selection": "triggered_sections_only" },
39
+ "full": { "requires_decryption": false, "intended_for": ["audit", "reference"] }
40
+ }
41
+ }
42
+ }
@@ -0,0 +1 @@
1
+ application/vnd.kdna.asset
@@ -0,0 +1,31 @@
1
+ {
2
+ "profile": "judgment-profile-v1",
3
+ "core": {
4
+ "highest_question": "What does this minimal example demonstrate?",
5
+ "axioms": [
6
+ {
7
+ "id": "a1",
8
+ "one_sentence": "The minimal payload is the smallest shape that passes the schema."
9
+ }
10
+ ],
11
+ "boundaries": [],
12
+ "risk_model": {}
13
+ },
14
+ "patterns": [],
15
+ "scenarios": [],
16
+ "cases": [],
17
+ "reasoning": {
18
+ "self_checks": [],
19
+ "failure_modes": []
20
+ },
21
+ "evolution": {
22
+ "changelog": [
23
+ {
24
+ "version": "1.0.0",
25
+ "date": "2026-06-16",
26
+ "changes": "Initial Phase 1 example."
27
+ }
28
+ ],
29
+ "version_notes": []
30
+ }
31
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aikdna/kdna-cli",
3
- "version": "0.21.1",
3
+ "version": "0.22.1",
4
4
  "description": "KDNA CLI — runtime control plane for verifying, installing, loading, comparing, publishing, and auditing existing .kdna assets.",
5
5
  "type": "commonjs",
6
6
  "bin": {
@@ -15,25 +15,31 @@
15
15
  "skills/",
16
16
  "LICENSE",
17
17
  "NOTICE",
18
- "SECURITY.md"
18
+ "SECURITY.md",
19
+ "schema/",
20
+ "fixtures/"
19
21
  ],
20
22
  "scripts": {
21
23
  "lint": "eslint src/ validators/ tests/",
22
24
  "format": "prettier --write .",
23
25
  "format:check": "prettier --check .",
24
- "test": "node --test tests/v07-commands.test.js tests/v012-commands.test.js tests/asset-store.test.js",
26
+ "test": "npm run test:v1 && npm run test:smoke",
25
27
  "test:integration": "node --test tests/integration.test.js",
26
- "test:all": "node --test tests/*.test.js",
28
+ "test:all": "npm run test:v1 && npm run test:smoke && npm run test:legacy",
29
+ "test:core-smoke": "node scripts/pretest-smoke.js",
27
30
  "release:preflight": "node scripts/release-preflight.js",
28
- "pretest": "npm install --ignore-scripts"
31
+ "test:v1": "node --test tests/v1-global-cli.test.js",
32
+ "test:smoke": "npm run test:core-smoke",
33
+ "test:legacy": "node --test tests/v07-commands.test.js tests/v012-commands.test.js tests/asset-store.test.js"
29
34
  },
30
35
  "keywords": [
31
36
  "kdna",
32
37
  "kdna-cli",
33
38
  "ai-agent",
34
39
  "domain-judgment",
35
- "judgment-protocol",
36
- "cli"
40
+ "cli",
41
+ "judgment-asset",
42
+ "official-toolchain"
37
43
  ],
38
44
  "license": "Apache-2.0",
39
45
  "repository": {
@@ -48,10 +54,10 @@
48
54
  "node": ">=18"
49
55
  },
50
56
  "dependencies": {
51
- "@aikdna/kdna-core": "^0.8.0"
57
+ "@aikdna/kdna-core": "^0.9.1"
52
58
  },
53
59
  "optionalDependencies": {
54
- "ajv": "^8.17.1",
60
+ "ajv": "^8.20.0",
55
61
  "ajv-formats": "^3.0.1",
56
62
  "cbor-x": "^1.6.4"
57
63
  },
@@ -0,0 +1,43 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://github.com/aikdna/kdna/schema/checksums.schema.json",
4
+ "title": "KDNA Checksums v1",
5
+ "description": "Per-entry digests for a .kdna container. Stored as checksums.json at the container root.",
6
+ "type": "object",
7
+ "required": ["algorithm"],
8
+ "additionalProperties": true,
9
+ "properties": {
10
+ "algorithm": {
11
+ "type": "string",
12
+ "description": "Hash algorithm. Phase 1 uses sha256.",
13
+ "enum": ["sha256", "sha512", "blake2b-256"]
14
+ },
15
+ "manifest_digest": {
16
+ "type": "string",
17
+ "description": "Digest of the kdna.json entry.",
18
+ "pattern": "^[a-z0-9-]+:.+$"
19
+ },
20
+ "payload_digest": {
21
+ "type": "string",
22
+ "description": "Digest of the payload.kdnab entry.",
23
+ "pattern": "^[a-z0-9-]+:.+$"
24
+ },
25
+ "asset_digest": {
26
+ "type": "string",
27
+ "description": "Digest of the whole container (per-entry digests combined deterministically).",
28
+ "pattern": "^[a-z0-9-]+:.+$"
29
+ },
30
+ "entries": {
31
+ "type": "object",
32
+ "description": "Per-entry digests, keyed by entry name within the container.",
33
+ "additionalProperties": {
34
+ "type": "object",
35
+ "required": ["algorithm", "value"],
36
+ "properties": {
37
+ "algorithm": { "type": "string" },
38
+ "value": { "type": "string" }
39
+ }
40
+ }
41
+ }
42
+ }
43
+ }
@@ -0,0 +1,41 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://github.com/aikdna/kdna/schema/load-contract.schema.json",
4
+ "title": "KDNA Load Contract",
5
+ "description": "Manifest block that describes how a loader may read the asset. See docs/core/load-contract.md.",
6
+ "type": "object",
7
+ "required": ["default_profile", "profiles"],
8
+ "additionalProperties": true,
9
+ "properties": {
10
+ "default_profile": {
11
+ "type": "string",
12
+ "enum": ["index", "compact", "scenario", "full"]
13
+ },
14
+ "profiles": {
15
+ "type": "object",
16
+ "required": ["index", "compact", "scenario", "full"],
17
+ "additionalProperties": false,
18
+ "properties": {
19
+ "index": { "$ref": "#/$defs/profile" },
20
+ "compact": { "$ref": "#/$defs/profile" },
21
+ "scenario": { "$ref": "#/$defs/profile" },
22
+ "full": { "$ref": "#/$defs/profile" }
23
+ }
24
+ }
25
+ },
26
+ "$defs": {
27
+ "profile": {
28
+ "type": "object",
29
+ "additionalProperties": true,
30
+ "properties": {
31
+ "requires_decryption": { "type": "boolean" },
32
+ "max_tokens_hint": { "type": "integer", "minimum": 0 },
33
+ "selection": { "type": "string" },
34
+ "intended_for": {
35
+ "type": "array",
36
+ "items": { "type": "string" }
37
+ }
38
+ }
39
+ }
40
+ }
41
+ }
@@ -0,0 +1,198 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://github.com/aikdna/kdna/schema/manifest.schema.json",
4
+ "title": "KDNA Manifest v1",
5
+ "description": "Schema for kdna.json, the public metadata layer of a .kdna container.",
6
+ "type": "object",
7
+ "required": [
8
+ "kdna_version",
9
+ "asset_id",
10
+ "asset_uid",
11
+ "asset_type",
12
+ "title",
13
+ "version",
14
+ "judgment_version",
15
+ "created_at",
16
+ "updated_at",
17
+ "creator",
18
+ "compatibility",
19
+ "payload"
20
+ ],
21
+ "additionalProperties": true,
22
+ "properties": {
23
+ "kdna_version": {
24
+ "type": "string",
25
+ "description": "KDNA Core version this manifest conforms to.",
26
+ "const": "1.0"
27
+ },
28
+ "asset_id": {
29
+ "type": "string",
30
+ "description": "Human-readable identifier with format namespace prefix (e.g. kdna:example:foo).",
31
+ "pattern": "^[a-zA-Z][a-zA-Z0-9_-]*(:[a-zA-Z0-9_.-]+)+$"
32
+ },
33
+ "asset_uid": {
34
+ "type": "string",
35
+ "description": "Globally unique identifier. URN form recommended.",
36
+ "format": "uri"
37
+ },
38
+ "asset_type": {
39
+ "type": "string",
40
+ "enum": ["domain", "cluster", "tool", "sample", "fixture"],
41
+ "description": "What kind of asset this is. Container format is the same; the value tells callers how to interpret the payload."
42
+ },
43
+ "title": {
44
+ "type": "string",
45
+ "minLength": 1,
46
+ "description": "Short, human-readable title."
47
+ },
48
+ "version": {
49
+ "type": "string",
50
+ "description": "Release version of this .kdna file (semver).",
51
+ "pattern": "^[0-9]+\\.[0-9]+\\.[0-9]+([+-].+)?$"
52
+ },
53
+ "judgment_version": {
54
+ "type": "string",
55
+ "description": "Semantic version of the encoded judgment system.",
56
+ "pattern": "^[0-9]+\\.[0-9]+\\.[0-9]+([+-].+)?$"
57
+ },
58
+ "created_at": {
59
+ "type": "string",
60
+ "format": "date-time",
61
+ "description": "When the asset was first created (ISO 8601)."
62
+ },
63
+ "updated_at": {
64
+ "type": "string",
65
+ "format": "date-time",
66
+ "description": "When this release of the asset was produced (ISO 8601)."
67
+ },
68
+ "creator": {
69
+ "type": "object",
70
+ "required": ["name"],
71
+ "additionalProperties": true,
72
+ "properties": {
73
+ "name": { "type": "string", "minLength": 1 },
74
+ "id": { "type": "string" }
75
+ },
76
+ "description": "Who produced the asset. Provenance only; not a trust claim."
77
+ },
78
+ "compatibility": {
79
+ "type": "object",
80
+ "required": ["min_loader_version", "profile"],
81
+ "additionalProperties": true,
82
+ "properties": {
83
+ "min_loader_version": {
84
+ "type": "string",
85
+ "pattern": "^[0-9]+\\.[0-9]+\\.[0-9]+([+-].+)?$"
86
+ },
87
+ "profile": {
88
+ "type": "string",
89
+ "description": "Payload profile identifier (e.g. judgment-profile-v1).",
90
+ "const": "judgment-profile-v1"
91
+ }
92
+ }
93
+ },
94
+ "payload": {
95
+ "type": "object",
96
+ "required": ["path", "encoding", "encrypted"],
97
+ "additionalProperties": true,
98
+ "properties": {
99
+ "path": { "type": "string", "const": "payload.kdnab" },
100
+ "encoding": { "type": "string", "enum": ["json", "cbor"] },
101
+ "encrypted": { "type": "boolean" }
102
+ }
103
+ },
104
+ "summary": { "type": "string" },
105
+ "description": { "type": "string" },
106
+ "language": { "type": "string" },
107
+ "languages": { "type": "array", "items": { "type": "string" } },
108
+ "license": {
109
+ "oneOf": [
110
+ { "type": "string" },
111
+ {
112
+ "type": "object",
113
+ "required": ["type"],
114
+ "properties": {
115
+ "type": { "type": "string" },
116
+ "url": { "type": "string" }
117
+ }
118
+ }
119
+ ]
120
+ },
121
+ "keywords": { "type": "array", "items": { "type": "string" } },
122
+ "domain_field": { "type": "array", "items": { "type": "string" } },
123
+ "lineage": {
124
+ "type": "object",
125
+ "description": "Provenance object describing how this asset relates to other assets. Phase 1 keeps this a single object (not an array) to avoid the unstable multi-source shape; future phases may add a separate `sources` or `references` field if multi-parent derivation becomes a real requirement.",
126
+ "required": ["type"],
127
+ "additionalProperties": true,
128
+ "properties": {
129
+ "type": {
130
+ "type": "string",
131
+ "enum": [
132
+ "original",
133
+ "fork",
134
+ "adaptation",
135
+ "translation",
136
+ "private_variant",
137
+ "organization_variant",
138
+ "course_variant"
139
+ ]
140
+ },
141
+ "fork_of": {
142
+ "oneOf": [
143
+ { "type": "null" },
144
+ { "type": "string" },
145
+ {
146
+ "type": "object",
147
+ "required": ["asset_uid"],
148
+ "properties": {
149
+ "asset_uid": { "type": "string" },
150
+ "version": { "type": "string" }
151
+ }
152
+ }
153
+ ]
154
+ },
155
+ "derived_from": {
156
+ "oneOf": [
157
+ { "type": "null" },
158
+ { "type": "string" },
159
+ { "type": "array", "items": { "type": "string" } },
160
+ {
161
+ "type": "object",
162
+ "additionalProperties": true
163
+ }
164
+ ]
165
+ }
166
+ }
167
+ },
168
+ "digests": {
169
+ "type": "object",
170
+ "description": "Per-entry digests. Alternative to a separate checksums.json.",
171
+ "additionalProperties": {
172
+ "type": "object",
173
+ "required": ["algorithm", "value"],
174
+ "properties": {
175
+ "algorithm": { "type": "string" },
176
+ "value": { "type": "string" }
177
+ }
178
+ }
179
+ },
180
+ "signatures": {
181
+ "type": "array",
182
+ "items": {
183
+ "type": "object",
184
+ "required": ["role", "path", "algorithm"],
185
+ "properties": {
186
+ "role": { "type": "string" },
187
+ "path": { "type": "string" },
188
+ "algorithm": { "type": "string" },
189
+ "key_id": { "type": "string" }
190
+ }
191
+ }
192
+ },
193
+ "encryption": { "type": "object" },
194
+ "load_contract": { "$ref": "load-contract.schema.json" },
195
+ "scope": { "type": "object" },
196
+ "evidence_claims": { "type": "object" }
197
+ }
198
+ }