Skip to content

v3.15.0: near-miss clone detection, diagnosable threshold overrides, community bug fixes

Choose a tag to compare

@BartWaardenburg BartWaardenburg released this 12 Aug 07:41
· 221 commits to main since this release
Immutable release. Only release title and notes can be modified.
v3.15.0
66ae724

Highlights

Near-miss clone detection and smarter duplicate triage

fallow dupes --near (config duplicates.near) now also detects function-scoped clones with small structural edits, reported with a similarity score. Every clone-group finding carries a new spread field, and --top ranking boosts clones scattered across distant files above local ones, so the duplicates worth consolidating first rise to the top. A new duplicates.ignoredClones list ("dup:<fingerprint>:<instance_count>") silences clone groups a reviewer has accepted; fingerprints hash the normalized token sequence, so formatting-only edits keep a reviewed clone recognized.

Threshold overrides are finally diagnosable (health schema 10)

A health.thresholdOverrides entry that did not silence a finding now tells you why: every override row carries a required dimension (complexity or crap), a new insufficient status marks an override that raises a ceiling the code still exceeds, and an optional outstanding[] lists the dimensions a matched unit still breaches. Overrides now also apply to synthetic <template> findings in Svelte, Vue, and Astro single-file components, and those findings get a framework-correct suppression hint (<!-- fallow-ignore-next-line complexity --> on the line above the reported line) instead of Angular decorator advice. A follow-up pass made the override rows agree with their findings everywhere: the <component> rollup consults the override resolver, outstanding no longer swaps dimensions between same-named units, status is scored across ceilings, and CodeClimate plus GitHub annotations quote the effective threshold instead of the run-global one. Standalone health and combined output move to schema_version 10; audit output does not embed the health contract and stays at 9. Thanks @lcestou for the report.

Bug fixes

  • Type-aware analysis respects an explicit "private-type-leaks": "off". Enabling typeAware force-enabled the opt-in rule regardless of config, which also made the api-surface sidecar capability mandatory. The explicit setting now wins; unset configs keep the default-on behavior. Thanks @thewirv.
  • cacheMaxAgeDays and FALLOW_AUDIT_CACHE_MAX_AGE_DAYS actually reclaim audit caches now. The base-snapshot cache GC only visited entries under the current repo's hash prefix, so caches from linked worktrees accumulated forever. Abandoned entries from other repo identities now age out, live owners are skipped (a repo's own 0 cannot be defeated from outside), and invalid env values warn instead of silently falling back. Thanks @EvanAgee.
  • --gate new-only no longer fails clone-removal refactors. Extracting a shared helper and deleting duplicate instances shifted the clone group's attribution key and made the surviving duplication look introduced. Groups whose instances contain no added line are now demoted to inherited, while a genuinely pasted clone still fails the gate. Thanks @devmax128.
  • Framework template findings show which conditions caused them. A <template> complexity finding in Vue, Angular, Svelte, or Astro now reports each decision point at its own line and column with the weight it added, so the VS Code inline breakdown and health --complexity-breakdown work in templates too. Thanks @Ericlm.
  • Windows: added-line attribution now sees untracked files. Native backslash paths passed to the untracked-file diff meant every added-line check on an untracked file silently missed on Windows.
  • Svelte await-block complexity labels match the source. {#await} / {:then} contributions were labeled if; they now use explicit await and then kinds, including inline then/catch continuations.
  • Malformed config shapes fail loud. A malformed extends value, an unknown key in an overrides entry, or an unknown key in an ignoreExports rule now fails config load with a named error instead of silently disabling the rule it was meant to configure. Run fallow config before upgrading to surface rejected shapes ahead of CI.
  • Resolver fixes: .js specifiers resolve to declaration-only .d.ts modules; ignoreUnresolvedImports entries with a leading ./ match again; multi-binding re-exports from a missing module produce one finding anchored on the specifier instead of one per binding.
  • Plus: fallow --help lists every subcommand again, fallow setup-hooks can no longer duplicate a hand-edited managed block, audit read failures no longer fabricate weakening signals, a malformed pnpm-workspace.yaml warns instead of silently disabling catalog resolution, MCP workspace comma lists work on API-backed tool paths, untrusted values are escaped in github-summary tables, fallow-ignore-next-line inside multi-line block comments anchors correctly, VS Code binary downloads can no longer hang LSP startup, and LSP analysis no longer blocks the dispatch loop.

Performance

Module resolution and graph build got faster: resolve fallback paths reuse the session canonicalize cache, dynamic-import pattern matching memoizes compiled glob matchers, and reference attachment on high-fan-in modules replaces two quadratic rescans with indexed lookups.

Compatibility

  • Health and combined JSON output move to schema_version 10 (required dimension on threshold_overrides[] rows, new insufficient status, optional outstanding[]). Audit output stays at 9.
  • Two --format compact health line grammars gained fields (exceeded= on high-complexity:, a dimension segment and optional outstanding= on threshold-override:); key=value consumers are unaffected.
  • Three previously-tolerated malformed config shapes are now load errors (see above).
  • TypeScript consumers: each output field references its own exact version alias (CheckSchemaVersion, HealthSchemaVersion, ...); the legacy SchemaVersion export remains an alias of CheckSchemaVersion.

Full Changelog: v3.14.0...v3.15.0