A field guide: what to run first, what to expect, and real results from a real open-source repo.
maintain is QodeX's self-improving codebase loop: every night it finds one provable
improvement, verifies it, and opens a PR you can trust — or ships nothing and tells you why
(see MAINTAIN.md for how the protocol works). This guide is about adopting it:
the 10-minute setup, the recommended rollout ladder, and what it actually did on a real codebase.
# 1. Install QodeX + the scheduler tick (macOS launchd / Linux crontab)
curl -fsSL https://qodexcli.github.io/install.sh | bash
qodex schedule install
# 2. See what maintain WOULD clean — read-only, no model, no edits
qodex dashboard # → "🔍 Run maintain now (preview)" button
# (or schedule a --dry-run first; it always blocks and just lists candidates)
# 3. Schedule the safest scope, delivered to your phone
qodex schedule add --name nightly-tidy \
--cron "0 4 * * *" \
--recipe maintain \
--prompt "unused-imports" \
--deliver telegram:<your-chat-id>Every run ends in one of exactly two messages — VERIFIED-PR: opened <url> (tests + types
actually passed) or VERIFIED-PR: blocked — <reason> (it proved nothing safe to do). Review the
PR over coffee; maintain never merges its own work.
We run maintain on this repository. These are merged PRs and real receipts, not a mockup — click through and audit them:
| Run | Scope | Outcome |
|---|---|---|
| PR #62 | unused-imports |
6 unused imports removed across the codebase, tsc + full test suite green, opened as a verified PR |
| PR #64 | unused-locals |
4 unused consts removed — and, more importantly, 6 candidates BLOCKED by the side-effect gate (their initializers contained calls/await, so deleting them could change behavior). The guardrail declined work no one was watching. |
full --dry-run |
all detection | Inventory of 37 unused symbols (21 imports, 10 locals, 6 params) with the hotspot (src/tools/, 16 of them) — used to decide which scopes to schedule |
find_similar_helpers |
near-dupe detection | Found a real 4-copy cluster: walkSource×3 + walkFiles, ~94% similar, ~81 collapsible lines — surfaced for review (read-only; merging near-dupes changes call sites) |
The #64 row is the one to internalize: blocked runs are the product working, not failing. A 3am "improvement" that changes behavior is worse than no improvement, so maintain proves safety or declines — and the receipt records which one happened.
We pointed the read-only detection (find_similar_helpers, zero setup — no install, no config)
at fresh clones of three widely-used projects (2026-07-02, shallow clones at the commits shown).
These are actual tool outputs, reproducible with the commands below:
git clone --depth 1 https://github.com/colinhacks/zod && cd zod
qodex # → find_similar_helpers path="packages/zod/src"| Repo (commit) | Scanned | Found |
|---|---|---|
zod 912f0f5 (packages/zod/src) |
116 files, 1,103 functions | 38 near-dupe clusters, ~671 collapsible lines. Headline: a 7-copy family — positive/negative/nonpositive/nonnegative across ZodNumber+ZodBigInt (~94% similar), each differing only in kind:/inclusive: |
hono b20d422 (src) |
186 files, 255 functions | 2 clusters: importPublicKey/importPrivateKey (~95%, ~26 lines) and a duplicated getQueryString in the aws-lambda handler (~92%, ~21 lines) |
axios e435384 (lib) |
66 files, 152 functions | 1 cluster: setFormDataHeaders byte-near-identical in helpers/resolveConfig.js and adapters/http.js (verified by eye: the only difference is a || {} guard) — a textbook consolidate-dupes candidate |
And because detection now includes the parameterize proposal (v2), the zod family comes back not as "these look similar" but as the concrete consolidation:
Proposed shared helper `extractedHelper(kind, inclusive)` — the bodies differ ONLY in these 2 spot(s):
positive(…) → extractedHelper("min", false)
nonpositive(…) → extractedHelper("max", true)
nonnegative(…) → extractedHelper("min", true)
(not covered — different structure: the ZodNumber variants, whose value is 0 vs BigInt(0))
We ran the v8 scope's selection live against this repo (1,686 functions → 25 near-dupe clusters) and the verdict was:
VERIFIED-PR: blocked — dry-run: 0 candidate(s)
Zero qualifying clusters — and hand-inspection confirmed every decline was correct:
dedupeStrvsdedupe(99% similar) — behaviorally different: one filters out empty strings, the other keeps them. Merging them mechanically would change behavior.- the two
isPrivateOrLocalcopies (100% similar) — one merges twoifs the other keeps separate; structurally divergent even though equivalent, so mechanical parameterization can't prove itself. - the famous 4-copy
walkSourcefamily — genuinely divergent (different result shapes and filters); it needs human-judgment consolidation, not a 3am mechanical pass. - the 5-copy path-helpers cluster — actually two families (1-arg and 2-arg) mixed; v8's full-coverage rule refuses to half-consolidate a cluster.
The dry-run also caught a real calibration bug — 100%-similar pairs were being declined just
because their comments tokenized differently — which is now fixed (comments are stripped
before alignment). That's the loop working end to end: run live → read the receipt → refine the
guardrail → the remaining blocks are all correct blocks. On a codebase with textbook
copy-paste-then-tweak-a-literal families (like zod's positive/negative above), v8 fires;
on a codebase without them, it proves that fact instead of forcing a refactor.
And one of them became a real upstream PR: the axios finding was reviewed by hand, refactored
into lib/helpers/setFormDataHeaders.js (keeping the more defensive of the two variants), verified
against axios's own suite (lint clean; all 89 form-data tests passing), and submitted —
axios/axios#11062.
Just as telling is what we didn't submit. The two hono clusters looked like duplicates but turned
out behaviorally distinct on inspection — importPublicKey/importPrivateKey differ in real
crypto branching, and the two getQueryStrings deliberately differ in URL-encoding (there's a
comment explaining why). The zod family is arguably deliberate API symmetry. Detection said
"similar"; judgment said "leave them." That division of labor — mechanical evidence, human
judgment — is the same verify-or-block philosophy the rest of maintain runs on, and it's why the
tool reports rather than auto-refactors.
Adopt one rung at a time; each rung is strictly riskier than the previous. Stay on a rung until you've reviewed a few of its PRs and trust it.
--dry-run/ dashboard preview — see the candidate list, change nothing.unused-imports— the safest edit that exists (zero-reference bindings; never side-effect imports).dead-code— one provably-unreferenced item per night (code-graph proof or it blocks).unused-locals+unused-params— the side-effect gate and the_-prefix rule do the caution for you.lint-fix— the linter's autofixable rules, bounded to a focus dir (--prompt "lint-fix src/api").dep-bump— one patch/minor bump, shipped only if your full test suite passes. Blocks if you have no tests — a dep bump without tests is unverifiable, and maintain refuses unverifiable work.consolidate-dupes— merges one exact-duplicate helper pair by repointing callers (every caller resolved via the code graph, or it blocks).
Monorepo / big repo? Add a focus path to any scope: --prompt "unused-imports packages/core/".
- Dashboard (
qodex dashboard) — the Maintain status panel: success/block rates, files cleaned, per-scope breakdown, 8-week trend, next-week forecast, and a suggested next scope with a one-click "Schedule it". - CLI report —
qodex maintain-reportfor the same numbers in your terminal. - Portable history —
qodex maintain-export -o history.json/maintain-import --mergeto archive or move the record between machines.
Every run's receipt (what ran, which checks passed, files, PR) chains into a tamper-evident audit log — altering, reordering, or dropping any past entry breaks every downstream hash:
qodex maintain-audit --sign -o audit.json # HMAC-signed via QODEX_AUDIT_KEY (env, never stored)
qodex maintain-audit-verify audit.json # offline verify — exit 1 on tamper → CI-friendlyIntegrity needs no key at all; the signature adds authenticity (proof a key-holder exported it).
Everything maintain produces can leave the terminal — pick the artifact for your audience:
| Audience | Command | You get |
|---|---|---|
| A teammate ("what is this?") | qodex maintain-demo |
interactive walkthrough page |
| A README / blog post | qodex maintain-demo --markdown |
the story as Markdown |
| A meeting | qodex maintain-demo --pdf |
one-page story PDF |
| A PR / standup thread | qodex maintain-report --markdown |
live receipt-backed numbers, paste-ready |
| A manager / monthly review | qodex maintain-report --pdf |
numbers one-pager with a real trend bar chart |
| An auditor / compliance | qodex maintain-audit --sign --pdf |
verification status + the full signed run chain |
| A pipeline / CI | qodex maintain-audit-verify <file> |
offline check, exit 1 on tamper |
| Another machine / archive | qodex maintain-export --sign |
portable, mergeable, signed history snapshot |
All PDFs are generated by a dependency-free writer (pdf-lite) — nothing extra to install.
What if my project has no tests? Scopes that need a suite (dep-bump) block outright.
Detection-based scopes (unused-imports, dead-code, …) still verify with the language
checkers (tsc, ruff, go vet, …) on every touched file.
Which languages? Detection leans on your toolchain (TypeScript/JS via tsc/eslint, Python
via ruff, Go via go vet/gofmt, …) plus QodeX's Tree-sitter code graph (TS/JS, Python, Rust,
Go, PHP). The verify-or-block protocol is language-agnostic: no checker, no proof → block.
Can it merge its own PRs? No. Opening a verified PR is the ceiling of its authority; merging is yours. (That's also why the morning-review workflow works: everything it did overnight is sitting in one place, verified, with a receipt.)
What does it cost? Detection scopes are mostly toolchain work (fast, cheap even on a local model). Each run makes one small change, so review time stays near zero.
How do I show this to my team? The story: qodex maintain-demo (interactive page) ·
--markdown (README-ready writeup) · --pdf (one-page PDF for the meeting). The numbers:
qodex maintain-report --markdown (paste the live receipt-backed report into a PR or standup
thread) · --pdf (one-pager with the 8-week trend as a real bar chart).