@mindflight/mindbrain-personal-studio 0.6.2 → 0.6.3

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 (202) hide show
  1. package/README.md +16 -8
  2. package/build/client/_app/immutable/chunks/{D0UIlUGZ.js → CIErFlYG.js} +1 -1
  3. package/build/client/_app/immutable/chunks/CIErFlYG.js.br +0 -0
  4. package/build/client/_app/immutable/chunks/CIErFlYG.js.gz +0 -0
  5. package/build/client/_app/immutable/entry/{app.CR-imLox.js → app.mURYm8o_.js} +2 -2
  6. package/build/client/_app/immutable/entry/app.mURYm8o_.js.br +0 -0
  7. package/build/client/_app/immutable/entry/app.mURYm8o_.js.gz +0 -0
  8. package/build/client/_app/immutable/entry/start.D4M9ZeGO.js +1 -0
  9. package/build/client/_app/immutable/entry/start.D4M9ZeGO.js.br +2 -0
  10. package/build/client/_app/immutable/entry/start.D4M9ZeGO.js.gz +0 -0
  11. package/build/client/_app/immutable/nodes/{1.CTEedoSY.js → 1.DHKtMeFI.js} +1 -1
  12. package/build/client/_app/immutable/nodes/1.DHKtMeFI.js.br +1 -0
  13. package/build/client/_app/immutable/nodes/1.DHKtMeFI.js.gz +0 -0
  14. package/build/client/_app/version.json +1 -1
  15. package/build/client/_app/version.json.br +0 -0
  16. package/build/client/_app/version.json.gz +0 -0
  17. package/build/handler.js +4 -4
  18. package/build/index.js +4 -4
  19. package/build/server/chunks/chunks/{internal.js-WOmQXGMa.js → internal.js-C3tV0XXj.js} +2 -2
  20. package/build/server/chunks/chunks/{internal.js-WOmQXGMa.js.map → internal.js-C3tV0XXj.js.map} +1 -1
  21. package/build/server/chunks/{handler-BIDedSZq.js → handler-DlaCCnxx.js} +3 -3
  22. package/build/server/chunks/{handler-BIDedSZq.js.map → handler-DlaCCnxx.js.map} +1 -1
  23. package/build/server/chunks/{index.js-YVPJa0so.js → index.js-BGfKWHak.js} +2 -2
  24. package/build/server/chunks/{index.js-YVPJa0so.js.map → index.js-BGfKWHak.js.map} +1 -1
  25. package/build/server/chunks/{manifest.js-aGRKuiqF.js → manifest.js-CnmaNf5D.js} +3 -3
  26. package/build/server/chunks/{manifest.js-aGRKuiqF.js.map → manifest.js-CnmaNf5D.js.map} +1 -1
  27. package/build/server/chunks/nodes/{1.js-Dhh3ErZY.js → 1.js-BypjwBYB.js} +2 -2
  28. package/build/server/chunks/nodes/{1.js-Dhh3ErZY.js.map → 1.js-BypjwBYB.js.map} +1 -1
  29. package/examples/immeuble/ACCEPTANCE.yaml +82 -0
  30. package/examples/immeuble/BIM_LITE.md +15 -0
  31. package/examples/immeuble/CHECKLIST.md +128 -0
  32. package/examples/immeuble/README.md +121 -0
  33. package/examples/immeuble/bundle/immeuble.bundle.json +22786 -0
  34. package/examples/immeuble/contracts/answer_artifacts.seed.jsonl +34 -0
  35. package/examples/immeuble/contracts/business_capabilities.seed.jsonl +25 -0
  36. package/examples/immeuble/contracts/consumer_contract.yaml +131 -0
  37. package/examples/immeuble/contracts/immeuble_structured_import_model.json +310 -0
  38. package/examples/immeuble/contracts/mapping_external_to_canonical.json +375 -0
  39. package/examples/immeuble/contracts/mapping_external_to_canonical.yaml +55 -0
  40. package/examples/immeuble/contracts/mapping_external_to_canonical_ws.json +65 -0
  41. package/examples/immeuble/contracts/model_contract.json +943 -0
  42. package/examples/immeuble/contracts/projection_catalog.yaml +366 -0
  43. package/examples/immeuble/contracts/scenarios.yaml +27 -0
  44. package/examples/immeuble/contracts/semantic_proposal.golden.json +1 -0
  45. package/examples/immeuble/contracts/source_profile.yaml +38 -0
  46. package/examples/immeuble/fake_data/DeltaFinding.csv +20 -0
  47. package/examples/immeuble/fake_data/ProjectionResult.csv +13 -0
  48. package/examples/immeuble/fake_data/ag_meeting.csv +4 -0
  49. package/examples/immeuble/fake_data/agenda_item.csv +4 -0
  50. package/examples/immeuble/fake_data/architect.csv +3 -0
  51. package/examples/immeuble/fake_data/architecture_firm.csv +2 -0
  52. package/examples/immeuble/fake_data/bank_account.csv +3 -0
  53. package/examples/immeuble/fake_data/billing_group.csv +41 -0
  54. package/examples/immeuble/fake_data/block.csv +10 -0
  55. package/examples/immeuble/fake_data/budget_line.csv +3 -0
  56. package/examples/immeuble/fake_data/building.csv +6 -0
  57. package/examples/immeuble/fake_data/cellar.csv +41 -0
  58. package/examples/immeuble/fake_data/change_order.csv +2 -0
  59. package/examples/immeuble/fake_data/charge_call.csv +58 -0
  60. package/examples/immeuble/fake_data/claim.csv +4 -0
  61. package/examples/immeuble/fake_data/coda_entry.csv +49 -0
  62. package/examples/immeuble/fake_data/compliance_certificate.csv +10 -0
  63. package/examples/immeuble/fake_data/contractor.csv +4 -0
  64. package/examples/immeuble/fake_data/decision.csv +5 -0
  65. package/examples/immeuble/fake_data/defect_reserve.csv +3 -0
  66. package/examples/immeuble/fake_data/household.csv +41 -0
  67. package/examples/immeuble/fake_data/inspection.csv +3 -0
  68. package/examples/immeuble/fake_data/insurance_policy.csv +4 -0
  69. package/examples/immeuble/fake_data/intervention.csv +13 -0
  70. package/examples/immeuble/fake_data/invoice.csv +3 -0
  71. package/examples/immeuble/fake_data/lease_contract.csv +12 -0
  72. package/examples/immeuble/fake_data/maintenance_ticket.csv +13 -0
  73. package/examples/immeuble/fake_data/meter.csv +4 -0
  74. package/examples/immeuble/fake_data/meter_reading.csv +19 -0
  75. package/examples/immeuble/fake_data/milestone.csv +3 -0
  76. package/examples/immeuble/fake_data/organization.csv +12 -0
  77. package/examples/immeuble/fake_data/parking_space.csv +8 -0
  78. package/examples/immeuble/fake_data/permit.csv +3 -0
  79. package/examples/immeuble/fake_data/person.csv +85 -0
  80. package/examples/immeuble/fake_data/private_garden.csv +7 -0
  81. package/examples/immeuble/fake_data/progress_event.csv +2 -0
  82. package/examples/immeuble/fake_data/quote.csv +3 -0
  83. package/examples/immeuble/fake_data/receipt.csv +2 -0
  84. package/examples/immeuble/fake_data/reminder.csv +11 -0
  85. package/examples/immeuble/fake_data/service_contract.csv +4 -0
  86. package/examples/immeuble/fake_data/shared_equipment.csv +8 -0
  87. package/examples/immeuble/fake_data/shared_space.csv +10 -0
  88. package/examples/immeuble/fake_data/unit.csv +41 -0
  89. package/examples/immeuble/fake_data/work_package.csv +4 -0
  90. package/examples/immeuble/fake_data/worksite_project.csv +3 -0
  91. package/examples/immeuble/gap-rules/L0-patrimoine.json +57 -0
  92. package/examples/immeuble/gap-rules/L1-syndic-naive.json +36 -0
  93. package/examples/immeuble/gap-rules/L1-syndic.json +61 -0
  94. package/examples/immeuble/gap-rules/L2-chantier.json +87 -0
  95. package/examples/immeuble/gap-rules/L2-exploitation.json +87 -0
  96. package/examples/immeuble/gap-rules/L2-finance.json +48 -0
  97. package/examples/immeuble/gap-rules/L2-maintenance.json +107 -0
  98. package/examples/immeuble/gap-rules/L2-syndic-filtered.json +39 -0
  99. package/examples/immeuble/gap-rules/L3-full.json +332 -0
  100. package/examples/immeuble/gap-rules/closed-world-contract.md +76 -0
  101. package/examples/immeuble/gap-rules/demo.json +51 -0
  102. package/examples/immeuble/gap-rules/expected-findings.yaml +100 -0
  103. package/examples/immeuble/gap-rules/gap-scenarios.yaml +79 -0
  104. package/examples/immeuble/gap-rules/motifs.json +38 -0
  105. package/examples/immeuble/gap-rules/syndic.json +40 -0
  106. package/examples/immeuble/import_manifest.yaml +25 -0
  107. package/examples/immeuble/import_ready/graph_edges_import.csv +848 -0
  108. package/examples/immeuble/import_ready/mfo_facets_import.csv +559 -0
  109. package/examples/immeuble/index.md +140 -0
  110. package/examples/immeuble/model/immeuble_model.json +418 -0
  111. package/examples/immeuble/reports/01-model.validation.json +56 -0
  112. package/examples/immeuble/reports/02-mapping.validation.json +9 -0
  113. package/examples/immeuble/reports/acceptance.validation.json +161 -0
  114. package/examples/immeuble/reports/consumer_contract.validation.json +162 -0
  115. package/examples/immeuble/reports/graph_edges.jsonl +847 -0
  116. package/examples/immeuble/reports/graph_nodes.jsonl +558 -0
  117. package/examples/immeuble/reports/hybrid-compare.json +144 -0
  118. package/examples/immeuble/reports/immeuble-import-scenario.json +233 -0
  119. package/examples/immeuble/reports/live-artifacts-refresh.validation.json +51 -0
  120. package/examples/immeuble/reports/pipeline_audit.json +59 -0
  121. package/examples/immeuble/reports/projection_audit.json +69 -0
  122. package/examples/immeuble/reports/projection_audit_immeuble.json +257 -0
  123. package/examples/immeuble/reports/projection_audit_immeuble.md +122 -0
  124. package/examples/immeuble/reports/projection_candidates.json +1596 -0
  125. package/examples/immeuble/reports/projection_candidates.md +117 -0
  126. package/examples/immeuble/reports/projection_model_validation.md +115 -0
  127. package/examples/immeuble/reports/reindex.json +4 -0
  128. package/examples/immeuble/reports/reset-immeuble-workspace.json +32 -0
  129. package/examples/immeuble/reports/schema-id-prefix-check.json +5 -0
  130. package/examples/immeuble/scripts/audit-immeuble-projections.mjs +254 -0
  131. package/examples/immeuble/scripts/build-immeuble-model.mjs +308 -0
  132. package/examples/immeuble/scripts/compare-immeuble-snapshots.sh +227 -0
  133. package/examples/immeuble/scripts/enrich-immeuble-demo.mjs +1135 -0
  134. package/examples/immeuble/scripts/reset-immeuble-workspace.mjs +139 -0
  135. package/examples/immeuble/scripts/run-immeuble-backend.sh +164 -0
  136. package/examples/immeuble/scripts/run-immeuble-import.mjs +232 -0
  137. package/examples/immeuble/scripts/run-immeuble-live-lab.sh +439 -0
  138. package/examples/immeuble/scripts/seed-immeuble-gap-rules.mjs +69 -0
  139. package/examples/immeuble/scripts/start-immeuble-demo.sh +52 -0
  140. package/examples/immeuble/scripts/starterkit/analysis-lenses.mjs +181 -0
  141. package/examples/immeuble/scripts/starterkit/analyze-projection-candidates.mjs +714 -0
  142. package/examples/immeuble/scripts/starterkit/audit-ghostcrab-projections.mjs +674 -0
  143. package/examples/immeuble/scripts/starterkit/facet-prefix.mjs +166 -0
  144. package/examples/immeuble/scripts/starterkit/sqlite-utils.mjs +131 -0
  145. package/examples/immeuble/scripts/verify-immeuble-acceptance.mjs +284 -0
  146. package/examples/immeuble/scripts/verify-immeuble-live-artifacts.mjs +140 -0
  147. package/examples/immeuble/scripts/yaml-lite.mjs +96 -0
  148. package/examples/immeuble/sources/agent-prompts/prompts/00-prerequisites-immo-mcp.md +161 -0
  149. package/examples/immeuble/sources/agent-prompts/prompts/00-prerequisites.md +39 -0
  150. package/examples/immeuble/sources/agent-prompts/prompts/01-discovery-and-model-proposal.md +42 -0
  151. package/examples/immeuble/sources/agent-prompts/prompts/02-ontology-register.md +44 -0
  152. package/examples/immeuble/sources/agent-prompts/prompts/03-gap-rules-design.md +44 -0
  153. package/examples/immeuble/sources/agent-prompts/prompts/04-document-ingest.md +40 -0
  154. package/examples/immeuble/sources/agent-prompts/prompts/05-graph-extraction.md +44 -0
  155. package/examples/immeuble/sources/agent-prompts/prompts/06-validate-and-compare-immo-mcp.md +109 -0
  156. package/examples/immeuble/sources/agent-prompts/prompts/06-validate-and-compare-test-immo-mcp3.md +30 -0
  157. package/examples/immeuble/sources/agent-prompts/prompts/06-validate-and-compare.md +63 -0
  158. package/examples/immeuble/sources/checklists/gap-rules-checklist.md +42 -0
  159. package/examples/immeuble/sources/checklists/ontology-checklist.md +54 -0
  160. package/examples/immeuble/sources/documents/README.md +42 -0
  161. package/examples/immeuble/sources/documents/annexes-caves-garages-jardins.md +8 -0
  162. package/examples/immeuble/sources/documents/annexes-jardins-garages.md +1 -0
  163. package/examples/immeuble/sources/documents/baux-erables.md +1 -0
  164. package/examples/immeuble/sources/documents/baux-locatifs.md +11 -0
  165. package/examples/immeuble/sources/documents/coda-janvier-2026.md +10 -0
  166. package/examples/immeuble/sources/documents/composition-menages.md +1 -0
  167. package/examples/immeuble/sources/documents/composition-occupants.md +18 -0
  168. package/examples/immeuble/sources/documents/expected-coverage.json +70 -0
  169. package/examples/immeuble/sources/documents/extrait-coda-janvier-2026.md +1 -0
  170. package/examples/immeuble/sources/documents/groupes-facturation.md +30 -0
  171. package/examples/immeuble/sources/documents/manifest.json +79 -0
  172. package/examples/immeuble/sources/documents/note-architecte-chantier-erables.md +3 -0
  173. package/examples/immeuble/sources/documents/permis-urbanisme-erables.md +3 -0
  174. package/examples/immeuble/sources/documents/procedures-operationnelles.md +3 -0
  175. package/examples/immeuble/sources/documents/pv-ag-budget-2026.md +1 -0
  176. package/examples/immeuble/sources/documents/pv-reception-reserves-erables.md +3 -0
  177. package/examples/immeuble/sources/documents/registre-coproprietaires.md +24 -0
  178. package/examples/immeuble/sources/documents/reglement-copropriete-tilleuls.md +1 -0
  179. package/examples/immeuble/sources/documents/statuts-erables.md +22 -0
  180. package/examples/immeuble/sources/documents/statuts-tilleuls.md +19 -0
  181. package/examples/immeuble/sources/documents/succession-jean-dupont.md +3 -0
  182. package/examples/immeuble/sources/documents/titre-propriete-tilleuls-a3.md +1 -0
  183. package/examples/immeuble/sources/documents/trous-pedagogiques.md +3 -0
  184. package/examples/immeuble/sources/ontology/README.md +1 -0
  185. package/examples/immeuble/sources/ontology/core.yaml +444 -0
  186. package/examples/immeuble/success-criteria.yaml +35 -0
  187. package/fixtures/immeuble-demo.sqlite +0 -0
  188. package/package.json +11 -2
  189. package/scripts/lib/sqlite-runtime.mjs +1 -1
  190. package/scripts/load-immeuble-demo.sh +103 -0
  191. package/scripts/seed-immeuble-projections.mjs +75 -148
  192. package/scripts/verify-immeuble-demo-sources.mjs +93 -0
  193. package/scripts/verify-immeuble-demo.mjs +69 -0
  194. package/build/client/_app/immutable/chunks/D0UIlUGZ.js.br +0 -0
  195. package/build/client/_app/immutable/chunks/D0UIlUGZ.js.gz +0 -0
  196. package/build/client/_app/immutable/entry/app.CR-imLox.js.br +0 -0
  197. package/build/client/_app/immutable/entry/app.CR-imLox.js.gz +0 -0
  198. package/build/client/_app/immutable/entry/start.DV-AjeAB.js +0 -1
  199. package/build/client/_app/immutable/entry/start.DV-AjeAB.js.br +0 -2
  200. package/build/client/_app/immutable/entry/start.DV-AjeAB.js.gz +0 -0
  201. package/build/client/_app/immutable/nodes/1.CTEedoSY.js.br +0 -0
  202. package/build/client/_app/immutable/nodes/1.CTEedoSY.js.gz +0 -0
@@ -0,0 +1,140 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Verify live projection artifacts on a running MindBrain backend.
4
+ *
5
+ * Usage:
6
+ * node examples/immeuble/scripts/verify-immeuble-live-artifacts.mjs
7
+ * [--workspace immeuble] [--url http://127.0.0.1:8091]
8
+ */
9
+
10
+ import { readFileSync, mkdirSync, writeFileSync } from "node:fs";
11
+ import { dirname, join, resolve } from "node:path";
12
+ import { fileURLToPath } from "node:url";
13
+ import { parseYaml } from "./yaml-lite.mjs";
14
+ import { spawnSync } from "node:child_process";
15
+
16
+ const immeubleRoot = resolve(dirname(fileURLToPath(import.meta.url)), "..");
17
+ const pkgRoot = resolve(immeubleRoot, "..", "..");
18
+ const gcp = join(pkgRoot, "bin", "gcp.mjs");
19
+ const reportsDir = join(immeubleRoot, "reports");
20
+ const acceptancePath = join(immeubleRoot, "ACCEPTANCE.yaml");
21
+
22
+ const args = process.argv.slice(2);
23
+ const workspaceId = parseFlag(args, "--workspace-id", "immeuble");
24
+ const backendUrl = parseFlag(args, "--url", process.env.GHOSTCRAB_MINDBRAIN_URL ?? "http://127.0.0.1:8091");
25
+
26
+ const acceptance = parseYaml(readFileSync(acceptancePath, "utf8"));
27
+ const requiredArtifacts = acceptance?.projections?.live_answer_view ?? [];
28
+ const result = { ok: true, workspace_id: workspaceId, checks: [] };
29
+
30
+ try {
31
+ const list = runArtifactCmd(["artifact", "list", "--url", backendUrl, "--workspace-id", workspaceId, "--kind", "live_answer_view"]);
32
+ result.checks.push({ name: "artifact.list", ok: true, detail: `status=${list.status}` });
33
+
34
+ const listPayload = parseJson(list.stdout);
35
+ const artifacts = Array.isArray(listPayload?.artifacts) ? listPayload.artifacts : [];
36
+ const present = new Set(artifacts.map((a) => a?.artifact_id).filter(Boolean));
37
+
38
+ const missing = requiredArtifacts.filter((artifactId) => !present.has(artifactId));
39
+ result.checks.push({
40
+ name: "required_artifacts_present",
41
+ ok: missing.length === 0,
42
+ detail: missing.length ? `missing: ${missing.join(", ")}` : "all required ids listed"
43
+ });
44
+ if (missing.length > 0) {
45
+ throw new Error("missing required live_answer_view artifacts");
46
+ }
47
+
48
+ const refreshChecks = [];
49
+ for (const artifactId of requiredArtifacts) {
50
+ const refresh = runArtifactCmd(["artifact", "refresh", "--url", backendUrl, artifactId]);
51
+ const refreshPayload = parseJson(refresh.stdout);
52
+ const refreshArtifactId = refreshPayload?.artifact_id || refreshPayload?.artifact?.artifact_id;
53
+ if (typeof refreshArtifactId === "string" && refreshArtifactId.length > 0) {
54
+ result.checks.push({ name: `artifact.refresh.${artifactId}`, ok: true, detail: `artifact_id=${refreshArtifactId}` });
55
+ } else {
56
+ result.checks.push({ name: `artifact.refresh.${artifactId}`, ok: true, detail: "refresh returned JSON payload" });
57
+ }
58
+ refreshChecks.push(refreshPayload);
59
+
60
+ const get = runArtifactCmd(["artifact", "get", "--url", backendUrl, artifactId]);
61
+ const getPayload = parseJson(get.stdout);
62
+ const payload = extractPayload(getPayload) ?? extractPayload(refreshPayload);
63
+ const artifactSummary = typeof payload?.summary === "string" && payload.summary.trim().length > 0;
64
+ const artifactRefreshChecks = Array.isArray(payload?.refresh_checks) && payload.refresh_checks.length > 0;
65
+ const refreshedOperational =
66
+ refreshPayload?.ok === true &&
67
+ payload &&
68
+ (typeof payload.graph_entities === "number" ||
69
+ typeof payload.facts === "number" ||
70
+ typeof payload.graph_relations === "number");
71
+ const payloadOk = (artifactSummary && artifactRefreshChecks) || refreshedOperational;
72
+ result.checks.push({
73
+ name: `artifact.payload.${artifactId}`,
74
+ ok: payloadOk,
75
+ detail: JSON.stringify({
76
+ has_summary: artifactSummary,
77
+ has_refresh_checks: artifactRefreshChecks,
78
+ refreshed_operational: refreshedOperational
79
+ })
80
+ });
81
+ if (!payloadOk) {
82
+ throw new Error(`artifact payload invalid for ${artifactId}`);
83
+ }
84
+ }
85
+
86
+ result.checks.push({ name: "refresh_count", ok: refreshChecks.length === requiredArtifacts.length, detail: `${refreshChecks.length}` });
87
+ result.ok = result.checks.every((item) => item.ok);
88
+ } catch (err) {
89
+ result.ok = false;
90
+ result.error = err instanceof Error ? err.message : String(err);
91
+ }
92
+
93
+ mkdirSync(reportsDir, { recursive: true });
94
+ writeFileSync(
95
+ join(reportsDir, "live-artifacts-refresh.validation.json"),
96
+ JSON.stringify(result, null, 2) + "\n",
97
+ "utf8"
98
+ );
99
+ console.log(JSON.stringify(result, null, 2));
100
+ process.exit(result.ok ? 0 : 1);
101
+
102
+ function runArtifactCmd(argsList) {
103
+ const res = spawnSync(process.execPath, [gcp, "brain", ...argsList], {
104
+ cwd: pkgRoot,
105
+ env: {
106
+ ...process.env,
107
+ GHOSTCRAB_MINDBRAIN_URL: backendUrl
108
+ },
109
+ encoding: "utf8"
110
+ });
111
+ if (res.status !== 0) {
112
+ throw new Error(`gcp brain ${argsList.join(" ")} failed (${res.status}): ${(res.stderr || res.stdout || "").trim()}`);
113
+ }
114
+ return { status: res.status, stdout: res.stdout || "", stderr: res.stderr || "" };
115
+ }
116
+
117
+ function parseJson(text) {
118
+ return JSON.parse(text || "{}");
119
+ }
120
+
121
+ function extractPayload(getPayload) {
122
+ const payloadJson = getPayload?.payload_json;
123
+ if (typeof payloadJson === "string") {
124
+ try {
125
+ return JSON.parse(payloadJson);
126
+ } catch {
127
+ return null;
128
+ }
129
+ }
130
+ if (getPayload?.payload && typeof getPayload.payload === "object") {
131
+ return getPayload.payload;
132
+ }
133
+ return null;
134
+ }
135
+
136
+ function parseFlag(argv, name, defaultValue) {
137
+ const index = argv.indexOf(name);
138
+ if (index === -1) return defaultValue;
139
+ return argv[index + 1] ?? defaultValue;
140
+ }
@@ -0,0 +1,96 @@
1
+ export function parseYaml(text) {
2
+ const root = {};
3
+ const stack = [{ indent: -1, value: root }];
4
+ const lines = String(text)
5
+ .split(/\r?\n/)
6
+ .map((raw) => raw.replace(/\s+#.*$/, ""))
7
+ .filter((raw) => raw.trim() && !raw.trim().startsWith("#"));
8
+
9
+ for (const raw of lines) {
10
+ const indent = raw.match(/^ */)?.[0].length ?? 0;
11
+ const line = raw.trim();
12
+ while (stack.length > 1 && indent <= stack[stack.length - 1].indent) stack.pop();
13
+ const parent = stack[stack.length - 1].value;
14
+
15
+ if (line.startsWith("- ")) {
16
+ if (!Array.isArray(parent)) continue;
17
+ const body = line.slice(2).trim();
18
+ if (body.includes(":")) {
19
+ const [key, rest] = splitKeyValue(body);
20
+ const item = {};
21
+ item[key] = parseScalar(rest);
22
+ parent.push(item);
23
+ stack.push({ indent, value: item });
24
+ } else {
25
+ parent.push(parseScalar(body));
26
+ }
27
+ continue;
28
+ }
29
+
30
+ const [key, rest] = splitKeyValue(line);
31
+ if (!key) continue;
32
+ if (rest === "") {
33
+ const next = nextMeaningfulLine(lines, raw);
34
+ const value = next?.trim().startsWith("- ") ? [] : {};
35
+ parent[key] = value;
36
+ stack.push({ indent, value });
37
+ } else {
38
+ parent[key] = parseScalar(rest);
39
+ }
40
+ }
41
+ return root;
42
+ }
43
+
44
+ function nextMeaningfulLine(lines, currentRaw) {
45
+ const currentIndex = lines.indexOf(currentRaw);
46
+ return lines.slice(currentIndex + 1).find((line) => line.trim());
47
+ }
48
+
49
+ function splitKeyValue(line) {
50
+ const index = line.indexOf(":");
51
+ if (index === -1) return [line.trim(), ""];
52
+ return [line.slice(0, index).trim(), line.slice(index + 1).trim()];
53
+ }
54
+
55
+ function parseScalar(raw) {
56
+ const value = raw.trim();
57
+ if (value === "") return "";
58
+ if (value === "true") return true;
59
+ if (value === "false") return false;
60
+ if (value === "null") return null;
61
+ if (/^-?\d+(\.\d+)?$/.test(value)) return Number(value);
62
+ if (value.startsWith("[") && value.endsWith("]")) {
63
+ const inner = value.slice(1, -1).trim();
64
+ if (!inner) return [];
65
+ return splitInlineList(inner).map(parseScalar);
66
+ }
67
+ if (
68
+ (value.startsWith("\"") && value.endsWith("\"")) ||
69
+ (value.startsWith("'") && value.endsWith("'"))
70
+ ) {
71
+ try {
72
+ return JSON.parse(value);
73
+ } catch {
74
+ return value.slice(1, -1);
75
+ }
76
+ }
77
+ return value;
78
+ }
79
+
80
+ function splitInlineList(value) {
81
+ const parts = [];
82
+ let current = "";
83
+ let quote = "";
84
+ for (const char of value) {
85
+ if ((char === "\"" || char === "'") && !quote) quote = char;
86
+ else if (char === quote) quote = "";
87
+ if (char === "," && !quote) {
88
+ parts.push(current.trim());
89
+ current = "";
90
+ } else {
91
+ current += char;
92
+ }
93
+ }
94
+ if (current.trim()) parts.push(current.trim());
95
+ return parts;
96
+ }
@@ -0,0 +1,161 @@
1
+ # Prerequisites — MCP lab immeuble (workspace `immo-mcp`)
2
+
3
+ **Phase 0 — read only, no writes.**
4
+
5
+ Variant of [`00-prerequisites.md`](00-prerequisites.md) with fixed IDs for workspace `immo-mcp`.
6
+
7
+ ## Human pre-flight (before opening the agent)
8
+
9
+ 1. **GhostCrab backend** reachable (`ghostcrab_status` must respond).
10
+ 2. **MCP `ghostcrab-personal-mcp`** enabled in Cursor — same SQLite as this lab (see below).
11
+ 3. **Single SQLite file** — do **not** use a separate `immo-mcp.sqlite`. Cursor MCP (`.cursor/mcp.json`) starts:
12
+
13
+ ```text
14
+ gcp brain up --db /home/dlamotte/Documents/ghostcrab-personal-mcp/data/ghostcrab.sqlite
15
+ ```
16
+
17
+ Lab process data lives in workspace **`immo-mcp`** inside that file. Golden reference uses workspace **`immeuble`** in the same file.
18
+
19
+ For CLI commands outside Cursor, align explicitly:
20
+
21
+ ```bash
22
+ export GHOSTCRAB_SQLITE_PATH="/home/dlamotte/Documents/ghostcrab-personal-mcp/data/ghostcrab.sqlite"
23
+ # optional: gcp brain db-who → must show the same path
24
+ ```
25
+
26
+ 4. **Golden workspace** (phase 06 comparison only — never load into `immo-mcp`):
27
+
28
+ ```bash
29
+ export GHOSTCRAB_SQLITE_PATH="/home/dlamotte/Documents/ghostcrab-personal-mcp/data/ghostcrab.sqlite"
30
+ node bin/gcp.mjs load examples/immeuble/bundle/immeuble.bundle.json \
31
+ --workspace immeuble --reindex all \
32
+ --db "$GHOSTCRAB_SQLITE_PATH"
33
+ ```
34
+
35
+ MCP and CLI must target the **same** `--db` / `GHOSTCRAB_SQLITE_PATH` for the whole lab.
36
+
37
+ ## Two workspaces, one SQLite — comparison model
38
+
39
+ | Workspace | Role |
40
+ |-----------|------|
41
+ | **`immo-mcp`** | What GhostCrab MCP **built** from corpus (phases 2–5) |
42
+ | **`immeuble`** | What we **expect** — golden from `bundle/immeuble.bundle.json` (load in phase 6 only) |
43
+
44
+ Both live in **`ghostcrab.sqlite`**. Phase 6 compares `immo-mcp` (process) vs `immeuble` (reference) vs [`success-criteria.yaml`](../success-criteria.yaml). See [`06-validate-and-compare-immo-mcp.md`](06-validate-and-compare-immo-mcp.md).
45
+
46
+ Never load the bundle into `immo-mcp`.
47
+
48
+ ## Run identifiers
49
+
50
+ | Field | Value |
51
+ |-------|-------|
52
+ | `workspace_id` | `immo-mcp` |
53
+ | `collection_id` | `immo-mcp::docs` |
54
+ | `ontology_id` | `immeuble::core` |
55
+ | `golden_workspace_id` | `immeuble` |
56
+ | `golden_bundle` | `examples/immeuble/bundle/immeuble.bundle.json` |
57
+ | `corpus` | `examples/immeuble/sources/documents/` (8 md files) |
58
+ | `criteria` | `examples/immeuble/success-criteria.yaml` |
59
+
60
+ ## Tools
61
+
62
+ 1. `ghostcrab_status`
63
+ 2. `ghostcrab_modeling_guidance`
64
+
65
+ ## Agent prompt (copy-paste)
66
+
67
+ ```
68
+ Tu es un agent GhostCrab MCP en session vierge. Ta mission : reconstruire le domaine
69
+ syndic belge (Résidence Les Tilleuls + Les Érables) depuis le corpus brut, en suivant
70
+ strictement le MCP lab documenté.
71
+
72
+ ## Documentation de référence (lire en premier)
73
+
74
+ 1. docs/explanation/mcp-lab-context.md — contexte, phases 00→06, critères
75
+ 2. examples/immeuble/README.md — point d'entrée opérationnel
76
+ 3. examples/immeuble/sources/agent-prompts/prompts/00-prerequisites-immo-mcp.md — démarrage (ce fichier)
77
+
78
+ ## Identifiants de cette run
79
+
80
+ | Champ | Valeur |
81
+ |-------|--------|
82
+ | workspace_id | immo-mcp |
83
+ | collection_id | immo-mcp::docs |
84
+ | ontology_id | immeuble::core |
85
+ | golden_workspace_id | immeuble |
86
+ | golden_bundle | examples/immeuble/bundle/immeuble.bundle.json |
87
+ | corpus | examples/immeuble/sources/documents/ (8 fichiers md) |
88
+ | critères | examples/immeuble/success-criteria.yaml |
89
+
90
+ ## Règles absolues
91
+
92
+ - Appeler ghostcrab_status en PREMIER.
93
+ - Lire avant d'écrire : count → search → pack (ou graph_search pour le graphe).
94
+ - NE PAS charger le bundle golden dans immo-mcp — le golden (immeuble) sert
95
+ uniquement à comparer en phase 06.
96
+ - Phases 00–01 : lecture seule — aucune écriture.
97
+ - Avant toute écriture (phases 02–05) : produire un Model Proposal (entités, arêtes,
98
+ facettes, questions de compétence) et ATTENDRE ma confirmation explicite
99
+ (ONBOARDING_CONTRACT §9).
100
+ - Ingestion documentaire : CLI gcp brain document (pas streaming MCP unitaire).
101
+ - Outils graph/gap extended : découvrir via ghostcrab_tool_search si absents du défaut.
102
+
103
+ ## Workflow — exécuter dans l'ordre
104
+
105
+ Phase 0 — examples/immeuble/sources/agent-prompts/prompts/00-prerequisites-immo-mcp.md
106
+ → ghostcrab_status, ghostcrab_modeling_guidance
107
+ → lire corpus/manifest.json, success-criteria.yaml, contracts/scenarios.yaml
108
+
109
+ Phase 1 — prompts/01-discovery-and-model-proposal.md
110
+ → affiner le Model Proposal depuis le corpus (toujours sans écriture)
111
+
112
+ GATE : stop et présente le Model Proposal. N'écris rien tant que je n'ai pas confirmé.
113
+
114
+ Phase 2 — prompts/02-ontology-register.md (après confirmation)
115
+ → gcp brain ontology compile sur ontologies/immeuble/core.yaml
116
+
117
+ Phase 3 — prompts/03-gap-rules-design.md
118
+ → ghostcrab_graph_gap_rules_import (réf. training/reference gap-rules)
119
+
120
+ Phase 4 — prompts/04-document-ingest.md
121
+ → gcp brain document : collection-create, ingest 8 corpus, profile, qualify
122
+
123
+ Phase 5 — prompts/05-graph-extraction.md
124
+ → ghostcrab_learn (incrémental) OU document-business-extract (batch)
125
+
126
+ Phase 6 — prompts/06-validate-and-compare-immo-mcp.md
127
+ → ghostcrab_graph_search, ghostcrab_graph_diagnostics vs success-criteria.yaml
128
+ → comparer immo-mcp (process) vs immeuble (bundle golden), même SQLite
129
+
130
+ ## Livrables attendus par phase
131
+
132
+ - Phase 0–1 : Model Proposal textuel
133
+ - Phase 2–5 : rapport court (ce qui a été écrit, outils utilisés, counts)
134
+ - Phase 6 : tableau écart vs success-criteria.yaml + recommandations
135
+
136
+ ## Commence maintenant
137
+
138
+ 1. ghostcrab_status
139
+ 2. Résume mcp-lab-context.md en 5 lignes
140
+ 3. Lis corpus/manifest.json et success-criteria.yaml
141
+ 4. Propose un premier Model Proposal (sans écrire)
142
+ ```
143
+
144
+ ## Required reading
145
+
146
+ - `corpus/manifest.json`
147
+ - `success-criteria.yaml`
148
+ - `../contracts/scenarios.yaml`
149
+
150
+ ## Deliverable
151
+
152
+ Model Proposal textuel (entités, arêtes, facettes, competency questions couvertes).
153
+
154
+ ## Gate
155
+
156
+ **Confirmation humaine requise** — reply **« je confirme »** (ou corrections) before `01-discovery-and-model-proposal.md` refinement and before any write phases (ONBOARDING_CONTRACT §9).
157
+
158
+ ## Docs if blocked
159
+
160
+ - [`01-reference-to-graph`](../../../docs/explanation/01-reference-to-graph.md)
161
+ - [`mcp-lab-context`](../../../docs/explanation/mcp-lab-context.md)
@@ -0,0 +1,39 @@
1
+ # Prerequisites — MCP lab immeuble
2
+
3
+ **Phase 0 — read only, no writes.**
4
+
5
+ ## Tools
6
+
7
+ 1. `ghostcrab_status`
8
+ 2. `ghostcrab_modeling_guidance`
9
+
10
+ ## Agent prompt (copy-paste)
11
+
12
+ ```
13
+ Je veux reconstruire le domaine syndic belge "Résidence Les Tilleuls + Les Érables"
14
+ depuis le corpus brut dans examples/immeuble/sources/documents/manifest.json.
15
+ Workspace cible : immeuble.
16
+ Référence de validation : examples/immeuble/bundle/immeuble.bundle.json (bundle golden immeuble).
17
+
18
+ Ne charge pas le bundle golden dans le workspace LLM — compare seulement à la fin.
19
+ Propose un Model Proposal (entités, relations, facettes documentaires) avant toute écriture.
20
+ ```
21
+
22
+ ## Required reading
23
+
24
+ - `manifest.json`
25
+ - `success-criteria.yaml`
26
+ - `../contracts/scenarios.yaml`
27
+ - `workspace.json`
28
+
29
+ ## Deliverable
30
+
31
+ Model Proposal textuel (entités, arêtes, facettes, competency questions couvertes).
32
+
33
+ ## Gate
34
+
35
+ **Confirmation humaine requise** avant de passer à `01-discovery-and-model-proposal.md` puis aux phases d'écriture (ONBOARDING_CONTRACT §9).
36
+
37
+ ## Next
38
+
39
+ → [`01-discovery-and-model-proposal.md`](01-discovery-and-model-proposal.md)
@@ -0,0 +1,42 @@
1
+ # Discovery and model proposal
2
+
3
+ **Phase 1 — read corpus, propose model (no writes yet).**
4
+
5
+ ## Tools
6
+
7
+ - File reads: `corpus/*.md`, `corpus/manifest.json`
8
+ - Optional: `ghostcrab_tool_search` if you need specialized tools
9
+
10
+ ## Agent prompt
11
+
12
+ ```
13
+ Lis les 8 fichiers listés dans examples/immeuble/sources/documents/manifest.json.
14
+
15
+ Produis une table avec :
16
+ 1. Entity types (building, block, unit, person, household, lease_contract, cellar, parking_space, …)
17
+ 2. Edge types (contains, owns, occupies, leases, assigned_cellar, assigned_garage, …)
18
+ 3. Facet dimensions documentaires (source.document_type, domain.building, domain.unit, domain.role, …)
19
+ 4. Mapping doc_id → document_type → entités/relations attendues
20
+ 5. Competency questions couvertes (réf. examples/immeuble/contracts/scenarios.yaml)
21
+
22
+ Compare à ../checklists/ontology-checklist.md. Si tu t'écartes, explique pourquoi.
23
+ Ne crée rien dans GhostCrab tant que le Model Proposal n'est pas validé.
24
+ ```
25
+
26
+ ## Deliverable
27
+
28
+ | Column | Content |
29
+ |--------|---------|
30
+ | Entity types | List with brief definition |
31
+ | Edge types | List with source/target |
32
+ | Facet dimensions | Namespace.dimension + value examples |
33
+ | Doc mapping | 8 rows from manifest |
34
+ | Scenarios | Which scenario IDs each doc supports |
35
+
36
+ ## Gate
37
+
38
+ Human validation of Model Proposal before `02-ontology-register.md`.
39
+
40
+ ## Next
41
+
42
+ → [`02-ontology-register.md`](02-ontology-register.md)
@@ -0,0 +1,44 @@
1
+ # Ontology register
2
+
3
+ **Phase 2 — create workspace and register ontology.**
4
+
5
+ ## Tools
6
+
7
+ - `ghostcrab_workspace_create`
8
+ - `ghostcrab_schema_register` or `ghostcrab_schema_inspect` / `ghostcrab_schema_list`
9
+ - CLI (documented): `gcp brain ontology compile …`
10
+
11
+ ## Agent prompt
12
+
13
+ ```
14
+ Crée le workspace immeuble (domain_profile: syndic).
15
+ Enregistre l'ontology immeuble::core alignée sur le Model Proposal validé :
16
+
17
+ Option A — LinkML :
18
+ gcp brain ontology compile \
19
+ --workspace-id immeuble \
20
+ --ontology-id immeuble::core \
21
+ --input ontologies/immeuble/core.yaml \
22
+ --profile syndic \
23
+ --import-db --force
24
+
25
+ Option B — MCP schema :
26
+ ghostcrab_schema_register avec facet dimensions du Model Proposal
27
+
28
+ Vérifie avec ghostcrab_schema_list / ghostcrab_schema_inspect sur immeuble.
29
+ Ne charge pas examples/immeuble/bundle/immeuble.bundle.json dans ce workspace.
30
+ ```
31
+
32
+ ## Reference (read-only)
33
+
34
+ Canonical LinkML: `ontologies/immeuble/core.yaml`
35
+ Checklist: `reference/ontology-checklist.md`
36
+
37
+ ## Deliverable
38
+
39
+ - Workspace `immeuble` exists
40
+ - Ontology `immeuble::core` visible in workspace
41
+
42
+ ## Next
43
+
44
+ → [`03-gap-rules-design.md`](03-gap-rules-design.md)
@@ -0,0 +1,44 @@
1
+ # Gap rules design
2
+
3
+ **Phase 3 — design and import closed-world gap rules.**
4
+
5
+ ## Tools
6
+
7
+ - `ghostcrab_graph_gap_rules_import`
8
+ - `ghostcrab_graph_gap_rules`
9
+ - Optional: `ghostcrab_graph_diagnostics` (smoke on empty graph)
10
+
11
+ ## Agent prompt
12
+
13
+ ```
14
+ Conçois les graph_gap_rules closed-world pour workspace immeuble, ontology immeuble::core.
15
+
16
+ Niveau 1 — Patrimoine (cf. examples/immeuble/gap-rules/L0-patrimoine.json) :
17
+ - unit-one-cellar, unit-in-building, garage-at-most-one-unit
18
+
19
+ Niveau 2 — Syndic filtré (cf. `../gap-rules/L2-syndic-filtered.json`) :
20
+ - unit-has-owner, occupied-unit-has-occupant, tenant-occupied-has-lease
21
+ - entity_filter sur usage_status (exclure vacant / vacant_works)
22
+
23
+ Importe avec replace:true via ghostcrab_graph_gap_rules_import.
24
+ Liste les rules actives via ghostcrab_graph_gap_rules.
25
+ Explique chaque rule_id en langage métier (cf. `../gap-rules/closed-world-contract.md`).
26
+
27
+ Pédagogie optionnelle : importer d'abord L1-syndic-naive.json, diagnostiquer le FP sur
28
+ Érables Appartement A4 (vacant_works), puis passer à L2.
29
+ ```
30
+
31
+ ## Reference files
32
+
33
+ - `../gap-rules/demo.json` — patrimoine demo
34
+ - `../gap-rules/syndic.json` — syndic L2 for immeuble
35
+ - `../gap-rules/` — curriculum L0–L3
36
+ - `../checklists/gap-rules-checklist.md`
37
+
38
+ ## Deliverable
39
+
40
+ JSON gap-rules imported; list of active rule_ids with business labels.
41
+
42
+ ## Next
43
+
44
+ → [`04-document-ingest.md`](04-document-ingest.md)
@@ -0,0 +1,40 @@
1
+ # Document ingest
2
+
3
+ **Phase 4 — enqueue, profile, and qualify source documents.**
4
+
5
+ ## Tools
6
+
7
+ MCP: `ghostcrab_workspace_create` (if needed)
8
+ CLI: `gcp brain document …` (profile + qualify pipeline)
9
+
10
+ ## Agent prompt
11
+
12
+ ```
13
+ Pour chaque entrée de examples/immeuble/sources/documents/ :
14
+
15
+ 1. collection-create --workspace-id immeuble --collection-id immeuble::docs
16
+ 2. ontology-attach --ontology-id immeuble::core --role taxonomy
17
+ 3. document-profile-enqueue --content-file examples/immeuble/sources/documents/<filename> \
18
+ --doc-id <doc_id> --language fr
19
+ 4. document-profile-worker --limit <n>
20
+ 5. qualification-vocab-list
21
+ 6. document-qualify --taxonomies immeuble::core \
22
+ --facets source.document_type,domain.building,domain.unit,domain.role,domain.scenario,finance.payment_status
23
+
24
+ Commence par 1 document si itération prompts (--limit-docs 1).
25
+ Vérifie les qualifications par doc_id avant de continuer.
26
+ ```
27
+
28
+ ## Mock CI reference
29
+
30
+ ```bash
31
+ node examples/immeuble/scripts/run-immeuble-import.mjs --apply --skip-provenance-validation --skip-preflight
32
+ ```
33
+
34
+ ## Deliverable
35
+
36
+ 8 documents profiled and qualified in `immeuble::docs`.
37
+
38
+ ## Next
39
+
40
+ → [`05-graph-extraction.md`](05-graph-extraction.md)
@@ -0,0 +1,44 @@
1
+ # Graph extraction
2
+
3
+ **Phase 5 — extract business entities and relations from qualified documents.**
4
+
5
+ ## Tools
6
+
7
+ - `ghostcrab_remember` / `ghostcrab_upsert`
8
+ - `ghostcrab_graph_search`
9
+ - Reindex: `gcp load` partial or workspace reindex CLI
10
+
11
+ ## Agent prompt
12
+
13
+ ```
14
+ À partir des documents qualifiés dans `immeuble`, extrais le graphe métier syndic :
15
+
16
+ Structure :
17
+ - 2 immeubles (Tilleuls, Érables), 13 lots, personnes, ménages
18
+ - 13 caves, garages sélectifs, jardins privatifs RDC
19
+ - 5 contrats de bail, 3 écritures CODA
20
+
21
+ Relations minimales :
22
+ contains, owns, occupies, household_member, leases, rented_to,
23
+ assigned_cellar, assigned_garage, uses_exclusive, matched_to
24
+
25
+ Utilise ghostcrab_remember / ghostcrab_upsert ou l'extraction LLM documentée
26
+ dans le flux d'ingestion `run-immeuble-import.mjs`.
27
+ Reindex le graphe après écriture.
28
+
29
+ Vérifie :
30
+ ghostcrab_graph_search query "appartement" → ≥ 13 unités
31
+ ghostcrab_graph_search query "Dupont" → résultats pertinents
32
+ ```
33
+
34
+ ## Success thresholds
35
+
36
+ See `success-criteria.yaml` → `entity_counts`, `relation_edges`.
37
+
38
+ ## Deliverable
39
+
40
+ Populated `graph_entity` for workspace `immeuble`; counts near success criteria.
41
+
42
+ ## Next
43
+
44
+ → [`06-validate-and-compare.md`](06-validate-and-compare.md)