review: address Opus findings on the subdevice flat-href fallback

- coordinator: _poll_subdevice_flat_hrefs called sess.pace() outside its
  try block and re-read self._session instead of using the caller's
  already-None-checked reference -- a session closed mid-poll (async_close()
  doesn't hold _session_lock) could crash the whole poll cycle instead of
  just dropping that one sibling. Now takes sess from the caller and guards
  pace() the same as get().
- coordinator: skip flat hrefs already covered by the hot/warm sub-poll
  tiers -- those are refreshed every few seconds by _run_subpolls already,
  so re-fetching them again on the once-per-summary-poll flat pass only adds
  GETs, not freshness. Matters because, unlike the Collection path (always
  one GET), a flat subdevice's summary-poll cost scales with its href count.
- subdevices.py module docstring: corrected an overclaim inherited from PR
  #199 that GET /<uuid>/device/0 had been "confirmed live" on jhkwon19's
  unit. Only an individual /information/vs/0 read was ever actually
  confirmed; the Collection endpoint itself has never been observed to
  answer on any known unit, which is exactly what issue #205 exposes.
- SKILL.md: fixed the numofsubdevice cross-check formula to match what
  coordinator.py actually compares (len(materialized) + 1, not
  len(subdevices) + len(subdevices_skipped)).
- Documented, not yet guarded against: a firmware that echoes state back
  under any unrecognized prefix instead of 4.04ing could pass the flat probe
  and the liveness gate, materializing a phantom duplicate of the master.
  Every board seen so far genuinely 4.04s on paths it doesn't own.
- Added test coverage the review flagged as missing: a materialized (not
  just skipped) flat subdevice re-polling end-to-end through to
  canonical_resources, the hot/warm skip itself, zero-master-hrefs and
  two-UUID no-cross-contamination edge cases, and a golden file for the
  #205 fixture (SKILL.md's "fixture + golden + test" discipline).
This commit is contained in:
Marc Billow
2026-07-30 02:44:26 +00:00
parent e78d941af3
commit 8600985a36
8 changed files with 270 additions and 26 deletions
@@ -400,8 +400,11 @@ the dump in this order; each step rules out a different cause.
more of its hrefs are confirmed live; that's the gate working as
intended, not a bug to chase.
4. **`multidevice.numofsubdevice`** — the board's own count, where it
reports one. Disagreement with `len(subdevices) + len(subdevices_skipped)`
is a strong hint, not proof; only one board family is known to expose it.
reports one. `coordinator._run_discovery` compares it against
`len(materialized) + 1` (materialized subdevices plus the master) and
only warns on disagreement — `subdevices_skipped` entries don't count
toward either side, since they never materialized. A strong hint, not
proof; only one board family is known to expose it.
5. **Which pattern is this board?** `identity.resources['/oic/res']` listing
`/device/1`, `/device/2` means indexed siblings. `resources['/subdevices/
vs/0']` carrying a `subdeviceIdList` means a UUID-prefixed tree, and that