§ 01

專案概況 Project Overview

IMMI-Case- 是澳洲移民法庭案例的下載、管理與分析平台。從 AustLII 抓取 9 個法院 / 仲裁庭(149,016 筆案件,2000–2026)。提供全文搜尋、法官排行榜、視覺化分析,以及 LLM Council 智能摘要。 主要使用者是自助申請的移民申請人——非法律專業人士,在壓力情境下使用。

IMMI-Case- is a platform for downloading, managing, and analyzing Australian immigration tribunal cases. It scrapes 9 courts/tribunals from AustLII (149,016 cases, 2000–2026), offering full-text search, judge leaderboards, visual analytics, and LLM Council intelligent summaries. Primary users are self-represented immigration applicants — non-lawyers using the platform under high-stress conditions.

品牌定位:權威 · 精準 · 學術。美學方向:「法律典籍」(暖米白 + 深海軍藍 + 琥珀金)。

Brand positioning: Authoritative · Precise · Academic. Aesthetic: "Legal Codex" (warm off-white + deep navy + amber gold).

案件記錄 Case Records
149K
9 個法院,2000–2026 9 courts, 2000–2026
React 頁面 React Pages
28
含 auth sprint 新增 LoginPage incl. LoginPage from auth sprint
Worker 行數 Worker Lines
2,889
proxy.js,44 個 handler proxy.js, 44 handlers
judge-leaderboard
383x
13.1s → 0.034s warm 13.1s → 0.034s (warm hit)
主包瘦身 Bundle Reduction
−51%
460 KB → 225 KB (gzip 146→72) 460 KB → 225 KB (gzip 146→72)
暖路徑延遲 Warm Latency
<55ms
所有分析端點,Cache API 命中 All analytics endpoints, Cache API hit
§ 02

架構快照 Architecture Snapshot

系統架構圖 System Diagram
flowchart TB subgraph USER["User / Browser"] B["React SPA /app/*"] end subgraph CF["Cloudflare Edge proxy.js"] direction TB CRON["Cron 5min 8 endpoints"] CACHE["Cache API caches.default 19 handlers TTL 120-600s"] HD["Hyperdrive PG pool max-age 10s"] DO["FlaskBackend DO flask-v15 writes+LLM"] AUTH["AuthNonce DO replay protect"] R2["R2 judge-photos"] end subgraph SUPABASE["Supabase PostgreSQL"] DB["immigration_cases 149016 rows"] RLS["Row Level Security multi-tenant JWT"] end subgraph FLASK["Flask Container"] APP["Python 3.14 POST/PUT/DELETE LLM Council"] end B -->|GET /api/v1/*| CF B -->|writes /app/*| CF CRON -->|pre-warm every 5min| CACHE CF -->|cache hit return| CACHE CACHE -->|cache miss| HD HD -->|connection pool| DB DB --> RLS CF -->|writes + LLM| DO DO --> APP APP --> DB CF -->|Telegram Login| AUTH CF -->|photos| R2 style CACHE fill:#3a5a40,color:#f8f2e3,stroke:#2d4a35 style CRON fill:#9c7b30,color:#f8f2e3,stroke:#7a5f20 style HD fill:#4a6a7a,color:#f8f2e3,stroke:#3a5a6a style CF fill:#f0e8d6,stroke:#9c7b30 style SUPABASE fill:#ede4d0,stroke:#a8552e
讀路徑(GET): Browser → Worker → Cache API 命中直接回傳;未命中 → Hyperdrive → Supabase。 寫路徑: Worker → FlaskBackend DO → Flask → Supabase。Cron 每 5 分鐘預熱 8 個端點的 Cache API。 Read path (GET): Browser → Worker → Cache API hit returns immediately; miss → Hyperdrive → Supabase. Write path: Worker → FlaskBackend DO → Flask → Supabase. Cron pre-warms 8 Cache API endpoints every 5 minutes.

三層快取架構 3-Layer Cache Architecture

  • L1 · Cache API — per-PoP HTTP 快取,caches.default.match/put,key = Request URL,TTL = Cache-Control max-age。零費用,<1ms 命中延遲。 — per-PoP HTTP cache, caches.default.match/put, key = Request URL, TTL = Cache-Control max-age. Zero cost, <1ms hit latency.
  • L2 · Hyperdrive — PostgreSQL TCP 連線池 + query-result 快取(max_age=10s)。跨請求保持連線存活,Worker 只傳 connectionString。 — PostgreSQL TCP connection pool + query-result cache (max_age=10s). Keeps connections alive across requests; Worker only passes connectionString.
  • L3 · Cron Pre-warm — */5 * * * * 主動預熱 L1,讓第一個使用者不用等冷啟動(5 分鐘窗口內)。 — */5 * * * * actively pre-warms L1 so the first user never waits for a cold start (within a 5-minute window).

技術棧一覽 Tech Stack

  • Cloudflare Workers — Edge runtime,nodejs_compat,60s CPU cap — Edge runtime, nodejs_compat, 60s CPU cap
  • React 18 + Vite 6 + TS — 28 頁,lazy code-split,SPA at /app/ — 28 pages, lazy code-split, SPA at /app/
  • 🐍 Flask Python 3.14 — 寫路徑,LLM Council,CSV 匯出 — write path, LLM Council, CSV export
  • 🐘 Supabase PostgreSQL — 149K 案件,Row Level Security — 149K cases, Row Level Security
  • 🔐 Telegram Login + JWT — HS256,5min access + 7d refresh cookie,multi-tenant RLS — HS256, 5min access + 7d refresh cookie, multi-tenant RLS
§ 03

近期動態(過去 2 週,50 commits) Recent Activity (Past 2 Weeks, 50 commits)

Performance · Waves 1–6 效能優化 Performance · Waves 1–6 Optimizations

P0-1 · judge-leaderboard: SQL 索引 + Cache API SQL Index + Cache API

根本原因:LATERAL unnest 在 149K 行上展開法官陣列,執行 3 次全表掃描。加索引 7.3x → 再加 Cache API TTL=600s → 合計 383x。

Root cause: LATERAL unnest expanding the judges array over 149K rows, causing 3 full-table scans. Adding an index yielded 7.3x; adding Cache API TTL=600s combined to 383x total.

13.12s → 0.034s warm (383x) 88b2d2b

P0-2 · 主包瘦身(i18n chunk 分割) Bundle Slim-down (i18n chunk split)

vite.config.ts manualChunks 從 object 改為 arrow function。i18next + react-i18next + 2x locale JSON(115 KB)拆成獨立 lazy chunk。

Changed vite.config.ts manualChunks from object to arrow function. i18next + react-i18next + 2× locale JSON (115 KB) split into an independent lazy chunk.

460 KB → 225 KB raw (−51%) 146 KB → 72 KB gzip 3ceb05e

P1-3 · Cron Warm-up(*/5 * * * *)

wrangler.toml 加 cron trigger,scheduled() 執行 SELECT 1 保持 Worker isolate + Hyperdrive TCP 連線池存活,消除冷啟動。

Added cron trigger to wrangler.toml; scheduled() runs SELECT 1 to keep Worker isolate + Hyperdrive TCP connection pool alive, eliminating cold starts.

冷啟動 ~4.2s → 5min 後不再發生 Cold start ~4.2s → eliminated after 5min 71b302b

Wave 2 · judges / legal-concepts / visa-families / success-rate

success-rate 冷啟動 8.9s(LATERAL unnest judges 欄位),visa-families 5.5s。四個端點加 Cache API,納入 cron 預熱。

success-rate cold start 8.9s (LATERAL unnest on judges column), visa-families 5.5s. All four endpoints given Cache API and included in cron pre-warming.

success-rate 278x visa-families 133x 75049d3

Wave 3 · concept-effectiveness / cooccurrence / trends

三個 concept 分析端點,LATERAL unnest legal_concepts 欄位,其中 concept-cooccurrence 冷啟動 13.0s。全部加 Cache API TTL=600s,URL key 含 limit/min_count 參數。

Three concept-analysis endpoints using LATERAL unnest on legal_concepts; concept-cooccurrence cold start was 13.0s. All given Cache API TTL=600s with URL keys including limit/min_count params.

concept-cooccurrence 174x 764ed1c

Wave 4–5 · analytics/filter-options / judge-profile / judge-compare / taxonomy/countries

judge-compare 冷啟動 6.6s(平行 LATERAL unnest 最多 4 法官)、taxonomy/countries 4.3s(GROUP BY country_of_origin 149K 行)。

judge-compare cold start 6.6s (parallel LATERAL unnest for up to 4 judges); taxonomy/countries 4.3s (GROUP BY country_of_origin over 149K rows).

judge-compare 95x taxonomy/countries 68x 4c24ef6 / e9a2ff5

Wave 6 · Cron 預熱從 4 → 8 個端點 Cron Pre-warming Expanded 4 → 8 Endpoints

Wave 5 部署後全部端點顯示冷啟動。根本原因:cron 只預熱 4 個端點,curl 打到不同 CF PoP。scheduled() 擴展至 8 個 handler,覆蓋所有高影響分析頁。

After Wave 5 deploy, all endpoints showed cold starts. Root cause: cron only warmed 4 endpoints while curls hit different CF PoPs. Expanded scheduled() to 8 handlers, covering all high-impact analytics pages.

8 endpoints pre-warmed every 5min aba5e8a
Auth · Telegram Login + Multi-tenant JWT + RLS Auth · Telegram Login + Multi-tenant JWT + RLS

Auth Sprint · 完整 auth 流程 Full Auth Flow

Telegram Widget → Worker HMAC 驗證 → AuthNonce DO replay 防護 → DB upsert user → HS256 JWT(5min access + 7d refresh cookie)→ Supabase RLS 透過 SET LOCAL request.jwt.claims 實現多租戶隔離。

Telegram Widget → Worker HMAC verify → AuthNonce DO replay protection → DB upsert user → HS256 JWT (5min access + 7d refresh cookie) → Supabase RLS multi-tenant isolation via SET LOCAL request.jwt.claims.

multi-tenant RLS kid rotation ba9fba0

Auth 強化 · 結構化日誌 + UX 硬化 + Sidebar 接入 Hardening · Structured Logging + UX Polish + Sidebar Integration

每個認證 DB 查詢發出 JSON 結構化日誌(kid、tenant_id、user_id、query_ms、ok)。4 個 schema mismatch 修正。Sidebar + Topbar 接入 auth state。

Every authenticated DB query emits JSON structured logs (kid, tenant_id, user_id, query_ms, ok). 4 schema mismatches fixed. Sidebar + Topbar wired to auth state.

b2fa78d / 6b622bb / f069d1e
Testing · RLS 整合測試 + k6 壓力測試 Testing · RLS Integration Tests + k6 Load Tests

新增 AC5/7/8/9/10 驗證腳本 New AC5/7/8/9/10 Validation Scripts

tests/integration/rls_isolation.sql(跨租戶查詢驗證)、test_revoke_member.py(成員移除閉環,高風險 race condition 修正)、tests/k6/auth-latency.js(auth 端點壓力測試,thresholds:p95<800ms)。

tests/integration/rls_isolation.sql (cross-tenant query validation), test_revoke_member.py (member-removal closed-loop with high-risk race condition fix), tests/k6/auth-latency.js (auth endpoint load test, thresholds: p95<800ms).

729c750 / 55374af
§ 04

決策記錄 Decision Log

效能 Performance
Cache API 而非 Workers KV 或 Redis Cache API over Workers KV or Redis
分析結果快取選用 caches.default,TTL 120–600s。 Analytics results cached via caches.default, TTL 120–600s.
Workers KV:$5/百萬寫入,全球複製 ~60s 一致性延遲。Redis:需獨立部署,RTT 20–50ms。Cache API:零費用,與 CDN 同 PoP,<1ms 命中延遲。對快取失效敏感度低的分析數據,per-PoP 特性無妨。 Workers KV: $5/million writes, ~60s global replication latency. Redis: requires separate deployment, RTT 20–50ms. Cache API: zero cost, co-located with CDN PoP, <1ms hit latency. For analytics data with low cache-invalidation sensitivity, per-PoP behavior is acceptable.
→ KV 適合全球靜態配置;Redis 適合 pub/sub 或計數器場景。 → KV suits global static config; Redis suits pub/sub or counter patterns.
效能 Performance
Cron 5 分鐘(非 1 或 10 分鐘) Cron Every 5 Minutes (not 1 or 10)
*/5 * * * * 作為 Cache API 預熱頻率。 */5 * * * * as the Cache API pre-warm frequency.
Hyperdrive TCP 連線池空閒逾時 ~3–5 分鐘。最短 TTL 端點 analytics/filter-options=120s。5 分鐘確保:(1) 連線池不斷;(2) 最短 TTL 端點在過期前已被預熱。1 分鐘浪費 DB;10 分鐘連線池可能斷開。 Hyperdrive TCP pool idle timeout ~3–5 minutes. Shortest TTL endpoint analytics/filter-options=120s. 5 minutes ensures: (1) pool never drops; (2) shortest-TTL endpoint is re-warmed before expiry. 1min wastes DB; 10min may drop the pool.
→ 若 TTL 調短至 <5min,cron 頻率需同步調整。 → If TTL is shortened to <5min, cron frequency must be adjusted accordingly.
產品 Product
接受 Recharts 413 KB(不換 visx) Accept Recharts 413 KB (skip visx migration)
P1-4 評估後,owner 決定接受現狀。 After P1-4 evaluation, owner decided to accept the status quo.
用戶原話:「I don't think 200 KB is gonna be a lot to speed up the web app.」Recharts chunk 是 lazy-loaded,gzip 後 120 KB,只在導航到圖表頁時載入。切換 visx 需重寫 24 個元件——投資報酬率極低。 Owner verbatim: "I don't think 200 KB is gonna be a lot to speed up the web app." The Recharts chunk is lazy-loaded, 120 KB gzipped, loaded only when navigating to chart pages. Switching to visx requires rewriting 24 components — extremely low ROI.
→ 若 Sankey 圖日後被移除,可節省 30–50 KB 再評估。 → If the Sankey chart is removed later, reassess after the 30–50 KB savings.
效能 Performance
Hyperdrive max_age 60s → 10s
wrangler hyperdrive update 設 caching.max_age=10s,stale_while_revalidate=0s。 wrangler hyperdrive update sets caching.max_age=10s, stale_while_revalidate=0s.
前端 setTimeout(invalidate, 10s) workaround 需要寫入後 ≤10s 可見。預設 60s 導致刪除/新增後 UI 不一致。 Frontend setTimeout(invalidate, 10s) workaround requires writes to be visible within ≤10s. Default 60s caused UI inconsistencies after delete/create operations.
→ 可回滾:wrangler hyperdrive update <id> --max-age 60 --swr 15 → Rollback: wrangler hyperdrive update <id> --max-age 60 --swr 15
安全 Security
JWT 雙密鑰輪換(kid claim) JWT Dual-Key Rotation (kid claim)
JWT_SECRET_CURRENT + JWT_SECRET_PREVIOUS,token 帶 kid。驗證時雙密鑰嘗試。 JWT_SECRET_CURRENT + JWT_SECRET_PREVIOUS; tokens carry kid claim. Verification tries both keys.
允許無停機密鑰輪換:部署新密鑰時舊 token 在 5min access TTL 內仍有效。 Enables zero-downtime key rotation: old tokens remain valid within the 5min access TTL when a new key is deployed.
→ 已知限制:refresh token 無 jti,無法即時撤銷(7 天盲點)。 → Known limitation: refresh tokens have no jti; instant revocation is impossible (7-day blind spot).
架構 Architecture
getSql(env) 每請求創建,絕不快取模組層級 getSql(env) Created Per Request, Never Module-Level Cached
每個 handler 開頭呼叫 getSql(env),不快取為 module-level singleton。 Every handler calls getSql(env) at the start; never cached as a module-level singleton.
Workers I/O context 綁定於單一請求。模組層級 singleton 跨請求重用 → "Cannot perform I/O on behalf of a different request" 錯誤。Hyperdrive 負責真正的 TCP 連線池。 Workers I/O context is bound to a single request. Module-level singletons reused across requests cause "Cannot perform I/O on behalf of a different request" errors. Hyperdrive handles the actual TCP connection pooling.
→ CLAUDE.md 明確列為 Gotcha,任何未來 Worker 開發者必須知道。 → Explicitly listed as a Gotcha in CLAUDE.md; every future Worker developer must know this.
§ 05

狀態儀表板 State Dashboard

✓ 運行正常 ✓ Working
  • 19 個 handler 已加 Cache API,warm <55ms 19 handlers with Cache API, warm <55ms
  • 8 個端點 cron 每 5min 預熱 8 endpoints cron-warmed every 5min
  • judge-leaderboard:13.1s → 0.034s
  • 主包:460 KB → 225 KB (−51%) Bundle: 460 KB → 225 KB (−51%)
  • Telegram Login + JWT + RLS
  • R2 法官照片 bucket R2 judge photos bucket
  • LLM Council(CF AI Gateway)
  • RLS 隔離整合測試通過 RLS isolation integration tests passing
  • k6 auth 壓力測試通過 k6 auth load tests passing
→ 進行中 → In Progress
  • Wave 6 後穩定性觀察中 Stability monitoring post-Wave 6
  • CLAUDE.md 頁數待更新 27→28 CLAUDE.md page count pending update 27→28
  • (無主動 sprint) (no active sprint)
⚠ 已接受限制 ⚠ Accepted Limits
  • Recharts 413 KB(owner 接受) Recharts 413 KB (owner accepted)
  • per-PoP cache:非 APAC 首次冷 per-PoP cache: non-APAC first hit cold
  • Federal Court DNS 斷線 Federal Court DNS broken
  • AATA 2025+ → 改用 ARTA AATA 2025+ → switched to ARTA
✗ 已知技術債 ✗ Known Tech Debt
  • Refresh token 無 jti,7d 撤銷盲點 Refresh token no jti, 7d revocation blind spot
  • judge-profile/compare 未加入 cron judge-profile/compare not in cron
  • CLAUDE.md 頁數 27(實際 28) CLAUDE.md page count 27 (actual 28)
  • LATERAL unnest 未 pre-materialized LATERAL unnest not pre-materialized
  • lib/api.ts 1304 行(超上限) lib/api.ts 1304 lines (over limit)
§ 06

心智模型精要 Mental Model Essentials

§ 07

認知技術債熱點 Tech Debt Hotspots

高風險 High Risk
Refresh Token 7 天撤銷盲點 Refresh Token 7-Day Revocation Blind Spot
無 jti claim,無伺服器端 session 表。被盜的 refresh token 最多 7 天內可持續 mint 新 access token。5min access TTL 限制讀路徑損害;寫路徑每次重新驗證 JWT。 No jti claim, no server-side session table. A stolen refresh token can mint new access tokens for up to 7 days. 5min access TTL limits read-path damage; write path re-validates JWT on every request.
修復: Fix: 建立 refresh_sessions(jti, user_id, expires_at, revoked_at) 表,refresh token 加 jti claim,AuthNonce DO 或 DB 驗證 jti 未撤銷。 Create a refresh_sessions(jti, user_id, expires_at, revoked_at) table, add jti claim to refresh tokens, and validate jti is not revoked via AuthNonce DO or DB.
高風險 High Risk
CLAUDE.md 頁數過時(27 → 實際 28) CLAUDE.md Page Count Stale (27 → actual 28)
Auth sprint 新增了 LoginPage.tsx,但 CLAUDE.md §架構 仍寫 "27 React pages"。P2-5 事實刷新沒有包含這個 auth sprint 的變更。 Auth sprint added LoginPage.tsx, but CLAUDE.md §Architecture still says "27 React pages". The P2-5 fact-refresh did not include this auth sprint change.
一行修復: One-line fix: CLAUDE.md "27 React pages" → "28 React pages(含 auth sprint 新增 LoginPage)"。 CLAUDE.md "27 React pages" → "28 React pages (incl. LoginPage added in auth sprint)".
中等風險 Medium Risk
judge-profile / judge-compare 未加入 Cron judge-profile / judge-compare Not in Cron
兩個端點冷啟動 3.4s / 6.6s(已加 Cache API,warm <70ms)。Wave 6 cron 擴展到 8 個端點,但未包含這兩個。部署後、首個使用者訪問 Judge Detail/Compare 前仍是冷的。 Two endpoints with cold starts of 3.4s / 6.6s (Cache API added, warm <70ms). Wave 6 cron expanded to 8 endpoints but excluded these two. After deploy, Judge Detail/Compare is still cold before the first user visit.
修復: Fix: scheduled() 加 handleAnalyticsJudgeProfile + handleAnalyticsJudgeCompare 的預熱呼叫(含典型 name/names 參數)。 Add pre-warming calls for handleAnalyticsJudgeProfile + handleAnalyticsJudgeCompare to scheduled() (with typical name/names params).
中等風險 Medium Risk
per-PoP Cache 限制未對開發者透明 per-PoP Cache Limitation Not Documented for Developers
Cron 預熱只覆蓋一個 PoP。非 APAC 開發者 / curl 測試可能命中不同 PoP,看到冷啟動而困惑(「為什麼有 cache 還是冷?」)。容易誤診為 cache 失效。 Cron warms only one PoP. Non-APAC developers / curl tests may hit a different PoP, see cold starts, and be confused ("why is it cold with a cache?"). Easy to misdiagnose as cache invalidation.
文件化: Document: CLAUDE.md 加「Cache API per-PoP」說明,解釋 cron 覆蓋範圍 + 真實 APAC 用戶的穩定暖路徑。 Add "Cache API per-PoP" explanation to CLAUDE.md, covering cron coverage scope and the reliable warm path for real APAC users.
中等風險 Medium Risk
LATERAL unnest 未 Pre-materialized LATERAL unnest Not Pre-materialized
judges / legal_concepts 在查詢時即時 unnest。Cache API 是目前的緩解,但 TTL 到期後仍需跑一次 3–13s 的昂貴 SQL。每次 cache miss 即是一次對 DB 的重擊。 judges / legal_concepts are live-unnested at query time. Cache API is the current mitigation, but after TTL expiry it still runs one expensive 3–13s SQL. Every cache miss is a DB hammer blow.
長期修復: Long-term fix: 建立 judge_appearances materialized view 或 generated column,定期 refresh。查詢從 O(n×m) 降至 O(n),TTL 可縮短或移除。 Create a judge_appearances materialized view or generated column with periodic refresh. Queries drop from O(n×m) to O(n), allowing TTL to be shortened or removed.
低風險 Low Risk
lib/api.ts 1304 行(超 800 行上限) lib/api.ts 1304 Lines (over 800-line limit)
所有端點 fetch helper 集中在一個檔案。P0-2 bundle 分析時未出現在熱點中,未進行拆分。隨著功能增長(auth、LLM Council、RLS headers),此檔案持續增長。 All endpoint fetch helpers concentrated in one file. Did not appear in hotspots during P0-2 bundle analysis, so not split. Continues growing as features expand (auth, LLM Council, RLS headers).
建議: Recommendation: 若出現 bundle 回歸或端點數突破 40+,按 domain 拆分(cases-api.ts、analytics-api.ts、auth-api.ts)。 If a bundle regression appears or endpoint count exceeds 40+, split by domain (cases-api.ts, analytics-api.ts, auth-api.ts).
§ 08

下一步方向 Next Steps

根據近期動態與未閉環事項推斷。非指令,而是「動能指向哪裡」的描述。 Inferred from recent activity and open items. Not directives — a description of "where the momentum points."

安全 · 高優先 Security · High Priority
Refresh Token 撤銷機制 Refresh Token Revocation
refresh_sessions 表 + jti claim,完成 auth sprint 最後的已知限制閉環。 refresh_sessions table + jti claim — closes the last known limitation from the auth sprint.
效能 · 中優先 Performance · Medium Priority
Cron 加入 judge-profile/compare Add judge-profile/compare to Cron
scheduled() 擴展至 10 個端點,覆蓋 JudgeDetail/Compare 頁面,實現 zero-cold-start。 Expand scheduled() to 10 endpoints, covering JudgeDetail/Compare pages for zero-cold-start.
文件 · 低優先 Docs · Low Priority
CLAUDE.md 第三輪事實刷新 CLAUDE.md Third Fact-Refresh
頁數 27→28,per-PoP Cache 說明,proxy.js 行數 2889,cron 端點數 8。 Page count 27→28, per-PoP Cache explanation, proxy.js line count 2889, cron endpoint count 8.
架構 · 長期 Architecture · Long-term
judge_appearances Materialized View judge_appearances Materialized View
消除 LATERAL unnest 根本原因。所有法官分析端點 cache miss 從 3–13s 降至 <0.5s。 Eliminate the LATERAL unnest root cause. All judge analytics cache misses drop from 3–13s to <0.5s.
功能 · 待決定 Feature · TBD
austlii-scraper Worker 整合 austlii-scraper Worker Integration
workers/austlii-scraper/ 已有 Queue + R2 架構。UI 觸發入口或主 Worker 整合尚未規劃。 workers/austlii-scraper/ already has Queue + R2 architecture. UI trigger entry or main Worker integration not yet planned.
可觀測性 · 待決定 Observability · TBD
Cloudflare Logpush → 可觀測性 Cloudflare Logpush → Observability
getSqlAsUser.js 已有結構化日誌(event/kid/tenant_id/query_ms)。接 Logpush → Grafana 可實現 per-tenant 延遲儀表板。 getSqlAsUser.js already emits structured logs (event/kid/tenant_id/query_ms). Wire Logpush → Grafana for per-tenant latency dashboards.