lobstah 0.7.1 → 0.7.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.
package/dist/main.js CHANGED
@@ -1621,6 +1621,10 @@ function expandHome(p) {
1621
1621
  function loadConfig() {
1622
1622
  const file2 = configPath();
1623
1623
  const raw = fs7.existsSync(file2) ? parse(fs7.readFileSync(file2, "utf8")) : {};
1624
+ const watch2 = { ...DEFAULT_WATCH, ...raw.watch ?? {} };
1625
+ if (typeof watch2.repairCooldownSecs !== "number" || !Number.isFinite(watch2.repairCooldownSecs) || watch2.repairCooldownSecs < 0) {
1626
+ throw new Error(`[watch].repairCooldownSecs must be a non-negative number in ${configPath()}`);
1627
+ }
1624
1628
  const readySettleSecs = raw.readySettleSecs ?? 600;
1625
1629
  if (typeof readySettleSecs !== "number" || !Number.isFinite(readySettleSecs) || readySettleSecs < 0) {
1626
1630
  throw new Error(`readySettleSecs must be a non-negative number in ${configPath()}`);
@@ -1656,7 +1660,7 @@ function loadConfig() {
1656
1660
  soak: parseSoak(raw.soak),
1657
1661
  helm: { ...DEFAULT_HELM, ...raw.helm ?? {} },
1658
1662
  glass: { ...DEFAULT_GLASS, ...raw.glass ?? {} },
1659
- watch: { ...DEFAULT_WATCH, ...raw.watch ?? {} },
1663
+ watch: watch2,
1660
1664
  grounds,
1661
1665
  notifyCommand: raw.notifyCommand ? String(raw.notifyCommand) : void 0,
1662
1666
  notifyVerbs: Array.isArray(raw.notifyVerbs) ? raw.notifyVerbs.map(String) : void 0,
@@ -1706,7 +1710,7 @@ var init_config = __esm({
1706
1710
  armGraceSecs: 5
1707
1711
  };
1708
1712
  DEFAULT_GLASS = { port: 4949 };
1709
- DEFAULT_WATCH = { maxForksPerCycle: 3, autoRepair: true, conflicts: true, checks: true, maxRepairsPerPr: 2, maxRepairsWithoutProgress: 2, repairSettleSecs: 600, repairTrapWaitSecs: 600 };
1713
+ DEFAULT_WATCH = { maxForksPerCycle: 3, autoRepair: true, conflicts: true, checks: true, maxRepairsPerPr: 2, maxRepairsWithoutProgress: 2, repairSettleSecs: 600, repairCooldownSecs: 0, repairTrapWaitSecs: 600 };
1710
1714
  DEFAULT_LIMITS = {
1711
1715
  maxConcurrent: 2,
1712
1716
  choreConcurrent: 1,
@@ -2210,6 +2214,7 @@ function upsertPr(pr, dispatchId) {
2210
2214
  key: ref.key,
2211
2215
  repo: `${ref.owner}/${ref.repo}`,
2212
2216
  dispatches,
2217
+ observedHeadShas: [...new Set([...before?.observedHeadShas ?? [], before?.headSha, pr.headSha].filter((s) => !!s))],
2213
2218
  standingSince,
2214
2219
  observations: (before?.observations ?? 0) + 1,
2215
2220
  firstSeenAt,
@@ -2249,16 +2254,18 @@ function removePr(key) {
2249
2254
  }
2250
2255
  function resetRepairStreaks(key) {
2251
2256
  const reset = [];
2257
+ const lastRepair = (pr) => pr.repairCooldown?.dispatchId ?? pr.repairStreak?.lastRepairId ?? pr.repair?.dispatchId;
2252
2258
  for (const pr of key === void 0 ? readPrs() : [readPr(key)].filter((p) => p !== void 0)) {
2253
- if (!pr.repairStreak)
2259
+ if (!pr.repairStreak && (!lastRepair(pr) || pr.repairCooldown?.released))
2254
2260
  continue;
2255
2261
  withPrLock(pr.key, () => {
2256
2262
  const current = readPr(pr.key);
2257
- if (!current?.repairStreak)
2263
+ if (!current || !current.repairStreak && (!lastRepair(current) || current.repairCooldown?.released))
2258
2264
  return;
2259
- const stopped = current.repair?.status === "gave-up" && current.repairStreak.stoppedHead !== void 0;
2265
+ const stopped = current.repair?.status === "gave-up" && current.repairStreak?.stoppedHead !== void 0;
2266
+ const cooldown = current.repairCooldown ?? (lastRepair(current) ? { dispatchId: lastRepair(current) } : void 0);
2260
2267
  const { repairStreak: _streak, ...rest } = current;
2261
- writePr({ ...rest, ...stopped ? { repair: void 0 } : {} });
2268
+ writePr({ ...rest, ...cooldown ? { repairCooldown: { ...cooldown, released: true } } : {}, ...stopped ? { repair: void 0 } : {} });
2262
2269
  reset.push(pr.key);
2263
2270
  });
2264
2271
  }
@@ -2317,7 +2324,7 @@ function derivePrStacks(records, opts = {}) {
2317
2324
  const order = members.map((p) => `#${p.number}`).join(" \u2192 ");
2318
2325
  const waiting = members.filter((p) => !isReady(p)).map((p) => `#${p.number} (${p.discoveryPending ? "awaiting watch check" : prStandingKinds(p).includes("pr:ready") ? "settling" : prBadge(p).text})`).join(", ");
2319
2326
  out.push({
2320
- id: parsePrRef(members.at(-1).url).key,
2327
+ id: parsePrRef(bottom.url).key,
2321
2328
  repo: repo(bottom),
2322
2329
  members,
2323
2330
  ready,
@@ -2338,54 +2345,80 @@ function readStackEpochs() {
2338
2345
  return {};
2339
2346
  }
2340
2347
  }
2341
- function quietStackTailUrls(records, epochs = readStackEpochs()) {
2342
- const byKey = new Map(records.map((p) => [parsePrRef(p.url)?.key, p]));
2343
- return Object.values(epochs).flatMap((s) => {
2344
- if (!s.ready || !s.notified)
2345
- return [];
2346
- const members = Object.entries(s.heads).map(([key, sha3]) => ({ p: byKey.get(key), sha: sha3 }));
2347
- const open = members.filter(({ p }) => p?.state === "OPEN");
2348
- return open.length === 1 && open[0].p.headSha === open[0].sha && members.every(({ p }) => p?.state === "OPEN" || p?.state === "MERGED") ? [open[0].p.url] : [];
2348
+ function identifyPrStacks(stacks, state = readStackEpochs()) {
2349
+ const used = /* @__PURE__ */ new Set();
2350
+ return stacks.map((s) => {
2351
+ const keys = Object.keys(headsOf(s));
2352
+ const matches = Object.entries(state).filter(([id2]) => !used.has(id2)).map(([id2, old]) => ({
2353
+ id: id2,
2354
+ old,
2355
+ overlap: keys.filter((key) => key in old.heads).length
2356
+ })).filter((x) => x.overlap > 0).sort((a, b) => b.overlap - a.overlap || a.old.at.localeCompare(b.old.at) || a.id.localeCompare(b.id));
2357
+ const id = matches[0]?.old.version === 2 ? matches[0].id : s.id;
2358
+ used.add(id);
2359
+ return { ...s, id, legacy: matches.some((m) => m.old.version !== 2) };
2349
2360
  });
2350
2361
  }
2351
2362
  function syncStackReadiness(cfg = loadConfig(), now2 = Date.now()) {
2352
2363
  const records = readPrs();
2353
- const stacks = derivePrStacks(records, { trunk: (r) => prStackTrunk(cfg, r), readySettleSecs: cfg.readySettleSecs, now: now2 });
2354
2364
  withPrLock("pr:lobstah/stack-state#0", () => {
2355
2365
  const state = readStackEpochs();
2366
+ const stacks = identifyPrStacks(derivePrStacks(records, { trunk: (r) => prStackTrunk(cfg, r), readySettleSecs: cfg.readySettleSecs, now: now2 }), state);
2367
+ const notices = listNotices(Number.MAX_SAFE_INTEGER).filter((n) => n.kind === "stack-ready");
2368
+ const kept = /* @__PURE__ */ new Set();
2356
2369
  for (const s of stacks) {
2357
- const old = state[s.id];
2358
2370
  const heads = headsOf(s);
2371
+ const aliases = Object.keys(state).filter((id) => Object.keys(heads).some((key) => key in state[id].heads));
2372
+ const old = state[s.id] ?? aliases.map((id) => state[id]).sort((a, b) => b.at.localeCompare(a.at))[0];
2373
+ const previous = notices.filter((n) => n.refId === s.id || aliases.includes(n.refId ?? "") || n.refId in heads);
2374
+ const migrating = !!old && old.version !== 2 || !old && previous.length > 0;
2359
2375
  const removed = old ? Object.keys(old.heads).filter((key) => !(key in heads)) : [];
2360
2376
  const partialMerge = !!old && Object.entries(heads).every(([key, sha3]) => old.heads[key] === sha3) && removed.every((key) => records.find((p) => p.key === key)?.state === "MERGED");
2361
2377
  const changed = !!old && !partialMerge && JSON.stringify(heads) !== JSON.stringify(old.heads);
2362
- const epoch = (old?.epoch ?? 0) + Number(changed || !!old?.ready && !s.allReady);
2378
+ const settlingOnly = !!old?.notified && !changed && s.members.every((p) => prStandingKinds(p).includes("pr:ready"));
2379
+ const epoch = (old?.epoch ?? 0) + Number(changed || !!old?.ready && !s.allReady && !settlingOnly);
2363
2380
  const next = {
2381
+ version: 2,
2364
2382
  heads,
2365
2383
  ready: s.allReady,
2366
2384
  epoch,
2367
2385
  at: !old || changed || old.ready !== s.allReady ? new Date(now2).toISOString() : old.at,
2368
- notified: changed || !s.allReady ? false : old?.notified
2386
+ notified: migrating ? s.allReady : changed || !s.allReady && !settlingOnly ? false : old?.notified,
2387
+ silent: migrating || !changed && (s.allReady || settlingOnly) && !!old?.silent
2369
2388
  };
2370
- if (s.allReady && !next.notified) {
2371
- postNotice({
2372
- kind: "stack-ready",
2373
- text: s.text,
2374
- refId: s.id,
2375
- stateHash: stackStateHash(s, epoch),
2376
- repo: repoKey(cfg, s.repo),
2377
- quiet: !stackReadyEnabled(cfg),
2378
- dedupeKey: `stack-ready-${s.id}-${epoch}`
2379
- });
2389
+ const wake = s.allReady && !next.notified && !migrating;
2390
+ const current = previous.at(-1);
2391
+ const fields = {
2392
+ kind: "stack-ready",
2393
+ text: s.text,
2394
+ refId: s.id,
2395
+ url: s.members.at(-1).url,
2396
+ stateHash: stackStateHash(s, epoch),
2397
+ repo: repoKey(cfg, s.repo),
2398
+ quiet: migrating || !s.allReady || !stackReadyEnabled(cfg) || !wake && (!current || !!current.quiet)
2399
+ };
2400
+ const notice = wake || !current ? postNotice(fields) : { ...current, ...fields };
2401
+ if (current && !wake) {
2402
+ const file3 = path7.join(noticesDir(), `${notice.seq}.json`), tmp2 = `${file3}.tmp-${process.pid}`;
2403
+ fs9.writeFileSync(tmp2, JSON.stringify(notice));
2404
+ fs9.renameSync(tmp2, file3);
2405
+ }
2406
+ kept.add(notice.seq);
2407
+ if (s.allReady)
2380
2408
  next.notified = true;
2381
- }
2409
+ for (const alias of aliases)
2410
+ if (alias !== s.id)
2411
+ delete state[alias];
2382
2412
  state[s.id] = next;
2383
2413
  }
2414
+ for (const n of notices)
2415
+ if (!kept.has(n.seq))
2416
+ fs9.rmSync(path7.join(noticesDir(), `${n.seq}.json`), { force: true });
2384
2417
  for (const [id, old] of Object.entries(state)) {
2385
2418
  if (stacks.some((s) => s.id === id))
2386
2419
  continue;
2387
2420
  const surviving = records.filter((p) => p.state === "OPEN" && p.key in old.heads);
2388
- if (old.ready && (surviving.length === 0 || records.some((p) => p.key in old.heads && p.state === "CLOSED") || surviving.some((p) => p.headSha !== old.heads[p.key] || !prStandingKinds(p, { readySettleSecs: cfg.readySettleSecs, now: now2 }).includes("pr:ready")))) {
2421
+ if (old.ready && (surviving.length === 0 || surviving.length >= 2 || records.some((p) => p.key in old.heads && p.state === "CLOSED") || surviving.some((p) => p.headSha !== old.heads[p.key] || !prStandingKinds(p, { readySettleSecs: cfg.readySettleSecs, now: now2 }).includes("pr:ready")))) {
2389
2422
  old.ready = false;
2390
2423
  old.notified = false;
2391
2424
  old.epoch++;
@@ -2416,7 +2449,7 @@ var init_pr_stacks = __esm({
2416
2449
  });
2417
2450
 
2418
2451
  // packages/core/dist/notices.js
2419
- import * as fs10 from "node:fs";
2452
+ import fs10 from "node:fs";
2420
2453
  import * as path8 from "node:path";
2421
2454
  function noticesDir() {
2422
2455
  return path8.join(lobstahHome(), "notices");
@@ -2435,13 +2468,14 @@ function postNotice(n) {
2435
2468
  return void 0;
2436
2469
  }
2437
2470
  }
2438
- const seq = `${String(Date.now()).padStart(15, "0")}-${process.pid}-${counter++}`;
2471
+ const seq = `${String(Date.now()).padStart(15, "0")}-${process.pid}-${String(counter++).padStart(8, "0")}`;
2439
2472
  const notice = {
2440
2473
  seq,
2441
2474
  kind: n.kind,
2442
2475
  at: (/* @__PURE__ */ new Date()).toISOString(),
2443
2476
  text: n.text,
2444
2477
  refId: n.refId,
2478
+ ...n.url ? { url: n.url } : {},
2445
2479
  repo: n.repo,
2446
2480
  by: n.by,
2447
2481
  ...n.quiet ? { quiet: true } : {},
@@ -2461,7 +2495,15 @@ function listNotices(limit = 20) {
2461
2495
  } catch {
2462
2496
  return [];
2463
2497
  }
2464
- return files.sort().slice(-limit).map((f) => JSON.parse(fs10.readFileSync(path8.join(dir, f), "utf8")));
2498
+ return files.sort().slice(-limit).flatMap((f) => {
2499
+ try {
2500
+ return [JSON.parse(fs10.readFileSync(path8.join(dir, f), "utf8"))];
2501
+ } catch (error) {
2502
+ if (error.code === "ENOENT")
2503
+ return [];
2504
+ throw error;
2505
+ }
2506
+ });
2465
2507
  }
2466
2508
  function unseenNotices(consume, filter, wakes) {
2467
2509
  let seen = "";
@@ -7987,14 +8029,14 @@ __export(stats_exports, {
7987
8029
  readStatsPage: () => readStatsPage,
7988
8030
  statsOutput: () => statsOutput
7989
8031
  });
7990
- import * as fs68 from "node:fs";
8032
+ import * as fs69 from "node:fs";
7991
8033
  import * as path59 from "node:path";
7992
8034
  function trapNames() {
7993
8035
  const dir = path59.join(lobstahHome(), "trap-names");
7994
8036
  const names = /* @__PURE__ */ new Map();
7995
8037
  for (const file2 of readDirIfPresent(dir).filter((f) => f.endsWith(".json")).sort()) {
7996
8038
  try {
7997
- const reg = JSON.parse(fs68.readFileSync(path59.join(dir, file2), "utf8"));
8039
+ const reg = JSON.parse(fs69.readFileSync(path59.join(dir, file2), "utf8"));
7998
8040
  if (typeof reg.trapId === "string") names.set(`wt:${reg.trapId}`, file2.slice(0, -5));
7999
8041
  } catch (err) {
8000
8042
  if (!(err instanceof SyntaxError) && err.code !== "ENOENT") throw err;
@@ -8326,25 +8368,25 @@ var init_claude = __esm({
8326
8368
  });
8327
8369
 
8328
8370
  // packages/adapters/dist/codex.js
8329
- import * as fs82 from "node:fs";
8371
+ import * as fs83 from "node:fs";
8330
8372
  import * as os12 from "node:os";
8331
8373
  import * as path68 from "node:path";
8332
8374
  import { spawn as spawn6 } from "node:child_process";
8333
8375
  function isolatedCodexHome() {
8334
8376
  const dir = path68.join(lobstahHome(), "codex-home");
8335
- fs82.mkdirSync(dir, { recursive: true });
8377
+ fs83.mkdirSync(dir, { recursive: true });
8336
8378
  const auth = path68.join(dir, "auth.json");
8337
8379
  const realAuth = path68.join(os12.homedir(), ".codex", "auth.json");
8338
- if (!fs82.existsSync(auth) && fs82.existsSync(realAuth)) {
8380
+ if (!fs83.existsSync(auth) && fs83.existsSync(realAuth)) {
8339
8381
  try {
8340
- fs82.symlinkSync(realAuth, auth);
8382
+ fs83.symlinkSync(realAuth, auth);
8341
8383
  } catch {
8342
- fs82.copyFileSync(realAuth, auth);
8384
+ fs83.copyFileSync(realAuth, auth);
8343
8385
  }
8344
8386
  }
8345
8387
  const cfg = path68.join(dir, "config.toml");
8346
- if (!fs82.existsSync(cfg)) {
8347
- fs82.writeFileSync(cfg, "# lobstah-managed: auth inherited from ~/.codex, interactive config not\n");
8388
+ if (!fs83.existsSync(cfg)) {
8389
+ fs83.writeFileSync(cfg, "# lobstah-managed: auth inherited from ~/.codex, interactive config not\n");
8348
8390
  }
8349
8391
  return dir;
8350
8392
  }
@@ -8970,7 +9012,7 @@ var init_wallclock = __esm({
8970
9012
  import { execFile as execFile5 } from "node:child_process";
8971
9013
  import { promisify as promisify5 } from "node:util";
8972
9014
  import { fileURLToPath as fileURLToPath4 } from "node:url";
8973
- import * as fs83 from "node:fs";
9015
+ import * as fs84 from "node:fs";
8974
9016
  import * as path69 from "node:path";
8975
9017
  function firstLine2(err) {
8976
9018
  const stderr = err && typeof err === "object" && "stderr" in err ? String(err.stderr).trim() : "";
@@ -9007,7 +9049,7 @@ function keepRemote(opts) {
9007
9049
  const root = path69.resolve(path69.dirname(fileURLToPath4(import.meta.url)), "..", "..", "..");
9008
9050
  const localCli = path69.join(root, "apps", "cli", "dist", "main.js");
9009
9051
  const args = ["watch", "add", url, "--for", id];
9010
- if (fs83.existsSync(localCli))
9052
+ if (fs84.existsSync(localCli))
9011
9053
  await command(process.execPath, [localCli, ...args], 15e3);
9012
9054
  else
9013
9055
  await command("lobstah", args, 15e3);
@@ -9169,7 +9211,7 @@ var init_remote = __esm({
9169
9211
  });
9170
9212
 
9171
9213
  // packages/runner/dist/run.js
9172
- import * as fs84 from "node:fs";
9214
+ import * as fs85 from "node:fs";
9173
9215
  import * as path70 from "node:path";
9174
9216
  import { execFileSync as execFileSync9 } from "node:child_process";
9175
9217
  import { fileURLToPath as fileURLToPath5 } from "node:url";
@@ -9208,13 +9250,13 @@ async function main(activeDir, lane, seams = {}) {
9208
9250
  throw new Error(`refusing headless runner for trap catch ${id}`);
9209
9251
  const status = (verb, note) => appendStatus(id, lane, verb, note);
9210
9252
  const cfg = loadConfig();
9211
- const descriptor = JSON.parse(fs84.readFileSync(path70.join(activeDir, "descriptor.json"), "utf8"));
9253
+ const descriptor = JSON.parse(fs85.readFileSync(path70.join(activeDir, "descriptor.json"), "utf8"));
9212
9254
  const resolved = resolveDispatch(descriptor, cfg);
9213
9255
  const repo = cfg.repos[descriptor.repo];
9214
9256
  const briefFile = path70.join(activeDir, "brief.md");
9215
- if (!fs84.existsSync(briefFile))
9216
- fs84.writeFileSync(briefFile, descriptor.brief);
9217
- const brief = fs84.readFileSync(briefFile, "utf8");
9257
+ if (!fs85.existsSync(briefFile))
9258
+ fs85.writeFileSync(briefFile, descriptor.brief);
9259
+ const brief = fs85.readFileSync(briefFile, "utf8");
9218
9260
  const attempts = Number(process.env.LOBSTAH_ATTEMPTS ?? "1");
9219
9261
  const wake = process.env.LOBSTAH_WAKE;
9220
9262
  const runnerInfo = {
@@ -9222,7 +9264,7 @@ async function main(activeDir, lane, seams = {}) {
9222
9264
  startedAt: (/* @__PURE__ */ new Date()).toISOString(),
9223
9265
  attempts
9224
9266
  };
9225
- fs84.writeFileSync(path70.join(activeDir, "runner.json"), JSON.stringify(runnerInfo, null, 2));
9267
+ fs85.writeFileSync(path70.join(activeDir, "runner.json"), JSON.stringify(runnerInfo, null, 2));
9226
9268
  if (descriptor.systemRepair && descriptor.pr?.url) {
9227
9269
  const state = deps.prState(descriptor.pr.url) ?? readPr(parsePrRef(descriptor.pr.url)?.key ?? "")?.state;
9228
9270
  if (state === "MERGED" || state === "CLOSED") {
@@ -9243,11 +9285,11 @@ async function main(activeDir, lane, seams = {}) {
9243
9285
  let recorded;
9244
9286
  let recovered = false;
9245
9287
  let choice3;
9246
- if (fs84.existsSync(wtFile)) {
9247
- recorded = JSON.parse(fs84.readFileSync(wtFile, "utf8")).path;
9248
- if (!fs84.existsSync(recorded))
9288
+ if (fs85.existsSync(wtFile)) {
9289
+ recorded = JSON.parse(fs85.readFileSync(wtFile, "utf8")).path;
9290
+ if (!fs85.existsSync(recorded))
9249
9291
  throw new Error(`recorded worktree ${recorded} is gone`);
9250
- } else if (fs84.existsSync(worktreePath(id))) {
9292
+ } else if (fs85.existsSync(worktreePath(id))) {
9251
9293
  recorded = await recoverWorktree(repo, id);
9252
9294
  recovered = true;
9253
9295
  } else if (descriptor.followUp && cfg.limits.reuseWorktree !== false) {
@@ -9277,7 +9319,7 @@ async function main(activeDir, lane, seams = {}) {
9277
9319
  throw new Error(`worktree at ${cwd} is in use by ${held.id} \u2014 wait for that dispatch to release it before retrying`);
9278
9320
  if (recovered) {
9279
9321
  await deps.prepareReuse(repo, cwd);
9280
- fs84.writeFileSync(wtFile, JSON.stringify({ path: cwd }, null, 2));
9322
+ fs85.writeFileSync(wtFile, JSON.stringify({ path: cwd }, null, 2));
9281
9323
  }
9282
9324
  } else if (choice3?.reuse) {
9283
9325
  cwd = choice3.path;
@@ -9289,13 +9331,13 @@ async function main(activeDir, lane, seams = {}) {
9289
9331
  type: "runner",
9290
9332
  data: { worktree: cwd, worktreeOf: choice3.owner, setupRan }
9291
9333
  });
9292
- fs84.writeFileSync(wtFile, JSON.stringify({ path: cwd, of: choice3.owner }, null, 2));
9334
+ fs85.writeFileSync(wtFile, JSON.stringify({ path: cwd, of: choice3.owner }, null, 2));
9293
9335
  } else {
9294
9336
  cwd = await deps.allocate(repo, id, descriptor.systemRepair ? descriptor.pr?.headRefName : void 0);
9295
9337
  acquireWorktreeLock(cwd, id, lane);
9296
- fs84.writeFileSync(wtFile, JSON.stringify({ path: cwd }, null, 2));
9338
+ fs85.writeFileSync(wtFile, JSON.stringify({ path: cwd }, null, 2));
9297
9339
  }
9298
- const worktreeOf = JSON.parse(fs84.readFileSync(wtFile, "utf8")).of;
9340
+ const worktreeOf = JSON.parse(fs85.readFileSync(wtFile, "utf8")).of;
9299
9341
  mergeEvidence(id, lane, { worktree: cwd, worktreeOf });
9300
9342
  const existingPr = boundPr(descriptor, lane);
9301
9343
  if (existingPr)
@@ -9330,13 +9372,13 @@ async function main(activeDir, lane, seams = {}) {
9330
9372
  const planNudge = plan.cold && !envNudge ? coldNote(plan.cold, cwd, repo.trunk) : void 0;
9331
9373
  const promptWith = (nudge) => buildPrompt(brief, { id, nudge, attachments: descriptor.attachments, existingPr });
9332
9374
  const binDir = path70.resolve(path70.dirname(fileURLToPath5(import.meta.url)), "..", "..", "..", "bin");
9333
- const workerPath = fs84.existsSync(path70.join(binDir, "lobstah")) ? `${binDir}${path70.delimiter}${process.env.PATH ?? ""}` : void 0;
9375
+ const workerPath = fs85.existsSync(path70.join(binDir, "lobstah")) ? `${binDir}${path70.delimiter}${process.env.PATH ?? ""}` : void 0;
9334
9376
  let wallClockHit = false;
9335
9377
  let current;
9336
9378
  const wallFile = path70.join(activeDir, "wallclock.json");
9337
9379
  const wallState = (() => {
9338
9380
  try {
9339
- return JSON.parse(fs84.readFileSync(wallFile, "utf8"));
9381
+ return JSON.parse(fs85.readFileSync(wallFile, "utf8"));
9340
9382
  } catch {
9341
9383
  return { elapsedMs: 0, windowMs: resolved.limits.wallClockSecs * 1e3 };
9342
9384
  }
@@ -9352,8 +9394,8 @@ async function main(activeDir, lane, seams = {}) {
9352
9394
  const maxWallMs = Math.max(resolved.limits.wallClockSecs ?? 0, cfg.limits.maxWallClockSecs ?? (resolved.limits.wallClockSecs ?? 0) * 4) * 1e3;
9353
9395
  const persistWall = (elapsedMs, windowMs) => {
9354
9396
  const tmp = `${wallFile}.tmp-${process.pid}`;
9355
- fs84.writeFileSync(tmp, JSON.stringify({ ...wallState, elapsedMs, windowMs }));
9356
- fs84.renameSync(tmp, wallFile);
9397
+ fs85.writeFileSync(tmp, JSON.stringify({ ...wallState, elapsedMs, windowMs }));
9398
+ fs85.renameSync(tmp, wallFile);
9357
9399
  };
9358
9400
  const madeProgress = () => {
9359
9401
  const activity = readActivity(id, lane);
@@ -9647,7 +9689,7 @@ __export(dist_exports, {
9647
9689
  startedBy: () => startedBy,
9648
9690
  stopPids: () => stopPids
9649
9691
  });
9650
- import * as fs85 from "node:fs";
9692
+ import * as fs86 from "node:fs";
9651
9693
  import * as path71 from "node:path";
9652
9694
  function runRunner(activeDirArg2, laneArg2) {
9653
9695
  const lane = laneArg2 === "chore" ? "chore" : "work";
@@ -9657,7 +9699,7 @@ function runRunner(activeDirArg2, laneArg2) {
9657
9699
  if (!isFinished(id, lane)) {
9658
9700
  appendStatus(id, lane, "failed", String(err instanceof Error ? err.message : err).slice(0, 500));
9659
9701
  }
9660
- fs85.renameSync(activeDirArg2, path71.join(laneDirs(lane).done, id));
9702
+ fs86.renameSync(activeDirArg2, path71.join(laneDirs(lane).done, id));
9661
9703
  } catch {
9662
9704
  }
9663
9705
  process.exitCode = 1;
@@ -9685,7 +9727,7 @@ var init_dist5 = __esm({
9685
9727
  // apps/cli/src/main.ts
9686
9728
  init_dist2();
9687
9729
  init_dist3();
9688
- import * as fs86 from "node:fs";
9730
+ import * as fs87 from "node:fs";
9689
9731
  import * as os13 from "node:os";
9690
9732
  import * as path72 from "node:path";
9691
9733
  import { randomUUID as randomUUID8 } from "node:crypto";
@@ -11959,7 +12001,10 @@ function pruneStaleAcks(standing) {
11959
12001
  return pruned;
11960
12002
  }
11961
12003
  function ackItemExists(key, culling = /* @__PURE__ */ new Set()) {
11962
- if (key.startsWith("stack:")) return readPr(key.slice("stack:".length))?.state === "OPEN";
12004
+ if (key.startsWith("stack:")) {
12005
+ const stack = readStackEpochs()[key.slice("stack:".length)];
12006
+ return stack ? Object.keys(stack.heads).some((pr) => readPr(pr)?.state === "OPEN") : readPr(key.slice("stack:".length))?.state === "OPEN";
12007
+ }
11963
12008
  if (key.startsWith("watch:")) return readWatch(key.slice("watch:".length)) !== void 0;
11964
12009
  if (key.startsWith("decision:")) return readDecision(key) !== void 0 && readDecisionAnswer(key) === void 0;
11965
12010
  if (key.startsWith("report:")) {
@@ -12056,6 +12101,15 @@ function reportAck(r) {
12056
12101
  const ack = currentAck(r.key, r.stateHash);
12057
12102
  return ack ? { at: ack.at, by: ack.by } : void 0;
12058
12103
  }
12104
+ function viewReport(key, now2 = /* @__PURE__ */ new Date()) {
12105
+ const r = readReport(key);
12106
+ if (!r) return void 0;
12107
+ const held = currentAck(r.key, r.stateHash);
12108
+ if (held) return { key: r.key, viewedAt: held.at, by: held.by, first: false };
12109
+ const ack = { key: r.key, kind: "report", stateHash: r.stateHash, at: now2.toISOString(), by: "glass" };
12110
+ writeAck(ack);
12111
+ return { key: r.key, viewedAt: ack.at, by: ack.by, first: true };
12112
+ }
12059
12113
  function reportRows() {
12060
12114
  return listReports().map((r) => ({
12061
12115
  key: r.key,
@@ -12331,7 +12385,7 @@ function deriveGlassPrs(dispatches, watches = [], records = [], stackOptions = {
12331
12385
  for (const d of dispatches) if (d.pr && !recordByUrl.has(d.pr.url) && (!evidenceByUrl.has(d.pr.url) || evidenceByUrl.get(d.pr.url).observedAt < d.pr.observedAt)) {
12332
12386
  evidenceByUrl.set(d.pr.url, d.pr);
12333
12387
  }
12334
- const readiness = derivePrStacks([...evidenceByUrl.values()], stackOptions);
12388
+ const readiness = identifyPrStacks(derivePrStacks([...evidenceByUrl.values()], stackOptions));
12335
12389
  for (const [id, members] of groupedStacks) {
12336
12390
  const root = members.find((p) => p.key === id);
12337
12391
  const ordered = [];
@@ -12364,6 +12418,8 @@ function deriveGlassPrs(dispatches, watches = [], records = [], stackOptions = {
12364
12418
  });
12365
12419
  const status = readiness.find((s) => s.members.some((p) => members.some((m) => p.url === m.url)));
12366
12420
  if (status) stacks.at(-1).readiness = {
12421
+ id: status.id,
12422
+ url: status.members.at(-1).url,
12367
12423
  ready: status.ready,
12368
12424
  total: status.members.length,
12369
12425
  allReady: status.allReady,
@@ -12723,6 +12779,15 @@ function progressStreak(pr) {
12723
12779
  if (!own && s.startHead !== void 0 && s.startHead !== pr.headSha) return { count: 0 };
12724
12780
  return { count: s.count + 1 };
12725
12781
  }
12782
+ function completedRepair(pr, now2) {
12783
+ const id = pr.repairCooldown?.dispatchId ?? pr.repairStreak?.lastRepairId ?? pr.repair?.dispatchId;
12784
+ if (!id || pr.repairCooldown?.endedAt || !isTerminal2(id)) return pr;
12785
+ const last = readStatusLog(id, laneOf(id) ?? "chore").at(-1);
12786
+ const at = last && ["done", "failed"].includes(last.verb) ? Date.parse(last.at) : NaN;
12787
+ const after = { ...pr, repairCooldown: { ...pr.repairCooldown, dispatchId: id, endedAt: new Date(Number.isFinite(at) ? at : now2).toISOString() } };
12788
+ writePr(after);
12789
+ return after;
12790
+ }
12726
12791
  function stackedAbove(pr, records, workers) {
12727
12792
  const out = [];
12728
12793
  for (const p of prsAbove(records, pr.key)) {
@@ -12759,8 +12824,9 @@ function deliverPrRepairs(log, cap = 3, opts = {}) {
12759
12824
  }
12760
12825
  const watch2 = w;
12761
12826
  withPrLock(watch2.key, () => {
12762
- const pr = readPr(watch2.key);
12827
+ let pr = readPr(watch2.key);
12763
12828
  if (!pr || pr.state !== "OPEN" || pr.dispatches.length === 0 || (pr.observations ?? 0) <= 1) return;
12829
+ pr = completedRepair(pr, now2);
12764
12830
  const owner = watch2.owner.slice("dispatch:".length);
12765
12831
  const origin = storedDescriptor(owner, "work")?.followUp;
12766
12832
  const ancestorPr = origin && chainPr(origin, "work");
@@ -12906,9 +12972,16 @@ function deliverPrRepairs(log, cap = 3, opts = {}) {
12906
12972
  log(`repair ${watch2.key}: stopped \u2014 ${reason}`);
12907
12973
  return;
12908
12974
  }
12975
+ const end = Date.parse(pr.repairCooldown?.endedAt ?? "");
12976
+ const cooldownUntil = end + cfg.repairCooldownSecs * 1e3;
12977
+ if (cfg.repairCooldownSecs > 0 && !pr.repairCooldown?.released && now2 < cooldownUntil) {
12978
+ const until2 = new Date(cooldownUntil).toISOString();
12979
+ return wait({ heldBy: "cooldown", reason: `cooldown until ${until2}` }, until2);
12980
+ }
12909
12981
  const id = randomUUID6();
12910
12982
  writePr({
12911
12983
  ...pr,
12984
+ repairCooldown: { dispatchId: id },
12912
12985
  repairStreak: { count: streak.count, lastRepairId: id, startHead: pr.headSha },
12913
12986
  repair: {
12914
12987
  headSha: pr.headSha,
@@ -13145,36 +13218,12 @@ function prAttention(now2, observed2 = observedPrs(), cfg = loadConfig()) {
13145
13218
  Object.values(readPickupMap()).filter((e) => e.kind === "review").map((e) => e.uuid)
13146
13219
  );
13147
13220
  const out = [];
13148
- const stacks = derivePrStacks(observed2.map((x) => x.pr), {
13221
+ const stacks = identifyPrStacks(derivePrStacks(observed2.map((x) => x.pr), {
13149
13222
  trunk: (repo) => prStackTrunk(cfg, repo),
13150
13223
  readySettleSecs: cfg.readySettleSecs,
13151
13224
  now: now2
13152
- });
13225
+ }));
13153
13226
  const epochs = readStackEpochs();
13154
- const quietReady = /* @__PURE__ */ new Set([
13155
- ...stacks.flatMap((s) => s.members.map((p) => p.url)),
13156
- ...quietStackTailUrls(observed2.map((x) => x.pr), epochs)
13157
- ]);
13158
- for (const s of stacks.filter((s2) => s2.allReady)) {
13159
- const top = s.members.at(-1), ref = parsePrRef(top.url);
13160
- const epoch = epochs[s.id];
13161
- const at = epoch?.ready ? epoch.at : new Date(now2).toISOString();
13162
- const source = observed2.find((x) => x.pr.url === top.url);
13163
- out.push({
13164
- kind: "stack-ready",
13165
- key: `stack:${s.id}`,
13166
- stateHash: stackStateHash(s, epoch?.epoch),
13167
- id: source.id,
13168
- lane: source.lane,
13169
- verb: "stack-ready",
13170
- at,
13171
- standingSince: at,
13172
- ageSecs: Math.max(0, Math.round((now2 - Date.parse(at)) / 1e3)),
13173
- note: s.text,
13174
- repo: source.dispatch ? repoOf(source.id, source.lane) : repoKey(cfg, s.repo),
13175
- prUrl: ref.url
13176
- });
13177
- }
13178
13227
  for (const { id, lane, pr, dispatch } of observed2) {
13179
13228
  const kinds = prKinds(pr, { readySettleSecs: cfg.readySettleSecs, now: now2 });
13180
13229
  if (kinds.length === 0) continue;
@@ -13183,6 +13232,10 @@ function prAttention(now2, observed2 = observedPrs(), cfg = loadConfig()) {
13183
13232
  const watchFollowUp = prWatch?.lastFollowUpId;
13184
13233
  const chain = dispatch ? chainOf(id) : [];
13185
13234
  for (const kind of kinds) {
13235
+ if (kind === "pr:checks" && stacks.some((s) => s.members.some((p) => p.url === pr.url))) {
13236
+ const gates = ["Approval Gate", ...humanGatesFor(readPr(pr.url), ref ? repoKey(cfg, `${ref.owner}/${ref.repo}`) : void 0)];
13237
+ if (pr.failingChecks?.length === pr.checks.failed && pr.failingChecks.every((c) => matchesGate(c.name, gates))) continue;
13238
+ }
13186
13239
  const human = humanPrAttention(pr, kind, cfg, !!prWatch && !prWatch.done, now2);
13187
13240
  if (!human.show) continue;
13188
13241
  const repairCannotAct = human.reason !== void 0 && !["auto-repair is off", "not owned by lobstah", "no active PR watch"].includes(human.reason);
@@ -13191,7 +13244,6 @@ function prAttention(now2, observed2 = observedPrs(), cfg = loadConfig()) {
13191
13244
  const standingSince = kind === "pr:ready" ? new Date(Date.parse(conditionSince) + cfg.readySettleSecs * 1e3).toISOString() : conditionSince;
13192
13245
  out.push({
13193
13246
  kind,
13194
- ...kind === "pr:ready" && quietReady.has(pr.url) ? { quiet: true } : {},
13195
13247
  key: ref?.key ?? pr.url,
13196
13248
  stateHash: prStateHash(pr),
13197
13249
  id,
@@ -13214,6 +13266,38 @@ function prAttention(now2, observed2 = observedPrs(), cfg = loadConfig()) {
13214
13266
  });
13215
13267
  }
13216
13268
  }
13269
+ for (const s of stacks) {
13270
+ const members = s.members.map((pr) => {
13271
+ const items = out.filter((a) => a.prUrl === pr.url);
13272
+ return {
13273
+ key: parsePrRef(pr.url).key,
13274
+ url: pr.url,
13275
+ number: pr.number,
13276
+ note: items.map((a) => a.note).join("; ") || `#${pr.number} ${prBadge(pr).text}`,
13277
+ kinds: items.map((a) => a.kind)
13278
+ };
13279
+ });
13280
+ const human = out.filter((a) => s.members.some((p) => p.url === a.prUrl) && a.kind !== "pr:ready" && a.kind !== "pr:draft" && cfg.attentionKinds.includes(a.kind));
13281
+ for (let i = out.length - 1; i >= 0; i--) if (s.members.some((p) => p.url === out[i].prUrl)) out.splice(i, 1);
13282
+ const top = s.members.at(-1), source = observed2.find((x) => x.pr.url === top.url);
13283
+ const at = epochs[s.id]?.at ?? s.members[0].observedAt;
13284
+ out.push({
13285
+ kind: "stack-ready",
13286
+ key: `stack:${s.id}`,
13287
+ stateHash: human.length ? statusStateHash(stackStateHash(s, epochs[s.id]?.epoch), JSON.stringify(human.map((a) => [a.key, a.kind, a.stateHash]))) : stackStateHash(s, epochs[s.id]?.epoch),
13288
+ id: source.id,
13289
+ lane: source.lane,
13290
+ verb: "stack-ready",
13291
+ at,
13292
+ standingSince: at,
13293
+ ageSecs: Math.max(0, Math.round((now2 - Date.parse(at)) / 1e3)),
13294
+ note: s.text,
13295
+ quiet: !s.allReady && human.length === 0 || !!epochs[s.id]?.silent || !!s.legacy,
13296
+ stack: { ready: s.ready, total: members.length, allReady: s.allReady, members },
13297
+ repo: repoKey(cfg, s.repo),
13298
+ prUrl: top.url
13299
+ });
13300
+ }
13217
13301
  return out;
13218
13302
  }
13219
13303
  function landedAttention(cfg, now2) {
@@ -13545,8 +13629,10 @@ function buildTendReport(now2 = Date.now()) {
13545
13629
  { readySettleSecs: cfg.readySettleSecs, now: now2, trunk: (r) => prStackTrunk(cfg, r) }
13546
13630
  ).stacks.filter((s) => s.open);
13547
13631
  attention.push(...landedAttention(cfg, now2), ...prAttention(now2, observed2, cfg), ...reportAttention(now2), ...decisionAttention(now2));
13632
+ const stackKeys = new Set(attention.flatMap((a) => a.stack?.members.map((m) => `watch:${m.key}`) ?? []));
13633
+ for (let i = attention.length - 1; i >= 0; i--) if (attention[i].kind === "watch" && stackKeys.has(attention[i].key)) attention.splice(i, 1);
13548
13634
  const enabled = new Set(cfg.attentionKinds);
13549
- const walking = attention.filter((a) => a.kind === "watch" || (a.kind === "stack-ready" ? stackReadyEnabled(cfg) : enabled.has(a.kind)));
13635
+ const walking = attention.filter((a) => a.kind === "watch" || (a.kind === "stack-ready" ? stackReadyEnabled(cfg) || a.stack?.members.some((m) => m.kinds.some((kind) => enabled.has(kind))) : enabled.has(a.kind)));
13550
13636
  const shown = enabled.has("decision") ? hideFramedQuestions(walking) : walking;
13551
13637
  attention.length = 0;
13552
13638
  attention.push(
@@ -13589,7 +13675,8 @@ function buildTendReport(now2 = Date.now()) {
13589
13675
  const notices = listNotices(5).map((n) => ({
13590
13676
  kind: n.kind,
13591
13677
  ageMins: Math.max(0, Math.round((now2 - (Date.parse(n.at) || 0)) / 6e4)),
13592
- text: n.text
13678
+ text: n.text,
13679
+ ...n.url ? { url: n.url } : {}
13593
13680
  }));
13594
13681
  const helms = listHelms().map((h) => ({
13595
13682
  grounds: h.grounds,
@@ -13662,7 +13749,9 @@ function renderTend(r) {
13662
13749
  if (r.queueWait) lines.push(r.queueWait);
13663
13750
  for (const stack of r.stacks) {
13664
13751
  lines.push(`stack ${stack.numbers.map((n) => `#${n}`).join(" \u2192 ")}: next ${stack.nextNumber ? `#${stack.nextNumber}` : "none"}`);
13665
- if (stack.readiness) lines.push(stack.readiness.text);
13752
+ if (stack.readiness) lines.push(`${stack.readiness.text} ${stack.readiness.url}`);
13753
+ const item = r.attention.find((a) => a.key === `stack:${stack.readiness?.id}`);
13754
+ if (item?.stack) for (const member of item.stack.members) lines.push(` ${member.note} ${member.url}`);
13666
13755
  }
13667
13756
  if (r.attention.length > 0) {
13668
13757
  lines.push("");
@@ -13674,8 +13763,9 @@ function renderTend(r) {
13674
13763
  verb: a.kind === "question" || a.kind === "watch" ? a.verb : a.kind === "landed" ? `landed (${a.verb})` : a.kind,
13675
13764
  waitingMins: Math.round(a.ageSecs / 60),
13676
13765
  held: a.held ? "yes" : "",
13677
- // Whether the human has opened it in the glass's decision modal.
13678
- viewed: a.kind === "decision" || a.kind === "question" ? a.viewedAt ? `yes ${a.viewedAt}` : "no" : "",
13766
+ // Whether the human has opened it in the glass's decision modal,
13767
+ // or acked a report (opening its page acks it).
13768
+ viewed: a.kind === "decision" || a.kind === "question" ? a.viewedAt ? `yes ${a.viewedAt}` : "no" : a.kind === "report" ? a.acked ? `yes ${a.acked.at}` : "no" : "",
13679
13769
  note: named(a.prUrl ? `${a.note ?? ""} ${a.prUrl}`.trim() : a.note ?? "")
13680
13770
  })),
13681
13771
  ["id", "verb", "waitingMins", "held", "viewed", "note"]
@@ -13791,7 +13881,7 @@ function renderTend(r) {
13791
13881
  lines.push(
13792
13882
  toonTable(
13793
13883
  "notices (recent)",
13794
- r.notices.map((n) => ({ kind: n.kind, ageMins: n.ageMins, text: n.text })),
13884
+ r.notices.map((n) => ({ kind: n.kind, ageMins: n.ageMins, text: `${n.text}${n.url ? ` ${n.url}` : ""}` })),
13795
13885
  ["kind", "ageMins", "text"]
13796
13886
  )
13797
13887
  );
@@ -13878,7 +13968,7 @@ function buildDigest(opts = {}) {
13878
13968
  landed,
13879
13969
  arisen,
13880
13970
  standing,
13881
- stacks: tend.stacks.filter((s) => inGrounds(repoKey(loadConfig(), s.repo)) && s.readiness).map((s) => s.readiness.text),
13971
+ stacks: tend.stacks.filter((s) => inGrounds(repoKey(loadConfig(), s.repo)) && s.readiness).map((s) => `${s.readiness.text} ${s.readiness.url}`),
13882
13972
  traps,
13883
13973
  verdict: tend.verdict,
13884
13974
  counts: tend.counts
@@ -14390,13 +14480,82 @@ ${help}`;
14390
14480
 
14391
14481
  // apps/cli/src/cull.ts
14392
14482
  init_dist2();
14393
- import * as fs59 from "node:fs";
14483
+ import * as fs60 from "node:fs";
14394
14484
  import * as path52 from "node:path";
14485
+ import { execFileSync as execFileSync8 } from "node:child_process";
14486
+
14487
+ // apps/cli/src/merged-worktree-safety.ts
14488
+ init_dist2();
14489
+ init_dist3();
14395
14490
  import { execFileSync as execFileSync7 } from "node:child_process";
14491
+ import * as fs59 from "node:fs";
14492
+ var git7 = (dir, ...args) => {
14493
+ try {
14494
+ return execFileSync7("git", args, { cwd: dir, encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] }).trim();
14495
+ } catch {
14496
+ return void 0;
14497
+ }
14498
+ };
14499
+ function mergedWorktreeUnsafe(dir, heads, trunk, fetched) {
14500
+ const status = git7(dir, "status", "--porcelain", "--untracked-files=all");
14501
+ if (status === void 0) return "unknown (not a readable git checkout; retry next pass)";
14502
+ if (status) return `dirty (${status.split("\n").length} files)`;
14503
+ const head = git7(dir, "rev-parse", "HEAD");
14504
+ const recorded = [...new Set(heads.filter((s) => /^[a-f0-9]{40,64}$/i.test(s)))];
14505
+ if (head && recorded.includes(head)) return void 0;
14506
+ let available = recorded.filter((s) => git7(dir, "cat-file", "-t", s) === "commit");
14507
+ const count = () => {
14508
+ const base = git7(dir, "rev-parse", "--verify", `refs/remotes/origin/${trunk}^{commit}`);
14509
+ const exclusions = [...available, ...base ? [base] : []];
14510
+ if (!exclusions.length) return void 0;
14511
+ const n2 = git7(dir, "rev-list", "--count", "HEAD", "--not", ...exclusions);
14512
+ return n2 === void 0 ? void 0 : Number(n2);
14513
+ };
14514
+ if (count() === 0) return void 0;
14515
+ const common = git7(dir, "rev-parse", "--path-format=absolute", "--git-common-dir") ?? dir;
14516
+ if (!fetched.has(common)) fetched.set(common, withRepoLockSync(dir, () => git7(dir, "fetch", "--quiet", "origin")) !== void 0);
14517
+ available = recorded.filter((s) => git7(dir, "cat-file", "-t", s) === "commit");
14518
+ if (count() === 0) return void 0;
14519
+ if (!fetched.get(common)) return "unknown (fetch failed, remote state unknown; retry next pass)";
14520
+ const n = count();
14521
+ if (n !== void 0 && recorded.length > 0 && available.length === recorded.length) return `ahead of PR head (${n} commits)`;
14522
+ return "unknown (recorded PR heads unavailable; retry next pass)";
14523
+ }
14524
+ function mergedCullProofs() {
14525
+ const proofs = /* @__PURE__ */ new Map();
14526
+ const records = readPrs();
14527
+ const config = loadConfig();
14528
+ for (const lane of ["work", "chore"]) {
14529
+ const done = laneDirs(lane).done;
14530
+ for (const id of fs59.readdirSync(done).filter((s) => !s.startsWith("."))) {
14531
+ const owner = dispatchWorktree(id, lane).owner;
14532
+ const ev = readEvidence(id, lane);
14533
+ const lineage = /* @__PURE__ */ new Set([id, ...followUpAncestors(id, lane)]);
14534
+ const prs = records.filter((r) => r.dispatches?.some((d) => lineage.has(d)) && (r.dispatches.includes(id) || ev.branch === r.headRefName));
14535
+ for (const url of dispatchPrUrls(ev)) {
14536
+ const ref = parsePrRef(url);
14537
+ if (!ref) continue;
14538
+ const r = readPr(ref.key) ?? (ev.pr?.url === url ? { ...ev.pr, key: ref.key, observedHeadShas: [] } : void 0);
14539
+ if (r && !prs.some((p) => p.key === r.key)) prs.push(r);
14540
+ }
14541
+ const proof = proofs.get(owner) ?? { heads: [], trunk: config.repos[storedDescriptor(id, lane)?.repo ?? ""]?.trunk ?? "main", keys: /* @__PURE__ */ new Set(), users: /* @__PURE__ */ new Set() };
14542
+ for (const r of prs) {
14543
+ if (r.state !== "MERGED") continue;
14544
+ proof.heads.push(...r.observedHeadShas ?? [], r.headSha);
14545
+ proof.keys.add(r.key);
14546
+ }
14547
+ proof.users.add(id);
14548
+ proofs.set(owner, proof);
14549
+ }
14550
+ }
14551
+ return new Map([...proofs].filter(([, p]) => p.keys.size > 0));
14552
+ }
14553
+
14554
+ // apps/cli/src/cull.ts
14396
14555
  var DAY = 864e5;
14397
14556
  function walkBytes(target) {
14398
- const stat = fs59.lstatSync(target);
14399
- if (stat.isDirectory()) return fs59.readdirSync(target).reduce((sum, name) => sum + walkBytes(path52.join(target, name)), 0);
14557
+ const stat = fs60.lstatSync(target);
14558
+ if (stat.isDirectory()) return fs60.readdirSync(target).reduce((sum, name) => sum + walkBytes(path52.join(target, name)), 0);
14400
14559
  return stat.isFile() ? stat.size : 0;
14401
14560
  }
14402
14561
  var sizing = {
@@ -14411,7 +14570,7 @@ var sizing = {
14411
14570
  worktree(target, platform = process.platform) {
14412
14571
  if (platform !== "win32") {
14413
14572
  try {
14414
- const out = execFileSync7("du", ["-sk", target], { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] });
14573
+ const out = execFileSync8("du", ["-sk", target], { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] });
14415
14574
  const kb = Number(out.trim().split(/\s+/)[0]);
14416
14575
  if (Number.isFinite(kb)) return kb * 1024;
14417
14576
  } catch {
@@ -14422,7 +14581,7 @@ var sizing = {
14422
14581
  };
14423
14582
  function idsIn(dir) {
14424
14583
  try {
14425
- return new Set(fs59.readdirSync(dir).filter((f) => !f.startsWith(".")).map((f) => f.replace(/\.json$/, "")));
14584
+ return new Set(fs60.readdirSync(dir).filter((f) => !f.startsWith(".")).map((f) => f.replace(/\.json$/, "")));
14426
14585
  } catch {
14427
14586
  return /* @__PURE__ */ new Set();
14428
14587
  }
@@ -14458,7 +14617,7 @@ function planCull(olderThanDays, now2 = Date.now(), opts = {}) {
14458
14617
  retainReferences(id, lane);
14459
14618
  continue;
14460
14619
  }
14461
- const m = fs59.statSync(p).mtimeMs;
14620
+ const m = fs60.statSync(p).mtimeMs;
14462
14621
  doneMtimes.set(id, m);
14463
14622
  if (m >= cutoff) retainReferences(id, lane);
14464
14623
  if (m < cutoff) items.push({ kind: "done", id, target: p, ageDays: Math.floor((now2 - m) / DAY), bytes: size(p), ageFrom: m });
@@ -14467,28 +14626,45 @@ function planCull(olderThanDays, now2 = Date.now(), opts = {}) {
14467
14626
  const wtRoot = path52.join(lobstahHome(), "worktrees");
14468
14627
  const trapped = trapWorktreeIds(wtRoot);
14469
14628
  const usage = worktreeUsage(opts.keepOpenPrs ? openPr : void 0);
14629
+ const proofs = mergedCullProofs();
14630
+ const fetched = /* @__PURE__ */ new Map();
14631
+ const keptUsers = /* @__PURE__ */ new Set();
14632
+ const keptPrs = /* @__PURE__ */ new Set();
14633
+ for (const [owner, proof] of proofs) if (fs60.existsSync(path52.join(wtRoot, owner))) for (const key of proof.keys) keptPrs.add(key);
14470
14634
  for (const id of idsIn(wtRoot)) {
14471
14635
  if (live.has(id) || trapped.has(id) || usage.live.has(id)) continue;
14472
14636
  const doneAt = usage.newest.get(id) ?? doneMtimes.get(id);
14473
14637
  if (doneAt !== void 0 && doneAt >= cutoff) continue;
14474
14638
  const p = path52.join(wtRoot, id);
14639
+ const proof = proofs.get(id);
14640
+ if (proof && mergedWorktreeUnsafe(p, proof.heads, proof.trunk, fetched)) {
14641
+ for (const user of proof.users) {
14642
+ live.add(user);
14643
+ keptUsers.add(user);
14644
+ retainReferences(user, "work");
14645
+ retainReferences(user, "chore");
14646
+ }
14647
+ for (const key of proof.keys) keptPrs.add(key);
14648
+ continue;
14649
+ }
14475
14650
  if (readTrapAnchor(p)?.createdBy === "soak" && inspectTrapWorktree(p).reason) continue;
14476
- const from = doneAt ?? fs59.statSync(p).mtimeMs;
14651
+ const from = doneAt ?? fs60.statSync(p).mtimeMs;
14477
14652
  items.push({ kind: "worktree", id, target: p, ageDays: Math.floor((now2 - from) / DAY), bytes: measure ? sizing.worktree(p) : 0, ageFrom: from });
14478
14653
  }
14654
+ for (let i = items.length - 1; i >= 0; i--) if (items[i].kind === "done" && keptUsers.has(items[i].id)) items.splice(i, 1);
14479
14655
  for (const lane of ["work", "chore"]) {
14480
14656
  const d = laneDirs(lane);
14481
14657
  const groups = /* @__PURE__ */ new Map();
14482
- for (const f of fs59.readdirSync(d.state)) {
14658
+ for (const f of fs60.readdirSync(d.state)) {
14483
14659
  const target = path52.join(d.state, f);
14484
- const directory = fs59.statSync(target).isDirectory();
14485
- if (directory && !fs59.existsSync(path52.join(target, "attachments")) && !fs59.existsSync(path52.join(target, "report.json"))) continue;
14660
+ const directory = fs60.statSync(target).isDirectory();
14661
+ if (directory && !fs60.existsSync(path52.join(target, "attachments")) && !fs60.existsSync(path52.join(target, "report.json"))) continue;
14486
14662
  const id = directory ? f : f.replace(/\.(status|events|evidence|attn|notified|runner\.log)$/, "");
14487
14663
  if (id === f && !directory) continue;
14488
14664
  if (live.has(id) || (doneMtimes.get(id) ?? 0) >= cutoff || referencedAttachmentState.has(path52.join(d.state, id))) continue;
14489
14665
  const previous = groups.get(id);
14490
14666
  groups.set(id, {
14491
- mtime: Math.max(previous?.mtime ?? 0, fs59.statSync(target).mtimeMs),
14667
+ mtime: Math.max(previous?.mtime ?? 0, fs60.statSync(target).mtimeMs),
14492
14668
  bytes: (previous?.bytes ?? 0) + size(target)
14493
14669
  });
14494
14670
  }
@@ -14499,7 +14675,7 @@ function planCull(olderThanDays, now2 = Date.now(), opts = {}) {
14499
14675
  }
14500
14676
  }
14501
14677
  for (const r of readPrs()) {
14502
- if (r.state === "OPEN") continue;
14678
+ if (r.state === "OPEN" || keptPrs.has(r.key)) continue;
14503
14679
  const seen = Date.parse(r.observedAt) || now2;
14504
14680
  if (seen < cutoff) items.push({ kind: "pr", id: r.key, target: r.key, ageDays: Math.floor((now2 - seen) / DAY), bytes: size(prRecordFile(r.key)), ageFrom: seen });
14505
14681
  }
@@ -14559,7 +14735,7 @@ function worktreeUsage(openPr) {
14559
14735
  if (openPr?.has(id)) live.add(owner);
14560
14736
  let m;
14561
14737
  try {
14562
- m = fs59.statSync(path52.join(d.done, id)).mtimeMs;
14738
+ m = fs60.statSync(path52.join(d.done, id)).mtimeMs;
14563
14739
  } catch {
14564
14740
  continue;
14565
14741
  }
@@ -14573,7 +14749,7 @@ function trapWorktreeIds(wtRoot) {
14573
14749
  const real = (p) => {
14574
14750
  let r;
14575
14751
  try {
14576
- r = fs59.realpathSync.native(p);
14752
+ r = fs60.realpathSync.native(p);
14577
14753
  } catch {
14578
14754
  r = path52.resolve(p);
14579
14755
  }
@@ -14602,13 +14778,15 @@ function limitBatch(items, maxGroups) {
14602
14778
  return { batch: items.filter((i) => keep.has(groupOf(i))), deferred: order.length - keep.size };
14603
14779
  }
14604
14780
  function planPressureCull(now2 = Date.now()) {
14781
+ const proofs = mergedCullProofs();
14782
+ const fetched = /* @__PURE__ */ new Map();
14605
14783
  const live = /* @__PURE__ */ new Set();
14606
14784
  const doneMtimes = /* @__PURE__ */ new Map();
14607
14785
  for (const lane of ["work", "chore"]) {
14608
14786
  const d = laneDirs(lane);
14609
14787
  for (const id of idsIn(d.queue)) live.add(id);
14610
14788
  for (const id of idsIn(d.active)) live.add(id);
14611
- for (const id of idsIn(d.done)) doneMtimes.set(id, fs59.statSync(path52.join(d.done, id)).mtimeMs);
14789
+ for (const id of idsIn(d.done)) doneMtimes.set(id, fs60.statSync(path52.join(d.done, id)).mtimeMs);
14612
14790
  }
14613
14791
  const openPr = openPrDispatches();
14614
14792
  for (const id of openPr) live.add(id);
@@ -14620,8 +14798,10 @@ function planPressureCull(now2 = Date.now()) {
14620
14798
  for (const id of idsIn(wtRoot)) {
14621
14799
  if (live.has(id)) continue;
14622
14800
  const p = path52.join(wtRoot, id);
14801
+ const proof = proofs.get(id);
14802
+ if (proof && mergedWorktreeUnsafe(p, proof.heads, proof.trunk, fetched)) continue;
14623
14803
  if (readTrapAnchor(p)?.createdBy === "soak" && inspectTrapWorktree(p).reason) continue;
14624
- const from = usage.newest.get(id) ?? doneMtimes.get(id) ?? fs59.statSync(p).mtimeMs;
14804
+ const from = usage.newest.get(id) ?? doneMtimes.get(id) ?? fs60.statSync(p).mtimeMs;
14625
14805
  items.push({ kind: "worktree", id, target: p, ageDays: Math.floor((now2 - from) / DAY), bytes: 0, ageFrom: from });
14626
14806
  }
14627
14807
  return items.sort((a, b) => a.ageFrom - b.ageFrom || a.id.localeCompare(b.id));
@@ -14635,28 +14815,28 @@ function removeWorktree(id, dir) {
14635
14815
  for (const lane of ["work", "chore"]) {
14636
14816
  const descFile = path52.join(laneDirs(lane).done, id, "descriptor.json");
14637
14817
  try {
14638
- const desc = JSON.parse(fs59.readFileSync(descFile, "utf8"));
14818
+ const desc = JSON.parse(fs60.readFileSync(descFile, "utf8"));
14639
14819
  const repo = loadConfig().repos[desc.repo];
14640
14820
  if (repo) {
14641
- execFileSync7("git", ["worktree", "remove", "--force", dir], { cwd: repo.path, stdio: "ignore" });
14821
+ execFileSync8("git", ["worktree", "remove", "--force", dir], { cwd: repo.path, stdio: "ignore" });
14642
14822
  return;
14643
14823
  }
14644
14824
  } catch {
14645
14825
  }
14646
14826
  }
14647
14827
  try {
14648
- const common = execFileSync7("git", ["rev-parse", "--path-format=absolute", "--git-common-dir"], {
14828
+ const common = execFileSync8("git", ["rev-parse", "--path-format=absolute", "--git-common-dir"], {
14649
14829
  cwd: dir,
14650
14830
  encoding: "utf8",
14651
14831
  stdio: ["ignore", "pipe", "ignore"]
14652
14832
  }).trim();
14653
14833
  if (common) {
14654
- execFileSync7("git", ["--git-dir", common, "worktree", "remove", "--force", dir], { stdio: "ignore" });
14834
+ execFileSync8("git", ["--git-dir", common, "worktree", "remove", "--force", dir], { stdio: "ignore" });
14655
14835
  return;
14656
14836
  }
14657
14837
  } catch {
14658
14838
  }
14659
- fs59.rmSync(dir, { recursive: true, force: true });
14839
+ fs60.rmSync(dir, { recursive: true, force: true });
14660
14840
  }
14661
14841
  function culledState(items) {
14662
14842
  const lanes = new Map(["work", "chore"].map((lane) => [path52.resolve(laneDirs(lane).state), lane]));
@@ -14668,7 +14848,7 @@ function culledState(items) {
14668
14848
  function applyCull(items) {
14669
14849
  foldCatches(culledState(items));
14670
14850
  for (const item of items.filter((i) => i.kind === "worktree")) removeWorktree(item.id, item.target);
14671
- for (const item of items.filter((i) => i.kind === "done")) fs59.rmSync(item.target, { recursive: true, force: true });
14851
+ for (const item of items.filter((i) => i.kind === "done")) fs60.rmSync(item.target, { recursive: true, force: true });
14672
14852
  for (const item of items.filter((i) => i.kind === "ack")) removeAck(item.target);
14673
14853
  for (const item of items.filter((i) => i.kind === "pr")) removePr(item.target);
14674
14854
  for (const item of items.filter((i) => i.kind === "report")) removeHelmReport(item.id);
@@ -14676,8 +14856,8 @@ function applyCull(items) {
14676
14856
  for (const item of items.filter((i) => i.kind === "release")) removeRelease(item.id);
14677
14857
  for (const item of items.filter((i) => i.kind === "state")) {
14678
14858
  const dir = path52.dirname(item.target);
14679
- for (const f of fs59.readdirSync(dir)) {
14680
- if (f === item.id || f.startsWith(`${item.id}.`)) fs59.rmSync(path52.join(dir, f), { recursive: true, force: true });
14859
+ for (const f of fs60.readdirSync(dir)) {
14860
+ if (f === item.id || f.startsWith(`${item.id}.`)) fs60.rmSync(path52.join(dir, f), { recursive: true, force: true });
14681
14861
  }
14682
14862
  }
14683
14863
  }
@@ -14746,13 +14926,11 @@ ${toonKV(statusFields(s))}${still}`;
14746
14926
 
14747
14927
  // apps/cli/src/release.ts
14748
14928
  init_dist2();
14749
- init_dist3();
14750
- import * as fs60 from "node:fs";
14751
- import { execFileSync as execFileSync8 } from "node:child_process";
14929
+ import * as fs61 from "node:fs";
14752
14930
  var DAY2 = 864e5;
14753
14931
  function listIds(dir) {
14754
14932
  try {
14755
- return fs60.readdirSync(dir).filter((f) => !f.startsWith(".")).map((f) => f.replace(/\.json$/, ""));
14933
+ return fs61.readdirSync(dir).filter((f) => !f.startsWith(".")).map((f) => f.replace(/\.json$/, ""));
14756
14934
  } catch {
14757
14935
  return [];
14758
14936
  }
@@ -14803,15 +14981,16 @@ function prUrlOf(ev) {
14803
14981
  }
14804
14982
  function mergedPrs(all) {
14805
14983
  const merged = /* @__PURE__ */ new Map();
14806
- const add2 = (url, key, headRef, id) => {
14807
- const m = merged.get(key) ?? { url, key, headRef, owners: /* @__PURE__ */ new Set() };
14984
+ const add2 = (url, key, headRef, id, heads) => {
14985
+ const m = merged.get(key) ?? { url, key, headRef, owners: /* @__PURE__ */ new Set(), heads: [] };
14986
+ m.heads = [.../* @__PURE__ */ new Set([...m.heads, ...heads])];
14808
14987
  m.headRef ??= headRef;
14809
14988
  m.owners.add(id);
14810
14989
  merged.set(key, m);
14811
14990
  };
14812
14991
  for (const r of readPrs()) {
14813
14992
  if (r.state !== "MERGED") continue;
14814
- for (const id of r.dispatches ?? []) if (all.has(id)) add2(r.url, r.key, r.headRefName, id);
14993
+ for (const id of r.dispatches ?? []) if (all.has(id)) add2(r.url, r.key, r.headRefName, id, [...r.observedHeadShas ?? [], r.headSha]);
14815
14994
  }
14816
14995
  for (const k of all.values()) {
14817
14996
  if (k.bucket !== "done") continue;
@@ -14821,28 +15000,10 @@ function mergedPrs(all) {
14821
15000
  if (!ref) continue;
14822
15001
  const record = readPr(ref.key);
14823
15002
  const state = record?.state ?? ev.pr?.state;
14824
- if (state === "MERGED") add2(ref.url, ref.key, record?.headRefName ?? ev.pr?.headRefName, k.id);
15003
+ if (state === "MERGED") add2(ref.url, ref.key, record?.headRefName ?? ev.pr?.headRefName, k.id, record ? [...record.observedHeadShas ?? [], record.headSha] : ev.pr?.headSha ? [ev.pr.headSha] : []);
14825
15004
  }
14826
15005
  return merged;
14827
15006
  }
14828
- function git7(cwd, ...args) {
14829
- try {
14830
- return execFileSync8("git", args, { cwd, encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] }).trim();
14831
- } catch {
14832
- return void 0;
14833
- }
14834
- }
14835
- function unsafe(dir, fetched) {
14836
- const status = git7(dir, "status", "--porcelain");
14837
- if (status === void 0) return "unpushed work: not a readable git checkout";
14838
- if (status !== "") return "unpushed work: uncommitted changes";
14839
- const common = git7(dir, "rev-parse", "--path-format=absolute", "--git-common-dir") ?? dir;
14840
- if (!fetched.has(common)) fetched.set(common, withRepoLockSync(dir, () => git7(dir, "fetch", "--quiet", "origin")) !== void 0);
14841
- if (!fetched.get(common)) return "unpushed work: fetch failed, remote state unknown";
14842
- const remote = git7(dir, "branch", "-r", "--contains", "HEAD");
14843
- if (remote === void 0 || remote === "") return "unpushed work: HEAD is not on the remote";
14844
- return void 0;
14845
- }
14846
15007
  function planMergeRelease(now2 = Date.now(), batch = Infinity) {
14847
15008
  const plan = { items: [], users: /* @__PURE__ */ new Map(), kept: [] };
14848
15009
  if (!loadConfig().limits.releaseOnMerge) return plan;
@@ -14869,7 +15030,11 @@ function planMergeRelease(now2 = Date.now(), batch = Infinity) {
14869
15030
  const chain = chains.get(id) ?? /* @__PURE__ */ new Set([id]);
14870
15031
  const home = owner(id);
14871
15032
  const add2 = (o) => {
14872
- if (!candidates.has(o)) candidates.set(o, { pr, chain });
15033
+ const previous = candidates.get(o);
15034
+ if (previous) {
15035
+ previous.pr.heads = [.../* @__PURE__ */ new Set([...previous.pr.heads, ...pr.heads])];
15036
+ previous.chain = /* @__PURE__ */ new Set([...previous.chain, ...chain]);
15037
+ } else candidates.set(o, { pr: { ...pr, heads: [...pr.heads] }, chain });
14873
15038
  };
14874
15039
  add2(home);
14875
15040
  for (const m of chain) {
@@ -14883,7 +15048,7 @@ function planMergeRelease(now2 = Date.now(), batch = Infinity) {
14883
15048
  }
14884
15049
  }
14885
15050
  }
14886
- const ordered = [...candidates.entries()].filter(([o]) => fs60.existsSync(worktreePath(o)) && !trapped.has(o)).map(([o, c]) => ({ o, c, from: usage.newest.get(o) ?? now2 })).sort((a, b) => a.from - b.from || a.o.localeCompare(b.o));
15051
+ const ordered = [...candidates.entries()].filter(([o]) => fs61.existsSync(worktreePath(o)) && !trapped.has(o)).map(([o, c]) => ({ o, c, from: usage.newest.get(o) ?? now2 })).sort((a, b) => a.from - b.from || a.o.localeCompare(b.o));
14887
15052
  const fetched = /* @__PURE__ */ new Map();
14888
15053
  let checked = 0;
14889
15054
  for (const { o, c, from } of ordered) {
@@ -14893,7 +15058,8 @@ function planMergeRelease(now2 = Date.now(), batch = Infinity) {
14893
15058
  if (![...c.chain].every((id) => finished(all.get(id))) || !users.every((k) => finished(k))) continue;
14894
15059
  checked++;
14895
15060
  const dir = worktreePath(o);
14896
- const why = unsafe(dir, fetched);
15061
+ const repoKey2 = users.map((k) => storedDescriptor(k.id, k.lane)?.repo).find(Boolean);
15062
+ const why = mergedWorktreeUnsafe(dir, c.pr.heads, loadConfig().repos[repoKey2 ?? ""]?.trunk ?? "main", fetched);
14897
15063
  if (why) {
14898
15064
  plan.kept.push({ id: o, reason: why, pr: c.pr.key, at: new Date(now2).toISOString() });
14899
15065
  continue;
@@ -14909,7 +15075,7 @@ function runMergeRelease(now2 = Date.now(), batch = Infinity) {
14909
15075
  const released = [];
14910
15076
  for (const item of plan.items) {
14911
15077
  applyCull([item]);
14912
- if (fs60.existsSync(item.target)) continue;
15078
+ if (fs61.existsSync(item.target)) continue;
14913
15079
  released.push(item.id);
14914
15080
  for (const k of plan.users.get(item.id) ?? []) mergeEvidence(k.id, k.lane, { worktreeReleased: at });
14915
15081
  }
@@ -15050,7 +15216,7 @@ full pattern and trade-offs: docs/man.md \xB7 config: docs/configuration.md`;
15050
15216
 
15051
15217
  // apps/cli/src/decision-answer.ts
15052
15218
  init_dist2();
15053
- import * as fs61 from "node:fs";
15219
+ import * as fs62 from "node:fs";
15054
15220
  function standingQuestion2(key) {
15055
15221
  return buildTendReport().attention.find((a) => a.kind === "question" && a.key === key);
15056
15222
  }
@@ -15077,7 +15243,7 @@ function answerKey(key, req) {
15077
15243
  try {
15078
15244
  return { decision: meta, answer: answerDecision({ key: meta.key, ...req, maxBytes }) };
15079
15245
  } catch (err) {
15080
- fs61.rmSync(decisionDir(meta.key), { recursive: true, force: true });
15246
+ fs62.rmSync(decisionDir(meta.key), { recursive: true, force: true });
15081
15247
  throw err;
15082
15248
  }
15083
15249
  }
@@ -15091,7 +15257,7 @@ function viewKey(key, now2 = /* @__PURE__ */ new Date()) {
15091
15257
  // apps/cli/src/doctor.ts
15092
15258
  init_dist();
15093
15259
  init_dist2();
15094
- import * as fs67 from "node:fs";
15260
+ import * as fs68 from "node:fs";
15095
15261
  import * as path58 from "node:path";
15096
15262
  import { spawnSync as spawnSync9 } from "node:child_process";
15097
15263
 
@@ -15108,7 +15274,7 @@ function parkedSummary(parked = parkedDispatches()) {
15108
15274
 
15109
15275
  // apps/cli/src/plugin-version.ts
15110
15276
  init_dist();
15111
- import * as fs62 from "node:fs";
15277
+ import * as fs63 from "node:fs";
15112
15278
  import * as os7 from "node:os";
15113
15279
  import * as path53 from "node:path";
15114
15280
  var UPDATE_COMMAND = {
@@ -15117,7 +15283,7 @@ var UPDATE_COMMAND = {
15117
15283
  };
15118
15284
  var readJson2 = (file2) => {
15119
15285
  try {
15120
- return JSON.parse(fs62.readFileSync(file2, "utf8"));
15286
+ return JSON.parse(fs63.readFileSync(file2, "utf8"));
15121
15287
  } catch {
15122
15288
  return void 0;
15123
15289
  }
@@ -15148,7 +15314,7 @@ function installedCodexPlugin(opts = {}) {
15148
15314
  const codexDir = env.CODEX_HOME ?? path53.join(opts.home ?? os7.homedir(), ".codex");
15149
15315
  let cfg;
15150
15316
  try {
15151
- cfg = parse(fs62.readFileSync(path53.join(codexDir, "config.toml"), "utf8"));
15317
+ cfg = parse(fs63.readFileSync(path53.join(codexDir, "config.toml"), "utf8"));
15152
15318
  } catch {
15153
15319
  return void 0;
15154
15320
  }
@@ -15157,7 +15323,7 @@ function installedCodexPlugin(opts = {}) {
15157
15323
  const cache = path53.join(codexDir, "plugins", "cache", "lobstah", "lobstah");
15158
15324
  let dirs;
15159
15325
  try {
15160
- dirs = fs62.readdirSync(cache);
15326
+ dirs = fs63.readdirSync(cache);
15161
15327
  } catch {
15162
15328
  return void 0;
15163
15329
  }
@@ -15215,7 +15381,7 @@ function pluginBehindLine(cliVersion, opts = {}) {
15215
15381
 
15216
15382
  // apps/cli/src/service.ts
15217
15383
  init_dist2();
15218
- import * as fs63 from "node:fs";
15384
+ import * as fs64 from "node:fs";
15219
15385
  import * as os8 from "node:os";
15220
15386
  import * as path54 from "node:path";
15221
15387
  import { spawnSync as spawnSync7 } from "node:child_process";
@@ -15280,7 +15446,7 @@ function serviceFile(kind) {
15280
15446
  return process.platform === "darwin" ? path54.join(os8.homedir(), "Library", "LaunchAgents", `lobstah.${kind}.plist`) : path54.join(os8.homedir(), ".config", "systemd", "user", `lobstah-${kind}.service`);
15281
15447
  }
15282
15448
  function serviceInstalled(kind) {
15283
- return fs63.existsSync(serviceFile(kind));
15449
+ return fs64.existsSync(serviceFile(kind));
15284
15450
  }
15285
15451
  function restartCommand(kind, platform = process.platform, uid = process.getuid?.() ?? 0) {
15286
15452
  return platform === "darwin" ? ["launchctl", "kickstart", "-k", `gui/${uid}/lobstah.${kind}`] : ["systemctl", "--user", "restart", `lobstah-${kind}.service`];
@@ -15299,7 +15465,7 @@ function installService(kind, port) {
15299
15465
  if (process.platform === "win32") {
15300
15466
  throw new Error(`no service manager support on Windows \u2014 run \`lobstah ${kind}\` under your process manager of choice`);
15301
15467
  }
15302
- const entry = COMPILED_BINARY ? "" : fs63.realpathSync(process.argv[1]);
15468
+ const entry = COMPILED_BINARY ? "" : fs64.realpathSync(process.argv[1]);
15303
15469
  const spec = {
15304
15470
  kind,
15305
15471
  nodePath: process.execPath,
@@ -15312,16 +15478,16 @@ function installService(kind, port) {
15312
15478
  if (kind !== "glass" && !onPath("git", spec.pathEnv)) {
15313
15479
  throw new Error(`git is not reachable on the service PATH (${spec.pathEnv}) \u2014 the daemon cannot allocate worktrees`);
15314
15480
  }
15315
- fs63.mkdirSync(spec.logDir, { recursive: true });
15481
+ fs64.mkdirSync(spec.logDir, { recursive: true });
15316
15482
  const file2 = serviceFile(kind);
15317
- fs63.mkdirSync(path54.dirname(file2), { recursive: true });
15483
+ fs64.mkdirSync(path54.dirname(file2), { recursive: true });
15318
15484
  if (process.platform === "darwin") {
15319
- if (fs63.existsSync(file2)) run2("launchctl", ["unload", file2]);
15320
- fs63.writeFileSync(file2, renderLaunchdPlist(spec));
15485
+ if (fs64.existsSync(file2)) run2("launchctl", ["unload", file2]);
15486
+ fs64.writeFileSync(file2, renderLaunchdPlist(spec));
15321
15487
  const load = run2("launchctl", ["load", file2]);
15322
15488
  return { file: file2, loaded: load.ok, detail: load.ok ? `label lobstah.${kind}` : load.out };
15323
15489
  }
15324
- fs63.writeFileSync(file2, renderSystemdUnit(spec));
15490
+ fs64.writeFileSync(file2, renderSystemdUnit(spec));
15325
15491
  run2("systemctl", ["--user", "daemon-reload"]);
15326
15492
  const enable = run2("systemctl", ["--user", "enable", "--now", `lobstah-${kind}.service`]);
15327
15493
  return { file: file2, loaded: enable.ok, detail: enable.ok ? `unit lobstah-${kind}.service` : enable.out };
@@ -15330,19 +15496,19 @@ function uninstallService(kind) {
15330
15496
  if (process.platform === "win32") throw new Error(`no service manager support on Windows`);
15331
15497
  const file2 = serviceFile(kind);
15332
15498
  if (process.platform === "darwin") {
15333
- if (fs63.existsSync(file2)) run2("launchctl", ["unload", file2]);
15499
+ if (fs64.existsSync(file2)) run2("launchctl", ["unload", file2]);
15334
15500
  } else {
15335
15501
  run2("systemctl", ["--user", "disable", "--now", `lobstah-${kind}.service`]);
15336
15502
  }
15337
- const removed = fs63.existsSync(file2);
15338
- if (removed) fs63.unlinkSync(file2);
15503
+ const removed = fs64.existsSync(file2);
15504
+ if (removed) fs64.unlinkSync(file2);
15339
15505
  if (process.platform !== "darwin") run2("systemctl", ["--user", "daemon-reload"]);
15340
15506
  return { file: file2, removed };
15341
15507
  }
15342
15508
 
15343
15509
  // apps/cli/src/pet.ts
15344
15510
  init_dist2();
15345
- import * as fs64 from "node:fs";
15511
+ import * as fs65 from "node:fs";
15346
15512
  import * as os9 from "node:os";
15347
15513
  import * as path55 from "node:path";
15348
15514
  import { spawnSync as spawnSync8 } from "node:child_process";
@@ -15394,7 +15560,7 @@ function installPet(binaryArg) {
15394
15560
  path55.join(process.cwd(), ".build", "debug", "LobstahPet"),
15395
15561
  petBinaryHome()
15396
15562
  ].filter((c) => c !== void 0);
15397
- const source = candidates.find((c) => fs64.existsSync(c));
15563
+ const source = candidates.find((c) => fs65.existsSync(c));
15398
15564
  if (!source) {
15399
15565
  throw new Error(
15400
15566
  "no pet binary found \u2014 build it first (`cd apps/pet && swift build -c release`) and run install from the repo root, or pass --binary <path>"
@@ -15402,33 +15568,33 @@ function installPet(binaryArg) {
15402
15568
  }
15403
15569
  const binary = petBinaryHome();
15404
15570
  const file0 = petPlistFile();
15405
- if (fs64.existsSync(file0)) run3("launchctl", ["unload", file0]);
15571
+ if (fs65.existsSync(file0)) run3("launchctl", ["unload", file0]);
15406
15572
  run3("pkill", ["-f", "LobstahPet"]);
15407
15573
  if (path55.resolve(source) !== path55.resolve(binary)) {
15408
- fs64.mkdirSync(path55.dirname(binary), { recursive: true });
15409
- fs64.rmSync(binary, { force: true });
15410
- fs64.copyFileSync(source, binary);
15411
- fs64.chmodSync(binary, 493);
15574
+ fs65.mkdirSync(path55.dirname(binary), { recursive: true });
15575
+ fs65.rmSync(binary, { force: true });
15576
+ fs65.copyFileSync(source, binary);
15577
+ fs65.chmodSync(binary, 493);
15412
15578
  const bundle = path55.join(path55.dirname(source), "LobstahPet_LobstahPet.bundle");
15413
- if (fs64.existsSync(bundle)) {
15579
+ if (fs65.existsSync(bundle)) {
15414
15580
  const dest = path55.join(path55.dirname(binary), "LobstahPet_LobstahPet.bundle");
15415
- fs64.rmSync(dest, { recursive: true, force: true });
15416
- fs64.cpSync(bundle, dest, { recursive: true });
15581
+ fs65.rmSync(dest, { recursive: true, force: true });
15582
+ fs65.cpSync(bundle, dest, { recursive: true });
15417
15583
  }
15418
15584
  }
15419
15585
  const logDir = path55.join(lobstahHome(), "logs");
15420
- fs64.mkdirSync(logDir, { recursive: true });
15586
+ fs65.mkdirSync(logDir, { recursive: true });
15421
15587
  const file2 = petPlistFile();
15422
- fs64.mkdirSync(path55.dirname(file2), { recursive: true });
15423
- fs64.writeFileSync(file2, renderPetPlist(binary, servicePathEnv(process.execPath), lobstahHome(), logDir));
15588
+ fs65.mkdirSync(path55.dirname(file2), { recursive: true });
15589
+ fs65.writeFileSync(file2, renderPetPlist(binary, servicePathEnv(process.execPath), lobstahHome(), logDir));
15424
15590
  const load = run3("launchctl", ["load", file2]);
15425
15591
  return { file: file2, binary, loaded: load.ok, detail: load.ok ? "label lobstah.pet \u2014 starts at login" : load.out };
15426
15592
  }
15427
15593
  function uninstallPet() {
15428
15594
  const file2 = petPlistFile();
15429
- if (process.platform === "darwin" && fs64.existsSync(file2)) run3("launchctl", ["unload", file2]);
15430
- const removed = fs64.existsSync(file2);
15431
- if (removed) fs64.unlinkSync(file2);
15595
+ if (process.platform === "darwin" && fs65.existsSync(file2)) run3("launchctl", ["unload", file2]);
15596
+ const removed = fs65.existsSync(file2);
15597
+ if (removed) fs65.unlinkSync(file2);
15432
15598
  run3("pkill", ["-f", "LobstahPet"]);
15433
15599
  return { file: file2, removed };
15434
15600
  }
@@ -15437,7 +15603,7 @@ function petStateFile() {
15437
15603
  }
15438
15604
  function readPetState() {
15439
15605
  try {
15440
- const s = JSON.parse(fs64.readFileSync(petStateFile(), "utf8"));
15606
+ const s = JSON.parse(fs65.readFileSync(petStateFile(), "utf8"));
15441
15607
  return typeof s.pid === "number" && typeof s.at === "string" ? s : void 0;
15442
15608
  } catch {
15443
15609
  return void 0;
@@ -15459,8 +15625,8 @@ function processAlive(pid) {
15459
15625
  function petRow(opts = {}) {
15460
15626
  const now2 = opts.now ?? Date.now();
15461
15627
  const alive2 = opts.alive ?? processAlive;
15462
- const agent = fs64.existsSync(opts.plist ?? petPlistFile());
15463
- const binary = fs64.existsSync(opts.binary ?? petBinaryHome());
15628
+ const agent = fs65.existsSync(opts.plist ?? petPlistFile());
15629
+ const binary = fs65.existsSync(opts.binary ?? petBinaryHome());
15464
15630
  const installed = agent ? "installed" : binary ? "binary installed, no login agent" : "not installed";
15465
15631
  const state = readPetState();
15466
15632
  if (!state) {
@@ -15483,13 +15649,13 @@ function petRow(opts = {}) {
15483
15649
  init_dist();
15484
15650
  init_dist2();
15485
15651
  import { createHash as createHash9 } from "node:crypto";
15486
- import * as fs66 from "node:fs";
15652
+ import * as fs67 from "node:fs";
15487
15653
  import * as os10 from "node:os";
15488
15654
  import * as path57 from "node:path";
15489
15655
 
15490
15656
  // apps/cli/src/watchers.ts
15491
15657
  init_dist2();
15492
- import * as fs65 from "node:fs";
15658
+ import * as fs66 from "node:fs";
15493
15659
  import * as path56 from "node:path";
15494
15660
  var STALE_MS = 5e3;
15495
15661
  var HEARTBEAT_MS = 1500;
@@ -15497,7 +15663,7 @@ var watcherDir = () => path56.join(lobstahHome(), "watchers");
15497
15663
  var watcherFile = (sessionId) => path56.join(watcherDir(), `${encodeURIComponent(sessionId)}.json`);
15498
15664
  function liveRegistration(sessionId, now2 = Date.now()) {
15499
15665
  try {
15500
- const w = JSON.parse(fs65.readFileSync(watcherFile(sessionId), "utf8"));
15666
+ const w = JSON.parse(fs66.readFileSync(watcherFile(sessionId), "utf8"));
15501
15667
  const beat = Date.parse(w.heartbeatAt);
15502
15668
  if (w.sessionId !== sessionId || !Number.isInteger(w.pid) || w.pid <= 0 || !Number.isFinite(beat) || now2 - beat > STALE_MS) return void 0;
15503
15669
  return w;
@@ -15545,7 +15711,7 @@ async function stopWatcher(sessionId, kind, graceMs = 3e3, pollMs = 50) {
15545
15711
  const file2 = watcherFile(sessionId);
15546
15712
  const claimed = () => {
15547
15713
  try {
15548
- return JSON.parse(fs65.readFileSync(file2, "utf8")).pid === w.pid;
15714
+ return JSON.parse(fs66.readFileSync(file2, "utf8")).pid === w.pid;
15549
15715
  } catch {
15550
15716
  return false;
15551
15717
  }
@@ -15553,7 +15719,7 @@ async function stopWatcher(sessionId, kind, graceMs = 3e3, pollMs = 50) {
15553
15719
  for (; ; ) {
15554
15720
  if (!claimed()) return { watcher: w, exited: true };
15555
15721
  if (!pidAlive4(w.pid)) {
15556
- if (claimed()) fs65.rmSync(file2, { force: true });
15722
+ if (claimed()) fs66.rmSync(file2, { force: true });
15557
15723
  return { watcher: w, exited: true };
15558
15724
  }
15559
15725
  if (Date.now() >= deadline) return { watcher: w, exited: false };
@@ -15561,13 +15727,13 @@ async function stopWatcher(sessionId, kind, graceMs = 3e3, pollMs = 50) {
15561
15727
  }
15562
15728
  }
15563
15729
  function armWatcher(sessionId, kind, trapId) {
15564
- fs65.mkdirSync(watcherDir(), { recursive: true });
15730
+ fs66.mkdirSync(watcherDir(), { recursive: true });
15565
15731
  const file2 = watcherFile(sessionId);
15566
15732
  const own = () => ({ sessionId, kind, pid: process.pid, heartbeatAt: (/* @__PURE__ */ new Date()).toISOString(), ...trapId ? { trapId } : {} });
15567
15733
  let claimed = false;
15568
15734
  for (let attempt = 0; attempt < 3; attempt++) {
15569
15735
  try {
15570
- fs65.writeFileSync(file2, `${JSON.stringify(own())}
15736
+ fs66.writeFileSync(file2, `${JSON.stringify(own())}
15571
15737
  `, { flag: "wx" });
15572
15738
  claimed = true;
15573
15739
  break;
@@ -15576,7 +15742,7 @@ function armWatcher(sessionId, kind, trapId) {
15576
15742
  const live = liveRegistration(sessionId);
15577
15743
  if (live) throw new Error(alreadyArmed(live));
15578
15744
  try {
15579
- fs65.rmSync(file2);
15745
+ fs66.rmSync(file2);
15580
15746
  } catch {
15581
15747
  }
15582
15748
  }
@@ -15584,7 +15750,7 @@ function armWatcher(sessionId, kind, trapId) {
15584
15750
  if (!claimed) throw new Error(`could not arm watcher for session ${sessionId}`);
15585
15751
  const ownsFile = () => {
15586
15752
  try {
15587
- return JSON.parse(fs65.readFileSync(file2, "utf8")).pid === process.pid;
15753
+ return JSON.parse(fs66.readFileSync(file2, "utf8")).pid === process.pid;
15588
15754
  } catch {
15589
15755
  return false;
15590
15756
  }
@@ -15593,12 +15759,12 @@ function armWatcher(sessionId, kind, trapId) {
15593
15759
  if (!ownsFile()) return;
15594
15760
  const tmp = `${file2}.tmp-${process.pid}`;
15595
15761
  try {
15596
- fs65.writeFileSync(tmp, `${JSON.stringify(own())}
15762
+ fs66.writeFileSync(tmp, `${JSON.stringify(own())}
15597
15763
  `);
15598
- fs65.renameSync(tmp, file2);
15764
+ fs66.renameSync(tmp, file2);
15599
15765
  } catch {
15600
15766
  try {
15601
- fs65.rmSync(tmp, { force: true });
15767
+ fs66.rmSync(tmp, { force: true });
15602
15768
  } catch {
15603
15769
  }
15604
15770
  }
@@ -15610,7 +15776,7 @@ function armWatcher(sessionId, kind, trapId) {
15610
15776
  if (stopped) return;
15611
15777
  stopped = true;
15612
15778
  clearInterval(timer);
15613
- if (ownsFile()) fs65.rmSync(file2, { force: true });
15779
+ if (ownsFile()) fs66.rmSync(file2, { force: true });
15614
15780
  process.off("exit", stop);
15615
15781
  process.off("SIGINT", interrupt);
15616
15782
  process.off("SIGTERM", terminate2);
@@ -15632,7 +15798,7 @@ function armWatcher(sessionId, kind, trapId) {
15632
15798
  // apps/cli/src/hook-readiness.ts
15633
15799
  var readJson3 = (file2) => {
15634
15800
  try {
15635
- return JSON.parse(fs66.readFileSync(file2, "utf8"));
15801
+ return JSON.parse(fs67.readFileSync(file2, "utf8"));
15636
15802
  } catch {
15637
15803
  return void 0;
15638
15804
  }
@@ -15669,7 +15835,7 @@ function codexHooks(opts = {}) {
15669
15835
  const codexDir = env.CODEX_HOME ?? path57.join(opts.home ?? os10.homedir(), ".codex");
15670
15836
  let cfg = {};
15671
15837
  try {
15672
- cfg = parse(fs66.readFileSync(path57.join(codexDir, "config.toml"), "utf8"));
15838
+ cfg = parse(fs67.readFileSync(path57.join(codexDir, "config.toml"), "utf8"));
15673
15839
  } catch {
15674
15840
  }
15675
15841
  const state = (cfg.hooks ?? {}).state ?? {};
@@ -15903,7 +16069,7 @@ var PROBE_PERMISSION = { "pull requests": "Pull requests", contents: "Contents",
15903
16069
  function githubRepos(repos) {
15904
16070
  const out = [];
15905
16071
  for (const [key, r] of Object.entries(repos)) {
15906
- const origin = r.origin ?? (fs67.existsSync(r.path) ? git8(r.path, "remote", "get-url", "origin").out : "");
16072
+ const origin = r.origin ?? (fs68.existsSync(r.path) ? git8(r.path, "remote", "get-url", "origin").out : "");
15907
16073
  const forgeRepo = origin ? githubRepoFromOrigin(origin) : void 0;
15908
16074
  if (forgeRepo) out.push({ key, forgeRepo, trunk: r.trunk });
15909
16075
  }
@@ -15913,12 +16079,12 @@ function diskRow(cfg, freeBytes = statfsFreeBytes, now2 = Date.now()) {
15913
16079
  const dir = worktreesDir();
15914
16080
  let free;
15915
16081
  try {
15916
- free = freeBytes(fs67.existsSync(dir) ? dir : lobstahHome());
16082
+ free = freeBytes(fs68.existsSync(dir) ? dir : lobstahHome());
15917
16083
  } catch {
15918
16084
  free = void 0;
15919
16085
  }
15920
16086
  const { minFreeGB, retentionDays } = cfg.limits;
15921
- const cullable = fs67.existsSync(dir) ? planPressureCull(now2) : [];
16087
+ const cullable = fs68.existsSync(dir) ? planPressureCull(now2) : [];
15922
16088
  const oldest = cullable.reduce((max, i) => Math.max(max, i.ageDays), 0);
15923
16089
  const parts = [
15924
16090
  free === void 0 ? "free space unreadable" : `${formatGB(free)} free on ${dir}`,
@@ -15930,7 +16096,7 @@ function diskRow(cfg, freeBytes = statfsFreeBytes, now2 = Date.now()) {
15930
16096
  const kept = readKeptWorktrees();
15931
16097
  if (kept.length > 0) {
15932
16098
  parts.push(
15933
- `kept: unpushed work (${kept.length} worktree(s) of merged PRs: ${kept.map((k) => `${k.id.slice(0, 8)} ${k.reason.replace(/^unpushed work: /, "")}`).join(", ")})`
16099
+ `kept: ${kept.length} worktree(s) of merged PRs: ${kept.map((k) => `${k.id.slice(0, 8)} ${k.reason}`).join(", ")}`
15934
16100
  );
15935
16101
  }
15936
16102
  const hold = readHold();
@@ -15956,7 +16122,7 @@ async function runDoctor(now2 = Date.now()) {
15956
16122
  try {
15957
16123
  const port = glassPort();
15958
16124
  const info = await probeGlass(port);
15959
- const installed = process.platform === "win32" ? false : fs67.existsSync(serviceFile("glass"));
16125
+ const installed = process.platform === "win32" ? false : fs68.existsSync(serviceFile("glass"));
15960
16126
  push(
15961
16127
  "glass",
15962
16128
  info ? "ok" : "warn",
@@ -15968,13 +16134,13 @@ async function runDoctor(now2 = Date.now()) {
15968
16134
  const pet = petRow({ now: now2 });
15969
16135
  push(pet.check, pet.status, pet.detail);
15970
16136
  const cfgFile = configPath();
15971
- if (!fs67.existsSync(cfgFile)) {
16137
+ if (!fs68.existsSync(cfgFile)) {
15972
16138
  push("config", "fail", `${cfgFile} missing \u2014 run \`lobstah init\``);
15973
16139
  return rows;
15974
16140
  }
15975
16141
  let parsed;
15976
16142
  try {
15977
- parsed = parse(fs67.readFileSync(cfgFile, "utf8"));
16143
+ parsed = parse(fs68.readFileSync(cfgFile, "utf8"));
15978
16144
  push("config", "ok", cfgFile);
15979
16145
  } catch (err) {
15980
16146
  push("config", "fail", `${cfgFile}: ${err instanceof Error ? err.message : String(err)}`);
@@ -15985,7 +16151,7 @@ async function runDoctor(now2 = Date.now()) {
15985
16151
  if (repoKeys.length === 0) push("repos", "warn", "none configured \u2014 add one with `lobstah repos add <path>`");
15986
16152
  for (const [key, repo] of Object.entries(cfg.repos)) {
15987
16153
  const label = `repo ${key}`;
15988
- if (!fs67.existsSync(repo.path)) {
16154
+ if (!fs68.existsSync(repo.path)) {
15989
16155
  push(label, "fail", `path ${repo.path} does not exist`);
15990
16156
  continue;
15991
16157
  }
@@ -16016,11 +16182,11 @@ async function runDoctor(now2 = Date.now()) {
16016
16182
  const trapNames2 = listTraps().map(trapLabel);
16017
16183
  const parked = parkedSummary();
16018
16184
  const slots = `headless: ${workSlots.headless} of ${cfg.limits.maxConcurrent} work, ${choreSlots.headless} of ${cfg.limits.choreConcurrent} chore; traps: ${workSlots.traps + choreSlots.traps}${trapNames2.length ? ` (${trapNames2.join(", ")})` : ""}${parked ? `; ${parked}` : ""}`;
16019
- if (!fs67.existsSync(hb)) {
16185
+ if (!fs68.existsSync(hb)) {
16020
16186
  push("daemon", "warn", `no heartbeat \u2014 daemon not running (\`lobstah daemon install\`); ${slots}`);
16021
16187
  } else {
16022
16188
  try {
16023
- const payload = JSON.parse(fs67.readFileSync(hb, "utf8"));
16189
+ const payload = JSON.parse(fs68.readFileSync(hb, "utf8"));
16024
16190
  const age2 = now2 - new Date(payload.heartbeat ?? 0).getTime();
16025
16191
  push(
16026
16192
  "daemon",
@@ -16053,9 +16219,9 @@ async function runDoctor(now2 = Date.now()) {
16053
16219
  push(disk.check, disk.status, disk.detail);
16054
16220
  try {
16055
16221
  const soaking = path58.join(lobstahHome(), "soaking");
16056
- const stale = fs67.readdirSync(soaking).filter((f) => f.endsWith(".json")).filter((f) => {
16222
+ const stale = fs68.readdirSync(soaking).filter((f) => f.endsWith(".json")).filter((f) => {
16057
16223
  try {
16058
- return typeof JSON.parse(fs67.readFileSync(path58.join(soaking, f), "utf8")).trapId !== "string";
16224
+ return typeof JSON.parse(fs68.readFileSync(path58.join(soaking, f), "utf8")).trapId !== "string";
16059
16225
  } catch {
16060
16226
  return true;
16061
16227
  }
@@ -16083,7 +16249,7 @@ async function runDoctor(now2 = Date.now()) {
16083
16249
  // apps/cli/src/glass.ts
16084
16250
  init_stats2();
16085
16251
  init_dist2();
16086
- import * as fs72 from "node:fs";
16252
+ import * as fs73 from "node:fs";
16087
16253
  import * as os11 from "node:os";
16088
16254
  import * as path62 from "node:path";
16089
16255
  import * as http3 from "node:http";
@@ -16091,17 +16257,17 @@ import { randomBytes as randomBytes6, timingSafeEqual as timingSafeEqual3 } from
16091
16257
  import { fileURLToPath as fileURLToPath3 } from "node:url";
16092
16258
 
16093
16259
  // apps/cli/src/glass-page.generated.ts
16094
- var GLASS_PAGE = '<!doctype html>\n<html>\n <head>\n <meta charset="utf-8" />\n <meta name="viewport" content="width=device-width,initial-scale=1" />\n <title>spyglass</title>\n <link rel="icon" type="image/png" href="/icon.png" />\n <style>:root{--bg: #0e1116;--card: #161b22;--line: #2b3240;--line-hover: #3a455a;--row-hover: #1c2330;--inset: #10151c;--seg-on: #26436b;--scrim: rgba(4, 7, 11, 0.72);--fg: #dbe2ea;--dim: #8b96a5;--ok: #4fc17c;--warn: #e2b93d;--bad: #e26d5c;--link: #6cb2e2;--on-fill: #fff;--pr-merged: #8957e5;--pr-merged-fg: #ab7df8;--pr-open: #238636;--pr-draft: #6e7681;--pr-closed: #da3633;--lobstah: #eb6644;--heat-0: var(--inset);--heat-1: color-mix(in srgb, var(--lobstah) 30%, var(--card));--heat-2: color-mix(in srgb, var(--lobstah) 55%, var(--card));--heat-3: color-mix(in srgb, var(--lobstah) 78%, var(--card));--heat-4: var(--lobstah);--heat-cell: 11px;--heat-gap: 3px;--font-mono: ui-monospace, SFMono-Regular, Menlo, monospace;--font-size: 13px;--space-1: 4px;--space-2: 8px;--space-3: 12px;--space-4: 16px;--page-pad-block: 14px;--radius: 6px;--card-radius: 8px;--card-pad: 12px 14px;--card-min: 300px;--deck-card-min: 240px;--badge-radius: 5px;--badge-pad: 1px 7px;--badge-font: 11px;--modal-radius: 10px;--modal-width: 780px;--layer-modal: 10;--layer-decision-modal: 15;--layer-alert: calc(var(--layer-modal) - 1);--unread-bg: color-mix(in srgb, var(--bad) 16%, var(--card))}*{box-sizing:border-box}body{margin:0;background:var(--bg);color:var(--fg);font:var(--font-size)/1.45 var(--font-mono);padding-block:var(--page-pad-block);padding-inline:var(--space-4);display:flex;flex-direction:column;min-height:100vh}h1{font-size:15px;margin:0 0 10px}h1 .dim{color:var(--dim);font-weight:normal}h2{font-size:12px;color:var(--dim);text-transform:uppercase;letter-spacing:0.08em;margin:20px 0 6px}.chips,.controls{display:flex;gap:var(--space-2);flex-wrap:wrap;align-items:center}.controls{margin:0 0 14px}.chips{margin-bottom:8px}.chip{background:var(--card);border:1px solid var(--line);border-radius:var(--radius);padding:3px 9px}.controls select,.newtrap select,.controls input[type=search]{background:var(--card);border:1px solid var(--line);border-radius:var(--radius);color:var(--fg);font:inherit;padding:4px 8px}.controls input[type=search]{flex:1 1 140px;min-width:120px;max-width:340px}#chain-control{display:inline-flex;align-items:center;gap:7px;white-space:nowrap}#f-chain{margin:0;width:14px;height:14px;accent-color:var(--link)}.tabs{display:flex;gap:var(--space-1);border-bottom:1px solid var(--line);margin:8px 0 10px}.tabs a{color:var(--dim);padding:6px 11px;border-bottom:2px solid transparent}.tabs a.on{color:var(--fg);border-color:var(--link)}.tabpage{display:none}.tabpage.on{display:block;flex:1 0 auto}.headerline{display:flex;align-items:center;gap:var(--space-2)}.headerline h1{flex:1}#settings-slot{min-width:34px;text-align:right;color:var(--dim)}#settings-slot #gearbtn{margin-left:0}.deckgrid{display:flex;flex-direction:column;gap:var(--space-3)}.deckgrid h2{margin:5px 0}.deckgrid section{width:100%;min-width:0}.deckline{padding:2px 0;border-top:1px solid var(--line);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.deckline.acked,.card.acked,.rowhead.acked,.badge.acked{opacity:0.55}.deckmore{color:var(--dim);font-size:11px;display:inline-block;margin-top:4px}.deckline.click{cursor:pointer}.deckline.click:hover{background:var(--row-hover)}.deckgrid h2 a{color:inherit}.deckgrid h2 a:hover{color:var(--fg)}.deckgrid .cards{grid-template-columns:repeat(auto-fill,minmax(min(var(--deck-card-min),100%),1fr));gap:10px}.deckstack{margin-bottom:9px}.deckstack>.dim{font-size:11px;margin:3px 0 5px}.seg{display:inline-flex;border:1px solid var(--line);border-radius:var(--radius);overflow:hidden}.seg button{background:var(--card);border:none;color:var(--dim);font:inherit;padding:4px 11px;cursor:pointer}.seg button.on{background:var(--seg-on);color:var(--fg)}.wrap{overflow-x:auto}table{border-collapse:collapse;width:100%;background:var(--card);border:1px solid var(--line);border-radius:var(--radius);overflow:hidden}th,td{text-align:left;padding:4px 9px;border-top:1px solid var(--line);vertical-align:top;white-space:nowrap}td.grow{white-space:normal;word-break:break-word;min-width:140px}th{color:var(--dim);font-weight:normal;border-top:none;font-size:11px}tr.rowhead{cursor:pointer}tr.rowhead:hover{background:var(--row-hover)}.cards{display:grid;grid-template-columns:repeat(auto-fill,minmax(min(var(--card-min),100%),1fr));gap:var(--space-3)}.card{background:var(--card);border:1px solid var(--line);border-radius:var(--card-radius);padding:var(--card-pad);cursor:pointer;min-width:0;overflow:hidden;overflow-wrap:anywhere}.card:hover{border-color:var(--line-hover)}.worker-harness{cursor:help}.worker-harness:focus-visible{outline:2px solid var(--ok);outline-offset:3px}.worker-tooltip{position:fixed;z-index:20;max-width:264px;padding:6px 9px;background:var(--card);color:var(--fg);border:1px solid var(--line-hover);border-radius:var(--badge-radius);box-shadow:0 3px 12px var(--scrim);font-size:12px;white-space:normal;pointer-events:none}.worker-details dl{display:grid;grid-template-columns:max-content 1fr;gap:4px 14px;font-size:12px}.worker-details dt{color:var(--dim)}.worker-details dd{margin:0;overflow-wrap:anywhere}.card .top{display:flex;justify-content:space-between;gap:var(--space-3);align-items:center;min-height:22px}.card .top>b,.card .top>.prname{flex:1 1000000 auto;min-width:min(10ch,100%);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.card .top .badge{flex:0 0 auto}.card .top .badge.long{flex:0 1 auto;min-width:min(12ch,100%);max-width:min(26ch,100%);overflow:hidden;text-overflow:ellipsis}.card .meta{color:var(--dim);font-size:12px;margin-top:2px;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}.card .note{margin-top:6px;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}.activity{margin-top:4px;font-size:12px;color:var(--fg);opacity:0.85}.waiting{margin-top:4px;font-size:12px;color:var(--dim)}.activity.stale{color:var(--dim);opacity:1}.card .foot{margin-top:8px;display:flex;gap:10px;flex-wrap:wrap;align-items:center;color:var(--dim);font-size:12px}.card .foot>*{min-width:0}.card .foot .footact{margin-left:auto}.catchn{white-space:nowrap}.btn{background:var(--card);border:1px solid var(--line);border-radius:5px;color:var(--dim);font:inherit;font-size:11px;padding:1px 8px;cursor:pointer;white-space:nowrap}.btn:hover{color:var(--fg);border-color:var(--line-hover)}.badge{border-radius:var(--badge-radius);padding:var(--badge-pad);font-size:var(--badge-font);line-height:1.5;border:1px solid var(--line);white-space:nowrap;display:inline-block}.badge.pr-merged{background:var(--pr-merged);border-color:var(--pr-merged-fg);color:var(--on-fill)}.badge.pr-open{background:var(--pr-open);border-color:var(--pr-open);color:var(--on-fill)}.badge.pr-draft{background:var(--pr-draft);border-color:var(--pr-draft);color:var(--on-fill)}.badge.pr-closed{background:var(--pr-closed);border-color:var(--pr-closed);color:var(--on-fill)}.badge.pr-conflicts{background:var(--pr-closed);border-color:var(--pr-closed);color:var(--on-fill)}.badge.pr-behind{background:var(--pr-draft);border-color:var(--pr-draft);color:var(--on-fill)}.badge.unreported{font-weight:normal;font-size:10px;padding:0 5px;vertical-align:1px}.v-done{color:var(--ok)}.v-working,.v-queued{color:var(--fg)}.v-needs-decision,.v-blocked{color:var(--bad);font-weight:bold}.v-failed{color:var(--bad)}.v-paused,.v-unknown{color:var(--dim)}.dim{color:var(--dim)}.ok{color:var(--ok)}.warn{color:var(--warn)}.bad{color:var(--bad)}.detail{display:none}.detail.open{display:table-row}.detail td{background:var(--inset);white-space:pre-wrap;word-break:break-word;font-size:12px;color:var(--dim)}.detail b{color:var(--fg)}.empty{color:var(--dim);padding:6px 2px}.older{padding:8px 2px}#stale{display:none;color:var(--bad)}a{color:var(--link);text-decoration:none}.dot{display:inline-block;width:8px;height:8px;border-radius:50%;margin-right:5px;vertical-align:middle}.dot.ok{background:var(--ok)}.dot.warn{background:var(--warn)}.dot.bad{background:var(--bad)}.dot.dim{background:var(--dim)}#overlay{position:fixed;inset:0;background:var(--scrim);display:none;z-index:var(--layer-modal);padding:28px 16px;overflow:auto}#overlay.open{display:block}.modal{max-width:var(--modal-width);margin:0 auto;background:var(--card);border:1px solid var(--line);border-radius:var(--modal-radius);padding:16px 18px;position:relative}.modal .x{position:absolute;top:8px;right:12px;cursor:pointer;color:var(--dim);font-size:18px;line-height:1}.modal .x:hover{color:var(--fg)}.modal h3{margin:0 24px 2px 0;font-size:14px}.modal .sub{color:var(--dim);font-size:12px;margin-bottom:10px}.modal .sec{color:var(--dim);text-transform:uppercase;letter-spacing:0.08em;font-size:11px;margin:14px 0 4px}.modal pre{white-space:pre-wrap;word-break:break-word;margin:0;font:inherit;color:var(--fg)}.msg{border-left:3px solid var(--line);padding:3px 10px;margin:8px 0;white-space:pre-wrap;word-break:break-word}.msg.from-helm{border-color:var(--link)}.msg .hdr{color:var(--dim);font-size:11px}.catch{border:1px solid var(--line);border-radius:var(--radius);padding:6px 10px;margin:8px 0}.catch.click{cursor:pointer}.catch .hdr{display:flex;gap:10px;flex-wrap:wrap;align-items:baseline}.loglines{color:var(--dim);font-size:12px;white-space:pre-wrap;word-break:break-word;margin-top:4px}.cmd{display:flex;gap:var(--space-2);align-items:center;margin:5px 0;flex-wrap:wrap}.cmd code{background:var(--inset);border:1px solid var(--line);border-radius:5px;padding:3px 8px;word-break:break-all;cursor:pointer}.cmd code:hover{border-color:var(--line-hover)}.cmd code.copied{border-color:var(--ok)}.cmd button{background:var(--card);border:1px solid var(--line);border-radius:5px;color:var(--dim);font:inherit;font-size:11px;padding:2px 8px;cursor:pointer}.cmd button:hover{color:var(--fg)}.chip.click{cursor:pointer}.chip.click:hover{border-color:var(--line-hover)}footer{margin-top:26px;padding-top:10px;border-top:1px solid var(--line);color:var(--dim);font-size:12px;display:flex;gap:var(--space-2);flex-wrap:wrap}.lob{position:fixed;bottom:6px;left:0;z-index:5;cursor:pointer;font-size:34px;line-height:1;user-select:none;animation:crawl 18s linear infinite}a.lob{color:inherit}.lob .bub .badge{display:inline-block;margin:0 0 3px}.pet-art{display:block;position:relative;width:72px;height:56px}.pet-art .sprite{width:72px;height:56px;background-position:0 0;background-repeat:no-repeat;background-size:400% 100%;image-rendering:pixelated;animation:step 0.5s steps(4) infinite}.lob .bub{display:none;position:absolute;bottom:58px;right:-8px;z-index:2;background:var(--card);border:1px solid var(--line);border-radius:9px;padding:6px 9px 5px;font-size:11px;line-height:1.35;color:var(--fg);width:max-content;max-width:150px;box-shadow:0 2px 8px rgba(0,0,0,0.4)}.lob:hover .bub{display:block}.lob .bub span{display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;white-space:normal;word-break:break-word}.pet-art .star{position:absolute;left:56px;top:-9px;height:14px;image-rendering:pixelated;z-index:3}.lob:hover{animation-play-state:paused}@keyframes crawl{0%{transform:translateX(-90px)}100%{transform:translateX(100vw)}}@keyframes step{to{background-position-x:-288px}}#gearbtn{background:none;border:1px solid var(--line);border-radius:var(--radius);color:var(--dim);font:inherit;font-size:13px;padding:1px 7px;margin-left:8px;cursor:pointer;vertical-align:1px}#gearbtn:hover,#gearbtn.on{color:var(--fg);border-color:var(--line-hover)}.settings .row{display:flex;justify-content:space-between;align-items:center;gap:14px;margin:12px 0}.settings .lbl{color:var(--fg);font-size:12px}.settings .hint{display:block;color:var(--dim);font-size:11px;margin-top:2px}.dlist{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:6px}.drow{display:flex;flex-wrap:wrap;align-items:baseline;gap:var(--space-2);background:var(--card);border:1px solid var(--line);border-radius:var(--card-radius);padding:8px 12px}.drow.unread{background:var(--unread-bg);border-color:var(--bad)}.drow.answered{opacity:0.7}.drow.focus{animation:dflash 1.6s ease-out 1}.drow .dopen{flex:1 1 280px;display:flex;align-items:baseline;gap:var(--space-2);min-width:0;background:none;border:none;padding:0;color:var(--fg);font:inherit;text-align:left;cursor:pointer}.drow .dopen:hover .dtitle{text-decoration:underline}.drow .dtitle{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.drow .dmeta,.drow .dage{font-size:11px}.drow .dlink,.dmodal .dlink{color:var(--link)}.dstate{font-size:var(--badge-font);border-radius:var(--badge-radius);padding:var(--badge-pad);border:1px solid var(--line);color:var(--dim)}.dstate.unread{border-color:var(--bad);color:var(--bad)}.dstate.answered{color:var(--ok)}.dbadge{display:inline-block;margin-left:6px;min-width:1.6em;text-align:center;border-radius:999px;padding:0 6px;font-size:11px;font-weight:bold;background:var(--bad);color:var(--on-fill)}#doverlay{position:fixed;inset:0;background:var(--scrim);z-index:var(--layer-decision-modal);padding:28px 16px;overflow:auto}.dmodal:focus{outline:none}.dmodal .x{background:none;border:none;font:inherit;font-size:18px;color:var(--dim)}.dnav{display:flex;align-items:center;gap:var(--space-2);margin:0 28px var(--space-3) 0}.dnav .dpos{color:var(--dim);font-size:11px}.dmodal .dtop{display:flex;flex-wrap:wrap;align-items:baseline;gap:var(--space-2);margin-bottom:var(--space-2)}.dmodal .dtitle{white-space:pre-wrap;overflow-wrap:anywhere;font-size:14px}.dmodal .dmeta{font-size:11px}.ddone{padding:28px 8px;text-align:center;cursor:pointer}.ddone .ok{font-size:15px;margin-bottom:var(--space-2)}.dalert-live{position:fixed;right:var(--space-4);bottom:var(--space-4);z-index:var(--layer-alert)}.dalert{display:flex;align-items:center;gap:4px;background:var(--card);border:1px solid var(--bad);border-radius:var(--card-radius);padding:8px 10px;box-shadow:0 6px 24px var(--scrim)}.dalert .dalert-open{display:flex;align-items:center;gap:var(--space-2);background:none;border:none;color:var(--fg);font:inherit;cursor:pointer}.dalert .dx{background:none;border:none;color:var(--dim);font:inherit;font-size:15px;cursor:pointer}.dcount{display:inline-block;min-width:1.8em;text-align:center;border-radius:999px;padding:1px 7px;font-weight:bold;background:var(--bad);color:var(--on-fill);animation:dglow 1.8s ease-in-out infinite}@keyframes dglow{0%,100%{box-shadow:0 0 0 0 transparent}50%{box-shadow:0 0 10px 3px var(--bad)}}@media(prefers-reduced-motion:reduce){.dcount,.drow.focus{animation:none}}.sr-only{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap}@keyframes dflash{0%,35%{border-color:var(--link);box-shadow:0 0 0 3px var(--link)}100%{border-color:var(--line);box-shadow:0 0 0 0 transparent}}.dfiles{display:flex;flex-wrap:wrap;gap:var(--space-2);margin:6px 0}.dfiles .dimg img{max-height:160px;max-width:320px;border:1px solid var(--line);border-radius:var(--radius);display:block}.dfiles .dfile{width:100%;font-size:11px}.doptions{display:flex;flex-wrap:wrap;gap:6px;margin:8px 0 6px}.btn.dopt{font-size:12px;padding:3px 12px;color:var(--fg)}.btn.dopt.on{background:var(--seg-on);border-color:var(--link)}.danswer{display:block;width:100%;min-height:3.2em;max-height:50vh;margin:6px 0;padding:6px 8px;resize:vertical;overflow-y:auto;field-sizing:content;background:var(--inset);color:var(--fg);border:1px solid var(--line);border-radius:var(--radius);font:inherit}.dkey,.dlegend kbd{display:inline-block;min-width:1.5em;text-align:center;font:inherit;font-size:10px;line-height:1.4;padding:0 4px;border:1px solid var(--line-hover);border-radius:4px;color:var(--dim);background:var(--inset)}.btn.dopt .dkey{margin-right:6px}.btn.dopt.on .dkey{border-color:var(--link);color:var(--fg)}.danswer-wrap{position:relative}.danswer-wrap .dkey-text{position:absolute;top:6px;right:8px;pointer-events:none}.dlegend{margin-top:var(--space-3);padding-top:var(--space-2);border-top:1px solid var(--line);font-size:11px;line-height:1.9}.danswer:focus{outline:none;border-color:var(--line-hover)}.dfoot{display:flex;flex-wrap:wrap;align-items:center;gap:6px}.dfoot .dattach input{display:none}.dfoot .dchip{font-size:11px;border:1px solid var(--line);border-radius:var(--badge-radius);padding:0 0 0 6px}.dfoot .dx{background:none;border:none;color:var(--dim);cursor:pointer;font:inherit}.dfoot .derr{font-size:11px}.btn.dsend{margin-left:auto;color:var(--fg);padding:2px 14px}.btn:disabled{cursor:default;opacity:0.6}.danswered{margin-top:6px}.thumb,.dimg,.mdimg{background:none;border:none;padding:0;cursor:zoom-in;font:inherit}.thumb{display:block;margin:4px 0}.thumb img{max-height:120px;max-width:240px;border:1px solid var(--line);border-radius:var(--radius);display:block}.lightbox{position:fixed;inset:0;z-index:20;background:rgba(0,0,0,0.85);display:flex;flex-direction:column;align-items:center;justify-content:center;gap:var(--space-2)}.lightbox .lbimg{max-width:90vw;max-height:90vh;object-fit:contain;border-radius:var(--radius);box-shadow:0 8px 40px rgba(0,0,0,0.6)}.lightbox .lbclose{position:absolute;top:12px;right:16px;background:none;border:none;color:var(--fg);font-size:28px;line-height:1;cursor:pointer}.lightbox .lboriginal{color:var(--link);font-size:12px}.mdpage{background:var(--inset);border:1px solid var(--line);border-radius:6px;padding:8px 14px;margin:6px 0;line-height:1.5;overflow-wrap:anywhere}.mdpage h1,.mdpage h2,.mdpage h3,.mdpage h4{margin:12px 0 6px;line-height:1.25}.mdpage h1{font-size:18px}.mdpage h2{font-size:15px}.mdpage h3,.mdpage h4{font-size:13px}.mdpage p,.mdpage ul,.mdpage ol,.mdpage blockquote{margin:6px 0}.mdpage ul,.mdpage ol{padding-left:22px}.mdpage a{color:var(--link)}.mdpage code{background:var(--card);border-radius:3px;padding:0 3px}.mdpage pre.mdcode{background:var(--card);border:1px solid var(--line);border-radius:4px;padding:8px;max-width:100%;overflow-x:auto;white-space:pre;overflow-wrap:normal}.mdpage pre.mdcode code{padding:0;background:none}.mdpage blockquote{border-left:3px solid var(--line);padding-left:10px;color:var(--dim)}.mdpage img{max-width:100%;height:auto;border-radius:4px;display:block;margin:6px 0}.mdpage hr{border:0;border-top:1px solid var(--line)}.mdpage .mdtable{max-width:100%;overflow-x:auto}.mdpage table{border-collapse:collapse;margin:6px 0}.mdpage th,.mdpage td{border:1px solid var(--line);padding:3px 8px;text-align:left;white-space:normal;overflow-wrap:anywhere;min-width:12ch}.tabhead{margin:0 0 8px}.newtrap{display:inline-flex;flex-wrap:wrap;gap:6px;align-items:center;font-size:12px;font-weight:normal;text-transform:none;letter-spacing:normal}.startcmds{margin:6px 0;font-size:11px;cursor:default}.reportview{width:100%;max-width:1100px;margin:0 auto;padding:20px 16px 48px;overflow-wrap:anywhere}.reportview h1{font-size:18px;margin:0 0 4px}.reportview .sub{color:var(--dim);font-size:12px;margin-bottom:10px}.reportview .sec{color:var(--dim);text-transform:uppercase;letter-spacing:0.08em;font-size:11px;margin:14px 0 4px}.reportview .mdpage{padding:12px 20px}@media(max-width:600px){.reportview{padding:12px 0 32px}.reportview .mdpage{padding:8px 12px}}.stats{display:flex;flex-direction:column;gap:var(--space-4);max-width:100%}.stats h2{margin:0 0 var(--space-2)}.stat-tiles{display:grid;grid-template-columns:repeat(auto-fit,minmax(100px,1fr));gap:var(--space-2)}.stat-tile{background:var(--card);border:1px solid var(--line);border-radius:var(--card-radius);padding:var(--card-pad)}.stat-tile .n{font-size:22px;font-weight:bold;color:var(--fg)}.stats-empty{display:flex;align-items:center;gap:var(--space-4);background:var(--card);border:1px solid var(--line);border-radius:var(--card-radius);padding:var(--space-4) var(--space-4) var(--space-3)}.stats-empty .pet-art{flex:none;margin-top:6px}.heat{background:var(--card);border:1px solid var(--line);border-radius:var(--card-radius);padding:var(--card-pad);min-width:0}.heat-scroll{overflow-x:auto;padding:2px 0 4px}.heatmap{border-collapse:separate;border-spacing:var(--heat-gap);width:auto;margin:0;background:none;border:0;border-radius:0;overflow:visible}.heatmap td{padding:0;border:0;vertical-align:middle;font-size:10px;line-height:1;color:var(--dim)}.heatmap .month{position:relative;height:14px}.heatmap .month span{position:absolute;left:0;bottom:3px;white-space:nowrap}.heatmap .wd{position:sticky;left:0;z-index:1;background:var(--card);padding-right:var(--space-1);white-space:nowrap}.heatmap .day,.heatmap .pad,.heat-legend .swatch{width:var(--heat-cell);min-width:var(--heat-cell);height:var(--heat-cell);border-radius:2px}.heatmap .day{position:relative;cursor:default;outline:1px solid rgba(255,255,255,0.04);outline-offset:-1px}.heatmap .day:focus-visible{outline:2px solid var(--link);outline-offset:0}.l0{background:var(--heat-0)}.l1{background:var(--heat-1)}.l2{background:var(--heat-2)}.l3{background:var(--heat-3)}.l4{background:var(--heat-4)}.heatmap .day:hover::after,.heatmap .day:focus::after{content:attr(data-tip);position:absolute;bottom:calc(100% + 6px);left:50%;transform:translateX(-50%);z-index:4;background:var(--bg);border:1px solid var(--line-hover);border-radius:var(--badge-radius);color:var(--fg);font-size:11px;padding:3px 7px;white-space:nowrap;pointer-events:none}.heatmap .day.below:hover::after,.heatmap .day.below:focus::after{bottom:auto;top:calc(100% + 6px)}.heatmap .day.tl:hover::after,.heatmap .day.tl:focus::after{left:0;transform:none}.heatmap .day.tr:hover::after,.heatmap .day.tr:focus::after{left:auto;right:0;transform:none}.heat-foot{display:flex;flex-wrap:wrap;justify-content:space-between;align-items:center;gap:var(--space-2);font-size:11px;margin-top:var(--space-1)}.heat-legend{display:inline-flex;align-items:center;gap:var(--heat-gap);color:var(--dim);margin-left:auto}.heat-legend .swatch{display:inline-block}.heat-legend .swatch:first-of-type{margin-left:var(--space-1)}.heat-legend .swatch:last-of-type{margin-right:var(--space-1)}.top-traps{background:var(--card);border:1px solid var(--line);border-radius:var(--card-radius);padding:var(--card-pad)}.top-traps ol{list-style:none;margin:0;padding:0;display:grid;gap:6px}.top-traps li{display:grid;grid-template-columns:minmax(0,14em) minmax(40px,32em) auto;justify-content:start;align-items:center;gap:var(--space-3)}.top-traps .name{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.top-traps .bar{height:6px;background:var(--inset);border-radius:3px;overflow:hidden}.top-traps .bar span{display:block;height:100%;background:var(--lobstah)}.top-traps .n{color:var(--dim);white-space:nowrap}@media(max-width:600px){.tabs{flex-wrap:wrap}.tabs a{white-space:nowrap}}</style>\n </head>\n <body>\n <!-- The page is rendered by Preact (src/components/app.ts): header, tabs, controls, the five tab pages, footer, lobs, modal overlay. -->\n <script>"use strict";(()=>{var qe,y,ys,qr,F,$s,_s,ws,kt,Ne,$e,Ss,_t,bt,yt,Kr,Ue={},Fe=[],Vr=/acit|ex(?:s|g|n|p|$)|rph|grid|ows|mnc|ntw|ine[ch]|zoo|^ord|itera/i,Ke=Array.isArray;function L(e,t){for(var s in t)e[s]=t[s];return e}function wt(e){e&&e.parentNode&&e.parentNode.removeChild(e)}function Z(e,t,s){var n,r,o,a={};for(o in t)o=="key"?n=t[o]:o=="ref"?r=t[o]:a[o]=t[o];if(arguments.length>2&&(a.children=arguments.length>3?qe.call(arguments,2):s),typeof e=="function"&&e.defaultProps!=null)for(o in e.defaultProps)a[o]===void 0&&(a[o]=e.defaultProps[o]);return je(e,a,n,r,null)}function je(e,t,s,n,r){var o={type:e,props:t,key:s,ref:n,__k:null,__:null,__b:0,__e:null,__c:null,constructor:void 0,__v:r??++ys,__i:-1,__u:0};return r==null&&y.vnode!=null&&y.vnode(o),o}function ee(e){return e.children}function Be(e,t){this.props=e,this.context=t}function V(e,t){if(t==null)return e.__?V(e.__,e.__i+1):null;for(var s;t<e.__k.length;t++)if((s=e.__k[t])!=null&&s.__e!=null)return s.__e;return typeof e.type=="function"?V(e):null}function zr(e){if(e.__P&&e.__d){var t=e.__v,s=t.__e,n=[],r=[],o=L({},t);o.__v=t.__v+1,y.vnode&&y.vnode(o),St(e.__P,o,t,e.__n,e.__P.namespaceURI,32&t.__u?[s]:null,n,s??V(t),!!(32&t.__u),r),o.__v=t.__v,o.__.__k[o.__i]=o,Ts(n,o,r),t.__e=t.__=null,o.__e!=s&&Ds(o)}}function Ds(e){if((e=e.__)!=null&&e.__c!=null)return e.__e=e.__c.base=null,e.__k.some(function(t){if(t!=null&&t.__e!=null)return e.__e=e.__c.base=t.__e}),Ds(e)}function vs(e){(!e.__d&&(e.__d=!0)&&F.push(e)&&!We.__r++||$s!=y.debounceRendering)&&(($s=y.debounceRendering)||_s)(We)}function We(){try{for(var e,t=1;F.length;)F.length>t&&F.sort(ws),e=F.shift(),t=F.length,zr(e)}finally{F.length=We.__r=0}}function Gs(e,t,s,n,r,o,a,l,u,c,d){var h,p,m,f,$,_,G=n&&n.__k||Fe,b=t.length;for(u=Jr(s,t,G,u,b),h=0;h<b;h++)(m=s.__k[h])!=null&&(p=m.__i!=-1&&G[m.__i]||Ue,m.__i=h,_=St(e,m,p,r,o,a,l,u,c,d),f=m.__e,m.ref&&p.ref!=m.ref&&(p.ref&&Dt(p.ref,null,m),d.push(m.ref,m.__c||f,m)),$==null&&f!=null&&($=f),4&m.__u?(u=xs(m,u,e),p.__e&&(p.__e=null)):typeof m.type=="function"&&_!==void 0?u=_:f&&(u=f.nextSibling),m.__u&=-7);return s.__e=$,u}function Jr(e,t,s,n,r){var o,a,l,u,c,d=s.length,h=d,p=0;for(e.__k=new Array(r),o=0;o<r;o++)(a=t[o])!=null&&typeof a!="boolean"&&typeof a!="function"?(typeof a=="string"||typeof a=="number"||typeof a=="bigint"||a.constructor==String?a=e.__k[o]=je(null,a,null,null,null):Ke(a)?a=e.__k[o]=je(ee,{children:a},null,null,null):a.constructor===void 0&&a.__b>0?a=e.__k[o]=je(a.type,a.props,a.key,a.ref?a.ref:null,a.__v):e.__k[o]=a,u=o+p,a.__=e,a.__b=e.__b+1,l=null,(c=a.__i=Yr(a,s,u,h))!=-1&&(h--,(l=s[c])&&(l.__u|=2)),l==null||l.__v==null?(c==-1&&(r>d?p--:r<d&&p++),typeof a.type!="function"&&(a.__u|=4)):c!=u&&(c==u-1?p--:c==u+1?p++:(c>u?p--:p++,a.__u|=4))):e.__k[o]=null;if(h)for(o=0;o<d;o++)(l=s[o])!=null&&(2&l.__u)==0&&(l.__e==n&&(n=V(l)),Rs(l,l));return n}function xs(e,t,s){var n,r;if(typeof e.type=="function"){for(n=e.__k,r=0;n&&r<n.length;r++)n[r]&&(n[r].__=e,t=xs(n[r],t,s));return t}e.__e!=t&&(t&&e.type&&!t.parentNode&&(t=V(e)),t=s.insertBefore(e.__e,t||null));do t=t&&t.nextSibling;while(t!=null&&t.nodeType==8);return t}function Yr(e,t,s,n){var r,o,a,l=e.key,u=e.type,c=t[s],d=c!=null&&(2&c.__u)==0;if(c===null&&l==null||d&&l==c.key&&u==c.type)return s;if(n>(d?1:0)){for(r=s-1,o=s+1;r>=0||o<t.length;)if((c=t[a=r>=0?r--:o++])!=null&&(2&c.__u)==0&&l==c.key&&u==c.type)return a}return-1}function ks(e,t,s){t[0]=="-"?e.setProperty(t,s??""):e[t]=s==null?"":typeof s!="number"||Vr.test(t)?s:s+"px"}function He(e,t,s,n,r){var o,a;e:if(t=="style")if(typeof s=="string")e.style.cssText=s;else{if(typeof n=="string"&&(e.style.cssText=n=""),n)for(t in n)s&&t in s||ks(e.style,t,"");if(s)for(t in s)n&&s[t]==n[t]||ks(e.style,t,s[t])}else if(t[0]=="o"&&t[1]=="n")o=t!=(t=t.replace(Ss,"$1")),a=t.toLowerCase(),t=a in e||t=="onFocusOut"||t=="onFocusIn"?a.slice(2):t.slice(2),e.l||(e.l={}),e.l[t+o]=s,s?n?s[$e]=n[$e]:(s[$e]=_t,e.addEventListener(t,o?yt:bt,o)):e.removeEventListener(t,o?yt:bt,o);else{if(r=="http://www.w3.org/2000/svg")t=t.replace(/xlink(H|:h)/,"h").replace(/sName$/,"s");else if(t!="width"&&t!="height"&&t!="href"&&t!="list"&&t!="form"&&t!="tabIndex"&&t!="download"&&t!="rowSpan"&&t!="colSpan"&&t!="role"&&t!="popover"&&t in e)try{e[t]=s??"";break e}catch{}typeof s=="function"||(s==null||s===!1&&t[4]!="-"?e.removeAttribute(t):e.setAttribute(t,t=="popover"&&s==1?"":s))}}function bs(e){return function(t){if(this.l){var s=this.l[t.type+e];if(t[Ne]==null)t[Ne]=_t++;else if(t[Ne]<s[$e])return;return s(y.event?y.event(t):t)}}}function St(e,t,s,n,r,o,a,l,u,c){var d,h,p,m,f,$,_,G,b,P,he,K,ge,gs,Le,vt,I=t.type;if(t.constructor!==void 0)return null;128&s.__u&&(u=!!(32&s.__u),o=[l=t.__e=s.__e]),(d=y.__b)&&d(t);e:if(typeof I=="function"){h=a.length;try{if(b=t.props,P=I.prototype&&I.prototype.render,he=(d=I.contextType)&&n[d.__c],K=d?he?he.props.value:d.__:n,s.__c?G=(p=t.__c=s.__c).__=p.__E:(P?t.__c=p=new I(b,K):(t.__c=p=new Be(b,K),p.constructor=I,p.render=Xr),he&&he.sub(p),p.state||(p.state={}),p.__n=n,m=p.__d=!0,p.__h=[],p._sb=[]),P&&p.__s==null&&(p.__s=p.state),P&&I.getDerivedStateFromProps!=null&&(p.__s==p.state&&(p.__s=L({},p.__s)),L(p.__s,I.getDerivedStateFromProps(b,p.__s))),f=p.props,$=p.state,p.__v=t,m)P&&I.getDerivedStateFromProps==null&&p.componentWillMount!=null&&p.componentWillMount(),P&&p.componentDidMount!=null&&p.__h.push(p.componentDidMount);else{if(P&&I.getDerivedStateFromProps==null&&b!==f&&p.componentWillReceiveProps!=null&&p.componentWillReceiveProps(b,K),t.__v==s.__v||!p.__e&&p.shouldComponentUpdate!=null&&p.shouldComponentUpdate(b,p.__s,K)===!1){t.__v!=s.__v&&(p.props=b,p.state=p.__s,p.__d=!1),t.__e=s.__e,t.__k=s.__k,t.__k.some(function(X){X&&(X.__=t)}),Fe.push.apply(p.__h,p._sb),p._sb=[],p.__h.length&&a.push(p),l=V(s);break e}p.componentWillUpdate!=null&&p.componentWillUpdate(b,p.__s,K),P&&p.componentDidUpdate!=null&&p.__h.push(function(){p.componentDidUpdate(f,$,_)})}if(p.context=K,p.props=b,p.__P=e,p.__e=!1,ge=y.__r,gs=0,P)p.state=p.__s,p.__d=!1,ge&&ge(t),d=p.render(p.props,p.state,p.context),Fe.push.apply(p.__h,p._sb),p._sb=[];else do p.__d=!1,ge&&ge(t),d=p.render(p.props,p.state,p.context),p.state=p.__s;while(p.__d&&++gs<25);p.state=p.__s,p.getChildContext!=null&&(n=L(L({},n),p.getChildContext())),P&&!m&&p.getSnapshotBeforeUpdate!=null&&(_=p.getSnapshotBeforeUpdate(f,$)),Le=d!=null&&d.type===ee&&d.key==null?As(d.props.children):d,l=Gs(e,Ke(Le)?Le:[Le],t,s,n,r,o,a,l,u,c),p.base=t.__e,t.__u&=-161,p.__h.length&&a.push(p),G&&(p.__E=p.__=null)}catch(X){if(a.length=h,t.__v=null,u||o!=null){if(X.then){for(t.__u|=u?160:128;l&&l.nodeType==8&&l.nextSibling;)l=l.nextSibling;o!=null&&(o[o.indexOf(l)]=null),t.__e=l}else if(o!=null)for(vt=o.length;vt--;)wt(o[vt])}else t.__e=s.__e;t.__k==null&&(t.__k=s.__k||[]),X.then||Cs(t),y.__e(X,t,s)}}else o==null&&t.__v==s.__v?(t.__k=s.__k,t.__e=s.__e):l=t.__e=Qr(s.__e,t,s,n,r,o,a,u,c);return(d=y.diffed)&&d(t),128&t.__u?void 0:l}function Cs(e){e&&(e.__c&&(e.__c.__e=!0),e.__k&&e.__k.some(Cs))}function Ts(e,t,s){for(var n=0;n<s.length;n++)Dt(s[n],s[++n],s[++n]);y.__c&&y.__c(t,e),e.some(function(r){try{e=r.__h,r.__h=[],e.some(function(o){o.call(r)})}catch(o){y.__e(o,r.__v)}})}function As(e){return typeof e!="object"||e==null||e.__b>0?e:Ke(e)?e.map(As):e.constructor!==void 0?null:L({},e)}function Qr(e,t,s,n,r,o,a,l,u){var c,d,h,p,m,f,$,_=s.props||Ue,G=t.props,b=t.type;if(b=="svg"?r="http://www.w3.org/2000/svg":b=="math"?r="http://www.w3.org/1998/Math/MathML":r||(r="http://www.w3.org/1999/xhtml"),o!=null){for(c=0;c<o.length;c++)if((m=o[c])&&"setAttribute"in m==!!b&&(b?m.localName==b:m.nodeType==3)){e=m,o[c]=null;break}}if(e==null){if(b==null)return document.createTextNode(G);e=document.createElementNS(r,b,G.is&&G),l&&(y.__m&&y.__m(t,o),l=!1),o=null}if(b==null)_===G||l&&e.data==G||(e.data=G);else{if(o=b=="textarea"&&G.defaultValue!=null?null:o&&qe.call(e.childNodes),!l&&o!=null)for(_={},c=0;c<e.attributes.length;c++)_[(m=e.attributes[c]).name]=m.value;for(c in _)m=_[c],c=="dangerouslySetInnerHTML"?h=m:c=="children"||c in G||c=="value"&&"defaultValue"in G||c=="checked"&&"defaultChecked"in G||He(e,c,null,m,r);for(c in G)m=G[c],c=="children"?p=m:c=="dangerouslySetInnerHTML"?d=m:c=="value"?f=m:c=="checked"?$=m:l&&typeof m!="function"||_[c]===m||He(e,c,m,_[c],r);if(d)l||h&&(d.__html==h.__html||d.__html==e.innerHTML)||(e.innerHTML=d.__html),t.__k=[];else if(h&&(e.innerHTML=""),Gs(t.type=="template"?e.content:e,Ke(p)?p:[p],t,s,n,b=="foreignObject"?"http://www.w3.org/1999/xhtml":r,o,a,o?o[0]:s.__k&&V(s,0),l,u),o!=null)for(c=o.length;c--;)wt(o[c]);l&&b!="textarea"||(c="value",b=="progress"&&f==null?e.removeAttribute("value"):f!=null&&(f!==e[c]||b=="progress"&&!f||b=="option"&&f!=_[c])&&He(e,c,f,_[c],r),c="checked",$!=null&&$!=e[c]&&He(e,c,$,_[c],r))}return e}function Dt(e,t,s){try{if(typeof e=="function"){var n=typeof e.__u=="function";n&&e.__u(),n&&t==null||(e.__u=e(t))}else e.current=t}catch(r){y.__e(r,s)}}function Rs(e,t,s){var n,r;if(y.unmount&&y.unmount(e),(n=e.ref)&&(n.current&&n.current!=e.__e||Dt(n,null,t)),(n=e.__c)!=null){if(n.componentWillUnmount)try{n.componentWillUnmount()}catch(o){y.__e(o,t)}n.base=n.__P=n.__n=null}if(n=e.__k)for(r=0;r<n.length;r++)n[r]&&Rs(n[r],t,s||typeof e.type!="function");s||wt(e.__e),e.__c=e.__=e.__e=void 0}function Xr(e,t,s){return this.constructor(e,s)}function Gt(e,t,s){var n,r,o,a;t==document&&(t=document.documentElement),y.__&&y.__(e,t),r=(n=typeof s=="function")?null:s&&s.__k||t.__k,o=[],a=[],St(t,e=(!n&&s||t).__k=Z(ee,null,[e]),r||Ue,Ue,t.namespaceURI,!n&&s?[s]:r?null:t.firstChild?qe.call(t.childNodes):null,o,!n&&s?s:r?r.__e:t.firstChild,n,a),Ts(o,e,a),e.props.children=null}qe=Fe.slice,y={__e:function(e,t,s,n){for(var r,o,a;t=t.__;)if((r=t.__c)&&!r.__)try{if((o=r.constructor)&&o.getDerivedStateFromError!=null&&(r.setState(o.getDerivedStateFromError(e)),a=r.__d),r.componentDidCatch!=null&&(r.componentDidCatch(e,n||{}),a=r.__d),a)return r.__E=r}catch(l){e=l}throw e}},ys=0,qr=function(e){return e!=null&&e.constructor===void 0},Be.prototype.setState=function(e,t){var s;s=this.__s!=null&&this.__s!=this.state?this.__s:this.__s=L({},this.state),typeof e=="function"&&(e=e(L({},s),this.props)),e&&L(s,e),e!=null&&this.__v&&(t&&this._sb.push(t),vs(this))},Be.prototype.forceUpdate=function(e){this.__v&&(this.__e=!0,e&&this.__h.push(e),vs(this))},Be.prototype.render=ee,F=[],_s=typeof Promise=="function"?Promise.prototype.then.bind(Promise.resolve()):setTimeout,ws=function(e,t){return e.__v.__b-t.__v.__b},We.__r=0,kt=Math.random().toString(8),Ne="__d"+kt,$e="__a"+kt,Ss=/(PointerCapture)$|Capture$/i,_t=0,bt=bs(!1),yt=bs(!0),Kr=0;var te=e=>e>999?"999+":String(e);function Zr(e){return{id:e,lane:"work",bucket:"done",repo:"",title:"",verb:"unknown",sort:0}}var Ms=new WeakMap;function xt(e,t){let s=e.dispatches||[],n=Ms.get(s);return n||Ms.set(s,n=new Map(s.map(r=>[r.id,r]))),{...t,catches:(t.catches||[]).map(r=>n.get(r)??Zr(r))}}function E(e){if(!e)return"dim";let t=e.state||"open";return t==="open"&&e.merge?"pr-"+e.merge:t==="open"&&e.tone&&e.tone!=="ok"?e.tone:"pr-"+t}var se=["deck","dispatches","traps","prs","reports","notices","stats"];function Os(e){let t=String(e||"").replace(/^#/,"");return se.includes(t)?t:"deck"}var Ct=e=>`#decision/${encodeURIComponent(e)}`;function ne(e){let t=/^#?decision\\/(.+)$/.exec(String(e||""));if(!t)return null;try{return decodeURIComponent(t[1])}catch{return null}}var Tt=(e,t,s)=>`/attachment/dispatch/${encodeURIComponent(e)}/${encodeURIComponent(t)}/${encodeURIComponent(s)}`,Ls=(e,t)=>`/attachment/trap/${encodeURIComponent(e)}/${encodeURIComponent(t)}`,Hs=e=>/\\.(png|jpe?g|gif|webp)$/i.test(e),At=(e,t)=>`/decision/${encodeURIComponent(e)}/files/${encodeURIComponent(t)}`;function Ve(e,t){let s=new Map(t.map(r=>[r.key,r])),n=[];for(let r of e)if(r.kind==="decision"){let o=s.get(r.key);if(!o)continue;n.push({kind:"decision",key:o.key,title:o.title,detail:o.detail,options:o.options,attachments:o.attachments,...o.dispatch?{dispatch:o.dispatch,lane:o.lane??"work"}:{},...o.repo?{repo:o.repo}:{},at:o.askedAt,...o.viewedAt??r.viewedAt?{viewedAt:o.viewedAt??r.viewedAt}:{}})}else r.kind==="question"&&n.push({kind:"question",key:r.key,verb:r.verb,note:r.note||r.verb,dispatch:r.id,lane:r.lane,...r.repo?{repo:r.repo}:{},at:r.at??"",...r.viewedAt?{viewedAt:r.viewedAt}:{}});return n.sort((r,o)=>o.at.localeCompare(r.at)||r.key.localeCompare(o.key))}function Ns(e){let t=(e.text??"").split(`\n`).map(s=>s.trim()).find(Boolean)??"";return[e.option,t&&(t.length>80?t.slice(0,79)+"\\u2026":t),e.files?`${e.files} file${e.files===1?"":"s"}`:""].filter(Boolean).join(" \\xB7 ")}function js(e,t){return(e.reports||[]).find(s=>s.key===`report:${t.lane}:${t.id}`)}function ve(e){return e.author==="helm"?"":e.trap?e.trap:e.author==="headless"?e.dispatch?e.dispatch.slice(0,8):"":e.author}var Ps=(e,t)=>+!!e.acked-+!!t.acked||t.filedAt.localeCompare(e.filedAt),x=e=>`/report/${encodeURIComponent(e)}`;function ke(e){let t=/^#?report\\/(.+)$/.exec(String(e||""));if(!t)return null;try{return decodeURIComponent(t[1])}catch{return null}}function Bs(e){return typeof e!="string"?!1:e===""?!0:e.length>1024||!e.startsWith("#")||/[\\s\\x00-\\x1f\\x7f]/.test(e)?!1:se.includes(e.slice(1))||ne(e)!==null||ke(e)!==null}function Us(e){let t=/^\\/report\\/([^/?#]+)\\/?$/.exec(e);if(!t)return null;try{return decodeURIComponent(t[1])}catch{return null}}var Fs=e=>`/report/${encodeURIComponent(e)}/md`,Rt=(e,t)=>`/report/${encodeURIComponent(e)}/files/${encodeURIComponent(t)}`,Mt={dispatches:[],notices:[],prs:[],stacks:[]};function Ws(e,t){if(t.kind==="dispatches"){let r=new Set(e.dispatches.map(o=>o.lane+":"+o.id));return{...e,dispatches:[...e.dispatches,...t.items.filter(o=>!r.has(o.lane+":"+o.id))]}}if(t.kind==="notices"){let r=new Set(e.notices.map(o=>o.seq));return{...e,notices:[...e.notices,...t.items.filter(o=>!r.has(o.seq))]}}let s=new Set(e.prs.map(r=>r.key)),n=new Set(e.stacks.map(r=>r.id));return{...e,prs:[...e.prs,...t.items.filter(r=>!s.has(r.key))],stacks:[...e.stacks,...t.stacks.filter(r=>!n.has(r.id))]}}function qs(e,t){if(t===Mt||!t.dispatches.length&&!t.notices.length&&!t.prs.length)return e;let s=new Set(e.dispatches.map(a=>a.lane+":"+a.id)),n=new Set(e.notices.map(a=>a.seq)),r=new Set(e.prs.map(a=>a.key)),o=new Set(e.stacks.map(a=>a.id));return{...e,dispatches:[...e.dispatches,...t.dispatches.filter(a=>!s.has(a.lane+":"+a.id))],notices:[...e.notices,...t.notices.filter(a=>!n.has(a.seq))],prs:[...e.prs,...t.prs.filter(a=>!r.has(a.key))],stacks:[...e.stacks,...t.stacks.filter(a=>!o.has(a.id))]}}function Ks(e,t,s){return Math.max(0,(e.older?.[s]??0)-t[s].length)}function Vs(e,t){return{...e,now:t.now,daemon:e.daemon&&t.daemon!==void 0?{...e.daemon,heartbeat:t.daemon}:e.daemon,helms:e.helms.map(s=>t.helms[s.grounds]!==void 0?{...s,heartbeatAt:t.helms[s.grounds]}:s),traps:e.traps.map(s=>t.traps[s.trapId]?{...s,...t.traps[s.trapId]}:s)}}function Is(e,t,s){return!!e&&s-Date.parse(e)>t}function Es(e,t){if(t.lane&&e.lane!==t.lane||t.repo&&e.repo!==t.repo||t.verb&&e.verb!==t.verb)return!1;if(t.q){let s=t.q.toLowerCase();if(!(e.id+" "+(e.note||"")+" "+(e.title||"")+" "+(e.repo||"")+" "+(e.for||"")).toLowerCase().includes(s))return!1}return!0}function re(e,t,s){if(!t)return null;if(t.type==="helm")return e.helms.find(r=>r.grounds===t.key)||null;if(t.type==="dispatch"){let r=e.dispatches.find(a=>a.lane+":"+a.id===t.key),o=s&&s.key===t.key?s.data:void 0;return r&&o?{...r,...o}:r??o??null}if(t.type==="pr")return(e.prs||[]).find(r=>r.key===t.key)||null;if(t.type==="settings")return{attentionKinds:e.attentionKinds||[],attentionError:e.attentionError};let n=e.traps.find(r=>r.trapId===t.key);return n?xt(e,n):null}function zs(e,t,s){let n=t.st,r=String(n.q||"").toLowerCase(),o=(...f)=>!r||f.join(" ").toLowerCase().includes(r),a=f=>({x:f,stale:Is(f.heartbeatAt,18e5,s)}),l=re(e,t.modal,t.detail),u=(f,$)=>!!f&&s-Date.parse(f)<=$,c=f=>Math.max(0,...(f.notices||[]).filter($=>$.kind==="trap-stowed"||$.kind==="trap-ghosted").map($=>Date.parse($.at)||0)),d=f=>f.live||!!f.starting||!!f.requested,h=[...e.traps||[]].sort((f,$)=>Number(d($))-Number(d(f))||Number($.live)-Number(f.live)||(f.live?(Date.parse(f.signedOnAt||"")||0)-(Date.parse($.signedOnAt||"")||0):c($)-c(f))||(f.name??f.trapId).localeCompare($.name??$.trapId)),p=h.filter(f=>(d(f)||(f.notices||[]).some($=>($.kind==="trap-stowed"||$.kind==="trap-ghosted")&&u($.at,36e5)))&&o(f.name,f.trapId,f.repo,f.worktree)),m=({ageSecs:f,...$})=>$;return{chips:{daemon:e.daemon,daemonStale:!!e.daemon&&Is(e.daemon.heartbeat,9e4,s),helms:e.helms.map(a),stats:e.stats},deck:{view:n.view,decisions:Ve(e.attention||[],e.decisions||[]).filter(f=>f.kind==="decision"?o(f.kind,f.repo,f.title,f.dispatch,f.detail):o(f.kind,f.repo,f.note,f.dispatch)),prAttention:(e.attention||[]).filter(f=>f.kind&&f.kind.startsWith("pr:")).map(m),landed:(e.landed||[]).filter(f=>u(f.at,864e5)&&o(f.repo,f.note,f.id)).sort((f,$)=>Date.parse($.at)-Date.parse(f.at)).slice(0,8),reports:(e.reports||[]).filter(f=>o(f.title,f.author,f.dispatch,f.repo)).sort(Ps),inflight:e.dispatches.filter(f=>f.bucket!=="done"&&Es(f,{...n,lane:"",repo:"",verb:""})),traps:p.map(f=>a(xt(e,f))),stacks:(e.stacks||[]).filter(f=>f.open&&o(f.repo,f.numbers.join(" "))),prs:(e.prs||[]).filter(f=>f.state==="OPEN"),error:e.attentionError},dispatches:{view:n.view,chain:n.chain,list:e.dispatches.filter(f=>Es(f,n))},traps:{view:n.view,list:h.filter(f=>(!n.repo||f.repo===n.repo)&&o(f.name,f.trapId,f.repo,f.worktree,f.harness)).map(f=>a(xt(e,f)))},prs:{view:n.view,stacks:(e.stacks||[]).filter(f=>!n.repo||f.repo===n.repo),prs:(e.prs||[]).filter(f=>(!n.repo||f.repo===n.repo)&&o(f.number,f.title,f.url,f.state,f.baseRefName,f.headRefName)),watches:(e.watches||[]).filter(f=>!String(f.key).startsWith("pr:"))},reports:{view:n.view,list:(e.reports||[]).filter(f=>(!n.repo||f.repo===n.repo)&&o(f.title,f.author,f.dispatch,f.repo)).sort(Ps)},notices:{list:(e.notices||[]).filter(f=>(!n.repo||!f.repo||f.repo===n.repo)&&(!n.noticeKind||f.kind===n.noticeKind)&&o(f.kind,f.text,f.repo))},foot:{version:e.version,repoUrl:e.repoUrl},modal:{modal:t.modal,item:l&&a(l),prefs:t.modal&&t.modal.type==="settings"?{view:n.view,lobs:n.lobs}:void 0}}}function Js(e,t){let s=(e.prs||[]).find(l=>l.key===t);if(!s)return null;let n=(e.stacks||[]).find(l=>l.id===s.stackId),r=new Map((e.dispatches||[]).map(l=>[l.id,l])),o=(s.dispatchIds||[]).map(l=>{let u=r.get(l);return u?{id:l,verb:u.verb,modalKey:u.lane+":"+u.id}:{id:l,culled:!0}}),a=s.watch;return{pr:s,stack:n?{numbers:n.numbers,titles:n.numbers.map(l=>(e.prs||[]).find(u=>u.stackId===n.id&&u.number===l)?.title||""),position:s.position+1,size:n.numbers.length,floor:n.floor,nextNumber:n.nextNumber,nextMergeable:!!s.nextMergeable,blockedBy:s.blockedBy}:null,chain:o,watch:a?{key:a.key,owner:a.owner||"",lastCheckedAt:a.lastCheckedAt||null,lastError:a.lastError||null,cursor:a.cursor}:null}}function Ys(e){return e?{text:e.lastError?"failing":"watching",at:e.lastCheckedAt||null}:{text:"no watch",at:null}}function ze(e,t){return e.filter(s=>!t(s.key)).sort((s,n)=>s.at.localeCompare(n.at)||s.key.localeCompare(n.key))}function Qs(e,t,s){return ze(e,t).filter(n=>!n.viewedAt&&!s(n.key))}function Xs(){let e={view:"table",lane:"",repo:"",verb:"",q:"",lobs:!0,chain:!1,noticeKind:""};try{Object.assign(e,JSON.parse(localStorage.getItem("spyglass")||"{}"))}catch{}return e.view!=="table"&&e.view!=="cards"&&(e.view="table"),e.lobs=e.lobs!==!1,e}function Zs(e){try{localStorage.setItem("spyglass",JSON.stringify(e))}catch{}}function en(){try{return JSON.parse(localStorage.getItem("spyglass-lob-hidden")||"{}")||{}}catch{return{}}}function tn(e){try{localStorage.setItem("spyglass-lob-hidden",JSON.stringify(e))}catch{}}var be,Je;function oe(e){if(e.snapshot)return(Je?.snapshot!==e.snapshot||Je.older!==e.older)&&(Je={snapshot:e.snapshot,older:e.older,view:qs(e.snapshot,e.older)}),Je.view}var sn=new Set,g=()=>be;function Pt(e){be=e}function k(e){be={...be,...e};for(let t of sn)t(be)}var It=e=>{sn.add(e)};function eo(e,t){let s=oe(e);return s&&re(s,t,e.detail)?!0:t.type==="dispatch"&&e.detail?.key===t.key&&!e.detail.error}function Ye(e){let{modal:t,drafts:s,older:n,detail:r,decisionModal:o}=g(),a=Ve(e.attention||[],e.decisions||[]),l=new Set(a.map(c=>c.key)),u=Object.fromEntries(Object.entries(s).filter(([c])=>l.has(c)));k({snapshot:e,stale:!1,modal:t&&eo({snapshot:e,older:n,detail:r},t)?t:null,...Object.keys(u).length!==Object.keys(s).length?{drafts:u}:{},...o?.key&&!l.has(o.key)&&!s[o.key]?.sent?{decisionModal:nn(a,u,o.key)}:{}})}function nn(e,t,s){let n=ze(e,o=>!!t[o]?.sent||o===s);if(!n.length)return null;let r=e.find(o=>o.key===s)?.at??"";return{key:(n.find(o=>o.at>r||o.at===r&&o.key>(s??""))??n[0]).key}}function Qe(){let e=oe(g());return e?Ve(e.attention||[],e.decisions||[]):[]}function ye(){let{drafts:e}=g();return ze(Qe(),t=>!!e[t]?.sent)}function Xe(){let{drafts:e,viewedHere:t}=g();return Qs(Qe(),s=>!!e[s]?.sent,s=>!!t[s])}function ae(e){let t=e??Xe()[0]?.key??ye()[0]?.key;t&&k({decisionModal:{key:t}})}function _e(e){let t=g().decisionModal;if(!t?.key)return;let s=ye(),n=s.findIndex(o=>o.key===t.key),r=s[n+e];n>=0&&r&&k({decisionModal:{key:r.key}})}var ie=()=>k({decisionModal:null}),we=9;function to(){let e=g().decisionModal?.key;return e?ye().find(t=>t.key===e):void 0}function rn(e){let t=to();if(!t||e<1||e>we)return!1;let s=g().drafts[t.key];if(s?.sending||s?.sent)return!1;let n=t.kind==="decision"?t.options:[];return e<=n.length?(A(t.key,{option:n[e-1],error:void 0}),document.querySelectorAll(".dmodal .dopt")[e-1]?.focus(),!0):e===n.length+1?(document.querySelector(".dmodal textarea.danswer")?.focus(),!0):!1}async function on(e){let t=g();if(t.viewedHere[e]||(k({viewedHere:{...t.viewedHere,[e]:!0}}),Qe().find(n=>n.key===e)?.viewedAt))return;let s=t.snapshot?.focusToken;if(s)try{await fetch("/requests",{method:"POST",headers:{"content-type":"application/json","x-lobstah-token":s},body:JSON.stringify({kind:"decision-viewed",payload:{key:e}})})}catch{}}function an(){k({alertDismissed:Xe().map(e=>e.key)})}function ln(e){let{snapshot:t}=g();k({stale:!1,...t?{snapshot:Vs(t,e)}:{}})}async function Ot(){let{modal:e}=g();if(e?.type!=="dispatch")return;let t=e.key,s=t.slice(t.indexOf(":")+1),n;try{let o=await fetch(`/data/dispatch/${encodeURIComponent(s)}`,{cache:"no-store"});n=o.ok?{data:await o.json()}:{error:o.status===404?"this dispatch is gone":`the detail answered ${o.status}`}}catch{n={error:"the detail could not be read"}}let r=g();r.modal?.type!=="dispatch"||r.modal.key!==t||n.error&&r.detail?.key===t&&r.detail.data||k({detail:{key:t,...n}})}var Et=!1;async function Se(){if(!Et){Et=!0;try{let e=await fetch("/data/stats",{cache:"no-store"});if(!e.ok)throw new Error(`/data/stats answered ${e.status}`);k({stats:await e.json(),statsError:null})}catch{k({statsError:"stats could not be read"})}finally{Et=!1}}}var Lt=e=>{g().statsFocus!==e&&k({statsFocus:e})};async function dn(e){let{olderLoading:t,older:s}=g();if(!t){k({olderLoading:e,olderError:null});try{let n=await fetch(`/data/older?kind=${e}&offset=${s[e].length}&limit=50`,{cache:"no-store"});if(!n.ok)throw new Error(`/data/older answered ${n.status}`);let r=await n.json();k({older:Ws(g().older,r),olderLoading:null})}catch{k({olderLoading:null,olderError:{kind:e,text:"older records could not be read"}})}}}var cn=()=>({text:"",files:[]});function A(e,t){let s=g().drafts;k({drafts:{...s,[e]:{...s[e]??cn(),...t}}})}function pn(e,t){let s=g().drafts[e]?.option;A(e,{option:s===t?void 0:t,error:void 0})}var so=e=>new Promise((t,s)=>{let n=new FileReader;n.onload=()=>t(String(n.result).replace(/^data:[^,]*,/,"")),n.onerror=()=>s(n.error),n.readAsDataURL(e)});async function Ht(e,t){let s=g().snapshot?.answerLimits,n=[...t??[]],r=g().drafts[e]?.files??[],o=[],a=[];for(let l of n){let u=(/\\.[^.]+$/.exec(l.name)?.[0]??"").toLowerCase();if(s&&!s.extensions.includes(u))o.push(`${l.name}: type not accepted`);else if(s&&l.size>s.maxBytes)o.push(`${l.name}: larger than ${s.maxBytes} bytes`);else if(s&&r.length+a.length>=s.maxFiles)o.push(`${l.name}: at most ${s.maxFiles} files`);else try{a.push({name:l.name,bytes:l.size,data:await so(l)})}catch{o.push(`${l.name}: could not be read`)}}A(e,{files:[...g().drafts[e]?.files??[],...a],error:o.length?o.join("; "):void 0})}var no={"image/png":".png","image/jpeg":".jpg","image/gif":".gif","image/webp":".webp"};async function un(e,t){let s=[...t.clipboardData?.items??[]],n=new Date().toISOString().replace(/[:.]/g,"-").replace(/-\\d{3}Z$/,"Z"),r=s.filter(o=>o.kind==="file"&&o.type.startsWith("image/")).map(o=>o.getAsFile()).filter(o=>o!==null).map((o,a,l)=>{let u=`pasted-${n}${l.length>1?`-${a+1}`:""}${no[o.type]??".png"}`;return new File([o],u,{type:o.type})});r.length&&await Ht(e,r)}function fn(e,t){let s=(g().drafts[e]?.files??[]).filter((n,r)=>r!==t);A(e,{files:s})}async function Ze(e){let t=g().snapshot?.focusToken,s=g().drafts[e]??cn();if(!(s.sending||s.sent)){if(!s.option&&!s.text.trim()&&s.files.length===0){A(e,{error:"Choose an option, write an answer, or attach a file."});return}if(!t){A(e,{error:"This page has no answer token; reload it."});return}A(e,{sending:!0,error:void 0});try{let n=await fetch("/requests",{method:"POST",headers:{"content-type":"application/json","x-lobstah-token":t},body:JSON.stringify({kind:"decision-answer",payload:{key:e,...s.option?{option:s.option}:{},...s.text.trim()?{text:s.text}:{},files:s.files.map(a=>({name:a.name,data:a.data}))}})}),r=await n.json();if(!n.ok||!r.ok){A(e,{sending:!1,error:r.reason??`The answer was refused (${n.status}).`});return}A(e,{sending:!1,sent:Ns({option:s.option,text:s.text,files:s.files.length})}),g().decisionModal?.key===e&&k({decisionModal:nn(Qe(),g().drafts,e)??{key:null}})}catch{A(e,{sending:!1,error:"The answer could not be sent."})}}}var mn=()=>k({stale:!0}),hn=(e,t)=>k({lightbox:{src:e,name:t}}),le=()=>k({lightbox:null});function O(e,t){let s=g(),n={type:e,key:t};if(e==="dispatch"){k({modal:n,detail:s.detail?.key===t?s.detail:{key:t}}),Ot();return}let r=oe(s);k({modal:!r||re(r,n)?n:null,detail:null})}function de(){k({modal:null,detail:null})}async function gn(e){let t=g().snapshot?.focusToken;if(t)try{let n=await(await fetch(`/api/focus/${encodeURIComponent(e)}`,{method:"POST",headers:{"x-lobstah-focus-token":t}})).json();k({focusResults:{...g().focusResults,[e]:n.message??n.reason??"Window focus failed."}})}catch{k({focusResults:{...g().focusResults,[e]:"Window focus failed."}})}}async function $n(e,t){let s=g().snapshot?.focusToken;if(!s)return"The glass has no page token yet.";try{let r=await(await fetch("/requests",{method:"POST",headers:{"content-type":"application/json","x-lobstah-token":s},body:JSON.stringify({kind:"trap-request",payload:{repo:e,harness:t}})})).json();if(!r.ok)return r.reason??"The request was refused."}catch{return"The request could not be sent."}try{Ye(await(await fetch("/data")).json())}catch{}}function H(e){let t={...g().prefs,...e};Zs(t),k({prefs:t})}function vn(e){e!=="table"&&e!=="cards"||H({view:e})}var kn=e=>H({lobs:e==="on"});function bn(e,t){let s={...g().lobHidden,[e]:t};tn(s),setTimeout(()=>k({lobHidden:s}),0)}async function yn(e){try{return await navigator.clipboard.writeText(e),!0}catch{return!1}}var wn=function(e,t,s,n){var r;t[0]=0;for(var o=1;o<t.length;o++){var a=t[o++],l=t[o]?(t[0]|=a?1:2,s[t[o++]]):t[++o];a===3?n[0]=l:a===4?n[1]=Object.assign(n[1]||{},l):a===5?(n[1]=n[1]||{})[t[++o]]=l:a===6?n[1][t[++o]]+=l+"":a?(r=e.apply(l,wn(e,l,s,["",null])),n.push(r),l[0]?t[0]|=2:(t[o-2]=0,t[o]=r)):n.push(l)}return n},_n=new Map;function Sn(e){var t=_n.get(this);return t||(t=new Map,_n.set(this,t)),(t=wn(this,t.get(e)||(t.set(e,t=(function(s){for(var n,r,o=1,a="",l="",u=[0],c=function(p){o===1&&(p||(a=a.replace(/^\\s*\\n\\s*|\\s*\\n\\s*$/g,"")))?u.push(0,p,a):o===3&&(p||a)?(u.push(3,p,a),o=2):o===2&&a==="..."&&p?u.push(4,p,0):o===2&&a&&!p?u.push(5,0,!0,a):o>=5&&((a||!p&&o===5)&&(u.push(o,0,a,r),o=6),p&&(u.push(o,p,0,r),o=6)),a=""},d=0;d<s.length;d++){d&&(o===1&&c(),c(d));for(var h=0;h<s[d].length;h++)n=s[d][h],o===1?n==="<"?(c(),u=[u],o=3):a+=n:o===4?a==="--"&&n===">"?(o=1,a=""):a=n+a[0]:l?n===l?l="":a+=n:n===\'"\'||n==="\'"?l=n:n===">"?(c(),o=1):o&&(n==="="?(o=5,r=a,a=""):n==="/"&&(o<5||s[d][h+1]===">")?(c(),o===3&&(u=u[0]),o=u,(u=u[0]).push(2,0,o),o=0):n===" "||n===" "||n===`\n`||n==="\\r"?(c(),o=2):a+=n),o===3&&a==="!--"&&(o=4,u=u[0])}return c(),u})(e)),t),arguments,[])).length>1?t:t[0]}var Dn=e=>Array.isArray(e)?e.map(Dn):e===""?null:e,ro=(e,t,...s)=>Z(e,t,...s.map(Dn)),i=Sn.bind(ro);var De,w,Nt,Gn,tt=0,In=[],D=y,xn=D.__b,Cn=D.__r,Tn=D.diffed,An=D.__c,Rn=D.unmount,Mn=D.__;function Bt(e,t){D.__h&&D.__h(w,e,tt||t),tt=0;var s=w.__H||(w.__H={__:[],__h:[]});return e>=s.__.length&&s.__.push({}),s.__[e]}function z(e){return tt=1,oo(On,e)}function oo(e,t,s){var n=Bt(De++,2);if(n.t=e,!n.__c&&(n.__=[s?s(t):On(void 0,t),function(l){var u=n.__N?n.__N[0]:n.__[0],c=n.t(u,l);u!==c&&(n.__N=[c,n.__[1]],n.__c.setState({}))}],n.__c=w,!w.__f)){var r=function(l,u,c){if(!n.__c.__H)return!0;var d=!1,h=n.__c.props!==l;if(n.__c.__H.__.some(function(m){if(m.__N){d=!0;var f=m.__[0];m.__=m.__N,m.__N=void 0,f!==m.__[0]&&(h=!0)}}),o){var p=o.call(this,l,u,c);return d?p||h:p}return!d||h};w.__f=!0;var o=w.shouldComponentUpdate,a=w.componentWillUpdate;w.componentWillUpdate=function(l,u,c){if(this.__e){var d=o;o=void 0,r(l,u,c),o=d}a&&a.call(this,l,u,c)},w.shouldComponentUpdate=r}return n.__N||n.__}function Ut(e,t){var s=Bt(De++,3);!D.__s&&En(s.__H,t)&&(s.__=e,s.u=t,w.__H.__h.push(s))}function Ft(e){return tt=5,Wt(function(){return{current:e}},[])}function Wt(e,t){var s=Bt(De++,7);return En(s.__H,t)&&(s.__=e(),s.__H=t,s.__h=e),s.__}function ao(){for(var e;e=In.shift();){var t=e.__H;if(e.__P&&t)try{t.__h.some(et),t.__h.some(jt),t.__h=[]}catch(s){t.__h=[],D.__e(s,e.__v)}}}D.__b=function(e){w=null,xn&&xn(e)},D.__=function(e,t){e&&t.__k&&t.__k.__m&&(e.__m=t.__k.__m),Mn&&Mn(e,t)},D.__r=function(e){Cn&&Cn(e),De=0;var t=(w=e.__c).__H;t&&(Nt===w?(t.__h=[],w.__h=[],t.__.some(function(s){s.__N&&(s.__=s.__N),s.u=s.__N=void 0})):(t.__h.some(et),t.__h.some(jt),t.__h=[],De=0)),Nt=w},D.diffed=function(e){Tn&&Tn(e);var t=e.__c;t&&t.__H&&(t.__H.__h.length&&(In.push(t)!==1&&Gn===D.requestAnimationFrame||((Gn=D.requestAnimationFrame)||io)(ao)),t.__H.__.some(function(s){s.u&&(s.__H=s.u,s.u=void 0)})),Nt=w=null},D.__c=function(e,t){t.some(function(s){try{s.__h.some(et),s.__h=s.__h.filter(function(n){return!n.__||jt(n)})}catch(n){t.some(function(r){r.__h&&(r.__h=[])}),t=[],D.__e(n,s.__v)}}),An&&An(e,t)},D.unmount=function(e){Rn&&Rn(e);var t,s=e.__c;s&&s.__H&&(s.__H.__.some(function(n){try{et(n)}catch(r){t=r}}),s.__H=void 0,t&&D.__e(t,s.__v))};var Pn=typeof requestAnimationFrame=="function";function io(e){var t,s=function(){clearTimeout(n),Pn&&cancelAnimationFrame(t),setTimeout(e)},n=setTimeout(s,35);Pn&&(t=requestAnimationFrame(s))}function et(e){var t=w,s=e.__c;typeof s=="function"&&(e.__c=void 0,s()),w=t}function jt(e){var t=w;e.__c=e.__(),w=t}function En(e,t){return!e||e.length!==t.length||t.some(function(s,n){return s!==e[n]})}function On(e,t){return typeof t=="function"?t(e):t}var lo=["none","minimal","low","medium","high","xhigh","max","ultra"],co=["default","acceptEdits","plan","dontAsk","bypassPermissions","auto"];function Ln(e){return typeof e=="string"&&/^[a-zA-Z0-9][a-zA-Z0-9._:/-]{0,127}$/.test(e)?e:null}function Hn(e,t){return typeof e=="string"&&t.includes(e)?e:null}function qt(e={}){return{harness:Ln(e.harness),model:Ln(e.model),config:{effort:Hn(e.config?.effort,lo),permissionMode:Hn(e.config?.permissionMode,co)},...typeof e.observedAt=="string"&&/^\\d{4}-\\d\\d-\\d\\dT\\d\\d:\\d\\d:\\d\\d\\.\\d{3}Z$/.test(e.observedAt)&&Number.isFinite(Date.parse(e.observedAt))?{observedAt:e.observedAt}:{}}}var J=e=>{if(!e)return"";let t=Math.max(0,(Date.now()-Date.parse(e))/1e3);return t<90?Math.round(t)+"s":t<5400?Math.round(t/60)+"m":t<172800?(t/3600).toFixed(1)+"h":Math.round(t/86400)+"d"},v=e=>i`<span data-age=${e??""}>${J(e)}</span>`;function N({worker:e,label:t,cls:s="badge"}){let n=qt(e),r=[n.model??"model unknown",n.config.effort].filter(Boolean).join(" \\xB7 "),[o,a]=z(null),l=u=>{let c=u.currentTarget.getBoundingClientRect();a({left:Math.max(8,Math.min(c.left,window.innerWidth-280)),top:c.bottom+6})};return i`<span class=${s+" worker-harness"} tabindex="0" aria-label=${r}\n onMouseEnter=${l} onFocus=${l} onBlur=${()=>a(null)}\n onMouseLeave=${u=>{document.activeElement!==u.currentTarget&&a(null)}}\n onKeyDown=${u=>{u.key==="Escape"&&a(null)}}>\n ${t??n.harness??"unknown harness"}${o&&!g().modal&&i`<span class="worker-tooltip" role="tooltip" aria-hidden="true" style=${o}>${r}</span>`}\n </span>`}var Kt=e=>e.worker&&i`<${N} worker=${e.worker} />`;function nt({worker:e,headless:t=!1}){let s=qt(e);return i`<section class="worker-details"><div class="sec">${t?"headless worker \\xB7 launch / observation":"worker observation"}</div><dl>\n <dt>harness</dt><dd>${s.harness??"unknown"}</dd>\n <dt>model</dt><dd>${s.model??"model unknown"}</dd>\n <dt>effort</dt><dd>${s.config.effort??"unknown"}</dd>\n <dt>permission mode</dt><dd>${s.config.permissionMode??"unknown"}</dd>\n <dt>observed</dt><dd>${s.observedAt??"unknown"}</dd>\n </dl></section>`}var rt=e=>e.activity&&i`<div class=${"activity"+(e.activity.stale?" stale":"")} title=${e.activity.stale?"no activity past the wedge threshold":e.activity.kind}>${e.activity.stale?"stale \\xB7 ":""}${e.activity.summary} \xB7 ${v(e.activity.at)} ago</div>`,po=e=>e&&/^https?:\\/\\//i.test(e)?e:void 0,ot=e=>{let t=e.waiting;if(!t)return;let s=po(t.link),n=s;try{s&&(n=new URL(s).host+new URL(s).pathname.replace(/\\/$/,""))}catch{n=s}return i`<div class="waiting">${e.verb}: waiting on ${t.on} \xB7 ${v(t.since)}${s&&[" \\xB7 ",i`<a href=${s} target="_blank" rel="noopener noreferrer" onClick=${C}>${n}</a>`]}</div>`},uo=12,Nn=e=>e!==void 0&&e.length>uo,j=e=>Nn(e)?e:void 0,B=e=>Nn(e)?" long":"",S=(e,t)=>()=>O(e,t),C=e=>e.stopPropagation(),fo=e=>e.sessionId?(e.harness==="codex"?"codex resume ":"claude --resume ")+e.sessionId:void 0;function Y(e){if(e.requested)return i`<span class="dim">Requested \u2014 no window yet</span>`;if(e.starting)return i`<span class="dim">${e.starting.failedAt?"Did not start":"Starting \\u2014 no window yet"}</span>`;if(!e.live){let n=fo(e);return n?i`<span class="dim">Resume: <code onClick=${C}>${n}</code></span>`:i`<span class="dim">Resume command unavailable</span>`}let t=g();return!t.snapshot?.focusSupported&&!e.link?i`<span class="dim">Window focus is not supported here</span>`:i`<span class="winaction"><button class="btn open" title="open this trap\'s window" onClick=${n=>{C(n),gn(e.trapId)}}>\u2197 open</button>${e.link&&[" ",i`<a href=${e.link} onClick=${C}>Session link</a>`]}${t.focusResults[e.trapId]&&[" ",i`<span class="dim">${t.focusResults[e.trapId]}</span>`]}</span>`}function M(e,t,s){return t.length?i`<div class="wrap"><table><tbody><tr>${e.map(n=>i`<th>${n}</th>`)}</tr>${t}</tbody></table></div>`:i`<div class="empty">${s}</div>`}function pe({kind:e,more:t}){return!t.left&&!t.error?null:i`<div class="older">${t.left>0&&i`<button class="btn" disabled=${t.loading} onClick=${()=>{dn(e)}}>${t.loading?"loading\\u2026":`show ${Math.min(t.left,50)} older (of ${t.left})`}</button>`}${t.error&&i` <span class="bad">${t.error}</span>`}</div>`}function mo({text:e}){let[t,s]=z(null),n=r=>async()=>{await yn(e)&&(s(r),setTimeout(()=>s(null),1e3))};return i`<div class="cmd"><code class=${t==="code"?"copied":void 0} title="click to copy" onClick=${n("code")}>${e}</code><button title="copy" onClick=${n("button")}>${t==="button"?"\\u2713":"\\u29C9"}</button></div>`}var T=(e,t)=>i`<${mo} key=${t} text=${e} />`,Ge=(e,t,s="thumb")=>i`<button\n type="button"\n class=${s}\n title=${"view "+t}\n onClick=${n=>{C(n),hn(e,t)}}\n >\n <img src=${e} alt=${t} loading="lazy" />\n </button>`,ce=(e,t)=>e.map(s=>[i`<div class="sub">${s.name} \xB7 ${s.type} \xB7 ${s.bytes} bytes</div>`,t&&Hs(s.name)&&Ge(t(s.name),s.name),T(s.path)]),jn=/\\bwt:([a-z0-9][a-z0-9-]*)/g,Vt=e=>g().snapshot?.trapNames?.[e],U=e=>e.replace(jn,(t,s,n)=>{let r=Vt(s);return r&&!Bn(e,n,t.length,r)?r:t}),Bn=(e,t,s,n)=>e.slice(Math.max(0,t-n.length-2),t)===`${n} (`&&e[t+s]===")";function at(e){let t="wt:"+e,s=Vt(e)??t;return g().snapshot?.traps.some(r=>r.trapId===e)?i`<a class="trapname" href="#traps" title=${t} onClick=${r=>{r.preventDefault(),C(r),O("trap",e)}}>${s}</a>`:i`<span class="trapname" title=${t}>${s}</span>`}var st=e=>{let t=/^wt:([a-z0-9][a-z0-9-]*)$/.exec(e);return t?at(t[1]):e};function R(e,t=1/0){let s=[],n=0;for(let l of e.matchAll(jn)){l.index>n&&s.push({text:e.slice(n,l.index)});let u=Vt(l[1]);s.push(u&&Bn(e,l.index,l[0].length,u)?{text:l[0]}:{text:u??l[0],trapId:l[1]}),n=l.index+l[0].length}n<e.length&&s.push({text:e.slice(n)});let r=s.reduce((l,u)=>l+u.text.length,0),o=r>t?t-1:1/0,a=[];for(let l of s){if(o<=0)break;l.text.length<=o?a.push(l.trapId?at(l.trapId):l.text):a.push(l.text.slice(0,o)),o-=l.text.length}return r>t&&a.push("\\u2026"),a}var ho=e=>e.log.flatMap((t,s)=>[s?`\n`:"",t.at+" "+t.verb,...t.note?[" ",...R(t.note)]:[]]),go=e=>"brief"in e;function Un(e,t){let s=[e.elapsed&&`elapsed: ${e.elapsed}`,e.attempt&&`attempt: ${e.attempt}`,e.branch&&`branch: ${e.branch}`,e.lastCommit&&`last commit: ${e.lastCommit}`,e.aheadTrunk&&`commits: ${e.aheadTrunk}`,e.draftPr&&`draft PR: ${e.draftPr}`,e.updated&&`updated: ${e.updated}`].filter(Boolean).join(`\n`),n=i`${e.waiting&&[i`<div class="sec">waiting</div>`,ot(e)]}${e.activity&&[i`<div class="sec">activity</div>`,rt(e)]}${s&&[i`<div class="sec">progress</div>`,i`<pre>${s}</pre>`]}`,r=e.evidence&&{...e.evidence};return r?.worker&&(delete r.worker,delete r.harness),go(e)?i`${n}<div class="sec">brief</div><pre>${e.brief}</pre>${e.attachments.length>0&&[i`<div class="sec">attachments (${e.attachments.length})</div>`,ce(e.attachments,o=>Tt(e.lane,e.id,o))]}${e.messageAttachments.length>0&&[i`<div class="sec">message attachments (${e.messageAttachments.length})</div>`,ce(e.messageAttachments,o=>Tt(e.lane,e.id,o))]}${e.followUp&&[i`<div class="sec">forks</div>`,i`<pre>${e.followUp}</pre>`]}<div class="sec">log</div><div class="loglines">${e.log.length?ho(e):"no entries yet"}</div>${e.inbox.length>0&&[i`<div class="sec">inbox</div>`,i`<div class="loglines">${e.inbox.join(`\n---\n`)}</div>`]}${e.awaitingReply&&[i`<div class="sec">awaiting reply</div>`,i`<div>${e.awaitingReply.line} \xB7 from ${e.awaitingReply.from} \xB7 ${v(e.awaitingReply.sentAt)} ago</div>`]}${r&&[i`<div class="sec">evidence</div>`,i`<div class="loglines">${JSON.stringify(r)}</div>`]}`:i`${n}<div class="sec">brief</div><div class=${t?.error?"bad":"dim"}>${t?.error??"loading\\u2026"}</div>${e.followUp&&[i`<div class="sec">forks</div>`,i`<pre>${e.followUp}</pre>`]}${e.note&&[i`<div class="sec">last note</div>`,i`<div class="loglines">${R(e.note)}</div>`]}`}function xe(e){return e.for?[st(e.for),e.evidence&&e.evidence.deliveredTo?[" ",i`<span class="ok">\u2713delivered</span>`]:[" ",i`<span class="warn">waiting</span>`]]:e.claimedBy?st(e.claimedBy):""}function ue(e){if(e.prList&&e.prList.length>1)return[e.prList.map((r,o)=>[o?" \\u2192 ":"",i`<a href=${r.url} target="_blank" rel="noopener" onClick=${C}>#${r.number}</a>`,r.badge&&[" ",i`<span class=${"badge "+E(r.badge)}>${r.badge.text}</span>`]]),e.prGate&&[" ",i`<span class="badge dim" title="merge gate (pick)">${e.prGate}</span>`]];let t=e.evidence&&(e.evidence.prUrl||e.evidence.pr&&e.evidence.pr.url);if(!t)return"";let s=e.prBadge,n=e.evidence&&e.evidence.prUrls&&e.evidence.prUrls.length>1?e.evidence.prUrls:void 0;return[n?n.map((r,o)=>[o?" ":"",i`<a href=${r} target="_blank" rel="noopener" onClick=${C}>#${r.split("/").pop()}</a>`]):i`<a href=${t} target="_blank" rel="noopener" onClick=${C}>PR</a>`,s&&[" ",i`<span class=${"badge "+E(s)} title=${"observed "+s.observedAt}>${s.text}</span>`],e.prGate&&[" ",i`<span class="badge dim" title="merge gate (pick)">${e.prGate}</span>`]]}var $o={"pr:draft":"draft","pr:review":"review","pr:checks":"checks","pr:conflict":"conflicts","pr:ready":"ready","stack-ready":"stack ready",landed:"landed",watch:"watch"},zt={"pr:review":"bad","pr:checks":"bad","pr:conflict":"pr-conflicts","pr:ready":"ok","stack-ready":"ok","pr:draft":"dim",watch:"warn"},Fn=e=>$o[e]||"";var it=e=>i`<a href=${e.url} target="_blank" rel="noopener" onClick=${C}>#${e.number}</a>`,lt=e=>i`<span class="prname" title=${e.title||void 0}><b>#${e.number}</b>${e.title&&[" ",e.title]}</span>`;function fe(e,t,s){return e.flatMap((n,r)=>{let o=t.get(n)||void 0,a=n===s?i`<b title=${o}>#${n}</b>`:i`<span title=${o}>#${n}</span>`;return r?[" \\u2192 ",a]:[a]})}function Ce(e){let t=e.checks;return t.passed+"/"+t.total+" passed"+(t.failed?" \\xB7 "+t.failed+" failed":"")+(t.pending?" \\xB7 "+t.pending+" pending":"")}function dt(e){let t=e.review||{};return(e.reviewDecision||"")+(t.unresolvedThreads?" \\xB7 "+t.unresolvedThreads+" unresolved":"")+(t.changesRequested?" \\xB7 changes requested":"")}function Te(e){return[e.mergeStateStatus,e.nextMergeable?[" \\xB7 ",i`<span class="ok">next mergeable</span>`]:e.blockedBy?" \\xB7 blocked by #"+e.blockedBy:""]}function Jt(e){let t=Ys(e);return e?[t.text," \\xB7 ",t.at?[v(t.at)," ago"]:"never checked"]:i`<span class="dim">${t.text}</span>`}function Ae(e){if(e.requested)return{stale:!1,listen:[i`<span class="dot"></span>`,i`<span class="dim">requested</span>`],hb:i`<span class="dim">\u2014</span>`};if(e.starting)return{stale:!1,listen:e.starting.failedAt?[i`<span class="dot bad"></span>`,i`<span class="bad">start failed</span>`]:[i`<span class="dot warn"></span>`,"starting"],hb:i`<span class="dim">\u2014</span>`};if(!e.live)return{stale:!1,listen:[i`<span class="dot"></span>`,i`<span class="dim">signed off</span>`],hb:i`<span class="dim">\u2014</span>`};let t=Date.now()-Date.parse(e.heartbeatAt)>18e5,s=e.listening??(!!e.firstParkedAt&&!t);return{stale:t,listen:s?[i`<span class="dot ok"></span>`,"listening"]:[i`<span class="dot warn"></span>`,"not listening"],hb:i`<span class=${t?"bad":"ok"}>${t&&"stale "}${v(e.heartbeatAt)} ago</span>`}}function Yt(e){if(e.requested)return{kind:"requested",helmOn:!!g().snapshot?.helmOn};if(e.starting)return e.starting.failedAt?{kind:"start failed",reason:e.starting.reason??"no session signed on in time"}:{kind:"starting",deadline:e.starting.deadline};if(!e.live)return{kind:"signed off"};let t=e.claimed?e.catches.find(s=>s.id===e.claimed&&s.bucket==="active"):void 0;return t?t.waiting||t.verb==="paused"?{kind:"parked",current:t}:{kind:"working",current:t}:{kind:"idle",listening:e.listening??(!!e.firstParkedAt&&Date.now()-Date.parse(e.heartbeatAt??"")<=18e5)}}function vo(e){let t=Yt(e);return t.kind==="working"||t.kind==="idle"&&t.listening?"ok":t.kind==="start failed"?"bad":t.kind==="parked"||t.kind==="starting"?"warn":"dim"}var Wn=e=>e.waiting?`waiting on ${e.waiting.on}`:e.note||"waiting",qn=e=>e.title.slice(0,40)||"(no title)";function Re(e){let t=Yt(e),s=i`<span class=${"dot trapdot "+vo(e)}></span>`;if(t.kind==="requested")return[s,i`<span class="dim">${Kn(e,t.helmOn)}</span>`];if(t.kind==="starting")return[s,"starting \\xB7 waiting for its session to sign on"];if(t.kind==="start failed")return[s,i`<span class="bad">start failed</span>`," \\xB7 ",t.reason];if(t.kind==="signed off")return[s,i`<span class="dim">signed off</span>`];if(t.kind==="idle")return[s,"idle \\xB7 ",t.listening?"listening":"not listening"];let n=t.current;return t.kind==="parked"?[s,"parked \\xB7 ",Wn(n)]:[s,"working \\xB7 ",i`<a href="#dispatches" onClick=${o=>{o.preventDefault(),C(o),O("dispatch",`${n.lane}:${n.id}`)}}>${n.id.slice(0,8)}</a>`," \\xB7 ",qn(n),n.activity&&[" \\xB7 ",n.activity.summary," ",v(n.activity.at)," ago"]]}var Kn=(e,t)=>`requested \\xB7 ${e.repo??""} \\xB7 ${e.harness??""} \\xB7 ${t?"waiting for the helm":"waiting for a helm"}`;function Q(e){let t=e.starting?.commands;return t?.length?i`<div class="startcmds" onClick=${C}><div class="dim">start it: run one in a terminal</div>${t.map(s=>T(s.command,s.harness))}</div>`:null}function ct(){let t=g().snapshot?.repoKeys??[],[s,n]=z(!1),[r,o]=z(""),[a,l]=z("claude"),[u,c]=z(null),d=r||t[0]||"",h=async p=>{p.preventDefault();let m=await $n(d,a);c(m??null),m||n(!1)};return s?i`<form class="newtrap" onClick=${C} onSubmit=${h}>\n <label>repo <select name="repo" value=${d} onChange=${p=>o(p.target.value)}>${t.map(p=>i`<option value=${p}>${p}</option>`)}</select></label>\n <label>harness <select name="harness" value=${a} onChange=${p=>l(p.target.value)}><option value="claude">claude</option><option value="codex">codex</option></select></label>\n <button class="btn" type="submit" disabled=${!d}>Request</button>\n <button class="btn" type="button" onClick=${()=>n(!1)}>Cancel</button>\n ${u&&i`<span class="bad">${u}</span>`}\n </form>`:i`<span class="newtrap"><button class="btn" onClick=${p=>(C(p),n(!0),c(null))}>+ New trap</button></span>`}function pt(e){let t=Yt(e);if(t.kind==="requested")return Kn(e,t.helmOn);if(t.kind==="starting")return`starting \\xB7 sign-on due by ${t.deadline}`;if(t.kind==="start failed")return`start failed \\xB7 ${t.reason}`;if(t.kind==="signed off")return"signed off";if(t.kind==="idle")return`idle \\xB7 ${t.listening?"listening":"not listening"}`;if(t.kind==="parked")return`parked \\xB7 ${Wn(t.current)}`;let s=t.current;return[`working \\xB7 ${s.id.slice(0,8)} \\xB7 ${qn(s)}`,s.activity&&`${s.activity.summary} ${J(s.activity.at)} ago`].filter(Boolean).join(" \\xB7 ")}function Qt(e){let t=e.messages.filter(s=>s.state==="pending").length;return e.messages.length?["\\u2709 "+e.messages.length,t>0&&[" ",i`<span class="warn">(${t} pending)</span>`]]:null}var ko=e=>()=>{window.open(x(e.key),"_blank","noopener")};function Xt(e){let t=ve(e);return{nodes:[t&&t+" \\xB7 ",v(e.filedAt)," ago",e.acked?" \\xB7 acked":""],text:[t,`${J(e.filedAt)} ago`,e.acked?"acked":""].filter(Boolean).join(" \\xB7 ")}}function bo(e){let t=Xt(e);return i`<a key=${e.key} class=${"card"+(e.acked?" acked":"")} href=${x(e.key)} target="_blank" rel="noopener"><div class="top"><b title=${e.title}>${e.title}</b></div><div class="meta" title=${t.text}>${t.nodes}</div></a>`}function yo(e){return i`<tr key=${e.key} class=${"rowhead"+(e.acked?" acked":"")} onClick=${ko(e)}><td class="grow"><a href=${x(e.key)} target="_blank" rel="noopener" onClick=${t=>t.stopPropagation()}>${e.title}</a></td><td>${ve(e)}</td><td>${v(e.filedAt)}</td><td>${e.acked?"acked":""}</td></tr>`}function Vn({inp:e}){return e.view==="cards"?e.list.length?i`<div class="cards">${e.list.map(bo)}</div>`:i`<div class="empty">no reports match</div>`:M(["title","from","filed","acked"],e.list.map(yo),"no reports match")}function Yn(e){let t=e.trim();return/^(https?:\\/\\/|mailto:|#)/i.test(t)?t:void 0}function Qn(e){let t=e.trim();return/^[^/\\\\:?#]+$/.test(t)&&t!=="."&&t!==".."?t:void 0}function zn(e,t,s,n){let r=0;for(let o=t;o<e.length;o++){if(e[o]==="\\\\"){o++;continue}if(e[o]===s)r++;else if(e[o]===n){if(r===0)return o;r--}}return-1}function Jn(e,t){let s=zn(e,t+1,"[","]");if(s<0||e[s+1]!=="(")return;let n=zn(e,s+2,"(",")");if(n<0)return;let r=e.slice(s+2,n).trim().replace(/\\s+"[^"]*"$/,"").replace(/^<(.*)>$/,"$1");return{text:e.slice(t+1,s),target:r,end:n+1}}function W(e){let t=[],s="",n=()=>{s&&t.push({t:"text",v:s}),s=""};for(let r=0;r<e.length;){let o=e[r];if(o==="\\\\"&&r+1<e.length&&/[\\\\`*_{}[\\]()#+\\-.!|<>~]/.test(e[r+1])){s+=e[r+1],r+=2;continue}if(o==="`"){let a=/^`+/.exec(e.slice(r))[0],l=e.indexOf(a,r+a.length);if(l>0){n(),t.push({t:"code",v:e.slice(r+a.length,l).replace(/^ (.*) $/,"$1")}),r=l+a.length;continue}}if(o==="!"&&e[r+1]==="["){let a=Jn(e,r+1);if(a){n(),t.push({t:"img",alt:a.text,src:a.target}),r=a.end;continue}}if(o==="["){let a=Jn(e,r);if(a){n(),t.push({t:"a",href:a.target,c:W(a.text)}),r=a.end;continue}}if((o==="*"||o==="_")&&e[r+1]===o){let a=e.indexOf(o+o,r+2);if(a>r+2){n(),t.push({t:"b",c:W(e.slice(r+2,a))}),r=a+2;continue}}if(o==="*"||o==="_"&&(r===0||!/\\w/.test(e[r-1]))){let a=r+1;for(;(a=e.indexOf(o,a))>0&&(e[a+1]===o||o==="_"&&/\\w/.test(e[a+1]??""));)a+=2;if(a>r+1&&e[r+1]!==" "){n(),t.push({t:"i",c:W(e.slice(r+1,a))}),r=a+1;continue}}if(o===`\n`){/ {2,}$/.test(s)||s.endsWith("\\\\")?(s=s.replace(/( {2,}|\\\\)$/,""),n(),t.push({t:"br"})):s+=" ",r++;continue}s+=o,r++}return n(),t}var Xn=/^ {0,3}(`{3,}|~{3,})\\s*([^`\\s]*)/,Zn=/^ {0,3}(#{1,6})\\s+(.*?)\\s*#*\\s*$/,er=/^ {0,3}([-*_])(\\s*\\1){2,}\\s*$/,ts=/^( {0,3})([-*+])\\s+(.*)$/,ss=/^( {0,3})(\\d{1,9})[.)]\\s+(.*)$/,ut=/^ {0,3}>\\s?(.*)$/,_o=/^\\s*\\|?\\s*:?-+:?\\s*(\\|\\s*:?-+:?\\s*)*\\|?\\s*$/;function Zt(e){let t=e.trim();t.startsWith("|")&&(t=t.slice(1)),t.endsWith("|")&&!t.endsWith("\\\\|")&&(t=t.slice(0,-1));let s=[],n="";for(let r=0;r<t.length;r++)t[r]==="\\\\"&&t[r+1]==="|"?(n+="|",r++):t[r]==="|"?(s.push(n.trim()),n=""):n+=t[r];return s.push(n.trim()),s}var es=e=>Xn.test(e)||Zn.test(e)||er.test(e)||ts.test(e)||ss.test(e)||ut.test(e);function ft(e){let t=e.replace(/\\r\\n?/g,`\n`).replace(/\\t/g," ").split(`\n`),s=[],n=0;for(;n<t.length;){let r=t[n];if(!r.trim()){n++;continue}let o=Xn.exec(r);if(o){let d=o[1],h=[];for(n++;n<t.length&&!new RegExp(`^ {0,3}${d[0]}{${d.length},}\\\\s*$`).test(t[n]);)h.push(t[n++]);n++,s.push({t:"code",lang:o[2]??"",v:h.join(`\n`)});continue}let a=Zn.exec(r);if(a){s.push({t:"h",level:a[1].length,c:W(a[2])}),n++;continue}if(er.test(r)){s.push({t:"hr"}),n++;continue}if(ut.test(r)){let d=[];for(;n<t.length&&t[n].trim()&&(ut.test(t[n])||!es(t[n]));)d.push(ut.exec(t[n])?.[1]??t[n]),n++;s.push({t:"quote",c:ft(d.join(`\n`))});continue}let l=ts.exec(r),u=ss.exec(r);if(l||u){let d=!l,h=d?ss:ts,p=[];for(;n<t.length;){let m=t[n],f=h.exec(m);if(f&&f[1].length<=3){p.push([f[3]]),n++;continue}if(!m.trim()){if(n+1<t.length&&/^ {2,}\\S/.test(t[n+1])){p.at(-1).push(""),n++;continue}break}if(/^ {2,}\\S/.test(m)){p.at(-1).push(m.replace(/^ {2,4}/,"")),n++;continue}if(es(m))break;p.at(-1).push(m.trim()),n++}s.push({t:"list",ordered:d,start:d?Number(u[2]):1,items:p.map(m=>ft(m.join(`\n`)))});continue}if(r.includes("|")&&n+1<t.length&&_o.test(t[n+1])&&t[n+1].includes("-")){let d=Zt(r),h=Zt(t[n+1]).map(m=>m.startsWith(":")&&m.endsWith(":")?"center":m.endsWith(":")?"right":m.startsWith(":")?"left":"");n+=2;let p=[];for(;n<t.length&&t[n].trim()&&t[n].includes("|");){let m=Zt(t[n++]);p.push(d.map((f,$)=>W(m[$]??"")))}s.push({t:"table",align:d.map((m,f)=>h[f]??""),head:d.map(W),rows:p});continue}let c=[];for(;n<t.length&&t[n].trim()&&!es(t[n])&&!(c.length&&/^ {0,3}(=+|-+)\\s*$/.test(t[n]));)c.push(t[n++].replace(/^ +/,""));if(c.length===0){s.push({t:"p",c:W(r.trim())}),n++;continue}s.push({t:"p",c:W(c.join(`\n`))})}return s}function q(e,t){return t.map(s=>{switch(s.t){case"text":return s.v;case"code":return i`<code>${s.v}</code>`;case"b":return i`<strong>${q(e,s.c)}</strong>`;case"i":return i`<em>${q(e,s.c)}</em>`;case"br":return i`<br />`;case"a":{let n=Yn(s.href);return n?i`<a href=${n} target="_blank" rel="noopener noreferrer">${q(e,s.c)}</a>`:q(e,s.c)}case"img":{let n=Qn(s.src);return n?Ge(e(n),s.alt||n,"mdimg"):i`<span class="dim">[image not shown: ${s.alt||s.src}]</span>`}}})}function mt(e,t){return t.map(s=>{switch(s.t){case"h":{let n=q(e,s.c);return s.level===1?i`<h1>${n}</h1>`:s.level===2?i`<h2>${n}</h2>`:s.level===3?i`<h3>${n}</h3>`:i`<h4>${n}</h4>`}case"p":return i`<p>${q(e,s.c)}</p>`;case"code":return i`<pre class="mdcode"><code>${s.v}</code></pre>`;case"hr":return i`<hr />`;case"quote":return i`<blockquote>${mt(e,s.c)}</blockquote>`;case"list":{let n=s.items.map(r=>i`<li>${mt(e,r)}</li>`);return s.ordered?i`<ol start=${s.start}>${n}</ol>`:i`<ul>${n}</ul>`}case"table":return i`<div class="mdtable"><table><thead><tr>${s.head.map((n,r)=>i`<th style=${s.align[r]?"text-align:"+s.align[r]:void 0}>${q(e,n)}</th>`)}</tr></thead><tbody>${s.rows.map(n=>i`<tr>${n.map((r,o)=>i`<td style=${s.align[o]?"text-align:"+s.align[o]:void 0}>${q(e,r)}</td>`)}</tr>`)}</tbody></table></div>`}})}var Me=new Map;function tr(e){let t=Me.get(e);return t||(t=ft(e),Me.set(e,t),Me.size>8&&Me.delete(Me.keys().next().value)),t}function wo({r:e,text:t}){let s=t?.text!==void 0?mt(n=>Rt(e.key,n),tr(t.text)):t?.error?i`<div class="bad">${t.error}</div>`:i`<div class="dim">loading\u2026</div>`;return i`<div class="mdpage" data-report=${e.key}>${s}</div>`}function sr({text:e,fileUrl:t}){return i`<div class="mdpage">${mt(t,tr(e))}</div>`}function ns(e){let t=ve(e);return[t&&t+" \\xB7 ",v(e.filedAt)," ago",e.acked&&[" \\xB7 ",i`<span class="ok">acked</span>`]]}function nr({r:e,text:t,heading:s}){return[s&&i`<div class="sec">report \xB7 ${e.title}</div>`,i`<div class="sub">${ns(e)}</div>`,i`<${wo} r=${e} text=${t} />`,e.attachments.length>0&&[i`<div class="sec">report attachments (${e.attachments.length})</div>`,ce(e.attachments,n=>Rt(e.key,n))],!e.acked&&[i`<div class="sec">ack</div>`,T("lobstah attention ack "+e.key)]]}var So=/\\.(png|jpe?g|gif|webp)$/i,Do=e=>{e.style.height="auto",e.style.height=e.scrollHeight+"px"};function Go(e,t){return t.length?i`<div class="dfiles">${t.map(s=>So.test(s.name)?i`<span key=${s.name}>${Ge(At(e,s.name),s.name,"dimg")}</span>`:i`<div key=${s.name} class="dfile"><span>${s.name}</span> <span class="dim">\xB7 ${s.type} \xB7 ${s.bytes} bytes \xB7 </span><code>${s.path}</code></div>`)}</div>`:null}function xo(e,t,s){let n=e.key,r=e.kind==="decision"?e.options:[],o=t?.text??"",a=t?.files??[],l=!!t?.sending,u=d=>d+1<=we?String(d+1):void 0,c=u(r.length);return[r.length>0&&i`<div class="doptions">${r.map((d,h)=>{let p=u(h);return i`<button key=${d} type="button" class=${"btn dopt"+(t?.option===d?" on":"")} aria-pressed=${t?.option===d?"true":"false"} aria-keyshortcuts=${p} disabled=${l} onClick=${()=>pn(n,d)}>${p&&i`<kbd class="dkey" aria-hidden="true">${p}</kbd>`}<span class="dlabel">${d}</span></button>`})}</div>`,i`<div class="danswer-wrap">${c&&i`<kbd class="dkey dkey-text" aria-hidden="true">${c}</kbd>`}<textarea\n class="danswer"\n rows="2"\n aria-keyshortcuts=${c}\n aria-label=${r.length?"Your answer, in your own words":"Your answer"}\n placeholder=${r.length?"Add to your answer, or answer in your own words":"Your answer"}\n value=${o}\n disabled=${l}\n onInput=${d=>{let h=d.currentTarget;Do(h),A(n,{text:h.value,error:void 0})}}\n onPaste=${d=>{un(n,d)}}\n ></textarea></div>`,i`<div class="dfoot">\n <label class="btn dattach" title="attach images or files">attach<input type="file" multiple accept=${s} disabled=${l} onChange=${d=>{let h=d.currentTarget;Ht(n,h.files).then(()=>{h.value=""})}} /></label>\n ${a.map((d,h)=>i`<span key=${d.name+h} class="dchip">${d.name}<button type="button" class="dx" title="remove" disabled=${l} onClick=${()=>fn(n,h)}>\xD7</button></span>`)}\n ${t?.error&&i`<span class="bad derr">${t.error}</span>`}\n <button type="button" class="btn dsend" disabled=${l} onClick=${()=>{Ze(n)}}>${l?"Sending\\u2026":"Send"}</button>\n </div>`]}var rr=e=>e.kind==="decision"?e.title:R(e.note);function Co(e,t,s){return t?.sent?"answered":e.viewedAt||s?"read":"unread"}function rs(e){let t=e.dispatch,s=e.lane??"work";return[t&&i`<a class="dlink" href="#" onClick=${r=>{r.preventDefault(),r.stopPropagation(),S("dispatch",s+":"+t)()}}>${t.slice(0,8)}</a>`,e.repo].filter(Boolean).flatMap((r,o)=>o?[" \\xB7 ",r]:[r])}function To(e,t,s,n){let r=Co(e,t,s),o=()=>ae(e.key);return i`<li key=${e.key} class=${"drow "+r+(n?" focus":"")} data-decision=${e.key}>\n <button type="button" class="dopen" onClick=${o} aria-label=${`${r}: ${e.kind==="decision"?e.title:e.note}`}>\n ${e.kind==="question"&&i`<span class=${"badge "+(e.verb==="blocked"?"bad":"warn")}>${e.verb}</span>`}\n <b class="dtitle">${rr(e)}</b>\n </button>\n <span class="dmeta dim">${rs(e)}</span>\n <span class="dage dim">${v(e.at)} ago</span>\n <span class=${"dstate "+r}>${r==="answered"?`answered \\xB7 ${t.sent}`:r}</span>\n </li>`}function or({cards:e,drafts:t,focus:s,viewedHere:n,unread:r}){let o=e.length?i`<ul class="dlist">${e.map(a=>To(a,t[a.key],!!n[a.key],a.key===s))}</ul>`:i`<div class="empty">none</div>`;return i`<section class="decisions"><h2>decisions${r>0&&i`<span class="dbadge" aria-label=${`${r} new`}>${r}</span>`}</h2>${o}</section>`}function Ao(e){let t=Math.min(e,we),s=e+1<=we?String(e+1):void 0,n=[t>0&&[i`<kbd>1</kbd>`,t>1&&["\\u2013",i`<kbd>${t}</kbd>`]," choose"],s&&[i`<kbd>${s}</kbd>`," write"],t>0&&[i`<kbd>Enter</kbd>`," on a chosen option sends"],[i`<kbd>\u2190</kbd>`," ",i`<kbd>\u2192</kbd>`," previous / next"],[i`<kbd>Esc</kbd>`," leaves the text box, then closes"],[i`<kbd>d</kbd>`," opens decisions anywhere"]].filter(Boolean);return i`<div class="dlegend dim">${n.flatMap((r,o)=>o?[" \\xB7 ",r]:[r])}</div>`}var Ro=\'a[href], button:not([disabled]), textarea:not([disabled]), input:not([disabled]), select:not([disabled]), [tabindex]:not([tabindex="-1"])\';function ar({open:e,order:t,drafts:s,extensions:n}){let r=Ft(null),o=Ft(null),a=!!e;Ut(()=>{if(a)return o.current=document.activeElement,r.current?.focus(),()=>{let m=o.current;m&&typeof m.focus=="function"&&document.contains(m)&&m.focus()}},[a]);let l=e?.key??null;if(Ut(()=>{l&&on(l)},[l]),!e)return null;let u=m=>{if(m.key!=="Tab"||!r.current)return;let f=[...r.current.querySelectorAll(Ro)];if(!f.length)return void m.preventDefault();let $=f[0],_=f[f.length-1];m.shiftKey&&(document.activeElement===$||document.activeElement===r.current)?(m.preventDefault(),_.focus()):!m.shiftKey&&document.activeElement===_&&(m.preventDefault(),$.focus())},c=l?t.find(m=>m.key===l):void 0,d=c?t.indexOf(c):-1,h=(m,f,$="")=>i`<div id="doverlay" class="open" onClick=${_=>_.target===_.currentTarget&&ie()}>\n <div class="modal dmodal" role="dialog" aria-modal="true" aria-labelledby="dmodal-title" tabindex="-1" ref=${r} onKeyDown=${u} data-decision=${$}>\n <button type="button" class="x" aria-label="close" aria-keyshortcuts="Escape" onClick=${ie}>\xD7</button>\n <h3 id="dmodal-title" class="sr-only">${m}</h3>\n ${f}\n </div>\n </div>`;if(!c)return h("All decisions answered",i`<div class="ddone" onClick=${ie}><div class="ok">\u2713 All decisions answered.</div><div class="dim">Click anywhere or press Escape to close.</div></div>`);let p=s[c.key];return h(c.kind==="decision"?c.title:c.note,i`<div class="dnav">\n <button type="button" class="btn dprev" aria-label="previous decision" aria-keyshortcuts="ArrowLeft" disabled=${d<=0} onClick=${()=>_e(-1)}>\u2039 prev</button>\n <span class="dpos" aria-live="polite">${d+1} of ${t.length}</span>\n <button type="button" class="btn dnext" aria-label="next decision" aria-keyshortcuts="ArrowRight" disabled=${d>=t.length-1} onClick=${()=>_e(1)}>next \u203A</button>\n </div>\n <div class="dtop">\n ${c.kind==="question"&&i`<span class=${"badge "+(c.verb==="blocked"?"bad":"warn")}>${c.verb}</span>`}\n <b class="dtitle">${rr(c)}</b>\n <span class="dmeta dim">${rs(c)}${rs(c).length?" \\xB7 ":""}${v(c.at)} ago</span>\n </div>\n ${c.kind==="decision"&&c.detail.trim()&&i`<${sr} text=${c.detail} fileUrl=${m=>At(c.key,m)} />`}\n ${c.kind==="decision"&&Go(c.key,c.attachments)}\n ${p?.sent?i`<div class="danswered ok">answered \xB7 ${p.sent}</div>`:xo(c,p,n.join(","))}\n ${Ao(c.kind==="decision"?c.options.length:0)}`,c.key)}function ir({unread:e,dismissed:t}){let s=e.some(a=>!t.includes(a.key)),n=e.length>0&&s,r=e.length,o=`${r} new decision${r===1?"":"s"}`;return i`<div class="dalert-live" role="status" aria-live="polite">${n&&i`<div class="dalert">\n <button type="button" class="dalert-open" aria-keyshortcuts="d" onClick=${()=>ae()}><span class="dcount" aria-hidden="true">${r}</span> <span>${o}</span></button>\n <button type="button" class="dx" aria-label="dismiss" title="dismiss" onClick=${an}>\xD7</button>\n </div>`}</div>`}function Eo(e,t){let s=e.badge&&(e.badge.worker?i`<${N} worker=${e.badge.worker} label=${e.badge.text} cls=${"badge "+(e.badge.tone||"dim")} />`:i`<span class=${"badge "+(e.badge.tone||"dim")+B(e.badge.text)} title=${j(e.badge.text)}>${e.badge.text}</span>`),n=e.worker&&i`<${N} worker=${e.worker} />`;if(e.href){let r=(t==="cards"?"card":"deckline click")+(e.acked?" acked":"");return t==="cards"?i`<a key=${e.key} class=${r} href=${e.href} target="_blank" rel="noopener"><div class="top"><b>${e.title}</b>${s}</div>${e.meta&&i`<div class="meta" title=${e.metaText}>${e.meta}</div>`}</a>`:i`<a key=${e.key} class=${r} href=${e.href} target="_blank" rel="noopener">${s&&[s," "]}<b>${e.title}</b>${e.meta&&[" ",i`<span class="dim">\xB7 ${e.meta}</span>`]}</a>`}return t==="cards"?i`<div key=${e.key} class=${"card"+(e.acked?" acked":"")} onClick=${e.open} style=${e.open?void 0:"cursor:default"}><div class="top"><b>${e.title}</b>${n}${s}</div>${e.meta&&i`<div class="meta" title=${e.metaText}>${e.meta}</div>`}${e.extra}${e.action&&i`<div class="foot"><span class="footact">${e.action}</span></div>`}</div>`:i`<div key=${e.key} class=${"deckline"+(e.open?" click":"")+(e.acked?" acked":"")} onClick=${e.open}>${s&&[s," "]}<b>${e.title}</b>${n&&[" ",n]}${e.meta&&[" ",i`<span class="dim">\xB7 ${e.meta}</span>`]}${e.action&&[" ",i`<span class="dim">\xB7 </span>`,e.action]}${e.extra}</div>`}var lr=(e,t)=>e>0&&i`<a class="deckmore" href=${"#"+t}>+${e} more \u2192</a>`;function ht(e,t,s,n,r,o){let a=t.slice(0,n),l=a.map(c=>Eo(c,r)),u=a.length?r==="cards"?i`<div class="cards">${l}</div>`:l:i`<div class="empty">none</div>`;return i`<section><h2><a href=${"#"+s}>${e} \u2192</a></h2>${o&&i`<div class="tabhead">${o}</div>`}${u}${lr(t.length-a.length,s)}</section>`}function Oo(e,t,s,n){let r=t.find(c=>c.number===e.nextNumber)||t[0],o=fe(e.numbers,new Map(t.map(c=>[c.number,c.title]))),a=r?"next #"+r.number:"nothing mergeable";if(n==="cards"){let c=d=>{let h=s.get(d.key)||[],p=h.length>0&&h.every($=>$.acked),m=h.length?h.map($=>i`<span class=${"badge "+(zt[$.kind]||"dim")}>${Fn($.kind)}</span>`):i`<span class=${"badge "+E(d.badge)+B(d.badge.text)} title=${j(d.badge.text)}>${d.badge.text}</span>`,f=`${d.repo} \\xB7 ${d.badge.text}${p?" \\xB7 acked":""}`;return i`<div key=${d.key} class=${"card"+(p?" acked":"")} onClick=${S("pr",d.key)}><div class="top">${lt(d)}${m}</div><div class="meta" title=${f}>${f}</div></div>`};return i`<div key=${e.id} class="deckstack"><div class="dim">${o} \xB7 ${a}</div>${e.readiness&&i`<div class=${e.readiness.allReady?"ok":"dim"}>${e.readiness.text}</div>`}<div class="cards">${t.map(c)}</div></div>`}let l=t.flatMap(c=>(s.get(c.key)||[]).map(d=>i`<span class=${"badge "+(zt[d.kind]||E(c.badge))+(d.acked?" acked":"")}>#${c.number} ${c.badge.text}</span>`)),u=l.flatMap((c,d)=>d?[" ",c]:[c]);return i`<div key=${e.id} class=${"deckline"+(r?" click":"")} onClick=${r&&S("pr",r.key)}><b>${o}</b> <span class="dim">\xB7 ${a}</span>${l.length>0&&[" \\xB7 ",u]}${e.readiness&&i`<div class=${e.readiness.allReady?"ok":"dim"}>${e.readiness.text}</div>`}</div>`}function Lo(e,t){let s=e.stacks.slice(0,3),n=new Map;for(let o of e.prAttention){let a=n.get(o.key)||[];a.push(o),n.set(o.key,a)}let r=s.length?s.map(o=>Oo(o,e.prs.filter(a=>a.stackId===o.id).sort((a,l)=>a.position-l.position),n,t)):i`<div class="empty">none</div>`;return i`<section><h2><a href="#prs">PRs \u2192</a></h2>${r}${lr(e.stacks.length-s.length,"prs")}</section>`}function dr({inp:e,drafts:t,focus:s,viewedHere:n,unread:r}){let o=e.view,a=e.inflight.map(d=>({key:d.lane+":"+d.id,title:[d.id.slice(0,8)," ",d.repo||""],worker:d.worker,badge:{text:d.verb,tone:d.verb==="needs-decision"||d.verb==="blocked"?"bad":"dim"},meta:[R(d.note||"",90),d.verbAt&&[" \\xB7 ",v(d.verbAt)," ago"],(d.for||d.claimedBy)&&[" \\xB7 ",st(d.for||d.claimedBy)]],metaText:[U(d.note||""),d.verbAt&&`${J(d.verbAt)} ago`,U(d.for||d.claimedBy||"")].filter(Boolean).join(" \\xB7 "),open:S("dispatch",d.lane+":"+d.id)})),l=e.landed.map(d=>({key:d.key,title:[d.id.slice(0,8)," ",d.repo||"",d.unreported&&[" ",i`<span class="badge warn unreported">unreported</span>`]],badge:{text:d.verb,tone:d.verb==="failed"?"bad":"ok"},meta:[R(d.note||"",90)," \\xB7 ",v(d.at)," ago"],metaText:`${U(d.note||"")} \\xB7 ${J(d.at)} ago`,open:S("dispatch",d.lane+":"+d.id)})),u=e.reports.map(d=>{let h=Xt(d);return{key:d.key,title:d.title,meta:h.nodes,metaText:h.text,href:x(d.key),acked:!!d.acked}}),c=e.traps.map(({x:d})=>({key:d.trapId,title:"\\u{1FAA4} "+(d.label??`wt:${d.trapId}`),badge:d.requested?{text:"requested",tone:"dim"}:d.starting?{text:d.starting.failedAt?"start failed":"starting",tone:d.starting.failedAt?"bad":"warn"}:{text:d.live?d.harness||"live":"signed off",tone:d.live?"ok":"dim",worker:d.live?d:void 0},meta:[d.repo||""," \\xB7 ",i`<span class="catchn" title="catches">\u{1F99E} ${te(d.totalCatches??0)}</span>`," \\xB7 ",Re(d)],metaText:`${d.repo||""} \\xB7 \\u{1F99E} ${te(d.totalCatches??0)} \\xB7 ${pt(d)}`,action:Y(d),extra:Q(d),open:S("trap",d.trapId)}));return i`<div class="deckgrid"><${or} cards=${e.decisions} drafts=${t} focus=${s} viewedHere=${n} unread=${r} />${ht("in flight",a,"dispatches",4,o)}${ht("Landed \\xB7 24h",l,"dispatches",8,o)}${ht("reports",u,"reports",8,o)}${ht("traps",c,"traps",8,o,i`<${ct} />`)}${Lo(e,o)}</div>`}function Ho(e,t){if(!t)return e;let s=new Map(e.map(o=>[o.id,o])),n=o=>{let a=o,l=new Set;for(;a.followUp&&s.has(a.followUp)&&!l.has(a.id);)l.add(a.id),a=s.get(a.followUp);return a.id},r=new Map;for(let o of e){let a=n(o),l=r.get(a)||[];l.push(o),r.set(a,l)}return[...r.values()].sort((o,a)=>a[0].sort-o[0].sort).flatMap(o=>o.sort((a,l)=>a.id===n(a)?-1:l.id===n(l)?1:a.sort-l.sort))}function No(e,t){let s=e.lane+":"+e.id;return i`<tr key=${s} class="rowhead" onClick=${S("dispatch",s)}><td>${t&&e.followUp&&i`<span class="dim">\u21B3 </span>`}${e.id.slice(0,8)}</td><td>${e.lane} / ${e.bucket}</td><td>${e.repo}</td><td class=${"v-"+e.verb}>${e.verb}</td><td class="grow">${R(e.note??"",90)}${ot(e)}${Kt(e)}${rt(e)}</td><td>${v(e.verbAt)}</td><td>${xe(e)}</td><td>${ue(e)}</td></tr>`}function jo(e){let t=e.lane+":"+e.id;return i`<div key=${t} class="card" onClick=${S("dispatch",t)}><div class="top"><b>${e.id.slice(0,8)}</b>${Kt(e)}<span class=${"badge v-"+e.verb+B(e.verb)} title=${j(e.verb)}>${e.verb}</span></div><div class="meta" title=${`${e.repo} \\xB7 ${e.lane} ${e.bucket}`}>${e.repo} \xB7 ${e.lane} ${e.bucket}${e.verbAt&&[" \\xB7 ",v(e.verbAt)]}</div>${e.note&&i`<div class="note" title=${U(e.note)}>${R(e.note)}</div>`}${ot(e)}${rt(e)}<div class="foot">${xe(e)} ${ue(e)}</div></div>`}function cr({inp:e,more:t}){let s=e.list,n=t&&i`<${pe} kind="dispatches" more=${t} />`;return e.view==="cards"?[s.length?i`<div class="cards">${s.map(jo)}</div>`:i`<div class="empty">no dispatches match</div>`,n]:[M(["id","lane / bucket","repo","verb","note","age","addressed","pr"],Ho(s,e.chain).map(r=>No(r,e.chain)),"no dispatches match"),n]}var Bo={deck:"On deck",dispatches:"Dispatches",traps:"Traps",prs:"PRs",reports:"Reports",notices:"Notices",stats:"Stats"},Uo=["working","needs-decision","blocked","paused","done","failed","unknown"],pr=(e,t)=>i`<span role="img" aria-label=${t} title=${t}>${e}</span>`;function Fo(e,t){let s=t.daemonStale,n=e.daemon?[i`<span class=${s?"bad":"ok"}>${s&&"stale "}${v(e.daemon.heartbeat)} ago</span>`," ",i`<span class="dim">v${e.daemon.version}</span>`]:i`<span class="bad">down</span>`;return[i`<span class="chip">daemon ${n}</span>`,t.stats&&i`<span class="chip" title="catches today">\u{1F99E} ${t.stats.catchesToday} today</span>`,e.slots&&i`<span class="chip">active ${pr("\\u{1F3A3}","headless")} ${e.slots.headless} of ${e.slots.limit}; ${pr("\\u{1FAA4}","traps")} ${e.slots.traps}${e.slots.parked?`; parked: ${e.slots.parked} (no slot)`:""}</span>`,t.helms.map(({x:r,stale:o})=>i`<span key=${r.grounds} class="chip click" onClick=${S("helm",r.grounds)}>\u26F5 <${N} worker=${r} label=${i`<b>${r.man}</b>`} cls="" /> <span class="dim">helm ${r.grounds}</span> <span class=${o?"warn":"ok"}>${o&&"stale "}${v(r.heartbeatAt)} ago</span></span>`)]}var os=[];function Wo(e){let t=[...new Set([...e.dispatches.map(s=>s.repo),...e.notices.map(s=>s.repo),...e.prs.map(s=>s.repo)].filter(Boolean))].sort();return t.length!==os.length&&(os=t),os}var as=[];function qo(e){let t=[...new Set(e.notices.map(s=>s.kind))].sort();return t.length!==as.length&&(as=t),as}var Pe=e=>e.currentTarget.value;function Ko(e,t,s){let n=a=>t===a?"":"display:none",r=e?Wo(e):[],o=e?qo(e):[];return i`<div class="controls" style=${t==="stats"?"display:none":void 0}>\n <select id="f-lane" style=${e&&n("dispatches")} value=${s.lane} onChange=${a=>H({lane:Pe(a)})}>\n <option value="">all lanes</option>\n <option value="work">work</option>\n <option value="chore">chore</option>\n </select>\n <select id="f-repo" value=${s.repo} onChange=${a=>H({repo:Pe(a)})}>\n <option value="">all repos</option>\n ${r.map(a=>i`<option key=${a}>${a}</option>`)}\n </select>\n <select id="f-verb" style=${e&&n("dispatches")} value=${s.verb} onChange=${a=>H({verb:Pe(a)})}>\n <option value="">all verbs</option>\n ${Uo.map(a=>i`<option key=${a}>${a}</option>`)}\n </select>\n <label id="chain-control" style=${e&&n("dispatches")}><input id="f-chain" type="checkbox" checked=${!!s.chain} onChange=${a=>H({chain:a.currentTarget.checked})} /> group by chain</label>\n <select id="f-kind" style=${e&&n("notices")} value=${s.noticeKind} onChange=${a=>H({noticeKind:Pe(a)})}>\n <option value="">all notice kinds</option>\n ${o.map(a=>i`<option key=${a}>${a}</option>`)}\n </select>\n <input id="f-q" type="search" placeholder="search id \xB7 note \xB7 brief" value=${s.q} onInput=${a=>H({q:Pe(a)})} />\n </div>`}function ur({d:e,inp:t,tab:s,st:n,stale:r,unread:o=0}){return[i`<div class="headerline"><h1>\u{1F99E}\u2728 spyglass<span id="stale" style=${e||r?r?"display:inline":"display:none":void 0}> \xB7 STALE FEED</span></h1><span id="settings-slot"><button id="gearbtn" title="settings" aria-label="settings" onClick=${()=>O("settings","browser")}>\u2699</button></span></div>`,i`<div class="chips"><span id="chips" style="display:contents">${e&&t&&Fo(e,t.chips)}</span><span class="chip dim" id="clock">${e&&new Date(e.now).toLocaleTimeString("en-GB")}</span></div>`,i`<nav class="tabs" id="tabs" aria-label="Spyglass views">${se.map(a=>i`<a href=${"#"+a} data-tab=${a} class=${e?a===s?"on":"":void 0}>${Bo[a]}${a==="deck"&&o>0&&i`<span class="dbadge" aria-label=${`${o} new decision${o===1?"":"s"}`}>${o}</span>`}</a>`)}</nav>`,Ko(e,s,n)]}var fr=({d:e})=>e&&["\\u{1F99E}\\u2728 lobstah v"+e.version+" \\xB7 ",i`<a href=${e.repoUrl} target="_blank">${e.repoUrl.replace("https://github.com/","")}</a>`];function mr(e,t){if(!t.lobs)return[];let s={"pr:draft":"draft","pr:review":"review","pr:checks":"checks","pr:conflict":"conflicts","pr:ready":"ready","stack-ready":"stack ready",landed:"landed",watch:"watch",report:"report"},n=t.hidden??{},r=e.filter(a=>!a.quiet&&!a.acked&&!(a.key!==void 0&&n[a.key]===a.stateHash)).map(a=>{let l=s[a.kind??""]??"",u=a.key!==void 0?{hideKey:a.key,hideHash:a.stateHash??""}:{};return a.kind==="decision"&&a.key?{key:"decision:"+a.key,text:a.note||a.verb,label:l,hash:Ct(a.key),...u}:(a.kind??"question")==="question"&&a.key?{key:"question:"+a.key,text:a.note||a.verb,label:l,hash:Ct(a.key),...u}:a.kind==="report"&&a.key?{key:"report:"+a.key,text:a.note||a.verb,label:l,href:x(a.key),...u}:typeof a.kind=="string"&&(a.kind.startsWith("pr:")||a.kind==="stack-ready")&&a.prUrl?{key:a.kind+":"+(a.key??a.prUrl),text:a.note||a.kind,href:a.prUrl,label:l,...u}:{key:(a.kind??"question")+":"+(a.key??a.lane+":"+a.id),text:a.note||a.verb,label:l,...a.kind==="watch"?{}:{open:{type:"dispatch",key:a.lane+":"+a.id}},...u}});!r.length&&t.preview&&(r=[{key:"preview",text:"attention questions crawl in here",...t.previewHelm?{open:{type:"helm",key:t.previewHelm}}:{}}]);let o=r.length>4?r.length-4:0;return r=r.slice(0,4),o&&(r[3]={...r[3],text:"\\u2026and "+o+" more \\u2014 see attention",label:""}),r}var hr="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAASAAAAA4CAYAAABdcSXCAAAVF0lEQVR4nO2d34sc15XHb/fUaGRpsIwky0qbRCiyIZh1NjEKxDixTUge8up9ccKSZSHZf8BP0R/gffI/kCwsLEvWL/FrHmKM88PEYOHsJiYIFGWwQjq2rBk8xpI1mp7p5XOrvj2n71R1V1VXz3RP3yOaaXV1Vd/7rXO/95xzzz3lXJS5kVe++3w/732UKGUk6k8EqDFFeu07l/uRhOIAqyNRfyJAE0uczeIAm0Si/kSAGlWmaA3FAVZXov6UAOjlr/1Df5GUoKpSRCUajU3Un9nUn/Y8DLCXfvFma+Xs2YUMvFbByB1hifpTT6L+jCGgqrO8W0Api9NRtoKi/tSXqD8RoANVIrdgEgfY/OrPgblg49yHrdu3x3b+qLsZZfo+DiNwXDSJ+jO/+jMTBIRceee9UgAtspQh4EWNlUX9mU/9mRkCqmLhLOIAq9L/i71PF3IFKerP7OnPXBGQZNEJZtJBtpasNvqbX3rouCPzel6IKOrPbOnPXBHQIsd5DlsgmHCLBzGBy50T/gURzfrgjvozXzJzBHRYMm9L2E23FfL51qUz7tnLj+871t1quePLieucOdnkTx4pWXT9OXIENE2AipQF33dWbkyZVZ8myQfr5rGLHffnte6+lZDOSt/d2+65Sw8uu2dO9ecCo6g/B6c/Ve5B+Fl7kQBSQJVBFA4kuRqQ0DzEOlitaJJ8sHy2Nzdyj2H9WDKa9aX+qD8Hpz95gt4wtsIxxnvGF27+TBPQNAACFAbO97/8iJ/pu+t3huIFDDTkqS+cGryfddHNDS26qnEQ63adP5mSja4HHq1225PQZ1v33bxI1J+D0x87xuTK/8tXPrfvWprk9d2ZJqCmAYJU/u3bT7qVlcTd7f7NXfv43uCYwGCQnVxq+VjHrLoZwoKZhNfbLz7n3zPb8F7fKWvFoSySfq/n8eEzlEVEvO36rtdO40DIPASjkag/09ef0FpmjDHBf3rrlnvjxvrQcf6iQ1rQQPZs6xlTGuUieHOt96knBQDqrqdxGgaIks/GCZ3XLL995zOXrCwPWTm8Z6a/t73tj2EFbN340M2SqM+y4n7eXfVKw81+7OLe9x7L/v766l48q4iwrZLJ/Vo+ddp1r1/3mIB5u7fter1UcZAHVo455+7O7GpT1J+D0x8rcuOR3Y8YO/tPYWIjligd4vrJrAGE/FNnyb170/kBAEBWAOvZDCAGEKb2OIA0y99fv+UJppUkQ7/J7zDz95ecS3b7rseImyHRjPXst58cfPbMna53I63yWEEZnrp1y717c9PjNI6ssX7ABYzAAxI+0UqDzwgWENhgDc2iRP05PP0ZmuCZyI4tO7fVG7ovjMEvri4N9IkJf627MxsumADCfMN3XD13zn8OQP31j/yg4AWz0kFedBgmHxc05pjiGhJmeURuxhceTNzS/e2h70ziZjTlntiguY3TyFrBlBUeeoEXOPE6tuTc04+f8Te/qE1grOtBQsiF08c9+Vi3C/JBeZZzZrbDlqg/h6c/oUiHmNAxDjTG0CkrH9zp+WO1tInGNGGCW3aUpQMANtiZmmt70t/dHbwnONpLltz7G/fcW5utXEuIG/CvX3vUEwwDill+a6vnmZ2ZHvLpbaWuF4OMa97Z6buf/eHDkS6erC/aLytMfrCySSfBSNhAsiJkBCVJejtjzxdO6nMeRgoYohxyTRFvBbZbg/cI5AMZ/eXTHe/bl3V/i/oW9We+9Od319dz22R1SOQjfWNyE9FpIpMl/dv3P/Fx2PY49rQBYGXKNrFUbQES+RAcpuOQjl6hQBB68V2Ihc6Pyk+BcGT6KdAKs3Me5OOByf6WbTuKgrWG1WY/47rMNpPkFAkbri3l0QxVRnmscLOFUdgmSARSkkUokZLovSwhMJRSlelD1J+joT9Jb8e3izFWdtyTroGb9Y0LD/r/a/ztu35RB3yA6ispOL++en1g5tIQFLaz1nWvnK03k1mARDwoeJ61A9HkiY4Rk0iyAdZdX983uzK7QDjbvT2CkbvlB1o260NAewGy/PbqeohMWqL9YIVVcPmJ0/7a3Gj5uHUFqw3hWr69x5YrKY8Imj7KbcLiW9vM/z795vsDRcksQns9MqKZtcZZP1F/jpr+OH8upJInGBCMYY0lJnm9v5fFghBZ1XZMJ0UAySrBF0Rxr924PQSQj6ts1gMoj3ys2acGFpGPhO8v852sY2pnKBDm0v0PXS8AYojlsxkewT2TaBZSIPut7BAZw2DEKhERf5nOnsTabfe+93H3liHLCjMMQfhBnGqr5060UtLsG8UowkfErL+tZMnd2LjnLpxeypII97eJvkDgS/f33FsIuWfIiV/H+skjn6g/i6A/x93uTkoqVuALSNQlexO5yAfR2KZfHOP/29nPokttWTYAxIv3EAQXhWgEKgChiAxeOkMQqY4AEG6XyM1m2VoQbKwnPBbGgG58klo0RQxNX3aYAVAUM6vb6yC0hWvZWR4sMJXT5cqO+/7FFY+Rgrd+j9RKf8jPpY0cr5oMJzPc+uzkJYWELBe0CKuB/54suf9852/u66/+yv1sbcstn3xgX3AdaxE3DB9fFiAv+qIXmLy9tule+OXVXPKJ+rMY+rOykuQuzsAF/KYIJi+cob7xuzc/6Q3y8NoAhDkIOCyT4WLxI2Jx2Az2wyT0jcouXjYWEAIEyVmALBj2/5aprdjPLUD8zRtg4YwkJvZ9ywiJuYHrcswmT+URGealsjkxSz2xrSz73+Z83hOoBeCq7qnuhSTcGoH8Zu1j99Pff+D/Cg/Ezmp6EQi88s57kD5t8bMibQ+3UtBO2vvff9rw12URgD7w+o//SxPK8sjHtjnqz2LoTyfYkEwbLZlqscJO9Bgt+hyCtosYQzYYXxJA+iHlzPg4iusPVkLqAiTXjmX10ATkZvzXX3seIBoqsglZOgTISh5AtJUZnnMU41HMR32kb1e7d/2sY90M/o+5LAw4bzdZdt+8+JAnQIvRic6jHvg65KzVBCvy2WUOszr3wi+v+j7TJ35/YCobrPgewsTxynef999/7TuX/Wfcv3CriWbOf34iNdshIpREilJ2xSvqz9HXn25O28LxhR7YiZ4YJASbt4LatgAhOomdz2HQCqDu9lu1zEMLEK6XzScRCPirMC0dtjNJaBFZgGBnhPYUBdk4BkiwPucCxiBItt3zRIaLkbfMKBfFA3nqtFce8iOUkAfwejHDaXmxyjK1LDaRs/IxbP99P1aSQX8tadMvCLv98CP+JpNCQD/B4wefT9ztH3/PW7Zck2uFW020R4cBIAIvSzxRfxZLf/KEyUvjC1KzLjzWD30Fo7z0jQSAWFo/f/KWW+086i0TxLsl2fo/F9d2BdyZqjkKClICkF8ODFabrCUEQNaq4RgdU1Y0ZElHiPIDEH+JKQGuKyAgfO7OpTOeOAHXWgDyRUfd8HR3dZqjAQbHzpwbrA5y/rfMtdMZMN9dGSWKiyGjVit8UtjZ5/3KiZR6LdlyL7z6pp+tFLj0md1Krlz/yK82aFXDz2JZ1Tu74x0FKwo0j8Im6s9i6M92ThY8+sPYFDfAFwifyVoSf+Rh5G0//PtUETf9hRSjKQKoztK7ACLhrSjwfHKpPQSQLCMIj9UtZhIBBMhpHlB63rZPgmoVAgR5XTi94a69k25ErdoPwINI/dJo9++DvWr0i6zhxy4+7lMTtMpRRbjW6jm7j6ZghaLX81bfBZO2w/eZpZ558Tn3RrYCCCkrKcyKksoQPYVEqRWoLLOU3aRbVqL+LIj+bKUB9pWzq0N77vCWWmcedu7m5kB/GLNbJSb31qiNfAjKyaoX5hmEVCdDkxlS9WZEQKOWAMP/Y9ZZS0xtCoNospZU01Zp4CRDYWrqGpNk8Ya5Lsoixq1jNvnJ63+sbD6rdIEUogw2eYISKPBYNAuGWIIN1w2Dg3Uk6s/R1p/frH08mLzhCIwVVtmQuvozZIpYYhFAiN8DlO2/uvb6H6tc31/nqU4abCtDPmE0HoFsCNo9e+Zh7yZgGo4CU6s82uIBqJrdmyAfCVYh7L9EYH7jnnMb3cFjTcqSdNrWz+0ljRUoi83NKBKw0Q3N+64vq3HnM/8e8gFTVrwIvjeBTdSfo60/0pvL53bd8WUm9TTsoUm/jv60xwEkJmQAE3+pukkzNQ9Tl07kU5Tjk7csr884B3YvAkaWEkIbf/TV8/7mkujIjI/71iT5SBjABA5hf/ASaZeRMK0dos3rfxMCNvd3nCcbBLcUxWHFq67yjJOoP0dHf8IVZoLVjCtWrCfZtza2HIdmyDoCQMyslM/QCgxSRCCjpAyorKIpZoVJyA2tE++pIgxgER6DHDM6rakyvhYP7YQYcW2xRvLcT/v/KhLmdyQ9536X+ei016/MeOKpHvCsIlF/5lt/wmuhP4PH9jSgP4UnK0BpV8CIjAOQVqJGDWwLENcBoGmJjf+IcCTTLJylPmJp4d5gYWDtCR+kKF1BxaBQHiTEp8jSG5VGnyf6Pm40yj4NSydPov6Ml5ej/hRbQCoPYLfaE8NxLtsHZlZSRg2wbjbAqkiZgJn9LhKmwB+UgIWPrZA/lSwPrRyKYGS6ioxttqpdrhy3L8fOYvxmldo8SrI8KIn6c/T0ZxqSlAEI8lGColZQQoDk7tjSjLhdOoZ5KR+1iGTGmYmjiKlOcuSkwu+xD4aaQsS3fvv+5j5sbILYW//79yE/Gnw4t7ueJleyHBtKnvIgZMzyTxtrpUzh97TClZLzdN2tPIn6c5T0573G9addBiByR8gc1moJKftYRRYgSMcm+Km2DwApPqMOqpOjCMduwdAr3JrBi+X1ultDmhCID9dLbQAb9tT5HcJZGkO4OueD6VlWqfJySOLKi5H5ejzJ0kBRBttHsu9oP5vwsDv9+Q4xmEmX1+tK1J/x0l1w/UnKAASJaAcrAJHFfCcjFGJDqmIo9gYgZTqznV8AEeeQ6SfysdmVtjF5rJ3HzhBb1ezdpgTC8y5ottk13WrS37cBFjx049n5bBVEq4xg982c2E5Yo5p8kVZvx89KKGoodhOgAsDWMrSlMxBV5ZsWflF/iuWlqD+jCUh+fOeT1I2yD6nTABtK2c7E7/DNBpYdYE8T5c9JcILFlXnJubhqDDCIL698hh1g73bv7kXlC2rTTHOAIcr+1C7nkGA+y3YCgx9FvS6tDJ/PeVyD/qj2DjWOwtKovH/7+ron8tTvP+kD4JynkrKSdOPfxmChQLhoW4o17f+81vfF5ZrGKepPObm2wPoz1gKiAUrRxvpJ0/nTmRd/FLJgdmeTHQDZAvC+WuFufwAQuURaMbI/7CuoZQDd3LiX+bXFAO1l7hYDhIuookwA9NpDl31KQNNEpN9W9jiKgj8ua1FWG8RqM06VMq8aPGCHtfL04w84/xycnMJpEDXX/cbursce07jzxOm0DEJWQIxrgVd3/e4QCYRPRbCCIr1RMcG0rET9GS0vLbj+jF1qogEoER3E/Lf5Nlb8ZtCsOBEi4Ng9rzo9CkbbWsMDcHz1tcSDgk8sgLgZtsgRN4LPFHS2AFFlUbVpIB+llYeP9mlaaANWGLMT/bDF2kTOVuS/q0aK9fV9WYPMr0fAAhFW4EjiGn3iPUvr9joQM4qo5EIVzw/rxKjgHCkDpP+HZSSaxCbqz3iM1hZUf9pVAMKCoXMCCGvH1lW2ZqOtB4KFI4B0jVAUC1IxNAHEBlgBpNIZdke+fbY5AjAEwH2N2t6O/wtA08r2tRhBmEqAlLlMu1md8DV1sxXBMJYlUvYlZTNLkTiZLZwmXFUYjn7JZEdhMJn5q+JhmllV/JwqB5yjoljs4VGJhLx8LhWUbwqbqD9Rf/KkdM62Ld05ONkUes/LSVDtExGOrBoNMLFzeA0GIQOl7ACDfEQ6DHYVuIcMYPi8ARY+7rkJgeBon6xDAs4qziTS0TGRqmYmSAHXk/5wDcxgjvsnk2bfEUlzDeVlYKbzf/1umIoQVrLkeuAuogyx0dMswqJlk0rUn/FyZQH1p9KTUWVxKN8HghnM0tl3OPbUTt8dC6wilQL40noK0PkvP+JnYdwpSCoEUatmlCvAChI4lAIIAbKlJ2WyfpDtzs0DyPvbWZZ30yKzlWtDpop54WN3suAhZPrF1VR5bAAeLPUYE4jT1zk6+YBPAKVP3PwTy4lXGo4PbRDsnPAWlx5drWPKzNZvI1yH+FmG2OAZZz5+dqrvLn3+fJr7kVPgfxKJ+jNeriyY/tRyScRwAKQsaRERHbPbEwQQrEnDcdeoHgfAKiRmAeI43w0Lh6ng0aDWSPZAN9wLLB8J59MW5SxZsW0uqtDWlNjSFEoeY9kc/xtRSU7iaaElCRbqq9pLsB/FQ2yhJ0R4I0r4VGW9H/7juX3PZFJA3z/na6s1VNBf6RNNlOcokqg/4+WVBdGfRmq/0Ek1QKadYj5KQBRIcssgIQuQSEzni2TCshoiMwAWieXdAD4TOAJIz4VPTAmBaScv2rQAESAKoH5TTF/Pzeam8znvCd7bXCJJuLn27Ref6+Maaxc1/STorydTQtx6zLIemTLuIYycj6tdVIi+CYn6Ux6no6w/rWkDRENkSqq0Ku/1SGVYUpaO9UEF0O0ff69v98MQ+MZqEkC+Vm1mgiqmZAnJPniwDkBNSvg4nLCsgp0tyn5WdB8kKJAsSxVEFz55yqSHDhQ96rppifozv/qjhaFJ9KdSDChPwviKN2ySVWrMDnX8jZIAhclMZ//9f1qjAJJoMx8SunM22u4B2jAlBQ5Qpjmgw/ug9507xN7SOAAiXOwDGTW7Ienjl+8OakZPW6L+zK/+QD3HJ9SfZFqdODCAsqc2uq3tXMtHAW7tZjjoAVZHRs1UVbACJ/n7BC01Ww2sHrNQoAQ0BScPY+Nq1J/mg9m8qupRWf3xVlG2wlZXf6ZTOi0QATDqwfZVBh0ACST/ULScQveha6HgmJKsDmPj6kGL8m9wcxWABAcfRLSZ5Un6wLi856IdtlTRizJ6hkT9mR39ORACmjZAsC4A6QmTei4RAJFvZAGadfKZ1PoJhb6qMBz9BwfImc3FYMZiAPEwiHzWsWlajqL+1CHkw9SfRl2wuibhJINOCYnsV1MQ3Ep3PV229DGfOVKePKlLSl9/9VctG6PTo42QMOg/S1JHL6q6HkdBf65M2WKdpv60Dkuhit5Pcv3waZ+zPsCijJa6elH3vKg/UaJEieIWR/4fY2sZ1Lh7NyEAAAAASUVORK5CYII=";var gr="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA8AAAAPCAYAAAA71pVKAAAA2UlEQVR4nGNgwAPeHo35D8K45JnwaRQS/YXPbAacmkEa/3z/BaZx2c6Ey1YGIgAjLsV8PF/g7E9feMC0sPUSRhTNuPwGcjI28AlqENzZIIUw/O41G8O58z/BkiycbGDFl278R3GRENQyJpBTkE1DtgFkyIMnn+EGwQDIApA+uB9AzgeZCtIEMhlmu54GI1gMFgafvvDA/Y4SADADYABkG7LfPyFpxIgq9IBD1ojsbAZ0zYTiFlvoY00ksFAGYWw2wtWhC4BD/gsiQUBcxAbxEiI4MAE+p2PLYQAGj4EmRoa2cwAAAABJRU5ErkJggg==";var gt=()=>i`<span class="pet-art" aria-hidden="true"><div class="sprite" style=${{backgroundImage:`url(${hr})`}}></div><img class="star" src=${gr} alt="" /></span>`;function Jo(e,t){let s="animation-duration:"+((innerWidth+180)/(100+t*12)).toFixed(1)+"s;animation-delay:-"+t*9%14+"s",n=()=>{e.hideKey&&bn(e.hideKey,e.hideHash??"")},r=[i`<div class="bub">${e.label&&[i`<span class="badge dim">${e.label}</span>`," "]}<span>${e.text.length>48?e.text.slice(0,47)+"\\u2026":e.text}</span></div>`,i`<${gt} />`],o=e.open;return e.hash?i`<a key=${e.key} class="lob" style=${s} title="open the decision" href=${e.hash} onClick=${n}>${r}</a>`:e.href?i`<a key=${e.key} class="lob" style=${s} title=${e.label==="report"?"open the report":"open the PR"} href=${e.href} target="_blank" rel="noopener" onClick=${n}>${r}</a>`:i`<div\n key=${e.key}\n class="lob"\n style=${s}\n title="click to open"\n onClick=${()=>{n(),o&&O(o.type,o.key)}}\n >\n ${r}\n </div>`}function $r({state:e}){let t=e.snapshot,s=mr(t?.attention||[],{lobs:e.prefs.lobs,hidden:e.lobHidden,preview:e.preview,previewHelm:t&&t.helms.length?t.helms[0].grounds:void 0}),n=s.map(r=>r.key).join("|");return Wt(()=>s.map((r,o)=>Jo(r,o)),[n])}function $t({box:e}){if(!e)return null;let t=s=>{s.target===s.currentTarget&&le()};return i`<div id="lightbox" class="lightbox" role="dialog" aria-label=${e.name} onClick=${t}>\n <button type="button" class="lbclose" title="close" onClick=${le}>\xD7</button>\n <img class="lbimg" src=${e.src} alt=${e.name} />\n <a class="lboriginal" href=${e.src} target="_blank" rel="noopener">open original \xB7 ${e.name}</a>\n </div>`}var me=i`<span class="x" onClick=${de}>\xD7</span>`,vr=(e,t,s)=>i`<span class="seg">${t.map(([n,r])=>i`<button class=${n===s?"on":""} onClick=${()=>e(n)}>${r}</button>`)}</span>`;function Yo(e,t){let s=vr(vn,[["table","table"],["cards","cards"]],t.view),n=vr(kn,[["on","on"],["off","off"]],t.lobs?"on":"off"),r=e.attentionError?e.attentionError:(e.attentionKinds||[]).join(" \\xB7 ");return[me,i`<h3>\u2699 settings <span class="dim" style="font-weight:normal">\xB7 this browser only</span></h3>`,i`<div class="settings"><div class="row"><span class="lbl">view<span class="hint">cards or table (notices stay a table)</span></span>${s}</div><div class="row"><span class="lbl">lobs<span class="hint">crawling lobsters in this page</span></span>${n}</div><div class="row"><span class="lbl">attention<span class="hint">kinds shown \u2014 attentionKinds in config.toml (read-only here)</span></span><span class="dim" style="font-size:11px;text-align:right;max-width:260px">${r}</span></div></div>`]}function Qo(e,t){let s=fe(t.numbers,new Map(t.numbers.map((r,o)=>[r,t.titles[o]])),e.number),n=t.nextMergeable?[" \\xB7 ",i`<span class="ok">next mergeable</span>`]:t.blockedBy?" \\xB7 blocked by #"+t.blockedBy:t.nextNumber?" \\xB7 next is #"+t.nextNumber:"";return[s," \\xB7 ",t.position," of ",t.size," \\xB7 floor ",t.floor,n]}var Xo=e=>t=>{t.preventDefault(),O("dispatch",e)};function Zo(e,t){let s=Js(e,t),n=s.pr,r=s.stack,o=s.watch,a=dt(n),l=s.chain.length?s.chain.flatMap((c,d)=>[d?" \\u2192 ":"",c.culled?i`<span class="dim">${c.id.slice(0,8)} (culled)</span>`:[i`<a href="#" onClick=${Xo(c.modalKey)}>${c.id.slice(0,8)}</a>`," ",i`<span class="dim">${c.verb}</span>`]]):i`<span class="dim">none</span>`,u=o?[i`<div>${o.key} \xB7 owner ${o.owner||"?"} \xB7 ${o.lastCheckedAt?["checked ",v(o.lastCheckedAt)," ago"]:"never checked"}</div>`,o.lastError&&i`<div class="bad">${o.lastError}</div>`,i`<div class="dim" style="font-size:11px;margin-top:4px">cursor</div>`,T(o.cursor)]:i`<div class="dim">no watch \u2014 <code>lobstah watch add ${n.url}</code> registers one</div>`;return[me,i`<h3>${it(n)} ${n.title||""} <span class=${"badge "+E(n.badge)}>${n.badge.text}</span>${n.draft&&n.badge.text!=="draft"&&[" ",i`<span class="badge pr-draft">draft</span>`]}</h3>`,i`<div class="sub">${n.repo} \xB7 ${n.state} \xB7 observed ${v(n.observedAt)} ago${n.gate&&" \\xB7 gate "+n.gate}</div>`,i`<div class="sec">checks</div><div>${Ce(n)}</div><div class="sec">review</div><div>${a||i`<span class="dim">no review yet</span>`}</div><div class="sec">merge</div><div>${Te(n)}</div>${n.repairWait&&i`<div class="sec">repair waits</div><div>${n.repairWait.heldBy}: ${n.repairWait.reason}${n.repairWait.until?" \\xB7 until "+n.repairWait.until:""}</div>`}<div class="sec">refs</div><div>${n.headRefName||"?"} \u2192 ${n.baseRefName||"?"}</div><div class="sec">stack</div><div>${r?Qo(n,r):i`<span class="dim">not stacked</span>`}</div><div class="sec">dispatch chain</div><div>${l}</div><div class="sec">watch</div>`,u]}var kr=(e,t)=>(e==="codex"?"codex resume ":"claude --resume ")+t;function ea(e){let t=Date.now()-Date.parse(e.heartbeatAt)>18e5;return[me,i`<h3>\u26F5 ${e.man}</h3><div class="sub">helm of <b>${e.grounds}</b> (${(e.repos||[]).join(", ")})</div><div class="sub">${e.cwd??"?"}${e.host&&" \\xB7 "+e.host}</div><div class="sub">session ${e.sessionId??""} \xB7 signed on ${v(e.signedOnAt)} ago \xB7 heartbeat <span class=${t?"warn":"ok"}>${v(e.heartbeatAt)} ago</span></div>`,i`<${nt} worker=${e} />`,e.sessionId&&[i`<div class="sec">open this session</div>`,T(kr(e.harness,e.sessionId))],e.transcript&&[i`<div class="sec">transcript</div>`,T(e.transcript)]]}function ta(e){return[i`<div class="sec">report \xB7 ${e.title}</div>`,i`<div class="sub">${ns(e)} \xB7 <a href=${x(e.key)} target="_blank" rel="noopener">open the report \u2197</a></div>`]}function sa(e,t,s){let n=e.claimedBy&&e.claimedBy.startsWith("wt:")?[i`<div class="sec">worked by trap</div>`,i`<div class="claimedby">claimed by ${at(e.claimedBy.slice(3))}</div>`,T(U(e.claimedBy)),i`<div class="dim" style="font-size:11px">an opted-in interactive session mans this seat \u2014 attach would resume someone\'s live thread. Message it instead: lobstah send ${U(e.claimedBy)} "\u2026"</div>`]:[i`<div class="sec">open this session</div>`,T("lobstah attach "+e.id)];return[me,i`<h3>${e.id.slice(0,8)} <span class=${"badge v-"+e.verb}>${e.verb}</span></h3>`,i`<div class="sub">${e.repo} \xB7 ${e.lane} ${e.bucket} \xB7 ${v(e.verbAt)}${e.for&&[" \\xB7 ",xe(e)]} ${ue(e)}</div>`,n,e.worker&&i`<${nt} worker=${e.worker} headless=${!e.claimedBy?.startsWith("wt:")} />`,e.worktree&&[i`<div class="sec">worktree${e.worktreeOf?" (reused from "+e.worktreeOf.slice(0,8)+")":""}</div>`,T(e.worktree),e.worktreeKept&&i`<div class="dim" style="font-size:11px">kept: ${e.worktreeKept}</div>`],e.transcript&&[i`<div class="sec">transcript</div>`,T(e.transcript)],t&&ta(t),Un(e,s)]}function na(e){let t=Ae(e),s=e.requested?[e.repo??""," \\xB7 ",e.harness??""," \\xB7 requested ",v(e.requested.at)," ago from the glass; the helm starts it with man throw --new."]:e.starting?e.starting.failedAt?[e.repo??""," \\xB7 reserved ",v(e.starting.reservedAt)," ago \\xB7 start failed: ",e.starting.reason??"",". Its addressed work stays queued; a session can still redeem the ticket, or `lobstah stow --wt ",e.name??e.trapId,"` withdraws it."]:[e.repo??""," \\xB7 reserved ",v(e.starting.reservedAt)," ago \\xB7 starting: waiting for a session to redeem the ticket (due by ",e.starting.deadline,")"]:e.live?[e.repo??"addressed bait only"," \\xB7 session ",e.sessionId??""," \\xB7 signed on ",v(e.signedOnAt)," ago \\xB7 ",t.listen," \\xB7 heartbeat ",t.hb]:"signed off \\u2014 registration gone; the lifecycle, messages, and catches are the surviving record. Re-soaking the same worktree restores this address.",n=e.notices.length?e.notices.map(a=>i`<div key=${a.seq} class="loglines">${v(a.at)} ago \xB7 <b>${a.kind}</b> \u2014 ${a.text}</div>`):i`<div class="empty">none recorded</div>`,r=e.messages.length?e.messages.map(a=>i`<div key=${a.file} class=${"msg"+(a.from==="helm"?" from-helm":"")}><div class="hdr">from ${a.from} \xB7 ${a.at&&[v(a.at)," ago"]} \xB7 ${a.state==="pending"?i`<span class="warn">pending</span>`:i`<span class="ok">delivered</span>`}</div>${a.text}${a.attachments?.length?ce(a.attachments,l=>Ls(e.trapId,l)):""}</div>`):i`<div class="empty">none</div>`,o=e.catches.length?e.catches.map(a=>i`<div key=${a.lane+":"+a.id} class="catch click" onClick=${S("dispatch",a.lane+":"+a.id)}><div class="hdr"><b>${a.id.slice(0,8)}</b><span class=${"badge v-"+a.verb}>${a.verb}</span><span class="dim">${v(a.verbAt)}</span>${ue(a)}</div>${a.note&&i`<div class="loglines">${R(a.note)}</div>`}</div>`):i`<div class="empty">none yet</div>`;return[me,i`<h3>\u{1FAA4} ${e.label??`wt:${e.trapId}`} <span class="badge">${e.requested?"requested":e.starting?e.starting.failedAt?"start failed":"starting":e.live?"live":"signed off"}</span></h3>`,i`<${nt} worker=${e} />`,e.worktree&&i`<div class="sub">${e.worktree}</div>`,i`<div class="sub">${s}</div>`,i`<div class="sec">window</div>`,Y(e),Q(e),e.live&&e.sessionId&&[i`<div class="sec">open this session</div>`,T(kr(e.harness,e.sessionId)),i`<div class="dim" style="font-size:11px">as registered at sign-on \u2014 a hookless enlistment may hold a made-up id</div>`],i`<div class="sec">lifecycle (${e.notices.length})</div>`,n,i`<div class="sec">messages (${e.messages.length})</div>`,r,i`<div class="sec">catches (${e.catches.length})</div>`,o]}function br({snapshot:e,modal:t,prefs:s,detail:n=null}){if(!e||!t)return null;let r=re(e,t,n);if(!r&&t.type==="dispatch"&&n?.key===t.key)return[me,i`<h3>${t.key.slice(t.key.indexOf(":")+1,t.key.indexOf(":")+9)}</h3>`,i`<div class=${n.error?"bad":"dim"}>${n.error??"loading\\u2026"}</div>`];if(!r)return null;if(t.type==="settings")return Yo(r,s);if(t.type==="pr")return Zo(e,t.key);if(t.type==="helm")return ea(r);if(t.type==="dispatch"){let o=js(e,r);return sa(r,o,n)}return na(r)}var ra=e=>i`<tr key=${e.seq}><td class="dim">${v(e.at)}</td><td>${e.kind}</td><td class="grow">${e.text}</td><td class="dim">${e.repo??""}</td></tr>`,yr=({inp:e,more:t})=>[M(["at","kind","text","repo"],e.list.map(ra),"no notices"),t&&i`<${pe} kind="notices" more=${t} />`];function _r(e){let t=new Map;for(let s of e.prs){let n=t.get(s.stackId)||[];n.push(s),t.set(s.stackId,n)}return e.stacks.map(s=>({s,prs:t.get(s.id)||[]})).filter(s=>s.prs.length)}var wr=(e,t)=>fe(e.numbers,new Map(t.map(s=>[s.number,s.title]))),is=e=>e.state+(e.draft?" \\xB7 draft":"")+(e.state==="MERGED"&&e.mergedAt?" \\xB7 "+e.mergedAt:"");function Sr(e){let t=n=>{let r=String(n??"");return r.length>24?r.slice(0,23)+"\\u2026":r},s=e.watches.map(n=>i`<tr key=${n.key}><td>${n.key}</td><td>${n.owner}</td><td title=${n.cursor}>${t(n.cursor)}</td><td>${n.lastCheckedAt&&v(n.lastCheckedAt)}</td><td>${n.lastError||""}</td></tr>`);return[i`<h2>other watches</h2>`,M(["key","owner","cursor","last check","error"],s,"no other watches")]}function oa(e,t){let s=_r(e).flatMap(({s:n,prs:r})=>[i`<tr key=${"stack:"+n.id}><th colspan="8">${wr(n,r)} \xB7 floor ${n.floor}${n.open?" \\xB7 open":" \\xB7 history"}${n.readiness&&i`<div class=${n.readiness.allReady?"ok":"dim"}>${n.readiness.text}</div>`}</th></tr>`,...r.map(o=>i`<tr key=${o.key} class="rowhead" onClick=${S("pr",o.key)}><td>${it(o)}</td><td class="grow">${o.title||""}</td><td>${is(o)}</td><td>${Ce(o)}</td><td>${dt(o)}</td><td>${Te(o)}</td><td>${Jt(o.watch)}</td><td>${o.gate||""}</td></tr>`)]);return[M(["PR","title","state","checks","review","merge","watch","gate"],s,"no PR evidence"),t,Sr(e)]}function aa(e,t){let s=_r(e),n=o=>i`<div key=${o.key} class="card" onClick=${S("pr",o.key)}><div class="top">${lt(o)}<span class=${"badge "+E(o.badge)+B(o.badge.text)} title=${j(o.badge.text)}>${o.badge.text}</span></div><div class="meta" title=${`${o.repo} \\xB7 ${is(o)}`}>${o.repo} \xB7 ${is(o)} \xB7 ${Te(o)}</div><div class="foot"><span>${Ce(o)}</span><span>${Jt(o.watch)}</span>${o.gate&&i`<span>gate ${o.gate}</span>`}</div></div>`;return[s.length?s.map(({s:o,prs:a})=>i`<${ee} key=${"stack:"+o.id}><h2>${wr(o,a)} \xB7 floor ${o.floor}${o.open?"":" \\xB7 history"}</h2>${o.readiness&&i`<div class=${o.readiness.allReady?"ok":"dim"}>${o.readiness.text}</div>`}<div class="cards">${a.map(n)}</div><//>`):i`<div class="empty">no PR evidence</div>`,t,Sr(e)]}function Dr({inp:e,more:t}){let s=t&&i`<${pe} kind="prs" more=${t} />`;return e.view==="cards"?aa(e,s):oa(e,s)}var ia=["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],la=5,ls=(e,t,s=t+"es")=>`${e} ${e===1?t:s}`,da=e=>e.count?`${ls(e.count,"catch")} on ${e.date}`:`No catches on ${e.date}`,Gr=new WeakSet,ca=e=>{!e||Gr.has(e)||(Gr.add(e),e.scrollLeft=e.scrollWidth)},xr={ArrowUp:-1,ArrowDown:1,ArrowLeft:-7,ArrowRight:7};function pa({page:e,focus:t}){let s=e.weeks.flat(),n=t&&s.some(u=>u.date===t)?t:e.today,r=u=>{let c=s.findIndex(p=>p.date===n),d;if(u.key in xr)d=c+xr[u.key];else if(u.key==="Home")d=0;else if(u.key==="End")d=s.length-1;else return;u.preventDefault();let h=s[Math.max(0,Math.min(s.length-1,d))];Lt(h.date),u.currentTarget.querySelector(`[data-date="${h.date}"]`)?.focus()},o=e.weeks.length-1,a=(u,c,d)=>{let h=u[d];if(!h)return i`<td key=${c} class="pad"></td>`;let p=da(h),m=c<4?" tl":c>o-4?" tr":"";return i`<td\n key=${c}\n role="gridcell"\n class=${`day l${h.level}${d<2?" below":""}${m}`}\n data-date=${h.date}\n data-tip=${p}\n aria-label=${p}\n tabindex=${h.date===n?0:-1}\n onFocus=${()=>Lt(h.date)}\n ></td>`},l=e.months[0]?.week??e.weeks.length;return i`<div class="heat-scroll" ref=${ca}>\n <table class="heatmap" role="grid" aria-label=${`Catches per day, last ${e.weeks.length} weeks`} onKeyDown=${r}>\n <thead aria-hidden="true">\n <tr>\n <td class="wd"></td>\n ${l>0&&i`<td colspan=${l}></td>`}\n ${e.months.map((u,c)=>i`<td key=${u.week} class="month" colspan=${(e.months[c+1]?.week??e.weeks.length)-u.week}><span>${u.label}</span></td>`)}\n </tr>\n </thead>\n <tbody>\n ${ia.map((u,c)=>i`<tr key=${u} role="row">\n <td class="wd" aria-hidden="true">${c%2?u:""}</td>\n ${e.weeks.map((d,h)=>a(d,h,c))}\n </tr>`)}\n </tbody>\n </table>\n </div>`}var ua=()=>i`<div class="heat-legend" aria-hidden="true">\n Less ${[0,1,2,3,4].map(e=>i`<span key=${e} class=${"swatch l"+e}></span>`)} More\n </div>`;function fa({page:e}){let t=[["today",e.catchesToday],["this week",e.catchesThisWeek],["total",e.totalCatches]];return i`<div class="stat-tiles">\n ${t.map(([s,n])=>i`<div key=${s} class="stat-tile"><div class="n">${n}</div><div class="dim">${s}</div></div>`)}\n </div>`}function ma({page:e}){let t=e.perTrap.slice(0,la);if(!t.length)return null;let s=t[0].catches||1;return i`<section class="top-traps">\n <h2>Top traps <span class="dim">\xB7 all time</span></h2>\n <ol>\n ${t.map(n=>i`<li key=${n.name}><span class="name" title=${n.name}>${n.name}</span><span class="bar"><span style=${{width:`${n.catches/s*100}%`}}></span></span><span class="n">${ls(n.catches,"catch")}</span></li>`)}\n </ol>\n </section>`}function Cr({page:e,error:t,focus:s}){return e?i`<div class="stats">\n <${fa} page=${e} />\n ${e.totalCatches===0&&i`<div class="stats-empty"><${gt} /><div><b>No catches yet.</b> <span class="dim">A dispatch that finishes done is a catch; each one lands on its day here.</span></div></div>`}\n <section class="heat">\n <h2>Catches per day</h2>\n <${pa} page=${e} focus=${s} />\n <div class="heat-foot">\n <span class="dim"\n >${e.undated>0?`${ls(e.undated,"earlier catch")} ${e.undated===1?"has":"have"} no recorded day${e.historyFrom?`; daily history starts ${e.historyFrom}`:""}.`:""}</span\n >\n <${ua} />\n </div>\n </section>\n <${ma} page=${e} />\n ${t&&i`<div class="dim">${t}; showing the last numbers read.</div>`}\n </div>`:i`<div class="empty">${t??"loading stats\\u2026"}</div>`}var ds=e=>e.requested?"requested":e.starting?e.starting.failedAt?"start failed":"starting":e.harness??(e.live?"":"signed off"),Tr=e=>e.live||e.starting?"":" dim";function ha(e){let t=Ae(e);return i`<tr key=${e.trapId} class=${"rowhead"+Tr(e)} onClick=${S("trap",e.trapId)}><td><b>${e.label??`wt:${e.trapId}`}</b></td><td>${e.repo??"\\u2014"}</td><td class="grow">${e.worktree??""}</td><td>${e.harness&&i`<${N} worker=${e} cls="" />`}</td><td>${(e.sessionId??"").slice(0,8)}</td><td>${Re(e)}${Q(e)}</td><td>${t.listen}</td><td>${t.hb}</td><td>${Qt(e)}</td><td class="catchn" title="catches">\u{1F99E} ${te(e.totalCatches??0)}</td><td>${Y(e)}</td></tr>`}function ga(e){let t=Ae(e),s=Qt(e),n=`${e.repo??(e.live?"addressed bait only":"history")}${e.sessionId?" \\xB7 session "+e.sessionId.slice(0,8):""}`;return i`<div key=${e.trapId} class=${"card"+Tr(e)} onClick=${S("trap",e.trapId)}><div class="top"><b>\u{1FAA4} ${e.label??`wt:${e.trapId}`}</b>${!e.requested&&!e.starting&&e.harness?i`<${N} worker=${e} />`:i`<span class=${"badge"+B(ds(e))} title=${j(ds(e))}>${ds(e)}</span>`}</div><div class="meta" title=${n}>${n}</div>${e.worktree&&i`<div class="note" title=${e.worktree}>${e.worktree}</div>`}<div class="note" title=${pt(e)}>${Re(e)}</div>${Q(e)}<div class="foot"><span>${t.listen}${e.live&&[" \\xB7 heartbeat ",t.hb]} \xB7 <span class="catchn" title="catches">\u{1F99E} ${te(e.totalCatches??0)}</span>${s&&[" \\xB7 ",s]}</span><span class="footact">${Y(e)}</span></div></div>`}function Ar({inp:e}){let t=e.list.map(n=>n.x),s=i`<div class="tabhead"><${ct} /></div>`;return e.view==="cards"?[s,t.length?i`<div class="cards">${t.map(ga)}</div>`:i`<div class="empty">no traps soaking</div>`]:[s,M(["address","repo","worktree","harness","session","now","listening","heartbeat","mail","catches","window"],t.map(ha),"no traps soaking")]}function Rr({state:e}){let t=oe(e),s=e.route,n=t?Xe():[],r=t&&zs(t,{st:e.prefs,modal:e.modal,detail:e.detail},Date.now()),o=u=>({left:e.snapshot?Ks(e.snapshot,e.older,u):0,loading:e.olderLoading===u,error:e.olderError?.kind===u?e.olderError.text:void 0}),a=u=>!r||u!==s?null:u==="deck"?i`<${dr} inp=${r.deck} drafts=${e.drafts} focus=${e.focusDecision} viewedHere=${e.viewedHere} unread=${n.length} />`:u==="dispatches"?i`<${cr} inp=${r.dispatches} more=${o("dispatches")} />`:u==="traps"?i`<${Ar} inp=${r.traps} />`:u==="prs"?i`<${Dr} inp=${r.prs} more=${o("prs")} />`:u==="reports"?i`<${Vn} inp=${r.reports} />`:u==="stats"?i`<${Cr} page=${e.stats} error=${e.statsError} focus=${e.statsFocus} />`:i`<${yr} inp=${r.notices} more=${o("notices")} />`,l=u=>{u.target===u.currentTarget&&de()};return[i`<${ur} d=${t} inp=${r} tab=${s} st=${e.prefs} stale=${e.stale} unread=${n.length} />`,se.map(u=>i`<main key=${u} id=${"page-"+u} class=${t&&u===s?"tabpage on":"tabpage"}><div id=${u}>${a(u)}</div></main>`),i`<footer id="foot"><${fr} d=${t} /></footer>`,i`<div id="lobs"><${$r} state=${e} /></div>`,i`<${$t} box=${e.lightbox} />`,i`<div id="overlay" class=${e.modal&&t?"open":t?"":void 0} onClick=${l}><div class="modal" id="modalbox"><${br} snapshot=${t} modal=${e.modal} prefs=${e.prefs} detail=${e.detail} /></div></div>`,t&&i`<${ar} open=${e.decisionModal} order=${ye()} drafts=${e.drafts} extensions=${t.answerLimits?.extensions??[]} />`,t&&i`<${ir} unread=${n} dismissed=${e.alertDismissed} />`]}function $a({view:e}){if(e.state==="loading")return i`<main class="reportview"><div class="dim">loading\u2026</div></main>`;if(e.state==="error")return i`<main class="reportview"><div class="bad">${e.error}</div></main>`;let t=e.report;return i`<main class="reportview">\n <h1>${t.title}</h1>\n <${nr} r=${t} text=${e.text!==void 0?{text:e.text}:{error:e.error}} />\n </main>`}function Mr({view:e,box:t}){return[i`<${$a} view=${e} />`,i`<${$t} box=${t} />`]}async function Pr(e){let t=encodeURIComponent(e),s;try{let n=await fetch(`/report/${t}/meta`);if(!n.ok)return{state:"error",error:`report not found (${n.status})`};s=JSON.parse(await n.text())}catch{return{state:"error",error:"report could not be read"}}try{let n=await fetch(Fs(e));return n.ok?{state:"ready",report:s,text:await n.text()}:{state:"ready",report:s,error:`report not found (${n.status})`}}catch{return{state:"ready",report:s,error:"report could not be read"}}}var va=3e4,Ir="lobstah-glass-page",Or="lobstah-glass-shown",ka=20;function ba(){try{let t=sessionStorage.getItem(Ir);if(t&&/^[A-Za-z0-9_-]{8,64}$/.test(t))return t}catch{}let e="";try{e=Array.from(crypto.getRandomValues(new Uint8Array(12)),t=>t.toString(16).padStart(2,"0")).join("")}catch{for(;e.length<24;)e+=Math.random().toString(36).slice(2);e=e.slice(0,24)}try{sessionStorage.setItem(Ir,e)}catch{}return e}var Er=null,Lr=()=>Er??(Er=ba()),ps=[];function Hr(){try{let e=JSON.parse(sessionStorage.getItem(Or)??"[]");if(Array.isArray(e))return e.filter(t=>typeof t=="string")}catch{}return ps}function ya(e){ps=[...Hr(),e].slice(-ka);try{sessionStorage.setItem(Or,JSON.stringify(ps))}catch{}}function _a(e){if(!e||typeof e!="object")return!1;let{id:t,hash:s}=e;if(typeof t!="string"||!t||!Bs(s)||Hr().includes(t))return!1;if(ya(t),s==="")return!0;let n=ke(s);return n!==null?location.pathname!==x(n)&&location.assign(x(n)):location.pathname!=="/"?location.assign(`/${s}`):location.hash!==s&&(location.hash=s),!0}var cs=!1;async function Ie(){if(!cs){cs=!0;try{let e=document.hidden?"hidden":"visible",t=await fetch(`/api/presence?page=${Lr()}&vis=${e}`,{method:"POST",cache:"no-store"});if(!t.ok)return;_a((await t.json())?.show)}catch{}finally{cs=!1}}}function us(e){let t=null,s=0,n=()=>{let r=document.hidden?va:e??0;r!==s&&(t&&clearInterval(t),t=r?setInterval(()=>{Ie()},r):null,s=r)};document.addEventListener("visibilitychange",()=>{n(),Ie()}),window.addEventListener("pagehide",()=>{try{navigator.sendBeacon?.(`/api/presence?page=${Lr()}&vis=gone`)}catch{}}),n(),Ie()}var fs=!1,Ee=null,Oe=null;function wa(e){let t=e.headers?.get("x-lobstah-beats");if(t)try{return JSON.parse(decodeURIComponent(t))}catch{return}}async function ms(){if(!fs){fs=!0,Ie();try{let e=await fetch("/data",{cache:"no-store",headers:Oe?{"if-none-match":Oe}:{}});if(e.status===304){let t=wa(e);t?ln(t):Ye(await Sa())}else{if(!e.ok)throw new Error(`/data answered ${e.status}`);Oe=e.headers?.get("etag")??null,Ye(await e.json())}Ot(),g().route==="stats"&&Se()}catch{mn()}finally{fs=!1}}}async function Sa(){Oe=null;let e=await fetch("/data",{cache:"no-store"});if(!e.ok)throw new Error(`/data answered ${e.status}`);return Oe=e.headers?.get("etag")??null,await e.json()}var Nr=()=>{Ee||(Ee=setInterval(ms,2e3))},Da=()=>{Ee&&clearInterval(Ee),Ee=null};function jr(){document.addEventListener("visibilitychange",()=>{document.hidden?Da():(ms(),Nr())}),ms(),document.hidden||Nr()}var hs=()=>Os(location.hash),Br=e=>window.addEventListener("hashchange",()=>e(hs()));function Fr(){let e=ke(location.hash);return e===null?!1:(location.replace(x(e)),!0)}var Wr=()=>({snapshot:void 0,route:hs(),prefs:Xs(),modal:null,detail:null,older:Mt,olderLoading:null,olderError:null,stale:!1,lobHidden:en(),focusResults:{},preview:new URLSearchParams(location.search).has("lob"),drafts:{},focusDecision:ne(location.hash),decisionModal:ne(location.hash)?{key:ne(location.hash)}:null,viewedHere:{},alertDismissed:[],lightbox:null,stats:null,statsError:null,statsFocus:null}),Ur=Us(location.pathname);Ur!==null?Ga(Ur):Fr()||xa();function Ga(e){Pt(Wr());let t={state:"loading"},s=()=>Gt(Z(Mr,{view:t,box:g().lightbox}),document.body);document.title="report \\xB7 lobstah glass",It(s),s(),us(2e3),Pr(e).then(n=>{t=n,n.state==="ready"&&(document.title=`${n.report.title} \\xB7 lobstah glass`),s()}),document.addEventListener("keydown",n=>{n.key==="Escape"&&g().lightbox&&le()})}function xa(){Pt(Wr());let e,t=!1,s=()=>{let a=g(),l=a.route!==e||!t&&!!a.snapshot;Gt(Z(Rr,{state:a}),document.body),e=a.route,t||(t=!!a.snapshot),l&&window.scrollTo(0,0),a.focusDecision||(n=null),r(a.focusDecision)},n=null;function r(a){if(!a||a===n)return;let l=[...document.querySelectorAll("[data-decision]")].find(u=>u.getAttribute("data-decision")===a);l&&(n=a,l.scrollIntoView?.({block:"center"}))}It(s),s(),Br(a=>{if(Fr())return;let l=ne(location.hash);l!==g().focusDecision&&(n=null),k({route:a,focusDecision:l}),l&&ae(l),a==="stats"&&Se()});let o=a=>!!a&&(!!a.closest?.("input, textarea, select, [contenteditable]")||a.isContentEditable===!0);document.addEventListener("keydown",a=>{let l=g(),u=a.target;if(a.key==="Escape"){l.lightbox?le():l.decisionModal?o(u)&&u.closest?.(".dmodal")?(u.blur(),document.querySelector(".dmodal")?.focus()):ie():de();return}if(!(a.metaKey||a.ctrlKey||a.altKey||a.isComposing||a.keyCode===229||o(u)||l.lightbox)){if(l.decisionModal){a.key==="ArrowLeft"||a.key==="ArrowRight"?(a.preventDefault(),_e(a.key==="ArrowLeft"?-1:1)):/^[1-9]$/.test(a.key)?rn(Number(a.key))&&a.preventDefault():a.key==="Enter"&&u?.matches?.(".dmodal .dopt.on")&&l.decisionModal.key&&(a.preventDefault(),Ze(l.decisionModal.key));return}a.key==="d"&&!a.shiftKey&&!l.modal&&(a.preventDefault(),ae())}}),us(),jr(),g().route==="stats"&&Se()}})();</script>\n </body>\n</html>\n';
16260
+ var GLASS_PAGE = '<!doctype html>\n<html>\n <head>\n <meta charset="utf-8" />\n <meta name="viewport" content="width=device-width,initial-scale=1" />\n <title>spyglass</title>\n <link rel="icon" type="image/png" href="/icon.png" />\n <style>:root{--bg: #0e1116;--card: #161b22;--line: #2b3240;--line-hover: #3a455a;--row-hover: #1c2330;--inset: #10151c;--seg-on: #26436b;--scrim: rgba(4, 7, 11, 0.72);--fg: #dbe2ea;--dim: #8b96a5;--ok: #4fc17c;--warn: #e2b93d;--bad: #e26d5c;--link: #6cb2e2;--on-fill: #fff;--pr-merged: #8957e5;--pr-merged-fg: #ab7df8;--pr-open: #238636;--pr-draft: #6e7681;--pr-closed: #da3633;--lobstah: #eb6644;--heat-0: var(--inset);--heat-1: color-mix(in srgb, var(--lobstah) 30%, var(--card));--heat-2: color-mix(in srgb, var(--lobstah) 55%, var(--card));--heat-3: color-mix(in srgb, var(--lobstah) 78%, var(--card));--heat-4: var(--lobstah);--heat-cell: 11px;--heat-gap: 3px;--font-mono: ui-monospace, SFMono-Regular, Menlo, monospace;--font-size: 13px;--space-1: 4px;--space-2: 8px;--space-3: 12px;--space-4: 16px;--page-pad-block: 14px;--radius: 6px;--card-radius: 8px;--card-pad: 12px 14px;--card-min: 300px;--deck-card-min: 240px;--badge-radius: 5px;--badge-pad: 1px 7px;--badge-font: 11px;--modal-radius: 10px;--modal-width: 780px;--layer-modal: 10;--layer-decision-modal: 15;--layer-alert: calc(var(--layer-modal) - 1);--unread-bg: color-mix(in srgb, var(--bad) 16%, var(--card))}*{box-sizing:border-box}body{margin:0;background:var(--bg);color:var(--fg);font:var(--font-size)/1.45 var(--font-mono);padding-block:var(--page-pad-block);padding-inline:var(--space-4);display:flex;flex-direction:column;min-height:100vh}h1{font-size:15px;margin:0 0 10px}h1 .dim{color:var(--dim);font-weight:normal}h2{font-size:12px;color:var(--dim);text-transform:uppercase;letter-spacing:0.08em;margin:20px 0 6px}.chips,.controls{display:flex;gap:var(--space-2);flex-wrap:wrap;align-items:center}.controls{margin:0 0 14px}.chips{margin-bottom:8px}.chip{background:var(--card);border:1px solid var(--line);border-radius:var(--radius);padding:3px 9px}.controls select,.newtrap select,.controls input[type=search]{background:var(--card);border:1px solid var(--line);border-radius:var(--radius);color:var(--fg);font:inherit;padding:4px 8px}.controls input[type=search]{flex:1 1 140px;min-width:120px;max-width:340px}#chain-control{display:inline-flex;align-items:center;gap:7px;white-space:nowrap}#f-chain{margin:0;width:14px;height:14px;accent-color:var(--link)}.tabs{display:flex;gap:var(--space-1);border-bottom:1px solid var(--line);margin:8px 0 10px}.tabs a{color:var(--dim);padding:6px 11px;border-bottom:2px solid transparent}.tabs a.on{color:var(--fg);border-color:var(--link)}.tabpage{display:none}.tabpage.on{display:block;flex:1 0 auto}.headerline{display:flex;align-items:center;gap:var(--space-2)}.headerline h1{flex:1}#settings-slot{min-width:34px;text-align:right;color:var(--dim)}#settings-slot #gearbtn{margin-left:0}.deckgrid{display:flex;flex-direction:column;gap:var(--space-3)}.deckgrid h2{margin:5px 0}.deckgrid section{width:100%;min-width:0}.deckline{padding:2px 0;border-top:1px solid var(--line);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.deckline.acked,.card.acked,.rowhead.acked,.badge.acked{opacity:0.55}.deckmore{color:var(--dim);font-size:11px;display:inline-block;margin-top:4px}.deckline.click{cursor:pointer}.deckline.click:hover{background:var(--row-hover)}.deckgrid h2 a{color:inherit}.deckgrid h2 a:hover{color:var(--fg)}.deckgrid .cards{grid-template-columns:repeat(auto-fill,minmax(min(var(--deck-card-min),100%),1fr));gap:10px}.deckstack{margin-bottom:9px}.deckstack>.dim{font-size:11px;margin:3px 0 5px}.seg{display:inline-flex;border:1px solid var(--line);border-radius:var(--radius);overflow:hidden}.seg button{background:var(--card);border:none;color:var(--dim);font:inherit;padding:4px 11px;cursor:pointer}.seg button.on{background:var(--seg-on);color:var(--fg)}.wrap{overflow-x:auto}table{border-collapse:collapse;width:100%;background:var(--card);border:1px solid var(--line);border-radius:var(--radius);overflow:hidden}th,td{text-align:left;padding:4px 9px;border-top:1px solid var(--line);vertical-align:top;white-space:nowrap}td.grow{white-space:normal;word-break:break-word;min-width:140px}th{color:var(--dim);font-weight:normal;border-top:none;font-size:11px}tr.rowhead{cursor:pointer}tr.rowhead:hover{background:var(--row-hover)}.cards{display:grid;grid-template-columns:repeat(auto-fill,minmax(min(var(--card-min),100%),1fr));gap:var(--space-3)}.card{background:var(--card);border:1px solid var(--line);border-radius:var(--card-radius);padding:var(--card-pad);cursor:pointer;min-width:0;overflow:hidden;overflow-wrap:anywhere}.card:hover{border-color:var(--line-hover)}.worker-harness{cursor:help}.worker-harness:focus-visible{outline:2px solid var(--ok);outline-offset:3px}.worker-tooltip{position:fixed;z-index:20;max-width:264px;padding:6px 9px;background:var(--card);color:var(--fg);border:1px solid var(--line-hover);border-radius:var(--badge-radius);box-shadow:0 3px 12px var(--scrim);font-size:12px;white-space:normal;pointer-events:none}.worker-details dl{display:grid;grid-template-columns:max-content 1fr;gap:4px 14px;font-size:12px}.worker-details dt{color:var(--dim)}.worker-details dd{margin:0;overflow-wrap:anywhere}.card .top{display:flex;justify-content:space-between;gap:var(--space-3);align-items:center;min-height:22px}.card .top>b,.card .top>.prname{flex:1 1000000 auto;min-width:min(10ch,100%);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.card .top .badge{flex:0 0 auto}.card .top .badge.long{flex:0 1 auto;min-width:min(12ch,100%);max-width:min(26ch,100%);overflow:hidden;text-overflow:ellipsis}.card .meta{color:var(--dim);font-size:12px;margin-top:2px;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}.card .note{margin-top:6px;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}.activity{margin-top:4px;font-size:12px;color:var(--fg);opacity:0.85}.waiting{margin-top:4px;font-size:12px;color:var(--dim)}.activity.stale{color:var(--dim);opacity:1}.card .foot{margin-top:8px;display:flex;gap:10px;flex-wrap:wrap;align-items:center;color:var(--dim);font-size:12px}.card .foot>*{min-width:0}.card .foot .footact{margin-left:auto}.catchn{white-space:nowrap}.btn{background:var(--card);border:1px solid var(--line);border-radius:5px;color:var(--dim);font:inherit;font-size:11px;padding:1px 8px;cursor:pointer;white-space:nowrap}.btn:hover{color:var(--fg);border-color:var(--line-hover)}.badge{border-radius:var(--badge-radius);padding:var(--badge-pad);font-size:var(--badge-font);line-height:1.5;border:1px solid var(--line);white-space:nowrap;display:inline-block}.badge.pr-merged{background:var(--pr-merged);border-color:var(--pr-merged-fg);color:var(--on-fill)}.badge.pr-open{background:var(--pr-open);border-color:var(--pr-open);color:var(--on-fill)}.badge.pr-draft{background:var(--pr-draft);border-color:var(--pr-draft);color:var(--on-fill)}.badge.pr-closed{background:var(--pr-closed);border-color:var(--pr-closed);color:var(--on-fill)}.badge.pr-conflicts{background:var(--pr-closed);border-color:var(--pr-closed);color:var(--on-fill)}.badge.pr-behind{background:var(--pr-draft);border-color:var(--pr-draft);color:var(--on-fill)}.badge.unreported{font-weight:normal;font-size:10px;padding:0 5px;vertical-align:1px}.v-done{color:var(--ok)}.v-working,.v-queued{color:var(--fg)}.v-needs-decision,.v-blocked{color:var(--bad);font-weight:bold}.v-failed{color:var(--bad)}.v-paused,.v-unknown{color:var(--dim)}.dim{color:var(--dim)}.ok{color:var(--ok)}.warn{color:var(--warn)}.bad{color:var(--bad)}.detail{display:none}.detail.open{display:table-row}.detail td{background:var(--inset);white-space:pre-wrap;word-break:break-word;font-size:12px;color:var(--dim)}.detail b{color:var(--fg)}.empty{color:var(--dim);padding:6px 2px}.older{padding:8px 2px}#stale{display:none;color:var(--bad)}a{color:var(--link);text-decoration:none}.dot{display:inline-block;width:8px;height:8px;border-radius:50%;margin-right:5px;vertical-align:middle}.dot.ok{background:var(--ok)}.dot.warn{background:var(--warn)}.dot.bad{background:var(--bad)}.dot.dim{background:var(--dim)}#overlay{position:fixed;inset:0;background:var(--scrim);display:none;z-index:var(--layer-modal);padding:28px 16px;overflow:auto}#overlay.open{display:block}.modal{max-width:var(--modal-width);margin:0 auto;background:var(--card);border:1px solid var(--line);border-radius:var(--modal-radius);padding:16px 18px;position:relative}.modal .x{position:absolute;top:8px;right:12px;cursor:pointer;color:var(--dim);font-size:18px;line-height:1}.modal .x:hover{color:var(--fg)}.modal h3{margin:0 24px 2px 0;font-size:14px}.modal .sub{color:var(--dim);font-size:12px;margin-bottom:10px}.modal .sec{color:var(--dim);text-transform:uppercase;letter-spacing:0.08em;font-size:11px;margin:14px 0 4px}.modal pre{white-space:pre-wrap;word-break:break-word;margin:0;font:inherit;color:var(--fg)}.msg{border-left:3px solid var(--line);padding:3px 10px;margin:8px 0;white-space:pre-wrap;word-break:break-word}.msg.from-helm{border-color:var(--link)}.msg .hdr{color:var(--dim);font-size:11px}.catch{border:1px solid var(--line);border-radius:var(--radius);padding:6px 10px;margin:8px 0}.catch.click{cursor:pointer}.catch .hdr{display:flex;gap:10px;flex-wrap:wrap;align-items:baseline}.loglines{color:var(--dim);font-size:12px;white-space:pre-wrap;word-break:break-word;margin-top:4px}.cmd{display:flex;gap:var(--space-2);align-items:center;margin:5px 0;flex-wrap:wrap}.cmd code{background:var(--inset);border:1px solid var(--line);border-radius:5px;padding:3px 8px;word-break:break-all;cursor:pointer}.cmd code:hover{border-color:var(--line-hover)}.cmd code.copied{border-color:var(--ok)}.cmd button{background:var(--card);border:1px solid var(--line);border-radius:5px;color:var(--dim);font:inherit;font-size:11px;padding:2px 8px;cursor:pointer}.cmd button:hover{color:var(--fg)}.chip.click{cursor:pointer}.chip.click:hover{border-color:var(--line-hover)}footer{margin-top:26px;padding-top:10px;border-top:1px solid var(--line);color:var(--dim);font-size:12px;display:flex;gap:var(--space-2);flex-wrap:wrap}.lob{position:fixed;bottom:6px;left:0;z-index:5;cursor:pointer;font-size:34px;line-height:1;user-select:none;animation:crawl 18s linear infinite}a.lob{color:inherit}.lob .bub .badge{display:inline-block;margin:0 0 3px}.pet-art{display:block;position:relative;width:72px;height:56px}.pet-art .sprite{width:72px;height:56px;background-position:0 0;background-repeat:no-repeat;background-size:400% 100%;image-rendering:pixelated;animation:step 0.5s steps(4) infinite}.lob .bub{display:none;position:absolute;bottom:58px;right:-8px;z-index:2;background:var(--card);border:1px solid var(--line);border-radius:9px;padding:6px 9px 5px;font-size:11px;line-height:1.35;color:var(--fg);width:max-content;max-width:150px;box-shadow:0 2px 8px rgba(0,0,0,0.4)}.lob:hover .bub{display:block}.lob .bub span{display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;white-space:normal;word-break:break-word}.pet-art .star{position:absolute;left:56px;top:-9px;height:14px;image-rendering:pixelated;z-index:3}.lob:hover{animation-play-state:paused}@keyframes crawl{0%{transform:translateX(-90px)}100%{transform:translateX(100vw)}}@keyframes step{to{background-position-x:-288px}}#gearbtn{background:none;border:1px solid var(--line);border-radius:var(--radius);color:var(--dim);font:inherit;font-size:13px;padding:1px 7px;margin-left:8px;cursor:pointer;vertical-align:1px}#gearbtn:hover,#gearbtn.on{color:var(--fg);border-color:var(--line-hover)}.settings .row{display:flex;justify-content:space-between;align-items:center;gap:14px;margin:12px 0}.settings .lbl{color:var(--fg);font-size:12px}.settings .hint{display:block;color:var(--dim);font-size:11px;margin-top:2px}.dlist{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:6px}.drow{display:flex;flex-wrap:wrap;align-items:baseline;gap:var(--space-2);background:var(--card);border:1px solid var(--line);border-radius:var(--card-radius);padding:8px 12px}.drow.unread{background:var(--unread-bg);border-color:var(--bad)}.drow.answered{opacity:0.7}.drow.focus{animation:dflash 1.6s ease-out 1}.drow .dopen{flex:1 1 280px;display:flex;align-items:baseline;gap:var(--space-2);min-width:0;background:none;border:none;padding:0;color:var(--fg);font:inherit;text-align:left;cursor:pointer}.drow .dopen:hover .dtitle{text-decoration:underline}.drow .dtitle{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.drow .dmeta,.drow .dage{font-size:11px}.drow .dlink,.dmodal .dlink{color:var(--link)}.dstate{font-size:var(--badge-font);border-radius:var(--badge-radius);padding:var(--badge-pad);border:1px solid var(--line);color:var(--dim)}.dstate.unread{border-color:var(--bad);color:var(--bad)}.dstate.answered{color:var(--ok)}.dbadge{display:inline-block;margin-left:6px;min-width:1.6em;text-align:center;border-radius:999px;padding:0 6px;font-size:11px;font-weight:bold;background:var(--bad);color:var(--on-fill)}#doverlay{position:fixed;inset:0;background:var(--scrim);z-index:var(--layer-decision-modal);padding:28px 16px;overflow:auto}.dmodal:focus{outline:none}.dmodal .x{background:none;border:none;font:inherit;font-size:18px;color:var(--dim)}.dnav{display:flex;align-items:center;gap:var(--space-2);margin:0 28px var(--space-3) 0}.dnav .dpos{color:var(--dim);font-size:11px}.dmodal .dtop{display:flex;flex-wrap:wrap;align-items:baseline;gap:var(--space-2);margin-bottom:var(--space-2)}.dmodal .dtitle{white-space:pre-wrap;overflow-wrap:anywhere;font-size:14px}.dmodal .dmeta{font-size:11px}.ddone{padding:28px 8px;text-align:center;cursor:pointer}.ddone .ok{font-size:15px;margin-bottom:var(--space-2)}.dalert-live{position:fixed;right:var(--space-4);bottom:var(--space-4);z-index:var(--layer-alert)}.dalert{display:flex;align-items:center;gap:4px;background:var(--card);border:1px solid var(--bad);border-radius:var(--card-radius);padding:8px 10px;box-shadow:0 6px 24px var(--scrim)}.dalert .dalert-open{display:flex;align-items:center;gap:var(--space-2);background:none;border:none;color:var(--fg);font:inherit;cursor:pointer}.dalert .dx{background:none;border:none;color:var(--dim);font:inherit;font-size:15px;cursor:pointer}.dcount{display:inline-block;min-width:1.8em;text-align:center;border-radius:999px;padding:1px 7px;font-weight:bold;background:var(--bad);color:var(--on-fill);animation:dglow 1.8s ease-in-out infinite}@keyframes dglow{0%,100%{box-shadow:0 0 0 0 transparent}50%{box-shadow:0 0 10px 3px var(--bad)}}@media(prefers-reduced-motion:reduce){.dcount,.drow.focus{animation:none}}.sr-only{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap}@keyframes dflash{0%,35%{border-color:var(--link);box-shadow:0 0 0 3px var(--link)}100%{border-color:var(--line);box-shadow:0 0 0 0 transparent}}.dfiles{display:flex;flex-wrap:wrap;gap:var(--space-2);margin:6px 0}.dfiles .dimg img{max-height:160px;max-width:320px;border:1px solid var(--line);border-radius:var(--radius);display:block}.dfiles .dfile{width:100%;font-size:11px}.doptions{display:flex;flex-wrap:wrap;gap:6px;margin:8px 0 6px}.btn.dopt{font-size:12px;padding:3px 12px;color:var(--fg)}.btn.dopt.on{background:var(--seg-on);border-color:var(--link)}.danswer{display:block;width:100%;min-height:3.2em;max-height:50vh;margin:6px 0;padding:6px 8px;resize:vertical;overflow-y:auto;field-sizing:content;background:var(--inset);color:var(--fg);border:1px solid var(--line);border-radius:var(--radius);font:inherit}.dkey,.dlegend kbd{display:inline-block;min-width:1.5em;text-align:center;font:inherit;font-size:10px;line-height:1.4;padding:0 4px;border:1px solid var(--line-hover);border-radius:4px;color:var(--dim);background:var(--inset)}.btn.dopt .dkey{margin-right:6px}.btn.dopt.on .dkey{border-color:var(--link);color:var(--fg)}.danswer-wrap{position:relative}.danswer-wrap .dkey-text{position:absolute;top:6px;right:8px;pointer-events:none}.dlegend{margin-top:var(--space-3);padding-top:var(--space-2);border-top:1px solid var(--line);font-size:11px;line-height:1.9}.danswer:focus{outline:none;border-color:var(--line-hover)}.dfoot{display:flex;flex-wrap:wrap;align-items:center;gap:6px}.dfoot .dattach input{display:none}.dfoot .dchip{font-size:11px;border:1px solid var(--line);border-radius:var(--badge-radius);padding:0 0 0 6px}.dfoot .dx{background:none;border:none;color:var(--dim);cursor:pointer;font:inherit}.dfoot .derr{font-size:11px}.btn.dsend{margin-left:auto;color:var(--fg);padding:2px 14px}.btn:disabled{cursor:default;opacity:0.6}.danswered{margin-top:6px}.thumb,.dimg,.mdimg{background:none;border:none;padding:0;cursor:zoom-in;font:inherit}.thumb{display:block;margin:4px 0}.thumb img{max-height:120px;max-width:240px;border:1px solid var(--line);border-radius:var(--radius);display:block}.lightbox{position:fixed;inset:0;z-index:20;background:rgba(0,0,0,0.85);display:flex;flex-direction:column;align-items:center;justify-content:center;gap:var(--space-2)}.lightbox .lbimg{max-width:90vw;max-height:90vh;object-fit:contain;border-radius:var(--radius);box-shadow:0 8px 40px rgba(0,0,0,0.6)}.lightbox .lbclose{position:absolute;top:12px;right:16px;background:none;border:none;color:var(--fg);font-size:28px;line-height:1;cursor:pointer}.lightbox .lboriginal{color:var(--link);font-size:12px}.mdpage{background:var(--inset);border:1px solid var(--line);border-radius:6px;padding:8px 14px;margin:6px 0;line-height:1.5;overflow-wrap:anywhere}.mdpage h1,.mdpage h2,.mdpage h3,.mdpage h4{margin:12px 0 6px;line-height:1.25}.mdpage h1{font-size:18px}.mdpage h2{font-size:15px}.mdpage h3,.mdpage h4{font-size:13px}.mdpage p,.mdpage ul,.mdpage ol,.mdpage blockquote{margin:6px 0}.mdpage ul,.mdpage ol{padding-left:22px}.mdpage a{color:var(--link)}.mdpage code{background:var(--card);border-radius:3px;padding:0 3px}.mdpage pre.mdcode{background:var(--card);border:1px solid var(--line);border-radius:4px;padding:8px;max-width:100%;overflow-x:auto;white-space:pre;overflow-wrap:normal}.mdpage pre.mdcode code{padding:0;background:none}.mdpage blockquote{border-left:3px solid var(--line);padding-left:10px;color:var(--dim)}.mdpage img{max-width:100%;height:auto;border-radius:4px;display:block;margin:6px 0}.mdpage hr{border:0;border-top:1px solid var(--line)}.mdpage .mdtable{max-width:100%;overflow-x:auto}.mdpage table{border-collapse:collapse;margin:6px 0}.mdpage th,.mdpage td{border:1px solid var(--line);padding:3px 8px;text-align:left;white-space:normal;overflow-wrap:anywhere;min-width:12ch}.tabhead{margin:0 0 8px}.newtrap{display:inline-flex;flex-wrap:wrap;gap:6px;align-items:center;font-size:12px;font-weight:normal;text-transform:none;letter-spacing:normal}.startcmds{margin:6px 0;font-size:11px;cursor:default}.reportview{width:100%;max-width:1100px;margin:0 auto;padding:20px 16px 48px;overflow-wrap:anywhere}.reportview h1{font-size:18px;margin:0 0 4px}.reportview .sub{color:var(--dim);font-size:12px;margin-bottom:10px}.reportview .sec{color:var(--dim);text-transform:uppercase;letter-spacing:0.08em;font-size:11px;margin:14px 0 4px}.reportview .mdpage{padding:12px 20px}@media(max-width:600px){.reportview{padding:12px 0 32px}.reportview .mdpage{padding:8px 12px}}.stats{display:flex;flex-direction:column;gap:var(--space-4);max-width:100%}.stats h2{margin:0 0 var(--space-2)}.stat-tiles{display:grid;grid-template-columns:repeat(auto-fit,minmax(100px,1fr));gap:var(--space-2)}.stat-tile{background:var(--card);border:1px solid var(--line);border-radius:var(--card-radius);padding:var(--card-pad)}.stat-tile .n{font-size:22px;font-weight:bold;color:var(--fg)}.stats-empty{display:flex;align-items:center;gap:var(--space-4);background:var(--card);border:1px solid var(--line);border-radius:var(--card-radius);padding:var(--space-4) var(--space-4) var(--space-3)}.stats-empty .pet-art{flex:none;margin-top:6px}.heat{background:var(--card);border:1px solid var(--line);border-radius:var(--card-radius);padding:var(--card-pad);min-width:0}.heat-scroll{overflow-x:auto;padding:2px 0 4px}.heatmap{border-collapse:separate;border-spacing:var(--heat-gap);width:auto;margin:0;background:none;border:0;border-radius:0;overflow:visible}.heatmap td{padding:0;border:0;vertical-align:middle;font-size:10px;line-height:1;color:var(--dim)}.heatmap .month{position:relative;height:14px}.heatmap .month span{position:absolute;left:0;bottom:3px;white-space:nowrap}.heatmap .wd{position:sticky;left:0;z-index:1;background:var(--card);padding-right:var(--space-1);white-space:nowrap}.heatmap .day,.heatmap .pad,.heat-legend .swatch{width:var(--heat-cell);min-width:var(--heat-cell);height:var(--heat-cell);border-radius:2px}.heatmap .day{position:relative;cursor:default;outline:1px solid rgba(255,255,255,0.04);outline-offset:-1px}.heatmap .day:focus-visible{outline:2px solid var(--link);outline-offset:0}.l0{background:var(--heat-0)}.l1{background:var(--heat-1)}.l2{background:var(--heat-2)}.l3{background:var(--heat-3)}.l4{background:var(--heat-4)}.heatmap .day:hover::after,.heatmap .day:focus::after{content:attr(data-tip);position:absolute;bottom:calc(100% + 6px);left:50%;transform:translateX(-50%);z-index:4;background:var(--bg);border:1px solid var(--line-hover);border-radius:var(--badge-radius);color:var(--fg);font-size:11px;padding:3px 7px;white-space:nowrap;pointer-events:none}.heatmap .day.below:hover::after,.heatmap .day.below:focus::after{bottom:auto;top:calc(100% + 6px)}.heatmap .day.tl:hover::after,.heatmap .day.tl:focus::after{left:0;transform:none}.heatmap .day.tr:hover::after,.heatmap .day.tr:focus::after{left:auto;right:0;transform:none}.heat-foot{display:flex;flex-wrap:wrap;justify-content:space-between;align-items:center;gap:var(--space-2);font-size:11px;margin-top:var(--space-1)}.heat-legend{display:inline-flex;align-items:center;gap:var(--heat-gap);color:var(--dim);margin-left:auto}.heat-legend .swatch{display:inline-block}.heat-legend .swatch:first-of-type{margin-left:var(--space-1)}.heat-legend .swatch:last-of-type{margin-right:var(--space-1)}.top-traps{background:var(--card);border:1px solid var(--line);border-radius:var(--card-radius);padding:var(--card-pad)}.top-traps ol{list-style:none;margin:0;padding:0;display:grid;gap:6px}.top-traps li{display:grid;grid-template-columns:minmax(0,14em) minmax(40px,32em) auto;justify-content:start;align-items:center;gap:var(--space-3)}.top-traps .name{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.top-traps .bar{height:6px;background:var(--inset);border-radius:3px;overflow:hidden}.top-traps .bar span{display:block;height:100%;background:var(--lobstah)}.top-traps .n{color:var(--dim);white-space:nowrap}@media(max-width:600px){.tabs{flex-wrap:wrap}.tabs a{white-space:nowrap}}</style>\n </head>\n <body>\n <!-- The page is rendered by Preact (src/components/app.ts): header, tabs, controls, the five tab pages, footer, lobs, modal overlay. -->\n <script>"use strict";(()=>{var We,y,ys,Kr,F,$s,_s,ws,kt,He,$e,Ss,_t,bt,yt,Vr,Ue={},Fe=[],zr=/acit|ex(?:s|g|n|p|$)|rph|grid|ows|mnc|ntw|ine[ch]|zoo|^ord|itera/i,Ke=Array.isArray;function L(e,t){for(var s in t)e[s]=t[s];return e}function wt(e){e&&e.parentNode&&e.parentNode.removeChild(e)}function Z(e,t,s){var n,r,o,a={};for(o in t)o=="key"?n=t[o]:o=="ref"?r=t[o]:a[o]=t[o];if(arguments.length>2&&(a.children=arguments.length>3?We.call(arguments,2):s),typeof e=="function"&&e.defaultProps!=null)for(o in e.defaultProps)a[o]===void 0&&(a[o]=e.defaultProps[o]);return je(e,a,n,r,null)}function je(e,t,s,n,r){var o={type:e,props:t,key:s,ref:n,__k:null,__:null,__b:0,__e:null,__c:null,constructor:void 0,__v:r??++ys,__i:-1,__u:0};return r==null&&y.vnode!=null&&y.vnode(o),o}function ee(e){return e.children}function Be(e,t){this.props=e,this.context=t}function V(e,t){if(t==null)return e.__?V(e.__,e.__i+1):null;for(var s;t<e.__k.length;t++)if((s=e.__k[t])!=null&&s.__e!=null)return s.__e;return typeof e.type=="function"?V(e):null}function Jr(e){if(e.__P&&e.__d){var t=e.__v,s=t.__e,n=[],r=[],o=L({},t);o.__v=t.__v+1,y.vnode&&y.vnode(o),St(e.__P,o,t,e.__n,e.__P.namespaceURI,32&t.__u?[s]:null,n,s??V(t),!!(32&t.__u),r),o.__v=t.__v,o.__.__k[o.__i]=o,Ts(n,o,r),t.__e=t.__=null,o.__e!=s&&Ds(o)}}function Ds(e){if((e=e.__)!=null&&e.__c!=null)return e.__e=e.__c.base=null,e.__k.some(function(t){if(t!=null&&t.__e!=null)return e.__e=e.__c.base=t.__e}),Ds(e)}function vs(e){(!e.__d&&(e.__d=!0)&&F.push(e)&&!qe.__r++||$s!=y.debounceRendering)&&(($s=y.debounceRendering)||_s)(qe)}function qe(){try{for(var e,t=1;F.length;)F.length>t&&F.sort(ws),e=F.shift(),t=F.length,Jr(e)}finally{F.length=qe.__r=0}}function xs(e,t,s,n,r,o,a,l,u,c,d){var h,p,m,f,$,_,x=n&&n.__k||Fe,b=t.length;for(u=Yr(s,t,x,u,b),h=0;h<b;h++)(m=s.__k[h])!=null&&(p=m.__i!=-1&&x[m.__i]||Ue,m.__i=h,_=St(e,m,p,r,o,a,l,u,c,d),f=m.__e,m.ref&&p.ref!=m.ref&&(p.ref&&Dt(p.ref,null,m),d.push(m.ref,m.__c||f,m)),$==null&&f!=null&&($=f),4&m.__u?(u=Gs(m,u,e),p.__e&&(p.__e=null)):typeof m.type=="function"&&_!==void 0?u=_:f&&(u=f.nextSibling),m.__u&=-7);return s.__e=$,u}function Yr(e,t,s,n,r){var o,a,l,u,c,d=s.length,h=d,p=0;for(e.__k=new Array(r),o=0;o<r;o++)(a=t[o])!=null&&typeof a!="boolean"&&typeof a!="function"?(typeof a=="string"||typeof a=="number"||typeof a=="bigint"||a.constructor==String?a=e.__k[o]=je(null,a,null,null,null):Ke(a)?a=e.__k[o]=je(ee,{children:a},null,null,null):a.constructor===void 0&&a.__b>0?a=e.__k[o]=je(a.type,a.props,a.key,a.ref?a.ref:null,a.__v):e.__k[o]=a,u=o+p,a.__=e,a.__b=e.__b+1,l=null,(c=a.__i=Qr(a,s,u,h))!=-1&&(h--,(l=s[c])&&(l.__u|=2)),l==null||l.__v==null?(c==-1&&(r>d?p--:r<d&&p++),typeof a.type!="function"&&(a.__u|=4)):c!=u&&(c==u-1?p--:c==u+1?p++:(c>u?p--:p++,a.__u|=4))):e.__k[o]=null;if(h)for(o=0;o<d;o++)(l=s[o])!=null&&(2&l.__u)==0&&(l.__e==n&&(n=V(l)),Rs(l,l));return n}function Gs(e,t,s){var n,r;if(typeof e.type=="function"){for(n=e.__k,r=0;n&&r<n.length;r++)n[r]&&(n[r].__=e,t=Gs(n[r],t,s));return t}e.__e!=t&&(t&&e.type&&!t.parentNode&&(t=V(e)),t=s.insertBefore(e.__e,t||null));do t=t&&t.nextSibling;while(t!=null&&t.nodeType==8);return t}function Qr(e,t,s,n){var r,o,a,l=e.key,u=e.type,c=t[s],d=c!=null&&(2&c.__u)==0;if(c===null&&l==null||d&&l==c.key&&u==c.type)return s;if(n>(d?1:0)){for(r=s-1,o=s+1;r>=0||o<t.length;)if((c=t[a=r>=0?r--:o++])!=null&&(2&c.__u)==0&&l==c.key&&u==c.type)return a}return-1}function ks(e,t,s){t[0]=="-"?e.setProperty(t,s??""):e[t]=s==null?"":typeof s!="number"||zr.test(t)?s:s+"px"}function Ne(e,t,s,n,r){var o,a;e:if(t=="style")if(typeof s=="string")e.style.cssText=s;else{if(typeof n=="string"&&(e.style.cssText=n=""),n)for(t in n)s&&t in s||ks(e.style,t,"");if(s)for(t in s)n&&s[t]==n[t]||ks(e.style,t,s[t])}else if(t[0]=="o"&&t[1]=="n")o=t!=(t=t.replace(Ss,"$1")),a=t.toLowerCase(),t=a in e||t=="onFocusOut"||t=="onFocusIn"?a.slice(2):t.slice(2),e.l||(e.l={}),e.l[t+o]=s,s?n?s[$e]=n[$e]:(s[$e]=_t,e.addEventListener(t,o?yt:bt,o)):e.removeEventListener(t,o?yt:bt,o);else{if(r=="http://www.w3.org/2000/svg")t=t.replace(/xlink(H|:h)/,"h").replace(/sName$/,"s");else if(t!="width"&&t!="height"&&t!="href"&&t!="list"&&t!="form"&&t!="tabIndex"&&t!="download"&&t!="rowSpan"&&t!="colSpan"&&t!="role"&&t!="popover"&&t in e)try{e[t]=s??"";break e}catch{}typeof s=="function"||(s==null||s===!1&&t[4]!="-"?e.removeAttribute(t):e.setAttribute(t,t=="popover"&&s==1?"":s))}}function bs(e){return function(t){if(this.l){var s=this.l[t.type+e];if(t[He]==null)t[He]=_t++;else if(t[He]<s[$e])return;return s(y.event?y.event(t):t)}}}function St(e,t,s,n,r,o,a,l,u,c){var d,h,p,m,f,$,_,x,b,P,he,K,ge,gs,Le,vt,I=t.type;if(t.constructor!==void 0)return null;128&s.__u&&(u=!!(32&s.__u),o=[l=t.__e=s.__e]),(d=y.__b)&&d(t);e:if(typeof I=="function"){h=a.length;try{if(b=t.props,P=I.prototype&&I.prototype.render,he=(d=I.contextType)&&n[d.__c],K=d?he?he.props.value:d.__:n,s.__c?x=(p=t.__c=s.__c).__=p.__E:(P?t.__c=p=new I(b,K):(t.__c=p=new Be(b,K),p.constructor=I,p.render=Zr),he&&he.sub(p),p.state||(p.state={}),p.__n=n,m=p.__d=!0,p.__h=[],p._sb=[]),P&&p.__s==null&&(p.__s=p.state),P&&I.getDerivedStateFromProps!=null&&(p.__s==p.state&&(p.__s=L({},p.__s)),L(p.__s,I.getDerivedStateFromProps(b,p.__s))),f=p.props,$=p.state,p.__v=t,m)P&&I.getDerivedStateFromProps==null&&p.componentWillMount!=null&&p.componentWillMount(),P&&p.componentDidMount!=null&&p.__h.push(p.componentDidMount);else{if(P&&I.getDerivedStateFromProps==null&&b!==f&&p.componentWillReceiveProps!=null&&p.componentWillReceiveProps(b,K),t.__v==s.__v||!p.__e&&p.shouldComponentUpdate!=null&&p.shouldComponentUpdate(b,p.__s,K)===!1){t.__v!=s.__v&&(p.props=b,p.state=p.__s,p.__d=!1),t.__e=s.__e,t.__k=s.__k,t.__k.some(function(X){X&&(X.__=t)}),Fe.push.apply(p.__h,p._sb),p._sb=[],p.__h.length&&a.push(p),l=V(s);break e}p.componentWillUpdate!=null&&p.componentWillUpdate(b,p.__s,K),P&&p.componentDidUpdate!=null&&p.__h.push(function(){p.componentDidUpdate(f,$,_)})}if(p.context=K,p.props=b,p.__P=e,p.__e=!1,ge=y.__r,gs=0,P)p.state=p.__s,p.__d=!1,ge&&ge(t),d=p.render(p.props,p.state,p.context),Fe.push.apply(p.__h,p._sb),p._sb=[];else do p.__d=!1,ge&&ge(t),d=p.render(p.props,p.state,p.context),p.state=p.__s;while(p.__d&&++gs<25);p.state=p.__s,p.getChildContext!=null&&(n=L(L({},n),p.getChildContext())),P&&!m&&p.getSnapshotBeforeUpdate!=null&&(_=p.getSnapshotBeforeUpdate(f,$)),Le=d!=null&&d.type===ee&&d.key==null?As(d.props.children):d,l=xs(e,Ke(Le)?Le:[Le],t,s,n,r,o,a,l,u,c),p.base=t.__e,t.__u&=-161,p.__h.length&&a.push(p),x&&(p.__E=p.__=null)}catch(X){if(a.length=h,t.__v=null,u||o!=null){if(X.then){for(t.__u|=u?160:128;l&&l.nodeType==8&&l.nextSibling;)l=l.nextSibling;o!=null&&(o[o.indexOf(l)]=null),t.__e=l}else if(o!=null)for(vt=o.length;vt--;)wt(o[vt])}else t.__e=s.__e;t.__k==null&&(t.__k=s.__k||[]),X.then||Cs(t),y.__e(X,t,s)}}else o==null&&t.__v==s.__v?(t.__k=s.__k,t.__e=s.__e):l=t.__e=Xr(s.__e,t,s,n,r,o,a,u,c);return(d=y.diffed)&&d(t),128&t.__u?void 0:l}function Cs(e){e&&(e.__c&&(e.__c.__e=!0),e.__k&&e.__k.some(Cs))}function Ts(e,t,s){for(var n=0;n<s.length;n++)Dt(s[n],s[++n],s[++n]);y.__c&&y.__c(t,e),e.some(function(r){try{e=r.__h,r.__h=[],e.some(function(o){o.call(r)})}catch(o){y.__e(o,r.__v)}})}function As(e){return typeof e!="object"||e==null||e.__b>0?e:Ke(e)?e.map(As):e.constructor!==void 0?null:L({},e)}function Xr(e,t,s,n,r,o,a,l,u){var c,d,h,p,m,f,$,_=s.props||Ue,x=t.props,b=t.type;if(b=="svg"?r="http://www.w3.org/2000/svg":b=="math"?r="http://www.w3.org/1998/Math/MathML":r||(r="http://www.w3.org/1999/xhtml"),o!=null){for(c=0;c<o.length;c++)if((m=o[c])&&"setAttribute"in m==!!b&&(b?m.localName==b:m.nodeType==3)){e=m,o[c]=null;break}}if(e==null){if(b==null)return document.createTextNode(x);e=document.createElementNS(r,b,x.is&&x),l&&(y.__m&&y.__m(t,o),l=!1),o=null}if(b==null)_===x||l&&e.data==x||(e.data=x);else{if(o=b=="textarea"&&x.defaultValue!=null?null:o&&We.call(e.childNodes),!l&&o!=null)for(_={},c=0;c<e.attributes.length;c++)_[(m=e.attributes[c]).name]=m.value;for(c in _)m=_[c],c=="dangerouslySetInnerHTML"?h=m:c=="children"||c in x||c=="value"&&"defaultValue"in x||c=="checked"&&"defaultChecked"in x||Ne(e,c,null,m,r);for(c in x)m=x[c],c=="children"?p=m:c=="dangerouslySetInnerHTML"?d=m:c=="value"?f=m:c=="checked"?$=m:l&&typeof m!="function"||_[c]===m||Ne(e,c,m,_[c],r);if(d)l||h&&(d.__html==h.__html||d.__html==e.innerHTML)||(e.innerHTML=d.__html),t.__k=[];else if(h&&(e.innerHTML=""),xs(t.type=="template"?e.content:e,Ke(p)?p:[p],t,s,n,b=="foreignObject"?"http://www.w3.org/1999/xhtml":r,o,a,o?o[0]:s.__k&&V(s,0),l,u),o!=null)for(c=o.length;c--;)wt(o[c]);l&&b!="textarea"||(c="value",b=="progress"&&f==null?e.removeAttribute("value"):f!=null&&(f!==e[c]||b=="progress"&&!f||b=="option"&&f!=_[c])&&Ne(e,c,f,_[c],r),c="checked",$!=null&&$!=e[c]&&Ne(e,c,$,_[c],r))}return e}function Dt(e,t,s){try{if(typeof e=="function"){var n=typeof e.__u=="function";n&&e.__u(),n&&t==null||(e.__u=e(t))}else e.current=t}catch(r){y.__e(r,s)}}function Rs(e,t,s){var n,r;if(y.unmount&&y.unmount(e),(n=e.ref)&&(n.current&&n.current!=e.__e||Dt(n,null,t)),(n=e.__c)!=null){if(n.componentWillUnmount)try{n.componentWillUnmount()}catch(o){y.__e(o,t)}n.base=n.__P=n.__n=null}if(n=e.__k)for(r=0;r<n.length;r++)n[r]&&Rs(n[r],t,s||typeof e.type!="function");s||wt(e.__e),e.__c=e.__=e.__e=void 0}function Zr(e,t,s){return this.constructor(e,s)}function xt(e,t,s){var n,r,o,a;t==document&&(t=document.documentElement),y.__&&y.__(e,t),r=(n=typeof s=="function")?null:s&&s.__k||t.__k,o=[],a=[],St(t,e=(!n&&s||t).__k=Z(ee,null,[e]),r||Ue,Ue,t.namespaceURI,!n&&s?[s]:r?null:t.firstChild?We.call(t.childNodes):null,o,!n&&s?s:r?r.__e:t.firstChild,n,a),Ts(o,e,a),e.props.children=null}We=Fe.slice,y={__e:function(e,t,s,n){for(var r,o,a;t=t.__;)if((r=t.__c)&&!r.__)try{if((o=r.constructor)&&o.getDerivedStateFromError!=null&&(r.setState(o.getDerivedStateFromError(e)),a=r.__d),r.componentDidCatch!=null&&(r.componentDidCatch(e,n||{}),a=r.__d),a)return r.__E=r}catch(l){e=l}throw e}},ys=0,Kr=function(e){return e!=null&&e.constructor===void 0},Be.prototype.setState=function(e,t){var s;s=this.__s!=null&&this.__s!=this.state?this.__s:this.__s=L({},this.state),typeof e=="function"&&(e=e(L({},s),this.props)),e&&L(s,e),e!=null&&this.__v&&(t&&this._sb.push(t),vs(this))},Be.prototype.forceUpdate=function(e){this.__v&&(this.__e=!0,e&&this.__h.push(e),vs(this))},Be.prototype.render=ee,F=[],_s=typeof Promise=="function"?Promise.prototype.then.bind(Promise.resolve()):setTimeout,ws=function(e,t){return e.__v.__b-t.__v.__b},qe.__r=0,kt=Math.random().toString(8),He="__d"+kt,$e="__a"+kt,Ss=/(PointerCapture)$|Capture$/i,_t=0,bt=bs(!1),yt=bs(!0),Vr=0;var te=e=>e>999?"999+":String(e);function eo(e){return{id:e,lane:"work",bucket:"done",repo:"",title:"",verb:"unknown",sort:0}}var Ms=new WeakMap;function Gt(e,t){let s=e.dispatches||[],n=Ms.get(s);return n||Ms.set(s,n=new Map(s.map(r=>[r.id,r]))),{...t,catches:(t.catches||[]).map(r=>n.get(r)??eo(r))}}function E(e){if(!e)return"dim";let t=e.state||"open";return t==="open"&&e.merge?"pr-"+e.merge:t==="open"&&e.tone&&e.tone!=="ok"?e.tone:"pr-"+t}var se=["deck","dispatches","traps","prs","reports","notices","stats"];function Os(e){let t=String(e||"").replace(/^#/,"");return se.includes(t)?t:"deck"}var Ct=e=>`#decision/${encodeURIComponent(e)}`;function ne(e){let t=/^#?decision\\/(.+)$/.exec(String(e||""));if(!t)return null;try{return decodeURIComponent(t[1])}catch{return null}}var Tt=(e,t,s)=>`/attachment/dispatch/${encodeURIComponent(e)}/${encodeURIComponent(t)}/${encodeURIComponent(s)}`,Ls=(e,t)=>`/attachment/trap/${encodeURIComponent(e)}/${encodeURIComponent(t)}`,Ns=e=>/\\.(png|jpe?g|gif|webp)$/i.test(e),At=(e,t)=>`/decision/${encodeURIComponent(e)}/files/${encodeURIComponent(t)}`;function Ve(e,t){let s=new Map(t.map(r=>[r.key,r])),n=[];for(let r of e)if(r.kind==="decision"){let o=s.get(r.key);if(!o)continue;n.push({kind:"decision",key:o.key,title:o.title,detail:o.detail,options:o.options,attachments:o.attachments,...o.dispatch?{dispatch:o.dispatch,lane:o.lane??"work"}:{},...o.repo?{repo:o.repo}:{},at:o.askedAt,...o.viewedAt??r.viewedAt?{viewedAt:o.viewedAt??r.viewedAt}:{}})}else r.kind==="question"&&n.push({kind:"question",key:r.key,verb:r.verb,note:r.note||r.verb,dispatch:r.id,lane:r.lane,...r.repo?{repo:r.repo}:{},at:r.at??"",...r.viewedAt?{viewedAt:r.viewedAt}:{}});return n.sort((r,o)=>o.at.localeCompare(r.at)||r.key.localeCompare(o.key))}function Hs(e){let t=(e.text??"").split(`\n`).map(s=>s.trim()).find(Boolean)??"";return[e.option,t&&(t.length>80?t.slice(0,79)+"\\u2026":t),e.files?`${e.files} file${e.files===1?"":"s"}`:""].filter(Boolean).join(" \\xB7 ")}function js(e,t){return(e.reports||[]).find(s=>s.key===`report:${t.lane}:${t.id}`)}function ve(e){return e.author==="helm"?"":e.trap?e.trap:e.author==="headless"?e.dispatch?e.dispatch.slice(0,8):"":e.author}var Ps=(e,t)=>+!!e.acked-+!!t.acked||t.filedAt.localeCompare(e.filedAt),G=e=>`/report/${encodeURIComponent(e)}`;function ke(e){let t=/^#?report\\/(.+)$/.exec(String(e||""));if(!t)return null;try{return decodeURIComponent(t[1])}catch{return null}}function Bs(e){return typeof e!="string"?!1:e===""?!0:e.length>1024||!e.startsWith("#")||/[\\s\\x00-\\x1f\\x7f]/.test(e)?!1:se.includes(e.slice(1))||ne(e)!==null||ke(e)!==null}function Us(e){let t=/^\\/report\\/([^/?#]+)\\/?$/.exec(e);if(!t)return null;try{return decodeURIComponent(t[1])}catch{return null}}var Fs=e=>`/report/${encodeURIComponent(e)}/md`,Rt=(e,t)=>`/report/${encodeURIComponent(e)}/files/${encodeURIComponent(t)}`,Mt={dispatches:[],notices:[],prs:[],stacks:[]};function qs(e,t){if(t.kind==="dispatches"){let r=new Set(e.dispatches.map(o=>o.lane+":"+o.id));return{...e,dispatches:[...e.dispatches,...t.items.filter(o=>!r.has(o.lane+":"+o.id))]}}if(t.kind==="notices"){let r=new Set(e.notices.map(o=>o.seq));return{...e,notices:[...e.notices,...t.items.filter(o=>!r.has(o.seq))]}}let s=new Set(e.prs.map(r=>r.key)),n=new Set(e.stacks.map(r=>r.id));return{...e,prs:[...e.prs,...t.items.filter(r=>!s.has(r.key))],stacks:[...e.stacks,...t.stacks.filter(r=>!n.has(r.id))]}}function Ws(e,t){if(t===Mt||!t.dispatches.length&&!t.notices.length&&!t.prs.length)return e;let s=new Set(e.dispatches.map(a=>a.lane+":"+a.id)),n=new Set(e.notices.map(a=>a.seq)),r=new Set(e.prs.map(a=>a.key)),o=new Set(e.stacks.map(a=>a.id));return{...e,dispatches:[...e.dispatches,...t.dispatches.filter(a=>!s.has(a.lane+":"+a.id))],notices:[...e.notices,...t.notices.filter(a=>!n.has(a.seq))],prs:[...e.prs,...t.prs.filter(a=>!r.has(a.key))],stacks:[...e.stacks,...t.stacks.filter(a=>!o.has(a.id))]}}function Ks(e,t,s){return Math.max(0,(e.older?.[s]??0)-t[s].length)}function Vs(e,t){return{...e,now:t.now,daemon:e.daemon&&t.daemon!==void 0?{...e.daemon,heartbeat:t.daemon}:e.daemon,helms:e.helms.map(s=>t.helms[s.grounds]!==void 0?{...s,heartbeatAt:t.helms[s.grounds]}:s),traps:e.traps.map(s=>t.traps[s.trapId]?{...s,...t.traps[s.trapId]}:s)}}function Is(e,t,s){return!!e&&s-Date.parse(e)>t}function Es(e,t){if(t.lane&&e.lane!==t.lane||t.repo&&e.repo!==t.repo||t.verb&&e.verb!==t.verb)return!1;if(t.q){let s=t.q.toLowerCase();if(!(e.id+" "+(e.note||"")+" "+(e.title||"")+" "+(e.repo||"")+" "+(e.for||"")).toLowerCase().includes(s))return!1}return!0}function re(e,t,s){if(!t)return null;if(t.type==="helm")return e.helms.find(r=>r.grounds===t.key)||null;if(t.type==="dispatch"){let r=e.dispatches.find(a=>a.lane+":"+a.id===t.key),o=s&&s.key===t.key?s.data:void 0;return r&&o?{...r,...o}:r??o??null}if(t.type==="pr")return(e.prs||[]).find(r=>r.key===t.key)||null;if(t.type==="settings")return{attentionKinds:e.attentionKinds||[],attentionError:e.attentionError};let n=e.traps.find(r=>r.trapId===t.key);return n?Gt(e,n):null}function zs(e,t,s){let n=t.st,r=String(n.q||"").toLowerCase(),o=(...f)=>!r||f.join(" ").toLowerCase().includes(r),a=f=>({x:f,stale:Is(f.heartbeatAt,18e5,s)}),l=re(e,t.modal,t.detail),u=(f,$)=>!!f&&s-Date.parse(f)<=$,c=f=>Math.max(0,...(f.notices||[]).filter($=>$.kind==="trap-stowed"||$.kind==="trap-ghosted").map($=>Date.parse($.at)||0)),d=f=>f.live||!!f.starting||!!f.requested,h=[...e.traps||[]].sort((f,$)=>Number(d($))-Number(d(f))||Number($.live)-Number(f.live)||(f.live?(Date.parse(f.signedOnAt||"")||0)-(Date.parse($.signedOnAt||"")||0):c($)-c(f))||(f.name??f.trapId).localeCompare($.name??$.trapId)),p=h.filter(f=>(d(f)||(f.notices||[]).some($=>($.kind==="trap-stowed"||$.kind==="trap-ghosted")&&u($.at,36e5)))&&o(f.name,f.trapId,f.repo,f.worktree)),m=({ageSecs:f,...$})=>$;return{chips:{daemon:e.daemon,daemonStale:!!e.daemon&&Is(e.daemon.heartbeat,9e4,s),helms:e.helms.map(a),stats:e.stats},deck:{view:n.view,decisions:Ve(e.attention||[],e.decisions||[]).filter(f=>f.kind==="decision"?o(f.kind,f.repo,f.title,f.dispatch,f.detail):o(f.kind,f.repo,f.note,f.dispatch)),prAttention:(e.attention||[]).filter(f=>f.kind&&f.kind.startsWith("pr:")).map(m),landed:(e.landed||[]).filter(f=>u(f.at,864e5)&&o(f.repo,f.note,f.id)).sort((f,$)=>Date.parse($.at)-Date.parse(f.at)).slice(0,8),reports:(e.reports||[]).filter(f=>o(f.title,f.author,f.dispatch,f.repo)).sort(Ps),inflight:e.dispatches.filter(f=>f.bucket!=="done"&&Es(f,{...n,lane:"",repo:"",verb:""})),traps:p.map(f=>a(Gt(e,f))),stacks:(e.stacks||[]).filter(f=>f.open&&o(f.repo,f.numbers.join(" "))),prs:(e.prs||[]).filter(f=>f.state==="OPEN"),error:e.attentionError},dispatches:{view:n.view,chain:n.chain,list:e.dispatches.filter(f=>Es(f,n))},traps:{view:n.view,list:h.filter(f=>(!n.repo||f.repo===n.repo)&&o(f.name,f.trapId,f.repo,f.worktree,f.harness)).map(f=>a(Gt(e,f)))},prs:{view:n.view,stacks:(e.stacks||[]).filter(f=>!n.repo||f.repo===n.repo),prs:(e.prs||[]).filter(f=>(!n.repo||f.repo===n.repo)&&o(f.number,f.title,f.url,f.state,f.baseRefName,f.headRefName)),watches:(e.watches||[]).filter(f=>!String(f.key).startsWith("pr:"))},reports:{view:n.view,list:(e.reports||[]).filter(f=>(!n.repo||f.repo===n.repo)&&o(f.title,f.author,f.dispatch,f.repo)).sort(Ps)},notices:{list:(e.notices||[]).filter(f=>(!n.repo||!f.repo||f.repo===n.repo)&&(!n.noticeKind||f.kind===n.noticeKind)&&o(f.kind,f.text,f.repo))},foot:{version:e.version,repoUrl:e.repoUrl},modal:{modal:t.modal,item:l&&a(l),prefs:t.modal&&t.modal.type==="settings"?{view:n.view,lobs:n.lobs}:void 0}}}function Js(e,t){let s=(e.prs||[]).find(l=>l.key===t);if(!s)return null;let n=(e.stacks||[]).find(l=>l.id===s.stackId),r=new Map((e.dispatches||[]).map(l=>[l.id,l])),o=(s.dispatchIds||[]).map(l=>{let u=r.get(l);return u?{id:l,verb:u.verb,modalKey:u.lane+":"+u.id}:{id:l,culled:!0}}),a=s.watch;return{pr:s,stack:n?{numbers:n.numbers,titles:n.numbers.map(l=>(e.prs||[]).find(u=>u.stackId===n.id&&u.number===l)?.title||""),position:s.position+1,size:n.numbers.length,floor:n.floor,nextNumber:n.nextNumber,nextMergeable:!!s.nextMergeable,blockedBy:s.blockedBy}:null,chain:o,watch:a?{key:a.key,owner:a.owner||"",lastCheckedAt:a.lastCheckedAt||null,lastError:a.lastError||null,cursor:a.cursor}:null}}function Ys(e){return e?{text:e.lastError?"failing":"watching",at:e.lastCheckedAt||null}:{text:"no watch",at:null}}function ze(e,t){return e.filter(s=>!t(s.key)).sort((s,n)=>s.at.localeCompare(n.at)||s.key.localeCompare(n.key))}function Qs(e,t,s){return ze(e,t).filter(n=>!n.viewedAt&&!s(n.key))}function Xs(){let e={view:"table",lane:"",repo:"",verb:"",q:"",lobs:!0,chain:!1,noticeKind:""};try{Object.assign(e,JSON.parse(localStorage.getItem("spyglass")||"{}"))}catch{}return e.view!=="table"&&e.view!=="cards"&&(e.view="table"),e.lobs=e.lobs!==!1,e}function Zs(e){try{localStorage.setItem("spyglass",JSON.stringify(e))}catch{}}function en(){try{return JSON.parse(localStorage.getItem("spyglass-lob-hidden")||"{}")||{}}catch{return{}}}function tn(e){try{localStorage.setItem("spyglass-lob-hidden",JSON.stringify(e))}catch{}}var be,Je;function oe(e){if(e.snapshot)return(Je?.snapshot!==e.snapshot||Je.older!==e.older)&&(Je={snapshot:e.snapshot,older:e.older,view:Ws(e.snapshot,e.older)}),Je.view}var sn=new Set,g=()=>be;function Pt(e){be=e}function k(e){be={...be,...e};for(let t of sn)t(be)}var It=e=>{sn.add(e)};function to(e,t){let s=oe(e);return s&&re(s,t,e.detail)?!0:t.type==="dispatch"&&e.detail?.key===t.key&&!e.detail.error}function Ye(e){let{modal:t,drafts:s,older:n,detail:r,decisionModal:o}=g(),a=Ve(e.attention||[],e.decisions||[]),l=new Set(a.map(c=>c.key)),u=Object.fromEntries(Object.entries(s).filter(([c])=>l.has(c)));k({snapshot:e,stale:!1,modal:t&&to({snapshot:e,older:n,detail:r},t)?t:null,...Object.keys(u).length!==Object.keys(s).length?{drafts:u}:{},...o?.key&&!l.has(o.key)&&!s[o.key]?.sent?{decisionModal:nn(a,u,o.key)}:{}})}function nn(e,t,s){let n=ze(e,o=>!!t[o]?.sent||o===s);if(!n.length)return null;let r=e.find(o=>o.key===s)?.at??"";return{key:(n.find(o=>o.at>r||o.at===r&&o.key>(s??""))??n[0]).key}}function Qe(){let e=oe(g());return e?Ve(e.attention||[],e.decisions||[]):[]}function ye(){let{drafts:e}=g();return ze(Qe(),t=>!!e[t]?.sent)}function Xe(){let{drafts:e,viewedHere:t}=g();return Qs(Qe(),s=>!!e[s]?.sent,s=>!!t[s])}function ae(e){let t=e??Xe()[0]?.key??ye()[0]?.key;t&&k({decisionModal:{key:t}})}function _e(e){let t=g().decisionModal;if(!t?.key)return;let s=ye(),n=s.findIndex(o=>o.key===t.key),r=s[n+e];n>=0&&r&&k({decisionModal:{key:r.key}})}var ie=()=>k({decisionModal:null}),we=9;function so(){let e=g().decisionModal?.key;return e?ye().find(t=>t.key===e):void 0}function rn(e){let t=so();if(!t||e<1||e>we)return!1;let s=g().drafts[t.key];if(s?.sending||s?.sent)return!1;let n=t.kind==="decision"?t.options:[];return e<=n.length?(A(t.key,{option:n[e-1],error:void 0}),document.querySelectorAll(".dmodal .dopt")[e-1]?.focus(),!0):e===n.length+1?(document.querySelector(".dmodal textarea.danswer")?.focus(),!0):!1}async function on(e){let t=g();if(t.viewedHere[e]||(k({viewedHere:{...t.viewedHere,[e]:!0}}),Qe().find(n=>n.key===e)?.viewedAt))return;let s=t.snapshot?.focusToken;if(s)try{await fetch("/requests",{method:"POST",headers:{"content-type":"application/json","x-lobstah-token":s},body:JSON.stringify({kind:"decision-viewed",payload:{key:e}})})}catch{}}function an(){k({alertDismissed:Xe().map(e=>e.key)})}function ln(e){let{snapshot:t}=g();k({stale:!1,...t?{snapshot:Vs(t,e)}:{}})}async function Ot(){let{modal:e}=g();if(e?.type!=="dispatch")return;let t=e.key,s=t.slice(t.indexOf(":")+1),n;try{let o=await fetch(`/data/dispatch/${encodeURIComponent(s)}`,{cache:"no-store"});n=o.ok?{data:await o.json()}:{error:o.status===404?"this dispatch is gone":`the detail answered ${o.status}`}}catch{n={error:"the detail could not be read"}}let r=g();r.modal?.type!=="dispatch"||r.modal.key!==t||n.error&&r.detail?.key===t&&r.detail.data||k({detail:{key:t,...n}})}var Et=!1;async function Se(){if(!Et){Et=!0;try{let e=await fetch("/data/stats",{cache:"no-store"});if(!e.ok)throw new Error(`/data/stats answered ${e.status}`);k({stats:await e.json(),statsError:null})}catch{k({statsError:"stats could not be read"})}finally{Et=!1}}}var Lt=e=>{g().statsFocus!==e&&k({statsFocus:e})};async function dn(e){let{olderLoading:t,older:s}=g();if(!t){k({olderLoading:e,olderError:null});try{let n=await fetch(`/data/older?kind=${e}&offset=${s[e].length}&limit=50`,{cache:"no-store"});if(!n.ok)throw new Error(`/data/older answered ${n.status}`);let r=await n.json();k({older:qs(g().older,r),olderLoading:null})}catch{k({olderLoading:null,olderError:{kind:e,text:"older records could not be read"}})}}}var cn=()=>({text:"",files:[]});function A(e,t){let s=g().drafts;k({drafts:{...s,[e]:{...s[e]??cn(),...t}}})}function pn(e,t){let s=g().drafts[e]?.option;A(e,{option:s===t?void 0:t,error:void 0})}var no=e=>new Promise((t,s)=>{let n=new FileReader;n.onload=()=>t(String(n.result).replace(/^data:[^,]*,/,"")),n.onerror=()=>s(n.error),n.readAsDataURL(e)});async function Nt(e,t){let s=g().snapshot?.answerLimits,n=[...t??[]],r=g().drafts[e]?.files??[],o=[],a=[];for(let l of n){let u=(/\\.[^.]+$/.exec(l.name)?.[0]??"").toLowerCase();if(s&&!s.extensions.includes(u))o.push(`${l.name}: type not accepted`);else if(s&&l.size>s.maxBytes)o.push(`${l.name}: larger than ${s.maxBytes} bytes`);else if(s&&r.length+a.length>=s.maxFiles)o.push(`${l.name}: at most ${s.maxFiles} files`);else try{a.push({name:l.name,bytes:l.size,data:await no(l)})}catch{o.push(`${l.name}: could not be read`)}}A(e,{files:[...g().drafts[e]?.files??[],...a],error:o.length?o.join("; "):void 0})}var ro={"image/png":".png","image/jpeg":".jpg","image/gif":".gif","image/webp":".webp"};async function un(e,t){let s=[...t.clipboardData?.items??[]],n=new Date().toISOString().replace(/[:.]/g,"-").replace(/-\\d{3}Z$/,"Z"),r=s.filter(o=>o.kind==="file"&&o.type.startsWith("image/")).map(o=>o.getAsFile()).filter(o=>o!==null).map((o,a,l)=>{let u=`pasted-${n}${l.length>1?`-${a+1}`:""}${ro[o.type]??".png"}`;return new File([o],u,{type:o.type})});r.length&&await Nt(e,r)}function fn(e,t){let s=(g().drafts[e]?.files??[]).filter((n,r)=>r!==t);A(e,{files:s})}async function Ze(e){let t=g().snapshot?.focusToken,s=g().drafts[e]??cn();if(!(s.sending||s.sent)){if(!s.option&&!s.text.trim()&&s.files.length===0){A(e,{error:"Choose an option, write an answer, or attach a file."});return}if(!t){A(e,{error:"This page has no answer token; reload it."});return}A(e,{sending:!0,error:void 0});try{let n=await fetch("/requests",{method:"POST",headers:{"content-type":"application/json","x-lobstah-token":t},body:JSON.stringify({kind:"decision-answer",payload:{key:e,...s.option?{option:s.option}:{},...s.text.trim()?{text:s.text}:{},files:s.files.map(a=>({name:a.name,data:a.data}))}})}),r=await n.json();if(!n.ok||!r.ok){A(e,{sending:!1,error:r.reason??`The answer was refused (${n.status}).`});return}A(e,{sending:!1,sent:Hs({option:s.option,text:s.text,files:s.files.length})}),g().decisionModal?.key===e&&k({decisionModal:nn(Qe(),g().drafts,e)??{key:null}})}catch{A(e,{sending:!1,error:"The answer could not be sent."})}}}var mn=()=>k({stale:!0}),hn=(e,t)=>k({lightbox:{src:e,name:t}}),le=()=>k({lightbox:null});function O(e,t){let s=g(),n={type:e,key:t};if(e==="dispatch"){k({modal:n,detail:s.detail?.key===t?s.detail:{key:t}}),Ot();return}let r=oe(s);k({modal:!r||re(r,n)?n:null,detail:null})}function de(){k({modal:null,detail:null})}async function gn(e){let t=g().snapshot?.focusToken;if(t)try{let n=await(await fetch(`/api/focus/${encodeURIComponent(e)}`,{method:"POST",headers:{"x-lobstah-focus-token":t}})).json();k({focusResults:{...g().focusResults,[e]:n.message??n.reason??"Window focus failed."}})}catch{k({focusResults:{...g().focusResults,[e]:"Window focus failed."}})}}async function $n(e,t){let s=g().snapshot?.focusToken;if(!s)return"The glass has no page token yet.";try{let r=await(await fetch("/requests",{method:"POST",headers:{"content-type":"application/json","x-lobstah-token":s},body:JSON.stringify({kind:"trap-request",payload:{repo:e,harness:t}})})).json();if(!r.ok)return r.reason??"The request was refused."}catch{return"The request could not be sent."}try{Ye(await(await fetch("/data")).json())}catch{}}function N(e){let t={...g().prefs,...e};Zs(t),k({prefs:t})}function vn(e){e!=="table"&&e!=="cards"||N({view:e})}var kn=e=>N({lobs:e==="on"});function bn(e,t){let s={...g().lobHidden,[e]:t};tn(s),setTimeout(()=>k({lobHidden:s}),0)}async function yn(e){try{return await navigator.clipboard.writeText(e),!0}catch{return!1}}var wn=function(e,t,s,n){var r;t[0]=0;for(var o=1;o<t.length;o++){var a=t[o++],l=t[o]?(t[0]|=a?1:2,s[t[o++]]):t[++o];a===3?n[0]=l:a===4?n[1]=Object.assign(n[1]||{},l):a===5?(n[1]=n[1]||{})[t[++o]]=l:a===6?n[1][t[++o]]+=l+"":a?(r=e.apply(l,wn(e,l,s,["",null])),n.push(r),l[0]?t[0]|=2:(t[o-2]=0,t[o]=r)):n.push(l)}return n},_n=new Map;function Sn(e){var t=_n.get(this);return t||(t=new Map,_n.set(this,t)),(t=wn(this,t.get(e)||(t.set(e,t=(function(s){for(var n,r,o=1,a="",l="",u=[0],c=function(p){o===1&&(p||(a=a.replace(/^\\s*\\n\\s*|\\s*\\n\\s*$/g,"")))?u.push(0,p,a):o===3&&(p||a)?(u.push(3,p,a),o=2):o===2&&a==="..."&&p?u.push(4,p,0):o===2&&a&&!p?u.push(5,0,!0,a):o>=5&&((a||!p&&o===5)&&(u.push(o,0,a,r),o=6),p&&(u.push(o,p,0,r),o=6)),a=""},d=0;d<s.length;d++){d&&(o===1&&c(),c(d));for(var h=0;h<s[d].length;h++)n=s[d][h],o===1?n==="<"?(c(),u=[u],o=3):a+=n:o===4?a==="--"&&n===">"?(o=1,a=""):a=n+a[0]:l?n===l?l="":a+=n:n===\'"\'||n==="\'"?l=n:n===">"?(c(),o=1):o&&(n==="="?(o=5,r=a,a=""):n==="/"&&(o<5||s[d][h+1]===">")?(c(),o===3&&(u=u[0]),o=u,(u=u[0]).push(2,0,o),o=0):n===" "||n===" "||n===`\n`||n==="\\r"?(c(),o=2):a+=n),o===3&&a==="!--"&&(o=4,u=u[0])}return c(),u})(e)),t),arguments,[])).length>1?t:t[0]}var Dn=e=>Array.isArray(e)?e.map(Dn):e===""?null:e,oo=(e,t,...s)=>Z(e,t,...s.map(Dn)),i=Sn.bind(oo);var De,w,Ht,xn,tt=0,In=[],D=y,Gn=D.__b,Cn=D.__r,Tn=D.diffed,An=D.__c,Rn=D.unmount,Mn=D.__;function Bt(e,t){D.__h&&D.__h(w,e,tt||t),tt=0;var s=w.__H||(w.__H={__:[],__h:[]});return e>=s.__.length&&s.__.push({}),s.__[e]}function z(e){return tt=1,ao(On,e)}function ao(e,t,s){var n=Bt(De++,2);if(n.t=e,!n.__c&&(n.__=[s?s(t):On(void 0,t),function(l){var u=n.__N?n.__N[0]:n.__[0],c=n.t(u,l);u!==c&&(n.__N=[c,n.__[1]],n.__c.setState({}))}],n.__c=w,!w.__f)){var r=function(l,u,c){if(!n.__c.__H)return!0;var d=!1,h=n.__c.props!==l;if(n.__c.__H.__.some(function(m){if(m.__N){d=!0;var f=m.__[0];m.__=m.__N,m.__N=void 0,f!==m.__[0]&&(h=!0)}}),o){var p=o.call(this,l,u,c);return d?p||h:p}return!d||h};w.__f=!0;var o=w.shouldComponentUpdate,a=w.componentWillUpdate;w.componentWillUpdate=function(l,u,c){if(this.__e){var d=o;o=void 0,r(l,u,c),o=d}a&&a.call(this,l,u,c)},w.shouldComponentUpdate=r}return n.__N||n.__}function Ut(e,t){var s=Bt(De++,3);!D.__s&&En(s.__H,t)&&(s.__=e,s.u=t,w.__H.__h.push(s))}function Ft(e){return tt=5,qt(function(){return{current:e}},[])}function qt(e,t){var s=Bt(De++,7);return En(s.__H,t)&&(s.__=e(),s.__H=t,s.__h=e),s.__}function io(){for(var e;e=In.shift();){var t=e.__H;if(e.__P&&t)try{t.__h.some(et),t.__h.some(jt),t.__h=[]}catch(s){t.__h=[],D.__e(s,e.__v)}}}D.__b=function(e){w=null,Gn&&Gn(e)},D.__=function(e,t){e&&t.__k&&t.__k.__m&&(e.__m=t.__k.__m),Mn&&Mn(e,t)},D.__r=function(e){Cn&&Cn(e),De=0;var t=(w=e.__c).__H;t&&(Ht===w?(t.__h=[],w.__h=[],t.__.some(function(s){s.__N&&(s.__=s.__N),s.u=s.__N=void 0})):(t.__h.some(et),t.__h.some(jt),t.__h=[],De=0)),Ht=w},D.diffed=function(e){Tn&&Tn(e);var t=e.__c;t&&t.__H&&(t.__H.__h.length&&(In.push(t)!==1&&xn===D.requestAnimationFrame||((xn=D.requestAnimationFrame)||lo)(io)),t.__H.__.some(function(s){s.u&&(s.__H=s.u,s.u=void 0)})),Ht=w=null},D.__c=function(e,t){t.some(function(s){try{s.__h.some(et),s.__h=s.__h.filter(function(n){return!n.__||jt(n)})}catch(n){t.some(function(r){r.__h&&(r.__h=[])}),t=[],D.__e(n,s.__v)}}),An&&An(e,t)},D.unmount=function(e){Rn&&Rn(e);var t,s=e.__c;s&&s.__H&&(s.__H.__.some(function(n){try{et(n)}catch(r){t=r}}),s.__H=void 0,t&&D.__e(t,s.__v))};var Pn=typeof requestAnimationFrame=="function";function lo(e){var t,s=function(){clearTimeout(n),Pn&&cancelAnimationFrame(t),setTimeout(e)},n=setTimeout(s,35);Pn&&(t=requestAnimationFrame(s))}function et(e){var t=w,s=e.__c;typeof s=="function"&&(e.__c=void 0,s()),w=t}function jt(e){var t=w;e.__c=e.__(),w=t}function En(e,t){return!e||e.length!==t.length||t.some(function(s,n){return s!==e[n]})}function On(e,t){return typeof t=="function"?t(e):t}var co=["none","minimal","low","medium","high","xhigh","max","ultra"],po=["default","acceptEdits","plan","dontAsk","bypassPermissions","auto"];function Ln(e){return typeof e=="string"&&/^[a-zA-Z0-9][a-zA-Z0-9._:/-]{0,127}$/.test(e)?e:null}function Nn(e,t){return typeof e=="string"&&t.includes(e)?e:null}function Wt(e={}){return{harness:Ln(e.harness),model:Ln(e.model),config:{effort:Nn(e.config?.effort,co),permissionMode:Nn(e.config?.permissionMode,po)},...typeof e.observedAt=="string"&&/^\\d{4}-\\d\\d-\\d\\dT\\d\\d:\\d\\d:\\d\\d\\.\\d{3}Z$/.test(e.observedAt)&&Number.isFinite(Date.parse(e.observedAt))?{observedAt:e.observedAt}:{}}}var J=e=>{if(!e)return"";let t=Math.max(0,(Date.now()-Date.parse(e))/1e3);return t<90?Math.round(t)+"s":t<5400?Math.round(t/60)+"m":t<172800?(t/3600).toFixed(1)+"h":Math.round(t/86400)+"d"},v=e=>i`<span data-age=${e??""}>${J(e)}</span>`;function H({worker:e,label:t,cls:s="badge"}){let n=Wt(e),r=[n.model??"model unknown",n.config.effort].filter(Boolean).join(" \\xB7 "),[o,a]=z(null),l=u=>{let c=u.currentTarget.getBoundingClientRect();a({left:Math.max(8,Math.min(c.left,window.innerWidth-280)),top:c.bottom+6})};return i`<span class=${s+" worker-harness"} tabindex="0" aria-label=${r}\n onMouseEnter=${l} onFocus=${l} onBlur=${()=>a(null)}\n onMouseLeave=${u=>{document.activeElement!==u.currentTarget&&a(null)}}\n onKeyDown=${u=>{u.key==="Escape"&&a(null)}}>\n ${t??n.harness??"unknown harness"}${o&&!g().modal&&i`<span class="worker-tooltip" role="tooltip" aria-hidden="true" style=${o}>${r}</span>`}\n </span>`}var Kt=e=>e.worker&&i`<${H} worker=${e.worker} />`;function nt({worker:e,headless:t=!1}){let s=Wt(e);return i`<section class="worker-details"><div class="sec">${t?"headless worker \\xB7 launch / observation":"worker observation"}</div><dl>\n <dt>harness</dt><dd>${s.harness??"unknown"}</dd>\n <dt>model</dt><dd>${s.model??"model unknown"}</dd>\n <dt>effort</dt><dd>${s.config.effort??"unknown"}</dd>\n <dt>permission mode</dt><dd>${s.config.permissionMode??"unknown"}</dd>\n <dt>observed</dt><dd>${s.observedAt??"unknown"}</dd>\n </dl></section>`}var rt=e=>e.activity&&i`<div class=${"activity"+(e.activity.stale?" stale":"")} title=${e.activity.stale?"no activity past the wedge threshold":e.activity.kind}>${e.activity.stale?"stale \\xB7 ":""}${e.activity.summary} \xB7 ${v(e.activity.at)} ago</div>`,uo=e=>e&&/^https?:\\/\\//i.test(e)?e:void 0,ot=e=>{let t=e.waiting;if(!t)return;let s=uo(t.link),n=s;try{s&&(n=new URL(s).host+new URL(s).pathname.replace(/\\/$/,""))}catch{n=s}return i`<div class="waiting">${e.verb}: waiting on ${t.on} \xB7 ${v(t.since)}${s&&[" \\xB7 ",i`<a href=${s} target="_blank" rel="noopener noreferrer" onClick=${C}>${n}</a>`]}</div>`},fo=12,Hn=e=>e!==void 0&&e.length>fo,j=e=>Hn(e)?e:void 0,B=e=>Hn(e)?" long":"",S=(e,t)=>()=>O(e,t),C=e=>e.stopPropagation(),mo=e=>e.sessionId?(e.harness==="codex"?"codex resume ":"claude --resume ")+e.sessionId:void 0;function Y(e){if(e.requested)return i`<span class="dim">Requested \u2014 no window yet</span>`;if(e.starting)return i`<span class="dim">${e.starting.failedAt?"Did not start":"Starting \\u2014 no window yet"}</span>`;if(!e.live){let n=mo(e);return n?i`<span class="dim">Resume: <code onClick=${C}>${n}</code></span>`:i`<span class="dim">Resume command unavailable</span>`}let t=g();return!t.snapshot?.focusSupported&&!e.link?i`<span class="dim">Window focus is not supported here</span>`:i`<span class="winaction"><button class="btn open" title="open this trap\'s window" onClick=${n=>{C(n),gn(e.trapId)}}>\u2197 open</button>${e.link&&[" ",i`<a href=${e.link} onClick=${C}>Session link</a>`]}${t.focusResults[e.trapId]&&[" ",i`<span class="dim">${t.focusResults[e.trapId]}</span>`]}</span>`}function M(e,t,s){return t.length?i`<div class="wrap"><table><tbody><tr>${e.map(n=>i`<th>${n}</th>`)}</tr>${t}</tbody></table></div>`:i`<div class="empty">${s}</div>`}function pe({kind:e,more:t}){return!t.left&&!t.error?null:i`<div class="older">${t.left>0&&i`<button class="btn" disabled=${t.loading} onClick=${()=>{dn(e)}}>${t.loading?"loading\\u2026":`show ${Math.min(t.left,50)} older (of ${t.left})`}</button>`}${t.error&&i` <span class="bad">${t.error}</span>`}</div>`}function ho({text:e}){let[t,s]=z(null),n=r=>async()=>{await yn(e)&&(s(r),setTimeout(()=>s(null),1e3))};return i`<div class="cmd"><code class=${t==="code"?"copied":void 0} title="click to copy" onClick=${n("code")}>${e}</code><button title="copy" onClick=${n("button")}>${t==="button"?"\\u2713":"\\u29C9"}</button></div>`}var T=(e,t)=>i`<${ho} key=${t} text=${e} />`,xe=(e,t,s="thumb")=>i`<button\n type="button"\n class=${s}\n title=${"view "+t}\n onClick=${n=>{C(n),hn(e,t)}}\n >\n <img src=${e} alt=${t} loading="lazy" />\n </button>`,ce=(e,t)=>e.map(s=>[i`<div class="sub">${s.name} \xB7 ${s.type} \xB7 ${s.bytes} bytes</div>`,t&&Ns(s.name)&&xe(t(s.name),s.name),T(s.path)]),jn=/\\bwt:([a-z0-9][a-z0-9-]*)/g,Vt=e=>g().snapshot?.trapNames?.[e],U=e=>e.replace(jn,(t,s,n)=>{let r=Vt(s);return r&&!Bn(e,n,t.length,r)?r:t}),Bn=(e,t,s,n)=>e.slice(Math.max(0,t-n.length-2),t)===`${n} (`&&e[t+s]===")";function at(e){let t="wt:"+e,s=Vt(e)??t;return g().snapshot?.traps.some(r=>r.trapId===e)?i`<a class="trapname" href="#traps" title=${t} onClick=${r=>{r.preventDefault(),C(r),O("trap",e)}}>${s}</a>`:i`<span class="trapname" title=${t}>${s}</span>`}var st=e=>{let t=/^wt:([a-z0-9][a-z0-9-]*)$/.exec(e);return t?at(t[1]):e};function R(e,t=1/0){let s=[],n=0;for(let l of e.matchAll(jn)){l.index>n&&s.push({text:e.slice(n,l.index)});let u=Vt(l[1]);s.push(u&&Bn(e,l.index,l[0].length,u)?{text:l[0]}:{text:u??l[0],trapId:l[1]}),n=l.index+l[0].length}n<e.length&&s.push({text:e.slice(n)});let r=s.reduce((l,u)=>l+u.text.length,0),o=r>t?t-1:1/0,a=[];for(let l of s){if(o<=0)break;l.text.length<=o?a.push(l.trapId?at(l.trapId):l.text):a.push(l.text.slice(0,o)),o-=l.text.length}return r>t&&a.push("\\u2026"),a}var go=e=>e.log.flatMap((t,s)=>[s?`\n`:"",t.at+" "+t.verb,...t.note?[" ",...R(t.note)]:[]]),$o=e=>"brief"in e;function Un(e,t){let s=[e.elapsed&&`elapsed: ${e.elapsed}`,e.attempt&&`attempt: ${e.attempt}`,e.branch&&`branch: ${e.branch}`,e.lastCommit&&`last commit: ${e.lastCommit}`,e.aheadTrunk&&`commits: ${e.aheadTrunk}`,e.draftPr&&`draft PR: ${e.draftPr}`,e.updated&&`updated: ${e.updated}`].filter(Boolean).join(`\n`),n=i`${e.waiting&&[i`<div class="sec">waiting</div>`,ot(e)]}${e.activity&&[i`<div class="sec">activity</div>`,rt(e)]}${s&&[i`<div class="sec">progress</div>`,i`<pre>${s}</pre>`]}`,r=e.evidence&&{...e.evidence};return r?.worker&&(delete r.worker,delete r.harness),$o(e)?i`${n}<div class="sec">brief</div><pre>${e.brief}</pre>${e.attachments.length>0&&[i`<div class="sec">attachments (${e.attachments.length})</div>`,ce(e.attachments,o=>Tt(e.lane,e.id,o))]}${e.messageAttachments.length>0&&[i`<div class="sec">message attachments (${e.messageAttachments.length})</div>`,ce(e.messageAttachments,o=>Tt(e.lane,e.id,o))]}${e.followUp&&[i`<div class="sec">forks</div>`,i`<pre>${e.followUp}</pre>`]}<div class="sec">log</div><div class="loglines">${e.log.length?go(e):"no entries yet"}</div>${e.inbox.length>0&&[i`<div class="sec">inbox</div>`,i`<div class="loglines">${e.inbox.join(`\n---\n`)}</div>`]}${e.awaitingReply&&[i`<div class="sec">awaiting reply</div>`,i`<div>${e.awaitingReply.line} \xB7 from ${e.awaitingReply.from} \xB7 ${v(e.awaitingReply.sentAt)} ago</div>`]}${r&&[i`<div class="sec">evidence</div>`,i`<div class="loglines">${JSON.stringify(r)}</div>`]}`:i`${n}<div class="sec">brief</div><div class=${t?.error?"bad":"dim"}>${t?.error??"loading\\u2026"}</div>${e.followUp&&[i`<div class="sec">forks</div>`,i`<pre>${e.followUp}</pre>`]}${e.note&&[i`<div class="sec">last note</div>`,i`<div class="loglines">${R(e.note)}</div>`]}`}function Ge(e){return e.for?[st(e.for),e.evidence&&e.evidence.deliveredTo?[" ",i`<span class="ok">\u2713delivered</span>`]:[" ",i`<span class="warn">waiting</span>`]]:e.claimedBy?st(e.claimedBy):""}function ue(e){if(e.prList&&e.prList.length>1)return[e.prList.map((r,o)=>[o?" \\u2192 ":"",i`<a href=${r.url} target="_blank" rel="noopener" onClick=${C}>#${r.number}</a>`,r.badge&&[" ",i`<span class=${"badge "+E(r.badge)}>${r.badge.text}</span>`]]),e.prGate&&[" ",i`<span class="badge dim" title="merge gate (pick)">${e.prGate}</span>`]];let t=e.evidence&&(e.evidence.prUrl||e.evidence.pr&&e.evidence.pr.url);if(!t)return"";let s=e.prBadge,n=e.evidence&&e.evidence.prUrls&&e.evidence.prUrls.length>1?e.evidence.prUrls:void 0;return[n?n.map((r,o)=>[o?" ":"",i`<a href=${r} target="_blank" rel="noopener" onClick=${C}>#${r.split("/").pop()}</a>`]):i`<a href=${t} target="_blank" rel="noopener" onClick=${C}>PR</a>`,s&&[" ",i`<span class=${"badge "+E(s)} title=${"observed "+s.observedAt}>${s.text}</span>`],e.prGate&&[" ",i`<span class="badge dim" title="merge gate (pick)">${e.prGate}</span>`]]}var vo={"pr:draft":"draft","pr:review":"review","pr:checks":"checks","pr:conflict":"conflicts","pr:ready":"ready","stack-ready":"stack ready",landed:"landed",watch:"watch"},zt={"pr:review":"bad","pr:checks":"bad","pr:conflict":"pr-conflicts","pr:ready":"ok","stack-ready":"ok","pr:draft":"dim",watch:"warn"},Fn=e=>vo[e]||"";var it=e=>i`<a href=${e.url} target="_blank" rel="noopener" onClick=${C}>#${e.number}</a>`,lt=e=>i`<span class="prname" title=${e.title||void 0}><b>#${e.number}</b>${e.title&&[" ",e.title]}</span>`;function fe(e,t,s){return e.flatMap((n,r)=>{let o=t.get(n)||void 0,a=n===s?i`<b title=${o}>#${n}</b>`:i`<span title=${o}>#${n}</span>`;return r?[" \\u2192 ",a]:[a]})}function Ce(e){let t=e.checks;return t.passed+"/"+t.total+" passed"+(t.failed?" \\xB7 "+t.failed+" failed":"")+(t.pending?" \\xB7 "+t.pending+" pending":"")}function dt(e){let t=e.review||{};return(e.reviewDecision||"")+(t.unresolvedThreads?" \\xB7 "+t.unresolvedThreads+" unresolved":"")+(t.changesRequested?" \\xB7 changes requested":"")}function Te(e){return[e.mergeStateStatus,e.nextMergeable?[" \\xB7 ",i`<span class="ok">next mergeable</span>`]:e.blockedBy?" \\xB7 blocked by #"+e.blockedBy:""]}function Jt(e){let t=Ys(e);return e?[t.text," \\xB7 ",t.at?[v(t.at)," ago"]:"never checked"]:i`<span class="dim">${t.text}</span>`}function Ae(e){if(e.requested)return{stale:!1,listen:[i`<span class="dot"></span>`,i`<span class="dim">requested</span>`],hb:i`<span class="dim">\u2014</span>`};if(e.starting)return{stale:!1,listen:e.starting.failedAt?[i`<span class="dot bad"></span>`,i`<span class="bad">start failed</span>`]:[i`<span class="dot warn"></span>`,"starting"],hb:i`<span class="dim">\u2014</span>`};if(!e.live)return{stale:!1,listen:[i`<span class="dot"></span>`,i`<span class="dim">signed off</span>`],hb:i`<span class="dim">\u2014</span>`};let t=Date.now()-Date.parse(e.heartbeatAt)>18e5,s=e.listening??(!!e.firstParkedAt&&!t);return{stale:t,listen:s?[i`<span class="dot ok"></span>`,"listening"]:[i`<span class="dot warn"></span>`,"not listening"],hb:i`<span class=${t?"bad":"ok"}>${t&&"stale "}${v(e.heartbeatAt)} ago</span>`}}function Yt(e){if(e.requested)return{kind:"requested",helmOn:!!g().snapshot?.helmOn};if(e.starting)return e.starting.failedAt?{kind:"start failed",reason:e.starting.reason??"no session signed on in time"}:{kind:"starting",deadline:e.starting.deadline};if(!e.live)return{kind:"signed off"};let t=e.claimed?e.catches.find(s=>s.id===e.claimed&&s.bucket==="active"):void 0;return t?t.waiting||t.verb==="paused"?{kind:"parked",current:t}:{kind:"working",current:t}:{kind:"idle",listening:e.listening??(!!e.firstParkedAt&&Date.now()-Date.parse(e.heartbeatAt??"")<=18e5)}}function ko(e){let t=Yt(e);return t.kind==="working"||t.kind==="idle"&&t.listening?"ok":t.kind==="start failed"?"bad":t.kind==="parked"||t.kind==="starting"?"warn":"dim"}var qn=e=>e.waiting?`waiting on ${e.waiting.on}`:e.note||"waiting",Wn=e=>e.title.slice(0,40)||"(no title)";function Re(e){let t=Yt(e),s=i`<span class=${"dot trapdot "+ko(e)}></span>`;if(t.kind==="requested")return[s,i`<span class="dim">${Kn(e,t.helmOn)}</span>`];if(t.kind==="starting")return[s,"starting \\xB7 waiting for its session to sign on"];if(t.kind==="start failed")return[s,i`<span class="bad">start failed</span>`," \\xB7 ",t.reason];if(t.kind==="signed off")return[s,i`<span class="dim">signed off</span>`];if(t.kind==="idle")return[s,"idle \\xB7 ",t.listening?"listening":"not listening"];let n=t.current;return t.kind==="parked"?[s,"parked \\xB7 ",qn(n)]:[s,"working \\xB7 ",i`<a href="#dispatches" onClick=${o=>{o.preventDefault(),C(o),O("dispatch",`${n.lane}:${n.id}`)}}>${n.id.slice(0,8)}</a>`," \\xB7 ",Wn(n),n.activity&&[" \\xB7 ",n.activity.summary," ",v(n.activity.at)," ago"]]}var Kn=(e,t)=>`requested \\xB7 ${e.repo??""} \\xB7 ${e.harness??""} \\xB7 ${t?"waiting for the helm":"waiting for a helm"}`;function Q(e){let t=e.starting?.commands;return t?.length?i`<div class="startcmds" onClick=${C}><div class="dim">start it: run one in a terminal</div>${t.map(s=>T(s.command,s.harness))}</div>`:null}function ct(){let t=g().snapshot?.repoKeys??[],[s,n]=z(!1),[r,o]=z(""),[a,l]=z("claude"),[u,c]=z(null),d=r||t[0]||"",h=async p=>{p.preventDefault();let m=await $n(d,a);c(m??null),m||n(!1)};return s?i`<form class="newtrap" onClick=${C} onSubmit=${h}>\n <label>repo <select name="repo" value=${d} onChange=${p=>o(p.target.value)}>${t.map(p=>i`<option value=${p}>${p}</option>`)}</select></label>\n <label>harness <select name="harness" value=${a} onChange=${p=>l(p.target.value)}><option value="claude">claude</option><option value="codex">codex</option></select></label>\n <button class="btn" type="submit" disabled=${!d}>Request</button>\n <button class="btn" type="button" onClick=${()=>n(!1)}>Cancel</button>\n ${u&&i`<span class="bad">${u}</span>`}\n </form>`:i`<span class="newtrap"><button class="btn" onClick=${p=>(C(p),n(!0),c(null))}>+ New trap</button></span>`}function pt(e){let t=Yt(e);if(t.kind==="requested")return Kn(e,t.helmOn);if(t.kind==="starting")return`starting \\xB7 sign-on due by ${t.deadline}`;if(t.kind==="start failed")return`start failed \\xB7 ${t.reason}`;if(t.kind==="signed off")return"signed off";if(t.kind==="idle")return`idle \\xB7 ${t.listening?"listening":"not listening"}`;if(t.kind==="parked")return`parked \\xB7 ${qn(t.current)}`;let s=t.current;return[`working \\xB7 ${s.id.slice(0,8)} \\xB7 ${Wn(s)}`,s.activity&&`${s.activity.summary} ${J(s.activity.at)} ago`].filter(Boolean).join(" \\xB7 ")}function Qt(e){let t=e.messages.filter(s=>s.state==="pending").length;return e.messages.length?["\\u2709 "+e.messages.length,t>0&&[" ",i`<span class="warn">(${t} pending)</span>`]]:null}var bo=e=>()=>{window.open(G(e.key),"_blank","noopener")};function Xt(e){let t=ve(e);return{nodes:[t&&t+" \\xB7 ",v(e.filedAt)," ago",e.acked?" \\xB7 acked":""],text:[t,`${J(e.filedAt)} ago`,e.acked?"acked":""].filter(Boolean).join(" \\xB7 ")}}function yo(e){let t=Xt(e);return i`<a key=${e.key} class=${"card"+(e.acked?" acked":"")} href=${G(e.key)} target="_blank" rel="noopener"><div class="top"><b title=${e.title}>${e.title}</b></div><div class="meta" title=${t.text}>${t.nodes}</div></a>`}function _o(e){return i`<tr key=${e.key} class=${"rowhead"+(e.acked?" acked":"")} onClick=${bo(e)}><td class="grow"><a href=${G(e.key)} target="_blank" rel="noopener" onClick=${t=>t.stopPropagation()}>${e.title}</a></td><td>${ve(e)}</td><td>${v(e.filedAt)}</td><td>${e.acked?"acked":""}</td></tr>`}function Vn({inp:e}){return e.view==="cards"?e.list.length?i`<div class="cards">${e.list.map(yo)}</div>`:i`<div class="empty">no reports match</div>`:M(["title","from","filed","acked"],e.list.map(_o),"no reports match")}function Yn(e){let t=e.trim();return/^(https?:\\/\\/|mailto:|#)/i.test(t)?t:void 0}function Qn(e){let t=e.trim();return/^[^/\\\\:?#]+$/.test(t)&&t!=="."&&t!==".."?t:void 0}function zn(e,t,s,n){let r=0;for(let o=t;o<e.length;o++){if(e[o]==="\\\\"){o++;continue}if(e[o]===s)r++;else if(e[o]===n){if(r===0)return o;r--}}return-1}function Jn(e,t){let s=zn(e,t+1,"[","]");if(s<0||e[s+1]!=="(")return;let n=zn(e,s+2,"(",")");if(n<0)return;let r=e.slice(s+2,n).trim().replace(/\\s+"[^"]*"$/,"").replace(/^<(.*)>$/,"$1");return{text:e.slice(t+1,s),target:r,end:n+1}}function q(e){let t=[],s="",n=()=>{s&&t.push({t:"text",v:s}),s=""};for(let r=0;r<e.length;){let o=e[r];if(o==="\\\\"&&r+1<e.length&&/[\\\\`*_{}[\\]()#+\\-.!|<>~]/.test(e[r+1])){s+=e[r+1],r+=2;continue}if(o==="`"){let a=/^`+/.exec(e.slice(r))[0],l=e.indexOf(a,r+a.length);if(l>0){n(),t.push({t:"code",v:e.slice(r+a.length,l).replace(/^ (.*) $/,"$1")}),r=l+a.length;continue}}if(o==="!"&&e[r+1]==="["){let a=Jn(e,r+1);if(a){n(),t.push({t:"img",alt:a.text,src:a.target}),r=a.end;continue}}if(o==="["){let a=Jn(e,r);if(a){n(),t.push({t:"a",href:a.target,c:q(a.text)}),r=a.end;continue}}if((o==="*"||o==="_")&&e[r+1]===o){let a=e.indexOf(o+o,r+2);if(a>r+2){n(),t.push({t:"b",c:q(e.slice(r+2,a))}),r=a+2;continue}}if(o==="*"||o==="_"&&(r===0||!/\\w/.test(e[r-1]))){let a=r+1;for(;(a=e.indexOf(o,a))>0&&(e[a+1]===o||o==="_"&&/\\w/.test(e[a+1]??""));)a+=2;if(a>r+1&&e[r+1]!==" "){n(),t.push({t:"i",c:q(e.slice(r+1,a))}),r=a+1;continue}}if(o===`\n`){/ {2,}$/.test(s)||s.endsWith("\\\\")?(s=s.replace(/( {2,}|\\\\)$/,""),n(),t.push({t:"br"})):s+=" ",r++;continue}s+=o,r++}return n(),t}var Xn=/^ {0,3}(`{3,}|~{3,})\\s*([^`\\s]*)/,Zn=/^ {0,3}(#{1,6})\\s+(.*?)\\s*#*\\s*$/,er=/^ {0,3}([-*_])(\\s*\\1){2,}\\s*$/,ts=/^( {0,3})([-*+])\\s+(.*)$/,ss=/^( {0,3})(\\d{1,9})[.)]\\s+(.*)$/,ut=/^ {0,3}>\\s?(.*)$/,wo=/^\\s*\\|?\\s*:?-+:?\\s*(\\|\\s*:?-+:?\\s*)*\\|?\\s*$/;function Zt(e){let t=e.trim();t.startsWith("|")&&(t=t.slice(1)),t.endsWith("|")&&!t.endsWith("\\\\|")&&(t=t.slice(0,-1));let s=[],n="";for(let r=0;r<t.length;r++)t[r]==="\\\\"&&t[r+1]==="|"?(n+="|",r++):t[r]==="|"?(s.push(n.trim()),n=""):n+=t[r];return s.push(n.trim()),s}var es=e=>Xn.test(e)||Zn.test(e)||er.test(e)||ts.test(e)||ss.test(e)||ut.test(e);function ft(e){let t=e.replace(/\\r\\n?/g,`\n`).replace(/\\t/g," ").split(`\n`),s=[],n=0;for(;n<t.length;){let r=t[n];if(!r.trim()){n++;continue}let o=Xn.exec(r);if(o){let d=o[1],h=[];for(n++;n<t.length&&!new RegExp(`^ {0,3}${d[0]}{${d.length},}\\\\s*$`).test(t[n]);)h.push(t[n++]);n++,s.push({t:"code",lang:o[2]??"",v:h.join(`\n`)});continue}let a=Zn.exec(r);if(a){s.push({t:"h",level:a[1].length,c:q(a[2])}),n++;continue}if(er.test(r)){s.push({t:"hr"}),n++;continue}if(ut.test(r)){let d=[];for(;n<t.length&&t[n].trim()&&(ut.test(t[n])||!es(t[n]));)d.push(ut.exec(t[n])?.[1]??t[n]),n++;s.push({t:"quote",c:ft(d.join(`\n`))});continue}let l=ts.exec(r),u=ss.exec(r);if(l||u){let d=!l,h=d?ss:ts,p=[];for(;n<t.length;){let m=t[n],f=h.exec(m);if(f&&f[1].length<=3){p.push([f[3]]),n++;continue}if(!m.trim()){if(n+1<t.length&&/^ {2,}\\S/.test(t[n+1])){p.at(-1).push(""),n++;continue}break}if(/^ {2,}\\S/.test(m)){p.at(-1).push(m.replace(/^ {2,4}/,"")),n++;continue}if(es(m))break;p.at(-1).push(m.trim()),n++}s.push({t:"list",ordered:d,start:d?Number(u[2]):1,items:p.map(m=>ft(m.join(`\n`)))});continue}if(r.includes("|")&&n+1<t.length&&wo.test(t[n+1])&&t[n+1].includes("-")){let d=Zt(r),h=Zt(t[n+1]).map(m=>m.startsWith(":")&&m.endsWith(":")?"center":m.endsWith(":")?"right":m.startsWith(":")?"left":"");n+=2;let p=[];for(;n<t.length&&t[n].trim()&&t[n].includes("|");){let m=Zt(t[n++]);p.push(d.map((f,$)=>q(m[$]??"")))}s.push({t:"table",align:d.map((m,f)=>h[f]??""),head:d.map(q),rows:p});continue}let c=[];for(;n<t.length&&t[n].trim()&&!es(t[n])&&!(c.length&&/^ {0,3}(=+|-+)\\s*$/.test(t[n]));)c.push(t[n++].replace(/^ +/,""));if(c.length===0){s.push({t:"p",c:q(r.trim())}),n++;continue}s.push({t:"p",c:q(c.join(`\n`))})}return s}function W(e,t){return t.map(s=>{switch(s.t){case"text":return s.v;case"code":return i`<code>${s.v}</code>`;case"b":return i`<strong>${W(e,s.c)}</strong>`;case"i":return i`<em>${W(e,s.c)}</em>`;case"br":return i`<br />`;case"a":{let n=Yn(s.href);return n?i`<a href=${n} target="_blank" rel="noopener noreferrer">${W(e,s.c)}</a>`:W(e,s.c)}case"img":{let n=Qn(s.src);return n?xe(e(n),s.alt||n,"mdimg"):i`<span class="dim">[image not shown: ${s.alt||s.src}]</span>`}}})}function mt(e,t){return t.map(s=>{switch(s.t){case"h":{let n=W(e,s.c);return s.level===1?i`<h1>${n}</h1>`:s.level===2?i`<h2>${n}</h2>`:s.level===3?i`<h3>${n}</h3>`:i`<h4>${n}</h4>`}case"p":return i`<p>${W(e,s.c)}</p>`;case"code":return i`<pre class="mdcode"><code>${s.v}</code></pre>`;case"hr":return i`<hr />`;case"quote":return i`<blockquote>${mt(e,s.c)}</blockquote>`;case"list":{let n=s.items.map(r=>i`<li>${mt(e,r)}</li>`);return s.ordered?i`<ol start=${s.start}>${n}</ol>`:i`<ul>${n}</ul>`}case"table":return i`<div class="mdtable"><table><thead><tr>${s.head.map((n,r)=>i`<th style=${s.align[r]?"text-align:"+s.align[r]:void 0}>${W(e,n)}</th>`)}</tr></thead><tbody>${s.rows.map(n=>i`<tr>${n.map((r,o)=>i`<td style=${s.align[o]?"text-align:"+s.align[o]:void 0}>${W(e,r)}</td>`)}</tr>`)}</tbody></table></div>`}})}var Me=new Map;function tr(e){let t=Me.get(e);return t||(t=ft(e),Me.set(e,t),Me.size>8&&Me.delete(Me.keys().next().value)),t}function So({r:e,text:t}){let s=t?.text!==void 0?mt(n=>Rt(e.key,n),tr(t.text)):t?.error?i`<div class="bad">${t.error}</div>`:i`<div class="dim">loading\u2026</div>`;return i`<div class="mdpage" data-report=${e.key}>${s}</div>`}function sr({text:e,fileUrl:t}){return i`<div class="mdpage">${mt(t,tr(e))}</div>`}function ns(e){let t=ve(e);return[t&&t+" \\xB7 ",v(e.filedAt)," ago",e.acked&&[" \\xB7 ",i`<span class="ok">acked</span>`]]}function nr({r:e,text:t,heading:s}){return[s&&i`<div class="sec">report \xB7 ${e.title}</div>`,i`<div class="sub">${ns(e)}</div>`,i`<${So} r=${e} text=${t} />`,e.attachments.length>0&&[i`<div class="sec">report attachments (${e.attachments.length})</div>`,ce(e.attachments,n=>Rt(e.key,n))],!e.acked&&[i`<div class="sec">ack</div>`,T("lobstah attention ack "+e.key)]]}var Do=/\\.(png|jpe?g|gif|webp)$/i,xo=e=>{e.style.height="auto",e.style.height=e.scrollHeight+"px"};function Go(e,t){return t.length?i`<div class="dfiles">${t.map(s=>Do.test(s.name)?i`<span key=${s.name}>${xe(At(e,s.name),s.name,"dimg")}</span>`:i`<div key=${s.name} class="dfile"><span>${s.name}</span> <span class="dim">\xB7 ${s.type} \xB7 ${s.bytes} bytes \xB7 </span><code>${s.path}</code></div>`)}</div>`:null}function Co(e,t,s){let n=e.key,r=e.kind==="decision"?e.options:[],o=t?.text??"",a=t?.files??[],l=!!t?.sending,u=d=>d+1<=we?String(d+1):void 0,c=u(r.length);return[r.length>0&&i`<div class="doptions">${r.map((d,h)=>{let p=u(h);return i`<button key=${d} type="button" class=${"btn dopt"+(t?.option===d?" on":"")} aria-pressed=${t?.option===d?"true":"false"} aria-keyshortcuts=${p} disabled=${l} onClick=${()=>pn(n,d)}>${p&&i`<kbd class="dkey" aria-hidden="true">${p}</kbd>`}<span class="dlabel">${d}</span></button>`})}</div>`,i`<div class="danswer-wrap">${c&&i`<kbd class="dkey dkey-text" aria-hidden="true">${c}</kbd>`}<textarea\n class="danswer"\n rows="2"\n aria-keyshortcuts=${c}\n aria-label=${r.length?"Your answer, in your own words":"Your answer"}\n placeholder=${r.length?"Add to your answer, or answer in your own words":"Your answer"}\n value=${o}\n disabled=${l}\n onInput=${d=>{let h=d.currentTarget;xo(h),A(n,{text:h.value,error:void 0})}}\n onPaste=${d=>{un(n,d)}}\n ></textarea></div>`,i`<div class="dfoot">\n <label class="btn dattach" title="attach images or files">attach<input type="file" multiple accept=${s} disabled=${l} onChange=${d=>{let h=d.currentTarget;Nt(n,h.files).then(()=>{h.value=""})}} /></label>\n ${a.map((d,h)=>i`<span key=${d.name+h} class="dchip">${d.name}<button type="button" class="dx" title="remove" disabled=${l} onClick=${()=>fn(n,h)}>\xD7</button></span>`)}\n ${t?.error&&i`<span class="bad derr">${t.error}</span>`}\n <button type="button" class="btn dsend" disabled=${l} onClick=${()=>{Ze(n)}}>${l?"Sending\\u2026":"Send"}</button>\n </div>`]}var rr=e=>e.kind==="decision"?e.title:R(e.note);function To(e,t,s){return t?.sent?"answered":e.viewedAt||s?"read":"unread"}function rs(e){let t=e.dispatch,s=e.lane??"work";return[t&&i`<a class="dlink" href="#" onClick=${r=>{r.preventDefault(),r.stopPropagation(),S("dispatch",s+":"+t)()}}>${t.slice(0,8)}</a>`,e.repo].filter(Boolean).flatMap((r,o)=>o?[" \\xB7 ",r]:[r])}function Ao(e,t,s,n){let r=To(e,t,s),o=()=>ae(e.key);return i`<li key=${e.key} class=${"drow "+r+(n?" focus":"")} data-decision=${e.key}>\n <button type="button" class="dopen" onClick=${o} aria-label=${`${r}: ${e.kind==="decision"?e.title:e.note}`}>\n ${e.kind==="question"&&i`<span class=${"badge "+(e.verb==="blocked"?"bad":"warn")}>${e.verb}</span>`}\n <b class="dtitle">${rr(e)}</b>\n </button>\n <span class="dmeta dim">${rs(e)}</span>\n <span class="dage dim">${v(e.at)} ago</span>\n <span class=${"dstate "+r}>${r==="answered"?`answered \\xB7 ${t.sent}`:r}</span>\n </li>`}function or({cards:e,drafts:t,focus:s,viewedHere:n,unread:r}){let o=e.length?i`<ul class="dlist">${e.map(a=>Ao(a,t[a.key],!!n[a.key],a.key===s))}</ul>`:i`<div class="empty">none</div>`;return i`<section class="decisions"><h2>decisions${r>0&&i`<span class="dbadge" aria-label=${`${r} new`}>${r}</span>`}</h2>${o}</section>`}function Ro(e){let t=Math.min(e,we),s=e+1<=we?String(e+1):void 0,n=[t>0&&[i`<kbd>1</kbd>`,t>1&&["\\u2013",i`<kbd>${t}</kbd>`]," choose"],s&&[i`<kbd>${s}</kbd>`," write"],t>0&&[i`<kbd>Enter</kbd>`," on a chosen option sends"],[i`<kbd>\u2190</kbd>`," ",i`<kbd>\u2192</kbd>`," previous / next"],[i`<kbd>Esc</kbd>`," leaves the text box, then closes"],[i`<kbd>d</kbd>`," opens decisions anywhere"]].filter(Boolean);return i`<div class="dlegend dim">${n.flatMap((r,o)=>o?[" \\xB7 ",r]:[r])}</div>`}var Mo=\'a[href], button:not([disabled]), textarea:not([disabled]), input:not([disabled]), select:not([disabled]), [tabindex]:not([tabindex="-1"])\';function ar({open:e,order:t,drafts:s,extensions:n}){let r=Ft(null),o=Ft(null),a=!!e;Ut(()=>{if(a)return o.current=document.activeElement,r.current?.focus(),()=>{let m=o.current;m&&typeof m.focus=="function"&&document.contains(m)&&m.focus()}},[a]);let l=e?.key??null;if(Ut(()=>{l&&on(l)},[l]),!e)return null;let u=m=>{if(m.key!=="Tab"||!r.current)return;let f=[...r.current.querySelectorAll(Mo)];if(!f.length)return void m.preventDefault();let $=f[0],_=f[f.length-1];m.shiftKey&&(document.activeElement===$||document.activeElement===r.current)?(m.preventDefault(),_.focus()):!m.shiftKey&&document.activeElement===_&&(m.preventDefault(),$.focus())},c=l?t.find(m=>m.key===l):void 0,d=c?t.indexOf(c):-1,h=(m,f,$="")=>i`<div id="doverlay" class="open" onClick=${_=>_.target===_.currentTarget&&ie()}>\n <div class="modal dmodal" role="dialog" aria-modal="true" aria-labelledby="dmodal-title" tabindex="-1" ref=${r} onKeyDown=${u} data-decision=${$}>\n <button type="button" class="x" aria-label="close" aria-keyshortcuts="Escape" onClick=${ie}>\xD7</button>\n <h3 id="dmodal-title" class="sr-only">${m}</h3>\n ${f}\n </div>\n </div>`;if(!c)return h("All decisions answered",i`<div class="ddone" onClick=${ie}><div class="ok">\u2713 All decisions answered.</div><div class="dim">Click anywhere or press Escape to close.</div></div>`);let p=s[c.key];return h(c.kind==="decision"?c.title:c.note,i`<div class="dnav">\n <button type="button" class="btn dprev" aria-label="previous decision" aria-keyshortcuts="ArrowLeft" disabled=${d<=0} onClick=${()=>_e(-1)}>\u2039 prev</button>\n <span class="dpos" aria-live="polite">${d+1} of ${t.length}</span>\n <button type="button" class="btn dnext" aria-label="next decision" aria-keyshortcuts="ArrowRight" disabled=${d>=t.length-1} onClick=${()=>_e(1)}>next \u203A</button>\n </div>\n <div class="dtop">\n ${c.kind==="question"&&i`<span class=${"badge "+(c.verb==="blocked"?"bad":"warn")}>${c.verb}</span>`}\n <b class="dtitle">${rr(c)}</b>\n <span class="dmeta dim">${rs(c)}${rs(c).length?" \\xB7 ":""}${v(c.at)} ago</span>\n </div>\n ${c.kind==="decision"&&c.detail.trim()&&i`<${sr} text=${c.detail} fileUrl=${m=>At(c.key,m)} />`}\n ${c.kind==="decision"&&Go(c.key,c.attachments)}\n ${p?.sent?i`<div class="danswered ok">answered \xB7 ${p.sent}</div>`:Co(c,p,n.join(","))}\n ${Ro(c.kind==="decision"?c.options.length:0)}`,c.key)}function ir({unread:e,dismissed:t}){let s=e.some(a=>!t.includes(a.key)),n=e.length>0&&s,r=e.length,o=`${r} new decision${r===1?"":"s"}`;return i`<div class="dalert-live" role="status" aria-live="polite">${n&&i`<div class="dalert">\n <button type="button" class="dalert-open" aria-keyshortcuts="d" onClick=${()=>ae()}><span class="dcount" aria-hidden="true">${r}</span> <span>${o}</span></button>\n <button type="button" class="dx" aria-label="dismiss" title="dismiss" onClick=${an}>\xD7</button>\n </div>`}</div>`}function Oo(e,t){let s=e.badge&&(e.badge.worker?i`<${H} worker=${e.badge.worker} label=${e.badge.text} cls=${"badge "+(e.badge.tone||"dim")} />`:i`<span class=${"badge "+(e.badge.tone||"dim")+B(e.badge.text)} title=${j(e.badge.text)}>${e.badge.text}</span>`),n=e.worker&&i`<${H} worker=${e.worker} />`;if(e.href){let r=(t==="cards"?"card":"deckline click")+(e.acked?" acked":"");return t==="cards"?i`<a key=${e.key} class=${r} href=${e.href} target="_blank" rel="noopener"><div class="top"><b>${e.title}</b>${s}</div>${e.meta&&i`<div class="meta" title=${e.metaText}>${e.meta}</div>`}</a>`:i`<a key=${e.key} class=${r} href=${e.href} target="_blank" rel="noopener">${s&&[s," "]}<b>${e.title}</b>${e.meta&&[" ",i`<span class="dim">\xB7 ${e.meta}</span>`]}</a>`}return t==="cards"?i`<div key=${e.key} class=${"card"+(e.acked?" acked":"")} onClick=${e.open} style=${e.open?void 0:"cursor:default"}><div class="top"><b>${e.title}</b>${n}${s}</div>${e.meta&&i`<div class="meta" title=${e.metaText}>${e.meta}</div>`}${e.extra}${e.action&&i`<div class="foot"><span class="footact">${e.action}</span></div>`}</div>`:i`<div key=${e.key} class=${"deckline"+(e.open?" click":"")+(e.acked?" acked":"")} onClick=${e.open}>${s&&[s," "]}<b>${e.title}</b>${n&&[" ",n]}${e.meta&&[" ",i`<span class="dim">\xB7 ${e.meta}</span>`]}${e.action&&[" ",i`<span class="dim">\xB7 </span>`,e.action]}${e.extra}</div>`}var lr=(e,t)=>e>0&&i`<a class="deckmore" href=${"#"+t}>+${e} more \u2192</a>`;function ht(e,t,s,n,r,o){let a=t.slice(0,n),l=a.map(c=>Oo(c,r)),u=a.length?r==="cards"?i`<div class="cards">${l}</div>`:l:i`<div class="empty">none</div>`;return i`<section><h2><a href=${"#"+s}>${e} \u2192</a></h2>${o&&i`<div class="tabhead">${o}</div>`}${u}${lr(t.length-a.length,s)}</section>`}function Lo(e,t,s,n){let r=t.find(c=>c.number===e.nextNumber)||t[0],o=fe(e.numbers,new Map(t.map(c=>[c.number,c.title]))),a=r?"next #"+r.number:"nothing mergeable";if(n==="cards"){let c=d=>{let h=s.get(d.key)||[],p=h.length>0&&h.every($=>$.acked),m=h.length?h.map($=>i`<span class=${"badge "+(zt[$.kind]||"dim")}>${Fn($.kind)}</span>`):i`<span class=${"badge "+E(d.badge)+B(d.badge.text)} title=${j(d.badge.text)}>${d.badge.text}</span>`,f=`${d.repo} \\xB7 ${d.badge.text}${p?" \\xB7 acked":""}`;return i`<div key=${d.key} class=${"card"+(p?" acked":"")} onClick=${S("pr",d.key)}><div class="top">${lt(d)}${m}</div><div class="meta" title=${f}>${f}</div></div>`};return i`<div key=${e.id} class="deckstack"><div class="dim">${o} \xB7 ${a}</div>${e.readiness&&i`<div class=${e.readiness.allReady?"ok":"dim"}>${e.readiness.text}</div>`}<div class="cards">${t.map(c)}</div></div>`}let l=t.flatMap(c=>(s.get(c.key)||[]).map(d=>i`<span class=${"badge "+(zt[d.kind]||E(c.badge))+(d.acked?" acked":"")}>#${c.number} ${c.badge.text}</span>`)),u=l.flatMap((c,d)=>d?[" ",c]:[c]);return i`<div key=${e.id} class=${"deckline"+(r?" click":"")} onClick=${r&&S("pr",r.key)}><b>${o}</b> <span class="dim">\xB7 ${a}</span>${l.length>0&&[" \\xB7 ",u]}${e.readiness&&i`<div class=${e.readiness.allReady?"ok":"dim"}>${e.readiness.text}</div>`}</div>`}function No(e,t){let s=e.stacks.slice(0,3),n=new Map;for(let o of e.prAttention){let a=n.get(o.key)||[];a.push(o),n.set(o.key,a)}let r=s.length?s.map(o=>Lo(o,e.prs.filter(a=>a.stackId===o.id).sort((a,l)=>a.position-l.position),n,t)):i`<div class="empty">none</div>`;return i`<section><h2><a href="#prs">PRs \u2192</a></h2>${r}${lr(e.stacks.length-s.length,"prs")}</section>`}function dr({inp:e,drafts:t,focus:s,viewedHere:n,unread:r}){let o=e.view,a=e.inflight.map(d=>({key:d.lane+":"+d.id,title:[d.id.slice(0,8)," ",d.repo||""],worker:d.worker,badge:{text:d.verb,tone:d.verb==="needs-decision"||d.verb==="blocked"?"bad":"dim"},meta:[R(d.note||"",90),d.verbAt&&[" \\xB7 ",v(d.verbAt)," ago"],(d.for||d.claimedBy)&&[" \\xB7 ",st(d.for||d.claimedBy)]],metaText:[U(d.note||""),d.verbAt&&`${J(d.verbAt)} ago`,U(d.for||d.claimedBy||"")].filter(Boolean).join(" \\xB7 "),open:S("dispatch",d.lane+":"+d.id)})),l=e.landed.map(d=>({key:d.key,title:[d.id.slice(0,8)," ",d.repo||"",d.unreported&&[" ",i`<span class="badge warn unreported">unreported</span>`]],badge:{text:d.verb,tone:d.verb==="failed"?"bad":"ok"},meta:[R(d.note||"",90)," \\xB7 ",v(d.at)," ago"],metaText:`${U(d.note||"")} \\xB7 ${J(d.at)} ago`,open:S("dispatch",d.lane+":"+d.id)})),u=e.reports.map(d=>{let h=Xt(d);return{key:d.key,title:d.title,meta:h.nodes,metaText:h.text,href:G(d.key),acked:!!d.acked}}),c=e.traps.map(({x:d})=>({key:d.trapId,title:"\\u{1FAA4} "+(d.label??`wt:${d.trapId}`),badge:d.requested?{text:"requested",tone:"dim"}:d.starting?{text:d.starting.failedAt?"start failed":"starting",tone:d.starting.failedAt?"bad":"warn"}:{text:d.live?d.harness||"live":"signed off",tone:d.live?"ok":"dim",worker:d.live?d:void 0},meta:[d.repo||""," \\xB7 ",i`<span class="catchn" title="catches">\u{1F99E} ${te(d.totalCatches??0)}</span>`," \\xB7 ",Re(d)],metaText:`${d.repo||""} \\xB7 \\u{1F99E} ${te(d.totalCatches??0)} \\xB7 ${pt(d)}`,action:Y(d),extra:Q(d),open:S("trap",d.trapId)}));return i`<div class="deckgrid"><${or} cards=${e.decisions} drafts=${t} focus=${s} viewedHere=${n} unread=${r} />${ht("in flight",a,"dispatches",4,o)}${ht("Landed \\xB7 24h",l,"dispatches",8,o)}${ht("reports",u,"reports",8,o)}${ht("traps",c,"traps",8,o,i`<${ct} />`)}${No(e,o)}</div>`}function Ho(e,t){if(!t)return e;let s=new Map(e.map(o=>[o.id,o])),n=o=>{let a=o,l=new Set;for(;a.followUp&&s.has(a.followUp)&&!l.has(a.id);)l.add(a.id),a=s.get(a.followUp);return a.id},r=new Map;for(let o of e){let a=n(o),l=r.get(a)||[];l.push(o),r.set(a,l)}return[...r.values()].sort((o,a)=>a[0].sort-o[0].sort).flatMap(o=>o.sort((a,l)=>a.id===n(a)?-1:l.id===n(l)?1:a.sort-l.sort))}function jo(e,t){let s=e.lane+":"+e.id;return i`<tr key=${s} class="rowhead" onClick=${S("dispatch",s)}><td>${t&&e.followUp&&i`<span class="dim">\u21B3 </span>`}${e.id.slice(0,8)}</td><td>${e.lane} / ${e.bucket}</td><td>${e.repo}</td><td class=${"v-"+e.verb}>${e.verb}</td><td class="grow">${R(e.note??"",90)}${ot(e)}${Kt(e)}${rt(e)}</td><td>${v(e.verbAt)}</td><td>${Ge(e)}</td><td>${ue(e)}</td></tr>`}function Bo(e){let t=e.lane+":"+e.id;return i`<div key=${t} class="card" onClick=${S("dispatch",t)}><div class="top"><b>${e.id.slice(0,8)}</b>${Kt(e)}<span class=${"badge v-"+e.verb+B(e.verb)} title=${j(e.verb)}>${e.verb}</span></div><div class="meta" title=${`${e.repo} \\xB7 ${e.lane} ${e.bucket}`}>${e.repo} \xB7 ${e.lane} ${e.bucket}${e.verbAt&&[" \\xB7 ",v(e.verbAt)]}</div>${e.note&&i`<div class="note" title=${U(e.note)}>${R(e.note)}</div>`}${ot(e)}${rt(e)}<div class="foot">${Ge(e)} ${ue(e)}</div></div>`}function cr({inp:e,more:t}){let s=e.list,n=t&&i`<${pe} kind="dispatches" more=${t} />`;return e.view==="cards"?[s.length?i`<div class="cards">${s.map(Bo)}</div>`:i`<div class="empty">no dispatches match</div>`,n]:[M(["id","lane / bucket","repo","verb","note","age","addressed","pr"],Ho(s,e.chain).map(r=>jo(r,e.chain)),"no dispatches match"),n]}var Uo={deck:"On deck",dispatches:"Dispatches",traps:"Traps",prs:"PRs",reports:"Reports",notices:"Notices",stats:"Stats"},Fo=["working","needs-decision","blocked","paused","done","failed","unknown"],pr=(e,t)=>i`<span role="img" aria-label=${t} title=${t}>${e}</span>`;function qo(e,t){let s=t.daemonStale,n=e.daemon?[i`<span class=${s?"bad":"ok"}>${s&&"stale "}${v(e.daemon.heartbeat)} ago</span>`," ",i`<span class="dim">v${e.daemon.version}</span>`]:i`<span class="bad">down</span>`;return[i`<span class="chip">daemon ${n}</span>`,t.stats&&i`<span class="chip" title="catches today">\u{1F99E} ${t.stats.catchesToday} today</span>`,e.slots&&i`<span class="chip">active ${pr("\\u{1F3A3}","headless")} ${e.slots.headless} of ${e.slots.limit}; ${pr("\\u{1FAA4}","traps")} ${e.slots.traps}${e.slots.parked?`; parked: ${e.slots.parked} (no slot)`:""}</span>`,t.helms.map(({x:r,stale:o})=>i`<span key=${r.grounds} class="chip click" onClick=${S("helm",r.grounds)}>\u26F5 <${H} worker=${r} label=${i`<b>${r.man}</b>`} cls="" /> <span class="dim">helm ${r.grounds}</span> <span class=${o?"warn":"ok"}>${o&&"stale "}${v(r.heartbeatAt)} ago</span></span>`)]}var os=[];function Wo(e){let t=[...new Set([...e.dispatches.map(s=>s.repo),...e.notices.map(s=>s.repo),...e.prs.map(s=>s.repo)].filter(Boolean))].sort();return t.length!==os.length&&(os=t),os}var as=[];function Ko(e){let t=[...new Set(e.notices.map(s=>s.kind))].sort();return t.length!==as.length&&(as=t),as}var Pe=e=>e.currentTarget.value;function Vo(e,t,s){let n=a=>t===a?"":"display:none",r=e?Wo(e):[],o=e?Ko(e):[];return i`<div class="controls" style=${t==="stats"?"display:none":void 0}>\n <select id="f-lane" style=${e&&n("dispatches")} value=${s.lane} onChange=${a=>N({lane:Pe(a)})}>\n <option value="">all lanes</option>\n <option value="work">work</option>\n <option value="chore">chore</option>\n </select>\n <select id="f-repo" value=${s.repo} onChange=${a=>N({repo:Pe(a)})}>\n <option value="">all repos</option>\n ${r.map(a=>i`<option key=${a}>${a}</option>`)}\n </select>\n <select id="f-verb" style=${e&&n("dispatches")} value=${s.verb} onChange=${a=>N({verb:Pe(a)})}>\n <option value="">all verbs</option>\n ${Fo.map(a=>i`<option key=${a}>${a}</option>`)}\n </select>\n <label id="chain-control" style=${e&&n("dispatches")}><input id="f-chain" type="checkbox" checked=${!!s.chain} onChange=${a=>N({chain:a.currentTarget.checked})} /> group by chain</label>\n <select id="f-kind" style=${e&&n("notices")} value=${s.noticeKind} onChange=${a=>N({noticeKind:Pe(a)})}>\n <option value="">all notice kinds</option>\n ${o.map(a=>i`<option key=${a}>${a}</option>`)}\n </select>\n <input id="f-q" type="search" placeholder="search id \xB7 note \xB7 brief" value=${s.q} onInput=${a=>N({q:Pe(a)})} />\n </div>`}function ur({d:e,inp:t,tab:s,st:n,stale:r,unread:o=0}){return[i`<div class="headerline"><h1>\u{1F99E}\u2728 spyglass<span id="stale" style=${e||r?r?"display:inline":"display:none":void 0}> \xB7 STALE FEED</span></h1><span id="settings-slot"><button id="gearbtn" title="settings" aria-label="settings" onClick=${()=>O("settings","browser")}>\u2699</button></span></div>`,i`<div class="chips"><span id="chips" style="display:contents">${e&&t&&qo(e,t.chips)}</span><span class="chip dim" id="clock">${e&&new Date(e.now).toLocaleTimeString("en-GB")}</span></div>`,i`<nav class="tabs" id="tabs" aria-label="Spyglass views">${se.map(a=>i`<a href=${"#"+a} data-tab=${a} class=${e?a===s?"on":"":void 0}>${Uo[a]}${a==="deck"&&o>0&&i`<span class="dbadge" aria-label=${`${o} new decision${o===1?"":"s"}`}>${o}</span>`}</a>`)}</nav>`,Vo(e,s,n)]}var fr=({d:e})=>e&&["\\u{1F99E}\\u2728 lobstah v"+e.version+" \\xB7 ",i`<a href=${e.repoUrl} target="_blank">${e.repoUrl.replace("https://github.com/","")}</a>`];function mr(e,t){if(!t.lobs)return[];let s={"pr:draft":"draft","pr:review":"review","pr:checks":"checks","pr:conflict":"conflicts","pr:ready":"ready","stack-ready":"stack ready",landed:"landed",watch:"watch",report:"report"},n=t.hidden??{},r=e.filter(a=>!a.quiet&&!a.acked&&!(a.key!==void 0&&n[a.key]===a.stateHash)).map(a=>{let l=s[a.kind??""]??"",u=a.key!==void 0?{hideKey:a.key,hideHash:a.stateHash??""}:{};return a.kind==="decision"&&a.key?{key:"decision:"+a.key,text:a.note||a.verb,label:l,hash:Ct(a.key),...u}:(a.kind??"question")==="question"&&a.key?{key:"question:"+a.key,text:a.note||a.verb,label:l,hash:Ct(a.key),...u}:a.kind==="report"&&a.key?{key:"report:"+a.key,text:a.note||a.verb,label:l,href:G(a.key),...u}:typeof a.kind=="string"&&(a.kind.startsWith("pr:")||a.kind==="stack-ready")&&a.prUrl?{key:a.kind+":"+(a.key??a.prUrl),text:a.note||a.kind,href:a.prUrl,label:l,...u}:{key:(a.kind??"question")+":"+(a.key??a.lane+":"+a.id),text:a.note||a.verb,label:l,...a.kind==="watch"?{}:{open:{type:"dispatch",key:a.lane+":"+a.id}},...u}});!r.length&&t.preview&&(r=[{key:"preview",text:"attention questions crawl in here",...t.previewHelm?{open:{type:"helm",key:t.previewHelm}}:{}}]);let o=r.length>4?r.length-4:0;return r=r.slice(0,4),o&&(r[3]={...r[3],text:"\\u2026and "+o+" more \\u2014 see attention",label:""}),r}var hr="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAASAAAAA4CAYAAABdcSXCAAAVF0lEQVR4nO2d34sc15XHb/fUaGRpsIwky0qbRCiyIZh1NjEKxDixTUge8up9ccKSZSHZf8BP0R/gffI/kCwsLEvWL/FrHmKM88PEYOHsJiYIFGWwQjq2rBk8xpI1mp7p5XOrvj2n71R1V1VXz3RP3yOaaXV1Vd/7rXO/95xzzz3lXJS5kVe++3w/732UKGUk6k8EqDFFeu07l/uRhOIAqyNRfyJAE0uczeIAm0Si/kSAGlWmaA3FAVZXov6UAOjlr/1Df5GUoKpSRCUajU3Un9nUn/Y8DLCXfvFma+Xs2YUMvFbByB1hifpTT6L+jCGgqrO8W0Api9NRtoKi/tSXqD8RoANVIrdgEgfY/OrPgblg49yHrdu3x3b+qLsZZfo+DiNwXDSJ+jO/+jMTBIRceee9UgAtspQh4EWNlUX9mU/9mRkCqmLhLOIAq9L/i71PF3IFKerP7OnPXBGQZNEJZtJBtpasNvqbX3rouCPzel6IKOrPbOnPXBHQIsd5DlsgmHCLBzGBy50T/gURzfrgjvozXzJzBHRYMm9L2E23FfL51qUz7tnLj+871t1quePLieucOdnkTx4pWXT9OXIENE2AipQF33dWbkyZVZ8myQfr5rGLHffnte6+lZDOSt/d2+65Sw8uu2dO9ecCo6g/B6c/Ve5B+Fl7kQBSQJVBFA4kuRqQ0DzEOlitaJJ8sHy2Nzdyj2H9WDKa9aX+qD8Hpz95gt4wtsIxxnvGF27+TBPQNAACFAbO97/8iJ/pu+t3huIFDDTkqS+cGryfddHNDS26qnEQ63adP5mSja4HHq1225PQZ1v33bxI1J+D0x87xuTK/8tXPrfvWprk9d2ZJqCmAYJU/u3bT7qVlcTd7f7NXfv43uCYwGCQnVxq+VjHrLoZwoKZhNfbLz7n3zPb8F7fKWvFoSySfq/n8eEzlEVEvO36rtdO40DIPASjkag/09ef0FpmjDHBf3rrlnvjxvrQcf6iQ1rQQPZs6xlTGuUieHOt96knBQDqrqdxGgaIks/GCZ3XLL995zOXrCwPWTm8Z6a/t73tj2EFbN340M2SqM+y4n7eXfVKw81+7OLe9x7L/v766l48q4iwrZLJ/Vo+ddp1r1/3mIB5u7fter1UcZAHVo455+7O7GpT1J+D0x8rcuOR3Y8YO/tPYWIjligd4vrJrAGE/FNnyb170/kBAEBWAOvZDCAGEKb2OIA0y99fv+UJppUkQ7/J7zDz95ecS3b7rseImyHRjPXst58cfPbMna53I63yWEEZnrp1y717c9PjNI6ssX7ABYzAAxI+0UqDzwgWENhgDc2iRP05PP0ZmuCZyI4tO7fVG7ovjMEvri4N9IkJf627MxsumADCfMN3XD13zn8OQP31j/yg4AWz0kFedBgmHxc05pjiGhJmeURuxhceTNzS/e2h70ziZjTlntiguY3TyFrBlBUeeoEXOPE6tuTc04+f8Te/qE1grOtBQsiF08c9+Vi3C/JBeZZzZrbDlqg/h6c/oUiHmNAxDjTG0CkrH9zp+WO1tInGNGGCW3aUpQMANtiZmmt70t/dHbwnONpLltz7G/fcW5utXEuIG/CvX3vUEwwDill+a6vnmZ2ZHvLpbaWuF4OMa97Z6buf/eHDkS6erC/aLytMfrCySSfBSNhAsiJkBCVJejtjzxdO6nMeRgoYohxyTRFvBbZbg/cI5AMZ/eXTHe/bl3V/i/oW9We+9Od319dz22R1SOQjfWNyE9FpIpMl/dv3P/Fx2PY49rQBYGXKNrFUbQES+RAcpuOQjl6hQBB68V2Ihc6Pyk+BcGT6KdAKs3Me5OOByf6WbTuKgrWG1WY/47rMNpPkFAkbri3l0QxVRnmscLOFUdgmSARSkkUokZLovSwhMJRSlelD1J+joT9Jb8e3izFWdtyTroGb9Y0LD/r/a/ztu35RB3yA6ispOL++en1g5tIQFLaz1nWvnK03k1mARDwoeJ61A9HkiY4Rk0iyAdZdX983uzK7QDjbvT2CkbvlB1o260NAewGy/PbqeohMWqL9YIVVcPmJ0/7a3Gj5uHUFqw3hWr69x5YrKY8Imj7KbcLiW9vM/z795vsDRcksQns9MqKZtcZZP1F/jpr+OH8upJInGBCMYY0lJnm9v5fFghBZ1XZMJ0UAySrBF0Rxr924PQSQj6ts1gMoj3ys2acGFpGPhO8v852sY2pnKBDm0v0PXS8AYojlsxkewT2TaBZSIPut7BAZw2DEKhERf5nOnsTabfe+93H3liHLCjMMQfhBnGqr5060UtLsG8UowkfErL+tZMnd2LjnLpxeypII97eJvkDgS/f33FsIuWfIiV/H+skjn6g/i6A/x93uTkoqVuALSNQlexO5yAfR2KZfHOP/29nPokttWTYAxIv3EAQXhWgEKgChiAxeOkMQqY4AEG6XyM1m2VoQbKwnPBbGgG58klo0RQxNX3aYAVAUM6vb6yC0hWvZWR4sMJXT5cqO+/7FFY+Rgrd+j9RKf8jPpY0cr5oMJzPc+uzkJYWELBe0CKuB/54suf9852/u66/+yv1sbcstn3xgX3AdaxE3DB9fFiAv+qIXmLy9tule+OXVXPKJ+rMY+rOykuQuzsAF/KYIJi+cob7xuzc/6Q3y8NoAhDkIOCyT4WLxI2Jx2Az2wyT0jcouXjYWEAIEyVmALBj2/5aprdjPLUD8zRtg4YwkJvZ9ywiJuYHrcswmT+URGealsjkxSz2xrSz73+Z83hOoBeCq7qnuhSTcGoH8Zu1j99Pff+D/Cg/Ezmp6EQi88s57kD5t8bMibQ+3UtBO2vvff9rw12URgD7w+o//SxPK8sjHtjnqz2LoTyfYkEwbLZlqscJO9Bgt+hyCtosYQzYYXxJA+iHlzPg4iusPVkLqAiTXjmX10ATkZvzXX3seIBoqsglZOgTISh5AtJUZnnMU41HMR32kb1e7d/2sY90M/o+5LAw4bzdZdt+8+JAnQIvRic6jHvg65KzVBCvy2WUOszr3wi+v+j7TJ35/YCobrPgewsTxynef999/7TuX/Wfcv3CriWbOf34iNdshIpREilJ2xSvqz9HXn25O28LxhR7YiZ4YJASbt4LatgAhOomdz2HQCqDu9lu1zEMLEK6XzScRCPirMC0dtjNJaBFZgGBnhPYUBdk4BkiwPucCxiBItt3zRIaLkbfMKBfFA3nqtFce8iOUkAfwejHDaXmxyjK1LDaRs/IxbP99P1aSQX8tadMvCLv98CP+JpNCQD/B4wefT9ztH3/PW7Zck2uFW020R4cBIAIvSzxRfxZLf/KEyUvjC1KzLjzWD30Fo7z0jQSAWFo/f/KWW+086i0TxLsl2fo/F9d2BdyZqjkKClICkF8ODFabrCUEQNaq4RgdU1Y0ZElHiPIDEH+JKQGuKyAgfO7OpTOeOAHXWgDyRUfd8HR3dZqjAQbHzpwbrA5y/rfMtdMZMN9dGSWKiyGjVit8UtjZ5/3KiZR6LdlyL7z6pp+tFLj0md1Krlz/yK82aFXDz2JZ1Tu74x0FKwo0j8Im6s9i6M92ThY8+sPYFDfAFwifyVoSf+Rh5G0//PtUETf9hRSjKQKoztK7ACLhrSjwfHKpPQSQLCMIj9UtZhIBBMhpHlB63rZPgmoVAgR5XTi94a69k25ErdoPwINI/dJo9++DvWr0i6zhxy4+7lMTtMpRRbjW6jm7j6ZghaLX81bfBZO2w/eZpZ558Tn3RrYCCCkrKcyKksoQPYVEqRWoLLOU3aRbVqL+LIj+bKUB9pWzq0N77vCWWmcedu7m5kB/GLNbJSb31qiNfAjKyaoX5hmEVCdDkxlS9WZEQKOWAMP/Y9ZZS0xtCoNospZU01Zp4CRDYWrqGpNk8Ya5Lsoixq1jNvnJ63+sbD6rdIEUogw2eYISKPBYNAuGWIIN1w2Dg3Uk6s/R1p/frH08mLzhCIwVVtmQuvozZIpYYhFAiN8DlO2/uvb6H6tc31/nqU4abCtDPmE0HoFsCNo9e+Zh7yZgGo4CU6s82uIBqJrdmyAfCVYh7L9EYH7jnnMb3cFjTcqSdNrWz+0ljRUoi83NKBKw0Q3N+64vq3HnM/8e8gFTVrwIvjeBTdSfo60/0pvL53bd8WUm9TTsoUm/jv60xwEkJmQAE3+pukkzNQ9Tl07kU5Tjk7csr884B3YvAkaWEkIbf/TV8/7mkujIjI/71iT5SBjABA5hf/ASaZeRMK0dos3rfxMCNvd3nCcbBLcUxWHFq67yjJOoP0dHf8IVZoLVjCtWrCfZtza2HIdmyDoCQMyslM/QCgxSRCCjpAyorKIpZoVJyA2tE++pIgxgER6DHDM6rakyvhYP7YQYcW2xRvLcT/v/KhLmdyQ9536X+ei016/MeOKpHvCsIlF/5lt/wmuhP4PH9jSgP4UnK0BpV8CIjAOQVqJGDWwLENcBoGmJjf+IcCTTLJylPmJp4d5gYWDtCR+kKF1BxaBQHiTEp8jSG5VGnyf6Pm40yj4NSydPov6Ml5ej/hRbQCoPYLfaE8NxLtsHZlZSRg2wbjbAqkiZgJn9LhKmwB+UgIWPrZA/lSwPrRyKYGS6ioxttqpdrhy3L8fOYvxmldo8SrI8KIn6c/T0ZxqSlAEI8lGColZQQoDk7tjSjLhdOoZ5KR+1iGTGmYmjiKlOcuSkwu+xD4aaQsS3fvv+5j5sbILYW//79yE/Gnw4t7ueJleyHBtKnvIgZMzyTxtrpUzh97TClZLzdN2tPIn6c5T0573G9addBiByR8gc1moJKftYRRYgSMcm+Km2DwApPqMOqpOjCMduwdAr3JrBi+X1ultDmhCID9dLbQAb9tT5HcJZGkO4OueD6VlWqfJySOLKi5H5ejzJ0kBRBttHsu9oP5vwsDv9+Q4xmEmX1+tK1J/x0l1w/UnKAASJaAcrAJHFfCcjFGJDqmIo9gYgZTqznV8AEeeQ6SfysdmVtjF5rJ3HzhBb1ezdpgTC8y5ottk13WrS37cBFjx049n5bBVEq4xg982c2E5Yo5p8kVZvx89KKGoodhOgAsDWMrSlMxBV5ZsWflF/iuWlqD+jCUh+fOeT1I2yD6nTABtK2c7E7/DNBpYdYE8T5c9JcILFlXnJubhqDDCIL698hh1g73bv7kXlC2rTTHOAIcr+1C7nkGA+y3YCgx9FvS6tDJ/PeVyD/qj2DjWOwtKovH/7+ron8tTvP+kD4JynkrKSdOPfxmChQLhoW4o17f+81vfF5ZrGKepPObm2wPoz1gKiAUrRxvpJ0/nTmRd/FLJgdmeTHQDZAvC+WuFufwAQuURaMbI/7CuoZQDd3LiX+bXFAO1l7hYDhIuookwA9NpDl31KQNNEpN9W9jiKgj8ua1FWG8RqM06VMq8aPGCHtfL04w84/xycnMJpEDXX/cbursce07jzxOm0DEJWQIxrgVd3/e4QCYRPRbCCIr1RMcG0rET9GS0vLbj+jF1qogEoER3E/Lf5Nlb8ZtCsOBEi4Ng9rzo9CkbbWsMDcHz1tcSDgk8sgLgZtsgRN4LPFHS2AFFlUbVpIB+llYeP9mlaaANWGLMT/bDF2kTOVuS/q0aK9fV9WYPMr0fAAhFW4EjiGn3iPUvr9joQM4qo5EIVzw/rxKjgHCkDpP+HZSSaxCbqz3iM1hZUf9pVAMKCoXMCCGvH1lW2ZqOtB4KFI4B0jVAUC1IxNAHEBlgBpNIZdke+fbY5AjAEwH2N2t6O/wtA08r2tRhBmEqAlLlMu1md8DV1sxXBMJYlUvYlZTNLkTiZLZwmXFUYjn7JZEdhMJn5q+JhmllV/JwqB5yjoljs4VGJhLx8LhWUbwqbqD9Rf/KkdM62Ld05ONkUes/LSVDtExGOrBoNMLFzeA0GIQOl7ACDfEQ6DHYVuIcMYPi8ARY+7rkJgeBon6xDAs4qziTS0TGRqmYmSAHXk/5wDcxgjvsnk2bfEUlzDeVlYKbzf/1umIoQVrLkeuAuogyx0dMswqJlk0rUn/FyZQH1p9KTUWVxKN8HghnM0tl3OPbUTt8dC6wilQL40noK0PkvP+JnYdwpSCoEUatmlCvAChI4lAIIAbKlJ2WyfpDtzs0DyPvbWZZ30yKzlWtDpop54WN3suAhZPrF1VR5bAAeLPUYE4jT1zk6+YBPAKVP3PwTy4lXGo4PbRDsnPAWlx5drWPKzNZvI1yH+FmG2OAZZz5+dqrvLn3+fJr7kVPgfxKJ+jNeriyY/tRyScRwAKQsaRERHbPbEwQQrEnDcdeoHgfAKiRmAeI43w0Lh6ng0aDWSPZAN9wLLB8J59MW5SxZsW0uqtDWlNjSFEoeY9kc/xtRSU7iaaElCRbqq9pLsB/FQ2yhJ0R4I0r4VGW9H/7juX3PZFJA3z/na6s1VNBf6RNNlOcokqg/4+WVBdGfRmq/0Ek1QKadYj5KQBRIcssgIQuQSEzni2TCshoiMwAWieXdAD4TOAJIz4VPTAmBaScv2rQAESAKoH5TTF/Pzeam8znvCd7bXCJJuLn27Ref6+Maaxc1/STorydTQtx6zLIemTLuIYycj6tdVIi+CYn6Ux6no6w/rWkDRENkSqq0Ku/1SGVYUpaO9UEF0O0ff69v98MQ+MZqEkC+Vm1mgiqmZAnJPniwDkBNSvg4nLCsgp0tyn5WdB8kKJAsSxVEFz55yqSHDhQ96rppifozv/qjhaFJ9KdSDChPwviKN2ySVWrMDnX8jZIAhclMZ//9f1qjAJJoMx8SunM22u4B2jAlBQ5Qpjmgw/ug9507xN7SOAAiXOwDGTW7Ienjl+8OakZPW6L+zK/+QD3HJ9SfZFqdODCAsqc2uq3tXMtHAW7tZjjoAVZHRs1UVbACJ/n7BC01Ww2sHrNQoAQ0BScPY+Nq1J/mg9m8qupRWf3xVlG2wlZXf6ZTOi0QATDqwfZVBh0ACST/ULScQveha6HgmJKsDmPj6kGL8m9wcxWABAcfRLSZ5Un6wLi856IdtlTRizJ6hkT9mR39ORACmjZAsC4A6QmTei4RAJFvZAGadfKZ1PoJhb6qMBz9BwfImc3FYMZiAPEwiHzWsWlajqL+1CHkw9SfRl2wuibhJINOCYnsV1MQ3Ep3PV229DGfOVKePKlLSl9/9VctG6PTo42QMOg/S1JHL6q6HkdBf65M2WKdpv60Dkuhit5Pcv3waZ+zPsCijJa6elH3vKg/UaJEieIWR/4fY2sZ1Lh7NyEAAAAASUVORK5CYII=";var gr="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA8AAAAPCAYAAAA71pVKAAAA2UlEQVR4nGNgwAPeHo35D8K45JnwaRQS/YXPbAacmkEa/3z/BaZx2c6Ey1YGIgAjLsV8PF/g7E9feMC0sPUSRhTNuPwGcjI28AlqENzZIIUw/O41G8O58z/BkiycbGDFl278R3GRENQyJpBTkE1DtgFkyIMnn+EGwQDIApA+uB9AzgeZCtIEMhlmu54GI1gMFgafvvDA/Y4SADADYABkG7LfPyFpxIgq9IBD1ojsbAZ0zYTiFlvoY00ksFAGYWw2wtWhC4BD/gsiQUBcxAbxEiI4MAE+p2PLYQAGj4EmRoa2cwAAAABJRU5ErkJggg==";var gt=()=>i`<span class="pet-art" aria-hidden="true"><div class="sprite" style=${{backgroundImage:`url(${hr})`}}></div><img class="star" src=${gr} alt="" /></span>`;function Yo(e,t){let s="animation-duration:"+((innerWidth+180)/(100+t*12)).toFixed(1)+"s;animation-delay:-"+t*9%14+"s",n=()=>{e.hideKey&&bn(e.hideKey,e.hideHash??"")},r=[i`<div class="bub">${e.label&&[i`<span class="badge dim">${e.label}</span>`," "]}<span>${e.text.length>48?e.text.slice(0,47)+"\\u2026":e.text}</span></div>`,i`<${gt} />`],o=e.open;return e.hash?i`<a key=${e.key} class="lob" style=${s} title="open the decision" href=${e.hash} onClick=${n}>${r}</a>`:e.href?i`<a key=${e.key} class="lob" style=${s} title=${e.label==="report"?"open the report":"open the PR"} href=${e.href} target="_blank" rel="noopener" onClick=${n}>${r}</a>`:i`<div\n key=${e.key}\n class="lob"\n style=${s}\n title="click to open"\n onClick=${()=>{n(),o&&O(o.type,o.key)}}\n >\n ${r}\n </div>`}function $r({state:e}){let t=e.snapshot,s=mr(t?.attention||[],{lobs:e.prefs.lobs,hidden:e.lobHidden,preview:e.preview,previewHelm:t&&t.helms.length?t.helms[0].grounds:void 0}),n=s.map(r=>r.key).join("|");return qt(()=>s.map((r,o)=>Yo(r,o)),[n])}function $t({box:e}){if(!e)return null;let t=s=>{s.target===s.currentTarget&&le()};return i`<div id="lightbox" class="lightbox" role="dialog" aria-label=${e.name} onClick=${t}>\n <button type="button" class="lbclose" title="close" onClick=${le}>\xD7</button>\n <img class="lbimg" src=${e.src} alt=${e.name} />\n <a class="lboriginal" href=${e.src} target="_blank" rel="noopener">open original \xB7 ${e.name}</a>\n </div>`}var me=i`<span class="x" onClick=${de}>\xD7</span>`,vr=(e,t,s)=>i`<span class="seg">${t.map(([n,r])=>i`<button class=${n===s?"on":""} onClick=${()=>e(n)}>${r}</button>`)}</span>`;function Qo(e,t){let s=vr(vn,[["table","table"],["cards","cards"]],t.view),n=vr(kn,[["on","on"],["off","off"]],t.lobs?"on":"off"),r=e.attentionError?e.attentionError:(e.attentionKinds||[]).join(" \\xB7 ");return[me,i`<h3>\u2699 settings <span class="dim" style="font-weight:normal">\xB7 this browser only</span></h3>`,i`<div class="settings"><div class="row"><span class="lbl">view<span class="hint">cards or table (notices stay a table)</span></span>${s}</div><div class="row"><span class="lbl">lobs<span class="hint">crawling lobsters in this page</span></span>${n}</div><div class="row"><span class="lbl">attention<span class="hint">kinds shown \u2014 attentionKinds in config.toml (read-only here)</span></span><span class="dim" style="font-size:11px;text-align:right;max-width:260px">${r}</span></div></div>`]}function Xo(e,t){let s=fe(t.numbers,new Map(t.numbers.map((r,o)=>[r,t.titles[o]])),e.number),n=t.nextMergeable?[" \\xB7 ",i`<span class="ok">next mergeable</span>`]:t.blockedBy?" \\xB7 blocked by #"+t.blockedBy:t.nextNumber?" \\xB7 next is #"+t.nextNumber:"";return[s," \\xB7 ",t.position," of ",t.size," \\xB7 floor ",t.floor,n]}var Zo=e=>t=>{t.preventDefault(),O("dispatch",e)};function ea(e,t){let s=Js(e,t),n=s.pr,r=s.stack,o=s.watch,a=dt(n),l=s.chain.length?s.chain.flatMap((c,d)=>[d?" \\u2192 ":"",c.culled?i`<span class="dim">${c.id.slice(0,8)} (culled)</span>`:[i`<a href="#" onClick=${Zo(c.modalKey)}>${c.id.slice(0,8)}</a>`," ",i`<span class="dim">${c.verb}</span>`]]):i`<span class="dim">none</span>`,u=o?[i`<div>${o.key} \xB7 owner ${o.owner||"?"} \xB7 ${o.lastCheckedAt?["checked ",v(o.lastCheckedAt)," ago"]:"never checked"}</div>`,o.lastError&&i`<div class="bad">${o.lastError}</div>`,i`<div class="dim" style="font-size:11px;margin-top:4px">cursor</div>`,T(o.cursor)]:i`<div class="dim">no watch \u2014 <code>lobstah watch add ${n.url}</code> registers one</div>`;return[me,i`<h3>${it(n)} ${n.title||""} <span class=${"badge "+E(n.badge)}>${n.badge.text}</span>${n.draft&&n.badge.text!=="draft"&&[" ",i`<span class="badge pr-draft">draft</span>`]}</h3>`,i`<div class="sub">${n.repo} \xB7 ${n.state} \xB7 observed ${v(n.observedAt)} ago${n.gate&&" \\xB7 gate "+n.gate}</div>`,i`<div class="sec">checks</div><div>${Ce(n)}</div><div class="sec">review</div><div>${a||i`<span class="dim">no review yet</span>`}</div><div class="sec">merge</div><div>${Te(n)}</div>${n.repairWait&&i`<div class="sec">repair waits</div><div>${n.repairWait.heldBy}: ${n.repairWait.reason}${n.repairWait.until?" \\xB7 until "+n.repairWait.until:""}</div>`}<div class="sec">refs</div><div>${n.headRefName||"?"} \u2192 ${n.baseRefName||"?"}</div><div class="sec">stack</div><div>${r?Xo(n,r):i`<span class="dim">not stacked</span>`}</div><div class="sec">dispatch chain</div><div>${l}</div><div class="sec">watch</div>`,u]}var kr=(e,t)=>(e==="codex"?"codex resume ":"claude --resume ")+t;function ta(e){let t=Date.now()-Date.parse(e.heartbeatAt)>18e5;return[me,i`<h3>\u26F5 ${e.man}</h3><div class="sub">helm of <b>${e.grounds}</b> (${(e.repos||[]).join(", ")})</div><div class="sub">${e.cwd??"?"}${e.host&&" \\xB7 "+e.host}</div><div class="sub">session ${e.sessionId??""} \xB7 signed on ${v(e.signedOnAt)} ago \xB7 heartbeat <span class=${t?"warn":"ok"}>${v(e.heartbeatAt)} ago</span></div>`,i`<${nt} worker=${e} />`,e.sessionId&&[i`<div class="sec">open this session</div>`,T(kr(e.harness,e.sessionId))],e.transcript&&[i`<div class="sec">transcript</div>`,T(e.transcript)]]}function sa(e){return[i`<div class="sec">report \xB7 ${e.title}</div>`,i`<div class="sub">${ns(e)} \xB7 <a href=${G(e.key)} target="_blank" rel="noopener">open the report \u2197</a></div>`]}function na(e,t,s){let n=e.claimedBy&&e.claimedBy.startsWith("wt:")?[i`<div class="sec">worked by trap</div>`,i`<div class="claimedby">claimed by ${at(e.claimedBy.slice(3))}</div>`,T(U(e.claimedBy)),i`<div class="dim" style="font-size:11px">an opted-in interactive session mans this seat \u2014 attach would resume someone\'s live thread. Message it instead: lobstah send ${U(e.claimedBy)} "\u2026"</div>`]:[i`<div class="sec">open this session</div>`,T("lobstah attach "+e.id)];return[me,i`<h3>${e.id.slice(0,8)} <span class=${"badge v-"+e.verb}>${e.verb}</span></h3>`,i`<div class="sub">${e.repo} \xB7 ${e.lane} ${e.bucket} \xB7 ${v(e.verbAt)}${e.for&&[" \\xB7 ",Ge(e)]} ${ue(e)}</div>`,n,e.worker&&i`<${nt} worker=${e.worker} headless=${!e.claimedBy?.startsWith("wt:")} />`,e.worktree&&[i`<div class="sec">worktree${e.worktreeOf?" (reused from "+e.worktreeOf.slice(0,8)+")":""}</div>`,T(e.worktree),e.worktreeKept&&i`<div class="dim" style="font-size:11px">kept: ${e.worktreeKept}</div>`],e.transcript&&[i`<div class="sec">transcript</div>`,T(e.transcript)],t&&sa(t),Un(e,s)]}function ra(e){let t=Ae(e),s=e.requested?[e.repo??""," \\xB7 ",e.harness??""," \\xB7 requested ",v(e.requested.at)," ago from the glass; the helm starts it with man throw --new."]:e.starting?e.starting.failedAt?[e.repo??""," \\xB7 reserved ",v(e.starting.reservedAt)," ago \\xB7 start failed: ",e.starting.reason??"",". Its addressed work stays queued; a session can still redeem the ticket, or `lobstah stow --wt ",e.name??e.trapId,"` withdraws it."]:[e.repo??""," \\xB7 reserved ",v(e.starting.reservedAt)," ago \\xB7 starting: waiting for a session to redeem the ticket (due by ",e.starting.deadline,")"]:e.live?[e.repo??"addressed bait only"," \\xB7 session ",e.sessionId??""," \\xB7 signed on ",v(e.signedOnAt)," ago \\xB7 ",t.listen," \\xB7 heartbeat ",t.hb]:"signed off \\u2014 registration gone; the lifecycle, messages, and catches are the surviving record. Re-soaking the same worktree restores this address.",n=e.notices.length?e.notices.map(a=>i`<div key=${a.seq} class="loglines">${v(a.at)} ago \xB7 <b>${a.kind}</b> \u2014 ${a.text}</div>`):i`<div class="empty">none recorded</div>`,r=e.messages.length?e.messages.map(a=>i`<div key=${a.file} class=${"msg"+(a.from==="helm"?" from-helm":"")}><div class="hdr">from ${a.from} \xB7 ${a.at&&[v(a.at)," ago"]} \xB7 ${a.state==="pending"?i`<span class="warn">pending</span>`:i`<span class="ok">delivered</span>`}</div>${a.text}${a.attachments?.length?ce(a.attachments,l=>Ls(e.trapId,l)):""}</div>`):i`<div class="empty">none</div>`,o=e.catches.length?e.catches.map(a=>i`<div key=${a.lane+":"+a.id} class="catch click" onClick=${S("dispatch",a.lane+":"+a.id)}><div class="hdr"><b>${a.id.slice(0,8)}</b><span class=${"badge v-"+a.verb}>${a.verb}</span><span class="dim">${v(a.verbAt)}</span>${ue(a)}</div>${a.note&&i`<div class="loglines">${R(a.note)}</div>`}</div>`):i`<div class="empty">none yet</div>`;return[me,i`<h3>\u{1FAA4} ${e.label??`wt:${e.trapId}`} <span class="badge">${e.requested?"requested":e.starting?e.starting.failedAt?"start failed":"starting":e.live?"live":"signed off"}</span></h3>`,i`<${nt} worker=${e} />`,e.worktree&&i`<div class="sub">${e.worktree}</div>`,i`<div class="sub">${s}</div>`,i`<div class="sec">window</div>`,Y(e),Q(e),e.live&&e.sessionId&&[i`<div class="sec">open this session</div>`,T(kr(e.harness,e.sessionId)),i`<div class="dim" style="font-size:11px">as registered at sign-on \u2014 a hookless enlistment may hold a made-up id</div>`],i`<div class="sec">lifecycle (${e.notices.length})</div>`,n,i`<div class="sec">messages (${e.messages.length})</div>`,r,i`<div class="sec">catches (${e.catches.length})</div>`,o]}function br({snapshot:e,modal:t,prefs:s,detail:n=null}){if(!e||!t)return null;let r=re(e,t,n);if(!r&&t.type==="dispatch"&&n?.key===t.key)return[me,i`<h3>${t.key.slice(t.key.indexOf(":")+1,t.key.indexOf(":")+9)}</h3>`,i`<div class=${n.error?"bad":"dim"}>${n.error??"loading\\u2026"}</div>`];if(!r)return null;if(t.type==="settings")return Qo(r,s);if(t.type==="pr")return ea(e,t.key);if(t.type==="helm")return ta(r);if(t.type==="dispatch"){let o=js(e,r);return na(r,o,n)}return ra(r)}var oa=e=>i`<tr key=${e.seq}><td class="dim">${v(e.at)}</td><td>${e.kind}</td><td class="grow">${e.text}</td><td class="dim">${e.repo??""}</td></tr>`,yr=({inp:e,more:t})=>[M(["at","kind","text","repo"],e.list.map(oa),"no notices"),t&&i`<${pe} kind="notices" more=${t} />`];function _r(e){let t=new Map;for(let s of e.prs){let n=t.get(s.stackId)||[];n.push(s),t.set(s.stackId,n)}return e.stacks.map(s=>({s,prs:t.get(s.id)||[]})).filter(s=>s.prs.length)}var wr=(e,t)=>fe(e.numbers,new Map(t.map(s=>[s.number,s.title]))),is=e=>e.state+(e.draft?" \\xB7 draft":"")+(e.state==="MERGED"&&e.mergedAt?" \\xB7 "+e.mergedAt:"");function Sr(e){let t=n=>{let r=String(n??"");return r.length>24?r.slice(0,23)+"\\u2026":r},s=e.watches.map(n=>i`<tr key=${n.key}><td>${n.key}</td><td>${n.owner}</td><td title=${n.cursor}>${t(n.cursor)}</td><td>${n.lastCheckedAt&&v(n.lastCheckedAt)}</td><td>${n.lastError||""}</td></tr>`);return[i`<h2>other watches</h2>`,M(["key","owner","cursor","last check","error"],s,"no other watches")]}function aa(e,t){let s=_r(e).flatMap(({s:n,prs:r})=>[i`<tr key=${"stack:"+n.id}><th colspan="8">${wr(n,r)} \xB7 floor ${n.floor}${n.open?" \\xB7 open":" \\xB7 history"}${n.readiness&&i`<div class=${n.readiness.allReady?"ok":"dim"}>${n.readiness.text}</div>`}</th></tr>`,...r.map(o=>i`<tr key=${o.key} class="rowhead" onClick=${S("pr",o.key)}><td>${it(o)}</td><td class="grow">${o.title||""}</td><td>${is(o)}</td><td>${Ce(o)}</td><td>${dt(o)}</td><td>${Te(o)}</td><td>${Jt(o.watch)}</td><td>${o.gate||""}</td></tr>`)]);return[M(["PR","title","state","checks","review","merge","watch","gate"],s,"no PR evidence"),t,Sr(e)]}function ia(e,t){let s=_r(e),n=o=>i`<div key=${o.key} class="card" onClick=${S("pr",o.key)}><div class="top">${lt(o)}<span class=${"badge "+E(o.badge)+B(o.badge.text)} title=${j(o.badge.text)}>${o.badge.text}</span></div><div class="meta" title=${`${o.repo} \\xB7 ${is(o)}`}>${o.repo} \xB7 ${is(o)} \xB7 ${Te(o)}</div><div class="foot"><span>${Ce(o)}</span><span>${Jt(o.watch)}</span>${o.gate&&i`<span>gate ${o.gate}</span>`}</div></div>`;return[s.length?s.map(({s:o,prs:a})=>i`<${ee} key=${"stack:"+o.id}><h2>${wr(o,a)} \xB7 floor ${o.floor}${o.open?"":" \\xB7 history"}</h2>${o.readiness&&i`<div class=${o.readiness.allReady?"ok":"dim"}>${o.readiness.text}</div>`}<div class="cards">${a.map(n)}</div><//>`):i`<div class="empty">no PR evidence</div>`,t,Sr(e)]}function Dr({inp:e,more:t}){let s=t&&i`<${pe} kind="prs" more=${t} />`;return e.view==="cards"?ia(e,s):aa(e,s)}var la=["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],da=5,ls=(e,t,s=t+"es")=>`${e} ${e===1?t:s}`,ca=e=>e.count?`${ls(e.count,"catch")} on ${e.date}`:`No catches on ${e.date}`,xr=new WeakSet,pa=e=>{!e||xr.has(e)||(xr.add(e),e.scrollLeft=e.scrollWidth)},Gr={ArrowUp:-1,ArrowDown:1,ArrowLeft:-7,ArrowRight:7};function ua({page:e,focus:t}){let s=e.weeks.flat(),n=t&&s.some(u=>u.date===t)?t:e.today,r=u=>{let c=s.findIndex(p=>p.date===n),d;if(u.key in Gr)d=c+Gr[u.key];else if(u.key==="Home")d=0;else if(u.key==="End")d=s.length-1;else return;u.preventDefault();let h=s[Math.max(0,Math.min(s.length-1,d))];Lt(h.date),u.currentTarget.querySelector(`[data-date="${h.date}"]`)?.focus()},o=e.weeks.length-1,a=(u,c,d)=>{let h=u[d];if(!h)return i`<td key=${c} class="pad"></td>`;let p=ca(h),m=c<4?" tl":c>o-4?" tr":"";return i`<td\n key=${c}\n role="gridcell"\n class=${`day l${h.level}${d<2?" below":""}${m}`}\n data-date=${h.date}\n data-tip=${p}\n aria-label=${p}\n tabindex=${h.date===n?0:-1}\n onFocus=${()=>Lt(h.date)}\n ></td>`},l=e.months[0]?.week??e.weeks.length;return i`<div class="heat-scroll" ref=${pa}>\n <table class="heatmap" role="grid" aria-label=${`Catches per day, last ${e.weeks.length} weeks`} onKeyDown=${r}>\n <thead aria-hidden="true">\n <tr>\n <td class="wd"></td>\n ${l>0&&i`<td colspan=${l}></td>`}\n ${e.months.map((u,c)=>i`<td key=${u.week} class="month" colspan=${(e.months[c+1]?.week??e.weeks.length)-u.week}><span>${u.label}</span></td>`)}\n </tr>\n </thead>\n <tbody>\n ${la.map((u,c)=>i`<tr key=${u} role="row">\n <td class="wd" aria-hidden="true">${c%2?u:""}</td>\n ${e.weeks.map((d,h)=>a(d,h,c))}\n </tr>`)}\n </tbody>\n </table>\n </div>`}var fa=()=>i`<div class="heat-legend" aria-hidden="true">\n Less ${[0,1,2,3,4].map(e=>i`<span key=${e} class=${"swatch l"+e}></span>`)} More\n </div>`;function ma({page:e}){let t=[["today",e.catchesToday],["this week",e.catchesThisWeek],["total",e.totalCatches]];return i`<div class="stat-tiles">\n ${t.map(([s,n])=>i`<div key=${s} class="stat-tile"><div class="n">${n}</div><div class="dim">${s}</div></div>`)}\n </div>`}function ha({page:e}){let t=e.perTrap.slice(0,da);if(!t.length)return null;let s=t[0].catches||1;return i`<section class="top-traps">\n <h2>Top traps <span class="dim">\xB7 all time</span></h2>\n <ol>\n ${t.map(n=>i`<li key=${n.name}><span class="name" title=${n.name}>${n.name}</span><span class="bar"><span style=${{width:`${n.catches/s*100}%`}}></span></span><span class="n">${ls(n.catches,"catch")}</span></li>`)}\n </ol>\n </section>`}function Cr({page:e,error:t,focus:s}){return e?i`<div class="stats">\n <${ma} page=${e} />\n ${e.totalCatches===0&&i`<div class="stats-empty"><${gt} /><div><b>No catches yet.</b> <span class="dim">A dispatch that finishes done is a catch; each one lands on its day here.</span></div></div>`}\n <section class="heat">\n <h2>Catches per day</h2>\n <${ua} page=${e} focus=${s} />\n <div class="heat-foot">\n <span class="dim"\n >${e.undated>0?`${ls(e.undated,"earlier catch")} ${e.undated===1?"has":"have"} no recorded day${e.historyFrom?`; daily history starts ${e.historyFrom}`:""}.`:""}</span\n >\n <${fa} />\n </div>\n </section>\n <${ha} page=${e} />\n ${t&&i`<div class="dim">${t}; showing the last numbers read.</div>`}\n </div>`:i`<div class="empty">${t??"loading stats\\u2026"}</div>`}var ds=e=>e.requested?"requested":e.starting?e.starting.failedAt?"start failed":"starting":e.harness??(e.live?"":"signed off"),Tr=e=>e.live||e.starting?"":" dim";function ga(e){let t=Ae(e);return i`<tr key=${e.trapId} class=${"rowhead"+Tr(e)} onClick=${S("trap",e.trapId)}><td><b>${e.label??`wt:${e.trapId}`}</b></td><td>${e.repo??"\\u2014"}</td><td class="grow">${e.worktree??""}</td><td>${e.harness&&i`<${H} worker=${e} cls="" />`}</td><td>${(e.sessionId??"").slice(0,8)}</td><td>${Re(e)}${Q(e)}</td><td>${t.listen}</td><td>${t.hb}</td><td>${Qt(e)}</td><td class="catchn" title="catches">\u{1F99E} ${te(e.totalCatches??0)}</td><td>${Y(e)}</td></tr>`}function $a(e){let t=Ae(e),s=Qt(e),n=`${e.repo??(e.live?"addressed bait only":"history")}${e.sessionId?" \\xB7 session "+e.sessionId.slice(0,8):""}`;return i`<div key=${e.trapId} class=${"card"+Tr(e)} onClick=${S("trap",e.trapId)}><div class="top"><b>\u{1FAA4} ${e.label??`wt:${e.trapId}`}</b>${!e.requested&&!e.starting&&e.harness?i`<${H} worker=${e} />`:i`<span class=${"badge"+B(ds(e))} title=${j(ds(e))}>${ds(e)}</span>`}</div><div class="meta" title=${n}>${n}</div>${e.worktree&&i`<div class="note" title=${e.worktree}>${e.worktree}</div>`}<div class="note" title=${pt(e)}>${Re(e)}</div>${Q(e)}<div class="foot"><span>${t.listen}${e.live&&[" \\xB7 heartbeat ",t.hb]} \xB7 <span class="catchn" title="catches">\u{1F99E} ${te(e.totalCatches??0)}</span>${s&&[" \\xB7 ",s]}</span><span class="footact">${Y(e)}</span></div></div>`}function Ar({inp:e}){let t=e.list.map(n=>n.x),s=i`<div class="tabhead"><${ct} /></div>`;return e.view==="cards"?[s,t.length?i`<div class="cards">${t.map($a)}</div>`:i`<div class="empty">no traps soaking</div>`]:[s,M(["address","repo","worktree","harness","session","now","listening","heartbeat","mail","catches","window"],t.map(ga),"no traps soaking")]}function Rr({state:e}){let t=oe(e),s=e.route,n=t?Xe():[],r=t&&zs(t,{st:e.prefs,modal:e.modal,detail:e.detail},Date.now()),o=u=>({left:e.snapshot?Ks(e.snapshot,e.older,u):0,loading:e.olderLoading===u,error:e.olderError?.kind===u?e.olderError.text:void 0}),a=u=>!r||u!==s?null:u==="deck"?i`<${dr} inp=${r.deck} drafts=${e.drafts} focus=${e.focusDecision} viewedHere=${e.viewedHere} unread=${n.length} />`:u==="dispatches"?i`<${cr} inp=${r.dispatches} more=${o("dispatches")} />`:u==="traps"?i`<${Ar} inp=${r.traps} />`:u==="prs"?i`<${Dr} inp=${r.prs} more=${o("prs")} />`:u==="reports"?i`<${Vn} inp=${r.reports} />`:u==="stats"?i`<${Cr} page=${e.stats} error=${e.statsError} focus=${e.statsFocus} />`:i`<${yr} inp=${r.notices} more=${o("notices")} />`,l=u=>{u.target===u.currentTarget&&de()};return[i`<${ur} d=${t} inp=${r} tab=${s} st=${e.prefs} stale=${e.stale} unread=${n.length} />`,se.map(u=>i`<main key=${u} id=${"page-"+u} class=${t&&u===s?"tabpage on":"tabpage"}><div id=${u}>${a(u)}</div></main>`),i`<footer id="foot"><${fr} d=${t} /></footer>`,i`<div id="lobs"><${$r} state=${e} /></div>`,i`<${$t} box=${e.lightbox} />`,i`<div id="overlay" class=${e.modal&&t?"open":t?"":void 0} onClick=${l}><div class="modal" id="modalbox"><${br} snapshot=${t} modal=${e.modal} prefs=${e.prefs} detail=${e.detail} /></div></div>`,t&&i`<${ar} open=${e.decisionModal} order=${ye()} drafts=${e.drafts} extensions=${t.answerLimits?.extensions??[]} />`,t&&i`<${ir} unread=${n} dismissed=${e.alertDismissed} />`]}function va({view:e}){if(e.state==="loading")return i`<main class="reportview"><div class="dim">loading\u2026</div></main>`;if(e.state==="error")return i`<main class="reportview"><div class="bad">${e.error}</div></main>`;let t=e.report;return i`<main class="reportview">\n <h1>${t.title}</h1>\n <${nr} r=${t} text=${e.text!==void 0?{text:e.text}:{error:e.error}} />\n </main>`}function Mr({view:e,box:t}){return[i`<${va} view=${e} />`,i`<${$t} box=${t} />`]}async function Pr(e){let t=encodeURIComponent(e),s,n;try{let r=await fetch(`/report/${t}/meta`);if(!r.ok)return{state:"error",error:`report not found (${r.status})`};n=r.headers?.get("x-lobstah-token")??void 0,s=JSON.parse(await r.text())}catch{return{state:"error",error:"report could not be read"}}try{let r=await fetch(Fs(e)),o=r.ok?{text:await r.text()}:{error:`report not found (${r.status})`};return{state:"ready",report:s,...o,...n?{token:n}:{}}}catch{return{state:"ready",report:s,error:"report could not be read"}}}async function Ir(e){if(!(e.state!=="ready"||e.text===void 0||e.report.acked||!e.token))try{await fetch("/requests",{method:"POST",headers:{"content-type":"application/json","x-lobstah-token":e.token},body:JSON.stringify({kind:"report-viewed",payload:{key:e.report.key}})})}catch{}}var ka=3e4,Er="lobstah-glass-page",Lr="lobstah-glass-shown",ba=20;function ya(){try{let t=sessionStorage.getItem(Er);if(t&&/^[A-Za-z0-9_-]{8,64}$/.test(t))return t}catch{}let e="";try{e=Array.from(crypto.getRandomValues(new Uint8Array(12)),t=>t.toString(16).padStart(2,"0")).join("")}catch{for(;e.length<24;)e+=Math.random().toString(36).slice(2);e=e.slice(0,24)}try{sessionStorage.setItem(Er,e)}catch{}return e}var Or=null,Nr=()=>Or??(Or=ya()),ps=[];function Hr(){try{let e=JSON.parse(sessionStorage.getItem(Lr)??"[]");if(Array.isArray(e))return e.filter(t=>typeof t=="string")}catch{}return ps}function _a(e){ps=[...Hr(),e].slice(-ba);try{sessionStorage.setItem(Lr,JSON.stringify(ps))}catch{}}function wa(e){if(!e||typeof e!="object")return!1;let{id:t,hash:s}=e;if(typeof t!="string"||!t||!Bs(s)||Hr().includes(t))return!1;if(_a(t),s==="")return!0;let n=ke(s);return n!==null?location.pathname!==G(n)&&location.assign(G(n)):location.pathname!=="/"?location.assign(`/${s}`):location.hash!==s&&(location.hash=s),!0}var cs=!1;async function Ie(){if(!cs){cs=!0;try{let e=document.hidden?"hidden":"visible",t=await fetch(`/api/presence?page=${Nr()}&vis=${e}`,{method:"POST",cache:"no-store"});if(!t.ok)return;wa((await t.json())?.show)}catch{}finally{cs=!1}}}function us(e){let t=null,s=0,n=()=>{let r=document.hidden?ka:e??0;r!==s&&(t&&clearInterval(t),t=r?setInterval(()=>{Ie()},r):null,s=r)};document.addEventListener("visibilitychange",()=>{n(),Ie()}),window.addEventListener("pagehide",()=>{try{navigator.sendBeacon?.(`/api/presence?page=${Nr()}&vis=gone`)}catch{}}),n(),Ie()}var fs=!1,Ee=null,Oe=null;function Sa(e){let t=e.headers?.get("x-lobstah-beats");if(t)try{return JSON.parse(decodeURIComponent(t))}catch{return}}async function ms(){if(!fs){fs=!0,Ie();try{let e=await fetch("/data",{cache:"no-store",headers:Oe?{"if-none-match":Oe}:{}});if(e.status===304){let t=Sa(e);t?ln(t):Ye(await Da())}else{if(!e.ok)throw new Error(`/data answered ${e.status}`);Oe=e.headers?.get("etag")??null,Ye(await e.json())}Ot(),g().route==="stats"&&Se()}catch{mn()}finally{fs=!1}}}async function Da(){Oe=null;let e=await fetch("/data",{cache:"no-store"});if(!e.ok)throw new Error(`/data answered ${e.status}`);return Oe=e.headers?.get("etag")??null,await e.json()}var jr=()=>{Ee||(Ee=setInterval(ms,2e3))},xa=()=>{Ee&&clearInterval(Ee),Ee=null};function Br(){document.addEventListener("visibilitychange",()=>{document.hidden?xa():(ms(),jr())}),ms(),document.hidden||jr()}var hs=()=>Os(location.hash),Ur=e=>window.addEventListener("hashchange",()=>e(hs()));function qr(){let e=ke(location.hash);return e===null?!1:(location.replace(G(e)),!0)}var Wr=()=>({snapshot:void 0,route:hs(),prefs:Xs(),modal:null,detail:null,older:Mt,olderLoading:null,olderError:null,stale:!1,lobHidden:en(),focusResults:{},preview:new URLSearchParams(location.search).has("lob"),drafts:{},focusDecision:ne(location.hash),decisionModal:ne(location.hash)?{key:ne(location.hash)}:null,viewedHere:{},alertDismissed:[],lightbox:null,stats:null,statsError:null,statsFocus:null}),Fr=Us(location.pathname);Fr!==null?Ga(Fr):qr()||Ca();function Ga(e){Pt(Wr());let t={state:"loading"},s=()=>xt(Z(Mr,{view:t,box:g().lightbox}),document.body);document.title="report \\xB7 lobstah glass",It(s),s(),us(2e3),Pr(e).then(n=>{t=n,n.state==="ready"&&(document.title=`${n.report.title} \\xB7 lobstah glass`),s(),Ir(n)}),document.addEventListener("keydown",n=>{n.key==="Escape"&&g().lightbox&&le()})}function Ca(){Pt(Wr());let e,t=!1,s=()=>{let a=g(),l=a.route!==e||!t&&!!a.snapshot;xt(Z(Rr,{state:a}),document.body),e=a.route,t||(t=!!a.snapshot),l&&window.scrollTo(0,0),a.focusDecision||(n=null),r(a.focusDecision)},n=null;function r(a){if(!a||a===n)return;let l=[...document.querySelectorAll("[data-decision]")].find(u=>u.getAttribute("data-decision")===a);l&&(n=a,l.scrollIntoView?.({block:"center"}))}It(s),s(),Ur(a=>{if(qr())return;let l=ne(location.hash);l!==g().focusDecision&&(n=null),k({route:a,focusDecision:l}),l&&ae(l),a==="stats"&&Se()});let o=a=>!!a&&(!!a.closest?.("input, textarea, select, [contenteditable]")||a.isContentEditable===!0);document.addEventListener("keydown",a=>{let l=g(),u=a.target;if(a.key==="Escape"){l.lightbox?le():l.decisionModal?o(u)&&u.closest?.(".dmodal")?(u.blur(),document.querySelector(".dmodal")?.focus()):ie():de();return}if(!(a.metaKey||a.ctrlKey||a.altKey||a.isComposing||a.keyCode===229||o(u)||l.lightbox)){if(l.decisionModal){a.key==="ArrowLeft"||a.key==="ArrowRight"?(a.preventDefault(),_e(a.key==="ArrowLeft"?-1:1)):/^[1-9]$/.test(a.key)?rn(Number(a.key))&&a.preventDefault():a.key==="Enter"&&u?.matches?.(".dmodal .dopt.on")&&l.decisionModal.key&&(a.preventDefault(),Ze(l.decisionModal.key));return}a.key==="d"&&!a.shiftKey&&!l.modal&&(a.preventDefault(),ae())}}),us(),Br(),g().route==="stats"&&Se()}})();</script>\n </body>\n</html>\n';
16095
16261
 
16096
16262
  // apps/cli/src/glass-snapshot-thread.ts
16097
16263
  init_dist2();
16098
- import * as fs69 from "node:fs";
16264
+ import * as fs70 from "node:fs";
16099
16265
  import { fileURLToPath as fileURLToPath2 } from "node:url";
16100
16266
  import { Worker } from "node:worker_threads";
16101
16267
  function workerEntry() {
16102
16268
  if (COMPILED_BINARY) return "./glass-snapshot-worker.ts";
16103
16269
  const url = new URL("./glass-snapshot-worker.js", import.meta.url);
16104
- return fs69.existsSync(fileURLToPath2(url)) ? url : void 0;
16270
+ return fs70.existsSync(fileURLToPath2(url)) ? url : void 0;
16105
16271
  }
16106
16272
  function startSnapshotThread() {
16107
16273
  const entry = workerEntry();
@@ -16288,7 +16454,7 @@ function pollBody(full, now2) {
16288
16454
 
16289
16455
  // apps/cli/src/glass-presence.ts
16290
16456
  init_dist2();
16291
- import * as fs70 from "node:fs";
16457
+ import * as fs71 from "node:fs";
16292
16458
  import * as http2 from "node:http";
16293
16459
  import * as path60 from "node:path";
16294
16460
  import { randomBytes as randomBytes5, timingSafeEqual as timingSafeEqual2 } from "node:crypto";
@@ -16371,8 +16537,8 @@ var SECRET_RE = /^[0-9a-f]{64}$/;
16371
16537
  function readShowSecret() {
16372
16538
  try {
16373
16539
  const file2 = showSecretPath();
16374
- if (process.platform !== "win32" && (fs70.statSync(file2).mode & 63) !== 0) return void 0;
16375
- const secret = fs70.readFileSync(file2, "utf8").trim();
16540
+ if (process.platform !== "win32" && (fs71.statSync(file2).mode & 63) !== 0) return void 0;
16541
+ const secret = fs71.readFileSync(file2, "utf8").trim();
16376
16542
  return SECRET_RE.test(secret) ? secret : void 0;
16377
16543
  } catch {
16378
16544
  return void 0;
@@ -16382,17 +16548,17 @@ function ensureShowSecret() {
16382
16548
  const existing = readShowSecret();
16383
16549
  if (existing) return existing;
16384
16550
  const file2 = showSecretPath();
16385
- fs70.mkdirSync(path60.dirname(file2), { recursive: true });
16551
+ fs71.mkdirSync(path60.dirname(file2), { recursive: true });
16386
16552
  const secret = randomBytes5(32).toString("hex");
16387
16553
  try {
16388
- fs70.writeFileSync(file2, secret, { mode: 384, flag: "wx" });
16554
+ fs71.writeFileSync(file2, secret, { mode: 384, flag: "wx" });
16389
16555
  } catch (e) {
16390
16556
  if (e.code !== "EEXIST") throw e;
16391
16557
  const raced = readShowSecret();
16392
16558
  if (raced) return raced;
16393
- fs70.writeFileSync(file2, secret, { mode: 384 });
16559
+ fs71.writeFileSync(file2, secret, { mode: 384 });
16394
16560
  }
16395
- fs70.chmodSync(file2, 384);
16561
+ fs71.chmodSync(file2, 384);
16396
16562
  return secret;
16397
16563
  }
16398
16564
  function showSecretMatches(supplied) {
@@ -16436,7 +16602,7 @@ function requestShow(port, hash, timeoutMs = 2e3) {
16436
16602
 
16437
16603
  // apps/cli/src/focus.ts
16438
16604
  init_dist2();
16439
- import * as fs71 from "node:fs";
16605
+ import * as fs72 from "node:fs";
16440
16606
  import * as path61 from "node:path";
16441
16607
  import { execFile as execFile2 } from "node:child_process";
16442
16608
  import { promisify as promisify2 } from "node:util";
@@ -16489,7 +16655,7 @@ function safeWorktree(reg) {
16489
16655
  const folder = reg.worktree;
16490
16656
  if (!path61.isAbsolute(folder) || /[\x00-\x1f\x7f]/.test(folder) || path61.normalize(folder) !== folder) return false;
16491
16657
  try {
16492
- return fs71.statSync(folder).isDirectory() && trapIdAt(folder) === reg.trapId;
16658
+ return fs72.statSync(folder).isDirectory() && trapIdAt(folder) === reg.trapId;
16493
16659
  } catch {
16494
16660
  return false;
16495
16661
  }
@@ -16574,25 +16740,26 @@ function requestBodyCap() {
16574
16740
  }
16575
16741
  var REQUEST_KIND_MAX_BYTES = /* @__PURE__ */ new Map([
16576
16742
  ["trap-request", 4096],
16577
- ["decision-viewed", 1024]
16743
+ ["decision-viewed", 1024],
16744
+ ["report-viewed", 1024]
16578
16745
  ]);
16579
16746
  var readJson4 = (f) => {
16580
16747
  try {
16581
- return JSON.parse(fs72.readFileSync(f, "utf8"));
16748
+ return JSON.parse(fs73.readFileSync(f, "utf8"));
16582
16749
  } catch {
16583
16750
  return void 0;
16584
16751
  }
16585
16752
  };
16586
16753
  var listDir = (d) => {
16587
16754
  try {
16588
- return fs72.readdirSync(d);
16755
+ return fs73.readdirSync(d);
16589
16756
  } catch {
16590
16757
  return [];
16591
16758
  }
16592
16759
  };
16593
16760
  var mtime = (f) => {
16594
16761
  try {
16595
- return fs72.statSync(f).mtimeMs;
16762
+ return fs73.statSync(f).mtimeMs;
16596
16763
  } catch {
16597
16764
  return 0;
16598
16765
  }
@@ -16601,7 +16768,7 @@ function assetPath(name) {
16601
16768
  for (const rel of [`../docs/assets/${name}`, `../../../docs/assets/${name}`]) {
16602
16769
  try {
16603
16770
  const p = fileURLToPath3(new URL(rel, import.meta.url));
16604
- if (fs72.existsSync(p)) return p;
16771
+ if (fs73.existsSync(p)) return p;
16605
16772
  } catch {
16606
16773
  }
16607
16774
  }
@@ -16610,7 +16777,7 @@ function assetPath(name) {
16610
16777
  function transcriptPath(harness, cwd, sessionId) {
16611
16778
  if (harness !== "claude" || !cwd || !sessionId) return void 0;
16612
16779
  const p = path62.join(os11.homedir(), ".claude", "projects", cwd.replace(/[^A-Za-z0-9-]/g, "-"), `${sessionId}.jsonl`);
16613
- return fs72.existsSync(p) ? p : void 0;
16780
+ return fs73.existsSync(p) ? p : void 0;
16614
16781
  }
16615
16782
  function messageAttachments(dir) {
16616
16783
  return [dir, path62.join(dir, "handled")].flatMap(
@@ -16622,7 +16789,7 @@ function trapMessages(trapId) {
16622
16789
  const parse2 = (file2, state) => {
16623
16790
  let raw;
16624
16791
  try {
16625
- raw = fs72.readFileSync(file2, "utf8");
16792
+ raw = fs73.readFileSync(file2, "utf8");
16626
16793
  } catch {
16627
16794
  return void 0;
16628
16795
  }
@@ -16712,7 +16879,7 @@ function dispatchRow(r, hold, staleSecs) {
16712
16879
  claimedBy: claim?.by,
16713
16880
  worker: evidence.worker,
16714
16881
  log,
16715
- inbox: listDir(inboxDir2).filter((f) => f.endsWith(".msg")).sort().map((f) => fs72.readFileSync(path62.join(inboxDir2, f), "utf8").trim()),
16882
+ inbox: listDir(inboxDir2).filter((f) => f.endsWith(".msg")).sort().map((f) => fs73.readFileSync(path62.join(inboxDir2, f), "utf8").trim()),
16716
16883
  ...awaitingOf(id),
16717
16884
  evidence: Object.keys(evidence).length > 0 ? evidence : void 0,
16718
16885
  ...r.bucket === "queued" ? {} : worktreeView(id, r.lane),
@@ -16805,7 +16972,7 @@ function serveReport(url, res) {
16805
16972
  const type = file2 && REPORT_IMAGE_TYPES[path62.extname(file2).toLowerCase()];
16806
16973
  if (!file2 || !type) return notFound(), true;
16807
16974
  res.writeHead(200, { ...headers, "content-type": type });
16808
- res.end(fs72.readFileSync(file2));
16975
+ res.end(fs73.readFileSync(file2));
16809
16976
  return true;
16810
16977
  }
16811
16978
  function trapNamesShown(traps, dispatches, attention, names) {
@@ -16848,7 +17015,7 @@ function serveAttachment(url, res) {
16848
17015
  const type = file2 && REPORT_IMAGE_TYPES[path62.extname(file2).toLowerCase()];
16849
17016
  let data;
16850
17017
  try {
16851
- if (file2 && type && fs72.lstatSync(file2, { throwIfNoEntry: false })?.isFile()) data = fs72.readFileSync(file2);
17018
+ if (file2 && type && fs73.lstatSync(file2, { throwIfNoEntry: false })?.isFile()) data = fs73.readFileSync(file2);
16852
17019
  } catch {
16853
17020
  }
16854
17021
  if (!data || !type) {
@@ -17119,6 +17286,16 @@ function serveGlass(port, options = {}) {
17119
17286
  return reply(500, { ok: false, reason: "The view could not be stored." });
17120
17287
  }
17121
17288
  }
17289
+ case "report-viewed": {
17290
+ const key = body.payload?.key;
17291
+ if (typeof key !== "string" || !key.startsWith("report:") || key.length > 200) return reply(400, { ok: false, reason: "A report key is required." });
17292
+ try {
17293
+ const viewed = viewReport(key);
17294
+ return viewed ? reply(200, { ok: true, ...viewed }) : reply(404, { ok: false, reason: `No report ${key}.` });
17295
+ } catch {
17296
+ return reply(500, { ok: false, reason: "The view could not be stored." });
17297
+ }
17298
+ }
17122
17299
  case "trap-request": {
17123
17300
  const error = trapRequestError(body.payload, Object.keys(loadConfig().repos));
17124
17301
  if (error) return reply(400, { ok: false, reason: `Invalid trap request: ${error}.` });
@@ -17215,17 +17392,17 @@ function serveGlass(port, options = {}) {
17215
17392
  }
17216
17393
  if (req.url === "/lob.png" && lob) {
17217
17394
  res.writeHead(200, { "content-type": "image/png", "cache-control": "no-cache" });
17218
- res.end(fs72.readFileSync(lob));
17395
+ res.end(fs73.readFileSync(lob));
17219
17396
  } else if (req.url === "/star.png" && star) {
17220
17397
  res.writeHead(200, { "content-type": "image/png", "cache-control": "no-cache" });
17221
- res.end(fs72.readFileSync(star));
17398
+ res.end(fs73.readFileSync(star));
17222
17399
  } else if (req.url === "/lob-sprite.png" && sprite) {
17223
17400
  res.writeHead(200, { "content-type": "image/png", "cache-control": "no-cache" });
17224
- res.end(fs72.readFileSync(sprite));
17401
+ res.end(fs73.readFileSync(sprite));
17225
17402
  } else if (req.url === "/icon.png") {
17226
17403
  if (icon) {
17227
17404
  res.writeHead(200, { "content-type": "image/png", "cache-control": "no-cache" });
17228
- res.end(fs72.readFileSync(icon));
17405
+ res.end(fs73.readFileSync(icon));
17229
17406
  } else {
17230
17407
  res.writeHead(200, { "content-type": "image/svg+xml", "cache-control": "no-cache" });
17231
17408
  res.end(fallbackIcon);
@@ -17245,7 +17422,12 @@ function serveGlass(port, options = {}) {
17245
17422
  }
17246
17423
  const row = key ? reportRow(key) : void 0;
17247
17424
  if (m[2] === "/meta") {
17248
- res.writeHead(row ? 200 : 404, { "content-type": "application/json", "cache-control": "no-store" });
17425
+ res.writeHead(row ? 200 : 404, {
17426
+ "content-type": "application/json",
17427
+ "cache-control": "no-store",
17428
+ // The page token, so the report page can record its view.
17429
+ ...row ? { "x-lobstah-token": focusToken } : {}
17430
+ });
17249
17431
  res.end(JSON.stringify(row ?? { error: "report not found" }));
17250
17432
  return;
17251
17433
  }
@@ -17349,12 +17531,12 @@ function serveGlass(port, options = {}) {
17349
17531
 
17350
17532
  // apps/cli/src/park-renewal.ts
17351
17533
  init_dist2();
17352
- import * as fs74 from "node:fs";
17534
+ import * as fs75 from "node:fs";
17353
17535
  import * as path63 from "node:path";
17354
17536
 
17355
17537
  // apps/cli/src/restart.ts
17356
17538
  init_dist2();
17357
- import * as fs73 from "node:fs";
17539
+ import * as fs74 from "node:fs";
17358
17540
  var RESTART_WAIT_MS = 1e4;
17359
17541
  var HEARTBEAT_STALE_MS3 = 9e4;
17360
17542
  var INSTALL_COMMAND = {
@@ -17378,7 +17560,7 @@ function restartRefusal(opts) {
17378
17560
  }
17379
17561
  function readHeartbeat() {
17380
17562
  try {
17381
- const hb = JSON.parse(fs73.readFileSync(executorPath(), "utf8"));
17563
+ const hb = JSON.parse(fs74.readFileSync(executorPath(), "utf8"));
17382
17564
  return typeof hb.heartbeat === "string" ? hb : void 0;
17383
17565
  } catch {
17384
17566
  return void 0;
@@ -17439,7 +17621,7 @@ var RENEW_REASON = "lobstah: this trap is still signed on and its park timed out
17439
17621
  var statePath = (trapId) => path63.join(lobstahHome(), "park-renewals", `${trapId}.json`);
17440
17622
  function readState(trapId) {
17441
17623
  try {
17442
- const s = JSON.parse(fs74.readFileSync(statePath(trapId), "utf8"));
17624
+ const s = JSON.parse(fs75.readFileSync(statePath(trapId), "utf8"));
17443
17625
  return { instant: Number.isFinite(s.instant) ? s.instant : 0, at: s.at };
17444
17626
  } catch {
17445
17627
  return { instant: 0, at: "" };
@@ -17447,11 +17629,11 @@ function readState(trapId) {
17447
17629
  }
17448
17630
  function writeState(trapId, state) {
17449
17631
  const file2 = statePath(trapId);
17450
- fs74.mkdirSync(path63.dirname(file2), { recursive: true });
17451
- fs74.writeFileSync(file2, JSON.stringify(state));
17632
+ fs75.mkdirSync(path63.dirname(file2), { recursive: true });
17633
+ fs75.writeFileSync(file2, JSON.stringify(state));
17452
17634
  }
17453
17635
  function clearParkRenewal(trapId) {
17454
- fs74.rmSync(statePath(trapId), { force: true });
17636
+ fs75.rmSync(statePath(trapId), { force: true });
17455
17637
  }
17456
17638
  function daemonReachable(now2 = Date.now()) {
17457
17639
  const at = Date.parse(readHeartbeat()?.heartbeat ?? "");
@@ -17489,7 +17671,7 @@ function parkRenewal(trapId, parkedMs, now2 = Date.now(), daemonUp = daemonReach
17489
17671
  // apps/cli/src/repos.ts
17490
17672
  init_dist();
17491
17673
  init_dist2();
17492
- import * as fs75 from "node:fs";
17674
+ import * as fs76 from "node:fs";
17493
17675
  import * as path64 from "node:path";
17494
17676
  import { spawnSync as spawnSync10 } from "node:child_process";
17495
17677
  function git9(dir, ...args) {
@@ -17510,27 +17692,27 @@ function detectRepo(dir) {
17510
17692
  if (git9(abs, "rev-parse", "--is-inside-work-tree") !== "true") return void 0;
17511
17693
  const top = git9(abs, "rev-parse", "--show-toplevel");
17512
17694
  const canon2 = (p) => {
17513
- const real = path64.resolve(fs75.realpathSync.native(p));
17695
+ const real = path64.resolve(fs76.realpathSync.native(p));
17514
17696
  return process.platform === "win32" ? real.toLowerCase() : real;
17515
17697
  };
17516
17698
  if (!top || canon2(top) !== canon2(abs)) return void 0;
17517
17699
  const origin = git9(abs, "remote", "get-url", "origin");
17518
17700
  const headRef = git9(abs, "symbolic-ref", "--short", "refs/remotes/origin/HEAD");
17519
17701
  const trunk = headRef?.replace(/^origin\//, "") ?? git9(abs, "branch", "--show-current") ?? "main";
17520
- const setup = LOCKFILE_SETUP.find(([f]) => fs75.existsSync(path64.join(abs, f)))?.[1];
17702
+ const setup = LOCKFILE_SETUP.find(([f]) => fs76.existsSync(path64.join(abs, f)))?.[1];
17521
17703
  return { key: path64.basename(abs), path: abs, trunk, origin, setup };
17522
17704
  }
17523
17705
  function scanForRepos(roots) {
17524
17706
  const found = [];
17525
17707
  for (const root of roots) {
17526
17708
  const abs = path64.resolve(root);
17527
- if (!fs75.existsSync(abs)) continue;
17709
+ if (!fs76.existsSync(abs)) continue;
17528
17710
  const self = detectRepo(abs);
17529
17711
  if (self) {
17530
17712
  found.push(self);
17531
17713
  continue;
17532
17714
  }
17533
- for (const entry of fs75.readdirSync(abs, { withFileTypes: true })) {
17715
+ for (const entry of fs76.readdirSync(abs, { withFileTypes: true })) {
17534
17716
  if (!entry.isDirectory() || entry.name.startsWith(".")) continue;
17535
17717
  const child = detectRepo(path64.join(abs, entry.name));
17536
17718
  if (child) found.push(child);
@@ -17539,8 +17721,8 @@ function scanForRepos(roots) {
17539
17721
  return found;
17540
17722
  }
17541
17723
  function configuredRepoKeys(file2 = configPath()) {
17542
- if (!fs75.existsSync(file2)) return /* @__PURE__ */ new Set();
17543
- const raw = parse(fs75.readFileSync(file2, "utf8"));
17724
+ if (!fs76.existsSync(file2)) return /* @__PURE__ */ new Set();
17725
+ const raw = parse(fs76.readFileSync(file2, "utf8"));
17544
17726
  return new Set(Object.keys(raw.repos ?? {}));
17545
17727
  }
17546
17728
  function appendRepoBlock(repo, opts = {}, file2 = configPath()) {
@@ -17548,19 +17730,19 @@ function appendRepoBlock(repo, opts = {}, file2 = configPath()) {
17548
17730
  if (repo.origin) lines.push(`origin = ${JSON.stringify(repo.origin)}`);
17549
17731
  if (repo.setup) lines.push(`setup = [${repo.setup.map((s) => JSON.stringify(s)).join(", ")}]`);
17550
17732
  if (opts.pickup) lines.push(`pickup = true`);
17551
- fs75.appendFileSync(file2, `${lines.join("\n")}
17733
+ fs76.appendFileSync(file2, `${lines.join("\n")}
17552
17734
  `);
17553
17735
  }
17554
17736
 
17555
17737
  // apps/cli/src/pr-watch.ts
17556
17738
  init_dist();
17557
17739
  init_dist2();
17558
- import * as fs77 from "node:fs";
17740
+ import * as fs78 from "node:fs";
17559
17741
 
17560
17742
  // apps/cli/src/pr-stack-watch.ts
17561
17743
  init_dist2();
17562
17744
  import { spawnSync as spawnSync11 } from "node:child_process";
17563
- import * as fs76 from "node:fs";
17745
+ import * as fs77 from "node:fs";
17564
17746
  import * as path65 from "node:path";
17565
17747
  var ghStackLink = (ref, branch, direction) => {
17566
17748
  const res = spawnSync11(
@@ -17590,7 +17772,7 @@ function discoverPrStack(ref, opts = {}) {
17590
17772
  const file2 = path65.join(lobstahHome(), "pr-stack-links.json");
17591
17773
  let checked;
17592
17774
  try {
17593
- checked = JSON.parse(fs76.readFileSync(file2, "utf8"));
17775
+ checked = JSON.parse(fs77.readFileSync(file2, "utf8"));
17594
17776
  } catch {
17595
17777
  checked = {};
17596
17778
  }
@@ -17627,15 +17809,15 @@ function discoverPrStack(ref, opts = {}) {
17627
17809
  }
17628
17810
  }
17629
17811
  const tmp = `${file2}.tmp-${process.pid}`;
17630
- fs76.writeFileSync(tmp, JSON.stringify(checked));
17631
- fs76.renameSync(tmp, file2);
17812
+ fs77.writeFileSync(tmp, JSON.stringify(checked));
17813
+ fs77.renameSync(tmp, file2);
17632
17814
  }
17633
17815
 
17634
17816
  // apps/cli/src/pr-watch.ts
17635
17817
  var shq = (s) => `'${s.replace(/'/g, `'\\''`)}'`;
17636
17818
  function selfCommand() {
17637
17819
  if (COMPILED_BINARY) return shq(process.execPath);
17638
- const entry = process.argv[1] ? fs77.realpathSync(process.argv[1]) : "lobstah";
17820
+ const entry = process.argv[1] ? fs78.realpathSync(process.argv[1]) : "lobstah";
17639
17821
  return `${shq(process.execPath)} ${shq(entry)}`;
17640
17822
  }
17641
17823
  function prCheckCommand(ref, forId) {
@@ -17685,7 +17867,7 @@ function backfillPrWatches(opts = {}) {
17685
17867
  for (const bucket of ["active", "done"]) {
17686
17868
  let ids;
17687
17869
  try {
17688
- ids = fs77.readdirSync(dirs[bucket]).filter((id) => !id.startsWith("."));
17870
+ ids = fs78.readdirSync(dirs[bucket]).filter((id) => !id.startsWith("."));
17689
17871
  } catch {
17690
17872
  continue;
17691
17873
  }
@@ -17697,7 +17879,7 @@ function backfillPrWatches(opts = {}) {
17697
17879
  }
17698
17880
  let files;
17699
17881
  try {
17700
- files = fs77.readdirSync(dirs.state).filter((f) => f.endsWith(".evidence"));
17882
+ files = fs78.readdirSync(dirs.state).filter((f) => f.endsWith(".evidence"));
17701
17883
  } catch {
17702
17884
  continue;
17703
17885
  }
@@ -17759,7 +17941,7 @@ function backfillPrWatches(opts = {}) {
17759
17941
  return rows;
17760
17942
  }
17761
17943
  function laneOf4(id) {
17762
- return ["work", "chore"].find((l) => fs77.existsSync(evidencePath(id, l)) || readStatusLog(id, l).length > 0);
17944
+ return ["work", "chore"].find((l) => fs78.existsSync(evidencePath(id, l)) || readStatusLog(id, l).length > 0);
17763
17945
  }
17764
17946
  var FIRST_SIGHT_NOTICE_MS = 24 * 60 * 60 * 1e3;
17765
17947
  function observePr(ref, view, opts = {}) {
@@ -17803,7 +17985,7 @@ function cancelQueuedRepairs(key) {
17803
17985
  }
17804
17986
  function rawConfig() {
17805
17987
  try {
17806
- return parse(fs77.readFileSync(configPath(), "utf8"));
17988
+ return parse(fs78.readFileSync(configPath(), "utf8"));
17807
17989
  } catch {
17808
17990
  return {};
17809
17991
  }
@@ -17978,7 +18160,7 @@ function finishResolvedWaits(log = () => {
17978
18160
  // apps/cli/src/pr-stack.ts
17979
18161
  init_dist2();
17980
18162
  import { spawnSync as spawnSync12 } from "node:child_process";
17981
- import * as fs78 from "node:fs";
18163
+ import * as fs79 from "node:fs";
17982
18164
  import * as path66 from "node:path";
17983
18165
  var defaultRun = (cmd, args, cwd) => {
17984
18166
  const res = spawnSync12(cmd, args, { cwd, encoding: "utf8", timeout: cmd === "gh" ? 1e4 : 3e3, windowsHide: true });
@@ -17991,7 +18173,7 @@ function ghStackPrs(cwd, keys, run4) {
17991
18173
  if (res.status !== 0 || !res.stdout.trim()) continue;
17992
18174
  let state;
17993
18175
  try {
17994
- state = JSON.parse(fs78.readFileSync(path66.join(path66.resolve(cwd, res.stdout.trim()), "gh-stack"), "utf8"));
18176
+ state = JSON.parse(fs79.readFileSync(path66.join(path66.resolve(cwd, res.stdout.trim()), "gh-stack"), "utf8"));
17995
18177
  } catch {
17996
18178
  continue;
17997
18179
  }
@@ -18352,7 +18534,7 @@ function batchText(results) {
18352
18534
  // apps/cli/src/soak-worktree.ts
18353
18535
  init_dist2();
18354
18536
  init_dist3();
18355
- import * as fs79 from "node:fs";
18537
+ import * as fs80 from "node:fs";
18356
18538
  import { spawnSync as spawnSync13 } from "node:child_process";
18357
18539
  function branchExists(repo, branch) {
18358
18540
  return spawnSync13("git", ["-C", repo.path, "rev-parse", "--verify", "-q", `refs/heads/${branch}`], { stdio: "ignore" }).status === 0;
@@ -18372,7 +18554,7 @@ async function createSoakWorktree(opts) {
18372
18554
  );
18373
18555
  }
18374
18556
  }
18375
- const taken = (id2) => branchExists(opts.repo, `lobstah/soak-${id2}`) || fs79.existsSync(worktreePath(`soak-${id2}`));
18557
+ const taken = (id2) => branchExists(opts.repo, `lobstah/soak-${id2}`) || fs80.existsSync(worktreePath(`soak-${id2}`));
18376
18558
  let trapId = opts.trapId ?? newTrapId();
18377
18559
  if (opts.trapId !== void 0 && taken(trapId)) {
18378
18560
  throw new Error(`could not create a worktree for repo ${opts.repoKey}: soak-${trapId} or its branch already exists`);
@@ -18397,7 +18579,7 @@ async function discardSoakWorktree(repo, made) {
18397
18579
  // apps/cli/src/forget.ts
18398
18580
  init_dist2();
18399
18581
  init_dist3();
18400
- import * as fs80 from "node:fs";
18582
+ import * as fs81 from "node:fs";
18401
18583
  import { spawnSync as spawnSync14 } from "node:child_process";
18402
18584
  var ForgetRefusedError = class extends Error {
18403
18585
  };
@@ -18418,7 +18600,7 @@ async function forgetTrap(address, cfg, opts = {}) {
18418
18600
  if (readReservation(entry.trapId)) throw new ForgetRefusedError(`${label} is starting (reserved) \u2014 withdraw it with \`lobstah stow --wt ${entry.name}\` first`);
18419
18601
  const repo = entry.repo ? cfg.repos[entry.repo] : void 0;
18420
18602
  const gitDir = entry.gitDir ?? (repo ? gitCommonDir(repo.path) : void 0);
18421
- const present = fs80.existsSync(entry.worktree);
18603
+ const present = fs81.existsSync(entry.worktree);
18422
18604
  const anchored = present && readTrapAnchor(entry.worktree)?.trapId === entry.trapId;
18423
18605
  const unsaved = anchored ? unsavedFiles(entry.worktree) : [];
18424
18606
  if (unsaved === void 0 && !opts.force) throw new ForgetRefusedError(`cannot read git status in ${entry.worktree} \u2014 inspect it, or pass --force`);
@@ -18465,7 +18647,7 @@ Push or merge it, or pass --force to forget the trap anyway.`);
18465
18647
  throw new Error(`could not remove ${entry.worktree}: ${removal.reason} \u2014 the trap is not forgotten`);
18466
18648
  }
18467
18649
  } else {
18468
- if (anchored) fs80.rmSync(`${entry.worktree}/${TRAP_ANCHOR_FILE}`, { force: true });
18650
+ if (anchored) fs81.rmSync(`${entry.worktree}/${TRAP_ANCHOR_FILE}`, { force: true });
18469
18651
  result.worktreeNote = "soak did not create it: left in place, its trap anchor removed";
18470
18652
  }
18471
18653
  }
@@ -18541,7 +18723,7 @@ async function setTerminalTitle(win, title, options = {}) {
18541
18723
 
18542
18724
  // apps/cli/src/beat.ts
18543
18725
  init_dist2();
18544
- import * as fs81 from "node:fs";
18726
+ import * as fs82 from "node:fs";
18545
18727
  import * as path67 from "node:path";
18546
18728
 
18547
18729
  // apps/cli/src/beat-pr.ts
@@ -18593,9 +18775,9 @@ function probeTrapPr(reg, opts = {}) {
18593
18775
  function logError(err) {
18594
18776
  try {
18595
18777
  const dir = path67.join(lobstahHome(), "logs");
18596
- fs81.mkdirSync(dir, { recursive: true });
18778
+ fs82.mkdirSync(dir, { recursive: true });
18597
18779
  const msg = err instanceof Error ? err.message : String(err);
18598
- fs81.appendFileSync(path67.join(dir, "beat.log"), `${(/* @__PURE__ */ new Date()).toISOString()} ${msg.replace(/\s+/g, " ").slice(0, 300)}
18780
+ fs82.appendFileSync(path67.join(dir, "beat.log"), `${(/* @__PURE__ */ new Date()).toISOString()} ${msg.replace(/\s+/g, " ").slice(0, 300)}
18599
18781
  `);
18600
18782
  } catch {
18601
18783
  }
@@ -19423,7 +19605,7 @@ function emitNotices(notices, sessionId) {
19423
19605
  for (const n of notices) {
19424
19606
  const answer = n.kind === "decision-answer" && n.refId ? readRequest(n.refId) : void 0;
19425
19607
  if (answer) console.log(toonKV(decisionEventFields(answer)));
19426
- else console.log(toonKV(trapRequestFields(n) ?? { notice: n.kind, ...n.refId ? { ref: n.refId } : {}, text: n.text }));
19608
+ else console.log(toonKV(trapRequestFields(n) ?? { notice: n.kind, ...n.refId ? { ref: n.refId } : {}, ...n.url ? { url: n.url } : {}, text: n.text }));
19427
19609
  }
19428
19610
  console.log(
19429
19611
  `next: each notice names its own decision or remedy \u2014 act on it (or note it and move on), then re-arm a background \`lobstah man wait${sessionId ? ` --session ${sessionId}` : ""}\`. \`lobstah man tend\` keeps the recent tail visible either way.`
@@ -19441,7 +19623,7 @@ function noticeLine(n, prefix = "notice ") {
19441
19623
  const answer = n.kind === "decision-answer" && n.refId ? readRequest(n.refId) : void 0;
19442
19624
  if (answer) return decisionLine(answer);
19443
19625
  const r = trapRequestFields(n);
19444
- return r ? `- trap-request ${r.id} \u2014 repo ${r.repo}, harness ${r.harness}: start it with \`lobstah man throw --new --request ${r.id}\`` : `- ${prefix}${n.kind}${n.refId ? ` ${n.refId}` : ""} \u2014 ${n.text}`;
19626
+ return r ? `- trap-request ${r.id} \u2014 repo ${r.repo}, harness ${r.harness}: start it with \`lobstah man throw --new --request ${r.id}\`` : `- ${prefix}${n.kind}${n.refId ? ` ${n.refId}` : ""} \u2014 ${n.text}${n.url ? ` ${n.url}` : ""}`;
19445
19627
  }
19446
19628
  function replyLine(r) {
19447
19629
  return `- reply ${r.id} (${r.entry.verb})${r.entry.note ? ` \u2014 ${r.entry.note}` : ""} \xB7 sent: ${r.sent}`;
@@ -19463,7 +19645,7 @@ function emitWatchAttention(attns, sessionId) {
19463
19645
  function findLane(id) {
19464
19646
  for (const lane of ["work", "chore"]) {
19465
19647
  const d = laneDirs(lane);
19466
- if (fs86.existsSync(path72.join(d.active, id)) || fs86.existsSync(path72.join(d.queue, `${id}.json`)) || fs86.existsSync(path72.join(d.done, id)) || fs86.existsSync(path72.join(d.state, `${id}.status`))) {
19648
+ if (fs87.existsSync(path72.join(d.active, id)) || fs87.existsSync(path72.join(d.queue, `${id}.json`)) || fs87.existsSync(path72.join(d.done, id)) || fs87.existsSync(path72.join(d.state, `${id}.status`))) {
19467
19649
  return lane;
19468
19650
  }
19469
19651
  }
@@ -19472,7 +19654,7 @@ function findLane(id) {
19472
19654
  function anythingInFlight() {
19473
19655
  const dispatches = ["work", "chore"].some((lane) => {
19474
19656
  const dirs = laneDirs(lane);
19475
- return [dirs.queue, dirs.active].some((dir) => fs86.readdirSync(dir).some((file2) => !file2.startsWith(".")));
19657
+ return [dirs.queue, dirs.active].some((dir) => fs87.readdirSync(dir).some((file2) => !file2.startsWith(".")));
19476
19658
  });
19477
19659
  return dispatches || listWatches().some((watch2) => watch2.owner === "man");
19478
19660
  }
@@ -19489,7 +19671,7 @@ function dispatchChain(target) {
19489
19671
  for (const lane of ["work", "chore"]) {
19490
19672
  const dirs = laneDirs(lane);
19491
19673
  for (const bucket of ["queue", "active", "done"]) {
19492
- for (const entry of fs86.readdirSync(dirs[bucket])) {
19674
+ for (const entry of fs87.readdirSync(dirs[bucket])) {
19493
19675
  if (entry.startsWith(".")) continue;
19494
19676
  const id = bucket === "queue" ? entry.replace(/\.json$/, "") : entry;
19495
19677
  const descriptor = storedDescriptor(id, lane);
@@ -19519,7 +19701,7 @@ function liveChainMember(member) {
19519
19701
  }
19520
19702
  if (isParked(member.id, member.lane)) return true;
19521
19703
  try {
19522
- const runner = JSON.parse(fs86.readFileSync(path72.join(dir, "runner.json"), "utf8"));
19704
+ const runner = JSON.parse(fs87.readFileSync(path72.join(dir, "runner.json"), "utf8"));
19523
19705
  return pidAlive2(runner.pid, runner.processStartTime);
19524
19706
  } catch {
19525
19707
  return false;
@@ -19528,7 +19710,7 @@ function liveChainMember(member) {
19528
19710
  function rowsFor(lane, bucket) {
19529
19711
  const dir = laneDirs(lane)[bucket];
19530
19712
  const staleSecs = wedgeSecs2();
19531
- const entries = fs86.readdirSync(dir).filter((f) => !f.startsWith(".")).map((f) => ({ f, m: fs86.statSync(path72.join(dir, f)).mtimeMs })).sort((a, b) => b.m - a.m);
19713
+ const entries = fs87.readdirSync(dir).filter((f) => !f.startsWith(".")).map((f) => ({ f, m: fs87.statSync(path72.join(dir, f)).mtimeMs })).sort((a, b) => b.m - a.m);
19532
19714
  const sliced = bucket === "done" ? entries.slice(0, 10) : entries;
19533
19715
  return sliced.map(({ f, m }) => {
19534
19716
  const id = f.replace(/\.json$/, "");
@@ -19553,7 +19735,7 @@ function rowsFor(lane, bucket) {
19553
19735
  function dispatchTrap(id, lane, names) {
19554
19736
  const claimedBy = (bucket) => {
19555
19737
  try {
19556
- return JSON.parse(fs86.readFileSync(path72.join(laneDirs(lane)[bucket], id, "claim.json"), "utf8")).by;
19738
+ return JSON.parse(fs87.readFileSync(path72.join(laneDirs(lane)[bucket], id, "claim.json"), "utf8")).by;
19557
19739
  } catch {
19558
19740
  return void 0;
19559
19741
  }
@@ -19705,7 +19887,7 @@ ${usageFor(cmd)}`);
19705
19887
  const d = {
19706
19888
  id: opt("--id") ?? randomUUID8(),
19707
19889
  repo,
19708
- brief: briefText ?? fs86.readFileSync(briefFile, "utf8"),
19890
+ brief: briefText ?? fs87.readFileSync(briefFile, "utf8"),
19709
19891
  harness: opt("--harness"),
19710
19892
  model: opt("--model"),
19711
19893
  effort: opt("--effort"),
@@ -19762,7 +19944,7 @@ ${usageFor(cmd)}`);
19762
19944
  const since = queuedAt(id, lane);
19763
19945
  const claimedAt = claim?.at;
19764
19946
  const state = displayState({ log, lastEventAt: lastEventAt(id, lane), queued: since !== void 0, claimedAt });
19765
- const live = fs86.existsSync(path72.join(laneDirs(lane).active, id));
19947
+ const live = fs87.existsSync(path72.join(laneDirs(lane).active, id));
19766
19948
  const activity = live ? activityView(readActivity(id, lane), wedgeSecs2()) : void 0;
19767
19949
  const waitingNow = live ? waitingView(log.at(-1)) : void 0;
19768
19950
  console.log(
@@ -19802,8 +19984,8 @@ ${usageFor(cmd)}`);
19802
19984
  if (!id) throw new Error("logs requires a dispatch id");
19803
19985
  const lane = findLane(id);
19804
19986
  const file2 = eventsPath(id, lane);
19805
- if (fs86.existsSync(file2)) {
19806
- const raw = fs86.readFileSync(file2, "utf8");
19987
+ if (fs87.existsSync(file2)) {
19988
+ const raw = fs87.readFileSync(file2, "utf8");
19807
19989
  const lines = raw.split("\n").filter((l) => l.length > 0);
19808
19990
  const LIMIT = 50;
19809
19991
  if (!has("--full") && lines.length > LIMIT) {
@@ -19814,15 +19996,15 @@ ${usageFor(cmd)}`);
19814
19996
  }
19815
19997
  }
19816
19998
  if (has("--follow")) {
19817
- let size = fs86.existsSync(file2) ? fs86.statSync(file2).size : 0;
19999
+ let size = fs87.existsSync(file2) ? fs87.statSync(file2).size : 0;
19818
20000
  setInterval(() => {
19819
- if (!fs86.existsSync(file2)) return;
19820
- const now2 = fs86.statSync(file2).size;
20001
+ if (!fs87.existsSync(file2)) return;
20002
+ const now2 = fs87.statSync(file2).size;
19821
20003
  if (now2 > size) {
19822
- const fd = fs86.openSync(file2, "r");
20004
+ const fd = fs87.openSync(file2, "r");
19823
20005
  const buf = Buffer.alloc(now2 - size);
19824
- fs86.readSync(fd, buf, 0, buf.length, size);
19825
- fs86.closeSync(fd);
20006
+ fs87.readSync(fd, buf, 0, buf.length, size);
20007
+ fs87.closeSync(fd);
19826
20008
  process.stdout.write(buf.toString("utf8"));
19827
20009
  size = now2;
19828
20010
  }
@@ -19920,7 +20102,7 @@ ${[text, block].filter(Boolean).join("\n\n")}`,
19920
20102
  const claimFile = path72.join(laneDirs(origin.lane)[origin.bucket], origin.id, "claim.json");
19921
20103
  let claimedBy;
19922
20104
  try {
19923
- claimedBy = JSON.parse(fs86.readFileSync(claimFile, "utf8")).by;
20105
+ claimedBy = JSON.parse(fs87.readFileSync(claimFile, "utf8")).by;
19924
20106
  } catch {
19925
20107
  claimedBy = readEvidence(origin.id, origin.lane).deliveredTo;
19926
20108
  }
@@ -20004,7 +20186,7 @@ ${usageFor("report")}`);
20004
20186
  if (verb === "done") {
20005
20187
  const claim = readSessionClaim(id, lane);
20006
20188
  let ownCheckout = false;
20007
- if (claim?.by.startsWith("wt:") && claim.worktree && fs86.existsSync(claim.worktree)) {
20189
+ if (claim?.by.startsWith("wt:") && claim.worktree && fs87.existsSync(claim.worktree)) {
20008
20190
  const rel = path72.relative(canon(claim.worktree), canon(process.cwd()));
20009
20191
  const inside = rel === "" || !rel.startsWith("..") && !path72.isAbsolute(rel);
20010
20192
  const who = callerSession(opt("--session"))?.id;
@@ -20026,7 +20208,7 @@ ${usageFor("report")}`);
20026
20208
  const branches = [.../* @__PURE__ */ new Set([ev.branch, ...(ev.pushes ?? []).map((p) => p.branch)])].filter(
20027
20209
  (b) => !!b && b !== "HEAD"
20028
20210
  );
20029
- reportPrs = stackPrUrls(reportedPrs, { cwd: worktree && fs86.existsSync(worktree) ? worktree : process.cwd(), branches });
20211
+ reportPrs = stackPrUrls(reportedPrs, { cwd: worktree && fs87.existsSync(worktree) ? worktree : process.cwd(), branches });
20030
20212
  const all = dispatchPrUrls({ prUrl, prUrls: [...reportPrs, ...dispatchPrUrls(ev)] });
20031
20213
  mergeEvidence(id, lane, { prUrl, ...all.length > 1 ? { prUrls: all } : {} });
20032
20214
  }
@@ -20092,7 +20274,7 @@ ${usageFor("report")}`);
20092
20274
  );
20093
20275
  }
20094
20276
  const worktree = dispatchWorktree(id, lane).path;
20095
- const cwd = fs86.existsSync(worktree) ? worktree : process.cwd();
20277
+ const cwd = fs87.existsSync(worktree) ? worktree : process.cwd();
20096
20278
  const invocation = harness === "codex" ? codexInvocation(["resume", sessionId]) : { file: "claude", argv: ["--resume", sessionId] };
20097
20279
  if (!invocation) throw new Error("no codex CLI found \u2014 neither on PATH nor vendored by @openai/codex-sdk");
20098
20280
  if (has("--print")) {
@@ -20111,9 +20293,9 @@ ${usageFor("report")}`);
20111
20293
  }
20112
20294
  const lane = findLane(id);
20113
20295
  const activeDir = path72.join(laneDirs(lane).active, id);
20114
- if (!fs86.existsSync(activeDir)) throw new Error(`${id} is not active \u2014 swap only applies to in-flight dispatches`);
20296
+ if (!fs87.existsSync(activeDir)) throw new Error(`${id} is not active \u2014 swap only applies to in-flight dispatches`);
20115
20297
  const descFile = path72.join(activeDir, "descriptor.json");
20116
- const descriptor = JSON.parse(fs86.readFileSync(descFile, "utf8"));
20298
+ const descriptor = JSON.parse(fs87.readFileSync(descFile, "utf8"));
20117
20299
  const fromHarness = resolveSessionHarness(id, loadConfig(), lane).harness ?? descriptor.harness ?? "default";
20118
20300
  for (const key of ["harness", "model", "effort"]) {
20119
20301
  const v = opt(`--${key}`);
@@ -20121,14 +20303,14 @@ ${usageFor("report")}`);
20121
20303
  }
20122
20304
  if (opt("--harness")) descriptor.harnessExplicit = true;
20123
20305
  if (opt("--model")) descriptor.modelExplicit = true;
20124
- fs86.writeFileSync(descFile, JSON.stringify(descriptor, null, 2));
20306
+ fs87.writeFileSync(descFile, JSON.stringify(descriptor, null, 2));
20125
20307
  const worktree = dispatchWorktree(id, lane).path;
20126
- fs86.writeFileSync(path72.join(activeDir, "handoff"), handoffNote(fromHarness, worktreeProgress(worktree)));
20308
+ fs87.writeFileSync(path72.join(activeDir, "handoff"), handoffNote(fromHarness, worktreeProgress(worktree)));
20127
20309
  const runnerFile = path72.join(activeDir, "runner.json");
20128
- if (fs86.existsSync(runnerFile)) {
20129
- const runner = JSON.parse(fs86.readFileSync(runnerFile, "utf8"));
20310
+ if (fs87.existsSync(runnerFile)) {
20311
+ const runner = JSON.parse(fs87.readFileSync(runnerFile, "utf8"));
20130
20312
  if (pidAlive2(runner.pid, runner.processStartTime)) killGroup(runner.pid, "SIGKILL");
20131
- fs86.unlinkSync(runnerFile);
20313
+ fs87.unlinkSync(runnerFile);
20132
20314
  }
20133
20315
  appendStatus(id, lane, "working", `swapped to ${descriptor.harness ?? "default"} \u2014 awaiting respawn`);
20134
20316
  console.log(toonKV({ id, harness: descriptor.harness ?? "default", model: descriptor.model, swapped: true }));
@@ -20189,7 +20371,7 @@ ${usageFor("report")}`);
20189
20371
  console.log(`commits[${ev.commits.length}]:`);
20190
20372
  for (const c of ev.commits) console.log(` ${c}`);
20191
20373
  }
20192
- if (fs86.existsSync(path72.join(laneDirs(lane).done, id))) {
20374
+ if (fs87.existsSync(path72.join(laneDirs(lane).done, id))) {
20193
20375
  console.log(toonHelp([`lobstah send ${id} "<instruction>" (wakes it as a follow-up in its worktree)`]));
20194
20376
  }
20195
20377
  const attachments = storedDescriptor(id, lane)?.attachments ?? [];
@@ -20729,7 +20911,7 @@ ${errs.map((e) => `- ${e}`).join("\n")}`);
20729
20911
  const keys = holdCancelledRepair(id);
20730
20912
  return keys.length > 0 ? { repairHeld: `${keys.join(", ")} (lobstah watch release <key> frees it)` } : {};
20731
20913
  };
20732
- if (fs86.existsSync(path72.join(laneDirs(lane).active, id))) {
20914
+ if (fs87.existsSync(path72.join(laneDirs(lane).active, id))) {
20733
20915
  requestCancel(id, lane);
20734
20916
  console.log(toonKV({ id, cancel: "requested", note: "the claimant (daemon or trap) winds it down at its next check", ...repairHeld() }));
20735
20917
  break;
@@ -20738,7 +20920,7 @@ ${errs.map((e) => `- ${e}`).join("\n")}`);
20738
20920
  console.log(toonKV({ id, cancel: "finalized", note: "cancelled before claim \u2014 recorded as failed in done/", ...repairHeld() }));
20739
20921
  break;
20740
20922
  }
20741
- if (fs86.existsSync(path72.join(laneDirs(lane).active, id))) {
20923
+ if (fs87.existsSync(path72.join(laneDirs(lane).active, id))) {
20742
20924
  requestCancel(id, lane);
20743
20925
  console.log(toonKV({ id, cancel: "requested", note: "claimed while cancelling \u2014 the claimant winds it down", ...repairHeld() }));
20744
20926
  break;
@@ -20865,17 +21047,17 @@ ${usageFor("man:wait")}`);
20865
21047
  const file2 = has("--global") ? path72.join(os13.homedir(), ".claude", "settings.json") : path72.join(".claude", has("--shared") ? "settings.json" : "settings.local.json");
20866
21048
  let existing;
20867
21049
  try {
20868
- existing = JSON.parse(fs86.readFileSync(file2, "utf8"));
21050
+ existing = JSON.parse(fs87.readFileSync(file2, "utf8"));
20869
21051
  } catch {
20870
21052
  existing = void 0;
20871
21053
  }
20872
21054
  const { settings, changed } = mergeHaulHook(existing);
20873
21055
  if (changed) {
20874
- fs86.mkdirSync(path72.dirname(file2), { recursive: true });
20875
- fs86.writeFileSync(file2, `${JSON.stringify(settings, null, 2)}
21056
+ fs87.mkdirSync(path72.dirname(file2), { recursive: true });
21057
+ fs87.writeFileSync(file2, `${JSON.stringify(settings, null, 2)}
20876
21058
  `);
20877
21059
  }
20878
- if (has("--marker")) fs86.writeFileSync(".lobstah-man", "");
21060
+ if (has("--marker")) fs87.writeFileSync(".lobstah-man", "");
20879
21061
  console.log(
20880
21062
  toonKV({
20881
21063
  hook: "lobstah man haul",
@@ -20933,7 +21115,7 @@ ${usageFor("man:wait")}`);
20933
21115
  if (helm) releaseHeldQuestions(helm);
20934
21116
  const cfgHaul = loadConfig();
20935
21117
  if (!helm && liveHelms(cfgHaul.helm.ttlSecs * 1e3).length > 0) break;
20936
- if (!helm && process.env.LOBSTAH_MAN !== "1" && !fs86.existsSync(".lobstah-man")) break;
21118
+ if (!helm && process.env.LOBSTAH_MAN !== "1" && !fs87.existsSync(".lobstah-man")) break;
20937
21119
  const dueDigest = () => helm ? dueHelmDigest(helm, cfgHaul.helm.reportSecs) : void 0;
20938
21120
  const blockDigest = (d) => {
20939
21121
  advanceCursor(helm.grounds, d.now);
@@ -21143,7 +21325,7 @@ ${behind}` : "");
21143
21325
  }
21144
21326
  repoKey2 = reservation.repo;
21145
21327
  if (reservation.worktree !== void 0) {
21146
- if (!fs86.existsSync(reservation.worktree) || trapIdAt(reservation.worktree) !== reservation.trapId) {
21328
+ if (!fs87.existsSync(reservation.worktree) || trapIdAt(reservation.worktree) !== reservation.trapId) {
21147
21329
  throw new Error(`the thrown trap ${trapLabel(reservation)} has no checkout anchoring it at ${reservation.worktree} \u2014 withdraw it with \`lobstah stow --wt ${reservation.name}\` and throw it again`);
21148
21330
  }
21149
21331
  worktree = canon(reservation.worktree);
@@ -21182,7 +21364,7 @@ ${behind}` : "");
21182
21364
  `this session already mans trap wt:${mine.trapId} (repo ${mine.repo ?? "none"}) at ${mine.worktree}. Sign it off with \`lobstah stow\` before you soak for repo ${repoFlag}.`
21183
21365
  );
21184
21366
  }
21185
- if (!fs86.existsSync(mine.worktree)) {
21367
+ if (!fs87.existsSync(mine.worktree)) {
21186
21368
  throw new Error(
21187
21369
  `this session's trap wt:${mine.trapId} is anchored at ${mine.worktree}, which no longer exists. Run \`lobstah stow\`, then soak again.`
21188
21370
  );
@@ -21366,7 +21548,7 @@ ${usageFor("stow")}`);
21366
21548
  const anchor = wtDir !== void 0 ? readTrapAnchor(wtDir) : void 0;
21367
21549
  const created = reg?.createdWorktree === true || anchor?.createdBy === "soak";
21368
21550
  let worktreeOut = {};
21369
- if (wtDir !== void 0 && fs86.existsSync(wtDir)) {
21551
+ if (wtDir !== void 0 && fs87.existsSync(wtDir)) {
21370
21552
  const keepReason = !has("--remove") ? "stow keeps the worktree; --remove removes it" : sessionEnd ? "the session ended; the SessionEnd hook keeps the worktree" : !created ? "soak did not create this worktree; stow leaves it in place" : void 0;
21371
21553
  if (keepReason) {
21372
21554
  worktreeOut = { worktree: "kept", path: wtDir, reason: keepReason };
@@ -21613,7 +21795,7 @@ ${usageFor("pet")}`);
21613
21795
  path: r.path,
21614
21796
  trunk: r.trunk,
21615
21797
  pickup: r.pickup ?? false,
21616
- exists: fs86.existsSync(r.path)
21798
+ exists: fs87.existsSync(r.path)
21617
21799
  }));
21618
21800
  console.log(toonTable("repos", rows, ["key", "path", "trunk", "pickup", "exists"]));
21619
21801
  break;
@@ -21731,7 +21913,7 @@ ${usageFor("pet")}`);
21731
21913
  break;
21732
21914
  }
21733
21915
  case "init": {
21734
- if (!fs86.existsSync(configPath())) {
21916
+ if (!fs87.existsSync(configPath())) {
21735
21917
  const exampleRepo = has("--scan") ? "" : `[repos.example]
21736
21918
  path = "~/src/example"
21737
21919
  trunk = "main"
@@ -21740,7 +21922,7 @@ trunk = "main"
21740
21922
  # pickup = true # opt into [pickup.github] multi-repo tracker pickup
21741
21923
 
21742
21924
  `;
21743
- fs86.writeFileSync(
21925
+ fs87.writeFileSync(
21744
21926
  configPath(),
21745
21927
  `# lobstah workspace definitions \u2014 the descriptor's repo key resolves here.
21746
21928
  # Top-level keys must come before any [section].
@@ -21778,7 +21960,7 @@ wallClockSecs = 3600
21778
21960
  let reference = "https://github.com/aequitas-labs/lobstah/blob/main/docs/configuration.md";
21779
21961
  try {
21780
21962
  const local = fileURLToPath6(new URL("../docs/configuration.md", import.meta.url));
21781
- if (fs86.existsSync(local)) reference = local;
21963
+ if (fs87.existsSync(local)) reference = local;
21782
21964
  } catch {
21783
21965
  }
21784
21966
  console.log(