100 lines
6.5 KiB
Bash
100 lines
6.5 KiB
Bash
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
|
|
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
cd "$ROOT_DIR"
|
|
|
|
fail() {
|
|
echo "[FAIL] $1" >&2
|
|
exit 1
|
|
}
|
|
|
|
echo "[CHECK] facade duplicate guards"
|
|
if rg -n "domain_digital_input_config_t" components/domain/include/domain.h >/dev/null 2>&1; then
|
|
fail "domain facade must not expose platform-owned digital input config"
|
|
fi
|
|
if rg -n "domain_runtime_stepper_|domain_runtime_platform_init\(" components/domain/include/domain.h >/dev/null 2>&1; then
|
|
fail "domain facade must not expose platform primitive runtime adapters"
|
|
fi
|
|
if rg -n "domain_runtime_storage_" components/domain/include/domain.h >/dev/null 2>&1; then
|
|
fail "domain facade must not expose generic platform storage adapters"
|
|
fi
|
|
if rg -n "domain_runtime_lcd_trigger_slot_animation" components/domain/include/domain.h components/domain/src >/dev/null 2>&1; then
|
|
fail "domain must request LCD slot reels through the expression catalog"
|
|
fi
|
|
if rg -n "play_prompt_named|platform_behavior_play_prompt_named" components/platform/include/platform.h components/platform/internal >/dev/null 2>&1; then
|
|
fail "platform facade/internal Wi-Fi behavior hooks must not expose semantic prompt string IDs"
|
|
fi
|
|
if rg -n "VOICE_PROMPT_WIFI_|voice_prompt_id_t|voice_prompt_play\\(" components/platform/internal components/platform/src >/dev/null 2>&1; then
|
|
fail "platform must expose voice prompt playback as a primitive file API, not semantic Wi-Fi prompt IDs"
|
|
fi
|
|
if rg -n "platform_multimodal_downstream_playback_event|PLATFORM_MULTIMODAL_DOWNSTREAM_PLAYBACK" components/platform/include/platform.h >/dev/null 2>&1; then
|
|
fail "platform facade must not duplicate domain-owned multimodal semantic events"
|
|
fi
|
|
if rg -n "esp-sr|esp_sr|esp_afe|afe_config|afe_sr|model_path\\.h|srmodels|CONFIG_SR_|SR_WN|MULTIMODAL_WAKE_WORD|wake_word|Wake word|Wake-word|wn9_" components main sdkconfig.defaults dependencies.lock >/dev/null 2>&1; then
|
|
fail "ESP-SR and wake-word code/dependencies must stay removed"
|
|
fi
|
|
if rg -n "platform_lcd_trigger_slot_animation|lcd_trigger_slot_animation" components/platform/include components/platform/internal components/platform/src/runtime >/dev/null 2>&1; then
|
|
fail "platform must expose LCD presentation primitives, not one-off expression APIs"
|
|
fi
|
|
if rg -n "control_plane_get_direction_(count|view|current_direction_index)|control_plane_get_current_angle" components/control_plane/include/control_plane.h components/control_plane/src >/dev/null 2>&1; then
|
|
fail "control-plane facade/source must not duplicate domain-owned direction catalog query contracts"
|
|
fi
|
|
if rg -n "control_plane_direction_default_(axis_config|calibration)|CONTROL_PLANE_CAL_" components/control_plane >/dev/null 2>&1; then
|
|
fail "direction calibration defaults are domain-owned and must not be duplicated in control-plane"
|
|
fi
|
|
if rg -n "void\\s+platform_multimodal_(transport_stop|audio_stop_uplink)\\s*\\(|void\\s+domain_runtime_multimodal_(transport_stop|audio_stop_uplink)\\s*\\(" components/platform/include/platform.h components/domain/include/domain.h >/dev/null 2>&1; then
|
|
fail "multimodal stop facade APIs must expose timeout/result semantics"
|
|
fi
|
|
if rg -n "void\\s+(domain_runtime|control_plane)_stop_lcd_render_loop\\s*\\(" components/domain/include/domain.h components/control_plane/include/control_plane.h >/dev/null 2>&1; then
|
|
fail "LCD stop facade APIs must expose timeout/result semantics"
|
|
fi
|
|
echo "[CHECK] component dependency ownership"
|
|
if rg -n "^\\s*esp_wifi\\s*$|^\\s*nvs_flash\\s*$" components/domain/CMakeLists.txt >/dev/null 2>&1; then
|
|
fail "domain must not depend directly on esp_wifi or nvs_flash"
|
|
fi
|
|
if rg -n "^\\s*REQUIRES\\s*$" components/control_plane/CMakeLists.txt >/dev/null 2>&1; then
|
|
fail "control-plane must not publicly re-export domain include paths; use PRIV_REQUIRES for domain"
|
|
fi
|
|
if rg -n "#include \"multimodal/(audio_pipeline|transport_ws)\\.h\"" components/domain >/dev/null 2>&1; then
|
|
fail "domain must not include platform multimodal internal headers directly"
|
|
fi
|
|
|
|
echo "[CHECK] no cross-component internal includes"
|
|
if rg -n "#include \"components/.*/internal/.*\"" components main >/dev/null 2>&1; then
|
|
fail "cross-component internal include pattern found"
|
|
fi
|
|
|
|
echo "[CHECK] no unbounded wait in control-plane runtime stop paths"
|
|
if rg -n "portMAX_DELAY" components/control_plane/src/runtime >/dev/null 2>&1; then
|
|
fail "control-plane runtime paths must avoid unbounded portMAX_DELAY"
|
|
fi
|
|
if rg -n "CONTROL_PLANE_MODEL_CMD_INTERNAL_STOP|command_type\\s*=\\s*\\(int\\)command_type" components/control_plane/src/runtime/control_plane_runtime_workers.c >/dev/null 2>&1; then
|
|
fail "control-plane model command queue must keep typed command contracts and avoid magic sentinels"
|
|
fi
|
|
if rg -n "FACTORY_CLI_PARK_DELAY_MS|Cooperative exit: park" components/control_plane/src/runtime/control_plane_factory_cli.c >/dev/null 2>&1; then
|
|
fail "factory CLI worker must cooperatively exit and clear its handle instead of parking for force-delete"
|
|
fi
|
|
if rg -n "if \\(s_(model_cmd|ws_wait|lcd_render_loop|wifi_state|ptt|slot_switch)_task == NULL\\)" components/control_plane/src/runtime/control_plane_runtime_workers.c >/dev/null 2>&1; then
|
|
fail "control-plane runtime worker task handles must be read through the runtime lock helpers"
|
|
fi
|
|
if ! rg -n "ws_start_async_stop" components/platform/src/multimodal/transport_ws/transport_ws_runtime_impl.c >/dev/null 2>&1; then
|
|
fail "websocket zero-timeout stop must start an asynchronous teardown worker"
|
|
fi
|
|
if rg -n "effective_timeout_ms = timeout_ms > 0 \\? timeout_ms : 1" components/platform/src/multimodal/transport_ws/transport_ws_runtime_impl.c >/dev/null 2>&1; then
|
|
fail "zero-timeout stop must be asynchronous, not a 1ms synchronous teardown"
|
|
fi
|
|
if perl -0ne 'our $found; $found = 1 if /esp_err_t multimodal_audio_stop_uplink\(int timeout_ms\).*?effective_timeout_ms\s*=\s*timeout_ms\s*>\s*0\s*\?\s*timeout_ms\s*:\s*1/s; END { exit($found ? 0 : 1) }' components/platform/src/multimodal/audio_pipeline/audio_pipeline_runtime_impl.c >/dev/null 2>&1; then
|
|
fail "zero-timeout stop must be asynchronous, not a 1ms synchronous teardown"
|
|
fi
|
|
|
|
echo "[CHECK] outbound HTTPS owner boundary"
|
|
if rg -n "#include \"esp_http_client.h\"|#include <esp_http_client.h>|esp_http_client_" components/control_plane >/dev/null 2>&1; then
|
|
fail "control-plane must not use esp_http_client directly; route outbound HTTPS through the owner adapter"
|
|
fi
|
|
if rg -n "domain_runtime_storage_" components/control_plane >/dev/null 2>&1; then
|
|
fail "control-plane must not use generic storage adapters; use owner-scoped domain ports"
|
|
fi
|
|
|
|
echo "[PASS] architecture guards"
|