labfy-investigation/docs/architecture/CORE_GRAPH_READONLY.md
fy59 d65f9559e6 chore(checkpoint): preserve local investigation workbench
Preserve the local graph-first investigation stack through macro V1.

Include reviewed source, tests, contracts, documentation and synthetic assets.

Retain the standalone financial V21 test foundation without runtime migration.

Runtime remains V20; operational JobStore remains V3.

Local experimental checkpoint, not a stable release.
2026-09-27 08:34:46 +02:00

5.7 KiB

Projection cœur → Web en lecture seule — lot J3

État : CURRENT local, expérimental, uniquement sur fixture synthétique Runtime lu : schéma V20 Contrat transport : labfy.web_graph.snapshot.v2

Ce lot relie pour la première fois le prototype Web aux données réellement persistées et relues par le cœur C :

core_graph_demo
  → database_initialize + DAO de production
  → Enquete.sqlite V20 synthétique
  → database_open_read_only
  → CoreGraphProjectionService
  → snapshot JSON v2 atomique
  → serveur Python statique
  → prototype Web

Le JSON n'est ni une nouvelle base métier, ni une source de vérité. Le serveur Python ne lit pas SQLite, ne reçoit aucun chemin de base par HTTP et ne complète pas les décisions du cœur.

API, ownership et cohérence

  • database_open_read_only(path, error) ouvre un fichier existant avec SQLITE_OPEN_READONLY, active query_only, refuse toute version autre que V20 et n'appelle ni migration ni installation de schéma. L'appelant ferme la connexion avec database_close().
  • database_transaction_begin_read_only() démarre une transaction différée : la première lecture fixe le snapshot SQLite sans demander de verrou d'écriture. Le service valide ou annule cette transaction avant de retourner.
  • L'ouverture ne change ni journal_mode ni synchronous et n'active pas immutable. Le writer de fixture utilise donc le journal de rollback du runtime ; son éventuel fichier Enquete.sqlite-journal est transitoire et supprimé à la fermeture. Le lecteur fermé ne laisse ni -journal, ni -wal, ni -shm, propriété vérifiée par le test C ciblé.
  • core_graph_projection_service_collect() emprunte la connexion et retourne un CoreGraphSnapshot possédé par l'appelant. Une erreur DAO ou une limite atteinte invalide toute la collecte ; aucun graphe vide n'est présenté comme complet.
  • Le snapshot possède ses chaînes, nœuds, arêtes et capabilities. Les vues de getters sont empruntées jusqu'à core_graph_snapshot_free().
  • core_graph_snapshot_write_atomic() sérialise entièrement en mémoire, vérifie UTF-8 et volume, écrit un staging privé voisin puis renomme. Aucun fichier final tronqué n'est publié.

Mapping actuel

Objet demandé Stockage V20 API de lecture Provenance exposée Limite honnête
Entité/personne/identifiant entites InvestigationGraphLoader / EntityDao état et identité persistante provenance uniforme absente
Relation métier relations InvestigationGraphLoader / RelationDao identité, direction, sémantique justifications non projetées dans ce lot
Preuve preuves EvidenceDao source/description si présentes aucun fichier servi par HTTP
Rattachement preuve-entité preuve_entites EvidenceEntityDao lien evidence_attachment rattachement ≠ preuve d'identité
Observation evidence_entity_observations variante typée list_observations() brut, normalisé, corrigé, revue, origine et promotion transformation parfois non identifiée
Exécution OSINT historique osint_executions OsintExecutionDao typé outil/version, dates, état, extrait brut borné et liens pas d'artefact/transformation séparé en V20
Claims/hypothèses/finance V21 non raccordés au runtime supporté aucune dans ce lot aucune aucune ligne ou chaîne fictive

Les références sont qualifiées par investigation_id, object_kind et object_id. Les identifiants de graphe préfixent la catégorie, par exemple entity:<uuid> et evidence:<uuid> : deux tables peuvent donc partager une valeur d'identifiant sans collision. Les arêtes composites sans UUID de table utilisent une identité dérivée déterministe à partir des clés persistées ; leur sémantique indique explicitement cette projection.

Contrat snapshot v2

La v1 J2 reste stricte et inchangée. La v2 ajoute :

  • origin: "core", schema_version: 20, transport_revision: 1 ;
  • complete et les limites ayant gouverné l'export ;
  • object_ref avec contexte d'enquête ;
  • détails structurés d'observation et de provenance ;
  • sémantique et disposition des arêtes ;
  • absence de coordonnées métier.

Le pont Web calcule une disposition déterministe en mémoire. Les déplacements et positions épinglées restent de l'état de vue et ne sont jamais réécrits dans SQLite. Les limites actuelles sont 500 nœuds, 1 000 arêtes, 1 MiB de JSON et 512 octets UTF-8 pour l'extrait stdout autorisé. Un dépassement échoue au lieu de publier un résultat partiel. La navigation de provenance du prototype reste bornée à 12 niveaux et 20 branches.

Les paramètres/argv, chemins absolus, stderr et fichiers de preuve ne sont pas exportés. La fixture atteste explicitement son caractère synthétique au sérialiseur ; celui-ci ne le déduit jamais du nom du dossier.

Démonstration

Après compilation :

make -j8 core-graph-demo
cd prototypes/web-graph
python3 run_core_demo.py --port 8765

Le lanceur crée un répertoire temporaire privé, invoque l'exécutable fixé avec des argv séparés, borne son exécution à 30 secondes, charge uniquement core-snapshot.json accompagné de son manifeste, puis écoute sur 127.0.0.1. Un échec d'export affiche un état d'erreur et ne retombe jamais sur les fixtures Python J2.

Ce lot ne clôt pas J3 : provenance uniforme en écriture, publication transactionnelle généralisée, backend Web de production, Toolkit, jobs, policies, claims/hypothèses et migration financière restent distincts.

L'extension ciblée analyse EML → graphe réutilise ce lecteur, ajoute le snapshot v3 pour les extractions persistées et ne modifie ni la v2 ni la fixture de ce lot.