Skip to content

feat: knowledge-graph service + observe identity provenance (and a buildable main) - #5

Merged
rrader26 merged 2 commits into
mainfrom
feat/graph-service-and-observe-identity
Aug 19, 2026
Merged

feat: knowledge-graph service + observe identity provenance (and a buildable main)#5
rrader26 merged 2 commits into
mainfrom
feat/graph-service-and-observe-identity

Conversation

@rrader26

Copy link
Copy Markdown
Contributor

.NET parity with @memmesh/sdk v0.9.0 — see
thinkfleet-memory-sdk#21.

⚠️ Read the first commit before the second. main does not currently
compile, and commit 1 is the fix. It is a large body of previously-uncommitted
work that I did not write; I kept it as its own commit so it can be reviewed
independently of the feature.

Commit 1 — main is broken; this restores it

A clean checkout of main fails with 25 CS0246 errors. MemMeshClient
and MemoryService reference RequestOptions, MemMeshException, SeekPage<>,
RequestInterceptor / ResponseInterceptor, and six service classes that exist
only as untracked files on a developer machine — 3,069 lines of staged parity
work that had never been committed.

That commit lands it as-is: Services.cs split into per-service files (Alerts,
Brains, Consent, Events, Financial, Lattice, Typed), plus Errors.cs,
Pagination.cs, RequestOptions.cs, an xunit suite with a stubbed
HttpMessageHandler, and the publish workflow. 103 tests pass.

I did not author this and have not reviewed it line by line — I verified only
that it builds and that its tests pass. If it was being held back
deliberately, drop commit 1 and this PR needs rebasing onto whatever does fix
the build.

Commit 2 — the actual feature

New: mm.Graph

Method Endpoint
StatsAsync() GET /admin/memory/graph/stats
ListEntitiesAsync(..) GET /admin/memory/entities
GetEntityAsync(id, ..) GET /admin/memory/entities/:id
ListEdgesAsync(..) GET /admin/memory/graph/edges
TraverseAsync(id, ..) POST /admin/memory/graph/traverse

Use StatsAsync(), not (await ListEntitiesAsync()).Count for any "how big
is it" question. The list routes page, so their length is the page size —
against a real project, 1,000 vs the actual 12,142.

Edges come back hydrated

ListEdgesAsync, TraverseAsync, and GetEntityAsync().Edges return the
server's GraphTraversalEdge: Subject and Object are full entities, not
ids
, plus a Hop counter. No subjectId / objectId on the wire. The raw
memory_edge row is deliberately not modelled — no read route returns it.

This bit for real in the Rust port (missing field 'subjectId') and was
silently wrong in the TypeScript PR before being fixed there.

Hop is 0 from ListEdgesAsync (no seed) and 1-indexed from TraverseAsync.

ObserveAsync forwards identity

userId / agentId / sessionId now reach the server. The route has always
accepted them; the SDK was dropping them. Added only when set, so existing
call sites send identical bodies (Observe_text_omits_identity_when_unset).

Provenance, not a tenancy boundary — search filters
chatIdentityId IS NULL OR = $1, permissive by design.

Query encoding

Filter values go through Uri.EscapeDataString. An unescaped & in a search
string would truncate the query server-side and quietly return the wrong page.

Verification

  • 113 tests pass (103 existing + 10 new).
  • Verified live against app.memmesh.ai: 12,142 entities / 287,698 edges,
    decoding NVIDIA CORP -[reported_metric]-> Cost of Revenue and the
    literal-object case NVIDIA CORP -[ticker_symbol]-> NVDA.

…ests

Lands the staged SDK parity work that has been sitting uncommitted.

This is not optional cleanup: `main` does not currently compile. MemMeshClient
and MemoryService reference RequestOptions, MemMeshException, SeekPage<>,
RequestInterceptor/ResponseInterceptor, and six service classes that exist only
in these files — 25 CS0246 errors on a clean checkout.

Splits the monolithic Services.cs into per-service files (Alerts, Brains,
Consent, Events, Financial, Lattice, Typed), adds Errors.cs, Pagination.cs, and
RequestOptions.cs, plus an xunit suite with a stubbed HttpMessageHandler.

103 tests pass.
Parity with @memmesh/sdk v0.9.0 (thinkfleet-memory-sdk#21).

New `mm.Graph` — StatsAsync, ListEntitiesAsync, GetEntityAsync,
ListEdgesAsync, TraverseAsync. There was no graph surface before, so the
structural half of memory was unreachable from .NET.

Edges deserialize as GraphTraversalEdge, the shape the read routes
actually return: Subject and Object are hydrated entities, not ids, plus a
Hop counter. The raw memory_edge row is not modelled — no read route
returns it. Modelling it that way failed outright in the Rust port.

Query values are percent-encoded via a small QueryString builder. An
unescaped `&` in a search filter would truncate it server-side and quietly
return the wrong page.

ObserveAsync gains userId / agentId / sessionId. The server route has
always accepted them; the SDK was dropping them, so provenance never
arrived. Added only when set, so existing call sites send identical bodies.
They are provenance, NOT a tenancy boundary.

Verified live against app.memmesh.ai — 12142 entities / 287698 edges,
decoding NVIDIA CORP -[reported_metric]-> Cost of Revenue. 113 tests pass.
@rrader26
rrader26 merged commit d61f8b7 into main Aug 19, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants