structured-context 0.11.0 → 0.13.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/README.md +52 -198
- package/dist/api.d.ts +17 -15
- package/dist/api.js +12 -9
- package/dist/commands/diagram.d.ts +1 -0
- package/dist/commands/diagram.js +7 -1
- package/dist/commands/docs.js +1 -0
- package/dist/commands/dump.d.ts +15 -2
- package/dist/commands/dump.js +18 -4
- package/dist/commands/providers.d.ts +4 -0
- package/dist/commands/providers.js +52 -0
- package/dist/commands/render.d.ts +4 -0
- package/dist/commands/render.js +64 -9
- package/dist/commands/schemas.d.ts +2 -2
- package/dist/commands/schemas.js +133 -22
- package/dist/commands/show.d.ts +1 -0
- package/dist/commands/show.js +7 -1
- package/dist/commands/spaces.d.ts +3 -1
- package/dist/commands/spaces.js +11 -2
- package/dist/commands/validate-file.d.ts +3 -0
- package/dist/commands/validate-file.js +24 -6
- package/dist/commands/validate.d.ts +4 -0
- package/dist/commands/validate.js +79 -23
- package/dist/config.d.ts +25 -1
- package/dist/config.js +106 -12
- package/dist/filter/augment-nodes.d.ts +3 -1
- package/dist/filter/augment-nodes.js +12 -7
- package/dist/filter/filter-nodes.js +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +96 -31
- package/dist/judge/context.d.ts +20 -0
- package/dist/judge/context.js +105 -0
- package/dist/judge/factory.d.ts +44 -0
- package/dist/judge/factory.js +148 -0
- package/dist/judge/index.d.ts +13 -0
- package/dist/judge/index.js +8 -0
- package/dist/judge/judge-node.d.ts +27 -0
- package/dist/judge/judge-node.js +122 -0
- package/dist/judge/prompt.d.ts +10 -0
- package/dist/judge/prompt.js +92 -0
- package/dist/judge/provider/anthropic.d.ts +17 -0
- package/dist/judge/provider/anthropic.js +43 -0
- package/dist/judge/provider/fake.d.ts +26 -0
- package/dist/judge/provider/fake.js +56 -0
- package/dist/judge/provider/jev.d.ts +37 -0
- package/dist/judge/provider/jev.js +169 -0
- package/dist/judge/provider/openai-decisions.d.ts +21 -0
- package/dist/judge/provider/openai-decisions.js +84 -0
- package/dist/judge/provider/openai.d.ts +20 -0
- package/dist/judge/provider/openai.js +53 -0
- package/dist/judge/provider/per-rule.d.ts +10 -0
- package/dist/judge/provider/per-rule.js +18 -0
- package/dist/judge/trim.d.ts +27 -0
- package/dist/judge/trim.js +62 -0
- package/dist/judge/types.d.ts +92 -0
- package/dist/judge/types.js +9 -0
- package/dist/judge/verdicts.d.ts +24 -0
- package/dist/judge/verdicts.js +82 -0
- package/dist/load-space-graph.d.ts +7 -5
- package/dist/load-space-graph.js +19 -8
- package/dist/plugins/loader.js +3 -3
- package/dist/plugins/markdown/extract-content-links.js +8 -29
- package/dist/plugins/markdown/index.js +8 -2
- package/dist/plugins/markdown/parse-embedded.d.ts +8 -0
- package/dist/plugins/markdown/parse-embedded.js +195 -24
- package/dist/plugins/markdown/read-space.js +42 -14
- package/dist/plugins/markdown/render-directory.d.ts +11 -0
- package/dist/plugins/markdown/render-directory.js +130 -0
- package/dist/plugins/markdown/template-sync.js +3 -4
- package/dist/plugins/markdown/util.d.ts +26 -5
- package/dist/plugins/markdown/util.js +56 -15
- package/dist/plugins/mermaid/render-mermaid.js +2 -1
- package/dist/plugins/miro/sync.js +7 -6
- package/dist/plugins/util.d.ts +39 -1
- package/dist/plugins/util.js +139 -0
- package/dist/read/resolve-graph-edges.d.ts +1 -1
- package/dist/read/resolve-graph-edges.js +46 -27
- package/dist/read/wikilink-utils.d.ts +17 -0
- package/dist/read/wikilink-utils.js +40 -0
- package/dist/render/render.d.ts +3 -1
- package/dist/render/render.js +2 -2
- package/dist/schema/evaluate-rule.d.ts +33 -2
- package/dist/schema/evaluate-rule.js +58 -4
- package/dist/schema/gating.d.ts +69 -0
- package/dist/schema/gating.js +253 -0
- package/dist/schema/meta-schemas.d.ts +13 -0
- package/dist/schema/meta-schemas.js +50 -0
- package/dist/schema/metadata-contract.d.ts +952 -183
- package/dist/schema/metadata-contract.js +177 -33
- package/dist/schema/rule-filter.d.ts +12 -0
- package/dist/schema/rule-filter.js +46 -0
- package/dist/schema/rule-severity.d.ts +13 -0
- package/dist/schema/rule-severity.js +33 -0
- package/dist/schema/schema.d.ts +14 -8
- package/dist/schema/schema.js +308 -199
- package/dist/schema/validate-graph.d.ts +2 -1
- package/dist/schema/validate-graph.js +40 -23
- package/dist/schema/validate-rules.d.ts +16 -3
- package/dist/schema/validate-rules.js +91 -29
- package/dist/schemas/_knowledge_wiki.json +24 -24
- package/dist/schemas/_okf.json +172 -0
- package/dist/schemas/_ost_strict.json +17 -8
- package/dist/schemas/_sctx_base.json +3 -3
- package/dist/schemas/_strategy_general.json +38 -38
- package/dist/schemas/generated/0.12.0/_structured_context_schema_meta.json +229 -0
- package/dist/schemas/generated/0.13.0/_structured_context_schema_meta.json +400 -0
- package/dist/schemas/generated/latest/_structured_context_schema_meta.json +400 -0
- package/dist/schemas/knowledge_wiki.json +10 -9
- package/dist/schemas/okf.json +8 -0
- package/dist/schemas/strategy_general.json +15 -14
- package/dist/schemas/strict_ost.json +17 -16
- package/dist/space-context.d.ts +5 -2
- package/dist/space-context.js +9 -4
- package/dist/space-graph.d.ts +9 -7
- package/dist/space-graph.js +7 -5
- package/dist/testing.d.ts +33 -0
- package/dist/testing.js +72 -0
- package/dist/types.d.ts +64 -16
- package/dist/validate.d.ts +62 -9
- package/dist/validate.js +229 -114
- package/docs/concepts.md +65 -9
- package/docs/config.md +78 -1
- package/docs/rules.md +168 -50
- package/docs/schemas.md +152 -52
- package/package.json +17 -11
- package/schemas/_knowledge_wiki.json +24 -24
- package/schemas/_okf.json +172 -0
- package/schemas/_ost_strict.json +17 -8
- package/schemas/_sctx_base.json +3 -3
- package/schemas/_strategy_general.json +38 -38
- package/schemas/generated/0.12.0/_structured_context_schema_meta.json +229 -0
- package/schemas/generated/0.13.0/_structured_context_schema_meta.json +400 -0
- package/schemas/generated/latest/_structured_context_schema_meta.json +400 -0
- package/schemas/knowledge_wiki.json +10 -9
- package/schemas/okf.json +8 -0
- package/schemas/strategy_general.json +15 -14
- package/schemas/strict_ost.json +17 -16
- package/dist/schemas/generated/_structured_context_schema_meta.json +0 -191
- package/schemas/generated/_structured_context_schema_meta.json +0 -191
|
@@ -3,6 +3,7 @@ export interface GraphValidationResult {
|
|
|
3
3
|
violations: GraphViolation[];
|
|
4
4
|
refErrors: Array<{
|
|
5
5
|
file: string;
|
|
6
|
+
line?: number;
|
|
6
7
|
parent: string;
|
|
7
8
|
error: string;
|
|
8
9
|
}>;
|
|
@@ -15,7 +16,7 @@ export declare function validateGraph(nodes: SpaceNode[], metadata: SchemaMetada
|
|
|
15
16
|
/**
|
|
16
17
|
* Validate that resolved parents follow hierarchy level rules and relationship type constraints.
|
|
17
18
|
*
|
|
18
|
-
* For hierarchy edges (fieldOn:'child', source:'hierarchy'): validates level ordering.
|
|
19
|
+
* For hierarchy edges (fieldOn:'child', source:'hierarchy'): validates level ordering within the edge's hierarchy.
|
|
19
20
|
* For relationship edges (fieldOn:'child', source:'relationship'): validates parent type.
|
|
20
21
|
* For fieldOn:'parent' edges: validates child type, violation attributed to the field-owner node.
|
|
21
22
|
*
|
|
@@ -4,14 +4,19 @@ import { resolveNodeType } from '../schema/schema';
|
|
|
4
4
|
* Assumes resolveGraphEdges has already been called to populate node.resolvedParents.
|
|
5
5
|
*/
|
|
6
6
|
export function validateGraph(nodes, metadata, unresolvedRefs = []) {
|
|
7
|
-
const refErrors = unresolvedRefs.map((u) => ({
|
|
7
|
+
const refErrors = unresolvedRefs.map((u) => ({
|
|
8
|
+
file: u.source?.file ?? u.label,
|
|
9
|
+
...(u.source?.line !== undefined ? { line: u.source.line } : {}),
|
|
10
|
+
parent: u.ref,
|
|
11
|
+
error: u.message,
|
|
12
|
+
}));
|
|
8
13
|
const violations = validateHierarchyStructure(nodes, metadata);
|
|
9
14
|
return { violations, refErrors };
|
|
10
15
|
}
|
|
11
16
|
/**
|
|
12
17
|
* Validate that resolved parents follow hierarchy level rules and relationship type constraints.
|
|
13
18
|
*
|
|
14
|
-
* For hierarchy edges (fieldOn:'child', source:'hierarchy'): validates level ordering.
|
|
19
|
+
* For hierarchy edges (fieldOn:'child', source:'hierarchy'): validates level ordering within the edge's hierarchy.
|
|
15
20
|
* For relationship edges (fieldOn:'child', source:'relationship'): validates parent type.
|
|
16
21
|
* For fieldOn:'parent' edges: validates child type, violation attributed to the field-owner node.
|
|
17
22
|
*
|
|
@@ -19,11 +24,12 @@ export function validateGraph(nodes, metadata, unresolvedRefs = []) {
|
|
|
19
24
|
*/
|
|
20
25
|
export function validateHierarchyStructure(nodes, metadata) {
|
|
21
26
|
const violations = [];
|
|
22
|
-
const
|
|
27
|
+
const hierarchies = metadata.hierarchies ?? [];
|
|
23
28
|
const relationships = metadata.relationships ?? [];
|
|
24
|
-
const allowSkipLevels = metadata.hierarchy?.allowSkipLevels ?? false;
|
|
25
29
|
const typeAliases = metadata.typeAliases;
|
|
26
|
-
const
|
|
30
|
+
const hierarchiesByName = new Map(hierarchies.map((h) => [h.name, h]));
|
|
31
|
+
const levelTypesByName = new Map(hierarchies.map((h) => [h.name, h.levels.map((level) => resolveNodeType(level.type, typeAliases))]));
|
|
32
|
+
const allHierarchyTypes = new Set([...levelTypesByName.values()].flat());
|
|
27
33
|
// Build type rules: (ownerType, field) → set of valid target types.
|
|
28
34
|
// For fieldOn:'child': owner=childType, target=parentType.
|
|
29
35
|
// For fieldOn:'parent': owner=parentType (field owner), target=childType.
|
|
@@ -38,18 +44,20 @@ export function validateHierarchyStructure(nodes, metadata) {
|
|
|
38
44
|
fieldMap.set(field, new Set());
|
|
39
45
|
fieldMap.get(field).add(target);
|
|
40
46
|
}
|
|
41
|
-
for (
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
47
|
+
for (const { levels } of hierarchies) {
|
|
48
|
+
for (let i = 1; i < levels.length; i++) {
|
|
49
|
+
const level = levels[i];
|
|
50
|
+
const parentLevel = levels[i - 1];
|
|
51
|
+
if (level.fieldOn === 'parent') {
|
|
52
|
+
addTypeRule(parentLevel.type, level.field, level.type);
|
|
53
|
+
if (level.selfRef)
|
|
54
|
+
addTypeRule(level.type, level.field, level.type);
|
|
55
|
+
}
|
|
56
|
+
else {
|
|
57
|
+
addTypeRule(level.type, level.field, parentLevel.type);
|
|
58
|
+
if (level.selfRef)
|
|
59
|
+
addTypeRule(level.type, level.field, level.type);
|
|
60
|
+
}
|
|
53
61
|
}
|
|
54
62
|
}
|
|
55
63
|
for (const rel of relationships) {
|
|
@@ -107,15 +115,24 @@ export function validateHierarchyStructure(nodes, metadata) {
|
|
|
107
115
|
}
|
|
108
116
|
}
|
|
109
117
|
else {
|
|
110
|
-
// Hierarchy edge (fieldOn:'child', source:'hierarchy'): validate level ordering.
|
|
111
|
-
const
|
|
112
|
-
|
|
113
|
-
|
|
118
|
+
// Hierarchy edge (fieldOn:'child', source:'hierarchy'): validate level ordering within its hierarchy.
|
|
119
|
+
const edgeHierarchy = parentRef.hierarchy !== undefined ? hierarchiesByName.get(parentRef.hierarchy) : undefined;
|
|
120
|
+
if (!edgeHierarchy)
|
|
121
|
+
continue;
|
|
122
|
+
const levelTypes = levelTypesByName.get(edgeHierarchy.name);
|
|
123
|
+
const typeIndex = levelTypes.indexOf(nodeType);
|
|
124
|
+
const parentIndex = levelTypes.indexOf(parentType);
|
|
125
|
+
// A parent from another hierarchy is invalid; a parent of a type outside every hierarchy is not checked here.
|
|
126
|
+
if (typeIndex === -1 || (parentIndex === -1 && !allHierarchyTypes.has(parentType)))
|
|
114
127
|
continue;
|
|
115
|
-
const level = levels[typeIndex];
|
|
128
|
+
const level = edgeHierarchy.levels[typeIndex];
|
|
116
129
|
const canSelfRef = level.selfRef || level.selfRefField !== undefined;
|
|
130
|
+
const allowSkipLevels = edgeHierarchy.allowSkipLevels ?? false;
|
|
117
131
|
let isValidHierarchy = false;
|
|
118
|
-
if (parentIndex ===
|
|
132
|
+
if (parentIndex === -1) {
|
|
133
|
+
isValidHierarchy = false;
|
|
134
|
+
}
|
|
135
|
+
else if (parentIndex === typeIndex - 1) {
|
|
119
136
|
isValidHierarchy = true;
|
|
120
137
|
}
|
|
121
138
|
else if (canSelfRef && parentIndex === typeIndex) {
|
|
@@ -1,10 +1,23 @@
|
|
|
1
|
+
import type { TrimReport } from '../judge/trim';
|
|
2
|
+
import type { SemanticJudgeProvider } from '../judge/types';
|
|
1
3
|
import type { Rule, RuleViolation, SpaceNode } from '../types';
|
|
4
|
+
export { DEFAULT_SEVERITY_BY_CATEGORY, ruleSeverity } from './rule-severity';
|
|
5
|
+
export interface ValidateRulesOptions {
|
|
6
|
+
/** Evaluates semantic rules. Without one, a rule runs only its `expression`, and a semantic-only rule is skipped. */
|
|
7
|
+
judge?: SemanticJudgeProvider;
|
|
8
|
+
/** Label of the only node to judge (`validate-file`); other nodes' semantic rules are skipped. */
|
|
9
|
+
judgeTarget?: string;
|
|
10
|
+
/** Set of node labels that have already failed upstream structural checks. */
|
|
11
|
+
gatedNodeLabels?: Set<string>;
|
|
12
|
+
trimReport?: TrimReport;
|
|
13
|
+
}
|
|
2
14
|
/**
|
|
3
15
|
* Validate nodes against rules metadata.
|
|
4
|
-
*
|
|
16
|
+
* Evaluates expressions and (when opt-in) semantic parts.
|
|
5
17
|
*
|
|
6
18
|
* @param nodes - All nodes in the space
|
|
7
19
|
* @param rules - Rules metadata list
|
|
8
|
-
* @
|
|
20
|
+
* @param options - Validation options, including the judge for semantic rules
|
|
21
|
+
* @returns Rule violations
|
|
9
22
|
*/
|
|
10
|
-
export declare function validateRules(nodes: SpaceNode[], rules: Rule[]): Promise<RuleViolation[]>;
|
|
23
|
+
export declare function validateRules(nodes: SpaceNode[], rules: Rule[], options?: ValidateRulesOptions): Promise<RuleViolation[]>;
|
|
@@ -1,14 +1,20 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { judgeNodes } from '../judge/judge-node';
|
|
2
|
+
import { buildSpaceGraph } from '../space-graph';
|
|
3
|
+
import { buildEvalContext, evaluateRuleExpression, expressionErrorViolation, tryEvaluateExpression, } from './evaluate-rule';
|
|
4
|
+
import { ruleSeverities, ruleSeverity } from './rule-severity';
|
|
5
|
+
export { DEFAULT_SEVERITY_BY_CATEGORY, ruleSeverity } from './rule-severity';
|
|
2
6
|
/**
|
|
3
7
|
* Validate nodes against rules metadata.
|
|
4
|
-
*
|
|
8
|
+
* Evaluates expressions and (when opt-in) semantic parts.
|
|
5
9
|
*
|
|
6
10
|
* @param nodes - All nodes in the space
|
|
7
11
|
* @param rules - Rules metadata list
|
|
8
|
-
* @
|
|
12
|
+
* @param options - Validation options, including the judge for semantic rules
|
|
13
|
+
* @returns Rule violations
|
|
9
14
|
*/
|
|
10
|
-
export async function validateRules(nodes, rules) {
|
|
15
|
+
export async function validateRules(nodes, rules, options = {}) {
|
|
11
16
|
const violations = [];
|
|
17
|
+
const judge = options.judge;
|
|
12
18
|
// Build node index for efficient lookups
|
|
13
19
|
const nodeIndex = new Map();
|
|
14
20
|
for (const node of nodes) {
|
|
@@ -17,35 +23,91 @@ export async function validateRules(nodes, rules) {
|
|
|
17
23
|
nodeIndex.set(title, node);
|
|
18
24
|
}
|
|
19
25
|
}
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
26
|
+
const sentinel = nodes[0];
|
|
27
|
+
const runnable = rules.filter((r) => r.expression !== undefined || (r.semantic !== undefined && judge));
|
|
28
|
+
// The nodes each per-node rule applies to: matching `type` and passing the applicability condition (`when`).
|
|
29
|
+
const applicable = new Map();
|
|
30
|
+
for (const rule of runnable) {
|
|
31
|
+
if (rule.global)
|
|
32
|
+
continue;
|
|
33
|
+
const matching = [];
|
|
34
|
+
for (const node of nodes) {
|
|
35
|
+
if (rule.type && rule.type !== node.resolvedType)
|
|
36
|
+
continue;
|
|
37
|
+
if (rule.when) {
|
|
38
|
+
const when = await tryEvaluateExpression(rule.when, buildEvalContext(node, nodes, nodeIndex));
|
|
39
|
+
if ('error' in when) {
|
|
40
|
+
violations.push(expressionErrorViolation(rule, node.label, when.error));
|
|
41
|
+
continue;
|
|
31
42
|
}
|
|
43
|
+
if (when.value !== true)
|
|
44
|
+
continue;
|
|
32
45
|
}
|
|
46
|
+
matching.push(node);
|
|
33
47
|
}
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
+
applicable.set(rule, matching);
|
|
49
|
+
}
|
|
50
|
+
// 1. Expressions, in rule order. A global rule is evaluated once against the full node set; a sentinel node
|
|
51
|
+
// provides the evaluation context (the nodes array is what matters).
|
|
52
|
+
const failedNodeLabels = new Set(options.gatedNodeLabels); // nodes that failed an expression (or upstream)
|
|
53
|
+
const notEvaluable = new Set(); // `${rule.id}\0${label}` whose own expression failed or errored
|
|
54
|
+
for (const rule of runnable) {
|
|
55
|
+
if (rule.expression === undefined)
|
|
56
|
+
continue;
|
|
57
|
+
if (rule.global) {
|
|
58
|
+
if (!sentinel)
|
|
59
|
+
continue;
|
|
60
|
+
const result = await evaluateRuleExpression(rule, sentinel, nodes, nodeIndex, '');
|
|
61
|
+
if (!result.passed)
|
|
62
|
+
violations.push(result.violation);
|
|
63
|
+
continue;
|
|
64
|
+
}
|
|
65
|
+
for (const node of applicable.get(rule) ?? []) {
|
|
66
|
+
const result = await evaluateRuleExpression(rule, node, nodes, nodeIndex);
|
|
67
|
+
if (result.passed)
|
|
68
|
+
continue;
|
|
69
|
+
violations.push(result.violation);
|
|
70
|
+
notEvaluable.add(`${rule.id}\0${node.label}`);
|
|
71
|
+
if (result.failed)
|
|
72
|
+
failedNodeLabels.add(node.label);
|
|
48
73
|
}
|
|
49
74
|
}
|
|
75
|
+
// 2. Semantic parts, for nodes that passed their own expression. A node that failed an expression or
|
|
76
|
+
// upstream is left out (safe cascading default).
|
|
77
|
+
if (judge) {
|
|
78
|
+
const perNode = []; // kept in node order
|
|
79
|
+
const pending = [];
|
|
80
|
+
for (const node of nodes) {
|
|
81
|
+
if (options.judgeTarget !== undefined && node.label !== options.judgeTarget)
|
|
82
|
+
continue;
|
|
83
|
+
const found = [];
|
|
84
|
+
perNode.push(found);
|
|
85
|
+
if (failedNodeLabels.has(node.label))
|
|
86
|
+
continue;
|
|
87
|
+
const semantic = runnable.filter((r) => r.semantic !== undefined && applicable.get(r)?.includes(node) && !notEvaluable.has(`${r.id}\0${node.label}`));
|
|
88
|
+
if (semantic.length > 0)
|
|
89
|
+
pending.push({ node, rules: semantic, found });
|
|
90
|
+
}
|
|
91
|
+
// One provider call for every node, so a provider with `evaluateNodes` can share model calls.
|
|
92
|
+
const judged = await judgeNodes(judge, pending, buildSpaceGraph(nodes, undefined), options.trimReport);
|
|
93
|
+
pending.forEach(({ node, found }, i) => {
|
|
94
|
+
for (const j of judged[i] ?? []) {
|
|
95
|
+
if (!j.failed)
|
|
96
|
+
continue;
|
|
97
|
+
found.push({
|
|
98
|
+
file: node.label,
|
|
99
|
+
ruleId: j.rule.id,
|
|
100
|
+
category: j.rule.category,
|
|
101
|
+
severity: j.severity ?? ruleSeverities(j.rule)[0] ?? ruleSeverity(j.rule),
|
|
102
|
+
description: j.error ? `${j.rule.description} (Semantic evaluation error: ${j.error})` : j.rule.description,
|
|
103
|
+
evidence: j.evidence,
|
|
104
|
+
...(j.verdict !== undefined && { verdict: j.verdict }),
|
|
105
|
+
...(j.guidance !== undefined && { guidance: j.guidance }),
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
});
|
|
109
|
+
for (const found of perNode)
|
|
110
|
+
violations.push(...found);
|
|
111
|
+
}
|
|
50
112
|
return violations;
|
|
51
113
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
|
-
"$schema": "https://raw.githubusercontent.com/mindsocket/structured-context/main/schemas/generated/_structured_context_schema_meta.json",
|
|
3
|
-
"$id": "sctx://_knowledge_wiki",
|
|
2
|
+
"$schema": "https://raw.githubusercontent.com/mindsocket/structured-context/main/schemas/generated/latest/_structured_context_schema_meta.json",
|
|
3
|
+
"$id": "sctx://core/_knowledge_wiki",
|
|
4
4
|
// Shared entity definitions for knowledge wiki schemas.
|
|
5
5
|
// Provides aliases, relationships, rules, and entity type $defs.
|
|
6
6
|
// This partial has no hierarchy — wiki entities are flat with no required parent links.
|
|
@@ -50,14 +50,14 @@
|
|
|
50
50
|
"category": "coherence",
|
|
51
51
|
"description": "Syntheses should reference at least one contributing source in their sources array",
|
|
52
52
|
"type": "synthesis",
|
|
53
|
-
"
|
|
53
|
+
"expression": "$exists(current.sources) and $count(current.sources) >= 1"
|
|
54
54
|
},
|
|
55
55
|
{
|
|
56
56
|
"id": "concept-has-summary",
|
|
57
57
|
"category": "best-practice",
|
|
58
58
|
"description": "Concept pages should have a short summary for index discoverability",
|
|
59
59
|
"type": "concept",
|
|
60
|
-
"
|
|
60
|
+
"expression": "$exists(current.summary) and $length(current.summary) > 0"
|
|
61
61
|
}
|
|
62
62
|
]
|
|
63
63
|
},
|
|
@@ -65,18 +65,18 @@
|
|
|
65
65
|
"source": {
|
|
66
66
|
"type": "object",
|
|
67
67
|
"description": "A processed summary of a raw source (article, paper, book, video, thread, podcast, etc.). Captures key information from the source in structured form with provenance.",
|
|
68
|
-
"allOf": [{ "$ref": "sctx://_sctx_base#/$defs/baseNodeProps" }],
|
|
68
|
+
"allOf": [{ "$ref": "sctx://core/_sctx_base#/$defs/baseNodeProps" }],
|
|
69
69
|
"properties": {
|
|
70
70
|
"type": { "const": "source" },
|
|
71
71
|
"summary": {
|
|
72
|
-
"$ref": "sctx://_sctx_base#/$defs/summary"
|
|
72
|
+
"$ref": "sctx://core/_sctx_base#/$defs/summary"
|
|
73
73
|
},
|
|
74
74
|
"url": {
|
|
75
75
|
"type": "string",
|
|
76
76
|
"description": "URL of the original source"
|
|
77
77
|
},
|
|
78
78
|
"local_copy": {
|
|
79
|
-
"$ref": "sctx://_sctx_base#/$defs/wikilink",
|
|
79
|
+
"$ref": "sctx://core/_sctx_base#/$defs/wikilink",
|
|
80
80
|
"description": "Wikilink to a local copy of the source (e.g. a saved PDF or clipped note)"
|
|
81
81
|
},
|
|
82
82
|
"author": {
|
|
@@ -89,7 +89,7 @@
|
|
|
89
89
|
},
|
|
90
90
|
"published_date": {
|
|
91
91
|
"type": "string",
|
|
92
|
-
"format": "date",
|
|
92
|
+
"format": "date-or-date-time",
|
|
93
93
|
"description": "Original publication date"
|
|
94
94
|
},
|
|
95
95
|
"source_type": {
|
|
@@ -98,7 +98,7 @@
|
|
|
98
98
|
},
|
|
99
99
|
"sources": {
|
|
100
100
|
"type": "array",
|
|
101
|
-
"items": { "$ref": "sctx://_sctx_base#/$defs/wikilink" },
|
|
101
|
+
"items": { "$ref": "sctx://core/_sctx_base#/$defs/wikilink" },
|
|
102
102
|
"description": "Wikilinks to other source pages that this one references or extends"
|
|
103
103
|
}
|
|
104
104
|
},
|
|
@@ -117,20 +117,20 @@
|
|
|
117
117
|
"concept": {
|
|
118
118
|
"type": "object",
|
|
119
119
|
"description": "A concept page — defines and frames a specific idea, mechanism, or domain term. May draw from multiple sources. The building blocks of the wiki's knowledge graph.",
|
|
120
|
-
"allOf": [{ "$ref": "sctx://_sctx_base#/$defs/baseNodeProps" }],
|
|
120
|
+
"allOf": [{ "$ref": "sctx://core/_sctx_base#/$defs/baseNodeProps" }],
|
|
121
121
|
"properties": {
|
|
122
122
|
"type": { "const": "concept" },
|
|
123
123
|
"summary": {
|
|
124
|
-
"$ref": "sctx://_sctx_base#/$defs/summary"
|
|
124
|
+
"$ref": "sctx://core/_sctx_base#/$defs/summary"
|
|
125
125
|
},
|
|
126
126
|
"sources": {
|
|
127
127
|
"type": "array",
|
|
128
|
-
"items": { "$ref": "sctx://_sctx_base#/$defs/wikilink" },
|
|
128
|
+
"items": { "$ref": "sctx://core/_sctx_base#/$defs/wikilink" },
|
|
129
129
|
"description": "Wikilinks to source pages that contributed to this concept"
|
|
130
130
|
},
|
|
131
131
|
"related": {
|
|
132
132
|
"type": "array",
|
|
133
|
-
"items": { "$ref": "sctx://_sctx_base#/$defs/wikilink" },
|
|
133
|
+
"items": { "$ref": "sctx://core/_sctx_base#/$defs/wikilink" },
|
|
134
134
|
"description": "Wikilinks to related concept or synthesis pages"
|
|
135
135
|
}
|
|
136
136
|
},
|
|
@@ -147,20 +147,20 @@
|
|
|
147
147
|
"entity": {
|
|
148
148
|
"type": "object",
|
|
149
149
|
"description": "A concrete entity — a person, organisation, product, model, or other named thing. The building blocks of the wiki's knowledge graph alongside concepts. Entities are distinct from concepts: a concept frames an idea or mechanism; an entity is a specific, identifiable thing in the world.",
|
|
150
|
-
"allOf": [{ "$ref": "sctx://_sctx_base#/$defs/baseNodeProps" }],
|
|
150
|
+
"allOf": [{ "$ref": "sctx://core/_sctx_base#/$defs/baseNodeProps" }],
|
|
151
151
|
"properties": {
|
|
152
152
|
"type": { "const": "entity" },
|
|
153
153
|
"summary": {
|
|
154
|
-
"$ref": "sctx://_sctx_base#/$defs/summary"
|
|
154
|
+
"$ref": "sctx://core/_sctx_base#/$defs/summary"
|
|
155
155
|
},
|
|
156
156
|
"sources": {
|
|
157
157
|
"type": "array",
|
|
158
|
-
"items": { "$ref": "sctx://_sctx_base#/$defs/wikilink" },
|
|
158
|
+
"items": { "$ref": "sctx://core/_sctx_base#/$defs/wikilink" },
|
|
159
159
|
"description": "Wikilinks to source pages that contributed to this entity page"
|
|
160
160
|
},
|
|
161
161
|
"related": {
|
|
162
162
|
"type": "array",
|
|
163
|
-
"items": { "$ref": "sctx://_sctx_base#/$defs/wikilink" },
|
|
163
|
+
"items": { "$ref": "sctx://core/_sctx_base#/$defs/wikilink" },
|
|
164
164
|
"description": "Wikilinks to related entity, concept, or synthesis pages"
|
|
165
165
|
}
|
|
166
166
|
},
|
|
@@ -177,20 +177,20 @@
|
|
|
177
177
|
"synthesis": {
|
|
178
178
|
"type": "object",
|
|
179
179
|
"description": "A synthesis page — connects multiple concepts and sources into a coherent model, framework, or thesis. The highest-value pages in a knowledge wiki. Must reference contributing sources.",
|
|
180
|
-
"allOf": [{ "$ref": "sctx://_sctx_base#/$defs/baseNodeProps" }],
|
|
180
|
+
"allOf": [{ "$ref": "sctx://core/_sctx_base#/$defs/baseNodeProps" }],
|
|
181
181
|
"properties": {
|
|
182
182
|
"type": { "const": "synthesis" },
|
|
183
183
|
"summary": {
|
|
184
|
-
"$ref": "sctx://_sctx_base#/$defs/summary"
|
|
184
|
+
"$ref": "sctx://core/_sctx_base#/$defs/summary"
|
|
185
185
|
},
|
|
186
186
|
"sources": {
|
|
187
187
|
"type": "array",
|
|
188
|
-
"items": { "$ref": "sctx://_sctx_base#/$defs/wikilink" },
|
|
188
|
+
"items": { "$ref": "sctx://core/_sctx_base#/$defs/wikilink" },
|
|
189
189
|
"description": "Wikilinks to source pages this synthesis draws from"
|
|
190
190
|
},
|
|
191
191
|
"concepts": {
|
|
192
192
|
"type": "array",
|
|
193
|
-
"items": { "$ref": "sctx://_sctx_base#/$defs/wikilink" },
|
|
193
|
+
"items": { "$ref": "sctx://core/_sctx_base#/$defs/wikilink" },
|
|
194
194
|
"description": "Wikilinks to concept pages this synthesis connects"
|
|
195
195
|
}
|
|
196
196
|
},
|
|
@@ -208,12 +208,12 @@
|
|
|
208
208
|
"note": {
|
|
209
209
|
"type": "object",
|
|
210
210
|
"description": "Personal notes, journal entries, talk notes, or rough thoughts not drawn from an external URL.",
|
|
211
|
-
"allOf": [{ "$ref": "sctx://_sctx_base#/$defs/baseNodeProps" }],
|
|
211
|
+
"allOf": [{ "$ref": "sctx://core/_sctx_base#/$defs/baseNodeProps" }],
|
|
212
212
|
"properties": {
|
|
213
213
|
"type": { "const": "note" },
|
|
214
214
|
"date": {
|
|
215
215
|
"type": "string",
|
|
216
|
-
"format": "date",
|
|
216
|
+
"format": "date-or-date-time",
|
|
217
217
|
"description": "Date this entry was created or relates to"
|
|
218
218
|
}
|
|
219
219
|
},
|
|
@@ -230,7 +230,7 @@
|
|
|
230
230
|
"index": {
|
|
231
231
|
"type": "object",
|
|
232
232
|
"description": "The wiki index — a catalog of all pages with links, summaries, and metadata. Updated by the agent on ingest. Used as the primary navigation entry point.",
|
|
233
|
-
"allOf": [{ "$ref": "sctx://_sctx_base#/$defs/baseNodeProps" }],
|
|
233
|
+
"allOf": [{ "$ref": "sctx://core/_sctx_base#/$defs/baseNodeProps" }],
|
|
234
234
|
"properties": {
|
|
235
235
|
"type": { "const": "index" }
|
|
236
236
|
},
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://raw.githubusercontent.com/mindsocket/structured-context/main/schemas/generated/latest/_structured_context_schema_meta.json",
|
|
3
|
+
"$id": "sctx://core/_okf",
|
|
4
|
+
// Open Knowledge Format v0.2 frontmatter families: core, provenance, trust, lifecycle, attested computation.
|
|
5
|
+
// OKF §4.1: consumers MUST NOT reject unknown fields, so every object here allows additional properties.
|
|
6
|
+
// Concept types are not registered centrally (§4.1), so there is deliberately no oneOf over `type` consts:
|
|
7
|
+
// type checking would reject every type not listed. `Attested Computation` is handled with if/then instead.
|
|
8
|
+
// index.md and log.md are reserved bundle files without a `type`; they are reported as excluded during parsing.
|
|
9
|
+
"$metadata": {
|
|
10
|
+
"rules": [
|
|
11
|
+
{
|
|
12
|
+
"id": "okf-description",
|
|
13
|
+
"category": "best-practice",
|
|
14
|
+
"severity": "warning",
|
|
15
|
+
"description": "Concepts should have a one-line description (used by index generators and search snippets)",
|
|
16
|
+
"expression": "$exists(current.description) and $length(current.description) > 0"
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
"id": "okf-stale-after-generated",
|
|
20
|
+
"category": "validation",
|
|
21
|
+
"description": "stale_after must be later than generated.at",
|
|
22
|
+
"expression": "$not($exists(current.stale_after)) or $not($exists(current.generated.at)) or $toMillis(current.stale_after) > $toMillis(current.generated.at)"
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
"id": "okf-usage-count-window",
|
|
26
|
+
"category": "coherence",
|
|
27
|
+
"severity": "warning",
|
|
28
|
+
"description": "A source's usage_count is only meaningful within a usage_window (on the source or shared at the top level)",
|
|
29
|
+
"expression": "$not($exists(current.sources)) or $count(current.sources[$exists(usage_count) and $not($exists(usage_window)) and $not($exists($$.current.usage_window))]) = 0"
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
"id": "okf-computation-present",
|
|
33
|
+
"category": "validation",
|
|
34
|
+
"type": "Attested Computation",
|
|
35
|
+
"description": "An Attested Computation needs its computation: a `computation` path or a body `# Computation` section. Known limitation: the section is found by matching a `# Computation` line in the body text, so such a line inside a code fence also counts",
|
|
36
|
+
"expression": "$exists(current.computation) or $contains(current.content, /^# Computation[ \\t]*$/m)"
|
|
37
|
+
}
|
|
38
|
+
]
|
|
39
|
+
},
|
|
40
|
+
"$defs": {
|
|
41
|
+
"timestamp": {
|
|
42
|
+
"type": "string",
|
|
43
|
+
"format": "date-time",
|
|
44
|
+
"description": "ISO 8601 datetime with an explicit UTC offset, e.g. 2026-06-30T14:00:00Z (§5)"
|
|
45
|
+
},
|
|
46
|
+
"actor": {
|
|
47
|
+
"type": "string",
|
|
48
|
+
"minLength": 1,
|
|
49
|
+
"description": "<producer>/<version> for agents, human:<id> for people, process:<id> for automated processes (§7)"
|
|
50
|
+
},
|
|
51
|
+
"usageWindow": {
|
|
52
|
+
"type": "object",
|
|
53
|
+
"properties": {
|
|
54
|
+
"from": { "$ref": "#/$defs/timestamp" },
|
|
55
|
+
"to": { "$ref": "#/$defs/timestamp" }
|
|
56
|
+
},
|
|
57
|
+
"required": ["from", "to"]
|
|
58
|
+
},
|
|
59
|
+
"source": {
|
|
60
|
+
"type": "object",
|
|
61
|
+
"properties": {
|
|
62
|
+
"resource": {
|
|
63
|
+
"type": "string",
|
|
64
|
+
"minLength": 1,
|
|
65
|
+
"description": "Followable artifact (URL or path) or a scope descriptor"
|
|
66
|
+
},
|
|
67
|
+
"id": { "type": "string", "description": "Stable key for footnote attribution" },
|
|
68
|
+
"title": { "type": "string" },
|
|
69
|
+
"author": { "$ref": "#/$defs/actor" },
|
|
70
|
+
"usage_count": { "type": "integer", "minimum": 0 },
|
|
71
|
+
"last_modified": { "$ref": "#/$defs/timestamp" },
|
|
72
|
+
"usage_window": { "$ref": "#/$defs/usageWindow" }
|
|
73
|
+
},
|
|
74
|
+
"required": ["resource"]
|
|
75
|
+
},
|
|
76
|
+
"generated": {
|
|
77
|
+
"type": "object",
|
|
78
|
+
"properties": {
|
|
79
|
+
"by": { "$ref": "#/$defs/actor" },
|
|
80
|
+
"at": { "$ref": "#/$defs/timestamp" }
|
|
81
|
+
},
|
|
82
|
+
"required": ["by"]
|
|
83
|
+
},
|
|
84
|
+
"verification": {
|
|
85
|
+
"type": "object",
|
|
86
|
+
"properties": {
|
|
87
|
+
"by": { "$ref": "#/$defs/actor" },
|
|
88
|
+
"at": { "$ref": "#/$defs/timestamp" }
|
|
89
|
+
},
|
|
90
|
+
"required": ["by", "at"]
|
|
91
|
+
},
|
|
92
|
+
"okfProps": {
|
|
93
|
+
"type": "object",
|
|
94
|
+
"properties": {
|
|
95
|
+
"type": {
|
|
96
|
+
"type": "string",
|
|
97
|
+
"minLength": 1,
|
|
98
|
+
"description": "Kind of concept; unregistered and open-ended (§4.1)"
|
|
99
|
+
},
|
|
100
|
+
"title": { "type": "string" },
|
|
101
|
+
"description": { "type": "string" },
|
|
102
|
+
"resource": { "type": "string", "minLength": 1, "description": "URI or path of the underlying asset" },
|
|
103
|
+
"tags": { "type": "array", "items": { "type": "string" } },
|
|
104
|
+
"sources": { "type": "array", "items": { "$ref": "#/$defs/source" } },
|
|
105
|
+
"usage_window": { "$ref": "#/$defs/usageWindow" },
|
|
106
|
+
"generated": { "$ref": "#/$defs/generated" },
|
|
107
|
+
"verified": {
|
|
108
|
+
"description": "One verification event, or a list of them (§5.2: a bare mapping is a one-element list)",
|
|
109
|
+
"anyOf": [
|
|
110
|
+
{ "$ref": "#/$defs/verification" },
|
|
111
|
+
{ "type": "array", "items": { "$ref": "#/$defs/verification" } }
|
|
112
|
+
]
|
|
113
|
+
},
|
|
114
|
+
"status": { "type": "string", "enum": ["draft", "stable", "deprecated"], "default": "stable" },
|
|
115
|
+
"stale_after": { "$ref": "#/$defs/timestamp" }
|
|
116
|
+
},
|
|
117
|
+
"required": ["type"]
|
|
118
|
+
},
|
|
119
|
+
"concept": {
|
|
120
|
+
"type": "object",
|
|
121
|
+
"allOf": [
|
|
122
|
+
{ "$ref": "#/$defs/okfProps" },
|
|
123
|
+
{
|
|
124
|
+
"if": { "properties": { "type": { "const": "Attested Computation" } }, "required": ["type"] },
|
|
125
|
+
"then": { "$ref": "#/$defs/attestedComputation" }
|
|
126
|
+
}
|
|
127
|
+
],
|
|
128
|
+
"additionalProperties": true
|
|
129
|
+
},
|
|
130
|
+
"attestedComputation": {
|
|
131
|
+
"type": "object",
|
|
132
|
+
"properties": {
|
|
133
|
+
"runtime": {
|
|
134
|
+
"type": "string",
|
|
135
|
+
"minLength": 1,
|
|
136
|
+
"description": "How to run the computation; defines what `parameters` mean (§10.2)"
|
|
137
|
+
},
|
|
138
|
+
"parameters": {
|
|
139
|
+
"type": "array",
|
|
140
|
+
"items": {
|
|
141
|
+
"type": "object",
|
|
142
|
+
"properties": {
|
|
143
|
+
"name": { "type": "string", "minLength": 1 },
|
|
144
|
+
"type": { "type": "string", "minLength": 1 },
|
|
145
|
+
"required": { "type": "boolean" }
|
|
146
|
+
},
|
|
147
|
+
"required": ["name", "type"]
|
|
148
|
+
}
|
|
149
|
+
},
|
|
150
|
+
"computation": {
|
|
151
|
+
"type": "string",
|
|
152
|
+
"minLength": 1,
|
|
153
|
+
"description": "Path to a file holding the computation, instead of the body fence"
|
|
154
|
+
},
|
|
155
|
+
"executor": {
|
|
156
|
+
"type": "object",
|
|
157
|
+
"properties": {
|
|
158
|
+
"resource": { "type": "string", "minLength": 1 },
|
|
159
|
+
"receipt": { "type": "array", "items": { "type": "string" }, "description": "Fields a run must return" }
|
|
160
|
+
},
|
|
161
|
+
"required": ["resource"]
|
|
162
|
+
},
|
|
163
|
+
"attester": {
|
|
164
|
+
"type": "object",
|
|
165
|
+
"properties": { "resource": { "type": "string", "minLength": 1 } },
|
|
166
|
+
"required": ["resource"]
|
|
167
|
+
}
|
|
168
|
+
},
|
|
169
|
+
"required": ["runtime"]
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|