@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.
- package/README.md +16 -8
- package/build/client/_app/immutable/chunks/{D0UIlUGZ.js → CIErFlYG.js} +1 -1
- 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.CR-imLox.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.CTEedoSY.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-WOmQXGMa.js → internal.js-C3tV0XXj.js} +2 -2
- package/build/server/chunks/chunks/{internal.js-WOmQXGMa.js.map → internal.js-C3tV0XXj.js.map} +1 -1
- package/build/server/chunks/{handler-BIDedSZq.js → handler-DlaCCnxx.js} +3 -3
- package/build/server/chunks/{handler-BIDedSZq.js.map → handler-DlaCCnxx.js.map} +1 -1
- package/build/server/chunks/{index.js-YVPJa0so.js → index.js-BGfKWHak.js} +2 -2
- package/build/server/chunks/{index.js-YVPJa0so.js.map → index.js-BGfKWHak.js.map} +1 -1
- package/build/server/chunks/{manifest.js-aGRKuiqF.js → manifest.js-CnmaNf5D.js} +3 -3
- package/build/server/chunks/{manifest.js-aGRKuiqF.js.map → manifest.js-CnmaNf5D.js.map} +1 -1
- package/build/server/chunks/nodes/{1.js-Dhh3ErZY.js → 1.js-BypjwBYB.js} +2 -2
- package/build/server/chunks/nodes/{1.js-Dhh3ErZY.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 +11 -2
- 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/D0UIlUGZ.js.br +0 -0
- package/build/client/_app/immutable/chunks/D0UIlUGZ.js.gz +0 -0
- package/build/client/_app/immutable/entry/app.CR-imLox.js.br +0 -0
- package/build/client/_app/immutable/entry/app.CR-imLox.js.gz +0 -0
- package/build/client/_app/immutable/entry/start.DV-AjeAB.js +0 -1
- package/build/client/_app/immutable/entry/start.DV-AjeAB.js.br +0 -2
- package/build/client/_app/immutable/entry/start.DV-AjeAB.js.gz +0 -0
- package/build/client/_app/immutable/nodes/1.CTEedoSY.js.br +0 -0
- package/build/client/_app/immutable/nodes/1.CTEedoSY.js.gz +0 -0
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
# Projection Candidate Review
|
|
2
|
+
|
|
3
|
+
- Workspace: `immeuble`
|
|
4
|
+
- Generated at: `2026-06-14T14:57:25.897Z`
|
|
5
|
+
- Candidate count: 13
|
|
6
|
+
- Materialized count: 0
|
|
7
|
+
- Active lenses: blind_spot_manager, jtbd_human, jtbd_ai
|
|
8
|
+
|
|
9
|
+
## competency
|
|
10
|
+
|
|
11
|
+
### Plan d'analyse immeuble — questions métier
|
|
12
|
+
- Scope: `immeuble:competency:questions`
|
|
13
|
+
- Status: `candidate`
|
|
14
|
+
- Origin: `projection_catalog`
|
|
15
|
+
- artifact_kind: `analysis_plan`
|
|
16
|
+
- Required facets: `building.name`, `unit.usage_status`, `lease_contract.validFrom`, `coda_entry.amount`
|
|
17
|
+
- Required edges: `CONTAINS`, `LEASES`, `MATCHED_TO`
|
|
18
|
+
|
|
19
|
+
## core
|
|
20
|
+
|
|
21
|
+
### Annuaire immeubles gérés
|
|
22
|
+
- Scope: `immeuble:core:annuaire_coproprietes`
|
|
23
|
+
- Status: `candidate`
|
|
24
|
+
- Origin: `projection_catalog`
|
|
25
|
+
- artifact_kind: `live_answer_view`
|
|
26
|
+
- Required facets: `building.address`, `building.name`, `building.quota_basis`, `block.name`
|
|
27
|
+
- Required edges: `CONTAINS`
|
|
28
|
+
|
|
29
|
+
### Baux locatifs actifs
|
|
30
|
+
- Scope: `immeuble:core:baux_actifs`
|
|
31
|
+
- Status: `candidate`
|
|
32
|
+
- Origin: `projection_catalog`
|
|
33
|
+
- artifact_kind: `live_answer_view`
|
|
34
|
+
- Required facets: `unit.usage_status`, `unit.lot`, `lease_contract.validFrom`, `lease_contract.monthly_rent`, `person.name`
|
|
35
|
+
- Required edges: `LEASES`, `RENTED_TO`, `OCCUPIES`
|
|
36
|
+
|
|
37
|
+
### Quotités par immeuble
|
|
38
|
+
- Scope: `immeuble:core:quotites_par_immeuble`
|
|
39
|
+
- Status: `candidate`
|
|
40
|
+
- Origin: `projection_catalog`
|
|
41
|
+
- artifact_kind: `live_answer_view`
|
|
42
|
+
- Required facets: `building.quota_basis`, `unit.tantiemes`, `unit.quota_basis`
|
|
43
|
+
- Required edges: `CONTAINS`
|
|
44
|
+
|
|
45
|
+
## gestionnaire_syndic
|
|
46
|
+
|
|
47
|
+
### Impayes et relances en cours
|
|
48
|
+
- Scope: `immeuble:gestionnaire_syndic:impayes_et_relances`
|
|
49
|
+
- Status: `candidate`
|
|
50
|
+
- Origin: `analysis_lens`
|
|
51
|
+
- artifact_kind: `analysis_plan`
|
|
52
|
+
- Required facets: `receipt.amount`, `reminder.level`, `reminder.amount_due`, `charge_call.amount`
|
|
53
|
+
- Required edges: `REQUIRES_REVIEW`, `MATCHED_TO`
|
|
54
|
+
|
|
55
|
+
### Baux proches de l'echeance
|
|
56
|
+
- Scope: `immeuble:gestionnaire_syndic:baux_echeance`
|
|
57
|
+
- Status: `candidate`
|
|
58
|
+
- Origin: `analysis_lens`
|
|
59
|
+
- artifact_kind: `analysis_plan`
|
|
60
|
+
- Required facets: `lease_contract.validFrom`, `lease_contract.monthly_rent`, `unit.usage_status`
|
|
61
|
+
- Required edges: `LEASES`, `RENTED_TO`
|
|
62
|
+
|
|
63
|
+
### Anomalies de quotites
|
|
64
|
+
- Scope: `immeuble:gestionnaire_syndic:quotites_anomalie`
|
|
65
|
+
- Status: `candidate`
|
|
66
|
+
- Origin: `analysis_lens`
|
|
67
|
+
- artifact_kind: `analysis_plan`
|
|
68
|
+
- Required facets: `building.quota_basis`, `unit.tantiemes`, `unit.quota_basis`
|
|
69
|
+
- Required edges: `CONTAINS`
|
|
70
|
+
|
|
71
|
+
### Lots sans occupation declaree
|
|
72
|
+
- Scope: `immeuble:gestionnaire_syndic:occupation_orpheline`
|
|
73
|
+
- Status: `candidate`
|
|
74
|
+
- Origin: `analysis_lens`
|
|
75
|
+
- artifact_kind: `analysis_plan`
|
|
76
|
+
- Required facets: `unit.usage_status`, `person.name`, `household.household_status`
|
|
77
|
+
- Required edges: `OCCUPIES`, `OWNS`, `PRIMARY_RESIDENCE_OF`
|
|
78
|
+
|
|
79
|
+
### Top actions manager du jour
|
|
80
|
+
- Scope: `immeuble:gestionnaire_syndic:manager_attention_today`
|
|
81
|
+
- Status: `candidate`
|
|
82
|
+
- Origin: `analysis_lens`
|
|
83
|
+
- artifact_kind: `analysis_plan`
|
|
84
|
+
- Required facets: `reminder.level`, `decision.date`, `lease_contract.validFrom`
|
|
85
|
+
- Required edges: `REQUIRES_REVIEW`, `LEASES`
|
|
86
|
+
|
|
87
|
+
### JTBD gestionnaire: arbitrer avec preuves
|
|
88
|
+
- Scope: `immeuble:gestionnaire_syndic:gestionnaire_decide_arbitrer`
|
|
89
|
+
- Status: `candidate`
|
|
90
|
+
- Origin: `analysis_lens`
|
|
91
|
+
- artifact_kind: `analysis_plan`
|
|
92
|
+
- Required facets: `decision.date`, `coda_entry.amount`, `coda_entry.status`
|
|
93
|
+
- Required edges: `MATCHED_TO`, `REQUIRES_REVIEW`
|
|
94
|
+
|
|
95
|
+
### JTBD syndic: coordonner la prochaine intervention
|
|
96
|
+
- Scope: `immeuble:gestionnaire_syndic:syndic_coordinate_intervention`
|
|
97
|
+
- Status: `candidate`
|
|
98
|
+
- Origin: `analysis_lens`
|
|
99
|
+
- artifact_kind: `analysis_plan`
|
|
100
|
+
- Required facets: `unit.usage_status`, `shared_space.category`, `shared_equipment.equipment_type`
|
|
101
|
+
- Required edges: `CONTAINS`, `USES_COMMON`
|
|
102
|
+
|
|
103
|
+
### JTBD agent IA: tour de controle priorisee
|
|
104
|
+
- Scope: `immeuble:gestionnaire_syndic:agent_watchtower_prioritize`
|
|
105
|
+
- Status: `candidate`
|
|
106
|
+
- Origin: `analysis_lens`
|
|
107
|
+
- artifact_kind: `analysis_plan`
|
|
108
|
+
- Required facets: `reminder.level`, `lease_contract.validFrom`, `unit.usage_status`
|
|
109
|
+
- Required edges: `REQUIRES_REVIEW`, `LEASES`
|
|
110
|
+
|
|
111
|
+
### JTBD agent IA: detective de reconciliation CODA
|
|
112
|
+
- Scope: `immeuble:gestionnaire_syndic:agent_reconciliation_detective`
|
|
113
|
+
- Status: `candidate`
|
|
114
|
+
- Origin: `analysis_lens`
|
|
115
|
+
- artifact_kind: `analysis_plan`
|
|
116
|
+
- Required facets: `coda_entry.amount`, `coda_entry.status`, `charge_call.amount`, `receipt.amount`
|
|
117
|
+
- Required edges: `MATCHED_TO`, `ALLOCATED_TO`
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
# Projection Model Validation
|
|
2
|
+
|
|
3
|
+
## Synthese
|
|
4
|
+
|
|
5
|
+
- Workspace: `immeuble`
|
|
6
|
+
- Projections catalogue: 4
|
|
7
|
+
- Questions manager: 0
|
|
8
|
+
- Ajouts par patterns: 9
|
|
9
|
+
- Scopes materialises: 0
|
|
10
|
+
|
|
11
|
+
## Projections catalogue
|
|
12
|
+
|
|
13
|
+
### Plan d'analyse immeuble — questions métier
|
|
14
|
+
- Question: Quelles questions métier le workspace immeuble doit-il pouvoir traiter ?
|
|
15
|
+
- Scope: `immeuble:competency:questions`
|
|
16
|
+
- artifact_kind: `analysis_plan`
|
|
17
|
+
- Statut: `candidate`
|
|
18
|
+
- Facettes requises: `building.name`, `unit.usage_status`, `lease_contract.validFrom`, `coda_entry.amount`
|
|
19
|
+
- Aretes requises: `CONTAINS`, `LEASES`, `MATCHED_TO`
|
|
20
|
+
|
|
21
|
+
### Annuaire immeubles gérés
|
|
22
|
+
- Question: Quels immeubles sont gérés et où sont-ils situés ?
|
|
23
|
+
- Scope: `immeuble:core:annuaire_coproprietes`
|
|
24
|
+
- artifact_kind: `live_answer_view`
|
|
25
|
+
- Statut: `candidate`
|
|
26
|
+
- Facettes requises: `building.address`, `building.name`, `building.quota_basis`, `block.name`
|
|
27
|
+
- Aretes requises: `CONTAINS`
|
|
28
|
+
|
|
29
|
+
### Baux locatifs actifs
|
|
30
|
+
- Question: Quels appartements sont loués, à qui, depuis quelle date ?
|
|
31
|
+
- Scope: `immeuble:core:baux_actifs`
|
|
32
|
+
- artifact_kind: `live_answer_view`
|
|
33
|
+
- Statut: `candidate`
|
|
34
|
+
- Facettes requises: `unit.usage_status`, `unit.lot`, `lease_contract.validFrom`, `lease_contract.monthly_rent`, `person.name`
|
|
35
|
+
- Aretes requises: `LEASES`, `RENTED_TO`, `OCCUPIES`
|
|
36
|
+
|
|
37
|
+
### Quotités par immeuble
|
|
38
|
+
- Question: Les quotités de chaque immeuble totalisent-elles 1000 ?
|
|
39
|
+
- Scope: `immeuble:core:quotites_par_immeuble`
|
|
40
|
+
- artifact_kind: `live_answer_view`
|
|
41
|
+
- Statut: `candidate`
|
|
42
|
+
- Facettes requises: `building.quota_basis`, `unit.tantiemes`, `unit.quota_basis`
|
|
43
|
+
- Aretes requises: `CONTAINS`
|
|
44
|
+
|
|
45
|
+
## Questions manquantes proposees
|
|
46
|
+
|
|
47
|
+
### Impayes et relances en cours
|
|
48
|
+
- Categorie: `blind_spot_manager`
|
|
49
|
+
- Question: Quels coproprietaires ont des impayes ou relances actives ?
|
|
50
|
+
- Facettes requises: `receipt.amount`, `reminder.level`, `reminder.amount_due`, `charge_call.amount`
|
|
51
|
+
- Aretes requises: `REQUIRES_REVIEW`, `MATCHED_TO`
|
|
52
|
+
|
|
53
|
+
### Baux proches de l'echeance
|
|
54
|
+
- Categorie: `blind_spot_manager`
|
|
55
|
+
- Question: Quels baux arrivent a echeance dans les 90 prochains jours ?
|
|
56
|
+
- Facettes requises: `lease_contract.validFrom`, `lease_contract.monthly_rent`, `unit.usage_status`
|
|
57
|
+
- Aretes requises: `LEASES`, `RENTED_TO`
|
|
58
|
+
|
|
59
|
+
### Anomalies de quotites
|
|
60
|
+
- Categorie: `blind_spot_manager`
|
|
61
|
+
- Question: Quels immeubles ont des quotites qui ne totalisent pas le quota_basis ?
|
|
62
|
+
- Facettes requises: `building.quota_basis`, `unit.tantiemes`, `unit.quota_basis`
|
|
63
|
+
- Aretes requises: `CONTAINS`
|
|
64
|
+
|
|
65
|
+
### Lots sans occupation declaree
|
|
66
|
+
- Categorie: `blind_spot_manager`
|
|
67
|
+
- Question: Quels lots n'ont ni proprietaire occupant ni locataire declare ?
|
|
68
|
+
- Facettes requises: `unit.usage_status`, `person.name`, `household.household_status`
|
|
69
|
+
- Aretes requises: `OCCUPIES`, `OWNS`, `PRIMARY_RESIDENCE_OF`
|
|
70
|
+
|
|
71
|
+
### Top actions manager du jour
|
|
72
|
+
- Categorie: `blind_spot_manager`
|
|
73
|
+
- Question: Quelles 3 a 5 actions doivent remonter au gestionnaire aujourd'hui ?
|
|
74
|
+
- Facettes requises: `reminder.level`, `decision.date`, `lease_contract.validFrom`
|
|
75
|
+
- Aretes requises: `REQUIRES_REVIEW`, `LEASES`
|
|
76
|
+
|
|
77
|
+
### JTBD gestionnaire: arbitrer avec preuves
|
|
78
|
+
- Categorie: `jtbd_human`
|
|
79
|
+
- Question: Quand plusieurs signaux se contredisent, quelle decision puis-je prendre avec suffisamment de preuves ?
|
|
80
|
+
- Facettes requises: `decision.date`, `coda_entry.amount`, `coda_entry.status`
|
|
81
|
+
- Aretes requises: `MATCHED_TO`, `REQUIRES_REVIEW`
|
|
82
|
+
|
|
83
|
+
### JTBD syndic: coordonner la prochaine intervention
|
|
84
|
+
- Categorie: `jtbd_human`
|
|
85
|
+
- Question: Que faut-il coordonner pour la prochaine intervention sur un lot ou une zone commune ?
|
|
86
|
+
- Facettes requises: `unit.usage_status`, `shared_space.category`, `shared_equipment.equipment_type`
|
|
87
|
+
- Aretes requises: `CONTAINS`, `USES_COMMON`
|
|
88
|
+
|
|
89
|
+
### JTBD agent IA: tour de controle priorisee
|
|
90
|
+
- Categorie: `jtbd_ai`
|
|
91
|
+
- Question: Quels signaux dois-je surveiller et remonter sans saturer le gestionnaire ?
|
|
92
|
+
- Facettes requises: `reminder.level`, `lease_contract.validFrom`, `unit.usage_status`
|
|
93
|
+
- Aretes requises: `REQUIRES_REVIEW`, `LEASES`
|
|
94
|
+
|
|
95
|
+
### JTBD agent IA: detective de reconciliation CODA
|
|
96
|
+
- Categorie: `jtbd_ai`
|
|
97
|
+
- Question: Quelles incoherences entre paiements CODA et appels de charges dois-je investiguer ?
|
|
98
|
+
- Facettes requises: `coda_entry.amount`, `coda_entry.status`, `charge_call.amount`, `receipt.amount`
|
|
99
|
+
- Aretes requises: `MATCHED_TO`, `ALLOCATED_TO`
|
|
100
|
+
|
|
101
|
+
## Dimensions et graphes a valider
|
|
102
|
+
|
|
103
|
+
### Facettes nouvelles par rapport aux sources
|
|
104
|
+
|
|
105
|
+
`charge_call.amount`, `coda_entry.status`, `decision.date`, `household.household_status`, `receipt.amount`, `reminder.amount_due`, `reminder.level`, `shared_equipment.equipment_type`, `shared_space.category`
|
|
106
|
+
|
|
107
|
+
### Aretes nouvelles par rapport aux sources
|
|
108
|
+
|
|
109
|
+
`ALLOCATED_TO`, `OWNS`, `PRIMARY_RESIDENCE_OF`, `REQUIRES_REVIEW`, `USES_COMMON`
|
|
110
|
+
|
|
111
|
+
## Gaps versus model_contract
|
|
112
|
+
|
|
113
|
+
- Schemas inconnus: n/a
|
|
114
|
+
- Facettes inconnues: n/a
|
|
115
|
+
- Aretes inconnues: n/a
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
{
|
|
2
|
+
"ok": true,
|
|
3
|
+
"db_path": "/tmp/immeuble-reset-test/immeuble.sqlite",
|
|
4
|
+
"steps": [
|
|
5
|
+
{
|
|
6
|
+
"step": "build",
|
|
7
|
+
"ok": true,
|
|
8
|
+
"status": 0,
|
|
9
|
+
"stderr": ""
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
"step": "import",
|
|
13
|
+
"ok": true,
|
|
14
|
+
"status": 0,
|
|
15
|
+
"stderr": ""
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
"step": "verify_acceptance",
|
|
19
|
+
"ok": true,
|
|
20
|
+
"status": 0,
|
|
21
|
+
"stderr": ""
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
"step": "audit_projections",
|
|
25
|
+
"ok": true,
|
|
26
|
+
"status": 0,
|
|
27
|
+
"stderr": ""
|
|
28
|
+
}
|
|
29
|
+
],
|
|
30
|
+
"started_at": "2026-06-14T08:31:20.938Z",
|
|
31
|
+
"finished_at": "2026-06-14T08:31:44.529Z"
|
|
32
|
+
}
|
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Audit Immeuble projections after import or bundle load.
|
|
4
|
+
*
|
|
5
|
+
* Checks:
|
|
6
|
+
* - projection_catalog.yaml entries are present in consumer contract and seed
|
|
7
|
+
* - live_answer_view artifacts are listable
|
|
8
|
+
* - each required artifact has non-empty payload content
|
|
9
|
+
* - consumer_contract.yaml structure is valid
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
13
|
+
import { dirname, join, resolve } from "node:path";
|
|
14
|
+
import { fileURLToPath } from "node:url";
|
|
15
|
+
import { spawnSync } from "node:child_process";
|
|
16
|
+
import { parseYaml } from "./yaml-lite.mjs";
|
|
17
|
+
|
|
18
|
+
const immeubleRoot = resolve(dirname(fileURLToPath(import.meta.url)), "..");
|
|
19
|
+
const pkgRoot = resolve(immeubleRoot, "..", "..");
|
|
20
|
+
const reportsDir = join(immeubleRoot, "reports");
|
|
21
|
+
const workspaceId = parseFlag(process.argv.slice(2), "--workspace-id", "immeuble");
|
|
22
|
+
const gcpPath = resolveGcp();
|
|
23
|
+
|
|
24
|
+
mkdirSync(reportsDir, { recursive: true });
|
|
25
|
+
|
|
26
|
+
const catalog = parseYaml(readFileSync(join(immeubleRoot, "contracts", "projection_catalog.yaml"), "utf8"));
|
|
27
|
+
const consumer = parseYaml(readFileSync(join(immeubleRoot, "contracts", "consumer_contract.yaml"), "utf8"));
|
|
28
|
+
const seedLines = readFileSync(join(immeubleRoot, "contracts", "answer_artifacts.seed.jsonl"), "utf8")
|
|
29
|
+
.trim()
|
|
30
|
+
.split("\n")
|
|
31
|
+
.filter(Boolean)
|
|
32
|
+
.map((line) => safeParseJson(line))
|
|
33
|
+
.filter(Boolean);
|
|
34
|
+
|
|
35
|
+
const expectedSeedById = Object.fromEntries(seedLines.map((row) => {
|
|
36
|
+
const id = row.artifact?.artifact_id;
|
|
37
|
+
return id ? [id, row] : null;
|
|
38
|
+
}).filter(Boolean));
|
|
39
|
+
|
|
40
|
+
const expectedCatalogIds = (catalog.projections ?? []).map((entry) => {
|
|
41
|
+
const kind = (entry.artifact_kind || "").toString();
|
|
42
|
+
const prefix = kind === "analysis_plan" ? "analysis_plan__" : "live_answer_view__";
|
|
43
|
+
return `${prefix}${entry.name}`;
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
const expectedLiveArtifactIds = seedLines
|
|
47
|
+
.filter((row) => row.artifact?.artifact_kind === "live_answer_view")
|
|
48
|
+
.map((row) => row.artifact.artifact_id)
|
|
49
|
+
.filter(Boolean);
|
|
50
|
+
|
|
51
|
+
const listRes = spawnSync(process.execPath, [
|
|
52
|
+
gcpPath,
|
|
53
|
+
"brain", "artifact", "list",
|
|
54
|
+
"--workspace-id", workspaceId,
|
|
55
|
+
"--kind", "live_answer_view",
|
|
56
|
+
"--limit", "200"
|
|
57
|
+
], {
|
|
58
|
+
cwd: pkgRoot,
|
|
59
|
+
encoding: "utf8",
|
|
60
|
+
env: process.env
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
let listedIds = [];
|
|
64
|
+
let listedById = new Map();
|
|
65
|
+
let listOk = listRes.status === 0;
|
|
66
|
+
if (listOk) {
|
|
67
|
+
const payload = safeParseJson(listRes.stdout.trim());
|
|
68
|
+
if (payload) {
|
|
69
|
+
const artifacts = (payload.artifacts ?? payload.items ?? []).filter(Boolean);
|
|
70
|
+
listedIds = artifacts.map((a) => a.artifact_id ?? a.id).filter(Boolean);
|
|
71
|
+
listedById = new Map(artifacts.map((a) => [a.artifact_id ?? a.id, a]));
|
|
72
|
+
} else {
|
|
73
|
+
listOk = false;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
if (!listOk) {
|
|
77
|
+
const fallback = listArtifactsFromSqlite();
|
|
78
|
+
if (fallback.ok) {
|
|
79
|
+
listOk = true;
|
|
80
|
+
listedIds = fallback.artifacts.map((a) => a.artifact_id).filter(Boolean);
|
|
81
|
+
listedById = new Map(fallback.artifacts.map((a) => [a.artifact_id, a]));
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const payloadIssues = [];
|
|
86
|
+
|
|
87
|
+
function pushIssue(label, detail) {
|
|
88
|
+
payloadIssues.push({ label, detail });
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function catalogIdForEntry(entry) {
|
|
92
|
+
const kind = (entry?.artifact_kind || "").toString();
|
|
93
|
+
const prefix = kind === "analysis_plan" ? "analysis_plan__" : "live_answer_view__";
|
|
94
|
+
return `${prefix}${entry?.name}`;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function isArrayOrStringArray(value) {
|
|
98
|
+
return Array.isArray(value) && value.length > 0;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
for (const entry of catalog.projections ?? []) {
|
|
102
|
+
const expectedId = catalogIdForEntry(entry);
|
|
103
|
+
const seed = expectedSeedById[expectedId];
|
|
104
|
+
if (!seed) {
|
|
105
|
+
pushIssue("seed_missing", `projection ${expectedId} missing in answer_artifacts.seed.jsonl`);
|
|
106
|
+
continue;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const listed = listedById.get(expectedId);
|
|
110
|
+
const listedPayload = listed ? parseArtifactPayload(listed) : null;
|
|
111
|
+
const seedPayload = safeParseJson(seed.artifact?.payload_json) || {};
|
|
112
|
+
|
|
113
|
+
const artifactKind = seed.artifact?.artifact_kind ?? entry.artifact_kind;
|
|
114
|
+
if (artifactKind === "analysis_plan") {
|
|
115
|
+
const questions = seedPayload.competency_questions ?? listedPayload?.competency_questions;
|
|
116
|
+
if (!isArrayOrStringArray(questions)) {
|
|
117
|
+
pushIssue("payload", `${expectedId}: competency_questions missing or empty`);
|
|
118
|
+
}
|
|
119
|
+
} else if (artifactKind === "live_answer_view") {
|
|
120
|
+
if (!listed && listOk) {
|
|
121
|
+
pushIssue("registry", `${expectedId}: not found in artifact list`);
|
|
122
|
+
}
|
|
123
|
+
const summary = seedPayload.summary ?? listedPayload?.summary;
|
|
124
|
+
if (typeof summary !== "string" || summary.trim() === "") {
|
|
125
|
+
pushIssue("payload", `${expectedId}: summary missing in payload`);
|
|
126
|
+
}
|
|
127
|
+
if (!isArrayOrStringArray(seedPayload.refresh_checks ?? listedPayload?.refresh_checks)) {
|
|
128
|
+
pushIssue("payload", `${expectedId}: refresh_checks missing or empty`);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
const catalogIds = expectedCatalogIds;
|
|
134
|
+
const listedLiveIds = listedIds;
|
|
135
|
+
const missingFromRegistry = expectedLiveArtifactIds.filter((id) => !listedIds.includes(id));
|
|
136
|
+
const missingFromSeed = catalogIds.filter((id) => !expectedSeedById[id]);
|
|
137
|
+
|
|
138
|
+
const audit = {
|
|
139
|
+
ok: payloadIssues.length === 0 && consumer.workspace_id === workspaceId && listOk,
|
|
140
|
+
workspace_id: workspaceId,
|
|
141
|
+
catalog_count: catalogIds.length,
|
|
142
|
+
seed_live_views: expectedLiveArtifactIds.length,
|
|
143
|
+
listed_live_views: listedLiveIds.length,
|
|
144
|
+
missing_from_registry: missingFromRegistry,
|
|
145
|
+
missing_from_seed: missingFromSeed,
|
|
146
|
+
required_live_views: expectedLiveArtifactIds,
|
|
147
|
+
listed_live_views_ids: listedLiveIds,
|
|
148
|
+
list_ok: listOk,
|
|
149
|
+
payload_issues: payloadIssues,
|
|
150
|
+
consumer_workspace: consumer.workspace_id,
|
|
151
|
+
consumer_facet_queries: (consumer.facet_queries ?? []).length,
|
|
152
|
+
consumer_answer_artifacts: (consumer.answer_artifacts ?? []).length,
|
|
153
|
+
generated_at: new Date().toISOString()
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
if (consumer.workspace_id !== workspaceId) {
|
|
157
|
+
payloadIssues.push({
|
|
158
|
+
label: "consumer_contract",
|
|
159
|
+
detail: `consumer workspace mismatch: expected ${workspaceId}, got ${consumer.workspace_id}`
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
if (!listOk) {
|
|
164
|
+
audit.list_skipped = true;
|
|
165
|
+
audit.list_stderr = (listRes.stderr || listRes.stdout || "").slice(0, 500);
|
|
166
|
+
payloadIssues.push({
|
|
167
|
+
label: "registry_list",
|
|
168
|
+
detail: "artifact list command failed; cannot validate live answer registry state"
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
if (missingFromRegistry.length > 0 && listOk) {
|
|
173
|
+
payloadIssues.push({
|
|
174
|
+
label: "missing_from_registry",
|
|
175
|
+
detail: `missing live_answer_view ids: ${missingFromRegistry.join(", ")}`
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
if (catalog.projections && catalogIds.length > 0 && expectedLiveArtifactIds.length === 0) {
|
|
180
|
+
payloadIssues.push({ label: "seed", detail: "no live_answer_view entries in answer_artifacts.seed.jsonl" });
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
audit.ok = payloadIssues.length === 0 && consumer.workspace_id === workspaceId && listOk;
|
|
184
|
+
|
|
185
|
+
writeFileSync(join(reportsDir, "projection_audit.json"), JSON.stringify(audit, null, 2) + "\n", "utf8");
|
|
186
|
+
|
|
187
|
+
const seedLiveViews = seedLines
|
|
188
|
+
.filter((row) => row.artifact?.artifact_kind === "live_answer_view")
|
|
189
|
+
.map((row) => ({
|
|
190
|
+
id: row.artifact?.artifact_id,
|
|
191
|
+
workspace_id: row.artifact?.workspace_id
|
|
192
|
+
}));
|
|
193
|
+
|
|
194
|
+
writeFileSync(join(reportsDir, "consumer_contract.validation.json"), JSON.stringify({
|
|
195
|
+
ok: audit.ok && (consumer.workspace_id === workspaceId),
|
|
196
|
+
workspace_id: consumer.workspace_id,
|
|
197
|
+
facet_queries: consumer.facet_queries?.length ?? 0,
|
|
198
|
+
answer_artifacts: consumer.answer_artifacts?.length ?? 0,
|
|
199
|
+
expected_live_views: expectedLiveArtifactIds,
|
|
200
|
+
listed_live_views: listedLiveIds,
|
|
201
|
+
seed_live_views: seedLiveViews
|
|
202
|
+
}, null, 2) + "\n", "utf8");
|
|
203
|
+
|
|
204
|
+
console.log(JSON.stringify(audit, null, 2));
|
|
205
|
+
process.exit(audit.ok ? 0 : 1);
|
|
206
|
+
|
|
207
|
+
function parseArtifactPayload(artifact) {
|
|
208
|
+
if (!artifact) return null;
|
|
209
|
+
const raw = artifact.payload_json ?? artifact.payload ?? artifact.payload_raw ?? artifact.payloadData;
|
|
210
|
+
return safeParseJson(raw);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
function safeParseJson(value) {
|
|
214
|
+
if (value == null) return null;
|
|
215
|
+
if (typeof value === "object") return value;
|
|
216
|
+
if (typeof value !== "string") return null;
|
|
217
|
+
try {
|
|
218
|
+
return JSON.parse(value);
|
|
219
|
+
} catch {
|
|
220
|
+
return null;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
function parseFlag(argv, name, defaultValue) {
|
|
225
|
+
const index = argv.indexOf(name);
|
|
226
|
+
if (index === -1) return defaultValue;
|
|
227
|
+
return argv[index + 1] ?? defaultValue;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
function resolveGcp() {
|
|
231
|
+
const candidates = [
|
|
232
|
+
process.env.GHOSTCRAB_GCP,
|
|
233
|
+
process.env.GHOSTCRAB_ROOT ? join(process.env.GHOSTCRAB_ROOT, "bin", "gcp.mjs") : "",
|
|
234
|
+
join(pkgRoot, "bin", "gcp.mjs"),
|
|
235
|
+
join(pkgRoot, "node_modules", "@mindflight", "ghostcrab-personal-mcp", "bin", "gcp.mjs"),
|
|
236
|
+
resolve(pkgRoot, "..", "ghostcrab-personal-mcp", "bin", "gcp.mjs")
|
|
237
|
+
].filter(Boolean);
|
|
238
|
+
const found = candidates.find((candidate) => existsSync(candidate));
|
|
239
|
+
return found ?? join(pkgRoot, "bin", "gcp.mjs");
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
function listArtifactsFromSqlite() {
|
|
243
|
+
const db = process.env.GHOSTCRAB_SQLITE_PATH;
|
|
244
|
+
if (!db || !existsSync(db)) return { ok: false, artifacts: [] };
|
|
245
|
+
const sql = `SELECT artifact_id, slug, workspace_id, artifact_kind, public_label, lifecycle, state, payload_json, legacy_ref FROM mindbrain_answer_artifacts WHERE workspace_id='${workspaceId.replace(/'/g, "''")}' AND artifact_kind='live_answer_view' LIMIT 500;`;
|
|
246
|
+
const res = spawnSync("sqlite3", ["-json", db, sql], { encoding: "utf8" });
|
|
247
|
+
if (res.status !== 0) return { ok: false, artifacts: [] };
|
|
248
|
+
try {
|
|
249
|
+
const artifacts = JSON.parse(res.stdout || "[]");
|
|
250
|
+
return { ok: Array.isArray(artifacts), artifacts: Array.isArray(artifacts) ? artifacts : [] };
|
|
251
|
+
} catch {
|
|
252
|
+
return { ok: false, artifacts: [] };
|
|
253
|
+
}
|
|
254
|
+
}
|