Files
GroundedHelper/frontend/CLAUDE.md
alvocool 16bff634b5 Initial commit: Grounded Flutter frontend
A to-do app that doesn't believe you — an enforcement layer rather than a
neutral ledger.

Architecture ported from Autoreceptives/Frontend/Receptive: stacked MVVM with
the mandatory 4-file screen pattern, one ParentViewModel owning the loading /
network / error overlays and the handleError decision tree, one AppDataManager
gateway, dio comms carrying the three identity headers, secure storage with
random-suffixed keys, and a single-chokepoint Navigator. Package root and Dart
package name are both Grounded; org is nya.

The enforcement engine, one unit per formula in utils/:

- DebtEngine      w(class) x severity(d) x decay(t), sublinear severity so old
                  misses cannot swamp the score; abandonment 2x with 30-day
                  decay immunity; late complete retains 30%
- StandingEngine  Good -> Warned -> Grounded -> Lockdown, derived not set;
                  Grounded replaces home with the overdue queue
- CapacityEngine  blocks over-scheduling against p50 of historically completed
                  minutes, with a learned per-category estimation multiplier
- IntegrityEngine session integrity, weekly volume, plyometric contact ceiling
                  and enforced recovery gaps
- ExcuseAnalyser  on-device excuse clustering plus the confrontation copy
- GuardrailEngine distress detection and rationed amnesty
- ToneEngine      all enforcement copy, so the tone cap lives in one place

CommitmentEvent is append-only and is the source of truth rather than the
status field, which is what makes honest history and excuse analysis possible.

Goals contain commitments via parentId, and a task can be run from a
full-screen runner that derives elapsed time from wall-clock so screen-off
cannot lose time. Backgrounding pauses the clock and is counted. The runner is
mirrored into an ongoing notification, with alarm-class full-screen intents
reserved for non-negotiables.

Design language, fonts, icon and native splash are in place; Mason bricks are
retargeted to this project and verified end-to-end.

flutter analyze lib/ reports no errors.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Mfu2gQLSFN21YRBcU2NrTt
2026-07-27 09:11:17 +03:00

180 lines
6.5 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Grounded — Flutter Frontend
*A to-do app that doesn't believe you.*
Architecture ported from `Documents/Autoreceptives/Frontend/Receptive`. Same
structure, conventions and infrastructure; the business logic is the debt and
enforcement engine.
---
## 1. Stack
Flutter (Dart `^3.6.2`), **stacked** MVVM, **dio**, **flutter_secure_storage**,
**flutter_dotenv**, **fl_chart**, **flutter_local_notifications**.
Package name and `lib/` root are both `Grounded`, so imports read
`package:Grounded/Grounded/...`. Org is `nya` (`nya.grounded`).
Codegen is via **Mason** — screens, endpoints and storage fields are generated,
never hand-scaffolded.
---
## 2. Layout (`lib/Grounded/`)
```
about/external/data/ responses & domain models (+ pages/ for pagination)
about/external/initial/ request bodies
about/internal/application/ enums and app-internal models
about/internal/file/ FileStorage
comms/ CommsDirections · ConnectComms · Comms
informatics/ DataManager · AppDataManager
memory/ ConnectInternalMemory · InternalMemory
configs/ Navigator · Env · NotificationServiceConfig
designs/ Component · Shell · Responsive · buttons/ input/ text/
see/<feature>/ screens, 4 files each
utils/ Colors · CommonUtils · engines · validators
```
---
## 3. The 4-file screen pattern (mandatory)
| File | Role |
|---|---|
| `Foo.dart` | `StatefulWidget` shell — creates `FooState` only. |
| `FooState.dart` | All UI + local state; implements `ConnectFoo`. |
| `ViewFoo.dart` | `extends ParentViewModel` — business logic, calls `DataManager`, pushes results back through `ConnectFoo`. |
| `ConnectFoo.dart` | Abstract callbacks so the ViewModel never touches widgets. |
`ViewModelBuilder<ViewFoo>.reactive` wires them. `onViewModelReady` assigns
`_model` and calls one named `_initiate()`. **No logic in `builder:` before the
`return`.**
Generate with: `mason make mvvc_template --project Grounded --screen Foo`
---
## 4. ParentViewModel
Constructs the single `AppDataManager` and owns everything cross-cutting:
`showLoading` / `closeLoading`, `hasNetwork(retry)`, `showError`,
`showApplicationNotification`, and `handleError` — whose decision tree routes
`401 → sessionExpired()` and error code `5000.901 → updateMe()`.
Guard every network call:
```dart
void loadThing(ThingRequest request) async {
if (!await hasNetwork(() => loadThing(request))) return;
showLoading('Loading…');
try {
final response = await getDataManager().getThing(request);
closeLoading();
connection.onThingLoaded(Thing.fromJson(response.data));
} catch (e) {
handleError(e, () => loadThing(request), () => dismissError(), 'Retry');
}
}
```
---
## 5. Domain — the enforcement engine
Three primitives: **Commitment** (what you said you'd do), **Debt** (weighted,
decaying score), **Standing** (`Good → Warned → Grounded → Lockdown`).
**Goals** contain commitments via `Commitment.parentId` — "Workout" holds
"Monday shoulders". Goals never carry debt; the tasks inside them do.
Engines in `utils/`, each mapping 1:1 to the spec's formulas:
| Engine | Owns |
|---|---|
| `DebtEngine` | `w(class) × severity(d) × decay(t)`; abandonment 2×, no decay 30d; late complete retains 30% |
| `StandingEngine` | tier derivation; what each tier permits |
| `CapacityEngine` | blocks over-scheduling against p50 of historical completed minutes × 0.85 |
| `IntegrityEngine` | session integrity, weekly volume, plyo contact ceiling, recovery gate |
| `ExcuseAnalyser` | on-device excuse clustering + the confrontation copy |
| `GuardrailEngine` | distress detection, amnesty tokens |
| `ToneEngine` | **all** enforcement copy — the one place the tone cap is enforced |
Thresholds live in `Thresholds.dart`. Never inline a limit.
`CommitmentEvent` is append-only and is the source of truth — **not** the
status field. That's what makes honest history and excuse analysis possible.
---
## 6. Design language
See `design.md`. In short: black chrome → white sheet with a 28 radius →
oversized **Light** display title → tiny grey all-caps labels above values.
- Every screen is built from `Sheet` (`designs/Shell.dart`).
- Display titles are `TextType.Light`. Bold is for labels and metrics only.
- Never raw `Text`/`TextStyle` — use `text()` from `designs/text/Text.dart`.
- Never inline hex — use `utils/Colors.dart`.
- Standing owns a colour used consistently everywhere (chrome, chip, meter).
- One primary button per screen; everything else outlined or text.
Font: **General Sans** (Fontshare, FFL — see `fonts/LICENSE-GeneralSans.txt`),
wrapped as `TextType.{Bold,Medium,Regular,Light}`.
---
## 7. Live task runner
`see/live/` is the full-screen runner: a task being run takes over the screen.
Elapsed time is derived from **wall-clock**, not counted by the ticker, so a
screen-off period cannot lose time. Backgrounding pauses the clock and
increments `backgroundedCount` — that's what makes Timer proof mean anything.
`LocalNotificationEngine` mirrors it into an ongoing notification and fires
alarm-class **full-screen intents** for non-negotiables. Android manifest
carries `USE_FULL_SCREEN_INTENT`, `SCHEDULE_EXACT_ALARM`, and the activity is
`showWhenLocked` + `turnScreenOn`.
---
## 8. Hard conventions
1. Named event handlers only — `onPressed: _onSave`, never `() => _save()`.
2. `selectField` loads on tap; never pre-load picker options.
3. List parsing via `utils/ObjectConvertors.dart` — never inline `.map(...fromJson)`.
4. Pagination: `HistoryRequest { PageAndSort }`; responses are `*Page`; `last == true` stops loading.
5. Reload-on-return: child `Navigator.pop(context, true)`, parent refreshes on the result.
6. New endpoint → `mason make api_endpoint` (injects into all four HTTP files).
7. New secure field → `mason make internal_memory`.
8. One class per file, named after the class.
---
## 9. Environment
`.env` is a bundled asset, loaded in `main()` before `runApp`:
```
GROUNDED_PRODUCTION_PATH · GROUNDED_LOCAL_PATH · LOCALISED_APP_VERSION
```
`isProd` in `CommsDirections.dart` flips local↔production. Requests carry the
three identity headers: `what`, `whom`, `version`.
---
## 10. Commands
```bash
flutter pub get
flutter analyze lib/ # bricks/ are excluded — templates aren't valid Dart
flutter run
mason get
mason make mvvc_template --project Grounded --screen MyScreen
dart run flutter_launcher_icons
dart run flutter_native_splash:create
```