Pages
The panel serves a git working tree. PANEL_REPO_URL is stored as origin. Pull rebases. Notify only rebuilds cache.
A panel repo is a git tree
Tracked paths: pages/, queries/, catalog/, components/, and data/.gitkeep. System pages (/events, /metrics, /infer, /cache) ship with the panel process. Product reports are pages/**/*.mdx under /p. Optional pages/root.mdx is /.
This product's template/ is the generic starting queries. A product keeps its own git URL.
Pull rebases local commits
PANEL_REPO_DIR is the working tree. Empty PANEL_REPO_URL uses that tree as-is. Set URL and no .git: clone --branch PANEL_REPO_BRANCH (default main) --single-branch. Existing .git: point origin at the URL, fetch, git rebase origin/<branch>.
Local commits stay on top. Conflict: stop and list paths. No -X ours. If paths cannot be listed, abort the rebase.
When pull runs
Process start. POST /api/repo/pull and POST /internal/repo-pull. After a successful pull, rebuild the UI dist when it does not match HEAD. A hosted deploy that restarts the panel runs start again. GET /api/repo sets behind when local and origin/<branch> SHAs differ.
Notify is not pull
Ingest that writes a row POSTs mapped panel URLs. POST /internal/notify refreshes cache only.
Cache
Named queries come from queries/<name>.sql and fenced ```sql <name> in MDX. Refresh copies named collector tables into cache.* first (Quack cannot scan the same table twice in one statement), then CTAS and COPY parquet.
Refresh-all uses the union of named tables. Single-query refresh recopies only that query's tables. GET /cache/index.json lists last status, last attempt, last success. Load gate 32 MiB or 500k rows. Backend CTAS is uncapped. GET /api/cache/<name> is 413 over the gate.
Collectors cover instances and relay. Getting started runs a local pair.