@kky42/pievo-server 2.0.1 → 2.0.3

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 (83) hide show
  1. package/.output/nitro.json +1 -1
  2. package/.output/public/assets/{LoopView-XXW59HAV.js → LoopView-CCYqXrKq.js} +3 -3
  3. package/.output/public/assets/{Modal-BnCPgN_k.js → Modal-BhRzAXQg.js} +1 -1
  4. package/.output/public/assets/{SignIn-BAoOONQ9.js → SignIn-CV3jqX_8.js} +1 -1
  5. package/.output/public/assets/{actionUi-Dxe9cHAc.js → actionUi-BRtdWVvO.js} +1 -1
  6. package/.output/public/assets/app-B03JVnFn.css +2 -0
  7. package/.output/public/assets/index-BATGA0jG.js +12 -0
  8. package/.output/public/assets/{invite._token-BAqaXvte.js → invite._token-CQgv8lTC.js} +1 -1
  9. package/.output/public/assets/{invite._token-DclJQM6f.js → invite._token-Dp2HL9X5.js} +1 -1
  10. package/.output/public/assets/lifecycleUi-C3k2MlX8.js +1 -0
  11. package/.output/public/assets/loops._loopId-C9vRjZ8W.js +4 -0
  12. package/.output/public/assets/{loops._loopId_.runs._runId-Cz3Z0VE_.js → loops._loopId_.runs._runId-BNN-dLrW.js} +2 -2
  13. package/.output/public/assets/notifyFns-DuBElaqG.js +1 -0
  14. package/.output/public/assets/{preload-helper-ycaoAemE.js → preload-helper-3xcHsDtt.js} +1 -1
  15. package/.output/public/assets/{routes-C7Pw5lGn.js → routes-CmuCVIIS.js} +1 -1
  16. package/.output/public/assets/{routes-DPWJg52d.js → routes-YnrsFY5X.js} +1 -1
  17. package/.output/public/assets/{t._teamId-CCgajlMS.js → t._teamId-Bi-C9s5j.js} +1 -1
  18. package/.output/public/assets/{t._teamId-BW5yIkA-.js → t._teamId-BlE4mCSj.js} +1 -1
  19. package/.output/public/assets/ui-By58RJgB.js +1 -0
  20. package/.output/server/_ssr/{DashboardView-DJlDpXxU.mjs → DashboardView-2eNPqWcy.mjs} +34 -109
  21. package/.output/server/_ssr/{LoopPlaybook-BxKPoWAp.mjs → LoopPlaybook-CnQ8Qgyf.mjs} +8 -8
  22. package/.output/server/_ssr/{LoopView-B0AwFjwS.mjs → LoopView-DM30OPCN.mjs} +13 -13
  23. package/.output/server/_ssr/{Modal-CfWC9R49.mjs → Modal-B7gBr5Pm.mjs} +2 -2
  24. package/.output/server/_ssr/{SignIn-C1duN7oy.mjs → SignIn-CaAi4_mk.mjs} +3 -3
  25. package/.output/server/_ssr/{actionUi-mDYOvJdB.mjs → actionUi-Be7dmMxb.mjs} +2 -2
  26. package/.output/server/_ssr/{adapters-C10CmWK4.mjs → adapters-CHURWoZx.mjs} +8 -9
  27. package/.output/server/_ssr/{artifactFiles-uxLErS_7.mjs → artifactFiles-D6b_RP78.mjs} +3 -3
  28. package/.output/server/_ssr/artifacts-CXMpBzMY.mjs +2 -0
  29. package/.output/server/_ssr/{auth-C6uZpC6v.mjs → auth-B_OxPpFr.mjs} +9 -9
  30. package/.output/server/_ssr/{boot-BpqTI96x.mjs → boot-BNoivm21.mjs} +315 -490
  31. package/.output/server/_ssr/boot-C9VAWt9Z.mjs +2 -0
  32. package/.output/server/_ssr/{bun-sqlite-dialect-BW9W1_Ps-DlRsM3s1.mjs → bun-sqlite-dialect-BW9W1_Ps-B3RHdVjo.mjs} +2 -2
  33. package/.output/server/_ssr/{d1-sqlite-dialect-BLC8LXE6-Ct-4mGlC.mjs → d1-sqlite-dialect-BLC8LXE6-DpVe1foU.mjs} +2 -2
  34. package/.output/server/_ssr/db-C11vQNxP.mjs +2 -0
  35. package/.output/server/_ssr/{dist-lkoxB9C9.mjs → dist-BTx1ljg7.mjs} +2 -2
  36. package/.output/server/_ssr/{factory-D73dkxmb.mjs → factory-TSnFcS96.mjs} +2 -2
  37. package/.output/server/_ssr/{invite._token-C80Kxhqw.mjs → invite._token-CDpO3cfB.mjs} +5 -5
  38. package/.output/server/_ssr/{invite._token-23daBIfe.mjs → invite._token-CvqaKH2R.mjs} +2 -2
  39. package/.output/server/_ssr/{invite._token-D-KuiRD1.mjs → invite._token-DWOPXXLS.mjs} +4 -4
  40. package/.output/server/_ssr/{kysely-adapter-YDb9pnV-.mjs → kysely-adapter-BWjzeCGK.mjs} +1 -1
  41. package/.output/server/_ssr/{lifecycleUi-l3nx9i3T.mjs → lifecycleUi-5jMhxINb.mjs} +1 -3
  42. package/.output/server/_ssr/logger-DE_PU5bE.mjs +2 -0
  43. package/.output/server/_ssr/{loopApi-BXHLHlt_.mjs → loopApi-Cpm8YV3U.mjs} +22 -16
  44. package/.output/server/_ssr/{loops._loopId-BVPj6kiH.mjs → loops._loopId-C-y4Pux_.mjs} +3 -3
  45. package/.output/server/_ssr/{loops._loopId-DVG3dDvp.mjs → loops._loopId-DfrHw2mZ.mjs} +62 -57
  46. package/.output/server/_ssr/{loops._loopId_.runs._runId-DbEF9MQu.mjs → loops._loopId_.runs._runId-BXyyqXwX.mjs} +3 -3
  47. package/.output/server/_ssr/{loops._loopId_.runs._runId-Db6cTcUT.mjs → loops._loopId_.runs._runId-C9d0EC2Q.mjs} +9 -16
  48. package/.output/server/_ssr/{machineFns-B1ZLIrOn.mjs → machineFns-CRbwYxD3.mjs} +5 -3
  49. package/.output/server/_ssr/{node-sqlite-dialect-01BHn7am.mjs → node-sqlite-dialect-B10G6C4W.mjs} +2 -2
  50. package/.output/server/_ssr/{notifyFns-qZ6FYwAL.mjs → notifyFns-CtmSVNmE.mjs} +35 -47
  51. package/.output/server/_ssr/{notifyFns-CM35mqF2.mjs → notifyFns-DvH4C-GH.mjs} +3 -3
  52. package/.output/server/_ssr/{router-B8W3-k_h.mjs → router-B4ipreZ4.mjs} +30 -30
  53. package/.output/server/_ssr/{routes-DjaTMZqZ.mjs → routes-DKox93l0.mjs} +2 -2
  54. package/.output/server/_ssr/{routes-hr1KuV9-.mjs → routes-iHT61OkI.mjs} +5 -5
  55. package/.output/server/_ssr/{routes-BiAkIPos.mjs → routes-l7tUe3lr.mjs} +5 -5
  56. package/.output/server/_ssr/{runDiff-CyN5ZR5F.mjs → runDiff-Bohd5W5R.mjs} +2 -2
  57. package/.output/server/_ssr/ssr.mjs +48 -48
  58. package/.output/server/_ssr/store-Ck7kCAGf.mjs +2 -0
  59. package/.output/server/_ssr/{t._teamId-D_vi_OuG.mjs → t._teamId-B3g0Cs8B.mjs} +2 -2
  60. package/.output/server/_ssr/{t._teamId-B9E7Vgt3.mjs → t._teamId-BPK2mrN3.mjs} +5 -5
  61. package/.output/server/_ssr/{t._teamId-CxuiffZZ.mjs → t._teamId-jO8dUmSQ.mjs} +5 -5
  62. package/.output/server/_ssr/{teamFns-DKpLbIiB.mjs → teamFns-6zjFV7lM.mjs} +3 -3
  63. package/.output/server/_ssr/tokens-oJm6yFyt.mjs +2 -0
  64. package/.output/server/_ssr/{ui-WGITbP6Z.mjs → ui-CI-OeTr7.mjs} +2 -18
  65. package/.output/server/{_tanstack-start-manifest_v-ClkHkr-O.mjs → _tanstack-start-manifest_v-CxHUYCv8.mjs} +26 -26
  66. package/.output/server/drizzle/0014_luxuriant_scrambler.sql +5 -0
  67. package/.output/server/drizzle/meta/_journal.json +7 -0
  68. package/.output/server/index.mjs +103 -103
  69. package/drizzle/0014_luxuriant_scrambler.sql +5 -0
  70. package/drizzle/meta/_journal.json +7 -0
  71. package/package.json +1 -1
  72. package/.output/public/assets/app-Cr02jQtI.css +0 -2
  73. package/.output/public/assets/index-Dg07vo1l.js +0 -12
  74. package/.output/public/assets/lifecycleUi-B8Je9exd.js +0 -1
  75. package/.output/public/assets/loops._loopId-vRXeqe7u.js +0 -4
  76. package/.output/public/assets/notifyFns-BDwtMV1G.js +0 -1
  77. package/.output/public/assets/ui-RFP3R3lU.js +0 -1
  78. package/.output/server/_ssr/artifacts-Dnvoxi-_.mjs +0 -2
  79. package/.output/server/_ssr/boot-C-47Ycr2.mjs +0 -2
  80. package/.output/server/_ssr/db-DB8pQ07M.mjs +0 -2
  81. package/.output/server/_ssr/logger-Dhuet6zm.mjs +0 -2
  82. package/.output/server/_ssr/store-DUMLJS0E.mjs +0 -2
  83. package/.output/server/_ssr/tokens-DuV-D_RB.mjs +0 -2
@@ -0,0 +1,2 @@
1
+ import { H as getArtifactSync, J as getGateway, K as getCliGateway } from "./boot-BNoivm21.mjs";
2
+ export { getArtifactSync, getCliGateway, getGateway };
@@ -1,6 +1,6 @@
1
- import { n as DefaultQueryCompiler, o as CompiledQuery, s as sql$1 } from "./auth-C6uZpC6v.mjs";
1
+ import { n as DefaultQueryCompiler, o as CompiledQuery, s as sql$1 } from "./auth-B_OxPpFr.mjs";
2
2
  import { n as DEFAULT_MIGRATION_TABLE, t as DEFAULT_MIGRATION_LOCK_TABLE } from "./kysely-migration-tables-JkVUjPF_-bPAX9LR_.mjs";
3
- //#region node_modules/.nitro/vite/services/ssr/assets/bun-sqlite-dialect-BW9W1_Ps-DlRsM3s1.js
3
+ //#region node_modules/.nitro/vite/services/ssr/assets/bun-sqlite-dialect-BW9W1_Ps-B3RHdVjo.js
4
4
  var BunSqliteAdapter = class {
5
5
  get supportsCreateIfNotExists() {
6
6
  return true;
@@ -1,6 +1,6 @@
1
- import { a as SqliteQueryCompiler, i as SqliteAdapter } from "./auth-C6uZpC6v.mjs";
1
+ import { a as SqliteQueryCompiler, i as SqliteAdapter } from "./auth-B_OxPpFr.mjs";
2
2
  import { n as DEFAULT_MIGRATION_TABLE, t as DEFAULT_MIGRATION_LOCK_TABLE } from "./kysely-migration-tables-JkVUjPF_-bPAX9LR_.mjs";
3
- //#region node_modules/.nitro/vite/services/ssr/assets/d1-sqlite-dialect-BLC8LXE6-Ct-4mGlC.js
3
+ //#region node_modules/.nitro/vite/services/ssr/assets/d1-sqlite-dialect-BLC8LXE6-DpVe1foU.js
4
4
  var D1SqliteAdapter = class extends SqliteAdapter {};
5
5
  var D1SqliteDriver = class {
6
6
  #config;
@@ -0,0 +1,2 @@
1
+ import { A as db } from "./boot-BNoivm21.mjs";
2
+ export { db };
@@ -1,5 +1,5 @@
1
- import { _ as logger, o as createAdapterFactory } from "./factory-D73dkxmb.mjs";
2
- //#region node_modules/.nitro/vite/services/ssr/assets/dist-lkoxB9C9.js
1
+ import { _ as logger, o as createAdapterFactory } from "./factory-TSnFcS96.mjs";
2
+ //#region node_modules/.nitro/vite/services/ssr/assets/dist-BTx1ljg7.js
3
3
  /**
4
4
  * Case-insensitive in-memory comparison helpers.
5
5
  * Used when evaluating where clauses in the memory adapter.
@@ -1,5 +1,5 @@
1
- import { K as getEnvVar, L as env, a as BetterAuthError } from "./boot-BpqTI96x.mjs";
2
- //#region node_modules/.nitro/vite/services/ssr/assets/factory-D73dkxmb.js
1
+ import { R as env, a as BetterAuthError, q as getEnvVar } from "./boot-BNoivm21.mjs";
2
+ //#region node_modules/.nitro/vite/services/ssr/assets/factory-TSnFcS96.js
3
3
  var __defProp = Object.defineProperty;
4
4
  var __commonJSMin = (cb, mod) => () => (mod || (cb((mod = { exports: {} }).exports, mod), cb = null), mod.exports);
5
5
  var __exportAll = (all, no_symbols) => {
@@ -1,13 +1,13 @@
1
1
  import { o as __toESM } from "../_runtime.mjs";
2
2
  import { g as require_jsx_runtime, h as require_react } from "../_libs/@base-ui/react+[...].mjs";
3
- import { l as btnPrimary } from "./ui-WGITbP6Z.mjs";
3
+ import { l as btnPrimary } from "./ui-CI-OeTr7.mjs";
4
4
  import { d as useNavigate } from "../_libs/@tanstack/react-router+[...].mjs";
5
- import { i as PievoLogo, s as useSession } from "./LoopPlaybook-BxKPoWAp.mjs";
5
+ import { i as PievoLogo, s as useSession } from "./LoopPlaybook-CnQ8Qgyf.mjs";
6
6
  import { t as setActiveTeamCookie } from "./teamCookie-B7hLOIie.mjs";
7
7
  import { c as redeemTeamInvite } from "./teamFns-D4I-0dEB.mjs";
8
- import { t as Route } from "./invite._token-D-KuiRD1.mjs";
9
- import { t as SignIn } from "./SignIn-C1duN7oy.mjs";
10
- //#region node_modules/.nitro/vite/services/ssr/assets/invite._token-C80Kxhqw.js
8
+ import { t as Route } from "./invite._token-DWOPXXLS.mjs";
9
+ import { t as SignIn } from "./SignIn-CaAi4_mk.mjs";
10
+ //#region node_modules/.nitro/vite/services/ssr/assets/invite._token-CDpO3cfB.js
11
11
  var import_react = /* @__PURE__ */ __toESM(require_react());
12
12
  var import_jsx_runtime = require_jsx_runtime();
13
13
  /**
@@ -1,7 +1,7 @@
1
1
  import { g as require_jsx_runtime } from "../_libs/@base-ui/react+[...].mjs";
2
2
  import { f as useRouter } from "../_libs/@tanstack/react-router+[...].mjs";
3
- import { r as LoadErrorCard } from "./actionUi-mDYOvJdB.mjs";
4
- //#region node_modules/.nitro/vite/services/ssr/assets/invite._token-23daBIfe.js
3
+ import { r as LoadErrorCard } from "./actionUi-Be7dmMxb.mjs";
4
+ //#region node_modules/.nitro/vite/services/ssr/assets/invite._token-CvqaKH2R.js
5
5
  var import_jsx_runtime = require_jsx_runtime();
6
6
  /**
7
7
  * Invite-link redeem (`/invite/<token>`, design §4 option B). The recipient opens
@@ -1,9 +1,9 @@
1
1
  import { c as createFileRoute, s as lazyRouteComponent } from "../_libs/@tanstack/react-router+[...].mjs";
2
2
  import { c as getAuthState } from "./loopApi-BfMnXaKT.mjs";
3
- import { a as authClient } from "./LoopPlaybook-BxKPoWAp.mjs";
4
- //#region node_modules/.nitro/vite/services/ssr/assets/invite._token-D-KuiRD1.js
5
- var $$splitErrorComponentImporter = () => import("./invite._token-23daBIfe.mjs");
6
- var $$splitComponentImporter = () => import("./invite._token-C80Kxhqw.mjs");
3
+ import { a as authClient } from "./LoopPlaybook-CnQ8Qgyf.mjs";
4
+ //#region node_modules/.nitro/vite/services/ssr/assets/invite._token-DWOPXXLS.js
5
+ var $$splitErrorComponentImporter = () => import("./invite._token-CvqaKH2R.mjs");
6
+ var $$splitComponentImporter = () => import("./invite._token-CDpO3cfB.mjs");
7
7
  /**
8
8
  * Invite-link redeem (`/invite/<token>`, design §4 option B). The recipient opens
9
9
  * the link the owner shared; if signed out (under the gate) they hit the normal
@@ -1,2 +1,2 @@
1
- import { r as kyselyAdapter, t as createKyselyAdapter } from "./auth-C6uZpC6v.mjs";
1
+ import { r as kyselyAdapter, t as createKyselyAdapter } from "./auth-B_OxPpFr.mjs";
2
2
  export { createKyselyAdapter, kyselyAdapter };
@@ -1,9 +1,8 @@
1
- //#region node_modules/.nitro/vite/services/ssr/assets/lifecycleUi-l3nx9i3T.js
1
+ //#region node_modules/.nitro/vite/services/ssr/assets/lifecycleUi-5jMhxINb.js
2
2
  var DAEMON_UPGRADE_REQUIRED = "Daemon upgrade required to stop a running process. Run `npm install -g @kky42/pievo@latest`, then `pievo daemon restart`.";
3
3
  /** Derive product lifecycle state only from durable server facts. */
4
4
  function deriveLoopLifecycle(job) {
5
5
  if (job.deleteRequestedAt != null) return "deleting";
6
- if (job.completedAt != null) return "completed";
7
6
  const running = job.runs.find((run) => run.running);
8
7
  if (!job.enabled && running?.cancelRequested) return "stopping";
9
8
  if (!job.enabled && (job.running || running)) return "paused-finishing";
@@ -26,7 +25,6 @@ function lifecycleDisplay(detail) {
26
25
  if (running && detail.machine.daemonProtocol !== 2) return DAEMON_UPGRADE_REQUIRED;
27
26
  if (state === "stopping" && running && !detail.machine.online) return `Stopping · waiting for ${detail.machine.name || "machine"}`;
28
27
  switch (state) {
29
- case "completed": return "Completed";
30
28
  case "deleting": return "Deleting";
31
29
  case "stopping": return "Stopping";
32
30
  case "paused-finishing": return detail.summary.pauseCause?.kind === "blocked" ? "Paused — blocked · current run finishing" : detail.summary.pauseCause?.kind === "owner" ? "Paused by owner · current run finishing" : "Paused · current run finishing";
@@ -0,0 +1,2 @@
1
+ import { kt as logger } from "./boot-BNoivm21.mjs";
2
+ export { logger };
@@ -1,9 +1,9 @@
1
- import { $t as updateLoop, Et as listTeamsForUser, F as ensureServer, Ft as pauseLoop, Ht as requestDeleteLoop, Jt as startLoop, Pt as normalizeProviderSetting, Qt as tryDeleteLoop, Ut as requestRunCancel, W as getChannel, X as getLoop, Yt as stopLoop, Z as getMachine, at as hasRunningRun, b as coerceUi, et as getRun, g as canEvolve, jt as machinePresence, nt as getTeam, rt as getTeamMember, v as coerceCodingAgent, wt as listRunsBefore, y as coerceStateSchema, yt as listLoops, z as forceDeleteLoop } from "./boot-BpqTI96x.mjs";
1
+ import { B as forceDeleteLoop, Et as listRunsBefore, G as getChannel, Gt as requestRunCancel, I as ensureServer, It as normalizeProviderSetting, Lt as pauseLoop, Nt as machinePresence, Ot as listTeamsForUser, Q as getMachine, Wt as requestDeleteLoop, Xt as startLoop, Z as getLoop, Zt as stopLoop, _ as canEvolve, b as coerceUi, en as tryDeleteLoop, in as validateSchema, it as getTeamMember, ot as hasRunningRun, rt as getTeam, tn as updateLoop, tt as getRun, xt as listLoops, y as coerceCodingAgent } from "./boot-BNoivm21.mjs";
2
2
  import { n as createServerFn } from "./ssr.mjs";
3
- import { i as toRunSummary, n as toJobDetail, r as toJobSummary } from "./adapters-C10CmWK4.mjs";
4
- import { canAccessLoop, requestScope } from "./auth-C6uZpC6v.mjs";
3
+ import { i as toRunSummary, n as toJobDetail, r as toJobSummary } from "./adapters-CHURWoZx.mjs";
4
+ import { canAccessLoop, requestScope } from "./auth-B_OxPpFr.mjs";
5
5
  import { t as createServerRpc } from "./createServerRpc-BD8Xn_0_.mjs";
6
- //#region node_modules/.nitro/vite/services/ssr/assets/loopApi-BXHLHlt_.js
6
+ //#region node_modules/.nitro/vite/services/ssr/assets/loopApi-Cpm8YV3U.js
7
7
  var meta_default$6 = {
8
8
  name: "dependency-triage",
9
9
  label: "Dependency Triage",
@@ -25,8 +25,8 @@ var meta_default$4 = {
25
25
  var meta_default$3 = {
26
26
  name: "follow-up-tracker",
27
27
  label: "Follow-up Tracker",
28
- desc: "A closed loop that watches what happens after something you just shipped and finishes itself once the outcome is confirmed. Works best pasted right after finishing the task you want tracked.",
29
- description: "Look at what was just built or shipped in this session and set up a loop that tracks its aftermath. Before creating anything, verify a concrete way to observe the outcome exists on this machine - logs, metrics or MCP tools, a URL to check, or gh - and run it once as a smoke test; if nothing can actually be observed, say so and do not create a blind loop. Define a concrete finish condition and create the loop closed, with that condition as its goal. Propose a cadence that fits the thing being tracked (a few runs per day is a common default) and confirm it with me. On each run, observe through the verified path, report what you find (and a metric when one is natural), and finish the loop only when the condition is genuinely met; if things regress, report that plainly. Set up a modest dashboard at creation: an embed of the latest report, plus a metric chart when a metric was defined."
28
+ desc: "A continuous loop that watches what happens after something you just shipped and surfaces regressions against a standing objective. Works best pasted right after finishing the task you want tracked.",
29
+ description: "Look at what was just built or shipped in this session and set up a loop that tracks its aftermath. Before creating anything, verify a concrete way to observe the outcome exists on this machine - logs, metrics or MCP tools, a URL to check, or gh - and run it once as a smoke test; if nothing can actually be observed, say so and do not create a blind loop. Define a concrete standing objective. Propose a cadence that fits the thing being tracked (a few runs per day is a common default) and confirm it with me. On each run, observe through the verified path, report what you find (and every declared metric), and keep watching after the target is met so regressions remain visible; the owner decides when to pause or delete the loop. Set up a modest dashboard at creation: an embed of the latest report, plus a metric chart when a metric was defined."
30
30
  };
31
31
  var meta_default$2 = {
32
32
  name: "housekeeper",
@@ -49,7 +49,7 @@ var meta_default = {
49
49
  var thumb_default$6 = "<svg viewBox=\"0 0 144 80\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\">\n <!-- Mock of the loop's dashboard: the merged / deferred / blocked triage\n kanban over the open-dependency-PR count easing down. Colors ride the\n app's CSS variables. -->\n <rect x=\"10\" y=\"8\" width=\"36\" height=\"3\" rx=\"1.5\" fill=\"var(--color-rubik-green, #009b48)\"/>\n <rect x=\"10.5\" y=\"16.5\" width=\"35\" height=\"15\" rx=\"4\" fill=\"var(--color-surface, #fff)\" stroke=\"var(--color-hairline, #e3e5ea)\"/>\n <rect x=\"15\" y=\"21\" width=\"20\" height=\"2.5\" rx=\"1.25\" fill=\"var(--color-wire, #cfd3db)\"/>\n <path d=\"M37 23l2 2 3.5-4\" stroke=\"var(--color-rubik-green, #009b48)\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\n <rect x=\"10.5\" y=\"36.5\" width=\"35\" height=\"15\" rx=\"4\" fill=\"var(--color-surface, #fff)\" stroke=\"var(--color-hairline, #e3e5ea)\"/>\n <rect x=\"15\" y=\"41\" width=\"24\" height=\"2.5\" rx=\"1.25\" fill=\"var(--color-wire, #cfd3db)\"/>\n <rect x=\"15\" y=\"46\" width=\"14\" height=\"2.5\" rx=\"1.25\" fill=\"var(--color-hairline, #e3e5ea)\"/>\n <rect x=\"54\" y=\"8\" width=\"36\" height=\"3\" rx=\"1.5\" fill=\"var(--color-rubik-yellow, #ffd500)\"/>\n <rect x=\"54.5\" y=\"16.5\" width=\"35\" height=\"15\" rx=\"4\" fill=\"var(--color-surface, #fff)\" stroke=\"var(--color-hairline, #e3e5ea)\"/>\n <rect x=\"59\" y=\"21\" width=\"22\" height=\"2.5\" rx=\"1.25\" fill=\"var(--color-wire, #cfd3db)\"/>\n <rect x=\"59\" y=\"26\" width=\"12\" height=\"2.5\" rx=\"1.25\" fill=\"var(--color-hairline, #e3e5ea)\"/>\n <rect x=\"98\" y=\"8\" width=\"36\" height=\"3\" rx=\"1.5\" fill=\"var(--color-rubik-red, #b71234)\"/>\n <rect x=\"98.5\" y=\"16.5\" width=\"35\" height=\"15\" rx=\"4\" fill=\"var(--color-surface, #fff)\" stroke=\"var(--color-hairline, #e3e5ea)\"/>\n <rect x=\"103\" y=\"21\" width=\"18\" height=\"2.5\" rx=\"1.25\" fill=\"var(--color-wire, #cfd3db)\"/>\n <circle cx=\"127\" cy=\"24\" r=\"2.5\" stroke=\"var(--color-rubik-red, #b71234)\" stroke-width=\"1.4\"/>\n <path d=\"M125.3 22.3l3.4 3.4\" stroke=\"var(--color-rubik-red, #b71234)\" stroke-width=\"1.4\" stroke-linecap=\"round\"/>\n <rect x=\"54\" y=\"42\" width=\"24\" height=\"7\" rx=\"3.5\" fill=\"var(--color-interactive-soft, rgba(0,70,173,0.08))\"/>\n <path d=\"M60 47.5v-4m0 0l-1.7 1.7M60 43.5l1.7 1.7\" stroke=\"var(--color-rubik-blue, #0046ad)\" stroke-width=\"1.3\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\n <rect x=\"65\" y=\"44.5\" width=\"10\" height=\"2.5\" rx=\"1.25\" fill=\"var(--color-rubik-blue, #0046ad)\" opacity=\"0.5\"/>\n <path d=\"M12 74h122\" stroke=\"var(--color-hairline, #e3e5ea)\" stroke-width=\"1.2\"/>\n <path d=\"M12 60 36 63 60 61 84 67 108 66 132 70\" stroke=\"var(--color-rubik-blue, #0046ad)\" stroke-width=\"1.6\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\n <rect x=\"12\" y=\"55\" width=\"20\" height=\"2.5\" rx=\"1.25\" fill=\"var(--color-hairline, #e3e5ea)\"/>\n</svg>\n";
50
50
  var thumb_default$5 = "<svg viewBox=\"0 0 144 80\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\">\n <!-- Mock of the loop's dashboard: a doc page with stale lines caught and\n corrected, beside the drift count trending to zero. Colors ride the\n app's CSS variables. -->\n <rect x=\"10.5\" y=\"8.5\" width=\"61\" height=\"63\" rx=\"5\" fill=\"var(--color-surface, #fff)\" stroke=\"var(--color-hairline, #e3e5ea)\"/>\n <rect x=\"17\" y=\"15\" width=\"30\" height=\"3\" rx=\"1.5\" fill=\"var(--color-wire, #cfd3db)\"/>\n <g fill=\"var(--color-hairline, #e3e5ea)\">\n <rect x=\"17\" y=\"25\" width=\"48\" height=\"2.5\" rx=\"1.25\"/>\n <rect x=\"17\" y=\"31\" width=\"40\" height=\"2.5\" rx=\"1.25\"/>\n <rect x=\"17\" y=\"49\" width=\"48\" height=\"2.5\" rx=\"1.25\"/>\n <rect x=\"17\" y=\"61\" width=\"28\" height=\"2.5\" rx=\"1.25\"/>\n </g>\n <rect x=\"17\" y=\"37\" width=\"44\" height=\"2.5\" rx=\"1.25\" fill=\"var(--color-rubik-orange, #ff5800)\" opacity=\"0.45\"/>\n <path d=\"M15 38.25h48\" stroke=\"var(--color-rubik-orange, #ff5800)\" stroke-width=\"1.2\" stroke-linecap=\"round\"/>\n <rect x=\"17\" y=\"43\" width=\"38\" height=\"2.5\" rx=\"1.25\" fill=\"var(--color-wire, #cfd3db)\"/>\n <path d=\"M58 43.5l2 2 3.5-4\" stroke=\"var(--color-rubik-green, #009b48)\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\n <rect x=\"17\" y=\"55\" width=\"34\" height=\"2.5\" rx=\"1.25\" fill=\"var(--color-rubik-orange, #ff5800)\" opacity=\"0.45\"/>\n <path d=\"M15 56.25h40\" stroke=\"var(--color-rubik-orange, #ff5800)\" stroke-width=\"1.2\" stroke-linecap=\"round\"/>\n <rect x=\"80\" y=\"12\" width=\"24\" height=\"3\" rx=\"1.5\" fill=\"var(--color-rubik-orange, #ff5800)\"/>\n <path d=\"M80 66h54\" stroke=\"var(--color-hairline, #e3e5ea)\" stroke-width=\"1.2\"/>\n <path d=\"M81 26 92 33 103 31 114 45 125 56 133 64\" stroke=\"var(--color-rubik-orange, #ff5800)\" stroke-width=\"1.6\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\n <circle cx=\"133\" cy=\"64\" r=\"3\" fill=\"var(--color-rubik-green, #009b48)\"/>\n</svg>\n";
51
51
  var thumb_default$4 = "<svg viewBox=\"0 0 144 80\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\">\n <!-- Mock of the loop's mechanics: a production log pane with an incident\n traced to a verified fix PR, and the actionable-error count falling.\n Colors ride the app's CSS variables. -->\n <rect x=\"10.5\" y=\"8.5\" width=\"62\" height=\"63\" rx=\"5\" fill=\"var(--color-surface, #fff)\" stroke=\"var(--color-hairline, #e3e5ea)\"/>\n <rect x=\"17\" y=\"15\" width=\"26\" height=\"3\" rx=\"1.5\" fill=\"var(--color-rubik-red, #b71234)\"/>\n <g fill=\"var(--color-hairline, #e3e5ea)\">\n <rect x=\"24\" y=\"26\" width=\"40\" height=\"2.5\" rx=\"1.25\"/>\n <rect x=\"24\" y=\"50\" width=\"42\" height=\"2.5\" rx=\"1.25\"/>\n <rect x=\"24\" y=\"62\" width=\"30\" height=\"2.5\" rx=\"1.25\"/>\n </g>\n <circle cx=\"19.5\" cy=\"27.25\" r=\"1.5\" fill=\"var(--color-wire, #cfd3db)\"/>\n <rect x=\"15\" y=\"34.5\" width=\"53\" height=\"9\" rx=\"3\" fill=\"var(--color-rubik-red, #b71234)\" opacity=\"0.08\"/>\n <circle cx=\"19.5\" cy=\"39\" r=\"2\" fill=\"var(--color-rubik-red, #b71234)\"/>\n <rect x=\"24\" y=\"37.75\" width=\"38\" height=\"2.5\" rx=\"1.25\" fill=\"var(--color-wire, #cfd3db)\"/>\n <circle cx=\"19.5\" cy=\"51.25\" r=\"1.5\" fill=\"var(--color-wire, #cfd3db)\"/>\n <circle cx=\"19.5\" cy=\"63.25\" r=\"1.5\" fill=\"var(--color-wire, #cfd3db)\"/>\n <path d=\"M72 39h16\" stroke=\"var(--color-wire, #cfd3db)\" stroke-width=\"1.2\" stroke-dasharray=\"3 3\"/>\n <rect x=\"90.5\" y=\"28.5\" width=\"43\" height=\"21\" rx=\"4\" fill=\"var(--color-surface, #fff)\" stroke=\"var(--color-hairline, #e3e5ea)\"/>\n <rect x=\"96\" y=\"34\" width=\"22\" height=\"2.5\" rx=\"1.25\" fill=\"var(--color-wire, #cfd3db)\"/>\n <rect x=\"96\" y=\"40\" width=\"14\" height=\"2.5\" rx=\"1.25\" fill=\"var(--color-hairline, #e3e5ea)\"/>\n <circle cx=\"125\" cy=\"39\" r=\"5.5\" fill=\"var(--color-success-soft, rgba(0,155,72,0.1))\"/>\n <path d=\"M122.5 39l1.8 1.8 3.2-3.6\" stroke=\"var(--color-rubik-green, #009b48)\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\n <rect x=\"90\" y=\"12\" width=\"20\" height=\"3\" rx=\"1.5\" fill=\"var(--color-hairline, #e3e5ea)\"/>\n <path d=\"M90 68h44\" stroke=\"var(--color-hairline, #e3e5ea)\" stroke-width=\"1.2\"/>\n <path d=\"M91 56 100 59 109 57 118 63 127 65 133 66.5\" stroke=\"var(--color-rubik-red, #b71234)\" stroke-width=\"1.6\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\n <circle cx=\"133\" cy=\"66.5\" r=\"2.5\" fill=\"var(--color-rubik-green, #009b48)\"/>\n</svg>\n";
52
- var thumb_default$3 = "<svg viewBox=\"0 0 144 80\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\">\n <!-- Mock of a closed loop driving toward its goal: a goal chip, observed\n checkpoints ticking off, and the finish. Colors ride the app's CSS variables. -->\n <rect x=\"10\" y=\"8\" width=\"62\" height=\"11\" rx=\"5.5\" fill=\"var(--color-success-soft, rgba(0,155,72,0.1))\"/>\n <circle cx=\"18\" cy=\"13.5\" r=\"2\" fill=\"var(--color-rubik-green, #009b48)\"/>\n <rect x=\"24\" y=\"12.5\" width=\"40\" height=\"2.5\" rx=\"1.25\" fill=\"var(--color-rubik-green, #009b48)\" opacity=\"0.55\"/>\n <g stroke=\"var(--color-rubik-green, #009b48)\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <circle cx=\"17\" cy=\"31\" r=\"4.5\" fill=\"none\"/>\n <path d=\"M15 31l1.6 1.6 2.8-3\"/>\n <circle cx=\"17\" cy=\"46\" r=\"4.5\" fill=\"none\"/>\n <path d=\"M15 46l1.6 1.6 2.8-3\"/>\n </g>\n <circle cx=\"17\" cy=\"61\" r=\"4.5\" stroke=\"var(--color-wire, #cfd3db)\" stroke-width=\"1.5\" fill=\"none\"/>\n <g fill=\"var(--color-wire, #cfd3db)\">\n <rect x=\"28\" y=\"29.5\" width=\"52\" height=\"2.5\" rx=\"1.25\"/>\n <rect x=\"28\" y=\"44.5\" width=\"44\" height=\"2.5\" rx=\"1.25\"/>\n </g>\n <g fill=\"var(--color-hairline, #e3e5ea)\">\n <rect x=\"28\" y=\"35\" width=\"34\" height=\"2.5\" rx=\"1.25\"/>\n <rect x=\"28\" y=\"50\" width=\"28\" height=\"2.5\" rx=\"1.25\"/>\n <rect x=\"28\" y=\"59.5\" width=\"48\" height=\"2.5\" rx=\"1.25\"/>\n <rect x=\"28\" y=\"65\" width=\"20\" height=\"2.5\" rx=\"1.25\"/>\n </g>\n <path d=\"M96 40h14\" stroke=\"var(--color-wire, #cfd3db)\" stroke-width=\"1.2\" stroke-dasharray=\"3 3\"/>\n <circle cx=\"121\" cy=\"40\" r=\"9\" fill=\"var(--color-success-soft, rgba(0,155,72,0.1))\"/>\n <path d=\"M116.5 40l3 3 5.5-6\" stroke=\"var(--color-rubik-green, #009b48)\" stroke-width=\"1.8\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\n</svg>\n";
52
+ var thumb_default$3 = "<svg viewBox=\"0 0 144 80\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\">\n <!-- Mock of an ongoing loop tracking a standing objective through observed\n checkpoints. Colors ride the app's CSS variables. -->\n <rect x=\"10\" y=\"8\" width=\"62\" height=\"11\" rx=\"5.5\" fill=\"var(--color-success-soft, rgba(0,155,72,0.1))\"/>\n <circle cx=\"18\" cy=\"13.5\" r=\"2\" fill=\"var(--color-rubik-green, #009b48)\"/>\n <rect x=\"24\" y=\"12.5\" width=\"40\" height=\"2.5\" rx=\"1.25\" fill=\"var(--color-rubik-green, #009b48)\" opacity=\"0.55\"/>\n <g stroke=\"var(--color-rubik-green, #009b48)\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <circle cx=\"17\" cy=\"31\" r=\"4.5\" fill=\"none\"/>\n <path d=\"M15 31l1.6 1.6 2.8-3\"/>\n <circle cx=\"17\" cy=\"46\" r=\"4.5\" fill=\"none\"/>\n <path d=\"M15 46l1.6 1.6 2.8-3\"/>\n </g>\n <circle cx=\"17\" cy=\"61\" r=\"4.5\" stroke=\"var(--color-wire, #cfd3db)\" stroke-width=\"1.5\" fill=\"none\"/>\n <g fill=\"var(--color-wire, #cfd3db)\">\n <rect x=\"28\" y=\"29.5\" width=\"52\" height=\"2.5\" rx=\"1.25\"/>\n <rect x=\"28\" y=\"44.5\" width=\"44\" height=\"2.5\" rx=\"1.25\"/>\n </g>\n <g fill=\"var(--color-hairline, #e3e5ea)\">\n <rect x=\"28\" y=\"35\" width=\"34\" height=\"2.5\" rx=\"1.25\"/>\n <rect x=\"28\" y=\"50\" width=\"28\" height=\"2.5\" rx=\"1.25\"/>\n <rect x=\"28\" y=\"59.5\" width=\"48\" height=\"2.5\" rx=\"1.25\"/>\n <rect x=\"28\" y=\"65\" width=\"20\" height=\"2.5\" rx=\"1.25\"/>\n </g>\n <path d=\"M96 40h14\" stroke=\"var(--color-wire, #cfd3db)\" stroke-width=\"1.2\" stroke-dasharray=\"3 3\"/>\n <circle cx=\"121\" cy=\"40\" r=\"9\" fill=\"var(--color-success-soft, rgba(0,155,72,0.1))\"/>\n <path d=\"M116.5 40l3 3 5.5-6\" stroke=\"var(--color-rubik-green, #009b48)\" stroke-width=\"1.8\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\n</svg>\n";
53
53
  var thumb_default$2 = "<svg viewBox=\"0 0 144 80\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\">\n <!-- Mock of the loop's mechanics: one proven deletion kept only because the\n checks stay green, a deferred candidate parked, and cleanups landing\n over time. Colors ride the app's CSS variables. -->\n <rect x=\"10.5\" y=\"8.5\" width=\"80\" height=\"63\" rx=\"5\" fill=\"var(--color-surface, #fff)\" stroke=\"var(--color-hairline, #e3e5ea)\"/>\n <rect x=\"17\" y=\"15\" width=\"34\" height=\"3\" rx=\"1.5\" fill=\"var(--color-rubik-yellow, #ffd500)\"/>\n <g fill=\"var(--color-hairline, #e3e5ea)\">\n <rect x=\"17\" y=\"25\" width=\"60\" height=\"2.5\" rx=\"1.25\"/>\n <rect x=\"17\" y=\"43\" width=\"64\" height=\"2.5\" rx=\"1.25\"/>\n <rect x=\"17\" y=\"61\" width=\"42\" height=\"2.5\" rx=\"1.25\"/>\n </g>\n <rect x=\"15\" y=\"31.5\" width=\"70\" height=\"8\" rx=\"3\" fill=\"var(--color-rubik-red, #b71234)\" opacity=\"0.08\"/>\n <rect x=\"19\" y=\"34.25\" width=\"3.5\" height=\"2.5\" rx=\"1.25\" fill=\"var(--color-rubik-red, #b71234)\"/>\n <rect x=\"26\" y=\"34.25\" width=\"46\" height=\"2.5\" rx=\"1.25\" fill=\"var(--color-wire, #cfd3db)\"/>\n <path d=\"M26 35.5h46\" stroke=\"var(--color-rubik-red, #b71234)\" stroke-width=\"1.2\" stroke-linecap=\"round\"/>\n <circle cx=\"20.5\" cy=\"53.25\" r=\"2\" fill=\"var(--color-rubik-yellow, #ffd500)\"/>\n <rect x=\"26\" y=\"52\" width=\"38\" height=\"2.5\" rx=\"1.25\" fill=\"var(--color-wire, #cfd3db)\"/>\n <rect x=\"68\" y=\"50\" width=\"14\" height=\"6\" rx=\"3\" fill=\"var(--color-interactive-soft, rgba(0,70,173,0.08))\"/>\n <circle cx=\"113\" cy=\"22\" r=\"9\" fill=\"var(--color-success-soft, rgba(0,155,72,0.1))\"/>\n <path d=\"M108.5 22l3 3 5.5-6\" stroke=\"var(--color-rubik-green, #009b48)\" stroke-width=\"1.8\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\n <g fill=\"var(--color-rubik-green, #009b48)\">\n <rect x=\"99\" y=\"60\" width=\"6\" height=\"8\" rx=\"1.5\" opacity=\"0.35\"/>\n <rect x=\"108\" y=\"55\" width=\"6\" height=\"13\" rx=\"1.5\" opacity=\"0.55\"/>\n <rect x=\"117\" y=\"50\" width=\"6\" height=\"18\" rx=\"1.5\" opacity=\"0.75\"/>\n <rect x=\"126\" y=\"44\" width=\"6\" height=\"24\" rx=\"1.5\"/>\n </g>\n</svg>\n";
54
54
  var thumb_default$1 = "<svg viewBox=\"0 0 144 80\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\">\n <!-- Mock of the loop's dashboard: a calendar of dated reports beside the\n newest report. Colors ride the app's CSS variables. -->\n <rect x=\"10\" y=\"10\" width=\"16\" height=\"3\" rx=\"1.5\" fill=\"var(--color-wire, #cfd3db)\"/>\n <g stroke=\"var(--color-hairline, #e3e5ea)\" fill=\"var(--color-surface, #fff)\">\n <rect x=\"10.5\" y=\"18.5\" width=\"15\" height=\"13\" rx=\"3\"/><rect x=\"29.5\" y=\"18.5\" width=\"15\" height=\"13\" rx=\"3\"/><rect x=\"48.5\" y=\"18.5\" width=\"15\" height=\"13\" rx=\"3\"/>\n <rect x=\"10.5\" y=\"35.5\" width=\"15\" height=\"13\" rx=\"3\"/><rect x=\"29.5\" y=\"35.5\" width=\"15\" height=\"13\" rx=\"3\"/><rect x=\"48.5\" y=\"35.5\" width=\"15\" height=\"13\" rx=\"3\"/>\n <rect x=\"10.5\" y=\"52.5\" width=\"15\" height=\"13\" rx=\"3\"/><rect x=\"29.5\" y=\"52.5\" width=\"15\" height=\"13\" rx=\"3\"/><rect x=\"48.5\" y=\"52.5\" width=\"15\" height=\"13\" rx=\"3\"/>\n </g>\n <g fill=\"var(--color-rubik-blue, #0046ad)\">\n <circle cx=\"18\" cy=\"25\" r=\"2\"/><circle cx=\"37\" cy=\"25\" r=\"2\"/><circle cx=\"56\" cy=\"25\" r=\"2\"/>\n <circle cx=\"18\" cy=\"42\" r=\"2\"/><circle cx=\"37\" cy=\"42\" r=\"2\"/>\n </g>\n <rect x=\"72.5\" y=\"10.5\" width=\"61\" height=\"59\" rx=\"5\" fill=\"var(--color-surface, #fff)\" stroke=\"var(--color-hairline, #e3e5ea)\"/>\n <rect x=\"79\" y=\"18\" width=\"28\" height=\"3\" rx=\"1.5\" fill=\"var(--color-wire, #cfd3db)\"/>\n <rect x=\"112\" y=\"17\" width=\"16\" height=\"5\" rx=\"2.5\" fill=\"var(--color-interactive-soft, rgba(0,70,173,0.08))\"/>\n <g fill=\"var(--color-hairline, #e3e5ea)\">\n <rect x=\"79\" y=\"28\" width=\"48\" height=\"2.5\" rx=\"1.25\"/>\n <rect x=\"79\" y=\"34\" width=\"42\" height=\"2.5\" rx=\"1.25\"/>\n <rect x=\"79\" y=\"40\" width=\"48\" height=\"2.5\" rx=\"1.25\"/>\n <rect x=\"79\" y=\"46\" width=\"34\" height=\"2.5\" rx=\"1.25\"/>\n <rect x=\"79\" y=\"56\" width=\"44\" height=\"2.5\" rx=\"1.25\"/>\n <rect x=\"79\" y=\"62\" width=\"26\" height=\"2.5\" rx=\"1.25\"/>\n </g>\n</svg>\n";
55
55
  var thumb_default = "<svg viewBox=\"0 0 144 80\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\">\n <!-- Mock of the loop's dashboard: a PR kanban (open -> merged) over the\n health-score trend. Colors ride the app's CSS variables. -->\n <rect x=\"10\" y=\"8\" width=\"58\" height=\"3\" rx=\"1.5\" fill=\"var(--color-rubik-yellow, #ffd500)\"/>\n <rect x=\"10.5\" y=\"16.5\" width=\"57\" height=\"15\" rx=\"4\" fill=\"var(--color-surface, #fff)\" stroke=\"var(--color-hairline, #e3e5ea)\"/>\n <rect x=\"16\" y=\"21\" width=\"30\" height=\"2.5\" rx=\"1.25\" fill=\"var(--color-wire, #cfd3db)\"/>\n <rect x=\"16\" y=\"26\" width=\"20\" height=\"2.5\" rx=\"1.25\" fill=\"var(--color-hairline, #e3e5ea)\"/>\n <rect x=\"10.5\" y=\"36.5\" width=\"57\" height=\"15\" rx=\"4\" fill=\"var(--color-surface, #fff)\" stroke=\"var(--color-hairline, #e3e5ea)\"/>\n <rect x=\"16\" y=\"41\" width=\"36\" height=\"2.5\" rx=\"1.25\" fill=\"var(--color-wire, #cfd3db)\"/>\n <rect x=\"16\" y=\"46\" width=\"24\" height=\"2.5\" rx=\"1.25\" fill=\"var(--color-hairline, #e3e5ea)\"/>\n <rect x=\"76\" y=\"8\" width=\"58\" height=\"3\" rx=\"1.5\" fill=\"var(--color-rubik-green, #009b48)\"/>\n <rect x=\"76.5\" y=\"16.5\" width=\"57\" height=\"15\" rx=\"4\" fill=\"var(--color-surface, #fff)\" stroke=\"var(--color-hairline, #e3e5ea)\"/>\n <rect x=\"82\" y=\"21\" width=\"28\" height=\"2.5\" rx=\"1.25\" fill=\"var(--color-wire, #cfd3db)\"/>\n <path d=\"M122 23.5l2.5 2.5 4.5-5\" stroke=\"var(--color-rubik-green, #009b48)\" stroke-width=\"1.6\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\n <path d=\"M12 70 38 68 64 61 90 63 116 55 132 51\" stroke=\"var(--color-rubik-green, #009b48)\" stroke-width=\"1.6\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\n <rect x=\"12\" y=\"59\" width=\"22\" height=\"2.5\" rx=\"1.25\" fill=\"var(--color-hairline, #e3e5ea)\"/>\n</svg>\n";
@@ -135,7 +135,7 @@ var getAuthState_createServerFn_handler = createServerRpc({
135
135
  filename: "src/server/loopApi.ts"
136
136
  }, (opts) => getAuthState.__executeServer(opts));
137
137
  var getAuthState = createServerFn({ method: "GET" }).handler(getAuthState_createServerFn_handler, async () => {
138
- const { authEnabled } = await import("./auth-C6uZpC6v.mjs");
138
+ const { authEnabled } = await import("./auth-B_OxPpFr.mjs");
139
139
  return { enabled: authEnabled };
140
140
  });
141
141
  var getConfig_createServerFn_handler = createServerRpc({
@@ -244,7 +244,7 @@ var getArtifacts_createServerFn_handler = createServerRpc({
244
244
  var getArtifacts = createServerFn({ method: "GET" }).validator((d) => d).handler(getArtifacts_createServerFn_handler, async ({ data }) => {
245
245
  await backend();
246
246
  if (!await ownedLoop(data.loopId)) return [];
247
- const { listLoopArtifacts } = await import("./artifactFiles-uxLErS_7.mjs");
247
+ const { listLoopArtifacts } = await import("./artifactFiles-D6b_RP78.mjs");
248
248
  return listLoopArtifacts(data.loopId);
249
249
  });
250
250
  var getArtifact_createServerFn_handler = createServerRpc({
@@ -255,7 +255,7 @@ var getArtifact_createServerFn_handler = createServerRpc({
255
255
  var getArtifact = createServerFn({ method: "GET" }).validator((d) => d).handler(getArtifact_createServerFn_handler, async ({ data }) => {
256
256
  await backend();
257
257
  if (!await ownedLoop(data.loopId)) return { error: "file not found" };
258
- const { readLoopArtifact } = await import("./artifactFiles-uxLErS_7.mjs");
258
+ const { readLoopArtifact } = await import("./artifactFiles-D6b_RP78.mjs");
259
259
  return readLoopArtifact(data.loopId, data.path);
260
260
  });
261
261
  var getRunDiff_createServerFn_handler = createServerRpc({
@@ -274,7 +274,7 @@ var getRunDiff = createServerFn({ method: "GET" }).validator((d) => d).handler(g
274
274
  hasSnapshot: false,
275
275
  files: []
276
276
  };
277
- const { computeRunDiff } = await import("./runDiff-CyN5ZR5F.mjs");
277
+ const { computeRunDiff } = await import("./runDiff-Bohd5W5R.mjs");
278
278
  return computeRunDiff(data.runId);
279
279
  });
280
280
  var listTemplates_createServerFn_handler = createServerRpc({
@@ -300,6 +300,13 @@ var patchJob = createServerFn({ method: "POST" }).validator((d) => d).handler(pa
300
300
  if (p.continuousDelayMinutes !== void 0 && (!Number.isInteger(p.continuousDelayMinutes) || p.continuousDelayMinutes < 1)) return { error: "continuousDelayMinutes must be an integer >= 1" };
301
301
  if (p.channelId && enforce && (await getChannel(p.channelId))?.teamId !== owned.loop.teamId) return { error: "channel not found" };
302
302
  const agent = coerceCodingAgent(p.agent);
303
+ let metricSchema;
304
+ if (p.metricSchema !== void 0) if (p.metricSchema === null) metricSchema = null;
305
+ else {
306
+ const validated = await validateSchema(data.id, p.metricSchema);
307
+ if (!validated.ok) return { error: validated.detail };
308
+ metricSchema = validated.value;
309
+ }
303
310
  const loop = await updateLoop(data.id, {
304
311
  ...p.name !== void 0 ? { name: p.name.trim() || null } : {},
305
312
  ...p.cron !== void 0 ? { cron: p.cron } : {},
@@ -311,7 +318,7 @@ var patchJob = createServerFn({ method: "POST" }).validator((d) => d).handler(pa
311
318
  ...agent ? { agent } : {},
312
319
  ...p.goal !== void 0 ? { goal: p.goal?.trim() || null } : {},
313
320
  ...p.taskFile !== void 0 ? { taskFile: p.taskFile.trim() || null } : {},
314
- ...p.stateSchema !== void 0 ? { stateSchema: coerceStateSchema(p.stateSchema) ?? null } : {},
321
+ ...p.metricSchema !== void 0 ? { metricSchema: metricSchema ?? null } : {},
315
322
  ...p.ui !== void 0 ? { ui: coerceUi(p.ui) ?? null } : {},
316
323
  ...p.exec?.workdir !== void 0 ? { workdir: p.exec.workdir.trim() || null } : {},
317
324
  ...p.exec?.model !== void 0 ? { model: normalizeProviderSetting(p.exec.model) } : {},
@@ -345,7 +352,6 @@ var startJob = createServerFn({ method: "POST" }).validator((id) => id).handler(
345
352
  const owned = await ownedLoop(id);
346
353
  if (!owned) return { error: "not found" };
347
354
  if (owned.loop.deleteRequestedAt) return { error: "loop is being deleted" };
348
- if (owned.loop.completedAt) return { error: "completed loops must be reopened" };
349
355
  const loop = await startLoop(id);
350
356
  if (!loop) return { error: "not found" };
351
357
  scheduler.addLoop(loop);
@@ -389,7 +395,7 @@ var deleteJob = createServerFn({ method: "POST" }).validator((id) => id).handler
389
395
  if (!await requestDeleteLoop(id)) return { error: "not found" };
390
396
  if (unreachable && mayRetireAuthority) {
391
397
  if (!await forceDeleteLoop(id)) return { error: "delete failed; server data was not deleted" };
392
- const { logger } = await import("./logger-Dhuet6zm.mjs");
398
+ const { logger } = await import("./logger-DE_PU5bE.mjs");
393
399
  logger.child({ mod: "loop-lifecycle" }).warn({
394
400
  action: "unreachable-delete",
395
401
  loopId: id,
@@ -429,7 +435,7 @@ var forceDeleteJob = createServerFn({ method: "POST" }).validator((d) => d).hand
429
435
  }
430
436
  scheduler.removeLoop(data.id);
431
437
  if (!await forceDeleteLoop(data.id)) return { error: "force delete failed; server data was not deleted" };
432
- const { logger } = await import("./logger-Dhuet6zm.mjs");
438
+ const { logger } = await import("./logger-DE_PU5bE.mjs");
433
439
  logger.child({ mod: "loop-lifecycle" }).warn({
434
440
  action: "force-delete",
435
441
  loopId: data.id,
@@ -520,7 +526,7 @@ var mintClaim_createServerFn_handler = createServerRpc({
520
526
  }, (opts) => mintClaim.__executeServer(opts));
521
527
  var mintClaim = createServerFn({ method: "POST" }).validator((teamId) => teamId).handler(mintClaim_createServerFn_handler, async ({ data: teamId }) => {
522
528
  await backend();
523
- const { mintDeviceToken, rememberConnectKey } = await import("./tokens-DuV-D_RB.mjs");
529
+ const { mintDeviceToken, rememberConnectKey } = await import("./tokens-oJm6yFyt.mjs");
524
530
  const { userId, teamId: active } = await requestScope(teamId);
525
531
  const owner = userId ?? "shared";
526
532
  const token = mintDeviceToken();
@@ -1,8 +1,8 @@
1
1
  import { c as createFileRoute, s as lazyRouteComponent } from "../_libs/@tanstack/react-router+[...].mjs";
2
2
  import { c as getAuthState } from "./loopApi-BfMnXaKT.mjs";
3
- import { a as authClient } from "./LoopPlaybook-BxKPoWAp.mjs";
4
- //#region node_modules/.nitro/vite/services/ssr/assets/loops._loopId-BVPj6kiH.js
5
- var $$splitComponentImporter = () => import("./loops._loopId-DVG3dDvp.mjs");
3
+ import { a as authClient } from "./LoopPlaybook-CnQ8Qgyf.mjs";
4
+ //#region node_modules/.nitro/vite/services/ssr/assets/loops._loopId-C-y4Pux_.js
5
+ var $$splitComponentImporter = () => import("./loops._loopId-DfrHw2mZ.mjs");
6
6
  /**
7
7
  * Loop detail PAGE — `/loops/$loopId`. The dedicated page that replaced the old
8
8
  * dashboard modal: a loop header + action toolbar, the unified Files panel (task
@@ -1,18 +1,18 @@
1
1
  import { o as __toESM } from "../_runtime.mjs";
2
2
  import { g as require_jsx_runtime, h as require_react } from "../_libs/@base-ui/react+[...].mjs";
3
- import { c as CODING_AGENTS, ft as isTaskEntry, h as buildFileEntries } from "./boot-BpqTI96x.mjs";
4
- import { A as sectionHeadCls, C as isCompleted, D as rel, M as tsShort, N as until, S as isClosed, _ as dur, b as humanBytes, c as btnDanger, d as btnQuiet, h as dotLabel, i as Pre, j as selectCls, k as runPulseStyle, l as btnPrimary, m as dotColor, n as Loading, o as btn, p as cronText, r as Pill, s as btnCost, t as ErrorBanner, v as fmt, w as labelCls, x as inputCls } from "./ui-WGITbP6Z.mjs";
3
+ import { c as CODING_AGENTS, g as buildFileEntries, mt as isTaskEntry } from "./boot-BNoivm21.mjs";
4
+ import { A as tsShort, D as runPulseStyle, O as sectionHeadCls, S as labelCls, T as rel, _ as dur, b as humanBytes, c as btnDanger, d as btnQuiet, h as dotLabel, i as Pre, j as until, k as selectCls, l as btnPrimary, m as dotColor, n as Loading, o as btn, p as cronText, r as Pill, s as btnCost, t as ErrorBanner, v as fmt, x as inputCls } from "./ui-CI-OeTr7.mjs";
5
5
  import { d as useNavigate, u as Link } from "../_libs/@tanstack/react-router+[...].mjs";
6
6
  import { C as stopJob, S as startJob, a as evolveJob, b as requestEdit, d as getJobDetail, g as loadOlderRuns, i as deleteJob, s as getArtifacts, v as patchJob, x as runJob, y as pauseJob } from "./loopApi-BfMnXaKT.mjs";
7
7
  import { n as TaskFileView, r as ViewerHead, t as ArtifactBody } from "./artifactView-CYV1r48i.mjs";
8
- import { i as useFlash, n as FlashLine, r as LoadErrorCard, t as ConfirmBar } from "./actionUi-mDYOvJdB.mjs";
9
- import { s as useSession } from "./LoopPlaybook-BxKPoWAp.mjs";
8
+ import { s as useSession } from "./LoopPlaybook-CnQ8Qgyf.mjs";
10
9
  import { t as setActiveTeamCookie } from "./teamCookie-B7hLOIie.mjs";
11
- import { t as SignIn } from "./SignIn-C1duN7oy.mjs";
12
- import { i as lifecycleDisplay, n as daemonStopSupport, r as deriveLoopLifecycle, t as DAEMON_UPGRADE_REQUIRED } from "./lifecycleUi-l3nx9i3T.mjs";
13
- import { a as listChannels, n as Timeline, s as mergeRuns, t as MachinesModal } from "./notifyFns-qZ6FYwAL.mjs";
14
- import { t as Route } from "./loops._loopId-BVPj6kiH.mjs";
15
- //#region node_modules/.nitro/vite/services/ssr/assets/loops._loopId-DVG3dDvp.js
10
+ import { t as SignIn } from "./SignIn-CaAi4_mk.mjs";
11
+ import { i as useFlash, n as FlashLine, r as LoadErrorCard, t as ConfirmBar } from "./actionUi-Be7dmMxb.mjs";
12
+ import { i as lifecycleDisplay, n as daemonStopSupport, r as deriveLoopLifecycle, t as DAEMON_UPGRADE_REQUIRED } from "./lifecycleUi-5jMhxINb.mjs";
13
+ import { a as listChannels, n as Timeline, s as mergeRuns, t as MachinesModal } from "./notifyFns-CtmSVNmE.mjs";
14
+ import { t as Route } from "./loops._loopId-C-y4Pux_.mjs";
15
+ //#region node_modules/.nitro/vite/services/ssr/assets/loops._loopId-DfrHw2mZ.js
16
16
  var import_react = /* @__PURE__ */ __toESM(require_react());
17
17
  var import_jsx_runtime = require_jsx_runtime();
18
18
  /**
@@ -212,7 +212,7 @@ function initState(initial) {
212
212
  taskFile: initial?.taskFile ?? "",
213
213
  notify: initial?.notify ?? "auto",
214
214
  channelId: initial?.channelId ?? "",
215
- stateSchema: initial?.stateSchema ? JSON.stringify(initial.stateSchema) : "",
215
+ metricSchema: initial?.metricSchema ? JSON.stringify(initial.metricSchema) : "",
216
216
  ui: initial?.ui ?? "",
217
217
  agent: initial?.agent ?? "claude-code",
218
218
  workdir: e?.workdir ?? "",
@@ -288,10 +288,10 @@ var LoopForm = (0, import_react.forwardRef)(function LoopForm({ initial, channel
288
288
  listChannels().then(setFetched).catch(() => {});
289
289
  }, [channelsProp]);
290
290
  (0, import_react.useImperativeHandle)(ref, () => ({ read() {
291
- let stateSchema;
292
- const ss = f.stateSchema.trim();
291
+ let metricSchema;
292
+ const ss = f.metricSchema.trim();
293
293
  if (ss) try {
294
- stateSchema = JSON.parse(ss);
294
+ metricSchema = JSON.parse(ss);
295
295
  } catch {
296
296
  setSchemaErr("Not valid JSON - expected an array like [{\"key\":\"mrr\",\"label\":\"MRR\",\"unit\":\"$\"}]");
297
297
  return null;
@@ -307,7 +307,7 @@ var LoopForm = (0, import_react.forwardRef)(function LoopForm({ initial, channel
307
307
  ui: f.ui.trim() || void 0,
308
308
  agent: f.agent,
309
309
  exec: buildFormExec(f),
310
- stateSchema
310
+ metricSchema
311
311
  };
312
312
  } }));
313
313
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
@@ -489,9 +489,9 @@ var LoopForm = (0, import_react.forwardRef)(function LoopForm({ initial, channel
489
489
  fallback: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(EditorFallback, { minHeight: "90px" }),
490
490
  children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CodeField, {
491
491
  lang: "json",
492
- value: f.stateSchema,
492
+ value: f.metricSchema,
493
493
  onChange: (v) => {
494
- set("stateSchema", v);
494
+ set("metricSchema", v);
495
495
  setSchemaErr(null);
496
496
  },
497
497
  minHeight: "90px",
@@ -552,11 +552,11 @@ var EDIT_SEEDS = [
552
552
  seed: "Improve the dashboard: "
553
553
  },
554
554
  {
555
- label: "Set a finish line",
556
- seed: "Give this loop a goal, and finish it once the goal is met: "
555
+ label: "Set an objective",
556
+ seed: "Give this loop a standing objective to optimize toward: "
557
557
  }
558
558
  ];
559
- var LoopView = (0, import_react.lazy)(() => import("./LoopView-B0AwFjwS.mjs").then((m) => ({ default: m.LoopView })));
559
+ var LoopView = (0, import_react.lazy)(() => import("./LoopView-DM30OPCN.mjs").then((m) => ({ default: m.LoopView })));
560
560
  /**
561
561
  * Loop detail PAGE body (`/loops/$loopId`) — the redesign of the former modal.
562
562
  * One scrolling page: a loop header (name / status / schedule / agent / machine +
@@ -664,10 +664,7 @@ function LoopDetailView({ id }) {
664
664
  setActionErr(null);
665
665
  setPending("lifecycle");
666
666
  try {
667
- const r = action === "pause" ? await pauseJob({ data: id }) : action === "start" ? await startJob({ data: id }) : action === "stop" ? await stopJob({ data: id }) : action === "reopen" ? await patchJob({ data: {
668
- id,
669
- patch: { enabled: true }
670
- } }) : await deleteJob({ data: id });
667
+ const r = action === "pause" ? await pauseJob({ data: id }) : action === "start" ? await startJob({ data: id }) : action === "stop" ? await stopJob({ data: id }) : await deleteJob({ data: id });
671
668
  if (r.error) return setActionErr(`${action[0].toUpperCase() + action.slice(1)} failed: ${r.error}`);
672
669
  setConfirmingDelete(false);
673
670
  if (r.deleted) {
@@ -677,7 +674,7 @@ function LoopDetailView({ id }) {
677
674
  }
678
675
  if (action === "delete") deletingRef.current = true;
679
676
  await refreshAll();
680
- setFlash({ label: action === "start" ? "Started" : action === "reopen" ? "Reopened" : action === "pause" ? "Paused" : action === "stop" ? "Stop requested" : "Deleting" });
677
+ setFlash({ label: action === "start" ? "Started" : action === "pause" ? "Paused" : action === "stop" ? "Stop requested" : "Deleting" });
681
678
  } finally {
682
679
  setPending(null);
683
680
  }
@@ -746,11 +743,9 @@ function LoopDetailView({ id }) {
746
743
  const online = detail.machine.online;
747
744
  const asleep = detail.machine.presence === "asleep";
748
745
  const offlineHint = !online ? asleep ? "Machine asleep - requests will stay queued until it wakes" : "Machine offline - requests will stay queued until it reconnects" : void 0;
749
- const completed = isCompleted(s);
750
746
  const lifecycle = deriveLoopLifecycle(s);
751
747
  const lifecycleText = lifecycleDisplay(detail);
752
748
  const protocolSupport = daemonStopSupport(detail.machine.daemonProtocol);
753
- const closedActive = isClosed(s) && !completed;
754
749
  const onMachine = detail.machine.name ? `“${detail.machine.name}”` : "the bound machine";
755
750
  const editRun = editDispatched && editRunId ? runs.find((r) => r.id === editRunId) : void 0;
756
751
  const editSettled = !!editRun && !editRun.queued && !editRun.running;
@@ -781,8 +776,8 @@ function LoopDetailView({ id }) {
781
776
  })
782
777
  });
783
778
  const deleting = lifecycle === "deleting";
784
- const paused = !s.enabled && !completed && !deleting;
785
- const active = s.enabled && !completed && !deleting;
779
+ const paused = !s.enabled && !deleting;
780
+ const active = s.enabled && !deleting;
786
781
  const actionDisabled = busy || deleting;
787
782
  const canRunWork = active || paused;
788
783
  const canStop = canRunWork && !!s.running && protocolSupport.supported;
@@ -797,13 +792,13 @@ function LoopDetailView({ id }) {
797
792
  className: btnPrimary,
798
793
  disabled: actionDisabled || !canRunWork,
799
794
  onClick: () => void doRun(),
800
- title: !canRunWork ? "Unavailable for a completed or deleting loop" : offlineHint ?? (job.exec ? "Spends credits" : void 0),
795
+ title: !canRunWork ? "Unavailable for a deleting loop" : offlineHint ?? (job.exec ? "Spends credits" : void 0),
801
796
  "aria-label": job.exec ? "Run once - spends credits" : "Run once",
802
797
  children: pending === "run" ? "Queuing…" : "Run once"
803
798
  }),
804
799
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", {
805
800
  className: btn,
806
- disabled: actionDisabled || !(canRunWork || completed),
801
+ disabled: actionDisabled || !canRunWork,
807
802
  title: "Dispatch or copy a prompt for an owner-requested edit",
808
803
  onClick: () => setEditVia(true),
809
804
  children: "Agent edit"
@@ -811,7 +806,7 @@ function LoopDetailView({ id }) {
811
806
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", {
812
807
  className: btn,
813
808
  disabled: actionDisabled || !canRunWork,
814
- title: !canRunWork ? "Unavailable for a completed or deleting loop" : offlineHint ?? "Spends credits",
809
+ title: !canRunWork ? "Unavailable for a deleting loop" : offlineHint ?? "Spends credits",
815
810
  onClick: () => void doEvolve(),
816
811
  children: pending === "evolve" ? "Evolving…" : "Evolve once"
817
812
  }),
@@ -840,12 +835,6 @@ function LoopDetailView({ id }) {
840
835
  onClick: () => void doLifecycle("stop"),
841
836
  children: "Stop"
842
837
  }),
843
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", {
844
- className: btn,
845
- disabled: actionDisabled || !completed,
846
- onClick: () => void doLifecycle("reopen"),
847
- children: "Reopen"
848
- }),
849
838
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", {
850
839
  className: btnDanger,
851
840
  disabled: actionDisabled,
@@ -1005,6 +994,36 @@ function LoopDetailView({ id }) {
1005
994
  })
1006
995
  ]
1007
996
  });
997
+ const needsUpdateEl = detail.machine.needsUpdate && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
998
+ className: "mb-2.5 flex flex-wrap items-center gap-x-3 gap-y-1.5 rounded-control border border-warn/40 bg-warn/10 px-4 py-2.5",
999
+ children: [
1000
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", {
1001
+ className: "inline-flex items-center gap-2 text-meta font-medium text-primary",
1002
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
1003
+ "aria-hidden": true,
1004
+ className: "size-2 rounded-full bg-warn"
1005
+ }), "Daemon update required"]
1006
+ }),
1007
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", {
1008
+ className: "text-meta text-secondary",
1009
+ children: [
1010
+ "This server requires v",
1011
+ detail.machine.requiredDaemonVersion,
1012
+ "; ",
1013
+ detail.machine.name ? `“${detail.machine.name}”` : "the bound machine",
1014
+ " reports ",
1015
+ detail.machine.daemonVersion ? `v${detail.machine.daemonVersion}` : "an unknown version",
1016
+ ". Queued work will wait."
1017
+ ]
1018
+ }),
1019
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", {
1020
+ type: "button",
1021
+ onClick: () => setMachinesOpen(true),
1022
+ className: "ml-auto cursor-pointer text-meta font-medium text-interactive underline underline-offset-2 transition-colors hover:text-display",
1023
+ children: "Update daemon"
1024
+ })
1025
+ ]
1026
+ });
1008
1027
  const offlineEl = !online && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
1009
1028
  className: "mb-2.5 flex flex-wrap items-center gap-x-3 gap-y-1.5 rounded-control border border-hairline bg-raised px-4 py-2.5",
1010
1029
  children: [
@@ -1088,11 +1107,7 @@ function LoopDetailView({ id }) {
1088
1107
  className: "text-[28px] font-semibold leading-tight tracking-[-0.015em] text-display",
1089
1108
  children: s.name
1090
1109
  }),
1091
- lifecycle === "completed" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Pill, {
1092
- tone: "success",
1093
- dot: "green",
1094
- children: "Completed"
1095
- }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Pill, {
1110
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Pill, {
1096
1111
  tone: lifecycle === "stopping" ? "running" : void 0,
1097
1112
  dot: lifecycle === "stopping" ? "pulse" : void 0,
1098
1113
  children: lifecycleText
@@ -1101,10 +1116,10 @@ function LoopDetailView({ id }) {
1101
1116
  tone: "outline",
1102
1117
  children: "Queued"
1103
1118
  }) : null,
1104
- closedActive && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Pill, {
1119
+ s.goal && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Pill, {
1105
1120
  tone: "success",
1106
1121
  title: s.goal ?? void 0,
1107
- children: "Goal"
1122
+ children: "Objective"
1108
1123
  }),
1109
1124
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Pill, {
1110
1125
  tone: "outline",
@@ -1144,7 +1159,7 @@ function LoopDetailView({ id }) {
1144
1159
  }),
1145
1160
  metaDot,
1146
1161
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { children: ["next ", fmt(s.nextRun)] }),
1147
- s.nextRun && s.enabled && !completed && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", {
1162
+ s.nextRun && s.enabled && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", {
1148
1163
  className: "text-disabled",
1149
1164
  children: [
1150
1165
  "(",
@@ -1170,24 +1185,13 @@ function LoopDetailView({ id }) {
1170
1185
  })
1171
1186
  ]
1172
1187
  }),
1173
- closedActive && s.goal && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
1188
+ s.goal && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
1174
1189
  className: "mt-2 text-body leading-snug text-secondary",
1175
1190
  children: ["Working toward: ", /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
1176
1191
  className: "text-primary",
1177
1192
  children: s.goal
1178
1193
  })]
1179
1194
  }),
1180
- lifecycle === "completed" && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
1181
- className: "mt-2 text-body leading-snug text-success",
1182
- children: [
1183
- "Completed",
1184
- s.completedAt ? ` · ${fmt(s.completedAt)}` : "",
1185
- s.completionReason && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", {
1186
- className: "text-secondary",
1187
- children: [" - ", s.completionReason]
1188
- })
1189
- ]
1190
- }),
1191
1195
  latestIncidentRun?.reportIncident && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
1192
1196
  className: "mt-2 text-body leading-snug text-secondary",
1193
1197
  children: latestIncidentRun.phase === "error" ? "Last run failed · Terminal report rejected" : "Last run telemetry warning · Terminal report rejected"
@@ -1198,6 +1202,7 @@ function LoopDetailView({ id }) {
1198
1202
  className: "mt-5 border-t border-hairline pt-4",
1199
1203
  children: [
1200
1204
  crossTeamEl,
1205
+ needsUpdateEl,
1201
1206
  offlineEl,
1202
1207
  actionErrEl,
1203
1208
  flashLine,
@@ -1,8 +1,8 @@
1
1
  import { c as createFileRoute, s as lazyRouteComponent } from "../_libs/@tanstack/react-router+[...].mjs";
2
2
  import { c as getAuthState } from "./loopApi-BfMnXaKT.mjs";
3
- import { a as authClient } from "./LoopPlaybook-BxKPoWAp.mjs";
4
- //#region node_modules/.nitro/vite/services/ssr/assets/loops._loopId_.runs._runId-DbEF9MQu.js
5
- var $$splitComponentImporter = () => import("./loops._loopId_.runs._runId-Db6cTcUT.mjs");
3
+ import { a as authClient } from "./LoopPlaybook-CnQ8Qgyf.mjs";
4
+ //#region node_modules/.nitro/vite/services/ssr/assets/loops._loopId_.runs._runId-BXyyqXwX.js
5
+ var $$splitComponentImporter = () => import("./loops._loopId_.runs._runId-C9d0EC2Q.mjs");
6
6
  /**
7
7
  * Run detail PAGE — `/loops/$loopId/runs/$runId`. A standalone page (the trailing
8
8
  * `_` on the `$loopId` segment opts it out of the loop page's component so the run