Skip to content

feat(control): carry structured argv on the operational control contracts #369

Description

@pengfei-threemoonslab

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:

  1. MINIMUM_CONTROL_CONTRACT_VERSION must advance 21 → 23. Per STABILITY.md, the minimum only holds while the union is unchanged.
  2. 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.
  3. 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.
  4. 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

  • control.next_action carries executable[] / args[] with the same computed-projection guarantee as NextAction (ignored on input, recomputed on read, omitted when the command has no faithful argv form).
  • A structured form exists for allowed_next_commands, or the issue explicitly records why the string list stays.
  • verifier.json repairs and fix_task.verification_command follow the same rule.
  • minimum_control_contract_version advances and every pinned literal moves with it.
  • --format codex-boundary-json still validates against the frozen v2 schema.
  • The compact envelope stays within agent_control_budget_bytes.
  • docs/diagnostics.md, docs/errors.json, AGENTS.md, and STABILITY.md drop the "argv is carried on next_actions[] only" scoping.

Metadata

Metadata

Assignees

No one assigned

    Labels

    P2Queued; valuable but not blockingarea:agent-modeAgent-mode contract: control envelope, next actions, command strings

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions