The shared substrate
This package is a catalogue of reusable contracts, not a promise that every agent imports every module. Typewriter deliberately imports no database channel, while Auditor and Accountant use their separately proven read-only connection instead of the ordinary pool. Five agents, one set of shared components. The rule behind it: anything two agents must agree about lives in exactly one place. Two copies of the document registry, or of the identity comparison, or of the validation rules, is two agents that agree until they do not, and the day they diverge nothing fails - the system just starts giving two answers.What is in it
Four pieces worth their own explanation
checks.py flags, it never fixes
The validation layer reports what is wrong with an extracted record.
It does not correct it.
That is the governance boundary in one sentence.
An agent that silently repairs a value has made an accounting decision nobody reviewed, and the flag that would have told a human about it is gone.
So a shape failure or a semantic failure becomes a flag on the record, the record goes to review as FLAGGED, and a person decides.
model.py is the one model path
Both the Clerk’s extractor and the Typewriter’s proposer go through it.
The Typewriter’s graph deliberately reuses the Clerk’s extractor protocol rather than introducing a second model path with its own authentication, its own cost and its own thing to secure.
Model access is keyless: the SDK path shells out to the local CLI with an OAuth token rather than carrying an API key.
That has one consequence worth knowing, which is recorded on the tracing page: the tracer sees node spans but not token counts.
checkpointer.py is what makes a gate durable
A paused run is a row in Postgres, not an object in memory.
So a container restart does not lose a document waiting at the posting gate, and a resume restores the checkpoint rather than re-running the expensive pipeline from scratch.
action_required.py decides what earns a task
Not every message an agent receives is work.
The human worklist at Audit → Actions is an operator’s to-do list, so a row must be created for real work only.
The top-level Actions run ledger has a separate completeness rule and retains failed and legitimate no-change runs.
Otherwise the tab fills with noise and stops meaning anything.
The rules, in the order they fire:
- an attachment is always an action - handing the Clerk a document is the request to process it;
- empty text is never an action;
- a polite wrapper (“please…”, “can you…”) is stripped, then the remainder is judged as an imperative;
- an informational question is never an action, even when it names a document. “How do I process an invoice?” asks for the procedure; it does not hand over an invoice;
- an imperative work verb aimed at a finance artifact or a demonstrative is an action;
- everything else is conversation.
Design rules the substrate encodes
Never pass raw text between agent steps. Nodes exchange validated typed structures. The model decides when to act; deterministic code controls how the act executes. Only the model-facing nodes are non-deterministic. In the Clerk, onlyclassify and extract use the model.
Everything else - registry loading, master resolution, validation, routing, queueing - is deterministic, so a weak proposal cannot reach the queue marked clean.
Retries are typed and bounded.
Only transient faults are retried: network, timeout, 5xx, rate limit.
Never a permanent shape error, which would just fail again more slowly.
One runtime gotcha that costs an afternoon.
Under the development runtime’s blocking detector, any
async def node doing blocking I/O aborts the run.
Sync nodes are threadpooled and fine, but the model-facing and knowledge-base-facing nodes are async, so their blocking calls are wrapped in a thread.
The production runtime has no such detector, but the event loop still stalls on blocking I/O, so the wrapping is right either way.Where it is enforced
All of it lives inlanggraph_chat/agents/common/.
The guard against a substrate regression is agents/tests/test_integration.py, which loads the real registry, builds the dependencies and compiles the graph.
Graph-logic tests use a deterministic stub instead, so a logic test never depends on a model call.
Related
- The agent org - the five agents that import this
- The SOP store - the knowledge half
- Guardrails and refusals - the scope gates that sit on top
- The 146-document registry - the knowledge base’s largest asset