Orchestrata Ultimate › Gates › prod-separation
Gate: prod-separation
At a glance
| Purpose | Refuse an installed orchestrata-*.service user unit whose path-bearing directives point at the development checkout. |
| Where it runs | The scripts/gates.py battery (registered as prod-separation). |
| Trigger | A battery run whose fingerprint changed, or whose last cached verdict was not ok. |
| Exact command | python scripts/check_prod_tree_separation.py |
| Blocks | Exit 1 on violations, 2 when the units could not be inspected, 0 clean. |
| Ruling / ADR | RUL-027 — prod runs its own checkout; ADR-0062 — production runs from a pinned-ref git checkout under /opt/orchestrata. |
| Last recorded verdict | ok: false in the primary checkout's .cache/gates.json (file last written 2026-09-22 10:01 local; the cause is not recorded there). wt-MAP-PRECOMMIT has no .cache/gates.json. |
What it refuses
An installed production systemd unit (orchestrata-*.service under
~/.config/systemd/user) that points any path-bearing directive at the development
checkout.
Why it exists
Module docstring, quoting the ruling and the state it corrected:
"Prod/dev tree separation gate — the mechanism for ruling RUL-027. 'Separate dev code from prod' (user command, 2026-08-08).
Production must execute from its own checkout. Before RUL-027 all six
orchestrata-*user units carriedWorkingDirectory=/home/mohamed/workspace/orchestrata-ultimate-architecturetogether withEnvironment=ORCHESTRATA_ENV_PROFILE=prod, so production ran whatever the development tree happened to contain: every edit was a pending deploy, andgit checkoutmoved production silently."
Why it reads installed units, not templates: "a template can be correct while the installed copy has drifted, and the installed copy is what actually runs."
One directive is there because of a recorded miss:
"
Environment=is here because of a real miss: orchestrata-history-mining carriedEnvironment=PYTHONPATH=<dev tree>/src. An exported PYTHONPATH OUTRANKS a checkout's own .pth, so that one line makes a unit execute the development tree's source no matter how correct its WorkingDirectory and ExecStart are. It is the same trap RUL-021 hit in worktrees, and the first version of this gate walked straight past it."
How it runs
- Command:
python scripts/check_prod_tree_separation.py - When it fires: the
scripts/gates.pybattery (registered asprod-separation). - What it scans: every
orchestrata-*.servicein the installed unit dir (default~/.config/systemd/user), checking thesePATH_DIRECTIVESfor a dev-tree reference:WorkingDirectory,ExecStart,ExecStartPre,ExecStartPost,ExecStop,ExecReload,EnvironmentFile,Environment.Description=is deliberately absent ("may name the tree harmlessly"). - Inputs / caching: fingerprint root
("deploy/systemd",). This root is a proxy, not the gate's read-set — the registry comment is explicit: "prod-separation reads the INSTALLED systemd user units, which are outside the repo, so no input-set fingerprint can detect drift in them. It is fingerprinted on the repo's unit TEMPLATES and always cheap to re-run; caching it on _SRC would let prod be re-pointed at the dev tree without the battery ever noticing." - The template proxy is mostly blind too. An earlier version of this page
described a
{".service", ".timer", ".socket", ".sh"}suffix mask that fixed this. No such mask exists:git log --all -S GATE_MASKS -- scripts/gates.pyfinds no commit that ever contained aGATE_MASKStable (checked 2026-10-23)._fingerprint()hashes only.py,.toml,.pl,.json,.mdand.shfiles (_interesting()), so of the 19 files underdeploy/systemd/(13.service, 4.timer, 1.sh, 1.md, counted 2026-10-23) only the.shand the.mdreach the fingerprint. After a cached PASS, neither an edited unit template nor a re-pointed installed unit re-runs the gate (derived from the code, not measured by a run).
Typical failures and how to fix them
- Exit codes: "0 clean, 1 violations found, 2 could not inspect."
- What a failure looks like (representative):
(unit, line number, line)for every dev-tree reference — the exact triplefind_violationsreturns. - Un-blocking: re-point the installed unit at the production checkout (or remove
the stray
Environment=PYTHONPATH=line) andsystemctl --user daemon-reload; fix the template underdeploy/systemd/in the same change so the next install is correct. - A unit change that the battery does not re-check. Because of the caching gap
above, run the gate fresh after installing or editing any unit:
python scripts/gates.py --only prod-separation --all, or the command directly. - Last recorded state: the primary checkout's cache records
ok: false(2026-09-22); which unit failed is not stored there. On 2026-10-23, 11orchestrata-*.servicefiles are installed under~/.config/systemd/user; their content was not checked in this run.
Wiring
- Invokers:
scripts/gates.py(battery). - Tests:
tests/scripts/test_check_prod_tree_separation.py.
Related
- Gates master index
- Development workflow
- RUL-027 — Production runs its own checkout
- ADR-0062
- RUL-021 — A worktree owns its env
- worktree-envs — "the same trap RUL-021 hit in worktrees", per this gate's own docstring
- env-subnet-scheme — the network side of environment separation