argus-code-review 0.2.11__tar.gz → 0.3.0__tar.gz

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 (158) hide show
  1. {argus_code_review-0.2.11 → argus_code_review-0.3.0}/CHANGELOG.md +18 -1
  2. {argus_code_review-0.2.11 → argus_code_review-0.3.0}/PKG-INFO +39 -3
  3. {argus_code_review-0.2.11 → argus_code_review-0.3.0}/README.md +38 -2
  4. {argus_code_review-0.2.11 → argus_code_review-0.3.0}/argus/cli.py +49 -5
  5. {argus_code_review-0.2.11 → argus_code_review-0.3.0}/argus/config.py +7 -0
  6. {argus_code_review-0.2.11 → argus_code_review-0.3.0}/argus/graph.py +247 -75
  7. {argus_code_review-0.2.11 → argus_code_review-0.3.0}/argus/models.py +46 -1
  8. argus_code_review-0.3.0/argus/prompts/pr-review-scope.md +18 -0
  9. {argus_code_review-0.2.11 → argus_code_review-0.3.0}/argus/prompts_runtime.py +3 -2
  10. argus_code_review-0.3.0/argus/scope.py +199 -0
  11. {argus_code_review-0.2.11 → argus_code_review-0.3.0}/argus/storage/http.py +5 -2
  12. {argus_code_review-0.2.11 → argus_code_review-0.3.0}/tests/golden/review_response.schema.json +159 -0
  13. {argus_code_review-0.2.11 → argus_code_review-0.3.0}/tests/test_bench_config_guard.py +11 -11
  14. {argus_code_review-0.2.11 → argus_code_review-0.3.0}/tests/test_cli_post_review.py +13 -1
  15. {argus_code_review-0.2.11 → argus_code_review-0.3.0}/tests/test_cli_preflight.py +2 -0
  16. {argus_code_review-0.2.11 → argus_code_review-0.3.0}/tests/test_graph_fetch_diff.py +113 -9
  17. {argus_code_review-0.2.11 → argus_code_review-0.3.0}/tests/test_graph_precheck.py +8 -4
  18. {argus_code_review-0.2.11 → argus_code_review-0.3.0}/tests/test_graph_progress.py +147 -130
  19. {argus_code_review-0.2.11 → argus_code_review-0.3.0}/tests/test_multi_round.py +30 -5
  20. argus_code_review-0.3.0/tests/test_scope.py +213 -0
  21. argus_code_review-0.3.0/tests/test_scope_gate.py +688 -0
  22. {argus_code_review-0.2.11 → argus_code_review-0.3.0}/.gitignore +0 -0
  23. {argus_code_review-0.2.11 → argus_code_review-0.3.0}/LICENSE +0 -0
  24. {argus_code_review-0.2.11 → argus_code_review-0.3.0}/argus/__init__.py +0 -0
  25. {argus_code_review-0.2.11 → argus_code_review-0.3.0}/argus/bench.py +0 -0
  26. {argus_code_review-0.2.11 → argus_code_review-0.3.0}/argus/bench_default.toml +0 -0
  27. {argus_code_review-0.2.11 → argus_code_review-0.3.0}/argus/coverage.py +0 -0
  28. {argus_code_review-0.2.11 → argus_code_review-0.3.0}/argus/dotenv_utils.py +0 -0
  29. {argus_code_review-0.2.11 → argus_code_review-0.3.0}/argus/gemini_cache.py +0 -0
  30. {argus_code_review-0.2.11 → argus_code_review-0.3.0}/argus/gemini_runner.py +0 -0
  31. {argus_code_review-0.2.11 → argus_code_review-0.3.0}/argus/github_client.py +0 -0
  32. {argus_code_review-0.2.11 → argus_code_review-0.3.0}/argus/helpers.py +0 -0
  33. {argus_code_review-0.2.11 → argus_code_review-0.3.0}/argus/llm/models.py +0 -0
  34. {argus_code_review-0.2.11 → argus_code_review-0.3.0}/argus/llm/output_models.py +0 -0
  35. {argus_code_review-0.2.11 → argus_code_review-0.3.0}/argus/llm/pricing.py +0 -0
  36. {argus_code_review-0.2.11 → argus_code_review-0.3.0}/argus/llm/usage.py +0 -0
  37. {argus_code_review-0.2.11 → argus_code_review-0.3.0}/argus/openai_client.py +0 -0
  38. {argus_code_review-0.2.11 → argus_code_review-0.3.0}/argus/openai_runner.py +0 -0
  39. {argus_code_review-0.2.11 → argus_code_review-0.3.0}/argus/pipeline_models.py +0 -0
  40. {argus_code_review-0.2.11 → argus_code_review-0.3.0}/argus/precheck/__init__.py +0 -0
  41. {argus_code_review-0.2.11 → argus_code_review-0.3.0}/argus/precheck/actions_scanner.py +0 -0
  42. {argus_code_review-0.2.11 → argus_code_review-0.3.0}/argus/precheck/engine.py +0 -0
  43. {argus_code_review-0.2.11 → argus_code_review-0.3.0}/argus/precheck/eslint_bundle/.gitignore +0 -0
  44. {argus_code_review-0.2.11 → argus_code_review-0.3.0}/argus/precheck/eslint_bundle/README.md +0 -0
  45. {argus_code_review-0.2.11 → argus_code_review-0.3.0}/argus/precheck/eslint_bundle/eslint.config.js +0 -0
  46. {argus_code_review-0.2.11 → argus_code_review-0.3.0}/argus/precheck/eslint_bundle/package-lock.json +0 -0
  47. {argus_code_review-0.2.11 → argus_code_review-0.3.0}/argus/precheck/eslint_bundle/package.json +0 -0
  48. {argus_code_review-0.2.11 → argus_code_review-0.3.0}/argus/precheck/js_scanner.py +0 -0
  49. {argus_code_review-0.2.11 → argus_code_review-0.3.0}/argus/precheck/migration_scanner.py +0 -0
  50. {argus_code_review-0.2.11 → argus_code_review-0.3.0}/argus/precheck/rules/README.md +0 -0
  51. {argus_code_review-0.2.11 → argus_code_review-0.3.0}/argus/precheck/sarif.py +0 -0
  52. {argus_code_review-0.2.11 → argus_code_review-0.3.0}/argus/precheck/scanner_utils.py +0 -0
  53. {argus_code_review-0.2.11 → argus_code_review-0.3.0}/argus/precheck/secrets_scanner.py +0 -0
  54. {argus_code_review-0.2.11 → argus_code_review-0.3.0}/argus/precheck/shadow.py +0 -0
  55. {argus_code_review-0.2.11 → argus_code_review-0.3.0}/argus/precheck/terraform_scanner.py +0 -0
  56. {argus_code_review-0.2.11 → argus_code_review-0.3.0}/argus/precheck/workflow_lint_scanner.py +0 -0
  57. {argus_code_review-0.2.11 → argus_code_review-0.3.0}/argus/prompts/__init__.py +0 -0
  58. {argus_code_review-0.2.11 → argus_code_review-0.3.0}/argus/prompts/pr-review-blocking-validator.md +0 -0
  59. {argus_code_review-0.2.11 → argus_code_review-0.3.0}/argus/prompts/pr-review-coverage-check.md +0 -0
  60. {argus_code_review-0.2.11 → argus_code_review-0.3.0}/argus/prompts/pr-review-cross-cutting.md +0 -0
  61. {argus_code_review-0.2.11 → argus_code_review-0.3.0}/argus/prompts/pr-review-feedback-verifier.md +0 -0
  62. {argus_code_review-0.2.11 → argus_code_review-0.3.0}/argus/prompts/pr-review-lite.md +0 -0
  63. {argus_code_review-0.2.11 → argus_code_review-0.3.0}/argus/prompts/pr-review-planner.md +0 -0
  64. {argus_code_review-0.2.11 → argus_code_review-0.3.0}/argus/prompts/pr-review-preflight-router.md +0 -0
  65. {argus_code_review-0.2.11 → argus_code_review-0.3.0}/argus/prompts/pr-review-prior-art.md +0 -0
  66. {argus_code_review-0.2.11 → argus_code_review-0.3.0}/argus/prompts/pr-review-specialist-deployment.md +0 -0
  67. {argus_code_review-0.2.11 → argus_code_review-0.3.0}/argus/prompts/pr-review-specialist-frontend.md +0 -0
  68. {argus_code_review-0.2.11 → argus_code_review-0.3.0}/argus/prompts/pr-review-specialist-infra.md +0 -0
  69. {argus_code_review-0.2.11 → argus_code_review-0.3.0}/argus/prompts/pr-review-specialist-llm-patterns.md +0 -0
  70. {argus_code_review-0.2.11 → argus_code_review-0.3.0}/argus/prompts/pr-review-specialist-observability.md +0 -0
  71. {argus_code_review-0.2.11 → argus_code_review-0.3.0}/argus/prompts/pr-review-specialist-orchestration.md +0 -0
  72. {argus_code_review-0.2.11 → argus_code_review-0.3.0}/argus/prompts/pr-review-specialist-security.md +0 -0
  73. {argus_code_review-0.2.11 → argus_code_review-0.3.0}/argus/prompts/pr-review-specialist-slackbot.md +0 -0
  74. {argus_code_review-0.2.11 → argus_code_review-0.3.0}/argus/prompts/pr-review-specialist-sql.md +0 -0
  75. {argus_code_review-0.2.11 → argus_code_review-0.3.0}/argus/prompts/pr-review-subagent.md +0 -0
  76. {argus_code_review-0.2.11 → argus_code_review-0.3.0}/argus/prompts/pr-review-tests-and-docs.md +0 -0
  77. {argus_code_review-0.2.11 → argus_code_review-0.3.0}/argus/prompts/pr-review-writer.md +0 -0
  78. {argus_code_review-0.2.11 → argus_code_review-0.3.0}/argus/repo_provision.py +0 -0
  79. {argus_code_review-0.2.11 → argus_code_review-0.3.0}/argus/review_tools.py +0 -0
  80. {argus_code_review-0.2.11 → argus_code_review-0.3.0}/argus/runners.py +0 -0
  81. {argus_code_review-0.2.11 → argus_code_review-0.3.0}/argus/storage/__init__.py +0 -0
  82. {argus_code_review-0.2.11 → argus_code_review-0.3.0}/argus/storage/models.py +0 -0
  83. {argus_code_review-0.2.11 → argus_code_review-0.3.0}/argus/storage/precheck.py +0 -0
  84. {argus_code_review-0.2.11 → argus_code_review-0.3.0}/argus/storage/resolver.py +0 -0
  85. {argus_code_review-0.2.11 → argus_code_review-0.3.0}/argus/storage/session.py +0 -0
  86. {argus_code_review-0.2.11 → argus_code_review-0.3.0}/argus/storage/sql.py +0 -0
  87. {argus_code_review-0.2.11 → argus_code_review-0.3.0}/argus/storage/sqlite.py +0 -0
  88. {argus_code_review-0.2.11 → argus_code_review-0.3.0}/pyproject.toml +0 -0
  89. {argus_code_review-0.2.11 → argus_code_review-0.3.0}/schema/008_add_code_reviews.sql +0 -0
  90. {argus_code_review-0.2.11 → argus_code_review-0.3.0}/schema/009_add_reviewer_version.sql +0 -0
  91. {argus_code_review-0.2.11 → argus_code_review-0.3.0}/schema/010_add_review_patterns.sql +0 -0
  92. {argus_code_review-0.2.11 → argus_code_review-0.3.0}/schema/011_add_review_progress_columns.sql +0 -0
  93. {argus_code_review-0.2.11 → argus_code_review-0.3.0}/schema/015_create_agent_runs.sql +0 -0
  94. {argus_code_review-0.2.11 → argus_code_review-0.3.0}/schema/016_add_agent_runs_failure_reason.sql +0 -0
  95. {argus_code_review-0.2.11 → argus_code_review-0.3.0}/schema/017_add_precheck_rules.sql +0 -0
  96. {argus_code_review-0.2.11 → argus_code_review-0.3.0}/schema/018_widen_agent_runs_failure_reason.sql +0 -0
  97. {argus_code_review-0.2.11 → argus_code_review-0.3.0}/tests/__init__.py +0 -0
  98. {argus_code_review-0.2.11 → argus_code_review-0.3.0}/tests/conftest.py +0 -0
  99. {argus_code_review-0.2.11 → argus_code_review-0.3.0}/tests/storage/__init__.py +0 -0
  100. {argus_code_review-0.2.11 → argus_code_review-0.3.0}/tests/storage/test_backend_contract.py +0 -0
  101. {argus_code_review-0.2.11 → argus_code_review-0.3.0}/tests/storage/test_http.py +0 -0
  102. {argus_code_review-0.2.11 → argus_code_review-0.3.0}/tests/storage/test_models.py +0 -0
  103. {argus_code_review-0.2.11 → argus_code_review-0.3.0}/tests/storage/test_resolver.py +0 -0
  104. {argus_code_review-0.2.11 → argus_code_review-0.3.0}/tests/storage/test_session.py +0 -0
  105. {argus_code_review-0.2.11 → argus_code_review-0.3.0}/tests/storage/test_sql.py +0 -0
  106. {argus_code_review-0.2.11 → argus_code_review-0.3.0}/tests/storage/test_sqlite_backend.py +0 -0
  107. {argus_code_review-0.2.11 → argus_code_review-0.3.0}/tests/test_actions_scanner.py +0 -0
  108. {argus_code_review-0.2.11 → argus_code_review-0.3.0}/tests/test_argus_review_local.py +0 -0
  109. {argus_code_review-0.2.11 → argus_code_review-0.3.0}/tests/test_bench.py +0 -0
  110. {argus_code_review-0.2.11 → argus_code_review-0.3.0}/tests/test_catchup_gate.py +0 -0
  111. {argus_code_review-0.2.11 → argus_code_review-0.3.0}/tests/test_cli_args.py +0 -0
  112. {argus_code_review-0.2.11 → argus_code_review-0.3.0}/tests/test_cli_output_contract.py +0 -0
  113. {argus_code_review-0.2.11 → argus_code_review-0.3.0}/tests/test_cli_prompts.py +0 -0
  114. {argus_code_review-0.2.11 → argus_code_review-0.3.0}/tests/test_config.py +0 -0
  115. {argus_code_review-0.2.11 → argus_code_review-0.3.0}/tests/test_conftest.py +0 -0
  116. {argus_code_review-0.2.11 → argus_code_review-0.3.0}/tests/test_gemini_cache.py +0 -0
  117. {argus_code_review-0.2.11 → argus_code_review-0.3.0}/tests/test_gemini_runner.py +0 -0
  118. {argus_code_review-0.2.11 → argus_code_review-0.3.0}/tests/test_github_client_checks_signal.py +0 -0
  119. {argus_code_review-0.2.11 → argus_code_review-0.3.0}/tests/test_github_client_write.py +0 -0
  120. {argus_code_review-0.2.11 → argus_code_review-0.3.0}/tests/test_graph_get_llm_temperature.py +0 -0
  121. {argus_code_review-0.2.11 → argus_code_review-0.3.0}/tests/test_graph_http_guards.py +0 -0
  122. {argus_code_review-0.2.11 → argus_code_review-0.3.0}/tests/test_graph_preflight_image_bump.py +0 -0
  123. {argus_code_review-0.2.11 → argus_code_review-0.3.0}/tests/test_graph_storage_resolution.py +0 -0
  124. {argus_code_review-0.2.11 → argus_code_review-0.3.0}/tests/test_graph_timeout_surfacing.py +0 -0
  125. {argus_code_review-0.2.11 → argus_code_review-0.3.0}/tests/test_js_scanner.py +0 -0
  126. {argus_code_review-0.2.11 → argus_code_review-0.3.0}/tests/test_llm_models.py +0 -0
  127. {argus_code_review-0.2.11 → argus_code_review-0.3.0}/tests/test_llm_models_override.py +0 -0
  128. {argus_code_review-0.2.11 → argus_code_review-0.3.0}/tests/test_llm_pricing.py +0 -0
  129. {argus_code_review-0.2.11 → argus_code_review-0.3.0}/tests/test_migration_scanner.py +0 -0
  130. {argus_code_review-0.2.11 → argus_code_review-0.3.0}/tests/test_models.py +0 -0
  131. {argus_code_review-0.2.11 → argus_code_review-0.3.0}/tests/test_openai_client.py +0 -0
  132. {argus_code_review-0.2.11 → argus_code_review-0.3.0}/tests/test_openai_runner.py +0 -0
  133. {argus_code_review-0.2.11 → argus_code_review-0.3.0}/tests/test_output_models.py +0 -0
  134. {argus_code_review-0.2.11 → argus_code_review-0.3.0}/tests/test_packaging.py +0 -0
  135. {argus_code_review-0.2.11 → argus_code_review-0.3.0}/tests/test_plan_review.py +0 -0
  136. {argus_code_review-0.2.11 → argus_code_review-0.3.0}/tests/test_precheck_engine.py +0 -0
  137. {argus_code_review-0.2.11 → argus_code_review-0.3.0}/tests/test_precheck_engine_integration.py +0 -0
  138. {argus_code_review-0.2.11 → argus_code_review-0.3.0}/tests/test_precheck_sarif.py +0 -0
  139. {argus_code_review-0.2.11 → argus_code_review-0.3.0}/tests/test_precheck_shadow.py +0 -0
  140. {argus_code_review-0.2.11 → argus_code_review-0.3.0}/tests/test_precheck_shadow_integration.py +0 -0
  141. {argus_code_review-0.2.11 → argus_code_review-0.3.0}/tests/test_prompts.py +0 -0
  142. {argus_code_review-0.2.11 → argus_code_review-0.3.0}/tests/test_repo_provision.py +0 -0
  143. {argus_code_review-0.2.11 → argus_code_review-0.3.0}/tests/test_review_patterns_integration.py +0 -0
  144. {argus_code_review-0.2.11 → argus_code_review-0.3.0}/tests/test_review_tools.py +0 -0
  145. {argus_code_review-0.2.11 → argus_code_review-0.3.0}/tests/test_runners_context7.py +0 -0
  146. {argus_code_review-0.2.11 → argus_code_review-0.3.0}/tests/test_runners_context_usage.py +0 -0
  147. {argus_code_review-0.2.11 → argus_code_review-0.3.0}/tests/test_runners_helpers.py +0 -0
  148. {argus_code_review-0.2.11 → argus_code_review-0.3.0}/tests/test_runners_new.py +0 -0
  149. {argus_code_review-0.2.11 → argus_code_review-0.3.0}/tests/test_runners_turn_budget.py +0 -0
  150. {argus_code_review-0.2.11 → argus_code_review-0.3.0}/tests/test_scanner_utils.py +0 -0
  151. {argus_code_review-0.2.11 → argus_code_review-0.3.0}/tests/test_secrets_scanner.py +0 -0
  152. {argus_code_review-0.2.11 → argus_code_review-0.3.0}/tests/test_specialist_validation.py +0 -0
  153. {argus_code_review-0.2.11 → argus_code_review-0.3.0}/tests/test_stage_costs.py +0 -0
  154. {argus_code_review-0.2.11 → argus_code_review-0.3.0}/tests/test_storage_precheck.py +0 -0
  155. {argus_code_review-0.2.11 → argus_code_review-0.3.0}/tests/test_structured_output_methods.py +0 -0
  156. {argus_code_review-0.2.11 → argus_code_review-0.3.0}/tests/test_terraform_scanner.py +0 -0
  157. {argus_code_review-0.2.11 → argus_code_review-0.3.0}/tests/test_terraform_scanner_integration.py +0 -0
  158. {argus_code_review-0.2.11 → argus_code_review-0.3.0}/tests/test_workflow_lint_scanner.py +0 -0
@@ -7,6 +7,22 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.3.0] - 2026-10-13
11
+
12
+ ### Added
13
+
14
+ - Scope gate: before the early verifier and preflight (including incremental and lite rounds), a new `pr-review-scope` prompt splits the full pinned PR (`merge-base..head`) into independently mergeable behaviors. More than one behavior halts with `scope_split`. An unverifiable manifest (overlapping, unknown, or unassigned files, cyclic dependencies, a 300-file listing, an oversized or indeterminate input, or any gate error) halts with `scope_unverified`.
15
+ - Workload halts: a plan that needs more reviewers than `ARGUS_REVIEWER_DISPATCH_BUDGET` (default 16, range 3-50) halts with `dispatch_budget_exceeded` before any reviewer starts. More than 50 gap-fill reviewers halts with `gap_fill_cap_exceeded`.
16
+ - `ReviewResponse.scope` (pipeline-generated receipt: `base_sha`, `head_sha`, `behaviors`, `rationale`, `override_reason`) and `ReviewResponse.halt` (`code`, `detail`, `head_sha`, `split_suggestions`, `dispatch_count`, `dispatch_budget`). Both are excluded from LLM-generated output. A halt is always `BLOCKING` with `HIGH` risk and has no findings.
17
+ - CLI `--review-override-sha` and `--review-override-reason`: a human exception for `scope_split` and for a plan over the budget (up to the hard ceiling of 50). The SHA must equal the resolved head. It never overrides `scope_unverified` or `gap_fill_cap_exceeded`.
18
+
19
+ ### Changed
20
+
21
+ - The fan-out ceiling of 50 reviewers no longer truncates the plan or the gap-fill list; exceeding it halts the review.
22
+ - A halted round is stored with `verdict = NULL` and `current_stage = completed`, so it never becomes the prior round: the last reviewed SHA and round count do not advance. The HTTP storage client now skips `verdict = null` rounds like the SQL backends. No schema migration.
23
+ - `--commit-status` posts every outcome to the reviewed head (`halt.head_sha` or `scope.head_sha`) instead of re-reading the live head. The scope gate and bench guard share one merge base pinned in `fetch_diff`; an unpinnable base, an empty manifest for a non-empty diff, an empty-file behavior, or untrimmed titles halt `scope_unverified`.
24
+ - `ReviewResponse` JSON schema gains `scope` and `halt` (golden snapshot updated). The stderr summary block is unchanged.
25
+
10
26
  ## [0.2.11] - 2026-10-12
11
27
 
12
28
  ### Fixed
@@ -407,7 +423,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
407
423
  packaged set.
408
424
  - `argus --version`, `argus prompts list`, and `argus prompts export`.
409
425
 
410
- [Unreleased]: https://github.com/redesignhealth/argus-review/compare/v0.2.11...HEAD
426
+ [Unreleased]: https://github.com/redesignhealth/argus-review/compare/v0.3.0...HEAD
427
+ [0.3.0]: https://github.com/redesignhealth/argus-review/compare/v0.2.11...v0.3.0
411
428
  [0.2.11]: https://github.com/redesignhealth/argus-review/compare/v0.2.10...v0.2.11
412
429
  [0.2.10]: https://github.com/redesignhealth/argus-review/compare/v0.2.9...v0.2.10
413
430
  [0.2.9]: https://github.com/redesignhealth/argus-review/compare/v0.2.8...v0.2.9
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: argus-code-review
3
- Version: 0.2.11
3
+ Version: 0.3.0
4
4
  Summary: Self-orchestrated PR review agent using LangGraph + Claude Agent SDK
5
5
  Project-URL: Repository, https://github.com/redesignhealth/argus-review
6
6
  Project-URL: Issues, https://github.com/redesignhealth/argus-review/issues
@@ -137,10 +137,46 @@ consolidates every finding into one review with a verdict and severity
137
137
  tags.
138
138
 
139
139
  ```
140
- fetch_diff → plan → [system reviewers ‖ specialists ‖ cross-cutting] (parallel)
140
+ fetch_diff → scope_gate → plan → [system reviewers ‖ specialists ‖ cross-cutting] (parallel)
141
141
  → check_coverage → (fill_gaps if needed) → write_review → persist
142
142
  ```
143
143
 
144
+ ### Scope and workload halts
145
+
146
+ Before dispatching reviewer sessions, Argus checks the whole PR (merge base to
147
+ head, on every round, including incremental and lite rounds). If the PR
148
+ contains more than one independently mergeable behavior, or Argus cannot
149
+ verify its scope, the review halts with a `BLOCKING` verdict, `HIGH` risk,
150
+ and no findings. The same happens when the plan needs more reviewers than
151
+ `ARGUS_REVIEWER_DISPATCH_BUDGET` (default 16, range 3-50), or when more
152
+ than 50 gap-fill reviewers are needed. The response carries a `halt`
153
+ object with one of these codes: `scope_split`, `scope_unverified`,
154
+ `dispatch_budget_exceeded`, `gap_fill_cap_exceeded`. A successful review
155
+ carries a `scope` receipt pinned to the reviewed head SHA.
156
+
157
+ A halted round is not a reviewed round: it is stored with a null verdict,
158
+ so the next run still diffs from the last reviewed SHA. Argus does not
159
+ revoke approvals issued before this guard was enabled, including stored
160
+ approvals for the same SHA. External merge gates must reject the latest
161
+ halt outcome instead of accepting any historical `APPROVE` row.
162
+
163
+ A human can authorize a multi-behavior PR, or a plan over the budget (up to 50
164
+ reviewers), for one exact head:
165
+
166
+ ```bash
167
+ argus review owner/repo --pr 123 \
168
+ --review-override-sha HEAD_SHA \
169
+ --review-override-reason "why this PR stays together"
170
+ ```
171
+
172
+ Both flags are required together. The SHA must equal the head Argus
173
+ resolves, or the run fails before it spends anything (with `--sha`,
174
+ pass the full 40-character SHA). The reason (1-500
175
+ characters) is stored in `scope.override_reason`. These flags exist only
176
+ on the CLI, never on the API request. Agents and skills must not add
177
+ them automatically. They never override `scope_unverified` or
178
+ `gap_fill_cap_exceeded`.
179
+
144
180
  Full design rationale is in [`docs/ARCHITECTURE.md`](docs/ARCHITECTURE.md).
145
181
 
146
182
  ## Getting started
@@ -351,7 +387,7 @@ details are in [`docs/STORAGE.md`](docs/STORAGE.md).
351
387
 
352
388
  ## Adapting Argus to your team
353
389
 
354
- Argus ships with a full set of production review prompts — 20 files, one
390
+ Argus ships with a full set of production review prompts — 21 files, one
355
391
  per reviewer role — rather than a thin default. Adapting them to your team
356
392
  is the main lever for making Argus *yours*.
357
393
 
@@ -86,10 +86,46 @@ consolidates every finding into one review with a verdict and severity
86
86
  tags.
87
87
 
88
88
  ```
89
- fetch_diff → plan → [system reviewers ‖ specialists ‖ cross-cutting] (parallel)
89
+ fetch_diff → scope_gate → plan → [system reviewers ‖ specialists ‖ cross-cutting] (parallel)
90
90
  → check_coverage → (fill_gaps if needed) → write_review → persist
91
91
  ```
92
92
 
93
+ ### Scope and workload halts
94
+
95
+ Before dispatching reviewer sessions, Argus checks the whole PR (merge base to
96
+ head, on every round, including incremental and lite rounds). If the PR
97
+ contains more than one independently mergeable behavior, or Argus cannot
98
+ verify its scope, the review halts with a `BLOCKING` verdict, `HIGH` risk,
99
+ and no findings. The same happens when the plan needs more reviewers than
100
+ `ARGUS_REVIEWER_DISPATCH_BUDGET` (default 16, range 3-50), or when more
101
+ than 50 gap-fill reviewers are needed. The response carries a `halt`
102
+ object with one of these codes: `scope_split`, `scope_unverified`,
103
+ `dispatch_budget_exceeded`, `gap_fill_cap_exceeded`. A successful review
104
+ carries a `scope` receipt pinned to the reviewed head SHA.
105
+
106
+ A halted round is not a reviewed round: it is stored with a null verdict,
107
+ so the next run still diffs from the last reviewed SHA. Argus does not
108
+ revoke approvals issued before this guard was enabled, including stored
109
+ approvals for the same SHA. External merge gates must reject the latest
110
+ halt outcome instead of accepting any historical `APPROVE` row.
111
+
112
+ A human can authorize a multi-behavior PR, or a plan over the budget (up to 50
113
+ reviewers), for one exact head:
114
+
115
+ ```bash
116
+ argus review owner/repo --pr 123 \
117
+ --review-override-sha HEAD_SHA \
118
+ --review-override-reason "why this PR stays together"
119
+ ```
120
+
121
+ Both flags are required together. The SHA must equal the head Argus
122
+ resolves, or the run fails before it spends anything (with `--sha`,
123
+ pass the full 40-character SHA). The reason (1-500
124
+ characters) is stored in `scope.override_reason`. These flags exist only
125
+ on the CLI, never on the API request. Agents and skills must not add
126
+ them automatically. They never override `scope_unverified` or
127
+ `gap_fill_cap_exceeded`.
128
+
93
129
  Full design rationale is in [`docs/ARCHITECTURE.md`](docs/ARCHITECTURE.md).
94
130
 
95
131
  ## Getting started
@@ -300,7 +336,7 @@ details are in [`docs/STORAGE.md`](docs/STORAGE.md).
300
336
 
301
337
  ## Adapting Argus to your team
302
338
 
303
- Argus ships with a full set of production review prompts — 20 files, one
339
+ Argus ships with a full set of production review prompts — 21 files, one
304
340
  per reviewer role — rather than a thin default. Adapting them to your team
305
341
  is the main lever for making Argus *yours*.
306
342
 
@@ -225,13 +225,22 @@ def _check_settings(settings: "Settings") -> None:
225
225
  sys.exit(1)
226
226
 
227
227
 
228
- async def run(request: "ReviewRequest") -> "ReviewResponse":
228
+ async def run(
229
+ request: "ReviewRequest",
230
+ review_override_sha: str | None = None,
231
+ review_override_reason: str | None = None,
232
+ ) -> "ReviewResponse":
229
233
  """Run the review pipeline (storage backend resolved automatically:
230
234
  Postgres if ``ARGUS_DB_URL`` is set, HTTP shim if the storage URLs are
231
235
  set, else local SQLite)."""
232
236
  from argus.graph import run_review
233
237
 
234
- return await run_review(request, flow_run_id=None)
238
+ return await run_review(
239
+ request,
240
+ flow_run_id=None,
241
+ review_override_sha=review_override_sha,
242
+ review_override_reason=review_override_reason,
243
+ )
235
244
 
236
245
 
237
246
  # Sentinel distinguishing "--specialist-model/--frontier-model not passed at
@@ -337,6 +346,23 @@ def _add_review_args(parser: argparse.ArgumentParser) -> None:
337
346
  "success iff verdict is APPROVE). Same token rules as --post."
338
347
  ),
339
348
  )
349
+ parser.add_argument(
350
+ "--review-override-sha",
351
+ dest="review_override_sha",
352
+ default=None,
353
+ help=(
354
+ "Human exception: allow a multi-behavior PR or a reviewer plan over the dispatch "
355
+ "budget (up to the hard ceiling of 50) to proceed. Must equal the 40-character "
356
+ "lowercase HEAD SHA being reviewed, and needs --review-override-reason. Never "
357
+ "overrides an unverified scope or the gap-fill cap."
358
+ ),
359
+ )
360
+ parser.add_argument(
361
+ "--review-override-reason",
362
+ dest="review_override_reason",
363
+ default=None,
364
+ help="Reason for --review-override-sha (1-500 characters); recorded in the scope receipt.",
365
+ )
340
366
  parser.add_argument(
341
367
  "--no-prompt-overrides",
342
368
  action="store_true",
@@ -457,6 +483,12 @@ def _validate_review_args(parser: argparse.ArgumentParser, args: argparse.Namesp
457
483
  """Cross-flag validation that argparse's own declarative options can't express."""
458
484
  if args.post and args.sha and not args.pr:
459
485
  parser.error("--post requires --pr (a PR to comment on); --sha alone has nowhere to post")
486
+ from argus.scope import validate_override
487
+
488
+ try:
489
+ validate_override(args.review_override_sha, args.review_override_reason)
490
+ except ValueError as exc:
491
+ parser.error(str(exc))
460
492
 
461
493
 
462
494
  def _check_prerequisites() -> None:
@@ -545,7 +577,9 @@ def _post_review(repo: str, args: argparse.Namespace, response: "ReviewResponse"
545
577
  sys.exit(1)
546
578
 
547
579
  if args.commit_status:
548
- sha = args.sha
580
+ # Post to the head that was reviewed; a re-read head could approve an unreviewed commit.
581
+ pinned = response.halt or response.scope
582
+ sha = pinned.head_sha if pinned else args.sha
549
583
  if not sha and args.pr:
550
584
  pr = client.get_pull_request(repo, args.pr)
551
585
  sha = pr["head_sha"]
@@ -553,7 +587,11 @@ def _post_review(repo: str, args: argparse.Namespace, response: "ReviewResponse"
553
587
  logger.error("--commit-status could not resolve a head SHA (need --pr or --sha)")
554
588
  sys.exit(1)
555
589
  state = "success" if response.verdict.value == "APPROVE" else "failure"
556
- description = f"Argus: {response.verdict.value} ({response.risk_level.value} risk)"
590
+ description = (
591
+ f"Argus: HALT {response.halt.code}"
592
+ if response.halt
593
+ else f"Argus: {response.verdict.value} ({response.risk_level.value} risk)"
594
+ )
557
595
  try:
558
596
  client.set_commit_status(repo, sha, state, description)
559
597
  except Exception as exc: # noqa: BLE001 — surface any GitHub API error clearly
@@ -633,7 +671,13 @@ def _run_review(parser: argparse.ArgumentParser, args: argparse.Namespace) -> No
633
671
 
634
672
  start = time.monotonic()
635
673
  try:
636
- response = asyncio.run(run(request))
674
+ response = asyncio.run(
675
+ run(
676
+ request,
677
+ review_override_sha=args.review_override_sha,
678
+ review_override_reason=args.review_override_reason,
679
+ )
680
+ )
637
681
  except HistoryBackendConnectivityError as exc:
638
682
  # Same clean-exit treatment as _check_settings' missing-secrets
639
683
  # case -- a bad ARGUS_DB_URL/ARGUS_HISTORY_DB_PATH is a config
@@ -9,6 +9,7 @@ from __future__ import annotations
9
9
 
10
10
  from functools import lru_cache
11
11
 
12
+ from pydantic import Field
12
13
  from pydantic_settings import BaseSettings, SettingsConfigDict
13
14
 
14
15
  # Single source of truth for the reviewer-subprocess wall-clock budget.
@@ -60,6 +61,10 @@ class Settings(BaseSettings):
60
61
  name the way prompts are, so there's nothing to merge. Only
61
62
  consulted when the ``prechecks`` extra is installed; see
62
63
  ``argus.precheck``.
64
+ ARGUS_REVIEWER_DISPATCH_BUDGET: Maximum reviewers one round may dispatch
65
+ (system groups, their specialists, plus 2 always-on). Default 16,
66
+ allowed 3-50. A plan over the budget halts with
67
+ ``dispatch_budget_exceeded``; the hard ceiling of 50 never moves.
63
68
  ARGUS_STOCK_SEMGREP_PACKS: Comma-separated semgrep registry pack IDs
64
69
  (e.g. ``"p/secrets"``) to run alongside (or instead of) a custom
65
70
  ``ARGUS_RULES_DIR`` -- unlike a local rules directory, each pack
@@ -189,6 +194,8 @@ class Settings(BaseSettings):
189
194
 
190
195
  ARGUS_SESSION_TIMEOUT: int = DEFAULT_ARGUS_SESSION_TIMEOUT_S
191
196
 
197
+ ARGUS_REVIEWER_DISPATCH_BUDGET: int = Field(16, ge=3, le=50)
198
+
192
199
  ARGUS_BENCH_FILE: str | None = None
193
200
  ARGUS_NO_BENCH_OVERRIDES: bool = False
194
201