Skip to content

fix(scripts): drop session-bundle imports deleted with the JSONL session tree - #2029

Closed
Astro-Han wants to merge 1 commit into
mainfrom
fix/measure-session-bundle-stale-imports
Closed

fix(scripts): drop session-bundle imports deleted with the JSONL session tree#2029
Astro-Han wants to merge 1 commit into
mainfrom
fix/measure-session-bundle-stale-imports

Conversation

@Astro-Han

@Astro-Han Astro-Han commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

Summary

scripts/measure-session-bundle.mjs fails to load on main, taking the whole extended script suite down with it:

SyntaxError: The requested module '@maka/storage' does not provide an export
named 'SESSION_BUNDLE_PORTABLE_SESSION_DIRECTORIES'

#1994 made SQLite the sole operational authority and removed the JSONL session tree along with the two constants that classified it — SESSION_BUNDLE_PORTABLE_SESSION_DIRECTORIES and SESSION_BUNDLE_PORTABLE_SESSION_FILES — but the script kept importing them.

Drop the two imports and the topLevel === 'sessions' branch they fed. That branch was already unreachable: sessions is no longer in SESSION_BUNDLE_STATE_ENTRIES, so the top-level portability check rejects such an entry before it can be reached. Net effect is 26 deleted lines and one rewritten import.

Known remaining gap

This restores module loading, not the harness's runtime correctness. findSessionId still requires sessions/<id>/session.jsonl, which no export has produced since #1994, so an actual measurement run would fail on its own. Deciding whether to port the harness to the operational database or retire it needs a storage-bundle judgement call, so it is tracked separately rather than guessed at here.

Verification

  • node --test scripts/measure-session-bundle.test.mjs — 2 passed, 0 failed (fails to even load on main).
  • npm run test:scripts:extended — 15 passed, 0 failed.
  • npm run lint, npm run format:check — clean.

Why main is green with this broken

main's CI never reaches the script. In the latest main run, test_workspaces succeeds with the step itself skipped:

skipped  Run fast script tests
skipped  Run extended script tests
success  Run affected standard workspace tests

ci.yml selects surfaces from a two-dot diff between pull_request.base.sha and pull_request.head.sha. A push to main diffs only that merge, so scriptMode stays none and the extended step never runs — regardless of the script being broken.

It surfaced on #2028 only because that branch was five commits behind main, so the two-dot diff also carried the inverse of those commits, including the root package.json change from a7d17e5bd. package.json is in FULL_SUITE_FILES (scripts/ci-test-plan.mjs:11), which forces scriptMode: 'full' and runs the extended scripts. Rebasing #2028 onto current main turns it green again without touching this bug.

Confirmed independently by running the test on a clean detached origin/main checkout, where it fails identically. So the breakage is real on main and merely invisible to main's own CI path.

…ion tree

#1994 made SQLite the sole operational authority and removed
SESSION_BUNDLE_PORTABLE_SESSION_DIRECTORIES and
SESSION_BUNDLE_PORTABLE_SESSION_FILES along with the JSONL session tree
they classified, but scripts/measure-session-bundle.mjs still imported
them. The module failed to load, so the whole test file errored out.

Drop the two imports and the classification branch they fed. With
'sessions' no longer in SESSION_BUNDLE_STATE_ENTRIES, that branch was
already unreachable — the top-level portability check rejects the entry
first.

This restores module loading and the extended script suite. It does not
revive the harness's runtime path: findSessionId still requires
sessions/<id>/session.jsonl, which no export produces since #1994.
@Astro-Han

Copy link
Copy Markdown
Contributor Author

Superseded by #1920, which landed the same fix (fix(scripts): keep legacy bundle measurement importable) while this was open. Verified on current main: node --test scripts/measure-session-bundle.test.mjs passes. Closing as duplicate.

@Astro-Han Astro-Han closed this Aug 3, 2026
@Astro-Han
Astro-Han deleted the fix/measure-session-bundle-stale-imports branch August 3, 2026 15:21
Astro-Han pushed a commit that referenced this pull request Aug 6, 2026
) (#2263)

* fix(storage): import legacy JSONL session transcripts into SQLite (#2260)

After the JSONL->SQLite cutover (#1994, #2029), sessions created before
the switch stayed on disk as sessions/<id>/session.jsonl but never
appeared in the UI: the new storage layer only reads SQLite and there was
no migration path (issue #2260).

Add a one-time importer (importLegacySessionsOnce) that scans the legacy
sessions directory, decodes each schemaVersion:1 transcript with the
pre-#1994 compatibility rules (backend remapping, missing-field defaults),
creates the session under its original id via the idempotent
createStableSession path, appends the decoded messages, and restores the
original lifecycle timestamps and flags.

Design:
- Idempotency key is the session id itself (probeStableSessionCreate),
  so re-runs and concurrent first launches converge without duplicates.
- Per-file atomicity: a transcript imports fully or is skipped and
  reported; corrupt records are never laundered into the authoritative
  store. Failures never block startup or other files.
- Legacy files are retained as migration evidence.
- Wired into createSessionStore: list/listCatalogPage/listHeaders await
  the lazy import so upgraded installs see their pre-cutover sessions.

* fix(storage): drop console diagnostic from legacy import wiring

The import result logging used console.error, which the repository
check-console audit rejects for new call sites. Remove the log entirely
and harden the lazy import to swallow unexpected errors (best-effort
semantics): a legacy-import failure must never block session listing.

* fix(storage): address #2260 review — validate before write, probe first, marker/subagent fail-closed, resume gate, observable results

Addresses Astro-Han's review (P1 + P2s):

- **P1 per-file atomicity**: the decoded header AND the post-create header
  patch are now validated through normalizeSessionHeader BEFORE any store
  write. Previously updateHeader (the third of three transactions) could
  throw after create+append committed, leaving a permanent partial session
  that later probes would report as skipped forever.
- **P2 marker laundering**: a session_transcript marker file with no backing
  SQLite row (restored backup, copied sessions/, reset DB) now fails closed
  instead of being fabricated into a fake session — matching the pre-#1994
  reader's contract.
- **P2 legacy field loss**: decodeLegacySessionHeader preserves
  subagentParent/Runtime/Spawn/Workspace, thinkingLevel, lastReadMessageId.
  Legacy subagent children route through createSubagent (parent lineage kept);
  an incomplete spawn identity fails the file instead of flattening the child
  into a top-level session.
- **P2 resume gate**: readHeaderSnapshot/readMessagesSnapshot now await the
  lazy import, so 'maka --resume <legacy-id>' no longer misses pre-cutover
  sessions on the first post-upgrade run.
- **P2 observability**: ensureLegacyImported retains the result and logs
  failures/imported counts instead of swallowing them; LegacySessionImportResult
  now splits skipped into existing vs collision.
- **P2 steady-state cost**: the idempotency probe now runs BEFORE the file
  read, so every launch skips known ids without touching their transcripts.
- **P2 torn tail**: an incomplete final line (interrupted append) is skipped
  like the pre-#1994 strict reader, instead of failing the whole file.

Tests: 11/11 in legacy-session-import (added lazy-list, resume, torn-tail,
marker, subagent fail-closed, field-preservation cases); session-store +
sqlite-session-metadata-store + foreign-session-store 70/70; full storage
suite 702 pass, 2 pre-existing env failures (dugite git binary + root
tsconfig load) verified unrelated via stash.

* chore: allow-list session-store.ts for legacy import diagnostics

check-console.mjs flagged the new console.error/warn/info sites in
session-store.ts (legacy JSONL import outcome diagnostics) as unlisted.
Same pattern as the existing automation-store.ts allow-list entry —
best-effort import diagnostics, no credentials or provider payloads.

* chore: retry CI — test_headless 'settles background child sessions at the task-run deadline' flaked on the previous run (identical headless code passed two runs ago; local 30/30 green; no headless files touched by this PR)

* fix(storage): refactor legacy session import onto a single-transaction store API

Addresses #2263 review round 3: collapse the importer's probe -> create ->
append -> update choreography (three transactions, constant fingerprint,
fidelity patch, in-memory latch, resume gate) into one store-level
importSession primitive.

- sqlite-session-metadata-store: importSession(header, messages, projection)
  writes the header row (with historical timestamps/flags) and all messages
  in one transaction. Idempotent by primary key (INSERT OR IGNORE), so
  concurrent first launches converge on one winner with no create claims;
  tombstoned ids are never resurrected; a failure mid-transaction rolls
  back, so a partial session can never persist (closes the crash-window P1).
- legacy-session-import: read -> decode -> normalizeSessionHeader (pre-write
  validation) -> one importSession call. Subagent children now import under
  their own legacy id with lineage preserved instead of a fresh UUID
  (fixes the phantom-session P1). Torn-tail tolerance tightened to the
  pre-#1994 strict-reader semantics: only a final line of a file with no
  trailing newline whose parse failure is an unclosed bracket is skipped;
  truncated lines ending in a newline and garbage tails fail the file.
- session-store: memoized import latch moves into ensureReady(), which every
  public method already awaits, so desktop/CLI/headless/--resume are all
  covered with zero per-caller wiring; appendMessages and closeAfterReady
  now await ensureReady() (pre-existing gaps). Import diagnostics are kept
  observable through the existing console allow-list.
- tests: payload pins (header model/status, deepEqual messages[0]),
  concurrent double-import, id collision, header-only, absent sessions/,
  empty file, garbage tail, truncated-with-newline, whole-run failure
  containment, and subagent legacy-id round-trip; 19/19 legacy import tests,
  full storage suite 713 pass (1 pre-existing dugite-binary env failure).

* fix(storage): probe legacy session ids before reading transcripts

Restores the probe-before-read steady-state cost from review round 2 on the
single-transaction design: the importer now asks the store whether a session
id already exists (live or tombstoned) before opening or parsing its file,
so every launch of an upgraded install pays a directory listing plus
per-id SQLite existence checks. importSession remains the idempotency
authority — a race between the probe and the write still converges on one
winner via the primary key. Adds hasSession to the store surface and a
test that corrupts the on-disk transcript between runs to pin that a
skipped id is never re-read.

---------

Co-authored-by: cat0825 <cat0825@users.noreply.github.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