Context
Discovered closing the #14082 Post-Merge Validation: the 2026-06-26 canonical backup completed, but its integrity report showed kb: status: skipped while mc/graph were pass. The 1.0 GB Knowledge Base bundle is written but never row-count parity-verified — a torn/partial KB export would not be caught, undermining the backup as a trustworthy recovery source (the exact #14030 concern).
The Problem
verifyBundleIntegrity (backup.mjs:311) derives each subsystem's source count as typeof raw === 'number' ? raw : raw?.count. For kb, subsystems.kb is the return of KB_DatabaseService.manageDatabaseBackup({action:'export'}), which is:
const count = await this.#exportCollection(collection, backupPath, 'knowledge-base-backup');
return {message: `Export complete. Exported ${count} knowledge base chunks.`};The count exists but is embedded in a string with no numeric field → raw?.count is undefined → verifyBundleIntegrity records skipped ("no numeric source count returned by SDK"). Memory Core's export return exposes a numeric count (which is why mc: pass 24044/24044 works), so KB is the lone unverified subsystem.
The Architectural Reality
ai/services/knowledge-base/DatabaseService.mjs:117-122 — exportDatabase returns {message} only; the numeric count is already computed at :121 but dropped from the return.
ai/services/knowledge-base/DatabaseService.mjs:242 — manageDatabaseBackup forwards exportDatabase's return for action: 'export'.
ai/scripts/maintenance/backup.mjs:311 — the verifier reads raw?.count; :329 then streams the bundle file (the #14082 streaming fix already handles KB's >512 MB file, so once a numeric count is present, KB verifies safely).
- Contrast:
ai/services/memory-core/DatabaseService.mjs export returns numeric expected/exported stats that surface as a numeric count.
The Fix
Surface the numeric count on KB's export return so verifyBundleIntegrity resolves a numeric source count for kb and verifies parity (pass/empty/fail) instead of skipped:
return {message: `Export complete. Exported ${count} knowledge base chunks.`, count};Additive — the existing message is preserved. No verifier change needed (raw?.count already reads it). The #14082 streaming counter handles the 1.0 GB KB bundle.
Contract Ledger Matrix
| Target Surface |
Source of Authority |
Proposed Behavior |
Fallback |
Docs |
Evidence |
KB_DatabaseService.exportDatabase() / manageDatabaseBackup({action:'export'}) return |
verifyBundleIntegrity raw?.count contract (backup.mjs:311) |
Add numeric count alongside the existing message |
n/a — additive, message unchanged |
exportDatabase JSDoc @returns |
Unit: KB export return carries numeric count; verifyBundleIntegrity reports pass (not skipped) for a populated KB |
verifyBundleIntegrity kb verdict |
unchanged |
kb now parity-verified like mc/graph |
Still skipped if count absent (back-compat) |
function JSDoc |
Integration: runBackup integrity[] has kb non-skipped |
Decision Record impact
aligned-with #14030 (backup-substrate trustworthiness) / ADR-0025/0026 (backup feeds the recovery source-of-last-resort). No ADR amended.
Acceptance Criteria
Out of Scope
- Changing the human-readable
message string.
- KB chunking / over-budget skip (
#14033 / #14000).
- The MC-side streaming verification (
#14082, shipped) — this consumes it.
Avoided Traps
- Parsing the count out of the message string in the verifier — brittle; surface a structured numeric field at the source instead.
Related
#14030 (parent — backup reliability / verify restorability), #14082 (sibling — the MC-side streaming verification fix that exposed this), #14048 (empty-parity verification), #13999 (the recovery whose completing backup surfaced it).
Handoff Retrieval Hints
query_raw_memories("KB backup export numeric count verifyBundleIntegrity skipped parity")
- Exact anchors:
exportDatabase (ai/services/knowledge-base/DatabaseService.mjs:121), verifyBundleIntegrity raw?.count (backup.mjs:311).
Authored by Vega (Claude Opus 4.8, Claude Code) — surfaced closing #14082's PMV during the 2026-06-26 v13.1 backup work. Session c94ea3b2-1ae8-48fd-8f34-1c54d90f5caa.
Context
Discovered closing the
#14082Post-Merge Validation: the 2026-06-26 canonical backup completed, but its integrity report showedkb: status: skippedwhilemc/graphwerepass. The 1.0 GB Knowledge Base bundle is written but never row-count parity-verified — a torn/partial KB export would not be caught, undermining the backup as a trustworthy recovery source (the exact#14030concern).The Problem
verifyBundleIntegrity(backup.mjs:311) derives each subsystem's source count astypeof raw === 'number' ? raw : raw?.count. Forkb,subsystems.kbis the return ofKB_DatabaseService.manageDatabaseBackup({action:'export'}), which is:// ai/services/knowledge-base/DatabaseService.mjs:121-122 const count = await this.#exportCollection(collection, backupPath, 'knowledge-base-backup'); return {message: `Export complete. Exported ${count} knowledge base chunks.`};The count exists but is embedded in a string with no numeric field →
raw?.countisundefined→verifyBundleIntegrityrecordsskipped("no numeric source count returned by SDK"). Memory Core's export return exposes a numeric count (which is whymc: pass 24044/24044works), so KB is the lone unverified subsystem.The Architectural Reality
ai/services/knowledge-base/DatabaseService.mjs:117-122—exportDatabasereturns{message}only; the numericcountis already computed at:121but dropped from the return.ai/services/knowledge-base/DatabaseService.mjs:242—manageDatabaseBackupforwardsexportDatabase's return foraction: 'export'.ai/scripts/maintenance/backup.mjs:311— the verifier readsraw?.count;:329then streams the bundle file (the#14082streaming fix already handles KB's >512 MB file, so once a numeric count is present, KB verifies safely).ai/services/memory-core/DatabaseService.mjsexport returns numericexpected/exportedstats that surface as a numeric count.The Fix
Surface the numeric
counton KB's export return soverifyBundleIntegrityresolves a numeric source count forkband verifies parity (pass/empty/fail) instead ofskipped:return {message: `Export complete. Exported ${count} knowledge base chunks.`, count};Additive — the existing
messageis preserved. No verifier change needed (raw?.countalready reads it). The#14082streaming counter handles the 1.0 GB KB bundle.Contract Ledger Matrix
KB_DatabaseService.exportDatabase()/manageDatabaseBackup({action:'export'})returnverifyBundleIntegrityraw?.countcontract (backup.mjs:311)countalongside the existingmessageexportDatabaseJSDoc@returnscount;verifyBundleIntegrityreportspass(notskipped) for a populated KBverifyBundleIntegritykbverdictkbnow parity-verified likemc/graphskippedif count absent (back-compat)runBackupintegrity[] haskbnon-skippedDecision Record impact
aligned-with#14030(backup-substrate trustworthiness) / ADR-0025/0026 (backup feeds the recovery source-of-last-resort). No ADR amended.Acceptance Criteria
exportDatabase/manageDatabaseBackup({action:'export'})returns a numericcount(existingmessagepreserved).verifyBundleIntegrityreportskbaspass/empty/fail(parity-verified), notskipped, for a populated KB.fail(the trust gap closed).kbverification path.Out of Scope
messagestring.#14033/#14000).#14082, shipped) — this consumes it.Avoided Traps
Related
#14030(parent — backup reliability / verify restorability),#14082(sibling — the MC-side streaming verification fix that exposed this),#14048(empty-parity verification),#13999(the recovery whose completing backup surfaced it).Handoff Retrieval Hints
query_raw_memories("KB backup export numeric count verifyBundleIntegrity skipped parity")exportDatabase(ai/services/knowledge-base/DatabaseService.mjs:121),verifyBundleIntegrityraw?.count(backup.mjs:311).Authored by Vega (Claude Opus 4.8, Claude Code) — surfaced closing #14082's PMV during the 2026-06-26 v13.1 backup work. Session c94ea3b2-1ae8-48fd-8f34-1c54d90f5caa.