From 0a618db10c493d27bbf70d39d6916be3c9f9275e Mon Sep 17 00:00:00 2001 From: Bryan Van Deusen Date: Wed, 1 Jul 2026 20:19:37 -0400 Subject: [PATCH] =?UTF-8?q?fix(agent):=20Status=20froze=20after=20one=20up?= =?UTF-8?q?date=20=E2=80=94=20conchint.textContent=20destroyed=20#capn?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit THE root cause of "the Status section doesn't update" (chased across several rounds; the backend was always healthy). `#capn` (the max-concurrency number) was nested inside `#conchint`:
… · max 8
and applyStatus() ran, every call: `capn.textContent=CAP` AND `conchint.textContent = '…max '+CAP`. Setting conchint.textContent replaces ALL of conchint's children — destroying the node. So: call 1: capn exists → tiles update → conchint.textContent DELETES capn call 2+: `capn.textContent` → "capn is not defined" (ReferenceError) → applyStatus throws on its FIRST line → aborts before any tile → frozen. This is exactly the observed "ticks a couple times then freezes", and why /gpu + /logs (which never touch capn) kept updating fine. The capn write was redundant anyway — conchint.textContent already renders the max. Remove the nested element and the capn.textContent line; the hint still shows "· max N". VERSION → .10. Co-Authored-By: Claude Opus 4.8 (1M context) --- agent/fc_agent/app.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/agent/fc_agent/app.py b/agent/fc_agent/app.py index 6ae7a1a..a484a9a 100644 --- a/agent/fc_agent/app.py +++ b/agent/fc_agent/app.py @@ -21,7 +21,7 @@ log = logging.getLogger("fc_agent.app") # Bump on every agent change. The page embeds this and /status reports it; the UI # warns to reload when they differ — so a stale browser-cached page can't be # mistaken for "the new image didn't deploy". (Belt-and-braces with no-store.) -VERSION = "2026-07-01.9 · server-computed jobs/min + downloads/min (poll-rate independent)" +VERSION = "2026-07-01.10 · fix Status freeze (conchint.textContent destroyed #capn)" logbuf.install() cfg = Config.from_env() @@ -217,7 +217,7 @@ _PAGE = """ -
auto-tuning downloaders to keep the GPU fed · max 8
+
auto-tuning downloaders to keep the GPU fed · max 8
@@ -286,7 +286,10 @@ _PAGE = """ applyStatus(s) } function applyStatus(s){ - CAP=s.max_concurrency||8; capn.textContent=CAP + // NB: don't write a separate `capn` element here — conchint.textContent below + // rewrites the whole hint (incl. the max), and any child element nested in it + // would be destroyed by that write, breaking the NEXT applyStatus call. + CAP=s.max_concurrency||8 // The backend owns the state now (stopped|starting|running|stopping) and drives // every transition, so the pill is always truthful — no client-side guessing // from active>0, which used to wedge on "stopping" forever. -- 2.52.0