@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.
- package/README.md +16 -8
- package/build/client/_app/immutable/chunks/CIErFlYG.js +1 -0
- package/build/client/_app/immutable/chunks/CIErFlYG.js.br +0 -0
- package/build/client/_app/immutable/chunks/CIErFlYG.js.gz +0 -0
- package/build/client/_app/immutable/entry/{app.CVz6aYsT.js → app.mURYm8o_.js} +2 -2
- package/build/client/_app/immutable/entry/app.mURYm8o_.js.br +0 -0
- package/build/client/_app/immutable/entry/app.mURYm8o_.js.gz +0 -0
- package/build/client/_app/immutable/entry/start.D4M9ZeGO.js +1 -0
- package/build/client/_app/immutable/entry/start.D4M9ZeGO.js.br +2 -0
- package/build/client/_app/immutable/entry/start.D4M9ZeGO.js.gz +0 -0
- package/build/client/_app/immutable/nodes/{1.BBtxY46Q.js → 1.DHKtMeFI.js} +1 -1
- package/build/client/_app/immutable/nodes/1.DHKtMeFI.js.br +1 -0
- package/build/client/_app/immutable/nodes/1.DHKtMeFI.js.gz +0 -0
- package/build/client/_app/version.json +1 -1
- package/build/client/_app/version.json.br +0 -0
- package/build/client/_app/version.json.gz +0 -0
- package/build/handler.js +4 -4
- package/build/index.js +4 -4
- package/build/server/chunks/chunks/{internal.js-D8EA_he2.js → internal.js-C3tV0XXj.js} +2 -2
- package/build/server/chunks/chunks/{internal.js-D8EA_he2.js.map → internal.js-C3tV0XXj.js.map} +1 -1
- package/build/server/chunks/entries/endpoints/api/graph/schema-registry/{_server.ts.js-Dyfsc-VA.js → _server.ts.js-CAsXxBRq.js} +7 -2
- package/build/server/chunks/entries/endpoints/api/graph/schema-registry/_server.ts.js-CAsXxBRq.js.map +1 -0
- package/build/server/chunks/{handler-BjXBCd1c.js → handler-DlaCCnxx.js} +3 -3
- package/build/server/chunks/{handler-BjXBCd1c.js.map → handler-DlaCCnxx.js.map} +1 -1
- package/build/server/chunks/{index.js-BJYcV8V7.js → index.js-BGfKWHak.js} +2 -2
- package/build/server/chunks/{index.js-BJYcV8V7.js.map → index.js-BGfKWHak.js.map} +1 -1
- package/build/server/chunks/{manifest.js-Ddaot0P4.js → manifest.js-CnmaNf5D.js} +4 -4
- package/build/server/chunks/{manifest.js-Ddaot0P4.js.map → manifest.js-CnmaNf5D.js.map} +1 -1
- package/build/server/chunks/nodes/{1.js-BRigw_9J.js → 1.js-BypjwBYB.js} +2 -2
- package/build/server/chunks/nodes/{1.js-BRigw_9J.js.map → 1.js-BypjwBYB.js.map} +1 -1
- package/examples/immeuble/ACCEPTANCE.yaml +82 -0
- package/examples/immeuble/BIM_LITE.md +15 -0
- package/examples/immeuble/CHECKLIST.md +128 -0
- package/examples/immeuble/README.md +121 -0
- package/examples/immeuble/bundle/immeuble.bundle.json +22786 -0
- package/examples/immeuble/contracts/answer_artifacts.seed.jsonl +34 -0
- package/examples/immeuble/contracts/business_capabilities.seed.jsonl +25 -0
- package/examples/immeuble/contracts/consumer_contract.yaml +131 -0
- package/examples/immeuble/contracts/immeuble_structured_import_model.json +310 -0
- package/examples/immeuble/contracts/mapping_external_to_canonical.json +375 -0
- package/examples/immeuble/contracts/mapping_external_to_canonical.yaml +55 -0
- package/examples/immeuble/contracts/mapping_external_to_canonical_ws.json +65 -0
- package/examples/immeuble/contracts/model_contract.json +943 -0
- package/examples/immeuble/contracts/projection_catalog.yaml +366 -0
- package/examples/immeuble/contracts/scenarios.yaml +27 -0
- package/examples/immeuble/contracts/semantic_proposal.golden.json +1 -0
- package/examples/immeuble/contracts/source_profile.yaml +38 -0
- package/examples/immeuble/fake_data/DeltaFinding.csv +20 -0
- package/examples/immeuble/fake_data/ProjectionResult.csv +13 -0
- package/examples/immeuble/fake_data/ag_meeting.csv +4 -0
- package/examples/immeuble/fake_data/agenda_item.csv +4 -0
- package/examples/immeuble/fake_data/architect.csv +3 -0
- package/examples/immeuble/fake_data/architecture_firm.csv +2 -0
- package/examples/immeuble/fake_data/bank_account.csv +3 -0
- package/examples/immeuble/fake_data/billing_group.csv +41 -0
- package/examples/immeuble/fake_data/block.csv +10 -0
- package/examples/immeuble/fake_data/budget_line.csv +3 -0
- package/examples/immeuble/fake_data/building.csv +6 -0
- package/examples/immeuble/fake_data/cellar.csv +41 -0
- package/examples/immeuble/fake_data/change_order.csv +2 -0
- package/examples/immeuble/fake_data/charge_call.csv +58 -0
- package/examples/immeuble/fake_data/claim.csv +4 -0
- package/examples/immeuble/fake_data/coda_entry.csv +49 -0
- package/examples/immeuble/fake_data/compliance_certificate.csv +10 -0
- package/examples/immeuble/fake_data/contractor.csv +4 -0
- package/examples/immeuble/fake_data/decision.csv +5 -0
- package/examples/immeuble/fake_data/defect_reserve.csv +3 -0
- package/examples/immeuble/fake_data/household.csv +41 -0
- package/examples/immeuble/fake_data/inspection.csv +3 -0
- package/examples/immeuble/fake_data/insurance_policy.csv +4 -0
- package/examples/immeuble/fake_data/intervention.csv +13 -0
- package/examples/immeuble/fake_data/invoice.csv +3 -0
- package/examples/immeuble/fake_data/lease_contract.csv +12 -0
- package/examples/immeuble/fake_data/maintenance_ticket.csv +13 -0
- package/examples/immeuble/fake_data/meter.csv +4 -0
- package/examples/immeuble/fake_data/meter_reading.csv +19 -0
- package/examples/immeuble/fake_data/milestone.csv +3 -0
- package/examples/immeuble/fake_data/organization.csv +12 -0
- package/examples/immeuble/fake_data/parking_space.csv +8 -0
- package/examples/immeuble/fake_data/permit.csv +3 -0
- package/examples/immeuble/fake_data/person.csv +85 -0
- package/examples/immeuble/fake_data/private_garden.csv +7 -0
- package/examples/immeuble/fake_data/progress_event.csv +2 -0
- package/examples/immeuble/fake_data/quote.csv +3 -0
- package/examples/immeuble/fake_data/receipt.csv +2 -0
- package/examples/immeuble/fake_data/reminder.csv +11 -0
- package/examples/immeuble/fake_data/service_contract.csv +4 -0
- package/examples/immeuble/fake_data/shared_equipment.csv +8 -0
- package/examples/immeuble/fake_data/shared_space.csv +10 -0
- package/examples/immeuble/fake_data/unit.csv +41 -0
- package/examples/immeuble/fake_data/work_package.csv +4 -0
- package/examples/immeuble/fake_data/worksite_project.csv +3 -0
- package/examples/immeuble/gap-rules/L0-patrimoine.json +57 -0
- package/examples/immeuble/gap-rules/L1-syndic-naive.json +36 -0
- package/examples/immeuble/gap-rules/L1-syndic.json +61 -0
- package/examples/immeuble/gap-rules/L2-chantier.json +87 -0
- package/examples/immeuble/gap-rules/L2-exploitation.json +87 -0
- package/examples/immeuble/gap-rules/L2-finance.json +48 -0
- package/examples/immeuble/gap-rules/L2-maintenance.json +107 -0
- package/examples/immeuble/gap-rules/L2-syndic-filtered.json +39 -0
- package/examples/immeuble/gap-rules/L3-full.json +332 -0
- package/examples/immeuble/gap-rules/closed-world-contract.md +76 -0
- package/examples/immeuble/gap-rules/demo.json +51 -0
- package/examples/immeuble/gap-rules/expected-findings.yaml +100 -0
- package/examples/immeuble/gap-rules/gap-scenarios.yaml +79 -0
- package/examples/immeuble/gap-rules/motifs.json +38 -0
- package/examples/immeuble/gap-rules/syndic.json +40 -0
- package/examples/immeuble/import_manifest.yaml +25 -0
- package/examples/immeuble/import_ready/graph_edges_import.csv +848 -0
- package/examples/immeuble/import_ready/mfo_facets_import.csv +559 -0
- package/examples/immeuble/index.md +140 -0
- package/examples/immeuble/model/immeuble_model.json +418 -0
- package/examples/immeuble/reports/01-model.validation.json +56 -0
- package/examples/immeuble/reports/02-mapping.validation.json +9 -0
- package/examples/immeuble/reports/acceptance.validation.json +161 -0
- package/examples/immeuble/reports/consumer_contract.validation.json +162 -0
- package/examples/immeuble/reports/graph_edges.jsonl +847 -0
- package/examples/immeuble/reports/graph_nodes.jsonl +558 -0
- package/examples/immeuble/reports/hybrid-compare.json +144 -0
- package/examples/immeuble/reports/immeuble-import-scenario.json +233 -0
- package/examples/immeuble/reports/live-artifacts-refresh.validation.json +51 -0
- package/examples/immeuble/reports/pipeline_audit.json +59 -0
- package/examples/immeuble/reports/projection_audit.json +69 -0
- package/examples/immeuble/reports/projection_audit_immeuble.json +257 -0
- package/examples/immeuble/reports/projection_audit_immeuble.md +122 -0
- package/examples/immeuble/reports/projection_candidates.json +1596 -0
- package/examples/immeuble/reports/projection_candidates.md +117 -0
- package/examples/immeuble/reports/projection_model_validation.md +115 -0
- package/examples/immeuble/reports/reindex.json +4 -0
- package/examples/immeuble/reports/reset-immeuble-workspace.json +32 -0
- package/examples/immeuble/reports/schema-id-prefix-check.json +5 -0
- package/examples/immeuble/scripts/audit-immeuble-projections.mjs +254 -0
- package/examples/immeuble/scripts/build-immeuble-model.mjs +308 -0
- package/examples/immeuble/scripts/compare-immeuble-snapshots.sh +227 -0
- package/examples/immeuble/scripts/enrich-immeuble-demo.mjs +1135 -0
- package/examples/immeuble/scripts/reset-immeuble-workspace.mjs +139 -0
- package/examples/immeuble/scripts/run-immeuble-backend.sh +164 -0
- package/examples/immeuble/scripts/run-immeuble-import.mjs +232 -0
- package/examples/immeuble/scripts/run-immeuble-live-lab.sh +439 -0
- package/examples/immeuble/scripts/seed-immeuble-gap-rules.mjs +69 -0
- package/examples/immeuble/scripts/start-immeuble-demo.sh +52 -0
- package/examples/immeuble/scripts/starterkit/analysis-lenses.mjs +181 -0
- package/examples/immeuble/scripts/starterkit/analyze-projection-candidates.mjs +714 -0
- package/examples/immeuble/scripts/starterkit/audit-ghostcrab-projections.mjs +674 -0
- package/examples/immeuble/scripts/starterkit/facet-prefix.mjs +166 -0
- package/examples/immeuble/scripts/starterkit/sqlite-utils.mjs +131 -0
- package/examples/immeuble/scripts/verify-immeuble-acceptance.mjs +284 -0
- package/examples/immeuble/scripts/verify-immeuble-live-artifacts.mjs +140 -0
- package/examples/immeuble/scripts/yaml-lite.mjs +96 -0
- package/examples/immeuble/sources/agent-prompts/prompts/00-prerequisites-immo-mcp.md +161 -0
- package/examples/immeuble/sources/agent-prompts/prompts/00-prerequisites.md +39 -0
- package/examples/immeuble/sources/agent-prompts/prompts/01-discovery-and-model-proposal.md +42 -0
- package/examples/immeuble/sources/agent-prompts/prompts/02-ontology-register.md +44 -0
- package/examples/immeuble/sources/agent-prompts/prompts/03-gap-rules-design.md +44 -0
- package/examples/immeuble/sources/agent-prompts/prompts/04-document-ingest.md +40 -0
- package/examples/immeuble/sources/agent-prompts/prompts/05-graph-extraction.md +44 -0
- package/examples/immeuble/sources/agent-prompts/prompts/06-validate-and-compare-immo-mcp.md +109 -0
- package/examples/immeuble/sources/agent-prompts/prompts/06-validate-and-compare-test-immo-mcp3.md +30 -0
- package/examples/immeuble/sources/agent-prompts/prompts/06-validate-and-compare.md +63 -0
- package/examples/immeuble/sources/checklists/gap-rules-checklist.md +42 -0
- package/examples/immeuble/sources/checklists/ontology-checklist.md +54 -0
- package/examples/immeuble/sources/documents/README.md +42 -0
- package/examples/immeuble/sources/documents/annexes-caves-garages-jardins.md +8 -0
- package/examples/immeuble/sources/documents/annexes-jardins-garages.md +1 -0
- package/examples/immeuble/sources/documents/baux-erables.md +1 -0
- package/examples/immeuble/sources/documents/baux-locatifs.md +11 -0
- package/examples/immeuble/sources/documents/coda-janvier-2026.md +10 -0
- package/examples/immeuble/sources/documents/composition-menages.md +1 -0
- package/examples/immeuble/sources/documents/composition-occupants.md +18 -0
- package/examples/immeuble/sources/documents/expected-coverage.json +70 -0
- package/examples/immeuble/sources/documents/extrait-coda-janvier-2026.md +1 -0
- package/examples/immeuble/sources/documents/groupes-facturation.md +30 -0
- package/examples/immeuble/sources/documents/manifest.json +79 -0
- package/examples/immeuble/sources/documents/note-architecte-chantier-erables.md +3 -0
- package/examples/immeuble/sources/documents/permis-urbanisme-erables.md +3 -0
- package/examples/immeuble/sources/documents/procedures-operationnelles.md +3 -0
- package/examples/immeuble/sources/documents/pv-ag-budget-2026.md +1 -0
- package/examples/immeuble/sources/documents/pv-reception-reserves-erables.md +3 -0
- package/examples/immeuble/sources/documents/registre-coproprietaires.md +24 -0
- package/examples/immeuble/sources/documents/reglement-copropriete-tilleuls.md +1 -0
- package/examples/immeuble/sources/documents/statuts-erables.md +22 -0
- package/examples/immeuble/sources/documents/statuts-tilleuls.md +19 -0
- package/examples/immeuble/sources/documents/succession-jean-dupont.md +3 -0
- package/examples/immeuble/sources/documents/titre-propriete-tilleuls-a3.md +1 -0
- package/examples/immeuble/sources/documents/trous-pedagogiques.md +3 -0
- package/examples/immeuble/sources/ontology/README.md +1 -0
- package/examples/immeuble/sources/ontology/core.yaml +444 -0
- package/examples/immeuble/success-criteria.yaml +35 -0
- package/fixtures/immeuble-demo.sqlite +0 -0
- package/package.json +16 -3
- package/scripts/lib/sqlite-runtime.mjs +1 -1
- package/scripts/load-immeuble-demo.sh +103 -0
- package/scripts/seed-immeuble-projections.mjs +75 -148
- package/scripts/verify-immeuble-demo-sources.mjs +93 -0
- package/scripts/verify-immeuble-demo.mjs +69 -0
- package/build/client/_app/immutable/chunks/BmeSanva.js +0 -1
- package/build/client/_app/immutable/chunks/BmeSanva.js.br +0 -0
- package/build/client/_app/immutable/chunks/BmeSanva.js.gz +0 -0
- package/build/client/_app/immutable/entry/app.CVz6aYsT.js.br +0 -0
- package/build/client/_app/immutable/entry/app.CVz6aYsT.js.gz +0 -0
- package/build/client/_app/immutable/entry/start.Bt5tVOz8.js +0 -1
- package/build/client/_app/immutable/entry/start.Bt5tVOz8.js.br +0 -2
- package/build/client/_app/immutable/entry/start.Bt5tVOz8.js.gz +0 -0
- package/build/client/_app/immutable/nodes/1.BBtxY46Q.js.br +0 -0
- package/build/client/_app/immutable/nodes/1.BBtxY46Q.js.gz +0 -0
- package/build/server/chunks/entries/endpoints/api/graph/schema-registry/_server.ts.js-Dyfsc-VA.js.map +0 -1
- package/scripts/build-serenity-v6-concept-review-pack.mjs +0 -493
- package/scripts/build-serenity-v6-review-pack.mjs +0 -479
- package/scripts/create-serenity-production-v6.mjs +0 -627
- package/scripts/export-serenity-v6-backup.mjs +0 -178
- package/scripts/import-serenity-v6-user-decisions.mjs +0 -543
- package/scripts/materialize-serenity-v6-snapshots.mjs +0 -675
|
@@ -1,675 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import { copyFileSync, mkdirSync, writeFileSync } from 'node:fs';
|
|
3
|
-
import { join } from 'node:path';
|
|
4
|
-
import { spawnSync } from 'node:child_process';
|
|
5
|
-
|
|
6
|
-
const WORKSPACE = 'serenity-production-v6';
|
|
7
|
-
const DEFAULT_DB = '/home/dlamotte/.ghostcrab/databases/ghostcrab-serenity-v4-demos.sqlite';
|
|
8
|
-
const DEFAULT_BACKUP_DIR = '/tmp/serenity-v6-snapshot-backups';
|
|
9
|
-
const GENERATOR = 'serenity-v6-snapshot-generator';
|
|
10
|
-
const args = new Set(process.argv.slice(2));
|
|
11
|
-
const apply = args.has('--apply');
|
|
12
|
-
const verbose = args.has('--verbose');
|
|
13
|
-
const emitSqlPath = valueAfter('--emit-sql');
|
|
14
|
-
const dbPath = valueAfter('--db') ?? process.env.GHOSTCRAB_SQLITE_PATH ?? DEFAULT_DB;
|
|
15
|
-
const backupDir = valueAfter('--backup-dir') ?? DEFAULT_BACKUP_DIR;
|
|
16
|
-
const now = Math.floor(Date.now() / 1000);
|
|
17
|
-
|
|
18
|
-
const NEW_SCENARIOS = [
|
|
19
|
-
{
|
|
20
|
-
scenario_id: 'scn.v6.mandataire_appel_fonds_contact',
|
|
21
|
-
projection_id: 'scenario_v6_mandataire_appel_fonds_contact',
|
|
22
|
-
public_label: 'Mandataire contacte avec les proprietaires lors d un appel de fonds',
|
|
23
|
-
business_question:
|
|
24
|
-
"Qui doit recevoir l'appel de fonds quand un mandataire represente plusieurs lots et comment prouver que le calcul reste rattache aux bons proprietaires ?",
|
|
25
|
-
copropriete: 'copropriete.tilleuls',
|
|
26
|
-
workflow: 'fund_call_delegated_contact',
|
|
27
|
-
records: [
|
|
28
|
-
['mandataire', 'Mandataire Dupont famille Tilleuls'],
|
|
29
|
-
['personne', 'Claire Dupont mandataire'],
|
|
30
|
-
['lot', 'Lot A3 represente par mandataire'],
|
|
31
|
-
['fund_call', 'Appel FDRO delegue Tilleuls T2'],
|
|
32
|
-
['fund_call_line', 'Ligne appel fonds lot A3 mandataire'],
|
|
33
|
-
['preuve_documentaire', 'Courrier de mandat et preuve d envoi']
|
|
34
|
-
],
|
|
35
|
-
statuses: [
|
|
36
|
-
'mandat: received -> verified -> active',
|
|
37
|
-
'fund_call: draft -> computed -> emitted',
|
|
38
|
-
'notification: prepared -> sent_to_owner_and_mandataire -> acknowledged'
|
|
39
|
-
],
|
|
40
|
-
requirements: [
|
|
41
|
-
'fund_call.contacts_mandataire',
|
|
42
|
-
'mandataire.represents_lot',
|
|
43
|
-
'fund_call_line.account_for_lot'
|
|
44
|
-
]
|
|
45
|
-
},
|
|
46
|
-
{
|
|
47
|
-
scenario_id: 'scn.v6.offre_prix_revision_bon_commande',
|
|
48
|
-
projection_id: 'scenario_v6_offre_prix_revision_bon_commande',
|
|
49
|
-
public_label: 'Offre de prix revisee puis acceptee comme bon de commande',
|
|
50
|
-
business_question:
|
|
51
|
-
"Quelle version du devis a ete acceptee, quel montant engage-t-elle et quel bon de commande en decoule ?",
|
|
52
|
-
copropriete: 'copropriete.horizon',
|
|
53
|
-
workflow: 'quote_revision_to_purchase_order',
|
|
54
|
-
records: [
|
|
55
|
-
['demande_travaux', 'Demande remplacement parlophone Galerie Horizon'],
|
|
56
|
-
['offre_prix', 'Offre prix initiale Parlophone SA'],
|
|
57
|
-
['offre_prix', 'Offre prix revisee Parlophone SA'],
|
|
58
|
-
['bon_commande', 'Bon commande parlophone Horizon'],
|
|
59
|
-
['supplier_intervention', 'Intervention planifiee parlophone Horizon'],
|
|
60
|
-
['preuve_documentaire', 'PV acceptation devis revise']
|
|
61
|
-
],
|
|
62
|
-
statuses: [
|
|
63
|
-
'quote: requested -> received -> revised -> accepted',
|
|
64
|
-
'purchase_order: draft -> signed -> sent_to_supplier',
|
|
65
|
-
'intervention: planned -> waiting_supplier'
|
|
66
|
-
],
|
|
67
|
-
requirements: [
|
|
68
|
-
'demande_travaux.requests_quote',
|
|
69
|
-
'offre_prix.revised_as',
|
|
70
|
-
'offre_prix.accepted_as_purchase_order'
|
|
71
|
-
]
|
|
72
|
-
},
|
|
73
|
-
{
|
|
74
|
-
scenario_id: 'scn.v6.extrait_bancaire_rapprochement',
|
|
75
|
-
projection_id: 'scenario_v6_extrait_bancaire_rapprochement',
|
|
76
|
-
public_label: 'Extrait bancaire rapproche avec ecriture comptable et facture',
|
|
77
|
-
business_question:
|
|
78
|
-
"Quel mouvement bancaire justifie le paiement, quelle facture est soldee et quelle ecriture comptable ferme le rapprochement ?",
|
|
79
|
-
copropriete: 'copropriete.erables',
|
|
80
|
-
workflow: 'bank_statement_reconciliation',
|
|
81
|
-
records: [
|
|
82
|
-
['extrait_compte_bancaire', 'Extrait bancaire Erables mars 2026'],
|
|
83
|
-
['bank_movement', 'Mouvement paiement chaufferie mars 2026'],
|
|
84
|
-
['supplier_invoice', 'Facture chaufferie Erables mars 2026'],
|
|
85
|
-
['accounting_entry', 'Ecriture paiement facture chaufferie'],
|
|
86
|
-
['payment_reconciliation', 'Rapprochement facture paiement chaufferie'],
|
|
87
|
-
['preuve_documentaire', 'PDF extrait bancaire et facture fournisseur']
|
|
88
|
-
],
|
|
89
|
-
statuses: [
|
|
90
|
-
'bank_statement: imported -> parsed -> reconciled',
|
|
91
|
-
'invoice: received -> booked -> paid',
|
|
92
|
-
'reconciliation: candidate -> matched -> verified'
|
|
93
|
-
],
|
|
94
|
-
requirements: [
|
|
95
|
-
'extrait_compte_bancaire.documents_account',
|
|
96
|
-
'bank_movement.pays_invoice',
|
|
97
|
-
'accounting_entry.supports_reconciliation'
|
|
98
|
-
]
|
|
99
|
-
}
|
|
100
|
-
];
|
|
101
|
-
|
|
102
|
-
function valueAfter(flag) {
|
|
103
|
-
const index = process.argv.indexOf(flag);
|
|
104
|
-
return index >= 0 ? process.argv[index + 1] : null;
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
function runSql(sql, { json = false, readonly = false } = {}) {
|
|
108
|
-
const argv = [];
|
|
109
|
-
if (readonly) argv.push('-readonly');
|
|
110
|
-
if (json) argv.push('-json');
|
|
111
|
-
argv.push(dbPath, sql);
|
|
112
|
-
const result = spawnSync('sqlite3', argv, { encoding: 'utf8', maxBuffer: 1024 * 1024 * 128 });
|
|
113
|
-
if (result.status !== 0) throw new Error(result.stderr || result.stdout || `sqlite3 failed: ${sql.slice(0, 180)}`);
|
|
114
|
-
return json ? JSON.parse(result.stdout || '[]') : result.stdout.trim();
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
function runSqlFile(sql) {
|
|
118
|
-
if (verbose) console.error(`[${GENERATOR}] applying sql bytes=${Buffer.byteLength(sql, 'utf8')}`);
|
|
119
|
-
mkdirSync(backupDir, { recursive: true });
|
|
120
|
-
const sqlPath = join(backupDir, `${WORKSPACE}-${process.pid}-${Date.now()}.sql`);
|
|
121
|
-
writeFileSync(sqlPath, sql);
|
|
122
|
-
const result = spawnSync('sqlite3', [dbPath, `.read ${sqlPath}`], {
|
|
123
|
-
encoding: 'utf8',
|
|
124
|
-
maxBuffer: 1024 * 1024 * 128
|
|
125
|
-
});
|
|
126
|
-
if (result.status !== 0) throw new Error(result.stderr || result.stdout || 'sqlite3 transaction failed');
|
|
127
|
-
return result.stdout;
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
function q(value) {
|
|
131
|
-
if (value === null || value === undefined) return 'NULL';
|
|
132
|
-
if (typeof value === 'number') return Number.isFinite(value) ? String(value) : 'NULL';
|
|
133
|
-
if (typeof value === 'boolean') return value ? '1' : '0';
|
|
134
|
-
return `'${String(value).replaceAll("'", "''")}'`;
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
function slug(value) {
|
|
138
|
-
return String(value)
|
|
139
|
-
.toLowerCase()
|
|
140
|
-
.normalize('NFD')
|
|
141
|
-
.replace(/[\u0300-\u036f]/g, '')
|
|
142
|
-
.replace(/[^a-z0-9]+/g, '_')
|
|
143
|
-
.replace(/^_+|_+$/g, '');
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
function parseJson(value, fallback = {}) {
|
|
147
|
-
try {
|
|
148
|
-
return JSON.parse(value || '');
|
|
149
|
-
} catch {
|
|
150
|
-
return fallback;
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
function jsonSql(value) {
|
|
155
|
-
return q(JSON.stringify(value));
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
function readScenarioPlans() {
|
|
159
|
-
return runSql(
|
|
160
|
-
`
|
|
161
|
-
SELECT artifact_id, slug, public_label, scope, payload_json
|
|
162
|
-
FROM mindbrain_answer_artifacts
|
|
163
|
-
WHERE workspace_id=${q(WORKSPACE)}
|
|
164
|
-
AND artifact_kind='analysis_plan'
|
|
165
|
-
AND scope LIKE ${q(`${WORKSPACE}:scenario:%`)}
|
|
166
|
-
ORDER BY artifact_id;
|
|
167
|
-
`,
|
|
168
|
-
{ json: true, readonly: true }
|
|
169
|
-
).map((row) => {
|
|
170
|
-
const payload = parseJson(row.payload_json);
|
|
171
|
-
const refs = Array.isArray(payload.scenario_refs) ? payload.scenario_refs : [];
|
|
172
|
-
const scenarioId = refs[0] ?? payload.projection_id ?? row.slug;
|
|
173
|
-
return {
|
|
174
|
-
source: 'existing',
|
|
175
|
-
artifact_id: row.artifact_id,
|
|
176
|
-
slug: row.slug,
|
|
177
|
-
public_label: row.public_label,
|
|
178
|
-
scope: row.scope,
|
|
179
|
-
projection_id: payload.projection_id ?? slug(row.public_label),
|
|
180
|
-
business_question: payload.business_question ?? payload.summary ?? row.public_label,
|
|
181
|
-
scenario_id: scenarioId,
|
|
182
|
-
required_edges: Array.isArray(payload.required_edges) ? payload.required_edges : [],
|
|
183
|
-
required_facets: Array.isArray(payload.required_facets) ? payload.required_facets : [],
|
|
184
|
-
scenario_refs: refs
|
|
185
|
-
};
|
|
186
|
-
});
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
function readScenarioEntityMap() {
|
|
190
|
-
const rows = runSql(
|
|
191
|
-
`
|
|
192
|
-
SELECT entity_id, entity_type, name, metadata_json
|
|
193
|
-
FROM graph_entity
|
|
194
|
-
WHERE workspace_id=${q(WORKSPACE)}
|
|
195
|
-
AND (
|
|
196
|
-
entity_type='scenario_gestion'
|
|
197
|
-
OR json_extract(metadata_json, '$.scenario_id') IS NOT NULL
|
|
198
|
-
)
|
|
199
|
-
ORDER BY entity_id;
|
|
200
|
-
`,
|
|
201
|
-
{ json: true, readonly: true }
|
|
202
|
-
);
|
|
203
|
-
const byScenario = new Map();
|
|
204
|
-
for (const row of rows) {
|
|
205
|
-
const metadata = parseJson(row.metadata_json);
|
|
206
|
-
const scenarioId = metadata.scenario_id ?? (row.entity_type === 'scenario_gestion' ? row.name : null);
|
|
207
|
-
if (!scenarioId) continue;
|
|
208
|
-
if (!byScenario.has(scenarioId)) byScenario.set(scenarioId, []);
|
|
209
|
-
byScenario.get(scenarioId).push({
|
|
210
|
-
entity_id: Number(row.entity_id),
|
|
211
|
-
entity_type: row.entity_type,
|
|
212
|
-
name: row.name,
|
|
213
|
-
metadata
|
|
214
|
-
});
|
|
215
|
-
}
|
|
216
|
-
return byScenario;
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
function readScenarioRelationCounts() {
|
|
220
|
-
const rows = runSql(
|
|
221
|
-
`
|
|
222
|
-
SELECT s.name AS scenario_id, COUNT(r.relation_id) AS relation_count,
|
|
223
|
-
group_concat(DISTINCT r.relation_type) AS relation_types
|
|
224
|
-
FROM graph_entity s
|
|
225
|
-
LEFT JOIN graph_relation r ON r.workspace_id=s.workspace_id AND r.source_id=s.entity_id
|
|
226
|
-
WHERE s.workspace_id=${q(WORKSPACE)}
|
|
227
|
-
AND s.entity_type='scenario_gestion'
|
|
228
|
-
GROUP BY s.entity_id, s.name;
|
|
229
|
-
`,
|
|
230
|
-
{ json: true, readonly: true }
|
|
231
|
-
);
|
|
232
|
-
return new Map(
|
|
233
|
-
rows.map((row) => [
|
|
234
|
-
row.scenario_id,
|
|
235
|
-
{
|
|
236
|
-
relation_count: Number(row.relation_count ?? 0),
|
|
237
|
-
relation_types: row.relation_types ? String(row.relation_types).split(',') : []
|
|
238
|
-
}
|
|
239
|
-
])
|
|
240
|
-
);
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
function newScenarioPlan(def) {
|
|
244
|
-
return {
|
|
245
|
-
source: 'generated',
|
|
246
|
-
artifact_id: `analysis_plan__${WORKSPACE.replaceAll('-', '_')}_scenario_${def.projection_id}`,
|
|
247
|
-
slug: `${WORKSPACE.replaceAll('-', '_')}_scenario_${def.projection_id}`,
|
|
248
|
-
public_label: def.public_label,
|
|
249
|
-
scope: `${WORKSPACE}:scenario:${def.projection_id}`,
|
|
250
|
-
projection_id: def.projection_id,
|
|
251
|
-
business_question: def.business_question,
|
|
252
|
-
scenario_id: def.scenario_id,
|
|
253
|
-
required_edges: ['scenario_requires_record', 'scenario_requires_relation', 'scenario_has_status_event', 'scenario_for_copropriete'],
|
|
254
|
-
required_facets: ['status', 'scenario_status', 'copropriete_id', 'actor_refs', 'evidence_refs'],
|
|
255
|
-
scenario_refs: [def.scenario_id],
|
|
256
|
-
def
|
|
257
|
-
};
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
function syntheticRuntimeRows(def) {
|
|
261
|
-
const rows = [{ entity_type: 'scenario_gestion', name: def.scenario_id, role: 'scenario' }];
|
|
262
|
-
for (let index = 0; index < def.records.length; index += 1) {
|
|
263
|
-
const [entityType, label] = def.records[index];
|
|
264
|
-
rows.push({
|
|
265
|
-
entity_type: entityType,
|
|
266
|
-
name: `${def.scenario_id}.${slug(entityType)}.${String(index + 1).padStart(2, '0')}`,
|
|
267
|
-
label,
|
|
268
|
-
role: 'runtime_record'
|
|
269
|
-
});
|
|
270
|
-
}
|
|
271
|
-
for (let index = 0; index < def.statuses.length; index += 1) {
|
|
272
|
-
rows.push({
|
|
273
|
-
entity_type: 'status_event',
|
|
274
|
-
name: `${def.scenario_id}.status_event.${String(index + 1).padStart(2, '0')}`,
|
|
275
|
-
label: def.statuses[index],
|
|
276
|
-
role: 'status_event',
|
|
277
|
-
transition: def.statuses[index]
|
|
278
|
-
});
|
|
279
|
-
}
|
|
280
|
-
for (let index = 0; index < def.requirements.length; index += 1) {
|
|
281
|
-
rows.push({
|
|
282
|
-
entity_type: 'relation_requirement',
|
|
283
|
-
name: `${def.scenario_id}.relation_requirement.${String(index + 1).padStart(2, '0')}`,
|
|
284
|
-
label: def.requirements[index],
|
|
285
|
-
role: 'relation_requirement',
|
|
286
|
-
relation_requirement: def.requirements[index]
|
|
287
|
-
});
|
|
288
|
-
}
|
|
289
|
-
return rows;
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
function syntheticEntityNameSql(name) {
|
|
293
|
-
return `(SELECT entity_id FROM graph_entity WHERE workspace_id=${q(WORKSPACE)} AND name=${q(name)} LIMIT 1)`;
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
function buildNewScenarioSql(plan) {
|
|
297
|
-
const def = plan.def;
|
|
298
|
-
const statements = [];
|
|
299
|
-
const basePayload = {
|
|
300
|
-
projection_id: plan.projection_id,
|
|
301
|
-
source: `${GENERATOR}.analysis_plan`,
|
|
302
|
-
origin: 'synthetic_scenario',
|
|
303
|
-
decision_level: 'operational_scenario',
|
|
304
|
-
artifact_kind: 'analysis_plan',
|
|
305
|
-
proj_type: 'STEP',
|
|
306
|
-
scope: plan.scope,
|
|
307
|
-
label: plan.public_label,
|
|
308
|
-
business_question: plan.business_question,
|
|
309
|
-
description: def.public_label,
|
|
310
|
-
required_schemas: [],
|
|
311
|
-
required_facets: plan.required_facets,
|
|
312
|
-
required_edges: plan.required_edges,
|
|
313
|
-
retrieval_jobs: ['scenario_state', 'scenario_evidence'],
|
|
314
|
-
status_filter_policy: 'current_by_default',
|
|
315
|
-
scenario_refs: plan.scenario_refs,
|
|
316
|
-
synthetic: true,
|
|
317
|
-
generated_by: GENERATOR
|
|
318
|
-
};
|
|
319
|
-
statements.push(`
|
|
320
|
-
INSERT INTO mindbrain_answer_artifacts (
|
|
321
|
-
artifact_id, slug, workspace_id, agent_id, scope, artifact_kind,
|
|
322
|
-
public_label_key, public_label, lifecycle, state, current_version,
|
|
323
|
-
payload_json, legacy_ref, created_at_unix, updated_at_unix
|
|
324
|
-
)
|
|
325
|
-
VALUES (
|
|
326
|
-
${q(plan.artifact_id)}, ${q(plan.slug)}, ${q(WORKSPACE)}, ${q(`agent:${WORKSPACE}:snapshot-generator`)},
|
|
327
|
-
${q(plan.scope)}, 'analysis_plan', ${q(`analysis_plan.${WORKSPACE}.${plan.projection_id}`)},
|
|
328
|
-
${q(plan.public_label)}, 'active', 'open', 1, ${jsonSql(basePayload)}, NULL, ${now}, ${now}
|
|
329
|
-
)
|
|
330
|
-
ON CONFLICT(artifact_id) DO UPDATE SET
|
|
331
|
-
public_label=excluded.public_label,
|
|
332
|
-
scope=excluded.scope,
|
|
333
|
-
payload_json=excluded.payload_json,
|
|
334
|
-
updated_at_unix=excluded.updated_at_unix;
|
|
335
|
-
`);
|
|
336
|
-
statements.push(`
|
|
337
|
-
INSERT INTO mindbrain_answer_artifacts (
|
|
338
|
-
artifact_id, slug, workspace_id, agent_id, scope, artifact_kind,
|
|
339
|
-
public_label_key, public_label, lifecycle, state, current_version,
|
|
340
|
-
payload_json, legacy_ref, created_at_unix, updated_at_unix
|
|
341
|
-
)
|
|
342
|
-
VALUES (
|
|
343
|
-
${q(`live_answer_view__${plan.slug}`)}, ${q(plan.slug)}, ${q(WORKSPACE)}, NULL, NULL,
|
|
344
|
-
'live_answer_view', ${q(`live_answer_view.${WORKSPACE}.${plan.projection_id}`)},
|
|
345
|
-
${q(plan.public_label)}, 'active', 'refreshed', 1,
|
|
346
|
-
${jsonSql({
|
|
347
|
-
source_plan_id: plan.artifact_id,
|
|
348
|
-
source_plan_scope: plan.scope,
|
|
349
|
-
projection_id: plan.projection_id,
|
|
350
|
-
summary: plan.business_question,
|
|
351
|
-
refresh_checks: ['graph relation coverage', 'status filter coverage', 'required evidence coverage'],
|
|
352
|
-
required_edges: plan.required_edges,
|
|
353
|
-
required_facets: plan.required_facets,
|
|
354
|
-
scenario_refs: plan.scenario_refs,
|
|
355
|
-
synthetic: true,
|
|
356
|
-
generated_by: GENERATOR
|
|
357
|
-
})},
|
|
358
|
-
NULL, ${now}, ${now}
|
|
359
|
-
)
|
|
360
|
-
ON CONFLICT(artifact_id) DO UPDATE SET
|
|
361
|
-
payload_json=excluded.payload_json,
|
|
362
|
-
state=excluded.state,
|
|
363
|
-
updated_at_unix=excluded.updated_at_unix;
|
|
364
|
-
`);
|
|
365
|
-
for (const row of syntheticRuntimeRows(def)) {
|
|
366
|
-
const metadata = {
|
|
367
|
-
display_name: row.label ?? def.public_label,
|
|
368
|
-
source_id: row.name,
|
|
369
|
-
source: GENERATOR,
|
|
370
|
-
scenario_id: def.scenario_id,
|
|
371
|
-
runtime_record_type: row.role,
|
|
372
|
-
transition: row.transition,
|
|
373
|
-
relation_requirement: row.relation_requirement,
|
|
374
|
-
synthetic: true
|
|
375
|
-
};
|
|
376
|
-
statements.push(`
|
|
377
|
-
INSERT INTO graph_entity (workspace_id, entity_type, name, confidence, metadata_json)
|
|
378
|
-
VALUES (${q(WORKSPACE)}, ${q(row.entity_type)}, ${q(row.name)}, 0.96, ${jsonSql(metadata)})
|
|
379
|
-
ON CONFLICT(workspace_id, entity_type, name)
|
|
380
|
-
DO UPDATE SET confidence=excluded.confidence, metadata_json=excluded.metadata_json, deprecated_at=NULL;
|
|
381
|
-
`);
|
|
382
|
-
}
|
|
383
|
-
for (const row of syntheticRuntimeRows(def).filter((item) => item.name !== def.scenario_id)) {
|
|
384
|
-
const relationType =
|
|
385
|
-
row.role === 'status_event'
|
|
386
|
-
? 'scenario_has_status_event'
|
|
387
|
-
: row.role === 'relation_requirement'
|
|
388
|
-
? 'scenario_requires_relation'
|
|
389
|
-
: 'scenario_requires_record';
|
|
390
|
-
statements.push(relationSql(relationType, def.scenario_id, row.name, {
|
|
391
|
-
source: GENERATOR,
|
|
392
|
-
scenario_id: def.scenario_id,
|
|
393
|
-
synthetic: true
|
|
394
|
-
}));
|
|
395
|
-
}
|
|
396
|
-
return statements;
|
|
397
|
-
}
|
|
398
|
-
|
|
399
|
-
function relationSql(type, sourceName, targetName, metadata) {
|
|
400
|
-
return `
|
|
401
|
-
INSERT INTO graph_relation (workspace_id, relation_type, source_id, target_id, confidence, metadata_json)
|
|
402
|
-
SELECT ${q(WORKSPACE)}, ${q(type)}, s.entity_id, t.entity_id, 0.98, ${jsonSql({
|
|
403
|
-
...metadata,
|
|
404
|
-
relation_group: 'synthetic_scenario'
|
|
405
|
-
})}
|
|
406
|
-
FROM graph_entity s, graph_entity t
|
|
407
|
-
WHERE s.workspace_id=${q(WORKSPACE)} AND s.name=${q(sourceName)}
|
|
408
|
-
AND t.workspace_id=${q(WORKSPACE)} AND t.name=${q(targetName)};
|
|
409
|
-
`;
|
|
410
|
-
}
|
|
411
|
-
|
|
412
|
-
function topEntitiesForPlan(plan, entityMap) {
|
|
413
|
-
const rows = entityMap.get(plan.scenario_id) ?? [];
|
|
414
|
-
const scenario = rows.find((row) => row.entity_type === 'scenario_gestion');
|
|
415
|
-
const runtime = rows.filter((row) => row.entity_type !== 'scenario_gestion');
|
|
416
|
-
const evidence = runtime.filter((row) =>
|
|
417
|
-
['preuve_documentaire', 'proof_document', 'death_certificate_evidence', 'claim_statement'].includes(row.entity_type)
|
|
418
|
-
);
|
|
419
|
-
const statusEvents = runtime.filter((row) => row.entity_type === 'status_event');
|
|
420
|
-
const requirements = runtime.filter((row) => row.entity_type === 'relation_requirement');
|
|
421
|
-
return {
|
|
422
|
-
scenario,
|
|
423
|
-
runtime,
|
|
424
|
-
evidence,
|
|
425
|
-
statusEvents,
|
|
426
|
-
requirements,
|
|
427
|
-
seedIds: [scenario, ...runtime.slice(0, 5)].filter(Boolean).map((row) => row.entity_id)
|
|
428
|
-
};
|
|
429
|
-
}
|
|
430
|
-
|
|
431
|
-
function snapshotPayload(plan, kind, entityMap, relationCounts) {
|
|
432
|
-
const entities = topEntitiesForPlan(plan, entityMap);
|
|
433
|
-
const relationInfo = relationCounts.get(plan.scenario_id) ?? { relation_count: 0, relation_types: [] };
|
|
434
|
-
const runtimeIds = entities.runtime.map((row) => row.entity_id).filter(Number.isFinite);
|
|
435
|
-
const evidenceIds = entities.evidence.map((row) => row.entity_id).filter(Number.isFinite);
|
|
436
|
-
const deltaIds = entities.requirements.slice(0, 4).map((row) => row.entity_id).filter(Number.isFinite);
|
|
437
|
-
const timeline = entities.statusEvents.map((row) => ({
|
|
438
|
-
entity_id: row.entity_id,
|
|
439
|
-
name: row.name,
|
|
440
|
-
transition: row.metadata.transition ?? row.metadata.display_name ?? row.name
|
|
441
|
-
}));
|
|
442
|
-
const base = {
|
|
443
|
-
source_plan_id: plan.artifact_id,
|
|
444
|
-
source_plan_scope: plan.scope,
|
|
445
|
-
projection_id: plan.projection_id,
|
|
446
|
-
scenario_id: plan.scenario_id,
|
|
447
|
-
scenario_refs: plan.scenario_refs,
|
|
448
|
-
business_question: plan.business_question,
|
|
449
|
-
snapshot_kind: kind,
|
|
450
|
-
summary:
|
|
451
|
-
kind === 'scenario_state'
|
|
452
|
-
? `${plan.public_label}: ${runtimeIds.length} objets runtime, ${timeline.length} transitions et ${relationInfo.relation_count} relations scenario.`
|
|
453
|
-
: `${plan.public_label}: ${evidenceIds.length} preuves directes, ${deltaIds.length} exigences/deltas et ${relationInfo.relation_types.length} types de relations.`,
|
|
454
|
-
runtime_entity_ids: runtimeIds,
|
|
455
|
-
evidence_entity_ids: evidenceIds,
|
|
456
|
-
delta_entity_ids: deltaIds,
|
|
457
|
-
seed_ids: entities.seedIds,
|
|
458
|
-
status_timeline: timeline,
|
|
459
|
-
relation_coverage: {
|
|
460
|
-
relation_count: relationInfo.relation_count,
|
|
461
|
-
relation_types: relationInfo.relation_types
|
|
462
|
-
},
|
|
463
|
-
required_edges: plan.required_edges,
|
|
464
|
-
required_facets: plan.required_facets,
|
|
465
|
-
synthetic: plan.source === 'generated',
|
|
466
|
-
generated_by: GENERATOR,
|
|
467
|
-
generated_at_unix: now
|
|
468
|
-
};
|
|
469
|
-
if (kind === 'scenario_evidence') {
|
|
470
|
-
base.deltas = entities.requirements.slice(0, 6).map((row) => ({
|
|
471
|
-
entity_id: row.entity_id,
|
|
472
|
-
requirement: row.metadata.relation_requirement ?? row.metadata.display_name ?? row.name,
|
|
473
|
-
status: evidenceIds.length > 0 ? 'covered' : 'needs_evidence'
|
|
474
|
-
}));
|
|
475
|
-
}
|
|
476
|
-
return base;
|
|
477
|
-
}
|
|
478
|
-
|
|
479
|
-
function buildSnapshotSql(plan, kind, entityMap, relationCounts) {
|
|
480
|
-
const payload = snapshotPayload(plan, kind, entityMap, relationCounts);
|
|
481
|
-
const snapshotSlug = `${plan.slug}__${kind}`;
|
|
482
|
-
const artifactId = `answer_snapshot__${snapshotSlug}`;
|
|
483
|
-
const title = `${plan.public_label} - ${kind === 'scenario_state' ? 'etat scenario' : 'preuves et deltas'}`;
|
|
484
|
-
const projectionName = `projection_result.${plan.projection_id}.${kind}`;
|
|
485
|
-
const metadata = {
|
|
486
|
-
artifact_id: artifactId,
|
|
487
|
-
artifact_kind: 'answer_snapshot',
|
|
488
|
-
public_label: title,
|
|
489
|
-
projection_id: plan.projection_id,
|
|
490
|
-
source_plan_id: plan.artifact_id,
|
|
491
|
-
scenario_id: plan.scenario_id,
|
|
492
|
-
snapshot_kind: kind,
|
|
493
|
-
seed_ids: payload.seed_ids,
|
|
494
|
-
source: GENERATOR,
|
|
495
|
-
workspace_id: WORKSPACE,
|
|
496
|
-
synthetic: payload.synthetic,
|
|
497
|
-
relation_group: 'snapshot'
|
|
498
|
-
};
|
|
499
|
-
const relationStatements = [];
|
|
500
|
-
const scenarioEntity = (entityMap.get(plan.scenario_id) ?? []).find((row) => row.entity_type === 'scenario_gestion');
|
|
501
|
-
if (scenarioEntity) {
|
|
502
|
-
relationStatements.push(`
|
|
503
|
-
INSERT INTO graph_relation (workspace_id, relation_type, source_id, target_id, confidence, metadata_json)
|
|
504
|
-
SELECT ${q(WORKSPACE)}, 'SNAPSHOT_FOR_SCENARIO', p.entity_id, ${scenarioEntity.entity_id}, 1.0, ${jsonSql(metadata)}
|
|
505
|
-
FROM graph_entity p
|
|
506
|
-
WHERE p.workspace_id=${q(WORKSPACE)} AND p.entity_type='ProjectionResult' AND p.name=${q(projectionName)};
|
|
507
|
-
`);
|
|
508
|
-
}
|
|
509
|
-
for (const targetId of payload.seed_ids.slice(0, 8)) {
|
|
510
|
-
relationStatements.push(`
|
|
511
|
-
INSERT INTO graph_relation (workspace_id, relation_type, source_id, target_id, confidence, metadata_json)
|
|
512
|
-
SELECT ${q(WORKSPACE)}, 'SNAPSHOT_REFERENCES_OBJECT', p.entity_id, ${targetId}, 0.97, ${jsonSql(metadata)}
|
|
513
|
-
FROM graph_entity p
|
|
514
|
-
WHERE p.workspace_id=${q(WORKSPACE)} AND p.entity_type='ProjectionResult' AND p.name=${q(projectionName)};
|
|
515
|
-
`);
|
|
516
|
-
}
|
|
517
|
-
for (const targetId of payload.delta_entity_ids.slice(0, 4)) {
|
|
518
|
-
relationStatements.push(`
|
|
519
|
-
INSERT INTO graph_relation (workspace_id, relation_type, source_id, target_id, confidence, metadata_json)
|
|
520
|
-
SELECT ${q(WORKSPACE)}, 'SNAPSHOT_HAS_DELTA', p.entity_id, ${targetId}, 0.94, ${jsonSql(metadata)}
|
|
521
|
-
FROM graph_entity p
|
|
522
|
-
WHERE p.workspace_id=${q(WORKSPACE)} AND p.entity_type='ProjectionResult' AND p.name=${q(projectionName)};
|
|
523
|
-
`);
|
|
524
|
-
}
|
|
525
|
-
return [
|
|
526
|
-
`
|
|
527
|
-
INSERT INTO mindbrain_answer_artifacts (
|
|
528
|
-
artifact_id, slug, workspace_id, agent_id, scope, artifact_kind,
|
|
529
|
-
public_label_key, public_label, lifecycle, state, current_version,
|
|
530
|
-
payload_json, legacy_ref, created_at_unix, updated_at_unix
|
|
531
|
-
)
|
|
532
|
-
VALUES (
|
|
533
|
-
${q(artifactId)}, ${q(snapshotSlug)}, ${q(WORKSPACE)}, NULL, ${q(plan.scope)},
|
|
534
|
-
'answer_snapshot', ${q(`answer_snapshot.${WORKSPACE}.${plan.projection_id}.${kind}`)},
|
|
535
|
-
${q(title)}, 'frozen', 'ready', 1, ${jsonSql(payload)}, NULL, ${now}, ${now}
|
|
536
|
-
)
|
|
537
|
-
ON CONFLICT(artifact_id) DO UPDATE SET
|
|
538
|
-
public_label=excluded.public_label,
|
|
539
|
-
scope=excluded.scope,
|
|
540
|
-
lifecycle=excluded.lifecycle,
|
|
541
|
-
state=excluded.state,
|
|
542
|
-
current_version=excluded.current_version,
|
|
543
|
-
payload_json=excluded.payload_json,
|
|
544
|
-
updated_at_unix=excluded.updated_at_unix;
|
|
545
|
-
`,
|
|
546
|
-
`
|
|
547
|
-
INSERT INTO graph_entity (workspace_id, entity_type, name, confidence, metadata_json)
|
|
548
|
-
VALUES (${q(WORKSPACE)}, 'ProjectionResult', ${q(projectionName)}, 1.0, ${jsonSql(metadata)})
|
|
549
|
-
ON CONFLICT(workspace_id, entity_type, name)
|
|
550
|
-
DO UPDATE SET confidence=excluded.confidence, metadata_json=excluded.metadata_json, deprecated_at=NULL;
|
|
551
|
-
`,
|
|
552
|
-
...relationStatements
|
|
553
|
-
];
|
|
554
|
-
}
|
|
555
|
-
|
|
556
|
-
function buildNewScenarioDataSql(generatedPlans) {
|
|
557
|
-
const statements = [];
|
|
558
|
-
statements.push(
|
|
559
|
-
`DELETE FROM graph_relation WHERE workspace_id=${q(WORKSPACE)} AND json_extract(metadata_json, '$.source')=${q(GENERATOR)} AND json_extract(metadata_json, '$.relation_group')='synthetic_scenario';`
|
|
560
|
-
);
|
|
561
|
-
for (const plan of generatedPlans) statements.push(...buildNewScenarioSql(plan));
|
|
562
|
-
return ['PRAGMA foreign_keys=ON;', 'BEGIN IMMEDIATE;', ...statements, 'COMMIT;'].join('\n');
|
|
563
|
-
}
|
|
564
|
-
|
|
565
|
-
function buildSnapshotDataSql(plans) {
|
|
566
|
-
const entityMap = readScenarioEntityMap();
|
|
567
|
-
const relationCounts = readScenarioRelationCounts();
|
|
568
|
-
const statements = [
|
|
569
|
-
`DELETE FROM graph_relation WHERE workspace_id=${q(WORKSPACE)} AND json_extract(metadata_json, '$.source')=${q(GENERATOR)} AND json_extract(metadata_json, '$.relation_group')='snapshot';`
|
|
570
|
-
];
|
|
571
|
-
for (const plan of plans) {
|
|
572
|
-
for (const kind of ['scenario_state', 'scenario_evidence']) {
|
|
573
|
-
statements.push(...buildSnapshotSql(plan, kind, entityMap, relationCounts));
|
|
574
|
-
}
|
|
575
|
-
}
|
|
576
|
-
return ['PRAGMA foreign_keys=ON;', 'BEGIN IMMEDIATE;', ...statements, 'COMMIT;'].join('\n');
|
|
577
|
-
}
|
|
578
|
-
|
|
579
|
-
function backupDb() {
|
|
580
|
-
mkdirSync(backupDir, { recursive: true });
|
|
581
|
-
const backupPath = join(backupDir, `${WORKSPACE}-pre-snapshots-${new Date().toISOString().replaceAll(':', '-')}.sqlite`);
|
|
582
|
-
copyFileSync(dbPath, backupPath);
|
|
583
|
-
return backupPath;
|
|
584
|
-
}
|
|
585
|
-
|
|
586
|
-
function counts() {
|
|
587
|
-
const rows = runSql(
|
|
588
|
-
`
|
|
589
|
-
SELECT 'answer_snapshot_artifacts' AS name, COUNT(*) AS count
|
|
590
|
-
FROM mindbrain_answer_artifacts
|
|
591
|
-
WHERE workspace_id=${q(WORKSPACE)} AND artifact_kind='answer_snapshot'
|
|
592
|
-
UNION ALL
|
|
593
|
-
SELECT 'projection_results' AS name, COUNT(*) AS count
|
|
594
|
-
FROM graph_entity
|
|
595
|
-
WHERE workspace_id=${q(WORKSPACE)} AND entity_type='ProjectionResult'
|
|
596
|
-
UNION ALL
|
|
597
|
-
SELECT 'scenario_plans' AS name, COUNT(*) AS count
|
|
598
|
-
FROM mindbrain_answer_artifacts
|
|
599
|
-
WHERE workspace_id=${q(WORKSPACE)} AND artifact_kind='analysis_plan' AND scope LIKE ${q(`${WORKSPACE}:scenario:%`)};
|
|
600
|
-
`,
|
|
601
|
-
{ json: true, readonly: true }
|
|
602
|
-
);
|
|
603
|
-
return Object.fromEntries(rows.map((row) => [row.name, Number(row.count)]));
|
|
604
|
-
}
|
|
605
|
-
|
|
606
|
-
function main() {
|
|
607
|
-
if (verbose) console.error(`[${GENERATOR}] reading counts`);
|
|
608
|
-
const before = counts();
|
|
609
|
-
if (verbose) console.error(`[${GENERATOR}] reading existing scenario plans`);
|
|
610
|
-
const existingPlans = readScenarioPlans();
|
|
611
|
-
const generatedPlans = NEW_SCENARIOS.map(newScenarioPlan);
|
|
612
|
-
const existingPlanIds = new Set(existingPlans.map((plan) => plan.artifact_id));
|
|
613
|
-
const missingGeneratedPlans = generatedPlans.filter((plan) => !existingPlanIds.has(plan.artifact_id));
|
|
614
|
-
const expectedPlanCount = existingPlans.length + missingGeneratedPlans.length;
|
|
615
|
-
const plannedSnapshots = expectedPlanCount * 2;
|
|
616
|
-
if (emitSqlPath) {
|
|
617
|
-
const previewSql = [
|
|
618
|
-
'-- new scenario data',
|
|
619
|
-
buildNewScenarioDataSql(generatedPlans),
|
|
620
|
-
'-- snapshot data for current plans only',
|
|
621
|
-
buildSnapshotDataSql(existingPlans)
|
|
622
|
-
].join('\n');
|
|
623
|
-
writeFileSync(emitSqlPath, previewSql);
|
|
624
|
-
console.log(JSON.stringify({ mode: 'emit-sql', path: emitSqlPath, bytes: Buffer.byteLength(previewSql, 'utf8') }, null, 2));
|
|
625
|
-
return;
|
|
626
|
-
}
|
|
627
|
-
if (!apply) {
|
|
628
|
-
console.log(
|
|
629
|
-
JSON.stringify(
|
|
630
|
-
{
|
|
631
|
-
mode: 'dry-run',
|
|
632
|
-
db_path: dbPath,
|
|
633
|
-
workspace_id: WORKSPACE,
|
|
634
|
-
existing_scenarios: existingPlans.length,
|
|
635
|
-
new_scenarios: missingGeneratedPlans.length,
|
|
636
|
-
planned_snapshots: plannedSnapshots,
|
|
637
|
-
planned_projection_results: plannedSnapshots,
|
|
638
|
-
before
|
|
639
|
-
},
|
|
640
|
-
null,
|
|
641
|
-
2
|
|
642
|
-
)
|
|
643
|
-
);
|
|
644
|
-
return;
|
|
645
|
-
}
|
|
646
|
-
if (verbose) console.error(`[${GENERATOR}] backing up`);
|
|
647
|
-
const backupPath = backupDb();
|
|
648
|
-
if (verbose) console.error(`[${GENERATOR}] applying generated scenario data`);
|
|
649
|
-
runSqlFile(buildNewScenarioDataSql(generatedPlans));
|
|
650
|
-
if (verbose) console.error(`[${GENERATOR}] reading plans after generated data`);
|
|
651
|
-
const plansAfterScenarioData = readScenarioPlans();
|
|
652
|
-
if (verbose) console.error(`[${GENERATOR}] applying snapshot data for ${plansAfterScenarioData.length} plans`);
|
|
653
|
-
runSqlFile(buildSnapshotDataSql(plansAfterScenarioData));
|
|
654
|
-
if (verbose) console.error(`[${GENERATOR}] reading final counts`);
|
|
655
|
-
const after = counts();
|
|
656
|
-
console.log(
|
|
657
|
-
JSON.stringify(
|
|
658
|
-
{
|
|
659
|
-
mode: 'apply',
|
|
660
|
-
db_path: dbPath,
|
|
661
|
-
workspace_id: WORKSPACE,
|
|
662
|
-
backup_path: backupPath,
|
|
663
|
-
existing_scenarios: existingPlans.length,
|
|
664
|
-
new_scenarios: missingGeneratedPlans.length,
|
|
665
|
-
planned_snapshots: plannedSnapshots,
|
|
666
|
-
before,
|
|
667
|
-
after
|
|
668
|
-
},
|
|
669
|
-
null,
|
|
670
|
-
2
|
|
671
|
-
)
|
|
672
|
-
);
|
|
673
|
-
}
|
|
674
|
-
|
|
675
|
-
main();
|