Development, delivery and runtime architecture / Field essay 22

Port agent controls across runtimes without assuming hook parity.

Different CLIs, IDEs and operating systems expose different observation and denial points. Port the contract, then prove each runtime route.

Published

ARCHITECTURE
FIELD ESSAY 22 / DEVELOPMENT, DELIVERY AND RUNTIME ARCHITECTURE

Port agent controls across runtimes without assuming hook parity

A guard can exist on disk, appear in configuration and still provide no control over the route an agent is using.

Those are separate facts. The runtime must load the guard, trust it where required, match the relevant event and tool, interpret its response as intended and act on that decision before the effect occurs. A service may still need its own authorization check after all of that.

I built our route-census work around those distinctions. Instead of asking whether the fleet has hooks, I want to know which control stands in front of each effective route. The output is an authority map with evidence attached, rather than a shared configuration file that looks reassuring across several tools.

For this essay, I replayed the pinned census's original owning suite: 96 checks passed. Changing its copied implementation to call missing controls covered made the same detector fail 14 checks. A separate source-bound exercise covers 47 cases around matching, live flags, required controls and runtime-specific rows. Those are concrete tests of the census model, with live host interception kept as a separate obligation.

Port the obligation before the hook syntax

The requirement usually exists above any one agent runtime. Do not modify a protected path without the required authority. Do not send a message through an unapproved channel. Do not treat a lost reply as proof an effect never happened. Preserve the original target identity through delegation.

A particular hook format is one place to implement part of that requirement. It is not the requirement itself. Copying the same script and configuration into another CLI may preserve the text while changing when the script runs, what arguments it receives and which responses can stop an action.

I start with the obligation, affected resource, actor identity and decisive effect. Then I identify which component can enforce it. A local runtime adapter might reject a tool invocation. The operating system might constrain filesystem or network access. A broker might hold scoped credentials. The destination service might evaluate the current grant at the final effect boundary.

That map survives a change of coding tool better than a collection of vendor-specific reminders. It also reveals which behavior the new runtime must demonstrate before it can take on the same responsibility.

Keep observation, denial, approval and execution separate

An event can tell an integration that something happened without granting it authority to stop the action. A callback that can stop a supported invocation may still have no way to obtain a valid approval. An approved request still needs a component that performs the effect under the intended identity.

I normalize the evidence those roles exchange, but I keep their capabilities explicit. The adapter should know which events are observational, which decision forms the host accepts, how approval is obtained and which executor ultimately holds the capability.

This is where a universal decision enum becomes dangerous. A shared word such as ask can conceal different semantics across hosts or event types. If the target cannot represent the required outcome, the integration needs an explicit unsupported state and an approved alternative owner. Translating an unrepresentable decision into allow would change the control contract.

The same caution applies after execution. A post-action hook can contribute audit evidence or prevent a result from flowing farther, depending on the host. It cannot undo an irreversible effect simply by describing that effect as blocked. The observation point must stay attached to the conclusion it supports.

Inventory routes the agent can use

A list of tools the agent called yesterday is a useful starting point. It is not a complete map of what it can invoke today.

The census design includes tools offered to a session, tools actually called and configured MCP servers that have not appeared in transcripts yet. The latter enter the inventory as unknown placeholders. That makes new capability visible before the first successful use provides a trace.

The route also includes its execution surface. A local shell invocation, a hosted tool, a remote worker, a browser action and input sent to an existing process may cross different observation points. A single tool name cannot establish that they share the same control path.

I would record runtime and version, tool identity, available payload fields, source of configuration, control owner, failure behavior and effect destination. For a long-running process, I would also identify which later inputs can change its behavior after initial admission. That is the unit of portability: the effective route, not the marketing category of the agent.

Treat installed, live and matched as different evidence

The static coverage function accepts matching hook records and their live flags. A hook explicitly marked not live cannot supply a required control. A known script name supplies only the controls listed in the census's own guard table.

That is already more useful than counting installed scripts. A disabled registration or an untrusted hook cannot contribute coverage merely because its file exists. A matching logger cannot be assumed to provide a deletion guard. A primary control cannot silently satisfy a second required control just because both concern the same command.

The retained implementation also has an input convention worth making explicit: a hook record without a live field defaults to live. That is how this static model handles its inputs. It is not proof that the real runtime loaded or trusted the hook. The collector supplying those records has to establish those facts.

I want the inventory and its evidence to preserve that distinction. Configuration discovery can identify what should load. Runtime observation can establish what did load. A behavioral probe can show what a known invocation caused the host to do. None should replace the next step by changing the label alone.

Require every control the route needs

A route may have more than one obligation. In the extracted census, selected tools require the exam control in addition to the primary control assigned by policy. The static function checks the full set against the matching live guards.

The central loop is small:

    missing = []
    for c in controls:
        if not any(h.get("guard") and c in GUARD_CONTROLS.get(h["guard"], ()) for h in live):
            missing.append(c)
    if not missing:
        return "COVERED", []
    if any(not h.get("guard") for h in live):
        return "UNVERIFIED", missing
    return "UNCOVERED", missing

The useful property is conjunction. Every required control must be accounted for. A connector guard that supplies the primary check does not also become the exam guard. An unknown matching hook leaves missing obligations unverified instead of automatically satisfying them.

The function still trusts the control table and its input records. It does not authenticate script contents, compare installed versions or prove the hook actually refuses a forbidden action. I use COVERED as the software's static status, and require separate behavioral evidence before describing an effective route as enforced.

Preserve residual and target-side responsibilities

Some routes do not fit a pre-tool decision oracle. A coordinate click, for example, may not expose enough structured target information for a local callback to decide what the click will do. Calling that route covered because a callback observed the coordinates would overstate the available evidence.

The census has a residual category for such cases. It also has a target-side category where the service itself checks the grant. Those labels preserve the owner of the actual decision rather than forcing every route into a local-hook story.

A target-side label is not a universal safety certificate. The service still needs to bind the grant to the intended caller, resource, operation and current state. A residual label needs a concrete operating procedure, such as an independent state readback, narrower credentials or a different execution surface with a structured contract.

For operations, this is useful specificity. An operator can see whether the next improvement belongs in the host adapter, the broker, the destination service or the acceptance observer. A single overall compliance badge would hide those choices.

Use matchers as versioned protocol behavior

Our pinned matcher helper encodes the rules the census researched for its original host snapshot: a simple exact-name list uses one interpretation, while expressions outside that restricted form use an unanchored regular-expression search.

The exercise checks exact names, list separators, anchored and unanchored patterns and malformed expressions. Those cases show what the retained helper does. They do not establish that Python's regular-expression engine implements every JavaScript expression or that another host uses the same matching rules.

That matters when tools gain prefixes, plugins rename routes or the runtime introduces aliases. A matcher written against one visible name may miss the effective name another integration receives. I prefer a small supported matching vocabulary with explicit host tests over an ambitious promise to emulate every expression language.

A portable adapter should retain both identities when needed: the original host event and the normalized control event. Normalization helps policy code compare obligations; the original record lets an operator explain why a route matched or did not match under that host's actual rules.

Check current host behavior instead of promoting old research

The September host-adapter research remains valuable as a dated record of what we investigated. I do not use it as a current feature-parity table.

For a current example, the official Codex hook documentation distinguishes supported local tool paths from hosted-tool exceptions and says input to an existing unified-exec session does not trigger another PreToolUse. That is a documented route distinction, not a live probe of this installation. Official Codex hook coverage.

The operating consequence is broader than that example. A runtime update can add an event, change a payload or reject a formerly meaningful decision shape. The adapter's supported contract should be pinned to the version and configuration being accepted. Revalidation needs to include the paths that do not pass through the expected callback.

I would not require every host to expose identical features. I would require it to make its available control points and residual obligations explicit, so the surrounding architecture can choose an appropriate owner for each effect.

Probe the actual decision path with harmless inputs

A useful canary is specific to the control. A script intended to reject an unauthorized deletion should see a harmless representation of that forbidden request. A path guard should see the protected-path condition it is meant to reject. A generic payload sent to every hook can create both false confidence and false alarms.

The owning census fixtures give synthetic event input to copied hook programs. They never execute the proposed destructive payload. They can therefore observe whether the copied guard returns the expected decision under those fixture conditions, including several broken-guard variants.

That is one level of evidence. A live host acceptance test would additionally need to show that the runtime actually invokes the hook for the intended route, interprets the result correctly and prevents the harmless forbidden effect. Independent readback should inspect the target state rather than trusting only the callback's output.

I keep those levels separate. The copied-hook test qualifies the detector's model and fixture behavior. The runtime test qualifies interception and interpretation. The destination test qualifies the final authority boundary. Together they can support an operational claim that no single one supplies alone.

Test the detector by breaking its assumption

The original owning suite completed with 96 checks passed and zero failed in the pinned scratch replay. It includes fixtures for missing route coverage, an allowing guard, broken guard behavior, configuration scope and process cleanup around a hanging probe.

I then used its built-in missing-hook-covered mutation. The mutant changed the census so a missing control looked covered. The suite exited one, with 81 checks passed and 14 failed. The mutant run omits one control-only fixture by design, so those totals are not a before-and-after performance comparison.

This is a negative control for the detector. It demonstrates that the owning fixture can reject that specific false-coverage behavior. It does not measure production coverage or the reliability of a fleet. The separate 47-case exercise adds focused observations of the extracted static functions, with three author fault controls and a restored clean run.

Independent mutation review asks another question: which failures did the author fail to imagine? I keep that result separate from both the original suite's built-in controls and the cases I wrote for this article.

Choose the shared layer deliberately

There are three credible ways to organize the work. Separate scripts for each host make local semantics straightforward but let obligations drift between implementations. One universal hook adapter reduces duplication but can erase differences in authority. A shared policy contract with explicit host capabilities preserves common requirements while keeping translation accountable.

I favor the third shape. The shared layer describes the obligation, required evidence and effect identity. The adapter maps actual events and supported decisions. The executor and destination service retain the controls they can enforce independently of the host. Acceptance records what was tested and what remains residual.

That has a maintenance cost. Someone owns the runtime matrix, version updates, harmless canaries and the evidence expiration policy. It is more work than distributing one configuration file. It is also a clearer operating commitment: adding a new coding tool adds a known adapter and acceptance task instead of silently expanding the authority surface.

Make portability an acceptance decision

Before promoting a new runtime route, I would ask four questions. Which obligation must survive? Which component has the authority to enforce it here? What harmless probe can show that the effective path is controlled? What happens when that component fails or the result becomes uncertain?

The answer should produce an evidence row, not a paragraph saying the runtime supports hooks. Retain the host and adapter versions, configuration scope, tool route, required controls, observed result and the owner of any residual condition. Give evidence a validity boundary so a runtime or policy update cannot inherit an old assurance silently.

For CTOs, this makes tool choice more flexible. The team can adopt a better model or harness while preserving the business controls in a form the next integration can demonstrate. The decision is about which responsibilities the new route can accept and which remain elsewhere.

Start with one route your team relies on today. Follow it from the offered capability to the final effect, including later process input or remote delegation. Then break the control harmlessly and require the detector to notice. That gives the portability discussion an observable foundation.

How this was verified

Seven complete constants and definitions are retained verbatim from Hawkeye commit de8ac2f0a441e7508b032f067b9266c628143c7b, with line-level hashes. The source-only packet at 4b15b57 preceded the 47-case author exercise. Three syntax-valid author mutations fail assertions and the restored exercise exits zero. The initial independent review caught 22 of 27 mutations with a clean control. Five added cases exercise suffix regex matching, the exact-name/regex split, the Write secondary control, distinct guard capabilities and wildcard probe substitution. The expanded 52-case exercise caught the same 27 of 27 with control exit zero and unchanged source. This is a same-set follow-up, not a fresh independent score; both receipts remain retained.

The original owning suite ran from immutable copied source with synthetic HOME, configuration, transcripts and guards: 96 passed, zero failed. Its built-in missing-hook-covered negative control produced 81 passed, 14 failed and exit one. Proposed canary effects were never executed. These are census-model and copied-guard fixture results, not live interception or current fleet-coverage measurements.

The September adapter note is retained as dated context. Current Codex documentation was fetched on 20 October 2026 for the narrow route-coverage distinction cited above. No current cross-provider protocol parity, arbitrary JavaScript-regex equivalence, authentic installed trust, full mediation or complete sandboxing is established by this packet.

CONNECTED READINGForward and back
Keep reading

The answer deserves
another experiment.

Back to the journal