Fintech

How to Write a Precedence Order an Agent Can Explain

Antonio Evans11 min read

A split diagram: on the left, clause cards with condition, priority tag, scope, and ID; on the right, a reasoning trace naming the winning clause, the overruled clause, and the deciding signal.
Key points

Key points

  1. 1Give every precedence rule a stable clause ID, an explicit priority signal, and a scope so the conflict has an addressable answer.
  2. 2Require the agent to emit a trace naming the winning clause, the overruled clause, and the deciding signal, or refuse and escalate.
  3. 3Debug wrong winners by checking missing signals, over-broad scopes, and tied priorities in that order.
  4. 4Hand reviewers a short checklist tied to the trace so a decision can be defended line by line.

Start with the conflict, not the prompt

Most agent instructions fail at precedence for a simple reason: they describe what the agent should do, not what happens when two instructions collide. A policy that says "refund if the item is damaged" and a policy that says "refunds above a threshold need manager approval" are both valid, and a model asked to apply them will pick an order silently. The problem is not that it picks wrong once. The problem is that nobody can tell why it picked.

The silence is measurable. When two agents run over the same task they disagree between 20% and 40% of the time depending on task type, and most systems respond by silently picking one answer, which makes debugging three to five times slower than a single-agent failure because you cannot tell which input was wrong or even that a conflict existed (conflict resolution patterns). The same invisibility applies inside a single agent reasoning over conflicting rules.

So the design goal is narrow and testable. Given a case where at least two clauses apply and point to different outcomes, the agent must produce three facts: which clause won, which clause it overruled, and which signal decided the contest. If it cannot name all three, it does not decide. That refusal is a feature, not a failure.

One framing worth internalizing: this is not evaluation of a free-text reason. It is structured output. The mature way to check a structured output is to assert on specific fields and to score confidence per field, not on the whole response. Error-detection work on structured outputs reports that per-field trust scores catch bad fields with 25% greater precision and recall than LLM-as-a-judge or token log probabilities (Cleanlab structured output benchmark). A precedence trace is exactly the kind of output where per-field scoring pays off: the winner field and the signal field can be checked independently.

Encode each rule as a clause with three parts

A clause is the unit of precedence. Give every rule a unique, stable ID (never renumber it when rules move; append instead), one priority signal, and a scope.

PartWhat it holdsExampleWhy it matters
Clause IDStable string, never reusedPOL-REFUND-004The trace can name exactly what won
Priority signalA number, a specificity tag, or a named exceptionspecificity: exceptionThe trace can say why it won
ScopeWhere the clause applies, and a stop conditionregion: EU, channel: cardPrevents a local rule from acting globally
OutcomeThe action the clause mandateshold_for_reviewGives the conflict something to compare

Pick one priority signal type per clause and be honest about which it is:

  • A number (explicit weight). Easy to sort, easy to tie. Two clauses at the same weight are an unhandled conflict unless you add a tiebreaker.
  • A specificity tag (general, condition-specific, exception). This encodes the common legal habit that a named exception beats the general rule, without arguing about numbers.
  • A named exception (a clause that explicitly says "notwithstanding POL-REFUND-004"). The most readable signal for reviewers because the overrule is written down.

The mistake to avoid is mixing all three without a declared order of types. Decide once, up front, which signal type outranks which. A simple workable order: named exception beats specificity tag beats numeric weight, and within a type the more specific scope wins. Write that order as a clause itself, so it can be cited.

Scopes are where most wrong calls are born. Scope should always include a stop condition, for example applies only when transaction_amount_under_100 or applies only to customers onboarded before the policy date. A clause whose scope has no boundary is a clause that will overreach, and the trace will faithfully report the overreach as if it were correct.

You do not have to invent the need for IDs from scratch. Regulated environments already build identity into records: audit retention setup expects record types, per-user or per-service criteria, and an explicit priority level that says which policy wins when several exist (Microsoft Purview audit retention). That is the same shape as a precedence clause. Borrow the pattern rather than reinventing it.

Require a trace with three named fields

The trace is a contract. Define it as a schema first, then instruct the model to fill it. Keep it small enough that a refusal is unambiguous.

{
  "decision": "hold_for_review",
  "winner_clause_id": "POL-REFUND-004",
  "overruled_clause_id": "POL-REFUND-001",
  "deciding_signal": "named_exception",
  "scope_checked": true,
  "confidence": 0.0
}

The instruction that goes with it:

Apply all clauses whose scope matches. If two or more clauses match with different outcomes, output winner_clause_id, overruled_clause_id and deciding_signal. deciding_signal must be one of: numeric_weight, specificity_tag, named_exception, tiebreaker. If you cannot identify both a winner and an overruled clause, or you cannot name the deciding signal, set decision to "escalate" and leave the other fields null. Never guess a signal.

Two things make this enforceable rather than aspirational. First, an explicit thinking section separated from the response section keeps the model from blending reasoning into the answer, a pattern popularized in hallucination-resistant prompt design where the model is asked whether it has enough facts before answering. Second, a structured output schema with required fields means the trace either validates or it does not. Evaluation frameworks let you assert on exact fields, on JSON structure, and on custom functions with thresholds, which is how you turn "the trace usually names the right winner" into a pass or fail rate (promptfoo expected outputs).

The refusal path deserves equal weight. If the trace is incomplete, escalate rather than default. A wrong automatic decision in a money movement or access-control flow costs more than a queued review. The legal AI literature makes the same point from the other direction: when contradictory retrieved evidence goes unresolved, generation models tend to merge it, producing outputs that are legally unsound (LegalWiz contradiction detection). Merging is exactly what an incomplete trace looks like from outside.

Explainability systems that record a full reasoning chain and keep it queryable show what this buys you operationally. When the trace links answer to source and stays persistent, you get audit logs and retrospective analysis instead of a one-time explanation (TrustGraph explainable AI). A precedence trace should be stored the same way, tied to the clause versions that were live when the decision was made.

A worked example with numbers

Suppose a payments platform has four clauses governing whether a disputed charge is refunded, held, or denied.

ClauseOutcomeSignalScope
POL-REFUND-001refundnumeric weight 10all card disputes
POL-REFUND-004hold_for_reviewnamed exceptiondisputes where delivery confirmation is missing
POL-FRAUD-002denyspecificity tagdisputes flagged as first-party fraud
POL-VIP-010refundnumeric weight 50enterprise accounts, manually chosen

Declared signal order: named exception, then specificity tag, then numeric weight. Declared tiebreaker within numeric weight: higher number wins, and equal numbers escalate.

Case 1. A standard card dispute with a missing delivery scan. POL-REFUND-001 wants a refund. POL-REFUND-004 names itself as an exception and wants a hold. The trace reads winner POL-REFUND-004, overruled POL-REFUND-001, deciding signal named_exception. Note what the numeric weight of 10 did: nothing. This is the decision rule most teams get wrong, assuming the bigger number wins. Under a declared order of types, the type outranks the number, and the trace makes that visible.

Case 2. An enterprise account with a fraud flag and a missing delivery scan. POL-VIP-010 (weight 50) says refund. POL-FRAUD-002 is a specificity tag and says deny. POL-REFUND-004 is a named exception and says hold. Winner: POL-REFUND-004, overruled POL-FRAUD-002, signal named_exception. The VIP weight never enters the final answer, which a human reviewer will want explained. The trace does that in one line.

Case 3. Two clauses with numeric weight 50 and no exception. The trace must come back escalate with null winner and null overruled. That is a designed outcome, and it should route to a queue rather than a coin flip.

The delivery evidence in case 1 is a good illustration of why the deciding signal needs to be checkable. Delivery confirmation is not a single thing: a basic delivered scan may not establish where a package was left or who received it, while photos, address data and signatures are stronger (chargeback evidence). "Delivery confirmation is missing" therefore depends on which evidence type the clause means. If the clause does not say, the trace will name a winner with a scope that is quietly too broad. Writing scope stop conditions is the fix.

Evidence discipline also matters for the reviewer's faith in the system. Merchants contesting chargebacks win roughly 30% to 45% of cases they fight, and those numbers drop fast when evidence is incomplete, mismatched or late (Chargeblast evidence packet). A trace is the agent's evidence packet. Incomplete, mismatched, late, and the review fails.

Three debugging moves when the trace names the wrong winner

When a wrong call comes in, do not start by rewriting the prompt. Read the trace and work through these three checks in order. They are ordered by how often they are the culprit.

Move 1: Missing or untyped signal. The trace names a winner and an overruled clause but the deciding signal is numeric_weight when the correct answer depended on an exception. Cause: the exception clause has no explicit "notwithstanding" language, or its signal type was left unset. Fix: type every clause and rerun the failing case only. If the signal is null but a decision was still returned, your schema is not enforcing the refusal path; tighten validation first, before touching logic.

Move 2: Over-broad scope. The winner is right in principle but should not have applied here. Cause: the scope has no stop condition, so a regional or product-specific clause fired on a case outside its intended range. Fix: add the boundary to the scope, then rerun the full case set, not just the one that failed. Scope changes tend to fix one case and break three.

Move 3: Tie or undeclared type order. The trace shows two clauses at equal priority, or the agent ranked a specificity tag below a number because the type order was never stated. Fix: declare the tiebreaker and the type order as citable clauses, and add a regression test asserting escalate on every known tie.

A worked sequence. A refund was issued on a case that should have been held. The trace says winner POL-REFUND-001, overruled POL-REFUND-004, signal numeric_weight. That tells you immediately the agent compared numbers and ignored the exception type. You check POL-REFUND-004 and find its signal field is empty in the source config. That is move 1. You set named_exception, add a test that asserts the deciding signal equals named_exception for missing-delivery cases, and rerun. If it still fails, you check scope (move 2). If it passes but a neighboring case flips, you check ties (move 3).

Keep the trace, the clause version, and the test that covers the case. A trace without the clause version is not auditable, because the rule may have changed since.

This is where model routing becomes a practical question rather than an architectural one. You often want a cheap model for the bulk of routine cases and a stronger one for escalation review, without maintaining separate integrations. Quanta Inference puts text, image, video, audio and embedding models behind one OpenAI-compatible API with prepaid pricing in US dollars, which keeps the trace schema and prompt identical across models (https://quantaintelligence.ai/inference). Switching the model should never change the trace contract.

A review checklist a compliance reviewer can follow

A reviewer defending a decision in a meeting or to a regulator needs a short, repeatable sequence. The checklist should mirror the trace field by field.

CheckQuestionPass condition
1. Winner existsDoes the trace name a clause ID that exists in the live ruleset?ID resolves, clause was in force on the decision date
2. Overruled clause namedIs there a specific clause that lost, not a vague "other rules"?A second real ID appears
3. Deciding signal matches declared orderIs the signal one of the four permitted values, and consistent with the declared type order?Signal type outranks the loser by the declared order
4. Scope checkedDid both clauses actually apply to this case?Both scopes evaluate true with boundaries respected
5. Evidence matches the reasonDoes the cited evidence prove the thing the winning clause requires?Evidence type matches what the clause demands
6. Refusal honoredOn incomplete traces, did the system escalate rather than decide?No decision with null winner, overruled or signal
7. ReproducibleCan a second reviewer rerun the case and get the same trace?Same winner, overruled clause and signal on rerun

Two of these deserve elaboration because they are the ones reviewers actually catch.

Check 3 is about the rule for ranking, not the ranking itself. A reviewer does not need to know which number is bigger. They need to see that the signal used matches the declared order of signal types. If the declared order says named exception beats specificity tag, and the trace says a specificity tag beat a named exception, that is a defect even if the outcome happens to be right.

Check 5 connects the trace to evidence discipline. A clause that requires proof of delivery is not satisfied by a tracking scan whose location is unknown. Reviewers in payment disputes work this way already: the evidence must address the specific reason the dispute was raised, and mismatched evidence loses cases (evidence checklist). A good cover letter summarizes the situation and lists what is attached; a good trace does the same for a decision (dispute package checklist).

Check 7 requires persistence. The trace should be stored with the clause versions and be queryable later. That is the same principle as retention policies that keep audit records for a defined period and let you retrieve them after the fact (Microsoft Purview). If you cannot pull the trace for a case from six months ago alongside the rules that were live then, you cannot defend the decision.

For teams building agent-based review workflows, the larger lesson from dispute resolution research is that specialized agents doing triage, evidence retrieval and reasoning outperform a single generalist, but explainability and escalation calibration are among the four challenges that decide whether such a build reaches production (multi-agent dispute resolution). Precedence trace design is a cheap way to get ahead of the explainability challenge before you add more agents.

When the ruleset spans several business lines and you want the precedence order reviewed against your actual policies, a short working session with someone who has built these systems will surface scope gaps faster than another round of prompt edits. A free 30-minute AI discovery call is one way to get that review (https://quantaintelligence.ai/calendar).

FAQ

FAQ

What is a precedence order in an agent system?

It is the explicit rule that decides which of several applicable instructions wins when they point to different outcomes. It consists of clause IDs, a priority signal per clause, and scopes, plus a declared order of signal types. Without it, the agent picks silently and the choice cannot be audited.

Why require a trace that names the overruled clause?

Naming the loser proves the agent saw the conflict rather than missing it. A trace with only a winner cannot be distinguished from an agent that never noticed the second rule. Naming both, plus the deciding signal, makes every decision checkable by a reviewer and reproducible by a second run.

What should the agent do when it cannot name all three trace fields?

It should refuse to decide and escalate. A complete trace requires a winner, an overruled clause, and a deciding signal drawn from the declared set. If any is missing, the correct output is an escalation, and the schema should enforce that no decision is returned alongside null trace fields.

How do I debug a trace that picks the wrong winner?

Check three things in order: whether the winning path is missing or untyped signal (especially a named exception left unset), whether the winner's scope is too broad and lacked a stop condition, and whether two clauses tied or the signal type order was never declared. Fix one thing at a time and rerun the full case set after any scope change.

How does a reviewer use the trace to defend a decision?

The reviewer walks a short checklist: the winner ID resolves in the live ruleset, a real overruled ID is named, the deciding signal matches the declared type order, both scopes truly applied, the evidence matches what the winning clause requires, and the case reproduces. If all six hold, the decision can be defended line by line rather than as a single opaque output.

Sources

Sources

  1. When Your Agents Disagree: Conflict Resolution Patterns ...
  2. Real-Time Error Detection for LLM Structured Outputs
  3. Manage audit log retention policies
  4. Building Hallucination Resistant Prompts
  5. Assertions and Metrics - LLM Output Validation
  6. A Multi-Agent Generation Framework for Contradiction ...
  7. Explainable AI
  8. Fighting Chargebacks with Delivery Confirmation
  9. Building a Winning Chargeback Evidence Packet
  10. Dispute Package Checklist: What to Submit to Win
  11. AI Dispute Resolution: Multi-Agent Architecture Guide
About the author

About the author

Antonio Evans
Antonio Evans

Antonio Evans is the founder of Quanta Intelligence and a fractional CTO who builds HIPAA-ready AI systems for health and life-science companies. Before that he was technical co-founder of FriendsEAT for twenty years, CEO of Socialdraft, and a data engineer at IBM. Antonio lives with his dog Santino in Lisbon, Portugal.

All articles