Expose knowledge graph data in the Memory view through a localized, resilient dashboard panel. - Add protected knowledge graph API routes and access helpers. - Add a Memory view tab, graph panel, loading/error states, and localized labels. - Document graph navigation and cover access, route, hook, and component behavior. Files changed: .changeset/fn-9038-knowledge-graph-tab.md | 7 + docs/dashboard-guide.md | 2 + docs/knowledge-graph.md | 10 ++ packages/dashboard/app/api/legacy.ts | 17 ++- packages/dashboard/app/api/system/index.ts | 1 + .../dashboard/app/api/system/knowledge-graph.ts | 23 +++ .../app/components/KnowledgeGraphPanel.css | 35 +++++ .../app/components/KnowledgeGraphPanel.tsx | 68 +++++++++ packages/dashboard/app/components/MemoryView.tsx | 15 +- .../__tests__/KnowledgeGraphPanel.test.tsx | 49 +++++++ .../app/components/__tests__/MemoryView.test.tsx | 13 ++ .../app/hooks/__tests__/useKnowledgeGraph.test.ts | 66 +++++++++ packages/dashboard/app/hooks/useKnowledgeGraph.ts | 94 ++++++++++++ .../src/__tests__/knowledge-graph-access.test.ts | 88 ++++++++++++ packages/dashboard/src/knowledge-graph-access.ts | 160 +++++++++++++++++++++ .../register-knowledge-graph-routes.test.ts | 75 ++++++++++ .../src/routes/register-knowledge-routes.ts | 120 ++++++++++++++++ packages/i18n/locales/en/app.json | 15 +- packages/i18n/locales/es/app.json | 15 +- packages/i18n/locales/fr/app.json | 15 +- packages/i18n/locales/ko/app.json | 15 +- packages/i18n/locales/pt-BR/app.json | 15 +- packages/i18n/locales/zh-CN/app.json | 15 +- packages/i18n/locales/zh-TW/app.json | 15 +- 24 files changed, 939 insertions(+), 9 deletions(-) Fusion-Task-Id: FN-9038 Fusion-Task-Lineage: 4200e214-ad22-4f92-b980-c9bcb8ffd3b9 Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
56 lines
8.1 KiB
Markdown
56 lines
8.1 KiB
Markdown
[← Docs index](./README.md)
|
|
|
|
# Knowledge graph
|
|
|
|
`fn knowledge-graph build` creates the deterministic structure graph for the FN-8920 memory epic. Memory Keeper may subsequently add clearly provenance-tagged inferred semantic relationships; deterministic extraction itself remains LLM-free.
|
|
|
|
## Artifact and configuration
|
|
|
|
The default `knowledgeGraphDir` is `.fusion-knowledge/graph`, deliberately outside ignored `.fusion/`. It contains `nodes.json`, `edges.json`, and `manifest.json`; all use sorted, LF JSON. Nodes and edges are written before the manifest so a torn write safely triggers a full rebuild. The manifest records SHA-256 fingerprints and import references, enabling changed files only to be re-extracted and deleted file ownership to be pruned.
|
|
|
|
The artifact is not committed. As of 2026-08-10 `.fusion-knowledge/` is gitignored: the graph is deterministic and fully regenerable from source (the manifest's SHA-256 fingerprints drive incremental rebuilds), so tracking it preserves nothing a rebuild cannot reproduce, while costing ~85MB of irreversible, compounding history bloat on every clone and CI checkout — `edges.json` alone is 43MB, past GitHub's 50MB warning threshold. Regenerate it with `fn knowledge-graph build` rather than pulling it.
|
|
|
|
An operator who explicitly wants a snapshot in history can still force one with `git add -f .fusion-knowledge/graph`; prefer a rebuild.
|
|
|
|
## Model
|
|
|
|
Nodes are `file`, `module`, `symbol`, `doc-concept`, or `rationale`. Structural edges are `contains`, `imports`, and `re-exports`; semantic edges are `relates-to` and `rationale-supports`. Every edge includes source, owner (`file` or `derived`), and provenance (`extracted` or `inferred`). IDs are path-derived (`file:path`, `module:dir`, `symbol:path#name`, `doc:path#slug~index`, and `rationale:path#area@stamp~index`) with reserved separators percent-escaped.
|
|
|
|
TypeScript/TSX parsing is parser-only. Exported declarations become symbols; duplicate exports collapse to one earliest-position node with `declarationCount`, including invalid source. Syntax errors remain best-effort. `export *` records a re-export relationship but cannot expand names without a checker. Relative imports are resolved lexically using `.ts`, `.tsx`, and index candidates; package and tsconfig aliases are out of scope.
|
|
|
|
Discovery includes package `src`, package scripts, `scripts`, plugins, docs, root policy documents, and `packages/dashboard/app`, whose UI is outside `src`. Modules are derived from direct TypeScript files every build. `queryNodes`, `neighbors`, and `shortestPath` expose the in-process API and retain complete edge provenance.
|
|
|
|
FNXC rationale comes from TypeScript parser comment ranges and markdown HTML comments. Markdown fenced/indented code cannot open a rationale comment; once opened, a comment continues through indentation. Multiple stamped headers in one comment produce separate rationale nodes.
|
|
|
|
## Extraction and recovery contract
|
|
|
|
The dispatcher creates exactly one file node per discovered file. TypeScript and TSX are parsed with the TypeScript parser only; malformed source remains best-effort and never blocks a build. Symbol identity uses the exported name. Legal declaration merges and invalid redeclarations use the same deterministic collapse: the first source location and attributes win, while `declarationCount` and (when needed) `symbolKinds` preserve the fact of the collision. The only graph errors are invalid caller paths, an impossible internal cross-kind/owner collision, and artifact I/O failures.
|
|
|
|
File-owned facts are replaced only when that file hash changes. Module nodes and module containment are derived from the final file set every build. Import references are persisted in the manifest and import/re-export edges are synthesized every build, so adding or deleting a target never requires parsing an unchanged importer. Relative resolution tries `.ts`, `.tsx`, then index candidates; it intentionally does not resolve aliases, packages, or expand `export *` names.
|
|
|
|
Every real source position is recorded as a repository-relative path, line, and column. File and module-derived items use a `syntheticSource: "true"` attribute and a 1:1 anchor. Artifact payloads contain only content-derived values (including source FNXC stamps and hashes), never build time, host, or process metadata. Before reuse, persisted nodes, edges, source locations, manifest entries, and import references must be complete, exactly-shaped records; an unknown key is rejected rather than retained. Import references must also be usable extractor output: only TypeScript/TSX owners may carry them, candidates are non-empty and unique, and references cannot duplicate an extractor identity. Shape violations report `invalid-artifact`; cross-record contradictions report `inconsistent-artifact`. Either recovery reason discards the entire artifact set and triggers a complete rebuild, never partial trust. Artifacts are written nodes, edges, then manifest so a torn write cannot validate incomplete output.
|
|
|
|
## FNXC and query behavior
|
|
|
|
FNXC rationale is first-class data. TypeScript-family comment ranges come from the parsed tree, not a raw scanner, which prevents strings, regexes, template text, and JSX text from becoming rationale. Markdown recognizes HTML comments outside fenced or narrowly defined indented code; code state gates the comment opener only, so an already-open multi-header comment is not truncated by indentation. Each stamped header starts a separate rationale node and runs to the next header in its comment.
|
|
|
|
`queryNodes(filter)`, `neighbors(id, options)`, and `shortestPath(from, to)` are deterministic in-process APIs. Neighbor and path results retain complete edge objects, including source, ownership, and provenance. The sole inferred-edge writer stamps `inferred` unconditionally and accepts an input type with no provenance field, so model output can never masquerade as deterministic extraction.
|
|
|
|
## Automatic recall capture
|
|
|
|
Memory capture is optional and detached. `RecallCaptureWriter.capture()` returns `void`, so task completion, research finalization/promotion, and insight upsert cannot await recall persistence. Completed tasks and research findings capture `solution` records with task-completion/deep-research provenance; insights capture `decision` records with the available `other` provenance. Live writers are composed at the in-process reflection runtime, research orchestrator construction, research-promotion callers, and the lazy async insight-store factory; absent memory falls back to a shared no-op writer.
|
|
|
|
## Non-goals
|
|
|
|
This layer has no LLM calls, embeddings, vector recall, MCP tools, source-validity diagnostics, language support beyond TypeScript/TSX symbols, CommonMark parser, cross-rename identity, or capability-fabric bundle. The FR-29/FR-34 bundle format is deferred.
|
|
|
|
## Dashboard navigation
|
|
|
|
The dashboard's **Memory → Knowledge Graph** tab provides list-and-detail navigation without transferring the full artifact or rendering a whole-graph canvas. It shows graph status, counts, directory and provenance data; operators can rebuild the artifact explicitly (including force rebuild). Reads use a small manifest-validated artifact cache and never rebuild the graph.
|
|
|
|
The tab queries capped node pages (200 nodes), node edges (200 per direction), and neighbors with direction, edge-kind, and depth controls. Node IDs are query parameters, so IDs containing `/`, `#`, `@`, or `~` remain safe to navigate. It also supports node filters for kind, path/id prefix, regular-expression name, FNXC area, symbol kind, and owner.
|
|
|
|
The HTTP surface is `GET /api/knowledge/graph/status`, `/nodes`, `/node`, `/neighbors`, and `/path`, plus `POST /api/knowledge/graph/build`. Missing artifacts are a recoverable status state; query endpoints report them as 404 rather than silently returning empty results.
|
|
|
|
Dashboard shortest paths use a bounded undirected BFS, not the core unbounded helper. The default is six hops, the maximum is ten hops, and at most 20,000 nodes are expanded. Results are `found`, exhaustive `not-found`, or `limit-reached`; the latter has `truncated: true` and is intentionally distinct from no path. Unknown node IDs return 404. Whole-graph force-directed/canvas rendering remains an explicit non-goal.
|