# How ai-Memory Ensures Session Continuity Across Different Harnesses

> Learn how ai-memory ensures session continuity across Claude Code and Codex by using a unified launch plan and managed workstream ledger to preserve session IDs.

- Repository: [Fabio Akita/ai-memory](https://github.com/akitaonrails/ai-memory)
- Tags: deep-dive
- Published: 2026-08-22

---

**ai-memory ensures session continuity across different harnesses by injecting native session selectors through a unified launch-plan builder, detecting existing user-specified selectors to avoid conflicts, and maintaining a managed-workstream ledger that preserves logical session IDs across harness boundaries.**

Switching between AI coding assistants like Claude Code and Codex typically fragments session context, forcing developers to manually rebuild state when changing tools. The ai-memory project eliminates this friction by implementing a hardware abstraction layer that maintains **session continuity across different harnesses** through runtime argument injection and persistent transcript storage. This architecture allows developers to start a task in Claude Code and seamlessly continue it in Codex—or any other supported harness—without losing conversation history or context.

## Unified Launch-Plan Builder for Native Session Injection

The core mechanism for cross-harness continuity resides in [`crates/ai-memory-workstream/src/harness.rs`](https://github.com/akitaonrails/ai-memory/blob/main/crates/ai-memory-workstream/src/harness.rs), specifically the `build_launch_plan` function. This builder generates native command lines that inject session selectors only when the harness operates in session mode and the user has not supplied their own native selector.

For **Claude Code**, the builder creates a fresh UUID and appends either `--session-id` for new sessions or `--resume <id>` when continuing a linked session (lines 17-27). The implementation checks the harness type and constructs the appropriate argument vector:

```rust
// Example: building a launch plan for Claude Code
let plan = build_launch_plan(
    ManagedHarness::Claude,
    None,
    vec![OsString::from("--model"), OsString::from("opus")],
    Some("a1b2c3d4")    // linked session ID supplied by ai-memory
)?;
// `plan.args` now contains "… --resume a1b2c3d4" and `plan.expected_session_id` is Some("a1b2c3d4")

```

For **Codex**, the builder handles both interactive and non-interactive modes. When executing commands via `exec`, it rewrites the argument list to insert `resume <id>` immediately after the subcommand (lines 28-44). This ensures Codex receives the session directive in the correct position within the argument hierarchy:

```rust
// Example: building a launch plan for Codex (non-interactive)
let plan = build_launch_plan(
    ManagedHarness::Codex,
    None,
    vec![OsString::from("exec"), OsString::from("some-script")],
    Some("deadbeef")
)?;
// `plan.args` becomes ["exec","resume","deadbeef","some-script"]

```

The `LaunchPlan` struct returned by this function includes an `expected_session_id` field that records the UUID the harness will use. This allows the CLI—specifically [`crates/ai-memory-cli/src/commands/run.rs`](https://github.com/akitaonrails/ai-memory/blob/main/crates/ai-memory-cli/src/commands/run.rs)—to correlate later observations with the same logical session, regardless of which native tool generated them.

## Guarding Against Conflicting Session Selectors

Before injecting any automatic selectors, ai-memory verifies whether the user has already specified native resume or continue flags. The `has_native_session_selector` function (lines 85-95 in [`crates/ai-memory-workstream/src/harness.rs`](https://github.com/akitaonrails/ai-memory/blob/main/crates/ai-memory-workstream/src/harness.rs)) scans the CLI arguments for harness-specific patterns like `--resume`, `--continue`, or `--fork`.

This detection prevents the automatic selector from overriding explicit user choices and guarantees idempotency when a linked session is requested. If the function returns true, `build_launch_plan` skips injection entirely:

```rust
// Detect whether the user already provided a native selector
if has_native_session_selector(ManagedHarness::Codex, &cli_args) {
    // Do not inject our own `resume` flag – the user is in full control
}

```

This guard clause ensures that power users retain full control over native session management while casual users benefit from automatic continuity.

## Cross-Harness Persistence via the Managed-Workstream Ledger

Session continuity extends beyond individual process lifecycles through the managed-workstream ledger, documented in [`docs/ARCHITECTURE.md`](https://github.com/akitaonrails/ai-memory/blob/main/docs/ARCHITECTURE.md) (lines 58-66). This ledger stores portable transcripts that can attach to any later harness invocation, effectively decoupling the logical session from the native tool implementing it.

When `ai-memory run` executes, it resolves the most recent usable native session—or creates a fresh one—and injects the stored transcript tail into the new harness. The implementation in [`crates/ai-memory-workstream/src/transcript.rs`](https://github.com/akitaonrails/ai-memory/blob/main/crates/ai-memory-workstream/src/transcript.rs) handles reading and writing these portable records, while [`crates/ai-memory-store/src/writer.rs`](https://github.com/akitaonrails/ai-memory/blob/main/crates/ai-memory-store/src/writer.rs) persists observations tied to a `SessionId` in SQLite.

The workflow operates as follows:

1. **CLI Parsing**: Arguments are scanned for existing session selectors via `has_native_session_selector`
2. **Launch Plan Construction**: `build_launch_plan` generates or reuses a UUID and injects the appropriate selector
3. **Native Execution**: The harness receives the selector and creates a native session (e.g., Claude Code emits a `session-id` page)
4. **Event Hooking**: Lifecycle events (SessionStart, SessionEnd) are sanitized and written to SQLite, with transcripts appended to the workstream ledger
5. **Cross-Harness Continuation**: Later harnesses read the ledger, discover previous native sessions, and resume using stored selectors

Because the logical session ID persists in both the `LaunchPlan.expected_session_id` field and the workstream ledger, every observation and LLM-driven consolidation links back to the same abstract session, regardless of which native tool the user invokes.

## Summary

- **Unified launch-plan builder**: The `build_launch_plan` function in [`crates/ai-memory-workstream/src/harness.rs`](https://github.com/akitaonrails/ai-memory/blob/main/crates/ai-memory-workstream/src/harness.rs) injects native-specific session selectors (e.g., `--session-id` for Claude Code, `resume` for Codex) while tracking the expected session ID.
- **Conflict prevention**: `has_native_session_selector` guards against overriding user-specified flags, ensuring explicit session choices take precedence over automatic injection.
- **Persistent ledger**: The managed-workstream ledger stores portable transcripts in SQLite via [`crates/ai-memory-store/src/writer.rs`](https://github.com/akitaonrails/ai-memory/blob/main/crates/ai-memory-store/src/writer.rs), enabling logical sessions to survive across different native harness invocations.
- **Cross-harness compatibility**: The architecture supports Claude Code, Codex, OpenCode, and Kiro through harness-specific argument rewriting without requiring changes to the underlying session storage model.

## Frequently Asked Questions

### How does ai-memory prevent duplicate session IDs when users specify their own resume flags?

The `has_native_session_selector` function scans incoming CLI arguments for harness-specific resume, continue, or fork flags before injection occurs. If it detects an existing selector, `build_launch_plan` skips automatic injection, ensuring user-specified session IDs take precedence and preventing duplicate or conflicting identifiers.

### What happens to session data when switching from Claude Code to Codex?

Session data persists in the managed-workstream ledger as portable transcripts. When switching harnesses, `ai-memory run` reads the ledger to discover the most recent native session, generates the appropriate selector for the new harness (e.g., converting a Claude Code session ID to a Codex `resume` argument), and injects it into the new command line. The logical session ID remains constant while the native representation adapts to the target harness.

### Where does ai-memory store the logical session transcripts?

Transcripts are stored in a SQLite database through [`crates/ai-memory-store/src/writer.rs`](https://github.com/akitaonrails/ai-memory/blob/main/crates/ai-memory-store/src/writer.rs), which persists observations tied to a `SessionId`. Additionally, [`crates/ai-memory-workstream/src/transcript.rs`](https://github.com/akitaonrails/ai-memory/blob/main/crates/ai-memory-workstream/src/transcript.rs) manages the portable transcript format that allows later harnesses to read and resume from previous session states, effectively creating a hardware abstraction layer for session storage.

### How does the LaunchPlan structure track the expected session ID?

The `LaunchPlan` struct returned by `build_launch_plan` contains an `expected_session_id` field of type `Option<String>`. When the builder generates a new UUID or receives a linked session ID, it populates this field with the identifier that will be passed to the native harness. The CLI uses this field to correlate subsequent observations and lifecycle events with the correct logical session, ensuring continuity even when the underlying native session identifier format differs between harnesses.