feat: add issue edit, close, and reopen (task 0005) #6

Merged
alexion merged 1 commits from task-0005-issue-edit-close-reopen into main 2026-07-12 18:35:01 -04:00
Owner

Task: .claude/tasks/0005-issue-edit-close-reopen.md

Summary

Implements the issue state-transition mutations issue edit, issue close, and issue reopen.

  • issue edit--title, --body/--body-file, --add-label/--remove-label, --add-assignee/--remove-assignee, --milestone. Label mutations use Gitea's dedicated additive/removal endpoints (names added directly, removals resolved to IDs, a 404 for an unapplied label treated as silent success); assignees use fetch-then-patch (ADR 0007). Title/body/milestone and the recomputed assignee list travel in one PATCH; name resolution runs before any mutation. Outputs edited: { number, status: "ok" }.
  • issue close — PATCHes state: "closed"; optional --comment is posted after the close with its failure surfaced rather than swallowed. Already-closed short-circuits with message: "Already closed".
  • issue reopen — PATCHes state: "open". Already-open short-circuits with message: "Already open".

All acceptance criteria are met. Deviations, all recorded in the task file's Implementation Notes:

  • The idempotent no-op renders an entity block (issue: { number, state, message }), mirroring gh-axi, since the spec's action-block departure is scoped to the success path only.
  • The --add/--remove label and assignee flags are repeatable (matching issue create's --label).
  • Added a VALIDATION_ERROR for issue edit invoked with no changes (documented in --help) — the spec left this case unspecified.
  • Extracted a shared getIssue helper now used by view/edit/close/reopen (from review).

Review

Overall: Medium (worst factor: Sensitive domain)

  • Blast radius: Low — confined to src/commands/issue.ts plus three new test files; only adds a dispatch case, no existing callers touched.
  • Reversibility: Low — pure additive feature, no migrations/deletions; trivially revertible.
  • Test coverage: Low — each subcommand covers happy path, no-op short-circuit, help, 404-as-success, ordering, and validation-before-mutation.
  • Sensitive domain: Medium — remote state mutations with fetch-then-patch read-modify-write and non-atomic multi-call sequences (scoped to issue metadata, per ADR 0007).
  • Size & complexity: Medium — ~240 added lines with multi-step mutation flows.
  • Runtime criticality: Low — developer-facing CLI tooling.

Unaddressed findings

  • Standards (judgement call): issueClose and issueReopen are near-identical mirror images (read state → short-circuit → PATCH → render). Left as two functions — the per-subcommand form is the file's existing idiom and stays readable; a shared setIssueState core was judged not worth the indirection.

All other review findings (the repeated fetch-issue block, comment density on the state-precheck, the missing --body-file edit test) were fixed and are in the diff. Spec review found no defects.

Task: `.claude/tasks/0005-issue-edit-close-reopen.md` ## Summary Implements the issue state-transition mutations `issue edit`, `issue close`, and `issue reopen`. - **`issue edit`** — `--title`, `--body`/`--body-file`, `--add-label`/`--remove-label`, `--add-assignee`/`--remove-assignee`, `--milestone`. Label mutations use Gitea's dedicated additive/removal endpoints (names added directly, removals resolved to IDs, a 404 for an unapplied label treated as silent success); assignees use fetch-then-patch (ADR 0007). Title/body/milestone and the recomputed assignee list travel in one PATCH; name resolution runs before any mutation. Outputs `edited: { number, status: "ok" }`. - **`issue close`** — PATCHes `state: "closed"`; optional `--comment` is posted after the close with its failure surfaced rather than swallowed. Already-closed short-circuits with `message: "Already closed"`. - **`issue reopen`** — PATCHes `state: "open"`. Already-open short-circuits with `message: "Already open"`. All acceptance criteria are met. Deviations, all recorded in the task file's Implementation Notes: - The idempotent no-op renders an entity block (`issue: { number, state, message }`), mirroring gh-axi, since the spec's action-block departure is scoped to the success path only. - The `--add/--remove` label and assignee flags are repeatable (matching `issue create`'s `--label`). - Added a `VALIDATION_ERROR` for `issue edit` invoked with no changes (documented in `--help`) — the spec left this case unspecified. - Extracted a shared `getIssue` helper now used by view/edit/close/reopen (from review). ## Review **Overall: Medium** (worst factor: Sensitive domain) - Blast radius: Low — confined to `src/commands/issue.ts` plus three new test files; only adds a dispatch case, no existing callers touched. - Reversibility: Low — pure additive feature, no migrations/deletions; trivially revertible. - Test coverage: Low — each subcommand covers happy path, no-op short-circuit, help, 404-as-success, ordering, and validation-before-mutation. - Sensitive domain: Medium — remote state mutations with fetch-then-patch read-modify-write and non-atomic multi-call sequences (scoped to issue metadata, per ADR 0007). - Size & complexity: Medium — ~240 added lines with multi-step mutation flows. - Runtime criticality: Low — developer-facing CLI tooling. ### Unaddressed findings - **Standards (judgement call):** `issueClose` and `issueReopen` are near-identical mirror images (read state → short-circuit → PATCH → render). Left as two functions — the per-subcommand form is the file's existing idiom and stays readable; a shared `setIssueState` core was judged not worth the indirection. All other review findings (the repeated fetch-issue block, comment density on the state-precheck, the missing `--body-file` edit test) were fixed and are in the diff. Spec review found no defects.
alexion added 1 commit 2026-07-12 09:48:27 -04:00
feat: add issue edit, close, and reopen (task 0005)
All checks were successful
CI / test (pull_request) Successful in 27s
CI / test (push) Successful in 37s
6296f47fab
Implement the issue state-transition mutations:

- issue edit: --title, --body/--body-file, --add-label/--remove-label,
  --add-assignee/--remove-assignee, --milestone. Label mutations use
  Gitea's dedicated additive/removal endpoints (names added directly,
  removals resolved to IDs, unapplied-label 404 as silent success);
  assignees use fetch-then-patch (ADR 0007); title/body/milestone and the
  recomputed assignee list travel in one PATCH. Outputs edited: {number,
  status: ok}.
- issue close: PATCHes state closed, optional --comment posted after with
  its failure surfaced; already-closed short-circuits with Already closed.
- issue reopen: PATCHes state open; already-open short-circuits with
  Already open.

Extract a shared getIssue helper used by view/edit/close/reopen.
alexion merged commit 6296f47fab into main 2026-07-12 18:35:01 -04:00
alexion deleted branch task-0005-issue-edit-close-reopen 2026-07-12 18:35:01 -04:00
Sign in to join this conversation.
No Reviewers
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: alexion/gitea-axi#6