build(melos): add melos workspace skeleton (useRootAsPackage) - #2276
Merged
Conversation
Install melos 7.8.1 as a dev_dependency and configure the repo as a single-package pub-workspace: the app stays at the repo root via useRootAsPackage, with packages/ and features/ reserved as homes for future workspace members. melos is dev-only and never compiled into the app, so the reproducible APK is unaffected (async stays 2.13.1; the lock diff is purely additive dev-tooling).
Add a Monorepo / melos section to AGENTS.md (always invoke via fvm dart run melos, makefile stays canonical, packages/+features/ reserved) plus an explicit exception to rule #14 for the pre-created dirs. Add a forward-looking Monorepo Migration section to ARCHITECTURE.md explaining how pub-workspace boundaries turn the dependency rules into compile-time guarantees.
Correct the make deps command to include --enforce-lockfile, and note that enforceLockfile requires each future workspace member to commit a pubspec.lock (or bootstrap with --no-enforce-lockfile until they do). Both surfaced by audit of the skeleton.
Hide the long 'fvm dart run melos' invocation behind 'make bootstrap', and add the missing 'make analyze' target (fvm flutter analyze --fatal-warnings --fatal-infos) that mirrors CI and the pre-commit hook. Keeps the makefile the single canonical entry point — no one types bare melos or long fvm commands.
PR self-review consistency: the toolchain bullet now shows make deps as --enforce-lockfile (matching the makefile), and the pubspec melos comment points at make bootstrap instead of the raw fvm dart run melos command.
…ealed UI) Expand the migration docs with concrete intent: root = thin app shell; features/ = Flutter feature packages (send/receive/buy/sell/recoverbull) mounted in the shell; packages/ = pure-Dart shared foundation (wallet, secrets, infra). Document enforced encapsulation (lib/src + curated export + implementation_imports + CI) and the sealed-UI pattern — e.g. secrets exposes a MnemonicView widget without exposing the mnemonic, the one reason a package may depend on Flutter.
This comment was marked as resolved.
This comment was marked as resolved.
The analyze flags (--fatal-warnings --fatal-infos) lived in three places: CI (raw), the makefile, and implicitly the pre-commit hook (which ran bare flutter analyze, so info lints passed locally but failed CI). Make 'make analyze' the single definition and call it from both the pre-commit hook and the CI workflow, so the floor provably equals CI and there's nothing to drift. Surfaced in the #2276 review.
Rewrite ARCHITECTURE around one layered pattern in iteration-1 vocabulary (no port/adapter/application/framework renaming). Add the "where does logic live" rule (invariants→domain, data-shaping→repository, orchestration→usecase), repository as abstract interface in domain/ + impl in data/, models cap at two, one sealed error family per feature, Result at the repo boundary, melos convergence table, and an enforcement section (class modifiers, sealed, @useResult, analyzer plugin, custom_lint, melos boundaries). Align AGENTS rules and naming tables to the consolidated vocabulary: use-case always present and thin, bloc never imports a repository/datasource, repository abstract in domain + impl in data, deprecate the hexagonal folders. FEATURES: align Key Dependency Patterns prose to the real graph edges and add the melos packages/<domain> note; dependency graph unchanged. Grounded in docs.flutter.dev/app-architecture.
Sharpen ARCHITECTURE.md and AGENTS.md so a new contributor can design features correctly and find their way around the code. - attribute recommendations to the Flutter team, not Google - errors: document toTranslated(context) for user-facing messages via context.loc; end user never sees a dev string; catch-all variant must return a generic localized message, not the raw text - add the repository boundary rule (public signatures carry domain types only) and make entity/model always two separate types - give each layer an Owns/Never/Smell block; document execute() as the use-case entry method; add an end-to-end vertical slice - explain the facade as the inter-feature contract (concrete class, no extra interface) and what types may cross it - distinguish a repository from a non-repository capability Port - onboarding: reading order + repo doc-map, "Entry points & code tour" (main -> locator -> router), and an "Adding a feature" checklist - add a Security section (secrets, sealed UI, capture blocking) and expand Tests with layout and tooling - frame the docs as a target we converge toward, not a finished state - agents: when touching non-compliant code, offer a scoped refactor as a separate change and let the developer decide - add model/mapper naming rows; fix l10n convention to context.loc
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
Groundwork for the monorepo migration — no app code moved, no feature sliced, no
workspace:member added. Two coordinated pieces:1. Architecture baseline (docs). Consolidate
ARCHITECTURE.md,AGENTS.mdandFEATURES.mdon one layered pattern and document it as an onboarding guide, so every future extraction has a clear contract to converge toward.ui → bloc → usecase → repository → datasourcein iteration-1 vocabulary (noport/adapter/application/frameworkrenaming).execute(...)use-case convention, and an end-to-end vertical slice.toTranslated(context)user messages viacontext.loc, the end user never sees a dev string (generic catch-all).Portdistinction; convergence table for the ~6 drifted hexagonal modules; a compiler-enforcement section.main → locator → router), and an "Adding a feature" checklist; a Security section (secrets, sealed UI, capture blocking).FEATURES.mdprose aligned to the real graph edges + a melos note;README.mdgains a "Development" signpost to these docs.2. Melos workspace skeleton (build/config).
melos7.8.1 as adev_dependency.melos:block inpubspec.yaml:useRootAsPackage: true(app stays at repo root), IntelliJ file generation off,command.bootstrap.enforceLockfile: true.packages/andfeatures/created as reserved homes for future members (documented exception to AGENTS.md rule 14).make bootstrap(wrapsfvm dart run melos bootstrap) andmake analyze(the CI/pre-commit analyze check, previously missing), with the pre-commit hook and CI routed throughmake analyzeas the single source.Why
We're migrating to a melos monorepo incrementally — extracting
lib/core/intopackages/andlib/features/intofeatures/over time, one reviewed slice at a time. A migration like that needs a documented target architecture and a workspace skeleton before any code moves; this PR lays both, and nothing else.We deliberately keep the app at the repo root via
useRootAsPackage: truerather than relocating it toapps/bull/(the layout most large melos repos use): movingios/ android/ lib/would disturb the reproducible-build chain (Containerfile/apppaths,SOURCE_DATE_EPOCH, makefile output paths) — high risk and out of scope for a skeleton.useRootAsPackagecoexisting with futureworkspace:members is supported (melos PR #927).Target architecture (documented, not built yet)
features/— Flutter packages per user-facing flow (send,receive,buy,sell,recoverbull, …), each behind itspublic/facade, mounted into the shell.packages/— pure-Dart shared foundation: shared domain (wallet,secrets) + infrastructure (storage,electrum,blockchain, …).lib/<name>.dart(export 'src/…' show …);lib/src/is package-internal; cross-packagesrc/imports fail via theimplementation_importslint +analyze --fatal-infos(CI forbids// ignore).secretsexposes aMnemonicViewwidget but no function returning the words — the seed never crosses the package boundary. This is the one reason a package may depend on Flutter. (Seal is programmatic-only — paired with screenshot blocking / semantics exclusion / ephemeral handling.)Reproducible build — unaffected
melos is a
dev_dependency, never compiled into the app. Thepubspec.lockchange is purely additive (only melos's own dev-tooling transitives;asyncstays 2.13.1, no runtime dependency shifted). The container build (make android) never invokes melos. The released APK/AAB hash is therefore expected to be byte-identical.make depsis unchanged (fvm flutter pub get --enforce-lockfile).Not in this PR
No code moved, no
workspace:members, no changes to the makefile build path / Containerfiles / devcontainer. Security/bitcoin audit deferred to the first real extraction (when crypto code actually moves into a package). When the first member lands: re-verify analyze coverage across members and theenforceLockfile+ per-member-lock interaction (noted in AGENTS.md).