open-code-review-toolkit 0.8.2__tar.gz → 0.8.4__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.
- {open_code_review_toolkit-0.8.2 → open_code_review_toolkit-0.8.4}/PKG-INFO +2 -2
- {open_code_review_toolkit-0.8.2 → open_code_review_toolkit-0.8.4}/README.md +1 -1
- {open_code_review_toolkit-0.8.2 → open_code_review_toolkit-0.8.4}/src/ocr_toolkit/_version.py +2 -2
- {open_code_review_toolkit-0.8.2 → open_code_review_toolkit-0.8.4}/src/ocr_toolkit/context/dlp.py +7 -4
- {open_code_review_toolkit-0.8.2 → open_code_review_toolkit-0.8.4}/src/ocr_toolkit/ocr_result.py +96 -1
- {open_code_review_toolkit-0.8.2 → open_code_review_toolkit-0.8.4}/src/ocr_toolkit/posting/approval.py +65 -2
- {open_code_review_toolkit-0.8.2 → open_code_review_toolkit-0.8.4}/src/ocr_toolkit/posting/formatting.py +72 -59
- {open_code_review_toolkit-0.8.2 → open_code_review_toolkit-0.8.4}/src/ocr_toolkit/posting/result.py +5 -2
- {open_code_review_toolkit-0.8.2 → open_code_review_toolkit-0.8.4}/src/ocr_toolkit/posting/settings.py +0 -6
- {open_code_review_toolkit-0.8.2 → open_code_review_toolkit-0.8.4}/src/ocr_toolkit/posting/workflow.py +48 -9
- {open_code_review_toolkit-0.8.2 → open_code_review_toolkit-0.8.4}/src/ocr_toolkit/preflight.py +1 -1
- {open_code_review_toolkit-0.8.2 → open_code_review_toolkit-0.8.4}/src/ocr_toolkit/review_runner.py +134 -105
- {open_code_review_toolkit-0.8.2 → open_code_review_toolkit-0.8.4}/.gitignore +0 -0
- {open_code_review_toolkit-0.8.2 → open_code_review_toolkit-0.8.4}/LICENSE +0 -0
- {open_code_review_toolkit-0.8.2 → open_code_review_toolkit-0.8.4}/pyproject.toml +0 -0
- {open_code_review_toolkit-0.8.2 → open_code_review_toolkit-0.8.4}/src/ocr_toolkit/__init__.py +0 -0
- {open_code_review_toolkit-0.8.2 → open_code_review_toolkit-0.8.4}/src/ocr_toolkit/cli.py +0 -0
- {open_code_review_toolkit-0.8.2 → open_code_review_toolkit-0.8.4}/src/ocr_toolkit/common/__init__.py +0 -0
- {open_code_review_toolkit-0.8.2 → open_code_review_toolkit-0.8.4}/src/ocr_toolkit/common/filesystem.py +0 -0
- {open_code_review_toolkit-0.8.2 → open_code_review_toolkit-0.8.4}/src/ocr_toolkit/common/git.py +0 -0
- {open_code_review_toolkit-0.8.2 → open_code_review_toolkit-0.8.4}/src/ocr_toolkit/common/language.py +0 -0
- {open_code_review_toolkit-0.8.2 → open_code_review_toolkit-0.8.4}/src/ocr_toolkit/common/markdown.py +0 -0
- {open_code_review_toolkit-0.8.2 → open_code_review_toolkit-0.8.4}/src/ocr_toolkit/common/redaction.py +0 -0
- {open_code_review_toolkit-0.8.2 → open_code_review_toolkit-0.8.4}/src/ocr_toolkit/config_writer.py +0 -0
- {open_code_review_toolkit-0.8.2 → open_code_review_toolkit-0.8.4}/src/ocr_toolkit/configure.py +0 -0
- {open_code_review_toolkit-0.8.2 → open_code_review_toolkit-0.8.4}/src/ocr_toolkit/context/__init__.py +0 -0
- {open_code_review_toolkit-0.8.2 → open_code_review_toolkit-0.8.4}/src/ocr_toolkit/context/adapters.py +0 -0
- {open_code_review_toolkit-0.8.2 → open_code_review_toolkit-0.8.4}/src/ocr_toolkit/context/broker.py +0 -0
- {open_code_review_toolkit-0.8.2 → open_code_review_toolkit-0.8.4}/src/ocr_toolkit/context/contracts.py +0 -0
- {open_code_review_toolkit-0.8.2 → open_code_review_toolkit-0.8.4}/src/ocr_toolkit/context/mcp.py +0 -0
- {open_code_review_toolkit-0.8.2 → open_code_review_toolkit-0.8.4}/src/ocr_toolkit/context/policy.py +0 -0
- {open_code_review_toolkit-0.8.2 → open_code_review_toolkit-0.8.4}/src/ocr_toolkit/context/recognizers.py +0 -0
- {open_code_review_toolkit-0.8.2 → open_code_review_toolkit-0.8.4}/src/ocr_toolkit/context/store.py +0 -0
- {open_code_review_toolkit-0.8.2 → open_code_review_toolkit-0.8.4}/src/ocr_toolkit/evidence/__init__.py +0 -0
- {open_code_review_toolkit-0.8.2 → open_code_review_toolkit-0.8.4}/src/ocr_toolkit/evidence/__main__.py +0 -0
- {open_code_review_toolkit-0.8.2 → open_code_review_toolkit-0.8.4}/src/ocr_toolkit/evidence/actions.py +0 -0
- {open_code_review_toolkit-0.8.2 → open_code_review_toolkit-0.8.4}/src/ocr_toolkit/evidence/artifacts.py +0 -0
- {open_code_review_toolkit-0.8.2 → open_code_review_toolkit-0.8.4}/src/ocr_toolkit/evidence/categorize.py +0 -0
- {open_code_review_toolkit-0.8.2 → open_code_review_toolkit-0.8.4}/src/ocr_toolkit/evidence/collect.py +0 -0
- {open_code_review_toolkit-0.8.2 → open_code_review_toolkit-0.8.4}/src/ocr_toolkit/evidence/collectors/__init__.py +0 -0
- {open_code_review_toolkit-0.8.2 → open_code_review_toolkit-0.8.4}/src/ocr_toolkit/evidence/collectors/graphs.py +0 -0
- {open_code_review_toolkit-0.8.2 → open_code_review_toolkit-0.8.4}/src/ocr_toolkit/evidence/collectors/orchestration.py +0 -0
- {open_code_review_toolkit-0.8.2 → open_code_review_toolkit-0.8.4}/src/ocr_toolkit/evidence/collectors/projections.py +0 -0
- {open_code_review_toolkit-0.8.2 → open_code_review_toolkit-0.8.4}/src/ocr_toolkit/evidence/collectors/registry.py +0 -0
- {open_code_review_toolkit-0.8.2 → open_code_review_toolkit-0.8.4}/src/ocr_toolkit/evidence/collectors/sources.py +0 -0
- {open_code_review_toolkit-0.8.2 → open_code_review_toolkit-0.8.4}/src/ocr_toolkit/evidence/coverage.py +0 -0
- {open_code_review_toolkit-0.8.2 → open_code_review_toolkit-0.8.4}/src/ocr_toolkit/evidence/ecosystems/__init__.py +0 -0
- {open_code_review_toolkit-0.8.2 → open_code_review_toolkit-0.8.4}/src/ocr_toolkit/evidence/ecosystems/ansible/__init__.py +0 -0
- {open_code_review_toolkit-0.8.2 → open_code_review_toolkit-0.8.4}/src/ocr_toolkit/evidence/ecosystems/ansible/requirements.py +0 -0
- {open_code_review_toolkit-0.8.2 → open_code_review_toolkit-0.8.4}/src/ocr_toolkit/evidence/ecosystems/ansible/topology.py +0 -0
- {open_code_review_toolkit-0.8.2 → open_code_review_toolkit-0.8.4}/src/ocr_toolkit/evidence/ecosystems/contracts.py +0 -0
- {open_code_review_toolkit-0.8.2 → open_code_review_toolkit-0.8.4}/src/ocr_toolkit/evidence/ecosystems/go.py +0 -0
- {open_code_review_toolkit-0.8.2 → open_code_review_toolkit-0.8.4}/src/ocr_toolkit/evidence/ecosystems/javascript.py +0 -0
- {open_code_review_toolkit-0.8.2 → open_code_review_toolkit-0.8.4}/src/ocr_toolkit/evidence/ecosystems/php.py +0 -0
- {open_code_review_toolkit-0.8.2 → open_code_review_toolkit-0.8.4}/src/ocr_toolkit/evidence/ecosystems/python.py +0 -0
- {open_code_review_toolkit-0.8.2 → open_code_review_toolkit-0.8.4}/src/ocr_toolkit/evidence/frameworks/__init__.py +0 -0
- {open_code_review_toolkit-0.8.2 → open_code_review_toolkit-0.8.4}/src/ocr_toolkit/evidence/frameworks/contracts.py +0 -0
- {open_code_review_toolkit-0.8.2 → open_code_review_toolkit-0.8.4}/src/ocr_toolkit/evidence/frameworks/detection.py +0 -0
- {open_code_review_toolkit-0.8.2 → open_code_review_toolkit-0.8.4}/src/ocr_toolkit/evidence/frameworks/providers/__init__.py +0 -0
- {open_code_review_toolkit-0.8.2 → open_code_review_toolkit-0.8.4}/src/ocr_toolkit/evidence/frameworks/providers/frontend.py +0 -0
- {open_code_review_toolkit-0.8.2 → open_code_review_toolkit-0.8.4}/src/ocr_toolkit/evidence/frameworks/providers/go.py +0 -0
- {open_code_review_toolkit-0.8.2 → open_code_review_toolkit-0.8.4}/src/ocr_toolkit/evidence/frameworks/providers/php.py +0 -0
- {open_code_review_toolkit-0.8.2 → open_code_review_toolkit-0.8.4}/src/ocr_toolkit/evidence/frameworks/providers/python.py +0 -0
- {open_code_review_toolkit-0.8.2 → open_code_review_toolkit-0.8.4}/src/ocr_toolkit/evidence/frameworks/registry.py +0 -0
- {open_code_review_toolkit-0.8.2 → open_code_review_toolkit-0.8.4}/src/ocr_toolkit/evidence/frameworks/schema.py +0 -0
- {open_code_review_toolkit-0.8.2 → open_code_review_toolkit-0.8.4}/src/ocr_toolkit/evidence/frameworks/templates.py +0 -0
- {open_code_review_toolkit-0.8.2 → open_code_review_toolkit-0.8.4}/src/ocr_toolkit/evidence/infrastructure.py +0 -0
- {open_code_review_toolkit-0.8.2 → open_code_review_toolkit-0.8.4}/src/ocr_toolkit/evidence/invocation.py +0 -0
- {open_code_review_toolkit-0.8.2 → open_code_review_toolkit-0.8.4}/src/ocr_toolkit/evidence/mcp.py +0 -0
- {open_code_review_toolkit-0.8.2 → open_code_review_toolkit-0.8.4}/src/ocr_toolkit/evidence/model.py +0 -0
- {open_code_review_toolkit-0.8.2 → open_code_review_toolkit-0.8.4}/src/ocr_toolkit/evidence/policy/__init__.py +0 -0
- {open_code_review_toolkit-0.8.2 → open_code_review_toolkit-0.8.4}/src/ocr_toolkit/evidence/policy/contracts.py +0 -0
- {open_code_review_toolkit-0.8.2 → open_code_review_toolkit-0.8.4}/src/ocr_toolkit/evidence/policy/decisions.py +0 -0
- {open_code_review_toolkit-0.8.2 → open_code_review_toolkit-0.8.4}/src/ocr_toolkit/evidence/policy/guidance.py +0 -0
- {open_code_review_toolkit-0.8.2 → open_code_review_toolkit-0.8.4}/src/ocr_toolkit/evidence/policy/registry.py +0 -0
- {open_code_review_toolkit-0.8.2 → open_code_review_toolkit-0.8.4}/src/ocr_toolkit/evidence/policy/schema.py +0 -0
- {open_code_review_toolkit-0.8.2 → open_code_review_toolkit-0.8.4}/src/ocr_toolkit/evidence/policy/scopes.py +0 -0
- {open_code_review_toolkit-0.8.2 → open_code_review_toolkit-0.8.4}/src/ocr_toolkit/evidence/project.py +0 -0
- {open_code_review_toolkit-0.8.2 → open_code_review_toolkit-0.8.4}/src/ocr_toolkit/evidence/repository.py +0 -0
- {open_code_review_toolkit-0.8.2 → open_code_review_toolkit-0.8.4}/src/ocr_toolkit/evidence/review_context.py +0 -0
- {open_code_review_toolkit-0.8.2 → open_code_review_toolkit-0.8.4}/src/ocr_toolkit/evidence/store/__init__.py +0 -0
- {open_code_review_toolkit-0.8.2 → open_code_review_toolkit-0.8.4}/src/ocr_toolkit/evidence/store/atomic.py +0 -0
- {open_code_review_toolkit-0.8.2 → open_code_review_toolkit-0.8.4}/src/ocr_toolkit/evidence/store/contracts.py +0 -0
- {open_code_review_toolkit-0.8.2 → open_code_review_toolkit-0.8.4}/src/ocr_toolkit/evidence/store/core.py +0 -0
- {open_code_review_toolkit-0.8.2 → open_code_review_toolkit-0.8.4}/src/ocr_toolkit/evidence/store/readback.py +0 -0
- {open_code_review_toolkit-0.8.2 → open_code_review_toolkit-0.8.4}/src/ocr_toolkit/evidence/store/values.py +0 -0
- {open_code_review_toolkit-0.8.2 → open_code_review_toolkit-0.8.4}/src/ocr_toolkit/mcp_config.py +0 -0
- {open_code_review_toolkit-0.8.2 → open_code_review_toolkit-0.8.4}/src/ocr_toolkit/posting/__init__.py +0 -0
- {open_code_review_toolkit-0.8.2 → open_code_review_toolkit-0.8.4}/src/ocr_toolkit/posting/__main__.py +0 -0
- {open_code_review_toolkit-0.8.2 → open_code_review_toolkit-0.8.4}/src/ocr_toolkit/posting/comments.py +0 -0
- {open_code_review_toolkit-0.8.2 → open_code_review_toolkit-0.8.4}/src/ocr_toolkit/posting/gitlab.py +0 -0
- {open_code_review_toolkit-0.8.2 → open_code_review_toolkit-0.8.4}/src/ocr_toolkit/posting/gitlab_approval.py +0 -0
- {open_code_review_toolkit-0.8.2 → open_code_review_toolkit-0.8.4}/src/ocr_toolkit/posting/markers.py +0 -0
- {open_code_review_toolkit-0.8.2 → open_code_review_toolkit-0.8.4}/src/ocr_toolkit/posting/payloads.py +0 -0
- {open_code_review_toolkit-0.8.2 → open_code_review_toolkit-0.8.4}/src/ocr_toolkit/posting/reconciliation.py +0 -0
- {open_code_review_toolkit-0.8.2 → open_code_review_toolkit-0.8.4}/src/ocr_toolkit/posting/snapshot.py +0 -0
- {open_code_review_toolkit-0.8.2 → open_code_review_toolkit-0.8.4}/src/ocr_toolkit/posting/suggestions.py +0 -0
- {open_code_review_toolkit-0.8.2 → open_code_review_toolkit-0.8.4}/src/ocr_toolkit/posting/transaction.py +0 -0
- {open_code_review_toolkit-0.8.2 → open_code_review_toolkit-0.8.4}/src/ocr_toolkit/pre_execution.py +0 -0
- {open_code_review_toolkit-0.8.2 → open_code_review_toolkit-0.8.4}/src/ocr_toolkit/provider_config.py +0 -0
- {open_code_review_toolkit-0.8.2 → open_code_review_toolkit-0.8.4}/src/ocr_toolkit/provider_failure.py +0 -0
- {open_code_review_toolkit-0.8.2 → open_code_review_toolkit-0.8.4}/src/ocr_toolkit/providers/__init__.py +0 -0
- {open_code_review_toolkit-0.8.2 → open_code_review_toolkit-0.8.4}/src/ocr_toolkit/providers/gitlab.py +0 -0
- {open_code_review_toolkit-0.8.2 → open_code_review_toolkit-0.8.4}/src/ocr_toolkit/providers/gitlab_context.py +0 -0
- {open_code_review_toolkit-0.8.2 → open_code_review_toolkit-0.8.4}/src/ocr_toolkit/providers/gitlab_discussions.py +0 -0
- {open_code_review_toolkit-0.8.2 → open_code_review_toolkit-0.8.4}/src/ocr_toolkit/providers/gitlab_identity.py +0 -0
- {open_code_review_toolkit-0.8.2 → open_code_review_toolkit-0.8.4}/src/ocr_toolkit/providers/gitlab_remediation.py +0 -0
- {open_code_review_toolkit-0.8.2 → open_code_review_toolkit-0.8.4}/src/ocr_toolkit/py.typed +0 -0
- {open_code_review_toolkit-0.8.2 → open_code_review_toolkit-0.8.4}/src/ocr_toolkit/result_contract.py +0 -0
- {open_code_review_toolkit-0.8.2 → open_code_review_toolkit-0.8.4}/src/ocr_toolkit/result_usage.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: open-code-review-toolkit
|
|
3
|
-
Version: 0.8.
|
|
3
|
+
Version: 0.8.4
|
|
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
|
|
@@ -249,7 +249,7 @@ ocr-ci --help
|
|
|
249
249
|
The exact recommended OCR release and its verified asset checksums live in the [versioned compatibility manifest](compatibility/ocr-support.json). CI should pin that release and checksum before execution.
|
|
250
250
|
The [versioned compatibility policy](docs/compatibility.md) records tested assets and evidence and describes the conservative Dependabot-like qualification workflow for later upstream releases.
|
|
251
251
|
Review output defaults to English. `OCR_REVIEW_LANGUAGE` accepts another explicit language name when a project needs localized review output; for example, `OCR_REVIEW_LANGUAGE=Russian`.
|
|
252
|
-
The current OCR 1.10.
|
|
252
|
+
The current OCR 1.10.1 integration defaults `OCR_REVIEW_EFFORT` to `medium` for two review rounds. `low` and `high` are explicit one- and three-round alternatives; see the [configuration reference](docs/configuration.md#review-effort) for cost, budget, and precedence boundaries.
|
|
253
253
|
|
|
254
254
|
Stable distributions are published to [PyPI](https://pypi.org/project/open-code-review-toolkit/) and mirrored as checksum-listed, provenance-attested assets in the corresponding [GitHub Release](https://github.com/xeonvs/open-code-review-toolkit/releases). Development snapshots are published only to TestPyPI.
|
|
255
255
|
|
|
@@ -22,7 +22,7 @@ ocr-ci --help
|
|
|
22
22
|
The exact recommended OCR release and its verified asset checksums live in the [versioned compatibility manifest](compatibility/ocr-support.json). CI should pin that release and checksum before execution.
|
|
23
23
|
The [versioned compatibility policy](docs/compatibility.md) records tested assets and evidence and describes the conservative Dependabot-like qualification workflow for later upstream releases.
|
|
24
24
|
Review output defaults to English. `OCR_REVIEW_LANGUAGE` accepts another explicit language name when a project needs localized review output; for example, `OCR_REVIEW_LANGUAGE=Russian`.
|
|
25
|
-
The current OCR 1.10.
|
|
25
|
+
The current OCR 1.10.1 integration defaults `OCR_REVIEW_EFFORT` to `medium` for two review rounds. `low` and `high` are explicit one- and three-round alternatives; see the [configuration reference](docs/configuration.md#review-effort) for cost, budget, and precedence boundaries.
|
|
26
26
|
|
|
27
27
|
Stable distributions are published to [PyPI](https://pypi.org/project/open-code-review-toolkit/) and mirrored as checksum-listed, provenance-attested assets in the corresponding [GitHub Release](https://github.com/xeonvs/open-code-review-toolkit/releases). Development snapshots are published only to TestPyPI.
|
|
28
28
|
|
{open_code_review_toolkit-0.8.2 → open_code_review_toolkit-0.8.4}/src/ocr_toolkit/_version.py
RENAMED
|
@@ -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.8.
|
|
22
|
-
__version_tuple__ = version_tuple = (0, 8,
|
|
21
|
+
__version__ = version = '0.8.4'
|
|
22
|
+
__version_tuple__ = version_tuple = (0, 8, 4)
|
|
23
23
|
|
|
24
24
|
__commit_id__ = commit_id = None
|
{open_code_review_toolkit-0.8.2 → open_code_review_toolkit-0.8.4}/src/ocr_toolkit/context/dlp.py
RENAMED
|
@@ -109,7 +109,7 @@ class ForbiddenMatcher:
|
|
|
109
109
|
exact: list[str] = []
|
|
110
110
|
seen: set[str] = set()
|
|
111
111
|
for value in values:
|
|
112
|
-
candidate = normalize_text(value)
|
|
112
|
+
candidate = normalize_text(value, allow_horizontal_tabs=True)
|
|
113
113
|
if not candidate:
|
|
114
114
|
continue
|
|
115
115
|
for representation in (_display_normalize(candidate), _source_normalize(candidate)):
|
|
@@ -157,14 +157,16 @@ class ForbiddenMatcher:
|
|
|
157
157
|
return self.match_reason(value) is not None
|
|
158
158
|
|
|
159
159
|
|
|
160
|
-
def normalize_text(value: object) -> str | None:
|
|
160
|
+
def normalize_text(value: object, *, allow_horizontal_tabs: bool = False) -> str | None:
|
|
161
161
|
"""Normalize NFC/newlines and reject unsupported controls."""
|
|
162
162
|
|
|
163
163
|
if not isinstance(value, str):
|
|
164
164
|
return None
|
|
165
165
|
normalized = unicodedata.normalize("NFC", value.replace("\r\n", "\n").replace("\r", "\n"))
|
|
166
|
+
allowed_controls = "\n\t" if allow_horizontal_tabs else "\n"
|
|
166
167
|
if any(
|
|
167
|
-
unicodedata.category(character) in {"Cc", "Cf", "Cs", "Zl", "Zp"}
|
|
168
|
+
unicodedata.category(character) in {"Cc", "Cf", "Cs", "Zl", "Zp"}
|
|
169
|
+
and character not in allowed_controls
|
|
168
170
|
for character in normalized
|
|
169
171
|
):
|
|
170
172
|
return None
|
|
@@ -178,10 +180,11 @@ def check_text(
|
|
|
178
180
|
publication: bool = False,
|
|
179
181
|
forbidden: tuple[str, ...] = (),
|
|
180
182
|
forbidden_matcher: ForbiddenMatcher | None = None,
|
|
183
|
+
allow_horizontal_tabs: bool = False,
|
|
181
184
|
) -> DLPResult:
|
|
182
185
|
"""Apply independent units, redaction, PII, and optional publication checks."""
|
|
183
186
|
|
|
184
|
-
normalized = normalize_text(value)
|
|
187
|
+
normalized = normalize_text(value, allow_horizontal_tabs=allow_horizontal_tabs)
|
|
185
188
|
if normalized is None:
|
|
186
189
|
return DLPResult(False, None, "invalid_text", "type_or_control")
|
|
187
190
|
normalized_bytes = normalized.encode("utf-8")
|
{open_code_review_toolkit-0.8.2 → open_code_review_toolkit-0.8.4}/src/ocr_toolkit/ocr_result.py
RENAMED
|
@@ -9,6 +9,7 @@ import secrets
|
|
|
9
9
|
import stat
|
|
10
10
|
import sys
|
|
11
11
|
from collections.abc import Callable, Mapping
|
|
12
|
+
from dataclasses import dataclass
|
|
12
13
|
from pathlib import Path
|
|
13
14
|
from typing import Any
|
|
14
15
|
|
|
@@ -20,6 +21,11 @@ MAX_RESULT_BYTES_HARD_LIMIT = 20_000_000
|
|
|
20
21
|
TOOLKIT_RESULT_KEY = "_ocr_toolkit"
|
|
21
22
|
TOOLKIT_RESULT_SCHEMA_VERSION = 5
|
|
22
23
|
SUPPORTED_TOOLKIT_RESULT_SCHEMA_VERSIONS = frozenset({TOOLKIT_RESULT_SCHEMA_VERSION})
|
|
24
|
+
TOOLKIT_ADVISORY_KEY = "_ocr_toolkit_advisory"
|
|
25
|
+
TOOLKIT_ADVISORY_SCHEMA_VERSION = "ocr.toolkit-advisory/v1"
|
|
26
|
+
TOOLKIT_ADVISORY_KIND = "background_recommended_limit"
|
|
27
|
+
TOOLKIT_ADVISORY_UNIT = "characters"
|
|
28
|
+
MAX_TOOLKIT_ADVISORY_VALUE = 999_999_999_999
|
|
23
29
|
# The receipt can name the 16 configured external servers plus the mandatory built-in.
|
|
24
30
|
MAX_TOOLKIT_MCP_USAGE_SERVERS = 17
|
|
25
31
|
MAX_TOOLKIT_MCP_TOOLS_PER_SERVER = 128
|
|
@@ -27,6 +33,23 @@ MAX_TOOLKIT_MCP_TOOL_NAME_CHARS = 4_096
|
|
|
27
33
|
MAX_TOOLKIT_MCP_USAGE_COUNT = 1_000_000_000
|
|
28
34
|
TOOLKIT_MCP_SERVER_NAME_RE = re.compile(r"^[A-Za-z0-9_-]{1,64}$")
|
|
29
35
|
|
|
36
|
+
# Closed names whose numeric call counts may appear in the public review summary.
|
|
37
|
+
# Dynamic external MCP tools remain represented by the receipt's verified
|
|
38
|
+
# per-server aggregate so provider-controlled names do not cross into GitLab.
|
|
39
|
+
PUBLIC_REVIEW_TOOL_CALL_NAMES = frozenset(
|
|
40
|
+
{
|
|
41
|
+
"code_comment",
|
|
42
|
+
"code_search",
|
|
43
|
+
"context_get",
|
|
44
|
+
"context_list",
|
|
45
|
+
"file_find",
|
|
46
|
+
"file_read",
|
|
47
|
+
"file_read_diff",
|
|
48
|
+
"ocr_toolkit_evidence",
|
|
49
|
+
"task_done",
|
|
50
|
+
}
|
|
51
|
+
)
|
|
52
|
+
|
|
30
53
|
|
|
31
54
|
class OcrResultMissing(Exception):
|
|
32
55
|
"""The OCR result artifact is missing or unreadable on disk."""
|
|
@@ -40,6 +63,69 @@ class OcrResultTooLarge(Exception):
|
|
|
40
63
|
"""The OCR result artifact exceeds the configured safety limit."""
|
|
41
64
|
|
|
42
65
|
|
|
66
|
+
@dataclass(frozen=True, slots=True)
|
|
67
|
+
class OcrToolkitAdvisory:
|
|
68
|
+
"""Carry one validated toolkit-authored numeric OCR advisory."""
|
|
69
|
+
|
|
70
|
+
actual: int
|
|
71
|
+
recommended: int
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def parse_toolkit_advisory(value: Any) -> OcrToolkitAdvisory:
|
|
75
|
+
"""Parse the exact private toolkit advisory without accepting extensions."""
|
|
76
|
+
|
|
77
|
+
if not isinstance(value, dict) or set(value) != {
|
|
78
|
+
"schema_version",
|
|
79
|
+
"kind",
|
|
80
|
+
"actual",
|
|
81
|
+
"recommended",
|
|
82
|
+
"unit",
|
|
83
|
+
}:
|
|
84
|
+
raise OcrResultMalformed("OCR toolkit advisory has an unsupported schema")
|
|
85
|
+
actual = value.get("actual")
|
|
86
|
+
recommended = value.get("recommended")
|
|
87
|
+
if (
|
|
88
|
+
value.get("schema_version") != TOOLKIT_ADVISORY_SCHEMA_VERSION
|
|
89
|
+
or value.get("kind") != TOOLKIT_ADVISORY_KIND
|
|
90
|
+
or value.get("unit") != TOOLKIT_ADVISORY_UNIT
|
|
91
|
+
or not isinstance(actual, int)
|
|
92
|
+
or isinstance(actual, bool)
|
|
93
|
+
or not isinstance(recommended, int)
|
|
94
|
+
or isinstance(recommended, bool)
|
|
95
|
+
or not 0 < recommended < actual <= MAX_TOOLKIT_ADVISORY_VALUE
|
|
96
|
+
):
|
|
97
|
+
raise OcrResultMalformed("OCR toolkit advisory has invalid closed values")
|
|
98
|
+
return OcrToolkitAdvisory(actual=actual, recommended=recommended)
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def background_recommended_advisory(*, actual: int, recommended: int) -> OcrToolkitAdvisory:
|
|
102
|
+
"""Construct one validated background recommendation advisory."""
|
|
103
|
+
|
|
104
|
+
return parse_toolkit_advisory(
|
|
105
|
+
{
|
|
106
|
+
"schema_version": TOOLKIT_ADVISORY_SCHEMA_VERSION,
|
|
107
|
+
"kind": TOOLKIT_ADVISORY_KIND,
|
|
108
|
+
"actual": actual,
|
|
109
|
+
"recommended": recommended,
|
|
110
|
+
"unit": TOOLKIT_ADVISORY_UNIT,
|
|
111
|
+
}
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def toolkit_advisory_payload(advisory: OcrToolkitAdvisory) -> dict[str, object]:
|
|
116
|
+
"""Serialize a validated advisory into its exact private result shape."""
|
|
117
|
+
|
|
118
|
+
payload: dict[str, object] = {
|
|
119
|
+
"schema_version": TOOLKIT_ADVISORY_SCHEMA_VERSION,
|
|
120
|
+
"kind": TOOLKIT_ADVISORY_KIND,
|
|
121
|
+
"actual": advisory.actual,
|
|
122
|
+
"recommended": advisory.recommended,
|
|
123
|
+
"unit": TOOLKIT_ADVISORY_UNIT,
|
|
124
|
+
}
|
|
125
|
+
parse_toolkit_advisory(payload)
|
|
126
|
+
return payload
|
|
127
|
+
|
|
128
|
+
|
|
43
129
|
def max_result_bytes() -> int:
|
|
44
130
|
"""Return the maximum OCR JSON artifact size to read into memory."""
|
|
45
131
|
|
|
@@ -217,7 +303,16 @@ def _decode_result(data: bytes) -> Any:
|
|
|
217
303
|
except UnicodeDecodeError as exc:
|
|
218
304
|
raise OcrResultMalformed(str(exc)) from exc
|
|
219
305
|
try:
|
|
220
|
-
|
|
306
|
+
|
|
307
|
+
def reject_duplicate_advisory(pairs: list[tuple[str, Any]]) -> dict[str, Any]:
|
|
308
|
+
result: dict[str, Any] = {}
|
|
309
|
+
for key, value in pairs:
|
|
310
|
+
if key == TOOLKIT_ADVISORY_KEY and key in result:
|
|
311
|
+
raise OcrResultMalformed("OCR result repeats the reserved toolkit advisory")
|
|
312
|
+
result[key] = value
|
|
313
|
+
return result
|
|
314
|
+
|
|
315
|
+
return json.loads(text, object_pairs_hook=reject_duplicate_advisory)
|
|
221
316
|
except (json.JSONDecodeError, RecursionError) as exc:
|
|
222
317
|
raise OcrResultMalformed(str(exc)) from exc
|
|
223
318
|
|
|
@@ -14,10 +14,11 @@ from ocr_toolkit.ocr_result import (
|
|
|
14
14
|
TOOLKIT_MCP_SERVER_NAME_RE,
|
|
15
15
|
)
|
|
16
16
|
from ocr_toolkit.posting.settings import BooleanSetting
|
|
17
|
-
from ocr_toolkit.result_contract import ReviewOutcome
|
|
17
|
+
from ocr_toolkit.result_contract import OcrResultContractError, ReviewOutcome
|
|
18
18
|
|
|
19
19
|
ALLOWED_CATEGORIES = frozenset({"style", "documentation", "maintainability"})
|
|
20
20
|
MAX_APPROVABLE_FINDINGS = 3
|
|
21
|
+
INVALID_APPROVAL_RECEIPT_REASON = "the review-time approval receipt is missing or invalid"
|
|
21
22
|
|
|
22
23
|
|
|
23
24
|
class ApprovalStatus(str, Enum):
|
|
@@ -218,6 +219,23 @@ def publication_dlp_state(value: Any) -> str | None:
|
|
|
218
219
|
)
|
|
219
220
|
):
|
|
220
221
|
return None
|
|
222
|
+
selected = original["selected"]
|
|
223
|
+
completed = original["completed"]
|
|
224
|
+
reused = original["reused"]
|
|
225
|
+
failed = original["failed"]
|
|
226
|
+
waived = original["waived"]
|
|
227
|
+
outcome = original["outcome"]
|
|
228
|
+
derived_outcomes = {"failed"} | (
|
|
229
|
+
{"skipped"}
|
|
230
|
+
if selected == 0
|
|
231
|
+
else {"clean", "warning"}
|
|
232
|
+
if failed == 0
|
|
233
|
+
else {"failed"}
|
|
234
|
+
if failed == selected
|
|
235
|
+
else {"partial"}
|
|
236
|
+
)
|
|
237
|
+
if selected != completed + reused + failed + waived or outcome not in derived_outcomes:
|
|
238
|
+
return None
|
|
221
239
|
return "publication-filtered"
|
|
222
240
|
|
|
223
241
|
|
|
@@ -237,7 +255,7 @@ def _valid_dlp_reason_counts(value: Any) -> bool:
|
|
|
237
255
|
def automatic_approval_metadata_reason(toolkit_metadata: Any) -> str:
|
|
238
256
|
"""Return the closed review-time receipt blocker for automatic approval."""
|
|
239
257
|
|
|
240
|
-
invalid =
|
|
258
|
+
invalid = INVALID_APPROVAL_RECEIPT_REASON
|
|
241
259
|
if not isinstance(toolkit_metadata, dict):
|
|
242
260
|
return invalid
|
|
243
261
|
if toolkit_metadata.get("schema_version") != 5 or set(toolkit_metadata) != {
|
|
@@ -433,6 +451,51 @@ def automatic_approval_metadata_reason(toolkit_metadata: Any) -> str:
|
|
|
433
451
|
return ""
|
|
434
452
|
|
|
435
453
|
|
|
454
|
+
def toolkit_receipt_is_valid(toolkit_metadata: Any) -> bool:
|
|
455
|
+
"""Return whether metadata is an exact receipt v5, including valid blockers."""
|
|
456
|
+
|
|
457
|
+
return automatic_approval_metadata_reason(toolkit_metadata) != INVALID_APPROVAL_RECEIPT_REASON
|
|
458
|
+
|
|
459
|
+
|
|
460
|
+
def publication_outcome_for_summary(outcome: ReviewOutcome, publication: Any) -> ReviewOutcome:
|
|
461
|
+
"""Recover only validated original coverage facts from a filtered receipt."""
|
|
462
|
+
|
|
463
|
+
if publication_dlp_state(publication) != "publication-filtered":
|
|
464
|
+
return outcome
|
|
465
|
+
if outcome.kind != "partial" or outcome.manifest_present:
|
|
466
|
+
raise OcrResultContractError(
|
|
467
|
+
"publication-filtered receipt is not bound to a safe result projection"
|
|
468
|
+
)
|
|
469
|
+
original = publication["original"]
|
|
470
|
+
kind = original["outcome"]
|
|
471
|
+
if outcome.budget_exceeded and kind != "partial":
|
|
472
|
+
raise OcrResultContractError(
|
|
473
|
+
"publication-filtered receipt contradicts the result budget state"
|
|
474
|
+
)
|
|
475
|
+
counts = {
|
|
476
|
+
field: original[field] for field in ("selected", "completed", "reused", "failed", "waived")
|
|
477
|
+
}
|
|
478
|
+
manifest_present = any(counts.values())
|
|
479
|
+
status = {
|
|
480
|
+
"clean": "complete" if manifest_present else "success",
|
|
481
|
+
"warning": "completed_with_warnings",
|
|
482
|
+
"partial": "budget_exceeded" if outcome.budget_exceeded else "completed_with_errors",
|
|
483
|
+
"failed": "failed",
|
|
484
|
+
"skipped": "skipped",
|
|
485
|
+
}[kind]
|
|
486
|
+
return ReviewOutcome(
|
|
487
|
+
status=status,
|
|
488
|
+
kind=kind,
|
|
489
|
+
budget_exceeded=outcome.budget_exceeded and kind == "partial",
|
|
490
|
+
manifest_present=manifest_present,
|
|
491
|
+
selected_count=counts["selected"],
|
|
492
|
+
completed_count=counts["completed"],
|
|
493
|
+
reused_count=counts["reused"],
|
|
494
|
+
failed_count=counts["failed"],
|
|
495
|
+
waived_count=counts["waived"],
|
|
496
|
+
)
|
|
497
|
+
|
|
498
|
+
|
|
436
499
|
def _valid_evidence_actions(value: Any, evidence_calls: Any) -> bool:
|
|
437
500
|
"""Validate verified counts or an explicit unavailable attribution state."""
|
|
438
501
|
|
|
@@ -20,8 +20,10 @@ from ocr_toolkit.common.redaction import redact_sensitive
|
|
|
20
20
|
from ocr_toolkit.ocr_result import (
|
|
21
21
|
MAX_TOOLKIT_MCP_USAGE_COUNT,
|
|
22
22
|
MAX_TOOLKIT_MCP_USAGE_SERVERS,
|
|
23
|
+
PUBLIC_REVIEW_TOOL_CALL_NAMES,
|
|
23
24
|
SUPPORTED_TOOLKIT_RESULT_SCHEMA_VERSIONS,
|
|
24
25
|
TOOLKIT_MCP_SERVER_NAME_RE,
|
|
26
|
+
OcrToolkitAdvisory,
|
|
25
27
|
)
|
|
26
28
|
from ocr_toolkit.posting.approval import (
|
|
27
29
|
ApprovalResult,
|
|
@@ -45,8 +47,6 @@ from ocr_toolkit.posting.settings import (
|
|
|
45
47
|
MAX_REVIEWER_GUIDE_LABEL_CHARS,
|
|
46
48
|
MAX_REVIEWER_GUIDE_LOCATION_CHARS,
|
|
47
49
|
MAX_REVIEWER_GUIDE_TEXT_CHARS,
|
|
48
|
-
MAX_TOOL_CALL_NAME_CHARS,
|
|
49
|
-
MAX_TOOL_CALL_SUMMARY_TOOLS,
|
|
50
50
|
SUGGESTION_HEADER,
|
|
51
51
|
post_badges,
|
|
52
52
|
post_emoji,
|
|
@@ -374,32 +374,25 @@ def nonnegative_int(value: Any) -> int | None:
|
|
|
374
374
|
return None
|
|
375
375
|
|
|
376
376
|
|
|
377
|
-
def truncate_tool_call_name(name: str) -> str:
|
|
378
|
-
"""Return a compact tool name for one-line MR summaries."""
|
|
379
|
-
|
|
380
|
-
if len(name) <= MAX_TOOL_CALL_NAME_CHARS:
|
|
381
|
-
return name
|
|
382
|
-
|
|
383
|
-
return name[: MAX_TOOL_CALL_NAME_CHARS - 3].rstrip() + "..."
|
|
384
|
-
|
|
385
|
-
|
|
386
377
|
def tool_call_name(value: Any) -> str:
|
|
387
|
-
"""Extract
|
|
378
|
+
"""Extract one closed public tool name from common OCR call shapes."""
|
|
388
379
|
|
|
389
380
|
if isinstance(value, str):
|
|
390
|
-
|
|
381
|
+
name = clean_text(value)
|
|
382
|
+
return name if name in PUBLIC_REVIEW_TOOL_CALL_NAMES else ""
|
|
391
383
|
|
|
392
384
|
if not isinstance(value, dict):
|
|
393
385
|
return ""
|
|
394
386
|
|
|
395
387
|
for key in ("name", "tool", "tool_name"):
|
|
396
388
|
name = clean_text(value.get(key))
|
|
397
|
-
if name:
|
|
389
|
+
if name in PUBLIC_REVIEW_TOOL_CALL_NAMES:
|
|
398
390
|
return name
|
|
399
391
|
|
|
400
392
|
function_value = value.get("function")
|
|
401
393
|
if isinstance(function_value, dict):
|
|
402
|
-
|
|
394
|
+
name = clean_text(function_value.get("name"))
|
|
395
|
+
return name if name in PUBLIC_REVIEW_TOOL_CALL_NAMES else ""
|
|
403
396
|
|
|
404
397
|
return ""
|
|
405
398
|
|
|
@@ -407,14 +400,17 @@ def tool_call_name(value: Any) -> str:
|
|
|
407
400
|
def tool_call_counts_from_items(
|
|
408
401
|
items: list[Any],
|
|
409
402
|
) -> tuple[int | None, list[tuple[str, int]]]:
|
|
410
|
-
"""Summarize a list-style OCR
|
|
403
|
+
"""Summarize admitted calls from a legacy list-style OCR payload."""
|
|
411
404
|
|
|
412
405
|
counts: dict[str, int] = {}
|
|
413
406
|
for item in items:
|
|
414
407
|
name = tool_call_name(item)
|
|
415
408
|
if not name:
|
|
416
409
|
continue
|
|
417
|
-
|
|
410
|
+
count = counts.get(name, 0) + 1
|
|
411
|
+
if count > MAX_TOOLKIT_MCP_USAGE_COUNT:
|
|
412
|
+
return None, []
|
|
413
|
+
counts[name] = count
|
|
418
414
|
|
|
419
415
|
total = sum(counts.values())
|
|
420
416
|
if total == 0 and items:
|
|
@@ -424,51 +420,52 @@ def tool_call_counts_from_items(
|
|
|
424
420
|
|
|
425
421
|
|
|
426
422
|
def format_tool_calls_summary(tool_calls: Any) -> str:
|
|
427
|
-
"""Return one bounded MR
|
|
423
|
+
"""Return one bounded MR line for admitted non-zero OCR tool counts."""
|
|
428
424
|
|
|
429
425
|
entries: list[tuple[str, int]]
|
|
430
426
|
total: int | None
|
|
431
|
-
|
|
432
|
-
if scalar_total is not None:
|
|
433
|
-
total = scalar_total
|
|
434
|
-
entries = []
|
|
435
|
-
elif isinstance(tool_calls, list):
|
|
427
|
+
if isinstance(tool_calls, list):
|
|
436
428
|
total, entries = tool_call_counts_from_items(tool_calls)
|
|
437
429
|
elif isinstance(tool_calls, dict):
|
|
438
430
|
by_tool_value = tool_calls.get("by_tool")
|
|
439
431
|
entries = []
|
|
440
|
-
|
|
441
|
-
valid_by_tool_count = False
|
|
432
|
+
admitted_total = 0
|
|
442
433
|
|
|
443
434
|
if isinstance(by_tool_value, dict):
|
|
444
435
|
for raw_name, raw_count in by_tool_value.items():
|
|
445
|
-
|
|
446
|
-
if count is None:
|
|
436
|
+
if not isinstance(raw_name, str) or raw_name not in PUBLIC_REVIEW_TOOL_CALL_NAMES:
|
|
447
437
|
continue
|
|
448
|
-
|
|
449
|
-
|
|
438
|
+
if (
|
|
439
|
+
not isinstance(raw_count, int)
|
|
440
|
+
or isinstance(raw_count, bool)
|
|
441
|
+
or not 0 < raw_count <= MAX_TOOLKIT_MCP_USAGE_COUNT
|
|
442
|
+
):
|
|
450
443
|
continue
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
444
|
+
admitted_total += raw_count
|
|
445
|
+
if admitted_total > MAX_TOOLKIT_MCP_USAGE_COUNT:
|
|
446
|
+
return ""
|
|
447
|
+
entries.append((raw_name, raw_count))
|
|
455
448
|
|
|
456
449
|
calls_value = tool_calls.get("calls")
|
|
457
|
-
if
|
|
458
|
-
list_total = None
|
|
459
|
-
elif isinstance(calls_value, list):
|
|
450
|
+
if not by_tool_value and isinstance(calls_value, list):
|
|
460
451
|
list_total, entries = tool_call_counts_from_items(calls_value)
|
|
461
452
|
else:
|
|
462
453
|
list_total = None
|
|
463
454
|
|
|
464
|
-
total
|
|
465
|
-
|
|
466
|
-
if
|
|
467
|
-
|
|
468
|
-
|
|
455
|
+
if "total" in tool_calls:
|
|
456
|
+
raw_total = tool_calls["total"]
|
|
457
|
+
if (
|
|
458
|
+
not isinstance(raw_total, int)
|
|
459
|
+
or isinstance(raw_total, bool)
|
|
460
|
+
or not 0 < raw_total <= MAX_TOOLKIT_MCP_USAGE_COUNT
|
|
461
|
+
):
|
|
462
|
+
return ""
|
|
463
|
+
total = raw_total
|
|
464
|
+
else:
|
|
465
|
+
if list_total is not None:
|
|
469
466
|
total = list_total
|
|
470
|
-
elif
|
|
471
|
-
total =
|
|
467
|
+
elif entries:
|
|
468
|
+
total = admitted_total
|
|
472
469
|
else:
|
|
473
470
|
return ""
|
|
474
471
|
else:
|
|
@@ -476,21 +473,14 @@ def format_tool_calls_summary(tool_calls: Any) -> str:
|
|
|
476
473
|
|
|
477
474
|
if total is None:
|
|
478
475
|
return ""
|
|
479
|
-
if total == 0:
|
|
476
|
+
if total == 0 or not entries:
|
|
477
|
+
return ""
|
|
478
|
+
if sum(count for _name, count in entries) > total:
|
|
480
479
|
return ""
|
|
481
480
|
|
|
482
481
|
line = f"- all OCR tool calls: {total} total"
|
|
483
|
-
if not entries:
|
|
484
|
-
return line
|
|
485
|
-
|
|
486
482
|
entries.sort(key=lambda item: (-item[1], item[0]))
|
|
487
|
-
|
|
488
|
-
detail_parts = [
|
|
489
|
-
f"{inline_code(truncate_tool_call_name(name))}: {count}" for name, count in shown_entries
|
|
490
|
-
]
|
|
491
|
-
omitted_entries = len(entries) - len(shown_entries)
|
|
492
|
-
if omitted_entries > 0:
|
|
493
|
-
detail_parts.append(f"+{omitted_entries} more")
|
|
483
|
+
detail_parts = [f"{inline_code(name)}: {count}" for name, count in entries]
|
|
494
484
|
|
|
495
485
|
return f"{line} ({', '.join(detail_parts)})"
|
|
496
486
|
|
|
@@ -610,10 +600,10 @@ def format_publication_dlp_details(signal: dict[str, Any] | None) -> str:
|
|
|
610
600
|
omitted = signal["omitted"]
|
|
611
601
|
carried = signal["carried_forward_comments"]
|
|
612
602
|
completeness = (
|
|
613
|
-
"One or more
|
|
614
|
-
"
|
|
603
|
+
"One or more public projection units were omitted. OCR coverage is reported "
|
|
604
|
+
"separately, and automatic approval remains unavailable."
|
|
615
605
|
if omitted["comments"] or omitted["warnings"]
|
|
616
|
-
else "The
|
|
606
|
+
else "The public projection changed, so automatic approval remains unavailable."
|
|
617
607
|
)
|
|
618
608
|
return "\n".join(
|
|
619
609
|
[
|
|
@@ -841,7 +831,11 @@ def format_reviewer_guide(
|
|
|
841
831
|
enumerate(comments),
|
|
842
832
|
key=lambda item: _guide_comment_rank(item[1], item[0]),
|
|
843
833
|
)
|
|
844
|
-
guide_comments =
|
|
834
|
+
guide_comments = (
|
|
835
|
+
[comment for _, comment in ranked_comments[:MAX_REVIEWER_GUIDE_COMMENTS]]
|
|
836
|
+
if len(comments) >= 2
|
|
837
|
+
else []
|
|
838
|
+
)
|
|
845
839
|
if guide_comments:
|
|
846
840
|
lines.append("")
|
|
847
841
|
lines.append("### Recommended focus areas")
|
|
@@ -890,6 +884,8 @@ def _review_outcome_line(
|
|
|
890
884
|
marker, status_text = "⚠️", "Review stopped at token budget"
|
|
891
885
|
elif partial_result:
|
|
892
886
|
marker, status_text = "⚠️", "Review incomplete"
|
|
887
|
+
elif outcome_status == "publication-filtered":
|
|
888
|
+
marker, status_text = "⚠️", "Review complete with publication filtering"
|
|
893
889
|
elif outcome_status in {"warning", "completed_with_warnings"} or warning_count:
|
|
894
890
|
marker, status_text = "⚠️", "Review complete with warnings"
|
|
895
891
|
elif has_finding_state:
|
|
@@ -933,6 +929,17 @@ def _review_outcome_line(
|
|
|
933
929
|
return f"{prefix}**{status_text} — {result_text}**"
|
|
934
930
|
|
|
935
931
|
|
|
932
|
+
def format_ocr_core_advisory(advisory: OcrToolkitAdvisory | None) -> str:
|
|
933
|
+
"""Render one validated numeric OCR advisory for Technical details only."""
|
|
934
|
+
|
|
935
|
+
if advisory is None:
|
|
936
|
+
return ""
|
|
937
|
+
return (
|
|
938
|
+
f"- OCR core advisory: background {advisory.actual} characters; recommended "
|
|
939
|
+
f"{advisory.recommended} characters; accepted by OCR core"
|
|
940
|
+
)
|
|
941
|
+
|
|
942
|
+
|
|
936
943
|
def summarize_result(
|
|
937
944
|
total: int,
|
|
938
945
|
inline_count: int,
|
|
@@ -944,6 +951,7 @@ def summarize_result(
|
|
|
944
951
|
tool_calls_summary: str = "",
|
|
945
952
|
mcp_usage_summary: str = "",
|
|
946
953
|
token_usage_summary: str = "",
|
|
954
|
+
ocr_core_advisory_summary: str = "",
|
|
947
955
|
publication_dlp_details: str = "",
|
|
948
956
|
reviewer_guide: str = "",
|
|
949
957
|
fallback_reasons: Mapping[str, int] | None = None,
|
|
@@ -1055,7 +1063,12 @@ def summarize_result(
|
|
|
1055
1063
|
)
|
|
1056
1064
|
if reasons:
|
|
1057
1065
|
technical.append(f"- Fallback reasons: {reasons}")
|
|
1058
|
-
for summary in (
|
|
1066
|
+
for summary in (
|
|
1067
|
+
mcp_usage_summary,
|
|
1068
|
+
tool_calls_summary,
|
|
1069
|
+
token_usage_summary,
|
|
1070
|
+
ocr_core_advisory_summary,
|
|
1071
|
+
):
|
|
1059
1072
|
if summary:
|
|
1060
1073
|
technical.append(summary)
|
|
1061
1074
|
technical.append(f"- Review mode: `{post_mode()}`")
|
{open_code_review_toolkit-0.8.2 → open_code_review_toolkit-0.8.4}/src/ocr_toolkit/posting/result.py
RENAMED
|
@@ -122,7 +122,10 @@ def _safe_detail(value: object, reason: str) -> str:
|
|
|
122
122
|
|
|
123
123
|
|
|
124
124
|
def normalize_coverage_diagnostics(
|
|
125
|
-
outcome: ReviewOutcome,
|
|
125
|
+
outcome: ReviewOutcome,
|
|
126
|
+
warnings: Sequence[Any],
|
|
127
|
+
*,
|
|
128
|
+
legacy_warning_fallback: bool = True,
|
|
126
129
|
) -> CoverageDiagnostics:
|
|
127
130
|
"""Normalize manifest failures or legacy warnings once at the posting boundary."""
|
|
128
131
|
|
|
@@ -136,7 +139,7 @@ def normalize_coverage_diagnostics(
|
|
|
136
139
|
)
|
|
137
140
|
for item in outcome.failed_items
|
|
138
141
|
)
|
|
139
|
-
elif outcome.kind == "partial":
|
|
142
|
+
elif outcome.kind == "partial" and legacy_warning_fallback:
|
|
140
143
|
for warning in warnings:
|
|
141
144
|
path = warning.get("file") or warning.get("path") if isinstance(warning, dict) else None
|
|
142
145
|
candidates.append((path, _legacy_reason(warning), ocr_warning_text(warning)))
|