open-code-review-toolkit 0.8.4__tar.gz → 0.8.6__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.4 → open_code_review_toolkit-0.8.6}/PKG-INFO +30 -3
- {open_code_review_toolkit-0.8.4 → open_code_review_toolkit-0.8.6}/README.md +29 -2
- {open_code_review_toolkit-0.8.4 → open_code_review_toolkit-0.8.6}/src/ocr_toolkit/_version.py +2 -2
- {open_code_review_toolkit-0.8.4 → open_code_review_toolkit-0.8.6}/src/ocr_toolkit/posting/formatting.py +10 -2
- {open_code_review_toolkit-0.8.4 → open_code_review_toolkit-0.8.6}/src/ocr_toolkit/posting/workflow.py +1 -1
- {open_code_review_toolkit-0.8.4 → open_code_review_toolkit-0.8.6}/src/ocr_toolkit/preflight.py +1 -1
- {open_code_review_toolkit-0.8.4 → open_code_review_toolkit-0.8.6}/src/ocr_toolkit/provider_failure.py +177 -31
- {open_code_review_toolkit-0.8.4 → open_code_review_toolkit-0.8.6}/src/ocr_toolkit/review_runner.py +35 -14
- {open_code_review_toolkit-0.8.4 → open_code_review_toolkit-0.8.6}/.gitignore +0 -0
- {open_code_review_toolkit-0.8.4 → open_code_review_toolkit-0.8.6}/LICENSE +0 -0
- {open_code_review_toolkit-0.8.4 → open_code_review_toolkit-0.8.6}/pyproject.toml +0 -0
- {open_code_review_toolkit-0.8.4 → open_code_review_toolkit-0.8.6}/src/ocr_toolkit/__init__.py +0 -0
- {open_code_review_toolkit-0.8.4 → open_code_review_toolkit-0.8.6}/src/ocr_toolkit/cli.py +0 -0
- {open_code_review_toolkit-0.8.4 → open_code_review_toolkit-0.8.6}/src/ocr_toolkit/common/__init__.py +0 -0
- {open_code_review_toolkit-0.8.4 → open_code_review_toolkit-0.8.6}/src/ocr_toolkit/common/filesystem.py +0 -0
- {open_code_review_toolkit-0.8.4 → open_code_review_toolkit-0.8.6}/src/ocr_toolkit/common/git.py +0 -0
- {open_code_review_toolkit-0.8.4 → open_code_review_toolkit-0.8.6}/src/ocr_toolkit/common/language.py +0 -0
- {open_code_review_toolkit-0.8.4 → open_code_review_toolkit-0.8.6}/src/ocr_toolkit/common/markdown.py +0 -0
- {open_code_review_toolkit-0.8.4 → open_code_review_toolkit-0.8.6}/src/ocr_toolkit/common/redaction.py +0 -0
- {open_code_review_toolkit-0.8.4 → open_code_review_toolkit-0.8.6}/src/ocr_toolkit/config_writer.py +0 -0
- {open_code_review_toolkit-0.8.4 → open_code_review_toolkit-0.8.6}/src/ocr_toolkit/configure.py +0 -0
- {open_code_review_toolkit-0.8.4 → open_code_review_toolkit-0.8.6}/src/ocr_toolkit/context/__init__.py +0 -0
- {open_code_review_toolkit-0.8.4 → open_code_review_toolkit-0.8.6}/src/ocr_toolkit/context/adapters.py +0 -0
- {open_code_review_toolkit-0.8.4 → open_code_review_toolkit-0.8.6}/src/ocr_toolkit/context/broker.py +0 -0
- {open_code_review_toolkit-0.8.4 → open_code_review_toolkit-0.8.6}/src/ocr_toolkit/context/contracts.py +0 -0
- {open_code_review_toolkit-0.8.4 → open_code_review_toolkit-0.8.6}/src/ocr_toolkit/context/dlp.py +0 -0
- {open_code_review_toolkit-0.8.4 → open_code_review_toolkit-0.8.6}/src/ocr_toolkit/context/mcp.py +0 -0
- {open_code_review_toolkit-0.8.4 → open_code_review_toolkit-0.8.6}/src/ocr_toolkit/context/policy.py +0 -0
- {open_code_review_toolkit-0.8.4 → open_code_review_toolkit-0.8.6}/src/ocr_toolkit/context/recognizers.py +0 -0
- {open_code_review_toolkit-0.8.4 → open_code_review_toolkit-0.8.6}/src/ocr_toolkit/context/store.py +0 -0
- {open_code_review_toolkit-0.8.4 → open_code_review_toolkit-0.8.6}/src/ocr_toolkit/evidence/__init__.py +0 -0
- {open_code_review_toolkit-0.8.4 → open_code_review_toolkit-0.8.6}/src/ocr_toolkit/evidence/__main__.py +0 -0
- {open_code_review_toolkit-0.8.4 → open_code_review_toolkit-0.8.6}/src/ocr_toolkit/evidence/actions.py +0 -0
- {open_code_review_toolkit-0.8.4 → open_code_review_toolkit-0.8.6}/src/ocr_toolkit/evidence/artifacts.py +0 -0
- {open_code_review_toolkit-0.8.4 → open_code_review_toolkit-0.8.6}/src/ocr_toolkit/evidence/categorize.py +0 -0
- {open_code_review_toolkit-0.8.4 → open_code_review_toolkit-0.8.6}/src/ocr_toolkit/evidence/collect.py +0 -0
- {open_code_review_toolkit-0.8.4 → open_code_review_toolkit-0.8.6}/src/ocr_toolkit/evidence/collectors/__init__.py +0 -0
- {open_code_review_toolkit-0.8.4 → open_code_review_toolkit-0.8.6}/src/ocr_toolkit/evidence/collectors/graphs.py +0 -0
- {open_code_review_toolkit-0.8.4 → open_code_review_toolkit-0.8.6}/src/ocr_toolkit/evidence/collectors/orchestration.py +0 -0
- {open_code_review_toolkit-0.8.4 → open_code_review_toolkit-0.8.6}/src/ocr_toolkit/evidence/collectors/projections.py +0 -0
- {open_code_review_toolkit-0.8.4 → open_code_review_toolkit-0.8.6}/src/ocr_toolkit/evidence/collectors/registry.py +0 -0
- {open_code_review_toolkit-0.8.4 → open_code_review_toolkit-0.8.6}/src/ocr_toolkit/evidence/collectors/sources.py +0 -0
- {open_code_review_toolkit-0.8.4 → open_code_review_toolkit-0.8.6}/src/ocr_toolkit/evidence/coverage.py +0 -0
- {open_code_review_toolkit-0.8.4 → open_code_review_toolkit-0.8.6}/src/ocr_toolkit/evidence/ecosystems/__init__.py +0 -0
- {open_code_review_toolkit-0.8.4 → open_code_review_toolkit-0.8.6}/src/ocr_toolkit/evidence/ecosystems/ansible/__init__.py +0 -0
- {open_code_review_toolkit-0.8.4 → open_code_review_toolkit-0.8.6}/src/ocr_toolkit/evidence/ecosystems/ansible/requirements.py +0 -0
- {open_code_review_toolkit-0.8.4 → open_code_review_toolkit-0.8.6}/src/ocr_toolkit/evidence/ecosystems/ansible/topology.py +0 -0
- {open_code_review_toolkit-0.8.4 → open_code_review_toolkit-0.8.6}/src/ocr_toolkit/evidence/ecosystems/contracts.py +0 -0
- {open_code_review_toolkit-0.8.4 → open_code_review_toolkit-0.8.6}/src/ocr_toolkit/evidence/ecosystems/go.py +0 -0
- {open_code_review_toolkit-0.8.4 → open_code_review_toolkit-0.8.6}/src/ocr_toolkit/evidence/ecosystems/javascript.py +0 -0
- {open_code_review_toolkit-0.8.4 → open_code_review_toolkit-0.8.6}/src/ocr_toolkit/evidence/ecosystems/php.py +0 -0
- {open_code_review_toolkit-0.8.4 → open_code_review_toolkit-0.8.6}/src/ocr_toolkit/evidence/ecosystems/python.py +0 -0
- {open_code_review_toolkit-0.8.4 → open_code_review_toolkit-0.8.6}/src/ocr_toolkit/evidence/frameworks/__init__.py +0 -0
- {open_code_review_toolkit-0.8.4 → open_code_review_toolkit-0.8.6}/src/ocr_toolkit/evidence/frameworks/contracts.py +0 -0
- {open_code_review_toolkit-0.8.4 → open_code_review_toolkit-0.8.6}/src/ocr_toolkit/evidence/frameworks/detection.py +0 -0
- {open_code_review_toolkit-0.8.4 → open_code_review_toolkit-0.8.6}/src/ocr_toolkit/evidence/frameworks/providers/__init__.py +0 -0
- {open_code_review_toolkit-0.8.4 → open_code_review_toolkit-0.8.6}/src/ocr_toolkit/evidence/frameworks/providers/frontend.py +0 -0
- {open_code_review_toolkit-0.8.4 → open_code_review_toolkit-0.8.6}/src/ocr_toolkit/evidence/frameworks/providers/go.py +0 -0
- {open_code_review_toolkit-0.8.4 → open_code_review_toolkit-0.8.6}/src/ocr_toolkit/evidence/frameworks/providers/php.py +0 -0
- {open_code_review_toolkit-0.8.4 → open_code_review_toolkit-0.8.6}/src/ocr_toolkit/evidence/frameworks/providers/python.py +0 -0
- {open_code_review_toolkit-0.8.4 → open_code_review_toolkit-0.8.6}/src/ocr_toolkit/evidence/frameworks/registry.py +0 -0
- {open_code_review_toolkit-0.8.4 → open_code_review_toolkit-0.8.6}/src/ocr_toolkit/evidence/frameworks/schema.py +0 -0
- {open_code_review_toolkit-0.8.4 → open_code_review_toolkit-0.8.6}/src/ocr_toolkit/evidence/frameworks/templates.py +0 -0
- {open_code_review_toolkit-0.8.4 → open_code_review_toolkit-0.8.6}/src/ocr_toolkit/evidence/infrastructure.py +0 -0
- {open_code_review_toolkit-0.8.4 → open_code_review_toolkit-0.8.6}/src/ocr_toolkit/evidence/invocation.py +0 -0
- {open_code_review_toolkit-0.8.4 → open_code_review_toolkit-0.8.6}/src/ocr_toolkit/evidence/mcp.py +0 -0
- {open_code_review_toolkit-0.8.4 → open_code_review_toolkit-0.8.6}/src/ocr_toolkit/evidence/model.py +0 -0
- {open_code_review_toolkit-0.8.4 → open_code_review_toolkit-0.8.6}/src/ocr_toolkit/evidence/policy/__init__.py +0 -0
- {open_code_review_toolkit-0.8.4 → open_code_review_toolkit-0.8.6}/src/ocr_toolkit/evidence/policy/contracts.py +0 -0
- {open_code_review_toolkit-0.8.4 → open_code_review_toolkit-0.8.6}/src/ocr_toolkit/evidence/policy/decisions.py +0 -0
- {open_code_review_toolkit-0.8.4 → open_code_review_toolkit-0.8.6}/src/ocr_toolkit/evidence/policy/guidance.py +0 -0
- {open_code_review_toolkit-0.8.4 → open_code_review_toolkit-0.8.6}/src/ocr_toolkit/evidence/policy/registry.py +0 -0
- {open_code_review_toolkit-0.8.4 → open_code_review_toolkit-0.8.6}/src/ocr_toolkit/evidence/policy/schema.py +0 -0
- {open_code_review_toolkit-0.8.4 → open_code_review_toolkit-0.8.6}/src/ocr_toolkit/evidence/policy/scopes.py +0 -0
- {open_code_review_toolkit-0.8.4 → open_code_review_toolkit-0.8.6}/src/ocr_toolkit/evidence/project.py +0 -0
- {open_code_review_toolkit-0.8.4 → open_code_review_toolkit-0.8.6}/src/ocr_toolkit/evidence/repository.py +0 -0
- {open_code_review_toolkit-0.8.4 → open_code_review_toolkit-0.8.6}/src/ocr_toolkit/evidence/review_context.py +0 -0
- {open_code_review_toolkit-0.8.4 → open_code_review_toolkit-0.8.6}/src/ocr_toolkit/evidence/store/__init__.py +0 -0
- {open_code_review_toolkit-0.8.4 → open_code_review_toolkit-0.8.6}/src/ocr_toolkit/evidence/store/atomic.py +0 -0
- {open_code_review_toolkit-0.8.4 → open_code_review_toolkit-0.8.6}/src/ocr_toolkit/evidence/store/contracts.py +0 -0
- {open_code_review_toolkit-0.8.4 → open_code_review_toolkit-0.8.6}/src/ocr_toolkit/evidence/store/core.py +0 -0
- {open_code_review_toolkit-0.8.4 → open_code_review_toolkit-0.8.6}/src/ocr_toolkit/evidence/store/readback.py +0 -0
- {open_code_review_toolkit-0.8.4 → open_code_review_toolkit-0.8.6}/src/ocr_toolkit/evidence/store/values.py +0 -0
- {open_code_review_toolkit-0.8.4 → open_code_review_toolkit-0.8.6}/src/ocr_toolkit/mcp_config.py +0 -0
- {open_code_review_toolkit-0.8.4 → open_code_review_toolkit-0.8.6}/src/ocr_toolkit/ocr_result.py +0 -0
- {open_code_review_toolkit-0.8.4 → open_code_review_toolkit-0.8.6}/src/ocr_toolkit/posting/__init__.py +0 -0
- {open_code_review_toolkit-0.8.4 → open_code_review_toolkit-0.8.6}/src/ocr_toolkit/posting/__main__.py +0 -0
- {open_code_review_toolkit-0.8.4 → open_code_review_toolkit-0.8.6}/src/ocr_toolkit/posting/approval.py +0 -0
- {open_code_review_toolkit-0.8.4 → open_code_review_toolkit-0.8.6}/src/ocr_toolkit/posting/comments.py +0 -0
- {open_code_review_toolkit-0.8.4 → open_code_review_toolkit-0.8.6}/src/ocr_toolkit/posting/gitlab.py +0 -0
- {open_code_review_toolkit-0.8.4 → open_code_review_toolkit-0.8.6}/src/ocr_toolkit/posting/gitlab_approval.py +0 -0
- {open_code_review_toolkit-0.8.4 → open_code_review_toolkit-0.8.6}/src/ocr_toolkit/posting/markers.py +0 -0
- {open_code_review_toolkit-0.8.4 → open_code_review_toolkit-0.8.6}/src/ocr_toolkit/posting/payloads.py +0 -0
- {open_code_review_toolkit-0.8.4 → open_code_review_toolkit-0.8.6}/src/ocr_toolkit/posting/reconciliation.py +0 -0
- {open_code_review_toolkit-0.8.4 → open_code_review_toolkit-0.8.6}/src/ocr_toolkit/posting/result.py +0 -0
- {open_code_review_toolkit-0.8.4 → open_code_review_toolkit-0.8.6}/src/ocr_toolkit/posting/settings.py +0 -0
- {open_code_review_toolkit-0.8.4 → open_code_review_toolkit-0.8.6}/src/ocr_toolkit/posting/snapshot.py +0 -0
- {open_code_review_toolkit-0.8.4 → open_code_review_toolkit-0.8.6}/src/ocr_toolkit/posting/suggestions.py +0 -0
- {open_code_review_toolkit-0.8.4 → open_code_review_toolkit-0.8.6}/src/ocr_toolkit/posting/transaction.py +0 -0
- {open_code_review_toolkit-0.8.4 → open_code_review_toolkit-0.8.6}/src/ocr_toolkit/pre_execution.py +0 -0
- {open_code_review_toolkit-0.8.4 → open_code_review_toolkit-0.8.6}/src/ocr_toolkit/provider_config.py +0 -0
- {open_code_review_toolkit-0.8.4 → open_code_review_toolkit-0.8.6}/src/ocr_toolkit/providers/__init__.py +0 -0
- {open_code_review_toolkit-0.8.4 → open_code_review_toolkit-0.8.6}/src/ocr_toolkit/providers/gitlab.py +0 -0
- {open_code_review_toolkit-0.8.4 → open_code_review_toolkit-0.8.6}/src/ocr_toolkit/providers/gitlab_context.py +0 -0
- {open_code_review_toolkit-0.8.4 → open_code_review_toolkit-0.8.6}/src/ocr_toolkit/providers/gitlab_discussions.py +0 -0
- {open_code_review_toolkit-0.8.4 → open_code_review_toolkit-0.8.6}/src/ocr_toolkit/providers/gitlab_identity.py +0 -0
- {open_code_review_toolkit-0.8.4 → open_code_review_toolkit-0.8.6}/src/ocr_toolkit/providers/gitlab_remediation.py +0 -0
- {open_code_review_toolkit-0.8.4 → open_code_review_toolkit-0.8.6}/src/ocr_toolkit/py.typed +0 -0
- {open_code_review_toolkit-0.8.4 → open_code_review_toolkit-0.8.6}/src/ocr_toolkit/result_contract.py +0 -0
- {open_code_review_toolkit-0.8.4 → open_code_review_toolkit-0.8.6}/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.6
|
|
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
|
|
@@ -238,18 +238,45 @@ Open Code Review Toolkit is an unofficial GitLab CI integration layer for [Aliba
|
|
|
238
238
|
|
|
239
239
|
## Install
|
|
240
240
|
|
|
241
|
-
|
|
241
|
+
Python 3.12 through 3.14 is supported. Install the toolkit CLI in an isolated tool
|
|
242
|
+
environment:
|
|
242
243
|
|
|
243
244
|
```console
|
|
245
|
+
uv tool install open-code-review-toolkit
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
If `uv` is unavailable, use `pip` only inside an activated virtual environment; do not
|
|
249
|
+
install the toolkit into a global Python environment:
|
|
250
|
+
|
|
251
|
+
```console
|
|
252
|
+
python -m venv .venv
|
|
253
|
+
. .venv/bin/activate
|
|
244
254
|
python -m pip install open-code-review-toolkit
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
The toolkit does not bundle OCR. Download the platform asset for exact
|
|
258
|
+
[Open Code Review 1.11.0](https://github.com/alibaba/open-code-review/releases/tag/v1.11.0),
|
|
259
|
+
verify it before installation against the [compatibility manifest](compatibility/ocr-support.json),
|
|
260
|
+
and place the verified binary on `PATH`. The current Linux amd64 digest is
|
|
261
|
+
`13f68cc2eca1a36d42140e9d37797b68fea5cbbf4b6345ec01ec1b06910fab60`; Darwin arm64 is
|
|
262
|
+
`ac8bf5a0fcd176bb9dcc15b169e90f4b52bf32787adef17a850489dbed97fb78`. The manifest owns
|
|
263
|
+
the other platform hashes.
|
|
264
|
+
|
|
265
|
+
Verify the installation without contacting an LLM provider:
|
|
266
|
+
|
|
267
|
+
```console
|
|
245
268
|
ocr --version
|
|
246
269
|
ocr-ci --help
|
|
247
270
|
```
|
|
248
271
|
|
|
272
|
+
`ocr --version` must report `open-code-review v1.11.0`; `ocr-ci --help` must exit
|
|
273
|
+
successfully. `ocr-ci preflight` is an operational configuration check, not the installation
|
|
274
|
+
smoke test.
|
|
275
|
+
|
|
249
276
|
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
277
|
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
278
|
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.
|
|
279
|
+
The current OCR 1.11.0 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
280
|
|
|
254
281
|
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
282
|
|
|
@@ -11,18 +11,45 @@ Open Code Review Toolkit is an unofficial GitLab CI integration layer for [Aliba
|
|
|
11
11
|
|
|
12
12
|
## Install
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
Python 3.12 through 3.14 is supported. Install the toolkit CLI in an isolated tool
|
|
15
|
+
environment:
|
|
15
16
|
|
|
16
17
|
```console
|
|
18
|
+
uv tool install open-code-review-toolkit
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
If `uv` is unavailable, use `pip` only inside an activated virtual environment; do not
|
|
22
|
+
install the toolkit into a global Python environment:
|
|
23
|
+
|
|
24
|
+
```console
|
|
25
|
+
python -m venv .venv
|
|
26
|
+
. .venv/bin/activate
|
|
17
27
|
python -m pip install open-code-review-toolkit
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
The toolkit does not bundle OCR. Download the platform asset for exact
|
|
31
|
+
[Open Code Review 1.11.0](https://github.com/alibaba/open-code-review/releases/tag/v1.11.0),
|
|
32
|
+
verify it before installation against the [compatibility manifest](compatibility/ocr-support.json),
|
|
33
|
+
and place the verified binary on `PATH`. The current Linux amd64 digest is
|
|
34
|
+
`13f68cc2eca1a36d42140e9d37797b68fea5cbbf4b6345ec01ec1b06910fab60`; Darwin arm64 is
|
|
35
|
+
`ac8bf5a0fcd176bb9dcc15b169e90f4b52bf32787adef17a850489dbed97fb78`. The manifest owns
|
|
36
|
+
the other platform hashes.
|
|
37
|
+
|
|
38
|
+
Verify the installation without contacting an LLM provider:
|
|
39
|
+
|
|
40
|
+
```console
|
|
18
41
|
ocr --version
|
|
19
42
|
ocr-ci --help
|
|
20
43
|
```
|
|
21
44
|
|
|
45
|
+
`ocr --version` must report `open-code-review v1.11.0`; `ocr-ci --help` must exit
|
|
46
|
+
successfully. `ocr-ci preflight` is an operational configuration check, not the installation
|
|
47
|
+
smoke test.
|
|
48
|
+
|
|
22
49
|
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
50
|
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
51
|
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.
|
|
52
|
+
The current OCR 1.11.0 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
53
|
|
|
27
54
|
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
55
|
|
{open_code_review_toolkit-0.8.4 → open_code_review_toolkit-0.8.6}/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.6'
|
|
22
|
+
__version_tuple__ = version_tuple = (0, 8, 6)
|
|
23
23
|
|
|
24
24
|
__commit_id__ = commit_id = None
|
|
@@ -650,10 +650,17 @@ SECURITY_SIGNAL_RE = re.compile(
|
|
|
650
650
|
r"(?i)\b("
|
|
651
651
|
r"security|credential|secret|token|password|private[_ -]?token|"
|
|
652
652
|
r"client[_ -]?secret|api[_ -]?key|authorization|auth|"
|
|
653
|
-
r"
|
|
653
|
+
r"xss|csrf|ssrf|rce|path traversal|host header|"
|
|
654
654
|
r"privilege|permission|access control|vault|leak"
|
|
655
655
|
r")\b"
|
|
656
656
|
)
|
|
657
|
+
SECURITY_INJECTION_RE = re.compile(
|
|
658
|
+
r"(?i)(?<!\w)(?:"
|
|
659
|
+
r"(?:os[\s_-]+)?command|shell(?:[\s_-]+command)?|sql|nosql|code|"
|
|
660
|
+
r"(?:server[\s_-]+side[\s_-]+)?template|prompt|ldap|xpath|crlf|"
|
|
661
|
+
r"(?:http[\s_-]+)?header|log|html|script|expression"
|
|
662
|
+
r")[\s_\-\u2010-\u2015]+injection(?!\w)"
|
|
663
|
+
)
|
|
657
664
|
|
|
658
665
|
|
|
659
666
|
HIGH_SIGNAL_RE = re.compile(r"(?i)\b(high|critical|blocker|security)\b")
|
|
@@ -674,7 +681,8 @@ def comment_signal_text(comment: dict[str, Any]) -> str:
|
|
|
674
681
|
def comment_has_security_signal(comment: dict[str, Any]) -> bool:
|
|
675
682
|
"""Return true only when OCR text explicitly carries security wording."""
|
|
676
683
|
|
|
677
|
-
|
|
684
|
+
text = comment_signal_text(comment)
|
|
685
|
+
return bool(SECURITY_SIGNAL_RE.search(text) or SECURITY_INJECTION_RE.search(text))
|
|
678
686
|
|
|
679
687
|
|
|
680
688
|
def comment_is_high_signal(comment: dict[str, Any]) -> bool:
|
|
@@ -1151,7 +1151,7 @@ def post_llm_provider_failure(
|
|
|
1151
1151
|
),
|
|
1152
1152
|
ProviderFailureReason.RATE_OR_SPENDING_LIMIT: (
|
|
1153
1153
|
"The LLM provider rejected the request under a rate or spending limit.",
|
|
1154
|
-
"
|
|
1154
|
+
"Try lowering `OCR_REVIEW_CONCURRENCY` and/or `OCR_LLM_MAX_COMPLETION_TOKENS`, then start a new merge request pipeline. If it still fails, check the provider's request and account limits.",
|
|
1155
1155
|
),
|
|
1156
1156
|
ProviderFailureReason.OVERLOADED: (
|
|
1157
1157
|
"The LLM provider reported that it was overloaded.",
|
|
@@ -4,6 +4,7 @@ from __future__ import annotations
|
|
|
4
4
|
|
|
5
5
|
from collections import Counter
|
|
6
6
|
from collections.abc import Mapping
|
|
7
|
+
from dataclasses import dataclass
|
|
7
8
|
from enum import StrEnum
|
|
8
9
|
from typing import Any
|
|
9
10
|
|
|
@@ -47,6 +48,55 @@ class ProviderFailureReason(StrEnum):
|
|
|
47
48
|
UNKNOWN = "unknown"
|
|
48
49
|
|
|
49
50
|
|
|
51
|
+
class ProviderFailureDetail(StrEnum):
|
|
52
|
+
"""Closed protocol detail safe for one toolkit-authored CI diagnostic."""
|
|
53
|
+
|
|
54
|
+
HTTP_BAD_REQUEST = "http-bad-request"
|
|
55
|
+
HTTP_UNAUTHORIZED = "http-unauthorized"
|
|
56
|
+
HTTP_PAYMENT_REQUIRED = "http-payment-required"
|
|
57
|
+
HTTP_FORBIDDEN = "http-forbidden"
|
|
58
|
+
HTTP_NOT_FOUND = "http-not-found"
|
|
59
|
+
HTTP_REQUEST_TIMEOUT = "http-request-timeout"
|
|
60
|
+
HTTP_CONFLICT = "http-conflict"
|
|
61
|
+
HTTP_CONTENT_TOO_LARGE = "http-content-too-large"
|
|
62
|
+
HTTP_UNPROCESSABLE_CONTENT = "http-unprocessable-content"
|
|
63
|
+
HTTP_RATE_LIMITED = "http-rate-limited"
|
|
64
|
+
HTTP_GATEWAY_TIMEOUT = "http-gateway-timeout"
|
|
65
|
+
HTTP_OVERLOADED = "http-overloaded"
|
|
66
|
+
HTTP_SERVER_ERROR = "http-server-error"
|
|
67
|
+
HTTP_NON_SUCCESS = "http-non-success"
|
|
68
|
+
RESPONSE_DECODE = "response-decode"
|
|
69
|
+
RESPONSE_STATUS = "response-status"
|
|
70
|
+
STREAM = "stream"
|
|
71
|
+
CANCELLED = "cancelled"
|
|
72
|
+
TIMEOUT = "timeout"
|
|
73
|
+
NETWORK = "network"
|
|
74
|
+
UNKNOWN = "unknown"
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
@dataclass(frozen=True)
|
|
78
|
+
class ProviderFailureProjection:
|
|
79
|
+
"""Validated provider-neutral failure state retained only for operator output."""
|
|
80
|
+
|
|
81
|
+
reason: ProviderFailureReason
|
|
82
|
+
details: tuple[tuple[ProviderFailureDetail, int], ...]
|
|
83
|
+
status_code: int | None
|
|
84
|
+
failed_requests: int
|
|
85
|
+
cancelled_requests: int
|
|
86
|
+
retried_requests: int
|
|
87
|
+
total_retries: int
|
|
88
|
+
recovered_requests: int
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
@dataclass(frozen=True)
|
|
92
|
+
class _AttemptFailure:
|
|
93
|
+
"""Internal terminal-attempt projection after retry-report validation."""
|
|
94
|
+
|
|
95
|
+
reason: ProviderFailureReason
|
|
96
|
+
detail: ProviderFailureDetail
|
|
97
|
+
status_code: int | None
|
|
98
|
+
|
|
99
|
+
|
|
50
100
|
class RetryReportError(Exception):
|
|
51
101
|
"""The private retry report is malformed or internally inconsistent."""
|
|
52
102
|
|
|
@@ -79,6 +129,32 @@ def _http_reason(status_code: int) -> ProviderFailureReason:
|
|
|
79
129
|
return ProviderFailureReason.REQUEST_REJECTED
|
|
80
130
|
|
|
81
131
|
|
|
132
|
+
def _http_detail(status_code: int) -> ProviderFailureDetail:
|
|
133
|
+
"""Map one bounded HTTP status to provider-neutral protocol detail."""
|
|
134
|
+
|
|
135
|
+
return {
|
|
136
|
+
400: ProviderFailureDetail.HTTP_BAD_REQUEST,
|
|
137
|
+
401: ProviderFailureDetail.HTTP_UNAUTHORIZED,
|
|
138
|
+
402: ProviderFailureDetail.HTTP_PAYMENT_REQUIRED,
|
|
139
|
+
403: ProviderFailureDetail.HTTP_FORBIDDEN,
|
|
140
|
+
404: ProviderFailureDetail.HTTP_NOT_FOUND,
|
|
141
|
+
408: ProviderFailureDetail.HTTP_REQUEST_TIMEOUT,
|
|
142
|
+
409: ProviderFailureDetail.HTTP_CONFLICT,
|
|
143
|
+
413: ProviderFailureDetail.HTTP_CONTENT_TOO_LARGE,
|
|
144
|
+
422: ProviderFailureDetail.HTTP_UNPROCESSABLE_CONTENT,
|
|
145
|
+
429: ProviderFailureDetail.HTTP_RATE_LIMITED,
|
|
146
|
+
504: ProviderFailureDetail.HTTP_GATEWAY_TIMEOUT,
|
|
147
|
+
529: ProviderFailureDetail.HTTP_OVERLOADED,
|
|
148
|
+
}.get(
|
|
149
|
+
status_code,
|
|
150
|
+
(
|
|
151
|
+
ProviderFailureDetail.HTTP_SERVER_ERROR
|
|
152
|
+
if 500 <= status_code <= 599
|
|
153
|
+
else ProviderFailureDetail.HTTP_NON_SUCCESS
|
|
154
|
+
),
|
|
155
|
+
)
|
|
156
|
+
|
|
157
|
+
|
|
82
158
|
def _validate_http_class(status_code: int, error_class: str, failure_phase: str) -> None:
|
|
83
159
|
"""Reject attempt fields that contradict OCR's status-derived v1 classifier."""
|
|
84
160
|
|
|
@@ -97,8 +173,8 @@ def _validate_http_class(status_code: int, error_class: str, failure_phase: str)
|
|
|
97
173
|
raise RetryReportError("retry report HTTP status contradicts its error class")
|
|
98
174
|
|
|
99
175
|
|
|
100
|
-
def
|
|
101
|
-
"""Validate one attempt and return its closed error
|
|
176
|
+
def _attempt_failure(attempt: Mapping[str, Any]) -> _AttemptFailure | None:
|
|
177
|
+
"""Validate one attempt and return its closed error projection, if any."""
|
|
102
178
|
|
|
103
179
|
outcome = attempt.get("outcome")
|
|
104
180
|
if outcome not in ATTEMPT_OUTCOMES:
|
|
@@ -117,13 +193,18 @@ def _attempt_reason(attempt: Mapping[str, Any]) -> ProviderFailureReason | None:
|
|
|
117
193
|
|
|
118
194
|
if status_value and not 200 <= status_value <= 299:
|
|
119
195
|
_validate_http_class(status_value, error_class, failure_phase)
|
|
120
|
-
return _http_reason(status_value)
|
|
196
|
+
return _AttemptFailure(_http_reason(status_value), _http_detail(status_value), status_value)
|
|
121
197
|
if status_value:
|
|
122
198
|
if failure_phase not in {"response_decode", "response_status", "stream"}:
|
|
123
199
|
raise RetryReportError("retry report successful HTTP status has invalid failure phase")
|
|
124
200
|
if error_class not in {"network", "provider", "unknown"}:
|
|
125
201
|
raise RetryReportError("retry report successful HTTP status has invalid error class")
|
|
126
|
-
|
|
202
|
+
detail = {
|
|
203
|
+
"response_decode": ProviderFailureDetail.RESPONSE_DECODE,
|
|
204
|
+
"response_status": ProviderFailureDetail.RESPONSE_STATUS,
|
|
205
|
+
"stream": ProviderFailureDetail.STREAM,
|
|
206
|
+
}[failure_phase]
|
|
207
|
+
return _AttemptFailure(ProviderFailureReason.INVALID_RESPONSE, detail, status_value)
|
|
127
208
|
if failure_phase == "http" or error_class in {
|
|
128
209
|
"authentication",
|
|
129
210
|
"overloaded",
|
|
@@ -132,18 +213,25 @@ def _attempt_reason(attempt: Mapping[str, Any]) -> ProviderFailureReason | None:
|
|
|
132
213
|
}:
|
|
133
214
|
raise RetryReportError("retry report transport failure has HTTP-only classification")
|
|
134
215
|
if error_class == "cancelled":
|
|
135
|
-
return
|
|
216
|
+
return _AttemptFailure(
|
|
217
|
+
ProviderFailureReason.CANCELLED, ProviderFailureDetail.CANCELLED, None
|
|
218
|
+
)
|
|
136
219
|
if error_class == "timeout":
|
|
137
|
-
return ProviderFailureReason.TIMEOUT
|
|
220
|
+
return _AttemptFailure(ProviderFailureReason.TIMEOUT, ProviderFailureDetail.TIMEOUT, None)
|
|
138
221
|
if error_class == "network":
|
|
139
222
|
if failure_phase in {"response_decode", "response_status", "stream"}:
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
223
|
+
detail = {
|
|
224
|
+
"response_decode": ProviderFailureDetail.RESPONSE_DECODE,
|
|
225
|
+
"response_status": ProviderFailureDetail.RESPONSE_STATUS,
|
|
226
|
+
"stream": ProviderFailureDetail.STREAM,
|
|
227
|
+
}[failure_phase]
|
|
228
|
+
return _AttemptFailure(ProviderFailureReason.INVALID_RESPONSE, detail, None)
|
|
229
|
+
return _AttemptFailure(ProviderFailureReason.NETWORK, ProviderFailureDetail.NETWORK, None)
|
|
230
|
+
return _AttemptFailure(ProviderFailureReason.UNKNOWN, ProviderFailureDetail.UNKNOWN, None)
|
|
143
231
|
|
|
144
232
|
|
|
145
|
-
def
|
|
146
|
-
"""Validate one logical request and
|
|
233
|
+
def _request_failure(request: Mapping[str, Any]) -> _AttemptFailure | None:
|
|
234
|
+
"""Validate one logical request and project only its terminal failure."""
|
|
147
235
|
|
|
148
236
|
outcome = request.get("outcome")
|
|
149
237
|
if outcome not in REQUEST_OUTCOMES:
|
|
@@ -151,7 +239,7 @@ def _request_reason(request: Mapping[str, Any]) -> ProviderFailureReason | None:
|
|
|
151
239
|
attempts = request.get("attempts")
|
|
152
240
|
if not isinstance(attempts, list) or not 0 < len(attempts) <= MAX_ATTEMPTS_PER_REQUEST:
|
|
153
241
|
raise RetryReportError("retry report request attempts are invalid")
|
|
154
|
-
|
|
242
|
+
failures: list[_AttemptFailure | None] = []
|
|
155
243
|
for index, attempt in enumerate(attempts, start=1):
|
|
156
244
|
if (
|
|
157
245
|
not isinstance(attempt, dict)
|
|
@@ -159,24 +247,26 @@ def _request_reason(request: Mapping[str, Any]) -> ProviderFailureReason | None:
|
|
|
159
247
|
or attempt.get("attempt") != index
|
|
160
248
|
):
|
|
161
249
|
raise RetryReportError("retry report attempt order is invalid")
|
|
162
|
-
|
|
250
|
+
failures.append(_attempt_failure(attempt))
|
|
163
251
|
if outcome == "succeeded":
|
|
164
|
-
if len(attempts) < 2 or any(
|
|
252
|
+
if len(attempts) < 2 or any(failure is not None for failure in failures):
|
|
165
253
|
raise RetryReportError("retry report succeeded request is inconsistent")
|
|
166
254
|
return None
|
|
167
255
|
if outcome == "recovered":
|
|
168
|
-
if
|
|
256
|
+
if failures[-1] is not None or not any(failure is not None for failure in failures[:-1]):
|
|
169
257
|
raise RetryReportError("retry report recovered request is inconsistent")
|
|
170
258
|
return None
|
|
171
259
|
if outcome == "cancelled":
|
|
172
|
-
return
|
|
173
|
-
|
|
260
|
+
return _AttemptFailure(
|
|
261
|
+
ProviderFailureReason.CANCELLED, ProviderFailureDetail.CANCELLED, None
|
|
262
|
+
)
|
|
263
|
+
if failures[-1] is None:
|
|
174
264
|
raise RetryReportError("retry report failed request ends in success")
|
|
175
|
-
return
|
|
265
|
+
return failures[-1]
|
|
176
266
|
|
|
177
267
|
|
|
178
|
-
def
|
|
179
|
-
"""Validate retry-report v1 and return one closed
|
|
268
|
+
def parse_retry_report_projection(result: object) -> ProviderFailureProjection | None:
|
|
269
|
+
"""Validate retry-report v1 and return one closed numeric failure projection."""
|
|
180
270
|
|
|
181
271
|
if not isinstance(result, dict):
|
|
182
272
|
raise RetryReportError("OCR result must be an object")
|
|
@@ -222,26 +312,82 @@ def parse_retry_report_failure(result: object) -> ProviderFailureReason | None:
|
|
|
222
312
|
if total_requests < len(requests):
|
|
223
313
|
raise RetryReportError("OCR retry report total_requests is inconsistent")
|
|
224
314
|
for field, expected in expected_counts.items():
|
|
225
|
-
|
|
315
|
+
maximum = (
|
|
316
|
+
MAX_RETRY_REQUESTS * (MAX_ATTEMPTS_PER_REQUEST - 1)
|
|
317
|
+
if field == "total_retries"
|
|
318
|
+
else MAX_RETRY_REQUESTS
|
|
319
|
+
)
|
|
320
|
+
if _bounded_count(report.get(field), field, maximum=maximum) != expected:
|
|
226
321
|
raise RetryReportError(f"OCR retry report {field} is inconsistent")
|
|
227
322
|
|
|
228
|
-
|
|
323
|
+
failures: list[_AttemptFailure] = []
|
|
229
324
|
for request in requests:
|
|
230
325
|
if not isinstance(request, dict):
|
|
231
326
|
raise RetryReportError("OCR retry report request is invalid")
|
|
232
|
-
|
|
233
|
-
if
|
|
234
|
-
|
|
235
|
-
if not
|
|
327
|
+
failure = _request_failure(request)
|
|
328
|
+
if failure is not None:
|
|
329
|
+
failures.append(failure)
|
|
330
|
+
if not failures:
|
|
236
331
|
return None
|
|
237
|
-
|
|
238
|
-
|
|
332
|
+
reason_counts = Counter(failure.reason for failure in failures)
|
|
333
|
+
reason = next(iter(reason_counts)) if len(reason_counts) == 1 else ProviderFailureReason.MIXED
|
|
334
|
+
detail_counts = Counter(failure.detail for failure in failures)
|
|
335
|
+
statuses = {failure.status_code for failure in failures}
|
|
336
|
+
shared_status = next(iter(statuses)) if len(statuses) == 1 else None
|
|
337
|
+
return ProviderFailureProjection(
|
|
338
|
+
reason=reason,
|
|
339
|
+
details=tuple(sorted(detail_counts.items(), key=lambda item: item[0].value)),
|
|
340
|
+
status_code=shared_status,
|
|
341
|
+
failed_requests=expected_counts["failed_requests"],
|
|
342
|
+
cancelled_requests=expected_counts["cancelled_requests"],
|
|
343
|
+
retried_requests=expected_counts["retried_requests"],
|
|
344
|
+
total_retries=expected_counts["total_retries"],
|
|
345
|
+
recovered_requests=expected_counts["recovered_requests"],
|
|
346
|
+
)
|
|
239
347
|
|
|
240
348
|
|
|
241
|
-
def
|
|
242
|
-
"""
|
|
349
|
+
def parse_retry_report_failure(result: object) -> ProviderFailureReason | None:
|
|
350
|
+
"""Validate retry-report v1 and return one closed terminal failure reason."""
|
|
351
|
+
|
|
352
|
+
projection = parse_retry_report_projection(result)
|
|
353
|
+
return projection.reason if projection is not None else None
|
|
354
|
+
|
|
355
|
+
|
|
356
|
+
def render_provider_diagnostics(projection: ProviderFailureProjection) -> str:
|
|
357
|
+
"""Render one bounded deterministic CI line from a validated projection."""
|
|
358
|
+
|
|
359
|
+
fields = [f"summary={projection.reason.value}"]
|
|
360
|
+
if len(projection.details) == 1:
|
|
361
|
+
fields.append(f"detail={projection.details[0][0].value}")
|
|
362
|
+
else:
|
|
363
|
+
details = ",".join(f"{detail.value}:{count}" for detail, count in projection.details)
|
|
364
|
+
fields.append(f"details={details}")
|
|
365
|
+
if projection.status_code is not None:
|
|
366
|
+
fields.append(f"status={projection.status_code}")
|
|
367
|
+
for name in (
|
|
368
|
+
"failed_requests",
|
|
369
|
+
"cancelled_requests",
|
|
370
|
+
"retried_requests",
|
|
371
|
+
"total_retries",
|
|
372
|
+
"recovered_requests",
|
|
373
|
+
):
|
|
374
|
+
value = getattr(projection, name)
|
|
375
|
+
if value:
|
|
376
|
+
fields.append(f"{name}={value}")
|
|
377
|
+
return "OCR provider diagnostics: " + " ".join(fields)
|
|
378
|
+
|
|
379
|
+
|
|
380
|
+
def provider_failure_projection(result: object) -> ProviderFailureProjection | None:
|
|
381
|
+
"""Return closed diagnostics, degrading malformed private data to unavailable."""
|
|
243
382
|
|
|
244
383
|
try:
|
|
245
|
-
return
|
|
384
|
+
return parse_retry_report_projection(result)
|
|
246
385
|
except RetryReportError:
|
|
247
386
|
return None
|
|
387
|
+
|
|
388
|
+
|
|
389
|
+
def provider_failure_reason(result: object) -> ProviderFailureReason | None:
|
|
390
|
+
"""Return a closed reason, degrading malformed private diagnostics to unavailable."""
|
|
391
|
+
|
|
392
|
+
projection = provider_failure_projection(result)
|
|
393
|
+
return projection.reason if projection is not None else None
|
{open_code_review_toolkit-0.8.4 → open_code_review_toolkit-0.8.6}/src/ocr_toolkit/review_runner.py
RENAMED
|
@@ -99,7 +99,11 @@ from ocr_toolkit.pre_execution import (
|
|
|
99
99
|
PreExecutionStatusError,
|
|
100
100
|
write_pre_execution_status,
|
|
101
101
|
)
|
|
102
|
-
from ocr_toolkit.provider_failure import
|
|
102
|
+
from ocr_toolkit.provider_failure import (
|
|
103
|
+
ProviderFailureProjection,
|
|
104
|
+
provider_failure_projection,
|
|
105
|
+
render_provider_diagnostics,
|
|
106
|
+
)
|
|
103
107
|
from ocr_toolkit.providers.gitlab import (
|
|
104
108
|
GitLabProviderError,
|
|
105
109
|
acquire_review_snapshot,
|
|
@@ -150,6 +154,16 @@ QUARANTINE_COMMENT_FIELDS = frozenset(
|
|
|
150
154
|
"suggestion_code",
|
|
151
155
|
}
|
|
152
156
|
)
|
|
157
|
+
PROVIDER_PRIVATE_RESULT_KEYS = frozenset(
|
|
158
|
+
{
|
|
159
|
+
"encrypted_content",
|
|
160
|
+
"native_payload",
|
|
161
|
+
"reasoning_content",
|
|
162
|
+
"redacted_thinking",
|
|
163
|
+
"thinking",
|
|
164
|
+
"tool_choice",
|
|
165
|
+
}
|
|
166
|
+
)
|
|
153
167
|
|
|
154
168
|
|
|
155
169
|
class ReviewRunnerError(Exception):
|
|
@@ -459,8 +473,10 @@ def _dlp_reasons(
|
|
|
459
473
|
while stack:
|
|
460
474
|
nested = stack.pop()
|
|
461
475
|
if isinstance(nested, dict):
|
|
462
|
-
|
|
463
|
-
|
|
476
|
+
for key, value in nested.items():
|
|
477
|
+
if isinstance(key, str) and key in PROVIDER_PRIVATE_RESULT_KEYS:
|
|
478
|
+
reasons["invalid_text"] += 1
|
|
479
|
+
stack.extend((key, value))
|
|
464
480
|
elif isinstance(nested, list):
|
|
465
481
|
stack.extend(nested)
|
|
466
482
|
elif isinstance(nested, str):
|
|
@@ -689,6 +705,11 @@ def _sanitize_nonpublication_fields(
|
|
|
689
705
|
child_path = (*path, key)
|
|
690
706
|
if isinstance(source, dict):
|
|
691
707
|
assert isinstance(key, str)
|
|
708
|
+
if key in PROVIDER_PRIVATE_RESULT_KEYS and not _is_publication_sink_path(
|
|
709
|
+
child_path
|
|
710
|
+
):
|
|
711
|
+
redacted_fields += 1
|
|
712
|
+
continue
|
|
692
713
|
if not _is_publication_sink_path(
|
|
693
714
|
child_path
|
|
694
715
|
) and not _is_static_public_tool_key_path(child_path):
|
|
@@ -890,7 +911,7 @@ def _publication_projection(
|
|
|
890
911
|
sanitized, private_reasons, redacted_fields = _sanitize_nonpublication_fields(
|
|
891
912
|
payload, budgets=budgets, matcher=matcher
|
|
892
913
|
)
|
|
893
|
-
if not sink_reasons and not private_reasons:
|
|
914
|
+
if not sink_reasons and not private_reasons and redacted_fields == 0:
|
|
894
915
|
return payload, {"state": "passed"}, False
|
|
895
916
|
|
|
896
917
|
try:
|
|
@@ -928,6 +949,8 @@ def _publication_projection(
|
|
|
928
949
|
projected["summary"] = {"budget_exceeded": True}
|
|
929
950
|
else:
|
|
930
951
|
reasons = sink_reasons + private_reasons
|
|
952
|
+
if not reasons:
|
|
953
|
+
return sanitized, {"state": "passed"}, False
|
|
931
954
|
return (
|
|
932
955
|
sanitized,
|
|
933
956
|
{
|
|
@@ -2081,14 +2104,16 @@ def read_stderr_excerpt(stderr_path: Path, max_chars: int = DEFAULT_DIAGNOSTIC_C
|
|
|
2081
2104
|
return redact_sensitive(text)[:max_chars]
|
|
2082
2105
|
|
|
2083
2106
|
|
|
2084
|
-
def
|
|
2085
|
-
|
|
2107
|
+
def _closed_provider_failure_projection(
|
|
2108
|
+
result_path: Path,
|
|
2109
|
+
) -> ProviderFailureProjection | None:
|
|
2110
|
+
"""Return only validated retry-report diagnostics from one private result."""
|
|
2086
2111
|
|
|
2087
2112
|
try:
|
|
2088
2113
|
result = load_ocr_result(result_path)
|
|
2089
2114
|
except (OcrResultMalformed, OcrResultMissing, OcrResultTooLarge):
|
|
2090
2115
|
return None
|
|
2091
|
-
return
|
|
2116
|
+
return provider_failure_projection(result)
|
|
2092
2117
|
|
|
2093
2118
|
|
|
2094
2119
|
def _resolve_ocr_binary() -> str:
|
|
@@ -2155,13 +2180,9 @@ def run_review(
|
|
|
2155
2180
|
|
|
2156
2181
|
if completed.returncode != 0:
|
|
2157
2182
|
print(f"Open Code Review exited with code {completed.returncode}.", file=sys.stderr)
|
|
2158
|
-
|
|
2159
|
-
if
|
|
2160
|
-
print(
|
|
2161
|
-
f"OCR provider failure classified as {provider_reason.value}; "
|
|
2162
|
-
"private diagnostics remain in the owner-only artifacts.",
|
|
2163
|
-
file=sys.stderr,
|
|
2164
|
-
)
|
|
2183
|
+
provider_projection = _closed_provider_failure_projection(result_path)
|
|
2184
|
+
if provider_projection is not None:
|
|
2185
|
+
print(render_provider_diagnostics(provider_projection), file=sys.stderr)
|
|
2165
2186
|
else:
|
|
2166
2187
|
excerpt = read_stderr_excerpt(stderr_path)
|
|
2167
2188
|
if excerpt:
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{open_code_review_toolkit-0.8.4 → open_code_review_toolkit-0.8.6}/src/ocr_toolkit/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{open_code_review_toolkit-0.8.4 → open_code_review_toolkit-0.8.6}/src/ocr_toolkit/common/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{open_code_review_toolkit-0.8.4 → open_code_review_toolkit-0.8.6}/src/ocr_toolkit/common/git.py
RENAMED
|
File without changes
|
{open_code_review_toolkit-0.8.4 → open_code_review_toolkit-0.8.6}/src/ocr_toolkit/common/language.py
RENAMED
|
File without changes
|
{open_code_review_toolkit-0.8.4 → open_code_review_toolkit-0.8.6}/src/ocr_toolkit/common/markdown.py
RENAMED
|
File without changes
|
|
File without changes
|
{open_code_review_toolkit-0.8.4 → open_code_review_toolkit-0.8.6}/src/ocr_toolkit/config_writer.py
RENAMED
|
File without changes
|
{open_code_review_toolkit-0.8.4 → open_code_review_toolkit-0.8.6}/src/ocr_toolkit/configure.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{open_code_review_toolkit-0.8.4 → open_code_review_toolkit-0.8.6}/src/ocr_toolkit/context/broker.py
RENAMED
|
File without changes
|
|
File without changes
|
{open_code_review_toolkit-0.8.4 → open_code_review_toolkit-0.8.6}/src/ocr_toolkit/context/dlp.py
RENAMED
|
File without changes
|
{open_code_review_toolkit-0.8.4 → open_code_review_toolkit-0.8.6}/src/ocr_toolkit/context/mcp.py
RENAMED
|
File without changes
|
{open_code_review_toolkit-0.8.4 → open_code_review_toolkit-0.8.6}/src/ocr_toolkit/context/policy.py
RENAMED
|
File without changes
|
|
File without changes
|
{open_code_review_toolkit-0.8.4 → open_code_review_toolkit-0.8.6}/src/ocr_toolkit/context/store.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{open_code_review_toolkit-0.8.4 → open_code_review_toolkit-0.8.6}/src/ocr_toolkit/evidence/mcp.py
RENAMED
|
File without changes
|
{open_code_review_toolkit-0.8.4 → open_code_review_toolkit-0.8.6}/src/ocr_toolkit/evidence/model.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{open_code_review_toolkit-0.8.4 → open_code_review_toolkit-0.8.6}/src/ocr_toolkit/mcp_config.py
RENAMED
|
File without changes
|
{open_code_review_toolkit-0.8.4 → open_code_review_toolkit-0.8.6}/src/ocr_toolkit/ocr_result.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{open_code_review_toolkit-0.8.4 → open_code_review_toolkit-0.8.6}/src/ocr_toolkit/posting/gitlab.py
RENAMED
|
File without changes
|
|
File without changes
|
{open_code_review_toolkit-0.8.4 → open_code_review_toolkit-0.8.6}/src/ocr_toolkit/posting/markers.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{open_code_review_toolkit-0.8.4 → open_code_review_toolkit-0.8.6}/src/ocr_toolkit/posting/result.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{open_code_review_toolkit-0.8.4 → open_code_review_toolkit-0.8.6}/src/ocr_toolkit/pre_execution.py
RENAMED
|
File without changes
|
{open_code_review_toolkit-0.8.4 → open_code_review_toolkit-0.8.6}/src/ocr_toolkit/provider_config.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{open_code_review_toolkit-0.8.4 → open_code_review_toolkit-0.8.6}/src/ocr_toolkit/result_contract.py
RENAMED
|
File without changes
|
{open_code_review_toolkit-0.8.4 → open_code_review_toolkit-0.8.6}/src/ocr_toolkit/result_usage.py
RENAMED
|
File without changes
|