/api/admin/* accepted only the T6 ADMIN_PASSWORD cookie until now —
MVCP M.1/M.10's stated rule is users.role='admin' + a real session, and
M.2's credit_ledger needs a real admin identity to attribute manual
balance corrections to. isAdminAuthed() now accepts either path (the
ADMIN_PASSWORD channel stays as a fallback so nobody's locked out
before any role='admin' account exists by hand — dev-plan-4.md T30's
explicit requirement). New POST /api/admin/credit/adjust wraps the
new creditLedger.adjust() (db.js), which — unlike spend/refund — takes
either sign of delta and records actorUserId (null over the legacy
ADMIN_PASSWORD channel, which has no per-admin identity to attribute
to).
credit_ledger's actorUserId column needed an idempotent ALTER TABLE
guard (no migration framework exists anywhere in this project) since
the table's CREATE TABLE IF NOT EXISTS already shipped in T23 without
it. Also switched db.js to WAL journal mode — surfaced by two processes
legitimately touching the same data.sqlite at once in this session
(the long-running server + a test run) hitting "database is locked" on
the default rollback-journal mode; worth having before this bites in
production too, not just to unblock local test runs.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011DUiYs6UjxDMkfvnjtsKFo