claude-dev-env 8.43.5 → 8.43.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 (32) hide show
  1. package/_shared/advisor/advisor-protocol.md +2 -2
  2. package/_shared/advisor/reference/cli-chain.md +9 -12
  3. package/_shared/advisor/reference/lifecycle.md +1 -1
  4. package/_shared/advisor/reference/third-party-bind.md +3 -3
  5. package/bin/install.test.mjs +1 -1
  6. package/docs/second-claude-account.md +11 -13
  7. package/package.json +1 -1
  8. package/scripts/_code_review_test_support.py +29 -39
  9. package/scripts/claude_account_worker.py +46 -297
  10. package/scripts/claude_account_worker_process.py +18 -154
  11. package/scripts/claude_account_worker_report.py +8 -4
  12. package/scripts/claude_chain_usage.py +7 -271
  13. package/scripts/codec_forwarding_test_support.py +11 -65
  14. package/scripts/dev_env_scripts_constants/claude_account_worker_constants.py +6 -24
  15. package/scripts/invoke_code_review.py +34 -56
  16. package/scripts/resolve_worker_spawn.py +40 -72
  17. package/scripts/test_account_broker_guard.py +0 -4
  18. package/scripts/test_claude_account_worker.py +105 -350
  19. package/scripts/test_claude_chain_usage.py +50 -510
  20. package/scripts/test_dispatcher_profile_import.py +5 -9
  21. package/scripts/test_invoke_code_review.py +7 -6
  22. package/scripts/test_invoke_code_review_chain.py +26 -0
  23. package/scripts/test_invoke_code_review_cli.py +8 -8
  24. package/scripts/test_invoke_code_review_codec.py +3 -11
  25. package/scripts/test_invoke_code_review_contract.py +5 -1
  26. package/scripts/test_resolve_worker_spawn.py +118 -265
  27. package/scripts/test_resolve_worker_spawn_codec.py +9 -21
  28. package/scripts/tests/test_code_review_constants.py +2 -2
  29. package/scripts/claude_account_choice.py +0 -422
  30. package/scripts/claude_chain_runner.py +0 -1289
  31. package/scripts/test_claude_account_choice.py +0 -355
  32. package/scripts/test_claude_chain_runner.py +0 -1839
@@ -2,15 +2,13 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
- import io
6
5
  import json
7
6
  import subprocess
8
7
  import sys
9
8
  import threading
10
- from collections.abc import Sequence
11
9
  from dataclasses import dataclass, field
10
+ from datetime import datetime, timezone
12
11
  from pathlib import Path
13
- from typing import Self
14
12
 
15
13
  import pytest
16
14
 
@@ -18,17 +16,17 @@ _SCRIPTS_DIR = Path(__file__).resolve().parent
18
16
  if str(_SCRIPTS_DIR) not in sys.path:
19
17
  sys.path.insert(0, str(_SCRIPTS_DIR))
20
18
 
21
- import claude_chain_runner as chain_runner # noqa: E402
19
+ import account_broker
22
20
  import invoke_code_review as invoker
23
21
  import resolve_worker_spawn as dispatcher # noqa: E402
24
- from claude_chain_runner import ( # noqa: E402
25
- ChainAttempt,
26
- ChainConfigurationError,
27
- ChainInvocationOutcome,
28
- )
29
- from dev_env_scripts_constants.claude_chain_constants import ( # noqa: E402
30
- TERMINAL_STATUS_CHAIN_EXHAUSTED,
31
- TERMINAL_STATUS_SERVED,
22
+ from dev_env_scripts_constants.account_broker_constants import (
23
+ Account,
24
+ BrokerConfigurationError,
25
+ JobOutcome,
26
+ Meters,
27
+ Product,
28
+ ProductAdapter,
29
+ WAIT_EXIT_CODE,
32
30
  )
33
31
  from dev_env_scripts_constants.grok_worker_constants import ( # noqa: E402
34
32
  AGENT_FLAG,
@@ -64,7 +62,6 @@ from dev_env_scripts_constants.grok_worker_constants import ( # noqa: E402
64
62
  RESULT_KEY_TIER_USED,
65
63
  SINGLE_TURN_FLAG,
66
64
  SPAWN_CONFIG_ERROR_EXIT_CODE,
67
- SPAWN_EXHAUSTED_EXIT_CODE,
68
65
  SPAWN_SERVED_EXIT_CODE,
69
66
  TIER_CLAUDE_AGENT,
70
67
  TIER_CLAUDE_HEADLESS,
@@ -91,7 +88,6 @@ FIXTURE_ROLE = "code-quality-agent"
91
88
  MIN_WORKER_TIMEOUT_SECONDS_CONSTANT_NAME = "MIN_WORKER_TIMEOUT_SECONDS"
92
89
  LARGE_PROMPT_CHARACTER_COUNT = 40000
93
90
  WINDOWS_SAFE_ARGV_ELEMENT_CEILING = 8192
94
- LOCK_WAIT_TIMEOUT_SECONDS = 1
95
91
  EXPECTED_PRIMARY_AGENT_FOR_DEFAULT_ROLE = Path(
96
92
  ALL_AGENT_FILENAMES_BY_ROLE[DEFAULT_ROLE][0]
97
93
  ).stem
@@ -135,25 +131,29 @@ def _claude_served(
135
131
  stdout: str = FIXTURE_CLAUDE_STDOUT,
136
132
  *,
137
133
  returncode: int = FIXTURE_CLAUDE_RETURNCODE,
138
- ) -> ChainInvocationOutcome:
139
- return ChainInvocationOutcome(
140
- served_command="claude",
134
+ ) -> JobOutcome:
135
+ return JobOutcome(
141
136
  returncode=returncode,
142
137
  stdout=stdout,
143
138
  stderr="",
144
- attempts=(ChainAttempt(command="claude", status="served"),),
145
- terminal_status=TERMINAL_STATUS_SERVED,
139
+ account_name="main",
140
+ attempts=(("main", "served"),),
141
+ status="served" if returncode == 0 else "advisor_blocked",
142
+ session_id=None,
143
+ wait_reset_at=None,
146
144
  )
147
145
 
148
146
 
149
- def _claude_exhausted() -> ChainInvocationOutcome:
150
- return ChainInvocationOutcome(
151
- served_command=None,
152
- returncode=FIXTURE_FAILED_RETURNCODE,
147
+ def _claude_exhausted() -> JobOutcome:
148
+ return JobOutcome(
149
+ account_name=None,
150
+ returncode=WAIT_EXIT_CODE,
153
151
  stdout="",
154
- stderr="usage limit reached",
155
- attempts=(ChainAttempt(command="claude", status="usage_limited"),),
156
- terminal_status=TERMINAL_STATUS_CHAIN_EXHAUSTED,
152
+ stderr="",
153
+ attempts=(("main", "usage_limited"),),
154
+ status="exhausted",
155
+ session_id=None,
156
+ wait_reset_at=datetime(2026, 10, 4, tzinfo=timezone.utc),
157
157
  )
158
158
 
159
159
 
@@ -174,8 +174,7 @@ class SeamCallLog:
174
174
  claude_calls: int = 0
175
175
  claude_arguments: list[str] | None = None
176
176
  host_profile_calls: int = 0
177
- is_stdin_from_prompt_file: bool = False
178
- claude_stdin_path: Path | None = None
177
+ claude_stdin_text: str | None = None
179
178
  claude_working_directory: Path | None = None
180
179
  grok_keyword_arguments: dict[str, object] | None = None
181
180
  max_argv_element_length: int = 0
@@ -187,7 +186,7 @@ def _install_seams(
187
186
  *,
188
187
  preflight_outcome: PreflightOutcome = _usable_preflight(),
189
188
  grok_outcome: GrokRunnerOutcome | None = None,
190
- claude_outcome: ChainInvocationOutcome | BaseException | None = None,
189
+ claude_outcome: JobOutcome | BaseException | None = None,
191
190
  host_profile: str = HOST_PROFILE_CLAUDE,
192
191
  ) -> SeamCallLog:
193
192
  call_log = SeamCallLog()
@@ -202,21 +201,18 @@ def _install_seams(
202
201
  assert grok_outcome is not None
203
202
  return grok_outcome
204
203
 
205
- def fake_claude(
206
- all_claude_arguments: list[str], *, timeout_seconds: int
207
- ) -> ChainInvocationOutcome:
204
+ def fake_claude(product: Product, all_claude_arguments: list[str], **options: object) -> JobOutcome:
205
+ assert product is Product.CLAUDE
208
206
  call_log.claude_calls += 1
209
- call_log.claude_arguments = list(all_claude_arguments)
210
- chain_runner.chain_subprocess_runner(
211
- ["claude", *all_claude_arguments],
212
- capture_output=True,
213
- text=True,
214
- timeout=timeout_seconds,
215
- check=False,
216
- )
207
+ call_log.claude_arguments = list(all_claude_arguments[1:])
208
+ call_log.max_argv_element_length = max(map(len, all_claude_arguments))
209
+ call_log.claude_stdin_text = options.get("stdin_text")
210
+ if options.get("cwd") is not None:
211
+ call_log.claude_working_directory = Path(options["cwd"])
212
+ call_log.all_observed_working_directories.append(Path(options["cwd"]))
217
213
  if isinstance(claude_outcome, BaseException):
218
214
  raise claude_outcome
219
- assert isinstance(claude_outcome, ChainInvocationOutcome)
215
+ assert isinstance(claude_outcome, JobOutcome)
220
216
  return claude_outcome
221
217
 
222
218
  def fake_host_profile(
@@ -231,37 +227,6 @@ def _install_seams(
231
227
  monkeypatch.setattr(dispatcher, "spawn_claude_runner", fake_claude)
232
228
  monkeypatch.setattr(dispatcher, "spawn_host_profile_detector", fake_host_profile)
233
229
 
234
- def _tracking_subprocess_runner(
235
- all_invocation_tokens: Sequence[str],
236
- *all_positionals: object,
237
- **all_keywords: object,
238
- ) -> subprocess.CompletedProcess[str]:
239
- del all_positionals
240
- for each_token in all_invocation_tokens:
241
- call_log.max_argv_element_length = max(
242
- call_log.max_argv_element_length, len(str(each_token))
243
- )
244
- maybe_stdin = all_keywords.get("stdin")
245
- if maybe_stdin is not None and maybe_stdin is not subprocess.DEVNULL:
246
- call_log.is_stdin_from_prompt_file = True
247
- maybe_name = getattr(maybe_stdin, "name", None)
248
- if maybe_name is not None:
249
- call_log.claude_stdin_path = Path(str(maybe_name))
250
- maybe_cwd = all_keywords.get("cwd")
251
- if maybe_cwd is not None:
252
- working_directory = Path(str(maybe_cwd))
253
- call_log.claude_working_directory = working_directory
254
- call_log.all_observed_working_directories.append(working_directory)
255
- return subprocess.CompletedProcess(
256
- args=list(all_invocation_tokens),
257
- returncode=0,
258
- stdout="{}",
259
- stderr="",
260
- )
261
-
262
- monkeypatch.setattr(
263
- chain_runner, "chain_subprocess_runner", _tracking_subprocess_runner
264
- )
265
230
  return call_log
266
231
 
267
232
 
@@ -487,8 +452,7 @@ def test_grok_auth_failed_on_third_party_runs_tier_three(
487
452
  AGENT_FLAG,
488
453
  FIXTURE_ROLE,
489
454
  ]
490
- assert call_log.is_stdin_from_prompt_file is True
491
- assert call_log.claude_stdin_path == prompt_file
455
+ assert call_log.claude_stdin_text == FIXTURE_PROMPT_TEXT
492
456
  assert call_log.claude_working_directory == working_directory
493
457
  assert spawn_outcome.all_attempts[0].tier == TIER_GROK
494
458
  assert spawn_outcome.all_attempts[0].reason == CLASSIFICATION_AUTH_FAILURE
@@ -496,11 +460,11 @@ def test_grok_auth_failed_on_third_party_runs_tier_three(
496
460
  assert spawn_outcome.all_attempts[1].is_ok is True
497
461
 
498
462
 
499
- def test_tier_three_exhausted_returns_exit_two(
463
+ def test_tier_three_wait_reports_reset_and_stops(
500
464
  monkeypatch: pytest.MonkeyPatch, tmp_path: Path, capsys: pytest.CaptureFixture[str]
501
465
  ) -> None:
502
466
  prompt_file, working_directory, run_state_directory = _paths(tmp_path)
503
- _install_seams(
467
+ call_log = _install_seams(
504
468
  monkeypatch,
505
469
  grok_outcome=_grok_failure(CLASSIFICATION_AUTH_FAILURE),
506
470
  claude_outcome=_claude_exhausted(),
@@ -522,11 +486,14 @@ def test_tier_three_exhausted_returns_exit_two(
522
486
  ]
523
487
  )
524
488
 
525
- assert exit_code == SPAWN_EXHAUSTED_EXIT_CODE
489
+ assert exit_code == WAIT_EXIT_CODE
526
490
  captured = capsys.readouterr()
527
491
  parsed_payload = json.loads(captured.out)
528
492
  assert parsed_payload[RESULT_KEY_OK] is False
529
493
  assert parsed_payload[RESULT_KEY_TIER_USED] is None
494
+ assert parsed_payload["status"] == "exhausted"
495
+ assert parsed_payload["wait_reset_at"] == datetime(2026, 10, 4, tzinfo=timezone.utc).isoformat()
496
+ assert call_log.claude_calls == 1
530
497
 
531
498
 
532
499
  def test_config_error_returns_exit_three(
@@ -536,7 +503,7 @@ def test_config_error_returns_exit_three(
536
503
  _install_seams(
537
504
  monkeypatch,
538
505
  grok_outcome=_grok_failure(CLASSIFICATION_AUTH_FAILURE),
539
- claude_outcome=ChainConfigurationError("chain config missing"),
506
+ claude_outcome=BrokerConfigurationError("chain config missing"),
540
507
  host_profile=HOST_PROFILE_THIRD_PARTY,
541
508
  )
542
509
 
@@ -660,6 +627,8 @@ def test_cli_stdout_carries_only_json_result(
660
627
  RESULT_KEY_ATTEMPTS,
661
628
  RESULT_KEY_OUTPUT,
662
629
  RESULT_KEY_RETURNCODE,
630
+ "status",
631
+ "wait_reset_at",
663
632
  }
664
633
  assert reparsed[RESULT_KEY_TIER_USED] == TIER_GROK
665
634
  assert reparsed[RESULT_KEY_OK] is True
@@ -797,8 +766,7 @@ def test_enable_claude_tier_flag_reaches_tier_three_on_claude_host(
797
766
 
798
767
  assert exit_code == SPAWN_SERVED_EXIT_CODE
799
768
  assert call_log.claude_calls == 1
800
- assert call_log.is_stdin_from_prompt_file is True
801
- assert call_log.claude_stdin_path == prompt_file
769
+ assert call_log.claude_stdin_text == FIXTURE_PROMPT_TEXT
802
770
  assert call_log.claude_working_directory == working_directory
803
771
  assert call_log.claude_arguments is not None
804
772
  assert PROMPT_FILE_FLAG not in call_log.claude_arguments
@@ -892,8 +860,7 @@ def test_large_prompt_stays_out_of_claude_argv(
892
860
  assert call_log.claude_arguments is not None
893
861
  assert large_prompt_text not in call_log.claude_arguments
894
862
  assert call_log.max_argv_element_length < WINDOWS_SAFE_ARGV_ELEMENT_CEILING
895
- assert call_log.is_stdin_from_prompt_file is True
896
- assert call_log.claude_stdin_path == prompt_file
863
+ assert call_log.claude_stdin_text == large_prompt_text
897
864
 
898
865
 
899
866
  def test_missing_prompt_file_returns_json_config_exit(
@@ -1027,19 +994,9 @@ def test_prompt_open_failure_not_classified_as_executable_not_found(
1027
994
  lambda *all_positionals, **all_keywords: HOST_PROFILE_THIRD_PARTY,
1028
995
  )
1029
996
  monkeypatch.setattr(
1030
- chain_runner,
1031
- "load_chain",
1032
- lambda _config_path: [chain_runner.ChainEntry(command="claude", extra_args=())],
1033
- )
1034
- monkeypatch.setattr(
1035
- chain_runner,
1036
- "chain_subprocess_runner",
1037
- lambda *all_positionals, **all_keywords: subprocess.CompletedProcess(
1038
- args=["claude"],
1039
- returncode=0,
1040
- stdout="{}",
1041
- stderr="",
1042
- ),
997
+ dispatcher,
998
+ "spawn_claude_runner",
999
+ lambda *args, **kwargs: pytest.fail("broker must not run"),
1043
1000
  )
1044
1001
  _deny_prompt_open(
1045
1002
  monkeypatch,
@@ -1073,27 +1030,22 @@ def test_prompt_open_failure_not_classified_as_executable_not_found(
1073
1030
  assert "executable_not_found" not in all_attempt_reasons
1074
1031
 
1075
1032
 
1076
- def test_headless_chain_runner_lock_serializes_distinct_cwds(
1033
+ def test_headless_jobs_keep_distinct_cwds(
1077
1034
  monkeypatch: pytest.MonkeyPatch, tmp_path: Path
1078
1035
  ) -> None:
1079
- prompt_file = tmp_path / "prompt.txt"
1080
- prompt_file.write_text(FIXTURE_PROMPT_TEXT, encoding="utf-8")
1081
- first_working_directory = tmp_path / "project-a"
1036
+ prompt_file, first_working_directory, run_state_directory = _paths(tmp_path)
1082
1037
  second_working_directory = tmp_path / "project-b"
1083
- first_working_directory.mkdir()
1084
1038
  second_working_directory.mkdir()
1085
- run_state_directory = tmp_path / "run-state"
1086
- run_state_directory.mkdir()
1087
1039
  call_log = _install_seams(
1088
1040
  monkeypatch,
1089
1041
  grok_outcome=_grok_failure(CLASSIFICATION_AUTH_FAILURE),
1090
1042
  claude_outcome=_claude_served(),
1091
1043
  host_profile=HOST_PROFILE_THIRD_PARTY,
1092
1044
  )
1093
- all_errors: list[Exception] = []
1045
+ errors: list[Exception] = []
1094
1046
  barrier = threading.Barrier(2)
1095
1047
 
1096
- def _run_with_working_directory(working_directory: Path) -> None:
1048
+ def run_with_directory(working_directory: Path) -> None:
1097
1049
  try:
1098
1050
  barrier.wait(timeout=5)
1099
1051
  dispatcher.resolve_worker_spawn(
@@ -1103,202 +1055,103 @@ def test_headless_chain_runner_lock_serializes_distinct_cwds(
1103
1055
  timeout_seconds=DEFAULT_WORKER_TIMEOUT_SECONDS,
1104
1056
  is_claude_tier_enabled=False,
1105
1057
  run_state_directory=run_state_directory,
1106
- )
1107
- except (OSError, RuntimeError, ValueError, AssertionError) as raised_error:
1108
- all_errors.append(raised_error)
1058
+ )
1059
+ except (OSError, RuntimeError, ValueError, AssertionError) as error:
1060
+ errors.append(error)
1109
1061
 
1110
- first_thread = threading.Thread(
1111
- target=_run_with_working_directory, args=(first_working_directory,)
1112
- )
1113
- second_thread = threading.Thread(
1114
- target=_run_with_working_directory, args=(second_working_directory,)
1115
- )
1062
+ first_thread = threading.Thread(target=run_with_directory, args=(first_working_directory,))
1063
+ second_thread = threading.Thread(target=run_with_directory, args=(second_working_directory,))
1116
1064
  first_thread.start()
1117
1065
  second_thread.start()
1118
1066
  first_thread.join(timeout=10)
1119
1067
  second_thread.join(timeout=10)
1120
1068
 
1121
- assert all_errors == []
1069
+ assert errors == []
1122
1070
  assert call_log.claude_calls == 2
1123
1071
  assert set(call_log.all_observed_working_directories) == {
1124
- first_working_directory,
1125
- second_working_directory,
1072
+ first_working_directory, second_working_directory
1126
1073
  }
1127
- assert chain_runner.chain_subprocess_runner is not None
1128
1074
 
1129
1075
 
1130
- def test_shared_chain_runner_lock_serializes_cross_dispatcher_cwds(
1076
+ def test_code_review_and_dispatcher_forward_their_own_cwds(
1131
1077
  monkeypatch: pytest.MonkeyPatch, tmp_path: Path
1132
1078
  ) -> None:
1133
- first_working_directory = tmp_path / "project-a"
1134
- second_working_directory = tmp_path / "project-b"
1079
+ first_working_directory = tmp_path / "review"
1080
+ second_working_directory = tmp_path / "spawn"
1135
1081
  first_working_directory.mkdir()
1136
1082
  second_working_directory.mkdir()
1137
- observed_working_directories: list[Path] = []
1138
- all_errors: list[BaseException] = []
1139
-
1140
- class CoordinatedLock:
1141
- def __init__(self) -> None:
1142
- self._lock = threading.Lock()
1143
- self.first_acquired = threading.Event()
1144
- self.second_attempted = threading.Event()
1145
- self.enter_count = 0
1146
-
1147
- def __enter__(self) -> Self:
1148
- self.enter_count += 1
1149
- if self.enter_count == 1:
1150
- self.first_acquired.set()
1151
- elif self.enter_count == 2:
1152
- self.second_attempted.set()
1153
- self._lock.acquire()
1154
- return self
1155
-
1156
- def __exit__(self, *all_positionals: object) -> None:
1157
- del all_positionals
1158
- self._lock.release()
1159
-
1160
- coordinated_lock = CoordinatedLock()
1161
-
1162
- def tracking_subprocess_runner(
1163
- all_invocation_tokens: Sequence[str],
1164
- *all_positionals: object,
1165
- **all_keywords: object,
1166
- ) -> subprocess.CompletedProcess[str]:
1167
- del all_invocation_tokens, all_positionals
1168
- observed_working_directories.append(Path(str(all_keywords["cwd"])))
1169
- return subprocess.CompletedProcess(
1170
- args=["claude"], returncode=0, stdout="{}", stderr=""
1171
- )
1083
+ observed: list[Path] = []
1172
1084
 
1173
- def run_review_runner(
1174
- all_claude_arguments: list[str], *, timeout_seconds: int
1175
- ) -> ChainInvocationOutcome:
1176
- del all_claude_arguments, timeout_seconds
1177
- if not coordinated_lock.second_attempted.wait(
1178
- timeout=LOCK_WAIT_TIMEOUT_SECONDS
1179
- ):
1180
- all_errors.append(AssertionError("shared lock was never contended"))
1181
- return _claude_served()
1182
- chain_runner.chain_subprocess_runner(
1183
- ["claude"], capture_output=True, text=True, timeout=1, check=False
1184
- )
1085
+ def runner(product: Product, argv: list[str], **options: object) -> JobOutcome:
1086
+ assert product is Product.CLAUDE
1087
+ observed.append(Path(options["cwd"]))
1185
1088
  return _claude_served()
1186
1089
 
1187
- def run_spawn_runner(
1188
- all_claude_arguments: list[str], *, timeout_seconds: int
1189
- ) -> ChainInvocationOutcome:
1190
- del all_claude_arguments, timeout_seconds
1191
- chain_runner.chain_subprocess_runner(
1192
- ["claude"], capture_output=True, text=True, timeout=1, check=False
1193
- )
1194
- return _claude_served()
1090
+ monkeypatch.setattr(invoker, "review_claude_runner", runner)
1091
+ monkeypatch.setattr(dispatcher, "spawn_claude_runner", runner)
1195
1092
 
1196
- monkeypatch.setattr(
1197
- chain_runner, "chain_subprocess_runner", tracking_subprocess_runner
1093
+ invoker._run_claude_with_empty_stdin(
1094
+ ["-p", "review"], timeout_seconds=1, working_directory=first_working_directory
1198
1095
  )
1199
- monkeypatch.setattr(
1200
- chain_runner, "chain_subprocess_runner_lock", lambda: coordinated_lock,
1201
- raising=False,
1096
+ dispatcher._run_claude_with_headless_overrides(
1097
+ ["-p", "spawn"],
1098
+ timeout_seconds=1,
1099
+ working_directory=second_working_directory,
1100
+ prompt_text=FIXTURE_PROMPT_TEXT,
1202
1101
  )
1203
- monkeypatch.setattr(invoker, "review_claude_runner", run_review_runner)
1204
- monkeypatch.setattr(dispatcher, "spawn_claude_runner", run_spawn_runner)
1205
1102
 
1206
- def run_review() -> None:
1207
- try:
1208
- invoker._run_claude_with_empty_stdin(
1209
- ["-p", "review"],
1210
- timeout_seconds=1,
1211
- working_directory=first_working_directory,
1212
- )
1213
- except Exception as raised_error: # noqa: BLE001
1214
- all_errors.append(raised_error)
1103
+ assert observed == [first_working_directory, second_working_directory]
1215
1104
 
1216
- def run_spawn() -> None:
1217
- try:
1218
- dispatcher._run_claude_with_headless_overrides(
1219
- ["-p", "spawn"],
1220
- timeout_seconds=1,
1221
- working_directory=second_working_directory,
1222
- prompt_stdin=io.StringIO(FIXTURE_PROMPT_TEXT),
1223
- )
1224
- except Exception as raised_error: # noqa: BLE001
1225
- all_errors.append(raised_error)
1226
-
1227
- review_thread = threading.Thread(target=run_review)
1228
- spawn_thread = threading.Thread(target=run_spawn)
1229
- review_thread.start()
1230
- coordinated_lock.first_acquired.wait(timeout=LOCK_WAIT_TIMEOUT_SECONDS)
1231
- spawn_thread.start()
1232
- review_thread.join(timeout=10)
1233
- spawn_thread.join(timeout=10)
1234
-
1235
- assert all_errors == []
1236
- assert coordinated_lock.enter_count == 2
1237
- assert observed_working_directories == [
1238
- first_working_directory,
1239
- second_working_directory,
1240
- ]
1241
- assert chain_runner.chain_subprocess_runner is tracking_subprocess_runner
1242
1105
 
1243
-
1244
- def test_usage_limit_fallover_delivers_full_prompt_to_each_binary(
1106
+ def test_usage_limit_fallover_delivers_full_prompt_to_each_account(
1245
1107
  monkeypatch: pytest.MonkeyPatch, tmp_path: Path
1246
1108
  ) -> None:
1247
1109
  prompt_file, working_directory, run_state_directory = _paths(tmp_path)
1248
- monkeypatch.setattr(
1249
- dispatcher,
1250
- "spawn_preflight_runner",
1251
- lambda **_keyword_arguments: _fallthrough_preflight(REASON_GROK_AUTH_FAILED),
1252
- )
1253
- monkeypatch.setattr(
1254
- dispatcher,
1255
- "spawn_host_profile_detector",
1256
- lambda *all_positionals, **all_keywords: HOST_PROFILE_THIRD_PARTY,
1110
+ accounts = (
1111
+ Account(Product.CLAUDE, "first", tmp_path / "first"),
1112
+ Account(Product.CLAUDE, "second", tmp_path / "second"),
1257
1113
  )
1258
- monkeypatch.setattr(
1259
- chain_runner,
1260
- "load_chain",
1261
- lambda _config_path: [
1262
- chain_runner.ChainEntry(command="claude", extra_args=()),
1263
- chain_runner.ChainEntry(command="claude-profile-c", extra_args=()),
1264
- ],
1114
+ reset_at = datetime(2026, 10, 5, tzinfo=timezone.utc)
1115
+ adapter = ProductAdapter(
1116
+ lambda: accounts,
1117
+ lambda account: Meters(80, reset_at, 80, reset_at),
1118
+ "CLAUDE_CONFIG_DIR",
1119
+ ("usage limit",),
1120
+ False,
1265
1121
  )
1266
- prompt_text_by_command: dict[str, str] = {}
1122
+ observed: list[tuple[str, bytes]] = []
1267
1123
 
1268
- def _reading_subprocess_runner(
1269
- all_invocation_tokens: Sequence[str],
1270
- *all_positionals: object,
1271
- **all_keywords: object,
1272
- ) -> subprocess.CompletedProcess[str]:
1273
- del all_positionals
1274
- command_name = str(all_invocation_tokens[0])
1275
- read_prompt = getattr(all_keywords.get("stdin"), "read", None)
1276
- prompt_text_by_command[command_name] = read_prompt() if read_prompt else ""
1277
- is_primary = command_name == "claude"
1278
- return subprocess.CompletedProcess(
1279
- args=list(all_invocation_tokens),
1280
- returncode=(
1281
- FIXTURE_FAILED_RETURNCODE if is_primary else SPAWN_SERVED_EXIT_CODE
1282
- ),
1283
- stdout="usage limit reached" if is_primary else FIXTURE_CLAUDE_STDOUT,
1284
- stderr="",
1285
- )
1124
+ def runner(argv: list[str], **options: object) -> subprocess.CompletedProcess[str]:
1125
+ account_name = Path(options["env"]["CLAUDE_CONFIG_DIR"]).name
1126
+ observed.append((account_name, options["input"]))
1127
+ if account_name == "first":
1128
+ return subprocess.CompletedProcess(argv, 1, "usage limit reached", "")
1129
+ return subprocess.CompletedProcess(argv, 0, FIXTURE_CLAUDE_STDOUT, "")
1286
1130
 
1131
+ monkeypatch.setitem(account_broker.all_product_adapters, Product.CLAUDE, adapter)
1132
+ monkeypatch.setattr(account_broker, "broker_state_path", lambda: tmp_path / "state.json")
1287
1133
  monkeypatch.setattr(
1288
- chain_runner, "chain_subprocess_runner", _reading_subprocess_runner
1134
+ dispatcher,
1135
+ "spawn_preflight_runner",
1136
+ lambda **kwargs: _fallthrough_preflight(REASON_GROK_AUTH_FAILED),
1289
1137
  )
1290
-
1291
- outcome = dispatcher.resolve_worker_spawn(
1292
- role=DEFAULT_ROLE,
1293
- prompt_file=prompt_file,
1294
- working_directory=working_directory,
1295
- timeout_seconds=DEFAULT_WORKER_TIMEOUT_SECONDS,
1296
- is_claude_tier_enabled=False,
1297
- run_state_directory=run_state_directory,
1138
+ monkeypatch.setattr(
1139
+ dispatcher, "spawn_host_profile_detector", lambda: HOST_PROFILE_THIRD_PARTY
1298
1140
  )
1141
+ with account_broker.override_subprocess_runner(runner):
1142
+ outcome = dispatcher.resolve_worker_spawn(
1143
+ role=DEFAULT_ROLE,
1144
+ prompt_file=prompt_file,
1145
+ working_directory=working_directory,
1146
+ timeout_seconds=DEFAULT_WORKER_TIMEOUT_SECONDS,
1147
+ is_claude_tier_enabled=False,
1148
+ run_state_directory=run_state_directory,
1149
+ )
1299
1150
 
1300
- assert prompt_text_by_command["claude"] == FIXTURE_PROMPT_TEXT
1301
- assert prompt_text_by_command["claude-profile-c"] == FIXTURE_PROMPT_TEXT
1151
+ assert observed == [
1152
+ ("first", FIXTURE_PROMPT_TEXT.encode("utf-8")),
1153
+ ("second", FIXTURE_PROMPT_TEXT.encode("utf-8")),
1154
+ ]
1302
1155
  assert outcome.tier_used == TIER_CLAUDE_HEADLESS
1303
1156
  assert outcome.is_ok is True
1304
1157
 
@@ -24,12 +24,9 @@ FIXTURE_PROMPT_TEXT = "do the work"
24
24
 
25
25
  def _install_dispatcher_codec_seams(
26
26
  monkeypatch: pytest.MonkeyPatch,
27
- *,
28
- all_chain_codec_keywords: dict[str, str],
29
27
  ) -> dict[str, object]:
30
28
  return install_codec_seams(
31
29
  monkeypatch,
32
- all_chain_codec_keywords=all_chain_codec_keywords,
33
30
  chain_stdout=FIXTURE_CHAIN_STDOUT,
34
31
  runner_host=dispatcher,
35
32
  runner_attribute_name="spawn_claude_runner",
@@ -42,16 +39,12 @@ def _run_headless_with_prompt(
42
39
  working_directory: Path,
43
40
  timeout_seconds: int = DEFAULT_WORKER_TIMEOUT_SECONDS,
44
41
  ) -> None:
45
- prompt_stdin = prompt_file.open(encoding=UTF8_ENCODING)
46
- try:
47
- dispatcher._run_claude_with_headless_overrides(
48
- [],
49
- timeout_seconds=timeout_seconds,
50
- working_directory=working_directory,
51
- prompt_stdin=prompt_stdin,
52
- )
53
- finally:
54
- prompt_stdin.close()
42
+ dispatcher._run_claude_with_headless_overrides(
43
+ [],
44
+ timeout_seconds=timeout_seconds,
45
+ working_directory=working_directory,
46
+ prompt_text=prompt_file.read_text(encoding=UTF8_ENCODING),
47
+ )
55
48
 
56
49
 
57
50
  def test_forwards_text_codec_keywords_to_subprocess_runner(
@@ -61,10 +54,6 @@ def test_forwards_text_codec_keywords_to_subprocess_runner(
61
54
  prompt_file.write_text(FIXTURE_PROMPT_TEXT, encoding=UTF8_ENCODING)
62
55
  all_observed_runner_keywords = _install_dispatcher_codec_seams(
63
56
  monkeypatch,
64
- all_chain_codec_keywords={
65
- FIXTURE_ENCODING_KEYWORD_NAME: FIXTURE_CHAIN_ENCODING,
66
- FIXTURE_ERRORS_KEYWORD_NAME: FIXTURE_CHAIN_ERRORS,
67
- },
68
57
  )
69
58
 
70
59
  _run_headless_with_prompt(
@@ -82,14 +71,13 @@ def test_forwards_text_codec_keywords_to_subprocess_runner(
82
71
  )
83
72
 
84
73
 
85
- def test_absent_text_codec_keywords_are_not_invented(
74
+ def test_prompt_and_cwd_reach_broker(
86
75
  monkeypatch: pytest.MonkeyPatch, tmp_path: Path
87
76
  ) -> None:
88
77
  prompt_file = tmp_path / "prompt.txt"
89
78
  prompt_file.write_text(FIXTURE_PROMPT_TEXT, encoding=UTF8_ENCODING)
90
79
  all_observed_runner_keywords = _install_dispatcher_codec_seams(
91
80
  monkeypatch,
92
- all_chain_codec_keywords={},
93
81
  )
94
82
 
95
83
  _run_headless_with_prompt(
@@ -97,5 +85,5 @@ def test_absent_text_codec_keywords_are_not_invented(
97
85
  working_directory=tmp_path,
98
86
  )
99
87
 
100
- assert FIXTURE_ENCODING_KEYWORD_NAME not in all_observed_runner_keywords
101
- assert FIXTURE_ERRORS_KEYWORD_NAME not in all_observed_runner_keywords
88
+ assert all_observed_runner_keywords["stdin_text"] == FIXTURE_PROMPT_TEXT
89
+ assert all_observed_runner_keywords["cwd"] == tmp_path
@@ -23,7 +23,7 @@ def _permission_mode_seen_by(
23
23
  monkeypatch: pytest.MonkeyPatch, effective_user_id: int
24
24
  ) -> str:
25
25
  """Read the review permission mode a caller with this user id resolves."""
26
- monkeypatch.setattr(os, "geteuid", lambda: effective_user_id)
26
+ monkeypatch.setattr(os, "geteuid", lambda: effective_user_id, raising=False)
27
27
  reloaded_constants = importlib.reload(code_review_constants)
28
28
  return str(reloaded_constants.REVIEW_PERMISSION_MODE)
29
29
 
@@ -31,7 +31,7 @@ def _permission_mode_seen_by(
31
31
  def test_root_detection_agrees_with_this_process(
32
32
  monkeypatch: pytest.MonkeyPatch,
33
33
  ) -> None:
34
- monkeypatch.setattr(os, "geteuid", lambda: ROOT_USER_ID)
34
+ monkeypatch.setattr(os, "geteuid", lambda: ROOT_USER_ID, raising=False)
35
35
  reloaded_constants = importlib.reload(code_review_constants)
36
36
 
37
37
  assert reloaded_constants.IS_ROOT_CALLER is True