Completes the part of the 0.19.0 removal schedule that covers compatibility paths with phlo.legacy.*.uses counters (ADR 0053 decision 4 and exit criteria). Each path is removed with its warning, counter, tests of the deprecated behaviour and documentation.
Removed paths and replacements
| Removed | Counter removed | Replacement |
|---|---|---|
PHLO_REGULATED_MODE environment fallback; phlo.yaml regulated_mode key; is_regulated_mode_enabled, PHLO_REGULATED_MODE_ENV, get_regulated_mode_config (config and infrastructure), create_regulated_mode_bridge, run_regulated_mode_validation, require_regulated_mode_validation | phlo.legacy.regulated_mode_env.uses | PHLO_REGULATED or regulated: true; is_regulated, PHLO_REGULATED_ENV, get_regulated_config, create_regulated_bridge, run_regulated_validation, require_regulated_validation |
Legacy Dagster .phlo/.env and .phlo/.env.local attachment per generated Phlo-dev service | phlo.legacy.dagster_env_file.uses | phlo services migrate to .phlo/overrides/.env and .phlo/secrets/.env |
Legacy MCP JSONL debug tracing: --trace-file, PHLO_MCP_TRACE_FILE, McpTraceSettings, McpConfig.trace_file, configure_tracing, load_spans, render_trace_tree | phlo.legacy.mcp_jsonl_span.uses | OBSERVE_DRAINS or OBSERVE_HTTP_ENDPOINT; canonical MCP operation scopes and trace queries are unchanged |
Behaviour after removal
- A phlo.yaml
regulated_modekey fails validation with a message namingregulated, instead of being ignored. A stale key therefore cannot silently turn regulated mode off. Root-level phlo.yaml validation errors now include their message (previously the field list was empty for them). phlo services init,addandremovestop with a CLI error namingphlo services migratewhile.phlo/.envor.phlo/.env.localexists. Migrated and new projects getenv_file: [overrides/.env, secrets/.env]as before.phlo services migrateis unchanged.PHLO_REGULATED_MODEno longer configures anything, but it still fails closed when stale:is_regulated()(and sorequires_http_authorization()) raises namingPHLO_REGULATEDwhen the removed variable is set and neitherPHLO_REGULATEDnor explicit configuration decides the result. The Compose override-layer guard still treats a true value as protected.phlo services migrateno longer requires.phlo/.env, so a project holding only.phlo/.env.localcan migrate. The phlo.yaml template, generated Compose header, env header and production-credential message now point secrets at.phlo/secrets/.env.
Verification
make checkpasses;make test-core-regressionpasses.- phlo-mcp package tests pass; the tool-tracing test now asserts nesting and outcomes from canonical observe events instead of the JSONL file.
- Manual: legacy-layout project →
phlo services init --forcefails with the migration message →phlo services migrate→ regeneration attaches only the shared files.docker compose config --quietpasses on a freshly generated stack. registry/support/v1.jsondoes not reference these paths; unchanged.
Activity
313Reviewed 91a15b6 against the full diff, ADR 0065, the migrate/upgrade call paths, and the settings/env inventories. Two issues, both in the "removed successfully" story rather than the removals themselves.
1. PHLO_REGULATED_MODE now turns regulated mode off silently
src/phlo/security/mode.py:38-55 removes the fallback, and tests/security/test_mode.py adds test_removed_regulated_mode_env_is_ignored to lock in the silence. A deployment whose only regulated signal was the deprecated variable now reports unregulated with no warning or error: requires_http_authorization() no longer forces authentication for it, phlo services init --observatory-dev no longer refuses (src/phlo/cli/commands/services/init.py, _configure_observatory_dev), and the Compose override guard keyed off the same predicate stops refusing layers.
That is the same outcome the description rejects for the regulated_mode key — "a stale key cannot silently turn regulated mode off" — and the new reject_removed_regulated_mode validator in src/phlo/config_schema.py enforces it there. Leaving the environment spelling fail-open makes the security posture depend on which spelling a deployment happened to keep.
Suggestion: keep a presence check for the removed name and raise (or warn once, as the deprecation did) naming PHLO_REGULATED when it is set, then assert that failure instead of the current silent-ignore test.
2. A lone .phlo/.env.local can neither init nor migrate
The new guard in src/phlo/plugins/compose/generator.py:413-419 raises whenever either legacy file exists, and every regeneration command routes that to "run phlo services migrate". But phlo services migrate starts from ensure_compose_project() (src/phlo/cli/commands/services/utils.py:99-113), which requires env_defaults_path(...), i.e. .phlo/.env for a legacy layout (src/phlo/config/layout.py:26-28). A project holding only .phlo/.env.local therefore gets "run phlo services init" from migrate and "run phlo services migrate" from init/add/remove/upgrade, and only manual file renaming recovers. Before this change init repaired that state by regenerating .phlo/.env and attaching .env.local.
The state is reachable through the repository's own guidance, which this PR leaves in place: PHLO_CONFIG_TEMPLATE in utils.py:80 tells new projects "Secrets belong in .phlo/.env.local (not committed)" (written into their phlo.yaml), and the generated Compose header in generator.py:152 still says "Modify ... .phlo/.env.local for secrets". New projects are shared-layout, so those paths are no longer read — and creating one now blocks every command that regenerates Compose.
Suggestion: point the template and header at .phlo/overrides/.env / .phlo/secrets/.env, and let migrate handle a missing defaults file (or have the new error spell out the manual move to .phlo/secrets/.env) so the documented recovery path actually exists.
Checked without findings
The phlo-mcp removal is self-contained in the reviewed modules (server.py, cli.py, cli_plugin.py, _tools_traces.py); the generated settings references (333 fields / 52 models), tests/tooling/phlo_env_reads_inventory.json, and docs/reference/python-api.md are consistent; registry/support/v1.json mentions none of the removed names. CI checks at this SHA were still running when I reviewed (only PR Lens had finished), so the description's make check claim is unverified here; this is static inspection only, no code executed.
91a15b6, the branch has moved onArchitecture
Inside the changed components — 2 views
Component view — Security & RBAC
Regulated mode configuration and validation components with removed aliases
Component view — MCP Server Tracing
MCP server instrumentation updated to remove custom JSONL trace drain
Data flow
Review
✨ Nothing flagged · reviewed
91a15b6🪧 More tips
npx skills add coldteadotai/pr-lens, then tell your coding agent: "Diagram the change you just made with PR Lens and attach it to the pull request."npx @coldtea/pr-lens-cli analyze --base origin/mainon a branch, thennpx @coldtea/pr-lens-cli render .pr-lens/graph.json. Same lenses, your own model key, before the pull request existsrenderreads.github/pr-lens.ymland applies your renames, exclusions and lane pins at draw timegithub.comment.collapsed: truein.github/pr-lens.ymlto fold the comment behind one View architecture… row. Drawing still runs as beforegithub.draw: on-demandin.github/pr-lens.ymland PR Lens stops drawing pull requests on its own. Comment@pr-lens drawon a pull request when you want that one drawn.github/workflows/pr-lens.ymlwithcoldteadotai/pr-lens/packages/action@v0and your model provider's key as itsapi-keyto run PR Lens from your own CI. Any/chat/completionsendpoint worksgithub.description: falsein.github/pr-lens.ymlto stop PR Lens adding buttons to your PR descriptionsThanks for using PR Lens! It's free for OSS. If it helped, a star helps other teams find it.