diff --git a/AGENTS.md b/AGENTS.md deleted file mode 100644 index 992fcd8..0000000 --- a/AGENTS.md +++ /dev/null @@ -1,96 +0,0 @@ -# LLM Agent Instructions - -This document contains instructions and documentation references for AI assistants working with this codebase. - -> **๐Ÿ“– Important**: Before working on this project, read [PHILOSOPHY.md](PHILOSOPHY.md) to understand the system vision, architectural patterns, and design goals. All development should work towards realizing those patterns. -# AGENTS.md - -> **Start every session by reading this file.** -> This file outlines the operational protocols, coding standards, and architectural decisions for this Flutter project. - -## 1. Agent Operational Protocols - -### ๐Ÿง  Work Patterns (Plan-Act-Reflect) -* **Plan:** Before writing code, briefly outline your plan. Identify which files you will touch and what the side effects might be. -* **Act:** Execute the changes in small, atomic steps. -* **Reflect:** After coding, verify your work. Did you break existing tests? Did you add new tests? - -### ๐ŸŒ Internal Service Access -* **git.schweitz.net**: Access via `http://localhost:3002` (direct Gitea) to bypass Authentik SSO - * Example: `curl http://localhost:3002/jpmschweitzer/library-desk/raw/branch/main/README.md` - * Public repos are readable without authentication - * Related repos: , `core-api`, `tatlock`, `library-desk`, `scheduler`, `portainer-core` - -### ๐Ÿณ Deployment & Infrastructure - -**โš ๏ธ IMPORTANT: Service Port Reference** -| Service | LAN Port | External URL | Notes | -|---------|----------|--------------|-------| -| **Core API** | 8083 | `api.schweitz.net` | FastAPI backend for this UI | -| **Tatlock API** | 8000 | `tatlock.schweitz.net` | Legacy Python API (Ollama proxy) | -| **Tatlock UI** | 9999 | `home.schweitz.net` | This Flutter app | - -* **Full stack documentation**: Available in the `portainer-core` repo - * Access: `curl http://192.168.86.149:3002/jpmschweitzer/portainer-core/raw/branch/main/CONTAINERS.md` - * Contains: All service ports, URLs, Redis DB allocations, external domains -* **Health checks**: - * Core API: `curl http://192.168.86.149:8083/health` - * Tatlock API: `curl http://192.168.86.149:8000/health` - -### ๐Ÿ›ก๏ธ Git Discipline -* **Commit Messages:** Use the [Conventional Commits](https://www.conventionalcommits.org/) format. - * `feat: add user login endpoint` - * `fix: resolve database connection timeout` - * `refactor: split monolith dependency file` -* **Atomic Commits:** Keep commits small. One logical change = one commit. -* **Version Tagging:** Every version increment (major.minor.patch, not build count) must have a corresponding git tag. - * Format: `v{major}.{minor}.{patch}` (e.g., `v0.3.0`) - * Tag after updating `pubspec.yaml` version and CHANGELOG - * Push tags with `git push --tags` - -### ๐Ÿš€ Release Procedure - -This project uses version-tag-based CI/CD. Releases trigger automated Docker builds and deployments. - -**Release Steps:** - -1. Update version in `pubspec.yaml` (bump major.minor.patch, not build number) -2. Update `CHANGELOG.md` with changes under `## [x.x.x] - YYYY-MM-DD` -3. Commit changes: `git commit -m "chore: release vX.X.X"` -4. Create git tag: `git tag vX.X.X` -5. Push with tags: `git push origin main --tags` - -CI/CD auto-triggers when a tag starting with `v` is pushed. - -**What happens on release:** - -* Gitea CI builds Flutter web app in Docker -* Image pushed to `git.schweitz.net/jpmschweitzer/tatlock-ui:latest` and `:vX.X.X` -* Watchtower detects new image and auto-updates running container -* App available at `http://tower:8092` (and eventually `home.schweitz.net`) - -**Rollback:** - -* In Portainer, update image tag to previous version (e.g., `:v0.2.0`) -* Or: `docker pull git.schweitz.net/jpmschweitzer/tatlock-ui:v0.2.0` - -### ๐Ÿงช Testing Requirements - -* **Always add tests for new code before committing.** No exceptions. -* Tests should cover the happy path and key edge cases. -* Run `flutter test` before committing to ensure all tests pass. -* For widgets: use widget tests. For business logic: use unit tests. -* Code coverage should not decrease with new commits. - -### ๐Ÿ“ Changelog Maintenance - -* **Update `CHANGELOG.md`** with every user-facing change. -* Format: `## [Unreleased] - YYYY-MM-DD` followed by `### Added`, `### Changed`, or `### Fixed`. - -### ๐ŸŽจ UI Patterns (MUST READ BEFORE CHANGES) - -* **Before modifying the widget tree**, read `docs/UI_LAYOUT.md` to understand established patterns. -* Investigate existing implementations in the codebase before creating new components. -* **DO NOT** reinvent wheels - check if shared components already exist in `lib/shared/components/`. -* Look at similar features for reference patterns (e.g., how other list views, forms, or CRUD screens are built). -* Deviating from established patterns creates inconsistency and technical debt. diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..ef48929 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,163 @@ +# CLAUDE.md โ€” tatlock-ui + +Flutter/Dart frontend for the homelab โ€” the dashboard at `home.schweitz.net`. Riverpod state, +Material 3, Go-Router. Built as a **Flutter web** app, served as compiled static assets by nginx +in the `tatlock-ui` container. `pubspec.yaml` version **1.7.1+1**, package name `tatlock_ui`, +Dart SDK `^3.10.4`. + +It is a **client**, not a service. It talks to core-api (:8083) and tatlock (:8000); it exposes +no API of its own and has no `/openapi.json`. + +## Read first + +- **[PHILOSOPHY.md](PHILOSOPHY.md)** โ€” the system vision and the architectural patterns all work + should move toward. The previous AGENTS.md made this a mandatory pre-work read and that + requirement is carried forward deliberately. +- **[docs/UI_LAYOUT.md](docs/UI_LAYOUT.md)** โ€” **read before touching the widget tree.** Check + `lib/shared/components/` for an existing component before building a new one, and look at how a + comparable feature already does it. Deviating from the established patterns is the main source + of drift here. +- `docs/` also holds `ARCHITECTURE.md`, `API_INTEGRATION.md`, `TESTING.md`, `THEMING.md`, + `DATAGRID.md`, `DEPLOYMENT.md`. + +## Ports and where it runs + +| | | +|---|---| +| Container | `tatlock-ui`, `127.0.0.1:9999 -> 80` (nginx serving the web build) | +| External | `home.schweitz.net` | +| Backends | core-api `:8083`, tatlock `:8000` | + +The old AGENTS.md said the app is "available at `http://tower:8092`". **That is stale** โ€” the +published port is 9999, verified against `docker ps` on 2026-08-09. + +It also pointed at `portainer-core` for full-stack documentation. **`portainer-core` is +deprecated** and must not be used as a source of infra facts; it was merged into +`system-admin-toj/containers/`. The live inventory is `CONTAINERS.md` there. + +The Gitea SSO-bypass trick is real and still works: `http://localhost:3002` reaches Gitea +directly, verified returning `{"version":"1.27.1"}`. Useful for reading a sibling repo's raw +files without going through Authentik. + +## Layout + +`lib/main.dart` โ†’ `lib/app.dart`; `lib/core/` (api, auth, config, error, providers, semantics, +theme), `lib/features//` (control_room, front_hall, media_room, parlor, security, +settings), `lib/routing/`, `lib/shared/` (components, layouts, theme, widgets). 132 Dart files +under `lib/`, 24 test files. + +## Establishing what is live โ€” and the trap in it + +There is no `sys.modules` here and nothing to `docker exec` into: the container holds compiled +assets, not source. The Dart analogue is a transitive walk of `import`/`export`/`part` +directives from `lib/main.dart`, resolving `package:tatlock_ui/โ€ฆ` to `lib/โ€ฆ`. Run 2026-08-09: +124 of 132 files reachable, 8 not. + +**Do not read that as a delete list. Five of the eight are the code that actually runs in +production.** They are conditional-import targets: + +```dart +import 'api_client_native.dart' if (dart.library.html) 'api_client_web.dart'; +``` + +A naive walk captures the *first* string and misses the branch. Since this app ships as Flutter +**web**, the `_web.dart` half is the live one and the `_stub`/`_native` half is the dormant one โ€” +the exact inverse of what the reachability count suggests. The five: `api_client_web.dart`, +`web_utils_web.dart`, `url_strategy_web.dart`, `url_state_web.dart`, +`iframe_view_web.dart`. Find them all with `grep -rn "if (dart.library" lib/`. + +That leaves three genuinely unreferenced files, and they are **not** all the same thing: + +| File | Status | +|---|---| +| `lib/core/auth/permission_gate.dart` | no reference anywhere in `lib/` or `test/` | +| `lib/core/semantics/semantic_widget.dart` | no reference anywhere in `lib/` or `test/` | +| `lib/features/control_room/stacks/data/models/stack_model.dart` | **referenced only by its own test** | + +The third is the interesting one: `stack_model_test.dart` imports and exercises it, so the suite +is green and gives confidence about a model the app never uses. A passing test is not evidence a +thing is wired in. + +Before deleting any of the three, check whether it is intended groundwork rather than debris โ€” +`TODO_AUTH_REFACTOR.md` describes an unimplemented auth redesign, and `permission_gate.dart` sits +squarely in that area. Neither that file nor `PLAN.md` mentions it by name, so its status is +**undetermined**, not dead. Ask before removing. + +## Tooling + +`flutter` and `dart` resolve from `/snap/bin`, which **is** on the non-interactive `PATH` โ€” so +bare commands work here (unlike `pql`, which needs its absolute path). + +```bash +flutter pub get +flutter test # 24 test files +flutter analyze # static analysis; analysis_options.yaml at the repo root +flutter build web --release +``` + +**Always add tests for new code before committing** โ€” happy path plus key edge cases, widget +tests for widgets, unit tests for logic. Coverage should not decrease. Carried over from the +previous AGENTS.md, which stated it as "no exceptions". + +Note there is **no CI test gate**: `.gitea/workflows/build.yml` triggers only on `v*` tag push +and goes straight to build and release. `flutter test` runs locally or not at all. + +## Work tracking + +Work lives in **pql**, not a markdown TODO. **This repo's vault is standalone** โ€” its tickets and +its internal decisions live here in `.pql/` and `governance/`, and travel with a clone, because +`.pql/changelog/` is committed and replayed by the git hooks (D-15). The databases are gitignored +and rebuildable with `pql plan rebuild`. + +`pql` is **not** on the non-interactive `PATH` โ€” invoke it as `/home/jpmschweitzer/.local/bin/pql`. +From inside this repo no `--vault` is needed: pql anchors at the nearest `.git/` ancestor, which +is this repo. + +```bash +/home/jpmschweitzer/.local/bin/pql ticket list # this repo's open work +/home/jpmschweitzer/.local/bin/pql plan whatsnext # next unblocked item, with context +/home/jpmschweitzer/.local/bin/pql decisions list # this repo's own decisions +``` + +Stack-level decisions that constrain this app live in the **workspace** vault and need the flag: + +```bash +/home/jpmschweitzer/.local/bin/pql --vault /mnt/media/Projects decisions list --domain tatlock-ui +``` + +Note `ticket new --decision D-N` resolves ids within **one** vault, so a ticket here cannot link +to a workspace decision. Cite the id in the ticket body instead. + +`PLAN.md` and `TODO_AUTH_REFACTOR.md` predate this convention. Treat them as research notes; +new work goes in pql. + +## Git + +- **History is linear โ€” no merge commits.** Work on `main`, or a short-lived branch that is + fast-forwarded and deleted. +- **Conventional Commits**: `feat:`, `fix:`, `refactor:`, `docs:`, `chore:`. +- **Stage explicitly. Never `git add -A`** โ€” denied by policy, and it sweeps in whatever else is + dirty. +- **Every version increment gets a tag** โ€” `vX.Y.Z`, on the `major.minor.patch` part, not the + build number. +- Update `CHANGELOG.md` for every user-facing change. + +**`.claude/settings.local.json` is currently tracked in git here.** Contents are benign โ€” a +four-entry permission allow list, no `env` block, no secrets โ€” but it is machine-local state that +should not be shared. There is now a `.gitignore` rule for it, and **that rule is inert**: git +applies ignore rules only to untracked paths, so edits still show in `git status` and still get +committed. It starts working only after `git rm --cached .claude/settings.local.json`, which is a +history decision and was deliberately left out of normalization. + +Consequence for checking: plain `git check-ignore` prints nothing for this path โ€” it consults the +index โ€” which looks identical to "no rule exists". Use `--no-index` to test the rule itself. + +## Releasing + +1. Bump `version` in `pubspec.yaml` (the `major.minor.patch` part). +2. Move `[Unreleased]` entries into a dated section in `CHANGELOG.md`. +3. Stage by name, commit, tag `vX.Y.Z`, `git push origin main --tags`. +4. Gitea CI builds the web app in Docker, pushes `:latest` and `:vX.Y.Z`; Watchtower deploys. +5. Verify at `home.schweitz.net`, or `curl -I http://localhost:9999`. + +**Rollback:** in Portainer, point the image tag at the previous version. diff --git a/PHILOSOPHY.md b/PHILOSOPHY.md index c544e5f..36c8aa5 100644 --- a/PHILOSOPHY.md +++ b/PHILOSOPHY.md @@ -109,7 +109,7 @@ The butler (backend) runs the household. The UI opens the door. **Related Documents**: - **README.md**: Project setup and operational details -- **AGENTS.md**: LLM agent development guidelines +- **CLAUDE.md**: LLM agent development guidelines - **PLAN.md**: Implementation roadmap and phases ---