lobstah 0.7.0 → 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";
@@ -11363,16 +11405,23 @@ function rebaseBrief(pr, id, trunk) {
11363
11405
  conflictUpdate(pr.baseRef, pr.headRef, standalone),
11364
11406
  pushRule(pr.headRef, id, standalone ? "merge" : "rebase"),
11365
11407
  `Do not merge the PR. Do not change anything beyond conflict resolution.`,
11366
- // A reviewed PR's review goes stale at the new head: ask its reviewers again and park.
11367
- pr.reviews.length > 0 ? rerequestReview({ url: pr.url, number: pr.number }, id, [...new Set(pr.reviews.map((r) => r.author))]) : `When pushed, report status done.`
11408
+ `When pushed, report status done.`
11368
11409
  ].join(" ");
11369
11410
  }
11370
- function choreVerdict(uuid) {
11411
+ function choreVerdict(uuid, pr) {
11412
+ const last = readStatusLog(uuid, "chore").at(-1);
11413
+ const pausedOnReview = last?.verb === "paused" && last.waitingOn === "review";
11414
+ if (last?.verb === "done" || pausedOnReview) {
11415
+ const startedHead = storedDescriptor(uuid, "chore")?.pr?.headSha;
11416
+ const unchangedConflict = pr.mergeableState === "dirty" && startedHead === pr.headSha;
11417
+ return { state: unchangedConflict ? "failed" : "done", pausedOnReview };
11418
+ }
11419
+ if (last?.verb === "paused")
11420
+ return { state: "active" };
11371
11421
  const d = laneDirs("chore");
11372
11422
  if (fs46.existsSync(path41.join(d.active, uuid)) || fs46.existsSync(path41.join(d.queue, `${uuid}.json`)))
11373
- return "active";
11374
- const last = readStatusLog(uuid, "chore").at(-1)?.verb;
11375
- return last === "done" ? "done" : "failed";
11423
+ return { state: "active" };
11424
+ return { state: "failed" };
11376
11425
  }
11377
11426
  async function mergeLoop(ms, policy, state, log = () => {
11378
11427
  }) {
@@ -11416,12 +11465,12 @@ async function mergeLoop(ms, policy, state, log = () => {
11416
11465
  observe(candidate, "rebase-failed");
11417
11466
  continue;
11418
11467
  }
11419
- const verdict = choreVerdict(rebase.uuid);
11420
- if (verdict === "active") {
11468
+ const verdict = choreVerdict(rebase.uuid, candidate);
11469
+ if (verdict.state === "active") {
11421
11470
  observe(candidate, `conflict-chore:${rebase.uuid}`);
11422
11471
  continue;
11423
11472
  }
11424
- if (verdict === "failed") {
11473
+ if (verdict.state === "failed") {
11425
11474
  await ms.comment(candidate.number, `Automated rebase failed (dispatch ${rebase.uuid}) \u2014 needs a human.`);
11426
11475
  await ms.addLabel(candidate.number, "needs-human");
11427
11476
  state.failRebase(prKey);
@@ -11429,6 +11478,8 @@ async function mergeLoop(ms, policy, state, log = () => {
11429
11478
  observe(candidate, "rebase-failed");
11430
11479
  continue;
11431
11480
  }
11481
+ if (verdict.pausedOnReview)
11482
+ log(`${prKey}: rebase chore ${rebase.uuid} paused on review \u2014 treating as finished; the merge gate owns the review wait`);
11432
11483
  state.clearRebase(prKey);
11433
11484
  }
11434
11485
  const provisional = qualifiedApproval(policy, candidate);
@@ -11950,7 +12001,10 @@ function pruneStaleAcks(standing) {
11950
12001
  return pruned;
11951
12002
  }
11952
12003
  function ackItemExists(key, culling = /* @__PURE__ */ new Set()) {
11953
- 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
+ }
11954
12008
  if (key.startsWith("watch:")) return readWatch(key.slice("watch:".length)) !== void 0;
11955
12009
  if (key.startsWith("decision:")) return readDecision(key) !== void 0 && readDecisionAnswer(key) === void 0;
11956
12010
  if (key.startsWith("report:")) {
@@ -12047,6 +12101,15 @@ function reportAck(r) {
12047
12101
  const ack = currentAck(r.key, r.stateHash);
12048
12102
  return ack ? { at: ack.at, by: ack.by } : void 0;
12049
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
+ }
12050
12113
  function reportRows() {
12051
12114
  return listReports().map((r) => ({
12052
12115
  key: r.key,
@@ -12322,7 +12385,7 @@ function deriveGlassPrs(dispatches, watches = [], records = [], stackOptions = {
12322
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)) {
12323
12386
  evidenceByUrl.set(d.pr.url, d.pr);
12324
12387
  }
12325
- const readiness = derivePrStacks([...evidenceByUrl.values()], stackOptions);
12388
+ const readiness = identifyPrStacks(derivePrStacks([...evidenceByUrl.values()], stackOptions));
12326
12389
  for (const [id, members] of groupedStacks) {
12327
12390
  const root = members.find((p) => p.key === id);
12328
12391
  const ordered = [];
@@ -12355,6 +12418,8 @@ function deriveGlassPrs(dispatches, watches = [], records = [], stackOptions = {
12355
12418
  });
12356
12419
  const status = readiness.find((s) => s.members.some((p) => members.some((m) => p.url === m.url)));
12357
12420
  if (status) stacks.at(-1).readiness = {
12421
+ id: status.id,
12422
+ url: status.members.at(-1).url,
12358
12423
  ready: status.ready,
12359
12424
  total: status.members.length,
12360
12425
  allReady: status.allReady,
@@ -12714,6 +12779,15 @@ function progressStreak(pr) {
12714
12779
  if (!own && s.startHead !== void 0 && s.startHead !== pr.headSha) return { count: 0 };
12715
12780
  return { count: s.count + 1 };
12716
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
+ }
12717
12791
  function stackedAbove(pr, records, workers) {
12718
12792
  const out = [];
12719
12793
  for (const p of prsAbove(records, pr.key)) {
@@ -12750,8 +12824,9 @@ function deliverPrRepairs(log, cap = 3, opts = {}) {
12750
12824
  }
12751
12825
  const watch2 = w;
12752
12826
  withPrLock(watch2.key, () => {
12753
- const pr = readPr(watch2.key);
12827
+ let pr = readPr(watch2.key);
12754
12828
  if (!pr || pr.state !== "OPEN" || pr.dispatches.length === 0 || (pr.observations ?? 0) <= 1) return;
12829
+ pr = completedRepair(pr, now2);
12755
12830
  const owner = watch2.owner.slice("dispatch:".length);
12756
12831
  const origin = storedDescriptor(owner, "work")?.followUp;
12757
12832
  const ancestorPr = origin && chainPr(origin, "work");
@@ -12897,9 +12972,16 @@ function deliverPrRepairs(log, cap = 3, opts = {}) {
12897
12972
  log(`repair ${watch2.key}: stopped \u2014 ${reason}`);
12898
12973
  return;
12899
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
+ }
12900
12981
  const id = randomUUID6();
12901
12982
  writePr({
12902
12983
  ...pr,
12984
+ repairCooldown: { dispatchId: id },
12903
12985
  repairStreak: { count: streak.count, lastRepairId: id, startHead: pr.headSha },
12904
12986
  repair: {
12905
12987
  headSha: pr.headSha,
@@ -13136,36 +13218,12 @@ function prAttention(now2, observed2 = observedPrs(), cfg = loadConfig()) {
13136
13218
  Object.values(readPickupMap()).filter((e) => e.kind === "review").map((e) => e.uuid)
13137
13219
  );
13138
13220
  const out = [];
13139
- const stacks = derivePrStacks(observed2.map((x) => x.pr), {
13221
+ const stacks = identifyPrStacks(derivePrStacks(observed2.map((x) => x.pr), {
13140
13222
  trunk: (repo) => prStackTrunk(cfg, repo),
13141
13223
  readySettleSecs: cfg.readySettleSecs,
13142
13224
  now: now2
13143
- });
13225
+ }));
13144
13226
  const epochs = readStackEpochs();
13145
- const quietReady = /* @__PURE__ */ new Set([
13146
- ...stacks.flatMap((s) => s.members.map((p) => p.url)),
13147
- ...quietStackTailUrls(observed2.map((x) => x.pr), epochs)
13148
- ]);
13149
- for (const s of stacks.filter((s2) => s2.allReady)) {
13150
- const top = s.members.at(-1), ref = parsePrRef(top.url);
13151
- const epoch = epochs[s.id];
13152
- const at = epoch?.ready ? epoch.at : new Date(now2).toISOString();
13153
- const source = observed2.find((x) => x.pr.url === top.url);
13154
- out.push({
13155
- kind: "stack-ready",
13156
- key: `stack:${s.id}`,
13157
- stateHash: stackStateHash(s, epoch?.epoch),
13158
- id: source.id,
13159
- lane: source.lane,
13160
- verb: "stack-ready",
13161
- at,
13162
- standingSince: at,
13163
- ageSecs: Math.max(0, Math.round((now2 - Date.parse(at)) / 1e3)),
13164
- note: s.text,
13165
- repo: source.dispatch ? repoOf(source.id, source.lane) : repoKey(cfg, s.repo),
13166
- prUrl: ref.url
13167
- });
13168
- }
13169
13227
  for (const { id, lane, pr, dispatch } of observed2) {
13170
13228
  const kinds = prKinds(pr, { readySettleSecs: cfg.readySettleSecs, now: now2 });
13171
13229
  if (kinds.length === 0) continue;
@@ -13174,6 +13232,10 @@ function prAttention(now2, observed2 = observedPrs(), cfg = loadConfig()) {
13174
13232
  const watchFollowUp = prWatch?.lastFollowUpId;
13175
13233
  const chain = dispatch ? chainOf(id) : [];
13176
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
+ }
13177
13239
  const human = humanPrAttention(pr, kind, cfg, !!prWatch && !prWatch.done, now2);
13178
13240
  if (!human.show) continue;
13179
13241
  const repairCannotAct = human.reason !== void 0 && !["auto-repair is off", "not owned by lobstah", "no active PR watch"].includes(human.reason);
@@ -13182,7 +13244,6 @@ function prAttention(now2, observed2 = observedPrs(), cfg = loadConfig()) {
13182
13244
  const standingSince = kind === "pr:ready" ? new Date(Date.parse(conditionSince) + cfg.readySettleSecs * 1e3).toISOString() : conditionSince;
13183
13245
  out.push({
13184
13246
  kind,
13185
- ...kind === "pr:ready" && quietReady.has(pr.url) ? { quiet: true } : {},
13186
13247
  key: ref?.key ?? pr.url,
13187
13248
  stateHash: prStateHash(pr),
13188
13249
  id,
@@ -13205,6 +13266,38 @@ function prAttention(now2, observed2 = observedPrs(), cfg = loadConfig()) {
13205
13266
  });
13206
13267
  }
13207
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
+ }
13208
13301
  return out;
13209
13302
  }
13210
13303
  function landedAttention(cfg, now2) {
@@ -13536,8 +13629,10 @@ function buildTendReport(now2 = Date.now()) {
13536
13629
  { readySettleSecs: cfg.readySettleSecs, now: now2, trunk: (r) => prStackTrunk(cfg, r) }
13537
13630
  ).stacks.filter((s) => s.open);
13538
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);
13539
13634
  const enabled = new Set(cfg.attentionKinds);
13540
- 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)));
13541
13636
  const shown = enabled.has("decision") ? hideFramedQuestions(walking) : walking;
13542
13637
  attention.length = 0;
13543
13638
  attention.push(
@@ -13580,7 +13675,8 @@ function buildTendReport(now2 = Date.now()) {
13580
13675
  const notices = listNotices(5).map((n) => ({
13581
13676
  kind: n.kind,
13582
13677
  ageMins: Math.max(0, Math.round((now2 - (Date.parse(n.at) || 0)) / 6e4)),
13583
- text: n.text
13678
+ text: n.text,
13679
+ ...n.url ? { url: n.url } : {}
13584
13680
  }));
13585
13681
  const helms = listHelms().map((h) => ({
13586
13682
  grounds: h.grounds,
@@ -13653,7 +13749,9 @@ function renderTend(r) {
13653
13749
  if (r.queueWait) lines.push(r.queueWait);
13654
13750
  for (const stack of r.stacks) {
13655
13751
  lines.push(`stack ${stack.numbers.map((n) => `#${n}`).join(" \u2192 ")}: next ${stack.nextNumber ? `#${stack.nextNumber}` : "none"}`);
13656
- 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}`);
13657
13755
  }
13658
13756
  if (r.attention.length > 0) {
13659
13757
  lines.push("");
@@ -13665,8 +13763,9 @@ function renderTend(r) {
13665
13763
  verb: a.kind === "question" || a.kind === "watch" ? a.verb : a.kind === "landed" ? `landed (${a.verb})` : a.kind,
13666
13764
  waitingMins: Math.round(a.ageSecs / 60),
13667
13765
  held: a.held ? "yes" : "",
13668
- // Whether the human has opened it in the glass's decision modal.
13669
- 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" : "",
13670
13769
  note: named(a.prUrl ? `${a.note ?? ""} ${a.prUrl}`.trim() : a.note ?? "")
13671
13770
  })),
13672
13771
  ["id", "verb", "waitingMins", "held", "viewed", "note"]
@@ -13782,7 +13881,7 @@ function renderTend(r) {
13782
13881
  lines.push(
13783
13882
  toonTable(
13784
13883
  "notices (recent)",
13785
- 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}` : ""}` })),
13786
13885
  ["kind", "ageMins", "text"]
13787
13886
  )
13788
13887
  );
@@ -13869,7 +13968,7 @@ function buildDigest(opts = {}) {
13869
13968
  landed,
13870
13969
  arisen,
13871
13970
  standing,
13872
- 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}`),
13873
13972
  traps,
13874
13973
  verdict: tend.verdict,
13875
13974
  counts: tend.counts
@@ -14381,13 +14480,82 @@ ${help}`;
14381
14480
 
14382
14481
  // apps/cli/src/cull.ts
14383
14482
  init_dist2();
14384
- import * as fs59 from "node:fs";
14483
+ import * as fs60 from "node:fs";
14385
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();
14386
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
14387
14555
  var DAY = 864e5;
14388
14556
  function walkBytes(target) {
14389
- const stat = fs59.lstatSync(target);
14390
- 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);
14391
14559
  return stat.isFile() ? stat.size : 0;
14392
14560
  }
14393
14561
  var sizing = {
@@ -14402,7 +14570,7 @@ var sizing = {
14402
14570
  worktree(target, platform = process.platform) {
14403
14571
  if (platform !== "win32") {
14404
14572
  try {
14405
- 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"] });
14406
14574
  const kb = Number(out.trim().split(/\s+/)[0]);
14407
14575
  if (Number.isFinite(kb)) return kb * 1024;
14408
14576
  } catch {
@@ -14413,7 +14581,7 @@ var sizing = {
14413
14581
  };
14414
14582
  function idsIn(dir) {
14415
14583
  try {
14416
- 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$/, "")));
14417
14585
  } catch {
14418
14586
  return /* @__PURE__ */ new Set();
14419
14587
  }
@@ -14449,7 +14617,7 @@ function planCull(olderThanDays, now2 = Date.now(), opts = {}) {
14449
14617
  retainReferences(id, lane);
14450
14618
  continue;
14451
14619
  }
14452
- const m = fs59.statSync(p).mtimeMs;
14620
+ const m = fs60.statSync(p).mtimeMs;
14453
14621
  doneMtimes.set(id, m);
14454
14622
  if (m >= cutoff) retainReferences(id, lane);
14455
14623
  if (m < cutoff) items.push({ kind: "done", id, target: p, ageDays: Math.floor((now2 - m) / DAY), bytes: size(p), ageFrom: m });
@@ -14458,28 +14626,45 @@ function planCull(olderThanDays, now2 = Date.now(), opts = {}) {
14458
14626
  const wtRoot = path52.join(lobstahHome(), "worktrees");
14459
14627
  const trapped = trapWorktreeIds(wtRoot);
14460
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);
14461
14634
  for (const id of idsIn(wtRoot)) {
14462
14635
  if (live.has(id) || trapped.has(id) || usage.live.has(id)) continue;
14463
14636
  const doneAt = usage.newest.get(id) ?? doneMtimes.get(id);
14464
14637
  if (doneAt !== void 0 && doneAt >= cutoff) continue;
14465
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
+ }
14466
14650
  if (readTrapAnchor(p)?.createdBy === "soak" && inspectTrapWorktree(p).reason) continue;
14467
- const from = doneAt ?? fs59.statSync(p).mtimeMs;
14651
+ const from = doneAt ?? fs60.statSync(p).mtimeMs;
14468
14652
  items.push({ kind: "worktree", id, target: p, ageDays: Math.floor((now2 - from) / DAY), bytes: measure ? sizing.worktree(p) : 0, ageFrom: from });
14469
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);
14470
14655
  for (const lane of ["work", "chore"]) {
14471
14656
  const d = laneDirs(lane);
14472
14657
  const groups = /* @__PURE__ */ new Map();
14473
- for (const f of fs59.readdirSync(d.state)) {
14658
+ for (const f of fs60.readdirSync(d.state)) {
14474
14659
  const target = path52.join(d.state, f);
14475
- const directory = fs59.statSync(target).isDirectory();
14476
- 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;
14477
14662
  const id = directory ? f : f.replace(/\.(status|events|evidence|attn|notified|runner\.log)$/, "");
14478
14663
  if (id === f && !directory) continue;
14479
14664
  if (live.has(id) || (doneMtimes.get(id) ?? 0) >= cutoff || referencedAttachmentState.has(path52.join(d.state, id))) continue;
14480
14665
  const previous = groups.get(id);
14481
14666
  groups.set(id, {
14482
- mtime: Math.max(previous?.mtime ?? 0, fs59.statSync(target).mtimeMs),
14667
+ mtime: Math.max(previous?.mtime ?? 0, fs60.statSync(target).mtimeMs),
14483
14668
  bytes: (previous?.bytes ?? 0) + size(target)
14484
14669
  });
14485
14670
  }
@@ -14490,7 +14675,7 @@ function planCull(olderThanDays, now2 = Date.now(), opts = {}) {
14490
14675
  }
14491
14676
  }
14492
14677
  for (const r of readPrs()) {
14493
- if (r.state === "OPEN") continue;
14678
+ if (r.state === "OPEN" || keptPrs.has(r.key)) continue;
14494
14679
  const seen = Date.parse(r.observedAt) || now2;
14495
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 });
14496
14681
  }
@@ -14550,7 +14735,7 @@ function worktreeUsage(openPr) {
14550
14735
  if (openPr?.has(id)) live.add(owner);
14551
14736
  let m;
14552
14737
  try {
14553
- m = fs59.statSync(path52.join(d.done, id)).mtimeMs;
14738
+ m = fs60.statSync(path52.join(d.done, id)).mtimeMs;
14554
14739
  } catch {
14555
14740
  continue;
14556
14741
  }
@@ -14564,7 +14749,7 @@ function trapWorktreeIds(wtRoot) {
14564
14749
  const real = (p) => {
14565
14750
  let r;
14566
14751
  try {
14567
- r = fs59.realpathSync.native(p);
14752
+ r = fs60.realpathSync.native(p);
14568
14753
  } catch {
14569
14754
  r = path52.resolve(p);
14570
14755
  }
@@ -14593,13 +14778,15 @@ function limitBatch(items, maxGroups) {
14593
14778
  return { batch: items.filter((i) => keep.has(groupOf(i))), deferred: order.length - keep.size };
14594
14779
  }
14595
14780
  function planPressureCull(now2 = Date.now()) {
14781
+ const proofs = mergedCullProofs();
14782
+ const fetched = /* @__PURE__ */ new Map();
14596
14783
  const live = /* @__PURE__ */ new Set();
14597
14784
  const doneMtimes = /* @__PURE__ */ new Map();
14598
14785
  for (const lane of ["work", "chore"]) {
14599
14786
  const d = laneDirs(lane);
14600
14787
  for (const id of idsIn(d.queue)) live.add(id);
14601
14788
  for (const id of idsIn(d.active)) live.add(id);
14602
- 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);
14603
14790
  }
14604
14791
  const openPr = openPrDispatches();
14605
14792
  for (const id of openPr) live.add(id);
@@ -14611,8 +14798,10 @@ function planPressureCull(now2 = Date.now()) {
14611
14798
  for (const id of idsIn(wtRoot)) {
14612
14799
  if (live.has(id)) continue;
14613
14800
  const p = path52.join(wtRoot, id);
14801
+ const proof = proofs.get(id);
14802
+ if (proof && mergedWorktreeUnsafe(p, proof.heads, proof.trunk, fetched)) continue;
14614
14803
  if (readTrapAnchor(p)?.createdBy === "soak" && inspectTrapWorktree(p).reason) continue;
14615
- 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;
14616
14805
  items.push({ kind: "worktree", id, target: p, ageDays: Math.floor((now2 - from) / DAY), bytes: 0, ageFrom: from });
14617
14806
  }
14618
14807
  return items.sort((a, b) => a.ageFrom - b.ageFrom || a.id.localeCompare(b.id));
@@ -14626,28 +14815,28 @@ function removeWorktree(id, dir) {
14626
14815
  for (const lane of ["work", "chore"]) {
14627
14816
  const descFile = path52.join(laneDirs(lane).done, id, "descriptor.json");
14628
14817
  try {
14629
- const desc = JSON.parse(fs59.readFileSync(descFile, "utf8"));
14818
+ const desc = JSON.parse(fs60.readFileSync(descFile, "utf8"));
14630
14819
  const repo = loadConfig().repos[desc.repo];
14631
14820
  if (repo) {
14632
- execFileSync7("git", ["worktree", "remove", "--force", dir], { cwd: repo.path, stdio: "ignore" });
14821
+ execFileSync8("git", ["worktree", "remove", "--force", dir], { cwd: repo.path, stdio: "ignore" });
14633
14822
  return;
14634
14823
  }
14635
14824
  } catch {
14636
14825
  }
14637
14826
  }
14638
14827
  try {
14639
- 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"], {
14640
14829
  cwd: dir,
14641
14830
  encoding: "utf8",
14642
14831
  stdio: ["ignore", "pipe", "ignore"]
14643
14832
  }).trim();
14644
14833
  if (common) {
14645
- execFileSync7("git", ["--git-dir", common, "worktree", "remove", "--force", dir], { stdio: "ignore" });
14834
+ execFileSync8("git", ["--git-dir", common, "worktree", "remove", "--force", dir], { stdio: "ignore" });
14646
14835
  return;
14647
14836
  }
14648
14837
  } catch {
14649
14838
  }
14650
- fs59.rmSync(dir, { recursive: true, force: true });
14839
+ fs60.rmSync(dir, { recursive: true, force: true });
14651
14840
  }
14652
14841
  function culledState(items) {
14653
14842
  const lanes = new Map(["work", "chore"].map((lane) => [path52.resolve(laneDirs(lane).state), lane]));
@@ -14659,7 +14848,7 @@ function culledState(items) {
14659
14848
  function applyCull(items) {
14660
14849
  foldCatches(culledState(items));
14661
14850
  for (const item of items.filter((i) => i.kind === "worktree")) removeWorktree(item.id, item.target);
14662
- 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 });
14663
14852
  for (const item of items.filter((i) => i.kind === "ack")) removeAck(item.target);
14664
14853
  for (const item of items.filter((i) => i.kind === "pr")) removePr(item.target);
14665
14854
  for (const item of items.filter((i) => i.kind === "report")) removeHelmReport(item.id);
@@ -14667,8 +14856,8 @@ function applyCull(items) {
14667
14856
  for (const item of items.filter((i) => i.kind === "release")) removeRelease(item.id);
14668
14857
  for (const item of items.filter((i) => i.kind === "state")) {
14669
14858
  const dir = path52.dirname(item.target);
14670
- for (const f of fs59.readdirSync(dir)) {
14671
- 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 });
14672
14861
  }
14673
14862
  }
14674
14863
  }
@@ -14737,13 +14926,11 @@ ${toonKV(statusFields(s))}${still}`;
14737
14926
 
14738
14927
  // apps/cli/src/release.ts
14739
14928
  init_dist2();
14740
- init_dist3();
14741
- import * as fs60 from "node:fs";
14742
- import { execFileSync as execFileSync8 } from "node:child_process";
14929
+ import * as fs61 from "node:fs";
14743
14930
  var DAY2 = 864e5;
14744
14931
  function listIds(dir) {
14745
14932
  try {
14746
- 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$/, ""));
14747
14934
  } catch {
14748
14935
  return [];
14749
14936
  }
@@ -14794,15 +14981,16 @@ function prUrlOf(ev) {
14794
14981
  }
14795
14982
  function mergedPrs(all) {
14796
14983
  const merged = /* @__PURE__ */ new Map();
14797
- const add2 = (url, key, headRef, id) => {
14798
- 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])];
14799
14987
  m.headRef ??= headRef;
14800
14988
  m.owners.add(id);
14801
14989
  merged.set(key, m);
14802
14990
  };
14803
14991
  for (const r of readPrs()) {
14804
14992
  if (r.state !== "MERGED") continue;
14805
- 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]);
14806
14994
  }
14807
14995
  for (const k of all.values()) {
14808
14996
  if (k.bucket !== "done") continue;
@@ -14812,28 +15000,10 @@ function mergedPrs(all) {
14812
15000
  if (!ref) continue;
14813
15001
  const record = readPr(ref.key);
14814
15002
  const state = record?.state ?? ev.pr?.state;
14815
- 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] : []);
14816
15004
  }
14817
15005
  return merged;
14818
15006
  }
14819
- function git7(cwd, ...args) {
14820
- try {
14821
- return execFileSync8("git", args, { cwd, encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] }).trim();
14822
- } catch {
14823
- return void 0;
14824
- }
14825
- }
14826
- function unsafe(dir, fetched) {
14827
- const status = git7(dir, "status", "--porcelain");
14828
- if (status === void 0) return "unpushed work: not a readable git checkout";
14829
- if (status !== "") return "unpushed work: uncommitted changes";
14830
- const common = git7(dir, "rev-parse", "--path-format=absolute", "--git-common-dir") ?? dir;
14831
- if (!fetched.has(common)) fetched.set(common, withRepoLockSync(dir, () => git7(dir, "fetch", "--quiet", "origin")) !== void 0);
14832
- if (!fetched.get(common)) return "unpushed work: fetch failed, remote state unknown";
14833
- const remote = git7(dir, "branch", "-r", "--contains", "HEAD");
14834
- if (remote === void 0 || remote === "") return "unpushed work: HEAD is not on the remote";
14835
- return void 0;
14836
- }
14837
15007
  function planMergeRelease(now2 = Date.now(), batch = Infinity) {
14838
15008
  const plan = { items: [], users: /* @__PURE__ */ new Map(), kept: [] };
14839
15009
  if (!loadConfig().limits.releaseOnMerge) return plan;
@@ -14860,7 +15030,11 @@ function planMergeRelease(now2 = Date.now(), batch = Infinity) {
14860
15030
  const chain = chains.get(id) ?? /* @__PURE__ */ new Set([id]);
14861
15031
  const home = owner(id);
14862
15032
  const add2 = (o) => {
14863
- 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 });
14864
15038
  };
14865
15039
  add2(home);
14866
15040
  for (const m of chain) {
@@ -14874,7 +15048,7 @@ function planMergeRelease(now2 = Date.now(), batch = Infinity) {
14874
15048
  }
14875
15049
  }
14876
15050
  }
14877
- 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));
14878
15052
  const fetched = /* @__PURE__ */ new Map();
14879
15053
  let checked = 0;
14880
15054
  for (const { o, c, from } of ordered) {
@@ -14884,7 +15058,8 @@ function planMergeRelease(now2 = Date.now(), batch = Infinity) {
14884
15058
  if (![...c.chain].every((id) => finished(all.get(id))) || !users.every((k) => finished(k))) continue;
14885
15059
  checked++;
14886
15060
  const dir = worktreePath(o);
14887
- 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);
14888
15063
  if (why) {
14889
15064
  plan.kept.push({ id: o, reason: why, pr: c.pr.key, at: new Date(now2).toISOString() });
14890
15065
  continue;
@@ -14900,7 +15075,7 @@ function runMergeRelease(now2 = Date.now(), batch = Infinity) {
14900
15075
  const released = [];
14901
15076
  for (const item of plan.items) {
14902
15077
  applyCull([item]);
14903
- if (fs60.existsSync(item.target)) continue;
15078
+ if (fs61.existsSync(item.target)) continue;
14904
15079
  released.push(item.id);
14905
15080
  for (const k of plan.users.get(item.id) ?? []) mergeEvidence(k.id, k.lane, { worktreeReleased: at });
14906
15081
  }
@@ -15041,7 +15216,7 @@ full pattern and trade-offs: docs/man.md \xB7 config: docs/configuration.md`;
15041
15216
 
15042
15217
  // apps/cli/src/decision-answer.ts
15043
15218
  init_dist2();
15044
- import * as fs61 from "node:fs";
15219
+ import * as fs62 from "node:fs";
15045
15220
  function standingQuestion2(key) {
15046
15221
  return buildTendReport().attention.find((a) => a.kind === "question" && a.key === key);
15047
15222
  }
@@ -15068,7 +15243,7 @@ function answerKey(key, req) {
15068
15243
  try {
15069
15244
  return { decision: meta, answer: answerDecision({ key: meta.key, ...req, maxBytes }) };
15070
15245
  } catch (err) {
15071
- fs61.rmSync(decisionDir(meta.key), { recursive: true, force: true });
15246
+ fs62.rmSync(decisionDir(meta.key), { recursive: true, force: true });
15072
15247
  throw err;
15073
15248
  }
15074
15249
  }
@@ -15082,7 +15257,7 @@ function viewKey(key, now2 = /* @__PURE__ */ new Date()) {
15082
15257
  // apps/cli/src/doctor.ts
15083
15258
  init_dist();
15084
15259
  init_dist2();
15085
- import * as fs67 from "node:fs";
15260
+ import * as fs68 from "node:fs";
15086
15261
  import * as path58 from "node:path";
15087
15262
  import { spawnSync as spawnSync9 } from "node:child_process";
15088
15263
 
@@ -15099,7 +15274,7 @@ function parkedSummary(parked = parkedDispatches()) {
15099
15274
 
15100
15275
  // apps/cli/src/plugin-version.ts
15101
15276
  init_dist();
15102
- import * as fs62 from "node:fs";
15277
+ import * as fs63 from "node:fs";
15103
15278
  import * as os7 from "node:os";
15104
15279
  import * as path53 from "node:path";
15105
15280
  var UPDATE_COMMAND = {
@@ -15108,7 +15283,7 @@ var UPDATE_COMMAND = {
15108
15283
  };
15109
15284
  var readJson2 = (file2) => {
15110
15285
  try {
15111
- return JSON.parse(fs62.readFileSync(file2, "utf8"));
15286
+ return JSON.parse(fs63.readFileSync(file2, "utf8"));
15112
15287
  } catch {
15113
15288
  return void 0;
15114
15289
  }
@@ -15139,7 +15314,7 @@ function installedCodexPlugin(opts = {}) {
15139
15314
  const codexDir = env.CODEX_HOME ?? path53.join(opts.home ?? os7.homedir(), ".codex");
15140
15315
  let cfg;
15141
15316
  try {
15142
- cfg = parse(fs62.readFileSync(path53.join(codexDir, "config.toml"), "utf8"));
15317
+ cfg = parse(fs63.readFileSync(path53.join(codexDir, "config.toml"), "utf8"));
15143
15318
  } catch {
15144
15319
  return void 0;
15145
15320
  }
@@ -15148,7 +15323,7 @@ function installedCodexPlugin(opts = {}) {
15148
15323
  const cache = path53.join(codexDir, "plugins", "cache", "lobstah", "lobstah");
15149
15324
  let dirs;
15150
15325
  try {
15151
- dirs = fs62.readdirSync(cache);
15326
+ dirs = fs63.readdirSync(cache);
15152
15327
  } catch {
15153
15328
  return void 0;
15154
15329
  }
@@ -15206,7 +15381,7 @@ function pluginBehindLine(cliVersion, opts = {}) {
15206
15381
 
15207
15382
  // apps/cli/src/service.ts
15208
15383
  init_dist2();
15209
- import * as fs63 from "node:fs";
15384
+ import * as fs64 from "node:fs";
15210
15385
  import * as os8 from "node:os";
15211
15386
  import * as path54 from "node:path";
15212
15387
  import { spawnSync as spawnSync7 } from "node:child_process";
@@ -15271,7 +15446,7 @@ function serviceFile(kind) {
15271
15446
  return process.platform === "darwin" ? path54.join(os8.homedir(), "Library", "LaunchAgents", `lobstah.${kind}.plist`) : path54.join(os8.homedir(), ".config", "systemd", "user", `lobstah-${kind}.service`);
15272
15447
  }
15273
15448
  function serviceInstalled(kind) {
15274
- return fs63.existsSync(serviceFile(kind));
15449
+ return fs64.existsSync(serviceFile(kind));
15275
15450
  }
15276
15451
  function restartCommand(kind, platform = process.platform, uid = process.getuid?.() ?? 0) {
15277
15452
  return platform === "darwin" ? ["launchctl", "kickstart", "-k", `gui/${uid}/lobstah.${kind}`] : ["systemctl", "--user", "restart", `lobstah-${kind}.service`];
@@ -15290,7 +15465,7 @@ function installService(kind, port) {
15290
15465
  if (process.platform === "win32") {
15291
15466
  throw new Error(`no service manager support on Windows \u2014 run \`lobstah ${kind}\` under your process manager of choice`);
15292
15467
  }
15293
- const entry = COMPILED_BINARY ? "" : fs63.realpathSync(process.argv[1]);
15468
+ const entry = COMPILED_BINARY ? "" : fs64.realpathSync(process.argv[1]);
15294
15469
  const spec = {
15295
15470
  kind,
15296
15471
  nodePath: process.execPath,
@@ -15303,16 +15478,16 @@ function installService(kind, port) {
15303
15478
  if (kind !== "glass" && !onPath("git", spec.pathEnv)) {
15304
15479
  throw new Error(`git is not reachable on the service PATH (${spec.pathEnv}) \u2014 the daemon cannot allocate worktrees`);
15305
15480
  }
15306
- fs63.mkdirSync(spec.logDir, { recursive: true });
15481
+ fs64.mkdirSync(spec.logDir, { recursive: true });
15307
15482
  const file2 = serviceFile(kind);
15308
- fs63.mkdirSync(path54.dirname(file2), { recursive: true });
15483
+ fs64.mkdirSync(path54.dirname(file2), { recursive: true });
15309
15484
  if (process.platform === "darwin") {
15310
- if (fs63.existsSync(file2)) run2("launchctl", ["unload", file2]);
15311
- fs63.writeFileSync(file2, renderLaunchdPlist(spec));
15485
+ if (fs64.existsSync(file2)) run2("launchctl", ["unload", file2]);
15486
+ fs64.writeFileSync(file2, renderLaunchdPlist(spec));
15312
15487
  const load = run2("launchctl", ["load", file2]);
15313
15488
  return { file: file2, loaded: load.ok, detail: load.ok ? `label lobstah.${kind}` : load.out };
15314
15489
  }
15315
- fs63.writeFileSync(file2, renderSystemdUnit(spec));
15490
+ fs64.writeFileSync(file2, renderSystemdUnit(spec));
15316
15491
  run2("systemctl", ["--user", "daemon-reload"]);
15317
15492
  const enable = run2("systemctl", ["--user", "enable", "--now", `lobstah-${kind}.service`]);
15318
15493
  return { file: file2, loaded: enable.ok, detail: enable.ok ? `unit lobstah-${kind}.service` : enable.out };
@@ -15321,19 +15496,19 @@ function uninstallService(kind) {
15321
15496
  if (process.platform === "win32") throw new Error(`no service manager support on Windows`);
15322
15497
  const file2 = serviceFile(kind);
15323
15498
  if (process.platform === "darwin") {
15324
- if (fs63.existsSync(file2)) run2("launchctl", ["unload", file2]);
15499
+ if (fs64.existsSync(file2)) run2("launchctl", ["unload", file2]);
15325
15500
  } else {
15326
15501
  run2("systemctl", ["--user", "disable", "--now", `lobstah-${kind}.service`]);
15327
15502
  }
15328
- const removed = fs63.existsSync(file2);
15329
- if (removed) fs63.unlinkSync(file2);
15503
+ const removed = fs64.existsSync(file2);
15504
+ if (removed) fs64.unlinkSync(file2);
15330
15505
  if (process.platform !== "darwin") run2("systemctl", ["--user", "daemon-reload"]);
15331
15506
  return { file: file2, removed };
15332
15507
  }
15333
15508
 
15334
15509
  // apps/cli/src/pet.ts
15335
15510
  init_dist2();
15336
- import * as fs64 from "node:fs";
15511
+ import * as fs65 from "node:fs";
15337
15512
  import * as os9 from "node:os";
15338
15513
  import * as path55 from "node:path";
15339
15514
  import { spawnSync as spawnSync8 } from "node:child_process";
@@ -15385,7 +15560,7 @@ function installPet(binaryArg) {
15385
15560
  path55.join(process.cwd(), ".build", "debug", "LobstahPet"),
15386
15561
  petBinaryHome()
15387
15562
  ].filter((c) => c !== void 0);
15388
- const source = candidates.find((c) => fs64.existsSync(c));
15563
+ const source = candidates.find((c) => fs65.existsSync(c));
15389
15564
  if (!source) {
15390
15565
  throw new Error(
15391
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>"
@@ -15393,33 +15568,33 @@ function installPet(binaryArg) {
15393
15568
  }
15394
15569
  const binary = petBinaryHome();
15395
15570
  const file0 = petPlistFile();
15396
- if (fs64.existsSync(file0)) run3("launchctl", ["unload", file0]);
15571
+ if (fs65.existsSync(file0)) run3("launchctl", ["unload", file0]);
15397
15572
  run3("pkill", ["-f", "LobstahPet"]);
15398
15573
  if (path55.resolve(source) !== path55.resolve(binary)) {
15399
- fs64.mkdirSync(path55.dirname(binary), { recursive: true });
15400
- fs64.rmSync(binary, { force: true });
15401
- fs64.copyFileSync(source, binary);
15402
- 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);
15403
15578
  const bundle = path55.join(path55.dirname(source), "LobstahPet_LobstahPet.bundle");
15404
- if (fs64.existsSync(bundle)) {
15579
+ if (fs65.existsSync(bundle)) {
15405
15580
  const dest = path55.join(path55.dirname(binary), "LobstahPet_LobstahPet.bundle");
15406
- fs64.rmSync(dest, { recursive: true, force: true });
15407
- fs64.cpSync(bundle, dest, { recursive: true });
15581
+ fs65.rmSync(dest, { recursive: true, force: true });
15582
+ fs65.cpSync(bundle, dest, { recursive: true });
15408
15583
  }
15409
15584
  }
15410
15585
  const logDir = path55.join(lobstahHome(), "logs");
15411
- fs64.mkdirSync(logDir, { recursive: true });
15586
+ fs65.mkdirSync(logDir, { recursive: true });
15412
15587
  const file2 = petPlistFile();
15413
- fs64.mkdirSync(path55.dirname(file2), { recursive: true });
15414
- 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));
15415
15590
  const load = run3("launchctl", ["load", file2]);
15416
15591
  return { file: file2, binary, loaded: load.ok, detail: load.ok ? "label lobstah.pet \u2014 starts at login" : load.out };
15417
15592
  }
15418
15593
  function uninstallPet() {
15419
15594
  const file2 = petPlistFile();
15420
- if (process.platform === "darwin" && fs64.existsSync(file2)) run3("launchctl", ["unload", file2]);
15421
- const removed = fs64.existsSync(file2);
15422
- 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);
15423
15598
  run3("pkill", ["-f", "LobstahPet"]);
15424
15599
  return { file: file2, removed };
15425
15600
  }
@@ -15428,7 +15603,7 @@ function petStateFile() {
15428
15603
  }
15429
15604
  function readPetState() {
15430
15605
  try {
15431
- const s = JSON.parse(fs64.readFileSync(petStateFile(), "utf8"));
15606
+ const s = JSON.parse(fs65.readFileSync(petStateFile(), "utf8"));
15432
15607
  return typeof s.pid === "number" && typeof s.at === "string" ? s : void 0;
15433
15608
  } catch {
15434
15609
  return void 0;
@@ -15450,8 +15625,8 @@ function processAlive(pid) {
15450
15625
  function petRow(opts = {}) {
15451
15626
  const now2 = opts.now ?? Date.now();
15452
15627
  const alive2 = opts.alive ?? processAlive;
15453
- const agent = fs64.existsSync(opts.plist ?? petPlistFile());
15454
- const binary = fs64.existsSync(opts.binary ?? petBinaryHome());
15628
+ const agent = fs65.existsSync(opts.plist ?? petPlistFile());
15629
+ const binary = fs65.existsSync(opts.binary ?? petBinaryHome());
15455
15630
  const installed = agent ? "installed" : binary ? "binary installed, no login agent" : "not installed";
15456
15631
  const state = readPetState();
15457
15632
  if (!state) {
@@ -15474,13 +15649,13 @@ function petRow(opts = {}) {
15474
15649
  init_dist();
15475
15650
  init_dist2();
15476
15651
  import { createHash as createHash9 } from "node:crypto";
15477
- import * as fs66 from "node:fs";
15652
+ import * as fs67 from "node:fs";
15478
15653
  import * as os10 from "node:os";
15479
15654
  import * as path57 from "node:path";
15480
15655
 
15481
15656
  // apps/cli/src/watchers.ts
15482
15657
  init_dist2();
15483
- import * as fs65 from "node:fs";
15658
+ import * as fs66 from "node:fs";
15484
15659
  import * as path56 from "node:path";
15485
15660
  var STALE_MS = 5e3;
15486
15661
  var HEARTBEAT_MS = 1500;
@@ -15488,7 +15663,7 @@ var watcherDir = () => path56.join(lobstahHome(), "watchers");
15488
15663
  var watcherFile = (sessionId) => path56.join(watcherDir(), `${encodeURIComponent(sessionId)}.json`);
15489
15664
  function liveRegistration(sessionId, now2 = Date.now()) {
15490
15665
  try {
15491
- const w = JSON.parse(fs65.readFileSync(watcherFile(sessionId), "utf8"));
15666
+ const w = JSON.parse(fs66.readFileSync(watcherFile(sessionId), "utf8"));
15492
15667
  const beat = Date.parse(w.heartbeatAt);
15493
15668
  if (w.sessionId !== sessionId || !Number.isInteger(w.pid) || w.pid <= 0 || !Number.isFinite(beat) || now2 - beat > STALE_MS) return void 0;
15494
15669
  return w;
@@ -15536,7 +15711,7 @@ async function stopWatcher(sessionId, kind, graceMs = 3e3, pollMs = 50) {
15536
15711
  const file2 = watcherFile(sessionId);
15537
15712
  const claimed = () => {
15538
15713
  try {
15539
- return JSON.parse(fs65.readFileSync(file2, "utf8")).pid === w.pid;
15714
+ return JSON.parse(fs66.readFileSync(file2, "utf8")).pid === w.pid;
15540
15715
  } catch {
15541
15716
  return false;
15542
15717
  }
@@ -15544,7 +15719,7 @@ async function stopWatcher(sessionId, kind, graceMs = 3e3, pollMs = 50) {
15544
15719
  for (; ; ) {
15545
15720
  if (!claimed()) return { watcher: w, exited: true };
15546
15721
  if (!pidAlive4(w.pid)) {
15547
- if (claimed()) fs65.rmSync(file2, { force: true });
15722
+ if (claimed()) fs66.rmSync(file2, { force: true });
15548
15723
  return { watcher: w, exited: true };
15549
15724
  }
15550
15725
  if (Date.now() >= deadline) return { watcher: w, exited: false };
@@ -15552,13 +15727,13 @@ async function stopWatcher(sessionId, kind, graceMs = 3e3, pollMs = 50) {
15552
15727
  }
15553
15728
  }
15554
15729
  function armWatcher(sessionId, kind, trapId) {
15555
- fs65.mkdirSync(watcherDir(), { recursive: true });
15730
+ fs66.mkdirSync(watcherDir(), { recursive: true });
15556
15731
  const file2 = watcherFile(sessionId);
15557
15732
  const own = () => ({ sessionId, kind, pid: process.pid, heartbeatAt: (/* @__PURE__ */ new Date()).toISOString(), ...trapId ? { trapId } : {} });
15558
15733
  let claimed = false;
15559
15734
  for (let attempt = 0; attempt < 3; attempt++) {
15560
15735
  try {
15561
- fs65.writeFileSync(file2, `${JSON.stringify(own())}
15736
+ fs66.writeFileSync(file2, `${JSON.stringify(own())}
15562
15737
  `, { flag: "wx" });
15563
15738
  claimed = true;
15564
15739
  break;
@@ -15567,7 +15742,7 @@ function armWatcher(sessionId, kind, trapId) {
15567
15742
  const live = liveRegistration(sessionId);
15568
15743
  if (live) throw new Error(alreadyArmed(live));
15569
15744
  try {
15570
- fs65.rmSync(file2);
15745
+ fs66.rmSync(file2);
15571
15746
  } catch {
15572
15747
  }
15573
15748
  }
@@ -15575,7 +15750,7 @@ function armWatcher(sessionId, kind, trapId) {
15575
15750
  if (!claimed) throw new Error(`could not arm watcher for session ${sessionId}`);
15576
15751
  const ownsFile = () => {
15577
15752
  try {
15578
- return JSON.parse(fs65.readFileSync(file2, "utf8")).pid === process.pid;
15753
+ return JSON.parse(fs66.readFileSync(file2, "utf8")).pid === process.pid;
15579
15754
  } catch {
15580
15755
  return false;
15581
15756
  }
@@ -15584,12 +15759,12 @@ function armWatcher(sessionId, kind, trapId) {
15584
15759
  if (!ownsFile()) return;
15585
15760
  const tmp = `${file2}.tmp-${process.pid}`;
15586
15761
  try {
15587
- fs65.writeFileSync(tmp, `${JSON.stringify(own())}
15762
+ fs66.writeFileSync(tmp, `${JSON.stringify(own())}
15588
15763
  `);
15589
- fs65.renameSync(tmp, file2);
15764
+ fs66.renameSync(tmp, file2);
15590
15765
  } catch {
15591
15766
  try {
15592
- fs65.rmSync(tmp, { force: true });
15767
+ fs66.rmSync(tmp, { force: true });
15593
15768
  } catch {
15594
15769
  }
15595
15770
  }
@@ -15601,7 +15776,7 @@ function armWatcher(sessionId, kind, trapId) {
15601
15776
  if (stopped) return;
15602
15777
  stopped = true;
15603
15778
  clearInterval(timer);
15604
- if (ownsFile()) fs65.rmSync(file2, { force: true });
15779
+ if (ownsFile()) fs66.rmSync(file2, { force: true });
15605
15780
  process.off("exit", stop);
15606
15781
  process.off("SIGINT", interrupt);
15607
15782
  process.off("SIGTERM", terminate2);
@@ -15623,7 +15798,7 @@ function armWatcher(sessionId, kind, trapId) {
15623
15798
  // apps/cli/src/hook-readiness.ts
15624
15799
  var readJson3 = (file2) => {
15625
15800
  try {
15626
- return JSON.parse(fs66.readFileSync(file2, "utf8"));
15801
+ return JSON.parse(fs67.readFileSync(file2, "utf8"));
15627
15802
  } catch {
15628
15803
  return void 0;
15629
15804
  }
@@ -15660,7 +15835,7 @@ function codexHooks(opts = {}) {
15660
15835
  const codexDir = env.CODEX_HOME ?? path57.join(opts.home ?? os10.homedir(), ".codex");
15661
15836
  let cfg = {};
15662
15837
  try {
15663
- cfg = parse(fs66.readFileSync(path57.join(codexDir, "config.toml"), "utf8"));
15838
+ cfg = parse(fs67.readFileSync(path57.join(codexDir, "config.toml"), "utf8"));
15664
15839
  } catch {
15665
15840
  }
15666
15841
  const state = (cfg.hooks ?? {}).state ?? {};
@@ -15894,7 +16069,7 @@ var PROBE_PERMISSION = { "pull requests": "Pull requests", contents: "Contents",
15894
16069
  function githubRepos(repos) {
15895
16070
  const out = [];
15896
16071
  for (const [key, r] of Object.entries(repos)) {
15897
- 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 : "");
15898
16073
  const forgeRepo = origin ? githubRepoFromOrigin(origin) : void 0;
15899
16074
  if (forgeRepo) out.push({ key, forgeRepo, trunk: r.trunk });
15900
16075
  }
@@ -15904,12 +16079,12 @@ function diskRow(cfg, freeBytes = statfsFreeBytes, now2 = Date.now()) {
15904
16079
  const dir = worktreesDir();
15905
16080
  let free;
15906
16081
  try {
15907
- free = freeBytes(fs67.existsSync(dir) ? dir : lobstahHome());
16082
+ free = freeBytes(fs68.existsSync(dir) ? dir : lobstahHome());
15908
16083
  } catch {
15909
16084
  free = void 0;
15910
16085
  }
15911
16086
  const { minFreeGB, retentionDays } = cfg.limits;
15912
- const cullable = fs67.existsSync(dir) ? planPressureCull(now2) : [];
16087
+ const cullable = fs68.existsSync(dir) ? planPressureCull(now2) : [];
15913
16088
  const oldest = cullable.reduce((max, i) => Math.max(max, i.ageDays), 0);
15914
16089
  const parts = [
15915
16090
  free === void 0 ? "free space unreadable" : `${formatGB(free)} free on ${dir}`,
@@ -15921,7 +16096,7 @@ function diskRow(cfg, freeBytes = statfsFreeBytes, now2 = Date.now()) {
15921
16096
  const kept = readKeptWorktrees();
15922
16097
  if (kept.length > 0) {
15923
16098
  parts.push(
15924
- `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(", ")}`
15925
16100
  );
15926
16101
  }
15927
16102
  const hold = readHold();
@@ -15947,7 +16122,7 @@ async function runDoctor(now2 = Date.now()) {
15947
16122
  try {
15948
16123
  const port = glassPort();
15949
16124
  const info = await probeGlass(port);
15950
- const installed = process.platform === "win32" ? false : fs67.existsSync(serviceFile("glass"));
16125
+ const installed = process.platform === "win32" ? false : fs68.existsSync(serviceFile("glass"));
15951
16126
  push(
15952
16127
  "glass",
15953
16128
  info ? "ok" : "warn",
@@ -15959,13 +16134,13 @@ async function runDoctor(now2 = Date.now()) {
15959
16134
  const pet = petRow({ now: now2 });
15960
16135
  push(pet.check, pet.status, pet.detail);
15961
16136
  const cfgFile = configPath();
15962
- if (!fs67.existsSync(cfgFile)) {
16137
+ if (!fs68.existsSync(cfgFile)) {
15963
16138
  push("config", "fail", `${cfgFile} missing \u2014 run \`lobstah init\``);
15964
16139
  return rows;
15965
16140
  }
15966
16141
  let parsed;
15967
16142
  try {
15968
- parsed = parse(fs67.readFileSync(cfgFile, "utf8"));
16143
+ parsed = parse(fs68.readFileSync(cfgFile, "utf8"));
15969
16144
  push("config", "ok", cfgFile);
15970
16145
  } catch (err) {
15971
16146
  push("config", "fail", `${cfgFile}: ${err instanceof Error ? err.message : String(err)}`);
@@ -15976,7 +16151,7 @@ async function runDoctor(now2 = Date.now()) {
15976
16151
  if (repoKeys.length === 0) push("repos", "warn", "none configured \u2014 add one with `lobstah repos add <path>`");
15977
16152
  for (const [key, repo] of Object.entries(cfg.repos)) {
15978
16153
  const label = `repo ${key}`;
15979
- if (!fs67.existsSync(repo.path)) {
16154
+ if (!fs68.existsSync(repo.path)) {
15980
16155
  push(label, "fail", `path ${repo.path} does not exist`);
15981
16156
  continue;
15982
16157
  }
@@ -16007,11 +16182,11 @@ async function runDoctor(now2 = Date.now()) {
16007
16182
  const trapNames2 = listTraps().map(trapLabel);
16008
16183
  const parked = parkedSummary();
16009
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}` : ""}`;
16010
- if (!fs67.existsSync(hb)) {
16185
+ if (!fs68.existsSync(hb)) {
16011
16186
  push("daemon", "warn", `no heartbeat \u2014 daemon not running (\`lobstah daemon install\`); ${slots}`);
16012
16187
  } else {
16013
16188
  try {
16014
- const payload = JSON.parse(fs67.readFileSync(hb, "utf8"));
16189
+ const payload = JSON.parse(fs68.readFileSync(hb, "utf8"));
16015
16190
  const age2 = now2 - new Date(payload.heartbeat ?? 0).getTime();
16016
16191
  push(
16017
16192
  "daemon",
@@ -16044,9 +16219,9 @@ async function runDoctor(now2 = Date.now()) {
16044
16219
  push(disk.check, disk.status, disk.detail);
16045
16220
  try {
16046
16221
  const soaking = path58.join(lobstahHome(), "soaking");
16047
- 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) => {
16048
16223
  try {
16049
- 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";
16050
16225
  } catch {
16051
16226
  return true;
16052
16227
  }
@@ -16074,7 +16249,7 @@ async function runDoctor(now2 = Date.now()) {
16074
16249
  // apps/cli/src/glass.ts
16075
16250
  init_stats2();
16076
16251
  init_dist2();
16077
- import * as fs72 from "node:fs";
16252
+ import * as fs73 from "node:fs";
16078
16253
  import * as os11 from "node:os";
16079
16254
  import * as path62 from "node:path";
16080
16255
  import * as http3 from "node:http";
@@ -16082,17 +16257,17 @@ import { randomBytes as randomBytes6, timingSafeEqual as timingSafeEqual3 } from
16082
16257
  import { fileURLToPath as fileURLToPath3 } from "node:url";
16083
16258
 
16084
16259
  // apps/cli/src/glass-page.generated.ts
16085
- 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';
16086
16261
 
16087
16262
  // apps/cli/src/glass-snapshot-thread.ts
16088
16263
  init_dist2();
16089
- import * as fs69 from "node:fs";
16264
+ import * as fs70 from "node:fs";
16090
16265
  import { fileURLToPath as fileURLToPath2 } from "node:url";
16091
16266
  import { Worker } from "node:worker_threads";
16092
16267
  function workerEntry() {
16093
16268
  if (COMPILED_BINARY) return "./glass-snapshot-worker.ts";
16094
16269
  const url = new URL("./glass-snapshot-worker.js", import.meta.url);
16095
- return fs69.existsSync(fileURLToPath2(url)) ? url : void 0;
16270
+ return fs70.existsSync(fileURLToPath2(url)) ? url : void 0;
16096
16271
  }
16097
16272
  function startSnapshotThread() {
16098
16273
  const entry = workerEntry();
@@ -16279,7 +16454,7 @@ function pollBody(full, now2) {
16279
16454
 
16280
16455
  // apps/cli/src/glass-presence.ts
16281
16456
  init_dist2();
16282
- import * as fs70 from "node:fs";
16457
+ import * as fs71 from "node:fs";
16283
16458
  import * as http2 from "node:http";
16284
16459
  import * as path60 from "node:path";
16285
16460
  import { randomBytes as randomBytes5, timingSafeEqual as timingSafeEqual2 } from "node:crypto";
@@ -16362,8 +16537,8 @@ var SECRET_RE = /^[0-9a-f]{64}$/;
16362
16537
  function readShowSecret() {
16363
16538
  try {
16364
16539
  const file2 = showSecretPath();
16365
- if (process.platform !== "win32" && (fs70.statSync(file2).mode & 63) !== 0) return void 0;
16366
- 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();
16367
16542
  return SECRET_RE.test(secret) ? secret : void 0;
16368
16543
  } catch {
16369
16544
  return void 0;
@@ -16373,17 +16548,17 @@ function ensureShowSecret() {
16373
16548
  const existing = readShowSecret();
16374
16549
  if (existing) return existing;
16375
16550
  const file2 = showSecretPath();
16376
- fs70.mkdirSync(path60.dirname(file2), { recursive: true });
16551
+ fs71.mkdirSync(path60.dirname(file2), { recursive: true });
16377
16552
  const secret = randomBytes5(32).toString("hex");
16378
16553
  try {
16379
- fs70.writeFileSync(file2, secret, { mode: 384, flag: "wx" });
16554
+ fs71.writeFileSync(file2, secret, { mode: 384, flag: "wx" });
16380
16555
  } catch (e) {
16381
16556
  if (e.code !== "EEXIST") throw e;
16382
16557
  const raced = readShowSecret();
16383
16558
  if (raced) return raced;
16384
- fs70.writeFileSync(file2, secret, { mode: 384 });
16559
+ fs71.writeFileSync(file2, secret, { mode: 384 });
16385
16560
  }
16386
- fs70.chmodSync(file2, 384);
16561
+ fs71.chmodSync(file2, 384);
16387
16562
  return secret;
16388
16563
  }
16389
16564
  function showSecretMatches(supplied) {
@@ -16427,7 +16602,7 @@ function requestShow(port, hash, timeoutMs = 2e3) {
16427
16602
 
16428
16603
  // apps/cli/src/focus.ts
16429
16604
  init_dist2();
16430
- import * as fs71 from "node:fs";
16605
+ import * as fs72 from "node:fs";
16431
16606
  import * as path61 from "node:path";
16432
16607
  import { execFile as execFile2 } from "node:child_process";
16433
16608
  import { promisify as promisify2 } from "node:util";
@@ -16480,7 +16655,7 @@ function safeWorktree(reg) {
16480
16655
  const folder = reg.worktree;
16481
16656
  if (!path61.isAbsolute(folder) || /[\x00-\x1f\x7f]/.test(folder) || path61.normalize(folder) !== folder) return false;
16482
16657
  try {
16483
- return fs71.statSync(folder).isDirectory() && trapIdAt(folder) === reg.trapId;
16658
+ return fs72.statSync(folder).isDirectory() && trapIdAt(folder) === reg.trapId;
16484
16659
  } catch {
16485
16660
  return false;
16486
16661
  }
@@ -16565,25 +16740,26 @@ function requestBodyCap() {
16565
16740
  }
16566
16741
  var REQUEST_KIND_MAX_BYTES = /* @__PURE__ */ new Map([
16567
16742
  ["trap-request", 4096],
16568
- ["decision-viewed", 1024]
16743
+ ["decision-viewed", 1024],
16744
+ ["report-viewed", 1024]
16569
16745
  ]);
16570
16746
  var readJson4 = (f) => {
16571
16747
  try {
16572
- return JSON.parse(fs72.readFileSync(f, "utf8"));
16748
+ return JSON.parse(fs73.readFileSync(f, "utf8"));
16573
16749
  } catch {
16574
16750
  return void 0;
16575
16751
  }
16576
16752
  };
16577
16753
  var listDir = (d) => {
16578
16754
  try {
16579
- return fs72.readdirSync(d);
16755
+ return fs73.readdirSync(d);
16580
16756
  } catch {
16581
16757
  return [];
16582
16758
  }
16583
16759
  };
16584
16760
  var mtime = (f) => {
16585
16761
  try {
16586
- return fs72.statSync(f).mtimeMs;
16762
+ return fs73.statSync(f).mtimeMs;
16587
16763
  } catch {
16588
16764
  return 0;
16589
16765
  }
@@ -16592,7 +16768,7 @@ function assetPath(name) {
16592
16768
  for (const rel of [`../docs/assets/${name}`, `../../../docs/assets/${name}`]) {
16593
16769
  try {
16594
16770
  const p = fileURLToPath3(new URL(rel, import.meta.url));
16595
- if (fs72.existsSync(p)) return p;
16771
+ if (fs73.existsSync(p)) return p;
16596
16772
  } catch {
16597
16773
  }
16598
16774
  }
@@ -16601,7 +16777,7 @@ function assetPath(name) {
16601
16777
  function transcriptPath(harness, cwd, sessionId) {
16602
16778
  if (harness !== "claude" || !cwd || !sessionId) return void 0;
16603
16779
  const p = path62.join(os11.homedir(), ".claude", "projects", cwd.replace(/[^A-Za-z0-9-]/g, "-"), `${sessionId}.jsonl`);
16604
- return fs72.existsSync(p) ? p : void 0;
16780
+ return fs73.existsSync(p) ? p : void 0;
16605
16781
  }
16606
16782
  function messageAttachments(dir) {
16607
16783
  return [dir, path62.join(dir, "handled")].flatMap(
@@ -16613,7 +16789,7 @@ function trapMessages(trapId) {
16613
16789
  const parse2 = (file2, state) => {
16614
16790
  let raw;
16615
16791
  try {
16616
- raw = fs72.readFileSync(file2, "utf8");
16792
+ raw = fs73.readFileSync(file2, "utf8");
16617
16793
  } catch {
16618
16794
  return void 0;
16619
16795
  }
@@ -16703,7 +16879,7 @@ function dispatchRow(r, hold, staleSecs) {
16703
16879
  claimedBy: claim?.by,
16704
16880
  worker: evidence.worker,
16705
16881
  log,
16706
- 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()),
16707
16883
  ...awaitingOf(id),
16708
16884
  evidence: Object.keys(evidence).length > 0 ? evidence : void 0,
16709
16885
  ...r.bucket === "queued" ? {} : worktreeView(id, r.lane),
@@ -16796,7 +16972,7 @@ function serveReport(url, res) {
16796
16972
  const type = file2 && REPORT_IMAGE_TYPES[path62.extname(file2).toLowerCase()];
16797
16973
  if (!file2 || !type) return notFound(), true;
16798
16974
  res.writeHead(200, { ...headers, "content-type": type });
16799
- res.end(fs72.readFileSync(file2));
16975
+ res.end(fs73.readFileSync(file2));
16800
16976
  return true;
16801
16977
  }
16802
16978
  function trapNamesShown(traps, dispatches, attention, names) {
@@ -16839,7 +17015,7 @@ function serveAttachment(url, res) {
16839
17015
  const type = file2 && REPORT_IMAGE_TYPES[path62.extname(file2).toLowerCase()];
16840
17016
  let data;
16841
17017
  try {
16842
- 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);
16843
17019
  } catch {
16844
17020
  }
16845
17021
  if (!data || !type) {
@@ -17110,6 +17286,16 @@ function serveGlass(port, options = {}) {
17110
17286
  return reply(500, { ok: false, reason: "The view could not be stored." });
17111
17287
  }
17112
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
+ }
17113
17299
  case "trap-request": {
17114
17300
  const error = trapRequestError(body.payload, Object.keys(loadConfig().repos));
17115
17301
  if (error) return reply(400, { ok: false, reason: `Invalid trap request: ${error}.` });
@@ -17206,17 +17392,17 @@ function serveGlass(port, options = {}) {
17206
17392
  }
17207
17393
  if (req.url === "/lob.png" && lob) {
17208
17394
  res.writeHead(200, { "content-type": "image/png", "cache-control": "no-cache" });
17209
- res.end(fs72.readFileSync(lob));
17395
+ res.end(fs73.readFileSync(lob));
17210
17396
  } else if (req.url === "/star.png" && star) {
17211
17397
  res.writeHead(200, { "content-type": "image/png", "cache-control": "no-cache" });
17212
- res.end(fs72.readFileSync(star));
17398
+ res.end(fs73.readFileSync(star));
17213
17399
  } else if (req.url === "/lob-sprite.png" && sprite) {
17214
17400
  res.writeHead(200, { "content-type": "image/png", "cache-control": "no-cache" });
17215
- res.end(fs72.readFileSync(sprite));
17401
+ res.end(fs73.readFileSync(sprite));
17216
17402
  } else if (req.url === "/icon.png") {
17217
17403
  if (icon) {
17218
17404
  res.writeHead(200, { "content-type": "image/png", "cache-control": "no-cache" });
17219
- res.end(fs72.readFileSync(icon));
17405
+ res.end(fs73.readFileSync(icon));
17220
17406
  } else {
17221
17407
  res.writeHead(200, { "content-type": "image/svg+xml", "cache-control": "no-cache" });
17222
17408
  res.end(fallbackIcon);
@@ -17236,7 +17422,12 @@ function serveGlass(port, options = {}) {
17236
17422
  }
17237
17423
  const row = key ? reportRow(key) : void 0;
17238
17424
  if (m[2] === "/meta") {
17239
- 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
+ });
17240
17431
  res.end(JSON.stringify(row ?? { error: "report not found" }));
17241
17432
  return;
17242
17433
  }
@@ -17340,12 +17531,12 @@ function serveGlass(port, options = {}) {
17340
17531
 
17341
17532
  // apps/cli/src/park-renewal.ts
17342
17533
  init_dist2();
17343
- import * as fs74 from "node:fs";
17534
+ import * as fs75 from "node:fs";
17344
17535
  import * as path63 from "node:path";
17345
17536
 
17346
17537
  // apps/cli/src/restart.ts
17347
17538
  init_dist2();
17348
- import * as fs73 from "node:fs";
17539
+ import * as fs74 from "node:fs";
17349
17540
  var RESTART_WAIT_MS = 1e4;
17350
17541
  var HEARTBEAT_STALE_MS3 = 9e4;
17351
17542
  var INSTALL_COMMAND = {
@@ -17369,7 +17560,7 @@ function restartRefusal(opts) {
17369
17560
  }
17370
17561
  function readHeartbeat() {
17371
17562
  try {
17372
- const hb = JSON.parse(fs73.readFileSync(executorPath(), "utf8"));
17563
+ const hb = JSON.parse(fs74.readFileSync(executorPath(), "utf8"));
17373
17564
  return typeof hb.heartbeat === "string" ? hb : void 0;
17374
17565
  } catch {
17375
17566
  return void 0;
@@ -17430,7 +17621,7 @@ var RENEW_REASON = "lobstah: this trap is still signed on and its park timed out
17430
17621
  var statePath = (trapId) => path63.join(lobstahHome(), "park-renewals", `${trapId}.json`);
17431
17622
  function readState(trapId) {
17432
17623
  try {
17433
- const s = JSON.parse(fs74.readFileSync(statePath(trapId), "utf8"));
17624
+ const s = JSON.parse(fs75.readFileSync(statePath(trapId), "utf8"));
17434
17625
  return { instant: Number.isFinite(s.instant) ? s.instant : 0, at: s.at };
17435
17626
  } catch {
17436
17627
  return { instant: 0, at: "" };
@@ -17438,11 +17629,11 @@ function readState(trapId) {
17438
17629
  }
17439
17630
  function writeState(trapId, state) {
17440
17631
  const file2 = statePath(trapId);
17441
- fs74.mkdirSync(path63.dirname(file2), { recursive: true });
17442
- fs74.writeFileSync(file2, JSON.stringify(state));
17632
+ fs75.mkdirSync(path63.dirname(file2), { recursive: true });
17633
+ fs75.writeFileSync(file2, JSON.stringify(state));
17443
17634
  }
17444
17635
  function clearParkRenewal(trapId) {
17445
- fs74.rmSync(statePath(trapId), { force: true });
17636
+ fs75.rmSync(statePath(trapId), { force: true });
17446
17637
  }
17447
17638
  function daemonReachable(now2 = Date.now()) {
17448
17639
  const at = Date.parse(readHeartbeat()?.heartbeat ?? "");
@@ -17480,7 +17671,7 @@ function parkRenewal(trapId, parkedMs, now2 = Date.now(), daemonUp = daemonReach
17480
17671
  // apps/cli/src/repos.ts
17481
17672
  init_dist();
17482
17673
  init_dist2();
17483
- import * as fs75 from "node:fs";
17674
+ import * as fs76 from "node:fs";
17484
17675
  import * as path64 from "node:path";
17485
17676
  import { spawnSync as spawnSync10 } from "node:child_process";
17486
17677
  function git9(dir, ...args) {
@@ -17501,27 +17692,27 @@ function detectRepo(dir) {
17501
17692
  if (git9(abs, "rev-parse", "--is-inside-work-tree") !== "true") return void 0;
17502
17693
  const top = git9(abs, "rev-parse", "--show-toplevel");
17503
17694
  const canon2 = (p) => {
17504
- const real = path64.resolve(fs75.realpathSync.native(p));
17695
+ const real = path64.resolve(fs76.realpathSync.native(p));
17505
17696
  return process.platform === "win32" ? real.toLowerCase() : real;
17506
17697
  };
17507
17698
  if (!top || canon2(top) !== canon2(abs)) return void 0;
17508
17699
  const origin = git9(abs, "remote", "get-url", "origin");
17509
17700
  const headRef = git9(abs, "symbolic-ref", "--short", "refs/remotes/origin/HEAD");
17510
17701
  const trunk = headRef?.replace(/^origin\//, "") ?? git9(abs, "branch", "--show-current") ?? "main";
17511
- 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];
17512
17703
  return { key: path64.basename(abs), path: abs, trunk, origin, setup };
17513
17704
  }
17514
17705
  function scanForRepos(roots) {
17515
17706
  const found = [];
17516
17707
  for (const root of roots) {
17517
17708
  const abs = path64.resolve(root);
17518
- if (!fs75.existsSync(abs)) continue;
17709
+ if (!fs76.existsSync(abs)) continue;
17519
17710
  const self = detectRepo(abs);
17520
17711
  if (self) {
17521
17712
  found.push(self);
17522
17713
  continue;
17523
17714
  }
17524
- for (const entry of fs75.readdirSync(abs, { withFileTypes: true })) {
17715
+ for (const entry of fs76.readdirSync(abs, { withFileTypes: true })) {
17525
17716
  if (!entry.isDirectory() || entry.name.startsWith(".")) continue;
17526
17717
  const child = detectRepo(path64.join(abs, entry.name));
17527
17718
  if (child) found.push(child);
@@ -17530,8 +17721,8 @@ function scanForRepos(roots) {
17530
17721
  return found;
17531
17722
  }
17532
17723
  function configuredRepoKeys(file2 = configPath()) {
17533
- if (!fs75.existsSync(file2)) return /* @__PURE__ */ new Set();
17534
- const raw = parse(fs75.readFileSync(file2, "utf8"));
17724
+ if (!fs76.existsSync(file2)) return /* @__PURE__ */ new Set();
17725
+ const raw = parse(fs76.readFileSync(file2, "utf8"));
17535
17726
  return new Set(Object.keys(raw.repos ?? {}));
17536
17727
  }
17537
17728
  function appendRepoBlock(repo, opts = {}, file2 = configPath()) {
@@ -17539,19 +17730,19 @@ function appendRepoBlock(repo, opts = {}, file2 = configPath()) {
17539
17730
  if (repo.origin) lines.push(`origin = ${JSON.stringify(repo.origin)}`);
17540
17731
  if (repo.setup) lines.push(`setup = [${repo.setup.map((s) => JSON.stringify(s)).join(", ")}]`);
17541
17732
  if (opts.pickup) lines.push(`pickup = true`);
17542
- fs75.appendFileSync(file2, `${lines.join("\n")}
17733
+ fs76.appendFileSync(file2, `${lines.join("\n")}
17543
17734
  `);
17544
17735
  }
17545
17736
 
17546
17737
  // apps/cli/src/pr-watch.ts
17547
17738
  init_dist();
17548
17739
  init_dist2();
17549
- import * as fs77 from "node:fs";
17740
+ import * as fs78 from "node:fs";
17550
17741
 
17551
17742
  // apps/cli/src/pr-stack-watch.ts
17552
17743
  init_dist2();
17553
17744
  import { spawnSync as spawnSync11 } from "node:child_process";
17554
- import * as fs76 from "node:fs";
17745
+ import * as fs77 from "node:fs";
17555
17746
  import * as path65 from "node:path";
17556
17747
  var ghStackLink = (ref, branch, direction) => {
17557
17748
  const res = spawnSync11(
@@ -17581,7 +17772,7 @@ function discoverPrStack(ref, opts = {}) {
17581
17772
  const file2 = path65.join(lobstahHome(), "pr-stack-links.json");
17582
17773
  let checked;
17583
17774
  try {
17584
- checked = JSON.parse(fs76.readFileSync(file2, "utf8"));
17775
+ checked = JSON.parse(fs77.readFileSync(file2, "utf8"));
17585
17776
  } catch {
17586
17777
  checked = {};
17587
17778
  }
@@ -17618,15 +17809,15 @@ function discoverPrStack(ref, opts = {}) {
17618
17809
  }
17619
17810
  }
17620
17811
  const tmp = `${file2}.tmp-${process.pid}`;
17621
- fs76.writeFileSync(tmp, JSON.stringify(checked));
17622
- fs76.renameSync(tmp, file2);
17812
+ fs77.writeFileSync(tmp, JSON.stringify(checked));
17813
+ fs77.renameSync(tmp, file2);
17623
17814
  }
17624
17815
 
17625
17816
  // apps/cli/src/pr-watch.ts
17626
17817
  var shq = (s) => `'${s.replace(/'/g, `'\\''`)}'`;
17627
17818
  function selfCommand() {
17628
17819
  if (COMPILED_BINARY) return shq(process.execPath);
17629
- const entry = process.argv[1] ? fs77.realpathSync(process.argv[1]) : "lobstah";
17820
+ const entry = process.argv[1] ? fs78.realpathSync(process.argv[1]) : "lobstah";
17630
17821
  return `${shq(process.execPath)} ${shq(entry)}`;
17631
17822
  }
17632
17823
  function prCheckCommand(ref, forId) {
@@ -17676,7 +17867,7 @@ function backfillPrWatches(opts = {}) {
17676
17867
  for (const bucket of ["active", "done"]) {
17677
17868
  let ids;
17678
17869
  try {
17679
- ids = fs77.readdirSync(dirs[bucket]).filter((id) => !id.startsWith("."));
17870
+ ids = fs78.readdirSync(dirs[bucket]).filter((id) => !id.startsWith("."));
17680
17871
  } catch {
17681
17872
  continue;
17682
17873
  }
@@ -17688,7 +17879,7 @@ function backfillPrWatches(opts = {}) {
17688
17879
  }
17689
17880
  let files;
17690
17881
  try {
17691
- files = fs77.readdirSync(dirs.state).filter((f) => f.endsWith(".evidence"));
17882
+ files = fs78.readdirSync(dirs.state).filter((f) => f.endsWith(".evidence"));
17692
17883
  } catch {
17693
17884
  continue;
17694
17885
  }
@@ -17750,7 +17941,7 @@ function backfillPrWatches(opts = {}) {
17750
17941
  return rows;
17751
17942
  }
17752
17943
  function laneOf4(id) {
17753
- 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);
17754
17945
  }
17755
17946
  var FIRST_SIGHT_NOTICE_MS = 24 * 60 * 60 * 1e3;
17756
17947
  function observePr(ref, view, opts = {}) {
@@ -17794,7 +17985,7 @@ function cancelQueuedRepairs(key) {
17794
17985
  }
17795
17986
  function rawConfig() {
17796
17987
  try {
17797
- return parse(fs77.readFileSync(configPath(), "utf8"));
17988
+ return parse(fs78.readFileSync(configPath(), "utf8"));
17798
17989
  } catch {
17799
17990
  return {};
17800
17991
  }
@@ -17969,7 +18160,7 @@ function finishResolvedWaits(log = () => {
17969
18160
  // apps/cli/src/pr-stack.ts
17970
18161
  init_dist2();
17971
18162
  import { spawnSync as spawnSync12 } from "node:child_process";
17972
- import * as fs78 from "node:fs";
18163
+ import * as fs79 from "node:fs";
17973
18164
  import * as path66 from "node:path";
17974
18165
  var defaultRun = (cmd, args, cwd) => {
17975
18166
  const res = spawnSync12(cmd, args, { cwd, encoding: "utf8", timeout: cmd === "gh" ? 1e4 : 3e3, windowsHide: true });
@@ -17982,7 +18173,7 @@ function ghStackPrs(cwd, keys, run4) {
17982
18173
  if (res.status !== 0 || !res.stdout.trim()) continue;
17983
18174
  let state;
17984
18175
  try {
17985
- 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"));
17986
18177
  } catch {
17987
18178
  continue;
17988
18179
  }
@@ -18343,7 +18534,7 @@ function batchText(results) {
18343
18534
  // apps/cli/src/soak-worktree.ts
18344
18535
  init_dist2();
18345
18536
  init_dist3();
18346
- import * as fs79 from "node:fs";
18537
+ import * as fs80 from "node:fs";
18347
18538
  import { spawnSync as spawnSync13 } from "node:child_process";
18348
18539
  function branchExists(repo, branch) {
18349
18540
  return spawnSync13("git", ["-C", repo.path, "rev-parse", "--verify", "-q", `refs/heads/${branch}`], { stdio: "ignore" }).status === 0;
@@ -18363,7 +18554,7 @@ async function createSoakWorktree(opts) {
18363
18554
  );
18364
18555
  }
18365
18556
  }
18366
- 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}`));
18367
18558
  let trapId = opts.trapId ?? newTrapId();
18368
18559
  if (opts.trapId !== void 0 && taken(trapId)) {
18369
18560
  throw new Error(`could not create a worktree for repo ${opts.repoKey}: soak-${trapId} or its branch already exists`);
@@ -18388,7 +18579,7 @@ async function discardSoakWorktree(repo, made) {
18388
18579
  // apps/cli/src/forget.ts
18389
18580
  init_dist2();
18390
18581
  init_dist3();
18391
- import * as fs80 from "node:fs";
18582
+ import * as fs81 from "node:fs";
18392
18583
  import { spawnSync as spawnSync14 } from "node:child_process";
18393
18584
  var ForgetRefusedError = class extends Error {
18394
18585
  };
@@ -18409,7 +18600,7 @@ async function forgetTrap(address, cfg, opts = {}) {
18409
18600
  if (readReservation(entry.trapId)) throw new ForgetRefusedError(`${label} is starting (reserved) \u2014 withdraw it with \`lobstah stow --wt ${entry.name}\` first`);
18410
18601
  const repo = entry.repo ? cfg.repos[entry.repo] : void 0;
18411
18602
  const gitDir = entry.gitDir ?? (repo ? gitCommonDir(repo.path) : void 0);
18412
- const present = fs80.existsSync(entry.worktree);
18603
+ const present = fs81.existsSync(entry.worktree);
18413
18604
  const anchored = present && readTrapAnchor(entry.worktree)?.trapId === entry.trapId;
18414
18605
  const unsaved = anchored ? unsavedFiles(entry.worktree) : [];
18415
18606
  if (unsaved === void 0 && !opts.force) throw new ForgetRefusedError(`cannot read git status in ${entry.worktree} \u2014 inspect it, or pass --force`);
@@ -18456,7 +18647,7 @@ Push or merge it, or pass --force to forget the trap anyway.`);
18456
18647
  throw new Error(`could not remove ${entry.worktree}: ${removal.reason} \u2014 the trap is not forgotten`);
18457
18648
  }
18458
18649
  } else {
18459
- if (anchored) fs80.rmSync(`${entry.worktree}/${TRAP_ANCHOR_FILE}`, { force: true });
18650
+ if (anchored) fs81.rmSync(`${entry.worktree}/${TRAP_ANCHOR_FILE}`, { force: true });
18460
18651
  result.worktreeNote = "soak did not create it: left in place, its trap anchor removed";
18461
18652
  }
18462
18653
  }
@@ -18532,7 +18723,7 @@ async function setTerminalTitle(win, title, options = {}) {
18532
18723
 
18533
18724
  // apps/cli/src/beat.ts
18534
18725
  init_dist2();
18535
- import * as fs81 from "node:fs";
18726
+ import * as fs82 from "node:fs";
18536
18727
  import * as path67 from "node:path";
18537
18728
 
18538
18729
  // apps/cli/src/beat-pr.ts
@@ -18584,9 +18775,9 @@ function probeTrapPr(reg, opts = {}) {
18584
18775
  function logError(err) {
18585
18776
  try {
18586
18777
  const dir = path67.join(lobstahHome(), "logs");
18587
- fs81.mkdirSync(dir, { recursive: true });
18778
+ fs82.mkdirSync(dir, { recursive: true });
18588
18779
  const msg = err instanceof Error ? err.message : String(err);
18589
- 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)}
18590
18781
  `);
18591
18782
  } catch {
18592
18783
  }
@@ -19414,7 +19605,7 @@ function emitNotices(notices, sessionId) {
19414
19605
  for (const n of notices) {
19415
19606
  const answer = n.kind === "decision-answer" && n.refId ? readRequest(n.refId) : void 0;
19416
19607
  if (answer) console.log(toonKV(decisionEventFields(answer)));
19417
- 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 }));
19418
19609
  }
19419
19610
  console.log(
19420
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.`
@@ -19432,7 +19623,7 @@ function noticeLine(n, prefix = "notice ") {
19432
19623
  const answer = n.kind === "decision-answer" && n.refId ? readRequest(n.refId) : void 0;
19433
19624
  if (answer) return decisionLine(answer);
19434
19625
  const r = trapRequestFields(n);
19435
- 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}` : ""}`;
19436
19627
  }
19437
19628
  function replyLine(r) {
19438
19629
  return `- reply ${r.id} (${r.entry.verb})${r.entry.note ? ` \u2014 ${r.entry.note}` : ""} \xB7 sent: ${r.sent}`;
@@ -19454,7 +19645,7 @@ function emitWatchAttention(attns, sessionId) {
19454
19645
  function findLane(id) {
19455
19646
  for (const lane of ["work", "chore"]) {
19456
19647
  const d = laneDirs(lane);
19457
- 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`))) {
19458
19649
  return lane;
19459
19650
  }
19460
19651
  }
@@ -19463,7 +19654,7 @@ function findLane(id) {
19463
19654
  function anythingInFlight() {
19464
19655
  const dispatches = ["work", "chore"].some((lane) => {
19465
19656
  const dirs = laneDirs(lane);
19466
- 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(".")));
19467
19658
  });
19468
19659
  return dispatches || listWatches().some((watch2) => watch2.owner === "man");
19469
19660
  }
@@ -19480,7 +19671,7 @@ function dispatchChain(target) {
19480
19671
  for (const lane of ["work", "chore"]) {
19481
19672
  const dirs = laneDirs(lane);
19482
19673
  for (const bucket of ["queue", "active", "done"]) {
19483
- for (const entry of fs86.readdirSync(dirs[bucket])) {
19674
+ for (const entry of fs87.readdirSync(dirs[bucket])) {
19484
19675
  if (entry.startsWith(".")) continue;
19485
19676
  const id = bucket === "queue" ? entry.replace(/\.json$/, "") : entry;
19486
19677
  const descriptor = storedDescriptor(id, lane);
@@ -19510,7 +19701,7 @@ function liveChainMember(member) {
19510
19701
  }
19511
19702
  if (isParked(member.id, member.lane)) return true;
19512
19703
  try {
19513
- 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"));
19514
19705
  return pidAlive2(runner.pid, runner.processStartTime);
19515
19706
  } catch {
19516
19707
  return false;
@@ -19519,7 +19710,7 @@ function liveChainMember(member) {
19519
19710
  function rowsFor(lane, bucket) {
19520
19711
  const dir = laneDirs(lane)[bucket];
19521
19712
  const staleSecs = wedgeSecs2();
19522
- 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);
19523
19714
  const sliced = bucket === "done" ? entries.slice(0, 10) : entries;
19524
19715
  return sliced.map(({ f, m }) => {
19525
19716
  const id = f.replace(/\.json$/, "");
@@ -19544,7 +19735,7 @@ function rowsFor(lane, bucket) {
19544
19735
  function dispatchTrap(id, lane, names) {
19545
19736
  const claimedBy = (bucket) => {
19546
19737
  try {
19547
- 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;
19548
19739
  } catch {
19549
19740
  return void 0;
19550
19741
  }
@@ -19696,7 +19887,7 @@ ${usageFor(cmd)}`);
19696
19887
  const d = {
19697
19888
  id: opt("--id") ?? randomUUID8(),
19698
19889
  repo,
19699
- brief: briefText ?? fs86.readFileSync(briefFile, "utf8"),
19890
+ brief: briefText ?? fs87.readFileSync(briefFile, "utf8"),
19700
19891
  harness: opt("--harness"),
19701
19892
  model: opt("--model"),
19702
19893
  effort: opt("--effort"),
@@ -19753,7 +19944,7 @@ ${usageFor(cmd)}`);
19753
19944
  const since = queuedAt(id, lane);
19754
19945
  const claimedAt = claim?.at;
19755
19946
  const state = displayState({ log, lastEventAt: lastEventAt(id, lane), queued: since !== void 0, claimedAt });
19756
- const live = fs86.existsSync(path72.join(laneDirs(lane).active, id));
19947
+ const live = fs87.existsSync(path72.join(laneDirs(lane).active, id));
19757
19948
  const activity = live ? activityView(readActivity(id, lane), wedgeSecs2()) : void 0;
19758
19949
  const waitingNow = live ? waitingView(log.at(-1)) : void 0;
19759
19950
  console.log(
@@ -19793,8 +19984,8 @@ ${usageFor(cmd)}`);
19793
19984
  if (!id) throw new Error("logs requires a dispatch id");
19794
19985
  const lane = findLane(id);
19795
19986
  const file2 = eventsPath(id, lane);
19796
- if (fs86.existsSync(file2)) {
19797
- const raw = fs86.readFileSync(file2, "utf8");
19987
+ if (fs87.existsSync(file2)) {
19988
+ const raw = fs87.readFileSync(file2, "utf8");
19798
19989
  const lines = raw.split("\n").filter((l) => l.length > 0);
19799
19990
  const LIMIT = 50;
19800
19991
  if (!has("--full") && lines.length > LIMIT) {
@@ -19805,15 +19996,15 @@ ${usageFor(cmd)}`);
19805
19996
  }
19806
19997
  }
19807
19998
  if (has("--follow")) {
19808
- let size = fs86.existsSync(file2) ? fs86.statSync(file2).size : 0;
19999
+ let size = fs87.existsSync(file2) ? fs87.statSync(file2).size : 0;
19809
20000
  setInterval(() => {
19810
- if (!fs86.existsSync(file2)) return;
19811
- const now2 = fs86.statSync(file2).size;
20001
+ if (!fs87.existsSync(file2)) return;
20002
+ const now2 = fs87.statSync(file2).size;
19812
20003
  if (now2 > size) {
19813
- const fd = fs86.openSync(file2, "r");
20004
+ const fd = fs87.openSync(file2, "r");
19814
20005
  const buf = Buffer.alloc(now2 - size);
19815
- fs86.readSync(fd, buf, 0, buf.length, size);
19816
- fs86.closeSync(fd);
20006
+ fs87.readSync(fd, buf, 0, buf.length, size);
20007
+ fs87.closeSync(fd);
19817
20008
  process.stdout.write(buf.toString("utf8"));
19818
20009
  size = now2;
19819
20010
  }
@@ -19911,7 +20102,7 @@ ${[text, block].filter(Boolean).join("\n\n")}`,
19911
20102
  const claimFile = path72.join(laneDirs(origin.lane)[origin.bucket], origin.id, "claim.json");
19912
20103
  let claimedBy;
19913
20104
  try {
19914
- claimedBy = JSON.parse(fs86.readFileSync(claimFile, "utf8")).by;
20105
+ claimedBy = JSON.parse(fs87.readFileSync(claimFile, "utf8")).by;
19915
20106
  } catch {
19916
20107
  claimedBy = readEvidence(origin.id, origin.lane).deliveredTo;
19917
20108
  }
@@ -19995,7 +20186,7 @@ ${usageFor("report")}`);
19995
20186
  if (verb === "done") {
19996
20187
  const claim = readSessionClaim(id, lane);
19997
20188
  let ownCheckout = false;
19998
- if (claim?.by.startsWith("wt:") && claim.worktree && fs86.existsSync(claim.worktree)) {
20189
+ if (claim?.by.startsWith("wt:") && claim.worktree && fs87.existsSync(claim.worktree)) {
19999
20190
  const rel = path72.relative(canon(claim.worktree), canon(process.cwd()));
20000
20191
  const inside = rel === "" || !rel.startsWith("..") && !path72.isAbsolute(rel);
20001
20192
  const who = callerSession(opt("--session"))?.id;
@@ -20017,7 +20208,7 @@ ${usageFor("report")}`);
20017
20208
  const branches = [.../* @__PURE__ */ new Set([ev.branch, ...(ev.pushes ?? []).map((p) => p.branch)])].filter(
20018
20209
  (b) => !!b && b !== "HEAD"
20019
20210
  );
20020
- 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 });
20021
20212
  const all = dispatchPrUrls({ prUrl, prUrls: [...reportPrs, ...dispatchPrUrls(ev)] });
20022
20213
  mergeEvidence(id, lane, { prUrl, ...all.length > 1 ? { prUrls: all } : {} });
20023
20214
  }
@@ -20083,7 +20274,7 @@ ${usageFor("report")}`);
20083
20274
  );
20084
20275
  }
20085
20276
  const worktree = dispatchWorktree(id, lane).path;
20086
- const cwd = fs86.existsSync(worktree) ? worktree : process.cwd();
20277
+ const cwd = fs87.existsSync(worktree) ? worktree : process.cwd();
20087
20278
  const invocation = harness === "codex" ? codexInvocation(["resume", sessionId]) : { file: "claude", argv: ["--resume", sessionId] };
20088
20279
  if (!invocation) throw new Error("no codex CLI found \u2014 neither on PATH nor vendored by @openai/codex-sdk");
20089
20280
  if (has("--print")) {
@@ -20102,9 +20293,9 @@ ${usageFor("report")}`);
20102
20293
  }
20103
20294
  const lane = findLane(id);
20104
20295
  const activeDir = path72.join(laneDirs(lane).active, id);
20105
- 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`);
20106
20297
  const descFile = path72.join(activeDir, "descriptor.json");
20107
- const descriptor = JSON.parse(fs86.readFileSync(descFile, "utf8"));
20298
+ const descriptor = JSON.parse(fs87.readFileSync(descFile, "utf8"));
20108
20299
  const fromHarness = resolveSessionHarness(id, loadConfig(), lane).harness ?? descriptor.harness ?? "default";
20109
20300
  for (const key of ["harness", "model", "effort"]) {
20110
20301
  const v = opt(`--${key}`);
@@ -20112,14 +20303,14 @@ ${usageFor("report")}`);
20112
20303
  }
20113
20304
  if (opt("--harness")) descriptor.harnessExplicit = true;
20114
20305
  if (opt("--model")) descriptor.modelExplicit = true;
20115
- fs86.writeFileSync(descFile, JSON.stringify(descriptor, null, 2));
20306
+ fs87.writeFileSync(descFile, JSON.stringify(descriptor, null, 2));
20116
20307
  const worktree = dispatchWorktree(id, lane).path;
20117
- fs86.writeFileSync(path72.join(activeDir, "handoff"), handoffNote(fromHarness, worktreeProgress(worktree)));
20308
+ fs87.writeFileSync(path72.join(activeDir, "handoff"), handoffNote(fromHarness, worktreeProgress(worktree)));
20118
20309
  const runnerFile = path72.join(activeDir, "runner.json");
20119
- if (fs86.existsSync(runnerFile)) {
20120
- const runner = JSON.parse(fs86.readFileSync(runnerFile, "utf8"));
20310
+ if (fs87.existsSync(runnerFile)) {
20311
+ const runner = JSON.parse(fs87.readFileSync(runnerFile, "utf8"));
20121
20312
  if (pidAlive2(runner.pid, runner.processStartTime)) killGroup(runner.pid, "SIGKILL");
20122
- fs86.unlinkSync(runnerFile);
20313
+ fs87.unlinkSync(runnerFile);
20123
20314
  }
20124
20315
  appendStatus(id, lane, "working", `swapped to ${descriptor.harness ?? "default"} \u2014 awaiting respawn`);
20125
20316
  console.log(toonKV({ id, harness: descriptor.harness ?? "default", model: descriptor.model, swapped: true }));
@@ -20180,7 +20371,7 @@ ${usageFor("report")}`);
20180
20371
  console.log(`commits[${ev.commits.length}]:`);
20181
20372
  for (const c of ev.commits) console.log(` ${c}`);
20182
20373
  }
20183
- if (fs86.existsSync(path72.join(laneDirs(lane).done, id))) {
20374
+ if (fs87.existsSync(path72.join(laneDirs(lane).done, id))) {
20184
20375
  console.log(toonHelp([`lobstah send ${id} "<instruction>" (wakes it as a follow-up in its worktree)`]));
20185
20376
  }
20186
20377
  const attachments = storedDescriptor(id, lane)?.attachments ?? [];
@@ -20720,7 +20911,7 @@ ${errs.map((e) => `- ${e}`).join("\n")}`);
20720
20911
  const keys = holdCancelledRepair(id);
20721
20912
  return keys.length > 0 ? { repairHeld: `${keys.join(", ")} (lobstah watch release <key> frees it)` } : {};
20722
20913
  };
20723
- if (fs86.existsSync(path72.join(laneDirs(lane).active, id))) {
20914
+ if (fs87.existsSync(path72.join(laneDirs(lane).active, id))) {
20724
20915
  requestCancel(id, lane);
20725
20916
  console.log(toonKV({ id, cancel: "requested", note: "the claimant (daemon or trap) winds it down at its next check", ...repairHeld() }));
20726
20917
  break;
@@ -20729,7 +20920,7 @@ ${errs.map((e) => `- ${e}`).join("\n")}`);
20729
20920
  console.log(toonKV({ id, cancel: "finalized", note: "cancelled before claim \u2014 recorded as failed in done/", ...repairHeld() }));
20730
20921
  break;
20731
20922
  }
20732
- if (fs86.existsSync(path72.join(laneDirs(lane).active, id))) {
20923
+ if (fs87.existsSync(path72.join(laneDirs(lane).active, id))) {
20733
20924
  requestCancel(id, lane);
20734
20925
  console.log(toonKV({ id, cancel: "requested", note: "claimed while cancelling \u2014 the claimant winds it down", ...repairHeld() }));
20735
20926
  break;
@@ -20856,17 +21047,17 @@ ${usageFor("man:wait")}`);
20856
21047
  const file2 = has("--global") ? path72.join(os13.homedir(), ".claude", "settings.json") : path72.join(".claude", has("--shared") ? "settings.json" : "settings.local.json");
20857
21048
  let existing;
20858
21049
  try {
20859
- existing = JSON.parse(fs86.readFileSync(file2, "utf8"));
21050
+ existing = JSON.parse(fs87.readFileSync(file2, "utf8"));
20860
21051
  } catch {
20861
21052
  existing = void 0;
20862
21053
  }
20863
21054
  const { settings, changed } = mergeHaulHook(existing);
20864
21055
  if (changed) {
20865
- fs86.mkdirSync(path72.dirname(file2), { recursive: true });
20866
- 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)}
20867
21058
  `);
20868
21059
  }
20869
- if (has("--marker")) fs86.writeFileSync(".lobstah-man", "");
21060
+ if (has("--marker")) fs87.writeFileSync(".lobstah-man", "");
20870
21061
  console.log(
20871
21062
  toonKV({
20872
21063
  hook: "lobstah man haul",
@@ -20924,7 +21115,7 @@ ${usageFor("man:wait")}`);
20924
21115
  if (helm) releaseHeldQuestions(helm);
20925
21116
  const cfgHaul = loadConfig();
20926
21117
  if (!helm && liveHelms(cfgHaul.helm.ttlSecs * 1e3).length > 0) break;
20927
- 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;
20928
21119
  const dueDigest = () => helm ? dueHelmDigest(helm, cfgHaul.helm.reportSecs) : void 0;
20929
21120
  const blockDigest = (d) => {
20930
21121
  advanceCursor(helm.grounds, d.now);
@@ -21134,7 +21325,7 @@ ${behind}` : "");
21134
21325
  }
21135
21326
  repoKey2 = reservation.repo;
21136
21327
  if (reservation.worktree !== void 0) {
21137
- if (!fs86.existsSync(reservation.worktree) || trapIdAt(reservation.worktree) !== reservation.trapId) {
21328
+ if (!fs87.existsSync(reservation.worktree) || trapIdAt(reservation.worktree) !== reservation.trapId) {
21138
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`);
21139
21330
  }
21140
21331
  worktree = canon(reservation.worktree);
@@ -21173,7 +21364,7 @@ ${behind}` : "");
21173
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}.`
21174
21365
  );
21175
21366
  }
21176
- if (!fs86.existsSync(mine.worktree)) {
21367
+ if (!fs87.existsSync(mine.worktree)) {
21177
21368
  throw new Error(
21178
21369
  `this session's trap wt:${mine.trapId} is anchored at ${mine.worktree}, which no longer exists. Run \`lobstah stow\`, then soak again.`
21179
21370
  );
@@ -21357,7 +21548,7 @@ ${usageFor("stow")}`);
21357
21548
  const anchor = wtDir !== void 0 ? readTrapAnchor(wtDir) : void 0;
21358
21549
  const created = reg?.createdWorktree === true || anchor?.createdBy === "soak";
21359
21550
  let worktreeOut = {};
21360
- if (wtDir !== void 0 && fs86.existsSync(wtDir)) {
21551
+ if (wtDir !== void 0 && fs87.existsSync(wtDir)) {
21361
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;
21362
21553
  if (keepReason) {
21363
21554
  worktreeOut = { worktree: "kept", path: wtDir, reason: keepReason };
@@ -21604,7 +21795,7 @@ ${usageFor("pet")}`);
21604
21795
  path: r.path,
21605
21796
  trunk: r.trunk,
21606
21797
  pickup: r.pickup ?? false,
21607
- exists: fs86.existsSync(r.path)
21798
+ exists: fs87.existsSync(r.path)
21608
21799
  }));
21609
21800
  console.log(toonTable("repos", rows, ["key", "path", "trunk", "pickup", "exists"]));
21610
21801
  break;
@@ -21722,7 +21913,7 @@ ${usageFor("pet")}`);
21722
21913
  break;
21723
21914
  }
21724
21915
  case "init": {
21725
- if (!fs86.existsSync(configPath())) {
21916
+ if (!fs87.existsSync(configPath())) {
21726
21917
  const exampleRepo = has("--scan") ? "" : `[repos.example]
21727
21918
  path = "~/src/example"
21728
21919
  trunk = "main"
@@ -21731,7 +21922,7 @@ trunk = "main"
21731
21922
  # pickup = true # opt into [pickup.github] multi-repo tracker pickup
21732
21923
 
21733
21924
  `;
21734
- fs86.writeFileSync(
21925
+ fs87.writeFileSync(
21735
21926
  configPath(),
21736
21927
  `# lobstah workspace definitions \u2014 the descriptor's repo key resolves here.
21737
21928
  # Top-level keys must come before any [section].
@@ -21769,7 +21960,7 @@ wallClockSecs = 3600
21769
21960
  let reference = "https://github.com/aequitas-labs/lobstah/blob/main/docs/configuration.md";
21770
21961
  try {
21771
21962
  const local = fileURLToPath6(new URL("../docs/configuration.md", import.meta.url));
21772
- if (fs86.existsSync(local)) reference = local;
21963
+ if (fs87.existsSync(local)) reference = local;
21773
21964
  } catch {
21774
21965
  }
21775
21966
  console.log(