argus-code-review 0.2.6__tar.gz → 0.2.7__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 (152) hide show
  1. {argus_code_review-0.2.6 → argus_code_review-0.2.7}/CHANGELOG.md +20 -1
  2. {argus_code_review-0.2.6 → argus_code_review-0.2.7}/PKG-INFO +1 -1
  3. {argus_code_review-0.2.6 → argus_code_review-0.2.7}/argus/graph.py +5 -1
  4. {argus_code_review-0.2.6 → argus_code_review-0.2.7}/tests/storage/test_backend_contract.py +98 -16
  5. {argus_code_review-0.2.6 → argus_code_review-0.2.7}/tests/storage/test_http.py +68 -0
  6. {argus_code_review-0.2.6 → argus_code_review-0.2.7}/.gitignore +0 -0
  7. {argus_code_review-0.2.6 → argus_code_review-0.2.7}/LICENSE +0 -0
  8. {argus_code_review-0.2.6 → argus_code_review-0.2.7}/README.md +0 -0
  9. {argus_code_review-0.2.6 → argus_code_review-0.2.7}/argus/__init__.py +0 -0
  10. {argus_code_review-0.2.6 → argus_code_review-0.2.7}/argus/bench.py +0 -0
  11. {argus_code_review-0.2.6 → argus_code_review-0.2.7}/argus/bench_default.toml +0 -0
  12. {argus_code_review-0.2.6 → argus_code_review-0.2.7}/argus/cli.py +0 -0
  13. {argus_code_review-0.2.6 → argus_code_review-0.2.7}/argus/config.py +0 -0
  14. {argus_code_review-0.2.6 → argus_code_review-0.2.7}/argus/coverage.py +0 -0
  15. {argus_code_review-0.2.6 → argus_code_review-0.2.7}/argus/dotenv_utils.py +0 -0
  16. {argus_code_review-0.2.6 → argus_code_review-0.2.7}/argus/gemini_cache.py +0 -0
  17. {argus_code_review-0.2.6 → argus_code_review-0.2.7}/argus/gemini_runner.py +0 -0
  18. {argus_code_review-0.2.6 → argus_code_review-0.2.7}/argus/github_client.py +0 -0
  19. {argus_code_review-0.2.6 → argus_code_review-0.2.7}/argus/helpers.py +0 -0
  20. {argus_code_review-0.2.6 → argus_code_review-0.2.7}/argus/llm/models.py +0 -0
  21. {argus_code_review-0.2.6 → argus_code_review-0.2.7}/argus/llm/output_models.py +0 -0
  22. {argus_code_review-0.2.6 → argus_code_review-0.2.7}/argus/llm/pricing.py +0 -0
  23. {argus_code_review-0.2.6 → argus_code_review-0.2.7}/argus/llm/usage.py +0 -0
  24. {argus_code_review-0.2.6 → argus_code_review-0.2.7}/argus/models.py +0 -0
  25. {argus_code_review-0.2.6 → argus_code_review-0.2.7}/argus/openai_client.py +0 -0
  26. {argus_code_review-0.2.6 → argus_code_review-0.2.7}/argus/openai_runner.py +0 -0
  27. {argus_code_review-0.2.6 → argus_code_review-0.2.7}/argus/pipeline_models.py +0 -0
  28. {argus_code_review-0.2.6 → argus_code_review-0.2.7}/argus/precheck/__init__.py +0 -0
  29. {argus_code_review-0.2.6 → argus_code_review-0.2.7}/argus/precheck/actions_scanner.py +0 -0
  30. {argus_code_review-0.2.6 → argus_code_review-0.2.7}/argus/precheck/engine.py +0 -0
  31. {argus_code_review-0.2.6 → argus_code_review-0.2.7}/argus/precheck/eslint_bundle/.gitignore +0 -0
  32. {argus_code_review-0.2.6 → argus_code_review-0.2.7}/argus/precheck/eslint_bundle/README.md +0 -0
  33. {argus_code_review-0.2.6 → argus_code_review-0.2.7}/argus/precheck/eslint_bundle/eslint.config.js +0 -0
  34. {argus_code_review-0.2.6 → argus_code_review-0.2.7}/argus/precheck/eslint_bundle/package-lock.json +0 -0
  35. {argus_code_review-0.2.6 → argus_code_review-0.2.7}/argus/precheck/eslint_bundle/package.json +0 -0
  36. {argus_code_review-0.2.6 → argus_code_review-0.2.7}/argus/precheck/js_scanner.py +0 -0
  37. {argus_code_review-0.2.6 → argus_code_review-0.2.7}/argus/precheck/migration_scanner.py +0 -0
  38. {argus_code_review-0.2.6 → argus_code_review-0.2.7}/argus/precheck/rules/README.md +0 -0
  39. {argus_code_review-0.2.6 → argus_code_review-0.2.7}/argus/precheck/sarif.py +0 -0
  40. {argus_code_review-0.2.6 → argus_code_review-0.2.7}/argus/precheck/scanner_utils.py +0 -0
  41. {argus_code_review-0.2.6 → argus_code_review-0.2.7}/argus/precheck/secrets_scanner.py +0 -0
  42. {argus_code_review-0.2.6 → argus_code_review-0.2.7}/argus/precheck/shadow.py +0 -0
  43. {argus_code_review-0.2.6 → argus_code_review-0.2.7}/argus/precheck/terraform_scanner.py +0 -0
  44. {argus_code_review-0.2.6 → argus_code_review-0.2.7}/argus/precheck/workflow_lint_scanner.py +0 -0
  45. {argus_code_review-0.2.6 → argus_code_review-0.2.7}/argus/prompts/__init__.py +0 -0
  46. {argus_code_review-0.2.6 → argus_code_review-0.2.7}/argus/prompts/pr-review-blocking-validator.md +0 -0
  47. {argus_code_review-0.2.6 → argus_code_review-0.2.7}/argus/prompts/pr-review-coverage-check.md +0 -0
  48. {argus_code_review-0.2.6 → argus_code_review-0.2.7}/argus/prompts/pr-review-cross-cutting.md +0 -0
  49. {argus_code_review-0.2.6 → argus_code_review-0.2.7}/argus/prompts/pr-review-feedback-verifier.md +0 -0
  50. {argus_code_review-0.2.6 → argus_code_review-0.2.7}/argus/prompts/pr-review-lite.md +0 -0
  51. {argus_code_review-0.2.6 → argus_code_review-0.2.7}/argus/prompts/pr-review-planner.md +0 -0
  52. {argus_code_review-0.2.6 → argus_code_review-0.2.7}/argus/prompts/pr-review-preflight-router.md +0 -0
  53. {argus_code_review-0.2.6 → argus_code_review-0.2.7}/argus/prompts/pr-review-prior-art.md +0 -0
  54. {argus_code_review-0.2.6 → argus_code_review-0.2.7}/argus/prompts/pr-review-specialist-deployment.md +0 -0
  55. {argus_code_review-0.2.6 → argus_code_review-0.2.7}/argus/prompts/pr-review-specialist-frontend.md +0 -0
  56. {argus_code_review-0.2.6 → argus_code_review-0.2.7}/argus/prompts/pr-review-specialist-infra.md +0 -0
  57. {argus_code_review-0.2.6 → argus_code_review-0.2.7}/argus/prompts/pr-review-specialist-llm-patterns.md +0 -0
  58. {argus_code_review-0.2.6 → argus_code_review-0.2.7}/argus/prompts/pr-review-specialist-observability.md +0 -0
  59. {argus_code_review-0.2.6 → argus_code_review-0.2.7}/argus/prompts/pr-review-specialist-orchestration.md +0 -0
  60. {argus_code_review-0.2.6 → argus_code_review-0.2.7}/argus/prompts/pr-review-specialist-security.md +0 -0
  61. {argus_code_review-0.2.6 → argus_code_review-0.2.7}/argus/prompts/pr-review-specialist-slackbot.md +0 -0
  62. {argus_code_review-0.2.6 → argus_code_review-0.2.7}/argus/prompts/pr-review-specialist-sql.md +0 -0
  63. {argus_code_review-0.2.6 → argus_code_review-0.2.7}/argus/prompts/pr-review-subagent.md +0 -0
  64. {argus_code_review-0.2.6 → argus_code_review-0.2.7}/argus/prompts/pr-review-tests-and-docs.md +0 -0
  65. {argus_code_review-0.2.6 → argus_code_review-0.2.7}/argus/prompts/pr-review-writer.md +0 -0
  66. {argus_code_review-0.2.6 → argus_code_review-0.2.7}/argus/prompts_runtime.py +0 -0
  67. {argus_code_review-0.2.6 → argus_code_review-0.2.7}/argus/repo_provision.py +0 -0
  68. {argus_code_review-0.2.6 → argus_code_review-0.2.7}/argus/review_tools.py +0 -0
  69. {argus_code_review-0.2.6 → argus_code_review-0.2.7}/argus/runners.py +0 -0
  70. {argus_code_review-0.2.6 → argus_code_review-0.2.7}/argus/storage/__init__.py +0 -0
  71. {argus_code_review-0.2.6 → argus_code_review-0.2.7}/argus/storage/http.py +0 -0
  72. {argus_code_review-0.2.6 → argus_code_review-0.2.7}/argus/storage/models.py +0 -0
  73. {argus_code_review-0.2.6 → argus_code_review-0.2.7}/argus/storage/precheck.py +0 -0
  74. {argus_code_review-0.2.6 → argus_code_review-0.2.7}/argus/storage/resolver.py +0 -0
  75. {argus_code_review-0.2.6 → argus_code_review-0.2.7}/argus/storage/session.py +0 -0
  76. {argus_code_review-0.2.6 → argus_code_review-0.2.7}/argus/storage/sql.py +0 -0
  77. {argus_code_review-0.2.6 → argus_code_review-0.2.7}/argus/storage/sqlite.py +0 -0
  78. {argus_code_review-0.2.6 → argus_code_review-0.2.7}/pyproject.toml +0 -0
  79. {argus_code_review-0.2.6 → argus_code_review-0.2.7}/schema/008_add_code_reviews.sql +0 -0
  80. {argus_code_review-0.2.6 → argus_code_review-0.2.7}/schema/009_add_reviewer_version.sql +0 -0
  81. {argus_code_review-0.2.6 → argus_code_review-0.2.7}/schema/010_add_review_patterns.sql +0 -0
  82. {argus_code_review-0.2.6 → argus_code_review-0.2.7}/schema/011_add_review_progress_columns.sql +0 -0
  83. {argus_code_review-0.2.6 → argus_code_review-0.2.7}/schema/015_create_agent_runs.sql +0 -0
  84. {argus_code_review-0.2.6 → argus_code_review-0.2.7}/schema/016_add_agent_runs_failure_reason.sql +0 -0
  85. {argus_code_review-0.2.6 → argus_code_review-0.2.7}/schema/017_add_precheck_rules.sql +0 -0
  86. {argus_code_review-0.2.6 → argus_code_review-0.2.7}/schema/018_widen_agent_runs_failure_reason.sql +0 -0
  87. {argus_code_review-0.2.6 → argus_code_review-0.2.7}/tests/__init__.py +0 -0
  88. {argus_code_review-0.2.6 → argus_code_review-0.2.7}/tests/conftest.py +0 -0
  89. {argus_code_review-0.2.6 → argus_code_review-0.2.7}/tests/golden/review_response.schema.json +0 -0
  90. {argus_code_review-0.2.6 → argus_code_review-0.2.7}/tests/storage/__init__.py +0 -0
  91. {argus_code_review-0.2.6 → argus_code_review-0.2.7}/tests/storage/test_models.py +0 -0
  92. {argus_code_review-0.2.6 → argus_code_review-0.2.7}/tests/storage/test_resolver.py +0 -0
  93. {argus_code_review-0.2.6 → argus_code_review-0.2.7}/tests/storage/test_session.py +0 -0
  94. {argus_code_review-0.2.6 → argus_code_review-0.2.7}/tests/storage/test_sql.py +0 -0
  95. {argus_code_review-0.2.6 → argus_code_review-0.2.7}/tests/storage/test_sqlite_backend.py +0 -0
  96. {argus_code_review-0.2.6 → argus_code_review-0.2.7}/tests/test_actions_scanner.py +0 -0
  97. {argus_code_review-0.2.6 → argus_code_review-0.2.7}/tests/test_argus_review_local.py +0 -0
  98. {argus_code_review-0.2.6 → argus_code_review-0.2.7}/tests/test_bench.py +0 -0
  99. {argus_code_review-0.2.6 → argus_code_review-0.2.7}/tests/test_bench_config_guard.py +0 -0
  100. {argus_code_review-0.2.6 → argus_code_review-0.2.7}/tests/test_catchup_gate.py +0 -0
  101. {argus_code_review-0.2.6 → argus_code_review-0.2.7}/tests/test_cli_args.py +0 -0
  102. {argus_code_review-0.2.6 → argus_code_review-0.2.7}/tests/test_cli_output_contract.py +0 -0
  103. {argus_code_review-0.2.6 → argus_code_review-0.2.7}/tests/test_cli_post_review.py +0 -0
  104. {argus_code_review-0.2.6 → argus_code_review-0.2.7}/tests/test_cli_preflight.py +0 -0
  105. {argus_code_review-0.2.6 → argus_code_review-0.2.7}/tests/test_cli_prompts.py +0 -0
  106. {argus_code_review-0.2.6 → argus_code_review-0.2.7}/tests/test_config.py +0 -0
  107. {argus_code_review-0.2.6 → argus_code_review-0.2.7}/tests/test_conftest.py +0 -0
  108. {argus_code_review-0.2.6 → argus_code_review-0.2.7}/tests/test_gemini_cache.py +0 -0
  109. {argus_code_review-0.2.6 → argus_code_review-0.2.7}/tests/test_gemini_runner.py +0 -0
  110. {argus_code_review-0.2.6 → argus_code_review-0.2.7}/tests/test_github_client_checks_signal.py +0 -0
  111. {argus_code_review-0.2.6 → argus_code_review-0.2.7}/tests/test_github_client_write.py +0 -0
  112. {argus_code_review-0.2.6 → argus_code_review-0.2.7}/tests/test_graph_fetch_diff.py +0 -0
  113. {argus_code_review-0.2.6 → argus_code_review-0.2.7}/tests/test_graph_get_llm_temperature.py +0 -0
  114. {argus_code_review-0.2.6 → argus_code_review-0.2.7}/tests/test_graph_http_guards.py +0 -0
  115. {argus_code_review-0.2.6 → argus_code_review-0.2.7}/tests/test_graph_precheck.py +0 -0
  116. {argus_code_review-0.2.6 → argus_code_review-0.2.7}/tests/test_graph_preflight_image_bump.py +0 -0
  117. {argus_code_review-0.2.6 → argus_code_review-0.2.7}/tests/test_graph_progress.py +0 -0
  118. {argus_code_review-0.2.6 → argus_code_review-0.2.7}/tests/test_graph_storage_resolution.py +0 -0
  119. {argus_code_review-0.2.6 → argus_code_review-0.2.7}/tests/test_graph_timeout_surfacing.py +0 -0
  120. {argus_code_review-0.2.6 → argus_code_review-0.2.7}/tests/test_js_scanner.py +0 -0
  121. {argus_code_review-0.2.6 → argus_code_review-0.2.7}/tests/test_llm_models.py +0 -0
  122. {argus_code_review-0.2.6 → argus_code_review-0.2.7}/tests/test_llm_models_override.py +0 -0
  123. {argus_code_review-0.2.6 → argus_code_review-0.2.7}/tests/test_llm_pricing.py +0 -0
  124. {argus_code_review-0.2.6 → argus_code_review-0.2.7}/tests/test_migration_scanner.py +0 -0
  125. {argus_code_review-0.2.6 → argus_code_review-0.2.7}/tests/test_models.py +0 -0
  126. {argus_code_review-0.2.6 → argus_code_review-0.2.7}/tests/test_multi_round.py +0 -0
  127. {argus_code_review-0.2.6 → argus_code_review-0.2.7}/tests/test_openai_client.py +0 -0
  128. {argus_code_review-0.2.6 → argus_code_review-0.2.7}/tests/test_openai_runner.py +0 -0
  129. {argus_code_review-0.2.6 → argus_code_review-0.2.7}/tests/test_output_models.py +0 -0
  130. {argus_code_review-0.2.6 → argus_code_review-0.2.7}/tests/test_packaging.py +0 -0
  131. {argus_code_review-0.2.6 → argus_code_review-0.2.7}/tests/test_plan_review.py +0 -0
  132. {argus_code_review-0.2.6 → argus_code_review-0.2.7}/tests/test_precheck_engine.py +0 -0
  133. {argus_code_review-0.2.6 → argus_code_review-0.2.7}/tests/test_precheck_engine_integration.py +0 -0
  134. {argus_code_review-0.2.6 → argus_code_review-0.2.7}/tests/test_precheck_sarif.py +0 -0
  135. {argus_code_review-0.2.6 → argus_code_review-0.2.7}/tests/test_precheck_shadow.py +0 -0
  136. {argus_code_review-0.2.6 → argus_code_review-0.2.7}/tests/test_precheck_shadow_integration.py +0 -0
  137. {argus_code_review-0.2.6 → argus_code_review-0.2.7}/tests/test_prompts.py +0 -0
  138. {argus_code_review-0.2.6 → argus_code_review-0.2.7}/tests/test_repo_provision.py +0 -0
  139. {argus_code_review-0.2.6 → argus_code_review-0.2.7}/tests/test_review_patterns_integration.py +0 -0
  140. {argus_code_review-0.2.6 → argus_code_review-0.2.7}/tests/test_review_tools.py +0 -0
  141. {argus_code_review-0.2.6 → argus_code_review-0.2.7}/tests/test_runners_context7.py +0 -0
  142. {argus_code_review-0.2.6 → argus_code_review-0.2.7}/tests/test_runners_context_usage.py +0 -0
  143. {argus_code_review-0.2.6 → argus_code_review-0.2.7}/tests/test_runners_helpers.py +0 -0
  144. {argus_code_review-0.2.6 → argus_code_review-0.2.7}/tests/test_runners_new.py +0 -0
  145. {argus_code_review-0.2.6 → argus_code_review-0.2.7}/tests/test_scanner_utils.py +0 -0
  146. {argus_code_review-0.2.6 → argus_code_review-0.2.7}/tests/test_secrets_scanner.py +0 -0
  147. {argus_code_review-0.2.6 → argus_code_review-0.2.7}/tests/test_specialist_validation.py +0 -0
  148. {argus_code_review-0.2.6 → argus_code_review-0.2.7}/tests/test_stage_costs.py +0 -0
  149. {argus_code_review-0.2.6 → argus_code_review-0.2.7}/tests/test_storage_precheck.py +0 -0
  150. {argus_code_review-0.2.6 → argus_code_review-0.2.7}/tests/test_terraform_scanner.py +0 -0
  151. {argus_code_review-0.2.6 → argus_code_review-0.2.7}/tests/test_terraform_scanner_integration.py +0 -0
  152. {argus_code_review-0.2.6 → argus_code_review-0.2.7}/tests/test_workflow_lint_scanner.py +0 -0
@@ -7,6 +7,24 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.2.7] - 2026-09-26
11
+
12
+ ### Fixed
13
+
14
+ - Reworded a misleading log message in `argus/graph.py`'s `_node_lite_review`
15
+ (TECH-6878, #29). The log previously stated "Lite round history skipped on HTTP
16
+ storage path", which was misinterpreted as indicating that review persistence
17
+ was skipped altogether and caused an operator/agent to file a false bug
18
+ report. The message now clarifies that only the comment markdown section for
19
+ prior lite history is omitted (because `select_recent_lite_rounds` is not
20
+ implemented in the HTTP storage shim), while the round's own finalize write is
21
+ not skipped and is attempted normally.
22
+ - Added test coverage for lite-round persistence round-tripping through both the
23
+ SQLite and HTTP storage backends (previously untested for HTTP), verifying that
24
+ `reviewer_version="v3-lite"` is preserved on write and read (TECH-6878, #29).
25
+ Also marked `test_insert_agent_runs_batch[http]` as an explicit xfail for the
26
+ documented HTTP shim analytics gap rather than letting it pass vacuously.
27
+
10
28
  ## [0.2.6] - 2026-09-19
11
29
 
12
30
  ### Fixed
@@ -338,7 +356,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
338
356
  packaged set.
339
357
  - `argus --version`, `argus prompts list`, and `argus prompts export`.
340
358
 
341
- [Unreleased]: https://github.com/redesignhealth/argus-review/compare/v0.2.6...HEAD
359
+ [Unreleased]: https://github.com/redesignhealth/argus-review/compare/v0.2.7...HEAD
360
+ [0.2.7]: https://github.com/redesignhealth/argus-review/compare/v0.2.6...v0.2.7
342
361
  [0.2.6]: https://github.com/redesignhealth/argus-review/compare/v0.2.5...v0.2.6
343
362
  [0.2.5]: https://github.com/redesignhealth/argus-review/compare/v0.2.4...v0.2.5
344
363
  [0.2.4]: https://github.com/redesignhealth/argus-review/compare/v0.2.3...v0.2.4
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: argus-code-review
3
- Version: 0.2.6
3
+ Version: 0.2.7
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
@@ -2228,7 +2228,11 @@ async def _node_lite_review(state: ReviewState) -> dict[str, Any]:
2228
2228
  except Exception: # noqa: BLE001
2229
2229
  logger.error("Failed to build lite round history — skipping section", exc_info=True)
2230
2230
  elif req.pr_number and _lite_history_backend_kind == "http":
2231
- logger.info("Lite round history skipped on HTTP storage path")
2231
+ logger.info(
2232
+ "Lite round history markdown section omitted on HTTP storage path "
2233
+ "(select_recent_lite_rounds not implemented in HTTP shim; "
2234
+ "the round's own finalize write is not skipped and will be attempted normally)"
2235
+ )
2232
2236
 
2233
2237
  response.review_comment = (
2234
2238
  f"## Code Review — Round {round_num} (Lite Mode)\n\n"
@@ -3,22 +3,20 @@
3
3
  Every scenario in this file exercises the seven logical operations defined
4
4
  by ``argus/storage/sql.py`` (the Postgres canonical writer) purely through
5
5
  their Pydantic input/output shapes — no backend-specific assertions. The
6
- goal is a single suite that can run, unmodified, against any backend that
7
- implements the same operations: today that's just
8
- :class:`argus.storage.sqlite.SqliteHistoryBackend` (wired via the
9
- ``backend`` fixture below); ``postgres`` and ``http`` params are declared
10
- already (each ``pytest.mark.skip``ped with the reason it isn't wired yet)
11
- so that adding those fixtures later is a matter of filling in the
12
- ``elif`` branch in ``backend()`` — no test bodies change.
13
-
6
+ goal is a single suite that runs against any backend implementing the same
7
+ operations:
8
+
9
+ - **sqlite**: fully supported locally (:class:`argus.storage.sqlite.SqliteHistoryBackend`).
10
+ - **http**: wired via ``pytest-httpx`` standing in for the two-endpoint HTTP
11
+ storage contract (see ``docs/STORAGE.md``). Operations not implemented by
12
+ the minimal HTTP shim (e.g. status polling, recent-rounds listing,
13
+ select_recent_lite_rounds, insert_agent_runs) are documented and marked xfail.
14
+ Completed-round persistence and latest-round reads (including for lite-mode
15
+ reviews) are fully supported and verified.
14
16
  - **postgres**: needs a live database and ``argus.storage.session`` wired
15
- up. Guard with ``ARGUS_DB_URL`` / ``SUPABASE_DB_URL`` env-var presence and
16
- the ``integration`` marker once wired, mirroring the pattern in
17
+ up. Guarded with ``ARGUS_DB_URL`` / ``SUPABASE_DB_URL`` env-var presence and
18
+ the ``integration`` marker, mirroring the pattern in
17
19
  ``tests/storage/test_sql.py``.
18
- - **http**: needs a ``pytest-httpx`` fixture standing in for your configured
19
- HTTP backend's endpoints (see ``docs/STORAGE.md`` for the contract). Once
20
- wired, ``HttpStorageClient`` would sit behind the same seven-operation
21
- surface as a thin adapter.
22
20
 
23
21
  Scenario coverage:
24
22
 
@@ -103,6 +101,9 @@ _HTTP_UNSUPPORTED_TESTS = {
103
101
  # select_recent_rounds -- not exposed over HTTP.
104
102
  "test_select_recent_rounds_orders_desc_and_respects_limit",
105
103
  "test_repeated_running_upsert_same_flow_run_id_does_not_duplicate",
104
+ # insert_agent_runs -- documented no-op on HTTP path (analytics inserts
105
+ # not covered by minimal HTTP shim, TECH-3487 follow-up).
106
+ "test_insert_agent_runs_batch",
106
107
  # select_recent_lite_rounds -- documented no-op (always returns []).
107
108
  "test_select_recent_lite_rounds_filters_reviewer_version",
108
109
  "test_select_recent_lite_rounds_default_limit_is_200",
@@ -112,6 +113,14 @@ _HTTP_UNSUPPORTED_TESTS = {
112
113
  def _mark_known_http_gaps(request: pytest.FixtureRequest) -> None:
113
114
  base_name = request.node.name.split("[")[0]
114
115
  if base_name in _HTTP_UNSUPPORTED_TESTS:
116
+ # insert_agent_runs is a documented no-op on the HTTP shim (analytics
117
+ # inserts not covered, TECH-3487 follow-up); the call does not assert
118
+ # rows or raise, so xfail directly rather than letting it pass vacuously.
119
+ if base_name == "test_insert_agent_runs_batch":
120
+ pytest.xfail(
121
+ "HTTP storage shim does not support agent_runs analytics inserts "
122
+ "(documented Phase-1 gap, TECH-3487)"
123
+ )
115
124
  request.node.add_marker(
116
125
  pytest.mark.xfail(
117
126
  reason=(
@@ -202,10 +211,10 @@ async def backend(
202
211
  return
203
212
 
204
213
  if backend_kind == "http":
214
+ _mark_known_http_gaps(request)
205
215
  httpx_mock = request.getfixturevalue("httpx_mock")
206
216
  httpx_mock.add_callback(_FakeHttpStorageBackend().handle, is_reusable=True)
207
217
  install_http_storage(read_url=_HTTP_READ_URL, write_url=_HTTP_WRITE_URL)
208
- _mark_known_http_gaps(request)
209
218
  try:
210
219
  yield HttpHistoryBackend()
211
220
  finally:
@@ -317,6 +326,79 @@ async def test_upsert_completed_row_defaults_reviewer_version_and_stage(
317
326
  assert written.verdict is None
318
327
 
319
328
 
329
+ async def test_lite_round_completed_upsert_round_trips_through_latest(
330
+ backend: HistoryBackend,
331
+ ) -> None:
332
+ """Lite-mode rounds (reviewer_version='v3-lite') must persist via
333
+ upsert_completed_row and round-trip through select_latest_completed_round
334
+ on all backends (including the HTTP storage shim).
335
+ """
336
+ payload = CodeReviewRoundIn(
337
+ flow_run_id="fr-lite-rt-1",
338
+ repo="org/repo-lite",
339
+ pr_number=10,
340
+ verdict="approve",
341
+ risk_level="low",
342
+ blocking_count=0,
343
+ suggestion_count=0,
344
+ review_comment="lgtm lite review",
345
+ reviewer_version="v3-lite",
346
+ result_json={"review_round": 2, "preflight_reason": "test-only diff"},
347
+ sha="deadbeef123",
348
+ base_ref="main",
349
+ )
350
+
351
+ written = await backend.upsert_completed_row(row=payload)
352
+ assert written.reviewer_version == "v3-lite"
353
+
354
+ latest = await backend.select_latest_completed_round(repo="org/repo-lite", pr_number=10)
355
+ assert latest is not None
356
+ assert latest.id == written.id
357
+ assert latest.reviewer_version == "v3-lite"
358
+ assert latest.verdict == "approve"
359
+ assert latest.sha == "deadbeef123"
360
+ assert latest.result_json == {"review_round": 2, "preflight_reason": "test-only diff"}
361
+ assert latest.prior_count == 1
362
+
363
+
364
+ async def test_lite_round_becomes_latest_completed_round(
365
+ backend: HistoryBackend,
366
+ ) -> None:
367
+ """When a lite review follows a full review on the same PR, the lite
368
+ round becomes the latest completed round and preserves its version.
369
+ """
370
+ first_full = await backend.upsert_completed_row(
371
+ row=CodeReviewRoundIn(
372
+ flow_run_id="fr-seq-1",
373
+ repo="org/repo-seq",
374
+ pr_number=20,
375
+ verdict="block",
376
+ reviewer_version="v3",
377
+ sha="sha-round-1",
378
+ )
379
+ )
380
+ second_lite = await backend.upsert_completed_row(
381
+ row=CodeReviewRoundIn(
382
+ flow_run_id="fr-seq-2",
383
+ repo="org/repo-seq",
384
+ pr_number=20,
385
+ verdict="approve",
386
+ reviewer_version="v3-lite",
387
+ sha="sha-round-2",
388
+ result_json={"review_round": 2, "preflight_reason": "catchup on green CI"},
389
+ )
390
+ )
391
+
392
+ latest = await backend.select_latest_completed_round(repo="org/repo-seq", pr_number=20)
393
+ assert latest is not None
394
+ assert latest.id == second_lite.id
395
+ assert latest.id != first_full.id
396
+ assert latest.reviewer_version == "v3-lite"
397
+ assert latest.verdict == "approve"
398
+ assert latest.sha == "sha-round-2"
399
+ assert latest.prior_count == 2
400
+
401
+
320
402
  # ---------------------------------------------------------------------------
321
403
  # 3. Two completed rounds -> latest wins; select_recent_rounds ordering + limit
322
404
  # ---------------------------------------------------------------------------
@@ -471,7 +553,7 @@ async def test_completed_upsert_same_flow_run_id_preserves_sha_via_coalesce(
471
553
  # ---------------------------------------------------------------------------
472
554
 
473
555
 
474
- async def test_insert_agent_runs_batch(backend: SqliteHistoryBackend) -> None:
556
+ async def test_insert_agent_runs_batch(backend: HistoryBackend) -> None:
475
557
  review = await backend.upsert_completed_row(
476
558
  row=CodeReviewRoundIn(repo="org/repo6", pr_number=2, verdict="approve")
477
559
  )
@@ -11,6 +11,7 @@ Tests use ``pytest-httpx`` to mock the backend side; no real network.
11
11
 
12
12
  from __future__ import annotations
13
13
 
14
+ import json
14
15
  from datetime import UTC, datetime
15
16
  from uuid import uuid4
16
17
 
@@ -249,3 +250,70 @@ def test_install_raises_on_conflicting_reinstall() -> None:
249
250
  install_http_storage(read_url=_READ_URL, write_url=_WRITE_URL, auth="ApiKey k")
250
251
  with pytest.raises(HttpStorageError, match="different parameters"):
251
252
  install_http_storage(read_url=_READ_URL, write_url=_WRITE_URL, auth="ApiKey DIFFERENT")
253
+
254
+
255
+ @pytest.mark.asyncio
256
+ async def test_write_and_read_lite_round_round_trip(httpx_mock: HTTPXMock) -> None:
257
+ """A lite-mode round (reviewer_version="v3-lite") written via
258
+ HttpStorageClient.write_round round-trips through
259
+ read_latest_completed_round with reviewer_version preserved.
260
+ """
261
+ written_id = uuid4()
262
+ round_payload = _row_payload(
263
+ id=str(written_id),
264
+ reviewer_version="v3-lite",
265
+ verdict="APPROVE",
266
+ sha="131e334046bf",
267
+ result_json={"review_round": 8, "preflight_reason": "test-only diff"},
268
+ )
269
+ httpx_mock.add_response(
270
+ method="POST",
271
+ url="https://fake-storage-backend.test/api/v1/code-review/storage/reviews/acme/example-repo/158/rounds",
272
+ status_code=201,
273
+ json=round_payload,
274
+ )
275
+ httpx_mock.add_response(
276
+ method="GET",
277
+ url="https://fake-storage-backend.test/api/v1/code-review/storage/reviews/acme/example-repo/158",
278
+ status_code=200,
279
+ json={"rounds": [round_payload]},
280
+ )
281
+
282
+ client = HttpStorageClient(read_url=_READ_URL, write_url=_WRITE_URL, auth="ApiKey test")
283
+ try:
284
+ round_data = CodeReviewRound(
285
+ repo="acme/example-repo",
286
+ pr_number=158,
287
+ verdict="APPROVE",
288
+ risk_level="LOW",
289
+ blocking_count=0,
290
+ suggestion_count=0,
291
+ reviewer_version="v3-lite",
292
+ sha="131e334046bf",
293
+ result_json={"review_round": 8, "preflight_reason": "test-only diff"},
294
+ )
295
+ written = await client.write_round(
296
+ owner="acme", repo="example-repo", pr=158, round_data=round_data
297
+ )
298
+ assert written.id == written_id
299
+ assert written.reviewer_version == "v3-lite"
300
+
301
+ # Verify POST payload sent the reviewer_version
302
+ post_request = httpx_mock.get_requests()[0]
303
+ assert post_request.method == "POST"
304
+ posted_body = json.loads(post_request.content)
305
+ assert posted_body["reviewer_version"] == "v3-lite"
306
+ assert posted_body["sha"] == "131e334046bf"
307
+
308
+ latest, count = await client.read_latest_completed_round(
309
+ owner="acme", repo="example-repo", pr=158
310
+ )
311
+ assert latest is not None
312
+ assert latest.id == written_id
313
+ assert latest.reviewer_version == "v3-lite"
314
+ assert latest.verdict == "APPROVE"
315
+ assert latest.sha == "131e334046bf"
316
+ assert latest.result_json == {"review_round": 8, "preflight_reason": "test-only diff"}
317
+ assert count == 1
318
+ finally:
319
+ await client.aclose()