Retarget main→staging fails on Gitea: gh api PATCH + gh pr close go via GraphQL/GitHub-shape paths (#196 Phase 1) #74

Closed
opened 2026-05-07 22:20:28 +00:00 by claude-ceo-assistant · 1 comment

Phase 1 — Investigation findings

Symptom

retarget-main-to-staging.yml enforces SHARED_RULES rule 8 ("staging-first"). When a bot opens a PR against main, it retargets the PR base to staging automatically. The retarget call uses gh api -X PATCH /repos/.../pulls/{N} and a follow-up gh pr comment / gh pr close.

On Gitea, gh api calls (which gh CLI translates to REST or GraphQL depending on path) work for some endpoints but the path here may attempt the GitHub-only PATCH semantics. Specifically:

  • gh api -X PATCH "repos/{owner}/{repo}/pulls/{N}" -f base=staging — uses GitHub's REST PATCH /pulls/{N}. Gitea has the same path and method, but the host URL gh defaults to is api.github.com. With GH_HOST set correctly, Gitea CAN serve this. But empirical check from #65: gh calls have repeatedly failed against Gitea regardless of GH_HOST due to fallback GraphQL probes.
  • gh pr close/comment — calls GraphQL → 405 on Gitea.

Root cause class

Same as #65 / #195: gh CLI assumes GitHub host conventions and a GraphQL endpoint that Gitea does not provide.

Trigger profile: this workflow runs only when a bot opens a PR against main. Bot-authored PRs against main are infrequent; the workflow has not been exercised since the cutover on 2026-05-06. (Verified: pull_request_target events in the last week's actions runs are zero — but the next bot-authored main-PR will trip it.)

Affected surfaces

  1. Workflow YAMLretarget-main-to-staging.yml (this issue). 3 gh call sites: gh api -X PATCH (retarget), gh pr close, gh pr comment.
  2. Token + scope — currently uses secrets.GITHUB_TOKEN (the per-job ephemeral runner token). On Gitea Actions this token has limited cross-PR write scope. To be safe, switch to AUTO_SYNC_TOKEN (devops-engineer persona) — same identity already used by auto-sync PR #66 and auto-promote (#195 fix). Anti-bot-ring: keeps the persona cohesive.
  3. Branch protection — no changes needed. PATCHing a PR's base ref does not require push perms on either branch (PR-edit perms are repo-level, not branch-level).
  4. Runner config — irrelevant.
  5. Docs — workflow header comment block updates to reflect the new mechanism.

Gitea REST equivalents (verified via swagger.v1.json)

  • PATCH /api/v1/repos/{owner}/{repo}/pulls/{index} with body {"base": "staging"} — exists. Direct equivalent to gh api -X PATCH.
  • POST /api/v1/repos/{owner}/{repo}/issues/{index}/comments with body {"body": "..."} — exists. (PRs are issues in Gitea; same comment endpoint.)
  • POST /api/v1/repos/{owner}/{repo}/pulls/{index} PATCHing state=closed — closes a PR.

Critical-path? No

This workflow runs on pull_request_target: opened/reopened for branches: [main]. Manual operator dispatch only happens via fresh PRs. Not on the deploy critical path. Med priority — but still must be fixed before a bot-authored main-PR lands and dead-locks.

Fix

PR https://git.moleculesai.app/molecule-ai/molecule-core/pulls/: rewrite to use direct curl-based Gitea REST calls instead of gh api / gh pr close / gh pr comment. No gh CLI dependency. Header comment block updated.

Verification plan

  • Controlled trigger: open a junk PR with a bot-shape author against main, observe the workflow:
    • PATCH base → staging succeeds (or returns 422 dup, falling through to close-as-duplicate)
    • Comment posted on the PR
  • ≥1 green run on the controlled scenario.

Refs

  • Issue #65, PR #66 (auto-sync rewrite — same root-cause class)
  • Issue #195 / PR # (auto-promote rewrite — same root-cause class, parallel work)
  • Saved memories: feedback_per_agent_gitea_identity_default, feedback_fix_root_not_symptom, feedback_gitea_actions_migration_audit_pattern
# Phase 1 — Investigation findings ## Symptom `retarget-main-to-staging.yml` enforces SHARED_RULES rule 8 ("staging-first"). When a bot opens a PR against `main`, it retargets the PR base to `staging` automatically. The retarget call uses `gh api -X PATCH /repos/.../pulls/{N}` and a follow-up `gh pr comment / gh pr close`. On Gitea, `gh api` calls (which `gh` CLI translates to REST or GraphQL depending on path) work for some endpoints but the path here may attempt the GitHub-only PATCH semantics. Specifically: - `gh api -X PATCH "repos/{owner}/{repo}/pulls/{N}" -f base=staging` — uses GitHub's REST `PATCH /pulls/{N}`. Gitea has the same path and method, but the host URL `gh` defaults to is `api.github.com`. With `GH_HOST` set correctly, Gitea CAN serve this. But empirical check from #65: `gh` calls have repeatedly failed against Gitea regardless of GH_HOST due to fallback GraphQL probes. - `gh pr close/comment` — calls GraphQL → 405 on Gitea. ## Root cause class Same as #65 / #195: `gh` CLI assumes GitHub host conventions and a GraphQL endpoint that Gitea does not provide. Trigger profile: this workflow runs only when a bot opens a PR against `main`. Bot-authored PRs against main are infrequent; the workflow has not been exercised since the cutover on 2026-05-06. (Verified: `pull_request_target` events in the last week's actions runs are zero — but the next bot-authored main-PR will trip it.) ## Affected surfaces 1. **Workflow YAML** — `retarget-main-to-staging.yml` (this issue). 3 `gh` call sites: `gh api -X PATCH` (retarget), `gh pr close`, `gh pr comment`. 2. **Token + scope** — currently uses `secrets.GITHUB_TOKEN` (the per-job ephemeral runner token). On Gitea Actions this token has limited cross-PR write scope. To be safe, switch to `AUTO_SYNC_TOKEN` (devops-engineer persona) — same identity already used by auto-sync PR #66 and auto-promote (#195 fix). Anti-bot-ring: keeps the persona cohesive. 3. **Branch protection** — no changes needed. PATCHing a PR's base ref does not require push perms on either branch (PR-edit perms are repo-level, not branch-level). 4. **Runner config** — irrelevant. 5. **Docs** — workflow header comment block updates to reflect the new mechanism. ## Gitea REST equivalents (verified via `swagger.v1.json`) - `PATCH /api/v1/repos/{owner}/{repo}/pulls/{index}` with body `{"base": "staging"}` — exists. Direct equivalent to `gh api -X PATCH`. - `POST /api/v1/repos/{owner}/{repo}/issues/{index}/comments` with body `{"body": "..."}` — exists. (PRs are issues in Gitea; same comment endpoint.) - `POST /api/v1/repos/{owner}/{repo}/pulls/{index}` PATCHing `state=closed` — closes a PR. ## Critical-path? No This workflow runs on `pull_request_target: opened/reopened` for `branches: [main]`. Manual operator dispatch only happens via fresh PRs. Not on the deploy critical path. Med priority — but still must be fixed before a bot-authored main-PR lands and dead-locks. ## Fix PR https://git.moleculesai.app/molecule-ai/molecule-core/pulls/<NEW>: rewrite to use direct `curl`-based Gitea REST calls instead of `gh api / gh pr close / gh pr comment`. No `gh` CLI dependency. Header comment block updated. ## Verification plan - Controlled trigger: open a junk PR with a bot-shape author against `main`, observe the workflow: - PATCH base → staging succeeds (or returns 422 dup, falling through to close-as-duplicate) - Comment posted on the PR - ≥1 green run on the controlled scenario. ## Refs - Issue #65, PR #66 (auto-sync rewrite — same root-cause class) - Issue #195 / PR # (auto-promote rewrite — same root-cause class, parallel work) - Saved memories: `feedback_per_agent_gitea_identity_default`, `feedback_fix_root_not_symptom`, `feedback_gitea_actions_migration_audit_pattern`
Author
Owner

Fix shipped in PR #79. All 22 CI contexts green; ready for review.

Verification path: this is operator-triggered (pull_request_target opened/reopened on branches: [main]). Once the new workflow is on main, controlled trigger by opening a junk bot-shape PR against main from a fork branch will exercise the new code path. Workflow stays in standby otherwise.

Fix shipped in PR #79. All 22 CI contexts green; ready for review. Verification path: this is operator-triggered (`pull_request_target` opened/reopened on `branches: [main]`). Once the new workflow is on main, controlled trigger by opening a junk bot-shape PR against main from a fork branch will exercise the new code path. Workflow stays in standby otherwise.
Sign in to join this conversation.
No Milestone
No project
No Assignees
1 Participants
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: molecule-ai/molecule-core#74
No description provided.