# How the Auto-Improvement Scheduler Handles Concurrent Projects Without Blocking in ai-memory

> Discover how the auto-improvement scheduler handles concurrent projects without blocking in ai-memory. Learn about state scoping and atomic claim tables for efficient session processing.

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

---

**The auto-improvement scheduler achieves non-blocking concurrency by scoping all state to the `(workspace_id, project_id)` pair and using an atomic claim table that isolates session processing across projects.**

The `ai-memory` repository implements a background auto-improvement system that reviews completed sessions and generates learning proposals. To handle multiple projects simultaneously without global locks or cross-project interference, the scheduler employs a lightweight, database-backed coordination mechanism. This design allows concurrent processing while maintaining exactly-once semantics per session.

## Per-Project State Isolation via Watermark Tracking

Each project maintains independent progress tracking through the `auto_improve_scheduler_state` table. A single row per project stores a `watermark_ended_at` timestamp representing the latest processed session end time. This watermark is strictly scoped to the `(workspace_id, project_id)` pair, ensuring that projects advance at their own pace without coordination overhead.

When the scheduler runs, it queries `auto_improve_scheduler_state` for the current watermark, then selects any session whose `ended_at` is newer than that watermark within the same workspace and project scope. According to the implementation in [`crates/ai-memory-store/src/reader.rs`](https://github.com/akitaonrails/ai-memory/blob/main/crates/ai-memory-store/src/reader.rs) at line 2708, this query ensures each project advances independently of others.

## Atomic Session Claims for Lock-Free Coordination

Before processing a session, the scheduler attempts to claim it atomically. The function `claim_auto_improve_scheduler_session` in [`crates/ai-memory-store/src/writer.rs`](https://github.com/akitaonrails/ai-memory/blob/main/crates/ai-memory-store/src/writer.rs) (lines 1611–1615) handles this coordination without explicit locks.

### The Insert-or-Ignore Pattern

The claim mechanism uses an `INSERT OR IGNORE ... SELECT 1 FROM auto_improve_scheduler_state ...` query to insert a row into `auto_improve_scheduler_claims`. This creates a unique entry keyed by `(workspace_id, project_id, session_id)`. Only the first concurrent caller succeeds; subsequent attempts see the existing claim and skip the session. This atomic operation eliminates blocking waits, limiting contention to a cheap, scoped `INSERT`.

### Idempotence Across Restarts

Because claim rows persist in the SQLite database, a scheduler that crashes or restarts will re-read the `auto_improve_scheduler_claims` table and avoid re-processing sessions. The watermark advances only after successful completion, guaranteeing exactly-once semantics per project regardless of process restarts. The initialization logic in `ensure_auto_improve_scheduler_state` and the claim function are implemented in [`writer.rs`](https://github.com/akitaonrails/ai-memory/blob/main/writer.rs) between lines 1593 and 1615.

## Database-Level Integrity Enforcement

SQLite triggers enforce strict scoping to prevent cross-project contamination. The trigger `auto_improve_scheduler_claims_session_pairing_ai`, defined in migration [`V22__auto_improve_scheduler.sql`](https://github.com/akitaonrails/ai-memory/blob/main/V22__auto_improve_scheduler.sql) (lines 31–36), validates that every claim's `workspace_id` and `project_id` match the associated session's scope. This database constraint ensures that even erroneous application code cannot accidentally claim a session for the wrong project.

## Configuration and Operation

The scheduler operates as a background job that iterates over projects, applying the claim-and-process pattern independently to each. It can be triggered via the CLI command `memory_auto_improve` or launched automatically by the server when enabled.

Configuration is controlled via TOML:

```toml
[auto_improve.scheduler]
enabled = true          # Turn the background reviewer on/off

interval_seconds = 300  # How often the scheduler wakes up

```

The core scheduling loop resides in [`crates/ai-memory-consolidate/src/auto_improve_schedule.rs`](https://github.com/akitaonrails/ai-memory/blob/main/crates/ai-memory-consolidate/src/auto_improve_schedule.rs), which orchestrates the candidate selection, claiming, and review invocation.

### Practical Implementation Example

To interact with the scheduler programmatically:

```rust
// Ensure the scheduler state exists for a specific project
store
    .ensure_auto_improve_scheduler_state(workspace_id, project_id)
    .await?;

// Attempt to atomically claim a session for processing
let claimed = store
    .claim_auto_improve_scheduler_session(
        workspace_id,
        project_id,
        session_id,
        session_ended_at,
    )
    .await?;

if claimed {
    // This instance owns the session – run the reviewer and stage proposals
} else {
    // Another instance already claimed this session; skip it
}

```

The data model definitions in [`crates/ai-memory-store/src/auto_improve.rs`](https://github.com/akitaonrails/ai-memory/blob/main/crates/ai-memory-store/src/auto_improve.rs) describe the scheduler tables and their relationships, while [`crates/ai-memory-store/src/ops.rs`](https://github.com/akitaonrails/ai-memory/blob/main/crates/ai-memory-store/src/ops.rs) contains migration helpers for workspace ID changes that maintain scheduler state integrity.

## Summary

- **Per-project watermarks** in `auto_improve_scheduler_state` isolate progress tracking to `(workspace_id, project_id)` pairs, preventing cross-project blocking.
- **Atomic claims** using `INSERT OR IGNORE` on `auto_improve_scheduler_claims` enable lock-free coordination where only the first caller processes a session.
- **Exactly-once semantics** are guaranteed by persisting claims in SQLite and advancing watermarks only after successful processing, surviving crashes and restarts.
- **Database triggers** enforce scope integrity, ensuring claims cannot leak across projects even in the presence of application bugs.
- **No global locks** exist; the scheduler iterates over projects independently, with contention limited to cheap, scoped inserts on the claims table.

## Frequently Asked Questions

### How does the auto-improvement scheduler prevent two workers from processing the same session?

The scheduler uses the `claim_auto_improve_scheduler_session` function in [`writer.rs`](https://github.com/akitaonrails/ai-memory/blob/main/writer.rs) to perform an atomic `INSERT OR IGNORE` into the `auto_improve_scheduler_claims` table. This creates a unique row keyed by `(workspace_id, project_id, session_id)`. Only the first concurrent caller succeeds; subsequent attempts detect the existing row and skip the session, ensuring exclusive processing without explicit locks.

### What happens if the scheduler process crashes while processing a session?

Because claim rows persist in the SQLite database, a restarted scheduler re-reads the `auto_improve_scheduler_claims` table on startup and recognizes sessions already marked as claimed. The `watermark_ended_at` timestamp is only updated after successful processing completes, so incomplete work is automatically picked up where it left off, maintaining exactly-once semantics.

### Is there a global lock that blocks all projects during auto-improvement scheduling?

No. The design explicitly avoids global locks by scoping all scheduler state to individual projects via the `(workspace_id, project_id)` pair. Each project maintains its own watermark and claim set. The background job simply iterates through projects and processes them independently, so activity on one project never blocks or interferes with another.

### How does the system prevent a session from being claimed by the wrong project?

SQLite triggers enforce this at the database level. The trigger `auto_improve_scheduler_claims_session_pairing_ai` in migration [`V22__auto_improve_scheduler.sql`](https://github.com/akitaonrails/ai-memory/blob/main/V22__auto_improve_scheduler.sql) validates that every claim's `workspace_id` and `project_id` columns match the corresponding session's scope. If a claim attempt violates this constraint, the database rejects the insert, preventing cross-project data leakage.