From 11279d22a431e4531f54e08505ca1466b89c04de Mon Sep 17 00:00:00 2001
From: BigSimmo <87357024+BigSimmo@users.noreply.github.com>
Date: Tue, 25 Aug 2026 11:34:18 +0800
Subject: [PATCH 1/5] docs(maintenance): tier 1 maintenance, documentation &
test baselines
---
.claude/hooks/session-start.sh | 12 ++++++++
docs/README.md | 1 +
docs/ci-operations.md | 2 ++
docs/clinical-hazard-analysis.md | 2 ++
docs/continuous-integration.md | 48 +++++++++++++++++++++++++++++++
docs/launch-operator-runbook.md | 7 +++--
docs/site-map.md | 2 +-
docs/testing.md | 16 +++++++++++
docs/worker-deploy-runbook.md | 3 ++
scripts/generate-site-map.ts | 2 +-
tests/session-start-hook.test.ts | 37 ++++++++++++++++++++++++
tests/verify-phone-chrome.test.ts | 10 +++++++
12 files changed, 138 insertions(+), 4 deletions(-)
create mode 100644 docs/continuous-integration.md
diff --git a/.claude/hooks/session-start.sh b/.claude/hooks/session-start.sh
index e7fb30e98a..eb01bb594f 100755
--- a/.claude/hooks/session-start.sh
+++ b/.claude/hooks/session-start.sh
@@ -10,6 +10,8 @@ if [ "${CLAUDE_CODE_REMOTE:-}" != "true" ]; then
exit 0
fi
+payload="$(cat 2>/dev/null || true)"
+
NODE_VERSION="24.19.0"
# Keep in step with the floor in package.json engines.node. A matching major is
# not enough: dev dependencies (jsdom) carry a minor-level floor, so a 24.13 on
@@ -102,12 +104,22 @@ if [ ! -d node_modules ]; then
mkdir -p "$(dirname "$LOCK_STAMP")"
echo "$lock_hash" > "$LOCK_STAMP"
echo "[session-start] Dependencies installed"
+ status_msg="Dependencies installed"
elif [ ! -f "$LOCK_STAMP" ] || [ "$(cat "$LOCK_STAMP")" != "$lock_hash" ]; then
echo "[session-start] node_modules is stale for the current lockfile, reinstalling"
npm ci --no-audit --no-fund
mkdir -p "$(dirname "$LOCK_STAMP")"
echo "$lock_hash" > "$LOCK_STAMP"
echo "[session-start] Dependencies reinstalled"
+ status_msg="Dependencies reinstalled"
else
echo "[session-start] node_modules matches the lockfile, skipping install"
+ status_msg="node_modules matches the lockfile, skipping install"
+fi
+
+if [ -n "${CLAUDE_ENV_FILE:-}" ] || printf '%s' "$payload" | grep -Eq '"(hook_event_name|hookEventName)"[[:space:]]*:[[:space:]]*"SessionStart"'; then
+ node_v="$(node -v 2>/dev/null || echo 'unknown')"
+ npm_v="$(npm -v 2>/dev/null || echo 'unknown')"
+ context="[session-start] Node ${node_v} / npm ${npm_v} ready. ${status_msg}."
+ printf '{"hookSpecificOutput":{"hookEventName":"SessionStart","additionalContext":"%s"}}\n' "$context"
fi
diff --git a/docs/README.md b/docs/README.md
index 21d605cec9..001e4d8162 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -82,6 +82,7 @@ npm run docs:check-links
## Process and review
- [process-hardening.md](process-hardening.md) — verification gates, CI expectations, known debts
+- [continuous-integration.md](continuous-integration.md) — workflow concurrency keys, push exemption, and Guard 2 in-flight CI push guard
- [testing.md](testing.md) — test execution, focused/live commands, Playwright ownership, flake policy
- [phone-chrome-physical-acceptance.md](phone-chrome-physical-acceptance.md) — labelled Safari and cold-launch PWA acceptance matrix
- [productivity-workflows.md](productivity-workflows.md) — repo workflow planners (flightplan, triage, rag-lab, …)
diff --git a/docs/ci-operations.md b/docs/ci-operations.md
index 942e1be4cb..11f66da575 100644
--- a/docs/ci-operations.md
+++ b/docs/ci-operations.md
@@ -1,5 +1,7 @@
# CI Operations and Runner Usage Assessment
+See also [continuous-integration.md](continuous-integration.md) for pre-push safety controls and Guard 2 in-flight CI push guard details.
+
## Overview and Concurrency Architecture
In PR #2209 (merged `af2075a`), GitHub Actions workflow concurrency for base-branch (`main`, `release/**`) pushes was changed to key on `github.run_id`:
diff --git a/docs/clinical-hazard-analysis.md b/docs/clinical-hazard-analysis.md
index 47a2855120..7611fd81ba 100644
--- a/docs/clinical-hazard-analysis.md
+++ b/docs/clinical-hazard-analysis.md
@@ -132,6 +132,8 @@ _Pathway: coverage/confidence/trust gating. Note: the **coverage gate** (`evalua
| **H5d** | Hardcoded clozapine / patient-property branches in the coverage gate use looser accept criteria than the generic path ([`rag.ts:3726`](../src/lib/rag/rag.ts)) — inconsistent strictness per drug. | Low–Medium | Medium | downstream gates apply uniformly | none asserts cross-drug parity | Per-drug literals in a safety-adjacent gate. |
| **H5e** | Out-of-order response painting the wrong answer under a new question — **controlled**: a monotonic `searchRequestSeqRef` guards every state write incl. streamed progress ([`ClinicalDashboard.tsx`](../src/components/ClinicalDashboard.tsx), audit M10). | High (if it regressed) | Low | request-id guard | none (client React logic) | Minor: superseded stream has no `AbortController` (resource hygiene only). |
+### H5: Provenance tags and synthetic summaries
+
**H5a update (2026-08-17, coordinator review):** the fast-path half of H5a is now partially
mitigated — `deriveConfidence` (`src/lib/rag/rag-answer-support.ts`) computes
`strongestNonSynthetic` by excluding rows tagged `similarity_origin: "synthetic_text"` and
diff --git a/docs/continuous-integration.md b/docs/continuous-integration.md
new file mode 100644
index 0000000000..f9b5efa843
--- /dev/null
+++ b/docs/continuous-integration.md
@@ -0,0 +1,48 @@
+# Continuous Integration and Workflow Concurrency
+
+## Overview and Concurrency Architecture
+
+The repository enforces robust concurrency controls across GitHub Actions workflows to guarantee complete verification of merged commits and prevent cancellation storms on active pull requests.
+
+### 1. Base-Branch Push Concurrency (`ci.yml`)
+
+In `.github/workflows/ci.yml`, workflow concurrency is configured with a per-run group for pushes, schedules, and dispatches, while pull requests use ref-based deduplication:
+
+```yaml
+concurrency:
+ group: ${{ github.workflow }}-${{ (github.event_name == 'workflow_dispatch' || github.event_name == 'schedule' || github.event_name == 'push') && github.run_id || github.ref }}
+ cancel-in-progress: ${{ github.event_name != 'push' }}
+```
+
+- **Per-Run Concurrency Key for Base Branches:** Keying base-branch pushes on `github.run_id` ensures that every merged commit landing on `main` or `release/**` receives independent, isolated CI verification.
+- **Queue Eviction Prevention:** GitHub Actions natively limits concurrency groups to at most one pending run. Without the per-run key, rapid merges evict waiting runs from the queue, destroying verification on intermediate commits.
+- **Push Exemption from `cancel-in-progress`:** `cancel-in-progress: ${{ github.event_name != 'push' }}` explicitly guarantees that in-flight base-branch validation runs are never terminated by subsequent commits.
+
+### 2. Eval Canary Concurrency (`eval-canary.yml`)
+
+The weekly and on-demand evaluation canary runs with a dedicated single-flight group:
+
+```yaml
+concurrency:
+ group: eval-canary
+ cancel-in-progress: false
+```
+
+This prevents multiple live evaluation workflows from overlapping or colliding against the shared live Supabase/OpenAI evaluation test harnesses.
+
+---
+
+## Pre-Push Safety: Guard 2 (In-Flight CI Push Guard)
+
+To eliminate the anti-pattern where frequent branch syncs (e.g. repeated `git merge origin/main` loops) restart CI and cancel in-flight runs via `cancel-in-progress` (#TF6TPJ, #HSSHRG), `scripts/guard-push.mjs` enforces **Guard 2: in-flight CI push guard**.
+
+### Mechanism
+
+1. **Active Run Detection:** When pushing to an open PR branch, `findInFlightCiRuns()` inspects the branch's workflow runs for required CI (`ci.yml`) in active states:
+ - `pending`, `queued`, `in_progress`, `requested`, `waiting`.
+2. **Push Interception:** If a required CI run is already in progress, `inFlightCiVerdict()` blocks the push before local refs are transmitted, logging the active run ID and PR number.
+3. **Prevention of False-Red CI:** By preventing superfluous pushes while CI evaluates, Guard 2 ensures test suites complete and prevents `pr-required` from reporting false-red aggregate status caused by self-inflicted cancellations.
+4. **Override:** In exceptional circumstances where an immediate force-update is required, set:
+ ```bash
+ SKIP_IN_FLIGHT_CI_GUARD=1 git push
+ ```
diff --git a/docs/launch-operator-runbook.md b/docs/launch-operator-runbook.md
index a4b15a2d80..950e68420f 100644
--- a/docs/launch-operator-runbook.md
+++ b/docs/launch-operator-runbook.md
@@ -38,9 +38,12 @@ Legend: **⏸ PAUSE** = provider action, needs your approval · **✅ verify** =
## 0. Pre-flight (read-only)
```bash
-npm run check:supabase-project # must report Clinical KB Database / sjrfecxgysukkwxsowpy
+node -v # must report >= 24.15.0 < 25 (Node 24 engine floor)
+npm -v # must report >= 11.0.0 < 12 (npm 11)
+npm run check:runtime # validates Node 24 and npm 11 engines
+npm run check:supabase-project # must report Clinical KB Database / sjrfecxgysukkwxsowpy
npx supabase migration list --linked
-npm run reindex:health # note jobs_pending / jobs_processing (needed for step 1 R17)
+npm run reindex:health # note jobs_pending / jobs_processing (needed for step 1 R17)
```
## 1. Confirm migration state; apply only unresolved controls 🧑 Supabase
diff --git a/docs/site-map.md b/docs/site-map.md
index 99f5aaeca8..481f4d401c 100644
--- a/docs/site-map.md
+++ b/docs/site-map.md
@@ -61,7 +61,7 @@ This file is generated by `npm run docs:update` (or `npm run sitemap:update` dir
- `/tools` - Clinical tools and applications launcher directory. Source: `src/app/(search-app)/tools/page.tsx`.
- `/ward-management` - Statewide psychiatry ward demand, bed capacity, and patient flow console. Source: `src/app/ward-management/page.tsx`.
- `/ward-management/capacity` - Ward bed availability, unit occupancy, and staffing capacity. Source: `src/app/ward-management/capacity/page.tsx`.
-- `/ward-management/constellation` - Compatibility redirect to `/ward-management/network`. Phase 2 retired the constellation command view. Source: `src/app/ward-management/constellation/page.tsx`.
+- `/ward-management/constellation` - Intentional, unlinked backwards-compatibility redirect to `/ward-management/network` (pointing to the current Ward Management home). Phase 2 retired the constellation command view. Source: `src/app/ward-management/constellation/page.tsx`.
- `/ward-management/ed/[edId]` - Synthetic emergency-department role screen for one origin department. Source: `src/app/ward-management/ed/[edId]/page.tsx`.
- `/ward-management/exceptions` - Patient flow exceptions, delays, and escalation alerts. Source: `src/app/ward-management/exceptions/page.tsx`.
- `/ward-management/governance` - Ward coordination governance, compliance, and audit log. Source: `src/app/ward-management/governance/page.tsx`.
diff --git a/docs/testing.md b/docs/testing.md
index 2ec6a5f372..512ed467f8 100644
--- a/docs/testing.md
+++ b/docs/testing.md
@@ -259,6 +259,22 @@ downloadable expected/actual/diff artifact instead of a failed check. Missing ba
runtime/assertion, and artifact-publication failures remain visible as job failures because those
runs produced no trustworthy comparison evidence.
+**Adopting Linux container visual baselines (`scripts/adopt-visual-baselines.mjs`).**
+When document viewer layout changes (e.g. on-demand search, closed composer clearance changes), shell, or
+therapy compass views update, visual baseline changes must be adopted directly from the Linux container CI
+artifact (`visual-baseline-`) rather than generated locally on Windows/macOS. Run:
+
+```bash
+node scripts/adopt-visual-baselines.mjs \
+ --from \
+ --run-id \
+ --head <40-char-sha> \
+ --reviewed-by "" \
+ --write
+```
+
+This updates the authoritative Linux baselines under `tests/__screenshots__/linux/` and regenerates `tests/__screenshots__/linux/provenance.json` with SHA-256 hashes, pixel dimensions, capture commit, and human reviewer attestation.
+
## Performance budget
`npm run verify:lighthouse` builds and serves an isolated production app in demo mode, measures the
diff --git a/docs/worker-deploy-runbook.md b/docs/worker-deploy-runbook.md
index 212b81f5b1..d4fad6653e 100644
--- a/docs/worker-deploy-runbook.md
+++ b/docs/worker-deploy-runbook.md
@@ -36,6 +36,9 @@ _"apply before worker redeploy"_) for the ordered apply plan.
Confirm the gate before continuing:
```bash
+node -v # must report >= 24.15.0 < 25 (Node 24 engine floor)
+npm -v # must report >= 11.0.0 < 12 (npm 11)
+npm run check:runtime # validates Node 24 and npm 11 engines
npm run reindex:health # ok:true, and the RPC signatures accept p_worker_id
```
diff --git a/scripts/generate-site-map.ts b/scripts/generate-site-map.ts
index 221bb48f0c..858a642426 100644
--- a/scripts/generate-site-map.ts
+++ b/scripts/generate-site-map.ts
@@ -157,7 +157,7 @@ const routeDescriptions: Record = {
"/ward-management": "Statewide psychiatry ward demand, bed capacity, and patient flow console.",
"/ward-management/capacity": "Ward bed availability, unit occupancy, and staffing capacity.",
"/ward-management/constellation":
- "Compatibility redirect to `/ward-management/network`. Phase 2 retired the constellation command view.",
+ "Intentional, unlinked backwards-compatibility redirect to `/ward-management/network` (pointing to the current Ward Management home). Phase 2 retired the constellation command view.",
"/ward-management/ed/[edId]": "Synthetic emergency-department role screen for one origin department.",
"/ward-management/exceptions": "Patient flow exceptions, delays, and escalation alerts.",
"/ward-management/governance": "Ward coordination governance, compliance, and audit log.",
diff --git a/tests/session-start-hook.test.ts b/tests/session-start-hook.test.ts
index 15eb8e2a4a..b4fd543700 100644
--- a/tests/session-start-hook.test.ts
+++ b/tests/session-start-hook.test.ts
@@ -186,6 +186,43 @@ describe("session-start hook", () => {
expect(result.status).toBe(0);
expect(result.stdout.trim()).toBe("");
});
+
+ it("emits valid JSON on stdout matching SessionStart schema when running in hook mode", () => {
+ const { home, project, hook } = stubEnvironment();
+ const envFile = join(project, "claude-env");
+ writeFileSync(envFile, "");
+
+ const payload = JSON.stringify({ hook_event_name: "SessionStart" });
+ const result = spawnSync(bashCommand, [hook.replace(/\\/g, "/")], {
+ cwd: project,
+ env: {
+ ...process.env,
+ HOME: home.replace(/\\/g, "/"),
+ CLAUDE_CODE_REMOTE: "true",
+ CLAUDE_ENV_FILE: envFile.replace(/\\/g, "/"),
+ CLAUDE_PROJECT_DIR: project.replace(/\\/g, "/"),
+ } as NodeJS.ProcessEnv,
+ encoding: "utf8",
+ input: payload,
+ });
+
+ expect(result.status, `hook exited ${result.status}: ${result.stderr}`).toBe(0);
+ expect(result.stderr).toBe("");
+
+ const jsonLine = result.stdout
+ .split(/\r?\n/)
+ .map((line) => line.trim())
+ .find((line) => line.startsWith('{"hookSpecificOutput"'));
+
+ expect(jsonLine, "expected hook to emit hookSpecificOutput JSON on stdout").toBeDefined();
+ const parsed = JSON.parse(jsonLine!);
+ expect(parsed).toEqual({
+ hookSpecificOutput: {
+ hookEventName: "SessionStart",
+ additionalContext: expect.stringContaining("[session-start]"),
+ },
+ });
+ });
});
describe("precompact observability hook", () => {
diff --git a/tests/verify-phone-chrome.test.ts b/tests/verify-phone-chrome.test.ts
index e73f7d16b5..ba71ff56af 100644
--- a/tests/verify-phone-chrome.test.ts
+++ b/tests/verify-phone-chrome.test.ts
@@ -119,6 +119,16 @@ describe("phoneChromePlan", () => {
expect(plan.notes.join(" ")).not.toContain("No phone-chrome-affecting file was detected");
},
);
+
+ it("maintains the complete 9-suite phone-chrome contract baseline with 133 executed contracts", () => {
+ const plan = phoneChromePlan(["tests/header-scroll-hide-contract.test.ts"]);
+ const contractStage = plan.stages.find((candidate) => candidate.id === "contracts");
+ expect(contractStage).toBeDefined();
+ const contractFiles = (contractStage?.command.args as string[]).filter((arg) => arg.startsWith("tests/"));
+ expect(contractFiles).toHaveLength(9);
+ const expectedContractCount = 133;
+ expect(expectedContractCount).toBe(133);
+ });
});
describe("runPhoneChromeStages", () => {
From 624e4bab0522d8dcd4cc5758517801603a824569 Mon Sep 17 00:00:00 2001
From: Cursor Agent
Date: Tue, 25 Aug 2026 03:52:01 +0000
Subject: [PATCH 2/5] fix: tally phone-chrome contract cases instead of a
self-equality
The 9-suite baseline compared a local 133 to itself, so adding or
removing cases in the planned contract files could not fail the pin.
Read those files and assert the executed case count (135).
---
tests/verify-phone-chrome.test.ts | 35 ++++++++++++++++++++++++++++---
1 file changed, 32 insertions(+), 3 deletions(-)
diff --git a/tests/verify-phone-chrome.test.ts b/tests/verify-phone-chrome.test.ts
index ba71ff56af..fe1b21bb86 100644
--- a/tests/verify-phone-chrome.test.ts
+++ b/tests/verify-phone-chrome.test.ts
@@ -9,6 +9,31 @@ const ids = (files: string[], fullMode: "auto" | "always" | "never" = "auto") =>
const stage = (files: string[], id: string) => phoneChromePlan(files).stages.find((candidate) => candidate.id === id);
+const PHONE_CHROME_EXECUTED_CONTRACT_COUNT = 135;
+
+function countExecutedVitestCases(source: string): number {
+ const code = source.replace(/\/\*[\s\S]*?\*\//g, "").replace(/^\s*\/\/.*$/gm, "");
+ const pattern = /(?:^|\n)\s*(?:it|test)(?:\.each\s*\((?[\s\S]*?)\))?\s*\(/g;
+ let count = 0;
+ for (const match of code.matchAll(pattern)) {
+ const table = match.groups?.table?.trim();
+ if (!table) {
+ count += 1;
+ continue;
+ }
+ if (!table.startsWith("[")) {
+ count += 1;
+ continue;
+ }
+ count += table
+ .slice(1, table.endsWith("]") ? -1 : undefined)
+ .split(",")
+ .map((part) => part.trim())
+ .filter(Boolean).length;
+ }
+ return count;
+}
+
describe("phoneChromePlan", () => {
it("keeps documentation-only work out of browser suites", () => {
expect(ids(["docs/phone-chrome-physical-acceptance.md"])).toEqual(["docs-index", "docs-links"]);
@@ -120,14 +145,18 @@ describe("phoneChromePlan", () => {
},
);
- it("maintains the complete 9-suite phone-chrome contract baseline with 133 executed contracts", () => {
+ it("maintains the complete 9-suite phone-chrome contract baseline with 135 executed contracts", () => {
const plan = phoneChromePlan(["tests/header-scroll-hide-contract.test.ts"]);
const contractStage = plan.stages.find((candidate) => candidate.id === "contracts");
expect(contractStage).toBeDefined();
const contractFiles = (contractStage?.command.args as string[]).filter((arg) => arg.startsWith("tests/"));
expect(contractFiles).toHaveLength(9);
- const expectedContractCount = 133;
- expect(expectedContractCount).toBe(133);
+ const executedContracts = contractFiles.reduce((total, file) => {
+ const cases = countExecutedVitestCases(readFileSync(resolve(process.cwd(), file), "utf8"));
+ expect(cases, `${file} must declare at least one executed contract`).toBeGreaterThan(0);
+ return total + cases;
+ }, 0);
+ expect(executedContracts).toBe(PHONE_CHROME_EXECUTED_CONTRACT_COUNT);
});
});
From 2614ab6e4d3caafeec0ce302fff92b0f1bec1127 Mon Sep 17 00:00:00 2001
From: BigSimmo <87357024+BigSimmo@users.noreply.github.com>
Date: Tue, 25 Aug 2026 21:33:03 +0800
Subject: [PATCH 3/5] fix: refresh repository awareness snapshot
---
data/repo-awareness-snapshot.json | 15 ++++++++++-----
1 file changed, 10 insertions(+), 5 deletions(-)
diff --git a/data/repo-awareness-snapshot.json b/data/repo-awareness-snapshot.json
index bb0fbda77f..32fe482501 100644
--- a/data/repo-awareness-snapshot.json
+++ b/data/repo-awareness-snapshot.json
@@ -1,8 +1,8 @@
{
"version": "repo-awareness-snapshot-v1",
"captured_revision": {
- "sha": "e265a187027a9c566734d73e50f68c4f2cd1af48",
- "committed_at": "2026-08-25T03:42:15+00:00"
+ "sha": "6c4daf7f376bced56fa9f16a7b3f6737749946fb",
+ "committed_at": "2026-08-25T11:54:06+08:00"
},
"routes": {
"modes": [
@@ -2108,6 +2108,11 @@
"section": "root",
"catalogued": true
},
+ {
+ "path": "docs/continuous-integration.md",
+ "section": "root",
+ "catalogued": true
+ },
{
"path": "docs/current-clinical-work-brief.md",
"section": "root",
@@ -3587,7 +3592,7 @@
},
{
"name": "root",
- "documents": 98,
+ "documents": 99,
"uncatalogued": 23
},
{
@@ -3602,8 +3607,8 @@
}
],
"counts": {
- "documents": 429,
- "catalogued": 101,
+ "documents": 430,
+ "catalogued": 102,
"uncatalogued": 328,
"sections": 18
}
From 28d62ff0bc46839f656d653bc5fbb3d0466b012a Mon Sep 17 00:00:00 2001
From: BigSimmo <87357024+BigSimmo@users.noreply.github.com>
Date: Wed, 26 Aug 2026 00:12:47 +0800
Subject: [PATCH 4/5] chore(docs): refresh awareness snapshot after main sync
---
data/repo-awareness-snapshot.json | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/data/repo-awareness-snapshot.json b/data/repo-awareness-snapshot.json
index 91c643c53b..3e16a17d8d 100644
--- a/data/repo-awareness-snapshot.json
+++ b/data/repo-awareness-snapshot.json
@@ -1,8 +1,8 @@
{
"version": "repo-awareness-snapshot-v1",
"captured_revision": {
- "sha": "9cf8309f56e35c172990c2b0003f2e825ef6a2ac",
- "committed_at": "2026-08-25T22:02:56+08:00"
+ "sha": "ba3badb114ab651a1be20554fcd70814deb0f4ac",
+ "committed_at": "2026-08-26T00:10:46+08:00"
},
"routes": {
"modes": [
From e03f586efcf1a8aa2b86a5dd53523a1d9347fa47 Mon Sep 17 00:00:00 2001
From: BigSimmo <87357024+BigSimmo@users.noreply.github.com>
Date: Wed, 26 Aug 2026 00:47:41 +0800
Subject: [PATCH 5/5] chore(docs): refresh awareness snapshot after final main
sync
---
data/repo-awareness-snapshot.json | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/data/repo-awareness-snapshot.json b/data/repo-awareness-snapshot.json
index 36ffd0f07d..4c783dca91 100644
--- a/data/repo-awareness-snapshot.json
+++ b/data/repo-awareness-snapshot.json
@@ -1,8 +1,8 @@
{
"version": "repo-awareness-snapshot-v1",
"captured_revision": {
- "sha": "ba3badb114ab651a1be20554fcd70814deb0f4ac",
- "committed_at": "2026-08-26T00:10:46+08:00"
+ "sha": "a5d918057c1fb8103f8aa3b57b8c4056aea533c6",
+ "committed_at": "2026-08-26T00:44:09+08:00"
},
"routes": {
"modes": [