mirai-graph 1.0.0 → 1.1.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.
Files changed (31) hide show
  1. package/.github/workflows/ci.yml +19 -0
  2. package/CHANGELOG.md +50 -0
  3. package/CITATION.cff +2 -1
  4. package/README.md +21 -2
  5. package/ROADMAP.md +11 -16
  6. package/docs/adoption/cli.md +16 -1
  7. package/examples/project-technology-context-traversal/README.md +17 -0
  8. package/examples/project-technology-context-traversal/graph/objects.json +78 -0
  9. package/examples/project-technology-context-traversal/graph/relations.json +47 -0
  10. package/examples/project-technology-context-traversal/graph.json +30 -0
  11. package/examples/project-technology-context-traversal/selection.example.json +29 -0
  12. package/examples/project-technology-context-traversal/sources/announcement.md +3 -0
  13. package/examples/project-technology-context-traversal/sources/release-guide.md +3 -0
  14. package/examples/project-technology-context-traversal/sources/release-smoke.md +3 -0
  15. package/examples/project-technology-context-traversal/sources/rollback-policy.md +3 -0
  16. package/package.json +4 -3
  17. package/packages/cli/project-technology.js +43 -3
  18. package/packages/cli/validate-context-traversal.js +292 -0
  19. package/packages/cli/validate-mirai-graph.js +21 -2
  20. package/packages/project-technology/context-traversal.js +668 -0
  21. package/packages/project-technology/index.js +23 -40
  22. package/releases/1.1.0.md +36 -0
  23. package/releases/1.1.1.md +25 -0
  24. package/releases/README.md +2 -0
  25. package/schemas/context-pack.schema.json +54 -1
  26. package/schemas/context-traversal-receipt.schema.json +64 -0
  27. package/standard/context-pack-generation.md +38 -1
  28. package/standard/object-model.md +11 -2
  29. package/standard/project-technology.md +37 -1
  30. package/standard/relation-model.md +11 -1
  31. package/standard/test-suite.md +5 -1
@@ -0,0 +1,292 @@
1
+ #!/usr/bin/env node
2
+
3
+ "use strict";
4
+
5
+ const assert = require("assert");
6
+ const fs = require("fs");
7
+ const os = require("os");
8
+ const path = require("path");
9
+ const { spawnSync } = require("child_process");
10
+ const technology = require("../project-technology");
11
+ const cli = path.join(__dirname, "project-technology.js");
12
+
13
+ function writeJson(file, value) {
14
+ fs.mkdirSync(path.dirname(file), { recursive: true });
15
+ fs.writeFileSync(file, `${JSON.stringify(value, null, 2)}\n`);
16
+ }
17
+
18
+ function git(repo, ...args) {
19
+ const completed = spawnSync("git", args, { cwd: repo, encoding: "utf8" });
20
+ if (completed.status !== 0) throw new Error(completed.stderr || `git ${args.join(" ")} failed`);
21
+ return completed.stdout.trim();
22
+ }
23
+
24
+ function object(id, kind, title, extra = {}) {
25
+ return {
26
+ id, kind, title, summary: `${title} summary`, readiness: "accepted",
27
+ evidence: [], profile: "project_management", expansion_policy: "conditional", ...extra,
28
+ };
29
+ }
30
+
31
+ function relation(id, type, source, target, readiness = "accepted") {
32
+ return { id, type, source, target, readiness, evidence: [], profile: "project_management" };
33
+ }
34
+
35
+ function createFixture(root, name, scope = "repository", mutation = null) {
36
+ const repo = path.join(root, name); fs.mkdirSync(repo, { recursive: true });
37
+ git(repo, "init", "-q"); git(repo, "config", "user.name", "Mirai Fixture"); git(repo, "config", "user.email", "fixture@example.invalid");
38
+ const objects = [
39
+ object("capability.delivery", "capability", "Deliver service", { aliases: ["build application"], owner: "role.delivery", expansion_policy: "expandable" }),
40
+ object("process.delivery", "process", "Delivery process", { owner: "role.delivery", expansion_policy: "expandable" }),
41
+ object("resource.delivery_guide", "resource", "Delivery guide", { owner: "role.delivery", expansion_policy: "expandable", source_refs: ["docs/delivery.md"] }),
42
+ object("constraint.safety", "constraint", "Safety boundary", { owner: "role.safety", expansion_policy: "expandable", source_refs: ["docs/safety.md"] }),
43
+ object("check.delivery", "check", "Delivery validation", { owner: "role.quality", expansion_policy: "terminal", source_refs: ["docs/check.md"] }),
44
+ object("resource.shared", "resource", "Shared source", { owner: "role.delivery", expansion_policy: "terminal", source_refs: ["docs/shared.md"] }),
45
+ object("capability.optional", "capability", "Optional reporting", { owner: "role.delivery", expansion_policy: "terminal", source_refs: ["docs/optional.md"] }),
46
+ object("capability.unrelated", "capability", "Unrelated payroll", { owner: "role.finance", expansion_policy: "terminal", source_refs: ["docs/unrelated.md"] }),
47
+ ];
48
+ const relations = [
49
+ relation("relation.delivery_contains_process", "contains", "capability.delivery", "process.delivery"),
50
+ relation("relation.process_contains_guide", "contains", "process.delivery", "resource.delivery_guide"),
51
+ relation("relation.process_requires_safety", "governed_by", "process.delivery", "constraint.safety"),
52
+ relation("relation.process_validated", "validated_by", "process.delivery", "check.delivery"),
53
+ relation("relation.guide_shared", "documented_by", "resource.delivery_guide", "resource.shared"),
54
+ relation("relation.safety_shared", "documented_by", "constraint.safety", "resource.shared"),
55
+ relation("relation.delivery_optional", "contains", "capability.delivery", "capability.optional"),
56
+ ];
57
+ if (mutation) mutation(objects, relations);
58
+ const manifest = {
59
+ $schema: "https://mirai-graph.dev/schemas/graph-manifest.schema.json", format: "mirai-graph", schema_version: "2.0.0",
60
+ id: name, aliases: [], title: name, scope, kind: `${scope}_graph`, owner: "fixture-owner", profiles: ["project_management", "implementation_control"],
61
+ graph: { root: "graph", source_of_truth: ["graph/specs"], objects: ["graph/specs/objects.json"], relations: ["graph/specs/relations.json"], schemas: [], generated: ["graph/generated"], raw_sources: ["docs"] },
62
+ imports: [], extensions: { "mirai.project_technology": { contract_version: "1.0.0", enabled: true, context_policy: "task_scoped", source_boundary: "hybrid_sot" } },
63
+ };
64
+ if (scope === "package") { manifest.version = "1.0.0"; manifest.conformance_level = "level_2"; }
65
+ writeJson(path.join(repo, "graph.json"), manifest);
66
+ writeJson(path.join(repo, "graph/specs/objects.json"), objects);
67
+ writeJson(path.join(repo, "graph/specs/relations.json"), relations);
68
+ for (const name of ["delivery", "safety", "check", "shared", "optional", "unrelated"]) {
69
+ fs.mkdirSync(path.join(repo, "docs"), { recursive: true });
70
+ fs.writeFileSync(path.join(repo, `docs/${name}.md`), `# ${name}\nSafe fixture source.\n`);
71
+ }
72
+ git(repo, "add", "."); git(repo, "commit", "-qm", "context traversal fixture");
73
+ return repo;
74
+ }
75
+
76
+ function createLegacyFixture(root) {
77
+ const repo = createFixture(root, "legacy-skill-graph", "skill");
78
+ const objectFile = path.join(repo, "graph/specs/objects.json");
79
+ const relationFile = path.join(repo, "graph/specs/relations.json");
80
+ const objects = JSON.parse(fs.readFileSync(objectFile, "utf8")).map((item) => {
81
+ const legacy = { ...item, type: item.kind };
82
+ delete legacy.kind;
83
+ delete legacy.readiness;
84
+ return legacy;
85
+ });
86
+ const aliases = {
87
+ contains: "implements",
88
+ governed_by: "governed_by",
89
+ validated_by: "validates",
90
+ documented_by: "evidenced_by",
91
+ };
92
+ const relations = JSON.parse(fs.readFileSync(relationFile, "utf8")).map((item) => {
93
+ const legacyType = aliases[item.type] || item.type;
94
+ const reverse = item.type === "validated_by";
95
+ const legacy = {
96
+ ...item,
97
+ relation_type: legacyType,
98
+ from: reverse ? item.target : item.source,
99
+ to: reverse ? item.source : item.target,
100
+ status: item.readiness,
101
+ };
102
+ delete legacy.type;
103
+ delete legacy.source;
104
+ delete legacy.target;
105
+ delete legacy.readiness;
106
+ return legacy;
107
+ });
108
+ writeJson(objectFile, objects);
109
+ writeJson(relationFile, relations);
110
+ git(repo, "add", "."); git(repo, "commit", "-qm", "legacy graph aliases");
111
+ return repo;
112
+ }
113
+
114
+ function selection(receipt) {
115
+ return {
116
+ selector: "ai", task_digest: receipt.task.digest, graph_digest: receipt.graph.digest,
117
+ result_owner_id: "role.delivery",
118
+ selected: [
119
+ { id: "capability.delivery", reason: "matches requested delivery outcome", confidence: 0.98 },
120
+ { id: "process.delivery", reason: "implements the outcome", confidence: 0.96 },
121
+ { id: "resource.delivery_guide", reason: "authoritative terminal guide", confidence: 0.94 },
122
+ { id: "resource.shared", reason: "shared source for selected path", confidence: 0.9 },
123
+ ],
124
+ rejected: [{ id: "capability.optional", reason: "reporting is outside the requested outcome" }],
125
+ };
126
+ }
127
+
128
+ const checks = [];
129
+ function check(id, condition, details = null) {
130
+ checks.push({ id, passed: Boolean(condition), details });
131
+ assert.ok(condition, `${id}: ${JSON.stringify(details)}`);
132
+ }
133
+
134
+ function runCli(args) {
135
+ const completed = spawnSync(process.execPath, [cli, ...args], { encoding: "utf8", maxBuffer: 64 * 1024 * 1024 });
136
+ const stdout = String(completed.stdout || "").replace(/^\uFEFF/, "").trim();
137
+ let output = null; let parseError = null;
138
+ try { output = JSON.parse(stdout); } catch (error) { parseError = error.message; }
139
+ return { status: completed.status, output, stderr: completed.stderr, stdout_length: stdout.length, parse_error: parseError };
140
+ }
141
+
142
+ const root = fs.mkdtempSync(path.join(os.tmpdir(), "mirai-context-traversal-"));
143
+ try {
144
+ const repo = createFixture(root, "software-project");
145
+ for (const [name, scope] of [["research-program", "domain"], ["organization", "workspace"], ["technology-platform", "package"], ["skill-like-repository", "skill"], ["multi-repository-system", "federation"]]) {
146
+ const fixture = createFixture(root, name, scope);
147
+ const discovered = technology.discoverContext(fixture, "deliver application safely", { maxCandidates: 2 });
148
+ check(`universal_scope_${scope}`, discovered.status === "success" && discovered.traversal_receipt.repository_id === name, discovered.blockers);
149
+ }
150
+
151
+ const legacyRepo = createLegacyFixture(root);
152
+ let legacyReceipt = technology.discoverContext(legacyRepo, "build application delivery safely").traversal_receipt;
153
+ for (const ids of [["capability.delivery"], ["process.delivery"], ["resource.delivery_guide", "constraint.safety", "check.delivery"], ["resource.shared"]]) {
154
+ legacyReceipt = technology.expandContext(legacyRepo, legacyReceipt, ids, { selector: "ai", reason: "legacy graph compatibility" }).traversal_receipt;
155
+ }
156
+ const legacyCompiled = technology.compileContext(legacyRepo, legacyReceipt, selection(legacyReceipt));
157
+ check("legacy_graph_2_aliases_compile", legacyCompiled.status === "ready", legacyCompiled.blockers);
158
+ check("legacy_relation_semantics_preserved", legacyCompiled.context_pack.required_closure.object_ids.includes("constraint.safety") && legacyCompiled.context_pack.validators.includes("check.delivery"), legacyCompiled.context_pack);
159
+ const legacyReadinessRepo = createFixture(root, "legacy-readiness", "skill", (objects) => {
160
+ objects.find((item) => item.id === "capability.delivery").readiness = "R3_structured";
161
+ });
162
+ let legacyReadinessReceipt = technology.discoverContext(legacyReadinessRepo, "build application delivery safely").traversal_receipt;
163
+ for (const ids of [["capability.delivery"], ["process.delivery"], ["resource.delivery_guide", "constraint.safety", "check.delivery"], ["resource.shared"]]) {
164
+ legacyReadinessReceipt = technology.expandContext(legacyReadinessRepo, legacyReadinessReceipt, ids).traversal_receipt;
165
+ }
166
+ check("legacy_ready_level_compiles", technology.compileContext(legacyReadinessRepo, legacyReadinessReceipt, selection(legacyReadinessReceipt)).status === "ready");
167
+ const legacyDraftRepo = createFixture(root, "legacy-draft-readiness", "skill", (objects) => {
168
+ objects.find((item) => item.id === "capability.delivery").readiness = "R2_seed";
169
+ });
170
+ const legacyDraftReceipt = technology.discoverContext(legacyDraftRepo, "build application delivery safely").traversal_receipt;
171
+ check("legacy_draft_level_remains_blocked", technology.compileContext(legacyDraftRepo, legacyDraftReceipt, selection(legacyDraftReceipt)).status !== "ready");
172
+
173
+ const before = git(repo, "status", "--porcelain=v1");
174
+ const discovered = technology.discoverContext(repo, "build application delivery safely", { maxCandidates: 2 });
175
+ check("discover_is_bounded", discovered.status === "success" && discovered.traversal_receipt.candidates.length <= 2, discovered);
176
+ check("discover_returns_top_level_candidate", discovered.traversal_receipt.candidates[0].id === "capability.delivery", discovered.traversal_receipt.candidates);
177
+ check("runtime_passport_is_machine_readable", discovered.traversal_receipt.nodes[0].expandable === true && Array.isArray(discovered.traversal_receipt.nodes[0].child_kinds));
178
+
179
+ const first = technology.expandContext(repo, discovered.traversal_receipt, ["capability.delivery"], { selector: "ai", reason: "delivery branch" });
180
+ check("first_expand_reveals_children", first.traversal_receipt.visited_ids.includes("process.delivery") && first.traversal_receipt.visited_ids.includes("capability.optional"));
181
+ const second = technology.expandContext(repo, first.traversal_receipt, ["process.delivery"], { selector: "ai", reason: "delivery process" });
182
+ check("expand_reaches_arbitrary_depth", second.traversal_receipt.visited_ids.includes("resource.delivery_guide"));
183
+ const third = technology.expandContext(repo, second.traversal_receipt, ["resource.delivery_guide", "constraint.safety", "check.delivery"], { selector: "ai", reason: "required leaves" });
184
+ const fourth = technology.expandContext(repo, third.traversal_receipt, ["resource.shared"], { selector: "ai", reason: "shared source" });
185
+ check("terminal_node_stops", !fourth.traversal_receipt.frontier_ids.includes("resource.shared"));
186
+
187
+ const batchFirst = technology.expandContext(repo, discovered.traversal_receipt, ["capability.delivery"], { selector: "ai", reason: "delivery branch" });
188
+ const batchSecond = technology.expandContext(repo, batchFirst.traversal_receipt, ["process.delivery"], { selector: "ai", reason: "delivery process" });
189
+ const batchLeaves = technology.expandContext(repo, batchSecond.traversal_receipt, ["check.delivery", "constraint.safety", "resource.delivery_guide"], { selector: "ai", reason: "required leaves" });
190
+ const batch = technology.expandContext(repo, batchLeaves.traversal_receipt, ["resource.shared"], { selector: "ai", reason: "shared source" });
191
+ let sequential = batchSecond;
192
+ for (const id of ["check.delivery", "constraint.safety", "resource.delivery_guide"]) sequential = technology.expandContext(repo, sequential.traversal_receipt, [id], { selector: "ai", reason: "required leaves" });
193
+ sequential = technology.expandContext(repo, sequential.traversal_receipt, ["resource.shared"], { selector: "ai", reason: "shared source" });
194
+ check("batch_and_sequential_expand_match", batch.traversal_receipt.traversal_digest === sequential.traversal_receipt.traversal_digest, { batch: batch.traversal_receipt.traversal_digest, sequential: sequential.traversal_receipt.traversal_digest });
195
+
196
+ const compiled = technology.compileContext(repo, fourth.traversal_receipt, selection(fourth.traversal_receipt));
197
+ check("compile_ready", compiled.status === "ready", compiled.blockers);
198
+ check("required_dependencies_are_automatic", compiled.context_pack.required_closure.object_ids.includes("constraint.safety") && compiled.context_pack.required_closure.object_ids.includes("check.delivery"));
199
+ const selectionWithoutSources = selection(fourth.traversal_receipt);
200
+ selectionWithoutSources.selected = selectionWithoutSources.selected.filter((item) => !item.id.startsWith("resource."));
201
+ const compiledWithSourceClosure = technology.compileContext(repo, fourth.traversal_receipt, selectionWithoutSources);
202
+ check("declared_sources_are_automatic", compiledWithSourceClosure.status === "ready" && compiledWithSourceClosure.context_pack.required_closure.object_ids.includes("resource.delivery_guide") && compiledWithSourceClosure.context_pack.required_closure.object_ids.includes("resource.shared"), { blockers: compiledWithSourceClosure.blockers, completeness: compiledWithSourceClosure.context_pack.completeness, closure: compiledWithSourceClosure.context_pack.required_closure.object_ids });
203
+ check("optional_branch_has_reason", compiled.context_pack.omissions.some((item) => item.id === "capability.optional"));
204
+ check("shared_source_is_not_duplicated", compiled.context_pack.terminal_sources.filter((item) => item.ref === "docs/shared.md").length === 1, compiled.context_pack.terminal_sources);
205
+ const compiledAgain = technology.compileContext(repo, fourth.traversal_receipt, selection(fourth.traversal_receipt));
206
+ check("compile_is_byte_identical", JSON.stringify(compiled) === JSON.stringify(compiledAgain));
207
+
208
+ const usage = compiled.context_pack.terminal_sources.map((source) => ({
209
+ context_pack_digest: compiled.context_pack.context_pack_digest,
210
+ source: source.ref,
211
+ applied_rule: compiled.context_pack.processes[0] || `apply ${source.node_ids[0]}`,
212
+ process: compiled.context_pack.processes[0] || null,
213
+ decision: "follow the accepted bounded path",
214
+ surface: "fixture surface",
215
+ validator: compiled.context_pack.validators[0] || "check.delivery",
216
+ outcome: "verified",
217
+ }));
218
+ check("usage_verification_passes", technology.verifyContext(repo, compiled.context_pack, usage).status === "success");
219
+ check("loading_is_not_application", technology.verifyContext(repo, compiled.context_pack, []).status === "blocked");
220
+ const wrongPackUsage = usage.map((item) => ({ ...item, context_pack_digest: `sha256:${"0".repeat(64)}` }));
221
+ check("different_pack_usage_is_blocked", technology.verifyContext(repo, compiled.context_pack, wrongPackUsage).blockers.includes("usage_evidence_context_pack_mismatch"));
222
+
223
+ const incomplete = technology.compileContext(repo, first.traversal_receipt, selection(first.traversal_receipt));
224
+ check("incomplete_closure_never_ready", incomplete.status !== "ready");
225
+ const tinyBudget = technology.compileContext(repo, fourth.traversal_receipt, selection(fourth.traversal_receipt), { contextBudget: 100 });
226
+ check("budget_never_drops_required_sources", tinyBudget.status === "blocked" && tinyBudget.context_pack.required_closure.object_ids.length === compiled.context_pack.required_closure.object_ids.length);
227
+
228
+ const mutations = {
229
+ required_cycle(objects, relations) { relations.push(relation("relation.safety_cycle", "requires", "constraint.safety", "process.delivery")); },
230
+ stale(objects) { objects.find((item) => item.id === "constraint.safety").readiness = "stale"; },
231
+ blocked(objects) { objects.find((item) => item.id === "constraint.safety").readiness = "blocked"; },
232
+ deprecated(objects) { objects.find((item) => item.id === "constraint.safety").readiness = "deprecated"; },
233
+ conflict(objects, relations) { relations.push(relation("relation.process_conflict", "conflicts_with", "process.delivery", "constraint.safety")); },
234
+ policy_mismatch(objects) { objects.find((item) => item.id === "process.delivery").expansion_policy = "terminal"; },
235
+ };
236
+ for (const [name, mutate] of Object.entries(mutations)) {
237
+ const fixture = createFixture(root, name, "repository", mutate);
238
+ let receipt = technology.discoverContext(fixture, "build application delivery safely").traversal_receipt;
239
+ for (const ids of [["capability.delivery"], ["process.delivery"], ["resource.delivery_guide", "constraint.safety", "check.delivery"], ["resource.shared"]]) receipt = technology.expandContext(fixture, receipt, ids).traversal_receipt;
240
+ const outcome = technology.compileContext(fixture, receipt, selection(receipt));
241
+ check(`${name}_blocks_ready`, outcome.status !== "ready", outcome.blockers);
242
+ }
243
+
244
+ const tamperedReceipt = JSON.parse(JSON.stringify(fourth.traversal_receipt)); tamperedReceipt.graph.digest = `sha256:${"1".repeat(64)}`;
245
+ check("tampered_receipt_is_blocked", technology.expandContext(repo, tamperedReceipt, ["resource.shared"]).status === "blocked");
246
+ const tamperedPack = JSON.parse(JSON.stringify(compiled.context_pack)); tamperedPack.validators = [];
247
+ check("tampered_pack_is_blocked", technology.verifyContext(repo, tamperedPack, usage).status === "blocked");
248
+ const secretUsage = [{ ...usage[0], decision: `Bearer ${"github"}_${"pat"}_not-a-real-token-123456789` }];
249
+ check("secret_like_usage_is_blocked", technology.verifyContext(repo, compiled.context_pack, secretUsage).blockers.includes("usage_evidence_contains_sensitive_data"));
250
+ fs.appendFileSync(path.join(repo, "docs/delivery.md"), "tampered\n");
251
+ check("changed_source_digest_is_blocked", technology.verifyContext(repo, compiled.context_pack, usage).blockers.includes("context_pack_source_revision_or_digest_mismatch"));
252
+ git(repo, "checkout", "--", "docs/delivery.md");
253
+
254
+ const unmatched = technology.discoverContext(repo, "zzzz semantic request without lexical overlap", { maxCandidates: 2 });
255
+ check("unmatched_task_still_returns_bounded_choices", unmatched.status === "success" && unmatched.traversal_receipt.candidates.length > 0 && unmatched.traversal_receipt.warnings.includes("no_lexical_match_candidates_unranked"));
256
+
257
+ const privateFixture = createFixture(root, "private-source", "repository", (objects) => {
258
+ objects.find((item) => item.id === "resource.delivery_guide").source_refs.push("source/private/customer.txt");
259
+ });
260
+ fs.mkdirSync(path.join(privateFixture, "source/private"), { recursive: true });
261
+ fs.writeFileSync(path.join(privateFixture, "source/private/customer.txt"), "private customer content must not escape\n");
262
+ let privateReceipt = technology.discoverContext(privateFixture, "build application delivery safely").traversal_receipt;
263
+ for (const ids of [["capability.delivery"], ["process.delivery"], ["resource.delivery_guide", "constraint.safety", "check.delivery"], ["resource.shared"]]) privateReceipt = technology.expandContext(privateFixture, privateReceipt, ids).traversal_receipt;
264
+ const privateText = JSON.stringify(privateReceipt);
265
+ check("private_source_content_never_escapes", !privateText.includes("customer.txt") && !privateText.includes("private customer content"));
266
+ const privateCompile = technology.compileContext(privateFixture, privateReceipt, selection(privateReceipt));
267
+ check("unsafe_private_reference_fails_closed", privateCompile.status === "blocked" && privateCompile.blockers.includes("unsafe_source_reference_omitted"), privateCompile.blockers);
268
+
269
+ const receiptFile = path.join(root, "receipt.json");
270
+ const selectionFile = path.join(root, "selection.json");
271
+ const packetFile = path.join(root, "packet.json");
272
+ const evidenceFile = path.join(root, "evidence.json");
273
+ writeJson(receiptFile, fourth.traversal_receipt); writeJson(selectionFile, selection(fourth.traversal_receipt));
274
+ writeJson(packetFile, compiled.context_pack); writeJson(evidenceFile, usage);
275
+ const cliDiscover = runCli(["context", repo, "--phase", "discover", "--task", "build application delivery safely"]);
276
+ check("cli_discover_uses_public_protocol", cliDiscover.status === 0 && cliDiscover.output?.operation_id.endsWith(".discover"), cliDiscover);
277
+ const cliExpand = runCli(["context", repo, "--phase", "expand", "--input", receiptFile, "--select", "resource.shared"]);
278
+ check("cli_expand_uses_public_protocol", cliExpand.status === 0 && cliExpand.output?.operation_id.endsWith(".expand"), cliExpand);
279
+ const cliCompile = runCli(["context", repo, "--phase", "compile", "--input", receiptFile, "--selection", selectionFile]);
280
+ check("cli_compile_uses_public_protocol", cliCompile.status === 0 && cliCompile.output?.status === "ready", cliCompile);
281
+ const cliVerify = runCli(["context", repo, "--phase", "verify", "--packet", packetFile, "--evidence", evidenceFile]);
282
+ check("cli_verify_uses_public_protocol", cliVerify.status === 0 && cliVerify.output?.status === "success", cliVerify);
283
+
284
+ const legacy = technology.context(repo, { task: "build application delivery safely" });
285
+ check("legacy_context_command_still_works", legacy.operation_id === "mirai.project_technology.context" && legacy.operation_mode === "read_only");
286
+ check("all_context_operations_are_zero_write", git(repo, "status", "--porcelain=v1") === before);
287
+
288
+ process.stdout.write(`${JSON.stringify({ status: "success", checks_passed: checks.length, checks_failed: 0, checks }, null, 2)}\n`);
289
+ } catch (error) {
290
+ process.stdout.write(`${JSON.stringify({ status: "fail", checks_passed: checks.filter((item) => item.passed).length, checks_failed: 1, checks, error: error.message }, null, 2)}\n`);
291
+ process.exitCode = 1;
292
+ }
@@ -24,7 +24,17 @@ const coreObjectKinds = new Set([
24
24
  "evidence",
25
25
  "context_pack",
26
26
  "governance_gate",
27
- "profile"
27
+ "profile",
28
+ "owner",
29
+ "role",
30
+ "capability",
31
+ "process",
32
+ "resource",
33
+ "source",
34
+ "check",
35
+ "gate",
36
+ "constraint",
37
+ "access_requirement"
28
38
  ]);
29
39
 
30
40
  const coreRelationTypes = new Set([
@@ -40,7 +50,16 @@ const coreRelationTypes = new Set([
40
50
  "derived_from",
41
51
  "supersedes",
42
52
  "belongs_to",
43
- "related_to"
53
+ "related_to",
54
+ "contains",
55
+ "specializes",
56
+ "requires",
57
+ "governed_by",
58
+ "validated_by",
59
+ "documented_by",
60
+ "implemented_by",
61
+ "hands_off_to",
62
+ "conflicts_with"
44
63
  ]);
45
64
 
46
65
  const conformanceLevels = new Set([