You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
bug(slm-frontend): update-all per-stage stage.message is never rendered — operators see that a run was partial, not why #13156
autobot-slm-backend/api/code_sync.py writes a per-stage human-readable summary into stage.message — including the explanation for why a fleet stage was skipped or partially applied — but CodeSyncView.vue never renders it anywhere.
Found while deriving UpdateAllStage/UpdateAllJob from the generated OpenAPI contract (#13138, PR #13152 / 66279b115).
Why it matters
This compounds the defect fixed in #13152. UpdateAllJob.status omitted 'partial', so a partially-successful update-all run matched neither the failure banner (CodeSyncView.vue:913, tests 'failed') nor the success banner (:921, tests 'completed' | 'already_current') and ended with no outcome shown at all. That is now fixed — but the backend's own explanation of what was partial still has no route to the screen, because stage.message is never displayed.
So an operator can now see that a run was partial without seeing why, when the server already computed and sent the reason.
Suggested fix
Render stage.message in the per-stage list, at minimum for non-completed stages. skipped_fleet_nodes (code_sync.py:4270) is now present in the derived type and gives the count to pair with it.
Not filed as part of #13152 because it is a rendering gap rather than a contract mismatch — the type is now correct; the template simply does not use the field.
Closed by PR #13167, merged to Dev_new_gui as 1adb1dc06 (closes #13156 and #13157 together — same surface, same underlying defect).
Both premises confirmed, and #13157 was worse than filed
stage.message was genuinely never rendered: all ten message references in CodeSyncView.vue are result.message from mutation responses (:285,:297,:333,:360,:386,:415,:416,:429,:570,:573). The stage track (:848-895) showed name, status, sha, deps badge and a log toggle — never the message.
getJobStatus had zero references outside its own definition (useCodeSync.ts:558) and export (:870) — not even a test, so "tests only" understated it.
Root cause:handleSyncSelected / handleSyncAll call syncFleet, receive a job_id for an asynchronous rollout, discard it, and show a green "Successfully synced N nodes" toast. A job that failed minutes later reported nothing at all.
My own filed guidance was wrong, and following it would have preserved the bug
#13156 said to render the message "at minimum for non-completed stages." That is incorrect. skipped_fleet_nodes (code_sync.py:4879) is folded into the fleet-stage summary at :4932-4938 while that stage is set to success — only the job becomes partial (:4940). Filtering to non-completed stages would have hidden exactly the message this issue was filed about. The message now renders for every stage that carries one, with a test pinning it on a success stage specifically.
Delivered
stage.message under each stage box (data-testid="stage-message"). A new Fleet Sync Jobs panel: the queued job_id is polled via getJobStatus (2s, until terminal) showing progress, failed count, failure_reason and per-node message; opening Advanced or hitting refresh calls getRecentJobs for the last 5 jobs with their reasons. Timer cleared in onUnmounted. Nothing deleted. Seven codeSyncView.* keys added to en.json (single-locale app).
In-scope backend fix:_list_jobs_from_db (autobot-slm-backend/api/code_sync.py:487) omitted failure_reason while _load_job_status_from_db (:470) set it — so every recent job would have rendered a null reason even after the frontend was wired. One line, no schema change.
Verification — including proof the tests are not vacuous
Non-vacuity:cp-swapping the pre-fix CodeSyncView.vue back in produced 8 failed / 7 passed in that file — the single new test that still passes is the negative case. Restored byte-identically afterwards.
Assertions read the DOM, not call counts: [data-testid="stage-message"] equals Updated 2/3 nodes (1 skipped — not operational) for a partial job whose fleet stage is success; [data-testid="fleet-job-failure-reason"] contains the ansible failure after clicking Sync All and advancing the poll timer; a polling test drives running → completed, asserts the text changes 1 / 2 nodes → 2 / 2 nodes, then that polling stops.
No AutoBot application code was executed (#13090) — frontend tooling only.
Activity
fix(slm-frontend): surface why an update-all was partial and why a fl…
mrveiss commentedon Jul 31, 2026
Closed by PR #13167, merged to
Dev_new_guias1adb1dc06(closes #13156 and #13157 together — same surface, same underlying defect).Both premises confirmed, and #13157 was worse than filed
stage.messagewas genuinely never rendered: all tenmessagereferences inCodeSyncView.vueareresult.messagefrom mutation responses (:285,:297,:333,:360,:386,:415,:416,:429,:570,:573). The stage track (:848-895) showed name, status, sha, deps badge and a log toggle — never the message.getJobStatushad zero references outside its own definition (useCodeSync.ts:558) and export (:870) — not even a test, so "tests only" understated it.Root cause:
handleSyncSelected/handleSyncAllcallsyncFleet, receive ajob_idfor an asynchronous rollout, discard it, and show a green "Successfully synced N nodes" toast. A job that failed minutes later reported nothing at all.My own filed guidance was wrong, and following it would have preserved the bug
#13156 said to render the message "at minimum for non-
completedstages." That is incorrect.skipped_fleet_nodes(code_sync.py:4879) is folded into the fleet-stage summary at:4932-4938while that stage is set tosuccess— only the job becomespartial(:4940). Filtering to non-completed stages would have hidden exactly the message this issue was filed about. The message now renders for every stage that carries one, with a test pinning it on asuccessstage specifically.Delivered
stage.messageunder each stage box (data-testid="stage-message"). A new Fleet Sync Jobs panel: the queuedjob_idis polled viagetJobStatus(2s, until terminal) showing progress, failed count,failure_reasonand per-nodemessage; opening Advanced or hitting refresh callsgetRecentJobsfor the last 5 jobs with their reasons. Timer cleared inonUnmounted. Nothing deleted. SevencodeSyncView.*keys added toen.json(single-locale app).In-scope backend fix:
_list_jobs_from_db(autobot-slm-backend/api/code_sync.py:487) omittedfailure_reasonwhile_load_job_status_from_db(:470) set it — so every recent job would have rendered a null reason even after the frontend was wired. One line, no schema change.Verification — including proof the tests are not vacuous
type-checkclean both sides.lint16 problems / 0 errors, identical pre-existing set. vitest 219 → 230, identical (empty) failing set.Non-vacuity:
cp-swapping the pre-fixCodeSyncView.vueback in produced 8 failed / 7 passed in that file — the single new test that still passes is the negative case. Restored byte-identically afterwards.Assertions read the DOM, not call counts:
[data-testid="stage-message"]equalsUpdated 2/3 nodes (1 skipped — not operational)for apartialjob whose fleet stage issuccess;[data-testid="fleet-job-failure-reason"]contains the ansible failure after clicking Sync All and advancing the poll timer; a polling test drivesrunning → completed, asserts the text changes1 / 2 nodes → 2 / 2 nodes, then that polling stops.No AutoBot application code was executed (#13090) — frontend tooling only.