* fix(api): explicit sweep scope, audit log and atomic delete for completed batches
Follow-up to #12969 (GHSA-wvxc-jp3v-5mg5) from the omni-code-review battery
(LEDGER-1/2/3/4/7/8/9/10):
- deleteCompletedBatches takes an explicit scope `{ apiKeyId } | { allTenants: true }`;
a missing/empty id throws instead of silently sweeping the whole instance
- route branches on the dashboard session explicitly; keys only sweep their own
batches; batches with no owner stay out of a key-scoped sweep on purpose (JSDoc)
- every sweep is logged (warn for instance-wide, info for key-scoped); a failing
sweep returns a sanitized 500 via buildErrorBody
- the file soft-deletes, checkpoint delete and batch delete run in one transaction;
the empty catch around deleteFile now logs the failure
- route-level regression test through the real handler; test files self-isolate
their DATA_DIR so the single-file command never touches ~/.omniroute
- changelog.d fragment
* fix(api): key-first sweep scope, audit at warn, mixed-scope guard for completed batches
Round-2 findings of the omni-code-review battery on the previous commit:
- a presented API key always scopes the sweep to that key, even when the request
also carries a dashboard session cookie (parity with GET /v1/batches; a leaked
or over-shared key can never widen a destructive sweep); only a session without
a key sweeps the whole instance
- both sweep modes log at warn so the audit trail survives APP_LOG_LEVEL=warn;
the failure log carries the error stack
- a scope carrying both apiKeyId and allTenants is rejected instead of widening
- changelog fragment links the PR
* test(batches): pin APP_LOG_LEVEL in the sweep-ownership test so the log assertion is environment-independent
* fix(api): fail closed on an unresolvable API key in the completed-batch sweep; no-op key sweeps log at info
Resynced onto the release tip after #12051/#12052/#12053 landed. Same LKGP-clear conflict as #12053 (kept the current clearStaleLKGP() helper at both call sites). One additional issue this final phase's combined-worktree validation surfaced: clearStaleLKGP() itself (added by #12013, which none of the 4 phase PRs could have seen since it landed after they were authored) still had a dynamic `await import("@/lib/localDb")` — a real break once this PR deletes the barrel. Fixed to `await import("@/lib/db/settings")`, matching the direct-import pattern used at every other call site. typecheck:core, check-db-rules, check:cycles, and the eslint-import-boundaries regression test (3/3, including "G14 rejects localDb barrel imports") all green after resync — zero barrel-importing production files remain. Nice clean 5-phase migration, and thanks for taking on the full #11795 cleanup.