fix(apply): force compose PROJECT_ROOT to remote root #413

Merged
pdurlej merged 1 commit from codex/fix/apply-project-root-override into main 2026-05-24 10:25:38 +02:00
Collaborator

Canary status: missing — fire canary via python -m platformctl.tools.run_review before merge

Summary

Fixes a deploy-runner drift where /opt/pdurlej-platform/runtime/compose.env can still contain PROJECT_ROOT=/opt/vps-home-platform-infra, causing docker compose build contexts to resolve against the legacy tree even when PLATFORMCTL_REMOTE_ROOT=/opt/pdurlej-platform/current.

Changes

  • platformctl apply now prefixes both compose preflight and apply commands with PROJECT_ROOT=<remote_root>.
  • This makes shell env override stale --env-file values for compose interpolation.
  • Adds tests for current release-root behavior and shell quoting.
  • Updates existing apply command expectations.

Verification

  • PYTHONPATH=control-plane pytest -q control-plane/platformctl/tests/test_apply*.py → 58 passed
  • git diff --check
  • Read-only RS2000 evidence before fix: full runner env-file render produced build contexts under /opt/vps-home-platform-infra/scripts/audio-mcp and /opt/vps-home-platform-infra/scripts/playwright-mcp.
  • Read-only RS2000 override proof: PROJECT_ROOT=/opt/pdurlej-platform/current docker compose ... config rendered those build contexts under /opt/pdurlej-platform/current/scripts/....

Test plan

  • Required Forgejo checks green.
  • After merge, run one trusted-main no-op apply and verify rendered/apply behavior uses release-root project context.
  • Later cleanup can still normalize /opt/pdurlej-platform/runtime/compose.env, but this fix removes the apply-path dependency on that stale value.

Canary Context Pack

Product story

After the control-plane cutoff, deploys must build and mount from the trusted release-root, not the legacy tree. A stale env file must not silently pull build context from /opt/vps-home-platform-infra.

What changed

Compose commands generated by platformctl apply now explicitly set PROJECT_ROOT to the same remote_root used for the compose file.

Why it changed

The Things rollout found that the runtime compose env still had PROJECT_ROOT=/opt/vps-home-platform-infra. Docker Compose uses that variable in local build contexts, so a merge/apply could build local-image modules from legacy code.

Files touched

  • control-plane/platformctl/apply.py
  • control-plane/platformctl/tests/test_apply_env_file.py
  • control-plane/platformctl/tests/test_apply_phase3.py

Relevant context

  • RS2000 cutoff: /opt/pdurlej-platform/current is the operational control-plane root.
  • ADR-0018: agent-visible false green/red drift must be fixed at root cause.
  • ADR-0020: legacy tree is no longer control-plane source of truth.

Runtime evidence

Read-only checks on RS2000 showed:

  • /opt/pdurlej-platform/runtime/compose.env: PROJECT_ROOT=/opt/vps-home-platform-infra.
  • Deploy runner env-file list includes compose env, Honcho env files, and Things env file.
  • Full compose render without override used legacy build contexts for audio-mcp and playwright-mcp.
  • Full compose render with PROJECT_ROOT=/opt/pdurlej-platform/current used current release-root build contexts.

No secrets were printed.

Known constraints

This PR changes command construction only. It does not edit runtime env files, restart services, apply production, or delete legacy material.

Explicit out-of-scope

  • Normalizing /opt/pdurlej-platform/runtime/compose.env.
  • Moving live data/config/env/products off legacy paths.
  • Running a production apply before merge.

Requested decision

Merge before any more local-build module applies, so stale env-file PROJECT_ROOT cannot pull source from the legacy tree.

Merge blockers

  • Any test failure in apply command generation.
  • Any reviewer finds that shell env assignment does not reliably override compose env-file interpolation.
  • Required Forgejo checks fail.

Spec sources read

  • control-plane/platformctl/apply.py — compose command generation.
  • control-plane/platformctl/tests/test_apply_env_file.py — env-file command tests.
  • control-plane/platformctl/tests/test_apply_phase3.py — apply transport command tests.
  • compose/apps/compose.yamlPROJECT_ROOT usage in build contexts and mounts.
  • docs/forgejo-agent-operations.md — Forgejo identity/write contract.
Canary status: missing — fire canary via `python -m platformctl.tools.run_review` before merge ## Summary Fixes a deploy-runner drift where `/opt/pdurlej-platform/runtime/compose.env` can still contain `PROJECT_ROOT=/opt/vps-home-platform-infra`, causing `docker compose` build contexts to resolve against the legacy tree even when `PLATFORMCTL_REMOTE_ROOT=/opt/pdurlej-platform/current`. ## Changes - `platformctl apply` now prefixes both compose preflight and apply commands with `PROJECT_ROOT=<remote_root>`. - This makes shell env override stale `--env-file` values for compose interpolation. - Adds tests for current release-root behavior and shell quoting. - Updates existing apply command expectations. ## Verification - [x] `PYTHONPATH=control-plane pytest -q control-plane/platformctl/tests/test_apply*.py` → 58 passed - [x] `git diff --check` - [x] Read-only RS2000 evidence before fix: full runner env-file render produced build contexts under `/opt/vps-home-platform-infra/scripts/audio-mcp` and `/opt/vps-home-platform-infra/scripts/playwright-mcp`. - [x] Read-only RS2000 override proof: `PROJECT_ROOT=/opt/pdurlej-platform/current docker compose ... config` rendered those build contexts under `/opt/pdurlej-platform/current/scripts/...`. ## Test plan - [ ] Required Forgejo checks green. - [ ] After merge, run one trusted-main no-op apply and verify rendered/apply behavior uses release-root project context. - [ ] Later cleanup can still normalize `/opt/pdurlej-platform/runtime/compose.env`, but this fix removes the apply-path dependency on that stale value. ## Canary Context Pack ### Product story After the control-plane cutoff, deploys must build and mount from the trusted release-root, not the legacy tree. A stale env file must not silently pull build context from `/opt/vps-home-platform-infra`. ### What changed Compose commands generated by `platformctl apply` now explicitly set `PROJECT_ROOT` to the same `remote_root` used for the compose file. ### Why it changed The Things rollout found that the runtime compose env still had `PROJECT_ROOT=/opt/vps-home-platform-infra`. Docker Compose uses that variable in local build contexts, so a merge/apply could build local-image modules from legacy code. ### Files touched - `control-plane/platformctl/apply.py` - `control-plane/platformctl/tests/test_apply_env_file.py` - `control-plane/platformctl/tests/test_apply_phase3.py` ### Relevant context - RS2000 cutoff: `/opt/pdurlej-platform/current` is the operational control-plane root. - ADR-0018: agent-visible false green/red drift must be fixed at root cause. - ADR-0020: legacy tree is no longer control-plane source of truth. ### Runtime evidence Read-only checks on RS2000 showed: - `/opt/pdurlej-platform/runtime/compose.env`: `PROJECT_ROOT=/opt/vps-home-platform-infra`. - Deploy runner env-file list includes compose env, Honcho env files, and Things env file. - Full compose render without override used legacy build contexts for `audio-mcp` and `playwright-mcp`. - Full compose render with `PROJECT_ROOT=/opt/pdurlej-platform/current` used current release-root build contexts. No secrets were printed. ### Known constraints This PR changes command construction only. It does not edit runtime env files, restart services, apply production, or delete legacy material. ### Explicit out-of-scope - Normalizing `/opt/pdurlej-platform/runtime/compose.env`. - Moving live data/config/env/products off legacy paths. - Running a production apply before merge. ### Requested decision Merge before any more local-build module applies, so stale env-file `PROJECT_ROOT` cannot pull source from the legacy tree. ### Merge blockers - Any test failure in apply command generation. - Any reviewer finds that shell env assignment does not reliably override compose env-file interpolation. - Required Forgejo checks fail. ## Spec sources read - `control-plane/platformctl/apply.py` — compose command generation. - `control-plane/platformctl/tests/test_apply_env_file.py` — env-file command tests. - `control-plane/platformctl/tests/test_apply_phase3.py` — apply transport command tests. - `compose/apps/compose.yaml` — `PROJECT_ROOT` usage in build contexts and mounts. - `docs/forgejo-agent-operations.md` — Forgejo identity/write contract.
fix(apply): force compose PROJECT_ROOT to remote root
All checks were successful
canary-required / collect-diff (pull_request) Successful in 5s
patchwarden-pr-sanity / collect-diff (pull_request) Successful in 4s
platformctl plan / auto-apply scope (pull_request) Successful in 22s
pyfallow / Pyfallow gate (control-plane) (pull_request) Successful in 19s
python-ci / Python 3.11 (pull_request) Successful in 41s
python-ci / Python 3.12 (pull_request) Successful in 42s
python-ci / Python 3.13 (pull_request) Successful in 40s
canary-required / canary (pull_request) Successful in 14s
base-is-main / guard (pull_request) Successful in 1s
patchwarden-pr-sanity / sanity (pull_request) Successful in 22s
32c955a68e
Sign in to join this conversation.
No reviewers
No labels
W6d-automerge-calibration
agent/claude-code
agent/codex
agent/hermes
agent/iskra
agent/ollama
agent/patchwarden
automerge-candidate
class/security-sensitive
cutover-gate
dependency/blocked
dependency/blocks-others
dependency/cross-repo
dependency/needs-confirmation
domain:agents
domain:ci
domain:docs
domain:forgejo
domain:infra
domain:memory
domain:runtime
domain:signal
domain:ux
flow/architecture
flow/blocked
flow/deployed
flow/done
flow/implementation
flow/intake
flow/maintained
flow/observed
flow/ready
flow/refining
flow/retired
flow/review
iterating
judge/codex-candidate
judge/hermes-candidate
judge/low-confidence
judge/needs-refinement
judge/operator-needed
judge/p0
judge/p1
judge/p2
judge/p3
judge/park
judge/patchwarden-candidate
judge/stale-priority
kind/adr
kind/bug
kind/chore
kind/feature
kind/infra
kind/ops
kind/refactor
kind/research
large-impact
merge/auto
merge/manual
merge/manual-dependency-conflict
merge/manual-failing-tests
merge/manual-merge-conflict
merge/manual-missing-review
merge/manual-operator-preference
merge/manual-red-zone
merge/manual-security-sensitive
merge/manual-unclear-scope
merge/manual-unknown
meta
mode:operator-only
mode:patchwarden-iskra-approved
mode:safe-auto
needs-operator-decision
needs-triage
not-ready
observed/erroring
observed/needs-followup
observed/pending
observed/retire-candidate
observed/unused
observed/used
operator-emotional
owner-attention
phase/02
phase/03
priority:p0
priority:p1
priority:p2
priority:p3
proposed
ready-for-agent
ready-for-operator
recovery
review:claude-reviewed
review:codex-reviewed
review:dziadek-reviewed
review:needs-human
risk/exposure
risk/process
risk/product
risk/runtime
safety:external-write
safety:no-prod-mutation
safety:prod-impact
safety:secret-touch
size/large
size/medium
size/small
size/tiny
size/unknown
source/adr
source/agent-generated
source/manual
source/operator-chat
source/voice-note
status:blocked
status:codex-ready
status:merged:pending-evidence
status:needs-evidence
status:operator-needed
status:parked
tier/full
tier/lite
tier/stacked
tier:0-platform-substrate
tier:1-iskra-value-layer
tier:2-tools-products-modules
type:bug
type:chore
type:docs
type:feat
type:policy
type:research
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
pdurlej/platform!413
No description provided.