open-code-review-toolkit 0.11.0__tar.gz → 0.11.2__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 (135) hide show
  1. {open_code_review_toolkit-0.11.0 → open_code_review_toolkit-0.11.2}/PKG-INFO +2 -1
  2. {open_code_review_toolkit-0.11.0 → open_code_review_toolkit-0.11.2}/pyproject.toml +3 -1
  3. {open_code_review_toolkit-0.11.0 → open_code_review_toolkit-0.11.2}/src/ocr_toolkit/_version.py +2 -2
  4. {open_code_review_toolkit-0.11.0 → open_code_review_toolkit-0.11.2}/src/ocr_toolkit/cli.py +2 -1
  5. {open_code_review_toolkit-0.11.0 → open_code_review_toolkit-0.11.2}/src/ocr_toolkit/ocr_result.py +1 -0
  6. {open_code_review_toolkit-0.11.0 → open_code_review_toolkit-0.11.2}/src/ocr_toolkit/posting/workflow.py +6 -0
  7. {open_code_review_toolkit-0.11.0 → open_code_review_toolkit-0.11.2}/src/ocr_toolkit/preflight.py +10 -2
  8. {open_code_review_toolkit-0.11.0 → open_code_review_toolkit-0.11.2}/src/ocr_toolkit/review_runner.py +107 -19
  9. {open_code_review_toolkit-0.11.0 → open_code_review_toolkit-0.11.2}/.gitignore +0 -0
  10. {open_code_review_toolkit-0.11.0 → open_code_review_toolkit-0.11.2}/LICENSE +0 -0
  11. {open_code_review_toolkit-0.11.0 → open_code_review_toolkit-0.11.2}/README.md +0 -0
  12. {open_code_review_toolkit-0.11.0 → open_code_review_toolkit-0.11.2}/src/ocr_toolkit/__init__.py +0 -0
  13. {open_code_review_toolkit-0.11.0 → open_code_review_toolkit-0.11.2}/src/ocr_toolkit/common/__init__.py +0 -0
  14. {open_code_review_toolkit-0.11.0 → open_code_review_toolkit-0.11.2}/src/ocr_toolkit/common/filesystem.py +0 -0
  15. {open_code_review_toolkit-0.11.0 → open_code_review_toolkit-0.11.2}/src/ocr_toolkit/common/git.py +0 -0
  16. {open_code_review_toolkit-0.11.0 → open_code_review_toolkit-0.11.2}/src/ocr_toolkit/common/language.py +0 -0
  17. {open_code_review_toolkit-0.11.0 → open_code_review_toolkit-0.11.2}/src/ocr_toolkit/common/markdown.py +0 -0
  18. {open_code_review_toolkit-0.11.0 → open_code_review_toolkit-0.11.2}/src/ocr_toolkit/common/redaction.py +0 -0
  19. {open_code_review_toolkit-0.11.0 → open_code_review_toolkit-0.11.2}/src/ocr_toolkit/config_writer.py +0 -0
  20. {open_code_review_toolkit-0.11.0 → open_code_review_toolkit-0.11.2}/src/ocr_toolkit/configure.py +0 -0
  21. {open_code_review_toolkit-0.11.0 → open_code_review_toolkit-0.11.2}/src/ocr_toolkit/context/__init__.py +0 -0
  22. {open_code_review_toolkit-0.11.0 → open_code_review_toolkit-0.11.2}/src/ocr_toolkit/context/broker.py +0 -0
  23. {open_code_review_toolkit-0.11.0 → open_code_review_toolkit-0.11.2}/src/ocr_toolkit/context/ci_outcomes.py +0 -0
  24. {open_code_review_toolkit-0.11.0 → open_code_review_toolkit-0.11.2}/src/ocr_toolkit/context/contracts.py +0 -0
  25. {open_code_review_toolkit-0.11.0 → open_code_review_toolkit-0.11.2}/src/ocr_toolkit/context/dlp.py +0 -0
  26. {open_code_review_toolkit-0.11.0 → open_code_review_toolkit-0.11.2}/src/ocr_toolkit/context/mcp.py +0 -0
  27. {open_code_review_toolkit-0.11.0 → open_code_review_toolkit-0.11.2}/src/ocr_toolkit/context/policy.py +0 -0
  28. {open_code_review_toolkit-0.11.0 → open_code_review_toolkit-0.11.2}/src/ocr_toolkit/context/recognizers.py +0 -0
  29. {open_code_review_toolkit-0.11.0 → open_code_review_toolkit-0.11.2}/src/ocr_toolkit/context/store.py +0 -0
  30. {open_code_review_toolkit-0.11.0 → open_code_review_toolkit-0.11.2}/src/ocr_toolkit/evidence/__init__.py +0 -0
  31. {open_code_review_toolkit-0.11.0 → open_code_review_toolkit-0.11.2}/src/ocr_toolkit/evidence/__main__.py +0 -0
  32. {open_code_review_toolkit-0.11.0 → open_code_review_toolkit-0.11.2}/src/ocr_toolkit/evidence/actions.py +0 -0
  33. {open_code_review_toolkit-0.11.0 → open_code_review_toolkit-0.11.2}/src/ocr_toolkit/evidence/artifacts.py +0 -0
  34. {open_code_review_toolkit-0.11.0 → open_code_review_toolkit-0.11.2}/src/ocr_toolkit/evidence/categorize.py +0 -0
  35. {open_code_review_toolkit-0.11.0 → open_code_review_toolkit-0.11.2}/src/ocr_toolkit/evidence/collect.py +0 -0
  36. {open_code_review_toolkit-0.11.0 → open_code_review_toolkit-0.11.2}/src/ocr_toolkit/evidence/collectors/__init__.py +0 -0
  37. {open_code_review_toolkit-0.11.0 → open_code_review_toolkit-0.11.2}/src/ocr_toolkit/evidence/collectors/graphs.py +0 -0
  38. {open_code_review_toolkit-0.11.0 → open_code_review_toolkit-0.11.2}/src/ocr_toolkit/evidence/collectors/orchestration.py +0 -0
  39. {open_code_review_toolkit-0.11.0 → open_code_review_toolkit-0.11.2}/src/ocr_toolkit/evidence/collectors/projections.py +0 -0
  40. {open_code_review_toolkit-0.11.0 → open_code_review_toolkit-0.11.2}/src/ocr_toolkit/evidence/collectors/registry.py +0 -0
  41. {open_code_review_toolkit-0.11.0 → open_code_review_toolkit-0.11.2}/src/ocr_toolkit/evidence/collectors/sources.py +0 -0
  42. {open_code_review_toolkit-0.11.0 → open_code_review_toolkit-0.11.2}/src/ocr_toolkit/evidence/coverage.py +0 -0
  43. {open_code_review_toolkit-0.11.0 → open_code_review_toolkit-0.11.2}/src/ocr_toolkit/evidence/ecosystems/__init__.py +0 -0
  44. {open_code_review_toolkit-0.11.0 → open_code_review_toolkit-0.11.2}/src/ocr_toolkit/evidence/ecosystems/ansible/__init__.py +0 -0
  45. {open_code_review_toolkit-0.11.0 → open_code_review_toolkit-0.11.2}/src/ocr_toolkit/evidence/ecosystems/ansible/requirements.py +0 -0
  46. {open_code_review_toolkit-0.11.0 → open_code_review_toolkit-0.11.2}/src/ocr_toolkit/evidence/ecosystems/ansible/topology.py +0 -0
  47. {open_code_review_toolkit-0.11.0 → open_code_review_toolkit-0.11.2}/src/ocr_toolkit/evidence/ecosystems/contracts.py +0 -0
  48. {open_code_review_toolkit-0.11.0 → open_code_review_toolkit-0.11.2}/src/ocr_toolkit/evidence/ecosystems/go.py +0 -0
  49. {open_code_review_toolkit-0.11.0 → open_code_review_toolkit-0.11.2}/src/ocr_toolkit/evidence/ecosystems/javascript.py +0 -0
  50. {open_code_review_toolkit-0.11.0 → open_code_review_toolkit-0.11.2}/src/ocr_toolkit/evidence/ecosystems/php.py +0 -0
  51. {open_code_review_toolkit-0.11.0 → open_code_review_toolkit-0.11.2}/src/ocr_toolkit/evidence/ecosystems/python.py +0 -0
  52. {open_code_review_toolkit-0.11.0 → open_code_review_toolkit-0.11.2}/src/ocr_toolkit/evidence/frameworks/__init__.py +0 -0
  53. {open_code_review_toolkit-0.11.0 → open_code_review_toolkit-0.11.2}/src/ocr_toolkit/evidence/frameworks/contracts.py +0 -0
  54. {open_code_review_toolkit-0.11.0 → open_code_review_toolkit-0.11.2}/src/ocr_toolkit/evidence/frameworks/detection.py +0 -0
  55. {open_code_review_toolkit-0.11.0 → open_code_review_toolkit-0.11.2}/src/ocr_toolkit/evidence/frameworks/providers/__init__.py +0 -0
  56. {open_code_review_toolkit-0.11.0 → open_code_review_toolkit-0.11.2}/src/ocr_toolkit/evidence/frameworks/providers/frontend.py +0 -0
  57. {open_code_review_toolkit-0.11.0 → open_code_review_toolkit-0.11.2}/src/ocr_toolkit/evidence/frameworks/providers/go.py +0 -0
  58. {open_code_review_toolkit-0.11.0 → open_code_review_toolkit-0.11.2}/src/ocr_toolkit/evidence/frameworks/providers/php.py +0 -0
  59. {open_code_review_toolkit-0.11.0 → open_code_review_toolkit-0.11.2}/src/ocr_toolkit/evidence/frameworks/providers/python.py +0 -0
  60. {open_code_review_toolkit-0.11.0 → open_code_review_toolkit-0.11.2}/src/ocr_toolkit/evidence/frameworks/registry.py +0 -0
  61. {open_code_review_toolkit-0.11.0 → open_code_review_toolkit-0.11.2}/src/ocr_toolkit/evidence/frameworks/schema.py +0 -0
  62. {open_code_review_toolkit-0.11.0 → open_code_review_toolkit-0.11.2}/src/ocr_toolkit/evidence/frameworks/templates.py +0 -0
  63. {open_code_review_toolkit-0.11.0 → open_code_review_toolkit-0.11.2}/src/ocr_toolkit/evidence/infrastructure.py +0 -0
  64. {open_code_review_toolkit-0.11.0 → open_code_review_toolkit-0.11.2}/src/ocr_toolkit/evidence/invocation.py +0 -0
  65. {open_code_review_toolkit-0.11.0 → open_code_review_toolkit-0.11.2}/src/ocr_toolkit/evidence/mcp.py +0 -0
  66. {open_code_review_toolkit-0.11.0 → open_code_review_toolkit-0.11.2}/src/ocr_toolkit/evidence/model.py +0 -0
  67. {open_code_review_toolkit-0.11.0 → open_code_review_toolkit-0.11.2}/src/ocr_toolkit/evidence/policy/__init__.py +0 -0
  68. {open_code_review_toolkit-0.11.0 → open_code_review_toolkit-0.11.2}/src/ocr_toolkit/evidence/policy/contracts.py +0 -0
  69. {open_code_review_toolkit-0.11.0 → open_code_review_toolkit-0.11.2}/src/ocr_toolkit/evidence/policy/decisions.py +0 -0
  70. {open_code_review_toolkit-0.11.0 → open_code_review_toolkit-0.11.2}/src/ocr_toolkit/evidence/policy/guidance.py +0 -0
  71. {open_code_review_toolkit-0.11.0 → open_code_review_toolkit-0.11.2}/src/ocr_toolkit/evidence/policy/registry.py +0 -0
  72. {open_code_review_toolkit-0.11.0 → open_code_review_toolkit-0.11.2}/src/ocr_toolkit/evidence/policy/schema.py +0 -0
  73. {open_code_review_toolkit-0.11.0 → open_code_review_toolkit-0.11.2}/src/ocr_toolkit/evidence/policy/scopes.py +0 -0
  74. {open_code_review_toolkit-0.11.0 → open_code_review_toolkit-0.11.2}/src/ocr_toolkit/evidence/project.py +0 -0
  75. {open_code_review_toolkit-0.11.0 → open_code_review_toolkit-0.11.2}/src/ocr_toolkit/evidence/repository.py +0 -0
  76. {open_code_review_toolkit-0.11.0 → open_code_review_toolkit-0.11.2}/src/ocr_toolkit/evidence/review_context.py +0 -0
  77. {open_code_review_toolkit-0.11.0 → open_code_review_toolkit-0.11.2}/src/ocr_toolkit/evidence/store/__init__.py +0 -0
  78. {open_code_review_toolkit-0.11.0 → open_code_review_toolkit-0.11.2}/src/ocr_toolkit/evidence/store/atomic.py +0 -0
  79. {open_code_review_toolkit-0.11.0 → open_code_review_toolkit-0.11.2}/src/ocr_toolkit/evidence/store/contracts.py +0 -0
  80. {open_code_review_toolkit-0.11.0 → open_code_review_toolkit-0.11.2}/src/ocr_toolkit/evidence/store/core.py +0 -0
  81. {open_code_review_toolkit-0.11.0 → open_code_review_toolkit-0.11.2}/src/ocr_toolkit/evidence/store/readback.py +0 -0
  82. {open_code_review_toolkit-0.11.0 → open_code_review_toolkit-0.11.2}/src/ocr_toolkit/evidence/store/values.py +0 -0
  83. {open_code_review_toolkit-0.11.0 → open_code_review_toolkit-0.11.2}/src/ocr_toolkit/federation/__init__.py +0 -0
  84. {open_code_review_toolkit-0.11.0 → open_code_review_toolkit-0.11.2}/src/ocr_toolkit/federation/admission.py +0 -0
  85. {open_code_review_toolkit-0.11.0 → open_code_review_toolkit-0.11.2}/src/ocr_toolkit/federation/contracts.py +0 -0
  86. {open_code_review_toolkit-0.11.0 → open_code_review_toolkit-0.11.2}/src/ocr_toolkit/federation/gateway.py +0 -0
  87. {open_code_review_toolkit-0.11.0 → open_code_review_toolkit-0.11.2}/src/ocr_toolkit/federation/registry.py +0 -0
  88. {open_code_review_toolkit-0.11.0 → open_code_review_toolkit-0.11.2}/src/ocr_toolkit/federation/relay.py +0 -0
  89. {open_code_review_toolkit-0.11.0 → open_code_review_toolkit-0.11.2}/src/ocr_toolkit/federation/runtime.py +0 -0
  90. {open_code_review_toolkit-0.11.0 → open_code_review_toolkit-0.11.2}/src/ocr_toolkit/federation/transport.py +0 -0
  91. {open_code_review_toolkit-0.11.0 → open_code_review_toolkit-0.11.2}/src/ocr_toolkit/mcp_config.py +0 -0
  92. {open_code_review_toolkit-0.11.0 → open_code_review_toolkit-0.11.2}/src/ocr_toolkit/posting/__init__.py +0 -0
  93. {open_code_review_toolkit-0.11.0 → open_code_review_toolkit-0.11.2}/src/ocr_toolkit/posting/__main__.py +0 -0
  94. {open_code_review_toolkit-0.11.0 → open_code_review_toolkit-0.11.2}/src/ocr_toolkit/posting/approval.py +0 -0
  95. {open_code_review_toolkit-0.11.0 → open_code_review_toolkit-0.11.2}/src/ocr_toolkit/posting/comments.py +0 -0
  96. {open_code_review_toolkit-0.11.0 → open_code_review_toolkit-0.11.2}/src/ocr_toolkit/posting/formatting.py +0 -0
  97. {open_code_review_toolkit-0.11.0 → open_code_review_toolkit-0.11.2}/src/ocr_toolkit/posting/gitlab.py +0 -0
  98. {open_code_review_toolkit-0.11.0 → open_code_review_toolkit-0.11.2}/src/ocr_toolkit/posting/gitlab_approval.py +0 -0
  99. {open_code_review_toolkit-0.11.0 → open_code_review_toolkit-0.11.2}/src/ocr_toolkit/posting/markers.py +0 -0
  100. {open_code_review_toolkit-0.11.0 → open_code_review_toolkit-0.11.2}/src/ocr_toolkit/posting/payloads.py +0 -0
  101. {open_code_review_toolkit-0.11.0 → open_code_review_toolkit-0.11.2}/src/ocr_toolkit/posting/reconciliation.py +0 -0
  102. {open_code_review_toolkit-0.11.0 → open_code_review_toolkit-0.11.2}/src/ocr_toolkit/posting/result.py +0 -0
  103. {open_code_review_toolkit-0.11.0 → open_code_review_toolkit-0.11.2}/src/ocr_toolkit/posting/settings.py +0 -0
  104. {open_code_review_toolkit-0.11.0 → open_code_review_toolkit-0.11.2}/src/ocr_toolkit/posting/snapshot.py +0 -0
  105. {open_code_review_toolkit-0.11.0 → open_code_review_toolkit-0.11.2}/src/ocr_toolkit/posting/suggestions.py +0 -0
  106. {open_code_review_toolkit-0.11.0 → open_code_review_toolkit-0.11.2}/src/ocr_toolkit/posting/transaction.py +0 -0
  107. {open_code_review_toolkit-0.11.0 → open_code_review_toolkit-0.11.2}/src/ocr_toolkit/pre_execution.py +0 -0
  108. {open_code_review_toolkit-0.11.0 → open_code_review_toolkit-0.11.2}/src/ocr_toolkit/provider_config.py +0 -0
  109. {open_code_review_toolkit-0.11.0 → open_code_review_toolkit-0.11.2}/src/ocr_toolkit/provider_failure.py +0 -0
  110. {open_code_review_toolkit-0.11.0 → open_code_review_toolkit-0.11.2}/src/ocr_toolkit/providers/__init__.py +0 -0
  111. {open_code_review_toolkit-0.11.0 → open_code_review_toolkit-0.11.2}/src/ocr_toolkit/providers/gitlab.py +0 -0
  112. {open_code_review_toolkit-0.11.0 → open_code_review_toolkit-0.11.2}/src/ocr_toolkit/providers/gitlab_ci.py +0 -0
  113. {open_code_review_toolkit-0.11.0 → open_code_review_toolkit-0.11.2}/src/ocr_toolkit/providers/gitlab_context.py +0 -0
  114. {open_code_review_toolkit-0.11.0 → open_code_review_toolkit-0.11.2}/src/ocr_toolkit/providers/gitlab_discussions.py +0 -0
  115. {open_code_review_toolkit-0.11.0 → open_code_review_toolkit-0.11.2}/src/ocr_toolkit/providers/gitlab_identity.py +0 -0
  116. {open_code_review_toolkit-0.11.0 → open_code_review_toolkit-0.11.2}/src/ocr_toolkit/providers/gitlab_remediation.py +0 -0
  117. {open_code_review_toolkit-0.11.0 → open_code_review_toolkit-0.11.2}/src/ocr_toolkit/providers/local.py +0 -0
  118. {open_code_review_toolkit-0.11.0 → open_code_review_toolkit-0.11.2}/src/ocr_toolkit/providers/local_config.py +0 -0
  119. {open_code_review_toolkit-0.11.0 → open_code_review_toolkit-0.11.2}/src/ocr_toolkit/py.typed +0 -0
  120. {open_code_review_toolkit-0.11.0 → open_code_review_toolkit-0.11.2}/src/ocr_toolkit/reporting/__init__.py +0 -0
  121. {open_code_review_toolkit-0.11.0 → open_code_review_toolkit-0.11.2}/src/ocr_toolkit/reporting/dlp.py +0 -0
  122. {open_code_review_toolkit-0.11.0 → open_code_review_toolkit-0.11.2}/src/ocr_toolkit/reporting/federation.py +0 -0
  123. {open_code_review_toolkit-0.11.0 → open_code_review_toolkit-0.11.2}/src/ocr_toolkit/reporting/metadata.py +0 -0
  124. {open_code_review_toolkit-0.11.0 → open_code_review_toolkit-0.11.2}/src/ocr_toolkit/reporting/model.py +0 -0
  125. {open_code_review_toolkit-0.11.0 → open_code_review_toolkit-0.11.2}/src/ocr_toolkit/reporting/outcome.py +0 -0
  126. {open_code_review_toolkit-0.11.0 → open_code_review_toolkit-0.11.2}/src/ocr_toolkit/reporting/result.py +0 -0
  127. {open_code_review_toolkit-0.11.0 → open_code_review_toolkit-0.11.2}/src/ocr_toolkit/reporting/sections.py +0 -0
  128. {open_code_review_toolkit-0.11.0 → open_code_review_toolkit-0.11.2}/src/ocr_toolkit/reporting/text.py +0 -0
  129. {open_code_review_toolkit-0.11.0 → open_code_review_toolkit-0.11.2}/src/ocr_toolkit/reporting/usage.py +0 -0
  130. {open_code_review_toolkit-0.11.0 → open_code_review_toolkit-0.11.2}/src/ocr_toolkit/result_contract.py +0 -0
  131. {open_code_review_toolkit-0.11.0 → open_code_review_toolkit-0.11.2}/src/ocr_toolkit/result_usage.py +0 -0
  132. {open_code_review_toolkit-0.11.0 → open_code_review_toolkit-0.11.2}/src/ocr_toolkit/review_debug.py +0 -0
  133. {open_code_review_toolkit-0.11.0 → open_code_review_toolkit-0.11.2}/src/ocr_toolkit/review_identity.py +0 -0
  134. {open_code_review_toolkit-0.11.0 → open_code_review_toolkit-0.11.2}/src/ocr_toolkit/review_progress.py +0 -0
  135. {open_code_review_toolkit-0.11.0 → open_code_review_toolkit-0.11.2}/src/ocr_toolkit/review_receipt.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: open-code-review-toolkit
3
- Version: 0.11.0
3
+ Version: 0.11.2
4
4
  Summary: Unofficial GitLab CI integration layer for Open Code Review
5
5
  Project-URL: Homepage, https://github.com/xeonvs/open-code-review-toolkit
6
6
  Project-URL: Repository, https://github.com/xeonvs/open-code-review-toolkit
@@ -228,6 +228,7 @@ Requires-Dist: certifi<2027,>=2026.7.22
228
228
  Requires-Dist: httpx2<3,>=2.13.0
229
229
  Requires-Dist: jsonschema<5,>=4.26.0
230
230
  Requires-Dist: mcp<3,>=2.2.0
231
+ Requires-Dist: pyjwt<3,>=2.15.0
231
232
  Description-Content-Type: text/markdown
232
233
 
233
234
  # Open Code Review Toolkit
@@ -25,6 +25,8 @@ classifiers = [
25
25
  ]
26
26
  dependencies = [
27
27
  "mcp>=2.2.0,<3",
28
+ # MCP crypto accepts older PyJWT; enforce its security floor for resolver installs.
29
+ "PyJWT>=2.15.0,<3",
28
30
  "jsonschema>=4.26.0,<5",
29
31
  "httpx2>=2.13.0,<3",
30
32
  "anyio>=4.10.0,<5",
@@ -52,7 +54,7 @@ dev = [
52
54
  "pytest>=9.1.1",
53
55
  "pytest-cov>=7.1.0",
54
56
  "ruff>=0.16.7",
55
- "setuptools-scm==10.2.3",
57
+ "setuptools-scm==10.3.4",
56
58
  "towncrier>=26.9.0",
57
59
  "twine>=7.0.0",
58
60
  "types-jsonschema>=4.26.0.20260518",
@@ -18,7 +18,7 @@ version_tuple: tuple[int | str, ...]
18
18
  commit_id: str | None
19
19
  __commit_id__: str | None
20
20
 
21
- __version__ = version = '0.11.0'
22
- __version_tuple__ = version_tuple = (0, 11, 0)
21
+ __version__ = version = '0.11.2'
22
+ __version_tuple__ = version_tuple = (0, 11, 2)
23
23
 
24
24
  __commit_id__ = commit_id = None
@@ -94,7 +94,8 @@ def main(argv: Sequence[str] | None = None) -> int:
94
94
  **({"debug_dir": Path(args.debug_dir)} if args.debug_dir is not None else {}),
95
95
  )
96
96
  except review_runner.ReviewRunnerError as exc:
97
- print(f"Cannot run Open Code Review: {exc}", file=sys.stderr)
97
+ if not isinstance(exc, review_runner.ReviewRejected):
98
+ print(f"Cannot run Open Code Review: {exc}", file=sys.stderr)
98
99
  return 2
99
100
  if args.command == "post":
100
101
  return posting_main([args.result, args.stderr])
@@ -19,6 +19,7 @@ from ocr_toolkit.common.redaction import sanitize_ocr_value
19
19
  DEFAULT_MAX_RESULT_BYTES = 2_000_000
20
20
  MAX_RESULT_BYTES_HARD_LIMIT = 20_000_000
21
21
  TOOLKIT_RESULT_KEY = "_ocr_toolkit"
22
+ TOOLKIT_PRIVATE_DIAGNOSTIC_KEY = "_ocr_toolkit_private_diagnostic"
22
23
  TOOLKIT_RESULT_SCHEMA_VERSION = 9
23
24
  SUPPORTED_TOOLKIT_RESULT_SCHEMA_VERSIONS = frozenset({TOOLKIT_RESULT_SCHEMA_VERSION})
24
25
  TOOLKIT_ADVISORY_KEY = "_ocr_toolkit_advisory"
@@ -18,6 +18,7 @@ from ocr_toolkit.common.markdown import markdown_code_block, neutralize_quick_ac
18
18
  from ocr_toolkit.evidence.artifacts import repository_artifacts
19
19
  from ocr_toolkit.ocr_result import (
20
20
  TOOLKIT_ADVISORY_KEY,
21
+ TOOLKIT_PRIVATE_DIAGNOSTIC_KEY,
21
22
  TOOLKIT_RESULT_KEY,
22
23
  OcrResultMalformed,
23
24
  OcrResultMissing,
@@ -666,6 +667,11 @@ def post_results(config: GitLabConfig, result: dict[str, Any]) -> int:
666
667
  print_posting_failure_banner()
667
668
  return 1
668
669
 
670
+ if TOOLKIT_PRIVATE_DIAGNOSTIC_KEY in result:
671
+ return invalid_ocr_schema_exit(
672
+ config, "private diagnostic result is not publication-eligible"
673
+ )
674
+
669
675
  toolkit_metadata = result.get(TOOLKIT_RESULT_KEY)
670
676
  if toolkit_metadata is not None and not toolkit_receipt_is_valid(toolkit_metadata):
671
677
  return invalid_ocr_schema_exit(
@@ -29,7 +29,7 @@ DEFAULT_REQUEST_HEADERS = {
29
29
  "Accept": "application/json",
30
30
  "User-Agent": "open-code-review-ci-preflight/1.0",
31
31
  }
32
- RECOMMENDED_OCR_VERSION = "1.12.7"
32
+ RECOMMENDED_OCR_VERSION = "1.12.11"
33
33
  SUPPORTED_OCR_VERSIONS = (
34
34
  "1.11.0",
35
35
  "1.11.1",
@@ -49,8 +49,16 @@ SUPPORTED_OCR_VERSIONS = (
49
49
  "1.12.5",
50
50
  "1.12.6",
51
51
  "1.12.7",
52
+ "1.12.8",
53
+ "1.12.9",
54
+ "1.12.10",
55
+ "1.12.11",
56
+ )
57
+ DEPRECATED_OCR_VERSIONS = (
58
+ "1.10.0",
59
+ "1.10.1",
60
+ "1.10.2",
52
61
  )
53
- DEPRECATED_OCR_VERSIONS = ("1.10.0", "1.10.1", "1.10.2")
54
62
  # Retain the public constant as the recommended pin, not as the runtime acceptance policy.
55
63
  EXPECTED_OCR_VERSION = RECOMMENDED_OCR_VERSION
56
64
 
@@ -89,6 +89,7 @@ from ocr_toolkit.ocr_result import (
89
89
  MAX_TOOLKIT_MCP_USAGE_COUNT,
90
90
  PUBLIC_REVIEW_TOOL_CALL_NAMES,
91
91
  TOOLKIT_ADVISORY_KEY,
92
+ TOOLKIT_PRIVATE_DIAGNOSTIC_KEY,
92
93
  TOOLKIT_RESULT_KEY,
93
94
  TOOLKIT_RESULT_SCHEMA_VERSION,
94
95
  OcrResultMalformed,
@@ -143,7 +144,7 @@ from ocr_toolkit.reporting.model import (
143
144
  failed_report,
144
145
  report_from_result,
145
146
  )
146
- from ocr_toolkit.result_contract import OcrResultContractError, parse_result_outcome
147
+ from ocr_toolkit.result_contract import OcrResultContractError, ReviewOutcome, parse_result_outcome
147
148
  from ocr_toolkit.result_usage import normalize_token_usage, token_usage_mapping
148
149
  from ocr_toolkit.review_debug import DebugBundle, DebugStatus
149
150
  from ocr_toolkit.review_progress import ReviewProgress, progress_enabled
@@ -210,7 +211,35 @@ MAX_TOOL_FAILURE_LOG_FIELD_CHARS = 500
210
211
 
211
212
 
212
213
  class ReviewRunnerError(Exception):
213
- """The local OCR review process could not be started safely."""
214
+ """OCR execution or result admission failed safely."""
215
+
216
+
217
+ class ReviewRejected(ReviewRunnerError):
218
+ """Carry closed admission facts without guessing the execution cause."""
219
+
220
+ def __init__(self, reason: str, outcome: ReviewOutcome, calls: int | None) -> None:
221
+ self.reason = reason
222
+ self.calls = calls
223
+ self.outcome = outcome
224
+ reported = "unavailable" if calls is None else str(calls)
225
+ detail = (
226
+ "required evidence use was not confirmed by OCR result telemetry"
227
+ if reason == "evidence-use-unconfirmed"
228
+ else "mandatory evidence summary action attribution was not confirmed"
229
+ if outcome.requires_evidence_mcp
230
+ else "evidence action attribution was not confirmed"
231
+ )
232
+ super().__init__(
233
+ f"{detail} (reported calls: {reported}; reason={reason}). "
234
+ "Result not accepted for publication or automatic approval."
235
+ )
236
+
237
+ def diagnostic(self) -> str:
238
+ """Render only parser-validated outcome and coverage counters."""
239
+ coverage = self.outcome.coverage_summary
240
+ return f"Review rejected: {self} outcome={self.outcome.kind}" + (
241
+ f"; {coverage}" if coverage else ""
242
+ )
214
243
 
215
244
 
216
245
  @dataclass(frozen=True, slots=True)
@@ -480,6 +509,10 @@ def _review_receipt(
480
509
  raise ReviewRunnerError(f"OCR result has an unsupported outcome contract: {exc}") from exc
481
510
 
482
511
  tool_calls = payload.get("tool_calls")
512
+ if tool_calls is None and "tool_calls" in payload:
513
+ raise ReviewRunnerError("OCR result has malformed MCP usage telemetry")
514
+ if tool_calls is None and outcome.requires_evidence_mcp:
515
+ raise ReviewRejected("evidence-use-unconfirmed", outcome, None)
483
516
  by_tool = tool_calls.get("by_tool") if isinstance(tool_calls, dict) else None
484
517
  total_calls = tool_calls.get("total") if isinstance(tool_calls, dict) else None
485
518
  if tool_calls is None and outcome.kind == "failed":
@@ -516,7 +549,11 @@ def _review_receipt(
516
549
  if (
517
550
  not isinstance(count, int)
518
551
  or isinstance(count, bool)
519
- or not 0 < count <= MAX_TOOLKIT_MCP_USAGE_COUNT
552
+ or not (
553
+ 0 <= count <= MAX_TOOLKIT_MCP_USAGE_COUNT
554
+ if tool == TOOL_NAME
555
+ else 0 < count <= MAX_TOOLKIT_MCP_USAGE_COUNT
556
+ )
520
557
  ):
521
558
  raise ReviewRunnerError("OCR result has an invalid known MCP usage count")
522
559
  owner = owners[tool]
@@ -538,7 +575,8 @@ def _review_receipt(
538
575
  }
539
576
  evidence_calls = sum(evidence_by_tool.values())
540
577
  if outcome.requires_evidence_mcp and not evidence_by_tool[TOOL_NAME]:
541
- raise ReviewRunnerError(f"OCR review did not call the mandatory {TOOL_NAME} tool")
578
+ reported = 0 if total_calls == 0 else by_tool.get(TOOL_NAME)
579
+ raise ReviewRejected("evidence-use-unconfirmed", outcome, reported)
542
580
  try:
543
581
  action_attribution = verified_evidence_actions(
544
582
  evidence_by_tool,
@@ -546,7 +584,9 @@ def _review_receipt(
546
584
  mandatory=outcome.requires_evidence_mcp,
547
585
  )
548
586
  except ValueError as exc:
549
- raise ReviewRunnerError(str(exc)) from exc
587
+ raise ReviewRejected(
588
+ "evidence-action-attribution-invalid", outcome, evidence_by_tool[TOOL_NAME]
589
+ ) from exc
550
590
  completed = action_attribution.get("completed")
551
591
  failure_state = (
552
592
  "absent"
@@ -1485,6 +1525,34 @@ def _publication_projection(
1485
1525
  return projected, publication, True
1486
1526
 
1487
1527
 
1528
+ def _retain_private_diagnostic_result(result_path: Path, *, reason: str) -> bool:
1529
+ """Mark bounded rejected execution data without granting publication authority."""
1530
+
1531
+ try:
1532
+ transform_ocr_result(
1533
+ result_path,
1534
+ lambda payload: {
1535
+ TOOLKIT_PRIVATE_DIAGNOSTIC_KEY: {"reason": reason},
1536
+ "result": payload,
1537
+ },
1538
+ )
1539
+ except (OcrResultMalformed, OcrResultMissing, OcrResultTooLarge, OSError):
1540
+ print(
1541
+ "OCR private diagnostic retention failed; original failure preserved.",
1542
+ file=sys.stderr,
1543
+ )
1544
+ try:
1545
+ result_path.unlink(missing_ok=True)
1546
+ except OSError:
1547
+ pass
1548
+ return False
1549
+ print(
1550
+ "OCR rejected result retained as private diagnostics; not posting-eligible.",
1551
+ file=sys.stderr,
1552
+ )
1553
+ return True
1554
+
1555
+
1488
1556
  def _finalize_ocr_result(
1489
1557
  result_path: Path,
1490
1558
  composition: mcp_config.MCPComposition,
@@ -1497,6 +1565,7 @@ def _finalize_ocr_result(
1497
1565
  toolkit_advisory: OcrToolkitAdvisory | None = None,
1498
1566
  report_consumer: Callable[[ReviewReport], None] | None = None,
1499
1567
  state: ReviewRunState | None = None,
1568
+ private_diagnostics: bool = False,
1500
1569
  ) -> tuple[dict[str, int], bool, dict[str, object]]:
1501
1570
  """Validate, DLP-project, and receipt-bind one result in one atomic read/replace."""
1502
1571
 
@@ -1509,7 +1578,7 @@ def _finalize_ocr_result(
1509
1578
 
1510
1579
  def finalize(payload: dict[str, object]) -> dict[str, object]:
1511
1580
  nonlocal failure_telemetry, filtered, publication, usage, report
1512
- for reserved in (TOOLKIT_RESULT_KEY, TOOLKIT_ADVISORY_KEY):
1581
+ for reserved in (TOOLKIT_RESULT_KEY, TOOLKIT_ADVISORY_KEY, TOOLKIT_PRIVATE_DIAGNOSTIC_KEY):
1513
1582
  if reserved in payload:
1514
1583
  raise OcrResultMalformed(f"OCR result contains reserved field {reserved!r}")
1515
1584
  warnings = payload.get("warnings", [])
@@ -1566,7 +1635,7 @@ def _finalize_ocr_result(
1566
1635
  finalized = {**projected, TOOLKIT_RESULT_KEY: metadata} if provider_receipt else projected
1567
1636
  if toolkit_advisory is not None and provider_receipt:
1568
1637
  finalized[TOOLKIT_ADVISORY_KEY] = toolkit_advisory_payload(toolkit_advisory)
1569
- if report_consumer is not None:
1638
+ if report_consumer is not None and not private_diagnostics:
1570
1639
  evidence = metadata.get("evidence")
1571
1640
  if not isinstance(evidence, dict):
1572
1641
  raise ReviewRunnerError("verified evidence facts are unavailable")
@@ -1585,17 +1654,31 @@ def _finalize_ocr_result(
1585
1654
  )
1586
1655
  except OcrResultContractError as exc:
1587
1656
  raise ReviewRunnerError("admitted review report is inconsistent") from exc
1657
+ if private_diagnostics:
1658
+ return {**payload, TOOLKIT_PRIVATE_DIAGNOSTIC_KEY: {"reason": "private-retention"}}
1588
1659
  return finalized
1589
1660
 
1590
1661
  try:
1591
1662
  transform_ocr_result(result_path, finalize)
1592
1663
  except (OcrResultMalformed, OcrResultMissing, OcrResultTooLarge) as exc:
1664
+ if private_diagnostics:
1665
+ try:
1666
+ result_path.unlink(missing_ok=True)
1667
+ except OSError:
1668
+ pass
1593
1669
  raise ReviewRunnerError("OCR result is not valid bounded JSON") from exc
1594
- except ReviewRunnerError:
1595
- try:
1596
- result_path.unlink(missing_ok=True)
1597
- except OSError:
1598
- pass
1670
+ except ReviewRunnerError as exc:
1671
+ if isinstance(exc, ReviewRejected):
1672
+ print(exc.diagnostic(), file=sys.stderr)
1673
+ retained = False
1674
+ if private_diagnostics:
1675
+ reason = exc.reason if isinstance(exc, ReviewRejected) else "result-validation-failed"
1676
+ retained = _retain_private_diagnostic_result(result_path, reason=reason)
1677
+ if not retained:
1678
+ try:
1679
+ result_path.unlink(missing_ok=True)
1680
+ except OSError:
1681
+ pass
1599
1682
  raise
1600
1683
  _print_tool_failure_diagnostics(failure_telemetry)
1601
1684
  if state is not None:
@@ -2916,7 +2999,7 @@ def _run_evidence_review(
2916
2999
  cleanup_error = OSError("federation cleanup could not be verified")
2917
3000
  if state.progress is not None:
2918
3001
  state.progress.phase("cleanup")
2919
- if exit_code == 0 and not preserve_authorized:
3002
+ if exit_code == 0:
2920
3003
  evidence_action_counts = read_action_receipt(artifacts.action_receipt)
2921
3004
  if not preserve_authorized:
2922
3005
  try:
@@ -2932,7 +3015,7 @@ def _run_evidence_review(
2932
3015
  os.environ.pop("HOME", None)
2933
3016
  else:
2934
3017
  os.environ["HOME"] = previous_home
2935
- if cleanup_error is None and exit_code == 0 and not preserve_authorized:
3018
+ if cleanup_error is None and exit_code == 0:
2936
3019
  state.enter("result-validation")
2937
3020
  forbidden: tuple[str | ForbiddenValue, ...] = tuple(
2938
3021
  ForbiddenValue(v, "operator_secret") for v in composition.secret_values
@@ -2954,15 +3037,20 @@ def _run_evidence_review(
2954
3037
  forbidden=forbidden,
2955
3038
  dlp_enabled=dlp_enabled,
2956
3039
  toolkit_advisory=background_qualification.advisory,
3040
+ **({"private_diagnostics": True} if preserve_authorized else {}),
2957
3041
  **({"report_consumer": state.admit_report} if state.local else {}),
2958
3042
  **({"state": state} if state.local or state.progress is not None else {}),
2959
3043
  )
2960
3044
  except ReviewRunnerError:
2961
- try:
2962
- result_path.unlink(missing_ok=True)
2963
- except OSError:
2964
- pass
3045
+ # Authorized diagnostic retention is owned by finalization.
3046
+ if not preserve_authorized:
3047
+ try:
3048
+ result_path.unlink(missing_ok=True)
3049
+ except OSError:
3050
+ pass
2965
3051
  raise
3052
+ if cleanup_error is None and exit_code != 0 and preserve_authorized:
3053
+ _retain_private_diagnostic_result(result_path, reason="execution-failed")
2966
3054
  if cleanup_error is None and exit_code == 0 and preserve_authorized:
2967
3055
  forbidden = tuple(
2968
3056
  ForbiddenValue(v, "operator_secret") for v in composition.secret_values
@@ -2991,7 +3079,7 @@ def _run_evidence_review(
2991
3079
  raise ReviewRunnerError("OCR private session cleanup failed") from cleanup_error
2992
3080
  if preserve_authorized:
2993
3081
  print(
2994
- "OCR private diagnostics retained; result is not posting-eligible "
3082
+ "OCR private session artifacts retained; no posting-eligible result handoff "
2995
3083
  f"session_home={session_home} artifact_directory={artifacts.directory}",
2996
3084
  file=sys.stderr,
2997
3085
  )