feat(ward-specs): add the full PR lifecycle (close, reopen, edit, update) to the forgejo KDL spec #489

Merged
coilysiren merged 1 commit from issue-488 into main 2026-07-13 05:25:50 +00:00
Member

What

Adds the coherent pull-request lifecycle to the forgejo KDL spec bundle, so a stranded or empty PR is resolvable through ward instead of the web UI.

  • guardfile.forgejo.kdl (the live ward ops forgejo surface, per ward.bundle.kdl) - grows pr files / pr commits reads plus pr create / pr edit / pr close / pr reopen / pr update. Close and reopen are fixed-body state toggles on repoEditPullRequest (the milestone/issue pattern), update is repoUpdatePullRequest (pull a stale head branch current). Merge stays out of the monolith, so the merge gate is unchanged.
  • guardfile.forgejo.read.kdl - trap 2 reconciled: the never pair over pr view/list was dead (the monolith granted both all along and the live director surface served them), and since deny inherits up the chain it would have silently shadowed every pr leaf an upper tier ever grew. Replaced with the pr read grants (view/list/files/commits), so the tier chain now agrees with the monolith.
  • guardfile.forgejo.merge.kdl - re-described as the PR-lifecycle overlay per the issue's Where: the mutating verbs ride it beside the untouched merge pair, outside the read/write/admin chain, bound to the director/engineer/pm roles only (roles.kdl).
  • forgejo.swagger.lock.json - trap 1: regenerated with the cli-guard kdl-specs lock --guardfile driver (v0.94.0 pinned, the committed lock's own provenance - same path as 1e239ed), not hand-edited. The diff is a strict superset: the 5 new operationIds (repoEditPullRequest, repoUpdatePullRequest, repoCreatePullRequest, repoGetPullRequestFiles, repoGetPullRequestCommits) plus 3 transitive definitions. specverb.lock came out byte-identical.
  • docs/ward-ops-forgejo-reference.md - regenerated via ward exec gen-ward-ops-reference against the edited bundle, so the new verb names are greppable off-disk.
  • tests/test_ward_specs_bundle.py - pins the new surface: lifecycle ops present in the lock, denials gone from the read tier, merge absent from the monolith, verbs present in the committed render.

Validation

  • ward doctor against the edited bundle: all checks pass (the ops bundle mounts fail-closed on any unresolvable operationId - the aos#452/ward#1123 failure mode).
  • ward ops forgejo pr close coilyco-bridge agentic-os-kai 721 --dry-run resolves to PATCH .../pulls/721 with body {"state": "closed"} - the issue's done-condition command, rehearsed end to end minus the fire.
  • ward exec surface-check passes, ward exec test 459 passed, pre-commit run --all-files clean.

Notes

  • The merge pair (repoMergePullRequest / repoPullRequestIsMerged) is still not in the lock: the lock is the monolith member's pruned contract, and the merge overlay is a separate ward-kdl-merge binary whose runtime mounting is ward-side work (ward#1072). Nothing regressed - that is today's state, and the driver output keeps it honest.
  • Follow-up cleanup of the 6 stranded PRs is dispatched separately once this lands, per the issue.

closes #488

ward.workflow: pull-request-and-merge

## What Adds the coherent pull-request lifecycle to the forgejo KDL spec bundle, so a stranded or empty PR is resolvable through ward instead of the web UI. * **`guardfile.forgejo.kdl`** (the live `ward ops forgejo` surface, per `ward.bundle.kdl`) - grows `pr files` / `pr commits` reads plus `pr create` / `pr edit` / `pr close` / `pr reopen` / `pr update`. Close and reopen are fixed-body state toggles on `repoEditPullRequest` (the milestone/issue pattern), update is `repoUpdatePullRequest` (pull a stale head branch current). Merge stays out of the monolith, so the merge gate is unchanged. * **`guardfile.forgejo.read.kdl`** - trap 2 reconciled: the `never` pair over pr view/list was dead (the monolith granted both all along and the live director surface served them), and since deny inherits up the chain it would have silently shadowed every pr leaf an upper tier ever grew. Replaced with the pr read grants (view/list/files/commits), so the tier chain now agrees with the monolith. * **`guardfile.forgejo.merge.kdl`** - re-described as the PR-lifecycle overlay per the issue's Where: the mutating verbs ride it beside the untouched merge pair, outside the read/write/admin chain, bound to the director/engineer/pm roles only (roles.kdl). * **`forgejo.swagger.lock.json`** - trap 1: regenerated with the cli-guard `kdl-specs lock --guardfile` driver (v0.94.0 pinned, the committed lock's own provenance - same path as 1e239ed), not hand-edited. The diff is a strict superset: the 5 new operationIds (`repoEditPullRequest`, `repoUpdatePullRequest`, `repoCreatePullRequest`, `repoGetPullRequestFiles`, `repoGetPullRequestCommits`) plus 3 transitive definitions. `specverb.lock` came out byte-identical. * **`docs/ward-ops-forgejo-reference.md`** - regenerated via `ward exec gen-ward-ops-reference` against the edited bundle, so the new verb names are greppable off-disk. * **`tests/test_ward_specs_bundle.py`** - pins the new surface: lifecycle ops present in the lock, denials gone from the read tier, merge absent from the monolith, verbs present in the committed render. ## Validation * `ward doctor` against the edited bundle: all checks pass (the ops bundle mounts fail-closed on any unresolvable operationId - the aos#452/ward#1123 failure mode). * `ward ops forgejo pr close coilyco-bridge agentic-os-kai 721 --dry-run` resolves to `PATCH .../pulls/721` with body `{"state": "closed"}` - the issue's done-condition command, rehearsed end to end minus the fire. * `ward exec surface-check` passes, `ward exec test` 459 passed, `pre-commit run --all-files` clean. ## Notes * The merge pair (`repoMergePullRequest` / `repoPullRequestIsMerged`) is still not in the lock: the lock is the monolith member's pruned contract, and the merge overlay is a separate `ward-kdl-merge` binary whose runtime mounting is ward-side work (ward#1072). Nothing regressed - that is today's state, and the driver output keeps it honest. * Follow-up cleanup of the 6 stranded PRs is dispatched separately once this lands, per the issue. closes #488 ward.workflow: pull-request-and-merge
feat(ward-specs): add the full PR lifecycle to the forgejo KDL spec
All checks were successful
ci / gate (pull_request) Successful in 1m7s
dda8f08a60
The stranded-PR sweep (agentic-os#488) found 6 open PRs no ward verb could
resolve: the pr surface stopped at view/list. This adds the coherent
lifecycle set, keeping the merge gate exactly where it was.

* guardfile.forgejo.kdl (the live `ward ops forgejo` surface via
  ward.bundle.kdl) grows files/commits reads plus create/edit/close/
  reopen/update - close and reopen as fixed-body state toggles on
  repoEditPullRequest, update as repoUpdatePullRequest to pull a stale
  head branch current. Merge stays out of the monolith, so the gate is
  unchanged.
* guardfile.forgejo.read.kdl drops the dead `never` pair over pr
  view/list (the monolith granted both all along and the live surface
  served them; deny inherits up, so the pair would have silently
  shadowed every pr leaf an upper tier ever grew) and grants the pr
  read side instead, reconciling the tier chain with the monolith.
* guardfile.forgejo.merge.kdl is re-described as the PR-lifecycle
  overlay and carries the mutating verbs beside the untouched merge
  pair - outside the read/write/admin chain, bound to the director/
  engineer/pm roles only (roles.kdl), the blast radius the issue asked
  for.
* forgejo.swagger.lock.json regenerated with the cli-guard kdl-specs
  driver (lock --guardfile, v0.94.0 pinned): a strict superset - the 5
  new operationIds plus their transitive definitions, specverb.lock
  byte-identical. Validated by `ward doctor` (ops bundle mounts
  fail-closed) and a --dry-run of the issue's done-condition command.
* docs/ward-ops-forgejo-reference.md regenerated so the new verb names
  are greppable off-disk; bundle tests pin the new surface.

closes #488
Author
Member

Render note for review: regenerating docs/ward-ops-forgejo-reference.md with ward v0.633.0 also dropped the two actions rerun exec-bridge sections the old v0.584.0 render carried. That is the binary's own mounting behavior (same-path exec overlays are skipped fail-closed, see docs/forgejo-actions-logs.md), not a bundle edit in this PR - the pinned pre-PR bundle mounts the identical surface in this container. The bridge scripts and docs/forgejo-actions-rerun.md are untouched, and ward#950 / agentic-os#434 already track the first-class replacements.

Render note for review: regenerating `docs/ward-ops-forgejo-reference.md` with ward v0.633.0 also dropped the two `actions rerun` exec-bridge sections the old v0.584.0 render carried. That is the binary's own mounting behavior (same-path exec overlays are skipped fail-closed, see `docs/forgejo-actions-logs.md`), not a bundle edit in this PR - the pinned pre-PR bundle mounts the identical surface in this container. The bridge scripts and `docs/forgejo-actions-rerun.md` are untouched, and ward#950 / agentic-os#434 already track the first-class replacements.
Sign in to join this conversation.
No description provided.