Epic: reflect-kb storage + retrieval upgrade — ports from Hindsight & ByteRover #227
Replies: 7 comments
Update — Memory category breakdown (LoCoMo dimensions) + 3 new ports for the Open-Domain gapFolding in a new dimension of analysis: the LoCoMo benchmark categories (Single-Hop / Multi-Hop / Temporal / Open-Domain) — exactly the reasoning shapes reflect-kb needs to support to be competitive with Hindsight and ByteRover. The catalogue grows from 31 → 34 ports with 1 · The scoreboard (from ByteRover's bundled paper, reusing Hindsight's eval prompts)
ByteRover wins 3 of 4 (Multi-Hop by a massive 22.5pp). Hindsight wins one — Open-Domain — and decisively. The architectural reasons matter because they tell us exactly what to port for each weakness in reflect-kb. 2.1 · Single-Hop ReasoningWhat's measured. recalling a simple explicit fact stated in one chat session. Scoreboard. ByteRover 97.5% · Hindsight 86.2% · gap +11.3pp → ByteRover wins. Why ByteRover wins. facts stored AS facts (typed Why Hindsight is weaker. fact-extraction LLM rephrases ('authentication mechanism is JWT' instead of 'JWT'); vector search drifts to similar-but-wrong facts; token-budget cut can drop single-fact answers. Reflect's analog (correction payload). 'what was the fix for bug X?' / 'what command was the right one?' — needs the exact correction, not five tangentially related ones. Ports that improve this in reflect-kb:
2.2 · Multi-Hop ReasoningWhat's measured. combining 2-3 facts from different conversations to infer an answer. Scoreboard. ByteRover 93.3% · Hindsight 70.8% · gap +22.5pp → ByteRover wins. Why ByteRover wins. LLM authors Why Hindsight is weaker. surprising given typed memory_links graph — but consolidation collapses distinct facts into single observations, which is brilliant for compression but DESTROYS the multi-hop bindings; reflect agent gathers and synthesizes but doesn't chain. Reflect's analog (correction payload). 'we changed auth from JWT to sessions because of X, which meant we had to update Y, which now constrains Z' — the most valuable engineering knowledge is the chain itself. Ports that improve this in reflect-kb:
2.3 · Temporal ReasoningWhat's measured. understanding time horizons, event ordering, conducting date arithmetic. Scoreboard. ByteRover 97.8% · Hindsight 83.8% · gap +14.0pp → ByteRover wins. Why ByteRover wins. system-managed createdat/updatedat NEVER agent-authored — reliable anchors that can't be poisoned by drift; compound score weight on recency = 0.2; maturity hysteresis preserves 'this WAS true' knowledge; tier 0-2 deterministic on dates. Why Hindsight is weaker. sophisticated Reflect's analog (correction payload). 'what did we decide last sprint' / 'this approach is from before we switched to TypeScript' / 'what was the convention before the rewrite'. Ports that improve this in reflect-kb:
2.4 · Open-Domain KnowledgeWhat's measured. persona details, user preferences, general background accumulated over many sessions. Scoreboard. ByteRover 85.9% · Hindsight 95.1% · gap +9.2pp → Hindsight wins. Why Hindsight wins. consolidation distills raw facts into evidence-counted observations (proof_count, history, contradiction reconciliation) — 50 small 'user prefers X' mentions become one strong observation; mental_models are curated query-defined synthesized docs with auto-refresh; banks.disposition/mission/personality/background make persona a first-class object; 3-tier forced retrieval mental_models→observations→facts puts curated layer first. Why ByteRover is weaker. topics are about specific things; no aggregation primitive — ByteRover's design says 'the LLM authors the structure' but persona is EMERGENT across topics, not authored as a topic; Reflect's analog (correction payload). 'what conventions does this codebase use' / 'what does this team prefer' / 'what are this developer's habits' / 'what's the agreed style for X here' — the most under-served category in reflect-kb today. Ports that improve this in reflect-kb:
3 · The 3 new ports (O1/O2/O3) — full detailThese were missing from the original catalogue. They're all sourced from Hindsight's open-domain wins.
|
Update — Fourth system folded in: rohitg00/agentmemory + 6 new ports (A1–A6)
1 · One-paragraph thesisagentmemory is the largest and most architecturally ambitious of the four systems — ~37.8K LOC, 174 source files, 128 REST endpoints, 53 MCP tools, 12–22 lifecycle hooks, 44 KV scopes. Built as a TypeScript Node worker on top of a Rust iii-engine runtime (a generic actor/worker framework — KV / streams / cron / queues / OTel / mesh / debug console all free for the taking). Capture-everything fire-hose: every tool call, every prompt, every file touch streams through. LLM compression is off-by-default (synthetic compression keeps the index building with zero API calls); SessionStart injection is also off-by-default since #143. Headlines: triple-stream BM25 + Vector + Graph retrieval with RRF k=60, optional cross-encoder rerank, query expansion, session diversification (max 3 per session). 4-tier memory model (working / episodic / semantic / procedural) plus lessons / insights / crystals / 8 pinned editable "slots". Apache-2.0, single primary maintainer (Rohit Ghumare). 2 · Where it sits in the quadrant
3 · The 6 new ports — full detailThese were missing from the catalogue because they're not in Hindsight or ByteRover.
|
| Wave | Count | New from this update |
|---|---|---|
| Wave 1 — Freebies | 4 | — |
| Wave 2 — ML upgrades | 8 | — |
| Wave 3 — Storage structuring | 16 | A1, A3, A4 |
| Wave 4 — Lifecycle & maintenance | 12 | A2, A5, A6 |
| Total | 40 | +6 |
5 · Which of these I'd actually prioritise
If I had to add only TWO from the agentmemory ports to the next sprint (after Wave 1+2 of the original catalogue), it would be:
A1Memory slots — biggest UX win, closes a tier that none of the four systems handles cleanly. Cheap to build (one table + 8 default rows + skill). High immediate value because the slots inject ahead of recall = zero-token ambient context.A6Branch-aware capture — this codebase literally runs multiple worktrees of itself. Every other day reflect-kb hands the agent a learning from a sibling branch that's wrong. Cheap fix, large invisible-cost reduction.
A4 followup-rate is also very cheap and gives empirical signal we currently lack. Worth bundling.
A2 bitemporal is conceptually elegant but the use case is narrower than the others.
6 · References
- Topology + how-it-works + benchmarks:
benchmark/{LONGMEMEVAL.md, QUALITY.md, SCALE.md, COMPARISON.md},src/state/{hybrid-search.ts, vector-index.ts, search-index.ts, reranker.ts},src/functions/{observe.ts, smart-search.ts, slots.ts, consolidation-pipeline.ts, reflect.ts, auto-forget.ts},src/types.ts,src/triggers/api.ts(128 endpoints),src/mcp/tools-registry.ts(53 tools),iii-config.yaml. - Commit @ snapshot:
7d01d057cda86fb191747f9f9d2f7e9064f2fed2. - LoCoMo gap: agentmemory does NOT report LoCoMo or BEAM scores. LongMemEval-S R@5 95.2% (BM25+Vector) is retrieval-only, not the QA accuracy the LongMemEval leaderboard tracks.
claude-mem vs reflect — adoption opinionDirect answer: Stevie, the honest take is stay on reflect, port 6-8 specific patterns from claude-mem, and do NOT adopt claude-mem as your memory system. claude-mem is a much bigger and more polished product than reflect, but it's solving an orthogonal problem (fire-hose tool-use capture with LLM-at-write extraction) rather than reflect's problem (correction-driven permanent behavioral change). Adopting it would cost you the agent-rewrite loop you actually care about, while bolting on a Bun daemon + Python Chroma subprocess + per-session Claude SDK observer subprocesses that bill on every tool call. Where claude-mem genuinely beats reflect today
What reflect does that claude-mem structurally doesn't
The blunt answer to "can claude-mem do reflect's correction-capture + skill-rewriting loop?" is no — not structurally, and not without forking. The taxonomy is fact-shaped (bugfix/feature/refactor/discovery/decision), not correction-shaped, and there is no rewriter agent or skill-emission path. Adoption costs specific to claude-mem
The three options
My honest recommendationOption A. Reflect's correction-capture + skill-rewriting loop is the actual differentiator and the thing you've been building toward; claude-mem doesn't have it and isn't trying to. But claude-mem has 10 patterns worth porting wholesale:
Skip CM-2 (per-user port arithmetic — overkill), CM-9 (Endless Mode — unproven, expensive), and the full multi-tenant Postgres/BullMQ tier (only matters if you're going hosted). The honest caveatThis recommendation flips to Option C if (a) you decide reflect's correction-capture loop isn't load-bearing for your workflow and you'd rather have a queryable tool-use log across 12 IDEs, or (b) you want to ship a hosted/team memory product and don't want to build the multi-tenant substrate yourself — claude-mem's server-beta (Postgres + BullMQ + BetterAuth + audit chain + scope refusal) is genuinely good and would save you weeks. It flips to Option B if you're willing to absorb the daemon + Chroma overhead and the per-tool-use LLM cost in exchange for claude-mem's real-time viewer and knowledge-corpus Q&A while keeping reflect's rewrite loop intact. Default stays A. |
Rebuildable kit — GistThis work is now bundled as a Gist that contains everything needed to regenerate the explainer locally and re-publish. The repo ( Gist: https://gist.github.com/stevengonsalvez/12ff627b80ee914a344469711f8e5ec1 What's in the Gist
Regenerate locallygh gist clone 12ff627b80ee914a344469711f8e5ec1
cd 12ff627b80ee914a344469711f8e5ec1
bash regenerate.sh # builds hindsight-vs-reflect.html
bash regenerate.sh --publish # also publishes to here.now (24h anonymous URL)To modify and re-publish
Architecture of the kit
|
GitHub epic issue createdPublic-facing tracker: #239 — links every one of the 57 bead IDs grouped by wave + layer, with dependency chains called out. Local detailed tracking: beads ( Discussion ← canonical narrative · GH issue #239 ← public tracker · Beads ← local detail · Gist ← rebuildable kit · here.now ← live HTML view. |
Fresh-agent bootstrap promptA self-contained drop-in prompt for picking this work up on a fresh machine with any capable coding agent (Claude Code, Codex, Cursor, etc.): 📄 Includes:
The new gh gist clone 12ff627b80ee914a344469711f8e5ec1 /tmp/recall-kit
cd <target-repo>
bd init && cp /tmp/recall-kit/beads-issues.jsonl .beads/issues.jsonl
bd sync --import && bd readyDrop the contents of |
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Epic: reflect-kb storage + retrieval upgrade — ports from Hindsight, ByteRover, agentmemory & claude-mem
Four source systems researched at depth:
vectorize-io/hindsightpinned atc255d35campfirein/byterover-clipinned atfaf456drohitg00/agentmemorypinned at7d01d057cda86fb191747f9f9d2f7e9064f2fed2thedotmack/claude-mempinned atf267d1d43b9b6652831c7ceff8084a556a25480ePlus a cross-system signals audit (153 signals catalogued across the 3 source systems + reflect baseline) that surfaced 22 novel signal patterns — the 8 highest-impact promoted to
SG1-SG8ports below.Every port has clickable source-code permalinks to the exact commits researched. Companion explainer (5-system architecture comparison + porting playbook + signals catalogue): URL in comments.
0 · Why this epic exists
reflect-kb today: dual-indexed (nano-graphrag + QMD), $0-gated capture, hook-based auto-injection. Strong baseline. But leaves multiple assets idle and multiple signal types unexploited:
reflect.dbusage counters logged but never feed rankingHindsight, ByteRover, agentmemory, and claude-mem each solve a different subset. This epic ports the right pieces from each.
1 · Scope — what's IN, what's OUT
In scope: storage, retrieval, reflect-loop injection, consolidation/maintenance, signal capture.
Out of scope: Postgres/pgvector backend, REST API + SDK clients (Hindsight: 71 routes, agentmemory: 128, claude-mem: 59 worker + 16 server-beta), schema-per-tenant multi-tenancy (Hindsight + claude-mem server-beta), iii-engine Rust runtime (agentmemory), Bun daemon + Chroma subprocess (claude-mem), Hindsight disposition/banks.personality, ByteRover
<bv-*>HTML vocabulary, claude-mem's Smart Explore tree-sitter code search, claude-mem's BullMQ + BetterAuth + audit chain, agentmemory orchestration primitives (actions/leases/routines/sentinels), cloud team-sync.2 · Phasing
Wave 1 · Freebies (asset exists, wire it)
M5🆕 Commit-hash verification at write time ·M6🆕 Privacy tag stripping at LLM-prompt boundary ·SG5🆕 Agent tool-loop detectionR1Graph-expansion arm ·R4Token-budget retrieval (not top-k) ·R7OOD relevance gateWave 2 · ML upgrades
S4Provenance + proof_count first-class ·S5Belief revision on ingest (CREATE/UPDATE/DELETE) ·SG2🆕 Git event capture (post-commit / merge / revert) ·SG4🆕 Test outcome parsing from Bash tool output ·SG7🆕 TodoWrite completion as capture signalM1🆕 Enforced 3-layer search workflow with __IMPORTANT bootstrap tool ·R16Project-affinity multiplicative boost ·R2Cross-encoder rerank ·R3MMR diversity step ·R5Temporal retrieval arm ·R6Query-time date parsing ·R8Multiplicative bounded boosts ·R9Fuzzy cache tier ·SG6🆕 Negative recall as knowledge-gap signalM8🆕 Token-economics surfacing on every recall blockC1Per-ingest semantic-dedup adjudication ·M2🆕 Writer-agent output classifier + respawn circuit breakerWave 3 · Storage structuring
A1Pinned editable memory slots (agent-curated scratchpads) ·A3Per-row TTL (forgetAfterISO timestamp) ·M4🆕 Pluggable mode system with single-level inheritance (parent--override) ·S1Structured field extraction at drain ·S2Typed causal links between learnings ·S3Numeric confidence (0–1) ·S6History snapshots on UPDATE ·S9Volatile signals out of frontmatter ·SG1🆕 Cross-turn contradiction detection ·SG8🆕 Permission prompt + reply captureA4Followup-rate diagnostic (recall-quality self-monitor) ·R12Per-arm calibrated thresholdsO2Auto-refreshing conventions doc per project ·R103-tier hierarchical inject ·R11Forced-grounding short-circuit ·R13Auto-skill-refresh trigger ·R14Per-skill staleness flag ·R20Skills indexC2Auto-trigger consolidation on N learnings ·M3🆕 Subscription-quota-aware writer abort (RateLimitStore) ·O1Consolidated observations layer (persona/conventions aggregate) ·SG3🆕 Session idle trigger for natural reflectionWave 4 · Lifecycle & maintenance
A2Bitemporal graph edges (tcommit + tvalid) ·A6Branch-aware capture & isolation ·O3First-class persona/preference fields per scope ·S10Write-validate-retry on note body ·S7Delta retain / chunk-hash dedup ·S8Document → chunks → learnings groupingM7🆕 Knowledge-corpus Q&A (build → prime → query → reprime)R15Per-project shardingA5Synthetic compression fallback (deterministic, no-LLM) ·C3Graph maintenance pass ·C4Lifecycle webhooks / events ·C5KB export / import3 · Memory category breakdown (LoCoMo dimensions)
Of the 4 source systems, only Hindsight and ByteRover publish LoCoMo scores. agentmemory measures only LongMemEval-S retrieval R@5 (95.2%, retrieval-only). claude-mem publishes NO memory benchmarks — only Smart Explore (code navigation, not memory).
3.1 · Single-Hop Reasoning
Scoreboard. ByteRover 97.5% · Hindsight 86.2% · gap +11.3pp → ByteRover wins.
Why ByteRover wins. facts stored AS facts (typed
<bv-fact>HTML, discrete atoms not embedded paragraphs); BM25 with title 3× / path 1.5× boost nails exact lookup; Tier-0/Tier-1 caches catch repeats at ~0ms.Why Hindsight is weaker. fact-extraction LLM rephrases ('authentication mechanism is JWT' instead of 'JWT'); vector search drifts to similar-but-wrong facts; token-budget cut can drop single-fact answers.
Ports that improve this:
R7·R9·S1·S3·R23.2 · Multi-Hop Reasoning
Scoreboard. ByteRover 93.3% · Hindsight 70.8% · gap +22.5pp → ByteRover wins.
Why ByteRover wins. LLM authors
related="@domain/topic"cross-references AT CURATION TIME — graph hand-built by the smartest thing in the loop; controlled-vocab entity labels bind entities across topics deterministically; Tier 4 agentic loop IS graph traversal.Why Hindsight is weaker. surprising given typed memory_links graph — but consolidation collapses distinct facts into single observations, which is brilliant for compression but DESTROYS the multi-hop bindings; reflect agent gathers and synthesizes but doesn't chain.
Ports that improve this:
R1·S2·R3·R11·C13.3 · Temporal Reasoning
Scoreboard. ByteRover 97.8% · Hindsight 83.8% · gap +14.0pp → ByteRover wins.
Why ByteRover wins. system-managed createdat/updatedat NEVER agent-authored — reliable anchors that can't be poisoned by drift; compound score weight on recency = 0.2; maturity hysteresis preserves 'this WAS true' knowledge; tier 0-2 deterministic on dates.
Why Hindsight is weaker. sophisticated
event_datevsmentioned_atdistinction (when-it-happened vs when-you-learned-it) confuses recall — a fact learned today about something a year ago gets weird ranking; consolidation overwrites timestamps on merge → loses original temporal anchor.Ports that improve this:
R5·R6·R8·S63.4 · Open-Domain Knowledge
Scoreboard. ByteRover 85.9% · Hindsight 95.1% · gap +9.2pp → Hindsight wins.
Why Hindsight wins. consolidation distills raw facts into evidence-counted observations (proof_count, history, contradiction reconciliation) — 50 small 'user prefers X' mentions become one strong observation; mental_models are curated query-defined synthesized docs with auto-refresh; banks.disposition/mission/personality/background make persona a first-class object; 3-tier forced retrieval mental_models→observations→facts puts curated layer first.
Why ByteRover is weaker. topics are about specific things; no aggregation primitive — ByteRover's design says 'the LLM authors the structure' but persona is EMERGENT across topics, not authored as a topic;
dreammerges near-duplicates but doesn't aggregate up to persona.Ports that improve this:
S5·R10·R13·O1🆕 ·O2🆕 ·O3🆕4 · Capture signals — cross-system audit
Audited all 4 systems (Hindsight: 42 signals · ByteRover: 39 · agentmemory: 43 · reflect baseline: 29). Identified 22 novel signal patterns reflect-kb does NOT capture today. The 8 highest-impact are promoted to
SG1-SG8ports in section 5.Synthesis: Hindsight is rich on contradiction/consolidation signals (LLM-judged DELETE, semantic dedup at write-and-update time, mental-model staleness via tag-scope, cascade invalidation on source delete) and on quality guardrails inside the reflect agent (premature-done, hallucinated citations, leaked structured output). ByteRover dominates on tier-escalation patterns (5-tier query routing with confidence-based bypass), maturity hysteresis (draft/validated/core with 5-point gap), and operational primitives (per-project task queues, PID locks, post-task index regeneration). agentmemory is scar-tissue-driven: every signal traces to a real regression (#138/#143/#149/#221/#248/#554) and dominates on lifecycle/operational concerns (idle detection, 5-min SHA-256 dedup, TTL forgetAfter, vector-dimension guard, image refcount eviction, hook timeout caps). Reflect-kb's biggest gaps are (1) operational/recall-quality feedback loops (no followup-rate, no maturity tier, no contradiction detection, no stale-learning pruning), (2) tool-event signal extraction (no test-outcome parsing, no file-churn, no git-event capture, no permission-prompt capture), and (3) idle/stall lifecycle (Stop+PreCompact are the only triggers — no idle detection, no tool-loop detection). The highest-impact ports are agent-tool-loop-detection (gap #1), session-idle-trigger (gap #9), cross-turn-contradiction-detection (gap #2), fingerprint-dedup-with-reinforcement (the AKL promotion engine), and maturity-tier-with-hysteresis — together these add the missing closed-loop feedback that today's static-confidence learnings lack.
Full novel-signal catalogue (sorted by impact):
agent-tool-loop-detection— Same (tool_name + arg_hash) repeated >=3 consecutive times in a sliding window, OR A->B->A->B oscill...SG5cross-turn-contradiction-detection— Two memories/learnings/utterances on the same topic with Jaccard token similarity > 0.9 but contradi...SG1fingerprint-dedup-with-reinforcement— Same content fingerprint (sha256 of normalized lowercased text) on a newly-saved learning matches an...C1(existing)git-event-capture— post-commit hook fires with (sha, branch, message, author, files); checkout/merge/rebase/reset event...SG2maturity-tier-with-hysteresis— Learning's importance (cumulative reinforcements + recall-hits) crosses promote (65) / demote (60) /...A1(existing)recall-followup-rate-as-recall-quality-signal— Two recall queries from the same session within 30s, with different query text but disjoint result I...A4(existing)session-idle-trigger— Session has been quiet for N seconds with no new user prompts AND no tool calls (agentmemory uses Op...SG3stale-mtime-with-tier-aware-decay— Learning hasn't been recalled or reinforced in DRAFT_STALENESS_DAYS (60) for drafts / VALIDATED_STAL...R8(existing)test-outcome-parsing-from-tool-output— Bash tool output matches test runner patterns: pytest 'X passed, Y failed', jest 'Tests: X passed, Y...SG4adaptive-bisect-on-llm-failure— claude -p call fails after REFLECT_DRAIN_MAX_RETRIES (3) for a batched slice of N transcriptsfile-cochange-and-churn-detection— Same file edited >=3 times in one session (churn), OR set of files always edited together >=3 sessio...file-touched-snapshot-diff-after-reflect— Pre/post FileState (mtime/hash) snapshot of ~/.learnings/documents/ around each reflect run. Detect ...mental-model-staleness-via-scope— A stored learning's 'scope' (tag set) overlaps with new learnings ingested since last_refreshed_at. ...negative-recall-as-knowledge-gap-signal— recall.py returns 0 results for a query. Same normalized query returns 0 results >=2 times across se...SG6per-project-task-serialization-lock— Two concurrent reflect drains on the same project_pathpermission-prompt-and-reply-capture— Claude Code Notification hook fires with notification_type=='permission_prompt'. Capture the (tool, ...SG8premature-done-on-empty-evidence— Assistant emits 'done' / final answer without having called any recall/search tool in the current tu...tier-based-recall-escalation— Recall query confidence tier: top BM25 score >= 0.93 (direct cache)todo-status-completion-signal— TodoWrite tool call where one or more items transition to status=='completed'. Capture (completed_it...SG7control-char-and-surrogate-sanitization— Any text crossing ingress (user prompt, tool output, assistant message) containing chars in \x00-\x0...structured-output-leak-detection— Final learning text contains leaked structured artifacts: trailing JSON,done(...)call patterns, ...trigger-source-attribution— Every reflect cycle tagged with trigger ∈ {'stop', 'precompact', 'idle', 'manual', 'cli'} and confid...5 · Port catalogue (full detail, by layer)
Every port carries: What · Why · How {source} does it · How to port · Source references (clickable GitHub permalinks) · Files to touch · Acceptance · Depends on.
5.1 · Storage / write-time
M5🆕 · Commit-hash verification at write timeclaude-mem · Wave 1 · Effort Low · Impact Medium
What. After an LLM writes a learning or observation containing commit hashes (sha1 7-40 hex), verify each hash actually exists in the local repo (git cat-file -e). Mark unverified hashes in the row's metadata as hallucinated_refs, optionally drop the learning entirely if all refs are hallucinated.
Why. Reflect-kb commonly stores PR-adjacent learnings that cite commits. Verifying at write time catches LLM-fabricated commit references before they pollute the knowledge base, and generalizes to file:line / function-name existence checks. Cheap (single git command), high-trust payoff.
How claude-mem does it. src/sdk/commit-verification.ts (verifies commit hashes mentioned in observations exist in the repo; marks unverified ones in metadata before persistence)
How to port. 1) Add packages/reflect-core/src/validators/commit-verifier.ts: regex extract hex strings ≥7 chars, run git cat-file -e in the project cwd, tag results. 2) Hook into the write pipeline immediately before persistence. 3) Add generalized ref-verifier interface so file_exists / symbol_exists can plug in later. 4) Expose metadata.unverified_refs[] on every stored row so retrieval can downrank or surface a warning.
References:
commit-verification.ts:1—src/sdk/commit-verification.tsFiles to touch:
packages/reflect-core/src/validators/commit-verifier.tspackages/reflect-core/src/validators/ref-verifier.tspackages/reflect-core/src/store/write-pipeline.tsAcceptance:
M6🆕 · Privacy tag stripping at LLM-prompt boundaryclaude-mem · Wave 1 · Effort Low · Impact High
What. Canonical list of secret-marker tags (, , , <system_instruction>, ) are stripped from any payload before it leaves for an LLM provider. Belt-and-suspenders: a downstream validator additionally discards any learning whose entire source span was inside a block.
Why. Reflect-kb has no privacy primitive today — sensitive prompt content (API keys, internal URLs, customer data) wrapped in user-side markers still gets forwarded to the reviewer/writer model. This is two tiny composable safety nets: outbound scrub + post-extraction rejection. Critical before any team/enterprise mode.
How claude-mem does it. src/utils/tag-stripping.ts:1-30 (strip-list); src/services/worker/validation/PrivacyCheckValidator.ts:1-50 (rejects observations whose source is entirely private); src/server/generation/providers/shared/prompt-builder.ts:29 (applies stripping at provider boundary)
How to port. 1) Add packages/reflect-core/src/privacy/tag-stripper.ts with a configurable strip-list (defaults: private, reflect-context, system-reminder, system_instruction, persisted-output). 2) Wrap every outbound LLM call in writer/reviewer with the stripper. 3) Add privacy-check-validator.ts that inspects the source span of each extracted learning; if 100% inside a block, drop it. 4) Configurable via mode's privacy.strip_tags[] and privacy.reject_if_private_source.
References:
tag-stripping.ts:1—src/utils/tag-stripping.tsPrivacyCheckValidator.ts:1—src/services/worker/validation/PrivacyCheckValidator.tsprompt-builder.ts:29—src/server/generation/providers/shared/prompt-builder.tsFiles to touch:
packages/reflect-core/src/privacy/tag-stripper.tspackages/reflect-core/src/privacy/privacy-validator.tspackages/reflect-core/src/agents/writer-runner.tspackages/reflect-core/src/agents/reviewer-runner.tsAcceptance:
SG5🆕 · Agent tool-loop detectionByteRover · Wave 1 · Effort Low · Impact High
What. Per-session sliding window of (tool_name + arg_hash) tuples. Detect: same (tool,arg) repeated >=3 consecutive times, OR A->B->A->B oscillation >=2 cycles in last 10 calls. On detection, arm a stall signal so the user's correction becomes a mini-learning.
Why. Tool-loops are the 'agent is stuck' signal. The user almost always corrects them on the next prompt — that correction is the highest-signal learning in the session. Reflect already has the arm/fire pipeline for failures; this extends it to loops.
How ByteRover does it. ByteRover: per-project overlap lock + repeated-curate detection at src/oclif/lib/curate-session.ts. Pattern is general — any 'repeated same call' detector.
How to port. New plugins/reflect/scripts/loop_detector.py with per-session sliding window persisted to ~/.reflect/loops/.json. Called from posttooluse_minilearning.py BEFORE failure check. On detection, write ~/.reflect/armed/.json with reason='loop' so the UserPromptSubmit arming pipeline fires.
References:
curate-session.ts—src/oclif/lib/curate-session.tsFiles to touch:
plugins/reflect/scripts/loop_detector.py (new)plugins/reflect/hooks/posttooluse_minilearning.pyAcceptance:
S4· Provenance + proof_count first-classHindsight · Wave 2 · Effort Low · Impact High
What. Every learning carries
source_memory_ids: [list]+proof_count: int. CREATE starts at 1; UPDATE appends source + bumps count.Why. Precondition for belief revision (S5) and proof-count boost in ranking. Lets recall trust well-evidenced rules.
How Hindsight does it. Hindsight observations:
source_memory_ids+proof_count DEFAULT 1— seeo1a2b3c4d5e6_oracle_baseline.py:103-124. Proof-count boost:reranking.py:113, α=0.1 (±5%).How to port. Add fields to
reflect.db.learnings+ frontmatter. Cascade UPDATE path appends; CREATE inits to 1.References:
o1a2b3c4d5e6_oracle_baseline.py:103-124—hindsight-api-slim/hindsight_api/alembic/versions/o1a2b3c4d5e6_oracle_baseline.pyreranking.py:113—hindsight-api-slim/hindsight_api/engine/search/reranking.pyFiles to touch:
plugins/reflect/scripts/reflect_db.pyplugins/reflect/scripts/reflect_cascade.pyplugins/reflect/assets/learning_template.mdAcceptance:
S5· Belief revision on ingest (CREATE/UPDATE/DELETE)Hindsight · Wave 2 · Effort Medium · Impact High
What. Drain LLM emits structured actions — CREATE / UPDATE / DELETE — over existing related learnings, instead of always creating a new note. UPDATE merges as evidence (proof_count++, history append); DELETE retires stale state.
Why. Currently every drain creates a new note; the weekly Opus synthesis catches duplicates after they've been served for days. Per-ingest belief revision catches them at write. Hindsight's whole memory-system thesis hinges on this.
How Hindsight does it.
_consolidate_batch_with_llm:1932+_execute_update_action:1581+_execute_create_action:1705+_execute_delete_action:1744inengine/consolidation/consolidator.py. Prompt atprompts.py:22-145mandates 'prefer UPDATE over CREATE'.How to port. In
reflect_cascade.pypost-slice, recall related learnings; pass them into the drain prompt with explicit instruction to emit{action, target_id?, content}. Add UPDATE/DELETE execution paths to drain output handling.References:
consolidator.py:1932—hindsight-api-slim/hindsight_api/engine/consolidation/consolidator.pyconsolidator.py:1581—hindsight-api-slim/hindsight_api/engine/consolidation/consolidator.pyconsolidator.py:1705—hindsight-api-slim/hindsight_api/engine/consolidation/consolidator.pyprompts.py:22-145—hindsight-api-slim/hindsight_api/engine/consolidation/prompts.pyFiles to touch:
plugins/reflect/scripts/reflect_cascade.pyplugins/reflect/hooks/reflect-drain-bg.shplugins/reflect/skills/reflect/SKILL.mdAcceptance:
Depends on:
S4,S6SG2🆕 · Git event capture (post-commit / merge / revert)agentmemory + ByteRover · Wave 2 · Effort Medium · Impact High
What. Install a git post-commit hook that POSTs (sha, branch, message, files, author) to a /reflect/commit endpoint. Link commit SHA to current session_id. On merge-conflict resolution, treat as a high-confidence learning trigger. On revert, mark the reverted commit's session learnings as 'contradicted by revert'.
Why. A git commit is the strongest 'this was deliberate, this matters' signal in engineering. Today reflect captures nothing at commit time — the most thoughtful moments in a session leave no memory trace. Bonus: commit SHA + files give recall a strong project-affinity + temporal anchor (pairs with R16 + A2).
How agentmemory + ByteRover does it. agentmemory: src/hooks/post-commit.ts:24-95 (sha/branch/message capture, session_id linkage). ByteRover: VC commands with structured error codes (ERR_VC_MERGE_CONFLICT, ERR_VC_UNCOMMITTED_CHANGES) at src/oclif/commands/vc/.
How to port. New plugins/reflect/hooks/post_commit.sh installed by /reflect setup. Writes to ~/.reflect/commits.jsonl with {sid, sha, branch, message, files, ts}. New reflect.db table commit_links(sha, session_id, conflict_resolved). SessionStart hook enriches query with current branch + recent commits.
References:
post-commit.ts:24-95—src/hooks/post-commit.tsFiles to touch:
plugins/reflect/hooks/post_commit.sh (new)plugins/reflect/scripts/reflect_db.pyplugins/reflect/skills/recall/hooks/session_start_recall.pyAcceptance:
Depends on:
A6SG4🆕 · Test outcome parsing from Bash tool outputagentmemory · Wave 2 · Effort Medium · Impact High
What. Parse Bash tool_response with test-runner regex set (pytest 'X passed, Y failed', jest 'Tests: X passed, Y failed', cargo 'test result: ok. X passed; Y failed', go 'PASS|FAIL: TestX'). Per-session state machine tracks failure_count history. On 0->N failures (fix happened), arm a HIGH-confidence learning. On N->0 (regression), arm a contradiction signal. On all-pass after multiple failures, mark prior session learnings as 'validated' (tier promotion).
Why. Test outcomes are the highest-signal events in coding sessions — they're where 'this works' is provable. Today reflect treats Bash output as opaque. Test parsing turns the test runner into a feedback loop for memory.
How agentmemory does it. agentmemory: src/functions/observe.ts and the tool-output classifier infrastructure. ResponseProcessor pattern from claude-mem (src/services/worker/agents/ResponseProcessor.ts:26).
How to port. New plugins/reflect/scripts/test_outcome_parser.py with TEST_RUNNERS dict. Called from posttooluse_minilearning.py before failure-arm path on tool_name=='Bash'. State per-session in ~/.reflect/test-state/.json. Fix-confirmed transition writes learning with confidence=HIGH source='test-outcome'.
References:
observe.ts—src/functions/observe.tsResponseProcessor.ts:26—src/services/worker/agents/ResponseProcessor.tsFiles to touch:
plugins/reflect/scripts/test_outcome_parser.py (new)plugins/reflect/hooks/posttooluse_minilearning.pyAcceptance:
SG7🆕 · TodoWrite completion as capture signalagentmemory · Wave 2 · Effort Low · Impact Medium
What. On TodoWrite tool call detect items transitioning to status='completed'. Capture (completed_item.content, prior in_progress duration, files touched during the todo) as a candidate learning ('how I accomplished X'). Tag category=Process, confidence=MEDIUM.
Why. Todo completions ARE the structured moments in a session — the user explicitly marked something done. Today reflect ignores them. They're a free signal that bundles intent + execution + outcome.
How agentmemory does it. agentmemory: TodoWrite hook handling in src/hooks/post-tool-use.ts. Pattern: per-session todo state diff.
How to port. Extend posttooluse_minilearning.py to detect tool_name=='TodoWrite' and diff prior vs new todos for completed transitions. Per-session ~/.reflect/todo-state/.json maintains state. On completion, gather file events since item went in_progress, write learning candidate.
References:
post-tool-use.ts—src/hooks/post-tool-use.tsFiles to touch:
plugins/reflect/hooks/posttooluse_minilearning.pyplugins/reflect/scripts/todo_state.py (new)Acceptance:
A1· Pinned editable memory slots (agent-curated scratchpads)agentmemory · Wave 3 · Effort Medium · Impact High
What. A small fixed set of named, size-limited, agent-editable scratchpad slots:
persona,user_preferences,tool_guidelines,project_context,guidance,pending_items,session_patterns,self_notes. Each slot has scope (project|global), size cap, optional readOnly flag. Agent edits via append/replace/get/delete. Slots auto-inject ahead of any recall results.Why. Sits between skills (workflow-shaped, slow to refresh) and observations (aggregated from corrections, abstract). Slots are the fast scratchpad an agent reaches for mid-session. Closes a gap none of the other systems reflect-kb compares to has — Hindsight's mental_models are query-defined and refreshed by consolidation; ByteRover's topics are LLM-authored documents; reflect's notes are drain-authored. Slots are the AGENT's working memory.
How agentmemory does it. agentmemory
types.ts:201-211(MemorySlot type),functions/slots.ts(slot ops). Stop-hook reflect (mem::slot-reflect, gated by SLOTS=on) auto-appends discovered TODOs topending_items, counts patterns intosession_patterns, records touched files intoproject_context— deterministic, no LLM.How to port. reflect.db.slots(project_id, name, content, scope, size_limit, read_only, last_edited_at) + 8 default slot rows on init. Expose via skills:
memory_slot_*operations. Surfaced as Tier-0 in tiered inject (R10) — slots inject BEFORE skills/observations/notes. Auto-append from Stop hook (deterministic patterns only — no LLM).References:
types.ts:201-211—src/types.tsslots.ts—src/functions/slots.tsworking-memory.ts—src/functions/working-memory.tsFiles to touch:
plugins/reflect/scripts/reflect_db.pyplugins/reflect/skills/recall/hooks/session_start_recall.pyplugins/reflect/skills/recall/hooks/stop_reflect.pyplugins/reflect/skills/slots/SKILL.md (new)Acceptance:
Depends on:
R10A3· Per-row TTL (forgetAfterISO timestamp)agentmemory · Wave 3 · Effort Low · Impact Medium
What. Each learning carries an optional
forgetAfter: <ISO timestamp>field. When set, an hourly sweep deletes (or archives) learnings past their TTL. Writer can set TTL at create time: 'this is only valid for the current migration / sprint / quarter'.Why. Sprint-scoped or migration-scoped knowledge becomes durable. Today reflect notes live forever; nothing reminds the agent that 'avoid X service, it's down' was a 2-day incident note from 3 months ago. Per-row TTL lets the writer be explicit about scope. Cheap and orthogonal to decay.
How agentmemory does it. agentmemory
Memory.forgetAfter(types.ts:103) +mem::auto-forgethourly cron (functions/auto-forget.ts:24-65). Optional ISO timestamp; absent = permanent.How to port. Add
forget_aftercolumn to reflect.db.learnings + frontmatter field. Addreflect_forget_sweep.pyscript run by launchd cron. Drain prompt can ask 'is this time-bounded?' and emit forget_after.References:
types.ts:103—src/types.tsauto-forget.ts:24-65—src/functions/auto-forget.tsFiles to touch:
plugins/reflect/scripts/reflect_db.pyplugins/reflect/assets/learning_template.mdplugins/reflect/scripts/reflect_forget_sweep.py (new)launchd/com.reflect.forget.plist (new)Acceptance:
M4🆕 · Pluggable mode system with single-level inheritance (parent--override)claude-mem · Wave 3 · Effort Medium · Impact Medium
What. Declarative JSON mode files that bundle (a) observation/learning types, (b) concept tags, (c) system prompts, (d) per-type emoji/labels, (e) locale, with single-level inheritance via parent--override naming (e.g. code--ja inherits code). Lets the same memory backend serve engineering, legal study, email investigation, etc., without forking.
Why. Reflect-kb currently hardcodes the taxonomy (instinct, learning, behavioural). A mode layer makes the taxonomy + prompt + locale swappable per project so a downstream team can ship reflect-legal or reflect-medical or reflect-zh without modifying core. Also unlocks proper i18n — claude-mem ships 29 language modes this way.
How claude-mem does it. src/services/domain/ModeManager.ts:1-50 (single-level inheritance, parent--override resolution); plugin/modes/code.json:1-200 (types, concepts, work_emoji, prompt template); plugin/modes/code--ja.json (locale variant)
How to port. 1) Create packages/reflect-core/src/modes/ with mode-loader.ts implementing parent--override resolution. 2) Add modes/engineering.json declaring current taxonomy + prompts + tags. 3) Refactor reviewer/writer prompts to pull from the active mode rather than hardcoded constants. 4) Wire reflect_mode CLI: list/get/set/diff. 5) Project's .reflect/config.json selects a mode by id.
References:
ModeManager.ts:1—src/services/domain/ModeManager.tscode.json:1—plugin/modes/code.jsoncode--ja.json:1—plugin/modes/code--ja.jsonFiles to touch:
packages/reflect-core/src/modes/mode-loader.tspackages/reflect-core/src/modes/mode-schema.tspackages/reflect-core/src/modes/engineering.jsonpackages/reflect-core/src/agents/writer-runner.tspackages/reflect-cli/src/commands/mode.tsAcceptance:
S1· Structured field extraction at drainHindsight · Wave 3 · Effort Medium · Impact High
What. Drain LLM extracts typed fields per learning —
problem/root_cause/fix/rule/category/entities/causal_relations— into structured frontmatter, not a free-form note body.Why. Recall can return 'just the rule' instead of a paragraph. Massive context-efficiency win on injection. Same benefit ByteRover gets from
<bv-fact>/<bv-rule>without forcing a markup vocabulary.How Hindsight does it.
fact_extraction.py:_extract_facts_from_chunkin Hindsight emits a strict schema (who/what/when/where/why + fact_type + entities + causal_relations) via JSON-mode LLM. The prompt is selectivity-tuned ('useful in 6 months?').How to port. Extend the drain prompt in the cascade to emit a Zod-style schema. Store fields in note frontmatter; keep the prose body as the human-readable rationale. Add
recall.pyflag--field=ruleto return just one field.References:
fact_extraction.py—hindsight-api-slim/hindsight_api/engine/retain/fact_extraction.pyfact_extraction.py:503-557—hindsight-api-slim/hindsight_api/engine/retain/fact_extraction.pyFiles to touch:
plugins/reflect/skills/reflect/SKILL.md (prompt)plugins/reflect/scripts/output_generator.pyplugins/reflect/assets/learning_template.mdAcceptance:
rule/root_cause/fixfieldsS2· Typed causal links between learningsHindsight · Wave 3 · Effort Low · Impact Medium
What. Extend
.entities.yamlrelationships[].typefrom flat 'relates_to' to a closed enum:caused_by · causes · enables · prevents · contradicts · supersedes · part_of · uses.Why. Graph queries gain semantics. 'What enabled this fix?' / 'What does this rule prevent?' / 'What does this learning supersede?' — answerable from sidecars, not just unrelated edges.
How Hindsight does it. Hindsight
memory_linkstable — 7 link types:temporal · semantic · entity · causes · caused_by · enables · prevents. Used bylink_expansion_retrieval.py.How to port. Update
references/knowledge_format.mdschema +validate_sidecar.pyenum. Drain prompt asks for typed edges. Backfill existing sidecars withtype:'relates_to'.References:
models.py:244-284—hindsight-api-slim/hindsight_api/models.pylink_expansion_retrieval.py—hindsight-api-slim/hindsight_api/engine/search/link_expansion_retrieval.pyFiles to touch:
plugins/reflect/references/knowledge_format.mdplugins/reflect/scripts/validate_sidecar.pyplugins/reflect/skills/reflect/SKILL.mdAcceptance:
Depends on:
R1S3· Numeric confidence (0–1)Hindsight · Wave 3 · Effort Low · Impact Medium
What. Store learning confidence as a continuous float 0–1 (with HIGH/MED/LOW as display buckets), not the other way around.
Why. Finer-grained ranking + clean threshold gates. Hindsight has this exactly because the bucketed version loses information at the bin edges.
How Hindsight does it.
memory_units.confidence_score FLOAT 0..1. Hindsight maps to buckets only at API edges.How to port. Add
confidence_numto frontmatter +reflect.db.learnings. Migrate existing rows: HIGH→0.9, MED→0.6, LOW→0.3.recall.pyranking uses the float.References:
models.py—hindsight-api-slim/hindsight_api/models.pyFiles to touch:
plugins/reflect/scripts/reflect_db.py (migration)plugins/reflect/assets/learning_template.mdplugins/reflect/skills/recall/scripts/recall.pyAcceptance:
S6· History snapshots on UPDATEHindsight · Wave 3 · Effort Low · Impact Medium
What. When a learning gets updated, snapshot the old form into
learning_history(reflect.dbtable) + a.history.yamlsidecar for git visibility.Why. Belief revision becomes non-destructive. 'Why did we change this rule?' becomes answerable from the audit trail.
How Hindsight does it.
_append_observation_history:1540+ dedicatedobservation_historytable — see migrationa7b8c9d0e1f2.How to port. New
learning_historytable inreflect.db. Sidecar.history.yamlwritten alongside note on UPDATE.References:
consolidator.py:1540—hindsight-api-slim/hindsight_api/engine/consolidation/consolidator.pyFiles to touch:
plugins/reflect/scripts/reflect_db.pyplugins/reflect/scripts/output_generator.pyAcceptance:
S9· Volatile signals out of frontmatterByteRover · Wave 3 · Effort Low · Impact Medium
What. Move ALL ranking signals (importance, maturity, recall_count, helpful_count, ignored_count) FULLY out of note frontmatter into
reflect.db. Note markdown stays immutable after write.Why. No per-query churn on git-tracked files. Clean diffs (team-KB-friendly). ByteRover's whole 'sidecar' design exists for this.
How ByteRover does it. ByteRover
runtime-signals-schema.ts— separate per-machine sidecar store; frontmatter holds only semantic fields.How to port. Schema migration: drop volatile fields from frontmatter. Move into
reflect.db.learning_signals(learning_id, importance, maturity, …). Recall reads from DB.References:
runtime-signals-schema.ts—src/server/core/domain/knowledge/runtime-signals-schema.tsFiles to touch:
plugins/reflect/scripts/reflect_db.pyplugins/reflect/assets/learning_template.mdAcceptance:
Depends on:
S3SG1🆕 · Cross-turn contradiction detectionHindsight + agentmemory · Wave 3 · Effort Medium · Impact High
What. On every new learning write, compute concept tags and run Jaccard token similarity against recent in-scope learnings sharing >=1 concept. If similarity >0.9 AND a negation marker is present in exactly one of the two ('not'/'never'/'don't'), mark the older one is_latest=false and emit a contradiction audit event.
Why. Today reflect-kb stores both 'use foo' and 'never use foo' as independent learnings and ranks them by independent recency/confidence. The agent gets contradictory injection. Cross-turn contradiction detection IS reflect-kb's analog of Hindsight's belief revision — but at the transcript/correction layer rather than the consolidator-LLM layer (S5).
How Hindsight + agentmemory does it. Hindsight: LLM-judged DELETE in consolidator.py (engine/consolidation/consolidator.py:1374-1384). agentmemory: deterministic Jaccard>0.9 with concept-index pruning (src/functions/auto-forget.ts:64-145, 1000-row recency cap).
How to port. Add post-write hook in reflect_db.add_learning(): compute token set, query concept_index (new sqlite table) for recent learnings sharing >=1 concept, Jaccard>0.9 + negation regex => mark older is_latest=false + emit audit event. Surface in /reflect:status.
References:
consolidator.py:1374-1384—hindsight-api-slim/hindsight_api/engine/consolidation/consolidator.pyauto-forget.ts:64-145—src/functions/auto-forget.tsFiles to touch:
plugins/reflect/scripts/reflect_db.pyplugins/reflect/scripts/contradiction_detector.py (new)plugins/reflect/skills/reflect-status/SKILL.mdAcceptance:
Depends on:
S5SG8🆕 · Permission prompt + reply captureagentmemory · Wave 3 · Effort Low · Impact Medium
What. Two-phase capture mirroring the tool-failure arming pattern: Notification hook (type=permission_prompt) writes ~/.reflect/permission-armed/.json. Next UserPromptSubmit checks the armed file and captures the user's approval/deny (regex: 'yes always'/'no never'/'only for X') as HIGH-confidence learning.
Why. Permission moments are 'decision' moments — user grants or denies a sensitive action. The pattern of decisions is durable project policy ('user always denies bash:rm -rf in this project'). Today reflect captures nothing here.
How agentmemory does it. agentmemory: src/hooks/notification.ts:33-58 handles permission_prompt notification type.
How to port. New plugins/reflect/hooks/notification_reflect.py modeled on agentmemory's pattern, only acting on permission_prompt. Reply detection in user_prompt_submit_recall.py adds permission-reply regex set. Output to learnings with source='permission-pattern'.
References:
notification.ts:33-58—src/hooks/notification.tsFiles to touch:
plugins/reflect/hooks/notification_reflect.py (new)plugins/reflect/skills/recall/hooks/user_prompt_submit_recall.pyAcceptance:
A2· Bitemporal graph edges (tcommit + tvalid)agentmemory · Wave 4 · Effort Medium · Impact Medium
What. Every
caused_by/enables/preventslink carries TWO timestamps:tcommit(when reflect learned this relationship) andtvalid/tvalidEnd(when the relationship was true in the world). On supersession the old edge getssupersededBy+tvalidEndrather than being deleted.Why. Answers 'what was the architecture in April?' (tvalid filter) vs 'what did we know about the architecture in April?' (tcommit filter). Critical for codebase memory because design knowledge evolves: 'we used JWT in April, sessions in June'. Hindsight's history snapshots (S6) preserve evolution per-row; bitemporal extends that to RELATIONSHIPS, which is where most multi-hop reasoning errors come from in stale memory.
How agentmemory does it. agentmemory GraphEdge type at
types.ts:390-419— fieldstcommit,tvalid,tvalidEnd,supersededBy,version,isLatest. Graph queries can filter by either temporal axis. Bitemporal model is uncommon outside enterprise databases.How to port. Extend
.entities.yamlrelationships schema withtcommit(default = ingest time) and optionaltvalid/tvalid_endfields. nano-graphrag edges already carry properties — pipe these through. recall.py graph-arm (R1) gains a temporal filter param.References:
types.ts:390-419—src/types.tsFiles to touch:
plugins/reflect/references/knowledge_format.mdplugins/reflect/scripts/validate_sidecar.pyplugins/reflect/skills/recall/scripts/recall.pyplugins/reflect/scripts/reflect_db.pyAcceptance:
Depends on:
R1,S2,R6A6· Branch-aware capture & isolationagentmemory · Wave 4 · Effort Medium · Impact Medium
What. Detect git worktrees and segregate session storage + learning lineage per branch. A worktree at
feat/authand another atfeat/paymentof the same repo do not pollute each other's recall.Why. Critical for any multi-worktree workflow — which agents-in-a-box LITERALLY uses (
/.agents-in-a-box/worktrees/by-name/...). Today reflect-kb scopes by project name, which collapses all worktrees of the same repo into one bucket. Symptom: working onfeat/Aand getting injected learnings fromfeat/Bthat are wrong for current context.How agentmemory does it. agentmemory
/branch/detect,/branch/worktrees,/branch/sessionsendpoints +functions/branch-aware.ts. Sessions tagged with branch + worktree path; recall filters default to current branch.How to port. Extend per-project sharding (R15) with a branch dimension:
~/.learnings/shards/<project>/<branch>/. Detect viagit rev-parse --abbrev-ref HEAD+git worktree list. Default recall scope: current branch. Override flag for cross-branch search.References:
api.ts—src/triggers/api.tsbranch-aware.ts—src/functions/branch-aware.tsFiles to touch:
plugins/reflect/skills/recall/scripts/recall.pyplugins/reflect/scripts/ingest.pyplugins/reflect/skills/recall/hooks/session_start_recall.pyAcceptance:
Depends on:
R15O3· First-class persona/preference fields per scopeHindsight · Wave 4 · Effort Medium · Impact Medium
What. Per-project scope object carrying structured persona/preference fields: testing_style, commit_style, error_handling, naming_convention, review_strictness, etc. Schema is open-ended but typed. Fields are AGGREGATED from observations (O1) — not hand-authored.
Why. Open-domain queries can do field lookups (testing_style='TDD' → done) instead of running recall. Provides the 'disposition' that Hindsight's bank carries — but populated from corrections rather than declared up-front. Lets the agent know it's working on a TDD-strict codebase before any LLM call.
How Hindsight does it. Hindsight banks table with disposition JSONB ({skepticism, literalism, empathy 1-5}), background TEXT, mission, personality — first-class persona object per bank; updated via PATCH /banks/{id}.
How to port. reflect.db.project_persona table: (project_id, field_name, value, confidence, source_observation_ids[], last_updated). Drain LLM emits CREATE/UPDATE actions to populate these from raw corrections. Recall checks persona fields first on open-domain queries before falling back to observations or notes.
References:
models.py:287-300—hindsight-api-slim/hindsight_api/models.pyFiles to touch:
plugins/reflect/scripts/reflect_db.pyplugins/reflect/scripts/reflect_cascade.pyplugins/reflect/skills/recall/scripts/recall.pyplugins/reflect/references/persona_schema.yaml (new)Acceptance:
Depends on:
O1S10· Write-validate-retry on note bodyByteRover · Wave 4 · Effort Medium · Impact Low
What. After drain LLM writes a learning, validate the structure (required frontmatter fields, sidecar matches body claims). On invalid, re-prompt with errors inlined (MAX 3 attempts).
Why. Malformed learnings self-heal at write time instead of polluting the corpus.
How ByteRover does it. ByteRover
curate-session.ts—correct-htmlprompt loop with errors inlined, MAX_ATTEMPTS=4.How to port. Validation step in cascade after drain output; rerun drain prompt with
errors:[...]context; bail at 3 attempts → write withvalidated:falseflag.References:
curate-session.ts—src/oclif/lib/curate-session.tshtml-writer.ts—src/server/infra/render/writer/html-writer.tsFiles to touch:
plugins/reflect/scripts/reflect_cascade.pyplugins/reflect/scripts/validate_sidecar.pyAcceptance:
Depends on:
S1S7· Delta retain / chunk-hash dedupHindsight · Wave 4 · Effort Low · Impact Low
What. Hash transcript slices at the chunk level and skip re-reflecting on unchanged chunks across drain re-runs.
Why. Marginal but cheap. Belt-and-braces against re-reflecting on the same transcript twice when queue gets requeued.
How Hindsight does it.
_classify_chunk_diff:1543+_try_delta_retain:1562inengine/retain/orchestrator.py. Content-hash key per chunk.How to port. Already partially in
reflect_cascade.py(signal-set hash). Extend to slice-level. Persist inreflect.db.chunk_hashes.References:
orchestrator.py:1543—hindsight-api-slim/hindsight_api/engine/retain/orchestrator.pyorchestrator.py:1562—hindsight-api-slim/hindsight_api/engine/retain/orchestrator.pyFiles to touch:
plugins/reflect/scripts/reflect_cascade.pyplugins/reflect/scripts/reflect_db.pyAcceptance:
S8· Document → chunks → learnings groupingHindsight · Wave 4 · Effort Medium · Impact Low
What. Track
(transcript_id, slice_hash) → learning_ids[]so we can attribute multiple learnings back to one conversation and dedup across them.Why. Enables 'show me everything that came out of session X' + better cross-learning consolidation.
How Hindsight does it. Hindsight's
documents+chunks+consolidated_atstamping onmemory_units.How to port.
reflect.dbtables:transcripts(id, captured_at),transcript_chunks(transcript_id, hash, slice),chunk_learnings(chunk_hash, learning_id).References:
models.py—hindsight-api-slim/hindsight_api/models.pyFiles to touch:
plugins/reflect/scripts/reflect_db.pyplugins/reflect/scripts/reflect_cascade.pyAcceptance:
Depends on:
S75.2 · Retrieval / read-time
R1· Graph-expansion armHindsight · Wave 1 · Effort Low · Impact High
What. Add a third parallel retrieval arm that, given the top vector+BM25 hits, walks the entity/relationship graph to surface linked learnings the keyword/embedding arms miss.
Why. reflect-kb already builds an entity graph from
.entities.yamlsidecars — nano-graphrag has bothlocal(entity-neighborhood) andglobal(community) modes. recall.py never calls them. This is the single biggest 'connect the asset' win.How Hindsight does it. Hindsight runs 4 parallel arms (semantic / BM25 / graph / temporal) merged via RRF. The graph arm is
link_expansion_retrieval.pywith budget-bounded traversal (LOW=100, MID=300, HIGH=1000 units) —engine/search/retrieval.py:690.How to port. In
plugins/reflect/skills/recall/scripts/recall.py, add a 3rd thread to the existing ThreadPoolExecutor that runsreflect search <q> --mode local --format json. Pass the union of vector+BM25 ids as the seed scope. Merge intorrf_fuse. Cap traversal at N entities (config:recall.graph.budget).References:
retrieval.py:690—hindsight-api-slim/hindsight_api/engine/search/retrieval.pylink_expansion_retrieval.py—hindsight-api-slim/hindsight_api/engine/search/link_expansion_retrieval.pyFiles to touch:
plugins/reflect/skills/recall/scripts/recall.pyAcceptance:
reflect search --mode localfailsR4· Token-budget retrieval (not top-k)Hindsight · Wave 1 · Effort Low · Impact Medium
What. Return learnings until a token budget (default 4096) is consumed, not a fixed
--limit. Estimate tokens per learning by length / 4.Why. SessionStart injection size is currently unpredictable — 3 long learnings can blow the context budget; 3 short ones waste it. Token-budget makes the inject size predictable.
How Hindsight does it.
memory_engine.py:_filter_by_token_budget:4704. Default 4096.How to port. In
recall.pyafter rerank/MMR, replace[:limit]slice with a token-counting loop. Usetiktokenor simplelen/4estimate.References:
memory_engine.py:4704—hindsight-api-slim/hindsight_api/engine/memory_engine.pyFiles to touch:
plugins/reflect/skills/recall/scripts/recall.pyplugins/reflect/skills/recall/hooks/session_start_recall.pyAcceptance:
--max-tokens Nflag works--limitwhen budget not specifiedR7· OOD relevance gateHindsight + ByteRover · Wave 1 · Effort Low · Impact Medium
What. If the top fused score after rerank is below threshold (Hindsight
MINIMUM_RELEVANCE_SCORE=0.45), return empty or one-line 'no prior art'.Why. reflect injects top-3 at every SessionStart regardless of relevance. Most sessions there is no relevant prior art — current behaviour is pure noise. ByteRover explicitly signals OOD.
How Hindsight + ByteRover does it. Hindsight: per-arm minimum relevance + OOD short-circuit. ByteRover:
query-executor.tsOOD detection emits explicit 'outside scope' rather than weak matches.How to port. Min-score gate before
emit()insession_start_recall.py. Config:recall.ood.threshold.References:
retrieval.py—hindsight-api-slim/hindsight_api/engine/search/retrieval.pyquery-executor.ts—src/server/infra/executor/query-executor.tsFiles to touch:
plugins/reflect/skills/recall/hooks/session_start_recall.pyplugins/reflect/scripts/reflect_config.pyAcceptance:
M1🆕 · Enforced 3-layer search workflow with __IMPORTANT bootstrap toolclaude-mem · Wave 2 · Effort Medium · Impact High
What. Expose a mandatory staged-retrieval pipeline through MCP: a first-class __IMPORTANT tool that returns the workflow contract, followed by step-numbered tools (search → timeline → get_observations) with descriptions that herd LLM clients into a token-cheap recall pattern (~10x claimed savings).
Why. Reflect-kb's recall today is single-shot — the model either dumps too much context or has to write its own multi-step query plan. A staged workflow with compact ID-only indexes (50-100 tok/result) → anchored timeline → full hydrate (500-1000 tok/result) gives downstream agents a deterministic cheap retrieval discipline without prompt engineering at the caller site. The __IMPORTANT tool is a self-documenting contract surface.
How claude-mem does it. src/servers/mcp-server.ts:425-503 (4 tools: __IMPORTANT @425, search @460, timeline @484, get_observations @503 — descriptions are 'Step 1:', 'Step 2:', 'Step 3:')
How to port. 1) Add three reflect MCP tools: reflect_index(query, limit) returning [{id, title, score, project, date}] only; reflect_timeline(anchor_id|query, depth_before, depth_after); reflect_hydrate(ids[]) returning full learning + entity sidecars. 2) Add reflect_workflow tool returning the staged-recall contract as text. 3) Rewrite tool descriptions to use 'Step 1/2/3:' phrasing. 4) Update plugin/commands/recall.md to reference the new pipeline.
References:
mcp-server.ts:425—src/servers/mcp-server.tsmcp-server.ts:460—src/servers/mcp-server.tsmcp-server.ts:484—src/servers/mcp-server.tsmcp-server.ts:503—src/servers/mcp-server.tsFiles to touch:
packages/reflect-mcp/src/server.tspackages/reflect-mcp/src/tools/index.tspackages/reflect-mcp/src/tools/timeline.tspackages/reflect-mcp/src/tools/hydrate.tspackages/reflect-mcp/src/tools/workflow.tsplugin/commands/recall.mdAcceptance:
R16· Project-affinity multiplicative boostHindsight · Wave 2 · Effort Low · Impact Medium
What. Add a bounded multiplicative boost on the rerank score for learnings whose
project_idmatches the current session's project. Same-project hits get α boost (default +10%); cross-project hits unchanged. Soft affinity, not hard isolation — gems from sibling projects still surface, just down-ranked.Why. Today reflect-kb either has no project scope at all (one global KB) or strict per-project sharding (
R15/A6). Strict isolation hides cross-project insights you'd genuinely want — 'oh, we solved the same OAuth bug in project Y last quarter'. A soft affinity boost gives you BOTH: same-project default, cross-project still discoverable. Sits naturally in the bounded-boost stack (R8):combined = CE × recency × proof × project_affinity.How Hindsight does it. Synthesised from Hindsight's bounded-boost pattern (
apply_combined_scoringinengine/search/reranking.py:20) extended with a project-match dimension. None of the three source systems has this exact pattern — Hindsight uses bank_id for strict isolation, ByteRover uses spaces, agentmemory fail-closed agent scopes. The bounded-boost shape is Hindsight's; the project-match concept is the gap Stevie identified.How to port. Extend the rerank formula in
recall.pywithproject_match = 1 + α·(current_project == hit_project ? 1 : 0), default α=0.1 (±10%). Read current project fromgit rev-parseor env. Config:recall.boost.project_affinity_alpha(set to 0 to disable). When R15 (per-project sharding) is enabled the boost still helps within the--globalfallback path.References:
reranking.py:20—hindsight-api-slim/hindsight_api/engine/search/reranking.pyconfig.py—hindsight-api-slim/hindsight_api/config.pyFiles to touch:
plugins/reflect/skills/recall/scripts/recall.pyplugins/reflect/scripts/reflect_config.pyAcceptance:
Depends on:
R8R2· Cross-encoder rerankHindsight · Wave 2 · Effort Low · Impact High
What. After RRF, score (query, learning) pairs with a local cross-encoder (
cross-encoder/ms-marco-MiniLM-L-6-v2) for true semantic relevance. The existingconfidence × recency × tagsformula becomes a boost on the CE score, not the primary sort.Why. Vector cosine + BM25 match SURFACE features. A cross-encoder reads (query, candidate) jointly and scores actual relevance. This is where Hindsight's recall feels qualitatively sharper.
How Hindsight does it.
engine/search/reranking.py:CrossEncoderReranker(defaultcross-encoder/ms-marco-MiniLM-L-6-v2);apply_combined_scoring:20wraps it with bounded boosts.How to port. New
recall.pystep betweenrrf_fuseand the currentrerank. Load model once viasentence_transformers.CrossEncoder(already a transitive dep of nano-graphrag). Batch ~20 candidates. Persist model to~/.reflect/models/. ~50ms per call on CPU.References:
reranking.py:20—hindsight-api-slim/hindsight_api/engine/search/reranking.pycross_encoder.py—hindsight-api-slim/hindsight_api/engine/search/cross_encoder.pyFiles to touch:
plugins/reflect/skills/recall/scripts/recall.pyplugins/reflect/scripts/reflect_config.pyAcceptance:
R3· MMR diversity stepHindsight · Wave 2 · Effort Low · Impact High
What. After rerank, apply Maximal Marginal Relevance: pick top-1, then bias subsequent picks AWAY from it by embedding similarity. λ=0.7 typical.
Why. Currently SessionStart can inject 3 near-identical learnings. MMR fixes that — the 3rd slot goes to something complementary. Massive UX win for free.
How Hindsight does it. Hindsight applies MMR after the cross-encoder. Formula
pick = argmax( λ·rel(d,q) − (1−λ)·max_{d' in S} sim(d,d') )over remaining candidates.How to port.
mmr_select(candidates, embeddings, k, λ=0.7)inrecall.py. Reuse the mpnet embeddings already computed by nano-graphrag (cache them on the hit objects).References:
reranking.py—hindsight-api-slim/hindsight_api/engine/search/reranking.pyFiles to touch:
plugins/reflect/skills/recall/scripts/recall.pyAcceptance:
--no-mmrflag for benchmarkingDepends on:
R2R5· Temporal retrieval armHindsight · Wave 2 · Effort Low · Impact Medium
What. 4th parallel arm that filters/ranks by date window from the query.
Why. 'What did we decide last week?' should rank recency explicitly, not just decay everything by age.
How Hindsight does it.
retrieve_temporal_combined:371inengine/search/retrieval.py. Usessearch/temporal_extraction.pyto pull dates from the query.How to port. 4th thread in the ThreadPoolExecutor; filters
learningsby frontmatterarchived/updated_atagainst a parsed date range. Returns empty when no temporal signal.References:
retrieval.py:371—hindsight-api-slim/hindsight_api/engine/search/retrieval.pytemporal_extraction.py—hindsight-api-slim/hindsight_api/engine/search/temporal_extraction.pyFiles to touch:
plugins/reflect/skills/recall/scripts/recall.pyAcceptance:
Depends on:
R6R6· Query-time date parsingHindsight · Wave 2 · Effort Medium · Impact Medium
What. Parse natural-language dates ('last week', 'in march', 'before the rewrite') out of the query string into a
(start, end)range.Why. Pairs with the temporal arm — without it, only explicit ISO dates trigger temporal ranking.
How Hindsight does it.
search/temporal_extraction.pyin Hindsight. Multi-pass regex + relative-date resolver.How to port. Python
dateparserlibrary + a custom regex pass for codebase-flavoured phrases ('before X commit', 'last sprint'). Output:{start, end, confidence}or None.References:
temporal_extraction.py—hindsight-api-slim/hindsight_api/engine/search/temporal_extraction.pyFiles to touch:
plugins/reflect/skills/recall/scripts/temporal_extraction.py (new)plugins/reflect/skills/recall/scripts/recall.pyAcceptance:
R8· Multiplicative bounded boostsHindsight · Wave 2 · Effort Low · Impact Medium
What. Replace flat
confidence × recency × (1+tags)with Hindsight's pattern:CE × recency × temporal × proof_count, each boost CLAMPED ±5-10%.Why. Bounded modifiers prevent one signal dominating (e.g. a very recent low-quality note out-ranking an older high-quality one). Hindsight's exact formula is calibrated; copy the shape.
How Hindsight does it.
apply_combined_scoring:20inengine/search/reranking.py. recency α=0.2 (±10%), proof_count α=0.1 (±5%), eachboost = 1 + α·norm(x).How to port. Refactor
rerank()inrecall.pyto multiplicative bounded form. Add config for each α.References:
reranking.py:20—hindsight-api-slim/hindsight_api/engine/search/reranking.pyreranking.py:113—hindsight-api-slim/hindsight_api/engine/search/reranking.pyFiles to touch:
plugins/reflect/skills/recall/scripts/recall.pyAcceptance:
Depends on:
R2R9· Fuzzy cache tierByteRover · Wave 2 · Effort Low · Impact Low
What. Before vector search, check the recall cache with Jaccard-similarity over query tokens (not just exact-hash) — near-identical queries reuse the prior result.
Why. Sessions often re-query slight variants ('how does auth work' vs 'auth flow'). Pure latency / cost win.
How ByteRover does it. ByteRover
query-executor.tsTier 0/1 — exact + fuzzy cache, Jaccard token overlap threshold.How to port. Extend
~/.reflect/recall_cache/index with a token-set per key; lookup falls back to Jaccard ≥0.85.References:
query-executor.ts—src/server/infra/executor/query-executor.tssearch-knowledge-service.ts—src/agent/infra/tools/implementations/search-knowledge-service.tsFiles to touch:
plugins/reflect/skills/recall/scripts/recall.pyAcceptance:
SG6🆕 · Negative recall as knowledge-gap signalByteRover · Wave 2 · Effort Low · Impact Medium
What. On 0-result recall, append query to ~/.reflect/knowledge-gaps.jsonl. On repeat (>=2 sessions, normalized query), surface in /reflect:status as 'knowledge gap — users keep asking about X with no learnings'. Becomes a curation backlog.
Why. Negative recall is unused information — today an empty result is silent. Capturing it turns the recall path into a needs-list for what the KB should cover. Cheap, exposes blind spots.
How ByteRover does it. ByteRover: query-executor handles 0-result case by escalating tiers; tracking the 0-result pattern is novel here.
How to port. Add post-recall check in recall.py: if results==[], append to ~/.reflect/knowledge-gaps.jsonl with normalized query + session_id. Daily aggregator in reflect_status.py reads and counts; >=2 hits surfaces in review queue.
References:
query-executor.ts—src/server/infra/executor/query-executor.tsFiles to touch:
plugins/reflect/skills/recall/scripts/recall.pyplugins/reflect/scripts/reflect_status.py (extend)Acceptance:
A4· Followup-rate diagnostic (recall-quality self-monitor)agentmemory · Wave 3 · Effort Low · Impact Medium
What. Track recent searches per session. If a second search arrives within N seconds (default 30s) with a DISJOINT result set from the first, increment a
followupcounter. High followup rate = recall didn't satisfy the first time.Why. Empirical recall-quality signal that costs nothing. Today there is NO feedback loop telling us whether top-3 SessionStart inject was useful — we ship into the void. Followup-rate exposes when the rerank weights need tuning, when the graph arm needs more budget, when OOD threshold is too high. Auto-tuning input.
How agentmemory does it. agentmemory
mem:recent-searchesscope +functions/smart-search.tsfollowup detection (issue #771). Window configurable viaAGENTMEMORY_FOLLOWUP_WINDOW_SECONDS.How to port. reflect.db.recall_events already exists. Add a
followupflag computed when next recall in same session within 30s returns disjoint ids. Surface via/reflect:costskill alongside drain cost. Optionally drive auto-calibration (R12).References:
smart-search.ts—src/functions/smart-search.tsschema.ts—src/state/schema.tsFiles to touch:
plugins/reflect/scripts/reflect_db.pyplugins/reflect/skills/recall/scripts/recall.pyplugins/reflect/skills/cost/SKILL.mdAcceptance:
R12· Per-arm calibrated thresholdsHindsight · Wave 3 · Effort Medium · Impact Low
What. Each retrieval arm gets its OWN OOD threshold (vector cosine, BM25 score, graph budget are not comparable).
Why. A single global threshold mis-calibrates at least 3 arms. Per-arm thresholds tighten gating.
How Hindsight does it. Hindsight per-strategy
RECALL_STRATEGY_BOOSTSconfig + per-arm gating.How to port. Config:
recall.arm.<name>.min_score. Calibrate from a corpus sample (reflect calibrate-thresholds).References:
config.py—hindsight-api-slim/hindsight_api/config.pyFiles to touch:
plugins/reflect/scripts/reflect_config.pyplugins/reflect/skills/recall/scripts/recall.pyAcceptance:
Depends on:
R7M7🆕 · Knowledge-corpus Q&A (build → prime → query → reprime)claude-mem · Wave 4 · Effort High · Impact Medium
What. Saved-filter abstraction over the knowledge base: build_corpus(project, types, concepts, files, query, date_range) snapshots matching learnings into a JSON file; prime_corpus spawns a fresh Claude Agent SDK session with the corpus loaded as the system prompt; query_corpus does conversational Q&A against that session; reprime_corpus spins up a clean session when context drifts.
Why. Reflect-kb today is search-shaped — every recall is a fresh hybrid query. A corpus pattern lets users 'ask the auth subsystem' or 'ask the migration log' as a long-lived chat-style session, which is what humans actually want when researching their own code's history. Skill-rewrite emission (existing) updates agent behaviour; corpus Q&A is the read-side complement.
How claude-mem does it. src/services/worker/knowledge/KnowledgeAgent.ts:25-110 (prime + query lifecycle); src/services/worker/knowledge/CorpusBuilder.ts:25-50 (filter-to-snapshot); src/services/worker/knowledge/CorpusStore.ts (~/.claude-mem/corpora/ JSON files)
How to port. 1) Add packages/reflect-core/src/corpus/ with builder.ts (apply saved filter against the kb), store.ts (~/.reflect/corpora/.json), agent.ts (prime an Agent SDK session with the corpus as system prompt, hold open, expose query()). 2) Add reflect_corpus_ MCP tools mirroring claude-mem's. 3) reflect corpus CLI: build/list/prime/query/rebuild/reprime.
References:
KnowledgeAgent.ts:25—src/services/worker/knowledge/KnowledgeAgent.tsCorpusBuilder.ts:25—src/services/worker/knowledge/CorpusBuilder.tsFiles to touch:
packages/reflect-core/src/corpus/builder.tspackages/reflect-core/src/corpus/store.tspackages/reflect-core/src/corpus/agent.tspackages/reflect-mcp/src/tools/corpus.tspackages/reflect-cli/src/commands/corpus.tsAcceptance:
Depends on:
M15.3 · Reflect-loop / injection
M8🆕 · Token-economics surfacing on every recall blockclaude-mem · Wave 2 · Effort Low · Impact Medium
What. Every recall block injected at SessionStart shows three numbers per learning: discovery_tokens (original cost to find this info in the wild), read_tokens (cost to re-read the stored learning), savings_pct. Type-specific glyphs (⚒/⌕/⚖/⚠) make the value visually legible. Per-block totals roll up to a header line.
Why. Reflect-kb's injected context is opaque about its ROI — the user sees N learnings but no signal about why memory paid off. Surfacing discovery vs read tokens makes recall self-justifying (operators see that memory saved 12k tokens this session) and creates a feedback loop for tuning injection budgets.
How claude-mem does it. src/services/context/TokenCalculator.ts:1-50 (calculateTokenEconomics computes discovery/read/savings per observation); plugin/modes/code.json:6-58 (work_emoji per type: ⚒ bugfix, ⌕ discovery, ⚖ decision, ⚠ security_alert); src/services/context/ContextBuilder.ts:1-50 (renders into the injected block)
How to port. 1) Add packages/reflect-core/src/inject/token-economics.ts: record discovery_tokens at write time (sum of tool-result tokens + LLM-call tokens that produced the learning), read_tokens computed lazily as tokenize(rendered learning). 2) Update the SessionStart injection renderer to print per-row 'D:1.2k → R:140 (-88%)' alongside the type glyph. 3) Add mode-level work_emoji map. 4) Aggregate header: 'memory saved ~Xk tokens vs cold discovery'.
References:
TokenCalculator.ts:1—src/services/context/TokenCalculator.tsContextBuilder.ts:1—src/services/context/ContextBuilder.tscode.json:6—plugin/modes/code.jsonFiles to touch:
packages/reflect-core/src/inject/token-economics.tspackages/reflect-core/src/inject/renderer.tspackages/reflect-core/src/modes/mode-schema.tspackages/reflect-core/src/store/learning-row.tsAcceptance:
Depends on:
M4O2· Auto-refreshing conventions doc per projectHindsight · Wave 3 · Effort Medium · Impact High
What. Living ~/.learnings/projects//CONVENTIONS.md (or per-codebase analog) aggregating all open-domain learnings for that project. Auto-refreshes when underlying observations change. Surfaced as Tier-1 in the hierarchical inject (R10) for open-domain queries.
Why. Currently the agent has no single place to look up 'what does this project generally do' — it has to recall and synthesize every time. A pre-synthesized conventions doc is 0-token to retrieve and lands at SessionStart as ambient context. Hindsight's mental_models pattern.
How Hindsight does it. Hindsight mental_models table with trigger.refresh_after_consolidation flag; refresh_mental_model in memory_engine.py:9154 re-runs the model's source_query through reflect and stores fresh content; freshness check in compute_mental_model_is_stale:9813.
How to port. reflect.db.conventions_docs table: (project_id, query, content, last_refreshed_at, scope_tags). Generated by a curated query over observations (O1). Refreshed by the same trigger as R13 (auto-skill-refresh) but for conventions instead of skills. Symlinked into the project root so the agent can read it as a regular file.
References:
memory_engine.py:9154—hindsight-api-slim/hindsight_api/engine/memory_engine.pymemory_engine.py:9813—hindsight-api-slim/hindsight_api/engine/memory_engine.pyFiles to touch:
plugins/reflect/scripts/reflect_db.pyplugins/reflect/scripts/conventions_generator.py (new)plugins/reflect/skills/recall/hooks/session_start_recall.pyplugins/reflect/scripts/reflect_cascade.pyAcceptance:
Depends on:
O1,R10,R14R10· 3-tier hierarchical injectHindsight · Wave 3 · Effort Medium · Impact High
What. At SessionStart/UserPromptSubmit, retrieve in tiers — skills (curated) → consolidated learnings → raw notes — and prefer the highest tier with a strong hit. Inject lower tiers only if the top tier is empty or stale.
Why. reflect already promotes high-confidence corrections into skills, but injection treats everything as flat. Tiering means a polished skill always wins over a raw note covering the same ground.
How Hindsight does it.
reflect/agent.py:610— Hindsight forcessearch_mental_models → search_observations → recallin that order on early iterations.How to port.
session_start_recall.pyqueries skills index first (file globs over.claude/skills/), then learnings viarecall.py. Emit best-tier result. Need a 'skills' index — built on-demand from skill SKILL.md + tags.References:
agent.py:610—hindsight-api-slim/hindsight_api/engine/reflect/agent.pyFiles to touch:
plugins/reflect/skills/recall/hooks/session_start_recall.pyplugins/reflect/skills/recall/scripts/skill_index.py (new)Acceptance:
Depends on:
R20R11· Forced-grounding short-circuitHindsight · Wave 3 · Effort Medium · Impact Medium
What. If the tier-1 (skills) hit is fresh AND high-confidence, skip retrieval of lower tiers entirely — no extra recall.py call.
Why. On the common case (returning to a familiar workflow), session start does ONE skill lookup and is done. Zero noise, zero token, fast boot.
How Hindsight does it.
_all_mental_models_are_usable_and_fresh:305+ the short-circuit atagent.py:993-1003. The c255d35 commit is exactly this.How to port. Add
freshness_check(skill)→ bool. If true and rerank-score > threshold, return early.References:
agent.py:305—hindsight-api-slim/hindsight_api/engine/reflect/agent.pyagent.py:993-1003—hindsight-api-slim/hindsight_api/engine/reflect/agent.pyFiles to touch:
plugins/reflect/skills/recall/hooks/session_start_recall.pyAcceptance:
Depends on:
R10,R20R13· Auto-skill-refresh triggerHindsight · Wave 3 · Effort Medium · Impact High
What. When belief revision UPDATEs a learning that backs a skill (by tag/category overlap), mark the skill stale and queue regeneration in
pending_reflections.jsonl.Why. Skills currently get promoted from learnings once, then drift. Auto-refresh closes the back-reaction loop — skills stay in sync with the evolving corpus.
How Hindsight does it.
_trigger_mental_model_refreshes:1150in Hindsightconsolidator.py. Mental model has atrigger.refresh_after_consolidationflag.How to port. After S5 UPDATE/DELETE, look up skills whose
tagsoverlap learning tags; markskill.is_stale=trueinreflect.db.skills; enqueue refresh task that re-runs the/reflectskill-edit step.References:
consolidator.py:1150—hindsight-api-slim/hindsight_api/engine/consolidation/consolidator.pyFiles to touch:
plugins/reflect/scripts/reflect_cascade.pyplugins/reflect/scripts/reflect_db.pyplugins/reflect/skills/reflect/SKILL.mdAcceptance:
Depends on:
S5,R20R14· Per-skill staleness flagHindsight · Wave 3 · Effort Low · Impact Medium
What. Each skill row carries
last_refreshed_at,scope_tags[], and computedis_stale.is_staleflips true iff any in-scope learning hasupdated_at > last_refreshed_at.Why. Precondition for the inject short-circuit (R11) AND the refresh trigger (R13).
How Hindsight does it.
compute_mental_model_is_stale:9813inmemory_engine.py. Hindsight checks every relevant memory.How to port. Add
skillstable toreflect.db(id, name, path, scope_tags JSON, last_refreshed_at).is_stalecomputed on read.References:
memory_engine.py:9813—hindsight-api-slim/hindsight_api/engine/memory_engine.pyFiles to touch:
plugins/reflect/scripts/reflect_db.pyAcceptance:
R20· Skills indexHindsight · Wave 3 · Effort Low · Impact Medium
What. Maintain an index of installed skills with
(name, path, tags[], summary, last_refreshed_at)so retrieval can match queries against skills without a full file scan.Why. Precondition for tiered inject (R10), short-circuit (R11), and refresh trigger (R13). Currently there's no fast 'is there a skill for this query?' check.
How Hindsight does it. Hindsight
mental_modelstable is exactly this kind of index — name + content + tags + trigger + last_refreshed_at + reflect_response.How to port.
reflect.db.skillstable. Rebuild onreflect:ingestand on filesystem mtime changes under.claude/skills/.References:
models.py—hindsight-api-slim/hindsight_api/models.pyFiles to touch:
plugins/reflect/scripts/reflect_db.pyplugins/reflect/scripts/skill_index.py (new)Acceptance:
R15· Per-project shardingHindsight · Wave 4 · Effort Medium · Impact Low
What. Maintain a separate nano-graphrag index per project, not one global KB.
Why. Faster recall (smaller corpus per call) + clean per-project isolation. Reduces cross-project noise in injection.
How Hindsight does it. Hindsight
bank_idpartitioning + per-bank HNSW indexes (d5e6f7a8b9c0migration).How to port. Index dir keyed on project id. Recall.py defaults to current project shard; global flag to search across.
References:
d5e6f7a8b9c0_add_bank_internal_id_and_per_bank_hnsw.py—hindsight-api-slim/hindsight_api/alembic/versions/d5e6f7a8b9c0_add_bank_internal_id_and_per_bank_hnsw.pyFiles to touch:
plugins/reflect/skills/recall/scripts/recall.pyplugins/reflect/scripts/ingest.pyAcceptance:
~/.learnings/shards/<project>/--global5.4 · Consolidation & maintenance
C1· Per-ingest semantic-dedup adjudicationHindsight · Wave 2 · Effort Medium · Impact High
What. Before writing a new learning, query existing learnings by embedding cosine; if any is ≥ threshold (0.97), call a focused 1-by-1 LLM merge instead of letting both rows land.
Why. The weekly Opus pass catches dupes after they've been served for 6 days. Per-ingest catches them at write. Cheap LLM call relative to letting bad UX accumulate.
How Hindsight does it.
_dedup_adjudicate:141inengine/consolidation/consolidator.py. Cosine threshold 0.97 (configurable). One LLM call per pair.How to port. In
reflect_cascade.pyafter drain authors a new learning, query nano-graphrag for top-1 by cosine. If ≥ thresh, prompt drain with both + 'merge?' as a final step.References:
consolidator.py:141—hindsight-api-slim/hindsight_api/engine/consolidation/consolidator.pyFiles to touch:
plugins/reflect/scripts/reflect_cascade.pyAcceptance:
Depends on:
S5M2🆕 · Writer-agent output classifier + respawn circuit breakerclaude-mem · Wave 2 · Effort Medium · Impact High
What. Classify every output from a memory-writing LLM into {valid_xml, prose, idle, poisoned, malformed}, count consecutive invalids, and kill+respawn the writer subprocess after N=3 in a row. Surfaces silent writer drift as a visible respawn event rather than a slow degradation.
Why. Reflect's reviewer/writer agents currently fail silently — a model that drifts into prose or rejects the schema just stops producing usable output. A classifier-driven circuit breaker makes liveness explicit and bounds the blast radius of a degraded model session. Critical when reviewer-never-authored runs over long sessions or when self-hosted models are swapped in.
How claude-mem does it. src/services/worker/agents/ResponseProcessor.ts:26-60 (classifyObserverOutput → INVALID_OUTPUT_RESPAWN_THRESHOLD=3 → kill+respawn); src/sdk/output-classifier.ts (categories: prose, idle, poisoned, malformed)
How to port. 1) Add packages/reflect-core/src/agents/output-classifier.ts with the same five categories. 2) Wrap reviewer + writer invocations in a small state machine that increments a per-session invalid counter, resets on valid output, and triggers a 'respawn' event (re-create the agent session, log a structured event) on threshold breach. 3) Emit a reflect:writer-drift hook so operators can subscribe.
References:
ResponseProcessor.ts:26—src/services/worker/agents/ResponseProcessor.tsoutput-classifier.ts:1—src/sdk/output-classifier.tsFiles to touch:
packages/reflect-core/src/agents/output-classifier.tspackages/reflect-core/src/agents/writer-runner.tspackages/reflect-core/src/agents/reviewer-runner.tspackages/reflect-core/src/events/writer-drift.tsAcceptance:
C2· Auto-trigger consolidation on N learningsHindsight · Wave 3 · Effort Low · Impact Medium
What. Trigger the weekly Opus synthesis EARLY when N (default 30) new learnings have landed in any project scope.
Why. Quiet projects → less compute. Active projects → fresher KB without waiting a week.
How Hindsight does it. Hindsight
enable_auto_consolidation+ counter in bank config triggerssubmit_async_consolidation.How to port. Counter in
reflect.db.metrics; cron + drain check triggerreflect_synthesis.py --since N learnings.References:
consolidator.py—hindsight-api-slim/hindsight_api/engine/consolidation/consolidator.pyconfig.py—hindsight-api-slim/hindsight_api/config.pyFiles to touch:
plugins/reflect/scripts/reflect_synthesis.pylaunchd/com.reflect.synthesis.plistplugins/reflect/scripts/metrics_updater.pyAcceptance:
M3🆕 · Subscription-quota-aware writer abort (RateLimitStore)claude-mem · Wave 3 · Effort Low · Impact Medium
What. Subscribe to the Claude Agent SDK's rate_limit system events (fields: five_hour, seven_day, seven_day_opus, surpassedThreshold, isUsingOverage) and abort further writer LLM calls before the user hits a hard quota wall. Cache the state across invocations within a TTL.
Why. Reflect-kb users on Claude Max plans currently hit cliffs mid-session with no warning. Reading the SDK's own quota telemetry lets the writer self-throttle and surface 'memory deferred — quota near limit' instead of failing opaquely. Zero additional API cost — events are already in the SDK stream.
How claude-mem does it. src/services/worker/RateLimitStore.ts:1-50 (subscribes to rate_limit system events, fields five_hour|seven_day|seven_day_opus|surpassedThreshold|isUsingOverage, aborts ingest before quota wall)
How to port. 1) Add packages/reflect-core/src/runtime/quota-store.ts that listens for rate_limit system events on the Agent SDK stream. 2) Persist the latest rate_limit_info to ~/.reflect/quota.json with a 60s TTL. 3) Before any writer LLM call, consult the store; if surpassedThreshold is true and isUsingOverage is false, skip and enqueue a deferred-write marker. 4) Expose CLI: reflect quota status.
References:
RateLimitStore.ts:1—src/services/worker/RateLimitStore.tsFiles to touch:
packages/reflect-core/src/runtime/quota-store.tspackages/reflect-core/src/runtime/writer-gate.tspackages/reflect-cli/src/commands/quota.tsAcceptance:
O1· Consolidated observations layer (persona/conventions aggregate)Hindsight · Wave 3 · Effort Medium · Impact High
What. Drain emits TWO output streams: raw corrections (today's behaviour) AND aggregated observation-style summaries that accumulate evidence over time. Each observation has proof_count + source_correction_ids + history. Distinct from skills: skills are workflow-shaped (how to do X); observations are persona/convention-shaped (what this team/codebase generally prefers).
Why. Open-domain queries — 'what conventions does this codebase use?' / 'what does this team prefer?' — currently surface 5 raw corrections and force the agent to aggregate them in-context. Hindsight wins LoCoMo Open-Domain (95.1% vs ByteRover 85.9%) because observations are pre-aggregated. Reflect-kb has no equivalent today.
How Hindsight does it. Hindsight memory_units with fact_type=observation, populated by consolidation/consolidator.py:_process_memory_batch:1245 — LLM batch emits CREATE/UPDATE/DELETE actions to maintain a separate semantic layer over the raw facts.
How to port. Extend belief revision (S5) so that after writing/updating raw learnings, drain queries existing observations in same scope, then emits a second-pass CREATE/UPDATE/DELETE over the observations. Frontmatter type field: type:correction vs type:observation. Stored in reflect.db.observations with proof_count and source_correction_ids[]. Retrieval treats them as a separate tier.
References:
consolidator.py:1245—hindsight-api-slim/hindsight_api/engine/consolidation/consolidator.pyo1a2b3c4d5e6_oracle_baseline.py:98-141—hindsight-api-slim/hindsight_api/alembic/versions/o1a2b3c4d5e6_oracle_baseline.pyFiles to touch:
plugins/reflect/scripts/reflect_cascade.pyplugins/reflect/scripts/reflect_db.pyplugins/reflect/skills/reflect/SKILL.mdplugins/reflect/assets/observation_template.md (new)Acceptance:
Depends on:
S4,S5SG3🆕 · Session idle trigger for natural reflectionHindsight + agentmemory · Wave 3 · Effort Medium · Impact High
What. Daemon/cron watches transcript mtime; after IDLE_THRESHOLD_SEC (default 600s) with no new events, enqueue with trigger='idle' for compression. Tag idle reflections as 'speculative' (lower confidence) since session may resume.
Why. Today reflect captures only at Stop or PreCompact — explicit session ends. But many real sessions go quiet (user steps away, switches context). Without idle detection those become 'lost' reflection opportunities. Pairs naturally with auto-consolidation trigger (C2).
How Hindsight + agentmemory does it. agentmemory: OpenCode 'idle' status event in src/triggers/api.ts. Hindsight: op-alive checks in worker poller.
How to port. New plugins/reflect/hooks/idle_reflect.sh launched via launchd plist. Walks ~/.claude/projects// transcript mtimes > threshold. Enqueues via stop_reflect.py pipeline with trigger='idle'. Confidence tagged 'speculative' downstream.
References:
api.ts—src/triggers/api.tsFiles to touch:
plugins/reflect/hooks/idle_reflect.sh (new)plugins/reflect/launchd/com.reflect.idle.plist (new)plugins/reflect/scripts/reflect_gate.pyAcceptance:
Depends on:
C2A5· Synthetic compression fallback (deterministic, no-LLM)agentmemory · Wave 4 · Effort Medium · Impact Low
What. When the drain LLM is unavailable (budget exceeded, network down, AGENTMEMORY_AUTO_COMPRESS=false), produce a structured learning record using heuristics only: tool input/output → title; keyword extraction → concepts; file-path regex → files affected; rule table → importance score. Always indexes successfully even with zero LLM tokens.
Why. Today if the drain LLM call fails, reflect captures nothing — the correction is lost. A synthetic fallback means EVERY signal contributes to the index, even when the drain is rate-limited or budget-poisoned. Less precise than the LLM path but the index is more complete. Pairs naturally with reflect's $0-gating philosophy.
How agentmemory does it. agentmemory
functions/compress-synthetic.ts— toggled by AGENTMEMORY_AUTO_COMPRESS=false (which is the default). Still produces a CompressedObservation with required fields for BM25 + vector indexing.How to port. Add
synthetic_compress(slice, signals)function to reflect_cascade.py. Used when (a) drain budget exhausted, (b) drain LLM errored 3 times, (c) explicit --no-llm flag. Stores learning withcompression=syntheticflag so rerank can de-prioritize.References:
compress-synthetic.ts—src/functions/compress-synthetic.tsconfig.ts:368-371—src/config.tsFiles to touch:
plugins/reflect/scripts/reflect_cascade.pyplugins/reflect/scripts/synthetic_compress.py (new)Acceptance:
C3· Graph maintenance passHindsight · Wave 4 · Effort Low · Impact Low
What. Post-delete sweep: relink top-up for nodes that lost neighbors + orphan entity prune (entities with no learning refs) + stale cooccurrence prune.
Why. Keeps the graph clean as learnings are deleted/superseded. Recall budgets stay accurate.
How Hindsight does it.
engine/graph/graph_maintenance.py:1-34— 3-pass cleanup after deletes.How to port. Extend
graphml_repair.pywith--maintainmode: scan for orphan entities, recompute relink, prune stale edges.References:
graph_maintenance.py:1-34—hindsight-api-slim/hindsight_api/engine/graph/graph_maintenance.pyFiles to touch:
plugins/reflect/scripts/graphml_repair.pyhooks/reflect-drain-bg.shAcceptance:
Depends on:
S5C4· Lifecycle webhooks / eventsHindsight · Wave 4 · Effort Low · Impact Low
What. Emit JSONL events on key lifecycle moments:
learning.created,learning.updated,skill.refreshed,consolidation.completed. Optional shell hook fires on each.Why. Other tools can react — auto-update CLAUDE.md, ping Slack on team-relevant rules, append digest. Hindsight ships this as the webhook delivery service.
How Hindsight does it. Hindsight
webhooks+webhook_deliveriestables with outbound HTTP service, exponential-backoff retries, transactional outbox pattern.How to port. Append-only
~/.reflect/events.jsonl. Config:events.on.<event>: <command>runs a shell hook per event.References:
http.py:5706—hindsight-api-slim/hindsight_api/api/http.pyFiles to touch:
plugins/reflect/scripts/reflect_events.py (new)plugins/reflect/scripts/reflect_cascade.pyAcceptance:
C5· KB export / importHindsight · Wave 4 · Effort Low · Impact Low
What.
reflect kb export <tarball>snapshots~/.learnings/+ relevantreflect.dbtables;reflect kb importrestores.Why. Move reflect-kb between machines without re-running every drain. Team sharing primitive.
How Hindsight does it. Hindsight admin-cli
export-bank/import-bankwith REPEATABLE-READ snapshot, optional re-embed without LLM re-extraction.How to port. Two scripts: tar
~/.learnings/documents/+ sidecar +reflect.db(filtered). Import validates schema + reindexes graphrag.References:
cli.py—hindsight-api-slim/hindsight_api/admin/cli.pyFiles to touch:
plugins/reflect/scripts/kb_export.py (new)plugins/reflect/scripts/kb_import.py (new)Acceptance:
6 · Roadmap (prioritised)
M5🆕M6🆕SG5🆕R1R4R7S4S5S4,S6SG2🆕A6SG4🆕SG7🆕M1🆕R16R8R2R3R2R5R6R6R8R2R9SG6🆕M8🆕M4C1S5M2🆕A1R10A3forgetAfterISO timestamp)M4🆕S1S2R1S3S6S9S3SG1🆕S5SG8🆕A4R12R7O2O1,R10,R14R10R20R11R10,R20R13S5,R20R14R20C2M3🆕O1S4,S5SG3🆕C2A2R1,S2,R6A6R15O3O1S10S1S7S8S7M7🆕M1R15A5C3S5C4C57 · Capability map — what recall will support after porting
R2rerankR1,S2R2R3R4R5+R6+R8+A2+A3+S6R16R15+A6R7R10+R11+R14+R20S5+S4+S6SG1O1+O2+O3A1slotsA4followup-rateSG6M1M2M3M4M5M6M7M8SG2SG3SG4SG5SG7SG8A5C4C58 · Picking up from a brand-new session
R1,R4,R7,S4,M5(commit-verification),M6(privacy stripping),SG5(tool-loop detection). All low-effort, mostly-already-have-the-pieces wins.feat/recall-<ID>-<slug>.R2R3S4S5R16C1+ newM1M2M8SG1SG2SG4SG6SG7) is the inflection point.A6(branch-aware) +SG2(git event capture) are the highest-value pair — this repo runs as multiple worktrees with frequent commits.M5commit-verification,M6privacy stripping,M13-layer MCP workflow.SG2git-event-capture,SG4test-outcome-parsing,SG5tool-loop-detection.9 · References
vectorize-io/hindsight@c255d35campfirein/byterover-cli@faf456drohitg00/agentmemory@7d01d057cda86fb191747f9f9d2f7e9064f2fed2thedotmack/claude-mem@f267d1d43b9b6652831c7ceff8084a556a25480eplugins/reflect/,~/.learnings/,~/.reflect/.10 · Changelog
O1/O2/O3(open-domain ports). Total: 34.A1–A6. Total: 40.R16. Added clickable source-code references per port. Total: 41.M1–M8(claude-mem patterns: 3-layer MCP retrieval, output-classifier + respawn, RateLimitStore, pluggable modes, commit verification, privacy stripping, corpus Q&A, token-economics surfacing). AddedSG1–SG8(cross-system signals audit: contradiction, git events, idle trigger, test outcomes, tool loops, negative recall, todo completion, permission prompts). Added full Section 4 capture-signals audit (153 signals catalogued, 22 novel). Total: 57 ports.All reactions