Build item_store v2 with per-kind fields and batches #315

doneCreated by Claude Code (User hu_yMavF3p1) • Assigned to Claude Code (User hu_yMavF3p1) • Normal priority

Description

The largest part of #309. Built on the existing data model: the revision removal is #320 and the visibility change is #321, both after the switch.

Done (cceec82 single items, 12c0d1d batches)

src/Conderis/Services/Items/ItemStoreTools.cs (not registered until #317), TaskService.Move and CreateMany, DocumentService.CreateMany, BatchLedger; tests in ItemStoreToolsTests, and the tool in BuiltV2ToolsMatchTheMeasuredSchemas.

Single items

  • fields per kind validated on the server; a field of another kind is refused naming its kind and the allowed fields ("'summary' is a field of a memory, not of a task"), description on a task points to content.
  • One patch rule: omitted keeps, null clears; null on a required field (title, summary, status, priority) is refused.
  • One version: a content change needs the version from item_get; a missing or stale one is refused naming the current version. For memories, pages and instructions the version is mapped to its revision internally; proposals record baseVersion next to baseRevisionId. No revision id in any request or response.
  • Moves need no version and keep it: TaskService.Move checks the parent and cycles in a serializable transaction; a page move repeats itself when another move landed in between, so the last move wins. A text change succeeds after a concurrent move (tested).
  • status: blocked is refused with a pointer to item_action block; a new task starts open. type and visibility of a memory are fixed after creation. Instructions need the version once they exist.
  • propose: true forces a proposal for shared memories, pages and instructions; tasks are never reviewed. A missing write permission is the outcome denied with requiredCapability.
  • The agent key setting "always review" was split off as #323.

Batches (decided 2026-09-19: mixed kinds, each kind atomic on its own)

  • Tasks, pages and memories are written by separate services with their own transactions, so a mixed batch cannot be atomic as a whole without rebuilding them on one transaction. Each kind is stored atomically; a failing kind stores nothing of itself, is reported with its error, and leaves the other kinds stored (outcome: partial).
  • Every kind records its result under idempotencyKey:task, :page and :memory. Sending the same batch again replays the stored kinds and retries only the failed one; a reused key with another payload is refused.
  • Up to 100 items and 1 MB; keys unique; parent names an earlier item of the same kind or an existing number; the instructions are not batched.
  • Pages in a batch are published directly and need moderation rights; without them the page group is refused with a hint to propose pages one at a time. Shared memories of a caller who must be reviewed become proposals, one per memory, as memory batches did before.
  • The Conderis Memory Batch v1 file is accepted unchanged under its own idempotency key, so files sent through memory_store replay the same result.
  • The item_store description now says "each kind atomically"; the v2 list is 6,462 characters.

Not covered by an automated concurrency test: two crossed task moves that would form a cycle, and two simultaneous page moves. Both rely on the serializable transactions the services already use (#271).

Connection interrupted or session ended. Reload

Rejoining the server...

Rejoin failed... trying again in seconds.

Failed to rejoin.
Please retry or reload the page.

The session has been paused by the server.

Failed to resume the session.
Please retry or reload the page.