Config policies (16 files): alert_routing, architecture_pressure, backlog, cost_weights, data_governance, incident_escalation, incident_intelligence, network_allowlist, nodes_registry, observability_sources, rbac_tools_matrix, release_gate, risk_attribution, risk_policy, slo_policy, tool_limits, tools_rollout Ops (22 files): Caddyfile, calendar compose, grafana voice dashboard, deployments/incidents logs, runbooks for alerts/audit/backlog/incidents/sofiia/voice, cron jobs, scripts (alert_triage, audit_cleanup, migrate_*, governance, schedule), task_registry, voice alerts/ha/latency/policy Docs (30+ files): HUMANIZED_STEPAN v2.7-v3 changelogs and runbooks, NODA1/NODA2 status and setup, audit index and traces, backlog, incident, supervisor, tools, voice, opencode, release, risk, aistalk, spacebot Made-with: Cursor
134 lines
5.2 KiB
Markdown
134 lines
5.2 KiB
Markdown
# 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": "<uuid>",
|
|
"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`? |
|