Skip to content

Data-loss: session close path can wipe a large transcript (fix + tests available on fork) #814

Description

@dwillitzer

Summary

A close/clear path that swaps in a fresh 0-message session and saves with status=Closed triggers save()'s shrink detection (messages.len() < persist_state.messages_len) and checkpoints a full snapshot — overwriting the real transcript snapshot with an empty stub and deleting the journal. The later .bak rotation then captures the already-wiped state, so both copies end up empty and --resume shows an empty session.

Hit this in production 2026-08-05 on a 12-hour session (686 messages, 1.5MB snapshot). Daemon log forensics:

10:55:06  SESSION_PERSISTENCE save_mode=append    messages=686  snapshot_bytes=1,539,899
10:55:38  SESSION_PERSISTENCE save_mode=snapshot  messages=0    snapshot_bytes=600  status=Closed   <- wipe
10:55:41  SESSION_PERSISTENCE load_done           messages=0                                        <- resume sees nothing

Fix (ready on my fork — PR creation appears restricted on this repo)

Branch: https://github.com/dwillitzer/jcode/tree/fix/session-pre-wipe-guard (single commit, cherry-picks cleanly onto origin/master)

checkpoint_snapshot() gains a guard_snapshot_shrink() pre-step: when the in-memory transcript is empty but the on-disk snapshot exceeds 4KB, snapshot + journal are first copied to timestamped .pre-wipe-<ts>.json.bak / .jsonl.bak siblings.

  • Never blocks the save — legitimate compaction shrinks are unaffected (guard requires empty; metadata stubs are under threshold)
  • Backup names avoid *.json so session scanners don't surface phantoms
  • Emits SESSION_PERSISTENCE phase=pre_wipe_backup for observability; backup failure logs and proceeds (fail-open)
  • Recovery: cp session_X.pre-wipe-<ts>.json.bak session_X.json

Includes two regression tests (wipe path produces byte-identical restorable backup with full recovery round-trip; small/compaction checkpoints produce no backups).

Related fixes on the same fork, happy to PR any/all if you enable PRs or prefer patches:

Branch What
fix/swarm-schema-anyof Top-level anyOf in the swarm tool schema breaks strict providers (Anthropic API rejects it); flattened to a plain object schema
fix/swarm-worker-provider-pin Swarm worker spawns drop the openai-compatible:<profile> prefix, so workers lose the auth profile and fail; pins the full prefix through spawn (with tests)
feat/prompt-cache-override supports_prompt_cache config override so openai-compatible profiles fronting Anthropic models emit cache_control breakpoints (verified ~92% cached-token hit rate through a claude-family proxy)
fix/mcp-tool-name-sanitize MCP tool names with characters outside [a-zA-Z0-9_-] are rejected by provider APIs; sanitizes at registration. Also prefers configured judge models over hardcoded defaults

All branches are single-purpose, based on current master (02439b4), and cherry-pick cleanly. If you'd rather receive these differently (git bundles, mailed patches, enabling PRs), say the word.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingpriority: criticalP0 - urgent, blocks usage / data loss / crash for many userstriage: fixed-pending-releaseFixed in code/committed; will close automatically on next release

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions