/* Brain of Reese — Git sources admin page (phase 35, task 04). * * The page module for /git-sources.html: the admin-only manager for the * stored git source list (git-sources table, phase 35 tasks 01/02). * This module is the single owner of the page's behaviour: * * • boot — initSharedHeader() (one cached whoami, shared with the * header toggling): anonymous → the sign-in gate shows and the * manager stays hidden (the exact Sources page gate pattern, and * NO /api/git-sources call is made); admin → gate hidden, * #git-sources-content revealed, loadSources(). * • loadSources() — GET /api/git-sources → the table rows * (#git-sources-tbody), the env-fallback note's visibility * (from_env), and the empty state. URLs are ALWAYS rendered with * textContent — never innerHTML (they may embed user:pass@ * credentials; phase 32's masking discipline). Non-2xx or a * network failure renders the role="alert" load error with a * retry button — never a stuck page. * • add — #git-source-form submit → POST /api/git-sources {url}. * §7.4 never-stale: the button disables + relabels "Adding…" * while the request is out, re-enables ("Add source") on success * AND failure. 201 clears the input, reloads the list, and * focuses the new row's Remove button (a11y); a failure (409 * duplicate, 422 shape) shows the server detail inline under the * form (role="alert", 422 shape-aware like the tuning forms) and * keeps the input — the instruction survives. * • remove — a row's Remove button asks window.confirm first * (removal prunes the documents only on the NEXT sync — the * confirm says so). Cancel → nothing; ok → the row button * disables, DELETE /api/git-sources/{id}, loadSources(). A * failure shows a per-row role="alert" error and re-enables the * button. Env-fallback rows (id null — the list comes from * BOR_GIT_SOURCES, not the table) carry no Remove: nothing is * stored to remove — they show a "from .env" tag instead. * • announce(msg) — #git-sources-announcer (role=status, * aria-live=polite): the screen-reader confirmation for loads, * adds, and removals. * * Scope boundary (phase locked decisions): adding or removing a repo * does NOT clone, import, or prune — the header's Sync sources button * (module-owned in assets/header.js) performs that; the page's hint * box says so. * * The shared header module loads through this script's own relative * import ("./header.js") — a hoisted import evaluated before this body * runs (single-evaluation design: no direct