codex-orchestrator 2.0.5 → 2.0.6

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 (47) hide show
  1. package/CHANGELOG.md +27 -0
  2. package/README.md +1 -0
  3. package/dist/src/v2/acceptance-proof.d.ts +7 -0
  4. package/dist/src/v2/acceptance-proof.d.ts.map +1 -1
  5. package/dist/src/v2/acceptance-proof.js +111 -5
  6. package/dist/src/v2/acceptance-proof.js.map +1 -1
  7. package/dist/src/v2/adapters/command.d.ts +2 -0
  8. package/dist/src/v2/adapters/command.d.ts.map +1 -1
  9. package/dist/src/v2/adapters/command.js +67 -8
  10. package/dist/src/v2/adapters/command.js.map +1 -1
  11. package/dist/src/v2/adapters/gh-issue-adapter.js +16 -5
  12. package/dist/src/v2/adapters/gh-issue-adapter.js.map +1 -1
  13. package/dist/src/v2/android-proof-runner.d.ts +93 -0
  14. package/dist/src/v2/android-proof-runner.d.ts.map +1 -0
  15. package/dist/src/v2/android-proof-runner.js +921 -0
  16. package/dist/src/v2/android-proof-runner.js.map +1 -0
  17. package/dist/src/v2/config.d.ts +13 -0
  18. package/dist/src/v2/config.d.ts.map +1 -1
  19. package/dist/src/v2/config.js +57 -1
  20. package/dist/src/v2/config.js.map +1 -1
  21. package/dist/src/v2/immutable-workflow-publisher.js +6 -1
  22. package/dist/src/v2/immutable-workflow-publisher.js.map +1 -1
  23. package/dist/src/v2/mobile-lease.d.ts +9 -0
  24. package/dist/src/v2/mobile-lease.d.ts.map +1 -1
  25. package/dist/src/v2/mobile-lease.js +27 -3
  26. package/dist/src/v2/mobile-lease.js.map +1 -1
  27. package/dist/src/v2/run-issue.d.ts +4 -0
  28. package/dist/src/v2/run-issue.d.ts.map +1 -1
  29. package/dist/src/v2/run-issue.js +156 -44
  30. package/dist/src/v2/run-issue.js.map +1 -1
  31. package/dist/src/v2/run-store.d.ts +3 -0
  32. package/dist/src/v2/run-store.d.ts.map +1 -1
  33. package/dist/src/v2/run-store.js +12 -1
  34. package/dist/src/v2/run-store.js.map +1 -1
  35. package/dist/src/v2/runtime.d.ts.map +1 -1
  36. package/dist/src/v2/runtime.js +63 -10
  37. package/dist/src/v2/runtime.js.map +1 -1
  38. package/docs/deep-dive.md +10 -2
  39. package/internal-workflow/manifest.json +1 -1
  40. package/internal-workflow/skills/acceptance-proof/SKILL.md +1 -1
  41. package/internal-workflow/skills/acceptance-proof/references/android.md +6 -6
  42. package/internal-workflow/skills/implementation-spec-maker/SKILL.md +10 -8
  43. package/internal-workflow/skills/implementation-spec-review/SKILL.md +17 -3
  44. package/internal-workflow/skills/implementation-spec-review/evals/evals.json +30 -0
  45. package/internal-workflow/skills/implementation-spec-review/references/review-loop.md +30 -6
  46. package/package.json +1 -1
  47. package/internal-workflow/skills/acceptance-proof/tools/android-lease.mjs +0 -280
@@ -104,7 +104,7 @@ export class RunIssue {
104
104
  return await this.publicationDiverged(active, 'commit-tree-diverged');
105
105
  if (this.signal.aborted)
106
106
  return await this.terminal(await this.clearIntent(active), { status: 'cancelled' });
107
- if (!await this.authorized(issueNumber, runId, branchName, config))
107
+ if (!await this.authorized(active, config))
108
108
  return await this.revoked(active);
109
109
  try {
110
110
  commitSha = await this.dependencies.git.commit({ worktreePath, message: intent.message });
@@ -136,7 +136,7 @@ export class RunIssue {
136
136
  if (!remoteSha) {
137
137
  if (this.signal.aborted)
138
138
  return await this.terminal(await this.clearIntent(active), { status: 'cancelled' });
139
- if (!await this.authorized(issueNumber, runId, branchName, config))
139
+ if (!await this.authorized(active, config))
140
140
  return await this.revoked(active);
141
141
  try {
142
142
  await this.dependencies.git.push({ worktreePath, branchName });
@@ -170,7 +170,7 @@ export class RunIssue {
170
170
  if (!observed) {
171
171
  if (this.signal.aborted)
172
172
  return await this.terminal(await this.clearIntent(active), { status: 'cancelled' });
173
- if (!await this.authorized(issueNumber, runId, branchName, config))
173
+ if (!await this.authorized(active, config))
174
174
  return await this.revoked(active);
175
175
  try {
176
176
  await this.dependencies.pullRequests.createDraft({
@@ -210,7 +210,7 @@ export class RunIssue {
210
210
  if (matching.length === 0) {
211
211
  if (this.signal.aborted)
212
212
  return await this.terminal(await this.clearIntent(active), { status: 'cancelled' });
213
- if (!await this.authorized(issueNumber, runId, branchName, config))
213
+ if (!await this.authorized(active, config))
214
214
  return await this.revoked(active);
215
215
  try {
216
216
  await this.dependencies.issues.postComment(issueNumber, handoffBody);
@@ -236,7 +236,7 @@ export class RunIssue {
236
236
  if (!sameStrings(observation.labels, terminalLabels)) {
237
237
  if (this.signal.aborted)
238
238
  return await this.terminal(await this.clearIntent(active), { status: 'cancelled' });
239
- if (!await this.authorized(issueNumber, runId, branchName, config))
239
+ if (!await this.authorized(active, config))
240
240
  return await this.revoked(active);
241
241
  try {
242
242
  await this.dependencies.issues.setLabels(issueNumber, terminalLabels);
@@ -304,7 +304,7 @@ export class RunIssue {
304
304
  }
305
305
  head = await this.dependencies.git.getHead(active.record.worktreePath);
306
306
  if (head === intent.parentSha) {
307
- if (!await this.authorized(issueNumber, active.record.runId, active.record.branchName, config)) {
307
+ if (!await this.authorized(active, config)) {
308
308
  return this.blockReviewFeedback(active, 'safety', 'review-feedback-publication-authority-revoked');
309
309
  }
310
310
  const validation = await coordinator.revalidate({ batch, epoch: 'pre-update', expectedHeadSha: oldHead });
@@ -350,7 +350,7 @@ export class RunIssue {
350
350
  }
351
351
  remote = await this.dependencies.git.getRemoteBranchSha(active.record.worktreePath, active.record.branchName);
352
352
  if (remote === oldHead) {
353
- if (!await this.authorized(issueNumber, active.record.runId, active.record.branchName, config)) {
353
+ if (!await this.authorized(active, config)) {
354
354
  return this.blockReviewFeedback(active, 'safety', 'review-feedback-publication-authority-revoked');
355
355
  }
356
356
  const validation = await coordinator.revalidate({ batch, epoch: 'pre-update', expectedHeadSha: oldHead });
@@ -410,7 +410,7 @@ export class RunIssue {
410
410
  return this.blockReviewFeedback(active, 'safety', 'review-feedback-summary-diverged');
411
411
  }
412
412
  if (matches.length === 0) {
413
- if (!await this.authorized(issueNumber, active.record.runId, active.record.branchName, config)) {
413
+ if (!await this.authorized(active, config)) {
414
414
  return this.blockReviewFeedback(active, 'safety', 'review-feedback-publication-authority-revoked');
415
415
  }
416
416
  try {
@@ -459,7 +459,7 @@ export class RunIssue {
459
459
  return validationFailure;
460
460
  let issue = await this.readIssue(issueNumber);
461
461
  if (!issue || !sameStrings(issue.labels, finalLabels)) {
462
- if (!await this.authorized(issueNumber, active.record.runId, active.record.branchName, config)) {
462
+ if (!await this.authorized(active, config)) {
463
463
  return this.blockReviewFeedback(active, 'safety', 'review-feedback-publication-authority-revoked');
464
464
  }
465
465
  try {
@@ -546,7 +546,7 @@ export class RunIssue {
546
546
  }
547
547
  if (feedback.phase !== 'idle' || observed.status === 'none')
548
548
  return { result: publicOutcome(terminal) };
549
- if (!await this.authorizedForExactLabels(starting.record.issueNumber, starting.record.runId, starting.record.branchName, [config.github.labels.review.name])) {
549
+ if (!await this.authorizedForExactLabels(starting, [config.github.labels.review.name])) {
550
550
  return { result: publicOutcome(terminal) };
551
551
  }
552
552
  const projected = projectReviewFeedbackBatch(observed.batch, starting.record.directReview.targetRevision);
@@ -595,9 +595,9 @@ export class RunIssue {
595
595
  }
596
596
  const issue = await this.readIssue(active.record.issueNumber);
597
597
  const labelsAreRunning = !!issue && issue.state === 'OPEN' && sameStrings(issue.labels, runningLabels)
598
- && this.hasTrustedClaim(issue, active.record.runId, active.record.issueNumber, active.record.branchName);
598
+ && this.hasTrustedClaim(issue, active.record);
599
599
  const labelsAreReview = !!issue && issue.state === 'OPEN' && sameStrings(issue.labels, reviewLabels)
600
- && this.hasTrustedClaim(issue, active.record.runId, active.record.issueNumber, active.record.branchName);
600
+ && this.hasTrustedClaim(issue, active.record);
601
601
  if (!labelsAreRunning && !labelsAreReview) {
602
602
  return { result: await this.blockReviewFeedback(active, 'safety', 'review-feedback-activation-authority-revoked') };
603
603
  }
@@ -616,7 +616,7 @@ export class RunIssue {
616
616
  }
617
617
  const observed = await this.readIssue(active.record.issueNumber);
618
618
  if (!observed || !sameStrings(observed.labels, runningLabels)
619
- || !this.hasTrustedClaim(observed, active.record.runId, active.record.issueNumber, active.record.branchName)) {
619
+ || !this.hasTrustedClaim(observed, active.record)) {
620
620
  return { result: await this.blockReviewFeedback(active, 'safety', 'review-feedback-activation-labels-diverged') };
621
621
  }
622
622
  }
@@ -779,15 +779,22 @@ export class RunIssue {
779
779
  if ('result' in claim)
780
780
  return claim.result;
781
781
  active = claim.active;
782
- const worktree = await this.dependencies.git.inspectWorktree({ worktreePath, branchName, baseSha });
783
- if (worktree === 'diverged')
784
- return await this.terminal(active, { status: 'blocked', kind: 'safety', resumable: true }, 'claim-worktree-diverged');
782
+ let worktree;
783
+ try {
784
+ worktree = await this.dependencies.git.inspectWorktree({ worktreePath, branchName, baseSha });
785
+ }
786
+ catch (error) {
787
+ return await this.invokedFailure(active, 'local-git-worktree-inspection-failed', claimedWorktreeInspectionFailureSummary(error));
788
+ }
789
+ if (worktree === 'diverged') {
790
+ return await this.invokedFailure(active, 'local-git-worktree-diverged', 'The claimed worktree path is present but does not match the expected branch and base. Correct or preserve the local artifact, then retry the same run.');
791
+ }
785
792
  if (worktree === 'absent') {
786
793
  try {
787
794
  await this.dependencies.git.createWorktree({ targetRoot, worktreePath, branchName, baseBranch: config.github.baseBranch, baseSha });
788
795
  }
789
- catch {
790
- return await this.terminal(active, { status: 'internal-error', code: 'local-git-effect-failed' });
796
+ catch (error) {
797
+ return await this.invokedFailure(active, 'local-git-worktree-creation-failed', worktreeCreationFailureSummary(error));
791
798
  }
792
799
  }
793
800
  active = await this.initializeClaimedRun(active, issue);
@@ -815,7 +822,7 @@ export class RunIssue {
815
822
  frozenCriteria = structuredClone(active.record.frozenCriteria);
816
823
  }
817
824
  if (existing.lifecycle === 'spec-authoring') {
818
- if (!await this.authorized(input.issueNumber, runId, branchName, config))
825
+ if (!await this.authorized(active, config))
819
826
  return await this.revoked(active);
820
827
  return await this.continueSpecRequired(active);
821
828
  }
@@ -853,7 +860,7 @@ export class RunIssue {
853
860
  if (!['triaging', 'routed', 'implementing', 'reworking', 'checking', 'proving'].includes(active.record.lifecycle)) {
854
861
  return await this.terminal(active, { status: 'internal-error', code: 'resume-phase-not-reconciled' });
855
862
  }
856
- if (!await this.authorized(input.issueNumber, runId, branchName, config))
863
+ if (!await this.authorized(active, config))
857
864
  return await this.revoked(active);
858
865
  if (active.record.lifecycle !== 'triaging' && active.record.lifecycle !== 'routed') {
859
866
  if (!active.record.routeExecution || !active.record.routeReceipt)
@@ -902,8 +909,8 @@ export class RunIssue {
902
909
  try {
903
910
  await this.dependencies.git.createWorktree({ targetRoot, worktreePath, branchName, baseBranch: config.github.baseBranch, baseSha });
904
911
  }
905
- catch {
906
- return await this.terminal(active, { status: 'internal-error', code: 'local-git-effect-failed' });
912
+ catch (error) {
913
+ return await this.invokedFailure(active, 'local-git-worktree-creation-failed', worktreeCreationFailureSummary(error));
907
914
  }
908
915
  active = await this.initializeClaimedRun(active, issue);
909
916
  issueSnapshot = structuredClone(active.record.issueSnapshot);
@@ -921,7 +928,7 @@ export class RunIssue {
921
928
  return await this.terminal(active, { status: 'internal-error', code: 'direct-review-state-missing' });
922
929
  }
923
930
  attemptLoop: while (true) {
924
- if (!await this.authorized(input.issueNumber, runId, branchName, config)) {
931
+ if (!await this.authorized(active, config)) {
925
932
  return await this.terminal(active, { status: 'blocked', kind: 'safety', resumable: true });
926
933
  }
927
934
  if (this.signal.aborted)
@@ -1443,12 +1450,31 @@ export class RunIssue {
1443
1450
  catch {
1444
1451
  throw new RouteInitializationUnrecoverableError();
1445
1452
  }
1446
- const issueSnapshot = issue?.state === 'OPEN' ? snapshotIssue(issue) : active.record.issueSnapshot;
1453
+ const issueSnapshot = issue?.state === 'OPEN'
1454
+ ? refreshClaimedIssueSnapshot(active.record.issueSnapshot, issue)
1455
+ : active.record.issueSnapshot;
1447
1456
  return this.persist(active, { lifecycle: 'triaging', routeExecution: initialRouteExecution(), issueSnapshot });
1448
1457
  }
1449
1458
  async routeRun(starting, issue, frozenCriteria, worktreePath, config, issueNumber, branchName) {
1450
1459
  let active = starting;
1451
1460
  while (active.record.lifecycle === 'triaging') {
1461
+ let currentIssue;
1462
+ try {
1463
+ currentIssue = await this.readIssue(active.record.issueNumber);
1464
+ }
1465
+ catch (error) {
1466
+ if (error instanceof TransportReadError) {
1467
+ return { result: await this.invokedFailure(active, 'authorization-read-failed') };
1468
+ }
1469
+ throw error;
1470
+ }
1471
+ if (!currentIssue || !this.isAuthorizedIssue(currentIssue, active.record, config)) {
1472
+ return { result: await this.revoked(active) };
1473
+ }
1474
+ active = await this.persist(active, {
1475
+ issueSnapshot: refreshClaimedIssueSnapshot(active.record.issueSnapshot, currentIssue),
1476
+ });
1477
+ issue = structuredClone(active.record.issueSnapshot);
1452
1478
  const state = {
1453
1479
  read: async () => requireRouteExecution(active.record.routeExecution),
1454
1480
  compareAndSwap: async (expected, next) => {
@@ -1550,7 +1576,7 @@ export class RunIssue {
1550
1576
  return { result: await this.terminal(active, { status: 'internal-error', code: 'route-state-not-dispatchable' }) };
1551
1577
  }
1552
1578
  try {
1553
- if (!await this.authorized(issueNumber, active.record.runId, branchName, config)) {
1579
+ if (!await this.authorized(active, config)) {
1554
1580
  return { result: await this.revoked(active) };
1555
1581
  }
1556
1582
  }
@@ -1575,7 +1601,7 @@ export class RunIssue {
1575
1601
  if (receipt.route !== 'awaiting-user')
1576
1602
  active = await this.persist(active, { lifecycle });
1577
1603
  try {
1578
- if (!await this.authorized(issueNumber, active.record.runId, branchName, config)) {
1604
+ if (!await this.authorized(active, config)) {
1579
1605
  return { result: await this.revoked(active) };
1580
1606
  }
1581
1607
  }
@@ -1808,33 +1834,47 @@ export class RunIssue {
1808
1834
  throw new PostEffectStateError(active);
1809
1835
  }
1810
1836
  }
1811
- async authorized(issueNumber, runId, branchName, config) {
1812
- const issue = await this.readIssue(issueNumber);
1813
- if (!issue || issue.state !== 'OPEN')
1837
+ async authorized(active, config) {
1838
+ const issue = await this.readIssue(active.record.issueNumber);
1839
+ return !!issue && this.isAuthorizedIssue(issue, active.record, config);
1840
+ }
1841
+ isAuthorizedIssue(issue, record, config) {
1842
+ if (issue.state !== 'OPEN')
1814
1843
  return false;
1815
1844
  const labels = new Set(issue.labels);
1816
1845
  if (!labels.has(config.github.labels.auto.name) || !labels.has(config.github.labels.running.name))
1817
1846
  return false;
1818
- return this.hasTrustedClaim(issue, runId, issueNumber, branchName);
1847
+ return this.hasTrustedClaim(issue, record);
1819
1848
  }
1820
- async authorizedForExactLabels(issueNumber, runId, branchName, expectedLabels) {
1821
- const issue = await this.readIssue(issueNumber);
1849
+ async authorizedForExactLabels(active, expectedLabels) {
1850
+ const issue = await this.readIssue(active.record.issueNumber);
1822
1851
  return !!issue && issue.state === 'OPEN' && sameStrings(issue.labels, expectedLabels)
1823
- && this.hasTrustedClaim(issue, runId, issueNumber, branchName);
1852
+ && this.hasTrustedClaim(issue, active.record);
1824
1853
  }
1825
- hasTrustedClaim(issue, runId, issueNumber, branchName) {
1826
- const exactBody = claimComment(runId, issueNumber, branchName);
1827
- const markerPattern = /^<!-- codex-orchestrator:run:([0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}):claim -->$/u;
1828
- const markers = issue.comments.filter((comment) => markerPattern.test(comment.body.split('\n')[0] ?? ''));
1854
+ hasTrustedClaim(issue, record) {
1855
+ const exactBody = claimComment(record.runId, record.issueNumber, record.branchName);
1856
+ const markers = issue.comments.filter(isClaimMarkerComment);
1829
1857
  if (issue.comments.some((comment) => {
1830
1858
  const firstLine = comment.body.split('\n')[0] ?? '';
1831
- return firstLine.startsWith(`<!-- codex-orchestrator:run:${runId}:claim`) && !markerPattern.test(firstLine);
1859
+ return firstLine.startsWith(`<!-- codex-orchestrator:run:${record.runId}:claim`) && !claimMarkerPattern.test(firstLine);
1832
1860
  }))
1833
1861
  return false;
1834
- if (markers.some((comment) => (comment.body.split('\n')[0] ?? '').match(markerPattern)?.[1] !== runId))
1862
+ const currentMarkers = markers.filter((comment) => claimRunId(comment) === record.runId);
1863
+ if (currentMarkers.some((comment) => comment.body !== exactBody
1864
+ || !['OWNER', 'MEMBER', 'COLLABORATOR'].includes(comment.authorAssociation)))
1835
1865
  return false;
1836
- const current = markers.filter((comment) => comment.body === exactBody);
1837
- return current.length === 1 && ['OWNER', 'MEMBER', 'COLLABORATOR'].includes(current[0].authorAssociation);
1866
+ const historicalClaims = historicalClaimCounts(record);
1867
+ for (const comment of markers) {
1868
+ const markerRunId = claimRunId(comment);
1869
+ if (markerRunId === record.runId)
1870
+ continue;
1871
+ const key = observedHistoricalClaimKeys(comment, record)
1872
+ .find((candidate) => (historicalClaims.get(candidate) ?? 0) > 0);
1873
+ if (!key)
1874
+ return false;
1875
+ historicalClaims.set(key, historicalClaims.get(key) - 1);
1876
+ }
1877
+ return currentMarkers.length === 1;
1838
1878
  }
1839
1879
  async readIssue(issueNumber) {
1840
1880
  try {
@@ -1848,7 +1888,7 @@ export class RunIssue {
1848
1888
  const batch = active.record.reviewFeedback?.activeBatch;
1849
1889
  if (!batch)
1850
1890
  return undefined;
1851
- if (!await this.authorized(issueNumber, active.record.runId, active.record.branchName, config)) {
1891
+ if (!await this.authorized(active, config)) {
1852
1892
  return this.blockReviewFeedback(active, 'safety', 'review-feedback-worker-authority-revoked');
1853
1893
  }
1854
1894
  if (!this.dependencies.reviewFeedback) {
@@ -2204,8 +2244,8 @@ export class RunIssue {
2204
2244
  async revoked(active) {
2205
2245
  return this.terminal(await this.clearIntent(active), { status: 'blocked', kind: 'safety', resumable: true });
2206
2246
  }
2207
- async invokedFailure(active, code) {
2208
- const evidence = await this.dependencies.writeEvidence({ runId: active.record.runId, code, summary: 'Publication delivery requires reconciliation.' });
2247
+ async invokedFailure(active, code, summary = 'Publication delivery requires reconciliation.') {
2248
+ const evidence = await this.dependencies.writeEvidence({ runId: active.record.runId, code, summary });
2209
2249
  return { status: 'transport-failed', resumable: true, evidencePath: evidence.path };
2210
2250
  }
2211
2251
  async terminal(active, outcome, evidenceCode = outcome.status, retainIntent = false) {
@@ -2285,6 +2325,63 @@ class PostEffectStateError extends Error {
2285
2325
  this.active = active;
2286
2326
  }
2287
2327
  }
2328
+ const claimMarkerPattern = /^<!-- codex-orchestrator:run:([0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}):claim -->$/u;
2329
+ function isClaimMarkerComment(comment) {
2330
+ return claimMarkerPattern.test(comment.body.split('\n')[0] ?? '');
2331
+ }
2332
+ function claimRunId(comment) {
2333
+ return (comment.body.split('\n')[0] ?? '').match(claimMarkerPattern)?.[1];
2334
+ }
2335
+ function trustedHistoricalClaimBodyKey(comment, issueNumber, branchName) {
2336
+ const runId = claimRunId(comment);
2337
+ if (!runId || !['OWNER', 'MEMBER', 'COLLABORATOR'].includes(comment.authorAssociation))
2338
+ return undefined;
2339
+ if (comment.body !== claimComment(runId, issueNumber, branchName))
2340
+ return undefined;
2341
+ return `${comment.authorAssociation}\u0000${comment.body}`;
2342
+ }
2343
+ function frozenHistoricalClaimKey(comment, issueNumber, branchName) {
2344
+ const bodyKey = trustedHistoricalClaimBodyKey(comment, issueNumber, branchName);
2345
+ if (!bodyKey)
2346
+ return undefined;
2347
+ return comment.id ? `id\u0000${comment.id}\u0000${bodyKey}` : `legacy\u0000${bodyKey}`;
2348
+ }
2349
+ function observedHistoricalClaimKeys(comment, record) {
2350
+ const bodyKey = trustedHistoricalClaimBodyKey(comment, record.issueNumber, record.branchName);
2351
+ if (!bodyKey)
2352
+ return [];
2353
+ const keys = comment.id ? [`id\u0000${comment.id}\u0000${bodyKey}`] : [];
2354
+ if (timestampAtOrBefore(comment.createdAt, record.createdAt)
2355
+ && timestampAtOrBefore(comment.updatedAt, record.createdAt)) {
2356
+ keys.push(`legacy\u0000${bodyKey}`);
2357
+ }
2358
+ return keys;
2359
+ }
2360
+ function timestampAtOrBefore(value, cutoff) {
2361
+ if (!value)
2362
+ return false;
2363
+ const timestamp = Date.parse(value);
2364
+ return !Number.isNaN(timestamp) && timestamp <= Date.parse(cutoff);
2365
+ }
2366
+ function historicalClaimCounts(record) {
2367
+ const counts = new Map();
2368
+ for (const comment of record.issueSnapshot.comments ?? []) {
2369
+ const key = frozenHistoricalClaimKey(comment, record.issueNumber, record.branchName);
2370
+ if (!key || claimRunId(comment) === record.runId)
2371
+ continue;
2372
+ counts.set(key, (counts.get(key) ?? 0) + 1);
2373
+ }
2374
+ return counts;
2375
+ }
2376
+ function refreshClaimedIssueSnapshot(baseline, issue) {
2377
+ return {
2378
+ ...structuredClone(baseline),
2379
+ comments: [
2380
+ ...(baseline.comments ?? []).filter(isClaimMarkerComment),
2381
+ ...issue.comments.filter((comment) => !isClaimMarkerComment(comment)),
2382
+ ],
2383
+ };
2384
+ }
2288
2385
  function snapshotIssue(issue) {
2289
2386
  if (issue.state !== 'OPEN')
2290
2387
  throw new Error('cannot snapshot a closed issue');
@@ -2422,6 +2519,21 @@ function commentsWithMarker(issue, marker) {
2422
2519
  function sortedUnique(values) {
2423
2520
  return [...new Set(values)].sort();
2424
2521
  }
2522
+ function worktreeCreationFailureSummary(error) {
2523
+ return boundedLocalGitFailureSummary('Local Git worktree creation failed; correct the local Git state and retry the same run.', error);
2524
+ }
2525
+ function claimedWorktreeInspectionFailureSummary(error) {
2526
+ return boundedLocalGitFailureSummary('The claimed worktree could not be inspected; correct or preserve the local artifact and retry the same run.', error);
2527
+ }
2528
+ function boundedLocalGitFailureSummary(prefix, error) {
2529
+ const raw = error instanceof Error ? error.message : String(error);
2530
+ const diagnostic = raw
2531
+ .replace(/[\u0000-\u0008\u000B\u000C\u000E-\u001F\u007F]/gu, ' ')
2532
+ .trim();
2533
+ if (diagnostic.length === 0)
2534
+ return prefix;
2535
+ return `${prefix}\n${diagnostic.slice(0, 4 * 1024 - prefix.length - 1)}`;
2536
+ }
2425
2537
  function assertUuid(value) {
2426
2538
  if (typeof value !== 'string' || !/^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/u.test(value))
2427
2539
  throw new Error('runId is invalid');