Skip to content

guard the user-action ledger's read path, not just its write path - #5847

Merged
atomantic merged 1 commit into
mainfrom
fix/user-actions-guard-followups
Sep 2, 2026
Merged

guard the user-action ledger's read path, not just its write path#5847
atomantic merged 1 commit into
mainfrom
fix/user-actions-guard-followups

Conversation

@atomantic

Copy link
Copy Markdown
Owner

Summary

Follow-up to #5627 (merged), which landed the #5605 structural guard on the user-action ledger's write path. Three minor items surfaced in that review but weren't worth another round-trip with the contributor.

  • The read path was unguarded. makeFileBackend().list called loadFileEvents() with no assertion, so an un-redirected suite could still pull the developer's live ledger into the test process via listUserActions. Those rows are machine-local operator records the privacy ADR keeps off the wire — an untethered suite shouldn't read them either.
  • The guard test could leave a mutated real ledger behind. It snapshotted the pre-existing file's bytes but its cleanup only ever deleted a file it had created. On a MEMORY_BACKEND=file install, a guard regression appended the probe event to the developer's real ledger and left it there. Cleanup now restores the snapshot when the bytes changed, and still only deletes a file this run leaked.
  • assertTestDataRootRedirected now takes the attempted operation as an argument, so the read path doesn't report itself as attempted a file-backend write.
  • server/lib/README.md — the dataRoot.js row enumerates the module's exports and was missing resolveCodeRootForModule (added in test: guard user-action-events file-backend writes against leaking to real data/ #5627). The barrel test only matches the filename, so this wasn't caught.

Test plan

  • userActionsDataRootGuard.test.js grows a read-path case; both cases bypass-probed — removing either assertion turns the corresponding test red, so neither passes vacuously
  • Every listUserActions / recordUserAction caller suite (routes/userActions, routes/cosTaskRoutes, routes/cos, services/cosToolRegistry, services/userActionReviewHooks, services/persistentMindUserActions, services/settings*, services/taskSchedule, services/cos*, lib/index, lib/dataRoot) — 680/680 pass
  • Full cd server && npm test — 37,400 pass, zero guard firings across the suite (so no existing suite is falsely tripped by guarding reads) and no data/user-action-events.json after the run
  • Confirmed with a seeded real ledger that a guard regression now leaves the file byte-identical instead of appended-to

#5627 landed the #5605 structural guard on the file backend's `record`
path, so an un-redirected suite can no longer write user-action-events.json
into the developer's live data/ tree. `list` was left unguarded, so such a
suite could still READ the live ledger — machine-local operator records the
privacy ADR keeps off the wire and out of untethered test processes.

Also hardens the guard test's own cleanup: it snapshotted the pre-existing
file's bytes but only ever deleted a file it had created, so a guard
regression on a MEMORY_BACKEND=file install left the developer's real
ledger with the probe event appended. It now restores the snapshot.

The guard message takes the attempted operation as an argument so the read
path doesn't report itself as a write.
@atomantic
atomantic enabled auto-merge (rebase) September 2, 2026 14:41
@atomantic
atomantic merged commit 9848cd2 into main Sep 2, 2026
7 checks passed
@atomantic
atomantic deleted the fix/user-actions-guard-followups branch September 2, 2026 14:46
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