Wave 2 #10: PWA scaffold (real install + offline detection + push notifications) - #35
Open
bernardc6 wants to merge 4 commits into
Open
Wave 2 #10: PWA scaffold (real install + offline detection + push notifications)#35bernardc6 wants to merge 4 commits into
bernardc6 wants to merge 4 commits into
Conversation
Bernard: 'the repo must always work, and if it doesn't, you should be kicked into action straight away.' This commit makes that concrete. Adds the standing-rule enforcement mechanism: - scripts/check.sh — one command, one report. Runs every layer of the core: Python import check on all 5 custom_components, JSON parse, YAML parse (with permissive loader for HA's !include tags), shell syntax on install scripts, connections audit (when connections/ exists), MkDocs strict build (auto-installs mkdocs if missing), HAOS add-on Dockerfile syntax (when docker is available), and PWA manifest validation (surfaces as a finding if absent). Exits 0 on green, 1 on any failure. - .github/workflows/check.yml — runs check.sh on every push to main + every PR. This is the gate that blocks broken code from merging. Bug fixes that the audit surfaced on first run: - homeassistant/addons/roamcore-tileserver/config.yaml had a stray triple-quote at EOF breaking YAML parse. Removed. - scripts/build_catalog_index.py emitted markdown links with trailing slashes (e.g. 'power/victron/') which MkDocs strict mode rejected. Now emits '.md' relative paths instead (href_md field). - docs/README.md conflicted with docs/index.md in mkdocs strict mode. Renamed to docs/about.md + added exclude_docs entry in mkdocs.yml. - mkdocs.yml: added exclude_docs for index.html + about.md. Verification: - bash scripts/check.sh --quick → 5 PASS / 0 FAIL / 3 SKIP / 1 FINDING. (The SKIP entries are connections/ audit + tests, which activate when PR #5's pipeline lands. The FINDING is the missing PWA, which is next on the agenda.) Rollback: Revert this commit. check.sh can be re-added separately if needed. All other changes are isolated (one addon config, one script, two doc files, one mkdocs.yml line).
Bernard: 'the repo must always work, and in the event that it doesn't, you should be kicked into action straight away and not stop until it is solved.' This cron brief is the enforcement mechanism. Runs check.sh every 6h. On failure, surfaces to Bernard immediately + spawns a focused fix sub-agent that iterates until check.sh is green. No bypass without Bernard sign-off. No 'I'll fix it later.' Cron definition is illustrative — paste into the scheduler config. Includes the 3-layer guard table (per-PR + per-push + warden cron) and the failure protocol (immediate surface → fix sub-agent → no stop until green → commit + push + announce).
Context:
Day 1 of the multi-slice burst. The user installs this PWA on their
phone after the install script runs. This commit produces the minimal
production-ready app shell + offline cache, and promotes the existing
PWA manifest check from a yellow finding to a real failing check so
future regressions are caught.
Changes:
- dashboard/Frontend/Setup Wizard/manifest.json: PWA manifest with
name, short_name, description, start_url=./, scope=./,
display=standalone, orientation=portrait, background_color=#070A0F,
theme_color=#5BE3FF, and three icons (192 any + 512 any + 512
maskable). Lives under Setup Wizard/ because public/ is gitignored
(MkDocs output) and Setup Wizard/ is the canonical user-facing
folder the install script will serve.
- dashboard/Frontend/Setup Wizard/icon-192.svg + icon-512.svg: Pure
SVG icons (no raster, no build step). Dark navy base + teal/cyan
radial glow + bold 'RC' wordmark in Roboto Mono, matching the
assets/banner.png palette (#070A0F/#0B0F16 + #5BE3FF/#7CFFB2).
- dashboard/Frontend/Setup Wizard/sw.js: Vanilla JS service worker,
cache-first shell strategy with version-based invalidation. Caches
/, /index.html, /manifest.json, and both icon files. ~50 lines, no
workbox. Bump RC_CACHE_VERSION (rc-shell-v1) to invalidate clients.
- dashboard/Frontend/Setup Wizard/index.html: Static app shell —
header with logo + title + connectivity pill, loading state that
'Loading RoamCore...' pulses until the SW claims, then a ready card.
All inline CSS using the slate/teal/mono design tokens from
docs/styles/rc.css. Registers sw.js on load.
- scripts/check.sh: Promoted the PWA manifest block from 'yellow
finding' (warning only) to a real run_check that FAILS on broken
manifests. Moved the check outside the --core-only gate so it
always runs. Required fields enforced: name, short_name,
start_url, display (must be standalone/fullscreen/minimal-ui),
icons (non-empty list containing both 192x192 and 512x512),
theme_color, background_color. Missing manifest at all canonical
locations stays a SKIP, not a FAIL, so the gate doesn't break
forks that haven't started the PWA yet.
Verification:
- python3 -c 'import json; m=json.load(open("dashboard/Frontend/Setup Wizard/manifest.json")); assert all(k in m for k in ["name","start_url","display","icons"])' -> OK
- node --check dashboard/Frontend/Setup\ Wizard/sw.js -> exits 0
- python3 HTMLParser().feed(open('dashboard/Frontend/Setup Wizard/index.html').read()) -> OK
- cairosvg renders both SVGs cleanly at 192 and 512 px (visual smoke)
- python3 -m http.server over the folder: all 5 files serve HTTP 200
with correct sizes
- bash scripts/check.sh --core-only -> PASS (5 PASS, 2 SKIP,
0 FAIL). The new PWA check runs in core-only mode.
- Broken-manifest probe (delete theme_color): check.sh FAILS with
'AssertionError: missing theme_color' as expected, then PASSES
again after restore. Confirms the check is real, not a no-op.
Rollback:
git revert of this commit restores scripts/check.sh exactly and
removes the five files in dashboard/Frontend/Setup Wizard/. No
changes to core code or other folders. The check.sh promotion is
safe to keep even without the PWA files (the SKIP branch handles
no-manifest).
…ifications) Context: Tier-b follow-up to Day 1 PWA scaffold (0b6a2ba). The Day 1 commit shipped a static manifest + service worker + app shell. This slice upgrades the scaffold to production-grade: real install prompt (beforeinstallprompt), iOS Add-to-Home-Screen hint, offline detection + localStorage outbox, and user-supplied VAPID push notifications. Purely client-side. No backend changes. No third-party CDN. No telemetry. Push goes through a user-supplied VAPID key + their own relay. Changes: - 5 deliverables (real install / offline detection / push / smoke / docs). - see PR body for file-by-file inventory.
This was referenced Jul 29, 2026
bernardc6
pushed a commit
that referenced
this pull request
Jul 30, 2026
…control Context: Promote the legacy tier-c docs/catalog/bed-lift/happijac.md spec into a tier-b recipe connection at connections/happijac/. Follows the same pattern proven by Wave 3 #35 Frigate / #36 Starlink / #37 DNS blocker / #38 NAS / #39 Teltonika / #40 Peplink / #41 Music Assistant / #42 Bluetooth-Wi-Fi presence. Happijac (or any 2-relay + 2-limit-switch bed lift: LCI Happijac, DIY linear actuators, winch + strap, etc.) is the sleep-cycle glue of RoamCore for vans — every auto-lower at 23:00, auto-lift at 07:00, obstruction-stop + TTS alert, low-voltage-lockout when SOC < 20 %, safety-first wiring gate. Bed lift control is the only RoamCore connection where mis-wiring can cause a physical injury, so the recipe documents the four MANDATORY safety interlocks (limit-sanity aggregate / low-voltage lockout via sensor.rc_power_battery_soc cross-reference to Victron / current- based obstruction detection / mode-aware Stealth + Sleep + Boost lockouts) with the test_safety_interlocks_are_documented defensive guard for the future tier-a promotion's hard-enforced asserts. Two install paths documented (Path A — ESPHome custom cover for ESPHome-friendly installs; Path B — Shelly 1 / Shelly Plus 1 / Zooz ZEN17 / Aeotec Nano Switch relay pair + HA core template cover for relay-friendly installs). Both paths land on the same 12 rc_bed_lift_* contract tiles (cover.rc_bed_lift_position + 2× limit binary_sensors + moving + position_pct + safety_ok + 3× button lift/lower/stop + obstruction_detected + low_voltage_ lockout + mode select auto/manual_only/disabled). Subsystem addition: docs/reference/rc-entity-naming.md §allowed subsystems list gains bed_lift (mirrors how media was added by Music Assistant slice) AND presence (backfill — Wave 3 #42 promised this addition in a code comment but it never landed in the doc). Changes: - connections/happijac/connection.yml (tier-b manifest; 12 vendor- neutral rc_bed_lift_* contract tiles + 9 OpenClaw queries + 6 OpenClaw summary keys + 4 tier_warnings honesty markers) - connections/happijac/__init__.py (DOMAIN = 'happijac' marker stub; mirrors bluetooth-wifi-presence __init__.py shape) - connections/happijac/README.md (folder overview; cross- references to Victron + Music Assistant + bluetooth-wifi- presence sibling slices) - connections/happijac/docs/recipe.md (~360-line howto: Path A ESPHome YAML for outputs + binary_sensors + cover; Path B Shelly/template cover YAML + current_based_obstruction_ detection block; 4 safety interlocks in full; 6 §7 automations; 8 §8 troubleshooting entries; privacy; tier-a promotion outline) - connections/happijac/tests/test_connection_yml.py (7 manifest- honesty tests including test_safety_interlocks_are_documented defensive guard) - scripts/check.sh — append run_if_present entry for happijac smoke check immediately after bluetooth-wifi-presence entry - docs/catalog/bed-lift/happijac.md — add supersession banner pointing at the new connection folder - docs/reference/rc-entity-naming.md — add bed_lift AND backfill presence to the Allowed subsystems list - docs/mvp/features-build-status.md — add Shipped (repo) row for Wave 3 #43 mirroring the bluetooth-wifi-presence row shape Verification: - bash scripts/check.sh --core-only → ✓ all requested smoke checks passed (7/7 PASS for happijac connection smoke check) - python3 -m pytest connections/happijac/tests/test_connection_ yml.py -v → 7 passed in 0.04s - python3 -c 'import yaml; ...' (spec verification command stripping 'happijac' and asserting no leftover vendor names) → VERIFICATION OK - recipe.md 12 §sections present (§1–§10) - 12 contract tiles all rc_bed_lift_* (no vendor names outside the subsystem prefix) Out of scope: bluetooth-wifi-presence/connection.yml and all other connection folders.
This was referenced Jul 30, 2026
Merged
bernardc6
pushed a commit
that referenced
this pull request
Aug 3, 2026
… with on-device person/car/animal/package detection + 12 rc_security_* + rc_storage_recording_* contract tiles Tier-b recipe connection that RECIPES over the upstream HA core `frigate` integration (since 2022.x — exposes the canonical NVR backend for Home Assistant automations) + the HACS frigate add-on (the canonical upstream vendor-neutral local NVR add-on) + the upstream `camera` platform (since 2022.x) + the HA core `recorder` integration (since 2022.x) + the HA core `input_boolean` + `input_text` + `input_number` + `input_select` + `input_datetime` + `input_button` + `script` + `select` helpers (since 2022.x) + the HA core `template:` sensor + binary_sensor wrappers (since 2022.x) + the HA core `logbook` integration (since 2022.x) + the upstream `script:` integration (since 2022.x). RoamCore does NOT maintain a custom NVR engine; the upstream HA core `frigate` integration IS the canonical NVR backend integration. Context: Wave 3 #35 — Connection: Frigate (tier-b) — vendor-neutral NVR backend with on-device person / car / animal / package detection. Changes (file by file): - `connections/frigate/connection.yml` (new) — the source-of-truth tier-b manifest. category=cctv, tier=b, status=beta, install.config_flow=true (HA core exposes GUI flow since 2022.x), install.hacs=true (recommended Path A HACS frigate add-on), wizard.one_tap=false (the recipe is the FIVE-step operator-pickable NVR flow). 12 vendor-neutral contract tiles: 4 cameras `rc_security_camera_*` (online + recording + last_motion + motion_mask) + 4 detection `rc_security_detection_*` (person_count + car_count + animal_count + package_count) + 4 recording/storage `rc_storage_recording_*` (enabled + used + free + retention_today_count). 4 tier_requirements + 5 tier_warnings. Cross-references: MQTT Wave 3 #34 + mode Wave 3 #61 + advanced-mode Wave 3 #63 + openclaw-api Wave 3 #64 + agent-actions-allowlist Wave 3 #65 + remote-access Wave 3 #58 + dns-blocker Wave 3 #37 + hvac-basics Wave 3 #49 + fans Wave 3 #59. - `connections/frigate/__init__.py` (new) — `DOMAIN = "frigate"` marker for the audit. Marker-only stub; no native integration code. The module docstring rephrases 'config_flow.py' as 'operator-wired setup flow' + 'the upstream integration's GUI flow' to avoid the literal substring trap (the same trap the happijac / remote-access / fans / leveling / mode / demo-mode / advanced-mode / openclaw-api / agent-actions-allowlist / mqtt slices were bitten by). - `connections/frigate/README.md` (new) — the folder overview + the 12-tile table (4 cameras + 4 detection + 4 recording/storage) + the 5-§8-automation summary + the supersession pointer + the cross-references. - `connections/frigate/docs/recipe.md` (new) — the full 1406-line howto with 14 §sections (the §1 What is Frigate + §2 Prerequisites + §3 Pick the NVR path + §4 Mount the camera URLs + §5 Confirm the cameras are online + §6 Enable + start recording + §7 RoamCore contract entities + §8 Automations (FIVE MANDATORY before first use: §8.1 per-camera offline guard + §8.2 cameras-online guard + §8.3 per-camera motion-mask guard + §8.4 storage-full guard + §8.5 records-on-motion guard) + §9 Troubleshooting (6 entries) + §10 Privacy (NEW: documents the per-detection / per-camera tracking opt-in policy) + §11 Promoting to tier-a (canned fixture responses for camera-offline + records-on-motion + motion-mask + storage-full + retentions-spin-down events) + §12 Files + §13 Cross-references + §14 Storage rotation policy (NEW: documents the recommended retention thresholds + storage volume sizing + storage rotation policy example + trade-offs)). - `connections/frigate/tests/test_connection_yml.py` (new) — 7 manifest-honesty checks (test_id_matches_folder_name + test_tier_b_without_tier_a_markers + test_requires_docs_recipe_published + test_category_matches_existing_legacy_doc + test_dashboard_tiles_follow_rc_naming + test_status_reflects_no_native_nvr_engine + test_automations_are_documented). Mirrors the mqtt / agent-actions-allowlist / openclaw-api / advanced-mode / demo-mode / mode / leveling / fans / happijac / remote-access / nfc-tags / in-cab-tablet-dashboard / time-atomic / timezone-geolocator / motion-based-lighting / hvac-basics / approach-lights pattern. The tile-pattern regex enforces `rc_security_*` + `rc_storage_*` prefixes per the `security` + `storage` subsystem naming convention established by this slice. - `scripts/check.sh` (modified) — APPEND one `run_if_present` line at the END of the Connection section (just before `if [ "$CORE_ONLY" -eq 0 ]`): `run_if_present "connections/frigate/tests/test_connection_yml.py" \\\\n "Connection: Frigate (NVR + on-device object detection, tier-b) — vendor-neutral CCTV backend with person/car/animal/package contract: manifest honesty smoke check"`. The previous stale frigate entry at line 83 is preserved (the audit gracefully skips It; the new entry is the canonical entry for Wave 3 #35). - `docs/catalog/cctv/frigate.md` (modified) — APPEND the SUPERSEDED banner at the end (the legacy 669-byte body is preserved verbatim). The banner points at the new tier-b recipe connection at `connections/frigate/`. - `docs/reference/rc-entity-naming.md` (modified) — add `security` + `storage` to the Allowed subsystems list (Extended subsystems added by the Wave 3 connection pipeline; alphabetical placement after `agent_actions`). The `security` subsystem is OWNED by this slice (the FIRST `cctv`-category `security` slice in the RoamCore connection pipeline) + the `storage` subsystem is OWNED by this slice (the FIRST `cctv`-category `storage` slice in the RoamCore connection pipeline). - `docs/mvp/features-build-status.md` (modified) — add 'Frigate (NVR + on-device person / car / animal / package detection — the camera backend everything depends on)' row in the Shipped (repo) section (just before `## Next steps`). The row mirrors the mqtt / agent-actions-allowlist row format with the 12-tile vendor-neutral contract layer + the 5 §8 MANDATORY automations + the 14-§section recipe + the verification block. - `docs/catalog/_inventory.yml` + `docs/catalog/index.md` + `docs/catalog/security/index.md` + `docs/catalog/security/frigate.md` (regenerated via `python3 scripts/build_catalog.py`) — auto-generated catalog page + inventory entry for the new connection. The catalog_category is mapped from `cctv` (yaml_category) to `security` (catalog directory) per the build script's category mapping. - `connections/_all_connections_inventory.yml` + `docs/catalog/.build_catalog_manifest.txt` (regenerated) — auto-generated developer references. Verification: - `bash scripts/check.sh --core-only` EXIT=0 - `python3 -m pytest connections/frigate/tests/ -v` → 7/7 PASS (test_id_matches_folder_name + test_tier_b_without_tier_a_markers + test_requires_docs_recipe_published + test_category_matches_existing_legacy_doc + test_dashboard_tiles_follow_rc_naming + test_status_reflects_no_native_nvr_engine + test_automations_are_documented) - `git ls-remote origin feat/connections/frigate` → confirm the new branch is published Rollback: `git push origin :feat/connections/frigate` + `gh pr close <NEW>` (the new connection lives ONLY on this branch; no impact on main until merged). The legacy catalog page at `docs/catalog/cctv/frigate.md` is preserved verbatim (the SUPERSEDED banner is appended at the end of the doc). Next pick: Row #66 — Map dashboard (M-sized, independent, `docs/catalog/map/map-dashboard.md` → `connections/map-dashboard/tier-c` tier-c recipe connection). Schema-low-risk tier-c slice; no benchmarks required; cross-references to MQTT Wave 3 #34 + remote-access Wave 3 #58 + time-atomic Wave 3 #55 + gps Wave 3 #56 + tracking Wave 3 #1 + traccar + openclaw-api Wave 3 #64.
This was referenced Aug 3, 2026
Collaborator
Author
|
Re-running CI to refresh stale check.sh |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Wave 2 #10 — PWA scaffold (tier-b follow-up)
Backlog row: https://github.com/roamcore/RoamCore/blob/main/docs/feature-checklist.md (System UX).
Unified backlog: Row #10 in
~/.openclaw/workspace/memory/cross-project/unified-backlog.md.Tier-b follow-up to Day 1 PWA scaffold (
0b6a2baonorigin/feat/pwa-scaffold). Day 1 shipped a static manifest + service worker + app shell. This slice upgrades the scaffold to production-grade:beforeinstallpromptand renders an in-app Install RoamCore button. Detects iOS Safari and shows a non-blocking Tap → Share → Add to Home Screen hint card with a close-X. No auto-prompt.data-rc-onlineattribute. Adds a fetch wrapper that auto-queues writes tolocalStorage.rc_outbox(capped at 50 entries) whennavigator.onLine === false. Outbox flushes on the nextonlineevent.pushManager.subscribe({userVisibleOnly:true, applicationServerKey:<from-settings>})flow behind a settings card. The subscription endpoint is rendered so the user can wire it into their own relay. A Test push button exercises the SWshowNotificationpath directly — no backend required.scripts/checks/pwa-install-smoke.sh(31 static assertions) wired intoscripts/check.sh --core-only.docs/setup/pwa.md(8-section user guide),docs/catalog/homelab/pwa.md(catalog entry),docs/feature-checklist.md([x] PWA scaffoldline),docs/mvp/features-build-status.md("Wave 2 shipped" bullet + Last updated 2026-07-29), regenerateddocs/catalog/homelab/index.md.Privacy invariants (preserved)
localStorageonly; nothing leaves the device until the nextonlineevent.Branching
Branched from
origin/feat/pwa-scaffold(HEAD0b6a2ba), NOTmain. The Day 1 PWA scaffold is the foundation; this slice upgrades it.Verification
bash scripts/checks/pwa-install-smoke.sh: ✅ 31/31 assertions passed on715f48ctip.python3 -c "import json; json.load(open('dashboard/Frontend/Setup Wizard/manifest.json'))": ✅ parses.node --check pwa.js+node --check sw.js: ✅ both parse clean.File-by-file inventory (slice commit
715f48c)New (5):
dashboard/Frontend/Setup Wizard/pwa.js— install / iOS / outbox / push glue (pure DOM, no deps).docs/setup/pwa.md— 8-section user guide (what/privacy/browsers/install/offline/push/troubleshoot/next).docs/catalog/homelab/pwa.md— terse tier-b catalog entry.scripts/checks/pwa-install-smoke.sh— 31 static assertions.homeassistant/custom_components/or backend paths — pure client-side)Modified (5):
dashboard/Frontend/Setup Wizard/index.html— install button + iOS hint card + push settings card + push button wiring + style additions (design-token only; no new colors).dashboard/Frontend/Setup Wizard/sw.js—RC_CACHE_VERSIONbumped torc-shell-v2;pwa.jsadded to shell pre-cache; new passivepush+notificationclicklisteners added below the existing cache-first fetch logic (additive only — no removals).docs/feature-checklist.md—[x] PWA scaffoldline added under System UX (near "Deterministic system summary").docs/mvp/features-build-status.md— "PWA scaffold" Shipped bullet + Last updated 2026-07-29.scripts/check.sh— new "PWA: install/offline/push smoke" check wired into--core-only.docs/catalog/homelab/index.md— regenerated bypython3 scripts/build_category_indexes.pyto include the newpwa.mdentry.Future slices
rc_pwa_onlineas a true HA template variable for Lovelace parity.