@gethmy/agent 1.47.2 → 1.47.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/dist/cli.js +723 -576
  2. package/dist/index.js +694 -548
  3. package/package.json +3 -3
package/dist/index.js CHANGED
@@ -5672,6 +5672,8 @@ function decideAutoMergeAction(input) {
5672
5672
  if (headSha && reviewedSha !== headSha)
5673
5673
  return "rereview";
5674
5674
  }
5675
+ if (input.mergeConflict)
5676
+ return "stamp-conflict";
5675
5677
  const independent = config.independentReview;
5676
5678
  if (independent.enabled) {
5677
5679
  const state = input.independentReview ?? "absent";
@@ -5740,6 +5742,9 @@ async function stampFindingsExhausted(client, card, maxRounds) {
5740
5742
  async function stampReviewIncomplete(client, card) {
5741
5743
  await stampForHumanOnce(client, card, "Independent review did not complete (no verdict)");
5742
5744
  }
5745
+ async function stampMergeConflict(client, card) {
5746
+ await stampForHumanOnce(client, card, "Merge conflict with the base branch");
5747
+ }
5743
5748
  async function removeApprovedLabel(client, card, resolvedLabels, approvedLabel) {
5744
5749
  const name = approvedLabel.toLowerCase();
5745
5750
  const obj = resolvedLabels.find((l) => l.name.toLowerCase() === name);
@@ -5760,7 +5765,14 @@ async function attemptAutoMerge(deps) {
5760
5765
  return "foreign-pr";
5761
5766
  }
5762
5767
  const independent = autoMerge.independentReview;
5763
- const { ciStatus, headSha, baseRefName, independentReview, reviewVerdict } = await getPrStatus(prUrl, cwd, provider, independent.enabled ? independent.checkName : undefined, independent.enabled ? independent.verdict.checkName : undefined);
5768
+ const {
5769
+ ciStatus,
5770
+ headSha,
5771
+ baseRefName,
5772
+ mergeConflict,
5773
+ independentReview,
5774
+ reviewVerdict
5775
+ } = await getPrStatus(prUrl, cwd, provider, independent.enabled ? independent.checkName : undefined, independent.enabled ? independent.verdict.checkName : undefined);
5764
5776
  deps.onPrRead?.({ headSha, baseRefName });
5765
5777
  if (driver === "interactive" && baseRefName !== config.worktree.baseBranch) {
5766
5778
  if (baseRefName !== null) {
@@ -5794,7 +5806,8 @@ async function attemptAutoMerge(deps) {
5794
5806
  independentReview,
5795
5807
  reviewVerdict,
5796
5808
  reviewRequestedSha,
5797
- blockingVerdictRounds
5809
+ blockingVerdictRounds,
5810
+ mergeConflict
5798
5811
  });
5799
5812
  switch (action) {
5800
5813
  case "wait":
@@ -5814,6 +5827,10 @@ async function attemptAutoMerge(deps) {
5814
5827
  log13.debug(TAG13, `#${card.short_id} waiting (ci=${ciStatus}, review=${independentReview}, verdict=${reviewVerdict})`);
5815
5828
  }
5816
5829
  return action;
5830
+ case "stamp-conflict":
5831
+ log13.info(TAG13, `#${card.short_id} PR conflicts with its base — flagging for human. Resolve: gh pr checkout <n> && git fetch origin ${baseRefName ?? "main"} && git merge origin/${baseRefName ?? "main"}`);
5832
+ await stampMergeConflict(client, card);
5833
+ return action;
5817
5834
  case "stamp-failure":
5818
5835
  log13.info(TAG13, `#${card.short_id} CI failed — flagging for human`);
5819
5836
  await stampCiFailure(client, card);
@@ -5974,6 +5991,28 @@ var init_merge_complete = __esm(() => {
5974
5991
  execFileAsync = promisify(execFile);
5975
5992
  });
5976
5993
 
5994
+ // src/pr-state-sync.ts
5995
+ import {
5996
+ log as log15,
5997
+ readPullRequestSnapshot
5998
+ } from "@gethmy/harness";
5999
+ async function syncPullRequestState(deps) {
6000
+ try {
6001
+ const snapshot = await readPullRequestSnapshot(deps.prUrl, deps.cwd, deps.provider);
6002
+ if (!snapshot)
6003
+ return;
6004
+ await deps.client.upsertCardPullRequest(deps.cardId, {
6005
+ url: deps.prUrl,
6006
+ state: snapshot.state,
6007
+ ci_status: snapshot.ciStatus
6008
+ });
6009
+ } catch (err) {
6010
+ log15.warn(TAG15, `Could not write PR state for ${deps.prUrl}: ${err instanceof Error ? err.message : String(err)}`);
6011
+ }
6012
+ }
6013
+ var TAG15 = "pr-state-sync";
6014
+ var init_pr_state_sync = () => {};
6015
+
5977
6016
  // src/merge-monitor.ts
5978
6017
  var exports_merge_monitor = {};
5979
6018
  __export(exports_merge_monitor, {
@@ -5982,7 +6021,7 @@ __export(exports_merge_monitor, {
5982
6021
  import {
5983
6022
  checkPrMergeStatus,
5984
6023
  detectGitProvider as detectGitProvider2,
5985
- log as log15,
6024
+ log as log16,
5986
6025
  resolvePrUrl
5987
6026
  } from "@gethmy/harness";
5988
6027
 
@@ -6016,7 +6055,7 @@ class MergeMonitor {
6016
6055
  clearTimeout(this.timer);
6017
6056
  this.timer = null;
6018
6057
  }
6019
- log15.info(TAG15, "Merge monitor stopped");
6058
+ log16.info(TAG16, "Merge monitor stopped");
6020
6059
  }
6021
6060
  async runOnce() {
6022
6061
  await this.tick();
@@ -6052,21 +6091,28 @@ class MergeMonitor {
6052
6091
  }
6053
6092
  }
6054
6093
  if (candidatesWithLabels.length === 0) {
6055
- log15.debug(TAG15, "No Ready to Merge cards found");
6094
+ log16.debug(TAG16, "No Ready to Merge cards found");
6056
6095
  return;
6057
6096
  }
6058
6097
  const batch = candidatesWithLabels.slice(0, 5);
6059
- log15.debug(TAG15, `Checking ${batch.length} Ready to Merge card(s)`);
6098
+ log16.debug(TAG16, `Checking ${batch.length} Ready to Merge card(s)`);
6060
6099
  const results = await Promise.allSettled(batch.map(async ({ card, labels }) => {
6061
6100
  const branchName = extractBranchFromDescription(card.description);
6062
6101
  const prUrl = resolvePrUrl(card.description ?? null, branchName, this.cwd, this.provider);
6063
6102
  if (!prUrl) {
6064
- log15.debug(TAG15, `#${card.short_id} has no resolvable PR — skipping`);
6103
+ log16.debug(TAG16, `#${card.short_id} has no resolvable PR — skipping`);
6065
6104
  return;
6066
6105
  }
6067
6106
  const state = await checkPrMergeStatus(prUrl, this.cwd, this.provider);
6107
+ await syncPullRequestState({
6108
+ client: this.client,
6109
+ cardId: card.id,
6110
+ prUrl,
6111
+ cwd: this.cwd,
6112
+ provider: this.provider
6113
+ });
6068
6114
  if (state === "merged") {
6069
- log15.info(TAG15, `#${card.short_id} PR merged — completing`);
6115
+ log16.info(TAG16, `#${card.short_id} PR merged — completing`);
6070
6116
  await this.completeMergedCard(card, labels);
6071
6117
  } else if (state === "open") {
6072
6118
  await attemptAutoMerge({
@@ -6080,16 +6126,16 @@ class MergeMonitor {
6080
6126
  ...this.repairContext ? { repair: this.repairContext } : {}
6081
6127
  });
6082
6128
  } else {
6083
- log15.debug(TAG15, `#${card.short_id} PR state: ${state}`);
6129
+ log16.debug(TAG16, `#${card.short_id} PR state: ${state}`);
6084
6130
  }
6085
6131
  }));
6086
6132
  for (const r of results) {
6087
6133
  if (r.status === "rejected") {
6088
- log15.warn(TAG15, `Card processing failed: ${r.reason}`);
6134
+ log16.warn(TAG16, `Card processing failed: ${r.reason}`);
6089
6135
  }
6090
6136
  }
6091
6137
  } catch (err) {
6092
- log15.error(TAG15, `Tick failed: ${err instanceof Error ? err.message : err}`);
6138
+ log16.error(TAG16, `Tick failed: ${err instanceof Error ? err.message : err}`);
6093
6139
  }
6094
6140
  }
6095
6141
  async completeMergedCard(card, resolvedLabels) {
@@ -6103,11 +6149,12 @@ class MergeMonitor {
6103
6149
  });
6104
6150
  }
6105
6151
  }
6106
- var TAG15 = "merge-monitor";
6152
+ var TAG16 = "merge-monitor";
6107
6153
  var init_merge_monitor = __esm(() => {
6108
6154
  init_auto_merge();
6109
6155
  init_board_helpers();
6110
6156
  init_merge_complete();
6157
+ init_pr_state_sync();
6111
6158
  init_review_worktree();
6112
6159
  });
6113
6160
 
@@ -6300,7 +6347,7 @@ __export(exports_plan_criteria, {
6300
6347
  criteriaSettledBy: () => criteriaSettledBy,
6301
6348
  CRITERION_COMPLETED: () => CRITERION_COMPLETED
6302
6349
  });
6303
- import { log as log16 } from "@gethmy/harness";
6350
+ import { log as log17 } from "@gethmy/harness";
6304
6351
  function criteriaSettledBy(tasks, cardId) {
6305
6352
  if (!cardId || !Array.isArray(tasks))
6306
6353
  return [];
@@ -6361,7 +6408,7 @@ async function settlePlanCriteria(client, tag, card) {
6361
6408
  const settled = card.plan_id ? criteriaSettledBy(payload?.tasks, card.id) : [];
6362
6409
  const divergent = divergentCriteria(payload, card.id, card.plan_id);
6363
6410
  if (divergent.length > 0) {
6364
- log16.warn(tag, describeDivergence(card.short_id, card.plan_id, divergent));
6411
+ log17.warn(tag, describeDivergence(card.short_id, card.plan_id, divergent));
6365
6412
  }
6366
6413
  const settleGroup = async (criteria, planIdOf, describe2) => {
6367
6414
  if (criteria.length === 0)
@@ -6372,15 +6419,15 @@ async function settlePlanCriteria(client, tag, card) {
6372
6419
  status: CRITERION_COMPLETED
6373
6420
  });
6374
6421
  }
6375
- log16.info(tag, describe2());
6422
+ log17.info(tag, describe2());
6376
6423
  } catch (err) {
6377
- log16.warn(tag, `plan criteria write-back failed for #${card.short_id}: ${err instanceof Error ? err.message : err}`);
6424
+ log17.warn(tag, `plan criteria write-back failed for #${card.short_id}: ${err instanceof Error ? err.message : err}`);
6378
6425
  }
6379
6426
  };
6380
6427
  await settleGroup(settled, () => card.plan_id, () => describeSettled(card.short_id, settled));
6381
6428
  await settleGroup(divergent, (criterion) => criterion.plan_id, () => `${describeSettled(card.short_id, divergent)} (from the criterion side)`);
6382
6429
  } catch (err) {
6383
- log16.warn(tag, `plan criteria write-back failed for #${card.short_id}: ${err instanceof Error ? err.message : err}`);
6430
+ log17.warn(tag, `plan criteria write-back failed for #${card.short_id}: ${err instanceof Error ? err.message : err}`);
6384
6431
  }
6385
6432
  }
6386
6433
  async function fetchAcceptanceCriteria(client, tag, card) {
@@ -6389,12 +6436,12 @@ async function fetchAcceptanceCriteria(client, tag, card) {
6389
6436
  try {
6390
6437
  const plan = await client.getPlanByCardId(card.id);
6391
6438
  if (!plan?.plan) {
6392
- log16.warn(tag, `#${card.short_id} names plan ${card.plan_id}, but the plan read returned no plan — grading the card text alone; check that the card's plan_id still resolves`);
6439
+ log17.warn(tag, `#${card.short_id} names plan ${card.plan_id}, but the plan read returned no plan — grading the card text alone; check that the card's plan_id still resolves`);
6393
6440
  return [];
6394
6441
  }
6395
6442
  return normalizeAcceptanceCriteria(plan.tasks, card.id);
6396
6443
  } catch (err) {
6397
- log16.warn(tag, `plan criteria fetch failed for #${card.short_id}: ${err instanceof Error ? err.message : err}`);
6444
+ log17.warn(tag, `plan criteria fetch failed for #${card.short_id}: ${err instanceof Error ? err.message : err}`);
6398
6445
  return [];
6399
6446
  }
6400
6447
  }
@@ -6501,7 +6548,7 @@ var init_budget_pause = __esm(() => {
6501
6548
  });
6502
6549
 
6503
6550
  // src/queue.ts
6504
- import { log as log17 } from "@gethmy/harness";
6551
+ import { log as log18 } from "@gethmy/harness";
6505
6552
  function scoreCardBreakdown(config, card, column, labels, signals = {}) {
6506
6553
  const w = config.ranking;
6507
6554
  const priority = w.priorityWeight * (PRIORITY_STEP[card.priority ?? "medium"] ?? PRIORITY_STEP.medium);
@@ -6562,7 +6609,7 @@ class PriorityQueue {
6562
6609
  enqueue(card, column, labels, mode = "implement", signals = {}) {
6563
6610
  const existing = this.items.findIndex((i) => i.cardId === card.id);
6564
6611
  if (existing !== -1) {
6565
- log17.debug(TAG16, `Card #${card.short_id} already queued, updating priority`);
6612
+ log18.debug(TAG17, `Card #${card.short_id} already queued, updating priority`);
6566
6613
  this.items.splice(existing, 1);
6567
6614
  }
6568
6615
  const breakdown = scoreCardBreakdown(this.config, card, column, labels, signals);
@@ -6583,7 +6630,7 @@ class PriorityQueue {
6583
6630
  }
6584
6631
  }
6585
6632
  this.items.splice(insertIdx, 0, item);
6586
- log17.info(TAG16, `Enqueued #${card.short_id} "${card.title}" (${formatBreakdown(breakdown)}, pos=${insertIdx}, queue=${this.items.length})`);
6633
+ log18.info(TAG17, `Enqueued #${card.short_id} "${card.title}" (${formatBreakdown(breakdown)}, pos=${insertIdx}, queue=${this.items.length})`);
6587
6634
  }
6588
6635
  dequeue() {
6589
6636
  return this.items.shift() ?? null;
@@ -6593,7 +6640,7 @@ class PriorityQueue {
6593
6640
  if (idx === -1)
6594
6641
  return null;
6595
6642
  const [item] = this.items.splice(idx, 1);
6596
- log17.info(TAG16, `Removed #${item.shortId} from queue`);
6643
+ log18.info(TAG17, `Removed #${item.shortId} from queue`);
6597
6644
  return item;
6598
6645
  }
6599
6646
  has(cardId) {
@@ -6612,7 +6659,7 @@ class PriorityQueue {
6612
6659
  return this.items.slice();
6613
6660
  }
6614
6661
  }
6615
- var TAG16 = "queue", MS_PER_DAY = 86400000, PRIORITY_STEP;
6662
+ var TAG17 = "queue", MS_PER_DAY = 86400000, PRIORITY_STEP;
6616
6663
  var init_queue = __esm(() => {
6617
6664
  init_src();
6618
6665
  PRIORITY_STEP = {
@@ -6624,7 +6671,7 @@ var init_queue = __esm(() => {
6624
6671
  });
6625
6672
 
6626
6673
  // src/reported-config-cache.ts
6627
- import { log as log18 } from "@gethmy/harness";
6674
+ import { log as log19 } from "@gethmy/harness";
6628
6675
 
6629
6676
  class ReportedConfigCache {
6630
6677
  client;
@@ -6641,27 +6688,27 @@ class ReportedConfigCache {
6641
6688
  await this.client.reportAgentConfig(workspaceId, agentId, report);
6642
6689
  this.last = serialized;
6643
6690
  } catch (err) {
6644
- log18.warn(TAG17, `Could not report the resolved model config: ${err instanceof Error ? err.message : String(err)}`);
6691
+ log19.warn(TAG18, `Could not report the resolved model config: ${err instanceof Error ? err.message : String(err)}`);
6645
6692
  }
6646
6693
  }
6647
6694
  }
6648
- var TAG17 = "reported-config";
6695
+ var TAG18 = "reported-config";
6649
6696
  var init_reported_config_cache = () => {};
6650
6697
 
6651
6698
  // src/run-closeout.ts
6652
- import { log as log19 } from "@gethmy/harness";
6699
+ import { log as log20 } from "@gethmy/harness";
6653
6700
  async function transferCardToCompletion(deps, card, moveToColumn, onPromoted) {
6654
6701
  await moveCardToColumn(deps.client, card, moveToColumn);
6655
6702
  try {
6656
6703
  await releaseAssignedAgent(deps.client, card.id);
6657
6704
  } catch (err) {
6658
- log19.warn(deps.tag, `assignment release failed for #${card.short_id}: ${err instanceof Error ? err.message : err}`);
6705
+ log20.warn(deps.tag, `assignment release failed for #${card.short_id}: ${err instanceof Error ? err.message : err}`);
6659
6706
  }
6660
6707
  if (onPromoted) {
6661
6708
  try {
6662
6709
  await onPromoted(card);
6663
6710
  } catch (err) {
6664
- log19.warn(deps.tag, `successor promotion failed for #${card.short_id}: ${err instanceof Error ? err.message : err}`);
6711
+ log20.warn(deps.tag, `successor promotion failed for #${card.short_id}: ${err instanceof Error ? err.message : err}`);
6665
6712
  }
6666
6713
  }
6667
6714
  }
@@ -6675,7 +6722,7 @@ async function endRunSession(deps, card, disposition, extraPayload, onError) {
6675
6722
  } catch (err) {
6676
6723
  if (onError === "throw")
6677
6724
  throw err;
6678
- log19.error(deps.tag, `endAgentSession after the run failed on #${card.short_id}: ${err instanceof Error ? err.message : err}`);
6725
+ log20.error(deps.tag, `endAgentSession after the run failed on #${card.short_id}: ${err instanceof Error ? err.message : err}`);
6679
6726
  }
6680
6727
  }
6681
6728
  var init_run_closeout = __esm(() => {
@@ -6691,7 +6738,7 @@ import {
6691
6738
  extractPrUrl as extractPrUrl2,
6692
6739
  GIT_NO_HOOKS as GIT_NO_HOOKS6,
6693
6740
  getBranchWebUrl,
6694
- log as log20,
6741
+ log as log21,
6695
6742
  pushBranch,
6696
6743
  reportFindings,
6697
6744
  runFormatFix,
@@ -6738,24 +6785,24 @@ async function runCompletion(client, card, branchName, worktreePath, config, wor
6738
6785
  if (!hasCommits) {
6739
6786
  const { maxTurnsExhausted, failureSummary } = describeNoCommitFailure(sessionStats?.cost?.numTurns ?? 0, effectiveMaxTurns ?? config.claude.maxTurns);
6740
6787
  if (noCommitOutcome(maxTurnsExhausted, config.budget.pause.enabled) === "park") {
6741
- log20.warn(TAG18, `No commits on branch ${branchName} — ${failureSummary}; parking for a decision`);
6788
+ log21.warn(TAG19, `No commits on branch ${branchName} — ${failureSummary}; parking for a decision`);
6742
6789
  return "park";
6743
6790
  }
6744
6791
  if (expectsCommits === false) {
6745
- log20.info(TAG18, `No commits on branch ${branchName} — this stage's deliverable is not a diff; its gate decides the run`);
6792
+ log21.info(TAG19, `No commits on branch ${branchName} — this stage's deliverable is not a diff; its gate decides the run`);
6746
6793
  if (onBeforeWorktreeCleanup) {
6747
6794
  try {
6748
6795
  await onBeforeWorktreeCleanup(worktreePath);
6749
6796
  } catch (err) {
6750
- log20.warn(TAG18, `onBeforeWorktreeCleanup hook failed for #${card.short_id}: ${err instanceof Error ? err.message : err}`);
6797
+ log21.warn(TAG19, `onBeforeWorktreeCleanup hook failed for #${card.short_id}: ${err instanceof Error ? err.message : err}`);
6751
6798
  }
6752
6799
  } else {
6753
- await endRunSession({ client, tag: TAG18 }, card, { status: "completed" }, buildTokenPayload(sessionStats), "throw");
6800
+ await endRunSession({ client, tag: TAG19 }, card, { status: "completed" }, buildTokenPayload(sessionStats), "throw");
6754
6801
  }
6755
6802
  await teardownWorktree(client, card.id, worktreePath, branchName, agentSessionId);
6756
6803
  return true;
6757
6804
  }
6758
- log20.warn(TAG18, `No commits on branch ${branchName} — ${failureSummary}; counting as a failed attempt`);
6805
+ log21.warn(TAG19, `No commits on branch ${branchName} — ${failureSummary}; counting as a failed attempt`);
6759
6806
  const noCommitHandback = await guardedHandback(client, card.id, {
6760
6807
  agentId,
6761
6808
  workingColumnId: card.column_id
@@ -6772,13 +6819,13 @@ async function runCompletion(client, card, branchName, worktreePath, config, wor
6772
6819
  await teardownWorktree(client, card.id, worktreePath, branchName, agentSessionId);
6773
6820
  return false;
6774
6821
  }
6775
- log20.info(TAG18, `Pushing branch ${branchName} (pre-verify)...`);
6822
+ log21.info(TAG19, `Pushing branch ${branchName} (pre-verify)...`);
6776
6823
  let lastPushedSha = null;
6777
6824
  try {
6778
6825
  pushBranch(branchName, worktreePath);
6779
6826
  lastPushedSha = readHeadSha(worktreePath);
6780
6827
  } catch (err) {
6781
- log20.error(TAG18, `pre-verify push failed for ${branchName}: ${err instanceof Error ? err.message : err}`);
6828
+ log21.error(TAG19, `pre-verify push failed for ${branchName}: ${err instanceof Error ? err.message : err}`);
6782
6829
  }
6783
6830
  const recoveryUrl = lastPushedSha ? getBranchWebUrl(branchName, worktreePath) : null;
6784
6831
  if (lastPushedSha) {
@@ -6797,7 +6844,7 @@ async function runCompletion(client, card, branchName, worktreePath, config, wor
6797
6844
  let autoFixAttempts = 0;
6798
6845
  if (!result.passed && config.verification.autoFix) {
6799
6846
  for (let attempt = 0;attempt < config.verification.maxFixAttempts; attempt++) {
6800
- log20.info(TAG18, `Auto-fix attempt ${attempt + 1}/${config.verification.maxFixAttempts}`);
6847
+ log21.info(TAG19, `Auto-fix attempt ${attempt + 1}/${config.verification.maxFixAttempts}`);
6801
6848
  await client.updateAgentProgress(card.id, {
6802
6849
  agentIdentifier: sessionIdentifier,
6803
6850
  agentName: AGENT_NAME,
@@ -6814,14 +6861,14 @@ async function runCompletion(client, card, branchName, worktreePath, config, wor
6814
6861
  result = await runVerification(worktreePath, config, workerId);
6815
6862
  autoFixAttempts = attempt + 1;
6816
6863
  if (result.passed) {
6817
- log20.info(TAG18, `Auto-fix succeeded on attempt ${attempt + 1}`);
6864
+ log21.info(TAG19, `Auto-fix succeeded on attempt ${attempt + 1}`);
6818
6865
  const sha = readHeadSha(worktreePath);
6819
6866
  if (sha && sha !== lastPushedSha) {
6820
6867
  try {
6821
6868
  pushBranch(branchName, worktreePath);
6822
6869
  lastPushedSha = sha;
6823
6870
  } catch (err) {
6824
- log20.warn(TAG18, `post-fix push failed for ${branchName}: ${err instanceof Error ? err.message : err}`);
6871
+ log21.warn(TAG19, `post-fix push failed for ${branchName}: ${err instanceof Error ? err.message : err}`);
6825
6872
  }
6826
6873
  }
6827
6874
  break;
@@ -6829,14 +6876,14 @@ async function runCompletion(client, card, branchName, worktreePath, config, wor
6829
6876
  }
6830
6877
  }
6831
6878
  if (!result.passed) {
6832
- log20.warn(TAG18, `Verification failed for #${card.short_id} — reporting findings`);
6879
+ log21.warn(TAG19, `Verification failed for #${card.short_id} — reporting findings`);
6833
6880
  const failSha = readHeadSha(worktreePath);
6834
6881
  if (failSha && failSha !== lastPushedSha) {
6835
6882
  try {
6836
6883
  pushBranch(branchName, worktreePath);
6837
6884
  lastPushedSha = failSha;
6838
6885
  } catch (err) {
6839
- log20.warn(TAG18, `post-fail push failed for ${branchName}: ${err instanceof Error ? err.message : err}`);
6886
+ log21.warn(TAG19, `post-fail push failed for ${branchName}: ${err instanceof Error ? err.message : err}`);
6840
6887
  }
6841
6888
  }
6842
6889
  const failureSummary = buildVerificationFailureSummary(result, autoFixAttempts);
@@ -6847,7 +6894,7 @@ async function runCompletion(client, card, branchName, worktreePath, config, wor
6847
6894
  recoveryBranch: branchName
6848
6895
  });
6849
6896
  } catch (err) {
6850
- log20.debug(TAG18, `recordFailureSummary failed: ${err instanceof Error ? err.message : err}`);
6897
+ log21.debug(TAG19, `recordFailureSummary failed: ${err instanceof Error ? err.message : err}`);
6851
6898
  }
6852
6899
  await reportFindings(client, card.id, result, lastPushedSha ? { branchName, branchUrl: recoveryUrl } : null);
6853
6900
  const verifyHandback = await guardedHandback(client, card.id, {
@@ -6867,15 +6914,24 @@ async function runCompletion(client, card, branchName, worktreePath, config, wor
6867
6914
  await teardownWorktree(client, card.id, worktreePath, branchName, agentSessionId);
6868
6915
  return false;
6869
6916
  }
6870
- log20.info(TAG18, `Verification passed for #${card.short_id}`);
6917
+ log21.info(TAG19, `Verification passed for #${card.short_id}`);
6871
6918
  }
6872
6919
  let prUrl = null;
6873
6920
  if (config.completion.createPR) {
6874
6921
  const provider = detectGitProvider3(worktreePath);
6875
6922
  prUrl = createPullRequest(card, branchName, worktreePath, config, provider);
6923
+ if (prUrl) {
6924
+ await syncPullRequestState({
6925
+ client,
6926
+ cardId: card.id,
6927
+ prUrl,
6928
+ cwd: worktreePath,
6929
+ provider
6930
+ });
6931
+ }
6876
6932
  }
6877
6933
  if (config.completion.moveToColumn) {
6878
- await transferCardToCompletion({ client, tag: TAG18 }, card, config.completion.moveToColumn, onMovedToCompletion);
6934
+ await transferCardToCompletion({ client, tag: TAG19 }, card, config.completion.moveToColumn, onMovedToCompletion);
6879
6935
  }
6880
6936
  if (config.completion.postSummary) {
6881
6937
  await postSummary(client, card, branchName, worktreePath, prUrl, config.worktree.baseBranch, sessionStats);
@@ -6884,14 +6940,14 @@ async function runCompletion(client, card, branchName, worktreePath, config, wor
6884
6940
  try {
6885
6941
  await onBeforeWorktreeCleanup(worktreePath);
6886
6942
  } catch (err) {
6887
- log20.warn(TAG18, `onBeforeWorktreeCleanup hook failed for #${card.short_id}: ${err instanceof Error ? err.message : err}`);
6943
+ log21.warn(TAG19, `onBeforeWorktreeCleanup hook failed for #${card.short_id}: ${err instanceof Error ? err.message : err}`);
6888
6944
  }
6889
6945
  }
6890
6946
  if (!onBeforeWorktreeCleanup) {
6891
- await endRunSession({ client, tag: TAG18 }, card, { status: "completed" }, buildTokenPayload(sessionStats), "throw");
6947
+ await endRunSession({ client, tag: TAG19 }, card, { status: "completed" }, buildTokenPayload(sessionStats), "throw");
6892
6948
  }
6893
6949
  await teardownWorktree(client, card.id, worktreePath, branchName, agentSessionId);
6894
- log20.info(TAG18, `Completion done for #${card.short_id}${prUrl ? ` — PR: ${prUrl}` : ""}`);
6950
+ log21.info(TAG19, `Completion done for #${card.short_id}${prUrl ? ` — PR: ${prUrl}` : ""}`);
6895
6951
  return true;
6896
6952
  }
6897
6953
  function buildVerificationFailureSummary(result, autoFixAttempts) {
@@ -6933,7 +6989,7 @@ function commitUncommittedChanges(worktreePath, card) {
6933
6989
  encoding: "utf-8"
6934
6990
  }).trim();
6935
6991
  } catch (err) {
6936
- log20.warn(TAG18, `git status failed in ${worktreePath}: ${err instanceof Error ? err.message : err}`);
6992
+ log21.warn(TAG19, `git status failed in ${worktreePath}: ${err instanceof Error ? err.message : err}`);
6937
6993
  return false;
6938
6994
  }
6939
6995
  if (status.length === 0)
@@ -6949,10 +7005,10 @@ function commitUncommittedChanges(worktreePath, card) {
6949
7005
  cwd: worktreePath,
6950
7006
  encoding: "utf-8"
6951
7007
  });
6952
- log20.warn(TAG18, `Auto-committed uncommitted worktree changes for #${card.short_id} — agent ended without committing`);
7008
+ log21.warn(TAG19, `Auto-committed uncommitted worktree changes for #${card.short_id} — agent ended without committing`);
6953
7009
  return true;
6954
7010
  } catch (err) {
6955
- log20.error(TAG18, `auto-commit failed for #${card.short_id}: ${err instanceof Error ? err.message : err}`);
7011
+ log21.error(TAG19, `auto-commit failed for #${card.short_id}: ${err instanceof Error ? err.message : err}`);
6956
7012
  return false;
6957
7013
  }
6958
7014
  }
@@ -7001,9 +7057,9 @@ async function recordBranchProvenance(client, card, branchName) {
7001
7057
  ${BRANCH_PROVENANCE_MARKER} (pushed — a later run continues it)
7002
7058
  Branch: \`${branchName}\``;
7003
7059
  await client.updateCard(card.id, { description: description + block });
7004
- log20.info(TAG18, `Recorded branch provenance on #${card.short_id}`);
7060
+ log21.info(TAG19, `Recorded branch provenance on #${card.short_id}`);
7005
7061
  } catch (err) {
7006
- log20.warn(TAG18, `Failed to record branch provenance on #${card.short_id}: ${err instanceof Error ? err.message : err}`);
7062
+ log21.warn(TAG19, `Failed to record branch provenance on #${card.short_id}: ${err instanceof Error ? err.message : err}`);
7007
7063
  }
7008
7064
  }
7009
7065
  async function postSummary(client, card, branchName, worktreePath, prUrl, baseBranch, sessionStats) {
@@ -7057,24 +7113,25 @@ ${commitLog}
7057
7113
  description: baseDesc + parts.join(`
7058
7114
  `)
7059
7115
  });
7060
- log20.info(TAG18, `Posted completion summary to #${card.short_id}`);
7116
+ log21.info(TAG19, `Posted completion summary to #${card.short_id}`);
7061
7117
  } catch (err) {
7062
- log20.error(TAG18, `Failed to post summary: ${err instanceof Error ? err.message : err}`);
7118
+ log21.error(TAG19, `Failed to post summary: ${err instanceof Error ? err.message : err}`);
7063
7119
  }
7064
7120
  }
7065
- var TAG18 = "completion", SUMMARY_MARKER = `---
7121
+ var TAG19 = "completion", SUMMARY_MARKER = `---
7066
7122
  **Agent completed**`, BRANCH_PROVENANCE_MARKER = `---
7067
7123
  **Agent branch**`;
7068
7124
  var init_completion = __esm(() => {
7069
7125
  init_src();
7070
7126
  init_board_helpers();
7071
7127
  init_handback();
7128
+ init_pr_state_sync();
7072
7129
  init_run_closeout();
7073
7130
  init_types();
7074
7131
  });
7075
7132
 
7076
7133
  // src/progress-tracker.ts
7077
- import { log as log21 } from "@gethmy/harness";
7134
+ import { log as log22 } from "@gethmy/harness";
7078
7135
  function truncate2(str, max) {
7079
7136
  return str.length > max ? `${str.slice(0, max - 3)}...` : str;
7080
7137
  }
@@ -7193,7 +7250,7 @@ class ProgressTracker {
7193
7250
  }
7194
7251
  onToolStart(name, input) {
7195
7252
  this.toolCallCount++;
7196
- log21.debug(TAG19, `Tool: ${name} (count: ${this.toolCallCount}, phase: ${this.phase})`);
7253
+ log22.debug(TAG20, `Tool: ${name} (count: ${this.toolCallCount}, phase: ${this.phase})`);
7197
7254
  const filePath = this.extractString(input, "file_path");
7198
7255
  if (filePath) {
7199
7256
  if (EDIT_TOOLS.has(name)) {
@@ -7260,7 +7317,7 @@ class ProgressTracker {
7260
7317
  transitionTo(newPhase) {
7261
7318
  if (PHASE_ORDER[newPhase] <= PHASE_ORDER[this.phase])
7262
7319
  return;
7263
- log21.info(TAG19, `Phase: ${this.phase} → ${newPhase}`);
7320
+ log22.info(TAG20, `Phase: ${this.phase} → ${newPhase}`);
7264
7321
  const previousPhase = this.phase;
7265
7322
  this.runEventSink?.recordPhaseChanged(newPhase, previousPhase);
7266
7323
  this.phase = newPhase;
@@ -7365,7 +7422,7 @@ class ProgressTracker {
7365
7422
  }
7366
7423
  sendUpdate(currentTask) {
7367
7424
  this.lastUpdateAt = Date.now();
7368
- log21.debug(TAG19, `Progress: ${this.progress}% — ${currentTask}`);
7425
+ log22.debug(TAG20, `Progress: ${this.progress}% — ${currentTask}`);
7369
7426
  this.client.updateAgentProgress(this.cardId, {
7370
7427
  agentIdentifier: this.sessionIdentifier,
7371
7428
  agentName: AGENT_NAME,
@@ -7382,7 +7439,7 @@ class ProgressTracker {
7382
7439
  modelName: this.lastCost?.modelName ?? this.requestedModel ?? undefined,
7383
7440
  numTurns: this.lastCost?.numTurns ?? 0
7384
7441
  }).catch((err) => {
7385
- log21.warn(TAG19, `Failed to send progress update: ${err}`);
7442
+ log22.warn(TAG20, `Failed to send progress update: ${err}`);
7386
7443
  });
7387
7444
  if (this.runEventSink && this.progress !== this.lastEmittedProgress) {
7388
7445
  this.lastEmittedProgress = this.progress;
@@ -7413,7 +7470,7 @@ class ProgressTracker {
7413
7470
  return null;
7414
7471
  }
7415
7472
  }
7416
- var TAG19 = "progress-tracker", THROTTLE_MS = 5000, HEARTBEAT_MS = 60000, MAX_TASK_LENGTH = 120, SENTENCE_SPLIT, ACTION_PREFIX, GIT_COMMIT_RE, BUILD_CMD_RE, PHASES, PHASE_ORDER, EDIT_TOOLS, FILE_TOOL_VERBS;
7473
+ var TAG20 = "progress-tracker", THROTTLE_MS = 5000, HEARTBEAT_MS = 60000, MAX_TASK_LENGTH = 120, SENTENCE_SPLIT, ACTION_PREFIX, GIT_COMMIT_RE, BUILD_CMD_RE, PHASES, PHASE_ORDER, EDIT_TOOLS, FILE_TOOL_VERBS;
7417
7474
  var init_progress_tracker = __esm(() => {
7418
7475
  init_types();
7419
7476
  SENTENCE_SPLIT = /\.\s|\n/;
@@ -7447,7 +7504,7 @@ var init_progress_tracker = __esm(() => {
7447
7504
  });
7448
7505
 
7449
7506
  // src/prompt.ts
7450
- import { log as log22 } from "@gethmy/harness";
7507
+ import { log as log23 } from "@gethmy/harness";
7451
7508
  import {
7452
7509
  buildMemoryQuery,
7453
7510
  RERANK_MAX_CANDIDATES,
@@ -7498,11 +7555,11 @@ async function buildPrompt(enriched, branchName, worktreePath, client, workspace
7498
7555
  Do NOT push to main. All your work stays on \`${branchName}\`.
7499
7556
  The daemon owns the run lifecycle: once your work is committed it ends the agent session, pushes the branch, and moves the card to Review for you. Do NOT call harmony_end_agent_session, do NOT start a new session, and do NOT move the card or change its column yourself. If the skill driving this work tells you to move the card or end the session as a final step, SKIP it — it is handled for you (those tools are disabled for this run). Finish the implementation, commit, and stop.`
7500
7557
  });
7501
- log22.info(TAG20, `Generated prompt for #${card.short_id} — ${result.contextSummary.memoryCount} memories, ${result.tokenEstimate} tokens`);
7558
+ log23.info(TAG21, `Generated prompt for #${card.short_id} — ${result.contextSummary.memoryCount} memories, ${result.tokenEstimate} tokens`);
7502
7559
  return result.prompt + referenceSection;
7503
7560
  } catch (err) {
7504
7561
  const msg = err instanceof Error ? err.message : String(err);
7505
- log22.warn(TAG20, `Failed to generate prompt via API, using fallback: ${msg}`);
7562
+ log23.warn(TAG21, `Failed to generate prompt via API, using fallback: ${msg}`);
7506
7563
  onPromptFallback?.();
7507
7564
  const commentsSection = await renderCommentsSection(client, card.id);
7508
7565
  return buildFallbackPrompt(enriched, branchName, worktreePath) + commentsSection + referenceSection;
@@ -7524,7 +7581,7 @@ ${untrustedDataBlock(section, {
7524
7581
  purpose: "discussion to take into account"
7525
7582
  })}` : "";
7526
7583
  } catch (err) {
7527
- log22.warn(TAG20, "comment-thread fetch failed", {
7584
+ log23.warn(TAG21, "comment-thread fetch failed", {
7528
7585
  event: "comment_fetch_failed",
7529
7586
  error: err instanceof Error ? err.message : String(err)
7530
7587
  });
@@ -7566,7 +7623,7 @@ ${bullets}`, {
7566
7623
  })}`;
7567
7624
  } catch (err) {
7568
7625
  const error = err instanceof Error ? err.message : String(err);
7569
- log22.warn(TAG20, "reference recall failed", {
7626
+ log23.warn(TAG21, "reference recall failed", {
7570
7627
  event: "reference_recall_failed",
7571
7628
  error
7572
7629
  });
@@ -7608,7 +7665,7 @@ ${subtaskStr}
7608
7665
  You are working in a git worktree at \`${worktreePath}\` on branch \`${branchName}\`.
7609
7666
  Do NOT push to main. All your work stays on \`${branchName}\`.`;
7610
7667
  }
7611
- var TAG20 = "prompt", RESUME_CONTINUATION = `Continue from where you stopped.
7668
+ var TAG21 = "prompt", RESUME_CONTINUATION = `Continue from where you stopped.
7612
7669
 
7613
7670
  This is the same session. The task you were given, the work you have already done,
7614
7671
  and everything you read are above in this conversation. None of it has changed, and
@@ -7633,7 +7690,7 @@ import {
7633
7690
  extractPrUrl as extractPrUrl3,
7634
7691
  getBranchWebUrl as getBranchWebUrl2,
7635
7692
  getHeadSha,
7636
- log as log23,
7693
+ log as log24,
7637
7694
  pushBranch as pushBranch2,
7638
7695
  renameRemoteBranch,
7639
7696
  sanitizeCiText as sanitizeCiText2,
@@ -7752,6 +7809,20 @@ function extractResult(parsed) {
7752
7809
  findings
7753
7810
  };
7754
7811
  }
7812
+ function parseStructuredReview(structured) {
7813
+ try {
7814
+ const parsed = JSON.parse(structured);
7815
+ if (parsed && typeof parsed === "object" && "verdict" in parsed) {
7816
+ return extractResult(parsed);
7817
+ }
7818
+ } catch {}
7819
+ log24.warn(TAG22, "Review spawn returned no schema-valid verdict — returning error verdict (card stays in Review)");
7820
+ return {
7821
+ verdict: "error",
7822
+ summary: structured.slice(0, 500),
7823
+ findings: []
7824
+ };
7825
+ }
7755
7826
  function parseReviewOutput(stdout) {
7756
7827
  const fencedBlocks = [...stdout.matchAll(/```json\s*([\s\S]*?)```/g)];
7757
7828
  for (let i = fencedBlocks.length - 1;i >= 0; i--) {
@@ -7759,7 +7830,7 @@ function parseReviewOutput(stdout) {
7759
7830
  try {
7760
7831
  const parsed = JSON.parse(raw);
7761
7832
  if (parsed && typeof parsed === "object" && "verdict" in parsed) {
7762
- log23.debug(TAG21, "Parsed review output from fenced JSON block");
7833
+ log24.debug(TAG22, "Parsed review output from fenced JSON block");
7763
7834
  return extractResult(parsed);
7764
7835
  }
7765
7836
  } catch {}
@@ -7785,21 +7856,21 @@ function parseReviewOutput(stdout) {
7785
7856
  try {
7786
7857
  const parsed = JSON.parse(candidates[i]);
7787
7858
  if (parsed && typeof parsed === "object" && "verdict" in parsed) {
7788
- log23.debug(TAG21, "Parsed review output from raw JSON object");
7859
+ log24.debug(TAG22, "Parsed review output from raw JSON object");
7789
7860
  return extractResult(parsed);
7790
7861
  }
7791
7862
  } catch {}
7792
7863
  }
7793
7864
  const verdictMatch = stdout.match(/"verdict"\s*:\s*"(approved|rejected)"/i);
7794
7865
  if (verdictMatch) {
7795
- log23.warn(TAG21, `Parsed verdict via regex fallback — findings lost (${verdictMatch[1]})`);
7866
+ log24.warn(TAG22, `Parsed verdict via regex fallback — findings lost (${verdictMatch[1]})`);
7796
7867
  return {
7797
7868
  verdict: verdictMatch[1].toLowerCase(),
7798
7869
  summary: "Parsed via regex fallback — original JSON was malformed. Check run log.",
7799
7870
  findings: []
7800
7871
  };
7801
7872
  }
7802
- log23.warn(TAG21, "Failed to parse review JSON output — returning error verdict (card stays in Review)");
7873
+ log24.warn(TAG22, "Failed to parse review JSON output — returning error verdict (card stays in Review)");
7803
7874
  return {
7804
7875
  verdict: "error",
7805
7876
  summary: stdout.slice(0, 500),
@@ -7835,7 +7906,7 @@ async function postReviewComment(client, card, commentType, body, agentSessionId
7835
7906
  agentSessionId: agentSessionId ?? undefined
7836
7907
  });
7837
7908
  } catch (err) {
7838
- log23.error(TAG21, `Failed to post review comment to #${card.short_id}: ${err instanceof Error ? err.message : err}`);
7909
+ log24.error(TAG22, `Failed to post review comment to #${card.short_id}: ${err instanceof Error ? err.message : err}`);
7839
7910
  }
7840
7911
  }
7841
7912
  async function runReviewCompletion(client, card, result, config, worktreePath, branchName, sessionStats, runLogPath, agentSessionId, stateStore, agentId, resolvedFromPrUrl, visualLensSkipReason) {
@@ -7859,7 +7930,7 @@ async function runReviewCompletion(client, card, result, config, worktreePath, b
7859
7930
  };
7860
7931
  }
7861
7932
  if (!handbackVerdict.proceed) {
7862
- log23.info(TAG21, `#${card.short_id}: review outcome will not move the card — ${handbackVerdict.detail} (${handbackVerdict.reason})`);
7933
+ log24.info(TAG22, `#${card.short_id}: review outcome will not move the card — ${handbackVerdict.detail} (${handbackVerdict.reason})`);
7863
7934
  }
7864
7935
  const lensLine = visualLensSkipReason ? `Visual QA did not run — ${sanitizeCiText2(visualLensSkipReason, REVIEW_FAILURE_REASON_MAX)}. Anything that needs a rendered page is ungraded.` : "";
7865
7936
  const LOOPING_REFUSALS = new Set(["unreadable", "released", "done"]);
@@ -7869,19 +7940,19 @@ async function runReviewCompletion(client, card, result, config, worktreePath, b
7869
7940
  }
7870
7941
  try {
7871
7942
  await addLabelByName(client, card, NEED_REVIEW_LABEL, NEED_REVIEW_LABEL_COLOR);
7872
- log23.warn(TAG21, `#${card.short_id} labelled "${NEED_REVIEW_LABEL}" (${handbackVerdict.reason}) so the daemon stops re-claiming and re-reviewing it`);
7943
+ log24.warn(TAG22, `#${card.short_id} labelled "${NEED_REVIEW_LABEL}" (${handbackVerdict.reason}) so the daemon stops re-claiming and re-reviewing it`);
7873
7944
  } catch (err) {
7874
- log23.warn(TAG21, `Failed to add "${NEED_REVIEW_LABEL}" label: ${err instanceof Error ? err.message : err}`);
7945
+ log24.warn(TAG22, `Failed to add "${NEED_REVIEW_LABEL}" label: ${err instanceof Error ? err.message : err}`);
7875
7946
  }
7876
7947
  }
7877
7948
  const currentCycle = getReviewCycle(freshDesc) + 1;
7878
7949
  const maxCycles = config.review.maxReviewCycles;
7879
7950
  if (result.verdict === "error") {
7880
- log23.warn(TAG21, `#${card.short_id} review output unparseable — labelling "${NEED_REVIEW_LABEL}" for manual inspection`);
7951
+ log24.warn(TAG22, `#${card.short_id} review output unparseable — labelling "${NEED_REVIEW_LABEL}" for manual inspection`);
7881
7952
  try {
7882
7953
  await addLabelByName(client, card, NEED_REVIEW_LABEL, NEED_REVIEW_LABEL_COLOR);
7883
7954
  } catch (err) {
7884
- log23.warn(TAG21, `Failed to add "${NEED_REVIEW_LABEL}" label: ${err instanceof Error ? err.message : err}`);
7955
+ log24.warn(TAG22, `Failed to add "${NEED_REVIEW_LABEL}" label: ${err instanceof Error ? err.message : err}`);
7885
7956
  }
7886
7957
  if (config.review.postFindings) {
7887
7958
  const rawTail = runLogPath ? tailRunLog(runLogPath) : null;
@@ -7924,12 +7995,21 @@ ${runLogTail}
7924
7995
  renameRemoteBranch(branchName, newRef, worktreePath);
7925
7996
  approvedBranch = newRef;
7926
7997
  } catch (err) {
7927
- log23.warn(TAG21, `Branch rename failed (continuing on ${branchName}): ${err instanceof Error ? err.message : err}`);
7998
+ log24.warn(TAG22, `Branch rename failed (continuing on ${branchName}): ${err instanceof Error ? err.message : err}`);
7928
7999
  }
7929
8000
  }
7930
8001
  if (config.review.createPR && approvedBranch) {
7931
8002
  const provider = detectGitProvider4(worktreePath);
7932
8003
  prUrl = createPullRequest2(card, approvedBranch, worktreePath, config, provider, resolvedFromPrUrl ?? null);
8004
+ if (prUrl) {
8005
+ await syncPullRequestState({
8006
+ client,
8007
+ cardId: card.id,
8008
+ prUrl,
8009
+ cwd: worktreePath,
8010
+ provider
8011
+ });
8012
+ }
7933
8013
  }
7934
8014
  }
7935
8015
  await addLabelByName(client, card, config.review.approvedLabel, config.review.approvedLabelColor);
@@ -7957,14 +8037,14 @@ ${runLogTail}
7957
8037
  await client.updateCard(card.id, { description: desc });
7958
8038
  }
7959
8039
  } catch (err) {
7960
- log23.warn(TAG21, `Failed to persist PR URL / branch rename to #${card.short_id} description: ${err instanceof Error ? err.message : err}`);
8040
+ log24.warn(TAG22, `Failed to persist PR URL / branch rename to #${card.short_id} description: ${err instanceof Error ? err.message : err}`);
7961
8041
  }
7962
8042
  }
7963
8043
  if (branchName) {
7964
8044
  try {
7965
8045
  await persistReviewedSha(client, card, worktreePath);
7966
8046
  } catch (err) {
7967
- log23.warn(TAG21, `Failed to persist Reviewed-SHA to #${card.short_id}: ${err instanceof Error ? err.message : err}`);
8047
+ log24.warn(TAG22, `Failed to persist Reviewed-SHA to #${card.short_id}: ${err instanceof Error ? err.message : err}`);
7968
8048
  }
7969
8049
  }
7970
8050
  if (config.review.postFindings) {
@@ -7987,7 +8067,7 @@ ${runLogTail}
7987
8067
  progressPercent: 100,
7988
8068
  ...buildTokenPayload(sessionStats)
7989
8069
  });
7990
- log23.info(TAG21, `#${card.short_id} approved${prUrl ? ` — PR: ${prUrl}` : ""} — labeled "${config.review.approvedLabel}"`);
8070
+ log24.info(TAG22, `#${card.short_id} approved${prUrl ? ` — PR: ${prUrl}` : ""} — labeled "${config.review.approvedLabel}"`);
7991
8071
  } else {
7992
8072
  const reworkFindings = result.findings.filter((f) => f.relatedToDiff !== false);
7993
8073
  const criticalFindings = reworkFindings.filter((f) => f.severity === "critical").slice(0, MAX_FINDINGS);
@@ -7995,7 +8075,7 @@ ${runLogTail}
7995
8075
  const linkedFindings = [...criticalFindings, ...majorFindings];
7996
8076
  const minorFindings = reworkFindings.filter((f) => f.severity === "minor").slice(0, MAX_FINDINGS);
7997
8077
  if (currentCycle >= maxCycles) {
7998
- log23.warn(TAG21, `#${card.short_id} reached max review cycles (${maxCycles}), moving to Done with note`);
8078
+ log24.warn(TAG22, `#${card.short_id} reached max review cycles (${maxCycles}), moving to Done with note`);
7999
8079
  if (handbackVerdict.proceed) {
8000
8080
  await moveCardToColumn(client, freshCard ?? card, config.review.moveToColumn);
8001
8081
  } else {
@@ -8023,7 +8103,7 @@ ${runLogTail}
8023
8103
  try {
8024
8104
  await client.createSubtask(card.id, clampSubtaskTitle(`[${finding.severity}] ${finding.title}`));
8025
8105
  } catch (err) {
8026
- log23.error(TAG21, `Failed to create finding subtask: ${err instanceof Error ? err.message : err}`);
8106
+ log24.error(TAG22, `Failed to create finding subtask: ${err instanceof Error ? err.message : err}`);
8027
8107
  }
8028
8108
  }));
8029
8109
  if (linkedFindings.length > 0) {
@@ -8035,7 +8115,7 @@ ${runLogTail}
8035
8115
  try {
8036
8116
  await client.createSubtask(card.id, clampSubtaskTitle(finding.title));
8037
8117
  } catch (err) {
8038
- log23.error(TAG21, `Failed to create subtask: ${err instanceof Error ? err.message : err}`);
8118
+ log24.error(TAG22, `Failed to create subtask: ${err instanceof Error ? err.message : err}`);
8039
8119
  }
8040
8120
  }));
8041
8121
  if (handbackVerdict.proceed) {
@@ -8044,7 +8124,7 @@ ${runLogTail}
8044
8124
  try {
8045
8125
  await client.updateCard(card.id, { description: updatedDesc });
8046
8126
  } catch (err) {
8047
- log23.error(TAG21, `Failed to update review cycle marker: ${err instanceof Error ? err.message : err}`);
8127
+ log24.error(TAG22, `Failed to update review cycle marker: ${err instanceof Error ? err.message : err}`);
8048
8128
  }
8049
8129
  }
8050
8130
  const scopeLine = result.scopeCheck ? `Scope: ${result.scopeCheck.status}${result.scopeCheck.notes ? ` — ${result.scopeCheck.notes}` : ""}` : "";
@@ -8063,9 +8143,9 @@ ${runLogTail}
8063
8143
  if (handbackVerdict.proceed && config.planning.enabled && card.plan_id) {
8064
8144
  try {
8065
8145
  await client.updateCard(card.id, { needsPlanRefresh: true });
8066
- log23.info(TAG21, `#${card.short_id} flagged needs_plan_refresh after rejected review`);
8146
+ log24.info(TAG22, `#${card.short_id} flagged needs_plan_refresh after rejected review`);
8067
8147
  } catch (err) {
8068
- log23.warn(TAG21, `Failed to flag needs_plan_refresh for #${card.short_id}: ${err instanceof Error ? err.message : err}`);
8148
+ log24.warn(TAG22, `Failed to flag needs_plan_refresh for #${card.short_id}: ${err instanceof Error ? err.message : err}`);
8069
8149
  }
8070
8150
  }
8071
8151
  if (handbackVerdict.proceed) {
@@ -8083,10 +8163,10 @@ ${runLogTail}
8083
8163
  recoveryBranch
8084
8164
  });
8085
8165
  } catch (err) {
8086
- log23.debug(TAG21, `recordFailureSummary failed: ${err instanceof Error ? err.message : err}`);
8166
+ log24.debug(TAG22, `recordFailureSummary failed: ${err instanceof Error ? err.message : err}`);
8087
8167
  }
8088
8168
  if (recoveryBranch) {
8089
- log23.info(TAG21, `#${card.short_id} recovery branch ${recoveryBranch}${recoveryUrl ? ` (${recoveryUrl})` : ""}`);
8169
+ log24.info(TAG22, `#${card.short_id} recovery branch ${recoveryBranch}${recoveryUrl ? ` (${recoveryUrl})` : ""}`);
8090
8170
  }
8091
8171
  await client.endAgentSession(card.id, {
8092
8172
  status: "failed",
@@ -8095,19 +8175,20 @@ ${runLogTail}
8095
8175
  recoveryBranch,
8096
8176
  ...buildTokenPayload(sessionStats)
8097
8177
  });
8098
- log23.info(TAG21, `#${card.short_id} rejected (cycle ${currentCycle}/${maxCycles}) — moved to "${config.review.failColumn}"`);
8178
+ log24.info(TAG22, `#${card.short_id} rejected (cycle ${currentCycle}/${maxCycles}) — moved to "${config.review.failColumn}"`);
8099
8179
  }
8100
8180
  if (branchName) {
8101
8181
  cleanupWorktree3(worktreePath, branchName);
8102
8182
  }
8103
8183
  }
8104
- var TAG21 = "review-completion", MAX_FINDINGS = 10, MAX_SUBTASK_TITLE = 120, COMMENT_BODY_BUDGET = 9500, REVIEW_MARKER = `---
8184
+ var TAG22 = "review-completion", MAX_FINDINGS = 10, MAX_SUBTASK_TITLE = 120, COMMENT_BODY_BUDGET = 9500, REVIEW_MARKER = `---
8105
8185
  **Review:`, RUN_LOG_TAIL_BYTES = 2048;
8106
8186
  var init_review_completion = __esm(() => {
8107
8187
  init_src();
8108
8188
  init_board_helpers();
8109
8189
  init_completion();
8110
8190
  init_handback();
8191
+ init_pr_state_sync();
8111
8192
  init_types();
8112
8193
  });
8113
8194
 
@@ -8249,7 +8330,7 @@ Set \`relatedToDiff\` on every finding — only diff-caused findings gate the ve
8249
8330
  ${step3Body}
8250
8331
 
8251
8332
  ### Step 4: Output
8252
- After completing all steps, output EXACTLY one JSON block (and nothing else after it):
8333
+ After completing all steps, answer with the structured verdict. Its fields:
8253
8334
 
8254
8335
  \`\`\`json
8255
8336
  ${REVIEW_VERDICT_SCHEMA}
@@ -8290,9 +8371,9 @@ ${serverSection}
8290
8371
  ## Finish with the verdict
8291
8372
 
8292
8373
  Only this turn's output is read. A verdict you already wrote earlier in this session
8293
- does NOT count — you must output it again here, or this review produces nothing.
8374
+ does NOT count — you must answer with the structured verdict again here, or this review produces nothing.
8294
8375
 
8295
- When the review is complete, output EXACTLY one JSON block (and nothing else after it):
8376
+ When the review is complete, answer with the structured verdict. Its fields:
8296
8377
 
8297
8378
  \`\`\`json
8298
8379
  ${REVIEW_VERDICT_SCHEMA}
@@ -8324,6 +8405,61 @@ var init_review_prompt = __esm(() => {
8324
8405
  init_types();
8325
8406
  });
8326
8407
 
8408
+ // src/review-verdict-schema.ts
8409
+ var REVIEW_VERDICT_JSON_SCHEMA;
8410
+ var init_review_verdict_schema = __esm(() => {
8411
+ REVIEW_VERDICT_JSON_SCHEMA = {
8412
+ type: "object",
8413
+ properties: {
8414
+ verdict: { type: "string", enum: ["approved", "rejected"] },
8415
+ summary: { type: "string" },
8416
+ scopeCheck: {
8417
+ type: "object",
8418
+ properties: {
8419
+ status: { type: "string", enum: ["clean", "drift", "missing"] },
8420
+ notes: { type: "string" }
8421
+ },
8422
+ required: ["status"],
8423
+ additionalProperties: false
8424
+ },
8425
+ acceptanceChecks: {
8426
+ type: "array",
8427
+ items: {
8428
+ type: "object",
8429
+ properties: {
8430
+ criterion: { type: "string" },
8431
+ status: {
8432
+ type: "string",
8433
+ enum: ["pass", "partial", "fail", "unverifiable"]
8434
+ },
8435
+ evidence: { type: "string" }
8436
+ },
8437
+ required: ["criterion", "status"],
8438
+ additionalProperties: false
8439
+ }
8440
+ },
8441
+ findings: {
8442
+ type: "array",
8443
+ items: {
8444
+ type: "object",
8445
+ properties: {
8446
+ severity: { type: "string", enum: ["critical", "major", "minor"] },
8447
+ category: { type: "string" },
8448
+ title: { type: "string" },
8449
+ description: { type: "string" },
8450
+ location: { type: "string" },
8451
+ relatedToDiff: { type: "boolean" }
8452
+ },
8453
+ required: ["severity", "title", "description", "relatedToDiff"],
8454
+ additionalProperties: false
8455
+ }
8456
+ }
8457
+ },
8458
+ required: ["verdict", "summary", "acceptanceChecks", "findings"],
8459
+ additionalProperties: false
8460
+ };
8461
+ });
8462
+
8327
8463
  // src/run-log.ts
8328
8464
  import {
8329
8465
  appendFileSync,
@@ -8331,7 +8467,7 @@ import {
8331
8467
  mkdirSync as mkdirSync3
8332
8468
  } from "node:fs";
8333
8469
  import { join as join5 } from "node:path";
8334
- import { log as log24 } from "@gethmy/harness";
8470
+ import { log as log25 } from "@gethmy/harness";
8335
8471
  function runLogPath(runId, shortId) {
8336
8472
  return join5(runsDir(), `${runId}-card-${shortId}.log`);
8337
8473
  }
@@ -8345,7 +8481,7 @@ function appendRunLog(tag, runId, shortId, line) {
8345
8481
  `, "utf-8");
8346
8482
  return path;
8347
8483
  } catch (err) {
8348
- log24.warn(tag, `Failed to append to run log: ${err instanceof Error ? err.message : err}`);
8484
+ log25.warn(tag, `Failed to append to run log: ${err instanceof Error ? err.message : err}`);
8349
8485
  return null;
8350
8486
  }
8351
8487
  }
@@ -8357,11 +8493,11 @@ function openRunLog(tag, runId, shortId) {
8357
8493
  const path = runLogPath(runId, shortId);
8358
8494
  const stream = createWriteStream(path, { flags: "a" });
8359
8495
  stream.on("error", (err) => {
8360
- log24.warn(tag, `Run log ${path} failed: ${err.message}`);
8496
+ log25.warn(tag, `Run log ${path} failed: ${err.message}`);
8361
8497
  });
8362
8498
  return { path, stream };
8363
8499
  } catch (err) {
8364
- log24.warn(tag, `Failed to open run log: ${err instanceof Error ? err.message : err}`);
8500
+ log25.warn(tag, `Failed to open run log: ${err instanceof Error ? err.message : err}`);
8365
8501
  return null;
8366
8502
  }
8367
8503
  }
@@ -8398,7 +8534,7 @@ function isOwnNoticeConflict(err) {
8398
8534
  const driver = sessionConflictDriver(err);
8399
8535
  return driver === null || driver === NOTICE_DRIVER;
8400
8536
  }
8401
- async function startSessionYieldingNotice(start, client, cardId, log25) {
8537
+ async function startSessionYieldingNotice(start, client, cardId, log26) {
8402
8538
  try {
8403
8539
  return await start();
8404
8540
  } catch (err) {
@@ -8407,12 +8543,12 @@ async function startSessionYieldingNotice(start, client, cardId, log25) {
8407
8543
  try {
8408
8544
  const { session } = await client.getAgentSession(cardId);
8409
8545
  if (!session) {
8410
- log25.info(`The session that refused the start is already gone — retrying: ${err instanceof Error ? err.message : String(err)}`);
8546
+ log26.info(`The session that refused the start is already gone — retrying: ${err instanceof Error ? err.message : String(err)}`);
8411
8547
  } else if (!isPoolNotice(session)) {
8412
- log25.warn("The session that refused the start is not a pool notice on a re-read — leaving it alone");
8548
+ log26.warn("The session that refused the start is not a pool notice on a re-read — leaving it alone");
8413
8549
  throw err;
8414
8550
  } else {
8415
- log25.info(`Ending this pool's own notice on the card and retrying the session start: ${err instanceof Error ? err.message : String(err)}`);
8551
+ log26.info(`Ending this pool's own notice on the card and retrying the session start: ${err instanceof Error ? err.message : String(err)}`);
8416
8552
  await client.endAgentSession(cardId, {
8417
8553
  status: "failed",
8418
8554
  failureReason: "stale",
@@ -8422,7 +8558,7 @@ async function startSessionYieldingNotice(start, client, cardId, log25) {
8422
8558
  } catch (endErr) {
8423
8559
  if (endErr === err)
8424
8560
  throw err;
8425
- log25.warn(`Could not yield this pool's own notice: ${endErr instanceof Error ? endErr.message : String(endErr)}`);
8561
+ log26.warn(`Could not yield this pool's own notice: ${endErr instanceof Error ? endErr.message : String(endErr)}`);
8426
8562
  throw err;
8427
8563
  }
8428
8564
  return await start();
@@ -8459,7 +8595,7 @@ import {
8459
8595
  writeFileSync as writeFileSync2
8460
8596
  } from "node:fs";
8461
8597
  import { dirname as dirname2, join as join6 } from "node:path";
8462
- import { log as log25 } from "@gethmy/harness";
8598
+ import { log as log26 } from "@gethmy/harness";
8463
8599
  function emptyTokens() {
8464
8600
  return cloneWithoutPrototype({
8465
8601
  inputTokens: 0,
@@ -8598,7 +8734,7 @@ class StateStore {
8598
8734
  const raw = readFileSync6(this.path, "utf-8");
8599
8735
  const parsed = parseJsonWithoutPrototypes(raw);
8600
8736
  if (parsed?.version !== SCHEMA_VERSION) {
8601
- log25.warn(TAG22, `state file has version ${parsed?.version}, expected ${SCHEMA_VERSION} — migrating (preserving card budget/attempts, dropping in-flight runs)`);
8737
+ log26.warn(TAG23, `state file has version ${parsed?.version}, expected ${SCHEMA_VERSION} — migrating (preserving card budget/attempts, dropping in-flight runs)`);
8602
8738
  return cloneWithoutPrototype({
8603
8739
  version: SCHEMA_VERSION,
8604
8740
  daemonId: null,
@@ -8623,7 +8759,7 @@ class StateStore {
8623
8759
  account: parsed.account ?? emptyAccount()
8624
8760
  });
8625
8761
  } catch (err) {
8626
- log25.error(TAG22, `failed to read state file: ${err instanceof Error ? err.message : err}`);
8762
+ log26.error(TAG23, `failed to read state file: ${err instanceof Error ? err.message : err}`);
8627
8763
  return emptyState();
8628
8764
  }
8629
8765
  }
@@ -9020,7 +9156,7 @@ class StateStore {
9020
9156
  return this.state.daily.find((d) => d.date === key)?.costCents ?? 0;
9021
9157
  }
9022
9158
  }
9023
- var TAG22 = "state-store", SCHEMA_VERSION = 1;
9159
+ var TAG23 = "state-store", SCHEMA_VERSION = 1;
9024
9160
  var init_state_store = __esm(() => {
9025
9161
  init_src();
9026
9162
  init_paths();
@@ -9028,7 +9164,7 @@ var init_state_store = __esm(() => {
9028
9164
 
9029
9165
  // src/stream-parser.ts
9030
9166
  import { EventEmitter } from "node:events";
9031
- import { log as log26 } from "@gethmy/harness";
9167
+ import { log as log27 } from "@gethmy/harness";
9032
9168
  function normalizeToolResultContent(raw) {
9033
9169
  if (raw == null)
9034
9170
  return;
@@ -9049,7 +9185,7 @@ function normalizeToolResultContent(raw) {
9049
9185
  return String(raw);
9050
9186
  }
9051
9187
  }
9052
- var TAG23 = "stream-parser", StreamParser;
9188
+ var TAG24 = "stream-parser", StreamParser;
9053
9189
  var init_stream_parser = __esm(() => {
9054
9190
  StreamParser = class StreamParser extends EventEmitter {
9055
9191
  buffer = "";
@@ -9096,14 +9232,14 @@ var init_stream_parser = __esm(() => {
9096
9232
  try {
9097
9233
  msg = JSON.parse(line);
9098
9234
  } catch {
9099
- log26.debug(TAG23, `Non-JSON line: ${line.slice(0, 100)}`);
9235
+ log27.debug(TAG24, `Non-JSON line: ${line.slice(0, 100)}`);
9100
9236
  return;
9101
9237
  }
9102
9238
  try {
9103
9239
  this.handleMessage(msg);
9104
9240
  } catch (err) {
9105
9241
  const errMsg = err instanceof Error ? err.message : String(err);
9106
- log26.warn(TAG23, `Error handling stream event: ${errMsg}`);
9242
+ log27.warn(TAG24, `Error handling stream event: ${errMsg}`);
9107
9243
  this.emit("parse_error", errMsg);
9108
9244
  }
9109
9245
  }
@@ -9170,6 +9306,9 @@ var init_stream_parser = __esm(() => {
9170
9306
  modelName: this.observedModel ?? (typeof msg.model === "string" ? msg.model : undefined)
9171
9307
  });
9172
9308
  }
9309
+ if (msg.structured_output !== undefined) {
9310
+ this.emit("structured_output", msg.structured_output);
9311
+ }
9173
9312
  this.emit("result", msg.stop_reason ?? msg.subtype ?? "unknown");
9174
9313
  break;
9175
9314
  }
@@ -9179,7 +9318,7 @@ var init_stream_parser = __esm(() => {
9179
9318
  });
9180
9319
 
9181
9320
  // src/transitions.ts
9182
- import { log as log27 } from "@gethmy/harness";
9321
+ import { log as log28 } from "@gethmy/harness";
9183
9322
  async function withRetry(step, cardShortId, op, attempts, backoffMs) {
9184
9323
  let lastErr;
9185
9324
  for (let i = 0;i < attempts; i++) {
@@ -9190,7 +9329,7 @@ async function withRetry(step, cardShortId, op, attempts, backoffMs) {
9190
9329
  const msg2 = err instanceof Error ? err.message : String(err);
9191
9330
  if (i < attempts - 1) {
9192
9331
  const wait = backoffMs * 2 ** i;
9193
- log27.warn(TAG24, `${step} failed for #${cardShortId} (attempt ${i + 1}/${attempts}): ${msg2} — retrying in ${wait}ms`);
9332
+ log28.warn(TAG25, `${step} failed for #${cardShortId} (attempt ${i + 1}/${attempts}): ${msg2} — retrying in ${wait}ms`);
9194
9333
  await new Promise((r) => setTimeout(r, wait));
9195
9334
  }
9196
9335
  }
@@ -9214,10 +9353,10 @@ async function runTransition(client, card, plan, opts = {}) {
9214
9353
  if (opts.strictColumn) {
9215
9354
  throw new TransitionError("move", 1, msg);
9216
9355
  }
9217
- log27.warn(TAG24, `#${shortId}: ${msg} — skipping move`);
9356
+ log28.warn(TAG25, `#${shortId}: ${msg} — skipping move`);
9218
9357
  } else if (card.column_id !== target.id) {
9219
9358
  await withRetry("move", shortId, () => client.moveCard(card.id, target.id), attempts, backoffMs);
9220
- log27.info(TAG24, `#${shortId} → "${target.name}"`);
9359
+ log28.info(TAG25, `#${shortId} → "${target.name}"`);
9221
9360
  card.column_id = target.id;
9222
9361
  moveLanded = true;
9223
9362
  } else {
@@ -9236,7 +9375,7 @@ async function runTransition(client, card, plan, opts = {}) {
9236
9375
  continue;
9237
9376
  await withRetry("addLabel", shortId, () => client.addLabelToCard(card.id, labelId), attempts, backoffMs);
9238
9377
  existing.add(labelId);
9239
- log27.info(TAG24, `#${shortId} +label "${name}"`);
9378
+ log28.info(TAG25, `#${shortId} +label "${name}"`);
9240
9379
  }
9241
9380
  card.labelIds = Array.from(existing);
9242
9381
  }
@@ -9248,23 +9387,23 @@ async function runTransition(client, card, plan, opts = {}) {
9248
9387
  continue;
9249
9388
  await withRetry("removeLabel", shortId, () => client.removeLabelFromCard(card.id, match.id), attempts, backoffMs);
9250
9389
  existing.delete(match.id);
9251
- log27.info(TAG24, `#${shortId} -label "${name}"`);
9390
+ log28.info(TAG25, `#${shortId} -label "${name}"`);
9252
9391
  }
9253
9392
  card.labelIds = Array.from(existing);
9254
9393
  }
9255
9394
  if (plan.updateCard) {
9256
9395
  await withRetry("updateCard", shortId, () => client.updateCard(card.id, plan.updateCard), attempts, backoffMs);
9257
- log27.info(TAG24, `#${shortId} updated`);
9396
+ log28.info(TAG25, `#${shortId} updated`);
9258
9397
  }
9259
9398
  if (plan.endSession) {
9260
9399
  const endResult = await withRetry("endSession", shortId, () => client.endAgentSession(card.id, plan.endSession), attempts, backoffMs);
9261
9400
  result.endSession = endResult;
9262
- log27.info(TAG24, `#${shortId} session ended (${plan.endSession.status})`);
9401
+ log28.info(TAG25, `#${shortId} session ended (${plan.endSession.status})`);
9263
9402
  }
9264
9403
  if (plan.assignAgent !== undefined) {
9265
9404
  const assignedAgentId = plan.assignAgent;
9266
9405
  await withRetry("assignAgent", shortId, () => client.updateCard(card.id, { assignedAgentId }), attempts, backoffMs);
9267
- log27.info(TAG24, assignedAgentId ? `#${shortId} assigned → agent ${assignedAgentId}` : `#${shortId} unassigned`);
9406
+ log28.info(TAG25, assignedAgentId ? `#${shortId} assigned → agent ${assignedAgentId}` : `#${shortId} unassigned`);
9268
9407
  }
9269
9408
  if (opts.store && opts.runId) {
9270
9409
  try {
@@ -9278,11 +9417,11 @@ async function ensureLabel(client, projectId, name, color, attempts, backoffMs)
9278
9417
  const result = await withRetry("addLabel", 0, () => client.createLabel(projectId, { name, color: color ?? "#8b5cf6" }), attempts, backoffMs);
9279
9418
  return result?.label?.id ?? null;
9280
9419
  } catch (err) {
9281
- log27.warn(TAG24, `ensureLabel "${name}" failed: ${err instanceof Error ? err.message : err}`);
9420
+ log28.warn(TAG25, `ensureLabel "${name}" failed: ${err instanceof Error ? err.message : err}`);
9282
9421
  return null;
9283
9422
  }
9284
9423
  }
9285
- var TAG24 = "transition", TransitionError;
9424
+ var TAG25 = "transition", TransitionError;
9286
9425
  var init_transitions = __esm(() => {
9287
9426
  TransitionError = class TransitionError extends Error {
9288
9427
  step;
@@ -9312,7 +9451,7 @@ import {
9312
9451
  formatDiffSummary,
9313
9452
  GIT_NO_HOOKS as GIT_NO_HOOKS7,
9314
9453
  implementRunContainmentCliArgs,
9315
- log as log28,
9454
+ log as log29,
9316
9455
  probeDevServer,
9317
9456
  removeSandboxContainer,
9318
9457
  resolveStageGate,
@@ -9408,11 +9547,11 @@ class ReviewWorker {
9408
9547
  cliSessionId: this.cliSessionId
9409
9548
  });
9410
9549
  } catch (err) {
9411
- log28.warn(this.tag, `state store updateRun failed: ${err instanceof Error ? err.message : err}`);
9550
+ log29.warn(this.tag, `state store updateRun failed: ${err instanceof Error ? err.message : err}`);
9412
9551
  }
9413
9552
  }
9414
9553
  get tag() {
9415
- return `${TAG25}:${this.id}`;
9554
+ return `${TAG26}:${this.id}`;
9416
9555
  }
9417
9556
  get isIdle() {
9418
9557
  return this.state === "idle";
@@ -9450,7 +9589,7 @@ class ReviewWorker {
9450
9589
  try {
9451
9590
  const after = await this.stateStore.addTurns(cardId, numTurns);
9452
9591
  if (crossedTurnCap(before, after, cap)) {
9453
- log28.warn(TAG25, `#${this.stateStore.getRun(this.runId ?? "")?.cardShortId ?? cardId} reached its per-card turn cap on a review leg — ${after} of ${cap} turns. The implement pipeline will not pick it up again until it is reassigned or the cap is raised.`);
9592
+ log29.warn(TAG26, `#${this.stateStore.getRun(this.runId ?? "")?.cardShortId ?? cardId} reached its per-card turn cap on a review leg — ${after} of ${cap} turns. The implement pipeline will not pick it up again until it is reassigned or the cap is raised.`);
9454
9593
  const legs = this.stateStore.getRun(this.runId ?? "")?.legs?.length || 1;
9455
9594
  await this.client.addComment(cardId, buildTurnCapComment(after, cap, legs), {
9456
9595
  commentType: "blocker",
@@ -9492,12 +9631,12 @@ class ReviewWorker {
9492
9631
  resumeMessage: null
9493
9632
  });
9494
9633
  } catch (err) {
9495
- log28.warn(this.tag, `Failed to clear the consumed grant: ${err}`);
9634
+ log29.warn(this.tag, `Failed to clear the consumed grant: ${err}`);
9496
9635
  }
9497
9636
  }
9498
9637
  try {
9499
9638
  this.state = "preparing";
9500
- log28.info(this.tag, resuming ? `Resuming review of #${card.short_id} "${card.title}" with ${this.grantedTurns ?? "the default"} more turns` : `Preparing review for #${card.short_id} "${card.title}"`);
9639
+ log29.info(this.tag, resuming ? `Resuming review of #${card.short_id} "${card.title}" with ${this.grantedTurns ?? "the default"} more turns` : `Preparing review for #${card.short_id} "${card.title}"`);
9501
9640
  this.startHeartbeat();
9502
9641
  if (!resuming) {
9503
9642
  await this.stateStore.insertRun({
@@ -9525,7 +9664,7 @@ class ReviewWorker {
9525
9664
  const resolution = await resolveReviewBranch(card.description, repoRoot);
9526
9665
  if (resolution.kind !== "branch") {
9527
9666
  const why = resolution.kind === "skip" ? resolution.reason : "no branch or PR reference";
9528
- log28.info(this.tag, `#${card.short_id} not auto-reviewable (${why}) — marking for human review (staying in Review)`);
9667
+ log29.info(this.tag, `#${card.short_id} not auto-reviewable (${why}) — marking for human review (staying in Review)`);
9529
9668
  await addLabelByName(this.client, card, NEED_REVIEW_LABEL, NEED_REVIEW_LABEL_COLOR);
9530
9669
  return;
9531
9670
  }
@@ -9536,13 +9675,13 @@ class ReviewWorker {
9536
9675
  reviewedSha: extractReviewedSha2(card.description ?? null)
9537
9676
  });
9538
9677
  if (continuity.kind === "skip") {
9539
- log28.warn(this.tag, `#${card.short_id} refusing the PR-derived re-review (${continuity.reason}) — marking for human review (staying in Review)`);
9678
+ log29.warn(this.tag, `#${card.short_id} refusing the PR-derived re-review (${continuity.reason}) — marking for human review (staying in Review)`);
9540
9679
  await addLabelByName(this.client, card, NEED_REVIEW_LABEL, NEED_REVIEW_LABEL_COLOR);
9541
9680
  return;
9542
9681
  }
9543
9682
  }
9544
9683
  this.branchName = resolution.branch;
9545
- log28.info(this.tag, `Review branch: ${this.branchName}`);
9684
+ log29.info(this.tag, `Review branch: ${this.branchName}`);
9546
9685
  let reviewSession;
9547
9686
  try {
9548
9687
  const startSession = () => this.client.startAgentSession(card.id, {
@@ -9558,14 +9697,14 @@ class ReviewWorker {
9558
9697
  awaitingDecisionUntil: null
9559
9698
  });
9560
9699
  const started = await startSessionYieldingNotice(startSession, this.client, card.id, {
9561
- info: (message) => log28.info(this.tag, message),
9562
- warn: (message) => log28.warn(this.tag, message)
9700
+ info: (message) => log29.info(this.tag, message),
9701
+ warn: (message) => log29.warn(this.tag, message)
9563
9702
  });
9564
9703
  reviewSession = started.session;
9565
9704
  } catch (err) {
9566
9705
  if (isSessionConflict(err)) {
9567
9706
  this.sessionConflict = true;
9568
- log28.info(this.tag, `Skipping review of #${card.short_id}: ${err instanceof Error ? err.message : String(err)}`);
9707
+ log29.info(this.tag, `Skipping review of #${card.short_id}: ${err instanceof Error ? err.message : String(err)}`);
9569
9708
  return;
9570
9709
  }
9571
9710
  throw err;
@@ -9586,7 +9725,7 @@ class ReviewWorker {
9586
9725
  const cwd = this.worktreePath;
9587
9726
  const devServer = await this.startDevServer(card, cwd, port);
9588
9727
  if (devServer.skipReason) {
9589
- log28.warn(this.tag, `Reviewing #${card.short_id} without the visual lens — ${devServer.skipReason}`);
9728
+ log29.warn(this.tag, `Reviewing #${card.short_id} without the visual lens — ${devServer.skipReason}`);
9590
9729
  }
9591
9730
  await this.client.updateAgentProgress(card.id, {
9592
9731
  agentIdentifier: this.sessionIdentifier,
@@ -9624,10 +9763,10 @@ class ReviewWorker {
9624
9763
  pinnedContract = extractPinnedContract(comments, this.identity);
9625
9764
  }
9626
9765
  } catch (err) {
9627
- log28.warn(this.tag, `pinned-contract fetch failed for #${card.short_id}: ${err instanceof Error ? err.message : err}`);
9766
+ log29.warn(this.tag, `pinned-contract fetch failed for #${card.short_id}: ${err instanceof Error ? err.message : err}`);
9628
9767
  }
9629
9768
  if (pinnedContract) {
9630
- log28.info(this.tag, `Grading pinned contract for #${card.short_id} (${pinnedContract.assertions.length} criteria)`);
9769
+ log29.info(this.tag, `Grading pinned contract for #${card.short_id} (${pinnedContract.assertions.length} criteria)`);
9631
9770
  }
9632
9771
  }
9633
9772
  const systemPrompt = buildReviewSystemPrompt();
@@ -9642,7 +9781,7 @@ class ReviewWorker {
9642
9781
  } else {
9643
9782
  const planCriteria2 = await fetchAcceptanceCriteria(this.client, this.tag, card);
9644
9783
  if (planCriteria2.length > 0) {
9645
- log28.info(this.tag, `Grading ${planCriteria2.length} plan criteria for #${card.short_id}`);
9784
+ log29.info(this.tag, `Grading ${planCriteria2.length} plan criteria for #${card.short_id}`);
9646
9785
  }
9647
9786
  userPrompt = buildReviewUserPrompt(enriched, this.branchName, cwd, previewUrl, diffSummary, this.config.worktree.baseBranch, undefined, pinnedContract, planCriteria2, devServer.skipReason);
9648
9787
  if (resuming && this.resumeMessage) {
@@ -9659,7 +9798,7 @@ ${userPrompt}`;
9659
9798
  contextIncluded: { source: "review-knowledge", mode: "review" }
9660
9799
  });
9661
9800
  } catch (err) {
9662
- log28.warn(this.tag, `prompt_history persistence skipped: ${err instanceof Error ? err.message : String(err)}`);
9801
+ log29.warn(this.tag, `prompt_history persistence skipped: ${err instanceof Error ? err.message : String(err)}`);
9663
9802
  }
9664
9803
  await this.client.updateAgentProgress(card.id, {
9665
9804
  agentIdentifier: this.sessionIdentifier,
@@ -9669,7 +9808,7 @@ ${userPrompt}`;
9669
9808
  progressPercent: 20
9670
9809
  });
9671
9810
  this.timeoutTimer = setTimeout(() => {
9672
- log28.warn(this.tag, `Review timeout reached (${this.config.review.maxTimeout}ms), cancelling`);
9811
+ log29.warn(this.tag, `Review timeout reached (${this.config.review.maxTimeout}ms), cancelling`);
9673
9812
  this.timedOut = true;
9674
9813
  this.cancel("timeout");
9675
9814
  }, this.config.review.maxTimeout);
@@ -9691,10 +9830,10 @@ ${userPrompt}`;
9691
9830
  }
9692
9831
  this.state = "completing";
9693
9832
  await this.recordPhase("completing");
9694
- log28.info(this.tag, `Claude review finished for #${card.short_id}`);
9833
+ log29.info(this.tag, `Claude review finished for #${card.short_id}`);
9695
9834
  this.killDevServer();
9696
- const result = parseReviewOutput(stdout);
9697
- log28.info(this.tag, `Review verdict: ${result.verdict} (${result.findings.length} finding(s))`);
9835
+ const result = parseStructuredReview(stdout);
9836
+ log29.info(this.tag, `Review verdict: ${result.verdict} (${result.findings.length} finding(s))`);
9698
9837
  await this.client.updateAgentProgress(card.id, {
9699
9838
  agentIdentifier: this.sessionIdentifier,
9700
9839
  agentName: `${AGENT_NAME} (Review)`,
@@ -9716,7 +9855,7 @@ ${userPrompt}`;
9716
9855
  this.state = "error";
9717
9856
  const msg = err instanceof Error ? err.message : String(err);
9718
9857
  this.lastErrorMessage = msg;
9719
- log28.error(this.tag, `Error reviewing #${card.short_id}: ${msg}`);
9858
+ log29.error(this.tag, `Error reviewing #${card.short_id}: ${msg}`);
9720
9859
  this.lastRunLogPath = appendRunLog(this.tag, this.runId, card.short_id, `[${new Date().toISOString()}] review failed before completion: ${msg}`) ?? this.lastRunLogPath;
9721
9860
  const reviewHandback = await guardedHandback(this.client, card.id, {
9722
9861
  agentId: this.identity.agentId,
@@ -9732,7 +9871,7 @@ No verdict was produced, so this card has not been reviewed. The run's log is at
9732
9871
  agentSessionId: this.sessionId ?? undefined
9733
9872
  });
9734
9873
  } catch (cErr) {
9735
- log28.warn(this.tag, `Failed to post the failure reason to #${card.short_id}: ${cErr instanceof Error ? cErr.message : cErr}`);
9874
+ log29.warn(this.tag, `Failed to post the failure reason to #${card.short_id}: ${cErr instanceof Error ? cErr.message : cErr}`);
9736
9875
  }
9737
9876
  }
9738
9877
  try {
@@ -9744,21 +9883,21 @@ No verdict was produced, so this card has not been reviewed. The run's log is at
9744
9883
  }
9745
9884
  });
9746
9885
  } catch (tErr) {
9747
- log28.error(this.tag, `endAgentSession unrecoverable on #${card.short_id}: ${tErr instanceof TransitionError ? tErr.detail : tErr}`);
9886
+ log29.error(this.tag, `endAgentSession unrecoverable on #${card.short_id}: ${tErr instanceof TransitionError ? tErr.detail : tErr}`);
9748
9887
  }
9749
9888
  if (reviewHandback.verdict.proceed) {
9750
9889
  try {
9751
9890
  await moveCardToColumn(this.client, reviewHandback.card ?? card, this.config.review.failColumn);
9752
- log28.info(this.tag, `Moved #${card.short_id} to "${this.config.review.failColumn}" after error`);
9891
+ log29.info(this.tag, `Moved #${card.short_id} to "${this.config.review.failColumn}" after error`);
9753
9892
  } catch {
9754
- log28.warn(this.tag, "Failed to move card to fail column after error");
9893
+ log29.warn(this.tag, "Failed to move card to fail column after error");
9755
9894
  }
9756
9895
  } else if (reviewHandback.verdict.reason === "unreadable" || reviewHandback.verdict.reason === "released" || reviewHandback.verdict.reason === "done") {
9757
9896
  try {
9758
9897
  await addLabelByName(this.client, card, NEED_REVIEW_LABEL, NEED_REVIEW_LABEL_COLOR);
9759
- log28.warn(this.tag, `#${card.short_id} could not be re-read — labelled "${NEED_REVIEW_LABEL}" so reconcile stops re-enqueueing the review`);
9898
+ log29.warn(this.tag, `#${card.short_id} could not be re-read — labelled "${NEED_REVIEW_LABEL}" so reconcile stops re-enqueueing the review`);
9760
9899
  } catch {
9761
- log28.warn(this.tag, `Failed to add "${NEED_REVIEW_LABEL}" label after an unreadable card`);
9900
+ log29.warn(this.tag, `Failed to add "${NEED_REVIEW_LABEL}" label after an unreadable card`);
9762
9901
  }
9763
9902
  }
9764
9903
  if (this.runId) {
@@ -9802,7 +9941,7 @@ No verdict was produced, so this card has not been reviewed. The run's log is at
9802
9941
  const holderMessage = err instanceof Error ? err.message : String(err);
9803
9942
  const waitHours = this.config.budget.pause.waitHours;
9804
9943
  const until = computeDecisionDeadline(waitHours);
9805
- log28.warn(this.tag, `#${card.short_id} stays parked — the resume could not reclaim its session: ${holderMessage}`);
9944
+ log29.warn(this.tag, `#${card.short_id} stays parked — the resume could not reclaim its session: ${holderMessage}`);
9806
9945
  try {
9807
9946
  await this.client.addComment(card.id, formatResumeConflictComment({
9808
9947
  holderMessage,
@@ -9814,7 +9953,7 @@ No verdict was produced, so this card has not been reviewed. The run's log is at
9814
9953
  agentSessionId: this.sessionId ?? undefined
9815
9954
  });
9816
9955
  } catch (commentErr) {
9817
- log28.warn(this.tag, `Failed to post the resume-conflict note for #${card.short_id}: ${commentErr}`);
9956
+ log29.warn(this.tag, `Failed to post the resume-conflict note for #${card.short_id}: ${commentErr}`);
9818
9957
  }
9819
9958
  if (this.runId) {
9820
9959
  const run = this.stateStore.getRun(this.runId);
@@ -9825,7 +9964,7 @@ No verdict was produced, so this card has not been reviewed. The run's log is at
9825
9964
  awaitingDecisionUntil: until
9826
9965
  });
9827
9966
  } catch (storeErr) {
9828
- log28.error(this.tag, `#${card.short_id} could not be re-parked after a resume conflict — the daemon has no hold on it: ${storeErr}`);
9967
+ log29.error(this.tag, `#${card.short_id} could not be re-parked after a resume conflict — the daemon has no hold on it: ${storeErr}`);
9829
9968
  }
9830
9969
  }
9831
9970
  }
@@ -9838,7 +9977,7 @@ No verdict was produced, so this card has not been reviewed. The run's log is at
9838
9977
  this.progressTracker = null;
9839
9978
  const waitHours = this.config.budget.pause.waitHours;
9840
9979
  const until = computeDecisionDeadline(waitHours);
9841
- log28.warn(this.tag, `#${card.short_id} parked (${trigger}) — awaiting a human decision for ${waitHours}h`);
9980
+ log29.warn(this.tag, `#${card.short_id} parked (${trigger}) — awaiting a human decision for ${waitHours}h`);
9842
9981
  const body = formatBudgetComment({
9843
9982
  trigger,
9844
9983
  numTurns: stats?.cost?.numTurns ?? 0,
@@ -9860,7 +9999,7 @@ No verdict was produced, so this card has not been reviewed. The run's log is at
9860
9999
  });
9861
10000
  commentId = res?.comment?.id ?? null;
9862
10001
  } catch (err) {
9863
- log28.warn(this.tag, `Failed to post the budget-pause comment: ${err}`);
10002
+ log29.warn(this.tag, `Failed to post the budget-pause comment: ${err}`);
9864
10003
  }
9865
10004
  try {
9866
10005
  await this.client.updateAgentProgress(card.id, {
@@ -9871,7 +10010,7 @@ No verdict was produced, so this card has not been reviewed. The run's log is at
9871
10010
  awaitingDecisionUntil: new Date(until).toISOString()
9872
10011
  });
9873
10012
  } catch (err) {
9874
- log28.warn(this.tag, `Failed to mark the session blocked: ${err}`);
10013
+ log29.warn(this.tag, `Failed to mark the session blocked: ${err}`);
9875
10014
  }
9876
10015
  if (this.runId) {
9877
10016
  try {
@@ -9882,14 +10021,14 @@ No verdict was produced, so this card has not been reviewed. The run's log is at
9882
10021
  ...this.endLedger()
9883
10022
  });
9884
10023
  } catch (err) {
9885
- log28.error(this.tag, `#${card.short_id} parkRun failed after the blocker comment + blocked session were already written — the daemon has no local record of this park: ${err}`);
10024
+ log29.error(this.tag, `#${card.short_id} parkRun failed after the blocker comment + blocked session were already written — the daemon has no local record of this park: ${err}`);
9886
10025
  }
9887
10026
  }
9888
10027
  }
9889
10028
  async pause() {
9890
10029
  if (!this.isActive || !this.process || this.process.killed)
9891
10030
  return;
9892
- log28.info(this.tag, `Pausing review on ${this.cardId}`);
10031
+ log29.info(this.tag, `Pausing review on ${this.cardId}`);
9893
10032
  signalGroup(this.process, "SIGSTOP");
9894
10033
  if (this.timeoutTimer) {
9895
10034
  clearTimeout(this.timeoutTimer);
@@ -9903,17 +10042,17 @@ No verdict was produced, so this card has not been reviewed. The run's log is at
9903
10042
  status: "paused"
9904
10043
  });
9905
10044
  } catch {
9906
- log28.warn(this.tag, "Failed to update agent session to paused");
10045
+ log29.warn(this.tag, "Failed to update agent session to paused");
9907
10046
  }
9908
10047
  }
9909
10048
  }
9910
10049
  async resume() {
9911
10050
  if (!this.isActive || !this.process || this.process.killed)
9912
10051
  return;
9913
- log28.info(this.tag, `Resuming review on ${this.cardId}`);
10052
+ log29.info(this.tag, `Resuming review on ${this.cardId}`);
9914
10053
  signalGroup(this.process, "SIGCONT");
9915
10054
  this.timeoutTimer = setTimeout(() => {
9916
- log28.warn(this.tag, `Timeout reached (${this.config.review.maxTimeout}ms), cancelling`);
10055
+ log29.warn(this.tag, `Timeout reached (${this.config.review.maxTimeout}ms), cancelling`);
9917
10056
  this.timedOut = true;
9918
10057
  this.cancel("timeout");
9919
10058
  }, this.config.review.maxTimeout);
@@ -9925,7 +10064,7 @@ No verdict was produced, so this card has not been reviewed. The run's log is at
9925
10064
  status: "working"
9926
10065
  });
9927
10066
  } catch {
9928
- log28.warn(this.tag, "Failed to update agent session to working");
10067
+ log29.warn(this.tag, "Failed to update agent session to working");
9929
10068
  }
9930
10069
  }
9931
10070
  }
@@ -9934,7 +10073,7 @@ No verdict was produced, so this card has not been reviewed. The run's log is at
9934
10073
  return;
9935
10074
  this.aborted = true;
9936
10075
  this.state = "cancelling";
9937
- log28.info(this.tag, `Cancelling review on ${this.cardId}`);
10076
+ log29.info(this.tag, `Cancelling review on ${this.cardId}`);
9938
10077
  const snapshotStats = this.lastSessionStats ?? this.progressTracker?.stats;
9939
10078
  if (this.progressTracker) {
9940
10079
  this.progressTracker?.stop();
@@ -9975,6 +10114,8 @@ No verdict was produced, so this card has not been reviewed. The run's log is at
9975
10114
  String(effectiveMaxTurns),
9976
10115
  "--allowedTools",
9977
10116
  "Bash(readonly),Read,Glob,Grep,Agent,mcp__harmony__*",
10117
+ "--json-schema",
10118
+ JSON.stringify(REVIEW_VERDICT_JSON_SCHEMA),
9978
10119
  ...opts.resumeSessionId ? ["--resume", opts.resumeSessionId] : [],
9979
10120
  ...systemPrompt ? ["--append-system-prompt", systemPrompt] : [],
9980
10121
  ...this.config.claude.additionalArgs,
@@ -9987,11 +10128,11 @@ No verdict was produced, so this card has not been reviewed. The run's log is at
9987
10128
  "--",
9988
10129
  prompt
9989
10130
  ];
9990
- log28.info(this.tag, `Spawning review: claude ${args.slice(0, 5).join(" ")} ...`);
10131
+ log29.info(this.tag, `Spawning review: claude ${args.slice(0, 5).join(" ")} ...`);
9991
10132
  const runLog = openRunLog(this.tag, this.runId, shortId);
9992
10133
  this.lastRunLogPath = runLog?.path ?? null;
9993
10134
  if (runLog) {
9994
- log28.info(this.tag, `Run log: ${runLog.path}`);
10135
+ log29.info(this.tag, `Run log: ${runLog.path}`);
9995
10136
  runLog.stream.write(`# run=${this.runId} card=#${shortId} pipeline=review started=${new Date().toISOString()}
9996
10137
  ` + `# args: ${args.slice(0, -2).join(" ")} -- <prompt:${prompt.length} chars>
9997
10138
 
@@ -10005,12 +10146,16 @@ No verdict was produced, so this card has not been reviewed. The run's log is at
10005
10146
  const parser = new StreamParser;
10006
10147
  tracker.attach(parser);
10007
10148
  const textChunks = [];
10149
+ let structuredOutput;
10150
+ parser.on("structured_output", (output) => {
10151
+ structuredOutput = output;
10152
+ });
10008
10153
  parser.on("text", (content) => {
10009
10154
  textChunks.push(content);
10010
10155
  this.captureCliSessionId(parser.sessionId);
10011
10156
  });
10012
10157
  parser.on("parse_error", (msg) => {
10013
- log28.debug(this.tag, `Stream parse error (non-fatal): ${msg}`);
10158
+ log29.debug(this.tag, `Stream parse error (non-fatal): ${msg}`);
10014
10159
  runLog?.stream.write(`
10015
10160
  [parse_error] ${msg}
10016
10161
  `);
@@ -10037,7 +10182,7 @@ No verdict was produced, so this card has not been reviewed. The run's log is at
10037
10182
  this.process?.on("close", (code) => {
10038
10183
  this.process = null;
10039
10184
  this.captureCliSessionId(parser.sessionId);
10040
- const stdout = textChunks.join("");
10185
+ const stdout = structuredOutput !== undefined ? JSON.stringify(structuredOutput) : textChunks.join("");
10041
10186
  const stats = tracker.stats;
10042
10187
  if (runLog) {
10043
10188
  runLog.stream.write(`
@@ -10089,16 +10234,16 @@ No verdict was produced, so this card has not been reviewed. The run's log is at
10089
10234
  const evidence = await collectGateEvidence(registry, context);
10090
10235
  const evaluation = gateEvaluate(resolved.gate, evidence);
10091
10236
  await this.client.request("POST", `/cards/${encodeURIComponent(card.id)}/stage-gate-evidence`, toStageGateEvidenceInsert(context, evidence));
10092
- log28.info(this.tag, `Recorded review_passed gate evidence for #${card.short_id} stage "${resolved.stage.name}": result=${evidence.result} passed=${evaluation.passed}`);
10237
+ log29.info(this.tag, `Recorded review_passed gate evidence for #${card.short_id} stage "${resolved.stage.name}": result=${evidence.result} passed=${evaluation.passed}`);
10093
10238
  } catch (err) {
10094
- log28.warn(this.tag, `review gate evidence collection failed for #${card.short_id}: ${err instanceof Error ? err.message : err}`);
10239
+ log29.warn(this.tag, `review gate evidence collection failed for #${card.short_id}: ${err instanceof Error ? err.message : err}`);
10095
10240
  }
10096
10241
  }
10097
10242
  async startDevServer(card, cwd, port) {
10098
10243
  const declined = devServerSkipReason(cwd, this.config.commands);
10099
10244
  if (declined)
10100
10245
  return { previewUrl: null, skipReason: declined };
10101
- log28.info(this.tag, `Starting dev server on port ${port}...`);
10246
+ log29.info(this.tag, `Starting dev server on port ${port}...`);
10102
10247
  const launch = devServerLaunch({
10103
10248
  worktreePath: cwd,
10104
10249
  port,
@@ -10139,14 +10284,14 @@ No verdict was produced, so this card has not been reviewed. The run's log is at
10139
10284
  skipReason: launch.origin ? `${launch.origin}, and ${failure}` : failure
10140
10285
  };
10141
10286
  }
10142
- log28.info(this.tag, `Dev server ready on port ${port}`);
10287
+ log29.info(this.tag, `Dev server ready on port ${port}`);
10143
10288
  return { previewUrl: `http://localhost:${port}`, skipReason: null };
10144
10289
  }
10145
10290
  killDevServer() {
10146
10291
  if (this.devServerProcess && !this.devServerProcess.killed) {
10147
10292
  signalGroup(this.devServerProcess, "SIGTERM");
10148
10293
  this.devServerProcess = null;
10149
- log28.debug(this.tag, "Killed dev server group");
10294
+ log29.debug(this.tag, "Killed dev server group");
10150
10295
  }
10151
10296
  if (this.devServerContainer) {
10152
10297
  const name = this.devServerContainer;
@@ -10169,7 +10314,7 @@ No verdict was produced, so this card has not been reviewed. The run's log is at
10169
10314
  try {
10170
10315
  cleanupWorktree4(this.worktreePath);
10171
10316
  } catch {
10172
- log28.warn(this.tag, "Failed to cleanup review worktree");
10317
+ log29.warn(this.tag, "Failed to cleanup review worktree");
10173
10318
  }
10174
10319
  }
10175
10320
  this.process = null;
@@ -10181,7 +10326,7 @@ No verdict was produced, so this card has not been reviewed. The run's log is at
10181
10326
  this.lastSessionStats = null;
10182
10327
  }
10183
10328
  }
10184
- var TAG25 = "review-worker", CANCEL_SIGINT_TIMEOUT = 30000, CANCEL_SIGTERM_TIMEOUT = 1e4;
10329
+ var TAG26 = "review-worker", CANCEL_SIGINT_TIMEOUT = 30000, CANCEL_SIGTERM_TIMEOUT = 1e4;
10185
10330
  var init_review_worker = __esm(() => {
10186
10331
  init_src();
10187
10332
  init_board_helpers();
@@ -10194,6 +10339,7 @@ var init_review_worker = __esm(() => {
10194
10339
  init_prompt();
10195
10340
  init_review_completion();
10196
10341
  init_review_prompt();
10342
+ init_review_verdict_schema();
10197
10343
  init_review_worktree();
10198
10344
  init_run_log();
10199
10345
  init_session_conflict();
@@ -10205,7 +10351,7 @@ var init_review_worker = __esm(() => {
10205
10351
 
10206
10352
  // src/sleep-guard.ts
10207
10353
  import { spawn } from "node:child_process";
10208
- import { log as log29 } from "@gethmy/harness";
10354
+ import { log as log30 } from "@gethmy/harness";
10209
10355
 
10210
10356
  class SleepGuard {
10211
10357
  platform;
@@ -10233,7 +10379,7 @@ class SleepGuard {
10233
10379
  if (!this.child.killed)
10234
10380
  this.child.kill("SIGTERM");
10235
10381
  this.child = null;
10236
- log29.info(TAG26, "sleep assertion released");
10382
+ log30.info(TAG27, "sleep assertion released");
10237
10383
  }
10238
10384
  }
10239
10385
  start() {
@@ -10248,7 +10394,7 @@ class SleepGuard {
10248
10394
  spawned = true;
10249
10395
  });
10250
10396
  child.on("error", (err) => {
10251
- log29.warn(TAG26, `caffeinate unavailable: ${err.message}`);
10397
+ log30.warn(TAG27, `caffeinate unavailable: ${err.message}`);
10252
10398
  if (this.child === child)
10253
10399
  this.child = null;
10254
10400
  });
@@ -10261,13 +10407,13 @@ class SleepGuard {
10261
10407
  });
10262
10408
  child.unref();
10263
10409
  this.child = child;
10264
- log29.info(TAG26, "sleep assertion acquired (caffeinate -i)");
10410
+ log30.info(TAG27, "sleep assertion acquired (caffeinate -i)");
10265
10411
  } catch (err) {
10266
- log29.warn(TAG26, `failed to spawn caffeinate: ${err instanceof Error ? err.message : err}`);
10412
+ log30.warn(TAG27, `failed to spawn caffeinate: ${err instanceof Error ? err.message : err}`);
10267
10413
  }
10268
10414
  }
10269
10415
  }
10270
- var TAG26 = "sleep-guard";
10416
+ var TAG27 = "sleep-guard";
10271
10417
  var init_sleep_guard = () => {};
10272
10418
 
10273
10419
  // src/stage-hold.ts
@@ -10292,13 +10438,13 @@ function stageHoldUntil(holdMinutes, nowMs) {
10292
10438
  }
10293
10439
 
10294
10440
  // src/unblock.ts
10295
- import { log as log30 } from "@gethmy/harness";
10441
+ import { log as log31 } from "@gethmy/harness";
10296
10442
  async function fetchBlocksLinks(client, cardId) {
10297
10443
  try {
10298
10444
  const { links } = await client.getCardLinks(cardId);
10299
10445
  return links.filter((l) => l.link_type === "blocks");
10300
10446
  } catch (err) {
10301
- log30.warn(TAG27, `link fetch failed for ${cardId}: ${err instanceof Error ? err.message : err}`);
10447
+ log31.warn(TAG28, `link fetch failed for ${cardId}: ${err instanceof Error ? err.message : err}`);
10302
10448
  return null;
10303
10449
  }
10304
10450
  }
@@ -10332,31 +10478,31 @@ async function promoteUnblockedSuccessors(completedCard, deps) {
10332
10478
  const successors = links.filter((l) => l.direction === "outgoing" && !l.target_card.done);
10333
10479
  if (successors.length === 0)
10334
10480
  return;
10335
- log30.info(TAG27, `#${completedCard.short_id} completed — checking ${successors.length} chained successor(s)`);
10481
+ log31.info(TAG28, `#${completedCard.short_id} completed — checking ${successors.length} chained successor(s)`);
10336
10482
  for (const link of successors) {
10337
10483
  const successorId = link.target_card.id;
10338
10484
  try {
10339
10485
  const { card } = await deps.client.getCard(successorId);
10340
10486
  if (card.assigned_agent_id === deps.agentId) {} else if (card.assigned_agent_id === null && !card.assignee_id) {
10341
- log30.info(TAG27, `successor #${card.short_id} unassigned — auto-assigning to continue chain`);
10487
+ log31.info(TAG28, `successor #${card.short_id} unassigned — auto-assigning to continue chain`);
10342
10488
  await deps.client.updateCard(successorId, {
10343
10489
  assignedAgentId: deps.agentId
10344
10490
  });
10345
10491
  } else {
10346
- log30.debug(TAG27, `successor #${card.short_id} assigned to different entity — skipping`);
10492
+ log31.debug(TAG28, `successor #${card.short_id} assigned to different entity — skipping`);
10347
10493
  continue;
10348
10494
  }
10349
10495
  await deps.enqueue(successorId);
10350
10496
  } catch (err) {
10351
- log30.warn(TAG27, `promotion failed for successor ${successorId}: ${err instanceof Error ? err.message : err}`);
10497
+ log31.warn(TAG28, `promotion failed for successor ${successorId}: ${err instanceof Error ? err.message : err}`);
10352
10498
  }
10353
10499
  }
10354
10500
  }
10355
- var TAG27 = "unblock";
10501
+ var TAG28 = "unblock";
10356
10502
  var init_unblock = () => {};
10357
10503
 
10358
10504
  // src/cli-agent-runner.ts
10359
- import { log as log31 } from "@gethmy/harness";
10505
+ import { log as log32 } from "@gethmy/harness";
10360
10506
  function truncateOutput(value) {
10361
10507
  return value === undefined ? undefined : value.slice(0, MAX_OUTPUT_CHARS);
10362
10508
  }
@@ -10521,7 +10667,7 @@ class CliAgentRunner {
10521
10667
  return;
10522
10668
  const draft = sanitizeRunEventDraft(body);
10523
10669
  if (runEventPayloadExceedsLimit(draft.payload)) {
10524
- log31.warn(TAG28, `Dropping ${body.kind} event: payload exceeds ${MAX_RUN_EVENT_PAYLOAD_BYTES} bytes, or cannot be serialized`);
10670
+ log32.warn(TAG29, `Dropping ${body.kind} event: payload exceeds ${MAX_RUN_EVENT_PAYLOAD_BYTES} bytes, or cannot be serialized`);
10525
10671
  return;
10526
10672
  }
10527
10673
  this.buffer.push({ ...draft, createdAt: new Date().toISOString() });
@@ -10553,15 +10699,15 @@ class CliAgentRunner {
10553
10699
  this.requeue(batch);
10554
10700
  if (batch.length > 1) {
10555
10701
  this.nextBatchSize = Math.ceil(batch.length / 2);
10556
- log31.warn(TAG28, `Flush of ${batch.length} run event(s) failed ${MAX_BATCH_ATTEMPTS}×; bisecting to ${this.nextBatchSize}: ${err}`);
10702
+ log32.warn(TAG29, `Flush of ${batch.length} run event(s) failed ${MAX_BATCH_ATTEMPTS}×; bisecting to ${this.nextBatchSize}: ${err}`);
10557
10703
  return;
10558
10704
  }
10559
10705
  const dropped = this.buffer.shift();
10560
10706
  this.nextBatchSize = MAX_BUFFER;
10561
- log31.warn(TAG28, `Dropping one ${dropped?.kind} event the API refused ${MAX_BATCH_ATTEMPTS}×: ${err}`);
10707
+ log32.warn(TAG29, `Dropping one ${dropped?.kind} event the API refused ${MAX_BATCH_ATTEMPTS}×: ${err}`);
10562
10708
  return;
10563
10709
  }
10564
- log31.warn(TAG28, `Failed to flush run events (attempt ${this.batchAttempts}/${MAX_BATCH_ATTEMPTS}): ${err}`);
10710
+ log32.warn(TAG29, `Failed to flush run events (attempt ${this.batchAttempts}/${MAX_BATCH_ATTEMPTS}): ${err}`);
10565
10711
  this.requeue(batch);
10566
10712
  } finally {
10567
10713
  this.flushing = false;
@@ -10604,26 +10750,26 @@ function mapCost(cost) {
10604
10750
  durationMs: cost.durationMs
10605
10751
  };
10606
10752
  }
10607
- var TAG28 = "cli-agent-runner", FLUSH_INTERVAL_MS = 2000, MAX_BUFFER = 1000, MAX_TEXT_LEN = 8000, MAX_BATCH_ATTEMPTS = 3, FINAL_FLUSH_ATTEMPTS = 40;
10753
+ var TAG29 = "cli-agent-runner", FLUSH_INTERVAL_MS = 2000, MAX_BUFFER = 1000, MAX_TEXT_LEN = 8000, MAX_BATCH_ATTEMPTS = 3, FINAL_FLUSH_ATTEMPTS = 40;
10608
10754
  var init_cli_agent_runner = __esm(() => {
10609
10755
  init_src();
10610
10756
  });
10611
10757
 
10612
10758
  // src/fanout.ts
10613
- import { log as log32 } from "@gethmy/harness";
10759
+ import { log as log33 } from "@gethmy/harness";
10614
10760
  async function fetchLinks(client, cardId) {
10615
10761
  try {
10616
10762
  const { links } = await client.getCardLinks(cardId);
10617
10763
  return links;
10618
10764
  } catch (err) {
10619
- log32.warn(TAG29, `link fetch failed for ${cardId}: ${err instanceof Error ? err.message : err}`);
10765
+ log33.warn(TAG30, `link fetch failed for ${cardId}: ${err instanceof Error ? err.message : err}`);
10620
10766
  return null;
10621
10767
  }
10622
10768
  }
10623
10769
  async function isFanoutChildOf(card, stage, client) {
10624
10770
  const links = await fetchLinks(client, card.id);
10625
10771
  if (links === null) {
10626
- log32.warn(TAG29, `#${card.short_id}: link read failed — treating as a fan-out child (fail closed, no recursive dispatch)`);
10772
+ log33.warn(TAG30, `#${card.short_id}: link read failed — treating as a fan-out child (fail closed, no recursive dispatch)`);
10627
10773
  return true;
10628
10774
  }
10629
10775
  const parents = links.filter((l) => l.direction === "outgoing" && l.link_type === "is_part_of");
@@ -10710,7 +10856,7 @@ async function readStageHandoff(card, fromStage, deps) {
10710
10856
  }
10711
10857
  return null;
10712
10858
  } catch (err) {
10713
- log32.warn(TAG29, `handoff read failed for #${card.short_id} stage "${fromStage}": ${err instanceof Error ? err.message : err}`);
10859
+ log33.warn(TAG30, `handoff read failed for #${card.short_id} stage "${fromStage}": ${err instanceof Error ? err.message : err}`);
10714
10860
  return null;
10715
10861
  }
10716
10862
  }
@@ -10811,7 +10957,7 @@ async function dispatchWave(parent, stage, plan, items, deps) {
10811
10957
  });
10812
10958
  spawned = res.children ?? [];
10813
10959
  } catch (err) {
10814
- log32.warn(TAG29, `child spawn failed for #${parent.short_id} stage "${stage.id}": ${err instanceof Error ? err.message : err}`);
10960
+ log33.warn(TAG30, `child spawn failed for #${parent.short_id} stage "${stage.id}": ${err instanceof Error ? err.message : err}`);
10815
10961
  return 0;
10816
10962
  }
10817
10963
  let created = 0;
@@ -10866,10 +11012,10 @@ async function seedChildHandoff(parent, stage, item, total, childId, deps) {
10866
11012
  try {
10867
11013
  await deps.client.addComment(childId, body, { commentType: "decision" });
10868
11014
  } catch (err) {
10869
- log32.warn(TAG29, `seed handoff failed for child ${childId}: ${err instanceof Error ? err.message : err}`);
11015
+ log33.warn(TAG30, `seed handoff failed for child ${childId}: ${err instanceof Error ? err.message : err}`);
10870
11016
  }
10871
11017
  }
10872
- var TAG29 = "fanout";
11018
+ var TAG30 = "fanout";
10873
11019
  var init_fanout = __esm(() => {
10874
11020
  init_src();
10875
11021
  });
@@ -11146,7 +11292,7 @@ function runSizedPayload(input) {
11146
11292
  }
11147
11293
 
11148
11294
  // src/stage-advance.ts
11149
- import { gateConfigErrorReason, log as log33 } from "@gethmy/harness";
11295
+ import { gateConfigErrorReason, log as log34 } from "@gethmy/harness";
11150
11296
  function endDispositionFor(outcome) {
11151
11297
  return "endDisposition" in outcome && outcome.endDisposition ? outcome.endDisposition : { status: "completed" };
11152
11298
  }
@@ -11177,7 +11323,7 @@ async function resolveStageColumnName(client, card, stage) {
11177
11323
  const match = board.columns.find((c) => c.id === target || c.name.toLowerCase() === target.toLowerCase());
11178
11324
  return match ? match.name : null;
11179
11325
  } catch (err) {
11180
- log33.warn(TAG30, `board fetch failed resolving stage column for #${card.short_id}: ${err instanceof Error ? err.message : err}`);
11326
+ log34.warn(TAG31, `board fetch failed resolving stage column for #${card.short_id}: ${err instanceof Error ? err.message : err}`);
11181
11327
  return null;
11182
11328
  }
11183
11329
  }
@@ -11216,7 +11362,7 @@ async function holdGateMisconfigured(card, stage, detail, deps) {
11216
11362
  });
11217
11363
  } catch {}
11218
11364
  const endDisposition = await holdForHuman(deps.client, card, reason, deps.runId, deps.stateStore, { agentSessionId: deps.agentSessionId });
11219
- log33.info(TAG30, `#${card.short_id} GateMisconfigured: ${reason}`);
11365
+ log34.info(TAG31, `#${card.short_id} GateMisconfigured: ${reason}`);
11220
11366
  return { kind: "held_misconfigured", reason, endDisposition };
11221
11367
  }
11222
11368
  function firstErrorMessage(evaluation) {
@@ -11247,7 +11393,7 @@ async function advanceConvergeLoop(card, stage, stageIndex, def, evaluation, loo
11247
11393
  evidence,
11248
11394
  summary
11249
11395
  });
11250
- log33.info(TAG30, `#${card.short_id} converge loop "${stage.name}": ${summary} → ${decision}`);
11396
+ log34.info(TAG31, `#${card.short_id} converge loop "${stage.name}": ${summary} → ${decision}`);
11251
11397
  if (decision === "exit") {
11252
11398
  await deps.stateStore.resetLoopIterations(card.id).catch(() => {});
11253
11399
  deps.sink?.recordLoopCompleted?.({
@@ -11292,7 +11438,7 @@ async function advanceConvergeLoop(card, stage, stageIndex, def, evaluation, loo
11292
11438
  blockers: [reason],
11293
11439
  agentSessionId: deps.agentSessionId
11294
11440
  });
11295
- log33.info(TAG30, `#${card.short_id} LoopExhausted: ${reason}`);
11441
+ log34.info(TAG31, `#${card.short_id} LoopExhausted: ${reason}`);
11296
11442
  return { kind: "held_gate_unmet", reason, endDisposition };
11297
11443
  }
11298
11444
  const guard = await guardStageReclaim(card, `converge-loop iteration of "${stage.name}"`, deps);
@@ -11317,7 +11463,7 @@ async function advanceConvergeLoop(card, stage, stageIndex, def, evaluation, loo
11317
11463
  addLabels: [{ name: AGENT_LABEL }],
11318
11464
  ...isAgentRunnableOwner(stage.owner) ? { assignAgent: deps.agentId } : {}
11319
11465
  }, { store: deps.stateStore, runId: deps.runId });
11320
- log33.info(TAG30, `#${card.short_id} converge loop "${stage.name}" — requeued to "${toColumn}" for iteration ${iteration + 1}/${maxIterations}`);
11466
+ log34.info(TAG31, `#${card.short_id} converge loop "${stage.name}" — requeued to "${toColumn}" for iteration ${iteration + 1}/${maxIterations}`);
11321
11467
  return { kind: "requeued_gate_unmet", toColumn };
11322
11468
  }
11323
11469
  async function writeIterationHandoff(card, stage, iteration, maxIterations, evaluation, deps) {
@@ -11339,7 +11485,7 @@ ${findings.map((f) => `- [${f.level}] ${f.message}`).join(`
11339
11485
  agentSessionId: deps.agentSessionId ?? undefined
11340
11486
  });
11341
11487
  } catch (err) {
11342
- log33.warn(TAG30, `iteration-handoff write failed for #${card.short_id}: ${err instanceof Error ? err.message : err}`);
11488
+ log34.warn(TAG31, `iteration-handoff write failed for #${card.short_id}: ${err instanceof Error ? err.message : err}`);
11343
11489
  }
11344
11490
  }
11345
11491
  async function advanceStageOnGate(card, stage, stageIndex, def, evaluation, deps) {
@@ -11367,7 +11513,7 @@ async function advanceStageOnGate(card, stage, stageIndex, def, evaluation, deps
11367
11513
  reason: "Playbook complete — final stage gate passed."
11368
11514
  });
11369
11515
  deps.stateStore.recordOutcome(card.id, "success").catch(() => {});
11370
- log33.info(TAG30, `#${card.short_id} terminal stage "${stage.name}" passed — playbook complete (done left to the column)`);
11516
+ log34.info(TAG31, `#${card.short_id} terminal stage "${stage.name}" passed — playbook complete (done left to the column)`);
11371
11517
  return { kind: "completed_terminal" };
11372
11518
  }
11373
11519
  if (next.kind === "out_of_range") {
@@ -11399,7 +11545,7 @@ async function advanceStageOnGate(card, stage, stageIndex, def, evaluation, deps
11399
11545
  ...isAgentRunnableOwner(next.stage.owner) ? { assignAgent: deps.agentId } : {}
11400
11546
  }, { store: deps.stateStore, runId: deps.runId });
11401
11547
  deps.stateStore.recordOutcome(card.id, "success").catch(() => {});
11402
- log33.info(TAG30, `#${card.short_id} advanced "${stage.name}" → "${next.stage.name}" (column "${toColumn}")`);
11548
+ log34.info(TAG31, `#${card.short_id} advanced "${stage.name}" → "${next.stage.name}" (column "${toColumn}")`);
11403
11549
  if (next.stage.owner === "human") {
11404
11550
  const reason = `Stage "${next.stage.name}" is yours: ${handoffText(next.stage)}`;
11405
11551
  const endDisposition = await holdForHuman(deps.client, card, reason, deps.runId, deps.stateStore, {
@@ -11436,7 +11582,7 @@ async function handleGateUnmet(card, stage, summary, deps) {
11436
11582
  blockers: [reason],
11437
11583
  agentSessionId: deps.agentSessionId
11438
11584
  });
11439
- log33.info(TAG30, `#${card.short_id} GateUnmetExhausted: ${reason}`);
11585
+ log34.info(TAG31, `#${card.short_id} GateUnmetExhausted: ${reason}`);
11440
11586
  return { kind: "held_gate_unmet", reason, endDisposition };
11441
11587
  }
11442
11588
  const guard = await guardStageReclaim(card, `gate-unmet re-run of "${stage.name}"`, deps);
@@ -11459,7 +11605,7 @@ async function handleGateUnmet(card, stage, summary, deps) {
11459
11605
  addLabels: [{ name: AGENT_LABEL }],
11460
11606
  ...isAgentRunnableOwner(stage.owner) ? { assignAgent: deps.agentId } : {}
11461
11607
  }, { store: deps.stateStore, runId: deps.runId });
11462
- log33.info(TAG30, `#${card.short_id} gate unmet for "${stage.name}" — requeued to "${toColumn}" for re-run (attempt ${attempts}/${deps.maxAttempts})`);
11608
+ log34.info(TAG31, `#${card.short_id} gate unmet for "${stage.name}" — requeued to "${toColumn}" for re-run (attempt ${attempts}/${deps.maxAttempts})`);
11463
11609
  return { kind: "requeued_gate_unmet", toColumn };
11464
11610
  }
11465
11611
  async function guardStageReclaim(card, what, deps) {
@@ -11485,10 +11631,10 @@ async function guardStageReclaim(card, what, deps) {
11485
11631
  }, { store: deps.stateStore, runId: deps.runId });
11486
11632
  released = stillOurs;
11487
11633
  } catch (err) {
11488
- log33.warn(TAG30, `#${card.short_id} could not hand the card over after refusing the ${what}: ${err instanceof Error ? err.message : err}${stillOurs ? " — the claim is still ours, so the next reconcile tick may re-pick the card" : ""}`);
11634
+ log34.warn(TAG31, `#${card.short_id} could not hand the card over after refusing the ${what}: ${err instanceof Error ? err.message : err}${stillOurs ? " — the claim is still ours, so the next reconcile tick may re-pick the card" : ""}`);
11489
11635
  }
11490
11636
  }
11491
- log33.info(TAG30, `#${card.short_id} ${what} refused — ${detail} (${reason})${released ? "; released the daemon's claim so the stage router stops here" : ""}`);
11637
+ log34.info(TAG31, `#${card.short_id} ${what} refused — ${detail} (${reason})${released ? "; released the daemon's claim so the stage router stops here" : ""}`);
11492
11638
  try {
11493
11639
  await deps.client.addComment(card.id, `Stopped the ${what} — ${detail}. The card is not the daemon's to reclaim, so this stage will not re-run until it is assigned back.`, {
11494
11640
  commentType: "decision",
@@ -11510,7 +11656,7 @@ async function holdForHuman(client, card, reason, runId, stateStore, opts = {})
11510
11656
  try {
11511
11657
  await runTransition(client, card, { removeLabels: [AGENT_LABEL] }, { store: stateStore, runId });
11512
11658
  } catch (err) {
11513
- log33.warn(TAG30, `hold transition failed for #${card.short_id}: ${err instanceof Error ? err.message : err}`);
11659
+ log34.warn(TAG31, `hold transition failed for #${card.short_id}: ${err instanceof Error ? err.message : err}`);
11514
11660
  }
11515
11661
  return {
11516
11662
  status: opts.endStatus ?? "paused",
@@ -11519,7 +11665,7 @@ async function holdForHuman(client, card, reason, runId, stateStore, opts = {})
11519
11665
  failureSummary: reason.slice(0, 300)
11520
11666
  };
11521
11667
  }
11522
- var TAG30 = "stage-advance", AGENT_LABEL = "agent";
11668
+ var TAG31 = "stage-advance", AGENT_LABEL = "agent";
11523
11669
  var init_stage_advance = __esm(() => {
11524
11670
  init_src();
11525
11671
  init_handback();
@@ -11627,7 +11773,7 @@ import {
11627
11773
  GIT_NO_HOOKS as GIT_NO_HOOKS8,
11628
11774
  implementRunContainment,
11629
11775
  implementRunContainmentCliArgs as implementRunContainmentCliArgs2,
11630
- log as log34,
11776
+ log as log35,
11631
11777
  makeBranchName,
11632
11778
  normalizeGateSpec,
11633
11779
  pushBranch as pushBranch3,
@@ -11833,11 +11979,11 @@ class Worker {
11833
11979
  sessionId: this.sessionId
11834
11980
  });
11835
11981
  } catch (err) {
11836
- log34.warn(this.tag, `state store updateRun failed: ${err instanceof Error ? err.message : err}`);
11982
+ log35.warn(this.tag, `state store updateRun failed: ${err instanceof Error ? err.message : err}`);
11837
11983
  }
11838
11984
  }
11839
11985
  get tag() {
11840
- return `${TAG31}:${this.id}`;
11986
+ return `${TAG32}:${this.id}`;
11841
11987
  }
11842
11988
  get isIdle() {
11843
11989
  return this.state === "idle";
@@ -11899,12 +12045,12 @@ class Worker {
11899
12045
  }
11900
12046
  if (!crossedTurnCap(before, after, cap))
11901
12047
  return;
11902
- log34.warn(TAG31, `#${shortId ?? cardId} reached its per-card turn cap — ${after} of ${cap} turns. It will not be picked up again until it is reassigned or the cap is raised.`);
12048
+ log35.warn(TAG32, `#${shortId ?? cardId} reached its per-card turn cap — ${after} of ${cap} turns. It will not be picked up again until it is reassigned or the cap is raised.`);
11903
12049
  try {
11904
12050
  const legs = this.stateStore.getRun(this.runId ?? "")?.legs?.length || 1;
11905
12051
  await this.client.addComment(cardId, buildTurnCapComment(after, cap, legs), { commentType: "blocker", agentSessionId: this.sessionId ?? undefined });
11906
12052
  } catch (err) {
11907
- log34.warn(TAG31, `Could not post the turn-cap notice for ${cardId}: ${err instanceof Error ? err.message : err}`);
12053
+ log35.warn(TAG32, `Could not post the turn-cap notice for ${cardId}: ${err instanceof Error ? err.message : err}`);
11908
12054
  }
11909
12055
  }
11910
12056
  async run(card, column, labels, subtasks) {
@@ -11959,7 +12105,7 @@ class Worker {
11959
12105
  resumeReason: null
11960
12106
  });
11961
12107
  } catch (err) {
11962
- log34.warn(this.tag, `Failed to clear the consumed grant: ${err}`);
12108
+ log35.warn(this.tag, `Failed to clear the consumed grant: ${err}`);
11963
12109
  }
11964
12110
  }
11965
12111
  try {
@@ -11967,20 +12113,20 @@ class Worker {
11967
12113
  if (!resuming) {
11968
12114
  this.branchName = makeBranchName(card.short_id, card.title, this.config.worktree.failedBranchPrefix);
11969
12115
  }
11970
- log34.info(this.tag, resuming ? `Resuming #${card.short_id} "${card.title}" with ${this.grantedTurns ?? "the default"} more turns` : `Preparing #${card.short_id} "${card.title}"`);
12116
+ log35.info(this.tag, resuming ? `Resuming #${card.short_id} "${card.title}" with ${this.grantedTurns ?? "the default"} more turns` : `Preparing #${card.short_id} "${card.title}"`);
11971
12117
  const attemptCount = await this.stateStore.incrementAttempt(card.id);
11972
12118
  const isRework = attemptCount > 1;
11973
12119
  const runStarts = this.stateStore.getCardRunStarts(card.id);
11974
12120
  const runCap = this.config.budget.maxRunsPerCard;
11975
12121
  if (crossedTurnCap(runStarts - 1, runStarts, runCap)) {
11976
- log34.warn(this.tag, `#${card.short_id} reached its per-card run backstop — ${runStarts} of ${runCap} run starts. It will not be picked up again until it is reassigned or the cap is raised.`);
12122
+ log35.warn(this.tag, `#${card.short_id} reached its per-card run backstop — ${runStarts} of ${runCap} run starts. It will not be picked up again until it is reassigned or the cap is raised.`);
11977
12123
  try {
11978
12124
  await this.client.addComment(card.id, buildRunCapComment(runStarts, runCap), {
11979
12125
  commentType: "blocker",
11980
12126
  agentSessionId: this.sessionId ?? undefined
11981
12127
  });
11982
12128
  } catch (err) {
11983
- log34.warn(this.tag, `Could not post the run-backstop notice for ${card.id}: ${err instanceof Error ? err.message : err}`);
12129
+ log35.warn(this.tag, `Could not post the run-backstop notice for ${card.id}: ${err instanceof Error ? err.message : err}`);
11984
12130
  }
11985
12131
  }
11986
12132
  const recordedBranch = recordedBranchForCard(card.description, card.short_id, [
@@ -11990,10 +12136,10 @@ class Worker {
11990
12136
  const continuesPushedWork = isRework || recordedBranch !== null;
11991
12137
  if (!resuming && recordedBranch) {
11992
12138
  if (recordedBranch !== this.branchName) {
11993
- log34.info(this.tag, `Card records pushed work on ${recordedBranch} — continuing that branch instead of ${this.branchName}`);
12139
+ log35.info(this.tag, `Card records pushed work on ${recordedBranch} — continuing that branch instead of ${this.branchName}`);
11994
12140
  this.branchName = recordedBranch;
11995
12141
  } else if (!isRework) {
11996
- log34.info(this.tag, `Card records pushed work on ${this.branchName} — continuing that branch instead of rebuilding from ${this.config.worktree.baseBranch}`);
12142
+ log35.info(this.tag, `Card records pushed work on ${this.branchName} — continuing that branch instead of rebuilding from ${this.config.worktree.baseBranch}`);
11997
12143
  }
11998
12144
  }
11999
12145
  this.startHeartbeat();
@@ -12035,14 +12181,14 @@ class Worker {
12035
12181
  awaitingDecisionUntil: null
12036
12182
  });
12037
12183
  const started = await startSessionYieldingNotice(startSession, this.client, card.id, {
12038
- info: (message) => log34.info(this.tag, message),
12039
- warn: (message) => log34.warn(this.tag, message)
12184
+ info: (message) => log35.info(this.tag, message),
12185
+ warn: (message) => log35.warn(this.tag, message)
12040
12186
  });
12041
12187
  session = started.session;
12042
12188
  } catch (err) {
12043
12189
  if (isSessionConflict(err)) {
12044
12190
  this.sessionConflict = true;
12045
- log34.info(this.tag, `Skipping #${card.short_id}: ${err instanceof Error ? err.message : String(err)}`);
12191
+ log35.info(this.tag, `Skipping #${card.short_id}: ${err instanceof Error ? err.message : String(err)}`);
12046
12192
  await this.stateStore.decrementAttempt(card.id);
12047
12193
  return;
12048
12194
  }
@@ -12050,7 +12196,7 @@ class Worker {
12050
12196
  }
12051
12197
  const sid = session && typeof session === "object" && "id" in session ? session.id : null;
12052
12198
  if (!sid) {
12053
- log34.warn(TAG31, "startAgentSession returned no session id");
12199
+ log35.warn(TAG32, "startAgentSession returned no session id");
12054
12200
  }
12055
12201
  this.sessionId = sid;
12056
12202
  }
@@ -12068,7 +12214,7 @@ class Worker {
12068
12214
  if (!resuming) {
12069
12215
  const moved = await moveCardAndAddLabel(this.client, card, IN_PROGRESS_COLUMN, "agent");
12070
12216
  if (!moved) {
12071
- log34.warn(this.tag, `Card #${card.short_id} was NOT moved to "In Progress" — check API logs`);
12217
+ log35.warn(this.tag, `Card #${card.short_id} was NOT moved to "In Progress" — check API logs`);
12072
12218
  }
12073
12219
  }
12074
12220
  if (this.aborted)
@@ -12095,10 +12241,10 @@ class Worker {
12095
12241
  }).trim();
12096
12242
  const target = resolveContinuationTarget(this.branchName, continueRequested, this.config.worktree.failedBranchPrefix, this.config.worktree.approvedBranchPrefix, (ref) => fetchExistingBranch(repoRoot, ref));
12097
12243
  if (target.branchName !== this.branchName) {
12098
- log34.info(this.tag, `Branch ${this.branchName} is gone from origin but its approved rename ${target.branchName} exists — continuing that branch`);
12244
+ log35.info(this.tag, `Branch ${this.branchName} is gone from origin but its approved rename ${target.branchName} exists — continuing that branch`);
12099
12245
  this.branchName = target.branchName;
12100
12246
  } else if (target.reason === "exists_on_origin") {
12101
- log34.info(this.tag, `Branch ${this.branchName} exists on origin with no card record — continuing it rather than resetting it`);
12247
+ log35.info(this.tag, `Branch ${this.branchName} exists on origin with no card record — continuing it rather than resetting it`);
12102
12248
  }
12103
12249
  this.worktreePath = createWorktree(this.config.worktree.basePath, this.config.worktree.baseBranch, this.branchName, {
12104
12250
  continueExisting: target.continueExisting,
@@ -12132,7 +12278,7 @@ class Worker {
12132
12278
  if (this.aborted)
12133
12279
  return;
12134
12280
  if (parked) {
12135
- log34.info(this.tag, `#${card.short_id} parked for plan approval — ending run`);
12281
+ log35.info(this.tag, `#${card.short_id} parked for plan approval — ending run`);
12136
12282
  return;
12137
12283
  }
12138
12284
  }
@@ -12153,7 +12299,7 @@ class Worker {
12153
12299
  progressPercent: 10
12154
12300
  });
12155
12301
  this.timeoutTimer = setTimeout(() => {
12156
- log34.warn(this.tag, `Timeout reached (${this.config.maxTimeout}ms), cancelling`);
12302
+ log35.warn(this.tag, `Timeout reached (${this.config.maxTimeout}ms), cancelling`);
12157
12303
  this.timedOut = true;
12158
12304
  this.cancel("timeout");
12159
12305
  }, this.config.maxTimeout);
@@ -12180,7 +12326,7 @@ class Worker {
12180
12326
  }
12181
12327
  this.state = "verifying";
12182
12328
  await this.recordPhase("verifying");
12183
- log34.info(this.tag, `Claude finished for #${card.short_id}, running verification & completion`);
12329
+ log35.info(this.tag, `Claude finished for #${card.short_id}, running verification & completion`);
12184
12330
  await this.client.updateAgentProgress(card.id, {
12185
12331
  agentIdentifier: this.sessionIdentifier,
12186
12332
  agentName: AGENT_NAME,
@@ -12215,7 +12361,7 @@ class Worker {
12215
12361
  break;
12216
12362
  case "reclaim_refused":
12217
12363
  this.held = true;
12218
- log34.info(this.tag, `#${card.short_id} stage reclaim refused (${outcome.reason})${outcome.released ? " — released the daemon's claim" : ""}`);
12364
+ log35.info(this.tag, `#${card.short_id} stage reclaim refused (${outcome.reason})${outcome.released ? " — released the daemon's claim" : ""}`);
12219
12365
  break;
12220
12366
  case "advanced":
12221
12367
  case "completed_terminal":
@@ -12253,7 +12399,7 @@ class Worker {
12253
12399
  return;
12254
12400
  }
12255
12401
  this.state = "error";
12256
- log34.error(this.tag, `Error on #${card.short_id}: ${msg}`);
12402
+ log35.error(this.tag, `Error on #${card.short_id}: ${msg}`);
12257
12403
  const apiError = errClass.kind !== null;
12258
12404
  const baseError = err instanceof WorktreeBaseError;
12259
12405
  const noBudgetBurn = apiError || baseError;
@@ -12271,7 +12417,7 @@ class Worker {
12271
12417
  try {
12272
12418
  await teardownWorktree2(this.client, card.id, this.worktreePath, this.branchName ?? undefined, this.sessionId);
12273
12419
  } catch {
12274
- log34.warn(this.tag, "Failed to cleanup worktree before requeue");
12420
+ log35.warn(this.tag, "Failed to cleanup worktree before requeue");
12275
12421
  }
12276
12422
  this.worktreePath = null;
12277
12423
  }
@@ -12296,7 +12442,7 @@ class Worker {
12296
12442
  }
12297
12443
  });
12298
12444
  } catch (tErr) {
12299
- log34.error(this.tag, `error transition failed on #${card.short_id}: ${tErr instanceof TransitionError ? tErr.detail : tErr}`);
12445
+ log35.error(this.tag, `error transition failed on #${card.short_id}: ${tErr instanceof TransitionError ? tErr.detail : tErr}`);
12300
12446
  }
12301
12447
  if (this.runId) {
12302
12448
  try {
@@ -12332,7 +12478,7 @@ class Worker {
12332
12478
  try {
12333
12479
  await teardownWorktree2(this.client, card.id, this.worktreePath, this.branchName ?? undefined, this.sessionId);
12334
12480
  } catch {
12335
- log34.warn(this.tag, "Failed to cleanup worktree before requeue");
12481
+ log35.warn(this.tag, "Failed to cleanup worktree before requeue");
12336
12482
  }
12337
12483
  this.worktreePath = null;
12338
12484
  }
@@ -12355,7 +12501,7 @@ class Worker {
12355
12501
  }
12356
12502
  });
12357
12503
  } catch (tErr) {
12358
- log34.error(this.tag, `timeout transition failed on #${card.short_id}: ${tErr instanceof TransitionError ? tErr.detail : tErr}`);
12504
+ log35.error(this.tag, `timeout transition failed on #${card.short_id}: ${tErr instanceof TransitionError ? tErr.detail : tErr}`);
12359
12505
  }
12360
12506
  try {
12361
12507
  await this.stateStore.endRun(this.runId, "failed", {
@@ -12369,15 +12515,15 @@ class Worker {
12369
12515
  try {
12370
12516
  await this.client.updateCard(card.id, { assignedAgentId: null });
12371
12517
  } catch (err) {
12372
- log34.warn(this.tag, `failed to release card after stop: ${err instanceof Error ? err.message : err}`);
12518
+ log35.warn(this.tag, `failed to release card after stop: ${err instanceof Error ? err.message : err}`);
12373
12519
  }
12374
12520
  try {
12375
12521
  await runTransition(this.client, card, { removeLabels: ["agent"] });
12376
12522
  } catch (tErr) {
12377
- log34.warn(this.tag, `stop label cleanup failed on #${card.short_id}: ${tErr instanceof TransitionError ? tErr.detail : tErr}`);
12523
+ log35.warn(this.tag, `stop label cleanup failed on #${card.short_id}: ${tErr instanceof TransitionError ? tErr.detail : tErr}`);
12378
12524
  }
12379
12525
  } else {
12380
- log34.info(this.tag, `cancel arrived after completion on #${card.short_id} — keeping assignment so review picks it up (#585)`);
12526
+ log35.info(this.tag, `cancel arrived after completion on #${card.short_id} — keeping assignment so review picks it up (#585)`);
12381
12527
  }
12382
12528
  try {
12383
12529
  await this.stateStore.endRun(this.runId, "paused", {
@@ -12441,28 +12587,28 @@ class Worker {
12441
12587
  };
12442
12588
  const { pick, reason } = selectAutoPlaybook(subject, playbooks ?? []);
12443
12589
  if (!pick) {
12444
- log34.info(this.tag, `No playbook auto-bound to #${card.short_id}: ${reason}`);
12590
+ log35.info(this.tag, `No playbook auto-bound to #${card.short_id}: ${reason}`);
12445
12591
  return card;
12446
12592
  }
12447
12593
  const undeclared = undeclaredGateMetricsIn(pick, declaredMetricSet(this.config));
12448
12594
  if (undeclared.length > 0) {
12449
- log34.warn(this.tag, `Not binding "${pick.name}" to #${card.short_id}: stage ` + undeclared.map((u) => `"${u.stageName}" gates on metric "${u.metric}" (declare \`agent.playbooks.metrics.${u.metric}\`)`).join(", ") + " — this daemon cannot measure it, so the card runs the generic path instead.");
12595
+ log35.warn(this.tag, `Not binding "${pick.name}" to #${card.short_id}: stage ` + undeclared.map((u) => `"${u.stageName}" gates on metric "${u.metric}" (declare \`agent.playbooks.metrics.${u.metric}\`)`).join(", ") + " — this daemon cannot measure it, so the card runs the generic path instead.");
12450
12596
  return card;
12451
12597
  }
12452
12598
  const applyResult = await this.client.request("POST", `/cards/${card.id}/apply-playbook`, {
12453
12599
  playbookId: pick.id
12454
12600
  });
12455
- log34.info(this.tag, `Auto-bound #${card.short_id} to playbook "${pick.name}": ${reason}`);
12601
+ log35.info(this.tag, `Auto-bound #${card.short_id} to playbook "${pick.name}": ${reason}`);
12456
12602
  try {
12457
12603
  await this.client.addComment(card.id, `Bound playbook "${pick.name}" automatically — ${reason} Apply a different playbook from the card's stage rail to override, or turn the rule off in the playbook editor.`);
12458
12604
  } catch (commentErr) {
12459
- log34.warn(this.tag, `Auto-bind comment failed for #${card.short_id}: ${commentErr instanceof Error ? commentErr.message : String(commentErr)}`);
12605
+ log35.warn(this.tag, `Auto-bind comment failed for #${card.short_id}: ${commentErr instanceof Error ? commentErr.message : String(commentErr)}`);
12460
12606
  }
12461
12607
  try {
12462
12608
  const { card: fresh } = await this.client.getCard(card.id);
12463
12609
  return fresh;
12464
12610
  } catch (fetchErr) {
12465
- log34.warn(this.tag, `Auto-bind re-fetch failed for #${card.short_id} after binding playbook "${pick.name}" — using the apply-playbook response's fields for this pickup: ${fetchErr instanceof Error ? fetchErr.message : String(fetchErr)}`);
12611
+ log35.warn(this.tag, `Auto-bind re-fetch failed for #${card.short_id} after binding playbook "${pick.name}" — using the apply-playbook response's fields for this pickup: ${fetchErr instanceof Error ? fetchErr.message : String(fetchErr)}`);
12466
12612
  return {
12467
12613
  ...card,
12468
12614
  playbook_id: applyResult.card.playbook_id,
@@ -12471,7 +12617,7 @@ class Worker {
12471
12617
  };
12472
12618
  }
12473
12619
  } catch (err) {
12474
- log34.warn(this.tag, `Auto-bind playbook check failed for #${card.short_id}, continuing unbound: ${err instanceof Error ? err.message : String(err)}`);
12620
+ log35.warn(this.tag, `Auto-bind playbook check failed for #${card.short_id}, continuing unbound: ${err instanceof Error ? err.message : String(err)}`);
12475
12621
  return card;
12476
12622
  }
12477
12623
  }
@@ -12583,7 +12729,7 @@ class Worker {
12583
12729
  });
12584
12730
  } catch (err) {
12585
12731
  const detail = err instanceof Error ? err.message : String(err);
12586
- log34.warn(this.tag, `fan-out tick failed on #${card.short_id}: ${detail}`);
12732
+ log35.warn(this.tag, `fan-out tick failed on #${card.short_id}: ${detail}`);
12587
12733
  await this.holdStageCard(card, `Fan-out stage "${ctx.stage.name}" could not run: ${detail}`);
12588
12734
  return;
12589
12735
  }
@@ -12592,7 +12738,7 @@ class Worker {
12592
12738
  case "waiting": {
12593
12739
  const total = outcome.kind === "dispatched" ? outcome.total : outcome.total;
12594
12740
  const note = outcome.kind === "dispatched" ? `Fan-out stage "${ctx.stage.name}": dispatched ${outcome.created} of ${total} item(s); ${outcome.inFlight} in flight.` : `Fan-out stage "${ctx.stage.name}": ${outcome.settled} of ${total} item(s) settled; waiting on the rest.`;
12595
- log34.info(this.tag, `#${card.short_id} ${note}`);
12741
+ log35.info(this.tag, `#${card.short_id} ${note}`);
12596
12742
  await this.client.updateAgentProgress(card.id, {
12597
12743
  agentIdentifier: "claude-code-stage",
12598
12744
  agentName: "Harmony Agent",
@@ -12620,7 +12766,7 @@ class Worker {
12620
12766
  if (advance.kind === "advanced" || advance.kind === "completed_terminal") {
12621
12767
  this.held = false;
12622
12768
  }
12623
- log34.info(this.tag, `#${card.short_id} ${summary} → ${advance.kind}`);
12769
+ log35.info(this.tag, `#${card.short_id} ${summary} → ${advance.kind}`);
12624
12770
  return;
12625
12771
  }
12626
12772
  case "halted": {
@@ -12640,7 +12786,7 @@ class Worker {
12640
12786
  }
12641
12787
  }
12642
12788
  async holdStageCard(card, reason, wait = false, armCooldown = false) {
12643
- log34.info(this.tag, `Holding #${card.short_id}: ${reason}`);
12789
+ log35.info(this.tag, `Holding #${card.short_id}: ${reason}`);
12644
12790
  await this.stateStore.decrementAttempt(card.id);
12645
12791
  let announced = false;
12646
12792
  const holdStage = armCooldown ? card.current_stage : null;
@@ -12653,11 +12799,11 @@ class Worker {
12653
12799
  });
12654
12800
  announced = recorded.announced;
12655
12801
  } catch (err) {
12656
- log34.warn(this.tag, `stage hold not recorded on #${card.short_id}: ${err instanceof Error ? err.message : err}`);
12802
+ log35.warn(this.tag, `stage hold not recorded on #${card.short_id}: ${err instanceof Error ? err.message : err}`);
12657
12803
  }
12658
12804
  }
12659
12805
  if (announced) {
12660
- log34.debug(this.tag, `#${card.short_id} already carries this blocker — not reposting`);
12806
+ log35.debug(this.tag, `#${card.short_id} already carries this blocker — not reposting`);
12661
12807
  } else {
12662
12808
  try {
12663
12809
  await this.client.addComment(card.id, reason, {
@@ -12679,7 +12825,7 @@ class Worker {
12679
12825
  }
12680
12826
  });
12681
12827
  } catch (tErr) {
12682
- log34.warn(this.tag, `hold transition failed on #${card.short_id}: ${tErr instanceof TransitionError ? tErr.detail : tErr}`);
12828
+ log35.warn(this.tag, `hold transition failed on #${card.short_id}: ${tErr instanceof TransitionError ? tErr.detail : tErr}`);
12683
12829
  }
12684
12830
  if (this.runId) {
12685
12831
  try {
@@ -12697,7 +12843,7 @@ class Worker {
12697
12843
  const holderMessage = err instanceof Error ? err.message : String(err);
12698
12844
  const waitHours = this.config.budget.pause.waitHours;
12699
12845
  const until = computeDecisionDeadline(waitHours);
12700
- log34.warn(this.tag, `#${card.short_id} stays parked — the resume could not reclaim its session: ${holderMessage}`);
12846
+ log35.warn(this.tag, `#${card.short_id} stays parked — the resume could not reclaim its session: ${holderMessage}`);
12701
12847
  try {
12702
12848
  await this.client.addComment(card.id, formatResumeConflictComment({
12703
12849
  holderMessage,
@@ -12709,7 +12855,7 @@ class Worker {
12709
12855
  agentSessionId: this.sessionId ?? undefined
12710
12856
  });
12711
12857
  } catch (commentErr) {
12712
- log34.warn(this.tag, `Failed to post the resume-conflict note for #${card.short_id}: ${commentErr}`);
12858
+ log35.warn(this.tag, `Failed to post the resume-conflict note for #${card.short_id}: ${commentErr}`);
12713
12859
  }
12714
12860
  if (this.runId) {
12715
12861
  const run = this.stateStore.getRun(this.runId);
@@ -12720,7 +12866,7 @@ class Worker {
12720
12866
  awaitingDecisionUntil: until
12721
12867
  });
12722
12868
  } catch (storeErr) {
12723
- log34.error(this.tag, `#${card.short_id} could not be re-parked after a resume conflict — the daemon has no hold on it: ${storeErr}`);
12869
+ log35.error(this.tag, `#${card.short_id} could not be re-parked after a resume conflict — the daemon has no hold on it: ${storeErr}`);
12724
12870
  }
12725
12871
  }
12726
12872
  }
@@ -12732,7 +12878,7 @@ class Worker {
12732
12878
  this.progressTracker = null;
12733
12879
  const waitHours = this.config.budget.pause.waitHours;
12734
12880
  const until = computeDecisionDeadline(waitHours);
12735
- log34.warn(this.tag, `#${card.short_id} parked (${trigger}) — awaiting a human decision for ${waitHours}h`);
12881
+ log35.warn(this.tag, `#${card.short_id} parked (${trigger}) — awaiting a human decision for ${waitHours}h`);
12736
12882
  const measured = this.worktreePath && this.runExpectsCommits ? measureRunCommits(this.worktreePath, this.config.worktree.baseBranch, this.runBaselineSha) : "unknown";
12737
12883
  const hasCommits = measured === "unknown" ? undefined : measured === "committed";
12738
12884
  if (hasCommits === false && !this.noCommitNoticeSettled) {
@@ -12762,7 +12908,7 @@ class Worker {
12762
12908
  });
12763
12909
  commentId = res?.comment?.id ?? null;
12764
12910
  } catch (err) {
12765
- log34.warn(this.tag, `Failed to post the budget-pause comment: ${err}`);
12911
+ log35.warn(this.tag, `Failed to post the budget-pause comment: ${err}`);
12766
12912
  }
12767
12913
  const cardCostCents = this.stateStore.getCardCostCents(card.id) + Math.max(0, this.runCostCents - this.chargedCents);
12768
12914
  const readout = [
@@ -12783,7 +12929,7 @@ class Worker {
12783
12929
  awaitingDecisionUntil: new Date(until).toISOString()
12784
12930
  });
12785
12931
  } catch (err) {
12786
- log34.warn(this.tag, `Failed to mark the session blocked: ${err}`);
12932
+ log35.warn(this.tag, `Failed to mark the session blocked: ${err}`);
12787
12933
  }
12788
12934
  if (this.runId) {
12789
12935
  try {
@@ -12794,7 +12940,7 @@ class Worker {
12794
12940
  ...this.runLedger()
12795
12941
  });
12796
12942
  } catch (err) {
12797
- log34.error(this.tag, `#${card.short_id} parkRun failed after the blocker comment + blocked session were already written — the daemon has no local record of this park: ${err}`);
12943
+ log35.error(this.tag, `#${card.short_id} parkRun failed after the blocker comment + blocked session were already written — the daemon has no local record of this park: ${err}`);
12798
12944
  }
12799
12945
  }
12800
12946
  }
@@ -12825,11 +12971,11 @@ class Worker {
12825
12971
  });
12826
12972
  const gate = normalizeGateSpec(ctx.stage.gate);
12827
12973
  const metricsPath = gate?.kind === "custom" ? writeMetricsFile(this.config.playbooks.metrics ?? {}) : null;
12828
- log34.info(this.tag, `Running stage "${ctx.stage.name}" (role ${ctx.role}) for #${card.short_id} under the harness motor`);
12974
+ log35.info(this.tag, `Running stage "${ctx.stage.name}" (role ${ctx.role}) for #${card.short_id} under the harness motor`);
12829
12975
  const motorAbort = new AbortController;
12830
12976
  this.motorAbort = motorAbort;
12831
12977
  this.timeoutTimer = setTimeout(() => {
12832
- log34.warn(this.tag, `Timeout reached (${this.config.maxTimeout}ms) during the motor stage run, cancelling`);
12978
+ log35.warn(this.tag, `Timeout reached (${this.config.maxTimeout}ms) during the motor stage run, cancelling`);
12833
12979
  this.timedOut = true;
12834
12980
  this.cancel("timeout");
12835
12981
  }, this.config.maxTimeout);
@@ -12840,12 +12986,12 @@ class Worker {
12840
12986
  let motorRunSettled = false;
12841
12987
  const onMotorLine = (line) => {
12842
12988
  if (line.type !== "agent_event") {
12843
- log34.info(this.tag, `motor: ${line.type}`);
12989
+ log35.info(this.tag, `motor: ${line.type}`);
12844
12990
  return;
12845
12991
  }
12846
12992
  if (motorRunSettled)
12847
12993
  return;
12848
- log34.debug(this.tag, `motor: agent_event ${line.event.kind}`);
12994
+ log35.debug(this.tag, `motor: agent_event ${line.event.kind}`);
12849
12995
  if (line.event.kind === "tool_started" && STAGE_DAEMON_OWNED_TOOLS.includes(line.event.payload.toolName)) {
12850
12996
  return;
12851
12997
  }
@@ -12865,7 +13011,7 @@ class Worker {
12865
13011
  currentTask: motorTask,
12866
13012
  progressPercent: MOTOR_RUN_PROGRESS_PERCENT
12867
13013
  }).catch((err) => {
12868
- log34.warn(this.tag, `motor session heartbeat failed for #${card.short_id}: ${err instanceof Error ? err.message : err}`);
13014
+ log35.warn(this.tag, `motor session heartbeat failed for #${card.short_id}: ${err instanceof Error ? err.message : err}`);
12869
13015
  });
12870
13016
  }, MOTOR_SESSION_HEARTBEAT_MS);
12871
13017
  heartbeat.unref?.();
@@ -12939,7 +13085,7 @@ class Worker {
12939
13085
  break;
12940
13086
  case "reclaim_refused":
12941
13087
  this.held = true;
12942
- log34.info(this.tag, `#${card.short_id} stage reclaim refused (${outcome.reason})${outcome.released ? " — released the daemon's claim" : ""}`);
13088
+ log35.info(this.tag, `#${card.short_id} stage reclaim refused (${outcome.reason})${outcome.released ? " — released the daemon's claim" : ""}`);
12943
13089
  break;
12944
13090
  case "advanced":
12945
13091
  case "completed_terminal":
@@ -12963,7 +13109,7 @@ class Worker {
12963
13109
  pushBranch3(this.branchName, worktreePath);
12964
13110
  pushed = true;
12965
13111
  } catch (err) {
12966
- log34.error(this.tag, `push after the motor stage "${ctx.stage.name}" failed for ${this.branchName}: ${err instanceof Error ? err.message : err}`);
13112
+ log35.error(this.tag, `push after the motor stage "${ctx.stage.name}" failed for ${this.branchName}: ${err instanceof Error ? err.message : err}`);
12967
13113
  }
12968
13114
  if (pushed) {
12969
13115
  await recordBranchProvenance(this.client, card, this.branchName);
@@ -12975,7 +13121,7 @@ class Worker {
12975
13121
  if (completionColumn) {
12976
13122
  await transferCardToCompletion({ client: this.client, tag: this.tag }, card, completionColumn, this.onCardCompleted);
12977
13123
  } else {
12978
- log34.warn(this.tag, `completion.moveToColumn is empty — #${card.short_id} stays in its current column after the motor stage run`);
13124
+ log35.warn(this.tag, `completion.moveToColumn is empty — #${card.short_id} stays in its current column after the motor stage run`);
12979
13125
  }
12980
13126
  await this.closeoutMotorWorktree(card);
12981
13127
  }
@@ -12986,7 +13132,7 @@ class Worker {
12986
13132
  try {
12987
13133
  await teardownWorktree2(this.client, card.id, worktreePath, this.branchName ?? undefined, this.sessionId);
12988
13134
  } catch {
12989
- log34.warn(this.tag, "Failed to cleanup worktree after the motor stage run");
13135
+ log35.warn(this.tag, "Failed to cleanup worktree after the motor stage run");
12990
13136
  }
12991
13137
  this.worktreePath = null;
12992
13138
  }
@@ -13046,7 +13192,7 @@ ${prompt}`;
13046
13192
  });
13047
13193
  return handoff ? renderInheritedHandoffSection(handoff) : "";
13048
13194
  } catch (err) {
13049
- log34.warn(this.tag, `inherited-handoff fetch failed for ${cardId}: ${err instanceof Error ? err.message : err}`);
13195
+ log35.warn(this.tag, `inherited-handoff fetch failed for ${cardId}: ${err instanceof Error ? err.message : err}`);
13050
13196
  return "";
13051
13197
  }
13052
13198
  }
@@ -13066,9 +13212,9 @@ ${prompt}`;
13066
13212
  commentType: "decision",
13067
13213
  agentSessionId: this.sessionId ?? undefined
13068
13214
  });
13069
- log34.info(this.tag, `Wrote stage handoff for #${card.short_id} (stage "${stage.name}")`);
13215
+ log35.info(this.tag, `Wrote stage handoff for #${card.short_id} (stage "${stage.name}")`);
13070
13216
  } catch (err) {
13071
- log34.warn(this.tag, `stage-handoff write failed for #${card.short_id}: ${err instanceof Error ? err.message : err}`);
13217
+ log35.warn(this.tag, `stage-handoff write failed for #${card.short_id}: ${err instanceof Error ? err.message : err}`);
13072
13218
  }
13073
13219
  }
13074
13220
  async collectStageGateEvidence(card, stage, worktreePath, subtasks) {
@@ -13080,12 +13226,12 @@ ${prompt}`;
13080
13226
  return null;
13081
13227
  }
13082
13228
  if (gate.pendingEngine === true) {
13083
- log34.info(this.tag, `Stage "${stage.name}" gate "${gate.kind}" is advisory — skipping enforcement`);
13229
+ log35.info(this.tag, `Stage "${stage.name}" gate "${gate.kind}" is advisory — skipping enforcement`);
13084
13230
  return null;
13085
13231
  }
13086
13232
  const review = gate.kind === "review_passed" ? parseReviewOutput(this.lastRunText) : undefined;
13087
13233
  if (review) {
13088
- log34.info(this.tag, `Review-gated stage "${stage.name}" verdict: ${review.verdict} (${review.findings.length} finding(s))`);
13234
+ log35.info(this.tag, `Review-gated stage "${stage.name}" verdict: ${review.verdict} (${review.findings.length} finding(s))`);
13089
13235
  }
13090
13236
  const registry = buildGateCollectorRegistry2({
13091
13237
  build: {
@@ -13102,7 +13248,7 @@ ${prompt}`;
13102
13248
  return [];
13103
13249
  const criteria = await fetchAcceptanceCriteria(this.client, this.tag, card);
13104
13250
  if (criteria.length > 0) {
13105
- log34.info(this.tag, `Grading the artifact gate for #${card.short_id} against ${criteria.length} plan criteria instead of the stage's own rubric`);
13251
+ log35.info(this.tag, `Grading the artifact gate for #${card.short_id} against ${criteria.length} plan criteria instead of the stage's own rubric`);
13106
13252
  }
13107
13253
  return criteria;
13108
13254
  })()
@@ -13124,10 +13270,10 @@ ${prompt}`;
13124
13270
  const evaluation = gateEvaluate(gate, evidence);
13125
13271
  const insert = toStageGateEvidenceInsert(context, evidence);
13126
13272
  await this.client.request("POST", `/cards/${encodeURIComponent(card.id)}/stage-gate-evidence`, insert);
13127
- log34.info(this.tag, `Recorded ${gate.kind} gate evidence for #${card.short_id} stage "${stage.name}": result=${evidence.result} passed=${evaluation.passed}`);
13273
+ log35.info(this.tag, `Recorded ${gate.kind} gate evidence for #${card.short_id} stage "${stage.name}": result=${evidence.result} passed=${evaluation.passed}`);
13128
13274
  return evaluation;
13129
13275
  } catch (err) {
13130
- log34.warn(this.tag, `stage-gate evidence collection failed for #${card.short_id}: ${err instanceof Error ? err.message : err}`);
13276
+ log35.warn(this.tag, `stage-gate evidence collection failed for #${card.short_id}: ${err instanceof Error ? err.message : err}`);
13131
13277
  return null;
13132
13278
  }
13133
13279
  }
@@ -13145,7 +13291,7 @@ ${prompt}`;
13145
13291
  agentSessionId: this.sessionId
13146
13292
  });
13147
13293
  } catch (err) {
13148
- log34.warn(this.tag, `stage advancement failed for #${card.short_id}: ${err instanceof Error ? err.message : err}`);
13294
+ log35.warn(this.tag, `stage advancement failed for #${card.short_id}: ${err instanceof Error ? err.message : err}`);
13149
13295
  return { kind: "no_advance" };
13150
13296
  }
13151
13297
  }
@@ -13171,7 +13317,7 @@ ${prompt}`;
13171
13317
  this.modelChoice = choice;
13172
13318
  const { model, escalated, source } = choice;
13173
13319
  if (source !== "policy" || escalated) {
13174
- log34.info(this.tag, `Implement model "${model}" (source=${source}, escalated=${escalated}, attempts=${attempts}, priority=${card.priority ?? "none"}, tier=${this.sizing?.tier ?? "none"})`);
13320
+ log35.info(this.tag, `Implement model "${model}" (source=${source}, escalated=${escalated}, attempts=${attempts}, priority=${card.priority ?? "none"}, tier=${this.sizing?.tier ?? "none"})`);
13175
13321
  }
13176
13322
  return model;
13177
13323
  }
@@ -13190,7 +13336,7 @@ ${prompt}`;
13190
13336
  encoding: "utf-8"
13191
13337
  }).trim();
13192
13338
  } catch (err) {
13193
- log34.warn(this.tag, `Sizing #${card.short_id} failed (spawn): could not resolve the repo root (${err instanceof Error ? err.message : String(err)}) — using the policy fallback`);
13339
+ log35.warn(this.tag, `Sizing #${card.short_id} failed (spawn): could not resolve the repo root (${err instanceof Error ? err.message : String(err)}) — using the policy fallback`);
13194
13340
  return { status: "failed", reason: "spawn" };
13195
13341
  }
13196
13342
  const outcome = await sizeRun({
@@ -13204,9 +13350,9 @@ ${prompt}`;
13204
13350
  scoreBands: claude.scoreBands
13205
13351
  });
13206
13352
  if (outcome.status === "sized") {
13207
- log34.info(this.tag, `Sized #${card.short_id}: complexity ${outcome.sizing.complexity}/10 -> ${outcome.sizing.tier}`);
13353
+ log35.info(this.tag, `Sized #${card.short_id}: complexity ${outcome.sizing.complexity}/10 -> ${outcome.sizing.tier}`);
13208
13354
  } else if (outcome.status === "failed") {
13209
- log34.warn(this.tag, `Sizing #${card.short_id} failed (${outcome.reason}) — using the policy fallback`);
13355
+ log35.warn(this.tag, `Sizing #${card.short_id} failed (${outcome.reason}) — using the policy fallback`);
13210
13356
  }
13211
13357
  return outcome;
13212
13358
  }
@@ -13222,14 +13368,14 @@ ${prompt}`;
13222
13368
  ]);
13223
13369
  const outcome = decideJevSizing(response);
13224
13370
  if (outcome.status === "failed") {
13225
- log34.warn(this.tag, `Sizing #${card.short_id} failed (${outcome.reason}): the sizing route answered with no usable tier — using the policy fallback`);
13371
+ log35.warn(this.tag, `Sizing #${card.short_id} failed (${outcome.reason}): the sizing route answered with no usable tier — using the policy fallback`);
13226
13372
  return outcome;
13227
13373
  }
13228
- log34.info(this.tag, `Sized #${card.short_id} with ${describeJevSizing(response, outcome.sizing.tier)}`);
13374
+ log35.info(this.tag, `Sized #${card.short_id} with ${describeJevSizing(response, outcome.sizing.tier)}`);
13229
13375
  return outcome;
13230
13376
  } catch (err) {
13231
13377
  const reason = err instanceof JevSizingDeadline ? "timeout" : "spawn";
13232
- log34.warn(this.tag, `Sizing #${card.short_id} failed (${reason}): ${err instanceof Error ? err.message : String(err)} — using the policy fallback`);
13378
+ log35.warn(this.tag, `Sizing #${card.short_id} failed (${reason}): ${err instanceof Error ? err.message : String(err)} — using the policy fallback`);
13233
13379
  return { status: "failed", reason };
13234
13380
  } finally {
13235
13381
  if (deadline)
@@ -13270,7 +13416,7 @@ ${prompt}`;
13270
13416
  const measured = measureRunCommits(this.worktreePath, this.config.worktree.baseBranch, this.runBaselineSha);
13271
13417
  if (measured !== "none") {
13272
13418
  if (measured === "unknown") {
13273
- log34.debug(this.tag, `no-commit check could not attribute this run's commits — staying quiet on ${this.branchName ?? "this branch"}`);
13419
+ log35.debug(this.tag, `no-commit check could not attribute this run's commits — staying quiet on ${this.branchName ?? "this branch"}`);
13274
13420
  }
13275
13421
  return;
13276
13422
  }
@@ -13279,7 +13425,7 @@ ${prompt}`;
13279
13425
  recordNoCommitsYet(trigger, numTurns) {
13280
13426
  const turnBudget = this.effectiveMaxTurns;
13281
13427
  const threshold = noCommitNoticeThreshold(turnBudget);
13282
- log34.warn(this.tag, `No commits yet on ${this.branchName ?? "this run's branch"} — ${this.runToolCalls} tool calls into a ${turnBudget}-turn budget (${trigger})`);
13428
+ log35.warn(this.tag, `No commits yet on ${this.branchName ?? "this run's branch"} — ${this.runToolCalls} tool calls into a ${turnBudget}-turn budget (${trigger})`);
13283
13429
  this.cliRunner?.record({
13284
13430
  kind: "no_commits_yet",
13285
13431
  source: "system",
@@ -13333,9 +13479,9 @@ ${prompt}`;
13333
13479
  agentSessionId: this.sessionId ?? undefined
13334
13480
  });
13335
13481
  giveUpCommentId = res?.comment?.id ?? null;
13336
- log34.warn(this.tag, `gave up on ${cardId} after ${attempts} attempts`);
13482
+ log35.warn(this.tag, `gave up on ${cardId} after ${attempts} attempts`);
13337
13483
  } catch (err) {
13338
- log34.warn(this.tag, `failed to post give-up comment for ${cardId}: ${err instanceof Error ? err.message : err}`);
13484
+ log35.warn(this.tag, `failed to post give-up comment for ${cardId}: ${err instanceof Error ? err.message : err}`);
13339
13485
  }
13340
13486
  if (this.config.budget.pause.enabled) {
13341
13487
  const waitHours = this.config.budget.pause.waitHours;
@@ -13349,7 +13495,7 @@ ${prompt}`;
13349
13495
  awaitingDecisionUntil: new Date(until).toISOString()
13350
13496
  });
13351
13497
  } catch (err) {
13352
- log34.warn(this.tag, `Failed to mark the attempt cap pending for ${cardId}: ${err}`);
13498
+ log35.warn(this.tag, `Failed to mark the attempt cap pending for ${cardId}: ${err}`);
13353
13499
  }
13354
13500
  try {
13355
13501
  await this.stateStore.markAwaitingDecision(cardId, {
@@ -13358,19 +13504,19 @@ ${prompt}`;
13358
13504
  agentIdentifier: this.sessionIdentifier
13359
13505
  });
13360
13506
  } catch (err) {
13361
- log34.warn(this.tag, `Failed to record the attempt-cap decision locally for ${cardId}: ${err}`);
13507
+ log35.warn(this.tag, `Failed to record the attempt-cap decision locally for ${cardId}: ${err}`);
13362
13508
  }
13363
13509
  }
13364
13510
  }
13365
13511
  }
13366
13512
  } catch (err) {
13367
- log34.warn(this.tag, `recordOutcome(${outcome}) failed: ${err instanceof Error ? err.message : err}`);
13513
+ log35.warn(this.tag, `recordOutcome(${outcome}) failed: ${err instanceof Error ? err.message : err}`);
13368
13514
  }
13369
13515
  }
13370
13516
  async pause() {
13371
13517
  if (!this.isActive || !this.process || this.process.killed)
13372
13518
  return;
13373
- log34.info(this.tag, `Pausing work on ${this.cardId}`);
13519
+ log35.info(this.tag, `Pausing work on ${this.cardId}`);
13374
13520
  signalGroup2(this.process, "SIGSTOP");
13375
13521
  if (this.timeoutTimer) {
13376
13522
  clearTimeout(this.timeoutTimer);
@@ -13384,17 +13530,17 @@ ${prompt}`;
13384
13530
  status: "paused"
13385
13531
  });
13386
13532
  } catch {
13387
- log34.warn(this.tag, "Failed to update agent session to paused");
13533
+ log35.warn(this.tag, "Failed to update agent session to paused");
13388
13534
  }
13389
13535
  }
13390
13536
  }
13391
13537
  async resume() {
13392
13538
  if (!this.isActive || !this.process || this.process.killed)
13393
13539
  return;
13394
- log34.info(this.tag, `Resuming work on ${this.cardId}`);
13540
+ log35.info(this.tag, `Resuming work on ${this.cardId}`);
13395
13541
  signalGroup2(this.process, "SIGCONT");
13396
13542
  this.timeoutTimer = setTimeout(() => {
13397
- log34.warn(this.tag, `Timeout reached (${this.config.maxTimeout}ms), cancelling`);
13543
+ log35.warn(this.tag, `Timeout reached (${this.config.maxTimeout}ms), cancelling`);
13398
13544
  this.timedOut = true;
13399
13545
  this.cancel("timeout");
13400
13546
  }, this.config.maxTimeout);
@@ -13406,7 +13552,7 @@ ${prompt}`;
13406
13552
  status: "working"
13407
13553
  });
13408
13554
  } catch {
13409
- log34.warn(this.tag, "Failed to update agent session to working");
13555
+ log35.warn(this.tag, "Failed to update agent session to working");
13410
13556
  }
13411
13557
  }
13412
13558
  }
@@ -13415,7 +13561,7 @@ ${prompt}`;
13415
13561
  return;
13416
13562
  this.aborted = true;
13417
13563
  this.state = "cancelling";
13418
- log34.info(this.tag, `Cancelling work on ${this.cardId}`);
13564
+ log35.info(this.tag, `Cancelling work on ${this.cardId}`);
13419
13565
  this.motorAbort?.abort();
13420
13566
  if (this.sdkRunner) {
13421
13567
  await this.sdkRunner.stop(this.timedOut ? "timeout" : "user_requested");
@@ -13433,14 +13579,14 @@ ${prompt}`;
13433
13579
  ...buildTokenPayload(stats)
13434
13580
  });
13435
13581
  } catch (err) {
13436
- log34.warn(this.tag, `endAgentSession after cancel failed: ${err instanceof Error ? err.message : err}`);
13582
+ log35.warn(this.tag, `endAgentSession after cancel failed: ${err instanceof Error ? err.message : err}`);
13437
13583
  }
13438
13584
  }
13439
13585
  }
13440
13586
  async runPlanningPhase(enriched) {
13441
13587
  const planning = this.config.planning;
13442
13588
  const { card } = enriched;
13443
- log34.info(this.tag, `Planning pass for #${card.short_id} (mode=${planning.mode}, model=${planning.model})`);
13589
+ log35.info(this.tag, `Planning pass for #${card.short_id} (mode=${planning.mode}, model=${planning.model})`);
13444
13590
  await this.client.updateAgentProgress(card.id, {
13445
13591
  agentIdentifier: this.sessionIdentifier,
13446
13592
  agentName: AGENT_NAME,
@@ -13453,7 +13599,7 @@ ${prompt}`;
13453
13599
  let planTimedOut = false;
13454
13600
  const planTimeout = setTimeout(() => {
13455
13601
  planTimedOut = true;
13456
- log34.warn(this.tag, "Planning pass exceeded timeout — abandoning, implementing directly");
13602
+ log35.warn(this.tag, "Planning pass exceeded timeout — abandoning, implementing directly");
13457
13603
  if (this.sdkRunner) {
13458
13604
  this.sdkRunner.stop("timeout").catch(() => {});
13459
13605
  } else if (this.process && !this.process.killed) {
@@ -13471,7 +13617,7 @@ ${prompt}`;
13471
13617
  initialPhase: "planning"
13472
13618
  });
13473
13619
  } catch (err) {
13474
- log34.warn(this.tag, `Planning pass failed (non-fatal): ${err instanceof Error ? err.message : err}`);
13620
+ log35.warn(this.tag, `Planning pass failed (non-fatal): ${err instanceof Error ? err.message : err}`);
13475
13621
  return false;
13476
13622
  } finally {
13477
13623
  clearTimeout(planTimeout);
@@ -13482,7 +13628,7 @@ ${prompt}`;
13482
13628
  await this.chargeDailyLedger(card.id, card.short_id);
13483
13629
  const planText = stats?.lastAssistantText ?? "";
13484
13630
  if (!planText.trim()) {
13485
- log34.warn(this.tag, `Planning pass for #${card.short_id} produced no text — implementing directly`);
13631
+ log35.warn(this.tag, `Planning pass for #${card.short_id} produced no text — implementing directly`);
13486
13632
  return false;
13487
13633
  }
13488
13634
  const artifact = extractPlanArtifact(planText, card.title);
@@ -13503,9 +13649,9 @@ ${prompt}`;
13503
13649
  });
13504
13650
  planId = createdId;
13505
13651
  }
13506
- log34.info(this.tag, `Stored plan ${planId ?? "(unlinked)"} for #${card.short_id} (${artifact.tasks.length} tasks)`);
13652
+ log35.info(this.tag, `Stored plan ${planId ?? "(unlinked)"} for #${card.short_id} (${artifact.tasks.length} tasks)`);
13507
13653
  } catch (err) {
13508
- log34.warn(this.tag, `Failed to store/link plan (non-fatal): ${err instanceof Error ? err.message : err}`);
13654
+ log35.warn(this.tag, `Failed to store/link plan (non-fatal): ${err instanceof Error ? err.message : err}`);
13509
13655
  }
13510
13656
  if (planning.mode === "gated" && planId) {
13511
13657
  try {
@@ -13524,11 +13670,11 @@ ${prompt}`;
13524
13670
  ...buildTokenPayload(stats)
13525
13671
  }
13526
13672
  }, { store: this.stateStore, runId: this.runId ?? undefined });
13527
- log34.info(this.tag, `#${card.short_id} parked in "${planning.awaitingApprovalColumn}" for plan approval`);
13673
+ log35.info(this.tag, `#${card.short_id} parked in "${planning.awaitingApprovalColumn}" for plan approval`);
13528
13674
  this.lastSessionStats = undefined;
13529
13675
  return true;
13530
13676
  } catch (err) {
13531
- log34.warn(this.tag, `Gated park failed for #${card.short_id} (non-fatal, implementing directly): ${err instanceof TransitionError ? err.detail : err instanceof Error ? err.message : err}`);
13677
+ log35.warn(this.tag, `Gated park failed for #${card.short_id} (non-fatal, implementing directly): ${err instanceof TransitionError ? err.detail : err instanceof Error ? err.message : err}`);
13532
13678
  }
13533
13679
  }
13534
13680
  if (planId && planning.postComment) {
@@ -13538,7 +13684,7 @@ ${prompt}`;
13538
13684
  agentSessionId: this.sessionId ?? undefined
13539
13685
  });
13540
13686
  } catch (err) {
13541
- log34.warn(this.tag, `Failed to post advisory plan comment (non-fatal): ${err instanceof Error ? err.message : err}`);
13687
+ log35.warn(this.tag, `Failed to post advisory plan comment (non-fatal): ${err instanceof Error ? err.message : err}`);
13542
13688
  }
13543
13689
  }
13544
13690
  return false;
@@ -13548,10 +13694,10 @@ ${prompt}`;
13548
13694
  const { card } = enriched;
13549
13695
  const existing = await this.loadPinnedContract(card.id);
13550
13696
  if (existing) {
13551
- log34.info(this.tag, `Contract already pinned for #${card.short_id} (${existing.assertions.length} assertions) — reusing`);
13697
+ log35.info(this.tag, `Contract already pinned for #${card.short_id} (${existing.assertions.length} assertions) — reusing`);
13552
13698
  return;
13553
13699
  }
13554
- log34.info(this.tag, `Contract pass for #${card.short_id} (model=${contractCfg.model})`);
13700
+ log35.info(this.tag, `Contract pass for #${card.short_id} (model=${contractCfg.model})`);
13555
13701
  await this.client.updateAgentProgress(card.id, {
13556
13702
  agentIdentifier: this.sessionIdentifier,
13557
13703
  agentName: AGENT_NAME,
@@ -13564,7 +13710,7 @@ ${prompt}`;
13564
13710
  let contractTimedOut = false;
13565
13711
  const contractTimeout = setTimeout(() => {
13566
13712
  contractTimedOut = true;
13567
- log34.warn(this.tag, "Contract pass exceeded timeout — abandoning, implementing directly");
13713
+ log35.warn(this.tag, "Contract pass exceeded timeout — abandoning, implementing directly");
13568
13714
  if (this.sdkRunner) {
13569
13715
  this.sdkRunner.stop("timeout").catch(() => {});
13570
13716
  } else if (this.process && !this.process.killed) {
@@ -13582,7 +13728,7 @@ ${prompt}`;
13582
13728
  initialPhase: "planning"
13583
13729
  });
13584
13730
  } catch (err) {
13585
- log34.warn(this.tag, `Contract pass failed (non-fatal): ${err instanceof Error ? err.message : err}`);
13731
+ log35.warn(this.tag, `Contract pass failed (non-fatal): ${err instanceof Error ? err.message : err}`);
13586
13732
  return;
13587
13733
  } finally {
13588
13734
  clearTimeout(contractTimeout);
@@ -13593,12 +13739,12 @@ ${prompt}`;
13593
13739
  await this.chargeDailyLedger(card.id, card.short_id);
13594
13740
  const contractText = stats?.lastAssistantText ?? "";
13595
13741
  if (!contractText.trim()) {
13596
- log34.warn(this.tag, `Contract pass for #${card.short_id} produced no text — implementing directly`);
13742
+ log35.warn(this.tag, `Contract pass for #${card.short_id} produced no text — implementing directly`);
13597
13743
  return;
13598
13744
  }
13599
13745
  const contract = extractContract(contractText, card);
13600
13746
  if (contract.assertions.length < contractCfg.minAssertions) {
13601
- log34.warn(this.tag, `Contract for #${card.short_id} had ${contract.assertions.length} assertion(s) (< ${contractCfg.minAssertions}) — not pinning, implementing directly`);
13747
+ log35.warn(this.tag, `Contract for #${card.short_id} had ${contract.assertions.length} assertion(s) (< ${contractCfg.minAssertions}) — not pinning, implementing directly`);
13602
13748
  return;
13603
13749
  }
13604
13750
  try {
@@ -13606,9 +13752,9 @@ ${prompt}`;
13606
13752
  commentType: "decision",
13607
13753
  agentSessionId: this.sessionId ?? undefined
13608
13754
  });
13609
- log34.info(this.tag, `Pinned acceptance contract for #${card.short_id} (${contract.assertions.length} assertions)`);
13755
+ log35.info(this.tag, `Pinned acceptance contract for #${card.short_id} (${contract.assertions.length} assertions)`);
13610
13756
  } catch (err) {
13611
- log34.warn(this.tag, `Failed to pin contract (non-fatal): ${err instanceof Error ? err.message : err}`);
13757
+ log35.warn(this.tag, `Failed to pin contract (non-fatal): ${err instanceof Error ? err.message : err}`);
13612
13758
  }
13613
13759
  }
13614
13760
  async loadPinnedContract(cardId) {
@@ -13618,7 +13764,7 @@ ${prompt}`;
13618
13764
  return null;
13619
13765
  return extractPinnedContract(comments, this.identity);
13620
13766
  } catch (err) {
13621
- log34.warn(this.tag, `pinned-contract fetch failed for ${cardId}: ${err instanceof Error ? err.message : err}`);
13767
+ log35.warn(this.tag, `pinned-contract fetch failed for ${cardId}: ${err instanceof Error ? err.message : err}`);
13622
13768
  return null;
13623
13769
  }
13624
13770
  }
@@ -13631,13 +13777,13 @@ ${prompt}`;
13631
13777
  const res = await this.client.getPendingUserMessages(this.cardId, this.sessionId, this.lastDrainedSeq);
13632
13778
  messages = res.messages ?? [];
13633
13779
  } catch (err) {
13634
- log34.warn(this.tag, `Failed to fetch steering messages (non-fatal): ${err instanceof Error ? err.message : err}`);
13780
+ log35.warn(this.tag, `Failed to fetch steering messages (non-fatal): ${err instanceof Error ? err.message : err}`);
13635
13781
  return;
13636
13782
  }
13637
13783
  if (messages.length === 0)
13638
13784
  return;
13639
13785
  this.lastDrainedSeq = Math.max(this.lastDrainedSeq, ...messages.map((m) => m.seq));
13640
- log34.info(this.tag, `Steering #${card.short_id}: resuming with ${messages.length} queued message(s)`);
13786
+ log35.info(this.tag, `Steering #${card.short_id}: resuming with ${messages.length} queued message(s)`);
13641
13787
  this.state = "running";
13642
13788
  await this.recordPhase("running");
13643
13789
  try {
@@ -13649,7 +13795,7 @@ ${prompt}`;
13649
13795
  ...this.activeRunSpawnOpts ?? {}
13650
13796
  });
13651
13797
  } catch (err) {
13652
- log34.warn(this.tag, `Steering resume failed (non-fatal): ${err instanceof Error ? err.message : err}`);
13798
+ log35.warn(this.tag, `Steering resume failed (non-fatal): ${err instanceof Error ? err.message : err}`);
13653
13799
  return;
13654
13800
  }
13655
13801
  }
@@ -13684,10 +13830,10 @@ ${prompt}`;
13684
13830
  "--",
13685
13831
  prompt
13686
13832
  ];
13687
- log34.info(this.tag, `Spawning: claude ${args.slice(0, 4).join(" ")} ...`);
13833
+ log35.info(this.tag, `Spawning: claude ${args.slice(0, 4).join(" ")} ...`);
13688
13834
  const runLog = openRunLog(this.tag, this.runId, card.short_id);
13689
13835
  if (runLog) {
13690
- log34.info(this.tag, `Run log: ${runLog.path}`);
13836
+ log35.info(this.tag, `Run log: ${runLog.path}`);
13691
13837
  runLog.stream.write(`# run=${this.runId} card=#${card.short_id} started=${new Date().toISOString()}
13692
13838
  ` + `# args: ${args.slice(0, -2).join(" ")} -- <prompt:${prompt.length} chars>
13693
13839
 
@@ -13724,7 +13870,7 @@ ${prompt}`;
13724
13870
  });
13725
13871
  }
13726
13872
  parser.on("parse_error", (msg) => {
13727
- log34.debug(this.tag, `Stream parse error (non-fatal): ${msg}`);
13873
+ log35.debug(this.tag, `Stream parse error (non-fatal): ${msg}`);
13728
13874
  runLog?.stream.write(`
13729
13875
  [parse_error] ${msg}
13730
13876
  `);
@@ -13797,10 +13943,10 @@ ${prompt}`;
13797
13943
  const disallowedTools = opts.disallowedTools ? opts.disallowedTools.split(",").map((t) => t.trim()).filter(Boolean) : undefined;
13798
13944
  const initialPhase = opts.initialPhase ?? "exploring";
13799
13945
  const maxBudgetUsd = this.effectiveMaxBudgetUsd;
13800
- log34.info(this.tag, `Spawning Agent SDK runner (model=${model}, maxBudgetUsd=${maxBudgetUsd || "off"}, maxTurns=${maxTurns}${opts.resumeSessionId ? ", resume" : ""})`);
13946
+ log35.info(this.tag, `Spawning Agent SDK runner (model=${model}, maxBudgetUsd=${maxBudgetUsd || "off"}, maxTurns=${maxTurns}${opts.resumeSessionId ? ", resume" : ""})`);
13801
13947
  const runLog = openRunLog(this.tag, this.runId, card.short_id);
13802
13948
  if (runLog) {
13803
- log34.info(this.tag, `Run log: ${runLog.path}`);
13949
+ log35.info(this.tag, `Run log: ${runLog.path}`);
13804
13950
  runLog.stream.write(`# run=${this.runId} card=#${card.short_id} runner=sdk started=${new Date().toISOString()}
13805
13951
  ` + `# model=${model} maxTurns=${maxTurns} <prompt:${prompt.length} chars>
13806
13952
 
@@ -13935,7 +14081,7 @@ ${prompt}`;
13935
14081
  try {
13936
14082
  await teardownWorktree2(this.client, this.cardId, this.worktreePath, this.branchName ?? undefined, this.sessionId);
13937
14083
  } catch {
13938
- log34.warn(this.tag, "Failed to cleanup worktree");
14084
+ log35.warn(this.tag, "Failed to cleanup worktree");
13939
14085
  }
13940
14086
  }
13941
14087
  this.process = null;
@@ -13954,7 +14100,7 @@ ${prompt}`;
13954
14100
  this.chargedTokens = emptyTokens();
13955
14101
  }
13956
14102
  }
13957
- var TAG31 = "worker", CANCEL_SIGINT_TIMEOUT2 = 30000, CANCEL_SIGTERM_TIMEOUT2 = 1e4, MOTOR_SESSION_HEARTBEAT_MS = 60000, MOTOR_RUN_PROGRESS_PERCENT = 10, STEERING_MAX_TURNS = 15, MAX_STEERING_ITERATIONS = 10, PLAN_ALLOWED_TOOLS = "Read,Grep,Glob,mcp__harmony__*", IMPLEMENT_ALLOWED_TOOLS = "Bash,Read,Write,Edit,Glob,Grep,Agent,mcp__harmony__*", PLAN_PHASE_TIMEOUT, MAX_STAGE_NAME_CHARS = 80, MAX_HANDOFF_CHARS = 400;
14103
+ var TAG32 = "worker", CANCEL_SIGINT_TIMEOUT2 = 30000, CANCEL_SIGTERM_TIMEOUT2 = 1e4, MOTOR_SESSION_HEARTBEAT_MS = 60000, MOTOR_RUN_PROGRESS_PERCENT = 10, STEERING_MAX_TURNS = 15, MAX_STEERING_ITERATIONS = 10, PLAN_ALLOWED_TOOLS = "Read,Grep,Glob,mcp__harmony__*", IMPLEMENT_ALLOWED_TOOLS = "Bash,Read,Write,Edit,Glob,Grep,Agent,mcp__harmony__*", PLAN_PHASE_TIMEOUT, MAX_STAGE_NAME_CHARS = 80, MAX_HANDOFF_CHARS = 400;
13958
14104
  var init_worker = __esm(() => {
13959
14105
  init_src();
13960
14106
  init_board_helpers();
@@ -13986,7 +14132,7 @@ var init_worker = __esm(() => {
13986
14132
  });
13987
14133
 
13988
14134
  // src/workspace-model-cache.ts
13989
- import { log as log35 } from "@gethmy/harness";
14135
+ import { log as log36 } from "@gethmy/harness";
13990
14136
 
13991
14137
  class WorkspaceModelCache {
13992
14138
  fetcher;
@@ -14021,7 +14167,7 @@ class WorkspaceModelCache {
14021
14167
  this.stale = false;
14022
14168
  } catch (err) {
14023
14169
  this.stale = true;
14024
- log35.warn("config", `Could not refresh a pickup-cached value, using ${this.everFetched ? "the last known value" : "local config only"}: ${err instanceof Error ? err.message : String(err)}`);
14170
+ log36.warn("config", `Could not refresh a pickup-cached value, using ${this.everFetched ? "the last known value" : "local config only"}: ${err instanceof Error ? err.message : String(err)}`);
14025
14171
  }
14026
14172
  return this.value;
14027
14173
  }
@@ -14032,7 +14178,7 @@ var init_workspace_model_cache = () => {};
14032
14178
  import {
14033
14179
  cooldownMsFor,
14034
14180
  describeApiError as describeApiError3,
14035
- log as log36
14181
+ log as log37
14036
14182
  } from "@gethmy/harness";
14037
14183
  function httpStatusOf(err) {
14038
14184
  if (typeof err !== "object" || err === null)
@@ -14125,11 +14271,11 @@ class Pool {
14125
14271
  }
14126
14272
  async enqueue(card, column, labels, subtasks, mode = "implement") {
14127
14273
  if (card.done || card.archived_at) {
14128
- log36.debug(TAG32, `#${card.short_id} skipped — ${card.done ? "done" : "archived"} (finished work is never enqueued, #1102)`);
14274
+ log37.debug(TAG33, `#${card.short_id} skipped — ${card.done ? "done" : "archived"} (finished work is never enqueued, #1102)`);
14129
14275
  return;
14130
14276
  }
14131
14277
  if (this.isCardKnown(card.id) || this.reservations.has(card.id)) {
14132
- log36.debug(TAG32, `Card ${card.id} already queued, active, or reserved, skipping`);
14278
+ log37.debug(TAG33, `Card ${card.id} already queued, active, or reserved, skipping`);
14133
14279
  return;
14134
14280
  }
14135
14281
  this.reservations.add(card.id);
@@ -14137,25 +14283,25 @@ class Pool {
14137
14283
  try {
14138
14284
  const account = this.accountGuard?.check();
14139
14285
  if (account && !account.running) {
14140
- log36.debug(TAG32, `#${card.short_id} held — ${account.detail}`);
14286
+ log37.debug(TAG33, `#${card.short_id} held — ${account.detail}`);
14141
14287
  await this.emitWaiting(card.id, account.detail);
14142
14288
  return;
14143
14289
  }
14144
14290
  if (mode === "implement") {
14145
14291
  if (this.authPaused) {
14146
- log36.debug(TAG32, `#${card.short_id} held — agent paused (auth error)`);
14292
+ log37.debug(TAG33, `#${card.short_id} held — agent paused (auth error)`);
14147
14293
  await this.emitWaiting(card.id, "Agent paused — Anthropic auth error, check API credentials");
14148
14294
  return;
14149
14295
  }
14150
14296
  const cooldownMs = this.apiCooldownRemainingMs();
14151
14297
  if (cooldownMs > 0) {
14152
- log36.debug(TAG32, `#${card.short_id} held — API cooldown ${Math.round(cooldownMs / 1000)}s remaining`);
14298
+ log37.debug(TAG33, `#${card.short_id} held — API cooldown ${Math.round(cooldownMs / 1000)}s remaining`);
14153
14299
  await this.emitWaiting(card.id, `Paused — Anthropic API limit, retrying in ~${Math.round(cooldownMs / 1000)}s`);
14154
14300
  return;
14155
14301
  }
14156
14302
  const stageHold = classifyStageHold(card, this.stateStore.getStageHold(card.id), Date.now());
14157
14303
  if (stageHold.kind === "holding") {
14158
- log36.debug(TAG32, `#${card.short_id} held — ${stageHold.detail}`);
14304
+ log37.debug(TAG33, `#${card.short_id} held — ${stageHold.detail}`);
14159
14305
  return;
14160
14306
  }
14161
14307
  if (stageHold.kind === "stale") {
@@ -14166,19 +14312,19 @@ class Pool {
14166
14312
  if (decision.reason === "daily_budget") {
14167
14313
  await this.denyDailyBudget(card, decision.detail);
14168
14314
  } else {
14169
- log36.debug(TAG32, `#${card.short_id} skipped (${decision.reason}): ${decision.detail}`);
14315
+ log37.debug(TAG33, `#${card.short_id} skipped (${decision.reason}): ${decision.detail}`);
14170
14316
  }
14171
14317
  return;
14172
14318
  }
14173
14319
  const { blockers, successors } = await getChainSignals(this.client, card, this.projectId);
14174
14320
  chainSuccessors = successors;
14175
14321
  if (blockers === null) {
14176
- log36.warn(TAG32, `#${card.short_id} blocker check failed — deferring to next tick`);
14322
+ log37.warn(TAG33, `#${card.short_id} blocker check failed — deferring to next tick`);
14177
14323
  return;
14178
14324
  }
14179
14325
  if (blockers.length > 0) {
14180
14326
  const list = blockers.map((b) => `#${b.shortId}`).join(", ");
14181
- log36.info(TAG32, `#${card.short_id} blocked by ${list} — waiting`);
14327
+ log37.info(TAG33, `#${card.short_id} blocked by ${list} — waiting`);
14182
14328
  await this.emitWaiting(card.id, `Blocked by ${list} — waiting for chain`);
14183
14329
  return;
14184
14330
  }
@@ -14210,9 +14356,9 @@ class Pool {
14210
14356
  const reason = `Daily budget reached — waiting for reset (${detail})`;
14211
14357
  const line = `#${card.short_id} skipped (daily_budget): ${detail}`;
14212
14358
  if (this.lastWaitingEmit.get(card.id) === reason)
14213
- log36.debug(TAG32, line);
14359
+ log37.debug(TAG33, line);
14214
14360
  else
14215
- log36.warn(TAG32, line);
14361
+ log37.warn(TAG33, line);
14216
14362
  await this.emitWaiting(card.id, reason);
14217
14363
  }
14218
14364
  lastWaitingEmit = new Map;
@@ -14229,7 +14375,7 @@ class Pool {
14229
14375
  });
14230
14376
  this.lastWaitingEmit.set(cardId, currentTask);
14231
14377
  } catch (err) {
14232
- log36.debug(TAG32, `waiting emit failed for ${cardId}: ${err instanceof Error ? err.message : err}`);
14378
+ log37.debug(TAG33, `waiting emit failed for ${cardId}: ${err instanceof Error ? err.message : err}`);
14233
14379
  }
14234
14380
  }
14235
14381
  noteApiError(err) {
@@ -14237,13 +14383,13 @@ class Pool {
14237
14383
  return;
14238
14384
  if (err.kind === "spend_limit") {
14239
14385
  this.accountGuard?.halt("spend_limit").catch((hErr) => {
14240
- log36.error(TAG32, `Failed to latch the account halt: ${hErr}`);
14386
+ log37.error(TAG33, `Failed to latch the account halt: ${hErr}`);
14241
14387
  });
14242
14388
  return;
14243
14389
  }
14244
14390
  if (err.kind === "auth") {
14245
14391
  if (!this.authPaused) {
14246
- log36.error(TAG32, "Auth error from Claude CLI — pausing implement pickups until the daemon is restarted with valid credentials");
14392
+ log37.error(TAG33, "Auth error from Claude CLI — pausing implement pickups until the daemon is restarted with valid credentials");
14247
14393
  }
14248
14394
  this.authPaused = true;
14249
14395
  return;
@@ -14252,7 +14398,7 @@ class Pool {
14252
14398
  const until = Date.now() + cooldownMs;
14253
14399
  if (until > this.apiCooldownUntil) {
14254
14400
  this.apiCooldownUntil = until;
14255
- log36.warn(TAG32, `${describeApiError3(err.kind)} — pausing implement pickups for ${Math.round(cooldownMs / 1000)}s`);
14401
+ log37.warn(TAG33, `${describeApiError3(err.kind)} — pausing implement pickups for ${Math.round(cooldownMs / 1000)}s`);
14256
14402
  }
14257
14403
  }
14258
14404
  apiCooldownRemainingMs() {
@@ -14266,13 +14412,13 @@ class Pool {
14266
14412
  const removed = queue.remove(cardId);
14267
14413
  if (removed) {
14268
14414
  this.cardDataCache.delete(cardId);
14269
- log36.info(TAG32, `Removed #${removed.shortId} from ${removed.mode} queue`);
14415
+ log37.info(TAG33, `Removed #${removed.shortId} from ${removed.mode} queue`);
14270
14416
  return;
14271
14417
  }
14272
14418
  }
14273
14419
  const worker = this.implWorkers.find((w) => w.cardId === cardId) ?? this.reviewWorkers.find((w) => w.cardId === cardId);
14274
14420
  if (worker) {
14275
- log36.info(TAG32, `Cancelling worker ${worker.id} for card ${cardId}`);
14421
+ log37.info(TAG33, `Cancelling worker ${worker.id} for card ${cardId}`);
14276
14422
  await worker.cancel("unassigned");
14277
14423
  }
14278
14424
  }
@@ -14329,10 +14475,10 @@ class Pool {
14329
14475
  }
14330
14476
  const worker = this.implWorkers.find((w) => w.cardId === cardId && w.isActive) ?? this.reviewWorkers.find((w) => w.cardId === cardId && w.isActive);
14331
14477
  if (!worker) {
14332
- log36.debug(TAG32, `No active worker for card ${cardId}, ignoring ${command}`);
14478
+ log37.debug(TAG33, `No active worker for card ${cardId}, ignoring ${command}`);
14333
14479
  return;
14334
14480
  }
14335
- log36.info(TAG32, `Agent command: ${command} → worker ${worker.id} (card ${cardId})`);
14481
+ log37.info(TAG33, `Agent command: ${command} → worker ${worker.id} (card ${cardId})`);
14336
14482
  switch (command) {
14337
14483
  case "pause":
14338
14484
  await worker.pause();
@@ -14380,7 +14526,7 @@ class Pool {
14380
14526
  };
14381
14527
  }
14382
14528
  async shutdown() {
14383
- log36.info(TAG32, "Shutting down pool...");
14529
+ log37.info(TAG33, "Shutting down pool...");
14384
14530
  this.shuttingDown = true;
14385
14531
  const active = [
14386
14532
  ...this.implWorkers.filter((w) => w.isActive),
@@ -14388,7 +14534,7 @@ class Pool {
14388
14534
  ];
14389
14535
  await Promise.all(active.map((w) => w.cancel("shutdown")));
14390
14536
  this.sleepGuard.stop();
14391
- log36.info(TAG32, "Pool shutdown complete");
14537
+ log37.info(TAG33, "Pool shutdown complete");
14392
14538
  }
14393
14539
  async drainBudgetDecisions(cardId) {
14394
14540
  const targets = cardId ? [cardId] : [
@@ -14423,7 +14569,7 @@ class Pool {
14423
14569
  await this.forgetDeletedCard(cardId, run);
14424
14570
  return;
14425
14571
  }
14426
- log36.warn(TAG32, `getBudgetDecisions failed for ${cardId}: ${err}`);
14572
+ log37.warn(TAG33, `getBudgetDecisions failed for ${cardId}: ${err}`);
14427
14573
  return;
14428
14574
  }
14429
14575
  if (decisions.length > 0) {
@@ -14454,16 +14600,16 @@ class Pool {
14454
14600
  errorMessage: "card deleted"
14455
14601
  });
14456
14602
  } catch (err) {
14457
- log36.warn(TAG32, `Failed to end the parked run for the deleted card ${cardId}: ${err}`);
14603
+ log37.warn(TAG33, `Failed to end the parked run for the deleted card ${cardId}: ${err}`);
14458
14604
  }
14459
14605
  }
14460
14606
  try {
14461
14607
  const dropped = await this.stateStore.forgetCard(cardId);
14462
14608
  if (dropped) {
14463
- log36.info(TAG32, `Card ${cardId} no longer exists on the server — dropped from the local ledger; its budget question can never be answered`);
14609
+ log37.info(TAG33, `Card ${cardId} no longer exists on the server — dropped from the local ledger; its budget question can never be answered`);
14464
14610
  }
14465
14611
  } catch (err) {
14466
- log36.warn(TAG32, `Failed to drop the deleted card ${cardId}: ${err}`);
14612
+ log37.warn(TAG33, `Failed to drop the deleted card ${cardId}: ${err}`);
14467
14613
  }
14468
14614
  }
14469
14615
  async releaseExpiredPark(run) {
@@ -14477,7 +14623,7 @@ class Pool {
14477
14623
  ...run.blockerCommentId ? { replyToId: run.blockerCommentId } : {}
14478
14624
  });
14479
14625
  } catch (err) {
14480
- log36.warn(TAG32, `Failed to post the expired-park closing comment for ${run.cardId}: ${err}`);
14626
+ log37.warn(TAG33, `Failed to post the expired-park closing comment for ${run.cardId}: ${err}`);
14481
14627
  }
14482
14628
  try {
14483
14629
  const { verdict, card } = await guardedHandback(this.client, run.cardId, {
@@ -14491,14 +14637,14 @@ class Pool {
14491
14637
  });
14492
14638
  }
14493
14639
  } catch (err) {
14494
- log36.error(TAG32, `Failed to move #${run.cardShortId} after an expired park: ${err}`);
14640
+ log37.error(TAG33, `Failed to move #${run.cardShortId} after an expired park: ${err}`);
14495
14641
  }
14496
14642
  try {
14497
14643
  await this.stateStore.endRun(run.runId, "failed", {
14498
14644
  errorMessage: "budget decision expired"
14499
14645
  });
14500
14646
  } catch (err) {
14501
- log36.warn(TAG32, `Failed to release the expired park for ${run.cardId}: ${err}`);
14647
+ log37.warn(TAG33, `Failed to release the expired park for ${run.cardId}: ${err}`);
14502
14648
  }
14503
14649
  }
14504
14650
  async releaseExpiredAttemptCap(cardId, blockerCommentId) {
@@ -14508,7 +14654,7 @@ class Pool {
14508
14654
  ...blockerCommentId ? { replyToId: blockerCommentId } : {}
14509
14655
  });
14510
14656
  } catch (err) {
14511
- log36.warn(TAG32, `Failed to post the expired attempt-cap note for ${cardId}: ${err}`);
14657
+ log37.warn(TAG33, `Failed to post the expired attempt-cap note for ${cardId}: ${err}`);
14512
14658
  }
14513
14659
  try {
14514
14660
  await this.client.endAgentSession(cardId, {
@@ -14517,7 +14663,7 @@ class Pool {
14517
14663
  failureSummary: "The attempt-budget decision expired with no answer. Reassign the card to grant a fresh attempt."
14518
14664
  });
14519
14665
  } catch (err) {
14520
- log36.warn(TAG32, `Failed to end the expired attempt-cap session for ${cardId}: ${err}`);
14666
+ log37.warn(TAG33, `Failed to end the expired attempt-cap session for ${cardId}: ${err}`);
14521
14667
  }
14522
14668
  await this.stateStore.clearAwaitingDecision(cardId);
14523
14669
  }
@@ -14528,16 +14674,16 @@ class Pool {
14528
14674
  }
14529
14675
  const account = this.accountGuard?.check();
14530
14676
  if (account && !account.running) {
14531
- log36.debug(TAG32, `#${run.cardShortId}: granted continue held — ${account.detail}`);
14677
+ log37.debug(TAG33, `#${run.cardShortId}: granted continue held — ${account.detail}`);
14532
14678
  return;
14533
14679
  }
14534
14680
  const first = !this.adoptAnnounced.has(run.runId);
14535
14681
  this.adoptAnnounced.add(run.runId);
14536
14682
  const line = `#${run.cardShortId}: granted continue never reached a worker — re-enqueueing (${run.pipeline})`;
14537
14683
  if (first)
14538
- log36.warn(TAG32, line);
14684
+ log37.warn(TAG33, line);
14539
14685
  else
14540
- log36.debug(TAG32, line);
14686
+ log37.debug(TAG33, line);
14541
14687
  await this.enqueueCard(run.cardId, run.pipeline);
14542
14688
  }
14543
14689
  adoptAnnounced = new Set;
@@ -14556,7 +14702,7 @@ class Pool {
14556
14702
  async continueRun(run, opts) {
14557
14703
  const turns = this.budget.checkCardTurns(run.cardId);
14558
14704
  if (!turns.allow) {
14559
- log36.warn(TAG32, `Refusing a Continue on ${run.cardId} — ${turns.detail}. Handing the card back: reassign it to clear the count, or raise budget.maxTurnsPerCard.`);
14705
+ log37.warn(TAG33, `Refusing a Continue on ${run.cardId} — ${turns.detail}. Handing the card back: reassign it to clear the count, or raise budget.maxTurnsPerCard.`);
14560
14706
  try {
14561
14707
  await this.client.addComment(run.cardId, `**That Continue cannot be granted.** ${turns.detail}, which is this card's ceiling (\`budget.maxTurnsPerCard\`), so the implement pipeline would refuse it again the moment it was dispatched.
14562
14708
 
@@ -14565,14 +14711,14 @@ I am handing the card back rather than holding it parked. Reassign it — that c
14565
14711
  agentSessionId: run.sessionId ?? undefined
14566
14712
  });
14567
14713
  } catch (err) {
14568
- log36.warn(TAG32, `Could not explain the refused Continue on ${run.cardId}: ${err instanceof Error ? err.message : err}`);
14714
+ log37.warn(TAG33, `Could not explain the refused Continue on ${run.cardId}: ${err instanceof Error ? err.message : err}`);
14569
14715
  }
14570
14716
  await this.stopRun(run, "Continue refused: the card is at its per-card turn cap.");
14571
14717
  return;
14572
14718
  }
14573
14719
  const runsSpent = this.budget.checkCardRuns(run.cardId);
14574
14720
  if (!runsSpent.allow) {
14575
- log36.warn(TAG32, `Refusing a Continue on ${run.cardId} — ${runsSpent.detail}. Handing the card back: reassign it to clear the count, or raise budget.maxRunsPerCard.`);
14721
+ log37.warn(TAG33, `Refusing a Continue on ${run.cardId} — ${runsSpent.detail}. Handing the card back: reassign it to clear the count, or raise budget.maxRunsPerCard.`);
14576
14722
  try {
14577
14723
  await this.client.addComment(run.cardId, `**That Continue cannot be granted.** ${runsSpent.detail}, which is this card's run backstop (\`budget.maxRunsPerCard\`), so the implement pipeline would refuse it again the moment it was dispatched.
14578
14724
 
@@ -14581,7 +14727,7 @@ I am handing the card back rather than holding it parked. Reassign it — that c
14581
14727
  agentSessionId: run.sessionId ?? undefined
14582
14728
  });
14583
14729
  } catch (err) {
14584
- log36.warn(TAG32, `Could not explain the refused Continue on ${run.cardId}: ${err instanceof Error ? err.message : err}`);
14730
+ log37.warn(TAG33, `Could not explain the refused Continue on ${run.cardId}: ${err instanceof Error ? err.message : err}`);
14585
14731
  }
14586
14732
  await this.stopRun(run, "Continue refused: the card is at its per-card run backstop.");
14587
14733
  return;
@@ -14605,7 +14751,7 @@ I am handing the card back rather than holding it parked. Reassign it — that c
14605
14751
  awaitingDecisionUntil: null
14606
14752
  });
14607
14753
  } catch (err) {
14608
- log36.warn(TAG32, `Failed to clear the decision deadline for ${run.cardId}: ${err}`);
14754
+ log37.warn(TAG33, `Failed to clear the decision deadline for ${run.cardId}: ${err}`);
14609
14755
  }
14610
14756
  if (run.blockerCommentId) {
14611
14757
  try {
@@ -14613,7 +14759,7 @@ I am handing the card back rather than holding it parked. Reassign it — that c
14613
14759
  resolve: true
14614
14760
  });
14615
14761
  } catch (err) {
14616
- log36.warn(TAG32, `Failed to resolve the blocker comment: ${err}`);
14762
+ log37.warn(TAG33, `Failed to resolve the blocker comment: ${err}`);
14617
14763
  }
14618
14764
  }
14619
14765
  await this.enqueueCard(run.cardId, run.pipeline);
@@ -14637,7 +14783,7 @@ I am handing the card back rather than holding it parked. Reassign it — that c
14637
14783
  awaitingDecisionUntil: null
14638
14784
  });
14639
14785
  } catch (err) {
14640
- log36.warn(TAG32, `Failed to clear the decision deadline for ${run.cardId}: ${err}`);
14786
+ log37.warn(TAG33, `Failed to clear the decision deadline for ${run.cardId}: ${err}`);
14641
14787
  }
14642
14788
  if (run.blockerCommentId) {
14643
14789
  try {
@@ -14645,13 +14791,13 @@ I am handing the card back rather than holding it parked. Reassign it — that c
14645
14791
  resolve: true
14646
14792
  });
14647
14793
  } catch (err) {
14648
- log36.warn(TAG32, `Failed to resolve the blocker comment: ${err}`);
14794
+ log37.warn(TAG33, `Failed to resolve the blocker comment: ${err}`);
14649
14795
  }
14650
14796
  }
14651
14797
  await this.enqueueCard(run.cardId, run.pipeline);
14652
14798
  released += 1;
14653
14799
  } catch (err) {
14654
- log36.error(TAG32, `Failed to release the account-limit park on ${run.cardId}: ${err}`);
14800
+ log37.error(TAG33, `Failed to release the account-limit park on ${run.cardId}: ${err}`);
14655
14801
  }
14656
14802
  }
14657
14803
  return released;
@@ -14663,7 +14809,7 @@ I am handing the card back rather than holding it parked. Reassign it — that c
14663
14809
  resolve: true
14664
14810
  });
14665
14811
  } catch (err) {
14666
- log36.warn(TAG32, `Failed to resolve the blocker comment: ${err}`);
14812
+ log37.warn(TAG33, `Failed to resolve the blocker comment: ${err}`);
14667
14813
  }
14668
14814
  }
14669
14815
  try {
@@ -14672,7 +14818,7 @@ I am handing the card back rather than holding it parked. Reassign it — that c
14672
14818
  awaitingDecisionUntil: null
14673
14819
  });
14674
14820
  } catch (err) {
14675
- log36.warn(TAG32, `Failed to clear the decision deadline for ${run.cardId}: ${err}`);
14821
+ log37.warn(TAG33, `Failed to clear the decision deadline for ${run.cardId}: ${err}`);
14676
14822
  }
14677
14823
  try {
14678
14824
  const { verdict, card } = await guardedHandback(this.client, run.cardId, {
@@ -14694,26 +14840,26 @@ I am handing the card back rather than holding it parked. Reassign it — that c
14694
14840
  await this.client.endAgentSession(run.cardId, endSession);
14695
14841
  }
14696
14842
  } catch (err) {
14697
- log36.error(TAG32, `Failed to hand #${run.cardShortId} back after a stop: ${err}`);
14843
+ log37.error(TAG33, `Failed to hand #${run.cardShortId} back after a stop: ${err}`);
14698
14844
  }
14699
14845
  try {
14700
14846
  await this.stateStore.endRun(run.runId, "failed", {
14701
14847
  errorMessage: "budget_decision_stop"
14702
14848
  });
14703
14849
  } catch (err) {
14704
- log36.warn(TAG32, `Failed to end the local run record for ${run.cardId}: ${err}`);
14850
+ log37.warn(TAG33, `Failed to end the local run record for ${run.cardId}: ${err}`);
14705
14851
  }
14706
14852
  }
14707
14853
  async grantAttempt(cardId) {
14708
14854
  const turns = this.budget.checkCardTurns(cardId);
14709
14855
  if (!turns.allow) {
14710
- log36.warn(TAG32, `Attempt grant on ${cardId} cannot start a run — ${turns.detail}. Clearing the attempt budget anyway; reassign the card to clear the turn count, or raise budget.maxTurnsPerCard.`);
14856
+ log37.warn(TAG33, `Attempt grant on ${cardId} cannot start a run — ${turns.detail}. Clearing the attempt budget anyway; reassign the card to clear the turn count, or raise budget.maxTurnsPerCard.`);
14711
14857
  try {
14712
14858
  await this.client.addComment(cardId, `**Attempt budget cleared, but this card is at its turn cap.** ${turns.detail}, which is its ceiling (\`budget.maxTurnsPerCard\`), so I will not pick it up even with attempts restored.
14713
14859
 
14714
14860
  Reassign the card — that clears the turn count and starts it fresh — or raise the cap in the daemon config.`, { commentType: "blocker" });
14715
14861
  } catch (err) {
14716
- log36.warn(TAG32, `Could not explain the capped attempt grant on ${cardId}: ${err instanceof Error ? err.message : err}`);
14862
+ log37.warn(TAG33, `Could not explain the capped attempt grant on ${cardId}: ${err instanceof Error ? err.message : err}`);
14717
14863
  }
14718
14864
  await this.stateStore.resetAttempts(cardId, { clearTurns: false });
14719
14865
  await this.stopAttemptCap(cardId, "Attempt budget cleared, but the card is at its per-card turn cap.");
@@ -14721,13 +14867,13 @@ Reassign the card — that clears the turn count and starts it fresh — or rais
14721
14867
  }
14722
14868
  const runsSpent = this.budget.checkCardRuns(cardId);
14723
14869
  if (!runsSpent.allow) {
14724
- log36.warn(TAG32, `Attempt grant on ${cardId} cannot start a run — ${runsSpent.detail}. Clearing the attempt budget anyway; reassign the card to clear the run count, or raise budget.maxRunsPerCard.`);
14870
+ log37.warn(TAG33, `Attempt grant on ${cardId} cannot start a run — ${runsSpent.detail}. Clearing the attempt budget anyway; reassign the card to clear the run count, or raise budget.maxRunsPerCard.`);
14725
14871
  try {
14726
14872
  await this.client.addComment(cardId, `**Attempt budget cleared, but this card is at its run backstop.** ${runsSpent.detail}, which is its ceiling (\`budget.maxRunsPerCard\`), so I will not pick it up even with attempts restored.
14727
14873
 
14728
14874
  Reassign the card — that clears the run count and starts it fresh — or raise the cap in the daemon config.`, { commentType: "blocker" });
14729
14875
  } catch (err) {
14730
- log36.warn(TAG32, `Could not explain the capped attempt grant on ${cardId}: ${err instanceof Error ? err.message : err}`);
14876
+ log37.warn(TAG33, `Could not explain the capped attempt grant on ${cardId}: ${err instanceof Error ? err.message : err}`);
14731
14877
  }
14732
14878
  await this.stateStore.resetAttempts(cardId, { clearTurns: false });
14733
14879
  await this.stopAttemptCap(cardId, "Attempt budget cleared, but the card is at its per-card run backstop.");
@@ -14742,7 +14888,7 @@ Reassign the card — that clears the run count and starts it fresh — or raise
14742
14888
  awaitingDecisionUntil: null
14743
14889
  });
14744
14890
  } catch (err) {
14745
- log36.warn(TAG32, `Failed to clear the attempt-cap decision deadline for ${cardId}: ${err}`);
14891
+ log37.warn(TAG33, `Failed to clear the attempt-cap decision deadline for ${cardId}: ${err}`);
14746
14892
  }
14747
14893
  await this.enqueueCard(cardId, "implement");
14748
14894
  }
@@ -14753,7 +14899,7 @@ Reassign the card — that clears the run count and starts it fresh — or raise
14753
14899
  try {
14754
14900
  await this.client.updateComment(blockerCommentId, { resolve: true });
14755
14901
  } catch (err) {
14756
- log36.warn(TAG32, `Failed to resolve the blocker comment: ${err}`);
14902
+ log37.warn(TAG33, `Failed to resolve the blocker comment: ${err}`);
14757
14903
  }
14758
14904
  }
14759
14905
  try {
@@ -14762,7 +14908,7 @@ Reassign the card — that clears the run count and starts it fresh — or raise
14762
14908
  awaitingDecisionUntil: null
14763
14909
  });
14764
14910
  } catch (err) {
14765
- log36.warn(TAG32, `Failed to clear the decision deadline for ${cardId}: ${err}`);
14911
+ log37.warn(TAG33, `Failed to clear the decision deadline for ${cardId}: ${err}`);
14766
14912
  }
14767
14913
  try {
14768
14914
  await this.client.endAgentSession(cardId, {
@@ -14771,7 +14917,7 @@ Reassign the card — that clears the run count and starts it fresh — or raise
14771
14917
  failureSummary
14772
14918
  });
14773
14919
  } catch (err) {
14774
- log36.warn(TAG32, `Failed to end the attempt-cap session for ${cardId}: ${err}`);
14920
+ log37.warn(TAG33, `Failed to end the attempt-cap session for ${cardId}: ${err}`);
14775
14921
  }
14776
14922
  await this.stateStore.clearAwaitingDecision(cardId);
14777
14923
  }
@@ -14802,12 +14948,12 @@ Reassign the card — that clears the run count and starts it fresh — or raise
14802
14948
  const subtasks = card.subtasks ?? [];
14803
14949
  await this.enqueue(card, column, cardLabels, subtasks, mode);
14804
14950
  } catch (err) {
14805
- log36.error(TAG32, `Failed to re-enqueue ${cardId} after a budget decision: ${err}`);
14951
+ log37.error(TAG33, `Failed to re-enqueue ${cardId} after a budget decision: ${err}`);
14806
14952
  }
14807
14953
  }
14808
14954
  async retireUnroutableRuns(shortId, cardId, reason = "column not found on this board") {
14809
14955
  const unended = this.stateStore.getRunsForCard(cardId).filter((r) => r.endedAt === null);
14810
- log36.warn(TAG32, `#${shortId}: ${reason} — retiring ${unended.length} run record(s) instead of re-enqueueing`);
14956
+ log37.warn(TAG33, `#${shortId}: ${reason} — retiring ${unended.length} run record(s) instead of re-enqueueing`);
14811
14957
  for (const run of unended) {
14812
14958
  await this.stateStore.endRun(run.runId, "orphaned", {
14813
14959
  errorMessage: `${reason} at re-enqueue — retired as unroutable (#1057/#1102)`
@@ -14820,12 +14966,12 @@ Reassign the card — that clears the run count and starts it fresh — or raise
14820
14966
  if (this.shuttingDown)
14821
14967
  return false;
14822
14968
  if (this.accountGuard && !this.accountGuard.check().running) {
14823
- log36.debug(TAG32, `${label} dispatch held — the account is halted`);
14969
+ log37.debug(TAG33, `${label} dispatch held — the account is halted`);
14824
14970
  return false;
14825
14971
  }
14826
14972
  const idle = workers.find((w) => w.isIdle);
14827
14973
  if (!idle) {
14828
- log36.debug(TAG32, `No idle ${label} workers (queue: ${queue.length})`);
14974
+ log37.debug(TAG33, `No idle ${label} workers (queue: ${queue.length})`);
14829
14975
  return false;
14830
14976
  }
14831
14977
  const next = queue.dequeue();
@@ -14833,18 +14979,18 @@ Reassign the card — that clears the run count and starts it fresh — or raise
14833
14979
  return false;
14834
14980
  const data = this.cardDataCache.get(next.cardId);
14835
14981
  if (!data) {
14836
- log36.warn(TAG32, `No cached data for card ${next.cardId}, skipping`);
14982
+ log37.warn(TAG33, `No cached data for card ${next.cardId}, skipping`);
14837
14983
  return false;
14838
14984
  }
14839
14985
  this.cardDataCache.delete(next.cardId);
14840
14986
  this.lastWaitingEmit.delete(next.cardId);
14841
- log36.info(TAG32, `Dispatching #${next.shortId} to ${label} worker ${idle.id}`);
14987
+ log37.info(TAG33, `Dispatching #${next.shortId} to ${label} worker ${idle.id}`);
14842
14988
  this.sleepGuard.acquire();
14843
14989
  idle.run(data.card, data.column, data.labels, data.subtasks);
14844
14990
  return true;
14845
14991
  }
14846
14992
  }
14847
- var TAG32 = "pool";
14993
+ var TAG33 = "pool";
14848
14994
  var init_pool = __esm(() => {
14849
14995
  init_src();
14850
14996
  init_board_helpers();
@@ -14879,7 +15025,7 @@ import {
14879
15025
  writeFileSync as writeFileSync4
14880
15026
  } from "node:fs";
14881
15027
  import { dirname as dirname4 } from "node:path";
14882
- import { log as log37 } from "@gethmy/harness";
15028
+ import { log as log38 } from "@gethmy/harness";
14883
15029
  function defaultRegistryPath() {
14884
15030
  return portRegistryPath();
14885
15031
  }
@@ -14894,7 +15040,7 @@ function load(path) {
14894
15040
  }
14895
15041
  return emptyRegistry();
14896
15042
  } catch (err) {
14897
- log37.warn(TAG33, `failed to read ${path}: ${err instanceof Error ? err.message : err}`);
15043
+ log38.warn(TAG34, `failed to read ${path}: ${err instanceof Error ? err.message : err}`);
14898
15044
  return emptyRegistry();
14899
15045
  }
14900
15046
  }
@@ -14915,7 +15061,7 @@ function recordDaemonPort(projectId, entry, path = defaultRegistryPath()) {
14915
15061
  registry[projectId] = { ...entry, updatedAt: Date.now() };
14916
15062
  save(path, registry);
14917
15063
  } catch (err) {
14918
- log37.warn(TAG33, `failed to record port for ${projectId}: ${err instanceof Error ? err.message : err}`);
15064
+ log38.warn(TAG34, `failed to record port for ${projectId}: ${err instanceof Error ? err.message : err}`);
14919
15065
  }
14920
15066
  }
14921
15067
  function daemonBaseUrl(bindAddr, port) {
@@ -14935,17 +15081,17 @@ function clearDaemonPort(projectId, pid, path = defaultRegistryPath()) {
14935
15081
  delete registry[projectId];
14936
15082
  save(path, registry);
14937
15083
  } catch (err) {
14938
- log37.warn(TAG33, `failed to clear port for ${projectId}: ${err instanceof Error ? err.message : err}`);
15084
+ log38.warn(TAG34, `failed to clear port for ${projectId}: ${err instanceof Error ? err.message : err}`);
14939
15085
  }
14940
15086
  }
14941
- var TAG33 = "port-registry";
15087
+ var TAG34 = "port-registry";
14942
15088
  var init_port_registry = __esm(() => {
14943
15089
  init_src();
14944
15090
  init_paths();
14945
15091
  });
14946
15092
 
14947
15093
  // src/recovery.ts
14948
- import { log as log38, teardownWorktree as teardownWorktree3 } from "@gethmy/harness";
15094
+ import { log as log39, teardownWorktree as teardownWorktree3 } from "@gethmy/harness";
14949
15095
  function isProcessAlive(pid, currentPid) {
14950
15096
  if (pid === currentPid)
14951
15097
  return true;
@@ -14961,7 +15107,7 @@ async function fetchCardSafely(client, cardId) {
14961
15107
  const { card } = await client.getCard(cardId);
14962
15108
  return card;
14963
15109
  } catch (err) {
14964
- log38.warn(TAG34, `cannot fetch card ${cardId}: ${err instanceof Error ? err.message : err}`);
15110
+ log39.warn(TAG35, `cannot fetch card ${cardId}: ${err instanceof Error ? err.message : err}`);
14965
15111
  return null;
14966
15112
  }
14967
15113
  }
@@ -14971,7 +15117,7 @@ async function recoverOrphans(store, client, config, opts = {}) {
14971
15117
  return [];
14972
15118
  }
14973
15119
  const outcomes = [];
14974
- log38.info(TAG34, `recovering ${active.length} orphan run(s) from prior daemon`);
15120
+ log39.info(TAG35, `recovering ${active.length} orphan run(s) from prior daemon`);
14975
15121
  for (const run of active) {
14976
15122
  const outcome = {
14977
15123
  runId: run.runId,
@@ -14983,16 +15129,16 @@ async function recoverOrphans(store, client, config, opts = {}) {
14983
15129
  };
14984
15130
  outcomes.push(outcome);
14985
15131
  if (isBudgetHeldRun(run)) {
14986
- log38.info(TAG34, `run ${run.runId} (#${run.cardShortId}) is held for a human budget decision — leaving it`);
15132
+ log39.info(TAG35, `run ${run.runId} (#${run.cardShortId}) is held for a human budget decision — leaving it`);
14987
15133
  outcome.actions.push("skipped: held for a human budget decision");
14988
15134
  continue;
14989
15135
  }
14990
15136
  if (isProcessAlive(run.daemonPid, process.pid)) {
14991
- log38.warn(TAG34, `run ${run.runId} claims live daemon pid ${run.daemonPid} — skipping`);
15137
+ log39.warn(TAG35, `run ${run.runId} claims live daemon pid ${run.daemonPid} — skipping`);
14992
15138
  outcome.actions.push("skipped: daemon pid still alive");
14993
15139
  continue;
14994
15140
  }
14995
- log38.info(TAG34, `recovering ${run.pipeline} run ${run.runId} for card #${run.cardShortId}`);
15141
+ log39.info(TAG35, `recovering ${run.pipeline} run ${run.runId} for card #${run.cardShortId}`);
14996
15142
  await recoverRun(run, store, client, config, outcome, {
14997
15143
  rollbackAttempt: true,
14998
15144
  agentId: opts.agentId
@@ -15013,7 +15159,7 @@ async function recoverRun(run, store, client, config, outcome, opts = {}) {
15013
15159
  } catch (err) {
15014
15160
  const msg = err instanceof Error ? err.message : String(err);
15015
15161
  outcome.errors.push(`endAgentSession: ${msg}`);
15016
- log38.warn(TAG34, `endAgentSession failed for ${run.cardId}: ${msg}`);
15162
+ log39.warn(TAG35, `endAgentSession failed for ${run.cardId}: ${msg}`);
15017
15163
  }
15018
15164
  const card = await fetchCardSafely(client, run.cardId);
15019
15165
  if (card) {
@@ -15073,9 +15219,9 @@ async function recoverRun(run, store, client, config, outcome, opts = {}) {
15073
15219
  outcome.errors.push(`decrementAttempt: ${msg}`);
15074
15220
  }
15075
15221
  }
15076
- log38.info(TAG34, `recovered run ${run.runId} (card #${run.cardShortId}): ${outcome.actions.join(", ")}${outcome.errors.length ? ` | errors: ${outcome.errors.join("; ")}` : ""}`);
15222
+ log39.info(TAG35, `recovered run ${run.runId} (card #${run.cardShortId}): ${outcome.actions.join(", ")}${outcome.errors.length ? ` | errors: ${outcome.errors.join("; ")}` : ""}`);
15077
15223
  }
15078
- var TAG34 = "recovery", RECOVERED_LABEL = "agent-recovered", RECOVERED_LABEL_COLOR = "#f59e0b";
15224
+ var TAG35 = "recovery", RECOVERED_LABEL = "agent-recovered", RECOVERED_LABEL_COLOR = "#f59e0b";
15079
15225
  var init_recovery = __esm(() => {
15080
15226
  init_board_helpers();
15081
15227
  init_handback();
@@ -15104,18 +15250,18 @@ function isTrustedCardAuthor(card, trustedAuthors) {
15104
15250
  }
15105
15251
 
15106
15252
  // src/claim.ts
15107
- import { log as log39 } from "@gethmy/harness";
15253
+ import { log as log40 } from "@gethmy/harness";
15108
15254
  async function claimUnassignedCard(client, cardId, agentId, opts) {
15109
15255
  try {
15110
15256
  const { claimed } = opts ? await client.claimCard(cardId, agentId, opts) : await client.claimCard(cardId, agentId);
15111
- log39.debug(TAG35, `claim ${cardId} → ${claimed ? "won" : "lost"}`);
15257
+ log40.debug(TAG36, `claim ${cardId} → ${claimed ? "won" : "lost"}`);
15112
15258
  return claimed;
15113
15259
  } catch (err) {
15114
- log39.error(TAG35, `claim ${cardId} failed: ${err instanceof Error ? err.message : err}`);
15260
+ log40.error(TAG36, `claim ${cardId} failed: ${err instanceof Error ? err.message : err}`);
15115
15261
  return false;
15116
15262
  }
15117
15263
  }
15118
- var TAG35 = "claim";
15264
+ var TAG36 = "claim";
15119
15265
  var init_claim = () => {};
15120
15266
 
15121
15267
  // src/strand-recovery.ts
@@ -15123,7 +15269,7 @@ var exports_strand_recovery = {};
15123
15269
  __export(exports_strand_recovery, {
15124
15270
  reclaimPreReviewStrands: () => reclaimPreReviewStrands
15125
15271
  });
15126
- import { log as log40, resolvePrUrl as resolvePrUrl2 } from "@gethmy/harness";
15272
+ import { log as log41, resolvePrUrl as resolvePrUrl2 } from "@gethmy/harness";
15127
15273
  async function reclaimPreReviewStrands(opts) {
15128
15274
  const {
15129
15275
  client,
@@ -15159,7 +15305,7 @@ async function reclaimPreReviewStrands(opts) {
15159
15305
  continue;
15160
15306
  const daemonBranch = daemonBranches.get(card.id);
15161
15307
  if (!isTrustedCardAuthor(card, trustedAuthors) && daemonBranch !== branch) {
15162
- log40.debug(TAG36, `#${card.short_id} - skipped: ${card.source != null ? `card was filed externally (source: ${card.source})` : `author ${card.created_by ?? "(none)"} is not trusted`}, and ${daemonBranch ? `the card records ${branch} while this daemon pushed ${daemonBranch}` : "this daemon has pushed no branch for it"}`);
15308
+ log41.debug(TAG37, `#${card.short_id} - skipped: ${card.source != null ? `card was filed externally (source: ${card.source})` : `author ${card.created_by ?? "(none)"} is not trusted`}, and ${daemonBranch ? `the card records ${branch} while this daemon pushed ${daemonBranch}` : "this daemon has pushed no branch for it"}`);
15163
15309
  continue;
15164
15310
  }
15165
15311
  const labels = resolveCardLabels(card, labelMap);
@@ -15175,22 +15321,22 @@ async function reclaimPreReviewStrands(opts) {
15175
15321
  continue;
15176
15322
  const won = await claimUnassignedCard(client, card.id, agentId);
15177
15323
  if (!won) {
15178
- log40.debug(TAG36, `#${card.short_id} — lost the review claim race, skipping`);
15324
+ log41.debug(TAG37, `#${card.short_id} — lost the review claim race, skipping`);
15179
15325
  continue;
15180
15326
  }
15181
- log40.warn(TAG36, `#${card.short_id} claimed for review (branch pushed, no PR, unowned)`);
15327
+ log41.warn(TAG37, `#${card.short_id} claimed for review (branch pushed, no PR, unowned)`);
15182
15328
  reclaimed.push(card.id);
15183
15329
  if (opts.onClaimed) {
15184
15330
  try {
15185
15331
  await opts.onClaimed(card);
15186
15332
  } catch (err) {
15187
- log40.error(TAG36, `onClaimed for #${card.short_id} failed: ${err instanceof Error ? err.message : err}`);
15333
+ log41.error(TAG37, `onClaimed for #${card.short_id} failed: ${err instanceof Error ? err.message : err}`);
15188
15334
  }
15189
15335
  }
15190
15336
  }
15191
15337
  return reclaimed;
15192
15338
  }
15193
- var TAG36 = "strand-recovery";
15339
+ var TAG37 = "strand-recovery";
15194
15340
  var init_strand_recovery = __esm(() => {
15195
15341
  init_src();
15196
15342
  init_board_helpers();
@@ -15356,7 +15502,7 @@ var init_scope = __esm(() => {
15356
15502
  });
15357
15503
 
15358
15504
  // src/sweep.ts
15359
- import { log as log41 } from "@gethmy/harness";
15505
+ import { log as log42 } from "@gethmy/harness";
15360
15506
  function rankSweepCandidates(opts) {
15361
15507
  const {
15362
15508
  agentId,
@@ -15475,10 +15621,10 @@ async function sweepForCard(opts) {
15475
15621
  let { candidates, skipped, scope } = rankSweepCandidates(opts);
15476
15622
  const cohesionCounts = opts.ranking.cohesionWeight !== 0;
15477
15623
  if (cohesionCounts) {
15478
- log41.debug(TAG37, describeActiveScope(scope.active));
15624
+ log42.debug(TAG38, describeActiveScope(scope.active));
15479
15625
  }
15480
15626
  if (candidates.length === 0) {
15481
- log41.debug(TAG37, `no claimable card (${skipped.length} skipped in a pickup column)`);
15627
+ log42.debug(TAG38, `no claimable card (${skipped.length} skipped in a pickup column)`);
15482
15628
  return { claimed: null, skipped, probed: 0 };
15483
15629
  }
15484
15630
  const probeWindow = candidates.slice(0, maxProbes);
@@ -15494,12 +15640,12 @@ async function sweepForCard(opts) {
15494
15640
  chainNeighbors: chainNeighbors(chain2)
15495
15641
  }));
15496
15642
  } else if (chainTermsCount && candidates.length > 1) {
15497
- log41.debug(TAG37, `chain signals not ranked — maxProbesPerTick=${maxProbes} leaves nothing to reorder among ${candidates.length} candidates`);
15643
+ log42.debug(TAG38, `chain signals not ranked — maxProbesPerTick=${maxProbes} leaves nothing to reorder among ${candidates.length} candidates`);
15498
15644
  }
15499
15645
  let probed = 0;
15500
15646
  for (const candidate of candidates) {
15501
15647
  if (probed >= maxProbes) {
15502
- log41.debug(TAG37, `probe budget ${maxProbes} spent — ${candidates.length - probed} candidate(s) deferred to the next tick`);
15648
+ log42.debug(TAG38, `probe budget ${maxProbes} spent — ${candidates.length - probed} candidate(s) deferred to the next tick`);
15503
15649
  break;
15504
15650
  }
15505
15651
  probed++;
@@ -15577,10 +15723,10 @@ async function sweepForCard(opts) {
15577
15723
  continue;
15578
15724
  }
15579
15725
  verified.card.assigned_agent_id = agentId;
15580
- log41.info(TAG37, claimLine(verified, probed, candidates, skipped, cohesionCounts ? scope.active : null));
15726
+ log42.info(TAG38, claimLine(verified, probed, candidates, skipped, cohesionCounts ? scope.active : null));
15581
15727
  return { claimed: verified, skipped, probed };
15582
15728
  }
15583
- log41.debug(TAG37, `claimed nothing this sweep (${probed} probed, ${skipped.length} skipped)`);
15729
+ log42.debug(TAG38, `claimed nothing this sweep (${probed} probed, ${skipped.length} skipped)`);
15584
15730
  return { claimed: null, skipped, probed };
15585
15731
  }
15586
15732
  function claimLine(claimed, rank, candidates, skipped, scope) {
@@ -15601,7 +15747,7 @@ async function refetchCard(client, cardId) {
15601
15747
  const { card } = await client.getCard(cardId);
15602
15748
  return card ?? null;
15603
15749
  } catch (err) {
15604
- log41.warn(TAG37, `re-read failed for ${cardId}: ${err instanceof Error ? err.message : err}`);
15750
+ log42.warn(TAG38, `re-read failed for ${cardId}: ${err instanceof Error ? err.message : err}`);
15605
15751
  return null;
15606
15752
  }
15607
15753
  }
@@ -15610,11 +15756,11 @@ async function hasLiveSession(client, cardId) {
15610
15756
  const { session } = await client.getAgentSession(cardId);
15611
15757
  return session != null;
15612
15758
  } catch (err) {
15613
- log41.warn(TAG37, `session lookup failed for ${cardId} — treating as busy: ${err instanceof Error ? err.message : err}`);
15759
+ log42.warn(TAG38, `session lookup failed for ${cardId} — treating as busy: ${err instanceof Error ? err.message : err}`);
15614
15760
  return true;
15615
15761
  }
15616
15762
  }
15617
- var TAG37 = "sweep";
15763
+ var TAG38 = "sweep";
15618
15764
  var init_sweep = __esm(() => {
15619
15765
  init_board_helpers();
15620
15766
  init_claim();
@@ -15625,7 +15771,7 @@ var init_sweep = __esm(() => {
15625
15771
  });
15626
15772
 
15627
15773
  // src/sweep-commands.ts
15628
- import { log as log42 } from "@gethmy/harness";
15774
+ import { log as log43 } from "@gethmy/harness";
15629
15775
  function parseSweepCommands(payload) {
15630
15776
  if (!payload || typeof payload !== "object")
15631
15777
  return [];
@@ -15652,14 +15798,14 @@ function parseSweepCommands(payload) {
15652
15798
  async function claimSweepCommands(client, agentId, projectId, daemonId) {
15653
15799
  const request = client.request?.bind(client);
15654
15800
  if (typeof request !== "function") {
15655
- log42.warn(TAG38, "installed @gethmy/mcp has no generic request method — board sweep commands cannot be claimed; use `harmony-agent sweep stop|resume` until it is upgraded");
15801
+ log43.warn(TAG39, "installed @gethmy/mcp has no generic request method — board sweep commands cannot be claimed; use `harmony-agent sweep stop|resume` until it is upgraded");
15656
15802
  return [];
15657
15803
  }
15658
15804
  try {
15659
15805
  const res = await request("POST", `/agents/${encodeURIComponent(agentId)}/sweep-commands/claim`, { projectId, daemonId });
15660
15806
  return parseSweepCommands(res);
15661
15807
  } catch (err) {
15662
- log42.warn(TAG38, `could not claim sweep commands: ${err instanceof Error ? err.message : err}`);
15808
+ log43.warn(TAG39, `could not claim sweep commands: ${err instanceof Error ? err.message : err}`);
15663
15809
  return [];
15664
15810
  }
15665
15811
  }
@@ -15672,15 +15818,15 @@ async function applySweepCommands(commands, actions) {
15672
15818
  await actions.resume();
15673
15819
  }
15674
15820
  applied = command.verb;
15675
- log42.info(TAG38, `applied "${command.verb}" from the board (${command.id})`);
15821
+ log43.info(TAG39, `applied "${command.verb}" from the board (${command.id})`);
15676
15822
  }
15677
15823
  return applied;
15678
15824
  }
15679
- var TAG38 = "sweep-cmd";
15825
+ var TAG39 = "sweep-cmd";
15680
15826
  var init_sweep_commands = () => {};
15681
15827
 
15682
15828
  // src/sweep-guard.ts
15683
- import { log as log43 } from "@gethmy/harness";
15829
+ import { log as log44 } from "@gethmy/harness";
15684
15830
 
15685
15831
  class SweepGuard {
15686
15832
  config;
@@ -15736,7 +15882,7 @@ class SweepGuard {
15736
15882
  await this.store.haltSweep(reason);
15737
15883
  if (!already) {
15738
15884
  const claimed = this.store.getSweep().claimed;
15739
- log43.info(TAG39, `claiming stopped (${reason}) after ${claimed} claimed`);
15885
+ log44.info(TAG40, `claiming stopped (${reason}) after ${claimed} claimed`);
15740
15886
  }
15741
15887
  return this.snapshot();
15742
15888
  }
@@ -15745,7 +15891,7 @@ class SweepGuard {
15745
15891
  const was = before.haltReason ? this.describeStop(before.haltReason) : null;
15746
15892
  await this.store.resumeSweep();
15747
15893
  if (was)
15748
- log43.info(TAG39, `claiming resumed by the operator — was stopped: ${was}`);
15894
+ log44.info(TAG40, `claiming resumed by the operator — was stopped: ${was}`);
15749
15895
  return this.snapshot();
15750
15896
  }
15751
15897
  snapshot() {
@@ -15807,11 +15953,11 @@ function sweepBannerLine(config) {
15807
15953
  const authors = extra ? `you + ${extra} trusted author${extra === 1 ? "" : "s"}` : "you only";
15808
15954
  return `Sweep ON — claims ${scope} in ${config.pickupColumns.join(", ")}, ` + `authored by ${authors}; caps: ${describeCaps(sweep, config.budget.dailyBudgetCents)}; ` + `${sweep.maxProbesPerTick} probes/tick`;
15809
15955
  }
15810
- var TAG39 = "sweep";
15956
+ var TAG40 = "sweep";
15811
15957
  var init_sweep_guard = () => {};
15812
15958
 
15813
15959
  // src/reconcile.ts
15814
- import { detectGitProvider as detectGitProvider5, log as log44 } from "@gethmy/harness";
15960
+ import { detectGitProvider as detectGitProvider5, log as log45 } from "@gethmy/harness";
15815
15961
  function daemonProcessLabels(approvedLabel) {
15816
15962
  const names = ["agent", "agent-recovered", NEED_REVIEW_LABEL, approvedLabel];
15817
15963
  return new Set(names.filter(Boolean).map((n) => n.toLowerCase()));
@@ -15874,7 +16020,7 @@ class Reconciler {
15874
16020
  clearInterval(this.timer);
15875
16021
  this.timer = null;
15876
16022
  }
15877
- log44.info(TAG40, "Heartbeat stopped");
16023
+ log45.info(TAG41, "Heartbeat stopped");
15878
16024
  }
15879
16025
  async recoverStaleRuns() {
15880
16026
  if (!this.stateStore || !this.agentConfig)
@@ -15885,7 +16031,7 @@ class Reconciler {
15885
16031
  const pool = this.pool;
15886
16032
  for (const run of active) {
15887
16033
  if (isBudgetHeldRun(run)) {
15888
- log44.info(TAG40, `run ${run.runId} (#${run.cardShortId}) is held for a human budget decision — leaving it`);
16034
+ log45.info(TAG41, `run ${run.runId} (#${run.cardShortId}) is held for a human budget decision — leaving it`);
15889
16035
  continue;
15890
16036
  }
15891
16037
  const foreignDaemon = run.daemonPid !== process.pid;
@@ -15895,7 +16041,7 @@ class Reconciler {
15895
16041
  if (!daemonDead && !(heartbeatStale && ourZombie))
15896
16042
  continue;
15897
16043
  const reason = daemonDead ? `foreign daemon ${run.daemonPid} is dead` : `our worker lost card ${run.cardId} with ${Math.round((now - run.lastHeartbeatAt) / 1000)}s stale heartbeat`;
15898
- log44.warn(TAG40, `zombie run ${run.runId} (#${run.cardShortId}): ${reason} — recovering`);
16044
+ log45.warn(TAG41, `zombie run ${run.runId} (#${run.cardShortId}): ${reason} — recovering`);
15899
16045
  await recoverRun(run, this.stateStore, this.client, this.agentConfig, {
15900
16046
  runId: run.runId,
15901
16047
  cardId: run.cardId,
@@ -15922,11 +16068,11 @@ class Reconciler {
15922
16068
  const stalledAt = Date.parse(card.updated_at ?? "");
15923
16069
  if (!Number.isFinite(stalledAt) || now - stalledAt < graceMs)
15924
16070
  continue;
15925
- log44.warn(TAG40, `#${card.short_id} stranded in "${inProgressCol.name}" (no live run) — requeueing to "${pickupCol.name}"`);
16071
+ log45.warn(TAG41, `#${card.short_id} stranded in "${inProgressCol.name}" (no live run) — requeueing to "${pickupCol.name}"`);
15926
16072
  try {
15927
16073
  await this.client.moveCard(card.id, pickupCol.id);
15928
16074
  } catch (err) {
15929
- log44.error(TAG40, `stranded requeue failed for #${card.short_id}: ${err instanceof Error ? err.message : err}`);
16075
+ log45.error(TAG41, `stranded requeue failed for #${card.short_id}: ${err instanceof Error ? err.message : err}`);
15930
16076
  }
15931
16077
  }
15932
16078
  }
@@ -15979,7 +16125,7 @@ class Reconciler {
15979
16125
  return;
15980
16126
  const cardLabels = resolveCardLabels(card, labelMap);
15981
16127
  const subtasks = card.subtasks ?? [];
15982
- log44.info(TAG40, `Enqueuing claimed review card #${card.short_id} (agent-agnostic pickup)`);
16128
+ log45.info(TAG41, `Enqueuing claimed review card #${card.short_id} (agent-agnostic pickup)`);
15983
16129
  await this.pool.enqueue(card, column, cardLabels, subtasks, "review");
15984
16130
  }
15985
16131
  });
@@ -16003,11 +16149,11 @@ class Reconciler {
16003
16149
  const parkedAt = Date.parse(card.updated_at ?? "");
16004
16150
  if (!Number.isFinite(parkedAt) || now - parkedAt < ttlMs)
16005
16151
  continue;
16006
- log44.warn(TAG40, `#${card.short_id} parked for approval > ${planning.approvalTtlHours}h — auto-releasing to "${pickupCol.name}"`);
16152
+ log45.warn(TAG41, `#${card.short_id} parked for approval > ${planning.approvalTtlHours}h — auto-releasing to "${pickupCol.name}"`);
16007
16153
  try {
16008
16154
  await this.client.moveCard(card.id, pickupCol.id);
16009
16155
  } catch (err) {
16010
- log44.error(TAG40, `auto-release failed for #${card.short_id}: ${err instanceof Error ? err.message : err}`);
16156
+ log45.error(TAG41, `auto-release failed for #${card.short_id}: ${err instanceof Error ? err.message : err}`);
16011
16157
  }
16012
16158
  }
16013
16159
  }
@@ -16042,7 +16188,7 @@ class Reconciler {
16042
16188
  }
16043
16189
  this.lastSweepStopReported = null;
16044
16190
  if (!this.pool.hasFreeImplementSlot()) {
16045
- log44.debug(TAG40, "sweep skipped — no free implement slot");
16191
+ log45.debug(TAG41, "sweep skipped — no free implement slot");
16046
16192
  return;
16047
16193
  }
16048
16194
  const { claimed } = await sweepForCard({
@@ -16061,9 +16207,9 @@ class Reconciler {
16061
16207
  reportSweepStopped(reason, detail) {
16062
16208
  const line = `sweep stopped claiming — ${detail}`;
16063
16209
  if (this.lastSweepStopReported === reason)
16064
- log44.debug(TAG40, line);
16210
+ log45.debug(TAG41, line);
16065
16211
  else
16066
- log44.warn(TAG40, line);
16212
+ log45.warn(TAG41, line);
16067
16213
  this.lastSweepStopReported = reason;
16068
16214
  }
16069
16215
  setSweepPresenceSink(sink) {
@@ -16101,7 +16247,7 @@ class Reconciler {
16101
16247
  return;
16102
16248
  this.lastSweepNudgeAt = now;
16103
16249
  this.drainSweepCommands().catch((err) => {
16104
- log44.error(TAG40, `sweep command nudge failed: ${err instanceof Error ? err.message : err}`);
16250
+ log45.error(TAG41, `sweep command nudge failed: ${err instanceof Error ? err.message : err}`);
16105
16251
  });
16106
16252
  }
16107
16253
  resolveRepoDirectories() {
@@ -16110,9 +16256,9 @@ class Reconciler {
16110
16256
  const dirs = readRepoDirectories(process.cwd());
16111
16257
  this.repoDirectories = dirs;
16112
16258
  if (dirs.size === 0) {
16113
- log44.warn(TAG40, `Could not read top-level directories of ${process.cwd()} — sweep cohesion runs without its path signal`);
16259
+ log45.warn(TAG41, `Could not read top-level directories of ${process.cwd()} — sweep cohesion runs without its path signal`);
16114
16260
  } else {
16115
- log44.debug(TAG40, `Path signal allows ${dirs.size} top-level directories`);
16261
+ log45.debug(TAG41, `Path signal allows ${dirs.size} top-level directories`);
16116
16262
  }
16117
16263
  return dirs;
16118
16264
  }
@@ -16148,7 +16294,7 @@ class Reconciler {
16148
16294
  if (accountHalt !== this.lastAccountHoldReported) {
16149
16295
  if (accountHalt) {
16150
16296
  const unqueued = assignedCards.filter((c) => !knownCardIds.has(c.id)).length;
16151
- log44.warn(TAG40, `Account halted (${accountHalt}) — ${unqueued} assigned card(s) not queued; nothing is dispatched until \`harmony-agent resume\``);
16297
+ log45.warn(TAG41, `Account halted (${accountHalt}) — ${unqueued} assigned card(s) not queued; nothing is dispatched until \`harmony-agent resume\``);
16152
16298
  }
16153
16299
  this.lastAccountHoldReported = accountHalt;
16154
16300
  }
@@ -16164,24 +16310,24 @@ class Reconciler {
16164
16310
  const subtasks = card.subtasks ?? [];
16165
16311
  const mode = route.mode;
16166
16312
  if (route.stage) {
16167
- log44.info(TAG40, `Stage card #${card.short_id} (stage "${card.current_stage}") in "${column.name}" — routing to the stage executor (implement) regardless of column`);
16313
+ log45.info(TAG41, `Stage card #${card.short_id} (stage "${card.current_stage}") in "${column.name}" — routing to the stage executor (implement) regardless of column`);
16168
16314
  }
16169
16315
  if (mode === "review" && this.approvedLabel && hasLabel(cardLabels, this.approvedLabel)) {
16170
- log44.debug(TAG40, `Skipping #${card.short_id} — already has "${this.approvedLabel}" label`);
16316
+ log45.debug(TAG41, `Skipping #${card.short_id} — already has "${this.approvedLabel}" label`);
16171
16317
  continue;
16172
16318
  }
16173
16319
  if (mode === "review" && hasLabel(cardLabels, NEED_REVIEW_LABEL)) {
16174
- log44.debug(TAG40, `Skipping #${card.short_id} — has "${NEED_REVIEW_LABEL}" label (needs human)`);
16320
+ log45.debug(TAG41, `Skipping #${card.short_id} — has "${NEED_REVIEW_LABEL}" label (needs human)`);
16175
16321
  continue;
16176
16322
  }
16177
16323
  if (mode === "review" && !qualifiesForAutoReview(card.description)) {
16178
- log44.debug(TAG40, `Skipping #${card.short_id} — no branch or PR reference (not qualified for auto-review)`);
16324
+ log45.debug(TAG41, `Skipping #${card.short_id} — no branch or PR reference (not qualified for auto-review)`);
16179
16325
  continue;
16180
16326
  }
16181
16327
  if (accountHalt) {
16182
- log44.debug(TAG40, `Missed assignment: #${card.short_id} "${card.title}" (${mode}) — holding, account halted (${accountHalt})`);
16328
+ log45.debug(TAG41, `Missed assignment: #${card.short_id} "${card.title}" (${mode}) — holding, account halted (${accountHalt})`);
16183
16329
  } else {
16184
- log44.info(TAG40, `Missed assignment: #${card.short_id} "${card.title}" (${mode}) — enqueueing`);
16330
+ log45.info(TAG41, `Missed assignment: #${card.short_id} "${card.title}" (${mode}) — enqueueing`);
16185
16331
  }
16186
16332
  await this.pool.enqueue(card, column, cardLabels, subtasks, mode);
16187
16333
  }
@@ -16192,13 +16338,13 @@ class Reconciler {
16192
16338
  try {
16193
16339
  await this.pool.drainBudgetDecisions();
16194
16340
  } catch (err) {
16195
- log44.error(TAG40, `budget decisions were not drained this tick: ${err instanceof Error ? err.message : err}`);
16341
+ log45.error(TAG41, `budget decisions were not drained this tick: ${err instanceof Error ? err.message : err}`);
16196
16342
  }
16197
16343
  await this.recoverStrandedInProgress(cards, columns, knownCardIds);
16198
16344
  await this.recoverStrandedReview(cards, columns, labelMap, knownCardIds);
16199
16345
  for (const knownId of knownCardIds) {
16200
16346
  if (!allAgentCardIds.has(knownId)) {
16201
- log44.info(TAG40, `Missed unassign: ${knownId} — removing`);
16347
+ log45.info(TAG41, `Missed unassign: ${knownId} — removing`);
16202
16348
  await this.pool.removeCard(knownId);
16203
16349
  }
16204
16350
  }
@@ -16206,20 +16352,20 @@ class Reconciler {
16206
16352
  try {
16207
16353
  await this.drainSweepCommands();
16208
16354
  } catch (err) {
16209
- log44.error(TAG40, `sweep commands were not drained this tick: ${err instanceof Error ? err.message : err}`);
16355
+ log45.error(TAG41, `sweep commands were not drained this tick: ${err instanceof Error ? err.message : err}`);
16210
16356
  }
16211
16357
  try {
16212
16358
  await this.sweepForWork(cards, columns, labelMap, pickupConfig);
16213
16359
  } catch (err) {
16214
- log44.error(TAG40, `sweep failed this tick: ${err instanceof Error ? err.message : err}`);
16360
+ log45.error(TAG41, `sweep failed this tick: ${err instanceof Error ? err.message : err}`);
16215
16361
  }
16216
- log44.debug(TAG40, `Reconciled: ${assignedCards.length} assigned, ${knownCardIds.size} known`);
16362
+ log45.debug(TAG41, `Reconciled: ${assignedCards.length} assigned, ${knownCardIds.size} known`);
16217
16363
  } catch (err) {
16218
- log44.error(TAG40, `Heartbeat failed: ${err instanceof Error ? err.message : err}`);
16364
+ log45.error(TAG41, `Heartbeat failed: ${err instanceof Error ? err.message : err}`);
16219
16365
  }
16220
16366
  }
16221
16367
  }
16222
- var TAG40 = "reconcile", SWEEP_NUDGE_COOLDOWN_MS = 15000;
16368
+ var TAG41 = "reconcile", SWEEP_NUDGE_COOLDOWN_MS = 15000;
16223
16369
  var init_reconcile = __esm(() => {
16224
16370
  init_board_helpers();
16225
16371
  init_recovery();
@@ -16338,7 +16484,7 @@ __export(exports_startup_banner, {
16338
16484
  createStartupBanner: () => createStartupBanner,
16339
16485
  contextSourceLine: () => contextSourceLine
16340
16486
  });
16341
- import { isPretty, log as log45 } from "@gethmy/harness";
16487
+ import { isPretty, log as log46 } from "@gethmy/harness";
16342
16488
  function createStartupBanner(config, version) {
16343
16489
  return isPretty() ? prettyBanner(config, version) : jsonBanner(config, version);
16344
16490
  }
@@ -16367,7 +16513,7 @@ function prettyBanner(config, version) {
16367
16513
  checks.push({ kind: "ok", message });
16368
16514
  },
16369
16515
  warn(message) {
16370
- log45.warn(TAG41, message);
16516
+ log46.warn(TAG42, message);
16371
16517
  checks.push({ kind: "warn", message: message.split(`
16372
16518
  `, 1)[0] });
16373
16519
  },
@@ -16393,31 +16539,31 @@ function prettyBanner(config, version) {
16393
16539
  };
16394
16540
  }
16395
16541
  function jsonBanner(config, version) {
16396
- log45.info(TAG41, `Harmony Agent Daemon v${version} starting...`);
16397
- log45.info(TAG41, `Project: ${config.projectId} | Pool: ${config.agent.poolSize} | Runner: ${config.agent.runner} | Pickup: ${config.agent.pickupColumns.join(", ")}`);
16398
- log45.info(TAG41, `Config: ${contextSourceLine(config)}`);
16542
+ log46.info(TAG42, `Harmony Agent Daemon v${version} starting...`);
16543
+ log46.info(TAG42, `Project: ${config.projectId} | Pool: ${config.agent.poolSize} | Runner: ${config.agent.runner} | Pickup: ${config.agent.pickupColumns.join(", ")}`);
16544
+ log46.info(TAG42, `Config: ${contextSourceLine(config)}`);
16399
16545
  if (config.agent.review.enabled) {
16400
- log45.info(TAG41, `Review: enabled | Columns: ${config.agent.review.pickupColumns.join(", ")} | → ${config.agent.review.moveToColumn} / ${config.agent.review.failColumn}`);
16546
+ log46.info(TAG42, `Review: enabled | Columns: ${config.agent.review.pickupColumns.join(", ")} | → ${config.agent.review.moveToColumn} / ${config.agent.review.failColumn}`);
16401
16547
  }
16402
16548
  let failed = false;
16403
16549
  let modelLogged = false;
16404
16550
  return {
16405
16551
  setProjectName(_name) {},
16406
16552
  setGitProvider(provider) {
16407
- log45.info(TAG41, `Git provider: ${provider}`);
16553
+ log46.info(TAG42, `Git provider: ${provider}`);
16408
16554
  },
16409
16555
  setHttpPort(port) {
16410
- log45.info(TAG41, `HTTP server on port ${port}`);
16556
+ log46.info(TAG42, `HTTP server on port ${port}`);
16411
16557
  },
16412
16558
  setModel(resolved) {
16413
16559
  modelLogged = true;
16414
- log45.info(TAG41, `Model: ${startupModelLine(resolved)}`);
16560
+ log46.info(TAG42, `Model: ${startupModelLine(resolved)}`);
16415
16561
  },
16416
16562
  check(message) {
16417
- log45.info(TAG41, message);
16563
+ log46.info(TAG42, message);
16418
16564
  },
16419
16565
  warn(message) {
16420
- log45.warn(TAG41, message);
16566
+ log46.warn(TAG42, message);
16421
16567
  },
16422
16568
  fail() {
16423
16569
  failed = true;
@@ -16426,8 +16572,8 @@ function jsonBanner(config, version) {
16426
16572
  if (failed)
16427
16573
  return;
16428
16574
  if (!modelLogged)
16429
- log45.info(TAG41, `Model: ${localModelLine(config)}`);
16430
- log45.info(TAG41, message);
16575
+ log46.info(TAG42, `Model: ${localModelLine(config)}`);
16576
+ log46.info(TAG42, message);
16431
16577
  }
16432
16578
  };
16433
16579
  }
@@ -16523,7 +16669,7 @@ function cyan(s) {
16523
16669
  function yellow(s) {
16524
16670
  return `${ANSI.yellow}${s}${ANSI.reset}`;
16525
16671
  }
16526
- var TAG41 = "daemon", RULE_WIDTH = 70, ANSI;
16672
+ var TAG42 = "daemon", RULE_WIDTH = 70, ANSI;
16527
16673
  var init_startup_banner = __esm(() => {
16528
16674
  init_port_registry();
16529
16675
  init_startup_model();
@@ -16545,7 +16691,7 @@ import {
16545
16691
  writeFileSync as writeFileSync5
16546
16692
  } from "node:fs";
16547
16693
  import { dirname as dirname5, resolve as resolve2, sep as sep2 } from "node:path";
16548
- import { log as log46 } from "@gethmy/harness";
16694
+ import { log as log47 } from "@gethmy/harness";
16549
16695
  function migrationMarkerPath(targetPath) {
16550
16696
  return `${targetPath}.migrated`;
16551
16697
  }
@@ -16619,7 +16765,7 @@ function migrateLegacyStateFile(opts) {
16619
16765
  throw new Error("not an object");
16620
16766
  legacy = parsed;
16621
16767
  } catch (err) {
16622
- log46.warn(TAG42, `legacy state file unreadable — skipping the split this start; it retries on the next (${err instanceof Error ? err.message : err})`);
16768
+ log47.warn(TAG43, `legacy state file unreadable — skipping the split this start; it retries on the next (${err instanceof Error ? err.message : err})`);
16623
16769
  return NO_MIGRATION;
16624
16770
  }
16625
16771
  const runs = Array.isArray(legacy.runs) ? legacy.runs : [];
@@ -16641,13 +16787,13 @@ function migrateLegacyStateFile(opts) {
16641
16787
  const legacyVersion = typeof legacy.version === "number" ? legacy.version : SCHEMA_VERSION;
16642
16788
  const existing = existsSync5(targetPath) ? readTargetFile(targetPath) : null;
16643
16789
  if (existsSync5(targetPath) && !existing) {
16644
- log46.warn(TAG42, "existing project state file unreadable — the split overwrites it");
16790
+ log47.warn(TAG43, "existing project state file unreadable — the split overwrites it");
16645
16791
  }
16646
16792
  let target;
16647
16793
  if (existing) {
16648
16794
  const existingVersion = typeof existing.version === "number" ? existing.version : SCHEMA_VERSION;
16649
16795
  if (existingVersion !== legacyVersion) {
16650
- log46.warn(TAG42, `legacy state is schema v${legacyVersion} but the project store is v${existingVersion} — skipping the split`);
16796
+ log47.warn(TAG43, `legacy state is schema v${legacyVersion} but the project store is v${existingVersion} — skipping the split`);
16651
16797
  return NO_MIGRATION;
16652
16798
  }
16653
16799
  const existingRuns = Array.isArray(existing.runs) ? existing.runs : [];
@@ -16693,7 +16839,7 @@ function migrateLegacyStateFile(opts) {
16693
16839
  if (!existsSync5(backupPath))
16694
16840
  copyFileSync(legacyPath, backupPath);
16695
16841
  } catch (err) {
16696
- log46.warn(TAG42, `could not back up the legacy state file (continuing): ${err instanceof Error ? err.message : err}`);
16842
+ log47.warn(TAG43, `could not back up the legacy state file (continuing): ${err instanceof Error ? err.message : err}`);
16697
16843
  }
16698
16844
  const remainingRuns = runs.filter((r) => !ownRunIds.has(r.runId));
16699
16845
  const remainingCards = cards.filter((c) => !ownCardIds.has(c.cardId));
@@ -16717,10 +16863,10 @@ function migrateLegacyStateFile(opts) {
16717
16863
  copiedIndeterminateCards: indeterminateCards.length,
16718
16864
  foreignUnendedRuns: remainingRuns.filter((r) => r.endedAt === null).length
16719
16865
  };
16720
- log46.info(TAG42, `split legacy state into ${targetPath}: moved ${result.movedRuns} run(s) ` + `(${result.movedUnendedRuns} unended) and ${result.movedCards} card record(s), ` + `copied ${result.copiedIndeterminateCards} unattributable card record(s); ` + `left ${remainingRuns.length} run(s) (${result.foreignUnendedRuns} unended) for other projects`);
16866
+ log47.info(TAG43, `split legacy state into ${targetPath}: moved ${result.movedRuns} run(s) ` + `(${result.movedUnendedRuns} unended) and ${result.movedCards} card record(s), ` + `copied ${result.copiedIndeterminateCards} unattributable card record(s); ` + `left ${remainingRuns.length} run(s) (${result.foreignUnendedRuns} unended) for other projects`);
16721
16867
  return result;
16722
16868
  }
16723
- var TAG42 = "state-migration", NO_MIGRATION;
16869
+ var TAG43 = "state-migration", NO_MIGRATION;
16724
16870
  var init_state_migration = __esm(() => {
16725
16871
  init_src();
16726
16872
  init_state_store();
@@ -16827,7 +16973,7 @@ var init_stream_parser_selftest = __esm(() => {
16827
16973
 
16828
16974
  // src/watcher.ts
16829
16975
  import { randomUUID as randomUUID3 } from "node:crypto";
16830
- import { isPretty as isPretty2, log as log47 } from "@gethmy/harness";
16976
+ import { isPretty as isPretty2, log as log48 } from "@gethmy/harness";
16831
16977
  import { createClient } from "@supabase/supabase-js";
16832
16978
  function toPresenceColumns(names) {
16833
16979
  return names.map((n) => n.trim()).filter((n) => n.length > 0).slice(0, MAX_PRESENCE_COLUMNS).map((n) => n.slice(0, MAX_PRESENCE_COLUMN_LENGTH));
@@ -16888,7 +17034,7 @@ class Watcher {
16888
17034
  const stillHeld = this.supabase?.getChannels().some((c) => c.topic === old.topic) ?? false;
16889
17035
  if (!stillHeld)
16890
17036
  return true;
16891
- log47.warn(TAG43, `removeChannel(${old.topic}) returned "${status}" and the client still holds that topic — retrying the removal`);
17037
+ log48.warn(TAG44, `removeChannel(${old.topic}) returned "${status}" and the client still holds that topic — retrying the removal`);
16892
17038
  return false;
16893
17039
  }
16894
17040
  constructor(credentials, projectId, identity, routing, onCardBroadcast, onAgentCommand, onSweepCommandNudge) {
@@ -16902,7 +17048,7 @@ class Watcher {
16902
17048
  }
16903
17049
  async start() {
16904
17050
  if (!isPretty2()) {
16905
- log47.info(TAG43, "Connecting to Supabase realtime (broadcast)...");
17051
+ log48.info(TAG44, "Connecting to Supabase realtime (broadcast)...");
16906
17052
  }
16907
17053
  this.supabase = createClient(this.credentials.supabaseUrl, this.credentials.supabaseAnonKey);
16908
17054
  this.subscribeBroadcast();
@@ -16940,7 +17086,7 @@ class Watcher {
16940
17086
  status = `threw (${String(err)})`;
16941
17087
  }
16942
17088
  if (status !== "ok") {
16943
- log47.debug(TAG43, `Presence sweep update returned "${status}"`);
17089
+ log48.debug(TAG44, `Presence sweep update returned "${status}"`);
16944
17090
  }
16945
17091
  })();
16946
17092
  }
@@ -16951,7 +17097,7 @@ class Watcher {
16951
17097
  const presenceChannel = this.supabase.channel(`board-presence-${this.projectId}`);
16952
17098
  this.presenceChannel = presenceChannel;
16953
17099
  presenceChannel.on("presence", { event: "sync" }, () => {
16954
- log47.debug(TAG43, "Presence sync");
17100
+ log48.debug(TAG44, "Presence sync");
16955
17101
  }).subscribe(async (status) => {
16956
17102
  if (gen !== this.presenceGen)
16957
17103
  return;
@@ -16967,13 +17113,13 @@ class Watcher {
16967
17113
  if (trackStatus !== "ok") {
16968
17114
  this.presenceTracked = false;
16969
17115
  if (!this.stopping) {
16970
- log47.warn(TAG43, `Presence track returned "${trackStatus}" — scheduling reconnect`);
17116
+ log48.warn(TAG44, `Presence track returned "${trackStatus}" — scheduling reconnect`);
16971
17117
  this.schedulePresenceReconnect();
16972
17118
  }
16973
17119
  return;
16974
17120
  }
16975
17121
  if (!isPretty2() || !this.suppressStartupLogs) {
16976
- log47.info(TAG43, "Presence tracked on board-presence channel");
17122
+ log48.info(TAG44, "Presence tracked on board-presence channel");
16977
17123
  }
16978
17124
  this.presenceTracked = true;
16979
17125
  this.presenceReconnectAttempts = 0;
@@ -16981,7 +17127,7 @@ class Watcher {
16981
17127
  } else if (status === "CHANNEL_ERROR" || status === "TIMED_OUT" || status === "CLOSED") {
16982
17128
  this.presenceTracked = false;
16983
17129
  if (!this.stopping) {
16984
- log47.warn(TAG43, `Presence subscription ${status} — scheduling reconnect`);
17130
+ log48.warn(TAG44, `Presence subscription ${status} — scheduling reconnect`);
16985
17131
  this.schedulePresenceReconnect();
16986
17132
  }
16987
17133
  }
@@ -17000,7 +17146,7 @@ class Watcher {
17000
17146
  async reconnectPresence() {
17001
17147
  if (this.stopping || !this.supabase)
17002
17148
  return;
17003
- log47.warn(TAG43, `Reconnecting presence subscription (attempt ${this.presenceReconnectAttempts})`);
17149
+ log48.warn(TAG44, `Reconnecting presence subscription (attempt ${this.presenceReconnectAttempts})`);
17004
17150
  if (this.presenceChannel) {
17005
17151
  const old = this.presenceChannel;
17006
17152
  this.presenceChannel = null;
@@ -17023,13 +17169,13 @@ class Watcher {
17023
17169
  return;
17024
17170
  const gen = ++this.broadcastGen;
17025
17171
  this.channel = this.supabase.channel(`board-${this.projectId}`).on("broadcast", { event: "card_update" }, (msg) => {
17026
- log47.debug(TAG43, `Broadcast: card_update ${JSON.stringify(msg.payload)}`);
17172
+ log48.debug(TAG44, `Broadcast: card_update ${JSON.stringify(msg.payload)}`);
17027
17173
  this.onCardBroadcast({
17028
17174
  event: "card_update",
17029
17175
  payload: msg.payload ?? {}
17030
17176
  });
17031
17177
  }).on("broadcast", { event: "card_created" }, (msg) => {
17032
- log47.debug(TAG43, `Broadcast: card_created ${JSON.stringify(msg.payload)}`);
17178
+ log48.debug(TAG44, `Broadcast: card_created ${JSON.stringify(msg.payload)}`);
17033
17179
  this.onCardBroadcast({
17034
17180
  event: "card_created",
17035
17181
  payload: msg.payload ?? {}
@@ -17039,11 +17185,11 @@ class Watcher {
17039
17185
  const cardId = payload.card_id;
17040
17186
  const command = payload.command;
17041
17187
  if (cardId && command) {
17042
- log47.info(TAG43, `Broadcast: agent_command ${command} for ${cardId}`);
17188
+ log48.info(TAG44, `Broadcast: agent_command ${command} for ${cardId}`);
17043
17189
  this.onAgentCommand?.({ cardId, command });
17044
17190
  }
17045
17191
  }).on("broadcast", { event: "sweep_command" }, () => {
17046
- log47.debug(TAG43, "Broadcast: sweep_command — draining");
17192
+ log48.debug(TAG44, "Broadcast: sweep_command — draining");
17047
17193
  this.onSweepCommandNudge?.();
17048
17194
  }).subscribe((status) => {
17049
17195
  if (gen !== this.broadcastGen)
@@ -17052,13 +17198,13 @@ class Watcher {
17052
17198
  this.connected = true;
17053
17199
  this.reconnectAttempts = 0;
17054
17200
  if (!isPretty2() || !this.suppressStartupLogs) {
17055
- log47.info(TAG43, "Broadcast subscription active");
17201
+ log48.info(TAG44, "Broadcast subscription active");
17056
17202
  }
17057
17203
  this.maybeResolveReady();
17058
17204
  } else if (status === "CHANNEL_ERROR" || status === "TIMED_OUT" || status === "CLOSED") {
17059
17205
  this.connected = false;
17060
17206
  if (!this.stopping) {
17061
- log47.warn(TAG43, `Broadcast subscription ${status} — scheduling reconnect`);
17207
+ log48.warn(TAG44, `Broadcast subscription ${status} — scheduling reconnect`);
17062
17208
  this.scheduleReconnect();
17063
17209
  }
17064
17210
  }
@@ -17077,7 +17223,7 @@ class Watcher {
17077
17223
  async reconnectBroadcast() {
17078
17224
  if (this.stopping || !this.supabase)
17079
17225
  return;
17080
- log47.warn(TAG43, `Reconnecting broadcast subscription (attempt ${this.reconnectAttempts})`);
17226
+ log48.warn(TAG44, `Reconnecting broadcast subscription (attempt ${this.reconnectAttempts})`);
17081
17227
  if (this.channel) {
17082
17228
  const old = this.channel;
17083
17229
  this.channel = null;
@@ -17119,10 +17265,10 @@ class Watcher {
17119
17265
  }
17120
17266
  this.connected = false;
17121
17267
  this.presenceTracked = false;
17122
- log47.info(TAG43, "Broadcast subscription stopped");
17268
+ log48.info(TAG44, "Broadcast subscription stopped");
17123
17269
  }
17124
17270
  }
17125
- var TAG43 = "watcher", MAX_PRESENCE_COLUMNS = 24, MAX_PRESENCE_COLUMN_LENGTH = 120;
17271
+ var TAG44 = "watcher", MAX_PRESENCE_COLUMNS = 24, MAX_PRESENCE_COLUMN_LENGTH = 120;
17126
17272
  var init_watcher = __esm(() => {
17127
17273
  init_sweep_guard();
17128
17274
  });
@@ -17396,7 +17542,7 @@ __export(exports_run_gc, {
17396
17542
  pruneRunLogs: () => pruneRunLogs
17397
17543
  });
17398
17544
  import { unlinkSync as unlinkSync2 } from "node:fs";
17399
- import { log as log48 } from "@gethmy/harness";
17545
+ import { log as log49 } from "@gethmy/harness";
17400
17546
  function pruneRunLogs(dir, beforeMs, keepRunIds = new Set) {
17401
17547
  let deleted = 0;
17402
17548
  let bytesFreed = 0;
@@ -17410,7 +17556,7 @@ function pruneRunLogs(dir, beforeMs, keepRunIds = new Set) {
17410
17556
  deleted++;
17411
17557
  bytesFreed += file.sizeBytes;
17412
17558
  } catch (err) {
17413
- log48.debug(TAG44, `Could not remove ${file.path}: ${err instanceof Error ? err.message : err}`);
17559
+ log49.debug(TAG45, `Could not remove ${file.path}: ${err instanceof Error ? err.message : err}`);
17414
17560
  }
17415
17561
  }
17416
17562
  return { deleted, bytesFreed };
@@ -17433,7 +17579,7 @@ async function runRetentionGc(store, opts) {
17433
17579
  logs = pruneRunLogs(opts.runsDirectory ?? runsDir(), now - opts.runLogDays * DAY_MS2, openRunIds);
17434
17580
  }
17435
17581
  if (runRecordsPurged > 0 || logs.deleted > 0) {
17436
- log48.info(TAG44, `Retention sweep: trimmed ${runRecordsPurged} run record(s), removed ${logs.deleted} run log(s) (${Math.round(logs.bytesFreed / 1024)} KB)`);
17582
+ log49.info(TAG45, `Retention sweep: trimmed ${runRecordsPurged} run record(s), removed ${logs.deleted} run log(s) (${Math.round(logs.bytesFreed / 1024)} KB)`);
17437
17583
  }
17438
17584
  return {
17439
17585
  runRecordsPurged,
@@ -17441,7 +17587,7 @@ async function runRetentionGc(store, opts) {
17441
17587
  logBytesFreed: logs.bytesFreed
17442
17588
  };
17443
17589
  }
17444
- var TAG44 = "gc", DAY_MS2 = 86400000;
17590
+ var TAG45 = "gc", DAY_MS2 = 86400000;
17445
17591
  var init_run_gc = __esm(() => {
17446
17592
  init_paths();
17447
17593
  init_run_stats();
@@ -17458,7 +17604,7 @@ __export(exports_worktree_gc, {
17458
17604
  import { execFileSync as execFileSync8 } from "node:child_process";
17459
17605
  import { existsSync as existsSync6, readdirSync as readdirSync5, statSync as statSync5 } from "node:fs";
17460
17606
  import { resolve as resolve3 } from "node:path";
17461
- import { cleanupWorktree as cleanupWorktree5, GIT_NO_HOOKS as GIT_NO_HOOKS9, log as log49 } from "@gethmy/harness";
17607
+ import { cleanupWorktree as cleanupWorktree5, GIT_NO_HOOKS as GIT_NO_HOOKS9, log as log50 } from "@gethmy/harness";
17462
17608
  function isTransientGitNetworkError(message) {
17463
17609
  return TRANSIENT_GIT_NETWORK_ERROR.test(message);
17464
17610
  }
@@ -17571,10 +17717,10 @@ function runWorktreeGc(basePath, store, opts = {}) {
17571
17717
  });
17572
17718
  } catch {}
17573
17719
  if (result.removed.length > 0) {
17574
- log49.info(TAG45, `GC removed ${result.removed.length} orphan worktree(s): ${result.removed.map((p) => p.split("/").pop()).join(", ")}`);
17720
+ log50.info(TAG46, `GC removed ${result.removed.length} orphan worktree(s): ${result.removed.map((p) => p.split("/").pop()).join(", ")}`);
17575
17721
  }
17576
17722
  if (result.errors.length > 0) {
17577
- log49.warn(TAG45, `GC had ${result.errors.length} error(s): ${result.errors.map((e) => `${e.path}: ${e.error}`).join("; ")}`);
17723
+ log50.warn(TAG46, `GC had ${result.errors.length} error(s): ${result.errors.map((e) => `${e.path}: ${e.error}`).join("; ")}`);
17578
17724
  }
17579
17725
  return result;
17580
17726
  }
@@ -17604,7 +17750,7 @@ function pruneFailedRemoteBranches(opts) {
17604
17750
  } catch (err) {
17605
17751
  const detail = gitErrorDetail2(err);
17606
17752
  if (isTransientGitNetworkError(detail)) {
17607
- log49.debug(TAG45, `Remote branch GC skipped — remote unreachable: ${detail}`);
17753
+ log50.debug(TAG46, `Remote branch GC skipped — remote unreachable: ${detail}`);
17608
17754
  return result;
17609
17755
  }
17610
17756
  result.errors.push({ ref: "fetch", error: detail });
@@ -17644,7 +17790,7 @@ function pruneFailedRemoteBranches(opts) {
17644
17790
  continue;
17645
17791
  }
17646
17792
  if (clock() > sweepDeadline) {
17647
- log49.debug(TAG45, `Remote branch GC budget spent — removed ${result.removed.length}, remaining deferred to next tick`);
17793
+ log50.debug(TAG46, `Remote branch GC budget spent — removed ${result.removed.length}, remaining deferred to next tick`);
17648
17794
  break;
17649
17795
  }
17650
17796
  try {
@@ -17657,17 +17803,17 @@ function pruneFailedRemoteBranches(opts) {
17657
17803
  } catch (err) {
17658
17804
  const detail = gitErrorDetail2(err);
17659
17805
  if (isTransientGitNetworkError(detail)) {
17660
- log49.debug(TAG45, `Remote branch GC interrupted — remote unreachable: ${detail}`);
17806
+ log50.debug(TAG46, `Remote branch GC interrupted — remote unreachable: ${detail}`);
17661
17807
  break;
17662
17808
  }
17663
17809
  result.errors.push({ ref, error: detail });
17664
17810
  }
17665
17811
  }
17666
17812
  if (result.removed.length > 0) {
17667
- log49.info(TAG45, `Pruned ${result.removed.length} stale remote branch(es) under ${opts.prefix}: ${result.removed.join(", ")}`);
17813
+ log50.info(TAG46, `Pruned ${result.removed.length} stale remote branch(es) under ${opts.prefix}: ${result.removed.join(", ")}`);
17668
17814
  }
17669
17815
  if (result.errors.length > 0) {
17670
- log49.warn(TAG45, `Remote branch GC had ${result.errors.length} error(s): ${result.errors.map((e) => `${e.ref}: ${e.error}`).join("; ")}`);
17816
+ log50.warn(TAG46, `Remote branch GC had ${result.errors.length} error(s): ${result.errors.map((e) => `${e.ref}: ${e.error}`).join("; ")}`);
17671
17817
  }
17672
17818
  return result;
17673
17819
  }
@@ -17700,18 +17846,18 @@ class WorktreeGc {
17700
17846
  try {
17701
17847
  runWorktreeGc(this.basePath, this.store);
17702
17848
  } catch (err) {
17703
- log49.warn(TAG45, `GC tick failed: ${err instanceof Error ? err.message : err}`);
17849
+ log50.warn(TAG46, `GC tick failed: ${err instanceof Error ? err.message : err}`);
17704
17850
  }
17705
17851
  if (this.remoteOpts) {
17706
17852
  try {
17707
17853
  pruneFailedRemoteBranches(this.remoteOpts);
17708
17854
  } catch (err) {
17709
- log49.warn(TAG45, `Remote GC tick failed: ${err instanceof Error ? err.message : err}`);
17855
+ log50.warn(TAG46, `Remote GC tick failed: ${err instanceof Error ? err.message : err}`);
17710
17856
  }
17711
17857
  }
17712
17858
  if (this.retentionOpts) {
17713
17859
  runRetentionGc(this.store, this.retentionOpts).catch((err) => {
17714
- log49.warn(TAG45, `Retention GC tick failed: ${err instanceof Error ? err.message : err}`);
17860
+ log50.warn(TAG46, `Retention GC tick failed: ${err instanceof Error ? err.message : err}`);
17715
17861
  });
17716
17862
  }
17717
17863
  }
@@ -17725,7 +17871,7 @@ function getRepoRoot2() {
17725
17871
  return null;
17726
17872
  }
17727
17873
  }
17728
- var TAG45 = "worktree-gc", GIT_NETWORK_TIMEOUT_MS = 30000, GIT_SSH_CONNECT_TIMEOUT_SECS = 10, GIT_PRUNE_SWEEP_BUDGET_MS = 60000, GIT_NETWORK_EXEC, TRANSIENT_GIT_NETWORK_ERROR;
17874
+ var TAG46 = "worktree-gc", GIT_NETWORK_TIMEOUT_MS = 30000, GIT_SSH_CONNECT_TIMEOUT_SECS = 10, GIT_PRUNE_SWEEP_BUDGET_MS = 60000, GIT_NETWORK_EXEC, TRANSIENT_GIT_NETWORK_ERROR;
17729
17875
  var init_worktree_gc = __esm(() => {
17730
17876
  init_run_gc();
17731
17877
  GIT_NETWORK_EXEC = {
@@ -17766,7 +17912,7 @@ import {
17766
17912
  claudeVersion,
17767
17913
  detectGitProvider as detectGitProvider6,
17768
17914
  GIT_NO_HOOKS as GIT_NO_HOOKS10,
17769
- log as log50,
17915
+ log as log51,
17770
17916
  validateGitProviderCli
17771
17917
  } from "@gethmy/harness";
17772
17918
  async function validatePrerequisites(config, banner) {
@@ -17840,7 +17986,7 @@ async function main() {
17840
17986
  } catch (err) {
17841
17987
  if (err instanceof ConfigValidationError) {
17842
17988
  banner.fail();
17843
- log50.error(TAG46, err.message);
17989
+ log51.error(TAG47, err.message);
17844
17990
  process.exit(1);
17845
17991
  }
17846
17992
  throw err;
@@ -17858,7 +18004,7 @@ async function main() {
17858
18004
  } catch (err) {
17859
18005
  if (err instanceof ConfigValidationError) {
17860
18006
  banner.fail();
17861
- log50.error(TAG46, err.message);
18007
+ log51.error(TAG47, err.message);
17862
18008
  process.exit(1);
17863
18009
  }
17864
18010
  throw err;
@@ -17882,7 +18028,7 @@ async function main() {
17882
18028
  try {
17883
18029
  await client.reportAgentConfig(config.workspaceId, agentId, effectiveModelReport(config.agent));
17884
18030
  } catch (err) {
17885
- log50.warn(TAG46, `Could not report model config: ${err instanceof Error ? err.message : String(err)}`);
18031
+ log51.warn(TAG47, `Could not report model config: ${err instanceof Error ? err.message : String(err)}`);
17886
18032
  }
17887
18033
  const startupModel = await resolveStartupModel(config, client);
17888
18034
  banner.setModel(startupModel);
@@ -17897,7 +18043,7 @@ async function main() {
17897
18043
  worktreePrefix: resolve4(config.repoRoot, config.agent.worktree.basePath)
17898
18044
  });
17899
18045
  } catch (err) {
17900
- log50.error(TAG46, `legacy state split failed — starting with the project store as-is: ${err instanceof Error ? err.message : err}`);
18046
+ log51.error(TAG47, `legacy state split failed — starting with the project store as-is: ${err instanceof Error ? err.message : err}`);
17901
18047
  }
17902
18048
  const stateStore = StateStore.open({ projectId: config.projectId });
17903
18049
  const daemonId = randomUUID4();
@@ -18048,7 +18194,7 @@ async function main() {
18048
18194
  if (shuttingDown)
18049
18195
  return;
18050
18196
  shuttingDown = true;
18051
- log50.info(TAG46, `Received ${signal}, shutting down gracefully...`);
18197
+ log51.info(TAG47, `Received ${signal}, shutting down gracefully...`);
18052
18198
  reconciler.stop();
18053
18199
  mergeMonitor?.stop();
18054
18200
  worktreeGc.stop();
@@ -18059,19 +18205,19 @@ async function main() {
18059
18205
  }
18060
18206
  await watcher.stop();
18061
18207
  await pool.shutdown();
18062
- log50.info(TAG46, "Daemon stopped.");
18208
+ log51.info(TAG47, "Daemon stopped.");
18063
18209
  process.exit(exitCode);
18064
18210
  };
18065
18211
  process.on("SIGINT", () => shutdown("SIGINT"));
18066
18212
  process.on("SIGTERM", () => shutdown("SIGTERM"));
18067
18213
  process.on("SIGHUP", () => shutdown("SIGHUP"));
18068
18214
  process.on("uncaughtException", (err) => {
18069
- log50.error(TAG46, `Uncaught exception: ${err.message}`);
18215
+ log51.error(TAG47, `Uncaught exception: ${err.message}`);
18070
18216
  exitCode = 1;
18071
18217
  shutdown("uncaughtException");
18072
18218
  });
18073
18219
  process.on("unhandledRejection", (reason) => {
18074
- log50.error(TAG46, `Unhandled rejection: ${reason instanceof Error ? reason.message : String(reason)}`);
18220
+ log51.error(TAG47, `Unhandled rejection: ${reason instanceof Error ? reason.message : String(reason)}`);
18075
18221
  exitCode = 1;
18076
18222
  shutdown("unhandledRejection");
18077
18223
  });
@@ -18131,33 +18277,33 @@ async function handleBroadcast(event, client, pool, config, agentId) {
18131
18277
  if (assignedAgentId === undefined)
18132
18278
  return;
18133
18279
  if (assignedAgentId === agentId) {
18134
- log50.info(TAG46, `Broadcast: card ${cardId} assigned to agent`);
18280
+ log51.info(TAG47, `Broadcast: card ${cardId} assigned to agent`);
18135
18281
  try {
18136
18282
  await pool.resetAttemptsForReassign(cardId);
18137
18283
  await tryEnqueueCard(cardId, client, pool, config, agentId);
18138
18284
  } catch (err) {
18139
- log50.error(TAG46, `Failed to process assignment: ${err instanceof Error ? err.message : err}`);
18285
+ log51.error(TAG47, `Failed to process assignment: ${err instanceof Error ? err.message : err}`);
18140
18286
  }
18141
18287
  } else if (pool.isCardKnown(cardId)) {
18142
- log50.info(TAG46, `Broadcast: card ${cardId} unassigned from agent`);
18288
+ log51.info(TAG47, `Broadcast: card ${cardId} unassigned from agent`);
18143
18289
  await pool.removeCard(cardId);
18144
18290
  }
18145
18291
  }
18146
18292
  async function tryEnqueueCard(cardId, client, pool, config, agentId) {
18147
18293
  const { card } = await client.getCard(cardId);
18148
18294
  if (card.assigned_agent_id !== agentId) {
18149
- log50.debug(TAG46, `Card ${cardId} no longer assigned to agent — skipping`);
18295
+ log51.debug(TAG47, `Card ${cardId} no longer assigned to agent — skipping`);
18150
18296
  return;
18151
18297
  }
18152
18298
  if (card.done || card.archived_at) {
18153
- log50.debug(TAG46, `Card ${cardId} is finished — skipping`);
18299
+ log51.debug(TAG47, `Card ${cardId} is finished — skipping`);
18154
18300
  return;
18155
18301
  }
18156
18302
  const board = await client.getBoard(config.projectId, { summary: true });
18157
18303
  const columns = board.columns;
18158
18304
  const column = columns.find((c) => c.id === card.column_id);
18159
18305
  if (!column) {
18160
- log50.warn(TAG46, `Column not found for card ${cardId}`);
18306
+ log51.warn(TAG47, `Column not found for card ${cardId}`);
18161
18307
  return;
18162
18308
  }
18163
18309
  const route = classifyPickup(card, column.name, {
@@ -18166,31 +18312,31 @@ async function tryEnqueueCard(cardId, client, pool, config, agentId) {
18166
18312
  playbooks: config.agent.playbooks
18167
18313
  });
18168
18314
  if (!route) {
18169
- log50.info(TAG46, `Card #${card.short_id} is in "${column.name}", not a pickup/review/stage column — skipping`);
18315
+ log51.info(TAG47, `Card #${card.short_id} is in "${column.name}", not a pickup/review/stage column — skipping`);
18170
18316
  return;
18171
18317
  }
18172
18318
  if (route.stage) {
18173
- log50.info(TAG46, `Card #${card.short_id} is a playbook stage card (stage "${card.current_stage}") in "${column.name}" — routing to the stage executor (implement pool) regardless of column`);
18319
+ log51.info(TAG47, `Card #${card.short_id} is a playbook stage card (stage "${card.current_stage}") in "${column.name}" — routing to the stage executor (implement pool) regardless of column`);
18174
18320
  }
18175
18321
  const mode = route.mode;
18176
18322
  const labelMap = buildLabelMap(board.labels ?? []);
18177
18323
  const cardLabels = resolveCardLabels(card, labelMap);
18178
18324
  const subtasks = card.subtasks ?? [];
18179
18325
  if (mode === "review" && config.agent.review.approvedLabel && hasLabel(cardLabels, config.agent.review.approvedLabel)) {
18180
- log50.debug(TAG46, `Card #${card.short_id} already has "${config.agent.review.approvedLabel}" — skipping review`);
18326
+ log51.debug(TAG47, `Card #${card.short_id} already has "${config.agent.review.approvedLabel}" — skipping review`);
18181
18327
  return;
18182
18328
  }
18183
18329
  if (mode === "review" && hasLabel(cardLabels, NEED_REVIEW_LABEL)) {
18184
- log50.debug(TAG46, `Card #${card.short_id} has "${NEED_REVIEW_LABEL}" label (needs human) — skipping review`);
18330
+ log51.debug(TAG47, `Card #${card.short_id} has "${NEED_REVIEW_LABEL}" label (needs human) — skipping review`);
18185
18331
  return;
18186
18332
  }
18187
18333
  if (mode === "review" && !qualifiesForAutoReview(card.description)) {
18188
- log50.info(TAG46, `Card #${card.short_id} has no branch or PR reference — skipping auto-review`);
18334
+ log51.info(TAG47, `Card #${card.short_id} has no branch or PR reference — skipping auto-review`);
18189
18335
  return;
18190
18336
  }
18191
18337
  await pool.enqueue(card, column, cardLabels, subtasks, mode);
18192
18338
  }
18193
- var TAG46 = "daemon", BASE_REMOTE = "origin", PKG_VERSION;
18339
+ var TAG47 = "daemon", BASE_REMOTE = "origin", PKG_VERSION;
18194
18340
  var init_src2 = __esm(() => {
18195
18341
  init_account_guard();
18196
18342
  init_account_probe();