You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The repo is migrating incrementally to a [melos](https://melos.invertase.dev/) pub-workspace. The Flutter app remains at the repo root through `useRootAsPackage: true`; current members are the `bull_ui` design system, its dev-only catalogue, the pure-Dart `primitives` package, and the pure-Dart `bull_payjoin` package. The makefile remains the canonical entry point across the workspace.
36
+
The repo is migrating incrementally to a [melos](https://melos.invertase.dev/) pub-workspace. The Flutter app remains at the repo root through `useRootAsPackage: true`; current members are the `bull_ui` design system, its dev-only catalogue, the pure-Dart `primitives` package, the pure-Dart `bull_payjoin` package, and the Flutter `bull_tor` package. The makefile remains the canonical entry point across the workspace.
37
37
38
38
-**Run melos through the makefile** (`make bootstrap`), which wraps `fvm dart run melos` so the pinned SDK ([`.fvmrc`](.fvmrc)) is used. Never type bare `melos` (wrong SDK). For melos subcommands without a make target yet, use `fvm dart run melos <cmd>` — and add a make wrapper if it becomes routine.
39
39
-**The makefile stays canonical** for daily commands. melos does not replace it: `make deps` is still `fvm flutter pub get --enforce-lockfile`, and the reproducible build chain ([Containerfile.app](Containerfile.app), [build-android.yml](.github/workflows/build-android.yml)) does not run melos. melos is a `dev_dependency` only — never compiled into the app, so the reproducible APK is unaffected.
40
-
-**`packages/` and `features/` are reserved homes** for the migration (exception to rule #14 below): pure-Dart shared-foundation packages in `packages/` (shared domain like `wallet`/`secrets` + infrastructure like `storage`/`electrum`, no Flutter UI — `packages/` holds the shared foundation, with `lib/core`'s infra-only spirit per rule #7 preserved for the infra packages); Flutter feature packages (`send`, `receive`, `buy`, `sell`, …) mounted by the root shell in `features/`. A `packages/` member may depend on Flutter only to own a sealed UI component (e.g. `secrets`' `MnemonicView`, which renders the mnemonic without exposing it). As code is extracted, each new package gets `resolution: workspace` and is added to a `workspace:` key in the root pubspec; the root keeps `useRootAsPackage: true` (this combination is supported — see melos PR #927). See ARCHITECTURE.md "Monorepo Migration" for the full layout and the sealed-UI / encapsulation rules.
40
+
- **`packages/` and `features/` are reserved homes** for the migration (exception to rule #14 below): app-internal shared-foundation and infrastructure packages live in `packages/` (shared domain like `wallet`/`secrets` + infrastructure like `storage`/`electrum`), with `lib/core`'s infra-only spirit per rule #7 preserved for the infra packages; Flutter feature packages (`send`, `receive`, `buy`, `sell`, …) mounted by the root shell live in `features/`. Packages stay pure Dart by default, but a `packages/` member may depend on Flutter when its encapsulated capability inherently requires a Flutter plugin or platform lifecycle (`bull_tor` is the canonical example), when it owns a sealed UI component (`secrets`' `MnemonicView`, which renders the mnemonic without exposing it), or when it implements the `bull_ui` design system. A Flutter dependency does not permit feature presentation or screens in an infrastructure package. As code is extracted, each new package gets `resolution: workspace` and is added to a `workspace:` key in the root pubspec; the root keeps `useRootAsPackage: true` (this combination is supported — see melos PR #927). See ARCHITECTURE.md "Monorepo Migration" for the full layout and the sealed-UI / encapsulation rules.
41
41
-**Workspace verification is measured, not assumed.** Whole-project `fvm flutter analyze` covers the root and all members, and `make bootstrap` succeeds with `enforceLockfile: true` (verified 2026-07-30). Pub workspaces use the single root `pubspec.lock`; do not add member lockfiles. `make unit-test` dispatches Flutter members to `fvm flutter test` and pure-Dart members to `fvm dart test`.
Copy file name to clipboardExpand all lines: ARCHITECTURE.md
+4-2Lines changed: 4 additions & 2 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -341,16 +341,18 @@ While the architecture described above represents our current standards, the cod
341
341
342
342
The codebase is migrating, incrementally, from a single Flutter package to a [melos](https://melos.invertase.dev/) pub-workspace. This is an organizational change layered on top of the architecture above — it does not alter the feature-based / layered rules, it gives them a stronger, compile-time boundary.
343
343
344
-
**Current state.** The app remains at the repo root and is declared as a workspace package through `useRootAsPackage: true`. `packages/bull_ui` owns the design system, `packages/bull_ui_catalogue` is its dev-only Widgetbook, `packages/primitives` owns shared pure-Dart value types and results, and `packages/bull_payjoin` owns the pure-Dart Payjoin contract, engine, and persistence. User-facing feature packages have not moved into `features/` yet.
344
+
**Current state.** The app remains at the repo root and is declared as a workspace package through `useRootAsPackage: true`. `packages/bull_ui` owns the design system, `packages/bull_ui_catalogue` is its dev-only Widgetbook, `packages/primitives` owns shared pure-Dart value types and results, `packages/bull_payjoin` owns the pure-Dart Payjoin contract, engine, and persistence, and the Flutter-dependent `packages/tor` (`bull_tor`) owns embedded Tor. User-facing feature packages have not moved into `features/` yet.
345
345
346
346
**Target layout.** As modules are extracted (one at a time, never a big-bang), they become pub-workspace members:
347
347
348
348
-**root = the app shell** — thin: routing, DI/get_it wiring, and composition of feature packages. It ships no domain or UI of its own beyond that wiring.
349
349
-**`features/`** — Flutter packages, one per user-facing flow (`send`, `receive`, `buy`, `sell`, `recoverbull`, …). Each owns its domain behind its `public/` facade and is mounted into the shell (routes + DI). A feature may carry an `example/` mini-app to run it in isolation during development; the shipped artifact is the shell embedding the feature, not a standalone app.
350
-
-**`packages/`** — mostly pure-Dart packages with no Flutter UI: the shared foundation consumed by features. This is both shared domain (`wallet`, `secrets`) and infrastructure (`storage`, `electrum`, `blockchain`, …). Two narrow categories may depend on Flutter: a package that must own a sealed UI component (see "Sealed UI" below), and the shared **design-system package** (`bull_ui`, see "Design-system package" below).
350
+
-**`packages/`** — app-internal foundation and infrastructure consumed by features. Packages stay pure Dart by default (`wallet`, `storage`, `electrum`, `blockchain`, …), but may depend on Flutter when encapsulating an inherently Flutter/platform capability (`bull_tor`), when owning a sealed UI component (see "Sealed UI" below), or when implementing the shared **design-system package** (`bull_ui`, see "Design-system package" below). A Flutter dependency must serve the package's encapsulated capability; it is not permission to add feature presentation or screens.
351
351
352
352
Each extracted package gets `resolution: workspace` and is listed under a `workspace:` key in the root `pubspec.yaml`; the root app keeps `useRootAsPackage: true` and consumes the members. Dependencies point one way and stay acyclic: **shell → features → packages**. `packages/` never import `features/`; a feature touches a package only through its published API.
353
353
354
+
**Tor package (`packages/bull_tor`).** Tor is a Bull Mobile-specific Flutter infrastructure package. The Bull-prefixed package name lets it coexist with the Foundation `tor` dependency while consumers migrate in independently testable slices, and keeps its ownership explicit after that dependency is removed. The feature-facing `tor.dart` library publishes validated routes, sealed connection states, typed failures, and the operations consumers need. The narrower shell-facing `tor_adapter.dart` publishes composition, lifecycle, logging, and test seams. Internally the package owns Onion/Arti initialization, state/cache paths, mobile dormancy, the embedded SOCKS listener adapter, and external Orbot SOCKS verification.
355
+
354
356
**Why it matters for architecture.** Package boundaries turn the existing rules into *enforced* ones rather than conventions:
355
357
356
358
- The **Dependency Rule** and **acyclic feature graph** become compile errors when violated — a `packages/` infrastructure package physically cannot import a `features/` module, and a feature cannot reach into another feature's internals (only its published API is importable).
@command -v strings >/dev/null 2>&1|| { echo"❌ 'strings' (binutils) not found — cannot verify rustc pins. Install binutils; failing closed rather than skipping the check (a skipped check must never read as green).";exit 1; }
0 commit comments