apply_changes: unified mutation tool — estimate/priority/assign/milestone, in-app + agent (#24) #53

Merged
christian merged 3 commits from feat/apply-changes-assign-milestone into main 2026-07-09 19:23:49 +00:00
Owner

Completes #24 — the propose-approve write path goes from label-only (est/p) to a unified mutation tool covering estimate, priority, assignee, and milestone, wired end-to-end: pure model → gitea client → bridge → in-app dialog + Reginald's tool. Assignee/milestone are exactly the gaps the dogfood run surfaced.

Engine (commit 1)

  • IssueChange gains assign + remilestone; planIssueChange typed to label kinds; isLabelChange narrows; pure summarizeChange gives one confirm line for any kind.
  • Client setIssueAssignees / setIssueMilestone; gitea:applyChange branches label-swap vs field-write; ApplyChangeResult.plan optional.

In-app + agent (commit 2)

  • propose_change tool + system prompt accept assignee/milestone; proposalsFor + new ProposalContext skip no-ops and label the milestone; ChangeProposal.summary (always present) drives the chat proposal cards.
  • Client listCollaborators() prepends the repo owner (/collaborators omits them — a solo-owner repo would otherwise have nobody assignable). New gitea:collaborators bridge.
  • The issue Adjust dialog gains Assignee + Milestone pickers (current values from the reconciled backlog); pending changes flow through the existing apply path.

Tests / verification

  • +9 core tests (assign/milestone proposals with no-op skip; collaborators owner-prepend). 138 core green; core + desktop typecheck clean; 14 fixture e2e green.
  • live-backlog e2e now drives the pickers on real gitea (owner prepended, unassign diff renders). Fixed stale P2 assertions there (P2 is shipped → correctly off the runway).

Base

Off main; touches issue-screen.tsx + global.d.ts, which PR #52 also edits in different regions — small rebase expected once #52 lands.

🤖 Generated with Claude Code

Completes **#24** — the propose-approve write path goes from label-only (est/p) to a **unified mutation tool** covering estimate, priority, **assignee**, and **milestone**, wired end-to-end: pure model → gitea client → bridge → **in-app dialog + Reginald's tool**. Assignee/milestone are exactly the gaps the dogfood run surfaced. ## Engine (commit 1) - `IssueChange` gains `assign` + `remilestone`; `planIssueChange` typed to label kinds; `isLabelChange` narrows; pure `summarizeChange` gives one confirm line for any kind. - Client `setIssueAssignees` / `setIssueMilestone`; `gitea:applyChange` branches label-swap vs field-write; `ApplyChangeResult.plan` optional. ## In-app + agent (commit 2) - `propose_change` tool + system prompt accept `assignee`/`milestone`; `proposalsFor` + new `ProposalContext` skip no-ops and label the milestone; `ChangeProposal.summary` (always present) drives the chat proposal cards. - Client `listCollaborators()` **prepends the repo owner** (`/collaborators` omits them — a solo-owner repo would otherwise have nobody assignable). New `gitea:collaborators` bridge. - The issue **Adjust dialog** gains Assignee + Milestone pickers (current values from the reconciled backlog); pending changes flow through the existing apply path. ## Tests / verification - +9 core tests (assign/milestone proposals with no-op skip; collaborators owner-prepend). **138 core green**; core + desktop typecheck clean; **14 fixture e2e** green. - **live-backlog** e2e now drives the pickers on real gitea (owner prepended, unassign diff renders). Fixed stale P2 assertions there (P2 is shipped → correctly off the runway). ## Base Off `main`; touches `issue-screen.tsx` + `global.d.ts`, which PR #52 also edits in different regions — small rebase expected once #52 lands. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
christian added 1 commit 2026-07-09 15:31:43 +00:00
The write path handled only the est/* and p/* label axes. Extend it to the two
mutations dogfooding proved were missing — assignee and milestone — behind one
unified change model:

- IssueChange gains `assign` and `remilestone` kinds. `planIssueChange` is now
  typed to LabelChange (the label kinds only); `isLabelChange` narrows, and a new
  pure `summarizeChange(change, currentLabels?)` gives one confirm line for ANY
  kind (label kinds delegate to describeChange; field writes describe directly).
- GiteaClient gains `setIssueAssignees` / `setIssueMilestone` (PATCH /issues/{n},
  returning the updated issue; milestone 0 clears).
- The main `gitea:applyChange` handler branches: label swaps resolve ids +
  setIssueLabels as before; field writes call the new client methods. Snapshot
  invalidated either way so board + forecast catch up. ApplyChangeResult.plan is
  now optional (absent for field writes).
- issue-screen uses summarizeChange for its diff preview.

Tests: +5 covering isLabelChange, and summarizeChange for assign/unassign,
milestone set-by-title / by-id / removal, and label delegation. Full core suite
132 green; core + desktop typecheck clean.

Follow-up (rest of #24): surface assignee/milestone pickers in the Adjust dialog
and extend the agent's propose_change tool so Reginald can propose them too.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
christian added 1 commit 2026-07-09 15:54:06 +00:00
Surfaces the assign/milestone mutations end-to-end so they're usable in-app and
by the agent — the rest of #24.

Agent path:
- propose_change tool + system prompt now accept `assignee` (login/null) and
  `milestone` (id/null). ProposeChangeArgs + proposalsFor extended: a new
  ProposalContext (current assignee/milestone + milestones list) lets a proposal
  skip no-ops and label the milestone. ChangeProposal gains an always-present
  `summary` (plan is now label-only) — chat-panel, use-chat, and the model
  executor render `summary`, so non-label proposals display correctly.

Dialog path:
- Client `listCollaborators()` (prepends the repo owner — /collaborators omits
  them, so a solo-owner repo still has an assignable person). New
  `gitea:collaborators` bridge. The Adjust dialog gains Assignee + Milestone
  pickers (current values from the reconciled backlog); pending assign/remilestone
  changes flow through the existing apply path.

Tests: +4 core (assign/milestone proposals with no-op skip; collaborators
owner-prepend + no-double-add). 138 core green; core + desktop typecheck clean;
14 fixture e2e green; live-backlog now drives the pickers on real data.
Fixed stale P2 refs in live-backlog (P2 is shipped → correctly off the runway).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
christian changed title from apply_changes: unify the mutation tool with assign + milestone (#24) to apply_changes: unified mutation tool — estimate/priority/assign/milestone, in-app + agent (#24) 2026-07-09 15:54:26 +00:00
christian added 1 commit 2026-07-09 19:23:46 +00:00
christian merged commit 354ba9227e into main 2026-07-09 19:23:49 +00:00
Sign in to join this conversation.
No description provided.