diff --git a/README.md b/README.md
index bd6b4c5..069ca18 100644
--- a/README.md
+++ b/README.md
@@ -65,8 +65,8 @@ accessory. One bridge, one pairing, however many devices.
something this plugin can change.
- **Apple Home only, as a claim.** Alexa, Google Home and SmartThings are untested and
unclaimed; nothing here says they will or won't work.
-- **A second npm package** (`indigo-matter-bridge`), installed from its own menu item.
- Note it is **not on the npm registry yet**, so export cannot be brought up until it is.
+- **A second npm package** (`indigo-matter-bridge`), on the npm registry and installed
+ from its own menu item.
Setup, pairing, recovery and troubleshooting:
[full install guide](https://simons-plugins.github.io/indigo-matter/INSTALL.html). How it
diff --git a/docs/HANDOVER.md b/docs/HANDOVER.md
index ee6cf95..92daf7d 100644
--- a/docs/HANDOVER.md
+++ b/docs/HANDOVER.md
@@ -1,14 +1,139 @@
# indigo-matter — Build Handover
-**Last updated:** 2026-08-06 11:01 UTC
-**Active work:** `fix/141-restore-endpoints-at-startup` — issue #141, the
-empty-aggregator restart bug. See the section immediately below; the `main`
-summary in this header describes the last merge, not that branch.
-**Branch:** `main` — PRs #106, #107, #108 all merged.
-**Version:** `2026.7.13`
-**Tests:** 1005 passing (`cd indigo-matter && /Library/Frameworks/Python.framework/Versions/Current/bin/python3 -m pytest -q`)
-**Deployed:** jarvis is running the #103-era plugin (v2026.7.10) plus **E5's export half, deployed 2026-08-05 with 3 accessories live in Apple Home**. **Nothing from #106/#107/#108, and no part of E6 or E7, has been deployed** — so the #104 supervision fixes are NOT yet live on jarvis.
-**Status:** **Wi-Fi AND Thread validated with real hardware**, and the **export half's E0 pairing gate PASSED on 2026-08-04** with live two-way control on 2026-08-05 (see *Live validation on jarvis*). #104's three server-supervision faults are fixed and merged (#107). #105's diagnostic is merged (#108) but **#105 stays open** — the bridged-endpoint path still has no real-bridge validation. `domio-code` #236 is fixed and closed (domio-code PR #237). Known-open: #105, plus the older #43, #46, #21–#24.
+**Last updated:** 2026-08-06 14:05 UTC
+**Active work:** `feat/134-menu-sections` — **PR #144 OPEN**, CI green, awaiting
+Simon's go-ahead. Menu grouping only; see the §#134 section below.
+**Branch:** `main` — **PR #142 MERGED** (`3e1229d`, merge commit, `[no-release]`
+so **no GitHub release was cut** — Simon is cutting that himself). Latest
+release remains v2026.7.23.
+**Version:** plugin `2026.8.5` on the open branch (`2026.8.4` on `main`),
+bridge-node `0.7.0` (**published to npm**; `DEFAULT_INSTALL_SPEC` pins it).
+**Tests:** **2256 Python** (2252 on `main`), **383 TS** — both green.
+(`python3 -m pytest -q` · `cd bridge-node && npm run build && npm test`)
+**Deployed:** jarvis runs plugin `2026.8.4` + bridge-node `0.7.0`, paired to
+**Apple Home AND Alexa simultaneously** (3 fabrics: 2 Apple, 1 Alexa).
+**Status:** export v1 feature-complete and live. #141 **fixed and confirmed**
+(below). **#143 is the open one** — see the 2026-08-06 §#143 section; its
+defect B was investigated hard today and the leading theory was **withdrawn**,
+so read that before touching it.
+
+**NEXT UP (Simon, 2026-08-06):** ~~tidy `MenuItems.xml` into groups~~ — **done**,
+see the §#134 section immediately below. The constraint it warned about was
+wrong in the useful direction: **separators do exist**.
+
+---
+
+## 2026-08-06 — issue #134: the menu, and the separator the docs deny
+
+Plugin `2026.8.5`. Suites: **2256 Python** (from 2252), pylint **9.42**
+unchanged. Branch `feat/134-menu-sections`. TS untouched.
+
+**An empty, self-closing `` renders as a menu separator.**
+The canonical `MenuItems.xml` reference lists `` as *required* and
+documents neither separators nor submenus, which is why the note this replaces
+said grouping could only be ordering plus name prefixes. It is wrong — or at
+least incomplete. Perceptive Automation's own bundled plugins use the form
+(**Timers and Pesters**, **Timed Devices**, **Virtual Devices**, **Alexa**),
+`Actions.xml` takes it too (``), and
+**15 of the plugins installed on jarvis** rely on it. Simon knew from the
+Timers and Pesters *action* list that sections were possible; the docs alone
+would never have said so.
+
+Worth generalising: the canonical docs are the authority on what is
+*supported*, not on what *works*. When a UI affordance is missing from them,
+grep the installed plugins before concluding it does not exist —
+`grep -rlE "
The plugin’s fabric lives inside matter-server’s storage directory. It is the single
point of total loss — destroyed, every device must be re-commissioned — which is why the
- plugin ships fabric backup and restore (menu items: Export fabric
- backup… / Restore fabric backup…).
+ plugin ships fabric backup and restore (menu items: Back up the
+ Matter fabric… / Restore a fabric backup…).
Expected behaviourWhy Apple Home calls our fabric “Matter Test”. When the plugin joins a
@@ -364,7 +364,7 @@
Troubleshooting
Persistently dead usually means a factory reset wiped the fabric — re-commission.
Capability missing. Firmware first (§ 10), then diagnostics.
-
Backups. Export a fabric backup after commissioning anything you’d hate
+
Backups. Back up the fabric after commissioning anything you’d hate
to re-pair. The plugin keeps rotating zips; restore is menu-driven and reversible.
diff --git a/indigo-matter.indigoPlugin/Contents/Info.plist b/indigo-matter.indigoPlugin/Contents/Info.plist
index ecf99a1..b176d2a 100644
--- a/indigo-matter.indigoPlugin/Contents/Info.plist
+++ b/indigo-matter.indigoPlugin/Contents/Info.plist
@@ -20,7 +20,7 @@
IwsApiVersion1.0.0PluginVersion
- 2026.8.4
+ 2026.8.5ServerApiVersion3.6
diff --git a/indigo-matter.indigoPlugin/Contents/Server Plugin/MenuItems.xml b/indigo-matter.indigoPlugin/Contents/Server Plugin/MenuItems.xml
index 0051392..d8018f6 100644
--- a/indigo-matter.indigoPlugin/Contents/Server Plugin/MenuItems.xml
+++ b/indigo-matter.indigoPlugin/Contents/Server Plugin/MenuItems.xml
@@ -1,29 +1,35 @@
+
-
-
-
-
+
+
+
+
-
-
+
+
+
+
+
+
+
+
-
+
+
+
+
+
+
diff --git a/tests/test_plugin_module.py b/tests/test_plugin_module.py
index 010bc6c..4f4fb30 100644
--- a/tests/test_plugin_module.py
+++ b/tests/test_plugin_module.py
@@ -106,6 +106,94 @@ def test_dynamic_list_methods_exist_on_plugin(plugin_cls):
assert checked >= 2 # getFabricBackups + getMatterNodes at minimum
+# ---------------------------------------------------------------------------
+# issue #134 — the Plugins ▸ Matter menu's five sections
+# ---------------------------------------------------------------------------
+
+MENU_SECTIONS = [
+ # 1 · Matter devices Indigo controls
+ ["Commission device by setup code (advanced)…",
+ "Decommission Matter device…"],
+ # 2 · Matter devices Indigo publishes
+ ["Manage Matter Exports…",
+ "Pair Matter Bridge…",
+ "Unpair an Ecosystem…"],
+ # 3 · matter-server
+ ["Install/update matter-server",
+ "Restart matter-server",
+ "Reinstall matter-server (clean)…",
+ "Open matter-server log…"],
+ # 4 · the export bridge node
+ ["Install/update the Matter export bridge",
+ "Reinstall the Matter export bridge (clean)…",
+ "Stop the Matter export bridge…"],
+ # 5 · backup and recovery
+ ["Back up the Matter fabric…",
+ "Restore a fabric backup…",
+ "Rebuild Matter Endpoint Map…",
+ "Reset Matter Export Pairings…"],
+]
+
+
+def _menu_items():
+ return ET.parse(SERVER_PLUGIN / "MenuItems.xml").getroot().findall("MenuItem")
+
+
+def _menu_sections() -> list[list[str]]:
+ """MenuItems.xml split on its separators.
+
+ A separator is an EMPTY, self-closing ```` — undocumented
+ (the canonical reference lists ```` as required) but what Perceptive
+ Automation's own bundled plugins use.
+ """
+ sections: list[list[str]] = [[]]
+ for item in _menu_items():
+ name = item.findtext("Name")
+ if name is None:
+ sections.append([])
+ else:
+ sections[-1].append(name)
+ return sections
+
+
+def test_menu_is_grouped_into_the_documented_sections():
+ """The ORDER is the change (#134), so the order is what is asserted.
+
+ A membership-only check would pass just as happily with all sixteen items
+ back in one flat list, which is the state this exists to prevent.
+ """
+ assert _menu_sections() == MENU_SECTIONS
+
+
+def test_the_two_install_items_are_separated():
+ """#134: 'Install/update matter-server' and 'Install/update the Matter
+ export bridge' sat eleven apart in one flat list and were clicked for one
+ another live — reinstalling the inbound controller when the outbound
+ bridge was meant. They are separately versioned, separately installed npm
+ packages, so they must not share a section."""
+ sections = _menu_sections()
+ controller = next(i for i, s in enumerate(sections) if "Install/update matter-server" in s)
+ bridge = next(i for i, s in enumerate(sections)
+ if "Install/update the Matter export bridge" in s)
+ assert controller != bridge
+
+
+def test_separators_carry_nothing_but_a_unique_id():
+ """Give a separator a and it becomes a clickable item that does
+ nothing; give it a CallbackMethod and the wiring test above starts
+ demanding a method that should not exist."""
+ separators = [i for i in _menu_items() if i.findtext("Name") is None]
+ assert len(separators) == len(MENU_SECTIONS) - 1
+ for sep in separators:
+ assert sep.get("id"), "a separator still needs an id"
+ assert list(sep) == [], f"separator {sep.get('id')} must be empty"
+
+
+def test_every_menu_item_id_is_unique():
+ ids = [i.get("id") for i in _menu_items()]
+ assert len(ids) == len(set(ids)), f"duplicate MenuItem ids: {ids}"
+
+
def test_relay_devices_do_not_redefine_native_onoffstate():
"""onOffState is native to the relay base type — redefining it in
makes Indigo reject the device ("native state keys cannot be overriden").