Repository navigation
M0: stop fixer damage and silent zeros; fix worst TypeScript false positives - #1
Merged
Merged
Conversation
…w prompt glob
## The problem
CI has been red on `main` since 2026-04-06, and on every PR opened
since, because of 5 pre-existing test failures that are unrelated to
any feature work. Because they live on `main`, every PR inherits them
and cannot go green without bundling a workaround. Two distinct root
causes:
1. Three bash tests in
`desloppify/tests/lang/common/test_bash_unused_imports.py` call
`detect_unused_imports`, which parses via tree-sitter. tree-sitter
is an *optional* dependency (`[project.optional-dependencies]`),
so the `tests-core` job (which installs no extras) does not have
it; there the function returns `[]` and the tests, asserting
specific findings, fail. In `tests-full` (installs `.[full]`)
tree-sitter is present and the tests pass.
2. Two review tests in
`desloppify/tests/review/test_review_commands.py` and its
integration counterpart use
`list(runs_dir.glob("*/prompts/batch-*.md"))` without sorting.
The glob order is filesystem-dependent: on macOS it happens to
return `batch-1.md` (the batch with `historical_issue_focus`)
first, on Linux it returns `batch-2.md` first. The assertion
`"Previously flagged issues" in prompt_text` then fails on Linux
because that string only appears in `batch-1.md`.
## The solution
Two small, behaviour-preserving test fixes:
1. Gate the bash test file on tree-sitter availability with the exact
pattern its sibling tree-sitter tests already use
(`test_treesitter.py`, `test_treesitter_complexity_and_integration.py`):
```python
pytestmark = pytest.mark.skipif(
not is_available(),
reason="tree-sitter-language-pack not installed",
)
```
This is a consistency fix — the bash file was the only
tree-sitter-dependent test file missing the guard. Coverage is
preserved: with tree-sitter installed (`tests-full`) the tests
run and assert real findings as before; without it (`tests-core`)
they skip cleanly, because the feature genuinely cannot run.
2. Wrap the review test's glob in `sorted(...)`:
```python
prompt_files = sorted(runs_dir.glob("*/prompts/batch-*.md"))
```
This makes the test deterministic across operating systems —
`batch-1.md` always sorts first — and removes a latent flake that
only passed on macOS by luck. The assertion still verifies exactly
what it intends (batch-1's content).
## Scope
This is a standalone maintenance PR against `main`: it touches only test
files and changes no product behaviour. Landing it here gives `main` —
and therefore every open and future PR (peteromallet#614, peteromallet#616, …) — a green
baseline, instead of each PR having to carry the same workaround.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Kotlin resolves some imports by convention rather than by name, so the imported
symbol never appears in the file body:
import androidx.compose.runtime.getValue
import androidx.compose.runtime.setValue
...
var expanded by remember { mutableStateOf(false) }
The identifier search in detect_unused_imports never sees "getValue", so every
Compose file using property delegation is reported as having 2 unused imports.
Acting on the finding breaks the build. On a Compose Multiplatform project this
was 37 of 73 unused-import findings — over half the detector's output.
Adds a TreeSitterLangSpec.implicit_import_uses field: (name_pattern, body_pattern)
pairs declaring per-language conventions where a matching body pattern means the
import is used. Kotlin declares getValue/setValue/provideDelegate (guarded on the
`by` keyword) and componentN (guarded on destructuring declarations). Other
languages are unaffected — the field defaults to empty.
Genuinely dead delegation imports are still flagged: the guard requires the
convention's syntax to actually be present in the file.
The Kotlin plugin declared no zone_rules, so it fell back to the common rules, which look for a '/test/' or '/tests/' path segment. Kotlin projects are laid out by Gradle source set instead, and Kotlin Multiplatform names every test source set '<target>Test': shared/src/commonTest/kotlin/... sync/src/jvmTest/kotlin/... composeApp/src/androidUnitTest/kotlin/... None contain '/test/', so every test file was zoned production. On a KMP project that put all 241 files in the production zone, with 64 test files reported as orphaned (a test file has no importers by definition) and hardcoded test credentials raised as production security findings. Adds desloppify/languages/kotlin/_zones.py covering KMP and plain Gradle/Android test source sets, marking Gradle build scripts as config, and wires it into the plugin via zone_rules.
The kotlin plugin declared the ktlint tool with fmt="json", which maps
to the generic parse_json parser. That parser expects a flat list of
objects with top-level file/line/message keys (like most linters), but
ktlint's --reporter=json output nests violations per file:
[{"file": ..., "errors": [{"line", "column", "message", "rule"}]}]
Because parse_json looked for top-level "line"/"message" keys that
don't exist on ktlint's file objects, every entry was silently dropped,
so ktlint_violation always produced zero entries despite ktlint exiting
non-zero with real violations. This surfaced as a permanent
'Coverage reduced (ktlint_violation): ... tool_failed_unparsed_output'
warning and meant Kotlin projects never got real ktlint findings from
desloppify.
Add a dedicated parse_ktlint parser that understands the nested
file/errors shape, register it in PARSERS, and switch the kotlin plugin
to fmt="ktlint". Verified against a real multi-module Kotlin project:
ktlint_violation coverage is no longer reduced and the mechanical
'Code quality' score reflects real lint findings.
The csharp plugin discovered only `.cs`, so `.razor` and `.cshtml` were invisible. Code reachable only from markup therefore looked unreferenced: a `Widget.razor.cs` code-behind partial is reported as an orphaned file with zero importers and a suggestion to delete it, even though deleting it breaks the build. Views are parsed for edges but stay out of the scored extension set, mirroring how the typescript plugin already handles `.svelte`, `.vue` and `.astro`. Scores for existing projects are unchanged. Edges resolve by symbol name rather than by namespace. A view's `@using` says which namespaces are in scope, not which files it depends on, so linking a whole namespace would mark every file in it as live and hide genuinely dead code. Resolved per view: - types the view names, via a type-name index - extension methods it calls, which name no type at the call site - its own code-behind partial (`Widget.razor` -> `Widget.razor.cs`) - components it renders, including components declared in plain C# - partials and layouts referenced by string name - view components and tag helpers, which resolve by naming convention - `@page` marks a view as a routable root, like `Program.cs` - `_Imports.razor` and `_ViewImports.cshtml` usings apply to the subtree Also zones the ambient import files as config and `.razor.g.cs` / `.cshtml.g.cs` as generated, and stops `build_dep_graph` returning early on a project that is all views and no `.cs`. Measured on a ten-project Razor Pages solution: 334 views enter the graph, contributing 1052 edges across 200 `.cs` files, with 136 routable pages marked as roots. No file changed orphan status on that codebase, because the existing namespace matching already linked them.
ts_build_dep_graph discarded every import edge when file_list used relative paths, which made every file in the project look orphaned. resolve_import returns a path in the same space as the source_file it is handed, so a relative file_list produces relative results. The builder then unconditionally joined those onto the absolute scan_path and tested membership against the relative file_set, so the lookup never matched. Reproduced on a real React Router project: same file set, relative file_list gives 0 edges, absolute gives 142, and a db.server.js with 15 importers was reported as having zero. Running the scanner over that project, orphaned findings drop from 49 to 24 and code quality goes from 42.8% to 72.0%. Resolved paths are now matched against the file set in whichever space it uses, with the previous scan_path-relative interpretation kept as a fallback. The existing test only covered an absolute file_list with a scan_path-relative resolver, which is the one combination that worked. Added a regression test for the relative case.
Route modules and framework entry points have no importers by design -- the file-based router loads them from the filesystem -- so every one of them is reported as an orphaned file on every project of this shape. Mirrors the existing Next.js App Router handling rather than adding a new mechanism: detect the framework at the scan root, then exempt its convention files. Detection prefers a react-router.config.* or remix.config.* file and falls back to checking package.json for @react-router/* or @remix-run/*, because the framework's own template ships a Vite config rather than a react-router.config. Exempted: anything beneath an app/routes/ or src/routes/ directory, plus root, entry.client and entry.server beside it. An ordinary module such as app/db.server.js is deliberately still reported -- a genuinely orphaned file has to stay visible, which is what the negative tests cover. On a real React Router project this drops orphaned findings from 49 to 27.
The bash unused-import check treated a source directive as used only when the sourced file's basename reappeared in the script body. Scripts that source a function library (source ./lib.sh) and then call its functions or read its variables never repeat the basename, so every such script was flagged as an unused import. Resolve the sourced path relative to the sourcing script, parse it, and collect the symbols it defines: function definitions in both name() and 'function name' forms plus top-level variable assignments, including export/declare wrappers while excluding function-local declarations. The import counts as used when any defined symbol is referenced in the script. Unresolvable paths keep the existing basename heuristic, and per-run caching avoids reparsing a library shared by many scripts.
The unused-import detector took the last path segment as the in-code identifier. In Go that is frequently wrong, so every affected import was reported as unused. Scanning a real Go repo produced 20 false positives and no true positives: _ "github.com/joho/godotenv/autoload" -> "unused import: autoload" "gopkg.in/yaml.v3" -> "unused import: v3" "math/rand/v2" -> "unused import: v2" "github.com/go-playground/validator/v10"-> "unused import: v10" "github.com/anthropics/anthropic-sdk-go"-> "unused import: anthropic-sdk-go" These are used as yaml., rand., validator. and anthropic. respectively; the first is a blank import that exists only for its init() side effect. Note that a hyphenated segment can never be a Go identifier at all. There is also a general argument: go build fails on a genuinely unused import, so a Go module that compiles cannot have one, and any finding of this class on a compiling module is a false positive. Go now gets its own resolver that skips blank and dot imports, strips major-version segments (/v2, .v3), and returns None when the identifier cannot be determined from the path alone — callers treat None as "do not report", because a false negative is far cheaper than a false positive here. Verified: full suite passes (5825 passed, 3 skipped); scanning the repo that surfaced this drops its 17 unused-import findings to 0 with no new findings.
The GDScript dependency graph only followed `preload("res://x.gd")` and
`extends "res://x.gd"`. Godot code is rarely linked that way: scripts refer to
each other by their global `class_name`, and are attached to nodes through a
scene's `[ext_resource type="Script"]` block or a `project.godot` autoload
entry. None of those were resolved, so on a real project essentially every
`.gd` file reported zero importers.
`_find_project_root` also searched upward only. A Godot project is commonly one
directory inside a larger repository (engine plugins, build tooling and CI
beside it), and the scan root is that repository — so no `project.godot` was
found, every `res://` path failed to resolve, and even the preload edges that
were implemented produced nothing.
Measured on a 384-file Godot project: orphaned findings drop from 402 (one per
file, plus a matching false test_coverage finding each) to 7 genuine ones.
- follow `class_name` declarations and their use sites as graph edges, with
comments and string literals stripped first so a name mentioned in prose does
not fabricate a dependency
- add `find_gdscript_dynamic_imports`, reporting scripts a scene or autoload
attaches, via the existing `dynamic_import_finder` orphan hook
- search downward for `project.godot` when no ancestor holds one
- thread `dynamic_import_finder` through `run_coupling_phase`
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Godot's project templates and documentation use PascalCase directories, so a project's folders are `Scripts/`, `Scenes/` and `Tests/`. The zone rules matched only the lowercase spellings, so on a real project every suite in `Tests/` was classified production and scored as if it were shipping code, while the one file classified as a test was `test_scene_builder.gd` — production code that builds an in-game scene and merely starts with the Python/JS `test_` prefix. That prefix is not a Godot convention and is dropped; a Godot suite is `<name>_tests.gd`, `<name>_test.gd` or `<name>_suite.gd` inside a tests directory. Test-to-source mapping had the same import blindness as the dependency graph: it read only `preload`/`extends`, but a suite names what it exercises by global `class_name`, so nothing mapped and every file read as uncovered. Measured on a 384-file Godot project: test health 0.0% -> 42.1%, with the 23 suites now recognised as tests and credited against what they cover. - match Godot-cased test directories, and `_tests.gd`/`_suite.gd` suffixes - resolve a bare `class_name` in `resolve_import_spec` via a cached registry built from the production file set - report class names from `parse_test_import_specs`, with comments and strings stripped so a class merely described in prose is not counted as covered Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Resolving class_name references made the graph correct but reported the new edges as import cycles, including one spanning 86 files. A class_name reference cannot cycle at load time: Godot resolves the global registry lazily, so two scripts naming each other — a plug and its port, a cable and its socket — is ordinary and correct. Only preload and extends can cycle while parsing. Record class_name edges in `deferred_imports`, which `detect_cycles` already skips, leaving preload/extends cycles reported as before. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
A registry or catalog commonly holds a script path in a data table and load()s
it later:
{"id": "atari_2600", "script": "res://Scripts/Objects/system_models/atari_2600_model.gd"}
No preload or extends pattern can see that, so every model registered this way
reported as orphaned even though the registry that names it is the only thing
that ever instantiates it. Comments are stripped first, so a path discussed in
prose is not counted as a reference.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
… from the package, not the path segment Source: peteromallet#703
…ry usage in unused-import detector Source: peteromallet#699
Follow-up to upstream PR peteromallet#682 (Razor/Blazor graph linking): views enter the C# dependency graph as edge sources, and detect_orphaned_files never filtered graph nodes by extension, so an unreferenced .razor/.cshtml view over 10 LOC was reported orphaned — a class of finding that did not exist before peteromallet#682. Apply the extension filter the function already receives, and strengthen the razor/orphan test to use a non-routable view so it actually exercises the filter instead of passing via the @page entrypoint path. Co-authored-by: Daniel Grimes <dan@dbhq.uk>
The JavaScript and TypeScript tree-sitter specs matched only
(import_statement ...), so a CommonJS codebase produced an essentially empty
dependency graph. Every file then reported importer_count == 0, which the
orphaned, coupling, single_use and test_coverage detectors all read.
On a 1372-file Strapi backend (1128 files using require(), 48 using ESM
import) this reported 1090 orphaned files -- including the logger imported by
175 modules and the core chat orchestrator.
Add require('...') and dynamic import('...') patterns to both specs. The
require pattern uses an (#eq? @_require_fn "require") predicate so
notrequire() and obj.require() do not match. Same 1372-file backend now
resolves logger.js to 175 importers and drops the zero-importer set to 36.7%,
the residue being framework convention-loaded files.
ts_build_dep_graph normalized every resolved import with join(path.resolve(), resolved) whenever it was not absolute. But file_finder returns paths relative to the current directory, and resolve_js_import derives its candidate from the source file, so it also returns a cwd-relative path. Joining that onto the absolute scan root produced a doubled path that never matched file_set, and every edge was dropped. Key the lookup on the absolute form of each file_list entry and try the resolved path as-is before falling back to scan-root-relative, so edges match whether the resolver returns absolute, cwd-relative or scan-relative paths. Edges are now stored under the file_list key rather than the normalized string, keeping imports and importers consistent with the graph keys. With both fixes, the 1372-file Strapi backend goes from 1090 reported orphans to 268, and the coupling detector finds 320 single-use candidates where it previously found 0.
Committed by: Developer
…ash tests on tree-sitter; sort review prompt glob (@elfensky) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…positive fixes, runner fixes, and one orphan-detector fix (@awdemos) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…6 framework scans (@jimmybrancaccio) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…unction bodies (@treygoff24) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…e() as an import edge (@sandeep-patel-alepo-fifth) Conflict resolution: graph.py keeps the relative-file-list fix from peteromallet#696 (merged via peteromallet#744), which covers the same bug peteromallet#750 fixed; the require() query change and both regression test sets are kept. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ck 1.6.3 The extractors import QueryCursor, which only exists in tree-sitter 0.25+, so the old >=0.21 floor installed a combination that crashes. Language pack 1.6.3 ships without an importable module, so exclude it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
find_dead_log_variables removed any declaration whose name only appeared in a removed log, including `const result = await saveUser(user)`, and cut only the first line of multi-line declarations. Restrict the cascade to complete one-line declarations with literal or identifier initializers. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The TypeScript unused-imports, unused-vars, unused-params, debug-logs and empty-if-chain fixers produce broken or behavior-changing edits on ordinary code (see dev/FORK_ROADMAP.md §2A). Until they are rebuilt on a real parser: - FixerConfig gains `unsafe`; unsafe fixers only write with --unsafe (dry-run still works). - Unsafe fixers are left out of lang capabilities, so next/plan/narrative never steer agents toward them. - The unused-imports cascade skips itself when that fixer is unsafe. - `detect logs --fix` no longer deletes lines itself; it points to `autofix debug-logs`. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Resolve node_modules/.bin/tsc by walking up (hoisted monorepos), then a global tsc. Never `npx tsc`: without a local install it downloads the unrelated "tsc" npm package and the scan reported zero unused symbols. - Pass --noUnusedLocals/--noUnusedParameters on the command line instead of writing tsconfig.desloppify.json into the user's project; stdin is /dev/null and output is forced non-pretty. - Parse every unused diagnostic (TS6133/6138/6192/6196/6198/6199/6205), not just 6133/6192. - Missing compiler, wrong package, crashed runs and tsconfig errors now record reduced coverage (new record_reduced_coverage helper) instead of reading as a clean result. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Find the package root (nearest package.json) and a locally installed knip by walking up, so the default `--path src` scan runs it. Workspace packages run from the monorepo root with --workspace. - Call the knip binary directly instead of `npx --yes knip`, and drop --no-gitignore so ignored build output is not analysed. - Read `line` from Knip's JSON; `pos` is a character offset and was being reported as the line number. - Resolve reported paths against Knip's cwd, not the scan path. - Potential is the export population, not the failure count. - When Knip can't run, record reduced coverage with a remediation hint instead of reporting zero dead exports. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…raph - Smell detectors kept only the first 50 matches per smell across the whole scan, so on large repos most files' smells vanished (zod: 734 `as any`, 50 reported, all in one benchmark package). - The TS import graph only contained files that import something, so a dead constants or types module could never be reported as orphaned. Seed every non-declaration TS file. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The import graph's tsconfig reader used strict json.loads, followed only a single relative string `extends`, ignored `references` and resolved inherited paths against the wrong directory. Any failure fell back to `@/ -> src/`, so aliased imports produced false orphans and "delete dead files" advice (16/16 false orphans on vercel/commerce). - Parse tsconfig as JSONC (comments, trailing commas, BOM). - Follow `extends` chains: relative paths, package specifiers from node_modules, and arrays, resolving paths/baseUrl against the config that defined them. - For solution-style configs, read paths from the projects listed in `references` (the Vite template layout). - Treat `baseUrl` as a fallback root for bare specifiers. - Prefer the first `paths` target that exists, as tsc does. - ts_alias_resolver now matches longest prefix first, like resolve_alias. On vercel/commerce: graph edges 39 -> 121, files with no importers 38 -> 17. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…rtions Test-quality analysis only knew Jest/Chai styles, so ky's retry tests (450 t.is/t.deepEqual assertions) were reported as assertion-free and became the #1 queue item. Recognise t.<assertion>(...), expect.soft / expect.poll, expectTypeOf and assertType, and count test.serial / test.only / backtick names / .each tables as test functions. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Next.js 16 renamed middleware.ts to proxy.ts; it (and mdx-components, forbidden, unauthorized, global-not-found, manifest) was reported as an orphan and became the top "delete dead files" action. - The eval check matched page.$eval(), redis.eval() and JSDoc examples. Only bare/global eval and new Function count now, and block-comment lines are skipped by the line-level security checks. - innerHTML = '' (clearing) and innerHTML comparisons are no longer XSS findings. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Match only the JSDoc @deprecated tag, not any "deprecated" text in strings, identifiers or comments. - Record whether a deprecated symbol is exported and whether it is used elsewhere in its own file. Exported symbols with no importers inside the scan may be a library's public API, so they stay tier 3 with a note instead of a tier-1 "safe to delete". Only unexported, unused symbols are quick fixes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
_categorize_unused fell back to "imports" for anything it couldn't classify (parameters, destructured bindings, class members, unreadable files), labelling them "unused imports" (tier 1) and routing them to the import-rewriting fixer. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
These were all scored as production code: tsd/vitest type tests (*.test-d.ts, test-d/), e2e suites (e2e/, cypress/, playwright/, *.cy.ts), benchmarks (zod's 734 `as any` hits came from packages/bench), codegen output (*.gen.ts, *.generated.ts, *_pb.ts, gql/, .d.mts/.d.cts) and common tool configs (playwright, tsup, rollup, astro, svelte, nuxt, drizzle, ...). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Fixer output went through read_text()/safe_write_text, which converted CRLF to LF, dropped nothing but hid a leading BOM from the first-line import match, replaced symlinks with regular files and left rewritten files at mode 0600. Decode as UTF-8 explicitly, give transforms normalized text, then restore CRLF/BOM and write through the symlink target with the original permissions. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
tree-sitter-language-pack 1.x downloads grammars at runtime. Offline or behind a proxy, get_parser raises, every tree-sitter phase swallows the error, and the scan read as clean. Record grammar load failures in _get_parser and, after the detector pipeline runs, turn them into a "treesitter" reduced-coverage warning naming the grammars and the download command to fix it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
`desloppify move` reported "Done." after breaking TypeScript projects (roadmap §2 #12). Stopgap until move is rebuilt on the shared resolver: - Languages that opt in (TypeScript) build the move graph from the project root, so importers outside src/ are seen and rewritten. - If any importer of a moved file gets no rewrite (e.g. a custom path alias), the move aborts and lists them; --dry-run warns, --force proceeds. Languages whose imports don't depend on file location (C#) are unaffected. - ESM/NodeNext specifiers with runtime extensions ('./x.js') are rewritten, keeping the extension. - Directory moves no longer rewrite relative imports between files that move together, which were being re-pointed at the old location. - Replacements are applied in one pass, so one rewrite can't be rewritten again by a later one. Verified on a fixture (alias, ./x.js, scripts/ importer, sibling imports): tsc --noEmit passes after a directory move, and an unknown alias importer aborts the move with no files touched. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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.
Milestone 0 from
dev/FORK_ROADMAP.md: stop the fixers corrupting code, stop detectors silently reporting zero, and remove the worst TypeScript false positives. Merge this with a merge commit (not squash) so upstream contributors keep authorship.Upstream PRs merged (
--no-ff, authorship kept)require()edges. This conflicted with fix: keep import edges when file_list holds relative paths peteromallet/desloppify#696 (in Integrate 27 reviewed PRs: false-positive fixes, runner fixes, and one orphan-detector fix peteromallet/desloppify#744), which fixes the same relative-path bug.graph.pykeeps fix: keep import edges when file_list holds relative paths peteromallet/desloppify#696's version; fix(js/ts): count CommonJS require() as an import edge peteromallet/desloppify#750'srequire()query change and both test sets are kept.Fixes
Fixers that break code
unused-imports,unused-vars,unused-params,debug-logsandempty-if-chainfixers only write with--unsafe;--dry-runstill works.next, the plan and the narrative no longer suggest them.const result = await saveUser(user).detect logs --fixno longer deletes lines itself.move(TypeScript):./x.jsspecifiers;--forceoverrides).Silent zeros now reported as reduced coverage
npx tsc(which downloads an unrelated npm package);--path srcworks, and from the workspace root with--workspacein monorepos;lineinstead of the character offsetpos;False positives and harmful advice
extendschains (relative paths, packages, arrays);references;baseUrlas a fallback root;pathstarget that exists.proxy.tsand the other new convention files are entry points.evalmatching no longer flagspage.$eval,redis.evalor JSDoc examples; clearing withinnerHTML = ''is no longer an XSS finding.vars, notimports.tree-sitter>=0.25(needed forQueryCursor) and excludestree-sitter-language-pack1.6.3, which has no importable module.Verification
maincurrently fails 5 tests, which chore(ci): unblock CI by gating bash tests on tree-sitter; sort review prompt glob peteromallet/desloppify#617 fixes.ruff(CI select),mypyandlint-importsare clean.tsc5 andknip6.39 run against a fixture project. An offline scan with an empty grammar cache now reports reduced coverage..jsspecifier,scripts/importer, sibling imports):tsc --noEmitpasses afterwards, and an importer using a custom alias aborts the move with no files changed.Not in this PR (tracked in the roadmap)
@generated-header zoning and directory-level zones, string-literal skipping in the security checks, a separateparamsunused category.exports), AST import extraction, entry points from package.json.🤖 Generated with Claude Code