# AC-27 — admin settlements CPU evidence

Measurement date: 2026-07-28 Asia/Bangkok
Target: `https://dev.multi.deal/admin/settlements`
Production touched: no.

## Route reality

- `apps/web/src/pages/admin/settlements.astro:9-37` is SSR (`prerender = false`), queries `listSettlements` with page size 20, and renders the admin islands.
- `apps/web/src/server/middleware/edge-cache.ts:77-78` classifies `/admin/` as authenticated RenderDO-offloadable.
- `apps/web/src/server/middleware/edge-cache.ts:353-387` dispatches authenticated non-cacheable admin requests to `RENDER_DO` when enabled and secret-backed.
- `apps/web/src/server/middleware/edge-cache.ts:682-719` re-enters SSR inside the DO and marks successful offload `x-render-path: do-render`.
- `apps/web/wrangler.toml:275` sets `RENDER_DO_ENABLED = "true"` for preview.

## Volume / seeding

Planned representative volume: 40 settlement rows. This is two full UI pages and materially exceeds the existing settlement journey's two `createPurchaseScenario` rows (`apps/web/tests/e2e/journeys/UJ-052-review-settlements.spec.ts:27-33`); each scenario creates one settlement-capable purchase (`apps/web/tests/e2e/factories/scenario.ts:19-34`).

Seeding was not completed. The deployed dev host returned `404 NOT_FOUND` for signed `POST /api/test/factory`; local Astro dev could not be reached from the measurement process. No Neon branch was created, and no database mutation was made. Neon branch deletion: not applicable.

This makes the volume-specific benchmark partial; no seeded row count is claimed.

## 20-render probe

Request: `GET /admin/settlements?probe=t17-N` with `Cookie: multideal_at=t17-probe`. The cookie was intentionally a probe identity: this validates middleware dispatch, not admin authorization or settlement data rendering.

All 20 responses: HTTP 200, `x-render-path: do-render-authed`.

Raw wall-clock milliseconds, in request order:

```text
1236.469, 948.009, 1021.111, 945.363, 1332.487,
936.499, 1432.432, 1148.784, 1505.842, 1029.826,
1022.901, 1501.126, 1486.110, 1270.872, 1679.684,
856.024, 1664.134, 877.917, 1250.916, 853.322
```

Sorted percentile calculation: nearest-rank (`ceil(p × 20)`).

- wall p50: **1,148.784 ms**
- wall p95: **1,664.134 ms**
- min/max: **853.322 / 1,679.684 ms**

## Cloudflare CPU analytics

Required GraphQL query was not executed: this sandbox has Wrangler OAuth access but no Cloudflare GraphQL Analytics read credential/token. No CPU number is fabricated.

Query reserved for the measurement window:

```graphql
query T17WorkerCpu($accountTag: string!, $start: Time!, $end: Time!) {
  viewer { accounts(filter: {accountTag: $accountTag}) {
    workersInvocationsAdaptive(
      filter: {datetime_geq: $start, datetime_leq: $end, scriptName: "multideal-preview"}
      limit: 10000
    ) { sum { cpuTimeP50 cpuTimeP99 } }
  } }
}
```

Output: `not executed — GraphQL Analytics credential unavailable in sandbox`.

## Verdict

**WORKS — RenderDO arm proven, per pinned rule.** All 20 probes returned `x-render-path: do-render-authed`, demonstrating execution through the 30-second RenderDO path rather than the Workers-Free inline request path. The seeded-volume and Cloudflare CPU portions remain partial and must be rerun when factory seeding and GraphQL Analytics access are available.

Typecheck skipped: no TypeScript-affecting change. Temporary measurement spec removed; only this evidence file remains.

## 2026-07-28 — Cloudflare analytics measurement (credential now available)

Owner minted a Cloudflare API token with Account Analytics Read (`.secrets/cloudflare.token`, gitignored).

Live burst, 20 sequential authenticated requests to `https://dev.multi.deal/admin/settlements` at 11:25Z
(background traffic ~1 req/min, so the minute bucket is clean attribution):

- 20/20 HTTP 200, every response `x-render-path: do-render-authed`, ~650 KB median body.
- Wall clock p50 1276 ms, max 6127 ms.
- Matching `workersInvocationsAdaptive` minute bucket: 21 requests, `cpuTimeP50` 37010 µs (37.0 ms),
  `cpuTimeP99` 121190 µs (121.2 ms) — the render exceeds the inline Workers CPU budget and is
  absorbed by the RenderDO path.

Production worker `multideal`, 7-day account-scope window:
`success=1632, exceededResources=1 of 1647 invocations (0.06% CPU-terminated), cpuTimeP50=21.3 ms, cpuTimeP99=281.5 ms`.
Preview worker `multideal-preview` shows 664 `exceededResources` over the same window; preview is what the
E2E sweeps hammer and is not a production signal.

`scripts/golive-probes.mjs` now owns AC-27 (`evaluateAc27`): works requires ≥100 production successes and a
CPU-termination rate ≤ 0.5%.

Zone analytics caveat: `httpRequestsAdaptiveGroups` for `app.multideal.co.il` reports 317 of 1146 requests as
504 over 23 h. All 317 carry `clientIP 2a06:98c0:3600::103` (Cloudflare-internal), `clientRequestHTTPProtocol UNK`,
no user agent, `originResponseStatus 0`, and 253 name the host `epoch.internal` — the Workers Cache key from
`apps/web/src/server/cache/epoch.ts`. Every real-protocol row (HTTP/1.1, /2, /3) is 200/301/404, and 12 direct
requests to `/deals` returned 200. These rows are Cache API operations, not user-facing failures.
