personal memory agent
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

convey/observer: adopt wave 0 error primitives for loadObservers (wave 1)

Migrate loadObservers to apiJson(...) with minimal shape validation, upgrade the loading scaffold to the shared surface-state loading markup, and route first-paint and refresh failures through replaceLoading('observersList', errorCard(...)). Drop showLocalError({ retry: true }) from this loader path while preserving showLocalError itself for in-file action errors.

Co-authored-by: OpenAI Codex <codex@openai.com>

+29 -7
+29 -7
apps/observer/workspace.html
··· 509 509 </form> 510 510 </section> 511 511 <div id="observersList" role="list"> 512 - <div class="no-observers">Loading...</div> 512 + <div class="surface-state surface-state--loading" role="status" aria-busy="true"> 513 + <div class="surface-state-spinner" aria-hidden="true"></div> 514 + <span class="surface-state-text" data-role="loading-status">Loading observers...</span> 515 + </div> 513 516 </div> 514 517 </section> 515 518 </div> ··· 744 747 lastPollTime = Date.now(); 745 748 if (countdownInterval) { clearInterval(countdownInterval); countdownInterval = null; } 746 749 try { 747 - const response = await fetch('/app/observer/api/list'); 748 - const payload = await response.json(); 750 + const payload = await window.apiJson('/app/observer/api/list'); 751 + if (!payload || typeof payload !== 'object' || !Array.isArray(payload.observers)) { 752 + throw new window.ApiError({ 753 + status: 200, 754 + statusText: 'OK', 755 + serverMessage: 'Malformed observer response', 756 + url: '/app/observer/api/list', 757 + cause: 'parse', 758 + }); 759 + } 760 + if (payload.thresholds && typeof payload.thresholds !== 'object') { 761 + throw new window.ApiError({ 762 + status: 200, 763 + statusText: 'OK', 764 + serverMessage: 'Malformed observer response', 765 + url: '/app/observer/api/list', 766 + cause: 'parse', 767 + }); 768 + } 749 769 if (payload && payload.thresholds) { 750 770 thresholds = payload.thresholds; 751 771 } 752 - const observers = payload?.observers || []; 772 + const observers = payload.observers; 753 773 754 774 // Rebuild on every poll so grouped headers stay aligned with server ordering. 755 775 const activeEl = document.activeElement; ··· 776 796 if (target) target.focus(); 777 797 } 778 798 } catch (err) { 779 - observersList.innerHTML = '<div class="no-observers">couldn\'t load observers</div>'; 780 - showLocalError('couldn\'t load observers — the server may be unreachable. it will retry automatically, or you can retry now.', { retry: true }); 781 - console.error('Failed to load observers:', err); 799 + window.SurfaceState.replaceLoading('observersList', window.SurfaceState.errorCard({ 800 + heading: 'Couldn\'t load observers', 801 + desc: 'Reload the page to try again.', 802 + serverMessage: err?.serverMessage || err?.message || '', 803 + })); 782 804 } 783 805 } 784 806