Skip to content

v3.2 "Prove It" — detailed plan (proposal, not yet a GSD milestone) #46

Description

@RichardHightower

Date: 2026-09-01 · Verified against: main @ d6c8ac7, release v3.1.0 (run 33458631284)
Status: Proposal for maintainer sign-off. Nothing here is implemented.
Repo copy: docs/plans/v3.2-prove-it-plan.md on claude/phase-54-toolchain-drift-3k4fer (18d6638)
Readable page: https://claude.ai/code/artifact/d559ad58-63e4-4a4d-808a-61f8b199ffda

This issue is the canonical home for the plan so it is visible outside .planning/. Decisions go in the comments; the four open ones are in section 10.


0. Thesis

v3.1 made the project's claims true. v3.2 makes them provable and operable: a real benchmark number committed next to the competitor figures, a quality artifact behind every "Solid" in the README status table, and a daemon someone can run for a week without a terminal open. No new retrieval capability ships unless a number says it is the bottleneck.

Same rules as v3.1, unchanged: execution-evidence (run-dependent requirements cite a committed artifact), reachability (cargo tree -i shows a shipped dependent), task pr-precheck green on every PR.


1. Where we are (verified today)

Fact Value How checked
Latest release v3.1.0, 2026-09-01 01:45 UTC, 5/5 platforms release page, run 33458631284
Code 20 crates, 64,626 LOC Rust find crates -name '*.rs' | xargs cat | wc -l
Tests 1,205 workspace + 60 e2e cargo tests green; 114 bats tests / 16 files; Tier 2 weekly ran once, passed local run on clean target/; Actions
GitHub issues 0 ever before this one API
Benchmark artifacts 2, both mock backend + mock judge benchmarks/results/
README status Solid ×6 · Works ×2 · Experimental ×2 · Not implemented ×3 README
Unmerged work one nested line of 99 commits on origin/gsd/phase-58-claude-registration-metadata git rev-list --count origin/main..

What was planned before this, and where it went

Plan Where Status today
v3.1 "Make It True" — PR #31 merged Shipped as v3.1.0. This document is its sequel
March roadmap — v3.1 Memory Export/Import (54–56), v3.2 Plugin Installer & OpenCode Converter (57–59), v3.3+ (--for all, --all, Gemini/Codex/Copilot registration) .planning/ROADMAP.md on the gsd/ branches Never merged; superseded 2026-03-26. Export/import built. Claude Code registration + plugin metadata built (all CREG/META boxes ticked). OpenCode converter built, now obsolete. Uninstall + Status not started. main has none of it
v3 design spec "does not include" docs/superpowers/specs/2026-03-21-v3-competitive-parity-design.md REST endpoint, Python SDK, memory views UI → v3.3+. Cross-encoder rerank → Phase 62 (conditional)

What the March line actually contains, by git diff --stat origin/main...:

  • crates/memory-service/src/backup.rs (+308), import.rs (+280), tests/import_round_trip.rs (+130), deltas to ingest.rs/query.rs
  • proto/memory.proto (+110): the export/import/streaming RPCs
  • crates/memory-storage/src/db.rs (+35), episodes.rs (+41)
  • crates/memory-cli: commands/timeline.rs (+212), output.rs (+265)
  • crates/memory-installer/src/converters/claude.rs (+584 — registration), opencode.rs (+779 — obsolete), tests/e2e_converters.rs (+117)
  • A parallel memory-orchestrator (orchestrator.rs, fusion.rs, rerank.rs, expand.rs…) — the branch was cut before feat(v3.0): Phase 51 Retrieval Orchestrator #28 landed main's orchestrator. This is a duplicate, not a modification. Never cherry-pick it.

2. Findings

F1 · Release pipeline has no guardrails — blocker

Evidence: today's first v3.1.0 push built acc7294 (Cargo.toml = 2.7.0, no Phase 54–57 code), published "Release 3.1.0", public for 17 minutes. .github/workflows/release.yml:

  • Get version (line 49) derives the version from the tag name only
  • no check that GITHUB_SHA is an ancestor of main
  • no check that workspace.package.version == tag
  • release: if: always() && !cancelled() (line 181) publishes whatever platforms succeeded; 7 of 13 historical runs were partial or failed
  • generate_release_notes: true (line 230) produces a PR-title list, not the CHANGELOG entry

The one guard that exists — four binaries per archive, added in #37 — ran for the first time today and held.

F2 · 99 commits of unmerged work — high

Evidence: section 1. The export/import feature is finished and tested (import_round_trip.rs); the Claude Code plugin registration is finished. Neither is on main. An import path is a backfill path (F5).

F3 · The real benchmark has never been run — blocker

Evidence: crates/memory-bench/src/cli.rs:14 (--backend mock|cli), judge.rs:101–126 (--scorer llm-judge reads OPENAI_API_KEY / ANTHROPIC_API_KEY, records model id), main.rs:137–147 (cli backend ingests via memory add and evaluates per question), benchmarks/scripts/download-locomo.sh. Only locomo-smoke.json (4 questions, mock/mock) is committed.

Harness gap for a real run: RunConfig (runner.rs:40) carries one endpoint; run_locomo (main.rs:134) loops conversations against it. The mock path gets a fresh MockStore per conversation; the cli path shares one daemon, so conversation N sees N−1's events — cross-conversation bleed the harness's own caveat names.

F4 · Retrieval quality evidence exists for one layer — high

Evidence: custom-harness fixtures (benchmarks/fixtures/*.toml) carry relevant = [...] and report recall_at_k — but every committed fixture is answerable by token overlap (expected_contains = ["JWT"] with JWT in the source). No fixture requires semantic retrieval. grep -rl "recall\|ndcg\|mrr" crates/memory-vector crates/memory-search crates/e2e-tests → lifecycle code only. memory-topics exposes cluster() and create_topics() (extraction.rs:51,96) with no quality metric anywhere.

F5 · Operational gaps — high

  • Backfill: IndexingPipeline::process_until_caught_up (memory-indexing/src/pipeline.rs:307) only drains the outbox forward from IndexCheckpoint; there is no path that re-reads events already past the checkpoint. admin rebuild-bm25 (memory-daemon/src/cli.rs:311) is a prune.
  • Daemonization: commands.rs:574 rejects --background with guidance to use systemd/launchd; nothing generates the unit.
  • TOC rebuild: AdminCommands::RebuildToc (cli.rs:231) exits non-zero.
  • Panic surface: 5 lock().unwrap() sites remain (vector_updater.rs:459, novelty.rs:709,745, sync.rs:43 + 1). A line-based grep counts 266 unwrap()/expect() in daemon+service, but it cannot exclude inline #[cfg(test)] modules (e.g. retrieval.rs:978+ is test code). The real production count is unknown. That is the first task of 61-03.

F6 · Planning source of truth is stale — medium

.planning/PROJECT.md: "Version: v3.0 (In Progress)"; adapter list includes OpenCode (removed in Phase 57); "API-based summarizer wiring" under Deferred though #27 shipped it (commands.rs:394 resolve_api_key). Zero GitHub issues means the backlog is invisible outside .planning/.

F7 · Launch is coupled to F3 — medium

The blog post stands alone. The Show HN / r/rust / r/LocalLLaMA drafts are product posts; the positioning doc's own rule forbids the comparison those audiences ask first.


3. Target state (milestone exit)

  1. One committed locomo_llm_judge result on the full dataset, real backend, hardware/model/dataset-SHA recorded, next to the competitor figures with a commensurability note.
  2. Every "Solid" row in the README status table cites a committed quality artifact, or is relabelled.
  3. memory-daemon install-service + admin backfill-index + admin rebuild-toc exist and are covered by e2e/bats; no unwrap() on a request path user input can reach.
  4. PROJECT.md accurate; known gaps are GitHub issues; no orphan gsd/ branches; release pipeline refuses a bad tag.

4. Plan

Owner is agent unless it needs credentials, hardware, or a judgment call. Effort in agent sessions (one session ≈ one merged PR of v3.1 size).

Phase 59 — Guardrails and Inventory (3 plans · 3 sessions)

59-01 · Release pipeline checks

Why: F1. Today's incident is reproducible by anyone with a stale local tag.

Files: .github/workflows/release.yml; new docs/RELEASING.md; CLAUDE.md Release Process section.

Steps:

  1. Add a verify job before build, runs-on: ubuntu-latest, that:
    • git fetch origin main and fails unless git merge-base --is-ancestor "$GITHUB_SHA" origin/main
    • reads workspace.package.version with cargo metadata --no-deps --format-version 1 | jq -r '.packages[0].version' (or grep -m1 '^version' Cargo.toml) and fails unless it equals ${GITHUB_REF_NAME#v}; on workflow_dispatch compares to the input
    • fails unless CHANGELOG.md contains a heading for that version
  2. build: needs: verify.
  3. release: — replace if: always() && !cancelled() with needs: [verify, build] and no if:. A failed platform fails the release. Keep the four-binary check from chore(v3.1): release prep — version 3.1.0, changelog, working release archives #37.
  4. Replace generate_release_notes: true with body_path: pointing at a file the job extracts from the matching CHANGELOG.md section (awk '/^## \[?'"$VER"'/{f=1;next} /^## /{f=0} f').
  5. docs/RELEASING.md: the explicit-SHA procedure — git tag -a vX.Y.Z <sha> -m "...", git rev-parse vX.Y.Z^{commit} before push, what the guard will refuse and why. Note the zsh interactivecomments trap. Update CLAUDE.md to point at it.

Acceptance:

  • A tag on a commit not in main fails at verify before any build starts (test with v0.0.0-guardtest on a throwaway branch; delete it)
  • A tag whose version ≠ Cargo.toml fails at verify
  • A build-job failure on one platform yields no GitHub release
  • The release body is the CHANGELOG section verbatim
  • docs/RELEASING.md exists and CLAUDE.md links it

Verify: the guard-test tag runs above; actionlint clean; YAML parses.

Effort: 1 session · Owner: agent (tag pushes for the test: maintainer)

59-02 · Orphan branch triage

Why: F2. Decide with a conflict map, not a feeling.

Files (read-only this plan): the three gsd/ branches; output docs/plans/march-branch-triage.md.

Steps:

  1. For each of backup.rs, import.rs, import_round_trip.rs, the ingest.rs/query.rs/db.rs/episodes.rs deltas, the proto additions, the memory-cli timeline/output deltas, and converters/claude.rs: git diff origin/main...origin/gsd/phase-58-claude-registration-metadata -- <path> and record: applies clean / conflicts / superseded.
  2. For the proto additions: check message and RPC names against proto/memory.proto on main (no Export*/Import*/Backup* RPCs exist today, so likely clean; the streaming RPC style needs review against tonic version on main).
  3. Explicitly mark the branch's memory-orchestrator and memory-bench directories do not port (parallel implementations).
  4. Write the recommendation: cherry-pick list with commit SHAs, in order, for 61-01 (export/import) and 61-05 (registration); estimated conflict count per file.
  5. After maintainer decision: delete gsd/phase-57-opencode-converter-registration immediately; delete the other two once 61-01 and 61-05 merge.

Acceptance:

  • docs/plans/march-branch-triage.md lists every ported file with clean/conflict status and the exact git cherry-pick/git checkout <sha> -- <path> sequence
  • Maintainer decision recorded in the doc (port / rewrite / drop)
  • OpenCode branch deleted

Effort: 1 session · Owner: agent; decision: maintainer

59-03 · Planning truth and a public backlog

Why: F6.

Files: .planning/PROJECT.md, .planning/ROADMAP.md, .planning/STATE.md, .planning/REQUIREMENTS.md; GitHub issues.

Steps:

  1. PROJECT.md: Current State → v3.1 shipped 2026-09-01; Current Milestone → v3.2 (this plan); adapters list → Claude Code, Codex (Tier 1), Gemini, Copilot (Tier 2); move "API-based summarizer wiring" to Validated with the feat(v3.0): wire API-based summarizer from config (supersedes #26) #27 reference; add "true daemonization" → superseded by install-service (61-02) once decided.
  2. REQUIREMENTS.md: replace the v3.0 block with v3.2 requirement IDs (REL-01..04, BENCH-10..13, QUAL-01..03, OPS-01..05, INST-01..03) mapped 1:1 to the plans below; keep the Future (v3.3+) list.
  3. Open one GitHub issue per known gap, each linking its README row and its plan ID: real LOCOMO run, vector quality, topic quality, backfill, daemonization, TOC rebuild, cross-encoder, uninstall/status. Label v3.2. Link each as a sub-issue of this one.
  4. ROADMAP.md/STATE.md: add v3.2 phases 59–62 in the existing format.

Acceptance:

  • grep -n "v3.0 (In Progress)\|OpenCode" .planning/PROJECT.md → none
  • 8 open issues labelled v3.2, each with a plan ID
  • Every requirement ID in REQUIREMENTS.md appears in exactly one plan

Effort: 1 session · Owner: agent

Phase 60 — Real Numbers (3 plans · 4 sessions + 1 maintainer run)

60-01 · Live-backend isolation for LOCOMO

Why: F3. Without isolation the number is contaminated and cannot be committed.

Files: crates/memory-bench/src/runner.rs (RunConfig), main.rs (run_locomo, evaluate_sample_cli), locomo.rs (ingest_sample_cli), cli.rs; crates/memory-daemon/src/cli.rs (Start args: --port, database path override already exist).

Design (pick A; B is fallback):

  • A. Spawn-per-conversation. RunConfig gains daemon_bin: Option<PathBuf>, isolation: Isolation::{Shared, DaemonPerConversation}. For each conversation: create a tempfile::tempdir(), spawn memory-daemon start --db-path <tmp> --port <free port>, wait for memory-daemon status healthy, run ingest + evaluate against that endpoint, send stop, drop the dir. Result JSON records "isolation": "per-conversation daemon".
  • B. Reset RPC. Add AdminReset gated by a --allow-reset daemon flag. Rejected unless A proves infeasible: it adds a destructive RPC to the daemon for the benefit of a benchmark.
  • Drain wait: replace any sleep with polling GetVectorIndexStatus / a new lightweight GetIndexCheckpoints RPC until BM25 and vector checkpoints ≥ the outbox sequence after ingest; timeout 5 min with a loud error. Record wait time per conversation in the result.

Steps:

  1. Implement A behind --isolation daemon-per-conversation (default for --backend cli; shared remains for local debugging and prints the bleed caveat).
  2. Add the checkpoint poll. If no RPC exposes checkpoints, add GetIndexCheckpoints to proto/memory.proto + memory-service — read only, small.
  3. Extend smoke so CI runs the 1-conversation fixture under --backend cli with a spawned daemon and the mock judge (ci.yml new job bench-cli-smoke, Linux only, after build).
  4. Update docs/benchmarks.md "Run" and "Modes".

Acceptance:

  • memory-bench locomo --backend cli --scorer mock --dataset benchmarks/fixtures/locomo-smoke.json completes with isolation: per-conversation daemon in the output and a drain_wait_ms per conversation
  • A unit test asserts two conversations ingested under isolation A do not retrieve each other's turns (mirror of mock_stores_do_not_bleed, runner.rs:346)
  • CI job bench-cli-smoke green
  • No std::thread::sleep remains in the cli-backend path

Effort: 1 session · Owner: agent

60-02 · The run

Why: F3, F7. The single most valuable artifact the project can produce.

Prereqs: 60-01 merged; OPENAI_API_KEY (or Anthropic); a machine you are willing to name in the result; --release build.

Steps:

  1. benchmarks/scripts/download-locomo.shlocomo-data/locomo10.json; record its SHA-256.
  2. Dry run, cost cap: memory-bench locomo --dataset locomo-data --backend cli --scorer llm-judge --limit-questions 200 --output benchmarks/results/locomo-2026-MM-DD-partial.json (add --limit-questions in 60-01 if absent). Check the judge's recorded cost/tokens.
  3. Full run to benchmarks/results/locomo-2026-MM-DD.json. Expect ~2,000 questions; under ~$10 on gpt-4o-mini; 1–2 h wall clock dominated by ingest + drain.
  4. Commit the result. Update docs/benchmarks.md "Committed result" table with hardware, profile (--release), model, temperature 0, dataset SHA, isolation mode, and the per-type breakdown (single-hop / multi-hop / temporal / adversarial) the harness already emits.
  5. Update the positioning doc "Benchmarks: what we can and cannot say" and the Claims Ledger row "Our own committed benchmark artifacts".
  6. Decision gate (from v3.1 56-03, unchanged): publish the number whatever it is; the only thing that can hold it is a methodology defect.

Acceptance:

  • benchmarks/results/locomo-*.json with "metric": "locomo_llm_judge", "isolation": "per-conversation daemon", non-null model, hardware, dataset_sha256
  • docs/benchmarks.md and the positioning doc cite the file path
  • Per-type scores present (feeds the Phase 62 gate)

Effort: 1 maintainer session · Owner: maintainer (agent prepares the exact commands and reviews the artifact)

60-03 · Vector and topic quality fixtures

Why: F4. "Solid" needs an artifact.

Files: benchmarks/fixtures/semantic-001.toml (new), benchmarks/fixtures/sessions/*.jsonl (new sessions), crates/memory-bench (a --layers bm25|vector|hybrid switch on the custom harness), crates/memory-topics (a metrics module), crates/e2e-tests/tests/topic_graph_test.rs, README status table, positioning Claims Ledger.

Steps:

  1. Semantic fixture set (≥ 15 tests). Each test's relevant items share meaning but not tokens with the query — "token expiry policy" → session text says "JWT lifetime"; "container orchestration cutover" → "EKS migration". Build the sessions so a BM25-only run scores recall@5 < 0.4 on the set (that is the point) and record it.
  2. Harness switch: memory-bench run --category semantic --layers bm25|vector|hybrid maps to TeleportSearch / VectorTeleport / HybridSearch. Commit three result files.
  3. Topic quality. A labelled fixture of 60–100 short documents in 6–8 known clusters; memory-topics::metrics::{purity, adjusted_rand_index} over cluster() output. Commit benchmarks/results/topics-quality.json.
  4. Status table: vector row cites the hybrid-vs-bm25 delta; topic row moves from "Works · not benchmarked" to "Works · ARI x.xx" or stays "Works" with the number. Positioning Claims Ledger gains both rows.

Acceptance:

  • benchmarks/results/semantic-{bm25,vector,hybrid}.json committed; hybrid recall@5 > bm25 recall@5 on the semantic set (if not, that is a finding and the README changes accordingly)
  • topics-quality.json committed with purity and ARI
  • README rows for vector and topic graph link the artifacts
  • cargo test -p memory-topics metrics covers purity/ARI on a hand-computed 3-cluster example

Effort: 2 sessions · Owner: agent

Phase 61 — Operate It (5 plans · 6 sessions)

61-01 · Backfill

Why: F5. Every pre-v3.1 store is stuck.

Files: crates/memory-indexing/src/pipeline.rs, checkpoint.rs; crates/memory-daemon/src/cli.rs (AdminCommands::BackfillIndex), commands.rs; crates/memory-storage (event iteration by sequence); docs/UPGRADING.md, README status table. If 59-02 kept memory-service/src/import.rs, reuse its event replay.

Design: memory-daemon admin backfill-index --index bm25|vector|all [--from-sequence N] [--batch 500] [--dry-run]. Runs against a stopped daemon (takes the RocksDB lock) — stopped-only for v3.2 (decision 4). Algorithm: iterate events from --from-sequence (default 0) in batches; for each batch call the IndexUpdater for the chosen index; commit; write IndexCheckpoint = last sequence processed. Idempotent (re-indexing an existing doc is an upsert in Tantivy and HNSW). Resumable by reading the checkpoint on restart. Progress n/total on stderr every batch.

Steps:

  1. IndexingPipeline::backfill(from: u64, batch: usize, indexes: &[IndexType]) -> Result<BackfillReport> beside process_until_caught_up.
  2. CLI subcommand + wiring; refuse to run if the daemon lock is held, with the message naming memory-daemon stop.
  3. Fixture: a RocksDB store produced by the v3.0 daemon (build 68ab122, ingest 50 events, commit the directory under crates/e2e-tests/fixtures/store-v3.0/).
  4. e2e: open fixture copy → backfill-index --index allTeleportSearch returns previews for all 50.
  5. Docs: README BM25 row drops the "no backfill" note; UPGRADING.md v3.1 section gains the command.

Acceptance:

  • e2e above passes; a second backfill-index run reports 0 new documents
  • --dry-run prints counts and writes nothing (checkpoint unchanged)
  • Interrupting mid-run (test sends SIGINT after batch 1) and re-running resumes from the checkpoint
  • README and UPGRADING updated in the same PR

Effort: 2 sessions · Owner: agent

61-02 · Daemon lifecycle via service units

Why: F5. Decision 3.

Files: crates/memory-daemon/src/cli.rs (Commands::{InstallService, UninstallService}), commands.rs, new service.rs; tests/cli/claude-code/*.bats, tests/cli/codex/*.bats; docs/setup/quickstart.md.

Design: memory-daemon install-service [--port] [--db-path] writes ~/Library/LaunchAgents/com.spillwave.memory-daemon.plist (macOS, launchctl bootstrap gui/$UID) or ~/.config/systemd/user/memory-daemon.service (Linux, systemctl --user enable --now). uninstall-service reverses it. Windows: exit non-zero with guidance (out of scope; Tier 2 at best). --background keeps exiting non-zero, now naming install-service.

Acceptance:

  • bats (both Tier 1 suites): install-servicememory-daemon status healthy within 10 s → uninstall-service → status reports not running; unit file removed
  • Re-running install-service is idempotent (unit rewritten, no duplicate)
  • Quickstart shows install-service as the recommended path
  • README row "Background daemonization" → "Via service unit"

Effort: 1 session · Owner: agent

61-03 · Panic audit

Why: F5. Get the real number, then fix the class that matters.

Files: crates/memory-service/src/{agents,retrieval,federated,episodes,teleport_service,topics,novelty}.rs, crates/memory-daemon/src/clod.rs, crates/memory-indexing/src/vector_updater.rs:459, memory-types/src/sync.rs:43; new crates/e2e-tests/tests/hostile_input_test.rs.

Steps:

  1. Real count: a cargo clippy run with -W clippy::unwrap_used -W clippy::expect_used restricted to non-test code (the lints already skip #[cfg(test)]). Commit the count in the PR description.
  2. Classify each site: (a) provably infallible — annotate with // INFALLIBLE: comment or convert to expect("<invariant>"); (b) fallible on a request path — convert to ? with a Status::internal/invalid_argument; (c) lock poisoning — finish the 5 with parking_lot or unwrap_or_else(PoisonError::into_inner) per the 54-06 policy already chosen.
  3. Enable #[warn(clippy::unwrap_used, clippy::expect_used)] in memory-service and memory-daemon lib roots so regressions are visible (warn, not deny, so tests keep passing).
  4. hostile_input_test.rs: for every RPC in proto/memory.proto, send empty, oversized (1 MiB string), malformed-UTF-8, negative/overflow numeric, and unknown-enum requests via the direct-handler pattern (tonic::Request, per v2.2 decision); after each, a health RPC must still answer.

Acceptance:

  • Clippy count of unwrap_used + expect_used in production code recorded before/after; class (b) count is 0 after
  • hostile_input_test.rs covers all 30 RPCs and passes
  • Lock-poisoning grep returns 0 production sites

Effort: 1 session · Owner: agent

61-04 · Offline TOC rebuild

Why: F5. The stub is honest but the gap is real.

Files: crates/memory-daemon/src/cli.rs:231 (AdminCommands::RebuildToc), commands.rs; crates/memory-toc (the rollup jobs the scheduler runs — reuse, do not reimplement); crates/e2e-tests/tests/pipeline_test.rs.

Design: admin rebuild-toc --from YYYY-MM-DD --to YYYY-MM-DD [--dry-run] runs the same day/week/month/year rollup code the scheduler invokes, over events in range, replacing existing nodes for that range. Stopped-daemon only, same lock rule as 61-01.

Acceptance:

  • e2e: build TOC via scheduler path → snapshot nodes → delete them → rebuild-toc → nodes byte-equal to the snapshot (ids, ranges, summaries)
  • --dry-run reports the node count it would write
  • README row "Offline TOC rebuild" → "Works"

Effort: 1 session · Owner: agent

61-05 · Installer: register, uninstall, status

Why: F2, and the March v3.2's still-valid half.

Files: crates/memory-installer/src/main.rs (Commands::{Uninstall, Status}), converters/claude.rs (port the registration from the branch: known_marketplaces.json, installed_plugins.json, settings.json enabledPlugins), .claude-plugin/plugin.json + marketplace.json (port), tests/e2e_converters.rs, tests/cli/claude-code/*.bats.

Steps:

  1. Port CREG-01..06 + META-01..03 per the 59-02 sequence; drop every OpenCode path.
  2. uninstall --agent claude|codex|gemini|copilot: remove registry entries (Claude) and installed files; no-op exit 0 when nothing is installed.
  3. status: table of runtime · installed version · path · registered (Claude only) · "not installed".
  4. Gemini/Codex/Copilot stay convert-only (registration is v3.3+ REG-F01).

Acceptance:

  • bats: install --agent claudestatus shows version+path+registered → Claude Code launched headless lists the plugin → uninstallstatus "not installed" → second uninstall exits 0 silently
  • .claude-plugin/plugin.json version is the single source for the install path (META-03)
  • Re-install is idempotent (CREG-06)

Effort: 1 session · Owner: agent

Phase 62 — Cross-encoder rerank (conditional · 2 sessions)

Gate: run only if 60-02's per-type breakdown shows retrieval is the limiter — e.g. multi-hop and temporal recall@k high but judge accuracy low means generation, not retrieval; the reverse means rerank might help. The extension point (memory-orchestrator rerank trait, explicit NotImplemented) stays as is until then. If the gate opens: local cross-encoder via Candle in memory-embeddings, wired behind --rerank=cross, measured on the same fixtures, committed before any README change. Do not build ahead of evidence.

Launch (side quest · maintainer)

  • Now: blog post (process story, no number needed).
  • After 60-02: repo description + topics (ai-agents, memory, rust, claude-code, local-first), enable Discussions, record the demo, then Show HN / r/rust / r/LocalLLaMA with the number in paragraph one. Each draft in docs/launch/launch-copy.md needs one edit to cite it.

5. Requirement map

ID Requirement Plan
REL-01 Tag must be an ancestor of main 59-01
REL-02 Crate version must equal tag 59-01
REL-03 Any failed platform build → no release 59-01
REL-04 Release body from CHANGELOG 59-01
BENCH-10 Per-conversation isolation on cli backend 60-01
BENCH-11 Deterministic drain wait 60-01
BENCH-12 Committed locomo_llm_judge full-dataset result 60-02
BENCH-13 Layer switch bm25/vector/hybrid on custom harness 60-03
QUAL-01 Semantic fixture set, ≥15 tests 60-03
QUAL-02 Topic clustering purity + ARI artifact 60-03
QUAL-03 README "Solid" rows cite artifacts 60-03
OPS-01 admin backfill-index resumable, idempotent 61-01
OPS-02 install-service/uninstall-service macOS+Linux 61-02
OPS-03 Zero fallible unwrap on request paths 61-03
OPS-04 Hostile-input e2e over all RPCs 61-03
OPS-05 admin rebuild-toc real 61-04
INST-01 Claude Code plugin registration (CREG/META) 61-05
INST-02 memory-installer uninstall 61-05
INST-03 memory-installer status 61-05

6. Sequencing

59-01 ──┐
59-02 ──┼──▶ 61-01 (needs 59-02 decision)
59-03 ──┘    61-05 (needs 59-02 decision)
60-01 ──▶ 60-02 (maintainer) ──▶ 62 gate
60-03 (independent)
61-02, 61-03, 61-04 (independent of each other; after 59-01 lands)
Phase Sessions Notes
59 3 can overlap with 60-01 and 60-03
60 4 + 1 maintainer run 60-02 blocks the launch posts
61 6 61-01/61-05 wait on 59-02
62 0 or 2 gated
Total 13–15 agent sessions + 1 maintainer run two to three weeks at v3.1 pace

7. Risks

Risk Mitigation
LOCOMO number is poor Commit it anyway (56-03 gate). The positioning doc already frames it. Phase 62 exists for exactly this
Cherry-pick conflicts worse than expected 59-02 maps them before anyone commits to porting; rewrite is a valid outcome
Spawn-per-conversation is slow Once per release; acceptable. Do not optimise before it exists
Judge cost surprise --limit-questions 200 dry run first
Backfill on a large store takes hours Batch + checkpoint make it resumable; document expected rate from the e2e fixture
clippy::unwrap_used warn is noisy Warn not deny; the PR records the count, CI stays green

8. Milestone success criteria

  • REL-01..04: a wrong tag cannot produce a release (59-01)
  • BENCH-12: one committed locomo_llm_judge result, full dataset, real backend, provenance fields non-null (60-02)
  • QUAL-03: every README "Solid" row links a committed artifact (60-03)
  • OPS-01: v3.0 fixture store backfills and searches (61-01)
  • OPS-02: service install round-trips on both Tier 1 platforms (61-02)
  • OPS-03/04: zero fallible unwraps on request paths; hostile-input suite green (61-03)
  • INST-01..03: install/status/uninstall round-trip on Claude Code (61-05)
  • PROJECT.md accurate; 8 v3.2 issues; no gsd/ branches (59-02, 59-03)
  • task pr-precheck green on every PR

9. Out of scope (stays v3.3+)

REST/HTTP endpoint · Python SDK · memory views UI · --for all / --all installer flags · Gemini/Codex/Copilot registration · Windows service install · true double-fork daemonization · consolidation hook · cross-project unified memory · per-agent dedup scoping.

10. Decisions needed from the maintainer

  1. March branch line — port export/import + Claude registration by feature (recommended), rewrite from scratch, or drop?
  2. Launch timing — blog now, product posts after 60-02 (recommended), or hold everything?
  3. Daemonization — service unit files (recommended; survives reboot, matches the guidance the daemon already prints) or true double-fork?
  4. Backfill mode — stopped-daemon CLI only for v3.2 (recommended), or also an admin RPC against a running daemon?

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions