Relationships
#2682 Add a duplicate_of relationship type to lab issues
Opened by stack72 · 9/29/2026· Shipped 9/29/2026
Add a duplicate_of issue relationship type to swamp-club's lab issues.
Why
The swamp repo's @swamp/issue-lifecycle model is getting a mark_duplicate
method. When issue M duplicates issue N, M should be linked to N and then
shipped with N's fix, not closed. Its reporter should get the normal
"issue shipped" notification with a View PR link. Today the only way to
record the connection is related_to, which the UI shows as "Sibling of".
That's the wrong meaning, and nothing distinguishes a duplicate from a
genuinely related issue.
This change is only about the relationship. Status transitions stay exactly
as they are. The lifecycle model steps M through
open → triaged → in_progress → shipped itself and passes N's githubPrUrl
on the ship request.
Semantics
- Direction: the duplicate is the source and the canonical issue is the
target.
POST /api/v1/lab/issues/{M}/relationshipswith{ "type": "duplicate_of", "targetIssueNumber": N }means "M is a duplicate of N". - An issue can be a duplicate of at most one issue, the same idea as
validateSingleParentforparent_of. - No chains: refuse if the target is itself
duplicate_ofsomething, and point the caller at that canonical issue instead. Refuse cycles and self-reference.validateNoSelfReferencealready handles self-reference. - Adding the same link again returns the existing one, like the other types
(
findExisting). - Permissions: allowed for an admin, or for the author of the source issue
(someone marking their own report as a duplicate). Match the style of the
existing per-type checks in
_addRelationship. - Removing the link follows the existing
removeRelationshiprules. It has no side effects on status.
Where to change
lib/domain/lab/relationship.ts: add the type toIssueRelationshipType, plus a validator for the single-canonical and no-chain rules. Put it next to theparent_ofvalidators.lib/app/lab/manage-relationships.ts: permission rule and validation.routes/api/v1/lab/issues/[number]/relationships.ts: add the type toVALID_RELATIONSHIP_TYPESand update the 400 error message that lists the valid types.lib/app/lab/relationship-summary.ts: summarise the new type.islands/lab/LabIssueDetail.tsx:- add it to the relationship picker (around line 1419);
- show "Duplicate of #N" on M and "Duplicated by #M" on N, following how the parent/child and blocked-by sections render both directions (around lines 1330–1380);
- check that the timeline event rendering (around lines 1993/2005, which
does
relationshipType.replace("_", " ")) reads well for the new type.
lib/infrastructure/mongo-lab-issue-repository.ts: check that nothing assumes the old three-value union when it reads events back.extensions/models/_lib/swamp_club.ts(this repo's own lifecycle copy): widen theaddRelationshiptype union.- Search for any other place that switches over relationship types, such as Discord handlers, reports, or OG images, and handle the new value or make sure it falls through safely.
Tests
Add to tests/domain/lab/relationship_test.ts and
tests/domain/lab/manage_relationships_test.ts, covering:
- creates the link and emits "relationship added" events on both issues;
- adding it again returns the existing link;
- rejects a second canonical issue for the same duplicate;
- rejects chains, cycles and self-reference;
- permissions: admin allowed, source author allowed, anyone else rejected.
Add a route test for the new type being accepted and for the updated error message.
Out of scope
- Any automatic status change when a duplicate link is added. The lifecycle model owns that.
- Changing
notify-issue-resolved. It already notifies each shipped issue's reporter.
Shipped
Click a lifecycle step above to view its details.
system commented 9/29/2026, 2:16:29 PM
Classified automatically when this issue was filed.
- Source: Swamp Club
If you feel this classification is incorrect, add a ripple to tell us so.
Sign in to post a ripple.