Claude Opus Council

OPUS Claude Opus 4.6 (1M context) Anthropic

Judge 1: ACCURACY AUDITOR

  • [HIGH] API Reference documents all state-setting endpoints as POST. The actual source code uses PUT for all of these: fan/routes.py:119, fan/routes.py:147, led/routes.py:147, led/routes.py:175, oled/routes.py:123, oled/routes.py:155. Anyone following the docs will get 405 Method Not Allowed errors.

  • [HIGH] API Reference shows health endpoint version as "version": "0.1.0". Both pyproject.toml (version = "0.1.0") and casectl/__init__.py (__version__ = "0.1.0") are wrong. The MQTT plugin already self-identifies as version “0.2.0”, so there is an internal version inconsistency.

  • [HIGH] Architecture page lists only 5 plugins. The pyproject.toml entry points show 8 plugins: those 5 plus mqtt, automation, and alerting. The architecture page also marks “TUI” as “soon” when casectl top is already implemented.

  • [HIGH] CLI Reference page is missing the casectl top command entirely. This is a major feature – an interactive terminal dashboard with live updates, interactive key bindings, and --once mode for scripting.

  • [MEDIUM] API Reference documents fan mode values as {"mode": 0} (integer body). The actual SetFanModeRequest accepts int | str, meaning both {"mode": 0} and {"mode": "follow-temp"} are valid. Docs only show integers.

  • [MEDIUM] API Reference is missing the OLED /power and /content endpoints, all MQTT plugin endpoints, all automation plugin endpoints, all alerting plugin endpoints, and the SSE endpoint (GET /api/sse).

  • [MEDIUM] Configuration page is missing mqtt, automation, and fan.curve config sections. The alerts section is missing 8 fields added in v0.2.0.

  • [LOW] WebSocket section documents only read-only streaming. The actual WebSocket supports 8 bidirectional commands. WebSocket auth via query param is undocumented.

  • [LOW] CLI Reference config get documents sections as fan, led, oled, service, alerts – missing mqtt and automation.


Judge 2: SECURITY REVIEWER

  • [HIGH] MQTT credentials (password field in MqttConfig) are stored in plaintext in config.yaml with no redaction. GET /api/config/mqtt returns the MQTT broker password in the API response. Unlike alerts.smtp_password which is redacted, mqtt.password is exposed.

  • [HIGH] The ntfy.sh access token (ntfy_token) is stored in plaintext and returned unredacted via GET /api/config/alerts.

  • [MEDIUM] SMTP credentials are sent over STARTTLS but there is no explicit certificate verification. Consider using ssl.create_default_context().

  • [MEDIUM] The tls_insecure option in MQTT config sets check_hostname = False and verify_mode = CERT_NONE. No warning log is emitted.

  • [MEDIUM] Webhook URL accepts any URL scheme including file://, ftp://, etc. – potential SSRF via httpx.AsyncClient. Should validate to http:// or https:// only.

  • [LOW] API token first 8 characters are logged at WARNING level. Could expose token prefixes in centralized logging.

  • [LOW] WebSocket only checks query parameter ?token= – no cookie or header auth. Weaker than HTTP auth.

  • [LOW] config.yaml permissions are not explicitly set to 0o600. Contains plaintext secrets.


Judge 3: ARCHITECTURE CRITIC

  • [MEDIUM] mqtt/state.py at 1273 lines handles too many concerns: fan/LED/OLED state publishing, command handling, HA discovery, colour parsing, mode mapping. Should be split into per-device modules.

  • [MEDIUM] Mode name-to-integer mappings duplicated in 5+ places: CLI, server, fan routes, LED routes, MQTT state. A single canonical mapping in config/models.py would eliminate drift risk.

  • [MEDIUM] CLI uses _api_post() for OLED screen and rotation but server routes use PUT. The CLI and server are not sharing a contract.

  • [LOW] Plugin cross-wiring uses app.state with string keys. Implicit coupling, but works and is documented.

  • [LOW] AlertDispatcher.dispatch() cooldown is implemented in the plugin, not the dispatcher. Split responsibility is confusing.

  • [LOW] Inconsistent config loading: automation reads from ctx.get_config() (plugins section), alerting reads from ctx.config_manager.get("alerts") (top-level).


Judge 4: API DESIGN JUDGE

  • [HIGH] HTTP method mismatch between CLI and server. The CLI sends POST for OLED screen and rotation, but the server defines PUT. These CLI commands return 405.

  • [MEDIUM] POST-to-PUT migration is incomplete. Fan/LED routes use PUT (correct), but CLI OLED still sends POST, and all docs show POST.

  • [MEDIUM] Fan speed API accepts 0-100 range but status returns 0-255 hardware range. Writing {"duty": [75]} then reading status shows duty: [191].

  • [MEDIUM] WebSocket bidirectional command protocol is completely undocumented. 8 command types exist but are invisible to API consumers.

  • [LOW] Response key duty_hw leaks internal “hardware” abstraction into the API response.

  • [LOW] SSE and WebSocket serve overlapping purposes; their relationship is not documented.


Judge 5: DOCUMENTATION COMPLETENESS

  • [HIGH] casectl top is completely undocumented. Supports interactive keys (m, +/-, q) and --once for scripting.

  • [HIGH] Three entire plugins are undocumented: MQTT (HA auto-discovery, 12 command topics), automation (rule engine with priority/cooldown), alerting (webhook, ntfy.sh, SMTP).

  • [HIGH] Configuration page missing mqtt, automation, and fan.curve sections. The alerts section is missing 8 fields.

  • [HIGH] SSE endpoint (GET /api/sse) is completely undocumented.

  • [MEDIUM] aiomqtt missing from pyproject.toml dependencies. Users cannot install it without reading the source.

  • [MEDIUM] Plugin development page references only 3 events; the codebase emits many more.

  • [LOW] Architecture diagram still shows TUI as “soon” when it is implemented.


Consolidated Report

Critical Issues (must fix)

  1. CLI/Server HTTP method mismatch (runtime bug): CLI sends POST for OLED, server expects PUT. Commands return 405.
  2. API docs show POST, server uses PUT: Every curl example will fail with 405.
  3. Version not bumped to 0.2.0: pyproject.toml and __init__.py both say 0.1.0.
  4. MQTT password exposed via API: GET /api/config/mqtt returns password unredacted.

Important Issues (should fix)

  1. Three new plugins completely undocumented (MQTT, automation, alerting)
  2. casectl top undocumented
  3. Configuration page missing mqtt, automation, fan.curve sections
  4. SSE endpoint undocumented
  5. aiomqtt missing from pyproject.toml dependencies
  6. Fan duty units inconsistent (0-100 API vs 0-255 status)
  7. Webhook URL allows arbitrary schemes (SSRF)
  8. Architecture page outdated (5/8 plugins, TUI “soon”)

Positive Observations

  • Excellent plugin architecture: Protocol-based structural subtyping, entry-point discovery
  • Robust MQTT implementation: TLS, exponential backoff reconnection, LWT, birth messages, HA auto-discovery
  • Sound automation engine: Priority-based conflict resolution, cooldown tracking, sub-500ms latency
  • Good auth design: Timing-safe comparison, cookie sessions, localhost bypass, proxy trust
  • SSE implementation is excellent: Heartbeat keepalives, backpressure, max client limits
  • Test suite growth from 194 to 1193 tests