Reflex

Change freeze for AI coding agents

During a change window you define, a command that is not read-only and touches production asks a human or is denied. Windows go in your own config.json or in the team policy, work in shadow and enforce mode, and can only tighten.

Install: npx @ursuciprian/reflex setup starts with local rules in shadow mode, no account or key. Plugins for Claude Code, Codex CLI and opencode: setup guide.

How do I set a deploy freeze or change window for AI coding agents?

Add a freeze list to ~/.config/reflex/config.json, or to the team policy (.reflex/policy.json) so the whole team gets it: weekly windows such as {"days": ["fri"], "after": "15:00", "tz": "Europe/Bucharest"} and date ranges such as {"from": "2026-12-20", "to": "2027-01-03", "outcome": "deny"}. During a window, a command that is not read-only and touches production (by the working directory, AWS profile, kube context, Terraform workspace, git branch, the command or a team prod marker) asks a human or is denied, with a reason such as change freeze: Friday after 15:00 (Europe/Bucharest). It works in shadow and enforce mode, System 2 never approves it, and it can only tighten: a rule deny stays a deny, and an invalid window is an error rather than a smaller window. reflex status shows whether a freeze is active now. This is change management for Claude Code, Codex CLI and the other supported agents, for their shell commands.

See: GUIDE: change freeze for AI coding agents.

Change freeze for AI coding agents

A deploy freeze for AI coding agents: during a change window you define, a command that is not read-only and touches production asks a human, or is denied. This is change management for Claude Code, Codex CLI and the other agents Reflex gates, in the same place as the rest of the policy. Windows go in ~/.config/reflex/config.json (yours) or in a team policy's freeze list (the repository's), and both apply:

{
  "freeze": [
    {"days": ["fri"], "after": "15:00", "tz": "Europe/Bucharest", "applies_to": "prod", "outcome": "ask"},
    {"from": "2026-12-20", "to": "2027-01-03", "outcome": "deny", "note": "year-end freeze"}
  ]
}
FieldMeaning
daysDays of the week: mon, tue, wed, thu, fri, sat, sun.
after, beforeLocal time, HH:MM. after is inclusive, before is exclusive, and after must be earlier than before; a window across midnight is two windows.
from, toDates, YYYY-MM-DD, both inclusive.
tzAn IANA time zone name such as Europe/Bucharest. Default UTC. Times and dates are read in it with Intl, so summer time is handled.
applies_toprod (default): commands that touch production. all: every command that is not read-only.
outcomeask (default) or deny.
noteText added to the reason, at most 200 characters.

Every field a window has must hold at once: {"days": ["fri"], "after": "15:00"} is Friday from 15:00 to midnight. A window needs at least one of days, after, before, from and to.

What counts as production. The markers Reflex already uses: the prod-destroy rule's production pattern over the working directory, the AWS profile, the kube context, the Terraform workspace, the git branch and the command text (envs/prod, --context prd-eu, aws_profile=production, tf_workspace=live and so on), plus the prod markers of the team policy. A command whose pipelines only write notes (git commit -m "prod fix") is not production.

What the agent sees. A frozen command gets a rule decision with a reason such as

reflex (rule): change freeze: Friday after 15:00 (Europe/Bucharest); production (cwd)

The reason names the kind of marker (cwd, aws_profile, kube_context, command), never its value, since it can reach a webhook. The trace and reflex audit keep the matched text.

It is a deterministic decision, so it applies in shadow mode too, and in the autonomous profile it is in the always-human class: System 2 never approves it. A human's approval in the approval queue can lift a freeze ask for that one command, but only when the item was parked and answered inside the window, so an approval given before the freeze never carries into it. Nothing lifts a deny.

It only tightens. A window has no outcome that passes. A freeze outcome replaces a pass (the read-only list excepted), a fast lane pass, a Jev or local judgment, and a rule ask when the window denies. A rule deny, or a rule ask under an ask window, keeps its own reason. Validation is strict: an unknown field, a day such as friday, a time such as 9:00, a date that does not exist, an unknown time zone or an outcome other than ask or deny is an error, never a smaller window. In a team policy, an invalid window makes the file invalid, so its fast lane and webhook are off while its valid stricter parts, other windows included, still apply. In config.json, an invalid window is never dropped: until you fix it, every command that is not read-only asks, while the rules keep running (a rule deny stays a deny). reflex doctor names the window and the problem.

Seeing it. reflex status prints Change freeze: ACTIVE now: ... or none active, with the number of windows, for config.json and the team policy of the directory it runs in. reflex check "kubectl apply -f app.yaml" --cwd ~/infra/envs/prod shows the decision a frozen command would get. reflex audit --prod-only lists what happened during a freeze.

Limits. The window is read from the machine's clock. You can edit your own config.json or turn Reflex off (--mode off, where no freeze applies); a freeze is a control on the agent, not on you. Production is read from the command and its context, not from the scripts it runs: a ./deploy.sh whose only production reference is inside the script is not frozen by a prod window (use "applies_to": "all" for a full stop). A team policy's windows are protected like the rest of the file: an agent shell command that writes .reflex/ gets a tamper ask, and code review protects the committed file. The production markers read text, so a command that names production in an argument (gh pr create --title "prod fix") counts, which only adds asks. Reflex gates shell commands; file edits and MCP calls are not frozen.