@powerhousedao/knowledge-note 1.0.3 → 1.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +359 -122
- package/dist/editors/knowledge-vault/components/DriveExplorer.d.ts.map +1 -1
- package/dist/editors/knowledge-vault/components/DriveExplorer.js +8 -2
- package/dist/editors/knowledge-vault/components/SearchView.d.ts +2 -0
- package/dist/editors/knowledge-vault/components/SearchView.d.ts.map +1 -0
- package/dist/editors/knowledge-vault/components/SearchView.js +96 -0
- package/dist/editors/knowledge-vault/hooks/use-graph-search.d.ts +25 -0
- package/dist/editors/knowledge-vault/hooks/use-graph-search.d.ts.map +1 -0
- package/dist/editors/knowledge-vault/hooks/use-graph-search.js +198 -0
- package/dist/package.json +2 -1
- package/dist/processors/graph-indexer/embedder.d.ts +5 -0
- package/dist/processors/graph-indexer/embedder.d.ts.map +1 -0
- package/dist/processors/graph-indexer/embedder.js +27 -0
- package/dist/processors/graph-indexer/embedding-store.d.ts +11 -0
- package/dist/processors/graph-indexer/embedding-store.d.ts.map +1 -0
- package/dist/processors/graph-indexer/embedding-store.js +70 -0
- package/dist/processors/graph-indexer/index.d.ts.map +1 -1
- package/dist/processors/graph-indexer/index.js +48 -0
- package/dist/processors/graph-indexer/migrations.d.ts.map +1 -1
- package/dist/processors/graph-indexer/migrations.js +35 -0
- package/dist/processors/graph-indexer/query.d.ts +21 -0
- package/dist/processors/graph-indexer/query.d.ts.map +1 -1
- package/dist/processors/graph-indexer/query.js +154 -13
- package/dist/processors/graph-indexer/schema.d.ts +11 -0
- package/dist/processors/graph-indexer/schema.d.ts.map +1 -1
- package/dist/style.css +63 -0
- package/dist/subgraphs/knowledge-graph/subgraph.d.ts +321 -12
- package/dist/subgraphs/knowledge-graph/subgraph.d.ts.map +1 -1
- package/dist/subgraphs/knowledge-graph/subgraph.js +237 -15
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -1,179 +1,416 @@
|
|
|
1
|
-
#
|
|
1
|
+
# bai-knowledge-note
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
It ensures compatibility with host applications like Connect and the Reactors for seamless document model and editor integration.
|
|
3
|
+
A Powerhouse Vetra package for team-wide institutional memory — atomic knowledge notes with typed content, structured links, lifecycle states, and provenance tracking.
|
|
5
4
|
|
|
6
|
-
##
|
|
7
|
-
This tutorial will guide you through the process of creating a new document model using the Document Model Editor in the Connect app.
|
|
5
|
+
## Architecture
|
|
8
6
|
|
|
9
|
-
|
|
10
|
-
|
|
7
|
+
```
|
|
8
|
+
bai-knowledge-note/
|
|
9
|
+
├── document-models/ 11 document models (data schemas + reducers)
|
|
10
|
+
├── editors/ 10 editors + 1 drive-app (UI layer)
|
|
11
|
+
├── processors/ Graph indexer (data pipeline)
|
|
12
|
+
├── subgraphs/ Knowledge graph (GraphQL query API)
|
|
13
|
+
├── powerhouse.manifest.json
|
|
14
|
+
└── powerhouse.config.json
|
|
15
|
+
```
|
|
11
16
|
|
|
12
|
-
|
|
13
|
-
- `lint`: Checks for errors with ESLint and TypeScript checking.
|
|
14
|
-
- `format`: Formats the code using Prettier.
|
|
15
|
-
- `build`: Builds the library project using Vite.
|
|
16
|
-
- `storybook`: Starts Storybook in development mode.
|
|
17
|
-
- `build-storybook`: Builds Storybook.
|
|
18
|
-
- `test`: Runs Jest for testing.
|
|
17
|
+
## Document Models
|
|
19
18
|
|
|
20
|
-
|
|
19
|
+
11 document types defining the knowledge vault's data layer:
|
|
21
20
|
|
|
22
|
-
|
|
23
|
-
|
|
21
|
+
| Model | Type | Role |
|
|
22
|
+
|-------|------|------|
|
|
23
|
+
| **KnowledgeNote** | `bai/knowledge-note` | Atomic knowledge claims with title, content, typed links, topics, provenance |
|
|
24
|
+
| **Moc** | `bai/moc` | Maps of Content — topic navigation hubs organizing notes into clusters |
|
|
25
|
+
| **Source** | `bai/source` | Raw ingested material (articles, transcripts, documentation) |
|
|
26
|
+
| **ResearchClaim** | `bai/research-claim` | Ars Contexta methodology foundation (249 claims) |
|
|
27
|
+
| **KnowledgeGraph** | `bai/knowledge-graph` | Materialized graph singleton |
|
|
28
|
+
| **PipelineQueue** | `bai/pipeline-queue` | Processing task tracker singleton |
|
|
29
|
+
| **HealthReport** | `bai/health-report` | Point-in-time vault diagnostics |
|
|
30
|
+
| **VaultConfig** | `bai/vault-config` | Vault configuration singleton |
|
|
31
|
+
| **Observation** | `bai/observation` | Operational learning signals |
|
|
32
|
+
| **Tension** | `bai/tension` | Unresolved contradictions between claims |
|
|
33
|
+
| **Derivation** | `bai/derivation` | Configuration audit trail |
|
|
24
34
|
|
|
25
|
-
|
|
35
|
+
Each model lives in `document-models/<name>/v1/` with `gen/` (auto-generated types, action creators) and `src/` (hand-written reducers).
|
|
26
36
|
|
|
27
|
-
|
|
28
|
-
npm create document-model-lib
|
|
29
|
-
```
|
|
37
|
+
## Editors
|
|
30
38
|
|
|
31
|
-
|
|
39
|
+
React components for viewing and editing each document type. All editors use the `useSelectedXDocument()` hook pattern from `@powerhousedao/reactor-browser` which returns `[document, dispatch]`.
|
|
32
40
|
|
|
33
|
-
|
|
41
|
+
### Drive App: Knowledge Vault
|
|
34
42
|
|
|
35
|
-
|
|
36
|
-
npm install ph-cmd
|
|
37
|
-
```
|
|
38
|
-
Now you are able to launch Connect in Studio Mode (Locally):
|
|
43
|
+
The main entry point (`editors/knowledge-vault/`). Registered as a drive-app in the manifest — this is what users see when they open the drive in Connect. Features:
|
|
39
44
|
|
|
40
|
-
|
|
41
|
-
|
|
45
|
+
- **Notes tab** — Paginated grid of NoteCards with status badges, topics, and link counts
|
|
46
|
+
- **Graph tab** — Cytoscape.js visualization with fcose layout, semantic clustering around MOC hubs, position persistence across sessions, and cross-cluster edge separation
|
|
47
|
+
- **Sources tab** — Ingested source material with status tracking
|
|
48
|
+
- **Pipeline tab** — Processing queue with phase tracking
|
|
49
|
+
- **Health tab** — Vault diagnostics dashboard
|
|
50
|
+
- **Config tab** — Vault configuration
|
|
51
|
+
|
|
52
|
+
### Document Editors
|
|
53
|
+
|
|
54
|
+
| Editor | Document Type | Purpose |
|
|
55
|
+
|--------|--------------|---------|
|
|
56
|
+
| `knowledge-note-editor` | `bai/knowledge-note` | Textarea editor with markdown preview, links section, topic management |
|
|
57
|
+
| `moc-editor` | `bai/moc` | Core ideas, tensions, open questions, child MOC references |
|
|
58
|
+
| `source-editor` | `bai/source` | Source ingestion with content preview and extraction stats |
|
|
59
|
+
| `research-claim-editor` | `bai/research-claim` | Methodology claim viewer |
|
|
60
|
+
| `health-report-editor` | `bai/health-report` | Health check results |
|
|
61
|
+
| `pipeline-queue-editor` | `bai/pipeline-queue` | Task queue management |
|
|
62
|
+
| `observation-editor` | `bai/observation` | Operational signals |
|
|
63
|
+
| `tension-editor` | `bai/tension` | Contradiction tracking |
|
|
64
|
+
| `vault-config-editor` | `bai/vault-config` | Configuration management |
|
|
65
|
+
| `knowledge-graph-editor` | `bai/knowledge-graph` | Graph document viewer |
|
|
66
|
+
|
|
67
|
+
## Processor: Graph Indexer
|
|
68
|
+
|
|
69
|
+
The data pipeline that turns document operations into a queryable relational index (`processors/graph-indexer/`).
|
|
70
|
+
|
|
71
|
+
### How it works
|
|
72
|
+
|
|
73
|
+
**Registration:** `processors/factory.ts` → `graph-indexer/factory.ts` — called once per drive on startup. Creates a namespaced PGlite store (`GraphIndexerProcessor_<driveId>`) and registers a filter for `bai/knowledge-note` + `powerhouse/document-drive` operations.
|
|
74
|
+
|
|
75
|
+
**Processing:** `onOperations()` is called whenever matching operations occur:
|
|
76
|
+
|
|
77
|
+
1. **Deduplicates** — keeps only the last operation per document in a batch
|
|
78
|
+
2. **Handles deletions** — `DELETE_NODE` on the drive removes the node + all edges from the index
|
|
79
|
+
3. **Filters** — skips anything that isn't `bai/knowledge-note`
|
|
80
|
+
4. **Reconciles** — for each changed document, reads `context.resultingState`:
|
|
81
|
+
- Upserts into `graph_nodes` (id, title, description, noteType, status)
|
|
82
|
+
- Deletes old edges for that source document
|
|
83
|
+
- Inserts new edges from the note's `links[]` array
|
|
84
|
+
|
|
85
|
+
**Schema:**
|
|
86
|
+
|
|
87
|
+
```
|
|
88
|
+
graph_nodes: id, document_id, title, description, note_type, status, updated_at
|
|
89
|
+
graph_edges: id, source_document_id, target_document_id, link_type, target_title, updated_at
|
|
42
90
|
```
|
|
43
91
|
|
|
44
|
-
|
|
45
|
-
This schema will define the structure and fields for your document model using GraphQL.
|
|
46
|
-
Follow one of our tutorials on Academy to get familiar with the process.
|
|
92
|
+
Indexes on `source_document_id`, `target_document_id`, and `status` for query performance.
|
|
47
93
|
|
|
48
|
-
|
|
49
|
-
Using the Document Model Operations Editor, define the operations for your document model and their GraphQL counterparts.
|
|
50
|
-
These operations will handle state changes within your document model.
|
|
94
|
+
**On disconnect:** No-op — preserves indexed data across restarts. The reactor does not replay historical operations, so wiping tables would leave the index permanently empty.
|
|
51
95
|
|
|
52
|
-
|
|
96
|
+
## Subgraph: Knowledge Graph
|
|
53
97
|
|
|
54
|
-
|
|
55
|
-
- Use GraphQL input types to specify the parameters for each operation.
|
|
56
|
-
- Ensure that operations align with user intent to maintain a clean and understandable API.
|
|
98
|
+
GraphQL query layer exposing the indexed data (`subgraphs/knowledge-graph/`). Registered at `/graphql/knowledgeGraph`.
|
|
57
99
|
|
|
58
|
-
###
|
|
59
|
-
Export your document model as a .zip file from Connect.
|
|
60
|
-
Import the .zip file into your project directory created in Step 1.
|
|
61
|
-
Run the following command to generate the scaffolding code:
|
|
100
|
+
### Queries
|
|
62
101
|
|
|
63
|
-
|
|
64
|
-
|
|
102
|
+
| Query | Description |
|
|
103
|
+
|-------|-------------|
|
|
104
|
+
| `knowledgeGraphNodes(driveId)` | All indexed nodes |
|
|
105
|
+
| `knowledgeGraphEdges(driveId)` | All indexed edges |
|
|
106
|
+
| `knowledgeGraphStats(driveId)` | Node count, edge count, orphan count |
|
|
107
|
+
| `knowledgeGraphSearch(driveId, query, limit?)` | Text search on title + description |
|
|
108
|
+
| `knowledgeGraphOrphans(driveId)` | Nodes with no incoming edges |
|
|
109
|
+
| `knowledgeGraphConnections(driveId, documentId, depth?)` | BFS traversal from a node |
|
|
110
|
+
| `knowledgeGraphBacklinks(driveId, documentId)` | Edges pointing TO a document |
|
|
111
|
+
| `knowledgeGraphForwardLinks(driveId, documentId)` | Edges pointing FROM a document |
|
|
112
|
+
| `knowledgeGraphTriangles(driveId, limit?)` | Pairs that share a target but aren't linked |
|
|
113
|
+
| `knowledgeGraphBridges(driveId)` | Articulation points that connect clusters |
|
|
114
|
+
| `knowledgeGraphDensity(driveId)` | Graph density metric |
|
|
115
|
+
| `knowledgeGraphDebug(driveId)` | Raw DB rows for debugging |
|
|
116
|
+
|
|
117
|
+
### Mutations
|
|
118
|
+
|
|
119
|
+
| Mutation | Description |
|
|
120
|
+
|----------|-------------|
|
|
121
|
+
| `knowledgeGraphReindex(driveId)` | Backfill the index by reading all notes — use when the processor missed historical operations |
|
|
122
|
+
|
|
123
|
+
## Deep Dive: Processor + Subgraph Architecture
|
|
124
|
+
|
|
125
|
+
### The Problem They Solve
|
|
126
|
+
|
|
127
|
+
Powerhouse documents are stored as **operation logs** (event sourcing). To read a note's title, you replay all its operations to reconstruct the current state. This works for individual documents, but becomes expensive when you need to:
|
|
128
|
+
|
|
129
|
+
- Search across 500+ notes by keyword
|
|
130
|
+
- Find all notes linking to a specific note
|
|
131
|
+
- Calculate graph metrics (density, orphans, bridges)
|
|
132
|
+
- Render a graph visualization of all notes and edges
|
|
133
|
+
|
|
134
|
+
Without an index, every query would require loading and replaying every document in the drive. The processor + subgraph pattern solves this by maintaining a **materialized read model** — a relational projection of document state that's always up to date.
|
|
135
|
+
|
|
136
|
+
### How the Processor Works (Write Path)
|
|
137
|
+
|
|
138
|
+
The `GraphIndexerProcessor` sits between the Reactor and a PGlite relational database. It implements the `RelationalDbProcessor` base class from `@powerhousedao/shared/processors`.
|
|
139
|
+
|
|
140
|
+
```
|
|
141
|
+
┌─────────────────────────────────────────────────────┐
|
|
142
|
+
│ Reactor │
|
|
143
|
+
│ │
|
|
144
|
+
│ Document A ──op──▶ ┌───────────────────────────┐ │
|
|
145
|
+
│ Document B ──op──▶ │ GraphIndexerProcessor │ │
|
|
146
|
+
│ Document C ──op──▶ │ │ │
|
|
147
|
+
│ Drive (delete)──op─▶│ filter: │ │
|
|
148
|
+
│ │ bai/knowledge-note │ │
|
|
149
|
+
│ │ powerhouse/document-drive│ │
|
|
150
|
+
│ │ │ │
|
|
151
|
+
│ │ onOperations(ops[]): │ │
|
|
152
|
+
│ │ 1. deduplicate │ │
|
|
153
|
+
│ │ 2. handle deletions │ │
|
|
154
|
+
│ │ 3. upsert graph_nodes │ │
|
|
155
|
+
│ │ 4. reconcile graph_edges │ │
|
|
156
|
+
│ └──────────┬────────────────┘ │
|
|
157
|
+
│ │ │
|
|
158
|
+
│ ┌──────────▼────────────────┐ │
|
|
159
|
+
│ │ PGlite (namespaced) │ │
|
|
160
|
+
│ │ │ │
|
|
161
|
+
│ │ graph_nodes │ │
|
|
162
|
+
│ │ id, document_id, title │ │
|
|
163
|
+
│ │ description, note_type │ │
|
|
164
|
+
│ │ status, updated_at │ │
|
|
165
|
+
│ │ │ │
|
|
166
|
+
│ │ graph_edges │ │
|
|
167
|
+
│ │ id, source_document_id │ │
|
|
168
|
+
│ │ target_document_id │ │
|
|
169
|
+
│ │ link_type, target_title │ │
|
|
170
|
+
│ └───────────────────────────┘ │
|
|
171
|
+
└─────────────────────────────────────────────────────┘
|
|
65
172
|
```
|
|
66
173
|
|
|
67
|
-
|
|
174
|
+
**Key design decisions:**
|
|
68
175
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
176
|
+
1. **Filter-based subscription** — The processor declares interest in specific document types and scopes. The Reactor only sends matching operations, avoiding unnecessary work:
|
|
177
|
+
```typescript
|
|
178
|
+
const filter: ProcessorFilter = {
|
|
179
|
+
branch: ["main"],
|
|
180
|
+
documentId: ["*"],
|
|
181
|
+
documentType: ["bai/knowledge-note", "powerhouse/document-drive"],
|
|
182
|
+
scope: ["global"],
|
|
183
|
+
};
|
|
184
|
+
```
|
|
73
185
|
|
|
74
|
-
|
|
75
|
-
|
|
186
|
+
2. **State reconciliation, not event replay** — The processor doesn't interpret individual operations (SET_TITLE, ADD_LINK, etc.). Instead, it reads `context.resultingState` — the full document state after the operation — and upserts the entire node + edges. This means it doesn't need to know the document model's operation semantics.
|
|
187
|
+
|
|
188
|
+
3. **Namespace isolation** — Each drive gets its own PGlite namespace (`GraphIndexerProcessor_<driveId>`), so multiple drives don't interfere with each other. The namespace is derived deterministically from the drive ID.
|
|
189
|
+
|
|
190
|
+
4. **Idempotent migrations** — Tables and indexes are created with `ifNotExists`, so the processor can restart safely without schema conflicts.
|
|
191
|
+
|
|
192
|
+
5. **No-op on disconnect** — The processor preserves its data across server restarts. Since the Reactor doesn't replay historical operations to processors, wiping on disconnect would leave the index empty until new edits arrive.
|
|
193
|
+
|
|
194
|
+
### How the Subgraph Works (Read Path)
|
|
195
|
+
|
|
196
|
+
The `KnowledgeGraphSubgraph` extends `BaseSubgraph` from `@powerhousedao/reactor-api` and exposes the indexed data via GraphQL at `/graphql/knowledgeGraph`.
|
|
76
197
|
|
|
77
|
-
```
|
|
78
|
-
|
|
198
|
+
```
|
|
199
|
+
┌──────────────────────────────────────────────────┐
|
|
200
|
+
│ GraphQL Clients │
|
|
201
|
+
│ │
|
|
202
|
+
│ Connect UI ─────────┐ │
|
|
203
|
+
│ AI Agent (MCP) ─────┤ │
|
|
204
|
+
│ Switchboard CLI ────┤ query / mutation │
|
|
205
|
+
│ Third-party app ────┤ │
|
|
206
|
+
│ curl / Postman ─────┘ │
|
|
207
|
+
│ │ │
|
|
208
|
+
│ ┌──────────▼───────────────────┐ │
|
|
209
|
+
│ │ /graphql/knowledgeGraph │ │
|
|
210
|
+
│ │ │ │
|
|
211
|
+
│ │ KnowledgeGraphSubgraph │ │
|
|
212
|
+
│ │ ├─ getDb(driveId) │ │
|
|
213
|
+
│ │ │ → namespaced Kysely │ │
|
|
214
|
+
│ │ ├─ getQuery(driveId) │ │
|
|
215
|
+
│ │ │ → typed query API │ │
|
|
216
|
+
│ │ ├─ reindexDrive() │ │
|
|
217
|
+
│ │ │ → backfill from │ │
|
|
218
|
+
│ │ │ reactorClient │ │
|
|
219
|
+
│ │ └─ ensureGraphDoc() │ │
|
|
220
|
+
│ │ → auto-create │ │
|
|
221
|
+
│ │ bai/knowledge- │ │
|
|
222
|
+
│ │ graph doc │ │
|
|
223
|
+
│ └──────────┬───────────────────┘ │
|
|
224
|
+
│ │ │
|
|
225
|
+
│ ┌──────────▼───────────────────┐ │
|
|
226
|
+
│ │ PGlite (same namespace as │ │
|
|
227
|
+
│ │ the processor writes to) │ │
|
|
228
|
+
│ └──────────────────────────────┘ │
|
|
229
|
+
└──────────────────────────────────────────────────┘
|
|
79
230
|
```
|
|
80
231
|
|
|
81
|
-
|
|
232
|
+
**The subgraph reads from the same PGlite tables the processor writes to.** This is the key architectural link — the processor is the write path, the subgraph is the read path, and they share a namespace.
|
|
82
233
|
|
|
83
|
-
|
|
234
|
+
The subgraph provides two levels of query abstraction:
|
|
84
235
|
|
|
85
|
-
|
|
86
|
-
|
|
236
|
+
1. **`getDb(driveId)`** — Returns a typed `Kysely<DB>` instance scoped to the processor's namespace. This centralizes the `IRelationalDbLegacy → IRelationalDb → Kysely` cast in one place.
|
|
237
|
+
|
|
238
|
+
2. **`getQuery(driveId)`** — Wraps `getDb` with the `createGraphQuery()` helper that provides high-level methods (`allNodes()`, `searchNodes()`, `connections()`, `triangles()`, `bridges()`, etc.). All queries use Kysely's type-safe query builder.
|
|
239
|
+
|
|
240
|
+
### How Third-Party Plugins Can Use This
|
|
241
|
+
|
|
242
|
+
Any application that can make GraphQL requests to the Reactor's endpoint can query the knowledge graph. The subgraph is accessible at `/graphql/knowledgeGraph` (local: `http://localhost:4001/graphql/knowledgeGraph`, remote: `https://your-switchboard.example.com/graphql/knowledgeGraph`).
|
|
243
|
+
|
|
244
|
+
**Example: Search notes from any client**
|
|
245
|
+
|
|
246
|
+
```graphql
|
|
247
|
+
query SearchNotes($driveId: ID!, $query: String!) {
|
|
248
|
+
knowledgeGraphSearch(driveId: $driveId, query: $query, limit: 20) {
|
|
249
|
+
documentId
|
|
250
|
+
title
|
|
251
|
+
description
|
|
252
|
+
noteType
|
|
253
|
+
status
|
|
254
|
+
}
|
|
255
|
+
}
|
|
87
256
|
```
|
|
88
257
|
|
|
89
|
-
|
|
258
|
+
**Example: Get graph structure for visualization**
|
|
259
|
+
|
|
260
|
+
```graphql
|
|
261
|
+
query GraphData($driveId: ID!) {
|
|
262
|
+
knowledgeGraphNodes(driveId: $driveId) {
|
|
263
|
+
documentId
|
|
264
|
+
title
|
|
265
|
+
noteType
|
|
266
|
+
status
|
|
267
|
+
}
|
|
268
|
+
knowledgeGraphEdges(driveId: $driveId) {
|
|
269
|
+
sourceDocumentId
|
|
270
|
+
targetDocumentId
|
|
271
|
+
linkType
|
|
272
|
+
targetTitle
|
|
273
|
+
}
|
|
274
|
+
knowledgeGraphStats(driveId: $driveId) {
|
|
275
|
+
nodeCount
|
|
276
|
+
edgeCount
|
|
277
|
+
orphanCount
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
```
|
|
90
281
|
|
|
91
|
-
|
|
282
|
+
**Example: Find synthesis opportunities (triangles)**
|
|
92
283
|
|
|
93
|
-
```
|
|
94
|
-
|
|
284
|
+
```graphql
|
|
285
|
+
query FindTriangles($driveId: ID!) {
|
|
286
|
+
knowledgeGraphTriangles(driveId: $driveId, limit: 10) {
|
|
287
|
+
noteA { documentId title }
|
|
288
|
+
noteB { documentId title }
|
|
289
|
+
sharedTarget { documentId title }
|
|
290
|
+
}
|
|
291
|
+
}
|
|
95
292
|
```
|
|
96
293
|
|
|
97
|
-
|
|
294
|
+
**Example: Backfill the index after deployment**
|
|
98
295
|
|
|
99
|
-
```
|
|
100
|
-
|
|
296
|
+
```graphql
|
|
297
|
+
mutation Reindex($driveId: ID!) {
|
|
298
|
+
knowledgeGraphReindex(driveId: $driveId) {
|
|
299
|
+
indexedNodes
|
|
300
|
+
indexedEdges
|
|
301
|
+
errors
|
|
302
|
+
}
|
|
303
|
+
}
|
|
101
304
|
```
|
|
102
305
|
|
|
103
|
-
|
|
306
|
+
**Using the Switchboard CLI:**
|
|
104
307
|
|
|
105
308
|
```bash
|
|
106
|
-
|
|
309
|
+
# Search
|
|
310
|
+
switchboard query '{ knowledgeGraphSearch(driveId: "<UUID>", query: "reactor") { documentId title } }'
|
|
311
|
+
|
|
312
|
+
# Stats
|
|
313
|
+
switchboard query '{ knowledgeGraphStats(driveId: "<UUID>") { nodeCount edgeCount orphanCount } }'
|
|
314
|
+
|
|
315
|
+
# Reindex
|
|
316
|
+
switchboard query 'mutation { knowledgeGraphReindex(driveId: "<UUID>") { indexedNodes indexedEdges errors } }'
|
|
107
317
|
```
|
|
108
318
|
|
|
109
|
-
|
|
110
|
-
|
|
319
|
+
**Using MCP (for AI agents):**
|
|
320
|
+
|
|
321
|
+
AI agents connected via MCP can't call the subgraph directly, but they can use `mcp__reactor-mcp__getDocument` to read individual documents. The Switchboard CLI provides the fastest path for agents to query the graph index — the `powerhouse-knowledge` plugin uses this pattern.
|
|
322
|
+
|
|
323
|
+
### Why This Pattern Matters
|
|
324
|
+
|
|
325
|
+
The processor + subgraph pattern is the recommended way to build **read-optimized projections** in Powerhouse:
|
|
326
|
+
|
|
327
|
+
| Concern | Without processor | With processor |
|
|
328
|
+
|---------|------------------|----------------|
|
|
329
|
+
| Search 500 notes by keyword | Load all 500 documents, replay ops, scan content | Single SQL `LIKE` query on `graph_nodes.title` |
|
|
330
|
+
| Find orphan notes | Load all docs, build adjacency list in memory | `SELECT * FROM graph_nodes WHERE document_id NOT IN (SELECT target_document_id FROM graph_edges)` |
|
|
331
|
+
| Graph density | Load everything, count manually | Two `COUNT(*)` queries |
|
|
332
|
+
| Incremental updates | Reload everything on each change | Upsert one row per changed document |
|
|
333
|
+
|
|
334
|
+
The same pattern can be applied to any domain — an invoice tracker could project invoice totals into a summary table, a project manager could index task statuses, etc. The processor handles the write path (operation → relational row), and the subgraph handles the read path (GraphQL → SQL → response).
|
|
335
|
+
|
|
336
|
+
## Data Flow
|
|
111
337
|
|
|
112
|
-
```bash
|
|
113
|
-
npm run generate -- --editor YourModelName --document-types powerhouse/YourModelName
|
|
114
338
|
```
|
|
339
|
+
User edits a note in the editor
|
|
340
|
+
→ dispatch(setTitle({...}))
|
|
341
|
+
→ operation recorded on the document
|
|
342
|
+
→ Reactor sends operation to GraphIndexerProcessor
|
|
343
|
+
→ Processor upserts graph_nodes + graph_edges in PGlite
|
|
344
|
+
→ Subgraph queries return updated data
|
|
345
|
+
→ GraphView in the drive-app renders the graph
|
|
346
|
+
```
|
|
347
|
+
|
|
348
|
+
## Drive Structure
|
|
115
349
|
|
|
116
|
-
|
|
117
|
-
The --document-types flag links the editor to your document model type.
|
|
118
|
-
After generation:
|
|
350
|
+
Documents are organized into folders within the drive:
|
|
119
351
|
|
|
120
|
-
|
|
352
|
+
| Folder | Document Types | Purpose |
|
|
353
|
+
|--------|---------------|---------|
|
|
354
|
+
| `/knowledge/notes/` | `bai/knowledge-note` | Atomic knowledge claims |
|
|
355
|
+
| `/knowledge/` | `bai/moc` | Maps of Content |
|
|
356
|
+
| `/sources/` | `bai/source` | Raw input material |
|
|
357
|
+
| `/research/` | `bai/research-claim` | Methodology foundation |
|
|
358
|
+
| `/ops/queue/` | `bai/pipeline-queue` | Pipeline singleton |
|
|
359
|
+
| `/ops/health/` | `bai/health-report` | Health report singleton |
|
|
360
|
+
| `/ops/sessions/` | `bai/observation` | Operational signals |
|
|
361
|
+
| `/self/` | `bai/knowledge-graph`, `bai/vault-config` | Singletons |
|
|
362
|
+
|
|
363
|
+
## Development
|
|
121
364
|
|
|
122
365
|
```bash
|
|
123
|
-
|
|
366
|
+
# Install dependencies
|
|
367
|
+
bun install
|
|
368
|
+
|
|
369
|
+
# Start Vetra Studio with live code generation
|
|
370
|
+
ph vetra --watch
|
|
371
|
+
|
|
372
|
+
# Type check
|
|
373
|
+
bun tsc --noEmit
|
|
374
|
+
|
|
375
|
+
# Lint
|
|
376
|
+
bun eslint <file>
|
|
377
|
+
|
|
378
|
+
# Format
|
|
379
|
+
bun prettier --write <file>
|
|
380
|
+
|
|
381
|
+
# Run tests
|
|
382
|
+
bun test
|
|
124
383
|
```
|
|
125
384
|
|
|
126
|
-
|
|
385
|
+
### Subgraph Endpoint Configuration
|
|
386
|
+
|
|
387
|
+
The Search tab and other editor features that query the Knowledge Graph subgraph need to reach the reactor's GraphQL endpoint. The endpoint is resolved automatically in most cases:
|
|
127
388
|
|
|
128
|
-
|
|
129
|
-
|
|
389
|
+
| Environment | How it works |
|
|
390
|
+
|-------------|-------------|
|
|
391
|
+
| **`ph vetra --watch`** (local dev) | Auto-detected: Vite runs on port 3000/3001, subgraph at `http://localhost:4001/graphql/knowledgeGraph` |
|
|
392
|
+
| **Connect production** (same origin) | Auto-detected: relative path `/graphql/knowledgeGraph` |
|
|
393
|
+
| **Deployed** (Connect and Switchboard on different domains) | Set `VITE_SUBGRAPH_URL` env var |
|
|
394
|
+
|
|
395
|
+
For deployed environments where Connect runs on a different domain than the Switchboard (e.g., `connect.example.com` vs `switchboard-dev.powerhouse.xyz`), create a `.env` file:
|
|
130
396
|
|
|
131
397
|
```bash
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
Verify that the editor functions as expected.
|
|
136
|
-
Perform end-to-end testing to ensure smooth integration between the document model and its editor.
|
|
137
|
-
|
|
138
|
-
### 7. Adding a Manifest File
|
|
139
|
-
Create a manifest file to describe your document model and editor. This enables proper integration with the host application.
|
|
140
|
-
|
|
141
|
-
**Example manifest.json:**
|
|
142
|
-
|
|
143
|
-
```json
|
|
144
|
-
{
|
|
145
|
-
"name": "your-model-name",
|
|
146
|
-
"description": "A brief description of your document model.",
|
|
147
|
-
"category": "your-category", // e.g., "Finance", "People Ops", "Legal"
|
|
148
|
-
"publisher": {
|
|
149
|
-
"name": "your-publisher-name",
|
|
150
|
-
"url": "your-publisher-url"
|
|
151
|
-
},
|
|
152
|
-
"documentModels": [
|
|
153
|
-
{
|
|
154
|
-
"id": "your-model-id",
|
|
155
|
-
"name": "your-model-name"
|
|
156
|
-
}
|
|
157
|
-
],
|
|
158
|
-
"editors": [
|
|
159
|
-
{
|
|
160
|
-
"id": "your-editor-id",
|
|
161
|
-
"name": "your-editor-name",
|
|
162
|
-
"documentTypes": ["your-model-id"]
|
|
163
|
-
}
|
|
164
|
-
]
|
|
165
|
-
}
|
|
398
|
+
# .env
|
|
399
|
+
VITE_SUBGRAPH_URL=https://switchboard-dev.powerhouse.xyz/graphql/knowledgeGraph
|
|
166
400
|
```
|
|
167
401
|
|
|
168
|
-
|
|
402
|
+
This is only needed when the app and reactor are on different origins. Local development and same-origin deployments work without any configuration.
|
|
403
|
+
|
|
404
|
+
## Graph View
|
|
405
|
+
|
|
406
|
+
The knowledge graph visualization uses `cytoscape-fcose` (force-directed layout) with semantic clustering:
|
|
169
407
|
|
|
170
|
-
|
|
171
|
-
|
|
408
|
+
- **MOC hubs** act as cluster anchors with higher repulsion, pulling their CORE_IDEA-linked notes into visible topic neighborhoods
|
|
409
|
+
- **Cross-cluster edges** have weak elasticity and long ideal lengths, preventing topic groups from collapsing together
|
|
410
|
+
- **Position persistence** via localStorage — positions survive tab switches and page reloads. New nodes are placed by fcose while existing nodes stay pinned
|
|
411
|
+
- **MOC group drag** — dragging a MOC diamond moves its entire cluster of connected notes
|
|
412
|
+
- **Re-layout button** clears cached positions and recomputes a fresh layout
|
|
172
413
|
|
|
173
|
-
|
|
174
|
-
You've now successfully created a Document Model and its corresponding Editor using the Connect app!
|
|
414
|
+
## License
|
|
175
415
|
|
|
176
|
-
|
|
177
|
-
- Expand functionality: Add more operations or complex logic to your document model.
|
|
178
|
-
- Improve UX: Enhance the document editor for a smoother user experience.
|
|
179
|
-
- Integrate with other systems: Use APIs or GraphQL to connect your document model with external services.
|
|
416
|
+
AGPL-3.0-only
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DriveExplorer.d.ts","sourceRoot":"","sources":["../../../../editors/knowledge-vault/components/DriveExplorer.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"DriveExplorer.d.ts","sourceRoot":"","sources":["../../../../editors/knowledge-vault/components/DriveExplorer.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAiClD,wBAAgB,aAAa,CAAC,EAAE,QAAQ,EAAE,EAAE,WAAW,2CAiWtD"}
|
|
@@ -7,6 +7,7 @@ import { GraphView } from "./GraphView.js";
|
|
|
7
7
|
import { NoteList } from "./NoteList.js";
|
|
8
8
|
import { SourceList } from "./SourceList.js";
|
|
9
9
|
import { HealthDashboard } from "./HealthDashboard.js";
|
|
10
|
+
import { SearchView } from "./SearchView.js";
|
|
10
11
|
import { GettingStartedButton } from "./GettingStarted.js";
|
|
11
12
|
import { useKnowledgeNotes } from "../hooks/use-knowledge-notes.js";
|
|
12
13
|
import { useKnowledgeGraph, buildSyncPayload, } from "../hooks/use-knowledge-graph.js";
|
|
@@ -15,7 +16,7 @@ import { useKnowledgeGraphDocumentById } from "@powerhousedao/knowledge-note/doc
|
|
|
15
16
|
import { actions as graphActions } from "@powerhousedao/knowledge-note/document-models/knowledge-graph";
|
|
16
17
|
import { ThemeToggle } from "../../shared/theme-context.js";
|
|
17
18
|
export function DriveExplorer({ children }) {
|
|
18
|
-
const [viewMode, setViewMode] = useState("
|
|
19
|
+
const [viewMode, setViewMode] = useState("search");
|
|
19
20
|
const { notes } = useKnowledgeNotes();
|
|
20
21
|
const { graphDoc, graphState, hasGraphDoc } = useKnowledgeGraph(notes);
|
|
21
22
|
const fileNodes = useFileNodesInSelectedDrive();
|
|
@@ -106,6 +107,11 @@ export function DriveExplorer({ children }) {
|
|
|
106
107
|
setViewMode(mode);
|
|
107
108
|
}
|
|
108
109
|
const TABS = [
|
|
110
|
+
{
|
|
111
|
+
key: "search",
|
|
112
|
+
label: "Search",
|
|
113
|
+
icon: (_jsxs("svg", { className: "h-4 w-4", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [_jsx("circle", { cx: "11", cy: "11", r: "8" }), _jsx("path", { d: "m21 21-4.35-4.35" })] })),
|
|
114
|
+
},
|
|
109
115
|
{
|
|
110
116
|
key: "notes",
|
|
111
117
|
label: "Notes",
|
|
@@ -155,7 +161,7 @@ export function DriveExplorer({ children }) {
|
|
|
155
161
|
}, children: tab.badge }))] }, tab.key))), showDocumentEditor && (_jsxs("span", { className: "flex items-center gap-1.5 rounded-md px-3 py-1.5 text-xs font-medium", style: {
|
|
156
162
|
backgroundColor: "var(--bai-hover)",
|
|
157
163
|
color: "var(--bai-accent)",
|
|
158
|
-
}, children: [_jsxs("svg", { className: "h-4 w-4", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [_jsx("path", { d: "M11 4H4a2 2 0 00-2 2v14a2 2 0 002 2h14a2 2 0 002-2v-7" }), _jsx("path", { d: "M18.5 2.5a2.121 2.121 0 013 3L12 15l-4 1 1-4 9.5-9.5z" })] }), "Editing"] }))] }), _jsxs("div", { className: "flex items-center gap-3", children: [hasGraphDoc && graphState && (_jsxs("span", { className: "text-[10px]", style: { color: "var(--bai-text-faint)" }, children: [graphState.nodes.length, "n / ", graphState.edges.length, "e"] })), _jsx(ThemeToggle, {}), _jsx(GettingStartedButton, {}), _jsx(CreateMenu, {})] })] }), _jsx("div", { className: "flex-1 overflow-auto", children: showDocumentEditor ? (_jsx("div", { className: "h-full", children: children })) : viewMode === "graph" ? (_jsx(GraphView, { notes: notes, graphState: graphState, mocs: mocs, tensions: tensions })) : viewMode === "sources" ? (_jsx(SourceList, {})) : viewMode === "health" ? (_jsx(HealthDashboard, {})) : (_jsx(NoteList, { notes: notes })) })] })] }));
|
|
164
|
+
}, children: [_jsxs("svg", { className: "h-4 w-4", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [_jsx("path", { d: "M11 4H4a2 2 0 00-2 2v14a2 2 0 002 2h14a2 2 0 002-2v-7" }), _jsx("path", { d: "M18.5 2.5a2.121 2.121 0 013 3L12 15l-4 1 1-4 9.5-9.5z" })] }), "Editing"] }))] }), _jsxs("div", { className: "flex items-center gap-3", children: [hasGraphDoc && graphState && (_jsxs("span", { className: "text-[10px]", style: { color: "var(--bai-text-faint)" }, children: [graphState.nodes.length, "n / ", graphState.edges.length, "e"] })), _jsx(ThemeToggle, {}), _jsx(GettingStartedButton, {}), _jsx(CreateMenu, {})] })] }), _jsx("div", { className: "flex-1 overflow-auto", children: showDocumentEditor ? (_jsx("div", { className: "h-full", children: children })) : viewMode === "graph" ? (_jsx(GraphView, { notes: notes, graphState: graphState, mocs: mocs, tensions: tensions })) : viewMode === "search" ? (_jsx(SearchView, {})) : viewMode === "sources" ? (_jsx(SourceList, {})) : viewMode === "health" ? (_jsx(HealthDashboard, {})) : (_jsx(NoteList, { notes: notes })) })] })] }));
|
|
159
165
|
}
|
|
160
166
|
const CREATE_ITEMS = [
|
|
161
167
|
{
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SearchView.d.ts","sourceRoot":"","sources":["../../../../editors/knowledge-vault/components/SearchView.tsx"],"names":[],"mappings":"AA+CA,wBAAgB,UAAU,4CAmGzB"}
|