Architecture Style: Feature-Sliced Design (adapted for Flutter Web + Firebase)
- Vertical slices around business capabilities (deck publication, discovery, user identity) rather than pure technical layers.
- Each slice owns UI + state + domain adapters but respects cross-slice layering rules.
- Centralized shared abstractions (config, firebase, design system) avoid duplication.
- Immutability constraints (deck core fields) enforced at repository and Firestore rules layers.
From outer (more concrete) to inner (more abstract):
- app (composition root, routing, global providers)
- processes (multi-step flows spanning multiple features, e.g., deck_publish_flow)
- features (user-facing atomic capabilities: create_deck, edit_deck_tags, list_decks, view_deck, auth_login, auth_signup; each owns its page widget)
- entities (core domain models + small domain logic: deck, tag, version, user)
- shared (reusable primitives: ui kit, theming, utils, firebase adapters, config)
(If a layer is not needed initially it can be omitted until first use.)
app/
  lib/
    app/                # Root app setup, MaterialApp, routes, dependency injection
      app.dart
      router.dart
      di/
        providers.dart
    processes/          # Orchestrated flows
      deck_publish_flow/
        deck_publish_flow.dart
  // Pages removed; each feature exposes its own page widget under features/<feature>/ui/
    features/
      create_deck/
        ui/
          create_deck_form.dart
        model/
          create_deck_state.dart
        logic/
          create_deck_controller.dart
      edit_deck_tags/
        ui/
          edit_tags_sheet.dart
        model/
          edit_tags_state.dart
      list_decks/
        ui/
          deck_list_view.dart
        model/
          deck_list_state.dart
        logic/
          deck_list_controller.dart
      view_deck/
        ui/
          deck_detail_view.dart
        model/
          deck_detail_state.dart
      auth_signup/
        ui/signup_form.dart
        model/signup_state.dart
        logic/signup_controller.dart
      auth_login/
        ui/login_form.dart
        model/login_state.dart
        logic/login_controller.dart
    entities/
      deck/
        deck.dart
        deck_repository.dart
      tag/
        tag.dart
        tag_repository.dart
      user/
        user.dart
        user_repository.dart
      version/
        version.dart
        version_provider.dart
    shared/
      firebase/
        firebase_init.dart
        firestore_paths.dart
        security_notes.md
      config/
        active_version_provider.dart
      ui/
        widgets/
          primary_button.dart
          tag_chip.dart
        theme/
          app_theme.dart
      utils/
        result.dart
        validators.dart
      constants/
        limits.dart
      services/
        auth_service.dart
        remote_config_service.dart
Higher layer can depend on same or inner layers only.
- app -> (processes, features, entities, shared)
- processes -> features, entities, shared
- features -> entities, shared
- entities -> shared (avoid upward references)
- shared -> (no dependencies on other slices; only platform & packages)
Forbidden: features referencing each other directly (coordinate via entities or processes). Avoid singletons; use DI/providers.
- Use bloc/Cubit for reactive state; each feature owns a small state notifier, use the cubit state pattern instead of copywith.
- No global monolithic state store; composition at page/process level.
- Repositories live under entities/* and expose domain-centric interfaces (DeckRepository, TagRepository, UserRepository, VersionProvider).
- Services (firebase auth, remote config) in shared/services; repositories depend on them.
- Snake_case for files, lowerCamelCase for members, PascalCase for types.
- Feature folder names reflect user intent (create_deck, list_decks) not technical terms.
app/
  test/
    features/
      create_deck/
        create_deck_controller_test.dart
    entities/
      deck/
        deck_repository_test.dart
    shared/
      utils/
        validators_test.dart
- Unit tests for domain logic & validators.
- Golden tests using alchymist lib for form validation & deck list filtering.
- users/{uid}
- usernames/{username}
- tags/{tagId}
- decks/{deckId} No subcollections required for MVP.
- Remote Config: activeVersion { id, label, discordUrl }
- Build-time flavors (optional later): dev, prod.
- .env style (Flutter --dart-define) for non-secret flags (e.g., ENABLE_ANALYTICS=false for MVP).
- Add analytics module under shared/services/analytics_service.dart
- Add moderation feature slice later (report_deck)
- Introduce Serverpod adapter by implementing new repositories while keeping interfaces stable.
- User logs in (auth_login feature)
- Active version fetched (version_provider)
- Tags loaded (tag_repository > Firestore)
- Deck creation form (create_deck feature) validates & submits to repository
- Deck list page queries decks (list_decks feature)
- Detail view (view_deck) renders code, tags, version link
- Edit tags sheet (edit_deck_tags) updates only strategy tags
Lightweight debug logging utility added at lib/shared/utils/app_logger.dart.
Highlights:
- Initialized in main.dartviaAppLogger.init()andAppLogger.runGuarded().
- Captures framework (FlutterError.onError), platform dispatcher, and zone uncaught errors.
- Colorized log levels (INFO/WARN/ERR) only in debug/profile; release minimizes noise.
- Repository example: user signup repository logs auth failures and rollback issues without exposing sensitive input.
Usage example:
AppLogger.info('Starting fetch', 'endpoint=/decks');
try {
  // ... work ...
} catch (e, s) {
  AppLogger.error('DeckRepository', 'Fetch failed', error: e, stack: s);
}
Avoid logging secrets (passwords, tokens, PII). Sanitize values first.
// Initialization (already invoked in main.dart)
await AppLogger.init();
// Info with context key-value pairs (serialize lightweight primitives only)
AppLogger.info('SignupController', 'submit_start', fields: {
  'username': usernameMasked(username), // never raw if sensitive
});
// Warning (non-fatal unexpected state)
AppLogger.warn('UserRepository', 'username_collision_retry');
// Error with exception + stack
try {
  await _repo.createUserWithUsername(...);
} catch (e, s) {
  AppLogger.error('UserRepository', 'create_failed', error: e, stack: s, fields: {
    'phase': 'firestore_batch',
  });
  rethrow; // still propagate if caller needs to handle
}
// Helper masking example (pseudo)
String usernameMasked(String raw) => raw.length <= 2
    ? '**'
    : raw.substring(0, 2) + ('*' * (raw.length - 2));Guidelines:
- Use stable event identifiers (e.g., submit_start, create_failed) for future log aggregation.
- Prefer one log per significant user intent phase (start, success, failure).
- Do not log full email or password; mask or omit.
TBD.
Status: Initial implementation merged on branch 001-auth-signup-feature.
Capability: Allows unauthenticated visitor to create an account with unique immutable username (regex ^[a-z0-9_]{3,20}$), email, and password (6-128 chars). Performs client-side validation and repository-mediated submission.
Key Components:
- features/auth_signup/ui/signup_page.dart: Page scaffold & success redirect to- /decks.
- features/auth_signup/ui/signup_form.dart: Form fields + validation + error mapping.
- features/auth_signup/logic/signup_controller.dart: Cubit controlling state machine (idle → submitting → success|error) and guards against double submission.
- features/auth_signup/model/signup_state.dart: Immutable state + error codes.
- entities/user/user.dart: User entity (id, username, email, createdAt).
- entities/user/user_repository.dart: Abstract repository ensuring atomic username + user profile creation (implementation stubbed / to be completed with Firebase integration).
- shared/utils/validators.dart: Username/email/password validation helpers (regex + length bounds).
- shared/utils/result.dart: Lightweight Result type (success/failure) enabling error mapping without exceptions.
User Flow (Happy Path):
- User enters valid values → submit.
- Controller performs sync validation; on success invokes repository.
- Repository (future impl) creates auth user + Firestore docs atomically.
- State transitions to success → page redirects to /decks.
Failure Modes & Handling:
- Invalid fields → immediate SignupStatus.errorwith specificSignupErrorCode.
- Network/unknown failure → networkFailuresurfaced.
- Username taken (repository-level) → usernameTakenerror.
Open Items / TODOs:
- Concrete Firebase-backed UserRepository+AuthServiceimplementation.
- Tests (unit, widget, integration) outlined in specs/001-auth-signup-feature/tasks.mdpending migration into test suite (some placeholders currently absent).
- Redirect guard for already-authenticated users (FR-010) to be formalized in router middleware.
Specification Artifacts:
- specs/001-auth-signup-feature/spec.md(functional requirements & scenarios)
- specs/001-auth-signup-feature/tasks.md(TDD task breakdown)
- specs/001-auth-signup-feature/quickstart.md(manual validation steps)
Traceability Matrix (Excerpt):
- FR-001/002/007/015: Handled via validators + controller early checks.
- FR-009: _inFlightguard in controller prevents double-submit.
- FR-011: Logging pending integration with AppLogger(add in repository/controller on completion).
- FR-016: Rollback semantics require concrete repository + auth service (NOT YET IMPLEMENTED).
Next planned features: auth login, deck creation slice. Each will follow the same spec → tasks → implementation → README update workflow.
Standard steps for adding a new feature slice:
- Create spec under specs/NNN-feature-slug/using templates.
- Add plan.md+tasks.mddescribing TDD order.
- Branch: NNN-feature-slug(e.g.,002-auth-login-feature).
- Write failing tests first.
- Implement minimal code to pass tests incrementally.
- Update READMEs with new slice summary.
- Open PR referencing spec & tasks; ensure constitution rules upheld.
See CONTRIBUTING.md for detailed guidelines (lint, commit messages, review gates).
The simplified coverage script runs tests with coverage and produces a pull-request focused diff report.
Usage:
./scripts/check-coverage.sh
Steps performed:
- flutter test --coverage(generates- coverage/lcov.info).
- git diff origin/main | pull_request_coverage --output-mode markdown --markdown-mode dart --fully-tested-message "All covered" > pull_request_coverage.md
Output:
- coverage/lcov.info: Raw lcov data from Flutter.
- pull_request_coverage.md: Markdown summary for only the lines changed relative to- origin/main. Shows uncovered changed lines or the message- All coveredif every changed Dart line has test coverage.
Notes:
- Requires the external CLI pull_request_coveragein your PATH. If absent, the diff report is skipped (tests still run).
- Intended for local validation and CI hooks; can be extended later with thresholds if needed.
Example (just run):
./scripts/check-coverage.sh
Additions or improvements (like custom base ref) can be added once needed.