System · Chronicle

The Cartarch Chronicle.

A journal of releases — what we shipped, what we refined, what we resolved, and a short note from the Archivist on each.

Folio IV · Issue 4 · Entry 0
v4.4.0 · 11 July 2026 · published

Chronicle of Changes.

Added2 entries

  • Physical Audit Mode can now audit just PART of a location — pick the sets you're checking ("just the LTR and MH3 cards in Drawer 3") instead of the whole drawer. Starting an audit shows a scope picker: audit everything, or tick sets (with per-set counts and a live selected total) and audit only those.
  • During a scoped audit, scanning a card from a set you didn't pick is filed as 'Out of scope' — acknowledged and shown collapsed, with no reconciliation prompts. It's not treated as an extra.

Refined1 entry

  • Scoped audits don't reset a location's 'last audited' date — that stays driven by the last FULL audit, so a quick partial check never makes a drawer look freshly verified. Scoped runs appear in history badged with their sets.

Resolved1 entry

  • Scoped audits: optional set-code filter at audit start (issue #73 follow-up), strictly additive to the full-location audit.
Notes from the Archivist

Scoped audits narrow Physical Audit Mode to a chosen set of set-codes. Two nullable JSON columns (audit_sessions.scope / audit_log.scope, {"set_codes":[...]}, NULL = full audit) carry the scope; the log copy powers history badges and staleness. A single _audit_expected_rows(session, audit) helper filters the location's rows to the scope and is the ONE source of truth for the snapshot hash, expected set, scan matching, live progress, validate_snapshot, and compute_delta — so scoped is additive and null-scope is byte-for-byte the original behavior (regression-pinned). start_audit(set_codes=...) hashes only the scoped rows, so concurrent edits to unscoped cards at the same location don't trip the optimistic-concurrency check (in-scope edits still do); an empty scope is rejected. record_scan gates scope BEFORE match/partial/extra: a scanned card whose OWN set isn't in scope becomes a new out_of_scope AuditScan (inventory_row_id NULL, like extras but excluded from extra counts/lists), even when an in-scope printing of the same name is expected — the operator scanned what's in hand. compute_delta surfaces out_of_scope as its own action-less list; list_auditable_locations splits last_audited_at (last FULL audit — drives the 'Never' badge) from last_scoped_audit_at; list_all_audit_history returns scope_sets. Routes: GET /audit/start renders a scope picker (full=1 fast path preserves start-immediately; resume/confirm-switch still run first), POST /audit/start starts a scoped audit and re-renders the picker with an error on empty scope. Workspace/reconcile show the scope in the header + a neutral collapsed out-of-scope panel; the hub badges scoped history and shows a 'scoped {date}' subline. Migration verified up/down on SQLite + offline SQL for both dialects. 797 tests green (+13 scope tests).