apply_changes UI: assign + milestone in the dialog and Reginald's tool (#24)
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>
This commit is contained in:
@@ -333,6 +333,17 @@ export function registerGiteaIpc(): void {
|
||||
}
|
||||
})
|
||||
|
||||
// Assignable people (repo collaborators) for the Adjust dialog's assignee picker.
|
||||
ipcMain.handle('gitea:collaborators', async () => {
|
||||
const client = getGiteaClient()
|
||||
if (!client) return []
|
||||
try {
|
||||
return await client.listCollaborators()
|
||||
} catch {
|
||||
return []
|
||||
}
|
||||
})
|
||||
|
||||
// ---- config (team onboarding) ----
|
||||
ipcMain.handle('config:get', () => publicConfig())
|
||||
|
||||
|
||||
@@ -13,7 +13,6 @@ import {
|
||||
type ChangeProposal,
|
||||
type ChatMessage,
|
||||
createChatClient,
|
||||
describeChange,
|
||||
type ModelRouter,
|
||||
type ProjectView,
|
||||
proposalsFor,
|
||||
@@ -130,10 +129,19 @@ export function registerModelIpc(): void {
|
||||
const a = (args ?? {}) as ProposeChangeArgs
|
||||
const issue = await client.getIssue(a.issue).catch(() => null)
|
||||
if (!issue) return { error: `issue #${a.issue} not found` }
|
||||
const built = proposalsFor(a, issue.labels, issue.title)
|
||||
// Milestone titles + current assignee/milestone let a proposal label the
|
||||
// milestone and skip a no-op assign/move.
|
||||
const milestones = (a.milestone !== undefined ? await client.listMilestones().catch(() => []) : []).map(
|
||||
(m) => ({ id: m.id, title: m.title }),
|
||||
)
|
||||
const built = proposalsFor(a, issue.labels, issue.title, {
|
||||
currentAssignee: issue.assignee,
|
||||
currentMilestoneId: issue.milestone?.id ?? null,
|
||||
milestones,
|
||||
})
|
||||
proposals.push(...built)
|
||||
return built.length
|
||||
? { proposed: built.map((p) => ({ issue: a.issue, diff: describeChange(p.plan) })) }
|
||||
? { proposed: built.map((p) => ({ issue: a.issue, diff: p.summary })) }
|
||||
: { proposed: [], note: 'no change — already at that value' }
|
||||
}
|
||||
if (name === 'record_directive') {
|
||||
|
||||
Reference in New Issue
Block a user