# Sofiia Dialog Graph — Canonical Contract v1.0 ## Core Invariants Every meaningful artifact in the Sofiia system MUST be represented in the Dialog Graph: ``` 1. Every artifact has a node. 2. Every action has an edge. 3. No artifact exists without graph presence. ``` --- ## Node Types | node_type | ref_id points to | Created by | |-----------------|-----------------------|------------------------------------| | `message` | message_id (session) | session message handler | | `task` | tasks.task_id | `create_task()` — atomically | | `meeting` | meetings.meeting_id | `create_meeting()` — atomically | | `doc` | documents.doc_id | document upload/create | | `agent_run` | run_id (supervisor) | `create_evidence_pack()` | | `ops_run` | job_id (ops) | ops job completion hook | | `repo_changeset`| changeset_id | repo diff / PR tracking | | `pull_request` | PR number/id | PR flow integration | | `decision` | decision_id | explicit decision recording | | `goal` | goal_id | strategic goal setting | --- ## Edge Types | edge_type | Meaning | Example | |---------------------|-------------------------------------------|------------------------------------| | `references` | A mentions/cites B | message → doc | | `summarizes` | A is a summary of B | doc → session | | `derives_task` | A produced task B | message → task | | `updates_doc` | A updates/modifies doc B | ops_run → doc | | `schedules_meeting` | A scheduled meeting B | message → meeting | | `resolves` | A resolves/closes B | task → task (blocker resolved) | | `blocks` | A blocks B | task → task | | `relates_to` | A is related to B | any → any | | `produced_by` | B was produced by run A | agent_run → task/doc | | `executed_as` | plan A was executed as ops_run B | decision → ops_run | --- ## Atomic Creation Rules When creating an artifact, the node MUST be created in the same SQLite transaction: ```python # CORRECT: task + node in one BEGIN...COMMIT await db.execute("BEGIN") await db.execute("INSERT INTO tasks ...") await db.execute("INSERT INTO dialog_nodes ... ON CONFLICT DO UPDATE") await db.commit() # WRONG: two separate commits await create_task(...) # commit 1 await upsert_dialog_node(...) # commit 2 — can diverge ``` Functions that guarantee atomicity: - `db.create_task()` — always upserts task node - `db.create_meeting()` — always upserts meeting node - `db.create_evidence_pack()` — creates agent_run node + derived task nodes + edges --- ## Evidence Pack After every Supervisor run, an Evidence Pack MUST be recorded: ```json { "run_id": "", "graph_name": "release_check|incident_triage|...", "status": "completed", "summary": "...", "findings": [...], "recommendations": [...], "follow_up_tasks": [ {"title": "...", "description": "...", "priority": "normal|high|urgent"} ] } ``` This creates: 1. `agent_run` dialog node 2. `doc_version` with evidence markdown (if evidence_log.md exists in project) 3. `task` nodes for each follow_up_task (in `backlog` with label `evidence`) 4. `produced_by` edges: agent_run → each task node --- ## Integrity Checks Run `GET /api/projects/{id}/graph/integrity` to verify: | Check | Description | |-------------------------|------------------------------------------------------| | `orphaned_edge_from` | Edges referencing non-existent from_node | | `orphaned_edge_to` | Edges referencing non-existent to_node | | `dangling_task_nodes` | `node_type=task` nodes with no matching task row | | `dangling_meeting_nodes`| `node_type=meeting` nodes with no matching meeting | | `self_loop_edges` | Edges where from_node_id == to_node_id | **Expected**: `{"ok": true, "violations": []}` --- ## DDL Freeze As of v1.0, the schema is **frozen**. Any schema changes require: 1. A migration file in `services/sofiia-console/migrations/` 2. Update to this contract document 3. Update to `tests/test_graph_integrity.py` Current canonical DDL: `services/sofiia-console/app/db.py` (init_db function) --- ## Quality Gates Before merging any feature that touches artifacts: | Gate | Check | |---------------------|----------------------------------------------| | **Reproducibility** | Does the feature create a node + edge? | | **Safety** | Is creation atomic (single transaction)? | | **Observability** | Does `GET /graph/integrity` stay `ok: true`? |