Skip to content

fix(session): scope write tracking by session, and stop wiping live sessions - #71

Open
liveoakwag wants to merge 1 commit into
cytostack:mainfrom
liveoakwag:fix/session-scoped-write-tracking
Open

fix(session): scope write tracking by session, and stop wiping live sessions#71
liveoakwag wants to merge 1 commit into
cytostack:mainfrom
liveoakwag:fix/session-scoped-write-tracking

Conversation

@liveoakwag

Copy link
Copy Markdown

.wolf/hooks/_session.json is one file per project, shared by every concurrent session. Two consequences, and the second is why the first was never visible.

1. Records carry no owner. Every "Session end: N writes across M files" row in memory.md reports the union of all live sessions. The same applies to the cerebrum-freshness and semantic-summary thresholds, the STATUS-freshness check, and the token ledger — each asks "what did this session do" and was answered with everyone's data. edit_counts is keyed by path alone, so the "edited 3+ times" reminder names files the current session never opened.

2. session-start resets files_written and edit_counts whenever a non-continuing session starts — which, with several agents open, is whenever anyone starts one. That deletes the history of every session already running. Scoping alone would have been cosmetic: the records would be attributed correctly and then thrown away before anything read them.

The change

  • post-write stamps each record with the harness session_id and namespaces the edit_counts key with it.
  • stop scopes once at the top and passes the result down — deliberately not assigning it back onto session, because that object is written back to the shared file, so narrowing it in place would delete the other sessions' records and cause the very thing being fixed.
  • session-start carries other sessions' records across the reset. The bound on growth is deliberately generous on both axes, because a bound whose job is to cap a file must not become a way to lose live data: 48h (a session running longer is implausible; 12h is not) and a count ceiling far above any real concurrent load, newest-first. edit_counts keys have no timestamp, so liveness is derived from whether their session still has a carried write.

Records predating this carry no sid and are ignored rather than shared out — attributing an unowned record to every session is precisely the defect, and the population is self-clearing once post-write stamps them.

Verification

  • node --test tests/session-write-scoping.test.ts4/4
  • npm test30/30, 0 fail
  • npx tsc --noEmit — clean apart from the pre-existing src/daemon/cron-engine.ts(52,27) TS2503
  • git am onto pristine main (f64e737) applies cleanly, suite still 30/30
  • Control: reverting the three sources and rebuilding makes all four cases fail

The test drives the real hooks as child processes — none of this reproduces inside a single process, since the whole defect is about what several processes sharing one file do to each other. Like #70 it loads from dist/ and builds on demand, because the hooks import each other with relative .js specifiers that type-stripping does not rewrite; same open question for you there.

Independent of #66, #69 and #70 — cut from main, depends on none of them.

🤖 Generated with Claude Code

…essions

`.wolf/hooks/_session.json` is one file per project, shared by every concurrent
session. Two consequences, and the second is why the first was never visible.

1. `files_written` records carry no owner, so every "Session end: N writes across
   M files" row in memory.md reports the UNION of all live sessions. The same
   applies to the cerebrum-freshness and semantic-summary thresholds, the
   STATUS-freshness check and the token ledger - each asks "what did THIS session
   do" and was answered with everyone's data. `edit_counts` is keyed by path
   alone, so the "edited 3+ times" reminder names files the current session never
   opened.

2. `session-start` resets `files_written` and `edit_counts` whenever a
   non-continuing session starts - which, with several agents open, is whenever
   anyone starts one. That deletes the history of every session already running.
   Scoping alone would have been cosmetic: the records would be attributed
   correctly and then thrown away before anything read them.

post-write now stamps each record with the harness `session_id` and namespaces
the edit_counts key with it. stop scopes once, at the top, and passes the result
down - deliberately NOT assigning it back onto `session`, because that object is
written back to the shared file and narrowing it in place would delete the other
sessions' records, i.e. cause the very thing being fixed.

session-start carries other sessions' records across the reset. The bound on
growth is deliberately generous on both axes, because a bound whose job is to cap
a file must not become a way to lose live data: 48h (a session running longer is
implausible; 12h is not) and a count ceiling far above any real concurrent load,
applied newest-first. edit_counts keys have no timestamp, so their liveness is
derived from whether their session still has a carried write.

Records predating this carry no `sid` and are ignored rather than shared out -
attributing an unowned record to every session is precisely the defect, and the
population is self-clearing once post-write stamps them.

tests/session-write-scoping.test.ts drives the real hooks as child processes,
since none of this reproduces inside a single process. Reverting the three
sources and rebuilding makes all four cases fail.

  node --test tests/session-write-scoping.test.ts   4/4
  npm test                                          30/30
  npx tsc --noEmit                                  clean except the pre-existing
                                                    src/daemon/cron-engine.ts
                                                    TS2503, present on an
                                                    unmodified checkout too

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant