Cognitive units · Call, await, enforce
Call, await, enforce
cognitive unit behave like async functions with governed side effects, call, await a structured judgment, enforce deterministic post-processing. The analogy speeds adoption and clarifies boundaries.
9 min read
Cover for Async functions and the cognitive unitBackend engineers already know how to reason about await fetchUser(id). Inputs are typed. The call returns or throws. Side effects belong elsewhere. cognitive units map cleanly onto that mental model, which matters because most teams will implement them as async functions whether or not anyone writes the theory down.
The analogy is useful precisely where it stops. A cognitive unit returns judgment, not truth. Abstention is success, not exception. And nothing mutates world state inside the call, because re-running is the foundation of every reliability technique you will later apply.
Helpful context: What is a cognitive unit defines the contract. A contract, not a prompt is what callers actually review. Deterministic code beside cognition owns validation and side effects after the await.
What transfers from functions
If you have written asynchronous code, you already possess most of the instincts a cognitive unit requires. A cognitive unit is awaited, with latency measured in seconds, not nanoseconds. It composes: one judgment feeds the next. It parallelises when evenings or invoice lines are independent. It costs per call, metered like any remote service. Retry, timeout, and circuit-breaking vocabulary applies to the machinery layer without modification. That sameness is deliberate. Thoughtware asks you to extend the same boundaries to judgment edges without abandoning software engineering.
The analogy breaks on four promises ordinary functions keep that cognitive units cannot. Same arguments, same answer: call a function twice and get the same result. Call a cognitive unit twice and you may get two reasonable answers from a distribution. Evaluation becomes statistical. Debugging requires recording, not reproducing. Reliability becomes something you buy rather than assume. Local implementation: you own function bodies. You do not own the substrate that interprets templates. Silent vendor changes are realistic hazards, which calls for version requests, response monitoring, and scheduled remeasurement. Bounded domain: typed functions refuse bad arguments. A cognitive unit refuses nothing unless you build refusal. Empty policy documents still produce fluent findings with invented citations unless abstention is constructed. See when the system should refuse. Loud failure: a broken function throws. A broken cognitive unit returns an answer, fluent, correctly shaped, plausible, wrong. The absence of an error is not evidence of success, and the exception-handling reflex is insufficient. Evaluation and structured returns replace it.
The call pattern
The discipline is one sentence: things about the case are returned, things about the machinery are thrown.
| Situation | Response | About |
|---|---|---|
| Insufficient information | Abstain | The case |
| Decision not mine to make | Refer | The case |
| Timeout, rate limit, service down | Throw | The machinery |
| Verdict with grounds | Answer | The case |
Callers branch on abstention and referral, fetch missing fields, escalate to a person, or proceed. Retries after infrastructure failure belong to the runtime, invisible to business logic. Retries because the answer was not good enough belong to the caller, who alone knows whether another draw is worth the price.
const judgment = await assessMealPracticality({
candidateMeal,
eveningContext,
}).
if (judgment.kind === "abstain") {
return askTargetedQuestion({ gap: judgment.missing }).
}
if (judgment.kind === "refer") {
return escalate({ to: judgment.to, why: judgment.why }).
}
const patch = applyPracticalityFinding(plan, judgment).
await deterministicApplyPatch(patch). // schema, allergy, audit
The cognitive unit proposes. Code validates and applies. The agent decides when to call, not what four portions means, not whether purchase is permitted.
The Meal Planning Agent's strategy loop is ordinary control flow: interpret, generate candidates, assess evenings, compose, critique, patch. Each await hits a named judgment edge. Retry, cache, and sampling wrap specific calls, not the whole loop in one opaque function. That structure is what makes a cognitive unit is one call arguable in design review: each await is one price line, one suite, one owner. Reading the template is not reading the behaviour. Why you cannot read a cognitive unit applies even when the surface looks like ordinary code.
Retries, composition, and branches
Retries divide cleanly, and teams blur them at their cost. Machinery retries follow rate limits, timeouts, and transient service failures. They belong in the runtime wrapper: bounded, logged, invisible to business logic. Nobody writing meal-plan orchestration thinks about HTTP 429 handling inside the practicality template. Case retries happen because the answer was not good enough. Only the caller knows whether another draw is worth the price. Bulk invoice screening skips expensive consensus. A held supplier payment retries three times and takes the majority verdict, possibly with a critic cognitive unit afterward. Lumping these together produces systems that are simultaneously fragile and expensive: retrying every flaky verdict on forty thousand rows, or failing a payment hold because the runtime exhausted machinery retries on a quality problem.
When Tuesday and Thursday practicality assessments are independent, parallel await calls reduce wall-clock time without merging decisions. Each result still binds to its own suite, cache key, and owner. Parallelism is an orchestration concern, not permission to hide two open decisions in one template because "we parallelise internally." Composite cognitive units at purity level one call declared callees, and they do not smuggle undeclared second judgments inside one identity.
Teams reach for scalar confidence when abstention and referral already encode the useful distinction. A number below threshold does not tell you whether to fetch missing calendar fields, escalate to a person, or sample three times. Abstention says fetch. Referral says escalate. Answer with grounds says proceed and contest. Branches in business logic are the right control plane. Confidence stays in analytics if needed, never as the sole decision mechanism.
Mechanically, a call substitutes values into a template and hands the result to an interpreter, construction of a program at run time followed by evaluation. That closeness explains why input discipline matters: content you do not control must not land in the instruction position. Contracts declare holes. Deterministic code sanitises grants. The async shape is familiar. The injection risk is not.
The cognitive unit · Ch. 4A broken cognitive unit returns an answer that is fluent, correctly shaped, plausible, and wrong.
Engineering the await boundary
unit cost in the software sense still apply to orchestration: given a fixture judgment, does the agent apply the patch correctly? Does deterministic code reject schema violations? Integration tests invoke cognitive units with recorded fixtures while mocking substrate responses, because live model calls are non-deterministic. Contract tests assert return shapes: abstain includes missing, refer includes to and why, answer includes typed grounds. Shape-only tests are insufficient for content, but necessary to prevent silent type drift.
Logging records call arguments, policy overrides, and structured returns. Traces stay out of audit evidence. Observability supports debugging. Grounds support contest and compliance. The async wrapper is the right place to attach correlation IDs linking agent loop steps to cognitive unit invocations.
Two retry budgets belong in runbooks: infrastructure retry budget (milliseconds, exponential backoff) and case retry budget (dollars, consensus thresholds). On-call playbooks that conflate them debug the wrong layer when payments stall or invoices flood. Callers import cognitive unit contracts from a stable module, types, names, expected branches, not template strings. The async entry point is the public API, and templates are private to the identity ring.
Treat cognitive unit substrate like any unreliable remote dependency: timeouts, circuit breakers, bulkheads between bulk screening and payment holds. The async metaphor extends to operational concerns, beyond code shape. Record arguments and structured returns for replay when debugging distribution-shaped failures. Load tests meter cognitive unit calls separately from orchestration overhead. A slow agent loop may be N awaits multiplied by latency, and splitting compounds reduces unnecessary sequential awaits while making parallelism visible in perf reports. Fake substrate responses in tests let orchestration logic run in CI without live model calls. Stubs attach to the await boundary, the same seam production uses, keeping tests fast and contracts honest.
The await boundary is where software engineering meets judgment engineering. Guard it accordingly.
What to do next
Each open judgment becomes one exported async entry with a contract file beside it. World writes stay in deterministic helpers the agent calls after validation. Policy overrides log at the call site, including model choice and sampling. Business logic branches on abstain and refer instead of treating them as generic errors. A second open decision does not hide inside one template "because the await is once."
See a cognitive unit is one call and deterministic code beside cognition.
Read next: The cognitive unit in the system.