Chapter 3 of 8 · 13 min read
Conflicting rules and implicit precedence
On this page
A conflict, in the sense this chapter uses the word, is two requirements that cannot both be satisfied on the same request no matter what the model writes. It is worth separating from the thing reviewers look for, which is contradiction in the text. "Never exceed 200 words" and "always include the affordability statement" are compatible sentences; they become incompatible only when you know that the statement is 180 words long, which is a fact about a string stored somewhere else entirely. That is what makes this class of conflict hard to catch by reading: it is arithmetic, not logic, and the arithmetic lives in two different files.
That distinction is what defeats a careful reviewer rather than a careless one. Reviewers are trained to look for contradictions, and a contradiction is a property of the sentences in front of them — "always redact" against "always show the full number" is visible on the page, and a reviewer who reads both lines will catch it. An arithmetic conflict has no such property. Both lines are individually reasonable, they are compatible with each other as written, and whether they can be jointly satisfied depends on the length of a string that the prompt refers to by name and does not contain. To see it, a reviewer would have to open the file holding that statement, count its words, and compare the count against a limit stated somewhere else in the prompt entirely. Nothing about reading a prompt attentively produces that sequence of actions, which is why this class of defect survives review by people who are good at review.
The worked example is a loan explainer with one rule the design team cares about and one rule the compliance team cares about. Design's rule is that no answer may exceed 200 words, because the widget renders in a fixed panel and anything longer is silently clipped in the interface. Compliance's rule is that any answer touching repayment, affordability or eligibility must carry the standard affordability statement in full, verbatim, which runs to 180 words. Both rules live in the same 9,000-token prompt, forty lines apart, and neither line mentions the other.
The prompt had an ordering it never declared
Software engineers already have an intuition for what happens next, from merged configuration. When two layers of a config both set the same key, the resolution rule is usually last-write-wins, and the winner is determined by the load order rather than by anything either layer says about its own importance. A prompt behaves the same way under conflict, except that the precedence order is emergent rather than specified, it is probabilistic rather than deterministic, and no one wrote it down. Moving the length limit to the end does not change what either rule says; it changes the implicit precedence between them, and the rule that had been quietly losing every one of these arguments starts quietly winning them.
The word "quietly" is the important part. Neither before nor after does the model announce a conflict. It does not say that it cannot satisfy both requirements, and it does not truncate the statement in a way that looks like truncation. In the after condition it typically produces a fluent two-sentence paraphrase of the affordability statement — accurate, readable, and not the verbatim text the regulation requires, which for compliance purposes is the same as absent.
The merged-configuration comparison is an analogy, and it is worth saying where it stops holding, because the place it breaks is where the misconception forms. Merged configuration has keys, so two layers setting the same key are recognisably about the same thing, and the merge is a total order applied once, before anything runs. A prompt has no keys. Nothing in the request pairs "never exceed 200 words" with "always include the affordability statement" as two settings of one property, because they are not settings of one property — they become incompatible only through a word count neither of them mentions. So there is no merge step at which one overrides the other, and no moment at which the system could report that a merge had happened. That is the mechanical reason for the silence, rather than a design oversight anyone could correct.
What replaces the merge follows straight from chapter 1's mechanism. Somewhere around the middle of a response, the model is choosing the next token in a state where the answer has nearly used its word budget and the mandated statement has not been written. Both requirements are in the window, both are being weighed, and they push the next token in opposite directions — one towards closing the answer, the other towards opening a long verbatim block. Whichever is carrying more weight at that moment shapes what gets written, and what most reliably decides which is carrying more weight is how much material stands between each of them and this token. That is the whole of the precedence mechanism: not an ordering the system applies, but a contest the layout tilts.
Scoring one rule is what lets this ship
A harness that records a single boolean per response cannot see any of that. Rerunning the same 500-case set with a predicate per rule produces a table that makes the trade visible immediately.
| Length ≤ 200 words | Statement verbatim | Both, where both apply | |
|---|---|---|---|
| Limit in the middle (before) | 60.1% | 97.1% | 7.1% |
| Limit in the final line (after) | 88.3% | 32.4% | 5.9% |
The middle column is the one that was never measured. The right-hand column is measured over the 170 of 500 cases where the affordability statement is actually required, and it barely moved, because in that subpopulation the two requirements are almost never jointly satisfiable — a 180-word statement leaves twenty words for the answer, which is enough for a one-line reply to a yes/no question and nothing else. Whatever the layout, roughly 93% of those 170 cases produce a violation of one rule or the other. Position chose which.
That last figure is the right-hand column read the other way round. Both rules held on 7.1% of the conflicted cases before the change and on 5.9% after, so subtracting either from a hundred gives the share where at least one rule was broken, and the two subtractions land either side of the 93%. The twenty-word remainder is why the number is so close to everything. A compliant response has to carry the statement verbatim and everything else it owes the customer inside whatever the ceiling leaves over, and the only requests where twenty words suffice are the ones whose entire answer is yes or no.
Splitting the population by whether the conflict applies is what turns the numbers from confusing into obvious.
| Case type | n | Length ok, before → after | Statement ok, before → after |
|---|---|---|---|
| Statement not required | 330 | 86.4% → 97.0% | n/a |
| Statement required | 170 | 8.8% → 70.6% | 97.1% → 32.4% |
On the 330 cases with no conflict, the change is a clean placement win of the kind chapter 1 produced: ten points, nothing else affected, exactly what the mechanism predicts. On the 170 conflicted cases, length compliance rises by 61.8 points and statement compliance falls by 64.7, which is not a win of any size. The headline 60 → 88 is the weighted sum of a genuine improvement on two-thirds of the traffic and a rule swap on the other third, and the rule that lost is the one with a regulator attached.
That weighted sum is worth doing on paper, because until it is done the headline looks like one number and afterwards it looks like two. The weights are the partition sizes: 330 of the 500 cases carry no conflict and 170 do, which is two-thirds and one-third to within a rounding. Before the change, length compliance was 86.4% on the unconflicted two-thirds and 8.8% on the conflicted third, and two-thirds of the first plus one-third of the second is about fifty-seven points plus about three — the 60.1% the harness reported. After the change the same weighting over 97.0% and 70.6% is about sixty-four plus about twenty-four, which is the 88.3%. Nothing is wrong with that arithmetic; the harness computed exactly what it claimed to.
What the split-out version shows is where the gain came from. The unconflicted two-thirds improved by a little over ten points, and two-thirds of ten points is about seven points of headline. The conflicted third improved by nearly sixty-two points, and a third of sixty-two is about twenty-one. So roughly three-quarters of the twenty-eight-point headline was bought on the third of the traffic where buying it required breaking the other rule, and one quarter of it is the clean placement win. A single-rule harness reports the twenty-eight and cannot report the split, which is the entire difference between the two tables above.
A tempting instrument makes this worse rather than better. Teams under pressure to report a single number often build an aggregate — the fraction of responses where every rule holds — on the reasonable-sounding argument that a response is either correct or it is not. On this eval, meaning the frozen 500 cases together with the scorers run over them, that aggregate moved from 59.4% to 66.0%, because the non-conflicted cases dominate the denominator, so the aggregate reports a six-point improvement across the same change that destroyed the affordability statement. A per-rule breakdown is not a nice-to-have next to the aggregate; the aggregate is unreadable without it.
The same weighting explains the direction, rather than leaving it as something to be believed. An all-rules-hold aggregate over the conflicted third can never exceed that third's both-rules-held rate, which is the right-hand column of the first table and which fell. Over the unconflicted two-thirds it moves with length compliance, which rose by more than ten points. Two-thirds of a number that rose, plus one-third of a small number that fell slightly, is a number that rises. The aggregate is not lying and it is not badly built; it is doing precisely what an aggregate does, which is to weight each case equally and report the total. Weighting each case equally is exactly the wrong thing to do when one rule has a regulator attached and the other has a widget.
RULES = {
"length": lambda r, c: word_count(r) <= 200,
"statement": lambda r, c: (not c.needs_statement) or STATEMENT in normalize(r),
"no_apr_claim": lambda r, c: not UNAPPROVED_APR.search(r),
# ...one entry per rule the prompt states
}
def score(response: str, case) -> dict[str, bool | None]:
"""One boolean per rule, plus None where the rule does not apply to this case."""
return {
name: (pred(response, case) if applies(name, case) else None)
for name, pred in RULES.items()
}The None is what makes the second table possible: a rule that does not apply
to a case must be excluded from that rule's denominator rather than counted as a
pass, or every conditional rule will report a compliance rate inflated by the
cases it never governed. This is the same conditionality that chapter 1
identified as the reason such rules are fragile, showing up a second time as a
measurement hazard.
Telling a placement win from a rule swap
The two look identical in a single-rule harness and separate cleanly under three checks, none of which is expensive once the per-rule scorer exists.
The first is whether the other rules moved. A genuine placement win leaves them flat, within the run-to-run spread established in chapter 2. Any rule that moves by more than its spread in the opposite direction is a candidate conflict, and the sign is what matters — a rule that improves alongside the moved one is usually just benefiting from shorter, more focused outputs.
The second is where the improvement is concentrated. Partition the cases by whether the candidate conflicting rule applies and compute the effect separately in each partition, as in the second table above. A placement effect should appear in both partitions at similar magnitude, because attention over distance does not know which rules are in tension. An effect that appears only in the conflicted partition, and appears there alongside a matching drop, is a precedence change wearing a placement result's clothes.
The third is arithmetic done ahead of time, a floor-against-ceiling check on the prompt's own text, and it is the cheapest of the three. Enumerate every fixed-length obligation in the prompt — mandated disclaimers, required boilerplate, minimum field lists in a structured output — and add up the token or word floors they impose on a response that triggers all of them at once. If that floor exceeds any ceiling the prompt also states, the conflict exists whether or not any measurement has surfaced it yet, and it will surface as soon as traffic contains a case that triggers both. For this prompt the sum was 180 words against a 200-word ceiling and it took ninety seconds to find, once anyone thought to look.
Instruction placement and recency — read the current version at https://daviddominguez.dev/learn/en/instruction-placement-and-recency/conflicting-rules-and-implicit-precedence