claude-dev-env 2.4.0 → 2.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (195) hide show
  1. package/CLAUDE.md +53 -49
  2. package/_shared/pr-loop/scripts/_claude_permissions_common.py +84 -0
  3. package/_shared/pr-loop/scripts/code_rules_gate.py +4 -2
  4. package/_shared/pr-loop/scripts/grant_project_claude_permissions.py +306 -306
  5. package/_shared/pr-loop/scripts/pr_loop_shared_constants/claude_permissions_constants.py +44 -0
  6. package/_shared/pr-loop/scripts/pr_loop_shared_constants/copilot_quota_constants.py +24 -24
  7. package/_shared/pr-loop/scripts/pr_loop_shared_constants/stale_worktree_rule_sweep_constants.py +107 -107
  8. package/_shared/pr-loop/scripts/revoke_project_claude_permissions.py +290 -48
  9. package/_shared/pr-loop/scripts/tests/test_claude_permissions_common.py +42 -2
  10. package/_shared/pr-loop/scripts/tests/test_claude_permissions_constants.py +36 -0
  11. package/_shared/pr-loop/scripts/tests/test_code_rules_gate.py +100 -1
  12. package/_shared/pr-loop/scripts/tests/test_fix_hookspath.py +497 -497
  13. package/_shared/pr-loop/scripts/tests/test_revoke_project_claude_permissions.py +311 -2
  14. package/_shared/pr-loop/scripts/tests/test_stale_worktree_rule_sweep.py +301 -301
  15. package/_shared/pr-loop/scripts/tests/test_stale_worktree_rule_sweep_constants.py +85 -85
  16. package/_shared/pr-loop/worker-spawn.md +1 -1
  17. package/agents/CLAUDE.md +2 -1
  18. package/agents/caveman.md +0 -1
  19. package/agents/clasp-deployment-orchestrator.md +0 -1
  20. package/agents/clean-coder.md +0 -1
  21. package/agents/code-advisor.md +0 -1
  22. package/agents/code-quality-agent.md +1 -2
  23. package/agents/code-verifier.md +0 -1
  24. package/agents/deep-research.md +0 -1
  25. package/agents/docs-agent.md +0 -1
  26. package/agents/git-commit-crafter.md +0 -1
  27. package/agents/issue-tracker.md +42 -0
  28. package/agents/plan-packet-validator.md +0 -1
  29. package/agents/pr-description-writer.md +0 -1
  30. package/agents/test_agent_frontmatter.py +67 -18
  31. package/audit-rubrics/category_rubrics/category-o-docstring-vs-impl-drift.md +143 -141
  32. package/bin/CLAUDE.md +68 -5
  33. package/bin/ever-shipped-skills.mjs +1 -0
  34. package/bin/install-constants.mjs +88 -0
  35. package/bin/install.mjs +1138 -114
  36. package/bin/install.prune.test.mjs +869 -19
  37. package/bin/install.test.mjs +906 -2
  38. package/commands/implement.md +1 -1
  39. package/commands/right-size.md +1 -1
  40. package/docs/CLAUDE.md +1 -0
  41. package/docs/host-pool-health-monitor.md +102 -0
  42. package/docs/references/CLAUDE.md +4 -2
  43. package/docs/references/advisor-tool.md +13 -0
  44. package/docs/references/code-review-enforcement.md +10 -0
  45. package/docs/references/team-advisor-skill.md +14 -0
  46. package/hooks/blocking/CLAUDE.md +1 -0
  47. package/hooks/blocking/code_review_pr_create_gate.py +7 -3
  48. package/hooks/blocking/code_review_push_gate.py +9 -4
  49. package/hooks/blocking/code_review_stamp_directory_write_blocker.py +8 -0
  50. package/hooks/blocking/config/__init__.py +5 -5
  51. package/hooks/blocking/config/code_review_enforcement_constants.py +4 -1
  52. package/hooks/blocking/config/test_code_review_enforcement_constants.py +5 -0
  53. package/hooks/blocking/config/verified_commit_constants.py +160 -159
  54. package/hooks/blocking/orchestrator_refresh_reschedule_gate.py +256 -0
  55. package/hooks/blocking/pre_tool_use_dispatcher.py +24 -24
  56. package/hooks/blocking/test_code_review_pr_create_gate.py +14 -0
  57. package/hooks/blocking/test_code_review_push_gate.py +16 -0
  58. package/hooks/blocking/test_code_review_stamp_directory_write_blocker.py +19 -0
  59. package/hooks/blocking/test_orchestrator_refresh_reschedule_gate.py +231 -0
  60. package/hooks/blocking/test_pre_tool_use_dispatcher.py +10 -1
  61. package/hooks/blocking/test_verdict_directory_write_blocker.py +808 -808
  62. package/hooks/blocking/test_verification_verdict_store.py +54 -0
  63. package/hooks/blocking/test_verified_commit_gate.py +581 -581
  64. package/hooks/blocking/test_verified_commit_message_accuracy_blocker.py +131 -131
  65. package/hooks/blocking/verdict_directory_write_blocker.py +687 -687
  66. package/hooks/blocking/verification_verdict_store.py +1039 -1036
  67. package/hooks/blocking/verified_commit_message_accuracy_blocker.py +167 -167
  68. package/hooks/blocking/verifier_verdict_minter.py +280 -280
  69. package/hooks/git-hooks/test_pre_push.py +25 -0
  70. package/hooks/hooks.json +10 -0
  71. package/hooks/hooks_constants/CLAUDE.md +2 -1
  72. package/hooks/hooks_constants/enter_worktree_prefetch_constants.py +18 -18
  73. package/hooks/hooks_constants/orchestrator_refresh_reschedule_gate_constants.py +48 -0
  74. package/hooks/hooks_constants/ruff_integration_constants.py +16 -0
  75. package/hooks/lifecycle/enter_worktree_origin_prefetch.py +163 -146
  76. package/hooks/lifecycle/test_enter_worktree_origin_prefetch.py +185 -178
  77. package/hooks/pyproject.toml +1 -0
  78. package/hooks/validators/CLAUDE.md +1 -0
  79. package/hooks/validators/config/__init__.py +0 -0
  80. package/hooks/validators/config/directory_exemption_constants.py +183 -0
  81. package/hooks/validators/config/test_directory_exemption_constants.py +21 -0
  82. package/hooks/validators/conftest.py +4 -0
  83. package/hooks/validators/ruff_integration.py +49 -5
  84. package/hooks/validators/run_all_validators.py +206 -9
  85. package/hooks/validators/test_directory_exemption_constants.py +185 -0
  86. package/hooks/validators/test_python_antipattern_checks.py +110 -5
  87. package/hooks/validators/test_ruff_integration.py +92 -1
  88. package/hooks/validators/test_run_all_validators.py +115 -68
  89. package/hooks/validators/test_run_all_validators_pretooluse.py +159 -1
  90. package/package.json +10 -2
  91. package/rules/CLAUDE.md +1 -0
  92. package/rules/docstring-prose-matches-implementation.md +45 -44
  93. package/rules/state-what-is.md +25 -0
  94. package/rules/verified-commit-gate-skip.md +1 -1
  95. package/scripts/CLAUDE.md +1 -0
  96. package/scripts/Capture-PoolHealth.ps1 +410 -0
  97. package/scripts/_code_review_test_support.py +404 -0
  98. package/scripts/claude_chain_runner.py +141 -1
  99. package/scripts/conftest.py +16 -1
  100. package/scripts/dev_env_scripts_constants/CLAUDE.md +1 -1
  101. package/scripts/dev_env_scripts_constants/claude_chain_constants.py +9 -0
  102. package/scripts/resolve_worker_spawn.py +626 -626
  103. package/scripts/spawn_grok_batch.py +672 -672
  104. package/scripts/test_claude_chain_runner.py +131 -0
  105. package/scripts/test_invoke_code_review_chain.py +70 -0
  106. package/scripts/test_invoke_code_review_cli.py +192 -0
  107. package/scripts/test_invoke_code_review_contract.py +256 -0
  108. package/scripts/test_invoke_code_review_git.py +123 -0
  109. package/scripts/test_invoke_code_review_mode.py +99 -0
  110. package/scripts/test_resolve_worker_spawn.py +1014 -1014
  111. package/skills/CLAUDE.md +2 -0
  112. package/skills/auditing-claude-config/SKILL.md +114 -114
  113. package/skills/autoconverge/SKILL.md +427 -427
  114. package/skills/autoconverge/reference/convergence.md +24 -3
  115. package/skills/autoconverge/workflow/CLAUDE.md +1 -0
  116. package/skills/autoconverge/workflow/converge.clean-audit.test.mjs +3 -3
  117. package/skills/autoconverge/workflow/converge.contract.test.mjs +1263 -1263
  118. package/skills/autoconverge/workflow/converge.mjs +167 -0
  119. package/skills/autoconverge/workflow/converge.p2-advance.test.mjs +202 -0
  120. package/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-a11d903476b803493.jsonl +2 -2
  121. package/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-a26213978adeef6fb.jsonl +2 -2
  122. package/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-a3def0d15ed9d9110.jsonl +2 -2
  123. package/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-a41f41b1b708ee3b7.jsonl +2 -2
  124. package/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-a758b880abecc3ff7.jsonl +2 -2
  125. package/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-a8897b89656b1bd16.jsonl +2 -2
  126. package/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-abd463d744a1437bc.jsonl +2 -2
  127. package/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-ad19d027ae8ee1816.jsonl +2 -2
  128. package/skills/autoconverge/workflow/fixtures/wf_run/workflows/wf_881252e6-700.json +265 -265
  129. package/skills/closeout/SKILL.md +33 -50
  130. package/skills/codex-review/scripts/codex_review_scripts_constants/run_constants.py +8 -0
  131. package/skills/codex-review/scripts/run_codex_review.py +233 -1
  132. package/skills/codex-review/scripts/test_run_codex_review.py +189 -0
  133. package/skills/condensing-instructions/SKILL.md +81 -0
  134. package/skills/copilot-review/SKILL.md +119 -119
  135. package/skills/e-code-review/SKILL.md +52 -0
  136. package/skills/e-code-review/reference/fix.md +54 -0
  137. package/skills/e-code-review/reference/loop.md +43 -0
  138. package/skills/e-code-review/reference/low.md +57 -0
  139. package/skills/e-code-review/reference/medium.md +153 -0
  140. package/skills/e-code-review/reference/xhigh.md +182 -0
  141. package/skills/e-simplify/SKILL.md +97 -0
  142. package/skills/issue-tracker/SKILL.md +92 -0
  143. package/skills/issue-tracker/reference/epic-and-sub-issue-model.md +55 -0
  144. package/skills/issue-tracker/reference/handoff-schema.md +64 -0
  145. package/skills/issue-tracker/reference/operation-matrix.md +41 -0
  146. package/skills/orchestrator/SKILL.md +162 -21
  147. package/skills/orchestrator/scripts/status_gate.py +625 -0
  148. package/skills/orchestrator/scripts/status_gate_constants/__init__.py +1 -0
  149. package/skills/orchestrator/scripts/status_gate_constants/config/__init__.py +1 -0
  150. package/skills/orchestrator/scripts/status_gate_constants/config/constants.py +47 -0
  151. package/skills/orchestrator/scripts/test_status_gate.py +439 -0
  152. package/skills/orchestrator-refresh/SKILL.md +110 -35
  153. package/skills/plan-to-pr/SKILL.md +155 -0
  154. package/skills/plan-to-pr/reference/final-validation-tasks.md +15 -0
  155. package/skills/plan-to-pr/reference/model-routing.md +36 -0
  156. package/skills/plan-to-pr/reference/packet-contract.md +43 -0
  157. package/skills/plan-to-pr/reference/packet-schema.json +57 -0
  158. package/skills/plan-to-pr/reference/process-inventory.md +22 -0
  159. package/skills/plan-to-pr/reference/review-loop.md +33 -0
  160. package/skills/plan-to-pr/reference/run-record.schema.json +27 -0
  161. package/skills/plan-to-pr/reference/self-audit-tasks.md +15 -0
  162. package/skills/plan-to-pr/reference/task-seeds.md +14 -0
  163. package/skills/plan-to-pr/reference/task-ticket.md +38 -0
  164. package/skills/plan-to-pr/scripts/config/__init__.py +1 -0
  165. package/skills/plan-to-pr/scripts/config/constants.py +193 -0
  166. package/skills/plan-to-pr/scripts/create_packet.py +173 -0
  167. package/skills/plan-to-pr/scripts/test_create_packet.py +102 -0
  168. package/skills/plan-to-pr/scripts/test_validate_packet.py +256 -0
  169. package/skills/plan-to-pr/scripts/test_validate_protocol.py +135 -0
  170. package/skills/plan-to-pr/scripts/test_validate_run.py +158 -0
  171. package/skills/plan-to-pr/scripts/validate_packet.py +655 -0
  172. package/skills/plan-to-pr/scripts/validate_protocol.py +622 -0
  173. package/skills/plan-to-pr/scripts/validate_run.py +173 -0
  174. package/skills/plan-to-pr/test_skill_contract.py +207 -0
  175. package/skills/plan-to-pr/test_task_ticket_contract.py +151 -0
  176. package/skills/pr-converge/SKILL.md +472 -469
  177. package/skills/pr-converge/reference/examples.md +3 -3
  178. package/skills/pr-converge/reference/fix-protocol.md +1 -1
  179. package/skills/pr-converge/reference/ground-rules.md +7 -4
  180. package/skills/pr-converge/reference/multi-pr-orchestration.md +4 -1
  181. package/skills/pr-converge/reference/per-tick.md +5 -5
  182. package/skills/pr-converge/reference/progress-checklist.md +1 -1
  183. package/skills/pr-converge/scripts/check_convergence_gates.py +279 -279
  184. package/skills/pr-converge/scripts/test_check_convergence_codex.py +507 -507
  185. package/skills/pr-converge/scripts/test_check_convergence_gates.py +84 -84
  186. package/skills/pr-converge/test_step5_host_branch.py +1 -1
  187. package/skills/pr-fix-protocol/SKILL.md +1 -1
  188. package/skills/privacy-hygiene/SKILL.md +68 -68
  189. package/skills/prototype/workflows/promotion.md +1 -1
  190. package/skills/release-notes-html/SKILL.md +164 -0
  191. package/skills/task-build/CLAUDE.md +8 -7
  192. package/skills/task-build/SKILL.md +16 -8
  193. package/skills/task-build/reference/tool-routing.md +19 -0
  194. package/scripts/test_invoke_code_review.py +0 -966
  195. package/skills/closeout/reference/issue-body-templates.md +0 -108
@@ -0,0 +1,123 @@
1
+ """Working-tree dirty detection and the git status porcelain command."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import subprocess
6
+ from collections.abc import Sequence
7
+ from pathlib import Path
8
+
9
+ import pytest
10
+
11
+ import invoke_code_review as invoker
12
+ from _code_review_test_support import (
13
+ DIRTY_FILE_CONTENTS,
14
+ DIRTY_FILE_NAME,
15
+ FIXTURE_CHAIN_RETURNCODE,
16
+ FIXTURE_GIT_STATUS_FAILURE_RETURNCODE,
17
+ FIXTURE_SERVED_COMMAND,
18
+ FIXTURE_SESSION_OPUS,
19
+ HOST_PROFILE_THIRD_PARTY,
20
+ claude_served,
21
+ init_git_repository,
22
+ install_seams,
23
+ run_review,
24
+ )
25
+ from dev_env_scripts_constants.code_review_constants import (
26
+ GIT_BINARY,
27
+ GIT_PORCELAIN_FLAG,
28
+ GIT_STATUS_SUBCOMMAND,
29
+ MODE_CHAIN,
30
+ )
31
+
32
+
33
+ def test_dirty_tree_true_after_chain_writes_file(
34
+ monkeypatch: pytest.MonkeyPatch, tmp_path: Path
35
+ ) -> None:
36
+ working_directory = init_git_repository(tmp_path / "repo")
37
+ install_seams(
38
+ monkeypatch,
39
+ host_profile=HOST_PROFILE_THIRD_PARTY,
40
+ claude_outcome=claude_served(),
41
+ should_dirty_tree_on_chain=True,
42
+ working_directory=working_directory,
43
+ )
44
+
45
+ review_outcome = run_review(working_directory, session_model=FIXTURE_SESSION_OPUS)
46
+
47
+ assert review_outcome.mode == MODE_CHAIN
48
+ assert review_outcome.is_dirty_tree is True
49
+ assert review_outcome.served_command == FIXTURE_SERVED_COMMAND
50
+ assert review_outcome.returncode == FIXTURE_CHAIN_RETURNCODE
51
+
52
+
53
+ def test_dirty_tree_false_when_chain_leaves_tree_clean(
54
+ monkeypatch: pytest.MonkeyPatch, tmp_path: Path
55
+ ) -> None:
56
+ working_directory = init_git_repository(tmp_path / "repo")
57
+ install_seams(
58
+ monkeypatch,
59
+ host_profile=HOST_PROFILE_THIRD_PARTY,
60
+ claude_outcome=claude_served(),
61
+ should_dirty_tree_on_chain=False,
62
+ working_directory=working_directory,
63
+ )
64
+
65
+ review_outcome = run_review(working_directory, session_model=FIXTURE_SESSION_OPUS)
66
+
67
+ assert review_outcome.is_dirty_tree is False
68
+
69
+
70
+ def test_is_working_tree_dirty_against_real_git_repo(tmp_path: Path) -> None:
71
+ working_directory = init_git_repository(tmp_path / "repo")
72
+ assert invoker.is_working_tree_dirty(working_directory) is False
73
+ (working_directory / DIRTY_FILE_NAME).write_text(
74
+ DIRTY_FILE_CONTENTS, encoding="utf-8"
75
+ )
76
+ assert invoker.is_working_tree_dirty(working_directory) is True
77
+
78
+
79
+ def test_git_status_command_uses_porcelain(
80
+ monkeypatch: pytest.MonkeyPatch, tmp_path: Path
81
+ ) -> None:
82
+ working_directory = init_git_repository(tmp_path / "repo")
83
+ all_commands: list[list[str]] = []
84
+
85
+ def fake_git_status(
86
+ all_command_tokens: Sequence[str],
87
+ *all_positionals: object,
88
+ **all_keywords: object,
89
+ ) -> subprocess.CompletedProcess[str]:
90
+ del all_positionals, all_keywords
91
+ all_commands.append(list(all_command_tokens))
92
+ return subprocess.CompletedProcess(
93
+ args=list(all_command_tokens),
94
+ returncode=0,
95
+ stdout="",
96
+ stderr="",
97
+ )
98
+
99
+ monkeypatch.setattr(invoker, "review_git_status_runner", fake_git_status)
100
+ invoker.is_working_tree_dirty(working_directory)
101
+ assert all_commands == [[GIT_BINARY, GIT_STATUS_SUBCOMMAND, GIT_PORCELAIN_FLAG]]
102
+
103
+
104
+ def test_is_working_tree_dirty_nonzero_returncode_is_not_clean(
105
+ monkeypatch: pytest.MonkeyPatch, tmp_path: Path
106
+ ) -> None:
107
+ working_directory = init_git_repository(tmp_path / "repo")
108
+
109
+ def fake_git_status(
110
+ all_command_tokens: Sequence[str],
111
+ *all_positionals: object,
112
+ **all_keywords: object,
113
+ ) -> subprocess.CompletedProcess[str]:
114
+ del all_positionals, all_keywords
115
+ return subprocess.CompletedProcess(
116
+ args=list(all_command_tokens),
117
+ returncode=FIXTURE_GIT_STATUS_FAILURE_RETURNCODE,
118
+ stdout="",
119
+ stderr="fatal: not a git repository",
120
+ )
121
+
122
+ monkeypatch.setattr(invoker, "review_git_status_runner", fake_git_status)
123
+ assert invoker.is_working_tree_dirty(working_directory) is True
@@ -0,0 +1,99 @@
1
+ """Mode-decision behavior of the host-aware ``/code-review`` invoker."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from pathlib import Path
6
+
7
+ import pytest
8
+
9
+ import invoke_code_review as invoker
10
+ from _code_review_test_support import (
11
+ FIXTURE_SESSION_HAIKU,
12
+ FIXTURE_SESSION_OPUS,
13
+ FIXTURE_SESSION_OPUS_UPPER,
14
+ FIXTURE_SESSION_SONNET,
15
+ HOST_PROFILE_CLAUDE,
16
+ HOST_PROFILE_THIRD_PARTY,
17
+ claude_served,
18
+ init_git_repository,
19
+ install_seams,
20
+ run_review,
21
+ )
22
+ from dev_env_scripts_constants.code_review_constants import (
23
+ IN_SESSION_RETURNCODE,
24
+ MODE_CHAIN,
25
+ MODE_IN_SESSION,
26
+ )
27
+
28
+
29
+ @pytest.mark.parametrize(
30
+ ("host_profile", "session_model", "expected_mode"),
31
+ [
32
+ (HOST_PROFILE_CLAUDE, FIXTURE_SESSION_OPUS, MODE_IN_SESSION),
33
+ (HOST_PROFILE_CLAUDE, FIXTURE_SESSION_OPUS_UPPER, MODE_IN_SESSION),
34
+ (HOST_PROFILE_CLAUDE, FIXTURE_SESSION_SONNET, MODE_CHAIN),
35
+ (HOST_PROFILE_CLAUDE, FIXTURE_SESSION_HAIKU, MODE_CHAIN),
36
+ (HOST_PROFILE_THIRD_PARTY, FIXTURE_SESSION_OPUS, MODE_CHAIN),
37
+ (HOST_PROFILE_THIRD_PARTY, FIXTURE_SESSION_SONNET, MODE_CHAIN),
38
+ ],
39
+ )
40
+ def test_mode_decision_host_and_model_matrix(
41
+ monkeypatch: pytest.MonkeyPatch,
42
+ tmp_path: Path,
43
+ host_profile: str,
44
+ session_model: str,
45
+ expected_mode: str,
46
+ ) -> None:
47
+ working_directory = init_git_repository(tmp_path / "repo")
48
+ claude_outcome = claude_served() if expected_mode == MODE_CHAIN else None
49
+ call_log = install_seams(
50
+ monkeypatch,
51
+ host_profile=host_profile,
52
+ claude_outcome=claude_outcome,
53
+ working_directory=working_directory,
54
+ )
55
+
56
+ review_outcome = run_review(working_directory, session_model=session_model)
57
+
58
+ assert review_outcome.mode == expected_mode
59
+ assert call_log.host_profile_calls == 1
60
+ if expected_mode == MODE_IN_SESSION:
61
+ assert call_log.claude_calls == 0
62
+ assert review_outcome.served_command is None
63
+ assert review_outcome.returncode == IN_SESSION_RETURNCODE
64
+ assert review_outcome.is_dirty_tree is False
65
+ else:
66
+ assert call_log.claude_calls == 1
67
+
68
+
69
+ @pytest.mark.parametrize(
70
+ ("session_model", "expected_is_opus"),
71
+ [
72
+ (FIXTURE_SESSION_OPUS, True),
73
+ (FIXTURE_SESSION_OPUS_UPPER, True),
74
+ (FIXTURE_SESSION_SONNET, False),
75
+ (" opus ", True),
76
+ ],
77
+ )
78
+ def test_is_opus_session_model(session_model: str, expected_is_opus: bool) -> None:
79
+ assert invoker.is_opus_session_model(session_model) is expected_is_opus
80
+
81
+
82
+ @pytest.mark.parametrize(
83
+ ("host_profile", "session_model", "expected_mode"),
84
+ [
85
+ (HOST_PROFILE_CLAUDE, FIXTURE_SESSION_OPUS, MODE_IN_SESSION),
86
+ (HOST_PROFILE_CLAUDE, FIXTURE_SESSION_SONNET, MODE_CHAIN),
87
+ (HOST_PROFILE_THIRD_PARTY, FIXTURE_SESSION_OPUS, MODE_CHAIN),
88
+ ],
89
+ )
90
+ def test_decide_review_mode(
91
+ host_profile: str, session_model: str, expected_mode: str
92
+ ) -> None:
93
+ assert (
94
+ invoker.decide_review_mode(
95
+ host_profile=host_profile,
96
+ session_model=session_model,
97
+ )
98
+ == expected_mode
99
+ )