feat(rees): detect resolved duplication via before/after comparison - #4760
Conversation
Adds a duplicationDelta analyzer that flags a duplicate block pair which existed in a changed file's pre-PR content and is no longer both present after (e.g. two near-identical functions consolidated into one) — the reverse of the existing duplication analyzer, which only detects NEW duplication a PR introduces against the rest of the repo. Confirmed by reading duplication-scan.ts directly before building on it (see PR description). Uses reconstructOldContent to recover each changed file's pre-PR text, then greedily assigns each old block to an unclaimed matching block in the new content so a duplicate-count reduction is detected correctly (rather than every old copy independently matching the same surviving text). Reuses duplication-scan.ts's own chunk-normalization and suffix-automaton matcher, now exported, so "what counts as a duplicate" stays identical between the add- and remove-detectors. Also fixes a repo-slug validation gap surfaced while mirroring the sibling analyzers' patterns: a bare [A-Za-z0-9._-]+ check accepts ".." as a whole segment (every character is individually allowed), so this analyzer requires an alphanumeric first character instead.
|
Superagent didn't find any vulnerabilities or security issues in this PR. |
Deploying with
|
| Status | Name | Latest Commit | Preview URL | Updated (UTC) |
|---|---|---|---|---|
| ✅ Deployment successful! View logs |
gittensory-ui | 1d8d6f4 | Commit Preview URL Branch Preview URL |
Jul 11 2026, 12:23 AM |
|
Tip 🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩 ✅ Gittensory review result - approve/merge recommendedReview updated: 2026-07-11 00:25:26 UTC
✅ Suggested Action - Approve/Merge
Review summary Nits — 6 non-blocking
Review context
Contributor next steps
Signal definitions
Visual preview
Click any thumbnail to open the full-size screenshot. Before = production · After = this PR's preview deploy. 🟩 Safe / merged · 🟦 Advisory · 🟨 Held for review · 🟥 Blocked / closed 💰 Earn for open-source contributions like this. Gittensor lets GitHub contributors earn for the work they already do — register to start earning →. Checked by Gittensory, a quiet PR intelligence layer for OSS maintainers.
|
The greedy, order-dependent old-to-new block assignment can rarely under-report a still-present duplicate as resolved in multi-candidate scenarios. Advisory-only, never a correctness/data-integrity issue, but worth stating explicitly rather than leaving implicit.
… onto boundedFetchText (#4759) (#4821) doc-comment-drift.ts, exhaustiveness-drift.ts, and complexity-delta.ts each carried their own private "fetch a file at headSha, bounded/streamed read capped at 1MB" helper -- near-byte-identical copies of the same logic, none with a timeout or circuit breaker. duplication-delta.ts (#4741/#4760) already migrated onto the more mature boundedFetchText (external-fetch.ts): a typed ok/failure result, a per-endpoint-category circuit breaker, a configurable timeout, and byte-size capping. Mirror that same fetchFileAtHead call pattern -- including the options.analysis.fetchText fallback for when an AnalysisContext is available -- in all three instead of leaving three more hand-rolled copies to drift further. doc-comment-drift.ts also switches to the shared githubHeaders() helper in place of its own inline auth headers, closing the one pre-existing inconsistency among the three (the other two already used it). Pure fetch-mechanism migration -- verified byte-faithful via each analyzer's existing test suite passing unchanged, plus one new test per file exercising the added options.analysis branch (mirroring duplication-delta.test.ts's own coverage of that path).


Confirmed: what
duplication(duplication-scan.ts) actually does todayRead
review-enrichment/src/analyzers/duplication-scan.tsin full before writing any new code (per thisissue's mandatory first step). Note the real file is
duplication-scan.ts, notduplication.tsas thisissue's description assumed.
Confirmed behavior matches the issue's working assumption:
scanDuplicationextracts only ADDED significant lines from each changed file's patch(
extractAddedBlocks), never anything from the file's own past.candidate files elsewhere in the repo — explicitly excluding the changed files themselves
(
if (changedPaths.has(entry.path) || isExcludedPath(entry.path)) continue;).its own history. It is a pure "does this new code already exist somewhere else in the repo" detector.
a real, unfilled gap this PR closes.
What this PR adds
A new
duplicationDeltaanalyzer (review-enrichment/src/analyzers/duplication-delta.ts) that detects thereverse signal: a duplicate block pair that existed in a changed file's pre-PR content and is no longer
both present after (e.g. two near-identical functions consolidated into one).
reconstructOldContentprimitive (Generalize reconstructOldContent into a shared before-content capability on AnalysisContext #4739) to recover each changed file's pre-PR text fromits post-PR content + patch.
duplication-scan.ts's own chunk-normalization + suffix-automaton longest-shared-run matcher (nowexported:
isSourceExt,isExcludedPath,normalizeFileBlocks,buildMatchIndex,longestSharedRun) —same
MIN_RUNthreshold, same "what counts as a duplicate" definition — instead of a second, differentlytuned algorithm.
each old block to an unclaimed matching block in the NEW content. This matters: a naive "does this old
block's text exist anywhere in NEW" check would let both old copies of an identical pair "see" the single
surviving occurrence and falsely conclude nothing changed. Greedy assignment lets only as many old blocks
survive as there are still-distinct occurrences in NEW.
partner, matched-line count) — never a bare total, never code content — so the sibling complexity-delta
sub-issue (Aggregate deterministic structural-improvement sub-score #4742, not yet started) has structured findings to aggregate.
clearly in the analyzer's own docs/notes rather than silently approximated. Left as a documented, scoped
follow-up rather than adding a second repo-wide tree crawl to this PR.
AnalyzerDescriptorcontract exactly (categoryquality, costgithub-light,requires: ["files","github-token","head-sha"], its ownrenderin the same modern per-descriptor style ascoverageDelta/callerImpact).duplication's existing new-duplication detection — it is untouched behaviorally; onlyfive previously-private helpers gained an
exportkeyword (verified via the full existingduplication-scantest suite passing unchanged).
Also fixed while building this
While mirroring the sibling analyzers' repo-slug validation pattern (
doc-comment-drift.ts/exhaustiveness-drift.ts/codeowners.ts), found that a bare/^[A-Za-z0-9._-]+$/check does not rejecta segment of exactly
".."— every character in".."is individually allowed by that character class, onlya first-character requirement catches it (which is why
codeowners.ts's own regex is stricter). Since thisnew file needed its own slug guard anyway, it uses the stricter,
codeowners.ts-style pattern from the start.Not fixed in the two pre-existing files (out of scope for this PR — see follow-up below).
Follow-ups flagged, not done here (deliberately out of scope for this PR)
doc-comment-drift.tsandexhaustiveness-drift.tseach hand-rollan identical private
readBoundedText+ inline fetch (no timeout, no circuit breaker) instead of theexisting shared
external-fetch.ts#boundedFetchText(which this PR's new analyzer uses, mirroringcodeowners.ts). A small, mechanical follow-up PR — the same shape as refactor(rees): promote reconstructOldContent into a shared analyzer helper #4752's own promotion ofreconstructOldContent— could migrate both to the shared helper.doc-comment-drift.ts/exhaustiveness-drift.tsnoted above.REES_ANALYZER_NAMESallowlist (src/review/enrichment-analyzer-names.ts) does not includeduplicationDelta. This is a separate, decoupled operator-config allowlist (.gittensory.ymlreview.enrichment.<name>toggles) — not required for the analyzer to run (defaultEnabled: truein itsown descriptor is authoritative for that), and nothing enforces the two lists staying in sync. Left as a
deliberate decision for whoever wires up per-repo toggling of this specific analyzer.
Gate status
npm run rees:test: green (1260/1260 — includes build + sourcemap validation +generate-analyzer-metadata.mjs --check+ the full existing suite — no regressions).npm run typecheck: green.reconstructOldContentreturningnull(unreconstructable patch) and
""(wholly-new file) as distinct test cases per its truthiness-checkcontract.
Closes #4741
Part of epic #4737