Docs Effect Reconciliation Queue

Effect Reconciliation Queue

When Mockarty performs a paid or irreversible external action — an AI model call, a deployment, a job on a paid runner — it records the exact outcome. Sometimes the outcome cannot be observed: the network dropped mid-call, a run was cancelled after the request went out, or the provider never answered. Mockarty never guesses in that situation. The action is kept in an unresolved state, its budget stays reserved, and the action appears in the effect reconciliation queue until an operator confirms what actually happened.

This page shows how an administrator reviews and resolves those items.

What the queue contains

Each item is one external action whose outcome is unresolved:

Field Meaning
executionId The unique identifier of the action.
effectFamily What kind of action it was, for example llm.chat (an AI model call) or coder.deploy.apply (a deployment).
status unknown — the outcome was not observed; operator_required — automatic recovery gave up and a decision is mandatory.
reason Why it is unresolved: transport_ambiguous, lease_expired, cancelled_after_dispatch or recovery_exhausted.
claim Present when another operator is already working on this item.

Listing the queue

Administrator access is required. The queue is always scoped to one namespace.

curl -H "Authorization: Bearer $TOKEN" \
  "http://localhost:5770/api/v1/admin/effects/reconciliation?namespace=my-team&limit=50"

Response:

{
  "items": [
    {
      "namespace": "my-team",
      "executionId": "coderdeploy-0f3a…",
      "effectFamily": "coder.deploy.apply",
      "status": "unknown",
      "reason": "transport_ambiguous",
      "createdAt": "2026-08-30T10:00:00Z",
      "updatedAt": "2026-08-30T10:05:00Z"
    }
  ],
  "nextCursor": ""
}

Useful filters: family (only one action kind), reason, minAgeSeconds (only items unresolved for at least that long), and cursor for the next page.

A cursor is bound to the namespace and every filter used to create it. If the scope changes, start again without a cursor; Mockarty rejects a cursor from another scope instead of silently skipping records.

Claiming an item

Claiming makes sure two operators never decide the same action at the same time. A claim is temporary — if you walk away, it expires and someone else can take over.

curl -X POST -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
  -d '{"namespace":"my-team","executionId":"coderdeploy-0f3a…"}' \
  http://localhost:5770/api/v1/admin/effects/reconciliation/claim

The response contains a claimToken and claimGeneration — keep both. While you investigate, extend the claim with POST /api/v1/admin/effects/reconciliation/heartbeat, or hand the item back with POST /api/v1/admin/effects/reconciliation/release.

Making the decision

First verify what the provider actually did — in the provider’s invoice, dashboard, or support answer. Then:

  • The provider demonstrably did nothing billable — close the item as no_effect:
curl -X POST -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
  -d '{
    "namespace": "my-team",
    "executionId": "coderdeploy-0f3a…",
    "decision": "no_effect",
    "claimToken": "<from claim>",
    "claimGeneration": 1
  }' \
  http://localhost:5770/api/v1/admin/effects/reconciliation/reconcile

For AI model calls (llm.chat) the reserved budget is released in the same step, and two extra fields are required: providerReference (the invoice line, dashboard operation or ticket that proves the no-effect) and evidenceSource (provider_invoice, provider_dashboard, provider_support or internal_effect_log).

  • The action actually happened — that cannot be declared here by design. A confirmed applied outcome requires the producing feature’s own reconciliation flow (for example, the coder mission’s deploy-outcome confirmation), which records the provider evidence it needs.

You can also claim and decide in one call by passing "autoClaim": true instead of a claim token.

From the command line

mockarty-cli effects queue --namespace my-team
mockarty-cli effects reconcile-no-effect coderdeploy-0f3a… \
  --namespace my-team --provider-reference invoice-77 --evidence-source provider_invoice

From the SDKs

The client namespace is used automatically. These methods require an administrator token.

Go SDK

page, err := client.EffectReconciliation().ListQueue(ctx, mockarty.EffectReconciliationListOptions{
    EffectFamily: "llm.chat",
    Limit:        50,
})
if err != nil {
    return err
}

result, err := client.EffectReconciliation().ReconcileNoEffect(
    ctx, page.Items[0].ExecutionID, "invoice-77", "provider_invoice",
)

Python SDK

page = client.effect_reconciliation.list_queue(effect_family="llm.chat", limit=50)
result = client.effect_reconciliation.reconcile_no_effect(
    page["items"][0]["executionId"],
    provider_reference="invoice-77",
    evidence_source="provider_invoice",
)

Java SDK

Map<String, Object> page = client.effectReconciliation()
    .listQueue(null, "llm.chat", null, 0, 50, null);
Map<String, Object> result = client.effectReconciliation()
    .reconcileNoEffect(executionId, "invoice-77", "provider_invoice");

For AI agents

The same queue is available over MCP through two tools: effect_reconciliation_queue (list what waits for a decision) and effect_reconcile_no_effect (close one item as provider-proven no-effect with evidence). Both require administrator authority.

Charges made after a cancellation

Sometimes a provider finishes and bills an AI model call even though its mission was cancelled while the call was in flight. Mockarty never refunds such a charge on its own: an administrator looks at the evidence and decides. Until then the charge stays in its own queue, Admin → Effects → Charged after cancellation.

Each item is an evidence card: the amount and currency, the provider and model, who cancelled the mission and why, the provider’s effect receipt, and an ineligible field. When ineligible is empty, the evidence is complete and you can decide. Otherwise it names why a decision is not possible yet, and the card stays in the queue:

ineligible Meaning
no_effect_receipt There is no receipt of what the provider did.
receipt_not_settled The provider’s outcome has not settled yet.
evidence_not_exact The receipt does not prove exactly what the provider did.
contradictory_evidence Two records disagree about the outcome.
cancellation_missing The cancellation of the mission cannot be found.
already_refunded This charge has already been refunded.
already_decided A decision about this charge has already been made.
hosted_wallet_refund_pending The charge was paid from a Mockarty Cloud wallet, but this installation has no connection to Mockarty Cloud through which the money could go back, so no decision is offered.

Write a reason (3 to 500 characters) and press Approve refund or Reject. An approval refunds the charge in the same step; a rejection keeps it. Either way the decision is final, the charge leaves the queue, and the owner of the mission receives a notification with the amount, the decision and your reason. If the evidence changed while the card was open, the decision is refused and the queue refreshes — review the card again.

When the charge was paid from the customer’s Mockarty Cloud wallet, an approval sends the money back to that wallet. The notice after Approve refund says whether it has already arrived or is on its way; if Mockarty Cloud is briefly unreachable, the return is retried by itself until the wallet is credited. If Mockarty Cloud refuses the return, the notice says why.

AI agents use the same queue through two MCP tools: refund_candidates (read the queue or one card) and refund_decide (decide one charge). Both require administrator authority.

The same guarantees apply as for the reconciliation queue: every decision is its own audit event, a decision is final, repeating it returns the first one, and the queue never spans namespaces.

Guarantees

  • Every decision is written to the audit log.
  • A decision is final: repeating the same decision is harmless, a contradicting one is refused.
  • An expired claim can be taken over; a stale claim can no longer decide anything.
  • The queue never spans namespaces.