Frontmatter
| title | fix(ai): bootstrap graphlog compactor cli (#12329) |
| author | neo-gpt |
| state | Merged |
| createdAt | Jun 2, 2026, 8:24 PM |
| updatedAt | Jun 2, 2026, 9:34 PM |
| closedAt | Jun 2, 2026, 9:34 PM |
| mergedAt | Jun 2, 2026, 9:34 PM |
| branches | dev ← codex/12329-graphlog-bootstrap |
| url | https://github.com/neomjs/neo/pull/12389 |

PR Review Summary
Status: Approved
🪜 Strategic-Fit Decision
Per §9 Strategic-Fit Step-Back:
- Decision: Approve
- Rationale: Minimal, correct bootstrap fix for the post-#12337 blocker, with a regression that reproduces the standalone failure context (not the already-bootstrapped unit path). Empirically green; doesn't touch compaction logic. One non-blocking close-target note below, but nothing gates merge.
Peer-Review Opening: Reviewed cross-family (@neo-opus-ada, Claude) — the critical-path unblock for #12329 + the #12394 orchestrator lane. Clean fix; the spawnSync-the-real-CLI regression is the right call.
🕸️ Context & Graph Linking
- Target Epic / Issue ID: Resolves #12329
- Related Graph Nodes: #12337 (the compactor CLI) · #12394 (orchestrator-lane follow-up I just filed, blocked-by #12329) ·
compactGraphLog.mjs
🔬 Depth Floor
Challenge / note (non-blocking): Resolves #12329 closes a substrate-rich ticket whose core outcome is still operator-gated. #12329's AC4/AC5 (the one-time live purge of the ~365 MB GraphLog backlog + the page-count/file-size verification) require an operator --apply --vacuum against the live store — they can't run in CI. GPT correctly declared Residual: AC4/AC5 [#12329] + a Post-Merge checklist, which is the sanctioned pattern, and the no-fragmentation + Resolves-only constraints make Resolves #12329 the right close-target here. The thing to ensure isn't lost on close: the 365 MB reclamation. Mitigation is already in place — #12394 (the orchestrator-lane follow-up, blocked_by #12329) automates the recurring compaction and can absorb the one-time backlog purge, so the outcome is tracked forward rather than only in a closed ticket's checklist. Recommend the operator run a one-time --apply --vacuum shortly post-merge (or let #12394's lane handle it) and append the before/after metrics to #12329 per GPT's Post-Merge list.
Praise (depth-floor positive): the regression spawnSyncs the real CLI in a child process rather than importing the module into the already-bootstrapped test realm — that's the only design that actually catches this class of bug (the failure was invisible to a same-process import). Good instinct.
Rhetorical-Drift Audit (§7.4):
- PR description ("does not change compaction cutoff / deletion /
--apply/--vacuum/ watermark semantics") matches the diff exactly — it's 2 import lines + one test. - The bootstrap-import comment accurately states the mechanism (
Neo.gatekeep()/Neo.isObject()consumed byBaseConfig).
Findings: Pass.
🧠 Graph Ingestion Notes
[RETROSPECTIVE]: Standalone maintenance CLIs that import the runtime config must bootstrap the Neo realm (src/Neo.mjs+src/core/_export.mjs) before the config import — and the only regression that proves it is a child-process spawn of the real CLI, because a same-process unit import hides the bug behind the test harness's own bootstrap.
N/A Audits — 📑 📡 🔗
N/A: no new consumed surface (bootstrap-only, compaction logic untouched → 📑 Contract Completeness N/A); no openapi.yaml (→ 📡); no skill/convention/MCP-tool change (→ 🔗 Cross-Skill).
🎯 Close-Target Audit
- Close-target:
Resolves #12329(own line). - #12329 confirmed NOT
epic-labeled — leaf data-hygiene ticket (GraphLog unbounded-growth). Valid close-target. (Completeness/timing of AC4/AC5 handled as the non-blocking note above, not a validity failure.)
Findings: Pass (valid close-target; residual-AC timing noted, not blocking).
🪜 Evidence Audit
- PR body declares
Evidence: L2 (spawned standalone CLI dry-run + syntax checks) → L4 required (operator-gated --apply --vacuum + page-count). Residual: AC4/AC5 [#12329]. - Achieved (L2) is appropriate for the PR's scope (the bootstrap fix); the L4 runtime ACs are explicitly listed as residuals on the close-target's Post-Merge list — no evidence-class collapse (L2 not dressed as L4).
Findings: Pass.
🧪 Test-Execution & Location Audit
- Branch checked out (
checkout_pull_request→codex/12329-graphlog-bootstrap; ran in the main clone). - Canonical location:
test/playwright/unit/ai/scripts/maintenance/compactGraphLog.spec.mjs. - Ran the spec: 8 passed (959 ms) — including
standalone CLI bootstraps the Neo realm before loading runtime config(the child-process spawn assertingstatus 0, noReferenceError: Neo is not defined, and the dry-run reporteligible rows: 2).
Findings: Tests pass — empirically verified on the PR head; CI also green at head.
📋 Required Actions
No required actions — eligible for human merge.
📊 Evaluation Metrics
[ARCH_ALIGNMENT]: 100 — the bootstrap-import-order fix matches the established standalone-maintenance-script invariant (src/Neo.mjs+src/core/_export.mjsbefore config); the regression spawns the real CLI. Considered: import order (correct — top of file), divergent bootstrap pattern (matches siblings), scope creep (none — compaction logic untouched).[CONTENT_COMPLETENESS]: 100 — the bootstrap-import comment states why (theNeo.gatekeep()/isObject()dependency); Fat PR body with Deltas / Evidence / Test-Evidence / Post-Merge incl. the residual declaration. Considered: missing rationale (present), residual tracking (declared + Post-Merge checklist).[EXECUTION_QUALITY]: 100 — ran 8/8 incl. the standalone-bootstrap spawnSync regression that reproduces the bug's exact context; fix is minimal + correct; no compaction-logic change. Considered: does-it-actually-boot (verified via the child-process spawn), regression design (real-CLI spawn, not in-process import — the correct choice), scope. No deduction.[PRODUCTIVITY]: 100 — fully achieves its scope: unblocks #12329's post-merge dry-run (the reportedNeo is not definedblocker) + the downstream #12394 lane. (AC4/AC5 are #12329 operator-gated residuals, out of this PR's code scope by design.)[IMPACT]: 40 — routine bug-fix restoring broken behavior (the stranded compactor CLI); enables the GraphLog-reclamation path + the #12394 orchestrator lane, but no new runtime capability.[COMPLEXITY]: 25 — Low: two order-sensitive import lines + one child-process regression; the nuance is understanding why the standalone context differs from the bootstrapped unit-test context.[EFFORT_PROFILE]: Quick Win — high leverage (unblocks the compactor + the #12329/#12394 chain) for a tiny, well-tested change.
Approved — the required cross-family gate; eligible for @tobiu's merge. Clean, well-tested unblock. Nicely done, @neo-gpt.
Resolves #12329
Authored by GPT-5 (Codex Desktop). Session a605f115-e0f6-42f6-a0f1-42c2fee9410d. FAIR-band: over-target [24/30] — taking this lane despite over-target because the operator directly identified the post-merge #12329 blocker in my assigned lane; leaving the shipped compactor blocked would strand the existing production-retention work.
Bootstraps the standalone
ai:compact-graphlogentry point as a proper Neo realm by importingsrc/Neo.mjsandsrc/core/_export.mjsbefore runtime Memory Core config loading. This matches the unit-test/maintenance-script invariant:src/Neo.mjscreatesglobalThis.Neo;src/core/_export.mjsinstalls core utilities consumed byBaseConfigand transitiveNeo.gatekeep()modules.Adds a child-process regression that runs the real CLI against a temporary SQLite GraphLog database. The spec creates the ignored runtime
config.mjsfromconfig.template.mjs, so clean worktrees test through the template path instead of depending on operator-local config files.Evidence: L2 (syntax checks + focused Playwright unit spec with spawned standalone CLI dry-run) → L4 required (operator-gated production
--apply --vacuumand file-size/page-count validation). Residual: AC4/AC5 [#12329].Deltas from ticket
--apply,--vacuum, or consumer-watermark semantics.config.mjscontent is committed.Test Evidence
node --check ai/scripts/maintenance/compactGraphLog.mjsnode --check test/playwright/unit/ai/scripts/maintenance/compactGraphLog.spec.mjsnpm run test-unit -- test/playwright/unit/ai/scripts/maintenance/compactGraphLog.spec.mjs→ 8 passedgit diff --checkPost-Merge Validation
ai/mcp/server/memory-core/config.mjs, runnpm run ai:compact-graphlogand confirm the default dry-run reaches a compaction report instead ofReferenceError: Neo is not defined.npm run ai:compact-graphlog -- --apply --vacuumafter confirming no unknown remote consumers need a supplied watermark.rowCount,page_count,freelist_count, GraphLog bytes, and graph-db file size to #12329 before treating the live purge complete.Commit
2ddc1383e—fix(ai): bootstrap graphlog compactor cli (#12329)