Skip to content

capture what a session learned without asking it to - #33

Merged
ayushcodes10 merged 1 commit into
mainfrom
fix/deterministic-memory-capture
Sep 2, 2026
Merged

capture what a session learned without asking it to#33
ayushcodes10 merged 1 commit into
mainfrom
fix/deterministic-memory-capture

Conversation

@ayushcodes10

Copy link
Copy Markdown
Owner

The measurement

From 2026-08-23 to 2026-09-02 the session-start briefing told every session in every project:

N memory file(s) are queued but not yet recorded as facts. Run echo-memory pending, read each, and write_episode what it states.

In that window 16 memory files were written across eigen and dugout. The capture hook noticed all 16. None became facts in the session that wrote them — they were ingested by hand, ten days late, from a different project.

The instruction was correct, specific, and lost 16 times out of 16. It was competing with Claude Code's own file-based memory, which is a permanent system-prompt section present on every turn, while this was one reminder at session start among several hundred registered tools. Everything that worked in that window was a hook.

What changed

Stop hookscripts/session-stop-hook.sh, cli/stop_gate.py. Returns {"decision": "block"} when this project still owes the graph, handing the agent the file list as its next instruction. It fires at the one moment the agent still holds the context to say what it learned and is no longer mid-task. Bounded three ways so it doesn't become the hook people disable:

  • once per session (stop_hook_active), so a nudge never becomes a loop
  • scoped to the current project — eigen's backlog can't hold an echo-mem session open
  • silent when the queue is clean, which is the steady state

echo-memory reconcile — reads the filesystem instead of trusting that the hook fired. Three eigen files were found holding content the queue had never seen; nothing detected it, because a queue only knows what it was told. Runs from SessionStart and again inside the stop gate, so a missed capture self-heals. Deliberately the same scope as the capture hook — if the two disagreed, the sweep would either keep reopening files the hook ignores or keep missing files it queues.

echo-memory install-hooks — registers all five hooks in one command instead of four JSON blocks merged by hand. That shape half-completes silently, and a machine missing one hook is indistinguishable from a machine missing none. Entries are matched by marker and by script name, so the first run on an existing machine upgrades the hand-merged hooks rather than doubling them (every hook firing twice, every file noticed twice).

Migration 0009read_event has carried group_id since it shipped one day ago, which is the same string for every Claude Code session in every repo. Asked "did the eigen sessions read memory?", the table had no answer: 12 rows, all identically labelled. It now records project and session_id, both nullable, because an unattributed read is worth more than an exception between the user and the agent.

Testing

  • 387 existing tests pass; 26 added
  • migration 0009 verified to apply and reverse from a fresh process — the standalone path that let 0007 pass CI while broken
  • gate tests cover the real bug shape: a file ingested, then edited by a session whose hook didn't fire

Scope

The Stop gate only reaches clients that support hooks, so it helps Claude Code and not Claude Desktop. That limit is real and unchanged by this PR.

Between 2026-08-23 and 2026-09-02 the session-start briefing told every
session, in every project, to drain the capture queue: "run `echo-memory
pending`, read each, and write_episode what it states." In that window 16
memory files were written across eigen and dugout. The capture hook noticed
all 16. None became facts in the session that wrote them - they were ingested
by hand, ten days late, from a different project.

The instruction was correct and specific and lost 16 times out of 16. It was
competing with Claude Code's own file-based memory, which is a permanent
system-prompt section present on every turn, while this was one reminder at
session start among several hundred registered tools. Everything that worked
in that same window was a hook.

So capture stops asking:

- Stop hook (scripts/session-stop-hook.sh, cli/stop_gate.py) returns
  {"decision": "block"} when this project still owes the graph, handing the
  agent the file list as its next instruction. It fires at the one moment the
  agent still holds the context to say what it learned. Bounded three ways so
  it does not become the hook people disable: once per session via
  stop_hook_active, scoped to the current project, silent when clean.

- reconcile (cli/reconcile.py) reads the filesystem rather than trusting the
  hook fired. Three eigen files were found holding content the queue had never
  seen; nothing detected that, because a queue only knows what it was told. It
  runs from SessionStart and again inside the stop gate, so a missed capture
  self-heals instead of being permanent. Same scope as the capture hook, so
  the two cannot disagree about what counts as a memory.

- install-hooks (cli/hooks.py) registers all five in one command instead of
  four JSON blocks merged by hand. That shape half-completes silently, and a
  machine missing one hook is indistinguishable from a machine missing none.
  Entries are matched by marker *and* by script name, so the first run on an
  existing machine upgrades the hand-merged hooks rather than doubling them.

Also 0009: read_event has carried group_id since it shipped, which is the same
string for every Claude Code session in every repo. Asked "did the eigen
sessions read memory?" the table had no answer. It now records project and
session_id, both nullable, because an unattributed read is worth more than an
exception between the user and the agent.

387 existing tests still pass; 26 added.
@ayushcodes10
ayushcodes10 merged commit 1b7b6e0 into main Sep 2, 2026
2 checks passed
@ayushcodes10
ayushcodes10 deleted the fix/deterministic-memory-capture branch September 2, 2026 04:22
@github-actions github-actions Bot locked and limited conversation to collaborators Sep 2, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant