Skip to content

V0.1.3/m2/chat history capture#5

Open
Vedansi18 wants to merge 51 commits into
hi0001234d:mainfrom
Vedansi18:v0.1.3/m2/chat-history-capture
Open

V0.1.3/m2/chat history capture#5
Vedansi18 wants to merge 51 commits into
hi0001234d:mainfrom
Vedansi18:v0.1.3/m2/chat-history-capture

Conversation

@Vedansi18
Copy link
Copy Markdown
Collaborator

Adds the chat-history capture layer of the VS Code extension —
modules M2 (watcher), M3 (per-version extractors), and M4
(schema fingerprint)
per dev plan §3 M2 §2.2. The watcher monitors
Cursor's state.vscdb (and the Windsurf chat-history dir), debounces
fs events, fingerprints the schema, dispatches rows to the appropriate
extractor, and emits normalised ChatHistoryEvents — without touching
any Layer C file. Also ships a dev-only dump-cursor-state.ts helper
(Option C) for capturing verified fixtures from real Cursor installs.

Stacked on M2 Branch 1 (v0.1.3/m2/extension-skeleton,
commit 879ed5e). Branch 2 needs Branch 1's sub-package skeleton, so
this PR's diff is meaningful only against B1, not against sub-7. See
"PR strategy" below.

Modules covered (per dev plan §3 M2 §2.2)

Module File(s) What it does
M2 — Watcher src/chat-history-watcher.ts + chat-history-types.ts fs.watch on each target with 250 ms debounce; reads ItemTable via injectable readItemTableFn (sql.js by default);
dedupes against seenSignatures; emits { prompt, rawSessionId, capturedAt, sourcePath, extractorId }. All deps (watchFn, readFileFn, readItemTableFn, nowFn) injectable for testing.
M3 — Extractors src/extractors/cursor-v2024-q4.ts, cursor-v2025-q1.ts, cursor-v2025-q2.ts, windsurf.ts Per-version row decoders implementing the ChatHistoryExtractor contract. Each
Cursor extractor handles role / type and content / text field variants. Windsurf is a deliberate placeholder — real JSON-file decoding lands in B4 alongside windsurfAdapter.
M4 — Fingerprint src/extractors/index.ts pickExtractor(observedKeys) — prefix-matches each extractor's fingerprintKeys against the observed ItemTable keys, picks the
highest-match-count extractor, ties broken by registry order (newest first). Returns FingerprintResult (kind: 'known' or 'unknown'). The 'unknown' payload is surfaced by the watcher via
onSchemaUnknown for the eventual "schema unknown" toast (wiring in B3/B4).
Bonus — Dump helper scripts/dump-cursor-state.ts + src/cursor-state-dump-helpers.ts Dev-only CLI for capturing verified state.vscdb fixtures. Discovers global + per-workspace DBs, dumps
ItemTable (filtered) + cursorDiskKV, optional --redact for sensitive content. Helpers live in src/ so they're typechecked + unit-tested.

Real-machine inspection findings (Cursor 3.4.20 / Linux)

This branch's commit 3794bc3 includes real-data refinements after
running the dump script against a live Cursor 3.4.20 install:

Finding Impact
WAL mode — main .vscdb is 4 KB; live writes go to .vscdb-wal. sql.js cannot read WAL siblings; better-sqlite3 can. Dump script switched to better-sqlite3 (dev-dep). **Production watcher
still uses sql.js** — flagged for B4 to choose between (a) switching to better-sqlite3 in the .vsix, or (b) implementing a copy + checkpoint shim.
Workspace DB holds the data, not the global one. Plan-era assumption was global-only. Dump script now scans User/globalStorage/state.vscdb AND every User/workspaceStorage/*/state.vscdb.
Watcher target list will need both paths (B4 wiring).
cursor-v2025-q1 key was wrong — community docs said composerData.composerData; real key is composer.composerData. Fixed in cursor-v2025-q1.ts + tests. Value shape (`allComposers /
conversation` array) is not what's at this key on 3.4.20 — JSDoc flagged.
cursor-v2025-q2 prefix cursorAIChatService.chatHistory. NOT observed on Cursor 3.4.20. Extractor still ships (in case older versions use it); JSDoc strengthened.
Three redacted fixtures committed for regression testing. test-fixtures/state-vscdb-samples/cursor-3-4-20-initial-{global,workspace-*}.json.

Files

Path Type Tests
src/ext-vscode/src/chat-history-types.ts shared types exempt
src/ext-vscode/src/chat-history-watcher.ts M2 watcher 11
src/ext-vscode/src/chat-history-watcher.test.ts tests
src/ext-vscode/src/extractors/cursor-v2024-q4.ts M3 (pre-Composer) 9
src/ext-vscode/src/extractors/cursor-v2025-q1.ts M3 (Composer era) 10
src/ext-vscode/src/extractors/cursor-v2025-q2.ts M3 (current schema, unverified) 11
src/ext-vscode/src/extractors/windsurf.ts M3 placeholder 4
src/ext-vscode/src/extractors/index.ts M4 pickExtractor + registry 12
src/ext-vscode/src/cursor-state-dump-helpers.ts dump-script helpers 28
src/ext-vscode/scripts/dump-cursor-state.ts dev-only CLI entry (helpers tested)
src/ext-vscode/test-fixtures/state-vscdb-samples/*.json 3 redacted fixtures
src/ext-vscode/package.json +sql.js runtime dep, +better-sqlite3 + tsx devDeps
src/ext-vscode/esbuild.config.mjs +sql.js marked external

85 new unit tests added by this branch (sub-package total: 110 across 10 files).

Test plan

Automated

# 1. Root + sub-package typecheck
npx tsc --noEmit                          # root
(cd src/ext-vscode && npx tsc --noEmit)   # sub-package — both EXIT=0

# 2. Sub-package isolated tests
(cd src/ext-vscode && npx vitest run)
# expected: 110/110 pass across 10 test files

# 3. Full root test suite
npm test
# expected: 1936 passing + 18 pre-existing TtySelectFn Windows-sim
#           failures (carried forward from dev plan §3.0, NOT caused
#           by this branch)

# 4. Extension bundle still builds clean
(cd src/ext-vscode && npm run build)
# expected: out/extension.js produced (~3.4 KB unchanged from B1)

Manual

The watcher isn't yet wired into extension.ts (deferred to B3/B4), so
the existing Cursor extension session doesn't show watcher behavior
yet. Manual verification is fixture-driven:

cd src/ext-vscode

# Round-trip the redaction logic
npx vitest run src/cursor-state-dump-helpers.test.ts

# Capture a fresh dump against any Cursor install
npx tsx scripts/dump-cursor-state.ts --name dev-dump --redact
# expected: 1-3 JSON files in test-fixtures/state-vscdb-samples/

End-to-end "drop known prompt into state.vscdb → watcher fires →
fingerprint picks extractor → prompt decoded" acceptance is unit-tested
at the contract level (see chat-history-watcher.test.ts + the four
extractor tests + extractors/index.test.ts). Live-Cursor end-to-end
verification is the Branch 5 smoke-test gate, per dev plan §3.0.

hi0001234d and others added 30 commits May 5, 2026 20:43
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…nt sets

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ofile

Adds selectAbsenceMap helper (hardcore_pro → formal, else → casual) and
updates resolveDecisionContent to use it for non-vibe profiles. Updates
6 no-profile tests and 2 priority-override tests to assert casual variants,
consistent with selectNonBeginnerVariant's undefined → casual behaviour.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add 3 hardcore_pro routing tests for remaining formal absence variants,
add all 8 non-beginner absence sets to allContent and per-set count tests,
add 4 beginner absence sets to C-02 structural validation block with
missing imports for ABSENCE_REGRESSION_CHECK_BEGINNER and
ABSENCE_SPEC_ACCEPTANCE_BEGINNER.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Three changes to buildMjsScript() in TtySelectFn.ts:
- Fix _lineCount to split labels by \n and compute per-line visual rows
  instead of treating the whole label as one long string; this correctly
  estimates height for multi-line (numbered-steps) option labels
- Add wrapping guard: when all options fit within budget (no-overflow) but
  their total visual lines exceed the option count, recompute _maxItems
  using the option count as a tight budget ceiling so visually dense
  option sets always get a viewport rather than a flat list
- Pass maxItems to select() so @clack/prompts k() uses _maxItems as the
  viewport window instead of rows-4; without this, maxItems computation
  had no effect on the actual rendered UI

Applies to Mac, Windows, and Linux new-window paths (all share buildMjsScript).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…word lists

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ded vibeKeyword sets

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds formal, casual, and beginner DecisionContent objects for
idea_scoping, idea_constraint_check, and idea_user_definition.
Includes content existence tests covering shape and non-empty strings.
Map wiring deferred to Phase 4.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds formal, casual, and beginner DecisionContent objects for
task_ordering, task_sizing, and task_definition_of_done.
Includes content existence tests covering shape and non-empty strings.
Map wiring deferred to Phase 4.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
hi0001234d and others added 21 commits May 7, 2026 18:03
Adds formal, casual, and beginner DecisionContent objects for
user_feedback_review and iteration_planning. Wires all 8 new signal
keys (Groups A, B, C) into ABSENCE_CONTENT, ABSENCE_CONTENT_CASUAL,
and ABSENCE_CONTENT_BEGINNER. Adds 24 routing tests covering all
3 registers for all 8 signals, plus Group C content existence tests.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…INNER

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…cture test

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… map

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…NG_CASUAL

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- 8 new absence signal definitions in signals.ts (scope_creep, context_loss, api_design_review, accessibility, environment_and_secrets, data_validation, ci_pipeline, rate_limiting)
- 24 new content set constants across 3 registers (formal, casual, beginner) in options.ts and options-beginner.ts
- All 8 keys added to ABSENCE_CONTENT, ABSENCE_CONTENT_CASUAL, ABSENCE_CONTENT_BEGINNER maps
- relevantProjectTypes filter on api_design_review, accessibility, rate_limiting signals
- AbsenceDetector.ts: Gate 3 now uses per-signal absenceThreshold with profile multiplier; project-type gate added
- types.ts: relevantProjectTypes field added to SignalDefinition
- auto.ts: projectType extracted from getProject() and passed to detectAbsenceFlags
- Tests: routing, content existence, structure, and buildOptionList coverage for all 8 new signals

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Branch 1 of Milestone M2 (v0.1.3/m2/extension-skeleton). Establishes the
src/ext-vscode/ sub-package with an esbuild-driven build pipeline
(ESM source -> CJS bundle for the VS Code host), activates on
onStartupFinished, and ships the four scoped modules:

  M1 - Skeleton: package.json (activationEvents, activity-bar container +
       placeholder view backed by viewsWelcome so the icon actually renders),
       tsconfig.json, esbuild.config.mjs, src/extension.ts entrypoint.
  M5 - IPC stub: src/ipc.ts. spawnAuto(prompt, sessionId) and
       spawnStop(sessionId) spawn the nexpath CLI as subprocesses and parse
       the decision-session JSON payload from stdout, with typed errors
       (NexpathBinaryNotFoundError, NexpathMalformedPayloadError) and
       configurable binary-path resolution
       (opts.binaryPath -> NEXPATH_BIN env -> 'nexpath' on PATH).
       The exact stdin envelope vs. Layer C input contract is intentionally
       a stub here; Branch 4 (cursor-windsurf-adapters) finalises it.
  M11 - Onboarding: src/onboarding.ts. First-launch consent toast persists
        the user's choice to globalState; on macOS, additionally shows a
        Full-Disk-Access guidance toast that deep-links to the System
        Settings privacy pane.
  M12 - Icon: media/icon.svg. Y-fork (branching path) representing
        "next path" decision points; monochrome currentColor, scalable.

25 unit tests co-located alongside source (8 onboarding, 11 ipc, 6 extension),
runnable via root vitest with vi.mock('vscode') stubs. Sub-package has its
own tsconfig + package-lock; root tsconfig now excludes src/ext-vscode/ so
each side owns its TS build. Both root and sub-package tsc --noEmit are
clean. Full root test suite: 1851 passing + 18 pre-existing unrelated
TtySelectFn Windows-sim failures (carried forward from dev plan §3.0).

Deferred (flagged for follow-up, not blockers for this branch):
- 5 moderate npm-audit warnings in the esbuild -> vite -> vitest dev chain
  (dev-only; will be addressed during M5 hardening).
- IPC stdin envelope contract: real wiring lands in Branch 4.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Branch 2 of Milestone M2 (v0.1.3/m2/chat-history-capture). Stacked on
M2 Branch 1 (commit 879ed5e). Adds the three scoped modules:

  M2 - chat-history-watcher.ts: fs.watch on Cursor's state.vscdb and
       Windsurf's ~/.codeium/windsurf/ dir, debounced (default 250ms),
       reads ItemTable via injectable readItemTableFn (sql.js by default),
       diffs against seenSignatures, emits {prompt, rawSessionId,
       capturedAt, sourcePath, extractorId}. Dependency-injectable
       throughout (watchFn, readFileFn, readItemTableFn, nowFn) so the
       unit tests run without sql.js or real fs.watch.

  M3 - extractors/: four per-version row decoders implementing the
       ChatHistoryExtractor contract from chat-history-types.ts.
         - cursor-v2024-q4 (aiService.prompts global key, pre-Composer)
         - cursor-v2025-q1 (composerData.composerData, Composer era)
         - cursor-v2025-q2 (cursorAIChatService.chatHistory.<tabId>
                            per-tab keys, current)
         - windsurf (cascade.* placeholder; real Windsurf decoding lands
                     in Branch 4 alongside windsurfAdapter)
       Each Cursor extractor handles both `role`/`type` and
       `content`/`text` field variants seen across minor versions.
       All four are TODO-flagged for verification against real dumps
       before Branch 6 publishes — scripts/dump-cursor-state.ts (below)
       captures those dumps.

  M4 - pickExtractor in extractors/index.ts: prefix-match each
       extractor's fingerprintKeys against the observed ItemTable keys,
       pick the highest match count (ties broken by registry order =
       newest first). Returns FingerprintResult; unknown schemas surface
       observedSampleKeys for the "schema unknown" toast hook.

scripts/dump-cursor-state.ts: dev-only helper (npx tsx) for capturing
state.vscdb fixtures from a machine with Cursor installed. Filters to
chat-related key prefixes, optional --redact for sensitive content.
Outputs to src/ext-vscode/test-fixtures/state-vscdb-samples/.

Sub-package additions:
  - dependencies: sql.js ^1 (runtime; loaded via dynamic import so wasm
    boot is lazy). Marked external in esbuild so the .vsix ships
    node_modules/sql.js rather than inlining it.
  - devDependencies: tsx ^4 (for running the dump script).

57 new unit tests (sub-package totals: 82 passing across 9 files):
  cursor-v2024-q4   9 tests
  cursor-v2025-q1  10 tests
  cursor-v2025-q2  11 tests
  windsurf          4 tests
  extractors/index 12 tests
  chat-history-watcher 11 tests

Verification: root tsc --noEmit clean; sub-package tsc --noEmit clean;
sub-package vitest 82/82 pass; full root test suite 1908 passing + 18
pre-existing TtySelectFn Windows-sim failures (carried forward from M1
3.0, unrelated); esbuild bundle still builds out/extension.js.

Deferred to follow-up (flagged, not blockers):
- Real-dump verification of all 4 extractors (use dump-cursor-state.ts
  on machines with each Cursor version installed; replace TODO comments
  in extractors with fixture-driven regression tests).
- Windsurf JSON-file decoder (Branch 4).
- Wiring the watcher into extension.ts activate() (Branch 3 webview-ui
  or Branch 4 adapters — depends on UI surface integration).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Real-machine inspection on Cursor 3.4.20 (2026-05-15) surfaced three
issues with the Branch 2 extractor designs. This commit fixes the
verifiable ones, captures redacted fixtures, and documents the still-
unknown bits for the next round.

Issue 1 — SQLite WAL mode.
The dump script previously used sql.js, which only reads the buffer of
the main `.vscdb` file. Live Cursor writes go to the sibling
`.vscdb-wal` (185 KB while the main file was 4 KB), so sql.js saw
"no such table: ItemTable" even though the table exists.

Fix: switched the dump script to better-sqlite3 (native, WAL-aware).
Copies main + wal + shm siblings to a tmp staging dir before reading
so the live Cursor write path is never touched, then runs
`PRAGMA wal_checkpoint(TRUNCATE)` on the staged copy for consistency.

The PRODUCTION watcher in `chat-history-watcher.ts` still uses sql.js
via dynamic import; the same WAL problem will surface when Branch 4
wires the watcher live. Flagged for Branch 4 design — options are:
(a) switch the watcher to better-sqlite3 (native binding in .vsix), or
(b) implement copy + checkpoint via sql.js. Out of scope for B2.

Issue 2 — `cursor-v2025-q1` extractor's fingerprint key was wrong.
Community docs said `composerData.composerData`; Cursor 3.4.20 actually
uses `composer.composerData`. Updated the key in both the extractor and
its tests + the fingerprint test.

Open finding: the `composer.composerData` value on a chat-less Cursor
3.4.20 workspace DB is metadata only (selectedComposerIds, migration
flags) — not the conversation messages this extractor's decodeRow logic
parses for. Logic falls through cleanly (returns [] when the expected
`allComposers` field is absent) and the JSDoc now documents that the
real Composer message storage location is still TBD and needs a
post-chat snapshot to confirm.

Issue 3 — `cursor-v2025-q2` extractor's fingerprint prefix
(`cursorAIChatService.chatHistory.`) was NOT observed on Cursor 3.4.20.
The extractor still ships (in case older versions use it) but the JSDoc
now flags this as unverified and points to the dump script for capturing
a real fixture before Branch 6 ships.

Dump script additions:
- Discovers ALL state.vscdb under Cursor's config tree (global +
  per-workspace) — chat messages live in the workspace DB, not global.
- Dumps both `ItemTable` (filtered to chat-related key prefixes) AND
  `cursorDiskKV` (Cursor 3.x's parallel KV table; currently empty but
  may hold Composer messages once chats happen).
- One output JSON per discovered DB; suffixed with `global` or
  `workspace-<id>` for traceability.
- `--redact` replaces string values > 8 chars with same-length asterisks.

Dependencies:
- Added better-sqlite3 ^11 + @types/better-sqlite3 ^7 as devDependencies
  in the sub-package. Dev-only — the production extension bundle is
  unaffected.

Captured fixtures (redacted) — all three DBs from a chat-less Cursor
3.4.20 session, committed for regression testing:
  - cursor-3-4-20-initial-global.json (9 rows)
  - cursor-3-4-20-initial-workspace-1778826246907.json (7 rows)
  - cursor-3-4-20-initial-workspace-empty-window.json (2 rows)

Verification:
- Sub-package tsc --noEmit clean.
- Sub-package vitest 82/82 pass.
- Root tsc --noEmit clean.
- Full root test suite 1908 passing + 18 pre-existing TtySelectFn
  carry-forward.

Next step (manual, user-driven): submit a real prompt in Cursor's Ask
mode AND in Composer mode, then re-run the dump script to capture a
chat-bearing snapshot. The new keys / tables that appear will pin down
the Composer-mode message storage location, and a follow-up commit will
finalise the extractor decode logic against that real data.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Closes the unit-test audit gap surfaced for M2 Branch 2. The dump script
had real business logic (`redactValue`, `shouldKeepItemTable`,
`parseArgs`, `cursorConfigRoot`, `discoverAllStateVscdb`) with zero test
coverage — `redactValue` in particular has data-leak consequences if
buggy.

Extracted the pure / near-pure helpers into a new module:
  - `src/cursor-state-dump-helpers.ts` — lives under tsconfig rootDir
    so it's typechecked by the sub-package's main `tsc --noEmit` and
    auto-picked-up by vitest. Re-exports `KEEP_ITEMTABLE_PREFIXES`,
    `shouldKeepItemTable`, `redactValue`, `cursorConfigRoot`,
    `discoverAllStateVscdb` (with injectable fs helpers), and
    `parseArgs` (returns a tagged-union result instead of calling
    `process.exit`, so the error paths are testable).

Co-located tests: `src/cursor-state-dump-helpers.test.ts` — 28 tests
covering:
  - `shouldKeepItemTable`: each default prefix matched, unrelated keys
    dropped, custom prefix lists, prefix-not-exact match.
  - `redactValue`: short-string preservation, long-string redaction,
    nested object/array recursion, non-string value preservation, bulk
    redact for non-JSON input, JSON-string root, exact 9-char boundary.
  - `cursorConfigRoot`: linux / darwin / win32 / unknown-platform paths
    and APPDATA fallback.
  - `discoverAllStateVscdb`: empty tree, global-only, global + multiple
    workspaces, skip workspace dirs missing the DB, injectable fs.
  - `parseArgs`: required `--name`, optional `--src` / `--redact`,
    `--help` / `-h` signal, missing-value rejection, unknown-argument
    rejection.

Script entry-point `scripts/dump-cursor-state.ts` now imports from
`../src/cursor-state-dump-helpers.js` and retains only the I/O
orchestration (file copy to tmp staging dir, better-sqlite3 read, fixture
write). Behaviour is byte-for-byte unchanged — verified by re-running
against the live machine and producing identical row counts to the
previous commit (`3794bc3`).

Sub-package totals:
  - Test files: 10 (was 9)
  - Tests: 110 passing (was 82) — +28 helpers tests
  - Sub-package tsc --noEmit clean
  - Root tsc --noEmit clean
  - Full root suite: 1936 passing + 18 pre-existing TtySelectFn
    Windows-sim failures (M1 §3.0 carry-forward, unrelated)

Closes the only remaining audit gap for M2 Branch 2. No further unit-test
work pending; per the auto-commit rule the branch is now closed pending
push.

Co-Authored-By: Claude Opus 4.7 (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.

2 participants