Summary
#322 (PR #367, contract v23) gave next_actions[] entries with kind="command" a shell-independent executable[] / args[] pair, computed from command so the two cannot disagree. The operational control contracts still publish the command string alone:
control.next_action.command (CodingAgentCommandAction)
control.allowed_next_commands[]
verifier.json repairs (VerifierRepair.command)
fix_task.verification_command
PreflightNextAction.command, PreflightSignalV1.related_command
AgentResultNextAction.command
A consumer of those must recover argv themselves. That is well-defined today — every emitted command is POSIX-rendered on every platform, so shlex.split(command) is exact, and tests/test_invocation_policy.py::test_every_control_surface_command_recovers_exact_argv pins it — but it is a step the primary routing surface should not require, and it is easy for a consumer to reach for shell=True instead.
Why it was not folded into #367
Adding the pair to CodingAgentCommandAction changes the AgentControl union itself, which triggers a cascade that deserves its own review rather than a fourth round on an already-long PR:
MINIMUM_CONTROL_CONTRACT_VERSION must advance 21 → 23. Per STABILITY.md, the minimum only holds while the union is unchanged.
- Raising the minimum cascades to
.well-known/agents-shipgate.json, README ×2, docs/agents/use-with-codex.md ×3, every adoption-kit literal via the render-hash protocol, and the pinned test literals.
docs/codex-boundary-result-schema.v2.json is frozen (excluded from scripts/generate_schemas.py, additionalProperties: false). CodexBoundaryResultV2 inherits AgentResultV2 and shares the same control, so any new field on the union makes check --format codex-boundary-json violate its own published schema. It needs a down-projection at the serialization boundary — not a @model_serializer, which would leak the downgraded control into the current format through build_agent_boundary_result.
- The compact envelope has a published
agent_control_budget_bytes of 4096 and currently runs ~2.2 KB, so the pair fits — but allowed_next_commands is a list[ExactCommand] (plain strings), and giving it structured siblings is a shape change rather than an addition.
Acceptance criteria
Summary
#322(PR #367, contract v23) gavenext_actions[]entries withkind="command"a shell-independentexecutable[]/args[]pair, computed fromcommandso the two cannot disagree. The operational control contracts still publish the command string alone:control.next_action.command(CodingAgentCommandAction)control.allowed_next_commands[]verifier.jsonrepairs (VerifierRepair.command)fix_task.verification_commandPreflightNextAction.command,PreflightSignalV1.related_commandAgentResultNextAction.commandA consumer of those must recover argv themselves. That is well-defined today — every emitted command is POSIX-rendered on every platform, so
shlex.split(command)is exact, andtests/test_invocation_policy.py::test_every_control_surface_command_recovers_exact_argvpins it — but it is a step the primary routing surface should not require, and it is easy for a consumer to reach forshell=Trueinstead.Why it was not folded into #367
Adding the pair to
CodingAgentCommandActionchanges theAgentControlunion itself, which triggers a cascade that deserves its own review rather than a fourth round on an already-long PR:MINIMUM_CONTROL_CONTRACT_VERSIONmust advance21 → 23. PerSTABILITY.md, the minimum only holds while the union is unchanged..well-known/agents-shipgate.json, README ×2,docs/agents/use-with-codex.md×3, every adoption-kit literal via the render-hash protocol, and the pinned test literals.docs/codex-boundary-result-schema.v2.jsonis frozen (excluded fromscripts/generate_schemas.py,additionalProperties: false).CodexBoundaryResultV2inheritsAgentResultV2and shares the samecontrol, so any new field on the union makescheck --format codex-boundary-jsonviolate its own published schema. It needs a down-projection at the serialization boundary — not a@model_serializer, which would leak the downgraded control into the current format throughbuild_agent_boundary_result.agent_control_budget_bytesof 4096 and currently runs ~2.2 KB, so the pair fits — butallowed_next_commandsis alist[ExactCommand](plain strings), and giving it structured siblings is a shape change rather than an addition.Acceptance criteria
control.next_actioncarriesexecutable[]/args[]with the same computed-projection guarantee asNextAction(ignored on input, recomputed on read, omitted when the command has no faithful argv form).allowed_next_commands, or the issue explicitly records why the string list stays.verifier.jsonrepairs andfix_task.verification_commandfollow the same rule.minimum_control_contract_versionadvances and every pinned literal moves with it.--format codex-boundary-jsonstill validates against the frozenv2schema.agent_control_budget_bytes.docs/diagnostics.md,docs/errors.json,AGENTS.md, andSTABILITY.mddrop the "argv is carried onnext_actions[]only" scoping.