* feat(items): field-level PATCH + conflict envelope + version history
Adds three related item-update primitives (TASK-2022 / IDEA-1480):
- Field-level merge: PATCH `fields_patch` shallow-merges onto the item's
current fields INSIDE the write transaction (null deletes a key), so
concurrent single-field updates no longer clobber each other via the
full-blob read-modify-write. `pad item update` and the MCP `pad_item.update`
action now send only the changed keys.
- Optimistic concurrency: optional `expected_updated_at` on update; on
mismatch the store returns *UpdateConflictError and the handler emits the
pad-structured-error/v1 conflict envelope (HTTP 409, code=update_conflict).
Surfaced on CLI (`--expected-updated-at`) and MCP (`expected_updated_at`).
- Read-only version history: `pad item history <ref>` (alias `versions`) and
MCP `pad_item.history`, reusing the existing item_versions store + versions
endpoint (no new store, no schema change).
MCP ToolSurfaceVersion bumped 0.9 -> 1.0 (new action + param; update
behavior change). No migration required.
Claude-Session: https://claude.ai/code/session_019knGmnHcx5rrgWXQ8V8DZS
* fix(items): address Codex review — dispatcher fields_patch, OCC ordering, date/required guards
Round 1+2 review fixes for TASK-2022:
- HTTP MCP dispatcher (dispatch_http_advanced.go) now sends fields_patch (only
changed keys) instead of a client-side merged full fields blob, and forwards
expected_updated_at — remote MCP callers get the same race-free merge +
optimistic concurrency the CLI/HTTP paths do.
- ValidatePartialFields rejects null-deleting a schema-declared REQUIRED field
(would otherwise persist a blob the full-update validator rejects).
- Open-children guard on the fields_patch path merges the patch onto the IN-TX
locked row inside the precheck (not a stale pre-lock preview), so a
priority-only patch can't false-fire the guard.
- Optimistic-concurrency check now runs BEFORE the open-children precheck in the
store, so a stale expected_updated_at yields update_conflict (not
open_children) — single in-tx re-read shared by both.
- Date auto-population on the patch path only fills an EMPTY current date; an
existing end_date the caller isn't touching is preserved.
Tests added for each fix.
Claude-Session: https://claude.ai/code/session_019knGmnHcx5rrgWXQ8V8DZS