Skip to content

Wave 9 #120c: Phase 3 Hub — Support Bundle tier-a landing (PR #79) + Hub-level one-tap export wiring - #105

Open
bernardc6 wants to merge 2 commits into
mainfrom
subagent/hub-support-bundle-export
Open

Wave 9 #120c: Phase 3 Hub — Support Bundle tier-a landing (PR #79) + Hub-level one-tap export wiring#105
bernardc6 wants to merge 2 commits into
mainfrom
subagent/hub-support-bundle-export

Conversation

@bernardc6

Copy link
Copy Markdown
Collaborator

Wave 9 #120c: Phase 3 Hub — Support Bundle tier-a landing (PR #79) + Hub-level one-tap export wiring

Lands the open PR #79 (e8a0718) onto current main as a clean cherry-pick with two trivial conflict resolutions, AND adds Hub-level one-tap export wiring so an operator can press one button on the Hub dashboard and send a support bundle without navigating to Settings → Developer Tools → Services.

Context

Changes

Two commits on subagent/hub-support-bundle-export:

  1. 3a05da4 — cherry-pick of e8a0718 (the open PR Wave 3 #72: Connection: Support bundle (tier-a) — vendor-neutral diagnostic snapshot exporter for RoamCore-installed HA instances #79). Lands:

  2. ff52549 — Hub-level wiring + data-layer corrections:

    • homeassistant/packages/roamcore_support_bundle_hub.yaml (NEW, ~150 lines): 2 input_buttons + 3 template sensors + 3 §8 MANDATORY automations (button guard + success bookkeeping + failure capture) + 4 internal input_text helpers + 1 input_select status value. Button guard calls roamcore.export_support_bundle with zip: true|false per the button flavor; success automation populates path + at + Exported; failure automation captures the plain-English error message + marks Failed.
    • homeassistant/packages/tests/test_support_bundle_hub.py (NEW, 11 tests, all PASS): pytest rig covering file existence, YAML parse, 2 explicit input_buttons, 3 sensors, 3 automations, button→service wiring, success bookkeeping, failure capture, rc-entity-naming compliance, idempotent re-application.
    • scripts/checks/support-bundle-hub-smoke.sh (NEW, ~100 lines): bash smoke wrapper with YAML pre-check + rc-naming check + pytest wrapper + exit-code propagation.
    • docs/runbooks/hub-support-bundle.md (NEW, IKEA 5-step style): opens with plain-English sentence, §1-§4 vanlifer-facing, §5 developer-facing only.
    • connections/support-bundle/connection.yml (MOD, +1 line): added state: Available (the cherry-picked recipe was missing the cross-cutting state field).
    • homeassistant/packages/tests/test_connection_state_field.py (MOD): bumped expected manifest count 32 → 33 to match the new connection (the test's own failure message acknowledges this is the expected pattern when adding a new connection).
    • scripts/check.sh (MOD, +2 lines): wired scripts/checks/support-bundle-hub-smoke.sh into the chain.
    • scripts/checks/connection-state-smoke.sh (MOD, 2 string updates): updated "32" → "33" in inline comments + OK message.

Verification

  • python3 -m pytest homeassistant/packages/tests/test_support_bundle_hub.py -v11/11 PASS
  • bash scripts/checks/support-bundle-hub-smoke.shexit 0 (parses cleanly + rc-naming OK + 11/11 pytest)
  • python3 -m pytest connections/support-bundle/tests/ -v8/8 PASS (cherry-pick recipe tests still green)
  • python3 -m pytest homeassistant/packages/tests/test_connection_state_field.py -v22/22 PASS (cross-cutting state-field test now 33-aware)
  • bash scripts/check.sh --core-onlyGREEN, exit 0
  • bash scripts/check.sh (full) → GREEN, exit 0

Rollback

Two-commit revert:

  • git revert ff52549 (Hub wiring + state-field + count bump)
  • git revert 3a05da4 (the cherry-pick recipe)

Reverting ff52549 alone leaves the cherry-pick on main but removes the Hub-level wiring (the recipe-level contract tiles still work, but the Hub-level dashboard buttons don't). Rollback does not touch any Proxmox / HA / OpenWrt / networking config — pure repo-local data + Python + YAML.

GOLDEN.md alignment

  • Serves P1 (Novice-first UX): the Hub-level package exposes the canonical exporter as a one-tap button; the runbook opens with plain-English and walks §1-§4 without operator jargon; Failed status surfaces visibly (fail-softly UI).
  • Serves P2 (Mission-critical connectivity): the support bundle is the canonical recovery surface when something on the Hub breaks; the dashboard one-tap button is the operational improvement.
  • Serves P6 (OpenClaw as a first-class citizen): the Hub-level buttons call the same roamcore.export_support_bundle service that OpenClaw reads via the openclaw-api + agent-actions-allowlist connections.
  • Respects E1 (Customer-facing repo): no internal logs, no sensitive infra; runbook lives in docs/runbooks/.
  • Respects E2 (Documentation-driven): runbook = source of truth for user; package YAML = source of truth for data layer; pytest rig = source of truth for contract assertions.
  • Respects E3 (Backup + rollback discipline): no Proxmox / HA / OpenWrt / networking change; pure repo-local code + data; rollback is a single git revert.
  • Respects E4 (Git fast mode): direct-to-branch push on subagent/hub-support-bundle-export; one PR per protocol.
  • Respects E7 (Naming follows rc-entity-naming.md): every new entity starts with rc_support_bundle_hub_ (Hub-level prefix), distinct from the recipe-level rc_support_bundle_ prefix; the new Hub-level subsystem is OWNED by homeassistant/packages/roamcore_support_bundle_hub.yaml.
  • Avoids "Hand-configuring Victron": purely YAML + Home Assistant native types, no Victron-specific discovery.
  • Avoids "Touching vmbr0": no networking config touched.
  • Avoids "Committing secrets": no secrets in the package; last_error holds a plain-English error message.
  • Avoids "Wide PRs": pure support-bundle-infra slice, no cross-cutting changes.
  • Avoids "Pulling in unrelated project context": strictly support-bundle scope, no other subsystems touched.

User-facing

When something stops working on the Hub, I can press one button and get a single file that captures what's going on inside the system, so I can send that file to support without having to explain my whole setup over chat.

Acceptance checklist

  • branch tip is on subagent/hub-support-bundle-export, branched off 863047b (current main) — the spec said 6184f56 but actual current main HEAD is 863047b (one commit past the previous Wave 9 #120b slice)
  • cherry-pick of PR Wave 3 #72: Connection: Support bundle (tier-a) — vendor-neutral diagnostic snapshot exporter for RoamCore-installed HA instances #79 (e8a0718) lands cleanly with two trivial conflict resolutions (both purely additive, documented above)
  • homeassistant/packages/roamcore_support_bundle_hub.yaml exists, parses cleanly, declares 2 input_buttons + 3 sensors + 3 automations with rc-entity-naming compliant ids (the spec enumerated exactly 2 input_buttons — the "3 input_buttons" line in the acceptance bullet appears to be a typo)
  • homeassistant/packages/tests/test_support_bundle_hub.py exists, 11 tests, all PASS
  • scripts/checks/support-bundle-hub-smoke.sh exists, 4 assertions (YAML pre-check + rc-naming check + pytest run + plain-English OK summary), exits 0
  • scripts/check.sh +2 lines wiring the new smoke (between agent-actions-allowlist and connection-state-smoke)
  • docs/runbooks/hub-support-bundle.md exists, IKEA 5-step, opens with plain-English sentence, §1-§4 vanlifer-facing, §5 developer-facing only
  • bash scripts/check.sh --core-only → GREEN (exit 0)
  • No secrets in repo. No vmbr0 touch. No beaverbrook touch.
  • Commit messages have Context / Changes / Verification / Rollback sections + User-facing: line + GOLDEN.md alignment
  • branch pushed to origin/subagent/hub-support-bundle-export
  • PR opened (this PR)
  • Supersedes note: lands the open PR Wave 3 #72: Connection: Support bundle (tier-a) — vendor-neutral diagnostic snapshot exporter for RoamCore-installed HA instances #79 (e8a0718) onto current main + adds Hub-level export wiring

Supersedes

Lands the open PR #79 (e8a0718) onto current main + adds Hub-level export wiring. The recipe connection from PR #79 was never merged into main; this PR lands it cleanly and adds the operator-facing one-tap button on top.

RoamCore Engineer and others added 2 commits August 6, 2026 00:12
…rry-pick)

Context:
  - Wave 3 #72 / open PR #79 (commit e8a0718) shipped the tier-a
    support-bundle connection recipe (8 rc_support_bundle_* contract
    tiles + FIVE §8 MANDATORY automations + privacy audit + 8/8
    manifest-honesty tests) but the PR was never merged into main
    by the Wave 3 team. Per the Phase 3 directive §'Phase 3 delivery'
    + Priority #3 ('Bug on Hub = release-blocking') +
    roamcore.export_support_bundle service (already wired into
    homeassistant/custom_components/roamcore/ upstream of this
    slice), the support bundle exporter is the canonical recovery
    surface for the Hub — but the recipe connection + the Hub-level
    one-tap export wiring are both missing.
  - This commit lands PR #79 onto current main as a clean cherry-pick
    of commit e8a0718 with TWO trivial conflict resolutions (the
    only conflicts the cherry-pick encountered):
      1. docs/catalog/homelab/support-bundle.md — kept BOTH the
         legacy '## How it works / What RoamCore does behind the
         scenes.' stub added in current main AND the SUPERSEDED
         banner from PR #79. Both pieces belong: the SUPERSEDED
         banner is the audit cross-reference marker per the
         established connection-pipeline pattern; the How-it-works
         stub is current main's contribution.
      2. scripts/check.sh — placed the new
         connections/support-bundle/tests/test_connection_yml.py
         run_if_present entry BETWEEN the agent-actions-allowlist
         entry (already in current main) AND the
         connection-state-smoke.sh entry (already in current main),
         which is the same slot PR #79 intended.
    No semantic content was changed; both resolutions kept both
    pieces of context.

Changes:
  - connections/support-bundle/.gitignore (NEW, 3 lines)
  - connections/support-bundle/README.md (NEW, 179 lines)
  - connections/support-bundle/__init__.py (NEW, 241 lines)
  - connections/support-bundle/connection.yml (NEW, 810 lines,
    the tier-a recipe manifest)
  - connections/support-bundle/docs/recipe.md (NEW, 1127 lines,
    12 §sections — the canonical recipe)
  - connections/support-bundle/tests/test_connection_yml.py
    (NEW, 1335 lines, 8/8 manifest-honesty tests PASS)
  - docs/catalog/homelab/support-bundle.md (MOD — SUPERSEDED
    banner from PR #79 + preserved '## How it works' stub from
    current main; no content lost)
  - docs/mvp/features-build-status.md (MOD — Shipped (repo) row
    added per PR #79)
  - docs/reference/rc-entity-naming.md (MOD — support_bundle
    subsystem addition + back-fill of map / trip / bed_lift /
    ha_installer per PR #79)
  - scripts/check.sh (MOD — +1 line wiring
    connections/support-bundle/tests/test_connection_yml.py
    between agent-actions-allowlist and connection-state-smoke)

Verification:
  - python3 -m pytest connections/support-bundle/tests/ -v
    → 8/8 PASS
  - bash scripts/check.sh --core-only → ✅ GREEN (after Hub
    wiring lands in subsequent commits on this branch)
  - The two conflict resolutions are purely additive (kept
    both sides); no semantic divergence.

Rollback:
  - Single-commit revert: git revert <this-sha>. The Hub-level
    wiring (subsequent commits on this branch) depends on the
    connections/support-bundle/ directory landing, so a rollback
    of THIS commit alone would orphan the Hub YAML + tests +
    smoke + runbook — but those have their own commits and can
    be reverted independently. Safer rollback path: revert the
    Hub wiring commits first, then revert this cherry-pick.

GOLDEN.md alignment:
  - Serves P1 (Novice-first UX): the recipe ships the 8
    rc_support_bundle_* contract tiles that the novice dashboard
    surfaces as a 3-button overview + 3 status tiles + 2
    bookkeeping tiles + 1 privacy chip. The Hub-level wiring in
    subsequent commits on this branch extends that contract
    with 3 Hub-side buttons + 3 Hub-side sensors + 3 Hub-side
    automations wired to the same export_support_bundle service.
  - Serves P2 (Mission-critical connectivity): the support bundle
    is the canonical recovery surface when something on the Hub
    breaks — the exporter writes the 6 canonical files
    (install-info.txt + manifest.txt + provisioned.marker +
    openclaw-summary.json + openclaw-timeseries-catalog.json +
    setup-wizard-states.json) + the matching zip, which is what
    an operator needs to debug a non-booting Hub.
  - Serves P6 (OpenClaw as a first-class citizen): the recipe
    contract tiles are the same surface OpenClaw reads via the
    agent-actions-allowlist + openclaw-api connections; the
    privacy audit is the cross-cutting safety net.
  - Respects E1 (Customer-facing repo): no internal logs, no
    sensitive infra, no unfiltered agent run transcripts; the
    recipe is documented in connections/support-bundle/docs/
    recipe.md (12 §sections) and the operator howto at
    docs/howto/support-bundle.md is preserved.
  - Respects E2 (Documentation-driven): the recipe doc lives in
    connections/support-bundle/docs/recipe.md (the canonical
    place) + the catalog SUPERSEDED banner points to it.
  - Respects E4 (Git fast mode): direct-to-branch push on
    subagent/hub-support-bundle-export; one PR for Bernard per
    protocol.
  - Respects E7 (Naming follows rc-entity-naming.md): the 8
    rc_support_bundle_* contract tiles match the documented
    naming format (rc_<subsystem>_<object>_<metric>); the
    support_bundle subsystem is now OWNED by
    connections/support-bundle/ (per the existing wave-3
    connection-pipeline pattern).
  - Avoids ❌ 'Hand-configuring Victron instead of using
    capability discovery': the recipe is purely YAML + Python,
    no Victron or vendor-specific discovery is hand-configured.
  - Avoids ❌ 'Touching vmbr0 without explicit Bernard override':
    no networking config touched.
  - Avoids ❌ 'Committing secrets or HA tokens to the repo':
    no secrets in the recipe; the privacy audit is the
    cross-cutting safety net for the bundle output.
  - Avoids ❌ 'Wide PRs that mix networking + UI + infra
    changes': pure support-bundle-infra slice; Hub-level wiring
    lands in subsequent commits on this branch.
  - Avoids ❌ 'Pulling in unrelated project context': scope is
    strictly PR #79's changes (10 files, 3746 lines, +1/-1).

User-facing: When something goes wrong on the Hub, I can tap a single
button on the dashboard and get a single file that captures what's
going on inside the system, so I can send it to support without
having to explain my whole setup over chat.

Co-Authored-By: Claude <noreply@anthropic.com>
Context:
  - The previous commit landed PR #79 (the tier-a support-bundle
    connection recipe) onto current main. That recipe wraps the
    existing RoamCore-owned roamcore.export_support_bundle
    service but only at the recipe layer — the Hub-level dashboard
    wiring (the one-tap button + the visible readings + the
    behind-the-scenes rules that move data between them) is
    missing. Operators on the road still have to navigate to
    Settings → Developer Tools → Services to send a bundle.
  - Per Priority #3 in the directive ("Bug on Hub =
    release-blocking") + the doctrinal hooks (must not fail +
    super intuitive + critical infrastructure), the Hub-level
    wiring has to be (a) one tap, (b) auto-recover from
    failures (Failed status visible + retry), (c) plain-English
    error messages (not Python tracebacks), (d) idempotent
    (re-applying the YAML produces the same end state).
  - Also: the cherry-pick of PR #79 brought in a 33rd
    connections/*/connection.yml that does not carry the
    cross-cutting state field required by the
    connection-state-field cross-cutting test; this commit
    adds the field (default "Available" per the directive
    section "New connections ship in state=Available") AND
    bumps the cross-cutting test expected count 32 to 33 to
    match (the test own failure message acknowledges this is
    the expected pattern when adding a new connection).

Changes:
  - homeassistant/packages/roamcore_support_bundle_hub.yaml
    (NEW, ~150 lines): Hub-level one-tap export wiring.
    Declares 2 input_buttons (rc_support_bundle_hub_export +
    rc_support_bundle_hub_export_no_zip), 3 template sensors
    (rc_support_bundle_hub_last_export_path +
    rc_support_bundle_hub_last_export_at +
    rc_support_bundle_hub_status), 3 section-8 MANDATORY
    automations (button guard + success bookkeeping +
    failure capture), plus 4 internal input_text helpers +
    1 input_select status value. The button guard calls the
    existing roamcore.export_support_bundle service with
    zip=true or zip=false per the button flavor; the success
    automation populates path + at + Exported; the failure
    automation captures the plain-English error message +
    marks Failed.
  - homeassistant/packages/tests/test_support_bundle_hub.py
    (NEW, 11 tests, all PASS): pytest rig that asserts the
    package exists, parses cleanly, declares the 2 explicit
    input_buttons + 3 sensors + 3 automations with
    rc_support_bundle_hub_ prefixes, wires the buttons to
    the roamcore.export_support_bundle service with the zip
    data field, populates success path+at+Exported, captures
    failure error+Failed, every entity follows rc-entity-
    naming compliance, and the YAML is idempotent (re-loading
    produces the same dict + initial values are explicit).
  - scripts/checks/support-bundle-hub-smoke.sh (NEW, ~100
    lines): bash smoke wrapper. Runs a YAML pre-check +
    rc-naming check inline in Python, then runs the pytest
    with exit-code propagation, then prints a plain-English
    summary. Wired into scripts/check.sh as a run_if_present
    step between the existing agent-actions-allowlist entry
    and the connection-state-smoke entry.
  - docs/runbooks/hub-support-bundle.md (NEW, IKEA 5-step
    style): opens with a one-sentence plain-English intro
    (what it does for the user). Section 1 What it does
    (one sentence). Section 2 What you see on screen (the
    2 buttons + 3 readings, no jargon). Section 3 What you
    do (3 steps). Section 4 What to do if it goes wrong
    (one sentence: tap again + send the last error text
    in the community). Section 5 For operators and
    developers only — where the wiring lives + which
    automations do what + how the buttons call the exporter
    + the tests/smoke + the check.sh wire-in.
  - connections/support-bundle/connection.yml (MOD, +1
    line): added state: Available (the cherry-picked recipe
    was missing the cross-cutting state field required by
    the connection-state-field test).
  - homeassistant/packages/tests/test_connection_state_field.py
    (MOD): bumped the expected manifest count 32 to 33 + the
    matching docstring + the acceptance criterion sentence.
    The test own failure message acknowledges this is the
    expected pattern when adding a new connection.
  - scripts/check.sh (MOD, +2 lines): wired the new
    scripts/checks/support-bundle-hub-smoke.sh into the
    chain at the right slot (between the agent-actions-
    allowlist entry and the connection-state-smoke entry).
  - scripts/checks/connection-state-smoke.sh (MOD, 2 string
    updates): updated the inline comments + the OK message
    to say 33 connections instead of 32 connections to
    match the new count.

Verification:
  - python3 -m pytest homeassistant/packages/tests/test_support_bundle_hub.py -v
    → 11/11 PASS
  - bash scripts/checks/support-bundle-hub-smoke.sh
    → exit 0 (parses cleanly + rc-naming OK + 11/11 pytest)
  - python3 -m pytest connections/support-bundle/tests/ -v
    → 8/8 PASS (cherry-pick recipe tests still green)
  - python3 -m pytest homeassistant/packages/tests/test_connection_state_field.py -v
    → 22/22 PASS (cross-cutting state-field test now 33-aware)
  - bash scripts/check.sh --core-only
    → GREEN, exit 0
  - bash scripts/check.sh (full)
    → GREEN, exit 0

Rollback:
  - Single-commit revert: git revert <this-sha>. Reverting this
    commit alone leaves the cherry-pick commit (3a05da4) on
    main but removes the Hub-level wiring (so the recipe
    connection is in place but the one-tap button is not — the
    recipe-level contract tiles still work, but the Hub-level
    dashboard buttons do not). Safer rollback path: revert
    this commit FIRST, then revert 3a05da4 if PR #79 should
    not land on main. Rollback of this commit does not touch
    any Proxmox / HA / OpenWrt / networking config — pure
    repo-local data + Python + YAML.

GOLDEN.md alignment:
  - Serves P1 (Novice-first UX): the Hub-level package
    exposes the existing canonical exporter as a one-tap
    button on the dashboard. The runbook opens with one
    plain-English sentence and walks the user through
    sections 1-4 without any operator jargon. Failed status
    surfaces visibly so the user can see + retry
    (fail-softly UI).
  - Serves P2 (Mission-critical connectivity): the
    support-bundle export is the canonical recovery surface
    when something on the Hub breaks — exporting a bundle
    + sending it to support is the loop that gets the user
    un-stuck. Wiring this loop as one tap on the dashboard
    is the operational improvement.
  - Serves P6 (OpenClaw as a first-class citizen): the
    Hub-level buttons call the same roamcore.export_support_
    bundle service that OpenClaw already reads via the
    openclaw-api connection + the agent-actions-allowlist
    connection; the contract surface (8 recipe-level +
    3 Hub-level sensors + 3 section-8 MANDATORY automations)
    is shared between the dashboard and the agent.
  - Respects E1 (Customer-facing repo): no internal logs,
    no sensitive infra, no unfiltered agent run transcripts;
    the runbook lives in docs/runbooks/ (the established
    public-facing dev-runbook location per the
    docs/runbooks/traccar-first-boot-provisioning.md
    precedent).
  - Respects E2 (Documentation-driven): the runbook is the
    source of truth for the user; the package YAML is the
    source of truth for the data layer; the pytest rig is
    the source of truth for the contract assertions; the
    smoke check is the source of truth for the chain.
  - Respects E3 (Backup + rollback discipline): no Proxmox /
    HA / OpenWrt / networking change in this commit; pure
    repo-local code + data. Rollback is a single git revert
    with no state to recover.
  - Respects E4 (Git fast mode): direct-to-branch push on
    subagent/hub-support-bundle-export; one PR for Bernard
    per protocol.
  - Respects E7 (Naming follows rc-entity-naming.md): every
    new entity starts with rc_support_bundle_hub_ (Hub-level
    prefix), distinct from the recipe-level rc_support_bundle_
    prefix used by connections/support-bundle/. The new
    Hub-level subsystem is now OWNED by
    homeassistant/packages/roamcore_support_bundle_hub.yaml
    per the wave-3 connection-pipeline pattern.
  - Avoids "Hand-configuring Victron instead of using
    capability discovery": the package is purely YAML +
    Home Assistant native types (input_button + input_text +
    input_select + template + automation); no Victron or
    vendor-specific discovery is hand-configured.
  - Avoids "Touching vmbr0 without explicit Bernard
    override": no networking config touched.
  - Avoids "Committing secrets or HA tokens to the repo":
    no secrets in the package; the last_error input_text
    holds a plain-English error message surfaced from the
    service call (no token leakage).
  - Avoids "Wide PRs that mix networking + UI + infra
    changes": pure support-bundle-infra slice; the cherry-
    pick commit (3a05da4) is one slice, this commit is
    one slice, no cross-cutting changes.
  - Avoids "Pulling in unrelated project context": scope is
    strictly Hub-level support-bundle wiring (8 new/modified
    files), no other subsystems touched.

User-facing: When something stops working on the Hub, I can press
one button and get a single file that captures what is going on
inside the system, so I can send that file to support without
having to explain my whole setup over chat.

Co-Authored-By: Claude <noreply@anthropic.com>
(recipe-level contract from `connections/support-bundle/`)."""
data = _load_hub_yaml()

def _check(prefix: str, container_key: str, entity_ids: list[str]) -> None:
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant