Skip to content

test: open, seed and read the agent-session record store through one test harness - #23986

Merged
brennanb2025 merged 2 commits into
mainfrom
brennanb2025/record-store-test-helper
Sep 30, 2026
Merged

brennanb2025 merged 2 commits into
mainfrom
brennanb2025/record-store-test-helper

Conversation

@brennanb2025

@brennanb2025 brennanb2025 commented Sep 30, 2026 •

Copy link
Copy Markdown
Contributor
Files Added Deleted Net
Test 73 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​333 $\color{#cf222e}{\Huge{\mathbf{−}}}$​404 $\color{#cf222e}{\Huge{\mathbf{−}}}$​71
Prod 8 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​134 $\color{#cf222e}{\Huge{\mathbf{−}}}$​39 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​95

ELI5

The app keeps a small durable list of every chat's session ownership (the "agent-session record store"). Today it lives in a file called agent-sessions.json. A planned change moves it into the chat database the app already has. About 75 test files open, fill or read that store directly, so that move would have to edit every one of them in the same PR. This PR is a test-only step first: every one of those tests now goes through one small test harness, so the storage move only has to change the harness.

This PR is step D0 of: R0 #23964 → L′-a #23978 → D0 (this) → D.

What Changed

Nothing a user sees or does is different. The only production edit moves the private 'agent-sessions' folder-name constant from the runtime (structured-agent-session-runtime.ts) into agent-session-record-store-file.ts as AGENT_SESSION_STORE_DIR_NAME, so the harness can share it without importing the runtime. Same value, same paths, no behaviour change. Everything else is test files and test-support files.

New file src/main/runtime/agent-session-record-store-test-harness.ts, built from what the existing tests actually do. Every function takes the host's state directory: the directory the chat database and the recovery file also live in, which is what the running app passes around. The harness owns the agent-sessions subfolder inside it exactly as the running app does, so the storage move can open the store on the same database the test already uses. Callers pass that state directory instead of a store-only store subfolder; tests that have no other state pass their temp directory as before.

  • openTestAgentSessionRecordStore(stateDirectory, { hostId? }): open, or reopen, the store (122 call sites that called AgentSessionRecordStore.open({ directory, hostId: 'local' }) now call this).
  • seedTestAgentSessionRecordStore(stateDirectory, { records }): leave records behind as an earlier run of the app would, before anything opens the store (replaces 4 hand-written copies of the JSON file layout).
  • readPersistedTestAgentSessionStore(stateDirectory): read back what the store committed, typed (records, operation rows, retired keys, chat tabs).
  • readPersistedTestAgentSessionStoreText(stateDirectory): the same as text, for "this secret never reached disk" and "this action wrote nothing" checks.
  • editPersistedTestAgentSessionStore(stateDirectory, edit): change the committed state behind the store's back, as an older build or a damaged disk would (used by the older-build lease fixture and the record-quarantine test).
  • seedTestAgentSessionStoreFromNewerBuild(stateDirectory): a store written by a newer build, which this build reads but never writes.
  • testAgentSessionStoreFilePath(stateDirectory): where the store's file is, only for the format tests listed below that share a directory with harness calls.

Two tests now read the store the running app itself writes, instead of reaching into its folder by hand: the integration test that checks a secret never reached disk, and the spawn-token descendants test that checks nothing is killed. Before, they named the app's agent-sessions folder directly, so after the storage move they would have read an empty separate store and passed without checking anything.

Migrated: 78 test and test-support files, including the shared host, rest, queued-message and restart-interruption rigs, the older-build lease fixture, the two tests/e2e unit tests that use the store as ordinary setup, and the two mobile tests that open it. Each test asserts exactly what it did before; only the way it reaches the store changed.

Left as they are on purpose, because the JSON file's format is itself what they test, so the storage move deletes or rewrites them rather than keeping them through a harness:

  • agent-session-backup-recovery.test.ts, agent-session-unreadable-record-salvage.test.ts, agent-session-record-store-security.test.ts, agent-session-record-unsupported-schema.test.ts, agent-session-recovery-publish-fault.test.ts (the .bak copy, salvage, file permissions, schema versions, the save/load functions).
  • tests/e2e/cross-version-wire/agent-session-death-evidence-downgrade.unit.test.ts and agent-session-unproven-release-downgrade.unit.test.ts (an older release reads the file this build wrote).
  • structured-agent-session-runtime.test.ts (checks the production path the runtime opens).
  • Parts of seven mixed files: the corrupt-file and .bak cases in agent-session-record-store.test.ts; the byte comparisons of legacy lease values in agent-session-record-store-legacy-handoff-lease.test.ts (it opens the store and finds the file through the harness because it shares a directory with the older-build lease fixture, but still reads the JSON file); the tab table read from an older store, a stale record field, and a malformed table in agent-session-record-store-tab-table.test.ts; "an older build drops the table" in structured-chat-tab-table.test.ts; the visibleSessionIds check in structured-agent-session-rollback-compatibility.test.ts; the unreadable-launch-payload case (which rewrites both the file and its .bak) in agent-launch-replay.test.ts; and the file-rename fault injection in structured-agent-session-settled-attach-retry.test.ts.

Why

The storage move is one reviewable change only if it does not also carry about 800 lines of mechanical edits across 78 test files. Doing the mechanical part first, with no behaviour change, means the move's diff shows only what actually changes: how the harness opens, seeds and reads the store.

Alternative considered: change each test inside the storage-move PR. Rejected because it buries the real change under unrelated edits, and a reviewer cannot tell a mechanical edit from a changed assertion.

Alternative considered: also route the format tests above through the harness. Rejected because their assertions are about the JSON file (its bytes, its .bak, what an older release parses), so no harness can keep them meaningful once the file is no longer written. Listing them here tells the storage move exactly what it must delete or rewrite.

No "simulate an unreadable store" helper was added: every test that does that is a JSON or .bak format test from the list above, and the storage move changes what an unreadable store does (it is reported and the app carries on, rather than refusing to open).

Linked Issue

None. Precursor for the planned record-store move (step D above).

Visual Proof

N/A: test-only change, nothing visible or interactive changed.

Testing

  • I manually tested these changes locally
  • Automated tests added/updated, or explained why not below

Ran every affected test file explicitly (103 files in the root suite: every file that touches the store, plus every consumer of the shared rigs and fixtures) before and after.

  • Before (origin/main fb67d5d7c3f): 863 tests passed. One cross-version file hit a 45 s archive-extraction timeout in the parallel batch and passed alone (17/17).
  • After: 862 of 863 passed in the full batch. The one timeout (structured-agent-session-refusal-retry.test.ts, a 90 s test on a heavily loaded machine) passes alone in 17 s, the same as the origin/main copy of that file (18.5 s). Its only change is the one-line store open.
  • The two mobile tests could not run locally (this checkout has no mobile dependencies installed); CI covers them. Their change is the same one-line store open.
  • After moving the harness to the state directory: 96 affected files, 838 of 840 passed in the full batch. The two failures were timeouts on a loaded machine (structured-agent-session-claude-compact-stop.test.ts, a 1 s wait; structured-agent-session-refusal-retry.test.ts, the same 90 s test as above) and both pass alone.
  • oxlint on all changed files and pnpm run check:code-quality:changed: clean.
  • Typecheck: pnpm tc:node passes.

Review

Agent skill upstream boundary

  • Not applicable, or this change follows docs/reference/agent-skill-sharing-upstream-boundary.md and copies or mechanically translates no upstream skill-installer source, tests, fixtures, registry entries, path tables, comments, or documentation.

Notes

Security, cross-platform, SSH, mobile, backwards compatibility and performance: not affected, since the only production edit moves a constant without changing its value or any path built from it. The harness builds paths with path.join through the existing store helpers.

Checklist

  • This PR is small and focused
  • I explained what changed and why (ELI5, the user-facing before/after, the mechanism, and why over the alternatives)
  • Before/after screenshots or videos attached for UI changes, or N/A with reason
  • Self-reviewed for correctness, security, and performance
  • Cross-platform, SSH/remote, and path/shortcut impact considered (or N/A)
  • pnpm lint, pnpm typecheck, pnpm test, and pnpm build pass (or CI will cover; local preferred)

…harness

Tests that open the durable agent-session record store, seed it, or read
back what it persisted now go through agent-session-record-store-test-harness.ts
instead of calling AgentSessionRecordStore.open or touching agent-sessions.json
themselves. A later change that moves the store into the chat database then
changes the harness instead of every test. No production code changes.

Tests whose subject is the JSON file itself (its .bak recovery, salvage,
schema versions, permissions, and what older builds read back) keep reading
and writing the file directly; the storage move rewrites or deletes them.
The harness took the store's own folder, so each caller picked one
(join(root, 'store'), or 'agent-sessions' where a test read the store the
runtime owns). A later change that moves the store into the state
directory's journal database could not tell those apart, and would have
had to edit every caller again.

Every harness function now takes the state directory, the one the test's
journal database and recovery capsule already live in, and keeps the
store in the same subfolder the runtime uses. Callers pass that directory;
store-only tests pass their temp directory unchanged. Format tests that
share a directory with harness calls take the file path from
testAgentSessionStoreFilePath.

The folder name moves from a private constant in the runtime to
AGENT_SESSION_STORE_DIR_NAME beside the store's file name, so the harness
shares it without importing the runtime. Its value and every path built
from it are unchanged.
@brennanb2025

Copy link
Copy Markdown
Contributor Author

Review status: ready for review (not merged)

The problem. The planned storage move (step D) moves the agent-session record store out of agent-sessions.json and into the chat database the app already keeps. About 75 test files open, seed or read that store directly, so that move would otherwise have to edit all of them in the same PR. This PR routes those tests through one test harness first, so step D only has to change the harness.

What changes for users. Nothing. It is a test-only change. The one production edit moves the private 'agent-sessions' folder-name constant into agent-session-record-store-file.ts as AGENT_SESSION_STORE_DIR_NAME. Its value and every path built from it are unchanged.

Fixed during review (ed88d25)

  • The harness was addressed by a store-only store subfolder rather than the directory the running app uses (where the chat database lives). After the storage move, tests could not have shared the rig's database connection without re-editing about 60 call sites, and the integration "secret never reached disk" test and the spawn-token "nothing is killed" test would have read an empty separate store and passed without checking anything. The harness now takes the host's state directory and owns the agent-sessions subfolder exactly as the app does. Callers pass the state directory, and both of those tests now read the store the app itself writes.
  • PR description corrected: the one production edit, the legacy-lease test now being a mixed file, and the accurate list of tab-table cases left on direct file access.

Deferred (P3, not worth changing here)

  • The harness's store type still has schemaVersion, hostId and unusableRecords, which the storage move won't keep. No caller reads them, so step D can narrow the type without touching callers.
  • The store folder path join(state, AGENT_SESSION_STORE_DIR_NAME) is built in three places (two in the runtime, one in the harness) and could be one exported function.
  • newStoreDirectory in the recovery-resolution test now returns a state directory, so its name is out of date.

Verified

  • Every migrated test asserts exactly what it did before. The per-file expect( counts are identical on main, the original head and the final head for all 66 changed test files, and no assertion was weakened. 44 files differ from the original head only by the mechanical join(X,'store') to X change. The rest differ only by removed aliases and imports, the new path helper, and one added mkdir.
  • In every rig, the directory passed to the harness is the one the test roots its chat database, recovery file or runtime at, and nothing else in those directories collides with agent-sessions.
  • Every remaining direct use of the store file is in the description's intentional list of format tests.
  • Local runs: tc:node typecheck passed, 96 affected test files 838/840, oxlint and the changed-code quality check clean. The two local failures were load timeouts, refusal-retry (90 s) and a 1 s vi.waitFor in compact-stop. Both pass when run alone and on main. They are not caused by this PR.
  • CI green at ed88d25, including Mobile Checks, which ran both mobile test files (pass) and the mobile tests-typecheck ratchet, plus cross-version wire, typecheck, static analysis and all unit shards.
  • Readiness checklist: pass, no candidates.

Not verified

  • No Electron or mobile UI QA, because nothing a user sees or does changed.
  • Whether the harness actually works against the SQLite-backed store can only be proven when step D is built. The review checked the fit against the step D design on paper.

@brennanb2025
brennanb2025 marked this pull request as ready for review September 30, 2026 03:54
@greptile-apps

greptile-apps Bot commented Sep 30, 2026 •

Copy link
Copy Markdown

RetriggerConfidence Score: 5/5

[Low risk] Test harness refactoring for record store initialization.

The PR appears safe to merge; no actionable regression was identified.

Summary

This PR centralizes test setup and inspection of the agent-session record store in a state-directory-based harness. The runtime continues to use the same store directory name; migrated tests open, seed, and read through the shared helpers.

Reviews (1) · Last reviewed commit: "test: address the record-store harness b..."

@coderabbitai

coderabbitai Bot commented Sep 30, 2026

Copy link
Copy Markdown
Contributor

Review in Change Stack →

Navigate logical layers of code changes, visualize relationships, and explore their blast radius.

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Advanced

Run ID: 8b8310ce-ea25-4430-a088-58a8b8bc4de4

📥 Commits

Reviewing files that changed from the base of the PR and between 45c63a6 and ed88d25.

📒 Files selected for processing (81)
  • mobile/src/tasks/agent-launch-mobile-replay.test.ts
  • mobile/src/tasks/mobile-agent-launch-architecture.test.ts
  • src/main/claude/claude-api-retry-idle-sweep.test.ts
  • src/main/codex/codex-structured-question-order.test.ts
  • src/main/native-chat/agent-session-wire/structured-agent-session-accept-then-deliver.test.ts
  • src/main/native-chat/agent-session-wire/structured-agent-session-acquisition-options.test.ts
  • src/main/native-chat/agent-session-wire/structured-agent-session-adopted-import.test.ts
  • src/main/native-chat/agent-session-wire/structured-agent-session-append-delivery.test.ts
  • src/main/native-chat/agent-session-wire/structured-agent-session-claude-compact-stop.test.ts
  • src/main/native-chat/agent-session-wire/structured-agent-session-claude-echo-working.test.ts
  • src/main/native-chat/agent-session-wire/structured-agent-session-claude-option-queue.test.ts
  • src/main/native-chat/agent-session-wire/structured-agent-session-claude-queued-stop.test.ts
  • src/main/native-chat/agent-session-wire/structured-agent-session-claude-root-exit.test.ts
  • src/main/native-chat/agent-session-wire/structured-agent-session-codex-stop-row.test.ts
  • src/main/native-chat/agent-session-wire/structured-agent-session-conversation-stop.test.ts
  • src/main/native-chat/agent-session-wire/structured-agent-session-crash-mid-start.test.ts
  • src/main/native-chat/agent-session-wire/structured-agent-session-crash-turn-end.test.ts
  • src/main/native-chat/agent-session-wire/structured-agent-session-failed-create-owner-verdict.test.ts
  • src/main/native-chat/agent-session-wire/structured-agent-session-failed-create-sink-release.test.ts
  • src/main/native-chat/agent-session-wire/structured-agent-session-grouped-prompt.test.ts
  • src/main/native-chat/agent-session-wire/structured-agent-session-host-test-abandon.test.ts
  • src/main/native-chat/agent-session-wire/structured-agent-session-host-test-harness.ts
  • src/main/native-chat/agent-session-wire/structured-agent-session-host.test.ts
  • src/main/native-chat/agent-session-wire/structured-agent-session-import-mismatch.test.ts
  • src/main/native-chat/agent-session-wire/structured-agent-session-late-settlement.test.ts
  • src/main/native-chat/agent-session-wire/structured-agent-session-lease-renewer.test.ts
  • src/main/native-chat/agent-session-wire/structured-agent-session-legacy-handoff-record.test.ts
  • src/main/native-chat/agent-session-wire/structured-agent-session-main-agent-working-agreement.test.ts
  • src/main/native-chat/agent-session-wire/structured-agent-session-option-settlement.test.ts
  • src/main/native-chat/agent-session-wire/structured-agent-session-owed-work-release.test.ts
  • src/main/native-chat/agent-session-wire/structured-agent-session-pre-spawn-first-answer.test.ts
  • src/main/native-chat/agent-session-wire/structured-agent-session-processless-reservation.test.ts
  • src/main/native-chat/agent-session-wire/structured-agent-session-provider-child-record.test.ts
  • src/main/native-chat/agent-session-wire/structured-agent-session-provider-restore.test.ts
  • src/main/native-chat/agent-session-wire/structured-agent-session-provider-started.test.ts
  • src/main/native-chat/agent-session-wire/structured-agent-session-queued-message-rig.test-fixture.ts
  • src/main/native-chat/agent-session-wire/structured-agent-session-recovery-exits.test.ts
  • src/main/native-chat/agent-session-wire/structured-agent-session-recovery-resolution.test.ts
  • src/main/native-chat/agent-session-wire/structured-agent-session-refusal-retry.test.ts
  • src/main/native-chat/agent-session-wire/structured-agent-session-rest-test-rig.ts
  • src/main/native-chat/agent-session-wire/structured-agent-session-restart-interruption-test-harness.ts
  • src/main/native-chat/agent-session-wire/structured-agent-session-restart-status-publication.test.ts
  • src/main/native-chat/agent-session-wire/structured-agent-session-rewind-at-rest.test.ts
  • src/main/native-chat/agent-session-wire/structured-agent-session-rewind.test.ts
  • src/main/native-chat/agent-session-wire/structured-agent-session-send-open-stale-turn.test.ts
  • src/main/native-chat/agent-session-wire/structured-agent-session-send-preparation.test.ts
  • src/main/native-chat/agent-session-wire/structured-agent-session-send-restarts-failed-start.test.ts
  • src/main/native-chat/agent-session-wire/structured-agent-session-settled-attach-retry.test.ts
  • src/main/native-chat/agent-session-wire/structured-agent-session-start-failure-writer.test.ts
  • src/main/native-chat/agent-session-wire/structured-agent-session-surface-lifetime.test.ts
  • src/main/native-chat/agent-session-wire/structured-agent-session-view-start-after-failed-start.test.ts
  • src/main/native-chat/agent-session-wire/structured-agent-session-wedged-profile-migration.test.ts
  • src/main/native-chat/agent-session-wire/structured-conversation-command.test.ts
  • src/main/runtime/agent-session-conversation-name-store.test.ts
  • src/main/runtime/agent-session-death-evidence-persistence.test.ts
  • src/main/runtime/agent-session-launch-env-admission.test.ts
  • src/main/runtime/agent-session-lease-renewal.test.ts
  • src/main/runtime/agent-session-older-build-lease.test-fixture.ts
  • src/main/runtime/agent-session-record-options.test.ts
  • src/main/runtime/agent-session-record-store-file.ts
  • src/main/runtime/agent-session-record-store-legacy-handoff-lease.test.ts
  • src/main/runtime/agent-session-record-store-tab-table.test.ts
  • src/main/runtime/agent-session-record-store-test-harness.ts
  • src/main/runtime/agent-session-record-store.test.ts
  • src/main/runtime/agent-session-released-reservation-replay.test.ts
  • src/main/runtime/agent-session-unsaved-creation-supersession.test.ts
  • src/main/runtime/rpc/methods/agent-launch-caller-selection.test.ts
  • src/main/runtime/rpc/methods/agent-launch-pane-reservation.test.ts
  • src/main/runtime/rpc/methods/agent-launch-prestart-failure.test.ts
  • src/main/runtime/rpc/methods/agent-launch-replay.test.ts
  • src/main/runtime/rpc/methods/agent-launch-session-reservation.test.ts
  • src/main/runtime/rpc/methods/structured-agent-session-adoption-replay.test.ts
  • src/main/runtime/rpc/methods/structured-agent-session-hold.test.ts
  • src/main/runtime/rpc/methods/structured-chat-tab-table.test.ts
  • src/main/runtime/structured-agent-session-integration.test.ts
  • src/main/runtime/structured-agent-session-rollback-compatibility.test.ts
  • src/main/runtime/structured-agent-session-runtime.ts
  • src/main/runtime/structured-agent-session-spawn-token-descendants.test.ts
  • src/main/runtime/structured-claude-pending-rewind.test.ts
  • tests/e2e/cross-version-wire/cross-version-agent-session-wire.unit.test.ts
  • tests/e2e/structured-chat-owner-status-activation.unit.test.ts

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 8 remain after this review.


📝 Walkthrough

Walkthrough

The pull request adds a shared test harness for the agent-session record store and migrates tests across mobile, native chat, runtime, RPC, and end-to-end areas to use it. The harness provides helpers to open stores, seed and edit persisted data, read persisted contents, and locate store files. The runtime now uses an exported constant for the store directory name. Test scenarios and assertions are generally unchanged.

Priority: ⬇️ Low

Merge Risk: ⚪ Minimal · up to ed88d

The shared harness centralizes test-store setup without an established production behavior change or weakened persistence checks. No actionable merge-blocking issue remains in the supplied evidence.

🚥 Pre-merge checks | ✅ 4 | ❓ 1

❌ Failed checks (1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ❓ Inconclusive Docstring coverage is 29.17% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 24 functions across 50 files. (31 skipped… Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the main change: consolidating agent-session record-store test setup through one shared harness.
Description check ✅ Passed The description is detailed and covers the change, motivation, scope, testing, review notes, compatibility considerations, and checklist. It marks the linked issue as "None" and leaves the manual-test…
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Full details: Docstring Coverage

Explanation

Docstring coverage is 29.17% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 24 functions across 50 files. (31 skipped: 31 over the file limit.)

✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Commit to this branch
  • Create a new PR

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ No new issues found.

Reviewed changes

  • Production constant move — AGENT_SESSION_STORE_DIR_NAME = 'agent-sessions' moves from the runtime's private RECORD_STORE_DIR_NAME into agent-session-record-store-file.ts; structured-agent-session-runtime.ts now imports it. Same literal, same paths built by hasPersistedStructuredAgentSessionStore and installOnJournal, so no behavior change.
  • New test harness (src/main/runtime/agent-session-record-store-test-harness.ts) — one place to open/seed/seedFromNewerBuild/read/readText/edit the store plus its file path. Each function takes the host's state directory and owns the agent-sessions subfolder inside it, composed through the runtime's own constant and agentSessionStorePath.
  • 78 test/support files migrated — store opens and JSON reads now go through the harness; the two tests that assert on the running app's store (secret-never-reached-disk, spawn-token descendants) now read through the harness instead of a hardcoded path.
  • Intentionally-left format tests — the JSON/.bak/schema/permission suites and the mixed format cases keep reading the file directly; the PR body enumerates them for step D.

Verification in this run: read the full diff; ran the store-adjacent suites (record-store, tab-table, legacy-handoff-lease, import-mismatch, send-open-stale-turn, spawn-token-descendants, claude-root-exit) — 67/67 pass; pnpm tc:node passes; audit:anti-slop clean on the changed production files. An independent pass over every migrated call site found no case where the store's new <stateDirectory>/agent-sessions location diverges from a journal/capsule/copy/raw-read base used in the same test.

Pullfrog  | View workflow run | Using DeepSeek Flash (free via Pullfrog for OSS) | 𝕏

@brennanb2025
brennanb2025 merged commit 75040eb into main Sep 30, 2026
60 checks passed
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