@mindflight/mindbrain-personal-studio 0.6.1 → 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 (212) hide show
  1. package/README.md +16 -8
  2. package/build/client/_app/immutable/chunks/CIErFlYG.js +1 -0
  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.CVz6aYsT.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.BBtxY46Q.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-D8EA_he2.js → internal.js-C3tV0XXj.js} +2 -2
  20. package/build/server/chunks/chunks/{internal.js-D8EA_he2.js.map → internal.js-C3tV0XXj.js.map} +1 -1
  21. package/build/server/chunks/entries/endpoints/api/graph/schema-registry/{_server.ts.js-Dyfsc-VA.js → _server.ts.js-CAsXxBRq.js} +7 -2
  22. package/build/server/chunks/entries/endpoints/api/graph/schema-registry/_server.ts.js-CAsXxBRq.js.map +1 -0
  23. package/build/server/chunks/{handler-BjXBCd1c.js → handler-DlaCCnxx.js} +3 -3
  24. package/build/server/chunks/{handler-BjXBCd1c.js.map → handler-DlaCCnxx.js.map} +1 -1
  25. package/build/server/chunks/{index.js-BJYcV8V7.js → index.js-BGfKWHak.js} +2 -2
  26. package/build/server/chunks/{index.js-BJYcV8V7.js.map → index.js-BGfKWHak.js.map} +1 -1
  27. package/build/server/chunks/{manifest.js-Ddaot0P4.js → manifest.js-CnmaNf5D.js} +4 -4
  28. package/build/server/chunks/{manifest.js-Ddaot0P4.js.map → manifest.js-CnmaNf5D.js.map} +1 -1
  29. package/build/server/chunks/nodes/{1.js-BRigw_9J.js → 1.js-BypjwBYB.js} +2 -2
  30. package/build/server/chunks/nodes/{1.js-BRigw_9J.js.map → 1.js-BypjwBYB.js.map} +1 -1
  31. package/examples/immeuble/ACCEPTANCE.yaml +82 -0
  32. package/examples/immeuble/BIM_LITE.md +15 -0
  33. package/examples/immeuble/CHECKLIST.md +128 -0
  34. package/examples/immeuble/README.md +121 -0
  35. package/examples/immeuble/bundle/immeuble.bundle.json +22786 -0
  36. package/examples/immeuble/contracts/answer_artifacts.seed.jsonl +34 -0
  37. package/examples/immeuble/contracts/business_capabilities.seed.jsonl +25 -0
  38. package/examples/immeuble/contracts/consumer_contract.yaml +131 -0
  39. package/examples/immeuble/contracts/immeuble_structured_import_model.json +310 -0
  40. package/examples/immeuble/contracts/mapping_external_to_canonical.json +375 -0
  41. package/examples/immeuble/contracts/mapping_external_to_canonical.yaml +55 -0
  42. package/examples/immeuble/contracts/mapping_external_to_canonical_ws.json +65 -0
  43. package/examples/immeuble/contracts/model_contract.json +943 -0
  44. package/examples/immeuble/contracts/projection_catalog.yaml +366 -0
  45. package/examples/immeuble/contracts/scenarios.yaml +27 -0
  46. package/examples/immeuble/contracts/semantic_proposal.golden.json +1 -0
  47. package/examples/immeuble/contracts/source_profile.yaml +38 -0
  48. package/examples/immeuble/fake_data/DeltaFinding.csv +20 -0
  49. package/examples/immeuble/fake_data/ProjectionResult.csv +13 -0
  50. package/examples/immeuble/fake_data/ag_meeting.csv +4 -0
  51. package/examples/immeuble/fake_data/agenda_item.csv +4 -0
  52. package/examples/immeuble/fake_data/architect.csv +3 -0
  53. package/examples/immeuble/fake_data/architecture_firm.csv +2 -0
  54. package/examples/immeuble/fake_data/bank_account.csv +3 -0
  55. package/examples/immeuble/fake_data/billing_group.csv +41 -0
  56. package/examples/immeuble/fake_data/block.csv +10 -0
  57. package/examples/immeuble/fake_data/budget_line.csv +3 -0
  58. package/examples/immeuble/fake_data/building.csv +6 -0
  59. package/examples/immeuble/fake_data/cellar.csv +41 -0
  60. package/examples/immeuble/fake_data/change_order.csv +2 -0
  61. package/examples/immeuble/fake_data/charge_call.csv +58 -0
  62. package/examples/immeuble/fake_data/claim.csv +4 -0
  63. package/examples/immeuble/fake_data/coda_entry.csv +49 -0
  64. package/examples/immeuble/fake_data/compliance_certificate.csv +10 -0
  65. package/examples/immeuble/fake_data/contractor.csv +4 -0
  66. package/examples/immeuble/fake_data/decision.csv +5 -0
  67. package/examples/immeuble/fake_data/defect_reserve.csv +3 -0
  68. package/examples/immeuble/fake_data/household.csv +41 -0
  69. package/examples/immeuble/fake_data/inspection.csv +3 -0
  70. package/examples/immeuble/fake_data/insurance_policy.csv +4 -0
  71. package/examples/immeuble/fake_data/intervention.csv +13 -0
  72. package/examples/immeuble/fake_data/invoice.csv +3 -0
  73. package/examples/immeuble/fake_data/lease_contract.csv +12 -0
  74. package/examples/immeuble/fake_data/maintenance_ticket.csv +13 -0
  75. package/examples/immeuble/fake_data/meter.csv +4 -0
  76. package/examples/immeuble/fake_data/meter_reading.csv +19 -0
  77. package/examples/immeuble/fake_data/milestone.csv +3 -0
  78. package/examples/immeuble/fake_data/organization.csv +12 -0
  79. package/examples/immeuble/fake_data/parking_space.csv +8 -0
  80. package/examples/immeuble/fake_data/permit.csv +3 -0
  81. package/examples/immeuble/fake_data/person.csv +85 -0
  82. package/examples/immeuble/fake_data/private_garden.csv +7 -0
  83. package/examples/immeuble/fake_data/progress_event.csv +2 -0
  84. package/examples/immeuble/fake_data/quote.csv +3 -0
  85. package/examples/immeuble/fake_data/receipt.csv +2 -0
  86. package/examples/immeuble/fake_data/reminder.csv +11 -0
  87. package/examples/immeuble/fake_data/service_contract.csv +4 -0
  88. package/examples/immeuble/fake_data/shared_equipment.csv +8 -0
  89. package/examples/immeuble/fake_data/shared_space.csv +10 -0
  90. package/examples/immeuble/fake_data/unit.csv +41 -0
  91. package/examples/immeuble/fake_data/work_package.csv +4 -0
  92. package/examples/immeuble/fake_data/worksite_project.csv +3 -0
  93. package/examples/immeuble/gap-rules/L0-patrimoine.json +57 -0
  94. package/examples/immeuble/gap-rules/L1-syndic-naive.json +36 -0
  95. package/examples/immeuble/gap-rules/L1-syndic.json +61 -0
  96. package/examples/immeuble/gap-rules/L2-chantier.json +87 -0
  97. package/examples/immeuble/gap-rules/L2-exploitation.json +87 -0
  98. package/examples/immeuble/gap-rules/L2-finance.json +48 -0
  99. package/examples/immeuble/gap-rules/L2-maintenance.json +107 -0
  100. package/examples/immeuble/gap-rules/L2-syndic-filtered.json +39 -0
  101. package/examples/immeuble/gap-rules/L3-full.json +332 -0
  102. package/examples/immeuble/gap-rules/closed-world-contract.md +76 -0
  103. package/examples/immeuble/gap-rules/demo.json +51 -0
  104. package/examples/immeuble/gap-rules/expected-findings.yaml +100 -0
  105. package/examples/immeuble/gap-rules/gap-scenarios.yaml +79 -0
  106. package/examples/immeuble/gap-rules/motifs.json +38 -0
  107. package/examples/immeuble/gap-rules/syndic.json +40 -0
  108. package/examples/immeuble/import_manifest.yaml +25 -0
  109. package/examples/immeuble/import_ready/graph_edges_import.csv +848 -0
  110. package/examples/immeuble/import_ready/mfo_facets_import.csv +559 -0
  111. package/examples/immeuble/index.md +140 -0
  112. package/examples/immeuble/model/immeuble_model.json +418 -0
  113. package/examples/immeuble/reports/01-model.validation.json +56 -0
  114. package/examples/immeuble/reports/02-mapping.validation.json +9 -0
  115. package/examples/immeuble/reports/acceptance.validation.json +161 -0
  116. package/examples/immeuble/reports/consumer_contract.validation.json +162 -0
  117. package/examples/immeuble/reports/graph_edges.jsonl +847 -0
  118. package/examples/immeuble/reports/graph_nodes.jsonl +558 -0
  119. package/examples/immeuble/reports/hybrid-compare.json +144 -0
  120. package/examples/immeuble/reports/immeuble-import-scenario.json +233 -0
  121. package/examples/immeuble/reports/live-artifacts-refresh.validation.json +51 -0
  122. package/examples/immeuble/reports/pipeline_audit.json +59 -0
  123. package/examples/immeuble/reports/projection_audit.json +69 -0
  124. package/examples/immeuble/reports/projection_audit_immeuble.json +257 -0
  125. package/examples/immeuble/reports/projection_audit_immeuble.md +122 -0
  126. package/examples/immeuble/reports/projection_candidates.json +1596 -0
  127. package/examples/immeuble/reports/projection_candidates.md +117 -0
  128. package/examples/immeuble/reports/projection_model_validation.md +115 -0
  129. package/examples/immeuble/reports/reindex.json +4 -0
  130. package/examples/immeuble/reports/reset-immeuble-workspace.json +32 -0
  131. package/examples/immeuble/reports/schema-id-prefix-check.json +5 -0
  132. package/examples/immeuble/scripts/audit-immeuble-projections.mjs +254 -0
  133. package/examples/immeuble/scripts/build-immeuble-model.mjs +308 -0
  134. package/examples/immeuble/scripts/compare-immeuble-snapshots.sh +227 -0
  135. package/examples/immeuble/scripts/enrich-immeuble-demo.mjs +1135 -0
  136. package/examples/immeuble/scripts/reset-immeuble-workspace.mjs +139 -0
  137. package/examples/immeuble/scripts/run-immeuble-backend.sh +164 -0
  138. package/examples/immeuble/scripts/run-immeuble-import.mjs +232 -0
  139. package/examples/immeuble/scripts/run-immeuble-live-lab.sh +439 -0
  140. package/examples/immeuble/scripts/seed-immeuble-gap-rules.mjs +69 -0
  141. package/examples/immeuble/scripts/start-immeuble-demo.sh +52 -0
  142. package/examples/immeuble/scripts/starterkit/analysis-lenses.mjs +181 -0
  143. package/examples/immeuble/scripts/starterkit/analyze-projection-candidates.mjs +714 -0
  144. package/examples/immeuble/scripts/starterkit/audit-ghostcrab-projections.mjs +674 -0
  145. package/examples/immeuble/scripts/starterkit/facet-prefix.mjs +166 -0
  146. package/examples/immeuble/scripts/starterkit/sqlite-utils.mjs +131 -0
  147. package/examples/immeuble/scripts/verify-immeuble-acceptance.mjs +284 -0
  148. package/examples/immeuble/scripts/verify-immeuble-live-artifacts.mjs +140 -0
  149. package/examples/immeuble/scripts/yaml-lite.mjs +96 -0
  150. package/examples/immeuble/sources/agent-prompts/prompts/00-prerequisites-immo-mcp.md +161 -0
  151. package/examples/immeuble/sources/agent-prompts/prompts/00-prerequisites.md +39 -0
  152. package/examples/immeuble/sources/agent-prompts/prompts/01-discovery-and-model-proposal.md +42 -0
  153. package/examples/immeuble/sources/agent-prompts/prompts/02-ontology-register.md +44 -0
  154. package/examples/immeuble/sources/agent-prompts/prompts/03-gap-rules-design.md +44 -0
  155. package/examples/immeuble/sources/agent-prompts/prompts/04-document-ingest.md +40 -0
  156. package/examples/immeuble/sources/agent-prompts/prompts/05-graph-extraction.md +44 -0
  157. package/examples/immeuble/sources/agent-prompts/prompts/06-validate-and-compare-immo-mcp.md +109 -0
  158. package/examples/immeuble/sources/agent-prompts/prompts/06-validate-and-compare-test-immo-mcp3.md +30 -0
  159. package/examples/immeuble/sources/agent-prompts/prompts/06-validate-and-compare.md +63 -0
  160. package/examples/immeuble/sources/checklists/gap-rules-checklist.md +42 -0
  161. package/examples/immeuble/sources/checklists/ontology-checklist.md +54 -0
  162. package/examples/immeuble/sources/documents/README.md +42 -0
  163. package/examples/immeuble/sources/documents/annexes-caves-garages-jardins.md +8 -0
  164. package/examples/immeuble/sources/documents/annexes-jardins-garages.md +1 -0
  165. package/examples/immeuble/sources/documents/baux-erables.md +1 -0
  166. package/examples/immeuble/sources/documents/baux-locatifs.md +11 -0
  167. package/examples/immeuble/sources/documents/coda-janvier-2026.md +10 -0
  168. package/examples/immeuble/sources/documents/composition-menages.md +1 -0
  169. package/examples/immeuble/sources/documents/composition-occupants.md +18 -0
  170. package/examples/immeuble/sources/documents/expected-coverage.json +70 -0
  171. package/examples/immeuble/sources/documents/extrait-coda-janvier-2026.md +1 -0
  172. package/examples/immeuble/sources/documents/groupes-facturation.md +30 -0
  173. package/examples/immeuble/sources/documents/manifest.json +79 -0
  174. package/examples/immeuble/sources/documents/note-architecte-chantier-erables.md +3 -0
  175. package/examples/immeuble/sources/documents/permis-urbanisme-erables.md +3 -0
  176. package/examples/immeuble/sources/documents/procedures-operationnelles.md +3 -0
  177. package/examples/immeuble/sources/documents/pv-ag-budget-2026.md +1 -0
  178. package/examples/immeuble/sources/documents/pv-reception-reserves-erables.md +3 -0
  179. package/examples/immeuble/sources/documents/registre-coproprietaires.md +24 -0
  180. package/examples/immeuble/sources/documents/reglement-copropriete-tilleuls.md +1 -0
  181. package/examples/immeuble/sources/documents/statuts-erables.md +22 -0
  182. package/examples/immeuble/sources/documents/statuts-tilleuls.md +19 -0
  183. package/examples/immeuble/sources/documents/succession-jean-dupont.md +3 -0
  184. package/examples/immeuble/sources/documents/titre-propriete-tilleuls-a3.md +1 -0
  185. package/examples/immeuble/sources/documents/trous-pedagogiques.md +3 -0
  186. package/examples/immeuble/sources/ontology/README.md +1 -0
  187. package/examples/immeuble/sources/ontology/core.yaml +444 -0
  188. package/examples/immeuble/success-criteria.yaml +35 -0
  189. package/fixtures/immeuble-demo.sqlite +0 -0
  190. package/package.json +16 -3
  191. package/scripts/lib/sqlite-runtime.mjs +1 -1
  192. package/scripts/load-immeuble-demo.sh +103 -0
  193. package/scripts/seed-immeuble-projections.mjs +75 -148
  194. package/scripts/verify-immeuble-demo-sources.mjs +93 -0
  195. package/scripts/verify-immeuble-demo.mjs +69 -0
  196. package/build/client/_app/immutable/chunks/BmeSanva.js +0 -1
  197. package/build/client/_app/immutable/chunks/BmeSanva.js.br +0 -0
  198. package/build/client/_app/immutable/chunks/BmeSanva.js.gz +0 -0
  199. package/build/client/_app/immutable/entry/app.CVz6aYsT.js.br +0 -0
  200. package/build/client/_app/immutable/entry/app.CVz6aYsT.js.gz +0 -0
  201. package/build/client/_app/immutable/entry/start.Bt5tVOz8.js +0 -1
  202. package/build/client/_app/immutable/entry/start.Bt5tVOz8.js.br +0 -2
  203. package/build/client/_app/immutable/entry/start.Bt5tVOz8.js.gz +0 -0
  204. package/build/client/_app/immutable/nodes/1.BBtxY46Q.js.br +0 -0
  205. package/build/client/_app/immutable/nodes/1.BBtxY46Q.js.gz +0 -0
  206. package/build/server/chunks/entries/endpoints/api/graph/schema-registry/_server.ts.js-Dyfsc-VA.js.map +0 -1
  207. package/scripts/build-serenity-v6-concept-review-pack.mjs +0 -493
  208. package/scripts/build-serenity-v6-review-pack.mjs +0 -479
  209. package/scripts/create-serenity-production-v6.mjs +0 -627
  210. package/scripts/export-serenity-v6-backup.mjs +0 -178
  211. package/scripts/import-serenity-v6-user-decisions.mjs +0 -543
  212. package/scripts/materialize-serenity-v6-snapshots.mjs +0 -675
@@ -0,0 +1,139 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Reset a clean immeuble workspace and run the canonical pipeline.
4
+ *
5
+ * Usage:
6
+ * node examples/immeuble/scripts/reset-immeuble-workspace.mjs [options]
7
+ *
8
+ * Options:
9
+ * --db <path> SQLite target (default: data/immeuble.sqlite)
10
+ * --keep-db Do not delete existing file first
11
+ * --with-bundle-load After import, load bundle/immeuble.bundle.json
12
+ * --with-artifact-seed Seed answer artifacts via demo-load profile
13
+ * --with-business-capabilities Seed ghostcrab:business-capability records
14
+ * --engine legacy|both Import engine (default: legacy)
15
+ * --skip-provenance-validation
16
+ * --require-hybrid Fail if hybrid-compare missing or non-zero deltas
17
+ */
18
+
19
+ import { existsSync, mkdirSync, rmSync, writeFileSync } from "node:fs";
20
+ import { dirname, join, resolve } from "node:path";
21
+ import { fileURLToPath } from "node:url";
22
+ import { spawnSync } from "node:child_process";
23
+
24
+ const immeubleRoot = resolve(dirname(fileURLToPath(import.meta.url)), "..");
25
+ const pkgRoot = resolve(immeubleRoot, "..", "..");
26
+ const node = process.execPath;
27
+ const reportsDir = join(immeubleRoot, "reports");
28
+
29
+ const args = process.argv.slice(2);
30
+ const dbPath = resolve(parseFlag(args, "--db", join(pkgRoot, "data/immeuble.sqlite")));
31
+ const engine = parseFlag(args, "--engine", "legacy");
32
+ const keepDb = args.includes("--keep-db");
33
+ const withBundle = args.includes("--with-bundle-load");
34
+ const withArtifacts = args.includes("--with-artifact-seed");
35
+ const withBusinessCapabilities = args.includes("--with-business-capabilities");
36
+ const skipProvenance = args.includes("--skip-provenance-validation");
37
+ const requireHybrid = args.includes("--require-hybrid");
38
+
39
+ const report = {
40
+ ok: true,
41
+ db_path: dbPath,
42
+ steps: [],
43
+ started_at: new Date().toISOString()
44
+ };
45
+
46
+ try {
47
+ if (!keepDb && existsSync(dbPath)) {
48
+ rmSync(dbPath);
49
+ report.steps.push({ step: "delete_db", ok: true });
50
+ }
51
+ mkdirSync(dirname(dbPath), { recursive: true });
52
+
53
+ runStep("build", [join(immeubleRoot, "scripts/build-immeuble-model.mjs")]);
54
+
55
+ const importArgs = [
56
+ join(immeubleRoot, "scripts/run-immeuble-import.mjs"),
57
+ "--apply",
58
+ "--db", dbPath,
59
+ "--engine", engine,
60
+ "--skip-preflight",
61
+ "--force"
62
+ ];
63
+ if (skipProvenance) importArgs.push("--skip-provenance-validation");
64
+ if (engine === "both") {
65
+ importArgs.push("--compare-output", join(reportsDir, "hybrid-compare.json"));
66
+ }
67
+ runStep("import", importArgs);
68
+
69
+ if (withArtifacts) {
70
+ runStep("artifact_seed", [
71
+ join(pkgRoot, "bin/gcp.mjs"),
72
+ "load", join(immeubleRoot, "contracts/answer_artifacts.seed.jsonl"),
73
+ "--workspace", "immeuble"
74
+ ], { env: { GHOSTCRAB_SQLITE_PATH: dbPath } });
75
+ }
76
+
77
+ if (withBusinessCapabilities) {
78
+ runStep("business_capability_seed", [
79
+ join(pkgRoot, "bin/gcp.mjs"),
80
+ "load", join(immeubleRoot, "contracts/business_capabilities.seed.jsonl"),
81
+ "--workspace", "immeuble"
82
+ ], { env: { GHOSTCRAB_SQLITE_PATH: dbPath } });
83
+ }
84
+
85
+ if (withBundle) {
86
+ runStep("bundle_load", [
87
+ join(pkgRoot, "bin/gcp.mjs"),
88
+ "load", join(immeubleRoot, "bundle/immeuble.bundle.json"),
89
+ "--workspace", "immeuble",
90
+ "--reindex", "all"
91
+ ], { env: { GHOSTCRAB_SQLITE_PATH: dbPath } });
92
+ }
93
+
94
+ const verifyArgs = [
95
+ join(immeubleRoot, "scripts/verify-immeuble-acceptance.mjs"),
96
+ "--db", dbPath
97
+ ];
98
+ if (requireHybrid) verifyArgs.push("--require-hybrid");
99
+ if (withBundle) verifyArgs.push("--require-bundle");
100
+ if (withBusinessCapabilities) verifyArgs.push("--require-business-capabilities");
101
+ runStep("verify_acceptance", verifyArgs);
102
+
103
+ runStep("audit_projections", [join(immeubleRoot, "scripts/audit-immeuble-projections.mjs")], { optional: true });
104
+
105
+ report.finished_at = new Date().toISOString();
106
+ writeFileSync(join(reportsDir, "reset-immeuble-workspace.json"), JSON.stringify(report, null, 2) + "\n", "utf8");
107
+ console.log(JSON.stringify({ ok: true, db_path: dbPath, steps: report.steps.length }, null, 2));
108
+ } catch (err) {
109
+ report.ok = false;
110
+ report.error = err instanceof Error ? err.message : String(err);
111
+ report.finished_at = new Date().toISOString();
112
+ writeFileSync(join(reportsDir, "reset-immeuble-workspace.json"), JSON.stringify(report, null, 2) + "\n", "utf8");
113
+ console.error(JSON.stringify(report, null, 2));
114
+ process.exit(1);
115
+ }
116
+
117
+ function runStep(name, cmdArgs, opts = {}) {
118
+ const env = { ...process.env, ...(opts.env ?? {}), GHOSTCRAB_SQLITE_PATH: opts.env?.GHOSTCRAB_SQLITE_PATH ?? dbPath };
119
+ let res = spawnSync(node, cmdArgs, { cwd: pkgRoot, env, encoding: "utf8" });
120
+ if (res.status !== 0 && opts.fallback) {
121
+ res = spawnSync(opts.fallback[0], opts.fallback.slice(1), { cwd: pkgRoot, env, encoding: "utf8" });
122
+ }
123
+ const ok = res.status === 0;
124
+ report.steps.push({
125
+ step: name,
126
+ ok,
127
+ status: res.status,
128
+ stderr: (res.stderr || "").slice(0, 400)
129
+ });
130
+ if (!ok && !opts.optional) {
131
+ throw new Error(`${name} failed: ${res.stderr || res.stdout}`);
132
+ }
133
+ }
134
+
135
+ function parseFlag(argv, name, defaultValue) {
136
+ const index = argv.indexOf(name);
137
+ if (index === -1) return defaultValue;
138
+ return argv[index + 1] ?? defaultValue;
139
+ }
@@ -0,0 +1,164 @@
1
+ #!/usr/bin/env bash
2
+ # Launch the Immeuble backend in standalone HTTP mode for live pipelines.
3
+ set -eu
4
+
5
+ REPO_ROOT="$(CDPATH= cd -- "$(dirname -- "$0")/../../.." && pwd)"
6
+ IMMEUBLE_ROOT="$REPO_ROOT/examples/immeuble"
7
+
8
+ BACKEND_BIN="${GHOSTCRAB_BACKEND_BIN:-$REPO_ROOT/cmd/backend/zig-out/bin/ghostcrab-backend}"
9
+ BACKEND_ADDR=":8091"
10
+ DB_PATH="$REPO_ROOT/data/immeuble-lab.sqlite"
11
+ WORKSPACE_NAME="immeuble"
12
+ LOG_FILE="$IMMEUBLE_ROOT/.tmp-immeuble-backend.log"
13
+ READY_TIMEOUT_SEC=20
14
+ WAIT_FOR_HEALTH=1
15
+ FOREGROUND=0
16
+ KILL_PORT=1
17
+
18
+ usage() {
19
+ cat <<EOF
20
+ Usage: examples/immeuble/scripts/run-immeuble-backend.sh [options]
21
+
22
+ Run ghostcrab-backend in standalone HTTP mode with an Immeuble workspace context.
23
+
24
+ Options:
25
+ --db <path> SQLite file to use (default: $DB_PATH)
26
+ --backend-bin <path> ghostcrab-backend binary path
27
+ --backend-addr <:port> listen address (default: :8091)
28
+ --workspace-name <name> value for GHOSTCRAB_WORKSPACE_NAME (default: immeuble)
29
+ --log-file <path> where backend logs are written
30
+ --ready-timeout <sec> health-check timeout (default: 20)
31
+ --foreground keep backend attached in the current shell
32
+ --no-health-check do not wait for /health
33
+ --no-kill-port do not free the listen port before start
34
+ -h, --help show this help
35
+ EOF
36
+ }
37
+
38
+ for arg in "$@"; do
39
+ case "$arg" in
40
+ -h|--help)
41
+ usage
42
+ exit 0
43
+ ;;
44
+ esac
45
+ done
46
+
47
+ while [[ $# -gt 0 ]]; do
48
+ case "$1" in
49
+ --db)
50
+ DB_PATH="${2:?--db requires a value}"
51
+ shift 2
52
+ ;;
53
+ --backend-bin)
54
+ BACKEND_BIN="${2:?--backend-bin requires a value}"
55
+ shift 2
56
+ ;;
57
+ --backend-addr)
58
+ BACKEND_ADDR="${2:?--backend-addr requires a value}"
59
+ shift 2
60
+ ;;
61
+ --workspace-name)
62
+ WORKSPACE_NAME="${2:?--workspace-name requires a value}"
63
+ shift 2
64
+ ;;
65
+ --log-file)
66
+ LOG_FILE="${2:?--log-file requires a value}"
67
+ shift 2
68
+ ;;
69
+ --ready-timeout)
70
+ READY_TIMEOUT_SEC="${2:?--ready-timeout requires a value}"
71
+ shift 2
72
+ ;;
73
+ --no-health-check)
74
+ WAIT_FOR_HEALTH=0
75
+ shift
76
+ ;;
77
+ --foreground)
78
+ FOREGROUND=1
79
+ shift
80
+ ;;
81
+ --no-kill-port)
82
+ KILL_PORT=0
83
+ shift
84
+ ;;
85
+ *)
86
+ echo "[immeuble-backend] unknown argument: $1" >&2
87
+ usage
88
+ exit 1
89
+ ;;
90
+ esac
91
+ done
92
+
93
+ if [[ ! -x "$BACKEND_BIN" ]]; then
94
+ echo "[immeuble-backend] backend binary not found: $BACKEND_BIN" >&2
95
+ exit 1
96
+ fi
97
+
98
+ mkdir -p "$(dirname "$DB_PATH")"
99
+ mkdir -p "$(dirname "$LOG_FILE")"
100
+
101
+ BACKEND_PORT="${BACKEND_ADDR##*:}"
102
+ BACKEND_URL="http://127.0.0.1:${BACKEND_PORT}"
103
+
104
+ if (( KILL_PORT == 1 )) && command -v fuser >/dev/null 2>&1; then
105
+ fuser -k "${BACKEND_ADDR#:}/tcp" 2>/dev/null || true
106
+ sleep 0.2
107
+ fi
108
+
109
+ run_backend() {
110
+ env \
111
+ GHOSTCRAB_BACKEND_ADDR="$BACKEND_ADDR" \
112
+ GHOSTCRAB_SQLITE_PATH="$DB_PATH" \
113
+ GHOSTCRAB_WORKSPACE_NAME="$WORKSPACE_NAME" \
114
+ "$BACKEND_BIN"
115
+ }
116
+
117
+ if (( FOREGROUND == 1 )); then
118
+ echo "[immeuble-backend] starting foreground: $BACKEND_BIN -- $BACKEND_ADDR (db=$DB_PATH)"
119
+ if ! run_backend 2>&1 | tee "$LOG_FILE"; then
120
+ echo "[immeuble-backend] backend exited with failure" >&2
121
+ exit 1
122
+ fi
123
+ exit 0
124
+ fi
125
+
126
+ echo "[immeuble-backend] starting detached: $BACKEND_BIN -- $BACKEND_ADDR (db=$DB_PATH)"
127
+ run_backend >"$LOG_FILE" 2>&1 &
128
+ BACKEND_PID=$!
129
+
130
+ if [[ "$WAIT_FOR_HEALTH" -ne 0 ]]; then
131
+ ready=0
132
+ for attempt in $(seq 1 200); do
133
+ if curl -sf "$BACKEND_URL/health" >/dev/null 2>&1; then
134
+ ready=1
135
+ break
136
+ fi
137
+ if ! kill -0 "$BACKEND_PID" 2>/dev/null; then
138
+ echo "[immeuble-backend] backend exited before health check" >&2
139
+ cat "$LOG_FILE" >&2
140
+ exit 1
141
+ fi
142
+ if (( READY_TIMEOUT_SEC > 0 )); then
143
+ # Keep legacy 0.2s cadence and enforce timeout.
144
+ sleep 0.2
145
+ if (( attempt >= READY_TIMEOUT_SEC * 5 )); then
146
+ break
147
+ fi
148
+ else
149
+ sleep 0.2
150
+ fi
151
+ done
152
+
153
+ if [[ "$ready" -ne 1 ]]; then
154
+ echo "[immeuble-backend] backend did not become healthy at $BACKEND_URL after ${READY_TIMEOUT_SEC}s" >&2
155
+ cat "$LOG_FILE" >&2
156
+ exit 1
157
+ fi
158
+ fi
159
+
160
+ echo "[immeuble-backend] started"
161
+ echo "[immeuble-backend] BACKEND_PID=$BACKEND_PID"
162
+ echo "[immeuble-backend] BACKEND_URL=$BACKEND_URL"
163
+ echo "[immeuble-backend] BACKEND_DB=$DB_PATH"
164
+ echo "[immeuble-backend] BACKEND_LOG=$LOG_FILE"
@@ -0,0 +1,232 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Immeuble import scenario — plan, apply, reindex, provenance, schema prefix checks.
4
+ *
5
+ * Usage:
6
+ * node examples/immeuble/scripts/run-immeuble-import.mjs [--apply] [--engine both] ...
7
+ */
8
+
9
+ import { mkdirSync, mkdtempSync, readFileSync, writeFileSync } from "node:fs";
10
+ import { tmpdir } from "node:os";
11
+ import { join, resolve } from "node:path";
12
+ import { fileURLToPath } from "node:url";
13
+ import { spawnSync } from "node:child_process";
14
+
15
+ const immeubleRoot = resolve(fileURLToPath(import.meta.url), "..", "..");
16
+ const pkgRoot = resolve(immeubleRoot, "..", "..");
17
+ const gcp = join(pkgRoot, "bin", "gcp.mjs");
18
+ const runner = join(pkgRoot, "scripts", "run-structured-import-system.mjs");
19
+ const defaultManifest = join(immeubleRoot, "import_manifest.yaml");
20
+ const reportsDir = join(immeubleRoot, "reports");
21
+
22
+ const args = process.argv.slice(2);
23
+ const workspaceId = parseFlag(args, "--workspace-id", "immeuble");
24
+ const engine = parseFlag(args, "--engine", "legacy");
25
+ const manifestPath = parseFlag(args, "--manifest", defaultManifest);
26
+ const dbPath = parseFlag(
27
+ args,
28
+ "--db",
29
+ join(mkdtempSync(join(tmpdir(), "gcp-immeuble-")), "immeuble.sqlite")
30
+ );
31
+ const evidenceDir = parseFlag(args, "--evidence-dir", reportsDir);
32
+ const compareOutput = parseFlag(args, "--compare-output", join(reportsDir, "hybrid-compare.json"));
33
+ const runWithApply = args.includes("--apply");
34
+ const runWithSkipPreflight = args.includes("--skip-preflight");
35
+ const runWithPreflight = args.includes("--preflight");
36
+ const runWithSkipProvenance =
37
+ args.includes("--skip-provenance-validation") || args.includes("--no-validate-provenance");
38
+ const runWithForce = args.includes("--force");
39
+
40
+ mkdirSync(evidenceDir, { recursive: true });
41
+ mkdirSync(reportsDir, { recursive: true });
42
+
43
+ const evidence = {
44
+ workspace_id: workspaceId,
45
+ db_path: dbPath,
46
+ manifest: manifestPath,
47
+ engine,
48
+ apply: runWithApply,
49
+ preflight: runWithSkipPreflight ? "skipped" : runWithPreflight ? "forced" : "manifest-default",
50
+ provenance: runWithSkipProvenance ? "skipped" : "forced",
51
+ phases: [],
52
+ ok: true
53
+ };
54
+
55
+ try {
56
+ const plan = runRunner(false);
57
+ evidence.phases.push({ step: "plan", summary: plan.summary });
58
+
59
+ if (runWithApply) {
60
+ const apply = runRunner(true);
61
+ evidence.phases.push({ step: "apply", summary: apply.summary });
62
+
63
+ const reindexArgs = ["structured-import", "reindex", "--workspace-id", workspaceId, "--scope", "all"];
64
+ if (runWithForce) reindexArgs.splice(1, 0, "--force");
65
+ const reindex = runGcp(reindexArgs, true);
66
+ evidence.reindex = reindex.json;
67
+ writeFileSync(join(reportsDir, "reindex.json"), JSON.stringify(reindex.json, null, 2) + "\n", "utf8");
68
+
69
+ const applySummary = evidence.phases.find((phase) => phase.step === "apply")?.summary;
70
+ const hybridReindex = applySummary?.compare?.hybrid_summary ?? null;
71
+ const graphProjected =
72
+ typeof reindex.json?.graph_projected === "number" && reindex.json.graph_projected > 0
73
+ ? reindex.json.graph_projected
74
+ : typeof hybridReindex?.graph_projected === "number"
75
+ ? hybridReindex.graph_projected
76
+ : 0;
77
+
78
+ if (graphProjected <= 0) {
79
+ throw new Error(`reindex.graph_projected expected > 0, got ${reindex.json?.graph_projected ?? 0}`);
80
+ }
81
+
82
+ if (
83
+ (reindex.json?.graph_projected ?? 0) <= 0 &&
84
+ typeof hybridReindex?.graph_projected === "number" &&
85
+ hybridReindex.graph_projected > 0
86
+ ) {
87
+ evidence.reindex = {
88
+ ...reindex.json,
89
+ graph_projected: hybridReindex.graph_projected,
90
+ facet_assignments: hybridReindex.facet_assignments ?? reindex.json?.facet_assignments ?? 0,
91
+ source: "hybrid-apply"
92
+ };
93
+ }
94
+
95
+ const prefixCheck = assertSchemaIdPrefix(dbPath, workspaceId);
96
+ evidence.schema_id_prefix_check = prefixCheck;
97
+ writeFileSync(join(reportsDir, "schema-id-prefix-check.json"), JSON.stringify(prefixCheck, null, 2) + "\n", "utf8");
98
+ if (!prefixCheck.ok) {
99
+ throw new Error(`schema_id prefix violations: ${JSON.stringify(prefixCheck.violations.slice(0, 5))}`);
100
+ }
101
+
102
+ if (!runWithSkipProvenance) {
103
+ const provenance = runGcp(["structured-import", "validate-provenance", "--workspace-id", workspaceId], true);
104
+ evidence.provenance = provenance.json;
105
+ if (provenance.json?.ok !== true) {
106
+ throw new Error(`provenance validation failed: ${JSON.stringify(provenance.json)}`);
107
+ }
108
+ }
109
+ }
110
+
111
+ if (engine === "both") {
112
+ evidence.hybrid_compare_path = compareOutput;
113
+ }
114
+
115
+ const reportPath = join(evidenceDir, "immeuble-import-scenario.json");
116
+ evidence.report_path = reportPath;
117
+ writeFileSync(reportPath, JSON.stringify(evidence, null, 2) + "\n", "utf8");
118
+ console.log(JSON.stringify({ ...evidence, ok: true }, null, 2));
119
+ } catch (err) {
120
+ evidence.ok = false;
121
+ evidence.error = err instanceof Error ? err.message : String(err);
122
+ const reportPath = join(evidenceDir, "immeuble-import-scenario.json");
123
+ evidence.report_path = reportPath;
124
+ writeFileSync(reportPath, JSON.stringify(evidence, null, 2) + "\n", "utf8");
125
+ console.error(JSON.stringify({ ...evidence, ok: false }, null, 2));
126
+ process.exit(1);
127
+ }
128
+
129
+ function runRunner(includeApply) {
130
+ const runnerArgs = [
131
+ "--manifest", manifestPath,
132
+ "--workspace-id", workspaceId,
133
+ "--db", dbPath,
134
+ "--engine", engine
135
+ ];
136
+ if (compareOutput && engine === "both") {
137
+ runnerArgs.push("--compare-output", compareOutput);
138
+ }
139
+ if (runWithPreflight) runnerArgs.push("--preflight");
140
+ else if (runWithSkipPreflight) runnerArgs.push("--skip-preflight");
141
+ if (runWithSkipProvenance) runnerArgs.push("--skip-provenance-validation");
142
+ if (runWithForce) runnerArgs.push("--force");
143
+ if (includeApply) runnerArgs.push("--apply");
144
+
145
+ const res = runCommand(process.execPath, [runner, ...runnerArgs], "run-structured-import-system");
146
+ if (res.status !== 0) {
147
+ throw new Error(`${res.label} failed (${res.status}): ${res.output}`);
148
+ }
149
+ const summary = parseSummary(res.stdout);
150
+ assertActivity(summary, includeApply ? "apply" : "plan");
151
+ return { stdout: res.stdout, summary };
152
+ }
153
+
154
+ function runGcp(brainArgs, parseJson = false) {
155
+ const res = runCommand(process.execPath, [gcp, "brain", ...brainArgs], "gcp brain");
156
+ if (res.status !== 0) {
157
+ throw new Error(`${res.label} failed (${res.status}): ${res.output}`);
158
+ }
159
+ return { stdout: res.stdout, json: parseJson ? parseSummary(res.stdout) : null };
160
+ }
161
+
162
+ function runCommand(executable, cmdArgs, label) {
163
+ const direct = spawnSync(executable, cmdArgs, {
164
+ cwd: pkgRoot,
165
+ env: { ...process.env, GHOSTCRAB_SQLITE_PATH: dbPath },
166
+ encoding: "utf8"
167
+ });
168
+ const output = [direct.stdout || "", direct.stderr || ""].join("");
169
+ return { status: direct.status ?? 0, stdout: direct.stdout || "", output, label };
170
+ }
171
+
172
+ function parseSummary(text) {
173
+ if (typeof text !== "string" || !text.trim()) return null;
174
+ try { return JSON.parse(text.trim()); } catch { /* continue */ }
175
+ const lines = text.trim().split("\n");
176
+ for (let i = lines.length - 1; i >= 0; i--) {
177
+ const line = lines[i].trim();
178
+ if (line.startsWith("{")) {
179
+ try { return JSON.parse(lines.slice(i).join("\n")); } catch { /* continue */ }
180
+ }
181
+ }
182
+ return null;
183
+ }
184
+
185
+ function extractKitSummary(payload) {
186
+ if (!payload || typeof payload !== "object") return null;
187
+ if (payload.runs && typeof payload.runs === "object") {
188
+ const merged = {};
189
+ for (const run of Object.values(payload.runs)) {
190
+ const s = run?.summary_parsed ?? (typeof run?.summary === "string" ? parseSummary(run.summary) : run?.summary);
191
+ if (s && typeof s === "object") Object.assign(merged, s);
192
+ }
193
+ return Object.keys(merged).length ? merged : null;
194
+ }
195
+ return payload.project ?? payload.summary_parsed ?? (typeof payload.summary === "string" ? parseSummary(payload.summary) : payload.summary);
196
+ }
197
+
198
+ function assertActivity(summary, label) {
199
+ const kit = extractKitSummary(summary);
200
+ const total = [
201
+ "entities_upserted", "facets_inserted", "edges_inserted", "facet_rows", "edge_rows"
202
+ ].reduce((acc, key) => acc + (typeof kit?.[key] === "number" ? kit[key] : 0), 0);
203
+ if (total <= 0) {
204
+ throw new Error(`${label}: expected activity > 0, got ${JSON.stringify(kit)}`);
205
+ }
206
+ }
207
+
208
+ function assertSchemaIdPrefix(db, workspace) {
209
+ let sqlite3;
210
+ try {
211
+ sqlite3 = spawnSync("sqlite3", [db, "-json", `SELECT schema_id FROM agent_facts WHERE workspace_id='${workspace}'`], { encoding: "utf8" });
212
+ } catch {
213
+ return { ok: true, skipped: true, reason: "sqlite3 not available" };
214
+ }
215
+ if (sqlite3.status !== 0) {
216
+ return { ok: true, skipped: true, reason: sqlite3.stderr || "sqlite3 query failed" };
217
+ }
218
+ let rows = [];
219
+ try { rows = JSON.parse(sqlite3.stdout || "[]"); } catch { rows = []; }
220
+ const violations = rows.filter((r) => r.schema_id && !String(r.schema_id).startsWith("immeuble:"));
221
+ return {
222
+ ok: violations.length === 0,
223
+ checked: rows.length,
224
+ violations: violations.map((r) => r.schema_id)
225
+ };
226
+ }
227
+
228
+ function parseFlag(argv, name, defaultValue) {
229
+ const index = argv.indexOf(name);
230
+ if (index === -1) return defaultValue;
231
+ return argv[index + 1] ?? defaultValue;
232
+ }