feat(semantic): curator-driven protocol hardening — decision memory + relation repair
- Add @RATIONALE/@REJECTED to 103+ C4/C5 contracts across backend core, services, API routes, and frontend models - Fix 109 unresolved @RELATION edges (Auth.*, SupersetClient.*, AgentChat.*, ADR cross-refs) - Add 13 @ingroup tags for DSA/HCA attention grouping - Repair 29 stale graph edges via index rebuild - Update .kilo agent prompts and skills for GRACE-Poly v2.6 compliance - Git integration: merge routes, branch lifecycle, remote providers, UX components - 0 broken anchor pairs, index rebuilt with 0 parse warnings
This commit is contained in:
@@ -10,6 +10,11 @@
|
||||
# @SIDE_EFFECT Starts background scheduler and binds network ports for HTTP/WS traffic.
|
||||
# @DATA_CONTRACT [HTTP Request | WS Message] -> [HTTP Response | JSON Log Stream]
|
||||
# @RATIONALE Duplicate @RELATION and @INVARIANT lines removed from header. Import sorting unified via ruff isort (I) rule across src/ — 139 fixes applied.
|
||||
# @REJECTED Monolithic route registration in app.py was rejected — route groups are now modular
|
||||
# in separate router modules under api/routes/ to keep AppModule focused on middleware,
|
||||
# lifespan, and WebSocket endpoint wiring. Embedding lifespan logic directly in module
|
||||
# scope was rejected — the asynccontextmanager pattern ensures clean startup/shutdown
|
||||
# lifecycle without module-level race conditions.
|
||||
|
||||
import asyncio
|
||||
from contextlib import asynccontextmanager
|
||||
@@ -384,7 +389,7 @@ async def log_requests(request: Request, call_next):
|
||||
# @BRIEF Register all FastAPI route groups exposed by the application entrypoint.
|
||||
# @RELATION DEPENDS_ON -> [FastAPI_App]
|
||||
# @RELATION DEPENDS_ON -> [Route_Group_Contracts]
|
||||
# @RELATION DEPENDS_ON -> [AuthApi]
|
||||
# @RELATION DEPENDS_ON -> [Api.Auth]
|
||||
# @RELATION DEPENDS_ON -> [AdminApi]
|
||||
# @RELATION DEPENDS_ON -> [PluginsRouter]
|
||||
# @RELATION DEPENDS_ON -> [TasksRouter]
|
||||
@@ -432,7 +437,7 @@ app.include_router(maintenance.maintenance_router)
|
||||
# @BRIEF Authenticate a WebSocket connection via JWT or API key from query param `token`.
|
||||
# @PRE websocket is a live Starlette WebSocket before accept().
|
||||
# @POST Returns True if token is valid, logs reason; returns False if rejected.
|
||||
# @RELATION DEPENDS_ON -> [AuthJwtModule]
|
||||
# @RELATION DEPENDS_ON -> [Auth.Jwt]
|
||||
# @RELATION DEPENDS_ON -> [APIKeyModel]
|
||||
# @SIDE_EFFECT Performs DB read to validate API key hash.
|
||||
async def _authenticate_websocket(websocket: WebSocket, endpoint_name: str) -> bool:
|
||||
@@ -506,6 +511,14 @@ async def _authenticate_websocket(websocket: WebSocket, endpoint_name: str) -> b
|
||||
# @DATA_CONTRACT [task_id: str, source: str, level: str] -> [JSON log entry objects]
|
||||
# @INVARIANT Every accepted WebSocket subscription is unsubscribed exactly once even when streaming fails or the client disconnects.
|
||||
# @UX_STATE Connecting -> Streaming -> (Disconnected)
|
||||
# @RATIONALE Uses asyncio.wait with two queues (log + status) to multiplex task logs and status
|
||||
# updates over a single WebSocket connection — eliminates the need for the client to
|
||||
# open a second socket or poll /api/tasks for status changes. Server-side source and
|
||||
# level filters reduce bandwidth by discarding irrelevant log entries before transmission.
|
||||
# @REJECTED Separate WebSocket connections for logs vs status was rejected — doubles connection
|
||||
# overhead and complicates client-side synchronization. Client-side filtering was rejected
|
||||
# — sends unnecessary data over the network when the server can discard early. Polling
|
||||
# /api/tasks for status changes was rejected — introduces latency and load.
|
||||
#
|
||||
# @TEST_CONTRACT WebSocketLogStreamApi ->
|
||||
# {
|
||||
@@ -725,6 +738,13 @@ async def websocket_endpoint(
|
||||
# @RELATION CALLS -> [TaskManagerPackage]
|
||||
# @PRE WebSocket must be authenticated via `token` query param.
|
||||
# @POST WebSocket streams task status events until disconnect.
|
||||
# @RATIONALE Push-based global task event stream replaces per-task status polling — a single
|
||||
# WebSocket delivers status changes for all tasks, enabling real-time dashboards and
|
||||
# notification UIs without repeated /api/tasks queries.
|
||||
# @REJECTED Per-task WebSocket subscriptions was rejected — the task-manager already publishes
|
||||
# global events, so subscribing per-task duplicates the subscription overhead.
|
||||
# Polling /api/tasks was rejected — introduces latency, load on the DB, and
|
||||
# misses events between polls.
|
||||
@app.websocket("/ws/task-events")
|
||||
async def task_events_websocket(websocket: WebSocket):
|
||||
"""
|
||||
@@ -773,6 +793,12 @@ async def task_events_websocket(websocket: WebSocket):
|
||||
# @RELATION CALLS -> [TaskManagerPackage]
|
||||
# @PRE WebSocket must be authenticated via `token` query param.
|
||||
# @POST WebSocket streams maintenance events until disconnect.
|
||||
# @RATIONALE Push-based maintenance event notification replaces polling /api/maintenance/events —
|
||||
# maintenance windows are time-sensitive (banner visibility, creation/ending); push
|
||||
# ensures sub-second delivery without DB load from polling.
|
||||
# @REJECTED Polling /api/maintenance/events was rejected — maintenance state changes are
|
||||
# infrequent but time-critical; polling wastes resources during long idle periods
|
||||
# and still incurs latency.
|
||||
@app.websocket("/ws/maintenance/events")
|
||||
async def maintenance_events_websocket(websocket: WebSocket):
|
||||
"""
|
||||
|
||||
Reference in New Issue
Block a user