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
@riverpodannotation (code-gen); rundart run build_runner watch - Providers live in the feature's
data/ordomain/layer - Never put providers in
presentation/widgets directly — import from domain/data - AsyncNotifier for async state, Notifier for sync state
Navigation
AppRoutesconstants inlib/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 withoutunawaited()annotation - Imports: always
package:imports, no relative../imports - Formatting:
dart formatenforced; 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
mocktailfor mocking repositories in widget tests - Test files mirror
lib/structure undertest/
.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:
- Break implementation plan into tickets → create in
.tasks/todo/ - Move one ticket to
.tasks/processing/before starting work - Move to
.tasks/done/when complete, then pick the next ticket - Update the overview table in
.tasks/README.mdfor 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 |