ADR 0010: Self-host the harness — install discern into its own repo
Retired — superseded by ADR 0019. Self-host inverted; the committed shell engine and
selfcheck/selfsyncare gone. Kept for history; not current architecture.
Status: accepted; superseded by ADR 0019 — see Update (single-binary cutover) below.
Update (single-binary cutover)
§The single-binary cutover inverts self-host. There is no committed shell harness to install into the repo — the engine is TypeScript compiled into the discern binary — so the repo self-hosts by running its own engine (deno task dev finish), with no second copy to keep in sync. The drift-gate this ADR established (selfcheck/selfsync) is removed: the regression class it guarded is made structurally impossible, because there is nothing that can drift.
Context
§discern is an agentic-development harness in two halves: a Deno/TypeScript installer (src/) and the POSIX-shell harness it installs (templates/, the source of truth). Until now the repo was not self-installed — there was no root discern.toml, bin/agent, or .discern/. The README's self-hosting section was therefore aspirational: the repo's real gate was the local convention deno fmt && deno lint && deno check src/main.ts && deno task test, and CI only built release binaries.
That left the product's two most distinctive pieces unexercised by real use: the agent finish gate as a daily driver, and the author-once→compile guidelines pipeline (the repo had no generated CLAUDE.md/AGENTS.md at all). The test suite drives the engine hermetically (tests/engine_* scaffold the real templates/ and shell out to bin/agent), but a test harness is not the same as living with the tool. Installing the harness into its own repo is the truest validation we can run, and the repo is pre-1.0 and pre-adoption, so there is no backward-compatibility cost to doing it now.
Decision
§Install the harness into the repo and make ./bin/agent finish the repo's gate.
- Committed managed copy ("Option C").
discern setupruns at the root; the managed set (bin/agent,.discern/engine/**,.ai/skills/**) is committed as a copy oftemplates/, alongside the seed filessetupwrites (discern.toml,docs/**,.ai/guidelines/discern.md,TODO.md). - Drift is a gate-enforced invariant. A new read-only
discern upgrade --check(exposed asdeno task selfcheck) exits non-zero if any managed file differs fromtemplates/. It is wired as[slots.selfcheck]in thecheckphase, soagent finishfails on drift. Healing is one command —deno task selfsync(≡discern upgrade) — which propagatestemplates/→ the install. - The golden rule. To change a managed file you edit
templates/and rundeno task selfsync; never edit the root copy (a direct edit is reported as drift and written back as<file>.new). Seed files are edited directly at the root and are never distributed to other projects. - Wired slots.
deno fmt(fix);deno lint,deno check src/main.ts, andselfcheck(check);deno task test(test). Build stays a no-op (deno task buildis release-only). - Guidance via the pipeline. Agent guidance is authored in
.ai/guidelines/discern.md(a seed) and compiled byagent guidelinesintoAGENTS.md(tracked) andCLAUDE.md(generated, gitignored). - CI runs the repo's own gate (
agent finish) on push and PR, with a trailinggit diff --exit-codeso the auto-fixingfixphase becomes a hard check. - fmt/lint exclude the managed artifacts (
.discern/,.ai/,AGENTS.md,CLAUDE.md, plus.idea/) so the formatter never rewrites a managed file into drift or fights the guidelines compiler.
Consequences
§- The repo now has two copies of the engine:
templates/.discern/engine/**(the source the test suite runs) and.discern/engine/**(the installed copy that gates this repo). Theselfcheckgate plus the golden rule keep them identical; the cost is the discipline of editingtemplates/and syncing. - The gate runs on itself:
agent finishrunsdeno task test, whoseengine_*tests shell out toagent finish. This nesting immediately surfaced a real bug —finishexported thefail_fast/streamenv vars only when on and never cleared them, so a nested gate inherited the parent's value and ignored its own[gate]config. Fixed (set both ways) with a regression test. Exactly the kind of defect only living with the tool reveals. - The test suite remains the correctness arbiter for the engine: a
templates/change is validated bydeno task testwhether or not the install is synced yet. If a bad engine change ever breaksagent finishitself, fall back todeno task testorgit checkout .discern/engine. discern upgrade --checkis a genuinely useful new feature for any user (CI can now assert harness sync), not just an internal device.- The isolated-worktree workflow is now installed and exercisable for real, but adopting it for this repo's own development is deferred (see
TODO.md); the SessionStart/WorktreeCreate hooks are scaffolded and dormant. - Backward compatibility is not a concern (pre-1.0, pre-adoption), so this was done without migration shims.
Alternatives considered
§- Symlink the install to
templates/. Simplest to keep in sync, but it bypasses the real copy/upgrademachinery — the part most worth validating in real use — and no installed project works that way. Rejected. - Generate the install on demand, don't commit it. Avoids the second engine copy, but adds a cold-start build step before the gate can run and makes the install invisible in the tree and in review. Rejected in favour of a committed copy kept honest by
selfcheck. - Auto-sync via a git hook. A pre-commit hook that runs
selfsyncwould heal drift silently — and could silently commit a broken engine change. Making drift a loud gate failure (the kind of invariant the harness exists to provide) is the point. Rejected.