github-issue-tower-defence-management 1.81.0 → 1.82.1

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 (46) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/README.md +4 -1
  3. package/bin/adapter/entry-points/cli/index.js +1 -0
  4. package/bin/adapter/entry-points/cli/index.js.map +1 -1
  5. package/bin/adapter/entry-points/cli/projectConfig.js +20 -0
  6. package/bin/adapter/entry-points/cli/projectConfig.js.map +1 -1
  7. package/bin/adapter/entry-points/handlers/HandleScheduledEventUseCaseHandler.js +48 -20
  8. package/bin/adapter/entry-points/handlers/HandleScheduledEventUseCaseHandler.js.map +1 -1
  9. package/bin/domain/usecases/ChangeTargetPullRequestApprover.js +9 -5
  10. package/bin/domain/usecases/ChangeTargetPullRequestApprover.js.map +1 -1
  11. package/bin/domain/usecases/HandleScheduledEventUseCase.js +1 -0
  12. package/bin/domain/usecases/HandleScheduledEventUseCase.js.map +1 -1
  13. package/bin/domain/usecases/IssueRejectionEvaluator.js +1 -1
  14. package/bin/domain/usecases/IssueRejectionEvaluator.js.map +1 -1
  15. package/bin/domain/usecases/NotifyFinishedIssuePreparationUseCase.js +1 -1
  16. package/bin/domain/usecases/NotifyFinishedIssuePreparationUseCase.js.map +1 -1
  17. package/bin/domain/usecases/RevertNotReadyReviewQueueIssueUseCase.js +1 -1
  18. package/bin/domain/usecases/RevertNotReadyReviewQueueIssueUseCase.js.map +1 -1
  19. package/bin/domain/usecases/StartPreparationUseCase.js +11 -4
  20. package/bin/domain/usecases/StartPreparationUseCase.js.map +1 -1
  21. package/package.json +1 -1
  22. package/src/adapter/entry-points/cli/index.test.ts +18 -0
  23. package/src/adapter/entry-points/cli/index.ts +1 -0
  24. package/src/adapter/entry-points/cli/projectConfig.ts +32 -0
  25. package/src/domain/usecases/ChangeTargetPullRequestApprover.test.ts +78 -0
  26. package/src/domain/usecases/ChangeTargetPullRequestApprover.ts +14 -4
  27. package/src/domain/usecases/HandleScheduledEventUseCase.ts +2 -0
  28. package/src/domain/usecases/IssueRejectionEvaluator.test.ts +18 -0
  29. package/src/domain/usecases/IssueRejectionEvaluator.ts +1 -1
  30. package/src/domain/usecases/NotifyFinishedIssuePreparationUseCase.test.ts +61 -0
  31. package/src/domain/usecases/NotifyFinishedIssuePreparationUseCase.ts +2 -0
  32. package/src/domain/usecases/RevertNotReadyReviewQueueIssueUseCase.test.ts +36 -0
  33. package/src/domain/usecases/RevertNotReadyReviewQueueIssueUseCase.ts +2 -0
  34. package/src/domain/usecases/StartPreparationUseCase.test.ts +316 -6
  35. package/src/domain/usecases/StartPreparationUseCase.ts +25 -6
  36. package/types/adapter/entry-points/cli/projectConfig.d.ts +1 -0
  37. package/types/adapter/entry-points/cli/projectConfig.d.ts.map +1 -1
  38. package/types/domain/usecases/ChangeTargetPullRequestApprover.d.ts +1 -1
  39. package/types/domain/usecases/ChangeTargetPullRequestApprover.d.ts.map +1 -1
  40. package/types/domain/usecases/HandleScheduledEventUseCase.d.ts +1 -0
  41. package/types/domain/usecases/HandleScheduledEventUseCase.d.ts.map +1 -1
  42. package/types/domain/usecases/NotifyFinishedIssuePreparationUseCase.d.ts +1 -0
  43. package/types/domain/usecases/NotifyFinishedIssuePreparationUseCase.d.ts.map +1 -1
  44. package/types/domain/usecases/RevertNotReadyReviewQueueIssueUseCase.d.ts +1 -0
  45. package/types/domain/usecases/RevertNotReadyReviewQueueIssueUseCase.d.ts.map +1 -1
  46. package/types/domain/usecases/StartPreparationUseCase.d.ts.map +1 -1
@@ -3013,5 +3013,66 @@ describe('NotifyFinishedIssuePreparationUseCase', () => {
3013
3013
  'https://github.com/user/repo/pull/1',
3014
3014
  );
3015
3015
  });
3016
+
3017
+ it('should normalize leading slashes in change-target label paths', async () => {
3018
+ setupApprovedPrScenario({ labels: ['change-target:/src/domain'] });
3019
+ mockIssueRepository.getPullRequestChangedFilePaths.mockResolvedValue([
3020
+ 'src/domain/entities/Foo.ts',
3021
+ ]);
3022
+
3023
+ await useCase.run({
3024
+ projectUrl: 'https://github.com/users/user/projects/1',
3025
+ issueUrl: 'https://github.com/user/repo/issues/1',
3026
+ thresholdForAutoReject: 3,
3027
+ workflowBlockerResolvedWebhookUrl: null,
3028
+ allowedIssueAuthors: null,
3029
+ });
3030
+
3031
+ expect(mockIssueRepository.approvePullRequest).toHaveBeenCalledWith(
3032
+ 'https://github.com/user/repo/pull/1',
3033
+ );
3034
+ });
3035
+
3036
+ it('should expand changeTargetPathAliases when alias matches a change-target label', async () => {
3037
+ setupApprovedPrScenario({ labels: ['change-target:adapters'] });
3038
+ mockIssueRepository.getPullRequestChangedFilePaths.mockResolvedValue([
3039
+ 'src/domain/usecases/adapter-interfaces/IssueRepository.ts',
3040
+ ]);
3041
+
3042
+ await useCase.run({
3043
+ projectUrl: 'https://github.com/users/user/projects/1',
3044
+ issueUrl: 'https://github.com/user/repo/issues/1',
3045
+ thresholdForAutoReject: 3,
3046
+ workflowBlockerResolvedWebhookUrl: null,
3047
+ allowedIssueAuthors: null,
3048
+ changeTargetPathAliases: {
3049
+ adapters: 'src/domain/usecases/adapter-interfaces',
3050
+ },
3051
+ });
3052
+
3053
+ expect(mockIssueRepository.approvePullRequest).toHaveBeenCalledWith(
3054
+ 'https://github.com/user/repo/pull/1',
3055
+ );
3056
+ });
3057
+
3058
+ it('should not approve when file is outside the alias-expanded path', async () => {
3059
+ setupApprovedPrScenario({ labels: ['change-target:adapters'] });
3060
+ mockIssueRepository.getPullRequestChangedFilePaths.mockResolvedValue([
3061
+ 'src/domain/usecases/SomeOtherUseCase.ts',
3062
+ ]);
3063
+
3064
+ await useCase.run({
3065
+ projectUrl: 'https://github.com/users/user/projects/1',
3066
+ issueUrl: 'https://github.com/user/repo/issues/1',
3067
+ thresholdForAutoReject: 3,
3068
+ workflowBlockerResolvedWebhookUrl: null,
3069
+ allowedIssueAuthors: null,
3070
+ changeTargetPathAliases: {
3071
+ adapters: 'src/domain/usecases/adapter-interfaces',
3072
+ },
3073
+ });
3074
+
3075
+ expect(mockIssueRepository.approvePullRequest).not.toHaveBeenCalled();
3076
+ });
3016
3077
  });
3017
3078
  });
@@ -78,6 +78,7 @@ export class NotifyFinishedIssuePreparationUseCase {
78
78
  workflowBlockerResolvedWebhookUrl: string | null;
79
79
  allowedIssueAuthors?: string[] | null;
80
80
  labelsAsLlmAgentName?: string[] | null;
81
+ changeTargetPathAliases?: Record<string, string> | null;
81
82
  }): Promise<void> => {
82
83
  const project = await this.projectRepository.getByUrl(params.projectUrl);
83
84
 
@@ -238,6 +239,7 @@ export class NotifyFinishedIssuePreparationUseCase {
238
239
  await this.changeTargetPullRequestApprover.approveIfConfined(
239
240
  issue.labels,
240
241
  approvedPrUrl,
242
+ params.changeTargetPathAliases,
241
243
  );
242
244
  issue.status = AWAITING_QUALITY_CHECK_STATUS_NAME;
243
245
  await this.issueRepository.update(issue, project);
@@ -762,6 +762,42 @@ describe('RevertNotReadyReviewQueueIssueUseCase', () => {
762
762
  'https://github.com/user/repo/pull/1',
763
763
  );
764
764
  });
765
+
766
+ it('should expand changeTargetPathAliases when alias matches a change-target label', async () => {
767
+ setupReadyIssue(['change-target:adapters']);
768
+ mockIssueRepository.getPullRequestChangedFilePaths.mockResolvedValue([
769
+ 'src/domain/usecases/adapter-interfaces/IssueRepository.ts',
770
+ ]);
771
+
772
+ await useCase.run({
773
+ projectUrl: 'https://github.com/users/user/projects/1',
774
+ allowIssueCacheMinutes: 10,
775
+ changeTargetPathAliases: {
776
+ adapters: 'src/domain/usecases/adapter-interfaces',
777
+ },
778
+ });
779
+
780
+ expect(mockIssueRepository.approvePullRequest).toHaveBeenCalledWith(
781
+ 'https://github.com/user/repo/pull/1',
782
+ );
783
+ });
784
+
785
+ it('should not approve when file is outside the alias-expanded path', async () => {
786
+ setupReadyIssue(['change-target:adapters']);
787
+ mockIssueRepository.getPullRequestChangedFilePaths.mockResolvedValue([
788
+ 'src/domain/usecases/SomeOtherUseCase.ts',
789
+ ]);
790
+
791
+ await useCase.run({
792
+ projectUrl: 'https://github.com/users/user/projects/1',
793
+ allowIssueCacheMinutes: 10,
794
+ changeTargetPathAliases: {
795
+ adapters: 'src/domain/usecases/adapter-interfaces',
796
+ },
797
+ });
798
+
799
+ expect(mockIssueRepository.approvePullRequest).not.toHaveBeenCalled();
800
+ });
765
801
  });
766
802
  });
767
803
 
@@ -44,6 +44,7 @@ export class RevertNotReadyReviewQueueIssueUseCase {
44
44
  projectUrl: string;
45
45
  allowIssueCacheMinutes: number;
46
46
  labelsAsLlmAgentName?: string[] | null;
47
+ changeTargetPathAliases?: Record<string, string> | null;
47
48
  }): Promise<void> => {
48
49
  const projectId = await this.projectRepository.findProjectIdByUrl(
49
50
  params.projectUrl,
@@ -103,6 +104,7 @@ export class RevertNotReadyReviewQueueIssueUseCase {
103
104
  await this.changeTargetPullRequestApprover.approveIfConfined(
104
105
  issue.labels,
105
106
  approvedPrUrl,
107
+ params.changeTargetPathAliases,
106
108
  );
107
109
  }
108
110
 
@@ -2551,6 +2551,232 @@ describe('StartPreparationUseCase', () => {
2551
2551
  ).toHaveLength(0);
2552
2552
  });
2553
2553
 
2554
+ it('should choose the sooner-7-day-reset token when two eligible tokens have equal remaining capacity', async () => {
2555
+ const awaitingIssue = createMockIssue({
2556
+ url: 'url1',
2557
+ title: 'Issue 1',
2558
+ labels: ['category:impl'],
2559
+ status: 'Awaiting Workspace',
2560
+ number: 1,
2561
+ itemId: 'item-1',
2562
+ });
2563
+ mockProjectRepository.getByUrl.mockResolvedValue(mockProject);
2564
+ mockIssueRepository.getStoryObjectMap.mockResolvedValue(
2565
+ createMockStoryObjectMap([awaitingIssue]),
2566
+ );
2567
+ mockLocalCommandRunner.runCommand.mockResolvedValue({
2568
+ stdout: '',
2569
+ stderr: '',
2570
+ exitCode: 0,
2571
+ });
2572
+ const nowEpochSeconds = Math.floor(Date.now() / 1000);
2573
+ mockClaudeTokenUsageRepository.getAvailableTokenUsages.mockResolvedValue([
2574
+ {
2575
+ name: 'token-far-reset',
2576
+ token: 'token-far-reset',
2577
+ fiveHourUtilization: 0.1,
2578
+ sevenDayUtilization: 0.1,
2579
+ blocked: false,
2580
+ rejected: false,
2581
+ blockedUntilEpoch: 0,
2582
+ modelWeeklyLimits: {
2583
+ seven_day_opus: {
2584
+ rejected: false,
2585
+ resetsAt: nowEpochSeconds + 100 * 3600,
2586
+ },
2587
+ },
2588
+ },
2589
+ {
2590
+ name: 'token-soon-reset',
2591
+ token: 'token-soon-reset',
2592
+ fiveHourUtilization: 0.1,
2593
+ sevenDayUtilization: 0.1,
2594
+ blocked: false,
2595
+ rejected: false,
2596
+ blockedUntilEpoch: 0,
2597
+ modelWeeklyLimits: {
2598
+ seven_day_opus: {
2599
+ rejected: false,
2600
+ resetsAt: nowEpochSeconds + 20 * 3600,
2601
+ },
2602
+ },
2603
+ },
2604
+ ]);
2605
+
2606
+ await useCase.run({
2607
+ projectUrl: 'https://github.com/user/repo',
2608
+ defaultAgentName: 'agent1',
2609
+ defaultLlmModelName: 'claude-opus',
2610
+ fallbackLlmModelName: null,
2611
+ defaultLlmAgentName: null,
2612
+ configFilePath: '/path/to/config.yml',
2613
+ maximumPreparingIssuesCount: null,
2614
+ utilizationPercentageThreshold: 90,
2615
+ allowedIssueAuthors: null,
2616
+ codexHomeCandidates: null,
2617
+ allowIssueCacheMinutes: 0,
2618
+ labelsAsLlmAgentName: null,
2619
+ });
2620
+
2621
+ expect(mockLocalCommandRunner.runCommand.mock.calls).toHaveLength(1);
2622
+ expect(mockLocalCommandRunner.runCommand.mock.calls[0][2]).toMatchObject({
2623
+ env: { CLAUDE_CODE_OAUTH_TOKEN: 'token-soon-reset' },
2624
+ });
2625
+ });
2626
+
2627
+ it('should choose the larger-remaining-capacity token on the tie-break when two eligible tokens share the same 7-day reset', async () => {
2628
+ const awaitingIssue = createMockIssue({
2629
+ url: 'url1',
2630
+ title: 'Issue 1',
2631
+ labels: ['category:impl'],
2632
+ status: 'Awaiting Workspace',
2633
+ number: 1,
2634
+ itemId: 'item-1',
2635
+ });
2636
+ mockProjectRepository.getByUrl.mockResolvedValue(mockProject);
2637
+ mockIssueRepository.getStoryObjectMap.mockResolvedValue(
2638
+ createMockStoryObjectMap([awaitingIssue]),
2639
+ );
2640
+ mockLocalCommandRunner.runCommand.mockResolvedValue({
2641
+ stdout: '',
2642
+ stderr: '',
2643
+ exitCode: 0,
2644
+ });
2645
+ mockClaudeTokenUsageRepository.getTokenInFlightCounts.mockResolvedValue({
2646
+ 'token-busy': 4,
2647
+ });
2648
+ const nowEpochSeconds = Math.floor(Date.now() / 1000);
2649
+ const sharedResetsAt = nowEpochSeconds + 30 * 3600;
2650
+ mockClaudeTokenUsageRepository.getAvailableTokenUsages.mockResolvedValue([
2651
+ {
2652
+ name: 'token-busy',
2653
+ token: 'token-busy',
2654
+ fiveHourUtilization: 0.1,
2655
+ sevenDayUtilization: 0.1,
2656
+ blocked: false,
2657
+ rejected: false,
2658
+ blockedUntilEpoch: 0,
2659
+ modelWeeklyLimits: {
2660
+ seven_day_opus: {
2661
+ rejected: false,
2662
+ resetsAt: sharedResetsAt,
2663
+ },
2664
+ },
2665
+ },
2666
+ {
2667
+ name: 'token-idle',
2668
+ token: 'token-idle',
2669
+ fiveHourUtilization: 0.1,
2670
+ sevenDayUtilization: 0.1,
2671
+ blocked: false,
2672
+ rejected: false,
2673
+ blockedUntilEpoch: 0,
2674
+ modelWeeklyLimits: {
2675
+ seven_day_opus: {
2676
+ rejected: false,
2677
+ resetsAt: sharedResetsAt,
2678
+ },
2679
+ },
2680
+ },
2681
+ ]);
2682
+
2683
+ await useCase.run({
2684
+ projectUrl: 'https://github.com/user/repo',
2685
+ defaultAgentName: 'agent1',
2686
+ defaultLlmModelName: 'claude-opus',
2687
+ fallbackLlmModelName: null,
2688
+ defaultLlmAgentName: null,
2689
+ configFilePath: '/path/to/config.yml',
2690
+ maximumPreparingIssuesCount: null,
2691
+ utilizationPercentageThreshold: 90,
2692
+ allowedIssueAuthors: null,
2693
+ codexHomeCandidates: null,
2694
+ allowIssueCacheMinutes: 0,
2695
+ labelsAsLlmAgentName: null,
2696
+ });
2697
+
2698
+ expect(mockLocalCommandRunner.runCommand.mock.calls).toHaveLength(1);
2699
+ expect(mockLocalCommandRunner.runCommand.mock.calls[0][2]).toMatchObject({
2700
+ env: { CLAUDE_CODE_OAUTH_TOKEN: 'token-idle' },
2701
+ });
2702
+ });
2703
+
2704
+ it('should exclude a token with zero remaining capacity even when its 7-day reset is the soonest', async () => {
2705
+ const awaitingIssue = createMockIssue({
2706
+ url: 'url1',
2707
+ title: 'Issue 1',
2708
+ labels: ['category:impl'],
2709
+ status: 'Awaiting Workspace',
2710
+ number: 1,
2711
+ itemId: 'item-1',
2712
+ });
2713
+ mockProjectRepository.getByUrl.mockResolvedValue(mockProject);
2714
+ mockIssueRepository.getStoryObjectMap.mockResolvedValue(
2715
+ createMockStoryObjectMap([awaitingIssue]),
2716
+ );
2717
+ mockLocalCommandRunner.runCommand.mockResolvedValue({
2718
+ stdout: '',
2719
+ stderr: '',
2720
+ exitCode: 0,
2721
+ });
2722
+ mockClaudeTokenUsageRepository.getTokenInFlightCounts.mockResolvedValue({
2723
+ 'token-soon-but-full': 6,
2724
+ });
2725
+ const nowEpochSeconds = Math.floor(Date.now() / 1000);
2726
+ mockClaudeTokenUsageRepository.getAvailableTokenUsages.mockResolvedValue([
2727
+ {
2728
+ name: 'token-soon-but-full',
2729
+ token: 'token-soon-but-full',
2730
+ fiveHourUtilization: 0.1,
2731
+ sevenDayUtilization: 0.1,
2732
+ blocked: false,
2733
+ rejected: false,
2734
+ blockedUntilEpoch: 0,
2735
+ modelWeeklyLimits: {
2736
+ seven_day_opus: {
2737
+ rejected: false,
2738
+ resetsAt: nowEpochSeconds + 10 * 3600,
2739
+ },
2740
+ },
2741
+ },
2742
+ {
2743
+ name: 'token-later-with-capacity',
2744
+ token: 'token-later-with-capacity',
2745
+ fiveHourUtilization: 0.1,
2746
+ sevenDayUtilization: 0.1,
2747
+ blocked: false,
2748
+ rejected: false,
2749
+ blockedUntilEpoch: 0,
2750
+ modelWeeklyLimits: {
2751
+ seven_day_opus: {
2752
+ rejected: false,
2753
+ resetsAt: nowEpochSeconds + 100 * 3600,
2754
+ },
2755
+ },
2756
+ },
2757
+ ]);
2758
+
2759
+ await useCase.run({
2760
+ projectUrl: 'https://github.com/user/repo',
2761
+ defaultAgentName: 'agent1',
2762
+ defaultLlmModelName: 'claude-opus',
2763
+ fallbackLlmModelName: null,
2764
+ defaultLlmAgentName: null,
2765
+ configFilePath: '/path/to/config.yml',
2766
+ maximumPreparingIssuesCount: null,
2767
+ utilizationPercentageThreshold: 90,
2768
+ allowedIssueAuthors: null,
2769
+ codexHomeCandidates: null,
2770
+ allowIssueCacheMinutes: 0,
2771
+ labelsAsLlmAgentName: null,
2772
+ });
2773
+
2774
+ expect(mockLocalCommandRunner.runCommand.mock.calls).toHaveLength(1);
2775
+ expect(mockLocalCommandRunner.runCommand.mock.calls[0][2]).toMatchObject({
2776
+ env: { CLAUDE_CODE_OAUTH_TOKEN: 'token-later-with-capacity' },
2777
+ });
2778
+ });
2779
+
2554
2780
  it('should pick the token with the soonest 7-day reset deadline first', async () => {
2555
2781
  const awaitingIssue = createMockIssue({
2556
2782
  url: 'url1',
@@ -2986,7 +3212,7 @@ describe('StartPreparationUseCase', () => {
2986
3212
  consoleWarnSpy.mockRestore();
2987
3213
  });
2988
3214
 
2989
- it('should sort tokens by 7-day reset deadline ascending when all have full process capacity', async () => {
3215
+ it('should drain the soonest-7-day-reset token first across spawns until its remaining capacity is exhausted', async () => {
2990
3216
  const awaitingIssues: Issue[] = [
2991
3217
  createMockIssue({
2992
3218
  url: 'url1',
@@ -3091,7 +3317,91 @@ describe('StartPreparationUseCase', () => {
3091
3317
  env: { CLAUDE_CODE_OAUTH_TOKEN: 'token-7d-soon-reset' },
3092
3318
  });
3093
3319
  expect(mockLocalCommandRunner.runCommand.mock.calls[1][2]).toMatchObject({
3094
- env: { CLAUDE_CODE_OAUTH_TOKEN: 'token-7d-mid-reset' },
3320
+ env: { CLAUDE_CODE_OAUTH_TOKEN: 'token-7d-soon-reset' },
3321
+ });
3322
+ expect(mockLocalCommandRunner.runCommand.mock.calls[2][2]).toMatchObject({
3323
+ env: { CLAUDE_CODE_OAUTH_TOKEN: 'token-7d-soon-reset' },
3324
+ });
3325
+ });
3326
+
3327
+ it('should move to the next-soonest-reset token only after the soonest token capacity is exhausted', async () => {
3328
+ const awaitingIssues: Issue[] = Array.from({ length: 3 }, (_, i) =>
3329
+ createMockIssue({
3330
+ url: `url${i + 1}`,
3331
+ title: `Issue ${i + 1}`,
3332
+ labels: ['category:impl'],
3333
+ status: 'Awaiting Workspace',
3334
+ number: i + 1,
3335
+ itemId: `item-${i + 1}`,
3336
+ }),
3337
+ );
3338
+ mockProjectRepository.getByUrl.mockResolvedValue(mockProject);
3339
+ mockIssueRepository.getStoryObjectMap.mockResolvedValue(
3340
+ createMockStoryObjectMap(awaitingIssues),
3341
+ );
3342
+ mockLocalCommandRunner.runCommand.mockResolvedValue({
3343
+ stdout: '',
3344
+ stderr: '',
3345
+ exitCode: 0,
3346
+ });
3347
+ mockClaudeTokenUsageRepository.getTokenInFlightCounts.mockResolvedValue({
3348
+ 'token-7d-soon-reset': 5,
3349
+ });
3350
+ const nowEpochSeconds = Math.floor(Date.now() / 1000);
3351
+ mockClaudeTokenUsageRepository.getAvailableTokenUsages.mockResolvedValue([
3352
+ {
3353
+ name: 'token-7d-soon-reset',
3354
+ token: 'token-7d-soon-reset',
3355
+ fiveHourUtilization: 0.1,
3356
+ sevenDayUtilization: 0.1,
3357
+ blocked: false,
3358
+ rejected: false,
3359
+ blockedUntilEpoch: 0,
3360
+ modelWeeklyLimits: {
3361
+ seven_day_opus: {
3362
+ rejected: false,
3363
+ resetsAt: nowEpochSeconds + 10 * 3600,
3364
+ },
3365
+ },
3366
+ },
3367
+ {
3368
+ name: 'token-7d-far-reset',
3369
+ token: 'token-7d-far-reset',
3370
+ fiveHourUtilization: 0.1,
3371
+ sevenDayUtilization: 0.1,
3372
+ blocked: false,
3373
+ rejected: false,
3374
+ blockedUntilEpoch: 0,
3375
+ modelWeeklyLimits: {
3376
+ seven_day_opus: {
3377
+ rejected: false,
3378
+ resetsAt: nowEpochSeconds + 150 * 3600,
3379
+ },
3380
+ },
3381
+ },
3382
+ ]);
3383
+
3384
+ await useCase.run({
3385
+ projectUrl: 'https://github.com/user/repo',
3386
+ defaultAgentName: 'agent1',
3387
+ defaultLlmModelName: 'claude-opus',
3388
+ fallbackLlmModelName: null,
3389
+ defaultLlmAgentName: null,
3390
+ configFilePath: '/path/to/config.yml',
3391
+ maximumPreparingIssuesCount: null,
3392
+ utilizationPercentageThreshold: 90,
3393
+ allowedIssueAuthors: null,
3394
+ codexHomeCandidates: null,
3395
+ allowIssueCacheMinutes: 0,
3396
+ labelsAsLlmAgentName: null,
3397
+ });
3398
+
3399
+ expect(mockLocalCommandRunner.runCommand.mock.calls).toHaveLength(3);
3400
+ expect(mockLocalCommandRunner.runCommand.mock.calls[0][2]).toMatchObject({
3401
+ env: { CLAUDE_CODE_OAUTH_TOKEN: 'token-7d-soon-reset' },
3402
+ });
3403
+ expect(mockLocalCommandRunner.runCommand.mock.calls[1][2]).toMatchObject({
3404
+ env: { CLAUDE_CODE_OAUTH_TOKEN: 'token-7d-far-reset' },
3095
3405
  });
3096
3406
  expect(mockLocalCommandRunner.runCommand.mock.calls[2][2]).toMatchObject({
3097
3407
  env: { CLAUDE_CODE_OAUTH_TOKEN: 'token-7d-far-reset' },
@@ -3967,11 +4277,11 @@ describe('StartPreparationUseCase', () => {
3967
4277
  env: { CLAUDE_CODE_OAUTH_TOKEN: 'token-b-known-soon-reset' },
3968
4278
  });
3969
4279
  expect(mockLocalCommandRunner.runCommand.mock.calls[1][2]).toMatchObject({
3970
- env: { CLAUDE_CODE_OAUTH_TOKEN: 'token-a-unknown-reset' },
4280
+ env: { CLAUDE_CODE_OAUTH_TOKEN: 'token-b-known-soon-reset' },
3971
4281
  });
3972
4282
  });
3973
4283
 
3974
- it('should sort tokens by 7-day reset deadline ascending when only the generic seven_day weekly limit (as bridged by the proxy from the snapshot top-level sevenDayReset) is present', async () => {
4284
+ it('should drain the soonest-reset token first when only the generic seven_day weekly limit (as bridged by the proxy from the snapshot top-level sevenDayReset) is present', async () => {
3975
4285
  const awaitingIssues: Issue[] = [
3976
4286
  createMockIssue({
3977
4287
  url: 'url1',
@@ -4076,10 +4386,10 @@ describe('StartPreparationUseCase', () => {
4076
4386
  env: { CLAUDE_CODE_OAUTH_TOKEN: 'token-7d-soon' },
4077
4387
  });
4078
4388
  expect(mockLocalCommandRunner.runCommand.mock.calls[1][2]).toMatchObject({
4079
- env: { CLAUDE_CODE_OAUTH_TOKEN: 'token-7d-mid' },
4389
+ env: { CLAUDE_CODE_OAUTH_TOKEN: 'token-7d-soon' },
4080
4390
  });
4081
4391
  expect(mockLocalCommandRunner.runCommand.mock.calls[2][2]).toMatchObject({
4082
- env: { CLAUDE_CODE_OAUTH_TOKEN: 'token-7d-far' },
4392
+ env: { CLAUDE_CODE_OAUTH_TOKEN: 'token-7d-soon' },
4083
4393
  });
4084
4394
  });
4085
4395
 
@@ -133,7 +133,11 @@ export class StartPreparationUseCase {
133
133
  ): {
134
134
  tokens: string[];
135
135
  effectiveCap: number;
136
- tokensWithLimits: Array<{ token: string; limit: number }>;
136
+ tokensWithLimits: Array<{
137
+ token: string;
138
+ limit: number;
139
+ secondsUntilSevenDayReset: number;
140
+ }>;
137
141
  } => {
138
142
  const weeklyLimitType = this.weeklyLimitTypeForModel(modelName);
139
143
  const nowEpochSeconds = Date.now() / 1000;
@@ -167,6 +171,11 @@ export class StartPreparationUseCase {
167
171
  usage.fiveHourUtilization,
168
172
  usage.sevenDayUtilization,
169
173
  ),
174
+ secondsUntilSevenDayReset: this.secondsUntilSevenDayReset(
175
+ usage,
176
+ weeklyLimitType,
177
+ nowEpochSeconds,
178
+ ),
170
179
  }));
171
180
 
172
181
  const totalCapacity = tokensWithLimits.reduce((sum, t) => sum + t.limit, 0);
@@ -261,7 +270,11 @@ export class StartPreparationUseCase {
261
270
  await this.claudeTokenUsageRepository.getAvailableTokenUsages();
262
271
  let rotationTokens: string[] | null = null;
263
272
  let proxyBaseUrl: string | null = null;
264
- let selectedTokensWithLimits: Array<{ token: string; limit: number }> = [];
273
+ let selectedTokensWithLimits: Array<{
274
+ token: string;
275
+ limit: number;
276
+ secondsUntilSevenDayReset: number;
277
+ }> = [];
265
278
  let tokenInFlightCounts: Record<string, number> = {};
266
279
  const rotationOrder: RotationOrderEntry[] | null =
267
280
  tokenUsages.length > 0
@@ -530,20 +543,26 @@ export class StartPreparationUseCase {
530
543
  }
531
544
  let spawnEnv: Record<string, string> | undefined;
532
545
  if (rotationTokens !== null && proxyBaseUrl !== null) {
533
- const tokenWithMostRemainingCapacity = selectedTokensWithLimits
546
+ const tokenWithSoonestResetAmongAvailable = selectedTokensWithLimits
534
547
  .map((t) => ({
535
548
  token: t.token,
536
549
  remaining:
537
550
  t.limit -
538
551
  (tokenInFlightCounts[t.token] ?? 0) -
539
552
  (spawnedInThisRunByToken[t.token] ?? 0),
553
+ secondsUntilSevenDayReset: t.secondsUntilSevenDayReset,
540
554
  }))
541
555
  .filter((t) => t.remaining > 0)
542
- .sort((a, b) => b.remaining - a.remaining)[0];
543
- if (tokenWithMostRemainingCapacity === undefined) {
556
+ .sort((a, b) => {
557
+ if (a.secondsUntilSevenDayReset !== b.secondsUntilSevenDayReset) {
558
+ return a.secondsUntilSevenDayReset - b.secondsUntilSevenDayReset;
559
+ }
560
+ return b.remaining - a.remaining;
561
+ })[0];
562
+ if (tokenWithSoonestResetAmongAvailable === undefined) {
544
563
  break;
545
564
  }
546
- const selected = tokenWithMostRemainingCapacity.token;
565
+ const selected = tokenWithSoonestResetAmongAvailable.token;
547
566
  spawnedInThisRunByToken[selected] =
548
567
  (spawnedInThisRunByToken[selected] ?? 0) + 1;
549
568
  spawnEnv = {
@@ -17,6 +17,7 @@ export type ConfigFile = {
17
17
  awLogDirectoryPath?: string;
18
18
  awLogStaleThresholdMinutes?: number;
19
19
  labelsAsLlmAgentName?: string[];
20
+ changeTargetPathAliases?: Record<string, string>;
20
21
  };
21
22
  export declare const isRecord: (value: unknown) => value is Record<string, unknown>;
22
23
  export declare const loadConfigFile: (configFilePath: string) => ConfigFile;
@@ -1 +1 @@
1
- {"version":3,"file":"projectConfig.d.ts","sourceRoot":"","sources":["../../../../src/adapter/entry-points/cli/projectConfig.ts"],"names":[],"mappings":"AAGA,MAAM,MAAM,UAAU,GAAG;IACvB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,2BAA2B,CAAC,EAAE,MAAM,CAAC;IACrC,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAChC,8BAA8B,CAAC,EAAE,MAAM,CAAC;IACxC,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAChC,iCAAiC,CAAC,EAAE,MAAM,CAAC;IAC3C,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,8BAA8B,CAAC,EAAE,MAAM,CAAC;IACxC,mBAAmB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC/B,gCAAgC,CAAC,EAAE,MAAM,CAAC;IAC1C,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,0BAA0B,CAAC,EAAE,MAAM,CAAC;IACpC,oBAAoB,CAAC,EAAE,MAAM,EAAE,CAAC;CACjC,CAAC;AAoCF,eAAO,MAAM,QAAQ,GAAI,OAAO,OAAO,KAAG,KAAK,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CACH,CAAC;AAoBvE,eAAO,MAAM,cAAc,GAAI,gBAAgB,MAAM,KAAG,UAoDvD,CAAC;AAEF,eAAO,MAAM,wBAAwB,GACnC,QAAQ,MAAM,EACd,aAAa,MAAM,KAClB,UAgEF,CAAC;AAEF,eAAO,MAAM,YAAY,GACvB,YAAY,UAAU,EACtB,cAAc,UAAU,EACxB,iBAAiB,UAAU,KAC1B,UAmED,CAAC;AAkBH,eAAO,MAAM,kBAAkB,GAC7B,YAAY,MAAM,EAClB,OAAO,MAAM,KACZ,OAAO,CAAC,MAAM,GAAG,IAAI,CA0DvB,CAAC"}
1
+ {"version":3,"file":"projectConfig.d.ts","sourceRoot":"","sources":["../../../../src/adapter/entry-points/cli/projectConfig.ts"],"names":[],"mappings":"AAGA,MAAM,MAAM,UAAU,GAAG;IACvB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,2BAA2B,CAAC,EAAE,MAAM,CAAC;IACrC,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAChC,8BAA8B,CAAC,EAAE,MAAM,CAAC;IACxC,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAChC,iCAAiC,CAAC,EAAE,MAAM,CAAC;IAC3C,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,8BAA8B,CAAC,EAAE,MAAM,CAAC;IACxC,mBAAmB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC/B,gCAAgC,CAAC,EAAE,MAAM,CAAC;IAC1C,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,0BAA0B,CAAC,EAAE,MAAM,CAAC;IACpC,oBAAoB,CAAC,EAAE,MAAM,EAAE,CAAC;IAChC,uBAAuB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAClD,CAAC;AAsDF,eAAO,MAAM,QAAQ,GAAI,OAAO,OAAO,KAAG,KAAK,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CACH,CAAC;AAqBvE,eAAO,MAAM,cAAc,GAAI,gBAAgB,MAAM,KAAG,UAwDvD,CAAC;AAEF,eAAO,MAAM,wBAAwB,GACnC,QAAQ,MAAM,EACd,aAAa,MAAM,KAClB,UAoEF,CAAC;AAEF,eAAO,MAAM,YAAY,GACvB,YAAY,UAAU,EACtB,cAAc,UAAU,EACxB,iBAAiB,UAAU,KAC1B,UAuED,CAAC;AAkBH,eAAO,MAAM,kBAAkB,GAC7B,YAAY,MAAM,EAClB,OAAO,MAAM,KACZ,OAAO,CAAC,MAAM,GAAG,IAAI,CA0DvB,CAAC"}
@@ -2,7 +2,7 @@ import { IssueRepository } from './adapter-interfaces/IssueRepository';
2
2
  export declare class ChangeTargetPullRequestApprover {
3
3
  private readonly issueRepository;
4
4
  constructor(issueRepository: Pick<IssueRepository, 'getPullRequestChangedFilePaths' | 'approvePullRequest'>);
5
- approveIfConfined: (issueLabels: string[], approvedPrUrl: string | null) => Promise<void>;
5
+ approveIfConfined: (issueLabels: string[], approvedPrUrl: string | null, pathAliases?: Record<string, string> | null) => Promise<void>;
6
6
  private extractChangeTargetPaths;
7
7
  private isFilePathConfinedToAllowedPaths;
8
8
  }
@@ -1 +1 @@
1
- {"version":3,"file":"ChangeTargetPullRequestApprover.d.ts","sourceRoot":"","sources":["../../../src/domain/usecases/ChangeTargetPullRequestApprover.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,sCAAsC,CAAC;AAEvE,qBAAa,+BAA+B;IAExC,OAAO,CAAC,QAAQ,CAAC,eAAe;gBAAf,eAAe,EAAE,IAAI,CACpC,eAAe,EACf,gCAAgC,GAAG,oBAAoB,CACxD;IAGH,iBAAiB,GACf,aAAa,MAAM,EAAE,EACrB,eAAe,MAAM,GAAG,IAAI,KAC3B,OAAO,CAAC,IAAI,CAAC,CAoBd;IAEF,OAAO,CAAC,wBAAwB,CAa9B;IAEF,OAAO,CAAC,gCAAgC,CAOpC;CACL"}
1
+ {"version":3,"file":"ChangeTargetPullRequestApprover.d.ts","sourceRoot":"","sources":["../../../src/domain/usecases/ChangeTargetPullRequestApprover.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,sCAAsC,CAAC;AAEvE,qBAAa,+BAA+B;IAExC,OAAO,CAAC,QAAQ,CAAC,eAAe;gBAAf,eAAe,EAAE,IAAI,CACpC,eAAe,EACf,gCAAgC,GAAG,oBAAoB,CACxD;IAGH,iBAAiB,GACf,aAAa,MAAM,EAAE,EACrB,eAAe,MAAM,GAAG,IAAI,EAC5B,cAAc,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI,KAC1C,OAAO,CAAC,IAAI,CAAC,CAuBd;IAEF,OAAO,CAAC,wBAAwB,CAmB9B;IAEF,OAAO,CAAC,gCAAgC,CAOpC;CACL"}
@@ -69,6 +69,7 @@ export declare class HandleScheduledEventUseCase {
69
69
  disabled: boolean;
70
70
  allowIssueCacheMinutes: number;
71
71
  labelsAsLlmAgentName?: string[] | null;
72
+ changeTargetPathAliases?: Record<string, string> | null;
72
73
  startPreparation?: {
73
74
  defaultAgentName: string;
74
75
  defaultLlmModelName?: string | null;
@@ -1 +1 @@
1
- {"version":3,"file":"HandleScheduledEventUseCase.d.ts","sourceRoot":"","sources":["../../../src/domain/usecases/HandleScheduledEventUseCase.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"HandleScheduledEventUseCase.d.ts","sourceRoot":"","sources":["../../../src/domain/usecases/HandleScheduledEventUseCase.ts"],"names":[],"mappings":"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"}
@@ -23,6 +23,7 @@ export declare class NotifyFinishedIssuePreparationUseCase {
23
23
  workflowBlockerResolvedWebhookUrl: string | null;
24
24
  allowedIssueAuthors?: string[] | null;
25
25
  labelsAsLlmAgentName?: string[] | null;
26
+ changeTargetPathAliases?: Record<string, string> | null;
26
27
  }) => Promise<void>;
27
28
  private isAuthorTrusted;
28
29
  private collectRejections;
@@ -1 +1 @@
1
- {"version":3,"file":"NotifyFinishedIssuePreparationUseCase.d.ts","sourceRoot":"","sources":["../../../src/domain/usecases/NotifyFinishedIssuePreparationUseCase.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,sCAAsC,CAAC;AACvE,OAAO,EAAE,iBAAiB,EAAE,MAAM,wCAAwC,CAAC;AAC3E,OAAO,EAAE,sBAAsB,EAAE,MAAM,6CAA6C,CAAC;AACrF,OAAO,EAAE,iBAAiB,EAAE,MAAM,wCAAwC,CAAC;AAa3E,qBAAa,kBAAmB,SAAQ,KAAK;gBAC/B,QAAQ,EAAE,MAAM;CAI7B;AACD,qBAAa,uBAAwB,SAAQ,KAAK;gBAE9C,QAAQ,EAAE,MAAM,EAChB,aAAa,EAAE,MAAM,GAAG,IAAI,EAC5B,cAAc,EAAE,MAAM,GAAG,IAAI;CAOhC;AAMD,qBAAa,qCAAqC;IAK9C,OAAO,CAAC,QAAQ,CAAC,iBAAiB;IAClC,OAAO,CAAC,QAAQ,CAAC,eAAe;IAahC,OAAO,CAAC,QAAQ,CAAC,sBAAsB;IAIvC,OAAO,CAAC,QAAQ,CAAC,iBAAiB;IAtBpC,OAAO,CAAC,QAAQ,CAAC,uBAAuB,CAA0B;IAClE,OAAO,CAAC,QAAQ,CAAC,+BAA+B,CAAkC;gBAG/D,iBAAiB,EAAE,IAAI,CAAC,iBAAiB,EAAE,UAAU,CAAC,EACtD,eAAe,EAAE,IAAI,CACpC,eAAe,EACb,KAAK,GACL,QAAQ,GACR,cAAc,GACd,oBAAoB,GACpB,mBAAmB,GACnB,oBAAoB,GACpB,gCAAgC,GAChC,oBAAoB,GACpB,iCAAiC,GACjC,qBAAqB,CACxB,EACgB,sBAAsB,EAAE,IAAI,CAC3C,sBAAsB,EACtB,sBAAsB,GAAG,eAAe,CACzC,EACgB,iBAAiB,EAAE,IAAI,CACtC,iBAAiB,EACjB,gBAAgB,CACjB;IAQH,GAAG,GAAU,QAAQ;QACnB,UAAU,EAAE,MAAM,CAAC;QACnB,QAAQ,EAAE,MAAM,CAAC;QACjB,sBAAsB,EAAE,MAAM,CAAC;QAC/B,iCAAiC,EAAE,MAAM,GAAG,IAAI,CAAC;QACjD,mBAAmB,CAAC,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;QACtC,oBAAoB,CAAC,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;KACxC,KAAG,OAAO,CAAC,IAAI,CAAC,CAuMf;IAEF,OAAO,CAAC,eAAe,CAIgD;IAEvE,OAAO,CAAC,iBAAiB,CA+BvB;IAEF,OAAO,CAAC,qBAAqB,CAuB3B;IAEF,OAAO,CAAC,gCAAgC,CAiBtC;IAEF,OAAO,CAAC,uBAAuB,CAQ7B;IAEF,OAAO,CAAC,+BAA+B,CAkCrC;CACH"}
1
+ {"version":3,"file":"NotifyFinishedIssuePreparationUseCase.d.ts","sourceRoot":"","sources":["../../../src/domain/usecases/NotifyFinishedIssuePreparationUseCase.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,sCAAsC,CAAC;AACvE,OAAO,EAAE,iBAAiB,EAAE,MAAM,wCAAwC,CAAC;AAC3E,OAAO,EAAE,sBAAsB,EAAE,MAAM,6CAA6C,CAAC;AACrF,OAAO,EAAE,iBAAiB,EAAE,MAAM,wCAAwC,CAAC;AAa3E,qBAAa,kBAAmB,SAAQ,KAAK;gBAC/B,QAAQ,EAAE,MAAM;CAI7B;AACD,qBAAa,uBAAwB,SAAQ,KAAK;gBAE9C,QAAQ,EAAE,MAAM,EAChB,aAAa,EAAE,MAAM,GAAG,IAAI,EAC5B,cAAc,EAAE,MAAM,GAAG,IAAI;CAOhC;AAMD,qBAAa,qCAAqC;IAK9C,OAAO,CAAC,QAAQ,CAAC,iBAAiB;IAClC,OAAO,CAAC,QAAQ,CAAC,eAAe;IAahC,OAAO,CAAC,QAAQ,CAAC,sBAAsB;IAIvC,OAAO,CAAC,QAAQ,CAAC,iBAAiB;IAtBpC,OAAO,CAAC,QAAQ,CAAC,uBAAuB,CAA0B;IAClE,OAAO,CAAC,QAAQ,CAAC,+BAA+B,CAAkC;gBAG/D,iBAAiB,EAAE,IAAI,CAAC,iBAAiB,EAAE,UAAU,CAAC,EACtD,eAAe,EAAE,IAAI,CACpC,eAAe,EACb,KAAK,GACL,QAAQ,GACR,cAAc,GACd,oBAAoB,GACpB,mBAAmB,GACnB,oBAAoB,GACpB,gCAAgC,GAChC,oBAAoB,GACpB,iCAAiC,GACjC,qBAAqB,CACxB,EACgB,sBAAsB,EAAE,IAAI,CAC3C,sBAAsB,EACtB,sBAAsB,GAAG,eAAe,CACzC,EACgB,iBAAiB,EAAE,IAAI,CACtC,iBAAiB,EACjB,gBAAgB,CACjB;IAQH,GAAG,GAAU,QAAQ;QACnB,UAAU,EAAE,MAAM,CAAC;QACnB,QAAQ,EAAE,MAAM,CAAC;QACjB,sBAAsB,EAAE,MAAM,CAAC;QAC/B,iCAAiC,EAAE,MAAM,GAAG,IAAI,CAAC;QACjD,mBAAmB,CAAC,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;QACtC,oBAAoB,CAAC,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;QACvC,uBAAuB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI,CAAC;KACzD,KAAG,OAAO,CAAC,IAAI,CAAC,CAwMf;IAEF,OAAO,CAAC,eAAe,CAIgD;IAEvE,OAAO,CAAC,iBAAiB,CA+BvB;IAEF,OAAO,CAAC,qBAAqB,CAuB3B;IAEF,OAAO,CAAC,gCAAgC,CAiBtC;IAEF,OAAO,CAAC,uBAAuB,CAQ7B;IAEF,OAAO,CAAC,+BAA+B,CAkCrC;CACH"}