fix: layered-architecture lets the app declare the data packages its bootstrap constructs - #165
Merged
ryzizub merged 2 commits intoOct 5, 2026
Conversation
…bootstrap constructs The Dependency Graph section required a repositories-only app pubspec while App Bootstrap had main_<flavor>.dart import and construct the data clients. depend_on_referenced_packages rejects that combination, so an agent following the skill kept the pubspec clean and had the repository build or default its own client, breaking the injection standard. The app pubspec now declares the data packages its bootstrap constructs, and the layer boundary is an import rule: only the entrypoints and bootstrap import a data package. Injection covers SDK objects as well as data clients, and a repository with no external source is valid. The pubspec reference gains an Import Boundary section, and a new eval case covers a request to keep the app pubspec to repositories only. Refs VeryGoodOpenSource#164 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
mark-wint
marked this pull request as ready for review
September 29, 2026 16:48
Contributor
|
@vgvbot rebase this PR |
Contributor
Contributor
|
@mark-wint can i ask to resolve conflicts. Otherwise it LGTM |
Resolve the Core Standards conflict in skills/layered-architecture/SKILL.md by keeping both new bullets: this branch's import-boundary rule and the Dart 3.13 primary-constructor rule from VeryGoodOpenSource#167. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Open
ryzizub
added a commit
that referenced
this pull request
Oct 5, 2026
#165 added a case and bumped 100 to 101; this branch did the same for its own. Together that is 102. Both sides' edits merged cleanly to 101, which was wrong. Co-Authored-By: Claude Fable 5.1 <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.
Description
The layered-architecture skill told agents the root app pubspec lists repository packages only, and in the same file had
main_<flavor>.dartimport and construct the data clients.depend_on_referenced_packagesrejects that combination, so an agent had to break one rule, and it broke injection: the repository built or defaulted its own client so the app pubspec could stay clean. This PR makes the two sections describe the same app. The evidence and the survey of VGV apps are in the linked issue.Closes #164
SKILL.mduser_api_clientfor the app.FirebaseAuth.instanceas well as data clients, and names defaulting inside the repository as out of bounds.lib/andtest/for data-package imports.references/pubspec.mdlists the data packages in both root-app examples and adds an Import Boundary section. That section covers which files count as entrypoints and bootstrap across app layouts, the grep and how to fix a hit, and how the rule applies when a third-party SDK is the data layer. When a bloc needs a data-layer type, the fix is a domain type the repository defines and maps to, the wayUserRepositoryalready turnsUserApiExceptionintoUserNotFoundException.Dropped from the issue. The issue proposed that repository barrels re-export the data-layer models, enums and exceptions their API exposes, following flutter_todos and news_toolkit. That contradicts the existing rule to transform data models into domain models and never leak API response shapes upstream, and an early eval run flagged it:
layered-architecture-refuses-domain-model-in-data-layerwent red on all three with-plugin runs with the re-export line in place. The PR keeps the transformation rule as it is.Evals. A new case,
layered-architecture-injects-client-the-app-pubspec-declares, asks for a repository wired into an app whose pubspec should list only the repository. It passes when the repository requires its client and never builds one,main_development.dartconstructs it, and the app pubspec declares the data package. Without the plugin, the model reproduces the failure the issue describes. The Verification section has the details.NOTES.mdand the case counts inAGENTS.mdandevals/README.mdare updated. Thedependency-chaingrader inlayered-architecture-lays-out-four-layersalready treats bootstrap constructing data clients as the expected pattern, so no existing grader changes.Line budget.
SKILL.mdgrows from 369 to 384 lines. Most of it is the six import lines in the bootstrap snippet and the new Core Standard. The rest of the detail went toreferences/pubspec.md.Out of scope. No mechanical CI check for the import rule ships here. It can follow once the question below is settled.
Open question: blocs that take a data-layer resource
The issue asked whether a bloc taking a data-layer resource directly is an accepted shortcut. It had no answer, so this PR keeps the layer ban the skill already states and writes the import rule as a plain directive.
io_crossword is the one VGV app that skips the layer consistently. Its Firestore reads go through repositories while calls to its own backend go through
api_clientresources straight from blocs, soHintBlocand several others importapi_client. If that split should be allowed, it is a one-sentence exception on the new Core Standard, for example: "A bloc may take a data-layer resource directly when a repository would only pass its calls through." I'm happy to add it if you want it.Verification
claude plugin validate .passes, with the rootCLAUDE.mdwarning that is already onmain.claude plugin eval . --scaffold --tag layered-architecture --runs 3 --threshold 0.8on Claude Code 2.1.277, both arms:injects-client-the-app-pubspec-declares(new)keeps-flutter-out-of-data-packageslays-out-four-layersrefuses-domain-model-in-data-layerrefuses-repository-to-repository-dependencystays-out-of-single-file-worktransforms-models-in-the-repositorywires-repositories-in-bootstrapTwo with-plugin runs dipped, and neither touches the changed text. The
refuses-domain-model-in-data-layerrun declined the request and explained the response-model and domain-model split, then, finding nothing on disk to move, closed by offering to putUserin the data package anyway if the user had a reason. The judge failed both content graders on that offer. The same case scored 3/3 onmainin a separate with-plugin run. Thekeeps-flutter-out-of-data-packagesdip is a split judge vote, PASS FAIL FAIL, onremoves-flutter-dependencyfor a response that says to drop the import.In the new case, the no-plugin arm reproduces the failure from the issue. It gives
WeatherRepositoryabaseUrland builds the client in a redirecting constructor, with a doc comment saying it does so "so that the app package never has to depend onweather_api_client."Type of Change
feat)fix)refactor)docs)ci)chore)🤖 Generated with Claude Code