# GOAP: PR #611 CI Failure Remediation

**Date**: 2026-06-09
**Type**: CI Fix (Bug Fix + Test Infrastructure)
**Priority**: P1 — Blocks merge of #611 (local/offline storage mode)
**PR**: [#611 — Expose local/offline mode as a first-class config path](https://github.com/d-o-hub/rust-self-learning-memory/pull/611)
**Issue**: Fixes #610
**Related ADR**: ADR-056

---

## Problem Statement

PR #611 introduces local/offline SQLite storage as a first-class path
(`StorageMode`, `TursoStorage::new_local/new_in_memory/new_remote`, CLI
`--storage-mode`/`--db-path` flags). Four CI checks fail:

| Check | Result | Root Cause |
|-------|--------|------------|
| Tests | ❌ fail | SIGSEGV in `do-memory-storage-turso` relationship tests |
| Multi-Platform Test (ubuntu-latest) | ❌ fail | Same SIGSEGV |
| Multi-Platform Test (macos-latest) | ❌ fail | Same SIGSEGV |
| Code Coverage Analysis | ❌ fail | CLI `test_cli_help_output` snapshot mismatch (exits before turso) |

All passing/skipping checks are unaffected.

---

## Root Cause Analysis

### Failure 1 — SIGSEGV in turso relationship tests (8 tests)

Failing: `relationships::tests::{test_add_relationship, test_episode_pattern_relationships,
test_get_dependencies, test_get_relationships, test_get_weighted_neighbors,
test_relationship_exists, test_remove_relationship, test_weighted_relationships}`

| Layer | Behavior | Issue |
|-------|----------|-------|
| Test setup | PR rewrote `create_test_storage()` to use `TursoStorage::new_local(path)` | New constructor path |
| `new_local` | Delegated to `new()` → `with_config(url, "", TursoConfig::default())` | Uses default config |
| `TursoConfig::default()` | `enable_pooling: true`, `enable_keepalive: true` | Pool + keepalive enabled |
| `with_config` (under `keepalive-pool` feature) | Builds `ConnectionPool` + `KeepAlivePool`, calls `start_background_task()` | Spawns a Tokio task holding libsql connections |
| `#[tokio::test]` teardown | Per-test runtime is dropped while keepalive task + pooled libsql connections are still live | libsql connections dropped **outside** a runtime → **SIGSEGV (signal 11)** |

**Why it passed locally but failed in CI**: Cargo feature unification. CI runs
`cargo nextest run --workspace`, and `memory-core` + `benches` enable
`do-memory-storage-turso/keepalive-pool`. Under `--workspace`, the turso test
binary is compiled **with** `keepalive-pool`, activating the background-task
path. A bare `cargo nextest run -p do-memory-storage-turso` does not enable the
feature, so the crash is invisible.

**Reproduction**:
```bash
cargo nextest run -p do-memory-storage-turso --features keepalive-pool relationships::tests
# => 8 SIGSEGV
```

**Key insight**: Connection pooling and keep-alive are *network* optimizations
for remote libSQL servers. For a single local SQLite file they add no value and
are actively harmful (multiple handles + a background task over one file).

### Failure 2 — CLI help snapshot mismatch

`do-memory-cli::snapshot_tests::test_cli_help_output` fails. The new
`--storage-mode` / `--db-path` flags were added to the snapshot, but the
blank description-continuation lines were written **without** the trailing
whitespace that clap actually emits (`          ` = 10 spaces). The committed
`.snap` had those lines stripped, so the assertion diff was whitespace-only.
The Coverage job runs the CLI test suite first and bails (exit 101) before
reaching the turso tests.

---

## Goal State

- [x] All 8 turso relationship tests pass with `keepalive-pool` enabled
- [x] `new_local` / `new_in_memory` are production-safe (no SIGSEGV on drop)
- [x] CLI help snapshot matches actual clap output (trailing whitespace correct)
- [x] `fmt`, `clippy -D warnings`, workspace test compile all clean
- [x] No behavior change for remote mode

---

## GOAP Plan (atomic actions)

| # | Action | Owner crate | Verification |
|---|--------|-------------|--------------|
| A1 | Add `TursoStorage::local_config()` — `enable_pooling=false`, `enable_keepalive=false` | `memory-storage-turso` | unit compile |
| A2 | Route `new_local` + `new_in_memory` through `with_config(.., local_config())` | `memory-storage-turso` | repro test green |
| A3 | Keep relationship tests on `new_local` (exercises the fixed path) | `memory-storage-turso` | `--features keepalive-pool` green |
| A4 | Regenerate `cli_help_output.snap` via `cargo insta accept` (correct trailing ws) | `memory-cli` | snapshot test green |
| A5 | `fmt` + `clippy --features keepalive-pool --all-targets` | workspace | zero warnings |
| A6 | Workspace test compile (feature-unified) + turso/cli runtime tests | workspace | all pass |

### Execution Strategy: **Sequential**

A1→A2 are causally linked (config feeds constructors). A3/A4 are independent
fixes but both depend on A2 compiling. A5/A6 are validation gates run last.

```diagram
╭────────────╮   ╭────────────╮   ╭──────────────╮
│ A1 local_  │──▶│ A2 reroute │──▶│ A3 turso     │
│   config() │   │  ctors     │   │   tests pass │
╰────────────╯   ╰─────┬──────╯   ╰──────┬───────╯
                       │                  │
                       ▼                  ▼
                 ╭────────────╮     ╭──────────────╮
                 │ A4 snapshot│────▶│ A5/A6 gates  │
                 │  regen     │     │  fmt/clippy/ │
                 ╰────────────╯     │  tests       │
                                    ╰──────────────╯
```

---

## Fix Detail

`memory-storage-turso/src/lib_impls/constructors_basic.rs`:

```rust
/// Local/in-memory backends: pooling + keepalive are remote-only optimizations
/// and cause SIGSEGV when libsql connections drop outside a Tokio runtime.
fn local_config() -> TursoConfig {
    TursoConfig {
        enable_pooling: false,
        #[cfg(feature = "keepalive-pool")]
        enable_keepalive: false,
        ..TursoConfig::default()
    }
}

pub async fn new_local(path: impl AsRef<Path>) -> Result<Self> {
    // ...normalize to file: url...
    Self::with_config(&url, "", Self::local_config()).await
}

pub async fn new_in_memory() -> Result<Self> {
    Self::with_config(":memory:", "", Self::local_config()).await
}
```

> Bonus: a single connection for `:memory:` also fixes the latent
> schema-isolation bug (each pooled `db.connect()` to `:memory:` is a separate,
> empty database — schema from `initialize_schema()` would be invisible).

---

## Validation Results (local)

| Gate | Command | Result |
|------|---------|--------|
| Repro (pre-fix) | `nextest -p ...-turso --features keepalive-pool relationships::tests` | 8 SIGSEGV ✅ confirmed |
| Repro (post-fix) | same | 8 passed |
| Turso full | `nextest -p do-memory-storage-turso --features keepalive-pool` | 378 passed |
| Snapshot | `nextest -p do-memory-cli test_cli_help_output` | 1 passed |
| Turso + CLI | `nextest -p ...-turso -p do-memory-cli --features .../keepalive-pool` | 739 passed |
| fmt | `./scripts/code-quality.sh fmt` | clean |
| clippy | `cargo clippy -p ...-turso --features keepalive-pool --all-targets` | zero warnings |
| Workspace test compile | `nextest --workspace --exclude benches/examples/test-utils --no-run` | compiled |

---

## Files Changed (fix)

- `memory-storage-turso/src/lib_impls/constructors_basic.rs` — `local_config()` + reroute
- `memory-storage-turso/src/relationships_tests.rs` — kept on `new_local` (remote PR version)
- `memory-cli/tests/snapshots/snapshot_tests__cli_help_output.snap` — regenerated

---

## Lessons

- **Feature unification bites tests**: `-p <crate>` ≠ `--workspace`. Always
  reproduce CI test failures with the features the workspace enables on the
  crate (here `keepalive-pool`).
- **libsql drop-outside-runtime → SIGSEGV**: never let pooled/keepalive libsql
  connections outlive a `#[tokio::test]` runtime. Local modes must not pool.
- **Snapshot trailing whitespace**: regenerate `.snap` via `cargo insta accept`,
  never hand-edit — clap emits trailing spaces on continuation lines.
