Skip to content

D291 — An agent can carry each client's own restriction, verbatim

Decision. An agent's frontmatter may hold a providers: map keyed by client (opencode, codex, kiro, antigravity). Each entry is copied verbatim into that client's native agent file, after the fields Cartographer writes, in the file's own syntax (YAML for OpenCode and Antigravity, TOML for Codex, JSON for Kiro). Nothing is inferred. A client with an entry counts as having its own restriction: the widened-agent warning (D283) is not printed for it and strict_tools does not skip the agent there. The widened-agent warning now points at providers.<client>. artifact_write rejects an unknown client, a non-map entry, claude (which takes its fields at the top level) and any key Cartographer writes itself; the same check fails the translation at sync.

Why. D283 made the widening visible and opt-out but left an author with only two choices: accept the widened agent or lose it on that client. #447 asked for a third: say what that client's restriction is. The author knows each client's syntax; Cartographer does not, and D55/D195 are right that a guessed mapping could grant a capability the author never gave.

What is and is not verified. Believed to restrict an agent, per the clients' public documentation and not exercised by this repository against the real clients: OpenCode permission (edit/bash/webfetch: allow, ask, deny) and tools in the agent frontmatter; Codex sandbox_mode in the agent TOML; Kiro tools and allowedTools in the agent JSON; Antigravity has no key known to us. D195 verified only Kiro's name/description/prompt. The mechanism makes no claim beyond copying what the author wrote: a key a client ignores restricts nothing, and that is the author's to check. The same statement sits next to the code (internal/provisioning/agentnative.go) and in docs/sync.md.

Alternatives rejected. - A built-in table mapping Claude's tools to each client's keys: still the D55 rejection, and it would need the verification above to exist first. - Whitelisting known native keys per client: it would reject a key a newer client version accepts, and turns an unverified belief into a gate. Only the keys Cartographer writes itself are reserved, because overriding them changes the agent's identity or prompt. - A new providers_strict switch (as worded in #447): D283 already ships strict_tools; a second name for it would be two spellings of one choice. The providers: entry is what scopes it per client. - Warning about a dropped model: kept out, as in D283. A different model is not a wider capability and would warn on nearly every agent. - Parsing providers: with a general YAML reader in internal/okf: the OKF subset has no nested maps. The parser now keeps an indented block verbatim as an okf.Block instead of flattening it into sibling keys (which made a nested tools: look like the agent's own); internal/provisioning interprets it with gopkg.in/yaml.v3, already a dependency.

Consequences. Supersedes the D283 alternative "a per-client providers: block left out" and the "no way to write a native restriction" sentence in D283, D55, D58 and D195 for fields under providers:; their dropping of tools and model as such is unchanged. Any concept frontmatter with an indented nested block now parses as one okf.Block value instead of flattened keys. The lockfile and the content hash are unchanged: the entry is part of the source, so editing it re-syncs the agent. A future verification of a client's keys belongs next to its cell in destinationMatrix and replaces the "believed" wording here.