> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (- [ ]) syntax for tracking.
Add pinned test dependencies pytest, pytest-asyncio and aiosqlite. Add Settings fields: environment, probe_packets_per_round=3 (1..10), offline_consecutive_rounds=2 (1..10), degraded_window_rounds=5 (2..60), degraded_loss_percent=20.0 (1..100), recovery_consecutive_clean_rounds=3 (1..20), wecom_notification_max_attempts, wecom_retry_base_seconds and WECOM_TO_PARTY. validate_runtime_settings rejects production default JWT keys, production TLS-disabled Casdoor, and enabled WeCom delivery without all three credentials.
Expected: FAIL because fields and NotificationOutbox do not exist.
- [ ]**Step 3: Implement models and migration**
Extend Device with nullable per-device policy overrides and allow degraded status. Add an index on probe (device_id, created_at), and a status/next_attempt_at index on outbox. Add AlertTypeEnum.degraded and fields previous_status, current_status, last_notification_error, notification_attempts. Implement one idempotent migration runner called before create_all; it records revision 20260803_reliability_monitoring in schema_migrations and never deletes existing data.
Expected: FAIL because runner and state machine modules are absent.
- [ ]**Step 3: Implement minimal parser and state machine**
Invoke fping with the argument sequence fping -C <count> -q -t <milliseconds> <ip...>; capture stdout and stderr because quiet count output is emitted to stderr. Numeric reply tokens count as success and dash tokens as loss. Missing/malformed expected IP output creates is_valid=False and cannot trigger a device failure.
State priority: invalid records retain state; configured consecutive full loss gives offline; configured aggregate window loss gives degraded; only offline/degraded plus configured clean records gives online; otherwise retain state. StateDecision contains next_status, event_type, should_notify, reason.
- [ ]**Step 4: Refactor Pinger to persist exactly one summary per enabled device**
Pinger.run_one_round saves every ProbeResult, reads the required recent valid records, computes StateDecision, updates Device, and returns DeviceStateChange values within one transaction. It must use asyncio.create_subprocess_exec(*command) and never a command string.
Replace in-memory pending alerts. record_transition inserts AlertEvent and NotificationOutbox before caller commit. Offline escalation annotates/resolves the prior open degraded event. Recovery closes one unresolved event, stores duration, and creates a recovered notification. Render notifications with event type, device, IP, location, project, loss summary, time and duration; split batches at a fixed safe count.
- [ ]**Step 4: Implement WeCom client and retry dispatcher**
Use httpx.AsyncClient with explicit connect/read/write timeout. Cache access_token until server expiry minus safety margin. Default payload uses touser="@all"; only use toparty when WECOM_TO_PARTY is non-empty. Never log token-bearing URLs, request JSON, response body, secret, or message content. Retry transport/timeouts, 429 and 5xx with next_attempt_at = now + base_seconds * 2 ** (attempt_count - 1); mark failed at configured maximum.
- [ ]**Step 5: Connect dispatcher to scheduler**
Run dispatch_due after a successful probe transaction and at a small bounded interval. The scheduler has one task and a lock; cancellation waits for the active probe/dispatch cycle to close its session.
Expected: FAIL on missing validation/health behavior.
- [ ]**Step 3: Implement schema and import validation**
Use IPvAnyAddress, Field bounds and field_validator to map blank overrides to None. Before decoding CSV enforce CSV_MAX_BYTES; reject invalid UTF-8 as 400; limit returned line errors to 100; de-duplicate both database IPs and earlier accepted CSV rows. Device and alert responses expose only aggregate notification status, never message content or external error body.
- [ ]**Step 4: Implement health and API projections**
Health router runs SELECT 1. The lifespan calls validate_runtime_settings before scheduling. CORS accepts only exact configured origins. Device lists return latest valid summary and policy fields; alerts return degraded/recovered filters, transition and retry fields.
- Produces: statusLabel(status) and statusTagType(status) supporting online, degraded, offline, unknown.
- Consumes: enhanced Task 5 device and alert DTOs.
- [ ]**Step 1: Write a failing presentation test**
~~~javascript
import { describe, expect, it } from "vitest"
import { statusLabel, statusTagType } from "./status"
describe("status presentation", () => {
it("marks degraded connectivity as a warning", () => {
expect(statusLabel("degraded")).toBe("故障")
expect(statusTagType("degraded")).toBe("warning")
})
})
~~~
- [ ]**Step 2: Run test to verify it fails**
Run: npm --prefix frontend run test -- --run
Expected: FAIL because the test script is absent.
- [ ]**Step 3: Implement minimal UI contract**
Add vitest development dependency and test script. Add status utility with explicit mappings. Devices view adds state, packet loss, RTT and last probe columns plus bounded policy form fields. Alerts view adds degraded filter, transition, delivery status and retry count. Dashboard adds degraded count. Use Vue interpolation exclusively; never use v-html for device data.
Expected: FAIL because Compose contains an embedded password and NET_ADMIN.
- [ ]**Step 3: Implement minimal hardening**
Require certificate/TLS verification before accepting production Casdoor id_token; remove unverified-claims fallback. Compose reads database credentials only from server-only environment file, exposes backend as 127.0.0.1:8001:8000, does not run frontend container in production, does not expose database, uses cap_drop ALL plus cap_add NET_RAW and no-new-privileges. Backend image has non-secret health check.
- [ ]**Step 4: Add OpenResty and operating material**
OpenResty listens on the agreed IP port, serves /opt/pingwatch/frontend, uses SPA try_files, proxies /api/ to 127.0.0.1:8001 and upgrades /ws, limits request body to 15m, and has separate access/error logs. Deployment guide gives preflight, server-only env permissions, build/copy, Compose lifecycle, OpenResty test/reload, health/API/WebSocket smoke tests and approved rollback. Backup guide defines PostgreSQL volume backup before upgrades and restore of prior frontend build/image.
- Produces: each of five requirement IDs mapped to code, test and Ubuntu/OpenResty validation evidence.
- [ ]**Step 1: Execute full automated verification**
Run: python -m pytest backend/tests -v from backend/
Expected: PASS.
Run: npm --prefix frontend run test -- --run
Expected: PASS.
Run: npm --prefix frontend run build
Expected: PASS.
Run: docker compose config
Expected: PASS.
- [ ]**Step 2: Execute local Compose smoke test**
Run: docker compose up --build -d, then curl -fsS http://127.0.0.1:8001/api/health, then docker compose down.
Expected: health contains status=ok and database=ready; no test data or secret file is committed.
- [ ]**Step 3: Write traceability/test reports**
Traceability maps requirements _01 through _05 to implementation file, exact automated test name and Ubuntu/OpenResty validation command. Test report records command, timestamp, result, limitations, and states that real network validation needs authorized test IPs and configured enterprise WeChat credentials. It excludes credentials, private IP inventories and full notification contents.
- [ ]**Step 4: Scan final changes and commit evidence**
- Spec coverage: Tasks 1-4 implement multi-packet probing, state transitions, durable notification, recovery and batch/node safety; Tasks 5-6 implement API/UI; Task 7 implements OpenResty, secrets, authentication and container controls; Task 8 provides C1 deployment evidence and traceability.
- Placeholder scan: the plan contains executable commands, named files, test behavior and data contracts for each task.
- Type consistency: ProbeResult, DeviceMonitoringPolicy, StateDecision, DeviceStateChange, NotificationOutbox and DeliveryResult are defined before their consuming tasks.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.