02 · STATE: WHAT MUST SURVIVE?

The agent says it finished. Prove it.

Case Study Claims Processing Agent

A claims decision reaches a reviewer marked verified. The auditor asks which document span supports it. The transcript is not an answer.

45-60 min · Optional lab 02: False Completion

Video coming soon. The written lesson below is complete on its own.

WHAT YOU'LL LEARN

Separate conversation, execution state, artifacts, and external reality, and make state represent evidence, not claims.

State modelingEvidence

INVARIANT I1: no item is verified unless resolvable evidence exists.

“Save state” is not enough. First decide what state means.

Four categories

Conversation

What users/models said.

Model: I found pricing information.

Useful, but not automatically authoritative.

Execution state

What the runtime believes about progress.

current requirement = pricing
status = researching
pages fetched = 7

Artifacts

Useful objects produced by the run.

fetched page
evidence record
verification result
draft report

External reality

What actually exists outside the runtime.

Did the report get published?
Did the refund happen?
Did the ticket get created?

These can disagree.

Model:            “I published it.”
Application:      published = true
External system:  no report exists

The external system is authoritative about that external effect.

State is a model of reality

A field such as:

state["published"] = True

is still only an application claim.

Good state design makes incorrect claims hard to represent.

Structured state

class Evidence(TypedDict):
    evidence_id: str
    requirement: str
    url: str
    content_hash: str
    quote: str
    retrieved_at: str

class RequirementState(TypedDict):
    status: str
    finding: str | None
    evidence_ids: list[str]

class VendorReviewState(TypedDict):
    run_id: str
    vendor_name: str
    vendor_url: str
    requirements: dict[str, RequirementState]
    discovered_urls: list[str]
    visited_urls: list[str]
    evidence: list[Evidence]
    contradictions: list[dict]
    errors: list[dict]
    status: str
    terminal_reason: str | None
    pages_fetched: int
    cost_usd: float
    schema_version: int
    code_version: str

Why structure matters

Weak:

“Pricing seems complete.”

Checkable:

{
  "status": "verified",
  "finding": "No public list price is available.",
  "evidence_ids": ["ev_91"]
}

Introduce invariants

An invariant is a property that must remain true even when expected failures occur.

Example:

IF requirement.status == VERIFIED
THEN at least one evidence ID must resolve to stored evidence.

Code:

def validate_state(state):
    for name, req in state["requirements"].items():
        if req["status"] == "verified" and not req["evidence_ids"]:
            raise StateInvariantError(
                f"{name} verified without evidence"
            )

This is stronger than asking the model to “remember to cite sources.”

State transitions

Do not allow arbitrary jumps.

PENDING

RESEARCHING

CANDIDATE_FOUND

VERIFYING
  ├────────→ VERIFIED
  ├────────→ UNKNOWN
  └────────→ NEEDS_MORE_RESEARCH

A finding is not the same as a verified finding.

First persistence

Before LangGraph, persist the state after meaningful transitions.

Conceptually:

transition(state)
save_state(state)
continue_work()

Kill the process.

Reload the saved state.

Now the application can recover the facts of the run.

But questions remain:

Which function should execute next?
What if a process dies halfway through a function?
Which operations can run again?
Who discovers unfinished work?

Those are durable-execution questions.

Schema evolution

A run may outlive a deployment.

Persist:

schema_version
code_version

When state changes, explicitly classify old runs as:

compatible
requires migration
cannot safely resume

FAILURE LAB 02: False Completion

Inject:

state["requirements"]["pricing"] = {
    "status": "verified",
    "finding": "Pricing available",
    "evidence_ids": [],
}

If the UI/report accepts this, the system has a state-model bug.

Fix the invariant in:

  1. the transition into verified;
  2. an independent report/evaluation check.

Prove it

A test must fail when verified state has no resolvable evidence.

Check your understanding

Answer before moving on. If one is fuzzy, the relevant section is a scroll away.

  1. What is the difference between conversation and execution state?
  2. Why is external reality not identical to application state?
  3. What is an invariant?
  4. Why model progress as transitions?
  5. Why does state schema version matter?

Exit criteria

Observable conditions, not “I understand it”. Check them off; progress is saved in your browser.

  • VendorReviewState persists to disk after every step and a fresh process can load it
  • Reducers are idempotent: the double-apply test passes
  • The operational questions are answerable from state alone, with the process dead
  • no_false_completion exists as code and passes on both fixture vendors