bug: surface invalid automerge merge-bot token as blocked state #243

Closed
opened 2026-06-25 21:54:15 +02:00 by codex · 1 comment
Collaborator

Bug

Patchwarden's W6d/automerge integration can report a PR as clean and ready, but the final controller merge still fails because the runner-local merge-bot token is invalid. The failure currently appears only inside the automerge-pilot Actions log, after Patchwarden has already produced green evidence.

Platform Case

Repo: pdurlej/platform

Related items:

  • platform PR #826: safe-scope self-merge smoke PR
  • platform PR #827: fixed the earlier stale read-token blocker in automerge-pilot collection
  • platform issue #823: W6d unattended merge-safety loop
  • Forgejo Actions run #5944: latest self-merge attempt after #827

Exact PR/head under test:

  • PR: pdurlej/platform#826
  • Head SHA: 3b1d252a97f18934d7385fffb16aea4ab8272aa0
  • Base SHA: b6615852544d8b339f15998f6d24c7f8f3d81a40

Observed

After #827 was merged, automerge-pilot run #5944 progressed correctly through the earlier blocker:

  • Collect Forgejo facts: passed via token_env=GITHUB_TOKEN
  • automerge_readiness.py: ready_for_controller_merge, ready=true
  • Required contexts were green:
    • base-is-main / guard (pull_request)
    • patchwarden-pr-sanity / sanity (pull_request)
    • patchwarden-client-dry-run / dry-run (pull_request)
  • Iskra Matrix approval returned approved=true for the exact PR/head SHA.

The run then failed in the final merge actor while validating PLATFORM_AUTOMERGE_BOT_TOKEN:

File "control-plane/platformctl/ci/automerge_actor.py", line 253, in _load_ready_context
  actor = _token_login(args.forgejo_host, token)
...
urllib.error.HTTPError: HTTP Error 401: Unauthorized

No secret value was printed in logs. The failing token name is runner-local PLATFORM_AUTOMERGE_BOT_TOKEN.

Expected

Patchwarden/merge-safety should surface this as a first-class blocked state before the operator has to inspect raw Actions logs, for example:

  • verdict: blocked_merge_actor_credential or equivalent
  • reason: merge actor token invalid/unusable for /api/v1/user
  • repair: refresh runner-local PLATFORM_AUTOMERGE_BOT_TOKEN for the non-cousin merge-bot identity, then rerun the same PR/head
  • explicit note that PR readiness and Patchwarden checks are clean, but final merge authority is unavailable

This should not weaken the W6d contract. GITHUB_TOKEN and cousin tokens must not become merge actors.

Why This Belongs In Patchwarden

Patchwarden is the operator-facing safety layer for PR/automerge state. In this case, the PR was clean and the controller path reached final approval, but the operator-facing state still required log spelunking to distinguish:

  • bad PR readiness,
  • failed Matrix approval,
  • stale read token,
  • invalid merge-bot credential.

The actual result was the fourth case.

Acceptance Criteria

  • Patchwarden or its platform integration can classify invalid merge-bot credentials as a distinct blocked state.
  • The state names the failed capability without exposing token values.
  • Repair instructions are specific enough: refresh/restore runner-local PLATFORM_AUTOMERGE_BOT_TOKEN, then rerun automerge-pilot for the same PR/head.
  • The implementation does not allow codex, claude, glm, pdurlej, GITHUB_TOKEN, or the PR author to act as merge actor.

Labels Suggested

bug, client:platform, domain:ci, domain:forgejo, dependency/cross-repo, agent/codex

## Bug Patchwarden's W6d/automerge integration can report a PR as clean and ready, but the final controller merge still fails because the runner-local merge-bot token is invalid. The failure currently appears only inside the `automerge-pilot` Actions log, after Patchwarden has already produced green evidence. ## Platform Case Repo: `pdurlej/platform` Related items: - platform PR #826: safe-scope self-merge smoke PR - platform PR #827: fixed the earlier stale read-token blocker in `automerge-pilot` collection - platform issue #823: W6d unattended merge-safety loop - Forgejo Actions run #5944: latest self-merge attempt after #827 Exact PR/head under test: - PR: `pdurlej/platform#826` - Head SHA: `3b1d252a97f18934d7385fffb16aea4ab8272aa0` - Base SHA: `b6615852544d8b339f15998f6d24c7f8f3d81a40` ## Observed After #827 was merged, `automerge-pilot` run #5944 progressed correctly through the earlier blocker: - `Collect Forgejo facts`: passed via `token_env=GITHUB_TOKEN` - `automerge_readiness.py`: `ready_for_controller_merge`, `ready=true` - Required contexts were green: - `base-is-main / guard (pull_request)` - `patchwarden-pr-sanity / sanity (pull_request)` - `patchwarden-client-dry-run / dry-run (pull_request)` - Iskra Matrix approval returned `approved=true` for the exact PR/head SHA. The run then failed in the final merge actor while validating `PLATFORM_AUTOMERGE_BOT_TOKEN`: ```text File "control-plane/platformctl/ci/automerge_actor.py", line 253, in _load_ready_context actor = _token_login(args.forgejo_host, token) ... urllib.error.HTTPError: HTTP Error 401: Unauthorized ``` No secret value was printed in logs. The failing token name is runner-local `PLATFORM_AUTOMERGE_BOT_TOKEN`. ## Expected Patchwarden/merge-safety should surface this as a first-class blocked state before the operator has to inspect raw Actions logs, for example: - verdict: `blocked_merge_actor_credential` or equivalent - reason: merge actor token invalid/unusable for `/api/v1/user` - repair: refresh runner-local `PLATFORM_AUTOMERGE_BOT_TOKEN` for the non-cousin merge-bot identity, then rerun the same PR/head - explicit note that PR readiness and Patchwarden checks are clean, but final merge authority is unavailable This should not weaken the W6d contract. `GITHUB_TOKEN` and cousin tokens must not become merge actors. ## Why This Belongs In Patchwarden Patchwarden is the operator-facing safety layer for PR/automerge state. In this case, the PR was clean and the controller path reached final approval, but the operator-facing state still required log spelunking to distinguish: - bad PR readiness, - failed Matrix approval, - stale read token, - invalid merge-bot credential. The actual result was the fourth case. ## Acceptance Criteria - Patchwarden or its platform integration can classify invalid merge-bot credentials as a distinct blocked state. - The state names the failed capability without exposing token values. - Repair instructions are specific enough: refresh/restore runner-local `PLATFORM_AUTOMERGE_BOT_TOKEN`, then rerun `automerge-pilot` for the same PR/head. - The implementation does not allow `codex`, `claude`, `glm`, `pdurlej`, `GITHUB_TOKEN`, or the PR author to act as merge actor. ## Labels Suggested `bug`, `client:platform`, `domain:ci`, `domain:forgejo`, `dependency/cross-repo`, `agent/codex`
pdurlej referenced this issue from a commit 2026-06-25 22:24:15 +02:00
Author
Collaborator

Addressed in platform PR #828: pdurlej/platform#828

Merged commit: f4ddb78724023edf21188e28e702f54a6fcc984e.

What changed:

  • automerge_actor.py now writes platform_automerge_actor_readiness.v0 for merge actor auth/identity checks.
  • 401/403 from runner-local PLATFORM_AUTOMERGE_BOT_TOKEN becomes blocked_merge_actor_credential with a repair instruction and no token/body leakage.
  • GITHUB_TOKEN is rejected before token login.
  • codex, claude, glm, pdurlej, and the PR author are rejected as merge actors, case-insensitively.

Verification:

  • python3 -m pytest control-plane/platformctl/tests/test_automerge_actor.py
  • python3 -m pytest control-plane/platformctl/tests/test_automerge_actor.py control-plane/platformctl/tests/test_forgejo_ci_scripts_contract.py control-plane/platformctl/tests/test_automerge_readiness.py
  • Platform PR #828 CI was green before merge.

Operational note: while merging this, the required patchwarden-pr-sanity / sanity status took 6m36s and the first status read looked pending because Forgejo returns status history out of order. That is a separate PR-sanity health/UX follow-up, not part of this issue.

Addressed in platform PR #828: https://git.pdurlej.com/pdurlej/platform/pulls/828 Merged commit: `f4ddb78724023edf21188e28e702f54a6fcc984e`. What changed: - `automerge_actor.py` now writes `platform_automerge_actor_readiness.v0` for merge actor auth/identity checks. - 401/403 from runner-local `PLATFORM_AUTOMERGE_BOT_TOKEN` becomes `blocked_merge_actor_credential` with a repair instruction and no token/body leakage. - `GITHUB_TOKEN` is rejected before token login. - `codex`, `claude`, `glm`, `pdurlej`, and the PR author are rejected as merge actors, case-insensitively. Verification: - `python3 -m pytest control-plane/platformctl/tests/test_automerge_actor.py` - `python3 -m pytest control-plane/platformctl/tests/test_automerge_actor.py control-plane/platformctl/tests/test_forgejo_ci_scripts_contract.py control-plane/platformctl/tests/test_automerge_readiness.py` - Platform PR #828 CI was green before merge. Operational note: while merging this, the required `patchwarden-pr-sanity / sanity` status took 6m36s and the first status read looked pending because Forgejo returns status history out of order. That is a separate PR-sanity health/UX follow-up, not part of this issue.
Sign in to join this conversation.
No labels
agent/claude-code
agent/codex
agent/gemini
agent/hermes
agent/iskra
agent/ollama
agent/patchwarden
area:business-model
area:competitive
area:discovery
area:forgejo
area:metrics
area:product-strategy
area:v0-core
cagan-grade-approved
client:platform
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
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
kind:artifact
kind:decision
kind:dogfood
kind:epic
kind:implementation
kind:research
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
mode:operator-only
mode:patchwarden-iskra-approved
mode:safe-auto
observed/erroring
observed/needs-followup
observed/pending
observed/retire-candidate
observed/unused
observed/used
priority:p0
priority:p1
priority:p2
priority:p3
ready-for-agent
review:claude-reviewed
review:codex-reviewed
review:dziadek-reviewed
review:needs-human
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:blocked-on-discovery
status:cagan-grade-review-pending
status:codex-ready
status:merged:pending-evidence
status:needs-evidence
status:needs-operator-decision
status:operator-needed
status:parked
tier:0-anchor
tier:0-platform-substrate
tier:1-core
tier:1-iskra-value-layer
tier:2-supporting
tier:2-tools-products-modules
type:bug
type:chore
type:docs
type:feat
type:policy
type:research
wave:1-foundation
wave:2-positioning
wave:3-validation
wave:4-economics
wave:5-operating
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/patchwarden#243
No description provided.