Skip to content

feat(bulk-duplicate): add "Create more" toggle to Deep Duplicate modal - #50

Merged
fathiraz merged 9 commits into
mainfrom
feat/create-more-deep-duplicate
Jul 17, 2026
Merged

fathiraz merged 9 commits into
mainfrom
feat/create-more-deep-duplicate

Conversation

@fathiraz

@fathiraz fathiraz commented Jul 3, 2026

Copy link
Copy Markdown
Owner

Summary

  • Add a Create more toggle beside the Deep Duplicate button — mirrors GitHub's "Create more" on the new-issue form: queue a copy without leaving the modal
  • Checked → queue the copy, reset the form to source defaults, keep the modal open to queue more copies of the same item; unchecked → close and hand off to the queue tracker
  • Drop the blocking await so the modal no longer stays frozen until every duplication finishes — the background worker runs each copy to completion regardless

Changes

Modal (src/features/bulk-duplicate-modal.tsx)

  • Extract applyPreviewDefaults() as the single source-defaults reset path, shared by the initial preview load and the Create-more re-arm
  • Add createMore state (unchecked, no persistence — resets each open)
  • Rewrite handleDuplicate to fire-and-forget the duplicateItem message; keep the 3-concurrent cap guard; checked re-arms the form, unchecked closes

Review step (src/features/bulk-duplicate-steps.tsx)

  • ReviewStep gains createMore / onToggleCreateMore props
  • Add a Primer Checkbox + label in the footer beside the Duplicate button (Primer + sx only, inside the existing Shadow DOM / ShadowThemeProvider tree)

Docs (CONTEXT.md)

  • Glossary of the domain terms (Deep Duplicate, Source item, Source defaults, Create more, Duplication queue, Queue tracker)

Stacked on the ponytail refactor cleanup (bulk-duplicate-steps.tsx extraction) already on this line — those commits are included in the diff against main.

Test Plan

  • Open Deep Duplicate on a project item; confirm a Create more checkbox sits beside the Duplicate button
  • With it checked, click Duplicate → form resets to <title> (copy) defaults, modal stays open, copy flies to the queue tracker; repeat to queue several
  • Queue 3 concurrent copies, then click a 4th → concurrent-limit Flash shows, form is kept, nothing enqueued
  • With it unchecked, click Duplicate → modal closes immediately, copy flies to the tracker, duplication completes in the background
  • Run pnpm typecheck && pnpm lint && pnpm test — 0 type errors, 0 lint errors, 415 tests pass

Summary by cubic

Add a Create more toggle to the Deep Duplicate modal so you can queue multiple copies without closing the modal. Duplication now runs in the background with a 3‑concurrent cap across tabs, and the modal only closes after the background accepts the request.

  • New Features

    • Add Create more checkbox beside Duplicate. Checked queues the copy, resets to source defaults, and keeps the modal open; unchecked closes after an accepted verdict.
    • Switch to fire‑and‑forget duplication with a cap guard that covers rapid clicks via a local pending tally, plus an immediate { accepted } verdict from duplicateItem. On reject or send failure, roll back the optimistic tally, keep the modal open, and show the cap warning. When Create more is off, lock the Duplicate button until the verdict returns to prevent double‑submits.
  • Refactors

    • Extract SelectSectionsStep and ReviewStep to src/features/bulk-duplicate-steps.tsx. Add applyPreviewDefaults() as the single reset path.
    • Split bulk‑edit subcomponents into bulk-edit-value-picker.tsx and bulk-edit-field-row.tsx; move firstRepoNameFromDom to helpers.
    • Remove unused modal-factory, use-subscription-ref, and UI primitives (and tests). Add targeted react-hooks/set-state-in-effect disables and dedupe sprint endDate mapping via a local withEndDate helper.

Written for commit fe25516. Summary will update on new commits.

Review in cubic

fathiraz added 4 commits July 3, 2026 02:55
- delete unused modal-factory + use-subscription-ref hooks and the unused
  progress-state / empty-state / section-header UI primitives (~560 LOC)
- add targeted eslint-disable + reason on legit external-sync effects
  (fetch / timer / reset-on-prop) flagged by react-hooks/set-state-in-effect
- dedupe the 3x iteration+endDate mapping in sprint-handlers via a local
  withEndDate helper
…siblings

Split bulk-edit-flyout.tsx (717 -> 440 LOC) by moving the presentational
sub-components into bulk-edit-value-picker.tsx and bulk-edit-field-row.tsx,
and the DOM helper firstRepoNameFromDom into bulk-edit-flyout-helpers.ts.
No behavior change; public component + props unchanged.
Move SelectSectionsStep and ReviewStep out of bulk-duplicate-modal.tsx
(1490 -> 1199 LOC) into bulk-duplicate-steps.tsx. No behavior change;
BulkDuplicateModal public API unchanged.
Remove the Primer components, icons, and helpers that are no longer
referenced in bulk-edit-flyout.tsx / bulk-duplicate-modal.tsx after their
sub-components moved to sibling modules.

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor auto review

No actionable issues found on changed lines.

The Create more feature is well-structured, but rapid Duplicate clicks can bypass the 3-concurrent guard: queueStore.getActiveCount() is checked before the background worker registers the job, so extra requests are silently dropped after flyToTracker already ran.

Generated automatically when this PR was submitted using Cursor CLI with --model auto.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All reported issues were addressed across 27 files

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread src/features/bulk-duplicate-modal.tsx
Mirror GitHub's new-issue "Create more": queue a copy without leaving the
modal. Checked keeps the modal open and resets edits to source defaults so
the user can queue another copy of the same item; unchecked closes and hands
off to the queue tracker.

Also drop the blocking await in handleDuplicate so the modal no longer stays
frozen until every duplication finishes — the background service worker runs
the copy to completion regardless, and errors surface via the queue tracker.

Extract applyPreviewDefaults() as the single source-defaults reset path,
shared by the initial preview load and the Create-more re-arm.
@fathiraz
fathiraz force-pushed the feat/create-more-deep-duplicate branch from 95c28aa to c0deeb9 Compare July 3, 2026 12:35
queueStore.getActiveCount() only reflects a fired duplicate once the
background SW's queueStateUpdate broadcast lands, one round-trip after
the fire. The fire-and-forget "Create more" flow could send several
duplicateItem messages ahead of that broadcast, letting rapid clicks
slip past the max-3-concurrent guard.

Track just-fired-but-not-yet-reflected duplicates in a local pending
tally and drain it as the real queue count catches up.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All reported issues were addressed across 3 files (changes from recent commits).

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread src/features/bulk-duplicate-modal.tsx
Let the modal roll back optimistic concurrent tally when the background
gate is full, instead of fire-and-forget with a silent reject.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All reported issues were addressed across 4 files (changes from recent commits).

Tip: Review your code locally with the cubic CLI to iterate faster.

Re-trigger cubic

Comment thread src/features/bulk-duplicate-modal.tsx
handleDuplicate closed the modal synchronously right after firing the
fire-and-forget duplicateItem message, before the accept/reject verdict
came back. If the background concurrency gate rejected the request, the
warning Flash rendered on an already-closed modal and the duplicate was
silently dropped.

Move the close/re-arm decision inside the promise resolution so it only
fires on an accepted verdict, and surface concurrentError on rejection
and on send failure.

Addresses #50 (comment)
cursor[bot]
cursor Bot approved these changes Jul 17, 2026

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All reported issues were addressed across 1 file (changes from recent commits).

Tip: Review your code locally with the cubic CLI to iterate faster.

Re-trigger cubic

Comment thread src/features/bulk-duplicate-modal.tsx
With Create more unchecked, repeated clicks before the accept/reject
reply could queue multiple copies even though that mode is meant to
submit exactly one and close. Add a submitting lock that disables the
Duplicate button and guards re-entry, clearing it on rejection or
transport error.

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Risk: medium. Approved — Cursor Bugbot was not present; CI passed and all cubic review threads are resolved on the latest commit. No reviewers assigned.

Open in Web View Automation 

Sent by Cursor Approval Agent: Pull Request Approver

@fathiraz
fathiraz merged commit 5213003 into main Jul 17, 2026
5 checks passed
@fathiraz
fathiraz deleted the feat/create-more-deep-duplicate branch July 17, 2026 17:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant