diff --git a/.github/ISSUE_TEMPLATE/reproduction-stuck-point.yml b/.github/ISSUE_TEMPLATE/reproduction-stuck-point.yml new file mode 100644 index 0000000..752f558 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/reproduction-stuck-point.yml @@ -0,0 +1,82 @@ +name: Reproduction stuck point +description: >- + A place where the docs alone did not get you through the reproduction checklist + (docs/reproduce/README.md). +title: "[reproduce] " +labels: + - bug +body: + - type: markdown + attributes: + value: >- + Thank you for running the reproduction checklist. File one issue per stuck point. + **Do not paste any secret**: no setup code, key, token, password, KEK, Secret value or + account credential. Write your domain as ``. + - type: dropdown + id: part + attributes: + label: Checklist part + options: + - A. Start clean + - B. The release + - C. Build the Node and the Hub + - D. Flash the Hub and the Node + - E. Deploy the Server at home + - F. Build your own apps + - G. Add the Hub and the Node, calibrate + - H. A Threshold Alert inside the Notification Window + validations: + required: true + - type: input + id: guide + attributes: + label: Guide page and section + description: The page and heading you were following. + placeholder: docs/build/flash.md#first-boot-and-the-setup-code + validations: + required: true + - type: textarea + id: did + attributes: + label: What you did + description: The steps, commands and values you used (secrets removed). + validations: + required: true + - type: textarea + id: expected + attributes: + label: What you expected + description: What the guide or the checklist said should happen. + validations: + required: true + - type: textarea + id: happened + attributes: + label: What happened + description: The output, error or screen you got instead, and any workaround you found. + render: text + validations: + required: true + - type: input + id: release + attributes: + label: Release tag + description: Write none if you have no tag yet. + placeholder: v0.2.0 + validations: + required: false + - type: textarea + id: environment + attributes: + label: Environment + description: >- + Machine OS, home server arch and OS, boards and BOM substitutes, phone model and OS. + validations: + required: false + - type: checkboxes + id: no-secrets + attributes: + label: No secrets + options: + - label: This issue contains no setup code, key, token, password or other credential. + required: true diff --git a/README.md b/README.md index 385b7a1..b124f4e 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,8 @@ and deployment all live in this repository. - [Build guide](docs/build/README.md): build, flash and add your own Node and Hub. - [Deploy guide](docs/deploy/README.md): run Coldframe on your home server, then [build your own apps](docs/build/apps.md). +- [Reproduce Coldframe](docs/reproduce/README.md): the end-to-end checklist for a second person + rebuilding Coldframe from these docs, and how to report where they got stuck. - [Firmware reference](docs/firmware/README.md): learn ESP-NOW, BLE provisioning, eFuse keys and deep sleep from the Node and Hub firmware. - [Hardware design](hardware/README.md): schematics, BOM, wiring, enclosure and probe sealing. diff --git a/_bmad-output/implementation-artifacts/deferred-work.md b/_bmad-output/implementation-artifacts/deferred-work.md index 80bd2b1..7aecf0b 100644 --- a/_bmad-output/implementation-artifacts/deferred-work.md +++ b/_bmad-output/implementation-artifacts/deferred-work.md @@ -946,3 +946,11 @@ source_spec: `spec-10-1-releases-with-one-version-and-firmware-binaries.md` severity: medium reason: dry_run is true for every non-push event, so these steps are skipped on PRs; only a real v* tag exercises them. Owed as an operator action (cut the first release and check it). status: open + +### DW-122: Hub comments still claim the eFuse adapter holds the Hub's only unsafe code, but board/rtc.rs has an unsafe block for the mbedTLS clock hooks. +origin: spec-deferred d5a00e9af471 +location: apps/rs/hub/src/board/efuse.rs:7 +source_spec: `spec-10-4-firmware-learning-reference.md` +severity: low +reason: apps/rs/hub/src/board/efuse.rs module doc line 7 and apps/rs/hub/Cargo.toml:60 say only efuse.rs may use unsafe; apps/rs/hub/src/board/rtc.rs:40-41 has #[allow(unsafe_code)] unsafe { ... }. Pre-existing, not caused by story 10-4; docs/firmware/identity.md already states the code's truth. +status: open diff --git a/_bmad-output/implementation-artifacts/spec-10-5-someone-else-reproduces-coldframe.md b/_bmad-output/implementation-artifacts/spec-10-5-someone-else-reproduces-coldframe.md new file mode 100644 index 0000000..a37f24b --- /dev/null +++ b/_bmad-output/implementation-artifacts/spec-10-5-someone-else-reproduces-coldframe.md @@ -0,0 +1,175 @@ +--- +title: 'Someone else reproduces Coldframe' +type: 'feature' +created: '2026-10-20' +status: 'awaiting-operator' +baseline_revision: 'c8c4a2c3c8e770d5b4c32e78273508c8dc0e222c' +review_loop_iteration: 0 +followup_review_recommended: true +context: + - '{project-root}/_bmad-output/implementation-artifacts/epic-10-context.md' +warnings: [oversized] +deferred: + - summary: >- + docs/check-links.sh ignores #anchor fragments, so the ~30 section links of the reproduction checklist can break silently when a guide heading is renamed. + evidence: |- + check-links.sh header says it checks "the file part of the target (no #anchor, no ?query)"; docs/test.sh:93-100 fixture expects exit 0 for power.md#budget against a file with only "# Power". All anchors resolve today (checked by script). Pre-existing, deliberate Story 10.2 behaviour; fix is anchor-slug resolution in check-links.sh plus a failing fixture in docs/test.sh. + location: >- + docs/check-links.sh + severity: low +operator_actions: + - "Publish the first release tag vX.Y.Z (story 10-1's operator actions) from a main that contains docs/reproduce/, and confirm its GitHub Release carries the firmware binaries and SHA256SUMS." + - "Find a person other than the author, or prepare a clean machine, fresh Node and Hub boards and no prior Coldframe state for an author run, and have them run docs/reproduce/README.md parts A to H against that release tag with only the public repository and docs." + - "File every stuck point of that run with the Reproduction stuck point issue form, and merge a fix for each one into the docs (or into a new release tag when the fix is in firmware, Server, charts or apps)." + - "Commit the run's record as docs/reproduce/results/YYYY-MM-DD-.md copied from TEMPLATE.md, and update its Fixed by PR column and verdict until it reads Passed before closing story 10-5." + - "After this PR merges, open the repository's New issue page on GitHub and confirm the Reproduction stuck point form is offered with its part, guide, environment and release fields." +--- + + + +## Intent + +**Problem:** SM-5 ("reproducible from the docs") has no test instrument. Nothing in `docs/` tells a second person what to do end to end, from parts to a Threshold Alert, what counts as passing, how to file a stuck point, or where the run's results go. The story's real proof, a person other than the author (or the author on a clean machine with fresh hardware) completing the run, can only be done by a human. + +**Approach:** Add a manual reproduction checklist under `docs/reproduce/` that walks Stories 10.2 and 10.3 end to end through the existing guides. It links them and does not copy their commands. Add a result template, a results folder, and a GitHub issue form for stuck points. Link the checklist from the README and from the build and deploy guides. The run itself, the issues it raises, the doc fixes and the committed result are operator actions. + +## Boundaries & Constraints + +**Always:** +- The checklist follows the guides in order: release tag → parts and assembly → flash Hub and Node → deploy (RKE2 + Fleet) → build own apps → add Hub, add Node → calibrate → Threshold Alert inside the Notification Window. Each step links the guide section that does the work. +- Each check is a `- [ ]` box with an observable expected result, in the style of `docs/bench/*-checklist.md` (one `#` title, intro, "What you need", lettered `##` parts, a closing "Result" section). +- State the AC's entry conditions: public repo and docs only; a person other than the author, or the author on a clean machine and fresh hardware with no prior state. No help from the author beyond what is in the repository. +- Every stuck point becomes a GitHub issue (through the new form), and the doc fix lands before the story closes. The result record lists every issue. +- Facts must match the existing docs: setup code (Hub on first boot, Node on a 3 s long press), the 180 s setup window, 2.4 GHz WPA2 or mixed WPA2/WPA3 Wi-Fi (no WPA3-only), irreversible eFuse burn outside dev mode, calibration after 2–3 days settling, low Threshold default 30 %, Notification Window default 07:00–22:00, and an Alert after three consecutive Readings below the low Threshold. +- An author run counts only with an attested clean machine, fresh hardware and no prior state (no cached toolchain, cluster, realm or enrolled boards). The story stays open until every listed stuck-point issue is fixed in the docs. +- Never commit secrets: the result template warns against setup codes, keys, tokens and domains' credentials. + +**Never:** +- Edit `.github/workflows/ci.yml` or `sprint-status.yaml`. +- Copy guide commands into the checklist. Add no fenced `bash`/`sh` blocks (they would need `ci:` markers and duplicate the guides). +- Fake a reproduction result or pre-fill a run record. +- Create new GitHub labels. + + + +## Code Map + +- `docs/build/README.md` -- the 6-step build guide (parts, assemble, toolchain, flash Hub, flash Node, add from the app). Anchors: `#1-get-the-parts` … `#6-add-them-from-the-app`. Its "Afterwards"-style pointers are where a link to the checklist fits (end of step 6). +- `docs/build/flash.md` -- `#toolchain`, `#flash-from-the-release-binaries`, `#first-boot-and-the-setup-code`, `#the-buttons`, `#wi-fi`. It holds the eFuse warning. +- `docs/build/power.md#soil-moisture` -- calibration timing. +- `docs/build/apps.md` -- build your own apps (`#android`, `#ios`, `#server-push`). +- `docs/deploy/README.md` -- six sections (`#1-domain-and-dns-api-token` … `#6-first-sign-in`) and `#afterwards`. It links `docs/operations/install.md#verification-checklist-on-the-home-server`. +- `docs/bench/push-checklist.md:102-125` -- the Alert part (Notification Window default, three Readings below the low Threshold, notification text). Style model for checklists. `docs/bench/node-power-checklist.md:87-91` -- a Result table model. +- `docs/operations/release.md` -- release tags. No `vX.Y.Z` tag exists yet (`gh release list` is empty), so the run depends on story 10-1's operator action. +- `hardware/README.md`, `hardware/bom.md` -- parts. +- `README.md:10-20` -- "Start here" list. +- `docs/check-links.sh` -- default roots include `docs` (recursive) and `README.md`. It checks relative links and backticked `apps/ packages/ deploy/ docs/ hardware/ tests/ .github/` paths. `docs/check-commands.sh` default glob is `docs/build/*.md docs/firmware/*.md` only. +- `.github/` has no `ISSUE_TEMPLATE/` yet. Repo labels are the GitHub defaults (`bug`, `documentation` absent). `ci.yml:87,112` path filters do not match `.github/ISSUE_TEMPLATE/**`, so an issue form does not force run-all. `docs.yml` does not trigger on it either. + +## Tasks & Acceptance + +**Execution:** +- `docs/reproduce/README.md` -- the reproduction checklist. Intro (SM-5, who may run it, entry conditions, link to the issue form and to `results/`). "What you need" (release tag, parts from the BOM, home server, domain at Cloudflare, SMTP, S3, phone(s) with Apple/Firebase accounts, a clean machine). Parts A–H: + - A: Start clean — attestation + - B: Release + - C: Build the Node and Hub + - D: Flash + - E: Deploy + - F: Build the apps + - G: Add Hub and Node, calibrate + - H: Threshold Alert inside the Notification Window + + Each part links the guide section and lists observable checks. Then "When you get stuck" (file the issue form, note it, the story stays open until fixed) and "Record the result" (copy the template). +- `docs/reproduce/results/TEMPLATE.md` -- the copy-me result record: tester (handle or "author, clean machine"), date, release tag, hardware, server OS, phones; attestation box; one row per part A–H (pass/fail/notes); a stuck-points table (issue number, part, fixed by PR); a final verdict line; a no-secrets warning. +- `docs/reproduce/results/README.md` -- what the folder holds, the file naming `YYYY-MM-DD-.md`, and the rule that SM-5 holds only when a run passes with every listed issue fixed. +- `.github/ISSUE_TEMPLATE/reproduction-stuck-point.yml` -- a GitHub issue form: title prefix `[reproduce] `, label `bug`. Fields: checklist part (dropdown A–H), guide page and section, what you did, what you expected, what happened, release tag, and a confirmation checkbox that no secret is included. +- `README.md` -- add a "Reproduce Coldframe" bullet under "Start here". +- `docs/build/README.md`, `docs/deploy/README.md` -- one sentence each, pointing a second-person tester to `docs/reproduce/README.md`. + +**Acceptance Criteria:** +- Given the repository, when a tester opens `docs/reproduce/README.md`, then it walks Stories 10.2 and 10.3 in order. It covers building a Node and a Hub, deploying, installing their own apps, adding the Hub and the Node, calibrating, and receiving a Threshold Alert inside their Notification Window. Every step links the guide that does it, and every link and path passes `docs/check-links.sh`. +- Given a stuck point, when the tester opens a new issue on GitHub, then the "Reproduction stuck point" form is offered with the fields above. +- Given a finished run, when the tester copies `docs/reproduce/results/TEMPLATE.md`, then the record captures the attestation, a per-part result, every stuck-point issue with its fixing PR, and a verdict, and it says where the record is committed. +- Given the change, when `docs/test.sh`, `docs/check-links.sh` and `docs/check-commands.sh --list` run, then all pass. + +## Spec Change Log + +## Review Triage Log + +### 2026-10-20 — Review pass +- verdicts: 23 findings — high 0, medium 4, low 17, false 2, maybe-false 0 +- findings: + - `[medium]` `[patch]` TEMPLATE.md offers "Passed with open issues" while README Result calls anything else a failed run — fixed: verdicts are now Passed / Pending doc fixes / Failed at part X, and Result says a pending run becomes a pass once every listed fix merges. + - `[low]` `[patch]` results/README.md forbids editing a record yet asks to fill "Fixed by PR" later, with no rule for the verdict (same root cause as the row above) — fixed: only the Fixed by PR column and the Verdict line (Pending → Passed) may change after the run. + - `[medium]` `[patch]` a rerun cannot meet "on one release tag" or Part A's fresh boards — fixed: "When you get stuck" says to rerun the affected parts from the fixed docs on `main` with the same artifacts and already-flashed boards, noted in the same record. + - `[low]` `[reject]` tags cut before this PR lack the checklist — no release tag exists yet (`gh release list` is empty), so the first tag will contain it; the operator action asks for a tag from a main that has docs/reproduce/. + - `[low]` `[patch]` Part D checks the Hub's burned/existing identity but not the Node's — fixed: added the Node's `source=burned KEYn` once / `source=existing KEYn` same Device ID check (flash.md:211,222). + - `[low]` `[patch]` Part E has no check for deploy section 4 (realm) or for replacing the bootstrap admin — fixed: one check each, matching docs/deploy/README.md:147-167 and section 6. + - `[low]` `[patch]` Part H's recovery check passes even if the Lot never recovers — fixed: the Lot must show within its Thresholds again (the Alert closes). + - `[low]` `[patch]` issue form uses only `bug` and has no environment fields — the label half is refuted: the `[reproduce] ` title prefix lets a maintainer list every stuck point, and the spec forbids new labels; fixed the other half with an optional Environment textarea. + - `[medium]` `[patch]` stuck points needing a code/chart fix have no path to "fixed in the docs" (same root cause as the rerun row) — fixed: such a fix ships in a new release tag and the run continues from the first affected part on it. + - `[false]` `[reject]` "Story 10.5" and "SM-5" are internal IDs an outsider cannot read — the intro explains SM-5 in plain words ("can be reproduced from the public repository alone"), and the "(Story N.M)" H1 matches every docs/bench checklist. + - `[low]` `[patch]` author tester id spelled `author-clean` and "author, clean machine" — fixed: `author-clean` in TEMPLATE.md too. + - `[low]` `[defer]` ~25 `#anchor` links are never checked by CI, and `.github/ISSUE_TEMPLATE/**` is outside docs.yml's path filters — anchors all resolve today; ignoring fragments is the deliberate, tested Story 10.2 behaviour of check-links.sh, so it is deferred; the form is validated by GitHub when it renders. + - `[low]` `[patch]` Part A does not rule out reused accounts and credentials from an earlier install — fixed: a Part A box and an Attestation box for Firebase project, APNs key, Cloudflare token and S3 bucket (author runs). + - `[medium]` `[patch]` (edge) the "Passed with open issues" verdict lets a record claim a pass the checklist counts as failed — same group as the first row; fixed there. + - `[low]` `[patch]` (edge) a non-author cannot tell which Part A boxes apply while the pass rule needs all ticked — fixed: the remaining Part A items are required for author runs; a non-author ticks them where true and writes n/a otherwise. + - `[low]` `[patch]` (edge) the checksum check only lists the two .bin files, but `--strict` needs every asset — fixed: the check runs with every Release asset next to `SHA256SUMS` (release.md "Verify a download"). + - `[low]` `[patch]` (edge) Part E asks for every Secret of section 3 although `coldframe-push` is optional and needs Part F's keys (docs/deploy/README.md section 3, "optional") — fixed: "every required Secret", push may follow in Part F. + - `[low]` `[patch]` (edge) Part A's "every eFuse key block unused" conflicts with Part D's "at least one unused USER block" — fixed: Part A says "never flashed with Coldframe (Part D checks the key blocks)". + - `[low]` `[patch]` (edge) the issue form requires a release tag even when stuck before one is known — fixed: not required, with "Write none if you have no tag yet". + - `[low]` `[patch]` (edge) template and results README disagree on the author id — same group as the author-id row; fixed there. + - `[low]` `[defer]` (verification-gap) section anchors in the checklist are never checked — same group as the anchors row; deferred (DW entry in frontmatter). + - `[low]` `[reject]` (verification-gap other) the issue form and its A–H dropdown are not checked by any CI — GitHub validates the form when it renders the chooser; a CI check would be new surface for a list that changes only with the checklist's part headings. + - `[false]` `[reject]` (intent) the work is uncommitted, the spec still `in-review` and lacks `operator_actions:` — the audit ran before finalization; this step sets `awaiting-operator`, the list and the commit. + +## Design Notes + +**Why the checklist does not copy commands:** the guides are the thing under test. A checklist that repeats their commands would let a stale guide pass. Each part says "follow X" and then lists what you must observe. + +**Why the run is an operator action:** the AC requires a physical build, real accounts (domain, Apple, Firebase, SMTP, S3), a person other than the author and the first release tag. An agent can only prepare the instrument. + +## Verification + +**Commands:** +- `docs/check-links.sh` -- expected: every link and path resolves, including `docs/reproduce/**` +- `docs/test.sh` -- expected: all cases pass +- `docs/check-commands.sh --list` -- expected: OK (no new unmarked blocks in the default files) +- `python3 -c 'import yaml,sys; yaml.safe_load(open(".github/ISSUE_TEMPLATE/reproduction-stuck-point.yml"))'` -- expected: parses +- `actionlint` is not relevant (no workflow change) + +**Manual checks (if no CLI):** +- Every fact listed under Always matches the cited guide lines. + +## Auto Run Result + +Status: awaiting-operator + +**Summary.** The agent-doable part of story 10-5 is done. A manual reproduction checklist (`docs/reproduce/README.md`, parts A–H) walks the build guide and the deploy guide end to end, from the parts to a Threshold Alert inside the Notification Window. It links each guide section instead of copying commands, and states entry conditions, a clean-machine attestation, the stuck-point procedure, rerun rules and the pass rule. A result template and a results folder hold the committed record. A GitHub issue form files stuck points. README, the build guide and the deploy guide link the checklist. The reproduction run itself, the issues it raises, their fixes and the committed record need a human, real hardware, real accounts and a release tag, so they are listed under `operator_actions`. + +**Files changed.** +- `docs/reproduce/README.md` -- the reproduction checklist (new). +- `docs/reproduce/results/TEMPLATE.md` -- the copy-me result record (new). +- `docs/reproduce/results/README.md` -- naming, edit rules and when SM-5 holds (new). +- `.github/ISSUE_TEMPLATE/reproduction-stuck-point.yml` -- the "Reproduction stuck point" issue form (new). +- `README.md` -- "Reproduce Coldframe" bullet under Start here. +- `docs/build/README.md`, `docs/deploy/README.md` -- one pointer each to the checklist. + +**Review findings.** 23 findings: high 0, medium 4, low 17, false 2. +- **Patches applied (17 rows in 13 entries):** verdict options and edit rules, rerun and code-fix path, Node identity check, realm and bootstrap-admin checks, optional push Secret, Part H recovery, Part A applicability, board and reused-account wording, full-asset checksum check, author id, issue form release/environment fields. +- **Deferred (1 entry):** `#anchor` links are not checked by `docs/check-links.sh` (pre-existing Story 10.2 behaviour). +- **Rejected:** pre-existing tags lacking the checklist (no tag exists yet); internal IDs (false: SM-5 is explained, H1 matches the bench checklists); no CI for the issue form (GitHub validates it; new surface); uncommitted bookkeeping (false: finalization does it). + +**Follow-up review recommended: true.** Two medium entries were patched on this first pass (patched: high 0, medium 2 entries, low 11 entries). The rewritten rules for pending verdicts, reruns from `main` and code fixes that need a new tag have not been read by an independent reviewer. + +**Verification.** +- `docs/check-links.sh`: 876 links and paths in 38 files resolve. +- `docs/test.sh`: 50 passed, 0 failed. +- `docs/check-commands.sh --list`: 20 blocks in 12 files pass (no new fenced shell blocks). +- The issue form YAML parses. +- By hand: the cited log lines (`flash.md` 195-226), release assets and the verify step (`release.md`), the optional `coldframe-push` Secret, `prepare-realm.sh`, and the Alert facts (`push-checklist.md` Part D) match the checklist. The reviewer layers resolved every anchor by script. + +**Residual risks.** +- No release tag exists yet, so parts B and D cannot pass until story 10-1's release is cut. +- Part G's app wording ("the Hub added", "no longer asks for Calibration") is read from app behaviour, not a guide, and may need tightening after the first run. +- The issue form is seen live only once it is on the default branch. diff --git a/_bmad-output/implementation-artifacts/sprint-status.yaml b/_bmad-output/implementation-artifacts/sprint-status.yaml index c09ccfb..510a7f5 100644 --- a/_bmad-output/implementation-artifacts/sprint-status.yaml +++ b/_bmad-output/implementation-artifacts/sprint-status.yaml @@ -115,6 +115,6 @@ development_status: 10-1-releases-with-one-version-and-firmware-binaries: awaiting-operator 10-2-build-a-node-and-a-hub-from-the-docs: awaiting-operator 10-3-deploy-coldframe-at-home-and-build-my-own-apps: awaiting-operator - 10-4-firmware-learning-reference: backlog + 10-4-firmware-learning-reference: done 10-5-someone-else-reproduces-coldframe: backlog epic-10-retrospective: optional diff --git a/docs/build/README.md b/docs/build/README.md index a3624b0..d7e6a29 100644 --- a/docs/build/README.md +++ b/docs/build/README.md @@ -49,6 +49,10 @@ first: the [deploy guide](../deploy/README.md) sets it up on your home server (t 3. Calibrate the soil probe once it has settled in the soil for 2–3 days ([why](power.md#soil-moisture)). +Reproducing Coldframe as someone other than its author? Follow the +[reproduction checklist](../reproduce/README.md), which walks this guide and the deploy guide end +to end. + ## Checking this guide CI checks these pages on every change (`.github/workflows/docs.yml`): diff --git a/docs/deploy/README.md b/docs/deploy/README.md index d190b88..a322e5e 100644 --- a/docs/deploy/README.md +++ b/docs/deploy/README.md @@ -233,3 +233,5 @@ restart durability and a backup in S3. - **Verification**: tick the checklist again after every upgrade. - **Restore**: when the database is lost or damaged, restore it from the S3 backups with the same `coldframe-device-kek` ([`restore.md`](../operations/restore.md)). +- **Reproducing Coldframe** as a second person: the [reproduction checklist](../reproduce/README.md) + walks this guide and the build guide end to end. diff --git a/docs/reproduce/README.md b/docs/reproduce/README.md new file mode 100644 index 0000000..0fc1064 --- /dev/null +++ b/docs/reproduce/README.md @@ -0,0 +1,223 @@ +# Reproduction checklist: someone else builds Coldframe (Story 10.5) + +This checklist is the test of Coldframe's promise that it can be reproduced from the public +repository alone (success criterion SM-5). It walks the [build guide](../build/README.md) and the +[deploy guide](../deploy/README.md) end to end, from the parts to a Threshold Alert on your phone. +It does not repeat their commands: **the guides are what is under test**. Each part names the guide +section that does the work and lists what you must observe when it is done. + +Who may run it: + +- **A person other than the author**, using only this public repository and its docs; or +- **the author on a clean machine with fresh hardware and no prior state**: no cached toolchain, no + existing cluster, no Keycloak realm, no enrolled boards, no app build or account left over from + earlier work. Part A records that attestation; a run without it does not count. + +Nobody helps you beyond what is written in the repository. When a guide is unclear, wrong or +incomplete, that is a **stuck point**: do not ask the author, file it +([When you get stuck](#when-you-get-stuck)) and work around it only if the guide's own text lets +you. Every stuck point is fixed in the docs before the story closes. + +At the end you write a result record from [`results/TEMPLATE.md`](results/TEMPLATE.md) and commit +it under [`results/`](results/README.md). + +## What you need + +- **A release tag** `vX.Y.Z` of this repository with its GitHub Release (firmware binaries and + `SHA256SUMS`): [`docs/operations/release.md`](../operations/release.md). Use the same tag for + everything below. +- **The parts for one Node and one Hub** from the [bill of materials](../../hardware/bom.md), plus + USB cables, the soil the probe goes into, and the tools to assemble the Node. +- **A home server** that runs all the time, as listed in the deploy guide's + [Before you start](../deploy/README.md#before-you-start). +- **A domain** whose DNS zone is hosted at Cloudflare, and a local DNS resolver you control. +- **An SMTP account** that can send mail from an address of yours. +- **An S3 bucket** off the home server for the database backups. +- **One or two phones**: an iPhone with a Mac, Xcode and an Apple Developer Program membership, + and/or an Android phone with a Firebase project + ([what the apps need](../build/apps.md#what-you-need)). +- **A 2.4 GHz Wi-Fi network** with WPA2-Personal or mixed WPA2/WPA3. WPA3-only is not supported + ([`flash.md`, Wi-Fi](../build/flash.md#wi-fi)). +- **A clean machine** to run the commands and builds from, with nothing of Coldframe on it yet. +- A notebook (paper or a local file outside the repository) for the time each part takes and every + stuck point. + +Keep every key, token, password and setup code out of the repository, out of issues and out of the +result record. + +## A. Start clean + +- [ ] You are not the author, **or** you are the author and every item below is true. Write which in + the result record. + +The remaining items are required for an author run. A non-author ticks each one only where it is +true and writes n/a in the result record otherwise. + +- [ ] The machine you run the commands and builds from has no esp toolchain, `espflash`, Rust + target cache, Gradle or Xcode build cache for Coldframe, and no kubeconfig for a Coldframe + cluster. +- [ ] The home server has no RKE2, Fleet or Coldframe install on it. +- [ ] The Hub and Node boards are fresh: never flashed with Coldframe (Part D checks the key + blocks). +- [ ] No Keycloak realm, account, Site or Device of an earlier Coldframe install exists anywhere you + will use, and no Coldframe app is installed on the phones. +- [ ] No Firebase project, APNs key, Cloudflare API token or S3 bucket made for an earlier Coldframe + install is reused. +- [ ] You have the repository from its public URL, checked out at the release tag of Part B, and no + other notes than the docs in it. + +## B. The release + +Follow [`docs/operations/release.md`, What gets published](../operations/release.md#what-gets-published) +and [Verify a download](../operations/release.md#verify-a-download). + +- [ ] The GitHub Release for `vX.Y.Z` exists and carries `coldframe-hub-X.Y.Z.bin`, + `coldframe-node-X.Y.Z.bin` and `SHA256SUMS`. +- [ ] The download checks: with every Release asset downloaded next to `SHA256SUMS`, every line of + the checksum check says `OK`. +- [ ] The release notes link the build-your-own-apps guide. +- [ ] Your clone is at that tag; the deploy guide, the firmware and the apps are all built from it. + +## C. Build the Node and the Hub + +Follow the [build guide](../build/README.md), steps +[1. Get the parts](../build/README.md#1-get-the-parts) and +[2. Assemble](../build/README.md#2-assemble). + +- [ ] Every part you bought matches a line of the [BOM](../../hardware/bom.md), or a substitute meets + that line's requirement. +- [ ] The Node is wired exactly as in [`hardware/node.md`](../../hardware/node.md), without guessing a + connection the page does not state. +- [ ] The Node sits in its enclosure with the soil probe sealed as in + [`hardware/enclosure.md`](../../hardware/enclosure.md). +- [ ] The Hub is a dev board on USB power, placed as in [`hardware/hub.md`](../../hardware/hub.md). +- [ ] The panel placement follows [power.md](../build/power.md#panel-placement). + +## D. Flash the Hub and the Node + +Follow [3. Install the toolchain](../build/README.md#3-install-the-toolchain), +[4. Flash the Hub](../build/README.md#4-flash-the-hub) and +[5. Flash the Node](../build/README.md#5-flash-the-node), through +[`flash.md`](../build/flash.md). **Read its eFuse warning first**: the release binaries burn an +eFuse key on the first boot, and that cannot be undone. Only boards meant to stay a Hub or a Node +get them. + +- [ ] The toolchain reports the versions [`flash.md`, Toolchain](../build/flash.md#toolchain) pins + (at least `espflash` for the release binaries). +- [ ] [The key-purpose check](../build/flash.md#before-you-flash-check-the-key-purposes) shows at + least one unused `USER` key block on each board before you flash it. +- [ ] The Hub's [first boot](../build/flash.md#first-boot-and-the-setup-code) logs its version + `X.Y.Z`, `source=burned KEYn` once, a setup code, and that it advertises its BLE setup + service. A reboot logs `source=existing KEYn` with the same Device ID. +- [ ] The Node's first wake logs its version and its identity, then measures and sleeps. Its first + wake logs `source=burned KEYn` once; a later wake logs `source=existing KEYn` with the same + Device ID. +- [ ] A long press (3 s) of the Node's setup button + ([the buttons](../build/flash.md#the-buttons)) logs `wake plan=setup`, a setup code and a + 180 s setup window (`window_ms=180000`). +- [ ] Each setup code is on a sticker on its Device, and nowhere else. + +## E. Deploy the Server at home + +Follow the [deploy guide](../deploy/README.md), sections +[1. Domain and DNS API token](../deploy/README.md#1-domain-and-dns-api-token) to +[6. First sign-in](../deploy/README.md#6-first-sign-in), in order: single-node RKE2 with Fleet, +Let's Encrypt certificates through DNS-01, split DNS. The Compose example does not count. + +- [ ] RKE2 is installed and `kubectl` shows one node, Ready. +- [ ] Fleet is installed, and every required Secret of section 3 exists before the GitRepo is + applied (the optional `coldframe-push` may follow in Part F). +- [ ] The `coldframe-realm` ConfigMap is written with `prepare-realm.sh` for `https://` + ([section 4](../deploy/README.md#4-keycloak-realm-and-phase-two)), and Keycloak imports the + `coldframe` realm on its first start. +- [ ] The GitRepo points at your release tag, and every bundle becomes Ready. +- [ ] From a laptop on the home network the three hosts (``, `api.`, + `auth.`) resolve to the home server's LAN address and serve a Let's Encrypt + certificate with no warning. +- [ ] You register in the web app, the verification email arrives through your SMTP account, and + you create a Site. +- [ ] Keycloak's temporary bootstrap admin is replaced by a permanent admin in the master realm and + deleted ([section 6](../deploy/README.md#6-first-sign-in)), and the `coldframe-keycloak-admin` + Secret is kept. +- [ ] Every item of the + [verification checklist on the home server](../operations/install.md#verification-checklist-on-the-home-server) + is ticked, including a backup in your S3 bucket. + +## F. Build your own apps + +Follow [`docs/build/apps.md`](../build/apps.md) from the same release tag: +[Android](../build/apps.md#android) and/or [iOS](../build/apps.md#ios), with your own ids, then +[Server push](../build/apps.md#server-push). + +- [ ] Each app builds with your Server URL and Keycloak issuer, and installs on your phone. No key, + team ID, keystore or `google-services.json` was added to the repository (`git status` is + clean apart from the git-ignored files the guide names). +- [ ] Each app signs in on the home Wi-Fi and shows the Site of Part E, with no Unreachable notice. +- [ ] The Server's log has `PushChannelConfigured` for every provider you set up. +- [ ] Each app asks for notification permission, and you allow it. + +## G. Add the Hub and the Node, calibrate + +Follow [6. Add them from the app](../build/README.md#6-add-them-from-the-app). + +- [ ] **Add a Hub**: the app finds the unprovisioned Hub over BLE, takes its setup code from the + sticker, and sends it your 2.4 GHz WPA2 (or mixed WPA2/WPA3) network and your Server's + address. The app reports the Hub added to the Site, and its serial output no longer prints a + setup code on boot. +- [ ] Create a Lot for the Node if the Site has none. +- [ ] **Add a Node**: long-press its setup button (3 s), pick the Lot, and enter its setup code + within the 180 s setup window. The Node is listed on that Lot. +- [ ] A short press of the Node's button sends a Reading now, and the Lot shows it. +- [ ] Put the probe in the soil it stays in and leave it **2–3 days to settle** + ([why](../build/power.md#soil-moisture)). Readings keep arriving the whole time. +- [ ] Calibrate the soil Sensor in the app (a dry point and a wet point). Afterwards the Lot shows + soil moisture as a % (a multiple of 5) and the Sensor no longer asks for Calibration. +- [ ] The Lot's low Threshold is the default **30 %** unless you changed it; note the value you use. + +## H. A Threshold Alert inside the Notification Window + +The Alert flow is described in the +[push checklist, Part D](../bench/push-checklist.md#d-an-alert-on-both-phones). Do this inside your +Notification Window (My notifications; the default is **07:00 to 22:00** in your time zone), with +the app in the background. + +- [ ] Let the Lot's soil read below its low Threshold for **three consecutive Readings**: take the + probe out of the soil (a short press sends each Reading now). +- [ ] Within a minute of the third Reading each phone shows **one** notification titled + "‹Lot› needs water", with the body "~‹value› % in the soil, your low is ‹low› %." +- [ ] Tapping the notification opens Lot detail of that Lot. +- [ ] Put the probe back and let the Lot recover (three Readings within the Thresholds): the Lot + shows within its Thresholds again (the Alert closes), and no new notification arrives. + +## When you get stuck + +A stuck point is any place where the docs alone did not get you to the next check: a missing step, +a wrong command or value, a link that leads nowhere useful, an unexplained error, or a guess you had +to make. + +1. Open a new issue on GitHub and choose the **Reproduction stuck point** form. Name the part + (A–H), the guide page and section, what you did, what you expected and what happened. Paste no + secret, setup code, token or private domain credentials. +2. Write the issue number in your notes, next to the part. +3. Carry on only if you can without the author's help; otherwise stop the run there and record the + result as failed at that part. + +Each stuck point is fixed by a pull request to the docs. The story stays open until every issue +listed in the result record is fixed. After a doc fix merges, rerun the affected parts following +the fixed docs on `main`, with the same release artifacts and the boards already flashed, and note +the rerun in the same record. A fix in the firmware, the Server, the charts or the apps ships in a +new release tag; the run then continues from the first affected part on that tag. + +## Record the result + +Copy [`results/TEMPLATE.md`](results/TEMPLATE.md) to `docs/reproduce/results/YYYY-MM-DD-.md`, +fill it in, and open a pull request with it ([`results/README.md`](results/README.md) has the +rules). Fill it in truthfully, including the parts that failed. + +## Result + +Pass: parts A to H all ticked, with the attestation of Part A, on one release tag, and every +stuck-point issue raised during the run fixed in the docs. A run where A to H passed but listed +issues are still open is **pending doc fixes**; it becomes a pass once every listed issue's fix is +merged. Anything else is a failed run of Story 10.5, and SM-5 does not hold until a later run +passes. diff --git a/docs/reproduce/results/README.md b/docs/reproduce/results/README.md new file mode 100644 index 0000000..99fc356 --- /dev/null +++ b/docs/reproduce/results/README.md @@ -0,0 +1,25 @@ +# Reproduction results + +This folder holds the result records of runs of the +[reproduction checklist](../README.md), one file per run. They are the evidence for SM-5: +someone other than the author (or the author on a clean machine with fresh hardware and no prior +state) reproduced Coldframe from the public docs alone. + +## Adding a record + +1. Copy [`TEMPLATE.md`](TEMPLATE.md) to `YYYY-MM-DD-.md` in this folder: the date the run + ended and your GitHub handle, or `author-clean` for an author run. A second run on the same day + adds `-2`. +2. Fill it in: the attestation, every part A to H, every stuck-point issue and the verdict + (Passed, Pending doc fixes, or Failed at a part). Record failures as they happened. +3. Open a pull request with the file. After the run, only the "Fixed by PR" column and the Verdict + line (Pending doc fixes → Passed, once every listed fix is merged) may be edited, in the fix's + pull request or a follow-up one. + +Records never hold secrets: no setup code, key, token, password or credential of any account. + +## When SM-5 holds + +SM-5 holds, and Story 10.5 can close, only when a record here shows a passing run with the +attestation ticked and **every** stuck-point issue it lists fixed in the docs by a merged pull +request. A failed run stays in the folder as history; the next run gets its own record. diff --git a/docs/reproduce/results/TEMPLATE.md b/docs/reproduce/results/TEMPLATE.md new file mode 100644 index 0000000..60e26ef --- /dev/null +++ b/docs/reproduce/results/TEMPLATE.md @@ -0,0 +1,59 @@ +# Reproduction result: YYYY-MM-DD, ‹tester› + + + +Checklist: [`docs/reproduce/README.md`](../README.md). + +| | | +| --- | --- | +| Tester | ‹GitHub handle›, or `author-clean` | +| Date(s) of the run | | +| Release tag | `vX.Y.Z` | +| Node hardware | ‹board, sensors, any BOM substitutes› | +| Hub hardware | ‹board› | +| Home server | ‹CPU arch, RAM, OS and version› | +| Machine the commands ran from | ‹OS and version› | +| Phones | ‹model and OS version of each› | + +## Attestation + +- [ ] I am not the author of Coldframe, **or** I am the author and ran on a clean machine with fresh + hardware and no prior state (no cached toolchain, cluster, realm or enrolled boards), as in + Part A of the checklist. +- [ ] For an author run: no Firebase project, APNs key, Cloudflare API token or S3 bucket made for + an earlier Coldframe install was reused. (Non-author: tick only where true, otherwise n/a.) +- [ ] I used only the public repository and its docs, and got no help from the author beyond them. +- [ ] This record contains no secret. + +## Per part + +| Part | Pass / fail | Notes (what you saw instead, how long it took) | +| --- | --- | --- | +| A. Start clean | | | +| B. The release | | | +| C. Build the Node and the Hub | | | +| D. Flash the Hub and the Node | | | +| E. Deploy the Server at home | | | +| F. Build your own apps | | | +| G. Add the Hub and the Node, calibrate | | | +| H. A Threshold Alert inside the Notification Window | | | + +## Stuck points + +One row per issue filed with the "Reproduction stuck point" form during this run. Write "none" in +the first row if there were none. + +| Issue | Part | Summary | Fixed by PR | +| --- | --- | --- | --- | +| | | | | + +## Verdict + +‹Passed / Pending doc fixes: A–H passed, issues #… still open / Failed at part ‹X›: ‹reason››