moda fix CLI family calls.
Base URL and authentication
Unlike the rest of the Data API, the fix routes are tenant-scoped and live under the control-plane base:x-api-key header as the Data API (a dashboard session also works). Reads require workspace membership; every mutation (POST) requires the admin role. Responses use camelCase field names — except the fix packet, which is snake_case by design (it is an agent-facing document).
Endpoints
The Fix object
Every fix endpoint (except the packet) returns or embeds this shape:Candidate refs
candidateRef carries the proposed candidate, discriminated by kind:
Statuses
DRAFT, SCOPING, NO_EVIDENCE, BLOCKED_COVERAGE, UNROUTED, PROPOSING, NO_HEADROOM, PROPOSED, GATING, GATE_FAILED, GATE_INCONCLUSIVE, VERIFIED, PR_OPEN, APPLIED_LOCALLY, HANDED_OFF, SHIPPED, CONFIRMING, HELD, REGRESSED, REJECTED, SUPERSEDED, ABANDONED. The dashboard lifecycle table explains each stage.
Gate result
gateResult is set once a gate finishes:
pass requires wins − losses ≥ max(2, noiseFloor + 1), losses ≤ noiseFloor, and abstained ≤ 30% of the holdout; more than 30% abstained is inconclusive (never a pass); anything else is fail. verdict values are exactly pass / fail / inconclusive.
While the gate is enqueued (GATING), gateResult may instead hold a pending marker — { "pending": true, "baselineFidelity": { ... } } — recorded at enqueue time, before any verdict exists. Treat it as “no verdict yet”; the display surfaces render no gate view for it. The pending marker may also carry enqueue-time counterfactual context (for example the anchors selected) with no counterfactual.verdict — the surfaces render no counterfactual copy for that either.
Counterfactual gate (gateResult.counterfactual)
gateResult.counterfactual is the counterfactual-anchored gate’s verdict: the fix replayed against recorded production failures from holdout traces, two arms per anchor — a control arm without the fix and a candidate arm with it. The block is optional: comparison-only verdicts omit it entirely, and every comparison field above is unchanged whether or not it is present.
The verdict math:
pass iff wins − losses ≥ max(2, ceil(0.2 × informative)) and informative ≥ 3 and abstained ≤ 30% of anchors; inconclusive when informative < 3 or abstained exceeds 30% (no informative anchors is inconclusive, never a pass); anything else is fail.
A counterfactual pass can upgrade a comparison GATE_FAILED/GATE_INCONCLUSIVE fix to VERIFIED — but only when the comparison gate did not fail with losses beyond its noise floor. Overall: pass if either gate passes and neither gate has losses above noise; otherwise the stricter of the two verdicts stands. The top-level reason always names which gate produced the verdict. Display surfaces render the block as prevented {wins} of {informative} replayed production failures (control still failed in {informative}; regressions {losses}) — alongside, never instead of, the comparison verdict. In the fix packet the block rides under the snake_case key counterfactual.
Verdict fidelity
baselineFidelity labels what the baseline arm was made of, so a verdict never claims more than the gate actually compared. It is optional: verdicts produced before fidelity labeling omit it.
baselineFidelity.prompt — the prompt pinned in both arms:
baselineFidelity.skills — where the baseline arm’s skill content came from:
Fidelity is always shown next to Verified. Every surface that renders the verdict — the dashboard badges, the CLI summaries, and the packet — qualifies it: Verified — tenant prompt baseline or Verified — standard-agent baseline, with · skill recovered from production receipts appended when
skills is observed. A generic-prompt or recovered-skill verdict never renders as plain “Verified”.
POST /tenants/:tenantId/problems/:problemId/fixes
Drafts a fix. Creation is idempotent while a fix is active: drafting again for the same problem returns the existing active fix instead of forking the pipeline. The new fix rests atDRAFT — drive it with advance.
string
default:"auto"
auto routes from evidence; prompt considers only the prompt route.400; an unknown problem returns 404. A merged-away problem ID is folded to its survivor, like the problem endpoints.
POST /tenants/:tenantId/fixes/draft-batch
Campaign mode: batch-drafts fixes for the tenant’s top-ranked fixable problems in one call — what the dashboard’s Draft fixes for top problems button andmoda fixes draft-batch call. Problems are scanned in rank order; each is drafted through the same guarded creation path as the single-problem route above, or skipped with a reason when it already has an active fix, its latest lifecycle is not open/reopened, or it aliases a problem already handled in the batch. Creation only — nothing advances server-side; drive the drafted fixes with advance, or in bulk with moda fixes drive.
integer
default:"10"
How many fixes to draft, 1–25 (
400 beyond the cap). The 25 cap is an LLM spend guard, not a paging nicety: every drafted fix burns distillation + propose budget once driven, so batches are deliberately small — run another after the first lands.Output (drafted entries trimmed)
drafted entries are full Fix objects decorated with the rank the batch ranked on. Per-problem failures never abort the batch — they land in skipped with the error message as the reason.
GET /tenants/:tenantId/fixes
The ranked queue: fixes ordered by their problem’s rank (descending), then recency. Also available per problem atGET /tenants/:tenantId/problems/:problemId/fixes (returns { "fixes": [...] } without pagination).
string
Filter to one status. An unknown status returns
400.integer
default:"25"
1–100.
string
Opaque cursor from a previous page’s
pagination.next_cursor.Reading a fix reconciles its confirmation state lazily:
SHIPPED/CONFIRMING fixes are checked against the problem’s lifecycle on every read, so a reopen or 14 clean monitoring days surface as REGRESSED/HELD without a background job.GET /tenants/:tenantId/fixes/:fixId/packet
The compact agent packet —moda.fix_packet.v1. Every fix has one, including diagnosis-only fixes without a candidate. Quoted evidence in the packet is neutralized as untrusted data before it reaches an agent.
target keys, candidate, and evidence entries are omitted when unknown (for example an UNROUTED fix has only fix_type in target and no candidate).
Typed candidates ride the packet under their own keys (injection-screened at propose time and scrubbed again on the way out):
candidate_tool(TOOL_SCHEMAfixes) —{ tool_name, current_description, current_description_source, proposed_description, param_notes, rationale }: the tool-description diff to apply in your own tool definition.candidate_skill(SKILLfixes) —{ skill_name, skill_md }: the complete drafted SKILL.md to install in your agent.candidate_skill_edit(SKILLfixes amending an existing synced skill) —{ skill_key, skill_name, current_skill_md, proposed_skill_md, change_summary }: the current-vs-proposed SKILL.md edit to apply to the skill already in your repo.
mark-applied — no GitHub integration required.
POST /tenants/:tenantId/fixes/:fixId/advance
Performs exactly one idempotent pipeline step and returns the updated fix. The backend runs no background jobs — clients drive the pipeline by callingadvance in a loop (this is what moda fix … --wait and the dashboard polling loop do). Steps are claimed optimistically, so concurrent advances never double-run a stage; the loser returns the current row untouched.
POST /tenants/:tenantId/fixes/:fixId/verify
Re-gates the fix: enqueues fresh gate and control runs and returns the pinned gate run ID. WithcandidateContent this gates uploaded local content instead of the stored candidate — the fail-to-pass loop behind moda fix verify --prompt-file. Then drive advance until the fix leaves GATING.
For TOOL_SCHEMA and SKILL fixes, both gate arms pin the same prompt so the candidate artifact is the only delta between arms: the tenant’s managed prompt when one exists (baselineFidelity.prompt: "tenant"), or Moda’s fixed standard-agent baseline prompt in both arms otherwise ("generic"). Skill candidates — net-new drafts and edits alike — are registered as content-addressed gate skills automatically: the candidate body rides the proposed arm, and a skill_edit pins the current body (synced or receipt-recovered) in the baseline arm. Only a candidate without gateable content rests at PROPOSED with the gate pending. The resulting verdict always carries its fidelity label.
string
Optional candidate content, 40–200,000 characters,
PROMPT fixes only (400 for other fix types). Omitted = re-gate the stored candidate version. Uploaded content is recorded by hash (candidateRef.contentSha256), not registered as a prompt version.PROPOSED, VERIFIED, GATE_FAILED, and GATE_INCONCLUSIVE. A fix already GATING returns 409 (“advance to poll it”), as does any other status.
POST /tenants/:tenantId/fixes/:fixId/submit
Delivers aVERIFIED fix (any other status returns 409).
string
required
pr — Moda’s GitHub App opens a draft PR on branch moda/fix/<shortref> with the composed body and magic word. local_ref — records that you applied the change yourself on a branch (status → APPLIED_LOCALLY).string
The branch name. Required when
channel is local_ref.409), one open fix PR per problem and per target file (409 names the sibling), and older drafts for the same problem or file are superseded. The pr channel requires a routed target file and a registered candidate version (400 otherwise — content verified via upload must be handed back as a PR by the agent itself); an unconfigured GitHub integration returns 503.
POST /tenants/:tenantId/fixes/:fixId/mark-applied
The no-GitHub confirmation: you applied the candidate in your own infrastructure — updated the tool description in your agent’s tool definition, installed the drafted skill, or hand-applied a prompt edit — so there is no PR merge to confirm the fix. The fix flips straight toSHIPPED with deliveryChannel: "local", records fixRef: { appliedNote, appliedAt, promptVersionId }, and posts the same deterministic mark_fixed problem feedback the merge webhook would, with actor: "fix-mark-applied" and a fix_ref of { fix_id, short_ref, applied_note? } (no pr_url / merge_commit).
string
Optional operator note, ≤ 600 characters, recorded on
fix_ref.applied_note.duplicate boolean. Allowed from any resting or delivered status (UNROUTED, PROPOSED, VERIFIED, GATE_FAILED, GATE_INCONCLUSIVE, PR_OPEN, APPLIED_LOCALLY, HANDED_OFF); pipeline-live (SCOPING, PROPOSING, GATING), pre-candidate, and terminal statuses return 409. Idempotent: marking an already-confirmed fix (SHIPPED, CONFIRMING, HELD, REGRESSED) is a duplicate no-op that still re-posts the deterministic feedback. Confirmation monitoring then behaves exactly as after a merge — CONFIRMING while the problem is in fixed-monitoring, HELD after 14 clean days from appliedAt, REGRESSED on reopen.
POST /tenants/:tenantId/fixes/:fixId/dismiss
Rejects the fix (status →REJECTED) and records the reason as problem feedback. Already-terminal fixes (REJECTED, SUPERSEDED, ABANDONED, HELD) return 409.
string
required
1–2,000 characters. Feeds the problem discovery loop.
Fix provenance on problem feedback (fix_ref)
When a PR whose body or head branch carriesMODA-FIX-<SHORTREF> is merged, Moda’s GitHub App notifies the backend, which flips the fix to SHIPPED and posts a mark_fixed problem feedback with actor: "moda-github-app" and a structured fix_ref:
mark-applied posts the same feedback shape with actor: "fix-mark-applied" and a fix_ref of { fix_id, short_ref, applied_note? } — no pr_url or merge_commit, because no PR exists.
POST /problems/:id/feedback accepts the same optional fix_ref object on any mark_fixed you submit yourself. Notes:
fix_refis stored on the feedback row and threaded into the problem’s lifecycle change reason for provenance; it is not part of the deterministicfeedback_id, so re-asserting the same action with an amended ref converges on one row instead of minting duplicates.- If a problem that was marked fixed with a
fix_reflater reopens automatically, the associatedSHIPPED/CONFIRMINGfixes flip toREGRESSED. - The merge notification is idempotent — redelivered webhook events converge on the same
SHIPPEDfix and the same pending feedback row.
Errors
Next steps
- Dashboard: Fixes — the same pipeline with the stepper cards and gate scoreboard.
- CLI reference: Fixes —
moda fixes/moda fix, including the exit-code contract for agents. - Problems — the evidence and feedback endpoints fixes are built on.