@ -114,3 +114,30 @@ Append-only role handoff log. Each role adds one entry when its step is complete
- Status: done. Commit message read from the T-005 next_task entry.
- All five board tasks are done; T-005 is the only task completed in this invocation and receives its own Conventional Commit.
### T-006 — plan — 2026-10-13T00:00:00Z
| Field | Value |
|-------|-------|
| Agent | claude |
| Summary | Planned Android home-screen support: cartoon washing-machine icon set, web manifest "Home Lanundry", embedded routes, head tags, README section and tests; no service worker or TLS |
Goal: implement `ROADMAP.md` Priority 3 — show the "Laundry done" status text in green (`#1a7f37`). T-001..T-004 are done and unchanged in behavior.
Goal: implement `ROADMAP.md` Priority 4 — make the page addable to an Android home screen as "Home Lanundry" with a cartoon washing-machine icon. T-001..T-005 are done and unchanged in behavior.
## Scope
- CSS-only change in the page plus docs and a test. No backend, JSON contract, status rule or message text changes.
- Out of scope: dark mode, other state colors, layout changes.
## T-005 — Green "Laundry done" text
Background: `internal/web/static/index.html` sets the `#status` element's class to `data.state`, so the done state gets class `done` (currently unstyled, i.e. default `#111`).
### Files to change
1. `internal/web/handler_test.go` (tests first): in `TestPageAndRouting`, add `.done { color: #1a7f37; }` (match the exact rule text used in the page) to the list of expected page substrings. Run `go test ./internal/web` and confirm it fails before the page change.
2. `internal/web/static/index.html`: add `.done { color: #1a7f37; }` next to the existing `.running` / `.waiting` rules (around line 12).
3. `README.md`: in the status table (line ~26) change the "Laundry done" color from "Default text color" to "Green". Check the rest of the README for any other mention of the done color and keep it consistent.
4. `ROADMAP.md`: already updated by the planner (Priority 1 table row and Priority 3); no further edit needed.
- New static assets (manifest and icons), embedded routes, three `<head>` tags, tests, README.
- Out of scope: service worker, offline mode, TLS, install prompt UI, any change to status rule, JSON contract, SSE, page layout or the `<title>`.
- Transport stays plain HTTP, so Chrome offers "Add to Home screen" (shortcut), not "Install app". This is accepted; document it.
## T-006 — Home screen icon and web manifest
### Design decisions
- Assets live in `internal/web/static/` and are embedded with `go:embed` (an `embed.FS` over a fixed file list, or individual `[]byte` vars). No new dependencies.
- Routes (all `GET`, exact paths, registered on the existing mux):
- Static assets use `Cache-Control: no-cache` (revalidate); the existing `no-store` on page/API stays.
- Manifest content: `name` and `short_name` "Home Lanundry", `start_url``/`, `scope``/`, `display``standalone`, `theme_color` and `background_color``#ffffff`, `icons` = 192 and 512 (`purpose: any`) plus maskable 512 (`purpose: maskable`).
- Icon art: flat cartoon front-loading washing machine — light-blue rounded-square background, white/pastel body, round porthole with blue water and a few bubbles, two or three control knobs and a small display on top. For the maskable variant, extend the background full-bleed and keep all artwork inside the central 80% safe zone.
- Table test for each new route: 200, expected `Content-Type`, non-empty body; PNG routes start with the PNG signature; `POST` returns 405.
- Manifest test: decode JSON; assert `name`/`short_name` == "Home Lanundry", `display` == "standalone", `start_url` == "/", and that every `icons[].src` resolves to a route that returns 200 and has the declared `sizes` (decode PNG config) and `type`; one icon has `purpose` "maskable".
- Extend `TestPageAndRouting` expected substrings with `rel="manifest"`, `name="theme-color"`, `rel="apple-touch-icon"`.
3. `internal/web/static/icon-192.png`, `icon-512.png`, `icon-maskable-512.png`, `apple-touch-icon.png` (new): rendered from the SVG (full-bleed variant for maskable). Suggested tools: headless `google-chrome --screenshot` or Pillow; keep the generation script out of the repo, or add it as `internal/web/static/README`-free `scripts/` only if small — implementer's choice, but the committed SVG must be the source of truth and PNGs must visually match it. Visually check the result (view the PNGs, including a circle-crop check of the maskable one).
4. `internal/web/static/manifest.webmanifest` (new): content as above.
5. `internal/web/handler.go`: embed the new assets, register the routes with correct content types and cache header, update the package/handler doc comment to mention the manifest and icons.
6. `internal/web/static/index.html`: add to `<head>`: `<link rel="manifest" href="/manifest.webmanifest">`, `<meta name="theme-color" content="#ffffff">`, `<link rel="apple-touch-icon" href="/apple-touch-icon.png">`, `<link rel="icon" href="/icons/icon.svg" type="image/svg+xml">`. Do not change title, layout or script.
7. `Dockerfile`: no change expected (it already copies `internal`); verify the build still embeds the new files (`.dockerignore` must not exclude `*.png`/`*.svg`/`*.webmanifest` — check and fix if it does).
8. `README.md`: new "Add to Android home screen" subsection (before "Limitations"): open the page in Chrome on Android, menu → "Add to Home screen"/"Install app" if offered, name "Home Lanundry". State the plain-HTTP limitation: without HTTPS Chrome only offers a home-screen shortcut with the icon, not a full install, and there is no offline mode. Add the new routes (`/manifest.webmanifest`, `/icons/*`, `/apple-touch-icon.png`) to the endpoint documentation. Do not touch the AI Workflow section.
9. `ROADMAP.md`: already updated by the planner; no further edit.
### Validation
- `go fmt ./...`, `go vet ./...`, `go test ./...`.
- Optional smoke: run the service, force a done state, and confirm the text renders green on a 360px viewport.
- `go fmt ./...`, `go vet ./...`, `go test ./...` (plus `-race` on `./internal/web`).
- Smoke: run the service, `curl -sI` each new route for status and content type; open the page in Chrome DevTools (Application → Manifest) at a 360px viewport and confirm no manifest or icon errors and a correct icon preview.
- Docker build if available (confirm assets are embedded).
### Commit message
`feat(web): show "Laundry done" status text in green`
`feat(web): add Home Lanundry home-screen icon and web manifest`
- Environment retry during implementation: sandbox blocked the Go build-cache write; approved rerun reached the expected failing test. No review retries or weakened assertions.
- Workflow limitation: `aide pr --dry-run` refuses the current `main` branch. Kept the current branch as instructed; no PR created.
- Verdict: PASS, approved for the per-task commit under `all_task`.
## Task: T-006
### Review Round 1 — 2026-10-13T09:08:01Z
- Findings: no required fixes or semantic concerns. The complete staged/unstaged diff matches the manifest, exact asset routes, head tags, icon design, and documentation plan. Status logic, API JSON, SSE, page title/layout/script, and the README AI Workflow section are unchanged.
- Tests first: `go test ./internal/web` failed on all six missing routes, missing page tags, and missing manifest before implementation; all assertions passed after implementation and were retained.
- Validation: `go fmt ./...`, `go vet ./...`, `go test ./...`, `go test -race ./internal/web`, and staged/unstaged diff checks PASS.
- Assets: PNGs rendered from the committed SVG using headless Chrome; inspected all PNGs plus a circular mask preview. Pixel checks confirm the maskable icon is fully opaque and all foreground artwork is inside the central safe circle. All declared PNG dimensions decode correctly.
- Packaging/browser smoke: Docker build and temporary `node /tmp/mqtt-washingmachine-t006-smoke.mjs` PASS. The scratch image serves byte-identical embedded assets; curl HEAD, GET, content types, cache headers, and POST 405 pass for all six routes. Chrome at 360px reports no DevTools manifest errors and decodes every icon at its declared size. Page, API, initial SSE event, and clean shutdown pass. Screenshot: `/tmp/mqtt-washingmachine-t006-mobile.png`.
- E2E: NOT_CONFIGURED as a repository command. Temporary Docker/Chrome smoke passed; physical Android home-screen installation was not exercised.
- Environment retries: Docker build metadata and the Git index were read-only in the sandbox; approved reruns passed. No review validation retries or weakened assertions.
- Workflow limitation: `aide pr --dry-run` refuses the current `main` branch. Kept the current branch as instructed; no PR created.
- Verdict: PASS, approved for the per-task commit under `all_task`.
| T-004 | SSE push (`GET /api/events`) so the page refreshes on each valid MQTT message: sensor Hub, `Sampler.OnReceive`, SSE handler with keep-alive and write-deadline override, page EventSource with 60 s polling fallback, README/comment updates, tests first | done | Valid message -> `update` event to all clients; invalid -> none; immediate `update` on connect; keep-alive sent; stream outlives 10 s WriteTimeout; slow subscriber never blocks Receive; disconnects/shutdown clean up; `/api/status` unchanged; README documents endpoint and updated refresh behavior; fmt/vet/test (+race) pass | 2026-10-12T13:18:18Z: fmt/vet/test/race PASS; MQTT/SSE/Chrome smoke PASS; review PASS | none |
| T-005 | Show "Laundry done" status text in green (`.done { color: #1a7f37 }` in index.html), page test first, README status table update | done | Page contains the `.done` green rule (test fails before, passes after); text and status rule unchanged; README table says Green; fmt/vet/test pass | 2026-10-12T13:24:25Z: tests first, fmt/vet/test PASS; review PASS; E2E NOT_CONFIGURED | none |
| T-006 | Home screen support: cartoon washing-machine icon (SVG + 192/512/maskable/apple-touch PNGs), `manifest.webmanifest` ("Home Lanundry", standalone), embedded routes with correct content types, head tags in index.html, README section, tests first | done | New routes return 200 with correct content types and valid PNG sizes; manifest JSON has name/short_name "Home Lanundry", display standalone, start_url /, 192/512 and maskable icons that all resolve; page has manifest/theme-color/apple-touch-icon tags; page layout, title, status rule, JSON and SSE unchanged; README documents the home-screen steps and plain-HTTP limitation; fmt/vet/test pass | 2026-10-13T09:08:01Z: tests first; fmt/vet/test/web-race PASS; Docker/Chrome/icon smoke PASS; review PASS | none |
@ -69,3 +69,23 @@ Objective: show the "Laundry done" status text in green.
### Open
- None.
## Priority 4
Objective: let the page be added to an Android phone's home screen with a cartoon washing-machine icon, named "Home Lanundry".
### Decided
- Mechanism: web app manifest (`manifest.webmanifest`) plus icons, served by the Go backend and embedded in the binary like `index.html`.
- Name: "Home Lanundry" (both `name` and `short_name`).
- Icon: cartoon front-loading washing machine (round porthole with blue water and bubbles, a few control knobs, flat pastel style) on a light-blue rounded background. Delivered as SVG source plus PNG at 192x192 and 512x512, and a maskable variant with the drawing inside the safe zone.
- Manifest: `display: standalone`, white `theme_color` and `background_color`, `start_url``/`.
- Page: add `<link rel="manifest">`, `theme-color` meta and `apple-touch-icon`.
- Transport: plain HTTP on the home LAN, no TLS (decided out of scope). Chrome on Android therefore offers "Add to Home screen" (a shortcut with the icon), not a full "Install app". This is accepted.
- No service worker and no offline support.
- Status rule, JSON contract, SSE behavior and page layout stay unchanged.
- Docs: README gets a short section on adding the page to the home screen and the plain-HTTP limitation; handler tests cover the new routes and content types.