Skip to content

AI second brain Phase 3: graph-aware retrieval live in the assistant (0211) - #232

Merged
crs48 merged 1 commit into
mainfrom
claude/ai-second-brain-phase3
Jun 22, 2026
Merged

AI second brain Phase 3: graph-aware retrieval live in the assistant (0211)#232
crs48 merged 1 commit into
mainfrom
claude/ai-second-brain-phase3

Conversation

@crs48

@crs48 crs48 commented Jun 22, 2026

Copy link
Copy Markdown
Owner

Phase 3 of exploration 0211 — makes the brain live in the app, building on the engine (#228) and the AiSurfaceService seam (#230).

What ships

The AI chat's context pack now comes from @xnetjs/brain's retrieve() instead of a flat keyword scan.

  • apps/web/.../ai-graph-retriever.ts (new, 6 tests) — createGraphContextRetriever builds an AiContextRetriever over the local NodeStore: keyword entry search → bounded expansion along typed relations (resolved from the schema registry) → token budget → each hit carries a readable provenance path.
  • AiChatPanel injects it: createAiSurfaceService({ store, schemas, retrieveContext: createGraphContextRetriever(store) }). So createContextPack({ query }) (already called per message) now walks the graph.
  • Exports: AiContextRetriever / AiRetrievedNode from the @xnetjs/plugins barrels; @xnetjs/brain added to apps/web deps.

Deliberately model-free

Entry search is keyword-only — no embedding-model download, no added bundle weight or startup cost (the 0204 cold-start constraint). The value-add here is the graph-walk + budget + provenance. The vector tier can swap in behind the same seam later without touching this call site.

Verification

  • @xnetjs/brain exercises (browser runtime, via Vite module graph): retrieve('inventory') returned the entry node plus its 1-hop neighbor with path "My inventory→ (items) Sword of testing".
  • App boots clean — no console errors with the new import in the bundle.
  • 6 retriever tests + 26 ai-surface tests green; tsc --noEmit clean for @xnetjs/plugins + xnet-web (40 turbo tasks); eslint + prettier clean.

Still deferred (future enhancements, doc stays [_])

Swap the vector tier in behind the seam (wake @xnetjs/vectors in-app + backfill/persist), an xnet_graph_expand MCP tool, the data-bridge query-path placement of the locality planner, WorkingSetPrewarm consumption, and a managed /ai/embed hub route.

🤖 Generated with Claude Code

…se 3)

The assistant's context pack now uses @xnetjs/brain's retrieve() via the
AiSurfaceService retrieveContext seam, instead of a flat keyword scan:

- New ai-graph-retriever.ts: createGraphContextRetriever builds an
  AiContextRetriever over the local NodeStore — keyword entry search + bounded
  expansion along typed relations (resolved from the schema registry) + token
  budget + readable provenance paths. Deliberately model-free (no embedding
  download) so cold-start is untouched per 0204; the vector tier can swap in
  behind the same seam later.
- AiChatPanel injects it into createAiSurfaceService({ retrieveContext }).
- Export AiContextRetriever/AiRetrievedNode from the @xnetjs/plugins barrels.
- 6 new tests; verified in-browser (entry + 1-hop neighbor with path label).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@crs48
crs48 temporarily deployed to pr-232 June 22, 2026 03:07 — with GitHub Actions Inactive
github-actions Bot added a commit that referenced this pull request Jun 22, 2026
@github-actions

Copy link
Copy Markdown
Contributor

🖼️ UI changes in this PR

No visual differences detected in the changed UI.

CI run

@github-actions

github-actions Bot commented Jun 22, 2026

Copy link
Copy Markdown
Contributor

Preview removed for PR #232.

github-actions Bot added a commit that referenced this pull request Jun 22, 2026
@crs48
crs48 merged commit c7bf3e7 into main Jun 22, 2026
14 checks passed
@crs48
crs48 deleted the claude/ai-second-brain-phase3 branch June 22, 2026 03:14
github-actions Bot added a commit that referenced this pull request Jun 22, 2026
crs48 added a commit that referenced this pull request Jun 22, 2026
Follow-up to exploration
[0211](docs/explorations/0211_[_]_AI_SECOND_BRAIN_GRAPHRAG_MEMORY_AND_TIERING.md)
(after [#228](#228) /
[#230](#230) /
[#232](#232)) — ships the
**just-in-time expansion** half of the anti-overwhelm design.

## What ships

A built-in `AiSurfaceService` tool, **`xnet_graph_expand`**, that walks
typed relation edges out from a node to its connected neighbors, bounded
by `hops` (1–2) and a result `limit`.

- Resolves relation fields from the schema registry; returns each
neighbor with the **relation + direction** it came through (traceable
answers).
- **Auto-surfaced to MCP/CLI agents** via `getTools()` — and correctly
**deferred** (JIT-discovered via the Tool Search Tool), not eagerly
loaded into context, which is exactly the anti-overwhelm posture: the
agent discovers and calls it only when it needs a node's connections.

This pairs directly with the graph-aware retrieval shipped in #232:
`createContextPack` hands the agent a budgeted slice plus expandable
node ids; `xnet_graph_expand` is how it pulls a specific node's
connections on demand instead of over-fetching the whole graph.

## Verification
- 4 new tests (30 in `ai-surface.test.ts`): outbound walk, not-found,
limit, tool-list presence.
- `tsc --noEmit` clean for `@xnetjs/plugins` (turbo); eslint + prettier
clean.
- `fallow audit` (with coverage): *"✓ No issues in 2 changed files"* —
the new functions add zero enforced complexity findings.
- No new dependencies (uses the service's existing `store` + `schemas`).

## Still deferred (0211 doc stays `[_]`)
The in-app **vector tier** (waking `@xnetjs/vectors` behind the
retriever seam — a perf-sensitive change better suited to its own PR,
possibly via a managed server-side embedding route given browser cost),
the `data-bridge` query-path placement of the locality planner,
`WorkingSetPrewarm` consumption, and a managed `/ai/embed` hub route.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
crs48 added a commit that referenced this pull request Jun 22, 2026
…ff) (#236)

The final piece of exploration
[0211](docs/explorations/0211_[x]_AI_SECOND_BRAIN_GRAPHRAG_MEMORY_AND_TIERING.md)
— wakes the dormant `@xnetjs/vectors` engine behind the retriever seam,
making the **semantic half** of the AI second brain live. After #228
(engine), #230 (seam), #232 (graph-aware retrieval live), and #233
(`xnet_graph_expand`), this flips 0211 to **`[x]`**.

## What ships

**`ai-vector-search.ts` — `createVectorEntrySearch`** (opt-in, lazy,
fallback-safe):
- The heavy `@xenova` model + `usearch` load via a **dynamic import only
on the first search after opt-in** — zero boot/bundle cost when off (the
[0204](docs/explorations/0204_[x]_FAST_LOCAL_FIRST_COLD_START_AND_CACHE_HYDRATION.md)
constraint).
- Once warm it **RRF-fuses vector + keyword**; until warm and on **any**
failure it falls back to keyword — so enabling it can only ever match or
improve results, **never break or slow** the chat.
- **`ai-vector-storage.ts`** — IndexedDB `BlobStore` so the index
**restores across sessions** instead of re-embedding the graph each
time.
- **`AiChatPanel`** — an opt-in toggle (off by default); the flag is
read via a ref so toggling **never tears down the AI runtime**
mid-conversation.

## Adversarial review caught 3 real bugs (fixed here)
A 22-agent review (18 findings → 15 false positives killed by
verification → 3 confirmed) surfaced:
1. `loadVectorTier` returned `false` on a *partial* restore but left the
index half-populated → now **clears on failure** so "false ⇒ cold" is
honest.
2. Persistence was never wired (no `storage`) → re-embedded every
session and the realm-fix was dead in prod → now **wired via
IndexedDB**.
3. Toggling the flag rebuilt `surface` → **reset the active thread**
mid-conversation → now decoupled via a ref.
Plus a realm-robustness fix in `brain/persist.ts`: a plain `instanceof
Uint8Array` mangles bytes across the dynamic-import / IndexedDB
structured-clone boundary; the `Symbol.toStringTag` brand holds.

## Verification
- **End-to-end in the real browser**: warm → hybrid hit (`source:
"hybrid"`); IndexedDB persist → restore (no re-embed) → query found the
node. The cross-realm persist fix exercised under real production
conditions.
- 17 unit tests (vector tier lazy/fallback/persist + the persist
clear-on-failure regression + graph retriever); `tsc` clean for
`@xnetjs/brain` + `xnet-web`; eslint + prettier clean; `fallow audit` —
*"✓ No issues in 10 changed files."*

## Justified defers (documented in the 0211 doc; none block the core)
- **Managed `/ai/embed` route** — the server-side embedding upgrade
path; dead infra until demand, so it follows demand.
- **data-bridge locality** — *blocked*: `apps/web` never instantiates a
`remoteNodeQueryClient`, so there's no remote read path to route to.
- **`WorkingSetPrewarm` planner scores** — marginal until per-node
frequency/pinned signals are tracked at boot (it already ranks by
recency).

🤖 Generated with [Claude Code](https://claude.com/claude-code)
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.

1 participant