Platform

What changed, and what ran.

Posture is not a thing you have; it is a thing you had when you last looked. Scans compare the machine against the last time anyone checked. The log does the other half: every tool call, in order, with the decision and the caller on the line.

Fingerprints, not timestamps

SHA-256 prefixes of settings.json, settings.local.json, the guard hook and CLAUDE.md, plus the size and mtime of the original store and the allow, deny and ask counts.

Four kinds of event

A rule that was holding and stopped. A config file whose fingerprint moved. A file that disappeared. Movement in the originals.

Attribution, where it exists

For movement in the originals the scan asks lsof whether anything holds the file open, so growth while the recorder runs reads differently from growth with nothing attached.

Why a diff and not a monitor

A daemon watching every file would need more access than the thing it protects and would be one more process to trust. Scans run on your cadence and compare against the last one, which is enough to catch the failure this is aimed at: a control that was in place on Monday and is not in place on Thursday, with nobody able to say which day it went.

The last sixty scans are kept, as plain JSON under ~/Library/Application Support/Mockingbyrd/. Nothing is uploaded, and there is no account to hold the history.

Replay is the log, read back

Every tool call is appended to the audit log — allowed and blocked alike — with the enforcement mode recorded on the line. That detail is the point: a window where enforcement was paused is visible afterwards, with the calls that went through during it, rather than being a gap in the record that looks like quiet.

{ "decision": "no-app", "layer": "t0", "reason": "T0 original. Use ~/agent/derived/ instead.",
        "command": "sqlite3 ~/Library/Messages/chat.db .tables",
        "tool": "Bash", "session": "abc12345", "cwd": "~/Code/Mockingbyrd",
        "chain": "zsh < claude < disclaimer < Claude" }

chain is the process tree above the hook — which program is actually running, rather than which one the payload claims. A block you cannot attribute is one you will eventually switch off.

Every line carries its session id, so one session's calls can be pulled out and read in order: what the agent reached for, what the guard decided, which interpreter was underneath. That is what replay means here — the record is complete enough to reconstruct the sequence afterwards, from a file on your own disk, with no service in the middle.

It is a log, not a recording. There is no scrubber and no screen to watch it back on. You read the lines, or you ask the app which caller last hit a given block. What the format buys is that the reconstruction is possible at all, and that it depends on nothing having been running at the time except the hook.

What an event is worth

Three of the four kinds are cheap to produce and occasionally decisive. The fourth — movement in the originals — is the one that needs care, because a screen recorder writing to its own store all day is not an incident. That is why the scan asks what holds the file open before it says anything about the growth.