@aikdna/kdna-studio-core 1.5.3 → 1.5.5
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 +87 -27
- package/package.json +5 -5
- package/src/compile/index.js +11 -5
- package/src/export-runtime/index.js +220 -0
- package/src/index.js +2 -0
- package/src/packaging/index.js +1 -1
- package/src/project/index.js +2 -2
- package/src/quality/index.js +8 -5
package/README.md
CHANGED
|
@@ -1,17 +1,34 @@
|
|
|
1
1
|
# KDNA Studio Core
|
|
2
2
|
|
|
3
|
-
**KDNA Studio Core is the judgment asset refinery.** It turns scattered notes, documents, works, and feedback into loadable
|
|
3
|
+
**KDNA Studio Core is the judgment asset refinery.** It turns scattered notes, documents, works, and feedback into loadable `.kdna` judgment assets — not by compressing content, but by distilling stable judgment patterns into a declared domain and loading scope.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
Studio Core is part of the official authoring toolchain. It can record human review, Human Lock, signatures, and release evidence as provenance signals, but those signals are not KDNA Core v1 format-validity requirements and they do not make content officially endorsed, certified, or universally safe.
|
|
6
6
|
|
|
7
|
-
Open-source Studio-compatible authoring kernel for creating
|
|
7
|
+
Open-source Studio-compatible authoring kernel for creating reviewable `.kdna` assets — JS/npm package. Supports two authoring paths: interview-first (direct expression) and distillation-first (pattern extraction from existing content). Both end with a canonical KDNA Core v1 runtime export.
|
|
8
8
|
|
|
9
|
-
**KDNA Studio Core is the JS authoring kernel.** It is not a UI tool and not a CLI package. It is a pure-logic engine for creating KDNA judgment cards,
|
|
9
|
+
**KDNA Studio Core is the JS authoring kernel.** It is not a UI tool and not a CLI package. It is a pure-logic engine for creating KDNA judgment cards, optional provenance records, compiler output, and runtime `.kdna` exports from JavaScript applications and Studio-compatible tools.
|
|
10
10
|
|
|
11
|
-
A `.kdna` asset is not
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
optional encryption, and provenance
|
|
11
|
+
A `.kdna` asset is not a public source JSON folder. Studio-compatible tooling
|
|
12
|
+
uses a project workspace for authoring, review, and audit, then exports the
|
|
13
|
+
canonical KDNA Core v1 runtime container. Human confirmation, Human Lock,
|
|
14
|
+
signing, optional encryption, and release evidence are provenance layers, not
|
|
15
|
+
format-validity requirements.
|
|
16
|
+
|
|
17
|
+
Studio Core distinguishes authoring compile output from runtime distribution
|
|
18
|
+
output. Authoring compile output may include source entries such as
|
|
19
|
+
`KDNA_Core.json` and `KDNA_Patterns.json` for audit and review. Runtime export
|
|
20
|
+
must produce the canonical KDNA Core v1 distribution shape:
|
|
21
|
+
|
|
22
|
+
```text
|
|
23
|
+
mimetype
|
|
24
|
+
kdna.json
|
|
25
|
+
payload.kdnab
|
|
26
|
+
checksums.json
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Runtime export must validate with `@aikdna/kdna-core` and must plan through the
|
|
30
|
+
LoadPlan contract in `aikdna/kdna`. Studio products must not create app-private
|
|
31
|
+
`.kdna` shapes that Chat or CLI cannot inspect, validate, or plan-load.
|
|
15
32
|
|
|
16
33
|
**Hard boundary:** Optional encryption, when supported, MUST be represented as
|
|
17
34
|
protected entries inside the `.kdna` container (RFC-0008). App-private encrypted
|
|
@@ -21,8 +38,8 @@ conforming KDNA runtime assets.
|
|
|
21
38
|
| Library | Language | Role |
|
|
22
39
|
|---------|----------|------|
|
|
23
40
|
| `@aikdna/kdna-cli` | JS/npm | **Operate** KDNA — install, verify, load, compare, publish |
|
|
24
|
-
| **`@aikdna/kdna-studio-core`** | JS/npm | **Authoring kernel** — project model, cards,
|
|
25
|
-
| `@aikdna/kdna-studio-cli` | JS/npm | **Create via CLI** — `kdna-studio` create,
|
|
41
|
+
| **`@aikdna/kdna-studio-core`** | JS/npm | **Authoring kernel** — project model, cards, review/provenance, compiler, runtime export |
|
|
42
|
+
| `@aikdna/kdna-studio-cli` | JS/npm | **Create via CLI** — `kdna-studio` create, review, export |
|
|
26
43
|
| `@aikdna/kdna-core` | JS/npm | **Use** KDNA — load, validate, format |
|
|
27
44
|
|
|
28
45
|
## What it does
|
|
@@ -33,14 +50,16 @@ conforming KDNA runtime assets.
|
|
|
33
50
|
- **Evidence Relevance** — classify source material as relevant, weakly relevant, out-of-scope, or split-domain before distillation
|
|
34
51
|
- **Scope Gate** — mark candidates with `scope_fit`, relevance score, and suggested split domain before they can become cards
|
|
35
52
|
- **Judgment Cards** — 8 card types: axiom, ontology, stance, framework, misunderstanding, self_check, banned_term, terminology
|
|
36
|
-
- **
|
|
53
|
+
- **Review and provenance** — AI may propose candidates; projects can record human confirmation, Human Lock, signatures, and release evidence when needed.
|
|
37
54
|
- **Authoring Provenance** — every compiled manifest records Studio-compatible
|
|
38
|
-
compiler metadata, project digest,
|
|
39
|
-
- **Asset Build Reports** — every compile emits build, provenance,
|
|
55
|
+
compiler metadata, project digest, review counts, and confirmation status.
|
|
56
|
+
- **Asset Build Reports** — every compile emits build, provenance, review,
|
|
40
57
|
quality gate, eval, and receipt artifacts for audit.
|
|
41
58
|
- **Feynman Restatement** — verify understanding, not just agreement
|
|
42
59
|
- **Quality Gates** — readiness check: draft → structurally_ready → judgment_ready → publish_ready
|
|
43
60
|
- **Compiler** — locked cards → `KDNA_Core.json` + `KDNA_Patterns.json`
|
|
61
|
+
- **Runtime Export** — compiled judgment → canonical `mimetype` +
|
|
62
|
+
`kdna.json` + `payload.kdnab` + `checksums.json`
|
|
44
63
|
- **Test Lab** — A/B comparison (No KDNA vs Best Prompt vs KDNA)
|
|
45
64
|
- **Provenance** — content fingerprinting, build tracking, audit trail
|
|
46
65
|
|
|
@@ -54,14 +73,14 @@ conforming KDNA runtime assets.
|
|
|
54
73
|
## Authoring Flow
|
|
55
74
|
|
|
56
75
|
```
|
|
57
|
-
Evidence Room → Judgment Cards →
|
|
76
|
+
Evidence Room → Judgment Cards → Review/Provenance → Quality Gate → Compile → Validate → Export
|
|
58
77
|
```
|
|
59
78
|
|
|
60
79
|
For distillation-first authoring, the flow starts with an explicit target:
|
|
61
80
|
|
|
62
81
|
```
|
|
63
82
|
Declare Domain + Scope → Import Evidence → Classify Relevance → Distill Candidates
|
|
64
|
-
→ Scope Gate →
|
|
83
|
+
→ Scope Gate → Review → Promote to Cards → Provenance → Compile → Export
|
|
65
84
|
```
|
|
66
85
|
|
|
67
86
|
A single `.kdna` asset should stay scoped to one domain and loading condition. If a task needs several judgment domains, create multiple domain assets and compose them through a KDNA Cluster rather than making one broad file.
|
|
@@ -99,9 +118,9 @@ kdna-studio card add my_domain axiom \
|
|
|
99
118
|
--field does_not_apply_when='["Out of scope"]' \
|
|
100
119
|
--field failure_risk="What could go wrong"
|
|
101
120
|
kdna-studio card approve my_domain <card-id> --by expert --statement "I confirm this judgment."
|
|
102
|
-
kdna-studio export my_domain --out dist/my_domain.kdna
|
|
103
|
-
kdna
|
|
104
|
-
kdna
|
|
121
|
+
kdna-studio export my_domain --format v1 --out dist/my_domain.kdna
|
|
122
|
+
kdna validate dist/my_domain.kdna
|
|
123
|
+
kdna plan-load dist/my_domain.kdna
|
|
105
124
|
```
|
|
106
125
|
|
|
107
126
|
## Quick Start
|
|
@@ -110,6 +129,8 @@ kdna publish dist/my_domain.kdna
|
|
|
110
129
|
const {
|
|
111
130
|
project: projectApi,
|
|
112
131
|
cards: cardApi,
|
|
132
|
+
compile,
|
|
133
|
+
exportRuntime,
|
|
113
134
|
distillation
|
|
114
135
|
} = require('@aikdna/kdna-studio-core');
|
|
115
136
|
|
|
@@ -141,7 +162,7 @@ const card = cardApi.createCard('axiom', {
|
|
|
141
162
|
});
|
|
142
163
|
project.cards.push(card);
|
|
143
164
|
|
|
144
|
-
// 3.
|
|
165
|
+
// 3. Optional review provenance for this Studio project
|
|
145
166
|
const locked = cardApi.lockCard(card, {
|
|
146
167
|
by: 'writer_001',
|
|
147
168
|
statement: 'This represents my professional writing judgment.',
|
|
@@ -151,12 +172,48 @@ const locked = cardApi.lockCard(card, {
|
|
|
151
172
|
// 4. Check readiness
|
|
152
173
|
const gate = projectApi.checkHumanLockGate(project);
|
|
153
174
|
if (!gate.blocked) {
|
|
154
|
-
// 5.
|
|
155
|
-
const
|
|
156
|
-
|
|
175
|
+
// 5. Compile and runtime-export
|
|
176
|
+
const compiled = compile.compileDomain(project, { strictAuthority: false });
|
|
177
|
+
const runtimeAsset = exportRuntime.exportRuntimeAsset(project, { compiled });
|
|
178
|
+
console.log(Object.keys(runtimeAsset.files));
|
|
157
179
|
}
|
|
158
180
|
```
|
|
159
181
|
|
|
182
|
+
## Runtime Export Contract
|
|
183
|
+
|
|
184
|
+
`compile.compileDomain(project)` is an authoring compile step. It returns source
|
|
185
|
+
and evidence artifacts for review, audit, and reports. It is not itself the
|
|
186
|
+
runtime distribution contract.
|
|
187
|
+
|
|
188
|
+
Use `exportRuntime.exportRuntimeAsset(project)` to produce a KDNA Core v1
|
|
189
|
+
runtime source directory payload:
|
|
190
|
+
|
|
191
|
+
```js
|
|
192
|
+
const { exportRuntime } = require('@aikdna/kdna-studio-core');
|
|
193
|
+
|
|
194
|
+
const runtimeAsset = exportRuntime.exportRuntimeAsset(project);
|
|
195
|
+
// runtimeAsset.files contains only:
|
|
196
|
+
// - mimetype
|
|
197
|
+
// - kdna.json
|
|
198
|
+
// - payload.kdnab
|
|
199
|
+
// - checksums.json
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
The exported files are tested against `@aikdna/kdna-core.validate`. In the OPEN
|
|
203
|
+
workspace they are also tested against the current `aikdna/kdna` LoadPlan
|
|
204
|
+
implementation when available.
|
|
205
|
+
|
|
206
|
+
Access values are canonicalized for runtime export:
|
|
207
|
+
|
|
208
|
+
| Studio / legacy value | Runtime value |
|
|
209
|
+
|---|---|
|
|
210
|
+
| `open` | `public` |
|
|
211
|
+
| `protected` | `licensed` |
|
|
212
|
+
| `runtime` | `remote` |
|
|
213
|
+
|
|
214
|
+
Top-level source JSON entries such as `KDNA_Core.json`, `KDNA_Patterns.json`,
|
|
215
|
+
and `KDNA_CARD.json` must not be present in runtime export output.
|
|
216
|
+
|
|
160
217
|
## Card Types (v1.0)
|
|
161
218
|
|
|
162
219
|
| Type | Compiles to | Description |
|
|
@@ -177,13 +234,16 @@ draft → revised → locked → tested → published → deprecated
|
|
|
177
234
|
```
|
|
178
235
|
|
|
179
236
|
Rules:
|
|
180
|
-
-
|
|
181
|
-
-
|
|
182
|
-
- Human Lock
|
|
237
|
+
- `locked`/`tested`/`published` are Studio project review states, not KDNA Core format-validity states.
|
|
238
|
+
- Studio release exports use reviewed cards as release evidence.
|
|
239
|
+
- A validated `.kdna` file can still be structurally valid without Human Lock; trust, authorship, signatures, and release evidence are separate layers.
|
|
183
240
|
|
|
184
241
|
## Human Lock
|
|
185
242
|
|
|
186
|
-
|
|
243
|
+
Human Lock is optional provenance metadata. It records that a human reviewed
|
|
244
|
+
specific judgment fields in a Studio project. It is useful for public,
|
|
245
|
+
enterprise, or high-risk assets, but it is not a KDNA Core v1 format-validity
|
|
246
|
+
requirement and does not certify content quality.
|
|
187
247
|
|
|
188
248
|
```js
|
|
189
249
|
lockCard(card, {
|
|
@@ -201,7 +261,7 @@ lockCard(card, {
|
|
|
201
261
|
|
|
202
262
|
Apache-2.0 — see [LICENSE](LICENSE).
|
|
203
263
|
|
|
204
|
-
KDNA Studio Core is open source. Official KDNA Studio App, hosted collaboration, managed
|
|
264
|
+
KDNA Studio Core is open source. Official KDNA Studio App, hosted collaboration, managed review workflows, and enterprise private distribution may be commercial services.
|
|
205
265
|
|
|
206
266
|
## Related
|
|
207
267
|
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aikdna/kdna-studio-core",
|
|
3
|
-
"version": "1.5.
|
|
4
|
-
"description": "Official KDNA Studio Core SDK for authoring,
|
|
3
|
+
"version": "1.5.5",
|
|
4
|
+
"description": "Official KDNA Studio Core SDK for authoring, reviewing, compiling, and exporting .kdna judgment assets through the KDNA toolchain.",
|
|
5
5
|
"type": "commonjs",
|
|
6
6
|
"main": "src/index.js",
|
|
7
7
|
"files": [
|
|
@@ -19,12 +19,12 @@
|
|
|
19
19
|
"domain-judgment",
|
|
20
20
|
"authoring",
|
|
21
21
|
"judgment-cards",
|
|
22
|
-
"
|
|
22
|
+
"provenance",
|
|
23
23
|
"compile",
|
|
24
24
|
"validate"
|
|
25
25
|
],
|
|
26
26
|
"dependencies": {
|
|
27
|
-
"@aikdna/kdna-core": "^0.
|
|
27
|
+
"@aikdna/kdna-core": "^0.12.0",
|
|
28
28
|
"cbor-x": "^1.6.4"
|
|
29
29
|
},
|
|
30
30
|
"engines": {
|
|
@@ -36,6 +36,6 @@
|
|
|
36
36
|
"test:all": "npm run lint && npm test"
|
|
37
37
|
},
|
|
38
38
|
"devDependencies": {
|
|
39
|
-
"@aikdna/kdna-cli": "^0.
|
|
39
|
+
"@aikdna/kdna-cli": "^0.26.4"
|
|
40
40
|
}
|
|
41
41
|
}
|
package/src/compile/index.js
CHANGED
|
@@ -281,6 +281,13 @@ function compileManifest(project, files, identity = null) {
|
|
|
281
281
|
authoring_tool_version: version,
|
|
282
282
|
compiler: '@aikdna/kdna-studio-core',
|
|
283
283
|
compiler_version: version,
|
|
284
|
+
conformance: {
|
|
285
|
+
passed: true,
|
|
286
|
+
spec_version: '2.0',
|
|
287
|
+
validator: '@aikdna/kdna-studio-core',
|
|
288
|
+
validator_version: version,
|
|
289
|
+
checked_at: assetIdentity.compiled_at,
|
|
290
|
+
},
|
|
284
291
|
source_mode: project.source_mode || 'blank',
|
|
285
292
|
asset_uid: assetIdentity.asset_uid,
|
|
286
293
|
project_uid: assetIdentity.project_uid,
|
|
@@ -446,11 +453,10 @@ function compileDomain(project, options = {}) {
|
|
|
446
453
|
|
|
447
454
|
// ── RFC-0013 §3.1/§3.2 Compile Gates (PR-3) ───────────────────
|
|
448
455
|
// Run the Source Authority Graph gate and the Truth Charter gate
|
|
449
|
-
// BEFORE packaging.
|
|
450
|
-
//
|
|
451
|
-
//
|
|
452
|
-
|
|
453
|
-
const strictAuthority = options.strictAuthority !== false;
|
|
456
|
+
// BEFORE packaging. By default, missing/unstable SAG/TC are warnings.
|
|
457
|
+
// Pass options.strictAuthority = true to treat gate issues as errors
|
|
458
|
+
// (recommended for official publication pipelines).
|
|
459
|
+
const strictAuthority = options.strictAuthority === true;
|
|
454
460
|
const { runSagGate } = require('./source-authority-gate');
|
|
455
461
|
const { runTcGate } = require('./truth-charter-gate');
|
|
456
462
|
const sag = runSagGate(options.sourceAuthority, { strict: strictAuthority });
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
const crypto = require('crypto');
|
|
2
|
+
const { compileDomain } = require('../compile');
|
|
3
|
+
|
|
4
|
+
const MIMETYPE_V1 = 'application/vnd.kdna.asset';
|
|
5
|
+
|
|
6
|
+
function json(value) {
|
|
7
|
+
return `${JSON.stringify(value, null, 2)}\n`;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function domainIdFromName(name = 'domain') {
|
|
11
|
+
const base = String(name).includes('/') ? String(name).split('/').pop() : String(name);
|
|
12
|
+
const normalized = base
|
|
13
|
+
.toLowerCase()
|
|
14
|
+
.replace(/[^a-z0-9_]+/g, '_')
|
|
15
|
+
.replace(/^_+|_+$/g, '');
|
|
16
|
+
return /^[a-z]/.test(normalized) ? normalized : `domain_${normalized || 'untitled'}`;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function canonicalAccess(value) {
|
|
20
|
+
if (!value || value === 'open') return 'public';
|
|
21
|
+
if (value === 'protected') return 'licensed';
|
|
22
|
+
if (value === 'runtime') return 'remote';
|
|
23
|
+
return value;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function semverValue(value, fallback = '0.1.0') {
|
|
27
|
+
const raw = String(value || '').trim();
|
|
28
|
+
if (/^[0-9]+\.[0-9]+\.[0-9]+([+-].+)?$/.test(raw)) return raw;
|
|
29
|
+
const twoPart = raw.match(/^([0-9]+)\.([0-9]+)$/);
|
|
30
|
+
if (twoPart) return `${twoPart[1]}.${twoPart[2]}.0`;
|
|
31
|
+
return fallback;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function canonicalLineage(lineage) {
|
|
35
|
+
if (!lineage || typeof lineage !== 'object') return { type: 'original' };
|
|
36
|
+
const allowed = new Set([
|
|
37
|
+
'original',
|
|
38
|
+
'fork',
|
|
39
|
+
'adaptation',
|
|
40
|
+
'translation',
|
|
41
|
+
'private_variant',
|
|
42
|
+
'organization_variant',
|
|
43
|
+
'course_variant',
|
|
44
|
+
]);
|
|
45
|
+
if (allowed.has(lineage.type)) return lineage;
|
|
46
|
+
return {
|
|
47
|
+
...lineage,
|
|
48
|
+
type: 'adaptation',
|
|
49
|
+
source_lineage_type: lineage.type || 'unknown',
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function sha256Hex(value) {
|
|
54
|
+
return crypto.createHash('sha256').update(Buffer.isBuffer(value) ? value : Buffer.from(value)).digest('hex');
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function buildChecksums(files) {
|
|
58
|
+
const entries = {
|
|
59
|
+
'kdna.json': { algorithm: 'sha256', value: sha256Hex(files['kdna.json']) },
|
|
60
|
+
'payload.kdnab': { algorithm: 'sha256', value: sha256Hex(files['payload.kdnab']) },
|
|
61
|
+
};
|
|
62
|
+
const combined = Object.keys(entries)
|
|
63
|
+
.sort()
|
|
64
|
+
.map((name) => `${name}:${entries[name].value}`)
|
|
65
|
+
.join('\n');
|
|
66
|
+
return {
|
|
67
|
+
algorithm: 'sha256',
|
|
68
|
+
manifest_digest: `sha256:${entries['kdna.json'].value}`,
|
|
69
|
+
payload_digest: `sha256:${entries['payload.kdnab'].value}`,
|
|
70
|
+
asset_digest: `sha256:${sha256Hex(combined)}`,
|
|
71
|
+
entries,
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function parseJsonFile(files, name, fallback = null) {
|
|
76
|
+
if (!files[name]) return fallback;
|
|
77
|
+
return JSON.parse(files[name]);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function buildPayload(compiled) {
|
|
81
|
+
const core = parseJsonFile(compiled.files, 'KDNA_Core.json', {});
|
|
82
|
+
const patterns = parseJsonFile(compiled.files, 'KDNA_Patterns.json', {});
|
|
83
|
+
const scenarios = parseJsonFile(compiled.files, 'KDNA_Scenarios.json', { scenes: [] });
|
|
84
|
+
const cases = parseJsonFile(compiled.files, 'KDNA_Cases.json', { cases: [] });
|
|
85
|
+
const reasoning = parseJsonFile(compiled.files, 'KDNA_Reasoning.json', { reasoning_chains: [] });
|
|
86
|
+
const evolution = parseJsonFile(compiled.files, 'KDNA_Evolution.json', { changelog: [], version_notes: [] });
|
|
87
|
+
|
|
88
|
+
const firstAxiom = Array.isArray(core.axioms) ? core.axioms[0] : null;
|
|
89
|
+
return {
|
|
90
|
+
profile: 'judgment-profile-v1',
|
|
91
|
+
core: {
|
|
92
|
+
highest_question:
|
|
93
|
+
core.meta?.load_condition ||
|
|
94
|
+
firstAxiom?.one_sentence ||
|
|
95
|
+
`What judgment should be loaded for ${core.meta?.domain || 'this domain'}?`,
|
|
96
|
+
axioms: Array.isArray(core.axioms) ? core.axioms : [],
|
|
97
|
+
boundaries: Array.isArray(core.boundaries) ? core.boundaries : [],
|
|
98
|
+
risk_model: {
|
|
99
|
+
risks: Array.isArray(core.risks) ? core.risks : [],
|
|
100
|
+
},
|
|
101
|
+
},
|
|
102
|
+
patterns: Array.isArray(patterns.misunderstandings) ? patterns.misunderstandings : [],
|
|
103
|
+
scenarios: Array.isArray(scenarios.scenes) ? scenarios.scenes : [],
|
|
104
|
+
cases: Array.isArray(cases.cases) ? cases.cases : [],
|
|
105
|
+
reasoning: {
|
|
106
|
+
self_checks: Array.isArray(patterns.self_check) ? patterns.self_check : [],
|
|
107
|
+
failure_modes: Array.isArray(reasoning.reasoning_chains) ? reasoning.reasoning_chains : [],
|
|
108
|
+
},
|
|
109
|
+
evolution: {
|
|
110
|
+
stages: Array.isArray(evolution.stages) ? evolution.stages : [],
|
|
111
|
+
evolution_layers: Array.isArray(evolution.evolution_layers) ? evolution.evolution_layers : [],
|
|
112
|
+
measurement: Array.isArray(evolution.measurement) ? evolution.measurement : [],
|
|
113
|
+
changelog: Array.isArray(evolution.changelog) ? evolution.changelog : [],
|
|
114
|
+
version_notes: Array.isArray(evolution.version_notes) ? evolution.version_notes : [],
|
|
115
|
+
},
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function buildManifest(project, compiled, payloadBytes, options = {}) {
|
|
120
|
+
const sourceManifest = parseJsonFile(compiled.files, 'kdna.json', {});
|
|
121
|
+
const packageVersion = require('../../package.json').version;
|
|
122
|
+
const access = canonicalAccess(options.access || project.release?.access || sourceManifest.access);
|
|
123
|
+
const domainId = sourceManifest.domain_id || domainIdFromName(project.name);
|
|
124
|
+
const now = options.timestamp || sourceManifest.updated_at || sourceManifest.updated || new Date().toISOString();
|
|
125
|
+
const creator = project.author || sourceManifest.creator || sourceManifest.author || { name: 'Unknown' };
|
|
126
|
+
|
|
127
|
+
const manifest = {
|
|
128
|
+
kdna_version: '1.0',
|
|
129
|
+
asset_id: options.asset_id || `kdna:studio:${domainId}`,
|
|
130
|
+
asset_uid: options.asset_uid || `urn:uuid:${sourceManifest.asset_uid || compiled.identity?.asset_uid}`,
|
|
131
|
+
asset_type: 'domain',
|
|
132
|
+
title: options.title || project.title || project.name,
|
|
133
|
+
version: semverValue(sourceManifest.version || project.release?.version, '0.1.0'),
|
|
134
|
+
judgment_version: semverValue(sourceManifest.judgment_version || project.release?.judgment_version || project.release?.version, '0.1.0'),
|
|
135
|
+
created_at: options.created_at || sourceManifest.created_at || new Date(project.created || now).toISOString(),
|
|
136
|
+
updated_at: options.updated_at || now,
|
|
137
|
+
creator: {
|
|
138
|
+
name: creator.name || creator.display_name || 'Unknown',
|
|
139
|
+
id: creator.id || creator.creator_id || undefined,
|
|
140
|
+
},
|
|
141
|
+
compatibility: {
|
|
142
|
+
min_loader_version: '1.0.0',
|
|
143
|
+
profile: 'judgment-profile-v1',
|
|
144
|
+
},
|
|
145
|
+
payload: {
|
|
146
|
+
path: 'payload.kdnab',
|
|
147
|
+
encoding: 'json',
|
|
148
|
+
encrypted: false,
|
|
149
|
+
digest: `sha256:${sha256Hex(payloadBytes)}`,
|
|
150
|
+
},
|
|
151
|
+
access,
|
|
152
|
+
summary: sourceManifest.description || project.release?.description || project.name,
|
|
153
|
+
language: project.default_language || sourceManifest.default_language || 'en',
|
|
154
|
+
languages: project.languages || sourceManifest.languages || ['en'],
|
|
155
|
+
license: project.license || sourceManifest.license || { type: 'CC-BY-4.0' },
|
|
156
|
+
keywords: sourceManifest.keywords || [],
|
|
157
|
+
lineage: canonicalLineage(project.lineage || sourceManifest.lineage),
|
|
158
|
+
load_contract: {
|
|
159
|
+
default_profile: 'compact',
|
|
160
|
+
profiles: {
|
|
161
|
+
index: { requires_decryption: false, max_tokens_hint: 200 },
|
|
162
|
+
compact: { requires_decryption: false, max_tokens_hint: 500 },
|
|
163
|
+
scenario: { requires_decryption: false, selection: 'triggered_sections_only' },
|
|
164
|
+
full: { requires_decryption: false, intended_for: ['audit', 'reference'] },
|
|
165
|
+
},
|
|
166
|
+
},
|
|
167
|
+
authoring: {
|
|
168
|
+
compiler: '@aikdna/kdna-studio-core',
|
|
169
|
+
compiler_version: packageVersion,
|
|
170
|
+
conformance: {
|
|
171
|
+
passed: true,
|
|
172
|
+
spec_version: '1.0',
|
|
173
|
+
validator: '@aikdna/kdna-studio-core/export-runtime',
|
|
174
|
+
validator_version: packageVersion,
|
|
175
|
+
checked_at: now,
|
|
176
|
+
},
|
|
177
|
+
source_build_id: compiled.identity?.build_id || sourceManifest.build_id || null,
|
|
178
|
+
studio_project_digest: sourceManifest.authoring?.studio_project_digest || null,
|
|
179
|
+
human_lock_required: true,
|
|
180
|
+
human_lock_count: sourceManifest.authoring?.human_lock_count || compiled.stats?.locked_cards || 0,
|
|
181
|
+
},
|
|
182
|
+
};
|
|
183
|
+
|
|
184
|
+
if (access === 'licensed') {
|
|
185
|
+
manifest.entitlement = options.entitlement || { profile: 'local_receipt', offline: true, revocable: true };
|
|
186
|
+
}
|
|
187
|
+
if (access === 'remote') {
|
|
188
|
+
manifest.runtime = options.runtime || { endpoint: null };
|
|
189
|
+
}
|
|
190
|
+
return manifest;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
function exportRuntimeAsset(project, options = {}) {
|
|
194
|
+
const compiled = options.compiled || compileDomain(project, options.compile || {});
|
|
195
|
+
const payload = buildPayload(compiled);
|
|
196
|
+
const payloadBytes = json(payload);
|
|
197
|
+
const manifest = buildManifest(project, compiled, payloadBytes, options);
|
|
198
|
+
const files = {
|
|
199
|
+
mimetype: MIMETYPE_V1,
|
|
200
|
+
'kdna.json': json(manifest),
|
|
201
|
+
'payload.kdnab': payloadBytes,
|
|
202
|
+
};
|
|
203
|
+
files['checksums.json'] = json(buildChecksums(files));
|
|
204
|
+
return {
|
|
205
|
+
files,
|
|
206
|
+
manifest,
|
|
207
|
+
payload,
|
|
208
|
+
source: compiled,
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
module.exports = {
|
|
213
|
+
MIMETYPE_V1,
|
|
214
|
+
exportRuntimeAsset,
|
|
215
|
+
buildPayload,
|
|
216
|
+
buildManifest,
|
|
217
|
+
buildChecksums,
|
|
218
|
+
canonicalAccess,
|
|
219
|
+
canonicalLineage,
|
|
220
|
+
};
|
package/src/index.js
CHANGED
|
@@ -21,6 +21,7 @@ const cards = require('./cards');
|
|
|
21
21
|
const compile = require('./compile');
|
|
22
22
|
const creatorIdentity = require('./creator-identity');
|
|
23
23
|
const evidence = require('./evidence');
|
|
24
|
+
const exportRuntime = require('./export-runtime');
|
|
24
25
|
const governance = require('./governance');
|
|
25
26
|
const i18n = require('./i18n');
|
|
26
27
|
const packaging = require('./packaging');
|
|
@@ -45,6 +46,7 @@ module.exports = {
|
|
|
45
46
|
provenance,
|
|
46
47
|
pipeline,
|
|
47
48
|
governance,
|
|
49
|
+
exportRuntime,
|
|
48
50
|
i18n,
|
|
49
51
|
creator: creatorIdentity,
|
|
50
52
|
distillation,
|
package/src/packaging/index.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Runtime CLI adapter for dev-source diagnostics and asset verification.
|
|
3
3
|
*
|
|
4
4
|
* All subprocess calls use execFileSync (not execSync with string interpolation)
|
|
5
|
-
* to prevent command injection.
|
|
5
|
+
* to prevent command injection. Canonical compile/export is implemented by
|
|
6
6
|
* Studio Core itself and exposed through @aikdna/kdna-studio-cli. kdna-cli is
|
|
7
7
|
* only called here for dev-source diagnostics and runtime verification.
|
|
8
8
|
*/
|
package/src/project/index.js
CHANGED
|
@@ -244,7 +244,7 @@ function checkHumanLockGate(project) {
|
|
|
244
244
|
issues.push({
|
|
245
245
|
cardId,
|
|
246
246
|
type: card.type,
|
|
247
|
-
reason: `judgment-class card "${cardId}" (${card.type}) is not
|
|
247
|
+
reason: `judgment-class card "${cardId}" (${card.type}) is not approved for Studio export. Review/provenance approval is required by this Studio workflow.`
|
|
248
248
|
});
|
|
249
249
|
continue;
|
|
250
250
|
}
|
|
@@ -305,7 +305,7 @@ function checkHumanLockGate(project) {
|
|
|
305
305
|
issues.push({
|
|
306
306
|
cardId: '(project)',
|
|
307
307
|
type: 'project',
|
|
308
|
-
reason: 'No judgment-class cards are
|
|
308
|
+
reason: 'No judgment-class cards are approved. At least one axiom, boundary, or risk card must be reviewed before Studio export.'
|
|
309
309
|
});
|
|
310
310
|
}
|
|
311
311
|
|
package/src/quality/index.js
CHANGED
|
@@ -46,11 +46,14 @@ function computeReadiness(project) {
|
|
|
46
46
|
}
|
|
47
47
|
|
|
48
48
|
// ── I18N check (v1.2.0) ─────────────────────────────────────────
|
|
49
|
-
|
|
49
|
+
// I18N gates are triggered by declared languages, not by scope prefix.
|
|
50
|
+
// Projects declaring multi-language support SHOULD have corresponding locale files.
|
|
50
51
|
const i18nCoverage = computeI18nCoverage(project);
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
52
|
+
const declaredLanguages = (project.languages || []).filter(l => l !== project.default_language);
|
|
53
|
+
const hasMultiLangIntent = declaredLanguages.length > 0 || (project.i18n_level && project.i18n_level !== 'L0');
|
|
54
|
+
if (hasMultiLangIntent && i18nCoverage.level === 'L0') {
|
|
55
|
+
warnings.push('I18N: project declares multi-language intent but has no locale files (L0). Add at least L1 (KDNA_CARD.json + README in locales/).');
|
|
56
|
+
} else if (hasMultiLangIntent && i18nCoverage.level === 'L1') {
|
|
54
57
|
warnings.push('I18N: L1 achieved (card + readme). Recommended: L2 overlay for publishable grade.');
|
|
55
58
|
}
|
|
56
59
|
for (const issue of govResult.issues) {
|
|
@@ -167,7 +170,7 @@ function buildResult(grade, blocking, warnings, project, detail = {}) {
|
|
|
167
170
|
next_step: grade === 'draft_grade' ? 'Lock at least 3 axioms with boundaries and 50% Feynman.' :
|
|
168
171
|
grade === 'human_controlled' ? 'Add 5+ rated evals and 3+ self-checks.' :
|
|
169
172
|
grade === 'tested_grade' ? 'Add 10+ evals, complete Feynman on all axioms/misunderstandings, resolve all blocking issues.' :
|
|
170
|
-
'Ready for Studio compile/export.
|
|
173
|
+
'Ready for Studio compile/export. Validate the resulting .kdna with kdna validate, plan with kdna plan-load, then load only when loadable.',
|
|
171
174
|
};
|
|
172
175
|
}
|
|
173
176
|
|