Skip to content
2 changes: 1 addition & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ loop→Indigo writes go straight through `device_sync.apply_states` (thread-safe
| `server_process.py` | `ServerProcess` = the matter-server (controller) specialisation of `LaunchAgent`: its prefs, its argv, its pinned version. Gated by the `serverLocation` pref — the config asks "is matter-server on this Mac?"; `local` (turnkey default) manages it here on loopback, `remote` connects to a server elsewhere. `manageLaunchAgent`/host/port are derived from that in `startup` (see `plugin.py:server_location`) |
| `commission_jobs.py` | Commissioning job state machine (API.md §3.2/§3.3) |
| `device_sync.py` | Node↔Indigo reconciliation + state/command seams |
| `fabric_backup.py` | Fabric backup/restore for the matter-server storage dir (issue #26) — zip snapshots into a sibling `backups/`, move-aside restore, retention prune. **Since E5 it also covers the export bridge node's storage dir** (`identity.json` + `endpoint-map.json`) under a reserved `bridge-node/` archive prefix, so an old controller-only archive still restores; a missing/empty bridge dir is skipped **with a WARNING naming the path**, because that path is derived from the controller's rather than configured. **Restore deliberately does not extract the bridge members** — that needs the node stopped and there is no stop seam until E7 — so it reports and skips. Pure + injectable (paths, clock, `stop()/start()` control) so it unit-tests against `tmp_path` |
| `fabric_backup.py` | Fabric backup/restore for the matter-server storage dir (issue #26) — zip snapshots into a sibling `backups/`, move-aside restore, retention prune. **Since E5 it also covers the export bridge node's storage dir** (`identity.json` + `endpoint-map.json`) under a reserved `bridge-node/` archive prefix, so an old controller-only archive still restores; a missing/empty bridge dir is skipped **with a WARNING naming the path**, because that path is derived from the controller's rather than configured. **Since #136, restore extracts the bridge members too** — it stops the bridge node (via the same `stop()`/`start()` seam, now given a second control), extracts its half alongside the controller fabric, and restarts it afterwards if it was running; a bridge-restart failure never rolls back an otherwise-good controller restore (XG5). It still falls back to reporting-and-skipping the bridge side (loudly, with the manual recipe) when there is no bridge control, no usable bridge storage path, or the path overlaps the controller's. Pure + injectable (paths, clock, two `stop()/start()` controls) so it unit-tests against `tmp_path` |
| `http_handlers.py` | Domio API routing (served over IWS, not aiohttp) |
| `matter_model.py` | Parse matter-server node dict → node/endpoint objects |
| `matter_handlers/` | One `ClusterHandler` per cluster + registry (OnOff in v1) |
Expand Down
4 changes: 2 additions & 2 deletions docs/HANDOVER.md
Original file line number Diff line number Diff line change
Expand Up @@ -1500,7 +1500,7 @@ New, and deferred on purpose:

3. **Restoring the bridge-node storage from a backup** needs a bridge `stop()`/
`start()` control — E7's launchd agent. Backup is done; restore reports and
skips (above).
skips (above). [wired by #136, v2026.9.0]

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Remove the stale “reports and skips” status.

Line 1503 still describes the pre-#136 behavior. fabric_backup.restore_backup now extracts bridge members when bridge control and storage are usable. Replace the stale sentence with the conditional behavior, or remove this item from the deferred list.

🤖 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/HANDOVER.md` at line 1503, Update the deferred-list entry around “skips”
in the handover documentation to remove the stale “reports and skips” status.
Reflect that fabric_backup.restore_backup extracts bridge members when bridge
control and storage are usable, or remove the item entirely from the deferred
list.

4. **Pinning endpoint numbers from the map** (the `Endpoint.Configuration.number`
finding above) — a protocol-level decision about what `preserveEndpointNumbers`
promises, not a patch.
Expand Down Expand Up @@ -2136,7 +2136,7 @@ restore it). Don't re-derive them.
| #133 | `get_pairing` races the last-fabric self-reset → spurious error |
| #134 | Group the two Install/update menu items (11 apart, confusable) |
| #135 | Reset reconnect backoff after a successful install (29s dead wait) |
| #136 | Wire bridge-storage restore (E7 gave it the stop seam it needed) |
| ~~#136~~ | ~~Wire bridge-storage restore (E7 gave it the stop seam it needed)~~ — done, `fabric_backup.restore_backup` takes a second `stop()/start()` control |
| #137 | Docs site has no route to the export half (matter.html/index.html) |
| #138 | Three screenshots wanted for the walkthrough |
| #139 | Live validation tracker + the solved room remedy |
Expand Down
16 changes: 12 additions & 4 deletions docs/INSTALL.md
Original file line number Diff line number Diff line change
Expand Up @@ -634,10 +634,18 @@ Losing *that* is the number-one real-world cause of duplicated accessories: ever
ecosystem re-creates every accessory, and their names, rooms and automations go
with the old ones.

**Back up the Matter fabric…** includes this directory in the zip. **Restore does not
yet put it back** — it reports the bridge files it found and skips them, because
restoring them safely needs the bridge node stopped and that wiring is a
follow-up. So for now, if you need to restore the bridge side, copy
**Back up the Matter fabric…** includes this directory in the zip, and **Restore a
fabric backup…** puts it back too: it stops the bridge node (if it is running)
before restoring, extracts its half of the archive alongside the controller
fabric, and starts it again afterwards. If the bridge node was not running
before the restore, it is left stopped — it starts again the next time
something is exported. Its previous storage is moved aside, never deleted, the
same as the controller's. The restored storage holds the accessory identities
and endpoint numbers as of that backup; if a paired ecosystem has changed
since, the bridge REPORTS the drift in the log rather than renumbering
anything. If restore has no way to stop the bridge node (for example, no
LaunchAgent control could be built), it falls back to the old behaviour: it
reports the bridge files it found and skips them, and you can copy
`bridge-node/` out of the zip by hand with the bridge stopped (*Stop the Matter
bridge…*). Your export **list** is not in here at all — it lives in the
plugin's preferences and rides along with Indigo's own database backup.
Expand Down
2 changes: 1 addition & 1 deletion indigo-matter.indigoPlugin/Contents/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
<key>IwsApiVersion</key>
<string>1.0.0</string>
<key>PluginVersion</key>
<string>2026.8.24</string>
<string>2026.9.0</string>
<key>ServerApiVersion</key>
<string>3.6</string>
</dict>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -325,14 +325,14 @@ You normally never need this: the bridge starts and stops with your export list.
<CallbackMethod>menuRestoreFabricBackup</CallbackMethod>
<ConfigUI>
<Field id="warning" type="label">
<Label>Restoring REPLACES the current fabric and restarts matter-server. The current fabric is moved aside (not deleted) so a bad restore is reversible. Requires 'Manage LaunchAgent' to be on.</Label>
<Label>Restoring REPLACES the current fabric and restarts matter-server. If the backup includes the Matter bridge node's storage, that is restored too — if the bridge node is running it is stopped and started around the swap; a stopped bridge stays stopped. The current fabric (and bridge storage, if restored) is moved aside, not deleted, so a bad restore is reversible. Requires 'Manage LaunchAgent' to be on.</Label>
</Field>
<Field id="backup" type="menu">
<Label>Backup to restore:</Label>
<List class="self" method="getFabricBackups"/>
</Field>
<Field id="confirm" type="checkbox" defaultValue="false">
<Label>I understand this replaces the current fabric and will restart matter-server.</Label>
<Label>I understand this replaces the current fabric (and the Matter bridge node's storage, if the backup has it) and will restart matter-server.</Label>
Comment on lines +328 to +335

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Keep bridge-restore documentation consistent with the runtime contract.

The implementation can restore the controller while skipping bridge storage when bridge control is unavailable, the bridge storage path is unusable, or the paths overlap. The confirmation and documentation currently do not state this complete fallback behavior.

  • indigo-matter.indigoPlugin/Contents/Server Plugin/MenuItems.xml#L328-L335: qualify the confirmation text so archive presence does not imply unconditional bridge replacement.
  • docs/INSTALL.md#L637-L648: document all fallback conditions and the manual-copy procedure.
  • docs/HANDOVER.md#L1503-L1503: replace the stale “reports and skips” status or remove the item from the deferred list.
Proposed wording
- If the backup includes the Matter bridge node's storage, that is restored too.
+ If the backup includes the Matter bridge node's storage, it is restored when
+ bridge control and a usable, non-overlapping storage path are available.
+ Otherwise, the bridge files are reported and skipped for manual recovery.
📍 Affects 3 files
  • indigo-matter.indigoPlugin/Contents/Server Plugin/MenuItems.xml#L328-L335 (this comment)
  • docs/INSTALL.md#L637-L648
  • docs/HANDOVER.md#L1503-L1503
🤖 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/MenuItems.xml around lines
328 - 335, Qualify the bridge-restore confirmation in MenuItems.xml so bridge
storage replacement is conditional on controller availability, a usable
non-overlapping storage path, and archive presence. Update docs/INSTALL.md to
document every fallback condition and the manual-copy procedure; update
docs/HANDOVER.md to remove or correct the stale deferred “reports and skips”
item.

</Field>
</ConfigUI>
</MenuItem>
Expand Down
Loading
Loading