What was caught, how it was caught — and why the how is the whole argument.
MoneyLedger Password Vault · 19–22 July 2026 · shipped v1.4.1
$ bugs --project vault --sort severity [CRITICAL] Vault reported "not set up" after every page reload found real-browser · Christopher reloaded the page why pvLoadMeta() existed but was never called at boot — data was there, app couldn't see it fix added to the boot chain; swallows read errors so it can't block startup [CRITICAL] Auto-lock silently destroyed typed credentials found real-browser · lock fired mid-entry on a 15s test build why modal left open over a locked vault; Save then discarded everything, no message fix capture draft → close modal → restore it pre-filled after unlock [CRITICAL] "Clipboard clears in 20s" never cleared — a false security promise found real-browser · pasted after 20s, password still there why deferred writeText() has no user activation; browser refused it, rejection unhandled fix clear on next interaction + manual button + wording that no longer overpromises [CRITICAL] Key-file export downloaded only one of two required files found real-browser · Firefox — would likely have passed on Chrome why two downloads from one click; the modal save dialog dropped one fix two explicit steps, one file each; cancelling now writes nothing [HIGH] Mobile layout had never once activated found conversation · "how does mobile work?" — nobody had asked why no viewport meta, so phones rendered ~980px and the 860px breakpoint never fired fix added viewport/charset/title — and it exposed a 2nd bug hiding behind it [HIGH] Mobile nav: 7 buttons in a hardcoded 6-column grid found code review · only visible once the layout could render fix auto-sizing columns so it can't break again when nav items change [HIGH] Dismissing the key-file step orphaned an unopenable backup ← regression from the fix above found adversarial audit · DEEPAUDIT #1 fix stage both files behind the one click; cancel writes nothing at all [HIGH] Failed write left a "phantom vault" that unlocked EMPTY found adversarial audit · DEEPAUDIT #2, on brand-new restore code why the "a vault exists" marker was written BEFORE the encrypted payload fix data first, marker last, full rollback on failure [HIGH] Damaged metadata made the app OFFER to overwrite a recoverable vault found adversarial audit · DEEPAUDIT #2 why corrupt meta → "no vault here" → both setup paths would clobber intact ciphertext fix detect orphaned data, explain it, refuse both destructive actions [MEDIUM] Hostile .json backup could execute script in a page holding a decrypted vault found pentest · traced import validation → unescaped confirm-dialog body fix whitelist the field on import AND escape the sink; payload neutralisation executed [MEDIUM] Draft-preservation kept plaintext credentials past teardown ← regression from the auto-lock fix found adversarial audit · DEEPAUDIT #1 fix dropped on explicit lock and vault deletion; kept only for auto-lock, by design [MEDIUM] Vault exports (.mlvault / .key) were not gitignored found pre-merge check · stray export sitting untracked in the working tree fix both patterns ignored before the release commit + 14 UX/design defects logged via FOUNDUX and batch-fixed (unreadable wizard label on some themes · borderless theme placards · missing restore entry point · ambiguous export-vs-import grouping · empty entries saveable · phantom "undefined" labels · cramped modal footers · a 1px seam over photo sidebars · …) $ summary automated (node/CI) proved crypto correct · caught 0 behavioral defects human in browser 5 defects — incl. all 4 CRITICAL adversarial re-read 5 defects — 2 of them created BY earlier fixes
The second audit is the point. The first pass came back clean against the code as it stood — it just wasn't the final code. Two of the most dangerous defects in the whole build were introduced by the fixes for earlier ones, and only existed because a second adversarial read was requested before merging. Both sat in vault creation, the one path where a mistake is unrecoverable.
Keyword-triggered rituals in a global operating contract, composed with a per-project charter. They are the reason the run above stayed inspectable rather than becoming a wall of chat.
| Keyword | What it triggers | Why it exists |
|---|---|---|
| SUNRISE | Pull first, then read the baton, backlog and charter; report state + a proposed first move | Two machines — reading before pulling orients you off a stale baton |
| SUNSET | Back up · rewrite the baton · update backlog/changelog · save memory · log usage · commit & push | Never end raw; the next session starts oriented, not archaeological |
| DEEPAUDIT | Deep correctness pass; verify every finding before reporting; rank CRITICAL→LOW | Caught the two worst bugs — both introduced by earlier fixes |
| FOUNDUX | Flag a UX defect mid-run without derailing; queue it; batch-fix at the end | Invented mid-session — testing kept stalling on cosmetic finds |
| STEP OFF | Execute the agreed plan autonomously; report at natural checkpoints, not every step | Momentum — re-confirming each step is friction |
| GENESIS | Scaffold a new project from the standard kit; set git vs local-only | Every project starts with the same spine |
c55d9d9..v1.4.1), the FOUNDUX
queue in MoneyLedger/TODO.md, and the two audit passes. Severity reflects impact on user data, not
effort to fix. "Found by automation = 0" is not a criticism of automated testing — it proved correctness
continuously and prevented regressions; it simply cannot see what only a person in a browser can.