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):

ComponentStatusLocation
GraphService✅ Implementedsrc/graph/graph.service.ts
GraphSyncService✅ Implementedsrc/graph/graph-sync.service.ts
GraphExpansionService✅ Implementedsrc/graph/graph-expansion.service.ts
GraphAdminController✅ Implementedsrc/graph/graph-admin.controller.ts
Sync Endpoint✅ ReadyPOST /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:

CriteriaApache AGENeo4j
InfrastructurePostgres extensionSeparate JVM service
Query LanguageOpenCypherCypher
IntegrationNative pg connectionRequires driver
MemoryShared with PostgresDedicated heap
LicensingApache 2.0GPL/Commercial
Local SetupSingle containerAdditional 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:

BeforeAfter
GraphRAG (Neo4j) | ⏳ TODOGraphRAG (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

  1. Schema Discovery: GraphSyncService queries information_schema from main Postgres
  2. Graph Hydration: Creates :Table, :Column nodes and :HAS_COLUMN, :FOREIGN_KEY edges
  3. Query Expansion: GraphExpansionService traverses graph to find related tables
  4. SQL Generation: Expanded context improves JOIN path accuracy

4. Implementation Plan

Phase 1: Infrastructure (Immediate)

  • Add jorvis-graph-db service to docker-compose.local.yml
  • Add graph_data volume
  • Use Docker Compose profile graph for optional activation

Phase 2: Configuration

  • Add JORVIS_GRAPH_DATABASE_URL to environment examples
  • Document startup sequence (graph DB must be ready before sync)

Phase 3: Testing

  • Verify AGE extension loads (LOAD 'age';)
  • Run /internal/graph/sync endpoint
  • 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/age image digest for reproducibility (PG17)
  • Consider Neo4j for future enterprise/analytics use cases

7. Alternatives Considered

AlternativeReason Rejected
Neo4jHeavy JVM sidecar, separate licensing, overkill for schema graph
NetworkXIn-memory only, no persistence, doesn't scale
Recursive SQLComplex to maintain, poor readability for graph traversal
DGraphAnother 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.md
  • docs/agent_ops/OUTBOX/task_s2_evidence.md
    Notes: API sync validated (/internal/graph/sync), graph created in ag_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.