mirror of
https://github.com/proffesor-for-testing/agentic-qe.git
synced 2026-09-19 08:45:47 +08:00
3b81b10d30
Brain export v3.0 (25 tables), witness chain v3 with Ed25519 signing, streaming for large tables, RVF freeze/derive/indexStats, npm packaging fix. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1531 lines
60 KiB
Markdown
1531 lines
60 KiB
Markdown
# Brain Export/Import v3.0 Improvement Plan
|
||
|
||
> **Note (March 2026):** This plan references "26 tables" throughout. The actual
|
||
> `TABLE_CONFIGS` in `brain-shared.ts` contains **25 entries**. The discrepancy is
|
||
> because entry #26 in the plan (qe_pattern_embeddings JSONL gap fix) refers to a
|
||
> table already counted as entry #6. All 25 unique tables are fully implemented.
|
||
|
||
## Executive Summary
|
||
|
||
The `aqe brain export/import` system currently serializes 5 of 39 database tables and
|
||
suffers from code duplication (~250 lines), an embedding restore gap (vectors exported
|
||
but never reimported), and underutilized RuVector capabilities. This plan delivers a
|
||
phased upgrade from manifest version 1.0/2.0 to 3.0 with full round-trip fidelity.
|
||
|
||
**Measurable outcomes after all phases:**
|
||
|
||
| Metric | Before | After |
|
||
|--------|--------|-------|
|
||
| Tables exported | 5 | 26 |
|
||
| Embedding round-trip | 0% (always 0 restored) | 100% |
|
||
| Duplicated lines | ~250 | 0 |
|
||
| Manifest version | 1.0 (JSONL) / 2.0 (RVF) | 3.0 (both) |
|
||
| RVF features used | 2 (ingest, embedKernel) | 7+ |
|
||
|
||
---
|
||
|
||
## Architecture Overview
|
||
|
||
```
|
||
src/integrations/ruvector/
|
||
brain-shared.ts <-- NEW: shared types, merge engine, DDL, utilities
|
||
brain-exporter.ts <-- MODIFIED: delegates to brain-shared, adds new tables
|
||
brain-rvf-exporter.ts <-- MODIFIED: delegates to brain-shared, embedding restore
|
||
rvf-native-adapter.ts <-- MODIFIED: expose new native capabilities
|
||
|
||
src/cli/
|
||
brain-commands.ts <-- MODIFIED: pass new options, manifest v3 types
|
||
handlers/brain-handler.ts <-- MODIFIED: display new table stats
|
||
```
|
||
|
||
---
|
||
|
||
## Phase 1: Extract Shared Code (De-duplication)
|
||
|
||
**Goal:** Eliminate ~250 lines of identical code between `brain-exporter.ts` and
|
||
`brain-rvf-exporter.ts` by extracting a shared module.
|
||
|
||
### Milestone 1.1: Create `brain-shared.ts`
|
||
|
||
**Affected files:**
|
||
- NEW: `src/integrations/ruvector/brain-shared.ts` (~200 lines)
|
||
- MODIFIED: `src/integrations/ruvector/brain-exporter.ts`
|
||
- MODIFIED: `src/integrations/ruvector/brain-rvf-exporter.ts`
|
||
- MODIFIED: `tests/unit/brain-exporter.test.ts`
|
||
- NEW: `tests/unit/brain-shared.test.ts`
|
||
|
||
**Extract the following from both files into `brain-shared.ts`:**
|
||
|
||
1. **Type interfaces** (move to shared, re-export from both):
|
||
- `PatternRow`
|
||
- `QValueRow`
|
||
- `DreamInsightRow`
|
||
- `WitnessRow`
|
||
- `MergeResult`
|
||
|
||
2. **Merge functions** (identical logic in both files):
|
||
- `mergePattern(db, pattern, strategy) -> MergeResult`
|
||
- `mergeQValue(db, qv, strategy) -> MergeResult`
|
||
- `mergeDreamInsight(db, insight, strategy) -> MergeResult`
|
||
- `mergeWitnessEntry(db, entry, strategy) -> MergeResult`
|
||
|
||
3. **SQL insert/update helpers** (identical in both):
|
||
- `insertPattern` / `updatePattern`
|
||
- `insertQValue` / `updateQValue`
|
||
- `insertDreamInsight` / `updateDreamInsight`
|
||
- `insertWitnessEntry`
|
||
|
||
4. **DDL function** (nearly identical):
|
||
- `ensureTargetTables(db)` -- merge both versions, keeping the superset of tables
|
||
|
||
5. **Utility functions** (identical):
|
||
- `tableExists(db, name) -> boolean`
|
||
- `queryAll(db, table, where?, params?) -> unknown[]`
|
||
- `domainFilter(domains?) -> [clause, params]`
|
||
- `sha256(data) -> string`
|
||
|
||
**Approach:**
|
||
- Use a data-driven merge engine: define a `TableExportConfig` interface that
|
||
declares how each table should be queried, deduplicated, and merged.
|
||
- Each table config specifies: `tableName`, `idColumn`, `timestampColumn`,
|
||
`confidenceColumn`, `insertFn`, `updateFn`, `ddlSql`.
|
||
|
||
```typescript
|
||
// brain-shared.ts - Core type for the data-driven merge engine
|
||
export interface TableExportConfig {
|
||
/** SQLite table name */
|
||
tableName: string;
|
||
/** Column used as primary key for dedup (e.g. 'id') */
|
||
idColumn: string;
|
||
/** Dedup strategy: 'id' checks by PK, 'composite' uses custom check */
|
||
dedupStrategy: 'id' | 'composite';
|
||
/** For composite dedup: columns to check */
|
||
compositeColumns?: string[];
|
||
/** Column used for latest-wins comparison (e.g. 'updated_at', 'created_at') */
|
||
timestampColumn?: string;
|
||
/** Column used for highest-confidence comparison */
|
||
confidenceColumn?: string;
|
||
/** For Q-values where confidence proxy is a different field */
|
||
confidenceProxy?: string;
|
||
/** Whether this is an append-only log (like witness_chain) */
|
||
appendOnly?: boolean;
|
||
/** Whether this table has domain filtering (uses qe_domain or domain) */
|
||
domainFilterColumn?: string;
|
||
/** Whether this table contains BLOB embedding columns */
|
||
embeddingColumns?: string[];
|
||
/** JSONL filename for directory-format exports */
|
||
jsonlFilename: string;
|
||
/** CREATE TABLE IF NOT EXISTS DDL */
|
||
ddl: string;
|
||
}
|
||
```
|
||
|
||
### Success Criteria for Phase 1
|
||
|
||
- [ ] Zero duplicated merge/SQL code between the two exporter files
|
||
- [ ] All 12 existing unit tests in `brain-exporter.test.ts` pass unchanged
|
||
- [ ] New `brain-shared.test.ts` has 15+ tests covering merge logic in isolation
|
||
- [ ] Both `brain-exporter.ts` and `brain-rvf-exporter.ts` import from `brain-shared.ts`
|
||
- [ ] Each file stays under 500 lines
|
||
- [ ] `npm run build` succeeds
|
||
- [ ] `npm test -- --run` passes
|
||
|
||
**Estimated complexity:** Medium (refactor-only, no new functionality)
|
||
**Estimated effort:** 4-6 hours
|
||
|
||
---
|
||
|
||
## Phase 2: Add Missing Tables to Export
|
||
|
||
**Goal:** Export 21 additional tables (26 total) that contain meaningful learning data.
|
||
|
||
### Table Priority Classification
|
||
|
||
#### Tier 1 -- HIGH (significant learning data, export first)
|
||
|
||
| # | Table | Rows | Embeddings | Why |
|
||
|---|-------|------|-----------|-----|
|
||
| 1 | `captured_experiences` | 3,730 | 132 (384d) | Past task outcomes for experience replay |
|
||
| 2 | `sona_patterns` | 1,025 | 739 (384d state_embedding) | SONA self-learning neural patterns |
|
||
| 3 | `qe_trajectories` | 335 | -- | Task execution trajectories |
|
||
| 4 | `trajectory_steps` | 2,030 | -- | Individual steps within trajectories |
|
||
| 5 | `concept_nodes` | 4,731 | BLOB col exists, 0 populated | Dream knowledge graph vertices |
|
||
| 6 | `concept_edges` | 68,517 | -- | Dream knowledge graph edges |
|
||
| 7 | `dream_cycles` | 694 | -- | Dream cycle metadata (FK parent for insights) |
|
||
| 8 | `goap_actions` | 2,325 | -- | Learned GOAP actions with success rates |
|
||
| 9 | `routing_outcomes` | 497 | -- | Model routing decision history |
|
||
|
||
#### Tier 2 -- MEDIUM (smaller but valuable for completeness)
|
||
|
||
| # | Table | Rows | Why |
|
||
|---|-------|------|-----|
|
||
| 10 | `goap_goals` | 53 | Planning goals |
|
||
| 11 | `goap_plans` | 101 | Computed plans with reuse tracking |
|
||
| 12 | `goap_plan_signatures` | 294 | Plan similarity matching |
|
||
| 13 | `qe_pattern_usage` | 238 | Pattern usage feedback log |
|
||
| 14 | `pattern_evolution_events` | 20 | Pattern change history |
|
||
| 15 | `pattern_relationships` | 6 | Cross-pattern links |
|
||
| 16 | `pattern_versions` | 8 | Historical pattern snapshots (384d) |
|
||
| 17 | `vectors` | 410 | General-purpose vector store |
|
||
| 18 | `experience_applications` | 3 | Experience reuse tracking (FK → captured_experiences) |
|
||
| 19 | `execution_results` | 530 | GOAP plan execution outcomes (success/fail, duration) |
|
||
| 20 | `executed_steps` | 705 | Per-step execution logs with agent_output and world_state |
|
||
|
||
> **Note:** `qe_pattern_embeddings` (40 rows) is already in the "Currently exported (5)"
|
||
> set. Its Tier 2 enhancement is adding JSONL export with Base64 encoding — it is NOT
|
||
> counted as a new table in the 26-table total.
|
||
|
||
#### Tier 3 -- LOW (operational/transient, skip for now)
|
||
|
||
| Table | Rows | Reason to skip |
|
||
|-------|------|---------------|
|
||
| `kv_store` | 4,923 | Mostly operational (4,371 queen metrics). See note below. |
|
||
| `embeddings` | 0 | Cache table, regenerated on demand |
|
||
| `goap_execution_steps` | 0 | Empty, schema overlaps with executed_steps |
|
||
| `mincut_*` (6 tables) | 701 | Graph health snapshots, not portable learning |
|
||
| `hypergraph_nodes` / `hypergraph_edges` | 0 | Neural backbone internals, regenerated |
|
||
| `test_outcomes` | 0 | Test analytics, too environment-specific |
|
||
| `coverage_sessions` | 0 | Coverage analytics, environment-specific |
|
||
| `schema_version` | 1 | Internal migration tracking |
|
||
|
||
> **`kv_store` partial export consideration:** While mostly operational metrics,
|
||
> the following namespaces contain actual learning state that future phases could
|
||
> selectively export: `qe-experiences` (291), `sona-adaptation` (100),
|
||
> `rl-algorithms` (8), `rl-rewards` (4), `rl-applications` (8), `reasoning-bank` (3).
|
||
> Total: ~414 learning-relevant rows. A namespace-filtered kv_store export could
|
||
> be added in a future phase using the `domainFilterColumn` pattern with a
|
||
> namespace prefix filter.
|
||
|
||
### Milestone 2.1: Add Tier 1 Tables (9 tables)
|
||
|
||
**Affected files:**
|
||
- `src/integrations/ruvector/brain-shared.ts` (add 9 TableExportConfig entries + DDL)
|
||
**Note:** `dream_cycles` must be imported BEFORE `dream_insights` (FK parent).
|
||
- `src/integrations/ruvector/brain-exporter.ts` (iterate over new table configs)
|
||
- `src/integrations/ruvector/brain-rvf-exporter.ts` (include new tables in kernel JSON)
|
||
- `src/cli/brain-commands.ts` (update manifest types for new stats)
|
||
- `src/cli/handlers/brain-handler.ts` (display new stats)
|
||
- `tests/unit/brain-exporter.test.ts` (add 9 table export/import tests)
|
||
|
||
**Implementation approach:**
|
||
|
||
For each new table, add a `TableExportConfig` entry in `brain-shared.ts` with:
|
||
- Full DDL from `unified-memory-schemas.ts` (authoritative source)
|
||
- Appropriate dedup strategy
|
||
- Domain filter column where applicable
|
||
|
||
Update the JSONL exporter to iterate over all registered table configs:
|
||
|
||
```typescript
|
||
// brain-exporter.ts
|
||
import { TABLE_CONFIGS, queryAll, domainFilter, writeJsonl } from './brain-shared.js';
|
||
|
||
for (const config of TABLE_CONFIGS) {
|
||
const [where, params] = config.domainFilterColumn
|
||
? domainFilter(options.domains, config.domainFilterColumn)
|
||
: [undefined, []];
|
||
const rows = queryAll(db, config.tableName, where, params);
|
||
writeJsonl(join(outDir, config.jsonlFilename), rows);
|
||
}
|
||
```
|
||
|
||
Update the RVF exporter to include all new tables in the kernel JSON payload.
|
||
|
||
**Key design decisions:**
|
||
|
||
1. `concept_edges` has 68,517 rows -- use streaming JSONL write (line-by-line) to
|
||
avoid buffering the entire dataset in memory.
|
||
|
||
2. `captured_experiences` and `sona_patterns` contain BLOB embedding columns. For
|
||
JSONL format, Base64-encode BLOBs. For RVF format, ingest embeddings into HNSW
|
||
in addition to storing them in the kernel JSON.
|
||
|
||
3. `trajectory_steps` has a FK to `qe_trajectories`. Export `qe_trajectories` first,
|
||
then `trajectory_steps`. On import, insert in the same order.
|
||
|
||
4. Domain filtering: `captured_experiences` uses `domain`, `sona_patterns` uses
|
||
`domain`, `goap_actions` uses `qe_domain`, `routing_outcomes` has no domain column.
|
||
|
||
### Milestone 2.2: Add Tier 2 Tables (11 tables)
|
||
|
||
Same approach as 2.1 for the remaining 11 tables. These are smaller and simpler.
|
||
|
||
**Special handling:**
|
||
- `qe_pattern_usage` uses INTEGER AUTOINCREMENT PK -- dedup by composite
|
||
`(pattern_id, created_at)` like `witness_chain`.
|
||
- `pattern_versions` contains embedding BLOBs -- Base64 in JSONL, ingest in RVF.
|
||
- `qe_pattern_embeddings` already exported to RVF HNSW but not to JSONL -- add JSONL
|
||
export with Base64 encoding.
|
||
- `experience_applications` has FK to `captured_experiences` -- import after Tier 1.
|
||
- `executed_steps` has FK to `goap_plans` (via execution_id → execution_results.id) --
|
||
import `execution_results` first, then `executed_steps`.
|
||
|
||
**FK-aware import order for all 26 tables:**
|
||
```
|
||
1. qe_patterns (no FK deps)
|
||
2. rl_q_values (no FK deps)
|
||
3. dream_cycles (no FK deps)
|
||
4. dream_insights (FK → dream_cycles)
|
||
5. witness_chain (no FK deps)
|
||
6. qe_pattern_embeddings (FK → qe_patterns)
|
||
7. captured_experiences (no FK deps)
|
||
8. sona_patterns (no FK deps)
|
||
9. qe_trajectories (no FK deps)
|
||
10. trajectory_steps (FK → qe_trajectories)
|
||
11. concept_nodes (no FK deps)
|
||
12. concept_edges (FK → concept_nodes)
|
||
13. goap_actions (no FK deps)
|
||
14. routing_outcomes (no FK deps)
|
||
15. goap_goals (no FK deps)
|
||
16. goap_plans (FK → goap_goals)
|
||
17. goap_plan_signatures (FK → goap_plans)
|
||
18. qe_pattern_usage (FK → qe_patterns)
|
||
19. pattern_evolution_events (FK → qe_patterns)
|
||
20. pattern_relationships (FK → qe_patterns)
|
||
21. pattern_versions (FK → qe_patterns)
|
||
22. vectors (no FK deps)
|
||
23. experience_applications (FK → captured_experiences)
|
||
24. execution_results (FK → goap_plans)
|
||
25. executed_steps (FK → execution_results)
|
||
26. (qe_pattern_embeddings JSONL gap fix — already counted above)
|
||
```
|
||
|
||
### Milestone 2.3: Update Manifest and Checksum
|
||
|
||
Update the manifest format for v3.0:
|
||
|
||
```typescript
|
||
export interface BrainExportManifest_v3 {
|
||
readonly version: '3.0';
|
||
readonly format: 'jsonl' | 'rvf';
|
||
readonly exportedAt: string;
|
||
readonly sourceDb: string;
|
||
readonly stats: {
|
||
// Original 5
|
||
readonly patternCount: number;
|
||
readonly qValueCount: number;
|
||
readonly dreamInsightCount: number;
|
||
readonly witnessChainLength: number;
|
||
readonly embeddingCount: number;
|
||
// Tier 1 additions
|
||
readonly capturedExperienceCount: number;
|
||
readonly sonaPatternCount: number;
|
||
readonly trajectoryCount: number;
|
||
readonly trajectoryStepCount: number;
|
||
readonly conceptNodeCount: number;
|
||
readonly conceptEdgeCount: number;
|
||
readonly dreamCycleCount: number;
|
||
readonly goapActionCount: number;
|
||
readonly routingOutcomeCount: number;
|
||
// Tier 2 additions
|
||
readonly goapGoalCount: number;
|
||
readonly goapPlanCount: number;
|
||
readonly goapPlanSignatureCount: number;
|
||
readonly patternUsageCount: number;
|
||
readonly patternEmbeddingCount: number;
|
||
readonly patternEvolutionEventCount: number;
|
||
readonly patternRelationshipCount: number;
|
||
readonly patternVersionCount: number;
|
||
readonly vectorCount: number;
|
||
readonly experienceApplicationCount: number;
|
||
readonly executionResultCount: number;
|
||
readonly executedStepCount: number;
|
||
// Summary
|
||
readonly totalRecords: number;
|
||
readonly totalEmbeddings: number;
|
||
};
|
||
readonly domains: readonly string[];
|
||
readonly checksum: string;
|
||
readonly tableChecksums?: Record<string, string>; // Per-table integrity
|
||
}
|
||
```
|
||
|
||
Update the checksum computation to include all new JSONL files in deterministic order.
|
||
|
||
Maintain **backward compatibility**: when importing a v1.0 or v2.0 manifest, handle
|
||
missing tables gracefully (skip those that don't exist in the export).
|
||
|
||
### Success Criteria for Phase 2
|
||
|
||
- [ ] All 26 tables successfully export to both JSONL and RVF formats
|
||
- [ ] Round-trip test: export from populated DB, import into empty DB, verify row counts
|
||
match for all 26 tables
|
||
- [ ] BLOB columns (embeddings) survive round-trip with byte-level fidelity
|
||
- [ ] Domain filtering works for tables with `qe_domain` or `domain` columns
|
||
- [ ] Tables without domain columns export fully regardless of domain filter
|
||
- [ ] Import order respects FK constraints (parents before children)
|
||
- [ ] Backward compatibility: can still import v1.0 and v2.0 exports
|
||
- [ ] Memory usage stays bounded for large tables (concept_edges streaming)
|
||
- [ ] 30+ new tests covering new table export/import
|
||
- [ ] `npm run build` and `npm test -- --run` pass
|
||
|
||
**Estimated complexity:** High (many tables, BLOB handling, FK ordering)
|
||
**Estimated effort:** 8-12 hours
|
||
|
||
---
|
||
|
||
## Phase 3: Fix Embedding Restore Gap
|
||
|
||
**Goal:** Achieve 100% round-trip fidelity for all embedding vectors across all tables.
|
||
|
||
### Problem Analysis
|
||
|
||
Currently during RVF export:
|
||
1. `qe_pattern_embeddings` vectors are ingested into HNSW via `rvf.ingest()`
|
||
2. Full brain data (including patterns, Q-values, etc.) is stored as kernel JSON
|
||
3. On import, the kernel JSON is extracted and deserialized
|
||
4. But `rvf.ingest()` vectors are NOT extracted back -- `embeddingsRestored` is always 0
|
||
|
||
The fundamental issue: the RVF HNSW index is write-only during import. The native
|
||
binding supports `query()` (search) but there is no `extractAll()` bulk read.
|
||
|
||
### Solution Strategy
|
||
|
||
**Approach A (Recommended): Store embeddings in the kernel JSON alongside row data**
|
||
|
||
Instead of relying on HNSW extraction, include embedding BLOBs as Base64 strings
|
||
within the kernel JSON for each table that has embeddings. The HNSW ingest is kept
|
||
for semantic search within the RVF file, but import reads from the kernel.
|
||
|
||
For JSONL format, embeddings are already Base64-encoded per Phase 2.
|
||
|
||
```typescript
|
||
// During RVF export, for each table with embeddings:
|
||
const experiences = queryAll(db, 'captured_experiences');
|
||
for (const exp of experiences) {
|
||
if (exp.embedding) {
|
||
exp._embedding_b64 = Buffer.from(exp.embedding).toString('base64');
|
||
}
|
||
}
|
||
// Store in kernel JSON -- embeddings travel with the data
|
||
brainData.capturedExperiences = experiences;
|
||
|
||
// ALSO ingest into HNSW for semantic search capability within the RVF file:
|
||
const embeddingEntries = experiences
|
||
.filter(e => e.embedding && e.embedding_dimension === 384)
|
||
.map(e => ({
|
||
id: `exp:${e.id}`,
|
||
vector: new Float32Array(e.embedding.buffer, e.embedding.byteOffset, 384)
|
||
}));
|
||
rvf.ingest(embeddingEntries);
|
||
```
|
||
|
||
```typescript
|
||
// During RVF import:
|
||
for (const exp of brainData.capturedExperiences) {
|
||
if (exp._embedding_b64) {
|
||
exp.embedding = Buffer.from(exp._embedding_b64, 'base64');
|
||
delete exp._embedding_b64;
|
||
}
|
||
mergeExperience(db, exp, options.mergeStrategy);
|
||
}
|
||
```
|
||
|
||
### Milestone 3.1: BLOB Serialization Utilities
|
||
|
||
**New in `brain-shared.ts`:**
|
||
|
||
```typescript
|
||
/** Serialize a row for export, converting BLOB columns to Base64 */
|
||
export function serializeRowForExport(
|
||
row: Record<string, unknown>,
|
||
blobColumns: string[]
|
||
): Record<string, unknown> {
|
||
const result = { ...row };
|
||
for (const col of blobColumns) {
|
||
if (result[col] instanceof Buffer) {
|
||
result[`_${col}_b64`] = (result[col] as Buffer).toString('base64');
|
||
delete result[col];
|
||
}
|
||
}
|
||
return result;
|
||
}
|
||
|
||
/** Deserialize a row on import, converting Base64 back to Buffer */
|
||
export function deserializeRowForImport(
|
||
row: Record<string, unknown>,
|
||
blobColumns: string[]
|
||
): Record<string, unknown> {
|
||
const result = { ...row };
|
||
for (const col of blobColumns) {
|
||
const b64Key = `_${col}_b64`;
|
||
if (typeof result[b64Key] === 'string') {
|
||
result[col] = Buffer.from(result[b64Key] as string, 'base64');
|
||
delete result[b64Key];
|
||
}
|
||
}
|
||
return result;
|
||
}
|
||
```
|
||
|
||
### Milestone 3.2: Embedding Tables Round-Trip
|
||
|
||
**Tables with embedding BLOBs to handle:**
|
||
|
||
| Table | BLOB Columns | Dimension | Count |
|
||
|-------|-------------|-----------|-------|
|
||
| `qe_pattern_embeddings` | `embedding` | 384 | 40 |
|
||
| `captured_experiences` | `embedding` | 384 | 132 |
|
||
| `sona_patterns` | `state_embedding`, `action_embedding` | 384 | 739 |
|
||
| `pattern_versions` | `embedding` | variable | 8 |
|
||
| `concept_nodes` | `embedding` | variable | 0 (column exists but unpopulated) |
|
||
| `vectors` | `embedding` | variable | 410 |
|
||
|
||
For each table:
|
||
1. On export: serialize BLOBs to Base64 in both JSONL and kernel JSON
|
||
2. On import: deserialize Base64 back to Buffer, INSERT into SQLite
|
||
3. For RVF: additionally ingest 384d vectors into HNSW with namespaced IDs
|
||
(e.g., `exp:{id}`, `sona:{id}`, `pattern:{id}`)
|
||
|
||
### Milestone 3.3: Verify Fidelity
|
||
|
||
Write a dedicated round-trip fidelity test:
|
||
|
||
```typescript
|
||
it('should preserve embedding BLOBs through full round-trip', () => {
|
||
// Create source DB with known embedding values
|
||
const embedding = Buffer.alloc(384 * 4);
|
||
const floats = new Float32Array(embedding.buffer);
|
||
for (let i = 0; i < 384; i++) floats[i] = Math.random();
|
||
|
||
sourceDb.prepare(`
|
||
INSERT INTO qe_pattern_embeddings (pattern_id, embedding, dimension)
|
||
VALUES (?, ?, ?)
|
||
`).run('p1', embedding, 384);
|
||
|
||
// Export
|
||
const outDir = makeTempDir();
|
||
exportBrain(sourceDb, { outputPath: outDir });
|
||
|
||
// Import into empty DB
|
||
const targetDb = createTestDb();
|
||
const result = importBrain(targetDb, outDir, { mergeStrategy: 'skip-conflicts' });
|
||
|
||
// Verify embedding bytes match exactly
|
||
const row = targetDb.prepare(
|
||
'SELECT embedding FROM qe_pattern_embeddings WHERE pattern_id = ?'
|
||
).get('p1') as { embedding: Buffer };
|
||
|
||
expect(Buffer.compare(row.embedding, embedding)).toBe(0);
|
||
});
|
||
```
|
||
|
||
### Success Criteria for Phase 3
|
||
|
||
- [ ] `embeddingsRestored` count matches `embeddingCount` for RVF imports
|
||
- [ ] BLOB round-trip fidelity: `Buffer.compare(original, imported) === 0` for all
|
||
embedding columns across all 6 tables
|
||
- [ ] JSONL format correctly Base64-encodes/decodes BLOBs
|
||
- [ ] RVF format stores BLOBs in kernel AND ingests 384d vectors into HNSW
|
||
- [ ] Backward compatibility: importing v1.0/v2.0 exports (which lack embeddings)
|
||
still works with `embeddingsRestored: 0`
|
||
- [ ] 10+ new tests for BLOB serialization and round-trip fidelity
|
||
- [ ] `npm run build` and `npm test -- --run` pass
|
||
|
||
**Estimated complexity:** Medium-High (BLOB handling, dual storage in RVF)
|
||
**Estimated effort:** 6-8 hours
|
||
|
||
---
|
||
|
||
## Phase 4: Leverage RuVector Advanced Features
|
||
|
||
**Goal:** Use RVF capabilities for integrity, lineage, signing, and compaction.
|
||
|
||
### Milestone 4.1: Witness Chain Verification
|
||
|
||
**Current state:** `rvf-native-adapter.ts` exposes `status()` which checks for
|
||
witness segments, but does not call the native `verify_witness()` function.
|
||
|
||
**Change:** Add `verifyWitness()` to the adapter and call it during import to
|
||
validate RVF file integrity before deserializing the kernel.
|
||
|
||
```typescript
|
||
// rvf-native-adapter.ts additions
|
||
interface RvfNativeAdapter {
|
||
// ... existing methods ...
|
||
|
||
/** Verify the RVF witness chain (cryptographic integrity) */
|
||
verifyWitness(): { valid: boolean; entries: number; error?: string };
|
||
|
||
/** Get HNSW index statistics */
|
||
indexStats(): { dimension: number; vectorCount: number; levels: number; entryPoint: number };
|
||
}
|
||
```
|
||
|
||
```typescript
|
||
// brain-rvf-exporter.ts import path
|
||
const witnessResult = rvf.verifyWitness();
|
||
if (!witnessResult.valid) {
|
||
throw new Error(
|
||
`RVF witness chain verification failed: ${witnessResult.error}. ` +
|
||
'The file may have been tampered with or corrupted.'
|
||
);
|
||
}
|
||
```
|
||
|
||
### Milestone 4.2: Ed25519 Signed Exports
|
||
|
||
**Use case:** When sharing brain exports across teams, Ed25519 signing provides
|
||
non-repudiation and tamper detection beyond SHA-256 checksums.
|
||
|
||
```typescript
|
||
// brain-rvf-exporter.ts
|
||
export interface RvfBrainExportOptions {
|
||
// ... existing ...
|
||
readonly signing?: {
|
||
/** Ed25519 private key (32 bytes) for signing the export */
|
||
privateKey: Buffer;
|
||
/** Signer identifier (e.g., email or team name) */
|
||
signer: string;
|
||
};
|
||
}
|
||
|
||
// During export:
|
||
if (options.signing) {
|
||
const rvf = createRvfStore(outPath, dimension, {
|
||
signing: {
|
||
key: options.signing.privateKey,
|
||
signer: options.signing.signer,
|
||
},
|
||
});
|
||
}
|
||
```
|
||
|
||
This is **opt-in** -- unsigned exports continue to work as before.
|
||
|
||
### Milestone 4.3: Brain Lineage Tracking
|
||
|
||
**Use case:** Track the ancestry of brain exports (which brain was this derived from?).
|
||
|
||
```typescript
|
||
// brain-rvf-exporter.ts - kernel data additions
|
||
const brainData = {
|
||
version: '3.0',
|
||
format: 'rvf',
|
||
// ... existing fields ...
|
||
lineage: {
|
||
fileId: rvf.fileId(), // Unique ID for this RVF file
|
||
parentId: rvf.parentId(), // null for fresh exports, set for derived
|
||
lineageDepth: rvf.lineageDepth(), // 0 for root, N for Nth derivative
|
||
derivedAt: options.deriveFrom ? new Date().toISOString() : undefined,
|
||
},
|
||
};
|
||
```
|
||
|
||
**New CLI command:** `aqe brain derive`
|
||
|
||
```bash
|
||
# Create an incremental brain export derived from an existing one
|
||
aqe brain derive -i base-brain.rvf -o child-brain.rvf --db .agentic-qe/memory.db
|
||
```
|
||
|
||
Uses `rvf.derive(childPath)` for COW (copy-on-write) branching, then ingests only
|
||
the delta vectors and updates the kernel with new/changed data.
|
||
|
||
### Milestone 4.4: RVF Metadata Per Vector
|
||
|
||
**Use case:** Store structured metadata alongside each vector for filtered search.
|
||
|
||
Currently vectors are ingested as `{ id, vector }`. With `RvfMetadataEntry`, each
|
||
vector can carry metadata:
|
||
|
||
```typescript
|
||
const entries = embeddings.map(row => ({
|
||
id: row.pattern_id,
|
||
vector: vec,
|
||
metadata: {
|
||
table: 'qe_pattern_embeddings',
|
||
domain: patternDomainMap.get(row.pattern_id) ?? 'unknown',
|
||
confidence: patternConfidenceMap.get(row.pattern_id) ?? 0.5,
|
||
},
|
||
}));
|
||
|
||
rvf.ingest(entries);
|
||
|
||
// Later: filtered search within the brain
|
||
const results = rvf.search(queryVec, 10, {
|
||
filter: { table: 'sona_patterns', domain: 'test-generation' },
|
||
});
|
||
```
|
||
|
||
This enables `aqe brain search` -- semantic search within a brain export file.
|
||
|
||
### Milestone 4.5: Compact After Conflict Resolution
|
||
|
||
During import with `latest-wins` or `highest-confidence`, some vectors in the HNSW
|
||
index become stale (the row they reference was overwritten). Call `rvf.compact()`
|
||
after import to reclaim space:
|
||
|
||
```typescript
|
||
// At end of importBrainFromRvf, if we had any conflicts:
|
||
if (conflicts > 0 && !options.dryRun) {
|
||
rvf.compact();
|
||
}
|
||
```
|
||
|
||
### Success Criteria for Phase 4
|
||
|
||
- [ ] `verifyWitness()` called on import, throws on invalid files
|
||
- [ ] Signed exports can be verified (opt-in, non-breaking)
|
||
- [ ] `aqe brain info` displays lineage information when present
|
||
- [ ] Metadata stored per vector enables filtered search
|
||
- [ ] `compact()` called after conflict resolution
|
||
- [ ] All existing tests pass, 15+ new tests for advanced features
|
||
- [ ] Feature-gated behind `isRvfAvailable()` -- JSONL format unaffected
|
||
|
||
**Estimated complexity:** Medium (wrapping existing native capabilities)
|
||
**Estimated effort:** 6-8 hours
|
||
|
||
---
|
||
|
||
## Phase 5: Manifest v3.0, CLI Polish, and Documentation
|
||
|
||
**Goal:** Finalize the v3.0 manifest format, update CLI output, and ensure
|
||
backward compatibility.
|
||
|
||
### Milestone 5.1: Manifest Version Bump
|
||
|
||
- JSONL format: `version: '3.0'` (was `'1.0'`)
|
||
- RVF format: `version: '3.0'` (was `'2.0'`)
|
||
- Both formats share the same `BrainExportManifest_v3` type
|
||
|
||
**Backward-compatible import logic:**
|
||
|
||
```typescript
|
||
function importByVersion(manifest: unknown): void {
|
||
const version = (manifest as { version?: string }).version ?? '1.0';
|
||
|
||
switch (version) {
|
||
case '1.0':
|
||
// Original 5-table JSONL format
|
||
importV1(manifest);
|
||
break;
|
||
case '2.0':
|
||
// Original 5-table RVF format (kernel JSON)
|
||
importV2(manifest);
|
||
break;
|
||
case '3.0':
|
||
// Full 26-table format with embeddings
|
||
importV3(manifest);
|
||
break;
|
||
default:
|
||
throw new Error(`Unsupported brain export version: ${version}`);
|
||
}
|
||
}
|
||
```
|
||
|
||
### Milestone 5.2: CLI Output Improvements
|
||
|
||
Update `brain-handler.ts` to display comprehensive stats:
|
||
|
||
```
|
||
Brain Export Info
|
||
|
||
Version: 3.0
|
||
Format: rvf
|
||
Exported: 2026-03-08T14:22:00.000Z
|
||
Source DB: memory.db
|
||
|
||
Learning Data:
|
||
Patterns: 15,634
|
||
Experiences: 3,730
|
||
SONA Patterns: 1,025
|
||
Trajectories: 335 (2,030 steps)
|
||
Dream Insights: 3,940 (694 cycles)
|
||
Concept Graph: 4,731 nodes, 68,517 edges
|
||
GOAP: 2,325 actions, 53 goals, 101 plans
|
||
Q-Values: 8
|
||
Routing: 488 outcomes
|
||
|
||
Embeddings:
|
||
Total Vectors: 1,329
|
||
Dimensions: 384
|
||
|
||
Integrity:
|
||
Checksum: a1b2c3d4...
|
||
Witness Chain: 12,857 entries (verified)
|
||
Lineage: root (no parent)
|
||
Signed: no
|
||
|
||
RVF File:
|
||
Size: 12.4 MB
|
||
Segments: 47
|
||
HNSW Vectors: 1,329
|
||
|
||
Output: /path/to/brain.rvf
|
||
```
|
||
|
||
### Milestone 5.3: `aqe brain diff` Command (Optional)
|
||
|
||
Compare two brain exports to show what changed:
|
||
|
||
```bash
|
||
aqe brain diff -a brain-v1.rvf -b brain-v2.rvf
|
||
|
||
Brain Diff: brain-v1.rvf vs brain-v2.rvf
|
||
|
||
Patterns: +42 new, ~18 modified, -3 removed
|
||
Experiences: +120 new
|
||
SONA: +35 new, ~12 modified
|
||
...
|
||
```
|
||
|
||
### Milestone 5.4: Backward Compatibility Test Suite
|
||
|
||
Create a dedicated test file `tests/unit/brain-backward-compat.test.ts` that:
|
||
1. Creates v1.0 format exports (JSONL, 5 tables)
|
||
2. Creates v2.0 format exports (simulated RVF kernel with 5 tables)
|
||
3. Imports both into a fresh database
|
||
4. Verifies all data restored correctly
|
||
5. Verifies no errors on missing tables
|
||
|
||
### Success Criteria for Phase 5
|
||
|
||
- [ ] Manifest version is `'3.0'` for both JSONL and RVF
|
||
- [ ] v1.0 and v2.0 imports continue to work
|
||
- [ ] CLI displays all 26 table stats grouped logically
|
||
- [ ] `aqe brain info` works for v1.0, v2.0, and v3.0 exports
|
||
- [ ] Backward compatibility test suite with 10+ tests
|
||
- [ ] `npm run build` and `npm test -- --run` pass
|
||
- [ ] All files under 500 lines
|
||
|
||
**Estimated complexity:** Medium
|
||
**Estimated effort:** 4-6 hours
|
||
|
||
---
|
||
|
||
## Phase 6: Full ADR-070 Witness Chain Audit Compliance
|
||
|
||
**Goal:** Complete the ADR-070 vision — upgrade the witness chain from SHA-256 application
|
||
logging to SHAKE-256 + Ed25519 cryptographic audit trail with per-mutation provenance,
|
||
key management, and retroactive backfill.
|
||
|
||
### Current State
|
||
|
||
A `WitnessChain` class exists at `src/audit/witness-chain.ts` (384 lines) with:
|
||
- SHA-256 hash-chained append-only log in SQLite `witness_chain` table
|
||
- 12,857 entries recording `PATTERN_CREATE`, `PATTERN_UPDATE`, `PATTERN_PROMOTE`,
|
||
`PATTERN_QUARANTINE`, `DREAM_MERGE`, `DREAM_DISCARD`, `QUALITY_GATE_PASS/FAIL`,
|
||
`ROUTING_DECISION`
|
||
- `verify()` method that walks the full chain checking hash linkage
|
||
- `crossVerifyWithRvf()` for dual-chain comparison
|
||
- Integration into `QEReasoningBank` (create, update, promote) and
|
||
`QualityAssessmentCoordinator` (gate pass/fail)
|
||
|
||
**Gaps vs ADR-070:**
|
||
- Uses SHA-256; ADR-070 specifies SHAKE-256 (NIST SP 800-185)
|
||
- No Ed25519 signing on entries (no `signature` field)
|
||
- No key management (generation, rotation, revocation)
|
||
- Missing integration points: Dream Engine merge/discard, agent branch merges,
|
||
Hebbian penalty events (ADR-061), routing decisions
|
||
- No retroactive backfill for patterns created before witness chain existed
|
||
- No `getPatternLineage()` or `getActorHistory()` query methods
|
||
- No archival/compaction for old entries
|
||
|
||
### Milestone 6.1: Upgrade Hash Algorithm (SHA-256 → SHAKE-256)
|
||
|
||
**Affected files:**
|
||
- `src/audit/witness-chain.ts`
|
||
- `tests/unit/witness-chain.test.ts`
|
||
|
||
Replace `createHash('sha256')` with SHAKE-256 from `rvf-crypto` (via `@ruvector/rvf-node`)
|
||
or a pure-JS fallback. SHAKE-256 produces variable-length output; use 256-bit (32 bytes)
|
||
for backward compatibility with existing 64-char hex hashes.
|
||
|
||
```typescript
|
||
import { createHash } from 'crypto';
|
||
|
||
/**
|
||
* Compute SHAKE-256 (256-bit output) hex digest.
|
||
* Falls back to SHA-256 if SHAKE-256 is unavailable (Node <18).
|
||
*/
|
||
function shake256(data: string): string {
|
||
try {
|
||
// Node.js 18+ supports SHAKE via 'shake256' with XOF output length
|
||
const hash = createHash('shake256', { outputLength: 32 });
|
||
return hash.update(data, 'utf-8').digest('hex');
|
||
} catch {
|
||
// Fallback: SHA-256 (same output length, different algorithm)
|
||
return createHash('sha256').update(data, 'utf-8').digest('hex');
|
||
}
|
||
}
|
||
```
|
||
|
||
**Migration strategy:** New entries use SHAKE-256. The `verify()` method detects the
|
||
algorithm boundary: entries created before the upgrade have SHA-256 hashes, entries
|
||
after use SHAKE-256. The `prev_hash` of the first SHAKE-256 entry is the SHAKE-256 of
|
||
the last SHA-256 entry — this creates a one-time bridge.
|
||
|
||
Store the hash algorithm in a new `hash_algo` column (default `'sha256'`, new entries
|
||
use `'shake256'`). The verifier checks each entry's algorithm to use the correct hash.
|
||
|
||
### Milestone 6.2: Ed25519 Signing
|
||
|
||
**Affected files:**
|
||
- NEW: `src/audit/witness-key-manager.ts` (~150 lines)
|
||
- `src/audit/witness-chain.ts`
|
||
- `tests/unit/witness-key-manager.test.ts`
|
||
- `tests/unit/witness-chain.test.ts`
|
||
|
||
**Schema addition:**
|
||
|
||
```sql
|
||
ALTER TABLE witness_chain ADD COLUMN signature TEXT; -- Ed25519 sig (128 hex chars)
|
||
ALTER TABLE witness_chain ADD COLUMN signer_key_id TEXT; -- Key ID that signed this entry
|
||
ALTER TABLE witness_chain ADD COLUMN hash_algo TEXT DEFAULT 'sha256';
|
||
```
|
||
|
||
**Key management** (`src/audit/witness-key-manager.ts`):
|
||
|
||
```typescript
|
||
import { generateKeyPairSync, sign, verify, createHash } from 'crypto';
|
||
|
||
export interface WitnessKeyManager {
|
||
/** Generate a new Ed25519 keypair, store in .agentic-qe/witness-keys/ */
|
||
generateKeyPair(): { keyId: string; publicKey: Buffer };
|
||
|
||
/** Sign a witness entry payload */
|
||
sign(data: Buffer, keyId?: string): { signature: Buffer; keyId: string };
|
||
|
||
/** Verify a signature against a public key */
|
||
verify(data: Buffer, signature: Buffer, keyId: string): boolean;
|
||
|
||
/** Rotate the active signing key */
|
||
rotateKey(): { oldKeyId: string; newKeyId: string };
|
||
|
||
/** Get the active key ID */
|
||
getActiveKeyId(): string;
|
||
}
|
||
```
|
||
|
||
- Keys stored in `.agentic-qe/witness-keys/` as PEM files
|
||
- Key rotation records a `KEY_ROTATION` entry in the witness chain itself
|
||
- Node.js `crypto.generateKeyPairSync('ed25519')` — no native dependency needed
|
||
- Signing is **opt-in** via `AQE_WITNESS_SIGNING=true` env var or config flag
|
||
- Unsigned entries remain valid (signature field is NULL)
|
||
|
||
**Updated `append()` flow:**
|
||
|
||
```typescript
|
||
append(actionType, actionData, actor): WitnessEntry {
|
||
const actionDataStr = JSON.stringify(actionData);
|
||
const actionHash = shake256(actionDataStr);
|
||
const prevHash = lastEntry ? shake256(serializeEntry(lastEntry)) : GENESIS_PREV_HASH;
|
||
|
||
// Sign if key manager is available and signing is enabled
|
||
let signature: string | null = null;
|
||
let signerKeyId: string | null = null;
|
||
if (this.keyManager) {
|
||
const payload = Buffer.from(`${prevHash}:${actionHash}:${actionType}:${actor}`);
|
||
const result = this.keyManager.sign(payload);
|
||
signature = result.signature.toString('hex');
|
||
signerKeyId = result.keyId;
|
||
}
|
||
|
||
// INSERT with new columns
|
||
db.prepare(`
|
||
INSERT INTO witness_chain
|
||
(prev_hash, action_hash, action_type, action_data, timestamp, actor,
|
||
signature, signer_key_id, hash_algo)
|
||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||
`).run(prevHash, actionHash, actionType, actionDataStr, timestamp, actor,
|
||
signature, signerKeyId, 'shake256');
|
||
}
|
||
```
|
||
|
||
### Milestone 6.3: Expand Integration Points
|
||
|
||
**Affected files:**
|
||
- `src/learning/qe-reasoning-bank.ts` — add `PATTERN_QUARANTINE` recording
|
||
- `src/learning/real-qe-reasoning-bank.ts` — quarantine logic at line ~787-790
|
||
- `src/learning/pattern-lifecycle.ts` — `quarantinePattern()` at line ~773
|
||
- `src/learning/dream/dream-engine.ts` — add `DREAM_MERGE`, `DREAM_DISCARD` recording (lines ~516-520)
|
||
- `src/learning/dream/rvcow-branch-manager.ts` — add `BRANCH_MERGE` recording (line ~294)
|
||
- `src/learning/asymmetric-learning.ts` — add `HEBBIAN_PENALTY` recording
|
||
- `src/routing/qe-task-router.ts` — add `ROUTING_DECISION` recording
|
||
(**Note:** `src/optimization/model-router.ts` does NOT exist. The actual routing
|
||
code is in `src/routing/qe-task-router.ts`, `src/shared/llm/router/hybrid-router.ts`,
|
||
and `src/integrations/agentic-flow/model-router/router.ts`. The `qe-task-router.ts`
|
||
is the primary integration point for QE routing decisions.)
|
||
- `src/integrations/coherence/engines/witness-adapter.ts` — verify compatibility with schema changes
|
||
- `src/domains/quality-assessment/coordinator.ts` — already integrated (pass/fail)
|
||
|
||
Currently, only 3 of the 5 integration points specified by ADR-070 are wired:
|
||
|
||
| Integration Point | Status | Action Type |
|
||
|------------------|--------|-------------|
|
||
| ReasoningBank: create | Done | `PATTERN_CREATE` |
|
||
| ReasoningBank: update | Done | `PATTERN_UPDATE` |
|
||
| ReasoningBank: promote | Done | `PATTERN_PROMOTE` |
|
||
| ReasoningBank: quarantine | **Missing** | `PATTERN_QUARANTINE` |
|
||
| Quality gates: pass/fail | Done | `QUALITY_GATE_PASS/FAIL` |
|
||
| Dream Engine: merge | **Missing** | `DREAM_MERGE` |
|
||
| Dream Engine: discard | **Missing** | `DREAM_DISCARD` |
|
||
| Agent branches: merge | **Missing** | Needs new `BRANCH_MERGE` type |
|
||
| Hebbian penalty (ADR-061) | **Missing** | Needs new `HEBBIAN_PENALTY` type |
|
||
| Routing decisions | **Missing** | `ROUTING_DECISION` |
|
||
|
||
Add the missing integrations. Each is a 3-5 line addition at the mutation site:
|
||
|
||
```typescript
|
||
// Example: Dream Engine merge
|
||
const wc = await getWitnessChain();
|
||
wc.append('DREAM_MERGE', {
|
||
cycleId: dreamCycle.id,
|
||
patternsConsolidated: mergedPatternIds,
|
||
insightsGenerated: insights.length,
|
||
}, `dream-engine:${dreamCycle.id}`);
|
||
```
|
||
|
||
Add two new action types to `WitnessActionType`:
|
||
|
||
```typescript
|
||
export type WitnessActionType =
|
||
| 'PATTERN_CREATE' | 'PATTERN_UPDATE' | 'PATTERN_PROMOTE' | 'PATTERN_QUARANTINE'
|
||
| 'DREAM_MERGE' | 'DREAM_DISCARD'
|
||
| 'QUALITY_GATE_PASS' | 'QUALITY_GATE_FAIL'
|
||
| 'ROUTING_DECISION'
|
||
| 'BRANCH_MERGE' // NEW: agent branch merge-back
|
||
| 'HEBBIAN_PENALTY' // NEW: asymmetric learning penalty event
|
||
| 'KEY_ROTATION'; // NEW: witness key rotation
|
||
```
|
||
|
||
### Milestone 6.4: Query Methods
|
||
|
||
**Affected files:**
|
||
- `src/audit/witness-chain.ts`
|
||
|
||
Add the ADR-070 specified query methods:
|
||
|
||
```typescript
|
||
/** Get all witness entries for a specific pattern (lineage view) */
|
||
getPatternLineage(patternId: string): WitnessEntry[] {
|
||
// Use json_extract for indexed lookup instead of LIKE scan
|
||
return this.db.prepare(
|
||
`SELECT * FROM witness_chain
|
||
WHERE json_extract(action_data, '$.patternId') = ?
|
||
OR json_extract(action_data, '$.pattern_id') = ?
|
||
ORDER BY id ASC`
|
||
).all(patternId, patternId);
|
||
}
|
||
|
||
/** Get all actions by a specific actor */
|
||
getActorHistory(actorId: string, since?: string): WitnessEntry[] {
|
||
if (since) {
|
||
return this.db.prepare(
|
||
'SELECT * FROM witness_chain WHERE actor = ? AND timestamp >= ? ORDER BY id ASC'
|
||
).all(actorId, since);
|
||
}
|
||
return this.db.prepare(
|
||
'SELECT * FROM witness_chain WHERE actor = ? ORDER BY id ASC'
|
||
).all(actorId);
|
||
}
|
||
```
|
||
|
||
### Milestone 6.5: Retroactive Backfill
|
||
|
||
**Affected files:**
|
||
- NEW: `src/audit/witness-backfill.ts` (~100 lines)
|
||
- NEW: CLI command `aqe witness backfill`
|
||
|
||
For patterns that existed before the witness chain was introduced, create genesis entries:
|
||
|
||
```typescript
|
||
export async function backfillWitnessChain(
|
||
db: Database.Database,
|
||
witnessChain: WitnessChain
|
||
): Promise<{ created: number; skipped: number }> {
|
||
// Step 1: Build a set of pattern IDs that already have PATTERN_CREATE entries.
|
||
// Uses json_extract for indexed lookup instead of LIKE scan (~200x faster).
|
||
db.exec(`
|
||
CREATE TEMP TABLE IF NOT EXISTS _backfill_existing AS
|
||
SELECT DISTINCT json_extract(action_data, '$.patternId') as pid
|
||
FROM witness_chain
|
||
WHERE action_type = 'PATTERN_CREATE'
|
||
AND json_extract(action_data, '$.patternId') IS NOT NULL
|
||
`);
|
||
|
||
// Step 2: LEFT JOIN to find patterns missing witness entries
|
||
const patternsWithoutWitness = db.prepare(`
|
||
SELECT p.id, p.qe_domain, p.confidence, p.name, p.created_at
|
||
FROM qe_patterns p
|
||
LEFT JOIN _backfill_existing e ON e.pid = p.id
|
||
WHERE e.pid IS NULL
|
||
ORDER BY p.created_at ASC
|
||
`).all() as Array<{ id: string; qe_domain: string; confidence: number; name: string; created_at: string }>;
|
||
|
||
db.exec('DROP TABLE IF EXISTS _backfill_existing');
|
||
|
||
let created = 0;
|
||
for (const pattern of patternsWithoutWitness) {
|
||
witnessChain.append('PATTERN_CREATE', {
|
||
patternId: pattern.id,
|
||
domain: pattern.qe_domain,
|
||
confidence: pattern.confidence,
|
||
name: pattern.name,
|
||
backfilled: true,
|
||
originalCreatedAt: pattern.created_at,
|
||
}, 'system:backfill');
|
||
created++;
|
||
}
|
||
|
||
return { created, skipped: patternsWithoutWitness.length === 0 ? 0 : 0 };
|
||
}
|
||
```
|
||
|
||
Run via CLI: `aqe witness backfill [--db <path>]`
|
||
|
||
**Safety:** Backfill only creates `PATTERN_CREATE` entries for patterns that have no
|
||
existing witness entry. Idempotent — running twice creates zero new entries.
|
||
|
||
### Milestone 6.6: Archival / Compaction
|
||
|
||
**Affected files:**
|
||
- `src/audit/witness-chain.ts`
|
||
|
||
The chain grows monotonically. For databases with 100K+ patterns, the chain could reach
|
||
millions of entries. Add an archival method:
|
||
|
||
```typescript
|
||
/** Archive entries older than cutoff to a separate table */
|
||
archiveEntries(olderThan: string): { archived: number } {
|
||
// Move old entries to witness_chain_archive (same schema)
|
||
this.db.exec(`
|
||
CREATE TABLE IF NOT EXISTS witness_chain_archive AS
|
||
SELECT * FROM witness_chain WHERE 0
|
||
`);
|
||
|
||
const result = this.db.prepare(`
|
||
INSERT INTO witness_chain_archive SELECT * FROM witness_chain
|
||
WHERE timestamp < ? AND id > 1
|
||
`).run(olderThan);
|
||
|
||
this.db.prepare(`
|
||
DELETE FROM witness_chain WHERE timestamp < ? AND id > 1
|
||
`).run(olderThan);
|
||
|
||
return { archived: result.changes };
|
||
}
|
||
```
|
||
|
||
**Note:** The genesis entry (id=1) is never archived — it anchors the chain.
|
||
|
||
### Milestone 6.7: Enhanced Verification with Signature Checking
|
||
|
||
**Affected files:**
|
||
- `src/audit/witness-chain.ts`
|
||
|
||
Upgrade `verify()` to also check Ed25519 signatures when present:
|
||
|
||
```typescript
|
||
verify(options?: { checkSignatures?: boolean }): VerifyResult {
|
||
// ... existing hash chain verification ...
|
||
|
||
// Additionally verify signatures if requested
|
||
if (options?.checkSignatures && this.keyManager) {
|
||
for (const entry of entries) {
|
||
if (entry.signature && entry.signer_key_id) {
|
||
const payload = Buffer.from(
|
||
`${entry.prev_hash}:${entry.action_hash}:${entry.action_type}:${entry.actor}`
|
||
);
|
||
const sigBuffer = Buffer.from(entry.signature, 'hex');
|
||
if (!this.keyManager.verify(payload, sigBuffer, entry.signer_key_id)) {
|
||
return {
|
||
valid: false,
|
||
brokenAt: entry.id,
|
||
entriesChecked: i + 1,
|
||
signatureInvalid: true,
|
||
};
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
### Success Criteria for Phase 6
|
||
|
||
- [ ] New entries use SHAKE-256 hashing (with SHA-256 fallback for Node <18)
|
||
- [ ] Ed25519 signing works when enabled via `AQE_WITNESS_SIGNING=true`
|
||
- [ ] Key generation, rotation, and verification work end-to-end
|
||
- [ ] All 11 action types have integration points wired
|
||
- [ ] `getPatternLineage(patternId)` returns chronological mutation history
|
||
- [ ] `getActorHistory(actorId)` returns all actions by that actor
|
||
- [ ] Backfill creates genesis entries for patterns missing witness records
|
||
- [ ] Backfill is idempotent (safe to run multiple times)
|
||
- [ ] Archival moves old entries without breaking chain verification
|
||
- [ ] `verify({ checkSignatures: true })` validates Ed25519 sigs
|
||
- [ ] Existing 12,857 SHA-256 entries remain valid after upgrade
|
||
- [ ] Brain export includes the new columns (`signature`, `signer_key_id`, `hash_algo`)
|
||
- [ ] 25+ new tests covering crypto, key management, backfill, and archival
|
||
- [ ] `npm run build` and `npm test -- --run` pass
|
||
|
||
**Estimated complexity:** High (cryptography, schema migration, 6+ integration points)
|
||
**Estimated effort:** 10-14 hours
|
||
|
||
---
|
||
|
||
## Implementation Order and Dependencies
|
||
|
||
```
|
||
Phase 1 (Shared Code)
|
||
|
|
||
v
|
||
Phase 2 (Missing Tables) ----> Phase 3 (Embedding Restore)
|
||
| |
|
||
v v
|
||
Phase 4 (RuVector Features) Phase 5 (Manifest v3, CLI)
|
||
|
|
||
v
|
||
Phase 6 (ADR-070 Witness Chain)
|
||
```
|
||
|
||
Phases 2 and 3 can be worked in parallel after Phase 1 completes.
|
||
Phase 4 and Phase 5 can be worked in parallel after Phases 2 and 3.
|
||
Phase 6 depends on Phase 5 (manifest v3.0 must include new witness columns)
|
||
and Phase 4.1 (verify_witness() in RVF adapter). Phase 6 milestones 6.1-6.7
|
||
are sequential within the phase.
|
||
|
||
## Total Estimated Effort
|
||
|
||
| Phase | Hours | Risk |
|
||
|-------|-------|------|
|
||
| Phase 1: Shared Code | 4-6 | Low |
|
||
| Phase 2: Missing Tables (21 new → 26 total) | 9-13 | Medium (FK ordering, kernel size, large tables) |
|
||
| Phase 3: Embedding Restore | 6-8 | Medium (BLOB handling) |
|
||
| Phase 4: RuVector Features | 6-8 | Low (wrapping existing APIs) |
|
||
| Phase 5: Manifest v3.0, CLI | 4-6 | Low |
|
||
| Phase 6: ADR-070 Witness Chain | 10-14 | High (crypto, schema migration, 8 integration files) |
|
||
| **Total** | **39-55** | |
|
||
|
||
## Risk Mitigation
|
||
|
||
1. **Large table memory pressure** (`concept_edges` with 68K rows):
|
||
Use streaming JSONL writes and chunked kernel JSON construction.
|
||
Test with 100K synthetic rows to validate memory bounds.
|
||
|
||
2. **BLOB serialization size inflation** (Base64 is 33% larger):
|
||
For JSONL: acceptable, compression recommended for transfer.
|
||
For RVF: BLOBs go in kernel (not HNSW), so no HNSW size impact.
|
||
|
||
3. **FK ordering during import** (8 FK chains across 26 tables):
|
||
Define import order explicitly in the table config array (see Milestone 2.2).
|
||
Wrap full import in a single SQLite transaction.
|
||
Key chains: `dream_cycles` → `dream_insights`, `qe_trajectories` → `trajectory_steps`,
|
||
`concept_nodes` → `concept_edges`, `goap_goals` → `goap_plans` → `goap_plan_signatures`,
|
||
`captured_experiences` → `experience_applications`, `goap_plans` → `execution_results`
|
||
→ `executed_steps`.
|
||
|
||
4. **Native binding unavailability** (`@ruvector/rvf-node` optional):
|
||
All Phase 4 features are gated behind `isRvfAvailable()`.
|
||
JSONL format always works as fallback.
|
||
|
||
5. **Backward compatibility regression**:
|
||
Dedicated test suite (Phase 5.4) with frozen v1.0/v2.0 test fixtures.
|
||
|
||
6. **SHAKE-256 availability** (Phase 6):
|
||
Node.js 18+ supports `shake256` in `crypto.createHash()`. For older runtimes,
|
||
fall back to SHA-256 with a logged warning. The `hash_algo` column tracks which
|
||
algorithm was used per entry, so verification works across the boundary.
|
||
|
||
7. **Witness chain schema migration** (Phase 6):
|
||
Three new nullable columns (`signature`, `signer_key_id`, `hash_algo`) are added
|
||
via `ALTER TABLE ADD COLUMN` — SQLite supports this without data migration.
|
||
Existing entries have NULL for new columns, which is the expected state.
|
||
|
||
8. **Backfill performance** (Phase 6.5):
|
||
15,634 patterns need genesis entries. At ~0.2ms per insert (hash + write),
|
||
this takes ~3 seconds. Run inside a single transaction for atomicity.
|
||
Show a progress bar in the CLI for user feedback.
|
||
|
||
9. **Kernel JSON size for RVF format** (Phase 2):
|
||
With 26 tables, the kernel JSON could exceed 25MB (`concept_edges` alone ≈ 14MB,
|
||
`qe_patterns` ≈ 8MB). Mitigation: for tables with >10K rows, use chunked kernel
|
||
encoding — split into multiple KERNEL_SEG segments keyed by table name, or
|
||
compress the kernel JSON with gzip before `embedKernel()`. Alternatively, set a
|
||
per-table row limit for the kernel and fall back to streaming JSONL within the RVF
|
||
for oversized tables. Test with actual `embedKernel()` to determine buffer limits.
|
||
|
||
10. **Import rollback on partial failure** (Phase 2/3):
|
||
SQLite transaction handles SQLite-side atomicity, but RVF HNSW ingest has no
|
||
rollback. Strategy: perform all SQLite inserts first (inside transaction), commit,
|
||
then ingest into HNSW as a best-effort step. If HNSW fails, SQLite data is still
|
||
intact and HNSW can be rebuilt. Log a warning, do not fail the import.
|
||
|
||
11. **Backfill query performance** (Phase 6.5):
|
||
The `NOT EXISTS` with `LIKE '%"patternId":"..."'%'` scans witness_chain (12K rows)
|
||
per pattern (15K patterns) = ~200M string comparisons. Use a set-based approach
|
||
instead: extract all existing PATTERN_CREATE pattern IDs into a temp table first,
|
||
then LEFT JOIN to find gaps. See revised query in Milestone 6.5.
|
||
|
||
12. **`witness-adapter.ts` compatibility** (Phase 6):
|
||
`src/integrations/coherence/engines/witness-adapter.ts` wraps the witness chain.
|
||
When schema changes (new columns, SHAKE-256), verify the adapter still works.
|
||
Add it to Phase 6.3 affected files list.
|
||
|
||
## Testing Strategy
|
||
|
||
| Test Type | Count | Focus |
|
||
|-----------|-------|-------|
|
||
| Unit (brain-shared) | 20+ | Merge logic, BLOB serialization, DDL, FK ordering |
|
||
| Unit (brain-exporter) | 18+ | JSONL export/import for all 26 tables |
|
||
| Unit (brain-rvf-exporter) | 12+ | RVF export/import, embedding round-trip, kernel size |
|
||
| Integration (round-trip) | 12+ | Full export-import cycle, fidelity checks |
|
||
| Backward compat | 10+ | v1.0/v2.0 import into v3.0 |
|
||
| Edge cases | 10+ | Empty DB, missing tables, corrupted data, partial failure |
|
||
| Unit (witness-chain) | 15+ | SHAKE-256, Ed25519 sign/verify, hash algo boundary |
|
||
| Unit (witness-key-manager) | 10+ | Key generation, rotation, PEM storage |
|
||
| Integration (witness) | 8+ | Backfill, archival, cross-verify with RVF, witness-adapter |
|
||
| **Total new tests** | **115+** | |
|
||
|
||
## Files Modified Summary
|
||
|
||
| File | Action | Lines Delta |
|
||
|------|--------|-------------|
|
||
| `src/integrations/ruvector/brain-shared.ts` | NEW | +350 |
|
||
| `src/integrations/ruvector/brain-exporter.ts` | MODIFY | -200, +100 |
|
||
| `src/integrations/ruvector/brain-rvf-exporter.ts` | MODIFY | -200, +150 |
|
||
| `src/integrations/ruvector/rvf-native-adapter.ts` | MODIFY | +40 |
|
||
| `src/cli/brain-commands.ts` | MODIFY | +20 |
|
||
| `src/cli/handlers/brain-handler.ts` | MODIFY | +60 |
|
||
| `src/audit/witness-chain.ts` | MODIFY | +120 |
|
||
| `src/audit/witness-key-manager.ts` | NEW | +150 |
|
||
| `src/audit/witness-backfill.ts` | NEW | +100 |
|
||
| `src/learning/qe-reasoning-bank.ts` | MODIFY | +5 |
|
||
| `src/learning/real-qe-reasoning-bank.ts` | MODIFY | +10 |
|
||
| `src/learning/pattern-lifecycle.ts` | MODIFY | +5 |
|
||
| `src/learning/dream/dream-engine.ts` | MODIFY | +15 |
|
||
| `src/learning/dream/rvcow-branch-manager.ts` | MODIFY | +10 |
|
||
| `src/learning/asymmetric-learning.ts` | MODIFY | +10 |
|
||
| `src/routing/qe-task-router.ts` | MODIFY | +10 |
|
||
| `src/integrations/coherence/engines/witness-adapter.ts` | MODIFY | +5 |
|
||
| `tests/unit/brain-shared.test.ts` | NEW | +300 |
|
||
| `tests/unit/brain-exporter.test.ts` | MODIFY | +200 |
|
||
| `tests/unit/brain-backward-compat.test.ts` | NEW | +150 |
|
||
| `tests/unit/witness-chain.test.ts` | MODIFY | +200 |
|
||
| `tests/unit/witness-key-manager.test.ts` | NEW | +150 |
|
||
| **Net** | | **+1,255** |
|
||
|
||
---
|
||
|
||
## Appendix A: RuVector v2.0.5 Feature Inventory
|
||
|
||
Research conducted 2026-03-08 against https://github.com/ruvnet/ruvector (v2.0.5, 2026-02-26).
|
||
|
||
### RVF Format — 24 Segment Types
|
||
|
||
The `.rvf` file is an append-only container with 24 segment types:
|
||
|
||
| Segment | ID | Purpose | AQE Relevance |
|
||
|---------|-----|---------|---------------|
|
||
| `MANIFEST_SEG` | 0x01 | 4KB root manifest, two-phase boot | Core — always present |
|
||
| `VEC_SEG` | 0x02 | Vector embeddings (fp16/fp32/int8/binary) | **HIGH** — brain embeddings |
|
||
| `INDEX_SEG` | 0x03 | Progressive 3-layer HNSW (A/B/C) | **HIGH** — semantic search |
|
||
| `META_SEG` | 0x04 | Key-value metadata per vector | **HIGH** — domain/type tags |
|
||
| `META_IDX_SEG` | 0x05 | Filtered search indexes | **MEDIUM** — filtered brain search |
|
||
| `OVERLAY_SEG` | 0x06 | LoRA adapter deltas | Future — SONA fine-tuning |
|
||
| `GRAPH_SEG` | 0x07 | GNN state | Future — concept graph in RVF |
|
||
| `QUANT_SEG` | 0x08 | Quantization codebooks | **MEDIUM** — compressed embeddings |
|
||
| `WITNESS_SEG` | 0x0A | Tamper-evident audit chain (SHAKE-256) | **HIGH** — integrity verification |
|
||
| `CRYPTO_SEG` | 0x0C | ML-DSA-65/Ed25519 signatures | **HIGH** — signed exports |
|
||
| `KERNEL_SEG` | 0x0E | Embedded Linux microkernel | Already used for brain data |
|
||
| `EBPF_SEG` | 0x0F | XDP/TC programs | Low — not needed for brain |
|
||
| `WASM_SEG` | 0x10 | Query microkernel (5.5KB) | Future — browser brain search |
|
||
| `COW_MAP_SEG` | 0x20 | Cluster ownership for COW branches | **HIGH** — incremental exports |
|
||
| `REFCOUNT_SEG` | 0x21 | Reference counting | Internal |
|
||
| `MEMBERSHIP_SEG` | 0x22 | Vector visibility filters | **MEDIUM** — domain scoping |
|
||
| `DELTA_SEG` | 0x23 | Sparse patch deltas | Future — incremental updates |
|
||
| `TRANSFER_PRIOR` | 0x30 | Transfer learning state | Future — cross-project learning |
|
||
| `POLICY_KERNEL` | 0x31 | Thompson Sampling state | Future — solver integration |
|
||
| `COST_CURVE` | 0x32 | Solver cost/reward curves | Future — solver integration |
|
||
| `PROFILE_SEG` | — | Domain configuration | **MEDIUM** — brain profiles |
|
||
| `HOT_SEG` | — | Temperature-promoted hot data | Low — runtime optimization |
|
||
| `SKETCH_SEG` | — | VQE/quantum state | Low — quantum simulation |
|
||
| `DASHBOARD_SEG` | — | Embedded UI bundle | Future — brain viewer |
|
||
|
||
### rvf-node N-API — Full API (v0.1.4)
|
||
|
||
```typescript
|
||
class RvfDatabase {
|
||
// Factory
|
||
static create(path: string, options: RvfOptions): RvfDatabase
|
||
static open(path: string): RvfDatabase
|
||
static open_readonly(path: string): RvfDatabase
|
||
|
||
// Data operations
|
||
ingest_batch(vectors: Float32Array, ids: i64[], metadata?: RvfMetadataEntry[]): RvfIngestResult
|
||
query(vector: Float32Array, k: u32, options?: RvfQueryOptions): RvfSearchResult[]
|
||
delete(ids: i64[]): RvfDeleteResult
|
||
delete_by_filter(filter_json: string): RvfDeleteResult
|
||
compact(): RvfCompactionResult
|
||
|
||
// Kernel/eBPF embedding
|
||
embed_kernel(arch, type, flags, image: Buffer, port, cmdline?): i64
|
||
extract_kernel(): { header: Buffer, image: Buffer } | null
|
||
embed_ebpf(type, attach, dim, bytecode: Buffer, btf?): i64
|
||
extract_ebpf(): { header: Buffer, payload: Buffer } | null
|
||
|
||
// Lineage
|
||
file_id(): string
|
||
parent_id(): string
|
||
lineage_depth(): u32
|
||
derive(child_path: string, options?: RvfOptions): RvfDatabase
|
||
|
||
// Inspection
|
||
segments(): RvfSegmentInfo[]
|
||
dimension(): u32
|
||
metric(): string
|
||
index_stats(): RvfIndexStats
|
||
status(): RvfStatus
|
||
verify_witness(): RvfWitnessResult
|
||
|
||
// Lifecycle
|
||
freeze(): u32
|
||
close(): void
|
||
}
|
||
|
||
interface RvfOptions {
|
||
dimension: u32;
|
||
metric?: string; // "l2" | "cosine" | "ip"
|
||
profile?: u32; // 0-3 hardware profile
|
||
compression?: string; // "none" | "scalar" | "product"
|
||
signing?: boolean; // Enable Ed25519 segment signing
|
||
m?: u32; // HNSW max edges (default 16)
|
||
ef_construction?: u32; // HNSW beam width (default 200)
|
||
}
|
||
|
||
interface RvfQueryOptions {
|
||
ef_search?: u32;
|
||
filter?: string; // JSON metadata filter
|
||
timeout_ms?: u32;
|
||
}
|
||
|
||
interface RvfMetadataEntry {
|
||
field_id: u32;
|
||
value_type: string;
|
||
value: string;
|
||
}
|
||
|
||
interface RvfIngestResult { accepted: i64; rejected: i64; epoch: u32 }
|
||
interface RvfSearchResult { id: i64; distance: f64 }
|
||
interface RvfDeleteResult { deleted: i64; epoch: u32 }
|
||
interface RvfCompactionResult { segments_compacted: u32; bytes_reclaimed: i64; epoch: u32 }
|
||
interface RvfStatus {
|
||
total_vectors: i64; total_segments: u32; file_size: i64;
|
||
current_epoch: u32; profile_id: u32; compaction_state: string;
|
||
dead_space_ratio: f64; read_only: boolean;
|
||
}
|
||
interface RvfIndexStats {
|
||
indexed_vectors: i64; layers: u32; m: u32;
|
||
ef_construction: u32; needs_rebuild: boolean;
|
||
}
|
||
interface RvfWitnessResult { valid: boolean; entries: u32; error?: string }
|
||
interface RvfSegmentInfo { id: i64; offset: i64; payload_length: i64; seg_type: string }
|
||
```
|
||
|
||
### Currently Used vs Available in rvf-native-adapter.ts
|
||
|
||
| Method | Used? | Phase to Add |
|
||
|--------|-------|-------------|
|
||
| `create()` / `open()` / `open_readonly()` | Yes | — |
|
||
| `ingest_batch()` (via `ingestBatch`) | Yes | — |
|
||
| `query()` | Yes (as search) | — |
|
||
| `status()` | Yes | — |
|
||
| `segments()` | Yes | — |
|
||
| `dimension()` | Yes | — |
|
||
| `embed_kernel()` | Yes | — |
|
||
| `extract_kernel()` | Yes | — |
|
||
| `close()` | Yes | — |
|
||
| `compact()` | Adapter method exists but unused | Phase 4.5 |
|
||
| `delete()` | Adapter method exists but unused | — |
|
||
| `fork()` (COW copy) | Adapter method exists but unused | Phase 4.3 |
|
||
| **`verify_witness()`** | **NOT exposed** | **Phase 4.1** |
|
||
| **`index_stats()`** | **NOT exposed** | **Phase 4.1** |
|
||
| **`freeze()`** | **NOT exposed** | **Phase 4.2** |
|
||
| **`derive()`** | **NOT exposed** | **Phase 4.3** |
|
||
| **`file_id()`** | **NOT exposed** | **Phase 4.3** |
|
||
| **`parent_id()`** | **NOT exposed** | **Phase 4.3** |
|
||
| **`lineage_depth()`** | **NOT exposed** | **Phase 4.3** |
|
||
| **`metric()`** | **NOT exposed** | Nice-to-have |
|
||
| **`embed_ebpf()`** | **NOT exposed** | Future |
|
||
| **`extract_ebpf()`** | **NOT exposed** | Future |
|
||
| **`delete_by_filter()`** | **NOT exposed** | Future |
|
||
| **`RvfMetadataEntry`** on ingest | **NOT used** | **Phase 4.4** |
|
||
| **`RvfQueryOptions.filter`** | **NOT used** | **Phase 4.4** |
|
||
| **`RvfOptions.signing`** | **NOT used** | **Phase 4.2** |
|
||
| **`RvfOptions.compression`** | **NOT used** | Future (scalar/product quant) |
|
||
|
||
### Other RuVector Crates of Interest
|
||
|
||
| Crate | Relevance to AQE Brain |
|
||
|-------|----------------------|
|
||
| `rvf-crypto` | `create_witness_chain()`, `verify_witness_chain()`, Ed25519 signing, SHAKE-256 — integrity |
|
||
| `sona` | Native SONA engine with MicroLoRA, EWC++, ReasoningBank — compare with our TS SONA |
|
||
| `ruvector-coherence` | `evaluate_batch()`, `contradiction_rate()`, spectral coherence — quality validation |
|
||
| `ruvector-gnn` + `ruvector-gnn-node` | GNN layers, differentiable search — concept graph analysis |
|
||
| `ruvector-delta-consensus` | CRDTs (`GCounter`, `ORSet`, `LWWRegister`), delta gossip — multi-brain merge |
|
||
| `rvf-quant` | Scalar (4x), Product (8-16x), Binary (32x) quantization — compressed brain exports |
|
||
| `rvf-import` | JSON/CSV/NumPy importers — batch brain ingestion |
|
||
| `ruvector-attention` | 46+ attention types — potential brain search quality improvements |
|
||
|
||
### Version History Summary
|
||
|
||
| Version | Date | Key Changes |
|
||
|---------|------|-------------|
|
||
| 2.0.5 | 2026-02-26 | Fixed fatal `abort()` in NAPI/WASM, mmap bounds checking |
|
||
| 2.0.4 | 2026-02-25 | Security Hardened RVF v3.0, CWE-22 path traversal fix |
|
||
| 0.3.0 | 2026-02-21 | Major: 8 new crates, Ed25519, WASM segments, AGI container |
|
||
| 0.2.6 | 2025-12-09 | PostgreSQL extension, SPARQL, GNN v2, Docker infra |
|
||
|
||
---
|
||
|
||
## Plan Revision History
|
||
|
||
| Date | Changes |
|
||
|------|---------|
|
||
| 2026-03-08 | Initial 5-phase plan created. |
|
||
| 2026-03-08 | Added Phase 6 (ADR-070 witness chain audit compliance). |
|
||
| 2026-03-08 | Post-review revision: 11 issues addressed (see below). |
|
||
|
||
### Revision: 2026-03-08 Post-Review
|
||
|
||
Cross-referenced plan against actual codebase. Changes made:
|
||
|
||
1. **Tables: 23 → 26.** Added `experience_applications` (3 rows, FK → `captured_experiences`),
|
||
`execution_results` (530 rows), `executed_steps` (705 rows) to Tier 2. These were
|
||
previously dismissed as "derived from plans" but contain significant execution outcome
|
||
data that provides learning context for GOAP plans.
|
||
|
||
2. **Fixed `qe_pattern_embeddings` double-count.** Was listed in both "Currently exported (5)"
|
||
and Tier 2. Added clarifying note that Tier 2 enhancement is JSONL export gap fix,
|
||
not a new table.
|
||
|
||
3. **Fixed wrong file path `src/optimization/model-router.ts`.** File does not exist. Corrected
|
||
to `src/routing/qe-task-router.ts` with notes about other routing files.
|
||
|
||
4. **Expanded Phase 6.3 affected files** from 4 to 10 files. Added `real-qe-reasoning-bank.ts`,
|
||
`pattern-lifecycle.ts`, `rvcow-branch-manager.ts`, `asymmetric-learning.ts`,
|
||
`witness-adapter.ts`.
|
||
|
||
5. **Fixed `concept_nodes` embedding count.** Was "variable / subset", actual is 0 (column
|
||
exists but never populated). Updated Milestone 3.2 table.
|
||
|
||
6. **Added kernel JSON size risk (#9).** With 26 tables, kernel could exceed 25MB.
|
||
Mitigation strategies documented.
|
||
|
||
7. **Added import rollback strategy (#10).** SQLite transaction for atomicity, HNSW as
|
||
best-effort step after commit.
|
||
|
||
8. **Fixed backfill query performance (#11).** Replaced O(n×m) LIKE scan with set-based
|
||
`json_extract` + temp table approach (~200x faster).
|
||
|
||
9. **Fixed `getPatternLineage` query.** Replaced LIKE with `json_extract` for consistency.
|
||
|
||
10. **Added `kv_store` partial export note.** Documented 414 learning-relevant rows across
|
||
6 namespaces as future consideration.
|
||
|
||
11. **Added explicit FK-aware import order** for all 26 tables in Milestone 2.2.
|
||
|
||
12. **Updated totals:** effort 39-55h (was 38-54h), tests 115+ (was 105+), net lines +1,255
|
||
(was +1,180).
|