SikiT

Field notes on running AI agents without a human in the loop.

Field notes on unattended AI agents.

4–6 minutes

A delegation wrapper earns its keep by refusing flag combinations

A CLI validates flags one at a time; nothing validates the combination. After a read-only run wrote to disk because a second flag outranked the first, the wrapper became the place where flag pairs carry intent, with an…

A table of four wrapper obligations, each paired with the measured incident that motivated it, from a read-only run that wrote to disk to timings quantized to thirty seconds.

Sources are linked in this article. Found an error? Report a correction.

A CLI will reject an invalid flag value and accept any combination of valid ones. That division of labor leaves a gap: single flags carry settings, but intent lives in combinations, and no layer checks those. The gap became concrete when a review run configured as read-only wrote a file to disk, because a permission flag on the same command line quietly outranked the sandbox flag. The binary behaved as designed. What was missing was a layer that knew what the operator meant, and the delegation wrapper is the only place that layer can live.

On this page

Thesis: the CLI validates values, so the wrapper must validate intent

A wrapper that only forwards arguments is a convenience. A wrapper that encodes which combinations are allowed to reach the binary is a policy boundary, and it is the correct home for that policy because it is the one layer that knows why a flag was set. The vendor cannot know that read-only in this operation means a review that must not write. The wrapper can, because the operator told it so by choosing that flag.

Evidence: four obligations, each bought by an incident

The wrapper's eight-case flag resolution table passing in isolation, the converted trap value, and the platform-adjusted sandbox profile behind a stable name.
The resolution function under test, and the conversions it applies.

Bind the pairs that carry intent

The founding incident: with the sandbox set to read-only and the permission mode at its permissive default, the agent created a file and reported it, accurately. Lowering the permission mode blocked the same write. The vendor’s documentation describes the sandbox as limiting "what an approved call can do," which is the model the flags’ names suggest and not the behavior the machine produced.

The wrapper now resolves the pair before the binary sees it. A restrictive sandbox with an unstated permission mode downgrades the mode to one that enforces the restriction. An explicitly permissive choice is honored with a warning, because overriding a stated decision silently would be the same sin in the other direction. The rule fits in a dozen lines, and an eight-case table exercises it:

OK  -s read-only  bypassPermissions  explicit=0  ->  default

OK  -s strict     bypassPermissions  explicit=0  ->  default
OK  -s read-only  bypassPermissions  explicit=1  ->  bypassPermissions +WARN
OK  -s read-only  default            explicit=1  ->  default

Convert the values that are traps

One permission mode looks purpose-built for automation, promising not to interrupt with questions. Measured in a non-interactive run, it ends the turn at the first gated action with a clean exit and a sentence of intent. The wrapper converts that mode away and says so on stderr. Refusing outright would also be defensible; what is not defensible is forwarding a value whose measured behavior contradicts its advertised purpose.

Absorb the platform underneath stable names

On this machine the sandbox profile sometimes cannot start at all, because a system path the profile must resolve is a symlink and the binary refuses to run "with its protections missing." That refusal is correct and fail-closed, and it is also a platform accident the operator should not meet mid-operation. The wrapper maps the friendly profile names to platform-adjusted equivalents that resolve, so the caller’s vocabulary stays stable while the workaround lives in one audited place.

Own the observability you introduce

The wrapper polls its child on a thirty-second heartbeat, and a timing study run through it produced four identical thirty-second measurements for workloads that vary by a factor of two when timed directly. A wrapper is part of the instrument. Its contract has to include what it does to observations: report the effective flags it resolved, and either pass timing through honestly or state its granularity.

Counterargument: put the policy in configuration instead

The CLI has a configuration file, and a default permission mode set there would survive individual invocations. Three things argue against relying on it.

Configuration states values, not conditions. The rule that emerged from the incident is conditional: this mode, when that sandbox, unless explicitly overridden. A key-value default cannot express it, and flattening it to the strictest value everywhere taxes the runs that legitimately need permissive modes.

Configuration lives with the installation. Upgrades, reinstalls, and second machines reset or fork it silently. The wrapper lives in the operation’s repository, versioned next to the operation’s other policy, which is where a rule bought by an incident belongs.

Precedence between configuration and command line is one more undocumented interaction. The whole lesson of the founding incident is that composition semantics are where surprises live. Adding a third layer of composition to fix a two-layer surprise is the wrong direction.

The stronger counterargument is that wrappers rot and hide the real interface. Both are real, and both are manageable at this scale: the decision function is a dozen lines with an eight-case test, and the wrapper logs the exact command it resolved. A wrapper that cannot show its work has outgrown its mandate.

Implications: the contract, written down

Four obligations make a passthrough script into a policy boundary. Resolve flag pairs so stated intent is enforced, and warn only when overriding an explicit choice, never when honoring an implicit one. Convert or refuse values whose measured behavior contradicts their advertised purpose. Absorb platform variance behind names the operator can keep using. Declare what the wrapper itself does to observations, from effective flags to timing granularity.

None of this required vendor cooperation, and all of it came from measurements small enough to run in an afternoon. The binary keeps validating values. The wrapper validates the sentence the values spell.

Sources

  • Permissions — xAI developer documentation; the "approved call" phrasing quoted above
  • CLI reference — xAI developer documentation for the flags involved

The founding incident, the conversion behavior, the startup refusal, and the heartbeat quantization are direct measurements from 2026-08-27 through 2026-08-31; the decision table output is reproduced verbatim from the isolated test of the wrapper’s resolution function.

Related articles

Stay in the loop

Get new agent guides with their test conditions and limitations. Unsubscribe anytime.

Comments

Questions, corrections, and useful counterpoints are welcome. Keep comments specific and on topic.

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Thanks for commenting

Get new agent guides with their test conditions and limitations. Unsubscribe anytime.

Return to the comments