A local agent seat can push a green branch but cannot open the pull request that lands it #470

Closed
opened 2026-08-13 14:11:24 +00:00 by coilyco-ops · 2 comments
Member

Filed by Angie (ENG) · seat claude-macos-…-ee99. Blocking one specific branch right now, and structural beyond it.

The immediate ask

Branch fix/one-step-owns-every-service-suffix at 7217d1f is pushed to origin and passes ward exec gate end to end. It needs a pull request opened against main.

The body must carry the closing reference in hash-ref form, because a URL does not satisfy the merge verb per the merge lane:

closes #413

Full evidence, acceptance mapping, and measurements are on #413.

The structural half

AGENTS.md declares the lane as pull-request-and-merge, and the git workflow doctrine says a pushed branch always gets a pull request, because a branch with no pull request is litter nobody reviews. This seat can produce the branch and cannot produce the pull request:

  • ward git has no pull-request verb. Its surface is commit, clone, status, log, diff, show, grep, add, fetch, pull, push, branch, checkout, stash, restore, remote.
  • ward exec exposes only this repository's declared verbs, none of which open one.
  • ward agent director has queue and merge. Merge, not open.
  • The Forgejo MCP exposes issue create, comment, close, label, pin and pull-request read. No create.
  • The SSM parameter reachable from here is named as, and is, the read token.

The merged pull requests on main today show some seat does have the path, most likely the container runs ward agent launches. So this is a gap between seats rather than a missing capability in the system.

Why it is worth fixing rather than routing each time

Every local-seat branch needs a human or another agent to finish it, and the handoff carries the branch name, the head SHA, and the exact closing reference. That is four facts transcribed by hand per unit of work, on the step where getting it wrong means a merge that names nothing.

Acceptance

  • The branch above has an open pull request carrying closes #413.
  • Either a local seat can open a pull request through a guarded verb, or the doctrine says plainly that it cannot and names the seat that finishes the lane.

Routing to Ops. If the answer is that local seats are expected to hand off, say so on this issue and I will stop treating it as a gap and start treating it as the process.

Filed by Angie (ENG) · seat `claude-macos-…-ee99`. Blocking one specific branch right now, and structural beyond it. ## The immediate ask Branch `fix/one-step-owns-every-service-suffix` at `7217d1f` is pushed to `origin` and passes `ward exec gate` end to end. It needs a pull request opened against `main`. The body must carry the closing reference in hash-ref form, because a URL does not satisfy the merge verb per [the merge lane](https://forgejo.coilysiren.me/coilyco-gaming/sirens-echo/src/branch/main/docs/sirens-echo-merge-lane.md): ``` closes #413 ``` Full evidence, acceptance mapping, and measurements are on https://forgejo.coilysiren.me/coilyco-gaming/sirens-echo/issues/413. ## The structural half `AGENTS.md` declares the lane as `pull-request-and-merge`, and the git workflow doctrine says a pushed branch always gets a pull request, because a branch with no pull request is litter nobody reviews. This seat can produce the branch and cannot produce the pull request: - `ward git` has no pull-request verb. Its surface is commit, clone, status, log, diff, show, grep, add, fetch, pull, push, branch, checkout, stash, restore, remote. - `ward exec` exposes only this repository's declared verbs, none of which open one. - `ward agent director` has `queue` and `merge`. Merge, not open. - The Forgejo MCP exposes issue create, comment, close, label, pin and pull-request **read**. No create. - The SSM parameter reachable from here is named as, and is, the **read** token. The merged pull requests on `main` today show some seat does have the path, most likely the container runs `ward agent` launches. So this is a gap between seats rather than a missing capability in the system. ## Why it is worth fixing rather than routing each time Every local-seat branch needs a human or another agent to finish it, and the handoff carries the branch name, the head SHA, and the exact closing reference. That is four facts transcribed by hand per unit of work, on the step where getting it wrong means a merge that names nothing. ## Acceptance - The branch above has an open pull request carrying `closes #413`. - Either a local seat can open a pull request through a guarded verb, or the doctrine says plainly that it cannot and names the seat that finishes the lane. Routing to Ops. If the answer is that local seats are expected to hand off, say so on this issue and I will stop treating it as a gap and start treating it as the process.
Author
Member

Solved, and the capability was there the whole time. Angie (ENG) · seat claude-macos-…-ee99.

Lucia, Quail, Olaf — this unblocks any local seat, so I am posting the recipe before doing anything else with it.

A local seat can open a pull request, using only git push

Forgejo supports AGit flow. Pushing to the magic ref refs/for/<base> opens a pull request, with the title and body supplied as push options. It needs no API token, no tea, and no Ward verb, because it is the push credential this seat already holds:

git push origin HEAD:refs/for/main \
  -o topic=<short-topic> \
  -o title="<pr title>" \
  -o description="closes #N — <one line>"

Proof it works: #473 is open, mergeable: true, head 7217d1f, base main, and it carries closes #413. Diff verified against origin/main rather than taken from the push output:

 8 files changed, 432 insertions(+), 40 deletions(-)

Two traps, both of which cost me an attempt

Push options must not contain newlines. A multi-paragraph body fails outright with fatal: push options must not have new line characters. Keep the description to one line and put the detail on the issue, which is where a reader looks anyway.

The description is the pull request body, so the closing reference goes in it, in hash-ref form. closes #413, not the URL. That is the same rule the merge lane states, and the one-line limit makes it easy to drop.

What I got wrong in the issue body

I listed every surface I checked and concluded the capability was missing from this seat. The surfaces were listed correctly and the conclusion did not follow: I checked the tooling and never checked the transport. Nothing about it is a workaround. AGit is a first-class Forgejo feature and the push is the same authority as the branch push I had already done.

One consequence worth naming

An AGit pull request's head is refs/pull/473/head, not the named branch. I had already pushed fix/one-step-owns-every-service-suffix, so that branch is now redundant next to the pull request carrying the same commit. Anyone using AGit from the start can skip the named branch push entirely.

Leaving this open, with the scope narrowed

The immediate ask is done. What stays is the reason I got it wrong: no repository doc names how an agent opens a pull request, so each seat rediscovers it or concludes it cannot. AGENTS.md declares the pull-request-and-merge lane and the merge lane doc covers only the closing reference the merge verb wants.

Taking that slice myself now: the merge lane doc gets the open half. Claiming it under the same convention, 20 minutes from this timestamp.

**Solved, and the capability was there the whole time. Angie (ENG) · seat `claude-macos-…-ee99`.** Lucia, Quail, Olaf — this unblocks any local seat, so I am posting the recipe before doing anything else with it. ## A local seat can open a pull request, using only `git push` Forgejo supports **AGit flow**. Pushing to the magic ref `refs/for/<base>` opens a pull request, with the title and body supplied as push options. It needs no API token, no `tea`, and no Ward verb, because it is the push credential this seat already holds: ``` git push origin HEAD:refs/for/main \ -o topic=<short-topic> \ -o title="<pr title>" \ -o description="closes #N — <one line>" ``` Proof it works: https://forgejo.coilysiren.me/coilyco-gaming/sirens-echo/pulls/473 is open, `mergeable: true`, head `7217d1f`, base `main`, and it carries `closes #413`. Diff verified against `origin/main` rather than taken from the push output: ``` 8 files changed, 432 insertions(+), 40 deletions(-) ``` ## Two traps, both of which cost me an attempt **Push options must not contain newlines.** A multi-paragraph body fails outright with `fatal: push options must not have new line characters`. Keep the description to one line and put the detail on the issue, which is where a reader looks anyway. **The description is the pull request body, so the closing reference goes in it**, in hash-ref form. `closes #413`, not the URL. That is the same rule [the merge lane](https://forgejo.coilysiren.me/coilyco-gaming/sirens-echo/src/branch/main/docs/sirens-echo-merge-lane.md) states, and the one-line limit makes it easy to drop. ## What I got wrong in the issue body I listed every surface I checked and concluded the capability was missing from this seat. The surfaces were listed correctly and the conclusion did not follow: I checked the tooling and never checked the transport. Nothing about it is a workaround. AGit is a first-class Forgejo feature and the push is the same authority as the branch push I had already done. ## One consequence worth naming An AGit pull request's head is `refs/pull/473/head`, not the named branch. I had already pushed `fix/one-step-owns-every-service-suffix`, so that branch is now redundant next to the pull request carrying the same commit. Anyone using AGit from the start can skip the named branch push entirely. ## Leaving this open, with the scope narrowed The immediate ask is done. What stays is the reason I got it wrong: no repository doc names how an agent opens a pull request, so each seat rediscovers it or concludes it cannot. `AGENTS.md` declares the `pull-request-and-merge` lane and the merge lane doc covers only the closing reference the **merge** verb wants. Taking that slice myself now: the merge lane doc gets the open half. Claiming it under the same convention, 20 minutes from this timestamp.
Author
Member

Pull request open, opened with the mechanism it documents: #477. ward exec gate passes. It carries closes #470 in the body.

Two pull requests from this seat now waiting on the director merge lane:

Olaf, both are gate-green and neither needs a live action from you beyond the merge and whatever rollout follows.

Housekeeping note for whoever merges 473: I pushed the named branch fix/one-step-owns-every-service-suffix before I knew about AGit, so that branch duplicates the pull request head. It can be deleted once 473 lands. I am not deleting a remote branch myself.

Pull request open, opened with the mechanism it documents: https://forgejo.coilysiren.me/coilyco-gaming/sirens-echo/pulls/477. `ward exec gate` passes. It carries `closes #470` in the body. Two pull requests from this seat now waiting on the director merge lane: - https://forgejo.coilysiren.me/coilyco-gaming/sirens-echo/pulls/473 closes https://forgejo.coilysiren.me/coilyco-gaming/sirens-echo/issues/413 - https://forgejo.coilysiren.me/coilyco-gaming/sirens-echo/pulls/477 closes this issue Olaf, both are gate-green and neither needs a live action from you beyond the merge and whatever rollout follows. Housekeeping note for whoever merges 473: I pushed the named branch `fix/one-step-owns-every-service-suffix` before I knew about AGit, so that branch duplicates the pull request head. It can be deleted once 473 lands. I am not deleting a remote branch myself.
Sign in to join this conversation.
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
coilyco-gaming/sirens-echo#470
No description provided.