Native PR workflow needs close, reopen, and closed-unmerged recovery commands #1173

Closed
opened 2026-07-13 04:24:08 +00:00 by coilyco-ops · 8 comments
Member

Problem

The director PR toolset can list, view, status-check, rerun, and merge, but it does not expose safe native close/reopen/recover operations for PRs. That leaves the burn-down lane stuck when a PR is known superseded or when a failed merge leaves a PR closed but unmerged.

Observed on 2026-07-13:

  • ward ops forgejo pr --help exposed only view and list in the ops layer.
  • The native ward agent pr lane had merge/status/runs/rerun, but no close/reopen/recover path was available from the director surface.
  • PR #987 is now known superseded by the ward#1163 fix, but the director cannot close it cleanly through ward.
  • PR #1164 became state: closed, merged: false after repeated native merge 405s, and the director has no safe reopen/retry/salvage path.

Do

Add native PR lifecycle commands through ward, gated by the same role x workflow permission model as the existing PR tools:

  • ward agent pr close <owner/repo#PR> with a required reason or superseding issue/PR reference.
  • ward agent pr reopen <owner/repo#PR> for closed-unmerged PRs when the branch is still recoverable.
  • ward agent pr recover <owner/repo#PR> or equivalent diagnosis for closed && merged=false, reporting head SHA, linked issue, and next safe action.
  • Head-pin and postcondition-check all mutations.
  • Ensure these commands do not depend on runtime KDL specgen.

Acceptance

  • A director can close a superseded PR without raw Forgejo/API fallback.
  • A director can reopen or diagnose a closed-unmerged PR with a deterministic ward command.
  • The command refuses ambiguous destructive actions without an explicit reason and reports the PR head SHA before and after.
  • Tests cover role/mode gating and the closed-unmerged recovery path.

Related: ward#1067, ward#1168, ward#1170, ward#987, ward#1164.

## Problem The director PR toolset can list, view, status-check, rerun, and merge, but it does not expose safe native close/reopen/recover operations for PRs. That leaves the burn-down lane stuck when a PR is known superseded or when a failed merge leaves a PR closed but unmerged. Observed on 2026-07-13: * `ward ops forgejo pr --help` exposed only `view` and `list` in the ops layer. * The native `ward agent pr` lane had merge/status/runs/rerun, but no close/reopen/recover path was available from the director surface. * PR #987 is now known superseded by the ward#1163 fix, but the director cannot close it cleanly through ward. * PR #1164 became `state: closed`, `merged: false` after repeated native merge 405s, and the director has no safe reopen/retry/salvage path. ## Do Add native PR lifecycle commands through ward, gated by the same role x workflow permission model as the existing PR tools: * `ward agent pr close <owner/repo#PR>` with a required reason or superseding issue/PR reference. * `ward agent pr reopen <owner/repo#PR>` for closed-unmerged PRs when the branch is still recoverable. * `ward agent pr recover <owner/repo#PR>` or equivalent diagnosis for `closed && merged=false`, reporting head SHA, linked issue, and next safe action. * Head-pin and postcondition-check all mutations. * Ensure these commands do not depend on runtime KDL specgen. ## Acceptance * A director can close a superseded PR without raw Forgejo/API fallback. * A director can reopen or diagnose a closed-unmerged PR with a deterministic ward command. * The command refuses ambiguous destructive actions without an explicit reason and reports the PR head SHA before and after. * Tests cover role/mode gating and the closed-unmerged recovery path. Related: ward#1067, ward#1168, ward#1170, ward#987, ward#1164.
Author
Member

The burn-down session confirms this gap from direct experience. The ward ops forgejo surface ships no pr merge, pr update, pr close, or pr reopen verbs - only pr view and pr list - and the native ward agent pr merge kept returning the ward#1168 405 for every PR in the jam. The session fell back to ward ops forgejo issue close against the PR index for all ten closes, which works because PRs share the issue index, but it is a workaround with no reopen path at all and no way to record merged-by-push provenance beyond a comment. Close, reopen, and closed-unmerged recovery deserve first-class verbs in the broker surface, per ward#1067.

The burn-down session confirms this gap from direct experience. The ward ops forgejo surface ships no pr merge, pr update, pr close, or pr reopen verbs - only pr view and pr list - and the native ward agent pr merge kept returning the ward#1168 405 for every PR in the jam. The session fell back to ward ops forgejo issue close against the PR index for all ten closes, which works because PRs share the issue index, but it is a workaround with no reopen path at all and no way to record merged-by-push provenance beyond a comment. Close, reopen, and closed-unmerged recovery deserve first-class verbs in the broker surface, per ward#1067.
Author
Member

Additional director burn-down evidence from 2026-07-14: after PR #1235 was closed unmerged because it missed ward#1105 acceptance, warded director queue --repo coilyco-flight-deck/ward --limit 120 still classified ward#1105 as merge-ready PR awaiting merge and listed next: merge PR, even though the only PR for that run was closed. The queue needs to treat closed-unmerged PRs as recovery/redo state, not merge-ready work.

Additional director burn-down evidence from 2026-07-14: after PR #1235 was closed unmerged because it missed ward#1105 acceptance, `warded director queue --repo coilyco-flight-deck/ward --limit 120` still classified ward#1105 as `merge-ready PR awaiting merge` and listed `next: merge PR`, even though the only PR for that run was closed. The queue needs to treat closed-unmerged PRs as recovery/redo state, not merge-ready work.
Author
Member

WARDED_WORKFLOW: reservation-held

reservation details

Holder: launch intent for container engineer-codex-ward-1173 on host kais-macbook-pro-2.local.

Accepted by ward agent --harness codex (reserved 2026-07-14T11:21:18Z). Concurrent ward agent runs are blocked until this intent becomes visible or the intent is released. The stale-intent fallback is still TTL-bounded (3h TTL). --override-reservation overrides.

Do not comment on or edit this issue to steer the run while it is reserved. The engineer seeded the body once at launch and never re-reads it, so a comment or edit reaches only human readers, never the running engineer. A correction goes to a new issue, dispatched fresh. That is the only channel that reaches a run in flight. Where the forge supports it, ward locks this conversation to make that a road-block rather than a convention (ward#494).

run seed context — what this run is carrying (ward#609)
  • Resolved: coilyco-flight-deck/ward#1173 · branch issue-1173 · harness codex · workflow pull-request-and-merge
  • Run: engineer-codex-ward-1173 · ward v0.679.0 · dispatched 2026-07-14T11:21:11Z
  • Comment thread: 2 included in the pre-flight read, 0 stripped (ward's own automated comments).

Static container doctrine and seed boilerplate are identical every run and omitted here (they ride ward v0.679.0).

— Codex, via ward agent

<!-- ward-agent-reservation --> WARDED_WORKFLOW: reservation-held <details><summary>reservation details</summary> Holder: launch intent for container `engineer-codex-ward-1173` on host `kais-macbook-pro-2.local`. Accepted by `ward agent --harness codex` (reserved 2026-07-14T11:21:18Z). Concurrent `ward agent` runs are blocked until this intent becomes visible or the intent is released. The stale-intent fallback is still TTL-bounded (3h TTL). `--override-reservation` overrides. **Do not comment on or edit this issue to steer the run while it is reserved.** The engineer seeded the body once at launch and never re-reads it, so a comment or edit reaches only human readers, never the running engineer. A correction goes to a **new issue, dispatched fresh**. That is the only channel that reaches a run in flight. Where the forge supports it, ward locks this conversation to make that a road-block rather than a convention (ward#494). <details><summary>run seed context — what this run is carrying (ward#609)</summary> - **Resolved:** `coilyco-flight-deck/ward#1173` · branch `issue-1173` · harness `codex` · workflow `pull-request-and-merge` - **Run:** `engineer-codex-ward-1173` · ward `v0.679.0` · dispatched `2026-07-14T11:21:11Z` - **Comment thread:** 2 included in the pre-flight read, 0 stripped (ward's own automated comments). - included: @coilyco-ops (2026-07-13T09:23:30Z), @coilyco-ops (2026-07-14T07:28:34Z) Static container doctrine and seed boilerplate are identical every run and omitted here (they ride ward v0.679.0). </details> </details> <!-- ward-agent-signature --> — Codex, via `ward agent`
Author
Member

WARDED_WORKFLOW: failed

details

workflow: pull-request-and-merge; review summary: review gate skipped by ~/.ward/config.yaml default
status: Forgejo PR #1280 failed again in golangci-lint after go vet and go test succeeded. The job page exposed no log text and the raw logs endpoint returned 404, so I could not extract the exact lint diagnostic here.
retrospective: the PR workflow feature landed cleanly. The blocker is runner/tooling space around the lint step, not the PR lifecycle code.
confidence: medium
follow-up: inspect the Forgejo runner scratch/tool cache for the lint job, or rerun the workflow with a known-good temp/cache mount.

WARDED_WORKFLOW: failed ❌ <details><summary>details</summary> workflow: pull-request-and-merge; review summary: review gate skipped by ~/.ward/config.yaml default status: Forgejo PR #1280 failed again in `golangci-lint` after `go vet` and `go test` succeeded. The job page exposed no log text and the raw logs endpoint returned 404, so I could not extract the exact lint diagnostic here. retrospective: the PR workflow feature landed cleanly. The blocker is runner/tooling space around the lint step, not the PR lifecycle code. confidence: medium follow-up: inspect the Forgejo runner scratch/tool cache for the lint job, or rerun the workflow with a known-good temp/cache mount. </details>
Author
Member

Director burn-down update from 2026-07-14: PR #1280 is open but Forgejo reports mergeable=false. ward ops forgejo pr update coilyco-flight-deck ward 1280 returned 409 Conflict with merge failed because of conflict, so this branch needs a conflict repair against current main before director merge can proceed. New engineer dispatch is currently blocked by agentic-os#532 because the host cannot launch agentic-os-full:latest.

Director burn-down update from 2026-07-14: PR #1280 is open but Forgejo reports `mergeable=false`. `ward ops forgejo pr update coilyco-flight-deck ward 1280` returned 409 Conflict with `merge failed because of conflict`, so this branch needs a conflict repair against current main before director merge can proceed. New engineer dispatch is currently blocked by agentic-os#532 because the host cannot launch `agentic-os-full:latest`.
Author
Member

WARDED_WORKFLOW: reservation-held

reservation details

Holder: launch intent for container engineer-codex-ward-1173 on host kais-macbook-pro-2.local.

Accepted by ward agent --harness codex (reserved 2026-07-14T18:51:40Z). Concurrent ward agent runs are blocked until this intent becomes visible or the intent is released. The stale-intent fallback is still TTL-bounded (3h TTL). --override-reservation overrides.

Do not comment on or edit this issue to steer the run while it is reserved. The engineer seeded the body once at launch and never re-reads it, so a comment or edit reaches only human readers, never the running engineer. A correction goes to a new issue, dispatched fresh. That is the only channel that reaches a run in flight. Where the forge supports it, ward locks this conversation to make that a road-block rather than a convention (ward#494).

run seed context — what this run is carrying (ward#609)
  • Resolved: coilyco-flight-deck/ward#1173 · branch issue-1173 · harness codex · workflow pull-request-and-merge
  • Run: engineer-codex-ward-1173 · ward v0.689.0 · dispatched 2026-07-14T18:51:31Z
  • Comment thread: 4 included in the pre-flight read, 1 stripped (ward's own automated comments).

Static container doctrine and seed boilerplate are identical every run and omitted here (they ride ward v0.689.0).

— Codex, via ward agent

<!-- ward-agent-reservation --> WARDED_WORKFLOW: reservation-held <details><summary>reservation details</summary> Holder: launch intent for container `engineer-codex-ward-1173` on host `kais-macbook-pro-2.local`. Accepted by `ward agent --harness codex` (reserved 2026-07-14T18:51:40Z). Concurrent `ward agent` runs are blocked until this intent becomes visible or the intent is released. The stale-intent fallback is still TTL-bounded (3h TTL). `--override-reservation` overrides. **Do not comment on or edit this issue to steer the run while it is reserved.** The engineer seeded the body once at launch and never re-reads it, so a comment or edit reaches only human readers, never the running engineer. A correction goes to a **new issue, dispatched fresh**. That is the only channel that reaches a run in flight. Where the forge supports it, ward locks this conversation to make that a road-block rather than a convention (ward#494). <details><summary>run seed context — what this run is carrying (ward#609)</summary> - **Resolved:** `coilyco-flight-deck/ward#1173` · branch `issue-1173` · harness `codex` · workflow `pull-request-and-merge` - **Run:** `engineer-codex-ward-1173` · ward `v0.689.0` · dispatched `2026-07-14T18:51:31Z` - **Comment thread:** 4 included in the pre-flight read, 1 stripped (ward's own automated comments). - included: @coilyco-ops (2026-07-13T09:23:30Z), @coilyco-ops (2026-07-14T07:28:34Z), @coilyco-ops (2026-07-14T12:09:41Z), @coilyco-ops (2026-07-14T12:12:20Z) - stripped: @coilyco-ops (2026-07-14T11:21:19Z) Static container doctrine and seed boilerplate are identical every run and omitted here (they ride ward v0.689.0). </details> </details> <!-- ward-agent-signature --> — Codex, via `ward agent`
Author
Member

WARDED_WORKFLOW: reservation-held

reservation details

Holder: launch intent for container engineer-codex-ward-1173 on host kais-macbook-pro-2.local.

Accepted by ward agent --harness codex (reserved 2026-07-15T00:59:33Z). Concurrent ward agent runs are blocked until this intent becomes visible or the intent is released. The stale-intent fallback is still TTL-bounded (3h TTL). --override-reservation overrides.

Do not comment on or edit this issue to steer the run while it is reserved. The engineer seeded the body once at launch and never re-reads it, so a comment or edit reaches only human readers, never the running engineer. A correction goes to a new issue, dispatched fresh. That is the only channel that reaches a run in flight. Where the forge supports it, ward locks this conversation to make that a road-block rather than a convention (ward#494).

run seed context — what this run is carrying (ward#609)
  • Resolved: coilyco-flight-deck/ward#1173 · branch issue-1173 · harness codex · workflow pull-request-and-merge
  • Run: engineer-codex-ward-1173 · ward v0.693.0 · dispatched 2026-07-15T00:59:25Z
  • Comment thread: 4 included in the pre-flight read, 2 stripped (ward's own automated comments).

Static container doctrine and seed boilerplate are identical every run and omitted here (they ride ward v0.693.0).

— Codex, via ward agent

<!-- ward-agent-reservation --> WARDED_WORKFLOW: reservation-held <details><summary>reservation details</summary> Holder: launch intent for container `engineer-codex-ward-1173` on host `kais-macbook-pro-2.local`. Accepted by `ward agent --harness codex` (reserved 2026-07-15T00:59:33Z). Concurrent `ward agent` runs are blocked until this intent becomes visible or the intent is released. The stale-intent fallback is still TTL-bounded (3h TTL). `--override-reservation` overrides. **Do not comment on or edit this issue to steer the run while it is reserved.** The engineer seeded the body once at launch and never re-reads it, so a comment or edit reaches only human readers, never the running engineer. A correction goes to a **new issue, dispatched fresh**. That is the only channel that reaches a run in flight. Where the forge supports it, ward locks this conversation to make that a road-block rather than a convention (ward#494). <details><summary>run seed context — what this run is carrying (ward#609)</summary> - **Resolved:** `coilyco-flight-deck/ward#1173` · branch `issue-1173` · harness `codex` · workflow `pull-request-and-merge` - **Run:** `engineer-codex-ward-1173` · ward `v0.693.0` · dispatched `2026-07-15T00:59:25Z` - **Comment thread:** 4 included in the pre-flight read, 2 stripped (ward's own automated comments). - included: @coilyco-ops (2026-07-13T09:23:30Z), @coilyco-ops (2026-07-14T07:28:34Z), @coilyco-ops (2026-07-14T12:09:41Z), @coilyco-ops (2026-07-14T12:12:20Z) - stripped: @coilyco-ops (2026-07-14T11:21:19Z), @coilyco-ops (2026-07-14T18:51:41Z) Static container doctrine and seed boilerplate are identical every run and omitted here (they ride ward v0.693.0). </details> </details> <!-- ward-agent-signature --> — Codex, via `ward agent`
Author
Member

WARDED_WORKFLOW: #1280

details

workflow: pull-request-and-merge; review summary: review gate intentionally skipped by temporary ward default pending brokered QA
retrospective: this felt like a merge-repair pass with a couple of environment detours.
confidence: high
surprises: the branch needed a merge from main, one fake reopen endpoint was missing, and the host scratch space was the last blocker.
follow-ups: watch Forgejo reflect the merge and let the issue close cleanly.

WARDED_WORKFLOW: https://forgejo.coilysiren.me/coilyco-flight-deck/ward/pulls/1280 <details><summary>details</summary> workflow: pull-request-and-merge; review summary: review gate intentionally skipped by temporary ward default pending brokered QA retrospective: this felt like a merge-repair pass with a couple of environment detours. confidence: high surprises: the branch needed a merge from main, one fake reopen endpoint was missing, and the host scratch space was the last blocker. follow-ups: watch Forgejo reflect the merge and let the issue close cleanly. </details>
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-flight-deck/ward#1173
No description provided.