timetracker/AGENT.md
2026-08-03 21:51:48 +02:00

5.5 KiB

AGENT.md — Timetrack

Project Overview

Flutter time-tracking app for capturing and analysing working hours. Targets: Android, iOS, Web. Data is stored locally (offline-first, no backend).

Stack

Concern Choice
Framework Flutter 3.41 / Dart 3.11
State Riverpod (riverpod_annotation)
Database Drift + SQLite
Navigation go_router (StatefulShellRoute)
Charts fl_chart
Models freezed + json_serializable
Export pdf + csv + share_plus
i18n flutter_localizations (ARB)
Tests flutter_test + mocktail

Architecture — Feature-first

lib/
  main.dart              # ProviderScope → App
  app.dart               # MaterialApp.router, theme, locales
  core/
    database/            # Drift AppDatabase, DAOs
    router/              # GoRouter provider (app_router.dart)
    theme/               # AppTheme.light / AppTheme.dark
    l10n/                # ARB files + AppLocalizations
  features/
    timer/               # Active timer — start/stop, project select
    entries/             # TimeEntry CRUD + manual input
    projects/            # Project CRUD (name, color, description)
    reports/             # Day/week/month views + fl_chart
    settings/            # Theme toggle, language, export

Each feature follows: data/domain/presentation/

Data Models

Project

int id, String name, int colorValue, String? description,
DateTime? archivedAt

TimeEntry

int id, int projectId, DateTime startTime, DateTime? endTime,
Duration? duration, String? note, List<String> tags

Tag

int id, String name

Junction table time_entry_tags links entries ↔ tags. Full schema: see .ai/database.md

Riverpod Conventions

  • Use @riverpod annotation (code-gen); run dart run build_runner watch
  • Providers live in the feature's data/ or domain/ layer
  • Never put providers in presentation/ widgets directly — import from domain/data
  • AsyncNotifier for async state, Notifier for sync state

Navigation

  • AppRoutes constants in lib/core/router/app_router.dart
  • Bottom nav: /timer | /entries | /projects | /reports | /settings
  • Deep links use GoRouter sub-routes inside each branch

Coding Conventions

  • Language in code: English (variables, comments, docs)
  • UI strings: ARB files only — never hardcode display text
  • Models: always freezed + copyWith; no mutable model classes
  • Async: always await; no fire-and-forget without unawaited() annotation
  • Imports: always package: imports, no relative ../ imports
  • Formatting: dart format enforced; trailing commas required
  • Single quotes throughout

Testing

See .ai/testing.md for full conventions.

  • Unit tests for all repository methods and domain logic
  • Widget tests for each screen (golden tests optional)
  • Use mocktail for mocking repositories in widget tests
  • Test files mirror lib/ structure under test/

.ai/ Context Files

File Content
.ai/database.md Full Drift schema, migration strategy
.ai/architecture.md ADRs, dependency decisions
.ai/testing.md Test conventions, patterns, examples
.ai/tickets.md Ticket workflow, template, agent rules
.ai/features/timer.md Timer state-machine, background rules
.ai/features/reports.md Report queries, chart data format
.ai/features/export.md CSV/PDF/JSON export logic

Ticket Workflow

Every step in an implementation plan becomes a ticket under .tasks/. Full rules, the ticket template, and the agent workflow are in .ai/tickets.md.

Short version:

  1. Break implementation plan into tickets → create in .tasks/todo/
  2. Move one ticket to .tasks/processing/ before starting work
  3. Move to .tasks/done/ when complete, then pick the next ticket
  4. Update the overview table in .tasks/README.md for every new ticket

Interacting with the User

When any decision, clarification, or preference is needed — no matter how small — always use the question tool rather than asking in plain text. This applies to architecture choices, naming, scope, priority, and ambiguous requirements. Only skip the question tool when the answer can be unambiguously derived from the codebase or existing documentation.

Do / Don't

Do Don't
Use ConsumerWidget / ConsumerStatefulWidget Don't use StatefulWidget + manual setState
Keep screens thin — delegate to providers Don't put business logic in widgets
Use drift DAOs for all DB access Don't use raw SQL strings outside DAOs
Write tests alongside new features Don't commit untested repository methods
Export via share_plus share sheet Don't write files to arbitrary paths
Use freezed for all domain models Don't use plain Dart classes for models
Single active timer — stop before starting new Don't allow concurrent timer instances