baa4e75d4a
User report: opened a parsing-in-progress doc, /spans was hit ONCE on mount and then never again — but parse_status said 'processing' and the LIVE badge was lit. The setTimeout-based recursive scheduler had a fragile property: any unexpected throw or skipped tick between the finally block and the schedule check could silently strand the loop with no way to re-arm. Refactor to a self-healing pattern: - setInterval polling: a single 2s tick checks "should we be polling? and is no fetch in flight?" — if both, fire fetchSpans. Decision is re-evaluated every tick from current reactive state, so flipping status from 'processing' to 'completed' between ticks naturally stops further fetches. - ensurePolling() helper is idempotent — calling it from fetchSpans's finally, from a parse_status watcher, and from onMounted all just arm the same single interval if not already running. - watch(data.value.parse_status) acts as a watchdog: any time the status flips into 'pending'/'processing', re-arm. Any time it flips out, tear down. This is the belt-and-suspenders that ensures the loop can't get stranded even if a future regression re-introduces a missed-schedule path. - fetchInFlight guard prevents overlapping fetches if a slow backend takes longer than POLL_INTERVAL_MS to respond. - Console warns on fetch failure — silent rejections is exactly what hid this bug. With this in place we can verify the loop's health from DevTools console. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>