Skip to content

refactor: extract the copy-paste the jscpd lanes were tracking, and split the longest test file - #71

Closed
vdavid wants to merge 17 commits into
mainfrom
claude/duplication-file-length-swarm-hit6i1
Closed

refactor: extract the copy-paste the jscpd lanes were tracking, and split the longest test file#71
vdavid wants to merge 17 commits into
mainfrom
claude/duplication-file-length-swarm-hit6i1

Conversation

@vdavid

@vdavid vdavid commented Sep 2, 2026

Copy link
Copy Markdown
Owner

A parallel sweep over the jscpd-rust / jscpd-frontend inventories and the top of the file-length list. Every pair was handled by extracting the shared code, or deliberately skipped with a reason. No allowlist number was raised and no entry was added.

What it does to the numbers

Lane Before After
jscpd-frontend 28 clones, 560 lines, 22 pairs 2 clones, 39 lines, 2 pairs
jscpd-rust 59 clones, 1,019 lines, 46 pairs 37 clones, 636 lines, 31 pairs
file-length 94 files listed 93, and the app crate's longest file is gone

Net across 117 files: +4,887 / -4,728, most of the insertions being the 2,266-line test file moving into eight.

The extractions

Frontend. The two new-entry dialogs shared their whole name-validation flow, now new-entry-name-check.svelte.ts and NewEntryNameField.svelte. Onboarding and Settings shared the beta email signup, now one store. Four more shared components came out of duplicated markup and CSS: OnboardingToggleCard, ShortcutPill, and the two toast bodies. The dev gallery pages shared their entire scaffolding, now CatalogPage, GlyphGrid, LabeledRow, and DemoAnchor. Five within-file clones collapsed into local helpers.

Rust. cmdr-index gained one entry-row mapper, one children-stats collector, and a shared test-fixture module in place of six copies. The rename engines, the cancellable runners, and the copy/move commands each stopped repeating a step. Three backends that answer scan_for_conflicts by listing now share cmdr_fs::volume::conflicts_in_listing instead of keeping three copies of the field mapping, and the tar reader walks members through one function.

The split. transfer/volume/copy_tests.rs (2,266 lines) is now copy_tests/ with one child per behavior. All 51 tests kept their names and bodies; 51 run and pass.

Skipped on purpose

DeleteDialogScanPhaseBody (23 CSS lines): the honest fix changes the delete dialog's DOM and orphans three catalog keys. GitSectionSearchSection (18 lines): section boilerplate that a helper would obscure. The sync and async cycle stagers in rename/bulk.rs (10 lines): unifying them needs an async abstraction over the local path.

Two things worth your attention

The split exposed a naming assumption in five checks. copy_tests.rs was excluded from jscpd-rust and from the Rust source scanners by name; copy_tests/progress.rs is not, so splitting it enrolled 2,300 lines of unchanged test code in lanes meant for production code (42 duplicated lines, 23 lock-poison sites, 2 swallowed probes, 1 orphaned opt-out comment). Rather than 25 opt-out comments apologizing for test code being test code, a <subject>_tests/ directory now counts alongside tests/ in isRustTestFile and in the jscpd ignore list. isRustTestFile's own doc comment already stated the principle for tests/. This trades away one pre-existing allowlist pair, cold_drive_tests/branches.rs ↔ switches.rs (6 lines between two test files), which was in the lane by the same naming accident.

A lint autofix silently breaks bindable props. NewEntryNameField.svelte typed value as required while defaulting it with $bindable(). @typescript-eslint/no-useless-default-assignment reads that as a useless default and its autofix deletes it, turning off two-way binding: a local pnpm check rewrote the file and svelte-check then failed on both dialogs. CI never sees it (--ci disables auto-fixing), so it only ever breaks a local run, in a way that reads like a bug in the dialogs. Fixed here by matching what TextInput / TextArea / Chip already do (value?: string with $bindable('')), but the trap is general.

Verification

pnpm check minus the lanes this Linux container cannot run: 104 OK, 3 warn, 1 skipped, 0 failed (19m38s). That includes clippy, rustfmt, svelte-check, both type-aware eslint lanes, all six Rust source scanners, the i18n and docs lanes, the Go suite, and 10,148 frontend tests.

The three warns: claude-md-reminder (19 directories touched without a doc edit, mostly one-line changes), invariant-density (mothballed), and one svelte-tests note that SettingCheckbox.svelte's coverage-allowlist entry now looks unneeded because the extraction took it to 100%. That entry is yours to remove; I left it alone.

Coverage and a11y gaps the extractions opened were closed rather than allowlisted: new-entry-name-check.svelte.ts and ShortcutPill.svelte went from 55% and 62.5% to 100%, and the four new components got tier-3 a11y tests.

Not run here, and why: desktop-e2e-linux, rust-integration-tests (no Docker daemon), bindings-fresh (compiles the macOS-only cmdr-fsevent-stream), changelog-links (shallow clone, so 2,390 historical SHAs do not resolve), cargo-audit / cargo-udeps / govulncheck, and the Playwright and website E2E suites. rust-tests needs your eye: 6,684 tests run, and the same 17 fail on an untouched main in this container as on this branch (virtual-MTP device discovery, network-hang cancellation, and two timing-sensitive tests). The exact set shifts run to run because the MTP tests share a device lock, so please treat CI's verdict as the real one.

Not attempted

The remaining 31 Rust pairs are led by ~200 lines of macOS-only code (menu/linux ↔ macos, clipboard, quick_look, native_drag, volumes ↔ volumes_linux) that does not compile on Linux, so an agent here would be writing unverifiable changes. That is a Mac session's work. The other 88 over-length files are mostly per-file architectural judgment rather than sweep material, and splitting the big Svelte components wants a visual check this container cannot give.

🤖 Generated with Claude Code

https://claude.ai/code/session_013JPKrH6kufjw9Q4eKmxqsy


Generated by Claude Code

…ard, shortcut pill, and toast bodies shared by two surfaces each

Five frontend copy-paste pairs from the `jscpd-frontend` inventory, each replaced by one shared module so the two surfaces can no longer drift apart:

- `StepBeta.svelte` and `UpdatesSection.svelte` both carried the beta contact email field's state and handlers (persist on keystroke, subscribe on commit, typed feedback). Now `settings/sections/beta-email-signup.svelte.ts` (`createBetaEmailSignup()`, per-mount `$state` behind getters).
- `StepBeta.svelte` and `StepOptional.svelte` shared the toggle card's markup and styles. Now `onboarding/OnboardingToggleCard.svelte`.
- `GlobalShortcutRow.svelte` and `KeyboardShortcutsSection.svelte` shared the editable shortcut pill's styles. Now `settings/sections/ShortcutPill.svelte` (class names preserved for the existing tests).
- `OperationFailedToastContent.svelte` and `OperationFailuresToastContent.svelte` shared the toast body. Now `status-corner/OperationFailureToastBody.svelte`.
- `AutoSendToastContent.svelte` and `ErrorReportToastContent.svelte` shared the sent-report toast body. Now `error-reporter/SentReportToastBody.svelte`.

`GitSection` and `SearchSection` keep their 18 shared lines: that is the settings-section registry boilerplate every section carries, and hiding it would obscure the contract.

Behavior preserved as-is, including the `analytics.email` listener that both surfaces subscribe at init without unsubscribing. Colocated `CLAUDE.md` / `DETAILS.md` module maps updated.

Verified: svelte-check, stylelint, css-unused green; eslint green on the touched files; targeted vitest run of the touched dirs (41 files, 487 tests) green.
…nd boolean-setting binding between the dialogs that copied them

Three frontend copy-paste pairs from the `jscpd-frontend` inventory, each replaced by one shared module:

- `NewFolderDialog.svelte` and `NewFileDialog.svelte` each carried the name validation (sync validators, debounced clash lookup, `directory-diff` re-validation) plus the field's markup and styles. Now `file-operations/new-entry-name-check.svelte.ts` (`NewEntryNameCheck`) and `file-operations/NewEntryNameField.svelte`, which owns the focus/select/listen lifecycle. Both dialogs compose them; an a11y block for the new component joins `file-operations.a11y.test.ts`.
- `new-folder-operations.ts` and `new-file-operations.ts` each re-derived the cursor entry. Now `file-operations/cursor-entry.ts` (`getCursorEntry()`); both pre-fill helpers are one-line wrappers.
- `SettingCheckbox.svelte` and `SettingSwitch.svelte` each bound a boolean setting the same way. Now `settings/components/boolean-setting.svelte.ts` (`useBooleanSetting(id)`); both controls are four lines of script.

`DeleteDialog` and `ScanPhaseBody` keep their 23 shared CSS lines: the honest fix is the delete dialog rendering `ScanPhaseBody`, which changes its DOM, needs an end-aligned variant, and orphans three catalog keys. A follow-up, not a sweep item.

One ordering nuance: mkdir's field now mounts focus/validate/listen from the child while the parent kicks off AI suggestions, so only the async IPC order shifted; the existing mkdir timeout, streaming, and a11y tests all pass. `file-operations/CLAUDE.md` compressed to stay under the word budget after the module-map addition.

Verified: svelte-check, eslint, stylelint, css-unused, a11y-coverage, claude-md-length, docs-dead-links, docs-link-text, docs-reachable green; targeted vitest run of mkdir, mkfile, the a11y suite, and settings/components (13 files, 102 tests) green.
… frontend into one local helper each

Five intra-file clones from the `jscpd-frontend` inventory, each now a single definition:

- `ImageSearchResults.svelte`: the thumbnail + filename tile face both grids rendered is one `{#snippet tileFace(tile)}`.
- `drag-image-renderer.ts`: the rounded-rect path tracing shared by the fill and stroke drawers is `traceRoundedRect()`.
- `mtp-store.svelte.ts`: the placeholder-device-in-error write both the exclusive-access and permission listeners performed is `markDeviceError()`.
- `viewer-copy.svelte.ts`: `runRangeIpc()` owns session/selection resolution, the busy and in-flight plumbing, and the `ViewerError` to `CopyResult` mapping; `performRead` and `saveAs` are one-line wrappers. It takes one object payload (anchor and focus share a type), per `no-confusable-callback-params`.
- `filter-popover-helpers.ts`: the inner `format()` in `resolveDatePreset` duplicated the module-level `isoLocalDate()` byte for byte; the seven call sites use the latter.

No exported surface changed. Verified: svelte-check, eslint, stylelint, css-unused green; targeted vitest run of search, drag, mtp, viewer-copy, and filter-chips (44 files, 536 tests) green.
…tor, and shared test fixtures replace six copies

Four Rust copy-paste pairs from the `jscpd-rust` inventory, all in `crates/cmdr-index`:

- `store/entries.rs` mapped the full `EntryRow` from a `rusqlite::Row` in five queries and streamed borrowed directory names in two loops. Now `entry_row_from()` (passed straight to `query_map` / `query_row`) and `for_each_directory_row()`. SQL strings, column order, the `inode_from_sql` bit-cast, the zero-alloc `get_ref` borrow, and `optional()` handling are unchanged.
- `aggregator/readers.rs` collected children stats identically in the bulk and scoped readers. Now `collect_children_stats()`; each reader keeps its own SQL.
- `writer/deferred_repair.rs` and `writer/repair.rs` each defined `dir_entry` / `file_entry` / `symlink_entry` test fixtures, and `writer/delta.rs` spelled out nine identical directory `EntryRow` literals. Now one `#[cfg(test)]` module, `writer/entry_fixtures.rs`.

Verified: `cargo check -p cmdr-index --all-targets` and clippy clean, `cargo fmt --check` clean, nextest over `indexing::(store|writer|aggregator)` 255 passed.
…ing their scaffolding

Seven frontend copy-paste pairs from the `jscpd-frontend` inventory, in dev-only and debug-only routes:

- `routes/dev/components/+page.svelte` and `routes/dev/graphics/+page.svelte` carried the same header, "Open in browser" link, scroll-parent lookup, anchor scrolling, IntersectionObserver sync, and catalog CSS. Now `routes/dev/CatalogPage.svelte`, parameterized by prefix, sub-ids, route, title, and a description snippet; each page keeps only its guard, props, ids, and section list.
- `IconsSection` and `StatusBadgesSection` shared the tooltip'd glyph grid. Now the generic `routes/dev/graphics/GlyphGrid.svelte` with `intro` and `glyph` snippets.
- `SizeBadges` and `StatusBadgeSection` shared the labeled row. Now `routes/dev/components/LabeledRow.svelte`.
- `FilterPopoverSection`, `PopoverSection`, and `MenuSection` shared the demo anchor button. Now `routes/dev/components/DemoAnchor.svelte`.
- `DebugErrorPreviewPanel.svelte` rendered three identical state groups and `DebugHistoryPanel.svelte` two identical pane columns; each is one `{#each}` over a small array now.

Rendering unchanged (the one host color `GlyphGrid` carries over only affects a fixed-color `<img>` child). Verified: svelte-check, eslint, stylelint, css-unused green; the sections a11y suite green.
…move commands share their once-duplicated steps

Six Rust copy-paste pairs from the `jscpd-rust` inventory in the write-operations area:

- `rename/bulk.rs`: the local and remote engines settled no-op rows and the finished run identically, and probed unclaimed destinations identically. Now `complete_noop_rows()`, `BulkRenameRun::settled()`, and `rows_with_unclaimed_destination()`; each engine keeps only its own existence probe. The sync and async cycle stagers stay as deliberate twins (ten lines; unifying them would need an async abstraction over the local path).
- `rename/bulk.rs` and `agent/tools/propose/rename/preflight.rs` each defined `same_local_file`. Now one `pub(crate)` fn in `write_operations/rename.rs` (cfg unix / not-unix), re-exported from `write_operations`; the two cfg'd `check_sibling_conflict` bodies in `rename.rs` collapse into one on top of it.
- `operation_intent.rs` tests parked a worker on the gate twice. Now `park_worker_on_gate()`, carrying the one `allowed-test-sleep` window.
- `cancellable.rs`: `run_cancellable` and `run_cancellable_scoped` shared the worker polling loop. Now `await_worker_result()`.
- `commands/file_system/write_ops.rs`: copy and move built the same local transfer request. Now `local_transfer_request()` (tilde expansion, archive refusal, default config).

Verified: `cargo check --workspace --exclude cmdr-fsevent-stream --features cmdr/virtual-mtp --all-targets` clean, clippy clean on touched files, `cargo fmt`, nextest over rename / operation_intent / cancellable / propose::rename / write_ops 67 passed.
…y listing, plus a shared tar member walk

Four Rust copy-paste pairs from the `jscpd-rust` inventory, resolved by moving the shared code down into the crate both sides already depend on:

- The name-collision half of `Volume::scan_for_conflicts` was hand-kept in three backends (the `cmdr-fs` in-memory volume, `cmdr-smb`, and the app's MTP scan). Now `cmdr_fs::volume::conflicts_in_listing()`, with its own tests. Three copies drift on exactly the field nobody re-tests, and `dest_is_directory` is what the transfer dialog classifies collisions by.
- `crates/cmdr-archive/src/read/tar.rs`: the single-member and subtree streamers each opened the codec, iterated members, and mapped decoder and header failures. Now `for_each_member()`, which hands every member to a visitor and returns `ControlFlow`, so both readers report a failure the same way.
- `apps/desktop/src-tauri/src/icons/per_path.rs` re-exports `is_package_dir` and `package_icon_id` from `cmdr_fs::icons::packages` but carried a verbatim copy of that module's six tests. Dropped; the crate keeps them where the code lives.

Verified: `cargo check --workspace --exclude cmdr-fsevent-stream --features cmdr/virtual-mtp --all-targets` clean, nextest over the icons, tar, in-memory, scan, and conflict suites 206 passed.
…ght suites named after what they cover

`transfer/volume/copy_tests.rs` was the app crate's longest file and the one a reader had to scroll to find anything in. It is now `copy_tests/` with a `mod.rs` holding the shared imports and the `make_state` / `make_volumes` fixtures, plus one child per behavior under test: `cancellation`, `conflicts`, `destination`, `error_mapping`, `progress`, `remove_tree`, `scan`, and `scan_hints` (112 to 588 lines each, all under the 800-line warn threshold).

A pure move: all 51 tests kept their names and bodies byte for byte, and 51 run and pass after the split. Only the module path changed, so the sibling `#[path]` suites and the two doc comments naming the old file were updated to match.

Verified: `cargo check --workspace --exclude cmdr-fsevent-stream --features cmdr/virtual-mtp --all-targets` clean, `cargo nextest run -E 'test(/volume::copy::tests::/)'` 51 passed (51 before).
…o what the sweep left behind

`pnpm check jscpd-rust jscpd-frontend file-length` shrink-wrapping itself after the extractions, plus the doc line re-wraps its auto-fixer made in the files the sweep touched.

Frontend duplication drops from 22 file pairs and 560 lines to 2 pairs and 39, Rust from 46 pairs and 1,019 lines to 31 and 640, and `copy_tests.rs` leaves the file-length list entirely. Every change is a removal or a ratchet down (`rename/bulk.rs` 44 to 10); no entry gained a number and none was added.
…ted with tier-3 a11y tests

`a11y-coverage` is an error, and the sweep's new shared components arrived without it: `SentReportToastBody`, `OnboardingToggleCard`, `ShortcutPill`, and `OperationFailureToastBody`.

Each block joins its directory's existing a11y file rather than opening a new one, since `svelte-tests` charges per test file (`docs/testing.md` § "What a test actually costs"). The states covered are the ones that differ: title and title-less for the sent-report body, switch on and off for the toggle card, editable / read-only / empty / recording-with-conflict for the pill, and title with and without a reason for the failure body.

One skipped test comes with the pill: axe flags `nested-interactive` on the `<span role="button">` remove control inside the pill's own button. That is the same finding the `KeyboardShortcutsSection` test above it has documented and skipped all along; the markup only moved into `ShortcutPill.svelte`. The TODO names both fix options and says to unskip the two together.

Verified: `pnpm check a11y-coverage` OK, `desktop-svelte-eslint` and `svelte-check` clean, targeted vitest run over the four directories 577 passed.
…ectory, as it already skips the single file

Splitting `copy_tests.rs` into `copy_tests/` moved 2,300 lines of test code from outside this lane to inside it: the ignore list caught `*_tests.rs` but nothing named `copy_tests/progress.rs`, so the children's shared setup surfaced as 42 lines of new production duplication. The lane's stated job is duplication in production Rust, and a suite that outgrows one file and becomes a directory of the same name is the same test code it was the day before.

So `**/*_tests/**` joins the list, covering the convention one level up. Without it, every future long-test-file split pays the same toll, and the only ways out would be an allowlist entry for test code the lane was never meant to see, or leaving the file long.

This also drops one pre-existing pair from the allowlist, `cold_drive_tests/branches.rs ↔ switches.rs`: that directory follows the same convention and was in the lane by the same naming accident. David, that is the one bit of coverage this trades away, and it is 6 lines of duplication between two test files.

Verified: `pnpm check jscpd-rust` OK (31 pairs, 636 duplicated lines), `gofmt`, `go-vet`, and `go-tests` green.
…tly, taking both to 100%

Extraction moves logic out of the components whose tests used to reach it, and `svelte-tests` caught the shortfall: `new-entry-name-check.svelte.ts` at 55% and `ShortcutPill.svelte` at 62.5%, both under the 70% per-file threshold. Both are now at 100%, tested through their own surface rather than through a dialog.

`NewEntryNameCheck` gets 12 tests: empty and whitespace names, each sync validator short-circuiting before the clash lookup, folder versus file clash wording including the entry-gone case, stale-message clearing, a failing lookup leaving the field clear, `isChecking` held for the duration, `schedule()` coalescing to the latest name, `listen()` re-checking only its own listing, and `dispose()` with and without a pending timer.

`ShortcutPill`'s remove control gets 3: no × on a slot that cannot be removed, a click removing without bubbling into the pill's own `onclick`, and Enter and Space removing where other keys do not.

Both blocks join existing files, which already own the mocks and fake timers they need; `svelte-tests` charges per test file (`docs/testing.md` § "What a test actually costs").

Verified: `pnpm check svelte-tests` green, 10,148 tests passed, no file under threshold; eslint and svelte-check clean.
…s tripped

`eslint-typecheck-svelte` and `svelte-check` see more than the fast eslint lane, and three of the sweep's extractions landed errors only they catch:

- `ImageSearchResults.svelte`: rendering the extracted `tileFace` snippet trips `no-confusing-void-expression`, which every `{@render}` of a locally declared snippet does. Uses the same targeted disable comment `FunctionKeyBar.svelte` already carries for it.
- `IconsSection.svelte` and `StatusBadgesSection.svelte`: the `glyph` snippet parameter typed as `any` under the new generic `GlyphGrid`, since a snippet parameter is not inferred from the `Snippet<[T]>` prop. Both now annotate it, which needed a named `IconEntry` for the icons list.
- `settled-operations.test.ts`: an autofix for the same void-expression rule in a test added a moment ago.

Verified: `eslint-typecheck-svelte` 297 files passed, `svelte-check` no type errors, the touched test file green.
…ctory as the test code it is

`isRustTestFile` already says the principle in its own doc comment: a module that outgrows one file becomes a directory, and every file in one is as much a dedicated test file as the `tests.rs` it was split out of. It only recognized a directory named exactly `tests`, so splitting `copy_tests.rs` into `copy_tests/` moved 2,300 lines of unchanged test code into the jurisdiction of every scanner that trusts this predicate.

The result was 25 findings against code nobody wrote today: 23 `lock-poison` sites (`events.complete.lock().unwrap()` in assertions), 2 `probe-unwrap-justified` swallowed probes, and an `error-string-match` opt-out comment that read as unused once its file was scanned for the first time.

So a `<subject>_tests/` (or `_test/`) directory now counts alongside `tests/`, in the one predicate all of these share, and `isRustTestPath` routes its relative path through it too. The alternative was 25 opt-out comments apologizing for test code being test code.

Verified: `error-string-match`, `probe-unwrap-justified`, `lock-poison`, `test-sleep`, `no-hand-rolled-fixture`, and `mtp-dropping-timeout` all green; `gofmt`, `go-vet`, `staticcheck`, and `go-tests` green.
`scan_conflicts.rs` arrived with the duplication sweep and is production code three backends call, so it belongs in the map beside its siblings.
…e way the house set does, so lint stops removing the binding

`NewEntryNameField.svelte` typed `value` as required while defaulting it with `$bindable()`. `@typescript-eslint/no-useless-default-assignment` reads a default on a required property as useless, and its autofix deletes it, which silently turns off two-way binding: `pnpm check` then rewrote the file and `svelte-check` failed on both dialogs with "Cannot use 'bind:' with this property". CI never sees the rewrite (`--ci` disables auto-fixing), so this only ever breaks a local run, and it breaks it in a way that reads like a bug in the dialogs.

`value?: string` with `$bindable('')` is what `TextInput`, `TextArea`, and `Chip` already do, and it survives the lane. The prop comment says why, since the required-looking version is the tempting one.
…ht themed files, so a test is found by its subject instead of by scrolling 2,265 lines

`entries/tests.rs` was the longest file left on the `file-length` list. It becomes `entries/tests/`, following the `copy_tests/` model: `mod.rs` carries the shared imports, the `insert_dir_with_stats` / `insert_file` / `seed_row` fixtures, and the child declarations, and every child opens with `use super::*;`.

The seams follow the behavior each test drives: `insert_upsert.rs` (212), `delete.rs` (141), `delta_propagation.rs` (355), `hardlink_dedup.rs` (420), `symlinks.rs` (257), `move_entry.rs` (587), `bulk_reconcile.rs` (154), `id_counter.rs` (96), plus `mod.rs` (80). Delete-by-id and delete-subtree sit with the other delete paths while their `*_auto_propagates_delta*` twins sit with the upsert propagation tests, because the propagation contract is the thing under test there, not the delete. The bulk-reconcile suppression guard and the id-counter healing tests, both about the writer's own bookkeeping rather than a single mutation, get one small file each rather than a shared grab bag.

Pure move: all 33 tests keep their names and their bodies byte-for-byte, verified by comparing every extracted body against the original; the only line-level differences across the whole split are the six section-banner comments becoming per-file `//!` docs, the eight `mod` declarations, and the eight `use super::*;` lines. The three helpers moved once each into `mod.rs` and are shared, not copied. No production code changed.

Proof: the sorted leaf test-name list from `cargo nextest list -p cmdr-index -- writer::entries` is identical before and after (33 names), and `cargo nextest run` reports 33 passed. `file-length` shrink-wrapped the stale 2,138-line entry away and added none; `jscpd-rust` is unchanged; `clippy`, `rustfmt`, `lock-poison`, `error-string-match`, `test-sleep`, and `probe-unwrap-justified` are green.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013JPKrH6kufjw9Q4eKmxqsy
@vdavid vdavid closed this Sep 4, 2026
@vdavid
vdavid deleted the claude/duplication-file-length-swarm-hit6i1 branch September 4, 2026 11:13
@vdavid

vdavid commented Sep 4, 2026

Copy link
Copy Markdown
Owner Author

Landed on main as 908960540 (rebased onto current main, re-authored, so the hashes differ).

Two resolutions worth recording, since the result differs from this PR as written:

  • The cmdr-fs conflict mapper was dropped. main had already done the same dedup, and better: 66734822d added volume/scan_walk.rs, whose conflicts_against is equivalent to this branch's conflicts_in_listing, and its call sites carry behavior this branch's lack (SMB's NotFound → empty list, MTP's parent-listing fallback). main's side won; the extra test assertions from here were ported into scan_walk_test.rs.
  • copy_tests/ landed as five suites, not eight. main had independently rehomed 22 of these tests to the modules that own their symbols, so error_mapping.rs, remove_tree.rs, and scan_hints.rs would have duplicated tests that already live elsewhere. The directory was rebuilt from main's current bodies rather than this branch's stale copies. The test-name set is identical to main's old copy_tests.rs, so nothing was lost.

The allowlist shrink this PR was after all landed: jscpd-frontend 22 pairs → 2, jscpd-rust 46 → 33, file-length 87 files → 85, with no new entry and no raised number.

Branch deleted.

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.

2 participants