> Markdown rendition of https://typeglish.dev/blog/prompt-graph-not-list ("Your prompt is a graph, not a list - Tidelines"). Canonical page: https://typeglish.dev/blog/prompt-graph-not-list · All pages: https://typeglish.dev/llms.txt

[← Tidelines](https://typeglish.dev/blog)/ Deep dives Aug 7, 2026

# 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.

by **TypeGlish team** 8 min read #deep-dives

Every pair agrees. The set does not.

**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.

## §1 Three 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.

triage.tg - three sentences nobody would send back ✗ blocked

```typeglish
# 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.
```

Read it top to bottom the way a reviewer does. There is nothing to stop on. The three claims are separated by two section headings and eight lines, and no two of them mention the same pair of things.

Put the file through the checker and it is three blocking errors.

tg check triage.tg - output

```
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
```

Note what the message names: an overlap between *chargeback* and *escalation*. Neither word appears in the same sentence as the other anywhere in the file. The overlap is derived, not written.

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.

tg score triage.tg - where the failure lands

```
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.

## §2 Every 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.

pairs_a.tg - the definition and the routing rule, alone ✓ compiles

```typeglish
# 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.

tg check - all three pairs

```
$ typeglish check pairs_a.tg pairs_b.tg pairs_c.tg
✓ 3 files - 0 error, 0 warning, 0 info
```

Nothing. Not a warning, not an info finding. There is no pair in this prompt that a pairwise review could have caught, because there is no faulty pair.

So the usual advice fails here, and it fails specifically. [Your prompt argues with itself](https://typeglish.dev/blog/prompt-contradictions) is about rules that provably conflict, and its examples are all two lines. [Two rules, no words in common](https://typeglish.dev/blog/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.

## §3 The 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.

chain4.tg - one more hop, same file ✗ blocked

```typeglish
# 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.
```

Four rules is six pairs and four triples. None of the six pairs is faulty. None of the four triples is faulty either, which is the next figure.

tg check chain4.tg - four rules, four reports

```
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
```

One report per participating rule, each naming the other three. The finding is reported four times because there is no single place to report it once.

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](https://typeglish.dev/blog/prompt-rules-ablation), and here it settles what kind of object the defect is.

tg check - each rule removed in turn

```
$ typeglish check loo_7.tg loo_11.tg loo_13.tg loo_17.tg
✓ 4 files - 0 error, 0 warning, 0 info
```

Four rules, four ways to remove one, four clean files. The set of four is unsatisfiable and every subset of three is fine, which is the textbook definition of a minimal unsatisfiable set and a very unhelpful thing to have in a code review.

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*.

## §4 A 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.

tg check triage.tg --json - one finding, trimmed

```
{
  "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.

## §5 Which 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.

tg score - three relaxations, all of them legal

```
// - 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
```

All three clear the errors and take `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](https://typeglish.dev/blog/one-more-rule) 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.

triage.tg - the same policy, written as destinations ✓ compiles

```typeglish
# 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.

## §6 Common 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/syllogism` errors and the file scores `F (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 from `logic/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)` with `hardness 100`, while hedging the middle rule to *most disputes are escalations* gives `B (87/100)` at `hardness 83` and *some disputes are escalations* gives `B (85/100)` at `hardness 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 `range` is a true file line, so a contradictory chain over three rules reports three findings at three real locations. The numbers inside the message body, the `with lines 4, 6` part, count positions in the file with blank lines and `@@` annotations removed, so in any real prompt they will not match your editor’s gutter. Read `range.line` from `typeglish check --json` and 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.

Field note

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](https://typeglish.dev/blog/prompt-arithmetic) 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.

∿ washed up Aug 7, 2026 ∿
