Before the call becomes an action

N17Q inserted parsing, normalization, policy, approval, effect identity, and receipts between model output and consequence, making tool use a state machine instead of a direct function call.

The model emitted a valid tool call and N17Q did nothing.

The request entered the trace, parsed successfully, and targeted a real capability. Its repository revision was stale, its effect budget was exhausted, and an earlier attempt with the same consequence remained unknown.

Calling the adapter would have been the mistake.

In an agent system, a tool call is model output shaped like a request. It becomes an action only after the product establishes what it means and whether it may happen.

The direct path hid every decision

The first prototype routed provider tool events into adapter functions. Schema validation occurred, exceptions returned to the model, and successful results reentered context.

This path conflated proposal, authorization, execution, and observation. A provider-generated call ID became accidental workflow identity. Retry logic lived inside generic error handling.

The code was small because the product had not represented the states it still experienced.

N17Q expanded the boundary into explicit transitions.

Candidate state preserved model output

When a provider emitted a complete tool item, N17Q stored candidate identity, run and turn, provider item, offered-catalogue manifest, raw safe arguments, time, and target checkpoint.

No adapter existed in this transition. Malformed or unavailable tool names became typed rejected candidates and remained useful evaluation evidence.

The model could be assessed for what it proposed without making every proposal executable.

Observation preceded interpretation.

The product schema rejected missing fields, unknown properties, oversized values, and invalid primitive forms. It produced a typed candidate.

Parsing did not establish resource ownership, current revision, data eligibility, budget, approval, idempotency, or effect safety.

Error responses named fields and expected shape without exposing hidden capabilities. Correcting syntax created another candidate linked to the first.

A valid object was only ready for semantic work.

Normalization established consequence

Adapters resolved aliases, relative paths, defaults, formats, and opaque handles into canonical product arguments. Traversal, ambiguity, stale handles, and locale-dependent interpretations failed.

The normalized request named exact resource revisions, artifact digests, destination, and effect class. A versioned serializer produced its digest.

Approval, policy, idempotency, and receipts all used this representation. Raw spelling never decided sameness.

Normalization turned model intent into something the product could inspect.

A provider or MCP server might call a tool write, publish, or run. N17Q mapped reviewed remote candidates into narrower product capabilities.

The mapping declared effect semantics, data flow, limits, recovery, replay, and remote translation. Unknown discovered tools stayed disabled.

The model selected a product-shaped capability from the offered set. A manually crafted remote name could not bypass the router.

The application owned the verbs whose consequences it promised.

Policy evaluated current state

The gate saw requester and delegation, run, task, normalized arguments, workspace and world state, tool contract, prior effects, approvals, budgets, and policy revision.

It could allow, deny, require approval, or pause for missing evidence. It never edited arguments and executed the narrower version silently.

The decision became a trace event with bounded reason. An allow at planning time remained historical; execution checked again.

Authority belonged to current relationships, not the tool-call syntax.

For consequential requests, N17Q prepared immutable patch or artifact, deterministic consequence summary, preconditions, recovery limits, and expiry.

The reviewer approved that exact object. The receipt bound normalized digest, intent, checkpoint, policy, mapping, reviewer, and time.

Changing any meaning-bearing field created a new review. Reauthentication did not cover changed bytes.

A vague “continue” message never converted the candidate into permission.

Effect identity existed before invocation

An approved semantic consequence received one durable intent identity and provider idempotency key where supported.

Execution attempts received separate IDs. A process restart or response loss could retry the same intent without creating a second intended effect. Changed arguments required a new intent.

The local transaction activated the intent and wrote the outbox before the adapter ran.

Identity connected approval, attempts, remote references, and recovery.

The final gate was the last local choice

Immediately before adapter invocation, N17Q rechecked authority, receipt, expiry, mapping, budget, prior effects, and world preconditions.

A queued allow did not survive a branch update or policy change automatically. Denial preserved prepared work and safe next actions.

Within the local boundary, effect activation and outbox state stayed coherent. The remote world remained outside and required conditional requests and receipts.

The gate sat where the product could still choose not to act.

Attempt state recorded transport reality

The adapter emitted prepared, sent, acknowledged, streaming, result observed, interrupted, and completed evidence according to its contract.

Timeout after send did not become failed by default. Cancellation stated what was actually known. Provider error text could not choose recovery.

Every attempt linked to one intent and retained raw bounded diagnostics.

Transport details mattered without defining semantic consequence.

A tool result could be malformed, hostile, stale, or inconsistent with world state. N17Q stored the bounded result and validated its product meaning.

Read output became source or workspace observation with provenance. Mutation output became receipt evidence and was reconciled against postconditions. Instruction-like text remained data.

The model received a safe projection. It did not receive authority from the result.

Execution completion and product observation were separate events.

Unknown outcome stopped automatic repetition

If an effect might have occurred and no authoritative receipt arrived, the intent moved to outcome unknown.

The tool contract supplied safe recovery: query by stable identity, wait, inspect manually, compensate later, or stop. Another create attempt was unavailable until evidence justified it.

New approval could not cure uncertainty. A second yes did not prove the first effect absent.

The state machine preserved the world from a convenient inference.

Read-only did not mean ungoverned. A read could expose sensitive data, cross a network, consume cost, or observe a mutable source.

N17Q normalized scope, checked requester and data policy, bounded output, and recorded provenance. Many reads could proceed without human approval.

Replay used fixtures rather than live fallthrough. Context compilation selected only eligible observations.

Learning about the world was a capability too.

Local commands had layered gates

The model proposed executable, arguments, working directory, and intended purpose. Product policy checked task and path. The sandbox enforced actual mounts, process, network, credentials, and resource limits.

A permitted package command whose script attempted network failed at the lower boundary. The trace recorded both decisions.

Output and resulting tree became observations tied to workspace revision.

No one layer assumed the other made commands safe.

A provider could emit parallel calls or one batch request. N17Q normalized each semantic item and applied per-item identity, policy, budget, and receipt.

Homogeneous safe reads could execute concurrently. Consequential items serialized where effect contract required. Partial success retained exact states and never repeated completed items wholesale.

The model's batching preference did not flatten product semantics.

Concurrency followed consequence rather than message shape.

Denial improved the next plan

A denied candidate returned bounded category, persistence, and safe alternatives. Equivalent effects shared lineage across tool names.

The model could narrow scope, use a local source, request approval, reconcile an effect, ask the owner, or stop. It could not argue policy into allow.

Repeated attempts without changed state triggered a progress checkpoint and eventual stop.

Refusal remained both firm and usable.

Candidates, denials, approvals, active intents, attempts, and unknown outcomes lived in structured checkpoint state.

A summary could explain them and could not change them. Tool availability came from current policy. The final gate did not parse prose for permission.

Resuming with another model preserved the exact unresolved consequence.

The state machine outlived the conversation that had proposed the call.

Replay used the same parsing, normalization, policy, and state transitions, then invoked deterministic fixture adapters in a simulated world.

Historical approvals became fixture evidence, not live authority. Missing fixtures failed closed. Counterfactual branches used new run and world identities.

This made it possible to test the full proposal-to-effect path without repeating real consequences.

The boundary was useful because it had remained explicit.

Evaluation graded both proposal and containment

An unsafe request blocked by policy preserved world integrity and remained an agent-quality finding. A safe request executed against the wrong resource was a system failure even if the model chose well.

N17Q linked candidate, decision, attempt, world result, and final account. Hard invariants and qualitative review stayed separate.

No one tool-call accuracy score described the trajectory.

The model and product could improve independently.

Users saw retrieve source, prepare patch, run focused tests, or deliver approved artifact—not raw provider method and JSON.

Review pages showed normalized consequence, current state, effect recovery, and receipt. Technical tool and protocol identities remained inspectable.

Candidate and execution states had clear labels. A streaming tool item never looked like a completed action.

Visual hierarchy reflected the authority boundary.

Proposal lineage prevented quiet substitution

The awkward case was not a plainly denied call. It was a sequence of individually reasonable revisions that slowly changed the consequence.

A model first proposed retrieving a local report, then corrected the path, then requested a generated summary, and finally proposed publishing that summary to an external review space. If the interface showed only the latest tool card, the sequence looked like one call being repaired. In reality, it crossed from read to derivation to disclosure.

N17Q represented every candidate as a node with a relationship to its predecessor. A syntax correction could be marked equivalent after normalization. Narrowing a path became a constrained descendant. Changing the effect class, destination, visibility, or bytes created a new semantic branch. The old denial or approval did not flow across that branch.

This lineage also improved evaluation. I could distinguish a model that repaired invalid syntax from one that kept searching for another spelling of a forbidden action. Both produced several tool calls; only one demonstrated useful adaptation.

The graph was deliberately modest. It did not try to infer the model's hidden intention. It recorded observable proposals and product-defined equivalence. That was enough to prevent a common visual lie: presenting the last request as though it had always been the request.

Tool availability changed with task stage, repository state, requester, data classification, remaining budget, and connection health. Supplying the model a fixed global catalogue made unavailable actions look permanently possible. Constructing the catalogue dynamically without recording it made later analysis speculative.

At each model turn, N17Q stored a digest of the exact product capabilities offered, their schema revisions, and their relevant effect annotations. The full definitions lived in the registry; the trace retained a bounded snapshot reference. Candidate parsing checked against that offered set before consulting the broader registry.

This mattered when a tool disappeared between planning and execution. The proposal could be valid relative to the earlier catalogue and still fail current policy. The trace said both things. It did not rewrite history by calling the model mistaken, and it did not treat yesterday's availability as present authority.

Catalogue construction became part of context compilation. Read tools could appear while research was incomplete. A patch-preparation capability could appear only after a target revision existed. External delivery remained absent until the workflow reached a reviewable artifact and the owner had enabled the connection.

The narrower catalogue reduced accidental proposals, but it was not a security boundary. A crafted candidate still met the same local gates. Progressive disclosure helped the model plan; enforcement protected the world.

Streaming stopped at the candidate boundary

Providers could stream a tool name and arguments over several events. The early prototype rendered these fragments as an action beginning to happen. That created urgency before the product even had a parseable request.

N17Q treated partial tool output as provider transport state. It could display a quiet drafting indicator, but it created no candidate until the item reached a provider-defined terminal boundary and passed size checks. Cancellation during argument generation left a bounded incomplete observation, not a failed action.

The distinction prevented speculative execution techniques from leaking into consequential tools. For safe local search, the system could eventually optimize by preparing resources while arguments streamed, but invocation still waited for a complete normalized candidate and current policy. No external connection received half an identifier because the model appeared likely to finish it.

Streaming errors became easier to explain as well. Invalid text, repeated fields, conflicting deltas, and truncated JSON belonged to the provider adapter. Product validation received one assembled payload or a typed assembly failure. The model could then retry with a clean candidate rather than inheriting a partially mutated application object.

Responsiveness came from honest interface state, not pretending a proposal was already an effect.

The first stop button set an abort signal and labeled every interrupted tool cancelled. That wording was reliable only before invocation.

N17Q made cancellation depend on state. A queued candidate could be withdrawn. A prepared local read could be abandoned. An external attempt that had not crossed its send boundary could be prevented. After possible transmission, the same user gesture requested interruption while the intent moved through contract-specific reconciliation.

The interface showed the difference. Stopped before execution meant no adapter invocation occurred. Interruption requested meant the transport had received a signal but the consequence might continue. Outcome unknown meant the system lacked enough evidence. These phrases were less comforting than a universal Cancelled badge and far more useful.

Tool contracts declared whether interruption was supported, what acknowledgement meant, whether partial output remained valid, and how to query state afterward. The final account included the strongest supported statement. A model summary could not strengthen it to “nothing changed.”

Cancellation therefore followed the same proposal boundary as execution. The user could always ask the system to stop. What the system could honestly promise depended on how far the proposal had travelled into the world.

The boundary made testing smaller, not larger

Adding states initially looked like it would multiply the test surface. It actually replaced a collection of timing-dependent integration tests with composable contracts.

Parser fixtures covered provider assembly and schema errors. Normalization fixtures covered canonical meaning. Policy tables covered current authority. Approval tests compared immutable digests. Adapter fixtures exercised send boundaries, acknowledgements, unknown outcomes, idempotency, and reconciliation. Scenario tests then selected a few important combinations across those seams.

The key invariant was simple: no adapter invocation existed without a candidate, normalized consequence, current allow decision, eligible approval when required, active effect intent for mutations, and available budget. The test harness inspected the trace and simulated world, not only the assistant's last message.

A useful adversarial case generated a perfectly valid call for a tool that had not appeared in the turn catalogue. Another altered one normalized byte after approval. A third lost the response after the simulated world committed. Each failure landed in a different state with a different safe next action.

The explicit boundary made those differences ordinary data. I no longer needed to infer which branch a generic exception probably represented.

In the original fixture, normalization found a stale repository revision, policy found exhausted effect capacity, and the registry found one unknown prior intent.

N17Q denied the create path, offered status reconciliation, and preserved the candidate. No adapter call occurred. The agent stopped with an exact blocker after the query contract proved insufficient.

The model output remained valid evidence of its plan and never became a consequence.

Tools are protocols across responsibility

A direct function call is a useful implementation detail after the product has decided to invoke it. Before that point, a model-generated tool item crosses parsing, meaning, authority, human consent, identity, time, and recovery.

N17Q represented those crossings as state. The extra machinery made denials, retries, unknown outcomes, replay, and audit coherent.

The model can be trusted to propose useful actions without being trusted to authorize them.

The tool call begins a conversation with the product boundary.

Only the boundary can decide whether that proposal becomes something the world must live with.