claude-dev-env 1.95.0 → 2.0.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/_shared/advisor/CLAUDE.md +2 -2
- package/_shared/advisor/advisor-protocol.md +20 -20
- package/_shared/advisor/scripts/config/advisor_scripts_constants/model_tier_run_validator_constants.py +15 -12
- package/_shared/advisor/scripts/model_tier_run_validator.py +11 -10
- package/_shared/advisor/scripts/tests/test_model_tier_run_validator.py +25 -19
- package/_shared/advisor/scripts/tests/test_tier_model_ids.py +17 -17
- package/_shared/advisor/scripts/tier_model_ids.py +18 -18
- package/_shared/pr-loop/CLAUDE.md +1 -0
- package/_shared/pr-loop/scripts/CLAUDE.md +2 -1
- package/_shared/pr-loop/scripts/README.md +1 -0
- package/_shared/pr-loop/scripts/code_rules_gate.py +253 -1980
- package/_shared/pr-loop/scripts/code_rules_gate_parts/CLAUDE.md +32 -0
- package/_shared/pr-loop/scripts/code_rules_gate_parts/__init__.py +7 -0
- package/_shared/pr-loop/scripts/code_rules_gate_parts/added_line_maps.py +268 -0
- package/_shared/pr-loop/scripts/code_rules_gate_parts/enforcer_loading.py +172 -0
- package/_shared/pr-loop/scripts/code_rules_gate_parts/gate_arguments.py +70 -0
- package/_shared/pr-loop/scripts/code_rules_gate_parts/gate_running.py +326 -0
- package/_shared/pr-loop/scripts/code_rules_gate_parts/git_blob_readers.py +85 -0
- package/_shared/pr-loop/scripts/code_rules_gate_parts/git_file_sets.py +331 -0
- package/_shared/pr-loop/scripts/code_rules_gate_parts/staged_test_running.py +369 -0
- package/_shared/pr-loop/scripts/code_rules_gate_parts/tests/conftest.py +14 -0
- package/_shared/pr-loop/scripts/code_rules_gate_parts/tests/test_added_line_maps.py +118 -0
- package/_shared/pr-loop/scripts/code_rules_gate_parts/tests/test_enforcer_loading.py +17 -0
- package/_shared/pr-loop/scripts/code_rules_gate_parts/tests/test_gate_arguments.py +29 -0
- package/_shared/pr-loop/scripts/code_rules_gate_parts/tests/test_gate_running.py +99 -0
- package/_shared/pr-loop/scripts/code_rules_gate_parts/tests/test_git_blob_readers.py +69 -0
- package/_shared/pr-loop/scripts/code_rules_gate_parts/tests/test_git_file_sets.py +137 -0
- package/_shared/pr-loop/scripts/code_rules_gate_parts/tests/test_staged_test_running.py +116 -0
- package/_shared/pr-loop/scripts/code_rules_gate_parts/tests/test_violation_scoping.py +75 -0
- package/_shared/pr-loop/scripts/code_rules_gate_parts/tests/test_wrapper_plumb_check.py +49 -0
- package/_shared/pr-loop/scripts/code_rules_gate_parts/violation_scoping.py +328 -0
- package/_shared/pr-loop/scripts/code_rules_gate_parts/wrapper_plumb_check.py +206 -0
- package/_shared/pr-loop/scripts/pr_loop_shared_constants/code_rules_gate_constants.py +24 -17
- package/_shared/pr-loop/scripts/pr_loop_shared_constants/reviews_disabled_constants.py +1 -0
- package/_shared/pr-loop/scripts/reviews_disabled.py +19 -2
- package/_shared/pr-loop/scripts/test_code_rules_gate.py +278 -0
- package/_shared/pr-loop/scripts/tests/test_code_rules_gate_constants.py +6 -39
- package/_shared/pr-loop/scripts/tests/test_reviews_disabled.py +43 -0
- package/_shared/pr-loop/worker-spawn.md +186 -0
- package/agents/code-verifier.md +1 -1
- package/bin/ever-shipped-skills.mjs +3 -0
- package/bin/expand_home_directory_tokens.mjs +1 -1
- package/bin/install.mjs +5 -2
- package/hooks/advisory/refactor_guard.py +3 -4
- package/hooks/blocking/CLAUDE.md +7 -1
- package/hooks/blocking/block_main_commit.py +2 -2
- package/hooks/blocking/claude_md_orphan_file_blocker.py +75 -699
- package/hooks/blocking/claude_md_orphan_file_blocker_parts/CLAUDE.md +28 -0
- package/hooks/blocking/claude_md_orphan_file_blocker_parts/__init__.py +1 -0
- package/hooks/blocking/claude_md_orphan_file_blocker_parts/config/__init__.py +1 -0
- package/hooks/blocking/claude_md_orphan_file_blocker_parts/config/orphan_blocker_constants.py +18 -0
- package/hooks/blocking/claude_md_orphan_file_blocker_parts/decision.py +81 -0
- package/hooks/blocking/claude_md_orphan_file_blocker_parts/references.py +307 -0
- package/hooks/blocking/claude_md_orphan_file_blocker_parts/scan_plan.py +124 -0
- package/hooks/blocking/claude_md_orphan_file_blocker_parts/subtree_scan.py +179 -0
- package/hooks/blocking/claude_md_orphan_file_blocker_parts/tests/conftest.py +10 -0
- package/hooks/blocking/claude_md_orphan_file_blocker_parts/tests/test_decision.py +34 -0
- package/hooks/blocking/claude_md_orphan_file_blocker_parts/tests/test_references.py +42 -0
- package/hooks/blocking/claude_md_orphan_file_blocker_parts/tests/test_scan_plan.py +27 -0
- package/hooks/blocking/claude_md_orphan_file_blocker_parts/tests/test_subtree_scan.py +30 -0
- package/hooks/blocking/code_rules_boolean_mustcheck.py +1 -1
- package/hooks/blocking/code_rules_mock_completeness.py +1 -1
- package/hooks/blocking/code_rules_optional_params.py +2 -2
- package/hooks/blocking/code_rules_shared.py +1 -1
- package/hooks/blocking/code_rules_test_assertions.py +1 -1
- package/hooks/blocking/code_rules_typeddict_stub.py +1 -1
- package/hooks/blocking/gh_pr_author_enforcer.py +1 -1
- package/hooks/blocking/inventory_intent_records/CLAUDE.md +26 -0
- package/hooks/blocking/inventory_intent_records/__init__.py +1 -0
- package/hooks/blocking/inventory_intent_records/config/__init__.py +1 -0
- package/hooks/blocking/inventory_intent_records/config/intent_records_constants.py +20 -0
- package/hooks/blocking/inventory_intent_records/records.py +271 -0
- package/hooks/blocking/inventory_intent_records/tests/conftest.py +10 -0
- package/hooks/blocking/inventory_intent_records/tests/test_records.py +80 -0
- package/hooks/blocking/package_inventory_stale_blocker.py +54 -384
- package/hooks/blocking/package_inventory_stale_blocker_parts/CLAUDE.md +26 -0
- package/hooks/blocking/package_inventory_stale_blocker_parts/__init__.py +1 -0
- package/hooks/blocking/package_inventory_stale_blocker_parts/config/__init__.py +1 -0
- package/hooks/blocking/package_inventory_stale_blocker_parts/config/inventory_blocker_constants.py +16 -0
- package/hooks/blocking/package_inventory_stale_blocker_parts/decision.py +84 -0
- package/hooks/blocking/package_inventory_stale_blocker_parts/inventory_detection.py +307 -0
- package/hooks/blocking/package_inventory_stale_blocker_parts/tests/conftest.py +10 -0
- package/hooks/blocking/package_inventory_stale_blocker_parts/tests/test_decision.py +38 -0
- package/hooks/blocking/package_inventory_stale_blocker_parts/tests/test_inventory_detection.py +61 -0
- package/hooks/blocking/pii_payload_scan.py +138 -42
- package/hooks/blocking/pii_prevention_blocker.py +185 -291
- package/hooks/blocking/pii_prevention_blocker_parts/CLAUDE.md +24 -0
- package/hooks/blocking/pii_prevention_blocker_parts/__init__.py +1 -0
- package/hooks/blocking/pii_prevention_blocker_parts/config/__init__.py +1 -0
- package/hooks/blocking/pii_prevention_blocker_parts/config/repository_resolution_constants.py +28 -0
- package/hooks/blocking/pii_prevention_blocker_parts/repository_exemption.py +214 -0
- package/hooks/blocking/pii_prevention_blocker_parts/repository_resolution.py +208 -0
- package/hooks/blocking/pr_description_command_parser.py +8 -4
- package/hooks/blocking/precommit_code_rules_gate.py +3 -3
- package/hooks/blocking/tdd_enforcer.py +97 -608
- package/hooks/blocking/tdd_enforcer_parts/CLAUDE.md +30 -0
- package/hooks/blocking/tdd_enforcer_parts/__init__.py +1 -0
- package/hooks/blocking/tdd_enforcer_parts/candidate_paths.py +142 -0
- package/hooks/blocking/tdd_enforcer_parts/config/__init__.py +1 -0
- package/hooks/blocking/tdd_enforcer_parts/config/tdd_enforcer_constants.py +32 -0
- package/hooks/blocking/tdd_enforcer_parts/content_analysis.py +268 -0
- package/hooks/blocking/tdd_enforcer_parts/decisions.py +92 -0
- package/hooks/blocking/tdd_enforcer_parts/freshness.py +80 -0
- package/hooks/blocking/tdd_enforcer_parts/git_tracking.py +63 -0
- package/hooks/blocking/tdd_enforcer_parts/path_classification.py +119 -0
- package/hooks/blocking/tdd_enforcer_parts/tests/conftest.py +10 -0
- package/hooks/blocking/tdd_enforcer_parts/tests/test_candidate_paths.py +31 -0
- package/hooks/blocking/tdd_enforcer_parts/tests/test_content_analysis.py +30 -0
- package/hooks/blocking/tdd_enforcer_parts/tests/test_decisions.py +34 -0
- package/hooks/blocking/tdd_enforcer_parts/tests/test_freshness.py +28 -0
- package/hooks/blocking/tdd_enforcer_parts/tests/test_git_tracking.py +48 -0
- package/hooks/blocking/tdd_enforcer_parts/tests/test_path_classification.py +36 -0
- package/hooks/blocking/test_inventory_deadlock_resolution.py +154 -0
- package/hooks/blocking/test_pii_payload_scan.py +168 -0
- package/hooks/blocking/test_tdd_enforcer_restore.py +108 -0
- package/hooks/blocking/tests/conftest.py +10 -0
- package/hooks/blocking/tests/test_pii_prevention_blocker.py +260 -0
- package/hooks/blocking/tests/test_repository_exemption.py +105 -0
- package/hooks/blocking/tests/test_repository_resolution.py +108 -0
- package/hooks/diagnostic/hook_log_extractor.py +12 -10
- package/hooks/git-hooks/post_commit.py +3 -4
- package/hooks/hooks_constants/CLAUDE.md +2 -2
- package/hooks/hooks_constants/banned_identifiers_constants.py +0 -1
- package/hooks/hooks_constants/code_rules_path_utils_constants.py +1 -1
- package/hooks/hooks_constants/local_identity.py +59 -8
- package/hooks/hooks_constants/pii_prevention_constants.py +0 -6
- package/hooks/hooks_constants/test_local_identity.py +105 -3
- package/hooks/pyproject.toml +13 -36
- package/hooks/session/plugin_data_dir_cleanup.py +0 -1
- package/hooks/validation/mypy_validator.py +2 -2
- package/hooks/validators/health_check.py +1 -0
- package/hooks/validators/mypy_integration.py +2 -0
- package/hooks/validators/ruff_integration.py +3 -0
- package/hooks/workflow/auto_formatter.py +5 -4
- package/package.json +1 -1
- package/scripts/CLAUDE.md +4 -0
- package/scripts/dev_env_scripts_constants/CLAUDE.md +6 -4
- package/scripts/dev_env_scripts_constants/code_review_constants.py +71 -0
- package/scripts/dev_env_scripts_constants/grok_worker_constants.py +435 -0
- package/scripts/dev_env_scripts_constants/timing.py +7 -1
- package/scripts/grok_headless_runner.py +294 -0
- package/scripts/grok_worker_preflight.py +410 -0
- package/scripts/invoke_code_review.py +463 -0
- package/scripts/resolve_worker_spawn.py +619 -0
- package/scripts/spawn_grok_batch.py +672 -0
- package/scripts/test_grok_headless_runner.py +626 -0
- package/scripts/test_grok_worker_preflight.py +1054 -0
- package/scripts/test_invoke_code_review.py +672 -0
- package/scripts/test_resolve_worker_spawn.py +1014 -0
- package/scripts/test_spawn_grok_batch.py +1017 -0
- package/skills/CLAUDE.md +5 -3
- package/skills/_shared/pr-loop/scripts/build_audit_prompt.py +72 -13
- package/skills/_shared/pr-loop/scripts/build_fix_prompt.py +121 -14
- package/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/path_resolver_constants.py +78 -0
- package/skills/_shared/pr-loop/scripts/test_build_audit_prompt.py +121 -0
- package/skills/_shared/pr-loop/scripts/test_build_fix_prompt.py +196 -6
- package/skills/autoconverge/CLAUDE.md +3 -3
- package/skills/autoconverge/SKILL.md +9 -3
- package/skills/autoconverge/reference/CLAUDE.md +2 -2
- package/skills/autoconverge/reference/convergence.md +33 -11
- package/skills/autoconverge/reference/stop-conditions.md +16 -5
- package/skills/autoconverge/workflow/CLAUDE.md +2 -1
- package/skills/autoconverge/workflow/converge.clean-audit.test.mjs +7 -2
- package/skills/autoconverge/workflow/converge.codex-gate.test.mjs +300 -0
- package/skills/autoconverge/workflow/converge.contract.test.mjs +5 -5
- package/skills/autoconverge/workflow/converge.copilot-gate.test.mjs +29 -29
- package/skills/autoconverge/workflow/converge.fix-progress.test.mjs +1 -1
- package/skills/autoconverge/workflow/converge.mjs +200 -16
- package/skills/bugteam/CLAUDE.md +2 -2
- package/skills/bugteam/CONSTRAINTS.md +3 -2
- package/skills/bugteam/PROMPTS.md +7 -6
- package/skills/bugteam/SKILL.md +18 -13
- package/skills/bugteam/reference/audit-and-teammates.md +215 -35
- package/skills/bugteam/reference/design-rationale.md +1 -1
- package/skills/bugteam/reference/obstacles/CLAUDE.md +1 -1
- package/skills/bugteam/reference/team-setup.md +8 -2
- package/skills/codex-review/CLAUDE.md +46 -0
- package/skills/codex-review/SKILL.md +181 -0
- package/skills/codex-review/reference/CLAUDE.md +15 -0
- package/skills/codex-review/reference/cli-contract.md +253 -0
- package/skills/codex-review/reference/loop-integration.md +118 -0
- package/skills/codex-review/scripts/codex_down_classifier.py +98 -0
- package/skills/codex-review/scripts/codex_review_scripts_constants/CLAUDE.md +18 -0
- package/skills/codex-review/scripts/codex_review_scripts_constants/__init__.py +1 -0
- package/skills/codex-review/scripts/codex_review_scripts_constants/classifier_constants.py +35 -0
- package/skills/codex-review/scripts/codex_review_scripts_constants/codex_usage_probe_constants.py +86 -0
- package/skills/codex-review/scripts/codex_review_scripts_constants/findings_constants.py +18 -0
- package/skills/codex-review/scripts/codex_review_scripts_constants/run_constants.py +45 -0
- package/skills/codex-review/scripts/codex_usage_probe.py +573 -0
- package/skills/codex-review/scripts/fixtures/auth_failure_synthetic.txt +1 -0
- package/skills/codex-review/scripts/fixtures/config_load_failure_v0.125.0.txt +1 -0
- package/skills/codex-review/scripts/fixtures/freeform_findings_v0.144.3.txt +6 -0
- package/skills/codex-review/scripts/fixtures/model_rejection_v0.125.0.jsonl +5 -0
- package/skills/codex-review/scripts/fixtures/structured_findings.txt +13 -0
- package/skills/codex-review/scripts/fixtures/success_stream_v0.144.3.jsonl +6 -0
- package/skills/codex-review/scripts/fixtures/unknown_failure_synthetic.txt +1 -0
- package/skills/codex-review/scripts/fixtures/usage_limit_synthetic.txt +1 -0
- package/skills/codex-review/scripts/parse_codex_findings.py +207 -0
- package/skills/codex-review/scripts/run_codex_review.py +415 -0
- package/skills/codex-review/scripts/test_codex_down_classifier.py +143 -0
- package/skills/codex-review/scripts/test_codex_usage_probe.py +678 -0
- package/skills/codex-review/scripts/test_parse_codex_findings.py +130 -0
- package/skills/codex-review/scripts/test_run_codex_review.py +812 -0
- package/skills/codex-review/test_skill_scaffold.py +192 -0
- package/skills/grok-spawn/CLAUDE.md +28 -0
- package/skills/grok-spawn/SKILL.md +226 -0
- package/skills/grok-spawn/reference/flag-profiles.md +132 -0
- package/skills/grok-spawn/reference/worker-briefs.md +152 -0
- package/skills/grokify/SKILL.md +9 -1
- package/skills/grokify/capability-claims.test.mjs +28 -0
- package/skills/grokify/evals/README.md +72 -0
- package/skills/grokify/evals/parse-payload.test.mjs +171 -0
- package/skills/grokify/evals/run-capability-evals.mjs +545 -0
- package/skills/orchestrator/SKILL.md +15 -11
- package/skills/orchestrator-refresh/SKILL.md +5 -5
- package/skills/pr-converge/SKILL.md +34 -13
- package/skills/pr-converge/reference/convergence-gates.md +42 -15
- package/skills/pr-converge/reference/fix-protocol.md +1 -1
- package/skills/pr-converge/reference/ground-rules.md +1 -1
- package/skills/pr-converge/reference/per-tick.md +130 -42
- package/skills/pr-converge/reference/state-schema.md +10 -0
- package/skills/pr-converge/scripts/CLAUDE.md +2 -0
- package/skills/pr-converge/scripts/_pr_converge_path_setup.py +5 -1
- package/skills/pr-converge/scripts/check_convergence.py +605 -29
- package/skills/pr-converge/scripts/check_convergence_availability.py +232 -0
- package/skills/pr-converge/scripts/check_convergence_gates.py +279 -235
- package/skills/pr-converge/scripts/check_convergence_thread_gates.py +1 -1
- package/skills/pr-converge/scripts/pr_converge_scripts_constants/convergence_gate_constants.py +36 -2
- package/skills/pr-converge/scripts/test__pr_converge_path_setup.py +4 -0
- package/skills/pr-converge/scripts/test_check_convergence.py +71 -3
- package/skills/pr-converge/scripts/test_check_convergence_availability.py +326 -0
- package/skills/pr-converge/scripts/test_check_convergence_codex.py +507 -0
- package/skills/pr-converge/scripts/test_check_convergence_contract.py +89 -17
- package/skills/pr-converge/scripts/test_check_convergence_fixture.py +179 -0
- package/skills/pr-converge/scripts/test_check_convergence_gates.py +84 -68
- package/skills/pr-converge/scripts/test_check_convergence_thread_gates.py +24 -0
- package/skills/pr-converge/test_step5_host_branch.py +106 -0
- package/skills/pr-loop-cloud-transport/SKILL.md +2 -0
- package/skills/reviewer-gates/SKILL.md +7 -5
- package/skills/team-advisor/SKILL.md +7 -7
|
@@ -2,13 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
Warm-advisor bind-and-consult protocol shared by `team-advisor`, `orchestrator`, `orchestrator-refresh`, and every executor subagent `orchestrator` routes work to. Changes here affect all of these simultaneously — treat this as a breaking-change surface.
|
|
4
4
|
|
|
5
|
-
Host profile (Claude vs
|
|
5
|
+
Host profile (Claude vs third-party) is detected first; Claude walks the multi-tier Agent spawn ladder (CLI chain as fallback), a third-party host binds a max-tier Claude advisor through the CLI Claude-chain (fail closed when the chain cannot serve) with a separate executor paste block.
|
|
6
6
|
|
|
7
7
|
## Key documents
|
|
8
8
|
|
|
9
9
|
| File | Purpose |
|
|
10
10
|
|---|---|
|
|
11
|
-
| `advisor-protocol.md` | Host profiles first, model floor, warm-up / CLI bind procedure and charter, consult format and cadence, lifecycle ownership (Agent spawn on Claude / CLI re-bind on
|
|
11
|
+
| `advisor-protocol.md` | Host profiles first, model floor, warm-up / CLI bind procedure and charter, consult format and cadence, lifecycle ownership (Agent spawn on Claude / CLI re-bind on a third-party host), host-matched Advisor blocks for executor spawns, and the shared CLI Claude-chain |
|
|
12
12
|
|
|
13
13
|
## Subdirectory
|
|
14
14
|
|
|
@@ -2,28 +2,28 @@
|
|
|
2
2
|
|
|
3
3
|
Shared spawn-once, consult-by-message protocol for a warm advisor. Two skills depend on this: `team-advisor` (binds the advisor for its own consulting session) and `orchestrator` (binds the same advisor and lets its own routed executor subagents consult it too). Executor spawn prompts are a third consumer, via the host-matched Advisor block below.
|
|
4
4
|
|
|
5
|
-
**First step of every bind:** detect the host profile (next section). Do not walk the model-floor ladder, spawn `session-advisor`, or open the CLI fallback until the host is known. On
|
|
5
|
+
**First step of every bind:** detect the host profile (next section). Do not walk the model-floor ladder, spawn `session-advisor`, or open the CLI fallback until the host is known. On a third-party host, skip straight to **Host profiles → Third-party host**. On Claude, continue with **Model floor** and the rest of this document.
|
|
6
6
|
|
|
7
7
|
## Host profiles
|
|
8
8
|
|
|
9
|
-
Detect the host profile **before** any model-floor walk. Source of truth for names and detection: `HOST_PROFILE_CLAUDE`, `
|
|
9
|
+
Detect the host profile **before** any model-floor walk. Source of truth for names and detection: `HOST_PROFILE_CLAUDE`, `HOST_PROFILE_THIRD_PARTY`, `ALL_HOST_PROFILES`, and `detect_host_profile(...)` in `$HOME/.claude/_shared/advisor/scripts/config/advisor_scripts_constants/model_tier_run_validator_constants.py` and `tier_model_ids.py`.
|
|
10
10
|
|
|
11
11
|
Detection order:
|
|
12
12
|
|
|
13
|
-
1. `ADVISOR_HOST_PROFILE=
|
|
14
|
-
2. `
|
|
13
|
+
1. `ADVISOR_HOST_PROFILE=ThirdParty` or `=Claude` (explicit override; any letter case).
|
|
14
|
+
2. `THIRD_PARTY=1` (or `true` / `yes` / `on`) — a third-party (non-Claude) harness.
|
|
15
15
|
3. Default: Claude.
|
|
16
16
|
|
|
17
17
|
### Claude host
|
|
18
18
|
|
|
19
19
|
Use the **Model floor** ladder below (Fable → Opus → Sonnet → Haiku). Warm-up spawns `subagent_type: session-advisor` via the Agent tool; consults go through `SendMessage` to that warm agent. When every candidate down to the floor fails, take the CLI Claude-chain fallback. Paste the **Claude host** Advisor block into every executor spawn prompt.
|
|
20
20
|
|
|
21
|
-
###
|
|
21
|
+
### Third-party host
|
|
22
22
|
|
|
23
|
-
|
|
23
|
+
A third-party (non-Claude) harness cannot spawn a Claude `session-advisor` through the Agent tool. Bind a **max-tier Claude advisor** through the shared CLI Claude-chain (account usage failover). Do **not** treat this third-party session as the advisor.
|
|
24
24
|
|
|
25
25
|
1. Detect host profile first (this section).
|
|
26
|
-
2. Set the advisor floor to **Opus** so the walk is `candidate_tiers = ["Fable", "Opus"]` with `own_tier = Opus`. The walk never drops to Sonnet or Haiku on a
|
|
26
|
+
2. Set the advisor floor to **Opus** so the walk is `candidate_tiers = ["Fable", "Opus"]` with `own_tier = Opus`. The walk never drops to Sonnet or Haiku on a third-party host.
|
|
27
27
|
3. **CLI bind (primary path):** for each candidate top-down, pipe a charter file into:
|
|
28
28
|
|
|
29
29
|
```
|
|
@@ -32,10 +32,10 @@ Grok Build / xAI cannot spawn a Claude `session-advisor` through the Agent tool.
|
|
|
32
32
|
|
|
33
33
|
Use `--model fable --effort high` on Fable; use `--model opus --effort max` on Opus. The chain runner walks `~/.claude/claude-chain.json` (binaries such as `claude`, `claude-ev`, `claude-editor`, `claude-mel`) and fails over only on a usage-limit signature.
|
|
34
34
|
4. Stop at the first successful bind. Record `{tier, result: "cli"}` and set `selected_tier` to that tier. Persist `session_id` from the JSON events (any event carries it; reply text is the `type == "result"` event's `.result` field). 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
|
-
5. **Fail closed:** when every candidate fails (chain exhausted or model unavailable), set `selected_tier = null` and a `fallback_reason`, report that the advisor is unreachable, and **stop**. Do **not** answer ENDORSE / CORRECTION / PLAN / STOP as this
|
|
36
|
-
6. Paste the **
|
|
35
|
+
5. **Fail closed:** when every candidate fails (chain exhausted or model unavailable), set `selected_tier = null` and a `fallback_reason`, report that the advisor is unreachable, and **stop**. Do **not** answer ENDORSE / CORRECTION / PLAN / STOP as this third-party session. Do **not** self-endorse.
|
|
36
|
+
6. Paste the **Third-party host** Advisor block into every executor spawn prompt — never the Claude SendMessage block. Executors report to the orchestrating session; that session consults the bound Claude CLI advisor and relays the four-signal reply.
|
|
37
37
|
|
|
38
|
-
Resolve a
|
|
38
|
+
Resolve a third-party session's own model field with `resolve_cli_model_id("ThirdParty")` → `third-party` when a host model alias is required. The **advisor** bind uses Fable/Opus aliases only.
|
|
39
39
|
|
|
40
40
|
## Model floor
|
|
41
41
|
|
|
@@ -43,9 +43,9 @@ Resolve a Grok session's own model field with `resolve_cli_model_id("Grok")` →
|
|
|
43
43
|
- `team-advisor`: the sole consumer is the calling session itself, so the floor is just that session's own tier.
|
|
44
44
|
- `orchestrator`: the consumer set is the orchestrating session plus every tier named in its routing table, so the floor is the max of those.
|
|
45
45
|
|
|
46
|
-
**
|
|
46
|
+
**Third-party host:** the CLI advisor floor is fixed at **Opus** (walk Fable → Opus only). The third-party session's own tier is not the advisor floor — see **Host profiles → Third-party host**.
|
|
47
47
|
|
|
48
|
-
Ladder, strongest first (canonical Title Case names: `Fable`, `Opus`, `Sonnet`, `Haiku`; the validator accepts any letter case and normalizes to Title Case): Fable, Opus, Sonnet, Haiku. Read the floor tier — the lower bound only — then try binds top-down from Fable, stopping at the floor tier — never bind below it. On a Claude host each walk attempt sets the Agent tool `model:` field to the short alias for that attempt's candidate tier (`resolve_cli_model_id(candidate_tier)` — for example `opus`, not Title Case `Opus`). On a
|
|
48
|
+
Ladder, strongest first (canonical Title Case names: `Fable`, `Opus`, `Sonnet`, `Haiku`; the validator accepts any letter case and normalizes to Title Case): Fable, Opus, Sonnet, Haiku. Read the floor tier — the lower bound only — then try binds top-down from Fable, stopping at the floor tier — never bind below it. On a Claude host each walk attempt sets the Agent tool `model:` field to the short alias for that attempt's candidate tier (`resolve_cli_model_id(candidate_tier)` — for example `opus`, not Title Case `Opus`). On a third-party host each walk attempt uses the CLI chain with that alias and the effort flags in **Host profiles → Third-party host**. The advisor is created at `selected_tier` (the first ladder tier that actually bound), which may sit above the floor. If even the floor tier fails on a Claude host, move to the CLI fallback below; on a third-party host the CLI chain **is** the primary path, so floor failure is fail-closed (report unreachable).
|
|
49
49
|
|
|
50
50
|
Emit a structured spawn-walk log so it can be checked mechanically rather than inferred from a transcript. Record: `own_tier` (the floor tier), `candidate_tiers` (the ladder slice down to that floor), `attempts` (one `{tier, result}` entry appended as each bind try happens, `result` one of `spawned` for a Claude Agent spawn, `cli` for a CLI Claude-chain bind, or a failure reason such as `unavailable`), and `selected_tier` (the tier of the first successful bind — first `spawned` or `cli` entry — or `null` paired with a `fallback_reason` string when none bound). Write the log as JSON with those field names to a path the session controls — typically `<job-temp-dir>/model-tier-run.json` (or the OS temp directory when no job directory exists). Check it with:
|
|
51
51
|
|
|
@@ -55,11 +55,11 @@ python "$HOME/.claude/_shared/advisor/scripts/model_tier_run_validator.py" <path
|
|
|
55
55
|
|
|
56
56
|
Exit code `0` means every invariant holds; `1` means a ladder invariant failed; `2` means the path or JSON was unusable. The same checks are available in-process via `validate_model_tier_run(run)`.
|
|
57
57
|
|
|
58
|
-
The validator checks ladder shape only (candidate slice, attempt order, success-token rules per tier). Host policy on top: a
|
|
58
|
+
The validator checks ladder shape only (candidate slice, attempt order, success-token rules per tier). Host policy on top: a third-party host with `selected_tier=null` after an exhausted Fable→Opus walk must fail closed (report unreachable; never self-endorse).
|
|
59
59
|
|
|
60
60
|
## Warm-up (once per session)
|
|
61
61
|
|
|
62
|
-
On a **
|
|
62
|
+
On a **third-party host**, follow **Host profiles → Third-party host** (CLI Claude-chain bind at Fable then Opus; no Agent-tool `session-advisor` spawn). Charter the CLI session as a standing reviewer that only answers with ENDORSE / CORRECTION / PLAN / STOP — same consult contract as the Agent path, without SendMessage.
|
|
63
63
|
|
|
64
64
|
On a **Claude host**, the consuming skill's session walks the candidate tiers top-down. For each attempt, spawn with:
|
|
65
65
|
- `subagent_type: session-advisor` (see [`agents/session-advisor.md`](../../agents/session-advisor.md) for the full signal contract).
|
|
@@ -92,7 +92,7 @@ Each consult carries, in order: who you are and your assignment (only needed on
|
|
|
92
92
|
|
|
93
93
|
**Report-back rule.** After a CORRECTION or PLAN, your next consult on that topic opens with what happened when you followed it.
|
|
94
94
|
|
|
95
|
-
Treat the reply as a serious second opinion: a CORRECTION — whether it names a wrong step or a risk worth closing — is something to address before treating the plan or the work as done. A STOP, or a consult that finds the advisor unreachable, is reported up rather than retried — team-advisor's sole consumer is the session itself, so it reports to the user; orchestrator's executors report to the orchestrating session, which decides. When the advisor becomes unreachable, report that to the session that owns its lifecycle (see below); that session alone decides whether to respawn (Claude Agent or
|
|
95
|
+
Treat the reply as a serious second opinion: a CORRECTION — whether it names a wrong step or a risk worth closing — is something to address before treating the plan or the work as done. A STOP, or a consult that finds the advisor unreachable, is reported up rather than retried — team-advisor's sole consumer is the session itself, so it reports to the user; orchestrator's executors report to the orchestrating session, which decides. When the advisor becomes unreachable, report that to the session that owns its lifecycle (see below); that session alone decides whether to respawn (Claude Agent or third-party CLI re-bind). A third-party host that cannot re-bind fails closed and reports to the user — it does not answer the four signals as itself.
|
|
96
96
|
|
|
97
97
|
## Advisor block — paste the host-matched block into every executor spawn prompt
|
|
98
98
|
|
|
@@ -102,9 +102,9 @@ Each paragraph is self-contained — the executor receives only this text, not t
|
|
|
102
102
|
|
|
103
103
|
> A shared session advisor named `<name>` is reachable via SendMessage. Consult it before locking in a nontrivial approach, once you believe your assignment is done, before any hard-to-reverse action, when the same failure repeats or progress has stalled, and when the chosen approach is being reconsidered. Open each consult with who you are and your assignment, then: what you tried, the exact decision or blocker, and relevant paths or excerpts. Re-raise something it already answered only when you have new evidence to attach — the result of trying its advice, fresh output, or a changed constraint; otherwise act on its standing answer. After a CORRECTION or PLAN, your next consult on that topic opens with what happened when you followed it. Its replies open with one of ENDORSE, CORRECTION, PLAN, or STOP — treat CORRECTION and PLAN as actions to take. On STOP, or if the advisor is unreachable, report that back to whoever assigned you and leave lifecycle decisions to the session that owns the advisor.
|
|
104
104
|
|
|
105
|
-
###
|
|
105
|
+
### Third-party host (Claude CLI advisor; report to orchestrating session)
|
|
106
106
|
|
|
107
|
-
> The orchestrating session owns a standing **Claude** advisor bound through the CLI Claude-chain (max tier: Fable high, then Opus max). There is no Agent-tool `session-advisor` and no SendMessage path to one. Report blockers and hard decisions to the **orchestrating session** (the session that assigned you) before locking in a nontrivial approach, once you believe your assignment is done, before any hard-to-reverse action, when the same failure repeats or progress has stalled, and when the chosen approach is being reconsidered. Open each report with who you are and your assignment, then: what you tried, the exact decision or blocker, and relevant paths or excerpts. Re-raise something already answered only when you have new evidence to attach — the result of trying prior advice, fresh output, or a changed constraint; otherwise act on the standing answer. After a CORRECTION or PLAN, your next report on that topic opens with what happened when you followed it. The orchestrating session consults the Claude CLI advisor and relays one of ENDORSE, CORRECTION, PLAN, or STOP — treat CORRECTION and PLAN as actions to take. On STOP, or if the orchestrating session reports the advisor unreachable, stop work and surface that upward; do not spawn a `session-advisor` agent yourself, and do not treat the
|
|
107
|
+
> The orchestrating session owns a standing **Claude** advisor bound through the CLI Claude-chain (max tier: Fable high, then Opus max). There is no Agent-tool `session-advisor` and no SendMessage path to one. Report blockers and hard decisions to the **orchestrating session** (the session that assigned you) before locking in a nontrivial approach, once you believe your assignment is done, before any hard-to-reverse action, when the same failure repeats or progress has stalled, and when the chosen approach is being reconsidered. Open each report with who you are and your assignment, then: what you tried, the exact decision or blocker, and relevant paths or excerpts. Re-raise something already answered only when you have new evidence to attach — the result of trying prior advice, fresh output, or a changed constraint; otherwise act on the standing answer. After a CORRECTION or PLAN, your next report on that topic opens with what happened when you followed it. The orchestrating session consults the Claude CLI advisor and relays one of ENDORSE, CORRECTION, PLAN, or STOP — treat CORRECTION and PLAN as actions to take. On STOP, or if the orchestrating session reports the advisor unreachable, stop work and surface that upward; do not spawn a `session-advisor` agent yourself, and do not treat the third-party orchestrator's own judgment as an advisor signal.
|
|
108
108
|
|
|
109
109
|
## Lifecycle ownership
|
|
110
110
|
|
|
@@ -114,7 +114,7 @@ The session that spawns the shared advisor owns its whole lifecycle — spawn, d
|
|
|
114
114
|
|
|
115
115
|
**Re-spawn on drift.** If a reply shows the agent working from a stale picture, or the session pivots to an unrelated task, the owning session ends that agent and spawns a fresh one with a new charter, rather than forcing the old context to stretch across two different jobs.
|
|
116
116
|
|
|
117
|
-
###
|
|
117
|
+
### Third-party host
|
|
118
118
|
|
|
119
119
|
The orchestrating session owns the Claude CLI advisor bind for the whole run — first bind, re-bind on drift or lost `session_id`, and fail-closed report when the chain cannot serve.
|
|
120
120
|
|
|
@@ -124,7 +124,7 @@ The orchestrating session owns the Claude CLI advisor bind for the whole run —
|
|
|
124
124
|
|
|
125
125
|
The shared runner is `python "$HOME/.claude/scripts/claude_chain_runner.py" -- <claude args...>`. It walks `~/.claude/claude-chain.json` and fails over to the next binary only on a usage-limit signature, so a usage-limited primary account still gets served.
|
|
126
126
|
|
|
127
|
-
**
|
|
127
|
+
**Third-party host:** this runner is the **primary** advisor bind and consult path (see **Host profiles → Third-party host**). Map each walk attempt to `--model <alias>` and the effort flags there. When the walk exhausts, fail closed.
|
|
128
128
|
|
|
129
129
|
**Claude host:** fall back to this runner when any of these holds, rather than on judgment call:
|
|
130
130
|
- The Agent-tool spawn errors at every candidate tier down to the floor — the tool itself, not just the top tier, is unavailable.
|
|
@@ -139,7 +139,7 @@ Map `selected_tier` when one exists (the warm agent already bound above the floo
|
|
|
139
139
|
| Opus | `opus` |
|
|
140
140
|
| Sonnet | `sonnet` |
|
|
141
141
|
| Haiku | `haiku` |
|
|
142
|
-
|
|
|
142
|
+
| ThirdParty (third-party session model field only; not an advisor walk tier) | `third-party` |
|
|
143
143
|
|
|
144
144
|
Resolve in code with `python -c "from tier_model_ids import resolve_cli_model_id; print(resolve_cli_model_id('Opus'))"` from `$HOME/.claude/_shared/advisor/scripts/` (any letter case accepted; unknown tiers raise `ValueError`). Write the charter or the consult brief to a temporary file under the job's own temporary directory (or the OS temp directory when no job directory exists) and pipe it in, rather than passing either as an inline argument, and drop that file once the consult completes.
|
|
145
145
|
|
|
@@ -13,25 +13,28 @@ that mark a bind.
|
|
|
13
13
|
flag: any other result token counts as no bind
|
|
14
14
|
|
|
15
15
|
The alias map turns each tier into its short CLI / Agent name (``opus``,
|
|
16
|
-
``
|
|
16
|
+
``third-party``), never a dated full model ID.
|
|
17
17
|
|
|
18
18
|
Host-profile detection (see ``detect_host_profile``):
|
|
19
19
|
|
|
20
|
-
- ``ADVISOR_HOST_PROFILE=
|
|
21
|
-
- ``
|
|
20
|
+
- ``ADVISOR_HOST_PROFILE=ThirdParty`` or ``=Claude`` — explicit override
|
|
21
|
+
- ``THIRD_PARTY=1`` (or ``true`` / ``yes``) — a third-party (non-Claude) harness
|
|
22
22
|
- default when neither is set: Claude
|
|
23
23
|
"""
|
|
24
24
|
|
|
25
25
|
from __future__ import annotations
|
|
26
26
|
|
|
27
27
|
HOST_PROFILE_CLAUDE: str = "Claude"
|
|
28
|
-
|
|
29
|
-
ALL_HOST_PROFILES: tuple[str, ...] = (
|
|
28
|
+
HOST_PROFILE_THIRD_PARTY: str = "ThirdParty"
|
|
29
|
+
ALL_HOST_PROFILES: tuple[str, ...] = (
|
|
30
|
+
HOST_PROFILE_CLAUDE,
|
|
31
|
+
HOST_PROFILE_THIRD_PARTY,
|
|
32
|
+
)
|
|
30
33
|
|
|
31
34
|
ALL_MODEL_TIERS: tuple[str, ...] = ("Fable", "Opus", "Sonnet", "Haiku")
|
|
32
|
-
|
|
33
|
-
ALL_KNOWN_TIER_NAMES: tuple[str, ...] = (*ALL_MODEL_TIERS,
|
|
34
|
-
|
|
35
|
+
THIRD_PARTY_MODEL_TIER: str = "ThirdParty"
|
|
36
|
+
ALL_KNOWN_TIER_NAMES: tuple[str, ...] = (*ALL_MODEL_TIERS, THIRD_PARTY_MODEL_TIER)
|
|
37
|
+
THIRD_PARTY_CLI_ADVISOR_FLOOR_TIER: str = "Opus"
|
|
35
38
|
|
|
36
39
|
ADVISOR_SENDMESSAGE_REPLY_WAIT_SECONDS: int = 120
|
|
37
40
|
|
|
@@ -40,12 +43,12 @@ ALL_CLI_MODEL_ID_BY_TIER: dict[str, str] = {
|
|
|
40
43
|
"Opus": "opus",
|
|
41
44
|
"Sonnet": "sonnet",
|
|
42
45
|
"Haiku": "haiku",
|
|
43
|
-
|
|
46
|
+
THIRD_PARTY_MODEL_TIER: "third-party",
|
|
44
47
|
}
|
|
45
48
|
|
|
46
49
|
HOST_PROFILE_ENV_VAR: str = "ADVISOR_HOST_PROFILE"
|
|
47
|
-
|
|
48
|
-
|
|
50
|
+
THIRD_PARTY_ENV_VAR: str = "THIRD_PARTY"
|
|
51
|
+
ALL_THIRD_PARTY_TRUTHY_VALUES: frozenset[str] = frozenset(
|
|
49
52
|
{"1", "true", "yes", "on"}
|
|
50
53
|
)
|
|
51
54
|
|
|
@@ -67,7 +70,7 @@ ATTEMPT_ORDER_MISMATCH_MESSAGE: str = (
|
|
|
67
70
|
"spawn tries do not walk the candidate tiers in ladder order"
|
|
68
71
|
)
|
|
69
72
|
SELECTED_TIER_MISMATCH_MESSAGE: str = (
|
|
70
|
-
"selected_tier does not match the first successful bind (spawned or
|
|
73
|
+
"selected_tier does not match the first successful bind (spawned or cli)"
|
|
71
74
|
)
|
|
72
75
|
SELECTED_TIER_NOT_NULL_MESSAGE: str = (
|
|
73
76
|
"selected_tier must be null when no spawn try succeeded"
|
|
@@ -23,7 +23,7 @@ from data, not inferred from a transcript.
|
|
|
23
23
|
attempts=[{"tier": "Fable", "result": "cli"}],
|
|
24
24
|
selected_tier="Fable",
|
|
25
25
|
)
|
|
26
|
-
validate_model_tier_run(cli_bind) # ok:
|
|
26
|
+
validate_model_tier_run(cli_bind) # ok: third-party-host CLI Claude-chain bind
|
|
27
27
|
|
|
28
28
|
A run whose selected_tier is not the first successful bind fails.
|
|
29
29
|
On any broken invariant, validate_model_tier_run raises ModelTierRunError.
|
|
@@ -58,14 +58,14 @@ from advisor_scripts_constants.model_tier_run_validator_constants import ( # no
|
|
|
58
58
|
CLI_SUCCESS_EXIT_CODE,
|
|
59
59
|
CLI_USAGE_MESSAGE,
|
|
60
60
|
CLI_VALIDATION_FAILURE_EXIT_CODE,
|
|
61
|
-
GROK_CLI_ADVISOR_FLOOR_TIER,
|
|
62
|
-
GROK_MODEL_TIER,
|
|
63
61
|
INCOMPLETE_FALLBACK_WALK_MESSAGE,
|
|
64
62
|
MISSING_FALLBACK_REASON_MESSAGE,
|
|
65
63
|
SELECTED_TIER_MISMATCH_MESSAGE,
|
|
66
64
|
SELECTED_TIER_NOT_NULL_MESSAGE,
|
|
67
65
|
SPAWN_OUTCOME_KEY,
|
|
68
66
|
SPAWN_SUCCESS_TOKEN,
|
|
67
|
+
THIRD_PARTY_CLI_ADVISOR_FLOOR_TIER,
|
|
68
|
+
THIRD_PARTY_MODEL_TIER,
|
|
69
69
|
TIER_KEY,
|
|
70
70
|
UNKNOWN_OWN_TIER_MESSAGE,
|
|
71
71
|
)
|
|
@@ -99,8 +99,8 @@ def _expected_candidate_tiers(own_tier: str) -> list[str]:
|
|
|
99
99
|
maybe_canonical_own_tier = canonical_tier_name(own_tier)
|
|
100
100
|
if maybe_canonical_own_tier is None:
|
|
101
101
|
raise ModelTierRunError(f"{UNKNOWN_OWN_TIER_MESSAGE}: {own_tier!r}")
|
|
102
|
-
if maybe_canonical_own_tier ==
|
|
103
|
-
floor_index = ALL_MODEL_TIERS.index(
|
|
102
|
+
if maybe_canonical_own_tier == THIRD_PARTY_MODEL_TIER:
|
|
103
|
+
floor_index = ALL_MODEL_TIERS.index(THIRD_PARTY_CLI_ADVISOR_FLOOR_TIER)
|
|
104
104
|
return list(ALL_MODEL_TIERS[: floor_index + 1])
|
|
105
105
|
floor_index = ALL_MODEL_TIERS.index(maybe_canonical_own_tier)
|
|
106
106
|
return list(ALL_MODEL_TIERS[: floor_index + 1])
|
|
@@ -110,7 +110,7 @@ def _is_successful_attempt_outcome(
|
|
|
110
110
|
canonical_tier: str,
|
|
111
111
|
outcome_token: str,
|
|
112
112
|
) -> bool:
|
|
113
|
-
if canonical_tier ==
|
|
113
|
+
if canonical_tier == THIRD_PARTY_MODEL_TIER:
|
|
114
114
|
return False
|
|
115
115
|
if outcome_token == SPAWN_SUCCESS_TOKEN:
|
|
116
116
|
return True
|
|
@@ -128,11 +128,12 @@ def validate_model_tier_run(run: ModelTierRun) -> None:
|
|
|
128
128
|
validate_model_tier_run(cli_bind) # ok: CLI Claude-chain bind
|
|
129
129
|
validate_model_tier_run(broken_log) # flag: ModelTierRunError
|
|
130
130
|
|
|
131
|
-
Candidate tiers must match the floor slice. ``own_tier=
|
|
132
|
-
|
|
131
|
+
Candidate tiers must match the floor slice. ``own_tier=ThirdParty`` maps to
|
|
132
|
+
the third-party-host CLI advisor floor (Fable → Opus). Tries walk that
|
|
133
|
+
slice in order;
|
|
133
134
|
early stop only after ``spawned`` or ``cli``. A null selected_tier requires
|
|
134
|
-
a full walk plus fallback_reason (fail-closed on
|
|
135
|
-
cannot serve).
|
|
135
|
+
a full walk plus fallback_reason (fail-closed on a third-party host when
|
|
136
|
+
the chain cannot serve).
|
|
136
137
|
|
|
137
138
|
Args:
|
|
138
139
|
run: The structured spawn-walk log to check.
|
|
@@ -118,7 +118,13 @@ def test_selected_tier_not_first_spawned_attempt_raises() -> None:
|
|
|
118
118
|
],
|
|
119
119
|
selected_tier="Fable",
|
|
120
120
|
)
|
|
121
|
-
with pytest.raises(
|
|
121
|
+
with pytest.raises(
|
|
122
|
+
ModelTierRunError,
|
|
123
|
+
match=(
|
|
124
|
+
"selected_tier does not match the first successful bind "
|
|
125
|
+
r"\(spawned or cli\)"
|
|
126
|
+
),
|
|
127
|
+
):
|
|
122
128
|
validate_model_tier_run(run)
|
|
123
129
|
|
|
124
130
|
|
|
@@ -261,9 +267,9 @@ def test_cli_bind_fallthrough_to_opus_passes() -> None:
|
|
|
261
267
|
assert validate_model_tier_run(run) is None
|
|
262
268
|
|
|
263
269
|
|
|
264
|
-
def
|
|
270
|
+
def test_third_party_own_tier_maps_to_fable_opus_cli_bind_passes() -> None:
|
|
265
271
|
run = ModelTierRun(
|
|
266
|
-
own_tier="
|
|
272
|
+
own_tier="ThirdParty",
|
|
267
273
|
candidate_tiers=["Fable", "Opus"],
|
|
268
274
|
attempts=[{"tier": "Fable", "result": "cli"}],
|
|
269
275
|
selected_tier="Fable",
|
|
@@ -271,9 +277,9 @@ def test_grok_own_tier_maps_to_fable_opus_cli_bind_passes() -> None:
|
|
|
271
277
|
assert validate_model_tier_run(run) is None
|
|
272
278
|
|
|
273
279
|
|
|
274
|
-
def
|
|
280
|
+
def test_third_party_own_tier_lowercase_cli_bind_passes() -> None:
|
|
275
281
|
run = ModelTierRun(
|
|
276
|
-
own_tier="
|
|
282
|
+
own_tier="thirdparty",
|
|
277
283
|
candidate_tiers=["fable", "opus"],
|
|
278
284
|
attempts=[{"tier": "fable", "result": "cli"}],
|
|
279
285
|
selected_tier="fable",
|
|
@@ -292,9 +298,9 @@ def test_self_token_is_not_bind_success_raises() -> None:
|
|
|
292
298
|
validate_model_tier_run(run)
|
|
293
299
|
|
|
294
300
|
|
|
295
|
-
def
|
|
301
|
+
def test_third_party_self_token_is_not_bind_success_raises() -> None:
|
|
296
302
|
run = ModelTierRun(
|
|
297
|
-
own_tier="
|
|
303
|
+
own_tier="ThirdParty",
|
|
298
304
|
candidate_tiers=["Fable", "Opus"],
|
|
299
305
|
attempts=[{"tier": "Fable", "result": "self"}],
|
|
300
306
|
selected_tier="Fable",
|
|
@@ -303,20 +309,20 @@ def test_grok_self_token_is_not_bind_success_raises() -> None:
|
|
|
303
309
|
validate_model_tier_run(run)
|
|
304
310
|
|
|
305
311
|
|
|
306
|
-
def
|
|
312
|
+
def test_third_party_host_legacy_single_tier_self_bind_raises() -> None:
|
|
307
313
|
run = ModelTierRun(
|
|
308
|
-
own_tier="
|
|
309
|
-
candidate_tiers=["
|
|
310
|
-
attempts=[{"tier": "
|
|
311
|
-
selected_tier="
|
|
314
|
+
own_tier="ThirdParty",
|
|
315
|
+
candidate_tiers=["ThirdParty"],
|
|
316
|
+
attempts=[{"tier": "ThirdParty", "result": "self"}],
|
|
317
|
+
selected_tier="ThirdParty",
|
|
312
318
|
)
|
|
313
319
|
with pytest.raises(ModelTierRunError):
|
|
314
320
|
validate_model_tier_run(run)
|
|
315
321
|
|
|
316
322
|
|
|
317
|
-
def
|
|
323
|
+
def test_third_party_cli_exhausted_fail_closed_passes() -> None:
|
|
318
324
|
run = ModelTierRun(
|
|
319
|
-
own_tier="
|
|
325
|
+
own_tier="ThirdParty",
|
|
320
326
|
candidate_tiers=["Fable", "Opus"],
|
|
321
327
|
attempts=[
|
|
322
328
|
{"tier": "Fable", "result": "unavailable"},
|
|
@@ -324,15 +330,15 @@ def test_grok_cli_exhausted_fail_closed_passes() -> None:
|
|
|
324
330
|
],
|
|
325
331
|
selected_tier=None,
|
|
326
332
|
fallback_reason=(
|
|
327
|
-
"
|
|
333
|
+
"third-party host CLI Claude-chain exhausted; fail closed"
|
|
328
334
|
),
|
|
329
335
|
)
|
|
330
336
|
assert validate_model_tier_run(run) is None
|
|
331
337
|
|
|
332
338
|
|
|
333
|
-
def
|
|
339
|
+
def test_third_party_cli_exhausted_without_fallback_reason_raises() -> None:
|
|
334
340
|
run = ModelTierRun(
|
|
335
|
-
own_tier="
|
|
341
|
+
own_tier="ThirdParty",
|
|
336
342
|
candidate_tiers=["Fable", "Opus"],
|
|
337
343
|
attempts=[
|
|
338
344
|
{"tier": "Fable", "result": "unavailable"},
|
|
@@ -344,9 +350,9 @@ def test_grok_cli_exhausted_without_fallback_reason_raises() -> None:
|
|
|
344
350
|
validate_model_tier_run(run)
|
|
345
351
|
|
|
346
352
|
|
|
347
|
-
def
|
|
353
|
+
def test_third_party_cli_selected_tier_mismatch_raises() -> None:
|
|
348
354
|
run = ModelTierRun(
|
|
349
|
-
own_tier="
|
|
355
|
+
own_tier="ThirdParty",
|
|
350
356
|
candidate_tiers=["Fable", "Opus"],
|
|
351
357
|
attempts=[{"tier": "Fable", "result": "cli"}],
|
|
352
358
|
selected_tier="Opus",
|
|
@@ -37,9 +37,9 @@ from advisor_scripts_constants.model_tier_run_validator_constants import ( # no
|
|
|
37
37
|
ALL_CLI_MODEL_ID_BY_TIER,
|
|
38
38
|
ALL_KNOWN_TIER_NAMES,
|
|
39
39
|
ALL_MODEL_TIERS,
|
|
40
|
-
GROK_MODEL_TIER,
|
|
41
40
|
HOST_PROFILE_CLAUDE,
|
|
42
|
-
|
|
41
|
+
HOST_PROFILE_THIRD_PARTY,
|
|
42
|
+
THIRD_PARTY_MODEL_TIER,
|
|
43
43
|
)
|
|
44
44
|
|
|
45
45
|
SCRIPTS_ROOT = Path(__file__).parent.parent
|
|
@@ -56,13 +56,13 @@ DOCUMENTED_RESOLVE_ONE_LINER = (
|
|
|
56
56
|
("Opus", "opus"),
|
|
57
57
|
("Sonnet", "sonnet"),
|
|
58
58
|
("Haiku", "haiku"),
|
|
59
|
-
("
|
|
59
|
+
("ThirdParty", "third-party"),
|
|
60
60
|
("fable", "fable"),
|
|
61
61
|
("OPUS", "opus"),
|
|
62
62
|
("sonnet", "sonnet"),
|
|
63
63
|
("hAiKu", "haiku"),
|
|
64
|
-
("
|
|
65
|
-
("
|
|
64
|
+
("thirdparty", "third-party"),
|
|
65
|
+
(" THIRDPARTY ", "third-party"),
|
|
66
66
|
(" Opus ", "opus"),
|
|
67
67
|
("\thaiku\n", "haiku"),
|
|
68
68
|
],
|
|
@@ -97,8 +97,8 @@ def test_sendmessage_reply_wait_is_positive_bound() -> None:
|
|
|
97
97
|
def test_cli_model_alias_map_keys_match_known_tiers() -> None:
|
|
98
98
|
assert set(ALL_CLI_MODEL_ID_BY_TIER) == set(ALL_KNOWN_TIER_NAMES)
|
|
99
99
|
assert set(ALL_MODEL_TIERS).issubset(set(ALL_KNOWN_TIER_NAMES))
|
|
100
|
-
assert
|
|
101
|
-
assert
|
|
100
|
+
assert THIRD_PARTY_MODEL_TIER in ALL_KNOWN_TIER_NAMES
|
|
101
|
+
assert THIRD_PARTY_MODEL_TIER not in ALL_MODEL_TIERS
|
|
102
102
|
assert all(
|
|
103
103
|
ALL_CLI_MODEL_ID_BY_TIER[each_tier] for each_tier in ALL_KNOWN_TIER_NAMES
|
|
104
104
|
)
|
|
@@ -106,7 +106,7 @@ def test_cli_model_alias_map_keys_match_known_tiers() -> None:
|
|
|
106
106
|
|
|
107
107
|
def test_canonical_tier_name_strips_and_normalizes() -> None:
|
|
108
108
|
assert canonical_tier_name(" opus ") == "Opus"
|
|
109
|
-
assert canonical_tier_name("
|
|
109
|
+
assert canonical_tier_name("thirdparty") == "ThirdParty"
|
|
110
110
|
assert canonical_tier_name("") is None
|
|
111
111
|
assert canonical_tier_name("Titan") is None
|
|
112
112
|
|
|
@@ -119,30 +119,30 @@ def test_detect_host_profile_defaults_to_claude() -> None:
|
|
|
119
119
|
"truthy_value",
|
|
120
120
|
["1", "true", "TRUE", "yes", "YES", "on", "On"],
|
|
121
121
|
)
|
|
122
|
-
def
|
|
122
|
+
def test_detect_host_profile_reads_third_party_truthy_values(
|
|
123
123
|
truthy_value: str,
|
|
124
124
|
) -> None:
|
|
125
125
|
assert (
|
|
126
|
-
detect_host_profile(setting_by_name={"
|
|
127
|
-
==
|
|
126
|
+
detect_host_profile(setting_by_name={"THIRD_PARTY": truthy_value})
|
|
127
|
+
== HOST_PROFILE_THIRD_PARTY
|
|
128
128
|
)
|
|
129
129
|
|
|
130
130
|
|
|
131
|
-
def
|
|
131
|
+
def test_detect_host_profile_reads_third_party_flag() -> None:
|
|
132
132
|
assert (
|
|
133
|
-
detect_host_profile(setting_by_name={"
|
|
133
|
+
detect_host_profile(setting_by_name={"THIRD_PARTY": "0"})
|
|
134
134
|
== HOST_PROFILE_CLAUDE
|
|
135
135
|
)
|
|
136
136
|
assert (
|
|
137
|
-
detect_host_profile(setting_by_name={"
|
|
137
|
+
detect_host_profile(setting_by_name={"THIRD_PARTY": "false"})
|
|
138
138
|
== HOST_PROFILE_CLAUDE
|
|
139
139
|
)
|
|
140
140
|
|
|
141
141
|
|
|
142
142
|
def test_detect_host_profile_reads_explicit_override() -> None:
|
|
143
143
|
assert (
|
|
144
|
-
detect_host_profile(setting_by_name={"ADVISOR_HOST_PROFILE": "
|
|
145
|
-
==
|
|
144
|
+
detect_host_profile(setting_by_name={"ADVISOR_HOST_PROFILE": "ThirdParty"})
|
|
145
|
+
== HOST_PROFILE_THIRD_PARTY
|
|
146
146
|
)
|
|
147
147
|
assert (
|
|
148
148
|
detect_host_profile(setting_by_name={"ADVISOR_HOST_PROFILE": "claude"})
|
|
@@ -150,7 +150,7 @@ def test_detect_host_profile_reads_explicit_override() -> None:
|
|
|
150
150
|
)
|
|
151
151
|
assert (
|
|
152
152
|
detect_host_profile(
|
|
153
|
-
setting_by_name={"ADVISOR_HOST_PROFILE": "Claude", "
|
|
153
|
+
setting_by_name={"ADVISOR_HOST_PROFILE": "Claude", "THIRD_PARTY": "1"}
|
|
154
154
|
)
|
|
155
155
|
== HOST_PROFILE_CLAUDE
|
|
156
156
|
)
|
|
@@ -4,16 +4,16 @@
|
|
|
4
4
|
|
|
5
5
|
resolve_cli_model_id("Opus") # ok: "opus"
|
|
6
6
|
resolve_cli_model_id("fable") # ok: "fable" (any letter case)
|
|
7
|
-
resolve_cli_model_id("
|
|
7
|
+
resolve_cli_model_id("ThirdParty") # ok: "third-party" (third-party host tier)
|
|
8
8
|
resolve_cli_model_id(" Opus ") # ok: "opus" (leading/trailing whitespace)
|
|
9
9
|
resolve_cli_model_id("Titan") # flag: ValueError
|
|
10
10
|
|
|
11
|
-
detect_host_profile(setting_by_name={"
|
|
11
|
+
detect_host_profile(setting_by_name={"THIRD_PARTY": "1"}) # ok: "ThirdParty"
|
|
12
12
|
detect_host_profile(
|
|
13
13
|
setting_by_name={"ADVISOR_HOST_PROFILE": "Claude"}
|
|
14
14
|
) # ok: "Claude"
|
|
15
15
|
|
|
16
|
-
Values are stable short aliases (``opus``, ``sonnet``, ``
|
|
16
|
+
Values are stable short aliases (``opus``, ``sonnet``, ``third-party``), not dated
|
|
17
17
|
full model IDs such as ``claude-opus-4-…``. The map lives in
|
|
18
18
|
``advisor_scripts_constants`` so protocol text, skills, and tests share one
|
|
19
19
|
source of truth for the aliases the Agent tool ``model:`` field and CLI
|
|
@@ -35,13 +35,13 @@ if _config_directory not in sys.path:
|
|
|
35
35
|
|
|
36
36
|
from advisor_scripts_constants.model_tier_run_validator_constants import ( # noqa: E402
|
|
37
37
|
ALL_CLI_MODEL_ID_BY_TIER,
|
|
38
|
-
ALL_GROK_BUILD_TRUTHY_VALUES,
|
|
39
38
|
ALL_HOST_PROFILES,
|
|
40
39
|
ALL_KNOWN_TIER_NAMES,
|
|
41
|
-
|
|
40
|
+
ALL_THIRD_PARTY_TRUTHY_VALUES,
|
|
42
41
|
HOST_PROFILE_CLAUDE,
|
|
43
42
|
HOST_PROFILE_ENV_VAR,
|
|
44
|
-
|
|
43
|
+
HOST_PROFILE_THIRD_PARTY,
|
|
44
|
+
THIRD_PARTY_ENV_VAR,
|
|
45
45
|
UNKNOWN_HOST_PROFILE_ERROR,
|
|
46
46
|
UNKNOWN_LADDER_NAME_ERROR,
|
|
47
47
|
)
|
|
@@ -53,13 +53,13 @@ def canonical_tier_name(tier_name: str) -> str | None:
|
|
|
53
53
|
::
|
|
54
54
|
|
|
55
55
|
canonical_tier_name("opus") # ok: "Opus"
|
|
56
|
-
canonical_tier_name("
|
|
56
|
+
canonical_tier_name("thirdparty") # ok: "ThirdParty"
|
|
57
57
|
canonical_tier_name(" Opus ") # ok: "Opus"
|
|
58
58
|
canonical_tier_name("") # ok: None
|
|
59
59
|
canonical_tier_name("Titan") # ok: None
|
|
60
60
|
|
|
61
61
|
Strips leading and trailing whitespace, then matches any letter case
|
|
62
|
-
against ``ALL_KNOWN_TIER_NAMES`` (Claude ladder plus the
|
|
62
|
+
against ``ALL_KNOWN_TIER_NAMES`` (Claude ladder plus the third-party host tier).
|
|
63
63
|
|
|
64
64
|
Args:
|
|
65
65
|
tier_name: Raw tier text from a spawn log, protocol walk, or caller.
|
|
@@ -83,7 +83,7 @@ def resolve_cli_model_id(tier: str) -> str:
|
|
|
83
83
|
|
|
84
84
|
resolve_cli_model_id("Sonnet") # ok: "sonnet"
|
|
85
85
|
resolve_cli_model_id("HAIKU") # ok: "haiku"
|
|
86
|
-
resolve_cli_model_id("
|
|
86
|
+
resolve_cli_model_id("ThirdParty") # ok: "third-party"
|
|
87
87
|
resolve_cli_model_id(" Opus ") # ok: "opus"
|
|
88
88
|
resolve_cli_model_id("Titan") # flag: ValueError
|
|
89
89
|
|
|
@@ -93,7 +93,7 @@ def resolve_cli_model_id(tier: str) -> str:
|
|
|
93
93
|
|
|
94
94
|
Args:
|
|
95
95
|
tier: Ladder tier name (``Fable``, ``Opus``, ``Sonnet``, ``Haiku``,
|
|
96
|
-
or ``
|
|
96
|
+
or ``ThirdParty``).
|
|
97
97
|
|
|
98
98
|
Returns:
|
|
99
99
|
The short model alias for that tier (for example ``"opus"``).
|
|
@@ -118,9 +118,9 @@ def detect_host_profile(
|
|
|
118
118
|
|
|
119
119
|
::
|
|
120
120
|
|
|
121
|
-
detect_host_profile(setting_by_name={"ADVISOR_HOST_PROFILE": "
|
|
122
|
-
# ok: "
|
|
123
|
-
detect_host_profile(setting_by_name={"
|
|
121
|
+
detect_host_profile(setting_by_name={"ADVISOR_HOST_PROFILE": "ThirdParty"})
|
|
122
|
+
# ok: "ThirdParty"
|
|
123
|
+
detect_host_profile(setting_by_name={"THIRD_PARTY": "1"}) # ok: "ThirdParty"
|
|
124
124
|
detect_host_profile(setting_by_name={}) # ok: "Claude"
|
|
125
125
|
detect_host_profile(setting_by_name={"ADVISOR_HOST_PROFILE": "X"})
|
|
126
126
|
# flag: ValueError
|
|
@@ -128,14 +128,14 @@ def detect_host_profile(
|
|
|
128
128
|
Order:
|
|
129
129
|
|
|
130
130
|
1. ``ADVISOR_HOST_PROFILE`` when set (must be a known profile name).
|
|
131
|
-
2. ``
|
|
131
|
+
2. ``THIRD_PARTY`` when truthy (``1``, ``true``, ``yes``, ``on``).
|
|
132
132
|
3. Default ``Claude``.
|
|
133
133
|
|
|
134
134
|
Args:
|
|
135
135
|
setting_by_name: Env name → setting text (defaults to ``os.environ``).
|
|
136
136
|
|
|
137
137
|
Returns:
|
|
138
|
-
``
|
|
138
|
+
``HOST_PROFILE_THIRD_PARTY`` or ``HOST_PROFILE_CLAUDE``.
|
|
139
139
|
|
|
140
140
|
Raises:
|
|
141
141
|
ValueError: When ``ADVISOR_HOST_PROFILE`` is set to an unknown name.
|
|
@@ -157,8 +157,8 @@ def detect_host_profile(
|
|
|
157
157
|
raise ValueError(UNKNOWN_HOST_PROFILE_ERROR.format(explicit_host_profile))
|
|
158
158
|
return maybe_canonical_host
|
|
159
159
|
raw_harness_marker = resolved_setting_by_name.get(
|
|
160
|
-
|
|
160
|
+
THIRD_PARTY_ENV_VAR, ""
|
|
161
161
|
).strip().lower()
|
|
162
|
-
if raw_harness_marker in
|
|
163
|
-
return
|
|
162
|
+
if raw_harness_marker in ALL_THIRD_PARTY_TRUTHY_VALUES:
|
|
163
|
+
return HOST_PROFILE_THIRD_PARTY
|
|
164
164
|
return HOST_PROFILE_CLAUDE
|
|
@@ -14,6 +14,7 @@ Runtime documents and scripts shared by every PR-loop skill. Changes here affect
|
|
|
14
14
|
| `state-schema.md` | Fields each PR-loop workflow tracks across iterations; documents common fields and per-skill extensions |
|
|
15
15
|
| `code-rules-gate.md` | Reference for the CODE_RULES pre-commit gate check; describes what the gate blocks and when it runs |
|
|
16
16
|
| `precatch-rubric.md` | Shared pre-catch lane checklist — deterministic sweep, doc-vs-code parity, test-assertion completeness, PR-description parity, adversarial audit — that autoconverge's lenses and pr-converge's CODE_REVIEW step read on demand |
|
|
17
|
+
| `worker-spawn.md` | Worker-spawn tier protocol and Claude-only slash-step host routing: three tiers, CLI/API inputs, JSON result shape, host detection, code-review modes via `detect_host_profile` + `invoke_code_review`, leader-socket rule, cloud setup; documents `scripts/resolve_worker_spawn.py` |
|
|
17
18
|
|
|
18
19
|
## Subdirectory
|
|
19
20
|
|
|
@@ -13,7 +13,7 @@ Python scripts invoked at runtime by the PR-loop skills. Each script is a standa
|
|
|
13
13
|
| `revoke_project_claude_permissions.py` | Removes the allow-rules and entries that `grant_project_claude_permissions.py` wrote; safe to run when no prior grant exists |
|
|
14
14
|
| `code_rules_gate.py` | Pre-commit gate that runs `code_rules_enforcer` checks on staged Python files before a fix commit lands, and the terminology sweep over the staged diff |
|
|
15
15
|
| `terminology_sweep.py` | Flags a prose term that near-misses an identifier introduced on added code lines of a unified diff (shared leading word, divergent tail) |
|
|
16
|
-
| `reviews_disabled.py` | Shared helper for the reviewer opt-out and opt-in gates; parses `CLAUDE_REVIEWS_DISABLED`
|
|
16
|
+
| `reviews_disabled.py` | Shared helper for the reviewer opt-out and opt-in gates; parses `CLAUDE_REVIEWS_DISABLED` tokens `bugteam`, `bugbot`, `copilot`, and `codex`, plus `CLAUDE_REVIEWS_ENABLED` for bugbot opt-in (bugbot is off by default and runs only when `CLAUDE_REVIEWS_ENABLED` lists it) |
|
|
17
17
|
| `copilot_quota.py` | Copilot premium-request quota pre-check: resolves a configured GitHub account, reads its remaining `premium_interactions` quota via `gh api copilot_internal/user`, and exits 0 (run Copilot) or non-zero (skip: out of quota, API down, or no account configured) |
|
|
18
18
|
| `reviewer_availability.py` | Unified reviewer-availability entry point for Copilot and Bugbot: reuses `copilot_quota.py` and `reviews_disabled.py` and exits 0 when the named `--reviewer` may be spawned, non-zero when it is opted out or (for Copilot) out of quota |
|
|
19
19
|
| `fix_hookspath.py` | Repairs a malformed `core.hooksPath` global git config entry |
|
|
@@ -23,6 +23,7 @@ Python scripts invoked at runtime by the PR-loop skills. Each script is a standa
|
|
|
23
23
|
|
|
24
24
|
| Entry | Description |
|
|
25
25
|
|---|---|
|
|
26
|
+
| `code_rules_gate_parts/` | The decomposed modules `code_rules_gate.py` wires together: enforcer loading, git file sets, blob readers, added-line maps, violation scoping, wrapper plumb-through, gate running, staged-test running, and argument parsing |
|
|
26
27
|
| `pr_loop_shared_constants/` | Named constants used by the scripts above |
|
|
27
28
|
| `tests/` | pytest suite for all scripts in this directory |
|
|
28
29
|
|
|
@@ -8,6 +8,7 @@ Runnable helpers used by **bugteam**, **qbug**, **pr-converge**, and related ski
|
|
|
8
8
|
| --- | --- |
|
|
9
9
|
| `preflight.py` | Local checks before a PR-loop run (pytest discovery, optional pre-commit, hooksPath sanity). |
|
|
10
10
|
| `reviewer_availability.py` | Unified availability check for Copilot and Bugbot; reuses `copilot_quota.py` and `reviews_disabled.py` and exits 0 when the named `--reviewer` may be spawned. |
|
|
11
|
+
| `reviews_disabled.py` | Opt-out and opt-in gate for reviewer tokens `bugteam`, `bugbot`, `copilot`, and `codex` via `CLAUDE_REVIEWS_DISABLED` / `CLAUDE_REVIEWS_ENABLED`. |
|
|
11
12
|
| `code_rules_gate.py` | CODE_RULES gate over PR-scoped diffs (`--base`, staged-only, path filters). |
|
|
12
13
|
| `fix_hookspath.py` | Repair `core.hooksPath` when it does not point at the packaged git-hooks tree. Single home for unexpected non-empty git stderr (hard-fail `RuntimeError`); skill wrappers may call it later. |
|
|
13
14
|
| `grant_project_claude_permissions.py` / `revoke_project_claude_permissions.py` | Claude Code permission JSON helpers used during publish-style flows. |
|