SDS-003: Knowledge Graph Service


sds: id: SDS-003 title: Knowledge Graph Service bounded_context: semantic-core satisfies_prds: [PRD-006, PRD-007, PRD-008] satisfies_adrs: [ADR-004, ADR-006, ADR-040] version: 1.1.0 status: approved owners: [“sea-core-team”] created: 2025-12-01 —

Addresses Requirements

MVP Status

MVP


Component Description

The Knowledge Graph Service provides an inferential projection of the Semantic Core, storing and managing enterprise knowledge as a graph of interconnected entities and relationships. It adopts a SPARQL-first approach, prioritizing explicit query patterns over implicit reasoning. While it supports RDF/OWL for semantic representation, heavyweight reasoning is optional and secondary to materialized projections.


Technical Details

Interfaces

Direction Description Format
Input Semantic data RDF triples (Turtle/JSON-LD)
Input Queries SPARQL (Primary), Cypher (Secondary)
Input Constraints SHACL definitions
Output Graph query results JSON/RDF
Output Inferred relationships JSON
Output SHACL validation reports JSON

Protocols


Data Model

Refer to SDS-001: Data Model Schemas for:


Dependencies

Dependency Type Version Justification
Oxigraph Rust Binary / Python 0.4.x Production-grade RDF triple store with SPARQL 1.1 engine
pyoxigraph Python Library 0.4.x Python bindings for Oxigraph
RDFLib Python Library 7.1.x Fallback RDF store for development/testing
Comunica Node.js Library 3.x Execute SPARQL queries in Node.js for TypeScript adapters
pySHACL Python Library 0.29.x Validate knowledge graph integrity against SHACL shapes
OWL-RL Python Library 7.1.x Enable logical inference over OWL ontologies

SHACL Enforcement Configuration

Runtime validation behavior is controlled by a single configuration source of truth and injected into runtime adapters:

Setting Type Default Purpose
SHACL_ENFORCEMENT_ENABLED boolean true Enable or disable enforcement
SHACL_ENFORCEMENT_CONTEXTS CSV empty If empty, all contexts are active
SHACL_MAINTENANCE_MODE boolean false Log violations but do not raise
SHACL_SHAPES_VERSION string v1 Versioned shapes selection

Rules


SHACL Validation Contract

validate_with_shacl(...) returns a structured ValidationResult at all call sites:

Field Type Description
conforms boolean SHACL conformance result
violations array Normalized violation details
report_graph string SHACL report as Turtle or JSON-LD
requires_context array Missing contexts from sea:requiresContext
shape_version string Shapes version used
maintenance_mode boolean Whether enforcement was muted
enforcement_applied boolean Whether an error would be raised
cache_hit boolean Whether validation used cached result

Fail-Closed Policy


Shape Loading and Versioning


Validation Scope Rules


Cross-Context Dependencies


Legacy Data Exemption


Port Reference

This service integrates with the following hexagonal ports:

Port Location Purpose
TripleStorePort libs/skeleton/graph/ports/src/lib/triple-store.port.ts RDF triple storage and SPARQL query interface

The TripleStorePort provides:


Debt Ontology (SEA-DEBT)

Prefix: sea-debt: <http://sea-forge.com/schema/debt#>

Class/Property Type Description
sea-debt:SemanticDebtItem Class Represents a recorded instance of semantic debt (e.g., translation loss).
sea-debt:Evidence Class Proof or context explaining why the debt occurred.
sea-debt:Decision Class The architectural decision or waiver allowing this debt.
sea-debt:MitigationPlan Class Planned steps to resolve the debt.
sea-debt:hasEvidence Property Links DebtItem to Evidence.
sea-debt:governedBy Property Links DebtItem to a Decision.
sea-debt:hasMitigation Property Links DebtItem to a MitigationPlan.
sea-debt:regardingConcept Property Links DebtItem/Evidence to a specific Semantic Concept (URI).
sea-debt:status Property Status of the debt (e.g., “Accepted”, “Expired”, “Critical”).
sea-debt:expiryDate Property Date when the accepted debt expires.

Incident Ontology (SEA-INC)

Prefix: sea-inc: <http://sea-forge.com/schema/incident#>

Groups multiple SemanticDebtItem instances under a common root cause. See SDS-016: Semantic Incidents for the complete specification.

Class/Property Type Description
sea-inc:SemanticIncident Class Groups related debt items under common root cause
sea-inc:RootCause Class The underlying cause of the incident
sea-inc:hasDebtItem Property Links Incident to constituent DebtItems
sea-inc:hasRootCause Property Links Incident to RootCause
sea-inc:affectedContext Property Links Incident to affected BoundedContext
sea-inc:owner Property Links Incident to responsible Principal
sea-inc:status Property Current lifecycle state (open, triaging, contained, resolving, resolved, postmortem, closed)
sea-inc:severity Property Aggregate severity
sea-inc:supersedes Property Links DebtItem to superseded DebtItem

Incident Query: Active Incidents by Severity

1
2
3
4
5
6
7
8
9
10
11
12
13
PREFIX sea-inc: <http://sea-forge.com/schema/incident#>

SELECT ?incident ?title ?severity (COUNT(?debt) AS ?debtCount)
WHERE {
  ?incident a sea-inc:SemanticIncident ;
            sea-inc:title ?title ;
            sea-inc:severity ?severity ;
            sea-inc:status ?status ;
            sea-inc:hasDebtItem ?debt .
  FILTER (?status IN ("open", "triaging", "contained", "resolving"))
}
GROUP BY ?incident ?title ?severity
ORDER BY DESC(?severity) DESC(?debtCount)

Projection Mappings (Events -> Triples)

These rules define how SemanticDebtCreated events are projected into the graph.

Source: SemanticDebtCreated Event Target: RDF Triples

  1. Debt Item Creation:
    1
    2
    3
    4
    
    <sea:debt:{eventId}> a sea-debt:SemanticDebtItem ;
        rdfs:label "{payload.title}" ;
        sea-debt:status "{payload.status}" ;
        dct:created "{timestamp}" .
    
  2. Linking Concept:
    1
    
    <sea:debt:{eventId}> sea-debt:regardingConcept <sea:concept:{payload.conceptId}> .
    
  3. Attaching Evidence:
    1
    2
    3
    
    <sea:evidence:{evidenceId}> a sea-debt:Evidence ;
        rdfs:comment "{payload.evidenceDescription}" .
    <sea:debt:{eventId}> sea-debt:hasEvidence <sea:evidence:{evidenceId}> .
    

Query Cookbook

1. Blockers by Context

Find debt items that are “Blocking” within a specific context (e.g., PaymentProcessing).

1
2
3
4
5
6
7
8
9
10
11
PREFIX sea-debt: <http://sea-forge.com/schema/debt#>
PREFIX sea: <http://sea-forge.com/schema/core#>

SELECT ?debt ?label ?concept
WHERE {
  ?debt a sea-debt:SemanticDebtItem ;
        sea-debt:status "Blocking" ;
        rdfs:label ?label ;
        sea-debt:regardingConcept ?concept .
  ?concept sea:inContext <http://sea-forge.com/context/PaymentProcessing> .
}

2. Debt Affecting a Concept + Depth-2 Dependencies

Trace debt on a concept and its upstream dependencies (2 levels deep).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
SELECT DISTINCT ?debt ?affectedConcept ?upstreamConcept
WHERE {
   # Debt directly on the concept
   {
     ?debt sea-debt:regardingConcept <http://sea-forge.com/concept/TargetConcept> .
     BIND(<http://sea-forge.com/concept/TargetConcept> AS ?affectedConcept)
   }
   UNION
   # Debt on direct dependency (Level 1)
   {
     <http://sea-forge.com/concept/TargetConcept> sea:dependsOn ?l1 .
     ?debt sea-debt:regardingConcept ?l1 .
     BIND(?l1 AS ?affectedConcept)
   }
   UNION
   # Debt on dependency of dependency (Level 2)
   {
     <http://sea-forge.com/concept/TargetConcept> sea:dependsOn ?l1 .
     ?l1 sea:dependsOn ?l2 .
     ?debt sea-debt:regardingConcept ?l2 .
     BIND(?l2 AS ?affectedConcept)
   }
}

3. Policy Hotspots

Concepts with the highest count of associated debt items.

1
2
3
4
5
6
7
8
9
SELECT ?policy (COUNT(?debt) AS ?debtCount)
WHERE {
  ?debt a sea-debt:SemanticDebtItem ;
        sea-debt:regardingConcept ?policy .
  ?policy a sea:Policy .
}
GROUP BY ?policy
ORDER BY DESC(?debtCount)
LIMIT 10

4. Expired Accepted Debt

Accepted debt that has passed its expiry date.

1
2
3
4
5
6
7
SELECT ?debt ?decision ?expiry
WHERE {
  ?debt sea-debt:status "Accepted" ;
        sea-debt:governedBy ?decision .
  ?decision sea-debt:expiryDate ?expiry .
  FILTER (?expiry < NOW())
}

5. Break-glass Accepted Debt

Debt accepted under “Break-glass” emergency conditions.

1
2
3
4
5
6
SELECT ?debt ?reason
WHERE {
  ?debt sea-debt:governedBy ?decision .
  ?decision sea-debt:type "BreakGlass" ;
            rdfs:comment ?reason .
}

6. Projection Hotspots

Attributes where translation loss frequency is high.

1
2
3
4
5
6
7
8
SELECT ?concept (COUNT(?debt) as ?lossCount)
WHERE {
    ?debt a sea-debt:SemanticDebtItem ;
          sea-debt:type "TranslationLoss" ;
          sea-debt:regardingConcept ?concept .
}
GROUP BY ?concept
ORDER BY DESC(?lossCount)


Snapshot & Versioning Model

Design Principles

Snapshot Schema

A Snapshot represents an immutable, versioned state of the Knowledge Graph at a specific point in time.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "title": "KnowledgeGraphSnapshot",
  "description": "Immutable, versioned state of the Knowledge Graph",
  "type": "object",
  "properties": {
    "id": {
      "type": "string",
      "pattern": "^ifl:snap:[a-f0-9]{64}$",
      "description": "Content-addressable snapshot identifier (SHA-256 hash)"
    },
    "version": {
      "type": "string",
      "description": "Semantic version (e.g., '1.0.0')"
    },
    "timestamp": {
      "type": "string",
      "format": "date-time",
      "description": "UTC timestamp when snapshot was created"
    },
    "parentSnapshotId": {
      "type": "string",
      "pattern": "^ifl:snap:[a-f0-9]{64}$",
      "description": "Parent snapshot ID (null for initial snapshot)"
    },
    "appliedChangeSets": {
      "type": "array",
      "items": {
        "type": "string",
        "pattern": "^ifl:cs:[a-f0-9]{64}$"
      },
      "description": "Ordered list of change set IDs applied to reach this snapshot"
    },
    "tripleCount": {
      "type": "integer",
      "minimum": 0,
      "description": "Total number of RDF triples in this snapshot"
    },
    "contentHash": {
      "type": "string",
      "pattern": "^[a-f0-9]{64}$",
      "description": "SHA-256 hash of canonical triple serialization (Turtle sorted)"
    },
    "metadata": {
      "type": "object",
      "properties": {
        "createdBy": {
          "type": "string",
          "description": "Principal or system that created the snapshot"
        },
        "description": {
          "type": "string",
          "description": "Human-readable description of this snapshot"
        },
        "tags": {
          "type": "array",
          "items": {"type": "string"},
          "description": "Semantic tags (e.g., 'production', 'staging')"
        }
      },
      "additionalProperties": true
    }
  },
  "required": ["id", "version", "timestamp", "contentHash", "appliedChangeSets"]
}

ChangeSet Schema

A ChangeSet represents a set of graph operations (add/delete triples) that can be applied to a snapshot.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "title": "KnowledgeGraphChangeSet",
  "description": "Set of graph operations to transition between snapshots",
  "type": "object",
  "properties": {
    "id": {
      "type": "string",
      "pattern": "^ifl:cs:[a-f0-9]{64}$",
      "description": "Content-addressable change set identifier (SHA-256 hash)"
    },
    "timestamp": {
      "type": "string",
      "format": "date-time",
      "description": "UTC timestamp when change set was created"
    },
    "sourceSnapshotId": {
      "type": "string",
      "pattern": "^ifl:snap:[a-f0-9]{64}$",
      "description": "Snapshot this change set is based on"
    },
    "operations": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "enum": ["add", "delete"],
            "description": "Operation type"
          },
          "triple": {
            "type": "object",
            "properties": {
              "subject": {"type": "string", "format": "uri"},
              "predicate": {"type": "string", "format": "uri"},
              "object": {
                "oneOf": [
                  {"type": "string", "format": "uri"},
                  {
                    "type": "object",
                    "properties": {
                      "value": {"type": "string"},
                      "datatype": {"type": "string", "format": "uri"},
                      "language": {"type": "string"}
                    },
                    "required": ["value"]
                  }
                ]
              }
            },
            "required": ["subject", "predicate", "object"]
          }
        },
        "required": ["type", "triple"]
      },
      "description": "Ordered list of add/delete operations"
    },
    "metadata": {
      "type": "object",
      "properties": {
        "createdBy": {"type": "string"},
        "description": {"type": "string"},
        "source": {
          "type": "string",
          "description": "Origin of change (e.g., 'SemanticDebtCreated event')"
        }
      },
      "additionalProperties": true
    }
  },
  "required": ["id", "timestamp", "sourceSnapshotId", "operations"]
}

Snapshot Query Result Schema

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "title": "SnapshotQueryResult",
  "description": "Result of executing a query against a snapshot",
  "type": "object",
  "properties": {
    "snapshotId": {
      "type": "string",
      "pattern": "^ifl:snap:[a-f0-9]{64}$"
    },
    "query": {
      "type": "object",
      "properties": {
        "language": {
          "type": "string",
          "enum": ["sparql", "cypher"]
        },
        "text": {"type": "string"}
      },
      "required": ["language", "text"]
    },
    "results": {
      "type": "array",
      "items": {"type": "object"},
      "description": "Query result bindings"
    },
    "executionTimeMs": {
      "type": "integer",
      "minimum": 0,
      "description": "Query execution time in milliseconds"
    },
    "resultHash": {
      "type": "string",
      "pattern": "^[a-f0-9]{64}$",
      "description": "SHA-256 hash of canonical result serialization (for stability verification)"
    }
  },
  "required": ["snapshotId", "query", "results", "executionTimeMs", "resultHash"]
}

API Specification

Endpoints

Endpoint Method Description
/graph/query POST Execute Cypher/SPARQL query
/graph/shacl/validate POST Run SHACL validation
/graph/snapshots GET List available snapshots
/graph/snapshots POST Create a new snapshot from current state
/graph/snapshots/{id} GET Retrieve snapshot metadata
/graph/snapshots/{id}/query POST Execute query against specific snapshot
/graph/changesets GET List available change sets
/graph/changesets POST Create a new change set
/graph/changesets/{id} GET Retrieve change set details
/graph/changesets/{id}/apply POST Apply change set to create new snapshot

Snapshot API Request/Response Examples

Create Snapshot

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
POST /graph/snapshots
{
  "version": "1.0.0",
  "description": "Initial production snapshot",
  "tags": ["production", "release-1.0"],
  "createdBy": "system"
}

// Response
{
  "id": "ifl:snap:a3b8c9d2e1f4567890abcdef1234567890abcdef1234567890abcdef12345678",
  "version": "1.0.0",
  "timestamp": "2025-12-30T10:00:00Z",
  "parentSnapshotId": null,
  "appliedChangeSets": [],
  "tripleCount": 1523,
  "contentHash": "a3b8c9d2e1f4567890abcdef1234567890abcdef1234567890abcdef12345678",
  "metadata": {
    "createdBy": "system",
    "description": "Initial production snapshot",
    "tags": ["production", "release-1.0"]
  }
}

List Snapshots

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
GET /graph/snapshots?tags=production&limit=10

// Response
{
  "snapshots": [
    {
      "id": "ifl:snap:a3b8c9d2...",
      "version": "1.0.0",
      "timestamp": "2025-12-30T10:00:00Z",
      "tripleCount": 1523,
      "tags": ["production", "release-1.0"]
    },
    {
      "id": "ifl:snap:b4c9d8e2...",
      "version": "1.1.0",
      "timestamp": "2025-12-31T14:30:00Z",
      "tripleCount": 1687,
      "tags": ["production", "release-1.1"]
    }
  ],
  "total": 2,
  "limit": 10,
  "offset": 0
}

Query Snapshot

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
POST /graph/snapshots/ifl:snap:a3b8c9d2.../query
{
  "language": "sparql",
  "query": "PREFIX sea-debt: <http://sea-forge.com/schema/debt#> SELECT ?debt ?status WHERE { ?debt a sea-debt:SemanticDebtItem ; sea-debt:status ?status . } LIMIT 10"
}

// Response
{
  "snapshotId": "ifl:snap:a3b8c9d2...",
  "query": {
    "language": "sparql",
    "text": "PREFIX sea-debt: <http://sea-forge.com/schema/debt#> SELECT ?debt ?status WHERE { ?debt a sea-debt:SemanticDebtItem ; sea-debt:status ?status . } LIMIT 10"
  },
  "results": [
    {
      "debt": "http://sea-forge.com/debt/123",
      "status": "Blocking"
    },
    {
      "debt": "http://sea-forge.com/debt/456",
      "status": "Accepted"
    }
  ],
  "executionTimeMs": 45,
  "resultHash": "f1e2d3c4b5a6978901234567890abcdef1234567890abcdef1234567890abcdef"
}

Create ChangeSet

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
POST /graph/changesets
{
  "sourceSnapshotId": "ifl:snap:a3b8c9d2...",
  "description": "Add new debt item from SemanticDebtCreated event",
  "createdBy": "event-projector",
  "source": "SemanticDebtCreated:evt-789",
  "operations": [
    {
      "type": "add",
      "triple": {
        "subject": "http://sea-forge.com/debt/789",
        "predicate": "http://www.w3.org/1999/02/22-rdf-syntax-ns#type",
        "object": "http://sea-forge.com/schema/debt#SemanticDebtItem"
      }
    },
    {
      "type": "add",
      "triple": {
        "subject": "http://sea-forge.com/debt/789",
        "predicate": "http://www.w3.org/2000/01/rdf-schema#label",
        "object": {
          "value": "Missing validation for payment amount",
          "datatype": "http://www.w3.org/2001/XMLSchema#string"
        }
      }
    },
    {
      "type": "add",
      "triple": {
        "subject": "http://sea-forge.com/debt/789",
        "predicate": "http://sea-forge.com/schema/debt#status",
        "object": {
          "value": "Blocking",
          "datatype": "http://www.w3.org/2001/XMLSchema#string"
        }
      }
    }
  ]
}

// Response
{
  "id": "ifl:cs:c4d5e6f7g8h9i0j1k2l3m4n5o6p7q8r9s0t1u2v3w4x5y6z7a8b9c0d1e2f3g4h5",
  "timestamp": "2025-12-30T11:15:00Z",
  "sourceSnapshotId": "ifl:snap:a3b8c9d2...",
  "operations": [...],
  "metadata": {
    "createdBy": "event-projector",
    "description": "Add new debt item from SemanticDebtCreated event",
    "source": "SemanticDebtCreated:evt-789"
  }
}

Apply ChangeSet

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
POST /graph/changesets/ifl:cs:c4d5e6f7.../apply
{
  "version": "1.0.1",
  "description": "Applied debt item creation",
  "tags": ["development"]
}

// Response
{
  "newSnapshotId": "ifl:snap:b4c9d8e2...",
  "version": "1.0.1",
  "timestamp": "2025-12-30T11:15:30Z",
  "parentSnapshotId": "ifl:snap:a3b8c9d2...",
  "appliedChangeSets": ["ifl:cs:c4d5e6f7..."],
  "tripleCount": 1526,
  "contentHash": "b4c9d8e2f1a3567890abcdef1234567890abcdef1234567890abcdef12345678",
  "metadata": {
    "description": "Applied debt item creation",
    "tags": ["development"]
  }
}

Request/Response

Graph Query

1
2
3
4
5
6
7
8
9
10
11
12
13
POST /graph/query
{
  "language": "cypher",
  "query": "MATCH (c:Customer)-[:PLACED]->(o:Order) RETURN c, o LIMIT 10"
}

// Response
{
  "results": [
    {"c": {...}, "o": {...}},
    ...
  ]
}

SHACL Validation

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
POST /graph/shacl/validate
{
  "targetGraph": "production",
  "context": "semantic-core",
  "shapesVersion": "v1",
  "validationScope": "delta",
  "dataGraph": "@prefix sea: <http://sea-forge.com/schema/core#> . ..."
}

// Response
{
  "conforms": false,
  "shapeVersion": "v1",
  "maintenanceMode": false,
  "enforcementApplied": true,
  "violations": [
    {
      "shape": "http://sea-forge.com/shapes/EmailFormatShape",
      "focusNode": "customer:123",
      "path": "sea:email",
      "message": "Invalid email format",
      "severity": "Violation",
      "requiresContext": []
    }
  ],
  "requiresContext": [],
  "reportGraph": "@prefix sh: <http://www.w3.org/ns/shacl#> . ..."
}

Error Codes

Code Description
400 Bad Request Invalid query/SHACL syntax
404 Not Found Snapshot or change set not found
409 Conflict Change set cannot be applied (conflict)
422 Unprocessable Entity Invalid change set operations
500 Internal Server Error Graph database error
503 Service Unavailable Snapshot creation in progress (retry later)

Installation Commands

1
2
3
4
5
# Python dependencies (add to pyproject.toml)
pip install pyoxigraph rdflib pyshacl owlrl

# Node.js dependencies (add to package.json)
pnpm add @comunica/query-sparql

Port/Adapter Architecture

1
2
3
4
5
6
7
8
9
interface KnowledgeGraphPort {
  querySparql(snapshotId: string, query: string): Promise<SparqlResult>;
  validateShacl(snapshotId: string, shapeUri: string, dataGraph: string): Promise<ValidationReport>;
  inferOwl(snapshotId: string, ontologyUri: string): Promise<InferenceResult>;
  createSnapshot(metadata: SnapshotMetadata): Promise<Snapshot>;
  applyChangeSet(changeSetId: string, targetMetadata: SnapshotMetadata): Promise<Snapshot>;
  getSnapshot(snapshotId: string): Promise<Snapshot>;
  listSnapshots(filters: SnapshotFilters): Promise<Snapshot[]>;
}

Error Handling


Performance Considerations


Security Considerations


Testing Strategy

References