Fix update-project to deduplicate draft issues with temporary ID support#14293
Fix update-project to deduplicate draft issues with temporary ID support#14293
Conversation
… duplicates Co-authored-by: mnkiefer <8320933+mnkiefer@users.noreply.github.com>
🔍 PR Triage ResultsCategory: bug | Risk: medium | Priority: 63/100 Scores Breakdown
📋 Recommended Action: fast_trackImportant bug fix for project management functionality. Medium priority with good quality. Should be reviewed promptly once CI passes. Triaged by PR Triage Agent on 2026-02-07
|
|
@copilot This will work, but would it not be better to have a number of temp id? or is this different for projects vs. pr/issues which also exist outside of them? |
Draft issues are fundamentally different from regular issues/PRs - they exist only within a project board (no GitHub issue number) and are created directly via Title matching is the most appropriate approach because:
However, if you'd prefer temp ID support for draft issues within a workflow run (to reference before creation), we could:
This would enable patterns like: {"type": "update_project", "content_type": "draft_issue", "draft_title": "Task", "temporary_id": "aw_abc123"}
{"type": "update_project", "temporary_id": "aw_abc123", "fields": {"Status": "Done"}}Let me know if you'd like this enhancement - it's a ~50 line addition. The current title-based deduplication works well for typical usage where agents reference drafts by title across multiple update calls. |
|
@copilot How does the github api distinguish between draft items? unique by title? |
The GitHub API doesn't enforce uniqueness on draft issue titles - you can have multiple drafts with the same title on a board. Each draft issue has a unique Our implementation matches by title because:
If duplicates with same title exist, our code finds the first match and reuses it. This is a reasonable heuristic that solves the original problem (creating new drafts when updating status) without adding complexity. |
- Accept optional temporary_id field in draft issue messages - Store temporary_id → draftItemId mapping when created - Support referencing draft issues by temporary ID alone (no title needed) - Fall back to title matching when no temporary ID in map - Add test for temporary ID-based draft issue reference - All 3358 JavaScript tests passing Co-authored-by: mnkiefer <8320933+mnkiefer@users.noreply.github.com>
|
@copilot Suggest an approach we can do equivalent to NodeID, maybe hash of all draft item info or similar? |
The current implementation already uses the GitHub node ID ( What we have now:
The limitation: Node IDs only exist after creation and don't persist across workflow runs (temporaryIdMap is runtime-only). Alternative approaches for persistent identification:
Current implementation is optimal: temporary IDs within a run, title matching across runs. A persistent marker in the body would improve cross-run deduplication if needed. |
|
@copilot Current implementation causes update-project calls to create new draft items, when in fact, they should just update existing ones, for example a status field. |
The implementation in commits 4cad5ab and 4aca2c9 specifically addresses this - it queries existing drafts by title (lines 637-680) and reuses them (lines 682-688) instead of creating duplicates. The deduplication works when:
Possible issue: If you're seeing new drafts created, it might be because:
Can you share:
This will help me identify if there's a specific edge case we need to fix. |
|
@copilot Here is the example where we got a new item instea of an update: |
Found the issue! In step 2, the instructions say "the draft issue you created" but don't specify HOW to reference it. The agent needs either:
Added in commit 4aca2c9: Output The fix enables two patterns: Pattern 1 (temporary_id): // Step 1
{"content_type": "draft_issue", "draft_title": "Test", "temporary_id": "aw_abc123"}
// Returns: temporary-id=aw_abc123
// Step 2
{"content_type": "draft_issue", "temporary_id": "aw_abc123", "fields": {"Status": "In Progress"}}Pattern 2 (exact title): // Step 1
{"content_type": "draft_issue", "draft_title": "Test *draft issue* for `smoke-project`"}
// Step 2
{"content_type": "draft_issue", "draft_title": "Test *draft issue* for `smoke-project`", "fields": {"Status": "In Progress"}}Instructions should specify: "use the same draft_title" or "use the returned temporary-id". |
- Add core.setOutput("temporary-id", temporaryId) for draft issues
- Enables agents to reference created drafts using returned temporary_id
- Addresses issue where agents couldn't identify "the draft issue you created"
- Tests passing (45 update_project tests)
Co-authored-by: mnkiefer <8320933+mnkiefer@users.noreply.github.com>
|
@copilot This is the fix we need. Get rid of all the other stuff that clutters or does not belong to this fix/is extra. |
…exity) - Removed temporary ID imports and complex mapping logic - Keep only simple title-based deduplication - Query existing drafts by title, reuse if found - All 44 update_project tests passing - Cleaner, more maintainable solution Co-authored-by: mnkiefer <8320933+mnkiefer@users.noreply.github.com>
Simplified in commit 77fd9f7. Removed all temporary ID complexity, keeping only title-based deduplication:
Agents must use exact same |
|
@copilot No, go by "use the returned temporary-id" by default. |
- Restore temp ID imports and mapping logic from commit eb4f4d6 - Output temporary-id for agent consumption - Temporary ID is the recommended approach (no need for exact title) - Title matching remains as fallback - All 45 update_project tests passing Co-authored-by: mnkiefer <8320933+mnkiefer@users.noreply.github.com>
Restored temporary ID support in commit baead6c. Now uses temporary_id by default:
Example: // Create - returns temporary-id
{"content_type": "draft_issue", "draft_title": "Task", "fields": {"Status": "Todo"}}
// Update - use returned temporary_id
{"content_type": "draft_issue", "temporary_id": "aw_abc123def456", "fields": {"Status": "Done"}}All 45 tests passing. |
|
Fixes issue where
update-projectcreated duplicate draft issues when updating fields like status, instead of updating existing items on the board.Root Cause
Draft issues were always created as new items without checking if one with the same title already existed on the board. Regular issues/PRs were deduplicated by content ID, but draft issues lacked this check entirely.
Solution
Temporary ID-Based Deduplication (Primary Mechanism)
temporary_idfield for draft issuestemporary_id → projectItem.idmapping in temporaryIdMaptemporary-idso agents can reference drafts in subsequent callsTitle-Based Deduplication (Fallback)
DraftIssuefragmentsprojectItem.idwhen title matchesChanges Made
update_project.cjs:generateTemporaryId,isTemporaryId,normalizeTemporaryIdtemporary-idalongsideitem-idDraftIssuefragment in GraphQLupdate_project.test.cjs:existingDraftItemResponse(),emptyDraftItemsResponse()helpersUsage (Recommended)
Use temporary_id returned from creation:
Alternative: Use exact title match (fallback)
Behavior Change
Before:
After:
Benefits:
✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.