Christopher Ward · Evidence ← How I Work

Bug Ledger & Method

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


The finding that matters Automated verification proved the cryptography correct — RFC 6238 vectors, encryption round-trips, syntax and wiring parity on every commit. It discovered zero behavioral defects. Every bug below was found by a human driving a real browser, or by adversarially re-reading code that had already passed.

01 The ledger

moneyledger — vault build · bug ledger
$ 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

02 The tally

Found by a human
5
including every CRITICAL
Found by re-reading
5
2 were caused by fixes
Found by automation
0
it proved, it didn't discover

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.

03 The working conventions

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.

KeywordWhat it triggersWhy it exists
SUNRISEPull first, then read the baton, backlog and charter; report state + a proposed first moveTwo machines — reading before pulling orients you off a stale baton
SUNSETBack up · rewrite the baton · update backlog/changelog · save memory · log usage · commit & pushNever end raw; the next session starts oriented, not archaeological
DEEPAUDITDeep correctness pass; verify every finding before reporting; rank CRITICAL→LOWCaught the two worst bugs — both introduced by earlier fixes
FOUNDUXFlag a UX defect mid-run without derailing; queue it; batch-fix at the endInvented mid-session — testing kept stalling on cosmetic finds
STEP OFFExecute the agreed plan autonomously; report at natural checkpoints, not every stepMomentum — re-confirming each step is friction
GENESISScaffold a new project from the standard kit; set git vs local-onlyEvery project starts with the same spine
The one that got invented FOUNDUX came out of a real problem mid-test: every cosmetic defect was interrupting the run. The convention — flag it, queue it, keep going, fix them in one pass — was written into the global contract the moment it was needed, and has applied to every project since.
← Back to How I Work
Provenance. Reconstructed from git history (28 commits, 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.