Traverse a typed memory graph

Status: unreleased 0.13.0 contract. Read a bounded neighborhood of typed memory assertions, with current eligible memories, exact endpoint revisions, original provenance and explicit coverage. The server evaluates the graph in one storage snapshot and at one clock value.

Use this contract to inspect connections or assemble graph context from selected roots. It does not choose search anchors, assign relevance scores, infer entities or generate relationships. Existing cosine, BM25 and hybrid search are unchanged. Declared derived_from dependencies remain available through the separate evidence ancestry API. A complete typed neighborhood does not imply that all derivation ancestry is displayed.

Choose the authority and route#

Route Required authority Selection
POST /api/v1/memory/graph/typed memory_read An authorized resource scope and root memory IDs
POST /api/v1/company/memory/graph/typed Company credential_admin A workspace from the native company directory and root memory IDs
POST /api/v1/company/memory/relations/inspect Company credential_admin Retained current assertion and its eligibility explanation
POST /api/v1/company/memory/relations/revision Company credential_admin Immutable assertion version and its receipt

The scoped route derives the company and private subject from the credential. Project, agent, mission and visibility are checked before any adjacency selection or retrieval admission. Every node, relation, request-local cache and counter belongs to that one authorized namespace. Incoming traversal never grants access to another scope. A relation UUID or workspace ID conveys no authority.

Native company administration includes all private subjects in the company, including memories whose original writer's credential has been revoked. It uses the administrative session or API key directly and issues no delegated agent key. An ordinary integration credential cannot call the company routes. Company administration does not grant access to another company.

Read a neighborhood#

POST /api/v1/memory/graph/typed
Authorization: Bearer <scoped-api-key>
Content-Type: application/json
{
  "contract_version": 1,
  "scope": {
    "project_id": "project",
    "mission_id": null,
    "agent_id": "agent",
    "visibility": "private"
  },
  "query": {
    "root_record_ids": ["018f0000-0000-4000-8000-000000000001"],
    "direction": "both",
    "relation_kinds": ["supports", "contradicts", "semantic_related"],
    "max_depth": 2,
    "node_limit": 128,
    "edge_limit": 256,
    "scan_limit": 1024
  }
}

Replace the fixture root with a real current memory ID. A root ID does not pin an earlier search revision: the request reads its current canonical record. Compare returned revisions with the context your application intended to use.

Query field Default and permitted values
root_record_ids Required: 1–16 distinct UUIDs, in caller-selected order
direction both; also accepts outgoing or incoming
relation_kinds All six kinds; a nonempty list of distinct accepted kinds
max_depth 2; range 0–8, with roots at depth zero
node_limit 128; range 1–256 completed canonical record lookups
edge_limit 256; range 1–1024 eligible assertions returned
scan_limit 1024; range 1–4096 physical adjacency entries examined

All kinds are semantic_related, same_entity, temporal_before, causal_claim, supports and contradicts. The kind-list order does not affect traversal. Unknown fields, types, duplicate roots, duplicate kinds and out-of-range limits return 400. There are no caller-supplied weights or ranking parameters.

Outgoing traversal follows an assertion's source to its target. Incoming traversal follows the reverse direction for discovery, while preserving the original assertion's source and target in the response. both visits outgoing entries before incoming entries for each node. It does not create reverse assertions or turn a causal claim into an observed fact.

Interpret nodes, edges and roots#

The scoped response contains contract_version, scope and graph. The company graph response contains contract_version, company_id, the resolved workspace and the same graph contract.

Graph field Meaning
traversal_version Always typed_relations_v1, the server-owned traversal contract
evaluated_at_millis One server evaluation clock for the complete response
direction, relation_kinds Effective selection parameters
max_depth, node_limit, edge_limit, scan_limit Effective work and output limits
roots One entry per input root, preserving request order
nodes Eligible canonical memory records with their minimum discovery depth
edges Current MemoryRelation values, retaining relation revision, original direction, declared provenance, reporter, validity and review
coverage Completeness, cuts and work counters

A root is included, unavailable or not_examined. Unavailable means its lookup completed but no eligible record can be served. The reason is not disclosed. Not examined means the canonical lookup budget prevented evaluation; it must not be treated as absence. Roots are read in request order before any neighbors, so an early expansion cannot consume the budget intended for a later root lookup. If node_limit is smaller than the root count, later roots remain unexamined.

Each returned assertion has both endpoints in nodes, at the exact revisions in edge.input.source and edge.input.target. Rejected or retired assertions, invalid assertion intervals, changed endpoint revisions, deleted/expired/rejected memories and invalid transitive evidence are excluded. Complete memory eligibility checks apply even when the memory's evidence sources lie beyond the display depth.

An unreviewed assertion can be eligible. The original origin and model identity remain caller declarations, while reported_by is authenticated authorship. Approval remains a review decision. Parallel assertions with different relation IDs are preserved, including repeated claims; their number is not independent corroboration or a confidence estimate.

Nodes are discovered breadth first. Within each node and index direction, assertions follow ascending relation UUID order. A memory appears once; an assertion appears at most once even when reached through both indexes or several roots. Valid edges between already included nodes can be returned at depth zero. Edges follow discovery order; a boundary-deferred edge whose endpoints later appear is appended during final reconciliation in its original encounter order. Preserve root order, relation IDs and revisions when reproducing a query.

Distinguish a complete result from a work cut#

coverage.complete is true only when every requested root was examined and the selected directed typed neighborhood was fully examined without a work cut. It does not certify the entire corpus, undiscovered semantic knowledge, evidence independence or retrieval relevance. All unavailable roots can produce a complete empty result. A kind filter can also produce a complete root-only graph.

Incomplete responses have one or more stop_reasons:

  • depth_limit: a potentially relevant connection would expand beyond the requested depth. Its other endpoint may remain unexamined; the cut does not certify that the omitted assertion is eligible.
  • node_limit: another canonical record could not be examined. This includes roots and candidate neighbors, not just nodes that would be returned.
  • edge_limit: another eligible connection could not be included in the output.
  • scan_limit: a further scoped adjacency entry exists beyond the work budget.

Filtered, duplicate, expired and stale assertions consume adjacency work, not the valid edge limit. For example, four deleted neighbors followed by one valid neighbor can return the valid edge with edge_limit: 1 when the scan and node budgets cover all five candidates. A smaller work budget can stop before that edge, but the response then explicitly reports incomplete coverage. Retired and rejected assertions have their adjacency entries removed atomically.

There is no continuation cursor or retained cross-request snapshot. A retry with larger limits is a new observation and can see mutations or expiration. Do not merge independent responses into an allegedly atomic graph. If an application requires complete context, reject an incomplete response or apply an explicitly documented fallback; do not silently present it as a complete empty graph.

Work, bytes and integrity#

Coverage field What is counted
records_examined Completed distinct canonical lookups, including missing and ineligible neighbors; at most node_limit
record_bytes Canonical memory bytes fetched through those lookups; at most 8 MiB
adjacency_entries_examined Physical scoped index entries examined, including duplicates and filtered/stale assertions; at most scan_limit
relations_examined Distinct canonical assertions loaded; at most the adjacency count
relation_bytes Serialized canonical assertion bytes; at most 4 MiB
dependency_work Shared transitive validation cache: at most 4096 distinct lookups and 16 MiB

Node, edge, depth and scan cuts return 200 with incomplete coverage. Byte-budget or dependency-budget exhaustion fails the whole request with 400; no partial graph is returned. Each memory retains the existing transitive depth-eight and 64-source-node eligibility limits. Typed discovery depth does not weaken those checks. All four routes share the configured retrieval admission slots through response serialization.

Counters are logical storage-work bounds, not an RSS or total network-byte limit. They exclude integrity headers, relation history, index bytes, JSON overhead and temporary allocations. RocksDB can fetch a crossing value before its size is known. A scoped key lookahead identifies a scan cut without decoding an additional assertion. Lower and upper iterator bounds prevent neighboring scopes from contributing index candidates or counters.

Every endpoint revision has outgoing and incoming completeness headers. Their entry counts and digest accumulators are updated in the same synchronous WAL batch as the assertion, both indexes, history and receipt. New memory revisions receive empty headers in their own atomic memory mutation batch. A fully scanned adjacency prefix must match its header; missing entries, including removal of both directions, cannot silently become a complete empty graph. Each encountered assertion also verifies its canonical value, both index values and current immutable history. A corrupt encountered header or value returns 500 storage_inconsistency with no partial result.

A cut prefix cannot be fully verified against its aggregate header and remains incomplete. These checks detect inconsistent storage, not coordinated malicious rewriting of every checksum or a full-volume corruption audit. Disconnected and unexamined prefixes are outside the requested observation.

Upgrade and recovery#

Before serving requests, startup initializes missing or stale headers from canonical memory revisions and the current assertion ledger. It validates the existing record/index and assertion/history pairs. Source data is not rewritten, and memory feed positions and command receipts remain unchanged. Rebuild batches contain at most 256 header rows. A final namespace marker is committed only after the build succeeds. If the process stops during initialization, reopening clears that namespace's partial header build and repeats it.

The marker fingerprints the memory journal, so a namespace changed by a legacy memory writer is rebuilt on the next open. Matching namespaces are skipped. Initial rebuild work is proportional to retained current records and assertions; it is not a constant-time migration. Rehearse an upgrade on a restored copy and retain an appropriate backup before deploying against a large corpus.

Do not write to an upgraded volume using an intermediate experimental typed- relation binary that predates these headers. Such a binary can change assertions without advancing the memory journal; encountered inconsistencies then fail closed instead of being silently repaired. Individual reads do not rebuild corrupt indexes. Recovery must preserve subsequent acknowledged writes and immutable audit history rather than overwriting a live database with an old snapshot.

Use company administration#

Obtain workspace_id from the company workspace directory, then send the same graph query with the administrative envelope:

{
  "contract_version": 1,
  "workspace_id": "0000000000000000000000000000000000000000000000000000000000000000",
  "query": {
    "root_record_ids": ["018f0000-0000-4000-8000-000000000001"],
    "direction": "both"
  }
}

Replace the placeholder workspace ID with the authorized directory result. Company graph reads still return only eligible memories and assertions. To inspect retained metadata, send this body to the company /relations/inspect route:

{
  "contract_version": 1,
  "workspace_id": "0000000000000000000000000000000000000000000000000000000000000000",
  "relation_id": "018f0000-0000-4000-8000-000000000003"
}

The response contains company_id, workspace and inspection. Add a positive revision to the same body and use /relations/revision for immutable history. Retained inspection can disclose a rejection or stale endpoint to the authorized administrator; historical reads preserve the original relation and receipt even after retirement. Neither route returns endpoint payloads or changes credentials.

Freshness and error handling#

All responses are Cache-Control: no-store. Missing/expired/revoked credentials return 401, missing authority returns 403, malformed fields or bounds return 400, and bodies over 65,536 bytes return 413. Native administrative reads return 404 record_not_found for unavailable workspaces, assertions or historical versions. Scoped graph roots use root status within a 200 response instead of 404. Occupied retrieval slots return 503 retrieval_busy; use bounded backoff.

The graph is a point-in-time observation, not a lease. Revalidate before reuse. The separate relation change feed now delivers assertion lifecycle changes. Observe both memory and relation changes when invalidating graph caches; a memory checkpoint alone does not certify a cached graph as current. Expiration can still occur without an event. Feed cursors do not paginate graph results or establish one atomic snapshot across both streams. Automatic consolidation and graph-assisted ranking remain separate work.

Qualification uses controlled storage and real HTTP fixtures for direction, cycles, repeated claims, exact revisions, scope/role isolation, native company inspection, cuts, independent work counters, expiry, transitive invalidation, corruption, bounded migration, incomplete-build recovery and abrupt process termination. It does not establish production capacity or quality gains. See the research and evaluation map for the separate retrieval and agent-task comparisons required before promoting a graph policy.