mirai-graph 1.1.0 → 1.2.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/.github/workflows/ci.yml +3 -0
- package/CHANGELOG.md +48 -0
- package/CITATION.cff +2 -2
- package/README.md +15 -3
- package/package.json +3 -2
- package/packages/cli/graph-manifest.js +2 -1
- package/packages/cli/project-technology.js +10 -1
- package/packages/cli/validate-context-traversal.js +60 -0
- package/packages/cli/validate-project-continuity.js +173 -0
- package/packages/cli/validate-project-technology.js +26 -2
- package/packages/project-technology/context-traversal.js +108 -13
- package/packages/project-technology/continuity.js +414 -0
- package/packages/project-technology/index.js +140 -36
- package/profiles/implementation-control/profile.json +3 -1
- package/profiles/project-management/profile.json +8 -1
- package/releases/1.1.1.md +25 -0
- package/releases/1.2.0.md +19 -0
- package/releases/README.md +2 -0
- package/schemas/project-technology-extension.schema.json +2 -1
- package/standard/context-pack-generation.md +8 -2
- package/standard/object-model.md +6 -0
- package/standard/project-technology.md +35 -4
- package/standard/test-suite.md +4 -1
package/.github/workflows/ci.yml
CHANGED
|
@@ -26,6 +26,9 @@ jobs:
|
|
|
26
26
|
- name: Verify deterministic context traversal
|
|
27
27
|
run: node packages/cli/validate-context-traversal.js
|
|
28
28
|
|
|
29
|
+
- name: Verify portable project continuity
|
|
30
|
+
run: node packages/cli/validate-project-continuity.js
|
|
31
|
+
|
|
29
32
|
release-check:
|
|
30
33
|
runs-on: ubuntu-latest
|
|
31
34
|
|
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,54 @@ All notable changes to Mirai Graph will be documented in this file.
|
|
|
4
4
|
|
|
5
5
|
## [Unreleased]
|
|
6
6
|
|
|
7
|
+
## [1.2.0] - 2026-08-27
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- Portable project continuity in the existing Project Technology `sync`,
|
|
12
|
+
`context` and `verify` operations.
|
|
13
|
+
- Task-boundary evidence compaction into accepted facts, evidence, reusable
|
|
14
|
+
regression cases and bounded proposals in `graph/specs`.
|
|
15
|
+
- Host-local receipts keyed by graph identity, with lease, compare-and-swap,
|
|
16
|
+
backup, atomic write, readback and rollback support for shared non-Git folders.
|
|
17
|
+
- Automatic discovery of relevant previous cases through the existing context
|
|
18
|
+
traversal protocol.
|
|
19
|
+
|
|
20
|
+
### Safety Boundary
|
|
21
|
+
|
|
22
|
+
- Chat transcripts, secrets, private source, user paths and unverified claims
|
|
23
|
+
are not portable project memory.
|
|
24
|
+
- New goals, architecture changes and ownership changes remain proposals.
|
|
25
|
+
- Generated human context is a projection only and never authorizes writes.
|
|
26
|
+
- The public `graph.json` schema and Project Technology activation contract
|
|
27
|
+
remain `2.0.0` and `1.0.0` respectively.
|
|
28
|
+
|
|
29
|
+
### Verification
|
|
30
|
+
|
|
31
|
+
- New fixtures cover Git and non-Git projects, two-installation portability,
|
|
32
|
+
idempotence, stale receipts, concurrent-write protection, secret rejection,
|
|
33
|
+
proposal boundaries and two-case method promotion.
|
|
34
|
+
|
|
35
|
+
## [1.1.1] - 2026-08-26
|
|
36
|
+
|
|
37
|
+
### Fixed
|
|
38
|
+
|
|
39
|
+
- Sequential context traversal now reads the accepted legacy aliases used by
|
|
40
|
+
existing `graph.json` 2.0.0 repositories (`type`, `from`, `to`,
|
|
41
|
+
`relation_type` and `status`) without requiring a graph rewrite.
|
|
42
|
+
- Established structural and required relation names are projected onto the
|
|
43
|
+
public traversal vocabulary while preserving their direction and mandatory
|
|
44
|
+
closure semantics.
|
|
45
|
+
- Existing accepted Mirai readiness levels and revision-bound objects without
|
|
46
|
+
an explicit historical readiness field remain usable; draft, seed, gap,
|
|
47
|
+
blocked, stale and deprecated states still fail closed.
|
|
48
|
+
|
|
49
|
+
### Compatibility
|
|
50
|
+
|
|
51
|
+
- This patch makes the 1.1.0 compatibility promise executable. It does not
|
|
52
|
+
change schema `2.0.0`, the Project Technology activation contract, provider
|
|
53
|
+
transport or write boundaries.
|
|
54
|
+
|
|
7
55
|
## [1.1.0] - 2026-08-26
|
|
8
56
|
|
|
9
57
|
### Added
|
package/CITATION.cff
CHANGED
package/README.md
CHANGED
|
@@ -10,7 +10,7 @@ Use it when a software project has useful knowledge spread across README files,
|
|
|
10
10
|
docs, issues, chats, code comments and AI prompts, and you need a small
|
|
11
11
|
machine-readable map that developers and AI assistants can validate and reuse.
|
|
12
12
|
|
|
13
|
-
Status: stable public `1.
|
|
13
|
+
Status: stable public `1.2.0` release.
|
|
14
14
|
|
|
15
15
|
## What Problem Does It Solve?
|
|
16
16
|
|
|
@@ -42,7 +42,7 @@ Start with a normal repository and use Mirai Graph to:
|
|
|
42
42
|
|
|
43
43
|
## Current Installation Status
|
|
44
44
|
|
|
45
|
-
The repository
|
|
45
|
+
The repository provides the stable `mirai-graph@1.2.0` package. To verify a
|
|
46
46
|
repository checkout directly, use:
|
|
47
47
|
|
|
48
48
|
```bash
|
|
@@ -96,7 +96,8 @@ Enable it in the existing root manifest:
|
|
|
96
96
|
"contract_version": "1.0.0",
|
|
97
97
|
"enabled": true,
|
|
98
98
|
"context_policy": "task_scoped",
|
|
99
|
-
"source_boundary": "hybrid_sot"
|
|
99
|
+
"source_boundary": "hybrid_sot",
|
|
100
|
+
"continuity_policy": "task_boundary"
|
|
100
101
|
}
|
|
101
102
|
}
|
|
102
103
|
}
|
|
@@ -130,6 +131,17 @@ The programmatic API exposes the same operations as `discoverContext`,
|
|
|
130
131
|
`expandContext`, `compileContext` and `verifyContext`. Mirai Graph does not call
|
|
131
132
|
or depend on a particular AI model.
|
|
132
133
|
|
|
134
|
+
Verified project experience can be saved without storing a chat transcript:
|
|
135
|
+
|
|
136
|
+
```bash
|
|
137
|
+
mirai-graph technology sync . --boundary task_complete --evidence continuity-evidence.json
|
|
138
|
+
mirai-graph technology sync . --boundary task_complete --evidence continuity-evidence.json --apply
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
Accepted facts and reusable cases live in `graph/specs`; mutable receipts and
|
|
142
|
+
rollback data stay host-local. The same contract works in Git repositories and
|
|
143
|
+
ordinary shared folders.
|
|
144
|
+
|
|
133
145
|
Changing operations return a preview unless `--apply` is explicit. See
|
|
134
146
|
[Project Technology](standard/project-technology.md) for provider/consumer
|
|
135
147
|
binding, local state and safety boundaries.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mirai-graph",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "An evolutionary graph operating model for managing the growth of complex systems.",
|
|
6
6
|
"license": "MIT",
|
|
@@ -13,9 +13,10 @@
|
|
|
13
13
|
"./project-technology": "./packages/project-technology/index.js"
|
|
14
14
|
},
|
|
15
15
|
"scripts": {
|
|
16
|
-
"pretest": "npm run test:project-technology && npm run test:context-traversal && npm run validate:project-technology-schemas",
|
|
16
|
+
"pretest": "npm run test:project-technology && npm run test:context-traversal && npm run test:project-continuity && npm run validate:project-technology-schemas",
|
|
17
17
|
"test:project-technology": "node packages/cli/validate-project-technology.js",
|
|
18
18
|
"test:context-traversal": "node packages/cli/validate-context-traversal.js",
|
|
19
|
+
"test:project-continuity": "node packages/cli/validate-project-continuity.js",
|
|
19
20
|
"validate:project-technology-schemas": "node -e \"const fs=require('fs'); for (const file of ['project-technology-extension.schema.json','project-technology-target-export.schema.json','context-traversal-receipt.schema.json','context-pack.schema.json']) JSON.parse(fs.readFileSync('schemas/'+file))\"",
|
|
20
21
|
"validate:graph-manifest": "node packages/cli/validate-graph-manifest-migration.js",
|
|
21
22
|
"validate:minimal": "node packages/cli/validate-mirai-graph.js examples/minimal-graph",
|
|
@@ -130,7 +130,8 @@ function validateManifest(manifest, repo = null) {
|
|
|
130
130
|
};
|
|
131
131
|
for (const field of Object.keys(expected)) if (technology[field] !== expected[field]) errors.push(`extensions.mirai.project_technology.${field} is invalid`);
|
|
132
132
|
if (typeof technology.enabled !== "boolean") errors.push("extensions.mirai.project_technology.enabled is invalid");
|
|
133
|
-
|
|
133
|
+
if (technology.continuity_policy !== undefined && technology.continuity_policy !== "task_boundary") errors.push("extensions.mirai.project_technology.continuity_policy is invalid");
|
|
134
|
+
for (const field of Object.keys(technology)) if (!(field in expected) && !["enabled", "continuity_policy"].includes(field)) errors.push(`extensions.mirai.project_technology has unknown field ${field}`);
|
|
134
135
|
}
|
|
135
136
|
}
|
|
136
137
|
if (manifest.graph !== null) {
|
|
@@ -13,6 +13,8 @@ function usage() {
|
|
|
13
13
|
console.error("Options: --apply --task <text> --source <file> --target-id <id>");
|
|
14
14
|
console.error(" --semantic-digest <sha256:...> --provider-revision <sha>");
|
|
15
15
|
console.error(" --refresh-binding --significant-work --max-objects <count>");
|
|
16
|
+
console.error("Continuity: sync --boundary task_start|stage_complete|task_complete [--evidence <json>] [--expected-graph-digest <sha256:...>]");
|
|
17
|
+
console.error(" verify --significant-work [--receipt-digest <sha256:...>]");
|
|
16
18
|
console.error("Context: --phase discover --task <text>");
|
|
17
19
|
console.error(" --phase expand --input <receipt.json> --select <id> [--select <id>]");
|
|
18
20
|
console.error(" --phase compile --input <receipt.json> --selection <selection.json>");
|
|
@@ -25,7 +27,7 @@ function parse(argv) {
|
|
|
25
27
|
let repository = ".";
|
|
26
28
|
if (argv[0] && !argv[0].startsWith("--")) repository = argv.shift();
|
|
27
29
|
const options = {};
|
|
28
|
-
const values = new Set(["--task", "--source", "--target-id", "--semantic-digest", "--provider-revision", "--max-objects", "--phase", "--input", "--select", "--selection", "--packet", "--evidence", "--selector", "--reason", "--confidence", "--context-budget"]);
|
|
30
|
+
const values = new Set(["--task", "--source", "--target-id", "--semantic-digest", "--provider-revision", "--max-objects", "--phase", "--input", "--select", "--selection", "--packet", "--evidence", "--selector", "--reason", "--confidence", "--context-budget", "--boundary", "--expected-graph-digest", "--receipt-digest", "--state-root"]);
|
|
29
31
|
const flags = new Set(["--apply", "--refresh-binding", "--significant-work"]);
|
|
30
32
|
const names = {
|
|
31
33
|
"--task": "task", "--source": "source", "--target-id": "targetId",
|
|
@@ -36,6 +38,8 @@ function parse(argv) {
|
|
|
36
38
|
"--selection": "selectionFile", "--packet": "packetFile", "--evidence": "evidenceFile",
|
|
37
39
|
"--selector": "selector", "--reason": "reason", "--confidence": "confidence",
|
|
38
40
|
"--context-budget": "contextBudget",
|
|
41
|
+
"--boundary": "boundary", "--expected-graph-digest": "expectedGraphDigest",
|
|
42
|
+
"--receipt-digest": "receiptDigest", "--state-root": "stateRoot",
|
|
39
43
|
};
|
|
40
44
|
while (argv.length) {
|
|
41
45
|
const token = argv.shift();
|
|
@@ -57,6 +61,11 @@ function readJsonFile(file) {
|
|
|
57
61
|
}
|
|
58
62
|
|
|
59
63
|
function hydrateContextOptions(request) {
|
|
64
|
+
if (request.operation === "sync" && request.options.boundary) {
|
|
65
|
+
if (request.options.boundary !== "task_start" && !request.options.evidenceFile) throw new Error(`${request.options.boundary} requires --evidence`);
|
|
66
|
+
if (request.options.evidenceFile) request.options.continuityEvidence = readJsonFile(request.options.evidenceFile);
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
60
69
|
if (request.operation !== "context" || !request.options.phase) return;
|
|
61
70
|
const phase = request.options.phase;
|
|
62
71
|
if (!new Set(["discover", "expand", "compile", "verify"]).has(phase)) throw new Error(`unsupported context phase ${phase}`);
|
|
@@ -73,6 +73,44 @@ function createFixture(root, name, scope = "repository", mutation = null) {
|
|
|
73
73
|
return repo;
|
|
74
74
|
}
|
|
75
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
|
+
|
|
76
114
|
function selection(receipt) {
|
|
77
115
|
return {
|
|
78
116
|
selector: "ai", task_digest: receipt.task.digest, graph_digest: receipt.graph.digest,
|
|
@@ -110,6 +148,28 @@ try {
|
|
|
110
148
|
check(`universal_scope_${scope}`, discovered.status === "success" && discovered.traversal_receipt.repository_id === name, discovered.blockers);
|
|
111
149
|
}
|
|
112
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
|
+
|
|
113
173
|
const before = git(repo, "status", "--porcelain=v1");
|
|
114
174
|
const discovered = technology.discoverContext(repo, "build application delivery safely", { maxCandidates: 2 });
|
|
115
175
|
check("discover_is_bounded", discovered.status === "success" && discovered.traversal_receipt.candidates.length <= 2, discovered);
|
|
@@ -0,0 +1,173 @@
|
|
|
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
|
+
|
|
12
|
+
function writeJson(file, value) {
|
|
13
|
+
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
14
|
+
fs.writeFileSync(file, `${JSON.stringify(value, null, 2)}\n`);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function git(repo, ...args) {
|
|
18
|
+
const run = spawnSync("git", args, { cwd: repo, encoding: "utf8" });
|
|
19
|
+
if (run.status !== 0) throw new Error(run.stderr || `git ${args.join(" ")} failed`);
|
|
20
|
+
return run.stdout.trim();
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function manifest(id) {
|
|
24
|
+
return {
|
|
25
|
+
$schema: "https://mirai-graph.dev/schemas/graph-manifest.schema.json",
|
|
26
|
+
format: "mirai-graph", schema_version: "2.0.0", id, aliases: [], title: id,
|
|
27
|
+
scope: "repository", kind: "repository_graph", owner: "fixture-owner",
|
|
28
|
+
profiles: ["project_management", "implementation_control"],
|
|
29
|
+
graph: {
|
|
30
|
+
root: "graph", source_of_truth: ["graph/specs"], objects: ["graph/specs/objects.json"],
|
|
31
|
+
relations: ["graph/specs/relations.json"], schemas: [], generated: ["graph/generated"], raw_sources: ["README.md"],
|
|
32
|
+
},
|
|
33
|
+
imports: [],
|
|
34
|
+
extensions: { "mirai.project_technology": technology.extensionContract() },
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function createRepo(root, name, withGit = true) {
|
|
39
|
+
const repo = path.join(root, name); fs.mkdirSync(repo, { recursive: true });
|
|
40
|
+
writeJson(path.join(repo, "graph.json"), manifest(name));
|
|
41
|
+
writeJson(path.join(repo, "graph/specs/objects.json"), [{
|
|
42
|
+
id: "goal.fixture.delivery", kind: "goal", title: "Deliver fixture", summary: "Complete a safe fixture task",
|
|
43
|
+
readiness: "accepted", profile: "project_management", source_refs: ["README.md"],
|
|
44
|
+
}]);
|
|
45
|
+
writeJson(path.join(repo, "graph/specs/relations.json"), []);
|
|
46
|
+
fs.writeFileSync(path.join(repo, "README.md"), "# Fixture\nPortable safe context.\n");
|
|
47
|
+
if (withGit) {
|
|
48
|
+
git(repo, "init", "-q"); git(repo, "config", "user.name", "Mirai Fixture"); git(repo, "config", "user.email", "fixture@example.invalid");
|
|
49
|
+
git(repo, "add", "."); git(repo, "commit", "-qm", "initial graph");
|
|
50
|
+
}
|
|
51
|
+
return repo;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function evidence(task, options = {}) {
|
|
55
|
+
return {
|
|
56
|
+
task_digest: technology.sha256(task, true),
|
|
57
|
+
outcome: options.outcome || `Verified result for ${task}`,
|
|
58
|
+
requirement_refs: options.requirement_refs || ["requirement.fixture.portability"],
|
|
59
|
+
evidence_refs: ["repo://fixture/checks/continuity"],
|
|
60
|
+
checks: [{ id: "check.fixture.continuity", verdict: "pass", evidence_ref: "repo://fixture/checks/continuity" }],
|
|
61
|
+
changed_surfaces: ["src/fixture.js"],
|
|
62
|
+
case_signature: options.case_signature || "case.fixture.portable-work",
|
|
63
|
+
method_candidate: options.method_candidate || null,
|
|
64
|
+
decisions: options.decisions || [],
|
|
65
|
+
source_revision: options.source_revision || null,
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const checks = [];
|
|
70
|
+
function check(id, condition, details = null) {
|
|
71
|
+
checks.push({ id, passed: Boolean(condition), details });
|
|
72
|
+
assert.ok(condition, `${id}: ${JSON.stringify(details)}`);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const root = fs.mkdtempSync(path.join(os.tmpdir(), "mirai-continuity-"));
|
|
76
|
+
const stateRoot = path.join(root, "host-state");
|
|
77
|
+
try {
|
|
78
|
+
const repo = createRepo(root, "git-project");
|
|
79
|
+
const before = git(repo, "status", "--porcelain=v1");
|
|
80
|
+
const taskStart = technology.execute("sync", repo, { apply: true, boundary: "task_start", stateRoot });
|
|
81
|
+
check("task_start_is_read_only", taskStart.status === "success" && taskStart.changed === false && git(repo, "status", "--porcelain=v1") === before, taskStart);
|
|
82
|
+
const preview = technology.execute("sync", repo, { apply: false, boundary: "task_complete", continuityEvidence: evidence("preview"), stateRoot });
|
|
83
|
+
check("preview_is_zero_write", preview.status === "preview" && git(repo, "status", "--porcelain=v1") === before, preview);
|
|
84
|
+
const missingReceiptVerify = technology.verify(repo, { stateRoot });
|
|
85
|
+
check("configured_continuity_without_receipt_blocks_verify", missingReceiptVerify.status === "blocked" && missingReceiptVerify.blockers.includes("continuity_missing"), missingReceiptVerify);
|
|
86
|
+
|
|
87
|
+
const first = technology.execute("sync", repo, { apply: true, boundary: "stage_complete", continuityEvidence: evidence("first", { method_candidate: "Use the verified bounded fixture path." }), stateRoot });
|
|
88
|
+
check("verified_result_is_saved", first.status === "success" && first.changed && first.continuity.saved_refs.length === 2, first);
|
|
89
|
+
check("portable_authority_is_graph_specs", fs.existsSync(path.join(repo, "graph/specs/project-continuity.json")) && first.continuity.authority === "graph/specs");
|
|
90
|
+
check("host_state_is_outside_project", !fs.existsSync(path.join(repo, ".mirai-graph/project-technology/receipts")) && fs.existsSync(stateRoot));
|
|
91
|
+
check("human_projection_is_generated", fs.readFileSync(path.join(repo, "graph/docs/project-context.md"), "utf8").includes("not a source of truth"));
|
|
92
|
+
const objectsAfterFirst = JSON.parse(fs.readFileSync(path.join(repo, "graph/specs/project-continuity.json"), "utf8")).objects;
|
|
93
|
+
check("first_method_is_proposal", objectsAfterFirst.some((item) => item.kind === "lesson" && item.readiness === "proposal"));
|
|
94
|
+
check("architecture_decision_is_not_accepted", technology.execute("sync", repo, {
|
|
95
|
+
apply: true, boundary: "task_complete", stateRoot,
|
|
96
|
+
continuityEvidence: evidence("decision", { decisions: [{ summary: "Replace the project architecture", changes_architecture: true }] }),
|
|
97
|
+
}).status === "success");
|
|
98
|
+
const decisionObjects = JSON.parse(fs.readFileSync(path.join(repo, "graph/specs/project-continuity.json"), "utf8")).objects;
|
|
99
|
+
check("architecture_change_remains_proposal", decisionObjects.some((item) => item.kind === "decision" && item.summary === "Replace the project architecture" && item.readiness === "proposal"));
|
|
100
|
+
|
|
101
|
+
const second = technology.execute("sync", repo, { apply: true, boundary: "task_complete", continuityEvidence: evidence("second", { method_candidate: "Use the verified bounded fixture path." }), stateRoot });
|
|
102
|
+
check("second_independent_case_is_saved", second.status === "success", second);
|
|
103
|
+
const objectsAfterSecond = JSON.parse(fs.readFileSync(path.join(repo, "graph/specs/project-continuity.json"), "utf8")).objects;
|
|
104
|
+
check("method_promotes_after_two_independent_cases", objectsAfterSecond.some((item) => item.kind === "lesson" && item.readiness === "accepted" && item.supporting_case_ids.length >= 2));
|
|
105
|
+
|
|
106
|
+
const beforeRepeat = fs.readFileSync(path.join(repo, "graph/specs/project-continuity.json"));
|
|
107
|
+
const repeated = technology.execute("sync", repo, { apply: true, boundary: "task_complete", continuityEvidence: evidence("second", { method_candidate: "Use the verified bounded fixture path." }), stateRoot });
|
|
108
|
+
check("repeat_is_byte_identical", repeated.status === "success" && repeated.changed === false && fs.readFileSync(path.join(repo, "graph/specs/project-continuity.json")).equals(beforeRepeat), repeated);
|
|
109
|
+
check("fresh_continuity_verifies_without_target_for_light_work", technology.verify(repo, { stateRoot }).status === "success");
|
|
110
|
+
check("fresh_receipt_allows_significant_work", technology.continuity.verify(repo, JSON.parse(fs.readFileSync(path.join(repo, "graph.json"), "utf8")), { stateRoot }).status === "success");
|
|
111
|
+
|
|
112
|
+
const staleDigest = technology.continuity.graphDigest(repo);
|
|
113
|
+
fs.appendFileSync(path.join(repo, "graph/specs/objects.json"), " ");
|
|
114
|
+
check("stale_continuity_blocks_significant_work", technology.continuity.verify(repo, JSON.parse(fs.readFileSync(path.join(repo, "graph.json"), "utf8")), { stateRoot }).blockers.includes("continuity_stale"));
|
|
115
|
+
const baseObjects = JSON.parse(fs.readFileSync(path.join(repo, "graph/specs/objects.json"), "utf8"));
|
|
116
|
+
baseObjects[0].summary = "Changed concurrently";
|
|
117
|
+
fs.writeFileSync(path.join(repo, "graph/specs/objects.json"), JSON.stringify(baseObjects, null, 2) + "\n");
|
|
118
|
+
const cas = technology.execute("sync", repo, { apply: true, boundary: "task_complete", continuityEvidence: evidence("cas"), expectedGraphDigest: staleDigest, stateRoot });
|
|
119
|
+
check("compare_and_swap_conflict_is_safe", cas.status === "blocked" && cas.blockers.includes("continuity_compare_and_swap_conflict"), cas);
|
|
120
|
+
baseObjects[0].summary = "Complete a safe fixture task";
|
|
121
|
+
fs.writeFileSync(path.join(repo, "graph/specs/objects.json"), JSON.stringify(baseObjects, null, 2) + "\n");
|
|
122
|
+
|
|
123
|
+
const secret = technology.execute("sync", repo, { apply: true, boundary: "task_complete", continuityEvidence: evidence("secret", { outcome: "token=not-a-real-secret-value-123456" }), stateRoot });
|
|
124
|
+
check("secret_is_rejected", secret.status === "blocked" && secret.blockers.includes("continuity_sensitive_or_host_data_forbidden"), secret);
|
|
125
|
+
const privatePath = technology.execute("sync", repo, { apply: true, boundary: "task_complete", continuityEvidence: { ...evidence("private"), changed_surfaces: ["source/memory/private.md"] }, stateRoot });
|
|
126
|
+
check("private_projection_is_rejected", privatePath.status === "blocked" && privatePath.blockers.includes("continuity_sensitive_or_host_data_forbidden"), privatePath);
|
|
127
|
+
const failedCheck = technology.execute("sync", repo, { apply: true, boundary: "task_complete", continuityEvidence: { ...evidence("fail"), checks: [{ id: "check.fixture", verdict: "fail", evidence_ref: "repo://fixture/fail" }] }, stateRoot });
|
|
128
|
+
check("unverified_result_is_rejected", failedCheck.status === "blocked" && failedCheck.blockers.includes("continuity_result_not_verified"), failedCheck);
|
|
129
|
+
|
|
130
|
+
git(repo, "add", "graph.json", "graph/specs", "graph/docs"); git(repo, "commit", "-qm", "portable continuity records");
|
|
131
|
+
const discovered = technology.context(repo, { phase: "discover", task: "use the verified portable work case" });
|
|
132
|
+
check("new_task_discovers_past_case", discovered.status === "success" && discovered.traversal_receipt.candidates.some((item) => item.id.startsWith("regression_case.continuity.")), discovered.blockers);
|
|
133
|
+
const beforeReadOnly = git(repo, "status", "--porcelain=v1");
|
|
134
|
+
technology.status(repo); technology.context(repo, { phase: "discover", task: "read only project context" });
|
|
135
|
+
check("read_only_operations_write_nothing", git(repo, "status", "--porcelain=v1") === beforeReadOnly);
|
|
136
|
+
|
|
137
|
+
const folder = createRepo(root, "shared-folder", false);
|
|
138
|
+
const folderFirst = technology.execute("sync", folder, { apply: true, boundary: "task_complete", continuityEvidence: evidence("folder"), stateRoot });
|
|
139
|
+
check("non_git_folder_supports_continuity", folderFirst.status === "success" && folderFirst.changed, folderFirst);
|
|
140
|
+
const folderRepeat = technology.execute("sync", folder, { apply: true, boundary: "task_complete", continuityEvidence: evidence("folder"), stateRoot });
|
|
141
|
+
check("non_git_repeat_is_idempotent", folderRepeat.status === "success" && folderRepeat.changed === false, folderRepeat);
|
|
142
|
+
const sharedLease = path.join(folder, "graph/.project-technology-continuity.lock");
|
|
143
|
+
fs.writeFileSync(sharedLease, "occupied\n");
|
|
144
|
+
const leaseConflict = technology.execute("sync", folder, { apply: true, boundary: "task_complete", continuityEvidence: evidence("parallel"), stateRoot: path.join(root, "third-installation-state") });
|
|
145
|
+
check("shared_folder_lease_blocks_parallel_writer", leaseConflict.status === "blocked" && leaseConflict.blockers.includes("continuity_lease_conflict"), leaseConflict);
|
|
146
|
+
fs.unlinkSync(sharedLease);
|
|
147
|
+
const folderDiscovery = technology.context(folder, { phase: "discover", task: "portable fixture case" });
|
|
148
|
+
check("non_git_context_is_available", folderDiscovery.status === "success", folderDiscovery.blockers);
|
|
149
|
+
check("same_content_has_stable_digest", technology.continuity.graphDigest(folder) === technology.continuity.graphDigest(folder));
|
|
150
|
+
|
|
151
|
+
const otherStateRoot = path.join(root, "other-installation-state");
|
|
152
|
+
const firstInstallContext = technology.context(repo, { phase: "discover", task: "use verified portable work" });
|
|
153
|
+
const secondInstallContext = technology.context(repo, { phase: "discover", task: "use verified portable work", stateRoot: otherStateRoot });
|
|
154
|
+
check("two_installations_get_same_context", firstInstallContext.traversal_receipt.traversal_digest === secondInstallContext.traversal_receipt.traversal_digest);
|
|
155
|
+
check("portable_specs_contain_no_host_paths", !/(?:\/Users\/|\/home\/|[A-Za-z]:\\Users\\)/.test(fs.readFileSync(path.join(repo, "graph/specs/project-continuity.json"), "utf8")));
|
|
156
|
+
check("continuity_policy_keeps_contract_version", JSON.parse(fs.readFileSync(path.join(repo, "graph.json"), "utf8")).extensions["mirai.project_technology"].contract_version === "1.0.0");
|
|
157
|
+
|
|
158
|
+
const bootstrapRepo = path.join(root, "existing-code-new-graph");
|
|
159
|
+
fs.mkdirSync(bootstrapRepo, { recursive: true });
|
|
160
|
+
fs.writeFileSync(path.join(bootstrapRepo, "README.md"), "# Existing code\n");
|
|
161
|
+
git(bootstrapRepo, "init", "-q"); git(bootstrapRepo, "config", "user.name", "Mirai Fixture"); git(bootstrapRepo, "config", "user.email", "fixture@example.invalid");
|
|
162
|
+
git(bootstrapRepo, "add", "README.md"); git(bootstrapRepo, "commit", "-qm", "existing code");
|
|
163
|
+
writeJson(path.join(bootstrapRepo, "graph.json"), manifest("bootstrap-project"));
|
|
164
|
+
writeJson(path.join(bootstrapRepo, "graph/specs/objects.json"), [{ id: "goal.bootstrap", kind: "goal", title: "Bootstrap", summary: "Use content-bound bootstrap context", readiness: "accepted", profile: "project_management" }]);
|
|
165
|
+
writeJson(path.join(bootstrapRepo, "graph/specs/relations.json"), []);
|
|
166
|
+
const bootstrapContext = technology.context(bootstrapRepo, { phase: "discover", task: "bootstrap context" });
|
|
167
|
+
check("new_git_graph_is_content_bound_before_first_commit", bootstrapContext.status === "success" && String(bootstrapContext.traversal_receipt.graph.revision).startsWith("content:"), bootstrapContext.blockers);
|
|
168
|
+
|
|
169
|
+
process.stdout.write(`${JSON.stringify({ status: "success", checks_passed: checks.length, checks_failed: 0, checks }, null, 2)}\n`);
|
|
170
|
+
} catch (error) {
|
|
171
|
+
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`);
|
|
172
|
+
process.exitCode = 1;
|
|
173
|
+
}
|
|
@@ -72,6 +72,18 @@ function executionContract() {
|
|
|
72
72
|
};
|
|
73
73
|
}
|
|
74
74
|
|
|
75
|
+
function continuityEvidence(repositoryId) {
|
|
76
|
+
return {
|
|
77
|
+
task_digest: technology.sha256(`task:${repositoryId}`, true),
|
|
78
|
+
outcome: `Exact target binding verified for ${repositoryId}`,
|
|
79
|
+
requirement_refs: ["requirement.fixture.runtime"],
|
|
80
|
+
evidence_refs: ["repo://fixture/evidence/project-technology"],
|
|
81
|
+
checks: [{ id: "check.fixture.binding", verdict: "pass", evidence_ref: "repo://fixture/evidence/project-technology" }],
|
|
82
|
+
changed_surfaces: ["graph/specs/project-continuity.json"],
|
|
83
|
+
case_signature: "case.fixture.target-binding",
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
|
|
75
87
|
function initRepo(root, name, scope = "repository", profiles) {
|
|
76
88
|
const repo = path.join(root, name); fs.mkdirSync(repo, { recursive: true });
|
|
77
89
|
git(repo, "init", "-q"); git(repo, "config", "user.name", "Mirai Fixture");
|
|
@@ -104,6 +116,7 @@ function check(id, condition, details = null) {
|
|
|
104
116
|
}
|
|
105
117
|
|
|
106
118
|
const root = fs.mkdtempSync(path.join(os.tmpdir(), "mirai-project-technology-test-"));
|
|
119
|
+
process.env.MIRAI_GRAPH_STATE_ROOT = path.join(root, "host-state");
|
|
107
120
|
try {
|
|
108
121
|
const provider = initRepo(root, "fixture-provider", "repository");
|
|
109
122
|
const consumer = initRepo(root, "fixture-consumer", "repository");
|
|
@@ -112,6 +125,11 @@ try {
|
|
|
112
125
|
const federation = initRepo(root, "fixture-federation", "federation", ["project_management", "implementation_control"]);
|
|
113
126
|
const providerRevision = addTarget(provider);
|
|
114
127
|
|
|
128
|
+
const legacyRuntimeRepo = initRepo(root, "legacy-runtime-repository");
|
|
129
|
+
writeJson(path.join(legacyRuntimeRepo, ".mirai-graph/project-technology/inventory.json"), { schema_version: "1.0.0", repository_id: "legacy-runtime-repository", inventory_digest: `sha256:${"0".repeat(64)}`, files: [] });
|
|
130
|
+
const legacyRuntimeEnabled = technology.execute("enable", legacyRuntimeRepo, { apply: true });
|
|
131
|
+
check("project_local_runtime_moves_to_host_state", legacyRuntimeEnabled.status === "success" && legacyRuntimeEnabled.migration_ref.startsWith("host-local://") && fs.existsSync(path.join(legacyRuntimeRepo, ".mirai-graph/project-technology/inventory.json")), legacyRuntimeEnabled);
|
|
132
|
+
|
|
115
133
|
const before = fs.readFileSync(path.join(consumer, "graph.json"));
|
|
116
134
|
const preview = technology.execute("enable", consumer, { apply: false });
|
|
117
135
|
check("preview_is_zero_write", preview.status === "preview" && fs.readFileSync(path.join(consumer, "graph.json")).equals(before));
|
|
@@ -147,12 +165,18 @@ try {
|
|
|
147
165
|
apply: true, source: exportPath, targetId: TARGET_ID,
|
|
148
166
|
semanticDigest: SEMANTIC_DIGEST, providerRevision,
|
|
149
167
|
});
|
|
150
|
-
|
|
168
|
+
const consumerContinuity = technology.execute("sync", consumer, {
|
|
169
|
+
apply: true, boundary: "task_complete", continuityEvidence: continuityEvidence("fixture-consumer"),
|
|
170
|
+
});
|
|
171
|
+
check("consumer_binding_ready", connected.status === "success" && consumerContinuity.status === "success" && technology.verify(consumer, { significantWork: true }).status === "success", { connect: connected.blockers, continuity: consumerContinuity.blockers });
|
|
151
172
|
const connectedTwo = technology.execute("connect", consumerTwo, {
|
|
152
173
|
apply: true, source: exportPath, targetId: TARGET_ID,
|
|
153
174
|
semanticDigest: SEMANTIC_DIGEST, providerRevision,
|
|
154
175
|
});
|
|
155
|
-
|
|
176
|
+
const consumerTwoContinuity = technology.execute("sync", consumerTwo, {
|
|
177
|
+
apply: true, boundary: "task_complete", continuityEvidence: continuityEvidence("fixture-consumer-two"),
|
|
178
|
+
});
|
|
179
|
+
check("second_consumer_binding_ready", connectedTwo.status === "success" && consumerTwoContinuity.status === "success" && technology.verify(consumerTwo, { significantWork: true }).status === "success", { connect: connectedTwo.blockers, continuity: consumerTwoContinuity.blockers });
|
|
156
180
|
const repeated = technology.execute("connect", consumer, {
|
|
157
181
|
apply: true, source: exportPath, targetId: TARGET_ID,
|
|
158
182
|
semanticDigest: SEMANTIC_DIGEST, providerRevision,
|