TypeScript / Bun / MetaObjects
22 metadata declarations now generate the notify registry, the probe and tag type unions, the config checks, and the docs — reached with 139 tests, 4 CI drift gates, and zero critical review findings.
smon — the Smart Monitor — is Doug's own homelab system monitor, open-sourced. It began as a 377-line bash script where a one-letter typo in a config value silently did nothing — the exact failure a monitor exists to prevent. Rewritten metadata-first, its domain model (probes, verdicts, alerts, email templates, notify adapters) is declared once and the glue is generated, so an unknown adapter now throws and names the valid set, and template drift fails the build.
In the original 377-line bash smon, configuration was free-text environment tokens. Set SMON_NOTIFY="mattrix" — one transposed letter — and it silently did nothing: no Matrix alert, no error, no hint. For a monitor, a silent no-op is the worst possible failure; the single job it has is to not stay quiet when something is wrong.
Underneath, state lived in space-separated .state files parsed into positional shell globals, alert emails were built by string concatenation (the ugly plaintext email that actually triggered the rewrite), and there was no test suite and no typed contract between the policy, the notify backends, and the templates. It worked — until you needed to change it.
Every concept is now declared once as typed metadata — seven value objects, five notify adapters, seven probes with 32 tags, and the email and enrichment templates: 22 declarations in 225 lines of JSON. meta gen turns that into typed entities, a notify registry, closed probe/tag unions, email renderers, and a generated docs page; meta verify plus a gen-and-assert-no-diff CI step make any drift between the model and the code a build failure.
Edit a bash case statement and hope the env token matches — no validation, no discovery.
Declare one adapter.notify node → the registry, config-check, and docs are generated; an unknown token now throws and names the valid set.
The rewrite shipped public with CI green in 17 seconds and drew zero critical or important findings across fourteen task reviews and a whole-branch review whose reviewers ran live experiments — corrupting a template to watch meta gen fail, disabling a registry hook to prove it was load-bearing. The alert-policy core became a pure, injected-clock function checked against the original bash suite: 33 assertions ported as a behavioral oracle, plus four bash bugs fixed as regressions.
--list-adapters and --list-probes are generated from the model — the tool enumerates its own capabilities."Fix the emails to not look like crap… if it makes sense you can use metaobjects for the whole project — all prompt calling, any entities or value objects."
— the one-line brief that started the rewrite
smon is public — clone it and read the model ↗.
Since 2026-09-09 it has run MetaObjects 1.0.0, with its generated model drift-checked in CI.
meta verify --templates originally skipped email templates, so a mustache variable could drift from its payload and surface only later at meta gen. smon's CI covered the hole locally and the gap was reported upstream as metaobjects#193 — closed July 2026, before 1.0. Adopter finds gap, gap gets fixed in the standard: that loop is what this page argues for, and here it can be shown rather than claimed.Doug works directly with teams evaluating MetaObjects. Tell us what you're building and we'll tell you, honestly, whether MetaObjects fits.
Email doug@dougmealing.com