fmemory 3.0.0

Memory library for the fiddlesticks agent harness framework
Documentation
# Context Layer API

`fmemory` provides durable state and transcript persistence for Fiddlesticks.

It is the persistence layer used by `fharness` for initializer/coding-run artifacts and by `fchat` (through an adapter) for conversation history.

## Responsibilities

- Persist session bootstrap artifacts (manifest, feature list, progress, run checkpoints)
- Persist transcript messages
- Expose a `MemoryBackend` contract for harness logic
- Adapt memory transcript storage to `fchat::ConversationStore`

`fmemory` does **not**:

- Execute model calls (`fprovider`)
- Orchestrate turns (`fchat`)
- Execute tools (`ftooling`)
- Decide multi-run harness strategy (`fharness`)

## Add dependency

```toml
[dependencies]
fmemory = { path = "../fmemory" }
```

## Core types

- `MemoryBackend`: async persistence trait
- `SqliteMemoryBackend`: default durable backend implementation
- `PostgresMemoryBackend`: durable backend using PostgreSQL
- `FilesystemMemoryBackend`: JSON-file durable backend implementation
- `InMemoryMemoryBackend`: ephemeral test-oriented backend implementation
- `MemoryBackendConfig`: backend selection configuration
- `MemoryConversationStore`: adapter implementing `fchat::ConversationStore`
- `SessionManifest`: harness session metadata (+ schema/harness versions)
- `FeatureRecord`: feature checklist item
- `ProgressEntry`: per-run progress log entry
- `RunCheckpoint`: run lifecycle status record
- `BootstrapState`: manifest + feature/progress/checkpoint aggregate

## Session initialization guards

`MemoryBackend` includes explicit initializer-safe methods:

- `is_initialized(session_id)`
- `initialize_session_if_missing(...)`

`initialize_session_if_missing(...)` is idempotent:

- returns `true` when it creates state for the first time
- returns `false` when state already exists (no overwrite)

## Basic backend usage

```rust
use fcommon::SessionId;
use fmemory::prelude::*;

async fn seed_backend(backend: &dyn MemoryBackend) -> Result<(), MemoryError> {
    let session = SessionId::from("session-1");

    let created = backend
        .initialize_session_if_missing(
            &session,
            SessionManifest::new(session.clone(), "feature/init", "Initialize harness"),
            vec![FeatureRecord {
                id: "feature-1".to_string(),
                category: "functional".to_string(),
                description: "Initializer artifacts exist".to_string(),
                steps: vec!["write init state".to_string()],
                passes: false,
            }],
            Some(ProgressEntry::new("run-1", "Initializer scaffold created")),
            Some(RunCheckpoint::started("run-1")),
        )
        .await?;

    let _ = created;
    Ok(())
}
```

## Backend selection

`fmemory` supports configurable backend construction:

```rust
use fmemory::{MemoryBackendConfig, create_default_memory_backend, create_memory_backend};

let sqlite_default = create_default_memory_backend()?;
let in_memory = create_memory_backend(MemoryBackendConfig::InMemory)?;
let sqlite_custom = create_memory_backend(MemoryBackendConfig::Sqlite {
    path: "./state/fmemory.sqlite3".into(),
})?;
let postgres = create_memory_backend(MemoryBackendConfig::Postgres {
    host: "127.0.0.1".into(),
    port: 5432,
    database: "fmemory".into(),
    username: "postgres".into(),
    password: "postgres".into(),
})?;
let filesystem = create_memory_backend(MemoryBackendConfig::Filesystem {
    root: "./state/fmemory".into(),
})?;

let _ = (sqlite_default, in_memory, sqlite_custom, postgres, filesystem);
# Ok::<(), fmemory::MemoryError>(())
```

Default backend path is `~/.fiddlesticks/fmemory.sqlite3` (or `%USERPROFILE%/.fiddlesticks/fmemory.sqlite3` on Windows). Override it with `FMEMORY_SQLITE_PATH`.

## `fchat` integration adapter

`MemoryConversationStore` lets `fchat` use `fmemory` without direct store duplication:

```rust
use std::sync::Arc;

use fmemory::prelude::*;

let backend: Arc<dyn MemoryBackend> = Arc::new(InMemoryMemoryBackend::new());
let store = MemoryConversationStore::new(backend);
let _ = store;
```

## Versioning fields

`SessionManifest` includes:

- `schema_version` (default: `1`)
- `harness_version` (default: `"v0"`)

These support forward migration for future harness behaviors.

## Error model

`MemoryErrorKind` variants:

- `Storage`
- `NotFound`
- `InvalidRequest`
- `Other`