Your prompt is a graph, not a list
Every contradiction we have written about, including our own first post, teaches you to hunt for two rules that fight. The defect in this file needs three, and deleting any one of them makes it compile.
TL;DR A prompt defect does not have to sit on a line or inside a pair of lines: three routing rules in three different sections of a contact-triage prompt, no two of which conflict, are 3 blocking logic/syllogism errors at F (72/100), and deleting any single one of them returns the file to 0 error, 0 warning, 0 info.
Here is the model most people carry: a system prompt is a list of rules, and a bad prompt is a list with a bad rule in it. Review is therefore a scan. You read down the file, you stop on anything that looks wrong, and when two lines look like they might disagree you hold them side by side and decide. That model is good enough for most of what goes wrong, which is why it survives. This post is about the class of defect it cannot see, and the class is not exotic. It is the ordinary result of three teams editing one file.
§1Three sentences, three sections, three teams
A contact-triage prompt for a broadband provider. Its job is small: read the contact, name the destination. Over eighteen months it accumulated a definitions section, because compliance wanted the word chargeback used precisely, and a routing section, because ops wanted disputes to reach a human with ledger access, and a constraints section, because billing kept getting chargebacks it could not act on.
Each of those three groups wrote one sentence. Each sentence is true of the business. Every one would survive a review, because every one is annotated, second person where it needs to be, and unambiguous on its own.
# Role @@ role: name the domain so "helpful" has a scope You are a contact triage agent for Northwind Broadband. # Definitions @@ chargeback: compliance asked for this wording in 2024 - All chargebacks are disputes. # Routing @@ dispute_route: a dispute needs a human with ledger access - All disputes are escalations. # Constraints @@ bank_owns_it: a chargeback is settled by the card network, not by us - No chargeback is an escalation.
Put the file through the checker and it is three blocking errors.
triage.tg:7:1 error logic/syllogism Contradictory chain - the rules on these lines force an overlap between "chargeback" and "escalation", which they also declare mutually exclusive. Relax one rule. (with lines 6, 8) triage.tg:11:1 error logic/syllogism Contradictory chain - the rules on these lines force an overlap between "chargeback" and "escalation", which they also declare mutually exclusive. Relax one rule. (with lines 4, 8) triage.tg:15:1 error logic/syllogism Contradictory chain - the rules on these lines force an overlap between "chargeback" and "escalation", which they also declare mutually exclusive. Relax one rule. (with lines 4, 6) ✗ 1 file - 3 error, 0 warning, 0 info
The arithmetic is one line of it. Every chargeback is a dispute, every dispute is an escalation, so every chargeback is an escalation, and the third rule says none is. That inference is two steps long, which is exactly one step longer than a human review reliably carries.
triage.tg - F (72/100) proven errors - grade capped at F tiers: base+z3
planes runtime 63 (what the model reads) · hygiene 100 (source only)
facets enforceability 50 x.21 · hardness 100 x.12 · directness 90 x.08 · consistency 0 x.17
structure 100 x.12 (hygiene) · annotation 100 x.12 (hygiene) · style 100 x.08 · security 100 x.08
lever consistency 0/100 (up to +17 overall) - Fix the ledger rows with logic/ or clarity/ codes;
state each fact in one place.
hardness 100, annotation 100, structure 100. Every facet that measures how well the rules are written is perfect, because they are well written. consistency 0 is carrying the whole grade, and consistency is the only facet that is a property of the set rather than of any member.§2Every pair passes
This is the part worth proving rather than asserting, because the claim is easy to doubt. Take the three rules and check them two at a time. Three files, three pairs, the annotations kept, nothing else changed.
# Role @@ role: name the domain so "helpful" has a scope You are a contact triage agent for Northwind Broadband. # Constraints @@ chargeback: compliance asked for this wording in 2024 - All chargebacks are disputes. @@ dispute_route: a dispute needs a human with ledger access - All disputes are escalations.
pairs_b.tg holds the definition beside the exclusion, and pairs_c.tg holds the routing rule beside the exclusion. Same shape, same annotations.$ typeglish check pairs_a.tg pairs_b.tg pairs_c.tg ✓ 3 files - 0 error, 0 warning, 0 info
So the usual advice fails here, and it fails specifically. Your prompt argues with itself is about rules that provably conflict, and its examples are all two lines. Two rules, no words in common goes one level harder: the conflicting pair need not share any vocabulary, because a rule is keyed to the action it names rather than the words you typed. Both posts are about pairs. Both would have you looking for the wrong shape.
A reviewer reads pairs. A prover reads the closure. Those are different objects.
§3The defect is the set
Lengthen the chain and the property gets sharper. Ops later split their routing rule in two, because a dispute gets a second pair of eyes before it leaves the bot, and the second pair of eyes is a human. Four rules now, spanning three sections, and the distance from chargeback to escalation is three hops.
# Role @@ role: name the domain so "helpful" has a scope You are a contact triage agent for Northwind Broadband. # Definitions @@ chargeback: compliance asked for this wording in 2024 - All chargebacks are disputes. # Routing @@ dispute_review: every dispute gets a second pair of eyes - All disputes are reviews. @@ review_route: a review is done by a human, so it leaves the bot - All reviews are escalations. # Constraints @@ bank_owns_it: a chargeback is settled by the card network, not by us - No chargeback is an escalation.
chain4.tg:7:1 error logic/syllogism ... (with lines 6, 7, 9) chain4.tg:11:1 error logic/syllogism ... (with lines 4, 7, 9) chain4.tg:13:1 error logic/syllogism ... (with lines 4, 6, 9) chain4.tg:17:1 error logic/syllogism ... (with lines 4, 6, 7) ✗ 1 file - 4 error, 0 warning, 0 info
Now delete each rule in turn, along with its @@ note, and check the four survivors. This is leave-one-out, the same instrument we pointed at a good prompt one rule at a time, and here it settles what kind of object the defect is.
$ typeglish check loo_7.tg loo_11.tg loo_13.tg loo_17.tg ✓ 4 files - 0 error, 0 warning, 0 info
That is the reframe, and it is worth stating flatly because it changes what you look for. Your prompt is not a list of rules that are individually right or wrong. It is a set of claims with edges between them, and a defect can be a property of a path. There is no line to blame. Ask which rule is broken and the honest answer is none of them, and you have four.
§4A finding with no single location
A defect with no location is awkward to report, and the reporting has a wrinkle you want to know about before you waste ten minutes on it. Each finding carries a range, and the range is a true file line: 7, 11 and 15 in the three-rule file, which is exactly where those rules sit. The numbers inside the message body are a different thing.
{
"code": "logic/syllogism",
"category": "logic",
"importance": "critical",
"severity": "error",
"blocking": true,
"range": { "line": 7, "column": 1, "endColumn": 32 },
"message": "Contradictory chain ... (with lines 6, 8)"
}
range.line is 7 and the rule is on line 7. The message says with lines 6, 8, and the other two rules are on 11 and 15.The companion numbers count positions in the file with blank lines and @@ annotations removed. In triage.tg that makes the first rule the fourth such line, the second the sixth and the third the eighth, which is where 4, 6 and 8 come from. Add a // comment and the numbering shifts by one, because a comment line keeps its position while an annotation does not. On a bare list of rules with no notes and no blank lines the two schemes coincide, which is presumably why it is easy to miss.
The score output prints both schemes in one report, and once you know that, it stops being confusing: the header rows above the facets say L4, L6, L8, and the ledger rows underneath say L7, L11, L15. Take the ranges, from --json or from the three lines of text output, and treat the in-message list as a count of how many rules the chain needed rather than as an address.
§5Which rule is wrong?
The message ends with Relax one rule, and it does not say which. That is not a gap. A prover can tell you that four claims cannot all hold; nothing in a compiler can tell you which of them your company does not actually believe. That decision is a policy question wearing a compiler error, and the useful thing about the error is that it forces the question into the open, in a pull request, in front of the three teams who each wrote one line.
There are three honest answers here and they do not cost the same. Weaken the middle rule to a majority claim, weaken it to an existential claim, or say what you actually meant and name the categories apart.
// - Most disputes are escalations. fix_most.tg - B (87/100) enforceability 50 · hardness 83 · consistency 100 // - Some disputes are escalations. fix_some.tg - B (85/100) enforceability 50 · hardness 70 · consistency 100 // - All disputes are reviews. fix_renamed.tg - B (89/100) enforceability 50 · hardness 100 · consistency 100
consistency from 0 to 100. The two hedges pay for it in hardness, 83 and 70 against 100, because most and some are quantifier claims the agent can satisfy by doing almost nothing.This is the trap in a chain finding, and it is worth naming: the cheapest edit is a hedge. Change one word to most, the three errors vanish, the build goes green, and you have shipped a routing policy that no longer says what happens to a dispute. The checker cannot object, because you did relax a rule and you were told to. The score is the only instrument that prices it, which is the same reason the add-a-rule checklist gates on --min 95 rather than --min B.
The version we would ship does not hedge and does not relax anything. It notices that the taxonomy was never the point. A triage prompt does not need to classify a contact into nested categories; it needs to name a destination. Categories chain, and destinations do not.
# Role @@ role: name the domain so "helpful" has a scope You are a contact triage agent for Northwind Broadband. # Definitions @@ chargeback: the card network settles a chargeback, so nobody here can reverse one - A chargeback IS a card-network reversal. @@ dispute: a dispute is ours to decide, which is what makes it different - A dispute IS a charge the customer asks us to review. # Routing @@ chargeback_route: the payments team holds the network case file - WHEN the customer reports a chargeback THEN you MUST hand off to payments. @@ dispute_route: a dispute needs a human with ledger access - WHEN the customer asks us to review a charge THEN you MUST escalate to tier 2. @@ one_route: two destinations in one reply sends the customer to neither - You MUST name at most 1 destination in a reply.
0 error, 0 warning, 0 info at A (95/100), up from F (72/100), and no policy was changed on the way. Both definitions still render, so the model still learns the distinction compliance cared about. What is gone is the transitive scaffolding: nothing in this file says that one category contains another, so there is no chain left to close.That is the transferable move, and it is smaller than it sounds. Every all X are Y in a prompt is an edge in a graph you are not drawing. Sometimes you need the edge, and then you want the checker holding it. Often you wrote a taxonomy when you meant a rule, and the fix is to write the rule.
§6Common questions
- Why does my prompt contradict itself when no two rules conflict?
- Because a contradiction does not have to be pairwise. If your prompt says all chargebacks are disputes, and all disputes are escalations, and no chargeback is an escalation, then every pair of those three sentences is consistent and the three together are not. A reviewer reads a prompt one rule at a time and at best one pair at a time, so a defect that needs three rules to appear is invisible to the reading. TypeGlish closes the chain instead: the same three lines are 3 blocking
logic/syllogismerrors and the file scoresF (72/100). - What is logic/syllogism in TypeGlish?
- It is the blocking error for a contradictory quantifier chain: a transitive path through your all and every claims that forces two categories to overlap while some other rule declares them mutually exclusive. It is reported once per participating rule, each report naming the others, because the defect has no single location. It is distinct from
logic/contradiction, which is the pairwise case, and fromlogic/quantifier, which is a direct conflict between two quantified claims. - Which rule should I fix when the checker reports a contradictory chain?
- Whichever one overstated your policy, and the checker will not tell you which that is, because it is not a compiler question. All three relaxations clear the error and they do not cost the same: naming the categories apart takes the file to
B (89/100)withhardness 100, while hedging the middle rule to most disputes are escalations givesB (87/100)athardness 83and some disputes are escalations givesB (85/100)athardness 70. The cheapest edit is a hedge, and the score is where you see what the hedge cost. - Do the line numbers in a logic/syllogism message point at my file?
- The primary one does and the companions do not. Each finding’s
rangeis a true file line, so a contradictory chain over three rules reports three findings at three real locations. The numbers inside the message body, thewith lines 4, 6part, count positions in the file with blank lines and@@annotations removed, so in any real prompt they will not match your editor’s gutter. Readrange.linefromtypeglish check --jsonand treat the message list as a count of how many rules the chain needed. - How do I review an agent prompt for defects that span several rules?
- Stop trying, and change what you review. Reading for pairs does not scale: a 40-rule prompt has 780 pairs and 9,880 triples, and no reviewer holds that. Point a checker at the whole file in CI and spend the human read on the things a prover cannot judge, which is whether each rule states the policy you actually have. When a chain does fire, the useful move is leave-one-out: delete each named rule in turn, and the one whose removal you are unwilling to ship is the rule you actually disagree about.
The reason this class of finding exists at all is that the quantifier words are read as logic rather than as emphasis. all, every and each, some and any, no and none form a square of opposition the checker quantifies over, which is also why the arithmetic in your prompt can be wrong in a way that is provable rather than arguable. Two of those words in one file is a claim. Three of them in a line is a graph, and the graph is the thing your review was never reading.