Context
The compute slice of #12768 ("Trim the Memory Core healthcheck to a lean liveness probe"), companion to the context-tax slice #13069 (PR #13071). Where #13069 removed two static/redundant fields, this removes the healthcheck's per-call batch-scan.
The Problem
The healthcheck payload runs the migration census on every call: #checkChromaMigrationState → #scanChromaMetadata batch-reads the full memory + summary Chroma collections (batchSize 2000, ~17.9k records), an O(records) cost on a probe that fires at boot, self-repair, and monitoring intervals. The census is operator-scraped — no programmatic consumer reads healthcheck.migration — so it does not belong on the hot path.
The Fix
Mirror the established on-demand pattern (graphLifecycleReport.mjs + GraphService.getLifecycleCensus):
- Add
HealthService.getMigrationCensus({includeChroma}) — cheap SQLite untagged-userId counts always; the O(records) ChromaDB scan only when includeChroma is set.
- Add
ai/scripts/maintenance/migrationCensusReport.mjs (+ npm run ai:migration-census-report) — a thin operator entrypoint; --chroma opts into the scan, --json emits raw.
- Remove the
migration field + the Step-1.5 payload.migration.chromadb mutation from #performHealthCheck. The private census methods + buildChromaMigrationStats remain (now reached via getMigrationCensus).
Contract Ledger
| Field |
Value |
| Target Surface |
ai/services/memory-core/HealthService.mjs (getMigrationCensus), ai/scripts/maintenance/migrationCensusReport.mjs (NEW), package.json |
| Source of Authority |
#12768 (parent trim ticket — AC: "migration census moved to an on-demand surface; healthcheck no longer batch-scans collections per call") |
| Proposed Behavior |
Healthcheck no longer runs the census/batch-scan; operators get it on demand via the script/method; cheap SQLite default, Chroma scan opt-in |
| Fallback |
Graph not mounted / Chroma unreachable → available: false (substrate-readiness, not error), surfaced by the report |
| Evidence |
New migrationCensusReport.spec.mjs green; HealthService.spec.mjs green (no payload.migration assertions existed) |
Acceptance Criteria
Out of Scope (remains in #12768)
features.wake.gateReason trim — a different block (buildWakeFeaturesBlock), debatable (a tripped-gate reason is arguably wanted in-band), with a test asserting current behavior. Left for a separate decision in #12768.
Related
Refs #12768 (parent trim umbrella; delivers its migration-census AC). Mirrors graphLifecycleReport / GraphService.getLifecycleCensus.
Release classification: post-release (token/compute-cost observability hygiene; nothing v13.x-blocking).
Authored by Claude Opus 4.8 (Claude Code, @neo-opus-grace / Grace).
Context
The compute slice of #12768 ("Trim the Memory Core healthcheck to a lean liveness probe"), companion to the context-tax slice #13069 (PR #13071). Where #13069 removed two static/redundant fields, this removes the healthcheck's per-call batch-scan.
The Problem
The healthcheck payload runs the migration census on every call:
#checkChromaMigrationState→#scanChromaMetadatabatch-reads the full memory + summary Chroma collections (batchSize 2000, ~17.9k records), anO(records)cost on a probe that fires at boot, self-repair, and monitoring intervals. The census is operator-scraped — no programmatic consumer readshealthcheck.migration— so it does not belong on the hot path.The Fix
Mirror the established on-demand pattern (
graphLifecycleReport.mjs+GraphService.getLifecycleCensus):HealthService.getMigrationCensus({includeChroma})— cheap SQLite untagged-userId counts always; theO(records)ChromaDB scan only whenincludeChromais set.ai/scripts/maintenance/migrationCensusReport.mjs(+npm run ai:migration-census-report) — a thin operator entrypoint;--chromaopts into the scan,--jsonemits raw.migrationfield + the Step-1.5payload.migration.chromadbmutation from#performHealthCheck. The private census methods +buildChromaMigrationStatsremain (now reached viagetMigrationCensus).Contract Ledger
ai/services/memory-core/HealthService.mjs(getMigrationCensus),ai/scripts/maintenance/migrationCensusReport.mjs(NEW),package.jsonavailable: false(substrate-readiness, not error), surfaced by the reportmigrationCensusReport.spec.mjsgreen;HealthService.spec.mjsgreen (nopayload.migrationassertions existed)Acceptance Criteria
getMigrationCensus({includeChroma})onHealthService— cheap SQLite always, Chroma scan opt-in.migrationCensusReport.mjs+ai:migration-census-reportnpm script, mirroringgraphLifecycleReport.migrationfield + Step-1.5 batch-scan removed from the healthcheck; the private methods +buildChromaMigrationStatsretained.HealthService.spec.mjsgreen.Out of Scope (remains in #12768)
features.wake.gateReasontrim — a different block (buildWakeFeaturesBlock), debatable (a tripped-gate reason is arguably wanted in-band), with a test asserting current behavior. Left for a separate decision in #12768.Related
Refs #12768(parent trim umbrella; delivers its migration-census AC). MirrorsgraphLifecycleReport/GraphService.getLifecycleCensus.Release classification: post-release (token/compute-cost observability hygiene; nothing v13.x-blocking).
Authored by Claude Opus 4.8 (Claude Code, @neo-opus-grace / Grace).