The thesis
Programs are deterministic but rigid. Humans are flexible but unreliable. Agents inherit the flexibility without inheriting the formal guarantees, and modern systems increasingly let them act anyway.
The failure is structural. An agentic workflow mixes two modes that do not belong in the same actor: generative reasoning, which proposes plans, explanations, code, and claims probabilistically; and operational commitment, where the surrounding system must decide whether work is complete, whether a transition is valid, whether an effect may be attempted. Without an external boundary, the same stochastic actor that performed the work also declares that the work succeeded.
Decision Gate places a deterministic boundary between proposal and acceptance. A workflow declares what must be established. Evidence is submitted or acquired, admitted under explicit policy, and evaluated against typed conditions. A claim or transition proceeds only when the declared proof relation is satisfied. If the obligation is false, unresolved, malformed, unavailable, or stale, the claim does not become accepted progress.
The actor may remain stochastic. Acceptance does not have to be.
Hallucination is only part of the problem
“Hallucination” usually names a model output that is false or fabricated. In agentic systems the commercially important failure is broader: the agent asserts that a condition is satisfied, and the host system proceeds as though the assertion were operational fact. “All tests pass.” “The cited case exists.” “The migration completed.” “The customer is eligible.”
These failures are not one thing, and collapsing them destroys the information needed to recover correctly. A claim can be semantically false: the test report shows a failing test. It can be unresolved: nobody ever submitted the report. Evidence can be present but insufficient: the report came from the actor being checked when policy demands an independent source. Absence can be positively observed: a complete registry query proves no record exists. And a check can fail operationally: the file would not parse, the source timed out. Each of these deserves a different response, and only some of them are the model’s fault.
Decision Gate begins by refusing the shortcut that causes the damage: treating the actor’s fluent declaration as the acceptance authority.
What it means to know something
Before any machinery, a question: what would it take to know that “the tests pass” is true?
Not certainty in the philosophical sense. Verification is always relative to a procedure: some named process, using named evidence from named sources, under stated assumptions, established a stated proposition at a stated time. That sentence has load-bearing parts. Change the source and you have a different fact. Change the scope, which commit, which account, which registry, and you have a different fact. Change when, and you may no longer have a fact at all.
Most systems flatten this into a boolean and lose everything that made the boolean meaningful. Decision Gate keeps the structure. Its claim is deliberately bounded: it can determine whether a named procedure, using named evidence under a named policy, established a named proposition. The credibility of the result depends on the law, the evidence, and the authorities that produced it, and the system is honest about that dependence rather than hiding it behind a confidence score.
That bounded claim turns out to be exactly what an agentic workflow needs, because a workflow does not need metaphysical truth. It needs to know whether it may proceed.
Typed conditions
An ambiguous instruction cannot be verified, so the first transformation is from prose to typed proof obligations. “Add five tests and make sure they pass” becomes a closed set of propositions:
P1: new_test_count == 5
P2: required_test_result_count == 5
P3: all(required_test_status == PASSED)
P4: tested_commit_digest == proposed_delivery_commit_digest
Completion law: P1 AND P2 AND P3 AND P4
The important move is not that language became truth. It is that an ambiguous sentence became a closed, inspectable set of obligations that either hold or do not.
Each condition declares an observation domain and a typed predicate over it, and the domains are closed on purpose: integers, exact decimals, booleans, bounded strings, dates, instants, closed records. Apparently trivial comparisons hide real semantics. Is 1 equal to 1.0? Does “contains” mean substring or set membership? Is an absent field different from a null one? Is a date a UTC instant or a local day? Decision Gate requires the law to answer these questions before evaluation. Invalid or incompatible operands are rejected when the condition is constructed; they do not drift into runtime and resurface as a convenient false.
Meaning, separated from retrieval
A condition defines what a proposition means: its domain, its predicate, and what evidence suffices for it. It deliberately does not define which API to call, which file to read, or which provider to trust. That is a separate, explicit binding: an acquisition path that describes how an observation can be produced for a condition.
The separation matters more than it first appears. “The tested commit matches the delivery commit” is one condition with one meaning. The digests might arrive from a CI receipt submitted by the calling harness, from a signed build manifest in a local document, or from a future registered integration that retrieves them directly. The condition does not change when the plumbing changes. Semantic law stays stable while acquisition varies by deployment, by privacy boundary, by customer environment.
Systems that fuse the two — where a condition simply is a provider query — cannot make this distinction, and it costs them: every change of data source silently becomes a change of meaning.
Evidence is a product, not a score
The common shortcut is to rank evidence on a single trust ladder. Decision Gate rejects the scalar. Evidence carries a set of independent facts: how it crossed into the system, what form its content takes, who or what produced it, what it was derived from, what exact scope it concerns, when it was observed, what integrity protections cover it, and how independent its source is from the actor being checked.
These axes do not reduce to one number. A value can have strong integrity and poor freshness. A source can be authoritative but out of scope. A local observation can be fresh and exact yet controlled by the very agent whose claim it supports. A signature can prove who signed the bytes without proving the statement is true. The evidence-use policy for each condition names the combination that suffices for that claim in that context — a development scenario may accept the coding agent’s own test report, while a release scenario demands an independent receipt bound to the exact commit. The same boolean does not carry the same proof.
Admission is also not sufficiency. Material first passes an admission boundary — bounded, validated, hostile by default. Admitted evidence is then assessed against the condition’s policy, and every condition required by an evaluation resolves exactly one way:
| Resolution | Meaning |
|---|---|
| Present | A policy-satisfying typed value is available. |
| ObservedAbsent | A complete admitted observation proves absence in a declared universe. |
| Missing | The evaluation has no candidate for this target at all. |
| Insufficient | Candidates exist, but none satisfies the use policy. |
The distinctions prevent specific, familiar mistakes. A court-records API timing out is not evidence that the case does not exist. A search whose pagination never completed cannot prove absence. “Not found” and “nobody looked” are different states, and a system that conflates them will eventually overclaim.
True, False, Unknown
A typed predicate over a valid evidence resolution produces one of three semantic results. True: the admitted evidence satisfies the predicate. False: it contradicts the predicate. Unknown: the admissible evidence does not determine the predicate under the declared policy.
Binary systems force one of two errors. Treat unresolved work as false, and you lose the distinction between “contradicted” and “not yet proven” — the workflow punishes missing data as if it were failure. Treat unresolved work as true or “best effort,” and unsupported progress walks through the boundary. Three-valued semantics let the workflow hold safely without pretending in either direction. The agent can acquire more evidence, repair the work, or escalate, knowing exactly which obligation is unresolved.
Unknown has a precise meaning, and it is not a garbage bin. It is a semantic result derived from validly admitted evidence. A parse failure is not Unknown. A timeout is not Unknown. A forged witness, an authorization denial, a malformed payload — these are operational and integrity failures, kept in their own typed families, because they require different handling: retry, alarm, audit, refusal. A system that launders its failures into Unknown has quietly destroyed its own failure taxonomy.
Requirement algebra
Single conditions rarely decide anything interesting. Requirements compose them:
ALL(tests_passed, coverage_met, NOT critical_vulnerability_present)
ANY(primary_registry_match, two_independent_attestations)
QUORUM(2 of: ci_passed, security_scan_passed, reviewer_approved)
Requirement evaluation is compositional and deterministic, and it preserves unresolved results: a requirement over True, False, and Unknown inputs stays Unknown when the inputs cannot yet decide it, rather than defaulting in either direction.
One structural distinction does real work here. Stage completion — is this unit of work done? — may use rich logic over condition results, including lawful negation, because evidence truth is not monotone: a vulnerability can be discovered. Workflow topology — which stages may open next? — is restricted to monotone requirements over completed progress, because progress must not flicker: once a stage is ready because its prerequisites completed, unrelated further progress cannot make it unready. Rich logic inside a stage; forward-only motion through the workflow.
Progress is a graph
Real workflows are not lists. Tasks fork and rejoin; a release wants two of three reviews; unrelated components proceed in parallel. Decision Gate models a scenario’s topology as a finite dependency graph — chains, forks, diamonds, multiple roots, disconnected components — with a stage’s readiness derived from accepted completed progress under its monotone prerequisite law.
Every stage is in exactly one of four states: not ready, its prerequisites do not yet hold; ready but unopened; open and incomplete; or completed, meaning its completion law evaluated True in an accepted mutation. Opening a stage assigns no agent, reserves nothing, and cancels nothing — work scheduling stays outside, where it belongs. And a scenario finishes only by its own explicitly declared completion law over completed stages, never by a structural accident like “the last node in the diagram was reached.”
The graph is what prevents a fluent narrative of progress from becoming an implicit workflow cursor. The system can always answer, from accepted facts rather than from the transcript: which stages are provably eligible right now?
Evaluate first, accept second
Evaluation is pure. The evaluator consumes validated law, accepted progress, and an admitted evidence snapshot, and performs no I/O — no file reads, no clock, no network, no storage. From those inputs it derives a candidate: perhaps a completion delta and a new frontier, perhaps a False or Unknown attempt that changes nothing.
A candidate is not yet progress. Each run has a single accepted history with an exact current head, and a candidate commits only if the head it was derived from is still the head. Two workers may race to extend the same run; one commits, and the other’s candidate is stale — not corrupt, not partially applied, just derived from a superseded state, and re-derivable. Repeated submissions of the same operation replay the original result instead of double-applying. When a commit’s outcome is genuinely unknowable — storage may have committed while the response was lost — the protocol reports that state honestly instead of guessing.
The separation is what makes the whole system auditable: evaluation is a deterministic function you can replay, and acceptance is a serialization point you can inspect.
Acceptance is not effect
An accepted mutation may carry an intent: send the refund, publish the artifact, deploy the release. Acceptance proves that the intent belongs to an accepted revision of the run and is eligible for dispatch. It does not prove the external system received it, performed it, or performed it exactly once. Effects live outside the boundary, in systems with their own failure modes, and pretending otherwise is how workflows come to believe in refunds that never happened.
The same honesty applies to the record itself. Durable state is revalidated on load rather than trusted because the system wrote it earlier. An exported run history can be independently re-verified — its artifacts re-hashed, its evaluations replayed against recorded evidence. And the verification claims stay separate on purpose: integrity of the record, authenticity of the source, currentness of the bundle, and truth of the original evidence are different properties, established by different means. A green check that collapses them is a green check you cannot trust.
What this does not solve
Decision Gate hardens workflows against unsupported and false claims that can be expressed as explicit proof obligations. That sentence has edges, and they are load-bearing.
It does not automatically decompose arbitrary prose into the right propositions; the workflow author, or an upstream tool, declares what matters. It does not make weak evidence strong: a lazily written predicate or a permissive evidence policy produces exactly the proof relation it declares, checked exactly. It does not see hidden reasoning, adjudicate contested interpretation, or detect deception outside the assurance facts the policy names. A source can be honestly queried and still be wrong; the policy decides how much independence and integrity a claim class requires.
What it removes is narrower and structural: the actor’s own fluency as the deciding authority. Every claim the workflow chooses to gate must produce evidence that survives a declared policy, and every acceptance leaves a record of exactly what was established, from what, under which law.
Where this leaves us
The mechanism is formally specified; its semantic and protocol properties are established; a working implementation exists, and its formal models and their bounds are documented rather than waved at. What its adoption changes about agent behavior across models, harnesses, and domains is an open empirical program — this system is the instrument that makes the question testable, not a claim that the answer is already in.
Agents will keep proposing reality fluently. The systems around them get to decide whether proposals become facts. Basics teaches the working vocabulary, Applications shows the boundary inside real products, and the Docs carry the full formal treatment: the requirement algebra, the graph model, the evidence standards, and their proofs.
(Somewhat) more seriously than the tagline suggests: this is the same system described in the Normal tab, walked through by the person who built it. I am going to skip past the introductory material on what hallucinations are, whether objective truth exists, and the fundamental nature of reality — I leave those simple issues as exercises for the reader.
The Normal and Informal documents are independently authored projections. Each mode owns its headings and end matter; the renderer scopes every heading anchor by mode so the two outlines cannot collide.
The thesis
LLMs hallucinate. Hallucinations are bad. “Logical” claims in sentences can be decomposed into chunks that can be checked for veracity. This provides an empirical way to detect certain types of hallucinations and harden agentic workflows against false claims of completion. The end.
The slightly longer version hinges on one observation: programs are fast and deterministic, humans are slow and can think, LLMs are faster than humans and approximate thinking — but are not deterministic. Somebody in that lineup has to be the adult at the moment a claim becomes a decision, and it should probably be the participant that cannot be talked into anything.
Hallucination is only part of the problem
In 2023, attorney Steven Schwartz filed a motion in Mata v. Avianca, Inc. (S.D.N.Y.) citing six court cases generated by ChatGPT. None of them existed. One — Varghese v. China Southern Airlines, Co., 925 F.3d 1339 (11th Cir. 2019) — was internally consistent enough to include citations to additional cases that also did not exist. When Schwartz asked ChatGPT to verify the case, it confirmed it was real. He was sanctioned by the court.
Note what actually failed here. It is not just that the model invented a case; it is that the workflow accepted the model’s own confirmation as verification. The claim “this case exists” is checkable — case_records.count("Varghese v. China Southern") != 0 is a database lookup, not a philosophy seminar. The implied claim, “and it supports my argument,” is a different and harder claim. Collapsing the two is how you end up explaining yourself to a judge.
What it means to know something
Consider two potential LLM outputs: “The sky is blue” and “Barcelona beat Real Madrid.”
Both sentences make claims about the nature of reality. A pedant might note, “Wildfires turn the sky red,” or “What even is blue,” and the pedant is annoyingly onto something: every claim is true relative to a procedure for checking it. So let us actually run the procedure on the second one.
Claim: "Barcelona beat Real Madrid."
For this to be accepted, what would need to be true?
C1: a match between the two exists on the claimed date
C2: winner == 'FC Barcelona'
Acquisition: my harness queries the ESPN scores API and submits the
response as caller evidence. The policy requires the competition, the
date, and both team identifiers to match exactly -- "some match, at
some point, probably" does not clear the bar.
Observed (submitted as caller evidence):
match: 2025-05-11, La Liga
final_score: Barcelona 4 - 3 Real Madrid
C1 -> True. C2 -> True. Accepted.
Now tighten the claim: "Barcelona beat Real Madrid 6-0."
C3: final_score == (6, 0)
Observed: (4, 3)
C3 -> False. Rejected as stated. A 4-3 win is still a win; it is not
the claim that was made. Precision is the entire point.
The worked example intentionally preserves the decomposition, evidence, and verdict structure used throughout this explanation.
That is the whole trick, seen small: a sentence became conditions, the conditions met data, and the verdict followed from the data rather than from the model’s confidence.
Typed conditions
Now for the commercially relevant version. Imagine you are a naive, well-intentioned developer who wants to improve their codebase. You might say: “<insert coding agent name>, please add 5 tests to this file AND make sure they all pass.” Pressing past the danger of asking for tests that pass, we are immediately presented with several problems:
- How do we know the tests were added?
- How do we know there were five of them?
- How do we know they passed?
Prompting helps with this. You can threaten the LLM with abuse, promise a reverse Skynet, invoke the great token shortage of 202X, and so on. Such antics improve reliability — or so I am told.
Alternatively, we can turn the sentence into data:
P1: new_test_count == 5
P2: all(required_test_status == PASSED)
P3: tested_commit_digest == delivery_commit_digest
The existence of P1’s answer trivially settles questions 1 and 2, P2 settles question 3, and P3 settles the question you forgot to ask: passed on which commit? Each proposition has a type, a domain, and no tolerance for vibes. Is 1 equal to 1.0? Wrong question — the condition already decided, before evaluation, what kind of number it accepts.
Meaning, separated from retrieval
Here is a distinction that looks pedantic until it saves you: what a condition means and where the data comes from are different things. “The tested commit matches the delivery commit” means the same thing whether the digests arrive from a CI receipt, a signed build manifest, or a very sincere intern. The condition is the meaning. The retrieval path is plumbing, declared separately, swappable per deployment.
Systems that fuse the two get a fun failure mode: change your data provider, silently change what your checks mean. We prefer our meanings load-bearing.
Evidence is a product, not a score
The industry’s favorite move is to give evidence a single trust number and call it governance. But a Boolean with a nice haircut is still just a Boolean. Where did it come from? Who controlled the source — was it, say, the same agent whose homework is being graded? How fresh is it? What exact commit, account, or registry does it concern? A signature proves who signed the bytes; it does not prove the bytes are telling the truth.
So evidence here carries all of those facts separately, and each condition’s policy says which combination is good enough for that claim. Your dev loop can accept the agent’s own test report. Your release gate can demand an independent receipt bound to the exact commit. Same boolean, different proof, and the system refuses to pretend otherwise.
And when evidence is not there, the system says how it is not there: nobody looked (Missing) is different from a complete search proving absence (ObservedAbsent), which is different from “candidates exist but none clears the policy” (Insufficient). A timeout is none of these; a timeout is the phone ringing unanswered, not an answer.
True, False, Unknown
Every gated condition resolves to True, False, or Unknown, and Unknown is the load-bearing one. Binary systems have to lie in one of two directions: call unproven work False (and punish missing data as failure) or call it True (and wave unsupported progress through). We do not solve missing data by lying with greater confidence. Unknown means exactly: valid evidence, honestly admitted, does not yet decide this — so the gate holds.
What Unknown is not is a junk drawer. Parse failures, timeouts, forged payloads, and permission denials are failures, typed and kept separate, because you retry a timeout and you alarm on a forgery, and a system that files both under “shrug” will eventually do neither.
Requirement algebra
Individual facts are rarely the decision. ALL, ANY, and QUORUM are the ways of making several facts everybody’s problem: all of tests-pass and coverage-met and no-critical-vulns; any of primary-registry-match or two independent attestations; two of three reviewers.
One rule with teeth: the logic inside a stage can be as rich as you like, including negation — “no critical vulnerability present” is a perfectly good requirement. The logic that moves the workflow forward is monotone only: once a stage is ready because its prerequisites completed, more progress elsewhere cannot un-ready it. Your workflow moves like history: forward, no takebacks.
Progress is a graph
Real work forks, joins, and proceeds in parallel; a to-do list that understands forks should not pretend the first arrow picked an employee. Scenarios here are dependency graphs. A stage is not-ready, ready, open, or completed, and “completed” means its completion law evaluated True in an accepted mutation — not that an agent said so in a chipper tone.
Also: reaching the last box in the diagram is not success. Success is its own declared condition. Diagrams are not contracts; contracts are contracts.
Evaluate first, accept second
Evaluation is a pure function: validated law in, admitted evidence in, candidate result out. No I/O, no clock, no network — nothing for a flaky environment to smuggle in. Then, separately, the candidate tries to commit against the run’s exact current head.
Two agents may both finish the same future; only one gets to make it history. The loser’s candidate is stale — not corrupt, not half-applied, just derived from a world that no longer exists, and cheap to re-derive. Submit the same operation twice and you get the original result replayed, not a double refund. And when the commit’s fate is genuinely unknowable — storage may have committed while the response was lost — the system reports exactly that, instead of picking whichever answer makes the dashboard greener.
Acceptance is not effect
Writing refund: true is, tragically, not a banking system. An accepted run can carry an intent — send the refund, ship the release — and acceptance proves precisely that the intent belongs to accepted history and may be dispatched. Whether the payment processor actually moved money is the processor’s story, with its own failure modes, tracked separately. Systems that conflate “we decided to” with “it happened” end up very confident about refunds nobody received.
Same discipline for the record itself: stored state is re-validated on load, exported histories can be re-verified independently, and “the record is intact,” “the source was authentic,” and “the claim was true” remain three different statements established three different ways. One green light per claim, no bulk discounts.
What this does not solve
Time to be honest about the edges, since we want to save tokens and dislike pointless rambling for comedic effect.
This does not detect every hallucination — only claims someone bothered to express as proof obligations. It does not make bad predicates good: declare a lazy gate and it will be enforced with impeccable, useless precision. It does not read the model’s mind, settle contested interpretation, or catch a liar your evidence policy chose to trust. Garbage law, enforced flawlessly, is still garbage.
What it removes is the specific failure that keeps showing up in incident reports: the actor grading its own claim. One still needs to contend with agents attempting to weaken the pass criteria, or ignoring the gate entirely — but a gate provides the objective reference that makes those moves visible, which prose never did.
Where this leaves us
In summary, we are doing:
- token output (text) ->
- logical claims that can be structured (Decision Gate) ->
- programmatic data retrieval (Decision Gate) ->
- deterministic claim evaluation (Decision Gate) ->
- data output (JSON) ->
- agentic loop
I love abstraction, so there is quite a bit more I could say. However, linking to the Docs is probably the best choice for readability.
Let me know if I can clarify anything!
Michael “Yung Bidness” Campbell
Inspirations
- HAWK. (2020). Counter Ops [Official visual video]. YouTube.
- thrown. (2023). guilt [Official video]. YouTube.
- thrown. (2023). on the verge [Official video]. YouTube.