Skip to main content

Gate Actions

gate_actions let a human-approved gate run repo-local automation before the approval is finalized. This is the narrow bridge between "the gate is approved" and "the repo actually did the governed follow-up work."

Use it for explicit, post-approval actions such as release wrappers, deploy wrappers, or repo-owned verification scripts that should run only after a human signs off.

This is not a second workflow-kit config tree. The contract lives directly on the gate definition itself under gates.<gate_id>.

Config shape

agentxchain.json
{
"gates": {
"qa_ship_verdict": {
"requires_human_approval": true,
"gate_actions": [
{
"label": "publish npm package",
"run": "bash scripts/release/publish-npm-if-needed.sh",
"timeout_ms": 900000
},
{
"label": "sync homebrew formula",
"run": "bash scripts/release/sync-homebrew-if-needed.sh",
"timeout_ms": 900000
}
]
}
}
}

Real validation rules

  • gate_actions is valid only on gates.<gate_id>.
  • The gate must set requires_human_approval: true.
  • gate_actions must be a non-empty array when present.
  • Every action needs a non-empty run string.
  • label is optional, but when present it must be a non-empty string.
  • timeout_ms is optional, but when present it must be an integer between 1000 and 3600000.

If you put gate_actions on a non-human gate, config validation rejects it. That is intentional. The shipped feature is a post-approval boundary, not a general automation runner.

Execution model

When an operator runs agentxchain approve-transition or agentxchain approve-completion, the runtime applies this order:

  1. The pending human gate is identified.
  2. before_gate hooks run first.
  3. Gate actions execute sequentially in the repo root via /bin/sh -lc.
  4. Each action uses its configured timeout_ms, or the runtime default of 900000ms (15 minutes) when no override is provided.
  5. The approval finalizes only after all gate actions succeed.

That last point matters. AgentXchain does not mark the gate complete and then hope the automation worked. If a gate action fails, the approval is not finalized.

Workspace boundary

Gate actions run in the real repo root with normal workspace write access. They are not isolated in a scratch checkout or temp directory.

That is deliberate. Release and deploy wrappers often need the real repo state, tags, build outputs, or generated metadata. A generic dirty-workspace ban would break valid post-approval automation.

The burden moves to the operator instead:

  • treat workspace writes as part of the approval boundary
  • make those writes intentional and rerunnable
  • prefer repo-owned wrappers that can detect already-completed work and exit cleanly on retry

If a gate action writes files you did not intend to carry forward, that is not a runtime mystery. It is a weak gate-action contract.

Timeout boundary

Hung gate actions are not acceptable. Every action gets a per-action timeout:

  • explicit timeout_ms when you set it
  • otherwise the runtime default of 900000ms (15 minutes)

If an action exceeds that limit, AgentXchain treats it as a failed gate action, blocks the run, preserves the pending gate, and records timeout evidence in .agentxchain/decision-ledger.jsonl.

For release or deploy wrappers, set an explicit timeout instead of relying on the default when you know the expected duration:

agentxchain.json
{
"gates": {
"release_publish": {
"requires_human_approval": true,
"gate_actions": [
{
"label": "deploy docs",
"run": "bash scripts/release/deploy-docs-if-needed.sh",
"timeout_ms": 1800000
}
]
}
}
}

Dry-run

Both approval commands support preview mode:

agentxchain approve-transition --dry-run
agentxchain approve-completion --dry-run

--dry-run shows the planned gate actions without executing hooks, actions, or state mutation. Use it when you want to inspect the exact release/deploy follow-up before crossing the approval boundary.

Failure and retry model

If any action fails:

  • the run becomes blocked
  • typed_reason becomes gate_action_failed
  • the pending gate stays intact
  • the recovery path remains the same approval command

That means the configured commands must be rerunnable. Do not treat raw one-shot commands as safe defaults. npm version patch, ad hoc git tag, or other non-idempotent shell snippets are bad examples because a partial execution makes retry behavior ambiguous.

Prefer repo-owned wrapper scripts that can detect already-completed steps and exit cleanly on rerun:

bash scripts/release/publish-npm-if-needed.sh
bash scripts/release/sync-homebrew-if-needed.sh
bash scripts/release/deploy-docs-if-needed.sh

If your automation cannot answer "what happens when I run the same approval command again after a partial failure?", the contract is too weak.

Environment available to actions

AgentXchain exposes these environment variables to each gate-action command:

  • AGENTXCHAIN_GATE_ID
  • AGENTXCHAIN_GATE_TYPE
  • AGENTXCHAIN_PHASE
  • AGENTXCHAIN_REQUESTED_BY_TURN
  • AGENTXCHAIN_TRIGGER_COMMAND

Use them in repo-owned wrappers when the script needs to log gate context or branch on approval type.

Evidence surface

Every executed action appends a type: "gate_action" entry to .agentxchain/decision-ledger.jsonl.

Recorded evidence includes:

  • gate id and gate type
  • phase and requesting turn
  • approval attempt id
  • action index and label
  • command
  • timeout configuration
  • status
  • exit code or signal
  • stdout/stderr tail
  • timestamp

That evidence is then surfaced through:

  • agentxchain status — latest gate-action attempt for the pending gate
  • agentxchain report — gate-action evidence in the governed report
  • agentxchain audit — gate-action evidence in the live audit surface

Worked example: release approval gate

This is the right shape for a release gate:

agentxchain.json
{
"gates": {
"release_publish": {
"requires_human_approval": true,
"gate_actions": [
{
"label": "publish npm if this version is not live",
"run": "bash scripts/release/publish-npm-if-needed.sh",
"timeout_ms": 900000
},
{
"label": "sync homebrew if formula lags",
"run": "bash scripts/release/sync-homebrew-if-needed.sh",
"timeout_ms": 900000
}
]
}
}
}

Then the operator flow is:

agentxchain status
agentxchain approve-completion --dry-run
agentxchain approve-completion

The approval does not complete unless both wrapper scripts succeed. If sync-homebrew-if-needed.sh fails, the run blocks with gate_action_failed, the pending completion gate remains, and the operator reruns agentxchain approve-completion after fixing the script or environment.

What gate actions are not

MechanismWhat it doesHow it differs from gate actions
Approval PolicyAuto-approves some human gates when conditions matchApproval policy decides whether the human pause can be skipped; gate actions decide what runs after a human-approved gate
PoliciesDeclarative turn-acceptance rulesPolicies govern turn acceptance, not post-approval automation
NotificationsBest-effort delivery to external channelsNotification failures do not block approval; gate-action failures do

Current boundary

The shipped feature is repo-local only. It does not yet provide coordinator-level gate actions or a hosted action runner. That boundary is deliberate. Retry safety and evidence semantics need to stay battle-tested at the repo gate first.

For command syntax, see CLI Reference. For blocked recovery after a failed action, see Recovery.