ADR-0020: GraphRAG Implementation Strategy
ADR-0020: GraphRAG Implementation Strategy
Date: 2026-01-23 Status: DEPRECATED (Superseded by ADR-0026) Author: Rovo (Architect Agent) Context: Phase S (GraphRAG), supersedes Neo4j references in ROADMAP.md
1. Context
Current State
Jorvis has implemented GraphRAG services using Apache AGE (a Graph Extension for PostgreSQL), and the local AGE infrastructure is now provisioned (Task-S-1):
| Component | Status | Location |
|---|---|---|
GraphService | ✅ Implemented | src/graph/graph.service.ts |
GraphSyncService | ✅ Implemented | src/graph/graph-sync.service.ts |
GraphExpansionService | ✅ Implemented | src/graph/graph-expansion.service.ts |
GraphAdminController | ✅ Implemented | src/graph/graph-admin.controller.ts |
| Sync Endpoint | ✅ Ready | POST /internal/graph/sync |
Documentation Gap
ROADMAP.md (line 101) incorrectly states:
| **GraphRAG (Neo4j)** | ⏳ TODO |
This is outdated. The decision to use Apache AGE was made in ADR-0011 (2026-01-06).
Infrastructure Status (Resolved)
The local Docker stack now includes the AGE container with a pinned image digest and a graph profile. The API is
configured via JORVIS_GRAPH_DATABASE_URL, and graph sync has been validated.
2. Decision
2.1 Technology: Apache AGE (Confirmed)
We confirm the ADR-0011 decision to use Apache AGE:
| Criteria | Apache AGE | Neo4j |
|---|---|---|
| Infrastructure | Postgres extension | Separate JVM service |
| Query Language | OpenCypher | Cypher |
| Integration | Native pg connection | Requires driver |
| Memory | Shared with Postgres | Dedicated heap |
| Licensing | Apache 2.0 | GPL/Commercial |
| Local Setup | Single container | Additional container |
Neo4j Status: Optional/Future — may be considered for enterprise deployments requiring advanced graph analytics.
2.2 Infrastructure: Add jorvis-graph-db Container
Add Apache AGE container to local Docker stack:
# deploy/docker-compose.local.yml
services:
jorvis-graph-db:
# Pinned digest from apache/age:latest (PG17)
image: apache/age@sha256:4ba7dd683fc83fcac0e603f76448f6afba0507cf4d7d21673cede0d6ff0265b2
container_name: jorvis-graph-db
ports:
- "5433:5432"
environment:
- POSTGRES_USER=jorvis_graph
- POSTGRES_PASSWORD=graphpass
- POSTGRES_DB=jorvis_graph
volumes:
- graph_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U jorvis_graph"]
interval: 10s
timeout: 5s
retries: 5
profiles:
- graph
volumes:
graph_data:
Connection String:
# Docker network
JORVIS_GRAPH_DATABASE_URL=postgresql://jorvis_graph:graphpass@jorvis-graph-db:5432/jorvis_graph
# Host access (mapped port)
# postgresql://jorvis_graph:graphpass@localhost:5433/jorvis_graph
2.3 Sync Automation
The sync endpoint is already implemented:
# Trigger graph sync (requires SERVICE_TOKEN)
curl -X POST http://localhost:3000/internal/graph/sync \
-H "Authorization: Bearer $INTERNAL_SERVICE_TOKEN"
Response:
{
"status": "success",
"nodesCreated": 150,
"edgesCreated": 200,
"durationMs": 1234
}
2.4 Documentation Updates
Update ROADMAP.md to reflect actual implementation and infra completion:
| Before | After |
|---|---|
GraphRAG (Neo4j) | ⏳ TODO | GraphRAG (Apache AGE) | ✅ IMPLEMENTED (infra + sync validated) |
3. Architecture
┌─────────────────────────────────────────────────────────────────┐
│ Docker Network │
│ │
│ ┌─────────────────┐ ┌─────────────────────────────────┐ │
│ │ jorvis-api │ │ jorvis-graph-db │ │
│ │ (NestJS) │─────▶│ (Apache AGE on Postgres 17) │ │
│ │ │:5433 │ │ │
│ │ GraphModule: │ │ Graph: jorvis_schema │ │
│ │ • GraphService │ │ ├── :Table nodes │ │
│ │ • GraphSync │ │ ├── :Column nodes │ │
│ │ • GraphExpand │ │ ├── [:HAS_COLUMN] edges │ │
│ └─────────────────┘ │ └── [:FOREIGN_KEY] edges │ │
│ │ └─────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────┐ │
│ │ Main Postgres │ │
│ │ (Data Warehouse)│ ◀── Schema source for sync │
│ └─────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
Data Flow
- Schema Discovery:
GraphSyncServicequeriesinformation_schemafrom main Postgres - Graph Hydration: Creates
:Table,:Columnnodes and:HAS_COLUMN,:FOREIGN_KEYedges - Query Expansion:
GraphExpansionServicetraverses graph to find related tables - SQL Generation: Expanded context improves JOIN path accuracy
4. Implementation Plan
Phase 1: Infrastructure (Immediate)
- Add
jorvis-graph-dbservice todocker-compose.local.yml - Add
graph_datavolume - Use Docker Compose profile
graphfor optional activation
Phase 2: Configuration
- Add
JORVIS_GRAPH_DATABASE_URLto environment examples - Document startup sequence (graph DB must be ready before sync)
Phase 3: Testing
- Verify AGE extension loads (
LOAD 'age';) - Run
/internal/graph/syncendpoint - Validate node/edge creation with Cypher queries
Phase 4: Documentation
- Update ROADMAP.md (Neo4j → AGE)
- Update LOCAL_RUNTIME.md with graph setup
- Archive/deprecate any Neo4j references
5. Startup Commands
# Start local stack with GraphRAG
docker compose -f deploy/docker-compose.local.yml --profile graph up -d
# Wait for graph DB to be ready
docker compose -f deploy/docker-compose.local.yml exec jorvis-graph-db pg_isready -U jorvis_graph
# Trigger initial sync
curl -X POST http://localhost:3000/internal/graph/sync \
-H "Authorization: Bearer $INTERNAL_SERVICE_TOKEN"
# Verify graph contents
docker compose -f deploy/docker-compose.local.yml exec jorvis-graph-db psql -U jorvis_graph -d jorvis_graph -c "
LOAD 'age';
SET search_path = ag_catalog, public;
SELECT * FROM cypher('jorvis_schema', \$\$MATCH (t:Table) RETURN t.name LIMIT 5\$\$) AS (name agtype);
"
6. Consequences
Positive
- Single database technology — No additional JVM/Neo4j to manage
- Native Postgres integration — Existing backups, monitoring, tooling work
- OpenCypher standard — Industry-standard graph query language
- Lower resource footprint — AGE shares Postgres resources
Negative
- AGE limitations — No parameterized Cypher (must escape strings)
- Postgres version dependency — Requires a compatible PG version (currently PG17 via
apache/age) - Less mature ecosystem — Neo4j has richer tooling/visualization
Mitigations
- String escaping implemented in
GraphSyncService.escape() - Pinned
apache/ageimage digest for reproducibility (PG17) - Consider Neo4j for future enterprise/analytics use cases
7. Alternatives Considered
| Alternative | Reason Rejected |
|---|---|
| Neo4j | Heavy JVM sidecar, separate licensing, overkill for schema graph |
| NetworkX | In-memory only, no persistence, doesn't scale |
| Recursive SQL | Complex to maintain, poor readability for graph traversal |
| DGraph | Another external service, GraphQL-focused |
8. Neo4j Future Consideration
Neo4j remains a valid option for future enterprise features:
- Advanced graph algorithms (PageRank, community detection)
- Graph visualization (Neo4j Bloom)
- Horizontal scaling (Neo4j Cluster)
Status: Optional — Not required for Phase S core functionality.
9. Implementation Status
Status: ✅ Infrastructure + ingestion complete (Task-S-1, Task-S-2 — 2026-01-23)
Evidence:
docs/agent_ops/OUTBOX/task_s1_evidence.mddocs/agent_ops/OUTBOX/task_s2_evidence.md
Notes: API sync validated (/internal/graph/sync), graph created inag_catalog.ag_graph, sample nodes verified.
10. References
- ADR-0011: Graph Architecture (original AGE decision)
- Code:
analytics-platform/src/graph/(full implementation) - Apache AGE: https://age.apache.org/
- Docker Image:
apache/age@sha256:4ba7dd683fc83fcac0e603f76448f6afba0507cf4d7d21673cede0d6ff0265b2
11. Approval
Status: DEPRECATED (Superseded by ADR-0026) — Approved by George (User) 2026-01-23
This ADR clarifies the GraphRAG implementation strategy and resolves the Neo4j/AGE documentation gap.