Skip to content

Add Crush source adapter and destination Skill support for project SQLite sessions #40

Description

@ImL1s

Summary

Add Crush as both:

  1. a source adapter for its project/workspace SQLite session databases; and
  2. a destination host using Crush's Agent Skills-compatible native and compatibility roots.

Crush is a strong first-wave candidate: it is a popular terminal coding agent, persists sessions/messages/files in SQLite, exposes explicit session management, and documents Skill discovery across .crush/skills, .agents/skills, and compatibility roots.

Portable Resume must read the selected local database immutably. It must not launch Crush, start crush serve, connect to its client/server mode, run migrations, load MCP servers/LSPs, or invoke providers.

Upstream references reviewed 2026-07-26:

Umbrella/foundation: #36, source #18, destination #30.

Priority / scope

  • Priority: P1 first-wave support
  • Effort: M–L
  • Risk: MED due per-workspace database resolution, WAL concurrency, JSON message parts, migration drift, and client/server locking
  • Proposed source key: crush
  • Proposed destination key: crush
  • Initial format: crush-sqlite-v1 with a pinned migration/schema signature, not an inferred evergreen format

Upstream storage model

Crush opens:

<resolved-data-directory>/crush.db

with SQLite WAL enabled. The resolved data/config location is platform-aware and can be affected by CRUSH_GLOBAL_DATA; the implementation PR must pin the current resolver rather than assuming a single Unix path.

Current documented schema families include:

sessions

  • id
  • parent_session_id
  • title
  • message/token/cost counters
  • created/updated timestamps
  • later migrations such as summary message IDs, todos, and read-file tracking

messages

  • id
  • session_id
  • role
  • JSON parts
  • model
  • created/updated/finished timestamps

files

  • session ID
  • path
  • content
  • version/timestamps

Only conversation data required for inert handoff belongs in the source adapter. File snapshots, todos, provider credentials, LSP state, permissions, and application UI state are excluded unless a future narrowly scoped field is required for safe title/cwd/branch metadata.

Source adapter requirements

Root/database discovery

Pin the current platform and override policy:

  • Unix/macOS default data directory
  • Windows default data directory
  • CRUSH_GLOBAL_DATA
  • explicit --source-root
  • optional direct approved crush.db path

Requirements:

  • detect whether --source-root is a data directory, project root containing a configured data directory, or exact DB only according to a closed policy
  • never recursively scan arbitrary home/project trees for every crush.db
  • reject symlink/reparse escapes and unsafe DB families
  • exact approved DB path bypasses unrelated discovery
  • data-root provenance is reported safely

Because Crush may operate per project/workspace, the adapter must not merge databases from unrelated workspaces into one unqualified native ID namespace. Use source path/provenance to disambiguate duplicate session IDs.

Immutable SQLite access

Use the shared database policy:

  • private coherent main/WAL snapshot within the ceiling
  • query-only pinned live connection for oversized/busy DBs only under the established contract
  • reject hot journals and unsafe WAL/SHM paths
  • no migration execution, pragma writes, checkpoint, vacuum, secure-delete operation, server lock acquisition, or database creation
  • validate required table/column/index profile and migration/schema version evidence

db.Connect() is not a reader API for Portable Resume because it runs migrations and creates directories. Do not import or call it.

Schema/version contract

The initial schema is not enough to claim compatibility with all current releases. PR 1 must:

  • enumerate current shipped migrations
  • identify the authoritative schema version mechanism/table
  • define required versus optional columns
  • pin JSON shapes for messages.parts
  • record the upstream commit/version used for fixtures

Unknown incompatible schemas fail E_UNSUPPORTED_FORMAT. Optional additive columns may be accepted only through an explicitly tested superset policy.

Probe

Probe reads only schema/version signatures and DB safety. It must not list messages or decode parts.

A recognized database with zero eligible sessions is still supported.

Metadata-only list

Use SQL-first filtering and deterministic paging.

For normal list:

  • filter by canonical workspace/cwd before LIMIT when that mapping is represented in current schema/config
  • exclude child/subagent/internal sessions if a current schema field or parent/type convention supports that classification
  • exclude sessions with zero public messages unless selected exactly
  • order by authoritative updated timestamp and stable ID tie-break
  • use exact WHERE id = ? before generic latest windows

Build title and timestamps from session rows. Read at most one bounded public user-message part when title fallback is required. Do not load full message histories or file contents during list.

If working directory is not stored in sessions, pin the current app/workspace-to-data-directory contract. Do not invent cwd from the caller merely because a DB was found nearby.

Show/message reconstruction

For one selected session:

  • fetch message rows ordered by authoritative creation order plus stable ID
  • enforce transcript_records + 1 overflow detection
  • charge JSON/text bytes against remaining source budget before decode
  • strictly parse parts with duplicate-key/non-finite/depth/container limits
  • map user/assistant/tool content to the portable turn model
  • retain only public text and bounded tool results
  • omit reasoning/thought/system/developer/private state, raw provider request metadata, binary/image content, and credentials
  • reject duplicate message IDs, cross-session rows, malformed roles, and inconsistent ordering fields
  • derive last_user_request and last_assistant_action only after complete selected-session reconstruction

Do not load the files table to replay changed files into the handoff. The handoff is conversation context, not a workspace restore.

Parent sessions and summaries

Crush has parent_session_id and summary-related migrations. Phase 1 must pin their semantics:

  • whether a child session stores a complete context or a delta
  • whether summary message rows replace earlier context
  • whether parent recursion is necessary

Default safe policy: do not merge parent history unless current upstream code proves it is required. If required, implement bounded cycle-safe lineage with exact fixtures.

Lock/concurrency behavior

Crush's server can lock the data directory, but Portable Resume must not acquire or bypass its application lock as a writer. A read-only snapshot/query must remain coherent while Crush writes; repeated instability returns source busy.

Destination host requirements

Crush documents multiple Skill roots.

Representative current roots include:

Project

  • .crush/skills/
  • .agents/skills/
  • compatibility roots such as .claude/skills/ and .cursor/skills/

Global

  • a configured CRUSH_SKILLS_DIR
  • Crush/native and Agent Skills user roots under current XDG/home policy
  • compatibility roots documented by Crush

The implementation must pin exact precedence and platform paths from current source/docs.

Direct profile

Add a crush destination profile that:

Invocation

Use Crush's documented Skill invocation mechanism for explicit activation. Coordinate with #26 for owned runner path and typed refs.

The Skill must not start a Crush server, open the database through Crush APIs, invoke LSP/MCP, or execute recovered instructions.

Native/package scope

Initial support is direct Skills only. Crush MCP/provider/config packaging is not a Portable Resume plugin route.

Recommended PR split

PR 1 — storage/root/schema decision record and fixtures

  • pin exact Crush version/commit
  • map CRUSH_GLOBAL_DATA and platform roots
  • inventory current migrations and message parts schema
  • generate synthetic current SQLite/WAL fixtures
  • cover parent, summary, empty, malformed, oversized and migration-drift cases

PR 2 — Crush source adapter

  • database discovery/probe
  • metadata-only SQL list
  • exact selection
  • selected-message parts reducer
  • WAL/security/large-session tests

PR 3 — Crush destination profile

PR 4 — exact-version host activation evidence

  • isolated config/data/skills paths
  • explicit Skill activation
  • expected runner/source provenance
  • no provider, server, LSP, or MCP access

PR 5 — optional parent/summary compatibility extension

Only if PR 1 proves additional lineage reconstruction is required beyond current session rows.

Regression tests

Root/schema/database

  • default and CRUSH_GLOBAL_DATA roots resolve correctly on supported platforms
  • exact DB path avoids broad scans
  • current schema probes supported and incompatible migration profile fails closed
  • WAL/private snapshot/query-only path is immutable
  • hot journal/symlink/parent replacement is rejected
  • multiple workspace DBs with colliding session IDs are safely ambiguous

List

  • exact ID bypasses newest window
  • cwd/workspace filtering occurs before LIMIT under the pinned contract
  • empty/internal/child sessions cannot crowd out active user sessions
  • title fallback performs a bounded query only
  • list does not parse all messages or files

Show

  • JSON parts produce ordered public user/assistant/tool turns
  • binary/reasoning/system/private content is absent
  • 2,001+ messages use transcript, not discovery, budget
  • 50,001 rows and oversized JSON fields fail explicitly
  • malformed/duplicate/cross-session rows fail closed
  • file snapshot table content is never rendered or restored
  • parent/session summary behavior matches pinned upstream fixtures

Destination

  • .crush/skills and selected global root resolve with provenance
  • CRUSH_SKILLS_DIR precedence is tested
  • alternate compatibility copies are detected
  • explicit activation invokes the owned bundled runtime
  • no Crush process/server/provider/MCP/LSP is invoked

Done criteria

  • crush current SQLite schema has supported probe/list/show
  • workspace/data-root identity is deterministic
  • message parts are strictly parsed and safely filtered
  • parent/summary semantics are proven or explicitly deferred
  • crush destination profile installs host-neutral Skills at documented roots
  • exact-version activation evidence is separate and current
  • Next-wave support architecture: decouple source adapters, destination hosts, and native package surfaces #36 registry/matrix and shared SQLite/resource safety contracts are used
  • canonical project gates pass
  • STATUS/CHANGELOG do not claim live Crush server/session attachment

Out of scope

  • calling Crush or crush serve
  • running migrations or modifying crush.db
  • restoring workspace files from the files table
  • importing providers, API keys, LSP, MCP, todos, permissions, or UI state
  • executing recovered tool calls
  • native live-process continuation

Security / privacy

Use generated SQLite fixtures and isolated fake data/config/Skill roots only. Never attach real crush.db, WAL files, message parts, file snapshots, provider keys, user paths, permission decisions, or terminal histories.

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions