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