You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Import best-effort merge (#46) collapses paired Analogue/Digital CPS rows at import time. Operators can still end up with split logical channels inside an active project when:
They created FM and DMR rows manually in CRUD before multi-mode existed
Import heuristics did not match (name stem, location, or suffix ambiguity)
They imported from a flat CPS twice or merged projects without collapse
Future multi-talkgroup work (#36) will have the same problem for repeater × talk-group pairs
Today there is no way to discover these duplicates or merge them into one logical multiMode or multi-TG channel after the fact.
Intended outcome
A Detect merge candidates action on the channels list page that scans the active codeplug, proposes groups of channels that likely represent one logical site, and lets the operator review and apply merges.
Candidate detection (vendor-neutral)
Group channels into merge candidates when they share strong identity signals and differ only in dimensions we intend to collapse:
Signal
Use
RX / TX frequency
Match (Hz equality, or within a small tolerance if documented)
Name
Fuzzy match — normalised stem, strip known export suffixes (-F / -D), Levenshtein or similar; case-sensitive awareness per CPS quirks at display only
Location
Match when both set (lat/lon); optional tie-breaker
Report: merged count, skipped ambiguous groups, zone member rewiring notes
Merge behaviour
Reuse / extend src/lib/channelExpansion/ helpers where possible (mergeImportChannelsBestEffort logic is a starting point — extract vendor-neutral candidate grouping and merge functions)
Zones: replace member ids for merged-away channels with the single logical channel id (dedupe)
Validation: merged channel must pass validateChannel
Undo: out of scope v1 — operator restores from export/backup
Detection must not run automatically
Operator-initiated only (button). No silent background merge on load.
Affected
src/routes/channels/list.tsx — entry point
New lib module e.g. src/lib/channelMergeCandidates.ts — grouping heuristics + merge apply
Builds on#46 (multi-mode model + expansion) — multi-mode merge path shippable first
Pairs with#36 — add multi-TG candidate detection when multi-talkgroup model ships
Related#113 — similar review-before-apply UX for import decisions
Vendor boundaries: detection and merge are internal model only; no OpenGD77 naming rules in the UI copy
No wire stash: merge must populate model fields (modeProfiles, refs, opengd77Extras per profile if needed) — not provenance replay (#46 round-trip rules)
Out of scope
Automatic merge on import (already best-effort in adapters)
Merging channels that differ by frequency (different sites)
Merging by mode or TG equality (opposite of intent)
Guaranteed merge of every split pair — ambiguous groups stay separate
Problem
Import best-effort merge (#46) collapses paired Analogue/Digital CPS rows at import time. Operators can still end up with split logical channels inside an active project when:
Today there is no way to discover these duplicates or merge them into one logical
multiModeor multi-TG channel after the fact.Intended outcome
A Detect merge candidates action on the channels list page that scans the active codeplug, proposes groups of channels that likely represent one logical site, and lets the operator review and apply merges.
Candidate detection (vendor-neutral)
Group channels into merge candidates when they share strong identity signals and differ only in dimensions we intend to collapse:
-F/-D), Levenshtein or similar; case-sensitive awareness per CPS quirks at display onlyDo not use as primary match criteria:
A candidate group must contain ≥2 channels and must be mergeable along one axis:
multiMode: truechannel withmodeProfilesIf a group is ambiguous (could be either axis, or conflicting shared fields), surface as review-only or skip — do not auto-merge silently.
UI (channels list)
Merge behaviour
src/lib/channelExpansion/helpers where possible (mergeImportChannelsBestEffortlogic is a starting point — extract vendor-neutral candidate grouping and merge functions)validateChannelDetection must not run automatically
Operator-initiated only (button). No silent background merge on load.
Affected
src/routes/channels/list.tsx— entry pointsrc/lib/channelMergeCandidates.ts— grouping heuristics + merge applysrc/lib/codeplugMutations.ts—mergeChannelsIntoOne(or similar)src/lib/validation/channel.ts— ensure multi-mode / multi-TG rules cover merged resultdocs/features/crud/README.md, optionaldocs/features/data-model/noteNotes / dependencies
modeProfiles, refs,opengd77Extrasper profile if needed) — not provenance replay (#46 round-trip rules)Out of scope
Workflow note
Branch from
origin/main, atomic conventional commits (detection lib → apply mutation → list UI → tests/docs), PR linkingCloses #N. Pair withdocs/features/ progress-tracking skills.