Consolidate duplicate code and repair clone matching and canonical navigation - #541
Merged
Merged
Conversation
) PR #485 made ReportCluster.mass an integer count (canonical nodes × additional visible occurrences, [RANK-MASS-SUM]) but left every render path on the two-decimal helper written for the old fractional weight, so the panel header, its body sentence, the stat row, the tree row tooltip, the file and folder row descriptions, the cluster document and the copy-for-AI payload all printed "527.00" for a count that can never carry a fraction — while the CLI text report printed "mass=527" for the same cluster. One formatter, `formatMass`, now renders a mass with no decimal point, and it is the only way a mass reaches text: the four-decimal `formatScorePrecise` had no remaining caller and is gone, and the two AI payloads that quoted mass at different precisions now print the same string. [RANK-MASS-SUM] in docs/specs/pipeline.md states the rule: mass is a whole number, every surface prints it as one, and the extension and the CLI print the identical string. Tests: - mass-format.unit.test.ts pins formatMass against the score formatter, pins that a measured pair signal keeps its two decimals, walks the parsed source of every file that prints a mass (extension host and webview) and fails if any rendered mass does not pass through formatMass, and scans the fixture with the staged CLI to assert the extension prints the exact string the text report prints for every cluster. - tree.topOffenders.unit.test.ts asserts the cluster tooltip, file row and folder row print "527", never "527.00". - cluster-document and command-impls tests now assert the exact whole-number line instead of the presence of the word "mass". - The playwright smoke fixture no longer lies about the wire: its masses are integers. The shared TS-AST walkers (descendants, hasDescendant) and parser move into webview-source.helpers.ts; the two test files that each carried a private copy now import them, which removes three duplicate clusters from clients/vscode/src. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…ool (gh #523) The MCP decodes every document the LSP serves it over IPC, and the two binaries can come from two builds: an extension bundle installed on 31 August beside an engine built after PR #485 renamed ReportCluster.weight to mass on 2 September. That drift surfaced as serde's raw field error — "ipc transport failure: ipc report parse: missing field `weight`" — on every agent-facing tool, naming neither binary, neither version, nor a remedy. PR #144 wired the IPC read path without any version negotiation, even though every report stamps its producer in tool_version and the fingerprint cache already partitions on it. Every IPC decode now passes through one guard, backend/wire.rs::decode ([MCP-IPC-WIRE-MISMATCH]): a reply stamped with another producer version is refused before it is decoded, and a reply that does not decode at the same version — the development-build case, where both binaries say 0.0.0-dev — is refused the same way. The refusal is a named BackendError::WireMismatch that carries both versions, the IPC method, the endpoint, what was found and the remedy (reinstall the VSIX so both binaries ship from one bundle, then re-run the analysis). The ten decode sites — report/get, report/forFile, report/forRange, session/config, duplicates/findSimilar, cluster/byId, pair/compare, merge/plan, embedding/listModels, embedding/setModel — no longer each carry their own "ipc <thing> parse" catch-all. docs/specs/mcp.md gains [MCP-IPC-WIRE-MISMATCH] beside [MCP-IPC-CLIENT], and [MCP-TESTING] names the refusal. Tests: - backend/wire/tests.rs pins the guard on a report another release wrote (tests/fixtures/report-from-another-release.json — the adversarial input, refused, never decoded): a foreign stamp is refused before decoding and no field error appears; a same-version drift names the moved field; an unstamped page says it carried no version; a report from this version decodes. - tests/ipc_wire_mismatch.rs drives the real deslop-mcp binary against a stub LSP serving that report: `duplicates` is refused on the stamp, cluster-by-id — whose page carries no stamp — is refused on the moved field, both name the MCP version, the engine version, the socket path and the remedy, and neither returns a page an agent could read as "no duplicates". - The stub LSP the #148 suite carried privately moves to tests/common/stub_lsp.rs with a scripted responder, and #148 now uses it. The in-bundle gate the issue asks for already exists: scripts/verify-vsix-package.mjs probes every bundled binary's --version against shipwright.json, so one bundle cannot ship binaries from two builds; this change covers the cross-install case that gate cannot see. Completes the guard that 9e284bd captured mid-flight: the remaining six decode sites, the fixture and constants renamed to say what they are (a report from another release, refused, never decoded), and the two mismatch suites collapsed onto shared helpers. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
… by that kind (gh #521, #522) #485 replaced the clone taxonomy on cluster surfaces with the neutral title "Duplicate code" and re-keyed every colour to the mass rank band, so the tree, bubble, underline, webview and HTML report could no longer say what relation a cluster holds, and rank-1 shape-only scaffolding painted crimson. This restores the taxonomy honestly instead of reviving the pre-#485 cluster-averaged signals. Engine ([CLONE-KIND-FOLD]): every cluster carries a `kind` — the weakest pair classification between its canonical occurrence and any other member, measured through the same pair/compare path an explicit comparison uses, reading the embedding pass's cosines rather than re-embedding. A member the direct comparison does not admit folds to `loosely_similar`. `ClusterKind` is generated from live-ipc.td; `buckets.rs` is the one label registry ([CLONE-KIND-LABELS]), with no advice sentences. Surfaces: JSON/text/markdown carry the wire label; HTML groups cards into one expander per kind and declares `--kind-*` colours; the terminal summary, LSP diagnostic message and data, and MCP cluster summaries name the kind. Extension ([CLONE-KIND-COLOR]): one paint table in design.ts, contributed as `deslop.kind.*` theme colours; the webview imports it instead of mirroring it. Colour follows the kind on the tree icon, bubble, underline and badges; the rank band keeps only its glyph density. Severity grouping becomes kind grouping; `severity.ts` and its test are deleted. Tests: every retired "Duplicate code" assertion now pins the kind title; bucket_groups pins identical-vs-nearly-identical in rank order; the report goldens are re-blessed and their contracts assert `identical` over the Type-1 corpora; the C# byte-truth suite pins that a wrapper-renamed window folds to `nearly_identical`; live-delta coverage walks the enum leaf; `kind.unit.test.ts` holds the extension's titles, taxonomy names and colours to what the bundled CLI renders and package.json contributes. Specs: taxonomy.md gains [CLONE-KIND], [CLONE-KIND-FOLD], [CLONE-KIND-LABELS], [CLONE-KIND-COLOR], [CLONE-KIND-TESTING]; severity.md becomes the two-channel model; SPEC, vsix, facets, fused, lsp, mcp, pipeline, REPORTING-CONTEXT and webview-runtime follow. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…ff with the engine's verdict on that pair (gh #524) The cluster panel's compare flow, plus the detector and spec repairs the same audit surfaced: - Engine: `PairEvidence.text_identity` (`byte_identical`, `indentation_only`, `different`) is read once from the pair's raw bytes next to the byte-identity verdict, so a copy that differs only by how its lines are indented says so on the wire ([FUSED-PAIR-SIGNALS]). Admission and classification are unchanged; the field is evidence only. - Extension: the native diff is titled `<left> vs <right>: <verdict>`, the verdict read from `deslop/pairCompare` (Identical bytes, Differs only by indentation, or the pair's clone kind). Tapping one occurrence row and then another compares exactly those two; the pick lives in the store; no per-row "Select for comparison" button and no gated "Compare selected occurrences" button exist ([VSIX-PAIR-COMPARE]). - Detector (gh #520): Rust type references no longer earn standalone fingerprints ([PIPELINE-FINGERPRINT-MERKLE-TYPE-REFERENCE]); a copied collaborator property may rename the operation called through it, so a renamed collaborator no longer defeats a genuine copy. Pinned by the Python inherited-boundary and Rust iterator-idiom suites. - Specs (gh #526): admission.md and pipeline.md carry the new contracts; cross-reference and restoration reports under reports/ are regenerated mechanically. Tests: pair_comparison (indentation-only copy), report_api, compare-title.unit, webview-cluster.unit, the Playwright smoke (two-row tap), clusters.e2e (one-click canonical compare on Alpha/Beta; indentation-only Gamma/Delta). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…n one is left broken `_vsix-install-code` deletes the unpacked extension directory before it installs, so a higher Marketplace version cannot keep winning. That one directory is what every profile points at, and `code --install-extension` registers the replacement in the default profile alone — so every other profile was left naming a directory the delete had just removed, showing "Unable to read file '.../package.json'" with no working extension behind it, after a build that reported success. Four of the developer's profiles were dead at once. The install now names every profile that already had Deslop.live, and no profile that did not, then proves no profile is left dangling. [DEPLOY-VSIX-INSTALL-PROFILES] Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
… scan would report A cluster id names one finding again (gh #531). The digest plus the member paths could not separate two copies of one shape inside one file, which feed it the same shape and the same path, so the id now also carries the cluster's position rank among the clusters sharing both. The first cluster of a source is still named by the source alone, so every id published before this is unchanged and a finding keeps its id when another copy of its shape appears after it. Derivation moves to cluster/identity.rs; [PIPELINE-DETERMINISM] states the rule. The shared-subtree rescue now requires the code two endpoints share to be a clone the scan would report on its own: at least min_nodes, checked before the content axes run ([FUSED-SHARED-SUBTREE-CORE]). Three site/tests scenarios open with the two Playwright lines every browser test starts with, and the one that shares nothing else rode the rescue onto the other two on a core of twenty nodes measuring exactly the content floor. The floor is the scan's own rather than the rescue's endpoint floor, because a near-miss is by construction smaller than its endpoints: python-type3's cores are 22 to 25 nodes and are real clones at --min-nodes 8. Both readers of a core share one judge_core. The two content_gate_signal_honesty accessor pins assert that the gh #460 false positive is reported. The fixture describes that pair as unrelated and the aligned core refuses it on an inconsistent rename, so they are ignored under [SKIP-UNFINISHED] citing gh #532, assertions untouched, with rows in CURATED_SKIPS and a plan section that says what closes them. The pair is held CLEARLY OUT in a new self-register, corpus/register/deslop.json, pinned at the merge-base where the original blocks live, so the score gate fails the moment anything reports it again. Its fifteen CLEARLY IN entries are byte-identical pairs verified by diff at that commit; the register is not blinded and says so. site/tests at floor 30 goes from 4 mixed-span clusters to 3 and 12.50% to 12.14%. Corpus score gate: 100% on click, cobra, axios and Deslop, no false negatives or positives. E2E 490 passed, core lib 215, core integration 228 and 193, LSP 62 and 80 under the Makefile feature matrix, MCP 10 and 126.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
TLDR
Consolidate duplicate parsing, rendering, and test code while repairing shared-subtree matching, wrapper-call filtering, Python fixture detection, and canonical navigation.
Details
deslop-coreindexes shared-subtree fingerprints by node identity, so a statement and its enclosing body cannot overwrite each other when their byte ranges coincide ([FUSED-SHARED-SUBTREE-CORE]). The existing fingerprint walk remains the single implementation.[CLONE-NOISE-LITERAL-VARIATION-CALLS]). Python fixture decorators are resolved from the AST, including calls, multiline arguments, and class methods ([CLONE-NOISE-PY-PYTEST-FIXTURE]).[VSIX-PAIR-COMPARE]). Comparison endpoints reuse the generated wire type; file/folder tooltips and tree settings share implementations.No coverage thresholds are lowered. The report wire changes already on
mainare not introduced again by this PR. Related to #532; issues remain open.How Do The Automated Tests Prove It Works?
a_differing_wrapper_callee_is_not_literal_variationfails when differing constructors are treated as interchangeable payload. The call-filter suite covers callback bodies, literal variation, and invariant authored logic.make cipassed on unchanged sources. Rust, extension-host, and webview coverage gates passed with unchanged thresholds; the command evidence is inreports/pr-validation.md.For AI
The diff against
mainis the complete scope. The main tree is identical to ancestorbd920260; the branch reconciles that squash ancestry without changing its validated source tree. Review and attribution use baselinef92300e5e1004ef6c53a94174a0d7e842232ec80. Preexisting registry output and embedding defects are not claimed as newly introduced regressions.reports/README.mdindexes the local measurements; GitHub records subsequent CI and merge state.