personal memory agent

refactor(todos): collapse 3 divergent moves onto atomic move_entry; drain bare-returns master

The todos app had three independent two-step "move a todo" implementations (route /move cross-day, route edit-with-facet-change, CLI move cross-facet), each doing append-to-target then cancel-source as separate locked steps — a replayed request duplicated a todo (the /move route appended a second copy on a same-item-moved-twice). Add TodoChecklist.move_entry(src_day, src_facet, line_number, dst_day, dst_facet, *, text=None): one atomic op that holds both files' locks in canonical (sorted-path) order, loads + re-validates the source under-lock, appends to the target, then cancels the source last — the safe direction, so a crash between the two writes leaves the item in the target AND still open in the source, never lost from both. Returns (created, cancelled). Typed exceptions (TodoNotMovableError, TodoMovePartialError) let each surface translate uniformly; routes and CLI become thin adapters. Guards the src==dst self-block (nested same-path hold_lock would LockTimeout). move_entry is policy-neutral on completion (it carries completed state); each caller keeps its own precondition. Drain the 5 JSON-governed `return "", 404` invalid-day guards to error_response(INVALID_DAY, status=404) and drop the todos bare-return entry from the api-conventions allowlist. CPO note — completed-item move policy diverges by surface: the CLI rejects a completed move pre-call; HTTP carries completion. Preserved here, not reconciled; reconciling it is an observable contract change (CPO lane, not VPE). Related nuance: the CLI no longer re-checks completion under-lock (move_entry is policy-neutral); the pre-call check covers every non-racing input. Discovered, deferred: solstone/think/tools/call.py facet-merge moves open todos with the same append-then-cancel two-step. Out of scope (merge = separate hardening task); fold it onto move_entry there once that task runs. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>