@aikdna/kdna-core 0.1.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.
@@ -0,0 +1,262 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://aikdna.com/schema/kdna-file.schema.json",
4
+ "title": "KDNA Single-File Format",
5
+ "description": "Schema for the .kdna single-file distribution format. Combines all KDNA domain files into one transportable file.",
6
+ "type": "object",
7
+ "required": ["kdna_spec", "meta", "core", "patterns"],
8
+ "properties": {
9
+ "kdna_spec": {
10
+ "type": "string",
11
+ "description": "KDNA spec version this file conforms to (e.g., '0.4')."
12
+ },
13
+ "meta": {
14
+ "type": "object",
15
+ "description": "Domain metadata — equivalent to kdna.json fields.",
16
+ "required": ["name", "domain", "version"],
17
+ "properties": {
18
+ "name": { "type": "string", "description": "Human-readable domain name." },
19
+ "domain": { "type": "string", "pattern": "^[a-z][a-z0-9_]*$", "description": "Domain identifier (snake_case)." },
20
+ "version": { "type": "string", "description": "Semantic version (MAJOR.MINOR.PATCH)." },
21
+ "spec_version": { "type": "string", "description": "KDNA spec version this domain conforms to." },
22
+ "created": { "type": "string", "description": "ISO date of first creation." },
23
+ "updated": { "type": "string", "description": "ISO date of last update." },
24
+ "purpose": { "type": "string", "description": "What judgment this domain improves." },
25
+ "description": { "type": "string" },
26
+ "language": { "type": "string" },
27
+ "languages": { "type": "array", "items": { "type": "string" } }
28
+ },
29
+ "additionalProperties": true
30
+ },
31
+ "author": {
32
+ "type": "object",
33
+ "description": "Domain author information.",
34
+ "properties": {
35
+ "name": { "type": "string" },
36
+ "id": { "type": "string" },
37
+ "email": { "type": "string", "format": "email" }
38
+ }
39
+ },
40
+ "license": {
41
+ "type": "object",
42
+ "description": "License information.",
43
+ "properties": {
44
+ "type": { "type": "string" },
45
+ "url": { "type": "string", "format": "uri" }
46
+ }
47
+ },
48
+ "status": {
49
+ "type": "string",
50
+ "enum": ["experimental", "basic", "stable", "pro"],
51
+ "description": "Domain maturity level."
52
+ },
53
+ "access": {
54
+ "type": "string",
55
+ "enum": ["open", "licensed", "runtime"],
56
+ "description": "Access mode for this domain."
57
+ },
58
+ "keywords": {
59
+ "type": "array",
60
+ "items": { "type": "string" },
61
+ "description": "Discovery keywords."
62
+ },
63
+ "core": {
64
+ "type": "object",
65
+ "description": "Core cognition — equivalent to KDNA_Core.json.",
66
+ "required": ["axioms", "ontology", "frameworks", "core_structure", "stances"],
67
+ "properties": {
68
+ "axioms": {
69
+ "type": "array",
70
+ "minItems": 1,
71
+ "items": {
72
+ "type": "object",
73
+ "required": ["id", "one_sentence", "full_statement", "why"],
74
+ "properties": {
75
+ "id": { "type": "string", "description": "Unique identifier (e.g., 'AX-001')." },
76
+ "one_sentence": { "type": "string", "minLength": 10, "description": "One-sentence judgment principle." },
77
+ "full_statement": { "type": "string", "minLength": 20, "description": "Full explanation — testable and domain-specific." },
78
+ "why": { "type": "string", "minLength": 10, "description": "Why this axiom matters for agent judgment." }
79
+ }
80
+ }
81
+ },
82
+ "ontology": {
83
+ "type": "array",
84
+ "minItems": 1,
85
+ "items": {
86
+ "type": "object",
87
+ "required": ["id", "one_sentence", "essence", "boundary", "trigger_signal"],
88
+ "properties": {
89
+ "id": { "type": "string" },
90
+ "one_sentence": { "type": "string" },
91
+ "essence": { "type": "string", "minLength": 10, "description": "Operational meaning — what the agent needs to check." },
92
+ "boundary": { "type": "string", "minLength": 5, "description": "What this concept is NOT. Must name a specific concept, not just 'not X'." },
93
+ "trigger_signal": { "type": "string", "minLength": 5, "description": "Observable words or patterns that signal this concept is relevant." }
94
+ }
95
+ }
96
+ },
97
+ "frameworks": {
98
+ "type": "array",
99
+ "items": {
100
+ "type": "object",
101
+ "required": ["id", "name", "when_to_use", "steps"],
102
+ "properties": {
103
+ "id": { "type": "string" },
104
+ "name": { "type": "string" },
105
+ "when_to_use": { "type": "string" },
106
+ "steps": { "type": "array", "minItems": 1, "items": { "type": "string" } }
107
+ }
108
+ }
109
+ },
110
+ "core_structure": {
111
+ "type": "array",
112
+ "minItems": 1,
113
+ "items": {
114
+ "type": "object",
115
+ "required": ["from", "to", "via"],
116
+ "properties": {
117
+ "from": { "type": "string" },
118
+ "to": { "type": "string" },
119
+ "via": { "type": "string" }
120
+ }
121
+ }
122
+ },
123
+ "stances": {
124
+ "type": "array",
125
+ "minItems": 1,
126
+ "items": {
127
+ "anyOf": [
128
+ { "type": "string" },
129
+ { "type": "object", "required": ["stance"] }
130
+ ]
131
+ }
132
+ }
133
+ }
134
+ },
135
+ "patterns": {
136
+ "type": "object",
137
+ "description": "Patterns — equivalent to KDNA_Patterns.json.",
138
+ "required": ["terminology", "misunderstandings", "self_check"],
139
+ "properties": {
140
+ "terminology": {
141
+ "type": "object",
142
+ "minProperties": 1,
143
+ "properties": {
144
+ "standard_terms": {
145
+ "type": "array",
146
+ "items": {
147
+ "type": "object",
148
+ "required": ["term", "definition"],
149
+ "properties": {
150
+ "term": { "type": "string" },
151
+ "definition": { "type": "string" }
152
+ }
153
+ }
154
+ },
155
+ "preferred_terms": {
156
+ "type": "array",
157
+ "items": { "type": "object" }
158
+ },
159
+ "banned_terms": {
160
+ "type": "array",
161
+ "items": {
162
+ "type": "object",
163
+ "required": ["term", "why", "replace_with"],
164
+ "properties": {
165
+ "term": { "type": "string" },
166
+ "why": { "type": "string" },
167
+ "replace_with": { "type": "string" }
168
+ }
169
+ }
170
+ }
171
+ }
172
+ },
173
+ "misunderstandings": {
174
+ "type": "array",
175
+ "minItems": 1,
176
+ "items": {
177
+ "type": "object",
178
+ "required": ["id", "wrong", "correct", "key_distinction", "why"],
179
+ "properties": {
180
+ "id": { "type": "string" },
181
+ "wrong": { "type": "string", "minLength": 10 },
182
+ "correct": { "type": "string", "minLength": 10 },
183
+ "key_distinction": { "type": "string", "minLength": 10 },
184
+ "why": { "type": "string", "minLength": 10 }
185
+ }
186
+ }
187
+ },
188
+ "self_check": {
189
+ "type": "array",
190
+ "minItems": 1,
191
+ "items": {
192
+ "anyOf": [
193
+ { "type": "string" },
194
+ { "type": "object", "required": ["question"] }
195
+ ]
196
+ }
197
+ }
198
+ }
199
+ },
200
+ "scenarios": {
201
+ "type": "object",
202
+ "description": "Optional scenarios (KDNA_Scenarios.json).",
203
+ "properties": {
204
+ "scenes": {
205
+ "type": "array",
206
+ "items": { "type": "object" }
207
+ }
208
+ }
209
+ },
210
+ "cases": {
211
+ "type": "object",
212
+ "description": "Optional cases (KDNA_Cases.json).",
213
+ "properties": {
214
+ "cases": {
215
+ "type": "array",
216
+ "items": { "type": "object" }
217
+ }
218
+ }
219
+ },
220
+ "reasoning": {
221
+ "type": "object",
222
+ "description": "Optional reasoning chains (KDNA_Reasoning.json).",
223
+ "properties": {
224
+ "reasoning_chains": {
225
+ "type": "array",
226
+ "items": { "type": "object" }
227
+ }
228
+ }
229
+ },
230
+ "evolution": {
231
+ "type": "object",
232
+ "description": "Optional evolution stages (KDNA_Evolution.json).",
233
+ "properties": {
234
+ "stages": {
235
+ "type": "array",
236
+ "items": { "type": "object" }
237
+ }
238
+ }
239
+ },
240
+ "registry": {
241
+ "type": "object",
242
+ "description": "Optional registry metadata.",
243
+ "properties": {
244
+ "repo": { "type": "string" },
245
+ "eval_score": { "type": "number" },
246
+ "quality_badge": { "type": "string" }
247
+ }
248
+ },
249
+ "evaluation_history": {
250
+ "type": "array",
251
+ "description": "Benchmark evaluation history.",
252
+ "items": {
253
+ "type": "object",
254
+ "properties": {
255
+ "version": { "type": "string" },
256
+ "eval_score": { "type": "number" },
257
+ "evaluated_at": { "type": "string" }
258
+ }
259
+ }
260
+ }
261
+ }
262
+ }
package/src/index.js ADDED
@@ -0,0 +1,14 @@
1
+ /**
2
+ * @aikdna/kdna-core — CJS entry point
3
+ */
4
+ const loader = require('./loader');
5
+ const lint = require('./lint-pure');
6
+ const validate = require('./validate-pure');
7
+ const render = require('./render');
8
+
9
+ module.exports = {
10
+ ...loader,
11
+ ...lint,
12
+ ...validate,
13
+ ...render,
14
+ };
package/src/index.mjs ADDED
@@ -0,0 +1,17 @@
1
+ /**
2
+ * @aikdna/kdna-core — ESM entry point
3
+ */
4
+ export {
5
+ FILE_MAP,
6
+ loadCorePatternsFromData,
7
+ loadDomainFromData,
8
+ loadDomainFromFiles,
9
+ classifyInput,
10
+ formatContext,
11
+ } from './loader.js';
12
+
13
+ export { lintDomain } from './lint-pure.js';
14
+
15
+ export { validateDomainSchema, validateCrossFile } from './validate-pure.js';
16
+
17
+ export { renderPreviewHTML, escHtml, renderCard } from './render.js';
@@ -0,0 +1,197 @@
1
+ /**
2
+ * KDNA Lint — Pure structural and content validation.
3
+ *
4
+ * Operates on in-memory data maps. No fs, no path, no Node.js dependencies.
5
+ * Input: { 'KDNA_Core.json': parsedObj, 'KDNA_Patterns.json': parsedObj, ... }
6
+ * Output: { errors: string[], warnings: string[] }
7
+ */
8
+
9
+ /**
10
+ * Lint a KDNA domain from a map of parsed JSON objects.
11
+ *
12
+ * @param {Object} dataMap — keyed by filename, e.g. { 'KDNA_Core.json': {...}, ... }
13
+ * @returns {{ errors: string[], warnings: string[] }}
14
+ */
15
+ function lintDomain(dataMap) {
16
+ const errors = [];
17
+ const warnings = [];
18
+
19
+ function has(o, k) {
20
+ return Object.prototype.hasOwnProperty.call(o || {}, k);
21
+ }
22
+
23
+ function req(o, k, loc, hint) {
24
+ if (!has(o, k) || o[k] === '' || o[k] == null) {
25
+ let msg = `${loc}: missing required field "${k}"`;
26
+ if (hint) msg += `\n → ${hint}`;
27
+ errors.push(msg);
28
+ }
29
+ }
30
+
31
+ function meta(o, file) {
32
+ req(o, 'meta', file);
33
+ if (!o.meta) return;
34
+ ['version', 'domain', 'created', 'purpose', 'load_condition'].forEach((f) =>
35
+ req(o.meta, f, `${file}.meta`),
36
+ );
37
+ }
38
+
39
+ function ids(v, file, set) {
40
+ if (Array.isArray(v)) return v.forEach((x) => ids(x, file, set));
41
+ if (v && typeof v === 'object') {
42
+ if (typeof v.id === 'string') {
43
+ if (set.has(v.id)) errors.push(`${file}: duplicate id "${v.id}"`);
44
+ set.add(v.id);
45
+ }
46
+ Object.values(v).forEach((x) => ids(x, file, set));
47
+ }
48
+ }
49
+
50
+ function yesno(s, loc) {
51
+ const t = String(s || '')
52
+ .trim()
53
+ .toLowerCase();
54
+ const cn = String(s || '').trim();
55
+ if (
56
+ t.endsWith('?') ||
57
+ cn.endsWith('?') ||
58
+ cn.endsWith('吗') ||
59
+ cn.includes('是否') ||
60
+ /^(have|has|can|does|do|is|are|能不能|会不会|有没有|要不要|是不是)/.test(t)
61
+ )
62
+ return;
63
+ warnings.push(
64
+ `${loc}: self_check should be answerable with yes/no\n → Try: "Did the response [do X specific domain check]?"`,
65
+ );
66
+ }
67
+
68
+ // Check required files
69
+ const requiredFiles = ['KDNA_Core.json', 'KDNA_Patterns.json'];
70
+ for (const f of requiredFiles) {
71
+ if (!dataMap[f]) errors.push(`Missing required file: ${f}`);
72
+ }
73
+
74
+ // Check file count
75
+ const kdnaFiles = Object.keys(dataMap).filter(
76
+ (f) => f.endsWith('.json') && f !== 'kdna.json',
77
+ );
78
+ if (kdnaFiles.length > 6) errors.push(`Domain has ${kdnaFiles.length} JSON files; KDNA allows at most 6.`);
79
+
80
+ // Validate meta on all files
81
+ for (const f of kdnaFiles) {
82
+ if (dataMap[f]) meta(dataMap[f], f);
83
+ }
84
+
85
+ // Check duplicate IDs
86
+ const seen = new Set();
87
+ for (const f of kdnaFiles) {
88
+ if (dataMap[f]) ids(dataMap[f], f, seen);
89
+ }
90
+
91
+ // Validate KDNA_Core.json
92
+ const core = dataMap['KDNA_Core.json'];
93
+ if (core) {
94
+ ['axioms', 'ontology', 'frameworks', 'core_structure', 'stances'].forEach((f) =>
95
+ req(core, f, 'KDNA_Core.json'),
96
+ );
97
+ (core.axioms || []).forEach((a, i) =>
98
+ [
99
+ ['id', 'Unique identifier like "AX-001". See SPEC.md §5.2'],
100
+ [
101
+ 'one_sentence',
102
+ 'One-sentence judgment principle. Must be specific enough to change agent behavior. See docs/authoring-guide.md',
103
+ ],
104
+ [
105
+ 'full_statement',
106
+ 'Full explanation of the axiom — testable and domain-specific. See SPEC.md §5.2',
107
+ ],
108
+ ['why', 'What the agent would get wrong WITHOUT this axiom. See SPEC.md §5.2'],
109
+ ].forEach(([f, hint]) => req(a, f, `KDNA_Core.json.axioms[${i}]`, hint)),
110
+ );
111
+ (core.ontology || []).forEach((c, i) =>
112
+ [
113
+ ['id', 'Unique identifier like "CON-001". See SPEC.md §5.3'],
114
+ ['one_sentence', 'Name one central concept the agent must distinguish.'],
115
+ [
116
+ 'essence',
117
+ 'Operational meaning in this domain — not a dictionary definition. See docs/authoring-guide.md',
118
+ ],
119
+ [
120
+ 'boundary',
121
+ 'What this concept is NOT. Name a specific concept it is often confused with. See docs/authoring-guide.md',
122
+ ],
123
+ [
124
+ 'trigger_signal',
125
+ 'Observable words or patterns that signal this concept is relevant. See SPEC.md §5.3',
126
+ ],
127
+ ].forEach(([f, hint]) => req(c, f, `KDNA_Core.json.ontology[${i}]`, hint)),
128
+ );
129
+ (core.frameworks || []).forEach((fw, i) =>
130
+ [
131
+ ['id', 'Unique identifier like "FW-001". See SPEC.md §5.4'],
132
+ ['name', 'Descriptive name for this framework.'],
133
+ ['when_to_use', 'Specific condition or context where this framework applies.'],
134
+ [
135
+ 'steps',
136
+ 'Array of actionable steps. Each step should tell the agent what to do. See SPEC.md §5.4',
137
+ ],
138
+ ].forEach(([f, hint]) => req(fw, f, `KDNA_Core.json.frameworks[${i}]`, hint)),
139
+ );
140
+ }
141
+
142
+ // Validate KDNA_Patterns.json
143
+ const pat = dataMap['KDNA_Patterns.json'];
144
+ if (pat) {
145
+ ['terminology', 'misunderstandings', 'self_check'].forEach((f) =>
146
+ req(pat, f, 'KDNA_Patterns.json'),
147
+ );
148
+ ((pat.terminology || {}).banned_terms || []).forEach((b, i) =>
149
+ ['term', 'why', 'replace_with'].forEach((f) =>
150
+ req(b, f, `KDNA_Patterns.json.terminology.banned_terms[${i}]`),
151
+ ),
152
+ );
153
+ (pat.misunderstandings || []).forEach((m, i) =>
154
+ [
155
+ ['id', 'Unique identifier like "MS-001". See SPEC.md §6.3'],
156
+ [
157
+ 'wrong',
158
+ 'Common wrong interpretation an agent without domain cognition would make. See docs/authoring-guide.md',
159
+ ],
160
+ ['correct', 'Correct interpretation according to domain principles. See SPEC.md §6.3'],
161
+ [
162
+ 'key_distinction',
163
+ 'The specific conceptual boundary the agent must preserve. See SPEC.md §6.3',
164
+ ],
165
+ ['why', 'What bad judgment results from the wrong interpretation. See SPEC.md §6.3'],
166
+ ].forEach(([f, hint]) => req(m, f, `KDNA_Patterns.json.misunderstandings[${i}]`, hint)),
167
+ );
168
+ (pat.self_check || []).forEach((s, i) => yesno(s, `KDNA_Patterns.json.self_check[${i}]`));
169
+ }
170
+
171
+ // Validate KDNA_Reasoning.json
172
+ const rea = dataMap['KDNA_Reasoning.json'];
173
+ if (rea) {
174
+ (rea.reasoning_chains || []).forEach((r, i) =>
175
+ ['id', 'one_sentence', 'logic', 'so_what'].forEach((f) =>
176
+ req(r, f, `KDNA_Reasoning.json.reasoning_chains[${i}]`),
177
+ ),
178
+ );
179
+ }
180
+
181
+ // Cross-reference: scene_id in cases
182
+ const scen = dataMap['KDNA_Scenarios.json'];
183
+ const sceneIds = new Set();
184
+ if (scen) (scen.scenes || []).forEach((s) => sceneIds.add(s.id));
185
+ const cases = dataMap['KDNA_Cases.json'];
186
+ if (cases && scen)
187
+ (cases.cases || []).forEach((c, i) => {
188
+ if (c.scene_id && !sceneIds.has(c.scene_id))
189
+ errors.push(
190
+ `KDNA_Cases.json.cases[${i}]: scene_id "${c.scene_id}" not found in KDNA_Scenarios.json`,
191
+ );
192
+ });
193
+
194
+ return { errors, warnings };
195
+ }
196
+
197
+ module.exports = { lintDomain };