fix(platformctl): verify apply PR base branch #665
No reviewers
Labels
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
No due date set.
Dependencies
No dependencies set.
Reference
pdurlej/platform!665
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "codex/189-apply-pr-base-guard"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Canary status: missing - platformctl apply approval hardening; run canary manually before merge if branch policy requires it.
Canary Context Pack
Product story
A trusted apply should only accept approval evidence from a PR merged into the expected deployment branch. A merged PR targeting some other branch must not unlock production apply.
What changed
verify_approved_shanow checks the approved PR base ref. It defaults tomain, supports an explicitexpected_base_refparameter, and also honors sanitizedPLATFORMCTL_EXPECTED_BASE_REFfor controlled non-main deployments.Why it changed
Issue #189 / PR-BIND-01 asked to bind approved apply PRs to the expected deployment branch, normally
main.Files touched
control-plane/platformctl/apply.pycontrol-plane/platformctl/tests/test_apply_phase3.pyRelevant context
base_ref_mismatch.Runtime evidence
No runtime commands were run. Validation:
Results: targeted 16 passed; broader 135 passed;
validate allexitCode 0.Known constraints
This PR does not change direct runtime apply mechanics. It changes only approval validation.
Explicit out-of-scope
Requested decision
Approve if
mainshould remain the default deployment branch and the sanitized override is acceptable for controlled future lanes.Merge blockers
base.refin production, which would now fail closed.Spec sources read
control-plane/platformctl/apply.py- approval validation path.control-plane/platformctl/tests/test_apply_phase3.py- approval regression tests.Closes #189
Patchwarden PR sanity
advisory_findings665ab082cba0a69cf0a11ebb911c2173266c58c5e4epresentDeterministic findings
infosensitive-path-touchedSensitive path touched —control-plane/platformctl/apply.pycontrol-plane/platformctl/apply.pyModel reviewers
global-glm/glm-5.1:cloudStatus:
okVerdict:
NOT_OKblockerForgejo API compatibility: missing base.ref causes fail-closed rejectionIn _pr_base_ref() (apply.py, lines ~272-278), if base is missing or base.ref is missing, the function returns None. In _check_specific_pr() (apply.py, lines ~297-303), None != expected_base_ref causes rejection with APPROVAL_REASON_BASE_REFmediumSilent fallback in _expected_base_ref() masks configuration errorsIn _expected_base_ref() (apply.py, lines ~258-263), if PLATFORMCTL_EXPECTED_BASE_REF env var is set to an invalid value, the function silently falls back to 'main' instead of raising an error or logging a warning. This could mask misconfiguglobal-deepseek/deepseek-v4-pro:cloudStatus:
okVerdict:
NOT_OKblockerProduction Forgejo API lacks base.ref causing all approvals to fail closedPR description states 'Forgejo PR payloads lack base.ref in production, which would now fail closed.' Code in apply.py_pr_base_refreturns None if base.ref missing, and_check_specific_prcompares None != expected_base_ref, resulting iblockerNon-main deployment lane requires canonical env name before mergePR description states 'A non-main deployment lane needs a different canonical env name before merge.' This implies the current default 'main' may not be appropriate for all lanes, and the env var override may not be fully validated.highMissing canary validation for approval hardeningPR description says 'Canary status: missing'. The change modifies the approval path which is critical for production apply; without canary, regressions may go undetected.redteam/kimi-k2.6:cloudStatus:
okVerdict:
NOT_OKblockerProduction Forgejo payloads lack base.ref causing total apply denialPR description merge blockers explicitly states: 'Forgejo PR payloads lackbase.refin production, which would now fail closed.' In apply.py,_pr_base_refreturnsNonewhen the 'base' dict or 'ref' key is missing, and_check_specific`base.ref, or add a fallback/shim that skips the base-ref check when the field is absent from the payload.Policy notes
PLATFORMCTL_PR_SANITY_REDTEAM_MODELis configured.