Build journal
Dated notes from building Rekhon, newest first. Each entry is tied to a decision record in the repository and links to it, so the reasoning can be read at its source rather than taken on trust.
Entries marked “compiled” are assembled from the repository’s own decision records and changelog, not written as first-person recollections. Planned entries are topics with a date, not drafts held back.
Published
5 entriesXIRR without floating point
CompiledA SIP’s cost-to-value ratio understates its return by roughly half, so the app needed a money-weighted return. The textbook way to compute one reaches for Double and a starting guess — both unusable here.
MoneyInvestmentsDeterminismA lint rule instead of a reviewer
CompiledRupees are integer paise in Rekhon, and the rule is enforced by a custom Android Lint module that fails the build. The interesting part is why a reviewer was not enough.
MoneyLintKotlinMoney is a Long, and Double fails my build
Compiled0.1 + 0.2 != 0.3. In a finance app that is not a curiosity, it is a bug you ship and find months later.
MoneyLintThe seven rules I cannot break
CompiledSeven rules written down before the first line of application code, under a heading saying they override feature requests. Six of them later became detectors that fail the build.
ProductRulesNumbers from math, words from AI
CompiledA language model in a finance app will eventually state a confident, wrong rupee figure. The architecture answers by never handing it one to state, and checking the sentence afterwards anyway.
AIGuardrails
Planned
Weekly, on TuesdaysThese are scheduled topics, each tied to a decision already recorded in the repository. They are not drafts being withheld, and nothing below has been written yet.
₹1,23,456.78 is not a formatting detail
Lakh and crore, Indian digit grouping, and why the formatter is a correctness concern rather than a cosmetic one.
MoneyFormatter — Indian digit grouping, lakh and croreThe gate that never ran
A coverage gate that passed because it was measuring almost nothing, and how that was found.
ProjectExtensions.configureCoverage — the coverage gateI put a real LLM on the phone and gave it no numbers
A real model running on the phone, handed figures already rendered and no arithmetic to do. The decision record publishes with the work.
My safety check was stricter than my safety gate
How an inner sanitiser ended up stricter than the guardrail it was guarding, and why a refused reply looked identical to a phone with no model installed. The decision record publishes with the work.
How do you test a feature that writes sentences?
The eval set and frozen expectations that stand in for a unit test when the output is prose. Publishes with the work.
A truncated download is not a model
Verifying length and digest before trusting a model file, and treating the presence of a verified file as the opt-in. Publishes with the work.
Your receipts never leave your phone
What on-device OCR has to guarantee, and how the consents dashboard shows the record rather than just the switch.
ADR-0059 — the consents dashboard shows the record, not just the switch“Erase everything” should destroy the key, not the files
Why an erase destroys keys first, and then says plainly what it cannot reach.
ADR-0060 — an erase destroys keys first and says what it cannot reachEvery verdict names the rule that fired
Thresholds as versioned data rows instead of constants buried in code, and what that buys six months later.
ai/rules — the versioned rulebook every insight citesYour SIP’s return is roughly double what your app shows
The user-facing half of the XIRR story: what the common portfolio screen gets wrong, and by how much.
domain/engines/investment — the holdings and XIRR engineOpen source as proof, not marketing
Why the source is public, and why the AGPL linking exception is deliberately narrow.
LICENSE — AGPL-3.0 with an ML Kit linking exceptionFifteen weeks in public — and what I am looking for
A closing retrospective on the series, and what it was for.
The repositoryAccount Aggregator, and the enum that had to ship first
Why a reserved enum value shipped before the integration it describes, and what that costs if the design later changes.
ADR-0074 — AA is designed and the reserved source value had to ship nowWhy Money is what blocks iOS
What a KMP spike actually found, and why the one type that must never change is the one that stands in the way.
ADR-0075 — Android-free is not KMP-portable, and Money is the blockerOrphaned blobs, and shredding what you cannot find
How a fix produced its own finding, and what “erase everything” has to mean when a blob has no row left to reach it from.
ADR-0078 — import_batches exists, and the wipe is checked, not trusted