claude-dev-env 2.36.0 → 4.1.0

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 (84) hide show
  1. package/.agents/skills/_shared/advisor/scripts/README.md +1 -1
  2. package/.agents/skills/syncing-submodules/SKILL.md +101 -0
  3. package/.agents/skills/syncing-submodules/reference/run-tasks.md +9 -0
  4. package/.agents/skills/syncing-submodules/scripts/submodule_sync.py +383 -0
  5. package/.agents/skills/syncing-submodules/scripts/submodule_sync_constants/__init__.py +1 -0
  6. package/.agents/skills/syncing-submodules/scripts/submodule_sync_constants/config/__init__.py +1 -0
  7. package/.agents/skills/syncing-submodules/scripts/submodule_sync_constants/config/constants.py +66 -0
  8. package/.agents/skills/syncing-submodules/scripts/sync_parent_pointer.py +88 -0
  9. package/.agents/skills/syncing-submodules/scripts/test_submodule_sync.py +303 -0
  10. package/.agents/skills/syncing-submodules/scripts/test_sync_parent_pointer.py +83 -0
  11. package/.agents/skills/team-advisor/SKILL.md +11 -11
  12. package/_shared/advisor/advisor-protocol.md +51 -51
  13. package/_shared/advisor/reference/advisor-block.md +9 -9
  14. package/_shared/advisor/reference/astra-rung.md +39 -0
  15. package/_shared/advisor/reference/identity.md +4 -4
  16. package/_shared/advisor/reference/lifecycle.md +6 -6
  17. package/_shared/advisor/reference/spawn-walk-log.md +9 -9
  18. package/_shared/advisor/reference/third-party-bind.md +11 -11
  19. package/_shared/advisor/reference/warm-up.md +13 -13
  20. package/_shared/advisor/scripts/codex_astra_advisor.py +308 -0
  21. package/_shared/advisor/scripts/codex_astra_preflight.py +115 -0
  22. package/_shared/advisor/scripts/codex_astra_reply.py +159 -0
  23. package/_shared/advisor/scripts/config/advisor_scripts_constants/advisor_route_constants.py +3 -3
  24. package/_shared/advisor/scripts/config/advisor_scripts_constants/astra_advisor_constants.py +38 -0
  25. package/_shared/advisor/scripts/model_tier_run_validator.py +17 -18
  26. package/_shared/advisor/scripts/tests/test_codex_astra_advisor.py +271 -0
  27. package/_shared/advisor/scripts/tests/test_codex_astra_preflight.py +24 -0
  28. package/_shared/advisor/scripts/tests/test_codex_astra_reply.py +42 -0
  29. package/_shared/advisor/scripts/tests/test_model_tier_run_validator.py +55 -30
  30. package/_shared/advisor/scripts/tests/test_tier_model_ids.py +14 -4
  31. package/_shared/pr-loop/scripts/pr_loop_shared_constants/terminology_sweep_constants.py +14 -0
  32. package/_shared/pr-loop/scripts/terminology_sweep.py +138 -50
  33. package/_shared/pr-loop/scripts/tests/test_terminology_sweep.py +13 -1
  34. package/bin/ever-shipped-skills.mjs +1 -0
  35. package/bin/ever-shipped-skills.test.mjs +30 -0
  36. package/bin/git_hooks_installer.mjs +0 -1
  37. package/bin/git_hooks_installer.test.mjs +16 -1
  38. package/bin/install.mjs +5 -2
  39. package/bin/install.prune.test.mjs +36 -24
  40. package/bin/install.test.mjs +7 -0
  41. package/docs/references/team-advisor-skill.md +3 -3
  42. package/hooks/validators/README.md +35 -1
  43. package/package.json +1 -1
  44. package/scripts/check.ps1 +18 -6
  45. package/scripts/policy_lint/adapter_detectors.py +42 -2
  46. package/scripts/policy_lint/adapters.py +24 -0
  47. package/scripts/policy_lint/config/constants.py +11 -0
  48. package/scripts/policy_lint/engine.py +1 -1
  49. package/scripts/policy_lint/model.py +1 -0
  50. package/scripts/policy_lint/registry.py +10 -1
  51. package/scripts/repository_checks/__init__.py +1 -0
  52. package/scripts/repository_checks/claude_md.py +108 -0
  53. package/scripts/repository_checks/config/__init__.py +1 -0
  54. package/scripts/repository_checks/config/constants.py +150 -0
  55. package/scripts/repository_checks/env_var_documentation.py +243 -0
  56. package/scripts/repository_checks/hook_modules.py +34 -0
  57. package/scripts/repository_checks/models.py +37 -0
  58. package/scripts/repository_checks/package_inventory.py +118 -0
  59. package/scripts/repository_checks/pytest_testpaths.py +84 -0
  60. package/scripts/repository_checks/reporting.py +39 -0
  61. package/scripts/repository_checks/runner.py +93 -0
  62. package/scripts/repository_checks/tracked_secrets.py +147 -0
  63. package/scripts/repository_checks/tracked_tree.py +28 -0
  64. package/scripts/repository_policy.py +120 -0
  65. package/scripts/tests/repository_policy_test_support.py +89 -0
  66. package/scripts/tests/test_cde_lint.py +25 -1
  67. package/scripts/tests/test_claude_md.py +68 -0
  68. package/scripts/tests/test_env_var_documentation.py +133 -0
  69. package/scripts/tests/test_hook_modules.py +17 -0
  70. package/scripts/tests/test_models.py +26 -0
  71. package/scripts/tests/test_package_inventory.py +96 -0
  72. package/scripts/tests/test_policy_lint_rules_registry.py +117 -0
  73. package/scripts/tests/test_pytest_testpaths.py +66 -0
  74. package/scripts/tests/test_reporting.py +54 -0
  75. package/scripts/tests/test_repository_policy.py +71 -0
  76. package/scripts/tests/test_runner.py +59 -0
  77. package/scripts/tests/test_tracked_secrets.py +175 -0
  78. package/scripts/tests/test_tracked_tree.py +28 -0
  79. package/_shared/advisor/reference/sol-rung.md +0 -39
  80. package/_shared/advisor/scripts/codex_sol_advisor.py +0 -581
  81. package/_shared/advisor/scripts/config/advisor_scripts_constants/sol_advisor_constants.py +0 -38
  82. package/_shared/advisor/scripts/tests/test_codex_sol_advisor.py +0 -740
  83. package/hooks/git-hooks/post_commit.py +0 -241
  84. package/hooks/git-hooks/test_post_commit.py +0 -298
@@ -0,0 +1,115 @@
1
+ """Evaluate Codex usage before an Astra advisor request."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import importlib
6
+ import json
7
+ import math
8
+ import subprocess
9
+ import sys
10
+ from collections.abc import Callable
11
+ from dataclasses import dataclass
12
+ from pathlib import Path
13
+
14
+ from advisor_scripts_constants.astra_advisor_constants import (
15
+ ASTRA_FALLBACK_KIND_BROKEN,
16
+ ASTRA_FALLBACK_KIND_DECLINED,
17
+ ASTRA_PREFLIGHT_FAILURE_REASON,
18
+ ASTRA_PROBE_TIMEOUT_REASON,
19
+ ASTRA_USAGE_PROBE_TIMEOUT_SECONDS,
20
+ )
21
+
22
+
23
+ @dataclass(frozen=True)
24
+ class AstraPreflight:
25
+ eligible: bool
26
+ percent_left: float | None
27
+ reason: str
28
+ fallback_kind: str | None = None
29
+
30
+
31
+ def _preflight_fallback(
32
+ reason: str, percent_left: float | None, fallback_kind: str
33
+ ) -> AstraPreflight:
34
+ return AstraPreflight(False, percent_left, reason, fallback_kind)
35
+
36
+
37
+ def _load_usage_gate(probe_path: Path) -> Callable[[float], bool]:
38
+ probe_directory = str(probe_path.parent)
39
+ if probe_directory not in sys.path:
40
+ sys.path.insert(0, probe_directory)
41
+ return importlib.import_module("codex_usage_probe").is_codex_review_required
42
+
43
+
44
+ def _parse_probe_percent(stdout_text: str) -> tuple[float | None, str | None]:
45
+ try:
46
+ report = json.loads(stdout_text)
47
+ except (TypeError, json.JSONDecodeError):
48
+ return None, "usage report is malformed"
49
+ if not isinstance(report, dict):
50
+ return None, "usage report is malformed"
51
+ raw_percent = report.get("percent_left")
52
+ if isinstance(raw_percent, bool) or not isinstance(raw_percent, (int, float)):
53
+ return None, "usage meter is unknown" if raw_percent is None else "usage meter is malformed"
54
+ percent_left = float(raw_percent)
55
+ if not math.isfinite(percent_left):
56
+ return None, "usage meter is malformed"
57
+ return percent_left, None
58
+
59
+
60
+ def _run_probe(
61
+ probe_path: Path,
62
+ process_runner: Callable[..., subprocess.CompletedProcess[str]],
63
+ ) -> subprocess.CompletedProcess[str]:
64
+ return process_runner(
65
+ [sys.executable, str(probe_path)],
66
+ capture_output=True,
67
+ text=True,
68
+ check=False,
69
+ shell=False,
70
+ timeout=ASTRA_USAGE_PROBE_TIMEOUT_SECONDS,
71
+ )
72
+
73
+
74
+ def _preflight_from_probe(
75
+ probe_path: Path, completed: subprocess.CompletedProcess[str]
76
+ ) -> AstraPreflight:
77
+ if completed.returncode != 0:
78
+ reason = f"{ASTRA_PREFLIGHT_FAILURE_REASON}: probe exit {completed.returncode}"
79
+ return _preflight_fallback(reason, None, ASTRA_FALLBACK_KIND_BROKEN)
80
+ percent_left, parse_reason = _parse_probe_percent(completed.stdout)
81
+ if parse_reason is not None or percent_left is None:
82
+ reason = f"{ASTRA_PREFLIGHT_FAILURE_REASON}: {parse_reason or 'usage meter is unknown'}"
83
+ return _preflight_fallback(reason, None, ASTRA_FALLBACK_KIND_BROKEN)
84
+ if not _load_usage_gate(probe_path)(percent_left):
85
+ reason = f"{ASTRA_PREFLIGHT_FAILURE_REASON}: usage meter is at or below the gate"
86
+ return _preflight_fallback(reason, percent_left, ASTRA_FALLBACK_KIND_DECLINED)
87
+ return AstraPreflight(True, percent_left, "usage meter is above the Astra gate")
88
+
89
+
90
+ def run_astra_preflight(
91
+ probe_path: Path,
92
+ process_runner: Callable[..., subprocess.CompletedProcess[str]],
93
+ ) -> AstraPreflight:
94
+ """Run the usage probe and decide whether Astra may bind.
95
+
96
+ Args:
97
+ probe_path: Path to the installed usage probe.
98
+ process_runner: Callable that runs the probe.
99
+
100
+ Returns:
101
+ Eligibility, remaining usage percent, reason, and fallback kind.
102
+ """
103
+ try:
104
+ completed = _run_probe(probe_path, process_runner)
105
+ return _preflight_from_probe(probe_path, completed)
106
+ except subprocess.TimeoutExpired as error:
107
+ return _preflight_fallback(
108
+ f"{ASTRA_PROBE_TIMEOUT_REASON}: {error}", None, ASTRA_FALLBACK_KIND_BROKEN
109
+ )
110
+ except (OSError, subprocess.SubprocessError, ImportError, AttributeError, TypeError, ValueError) as error:
111
+ return _preflight_fallback(
112
+ f"{ASTRA_PREFLIGHT_FAILURE_REASON}: {error}",
113
+ None,
114
+ ASTRA_FALLBACK_KIND_BROKEN,
115
+ )
@@ -0,0 +1,159 @@
1
+ """Parse Codex JSONL and build Astra advisor replies."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import json
6
+ from dataclasses import dataclass
7
+
8
+ from advisor_scripts_constants.advisor_route_constants import (
9
+ ADVISOR_FALLBACK_RESULT,
10
+ ADVISOR_FALLBACK_TIER,
11
+ ADVISOR_MODEL_TIER,
12
+ ALL_ADVISOR_GUIDANCE_SIGNALS,
13
+ CODEX_BIND_SUCCESS_TOKEN,
14
+ )
15
+ from advisor_scripts_constants.astra_advisor_constants import (
16
+ ASTRA_FALLBACK_KIND_BROKEN,
17
+ ASTRA_INVALID_SIGNAL_REASON,
18
+ ASTRA_MALFORMED_JSONL_REASON,
19
+ ASTRA_MISSING_SESSION_REASON,
20
+ ASTRA_REPLY_FAILURE_REASON,
21
+ )
22
+
23
+
24
+ @dataclass(frozen=True)
25
+ class CodexAstraAdvisorReply:
26
+ session_id: str | None
27
+ guidance: str | None
28
+ successful: bool
29
+ reason: str | None
30
+ is_fallback: bool
31
+ signal: str | None
32
+ astra_enabled: bool
33
+ selected_tier: str
34
+ outcome: str
35
+ fallback_kind: str | None
36
+
37
+
38
+ def build_fallback_reply(
39
+ reason: str,
40
+ is_astra_enabled: bool,
41
+ fallback_kind: str | None = ASTRA_FALLBACK_KIND_BROKEN,
42
+ ) -> CodexAstraAdvisorReply:
43
+ """Build a failed advisor reply.
44
+
45
+ Args:
46
+ reason: Why the request fell back.
47
+ is_astra_enabled: True when the Astra flag was on.
48
+ fallback_kind: Kind of fallback.
49
+
50
+ Returns:
51
+ Failed reply with no session or guidance.
52
+ """
53
+ return CodexAstraAdvisorReply(
54
+ None,
55
+ None,
56
+ False,
57
+ reason,
58
+ True,
59
+ None,
60
+ is_astra_enabled,
61
+ ADVISOR_FALLBACK_TIER,
62
+ ADVISOR_FALLBACK_RESULT,
63
+ fallback_kind,
64
+ )
65
+
66
+
67
+ def build_success_reply(
68
+ session_id: str, guidance: str, signal: str
69
+ ) -> CodexAstraAdvisorReply:
70
+ """Build a successful advisor reply.
71
+
72
+ Args:
73
+ session_id: Codex session id.
74
+ guidance: Advisor text.
75
+ signal: First-line guidance signal.
76
+
77
+ Returns:
78
+ Successful reply with Astra enabled.
79
+ """
80
+ return CodexAstraAdvisorReply(
81
+ session_id,
82
+ guidance,
83
+ True,
84
+ None,
85
+ False,
86
+ signal,
87
+ True,
88
+ ADVISOR_MODEL_TIER,
89
+ CODEX_BIND_SUCCESS_TOKEN,
90
+ None,
91
+ )
92
+
93
+
94
+ def _guidance_signal(guidance: str) -> str | None:
95
+ for each_line in guidance.splitlines():
96
+ candidate = each_line.strip()
97
+ if candidate:
98
+ return candidate if candidate in ALL_ADVISOR_GUIDANCE_SIGNALS else None
99
+ return None
100
+
101
+
102
+ def _parse_event_line(each_line: str) -> tuple[str | None, str | None]:
103
+ event = json.loads(each_line)
104
+ if not isinstance(event, dict):
105
+ raise TypeError("event must be an object")
106
+ discovered_session_id: str | None = None
107
+ discovered_guidance: str | None = None
108
+ if event.get("type") == "thread.started":
109
+ raw_session = event.get("thread_id")
110
+ if isinstance(raw_session, str) and raw_session.strip():
111
+ discovered_session_id = raw_session.strip()
112
+ completed_event = event.get("item")
113
+ if event.get("type") == "item.completed" and isinstance(completed_event, dict):
114
+ message_text = completed_event.get("text")
115
+ if completed_event.get("type") == "agent_message" and isinstance(message_text, str):
116
+ discovered_guidance = message_text.strip()
117
+ return discovered_session_id, discovered_guidance
118
+
119
+
120
+ def _collect_reply_parts(jsonl_text: str) -> tuple[str | None, str | None]:
121
+ session_id: str | None = None
122
+ guidance: str | None = None
123
+ for each_line in jsonl_text.splitlines():
124
+ if each_line.strip():
125
+ discovered_session_id, discovered_guidance = _parse_event_line(each_line)
126
+ session_id = discovered_session_id or session_id
127
+ guidance = discovered_guidance or guidance
128
+ return session_id, guidance
129
+
130
+
131
+ def parse_codex_jsonl_reply(
132
+ jsonl_text: str,
133
+ existing_session_id: str | None,
134
+ is_astra_enabled: bool,
135
+ fallback_kind: str | None = None,
136
+ ) -> CodexAstraAdvisorReply:
137
+ """Parse Codex JSONL into an Astra advisor reply.
138
+
139
+ Args:
140
+ jsonl_text: JSONL from Codex stdout.
141
+ existing_session_id: Session expected on resume, or None on bind.
142
+ is_astra_enabled: True when the Astra flag was on.
143
+ fallback_kind: Kind of fallback on a failed reply.
144
+
145
+ Returns:
146
+ Advisor reply, or a fallback.
147
+ """
148
+ try:
149
+ session_id, guidance = _collect_reply_parts(jsonl_text)
150
+ except (TypeError, json.JSONDecodeError):
151
+ return build_fallback_reply(ASTRA_MALFORMED_JSONL_REASON, is_astra_enabled, fallback_kind)
152
+ if session_id is None or (existing_session_id is not None and session_id != existing_session_id):
153
+ return build_fallback_reply(ASTRA_MISSING_SESSION_REASON, is_astra_enabled, fallback_kind)
154
+ if not guidance:
155
+ return build_fallback_reply(ASTRA_REPLY_FAILURE_REASON, is_astra_enabled, fallback_kind)
156
+ signal = _guidance_signal(guidance)
157
+ if signal is None:
158
+ return build_fallback_reply(ASTRA_INVALID_SIGNAL_REASON, is_astra_enabled, fallback_kind)
159
+ return build_success_reply(session_id, guidance, signal)
@@ -1,9 +1,9 @@
1
- """Shared advisor_route_constants for advisor selection and replies."""
1
+ """Shared constants for advisor selection and replies."""
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
- ADVISOR_MODEL_TIER: str = "Sol"
6
- ADVISOR_CODEX_MODEL_ID: str = "gpt-5.6-sol"
5
+ ADVISOR_MODEL_TIER: str = "Astra"
6
+ ADVISOR_CODEX_MODEL_ID: str = "gpt-6-astra"
7
7
  ADVISOR_FALLBACK_TIER: str = "Fable"
8
8
  ADVISOR_FALLBACK_RESULT: str = "fable"
9
9
  ADVISOR_EFFORT_ENV_VAR: str = "ADVISOR_EFFORT"
@@ -0,0 +1,38 @@
1
+ """Constants for the optional Codex Astra advisor bind."""
2
+
3
+ from __future__ import annotations
4
+
5
+ CODEX_EXECUTABLE: str = "codex"
6
+ ADVISOR_CODEX_EXECUTABLE_ENV_VAR: str = "ADVISOR_CODEX_EXECUTABLE"
7
+ CODEX_READ_ONLY_SANDBOX: str = "read-only"
8
+ CODEX_JSON_FLAG: str = "--json"
9
+ CODEX_MODEL_FLAG: str = "--model"
10
+ CODEX_CONFIG_FLAG: str = "--config"
11
+ CODEX_REASONING_CONFIG_TEMPLATE: str = 'model_reasoning_effort="{effort}"'
12
+ CODEX_PROMPT_FROM_STDIN: str = "-"
13
+ CODEX_EXEC_SUBCOMMAND: str = "exec"
14
+ CODEX_RESUME_SUBCOMMAND: str = "resume"
15
+ CODEX_SANDBOX_FLAG: str = "--sandbox"
16
+ CLAUDE_CONFIG_DIRECTORY_NAME: str = ".claude"
17
+ USAGE_PROBE_SHARED_DIRECTORY_NAME: str = "_shared"
18
+ USAGE_PROBE_PACKAGE_DIRECTORY_NAME: str = "pr-loop"
19
+ USAGE_PROBE_SCRIPTS_DIRECTORY_NAME: str = "scripts"
20
+ USAGE_PROBE_FILENAME: str = "codex_usage_probe.py"
21
+ ASTRA_SESSION_ID_METAVAR: str = "SESSION_ID"
22
+ ASTRA_CODEX_TIMEOUT_SECONDS: float = 120.0
23
+ ASTRA_USAGE_PROBE_TIMEOUT_SECONDS: float = 30.0
24
+ ASTRA_ENV_VAR: str = "ADVISOR_ASTRA"
25
+ ALL_ASTRA_TRUTHY_VALUES: frozenset[str] = frozenset({"1", "true", "yes", "on"})
26
+ ASTRA_PREFLIGHT_FAILURE_REASON: str = "astra preflight did not establish an eligible Codex meter"
27
+ ASTRA_BIND_FAILURE_REASON: str = "Codex Astra bind failed"
28
+ ASTRA_REPLY_FAILURE_REASON: str = "Codex Astra returned no advisor guidance"
29
+ ASTRA_PROBE_TIMEOUT_REASON: str = "Codex Astra meter check timed out"
30
+ ASTRA_CODEX_TIMEOUT_REASON: str = "Codex Astra request timed out"
31
+ ASTRA_MALFORMED_JSONL_REASON: str = "Codex Astra returned malformed JSONL"
32
+ ASTRA_MISSING_SESSION_REASON: str = "Codex Astra returned no session id"
33
+ ASTRA_INVALID_SIGNAL_REASON: str = "Codex Astra returned an invalid guidance signal"
34
+ ASTRA_EXECUTABLE_NOT_FOUND_REASON: str = "Codex Astra could not find the codex executable on PATH"
35
+ ASTRA_FALLBACK_KIND_DECLINED: str = "declined"
36
+ ASTRA_FALLBACK_KIND_BROKEN: str = "broken"
37
+ ASTRA_ENABLE_FLAG: str = "--enable-astra"
38
+ ASTRA_EFFORT_FLAG: str = "--effort"
@@ -1,8 +1,7 @@
1
1
  """Mechanically validate a model-tier spawn-walk log.
2
2
 
3
3
  The advisor protocol's Model floor section emits a structured spawn-walk log.
4
- This validator reads that log back and checks its invariants. A run is judged
5
- from data, not inferred from a transcript.
4
+ This validator reads that log back and checks its invariants.
6
5
 
7
6
  ::
8
7
 
@@ -24,12 +23,12 @@ from data, not inferred from a transcript.
24
23
 
25
24
  codex_bind = ModelTierRun(
26
25
  own_tier="Opus",
27
- candidate_tiers=["Sol"],
28
- attempts=[{"tier": "Sol", "result": "spawned"}],
29
- selected_tier="Sol",
26
+ candidate_tiers=["Astra"],
27
+ attempts=[{"tier": "Astra", "result": "spawned"}],
28
+ selected_tier="Astra",
30
29
  host_profile="Codex",
31
30
  )
32
- validate_model_tier_run(codex_bind) # ok: Codex in-session Sol spawn
31
+ validate_model_tier_run(codex_bind) # ok: Codex in-session Astra spawn
33
32
 
34
33
  A run whose selected_tier is not the first successful bind fails.
35
34
  On any broken invariant, validate_model_tier_run raises ModelTierRunError.
@@ -93,7 +92,7 @@ class ModelTierRun:
93
92
  attempts: list[dict[str, str]]
94
93
  selected_tier: str | None
95
94
  fallback_reason: str | None = None
96
- is_sol_enabled: bool = False
95
+ is_astra_enabled: bool = False
97
96
  host_profile: str = HOST_PROFILE_CLAUDE
98
97
 
99
98
 
@@ -113,7 +112,7 @@ def _canonical_tier_list(all_tier_names: list[str]) -> list[str] | None:
113
112
 
114
113
  def _expected_candidate_tiers(
115
114
  own_tier: str,
116
- is_sol_enabled: bool = False,
115
+ is_astra_enabled: bool = False,
117
116
  host_profile: str = HOST_PROFILE_CLAUDE,
118
117
  ) -> list[str]:
119
118
  maybe_canonical_own_tier = canonical_tier_name(own_tier)
@@ -127,7 +126,7 @@ def _expected_candidate_tiers(
127
126
  if maybe_canonical_host == HOST_PROFILE_CODEX:
128
127
  return [ADVISOR_MODEL_TIER]
129
128
  all_expected_candidates = [ADVISOR_FALLBACK_TIER]
130
- if is_sol_enabled:
129
+ if is_astra_enabled:
131
130
  all_expected_candidates.append(ADVISOR_MODEL_TIER)
132
131
  return all_expected_candidates
133
132
 
@@ -164,11 +163,11 @@ def validate_model_tier_run(run: ModelTierRun) -> None:
164
163
  validate_model_tier_run(cli_bind) # ok: CLI Claude-chain bind
165
164
  validate_model_tier_run(broken_log) # flag: ModelTierRunError
166
165
 
167
- Candidate tiers are Fable, plus Sol when ``is_sol_enabled`` is true, on
168
- Claude and ThirdParty hosts. A Codex host walks Sol only. Consumer
169
- ``own_tier`` is recorded and must be a known tier; it does not add Opus
166
+ Candidate tiers are Fable, plus Astra when ``is_astra_enabled`` is true, on
167
+ Claude and ThirdParty hosts. A Codex host walks Astra only. Consumer
168
+ ``own_tier`` is recorded and must be a known tier. It does not add Opus
170
169
  to the advisor walk. Tries walk that list in order. Early stop only
171
- after ``spawned``, ``cli``, or Sol ``codex`` (and Sol ``spawned`` on a
170
+ after ``spawned``, ``cli``, or Astra ``codex`` (and Astra ``spawned`` on a
172
171
  Codex host). A null selected_tier requires a full walk plus
173
172
  fallback_reason.
174
173
 
@@ -183,7 +182,7 @@ def validate_model_tier_run(run: ModelTierRun) -> None:
183
182
  """
184
183
  all_expected_candidates = _expected_candidate_tiers(
185
184
  run.own_tier,
186
- is_sol_enabled=run.is_sol_enabled,
185
+ is_astra_enabled=run.is_astra_enabled,
187
186
  host_profile=run.host_profile,
188
187
  )
189
188
  maybe_canonical_candidates = _canonical_tier_list(run.candidate_tiers)
@@ -257,9 +256,9 @@ def load_model_tier_run_from_json_path(from_path: Path) -> ModelTierRun:
257
256
  TypeError: When a field has the wrong shape.
258
257
  """
259
258
  parsed_payload = json.loads(from_path.read_text(encoding="utf-8"))
260
- raw_sol_enabled = parsed_payload.get("sol_enabled", False)
261
- if not isinstance(raw_sol_enabled, bool):
262
- raise TypeError("sol_enabled must be a boolean")
259
+ raw_astra_enabled = parsed_payload.get("astra_enabled", False)
260
+ if not isinstance(raw_astra_enabled, bool):
261
+ raise TypeError("astra_enabled must be a boolean")
263
262
  raw_host_profile = parsed_payload.get(HOST_PROFILE_JSON_KEY, HOST_PROFILE_CLAUDE)
264
263
  if not isinstance(raw_host_profile, str):
265
264
  raise TypeError(HOST_PROFILE_MUST_BE_STRING_MESSAGE)
@@ -269,7 +268,7 @@ def load_model_tier_run_from_json_path(from_path: Path) -> ModelTierRun:
269
268
  attempts=list(parsed_payload["attempts"]),
270
269
  selected_tier=parsed_payload.get("selected_tier"),
271
270
  fallback_reason=parsed_payload.get("fallback_reason"),
272
- is_sol_enabled=raw_sol_enabled,
271
+ is_astra_enabled=raw_astra_enabled,
273
272
  host_profile=raw_host_profile,
274
273
  )
275
274