@coreplane/switchboard 1.260.0 → 1.260.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/bin/switchboard.js +15 -11
  2. package/dist/assets/config/config.example.yaml +1 -0
  3. package/dist/assets/deploy/cloudflare/worker.ts +17 -56
  4. package/dist/assets/deploy/cloudflare/wrangler.template.jsonc +6 -0
  5. package/dist/assets/deploy/cloudflare-memory/backgroundTasks.ts +25 -0
  6. package/dist/assets/deploy/cloudflare-memory/worker.ts +36 -16
  7. package/dist/assets/deploy/cloudflare-resident/worker.ts +39 -18
  8. package/dist/assets/deploy/profile.example.json +2 -1
  9. package/dist/assets/package-lock.json +3 -3
  10. package/dist/assets/package.json +1 -1
  11. package/dist/assets/source.json +3 -3
  12. package/dist/assets/src/agents/registry.ts +2 -2
  13. package/dist/assets/src/core/budgets.ts +19 -6
  14. package/dist/assets/src/core/coordinator/driver.ts +47 -9
  15. package/dist/assets/src/core/pipelineStanding.ts +2 -0
  16. package/dist/assets/src/core/reviewedHead.ts +16 -2
  17. package/dist/assets/src/core/runEvents.ts +9 -1
  18. package/dist/assets/src/core/runLedger/types.ts +3 -3
  19. package/dist/assets/src/core/runRecord.ts +28 -8
  20. package/dist/assets/src/core/ship/coordinator.ts +248 -44
  21. package/dist/assets/src/core/ship/renewal.ts +2 -0
  22. package/dist/assets/src/deploy/liveGate.ts +8 -0
  23. package/dist/assets/src/deploy/profile.ts +8 -0
  24. package/dist/assets/src/deploy/restart.ts +49 -35
  25. package/dist/assets/web/dist/.vite/manifest.json +67 -67
  26. package/dist/assets/web/dist/assets/{DeliveryPage-DvMrWUg7.js → DeliveryPage-62qCSLvk.js} +1 -1
  27. package/dist/assets/web/dist/assets/{HomePage-CcGEJ4w0.js → HomePage-Dk3HRBc3.js} +1 -1
  28. package/dist/assets/web/dist/assets/{PendingTurnRow-BzGVxDYs.js → PendingTurnRow-CeTGOXUy.js} +1 -1
  29. package/dist/assets/web/dist/assets/{PlanePage-Arj9cyd5.js → PlanePage-RD0M6O6W.js} +1 -1
  30. package/dist/assets/web/dist/assets/{ResidentDetailPage-CBPPe4Sj.js → ResidentDetailPage-Dl2KId95.js} +1 -1
  31. package/dist/assets/web/dist/assets/{ResidentsIndexPage-CEpXgGo7.js → ResidentsIndexPage-DBy43I1R.js} +1 -1
  32. package/dist/assets/web/dist/assets/{RunFoldRow-BJq7tnLr.js → RunFoldRow-CwHDL-Mo.js} +1 -1
  33. package/dist/assets/web/dist/assets/{RunRoutePage-DWiny7LN.js → RunRoutePage-Du1HqLZs.js} +3 -3
  34. package/dist/assets/web/dist/assets/{RunsIndexPage-Cxufw5sV.js → RunsIndexPage-BZphy3Hr.js} +1 -1
  35. package/dist/assets/web/dist/assets/{ScheduledPage-SBcuDdVg.js → ScheduledPage-B7jh898t.js} +1 -1
  36. package/dist/assets/web/dist/assets/{SettingsPage-DrD_vWdp.js → SettingsPage-3uLEsiiE.js} +1 -1
  37. package/dist/assets/web/dist/assets/{SilentTurn-Bjl7EPEN.js → SilentTurn-CKxSxnQC.js} +1 -1
  38. package/dist/assets/web/dist/assets/{StatusDot-BqteQTav.js → StatusDot-x7vcK0iE.js} +1 -1
  39. package/dist/assets/web/dist/assets/{Tooltip-KRqJXEPN.js → Tooltip-wVXCWhMp.js} +1 -1
  40. package/dist/assets/web/dist/assets/{UnitRoutePage-Cv28FV4I.js → UnitRoutePage-V5BXK7FH.js} +1 -1
  41. package/dist/assets/web/dist/assets/budgets-KNNkT4PZ.js +1 -0
  42. package/dist/assets/web/dist/assets/{dist-CTASUSno.js → dist-C-XGnvM4.js} +1 -1
  43. package/dist/assets/web/dist/assets/{indexRow-B1YBy_IL.js → indexRow-Dxg7C8s9.js} +1 -1
  44. package/dist/assets/web/dist/assets/{main-eTAe9-hk.js → main-DAL--hyj.js} +2 -2
  45. package/dist/assets/web/dist/assets/sseReplay-BqgOggyQ.js +11 -0
  46. package/dist/cli.js +1405 -436
  47. package/package.json +1 -1
  48. package/dist/assets/web/dist/assets/budgets-DQXVllsm.js +0 -1
  49. package/dist/assets/web/dist/assets/sseReplay-BcsNbn2j.js +0 -11
@@ -18,34 +18,25 @@ import { profileUrls, type DeploymentProfile } from "./profile.js";
18
18
  // (src/deploy/run.ts `runBotRestart`), so both sides are unit-tested here and
19
19
  // nothing in this file imports node:*.
20
20
  //
21
- // Authorization: the bearer must be an entry of the bot's own
22
- // `SWITCHBOARD_INGRESS_TOKENS` map whose `http:<subject>` actor holds
23
- // `deploy:write` in the bot's config (`grants`, authorization.md item 9) — the
24
- // very action the `deploy.restart` command declares, so the Worker route is
25
- // authorized exactly as `/api/deploy.restart` would be if the bot served it.
26
- // Two halves, because the two sides hold different things: the Worker holds the
27
- // token map (it fires scheduled runs with the `cron` entry) and can tell WHO a
28
- // bearer is — `authenticateRestart`, 401/503 without touching the container —
29
- // but the grants live in the container's config, so it asks the bot
30
- // (`POST /admin/restart/authorize`, src/channels/adminRestartAuthorize.ts) whether
31
- // that subject holds the action and relays the answer (`parseRestartAuthorization`).
32
- // The Worker names the subject it authenticated in `RESTART_SUBJECT_HEADER` and
33
- // the bot decides the grant for THAT subject without re-authenticating the
34
- // bearer: the two sides can hold different generations of the token map (a
35
- // `wrangler secret put` reaches the Worker's env at once and the container's
36
- // only after a restart), so re-authenticating in the container would refuse the
37
- // very rotation the restart exists to finish. The header is trustworthy because
38
- // the container is reachable only through the Worker, which strips it from every
39
- // proxied request (`stripRestartSubject`) and sets it only on its own internal
40
- // call. The bot's own `/admin/crash` runs the whole check in one place
41
- // (`authorizeRestart`).
21
+ // Authorization stays entirely on the Worker side: the bearer is an entry of
22
+ // `SWITCHBOARD_INGRESS_TOKENS`, and its subject must equal
23
+ // `SWITCHBOARD_RESTART_DEPLOYER`, rendered from the deployment profile (or set
24
+ // directly as a Worker var). The route never asks the container or its runtime
25
+ // grants: a missing/invalid config is exactly when recovery must still work.
26
+ // The bot's separate `/admin/crash` check still uses `authorizeRestart`, the
27
+ // ordinary runtime-grant path for a route served by the bot itself.
42
28
  //
43
29
  // The route's URL is the installation's: the deployment profile names the
44
30
  // bot's hostname, `planRestart` derives `https://<bot>/admin/restart` from it.
45
31
 
46
32
  /** The operator's env var holding a `SWITCHBOARD_INGRESS_TOKENS` bearer with `deploy:write`. */
47
33
  export const RESTART_TOKEN_ENV = "SWITCHBOARD_DEPLOY_TOKEN";
48
- /** The scope the bearer's identity must carry — the `deploy.restart` command's own. */
34
+ /** Worker var naming the authenticated ingress subject allowed to restart. It
35
+ * is rendered from the deployment profile and never forwarded into the
36
+ * container, so a broken runtime config cannot lock out its own recovery. */
37
+ export const RESTART_DEPLOYER_ENV = "SWITCHBOARD_RESTART_DEPLOYER";
38
+ /** The scope the command declares. The Worker-side restart grant above is the
39
+ * independent recovery authorization for the route. */
49
40
  export const RESTART_SCOPE = "deploy:write";
50
41
 
51
42
  // ---- refusal decision (Worker side; the CLI relies on the 409) -----------------------------
@@ -122,6 +113,25 @@ export function constantTimeEqual(a: string, b: string): boolean {
122
113
  return diff === 0;
123
114
  }
124
115
 
116
+ /** Authorize the already-authenticated ingress subject against the deployment
117
+ * profile's Worker var. This check deliberately has no ConfigStore input: the
118
+ * route exists to recover a missing or invalid runtime config. */
119
+ export function authorizeRestartDeployer(subject: string, configured: string | undefined): RestartAuth {
120
+ if (!configured)
121
+ return {
122
+ ok: false,
123
+ status: 503,
124
+ reason: `restart disabled: ${RESTART_DEPLOYER_ENV} is not set by the deployment profile or Worker env`,
125
+ };
126
+ if (!constantTimeEqual(subject, configured))
127
+ return {
128
+ ok: false,
129
+ status: 403,
130
+ reason: `forbidden: identity "${subject}" is not the restart deployer named by ${RESTART_DEPLOYER_ENV}`,
131
+ };
132
+ return { ok: true, subject };
133
+ }
134
+
125
135
  /** Find the presented bearer among the configured tokens by comparing against
126
136
  * EVERY entry (fixed work; a plain object-key lookup would let a probe learn
127
137
  * which prefixes exist from timing). Returns the matched identity, if any. */
@@ -134,14 +144,14 @@ export function lookupConstantTime<T>(tokens: Record<string, T>, presented: stri
134
144
  export type RestartAuth = { ok: true; subject: string } | { ok: false; status: 401 | 403 | 503; reason: string };
135
145
  export type RestartAuthn = { ok: true; identity: IngressIdentity } | { ok: false; status: 401 | 503; reason: string };
136
146
 
137
- /** The bot route the Worker asks before stopping the container: 200 `{ ok, subject }`
138
- * when the bearer's actor holds `deploy:write`, else `authorizeRestart`'s 401/403/503. */
147
+ /** Compatibility contract for a bot-served authorization probe. The external
148
+ * restart route no longer calls it; keeping the pure shape avoids breaking an
149
+ * older shim while a fleet rolls between versions. */
139
150
  export const RESTART_AUTHORIZE_PATH = "/admin/restart/authorize";
140
- /** The header the Worker sets on its internal authorize call, naming the subject it
141
- * authenticated from its own token map; stripped from every proxied request. */
142
151
  export const RESTART_SUBJECT_HEADER = "x-switchboard-restart-subject";
143
152
 
144
- /** WHETHER, for a subject the Worker already authenticated: the bot's grants alone. */
153
+ /** WHETHER for the bot's own runtime-granted admin routes (`/admin/crash`).
154
+ * The Worker's restart route deliberately does not call this path. */
145
155
  export function authorizeRestartSubject(subject: string, grantsFor: GrantsLookup): RestartAuth {
146
156
  if (subject.trim() === "") return { ok: false, status: 401, reason: "unauthorized: an empty subject" };
147
157
  if (!hasAction(grantsFor(`http:${subject}`).actions, RESTART_SCOPE))
@@ -153,8 +163,7 @@ export function authorizeRestartSubject(subject: string, grantsFor: GrantsLookup
153
163
  return { ok: true, subject };
154
164
  }
155
165
 
156
- /** The request without the subject header — what the Worker forwards to the container for
157
- * every route it does not answer itself, so a caller can never assert a subject. */
166
+ /** Strip the compatibility probe's trusted header from ordinary proxying. */
158
167
  export function stripRestartSubject(request: Request): Request {
159
168
  if (!request.headers.has(RESTART_SUBJECT_HEADER)) return request;
160
169
  const headers = new Headers(request.headers);
@@ -232,11 +241,8 @@ export function authorizeIngressBearer(
232
241
  return { ok: true, subject: identity.subject };
233
242
  }
234
243
 
235
- /** The bot's `POST /admin/restart/authorize` answer, as the Worker reads it: 200
236
- * `{ ok: true, subject }` → allowed; 401 / 403 / 503 `{ ok: false, error }` →
237
- * relayed as they are; anything else (a bot without the route, a non-JSON body,
238
- * an unexpected status) → 503, fail-closed — the Worker never restarts on an
239
- * answer it cannot read. */
244
+ /** Parse the compatibility probe's answer. New restart routes authorize on the
245
+ * Worker without making this request. */
240
246
  export function parseRestartAuthorization(status: number, text: string): RestartAuth {
241
247
  let parsed: unknown;
242
248
  try {
@@ -358,6 +364,9 @@ export interface RestartPlan {
358
364
  target: "bot";
359
365
  adminUrl: string;
360
366
  healthUrl: string;
367
+ /** The source and durable base document this restart must prove equal before
368
+ * it stops anything. `generation` is the document version read at runtime. */
369
+ config: { source: string; document: "base"; stateWorkerUrl?: string };
361
370
  tokenEnv: string;
362
371
  force: boolean;
363
372
  /** Budget for waiting out a 409 (runs in flight) before giving up. */
@@ -373,6 +382,11 @@ export function planRestart(opts: RestartOptions, profile: DeploymentProfile): R
373
382
  target: opts.only,
374
383
  adminUrl: urls.botAdminRestartUrl,
375
384
  healthUrl: `${urls.publicBaseUrl}/healthz`,
385
+ config: {
386
+ source: profile.configSource,
387
+ document: "base",
388
+ ...(urls.stateWorkerUrl !== undefined ? { stateWorkerUrl: urls.stateWorkerUrl } : {}),
389
+ },
376
390
  tokenEnv: RESTART_TOKEN_ENV,
377
391
  force: opts.force,
378
392
  waitMaxMs: opts.waitMaxMinutes * 60_000,
@@ -386,7 +400,7 @@ export function formatRestartPlan(plan: RestartPlan): string {
386
400
  ? `preflight FORCED — in-flight runs are drained (SIGTERM), killed only at the drain deadline`
387
401
  : `refused while runs are in flight or draining (409) — retry every ${plan.pollMs / 1000}s up to ${plan.waitMaxMs / 60_000} min`;
388
402
  return [
389
- `Restart ${plan.target}: POST ${plan.adminUrl} (bearer from $${plan.tokenEnv}, needs ${RESTART_SCOPE}) — ${gate}`,
403
+ `Restart ${plan.target}: prove ${plan.config.source} matches document "${plan.config.document}" on ${plan.config.stateWorkerUrl ?? "the missing state Worker"}, then POST ${plan.adminUrl} (bearer from $${plan.tokenEnv}, needs ${RESTART_SCOPE}) — ${gate}`,
390
404
  ` then wait until ${plan.healthUrl} answers not draining with a later startedAt (up to ${Math.round(plan.liveDeadlineMs / 60_000)} min: drain + cold start)`,
391
405
  ` no image build: the container restarts on the same build with the Worker's CURRENT secrets`,
392
406
  ].join("\n");
@@ -28,7 +28,7 @@
28
28
  "src": "../node_modules/@fontsource-variable/jetbrains-mono/files/jetbrains-mono-vietnamese-wght-normal.woff2"
29
29
  },
30
30
  "../node_modules/@pierre/diffs/dist/index.js": {
31
- "file": "assets/dist-CTASUSno.js",
31
+ "file": "assets/dist-C-XGnvM4.js",
32
32
  "name": "dist",
33
33
  "src": "../node_modules/@pierre/diffs/dist/index.js",
34
34
  "isDynamicEntry": true,
@@ -2570,29 +2570,29 @@
2570
2570
  "_RunsTabs-kHbbxVHx.js"
2571
2571
  ]
2572
2572
  },
2573
- "_PendingTurnRow-BzGVxDYs.js": {
2574
- "file": "assets/PendingTurnRow-BzGVxDYs.js",
2573
+ "_PendingTurnRow-CeTGOXUy.js": {
2574
+ "file": "assets/PendingTurnRow-CeTGOXUy.js",
2575
2575
  "name": "PendingTurnRow",
2576
2576
  "imports": [
2577
2577
  "_AppShell-CSXDCV4x.js",
2578
2578
  "_browser-B6T5hjfR.js",
2579
- "_indexRow-B1YBy_IL.js",
2579
+ "_indexRow-Dxg7C8s9.js",
2580
2580
  "_format-BldUwl_R.js",
2581
- "_sseReplay-BcsNbn2j.js"
2581
+ "_sseReplay-BqgOggyQ.js"
2582
2582
  ]
2583
2583
  },
2584
- "_RunFoldRow-BJq7tnLr.js": {
2585
- "file": "assets/RunFoldRow-BJq7tnLr.js",
2584
+ "_RunFoldRow-CwHDL-Mo.js": {
2585
+ "file": "assets/RunFoldRow-CwHDL-Mo.js",
2586
2586
  "name": "RunFoldRow",
2587
2587
  "imports": [
2588
2588
  "_browser-B6T5hjfR.js",
2589
2589
  "_indexFormat-B-pd4r7Y.js",
2590
- "_indexRow-B1YBy_IL.js",
2590
+ "_indexRow-Dxg7C8s9.js",
2591
2591
  "_format-BldUwl_R.js",
2592
2592
  "_SlackMark-CtiHrNjr.js",
2593
- "_sseReplay-BcsNbn2j.js",
2593
+ "_sseReplay-BqgOggyQ.js",
2594
2594
  "_durationTone-CVpX_yIk.js",
2595
- "_StatusDot-BqteQTav.js"
2595
+ "_StatusDot-x7vcK0iE.js"
2596
2596
  ]
2597
2597
  },
2598
2598
  "_RunsTabs-kHbbxVHx.js": {
@@ -2611,17 +2611,17 @@
2611
2611
  "_browser-B6T5hjfR.js"
2612
2612
  ]
2613
2613
  },
2614
- "_SilentTurn-Bjl7EPEN.js": {
2615
- "file": "assets/SilentTurn-Bjl7EPEN.js",
2614
+ "_SilentTurn-CKxSxnQC.js": {
2615
+ "file": "assets/SilentTurn-CKxSxnQC.js",
2616
2616
  "name": "SilentTurn",
2617
2617
  "imports": [
2618
2618
  "_browser-B6T5hjfR.js",
2619
- "_PendingTurnRow-BzGVxDYs.js",
2619
+ "_PendingTurnRow-CeTGOXUy.js",
2620
2620
  "_eventSource-Bj_dZf6M.js",
2621
2621
  "_indexFormat-B-pd4r7Y.js",
2622
- "_indexRow-B1YBy_IL.js",
2622
+ "_indexRow-Dxg7C8s9.js",
2623
2623
  "_format-BldUwl_R.js",
2624
- "_sseReplay-BcsNbn2j.js",
2624
+ "_sseReplay-BqgOggyQ.js",
2625
2625
  "_durationTone-CVpX_yIk.js"
2626
2626
  ]
2627
2627
  },
@@ -2632,16 +2632,16 @@
2632
2632
  "_browser-B6T5hjfR.js"
2633
2633
  ]
2634
2634
  },
2635
- "_StatusDot-BqteQTav.js": {
2636
- "file": "assets/StatusDot-BqteQTav.js",
2635
+ "_StatusDot-x7vcK0iE.js": {
2636
+ "file": "assets/StatusDot-x7vcK0iE.js",
2637
2637
  "name": "StatusDot",
2638
2638
  "imports": [
2639
2639
  "_browser-B6T5hjfR.js",
2640
- "_Tooltip-KRqJXEPN.js"
2640
+ "_Tooltip-wVXCWhMp.js"
2641
2641
  ]
2642
2642
  },
2643
- "_Tooltip-KRqJXEPN.js": {
2644
- "file": "assets/Tooltip-KRqJXEPN.js",
2643
+ "_Tooltip-wVXCWhMp.js": {
2644
+ "file": "assets/Tooltip-wVXCWhMp.js",
2645
2645
  "name": "Tooltip",
2646
2646
  "imports": [
2647
2647
  "_AppShell-CSXDCV4x.js",
@@ -2661,8 +2661,8 @@
2661
2661
  "file": "assets/browser-B6T5hjfR.js",
2662
2662
  "name": "browser"
2663
2663
  },
2664
- "_budgets-DQXVllsm.js": {
2665
- "file": "assets/budgets-DQXVllsm.js",
2664
+ "_budgets-KNNkT4PZ.js": {
2665
+ "file": "assets/budgets-KNNkT4PZ.js",
2666
2666
  "name": "budgets"
2667
2667
  },
2668
2668
  "_c-L_QOnFVv.js": {
@@ -2748,11 +2748,11 @@
2748
2748
  "file": "assets/indexFormat-B-pd4r7Y.js",
2749
2749
  "name": "indexFormat"
2750
2750
  },
2751
- "_indexRow-B1YBy_IL.js": {
2752
- "file": "assets/indexRow-B1YBy_IL.js",
2751
+ "_indexRow-Dxg7C8s9.js": {
2752
+ "file": "assets/indexRow-Dxg7C8s9.js",
2753
2753
  "name": "indexRow",
2754
2754
  "imports": [
2755
- "_budgets-DQXVllsm.js",
2755
+ "_budgets-KNNkT4PZ.js",
2756
2756
  "_format-BldUwl_R.js"
2757
2757
  ]
2758
2758
  },
@@ -2832,13 +2832,13 @@
2832
2832
  "_AppShell-CSXDCV4x.js"
2833
2833
  ]
2834
2834
  },
2835
- "_sseReplay-BcsNbn2j.js": {
2836
- "file": "assets/sseReplay-BcsNbn2j.js",
2835
+ "_sseReplay-BqgOggyQ.js": {
2836
+ "file": "assets/sseReplay-BqgOggyQ.js",
2837
2837
  "name": "sseReplay",
2838
2838
  "imports": [
2839
2839
  "_browser-B6T5hjfR.js",
2840
2840
  "_wallClock-DmaC1sKA.js",
2841
- "_indexRow-B1YBy_IL.js",
2841
+ "_indexRow-Dxg7C8s9.js",
2842
2842
  "_format-BldUwl_R.js",
2843
2843
  "_durationTone-CVpX_yIk.js"
2844
2844
  ]
@@ -2888,7 +2888,7 @@
2888
2888
  ]
2889
2889
  },
2890
2890
  "src/main.ts": {
2891
- "file": "assets/main-eTAe9-hk.js",
2891
+ "file": "assets/main-DAL--hyj.js",
2892
2892
  "name": "main",
2893
2893
  "src": "src/main.ts",
2894
2894
  "isEntry": true,
@@ -2938,7 +2938,7 @@
2938
2938
  ]
2939
2939
  },
2940
2940
  "src/pages/DeliveryPage.vue": {
2941
- "file": "assets/DeliveryPage-DvMrWUg7.js",
2941
+ "file": "assets/DeliveryPage-62qCSLvk.js",
2942
2942
  "name": "DeliveryPage",
2943
2943
  "src": "src/pages/DeliveryPage.vue",
2944
2944
  "isDynamicEntry": true,
@@ -2947,28 +2947,28 @@
2947
2947
  "_browser-B6T5hjfR.js",
2948
2948
  "_delivery-CTdOqpVK.js",
2949
2949
  "_wallClock-DmaC1sKA.js",
2950
- "_budgets-DQXVllsm.js"
2950
+ "_budgets-KNNkT4PZ.js"
2951
2951
  ]
2952
2952
  },
2953
2953
  "src/pages/HomePage.vue": {
2954
- "file": "assets/HomePage-CcGEJ4w0.js",
2954
+ "file": "assets/HomePage-Dk3HRBc3.js",
2955
2955
  "name": "HomePage",
2956
2956
  "src": "src/pages/HomePage.vue",
2957
2957
  "isDynamicEntry": true,
2958
2958
  "imports": [
2959
2959
  "_AppShell-CSXDCV4x.js",
2960
2960
  "_browser-B6T5hjfR.js",
2961
- "_Tooltip-KRqJXEPN.js",
2962
- "_PendingTurnRow-BzGVxDYs.js",
2961
+ "_Tooltip-wVXCWhMp.js",
2962
+ "_PendingTurnRow-CeTGOXUy.js",
2963
2963
  "src/main.ts",
2964
2964
  "_eventSource-Bj_dZf6M.js",
2965
2965
  "_wallClock-DmaC1sKA.js",
2966
2966
  "_indexFormat-B-pd4r7Y.js",
2967
- "_indexRow-B1YBy_IL.js",
2967
+ "_indexRow-Dxg7C8s9.js",
2968
2968
  "_format-BldUwl_R.js",
2969
- "_SilentTurn-Bjl7EPEN.js",
2969
+ "_SilentTurn-CKxSxnQC.js",
2970
2970
  "_SlackMark-CtiHrNjr.js",
2971
- "_sseReplay-BcsNbn2j.js"
2971
+ "_sseReplay-BqgOggyQ.js"
2972
2972
  ]
2973
2973
  },
2974
2974
  "src/pages/MetricsPage.vue": {
@@ -2983,23 +2983,23 @@
2983
2983
  ]
2984
2984
  },
2985
2985
  "src/pages/PlanePage.vue": {
2986
- "file": "assets/PlanePage-Arj9cyd5.js",
2986
+ "file": "assets/PlanePage-RD0M6O6W.js",
2987
2987
  "name": "PlanePage",
2988
2988
  "src": "src/pages/PlanePage.vue",
2989
2989
  "isDynamicEntry": true,
2990
2990
  "imports": [
2991
2991
  "_AppShell-CSXDCV4x.js",
2992
2992
  "_browser-B6T5hjfR.js",
2993
- "_PendingTurnRow-BzGVxDYs.js",
2993
+ "_PendingTurnRow-CeTGOXUy.js",
2994
2994
  "_wallClock-DmaC1sKA.js",
2995
- "_indexRow-B1YBy_IL.js",
2995
+ "_indexRow-Dxg7C8s9.js",
2996
2996
  "_format-BldUwl_R.js",
2997
- "_SilentTurn-Bjl7EPEN.js",
2998
- "_sseReplay-BcsNbn2j.js"
2997
+ "_SilentTurn-CKxSxnQC.js",
2998
+ "_sseReplay-BqgOggyQ.js"
2999
2999
  ]
3000
3000
  },
3001
3001
  "src/pages/ResidentDetailPage.vue": {
3002
- "file": "assets/ResidentDetailPage-CBPPe4Sj.js",
3002
+ "file": "assets/ResidentDetailPage-Dl2KId95.js",
3003
3003
  "name": "ResidentDetailPage",
3004
3004
  "src": "src/pages/ResidentDetailPage.vue",
3005
3005
  "isDynamicEntry": true,
@@ -3007,46 +3007,46 @@
3007
3007
  "_AppShell-CSXDCV4x.js",
3008
3008
  "_browser-B6T5hjfR.js",
3009
3009
  "src/main.ts",
3010
- "_StatusDot-BqteQTav.js"
3010
+ "_StatusDot-x7vcK0iE.js"
3011
3011
  ]
3012
3012
  },
3013
3013
  "src/pages/ResidentsIndexPage.vue": {
3014
- "file": "assets/ResidentsIndexPage-CEpXgGo7.js",
3014
+ "file": "assets/ResidentsIndexPage-DBy43I1R.js",
3015
3015
  "name": "ResidentsIndexPage",
3016
3016
  "src": "src/pages/ResidentsIndexPage.vue",
3017
3017
  "isDynamicEntry": true,
3018
3018
  "imports": [
3019
3019
  "_AppShell-CSXDCV4x.js",
3020
3020
  "_browser-B6T5hjfR.js",
3021
- "_Tooltip-KRqJXEPN.js",
3021
+ "_Tooltip-wVXCWhMp.js",
3022
3022
  "src/main.ts",
3023
3023
  "_eventSource-Bj_dZf6M.js",
3024
3024
  "_wallClock-DmaC1sKA.js",
3025
3025
  "_indexFormat-B-pd4r7Y.js",
3026
- "_indexRow-B1YBy_IL.js",
3026
+ "_indexRow-Dxg7C8s9.js",
3027
3027
  "_format-BldUwl_R.js",
3028
- "_StatusDot-BqteQTav.js"
3028
+ "_StatusDot-x7vcK0iE.js"
3029
3029
  ]
3030
3030
  },
3031
3031
  "src/pages/RunRoutePage.vue": {
3032
- "file": "assets/RunRoutePage-DWiny7LN.js",
3032
+ "file": "assets/RunRoutePage-Du1HqLZs.js",
3033
3033
  "name": "RunRoutePage",
3034
3034
  "src": "src/pages/RunRoutePage.vue",
3035
3035
  "isDynamicEntry": true,
3036
3036
  "imports": [
3037
3037
  "_AppShell-CSXDCV4x.js",
3038
3038
  "_browser-B6T5hjfR.js",
3039
- "_Tooltip-KRqJXEPN.js",
3040
- "_PendingTurnRow-BzGVxDYs.js",
3039
+ "_Tooltip-wVXCWhMp.js",
3040
+ "_PendingTurnRow-CeTGOXUy.js",
3041
3041
  "src/main.ts",
3042
3042
  "_eventSource-Bj_dZf6M.js",
3043
3043
  "_wallClock-DmaC1sKA.js",
3044
3044
  "_indexFormat-B-pd4r7Y.js",
3045
- "_indexRow-B1YBy_IL.js",
3045
+ "_indexRow-Dxg7C8s9.js",
3046
3046
  "_format-BldUwl_R.js",
3047
- "_RunFoldRow-BJq7tnLr.js",
3047
+ "_RunFoldRow-CwHDL-Mo.js",
3048
3048
  "_SlackMark-CtiHrNjr.js",
3049
- "_sseReplay-BcsNbn2j.js",
3049
+ "_sseReplay-BqgOggyQ.js",
3050
3050
  "_durationTone-CVpX_yIk.js",
3051
3051
  "_NotFoundPage-DHuWyjlq.js"
3052
3052
  ],
@@ -3058,36 +3058,36 @@
3058
3058
  ]
3059
3059
  },
3060
3060
  "src/pages/RunsIndexPage.vue": {
3061
- "file": "assets/RunsIndexPage-Cxufw5sV.js",
3061
+ "file": "assets/RunsIndexPage-BZphy3Hr.js",
3062
3062
  "name": "RunsIndexPage",
3063
3063
  "src": "src/pages/RunsIndexPage.vue",
3064
3064
  "isDynamicEntry": true,
3065
3065
  "imports": [
3066
3066
  "_AppShell-CSXDCV4x.js",
3067
3067
  "_browser-B6T5hjfR.js",
3068
- "_Tooltip-KRqJXEPN.js",
3068
+ "_Tooltip-wVXCWhMp.js",
3069
3069
  "_InputMenu-BdZXnibG.js",
3070
3070
  "src/main.ts",
3071
3071
  "_eventSource-Bj_dZf6M.js",
3072
3072
  "_wallClock-DmaC1sKA.js",
3073
3073
  "_indexFormat-B-pd4r7Y.js",
3074
- "_indexRow-B1YBy_IL.js",
3074
+ "_indexRow-Dxg7C8s9.js",
3075
3075
  "_format-BldUwl_R.js",
3076
3076
  "_SlackMark-CtiHrNjr.js",
3077
3077
  "_durationTone-CVpX_yIk.js",
3078
3078
  "_RunsTabs-kHbbxVHx.js",
3079
- "_StatusDot-BqteQTav.js"
3079
+ "_StatusDot-x7vcK0iE.js"
3080
3080
  ]
3081
3081
  },
3082
3082
  "src/pages/ScheduledPage.vue": {
3083
- "file": "assets/ScheduledPage-SBcuDdVg.js",
3083
+ "file": "assets/ScheduledPage-B7jh898t.js",
3084
3084
  "name": "ScheduledPage",
3085
3085
  "src": "src/pages/ScheduledPage.vue",
3086
3086
  "isDynamicEntry": true,
3087
3087
  "imports": [
3088
3088
  "_AppShell-CSXDCV4x.js",
3089
3089
  "_browser-B6T5hjfR.js",
3090
- "_Tooltip-KRqJXEPN.js",
3090
+ "_Tooltip-wVXCWhMp.js",
3091
3091
  "_wallClock-DmaC1sKA.js",
3092
3092
  "_indexFormat-B-pd4r7Y.js",
3093
3093
  "_format-BldUwl_R.js",
@@ -3095,7 +3095,7 @@
3095
3095
  ]
3096
3096
  },
3097
3097
  "src/pages/SettingsPage.vue": {
3098
- "file": "assets/SettingsPage-DrD_vWdp.js",
3098
+ "file": "assets/SettingsPage-3uLEsiiE.js",
3099
3099
  "name": "SettingsPage",
3100
3100
  "src": "src/pages/SettingsPage.vue",
3101
3101
  "isDynamicEntry": true,
@@ -3103,14 +3103,14 @@
3103
3103
  "_AppShell-CSXDCV4x.js",
3104
3104
  "_browser-B6T5hjfR.js",
3105
3105
  "_SettingSelect-BfID5nF4.js",
3106
- "_Tooltip-KRqJXEPN.js",
3106
+ "_Tooltip-wVXCWhMp.js",
3107
3107
  "_InputMenu-BdZXnibG.js",
3108
3108
  "_settingsApi-Dm6eaQcu.js",
3109
- "_indexRow-B1YBy_IL.js"
3109
+ "_indexRow-Dxg7C8s9.js"
3110
3110
  ]
3111
3111
  },
3112
3112
  "src/pages/UnitRoutePage.vue": {
3113
- "file": "assets/UnitRoutePage-Cv28FV4I.js",
3113
+ "file": "assets/UnitRoutePage-V5BXK7FH.js",
3114
3114
  "name": "UnitRoutePage",
3115
3115
  "src": "src/pages/UnitRoutePage.vue",
3116
3116
  "isDynamicEntry": true,
@@ -3120,11 +3120,11 @@
3120
3120
  "_SettingSelect-BfID5nF4.js",
3121
3121
  "_wallClock-DmaC1sKA.js",
3122
3122
  "_indexFormat-B-pd4r7Y.js",
3123
- "_indexRow-B1YBy_IL.js",
3123
+ "_indexRow-Dxg7C8s9.js",
3124
3124
  "_format-BldUwl_R.js",
3125
- "_RunFoldRow-BJq7tnLr.js",
3125
+ "_RunFoldRow-CwHDL-Mo.js",
3126
3126
  "_SlackMark-CtiHrNjr.js",
3127
- "_sseReplay-BcsNbn2j.js",
3127
+ "_sseReplay-BqgOggyQ.js",
3128
3128
  "_NotFoundPage-DHuWyjlq.js"
3129
3129
  ]
3130
3130
  }
@@ -1 +1 @@
1
- import{t as e}from"./AppShell-CSXDCV4x.js";import{An as t,Cn as n,Cr as r,Li as i,Mn as ee,Pi as a,Sn as o,Tr as te,Xr as s,_r as c,bn as l,ji as u,kn as d,ln as f,o as p,xn as m,yn as h}from"./browser-B6T5hjfR.js";import{a as g,c as ne,d as re,i as ie,l as _,n as ae,o as v,r as y,s as b,t as x,u as oe}from"./delivery-CTdOqpVK.js";import{t as se}from"./wallClock-DmaC1sKA.js";import"./budgets-DQXVllsm.js";function S(e,t){return e.truncated&&e.completeFrom!==void 0&&Date.parse(`${t}T00:00:00Z`)<Date.parse(e.completeFrom)}Object.freeze({reviewers:[],agentLogins:[],agentCoauthors:[]}),Object.freeze({default:60,min:5,max:1440});var C={class:`text-xs text-muted`},w={key:1,class:`text-highlighted`},T={class:`mb-4 flex flex-wrap items-center gap-x-4 gap-y-2`},E={key:0,class:`flex items-center gap-1`,"aria-label":`Repositories`},D={key:0,class:`rounded-md bg-accented px-2.5 py-1 text-xs font-medium text-highlighted`,"aria-current":`page`},O={class:`font-mono text-sm tabular-nums text-muted`},k={class:`mb-5 grid gap-3`},A={key:0,class:`text-sm text-muted`},j={class:`grid grid-cols-[repeat(auto-fit,minmax(190px,1fr))] gap-3`},M={class:`font-mono text-[0.6875rem] font-medium uppercase tracking-widest text-dimmed`},N={class:`font-mono text-2xl font-medium tabular-nums`},P={class:`text-xs text-muted`},F={class:`mb-5 grid gap-3 rounded-lg border border-default bg-elevated px-5 py-4`},I={class:`overflow-x-auto`},L={class:`weeks w-full min-w-[52rem] border-collapse whitespace-nowrap font-mono text-[0.8125rem] tabular-nums`},R=[`title`],z={key:0,class:`ml-1 text-xs`},B={key:0,class:`text-xs text-muted`},V={class:`mb-5 grid gap-3 rounded-lg border border-default bg-elevated px-5 py-4`},ce={key:0,class:`text-sm text-muted`},H={key:1,class:`overflow-x-auto`},U={class:`units w-full min-w-[52rem] border-collapse font-mono text-[0.8125rem] tabular-nums`},W=[`href`],G={key:1,class:`text-dimmed`},le=[`title`],ue=[`href`],de={class:`text-xs text-muted`},fe={class:`rounded bg-accented px-1 py-0.5`},pe={class:`border-t border-default pt-3.5 text-xs text-dimmed`},me={key:0,class:`mb-2 font-mono tabular-nums text-muted`},he={class:`mt-2 grid gap-1.5`},K=`border-b border-muted bg-(--ui-bg-muted) px-2.5 py-1.5 text-xs font-medium text-muted whitespace-nowrap`,q=`border-b border-muted px-2.5 py-1.5`,J=ee({__name:`DeliveryPage`,setup(ee){let J=p(`delivery`),Y=h(()=>J?.report??null),X=h(()=>J?.repos??[]),ge=h(()=>Y.value?oe(Y.value):[]),Z=h(()=>Y.value?ae(Y.value):void 0),Q=e=>Y.value?S(Y.value,e):!1,$=h(()=>Y.value?.weeks.some(e=>Q(e.week))??!1),_e=[1,2,4,8,13],ve=se(void 0,6e4),ye=typeof window>`u`?``:window.location.search;return(ee,p)=>{let h=te(`RouterLink`);return Y.value?(c(),m(e,{key:0,title:`${Y.value.repo} delivery`,nav:`delivery`},{actions:s(()=>[l(`span`,C,[(c(),n(f,null,r(_e,(e,t)=>(c(),n(f,{key:e},[t>0?(c(),n(f,{key:0},[d(` · `)],64)):o(``,!0),e===Y.value.range.weeks?(c(),n(`b`,w,i(e)+`w`,1)):(c(),m(h,{key:2,class:`text-primary hover:underline`,to:`/delivery/${Y.value.repo}?weeks=${e}`},{default:s(()=>[d(i(e)+`w`,1)]),_:2},1032,[`to`]))],64))),64))])]),default:s(()=>[l(`div`,T,[X.value.length>1?(c(),n(`nav`,E,[(c(!0),n(f,null,r(X.value,e=>(c(),n(f,{key:e},[e===Y.value.repo?(c(),n(`span`,D,i(e),1)):(c(),m(h,{key:1,class:`rounded-md px-2.5 py-1 text-xs text-muted no-underline hover:bg-elevated hover:text-highlighted`,to:`/delivery/${e}`},{default:s(()=>[d(i(e),1)]),_:2},1032,[`to`]))],64))),128))])):o(``,!0),l(`p`,O,i(u(v)(Y.value.range.since))+` → `+i(u(v)(Y.value.range.until))+` · `+i(Y.value.range.weeks)+`w · weeks start Monday, UTC `,1)]),l(`section`,k,[Z.value?(c(),n(`p`,A,i(Z.value),1)):o(``,!0),l(`div`,j,[(c(!0),n(f,null,r(ge.value,e=>(c(),n(`div`,{key:e.label,class:`grid gap-0.5 rounded-lg border border-default bg-elevated px-4 py-3.5`},[l(`span`,M,i(e.label),1),l(`span`,N,i(e.value),1),l(`span`,P,i(e.note),1)]))),128))])]),l(`section`,F,[p[0]||=l(`div`,null,[l(`h2`,{class:`text-[0.9375rem] font-medium`},`By week`),l(`p`,{class:`text-sm text-muted`},`One row per Monday-start week · merged pull requests only`)],-1),l(`div`,I,[l(`table`,L,[l(`thead`,null,[l(`tr`,null,[l(`th`,{class:a([K,`text-left`])},`Week`,2),l(`th`,{class:a([K,`text-right`])},`Merged`,2),l(`th`,{class:a([K,`text-right`])},`Agent-authored`,2),l(`th`,{class:a([K,`text-right`])},`Issue → merge`,2),l(`th`,{class:a([K,`text-right`])},`First-pass CI`,2),l(`th`,{class:a([K,`text-right`])},`Rounds / PR`,2),l(`th`,{class:a([K,`text-right`])},`Findings`,2),l(`th`,{class:a([K,`text-right`])},`No human edit`,2),l(`th`,{class:a([K,`text-right`])},`Agent run min`,2)])]),l(`tbody`,null,[(c(!0),n(f,null,r(Y.value.weeks,e=>(c(),n(`tr`,{key:e.week,class:a(Q(e.week)?`text-dimmed`:``)},[l(`td`,{class:a(q),title:`${e.week} → ${e.until}`},[d(i(u(v)(e.week))+` – `+i(u(v)(e.until))+` `,1),Q(e.week)?(c(),n(`span`,z,`(incomplete)`)):o(``,!0)],8,R),l(`td`,{class:a([q,`text-right font-medium`])},i(e.prsMerged),3),l(`td`,{class:a([q,`text-right`])},i(e.agentAuthoredPrs),3),l(`td`,{class:a([q,`text-right`])},i(u(g)(e.leadTimeHours.median)),3),l(`td`,{class:a([q,`text-right`])},i(u(x)(e)),3),l(`td`,{class:a([q,`text-right`])},i(u(ne)(e.reviewRounds.perPr)),3),l(`td`,{class:a([q,`text-right`])},i(u(y)(e)),3),l(`td`,{class:a([q,`text-right`])},i(u(b)(e.findings.noHumanEditShare)),3),l(`td`,{class:a([q,`text-right`])},i(e.agentRuns.minutes.toFixed(0)),3)],2))),128))])])]),$.value&&Y.value.completeFrom?(c(),n(`p`,B,` Incomplete weeks hold the newest pull requests the read reached — it is complete from `+i(u(_)(Y.value.completeFrom))+` — so their numbers are a floor on the merges, not the week's. `,1)):o(``,!0)]),l(`section`,V,[p[3]||=l(`div`,null,[l(`h2`,{class:`text-[0.9375rem] font-medium`},`By unit`),l(`p`,{class:`text-sm text-muted`},` One row per board issue the pull requests link · the issue's opening starts the clock `)],-1),Y.value.units.length===0?(c(),n(`p`,ce,`Nothing merged in range.`)):(c(),n(`div`,H,[l(`table`,U,[l(`thead`,null,[l(`tr`,null,[l(`th`,{class:a([K,`text-left`])},`Issue`,2),l(`th`,{class:a([K,`text-left`])},`Unit`,2),l(`th`,{class:a([K,`text-left`])},`PRs`,2),l(`th`,{class:a([K,`text-right`])},`Issue → merge`,2),l(`th`,{class:a([K,`text-right`])},`First-pass CI`,2),l(`th`,{class:a([K,`text-right`])},`Rounds`,2),l(`th`,{class:a([K,`text-right`])},`Findings`,2),l(`th`,{class:a([K,`text-right`])},`No human edit`,2)])]),l(`tbody`,null,[(c(!0),n(f,null,r(Y.value.units,e=>(c(),n(`tr`,{key:e.issue??`none`},[l(`td`,{class:a([q,`whitespace-nowrap`])},[e.issue===null?(c(),n(`span`,G,`—`)):(c(),n(`a`,{key:0,class:`text-primary hover:underline`,href:`https://github.com/${Y.value.repo}/issues/${e.issue}`},`#`+i(e.issue),9,W))],2),l(`td`,{class:a([q,`max-w-[28rem] truncate font-sans`]),title:e.title},i(e.title),11,le),l(`td`,{class:a([q,`whitespace-nowrap`])},[(c(!0),n(f,null,r(e.prs,(e,t)=>(c(),n(f,{key:e},[t>0?(c(),n(f,{key:0},[d(`, `)],64)):o(``,!0),l(`a`,{class:`text-primary hover:underline`,href:`https://github.com/${Y.value.repo}/pull/${e}`},i(e),9,ue)],64))),128))],2),l(`td`,{class:a([q,`text-right`])},i(u(g)(e.leadTimeHours.median)),3),l(`td`,{class:a([q,`text-right`])},i(u(x)(e)),3),l(`td`,{class:a([q,`text-right`])},i(e.reviewRounds.verdicts),3),l(`td`,{class:a([q,`text-right`])},i(u(y)(e)),3),l(`td`,{class:a([q,`text-right`])},i(u(b)(e.findings.noHumanEditShare)),3)]))),128))])])])),l(`p`,de,[p[1]||=d(` Machine-readable twin: `,-1),l(`code`,fe,`GET /delivery/`+i(Y.value.repo)+`.json`,1),p[2]||=d(` (same gate). `,-1)])]),l(`footer`,pe,[Y.value.snapshotAt?(c(),n(`p`,me,[d(` As of `+i(u(_)(Y.value.snapshotAt))+`, `+i(u(re)(Y.value.snapshotAt,u(ve)))+` · `,1),t(h,{class:`text-primary hover:underline`,to:`/delivery/${Y.value.repo}${u(ie)(u(ye))}`},{default:s(()=>[...p[4]||=[d(`read GitHub now`,-1)]]),_:1},8,[`to`])])):o(``,!0),l(`details`,null,[p[14]||=l(`summary`,{class:`cursor-pointer text-muted`},`How these numbers are computed`,-1),l(`div`,he,[p[12]||=l(`div`,null,[l(`b`,null,`Snapshot.`),d(` GitHub's record of the merged pull requests (each one's timeline and its branch's workflow runs, the board issue its body links) is read on an interval and kept as a snapshot the page, its JSON twin and `),l(`code`,null,`delivery report`),d(` serve; `),l(`code`,null,`?fresh=1`),d(` reads GitHub now. The run history you may see joins on every load. `)],-1),p[13]||=l(`div`,null,[l(`b`,null,`Issue → merge`),d(` runs from the linked board issue's opening (a closing keyword or a `),l(`code`,null,`board item`),d(` reference in the body) to the merge; a pull request that links no issue counts from its own opening. `),l(`b`,null,`First-pass CI`),d(` is green when every pull-request-triggered workflow run (`),l(`code`,null,`pull_request`),d(`, `),l(`code`,null,`pull_request_target`),d(`) on the first head succeeded at its first attempt — a rerun or a red run fails it; a head with no such run is not counted. `),l(`b`,null,`Review rounds`),d(` are the review agent's verdicts; a `),l(`i`,null,`fix round`),d(` is a verdict that followed a push. `),l(`b`,null,`No human edit`),d(` is the share of the agent's findings on pull requests where every push after the first verdict was an agent's: a bot login, or a commit co-authored by an agent. `),l(`b`,null,`Agent run minutes`),d(` are the finished runs of this repository that named a pull request. `)],-1),l(`div`,null,[p[6]||=l(`b`,null,`Identities.`,-1),p[7]||=d(` Verdicts by `,-1),l(`code`,null,i(Y.value.identities.reviewers.join(`, `)||`(none configured)`),1),p[8]||=d(`; agent logins end in `,-1),p[9]||=l(`code`,null,`[bot]`,-1),Y.value.identities.agentLogins.length?(c(),n(f,{key:0},[p[5]||=d(`, plus `,-1),l(`code`,null,i(Y.value.identities.agentLogins.join(`, `)),1)],64)):o(``,!0),p[10]||=d(`; agent co-authors named `,-1),l(`code`,null,i(Y.value.identities.agentCoauthors.join(`, `)||`(none)`),1),p[11]||=d(`. `,-1)])])])])]),_:1},8,[`title`])):o(``,!0)}}});export{J as default};
1
+ import{t as e}from"./AppShell-CSXDCV4x.js";import{An as t,Cn as n,Cr as r,Li as i,Mn as ee,Pi as a,Sn as o,Tr as te,Xr as s,_r as c,bn as l,ji as u,kn as d,ln as f,o as p,xn as m,yn as h}from"./browser-B6T5hjfR.js";import{a as g,c as ne,d as re,i as ie,l as _,n as ae,o as v,r as y,s as b,t as x,u as oe}from"./delivery-CTdOqpVK.js";import{t as se}from"./wallClock-DmaC1sKA.js";import"./budgets-KNNkT4PZ.js";function S(e,t){return e.truncated&&e.completeFrom!==void 0&&Date.parse(`${t}T00:00:00Z`)<Date.parse(e.completeFrom)}Object.freeze({reviewers:[],agentLogins:[],agentCoauthors:[]}),Object.freeze({default:60,min:5,max:1440});var C={class:`text-xs text-muted`},w={key:1,class:`text-highlighted`},T={class:`mb-4 flex flex-wrap items-center gap-x-4 gap-y-2`},E={key:0,class:`flex items-center gap-1`,"aria-label":`Repositories`},D={key:0,class:`rounded-md bg-accented px-2.5 py-1 text-xs font-medium text-highlighted`,"aria-current":`page`},O={class:`font-mono text-sm tabular-nums text-muted`},k={class:`mb-5 grid gap-3`},A={key:0,class:`text-sm text-muted`},j={class:`grid grid-cols-[repeat(auto-fit,minmax(190px,1fr))] gap-3`},M={class:`font-mono text-[0.6875rem] font-medium uppercase tracking-widest text-dimmed`},N={class:`font-mono text-2xl font-medium tabular-nums`},P={class:`text-xs text-muted`},F={class:`mb-5 grid gap-3 rounded-lg border border-default bg-elevated px-5 py-4`},I={class:`overflow-x-auto`},L={class:`weeks w-full min-w-[52rem] border-collapse whitespace-nowrap font-mono text-[0.8125rem] tabular-nums`},R=[`title`],z={key:0,class:`ml-1 text-xs`},B={key:0,class:`text-xs text-muted`},V={class:`mb-5 grid gap-3 rounded-lg border border-default bg-elevated px-5 py-4`},ce={key:0,class:`text-sm text-muted`},H={key:1,class:`overflow-x-auto`},U={class:`units w-full min-w-[52rem] border-collapse font-mono text-[0.8125rem] tabular-nums`},W=[`href`],G={key:1,class:`text-dimmed`},le=[`title`],ue=[`href`],de={class:`text-xs text-muted`},fe={class:`rounded bg-accented px-1 py-0.5`},pe={class:`border-t border-default pt-3.5 text-xs text-dimmed`},me={key:0,class:`mb-2 font-mono tabular-nums text-muted`},he={class:`mt-2 grid gap-1.5`},K=`border-b border-muted bg-(--ui-bg-muted) px-2.5 py-1.5 text-xs font-medium text-muted whitespace-nowrap`,q=`border-b border-muted px-2.5 py-1.5`,J=ee({__name:`DeliveryPage`,setup(ee){let J=p(`delivery`),Y=h(()=>J?.report??null),X=h(()=>J?.repos??[]),ge=h(()=>Y.value?oe(Y.value):[]),Z=h(()=>Y.value?ae(Y.value):void 0),Q=e=>Y.value?S(Y.value,e):!1,$=h(()=>Y.value?.weeks.some(e=>Q(e.week))??!1),_e=[1,2,4,8,13],ve=se(void 0,6e4),ye=typeof window>`u`?``:window.location.search;return(ee,p)=>{let h=te(`RouterLink`);return Y.value?(c(),m(e,{key:0,title:`${Y.value.repo} delivery`,nav:`delivery`},{actions:s(()=>[l(`span`,C,[(c(),n(f,null,r(_e,(e,t)=>(c(),n(f,{key:e},[t>0?(c(),n(f,{key:0},[d(` · `)],64)):o(``,!0),e===Y.value.range.weeks?(c(),n(`b`,w,i(e)+`w`,1)):(c(),m(h,{key:2,class:`text-primary hover:underline`,to:`/delivery/${Y.value.repo}?weeks=${e}`},{default:s(()=>[d(i(e)+`w`,1)]),_:2},1032,[`to`]))],64))),64))])]),default:s(()=>[l(`div`,T,[X.value.length>1?(c(),n(`nav`,E,[(c(!0),n(f,null,r(X.value,e=>(c(),n(f,{key:e},[e===Y.value.repo?(c(),n(`span`,D,i(e),1)):(c(),m(h,{key:1,class:`rounded-md px-2.5 py-1 text-xs text-muted no-underline hover:bg-elevated hover:text-highlighted`,to:`/delivery/${e}`},{default:s(()=>[d(i(e),1)]),_:2},1032,[`to`]))],64))),128))])):o(``,!0),l(`p`,O,i(u(v)(Y.value.range.since))+` → `+i(u(v)(Y.value.range.until))+` · `+i(Y.value.range.weeks)+`w · weeks start Monday, UTC `,1)]),l(`section`,k,[Z.value?(c(),n(`p`,A,i(Z.value),1)):o(``,!0),l(`div`,j,[(c(!0),n(f,null,r(ge.value,e=>(c(),n(`div`,{key:e.label,class:`grid gap-0.5 rounded-lg border border-default bg-elevated px-4 py-3.5`},[l(`span`,M,i(e.label),1),l(`span`,N,i(e.value),1),l(`span`,P,i(e.note),1)]))),128))])]),l(`section`,F,[p[0]||=l(`div`,null,[l(`h2`,{class:`text-[0.9375rem] font-medium`},`By week`),l(`p`,{class:`text-sm text-muted`},`One row per Monday-start week · merged pull requests only`)],-1),l(`div`,I,[l(`table`,L,[l(`thead`,null,[l(`tr`,null,[l(`th`,{class:a([K,`text-left`])},`Week`,2),l(`th`,{class:a([K,`text-right`])},`Merged`,2),l(`th`,{class:a([K,`text-right`])},`Agent-authored`,2),l(`th`,{class:a([K,`text-right`])},`Issue → merge`,2),l(`th`,{class:a([K,`text-right`])},`First-pass CI`,2),l(`th`,{class:a([K,`text-right`])},`Rounds / PR`,2),l(`th`,{class:a([K,`text-right`])},`Findings`,2),l(`th`,{class:a([K,`text-right`])},`No human edit`,2),l(`th`,{class:a([K,`text-right`])},`Agent run min`,2)])]),l(`tbody`,null,[(c(!0),n(f,null,r(Y.value.weeks,e=>(c(),n(`tr`,{key:e.week,class:a(Q(e.week)?`text-dimmed`:``)},[l(`td`,{class:a(q),title:`${e.week} → ${e.until}`},[d(i(u(v)(e.week))+` – `+i(u(v)(e.until))+` `,1),Q(e.week)?(c(),n(`span`,z,`(incomplete)`)):o(``,!0)],8,R),l(`td`,{class:a([q,`text-right font-medium`])},i(e.prsMerged),3),l(`td`,{class:a([q,`text-right`])},i(e.agentAuthoredPrs),3),l(`td`,{class:a([q,`text-right`])},i(u(g)(e.leadTimeHours.median)),3),l(`td`,{class:a([q,`text-right`])},i(u(x)(e)),3),l(`td`,{class:a([q,`text-right`])},i(u(ne)(e.reviewRounds.perPr)),3),l(`td`,{class:a([q,`text-right`])},i(u(y)(e)),3),l(`td`,{class:a([q,`text-right`])},i(u(b)(e.findings.noHumanEditShare)),3),l(`td`,{class:a([q,`text-right`])},i(e.agentRuns.minutes.toFixed(0)),3)],2))),128))])])]),$.value&&Y.value.completeFrom?(c(),n(`p`,B,` Incomplete weeks hold the newest pull requests the read reached — it is complete from `+i(u(_)(Y.value.completeFrom))+` — so their numbers are a floor on the merges, not the week's. `,1)):o(``,!0)]),l(`section`,V,[p[3]||=l(`div`,null,[l(`h2`,{class:`text-[0.9375rem] font-medium`},`By unit`),l(`p`,{class:`text-sm text-muted`},` One row per board issue the pull requests link · the issue's opening starts the clock `)],-1),Y.value.units.length===0?(c(),n(`p`,ce,`Nothing merged in range.`)):(c(),n(`div`,H,[l(`table`,U,[l(`thead`,null,[l(`tr`,null,[l(`th`,{class:a([K,`text-left`])},`Issue`,2),l(`th`,{class:a([K,`text-left`])},`Unit`,2),l(`th`,{class:a([K,`text-left`])},`PRs`,2),l(`th`,{class:a([K,`text-right`])},`Issue → merge`,2),l(`th`,{class:a([K,`text-right`])},`First-pass CI`,2),l(`th`,{class:a([K,`text-right`])},`Rounds`,2),l(`th`,{class:a([K,`text-right`])},`Findings`,2),l(`th`,{class:a([K,`text-right`])},`No human edit`,2)])]),l(`tbody`,null,[(c(!0),n(f,null,r(Y.value.units,e=>(c(),n(`tr`,{key:e.issue??`none`},[l(`td`,{class:a([q,`whitespace-nowrap`])},[e.issue===null?(c(),n(`span`,G,`—`)):(c(),n(`a`,{key:0,class:`text-primary hover:underline`,href:`https://github.com/${Y.value.repo}/issues/${e.issue}`},`#`+i(e.issue),9,W))],2),l(`td`,{class:a([q,`max-w-[28rem] truncate font-sans`]),title:e.title},i(e.title),11,le),l(`td`,{class:a([q,`whitespace-nowrap`])},[(c(!0),n(f,null,r(e.prs,(e,t)=>(c(),n(f,{key:e},[t>0?(c(),n(f,{key:0},[d(`, `)],64)):o(``,!0),l(`a`,{class:`text-primary hover:underline`,href:`https://github.com/${Y.value.repo}/pull/${e}`},i(e),9,ue)],64))),128))],2),l(`td`,{class:a([q,`text-right`])},i(u(g)(e.leadTimeHours.median)),3),l(`td`,{class:a([q,`text-right`])},i(u(x)(e)),3),l(`td`,{class:a([q,`text-right`])},i(e.reviewRounds.verdicts),3),l(`td`,{class:a([q,`text-right`])},i(u(y)(e)),3),l(`td`,{class:a([q,`text-right`])},i(u(b)(e.findings.noHumanEditShare)),3)]))),128))])])])),l(`p`,de,[p[1]||=d(` Machine-readable twin: `,-1),l(`code`,fe,`GET /delivery/`+i(Y.value.repo)+`.json`,1),p[2]||=d(` (same gate). `,-1)])]),l(`footer`,pe,[Y.value.snapshotAt?(c(),n(`p`,me,[d(` As of `+i(u(_)(Y.value.snapshotAt))+`, `+i(u(re)(Y.value.snapshotAt,u(ve)))+` · `,1),t(h,{class:`text-primary hover:underline`,to:`/delivery/${Y.value.repo}${u(ie)(u(ye))}`},{default:s(()=>[...p[4]||=[d(`read GitHub now`,-1)]]),_:1},8,[`to`])])):o(``,!0),l(`details`,null,[p[14]||=l(`summary`,{class:`cursor-pointer text-muted`},`How these numbers are computed`,-1),l(`div`,he,[p[12]||=l(`div`,null,[l(`b`,null,`Snapshot.`),d(` GitHub's record of the merged pull requests (each one's timeline and its branch's workflow runs, the board issue its body links) is read on an interval and kept as a snapshot the page, its JSON twin and `),l(`code`,null,`delivery report`),d(` serve; `),l(`code`,null,`?fresh=1`),d(` reads GitHub now. The run history you may see joins on every load. `)],-1),p[13]||=l(`div`,null,[l(`b`,null,`Issue → merge`),d(` runs from the linked board issue's opening (a closing keyword or a `),l(`code`,null,`board item`),d(` reference in the body) to the merge; a pull request that links no issue counts from its own opening. `),l(`b`,null,`First-pass CI`),d(` is green when every pull-request-triggered workflow run (`),l(`code`,null,`pull_request`),d(`, `),l(`code`,null,`pull_request_target`),d(`) on the first head succeeded at its first attempt — a rerun or a red run fails it; a head with no such run is not counted. `),l(`b`,null,`Review rounds`),d(` are the review agent's verdicts; a `),l(`i`,null,`fix round`),d(` is a verdict that followed a push. `),l(`b`,null,`No human edit`),d(` is the share of the agent's findings on pull requests where every push after the first verdict was an agent's: a bot login, or a commit co-authored by an agent. `),l(`b`,null,`Agent run minutes`),d(` are the finished runs of this repository that named a pull request. `)],-1),l(`div`,null,[p[6]||=l(`b`,null,`Identities.`,-1),p[7]||=d(` Verdicts by `,-1),l(`code`,null,i(Y.value.identities.reviewers.join(`, `)||`(none configured)`),1),p[8]||=d(`; agent logins end in `,-1),p[9]||=l(`code`,null,`[bot]`,-1),Y.value.identities.agentLogins.length?(c(),n(f,{key:0},[p[5]||=d(`, plus `,-1),l(`code`,null,i(Y.value.identities.agentLogins.join(`, `)),1)],64)):o(``,!0),p[10]||=d(`; agent co-authors named `,-1),l(`code`,null,i(Y.value.identities.agentCoauthors.join(`, `)||`(none)`),1),p[11]||=d(`. `,-1)])])])])]),_:1},8,[`title`])):o(``,!0)}}});export{J as default};