.app bundle by scripts/build-app.sh. It has no dependencies, no .xcodeproj and makes no network calls.
How a scan works
AppModel.scan goes through the rule list and evaluates each rule off the main actor, so the progress bar stays accurate. Then it measures the posture numbers once and adds a Scan record.
A shell rule’s check is appended to DefaultRules.prelude and run with /bin/bash -c, with a 25-second timeout. The prelude provides helper functions, which keeps each probe short enough to read and edit in the app.
Probe helpers
Exit codes
The probe’s stdout and stderr become the evidence shown in the app.
Detecting blank cheques
mb_subsuming exists because matching the exact strings the doctrine names is not enough. Bash(sudo sqlite3:*), Bash(timeout 30 python3:*) and Bash(/usr/bin/python3 -c ' *) are all blank cheques, and none of them match ^Bash\(sqlite3:\*\)$.
The helper splits each entry into tokens and finds the first token whose basename is an interpreter. Then it checks what follows. A bare *, a trailing :*, -c, -e or stdin all count as blank cheques. A spelled-out command does not, because N5 explicitly tells you to keep a few of those.
Fixes for these rules use the same detector instead of a regex of their own. An earlier version used a separate pattern, and its sh alternative matched the sh in ./build.sh. That removed a grant that was never a blank cheque. Matching the whole basename keeps build.sh, du -sh and node --version out. Sharing one detector also means a probe can’t report a finding its own fix can’t fix.
Scoring
A rule’s severity is its weight: critical 10, high 6, medium 3, low 1. For each group of rules,possible is the total weight of every enabled rule. earned is the sum of credit × weight, where credit is 1.0 for holding, 0.5 for partial and 0 otherwise. Disabled rules are left out of the total, so turning a rule off lowers the maximum score instead of inflating your score.
The same function scores the whole pack, a single invariant or a single phase.
Drift
Each scan records:- SHA-256 prefixes of
settings.json,settings.local.json, the guard hook andCLAUDE.md - The size and modification time of the T0 SQLite file triplet (the database plus its
-waland-shmfiles) - The number of allow, deny and ask entries
- A rule that was holding stopped holding.
- A config file’s fingerprint changed.
- A file disappeared.
- The T0 store changed.
p2.t0-movement-attributable rule goes further. It uses lsof to check whether any process has the original open. That separates growth while the recorder is running from growth with no process attached.
Persistence
Mockingbird stores plain JSON under~/Library/Application Support/Mockingbird/:
Enforcement state is stored separately at
~/.config/mockingbird/enforcement.json, because the guard reads it on every call.
Build
scripts/make-icon.swift finds the accent-colored glyph in the brand logo and removes its white background using the blue channel. A plain crop would include the “D” from the wordmark. The script then renders the iconset and a transparent mark for the sidebar.