Repository navigation
Add hedgehoglab_lints analyzer plugin (STATE rules and metric limits) - #2
Open
hedgehogowainbrown wants to merge 12 commits into
Open
hedgehogowainbrown wants to merge 12 commits into
hedgehogowainbrown wants to merge 12 commits into
Conversation
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
# Conflicts: # README.md
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The root job ran dart analyze over the whole repository, which now includes the plugin package; without its dependencies resolved that reported 380 unresolved-type issues. The plugin has its own workflow. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…atalogue Widget build methods keep 50 source lines; every other function gets 30. Add standards.lock.json (catalogue revision and per-rule content hash), a offline test that the lock and registered rules agree, and tool/verify_standards_pin.dart, which recomputes Quilltrail's content_hash. Document exceptions and the fix-on-change versioning policy. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…ersions Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
shouldAnalyse used RuleContext.isInLibDir, which accepts any path with a lib segment, so a Swift Package Manager checkout under build/ was reported. Resolve against the package root and skip build/ and .dart_tool/ (including a package nested inside another's output). Read the defining unit: currentUnit is null while processors register, so the generated-file skip never applied. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…ject file flutter pub get rewrites analysis_options.yaml to add them unless the project's own file lists them; it does not follow include:. Show the exact recommended project file. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…s created on release Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What and why
Adds
plugin/hedgehoglab_lints/, an analyzer plugin (analysis_server_plugin0.3.24, Dart 3.10+) sodart analyzeitself enforces code-metric limits and the first mechanically checkable rules from flutter-standards04-state-management.md. Nothing is read into an agent's context until a rule is violated. Seven warning rules, all prefixedhl_:hl_notifier_suffix, STATE-007hl_notifier_void_methods, STATE-002hl_no_static_singleton(conservative heuristic, documented).hl_max_function_length,hl_max_cyclomatic_complexity(20),hl_max_nesting(5),hl_max_parameters(4).Metrics are reimplemented because
dart_code_linter4.4.2 exposes its metrics only through its own CLI, notdart analyze(a function breaking every limit gave no warnings underdart analyze). Its lint rules can still run alongside.Owner decisions applied
buildkeeps 50 source lines; every other function or method gets 30. A widget build is a non-static method namedbuildwhose resolved return type isWidget(frompackage:flutter) or a subtype (covers StatelessWidget, State, ConsumerWidget). A Riverpod notifierbuild()returns state and gets 30. Messages say which limit applied ("over the widget build limit of 50" / "over the function limit of 30").// ignore: hl_<rule>stays the mechanism. The README states that in projects using the agentic toolkit an added// ignore:fails the switch-off check unless a human applies therule-exceptionlabel to the PR (implemented in the toolkit, not here).standards.lock.jsonrecords the catalogue (flutter-standards67839f58, tagfeat/quilltrail-catalog-reference, policy 2026.09.2) and, per rule,{file, title, content_hash, lints}for STATE-002/005/007. The README now says IDs are Quilltrail catalogue IDs at the pinned revision and each lint is pinned to the rule's content hash.content_hashis SHA-256 oftitle\ntag\ntrimmed body(resolve_standards.dart,hashRuleContent). I reproduced all three hashes from the pinned commit's markdown and they match bnf-flutter'sprovenance.json.tool/verify_standards_pin.dartrecomputes each hash and exits 1 on a mismatch (a standard changed, so re-review the lint). Modes: default fetches the pinned commit withgh;--standards-dir DIR;--provenance FILE(also checks commit and policy version).test/standards_lock_test.dart(offline): every standards rule class ID is in the lock, every lock entry names registered lints, plus unit tests of the hash algorithm.Adoption cost (new limits, plugin run over the exported BNF PR trees)
hl_max_function_length, widget build over 50hl_max_function_length, other function over 30hl_notifier_suffixhl_notifier_void_methods,hl_no_static_singleton, complexity, nesting, parametersNo notifier
build()was over 30 in either tree. Hits of the 30-line limit:catalogue/lib/catalogue/components.dart:componentsCategory(36),components/monograph.dart:monographComponents(63),components/navigation.dart:navigationComponents(58),foundations.dart:foundationsCategory(32),pages.dart:pagesCategory(59),catalogue/lib/synthetic_content_styles.dart:forElement(82),lib/core/firebase/firebase_bootstrap.dart:bootstrapFirebase(34),lib/features/content/data/schema/content_database.dart:open(34).firebase_bootstrap.dart:bootstrapFirebase(34),content_database.dart:open(34),features/app/routing/go_router_tab_navigator.dart:_pagesForMatches(35),features/navigation/data/demo_navigation_repository_impl.dart:_resolve(31) and_content(49).Verification
dart format --set-exit-if-changed .(plugin and repo root),dart analyze: clean. CI: both jobs pass.dart test: 48 passed. Function length has positive/negative/edge cases for both limits (30 and 31 lines; widget build at 50 and 51; State and indirect-ConsumerWidget builds; notifierbuild()at 30 and 31;_buildBody()helper; non-widgetbuild).dart analyzereports exactly 8 warnings inviolations*.dart(one per rule, plus a 31-line notifierbuild()under the 30 limit);clean.dartandstubs.dartreport none. CI greps both length messages.verify_standards_pin.dartpasses in all three modes against the pinned commit and bnf-flutter'sprovenance.json, and fails with a clear message on an altered hash.Findings that decide adoption
plugins:block in aninclude:d file was honoured bydart analyzeon Dart 3.13, but the docs say it is unsupported: put the block in each project's ownanalysis_options.yaml.flutter analyzeis unreliable with plugins on 3.47.0 (plugin warnings in 3 of 6 runs); gate ondart analyze.riverpod_lint: ^3.1.9loads alongside.Open points
STANDARDS_READ_TOKENsecret (read access to flutter-standards) exists; without it the lock is checked bydart testonly.Out of scope
Per-project thresholds (the plugin API offers on/off only), quick fixes, further standards, tagging a release, and the toolkit-side
rule-exceptioncheck.Trial adoption fixes
Defects found adopting this in bnf-flutter, fixed in separate commits:
isInLibDiraccepts anylibsegment, so a Swift Package Manager checkout underbuild/produced 18hl_max_parameterswarnings. It now analyses only<package root>/lib/**, and skipsbuild/,.dart_tool/and packages nested inside them. Tests added (test/scope_test.dart). This also fixed the generated-file skip, which never applied because it readcurrentUnit(null while rules register).flutter pub getrewrites a project'sanalysis_options.yamlunless the project's own file lists seven excludes (build/**and the six platform directories); it does not followinclude:. The README now shows the exact recommended project file.dart analyze --fatal-infosthroughout, sinceflutter analyze3.47 drops plugin diagnostics intermittently, and says the plugin tag is created on release.riverpod_lintis removed frompubspec.yamlonce listed underplugins:.🤖 Generated with Claude Code