NewYour coding agent can read the release notes before it upgrades.Set up the MCP server →
pub.dev · #4349 most downloaded on pub.dev
2332 custom lint rules with 254 quick fixes for Flutter and Dart. Static analysis for security, accessibility, and performance.
Last release 2 days ago
06 Oct 2026
Ships fairly regularly
a new release about every 9 days
Most releases are documented
notes for 43 of the last 60 stable releases
1 version withdrawn
withdrawn after publishing
2 years old
326 releases · first in 2025
One column per month.
Skipped release - use 16.8.0 ---
Skipped release - use 16.8.0
Test-suite repair, dependency security updates, and a linting fix. No change to rule behavior or shipped output. log
audit --baseline now exits 1 only when a finding is new. It used to exit 1 for every finding, baselined or not, so the action's baseline input could never let a gate accept a known backlog. Same contract as cross_file --baseline and project_health --baseline. With no baseline file, every finding still counts.audit --since works for a project in a subdirectory of its repository (a monorepo package). The changed-file paths were joined onto the project path twice and matched nothing, so the audit covered no files. An unknown ref now also returns an empty list on git versions that report it as a "bad revision", instead of crashing.init --emit-ci writes the workflow at the repository root, where GitHub runs workflows from, and adds working-directory when the project is in a subdirectory. It honors an explicit --tier, and reports a write failure instead of printing a stack trace.lib/gen/**, test/** no longer yields a pattern with a leading space that matches nothing), accepts one pattern per line, and no longer aborts on an empty pattern. It fetches since given as a bare branch name, a branch with slashes or a commit SHA, and names the problem when it is on a remote other than origin. It removes a stale SARIF file before the run, creates the SARIF directory, handles an absolute sarif-file, and rejects resolve values other than true/false and a fail-on-count without its fail-on.v16 tag, which matched the publish workflow's v* trigger. Pre-releases leave v16 on the last stable release, the tag points at the release commit rather than whatever is checked out, and re-running the tag step repairs a v16 push that failed.extension/scripts/check_l10n_keys.py key-union regex rewritten to eliminate exponential backtracking (ReDoS vulnerability, code-scanning alert 23). No action required.avoid_string_substring now documents that it cannot prove bounds established inside a helper function it would have to inline; suppress with // ignore: at the call site when the helper is bounds-safe. No action required.working-directory for a project in a subdirectory. It was written under the project, where GitHub never runs it.git checkout -b. It refuses to publish when pubspec.yaml already had uncommitted edits of yours (a commit takes whole files, so they would have been pushed too), when there is nothing to publish, and when a branch of the same name already exists on the remote. Opening the pull request gives up after 15 seconds on a connection that never answers and offers the compare page.saropa_lints to pubspec.yaml keeps valid YAML for a dev_dependencies: with a trailing comment, four-space indentation, an existing entry at any indentation, and an empty {}. It also adds ^16.2.1 instead of the long-stale ^9.1.0.failOnVulnerability is enforced from the security advisories pub outdated reports, where it used to claim there was no data and pass. The pull request comment shows pass or fail against the threshold, the shell script uses a private temporary directory, and the GitLab job uses the maintained Flutter image.test job fail.v16 and v16.6.0) share a commit.Patch release fixing false positives in two lint rules, adding a project-level allowlist for avoid_ignoring_return_values , and resolving unwanted rel
Patch release fixing false positives in two lint rules, adding a project-level allowlist for avoid_ignoring_return_values, and resolving unwanted reload behavior in the Config and Findings Dashboards when editing text fields. log
avoid_ignoring_return_values now supports a project-level allowlist via analysis_options_custom.yaml — add method names under avoid_ignoring_return_values: safe_to_ignore: to exempt project-specific methods whose return values are safely ignored. No action required unless you have project-specific methods you want to allowlist.
google_sign_in_auth_token_from_authenticate no longer flags .accessToken reads on already-migrated GoogleSignInClientAuthorization results or unrelated model classes with a same-named field. No action required.
avoid_public_members_in_states no longer flags WidgetsBindingObserver/RouteAware/AutomaticKeepAliveClientMixin callback methods (e.g. didChangeAppLifecycleState, didPushNext, wantKeepAlive) on a State class that carries the interface via with or implements — their public spelling is mandated by the framework, so the rule's own suggested private-rename fix would have silently broken dispatch. No action required.
prefer_late_final no longer flags a late field whose assigning method is passed elsewhere as a bare tear-off (e.g. setState(_initFutures)) — the tear-off's runtime call count can't be bounded from the declaration site, so the field may genuinely be reassigned even though only one direct call site is visible in the AST. No action required.
avoid_ignoring_return_values no longer flags a project-local extension method whose name follows a mutate-verb convention (add*, append*, insert*, remove*, update*, set*) and returns bool — the same structural shape as allowlisted stdlib mutators like List.add, where the bool is a "did it happen" convenience the caller is not required to consult. No action required.
require_ios_accessibility_large_text no longer flags TextStyle(fontSize:) when the value comes from a non-const getter, method call, or property access — only bare numeric literals and const identifiers are flagged. Previously the rule pattern-matched source text for textScaleFactor/textScaler/MediaQuery substrings and missed any design-system token that applies Dynamic Type scaling through a helper. No action required.
<package>" links appear dead since the panel they lived in kept getting torn down. Both dashboards now wait until you leave the field before redrawing.The audit command now gives you complete visibility into suppressed warnings across your codebase. You can optionally expose findings previously hidde
The audit command now gives you complete visibility into suppressed warnings across your codebase. You can optionally expose findings previously hidden by ignore directives or baseline files to understand exactly what is being silenced in your project. log
New rule avoid_unbounded_image_in_full_bleed_container: flags an Image/Image.asset/Image.network/Image.memory/Image.file with no width/height/cacheWidth/cacheHeight sitting inside a full-bleed ancestor (Positioned.fill, SizedBox.expand, or a Stack with fit: StackFit.expand) — the image decodes at native resolution and is then stretched to fill an arbitrarily large parent, wasting decode memory. Skips false positives where a nearer SizedBox/Container/ConstrainedBox/AspectRatio already constrains the image's own size.
files.watcherExclude setting on activation and recommends missing patterns for heap dumps, build output, and tooling caches that can crash VS Code when tracked. "Add All" writes them into workspace settings in one click; "Dismiss" suppresses the prompt permanently for that workspace.extensionHostWarningGB, default 1 GB) that surfaces in the status bar — the blind spot behind the 2026-09-05 crash.files.watcherExclude patterns. Disable via saropaLints.systemHealth.workspaceHazardScan.saropaLints.showWorkspaceReadiness command that opens an actionable quick-pick listing each issue.--include-suppressed CLI flag through the UI — check it, run audit, and suppressed violations appear in the findings table with an orange "Suppressed" pill badge. No config files are touched.--include-suppressed active and there are suppressed violations. Groups findings by suppression kind (ignore, ignore_for_file, baseline) with a mini table and per-row "Unsuppress" button (currently shows a hint — full comment-removal is planned).// ignore: <rule> above every finding currently shown in the table, in one confirmed, all-or-nothing edit across every affected file. Disabled when there are no findings to suppress.bugs/*.md report marked Fixed, Closed, or Declined that is still sitting in bugs/ instead of being archived to plans/history/, as a Problems-panel hint on the report's Status: line. No action required — this only surfaces reports that were left un-archived.plans/history/) that still reads as open work — an open Status:/Severity: field or an unaddressed action-items heading — as a Problems-panel hint suggesting it be moved to the open-issues directory instead. Archive glob, open-issues directory, and the open/closed signal patterns are all configurable via saropaLints.docPlacement.* settings; disable with saropaLints.docPlacement.enabled.Drift Advisor integration now supports authenticated servers via a new saropaLints.driftAdvisor.authToken setting, with matching guidance states in both the tree view (linking to Settings) and the Findings Dashboard status pill when a token is missing or was rejected by the server.
Fixed Code Health dashboard KPI tiles silently losing their semantic color coding (red/amber/info) after the pill-unification refactor — kpiCard() was missing the .pill class that the updated CSS selectors require.
Fixed silent status bar disappearance when updateAllStatusBars throws (e.g. corrupted workspaceState after a VS Code hard crash). The bar now catches errors, shows a visible $(error) Saropa Lints: Error state with an error-themed background, and logs to the output channel so the failure is discoverable.
fs.readdirSync/fs.statSync to async fs.promises.readdir/fs.promises.stat, preventing the extension host thread from blocking on large workspaces. Subdirectory walks and file stat calls now fan out concurrently via Promise.all.workspaceHazardScan.ts and watcherExcludeAudit.ts into a shared mergeWatcherExcludes helper in watcherExcludeHelpers.ts.workspaceState.get call so a corrupted workspace state after a VS Code crash does not prevent the audit from running.16.2.x > 16.1.x).lib/src/rules/, scoped to resolved .dart files) and ad hoc extension-native checks (extension/src/, full workspace file access, own DiagnosticCollection each, not yet surfaced in the web report) in bugs/ISSUE_REPORT_GUIDE.md, so non-Dart-file issues are routed correctly instead of being misfiled as out of scope. No action required.…v7→v8), resolving the Node.js 20 deprecation warning on GitHub-hosted runners.
Adds a "What's New" panel that surfaces on activation, flagging the v16 diagnostic engine change (LSP server replacing the Analyzer Plugin), the new machine health monitoring, and the sidebar redesign — with a one-click revert to the previous engine. log
always_specify_parameter_names rule (Professional tier) flags call sites passing 2+ consecutive positional arguments of the same or confusable type (e.g. two Strings, int+double), where named arguments could prevent silent swap bugs. Allowlists idiomatic Dart/Flutter constructors like Offset(dx, dy) (matched by declaring library, so a project's own same-named class is never silently exempted); add project-specific allowlist entries under always_specify_parameter_names: allowlist: in analysis_options_custom.yaml.avoid_unbounded_dependency false positive in Melos/pub-workspace monorepos where a dependency with any constraint is paired with a path: entry in dependency_overrides:. The any is inert in that case because pub resolves via the local path, not the loose constraint. Only path: overrides suppress the lint; git: and hosted: overrides do not.dart analyze subprocess, completing in milliseconds instead of tens of seconds on large projects. The three analysis paths (full workspace, per-file, and post-config-change) all use the live diagnostic stream. The data written to violations.json is structurally identical to what the Problems panel shows, eliminating stale-data divergence between runs.setup-dart mints the credential once, right after SDK install; Analyze plus the full test suite then run for 9-10 minutes before the publish step, long enough for the short-lived token to expire and be rejected as Invalid JWT token: invalid timestamps. This had been misdiagnosed twice (beta.6, beta.9) as a transient pub.dev outage. setup-dart now re-runs immediately before dart pub publish so the token is minted at the point of use.actions/checkout v4→v5, actions/setup-python v5→v6, actions/setup-node v4→v5 with runtime bumped to Node 22, actions/upload-artifact v4→v5, actions/github-script v7→v8), resolving the Node.js 20 deprecation warning on GitHub-hosted runners.Activation is now resilient — commands register and the sidebar warns on failure instead of going blank. The Findings Dashboard absorbs the full-proje
Activation is now resilient — commands register and the sidebar warns on failure instead of going blank. The Findings Dashboard absorbs the full-project audit as a scope selector and gains severity coloring, clickable file paths and rule names, a filter-aware page limit, and JSON export. Sidebar rows show live counts, and per-file analysis no longer blocks the extension host. Publish-pipeline fixes stop the i18n audit from launching Ollama and the local pub.dev fallback from flooding the terminal.
dart directly instead of via a cmd.exe wrapper, eliminating process-tree complexity that competed for SDK resources. All dart CLI invocations now use direct spawn; flutter (a .bat wrapper) retains the shell path, and an ENOENT fallback retries with a shell for legacy SDK installs. No action required.runAnalysisForFiles) blocking the extension host with a synchronous spawnSync call for the entire dart analyze duration. Converted to the async runInWorkspaceAsync variant that the full-workspace analysis already uses, keeping the event loop responsive and adding a Cancel button to the progress notification. No action required.saropa_lints version's audit output normalizes to the current severity vocabulary the same way the batch report already did.prepareRefreshCycle) instead of being cleared and rebuilt by each provider independently. Eliminates redundant readVisibleLiveViolations + computeLiveHealthScore calls when multiple sidebar sections refresh together.--mode audit) probing Ollama engine availability via low_quality_entries(), which self-provisioned the daemon and pulled the model — an expensive, risky side effect during a read-only coverage check. Audit now uses audit_only=True to scan cache provenance tags without any subprocess calls. No action required.dart pub publish --force (local fallback) printing its full file-tree listing to stdout, flooding the terminal and pushing prior publish-step output out of the scrollback buffer. Output is now captured; only the pub.dev confirmation line is surfaced. No action required.dictionaries.py for 5 gaps across 4 locales (ar, de, fil, pt) that MT engines did not translate: RSS warning description, "Dev Tool Budget", "Set Cap", "Translation Engine (Ollama)". No action required.COGNATES approval list to dictionaries.py for words that are spelled identically in specific target languages (e.g. "Source" in French). Previously each cognate needed a per-locale "X": "X" passthrough scattered across the file; the centralized list merges them at import time, with locale-code validation (typos raise ValueError), empty/duplicate-locale guards, drift detection (--fail-on-drift), and a --check-cognates flag (now in the publish pipeline) that catches conflicts and DO_NOT_TRANSLATE redundancies. No action required.Activation is now resilient — commands register and the sidebar warns on failure instead of going blank. The Findings Dashboard absorbs the full-project audit as a scope selector and gains severity coloring, clickable file paths and rule names, a filter-aware page limit, and JSON export. Sidebar rows show live counts, and per-file analysis no longer blocks the extension host. Publish-pipeline fixes stop the i18n audit from launching Ollama and the local pub.dev fallback from flooding the terminal. log
dart directly instead of via a cmd.exe wrapper, eliminating process-tree complexity that competed for SDK resources. All dart CLI invocations now use direct spawn; flutter (a .bat wrapper) retains the shell path, and an ENOENT fallback retries with a shell for legacy SDK installs. No action required.runAnalysisForFiles) blocking the extension host with a synchronous spawnSync call for the entire dart analyze duration. Converted to the async runInWorkspaceAsync variant that the full-workspace analysis already uses, keeping the event loop responsive and adding a Cancel button to the progress notification. No action required.dart analyze subprocess. Completes in milliseconds instead of tens of seconds. The zero-violations case now shows a confirmation message instead of silent completion. Config-change rescans use an event-driven freshness gate instead of a fixed delay. No action required..dart file diagnostics, not unrelated file types, and the row's icon switches from a warning triangle to a clock once the timestamp is over an hour old, so an aging count no longer reads as an up-to-date warning. No action required.saropa_lints version's audit output normalizes to the current severity vocabulary the same way the batch report already did.prepareRefreshCycle) instead of being cleared and rebuilt by each provider independently. Eliminates redundant readVisibleLiveViolations + computeLiveHealthScore calls when multiple sidebar sections refresh together.--mode audit) probing Ollama engine availability via low_quality_entries(), which self-provisioned the daemon and pulled the model — an expensive, risky side effect during a read-only coverage check. Audit now uses audit_only=True to scan cache provenance tags without any subprocess calls. No action required.dart pub publish --force (local fallback) printing its full file-tree listing to stdout, flooding the terminal and pushing prior publish-step output out of the scrollback buffer. Output is now captured; only the pub.dev confirmation line is surfaced. No action required.dictionaries.py for 5 gaps across 4 locales (ar, de, fil, pt) that MT engines did not translate: RSS warning description, "Dev Tool Budget", "Set Cap", "Translation Engine (Ollama)". No action required.COGNATES approval list to dictionaries.py for words that are spelled identically in specific target languages (e.g. "Source" in French). Previously each cognate needed a per-locale "X": "X" passthrough scattered across the file; the centralized list merges them at import time, with locale-code validation (typos raise ValueError), empty/duplicate-locale guards, drift detection (--fail-on-drift), and a --check-cognates flag (now in the publish pipeline) that catches conflicts and DO_NOT_TRANSLATE redundancies. No action required.Fixed a sidebar action that could crash on a project's first scan or run twice on rapid clicks, and shortened several sidebar labels. System Health no
Fixed a sidebar action that could crash on a project's first scan or run twice on rapid clicks, and shortened several sidebar labels. System Health now monitors the whole machine — not just saropa_lints' own processes — with proactive warnings and one-click fixes. log
saropaLints.systemHealth.devToolBudgetPercent, default 60%). Warns when dev tools exceed the budget. No action required.saropaLints.systemHealth.systemMemoryWarningPercent, default 15%) or any single analysis server exceeds a configurable size (saropaLints.systemHealth.analysisServerWarningGB, default 4 GB), plus a one-time session-start check. System-wide free RAM now appears in the status bar tooltip. No action required.reports/.saropa_lints/ directory does not yet exist (e.g. first run on a project). The directory is now created before the scan CLI writes its JSON output. No action required.createBusyGuard to commandGuards.ts as a reusable concurrency guard with visible status-bar feedback, replacing four identical inline busy-flag patterns in the stale-ignore commands. No action required.l10n() with new sidebar.actions.* keys in en.json, closing an i18n gap where two of the three labels were hardcoded English. No action required.Process Health status bar and tooltip now isolate saropa-owned memory from system-wide Dart processes, preventing false alerts when analysis servers f
Process Health status bar and tooltip now isolate saropa-owned memory from system-wide Dart processes, preventing false alerts when analysis servers from other VS Code windows consume significant memory. The tooltip adds a trend indicator, Unicode sparkline chart, and automatic leak detection to catch memory issues early, plus a per-process breakdown for diagnostic detail. Fixed the dashboard's "Enable all recommended packs" button using stale pack detection while the table showed the current state. log
add_resolution_workspace (recommended tier, WARNING): flags a package listed in a Dart pub workspace that is missing resolution: workspace in its pubspec.yaml, catching version drift before pub get fails. Quick fix inserts the key after the environment: block. No action required.flag_missing_workspace_member (recommended tier, INFO): flags a workspace root whose subdirectories contain pubspec.yaml files not listed in the workspace: list, catching packages that silently resolve independently instead of joining the shared lockfile. Scans up to 3 levels deep, skips listed members' children (no example/ false positives). No action required.workspace_dependency_version_sync (recommended tier, INFO): flags a workspace whose member packages declare different version constraints for the same dependency, catching version drift that produces misleading pubspecs since all members share one lockfile. No action required.workspace_member_order (recommended tier, INFO): flags a workspace root whose workspace: list entries are not in alphabetical order, making large workspaces easier to scan and reducing merge conflicts from unordered insertions. No action required.prefer_publish_to_none (recommended tier, INFO): flags a pubspec.yaml that has no publish_to field and appears to be an application (missing homepage/repository metadata), guarding against accidental dart pub publish to pub.dev. Quick fix inserts publish_to: none after description: (or after name: if there is no description). No action required.avoid_dependency_overrides (recommended tier, WARNING): flags any non-empty dependency_overrides: section in pubspec.yaml, which silently diverges the resolved dependency graph from declared constraints and masks real conflicts. No action required.prefer_pinned_version_syntax (stylistic tier, INFO): flags caret-range version constraints (^X.Y.Z) in app pubspecs (publish_to: none), suggesting exact pins for reproducible builds. Conflicting pair with prefer_caret_constraint_in_app — opt-in only. No action required.getDetectedPackIds, a shared helper that both the table and the button call, so the two surfaces can never diverge. No action required.add_resolution_workspace quick fix: re-verifies resolution: workspace absence before inserting (guards against stale diagnostics and batch "fix all" duplicates), added trailing-newline guard when inserting after the environment block, and tolerates blank lines inside the environment block. The detection regex now also accepts quoted forms ("workspace" / 'workspace').flag_missing_workspace_member scan: deduplicated path-normalization logic into a shared public helper, extended case-insensitive path comparison to macOS (default APFS), and made the build/ directory skip case-insensitive.findDivergentDependencyConstraints from workspace_dependency_version_sync into the pubspec constraint parser, creating a pure-function seam for unit testing and removing an unreachable block-dep guard that the parser already handles.--protocol=lsp for future binary rename resilience, ✓/↑ conflict resolved (rising trend suppresses the healthy checkmark to avoid mixed signals), and partition assertion added to catch filter coupling drift.classifyProcess() as a discriminated union (ProcessCategory + label) replacing the duplicated predicate chains across tooltip, snapshot, and label functions. Boolean predicates (isSaropaProcess, isDaemonProcess, isAnalysisServerProcess) now delegate to it, and the tooltip builder uses a single-pass partition instead of three independent filter passes with a runtime assertion.showHealthPanel instead of the registered showProcessHealth.classifyProcess discriminated union (8), process partition mutual exclusivity (3), marker substring containment invariant (1), plus the existing processLabel (9), isAnalysisServerProcess (4), truncateLabel (5), RSS trend boundary (2), renderSparkline (6), detectMonotonicGrowth (7).getDetectedPackIds in rulePackDefinitions.ts as the single source of truth for pack applicability. The dashboard table and "Enable all" button both call it instead of inlining isPackDetected filters independently.### Internal heading, enforced by a pre-commit hook and a publish-time gate. No action required.Fixes a false positive in the l10n diagnostic provider and catches more CI-only test failures locally before publishing. log
Fixes a false positive in the l10n diagnostic provider and catches more CI-only test failures locally before publishing. log
saropa-l10n) reporting false-positive "expects params but none passed" warnings when l10n() receives its params via a variable or expression instead of an inline object literal. The parser now recognizes non-literal second arguments and skips static key extraction for them. No action required.l10n('key', undefined) and l10n('key', null) without warning when the template expects params. These keyword arguments are now correctly treated as "no params passed." No action required.OpaqueParams type for the l10n parser sentinel, preventing accidental use of the sentinel string in key-extraction functions at compile time.extractBalancedBrace as a shared export from l10nParsers.ts, deduplicating the brace-matching loop previously inlined in extractParamsBlock.usesTypeResolution override to AvoidCaseSensitivePathComparisonRule (uses staticType).flutter_skill_lints migration pack that moved from TODO/PARTIAL to HAVE.create_git_tag now prompts before moving a stale remote tag to HEAD on retry instead of hard-failing._find_workflow_run skips already-failed runs so retry doesn't re-attach to old workflows.Hardens the LSP server against normal editor traffic and adds a doctor command to catch misconfigured project settings before they cause confusing war
Hardens the LSP server against normal editor traffic and adds a doctor command to catch misconfigured project settings before they cause confusing warnings. log
textDocument/didChange, $/cancelRequest, $/setTrace, and workspace/didChangeConfiguration so the inert server stays alive under normal VS Code traffic. Two-level logging surfaces server activity in the Output channel: lifecycle events always log, high-frequency messages (didChange, codeAction) are suppressed unless $/setTrace is set to verbose. No action required.doctor command scans consumer project configuration for misplaced keys, missing custom file, and other issues that produce SDK warnings. Run dart run saropa_lints doctor [directory].--trace flag for LSP server enables verbose logging from startup without waiting for the editor to send $/setTrace. Useful for standalone debugging: dart run saropa_lints:lsp_server --trace.unsupported_option bug for rule_packs and log_level — investigation confirmed the fix was already implemented; consumer projects just need to run dart run saropa_lints migrate-config.migrate-config now removes orphan rule_packs: keys that have no enabled: child, and handles trailing comments on the key line._leadingSpaces) now counts tabs as indentation, matching the scalar parser — fixes silent parse failures on tab-indented YAML.doctor command now scopes key detection to the saropa_lints: plugin block — no longer false-positives on identically named top-level keys.--log-file, --log-append, --mode, --auto-retry, and --output-level flags for non-interactive/CI execution. Auto-detects non-TTY stdin. Mode definitions are unified in a single table driving both CLI and interactive menu.Cross-platform SARIF output fix so CI-generated code-scanning annotations resolve file paths correctly on Linux runners. The publish script gains a de
Cross-platform SARIF output fix so CI-generated code-scanning annotations resolve file paths correctly on Linux runners. The publish script gains a dedicated pub.dev-only mode for releasing the Dart package without touching the VS Code extension. log
../C:/project/... URIs on Linux CI — switched from p.relative() to prefix stripping after forward-slash normalization so Windows-style paths resolve correctly cross-platform.--fail-on error scan test failing when error-level diagnostics exist in the fixture — test now uses --fail-on-count 9999 to decouple exit-code assertion from project error count.Rule shedding under memory pressure is now cost-aware — expensive rules that drive the most memory consumption are shed first, keeping cheap syntactic
Rule shedding under memory pressure is now cost-aware — expensive rules that drive the most memory consumption are shed first, keeping cheap syntactic rules running longer. The Config Dashboard surfaces which rules are currently shed and why, and the status bar tooltip shows shed category breakdowns. log
Nothing published for this version
Nothing published for this version
Projects using the old location get a deprecation warning and automatic fallback — the keys still work from the plugin block, but moving them to the c…
This patch moves log_level, lane, and memory_mode configuration from the plugins > saropa_lints: block in analysis_options.yaml to top-level keys in analysis_options_custom.yaml, eliminating false unsupported_option warnings from the Dart SDK's plugin-block validator. Projects using the old location get a deprecation warning and automatic fallback — the keys still work from the plugin block, but moving them to the custom file silences the warnings.
dart run saropa_lints audit <dir> runs every rule (pedantic + stylistic) against a codebase regardless of the project's configured tier. Produces enriched JSON with per-diagnostic tier and category fields. Supports --since <ref> to audit only changed files, --min-severity/--min-impact post-filters, --profile timing, and --exclude-globs/--include-globs.--save-baseline saves the current audit as a project baseline at .saropa/audit_baseline.json; --baseline compares against the saved baseline and tags each diagnostic as new or unchanged. The sidebar quick-pick shows a "Compare to baseline" option when a baseline exists, and the report webview has a "Save as baseline" button and new/unchanged filter chips.dart run saropa_lints migrate-config and a sidebar button ("Migrate config keys") automatically move log_level, lane, and memory_mode from the old plugin block to analysis_options_custom.yaml. Safe to run multiple times; already-migrated keys are skipped.max_declarations_per_file — set max_declarations_per_file: N in analysis_options_custom.yaml to allow up to N top-level declarations before prefer_single_declaration_per_file fires (default 1). No action required — existing behavior is unchanged.max_sealed_hierarchy_lines: N in analysis_options_custom.yaml to get a lint when a sealed class file exceeds N lines, suggesting part/part of to split subtypes while keeping them in the same library (default 0 = disabled).log_level, lane, and memory_mode plugin configuration keys no longer trigger unsupported_option warnings from the Dart SDK analyzer. These keys now live as top-level entries in analysis_options_custom.yaml instead of under plugins > saropa_lints:. Projects still using the old location get a deprecation warning with the key's value honored as a fallback; dart run saropa_lints init generates the updated layout automatically.prefer_sorted_parameters no longer conflicts with dart format. The rule now respects always_put_required_named_parameters_first: required named parameters come first, then optional named parameters, each group sorted alphabetically. Includes a quick fix that reorders parameters automatically. (#321)require_text_overflow_handling and require_text_overflow_in_row correction messages no longer default to TextOverflow.ellipsis. The guidance now recommends wrapping in Expanded/Flexible first — ellipsis is a last resort when truncation is intentional. Both rules offer context-aware quick fixes: "Wrap in Expanded" inside Row/Column/Flex, or "Add maxLines" elsewhere. (#320)prefer_single_declaration_per_file no longer fires on sealed class hierarchies. Dart requires sealed subtypes in the same library, so co-locating them is mandatory, not a style violation. (#322)avoid_unused_parameters no longer fires on abstract, external, or native method declarations. These methods have no implementation body, so their parameters define the interface contract and cannot be "used." (#319)lane: from analysis_options_custom.yaml instead of analysis_options.yaml. No action required — the extension handles the new location transparently.avoid_unguarded_debug no longer false-positives when debugPrint() is dominated by an early-return guard ( if (!kDebugMode) return; ) at the top of the
avoid_unguarded_debug no longer false-positives when debugPrint() is dominated by an early-return guard (if (!kDebugMode) return;) at the top of the enclosing block. Also recognizes kDebugMode == false, kDebugMode != true, reversed operand order (false == kDebugMode), and multi-statement then-blocks ending in return. No action required.avoid_unguarded_debug now recognizes variable-indirection guards: final isDebug = kDebugMode; if (!isDebug) return; is accepted, including chained assignments up to 3 levels deep and top-level/static const fields in the same file. Only final and const are trusted — mutable assignments are correctly rejected. No action required.early_exit_guard_utils.dart — containsEarlyExit, endsWithEarlyExit, findPrecedingGuardInBlock, and hasDominatingEarlyExitGuard replace five independent reimplementations across debug_rules.dart, collection_rules.dart, async_rules.dart, type_rules.dart, and code_quality_avoid_rules.dart.hasDominatingEarlyExitGuard now supports a stopAtClosureBoundary parameter — runtime-mutable guards (collection emptiness) stop at closure/function boundaries; compile-time constants (kDebugMode) opt out since closures in the guarded zone are safe.endsWithEarlyExit now recognizes break and continue statements, matching the coverage of containsEarlyExit.final/const assignments up to 3 levels with cycle detection, and resolves top-level/static class fields via pure AST walk (no type resolution — rule stays in the light analysis lane)._findLocalInitializer now only considers declarations preceding the usage site (offset-based guard prevents forward-reference resolution).Major scan CLI expansion: lane control ( --lane full|light , --lane-stats ), CI gating by rule impact or tier ( --fail-on-impact , --fail-on-tier ), s
Major scan CLI expansion: lane control (--lane full|light, --lane-stats), CI gating by rule impact or tier (--fail-on-impact, --fail-on-tier), stale-ignore detection (--find-stale-ignores), SDK compatibility audit (--check-sdk-compat), and include/exclude glob filters for fine-grained file targeting. Eight false-positive fixes across core rules including avoid_context_in_async_static, avoid_large_list_copy, avoid_datetime_constructor, no_equal_nested_conditions, and the context-across-async family. An OOM crash fix for projects over 4 000 files adds per-file memory budgeting and adaptive RSS caps. Two new rules: prefer_primary_constructor (Dart 3.13+ syntax) and require_sdk_syntax_match (catches AI-generated code using syntax the project's SDK constraint doesn't support).
The
analyzer ^13.1.0migration (Dart 3.13+ / Flutter 3.47.1+, released 2026-08-19) is complete and tested but held offmain— adoption of 3.47.1 is near zero. It is parked on theanalyzer-13-migrationbranch and will ship as a<n+1>.0.0 major bump once adoption is widespread.
avoid_context_in_async_static no longer false-positives when BuildContext is passed solely as an argument to the awaited call and never read after the await resumes (e.g. await showDialog(context: context)). The rule now walks all context usages in the method body and suppresses the diagnostic when every usage is consumed synchronously inside the awaited expression. No action required.avoid_large_list_copy no longer false-positives when .toList() feeds a ?? expression, a List<T>-typed argument, an explicit List<T> variable, a List<T> return type, a cascade, a property access, or a collection literal — all cases where removing .toList() would cause a compile error. No action required.avoid_datetime_constructor and avoid_datetime_constructor_unvalidated no longer flag DateTime() / DateTime.utc() calls when all three date components (year, month, day) are property accesses on a DateTime-typed expression, since the source object already guarantees valid components. Day arithmetic (dt.day ± N) is also suppressed because Dart documents rollover behavior. No action required.no_equal_nested_conditions no longer false-positives when the condition variable is reassigned between the outer and inner checks (e.g. if (x == null) { x = compute(); if (x == null) ... }). Simple, null-aware (??=), and compound (+=) assignments are all recognized. No action required.avoid_future_in_build (v3) removed name-prefix heuristic that only caught methods starting with fetch/load/get/etc. Now flags ANY method invocation in FutureBuilder(future:) inside build(). Also detects non-deterministic Future constructors while exempting Future.value() and Future.error(). Scoped to FutureBuilder only (no longer flags custom widgets with a future: parameter). Widget class detection now covers third-party bases (HookWidget, ConsumerWidget, etc.). No action required.pass_existing_future_to_future_builder (v9) no longer flags Future.value() and Future.error() constructors. Cache-method exemption now also recognizes @cachedFuture annotation from package:saropa_lints/annotations.dart. No action required.require_error_widget no longer false-positives when error handling is delegated to an extension method on the snapshot parameter (e.g. snapshot.snapLoadingProgress()). Any method invocation on the snapshot is now recognized as delegated error handling. No action required.SAROPA_LINTS_MAX_RSS_MB to override the adaptive cap.usesTypeResolution, INFO severity, or cost above low were silently blocked by the analysis-server lane gate, which defaulted to light in the CLI path. The scanner now runs at full lane coverage so all enabled rules fire correctly. No action required.avoid_context_across_async and avoid_retaining_disposed_widgets now check the resolved type (when type information is available, e.g. in-editor or --resolve scans) instead of matching on the bare identifier/type name alone. Fixes false positives on non-Flutter classes that happen to be named context or Element (an analyzer Element, a custom Context type, etc.). No action required.ephemeral/, .plugin_symlinks/) by default. Previously these symlinked plugin sources appeared in scan results even though the user doesn't control them. No action required — the exclusion is automatic. (#313)DateUtils.dateOnly() quick fix for avoid_datetime_constructor and avoid_datetime_constructor_unvalidated — recognizes the strip-time idiom DateTime(x.year, x.month, x.day) and the explicit-midnight-zeros variant DateTime(x.year, x.month, x.day, 0, 0, 0), replacing both with DateUtils.dateOnly(x). Appears above the existing DateTime.tryParse() fix when both apply. Not offered for .utc() constructors, nullable receivers, non-DateTime types, or pure Dart projects without Flutter. No action required.@cachedFuture annotation (package:saropa_lints/annotations.dart) — marks a method as returning a cached Future, suppressing pass_existing_future_to_future_builder without needing the heuristic (private method + Future? field). Use when your naming convention doesn't match the heuristic.prefer_primary_constructor (Professional, INFO) — flags classes eligible for Dart 3.13+ primary constructor syntax when the project's SDK lower bound is >=3.13.0. Reduces boilerplate for simple data classes that AI generators consistently produce in the verbose pre-3.13 form. Detection only for now — the quick fix ships with the analyzer 13 migration on the analyzer-13-migration branch. No action required.require_sdk_syntax_match (Comprehensive, WARNING) — flags Dart syntax features that require a newer SDK than the lower bound declared in pubspec.yaml, with a quick fix to raise the SDK lower bound. Catches AI-generated code that uses records, switch expressions, extension types, or digit separators when the project's SDK constraint doesn't support them. No action required.--lane full|light flag controls which rule lane the scanner uses. Defaults to full (every enabled rule); light restricts to the same cheap, resolution-free subset the analysis server runs in its default lane. No action required — existing scans are unaffected.--lane-stats prints how many of the loaded rules are light-lane vs full-only; when in light lane, lists every blocked rule name so the gate's effect is fully observable.--check-sdk-compat standalone audit cross-references the pubspec SDK lower bound against Dart syntax features in lib/. Prints a grouped summary showing which files force each version bump. Exits 1 on mismatch, 0 when compatible — suitable for CI gating.--exclude-globs <pattern>... flag excludes files matching glob patterns from the scan. Supports ** (any path segments), * (any non-separator chars), and ? (single char). Use it to skip vendored code, generated directories, or any paths the hardcoded exclusions don't cover. (#313)--include-globs <pattern>... flag overrides the hardcoded exclusions for matching paths — when a path matches both a default exclusion and an include-glob, the include wins. Use it to force-scan third-party plugin code in ephemeral or generated directories. (#313)--fail-on-impact <level> flag exits 1 when any saropa rule's declared impact meets the threshold (info/warning/error). Unlike --fail-on (which uses analyzer severity), this checks the rule author's business-consequence rating — use it to gate CI on high-impact rules regardless of their configurable severity. Pair with --fail-on-impact-count <n> to tolerate a known baseline during migration. (#312)--fail-on-tier <name> flag exits 1 only when a diagnostic comes from a rule in the specified tier or below. Scan at a high tier for visibility but only fail on essential-tier findings during incremental adoption — e.g. --tier comprehensive --fail-on-tier essential. (#312)--find-stale-ignores flag detects // ignore: comments whose suppressed saropa_lints rule no longer fires on the target line — the code was fixed but the ignore was left behind. Reports each stale ignore with file path, line number, and rule name. Supports --format json for CI integration. Exits 1 if any stale ignores found, 0 if clean. No action required.--fix-stale-ignores flag detects AND automatically removes stale // ignore: directives from source files. Standalone comments are deleted entirely; inline comments are stripped preserving the code; multi-rule comments have only the stale rules pruned. Prints a summary of files modified. No action required.// ignore: comments from source files. A lightbulb quick fix on each stale-ignore diagnostic offers a file-scoped "Fix stale ignores in this file" action with no confirmation prompt, for cleaning up one file at a time without leaving the editor. No action required.avoid_wildcard_cases_with_enums (v6) now suppresses the diagnostic when the switched enum has more than 20 members, where exhaustive case listing is impractical and a default: catch-all is the correct design choice. Also upgraded from string heuristic to proper EnumElement resolution when type information is available. No action required.avoid_stream_in_build (v3) now also detects StreamBuilder(stream: method()) where a method invocation creates a new subscription on every rebuild. Previously only caught StreamController() instantiation inside build(). Excludes safe constructors (Stream.value(), Stream.empty()) and the ??= caching idiom. A new quick fix converts a simple StatelessWidget flagged this way into a StatefulWidget with the stream cached in initState(). No action required.isWidgetOrStateClass() and isInsideBuildMethod() utilities into target_matcher_utils.dart — used by avoid_stream_in_build and avoid_future_in_build; replaces per-rule private duplicates.dart test -j <all-cores> (24 on a 24-core machine) caused native access violations (STATUS_ACCESS_VIOLATION) and front_end compiler exceptions during test compilation. The test step now auto-tunes concurrency by probing a single test at increasing -j levels (4, 6, 8, 10, 12), caching the result in build/.dart_test_max_j; crash retries halve concurrency automatically, the failure prompt offers [F]ewer workers to halve manually, and set SAROPA_TEST_MAX_J=N to skip the probe entirely..dill files were written inside the project tree (build/test_tmp/), causing uri_does_not_exist scan errors and filling the C: drive. Temp dir now defaults to <system_temp>/saropa_dart_test outside the project tree; set SAROPA_TEST_TMP to override (validated: falls back if inside project tree).plans/known_issues_review.md from git tracking (generated file, regenerated each publish run).dart run saropa_lints:memory_report command — summarizes the analysis server's RSS trend from plugin.log for post-crash diagnosis. The in-process plugin now writes a memory sample line roughly every 30 seconds; the command reports min/max/latest RSS, percent of the configured cap, and a CAVEAT when plugin.log was rotated mid-session (so the summary is known to be incomplete rather than silently wrong). A one-time log line now also flags when RSS sampling itself is unavailable on the current platform, so an empty trend log is diagnosable instead of looking identical to "plugin never ran".plans/PLAN_analyzer_memory_monitor.md (phased checklist: soft RSS threshold, selective rule shedding, VS Code status-bar integration), matching the repo's PLAN_* convention. Added scripts/check_plan_naming.py — an informational, non-blocking report of plans/*.md files that don't follow the PLAN_<name>.md naming convention.--dry-run CLI flag — runs dependency resolution, audit, format, analysis, tests, and dart pub publish --dry-run with no commit, tag, version bump, or publish. Needs no pub.dev credentials; intended for CI pre-merge validation.appliesToMaxVersion or replacementObsoleteFromVersion to avoid false-positive flagging of version-scoped entries that are correct by design.require_sdk_syntax_match quick fix: removed dead Map<Type, String> lookup (analyzer concrete types are private *Impl classes that never matched abstract keys); hardened regex with triple-quoted raw string to handle embedded quotes.bugs/BUG_REPORT_GUIDE.md renamed to bugs/ISSUE_REPORT_GUIDE.md and extended with a feature request template, proposal naming patterns, and lifecycle, alongside the existing bug report process._rule_metrics.py's bug counter now reports open feature proposals separately from unsolved bugs in the publish "WORK REPORT" banner, instead of lumping both into one count.lane: RuleLane.full explicitly instead of relying on the constructor default.--lane light is combined with --exclude-light-lane (degenerate: zero rules to scan).double.parse(x.toStringAsFixed(n)) round-trip patterns in the project-health/vibrancy models now round arithmetically via a shared roundToDecimalPlaces helper instead of parsing a self-produced string; a regex-guaranteed-digits int.parse triple in the pubspec constraint parser is annotated as a verified false positive.scripts/hooks/changelog_guard.py (dual-mode: Claude PostToolUse + git pre-commit) blocks commits that introduce multiple unreleased sections in CHANGELOG.md or bump version numbers in pubspec.yaml / package.json ahead of the publish script. Publish script also gained assert_single_unreleased_section() as a belt-and-suspenders gate.dart test output now streams to the terminal with a real-time progress bar showing elapsed time, pass/skip/fail counts, and the current test name. Failure details print immediately instead of silently accumulating in a log file. Full output is still written to reports/ for post-mortem analysis.git diff, maps them to corresponding test files, and runs only those first (seconds instead of minutes). If the delta pass fails, it stops immediately without compiling the full 340+ file suite. Infrastructure changes (tiers, registration, pubspec) bypass delta and run the full suite.dart run saropa_lints audit), visible sidebar button + Explorer context menu, filterable webview report with tier/severity/impact/category facets, diff mode (--since <ref>) as a UI quick-pick, and baseline diffing (--save-baseline / --baseline) with datetime-stamped outputs. Plan at plans/PLAN_full_audit.md.extension/scripts/i18n/generate_locales.py and mt_fallback.py previously resolved the primary MT engine (self-provisioning Ollama: starting the daemon, pulling a multi-GB model on first use) unconditionally before checking whether any locale actually had untranslated strings. A fully-cached run now never touches Qwen/Ollama at all — engine resolution is deferred until a string is confirmed missing from every cached engine's keyspace.scan_cli_args_test.dart: five untagged process groups now marked tags: ['slow'] — the (process) groups shell out to real dart run saropa_lints:scan subprocesses (including full essential-tier scans of the project itself) and were timing out at 2 minutes each under full-suite -j contention, failing the fast publish pass. Only the process-spawning groups are tagged; the ~250 in-memory parseScanArgs unit tests in the same file remain in the fast pass.dart fix audit crash on Windows — the two dart fix subprocess calls in the pre-publish audit were the only dart invocations in the publish modules missing shell mode, so they crashed with WinError 2 on Windows where dart resolves to a .bat wrapper that CreateProcess cannot launch directly. Both calls now pass shell mode like every other subprocess in the pipeline.scan_cli_args_test.dart: slow process groups given an explicit 5-minute timeout — the publish delta pass runs changed test files with tag filters deliberately ignored, so the slow tag alone could not protect these tests there; each cold-starts an uncompiled scan CLI that can exceed the 2-minute default. Known limitation: the --fail-on group can still exceed even 5 minutes under contention — the durable fix (a precompiled scan snapshot) is tracked separately.Dart 3.13 parses implicit constructor calls (e.g. File('x')) as InstanceCreationExpression even in syntactic mode, so the syntactic and resolved scans
Dart 3.13 parses implicit constructor calls (e.g. File('x')) as
InstanceCreationExpression even in syntactic mode, so the syntactic
and resolved scans now fire the same rules. Changed the test from
requiring a strict superset to requiring a non-strict superset,
which passes on both Dart 3.12 and 3.13.
…sites), the package's vibrancy score/license/vulnerabilities/known-issue status, and any GitHub issues flagged as breaking or deprecation-related — pr…
The Upgrade Opportunities panel's AI prompt is more accurate and less noisy: it now surfaces the deprecated APIs a project actually calls, dual-dependency version risk, and possible local reimplementations of library code, while dropping dev-only and transitive dependencies that aren't actionable. The old per-card clipboard copy is replaced by "Write Report" buttons — global (all packages in one file) and per-card (single package) — that save dated files and copy the path. Seven actively-maintained packages were removed from the known-issues database after being incorrectly flagged as end-of-life. log
build_runner), since new features in a package the project never calls are not actionable. No action required.reports/ and copy the absolute path to the clipboard. No action required.timezone, retrofit, sqflite_sqlcipher, intl_translation, window_size, routemaster, flutter_keychain) that flagged actively-maintained packages as end-of-life based on outdated data. No action required.known_issues.json lifecycle claims (end-of-life/caution/maintenance-mode) against live pub.dev data and warns when a package has since shipped a non-discontinued release contradicting the recorded reason. Non-blocking (network-dependent, 5s per-request timeout); run standalone with python scripts/check_known_issues_freshness.py.plans/known_issues_review.md on every publish run (previously only via manually running scripts/generate_known_issues_review.py), sharing one pub.dev fetch pass with the freshness check above instead of double-fetching the overlapping entries.Five new quick fixes for stylistic rules: convert regular comments to doc comments, remove redundant type annotations, replace string + concatenation
Five new quick fixes for stylistic rules: convert regular comments to doc comments, remove redundant type annotations, replace string + concatenation with adjacent literals, simplify BorderRadius.all(Radius.circular(r)) to BorderRadius.circular(r), and replace sizing-only Container with SizedBox. log
prefer_doc_comments_over_regular: converts // comments to /// doc comments with one click. No action required.avoid_explicit_type_declaration: removes the redundant type annotation, letting the compiler infer the type. No action required.prefer_adjacent_strings: strips + operators between string literals, producing idiomatic adjacent-string syntax. No action required.prefer_borderradius_circular: rewrites BorderRadius.all(Radius.circular(r)) to the shorter BorderRadius.circular(r). No action required.prefer_sizedbox_over_container: replaces sizing-only Container with SizedBox. No action required.- Unreleased suffix (and typo variants) from versioned CHANGELOG headings at publish time, so ## [X.Y.Z] - Unreleased is cleaned to ## [X.Y.Z] before version sync.prefer_single_quotes → prefer_single_quotes_strict ); 3 duplicates with no behavioral difference are removed. Old names are deprecated aliases for one…
Breaking: 35 rule names that collided with core Dart/Flutter lint names are renamed with semantic suffixes (e.g. prefer_single_quotes → prefer_single_quotes_strict); 3 duplicates with no behavioral difference are removed. Old names are deprecated aliases for one release cycle. Use --fix-ignores to migrate downstream projects. log
require_ignore_comment_plugin_prefix now validates prefixed ignore comments against the rule registry. Four false-positive fixes across gradient-in-build, dartdoc cross-refs, cyclomatic-complexity flat switches, and large-objects-in-state recomputed caches.
avoid_gradient_in_build no longer flags gradients inside AnimatedBuilder.builder, TweenAnimationBuilder.builder, ListenableBuilder.builder, or ValueListenableBuilder.builder closures, where the gradient intentionally varies every animation frame. Also exempts gradients in any builder: closure when the gradient's arguments reference a closure-unique parameter (e.g. a tween value), making the gate work for custom animation builders too. No action required.verify_documented_parameters_exist no longer flags valid dartdoc cross-references to methods, functions, or getters as stale parameter names. No action required.require_ignore_comment_plugin_prefix now validates the suffix of already-prefixed ignore comments against the rule registry. A prefixed name that doesn't match any registered rule (typo, renamed rule, or fabricated name) now produces a diagnostic with a "did you mean?" suggestion and a quick fix to auto-replace the typo. No action required.avoid_high_cyclomatic_complexity no longer flags flat switch dispatch tables where every case is a single return, break, or expression with no nested branching — these are enum-to-value lookups with mechanical complexity, not logical branching. No action required.avoid_large_objects_in_state no longer flags collection fields that are reassigned wholesale in method bodies without accumulating mutations. Accumulation detection now uses element-resolved field matching (immune to shadowed locals), per-variable tracking for multi-variable declarations, constructor initializer list walking, and treats ??= as a conditional reassignment instead of growth. No action required.kAnimatedRebuilders in compound_performance_patterns.dart, used by both compound-performance rules and avoid_gradient_in_build.python scripts/update_core_lint_names.py.tiers.dart import from formatting rules.saropaLints.severity.error, .warning, .info, .hint) plus the Lint integration, Analyzer plugin, and Tier controls (moved from Settings). Each severity has a colored icon (red/yellow/blue/green) and requires double-click to toggle, preventing accidental flips. No action required.analysis_options.yaml and // ignore: comments to use the new names (e.g. prefer_single_quotes → prefer_single_quotes_strict). Old names remain as deprecated aliases for one release cycle.avoid_private_typedef_functions, missing_code_block_language_in_doc_comment, and prefer_initializing_formals removed — identical to core Dart lints with no behavioral difference. Use the core Dart lint instead; no action required if already enabled.Switching Lint integration on is now near-instant instead of a two-minute wait that looked like a freeze. Every command the extension shells out to a
Switching Lint integration on is now near-instant instead of a two-minute wait that looked like a freeze. Every command the extension shells out to a Dart tool for now uses the Dart executable directly rather than routing through Flutter, and the dependency resolve is skipped altogether when nothing needs resolving. log
dart rather than flutter for pub get and analyze — the same work without the Flutter tool's startup cost, measured at 1.9 s versus 116 s on the same project — and skips pub get entirely when pubspec.yaml is unchanged and the package is already resolved. No action required.The scan CLI now lets users filter diagnostics by severity, so AI agents and CI pipelines can suppress info-level noise and focus on warnings and erro
The scan CLI now lets users filter diagnostics by severity, so AI agents and CI pipelines can suppress info-level noise and focus on warnings and errors. The extension also stops losing the in-editor analyzer plugin when Lint integration is switched off and back on, and now reports that plugin's real state instead of implying it from a setting that does not control it. log
--min-severity flag for the scan command filters diagnostics by severity threshold — --min-severity warning excludes info-level output from both stdout and the report file, reducing noise for AI agents and CI pipelines. No action required.--max-severity flag for the scan command caps output at a severity ceiling — --max-severity warning hides errors so you can triage lower-priority noise in isolation. No action required.plugins: block in analysis_options.yaml, and the on step never put it back, so a project silently lost live diagnostics with no indication of why. Enable now restores the block when it was this extension's own Off that commented it out, leaving new projects (which default to the lighter scan-on-save delivery) untouched. This has proven to be tricky!plugins: block is commented out — clicking that row while it reads Off restores the plugin. No action required."types" field to both tsconfig.json and tsconfig.test.json so the TypeScript compiler reliably resolves Node.js globals and test framework types instead of relying on auto-discovery. No action required.verify-tsconfig-types build gate that validates both tsconfig files during precompile — fails when an imported @types/* package is missing from either config's "types" array. No action required.--fail-on-drift to hard-gate (added to publish pipeline). No action required.avoid_positioned_outside_stack — covers the Positioned-in-list-passed-to-custom-widget false positive that was already fixed in v4.13.0 but had no test. No action required.This release focuses on improving the reliability and user experience of the extension's setup workflows. Progress notifications now provide real-time
This release focuses on improving the reliability and user experience of the extension's setup workflows. Progress notifications now provide real-time feedback during lengthy operations to clearly communicate the current status. Safeguards have also been introduced to prevent duplicate, conflicting tasks from executing concurrently if a command is triggered multiple times. log
pub get step, which can take over a minute on projects with many plugins — with nothing on screen to distinguish "still working" from "stuck," clicking Cancel (or clicking "Enable" again) mid-run was a reasonable reaction. The notification now shows which step is running and a live elapsed-time counter (e.g. "Running pub get… (45s)"). No action required.pubspec.yaml/analysis_options.yaml and shelling out to pub get/write_config at the same time — instead of joining the one already in progress, which could stack duplicate progress notifications and race on the same files. A second call now joins the in-flight run instead of starting a new one. The same fix applies to "Create Baseline" (saropa_baseline.json), which had the same gap. No action required.Version 15.0.1 improves the editor extension's responsiveness and resolves a file-parsing bug that prevented the plugin from re-enabling. The setup fl
Version 15.0.1 improves the editor extension's responsiveness and resolves a file-parsing bug that prevented the plugin from re-enabling. The setup flow now executes asynchronously to prevent UI freezes, while deactivated lint configurations generate significantly smaller files by omitting unused inline documentation. log
plugins: block is written commented-out (new projects, or one where "Turn Off Lint Integration" was used) no longer gets the full per-rule description dump on every regenerate — the disabled block now keeps only the rule_name: true/false lines needed to restore the exact configured tier, dropping the multi-hundred-line prose and box-drawing headers that served no purpose while inert. A live (uncommented) block is unaffected and keeps its full inline documentation. No action required; re-run dart run saropa_lints:init or trigger a config write to see the smaller file.pub get, config write, and analysis synchronously, freezing the whole editor for as long as those took instead of just showing progress. The flow now runs them without blocking the UI and can be canceled from the progress notification. Canceling during the final analysis step also no longer silently reports "Enable" as successful — it now stops and logs the cancellation instead of turning the plugin on as if the flow had completed. No action required.analysis_options.yaml mixed CRLF and plain-LF line endings, even though the disabled plugins: block was plainly present — line detection now tolerates mixed endings instead of assuming one for the whole file. No action required.…saropa_tier: in analysis_options_custom.yaml is deprecated in favor of analysis_options.yaml . Maintenance
Version 15.0.0 adds new quick fixes for error logging and variable placement while introducing a persistent background daemon for significantly faster IDE save-scans. This release resolves false positives across exception handling, lifecycle timers, static method detection, and platform target checks. Project tier management is now unified directly through project configuration, reducing default editor memory overhead. log
require_error_logging now offers a quick fix: applying it inserts a debugPrint call logging the caught error (interpolating the captured exception variable when one exists, or naming the statically-known exception type when it does not) instead of only reporting the missing log call.move_variable_closer_to_its_usage now offers a quick fix: applying it moves the flagged declaration down to just before its first use. The fix only activates when doing so is provably safe (a single-variable declaration whose initializer shares no identifier with any statement it would move past) and otherwise leaves the diagnostic for manual review, so no action is required beyond reviewing the proposed edit before applying it.avoid_catching_generic_exception no longer flags on Object/on Exception/dynamic catch clauses whose body forwards the caught error to a logging or crash-reporting call (or rethrows it) before falling back — this is a deliberate pattern for also catching Error subtypes and reporting them, not a swallowed exception. Untyped catch (e) is unaffected. (plans/history/2026.08/2026.08.15/avoid_catching_generic_exception_false_positive_logged_broad_catch.md)require_error_boundary no longer flags a MaterialApp/CupertinoApp built inside main()'s own catch clause when that clause already logged the caught error and its try body attempted runApp(...) — that's the app's crash-recovery fallback screen, not its normal entry point, and demanding it also carry an error-boundary builder: is recursive. The same shape outside main(), without logging, or without an runApp attempt in the try body still requires a builder: as before. (plans/history/2026.08/2026.08.15/require_error_boundary_false_positive_fallback_ui_inside_catch.md)require_error_logging no longer flags a catch/on Type clause with no captured exception variable if its body still calls a recognized logging function — a static message like on TimeoutException { debug('timed out'); } is a complete log entry even without touching the exception object. A clause with no captured variable and no logging call is still flagged, as before. (plans/history/2026.08/2026.08.15/require_error_logging_false_positive_unparamed_catch_with_logged_body.md)require_app_lifecycle_handling, avoid_work_in_paused_state, and require_lifecycle_observer no longer flag a Timer/Stream.periodic/.listen() subscription that is created and canceled/closed within the same State class's own initState/dispose() pair — that's Flutter's standard cleanup contract for a foreground-only ticker that doesn't need to pause on backgrounding, since it stops existing when the widget is disposed. A class whose dispose() does not cancel the field it created, or that assigns the Timer/subscription somewhere dispose() can't prove cleanup for, is still flagged, as before. (plans/history/2026.08/2026.08.15/require_app_lifecycle_handling_false_positive_dispose_cancels_timer.md)require_ios_deployment_target_consistency no longer flags import 'dart:async' (or any other import/export URI) as Swift async/await usage — the rule now skips string literals inside import/export directives before checking them against its tracked iOS 15+ API names. A genuine API name appearing elsewhere in the file is still flagged, as before. The same import/export-URI substring-match false positive was also fixed in require_ios_live_activities_setup (triggered by import 'package:live_activities/...') and require_ios_certificate_pinning (triggered by package import paths containing segments like /auth). (plans/history/2026.08/2026.08.15/require_ios_deployment_target_consistency_false_positive_import_uri_misattribution.md)prefer_static_method no longer flags methods that read instance fields or call instance methods via bare (unprefixed) identifiers — the idiomatic Dart style used throughout most codebases. Previously the rule only recognized an explicit this. prefix, so any method touching instance state through a bare identifier (including inside a nested closure) was misdiagnosed as "could be static." A method that truly uses no instance state anywhere is still flagged, as before. (plans/history/2026.08/2026.08.15/prefer_static_method_false_positive_implicit_field_access.md)move_variable_closer_to_its_usage no longer flags a deliberate "load N values, then consume all N in the same order" batch shape (e.g. five sequential await-loads followed by five field assignments) — a sibling declaration in the same contiguous run that is itself genuinely used elsewhere, or the first-use site of another such sibling, no longer counts toward the "unrelated intervening statements" distance. A genuinely far-apart single declaration, declarations used out of matching order, or unused padding declarations sitting next to a real one, are all still flagged, as before. (plans/history/2026.08/2026.08.15/move_variable_closer_to_its_usage_false_positive_batch_declaration_grouping.md)require_firebase_app_check_production and require_firebase_app_check no longer flag Firebase.initializeApp() when FirebaseAppCheck/AppCheck activation is deferred to a separate, actually-called function elsewhere in the same file — a common pattern for keeping a slow/flaky Play Integrity check off the startup path. A file where App Check is only mentioned in a comment, or where the activating function exists but is never called from anywhere, is still flagged, as before. (plans/history/2026.08/2026.08.15/require_firebase_app_check_production_false_positive_activation_in_separate_function.md)require_log_level_for_production no longer flags a bare verbose-log call (e.g. debug(...)) when the called function's own log-level parameter (level, logLevel, severity, or verbosity) already defaults to a safe value — demanding an explicit level: argument in that case would be a no-op. A callee whose default is itself verbose, unrecognized (numeric or constructor-call), or unresolvable, is still flagged, as before. (plans/history/2026.08/2026.08.15/require_log_level_for_production_false_positive_default_level_param.md)saropaLints.enabled now does. Turning that toggle off stops save scans and shuts the scanner down immediately, rather than leaving stale findings in the Problems panel. saropaLints.scanOnSave.resolveTypes (default on) controls whether scans fully resolve types so type-based rules fire; turn it off only if save latency matters more than catching those rules.scan_daemon process that builds the analyzer's project context once and keeps it warm, so a save is checked in a few seconds instead of re-paying a roughly one-minute analyzer warmup on every save. The status bar shows a warming message while the first scan after opening is still resolving; the daemon restarts automatically (with backoff) if it stops. Measured memory is comparable to the in-process analyzer plugin — the daemon's advantage is living outside the editor's own process, not a smaller footprint.dart run saropa_lints:init or the extension's Enable) no longer get a live in-process analyzer plugin — the plugins: block is written commented out by default, since it can hold several GB of resolved analysis state on large projects for no benefit over the scan-on-save daemon above. A project that already had the plugin running, or had it explicitly turned off, keeps that state through tier changes and re-enabling; uncomment the block in analysis_options.yaml to opt back in to live in-editor squiggles, or run the new "Saropa Lints: Re-enable In-Process Plugin" command to do it in one step (it also restarts the Dart analysis server so the plugin reloads immediately).scan CLI) show a plain file count instead.analysis_options.yaml is now the single source of truth for a project's lint tier. Save-triggered scans, the whole-project baseline scan, and the tier picker's "current tier" display now read the tier straight from analysis_options.yaml instead of trusting the (possibly stale) saropaLints.tier setting, so a hand-edited or regenerated config file can no longer silently disagree with what the extension shows or scans with. SAROPA_TIER remains available as a dev-only override but now logs a warning when it disagrees with the project's own config; saropa_tier: in analysis_options_custom.yaml is deprecated in favor of analysis_options.yaml.no_magic_string false-positive report (string literal inside a //-commented-out debugPrint call) and confirmed by code inspection it cannot occur — the rule and all its gating helpers are AST-callback-only, with no raw-text scanning. Added a resolved-analyzer regression test pinning this behavior. (bugs/no_magic_string_false_positive_commented_out_code.md)--resolve CLI flag, two entries collapsed into a repetition loop (one leaking a fragment resembling a stray prompt artifact), and grammatically broken fallback text — and added each as a curated dictionaries.py override so a future translation run can never regenerate the same corruption from cache.Nothing published for this version
Nothing published for this version
Dependency maintenance release — no rule or extension changes. log
Dependency maintenance release — no rule or extension changes. log
<details> <summary>Maintenance</summary>
js-yaml (extension dev dependency, via mocha) from 4.3.0 to 4.3.1, resolving GHSA-5p4m-2wfm-xmqj.</details>
This release introduces a new rule to ensure lint suppression comments work correctly in your IDE. The require_ignore_comment_plugin_prefix rule flags
This release introduces a new rule to ensure lint suppression comments work correctly in your IDE. The require_ignore_comment_plugin_prefix rule flags ignore comments referencing saropa_lints rules that lack the required package prefix, preventing suppressions from failing silently. An automated quick fix is included to instantly apply the missing prefix. log
require_ignore_comment_plugin_prefix (Essential tier, WARNING) — flags // ignore: rule_name and // ignore_for_file: rule_name comments that reference a saropa_lints rule without the required saropa_lints/ prefix, which causes the suppression to silently fail in the IDE. A quick fix inserts the prefix. No action required.dart run saropa_lints scan --fix-ignores — bulk-converts bare // ignore: rule_name to // ignore: saropa_lints/rule_name for all known saropa_lints rules across lib/, test/, and bin/.require_ignore_comment_plugin_prefix's quick fix could insert the prefix into the wrong // ignore: comment when another ignore comment sat nearby in the file. It now targets the exact flagged comment. No action required.--fix-ignores skipped hyphenated rule names (e.g. avoid-null-assertion), leaving them unprefixed. It now converts them correctly. No action required.The Analysis Optimizer now makes changes safely: it surgically updates only the patterns you're modifying while preserving your file structure, commen
The Analysis Optimizer now makes changes safely: it surgically updates only the patterns you're modifying while preserving your file structure, comments, and ordering, and backs up your configuration before every write for easy manual recovery. The dashboard excludes redundant recommendations when patterns are already covered and automatically rescans to keep the status current. log
analysis_options.yaml structure — every Apply/Remove/Fix Syntax action rebuilt the entire exclude: block from scratch, discarding section-header comments and blank-line grouping (which aren't attached to any single pattern) and re-sorting every entry alphabetically. Writes are now surgical: only the lines for patterns actually being added or removed are touched, and every other line — comments, spacing, order — is left exactly as it was. No action required.dependency_overrides/<package>/** entries never matched as "Applied" despite a dependency_overrides/** already excluding them). These redundant recommendations no longer appear. No action required.analysis_options.yaml to analysis_options.yaml.bak first, so a change can always be manually reverted. No action required.This release fixes the Analysis Optimizer's exclusion detection, which previously missed patterns already present in analysis_options.yaml and duplica
This release fixes the Analysis Optimizer's exclusion detection, which previously missed patterns already present in analysis_options.yaml and duplicated them on apply. The dashboard's two separate exclusion lists are now one sortable table with clearer status and impact indicators, plus a quick line preview before applying. log
** (routine for Dart globs) is YAML alias syntax, not a literal string, and caused a real Undefined alias parse error the moment the analyzer read the file. Every written pattern is now quoted, and previously-malformed unquoted entries are automatically re-quoted the next time any change is applied through the dashboard. No action required.# comment or a stray trailing quote (- **/*.g.dart" # ...) were never recognized as already excluded, so the dashboard kept recommending them and applying created a duplicate line. The reader now strips comments and malformed quoting before comparing, and the writer preserves each pattern's original comment on write. No action required.analysis_options.yaml, without leaving the table. No action required.analysis_options.yaml already has invalid exclude syntax and offers a one-click "Fix Syntax" that re-quotes every entry, so a broken file can be repaired without needing to apply or remove a specific pattern first. No action required.This release fixes a test-suite timeout in the health-history archival path and completes Filipino and Dutch translation coverage across the extension
This release fixes a test-suite timeout in the health-history archival path and completes Filipino and Dutch translation coverage across the extension UI. No action required. log
loadHealthHistory test timeout — complexity parsing every Dart file across archived tags exceeded the 2-minute test budget. The function now accepts an optional withComplexity parameter (defaults true; test passes false).<details> <summary>Maintenance</summary>
loadHealthHistory now caches each tag's computed HistoryPoint on disk (.dart_tool/saropa_lints/health_history_cache.json), keyed by the tag's resolved commit SHA. Repeat calls against unchanged tags skip re-archiving and re-scanning entirely.fil/nl extension i18n coverage gaps for Default, Pattern, Medium, and Open analysis_options.yaml. Pattern is kept as the English loanword already used in the sibling {count} file pattern(s) string; Open analysis_options.yaml uses verb-final Dutch order to match the existing pubspec.yaml openen sibling.</details>
This release introduces the Analysis Optimizer to help developers proactively manage their Dart analyzer's resource footprint. The extension now ident
This release introduces the Analysis Optimizer to help developers proactively manage their Dart analyzer's resource footprint. The extension now identifies memory-intensive files and provides an interactive dashboard for safely previewing and applying workspace exclusion patterns. By intelligently filtering out generated code and high-cost directories, users can easily maintain editor performance and swiftly resolve critical memory warnings. log
analyzer: exclude: patterns to reduce Dart analyzer memory usage. Applying a pattern opens a diff preview of the resulting analysis_options.yaml before writing; multi-pattern applies require confirmation. Generated code patterns (*.g.dart, *.freezed.dart, etc.) are recommended by default. No action required.This release introduces a new balanced memory mode to drastically reduce RAM consumption during incremental analysis, alongside a Full Opportunities R
This release introduces a new balanced memory mode to drastically reduce RAM consumption during incremental analysis, alongside a Full Opportunities Report designed specifically for AI-driven dependency reviews. It also refines localization workflows by eliminating false-positive translation warnings on placeholder-only templates. Developers will experience a significantly lighter background footprint on large projects and gain deeper, exportable insights into their codebase's dependency utilization. log
Saropa Lints: Export Full Opportunities Report) that consolidates every dependency and every changelog feature into one HTML, Markdown, and JSON report under reports/. Unlike the Upgrade Opportunities panel, it keeps fully-adopted packages and every changelog category, and counts each feature's usage from zero upward with the exact project file and line of every reference. Built to hand to an AI for a dependency-usage review.memory_mode: balanced setting (default) that skips type-heavy rules on unchanged files during incremental analysis, reducing CPU work on re-analysis passes. When a dependency changes, all transitive importers are automatically re-analyzed via import-graph invalidation. Set memory_mode: full in analysis_options_custom.yaml or SAROPA_MEMORY_MODE=full to restore previous behavior. No action required.<details> <summary>Maintenance</summary>
.vsix output from extension/ to the project root for easier access after packaging.{category} ({count})) as untranslatable, eliminating 48 false-positive missing-translation reports.$-prefixed identifiers.jsdom) that executes the opportunities report's inline script, so its filter, mode toggles, expand/collapse, and column sort are verified to work rather than merely to be present.usesTypeResolution) to support balanced memory mode filtering.</details>
This release introduces comprehensive system health monitoring to track memory usage and safely terminate orphaned background processes. It also resol
This release introduces comprehensive system health monitoring to track memory usage and safely terminate orphaned background processes. It also resolves severe memory retention issues during analysis of large codebases and refines localization workflows by preventing false-positive translation warnings. Developers will experience a significantly more stable and responsive environment with highly accurate diagnostic results during extended coding sessions. log
TERM_PROGRAM is set. No action required.<details> <summary>Maintenance</summary>
flutter daemon processes accumulating on Windows and exhausting system RAM. Includes hardened cleanup scripts with PID-reuse detection, a scheduled task to break the OOM feedback loop, and a Win32 Job Object permanent fix concept.PID, RSS, Daemon to MT do-not-translate list and expand skip logic for emoji+placeholder patterns (⚠ {size}, 🔴 {size}), resolving 71 false-positive missing-translation entries across 24 locales. No action required.</details>
Resolves a runtime error in the lint diagnostic reporter that could prevent ignore-comments and deduplication checks from functioning correctly. This
Resolves a runtime error in the lint diagnostic reporter that could prevent ignore-comments and deduplication checks from functioning correctly. This release also hardens internal code quality with broad static analysis improvements and introduces new automated CI gates to prevent future regressions. log
ruleContext reference in SaropaLintRule.registerNodeProcessors — the diagnostic reporter was receiving an unresolved identifier instead of the method's RuleContext parameter, which could cause ignore-comment and dedup checks to fail at runtime. No action required.<details> <summary>Maintenance</summary>
unnecessary_string_interpolations, unnecessary_string_escapes, and prefer_adjacent_string_concatenation lint issues across lib/ to future-proof against pana baseline upgrades to package:lints/recommended.yaml. No action required.this/late, missing @override, prefer_contains, use_super_parameters, prefer_collection_literals, and parameter naming alignment with base class signatures._isProjectRootInitialized from SaropaLintRule.scripts/check_dart_fix.py — CI gate that fails if fixable dart issues exist; hardened with multiple regex patterns and error handling for missing dart or timeout.dart fix --dry-run / --apply into the publish pipeline as an auto-fix step before blocking checks.// ignore: suppressions for 5 unfixable recommended.yaml issues (implementation_imports, library_private_types_in_public_api, prefer_interpolation_to_compose_strings) with verified rationale comments.scripts/check_recommended_yaml.py — CI gate that temporarily enables recommended.yaml analysis and asserts zero unsuppressed issues; preserves original file bytes on restore, handles YAML document markers and missing dart.dart fix and recommended.yaml checks to pre-commit hook — regressions are now caught before push; dart availability is checked by each Python script (exit 2 = skip), not the shell.</details>
Fix 41 static analysis issues flagged by pub.dev pana scoring (unnecessary null checks, unused imports, missing curly braces, dead code, redundant ign
Static analysis fixes. log
Ignore: Published build error - mixed code/versions. Ignore.
Ignore: Published build error - mixed code/versions. Ignore.
Introduces a new lint rule to catch invalid date initializations that would otherwise silently roll over into incorrect dates. Developers are now guid
Introduces a new lint rule to catch invalid date initializations that would otherwise silently roll over into incorrect dates. Developers are now guided toward strict parsing methods to make date handling safer across Dart and Flutter projects. log
avoid_datetime_constructor — flags DateTime() and DateTime.utc() constructors, which silently roll over out-of-range values (e.g. month 13 becomes January of the next year). All-literal in-range calls are allowed. Quick fix available: replace with DateTime.tryParse(). No action required.avoid_datetime_constructor_unvalidated — flags DateTime() calls whose result is consumed directly (returned, passed as argument, used in field initializer) without being assigned to a local variable where components can be validated. No action required.Fix false positive in avoid_bluetooth_scan_without_timeout — the rule no longer fires on non-Bluetooth scan() calls. log
Fix false positive in avoid_bluetooth_scan_without_timeout — the rule no longer fires on non-Bluetooth scan() calls. log
avoid_bluetooth_scan_without_timeout no longer flags scan().listen() on non-Bluetooth receivers (e.g. dedup scanners, port scanners). No action required.require_bluetooth_state_check now recognizes additional Bluetooth package types (flutter_reactive_ble, bluetooth_low_energy, quick_blue, universal_ble). No action required.avoid_bluetooth_scan_without_timeout skips files without scan-related strings via requiredPatterns pre-filter, reducing unnecessary AST traversal. No action required.Re-release of v14.3.10 with a build fix — no rule or extension changes. log
Re-release of v14.3.10 with a build fix — no rule or extension changes. log
<details> <summary>Maintenance</summary>
parseMethodBody test helper and add CI guard against childEntities usage on class-like declarations. No action required.</details>
Skipped: Internal build only. ---
Skipped: Internal build only.
…Translate as the per-string fallback. NLLB is deprecated; existing NLLB-provenance translations are treated as low-quality and re-translated on the ne…
Two new comprehensive rules help monitor native bridge performance by requiring the @MethodChannelInstrumented annotation on channel classes and ensuring those calls are wrapped in timing helpers like noteIfSlow. log
require_method_channel_instrumented — flags classes that call MethodChannel.invokeMethod / invokeListMethod / invokeMapMethod without a @MethodChannelInstrumented annotation, one diagnostic per class. Quick fix inserts the annotation. Comprehensive tier.prefer_method_channel_note_if_slow — flags bare invoke-method calls inside @MethodChannelInstrumented classes that are not wrapped in noteIfSlow or an equivalent timing helper. Comprehensive tier.<details><summary>Maintenance</summary>
name: saropa_lints in pubspec, treats the implicit plugin load as configured, and creates a plugins: saropa_lints: block when no anchor exists for rule_packs writes.health_history_test needs git tags; shallow CI clones lacked them. Changed to fetch-depth: 0 (full clone) so tags and history are always available.shell-quote 1.8.4 → 1.10.0 (quadratic DoS in parse()), replaced abandoned npm-run-all with maintained npm-run-all2@8, and overrode brace-expansion to patched versions (exponential DoS). All dev-only dependencies..github/dependabot.yml to batch all extension npm security updates into a single weekly PR (Mondays) instead of one PR per alert.--mode upgrade run. No user action required./no_think, <|endoftext|>, [INST], etc.). Contaminated entries auto-heal on the next translation run. GPU detection is deferred to first use so importing the engine no longer runs nvidia-smi.</details>
Fixes an issue where the analysis server repeatedly restarted the plugin isolate, causing IDE diagnostic results to clear continuously. Automatically
Fixes an issue where the analysis server repeatedly restarted the plugin isolate, causing IDE diagnostic results to clear continuously. Automatically excludes common non-Dart output directories during initialization to prevent file-watcher feedback loops. Adds restart-rate telemetry, log rotation, and a configurable log_level setting to control plugin log verbosity. log
contacts project), clearing all diagnostics from the Problems tab each time. Two causes addressed: (1) Plugin.start() now skips config loading when the working directory is not a Dart project (e.g. the VS Code install directory), eliminating the 0-rules phase and noisy log entries; (2) PluginLogger.setProjectRoot() now validates that the root contains pubspec.yaml before writing log files, preventing log writes into non-project directories that could trigger file-watcher restarts.dart run saropa_lints:init and the headless config writer now ensure common non-Dart directories (reports/**, docs/**, bugs/**, plans/**, doc/**, output/**, tmp/**) are in the analyzer > exclude list. Without this, plugin log writes to reports/.saropa_lints/ could trigger the analysis server's file watcher and restart the plugin isolate in a feedback loop.PluginLogger counts recent "session started" entries in the log file. When the rate exceeds 10 restarts in 10 minutes, a WARNING line is emitted with remediation advice. The log file itself is the durable counter since statics reset per isolate.ensureNonDartExcludes now detects flow-style exclude: [...] under the analyzer: section and leaves it unchanged instead of inserting a duplicate exclude: key. Trailing comments after exclude: are also handled correctly.plugin.log is now capped at 512 KB; oldest content is discarded at each isolate start, bounding the cost of the restart-rate telemetry read and preventing unbounded disk growth. No action required.log_level: key under plugins > saropa_lints in analysis_options.yaml controls which messages are written to plugin.log. Valid values: off, error, warning, info (default), debug. Messages below the configured level are still sent to the analysis server's developer log but skip the user-visible file. The init command writes log_level: info by default.PluginLogger.debug(), .warning(), and .error() replace the level: named parameter pattern, making log call sites more concise. Unrecognized log_level values now emit a warning instead of silently falling back to info. Tab-indented configs are now parsed correctly.Updates the Dio linting behavior to favor dependency injection and factory patterns over static singletons. The updated rule flags top-level and stati
Updates the Dio linting behavior to favor dependency injection and factory patterns over static singletons. The updated rule flags top-level and static Dio declarations while permitting instantiation inside methods, constructors, and callbacks, resolving an architectural contradiction with anti-singleton guidelines. log
require_dio_singleton to require_dio_factory — the rule now flags Dio() in static fields and top-level variables (the singleton anti-pattern) instead of recommending them. Dio() inside methods, constructors, closures, and DI callbacks is allowed. Resolves the architectural contradiction with avoid_singleton_pattern (#274). No action required if already using factory/DI patterns.require_dio_factory config alias: Projects using require_dio_singleton in analysis_options.yaml continue working via configAliases — no config migration required on upgrade.require_dio_factory detection: Added coverage for late static final Dio fields, static getters, nested closures, and mixin method bodies. No action required.<details><summary>Maintenance</summary>
bugs/*.md references in active documents (skips frozen plans/history/).</details>
Removes the avoid_debug_print rule, which contradicted the existing prefer_debug_print and left no valid console output path. Also fixes false positiv
Removes the avoid_debug_print rule, which contradicted the existing prefer_debug_print and left no valid console output path. Also fixes false positives in avoid_redundant_null_check and avoid_redundant_await when types are nullable or resolve across package boundaries. A new --debug-rule flag on the scan CLI traces type resolution for any named rule, making it easier to diagnose false positives.
log
avoid_debug_print rule deleted. The rule contradicted prefer_debug_print — one said "use debugPrint," the other said "don't" — leaving no valid console output function for projects without a custom logging wrapper. prefer_debug_print remains and covers the print() → debugPrint() upgrade path. No action required unless your config explicitly enabled avoid_debug_print; if so, remove the entry.CommentOutDebugPrintFix quick fix deleted (was the only fix for the removed rule). No action required.--debug-rule <name> flag for the scan CLI. Emits per-node type-resolution trace output (staticType, staticInvokeType, returnType) for the named rule during a scan. Use with --resolve for full type information. Designed for diagnosing false positives caused by type-resolution divergence in the analyzer plugin context. No action required.avoid_redundant_null_check no longer fires on variables, parameters, fields, or getters declared with a nullable type (Type?). The rule cross-checks the element's declared type against the resolved staticType and guards against InvalidType from failed type resolution, preventing false positives in cross-package contexts.avoid_redundant_await no longer fires on await of static methods returning Future<T>. The rule now guards against InvalidType (unresolvable types) and falls back to checking the invoked method signature's return type via staticInvokeType when staticType fails to resolve for cross-file static invocations.This update improves the precision of our accessibility lints by isolating Flutter UI components from lower-level graphics classes. Projects utilizing
This update improves the precision of our accessibility lints by isolating Flutter UI components from lower-level graphics classes. Projects utilizing external image processing libraries alongside Flutter will no longer experience irrelevant warnings. log
isFlutterWidgetNamed(Element?, String) shared utility for verifying a resolved element is a Flutter SDK widget by name and library origin, with TypeAliasElement unwrapping.require_image_semantics, require_image_description, require_accessible_images no longer fire on non-Flutter classes named Image (e.g. package:image's pixel-buffer Image or dart:ui's Image). All three rules now verify the declaring library is package:flutter/ before reporting, with TypeAliasElement unwrapping for typedef'd widget references.Adds a cross-tool data channel so sibling Saropa Suite tools can pull this project's daily health snapshot, adds a validated fresh_code risk flag to t
Adds a cross-tool data channel so sibling Saropa Suite tools can pull this project's daily health snapshot, adds a validated fresh_code risk flag to the Code Health report, and revives a batch of lint rules that never fired for anyone: seven that were missing their most common bad-code shape, and fourteen whole-file rules (desktop, BLoC, Riverpod, iOS, testing, navigation, i18n, and animation checks) that reported through an end-of-file step the analysis engine ignored. Also fixes a broken age signal that scored every function as maximally stale. No action required — the API is opt-in and the new flag and fixes take effect automatically. log
fresh_code flag in the Code Health (vibrancy) report. Functions with cyclomatic complexity above 10 whose body was written or rewritten within the last 90 days are now flagged, because validation against real bug-fix history showed recently rewritten complex code causes incidents far more often than old code. No action required — the flag appears in the CLI report and as a filterable pill in the extension's Code Health view.getDailySummary(date) on the extension's public API. Sibling Saropa Suite tools can now read this project's current health score, violation counts, and error-level trouble items for a given day via getExtension('saropa.saropa-lints').exports.getDailySummary('YYYY-MM-DD'), which resolves to a documented DailySummary (or undefined before any analysis has run). No action required — the summary is built lazily on call, reads only local analysis output, and transmits nothing.prefer_notifier_over_state false positives eliminated. The rule matched StateProvider by scanning serialized source text, which could match unrelated identifiers containing that substring; it now checks the constructor/invocation name directly via the AST. The MethodInvocation branch is restricted to the known Riverpod factory methods (autoDispose, family) to prevent false positives from unrelated static methods. A fixture pins all three detection branches and a false-positive decoy. No action required.prefer_list_contains now flags indexOf(x) != -1. The rule only recognized a bare 0 or -1 on the right of the comparison, but -1 is written as a negation, not a plain number, so the most common presence check — list.indexOf(x) != -1 — was never flagged. It now is. No action required.avoid_map_keys_contains now flags map.keys.contains(k) on a plain variable. The rule previously matched only chained receivers (like this.map.keys.contains(k)) and missed the ordinary map.keys.contains(k) on a simple map variable — the usual shape. Its quick fix (map.containsKey(k)) now applies to those cases too. No action required.avoid_unnecessary_collections now flags List.of([...])/Set.of(...)/Map.of(...). The rule missed these wrapped-literal constructors during full analysis because they are constructor calls, which analysis represents differently from the method-call shape the rule looked for. Both shapes are now flagged. No action required.prefer_asmap_over_indexed_iteration now flags for (i = 0; i < list.length; i++). The rule required the loop bound to be a chained property read and missed the ordinary list.length on a plain list variable — the usual shape — so it effectively never fired. It now does. No action required.require_key_for_collection now flags ListView.builder/GridView.builder during full analysis. These are constructor calls, which full analysis represents differently from the method-call shape the rule looked for, so keyless items in the most common list builders went unflagged; only a few less-common widgets were caught. All shapes are now flagged. No action required.prefer_commenting_future_delayed now works during full analysis and stops flagging already-commented delays. Future.delayed is a constructor call (represented differently from a method call during full analysis), so the rule never fired for anyone; and it looked for the explanatory comment on the wrong token, so an await Future.delayed(...) with a comment above it was treated as uncommented. Both are fixed: the rule fires on uncommented delays and stays quiet when a comment precedes the statement. No action required.avoid_sequential_awaits now fires. The rule registered for a callback the analysis engine silently ignores, so three or more independent sequential awaits (which could run together with Future.wait) were never flagged for anyone. It now registers correctly and reports. No action required.prefer_single_exit_point, prefer_guard_clauses, require_getit_registration_order, and require_hive_adapter_registration_order. All registered through the same ignored callback as avoid_sequential_awaits, so none produced a diagnostic for anyone. All four now register correctly and report. No action required.pass_correct_accepted_type now fires, and prefer_correct_identifier_length now checks parameter names. Both registered for a parameter callback the engine ignores: pass_correct_accepted_type never fired at all, and prefer_correct_identifier_length only checked variable names, silently skipping parameters. Both now register correctly. No action required.require_menu_bar_for_desktop, require_window_close_confirmation, require_error_state, avoid_circular_provider_deps, prefer_notifier_over_state, require_apple_sign_in, require_error_case_tests, avoid_test_coupling, require_test_cleanup, prefer_test_variant, require_route_transition_consistency, prefer_shell_route_for_persistent_ui, require_intl_locale_initialization, and prefer_implicit_animations. All now scan the file in a single pass and report correctly; require_intl_locale_initialization also stops missing usages that a duplicate registration had been discarding, and require_apple_sign_in now recognizes the standard GoogleSignIn().signIn() call shape (a constructor-call receiver) that its detection had been skipping. No action required.<details> <summary>Maintenance</summary>
accuracy_report) so it exercises stylistic rules. No tier — not even pedantic — contains the stylistic rules, so the previous tier-scoped scan never enabled them and falsely reported stylistic rules with fixtures as silent; correcting it flipped 80 previously-false-silent rules to firing (the silent worklist dropped from 744 to 664). The report now defaults to all defined rules (--tier <name> narrows it), via a new optional explicit rule-set on the scan runner.WebSocketChannel). The four remaining are two rules whose fixtures resolve to zero diagnostics under the full-corpus scan (cause not yet isolated with per-file tooling) and two expect_lint markers naming rules that were never implemented.addPostRunCallback, addFunctionBody, addFormalParameter), which silently discard their callback and were the root cause of the fourteen dead whole-file rules revived this release. The guard forces authors to the real registrations instead.require_error_state, avoid_circular_provider_deps, prefer_notifier_over_state). Because these rules judge the whole file, a fixture that placed a BAD and a GOOD example together let the GOOD example mask the BAD; the compliant examples were moved to sibling *_good.dart files, path-gated fixtures were relocated, and mock classes (GoogleSignIn, CupertinoPageRoute, FadeTransitionRoute) were added so constructor-based rules resolve. All fourteen are confirmed firing (six in the full corpus scan, eight in isolated scans — the eight hit the pre-existing full-corpus-scan measurement limitation with the crowded test-fixture directory, already noted for the async cluster).unused flag producing ~50% false positives on multi-package repos. Nested pubspec.yaml files fragmented the analysis context, the resolved-usage pass silently degraded, and every @override method and bin/-only-called function was flagged dead. The fix scopes the analysis context to lib/, test/, bin/ (preventing fragmentation), includes bin/ files in the usage set (so CLI delegates get real caller counts), and adds a syntactic @override safety net that protects polymorphic methods even if resolution degrades. No action required.loadHealthHistory test so it asserts real behavior instead of silently passing on empty results. The test had if (points.isEmpty) return, which meant a completely broken function still produced a green test. It now requires non-empty results (this repo has tags), asserts codeLoc > 0, the codeLoc <= loc invariant, and distinct tags when two points are returned.HistoryPoint.toMarkdownRow(), HistoryPoint.markdownHeader, and HistoryPoint.toMarkdownTable() for rendering health trajectory as markdown tables.require_window_close_confirmation_desktop_fixture.dart to match the rule-name convention, and added 8 fixture files that existed on disk but were missing from the verification list.require_window_close_confirmation_desktop_good.dart to drop the _desktop suffix.Directory.listSync() auto-discovery pattern. Every fixture verification group now scans its directory on disk, so adding a fixture file is automatically tested — no manual list to maintain or drift out of sync. The android_rules_test retains one explicit test for a cross-directory fixture (require_android_manifest_entries in example/lib/platform/). Two files (roadmap_15_rules_test, migration_rules_test) were excluded because their fixture groups contain content-validation tests beyond simple existence checks.discoverFixtures() helper (test/helpers/fixture_discovery.dart) and migrated all 127 fixture-verification test files to use it. The helper returns an empty list when the directory is missing, so the guard test fails with a clear assertion instead of a FileSystemException aborting the group. Removes ~7 lines of duplicated listSync chain per file.test/integrity/fixture_integrity_test.dart) that cross-references every *_fixture.dart on disk against getAllDefinedRules(). Catches stale or misspelled fixture files whose names don't match any registered rule. Group/category fixtures (covering multiple rules) are logged but not failed. Includes a regression floor at >2300 exact-match fixtures.</details>
Adds a rule pack for device_calendar_plus, a maintained replacement for the abandoned device_calendar plugin with a different API (no relation to the
Adds a rule pack for device_calendar_plus, a maintained replacement for the abandoned device_calendar plugin with a different API (no relation to the existing device_calendar rule pack, which stays as-is). Also fixes the Package Dashboard's Opportunities detection so document files like README.md are never counted as an adoptable API, and adds an Opportunities section to the Package Detail sidebar with per-feature links to the package's source code and documentation. No action required — the new rules and the Opportunities fixes take effect automatically. log
isAllDay: true) given a UTC-converted date, which can shift the event to the wrong calendar day; and flags updateEvent calls that change no field, a no-op the package treats as silently harmless. No action required — rules run automatically wherever device_calendar_plus is imported.README.md, CHANGELOG.md, or pubspec.yaml was being extracted as if it were a dotted API reference (like ReelText.rich), so the Package Dashboard's Opportunities column and count could include filenames instead of real code. Extraction now excludes filename-shaped tokens. No action required — rescanning drops the false entries.<details> <summary>Maintenance</summary>
target/realTarget accessor in the device_calendar_plus UTC-taint helper's DateTime.parse branch (no behavior change — not a realistic cascade shape).</details>
Cuts sustained editor CPU while you type. The analyzer plugin runs inside the Dart analysis server, which re-analyzes a file on nearly every keystroke
Cuts sustained editor CPU while you type. The analyzer plugin runs inside the Dart analysis server, which re-analyzes a file on nearly every keystroke; until now each pass re-ran the entire configured tier over code that was still in flux. During rapid editing the plugin now defers all of its rules until editing settles — the Dart analyzer still reports compile errors live. Full-fidelity batch analysis is unchanged. log
dart run saropa_lints scan, dart analyze) still run every rule at full fidelity, and the Dart analyzer keeps reporting compile errors live while you type. No action required; saropa_lints diagnostics reappear once editing pauses.Fix for raised issue: https://github.com/saropa/saropa_lints/issues/269 log
Fix for raised issue: https://github.com/saropa/saropa_lints/issues/269 log
dart run saropa_lints:baseline) parsed dart analyze output with the wrong format matcher and so reported every project as clean, generating an empty baseline. It now reads the analyzer's diagnostic format correctly and captures real violations. Re-run the command to regenerate an accurate baseline.Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Your coding agent can read these notes before it upgrades. Set up the MCP server →