diff options
| author | cirakg <cirakg@cirakg.xyz> | 2026-10-18 21:10:37 +0200 |
|---|---|---|
| committer | cirakg <cirakg@cirakg.xyz> | 2026-10-18 21:10:37 +0200 |
| commit | b70cde45b67e1f6bb65d598d25c1a5184bdab520 (patch) | |
| tree | 2460f861600912b4ab18eb7f672ab559729dd5cf | |
| parent | 773df56883b9961b607a1994920f5ef5299423b8 (diff) | |
docs: add developer, testing and performance guides; record M0 resultsv0.1.0
- DEVELOPER_GUIDE: workflow, how the tooling works (verified simulator
behaviour), conventions and how to add devices.
- TESTING and PERFORMANCE: test layers, M0 results and measured numbers.
- FEASIBILITY: what M0 verified (watchdog 120k/240k, 10 MB storage in
device profiles, unreliable monkeydo exit code, icon sizes).
- README status, new make targets and layout; CHANGELOG 0.1.0.
- Author/Summary headers on every document, including the original brief.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
| -rw-r--r-- | CHANGELOG.md | 36 | ||||
| -rw-r--r-- | Garmin OTP Prompt.md | 4 | ||||
| -rw-r--r-- | README.md | 13 | ||||
| -rw-r--r-- | docs/DEVELOPER_GUIDE.md | 91 | ||||
| -rw-r--r-- | docs/FEASIBILITY.md | 16 | ||||
| -rw-r--r-- | docs/PERFORMANCE.md | 39 | ||||
| -rw-r--r-- | docs/TESTING.md | 47 |
7 files changed, 238 insertions, 8 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index 6d7f149..acc2c9d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,16 +1,40 @@ +<!-- + Author: cirakg & claude ai + Summary: Notable changes per version, in Keep a Changelog format with semantic versioning. +--> # Changelog All notable changes to this project are documented here. The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and the project uses [Semantic Versioning](https://semver.org/spec/v2.0.0.html). Versions stay at -0.x until milestone M8. +0.x until milestone M8; each milestone raises the minor version. ## [Unreleased] +## [0.1.0] - 2026-10-18 + +Milestone M0: scaffolding. Not useful on a watch yet; the app shows only its +name and version. + ### Added -- Project plan (`README.md`) and Phase 0 feasibility findings (`docs/FEASIBILITY.md`). -- M0 scaffolding: Docker development and documentation images with pinned - versions, a host `Makefile` that runs every step in Docker, interactive - Connect IQ SDK setup, developer key generation, headless simulator test - runner, and a minimal watch app with a smoke test. +- Project plan (`README.md`), Phase 0 feasibility findings with what M0 + verified (`docs/FEASIBILITY.md`), developer guide, testing and performance + documents. +- Docker images with pinned versions: `garmin-otp/dev` (Ubuntu 22.04 snapshot, + Java 17, headless simulator stack, hash-locked Python tools, + checksum-verified SDK manager CLI) and `garmin-otp/docs` (Markdown lint and + Mermaid rendering). +- `Makefile` running every step in hardened containers: no capabilities, + read-only root filesystem, only the project folder mounted, no network + outside image builds and `make sdk`, serial even under `make -j`. +- `make sdk` (agreements, login, SDK 9.2.0, device files for 72 watches, + token deleted afterwards) and `make key` (RSA-4096 developer key). +- Build pipeline with strict type checking and warnings as errors, per-size + launcher icons for all 72 products, unit tests in the headless simulator on + four devices, golden-image screenshot tests, start-up and memory budgets, and + the signed `.iq` package. +- Python tools with 44 tests: test-log judge, screenshot checker, budget + checker, product rule checker, Markdown link checker, project linter (code + rules and the Author/Summary header rule) and icon generator. +- A minimal watch app (home screen with name and version) and a smoke test. diff --git a/Garmin OTP Prompt.md b/Garmin OTP Prompt.md index 3121bb6..99b06c8 100644 --- a/Garmin OTP Prompt.md +++ b/Garmin OTP Prompt.md @@ -1,3 +1,7 @@ +<!-- + Author: cirakg & claude ai + Summary: The original project brief (master prompt) this project is built from, kept as written. +--> # Prompt: Garmin Connect IQ OTP Authenticator and Code Wallet Copy everything below the line into your coding model (Claude Code works best, since it can run Docker and files). @@ -1,8 +1,12 @@ +<!-- + Author: cirakg & claude ai + Summary: Project overview, design and build plan for the OTP Wallet Connect IQ app. +--> # OTP Wallet for Connect IQ A Garmin Connect IQ watch app that shows one-time passwords (TOTP, HOTP, and optional Steam Guard codes) and stores barcodes and QR codes (loyalty cards, tickets) to show at a scanner. Everything lives encrypted on the watch. There is no cloud, no backend and no analytics, and the app requests no permissions, so it cannot use the network. -> **Status: Phase 1 (plan), waiting for your approval.** Nothing is built yet. This README is the design and the build plan. The Phase 0 research, with sources, is in [docs/FEASIBILITY.md](docs/FEASIBILITY.md). +> **Status: M0 (scaffolding) done.** The plan below was approved on 2026-10-18. The Docker toolchain, the build, test, screenshot, performance and packaging pipeline, and a placeholder app are in place and pass `make ci`. Next: M1, the settings spike (needs your phone and watch), alongside M2, crypto and OTP. The Phase 0 research, with sources, is in [docs/FEASIBILITY.md](docs/FEASIBILITY.md); the daily workflow is in [docs/DEVELOPER_GUIDE.md](docs/DEVELOPER_GUIDE.md). "OTP Wallet" is a working title. @@ -719,6 +723,8 @@ The Garmin SDK and device files are never baked into an image (Garmin's licence) | `make test` | Unit tests in the headless simulator on the 3 test devices | no | | `make sim` | Run the app in the simulator; view it at http://127.0.0.1:6080/vnc.html | localhost only | | `make screenshots` | UI screenshot tests and barcode decoding from screenshots | no | +| `make screenshots-approve` | Save the current screens as the approved images, after you've reviewed them | no | +| `make icons` | Regenerate the per-size launcher icons after the product list changes | no | | `make perf` | Performance measurements against the budgets | no | | `make memtest` | Peak memory on Forerunner 255S, app and glance | no | | `make lint` | Strict type check (`-l 3`) with warnings as errors, project lint rules, settings XML schema check, Python lint for the tools | no | @@ -1019,13 +1025,16 @@ The simulator runs on your PC's CPU, so its timings don't represent a watch. In │ ├── Dockerfile garmin-otp/dev │ ├── docs.Dockerfile garmin-otp/docs │ └── scripts/ SDK setup, headless simulator, test runner, VNC +├── icons.jungle generated: each product's launcher-icon folder ├── source/ │ ├── app/ crypto/ otp/ codes/ storage/ import/ settings/ ui/ util/ ├── test/ Monkey C (:test) modules, mirroring source/ -│ └── data/ generated vectors and fake fixtures +│ ├── data/ generated vectors and fake fixtures +│ └── golden/ approved simulator screenshots ├── resources/ │ ├── settings/ properties.xml, settings.xml │ ├── strings/ drawables/ fonts/ +├── resources-icons/ generated: launcher icon at each size the products need ├── tools/ Python: vector generation, reference encoders, decoding, screenshots, reports, lint ├── docs/ └── .cache/ build/ git-ignored: SDK, device files, keys, outputs diff --git a/docs/DEVELOPER_GUIDE.md b/docs/DEVELOPER_GUIDE.md new file mode 100644 index 0000000..e6f53a9 --- /dev/null +++ b/docs/DEVELOPER_GUIDE.md @@ -0,0 +1,91 @@ +<!-- + Author: cirakg & claude ai + Summary: How to build, test and debug OTP Wallet in Docker, how the tooling works, and the coding conventions. +--> +# Developer guide + +Everything runs in Docker through the `Makefile`; the host needs only Docker and make. This guide covers the daily workflow, how the tooling works underneath, and the conventions every change follows. The design and the build plan are in the [README](../README.md). + +## First-time setup + +1. Docker access: your user must be in the `docker` group (log out and in after joining, or run `newgrp docker` in a terminal). +2. `make image` builds the development image (about 1.1 GB; it needs the network). +3. `make sdk` is interactive: you accept Garmin's agreements, log in, and the SDK (9.2.0) plus the device files of all 72 products download into `.cache/home` (about 1.1 GB). The login token is deleted at the end. +4. `make key` creates the developer key in `.cache/keys/`. Back that folder up; see [README 15.6](../README.md#156-developer-key). + +## Daily workflow + +| Task | Command | +| --- | --- | +| Build for one device | `make build DEVICE=fr70` | +| Run the app and watch it in your browser | `make sim DEVICE=fr70`, then open http://127.0.0.1:6080/vnc.html | +| Unit tests (Python tools and Monkey C, 4 devices) | `make test` | +| All static checks | `make lint` | +| Screenshots against the approved images | `make screenshots` | +| Accept changed screens after reviewing build/screenshots/ | `make screenshots-approve` | +| Start-up time and memory budgets | `make perf`, `make memtest` | +| Signed package for the Store beta | `make package` | +| Everything, in order | `make ci` | +| A shell in the container | `make shell` | + +Use other devices with `TEST_DEVICES="fr70 venu3"` on any target that takes a device list. Every target runs serially, even under `make -j`. + +## Repository layout + +| Path | Content | +| --- | --- | +| `source/` | The watch app (Monkey C), one folder per module (README section 5) | +| `test/` | Monkey C unit tests (`(:test)` functions), compiled only into test builds | +| `test/golden/` | Approved simulator screenshots, one per test device | +| `resources/` | Strings, the default launcher icon and, from M7, the settings | +| `resources-icons/`, `icons.jungle` | Launcher icons at each size the products need, generated by `make icons` | +| `manifest.xml`, `monkey.jungle`, `test.jungle` | App manifest and build configuration | +| `docker/` | Images (`Dockerfile`, `docs.Dockerfile`), hash-locked dependencies, and the container scripts | +| `tools/` | Python tools (test-log judge, screenshot checker, linters, icon generator) and their tests | +| `docs/` | This guide and the other documents | +| `.cache/`, `build/` | Git-ignored: SDK, device files, key; build outputs | + +## How the tooling works + +### Builds + +`docker/scripts/build-app.sh` compiles one device in one mode: + +- `debug`: what `make build`, `make sim`, `make perf` and `make screenshots` use. It contains the `(:debug)` diagnostics. +- `release`: `monkeyc -r` removes every `(:debug)` declaration; used by `make package`. +- `test`: adds `test.jungle` and `--unit-test`. + +Every build uses strict type checking (`-l 3`) and fails on any compiler warning. The launcher icon is the classic source of warnings: the compiler warns when it has to scale an icon, so every product gets an icon of exactly its size through `icons.jungle`. After adding a product to the manifest, run `make sdk` (device files) and then `make icons`. + +### The headless simulator + +The simulator is a GTK program. The scripts start it on a virtual display (Xvfb on `:99`) with the openbox window manager, so its window can be found for screenshots. Behaviour verified with SDK 9.2.0: + +- It accepts `monkeydo` on TCP port 1234; the scripts wait until that port listens (read from `/proc/net/tcp`). +- It keeps a single-instance lock file, `~/Sim-dev`, holding its process ID. That file survives in `.cache/home`, and a later container often reuses the process ID for something else, so the simulator would believe it's already running. `ciq_start_simulator` deletes the stale lock first. +- `monkeydo` exits with 1 even when every unit test passed, so `tools/ciq_test_report.py` judges the console output instead. +- For a normal (non-test) run, `monkeydo` blocks while the app runs and streams the app's `System.println` output. Quotes in that output arrive escaped, which is why the diagnostics use plain `key=value` pairs. + +### Screenshots + +`tools/sim_screenshot.py` doesn't assume where the watch is drawn in the simulator window. It searches for the device drawing (named in the device's `simulator.json`) in the window capture, comparing only the bezel, because the app draws inside the display area. The match is near-exact (mean difference below 0.3 out of 255); the drawing sits at offset (1, 26) because of the window border and the menu bar. The display rectangle from `simulator.json` is then cut out, round screens get their corners blacked out, and the result is compared with `test/golden/<device>.png`. + +### Performance and memory + +Debug builds print `DIAG first_frame startup_ms=... mem_used=... mem_total=...` after their first frame (`source/util/Diag.mc`). `docker/scripts/app-diag.sh` adds a `HARNESS first_frame wall_ms=...` line, the wall-clock time from launching `monkeydo` until that first frame. `tools/check_budgets.py` compares both with `tools/budgets.json`. See [PERFORMANCE.md](PERFORMANCE.md) for the numbers and their limits. + +## Conventions + +- **Headers**: every file starts with `Author: cirakg & claude ai` and a one-line `Summary:` in its comment syntax. JSON files use `author`/`_author` and `description`/`_summary` keys, and PNG images use Author and Description text chunks. Only `docker/docs/package-lock.json`, which npm rewrites, is exempt. `make lint` enforces this (rule HDR001). +- **Comments**: every module, class and non-trivial function explains its purpose, inputs, outputs, errors and security implications. +- **No logging**: `System.println` is allowed only in `source/util/Diag.mc`, and only in `(:debug)` code. Secrets, codes and entry names never go to a log. +- **No network**: `manifest.xml` requests no permissions and no code uses `Toybox.Communications`. `make lint` enforces both. +- **Secrets**: no real secrets anywhere in the repository; test data is fake and says so. +- **Small functions, named constants**, strict typing in Monkey C (`-l 3`) and Python (`mypy --strict`), and warnings treated as errors in the compiler, pytest and the linters. +- **Commits**: [Conventional Commits](https://www.conventionalcommits.org/) on `main`, one finished and verified unit per commit, with `CHANGELOG.md` updated. + +## Adding things + +- **A device**: add `<iq:product id="..."/>` to `manifest.xml`, then `make sdk` (downloads its files), `make icons` and `make products` (checks it meets the target rule: a watch, API 5.0 or newer, at least 512 KB). +- **A test device**: add it to `TEST_DEVICES` in the `Makefile`, run `make screenshots`, review `build/screenshots/<device>.png` and run `make screenshots-approve`. +- **A barcode symbology** (from M6): one encoder module under `source/codes/`, its validator, unit tests with reference patterns, and the checks in [README 12.4](../README.md#124-how-each-symbology-is-proven-scannable). diff --git a/docs/FEASIBILITY.md b/docs/FEASIBILITY.md index 255f195..cb3431a 100644 --- a/docs/FEASIBILITY.md +++ b/docs/FEASIBILITY.md @@ -1,3 +1,7 @@ +<!-- + Author: cirakg & claude ai + Summary: Phase 0 research: what the Connect IQ platform allows, with verdicts, decisions and sources. +--> # Feasibility findings (Phase 0) Researched on 2026-10-18 against the Connect IQ SDK 9.2.0 documentation (API reference generated 2026-10-06), Garmin's device reference pages, and, where Garmin's documentation is silent, Garmin developer-forum reports. Forum-only evidence is marked **(forum)**. Nothing has been built yet. Anything marked **to measure** gets measured in the milestones, not estimated. @@ -238,6 +242,18 @@ Rules that apply to every path (detailed in the README): - **Status**: one read-only status field summarises the last import. - **Editing and removing after import**: on the watch (delete, reorder, lock), or by re-importing. +## Verified during M0 (2026-10-18) + +With SDK 9.2.0 and the device files of all 72 products installed: + +- **Headless simulator in Docker: confirmed.** Every library the simulator needs is in the Ubuntu 22.04 image; it runs under Xvfb, and unit tests, screenshots and diagnostics work on all four test devices. +- **`monkeydo` exit code: confirmed unreliable.** It exits with 1 after a fully passing test run, so the test runner judges the console output. +- **Unit-test summary format:** SDK 9.2.0 prints `PASSED (passed=1, failed=0, errors=0)`, not the `failures=` form in Garmin's documentation. The parser accepts both. +- **Watchdog budget (11):** the device files give 120,000 bytecodes per event on the Forerunner 255 and 255S (the 512 KB watches) and 240,000 on every other target. Long work will be chunked for the smaller budget. +- **Storage (4): new evidence.** Every target's simulator profile reports an app storage capacity of 10 MB, far above the "128 KB total" in Garmin's core-topic page. The design keeps the stricter figure until it's measured on your Forerunner 70 in M3. +- **Launcher icons:** the 72 products use eight icon sizes (38 to 70 px). Each gets an exact-size icon, because the compiler warns when it scales one, and warnings fail the build. +- **SDK login (11):** the CLI's browser login can't reach the container (it waits on a random localhost port), so `make sdk` uses email and password; that worked for your account. + ## Decisions Answered on 2026-10-18: diff --git a/docs/PERFORMANCE.md b/docs/PERFORMANCE.md new file mode 100644 index 0000000..2e668c2 --- /dev/null +++ b/docs/PERFORMANCE.md @@ -0,0 +1,39 @@ +<!-- + Author: cirakg & claude ai + Summary: Performance and memory budgets, how they are measured, and the measured numbers per milestone. +--> +# Performance + +The targets are in [README section 18](../README.md#18-performance-budgets). This document records how each number is measured, what it does and doesn't tell you, and the results. + +## How it is measured + +- **In-app start-up** (`startup_ms`): debug builds take `System.getTimer()` when the app object is created and again after the first complete frame, and print the difference (`source/util/Diag.mc`). It doesn't include the time the watch needs to load the program before the app object exists. +- **Launch to first frame** (`first_frame_wall_ms`): `docker/scripts/app-diag.sh` measures the wall-clock time from launching `monkeydo` until the app's first-frame line appears. This includes the Java start of `monkeydo`, sending the program to the simulator and loading it, so it is mostly tool overhead; it's only useful to catch large regressions. +- **Memory** (`mem_used_fraction`): `System.getSystemStats()` after the first frame: used memory divided by the memory the device gives watch apps. +- **On the watch**: the numbers that matter are measured on your Forerunner 70 with a sideloaded debug build that shows them on screen. That arrives with M4; the simulator runs on your PC's CPU and says nothing about watch speed. + +`make perf` and `make memtest` fail when a value exceeds `tools/budgets.json`. + +## Budgets in force + +| Value | Budget | Why | +| --- | --- | --- | +| `startup_ms` | 3000 ms | Placeholder until M4 sets about 2× the measured baseline | +| `first_frame_wall_ms` | 15000 ms | Regression guard only (tool overhead dominates) | +| `mem_used_fraction` | 70 % | Hard limit, leaves room for spikes on the 512 KB watches | + +## Results + +### M0, 2026-10-18 (version 0.1.0, placeholder app) + +Simulator in Docker on your PC (12 cores), debug builds: + +| Device | Watch-app memory | In-app start-up | Launch to first frame | Memory used | +| --- | --- | --- | --- | --- | +| Forerunner 255S | 512 KB | 1 ms | 6.4 s | 1.3 % | +| Venu Sq 2 | 768 KB | 3 ms | 6.5 s | 0.9 % | +| Forerunner 70 | 768 KB | 0 ms | 6.7 s | 0.9 % | +| Forerunner 965 | 768 KB | 6 ms | 6.8 s | 0.9 % | + +The placeholder app draws two lines of text, so these numbers only prove the measuring works. The first meaningful baseline comes with M4 (the real list and code screens). The signed package is 488 KB for all 72 products; a release build for one device is 7 to 15 KB. diff --git a/docs/TESTING.md b/docs/TESTING.md new file mode 100644 index 0000000..b3b5a7b --- /dev/null +++ b/docs/TESTING.md @@ -0,0 +1,47 @@ +<!-- + Author: cirakg & claude ai + Summary: Test strategy, how to run every check in Docker, current results, and the real-watch checklist. +--> +# Testing + +The full test plan is in [README section 17](../README.md#17-test-plan). This document tracks what exists, how to run it, and the latest results. Every check runs in Docker; `make ci` runs them all in order and stops at the first failure. + +## Test layers + +| Layer | Command | What it proves | +| --- | --- | --- | +| Python tool tests | `make test` (first part) | The tools that judge everything else are correct: test-log parser, screenshot checker, budget checker, product rule, link checker, linter, icon generator. Includes Hypothesis fuzzing of the log parser and integration checks against the real docs and sources | +| Monkey C unit tests | `make test` (second part) | Garmin's Run No Evil tests, run in the headless simulator on each test device; judged from the console output, because `monkeydo` always exits with 1 | +| Static checks | `make lint` | Project policy (headers, no logging, no network, no secret-like literals), ruff and strict mypy on the tools, the product rule for all 72 products, and strict debug and release builds with zero compiler warnings | +| Screenshots | `make screenshots` | Each test device's screen matches its approved image in `test/golden/` (at most 0.5 % of pixels may differ by more than 24 levels) | +| Performance and memory | `make perf`, `make memtest` | First-frame time and memory use within `tools/budgets.json` ([PERFORMANCE.md](PERFORMANCE.md)) | +| Packaging | `make package` | The signed `.iq` builds for all 72 products with zero warnings | +| Docs | `make docs` | No broken links or anchors, no Markdown lint issues, every Mermaid diagram renders | + +Test devices (`TEST_DEVICES`): Forerunner 255S (weakest: 218 px round MIP, buttons only, 512 KB), Venu Sq 2 (rectangular AMOLED), Forerunner 70 (your watch) and Forerunner 965 (454 px round AMOLED). + +## Results + +### M0, 2026-10-18 (version 0.1.0) + +`make ci` passed in full: + +- Python tool tests: 44 passed, with warnings treated as errors. +- Monkey C unit tests: the smoke test passed on all four test devices. +- Lint: 0 policy problems; ruff and mypy clean; 72 of 72 products meet the target rule; strict builds without warnings on all four test devices. +- Screenshots: all four devices match their approved images (0.00 % of pixels differ). +- Performance and memory: within budget on all four devices (numbers in [PERFORMANCE.md](PERFORMANCE.md)). +- Package: `otp-wallet-0.1.0.iq` built for all 72 products without warnings. +- Docs: 0 broken links, 0 lint issues, all diagrams render. + +M0 has only the smoke test on the watch side; the real Monkey C suites start with M2 (crypto and OTP test vectors). + +## Fake data only + +Tests never use real secrets or personal data. Fixtures are generated or hand-written and labelled as fake. + +## Real-watch checklist + +Things the simulator can't verify, done by you on the Forerunner 70. The checklist grows with each milestone; it starts with M1. + +- [ ] M1: install the settings spike from the Store beta; record field limits, accepted characters and write-back behaviour in `docs/SETTINGS_SPIKE.md`. |
