fix(sync): keep a human reviewer when parking an issue in maintainer wait - #210
Conversation
…wait `updatePaperclipIssueState` treated `clearAssignee` as "unassign everything" and patched both `assigneeAgentId` and `assigneeUserId` to null. That flag is only ever set for an `in_review` maintainer-wait park, where the intent is "no agent owns this any more" — so a person deliberately parked on the issue as the reviewer the work is waiting on was wiped on the next sync pass, and re-wiped on every pass after that. Clear the agent assignee and carry the issue's existing human assignee through the same patch. Patching it explicitly (rather than omitting the key) keeps `isPaperclipIssuePatchApplied` able to recognise the settled state, so a parked issue is not re-patched and re-ledgered on every sync. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
There was a problem hiding this comment.
🟡 Changes recommended
The current preservation logic and the new test setup can diverge from the worker’s real syncContext derivation when an agent assignee is present, risking continued clearing of assigneeUserId in the production path.
Get a fresh assessment by requesting another Copilot review.
Pull request overview
This PR adjusts GitHub Sync’s updatePaperclipIssueState behavior so “parking” an issue in in_review (maintainer wait) clears only the agent assignee while preserving any existing human assignee, preventing repeated re-patching and ledger churn on subsequent sync passes. It also updates the written spec/docs and adds integration coverage for the parking scenarios.
Changes:
- Update
updatePaperclipIssueStateto avoid wiping a human assignee whenclearAssigneeis used for maintainer-wait parking. - Add integration tests to ensure parking is idempotent (no repeated
issues.updateand no repeatedupdate_issueledger pairs). - Update
SPEC.mdandREADME.mdto reflect the “clear agent, keep human” semantics for maintainer wait.
File summaries
| File | Description |
|---|---|
src/worker.ts |
Changes clearAssignee patching logic to preserve a human assignee when parking in maintainer wait. |
tests/issue-interactions-integration.spec.ts |
Adds integration tests for maintainer-wait parking idempotency and assignee preservation. |
SPEC.md |
Updates normative behavior for maintainer-wait unassignment to preserve human assignee while clearing agent. |
README.md |
Updates documentation to match the new maintainer-wait assignee semantics. |
Review details
- Files reviewed: 4/4 changed files
- Comments generated: 2
- Review effort level: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
`getPaperclipIssueSyncContext` answers "who owns this issue", and that resolves to the agent whenever both ids are set — so reading the preserved human from the principal would still null `assigneeUserId` in exactly the case the change is meant to protect. Read the raw field off the live issue instead, falling back to the sync context only when the live read came back empty. The regression test seeded both ids but declared a user principal, so it passed against the principal-based read. It now declares the agent principal the worker would really derive, and fails without this fix. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Why
updatePaperclipIssueStatetreatsclearAssigneeas "unassign everything": it patches bothassigneeAgentIdandassigneeUserIdtonull. But that flag is only ever set from one place —shouldClearTransitionAssignee, which isnextStatus === 'in_review' && (nextTransitionAssignee === null || shouldPreserveMaintainerWaitRouting), i.e. parking an issue in maintainer wait.In that situation the intent is "no agent owns this any more". A person deliberately assigned to the issue is the reviewer the work is waiting on, and today sync wipes them on the next pass — and, because the wiped state no longer matches the patch, re-wipes on every pass after that.
This blocks using
assigneeUserIdas the human-reviewer signal for PRs that are green and waiting on maintainers.What
clearAssigneenow clears the agent assignee and carries the issue's existing human assignee through in the same patch.issuePatchexplicitly rather than omitting the key, soisPaperclipIssuePatchAppliedstill recognises the settled state and a parked issue is not re-patched (and re-ledgered) on every sync pass — the regression class fix(sync): repeated no-op status decisions reuse their settled attempt #209 fixed.SPEC.md/README.mdupdated: "leave unassigned" now reads "clear the agent assignee, keep a human assignee".Tests
Two new cases in
tests/issue-interactions-integration.spec.ts:assigneeUserId, dropsassigneeAgentId, issues exactly oneissues.update, and a second identical pass performs no further update and appends no extra ledger pair;npm test361/361 green,npm run typecheckclean.🤖 Generated with Claude Code