feat(naming): Matter controller / Matter bridge terminology (#147) - #158
Conversation
The two halves get role names in every user-facing surface: the inbound managed matter-server process is "the Matter controller (matter-server)" and the outbound export node is "the Matter bridge". - MenuItems.xml: 8 <Name> renames (ids/callbacks untouched, section order unchanged); section comments follow - The "Matter export:" log namespace (92 lines) -> "Matter bridge:"; "Matter export allow-list:" and per-entry "Matter export for X" wording kept (they name the devices/feature, not the subsystem) - PluginConfig.xml labels: role name for the subsystem, package name kept where it identifies the package - Pinned menu-name constants (INSTALL_MENU / BRIDGE_INSTALL_MENU) and every "Plugins > Matter > ..." log reference follow the new names - README/INSTALL/MATTER: renamed-menu references + the two-halves intro; INSTALL's troubleshooting table's quoted log lines updated to match the real output; headings/anchors untouched (no restructure) - Kept: matter-server as the literal npm package name in technical contexts, "bridge node" (already right), all ids/prefs/paths/labels, Manage Matter Exports... / Pair Matter Bridge... / fabric + map items Suites: 2289 Python, 405 TS (bridge-node untouched). Version 2026.8.14 -> 2026.8.15. Closes #147 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018Unpt5UPRdLoZkahH6a4gL
📝 WalkthroughWalkthroughThe PR standardizes Matter terminology across plugin menus, configuration, runtime messages, documentation, and tests. It also clarifies selected bridge recovery messages, adds retired-name regression checks, and increments the plugin version. ChangesMatter controller and bridge naming
Estimated code review effort: 2 (Simple) | ~10 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
…m impossible (#147) The review found the sweep's structural blind spot: menu names WRAPPED across source lines, invisible to any grep of source text. Two were blocking — plugin.py's last-ecosystem-unpair warning sent the user to 'Reset Matter Export Pairings…' (renamed this PR), and INSTALL.md carried the same sentence with the same wrap. Also folded in: the bridge-node README (npm package page) and HANDOVER's seven references (the E2E repro steps must name real menus; historical log QUOTES keep their old prefixes, menu names do not). New regression tests: RETIRED_MENU_NAMES scanned against every Server Plugin module's AST string constants (adjacent literals concatenate, so wrapping cannot hide) and against current docs with newlines collapsed. The doc scan caught an EIGHTH straggler on its first run (HANDOVER's not-filed polish note quoting "Restart matter-server"). The frozen PRDs are deliberately excluded — their milestone tables name menus as they were when each milestone landed. Polish from review: the config header tautology ("Matter bridge (Indigo as a bridge)" -> "Matter bridge (export)"), the checkbox colon restored, the remote-mode warning's parenthetical moved to the noun it names, README paragraph reflowed. Suites: 2302 Python (+13 scan tests), 405 TS. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018Unpt5UPRdLoZkahH6a4gL
There was a problem hiding this comment.
Actionable comments posted: 5
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
indigo-matter.indigoPlugin/Contents/Server Plugin/plugin.py (2)
2585-2612: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick winCheck the LaunchAgent state before reporting success.
ensure_installed()applying the plist does not prove that the bridge process remains running.retry_now()only reports whether the client accepted a retry request.The normal start path calls
run_state()and distinguishesRUNNING,UNKNOWN, andLOADED_NOT_RUNNING. This install path skips that check and can log “installed and restarted” while the node has already exited.Call
run_state()afterensure_installed()orrestart(). Report the actual state and include the existing diagnosis when the node is not running.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@indigo-matter.indigoPlugin/Contents/Server` Plugin/plugin.py around lines 2585 - 2612, Update the installation success flow around ensure_installed(), restart(), and retry_now() to call bridge_process.run_state() before reporting success. Distinguish RUNNING, UNKNOWN, and LOADED_NOT_RUNNING like the normal start path, report the actual LaunchAgent state, and include the existing diagnosis when the bridge is not running; do not let retry_now() alone determine the success message.
2573-2594: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick winKeep bridge startup under the export lifecycle owner.
This path starts the LaunchAgent whenever the allow-list is non-empty. It does not check
self.export_bridge.enabled.If
exportEnabledis false, the install menu can start the bridge after the configuration flow stopped it. The bridge can then run with an empty client and stale exported endpoints.This path also bypasses
ExportBridge._ensure_agent(). That method sets_agent_started. If the menu stop action or a previous state cleared that latch, this install path can start the agent but leave the latch false. A later empty transition then skips_stop_agent(), so the LaunchAgent remains running with no exports.Gate this path on the export switch. Route successful startup through a shared ownership seam that records the agent as started by the current session.
As per coding guidelines,
**/export_bridge.pyrequires LaunchAgent start and stop transitions to follow the allow-list and to stop only an agent started by the current session.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@indigo-matter.indigoPlugin/Contents/Server` Plugin/plugin.py around lines 2573 - 2594, Gate the startup path around bridge_process.ensure_installed() and restart() on self.export_bridge.enabled, and route successful startup through ExportBridge’s shared _ensure_agent() ownership seam so _agent_started is set for the current session. Preserve the existing empty-export early return and installation error handling, ensuring this path cannot start the LaunchAgent when exports are disabled.Source: Coding guidelines
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@docs/INSTALL.md`:
- Around line 110-112: Synchronize the renamed user-facing documentation: in
docs/INSTALL.md lines 110-112, replace abbreviated menu entries with “Restart
the Matter controller,” “Reinstall the Matter controller (clean)…,” “Reinstall
the Matter bridge (clean)…,” and “Stop the Matter bridge….” In docs/HANDOVER.md
lines 983-985, replace “Matter export bridge installed” with the current “Matter
bridge installed” text.
- Line 701: Update the clean reinstall documentation in the Matter controller
troubleshooting table to describe per-package removal rather than deleting the
shared ~/indigo-matter/node_modules directory. State that shared node_modules,
package-lock.json, and the .indigo-node installation stamp are preserved while
installed Node packages are removed and reinstalled individually.
- Line 378: Update the fenced code block containing Event Log output in the
installation documentation to declare the text language identifier, using
```text instead of an untyped fence to satisfy markdownlint MD040.
- Around line 6-12: Update the bridge-related instructions in docs/INSTALL.md
around the later menu setup and installation sections to consistently state that
indigo-matter-bridge is already published and installable from the registry,
matching the publication status described near the document introduction; remove
or revise any claim that menu resolution depends on future publication.
In `@indigo-matter.indigoPlugin/Contents/Server` Plugin/plugin.py:
- Around line 2566-2573: In the bridge installation flow around
LaunchAgent.install(), persist agent.resolved_bin_dir to the plugin preferences
key nodeBinDir, flush the preferences, then construct BridgeProcess using the
updated pinned preferences. Keep the existing failure and stopping checks
unchanged.
---
Outside diff comments:
In `@indigo-matter.indigoPlugin/Contents/Server` Plugin/plugin.py:
- Around line 2585-2612: Update the installation success flow around
ensure_installed(), restart(), and retry_now() to call
bridge_process.run_state() before reporting success. Distinguish RUNNING,
UNKNOWN, and LOADED_NOT_RUNNING like the normal start path, report the actual
LaunchAgent state, and include the existing diagnosis when the bridge is not
running; do not let retry_now() alone determine the success message.
- Around line 2573-2594: Gate the startup path around
bridge_process.ensure_installed() and restart() on self.export_bridge.enabled,
and route successful startup through ExportBridge’s shared _ensure_agent()
ownership seam so _agent_started is set for the current session. Preserve the
existing empty-export early return and installation error handling, ensuring
this path cannot start the LaunchAgent when exports are disabled.
🪄 Autofix
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: fb95842e-82f1-41ec-aeff-3e6c732ee2a9
📒 Files selected for processing (16)
README.mdbridge-node/README.mddocs/HANDOVER.mddocs/INSTALL.mddocs/MATTER.mdindigo-matter.indigoPlugin/Contents/Info.plistindigo-matter.indigoPlugin/Contents/Server Plugin/MenuItems.xmlindigo-matter.indigoPlugin/Contents/Server Plugin/PluginConfig.xmlindigo-matter.indigoPlugin/Contents/Server Plugin/bridge_agent.pyindigo-matter.indigoPlugin/Contents/Server Plugin/export_bridge.pyindigo-matter.indigoPlugin/Contents/Server Plugin/export_catalog.pyindigo-matter.indigoPlugin/Contents/Server Plugin/fabric_backup.pyindigo-matter.indigoPlugin/Contents/Server Plugin/launch_agent.pyindigo-matter.indigoPlugin/Contents/Server Plugin/plugin.pyindigo-matter.indigoPlugin/Contents/Server Plugin/server_process.pytests/test_plugin_module.py
| - **The Matter controller** (`matter-server`) — the inbound half. The plugin drives it so | ||
| that Matter devices in your house become Indigo devices. **Steps 1–4**, and this is what | ||
| a normal install needs. These steps are derived from the live | ||
| managed-LaunchAgent bring-up on the reference Indigo server. | ||
| - **the Matter export bridge** — the optional outbound half, which publishes | ||
| selected *Indigo* devices to Apple Home as Matter accessories. **Steps E1–E4**. | ||
| Its npm package (`indigo-matter-bridge`) is on the registry and installs from | ||
| its own menu item — see | ||
| - **The Matter bridge** (`indigo-matter-bridge`) — the optional outbound half, which | ||
| publishes selected *Indigo* devices to Apple Home as Matter accessories. **Steps E1–E4**. | ||
| Its npm package is on the registry and installs from its own menu item — see |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
status="$(curl -sS -o /dev/null -w '%{http_code}' \
https://registry.npmjs.org/indigo-matter-bridge)"
printf 'indigo-matter-bridge registry status: %s\n' "$status"
# 200 means published; 404 means unpublished.Repository: simons-plugins/indigo-matter
Length of output: 208
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '--- docs/INSTALL.md excerpts ---\n'
sed -n '1,18p' docs/INSTALL.md
printf '\nLine 302 area:\n'
sed -n '296,307p' docs/INSTALL.md
printf '\nLine 741 area:\n'
sed -n '736,746p' docs/INSTALL.md
printf '\n--- HANDOVER excerpts ---\n'
sed -n '830,838p' docs/HANDOVER.md
printf '\n--- package metadata ---\n'
if [ -f package.json ]; then cat package.json; fiRepository: simons-plugins/indigo-matter
Length of output: 257
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '%s\n' '--- docs/INSTALL.md excerpts ---'
sed -n '1,18p' docs/INSTALL.md
printf '%s\n' 'Line 302 area:'
sed -n '296,307p' docs/INSTALL.md
printf '%s\n' 'Line 741 area:'
sed -n '736,746p' docs/INSTALL.md
printf '%s\n' '--- HANDOVER excerpts ---'
sed -n '830,838p' docs/HANDOVER.md
printf '%s\n' '--- package metadata ---'
if [ -f package.json ]; then cat package.json; fiRepository: simons-plugins/indigo-matter
Length of output: 5290
Align the bridge package publication status.
indigo-matter-bridge is published, but docs/INSTALL.md later says the menu cannot resolve until publication. Update the conflicting steps in docs/INSTALL.md lines 302 and 741 to match the published-only message in lines 6–12, or remove the registry statement if the bridge is intended to be unpublished.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@docs/INSTALL.md` around lines 6 - 12, Update the bridge-related instructions
in docs/INSTALL.md around the later menu setup and installation sections to
consistently state that indigo-matter-bridge is already published and
installable from the registry, matching the publication status described near
the document introduction; remove or revise any claim that menu resolution
depends on future publication.
| | 3 | *Install/update the Matter controller (matter-server)*, *Restart…*, *Reinstall (clean)…*, *Open the Matter controller log…* | The inbound controller's own plumbing | | ||
| | 4 | *Install/update the Matter bridge*, *Reinstall… (clean)…*, *Stop…* | The outbound bridge node's own plumbing | | ||
| | 5 | *Back up the Matter fabric…*, *Restore a fabric backup…*, *Rebuild Matter Endpoint Map…*, *Reset Matter Bridge Pairings…* | Backup and recovery. The last two are destructive — see [the two destructive recovery actions](#the-two-destructive-recovery-actions) | |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Synchronize the renamed user-facing documentation.
The terminology update leaves two documentation sites that do not match the current menu and log contracts.
docs/INSTALL.md#L110-L112: replace abbreviated entries withRestart the Matter controller,Reinstall the Matter controller (clean)…,Reinstall the Matter bridge (clean)…, andStop the Matter bridge….docs/HANDOVER.md#L983-L985: replaceMatter export bridge installedwith the currentMatter bridge installedtext.
📍 Affects 2 files
docs/INSTALL.md#L110-L112(this comment)docs/HANDOVER.md#L983-L985
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@docs/INSTALL.md` around lines 110 - 112, Synchronize the renamed user-facing
documentation: in docs/INSTALL.md lines 110-112, replace abbreviated menu
entries with “Restart the Matter controller,” “Reinstall the Matter controller
(clean)…,” “Reinstall the Matter bridge (clean)…,” and “Stop the Matter
bridge….” In docs/HANDOVER.md lines 983-985, replace “Matter export bridge
installed” with the current “Matter bridge installed” text.
| It installs into the same `~/indigo-matter` npm root as matter-server, with the | ||
| same Node, and takes a minute; watch the Event Log. On success you will see: | ||
|
|
||
| ``` |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
Add a language identifier to the fenced block.
Line [378] contains Event Log output but has no fence language. Use text to satisfy markdownlint rule MD040.
Proposed fix
-```
+```text📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| ``` |
🧰 Tools
🪛 markdownlint-cli2 (0.23.2)
[warning] 378-378: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@docs/INSTALL.md` at line 378, Update the fenced code block containing Event
Log output in the installation documentation to declare the text language
identifier, using ```text instead of an untyped fence to satisfy markdownlint
MD040.
Source: Linters/SAST tools
| | Log says the server started, but it behaves like the old one | A matter-server started **outside** this plugin — or with a different `--storage-path` — is holding the port. The plugin's orphan reaper only matches servers using *its* package dir and storage path, so it never stops that one, and the fresh instance dies on the port bind (`EADDRINUSE` in `matter-server.err.log`) while the stray keeps answering on 5580 with its own arguments. (A stray with the *same* storage path fails earlier and differently — see the storage-lock row above.) | Find the owner with `lsof -nP -iTCP:5580 -sTCP:LISTEN`, confirm with `ps -ww -o pid=,command= -p <PID>`, then `kill <PID>`. Note matter-server may exit 0 on that failure, which `KeepAlive` treats as a clean exit, so launchd will not respawn it — use Plugins ▸ Matter ▸ **Restart matter-server** afterwards. | | ||
| | New managed LaunchAgent doesn't take effect | A prior agent with the same label is still loaded. | Plugins ▸ Matter ▸ **Restart the Matter controller** (it reloads the plist and stops strays), or `launchctl bootout gui/$(id -u)/com.simons-plugins.indigo-matter` then restart the plugin. | | ||
| | `Server failed to start [storage-lock] Storage is locked by another process (pid N)` | A second matter-server (orphaned from an earlier LaunchAgent) is still running and holds the storage lock. | The plugin now reaps such strays on start/restart — Plugins ▸ Matter ▸ **Restart the Matter controller**. If it persists, reboot the Mac (kills the orphan; the lock goes stale and is reclaimed automatically). | | ||
| | matter-server won't start after an upgrade (wedged install) | The installed package is damaged or incompatible. | Plugins ▸ Matter ▸ **Reinstall the Matter controller (clean)…** — deletes `~/indigo-matter/node_modules` and reinstalls fresh. Your commissioned devices are kept (the fabric/storage is untouched). | |
There was a problem hiding this comment.
🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win
Document the per-package clean reinstall behavior.
Line [701] says that the clean reinstall deletes the shared ~/indigo-matter/node_modules directory. This conflicts with the launch-agent contract, which removes installed packages individually and preserves shared installation files. If the implementation follows the contract, this documentation is false. If it follows this text, it violates the required safety behavior.
Proposed fix
-| matter-server won't start after an upgrade (wedged install) | The installed package is damaged or incompatible. | Plugins ▸ Matter ▸ **Reinstall the Matter controller (clean)…** — deletes `~/indigo-matter/node_modules` and reinstalls fresh. Your commissioned devices are kept (the fabric/storage is untouched). |
+| matter-server won't start after an upgrade (wedged install) | The installed package is damaged or incompatible. | Plugins ▸ Matter ▸ **Reinstall the Matter controller (clean)…** — removes the installed package individually and reinstalls it. Shared `node_modules`, `package-lock.json`, and the `.indigo-node` installation stamp are retained. Your commissioned devices are kept (the fabric/storage is untouched). |As per path instructions, **/launch_agent.py: “Remove installed Node packages individually; never delete shared node_modules or package-lock.json, and retain the shared .indigo-node installation stamp.”
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| | matter-server won't start after an upgrade (wedged install) | The installed package is damaged or incompatible. | Plugins ▸ Matter ▸ **Reinstall the Matter controller (clean)…** — deletes `~/indigo-matter/node_modules` and reinstalls fresh. Your commissioned devices are kept (the fabric/storage is untouched). | | |
| | matter-server won't start after an upgrade (wedged install) | The installed package is damaged or incompatible. | Plugins ▸ Matter ▸ **Reinstall the Matter controller (clean)…** — removes the installed package individually and reinstalls it. Shared `node_modules`, `package-lock.json`, and the `.indigo-node` installation stamp are retained. Your commissioned devices are kept (the fabric/storage is untouched). | |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@docs/INSTALL.md` at line 701, Update the clean reinstall documentation in the
Matter controller troubleshooting table to describe per-package removal rather
than deleting the shared ~/indigo-matter/node_modules directory. State that
shared node_modules, package-lock.json, and the .indigo-node installation stamp
are preserved while installed Node packages are removed and reinstalled
individually.
Source: Path instructions
| if not agent.install(): | ||
| self.logger.error( | ||
| "Install/update of the Matter export bridge did not complete — see the error " | ||
| "Install/update of the Matter bridge did not complete — see the error " | ||
| "above. Nothing was changed; retry when resolved.") | ||
| return | ||
| if self._stopping: # plugin is tearing down — don't mutate its state | ||
| return | ||
| self.bridge_process = bridge_agent.BridgeProcess(dict(self.pluginPrefs), self.logger) |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟠 Major | ⚡ Quick win
Persist the Node used for the bridge installation.
LaunchAgent.install() runs npm with agent.resolved_bin_dir. This path does not save that directory to nodeBinDir, unlike the controller installation at Lines 1362-1367.
On a host with multiple Node installations, the next BridgeProcess can select another Node. Native modules can then fail to load, and the bridge can crash-loop.
Save agent.resolved_bin_dir, flush the plugin preferences, and construct the new BridgeProcess from the pinned preferences.
Suggested fix
if not agent.install():
self.logger.error(
"Install/update of the Matter bridge did not complete — see the error "
"above. Nothing was changed; retry when resolved.")
return
+ self.pluginPrefs["nodeBinDir"] = agent.resolved_bin_dir
+ indigo.server.savePluginPrefs()
if self._stopping: # plugin is tearing down — don't mutate its state
return
self.bridge_process = bridge_agent.BridgeProcess(dict(self.pluginPrefs), self.logger)🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@indigo-matter.indigoPlugin/Contents/Server` Plugin/plugin.py around lines
2566 - 2573, In the bridge installation flow around LaunchAgent.install(),
persist agent.resolved_bin_dir to the plugin preferences key nodeBinDir, flush
the preferences, then construct BridgeProcess using the updated pinned
preferences. Keep the existing failure and stopping checks unchanged.
Closes #147.
The two halves get role names in every user-facing surface, per the issue: the inbound managed `matter-server` process is the Matter controller (matter-server), the outbound export node is the Matter bridge.
Menu (ids/callbacks/order untouched)
Everywhere else
Suites: 2289 Python / 405 TS green. Version `2026.8.15`.
🤖 Generated with Claude Code
https://claude.ai/code/session_018Unpt5UPRdLoZkahH6a4gL
Summary by CodeRabbit