{"_id":"@adamspitz/verifier","_rev":"3-3c1dc726a9f3383c67b9e8d39c2c96ef","name":"@adamspitz/verifier","dist-tags":{"latest":"0.1.2"},"versions":{"0.1.0":{"name":"@adamspitz/verifier","version":"0.1.0","_id":"@adamspitz/verifier@0.1.0","maintainers":[{"name":"adamspitz","email":"adam@acspitz.xyz"}],"bin":{"verifier-run":"dist/run.js","verifier-tree":"dist/tree.js","verifier-heartbeat":"scripts/heartbeat-check.sh","verifier-scheduler":"dist/scheduler.js","verifier-summarize":"scripts/summarize.mjs"},"dist":{"shasum":"b6b9551ccda6bd2444bccffe091e5d48d10be169","tarball":"https://registry.npmjs.org/@adamspitz/verifier/-/verifier-0.1.0.tgz","fileCount":15,"integrity":"sha512-C62E3i2bvi60ke55dE1XUb/cOwaBiGVI/OCRDxvrAWcdl83AzzJIZlsh1CDP3M3o1Iy9lR38HhegEgtN9UyOLQ==","signatures":[{"sig":"MEQCIEeXjKzPSCUPyl9Fe/b3gcQ3yY/AuqiXSoG4MUXh0BSPAiAoajua8sUnDGK4aQCNoT0kX1QlOr7DpvXGNM7CApgD9Q==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":71042},"type":"module","gitHead":"4907d37a0bcc85f130966123c4ecf3ccd0ebc579","scripts":{"run":"tsx src/run.ts","tree":"tsx src/tree.ts","build":"tsc","prepare":"npm run build","schedule":"tsx src/scheduler.ts","deploy-skill":"rm -rf ~/Projects/ai-stuff/skills/coding/using-verifier && cp -r skills/using-verifier ~/Projects/ai-stuff/skills/coding/using-verifier","install:global":"npm run build && npm install -g ."},"_npmUser":{"name":"adamspitz","email":"adam@acspitz.xyz"},"_npmVersion":"11.11.0","description":"A small harness for ongoing verification of complex systems.","directories":{},"_nodeVersion":"24.14.1","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.22.4","typescript":"^6.0.3","@types/node":"^25.9.1"},"_npmOperationalInternal":{"tmp":"tmp/verifier_0.1.0_1781123948418_0.6189275159649901","host":"s3://npm-registry-packages-npm-production"}},"0.1.1":{"name":"@adamspitz/verifier","version":"0.1.1","_id":"@adamspitz/verifier@0.1.1","maintainers":[{"name":"adamspitz","email":"adam@acspitz.xyz"}],"bin":{"verifier-run":"dist/run.js","verifier-tree":"dist/tree.js","verifier-heartbeat":"scripts/heartbeat-check.sh","verifier-scheduler":"dist/scheduler.js","verifier-summarize":"scripts/summarize.mjs"},"dist":{"shasum":"951aeb8247cc92a3dadb9687cfa132c550891262","tarball":"https://registry.npmjs.org/@adamspitz/verifier/-/verifier-0.1.1.tgz","fileCount":15,"integrity":"sha512-/wvshP+ByQ4dzXpXVXQ/mjzp1fB7JFsXx+cspu7pVJpRHpvN6vDw+l8H+NoqEANxVjz3acw7yY/cq/KavfQaeQ==","signatures":[{"sig":"MEUCICMmfag5/YXBgM7nE+5rfF782wIe/gxIdy3FYCB7rsPtAiEA0VH8siBBQ/COueU2CF5XMRgihq8MA+xUMGZNjVcCoh0=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":71032},"type":"module","gitHead":"4907d37a0bcc85f130966123c4ecf3ccd0ebc579","scripts":{"run":"tsx src/run.ts","tree":"tsx src/tree.ts","build":"tsc","prepare":"npm run build","schedule":"tsx src/scheduler.ts","deploy-skill":"rm -rf ~/Projects/ai-stuff/skills/coding/using-verifier && cp -r skills/using-verifier ~/Projects/ai-stuff/skills/coding/using-verifier","install:global":"npm run build && npm install -g ."},"_npmUser":{"name":"adamspitz","email":"adam@acspitz.xyz"},"_npmVersion":"11.11.0","description":"A small harness for ongoing verification of complex systems.","directories":{},"_nodeVersion":"24.14.1","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.22.4","typescript":"^6.0.3","@types/node":"^25.9.1"},"_npmOperationalInternal":{"tmp":"tmp/verifier_0.1.1_1781124083013_0.31931139184683155","host":"s3://npm-registry-packages-npm-production"}},"0.1.2":{"name":"@adamspitz/verifier","version":"0.1.2","description":"A small harness for ongoing verification of complex systems.","type":"module","publishConfig":{"access":"public"},"bin":{"verifier-run":"dist/run.js","verifier-scheduler":"dist/scheduler.js","verifier-heartbeat":"scripts/heartbeat-check.sh","verifier-summarize":"scripts/summarize.mjs","verifier-tree":"dist/tree.js"},"scripts":{"deploy-skill":"rm -rf ~/Projects/ai-stuff/skills/coding/using-verifier && cp -r skills/using-verifier ~/Projects/ai-stuff/skills/coding/using-verifier","build":"tsc","prepare":"npm run build","install:global":"npm run build && npm install -g .","schedule":"tsx src/scheduler.ts","run":"tsx src/run.ts","tree":"tsx src/tree.ts"},"devDependencies":{"@types/node":"^25.9.1","tsx":"^4.22.4","typescript":"^6.0.3"},"gitHead":"4aa8dc94fec7addd5315c50c32b1331c2e3271b2","_id":"@adamspitz/verifier@0.1.2","_nodeVersion":"24.14.1","_npmVersion":"11.11.0","dist":{"integrity":"sha512-sx/u8FvgsfnDpPRi4zGNsxoQK8CNNuFGoIXByIalXwt4Yi+jXBe7qWXZgv5X9Tgi+9qIyUg/GumM7zUTHJOd+g==","shasum":"61f962c7f8adca4ebccf8d9b0bfdedff966b37d8","tarball":"https://registry.npmjs.org/@adamspitz/verifier/-/verifier-0.1.2.tgz","fileCount":15,"unpackedSize":87765,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIFbChBX/HSCLqQXXef4W08xzHkUAGQSrWnACGC1t3dyRAiBcAQLDmZ4vDVeoRppHaKxFzZVG18mMAM6H9QqKJGhbBA=="}]},"_npmUser":{"name":"adamspitz","email":"adam@acspitz.xyz"},"directories":{},"maintainers":[{"name":"adamspitz","email":"adam@acspitz.xyz"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/verifier_0.1.2_1782245331003_0.4630291308423484"},"_hasShrinkwrap":false}},"time":{"created":"2026-06-10T20:39:08.294Z","modified":"2026-06-23T20:08:51.236Z","0.1.0":"2026-06-10T20:39:08.570Z","0.1.1":"2026-06-10T20:41:23.163Z","0.1.2":"2026-06-23T20:08:51.122Z"},"description":"A small harness for ongoing verification of complex systems.","maintainers":[{"name":"adamspitz","email":"adam@acspitz.xyz"}],"readme":"# verifier\n\nA small harness for the ongoing verification of a complex system.\n\nYou can use it for running tests on the code, asking LLMs to make sure the docs make sense, doing monitoring on the live system... any sort of ongoing verification. It's very open-ended; write whatever kinds of checks you want.\n\n**It is a script, not a project.** The harness is ~6 tiny modules that stay small and stable; you'll edit them rarely. All the real, ever-growing work lives in `checks/`, which belong to *your* project, not to this repo.\n\n> **Writing or wiring up checks?** The full how-to is [`skills/using-verifier/SKILL.md`](skills/using-verifier/SKILL.md) — read it before you add or edit a check. This README is the map of the harness itself.\n\n## Motivation\n\nI built this little framework because I was working on a big project that was becoming too complicated for me to verify manually. If I had been the founder or CEO of a company, I would have hired human employees, so that I could assign each of them to be in charge of some portion of the project, and then I could ask them, \"Hey, how's your portion of the project going?\" and they could each give me a report. But I didn't want to hire humans, I wanted to use LLMs instead.\n\nI didn't want to create a bunch of long-lived always-running AI agents; that struck me as overkill and I thought it would introduce more complexity.\n\nThe design I settled on was the simplest thing that I thought could possibly work:\n  - you make a big list of \"checks\" (which could be conventional code or could run an LLM - whatever you choose, it's just a script that runs and produces some JSON, either periodically or in response to some event or whatever);\n  - and you can make checks that take the results of other checks as input.\n\nSo you can organize the checks into a hierarchy (well, a DAG) until at the very top you have a check that gives you a summary of the state of the entire project.\n\nThat's all.\n\nOnce I had this basic framework in place (with an [AI skill](./skills/using-verifier/SKILL.md) to teach my coding agent how to use it), I found that it was pretty easy to start offloading all the ongoing concerns that I was previously keeping track of in my head: \"make sure we have a check that makes sure the landing page is compelling\", \"make sure we have a check that the UX is smooth for all the main use cases\", \"make sure we have a check that watches the logs for errors in prod\", etc. It feels like having a company full of employees I can delegate to.\n\nAt the time of writing this, I don't have enough experience with it yet to know how well it'll work in practice in the long term.\n\n## The one idea\n\nEverything is a **check**: a command that prints one `Result` JSON object to stdout and exits. The harness never knows what a check *does* — only what it needs to run (its `inputs`), how to run it, and how to route its result.\n\n```json\n{ \"status\": \"pass\" | \"fail\" | \"uncertain\" | \"error\", \"summary\": \"...\", \"findings\": ... }\n```\n\n- **pass** — healthy. Ignored.\n- **fail** — the *system* is wrong. Pages you.\n- **uncertain** — ran fine, but judgment is ambiguous; wants a smarter look. Queued.\n- **error** — the *check* couldn't run. A blind spot. Queued; escalates if it persists.\n\nExit non-zero, time out, or print garbage and the harness records an `error` for you — **a check cannot hide by crashing.**\n\nTwo consequences make this more than a cron runner:\n\n- **There is no separate \"supervisor\" type.** A supervisor is just a check whose inputs include *other checks' latest results*. The whole hierarchy — which manager summarizes which workers — *emerges* from those inputs. It's a DAG, so one worker can feed several supervisors. A graph of stateless evaluations, not an agent society.\n- **A run picks its own next run.** Alongside its status a check may emit `nextRun: { inMinutes: 60 }`, making adaptive backoff (`inMinutes: healthy ? 240 : 5`) trivial and letting an LLM check decide when to look again. The `trigger` in the definition is only the seed and the fallback.\n- **A pure check need not re-run when nothing changed.** Set `\"memoize\": true` in a definition and the harness fingerprints the check's resolved inputs *plus its own builder files* (the existing files named in `command`) before each run; if the fingerprint matches the last run's, it **skips the command entirely** and reuses the prior `Result` (flagged `memoized: true`). This is Nix-style input-addressed caching: re-running a check whose world hasn't changed costs nothing — no subprocess, no LLM tokens — which is what makes an expensive LLM-synthesis check safe to put behind a \"give me the report\" command you hit repeatedly. Only sound for checks that are pure functions of their *declared* inputs; a leaf that reads the live system directly (not via a declared input) must not set it. `error` results are never memoized — a blind spot is always retried — so a memoized check self-heals after a failure.\n\nThe skill covers how to actually write all of this; the rest of this README is what's behind the curtain.\n\n## The harness (`src/` — small and stable)\n\n    types.ts        the Result/Input/Definition contract — read this first\n    config.ts       resolves the per-project verification workspace\n    inputs.ts       resolves a check's declared inputs; check-side readInputs()\n    loader.ts       reads checks/*.def.json, validates the input graph (no cycles, all ids exist)\n    runner.ts       resolves inputs, spawns one check, enforces timeout, manufactures `error`\n    store.ts        directory-as-index JSON storage + run state\n    router.ts       pass=ignore  fail=alert  uncertain=queue  error=queue→alert-if-stale\n    scheduler.ts    the only long-running process: due? → run → store → route\n    run.ts          one-shot CLI: `verifier-run --workspace <dir> <checkId>`\n    heartbeat-check.sh   dead-man's-switch — runs from REAL OS cron, outside all of this\n\n## The verification workspace (per project)\n\nThe harness is generic; each project that uses it has a **verification workspace** — the directory where that project's checks, history, and mutable state live. It is not this repo, and not necessarily the project root.\n\n    checks/      YOUR checks (grows forever): *.mjs logic + *.def.json definitions\n                 supervisor.mjs    one reusable generic supervisor (folds inputs by a rule)\n                 prune.mjs         retention enforcement — itself a check\n                 meta/liveness.mjs the silent/overdue watchdog — itself a check\n    results/     results/<checkId>/<runId>.json    (rolling, pruned)\n    artifacts/   artifacts/<checkId>/<runId>/...    (bulky per-run evidence)\n    state/       state/<checkId>.json + heartbeat   (mutable)\n\nChecks are **independent programs that import nothing from the harness** — they read env vars and print one Result. So a check can be written in any language; the `.mjs` examples just run with plain `node` (no build step). See the skill for the authoring details.\n\n## Running\n\nFrom this repo during development:\n\n    npm run build\n    node dist/scheduler.js --workspace /path/to/workspace\n    node dist/run.js --workspace /path/to/workspace root      # runs the root supervisor; this IS your dashboard\n\nInstall the harness on your PATH:\n\n    npm install -g @adamspitz/verifier\n\nOr, from this checkout while developing the harness itself:\n\n    npm run build\n    npm run install:global\n\nThen:\n\n    verifier-scheduler --workspace /path/to/workspace\n    verifier-run       --workspace /path/to/workspace <checkId>\n    verifier-tree      --workspace /path/to/workspace [rootCheckId] # interactive tree viewer\n    verifier-summarize --workspace /path/to/workspace          # markdown summary of the check definitions\n    verifier-heartbeat /path/to/workspace                      # put this in real OS cron\n\nThe workspace can also come from `VERIFIER_WORKSPACE`; individual dirs override with `VERIFIER_CHECKS`, `VERIFIER_RESULTS`, `VERIFIER_ARTIFACTS`, `VERIFIER_STATE`, `VERIFIER_HEARTBEAT`.\n\n`verifier-tree` is the interactive dashboard. It has two modes:\n\n- **commands** (the default landing screen when the workspace defines a `commands.json`) — a project-defined menu of runnable commands, each shown with a description as you highlight it, plus a synthetic `Open check dashboard` entry. Press Enter to run the highlighted command (in your real terminal, so you see its output and can Ctrl-C), `t` to jump straight to the tree, `j`/`k` move, `l` reloads, `q` quits.\n- **tree** — the check DAG browsable by status, with each selected check's summary/findings/artifacts in a `details` pane on the right. Keys: `j`/`k` (or arrows) move the selection, Enter/Space expand/collapse a supervisor, `r` re-runs the selected check in place, `l` reloads stored results, `v` opens the latest result JSON in `$PAGER`, `o` opens the current artifact, `1`–`9` pick an artifact, `d` toggles the details pane between **report** (the artifact's rendered text) and **info** (run meta + the full findings JSON), `Tab` switches focus between the tree and the details pane (when the details pane is focused, `j`/`k` and PgUp/PgDn scroll it), `c` returns to the commands menu, `q` quits. Pass an optional check id to open only that subtree and skip the commands landing, e.g. `verifier-tree --workspace verifier facet.docs`.\n\n### Workspace `commands.json`\n\nA workspace may define `commands.json` at its root to populate the commands menu — handy for the project-specific runner scripts (`npm run verifier:go`, deep-cadence cron, wipe/reseed, etc.) that are not part of the harness itself:\n\n```json\n{\n  \"commands\": [\n    { \"name\": \"Go — top-level report\", \"description\": \"…what it does…\", \"command\": [\"npm\", \"run\", \"verifier:go\"] },\n    { \"name\": \"Deep cadence\", \"description\": \"…~20 min, runs the guarded local stack checks…\", \"command\": [\"npm\", \"run\", \"verifier:deep-cadence\"] }\n  ]\n}\n```\n\n`command` is argv (no shell, preferred) or a single string run through the shell; `cwd` is relative to the verifier workspace (default `.`). Entries with no name or command are skipped. With no `commands.json`, or when a root check id is passed on the command line, the tool opens straight in tree mode.\n\n### Showing a check's report by default (`display.preferredArtifact`)\n\nA check that writes a report artifact can ask `verifier-tree` to show *that* by default in the details pane rather than the findings dump, by naming it in the definition. The details pane's report view renders the artifact's text content (scrollable); `d` still flips to the findings JSON when you want it.\n\n```json\n{ \"id\": \"root\", \"…\": \"…\", \"display\": { \"preferredArtifact\": \"report.md\" } }\n```\n\n`preferredArtifact` matches an artifact's `name`, basename(`path`), or full `path`. If omitted, the tree heuristically prefers an artifact named `report*`, then any text/markdown-ish artifact, then the first artifact.\n\n## Design philosophy — three things to deliberately NOT build\n\n- **No database.** Readable JSON on disk; recent history doesn't need queryability.\n- **No web dashboard.** `run.ts root` IS the dashboard — a one-shot rollup report.\n- **No plugin/agent framework.** The subprocess-emits-JSON contract is the whole API.\n\nIf a fresh reader can't understand the harness top-to-bottom in an afternoon, it's drifting toward a project — stop and cut.\n","readmeFilename":"README.md"}