Task 14 Execution Packet: Knowledge Graph Reasoning Integration (Aligned to 2026-01-25 Plan)

This packet replaces the old Task 14 notes. It is aligned to Phase 14 / Task 14 in docs/plans/2026-01-25-end-state.md and to the current knowledge-graph service code in this repo.

Use this as the exact execution guide for an agent implementing Task 14.


Objective

Enable bounded, deterministic reasoning for the Knowledge Graph service with explicit profile selection and separable inferred data.

Key outcomes:


Non-negotiables


Step 0 — Ground the current code reality (must confirm before editing)

Current state in this repo (verify quickly before coding):

If any of the above differs, adjust this packet to match the repo before coding.


Step 1 — Introduce a reasoning module with explicit profiles

Create:

Required profile list (exact)

Reasoner contract (stable API)

Define a small, stable interface:

1
2
3
4
5
6
7
8
9
10
11
ReasoningProfile = Literal["none", "rdfs", "owlrl-lite", "custom_ruleset_v1"]

@dataclass(frozen=True)
class ReasoningOutput:
    profile: ReasoningProfile
    snapshot_id: str
    explicit_triples: int
    inferred_triples: int
    rule_set_hash: str
    duration_ms: float
    inferred_graph: Graph  # inferred triples only

Profile implementations

Ruleset hashing

Compute rule_set_hash as sha256 of the ruleset file contents (or a fixed string for rdfs/owlrl-lite).

Example:


Step 2 — Switch adapter storage to support named graphs

Why: Named graphs are required to keep inferred data separable.

Required structural change

In OxigraphAdapter:

Snapshot ID derivation

Use the existing canonical hash logic to derive a snapshot ID without mutating state:

Snapshot model decision (resolved): metadata-only content IDs. The system does not store historical graph versions; snapshot_id is a deterministic label for the current explicit graph only. Do not rely on _current_snapshot_id for correctness; compute from graph content.


Step 3 — Annotate each inferred triple

Each inferred triple must be annotated with:

Required mechanism

Use RDF reification (standard, deterministic):

For each inferred triple (s, p, o) in the inferred named graph:

Use SEA = Namespace("http://sea-forge.com/schema/core#") (already present in adapter).


Step 4 — Wire reasoning into query paths (explicit per query)

API surface

Update SparqlRequest in services/knowledge-graph/src/api/routes.py:

Adapter signatures

Update adapter methods to accept these:

Query execution rules

Snapshot handling: if snapshot_id is provided, it must equal the current computed snapshot ID. If it does not match, return 400 (or ignore and log a warning, but prefer 400 to avoid ambiguity).

Response metadata (required)

Include inferred in response when profile != none:

1
2
3
4
5
6
7
8
9
10
"inferred": {
  "enabled": true,
  "profile": "rdfs",
  "snapshot_id": "ifl:snap:...",
  "graph_uri": "urn:sea:inferred:<snapshot_id>",
  "explicit_triples": 123,
  "inferred_triples": 45,
  "rule_set_hash": "<sha256>",
  "duration_ms": 12.3
}

If profile is none, omit inferred or set enabled=false.

Format-specific metadata handling (required)

Optional (opt-in): allow embedding metadata as triples in a named metadata graph when include_metadata=true; do not reject reasoning requests for non-JSON formats.


Step 5 — Precomputed artifacts must not pollute explicit truth

Update load_precomputed_artifacts to keep explicit/inferred separate:


Step 6 — Dependencies

Update services/knowledge-graph/pyproject.toml:


Step 7 — Tests (must prove real inference + separation)

Create services/knowledge-graph/tests/test_reasoning.py with at least these tests:

Test 1: RDFS subclass inference works

Test 2: Inferred named graph + annotations exist

Test 3: Custom ruleset v1 works

Test 4: Determinism


Step 8 — API docs update (avoid doc drift)

Update:

Add:


Step 9 — Verification

Run:

1
pytest services/knowledge-graph/tests/test_reasoning.py -v

Optional:

1
pytest services/knowledge-graph/tests -v

Only mark Task 14 complete after the test passes then open a pull request.


Completion Status

Status: ✅ COMPLETED (2026-01-27)

All 8 tests pass:

Implementation summary:

  1. Created src/reasoner.py with Reasoner class and 4 profiles
  2. Created src/rules/custom_ruleset_v1.rq with SPARQL CONSTRUCT rules
  3. Switched adapter to use rdflib.Dataset for named graph support
  4. Implemented per-triple reification annotations
  5. Wired reasoning into query paths with reasoning_profile and snapshot_id parameters
  6. Updated API routes to support reasoning parameters
  7. Updated load_precomputed_artifacts for explicit/inferred separation
  8. Added owlrl>=6.0.2 to dependencies
  9. Updated README and HOWTO documentation