ci: prepare PM-safe platformctl auto deploy #234

Merged
pdurlej merged 2 commits from codex/auto-deploy/deploy-runner-boundary into main 2026-05-12 08:06:56 +02:00
Collaborator

Canary status: missing — fire canary 3+3 manually before merge

Canary Context Pack

Product story

Piotr should move toward “review PR → click Merge → trusted pipeline plans, applies, checks health, and leaves evidence”, while still being protected from accidental production deploys, repo-secret shortcuts, and PR-head execution.

What changed

This PR adds the deploy-runner boundary and a PM-safe platformctl auto-deploy skeleton:

  • records deploy-host as the dedicated trusted production runner lane;
  • forbids production deploy credentials from Forgejo repo secrets;
  • adds .forgejo/workflows/platformctl-plan.yml for secret-free PR scope evidence;
  • adds .forgejo/workflows/platformctl-auto-apply.yml for post-merge trusted-main plan → apply → health evidence;
  • adds auto_apply_scope.py to allow only narrow v1 modules: rs2000, docker-compose, relative compose file, and no secret/provider-like diff lines;
  • adds find_merged_pr.py so auto-apply binds trusted main SHA back to a merged PR;
  • adds platformctl apply --remote-root and PLATFORMCTL_REMOTE_ROOT support.

Why it changed

Manual SSH/apply is still too much burden for a non-technical PM. Before a real auto-deploy can be trusted, the repo needs a hard runner/secret boundary and a narrow active workflow that fails closed when the runner is not ready.

Files touched

  • .forgejo/ci-policy.yaml
  • .forgejo/workflows/merged-in-main-audit.yml
  • .forgejo/workflows/platformctl-plan.yml
  • .forgejo/workflows/platformctl-auto-apply.yml
  • control-plane/platformctl/apply.py
  • control-plane/platformctl/cli.py
  • control-plane/platformctl/ci/auto_apply_scope.py
  • control-plane/platformctl/ci/find_merged_pr.py
  • control-plane/platformctl/tests/test_apply_phase3.py
  • control-plane/platformctl/tests/test_forgejo_ci_scripts_contract.py
  • control-plane/pyproject.toml
  • docs/ci/README.md
  • docs/ci/runner-contract.md
  • runbooks/forgejo-actions-runner.md

Relevant context

  • ADR-0017 merged in #220: PM-safe merge flow and main-landing protection.
  • Existing Phase 3 platformctl plan/apply/health chain.
  • AGENTS.md security-sensitive class of service.

Runtime evidence

No RS2000 runtime mutation. Local checks passed:

PYTHONPATH=control-plane pytest \
  control-plane/platformctl/tests/test_plan_phase3.py \
  control-plane/platformctl/tests/test_apply_phase3.py \
  control-plane/platformctl/tests/test_forgejo_ci_scripts_contract.py \
  control-plane/platformctl/tests/test_health_phase3.py \
  control-plane/platformctl/tests/test_smoke.py \
  -q
# 97 passed

PYTHONPATH=control-plane python3 control-plane/platformctl/ci/lint_workflows.py \
  --policy .forgejo/ci-policy.yaml \
  --workflows .forgejo/workflows \
  --json /tmp/workflow-lint-auto.json \
  --markdown /tmp/workflow-lint-auto.md
# 0 findings

git diff --check

Known constraints

The deploy runner must be registered/configured separately before the post-merge workflow can perform real work. The auto-apply job fails closed unless runner-local PLATFORMCTL_DEPLOY_READY=1 and local deploy capability are present.

Explicit out-of-scope

  • No RS2000 production mutation in this PR.
  • No Forgejo repo secrets for deploy.
  • No vps1000 auto-apply.
  • No absolute Agaria compose paths.
  • No canonical compose import/migration yet.
  • No arbitrary “run command with secrets” workflow.

Requested decision

Approve the dedicated deploy-runner boundary and narrow active workflow skeleton, then perform the one-time deploy runner setup before relying on auto-apply as the normal RS2000 change path.

Merge blockers

  • Any production deploy secret routed through Forgejo repo secrets.
  • Any PR-head deploy execution path.
  • Any broad shared runner replacing the dedicated deploy-host tier.
  • Any reviewer finding that the workflow can mutate production from untrusted PR code.

Operator Merge Checklist

  1. Merge #233 first, or confirm its branch-protection readiness checker is already on main.
  2. Do not create Forgejo repo secrets for production deploy: no SSH key, no Tailscale client secret, no Forgejo PAT/provider key.
  3. Register/configure dedicated runner rs2000-deploy-host with label deploy-host before expecting auto-apply to work.
  4. Set runner-local deploy readiness only after checks pass: PLATFORMCTL_DEPLOY_READY=1, local Forgejo apply token bridge, SSH/Tailscale capability, and PLATFORMCTL_REMOTE_ROOT=/opt/pdurlej-platform/current.
  5. If the PR is not mergeable or auto-apply reports not_configured, do not guess; ask Codex/Claude for the next exact command.

Spec sources read

  • AGENTS.md — identity, Forgejo API, security-sensitive review class.
  • docs/ci/README.md — CI trust-tier documentation target.
  • docs/ci/runner-contract.md — runner contract target.
  • runbooks/forgejo-actions-runner.md — runner setup documentation target.
  • .forgejo/ci-policy.yaml — workflow policy source.
  • control-plane/platformctl/apply.py — apply remote-root and approval behavior.
  • control-plane/platformctl/cli.py — CLI flag surface.
  • control-plane/platformctl/tests/test_apply_phase3.py — apply tests.
  • control-plane/platformctl/tests/test_forgejo_ci_scripts_contract.py — contract tests.

Refs #142
Refs #220

Canary status: missing — fire canary 3+3 manually before merge ## Canary Context Pack ### Product story Piotr should move toward “review PR → click Merge → trusted pipeline plans, applies, checks health, and leaves evidence”, while still being protected from accidental production deploys, repo-secret shortcuts, and PR-head execution. ### What changed This PR adds the deploy-runner boundary and a PM-safe `platformctl` auto-deploy skeleton: - records `deploy-host` as the dedicated trusted production runner lane; - forbids production deploy credentials from Forgejo repo secrets; - adds `.forgejo/workflows/platformctl-plan.yml` for secret-free PR scope evidence; - adds `.forgejo/workflows/platformctl-auto-apply.yml` for post-merge trusted-main plan → apply → health evidence; - adds `auto_apply_scope.py` to allow only narrow v1 modules: `rs2000`, `docker-compose`, relative compose file, and no secret/provider-like diff lines; - adds `find_merged_pr.py` so auto-apply binds trusted main SHA back to a merged PR; - adds `platformctl apply --remote-root` and `PLATFORMCTL_REMOTE_ROOT` support. ### Why it changed Manual SSH/apply is still too much burden for a non-technical PM. Before a real auto-deploy can be trusted, the repo needs a hard runner/secret boundary and a narrow active workflow that fails closed when the runner is not ready. ### Files touched - `.forgejo/ci-policy.yaml` - `.forgejo/workflows/merged-in-main-audit.yml` - `.forgejo/workflows/platformctl-plan.yml` - `.forgejo/workflows/platformctl-auto-apply.yml` - `control-plane/platformctl/apply.py` - `control-plane/platformctl/cli.py` - `control-plane/platformctl/ci/auto_apply_scope.py` - `control-plane/platformctl/ci/find_merged_pr.py` - `control-plane/platformctl/tests/test_apply_phase3.py` - `control-plane/platformctl/tests/test_forgejo_ci_scripts_contract.py` - `control-plane/pyproject.toml` - `docs/ci/README.md` - `docs/ci/runner-contract.md` - `runbooks/forgejo-actions-runner.md` ### Relevant context - ADR-0017 merged in #220: PM-safe merge flow and main-landing protection. - Existing Phase 3 `platformctl plan/apply/health` chain. - AGENTS.md security-sensitive class of service. ### Runtime evidence No RS2000 runtime mutation. Local checks passed: ```bash PYTHONPATH=control-plane pytest \ control-plane/platformctl/tests/test_plan_phase3.py \ control-plane/platformctl/tests/test_apply_phase3.py \ control-plane/platformctl/tests/test_forgejo_ci_scripts_contract.py \ control-plane/platformctl/tests/test_health_phase3.py \ control-plane/platformctl/tests/test_smoke.py \ -q # 97 passed PYTHONPATH=control-plane python3 control-plane/platformctl/ci/lint_workflows.py \ --policy .forgejo/ci-policy.yaml \ --workflows .forgejo/workflows \ --json /tmp/workflow-lint-auto.json \ --markdown /tmp/workflow-lint-auto.md # 0 findings git diff --check ``` ### Known constraints The deploy runner must be registered/configured separately before the post-merge workflow can perform real work. The auto-apply job fails closed unless runner-local `PLATFORMCTL_DEPLOY_READY=1` and local deploy capability are present. ### Explicit out-of-scope - No RS2000 production mutation in this PR. - No Forgejo repo secrets for deploy. - No vps1000 auto-apply. - No absolute Agaria compose paths. - No canonical compose import/migration yet. - No arbitrary “run command with secrets” workflow. ### Requested decision Approve the dedicated deploy-runner boundary and narrow active workflow skeleton, then perform the one-time deploy runner setup before relying on auto-apply as the normal RS2000 change path. ### Merge blockers - Any production deploy secret routed through Forgejo repo secrets. - Any PR-head deploy execution path. - Any broad shared runner replacing the dedicated `deploy-host` tier. - Any reviewer finding that the workflow can mutate production from untrusted PR code. ## Operator Merge Checklist 1. Merge #233 first, or confirm its branch-protection readiness checker is already on `main`. 2. Do not create Forgejo repo secrets for production deploy: no SSH key, no Tailscale client secret, no Forgejo PAT/provider key. 3. Register/configure dedicated runner `rs2000-deploy-host` with label `deploy-host` before expecting auto-apply to work. 4. Set runner-local deploy readiness only after checks pass: `PLATFORMCTL_DEPLOY_READY=1`, local Forgejo apply token bridge, SSH/Tailscale capability, and `PLATFORMCTL_REMOTE_ROOT=/opt/pdurlej-platform/current`. 5. If the PR is not mergeable or auto-apply reports `not_configured`, do not guess; ask Codex/Claude for the next exact command. ## Spec sources read - `AGENTS.md` — identity, Forgejo API, security-sensitive review class. - `docs/ci/README.md` — CI trust-tier documentation target. - `docs/ci/runner-contract.md` — runner contract target. - `runbooks/forgejo-actions-runner.md` — runner setup documentation target. - `.forgejo/ci-policy.yaml` — workflow policy source. - `control-plane/platformctl/apply.py` — apply remote-root and approval behavior. - `control-plane/platformctl/cli.py` — CLI flag surface. - `control-plane/platformctl/tests/test_apply_phase3.py` — apply tests. - `control-plane/platformctl/tests/test_forgejo_ci_scripts_contract.py` — contract tests. Refs #142 Refs #220
ci: document deploy runner secret boundary
All checks were successful
canary-required / collect-diff (pull_request) Successful in 4s
infra-docs-drift / docs-drift (pull_request) Successful in 4s
pyfallow / Pyfallow gate (control-plane) (pull_request) Successful in 16s
python-ci / Python 3.11 (pull_request) Successful in 43s
python-ci / Python 3.12 (pull_request) Successful in 44s
python-ci / Python 3.13 (pull_request) Successful in 45s
workflow-lint / lint (pull_request) Successful in 4s
canary-required / canary (pull_request) Successful in 12s
base-is-main / guard (pull_request) Successful in 1s
990d7bf7b0
ci: add PM-safe platformctl auto apply skeleton
All checks were successful
canary-required / collect-diff (pull_request) Successful in 4s
infra-docs-drift / docs-drift (pull_request) Successful in 4s
platformctl plan / auto-apply scope (pull_request) Successful in 19s
base-is-main / guard (pull_request) Successful in 1s
pyfallow / Pyfallow gate (control-plane) (pull_request) Successful in 17s
python-ci / Python 3.11 (pull_request) Successful in 46s
python-ci / Python 3.12 (pull_request) Successful in 45s
python-ci / Python 3.13 (pull_request) Successful in 47s
workflow-lint / lint (pull_request) Successful in 4s
canary-required / canary (pull_request) Successful in 12s
a71167d385
codex changed title from ci: document deploy runner secret boundary to ci: prepare PM-safe platformctl auto deploy 2026-05-12 07:56:03 +02:00
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!234
No description provided.