Repository navigation
Conversation
Read the import and export directives of every Dart file under a
package's lib/ and bin/, and add five rules:
- missing-primary-barrel: a feature package has no lib/{package}.dart.
- deep-import: an import or export reaches past the barrels at the root
of another workspace package's lib/, src/ or not.
- cross-feature-primary-barrel: a feature imports another feature's
primary presentation barrel instead of a dedicated one.
- data-barrel-exports-internals: a data barrel exports from
data_sources/, dtos/, or mappers/.
- unused-dependency (warning): a workspace dependency under
dependencies is never imported.
Findings in a Dart file carry its path and line, and can be suppressed
in that file with `// ffca-ignore: <rule-id> — <reason>`.
The hook now also runs on .dart edits. For a Dart file the validator
reports only the findings located in that file, so a Dart edit never
blocks on a violation elsewhere in the package.
Every fixture package gains a barrel that imports its declared
dependencies, and analysis_options.yaml keeps the fixtures out of
dart analyze.
Closes #25
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017Vxxv9fVFGupLXEnmx5bSq
8 of 10 tasks
data-barrel-exports-internals flagged every data-source export, which reported five false positives on mealify_feature_first: the app is the composition root and constructs the data sources to inject them into each repository, so the data barrels export them on purpose. The rule now covers DTOs and mappers only. The data page's "Neither is visible outside the package" contradicts mealify and is tracked in VGVentures/very_good_engineering#294. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017Vxxv9fVFGupLXEnmx5bSq
From a review of #29, #30 and #31: - Check imports of undeclared workspace packages against the rules table. A Dart workspace resolves them, so `product_presentation` could import `cart_data` without either mode noticing. A forbidden layer reports that layer rule; an allowed one reports the new undeclared-dependency error. - On a pubspec edit, run import checks and suppression validation for the edited package only, so a clean edit never blocks on a dependent's existing Dart findings. - Skip missing-primary-barrel in incremental mode while a package has no lib/, so a new package can be scaffolded pubspec first. - Read directives with a scanner that stops at the first declaration, follows multi-line directives, and reads every URI, so imports inside strings are ignored and both sides of a conditional import are checked. - Check every barrel at the root of a data package's lib/, following relative exports one level, for exported DTOs and mappers. - Report a `//` suppression naming a pubspec-only rule, and group suppression findings under the file the comment is in. - Fix CLAUDE.md, which said dart only runs for pubspec edits, and cite the barrel rule's actual sources. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017Vxxv9fVFGupLXEnmx5bSq
This was referenced Oct 15, 2026
| final root = _findWorkspaceRoot(file.parent.absolute.path); | ||
| if (root == null) return 0; // not FFCA-shaped | ||
|
|
||
| final workspace = _discover(root); |
Member
There was a problem hiding this comment.
On a Dart edit, _discover still reads and scans every Dart file in the workspace, though only the edited file's directives are reported. Since this now runs on every Dart Edit/Write, could discovery read Dart files lazily? For example, keep pubspec discovery as it is, but only call _readDartFiles for the edited package in --file mode, and for all packages in --all. That would keep the per-edit cost proportional to one package, not the whole monorepo.
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.
Description
Closes #25. Stacked on #30, which is stacked on #29: the base is
feat/validator-severity, so this diff shows only the #25 changes. Merge #29, then #30, then this.The validator now reads the
importandexportdirectives of every Dart file under a package'slib/andbin/and adds five rules:missing-primary-barrellib/{package}.dartdeep-importlib/cross-feature-primary-barreldata-barrel-exports-internalsdtos/ormappers/; repositories and data sources are fine, since the app constructs data sources to inject themunused-dependencydependenciesnever imported inlib/orbin/Findings in a Dart file are grouped under that file with their line, and can be suppressed in it with
// ffca-ignore: <rule-id> — <reason>, the Dart form #21 deferred to this issue.Decisions
.dartedits (the open question in feat: check imports and barrels in the layer validator #25), but for a Dart file the validator reports only the findings located in that file, so a repo with existing violations does not block every Dart edit. Package-wide findings (missing barrel, unused dependency) come with pubspec edits and--all. Cost: onedart runper Dart edit in an FFCA repo.deep-importinstead of asrc/rule.project_structure.md(Layer subfolders) putsmodels/,data_sources/, and so on directly underlib/, whiledata.md's examples uselib/src/. Asrc/-only rule would miss deep imports in the reference's own layout, so any import past a root barrel counts.lib/{package}.dart. The reference names it{feature}_{layer}.dart, which differs for backend packages:auth_data_firebaseneedsauth_data_firebase.dart.package:analyzer, which keeps the script free of dependencies, as it stays in the plugin rather than moving into Very Good CLI. The scanner stops at the first declaration and follows multi-line directives (see Review fixes below).Fixtures and tests
valid_workspacecovers the passing shapes: an app importing primary barrels,ideas_presentationimportingcart_presentation's dedicatedcart_badge.dartbarrel, andproduct_dataexporting its repository.invalid_workspacegainssigma/upsilon(primary barrel),tau(data barrel exports),phi(deep import, plus a file undertest/that must be ignored), andchi(no barrel).warning_workspacegainsomega: an unused dependency and a deep import suppressed in its Dart file.scripts/analysis_options.yamlkeeps the fixtures out ofdart analyze.hooks/validate_layers_test.sh.Docs
README rule table and Dart suppression syntax,
CLAUDE.mdhook behavior on Dart edits,AGENTS.mdstructure, and thehooks.jsondescription. The auditor agent and theffca-auditskill drop their manual unused-dependency and barrel checks, now deterministic, and keep barrel scope and the DTO leaks a regex cannot see.Checked against a real repo.
mealify_feature_first, the example app the reference cites, passes with no findings. A first run reported five false positives because the rule also covered data sources, which mealify exports on purpose; the rule now covers DTOs and mappers only, and the contradicting sentence in the data page is tracked in VGVentures/very_good_engineering#294.Review fixes
An independent review before team review found these gaps, all fixed, each with a fixture and a test:
product_presentationcould importcart_datawithout declaring it, and neither mode noticed. Imports of undeclared workspace packages now go through the rules table: a forbidden layer reports that layer rule's ID on the import line, and an allowed one reports the newundeclared-dependencyerror.lib/, failedmissing-primary-barrel. Incremental mode now skips it untillib/exists;--allstill reports it.lib/is checked now, following relative exports one level.//comment naming a pubspec-only rule is reported instead of silently matching nothing, and suppression findings are grouped under the file they are in.CLAUDE.mdsaiddartonly runs for pubspec edits, and the barrel rule's citation now points at what the FFCA pages actually say (the general rule is missing from the FFCA section, raised on VGVentures/very_good_engineering#294).New fixture shapes that must pass: a multi-line string containing an
import, a two-lineexport … show, a double-quoteddeferredimport, and a shared package with no barrel. mealify_feature_first still passes with no findings.Verified locally:
dart analyze,dart format,dart test(71 tests), both hook test scripts (validate_layers_test.sh: 21 cases), markdownlint, and cspell.Type of Change
feat)fix)refactor)docs)ci)chore)🤖 Generated with Claude Code
https://claude.ai/code/session_017Vxxv9fVFGupLXEnmx5bSq