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: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.
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.
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).
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.
string
Optional candidate content, 40–200,000 characters. 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/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:
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.