fix(cutover): remove deploy-control legacy root mount #408

Merged
pdurlej merged 1 commit from codex/wave1/deploy-control-rootmount into main 2026-05-24 09:00:23 +02:00
Collaborator

Canary status: missing — rely on required Forgejo checks before merge

Canary Context Pack

Product story

RS2000 cutoff is accepted, but deploy-control still bind-mounts the whole legacy tree into the container as /repo. That keeps /opt/vps-home-platform-infra alive as active runtime input instead of rollback/data/config material. This PR removes that broad dependency as the first safe Phase 1 legacy cleanup slice.

What changed

  • Removed the missing build.context: ${PROJECT_ROOT}/scripts/deploy-control from deploy-control; the canonical repo no longer contains that source directory, and RS2000 already runs the local home-platform-deploy-control:1.0.0 image.
  • Removed ${PROJECT_ROOT}:/repo from deploy-control volumes.
  • Set DEPLOY_REPO_ROOT=/repo-disabled while SIGNAL_DEPLOY_ENABLED remains default-false.
  • Documented the cutoff contract in the module manifest and runbook.
  • Added a regression test that prevents reintroducing the missing build context or the whole-legacy-root /repo mount.

Why it changed

Phase 0 inventory for #387 found home-platform-deploy-control-1 bind-mounting /opt/vps-home-platform-infra wholesale. Live source audit showed deploy execution is disabled (SIGNAL_DEPLOY_ENABLED=false), and the service's /health, /plan, and /apply disabled paths do not need /repo.

Files touched

  • compose/apps/compose.yaml
  • modules/deploy-control/module.yaml
  • modules/deploy-control/runbook.md
  • tests/test_deploy_control_cutoff_contract.py

Relevant context

  • #387 legacy cleanup Phase 0 inventory
  • state/cutover/rs2000-control-plane-cutoff.md
  • ADR-0022 module source/release boundaries

Runtime evidence

Read-only RS2000 checks before the PR:

  • current release-root: releases/c6928decfb42e12756ca4cd638fa66c704ba9498
  • /opt/pdurlej-platform/current/scripts/deploy-control: missing
  • /opt/vps-home-platform-infra/scripts/deploy-control: present
  • live image exists: home-platform-deploy-control:1.0.0
  • live mounts currently include /opt/vps-home-platform-infra => /repo, /opt/vps-home-platform-infra/data/integrations => /state, /opt/vps-home-platform-infra/env => /env, and docker socket
  • source audit from legacy server.py: with SIGNAL_DEPLOY_ENABLED=false, /plan and /apply return disabled before git/current-branch/deploy execution; /health does not touch /repo

Known constraints

This is a real compose change for a stateful module. Merge alone must not be treated as production mutation. Production apply/recreate requires operator-gated F3 flow with a fresh backup-before-apply.sh deploy-control backup reference and stateful_confirm=BACKUP_DONE_F3.

Explicit out-of-scope

  • Moving /state and /env off legacy paths.
  • Removing the docker socket mount.
  • Rebuilding or rewriting the legacy deploy-control image.
  • Deleting, pruning, or renaming /opt/vps-home-platform-infra.

Requested decision

Merge if required checks are green, then operator can run the separate backup-backed deploy-control apply/smoke when ready.

Merge blockers

  • Test failure.
  • Evidence that SIGNAL_DEPLOY_ENABLED=true is required in current production.
  • Any objection to using the existing local home-platform-deploy-control:1.0.0 image without a repo-local build context.

Spec sources read

  • docs/forgejo-agent-operations.md — actor identity and Forgejo API contract.
  • compose/apps/compose.yaml — deploy-control runtime definition.
  • modules/deploy-control/module.yaml — module metadata and statefulness.
  • modules/deploy-control/runbook.md — operator recovery contract.
  • scripts/cutover/backup-before-apply.sh — backup gate and deploy-control backup behavior.
  • RS2000 legacy /opt/vps-home-platform-infra/scripts/deploy-control/server.py — only available source for live image behavior; read-only, no secrets.

Validation

  • PYTHONPATH=control-plane python3 -m platformctl.cli validate modules/deploy-control/module.yaml → pass
  • python3 -m pytest tests/test_deploy_control_cutoff_contract.py → 2 passed
  • python3 -m pytest control-plane/platformctl/tests/test_forgejo_ci_scripts_contract.py → 38 passed
  • git diff --check → pass
  • auto_apply_scope --base origin/main --head HEAD → blocked as expected: compose/test paths plus stateful module require operator-gated F3, not automatic push apply

Refs #387

Canary status: missing — rely on required Forgejo checks before merge ## Canary Context Pack ### Product story RS2000 cutoff is accepted, but `deploy-control` still bind-mounts the whole legacy tree into the container as `/repo`. That keeps `/opt/vps-home-platform-infra` alive as active runtime input instead of rollback/data/config material. This PR removes that broad dependency as the first safe Phase 1 legacy cleanup slice. ### What changed - Removed the missing `build.context: ${PROJECT_ROOT}/scripts/deploy-control` from `deploy-control`; the canonical repo no longer contains that source directory, and RS2000 already runs the local `home-platform-deploy-control:1.0.0` image. - Removed `${PROJECT_ROOT}:/repo` from `deploy-control` volumes. - Set `DEPLOY_REPO_ROOT=/repo-disabled` while `SIGNAL_DEPLOY_ENABLED` remains default-false. - Documented the cutoff contract in the module manifest and runbook. - Added a regression test that prevents reintroducing the missing build context or the whole-legacy-root `/repo` mount. ### Why it changed Phase 0 inventory for #387 found `home-platform-deploy-control-1` bind-mounting `/opt/vps-home-platform-infra` wholesale. Live source audit showed deploy execution is disabled (`SIGNAL_DEPLOY_ENABLED=false`), and the service's `/health`, `/plan`, and `/apply` disabled paths do not need `/repo`. ### Files touched - `compose/apps/compose.yaml` - `modules/deploy-control/module.yaml` - `modules/deploy-control/runbook.md` - `tests/test_deploy_control_cutoff_contract.py` ### Relevant context - #387 legacy cleanup Phase 0 inventory - `state/cutover/rs2000-control-plane-cutoff.md` - ADR-0022 module source/release boundaries ### Runtime evidence Read-only RS2000 checks before the PR: - current release-root: `releases/c6928decfb42e12756ca4cd638fa66c704ba9498` - `/opt/pdurlej-platform/current/scripts/deploy-control`: missing - `/opt/vps-home-platform-infra/scripts/deploy-control`: present - live image exists: `home-platform-deploy-control:1.0.0` - live mounts currently include `/opt/vps-home-platform-infra => /repo`, `/opt/vps-home-platform-infra/data/integrations => /state`, `/opt/vps-home-platform-infra/env => /env`, and docker socket - source audit from legacy `server.py`: with `SIGNAL_DEPLOY_ENABLED=false`, `/plan` and `/apply` return disabled before git/current-branch/deploy execution; `/health` does not touch `/repo` ### Known constraints This is a real compose change for a stateful module. Merge alone must not be treated as production mutation. Production apply/recreate requires operator-gated F3 flow with a fresh `backup-before-apply.sh deploy-control` backup reference and `stateful_confirm=BACKUP_DONE_F3`. ### Explicit out-of-scope - Moving `/state` and `/env` off legacy paths. - Removing the docker socket mount. - Rebuilding or rewriting the legacy deploy-control image. - Deleting, pruning, or renaming `/opt/vps-home-platform-infra`. ### Requested decision Merge if required checks are green, then operator can run the separate backup-backed deploy-control apply/smoke when ready. ### Merge blockers - Test failure. - Evidence that `SIGNAL_DEPLOY_ENABLED=true` is required in current production. - Any objection to using the existing local `home-platform-deploy-control:1.0.0` image without a repo-local build context. ## Spec sources read - `docs/forgejo-agent-operations.md` — actor identity and Forgejo API contract. - `compose/apps/compose.yaml` — deploy-control runtime definition. - `modules/deploy-control/module.yaml` — module metadata and statefulness. - `modules/deploy-control/runbook.md` — operator recovery contract. - `scripts/cutover/backup-before-apply.sh` — backup gate and deploy-control backup behavior. - RS2000 legacy `/opt/vps-home-platform-infra/scripts/deploy-control/server.py` — only available source for live image behavior; read-only, no secrets. ## Validation - `PYTHONPATH=control-plane python3 -m platformctl.cli validate modules/deploy-control/module.yaml` → pass - `python3 -m pytest tests/test_deploy_control_cutoff_contract.py` → 2 passed - `python3 -m pytest control-plane/platformctl/tests/test_forgejo_ci_scripts_contract.py` → 38 passed - `git diff --check` → pass - `auto_apply_scope --base origin/main --head HEAD` → blocked as expected: compose/test paths plus stateful module require operator-gated F3, not automatic push apply Refs #387
fix(cutover): remove deploy-control legacy root mount
All checks were successful
base-is-main / guard (pull_request) Successful in 1s
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 18s
python-ci / Python 3.11 (pull_request) Successful in 38s
python-ci / Python 3.12 (pull_request) Successful in 39s
python-ci / Python 3.13 (pull_request) Successful in 42s
canary-required / canary (pull_request) Successful in 15s
patchwarden-pr-sanity / sanity (pull_request) Successful in 22s
be4bb13be4
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!408
No description provided.