Master-Debt Auto-Fixer
A long-lived shared branch accumulates debt: a test, lint rule or build that has been failing for a while and belongs to no single branch. This watches for failures that already exist on the shared branch, waits until the same one has survived three separate runs, and then gets it fixed — per file, and only when it sticks around.
What it is
In development, hidden by default. Reveal it with the Settings → Lab → "Master-Debt Auto-Fixer" toggle (or launch with
AMC_SHOW_MASTER_DEBT_AUTO_FIXER=1). Off by default because the auto mode can spawn paid fix sessions on your behalf — see the two modes below.
A long-lived master branch accumulates debt: a test, lint rule,
type-check, or build that has been red for a while and isn't any one branch's
fault. When a session finishes a ready-to-merge run and discovers that some of
its failing checks already exist on master, the Master-Debt Auto-Fixer can
get that debt fixed instead of leaving it to rot.
It works per file. For each file that has pre-existing master debt, Omniscio holds a single claim and does ONE of two things depending on your mode.
It only spends money on debt that sticks around. Before spawning any paid fixer, Omniscio waits until the SAME failure shows up on three separate ready-to-merge runs — its durability gate. Debt that master fixes on its own before then, a flaky test that fails differently each run, or a one-off blip never clears that bar, so none of them ever cost a fixer. Until a file is confirmed durable it just sits as a cost-free "watching" entry on the tracker.
Where to find it
Settings → Lab → "Master-Debt Auto-Fixer". It is hidden by default because its automatic mode can start paid fix sessions on your behalf, so revealing it is a deliberate act — and so is leaving it in that mode.
How it behaves
The two modes
- Ask first (the default). Omniscio drops ONE dismissible inbox card per
failing file — "Fix master-branch debt in
<file>(N failing checks)". Approve it and Omniscio spawns ONE background fixer session for that file. Reject or dismiss it and nothing is spawned (the file becomes offerable again later). - Auto (opt-in). Turn on the companion "Auto-fix master debt" Lab toggle and Omniscio skips the card — it spawns a fixer session per failing file directly. This is the mode that costs money without asking each time, which is why it is off by default and the whole feature is Lab-gated.
A fixer session is a normal background Claude session started in the
reporting session's own project. Its brief tells it the file and its failing
checks, frames the failure as pre-existing master debt (NOT the branch's fault),
forbids weakening the check (no .skip / xfail / deleting / relaxing the
test/lint/type/build check to go green), and asks it to root-cause the real
problem on its own worktree and take it to ready-to-merge.
When it's done — whether it fixed the debt or found there was nothing left to fix (it won't fabricate a change) — the fixer cleans up after itself: once its branch is at ready-to-merge it self-archives, but ONLY if nothing still needs you (it isn't waiting on a question or approval and hasn't left a heads-up open). Its worktree and branch are then reaped automatically once the branch lands or is retired. If anything still needs your attention, it leaves the session visible instead of hiding it.
Safety — it cannot run away
Every part is bounded so a bad failure set can't spawn an army of sessions or loop forever:
- It only acts on durable debt. A file must be reported as the SAME failure by three separate ready-to-merge runs before any fixer or card — transient, flaky, and one-off failures age out for free without ever spending. This is the biggest money-safety bound.
- One fixer per file. A file already being handled (an active claim or a pending approval card) is never claimed twice. Two things triaging the same file at once still produce exactly one claim and one fixer.
- At most 3 fixers in flight. Extra eligible files wait their turn — they're listed on the tracker (below), not acted on, until a slot frees.
- Which engine a fixer runs on is a SETTING, and it defaults to DeepSeek V4.1 Flash. Fixer work is mechanical and there can be many at once, so the cheapest capable engine is the shipped default. Change it at Settings → Lab → "Master-debt fixer engine" (it appears alongside the feature's own toggle). The choice is reachability-gated: if the engine you pick has no key on this machine and Omniscio's shared gateway cannot serve it, the fixer quietly falls back to the reporting project's own engine — the Worker tier (Sonnet) on a Claude project — so a missing key can never strand a fix. The engine is no longer inherited from the project, which is what previously put the same job on two different models in a single night.
- At most 3 attempts, then it asks a human. If spawning a fixer keeps failing, Omniscio retries a bounded number of times and then marks the file "gave up" rather than retrying forever.
- Crash orphans are released. If Omniscio crashes between claiming a file and actually starting the fixer, the stale claim is removed on the next pass so the file is eligible again (it is NOT permanently marked as given-up).
- Stuck fixers are cleaned up. A fixer that dies without fixing the debt, or an active claim older than 24h with the file still failing, is marked stalled; finished/given-up claims are pruned after 7 days.
A claim resolves either way the debt genuinely goes away: when a later ready-to-merge run no longer lists the file (whether a fixer fixed it or master churn did, including clearing the LAST failure of a kind), OR the moment the fixer itself reaches ready-to-merge on its branch. The fixer now runs on an Omniscio-tracked worktree branch (it is spawned with forced isolation), so Omniscio reads that branch's ready-to-merge tag and credits the fixer as resolved — instead of the old behavior, where a finished fixer that hadn't landed yet was mislabeled "stalled" until a later run happened to omit the file.
The ledger and the tracker
Two files live in ~/.amc:
master-debt-claims.json— the machine-readable ledger of every claim (one per file) and its status. This is the dedup + safety record; Omniscio serializes all writes to it so two callers can't clobber each other, and a missing or corrupt file is simply treated as empty.master-debt-tracker.md— a human-readable dashboard rebuilt on every pass: a table of every open claim (file · failing checks · status · owner · since), the files queued behind the 3-in-flight cap, and what was resolved in the last 7 days.
Limitations (v1)
- Ready-to-merge detection reads the fixer's Omniscio-tracked worktree branch (fixers spawn with forced isolation). If a fixer ignores its brief and nests its OWN worktree, that branch's tag isn't the tracked one, so resolution falls back to report-omission (a later ready-to-merge run no longer listing the file).
- The triage caller is the Omniscio dev pipeline on the Omniscio repo; there is no general-purpose external intake.
For agents
How a session reports debt — the route
Omniscio's local control server exposes POST /master-debt/triage. The dev pipeline
(after a ready-to-merge run) submits the batch of failing checks it found to be
pre-existing on master: { v: 1, failures: [{ kind, file, ... }] }. The route:
- requires the bearer token (401 otherwise) and shares the 10/min mutation rate limit (429),
- returns 403 unless the Lab feature is enabled (the flag is your consent that Omniscio may spawn paid fixers),
- validates the body strictly (≤ 200 failures; bad body → 400),
- uses the calling session's provenance (
X-AMC-Source-Session-Id) to find a spawnable source project — and is a benign no-op 200 (not an error) when the source session or its project can't host a spawn.
It applies immediately (no approval round-trip for the triage itself); the paid spawn only happens under auto mode or an explicit card approval. Scope (v1): "only the Omniscio repo" is enforced by the caller (the ready-to-merge ping fires only on the Omniscio repo), not by the route — the route is Omniscio-internal intake gated by the feature flag plus the source project's spawnability.
How it works
- The pure decision brain — master-debt-service.ts —
has every side effect injected (
spawn,raiseCard, session-liveness, settings,now,genId), so it imports no Electron/DB and is fully unit-tested. The production singleton (master-debt-singleton.ts) wires it to the real ledger, the real fixer spawn, the real ask-mode inbox card, andgetSettings. - Two deterministic identity functions underpin the dedup: a failure key
(
kind::file::discriminator) makes the same failing check map to one row across runs, and a file fingerprint (sha1 over the file's sorted-unique failure keys) makes "the same debt" block re-triage while a CHANGED failure set becomes eligible again. - Durability gate.
triagerecords anobservedsighting per reported debt and only spawns/cards a file once the SAME fingerprint reachesCONFIRM_COUNT(3) distinct reports.observedrows are non-occupying (no slot, no money) and age out if the debt stops recurring; a CHANGED fingerprint restarts confirmation. - Resolution is wired through the triage route: after each triage the route calls
reconcile({ latestReport }), and a claim resolves when its file is absent from that report. The periodic 5-min reconcile has NO report (so it only stales/prunes) — feeding the report at triage time is what makes resolution actually fire (it never did before, which is why claims used to pile up unresolved). - The
master-debt.spawninbox action is always approval-required (it's in the non-toggleable set) — auto mode is a separate decision made at triage time, not a per-action approval toggle. - A 5-minute background reconcile loop resolves / stales / abandons / prunes claims and releases orphans; it self-gates on the Lab flag each tick.
The full design and the test-locked invariants (one-fixer-per-file, claim-first, the concurrency cap, the bounded give-up, orphan release, the route gate order) live in the contract: master-debt-auto-fixer-contract.md.
Related
- Omniscio’s unreleased-feature (“Lab”) gate — how the Lab
toggle /
AMC_SHOW_*gate hides this until it ships - session-provenance.md — the
X-AMC-Source-Session-Idorigin the route uses to find the spawnable project
Last verified 2026-09-23