claude-dev-env 8.46.0 → 8.46.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.
@@ -27,6 +27,7 @@ The broker stores cached meters, spent marks, and Claude session bindings in one
27
27
  | `0` | `choose` or `check` found an account, or the job exited successfully. |
28
28
  | `3` | No account clears its floor. The decision includes `resets_at`. |
29
29
  | `4` | A Claude job stopped with `advisor_blocked`. |
30
+ | `127` | A Codex job timed out, or its command could not start while no Codex roster is configured. |
30
31
  | Other command code | `run` returns the command's exit code after a served response. |
31
32
 
32
33
  `run` writes a JSON report with `product`, `command`, `events`, and `final_decision`. A wait report has `final_decision.action` equal to `wait` and a UTC `final_decision.resets_at`.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-dev-env",
3
- "version": "8.46.0",
3
+ "version": "8.46.1",
4
4
  "description": "Claude Code development standards — rules, hooks, agents, commands, and skills",
5
5
  "type": "module",
6
6
  "bin": {
@@ -263,7 +263,8 @@ def _attempt_once(context: _RunContext, decision: Decision) -> JobOutcome | None
263
263
  _record_spent_attempt(context, account, status, COMMAND_MISSING_EXIT_CODE)
264
264
  if isinstance(error, subprocess.TimeoutExpired) or not context.all_readings:
265
265
  context.report.final_decision = decision
266
- return JobOutcome(COMMAND_MISSING_EXIT_CODE, "", str(error), account.name, tuple(context.all_attempts), "advisor_blocked", None, None)
266
+ final_status = "advisor_blocked" if context.product is Product.CLAUDE else status
267
+ return JobOutcome(COMMAND_MISSING_EXIT_CODE, "", str(error), account.name, tuple(context.all_attempts), final_status, None, None)
267
268
  return None
268
269
  combined = f"{completion.stdout}{completion.stderr}".casefold()
269
270
  is_limited = completion.returncode != 0 and any(
@@ -435,13 +435,44 @@ def test_should_stop_after_timeout_without_running_job_again(
435
435
 
436
436
  assert invoked_homes == ["first"]
437
437
  assert outcome.attempts == (("first", "timeout"),)
438
- assert outcome.status == "advisor_blocked"
438
+ assert outcome.status == "timeout"
439
439
  assert outcome.account_name == "first"
440
440
  assert report.final_decision.account.name == "first"
441
441
  assert account_broker.main(("choose", "--product", "codex")) == 0
442
442
  assert json.loads(capsys.readouterr().out)["decision"]["account"] == "first"
443
443
 
444
444
 
445
+ @pytest.mark.parametrize(
446
+ ("product", "roster_names", "start_error", "expected_code"),
447
+ (
448
+ (Product.CLAUDE, ("first",), subprocess.TimeoutExpired("job", 1), 4),
449
+ (Product.CODEX, ("first",), subprocess.TimeoutExpired("job", 1), 127),
450
+ (Product.CODEX, (), OSError("missing command"), 127),
451
+ ),
452
+ )
453
+ def test_should_exit_four_only_for_a_blocked_claude_job(
454
+ monkeypatch: pytest.MonkeyPatch,
455
+ tmp_path: Path,
456
+ product: Product,
457
+ roster_names: tuple[str, ...],
458
+ start_error: Exception,
459
+ expected_code: int,
460
+ ) -> None:
461
+ accounts = tuple(_account(each_name, product) for each_name in roster_names)
462
+ monkeypatch.setitem(account_broker.all_product_adapters, product, _adapter(accounts, {
463
+ each_name: _meters(80, 80) for each_name in roster_names
464
+ }))
465
+ monkeypatch.setattr(account_broker.sys, "stdin", io.TextIOWrapper(io.BytesIO(b""), encoding="utf-8"))
466
+
467
+ def runner(argv: object, **options: object) -> subprocess.CompletedProcess[str]:
468
+ raise start_error
469
+
470
+ with account_broker.override_subprocess_runner(runner):
471
+ code = account_broker.main(("run", "--product", product.value, "--report", str(tmp_path / "report.json"), "--", "job"))
472
+
473
+ assert code == expected_code
474
+
475
+
445
476
  def test_should_keep_usage_limited_account_spent_for_later_choices(
446
477
  monkeypatch: pytest.MonkeyPatch, capsys: pytest.CaptureFixture[str]
447
478
  ) -> None: