feat:[ENG-2080] Integrate Memory Wiki with Swarm memory#405
feat:[ENG-2080] Integrate Memory Wiki with Swarm memory#405danhdoan merged 5 commits intoproj/swarm-memoryfrom
Conversation
Adds memory-wiki as the 7th swarm provider, connecting to OpenClaw's Memory Wiki plugin via the `openclaw wiki search/get` CLI subprocess. - Add 'memory-wiki' to PROVIDER_TYPES and createDefaultCapabilities - Add MemoryWikiProviderSchema to config schema (openclaw_path, vault_path, search_corpus) - Create MemoryWikiAdapter implementing IMemoryProvider with injectable executor for testability (same pattern as GBrain) - Wire in provider factory, DEFAULT_WEIGHTS (0.8), selection matrix (all types except personal) - Add [wiki] source label in whiteBright for CLI output - Add memory-wiki to config validator (getEnabledProviderIds, isConfiguredProvider) - Add memory-wiki to swarm status display - Score normalization: BM25 unbounded → [0,1) via score/(1+score) - Read-only provider (wiki is compiled, not directly writable) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
|
Claude finished @cuongdo-byterover's task in 2m 45s —— View job PR Review: feat:[ENG-2080] Integrate Memory Wiki with Swarm memory
Overall Assessment: 3/5The adapter architecture is well-structured and consistent with the existing Obsidian/Local Markdown adapter pattern. The MiniSearch integration, digest-based cache invalidation, and freshness/kind boosting are solid. However, there are several correctness bugs and integration gaps that need to be addressed. Bugsbug (critical):
case 'memory-wiki': {
return {
// ...
semanticSearch: true, // ← WRONG: adapter declares false
writeSupported: false, // ← WRONG: adapter declares true and implements store()
}
}The adapter at public readonly capabilities: ProviderCapabilities = {
semanticSearch: false,
writeSupported: true,
// ...
}Both fields are inverted in the static defaults function. Any code path that reads defaults before constructing an adapter instance (e.g. onboarding, cost estimators) gets wrong data. Fix this → bug:
const LOCAL_PROVIDERS: ReadonlySet<ProviderType> = new Set(['byterover', 'local-markdown', 'obsidian'])The adapter correctly declares bug:
Correctness Issuesissue: Missing vault-path validation in Every other path-based provider has a dedicated validator called from Add a issue:
const enabledCount = [
providers.byterover.enabled,
providers.obsidian?.enabled,
providers.localMarkdown?.enabled,
// Honcho and Hindsight temporarily disabled
providers.gbrain?.enabled,
].filter(Boolean).length
// ...
this.log(`\nSwarm is ${status} (${enabledCount}/4 providers configured).`)
issue:
Nitpicksnitpick: Indentation misalignment on line 108
private readonly boostFresh: boolean // ← missing 2-space indent
private readonly vaultPath: string // ← correctly indentednitpick: The nitpick: RRF weight in docs (0.9) doesn't match code (0.8) PR description says "RRF weight 0.9 — wiki results rank second only to ByteRover (1.0)", but nitpick: The Test Coverage Gapsuggestion: Both have bugs (see above) that would have been caught if the existing test suite in |
b548531 to
51f5454
Compare
|
Claude finished @cuongdo-byterover's task in 2m 54s —— View job PR Review: feat:[ENG-2080] Integrate Memory Wiki with Swarm memory
Overall Assessment: 2/5The adapter architecture is clean and follows the Obsidian/LocalMarkdown pattern well. MiniSearch integration, digest-based cache invalidation, and the freshness/kind boost scoring model are all solid. However, there are 3 confirmed correctness bugs that will cause wrong routing decisions or runtime failures, plus several display/count issues and test coverage gaps. These need to be resolved before merge. Bugs (Critical)bug (critical):
case 'memory-wiki': {
semanticSearch: true, // ← WRONG: adapter declares false
writeSupported: false, // ← WRONG: adapter declares true
}The adapter ( semanticSearch: false,
writeSupported: true,Any onboarding, cost estimator, or routing path that reads these defaults before constructing an adapter will get inverted data — potentially excluding memory-wiki from write routing or treating it as a semantic-search provider it is not. Fix this → bug:
const LOCAL_PROVIDERS: ReadonlySet<ProviderType> = new Set(['byterover', 'local-markdown', 'obsidian'])The adapter declares bug:
const dirPath = join(this.vaultPath, dir)
// ... filename resolution loop ...
writeFileSync(filePath, pageContent) // ← ENOENT if dir doesn't exist
Correctness Issuesissue: Missing
issue:
const enabledCount = [
providers.byterover.enabled,
providers.obsidian?.enabled,
providers.localMarkdown?.enabled,
providers.gbrain?.enabled,
].filter(Boolean).length
// ...
this.log(`\nSwarm is ${status} (${enabledCount}/4 providers configured).`)
issue:
Test Coverage Gapssuggestion:
Nitpicksnitpick: Indentation misalignment at line 108
private readonly boostFresh: boolean // ← missing 2-space indent vs. sibling fields
private readonly vaultPath: stringnitpick:
nitpick: RRF weight in PR description (0.9) doesn't match code (0.8)
'memory-wiki': 0.8, // PR description says 0.9Resolve the discrepancy — either update the description or change the weight. nitpick:
nitpick: The adapter declares nitpick:
this.renderProviderLine('Memory Wiki', providers.memoryWiki.enabled && !hasError, 'OpenClaw wiki')GBrain and Obsidian both display their configured path. Memory Wiki should show |
Critical bugs:
- Fix inverted capabilities in createDefaultCapabilities (semanticSearch
false not true, writeSupported true not false, avgLatencyMs 60 not 150)
- Add memory-wiki to LOCAL_PROVIDERS set (was misclassified as cloud)
- Add mkdirSync in store() to handle fresh vaults without concepts/
or entities/ directories
Correctness:
- Add validateMemoryWiki in config validator (checks vault_path exists)
- Fix status count: include memoryWiki in enabledCount, dynamic
denominator instead of hardcoded /4
- Add memory-wiki to write targets display
- Show vault_path instead of hardcoded 'OpenClaw wiki' in status
- Fix RRF weight to 0.9 (was reverted to 0.8 by merge)
Content extraction:
- extractContentSection now checks openclaw:wiki:content markers first
(recognizes pages written by store()), then code blocks, then
frontmatter stripping
Tests:
- Add createDefaultCapabilities('memory-wiki') test
- Add isLocalProvider/isCloudProvider tests for memory-wiki
|
Claude finished @cuongdo-byterover's task in 1m 31s —— View job PR Review: feat:[ENG-2080] Integrate Memory Wiki with Swarm memory
Overall Assessment: 4/5All three critical bugs from the previous reviews have been resolved, and most nitpicks were addressed. The adapter is now well-integrated: capabilities are correct, memory-wiki is classified as a local provider, vault directory creation works on first write, the status command shows the configured vault path, write targets are listed, and RRF weight is consistently 0.9. Two minor issues remain. Remaining Issuesnitpick:
writeFileSync(filePath, pageContent)The method is declared import {writeFile, mkdirSync, ...} from 'node:fs'
import {writeFile} from 'node:fs/promises'
// ...
await writeFile(filePath, pageContent, 'utf8')nitpick: Indentation misalignment — two places
public readonly type = 'memory-wiki' as const
private readonly boostFresh: boolean // ← missing 2-space indent
private readonly vaultPath: string
if (providers.memoryWiki?.enabled && !existsSync(providers.memoryWiki.vaultPath)) {
errors.push({ // ← 6-space indent; sibling validators use 4-space
...
})
}Both are cosmetic but inconsistent with the surrounding code style. Fixed Since Last Review ✓
Minor Observation — Test StructureThe new |
… adapter - Fix indentation misalignment on private readonly boostFresh field - Replace writeFileSync with async writeFile from fs/promises to avoid blocking the event loop in the async store() method
|
Claude finished @cuongdo-byterover's task in 2m 2s —— View job PR Review: feat:[ENG-2080] Integrate Memory Wiki with Swarm memory
Overall Assessment: 4.5/5Good progress — the one blocking nitpick from the previous review ( Fixed Since Last Review ✓
Remaining Nitpicksnitpick: Indentation misalignment — public readonly type = 'memory-wiki' as const
private readonly boostFresh: boolean // ← missing 2-space indent
private readonly vaultPath: string // ← correctly indented
nitpick: Indentation misalignment — if (providers.memoryWiki?.enabled && !existsSync(providers.memoryWiki.vaultPath)) {
- errors.push({ // ← 6-space; sibling validators use 4-space
- field: '...',
- message: '...',
- provider: 'memory-wiki',
- })
- } // ← 4-space; should be 2-space
+ errors.push({
+ field: '...',
+ message: '...',
+ provider: 'memory-wiki',
+ })
+ }The Minor Observation (Non-blocking)
if (config.providers.memoryWiki?.enabled && config.providers.memoryWiki.vaultPath) {
|
- Fix indentation: boostFresh field in adapter, errors.push in validator - Remove redundant vaultPath truthy guard in factory (required field) - Fold memory-wiki isLocalProvider/isCloudProvider assertions into existing describe blocks instead of standalone section
|
Claude finished @cuongdo-byterover's task in 2m 3s —— View job PR Review: feat:[ENG-2080] Integrate Memory Wiki with Swarm memory
Overall Assessment: 5/5All previous review issues have been resolved. The adapter is correct, complete, and well-integrated. Two minor nitpicks remain — one is cosmetically wrong in a specific edge case, the other is a docs-vs-code inconsistency. Fixed Since Last Review ✓
Remaining Nitpicksnitpick:
targets.push(`memory-wiki (${providers.memoryWiki.writePageType ?? 'concept'}, entity)`)When the user has set // Option A — show both possible page types (static, always accurate):
targets.push('memory-wiki (concept, entity)')
// Option B — show only the configured type:
targets.push(`memory-wiki (${providers.memoryWiki.writePageType ?? 'concept'})`)nitpick:
case 'memory-wiki': { return 'memory-wiki' }Renders as Minor Observations (Non-blocking)
|
Summary
brv swarm query.memory-wikias the 7th swarm provider. The adapter reads directly from the wiki vault on disk (no subprocess), uses MiniSearch for fast in-process search (~12ms for 118 pages), supports writes toentities/andconcepts/directories with proper OpenClaw frontmatter, and applies freshness + kind boosts for structured ranking.Type of change
Scope (select all touched areas)
Linked issues
Root cause (bug fixes only, otherwise write
N/A)N/A
Test plan
test/unit/agent/core/domain/swarm/types.test.ts(updated: 7 providers)concepts/with correct frontmatter, immediately queryable✓inbrv swarm status--explainUser-visible changes
memory_wikiin.brv/swarm/config.yaml[wiki]source label in whiteBrightbrv swarm statusdisplays Memory Wiki healthbrv swarm curatecan write to wiki vault (concept/entity pages with OpenClaw frontmatter)personalqueries (wiki content is factual, not personal)Evidence
Status:
Query with explain (118 pages, 5 providers):
Write to wiki:
Performance (15 queries, 118 pages):
Checklist
npm test) — 5891 passingnpm run lint) — pre-commit hook verifiednpm run typecheck)npm run build)mainRisks and mitigations
Risk: Direct file system access to wiki vault — concurrent writes from OpenClaw and ByteRover could conflict.
concepts/andentities/with aswarm.prefix in the page ID. OpenClaw wiki operations target different page types (sources, syntheses). The.openclaw-wiki/locks/directory exists for OpenClaw's internal locking — ByteRover does not acquire locks (acceptable for low-frequency writes).Risk: MiniSearch index not invalidated when OpenClaw updates wiki pages externally.
agent-digest.jsonmtime. When OpenClaw runswiki compile, the digest is updated, which invalidates the MiniSearch cache on next query.Risk:
extractContentSection()may not correctly strip all OpenClaw page templates.```text\n...\n```code block (standard wiki source page format), (2) fallback to stripping YAML frontmatter. Tested with both imported source pages and manually curated concept pages.