Commit bb13eb2
authored
fix: report per-item failures in bulk operation status instead of aborting the batch (#2076)
Started as the residual-dead-code sweep from #2043's final review; reviewing that sweep surfaced real defects in the operation-status path, fixed here as well.
Fixes (user-facing):
* ha_get_operation_status with a list of IDs re-raised the first failed/timed-out/not-found operation's error, discarding the status of every other operation and making the failed/timeout aggregation unreachable. Per-item errors now become structured entries in detailed_results — preserving the full structured payload including top-level context keys — with a not_found bucket in the summary and a top-level success key per the repo-wide response contract.
* The bulk path polled each pending operation serially for the full 10s single-op timeout while the docstring promised "a short internal timeout"; it now polls all operations concurrently under the caller's timeout_seconds window.
* cleanup_expired_operations never reclaimed TIMEOUT-status operations (get_operation() flips an expired PENDING op to TIMEOUT in place on the read path), so polled-after-timeout operations accumulated for the process lifetime. TIMEOUT now shares FAILED's 60s TTL, anchored on completion_time, and the sweep-marked path keeps the same terminal minute as the read path so a poll after expiry reports timeout rather than not_found regardless of which path noticed first. The overflow trim considers all terminal statuses — never in-flight PENDING — so max_operations is enforceable except against a backlog of purely in-flight PENDING operations, which expire on their own timeouts.
* completion_percentage is now the terminal fraction (completed + failed + not_found over total) so it agrees with all_complete; success_rate keeps the successful fraction. The ha_get_operation_status docstring starts with an approved verb (Get) with the locale source baseline regenerated.
* get_domain_handler required a dot in its argument while both control paths pass a bare domain, so every per-domain valid_actions table was unreachable and the default handler's on/off/toggle applied everywhere — climate's heat/cool/set were rejected upfront as invalid actions (closes #2090). The lookup now accepts an entity ID or a bare domain, the tables were made purely additive and resolvable (short on/off/toggle actions restored where their services exist; unmappable entries mapped — cover stop/set, media next/previous, lock open, alarm arm_*/disarm, climate heat_cool — or trimmed), and a new e2e pin drives every table action through the resolver against the live service list.
Dead-code residuals from #2043 (internal):
* settings_ui/locales/es.json: drop the orphaned advanced.entity_search_limit.* keys (es.json merged via #2055 mid-review; the help string still named ha_search_entities).
* tools/util_helpers.py: remove strip_internal_fields (its only production caller was the server bridge deleted in #2043); its rationale moves into public_fields' docstring.
* client/websocket_listener.py: drop the unread self.settings assignment and its import.
* tests/src/unit/test_websocket_listener.py: fixture no longer builds the stats dict deleted in #2043.
* utils/operation_manager.py: remove OperationStatus.CANCELLED (unproducible since cancel_operation went; nothing ever read it).
Tests: new unit pins for the cleanup TTLs (terminal anchor, discriminating overflow trim), the bulk summary contract (completion_percentage semantics, preserved per-item payload), and the shared wait window (a pending operation flipping to completed inside the window, red under a 0s snapshot); real per-item assertions in the bulk-status e2e test via MCPAssertions; the mixed-entities e2e block in test_network_errors made live end to end (the bulk response has no top-level success key, so the gate now mirrors assert_mcp_success's bulk indicator; the action string is "on" — "turn_on" was rejected for every entity; value checks pin that the valid entities succeed and both fabricated entities fail as per-item entries; a 5s status window avoids dead-heating the 10s call wrapper); projection pin for the area-only branch's public_fields strip; flip task joined via asyncio.gather per CodeQL.1 parent 83168a4 commit bb13eb2
16 files changed
Lines changed: 817 additions & 239 deletions
File tree
- src/ha_mcp
- client
- tools
- utils
- tests/src
- e2e
- error_handling
- workflows/device_control
- unit
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
9 | 9 | | |
10 | 10 | | |
11 | 11 | | |
12 | | - | |
13 | 12 | | |
14 | 13 | | |
15 | 14 | | |
| |||
21 | 20 | | |
22 | 21 | | |
23 | 22 | | |
24 | | - | |
25 | 23 | | |
26 | 24 | | |
27 | 25 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
301 | 301 | | |
302 | 302 | | |
303 | 303 | | |
304 | | - | |
305 | | - | |
306 | | - | |
307 | | - | |
308 | | - | |
309 | | - | |
310 | | - | |
311 | | - | |
312 | | - | |
313 | | - | |
| 304 | + | |
| 305 | + | |
| 306 | + | |
| 307 | + | |
314 | 308 | | |
315 | | - | |
316 | | - | |
317 | | - | |
318 | | - | |
319 | | - | |
320 | | - | |
321 | | - | |
322 | | - | |
| 309 | + | |
| 310 | + | |
| 311 | + | |
| 312 | + | |
323 | 313 | | |
324 | | - | |
325 | | - | |
326 | | - | |
327 | | - | |
328 | | - | |
| 314 | + | |
| 315 | + | |
| 316 | + | |
| 317 | + | |
| 318 | + | |
| 319 | + | |
| 320 | + | |
| 321 | + | |
| 322 | + | |
| 323 | + | |
| 324 | + | |
| 325 | + | |
| 326 | + | |
| 327 | + | |
| 328 | + | |
| 329 | + | |
| 330 | + | |
| 331 | + | |
| 332 | + | |
| 333 | + | |
| 334 | + | |
| 335 | + | |
| 336 | + | |
| 337 | + | |
| 338 | + | |
329 | 339 | | |
330 | | - | |
| 340 | + | |
| 341 | + | |
| 342 | + | |
| 343 | + | |
| 344 | + | |
| 345 | + | |
| 346 | + | |
| 347 | + | |
331 | 348 | | |
332 | 349 | | |
333 | 350 | | |
| |||
1274 | 1291 | | |
1275 | 1292 | | |
1276 | 1293 | | |
1277 | | - | |
| 1294 | + | |
1278 | 1295 | | |
1279 | 1296 | | |
1280 | 1297 | | |
1281 | 1298 | | |
| 1299 | + | |
| 1300 | + | |
| 1301 | + | |
| 1302 | + | |
| 1303 | + | |
| 1304 | + | |
1282 | 1305 | | |
1283 | 1306 | | |
| 1307 | + | |
1284 | 1308 | | |
1285 | 1309 | | |
1286 | 1310 | | |
| |||
1296 | 1320 | | |
1297 | 1321 | | |
1298 | 1322 | | |
1299 | | - | |
1300 | | - | |
1301 | | - | |
1302 | | - | |
1303 | | - | |
| 1323 | + | |
| 1324 | + | |
| 1325 | + | |
| 1326 | + | |
| 1327 | + | |
| 1328 | + | |
| 1329 | + | |
| 1330 | + | |
| 1331 | + | |
| 1332 | + | |
| 1333 | + | |
| 1334 | + | |
| 1335 | + | |
| 1336 | + | |
| 1337 | + | |
| 1338 | + | |
| 1339 | + | |
| 1340 | + | |
| 1341 | + | |
| 1342 | + | |
| 1343 | + | |
| 1344 | + | |
| 1345 | + | |
| 1346 | + | |
| 1347 | + | |
| 1348 | + | |
| 1349 | + | |
| 1350 | + | |
| 1351 | + | |
| 1352 | + | |
| 1353 | + | |
| 1354 | + | |
| 1355 | + | |
| 1356 | + | |
| 1357 | + | |
| 1358 | + | |
1304 | 1359 | | |
1305 | 1360 | | |
1306 | 1361 | | |
1307 | 1362 | | |
| 1363 | + | |
1308 | 1364 | | |
1309 | 1365 | | |
| 1366 | + | |
| 1367 | + | |
| 1368 | + | |
1310 | 1369 | | |
| 1370 | + | |
1311 | 1371 | | |
1312 | 1372 | | |
1313 | 1373 | | |
| 1374 | + | |
1314 | 1375 | | |
1315 | 1376 | | |
1316 | 1377 | | |
1317 | 1378 | | |
1318 | | - | |
| 1379 | + | |
| 1380 | + | |
| 1381 | + | |
| 1382 | + | |
| 1383 | + | |
| 1384 | + | |
| 1385 | + | |
1319 | 1386 | | |
1320 | 1387 | | |
1321 | 1388 | | |
| |||
1324 | 1391 | | |
1325 | 1392 | | |
1326 | 1393 | | |
1327 | | - | |
| 1394 | + | |
1328 | 1395 | | |
1329 | 1396 | | |
1330 | 1397 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1385 | 1385 | | |
1386 | 1386 | | |
1387 | 1387 | | |
1388 | | - | |
| 1388 | + | |
1389 | 1389 | | |
1390 | 1390 | | |
1391 | 1391 | | |
1392 | 1392 | | |
1393 | | - | |
1394 | | - | |
| 1393 | + | |
| 1394 | + | |
| 1395 | + | |
| 1396 | + | |
1395 | 1397 | | |
1396 | 1398 | | |
1397 | 1399 | | |
| |||
1401 | 1403 | | |
1402 | 1404 | | |
1403 | 1405 | | |
1404 | | - | |
| 1406 | + | |
1405 | 1407 | | |
1406 | 1408 | | |
1407 | 1409 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
77 | 77 | | |
78 | 78 | | |
79 | 79 | | |
80 | | - | |
81 | | - | |
82 | | - | |
| 80 | + | |
| 81 | + | |
83 | 82 | | |
84 | 83 | | |
85 | 84 | | |
86 | 85 | | |
87 | 86 | | |
88 | 87 | | |
89 | | - | |
90 | | - | |
91 | | - | |
92 | | - | |
93 | | - | |
94 | | - | |
95 | | - | |
96 | | - | |
97 | | - | |
98 | | - | |
99 | | - | |
100 | | - | |
101 | | - | |
102 | | - | |
103 | | - | |
104 | | - | |
105 | | - | |
106 | | - | |
107 | | - | |
108 | | - | |
109 | | - | |
110 | | - | |
111 | | - | |
112 | | - | |
113 | | - | |
114 | | - | |
115 | | - | |
116 | | - | |
117 | 88 | | |
118 | 89 | | |
119 | 90 | | |
| |||
0 commit comments