Decision log
Sixteen decisions this project made under pressure, and what each one cost to learn.
Sixteen decisions, dated, written at the moment each one was made rather than reconstructed afterward: what was decided, and why, without the blow-by-blow.
Target selection
Primary target moved from Tailscale's changelog to PostgreSQL's security advisories. The original
pick failed twice: the page was 1.63 MB, and the intent analyser cannot ingest documents anywhere near
that size. The replacement is 67 KB — roughly 25× smaller — and carries 327 CVE advisories across 70
rows from one page load. This single failure is the reason Onboarding a target
preflights size before ever calling create.
The chaos site changes by redeploy, not by a query parameter. A layout switch driven by
?v=2 would let anyone flip the site back to healthy mid-demo by editing a URL — the deploy itself has
to be the only way the site's shape changes, or a "self-healing" demonstration is not actually proving
anything.
The chaos site must have no outbound navigation, and deploy scripts must verify the alias actually moved. Two guardrails earned by two separate near-misses: an internal link the collector could wander off into, and a deploy that reported success while an old alias silently kept serving stale content.
What the detection core got right — and what it got wrong once
A field zeroed out is broken, not degraded. The distorted classification (see
Concepts) exists because a field returning 0 instead of its real value fills
on every row — indistinguishable from healthy by presence alone. Not obvious in advance; costly to
miss.
Recovery must be the negation of the fault, at the same threshold that opened the incident — not
"some data came back." An approved heal that returns non-null values but does not actually clear the
original fault is not a resolution, and molt approve's verify step is built to say so.
The web UI's Fleet page showed a false-green fill rate for a zeroed field. The exact bug the detection core exists to catch, reproduced by the dashboard meant to display it — because an early version coloured cells by raw fill rate rather than by classification. Covered in more detail on Honest limits.
Failures that were fixed, not hidden
resolveCliEntry broke under webpack; the web UI's Approve button did nothing. A resolution path
that worked from a plain Node process silently failed once bundled — the button did not error, it just
had no effect, which is a worse failure mode than a crash. Caught by the same verification step
described next.
Verification caught a bug in Molt's own approve call, and self-healing was verified end to end
on real infrastructure rather than only against fixtures — both entries exist because a mocked
integration test would have passed while the real command silently failed.
One refactor job per collector — a 409 that must not be retried. Scraper Studio refuses a second
heal while one is already pending; retrying blindly just repeats the refusal. This is why
molt unblock exists as an explicit human decision rather than an
automatic retry.
Infrastructure
SQLite over Postgres, and per-entry id anchors are also a discovery surface — a smaller
decision about the chaos target's own markup, made because Molt's own link-graph preflight (see
Onboarding a target) treats bare anchor ids as addressable pages, so the
demo target's own HTML had to be written with that in mind.