diff --git a/.planning/REQUIREMENTS.md b/.planning/REQUIREMENTS.md index fc8ab22..3bf4964 100644 --- a/.planning/REQUIREMENTS.md +++ b/.planning/REQUIREMENTS.md @@ -38,7 +38,7 @@ ### Link Discovery -- [ ] **LINK-01**: Backend auto-discovers AP-CPE relationships by matching registration table MAC addresses against known device interface MACs +- [x] **LINK-01**: Backend auto-discovers AP-CPE relationships by matching registration table MAC addresses against known device interface MACs - [x] **LINK-02**: Link state uses a temporal state machine (discovered -> active -> degraded -> down -> stale) with consecutive-miss threshold to prevent false flapping - [x] **LINK-03**: Wireless links are stored in a materialized table for fast dashboard queries - [ ] **LINK-04**: Unmanaged wireless clients (MACs not matching any TOD device) are displayed as "unknown clients" with signal/rate data @@ -113,7 +113,7 @@ | WRCL-04 | Phase 12 | Complete | | WRCL-05 | Phase 12 | Complete | | WRCL-06 | Phase 12 | Complete | -| LINK-01 | Phase 13 | Pending | +| LINK-01 | Phase 13 | Complete | | LINK-02 | Phase 13 | Complete | | LINK-03 | Phase 13 | Complete | | LINK-04 | Phase 13 | Pending | diff --git a/.planning/ROADMAP.md b/.planning/ROADMAP.md index 256dd4b..d39d437 100644 --- a/.planning/ROADMAP.md +++ b/.planning/ROADMAP.md @@ -84,7 +84,7 @@ Plans: 2. Link state follows a temporal state machine (discovered, active, degraded, down, stale) with consecutive-miss threshold to prevent false flapping 3. Discovered links are stored in a materialized wireless_links table for fast dashboard queries 4. Wireless clients whose MACs do not match any managed device appear as "unknown clients" with their signal and rate data preserved -**Plans:** 1/3 plans executed +**Plans:** 2/3 plans executed Plans: - [ ] 13-01-PLAN.md — Go poller interface collector (/interface/print) and DEVICE_EVENTS publisher @@ -130,7 +130,7 @@ Plans: | Sites | SITE-01, SITE-02, SITE-03, SITE-04, SITE-05, SITE-06 | 11 | 3/3 | Complete | 2026-03-19 | DASH-01 | 11 | 1 | | Site Dashboard | DASH-02, DASH-03, DASH-04 | 14 | 3 | | Sectors | SECT-01, SECT-02, SECT-03 | 14 | 3 | -| Wireless Collection | WRCL-01, WRCL-02, WRCL-03, WRCL-04, WRCL-05, WRCL-06 | 12 | 2/2 | Complete | 2026-03-19 | LINK-01, LINK-02, LINK-03, LINK-04 | 13 | 1/3 | In Progress| | WRUI-01, WRUI-02, WRUI-03 | 14 | 3 | +| Wireless Collection | WRCL-01, WRCL-02, WRCL-03, WRCL-04, WRCL-05, WRCL-06 | 12 | 2/2 | Complete | 2026-03-19 | LINK-01, LINK-02, LINK-03, LINK-04 | 13 | 2/3 | In Progress| | WRUI-01, WRUI-02, WRUI-03 | 14 | 3 | | Signal Trending | TRND-01, TRND-02 | 15 | 2 | | Site Alerting | ALRT-01, ALRT-02 | 15 | 2 | | **Total** | | | **30** | diff --git a/.planning/STATE.md b/.planning/STATE.md index 5197b5d..13c5a2d 100644 --- a/.planning/STATE.md +++ b/.planning/STATE.md @@ -2,9 +2,9 @@ gsd_state_version: 1.0 milestone: v9.7 milestone_name: Tower & Site Management -status: executing -stopped_at: Completed 13-02-PLAN.md -last_updated: "2026-03-19T11:02:22Z" +status: unknown +stopped_at: Completed 13-01-PLAN.md +last_updated: "2026-03-19T11:07:35.900Z" progress: total_phases: 5 completed_phases: 2 @@ -41,6 +41,7 @@ Plan: 2 of 3 | 11 | 3 | 12min | 4min | | 12 | 2 | 6min | 3min | | 13 | 2 | 5min | 2.5min | +| Phase 13 P01 | 5min | 2 tasks | 4 files | ## Accumulated Context @@ -68,6 +69,8 @@ Decisions are logged in PROJECT.md Key Decisions table. - [Phase 12]: WIRELESS_REGISTRATIONS NATS stream uses 30-day retention (vs 24h for DEVICE_EVENTS) for historical client analytics - [Phase 12]: RF monitor collection gated on wireless interface presence to avoid unnecessary API calls - [Phase 13]: No backref on DeviceInterface.device relationship -- link discovery reads interfaces directionally +- [Phase 13]: MAC addresses lowercased at collection time for consistent downstream matching +- [Phase 13]: InterfaceInfo (identity/link discovery) kept separate from InterfaceStats (traffic counters) ### Pending Todos @@ -81,6 +84,6 @@ None yet. ## Session Continuity -Last session: 2026-03-19T11:02:22Z -Stopped at: Completed 13-02-PLAN.md +Last session: 2026-03-19T11:07:35.897Z +Stopped at: Completed 13-01-PLAN.md Resume file: None diff --git a/.planning/phases/13-link-discovery-registration-ingestion/13-01-SUMMARY.md b/.planning/phases/13-link-discovery-registration-ingestion/13-01-SUMMARY.md new file mode 100644 index 0000000..a903794 --- /dev/null +++ b/.planning/phases/13-link-discovery-registration-ingestion/13-01-SUMMARY.md @@ -0,0 +1,117 @@ +--- +phase: 13-link-discovery-registration-ingestion +plan: 01 +subsystem: poller +tags: [routeros, nats, interfaces, mac-address, link-discovery, go] + +requires: + - phase: 12-wireless-registration-collection + provides: "NATS publisher pattern, WIRELESS_REGISTRATIONS stream, withTimeout wrapper" +provides: + - "InterfaceInfo struct with name, MAC, type, running fields" + - "CollectInterfaceInfo function for RouterOS /interface/print" + - "DeviceInterfaceEvent NATS publisher on device.interfaces.{device_id}" + - "DEVICE_EVENTS stream includes device.interfaces.> subject" +affects: [13-link-discovery-registration-ingestion, link-state-machine, topology-map] + +tech-stack: + added: [] + patterns: [interface-identity-collector, mac-normalization] + +key-files: + created: + - poller/internal/device/interfaces.go + - poller/internal/device/interfaces_test.go + modified: + - poller/internal/bus/publisher.go + - poller/internal/poller/worker.go + +key-decisions: + - "MAC addresses lowercased at collection time for consistent downstream matching" + - "Entries without mac-address skipped (loopback, bridge without MAC)" + - "Interface info collected separately from traffic counters (InterfaceInfo vs InterfaceStats)" + +patterns-established: + - "MAC normalization: all MAC addresses lowercased at the collection layer before publishing" + - "InterfaceInfo vs InterfaceStats: identity data (link discovery) separate from traffic counters (metrics)" + +requirements-completed: [LINK-01] + +duration: 5min +completed: 2026-03-19 +--- + +# Phase 13 Plan 01: Interface Info Collector Summary + +**Go poller InterfaceInfo collector publishing MAC addresses to DEVICE_EVENTS NATS stream for link discovery** + +## Performance + +- **Duration:** 5 min +- **Started:** 2026-03-19T11:00:38Z +- **Completed:** 2026-03-19T11:06:20Z +- **Tasks:** 2 +- **Files modified:** 4 + +## Accomplishments +- InterfaceInfo struct and CollectInterfaceInfo function collect name, MAC, type, running from /interface/print +- MAC addresses lowercased at collection time for consistent downstream link resolution +- DeviceInterfaceEvent publisher sends to device.interfaces.{device_id} on DEVICE_EVENTS stream +- Wired into PollDevice cycle after traffic counters, before health metrics + +## Task Commits + +Each task was committed atomically: + +1. **Task 1: Create interface info collector with v6/v7 routing** - `4b5bb94` (test) + `6939584` (feat) +2. **Task 2: Add DeviceInterfaceEvent publisher, update DEVICE_EVENTS stream, wire into PollDevice** - `397a33a` (feat) + +_Note: Task 1 used TDD with separate test and implementation commits_ + +## Files Created/Modified +- `poller/internal/device/interfaces.go` - InterfaceInfo struct, CollectInterfaceInfo function, MAC normalization (added alongside existing InterfaceStats) +- `poller/internal/device/interfaces_test.go` - Unit tests for struct fields, MAC lowercasing, running bool parsing +- `poller/internal/bus/publisher.go` - DeviceInterfaceEvent type, PublishDeviceInterfaces method, device.interfaces.> added to DEVICE_EVENTS stream +- `poller/internal/poller/worker.go` - Interface info collection wired into PollDevice after traffic counters + +## Decisions Made +- MAC addresses lowercased at collection time (strings.ToLower) so downstream consumers never need to normalize +- Entries without mac-address are silently skipped (loopback, bridge without MAC are not useful for link discovery) +- InterfaceInfo is a separate type from InterfaceStats -- identity data for link discovery vs traffic counters for metrics +- Collection placed after traffic counters, before health metrics in PollDevice to group interface-related work + +## Deviations from Plan + +### Auto-fixed Issues + +**1. [Rule 3 - Blocking] Restored InterfaceStats collector overwritten during file creation** +- **Found during:** Task 1 +- **Issue:** Creating interfaces.go overwrote the pre-existing InterfaceStats type and CollectInterfaces function that publisher.go and worker.go depend on +- **Fix:** Restored InterfaceStats and CollectInterfaces, appended new InterfaceInfo types below +- **Files modified:** poller/internal/device/interfaces.go +- **Verification:** go build ./... succeeds +- **Committed in:** 6939584 (Task 1 commit) + +--- + +**Total deviations:** 1 auto-fixed (1 blocking) +**Impact on plan:** File creation overwrote existing code; restored immediately. No scope creep. + +## Issues Encountered +None beyond the deviation noted above. + +## User Setup Required +None - no external service configuration required. + +## Next Phase Readiness +- Interface MAC data now published to NATS on every poll cycle +- Backend subscriber (Plan 03) can consume device.interfaces.> to populate device_interfaces table +- Link discovery (Plan 02) can match registration MACs against interface MACs for topology resolution + +## Self-Check: PASSED + +All 4 files verified present. All 3 commits verified in git log. + +--- +*Phase: 13-link-discovery-registration-ingestion* +*Completed: 2026-03-19*