1
Clone and install
2
Start Postgres and Redis
ops/docker-compose.yml.3
Apply the schema
4
Run the gate
5
Watch a transfer move
What pnpm verify actually checks
It is worth knowing what fails and why, because several of these gates are unusual.
Lint, including rules that ban floats near money
Lint, including rules that ban floats near money
ESLint rejects Exactly two files carry a scoped exception with a written justification: the seeded PRNG in
parseFloat, toFixed, Math.round/floor/ceil, and fractional numeric literals in source:packages/chain/src/random.ts, which does 32-bit bit-mixing, and the Jaro-Winkler implementation in services/risk/src/sanctions.ts, where 0.9 is an algorithm threshold rather than an amount.Why money is never a float →Architecture boundaries: two independent layers
Architecture boundaries: two independent layers
dependency-cruiser fails the build on a cross-context import. So does an ESLint no-restricted-imports rule.Both are needed, and finding out why was its own small investigation: dependency-cruiser catches relative imports like ../../ledger/src/posting but silently misses package-name imports like @arc/ledger, because that resolves through node_modules to a dist path which does not exist until after a build.The boundary that wasn’t →Tests: unit, property-based, and chaos
Tests: unit, property-based, and chaos
Roughly 240 tests across the workspace. The ones that matter most are the property suite (ledger invariants under randomised sequences) and the chaos suite (a failure injected at each saga step, asserting the ledger ends balanced every time).What the tests prove →
The layout you just cloned
packages/ holds shared primitives that anything may import. services/ holds the bounded contexts, which may import from packages/ and from themselves: never from each other. That rule is the load-bearing one, and it is mechanically enforced rather than merely documented.
Poke at it
Reading a passing test suite proves less than breaking it. Run the watcher and make the system object:Break a journal by one cent
Break a journal by one cent
In
services/ledger/test/posting.test.ts, change a credit so it no longer matches its debit. The
error names the currency and the exact difference. There is no tolerance to widen: a single minor
unit is a rejection, which is the whole reason money is an integer count rather than a float.Force a sandbox failure with a magic amount
Force a sandbox failure with a magic amount
In
apps/api/test/last-mile.test.ts, send 100066 instead of 100000. The last two minor units
are read as an instruction, and …66 means the payout rail rejects. Watch the saga compensate and
the ledger still balance. Try …61 for a compliance block and …68 for a settlement that never
reaches finality.Remove the row locking
Remove the row locking
Delete the
SELECT … FOR UPDATE from withAccountLocks in
services/ledger/src/prisma-store.ts, keeping the transaction. The unit tests still pass. The
concurrency test does not: eight simultaneous spends against a balance that funds three will
overdraw the account.Try to edit history
Try to edit history
Connect with
psql and UPDATE a row in ledger_entry. The database refuses: entries are
append-only, and a correction is a new opposing journal rather than an edit. The same applies to
DELETE.Where to go next
Read the ledger
The core. Everything else is an interface onto it.
Walk a transfer end to end
€1,000 from Germany to a Kenyan mobile-money wallet, entry by entry.