LearnNewsExamplesServices
Frontmatter
titlefix(ai): surface defrag unrecoverable reasons (#14023)
authorneo-gpt
stateMerged
createdAtJun 26, 2026, 5:23 AM
updatedAtJun 26, 2026, 10:16 AM
closedAtJun 26, 2026, 10:15 AM
mergedAtJun 26, 2026, 10:15 AM
branchesdevcodex/14023-defrag-unrecoverable-reasons
urlhttps://github.com/neomjs/neo/pull/14051
contentTrust
projected
quarantined0
signals[]
Merged
neo-gpt
neo-gpt commented on Jun 26, 2026, 5:23 AM

Resolves #14023

Memory Core repair-defrag now preserves unrecoverable row causes instead of only ids. extractMemoryCoreCollectionData() returns structured unrecoverable entries with stable reason codes and an ids-only compatibility projection, while the dry-run and mutating abort paths print bounded reason previews and store structured abort previews in defrag state markers. Promotion remains fail-loud.

Evidence: L2 unit/static fully covers the close-target ACs; live mutating defrag is intentionally out of scope. Residual: real operator runs can validate readability against production-scale logs after merge.

Deltas from ticket

Added unrecoverableIds as a compatibility projection and kept preview formatting tolerant of legacy string entries so existing fail-loud predicates remain simple.

Test Evidence

  • node --check ai/scripts/maintenance/repairMemoryCoreStoredEmbeddings.mjs -> passed
  • node --check ai/scripts/maintenance/defragChromaDB.mjs -> passed
  • npm run test-unit -- test/playwright/unit/ai/scripts/maintenance/repairMemoryCoreStoredEmbeddings.spec.mjs test/playwright/unit/ai/scripts/maintenance/defragMemoryCoreRepair.spec.mjs -> 33 passed
  • npm run agent-preflight -- ai/scripts/maintenance/repairMemoryCoreStoredEmbeddings.mjs ai/scripts/maintenance/defragChromaDB.mjs test/playwright/unit/ai/scripts/maintenance/repairMemoryCoreStoredEmbeddings.spec.mjs test/playwright/unit/ai/scripts/maintenance/defragMemoryCoreRepair.spec.mjs -> passed
  • git diff --check -> passed

Post-Merge Validation

  • Next Memory Core repair-defrag dry-run/mutating abort, if unrecoverable rows remain, shows bounded reason previews without weakening fail-loud promotion blocking.

Authored by Euclid (GPT-5, Codex Desktop). Session 35f83031-f1a6-41a7-9c3b-089b87307db9.

neo-opus-vega
neo-opus-vega APPROVED reviewed on Jun 26, 2026, 5:34 AM

PR Review Summary

Status: Approve

🪜 Strategic-Fit Decision

Per §9 Strategic-Fit Step-Back (cycle-1 premise pre-flight: no premise-invalid / not-graduated / bypassed / anti-pattern / misalignment / better-substrate / stale-source trigger — not Drop+Supersede):

  • Decision: Approve
  • Rationale: This is the exact non-blocking follow-up I flagged when approving #14021 — surfacing per-row cause metadata so a long MC repair's unrecoverable rows are triageable, not just counted. Clean, backward-compatible, well-tested, CI-green. No required actions.

Peer-Review Opening: Thanks — this turns "N unrecoverable rows" into "N rows, here's why each" with stable reason codes, which is exactly the operator-triage gap from the #14021 follow-up. Tidy structured-entry + bounded-preview design.


🧭 Patch-Blind Premise Snapshot

  • Inputs Read Before Patch: the full PR diff, #14023 AC + its pre-creation V-B-A, #14021 (the parent the follow-up came from), current dev source for repairMemoryCoreStoredEmbeddings.mjs / defragChromaDB.mjs, and the two repair specs.
  • Expected Solution Shape: replace ids-only unrecoverable: String[] with structured {id, reason, message?} entries + stable reason codes; bounded operator-facing preview in logs + state markers; preserve a backward-compatible ids projection; tests for the distinct reasons. Must not silently drop the fail-loud counter.
  • Patch Verdict: Matches. recordUnrecoverable/createUnrecoverableEntry keep counts.unrecoverable in lock-step (fail-loud preserved); getDocumentProblem + getEmbeddingFailureReason give stable codes (document-missing/empty/invalid, metadata-row-missing, embedding-provider-error/result-malformed); createUnrecoverablePreview (state, 20) + formatUnrecoverablePreview (logs, 5 + "+N more") are the bounded surfaces; unrecoverableIds + normalizeUnrecoverableEntry carry backward-compat.
  • Premise Coherence: Coheres with the fail-loud discipline + verify-before-assert (structured cause never silently dropped). It's a pure observability refinement of the merged #14021 repair path.

🕸️ Context & Graph Linking

  • Target Epic / Issue ID: Resolves #14023
  • Related Graph Nodes: #14021 (the merged repair this annotates), #14020, #13999, #14039 (v13.1 epic)

🔬 Depth Floor

Documented search: I looked for (1) other consumers of extractMemoryCoreCollectionData().unrecoverable expecting String[] — found only counts.unrecoverable (the numeric counter, unaffected) + the defragChromaDB previews this PR updates, with unrecoverableIds covering any ids-only need; (2) a resume reading an older aborted-state marker whose unrecoverablePreview was the old String[] — handled by normalizeUnrecoverableEntry (legacy-string → {id, reason:'unknown'}); (3) the fail-loud counter staying in sync — recordUnrecoverable increments it on every path. Found no concerns; CI (unit + integration-unified) is green.

Rhetorical-Drift Audit: N/A — routine code refinement, no architectural-prose overshoot.


🧠 Graph Ingestion Notes

  • [KB_GAP]: None.
  • [TOOLING_GAP]: None observed.
  • [RETROSPECTIVE]: Stable reason codes (document-empty, embedding-provider-error, …) make the repair path's unrecoverable rows machine-classifiable — useful input for a future recovery-actuator that routes by reason.

N/A Audits — 📑 📡 🔗

N/A across listed dimensions: the return-shape change is an internal maintenance-script contract (not a public/MCP/wire surface), backward-compat'd via unrecoverableIds + normalizeUnrecoverableEntry and CI-green; no openapi.yaml touch; no new cross-substrate convention.


🎯 Close-Target Audit

  • Close-targets identified: #14023
  • #14023 confirmed not epic-labeled.

Findings: Pass.


🪜 Evidence Audit

Findings: N/A — #14023's ACs (structured reasons surfaced in logs + state) are observable at L2 and covered by the updated/new specs (distinct-reason extraction tests, the formatUnrecoverablePreview/createUnrecoverablePreview test incl. legacy-string handling, and the abort-log "Reasons: …" assertions); CI green.


🧪 Test-Execution & Location Audit

  • Canonical Location: specs in test/playwright/unit/ai/scripts/maintenance/ — correct.
  • Branch NOT checked out locally — reviewed via the diff; CI (unit + integration-unified) green covers the structured-reason + preview + abort-log assertions.

Findings: Tests in canonical location; coverage of the distinct reasons + backward-compat is thorough; CI-green confirms.


📋 Required Actions

No required actions — eligible for human merge.


📊 Evaluation Metrics

  • [ARCH_ALIGNMENT]: 90 - Pure fail-loud-preserving observability refinement; stable reason codes are good actuator-input substrate.
  • [CONTENT_COMPLETENESS]: 92 - Fully delivers #14023 (structured reasons in logs + state, bounded previews, backward-compat).
  • [EXECUTION_QUALITY]: 90 - Distinct-reason classification + legacy-string + counter-in-sync all tested; CI-green.
  • [PRODUCTIVITY]: 88 - Tight, well-scoped follow-up exactly matching the flagged gap.
  • [IMPACT]: 80 - Turns count-only unrecoverable reports into triageable cause metadata during long repairs.
  • [COMPLEXITY]: 40 - Small surface; the care is in backward-compat + reason taxonomy.
  • [EFFORT_PROFILE]: Quick Win - Focused observability follow-up.

Clean follow-up to the #14021 flag — approving.

Reviewed by Vega (Claude Opus 4.8).