diff --git a/.planning/REQUIREMENTS.md b/.planning/REQUIREMENTS.md index 2789adf..fc8ab22 100644 --- a/.planning/REQUIREMENTS.md +++ b/.planning/REQUIREMENTS.md @@ -39,8 +39,8 @@ ### Link Discovery - [ ] **LINK-01**: Backend auto-discovers AP-CPE relationships by matching registration table MAC addresses against known device interface MACs -- [ ] **LINK-02**: Link state uses a temporal state machine (discovered -> active -> degraded -> down -> stale) with consecutive-miss threshold to prevent false flapping -- [ ] **LINK-03**: Wireless links are stored in a materialized table for fast dashboard queries +- [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 ### Wireless UI @@ -114,8 +114,8 @@ | WRCL-05 | Phase 12 | Complete | | WRCL-06 | Phase 12 | Complete | | LINK-01 | Phase 13 | Pending | -| LINK-02 | Phase 13 | Pending | -| LINK-03 | Phase 13 | Pending | +| LINK-02 | Phase 13 | Complete | +| LINK-03 | Phase 13 | Complete | | LINK-04 | Phase 13 | Pending | | WRUI-01 | Phase 14 | Pending | | WRUI-02 | Phase 14 | Pending | diff --git a/.planning/ROADMAP.md b/.planning/ROADMAP.md index 06d9148..256dd4b 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:** 3 plans +**Plans:** 1/3 plans executed Plans: - [ ] 13-01-PLAN.md — Go poller interface collector (/interface/print) and DEVICE_EVENTS publisher @@ -130,8 +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 | 4 | -| Wireless UI | 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 | 1/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 672a008..5197b5d 100644 --- a/.planning/STATE.md +++ b/.planning/STATE.md @@ -2,14 +2,14 @@ gsd_state_version: 1.0 milestone: v9.7 milestone_name: Tower & Site Management -status: unknown -stopped_at: Completed 12-01-PLAN.md -last_updated: "2026-03-19T10:40:03.896Z" +status: executing +stopped_at: Completed 13-02-PLAN.md +last_updated: "2026-03-19T11:02:22Z" progress: total_phases: 5 completed_phases: 2 - total_plans: 5 - completed_plans: 5 + total_plans: 8 + completed_plans: 7 --- # Project State @@ -19,26 +19,28 @@ progress: See: .planning/PROJECT.md (updated 2026-03-18) **Core value:** Operators can monitor, configure, and troubleshoot their entire MikroTik fleet from a single pane of glass -**Current focus:** Phase 12 — per-client-wireless-collection +**Current focus:** Phase 13 — link-discovery-registration-ingestion ## Current Position -Phase: 12 (per-client-wireless-collection) — COMPLETE -Plan: 2 of 2 (all complete) +Phase: 13 (link-discovery-registration-ingestion) — EXECUTING +Plan: 2 of 3 ## Performance Metrics **Velocity:** -- Total plans completed: 1 +- Total plans completed: 7 - Average duration: 3 min -- Total execution time: 0.05 hours +- Total execution time: 0.35 hours **By Phase:** | Phase | Plans | Total | Avg/Plan | |-------|-------|-------|----------| -| - | - | - | - | +| 11 | 3 | 12min | 4min | +| 12 | 2 | 6min | 3min | +| 13 | 2 | 5min | 2.5min | ## Accumulated Context @@ -47,7 +49,8 @@ Plan: 2 of 2 (all complete) | Phase 11 P03 | 3min | 2 tasks | 5 files | | Phase 12 P01 | 3min | 2 tasks | 6 files | | Phase 12 P02 | 3min | 2 tasks | 3 files | -| Phase 12 P01 | 3min | 2 tasks | 6 files | +| Phase 13 P01 | 3min | 2 tasks | 6 files | +| Phase 13 P02 | 2min | 2 tasks | 5 files | ### Decisions @@ -64,6 +67,7 @@ Decisions are logged in PROJECT.md Key Decisions table. - [Phase 12]: Used unified tenant_isolation RLS policy with super_admin OR clause (matching codebase convention) instead of separate super_admin_bypass policy - [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 ### Pending Todos @@ -77,6 +81,6 @@ None yet. ## Session Continuity -Last session: 2026-03-19T10:40:03.893Z -Stopped at: Completed 12-01-PLAN.md +Last session: 2026-03-19T11:02:22Z +Stopped at: Completed 13-02-PLAN.md Resume file: None diff --git a/.planning/phases/13-link-discovery-registration-ingestion/13-02-SUMMARY.md b/.planning/phases/13-link-discovery-registration-ingestion/13-02-SUMMARY.md new file mode 100644 index 0000000..f3be71b --- /dev/null +++ b/.planning/phases/13-link-discovery-registration-ingestion/13-02-SUMMARY.md @@ -0,0 +1,95 @@ +--- +phase: 13-link-discovery-registration-ingestion +plan: 02 +subsystem: database +tags: [alembic, sqlalchemy, rls, wireless-links, device-interfaces, postgres] + +requires: + - phase: 12-wireless-registration-collection + provides: wireless_registrations hypertable and NATS stream +provides: + - device_interfaces table with MAC-to-device resolution index + - wireless_links table with link state machine columns + - DeviceInterface and WirelessLink ORM models + - LinkState enum (discovered/active/degraded/down/stale) +affects: [13-link-discovery-registration-ingestion] + +tech-stack: + added: [] + patterns: [link state machine with missed_polls counter, MAC-indexed interface table] + +key-files: + created: + - backend/alembic/versions/032_device_interfaces_table.py + - backend/alembic/versions/033_wireless_links_table.py + - backend/app/models/device_interface.py + - backend/app/models/wireless_link.py + modified: + - backend/app/models/__init__.py + +key-decisions: + - "No backref on DeviceInterface.device relationship -- link discovery reads interfaces, does not navigate from Device to interfaces" + +patterns-established: + - "LinkState enum: discovered -> active -> degraded -> down -> stale state machine for wireless link health" + - "MAC-indexed interface table pattern for cross-device MAC resolution" + +requirements-completed: [LINK-02, LINK-03] + +duration: 2min +completed: 2026-03-19 +--- + +# Phase 13 Plan 02: Database Schema Summary + +**Alembic migrations and SQLAlchemy models for device_interfaces (MAC resolution) and wireless_links (AP-CPE state tracking) tables with RLS** + +## Performance + +- **Duration:** 2 min +- **Started:** 2026-03-19T11:00:46Z +- **Completed:** 2026-03-19T11:02:22Z +- **Tasks:** 2 +- **Files modified:** 5 + +## Accomplishments +- device_interfaces table with MAC index for link discovery MAC-to-device resolution +- wireless_links table with state machine columns (state, missed_polls) for AP-CPE tracking +- Both tables have tenant isolation RLS matching codebase convention +- DeviceInterface and WirelessLink models registered in app.models with LinkState enum + +## Task Commits + +Each task was committed atomically: + +1. **Task 1: Create device_interfaces table migration and ORM model** - `7147b15` (feat) +2. **Task 2: Create wireless_links table migration, ORM model, and register both models** - `a71df2a` (feat) + +## Files Created/Modified +- `backend/alembic/versions/032_device_interfaces_table.py` - device_interfaces table with RLS, MAC index, unique(device_id, name) +- `backend/alembic/versions/033_wireless_links_table.py` - wireless_links table with state machine, missed_polls, 4 indexes, RLS +- `backend/app/models/device_interface.py` - DeviceInterface ORM model with device relationship +- `backend/app/models/wireless_link.py` - WirelessLink ORM model with LinkState enum, ap_device/cpe_device relationships +- `backend/app/models/__init__.py` - Added DeviceInterface, WirelessLink, LinkState to model registry + +## Decisions Made +- No backref on DeviceInterface.device relationship -- link discovery reads interfaces directionally, no need to navigate from Device to its interfaces via ORM + +## Deviations from Plan + +None - plan executed exactly as written. + +## Issues Encountered +None + +## User Setup Required +None - no external service configuration required. + +## Next Phase Readiness +- device_interfaces and wireless_links tables ready for Plan 03's link discovery subscriber +- LinkState enum available for state machine logic in the NATS subscriber +- MAC index on device_interfaces enables efficient MAC-to-device lookups + +--- +*Phase: 13-link-discovery-registration-ingestion* +*Completed: 2026-03-19*