claude-dev-env 8.13.0 → 8.14.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.
- package/.agents/skills/team-advisor/SKILL.md +1 -1
- package/_shared/advisor/advisor-protocol.md +19 -19
- package/_shared/advisor/reference/advisor-block.md +1 -1
- package/_shared/advisor/reference/astra-rung.md +6 -6
- package/_shared/advisor/reference/identity.md +4 -4
- package/_shared/advisor/reference/lifecycle.md +2 -2
- package/_shared/advisor/reference/spawn-walk-log.md +4 -4
- package/_shared/advisor/reference/third-party-bind.md +6 -6
- package/_shared/advisor/reference/warm-up.md +3 -3
- package/_shared/advisor/scripts/config/advisor_scripts_constants/advisor_route_constants.py +2 -2
- package/_shared/advisor/scripts/config/advisor_scripts_constants/model_tier_run_validator_constants.py +3 -3
- package/_shared/advisor/scripts/model_tier_run_validator.py +8 -9
- package/_shared/advisor/scripts/tests/test_model_tier_run_validator.py +91 -91
- package/package.json +1 -1
|
@@ -24,7 +24,7 @@ One warm advisor at the strongest tier this session can reach. This session is t
|
|
|
24
24
|
## Bind
|
|
25
25
|
|
|
26
26
|
1. Name the session identity first (protocol **Host profiles**), then walk the model floor.
|
|
27
|
-
2. Claude: when `advisor` is in this session's tool list, the built-in advisor tool is the advisor. Spawn nothing and call `advisor()` at each consult point. Otherwise spawn or resume `pstack:poteto-agent` at `ADVISOR_EFFORT` (default
|
|
27
|
+
2. Claude: when `advisor` is in this session's tool list, the built-in advisor tool is the advisor. Spawn nothing and call `advisor()` at each consult point. Otherwise spawn or resume `pstack:poteto-agent` at `ADVISOR_EFFORT` (default xhigh) with the advisor response contract in the prompt. When the bound model is out of usage, use the configured fallback at the same effort. Codex: use the native advisor path. Third-party: use the headless advisor path. When the host's walk fails, fail closed.
|
|
28
28
|
3. Name the warm instance `team-advisor-agent` and keep its advisor response contract in every first prompt. When a native Codex subagent spawn is the advisor path and the spawn tool has a `flags` field, pass `flags: ["--advisor"]` with Astra and record `advisor_flag: passed`. A spawn that skips an offered flag is a failed bind. When the spawn tool has no `flags` field, as in `codex exec`, spawn `gpt-6-astra` plainly, record `advisor_flag: unavailable`, and treat it as bound.
|
|
29
29
|
4. Skip the multi-consumer "who you are" opener. This session is the sole consumer.
|
|
30
30
|
5. When the bind or reply path fails, fail closed and report to the user. On a third-party host, only the bound advisor issues ENDORSE / CORRECTION / PLAN / STOP.
|
|
@@ -18,10 +18,10 @@ The sections below hold the standing rules. Open a reference file at the moment
|
|
|
18
18
|
| Moment | Open |
|
|
19
19
|
|---|---|
|
|
20
20
|
| Naming the session identity | [`reference/identity.md`](reference/identity.md): Claude, Codex, or neither |
|
|
21
|
-
| Binding on a Claude host | [`reference/warm-up.md`](reference/warm-up.md): spawn fields,
|
|
21
|
+
| Binding on a Claude host | [`reference/warm-up.md`](reference/warm-up.md): spawn fields, Opus token, charter |
|
|
22
22
|
| Binding on a Codex host | [`reference/identity.md`](reference/identity.md): in-session Astra spawn |
|
|
23
23
|
| Binding from a third-party host | [`reference/third-party-bind.md`](reference/third-party-bind.md): CLI bind steps, fail-closed rule |
|
|
24
|
-
|
|
|
24
|
+
| Opus is out of usage | [`reference/astra-rung.md`](reference/astra-rung.md): Astra fallback at shared effort |
|
|
25
25
|
| Composing a consult | [`reference/consult-format.md`](reference/consult-format.md): packet, new-evidence and report-back rules |
|
|
26
26
|
| Assembling an executor spawn prompt | [`reference/advisor-block.md`](reference/advisor-block.md): the paste parts |
|
|
27
27
|
| Advisor drifts, dies, or the task pivots | [`reference/lifecycle.md`](reference/lifecycle.md): re-spawn and re-bind steps |
|
|
@@ -47,25 +47,25 @@ Mechanical override for scripts:
|
|
|
47
47
|
|
|
48
48
|
### Shared effort, any host
|
|
49
49
|
|
|
50
|
-
|
|
50
|
+
Opus and Astra both read `ADVISOR_EFFORT` (`low`, `medium`, `high`, `xhigh`, `max`). Opus keeps its xhigh default. Astra uses the policy default, Medium.
|
|
51
51
|
Pass `--effort <level>` on the Astra helper to set effort for that Astra run.
|
|
52
|
-
Pass `--effort <level>` on the Claude CLI bind for
|
|
52
|
+
Pass `--effort <level>` on the Claude CLI bind for Opus. An unset or unrecognized Opus value uses `xhigh`.
|
|
53
53
|
|
|
54
54
|
### Astra rung
|
|
55
55
|
|
|
56
|
-
On Claude and ThirdParty: when
|
|
56
|
+
On Claude and ThirdParty: when Opus is out of usage, bind Astra through the Codex helper.
|
|
57
57
|
Open that attempt with `ADVISOR_ASTRA=1` (or `true` / `yes` / `on`) in the environment, or pass `--enable-astra` on the helper invocation.
|
|
58
|
-
Flag off both ways: fail closed when
|
|
59
|
-
Flag on: run the Codex preflight and bind per [`reference/astra-rung.md`](reference/astra-rung.md); a failed preflight fails closed when
|
|
58
|
+
Flag off both ways: fail closed when Opus did not bind.
|
|
59
|
+
Flag on: run the Codex preflight and bind per [`reference/astra-rung.md`](reference/astra-rung.md); a failed preflight fails closed when Opus did not bind.
|
|
60
60
|
|
|
61
61
|
On Codex: Astra is the in-session default. The `ADVISOR_ASTRA` flag is not required. Fail closed when that spawn does not bind.
|
|
62
62
|
|
|
63
63
|
### Claude host
|
|
64
64
|
|
|
65
|
-
When `advisor` is in the session's tool list, the built-in advisor tool is the advisor. The user turns it on with `/advisor`, the `advisorModel` setting, or `--advisor`. Spawn nothing and call `advisor()` at each consult point. The tool forwards the whole transcript, so a consult needs no packet. Its reply is free text, so the four signal words do not apply; weigh it per `docs/references/advisor-tool.md` **How to treat advice**. It is a server tool, so hooks and the spawn-walk log do not see it.
|
|
65
|
+
When `advisor` is in the session's tool list, the built-in advisor tool is the advisor. The user turns it on with `/advisor`, the `advisorModel` setting, or `--advisor`. Spawn nothing and call `advisor()` at each consult point. The tool forwards the whole transcript, so a consult needs no packet. Its reply is free text, so the four signal words do not apply; weigh it per `docs/references/advisor-tool.md` **How to treat advice**. It is a server tool, so hooks and the spawn-walk log do not see it. An Opus advisor returns an encrypted result that a log cannot read. When a readable record matters, use the warm agent below.
|
|
66
66
|
|
|
67
|
-
When `advisor` is not in the tool list, use the **Model floor** ladder below (
|
|
68
|
-
Warm-up spawns `subagent_type: session-advisor` via the Agent tool for
|
|
67
|
+
When `advisor` is not in the tool list, use the **Model floor** ladder below (Opus first, then Astra when Opus is out of usage).
|
|
68
|
+
Warm-up spawns `subagent_type: session-advisor` via the Agent tool for Opus; consults go through `SendMessage` to that warm agent. Astra uses the Codex helper.
|
|
69
69
|
Assemble and paste each executor's Advisor block per the **Advisor block** section.
|
|
70
70
|
|
|
71
71
|
### Codex host
|
|
@@ -73,14 +73,14 @@ Assemble and paste each executor's Advisor block per the **Advisor block** secti
|
|
|
73
73
|
Spawn a native in-session Astra subagent at `resolve_codex_model_id("Astra")`.
|
|
74
74
|
Walk `candidate_tiers = ["Astra"]`. Record `{tier: "Astra", result: "spawned"}` on success.
|
|
75
75
|
**Fail closed:** when Astra does not bind, set `selected_tier = null` and a `fallback_reason`, report that the advisor is unreachable, and **stop**.
|
|
76
|
-
Do not walk
|
|
76
|
+
Do not walk Opus on a Codex host. Consults stay in-session with that Astra subagent.
|
|
77
77
|
Assemble and paste each executor's Advisor block per the **Advisor block** section.
|
|
78
78
|
|
|
79
79
|
### Third-party host
|
|
80
80
|
|
|
81
|
-
On a third-party (non-Claude, non-Codex) harness, the shared CLI Claude-chain is the one path to a Claude advisor: bind
|
|
81
|
+
On a third-party (non-Claude, non-Codex) harness, the shared CLI Claude-chain is the one path to a Claude advisor: bind Opus through it, per [`reference/third-party-bind.md`](reference/third-party-bind.md).
|
|
82
82
|
The bound Claude session is the advisor; this third-party session stays the executor.
|
|
83
|
-
Walk `candidate_tiers = ["
|
|
83
|
+
Walk `candidate_tiers = ["Opus"]`. When Opus is out of usage, the Astra rung binds after Opus (`candidate_tiers = ["Opus", "Astra"]`).
|
|
84
84
|
**Cursor Astra first shot:** when the walk reaches Astra (or the user asks for Astra), bind through `codex_astra_advisor.py --bind --enable-astra` on the first tool call (see the GOTCHA in [`reference/third-party-bind.md`](reference/third-party-bind.md)). Do not use the Agent tool, and do not hunt a probe path.
|
|
85
85
|
**Fail closed:** when every candidate fails, set `selected_tier = null` and a `fallback_reason`, report that the advisor is unreachable, and **stop**. ENDORSE / CORRECTION / PLAN / STOP come only from a bound advisor.
|
|
86
86
|
Executors report to the orchestrating session; that session consults the bound advisor and relays the four-signal reply.
|
|
@@ -88,26 +88,26 @@ Executors report to the orchestrating session; that session consults the bound a
|
|
|
88
88
|
## Model floor
|
|
89
89
|
|
|
90
90
|
The built-in advisor tool sits outside this ladder. Claude Code and the API accept it only when the advisor is at least as capable as the main model, and that check is its floor.
|
|
91
|
-
On Claude and ThirdParty the advisor ladder is `
|
|
91
|
+
On Claude and ThirdParty the advisor ladder is `Opus` first, then Astra (flag-gated, Codex CLI) when Opus is out of usage.
|
|
92
92
|
On Codex the walk is Astra only, in-session.
|
|
93
93
|
Opus is not an advisor candidate. `Sonnet` and `Haiku` are executor tiers only.
|
|
94
94
|
Consumer `own_tier` is recorded on the spawn-walk log; it does not add Opus to the advisor walk.
|
|
95
95
|
Tier names are canonical Title Case; the validator accepts any letter case and normalizes to Title Case.
|
|
96
|
-
Try binds top-down.
|
|
96
|
+
Try binds top-down. Opus resolves to the short model alias in [`reference/cli-chain.md`](reference/cli-chain.md); Astra resolves through `resolve_codex_model_id("Astra")` to `gpt-6-astra`.
|
|
97
97
|
The advisor is created at `selected_tier`, the first ladder tier that binds.
|
|
98
|
-
When
|
|
98
|
+
When Opus fails on a Claude host, try Astra if the flag is on, else fail closed. The CLI chain is the Opus bind path on a third-party host and the Claude-host fallback for Opus.
|
|
99
99
|
On a Codex host a failed Astra spawn fails closed per **Host profiles → Codex host**.
|
|
100
|
-
On a third-party host the CLI chain is already the primary path, so a failed
|
|
100
|
+
On a third-party host the CLI chain is already the primary path, so a failed Opus (and Astra, when enabled) walk fails closed per **Host profiles → Third-party host**.
|
|
101
101
|
|
|
102
102
|
Emit a structured spawn-walk log so the walk can be checked mechanically: [`reference/spawn-walk-log.md`](reference/spawn-walk-log.md).
|
|
103
103
|
The validator checks ladder shape only; host policy sits on top.
|
|
104
104
|
|
|
105
|
-
**Equal-tier pairings.** Bind
|
|
105
|
+
**Equal-tier pairings.** Bind Opus for an independent second pass on irreversible or security-sensitive work. The advisor is Opus or Astra.
|
|
106
106
|
|
|
107
107
|
## Warm-up (once per session)
|
|
108
108
|
|
|
109
109
|
On a **Claude host** with `advisor` in the tool list, skip the warm-up; the built-in tool needs none.
|
|
110
|
-
On a **Claude host** without it, spawn `session-advisor` at the
|
|
110
|
+
On a **Claude host** without it, spawn `session-advisor` at the Opus alias with the charter as its prompt. When Opus is out of usage and the Astra rung is open, bind Astra through the Codex helper instead.
|
|
111
111
|
Full spawn fields and the charter template: [`reference/warm-up.md`](reference/warm-up.md).
|
|
112
112
|
|
|
113
113
|
On a **Codex host**, spawn a native in-session Astra subagent with the same charter. Bind fields: [`reference/identity.md`](reference/identity.md) and [`reference/warm-up.md`](reference/warm-up.md).
|
|
@@ -26,7 +26,7 @@ Subagents inherit the session's advisor tool, so the executor calls it directly.
|
|
|
26
26
|
## Transport preamble for a third-party host
|
|
27
27
|
|
|
28
28
|
> The orchestrating session owns a standing advisor for this run.
|
|
29
|
-
> The advisor chain: Claude
|
|
29
|
+
> The advisor chain: Claude Opus, then Astra through the Codex CLI when Opus is out of usage and the Astra flag and its preflight open that rung. Opus uses its xhigh default. Astra uses the policy default, Medium. When neither binds, there is no advisor.
|
|
30
30
|
> The orchestrating session is your one path to it: send each consult as a report to the session that assigned you, and it relays the advisor's reply.
|
|
31
31
|
|
|
32
32
|
## Shared core for every host
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
# Astra rung
|
|
2
2
|
|
|
3
3
|
Detail behind the **Host profiles → Astra rung** section of [`advisor-protocol.md`](../advisor-protocol.md).
|
|
4
|
-
Open this when
|
|
4
|
+
Open this when Opus is out of usage on a Claude or ThirdParty host and an Astra fallback bind is starting.
|
|
5
5
|
On a Codex host, Astra is the in-session default. See [`identity.md`](identity.md).
|
|
6
6
|
|
|
7
7
|
## Flag
|
|
8
8
|
|
|
9
|
-
`ADVISOR_ASTRA=1` (or `true` / `yes` / `on`) opens the Astra fallback after
|
|
10
|
-
Flag off both ways: fail closed when
|
|
9
|
+
`ADVISOR_ASTRA=1` (or `true` / `yes` / `on`) opens the Astra fallback after Opus is out of usage. Set the variable in the helper's process environment, or pass `--enable-astra` on the helper invocation. The CLI flag opens the rung for that run without touching the environment.
|
|
10
|
+
Flag off both ways: fail closed when Opus did not bind.
|
|
11
11
|
A Windows `setx` write only updates the persisted user environment; only a process started after that write inherits the new value, so an already-running session either sets the flag in its own invoking process environment or passes `--enable-astra`.
|
|
12
12
|
|
|
13
13
|
## Effort
|
|
@@ -28,12 +28,12 @@ python ~/.claude/scripts/codex_account_choice.py choose
|
|
|
28
28
|
|
|
29
29
|
The shared entry point is `~/.claude/_shared/advisor/scripts/codex_astra_advisor.py`. It runs the picker and owns Astra bind or resume parsing. Bind with `python ~/.claude/_shared/advisor/scripts/codex_astra_advisor.py --bind --enable-astra --cwd <repo-root>` and pipe the charter on stdin. Resume with `--resume <session_id>` and pipe the delta consult on stdin.
|
|
30
30
|
|
|
31
|
-
The gate passes only when the picker exits 0 and answers `tier: normal` with a `codex_home` and a finite `percent_left`. The helper then runs Codex with `CODEX_HOME` set to that `codex_home`. A `luna` or `wait` answer fails closed when
|
|
31
|
+
The gate passes only when the picker exits 0 and answers `tier: normal` with a `codex_home` and a finite `percent_left`. The helper then runs Codex with `CODEX_HOME` set to that `codex_home`. A `luna` or `wait` answer fails closed when Opus did not bind.
|
|
32
32
|
|
|
33
33
|
## Branches
|
|
34
34
|
|
|
35
35
|
**Preflight pass.** Bind one Codex CLI session at `gpt-6-astra` with `model_reasoning_effort` set from the policy-selected `ADVISOR_EFFORT` value, `--sandbox read-only`, and JSON output. The helper receives the standing-reviewer charter on stdin and returns only parsed ENDORSE / CORRECTION / PLAN / STOP guidance with a session ID.
|
|
36
36
|
|
|
37
|
-
**Preflight fail.** A picker failure, non-zero exit, timeout, malformed answer, or a `luna` or `wait` tier fails closed when
|
|
37
|
+
**Preflight fail.** A picker failure, non-zero exit, timeout, malformed answer, or a `luna` or `wait` tier fails closed when Opus did not bind.
|
|
38
38
|
|
|
39
|
-
The helper owns the Astra attempt and returns an explicit fallback result. The consuming advisor path owns the
|
|
39
|
+
The helper owns the Astra attempt and returns an explicit fallback result. The consuming advisor path owns the Opus bind. Apply the same gate to every Astra attempt, including resume.
|
|
@@ -10,18 +10,18 @@ Call `resolve_session_identity` from `$HOME/.claude/_shared/advisor/scripts/tier
|
|
|
10
10
|
| Identity text | Host profile | Bind path |
|
|
11
11
|
|---|---|---|
|
|
12
12
|
| a `codex` token | Codex | In-session Astra spawn |
|
|
13
|
-
| a `claude` token | Claude | In-session
|
|
13
|
+
| a `claude` token | Claude | In-session Opus spawn of `session-advisor` |
|
|
14
14
|
| any other identity | ThirdParty | Headless CLI chain |
|
|
15
15
|
|
|
16
16
|
When both `codex` and `claude` tokens appear, Codex wins. Empty text is ThirdParty.
|
|
17
17
|
|
|
18
18
|
## Bind path
|
|
19
19
|
|
|
20
|
-
**Claude.** Spawn `subagent_type: session-advisor` at
|
|
20
|
+
**Claude.** Spawn `subagent_type: session-advisor` at Opus through the Agent tool. When Opus is out of usage, bind Astra through the Codex helper if `ADVISOR_ASTRA` is on. Fail closed when neither binds.
|
|
21
21
|
|
|
22
|
-
**Codex.** Spawn a native in-session Astra subagent at `resolve_codex_model_id("Astra")` (`gpt-6-astra`). Walk `candidate_tiers = ["Astra"]`. When the spawn tool has a `flags` field, pass `flags: ["--advisor"]` and record `fallback.advisor_flag: "passed"`. When it has none, as in `codex exec`, spawn `gpt-6-astra` plainly and record `fallback.advisor_flag: "unavailable"`. Both count as a bind. Record `{tier: "Astra", result: "spawned"}` on success. The `ADVISOR_ASTRA` flag is not required. Fail closed when Astra does not bind, or when the spawn skips a flag the host offers. Do not walk
|
|
22
|
+
**Codex.** Spawn a native in-session Astra subagent at `resolve_codex_model_id("Astra")` (`gpt-6-astra`). Walk `candidate_tiers = ["Astra"]`. When the spawn tool has a `flags` field, pass `flags: ["--advisor"]` and record `fallback.advisor_flag: "passed"`. When it has none, as in `codex exec`, spawn `gpt-6-astra` plainly and record `fallback.advisor_flag: "unavailable"`. Both count as a bind. Record `{tier: "Astra", result: "spawned"}` on success. The `ADVISOR_ASTRA` flag is not required. Fail closed when Astra does not bind, or when the spawn skips a flag the host offers. Do not walk Opus on a Codex host.
|
|
23
23
|
|
|
24
|
-
**ThirdParty.** Bind
|
|
24
|
+
**ThirdParty.** Bind Opus through the CLI Claude-chain. When Opus is out of usage, bind Astra through the Codex helper if `ADVISOR_ASTRA` is on. Fail closed when neither binds.
|
|
25
25
|
|
|
26
26
|
## Mechanical override
|
|
27
27
|
|
|
@@ -22,6 +22,6 @@ Every other consumer reaches it by message alone; spawn, respawn, and shutdown b
|
|
|
22
22
|
|
|
23
23
|
The orchestrating session owns the CLI advisor bind for the whole run: first bind, re-bind on drift or lost `session_id`, and fail-closed report when the bound path cannot serve.
|
|
24
24
|
|
|
25
|
-
**Re-bind on drift.** If a reply shows a stale picture, the task pivots, or `--resume` fails after a usage-limit failover (session stores are per binary/account), re-bind
|
|
26
|
-
Capture the new `session_id`, and log a fresh
|
|
25
|
+
**Re-bind on drift.** If a reply shows a stale picture, the task pivots, or `--resume` fails after a usage-limit failover (session stores are per binary/account), re-bind Opus through `claude_chain_runner.py` with the charter plus a compact recap of consults so far.
|
|
26
|
+
Capture the new `session_id`, and log a fresh Opus walk with `result: "cli"` on success, then Astra when that rung is open. An Astra re-bind uses `codex_astra_advisor.py --bind --enable-astra` with the same charter and recap, and records `result: "codex"` on success.
|
|
27
27
|
Executors keep reporting to the orchestrating session; advisor binding stays with that session alone.
|
|
@@ -6,10 +6,10 @@ Detail behind the **Model floor** section of [`advisor-protocol.md`](../advisor-
|
|
|
6
6
|
|
|
7
7
|
Write the log as JSON with these field names:
|
|
8
8
|
|
|
9
|
-
- `own_tier`: the consuming session's tier
|
|
9
|
+
- `own_tier`: the consuming session's tier.
|
|
10
10
|
- `host_profile`: `Claude`, `Codex`, or `ThirdParty`. Omit to default to `Claude`.
|
|
11
|
-
- `candidate_tiers`: on Claude and ThirdParty,
|
|
12
|
-
- `astra_enabled`: a boolean recorded before candidate selection. On Claude and ThirdParty, `true` adds Astra after
|
|
11
|
+
- `candidate_tiers`: on Claude and ThirdParty, Opus, plus Astra when `astra_enabled` is true. On Codex, Astra only.
|
|
12
|
+
- `astra_enabled`: a boolean recorded before candidate selection. On Claude and ThirdParty, `true` adds Astra after Opus and `false` walks Opus alone. On Codex this flag does not change the walk.
|
|
13
13
|
- `attempts`: one `{tier, result}` entry appended as each bind try happens. `result` is `codex` for an Astra helper bind, `spawned` for an in-session spawn (Claude Agent or Codex native Astra), `cli` for a CLI Claude-chain bind, or a failure reason such as `unavailable`.
|
|
14
14
|
- `selected_tier`: the tier of the first successful bind (first `codex`, `spawned`, or `cli` entry), or `null` paired with a `fallback_reason` string when none bound.
|
|
15
15
|
- `evidence`: the versioned reference, fallback, and consult record described in `docs/references/advisor-tool.md`. A reference status records optional path repair separately from bind status.
|
|
@@ -29,5 +29,5 @@ Exit code `1` means a ladder invariant failed.
|
|
|
29
29
|
Exit code `2` means the path or JSON was unusable.
|
|
30
30
|
The same checks are available in-process via `validate_model_tier_run(run)`.
|
|
31
31
|
|
|
32
|
-
The validator checks ladder shape and validates `evidence` when it is present. On Claude and ThirdParty, Astra is attempted after
|
|
32
|
+
The validator checks ladder shape and validates `evidence` when it is present. On Claude and ThirdParty, Astra is attempted after Opus when `astra_enabled` is enabled, and `selected_tier: "Astra"` requires `result: "codex"`. On Codex, the walk is Astra only, and `result: "spawned"` or `result: "codex"` counts as success. A Codex success record uses `reply_path: "native"` and `advisor_flag: "passed"` or `advisor_flag: "unavailable"`. A missing `advisor_flag` or `advisor_flag: "omitted"` fails validation.
|
|
33
33
|
Host policy sits on top of it. See the Model floor section of the protocol.
|
|
@@ -5,7 +5,7 @@ Open this when binding or re-binding the advisor from a third-party (non-Claude,
|
|
|
5
5
|
|
|
6
6
|
## GOTCHA: Cursor Astra first shot
|
|
7
7
|
|
|
8
|
-
Cursor is a third-party host. When the walk reaches Astra (`ADVISOR_ASTRA=1` / `--enable-astra` after
|
|
8
|
+
Cursor is a third-party host. When the walk reaches Astra (`ADVISOR_ASTRA=1` / `--enable-astra` after Opus is out of usage, or the user asks for Astra), bind Astra on the **first** tool call through the headless Codex helper. Do not use the Agent or Task tool, do not spawn Grok as a substitute, and do not search the filesystem for the Codex account picker.
|
|
9
9
|
|
|
10
10
|
```
|
|
11
11
|
python "$HOME/.claude/_shared/advisor/scripts/codex_astra_advisor.py" --bind --enable-astra --cwd <repo-root>
|
|
@@ -18,18 +18,18 @@ When the Astra flag is off, follow the Claude-chain steps below.
|
|
|
18
18
|
## Bind steps
|
|
19
19
|
|
|
20
20
|
1. Name the session identity first (protocol **Host profiles**). This path is for a ThirdParty profile.
|
|
21
|
-
2. Walk `candidate_tiers = ["
|
|
22
|
-
3. **CLI bind (primary path):** for
|
|
21
|
+
2. Walk `candidate_tiers = ["Opus"]`. When Opus is out of usage, the Astra rung ([`astra-rung.md`](astra-rung.md)) binds Astra after Opus (`candidate_tiers = ["Opus", "Astra"]`).
|
|
22
|
+
3. **CLI bind (primary path):** for Opus, pipe a charter file into:
|
|
23
23
|
|
|
24
24
|
```
|
|
25
25
|
python "$HOME/.claude/scripts/claude_chain_runner.py" --routing-mode ordered_account -- -p --model <alias> --effort <effort> --output-format json
|
|
26
26
|
```
|
|
27
27
|
|
|
28
|
-
Use `--model
|
|
28
|
+
Use `--model opus --effort` with the value of `ADVISOR_EFFORT` (default `xhigh`) on Opus.
|
|
29
29
|
User-facing wording follows [`rules/asd-ste100-language.md`](../../../rules/asd-ste100-language.md).
|
|
30
30
|
A root advisor bind uses `--routing-mode ordered_account`. Walk order, failover, and the `advisor_blocked` terminal status are in [`cli-chain.md`](cli-chain.md).
|
|
31
31
|
4. Stop at the first successful bind.
|
|
32
|
-
Record `{tier, result: "cli"}` for
|
|
32
|
+
Record `{tier, result: "cli"}` for Opus or `{tier: "Astra", result: "codex"}` for the Astra helper, and set `selected_tier` to that tier.
|
|
33
33
|
Persist `session_id` from the JSON events (any event carries it; the runner also surfaces it on `ChainInvocationOutcome.session_id`; reply text is the `type == "result"` event's `.result` field).
|
|
34
34
|
Run every bind and every later consult with cwd set to the repo root the work is for. Claude sessions are project-scoped by working directory.
|
|
35
35
|
5. **Fail closed:** when every candidate fails (chain exhausted, `advisor_blocked`, or model unavailable), set `selected_tier = null` and a `fallback_reason`, report that the advisor is unreachable, and **stop**. ENDORSE / CORRECTION / PLAN / STOP come only from a bound advisor.
|
|
@@ -38,4 +38,4 @@ When the Astra flag is off, follow the Claude-chain steps below.
|
|
|
38
38
|
## Session model field
|
|
39
39
|
|
|
40
40
|
Resolve a third-party session's own model field with `resolve_cli_model_id("ThirdParty")` → `third-party` when a host model alias is required.
|
|
41
|
-
The **advisor** bind uses the
|
|
41
|
+
The **advisor** bind uses the Opus alias. Astra binds through the Codex helper.
|
|
@@ -5,10 +5,10 @@ Open this when binding the warm advisor on a Claude host or a Codex host, and fo
|
|
|
5
5
|
|
|
6
6
|
## Spawn fields for a Claude host
|
|
7
7
|
|
|
8
|
-
The consuming skill's session tries
|
|
8
|
+
The consuming skill's session tries Opus first. Spawn with:
|
|
9
9
|
|
|
10
10
|
- `subagent_type: session-advisor` (see [`agents/session-advisor.md`](../../../agents/session-advisor.md) for the full signal contract).
|
|
11
|
-
- `model`: the short alias via `resolve_cli_model_id("
|
|
11
|
+
- `model`: the short alias via `resolve_cli_model_id("Opus")` (alias table: [`cli-chain.md`](cli-chain.md)): `opus`. When Opus is out of usage and the Astra rung is open, continue with the Codex helper in [`astra-rung.md`](astra-rung.md) as the Astra fallback.
|
|
12
12
|
- `name`: a name the session and every consumer will use to reach it (e.g. `team-advisor-agent`).
|
|
13
13
|
- `run_in_background: true`.
|
|
14
14
|
- `prompt`: the charter below.
|
|
@@ -34,7 +34,7 @@ Spawn a native in-session Astra subagent at `resolve_codex_model_id("Astra")` (`
|
|
|
34
34
|
When the spawn tool has a `flags` field, pass `flags: ["--advisor"]` and record `fallback.advisor_flag: "passed"`.
|
|
35
35
|
When the spawn tool has no `flags` field, as in `codex exec`, spawn `gpt-6-astra` plainly, record `fallback.advisor_flag: "unavailable"`, and treat the advisor as bound.
|
|
36
36
|
Record `{tier: "Astra", result: "spawned"}` on success. Fail closed when that spawn does not bind, or when it skips a flag the host offers.
|
|
37
|
-
The `ADVISOR_ASTRA` flag is not required. Do not walk
|
|
37
|
+
The `ADVISOR_ASTRA` flag is not required. Do not walk Opus. Consults stay in-session with that Astra subagent.
|
|
38
38
|
Identity routing: [`identity.md`](identity.md).
|
|
39
39
|
|
|
40
40
|
## Third-party host
|
|
@@ -6,8 +6,8 @@ import json
|
|
|
6
6
|
from pathlib import Path
|
|
7
7
|
|
|
8
8
|
ADVISOR_MODEL_TIER: str = "Astra"
|
|
9
|
-
ADVISOR_FALLBACK_TIER: str = "
|
|
10
|
-
ADVISOR_FALLBACK_RESULT: str = "
|
|
9
|
+
ADVISOR_FALLBACK_TIER: str = "Opus"
|
|
10
|
+
ADVISOR_FALLBACK_RESULT: str = "opus"
|
|
11
11
|
ADVISOR_EFFORT_ENV_VAR: str = "ADVISOR_EFFORT"
|
|
12
12
|
ADVISOR_ROUTING_TIMEOUT_SECONDS: int = 10
|
|
13
13
|
|
|
@@ -43,8 +43,8 @@ SESSION_IDENTITY_WORD_PATTERN: str = r"[a-z0-9]+"
|
|
|
43
43
|
HOST_PROFILE_JSON_KEY: str = "host_profile"
|
|
44
44
|
|
|
45
45
|
ALL_MODEL_TIERS: tuple[str, ...] = (
|
|
46
|
+
"Fable",
|
|
46
47
|
ADVISOR_FALLBACK_TIER,
|
|
47
|
-
"Opus",
|
|
48
48
|
"Sonnet",
|
|
49
49
|
"Haiku",
|
|
50
50
|
)
|
|
@@ -58,8 +58,8 @@ ALL_KNOWN_TIER_NAMES: tuple[str, ...] = (
|
|
|
58
58
|
ADVISOR_SENDMESSAGE_REPLY_WAIT_SECONDS: int = 120
|
|
59
59
|
|
|
60
60
|
ALL_CLI_MODEL_ID_BY_TIER: dict[str, str] = {
|
|
61
|
-
|
|
62
|
-
|
|
61
|
+
"Fable": "fable",
|
|
62
|
+
ADVISOR_FALLBACK_TIER: "opus",
|
|
63
63
|
"Sonnet": "sonnet",
|
|
64
64
|
"Haiku": "haiku",
|
|
65
65
|
THIRD_PARTY_MODEL_TIER: "third-party",
|
|
@@ -7,17 +7,17 @@ This validator reads that log back and checks its invariants.
|
|
|
7
7
|
|
|
8
8
|
ladder_walk = ModelTierRun(
|
|
9
9
|
own_tier="Opus",
|
|
10
|
-
candidate_tiers=["
|
|
11
|
-
attempts=[{"tier": "
|
|
12
|
-
selected_tier="
|
|
10
|
+
candidate_tiers=["Opus"],
|
|
11
|
+
attempts=[{"tier": "Opus", "result": "spawned"}],
|
|
12
|
+
selected_tier="Opus",
|
|
13
13
|
)
|
|
14
14
|
validate_model_tier_run(ladder_walk) # ok: returns None, raises nothing
|
|
15
15
|
|
|
16
16
|
cli_bind = ModelTierRun(
|
|
17
17
|
own_tier="Opus",
|
|
18
|
-
candidate_tiers=["
|
|
19
|
-
attempts=[{"tier": "
|
|
20
|
-
selected_tier="
|
|
18
|
+
candidate_tiers=["Opus"],
|
|
19
|
+
attempts=[{"tier": "Opus", "result": "cli"}],
|
|
20
|
+
selected_tier="Opus",
|
|
21
21
|
)
|
|
22
22
|
validate_model_tier_run(cli_bind) # ok: third-party-host CLI Claude-chain bind
|
|
23
23
|
|
|
@@ -330,10 +330,9 @@ def validate_model_tier_run(run: ModelTierRun) -> None:
|
|
|
330
330
|
validate_model_tier_run(cli_bind) # ok: CLI Claude-chain bind
|
|
331
331
|
validate_model_tier_run(broken_log) # flag: ModelTierRunError
|
|
332
332
|
|
|
333
|
-
Candidate tiers are
|
|
333
|
+
Candidate tiers are Opus, plus Astra when ``is_astra_enabled`` is true, on
|
|
334
334
|
Claude and ThirdParty hosts. A Codex host walks Astra only. Consumer
|
|
335
|
-
``own_tier`` is recorded and must be a known tier.
|
|
336
|
-
to the advisor walk. Tries walk that list in order. Early stop only
|
|
335
|
+
``own_tier`` is recorded and must be a known tier. Tries walk that list in order. Early stop only
|
|
337
336
|
after ``spawned``, ``cli``, or Astra ``codex`` (and Astra ``spawned`` on a
|
|
338
337
|
Codex host). A null selected_tier requires a full walk plus
|
|
339
338
|
fallback_reason.
|
|
@@ -218,19 +218,19 @@ def test_non_object_evidence_reports_the_same_message_at_validate_and_load(
|
|
|
218
218
|
def test_clean_single_spawn_at_top_of_slice_passes() -> None:
|
|
219
219
|
run = ModelTierRun(
|
|
220
220
|
own_tier="Opus",
|
|
221
|
-
candidate_tiers=["
|
|
222
|
-
attempts=[{"tier": "
|
|
223
|
-
selected_tier="
|
|
221
|
+
candidate_tiers=["Opus"],
|
|
222
|
+
attempts=[{"tier": "Opus", "result": "spawned"}],
|
|
223
|
+
selected_tier="Opus",
|
|
224
224
|
)
|
|
225
225
|
assert validate_model_tier_run(run) is None
|
|
226
226
|
|
|
227
227
|
|
|
228
|
-
def
|
|
228
|
+
def test_astra_codex_bind_succeeds_after_opus_when_enabled() -> None:
|
|
229
229
|
run = ModelTierRun(
|
|
230
230
|
own_tier="Opus",
|
|
231
|
-
candidate_tiers=["
|
|
231
|
+
candidate_tiers=["Opus", ADVISOR_MODEL_TIER],
|
|
232
232
|
attempts=[
|
|
233
|
-
{"tier": "
|
|
233
|
+
{"tier": "Opus", "result": "unavailable"},
|
|
234
234
|
{"tier": ADVISOR_MODEL_TIER, "result": CODEX_BIND_SUCCESS_TOKEN},
|
|
235
235
|
],
|
|
236
236
|
selected_tier=ADVISOR_MODEL_TIER,
|
|
@@ -239,12 +239,12 @@ def test_astra_codex_bind_succeeds_after_fable_when_enabled() -> None:
|
|
|
239
239
|
assert validate_model_tier_run(run) is None
|
|
240
240
|
|
|
241
241
|
|
|
242
|
-
def
|
|
242
|
+
def test_opus_success_with_astra_enabled_stops_before_astra() -> None:
|
|
243
243
|
run = ModelTierRun(
|
|
244
244
|
own_tier="Opus",
|
|
245
|
-
candidate_tiers=["
|
|
246
|
-
attempts=[{"tier": "
|
|
247
|
-
selected_tier="
|
|
245
|
+
candidate_tiers=["Opus", ADVISOR_MODEL_TIER],
|
|
246
|
+
attempts=[{"tier": "Opus", "result": "spawned"}],
|
|
247
|
+
selected_tier="Opus",
|
|
248
248
|
is_astra_enabled=True,
|
|
249
249
|
)
|
|
250
250
|
assert validate_model_tier_run(run) is None
|
|
@@ -253,7 +253,7 @@ def test_fable_success_with_astra_enabled_stops_before_astra() -> None:
|
|
|
253
253
|
def test_astra_first_walk_raises_when_astra_is_enabled() -> None:
|
|
254
254
|
run = ModelTierRun(
|
|
255
255
|
own_tier="Opus",
|
|
256
|
-
candidate_tiers=[ADVISOR_MODEL_TIER, "
|
|
256
|
+
candidate_tiers=[ADVISOR_MODEL_TIER, "Opus"],
|
|
257
257
|
attempts=[{"tier": ADVISOR_MODEL_TIER, "result": CODEX_BIND_SUCCESS_TOKEN}],
|
|
258
258
|
selected_tier=ADVISOR_MODEL_TIER,
|
|
259
259
|
is_astra_enabled=True,
|
|
@@ -262,12 +262,12 @@ def test_astra_first_walk_raises_when_astra_is_enabled() -> None:
|
|
|
262
262
|
validate_model_tier_run(run)
|
|
263
263
|
|
|
264
264
|
|
|
265
|
-
def
|
|
265
|
+
def test_astra_rung_follows_opus_on_third_party_cli_floor_when_enabled() -> None:
|
|
266
266
|
run = ModelTierRun(
|
|
267
267
|
own_tier="ThirdParty",
|
|
268
|
-
candidate_tiers=["
|
|
268
|
+
candidate_tiers=["Opus", ADVISOR_MODEL_TIER],
|
|
269
269
|
attempts=[
|
|
270
|
-
{"tier": "
|
|
270
|
+
{"tier": "Opus", "result": "unavailable"},
|
|
271
271
|
{"tier": ADVISOR_MODEL_TIER, "result": CODEX_BIND_SUCCESS_TOKEN},
|
|
272
272
|
],
|
|
273
273
|
selected_tier=ADVISOR_MODEL_TIER,
|
|
@@ -281,9 +281,9 @@ def test_astra_rung_follows_fable_on_third_party_cli_floor_when_enabled() -> Non
|
|
|
281
281
|
def test_astra_codex_result_requires_astra_candidate() -> None:
|
|
282
282
|
run = ModelTierRun(
|
|
283
283
|
own_tier="Opus",
|
|
284
|
-
candidate_tiers=["
|
|
285
|
-
attempts=[{"tier": "
|
|
286
|
-
selected_tier="
|
|
284
|
+
candidate_tiers=["Opus"],
|
|
285
|
+
attempts=[{"tier": "Opus", "result": CODEX_BIND_SUCCESS_TOKEN}],
|
|
286
|
+
selected_tier="Opus",
|
|
287
287
|
)
|
|
288
288
|
with pytest.raises(ModelTierRunError):
|
|
289
289
|
validate_model_tier_run(run)
|
|
@@ -292,9 +292,9 @@ def test_astra_codex_result_requires_astra_candidate() -> None:
|
|
|
292
292
|
def test_astra_spawned_result_does_not_count_as_codex_success() -> None:
|
|
293
293
|
run = ModelTierRun(
|
|
294
294
|
own_tier="Opus",
|
|
295
|
-
candidate_tiers=["
|
|
295
|
+
candidate_tiers=["Opus", ADVISOR_MODEL_TIER],
|
|
296
296
|
attempts=[
|
|
297
|
-
{"tier": "
|
|
297
|
+
{"tier": "Opus", "result": "unavailable"},
|
|
298
298
|
{"tier": ADVISOR_MODEL_TIER, "result": "spawned"},
|
|
299
299
|
],
|
|
300
300
|
selected_tier=ADVISOR_MODEL_TIER,
|
|
@@ -304,13 +304,13 @@ def test_astra_spawned_result_does_not_count_as_codex_success() -> None:
|
|
|
304
304
|
validate_model_tier_run(run)
|
|
305
305
|
|
|
306
306
|
|
|
307
|
-
def
|
|
307
|
+
def test_exhausted_opus_walk_fails_closed() -> None:
|
|
308
308
|
run = ModelTierRun(
|
|
309
309
|
own_tier="Opus",
|
|
310
|
-
candidate_tiers=["
|
|
311
|
-
attempts=[{"tier": "
|
|
310
|
+
candidate_tiers=["Opus"],
|
|
311
|
+
attempts=[{"tier": "Opus", "result": "unavailable"}],
|
|
312
312
|
selected_tier=None,
|
|
313
|
-
fallback_reason="
|
|
313
|
+
fallback_reason="Opus did not bind; no advisor",
|
|
314
314
|
)
|
|
315
315
|
assert validate_model_tier_run(run) is None
|
|
316
316
|
|
|
@@ -318,8 +318,8 @@ def test_exhausted_fable_walk_fails_closed() -> None:
|
|
|
318
318
|
def test_fully_exhausted_walk_with_fallback_reason_passes() -> None:
|
|
319
319
|
run = ModelTierRun(
|
|
320
320
|
own_tier="Opus",
|
|
321
|
-
candidate_tiers=["
|
|
322
|
-
attempts=[{"tier": "
|
|
321
|
+
candidate_tiers=["Opus"],
|
|
322
|
+
attempts=[{"tier": "Opus", "result": "unavailable"}],
|
|
323
323
|
selected_tier=None,
|
|
324
324
|
fallback_reason="every candidate tier failed",
|
|
325
325
|
)
|
|
@@ -329,9 +329,9 @@ def test_fully_exhausted_walk_with_fallback_reason_passes() -> None:
|
|
|
329
329
|
def test_opus_candidate_on_advisor_walk_raises() -> None:
|
|
330
330
|
run = ModelTierRun(
|
|
331
331
|
own_tier="Opus",
|
|
332
|
-
candidate_tiers=["
|
|
333
|
-
attempts=[{"tier": "
|
|
334
|
-
selected_tier="
|
|
332
|
+
candidate_tiers=["Opus", "Opus"],
|
|
333
|
+
attempts=[{"tier": "Opus", "result": "spawned"}],
|
|
334
|
+
selected_tier="Opus",
|
|
335
335
|
)
|
|
336
336
|
with pytest.raises(ModelTierRunError):
|
|
337
337
|
validate_model_tier_run(run)
|
|
@@ -340,7 +340,7 @@ def test_opus_candidate_on_advisor_walk_raises() -> None:
|
|
|
340
340
|
def test_attempt_tier_outside_candidate_slice_raises() -> None:
|
|
341
341
|
run = ModelTierRun(
|
|
342
342
|
own_tier="Opus",
|
|
343
|
-
candidate_tiers=["
|
|
343
|
+
candidate_tiers=["Opus"],
|
|
344
344
|
attempts=[{"tier": "Haiku", "result": "spawned"}],
|
|
345
345
|
selected_tier="Haiku",
|
|
346
346
|
)
|
|
@@ -351,10 +351,10 @@ def test_attempt_tier_outside_candidate_slice_raises() -> None:
|
|
|
351
351
|
def test_attempts_out_of_ladder_order_raises() -> None:
|
|
352
352
|
run = ModelTierRun(
|
|
353
353
|
own_tier="Opus",
|
|
354
|
-
candidate_tiers=["
|
|
354
|
+
candidate_tiers=["Opus", ADVISOR_MODEL_TIER],
|
|
355
355
|
attempts=[
|
|
356
356
|
{"tier": ADVISOR_MODEL_TIER, "result": CODEX_BIND_SUCCESS_TOKEN},
|
|
357
|
-
{"tier": "
|
|
357
|
+
{"tier": "Opus", "result": "unavailable"},
|
|
358
358
|
],
|
|
359
359
|
selected_tier=ADVISOR_MODEL_TIER,
|
|
360
360
|
is_astra_enabled=True,
|
|
@@ -366,12 +366,12 @@ def test_attempts_out_of_ladder_order_raises() -> None:
|
|
|
366
366
|
def test_selected_tier_not_first_spawned_attempt_raises() -> None:
|
|
367
367
|
run = ModelTierRun(
|
|
368
368
|
own_tier="Opus",
|
|
369
|
-
candidate_tiers=["
|
|
369
|
+
candidate_tiers=["Opus", ADVISOR_MODEL_TIER],
|
|
370
370
|
attempts=[
|
|
371
|
-
{"tier": "
|
|
371
|
+
{"tier": "Opus", "result": "unavailable"},
|
|
372
372
|
{"tier": ADVISOR_MODEL_TIER, "result": CODEX_BIND_SUCCESS_TOKEN},
|
|
373
373
|
],
|
|
374
|
-
selected_tier="
|
|
374
|
+
selected_tier="Opus",
|
|
375
375
|
is_astra_enabled=True,
|
|
376
376
|
)
|
|
377
377
|
with pytest.raises(
|
|
@@ -387,9 +387,9 @@ def test_selected_tier_not_first_spawned_attempt_raises() -> None:
|
|
|
387
387
|
def test_exhausted_walk_with_non_null_selected_tier_raises() -> None:
|
|
388
388
|
run = ModelTierRun(
|
|
389
389
|
own_tier="Opus",
|
|
390
|
-
candidate_tiers=["
|
|
391
|
-
attempts=[{"tier": "
|
|
392
|
-
selected_tier="
|
|
390
|
+
candidate_tiers=["Opus"],
|
|
391
|
+
attempts=[{"tier": "Opus", "result": "unavailable"}],
|
|
392
|
+
selected_tier="Opus",
|
|
393
393
|
fallback_reason="every candidate tier failed",
|
|
394
394
|
)
|
|
395
395
|
with pytest.raises(ModelTierRunError):
|
|
@@ -399,8 +399,8 @@ def test_exhausted_walk_with_non_null_selected_tier_raises() -> None:
|
|
|
399
399
|
def test_exhausted_walk_missing_fallback_reason_raises() -> None:
|
|
400
400
|
run = ModelTierRun(
|
|
401
401
|
own_tier="Opus",
|
|
402
|
-
candidate_tiers=["
|
|
403
|
-
attempts=[{"tier": "
|
|
402
|
+
candidate_tiers=["Opus"],
|
|
403
|
+
attempts=[{"tier": "Opus", "result": "unavailable"}],
|
|
404
404
|
selected_tier=None,
|
|
405
405
|
)
|
|
406
406
|
with pytest.raises(ModelTierRunError):
|
|
@@ -421,7 +421,7 @@ def test_unknown_own_tier_raises() -> None:
|
|
|
421
421
|
def test_empty_attempts_with_null_selected_tier_raises() -> None:
|
|
422
422
|
run = ModelTierRun(
|
|
423
423
|
own_tier="Opus",
|
|
424
|
-
candidate_tiers=["
|
|
424
|
+
candidate_tiers=["Opus"],
|
|
425
425
|
attempts=[],
|
|
426
426
|
selected_tier=None,
|
|
427
427
|
fallback_reason="skipped straight to CLI fallback",
|
|
@@ -433,10 +433,10 @@ def test_empty_attempts_with_null_selected_tier_raises() -> None:
|
|
|
433
433
|
def test_incomplete_fallback_walk_before_astra_raises() -> None:
|
|
434
434
|
run = ModelTierRun(
|
|
435
435
|
own_tier="Opus",
|
|
436
|
-
candidate_tiers=["
|
|
437
|
-
attempts=[{"tier": "
|
|
436
|
+
candidate_tiers=["Opus", ADVISOR_MODEL_TIER],
|
|
437
|
+
attempts=[{"tier": "Opus", "result": "unavailable"}],
|
|
438
438
|
selected_tier=None,
|
|
439
|
-
fallback_reason="stopped after
|
|
439
|
+
fallback_reason="stopped after Opus without trying Astra",
|
|
440
440
|
is_astra_enabled=True,
|
|
441
441
|
)
|
|
442
442
|
with pytest.raises(ModelTierRunError):
|
|
@@ -446,9 +446,9 @@ def test_incomplete_fallback_walk_before_astra_raises() -> None:
|
|
|
446
446
|
def test_lowercase_own_tier_and_candidates_pass() -> None:
|
|
447
447
|
run = ModelTierRun(
|
|
448
448
|
own_tier="opus",
|
|
449
|
-
candidate_tiers=["
|
|
450
|
-
attempts=[{"tier": "
|
|
451
|
-
selected_tier="
|
|
449
|
+
candidate_tiers=["opus"],
|
|
450
|
+
attempts=[{"tier": "opus", "result": "spawned"}],
|
|
451
|
+
selected_tier="opus",
|
|
452
452
|
)
|
|
453
453
|
assert validate_model_tier_run(run) is None
|
|
454
454
|
|
|
@@ -459,16 +459,16 @@ def test_cli_validates_json_log_file(tmp_path: Path) -> None:
|
|
|
459
459
|
json.dumps(
|
|
460
460
|
{
|
|
461
461
|
"own_tier": "Opus",
|
|
462
|
-
"candidate_tiers": ["
|
|
463
|
-
"attempts": [{"tier": "
|
|
464
|
-
"selected_tier": "
|
|
462
|
+
"candidate_tiers": ["Opus"],
|
|
463
|
+
"attempts": [{"tier": "Opus", "result": "spawned"}],
|
|
464
|
+
"selected_tier": "Opus",
|
|
465
465
|
}
|
|
466
466
|
),
|
|
467
467
|
encoding="utf-8",
|
|
468
468
|
)
|
|
469
469
|
assert main([str(log_path)]) == 0
|
|
470
470
|
loaded_run = load_model_tier_run_from_json_path(from_path=log_path)
|
|
471
|
-
assert loaded_run.selected_tier == "
|
|
471
|
+
assert loaded_run.selected_tier == "Opus"
|
|
472
472
|
assert loaded_run.host_profile == "Claude"
|
|
473
473
|
|
|
474
474
|
|
|
@@ -478,9 +478,9 @@ def test_cli_rejects_non_string_host_profile(tmp_path: Path) -> None:
|
|
|
478
478
|
json.dumps(
|
|
479
479
|
{
|
|
480
480
|
"own_tier": "Opus",
|
|
481
|
-
"candidate_tiers": ["
|
|
482
|
-
"attempts": [{"tier": "
|
|
483
|
-
"selected_tier": "
|
|
481
|
+
"candidate_tiers": ["Opus"],
|
|
482
|
+
"attempts": [{"tier": "Opus", "result": "spawned"}],
|
|
483
|
+
"selected_tier": "Opus",
|
|
484
484
|
"host_profile": 1,
|
|
485
485
|
}
|
|
486
486
|
),
|
|
@@ -495,9 +495,9 @@ def test_cli_rejects_non_boolean_astra_enabled(tmp_path: Path) -> None:
|
|
|
495
495
|
json.dumps(
|
|
496
496
|
{
|
|
497
497
|
"own_tier": "Opus",
|
|
498
|
-
"candidate_tiers": ["
|
|
499
|
-
"attempts": [{"tier": "
|
|
500
|
-
"selected_tier": "
|
|
498
|
+
"candidate_tiers": ["Opus"],
|
|
499
|
+
"attempts": [{"tier": "Opus", "result": "spawned"}],
|
|
500
|
+
"selected_tier": "Opus",
|
|
501
501
|
"astra_enabled": "false",
|
|
502
502
|
}
|
|
503
503
|
),
|
|
@@ -512,8 +512,8 @@ def test_cli_rejects_incomplete_fallback_log(tmp_path: Path) -> None:
|
|
|
512
512
|
json.dumps(
|
|
513
513
|
{
|
|
514
514
|
"own_tier": "Opus",
|
|
515
|
-
"candidate_tiers": ["
|
|
516
|
-
"attempts": [{"tier": "
|
|
515
|
+
"candidate_tiers": ["Opus", "Astra"],
|
|
516
|
+
"attempts": [{"tier": "Opus", "result": "unavailable"}],
|
|
517
517
|
"selected_tier": None,
|
|
518
518
|
"fallback_reason": "incomplete",
|
|
519
519
|
"astra_enabled": True,
|
|
@@ -531,9 +531,9 @@ def test_cli_loads_astra_enabled_fallback_walk(tmp_path: Path) -> None:
|
|
|
531
531
|
{
|
|
532
532
|
"own_tier": "ThirdParty",
|
|
533
533
|
"host_profile": "ThirdParty",
|
|
534
|
-
"candidate_tiers": ["
|
|
534
|
+
"candidate_tiers": ["Opus", "Astra"],
|
|
535
535
|
"attempts": [
|
|
536
|
-
{"tier": "
|
|
536
|
+
{"tier": "Opus", "result": "unavailable"},
|
|
537
537
|
{"tier": "Astra", "result": "codex"},
|
|
538
538
|
],
|
|
539
539
|
"selected_tier": "Astra",
|
|
@@ -552,12 +552,12 @@ def test_cli_missing_path_returns_usage_exit_code() -> None:
|
|
|
552
552
|
assert main([]) == 2
|
|
553
553
|
|
|
554
554
|
|
|
555
|
-
def
|
|
555
|
+
def test_cli_bind_at_opus_passes() -> None:
|
|
556
556
|
run = ModelTierRun(
|
|
557
557
|
own_tier="Opus",
|
|
558
|
-
candidate_tiers=["
|
|
559
|
-
attempts=[{"tier": "
|
|
560
|
-
selected_tier="
|
|
558
|
+
candidate_tiers=["Opus"],
|
|
559
|
+
attempts=[{"tier": "Opus", "result": "cli"}],
|
|
560
|
+
selected_tier="Opus",
|
|
561
561
|
)
|
|
562
562
|
assert validate_model_tier_run(run) is None
|
|
563
563
|
|
|
@@ -565,9 +565,9 @@ def test_cli_bind_at_fable_passes() -> None:
|
|
|
565
565
|
def test_cli_bind_fallthrough_to_opus_raises() -> None:
|
|
566
566
|
run = ModelTierRun(
|
|
567
567
|
own_tier="Opus",
|
|
568
|
-
candidate_tiers=["
|
|
568
|
+
candidate_tiers=["Opus", "Opus"],
|
|
569
569
|
attempts=[
|
|
570
|
-
{"tier": "
|
|
570
|
+
{"tier": "Opus", "result": "unavailable"},
|
|
571
571
|
{"tier": "Opus", "result": "cli"},
|
|
572
572
|
],
|
|
573
573
|
selected_tier="Opus",
|
|
@@ -576,12 +576,12 @@ def test_cli_bind_fallthrough_to_opus_raises() -> None:
|
|
|
576
576
|
validate_model_tier_run(run)
|
|
577
577
|
|
|
578
578
|
|
|
579
|
-
def
|
|
579
|
+
def test_third_party_own_tier_maps_to_opus_cli_bind_passes() -> None:
|
|
580
580
|
run = ModelTierRun(
|
|
581
581
|
own_tier="ThirdParty",
|
|
582
|
-
candidate_tiers=["
|
|
583
|
-
attempts=[{"tier": "
|
|
584
|
-
selected_tier="
|
|
582
|
+
candidate_tiers=["Opus"],
|
|
583
|
+
attempts=[{"tier": "Opus", "result": "cli"}],
|
|
584
|
+
selected_tier="Opus",
|
|
585
585
|
)
|
|
586
586
|
assert validate_model_tier_run(run) is None
|
|
587
587
|
|
|
@@ -589,9 +589,9 @@ def test_third_party_own_tier_maps_to_fable_cli_bind_passes() -> None:
|
|
|
589
589
|
def test_third_party_own_tier_lowercase_cli_bind_passes() -> None:
|
|
590
590
|
run = ModelTierRun(
|
|
591
591
|
own_tier="thirdparty",
|
|
592
|
-
candidate_tiers=["
|
|
593
|
-
attempts=[{"tier": "
|
|
594
|
-
selected_tier="
|
|
592
|
+
candidate_tiers=["opus"],
|
|
593
|
+
attempts=[{"tier": "opus", "result": "cli"}],
|
|
594
|
+
selected_tier="opus",
|
|
595
595
|
)
|
|
596
596
|
assert validate_model_tier_run(run) is None
|
|
597
597
|
|
|
@@ -599,9 +599,9 @@ def test_third_party_own_tier_lowercase_cli_bind_passes() -> None:
|
|
|
599
599
|
def test_self_token_is_not_bind_success_raises() -> None:
|
|
600
600
|
run = ModelTierRun(
|
|
601
601
|
own_tier="Opus",
|
|
602
|
-
candidate_tiers=["
|
|
603
|
-
attempts=[{"tier": "
|
|
604
|
-
selected_tier="
|
|
602
|
+
candidate_tiers=["Opus"],
|
|
603
|
+
attempts=[{"tier": "Opus", "result": "self"}],
|
|
604
|
+
selected_tier="Opus",
|
|
605
605
|
)
|
|
606
606
|
with pytest.raises(ModelTierRunError):
|
|
607
607
|
validate_model_tier_run(run)
|
|
@@ -610,9 +610,9 @@ def test_self_token_is_not_bind_success_raises() -> None:
|
|
|
610
610
|
def test_third_party_self_token_is_not_bind_success_raises() -> None:
|
|
611
611
|
run = ModelTierRun(
|
|
612
612
|
own_tier="ThirdParty",
|
|
613
|
-
candidate_tiers=["
|
|
614
|
-
attempts=[{"tier": "
|
|
615
|
-
selected_tier="
|
|
613
|
+
candidate_tiers=["Opus"],
|
|
614
|
+
attempts=[{"tier": "Opus", "result": "self"}],
|
|
615
|
+
selected_tier="Opus",
|
|
616
616
|
)
|
|
617
617
|
with pytest.raises(ModelTierRunError):
|
|
618
618
|
validate_model_tier_run(run)
|
|
@@ -632,8 +632,8 @@ def test_third_party_host_legacy_single_tier_self_bind_raises() -> None:
|
|
|
632
632
|
def test_third_party_cli_exhausted_fail_closed_passes() -> None:
|
|
633
633
|
run = ModelTierRun(
|
|
634
634
|
own_tier="ThirdParty",
|
|
635
|
-
candidate_tiers=["
|
|
636
|
-
attempts=[{"tier": "
|
|
635
|
+
candidate_tiers=["Opus"],
|
|
636
|
+
attempts=[{"tier": "Opus", "result": "unavailable"}],
|
|
637
637
|
selected_tier=None,
|
|
638
638
|
fallback_reason=(
|
|
639
639
|
"third-party host CLI Claude-chain exhausted; fail closed"
|
|
@@ -645,8 +645,8 @@ def test_third_party_cli_exhausted_fail_closed_passes() -> None:
|
|
|
645
645
|
def test_third_party_cli_exhausted_without_fallback_reason_raises() -> None:
|
|
646
646
|
run = ModelTierRun(
|
|
647
647
|
own_tier="ThirdParty",
|
|
648
|
-
candidate_tiers=["
|
|
649
|
-
attempts=[{"tier": "
|
|
648
|
+
candidate_tiers=["Opus"],
|
|
649
|
+
attempts=[{"tier": "Opus", "result": "unavailable"}],
|
|
650
650
|
selected_tier=None,
|
|
651
651
|
)
|
|
652
652
|
with pytest.raises(ModelTierRunError):
|
|
@@ -656,9 +656,9 @@ def test_third_party_cli_exhausted_without_fallback_reason_raises() -> None:
|
|
|
656
656
|
def test_third_party_cli_selected_tier_mismatch_raises() -> None:
|
|
657
657
|
run = ModelTierRun(
|
|
658
658
|
own_tier="ThirdParty",
|
|
659
|
-
candidate_tiers=["
|
|
660
|
-
attempts=[{"tier": "
|
|
661
|
-
selected_tier="
|
|
659
|
+
candidate_tiers=["Opus"],
|
|
660
|
+
attempts=[{"tier": "Opus", "result": "cli"}],
|
|
661
|
+
selected_tier="Sonnet",
|
|
662
662
|
)
|
|
663
663
|
with pytest.raises(ModelTierRunError):
|
|
664
664
|
validate_model_tier_run(run)
|
|
@@ -667,9 +667,9 @@ def test_third_party_cli_selected_tier_mismatch_raises() -> None:
|
|
|
667
667
|
def test_claude_host_self_token_is_not_spawn_success_raises() -> None:
|
|
668
668
|
run = ModelTierRun(
|
|
669
669
|
own_tier="Opus",
|
|
670
|
-
candidate_tiers=["
|
|
671
|
-
attempts=[{"tier": "
|
|
672
|
-
selected_tier="
|
|
670
|
+
candidate_tiers=["Opus"],
|
|
671
|
+
attempts=[{"tier": "Opus", "result": "self"}],
|
|
672
|
+
selected_tier="Opus",
|
|
673
673
|
)
|
|
674
674
|
with pytest.raises(ModelTierRunError):
|
|
675
675
|
validate_model_tier_run(run)
|
|
@@ -699,10 +699,10 @@ def test_codex_host_astra_codex_token_counts_as_success() -> None:
|
|
|
699
699
|
assert validate_model_tier_run(run) is None
|
|
700
700
|
|
|
701
701
|
|
|
702
|
-
def
|
|
702
|
+
def test_codex_host_opus_then_astra_walk_raises() -> None:
|
|
703
703
|
run = ModelTierRun(
|
|
704
704
|
own_tier="Opus",
|
|
705
|
-
candidate_tiers=["
|
|
705
|
+
candidate_tiers=["Opus", ADVISOR_MODEL_TIER],
|
|
706
706
|
attempts=[{"tier": ADVISOR_MODEL_TIER, "result": "spawned"}],
|
|
707
707
|
selected_tier=ADVISOR_MODEL_TIER,
|
|
708
708
|
is_astra_enabled=True,
|