From 123c331a8ea47e8ac95c2f7acaac8c6bba56e515 Mon Sep 17 00:00:00 2001 From: xeonvs <11463419+xeonvs@users.noreply.github.com> Date: Thu, 27 Aug 2026 10:44:56 +0200 Subject: [PATCH 1/7] Plan toolkit 0.8.5 work --- PLANS.md | 265 ++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 264 insertions(+), 1 deletion(-) diff --git a/PLANS.md b/PLANS.md index ca171a2..c67aab0 100644 --- a/PLANS.md +++ b/PLANS.md @@ -4,4 +4,267 @@ Use this file for active or blocked repository work. Update it before implementa ## Active Work -No active or blocked repository work. +### Toolkit 0.8.5 - provider diagnostics, OCR 1.10.2, and compatibility scheduling + +Status: active + +#### Goal + +Deliver a production-ready Draft feature pull request for toolkit 0.8.5 that implements +issue #149's bounded provider diagnostics, qualifies and adopts OCR 1.10.2, moves the +daily compatibility discovery after the observed upstream release window, reconciles +upstream capabilities with the durable backlog, and leaves stable publication deferred. + +#### Plan Origin + +`plan_mode_approved` + +#### Requested Scope + +- Take open issue #149 and the canonical OCR 1.10.2 compatibility issue into one + `v0.8.5` milestone and assign them to the repository owner. +- Change the single daily compatibility cron from `05:41 UTC` to `07:15 UTC`, retaining + manual dispatch as the recovery path. +- Manually qualify exact OCR 1.10.2 rather than waiting for the next scheduled run. +- Promote the checksum-pinned compatibility manifest, preflight, GitLab example, current + documentation, and effective Rules contract to OCR 1.10.2. +- Update the PATH-effective local Darwin arm64 OCR to 1.10.2 atomically after checksum + verification and no-LLM isolated checks. +- Add one bounded deterministic CI diagnostic line derived only from a fully validated + `ocr.llm-retry-report/v1`, without changing the merge-request summary structure. +- Reconcile every relevant OCR 1.10.2 capability and #149 outcome against backlog goals, + activation triggers, dependencies, and acceptance criteria. +- Finish at a green Draft feature PR. Do not merge, prepare a release PR, tag, or publish + toolkit 0.8.5. + +Release classification: `release-required`; target stable version: `0.8.5`; delivery +state for this task: `release-deferred` at the green Draft PR boundary. + +#### Requirement Traceability + +| Requirement | Outcome | Queue | Verification | +| --- | --- | --- | --- | +| `REQ-001` | Start from synchronized released `v0.8.4`, materialize this complete plan first, and open a Draft PR after one signed planning commit/push. | `WQ-01` | Plan fidelity, signed commit, remote branch, Draft readback | +| `REQ-002` | Create milestone `v0.8.5`; assign/milestone #149 and the canonical OCR 1.10.2 issue while leaving both open. | `WQ-02` | GitHub API readback | +| `REQ-003` | Run daily compatibility discovery at `07:15 UTC` with manual recovery retained. | `WQ-03` | Exact workflow contract test and documentation test | +| `REQ-004` | Qualify and adopt checksum-pinned OCR 1.10.2 without weakening compatibility gates. | `WQ-02`, `WQ-03` | Hosted run, evidence/manifest validation, exact hashes, focused probes | +| `REQ-005` | Classify every upstream 1.10.2 change and reconcile any completed or partially satisfied backlog item. | `WQ-03` | Source audit, backlog/roadmap/strategy consistency review | +| `REQ-006` | Update local OCR to 1.10.2 without LLM calls or user configuration changes. | `WQ-03` | Binary digest/version/help and isolated Solidity/Vyper preview | +| `REQ-007` | Produce at most one closed numeric provider diagnostic line from a strictly validated retry report. | `WQ-04` | Parser, renderer, bounds, aggregation, and hostile-input tests | +| `REQ-008` | Preserve GitLab summary shape, prior review, DLP, receipt, telemetry, and approval boundaries while improving remediation text. | `WQ-04` | Posting/review/approval/privacy regressions | +| `REQ-009` | Document current behavior and classify dependency, Rules, feature, and scheduling changes accurately for humans and release agents. | `WQ-03`, `WQ-05` | Towncrier draft and documentation contract tests | +| `REQ-010` | Complete one local final gate, one final push, hosted CI reconciliation, and exact Draft handoff state. | `WQ-05`, `WQ-06` | Quality/coverage/Gitleaks/checks, PR/head/tree/status readback | + +#### Explicit Non-Goals + +- No real LLM or provider request, local model peer, OCR config/credential change, or + caller-owned `HOME` use. +- No second scheduled compatibility run, dynamically calculated cron, polling service, + or general provider-diagnostics framework. +- No new environment variable, public OCR result schema, receipt field, telemetry field, + DLP input, or approval signal. +- No raw provider response parsing or publication of provider/model/task identity, + response text, headers, URLs, request IDs, paths, warnings, or stderr. +- No toolkit consumption of OCR's GitHub Action checkpoint ranges or `ocr session + compare`; resemblance alone does not activate or close backlog work. +- No mechanical test-directory reorganization, production refactor solely for coverage, + stable release, issue closure, milestone closure, merge, tag, or registry publication. + +#### Constraints + +- Keep one owner for retry-report validation and let review/post consume the same closed + projection; provider-controlled strings never cross that boundary. +- The GitLab renderer consumes only the existing closed reason. Numeric diagnostics stay + in toolkit-controlled CI output and cannot affect DLP, receipt v5, findings, severity, + suppression, resolution, or automatic approval. +- Only positive/bounded schema counts and HTTP statuses `100..599` are admitted. + Malformed, oversized, contradictory, unknown-version, or unsupported reports retain the + existing generic fail-closed behavior. +- Omit unavailable and zero counters. Emit a single `detail=` for one terminal category, + deterministic `details=:,...` for mixed categories, and `status=` only + for one shared HTTP status. +- Use `http-payment-required` for 402 and `http-rate-limited` for 429 without claiming a + provider business cause. Other details remain closed and provider-neutral. +- Recovered requests affect aggregate counts but never the terminal failure reason. +- A backlog item becomes completed only when its acceptance criteria are met. Partial + overlap updates the item's upstream overlap and remaining criteria without a false + completion claim. +- Every production/test function added in this work receives a purpose-focused docstring. +- Before each signed logical commit: focused tests, complete slice diff review, + trust/data-flow/privacy review, requirement/backlog reconciliation, and + `git diff --check`. +- After the initial Draft push, do not push again until local implementation and the final + gate are complete. + +#### Inputs And Sources + +- Approved implementation plan in the preceding Plan Mode conversation. +- Open toolkit issue #149 and released toolkit `v0.8.4`/next line `0.8.5`. +- Official OCR v1.10.2 release, release assets, checksums, comparison with v1.10.1, and + upstream PRs #961, #945, #946, #1066, and #1067. +- Current `compatibility/ocr-support.json`, compatibility workflow, evidence harness, + provider-failure parser, review runner, posting workflow, tests, public docs, strategy, + roadmap, and backlog. +- Observed 30-release sample: approximately 5.35 stable releases/week, 27.3-hour median + interval, and long-window publish density around 06:43 UTC. OCR 1.10.2 was published at + 05:49 UTC after the current 05:41 cron; no 2026-08-27 scheduled run existed by 08:29 UTC. +- Exact release anchors: Linux amd64 + `e9205614f80e009ee7b1f444c9da08486fb9ff6db022954fe9203d923ab720b2`, Darwin arm64 + `74fc7bcc0e6d0790c5ca033fd82a5474b6f05d443ed51a26a6f61c0cac6589fd`, and + `sha256sum.txt` `b5176aaa04a7f00bd84dd61556ca29e6cbdfcfe64cc50af6653163d9be4e7654`. + +#### User Decisions And Answers + +- Delivery ends at a green Draft PR; stable toolkit 0.8.5 publication is deferred. +- Use one daily `07:15 UTC` compatibility check, not two checks. +- Update any backlog item that toolkit or OCR actually closes; update partial overlap + truthfully and keep unmet work open. +- Update the local OCR binary, but skip local LLM execution. +- Work efficiently in complete logical commits with self-review before each commit. + +#### Completed Baseline State + +- `main` is synchronized and clean at released toolkit v0.8.4 commit `299e7b1`. +- `.next-version` is `0.8.5`; `PLANS.md` contained no active work before this plan. +- Issue #149 is the only open toolkit issue; no open PR or `v0.8.5` milestone exists. +- OCR compatibility is active, daily at `05:41 UTC`, and supports exact manual tags. +- The manifest recommends OCR 1.10.1; local `/opt/homebrew/bin/ocr` reports 1.10.1. +- Existing retry-report v1 validation already maps terminal failures to a closed public + reason but discards safe aggregate/status detail. +- Repository workflow audit classified the repository as mature with canonical plan, + backlog, principle, instruction, and documentation-index owners present. + +#### Current Work Queue + +| Queue | Status | Deliverable | +| --- | --- | --- | +| `WQ-01` | `in_progress` | Review this plan, pass fidelity/diff checks, commit it signed, push once, and open the Draft PR. | +| `WQ-02` | `pending` | Create/assign milestone coordination and manually run exact OCR 1.10.2 qualification to create the canonical issue. | +| `WQ-03` | `pending` | Implement schedule, OCR evidence/pins/current docs/Rules changes, semantic audit, backlog reconciliation, and local no-LLM OCR update. | +| `WQ-04` | `pending` | Implement the single closed provider diagnostic projection/renderer and GitLab remediation with boundary regressions. | +| `WQ-05` | `pending` | Finalize changelog/docs/plan truth and run the complete local gate once. | +| `WQ-06` | `pending` | Push the complete history once, reconcile hosted CI, update Draft/issue coordination with exact evidence, and verify final state. | + +#### Locked Decisions + +- Branch: `codex/v0.8.5-provider-diagnostics-ocr-1.10.2`; Draft base: protected `main`. +- Schedule: `15 7 * * *`; `workflow_dispatch` remains unchanged. +- Provider data flow: + `private OCR result -> bounded retry-report parser -> closed numeric projection ->` + `one toolkit-authored CI line`; GitLab continues to receive only the public reason. +- Example single-detail output: + `OCR provider diagnostics: summary=rate-or-spending-limit detail=http-rate-limited status=429 failed_requests=1 retried_requests=1 total_retries=2`. +- Mixed output uses deterministic category counts and omits a non-uniform status. +- Rate/spending remediation recommends lowering `OCR_REVIEW_CONCURRENCY` and/or + `OCR_LLM_MAX_COMPLETION_TOKENS`, starting a new MR pipeline, then checking provider + request/account limits; it never states the cap is the proven cause. +- OCR 1.10.2 Solidity/Vyper support is a Rules change. Pin/evidence/schedule work is + Maintenance. #149 is a Feature. Do not combine or misclassify these release notes. +- BL-021 remains conditional because an upstream GitHub Action is not a toolkit forge + adapter. BL-010 is not activated by review-language rules alone. #149 remains bounded + operator output and does not reopen the completed telemetry/export audit. + +#### Verification + +- Focused workflow/compatibility tests for exact cron, manual dispatch, evidence, hashes, + manifest/preflight/example consistency, and Rules documentation. +- No-LLM exact OCR 1.10.2 version/help and Solidity/Vyper selection/rule preview in an + isolated temporary home. +- Provider parser/renderer cases for 400, 401, 402, 403, 404, 408, 409, 413, 422, 429, + 5xx, 529, timeout, network, response decode/status/stream, cancellation, retries, + recovery, grouping/grace-round records, mixed aggregation, bounds, zero omission, + malformed/oversized/contradictory inputs, and deterministic rendering. +- Privacy regressions prove credentials, provider body/code, URL, model, request ID, task + identity, paths, warnings, and stderr do not enter toolkit-controlled logs, GitLab + notes, receipts, DLP signals, or approval inputs. +- Lifecycle regressions prove previous review preservation, no failed-result findings, + no approval, unchanged public classification, and strict/non-strict posting behavior. +- Final local gate once: `scripts/quality.sh check`, all coverage floors, + `scripts/ocr_compat.py validate`, lock check, Towncrier draft, `scripts/gitleaks.sh`, + and `git diff --check`. Hosted PR workflows own package/OS/Python/Security/CodeQL gates. + +#### Latest Validation Results + +- 2026-08-27: synchronized `main` to released v0.8.4 commit `299e7b1`; worktree clean. +- 2026-08-27: engineering-workflow repository audit reported `mature_repo`, complete + required documentation indexes, and all canonical workflow owners present. +- 2026-08-27: live GitHub readback found open #149, no open PR, no open milestone, active + compatibility workflow, and no scheduled compatibility run for the day by 08:29 UTC. +- No implementation tests have run yet; WQ-01 is the current gate. + +#### Risks And Recovery + +- GitHub scheduled workflows may be delayed or omitted. The later cron fixes the observed + pre-release ordering but does not claim delivery guarantees; exact manual dispatch is + the retained recovery path. +- The hosted compatibility run may classify 1.10.2 as human-review-required or fail. Keep + its canonical issue/evidence, perform semantic review, and do not promote until every + consumed contract is reconciled. +- An automation PR may appear. Compare its exact patch; integrate only reviewed bytes into + this Draft and close/supersede the automation PR truthfully. +- Local OCR replacement may fail. Keep the verified 1.10.1 binary until the new binary + passes all isolated checks and restore it on any post-replacement mismatch. +- A broader diagnostic projection could leak provider data or affect control flow. Keep + the type closed/numeric, render through one bounded owner, and prove GitLab/DLP/approval + independence with hostile values. +- Upstream features can resemble backlog outcomes without meeting toolkit acceptance + criteria. Require exact goal/dependency/acceptance mapping before status changes. +- Hosted CI evidence may expose a real defect. Fix only the evidenced boundary through a + separately reviewed signed commit; do not broaden scope or push partial work. + +#### Resume Point + +Finish `WQ-01`: inspect the complete plan diff against the approved Plan Mode outcome, +run the plan fidelity and `git diff --check` gates, make the signed planning commit, push +the branch once, and open the Draft PR. Then begin `WQ-02` without another repository +push. + +#### Plan Fidelity Check + +- [x] Every approved outcome has a stable requirement and queue owner. +- [x] Release classification, target, Draft-only boundary, and deferred stable delivery + are explicit. +- [x] Inputs, exact hashes, schedule evidence, public/private data flow, backlog rules, + non-goals, validation, recovery, and resume state are retained. +- [x] DLP, receipt, telemetry, approval, GitLab summary, local OCR, and no-LLM boundaries + are explicit. +- [x] Initial and final push behavior plus hosted ownership are explicit. + +#### Reconciliation Check + +- [x] Current `main`, tag, next version, open issues/PRs/milestones, local OCR, workflow, + plan, and worktree were read before this first write. +- [x] Current backlog, strategy, roadmap, compatibility, release, development, operations, + security, and GitLab owners were identified for focused reconciliation. +- [ ] OCR 1.10.2 semantic audit/evidence and canonical issue are reconciled. +- [ ] Backlog/roadmap/strategy status agrees with demonstrated upstream/toolkit outcomes. +- [ ] Final diff, validation, Draft, issues, milestone, remote ref, and hosted CI agree. + +#### Closure Gate + +- [ ] All requirements and queue items are `done` or explicitly `out_of_scope`. +- [ ] Every logical commit passed focused tests, self-review, boundary review, and + `git diff --check`. +- [ ] Final quality, coverage, OCR validation, lock, Towncrier, Gitleaks, and diff gates + are green on the exact final tree. +- [ ] Hosted required checks are green and the Draft PR has no unresolved conversations. +- [ ] #149 and the OCR issue remain open in milestone `v0.8.5`; milestone remains open. +- [ ] Draft remains unmerged; no release PR, tag, registry publication, or stable closure + occurred. + +#### Post-Close Delivery + +- This task ends with a green Draft feature PR and an explicit `release-deferred` state. +- Merge, protected-main TestPyPI publication, `release/v0.8.5`, stable registries, tag, + immutable GitHub Release, issue receipts/closure, milestone closure, and independent + external reconciliation require a later owner instruction. +- Hosted package, OS/Python, Dependency Review, Security, and CodeQL jobs are required PR + evidence but are not duplicated locally. + +#### Handoff Notes + +- Final Draft body must identify toolkit target 0.8.5, OCR target 1.10.2, exact head/tree, + exact OCR hashes and qualification run/issue, schedule rationale, Added/Fixed/Changed/ + Unchanged behavior, backlog disposition, local no-LLM checks, and hosted validation. +- The next agent starts from the exact Draft head; it does not rerun completed local + development or publish stable 0.8.5 without explicit authorization. From 9f5adfb7c6f6b191afd65b321fc392a480caf163 Mon Sep 17 00:00:00 2001 From: xeonvs <11463419+xeonvs@users.noreply.github.com> Date: Thu, 27 Aug 2026 11:00:58 +0200 Subject: [PATCH 2/7] Qualify OCR 1.10.2 safely --- .github/workflows/ocr-compatibility.yml | 2 +- PLANS.md | 27 ++- README.md | 2 +- changelog.d/151.bugfix.md | 1 + changelog.d/151.maintenance.md | 5 + changelog.d/151.rules.md | 1 + compatibility/evidence/ocr-1.10.2.json | 205 +++++++++++++++++++++++ compatibility/ocr-support.json | 57 ++++++- docs/codex/TASKS_BACKLOG.md | 3 +- docs/compatibility.md | 18 +- docs/configuration.md | 10 +- docs/engineering/test_evidence_matrix.md | 9 + docs/engineering/toolkit_strategy.md | 8 +- docs/gitlab.md | 2 +- docs/operations.md | 4 +- docs/security.md | 2 +- examples/gitlab/README.md | 2 +- examples/gitlab/ocr-review.gitlab-ci.yml | 4 +- scripts/ocr_compat.py | 28 +++- src/ocr_toolkit/preflight.py | 2 +- tests/test_environment_contract.py | 6 +- tests/test_ocr_compat.py | 59 ++++--- tests/test_operations_docs.py | 6 + tests/test_runtime_helpers.py | 2 +- 24 files changed, 397 insertions(+), 68 deletions(-) create mode 100644 changelog.d/151.bugfix.md create mode 100644 changelog.d/151.maintenance.md create mode 100644 changelog.d/151.rules.md create mode 100644 compatibility/evidence/ocr-1.10.2.json diff --git a/.github/workflows/ocr-compatibility.yml b/.github/workflows/ocr-compatibility.yml index 4ed29ef..3828152 100644 --- a/.github/workflows/ocr-compatibility.yml +++ b/.github/workflows/ocr-compatibility.yml @@ -2,7 +2,7 @@ name: OCR compatibility on: schedule: - - cron: "41 5 * * *" + - cron: "15 7 * * *" workflow_dispatch: inputs: tag: diff --git a/PLANS.md b/PLANS.md index c67aab0..96eeb06 100644 --- a/PLANS.md +++ b/PLANS.md @@ -138,9 +138,9 @@ state for this task: `release-deferred` at the green Draft PR boundary. | Queue | Status | Deliverable | | --- | --- | --- | -| `WQ-01` | `in_progress` | Review this plan, pass fidelity/diff checks, commit it signed, push once, and open the Draft PR. | -| `WQ-02` | `pending` | Create/assign milestone coordination and manually run exact OCR 1.10.2 qualification to create the canonical issue. | -| `WQ-03` | `pending` | Implement schedule, OCR evidence/pins/current docs/Rules changes, semantic audit, backlog reconciliation, and local no-LLM OCR update. | +| `WQ-01` | `completed` | Planning commit `123c331` was signed and pushed once; Draft PR #150 is open. | +| `WQ-02` | `completed` | Milestone `v0.8.5` owns assigned open issues #149/#151; hosted run 33055459209 qualified exact OCR 1.10.2. | +| `WQ-03` | `completed` | OCR 1.10.2 pins/evidence, schedule, classifier correction, current docs/Rules, source audit, backlog reconciliation, and local no-LLM update are complete. | | `WQ-04` | `pending` | Implement the single closed provider diagnostic projection/renderer and GitLab remediation with boundary regressions. | | `WQ-05` | `pending` | Finalize changelog/docs/plan truth and run the complete local gate once. | | `WQ-06` | `pending` | Push the complete history once, reconcile hosted CI, update Draft/issue coordination with exact evidence, and verify final state. | @@ -190,7 +190,19 @@ state for this task: `release-deferred` at the green Draft PR boundary. required documentation indexes, and all canonical workflow owners present. - 2026-08-27: live GitHub readback found open #149, no open PR, no open milestone, active compatibility workflow, and no scheduled compatibility run for the day by 08:29 UTC. -- No implementation tests have run yet; WQ-01 is the current gate. +- 2026-08-27: signed planning commit `123c331` was pushed and Draft PR #150 opened; + milestone `v0.8.5` was created and assigned open issues #149/#151. +- 2026-08-27: hosted run 33055459209 passed OCR 1.10.2 checksum and compatibility + probes. Human semantic review overrode its erroneous `automatic-safe` result because + the release contains Features; the classifier now routes feature-bearing patches to + human review. +- 2026-08-27: exact local Darwin arm64 OCR 1.10.2 at SHA-256 + `74fc7bcc0e6d0790c5ca033fd82a5474b6f05d443ed51a26a6f61c0cac6589fd` passed + version/help, isolated Solidity/Vyper selection/rule checks, and a loopback-only + semantic grouping probe with cap `16384` and default-medium stage sequence. No external + LLM/provider call or user OCR configuration change occurred. +- 2026-08-27: 104 focused compatibility/workflow/environment tests pass; manifest + validation, focused Ruff, Towncrier draft, and `git diff --check` pass. #### Risks And Recovery @@ -214,10 +226,9 @@ state for this task: `release-deferred` at the green Draft PR boundary. #### Resume Point -Finish `WQ-01`: inspect the complete plan diff against the approved Plan Mode outcome, -run the plan fidelity and `git diff --check` gates, make the signed planning commit, push -the branch once, and open the Draft PR. Then begin `WQ-02` without another repository -push. +Begin `WQ-04` from the reviewed OCR 1.10.2 slice. Implement the single closed numeric +provider diagnostic owner and GitLab remediation regressions without changing public +summary, DLP, receipt, telemetry, or approval schemas. Do not push until WQ-05 completes. #### Plan Fidelity Check diff --git a/README.md b/README.md index b8f23d2..73cbd9b 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ ocr-ci --help 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. The [versioned compatibility policy](docs/compatibility.md) records tested assets and evidence and describes the conservative Dependabot-like qualification workflow for later upstream releases. 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`. -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. +The current OCR 1.10.2 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. 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. diff --git a/changelog.d/151.bugfix.md b/changelog.d/151.bugfix.md new file mode 100644 index 0000000..f7b79ac --- /dev/null +++ b/changelog.d/151.bugfix.md @@ -0,0 +1 @@ +OCR compatibility classification now sends feature-bearing patch releases to human review instead of allowing adjacent maintenance keywords to misclassify them as `automatic-safe`. diff --git a/changelog.d/151.maintenance.md b/changelog.d/151.maintenance.md new file mode 100644 index 0000000..c6a453e --- /dev/null +++ b/changelog.d/151.maintenance.md @@ -0,0 +1,5 @@ +Open Code Review 1.10.2 is the checksum-pinned runtime dependency for toolkit 0.8.5. + +- **Changed:** The current GitLab example, runtime preflight, compatibility manifest, and qualification evidence move from OCR 1.10.1 to 1.10.2. The inherited OpenAI completion cap remains `16384`, but OCR grouping requests now use that template-owned cap instead of a separate `4096`; an explicit `OCR_LLM_MAX_COMPLETION_TOKENS` continues to override every request. +- **Changed:** Daily OCR compatibility discovery moves from `05:41 UTC` to `07:15 UTC`; exact-tag manual dispatch remains the recovery path when GitHub delays or omits a scheduled run. +- **Unchanged:** OCR result and manifest schemas, default `medium` effort, effective max-tools behavior, DLP, receipt v5, toolkit telemetry ownership, and approval contracts are unchanged. diff --git a/changelog.d/151.rules.md b/changelog.d/151.rules.md new file mode 100644 index 0000000..9f05787 --- /dev/null +++ b/changelog.d/151.rules.md @@ -0,0 +1 @@ +The recommended OCR 1.10.2 built-in Rules and reviewable-file allowlist now cover Solidity (`.sol`) and Vyper (`.vy`). diff --git a/compatibility/evidence/ocr-1.10.2.json b/compatibility/evidence/ocr-1.10.2.json new file mode 100644 index 0000000..85490f6 --- /dev/null +++ b/compatibility/evidence/ocr-1.10.2.json @@ -0,0 +1,205 @@ +{ + "assets": [ + { + "name": "opencodereview-darwin-amd64", + "sha256": "91d30daa43ea698775df16cb4999e69624f009c93bb7a832310d13070981303d", + "size": 57674784 + }, + { + "name": "opencodereview-darwin-arm64", + "sha256": "74fc7bcc0e6d0790c5ca033fd82a5474b6f05d443ed51a26a6f61c0cac6589fd", + "size": 54711010 + }, + { + "name": "opencodereview-linux-amd64", + "sha256": "e9205614f80e009ee7b1f444c9da08486fb9ff6db022954fe9203d923ab720b2", + "size": 55931042 + }, + { + "name": "opencodereview-linux-arm64", + "sha256": "8ca3f4931200fbf48ff4b2045a6fb69da18246d11f74e630558a6ba5bab53ef4", + "size": 52691106 + }, + { + "name": "opencodereview-windows-amd64.exe", + "sha256": "3ec56cc810500fac54d2c4c0ebe580b7841b1cd1966f2e2e86ce51d0292aa56e", + "size": 57393152 + }, + { + "name": "opencodereview-windows-arm64.exe", + "sha256": "27ee3472871a32cd5d843579361db198ed10a83855fd43d26a763a0e9cd3cddd", + "size": 53370880 + }, + { + "name": "sha256sum.txt", + "sha256": "b5176aaa04a7f00bd84dd61556ca29e6cbdfcfe64cc50af6653163d9be4e7654", + "size": 572 + } + ], + "classification": "human-review-required", + "classification_reasons": [ + "release notes contain a material or ambiguous compatibility signal" + ], + "comparison_version": "1.10.1", + "contracts": { + "comment_thinking_probe": { + "additive_field_preserved": true, + "posting_exposes_thinking": false, + "result": "passed" + }, + "completion_cap_probe": { + "explicit": 4096, + "inherited": 16384, + "result": "passed", + "wire_field": "max_completion_tokens" + }, + "numeric_cli_probe": { + "max_tokens_budget": { + "cases": { + "invalid_below": { + "effective": null, + "input": -1, + "outcome": "rejected" + }, + "minimum": { + "effective": 1, + "input": 1, + "outcome": "accepted" + }, + "omitted": { + "effective": "unlimited", + "input": null, + "outcome": "accepted" + }, + "representative": { + "effective": 30000, + "input": 30000, + "outcome": "accepted" + }, + "sentinel": { + "effective": "unlimited", + "input": 0, + "outcome": "accepted" + } + }, + "maximum": null, + "owner": "ocr-cli" + }, + "max_tools": { + "cases": { + "invalid_below": { + "effective": null, + "input": -1, + "outcome": "rejected" + }, + "minimum": { + "effective": 100, + "input": 50, + "outcome": "accepted" + }, + "minimum_minus_one": { + "effective": 100, + "input": 49, + "outcome": "normalized", + "reported_normalization": 50 + }, + "omitted": { + "effective": 100, + "input": null, + "outcome": "accepted" + }, + "representative": { + "effective": 101, + "input": 101, + "outcome": "accepted" + }, + "sentinel": { + "effective": 100, + "input": 0, + "outcome": "accepted" + } + }, + "maximum": null, + "owner": "ocr-template-or-higher-cli", + "reported_minimum": 50 + }, + "result": "passed" + }, + "optional_capabilities": [ + "llm_result_identity", + "per_run_model_override", + "per_run_provider_override", + "review_effort", + "semantic_grouping" + ], + "preview_probe": { + "format": "json", + "path": "example.py", + "result": "passed", + "session_store_created": false + }, + "required_review_flags": [ + "--audience", + "--background-file", + "--effort", + "--format", + "--from", + "--max-tokens-budget", + "--max-tools", + "--preview", + "--rule", + "--to" + ], + "result_contract_probe": { + "additive_fields_allowed": true, + "comment_fields": [ + "category", + "content", + "end_line", + "existing_code", + "path", + "severity", + "start_line", + "thinking" + ], + "manifest_schema": "ocr.run-manifest/v1", + "normalized_outcome": "clean", + "result": "passed" + }, + "review_budget_probe": { + "budget": 30000, + "completed": 2, + "failed_budget": 1, + "partial_findings_preserved": true, + "result": "passed", + "selected": 3 + }, + "semantic_grouping_probe": { + "default_effort": "medium", + "filter_requests": 1, + "grouping_completion_cap": 16384, + "grouping_requests": 1, + "main_requests": 3, + "result": "passed", + "review_rounds": 2 + }, + "target_rule_selection_probe": { + "format": "json", + "from_to_unchanged": true, + "path": "synthetic-template.ocrfixture", + "result": "passed", + "source_exclusion": "unsupported_ext", + "target_selected": true + }, + "version_probe": "passed" + }, + "published_at": "2026-08-27T05:49:24Z", + "release_changes": "## 🚀 Features\n\n- feat(session): register GroupingTask as a tracked TaskType (#1067)\n- feat(rules): add Solidity and Vyper review rules (#961)\n- feat(action): opt-in completeness-gated checkpoint ranges (#476) (#945)\n- feat(session): compare findings across two review sessions (#922) (#946)\n\n## 🐛 Bug Fixes\n\n- fix: record grace round requests (#1066)\n\n## 📖 Documentation\n\n- docs(ko): add Korean translation for integration guides (#1072)\n\n**Full Changelog**: https://github.com/alibaba/open-code-review/compare/v1.10.1...v1.10.2", + "release_notes_sha256": "cad919d43d01e6f8c5ed1a2cb83120d76d082996e5c246c356a97155443d2e83", + "result": "compatible", + "schema_version": 2, + "tag": "v1.10.2", + "tested_baseline_version": "1.10.1", + "upstream_repository": "alibaba/open-code-review", + "version": "1.10.2" +} diff --git a/compatibility/ocr-support.json b/compatibility/ocr-support.json index a0890b6..3949488 100644 --- a/compatibility/ocr-support.json +++ b/compatibility/ocr-support.json @@ -1,6 +1,6 @@ { - "monitoring_floor": "1.10.1", - "recommended_version": "1.10.1", + "monitoring_floor": "1.10.2", + "recommended_version": "1.10.2", "releases": [ { "assets": [ @@ -1240,6 +1240,59 @@ "release_url": "https://github.com/alibaba/open-code-review/releases/tag/v1.10.1", "status": "tested", "version": "1.10.1" + }, + { + "assets": [ + { + "name": "opencodereview-darwin-amd64", + "sha256": "91d30daa43ea698775df16cb4999e69624f009c93bb7a832310d13070981303d", + "size": 57674784 + }, + { + "name": "opencodereview-darwin-arm64", + "sha256": "74fc7bcc0e6d0790c5ca033fd82a5474b6f05d443ed51a26a6f61c0cac6589fd", + "size": 54711010 + }, + { + "name": "opencodereview-linux-amd64", + "sha256": "e9205614f80e009ee7b1f444c9da08486fb9ff6db022954fe9203d923ab720b2", + "size": 55931042 + }, + { + "name": "opencodereview-linux-arm64", + "sha256": "8ca3f4931200fbf48ff4b2045a6fb69da18246d11f74e630558a6ba5bab53ef4", + "size": 52691106 + }, + { + "name": "opencodereview-windows-amd64.exe", + "sha256": "3ec56cc810500fac54d2c4c0ebe580b7841b1cd1966f2e2e86ce51d0292aa56e", + "size": 57393152 + }, + { + "name": "opencodereview-windows-arm64.exe", + "sha256": "27ee3472871a32cd5d843579361db198ed10a83855fd43d26a763a0e9cd3cddd", + "size": 53370880 + }, + { + "name": "sha256sum.txt", + "sha256": "b5176aaa04a7f00bd84dd61556ca29e6cbdfcfe64cc50af6653163d9be4e7654", + "size": 572 + } + ], + "capabilities": [ + "llm_result_identity", + "per_run_model_override", + "per_run_provider_override", + "review_effort", + "semantic_grouping" + ], + "evidence": "compatibility/evidence/ocr-1.10.2.json", + "evidence_sha256": "5f37a74a412734c3ebb6846a1082c4a553c087d7d0b5221597a381ec28c16ce3", + "human_conclusion": "Compatible after human review in issue #151 and hosted workflow run 33055459209. OCR 1.10.2 adds tracked grouping and grace-round requests, aligns grouping with the inherited 16384 completion cap, and adds Solidity/Vyper review rules without changing toolkit result, manifest, medium-effort, max-tools, DLP, receipt, telemetry, or approval contracts. Session comparison and reusable GitHub Action checkpoint ranges are not consumed; the latter only partially overlaps BL-021. The original machine automatic-safe classification was overridden because feature-bearing release notes require human review.", + "published_at": "2026-08-27T05:49:24Z", + "release_url": "https://github.com/alibaba/open-code-review/releases/tag/v1.10.2", + "status": "tested", + "version": "1.10.2" } ], "schema_version": 1, diff --git a/docs/codex/TASKS_BACKLOG.md b/docs/codex/TASKS_BACKLOG.md index c949b41..f6f0819 100644 --- a/docs/codex/TASKS_BACKLOG.md +++ b/docs/codex/TASKS_BACKLOG.md @@ -44,7 +44,7 @@ M3 is established. BL-011 is complete and recorded above rather than retained as ## M6 Profiles and quality measurement -Provider/network telemetry remains outside M1 and M5. OCR owns token, cost, budget, provider-level review duration, request, and tool-call telemetry. Toolkit v0.7.1 exposes only validated provider-neutral token buckets, distinguishes OCR-wide tool totals from verified MCP-server and count-only evidence-action use, and carries deterministic publication-DLP state in receipt v5, a parseable GitLab summary marker, and a structured local log event; it adds no exporter or endpoint. M6 audits whether those result-derived lifecycle signals need provider-neutral export/alert routing instead of duplicating OCR telemetry. +Provider/network telemetry remains outside M1 and M5. OCR owns token, cost, budget, provider-level review duration, request, and tool-call telemetry. The toolkit exposes only validated provider-neutral token buckets, distinguishes OCR-wide tool totals from verified MCP-server and count-only evidence-action use, and carries deterministic publication-DLP state in receipt v5, a parseable GitLab summary marker, and a structured local log event; it adds no exporter or endpoint. The 0.8.5 development line adds at most one bounded toolkit-authored CI failure diagnostic from closed retry-report enums and counts, not provider telemetry or an export path. M6 audits whether result-derived lifecycle signals need provider-neutral export/alert routing instead of duplicating OCR telemetry. ### BL-016: Evaluate explicit run-level model profiles @@ -127,6 +127,7 @@ Provider/network telemetry remains outside M1 and M5. OCR owns token, cost, budg - **Roadmap theme:** M7 Later and conditional work - **Dependencies:** Stable provider-neutral core contracts and a funded non-GitLab use case. GitLab-first M5 does not depend on it. - **Activation trigger:** A named forge has an owner, synthetic fixtures, and explicit parity requirements for CI orchestration, positioning, deduplication, discussion ownership, and safe publication. +- **Upstream overlap:** OCR 1.10.2 reusable GitHub Action checkpoint ranges cover only upstream Action range selection. They do not provide toolkit forge acquisition, discussion, publication, or lifecycle parity, so the trigger and acceptance criteria remain unmet. - **Goal:** Add one coherent host adapter without leaking forge semantics into evidence or core result handling. - **Scoped deliverables:** The capability matrix covers authentication, diff positions, drafts, discussion acquisition, provider-declared account classification, thread/reply structure, edit/version identity, anchors, resolved/stale state, pagination/snapshot mutation, ambiguous writes, permissions, and idempotency. - **Acceptance criteria:** Core remains provider-neutral, GitLab behavior does not regress, unsupported host capabilities fail or degrade explicitly rather than emulate unsafe parity, and the new host meets the approved lifecycle and security matrix. diff --git a/docs/compatibility.md b/docs/compatibility.md index a71432e..d8a634d 100644 --- a/docs/compatibility.md +++ b/docs/compatibility.md @@ -4,7 +4,7 @@ The versioned support contract lives in [`compatibility/ocr-support.json`](../co ## Qualification lanes -The scheduled **OCR compatibility** workflow discovers stable upstream releases newer than the manifest monitoring floor. Drafts, prereleases, non-semantic tags, unexpected asset sets, oversized metadata or downloads, redirects outside the reviewed GitHub origins, and checksum disagreement fail closed. Every binary digest must agree with both GitHub release metadata and the upstream `sha256sum.txt`. +The **OCR compatibility** workflow discovers stable upstream releases newer than the manifest monitoring floor. Its daily trigger is scheduled for `07:15 UTC`, after the observed upstream release window; GitHub may delay or omit scheduled delivery, so exact-tag manual dispatch remains the recovery path. Drafts, prereleases, non-semantic tags, unexpected asset sets, oversized metadata or downloads, redirects outside the reviewed GitHub origins, and checksum disagreement fail closed. Every binary digest must agree with both GitHub release metadata and the upstream `sha256sum.txt`. Candidate execution uses the verified Linux amd64 binary on an Ubuntu runner. The harness checks the reported version, the CLI flags consumed by the GitLab integration, range preview behavior, an actual JSON review through a deterministic local gateway, an aggregate-budget review that must preserve completed findings and emit budget-attributed partial coverage, and the additive JSON fields consumed by posting. Toolkit-managed numeric OCR options are also exercised at omitted/default, sentinel, invalid-below-boundary, minimum minus one, minimum, representative, and maximum edges when bounded. The evidence records closed outcomes, recognized diagnostics, normalization, ownership, and effective values observed through real loop behavior; CLI help text is not accepted as runtime evidence. For OCR 1.9.0 and later the harness also requires JSON preview without a session-store side effect and proves that additive comment `thinking` is accepted but not published to GitLab. Upstream source review separately verifies how OCR derives that field; the toolkit probe does not claim to reproduce a provider's private reasoning channel. Evidence permits unknown new fields but requires the fields the toolkit reads. Legacy result statuses and the versioned `ocr.run-manifest/v1` outcome are normalized through one shared toolkit contract; manifest coverage sets, failure classifications, terminal state, and budget attribution must agree before a result can be published. @@ -19,7 +19,7 @@ Before selecting a lane, classify every upstream changelog item as a toolkit-con Classify a pin-only promotion and its machine or human qualification evidence as Maintenance. If the qualification exposes an existing toolkit incompatibility and the delivery corrects it, classify that outcome as a Bug Fix. If the toolkit begins exposing or consuming an upstream capability, classify that outcome as a Feature. Effective built-in rule or reviewable-file changes remain Rules even when they arrive with an otherwise maintenance-only OCR promotion. - `automatic-safe`: only an adjacent patch in the already-tested major/minor line, with every probe passing and maintenance-only release notes containing no material compatibility signal. When several releases are discovered together, each keeps the manifest recommendation as its tested baseline but is classified against its adjacent predecessor. The aggregation job prepares one cumulative compatibility patch only when the complete sequence is contiguous and every member is automatic-safe. The patch covers the manifest, evidence, runtime preflight version, and GitLab example version/checksum. It never writes directly to `main`. -- `human-review-required`: every minor/major release, skipped or non-increasing patch, changed or failed contract, material/security/deprecation/config/provider signal, or ambiguous release notes. The workflow creates or refreshes one qualification issue with machine evidence, a bounded plain-text release-change excerpt, and a human checklist. +- `human-review-required`: every minor/major release, skipped or non-increasing patch, changed or failed contract, feature-bearing release, material/security/deprecation/config/provider signal, or ambiguous release notes. The workflow creates or refreshes one qualification issue with machine evidence, a bounded plain-text release-change excerpt, and a human checklist. Each OCR version owns one stable HTML marker and one qualification issue. The workflow performs a single upsert through bounded direct issue listing rather than GitHub's eventually consistent search index. Historical issues closed with the `duplicate` label remain as incident evidence but do not compete for canonical identity. Any other duplicate state fails closed instead of creating another issue; after operators select and reconcile the canonical issue, reruns update it in place. @@ -51,7 +51,7 @@ Deploy toolkit 0.8.2 or 0.8.3 directly with OCR 1.10.0; do not install OCR 1.9.1 ### OCR 1.10.1 — toolkit 0.8.4 target -OCR 1.10.1 is the only version accepted by toolkit 0.8.4 preflight and pinned by the current GitLab example. Hosted Linux and independent Darwin arm64 qualification preserve the OCR result and `ocr.run-manifest/v1` contracts, inherited OpenAI completion cap `16384`, explicit `4096` override, default-medium two-round behavior, and effective max-tools values from 1.10.0. The `--max-tools` help text advertises a lower minimum, but actual CLI behavior still normalizes `49` to `50`, the embedded template keeps effective `100`, and explicit `101` raises it; the toolkit uses those behaviorally observed values and keeps the example on sentinel `0`. +OCR 1.10.1 is the historical version accepted by toolkit 0.8.4 preflight and pinned by its published GitLab example. Hosted Linux and independent Darwin arm64 qualification preserve the OCR result and `ocr.run-manifest/v1` contracts, inherited OpenAI completion cap `16384`, explicit `4096` override, default-medium two-round behavior, and effective max-tools values from 1.10.0. The `--max-tools` help text advertises a lower minimum, but actual CLI behavior still normalizes `49` to `50`, the embedded template keeps effective `100`, and explicit `101` raises it; the toolkit uses those behaviorally observed values and keeps the example on sentinel `0`. Toolkit 0.8.4 reduces an exact accepted soft background diagnostic to the private numeric `ocr.toolkit-advisory/v1` projection after publication DLP. It is rendered only with a valid receipt v5 in GitLab Technical details and does not enter OCR warnings, coverage, DLP counts, telemetry, or automatic-approval inputs. Raw OCR output cannot supply the reserved field; duplicate, extended, unknown, malformed, or unbound forms fail closed. @@ -61,6 +61,18 @@ The upstream GitHub Action, delegate skill, npm launcher, and built-in provider Deploy toolkit 0.8.4 directly with OCR 1.10.1. The public Linux amd64 example verifies SHA-256 `8b806c221d409727a21611b4a7952d8e15edadbbc25f5affccaeb8f677e4055c`; the local Darwin arm64 artifact verifies `8fc24bd825c9d918b894be05c0cf27fac8d30bc549257c812d87337167c7563c`, and the upstream `sha256sum.txt` verifies `ec72bda51f1227f412ee00602d952868efc57d847cce0ae1586fb97069d4139d`. +### OCR 1.10.2 — toolkit 0.8.5 target + +OCR 1.10.2 is the only version accepted by the toolkit 0.8.5 development preflight and pinned by the current GitLab example. Hosted run `33055459209`, canonical issue #151, adjacent source review, and checksum-verified isolated Darwin checks preserve the OCR result, `ocr.run-manifest/v1`, default-medium two-round review, effective max-tools, completion-cap override, DLP, receipt v5, telemetry ownership, and approval contracts. + +OCR now records grouping tasks and grace-round requests in its private session/retry state. The grouping request uses the review template's inherited OpenAI completion cap `16384` instead of the separate `4096` used by the predecessor; `OCR_LLM_MAX_COMPLETION_TOKENS=4096` still overlays every protocol request when an operator needs a fixed gateway cap. These private task records and group/path values do not enter toolkit findings, GitLab text, receipts, DLP signals, telemetry, or approval inputs. + +Solidity (`.sol`) and Vyper (`.vy`) join the effective built-in Rules and reviewable-file contract. The upstream session-comparison command is not invoked by the toolkit. Upstream reusable GitHub Action checkpoint ranges cover only that Action's range selection; the GitLab toolkit keeps its own immutable range/snapshot and publication lifecycle, so the change only partially overlaps BL-021 and does not activate or complete a second forge adapter. + +The hosted qualification initially labelled the patch `automatic-safe` because maintenance keywords masked the `Features` section. Human review overrode that result, and the classifier now treats feature-bearing patch notes as material so they require the human lane. + +Deploy toolkit 0.8.5 directly with OCR 1.10.2 after the Draft release work is approved. The public Linux amd64 example verifies SHA-256 `e9205614f80e009ee7b1f444c9da08486fb9ff6db022954fe9203d923ab720b2`; the independently checked Darwin arm64 artifact verifies `74fc7bcc0e6d0790c5ca033fd82a5474b6f05d443ed51a26a6f61c0cac6589fd`, and the upstream `sha256sum.txt` verifies `b5176aaa04a7f00bd84dd61556ca29e6cbdfcfe64cc50af6653163d9be4e7654`. + The contiguous 1.9.7 through 1.9.8 promotion combines the repository-owned hosted Linux qualification with adjacent upstream source review. It also repeats the required 1.9.8 contract probes through the checksum-verified Darwin arm64 artifact. Multi-tool MCP initialization, fixed tool discovery and dispatch, and owner-only session persistence remain compatible with the M5 design: the toolkit may expose several fixed tools from its one built-in process, while an isolated `HOME` contains OCR's `.opencodereview/sessions` state for deterministic cleanup. Gemini and Bedrock are optional upstream provider capabilities that the toolkit neither selects nor configures; mirror-aware installation is outside the toolkit-owned download path; native severity retains the structured result fields already consumed; and human-audience progress routing does not affect the toolkit's agent-audience invocation. Therefore this promotion requires no toolkit CI behavior adaptation beyond version and checksum pins. These source observations establish capability only; the M5 production-path tests remain responsible for proving the toolkit's composition and containment. An automatic-safe result is not an automatic stable release. It must still pass a normal protected compatibility PR and a separate signed stable-release PR. If a dedicated OCR update bot credential is not configured, the workflow publishes the exact patch as an artifact and records the resume action in the issue; the default `GITHUB_TOKEN` is intentionally not used to create a PR that would fail to trigger the full protected workflow set. diff --git a/docs/configuration.md b/docs/configuration.md index be5c021..3352d43 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -22,7 +22,7 @@ These are the complete supported toolkit-owned runtime inputs. `Required` is sco | `OCR_LLM_VALIDATE_MODEL` | Operator / `ocr-ci preflight` | No | `false` | `true` validates through `/models`; `auto` may use the offline allowlist; false values skip validation. | | `OCR_LLM_MODELS_URL` | Operator / `ocr-ci preflight` | No | Derived from `OCR_LLM_URL` | Explicit absolute credential-free HTTPS metadata URL when validation is enabled or inference query parameters make derivation ambiguous. | | `OCR_LLM_ALLOWED_MODELS` | Operator / `ocr-ci preflight` | No | Empty list | Comma-separated exact model identifiers for offline or `auto` validation. | -| `OCR_TELEMETRY_ENABLED` | Operator / `ocr-ci configure` | No | `false` | Exact `true` enables OCR telemetry configuration; OCR 1.10.1 spans may include path-derived group keys and model-produced labels. | +| `OCR_TELEMETRY_ENABLED` | Operator / `ocr-ci configure` | No | `false` | Exact `true` enables OCR telemetry configuration; OCR 1.10.2 spans may include path-derived group keys and model-produced labels. | | `OCR_TELEMETRY_CONTENT_LOGGING` | Operator / `ocr-ci configure` | No | `false` | Exact `true` enables OCR content logging; keep disabled for private review data. | | `OCR_TELEMETRY_EXPORTER` | Operator / `ocr-ci configure` | No | Empty string | Exporter name written only when telemetry is enabled. | | `OCR_TELEMETRY_OTLP_ENDPOINT` | Operator / `ocr-ci configure` | No | Unset | OTLP endpoint written only when telemetry is enabled and the value is non-empty. | @@ -60,11 +60,11 @@ If `OCR_LLM_EXTRA_BODY` already owns that field, an exactly equal JSON integer i The toolkit does not derive this value from `/models.max_completion_tokens`. That metadata is a model capability boundary, not an account spending limit or proof of how a gateway reserves request cost. -The inherited value is version-owned and therefore changes with a qualified OCR upgrade. The toolkit observed `max_completion_tokens=58888` from OCR 1.9.10 and `16384` from both OCR 1.10.0 and 1.10.1 when the variable was unset. Deployments that require an invariant gateway-specific cap must set `OCR_LLM_MAX_COMPLETION_TOKENS` explicitly rather than depending on an OCR default. +The inherited value is version-owned and therefore changes with a qualified OCR upgrade. The toolkit observed `max_completion_tokens=58888` from OCR 1.9.10 and `16384` from OCR 1.10.0 through 1.10.2 when the variable was unset. In OCR 1.10.2 the grouping request also uses this template-owned cap instead of a separate `4096`; an explicit toolkit override still applies to every protocol request. Deployments that require an invariant gateway-specific cap must set `OCR_LLM_MAX_COMPLETION_TOKENS` explicitly rather than depending on an OCR default. ### Review effort -`OCR_REVIEW_EFFORT` defaults to `medium` and is written to OCR's root `effort` configuration key. OCR 1.10.1 maps `low`, `medium`, and `high` to one, two, and three review rounds respectively. The environment is operator-owned; merge-request text cannot change it. An explicit caller `--effort` passed after `ocr-ci review --` has normal OCR CLI precedence over the generated config, while an unknown environment value fails configuration before preview or model execution. +`OCR_REVIEW_EFFORT` defaults to `medium` and is written to OCR's root `effort` configuration key. OCR 1.10.2 maps `low`, `medium`, and `high` to one, two, and three review rounds respectively. The environment is operator-owned; merge-request text cannot change it. An explicit caller `--effort` passed after `ocr-ci review --` has normal OCR CLI precedence over the generated config, while an unknown environment value fails configuration before preview or model execution. Effort controls review depth, not the prompt/context ceiling, per-call completion cap, aggregate token budget, or per-round tool limit. Semantic grouping and filtering can add requests even at `low`; higher effort can add further rounds until OCR stops early, reaches a coverage/budget boundary, or completes the configured depth. @@ -92,8 +92,8 @@ These names belong to `examples/gitlab/ocr-review.gitlab-ci.yml`; they are shell | Variable | Source / owner | Required | Exact default | Behavior | | --- | --- | --- | --- | --- | -| **`OCR_VERSION`** | Example pipeline | Yes | `v1.10.1` | Checksum-pinned recommended OCR binary release for toolkit 0.8.4. | -| **`OCR_SHA256`** | Example pipeline | Yes | `8b806c221d409727a21611b4a7952d8e15edadbbc25f5affccaeb8f677e4055c` | Expected Linux AMD64 OCR binary digest. | +| **`OCR_VERSION`** | Example pipeline | Yes | `v1.10.2` | Checksum-pinned recommended OCR binary release for toolkit 0.8.5. | +| **`OCR_SHA256`** | Example pipeline | Yes | `e9205614f80e009ee7b1f444c9da08486fb9ff6db022954fe9203d923ab720b2` | Expected Linux AMD64 OCR binary digest. | | **`OCR_TOOLKIT_VERSION`** | Example pipeline | Yes | `0.8.4` | Exact toolkit wheel release installed by the current published example. | | **`OCR_TOOLKIT_CHECKSUMS_URL`** | Example pipeline | Yes | Release URL derived from `OCR_TOOLKIT_VERSION` | Toolkit `SHA256SUMS` URL. | | `OCR_TOOLKIT_WHEEL` | Example shell | Computed | `open_code_review_toolkit-${OCR_TOOLKIT_VERSION}-py3-none-any.whl` | Exact wheel filename selected from the release. | diff --git a/docs/engineering/test_evidence_matrix.md b/docs/engineering/test_evidence_matrix.md index d037058..55c540f 100644 --- a/docs/engineering/test_evidence_matrix.md +++ b/docs/engineering/test_evidence_matrix.md @@ -114,6 +114,15 @@ The M5 negative suite is an attacker matrix, not merely a replay of observed rev | New upstream rule selection | exact OCR preview -> allowlist and built-in rule resolver | `.m` input is reviewable and content selects MATLAB or Objective-C behavior without changing the toolkit rule file | isolated Darwin arm64 no-LLM `ocr rules check` readback for MATLAB and Objective-C plus upstream semantic audit | selection evidence proves language/rule routing, not finding quality | proven | | Bounded upstream session keys stay private | OCR runtime private cache/session owner -> toolkit result boundary | new bounded keys do not enter result, receipt v5, toolkit telemetry, GitLab text, or approval | upstream semantic audit plus unchanged result/manifest probes | source and result evidence prove the consumed boundary, not every upstream internal cache implementation | proven | +## v0.8.5 OCR 1.10.2 evidence + +| Requirement or boundary | Production owner and entry point | Required observable result | Evidence | Double boundary and claim limit | State | +| --- | --- | --- | --- | --- | --- | +| Exact OCR 1.10.2 review contract | compatibility gateway -> hosted Linux binary -> human-reviewed manifest/evidence promotion | result/manifest, inherited and explicit completion caps, default-medium rounds, semantic grouping, and effective max-tools remain compatible; grouping uses the inherited `16384` cap | hosted run 33055459209; checksum-pinned `compatibility/evidence/ocr-1.10.2.json`; adjacent source audit; isolated checksum-verified Darwin arm64 version/help and no-LLM selection/rule checks | deterministic gateways and previews prove consumed wire/result/selection contracts, not provider quality or production spending policy | proven for exact qualified OCR contract | +| Feature-bearing patch notes cannot silently promote | release-note classifier -> qualification lane -> protected reviewed promotion | a patch containing `Features` or a conventional `feat(...)` entry requires human review even when maintenance keywords are also present | focused classifier regression plus the reviewed correction of run 33055459209 evidence | static release-note classification proves routing, while human review owns semantic impact | proven | +| New language Rules are selected without widening toolkit authority | exact OCR preview/rule resolver -> recommended built-in Rules | `.sol` and `.vy` inputs are reviewable and resolve Solidity/Vyper Rules; no toolkit result, DLP, receipt, telemetry, or approval contract changes | isolated Darwin arm64 no-LLM preview/rule readback plus upstream semantic audit | selection proves language/rule routing, not finding quality | proven | +| Upstream Action/session additions do not claim backlog completion | source audit -> BL-021 and M6 ownership records | Action checkpoint ranges remain upstream-Action-only; session comparison and private grouping/grace records are not toolkit forge parity or telemetry | adjacent source/release audit and backlog/strategy reconciliation | source evidence classifies overlap only; no second forge integration is claimed | proven as partial overlap, backlog remains open | + ## Complete suite module audit Every top-level test module is classified below. A module can contain more than one evidence class; the strongest class applies only to the named boundary, never to all tests in that file. diff --git a/docs/engineering/toolkit_strategy.md b/docs/engineering/toolkit_strategy.md index 261480b..4d09a91 100644 --- a/docs/engineering/toolkit_strategy.md +++ b/docs/engineering/toolkit_strategy.md @@ -154,9 +154,11 @@ Qualified OCR releases expose explicit per-run provider/model overrides, additiv The completed [BL-017 signal-ownership audit](review_signal_ownership.md) reaches `no-new-layer`. OCR owns provider, request, latency, cost, grouping, round, and tool telemetry. The toolkit owns only deterministic closed result, receipt, -context, publication-DLP, posting, and approval projections. OCR 1.10.1 group +context, publication-DLP, posting, and approval projections. OCR 1.10.2 group keys contain changed paths and its labels are model-produced, so neither becomes -a toolkit metric, routing input, or approval authority. +a toolkit metric, routing input, or approval authority. A single bounded +toolkit-authored failure diagnostic derived from closed retry-report enums and +counts is operator feedback, not a second telemetry or exporter layer. Automatic routing is conditional on stable evidence, latency, token, and review-quality metrics. If activated, it is deterministic, conservative, observable, never changes explicit coverage controls, and never routes a merge request to a full-repository scan. @@ -164,7 +166,7 @@ Automatic routing is conditional on stable evidence, latency, token, and review- Fast-moving upstream compatibility is a product capability, not an ad hoc version string update. One machine-readable manifest should define recommended and tested OCR releases and known capabilities. Version and capability inspection is centralized, additive output fields are parsed tolerantly, and required contract removal fails closed. -Contract tests cover the supported release set. Scheduled automation enumerates every unseen stable upstream release, verifies official checksums before bounded machine probes, and records reproducible evidence. Ordered candidates retain the current tested baseline while each classification uses its adjacent predecessor. A conservative same-minor maintenance classifier may prepare one cumulative compatibility patch only when the sequence is contiguous, every consumed contract remains stable, and no release notes contain a material signal. Minor/major, ambiguous, changed, failed, or mixed candidates always require human qualification. No lane writes directly to `main`: updating the manifest or recommended version remains a separate reviewed, checksum-pinned change with the normal protected PR and release gates. +Contract tests cover the supported release set. Daily automation runs at `07:15 UTC`, enumerates every unseen stable upstream release, verifies official checksums before bounded machine probes, and records reproducible evidence; exact-tag manual dispatch remains the recovery path when GitHub delays or omits a scheduled run. Ordered candidates retain the current tested baseline while each classification uses its adjacent predecessor. A conservative same-minor maintenance classifier may prepare one cumulative compatibility patch only when the sequence is contiguous, every consumed contract remains stable, and no release notes contain a feature or other material signal. Minor/major, feature-bearing, ambiguous, changed, failed, or mixed candidates always require human qualification. No lane writes directly to `main`: updating the manifest or recommended version remains a separate reviewed, checksum-pinned change with the normal protected PR and release gates. ## Historical and migration-only evidence diff --git a/docs/gitlab.md b/docs/gitlab.md index 84af6cf..ba96e9c 100644 --- a/docs/gitlab.md +++ b/docs/gitlab.md @@ -20,7 +20,7 @@ The public pipeline stores the OCR binary checksum as the non-secret `OCR_SHA256 `OCR_REVIEW_LANGUAGE` defaults to `English`; `Russian` is one example of an explicit review language. The example pins its qualified OCR release and sets `OCR_REVIEW_EFFORT=medium`, allowing two review rounds; `low` and `high` explicitly select one or three. OCR may stop early when a round adds no finding. `OCR_MAX_TOOLS=0` delegates the per-file tool-call limit to the installed OCR template; use a positive value only after checking that release's behaviorally qualified minimum, normalization, and effective value. `OCR_MAX_TOKENS_BUDGET` defaults to `0`, meaning unlimited; a positive budget may stop dispatch and produce an explicitly partial, automatic-approval-ineligible review. `OCR_LLM_MAX_COMPLETION_TOKENS` defaults to unset and separately controls only the provider request's completion/output cap; `4096` is a practical explicit value for gateways that reserve spending against a larger requested maximum. -OCR 1.10.1 semantically groups related changed files before review and filters candidates per group. Grouping, filtering, and multiple rounds can increase provider requests, latency, and token cost, while the manifest and aggregate budget continue to report completeness. Group labels and path-derived keys are untrusted private OCR diagnostics; the toolkit does not publish or use them for severity, fingerprints, lifecycle commands, receipts, telemetry, or approval. OCR's separately configured telemetry may export those high-cardinality repository-derived values, so keep `OCR_TELEMETRY_ENABLED=false` unless the exporter and retention policy are approved. +OCR 1.10.2 semantically groups related changed files before review and filters candidates per group. Grouping, filtering, and multiple rounds can increase provider requests, latency, and token cost, while the manifest and aggregate budget continue to report completeness. Grouping and grace-round requests are tracked inside OCR's private session/retry diagnostics. Group labels and path-derived keys remain untrusted: the toolkit does not publish or use them for severity, fingerprints, lifecycle commands, receipts, telemetry, or approval. OCR's separately configured telemetry may export those high-cardinality repository-derived values, so keep `OCR_TELEMETRY_ENABLED=false` unless the exporter and retention policy are approved. The summary keeps four inputs separate: OCR manifest coverage, publication integrity, published findings, and an optional OCR core advisory. Complete manifest coverage that later loses public fields is labelled `Review complete with publication filtering`; it is warning-bearing and approval-ineligible, but it is not called incomplete OCR coverage and does not invent failed-file diagnostics. Actual partial, failed, waived, or budget-stopped manifest coverage keeps its stronger status. Ordinary OCR warnings retain their own complete-with-warnings state. The reviewer guide ranks bounded focus areas only when at least two findings are published; one finding remains in its inline or fallback discussion without a duplicate snippet. diff --git a/docs/operations.md b/docs/operations.md index c82030e..b63c5d5 100644 --- a/docs/operations.md +++ b/docs/operations.md @@ -34,7 +34,7 @@ suppressed. `OCR_MAX_TOKENS_BUDGET` can set an aggregate input-plus-output token ceiling for the OCR diff review. The default `0` is unlimited. A positive ceiling is approximate rather than a hard billing cutoff because already-running work may complete; when it stops further dispatch, completed findings remain publishable and unreviewed files stay explicit as budget-attributed failed coverage. Such a run is partial and cannot automatically approve. -This aggregate budget is separate from both OCR's prompt/context `max_tokens` ceiling and the provider request's completion/output cap. The toolkit does not add an environment alias for OCR's prompt/context control. `OCR_LLM_MAX_COMPLETION_TOKENS` defaults to unset and, when set, overlays only the protocol-specific output field. The inherited OpenAI value was 58,888 in OCR 1.9.10 and is 16,384 in OCR 1.10.0 and 1.10.1. A gateway may reserve cost against that requested maximum before generation even when the eventual response would be short. The `/models` capability value does not reveal an account spending limit or reservation policy, so the toolkit never selects the cap from it automatically. Set an explicit cap when a deployment must not change with the qualified OCR version. +This aggregate budget is separate from both OCR's prompt/context `max_tokens` ceiling and the provider request's completion/output cap. The toolkit does not add an environment alias for OCR's prompt/context control. `OCR_LLM_MAX_COMPLETION_TOKENS` defaults to unset and, when set, overlays only the protocol-specific output field. The inherited OpenAI value was 58,888 in OCR 1.9.10 and is 16,384 in OCR 1.10.0 through 1.10.2. OCR 1.10.2 also applies that template-owned cap to grouping requests; an explicit toolkit override applies to all requests. A gateway may reserve cost against that requested maximum before generation even when the eventual response would be short. The `/models` capability value does not reveal an account spending limit or reservation policy, so the toolkit never selects the cap from it automatically. Set an explicit cap when a deployment must not change with the qualified OCR version. `OCR_REVIEW_EFFORT=medium` is the toolkit default for the qualified OCR release and permits two review rounds; `low` permits one and `high` permits three. This is a review-depth choice, not one of the three token controls. OCR first groups related changed files and may make group-filter requests; additional rounds can add requests, latency, and cost, but may stop early when they add no finding. The published GitLab example separately passes `OCR_MAX_TOOLS=0`, delegating the effective per-file tool-call limit to the installed OCR template instead of carrying a release-specific minimum. A positive value can raise that limit, but operators should check the release's behavioral qualification because CLI help, normalization text, and effective template value can differ. Exhausting the effective limit, an aggregate budget stop, or incomplete manifest coverage remains explicit and approval-ineligible; increasing either effort or tools is never a way to hide partial coverage. @@ -130,7 +130,7 @@ Suppression checks both the recorded inline position and compatible fingerprints Run OCR through `ocr-ci review --result PATH --stderr PATH -- ...`. This wrapper does not post to GitLab: it creates private artifacts, acquires enriched context when selected, asks the exact resolved and preflight-qualified OCR executable to preview the production refs/rules/selection/background without an LLM, then runs the model review only if OCR accepts that background. OCR owns the current recommendation and rejection thresholds; the toolkit has no threshold setting. A recognized soft diagnostic is reduced to a toolkit-authored `ocr.toolkit-advisory/v1` enum and two positive character counts. It is attached after publication DLP, rendered only with an exact receipt v5 in Technical details, and does not change warnings, coverage, DLP counts, telemetry, or automatic approval. A recognized hard character/file-size rejection stops before the model and lets `ocr-ci post` publish only a static numeric failure summary; the OCR path and raw diagnostic remain private. Unknown preview failures use the generic fail-closed diagnostic path. The ordinary review still validates the same background, the wrapper validates the complete output, and context/session/configuration data is removed. On an unclassified ordinary failure it prints only a bounded redacted stderr excerpt to the runner log; a classified provider failure keeps that excerpt private. Pass the paths and captured exit code to `ocr-ci post` afterward. Set `OCR_POST_ERROR_DETAILS=1` only when the generic path's safe excerpt should also appear in the merge-request failure note. Cleanup uncertainty blocks result publication. DLP atomically converts unsafe publication output into a safe `completed_with_errors` subset, but sanitizes unsafe private-only result fields without discarding an otherwise valid manifest or finding set. Safe findings are posted, unsafe finding content/warnings and unsafe optional fields are omitted, previous OCR comments remain, and matching prior findings are consumed one-for-one rather than duplicated. Receipt v5 and the `ocr.publication-dlp-signal/v2` marker distinguish `private-sanitized`, where the canonical published and approval-relevant projection is unchanged, from approval-ineligible `publication-filtered`, where the public projection is incomplete even when the receipt proves OCR coverage was complete. Posting uses only the validated original coverage kind and counts for status and Technical details; it never reconstructs or republishes rejected values. Horizontal tabs survive only in `existing_code` and `suggestion_code`; the unchanged values pass the same secret, PII, forbidden-value, laundering, and budget checks, while tabs elsewhere and all other control/format characters remain blocking. The same count-only JSON is logged as `OCR toolkit telemetry event` for optional CI collection/alerting. It is not an OTLP/network exporter and contains no rejected value or location. Never interpret a filtered subset as a full publishable review or as proof that OCR coverage was partial. -OCR 1.10.1 may add group labels, file membership, and round diagnostics to its private result. Safe values remain private; DLP removes or replaces unsafe values before atomic retention. These fields are deliberately absent from the canonical finding/posting projection and receipt v5, so private-only sanitization does not block an otherwise eligible auto-approval. If any group or round field appears inside receipt v5, the receipt is invalid and approval fails closed. Caller `--output`/`-o` is rejected before preview: only `ocr-ci review --result` owns the result descriptor and posting handoff. +OCR 1.10.2 may add group labels, file membership, grouping-task records, and round diagnostics to its private result/session data. Safe values remain private; DLP removes or replaces unsafe result values before atomic retention. These fields are deliberately absent from the canonical finding/posting projection and receipt v5, so private-only sanitization does not block an otherwise eligible auto-approval. If any group or round field appears inside receipt v5, the receipt is invalid and approval fails closed. Caller `--output`/`-o` is rejected before preview: only `ocr-ci review --result` owns the result descriptor and posting handoff. When OCR exits nonzero with a valid bounded `ocr.llm-retry-report/v1`, the toolkit reads only its closed error class, failure phase, terminal outcome, and HTTP status. It maps those facts to `authentication`, `authorization`, `rate-or-spending-limit`, `overloaded`, `timeout`, `network`, `endpoint-or-model-not-found`, `request-rejected`, `provider-unavailable`, `invalid-response`, `cancelled`, `mixed`, or `unknown`, then writes a completely toolkit-authored note. A runtime `404` remains `endpoint-or-model-not-found` because safely distinguishing the endpoint from the model would require trusting the raw response body. diff --git a/docs/security.md b/docs/security.md index b0c2327..5fcd1e7 100644 --- a/docs/security.md +++ b/docs/security.md @@ -115,7 +115,7 @@ Policy v2 adds optional toolkit-owned remediation threads without changing these - **Identity spoofing and privacy:** represent authors by provider-declared class plus run-local pseudonym; omit name, email, avatar, and profile URL. Unknown classification fails closed. - **TOCTOU/replay/cache poisoning:** bind snapshots and handles to version/ETag or digest, policy version, run, and expiry; use atomic store commits and uniform unavailable outcomes. - **PII/secret bypass and output laundering:** make retrieval, model egress, publication sinks, and private retention separate decisions. Minimize before model egress; publication DLP cannot undo prior disclosure. Scan decoded source and rendered forms so HTML comments/entities/tags, Markdown inline/reference/autolink destinations, controls, and Unicode formatting cannot hide exact protected values. Sink uncertainty yields a safe partial result; private-only uncertainty yields an atomically sanitized valid result or falls back to that partial form. Expose only low-cardinality receipt/summary/log signals in ordinary runs. The explicit local-only diagnostic switch retains owner-only raw private state, emits no receipt, and is rejected by the validated GitLab MR profile. -- **Upstream telemetry cardinality:** OCR 1.10.1 can place sorted changed paths in group span names/attributes and attach model-produced group labels and round counters. OCR telemetry is disabled by default. Enabling it authorizes the configured exporter to receive repository-derived high-cardinality data; content-logging disablement is not a promise that these identifiers disappear. The toolkit neither ingests nor duplicates them. +- **Upstream telemetry cardinality:** OCR 1.10.2 can place sorted changed paths in group span names/attributes and attach model-produced group labels and round counters. OCR telemetry is disabled by default. Enabling it authorizes the configured exporter to receive repository-derived high-cardinality data; content-logging disablement is not a promise that these identifiers disappear. The toolkit neither ingests nor duplicates them. - **OCR advisory spoofing:** raw OCR output cannot supply `_ocr_toolkit_advisory`. The toolkit adds only the exact numeric `ocr.toolkit-advisory/v1` shape after publication DLP; posting requires a valid receipt v5 and rejects duplicate, extended, unknown, or malformed forms. The advisory cannot change warnings, coverage, DLP, telemetry, findings, or approval. - **Local executable and subprocess confused deputy:** accept the OCR executable only through absolute search-path entries and reject resolution inside the reviewed repository. Stdio adapter deadlines cover request delivery as well as response/exit, use a clean environment and isolated directory, and terminate the process group on uncertainty. - **Marker and retry spoofing:** recognize ownership, run-summary, and setup identities only in the exact toolkit preamble. Repository/provider text containing marker lookalikes has no authority. Duplicate findings consume visible prior occurrences one-for-one; successful setup retries replace only previous toolkit setup-pending notes. diff --git a/examples/gitlab/README.md b/examples/gitlab/README.md index fb0dee4..1d563c1 100644 --- a/examples/gitlab/README.md +++ b/examples/gitlab/README.md @@ -2,7 +2,7 @@ [`ocr-review.gitlab-ci.yml`](ocr-review.gitlab-ci.yml) is the complete checksum-pinned pipeline. It defaults to identity-only review and contains the install, preflight, configure, review, and post lifecycle. Copy one mode file below into the pipeline's top-level `variables` mapping, or include exactly one file after reviewing its trust boundary. -The pipeline pins OCR 1.10.1 and explicitly sets `OCR_REVIEW_EFFORT=medium`, which permits two review rounds. Use `low` for one round when latency/cost is the priority, or `high` for three only after accepting the additional provider work. Semantic grouping and filtering are OCR behavior shared by every context mode; they do not change which merge-request text a mode admits. `OCR_MAX_TOOLS=0` delegates the effective per-file tool-call limit to the installed OCR template. `OCR_MAX_TOKENS_BUDGET` and the optional completion cap remain independent controls. +The pipeline pins OCR 1.10.2 and explicitly sets `OCR_REVIEW_EFFORT=medium`, which permits two review rounds. Use `low` for one round when latency/cost is the priority, or `high` for three only after accepting the additional provider work. Semantic grouping and filtering are OCR behavior shared by every context mode; they do not change which merge-request text a mode admits. `OCR_MAX_TOOLS=0` delegates the effective per-file tool-call limit to the installed OCR template. `OCR_MAX_TOKENS_BUDGET` and the optional completion cap remain independent controls. | Mode | Recipe | MR text admitted | External access | Automatic approval | | --- | --- | --- | --- | --- | diff --git a/examples/gitlab/ocr-review.gitlab-ci.yml b/examples/gitlab/ocr-review.gitlab-ci.yml index b7a9bef..f63b407 100644 --- a/examples/gitlab/ocr-review.gitlab-ci.yml +++ b/examples/gitlab/ocr-review.gitlab-ci.yml @@ -6,10 +6,10 @@ default: image: python:3.12-slim variables: - OCR_VERSION: "v1.10.1" + OCR_VERSION: "v1.10.2" OCR_TOOLKIT_VERSION: "0.8.4" OCR_TOOLKIT_CHECKSUMS_URL: "https://github.com/xeonvs/open-code-review-toolkit/releases/download/v${OCR_TOOLKIT_VERSION}/SHA256SUMS" - OCR_SHA256: "8b806c221d409727a21611b4a7952d8e15edadbbc25f5affccaeb8f677e4055c" + OCR_SHA256: "e9205614f80e009ee7b1f444c9da08486fb9ff6db022954fe9203d923ab720b2" OCR_POST_MODE: "draft" OCR_STRICT_POSTING: "true" # Default-on receipt-bound approval; external MCP always remains comment-only. diff --git a/scripts/ocr_compat.py b/scripts/ocr_compat.py index 13d8912..c0a6691 100644 --- a/scripts/ocr_compat.py +++ b/scripts/ocr_compat.py @@ -55,8 +55,9 @@ r"\b(fix|bug|documentation|docs|test|chore|refactor|performance)\b", re.I ) MATERIAL_NOTES_RE = re.compile( - r"\b(breaking|remove[ds]?|deprecat|security|vulnerab|protocol|schema|format|cli|flag|config|provider|gitlab)\b", - re.I, + r"\b(breaking|remove[ds]?|deprecat|security|vulnerab|protocol|schema|format|cli|flag|config|provider|gitlab|features?)\b" + r"|^[ \t]*-[ \t]*feat(?:\([^\r\n)]{1,80}\))?:", + re.I | re.M, ) REQUIRED_REVIEW_FLAGS = { "--audience", @@ -380,14 +381,17 @@ def validate_manifest(manifest: dict[str, Any], root: Path = ROOT) -> None: "semantic_grouping", }.issubset(capabilities): _fail(f"evidence does not qualify effort and grouping for {version}") - if contracts.get("semantic_grouping_probe") != { + expected_grouping_probe = { "default_effort": "medium", "filter_requests": 1, "grouping_requests": 1, "main_requests": 3, "result": "passed", "review_rounds": 2, - }: + } + if _version(version) >= (1, 10, 2): + expected_grouping_probe["grouping_completion_cap"] = 16_384 + if contracts.get("semantic_grouping_probe") != expected_grouping_probe: _fail(f"evidence does not qualify semantic grouping behavior for {version}") if contracts.get("completion_cap_probe") != { "explicit": 4_096, @@ -1443,7 +1447,7 @@ def _numeric_cli_probe( } -def _semantic_grouping_probe(binary: Path, directory: Path) -> dict[str, object]: +def _semantic_grouping_probe(binary: Path, version: str, directory: Path) -> dict[str, object]: """Drive one real two-file group through grouping and medium review rounds.""" root = directory / "semantic-grouping-probe" @@ -1495,6 +1499,7 @@ def _semantic_grouping_probe(binary: Path, directory: Path) -> dict[str, object] env=env, ) stages = list(_StubHandler.request_stages) + completion_caps = list(_StubHandler.completion_caps) try: sample = json.loads(output) except json.JSONDecodeError as exc: @@ -1511,7 +1516,13 @@ def _semantic_grouping_probe(binary: Path, directory: Path) -> dict[str, object] _fail("semantic grouping review did not preserve the accepted group") if stages != ["grouping", "main", "main", "filter", "main"]: _fail(f"default medium review emitted an unexpected stage sequence: {stages!r}") - return { + expected_grouping_cap = 16_384 if _version(version) >= (1, 10, 2) else 4_096 + if len(completion_caps) != len(stages) or completion_caps[0] != expected_grouping_cap: + _fail( + "semantic grouping review emitted an unexpected grouping completion cap: " + f"{completion_caps!r}" + ) + result: dict[str, object] = { "default_effort": "medium", "filter_requests": 1, "grouping_requests": 1, @@ -1519,6 +1530,9 @@ def _semantic_grouping_probe(binary: Path, directory: Path) -> dict[str, object] "result": "passed", "review_rounds": 2, } + if _version(version) >= (1, 10, 2): + result["grouping_completion_cap"] = expected_grouping_cap + return result def _completion_cap_probe(binary: Path, version: str, directory: Path) -> dict[str, object]: @@ -1843,7 +1857,7 @@ def run_contracts(binary: Path, version: str, directory: Path) -> dict[str, Any] }, } if _version(version) >= (1, 10, 0): - contracts["semantic_grouping_probe"] = _semantic_grouping_probe(binary, directory) + contracts["semantic_grouping_probe"] = _semantic_grouping_probe(binary, version, directory) if _version(version) >= (1, 9, 10): contracts["completion_cap_probe"] = _completion_cap_probe(binary, version, directory) if thinking_probe is not None: diff --git a/src/ocr_toolkit/preflight.py b/src/ocr_toolkit/preflight.py index e62204c..e049dbb 100644 --- a/src/ocr_toolkit/preflight.py +++ b/src/ocr_toolkit/preflight.py @@ -28,7 +28,7 @@ "Accept": "application/json", "User-Agent": "open-code-review-ci-preflight/1.0", } -EXPECTED_OCR_VERSION = "1.10.1" +EXPECTED_OCR_VERSION = "1.10.2" class PreflightError(Exception): diff --git a/tests/test_environment_contract.py b/tests/test_environment_contract.py index 0ba1936..5f80992 100644 --- a/tests/test_environment_contract.py +++ b/tests/test_environment_contract.py @@ -69,8 +69,8 @@ } EXAMPLE_DEFAULTS = { - "OCR_VERSION": "v1.10.1", - "OCR_SHA256": "8b806c221d409727a21611b4a7952d8e15edadbbc25f5affccaeb8f677e4055c", + "OCR_VERSION": "v1.10.2", + "OCR_SHA256": "e9205614f80e009ee7b1f444c9da08486fb9ff6db022954fe9203d923ab720b2", "OCR_TOOLKIT_VERSION": STABLE_TOOLKIT_VERSION, "OCR_TOOLKIT_CHECKSUMS_URL": "Release URL derived from `OCR_TOOLKIT_VERSION`", "OCR_TOOLKIT_WHEEL": "open_code_review_toolkit-${OCR_TOOLKIT_VERSION}-py3-none-any.whl", @@ -299,7 +299,7 @@ def test_example_local_defaults_match_the_pipeline() -> None: encoding="utf-8" ) for name, value in { - "OCR_VERSION": "v1.10.1", + "OCR_VERSION": "v1.10.2", "OCR_SHA256": EXAMPLE_DEFAULTS["OCR_SHA256"], "OCR_TOOLKIT_VERSION": STABLE_TOOLKIT_VERSION, "OCR_MAX_TOOLS": "0", diff --git a/tests/test_ocr_compat.py b/tests/test_ocr_compat.py index 0f3dd86..ffee380 100644 --- a/tests/test_ocr_compat.py +++ b/tests/test_ocr_compat.py @@ -43,8 +43,8 @@ def test_committed_manifest_is_valid_and_has_recommended_tested_baseline() -> No module.validate_manifest(manifest, PROJECT_ROOT) - assert manifest["recommended_version"] == "1.10.1" - assert manifest["monitoring_floor"] == "1.10.1" + assert manifest["recommended_version"] == "1.10.2" + assert manifest["monitoring_floor"] == "1.10.2" assert [(item["version"], item["status"]) for item in manifest["releases"]] == [ ("1.7.17", "tested"), ("1.8.0", "tested"), @@ -71,6 +71,7 @@ def test_committed_manifest_is_valid_and_has_recommended_tested_baseline() -> No ("1.9.10", "tested"), ("1.10.0", "tested"), ("1.10.1", "tested"), + ("1.10.2", "tested"), ] @@ -155,9 +156,9 @@ def test_discovery_filters_known_prerelease_and_old_versions() -> None: def test_discovery_pages_until_the_monitoring_floor() -> None: module = load_script() manifest = module.load_json(MANIFEST) - first_page = [release("1.10.2")] + first_page = [release("1.10.3")] first_page.extend({"draft": True} for _ in range(module.MAX_RELEASES_PER_PAGE - 1)) - second_page = [release("1.10.1")] + second_page = [release("1.10.2")] requested: list[str] = [] def fake_request(url: str) -> list[dict[str, Any]]: @@ -167,14 +168,14 @@ def fake_request(url: str) -> list[dict[str, Any]]: with patched_attr(module, "_request_json", fake_request): unseen = module.discover_unseen(manifest) - assert [item["tag_name"] for item in unseen] == ["v1.10.2"] + assert [item["tag_name"] for item in unseen] == ["v1.10.3"] assert len(requested) == 2 def test_discovery_fails_when_bounded_pages_do_not_reach_floor() -> None: module = load_script() manifest = module.load_json(MANIFEST) - page = [release("1.10.2")] + page = [release("1.10.3")] page.extend({"draft": True} for _ in range(module.MAX_RELEASES_PER_PAGE - 1)) with patched_attr(module, "_request_json", lambda _url: page): @@ -219,14 +220,14 @@ def test_qualification_matrix_accepts_the_next_manual_patch() -> None: module = load_script() manifest = module.load_json(MANIFEST) - matrix = module.qualification_matrix(manifest, [release("1.10.2")]) + matrix = module.qualification_matrix(manifest, [release("1.10.3")]) assert matrix == { "include": [ { - "comparison_version": "1.10.1", - "tag": "v1.10.2", - "tested_baseline_version": "1.10.1", + "comparison_version": "1.10.2", + "tag": "v1.10.3", + "tested_baseline_version": "1.10.2", } ] } @@ -255,6 +256,12 @@ def test_automatic_safe_policy_is_conservative() -> None: release_notes="fix: change JSON schema for comments", contracts_passed=True, ) + feature_patch, feature_reasons = module.classify_candidate( + comparison_version="1.10.1", + version="1.10.2", + release_notes="## Features\n\n- feat(rules): add Solidity support\n\n## Bug Fixes\n\n- fix: correct grouping", + contracts_passed=True, + ) minor, minor_reasons = module.classify_candidate( comparison_version="1.7.17", version="1.8.0", @@ -272,6 +279,8 @@ def test_automatic_safe_policy_is_conservative() -> None: assert reasons assert breaking == "human-review-required" assert any("material" in reason for reason in breaking_reasons) + assert feature_patch == "human-review-required" + assert any("material" in reason for reason in feature_reasons) assert minor == "human-review-required" assert any("major/minor" in reason for reason in minor_reasons) assert skipped == "human-review-required" @@ -1428,11 +1437,11 @@ def test_prepare_update_rejects_human_review_candidate(tmp_path: Path) -> None: module = load_script() evidence = { "schema_version": 2, - "version": "1.10.2", + "version": "1.10.3", "result": "compatible", "classification": "human-review-required", - "comparison_version": "1.10.1", - "tested_baseline_version": "1.10.1", + "comparison_version": "1.10.2", + "tested_baseline_version": "1.10.2", } with pytest.raises(module.CompatibilityError, match="bounded conclusion"): @@ -1451,8 +1460,8 @@ def test_prepare_update_requires_human_review_for_minor_transition() -> None: "version": "1.11.0", "result": "compatible", "classification": "automatic-safe", - "comparison_version": "1.10.1", - "tested_baseline_version": "1.10.1", + "comparison_version": "1.10.2", + "tested_baseline_version": "1.10.2", } with pytest.raises(module.CompatibilityError, match="explicit human review"): @@ -1493,8 +1502,8 @@ def test_prepare_update_rejects_nonadjacent_minor_transition() -> None: "version": "1.12.0", "result": "compatible", "classification": "human-review-required", - "comparison_version": "1.10.1", - "tested_baseline_version": "1.10.1", + "comparison_version": "1.10.2", + "tested_baseline_version": "1.10.2", } with pytest.raises(module.CompatibilityError, match="contiguous release sequence"): @@ -1511,11 +1520,11 @@ def test_prepare_update_rejects_conclusion_outside_evidence_chain() -> None: module = load_script() evidence = { "schema_version": 2, - "version": "1.10.2", + "version": "1.10.3", "result": "compatible", "classification": "automatic-safe", - "comparison_version": "1.10.1", - "tested_baseline_version": "1.10.1", + "comparison_version": "1.10.2", + "tested_baseline_version": "1.10.2", } with pytest.raises(module.CompatibilityError, match="only evidence versions"): @@ -1523,7 +1532,7 @@ def test_prepare_update_rejects_conclusion_outside_evidence_chain() -> None: manifest_path=MANIFEST, evidence=evidence, fragment_number=72, - human_conclusions={"1.10.3": "Synthetic unrelated conclusion."}, + human_conclusions={"1.10.4": "Synthetic unrelated conclusion."}, root=PROJECT_ROOT, ) @@ -1535,11 +1544,11 @@ def test_prepare_update_rejects_invalid_optional_reviewed_conclusion( module = load_script() evidence = { "schema_version": 2, - "version": "1.10.2", + "version": "1.10.3", "result": "compatible", "classification": "automatic-safe", - "comparison_version": "1.10.1", - "tested_baseline_version": "1.10.1", + "comparison_version": "1.10.2", + "tested_baseline_version": "1.10.2", } with pytest.raises(module.CompatibilityError, match="bounded plain text"): @@ -1547,7 +1556,7 @@ def test_prepare_update_rejects_invalid_optional_reviewed_conclusion( manifest_path=MANIFEST, evidence=evidence, fragment_number=72, - human_conclusions={"1.10.2": conclusion}, + human_conclusions={"1.10.3": conclusion}, root=PROJECT_ROOT, ) diff --git a/tests/test_operations_docs.py b/tests/test_operations_docs.py index fbd9a72..3b614fb 100644 --- a/tests/test_operations_docs.py +++ b/tests/test_operations_docs.py @@ -460,6 +460,8 @@ def test_ocr_compatibility_workflow_is_bounded_and_protected() -> None: policy = (PROJECT_ROOT / "docs" / "compatibility.md").read_text(encoding="utf-8") assert "schedule:" in workflow + assert '- cron: "15 7 * * *"' in workflow + assert '- cron: "41 5 * * *"' not in workflow assert "workflow_dispatch:" in workflow assert "contents: read" in workflow assert "issues: write" in workflow @@ -499,15 +501,19 @@ def test_ocr_compatibility_workflow_is_bounded_and_protected() -> None: "OCR 1.9.10 — toolkit 0.8.0 target and 0.8.2 predecessor", "OCR 1.10.0 — toolkit 0.8.2 and 0.8.3 target", "OCR 1.10.1 — toolkit 0.8.4 target", + "OCR 1.10.2 — toolkit 0.8.5 target", "ocr.toolkit-advisory/v1", "ocr.llm-retry-report/v1", "not toolkit telemetry", "Deploy toolkit 0.8.2 or 0.8.3 directly with OCR 1.10.0", "Deploy toolkit 0.8.4 directly with OCR 1.10.1", + "Deploy toolkit 0.8.5 directly with OCR 1.10.2", "max_completion_tokens=16384", "do not install OCR 1.9.10 as an intermediate step", ): assert contract in policy + assert "daily trigger is scheduled for `07:15 UTC`" in policy + assert "feature-bearing patch notes" in policy assert "--search" not in workflow assert "git push origin main" not in workflow assert "gh pr merge" not in workflow diff --git a/tests/test_runtime_helpers.py b/tests/test_runtime_helpers.py index ce7e254..72ae82e 100644 --- a/tests/test_runtime_helpers.py +++ b/tests/test_runtime_helpers.py @@ -1476,7 +1476,7 @@ def test_invalid_json_error_does_not_echo_secret_payload(self) -> None: class PreflightTests(unittest.TestCase): def test_validate_ocr_binary_accepts_supported_version(self) -> None: completed = subprocess.CompletedProcess( - args=["ocr", "--version"], returncode=0, stdout="ocr 1.10.1\n", stderr="" + args=["ocr", "--version"], returncode=0, stdout="ocr 1.10.2\n", stderr="" ) with ( patched_attr(preflight.shutil, "which", lambda _name: "/usr/bin/ocr"), From e8cae9bbec12e953a8a3146063cab288fca65ba4 Mon Sep 17 00:00:00 2001 From: xeonvs <11463419+xeonvs@users.noreply.github.com> Date: Thu, 27 Aug 2026 11:08:53 +0200 Subject: [PATCH 3/7] Add bounded provider diagnostics --- PLANS.md | 14 +- changelog.d/149.feature.md | 5 + docs/engineering/review_signal_ownership.md | 15 +- docs/gitlab.md | 2 +- docs/operations.md | 10 +- docs/security.md | 4 +- src/ocr_toolkit/posting/workflow.py | 2 +- src/ocr_toolkit/provider_failure.py | 208 +++++++++++++++++--- src/ocr_toolkit/review_runner.py | 24 +-- tests/test_operations_docs.py | 8 +- tests/test_posting_helpers.py | 11 +- tests/test_provider_failure.py | 165 ++++++++++++++-- tests/test_review_runner.py | 11 +- 13 files changed, 393 insertions(+), 86 deletions(-) create mode 100644 changelog.d/149.feature.md diff --git a/PLANS.md b/PLANS.md index 96eeb06..816150e 100644 --- a/PLANS.md +++ b/PLANS.md @@ -141,8 +141,8 @@ state for this task: `release-deferred` at the green Draft PR boundary. | `WQ-01` | `completed` | Planning commit `123c331` was signed and pushed once; Draft PR #150 is open. | | `WQ-02` | `completed` | Milestone `v0.8.5` owns assigned open issues #149/#151; hosted run 33055459209 qualified exact OCR 1.10.2. | | `WQ-03` | `completed` | OCR 1.10.2 pins/evidence, schedule, classifier correction, current docs/Rules, source audit, backlog reconciliation, and local no-LLM update are complete. | -| `WQ-04` | `pending` | Implement the single closed provider diagnostic projection/renderer and GitLab remediation with boundary regressions. | -| `WQ-05` | `pending` | Finalize changelog/docs/plan truth and run the complete local gate once. | +| `WQ-04` | `completed` | One closed provider-neutral projection renders the single numeric review log line; GitLab keeps only the prior reason with safer remediation. | +| `WQ-05` | `in_progress` | Finalize changelog/docs/plan truth and run the complete local gate once. | | `WQ-06` | `pending` | Push the complete history once, reconcile hosted CI, update Draft/issue coordination with exact evidence, and verify final state. | #### Locked Decisions @@ -203,6 +203,10 @@ state for this task: `release-deferred` at the green Draft PR boundary. LLM/provider call or user OCR configuration change occurred. - 2026-08-27: 104 focused compatibility/workflow/environment tests pass; manifest validation, focused Ruff, Towncrier draft, and `git diff --check` pass. +- 2026-08-27: 403 provider parser, review runner, posting, approval, and documentation + tests plus 217 subtests pass. The reviewed flow is `private OCR result -> strict retry + parser -> closed immutable reason/detail/status/counts -> one local line`; GitLab uses + only the reason, and DLP/receipt/telemetry/approval projections are unchanged. #### Risks And Recovery @@ -226,9 +230,9 @@ state for this task: `release-deferred` at the green Draft PR boundary. #### Resume Point -Begin `WQ-04` from the reviewed OCR 1.10.2 slice. Implement the single closed numeric -provider diagnostic owner and GitLab remediation regressions without changing public -summary, DLP, receipt, telemetry, or approval schemas. Do not push until WQ-05 completes. +Finish `WQ-05`: self-review and commit the provider-diagnostic slice, then update final +Draft-only plan truth and run the one complete local quality/coverage/manifest/lock/ +Towncrier/Gitleaks/diff gate. Do not push until that gate and final handoff commit pass. #### Plan Fidelity Check diff --git a/changelog.d/149.feature.md b/changelog.d/149.feature.md new file mode 100644 index 0000000..d809a9e --- /dev/null +++ b/changelog.d/149.feature.md @@ -0,0 +1,5 @@ +Classified LLM-provider failures now emit at most one toolkit-authored CI diagnostic line with closed HTTP detail and non-zero aggregate retry counts from a fully validated `ocr.llm-retry-report/v1`. + +- **Added:** HTTP `402` is shown as `http-payment-required`, HTTP `429` as `http-rate-limited`, a uniform status is shown only when all terminal failures share it, and mixed details are deterministically count-aggregated. +- **Changed:** The `rate-or-spending-limit` GitLab remediation now recommends lowering `OCR_REVIEW_CONCURRENCY` and/or `OCR_LLM_MAX_COMPLETION_TOKENS`, starting a new merge request pipeline, and checking provider request/account limits without claiming either setting caused the failure. +- **Unchanged:** The GitLab summary/reason, previous-review preservation, DLP, receipt v5, toolkit telemetry, and automatic-approval contracts are unchanged; raw provider-controlled fields and stderr remain private. diff --git a/docs/engineering/review_signal_ownership.md b/docs/engineering/review_signal_ownership.md index 4273181..e5678f3 100644 --- a/docs/engineering/review_signal_ownership.md +++ b/docs/engineering/review_signal_ownership.md @@ -1,15 +1,15 @@ # Review signal ownership -This matrix records the completed BL-017 audit for toolkit 0.8.2 and the -qualified OCR 1.10.0 runtime. It separates provider/review telemetry from the -toolkit's deterministic control-plane receipts. It is an ownership map, not a -new telemetry API. +This matrix records the completed BL-017 audit and its narrow toolkit 0.8.5 +operator-diagnostic reconciliation. It separates provider/review telemetry from +the toolkit's deterministic control-plane receipts. It is an ownership map, not +a new telemetry API. ## Source-to-signal matrix | Signal | Authoritative source | Toolkit projection | Privacy and authority boundary | | --- | --- | --- | --- | -| Provider/model identity, request/session correlation, retries, latency, HTTP outcome, and cost | OCR and its configured provider telemetry | None, except the closed provider-failure class used for static failure text | Raw identities, request IDs, response text, and provider codes do not enter GitLab notes, receipt v5, DLP signals, or approval. | +| Provider/model identity, request/session correlation, retries, latency, HTTP outcome, and cost | OCR and its configured provider telemetry | One closed provider-failure class for static GitLab text; on failure, at most one local toolkit-authored line may add closed protocol detail, one shared HTTP status, and non-zero bounded retry counters | Raw identities, request IDs, response text, provider codes, URLs, paths, warnings, and stderr do not enter the projection, GitLab notes, receipt v5, DLP signals, toolkit telemetry, or approval. HTTP detail does not prove a provider business cause. | | Prompt, completion, cached, reasoning, and total tokens | OCR result and OCR telemetry | Closed non-negative provider-neutral token buckets in the result summary and canonical publication comparison | Unknown fields are ignored; malformed or contradictory counters are unavailable. Token counts never authorize approval or automatic routing. | | Review effort and executed rounds | Operator-owned root `effort` config and OCR runtime | `OCR_REVIEW_EFFORT` writes one closed `low`, `medium`, or `high` setting; the toolkit does not publish inferred round telemetry | Merge-request content cannot select effort. Budget or incomplete coverage remains approval-ineligible through the existing result contract. | | Semantic grouping, group file membership, per-group spans, and filter activity | OCR runtime and OCR telemetry | Additive private result fields may be DLP-sanitized; no group or round field enters receipt v5, GitLab text, fingerprints, severity, lifecycle commands, toolkit telemetry, or approval | Group labels are model-produced. Group keys are sorted changed paths, so both are untrusted and potentially high-cardinality. | @@ -41,8 +41,9 @@ The established owners already cover provider operations, completeness, evidence use, context degradation, publication safety, posting, and approval. The remaining data is either provider-specific telemetry already owned by OCR or untrusted high-cardinality group data that should not be duplicated. -Therefore BL-017 concludes `no-new-layer`: toolkit 0.8.2 adds no exporter, -metric schema, context telemetry implementation, or automatic routing. +Therefore BL-017 concludes `no-new-layer`: the toolkit adds no exporter, +metric schema, context telemetry implementation, or automatic routing. The +bounded failure-only operator line does not reopen that conclusion. No safe stable objective currently supports automatic profile routing or a generic review-quality score. BL-016 remains parked, BL-018 remains conditional diff --git a/docs/gitlab.md b/docs/gitlab.md index ba96e9c..8c7ba86 100644 --- a/docs/gitlab.md +++ b/docs/gitlab.md @@ -62,7 +62,7 @@ The toolkit authenticates the token owner with live `GET /user`. No configured b Remediation text is untrusted review history. It may locate a claim that OCR must re-check against current code and tests, but it cannot change severity, prove a fix, suppress or resolve a finding, issue a lifecycle command, or authorize approval. Any admitted remediation record therefore makes the review comment-only. DLP-clean non-remediation context does not independently disable an otherwise eligible receipt; a DLP rejection cannot make approval easier. -Provider configuration is forge-neutral. Configure and preflight share one normalized absolute HTTPS API root, explicit protocol, headers, request-body controls, and optional models URL. A protocol-mismatched terminal endpoint, embedded credential, or fragment fails before OCR. On a classified provider failure, `post` publishes only a static safe reason and guidance; raw provider/model fields, response bodies, request IDs, paths, warnings, and stderr remain private. A `429` note lists rate, spending, and requested-cap reservation as possibilities and may suggest `OCR_LLM_MAX_COMPLETION_TOKENS=4096` without claiming a cause. The previous successful review remains visible, no failed-result findings are posted, and approval is not attempted. +Provider configuration is forge-neutral. Configure and preflight share one normalized absolute HTTPS API root, explicit protocol, headers, request-body controls, and optional models URL. A protocol-mismatched terminal endpoint, embedded credential, or fragment fails before OCR. On a classified provider failure, `post` publishes only a static safe reason and guidance; raw provider/model fields, response bodies, request IDs, paths, warnings, and stderr remain private. The review log may contain one toolkit-authored line of closed HTTP detail and non-zero aggregate retry counts from a fully validated retry report, but the GitLab summary does not. A `rate-or-spending-limit` note suggests lowering `OCR_REVIEW_CONCURRENCY` and/or `OCR_LLM_MAX_COMPLETION_TOKENS`, starting a new merge request pipeline, and then checking provider request/account limits without claiming either setting caused the failure. The previous successful review remains visible, no failed-result findings are posted, and approval is not attempted. When a merge request introduces a repository-owned OCR rules path absent from both trusted baselines, `review` stops before OCR and `post` may publish only the static setup-pending message after hostile identity validation. The source file never becomes policy evidence for its own merge request. diff --git a/docs/operations.md b/docs/operations.md index b63c5d5..202acbd 100644 --- a/docs/operations.md +++ b/docs/operations.md @@ -34,7 +34,7 @@ suppressed. `OCR_MAX_TOKENS_BUDGET` can set an aggregate input-plus-output token ceiling for the OCR diff review. The default `0` is unlimited. A positive ceiling is approximate rather than a hard billing cutoff because already-running work may complete; when it stops further dispatch, completed findings remain publishable and unreviewed files stay explicit as budget-attributed failed coverage. Such a run is partial and cannot automatically approve. -This aggregate budget is separate from both OCR's prompt/context `max_tokens` ceiling and the provider request's completion/output cap. The toolkit does not add an environment alias for OCR's prompt/context control. `OCR_LLM_MAX_COMPLETION_TOKENS` defaults to unset and, when set, overlays only the protocol-specific output field. The inherited OpenAI value was 58,888 in OCR 1.9.10 and is 16,384 in OCR 1.10.0 through 1.10.2. OCR 1.10.2 also applies that template-owned cap to grouping requests; an explicit toolkit override applies to all requests. A gateway may reserve cost against that requested maximum before generation even when the eventual response would be short. The `/models` capability value does not reveal an account spending limit or reservation policy, so the toolkit never selects the cap from it automatically. Set an explicit cap when a deployment must not change with the qualified OCR version. +This aggregate budget is separate from both OCR's prompt/context `max_tokens` ceiling and the provider request's completion/output cap. The toolkit does not add an environment alias for OCR's prompt/context control. `OCR_LLM_MAX_COMPLETION_TOKENS` defaults to unset and, when set, overlays only the protocol-specific output field. The inherited OpenAI value was 58,888 in OCR 1.9.10 and is 16,384 in OCR 1.10.0 through 1.10.2. OCR 1.10.2 also applies that template-owned cap to grouping requests; an explicit toolkit override applies to all requests. A gateway may reserve cost against that requested maximum before generation even when the eventual response would be short. The `/models` capability value does not reveal an account spending limit or reservation policy, so the toolkit never selects the cap from it automatically. Set an explicit cap when a deployment must not change with the qualified OCR version; `4096` is one practical operator-selected value, not a universal default. `OCR_REVIEW_EFFORT=medium` is the toolkit default for the qualified OCR release and permits two review rounds; `low` permits one and `high` permits three. This is a review-depth choice, not one of the three token controls. OCR first groups related changed files and may make group-filter requests; additional rounds can add requests, latency, and cost, but may stop early when they add no finding. The published GitLab example separately passes `OCR_MAX_TOOLS=0`, delegating the effective per-file tool-call limit to the installed OCR template instead of carrying a release-specific minimum. A positive value can raise that limit, but operators should check the release's behavioral qualification because CLI help, normalization text, and effective template value can differ. Exhausting the effective limit, an aggregate budget stop, or incomplete manifest coverage remains explicit and approval-ineligible; increasing either effort or tools is never a way to hide partial coverage. @@ -132,11 +132,13 @@ Run OCR through `ocr-ci review --result PATH --stderr PATH -- ...`. This wrapper OCR 1.10.2 may add group labels, file membership, grouping-task records, and round diagnostics to its private result/session data. Safe values remain private; DLP removes or replaces unsafe result values before atomic retention. These fields are deliberately absent from the canonical finding/posting projection and receipt v5, so private-only sanitization does not block an otherwise eligible auto-approval. If any group or round field appears inside receipt v5, the receipt is invalid and approval fails closed. Caller `--output`/`-o` is rejected before preview: only `ocr-ci review --result` owns the result descriptor and posting handoff. -When OCR exits nonzero with a valid bounded `ocr.llm-retry-report/v1`, the toolkit reads only its closed error class, failure phase, terminal outcome, and HTTP status. It maps those facts to `authentication`, `authorization`, `rate-or-spending-limit`, `overloaded`, `timeout`, `network`, `endpoint-or-model-not-found`, `request-rejected`, `provider-unavailable`, `invalid-response`, `cancelled`, `mixed`, or `unknown`, then writes a completely toolkit-authored note. A runtime `404` remains `endpoint-or-model-not-found` because safely distinguishing the endpoint from the model would require trusting the raw response body. +When OCR exits nonzero with a valid bounded `ocr.llm-retry-report/v1`, the toolkit reads only its closed error class, failure phase, terminal outcome, HTTP status, and schema counters. It maps those facts to `authentication`, `authorization`, `rate-or-spending-limit`, `overloaded`, `timeout`, `network`, `endpoint-or-model-not-found`, `request-rejected`, `provider-unavailable`, `invalid-response`, `cancelled`, `mixed`, or `unknown`, then writes a completely toolkit-authored note. A runtime `404` remains `endpoint-or-model-not-found` because safely distinguishing the endpoint from the model would require trusting the raw response body. -For `429`, the note says that ordinary throttling, an account or API-key spending limit, or cost reservation from the requested output cap are all possible. Retry later and check provider limits. If short probes pass while a full review fails before generation, try an explicit `OCR_LLM_MAX_COMPLETION_TOKENS`, for example `4096`; this is a diagnostic workaround, not a claim that the cap was the cause. +The review step emits at most one numeric line beginning `OCR provider diagnostics:`. It distinguishes HTTP `402` as `http-payment-required` and `429` as `http-rate-limited`, includes a shared bounded status only when every terminal failure has that status, and includes only non-zero failed, cancelled, retried, total-retry, and recovered request counters. Mixed terminal details are sorted and count-aggregated rather than emitted per request. These protocol facts do not prove ordinary throttling, a spending policy, or cost reservation from the requested output cap. The line is local operator feedback, not toolkit telemetry, a receipt field, a DLP input, a GitLab summary field, or an approval signal. -Raw provider/model identities, response bodies, error codes and messages, request IDs, paths, warnings, and stderr remain in owner-only private artifacts for a classified provider failure. `OCR_POST_ERROR_DETAILS=1` cannot add them to that note. Normal findings from the failed result are ignored, the previous successful review is preserved, and automatic approval is not attempted. Missing, oversized, malformed, or internally contradictory retry reports keep the existing generic failure path instead of guessing a classification. +The `rate-or-spending-limit` note keeps that intentionally broad public reason and recommends lowering `OCR_REVIEW_CONCURRENCY` and/or `OCR_LLM_MAX_COMPLETION_TOKENS`, starting a new merge request pipeline, and then checking provider request/account limits if the failure persists. Neither setting is presented as the proven cause. + +Raw provider/model identities, response bodies, error codes and messages, request IDs, paths, warnings, and stderr remain in owner-only private artifacts for a classified provider failure. `OCR_POST_ERROR_DETAILS=1` cannot add them to that note or the numeric line. Normal findings from the failed result are ignored, the previous successful review is preserved, and automatic approval is not attempted. Missing, oversized, malformed, or internally contradictory retry reports keep the existing generic failure path instead of guessing a classification. For a local diagnosis, add `--preserve-private-artifacts` before the `--` separator. The command retains the isolated OCR home plus repository-local private evidence/context artifacts, prints only their paths, and deliberately leaves the raw OCR result without receipt v5; do not pass that result to `ocr-ci post`. It writes `.review-context/private-dlp-decisions.json` with value-free bounded JSON paths, closed reason and detector subtype, size units, and SHA-256 for up to 1,000 rejected keys/values, plus explicit truncation and omitted-decision counts. Use matching digests to identify one repeated technical value and inspect the retained raw result locally before deciding whether a conservative PII match is a false positive; the sidecar itself is not proof that content is safe. These owner-only files can contain source/provider context, prompts, model responses, tool arguments/results, and generated runtime configuration. Inspect them locally, keep them out of commits and shared artifacts, then delete them after extracting the needed evidence. Ordinary runs do not retain this attribution. The authoritative GitLab merge-request profile rejects the flag before OCR execution and performs normal cleanup; an arbitrary `CI=true` value neither grants nor blocks the local mode. diff --git a/docs/security.md b/docs/security.md index 5fcd1e7..8617770 100644 --- a/docs/security.md +++ b/docs/security.md @@ -23,7 +23,7 @@ An operator controls CI configuration, direct MCP servers, adapter commands/endp 5. Typed repository evidence crosses into the compact bootstrap and mandatory read-only evidence MCP. During enriched OCR, only the same built-in process's fixed `context_list`/`context_get` can read committed local handles; adapter/provider network paths are absent from the model loop. 6. Persisted evidence, context, results, OCR configuration, private pre-execution status, and receipts re-enter hostile parsing. OCR runs in a fresh isolated home; session, configuration, adapter, and context data crosses deterministic cleanup before a result becomes publishable. 7. In the direct-MCP GitLab profile, operator-configured external MCP is remote HTTPS only; developer-local execution may pass explicit stdio command/setup configuration. Server-authored descriptions and schemas cross into plan and main model context; model-generated arguments cross to allowed tools; textual responses cross back to the model and OCR session. This path is separate from M5 adapters. -8. The complete successful OCR result crosses independent publication DLP, deterministic GitLab publication, suppression, and receipt-v5 approval policy. On non-zero OCR exit with a valid bounded `ocr.llm-retry-report/v1`, only a closed provider-neutral reason may cross the separate strict parser into a toolkit-authored failure note; in that classified path raw result fields and stderr remain private, normal findings are ignored, and approval is unreachable. Missing or invalid retry diagnostics use the pre-existing generic failure boundary. +8. The complete successful OCR result crosses independent publication DLP, deterministic GitLab publication, suppression, and receipt-v5 approval policy. On non-zero OCR exit with a valid bounded `ocr.llm-retry-report/v1`, only a closed provider-neutral reason may cross into a toolkit-authored failure note; one separate local line may contain closed protocol detail, a shared HTTP status, and bounded aggregate counts from the same strict parser. Raw result fields and stderr remain private, normal findings are ignored, and approval is unreachable. Missing or invalid retry diagnostics use the pre-existing generic failure boundary. 9. A release candidate crosses protected-base authorization, publication, provenance, and live readback. ### Security objectives @@ -37,7 +37,7 @@ An operator controls CI configuration, direct MCP servers, adapter commands/endp - Adapter credentials and services enforce tenant/object/operation/field authorization independently; reference syntax and authentication alone never authorize a resource. - Context policy cannot come from the source branch, context budgets cannot evict repository evidence, and model-facing context cannot add a network, arbitrary ID/URL, search, traversal, or write path. - Publication and retention are independent from retrieval/model egress. Cleanup uncertainty blocks publication. DLP selects exact posting sinks separately from private OCR metadata. Unsafe sinks produce an explicit safe partial result; unsafe private-only keys/values are removed or replaced before retention without discarding a still-valid manifest or safe findings. Both paths atomically destroy the rejected value/location and expose only closed counts. Publication filtering preserves prior review state, matches repeated fingerprints one-for-one, and blocks approval; private-only sanitization may continue through all existing approval gates only after exact canonical equivalence. -- Classified provider failure reporting consumes only validated retry class, phase, terminal outcome, and HTTP status. Provider/model identity, URL, response body, error code or message, request ID, path, warning text, token, and stderr cannot enter the static note or become a receipt, DLP, telemetry, severity, finding, or approval signal. +- Classified provider failure reporting consumes only validated retry class, phase, terminal outcome, HTTP status, and schema counters. Provider/model identity, URL, response body, error code or message, request ID, path, warning text, token, and stderr cannot enter the static note or closed numeric line or become a receipt, DLP, telemetry, severity, finding, or approval signal. A status-derived label describes protocol state only; it cannot assert the provider's billing or admission cause. - OCR semantic-group labels, path-derived group keys, file membership, and round diagnostics are untrusted private data. They may be DLP-sanitized in the owner-only result but cannot enter findings, GitLab text, receipt v5, fingerprints, severity, lifecycle commands, toolkit telemetry, or approval. Receipt extensions fail closed. - Provider mutations bind reviewed identity where supported; ambiguous inline creates use one author-bound readback without retry, and unresolved ambiguity preserves prior state. - Secrets remain outside repository-controlled context, public notes, fixtures, and release artifacts. diff --git a/src/ocr_toolkit/posting/workflow.py b/src/ocr_toolkit/posting/workflow.py index 0bcdeda..a75a3a8 100644 --- a/src/ocr_toolkit/posting/workflow.py +++ b/src/ocr_toolkit/posting/workflow.py @@ -1151,7 +1151,7 @@ def post_llm_provider_failure( ), ProviderFailureReason.RATE_OR_SPENDING_LIMIT: ( "The LLM provider rejected the request under a rate or spending limit.", - "The cause may be ordinary throttling, an account or API-key spending limit, or cost reservation from the requested output cap. Retry later and check account limits. If short probes pass but a full review fails before generation, try an explicit `OCR_LLM_MAX_COMPLETION_TOKENS`, for example `4096`.", + "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.", ), ProviderFailureReason.OVERLOADED: ( "The LLM provider reported that it was overloaded.", diff --git a/src/ocr_toolkit/provider_failure.py b/src/ocr_toolkit/provider_failure.py index d8ae52f..ff373fb 100644 --- a/src/ocr_toolkit/provider_failure.py +++ b/src/ocr_toolkit/provider_failure.py @@ -4,6 +4,7 @@ from collections import Counter from collections.abc import Mapping +from dataclasses import dataclass from enum import StrEnum from typing import Any @@ -47,6 +48,55 @@ class ProviderFailureReason(StrEnum): UNKNOWN = "unknown" +class ProviderFailureDetail(StrEnum): + """Closed protocol detail safe for one toolkit-authored CI diagnostic.""" + + HTTP_BAD_REQUEST = "http-bad-request" + HTTP_UNAUTHORIZED = "http-unauthorized" + HTTP_PAYMENT_REQUIRED = "http-payment-required" + HTTP_FORBIDDEN = "http-forbidden" + HTTP_NOT_FOUND = "http-not-found" + HTTP_REQUEST_TIMEOUT = "http-request-timeout" + HTTP_CONFLICT = "http-conflict" + HTTP_CONTENT_TOO_LARGE = "http-content-too-large" + HTTP_UNPROCESSABLE_CONTENT = "http-unprocessable-content" + HTTP_RATE_LIMITED = "http-rate-limited" + HTTP_GATEWAY_TIMEOUT = "http-gateway-timeout" + HTTP_OVERLOADED = "http-overloaded" + HTTP_SERVER_ERROR = "http-server-error" + HTTP_NON_SUCCESS = "http-non-success" + RESPONSE_DECODE = "response-decode" + RESPONSE_STATUS = "response-status" + STREAM = "stream" + CANCELLED = "cancelled" + TIMEOUT = "timeout" + NETWORK = "network" + UNKNOWN = "unknown" + + +@dataclass(frozen=True) +class ProviderFailureProjection: + """Validated provider-neutral failure state retained only for operator output.""" + + reason: ProviderFailureReason + details: tuple[tuple[ProviderFailureDetail, int], ...] + status_code: int | None + failed_requests: int + cancelled_requests: int + retried_requests: int + total_retries: int + recovered_requests: int + + +@dataclass(frozen=True) +class _AttemptFailure: + """Internal terminal-attempt projection after retry-report validation.""" + + reason: ProviderFailureReason + detail: ProviderFailureDetail + status_code: int | None + + class RetryReportError(Exception): """The private retry report is malformed or internally inconsistent.""" @@ -79,6 +129,32 @@ def _http_reason(status_code: int) -> ProviderFailureReason: return ProviderFailureReason.REQUEST_REJECTED +def _http_detail(status_code: int) -> ProviderFailureDetail: + """Map one bounded HTTP status to provider-neutral protocol detail.""" + + return { + 400: ProviderFailureDetail.HTTP_BAD_REQUEST, + 401: ProviderFailureDetail.HTTP_UNAUTHORIZED, + 402: ProviderFailureDetail.HTTP_PAYMENT_REQUIRED, + 403: ProviderFailureDetail.HTTP_FORBIDDEN, + 404: ProviderFailureDetail.HTTP_NOT_FOUND, + 408: ProviderFailureDetail.HTTP_REQUEST_TIMEOUT, + 409: ProviderFailureDetail.HTTP_CONFLICT, + 413: ProviderFailureDetail.HTTP_CONTENT_TOO_LARGE, + 422: ProviderFailureDetail.HTTP_UNPROCESSABLE_CONTENT, + 429: ProviderFailureDetail.HTTP_RATE_LIMITED, + 504: ProviderFailureDetail.HTTP_GATEWAY_TIMEOUT, + 529: ProviderFailureDetail.HTTP_OVERLOADED, + }.get( + status_code, + ( + ProviderFailureDetail.HTTP_SERVER_ERROR + if 500 <= status_code <= 599 + else ProviderFailureDetail.HTTP_NON_SUCCESS + ), + ) + + def _validate_http_class(status_code: int, error_class: str, failure_phase: str) -> None: """Reject attempt fields that contradict OCR's status-derived v1 classifier.""" @@ -97,8 +173,8 @@ def _validate_http_class(status_code: int, error_class: str, failure_phase: str) raise RetryReportError("retry report HTTP status contradicts its error class") -def _attempt_reason(attempt: Mapping[str, Any]) -> ProviderFailureReason | None: - """Validate one attempt and return its closed error reason, if any.""" +def _attempt_failure(attempt: Mapping[str, Any]) -> _AttemptFailure | None: + """Validate one attempt and return its closed error projection, if any.""" outcome = attempt.get("outcome") if outcome not in ATTEMPT_OUTCOMES: @@ -117,13 +193,18 @@ def _attempt_reason(attempt: Mapping[str, Any]) -> ProviderFailureReason | None: if status_value and not 200 <= status_value <= 299: _validate_http_class(status_value, error_class, failure_phase) - return _http_reason(status_value) + return _AttemptFailure(_http_reason(status_value), _http_detail(status_value), status_value) if status_value: if failure_phase not in {"response_decode", "response_status", "stream"}: raise RetryReportError("retry report successful HTTP status has invalid failure phase") if error_class not in {"network", "provider", "unknown"}: raise RetryReportError("retry report successful HTTP status has invalid error class") - return ProviderFailureReason.INVALID_RESPONSE + detail = { + "response_decode": ProviderFailureDetail.RESPONSE_DECODE, + "response_status": ProviderFailureDetail.RESPONSE_STATUS, + "stream": ProviderFailureDetail.STREAM, + }[failure_phase] + return _AttemptFailure(ProviderFailureReason.INVALID_RESPONSE, detail, status_value) if failure_phase == "http" or error_class in { "authentication", "overloaded", @@ -132,18 +213,25 @@ def _attempt_reason(attempt: Mapping[str, Any]) -> ProviderFailureReason | None: }: raise RetryReportError("retry report transport failure has HTTP-only classification") if error_class == "cancelled": - return ProviderFailureReason.CANCELLED + return _AttemptFailure( + ProviderFailureReason.CANCELLED, ProviderFailureDetail.CANCELLED, None + ) if error_class == "timeout": - return ProviderFailureReason.TIMEOUT + return _AttemptFailure(ProviderFailureReason.TIMEOUT, ProviderFailureDetail.TIMEOUT, None) if error_class == "network": if failure_phase in {"response_decode", "response_status", "stream"}: - return ProviderFailureReason.INVALID_RESPONSE - return ProviderFailureReason.NETWORK - return ProviderFailureReason.UNKNOWN + detail = { + "response_decode": ProviderFailureDetail.RESPONSE_DECODE, + "response_status": ProviderFailureDetail.RESPONSE_STATUS, + "stream": ProviderFailureDetail.STREAM, + }[failure_phase] + return _AttemptFailure(ProviderFailureReason.INVALID_RESPONSE, detail, None) + return _AttemptFailure(ProviderFailureReason.NETWORK, ProviderFailureDetail.NETWORK, None) + return _AttemptFailure(ProviderFailureReason.UNKNOWN, ProviderFailureDetail.UNKNOWN, None) -def _request_reason(request: Mapping[str, Any]) -> ProviderFailureReason | None: - """Validate one logical request and classify only its terminal failure.""" +def _request_failure(request: Mapping[str, Any]) -> _AttemptFailure | None: + """Validate one logical request and project only its terminal failure.""" outcome = request.get("outcome") if outcome not in REQUEST_OUTCOMES: @@ -151,7 +239,7 @@ def _request_reason(request: Mapping[str, Any]) -> ProviderFailureReason | None: attempts = request.get("attempts") if not isinstance(attempts, list) or not 0 < len(attempts) <= MAX_ATTEMPTS_PER_REQUEST: raise RetryReportError("retry report request attempts are invalid") - reasons: list[ProviderFailureReason | None] = [] + failures: list[_AttemptFailure | None] = [] for index, attempt in enumerate(attempts, start=1): if ( not isinstance(attempt, dict) @@ -159,24 +247,26 @@ def _request_reason(request: Mapping[str, Any]) -> ProviderFailureReason | None: or attempt.get("attempt") != index ): raise RetryReportError("retry report attempt order is invalid") - reasons.append(_attempt_reason(attempt)) + failures.append(_attempt_failure(attempt)) if outcome == "succeeded": - if len(attempts) < 2 or any(reason is not None for reason in reasons): + if len(attempts) < 2 or any(failure is not None for failure in failures): raise RetryReportError("retry report succeeded request is inconsistent") return None if outcome == "recovered": - if reasons[-1] is not None or not any(reason is not None for reason in reasons[:-1]): + if failures[-1] is not None or not any(failure is not None for failure in failures[:-1]): raise RetryReportError("retry report recovered request is inconsistent") return None if outcome == "cancelled": - return ProviderFailureReason.CANCELLED - if reasons[-1] is None: + return _AttemptFailure( + ProviderFailureReason.CANCELLED, ProviderFailureDetail.CANCELLED, None + ) + if failures[-1] is None: raise RetryReportError("retry report failed request ends in success") - return reasons[-1] + return failures[-1] -def parse_retry_report_failure(result: object) -> ProviderFailureReason | None: - """Validate retry-report v1 and return one closed terminal failure reason.""" +def parse_retry_report_projection(result: object) -> ProviderFailureProjection | None: + """Validate retry-report v1 and return one closed numeric failure projection.""" if not isinstance(result, dict): raise RetryReportError("OCR result must be an object") @@ -222,26 +312,82 @@ def parse_retry_report_failure(result: object) -> ProviderFailureReason | None: if total_requests < len(requests): raise RetryReportError("OCR retry report total_requests is inconsistent") for field, expected in expected_counts.items(): - if _bounded_count(report.get(field), field, maximum=MAX_RETRY_REQUESTS * 100) != expected: + maximum = ( + MAX_RETRY_REQUESTS * (MAX_ATTEMPTS_PER_REQUEST - 1) + if field == "total_retries" + else MAX_RETRY_REQUESTS + ) + if _bounded_count(report.get(field), field, maximum=maximum) != expected: raise RetryReportError(f"OCR retry report {field} is inconsistent") - reasons: list[ProviderFailureReason] = [] + failures: list[_AttemptFailure] = [] for request in requests: if not isinstance(request, dict): raise RetryReportError("OCR retry report request is invalid") - reason = _request_reason(request) - if reason is not None: - reasons.append(reason) - if not reasons: + failure = _request_failure(request) + if failure is not None: + failures.append(failure) + if not failures: return None - counts = Counter(reasons) - return next(iter(counts)) if len(counts) == 1 else ProviderFailureReason.MIXED + reason_counts = Counter(failure.reason for failure in failures) + reason = next(iter(reason_counts)) if len(reason_counts) == 1 else ProviderFailureReason.MIXED + detail_counts = Counter(failure.detail for failure in failures) + statuses = {failure.status_code for failure in failures} + shared_status = next(iter(statuses)) if len(statuses) == 1 else None + return ProviderFailureProjection( + reason=reason, + details=tuple(sorted(detail_counts.items(), key=lambda item: item[0].value)), + status_code=shared_status, + failed_requests=expected_counts["failed_requests"], + cancelled_requests=expected_counts["cancelled_requests"], + retried_requests=expected_counts["retried_requests"], + total_retries=expected_counts["total_retries"], + recovered_requests=expected_counts["recovered_requests"], + ) -def provider_failure_reason(result: object) -> ProviderFailureReason | None: - """Return a closed reason, degrading malformed private diagnostics to unavailable.""" +def parse_retry_report_failure(result: object) -> ProviderFailureReason | None: + """Validate retry-report v1 and return one closed terminal failure reason.""" + + projection = parse_retry_report_projection(result) + return projection.reason if projection is not None else None + + +def render_provider_diagnostics(projection: ProviderFailureProjection) -> str: + """Render one bounded deterministic CI line from a validated projection.""" + + fields = [f"summary={projection.reason.value}"] + if len(projection.details) == 1: + fields.append(f"detail={projection.details[0][0].value}") + else: + details = ",".join(f"{detail.value}:{count}" for detail, count in projection.details) + fields.append(f"details={details}") + if projection.status_code is not None: + fields.append(f"status={projection.status_code}") + for name in ( + "failed_requests", + "cancelled_requests", + "retried_requests", + "total_retries", + "recovered_requests", + ): + value = getattr(projection, name) + if value: + fields.append(f"{name}={value}") + return "OCR provider diagnostics: " + " ".join(fields) + + +def provider_failure_projection(result: object) -> ProviderFailureProjection | None: + """Return closed diagnostics, degrading malformed private data to unavailable.""" try: - return parse_retry_report_failure(result) + return parse_retry_report_projection(result) except RetryReportError: return None + + +def provider_failure_reason(result: object) -> ProviderFailureReason | None: + """Return a closed reason, degrading malformed private diagnostics to unavailable.""" + + projection = provider_failure_projection(result) + return projection.reason if projection is not None else None diff --git a/src/ocr_toolkit/review_runner.py b/src/ocr_toolkit/review_runner.py index 9725582..8e116eb 100644 --- a/src/ocr_toolkit/review_runner.py +++ b/src/ocr_toolkit/review_runner.py @@ -99,7 +99,11 @@ PreExecutionStatusError, write_pre_execution_status, ) -from ocr_toolkit.provider_failure import ProviderFailureReason, provider_failure_reason +from ocr_toolkit.provider_failure import ( + ProviderFailureProjection, + provider_failure_projection, + render_provider_diagnostics, +) from ocr_toolkit.providers.gitlab import ( GitLabProviderError, acquire_review_snapshot, @@ -2081,14 +2085,16 @@ def read_stderr_excerpt(stderr_path: Path, max_chars: int = DEFAULT_DIAGNOSTIC_C return redact_sensitive(text)[:max_chars] -def _closed_provider_failure_reason(result_path: Path) -> ProviderFailureReason | None: - """Return only a validated retry-report reason from one private result artifact.""" +def _closed_provider_failure_projection( + result_path: Path, +) -> ProviderFailureProjection | None: + """Return only validated retry-report diagnostics from one private result.""" try: result = load_ocr_result(result_path) except (OcrResultMalformed, OcrResultMissing, OcrResultTooLarge): return None - return provider_failure_reason(result) + return provider_failure_projection(result) def _resolve_ocr_binary() -> str: @@ -2155,13 +2161,9 @@ def run_review( if completed.returncode != 0: print(f"Open Code Review exited with code {completed.returncode}.", file=sys.stderr) - provider_reason = _closed_provider_failure_reason(result_path) - if provider_reason is not None: - print( - f"OCR provider failure classified as {provider_reason.value}; " - "private diagnostics remain in the owner-only artifacts.", - file=sys.stderr, - ) + provider_projection = _closed_provider_failure_projection(result_path) + if provider_projection is not None: + print(render_provider_diagnostics(provider_projection), file=sys.stderr) else: excerpt = read_stderr_excerpt(stderr_path) if excerpt: diff --git a/tests/test_operations_docs.py b/tests/test_operations_docs.py index 3b614fb..b56badc 100644 --- a/tests/test_operations_docs.py +++ b/tests/test_operations_docs.py @@ -217,13 +217,17 @@ def test_completion_cap_and_provider_failure_boundaries_are_public() -> None: for phrase in ( "endpoint-or-model-not-found", "cost reservation from the requested output cap", - "not a claim that the cap was the cause", + "OCR provider diagnostics:", + "http-payment-required", + "http-rate-limited", + "not toolkit telemetry", + "Neither setting is presented as the proven cause", "`OCR_POST_ERROR_DETAILS=1` cannot add them", "the previous successful review is preserved", "automatic approval is not attempted", ): assert phrase in operations - assert "closed provider-neutral reason may cross the separate strict parser" in security + assert "one separate local line may contain closed protocol detail" in security assert "receipt, DLP, telemetry, severity, finding, or approval signal" in security diff --git a/tests/test_posting_helpers.py b/tests/test_posting_helpers.py index e8a4d2d..7d45723 100644 --- a/tests/test_posting_helpers.py +++ b/tests/test_posting_helpers.py @@ -1697,6 +1697,13 @@ def capture( self.assertIn("rate-or-spending-limit", published) self.assertIn("Previous OCR review comments were preserved", published) self.assertIn("Automatic approval was not attempted", published) + self.assertIn( + "Try lowering `OCR_REVIEW_CONCURRENCY` and/or " + "`OCR_LLM_MAX_COMPLETION_TOKENS`, then start a new merge request pipeline.", + published, + ) + self.assertNotIn("OCR provider diagnostics:", published) + self.assertNotIn("OCR provider diagnostics:", controlled_log) for private in private_values: self.assertNotIn(private, published) self.assertNotIn(private, controlled_log) @@ -2349,9 +2356,9 @@ def capture( self.assertEqual(exit_code, expected) self.assertIn("rate-or-spending-limit", notes[0]) - self.assertIn("cost reservation", notes[0]) + self.assertIn("OCR_REVIEW_CONCURRENCY", notes[0]) + self.assertIn("start a new merge request pipeline", notes[0]) self.assertIn("OCR_LLM_MAX_COMPLETION_TOKENS", notes[0]) - self.assertIn("4096", notes[0]) def test_previous_review_cleanup_depends_only_on_coverage_completeness(self) -> None: """Clean prior review state only after complete replacement coverage.""" diff --git a/tests/test_provider_failure.py b/tests/test_provider_failure.py index 930e51f..31d3a57 100644 --- a/tests/test_provider_failure.py +++ b/tests/test_provider_failure.py @@ -8,10 +8,14 @@ from ocr_toolkit.provider_failure import ( MAX_ATTEMPTS_PER_REQUEST, + ProviderFailureDetail, ProviderFailureReason, RetryReportError, parse_retry_report_failure, + parse_retry_report_projection, + provider_failure_projection, provider_failure_reason, + render_provider_diagnostics, ) @@ -70,26 +74,31 @@ def _result(requests: list[dict[str, Any]], *, total_requests: int | None = None @pytest.mark.parametrize( - ("status", "error_class", "expected"), + ("status", "error_class", "expected", "detail"), [ - (400, "provider", ProviderFailureReason.REQUEST_REJECTED), - (401, "authentication", ProviderFailureReason.AUTHENTICATION), - (402, "provider", ProviderFailureReason.RATE_OR_SPENDING_LIMIT), - (403, "authentication", ProviderFailureReason.AUTHORIZATION), - (404, "provider", ProviderFailureReason.ENDPOINT_OR_MODEL_NOT_FOUND), - (408, "timeout", ProviderFailureReason.TIMEOUT), - (409, "provider", ProviderFailureReason.REQUEST_REJECTED), - (413, "provider", ProviderFailureReason.REQUEST_REJECTED), - (422, "provider", ProviderFailureReason.REQUEST_REJECTED), - (429, "rate_limited", ProviderFailureReason.RATE_OR_SPENDING_LIMIT), - (500, "provider", ProviderFailureReason.PROVIDER_UNAVAILABLE), - (503, "provider", ProviderFailureReason.PROVIDER_UNAVAILABLE), - (504, "timeout", ProviderFailureReason.TIMEOUT), - (529, "overloaded", ProviderFailureReason.OVERLOADED), + (400, "provider", ProviderFailureReason.REQUEST_REJECTED, "http-bad-request"), + (401, "authentication", ProviderFailureReason.AUTHENTICATION, "http-unauthorized"), + (402, "provider", ProviderFailureReason.RATE_OR_SPENDING_LIMIT, "http-payment-required"), + (403, "authentication", ProviderFailureReason.AUTHORIZATION, "http-forbidden"), + (404, "provider", ProviderFailureReason.ENDPOINT_OR_MODEL_NOT_FOUND, "http-not-found"), + (408, "timeout", ProviderFailureReason.TIMEOUT, "http-request-timeout"), + (409, "provider", ProviderFailureReason.REQUEST_REJECTED, "http-conflict"), + (413, "provider", ProviderFailureReason.REQUEST_REJECTED, "http-content-too-large"), + ( + 422, + "provider", + ProviderFailureReason.REQUEST_REJECTED, + "http-unprocessable-content", + ), + (429, "rate_limited", ProviderFailureReason.RATE_OR_SPENDING_LIMIT, "http-rate-limited"), + (500, "provider", ProviderFailureReason.PROVIDER_UNAVAILABLE, "http-server-error"), + (503, "provider", ProviderFailureReason.PROVIDER_UNAVAILABLE, "http-server-error"), + (504, "timeout", ProviderFailureReason.TIMEOUT, "http-gateway-timeout"), + (529, "overloaded", ProviderFailureReason.OVERLOADED, "http-overloaded"), ], ) def test_http_status_matrix_maps_only_validated_attempt_fields( - status: int, error_class: str, expected: ProviderFailureReason + status: int, error_class: str, expected: ProviderFailureReason, detail: str ) -> None: """Map the complete required HTTP matrix without reading provider text.""" @@ -97,7 +106,11 @@ def test_http_status_matrix_maps_only_validated_attempt_fields( [_request("failed", [_error_attempt(status=status, error_class=error_class, phase="http")])] ) - assert parse_retry_report_failure(result) is expected + projection = parse_retry_report_projection(result) + + assert projection is not None + assert projection.reason is expected + assert projection.details[0][0].value == detail @pytest.mark.parametrize( @@ -228,3 +241,121 @@ def test_raw_provider_identity_and_payload_fields_do_not_affect_reason() -> None assert reason is ProviderFailureReason.ENDPOINT_OR_MODEL_NOT_FOUND assert "private" not in reason.value + + +@pytest.mark.parametrize( + ("status", "error_class", "detail"), + [ + (402, "provider", ProviderFailureDetail.HTTP_PAYMENT_REQUIRED), + (429, "rate_limited", ProviderFailureDetail.HTTP_RATE_LIMITED), + ], +) +def test_payment_and_rate_statuses_keep_one_public_reason_with_distinct_detail( + status: int, + error_class: str, + detail: ProviderFailureDetail, +) -> None: + """Distinguish protocol status without claiming a provider business cause.""" + + result = _result( + [_request("failed", [_error_attempt(status=status, error_class=error_class, phase="http")])] + ) + + projection = parse_retry_report_projection(result) + + assert projection is not None + assert projection.reason is ProviderFailureReason.RATE_OR_SPENDING_LIMIT + assert projection.details == ((detail, 1),) + assert projection.status_code == status + assert render_provider_diagnostics(projection) == ( + "OCR provider diagnostics: summary=rate-or-spending-limit " + f"detail={detail.value} status={status} failed_requests=1" + ) + + +def test_retries_and_recovered_requests_are_aggregated_without_zero_fields() -> None: + """Expose only useful validated counts and one terminal status.""" + + failed = _request( + "failed", + [ + _error_attempt(status=429, error_class="rate_limited", phase="http", number=number) + for number in (1, 2, 3) + ], + ) + recovered = _request( + "recovered", + [ + _error_attempt(status=429, error_class="rate_limited", phase="http"), + _success_attempt(2), + ], + ) + + projection = parse_retry_report_projection(_result([failed, recovered])) + + assert projection is not None + assert render_provider_diagnostics(projection) == ( + "OCR provider diagnostics: summary=rate-or-spending-limit " + "detail=http-rate-limited status=429 failed_requests=1 retried_requests=2 " + "total_retries=3 recovered_requests=1" + ) + assert "cancelled_requests=" not in render_provider_diagnostics(projection) + + +def test_mixed_details_are_counted_and_sorted_without_nonuniform_status() -> None: + """Collapse heterogeneous terminal failures into one deterministic line.""" + + requests = [ + _request( + "failed", + [_error_attempt(status=429, error_class="rate_limited", phase="http")], + ), + _request( + "failed", + [_error_attempt(status=402, error_class="provider", phase="http")], + ), + _request( + "failed", + [_error_attempt(status=0, error_class="network", phase="transport")], + ), + ] + + projection = parse_retry_report_projection(_result(requests)) + + assert projection is not None + line = render_provider_diagnostics(projection) + assert line == ( + "OCR provider diagnostics: summary=mixed " + "details=http-payment-required:1,http-rate-limited:1,network:1 " + "failed_requests=3" + ) + assert "status=" not in line + + +def test_malformed_projection_and_private_extensions_never_reach_renderer() -> None: + """Keep malformed reports and provider-controlled additive values unavailable.""" + + result = _result( + [ + _request( + "failed", + [_error_attempt(status=429, error_class="rate_limited", phase="http")], + ) + ] + ) + projection = provider_failure_projection(result) + + assert projection is not None + line = render_provider_diagnostics(projection) + for private in ( + "private-provider", + "private-model", + "private-request-id", + "private-token", + "/private/repository/file.py", + "private-response-body", + ): + assert private not in line + + result["retry_report"]["failed_requests"] = 2 + assert provider_failure_projection(result) is None diff --git a/tests/test_review_runner.py b/tests/test_review_runner.py index 1ba2141..4cd5d06 100644 --- a/tests/test_review_runner.py +++ b/tests/test_review_runner.py @@ -2160,9 +2160,14 @@ def fake_run(argv: list[str], **kwargs: object) -> subprocess.CompletedProcess[b assert stderr_path.read_text(encoding="utf-8") == "private provider body and token\n" assert exit_code == 1 - assert "rate-or-spending-limit" in output.getvalue() - assert "private provider body" not in output.getvalue() - assert "private-provider" not in output.getvalue() + controlled_output = output.getvalue() + assert controlled_output.count("OCR provider diagnostics:") == 1 + assert ( + "OCR provider diagnostics: summary=rate-or-spending-limit " + "detail=http-rate-limited status=429 failed_requests=1" + ) in controlled_output + assert "private provider body" not in controlled_output + assert "private-provider" not in controlled_output @pytest.mark.skipif(os.name == "nt", reason="synthetic executable contract is POSIX-only") From ec87cbb77775bb9293dfdde8bef30b0306f39811 Mon Sep 17 00:00:00 2001 From: xeonvs <11463419+xeonvs@users.noreply.github.com> Date: Thu, 27 Aug 2026 11:14:27 +0200 Subject: [PATCH 4/7] Record Draft handoff state --- PLANS.md | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/PLANS.md b/PLANS.md index 816150e..89ef945 100644 --- a/PLANS.md +++ b/PLANS.md @@ -6,7 +6,7 @@ Use this file for active or blocked repository work. Update it before implementa ### Toolkit 0.8.5 - provider diagnostics, OCR 1.10.2, and compatibility scheduling -Status: active +Status: implementation complete; stable release deferred #### Goal @@ -142,8 +142,8 @@ state for this task: `release-deferred` at the green Draft PR boundary. | `WQ-02` | `completed` | Milestone `v0.8.5` owns assigned open issues #149/#151; hosted run 33055459209 qualified exact OCR 1.10.2. | | `WQ-03` | `completed` | OCR 1.10.2 pins/evidence, schedule, classifier correction, current docs/Rules, source audit, backlog reconciliation, and local no-LLM update are complete. | | `WQ-04` | `completed` | One closed provider-neutral projection renders the single numeric review log line; GitLab keeps only the prior reason with safer remediation. | -| `WQ-05` | `in_progress` | Finalize changelog/docs/plan truth and run the complete local gate once. | -| `WQ-06` | `pending` | Push the complete history once, reconcile hosted CI, update Draft/issue coordination with exact evidence, and verify final state. | +| `WQ-05` | `completed` | Changelog/docs are reconciled; the complete local quality, coverage, manifest, lock, Towncrier, Gitleaks, and diff gates pass. | +| `WQ-06` | `in_progress` | Push the complete history once, reconcile hosted CI, update Draft/issue coordination with exact evidence, and verify final state. | #### Locked Decisions @@ -207,6 +207,17 @@ state for this task: `release-deferred` at the green Draft PR boundary. tests plus 217 subtests pass. The reviewed flow is `private OCR result -> strict retry parser -> closed immutable reason/detail/status/counts -> one local line`; GitLab uses only the reason, and DLP/receipt/telemetry/approval projections are unchanged. +- 2026-08-27: `scripts/quality.sh check` passes 1,296 tests and 337 subtests with + branch-aware total coverage 86.51%. Risk groups pass at 85%, 82%, 86%, and 87% + against required 80%, 80%, 85%, and 85% floors. +- 2026-08-27: locked dependency resolution, OCR manifest validation, rendered Towncrier + 0.8.5 draft, and `git diff --check` pass. Checksum-verified temporary Gitleaks 8.24.3 + passes the complete first-parent feature history; the installed unrelated 8.30.1 was + not substituted for the repository pin. +- 2026-08-27: requirements, provider-data-flow, privacy, DLP/approval independence, + compatibility, telemetry ownership, backlog, documentation, and complete feature diff + self-reviews are complete. Exact temporary OCR/Gitleaks artifacts and the recoverable + OCR 1.10.1 backup were removed after successful validation; local OCR remains 1.10.2. #### Risks And Recovery @@ -230,9 +241,13 @@ state for this task: `release-deferred` at the green Draft PR boundary. #### Resume Point -Finish `WQ-05`: self-review and commit the provider-diagnostic slice, then update final -Draft-only plan truth and run the one complete local quality/coverage/manifest/lock/ -Towncrier/Gitleaks/diff gate. Do not push until that gate and final handoff commit pass. +Finish `WQ-06`: commit this Draft-only handoff truth, push the locally reviewed history +with lease protection because the two implementation commits were autosquashed and +re-signed after gate corrections, wait for hosted PR workflows, and fix only evidenced +failures through the same self-review/commit gate. When green, update Draft PR #150 and +open issues #149/#151 with exact head/tree, hashes, validation, Added/Fixed/Changed/ +Unchanged summaries, and the stable-release resume action. Leave the PR Draft and both +issues plus milestone `v0.8.5` open; do not merge, tag, publish, or start a release PR. #### Plan Fidelity Check From fca7699491211d430b5ad15a2ccfc8421b6e1b86 Mon Sep 17 00:00:00 2001 From: xeonvs <11463419+xeonvs@users.noreply.github.com> Date: Thu, 27 Aug 2026 11:36:51 +0200 Subject: [PATCH 5/7] Resume stable v0.8.5 delivery --- PLANS.md | 90 ++++++++++++++++++++++++++++++++++---------------------- 1 file changed, 55 insertions(+), 35 deletions(-) diff --git a/PLANS.md b/PLANS.md index 89ef945..2bb85c7 100644 --- a/PLANS.md +++ b/PLANS.md @@ -6,14 +6,15 @@ Use this file for active or blocked repository work. Update it before implementa ### Toolkit 0.8.5 - provider diagnostics, OCR 1.10.2, and compatibility scheduling -Status: implementation complete; stable release deferred +Status: release execution in progress #### Goal -Deliver a production-ready Draft feature pull request for toolkit 0.8.5 that implements +Deliver and publish stable toolkit 0.8.5 with verified artifacts. The release implements issue #149's bounded provider diagnostics, qualifies and adopts OCR 1.10.2, moves the daily compatibility discovery after the observed upstream release window, reconciles -upstream capabilities with the durable backlog, and leaves stable publication deferred. +upstream capabilities with the durable backlog, and completes independent external +readback and issue/milestone closure. #### Plan Origin @@ -34,11 +35,15 @@ upstream capabilities with the durable backlog, and leaves stable publication de `ocr.llm-retry-report/v1`, without changing the merge-request summary structure. - Reconcile every relevant OCR 1.10.2 capability and #149 outcome against backlog goals, activation triggers, dependencies, and acceptance criteria. -- Finish at a green Draft feature PR. Do not merge, prepare a release PR, tag, or publish - toolkit 0.8.5. +- Update the PATH-effective local OCR to exact 1.10.2, run one real final review of the + complete feature range with the configured provider at concurrency `2`, retain its + private artifacts only through finding analysis, and remediate confirmed findings. +- Complete holistic self-review and deterministic gates, merge the protected feature PR, + verify the protected-main development publication, prepare and merge `release/v0.8.5`, + publish stable artifacts, and independently reconcile every release surface. Release classification: `release-required`; target stable version: `0.8.5`; delivery -state for this task: `release-deferred` at the green Draft PR boundary. +state for this task: full stable delivery authorized on 2026-08-27. #### Requirement Traceability @@ -53,7 +58,10 @@ state for this task: `release-deferred` at the green Draft PR boundary. | `REQ-007` | Produce at most one closed numeric provider diagnostic line from a strictly validated retry report. | `WQ-04` | Parser, renderer, bounds, aggregation, and hostile-input tests | | `REQ-008` | Preserve GitLab summary shape, prior review, DLP, receipt, telemetry, and approval boundaries while improving remediation text. | `WQ-04` | Posting/review/approval/privacy regressions | | `REQ-009` | Document current behavior and classify dependency, Rules, feature, and scheduling changes accurately for humans and release agents. | `WQ-03`, `WQ-05` | Towncrier draft and documentation contract tests | -| `REQ-010` | Complete one local final gate, one final push, hosted CI reconciliation, and exact Draft handoff state. | `WQ-05`, `WQ-06` | Quality/coverage/Gitleaks/checks, PR/head/tree/status readback | +| `REQ-010` | Run one complete real OCR 1.10.2 review over the final feature range, remediate confirmed findings, and finish deterministic validation and self-review. | `WQ-05`, `WQ-06` | Private complete OCR result, finding trace, quality/coverage/Gitleaks/checks | +| `REQ-011` | Merge the exact protected feature PR after required checks and thread reconciliation, then verify the deterministic TestPyPI development build. | `WQ-07` | PR/merge/tree/rules/checks and registry/provenance readback | +| `REQ-012` | Prepare and merge the final protected `Release v0.8.5` PR with release metadata, changelog, notes, plan archival, and next-version state. | `WQ-08` | Exact release head/tree/checks/threads/authorization metadata | +| `REQ-013` | Publish and independently verify stable TestPyPI/PyPI bytes, provenance/attestations, supported-Python installs, annotated tag, immutable GitHub Release/receipt, issue receipts, and milestone closure. | `WQ-09` | Live registry, GitHub, workflow, install, issue, milestone, and clean-main readback | #### Explicit Non-Goals @@ -67,8 +75,8 @@ state for this task: `release-deferred` at the green Draft PR boundary. response text, headers, URLs, request IDs, paths, warnings, or stderr. - No toolkit consumption of OCR's GitHub Action checkpoint ranges or `ocr session compare`; resemblance alone does not activate or close backlog work. -- No mechanical test-directory reorganization, production refactor solely for coverage, - stable release, issue closure, milestone closure, merge, tag, or registry publication. +- No mechanical test-directory reorganization or production refactor solely for coverage. +- No B2B, `core/common`, shared-template, or consumer-repository integration. #### Constraints @@ -93,8 +101,8 @@ state for this task: `release-deferred` at the green Draft PR boundary. - Before each signed logical commit: focused tests, complete slice diff review, trust/data-flow/privacy review, requirement/backlog reconciliation, and `git diff --check`. -- After the initial Draft push, do not push again until local implementation and the final - gate are complete. +- Do not run a second repository-diff OCR review after the authorized final run. Confirmed + findings are repaired and covered by deterministic tests plus holistic self-review. #### Inputs And Sources @@ -115,11 +123,12 @@ state for this task: `release-deferred` at the green Draft PR boundary. #### User Decisions And Answers -- Delivery ends at a green Draft PR; stable toolkit 0.8.5 publication is deferred. +- The 2026-08-27 owner instruction supersedes the Draft-only boundary and authorizes + feature merge, release preparation/merge, stable publication, and external closure. - Use one daily `07:15 UTC` compatibility check, not two checks. - Update any backlog item that toolkit or OCR actually closes; update partial overlap truthfully and keep unmet work open. -- Update the local OCR binary, but skip local LLM execution. +- Update the PATH-effective local OCR binary and run the required real local OCR review. - Work efficiently in complete logical commits with self-review before each commit. #### Completed Baseline State @@ -143,7 +152,10 @@ state for this task: `release-deferred` at the green Draft PR boundary. | `WQ-03` | `completed` | OCR 1.10.2 pins/evidence, schedule, classifier correction, current docs/Rules, source audit, backlog reconciliation, and local no-LLM update are complete. | | `WQ-04` | `completed` | One closed provider-neutral projection renders the single numeric review log line; GitLab keeps only the prior reason with safer remediation. | | `WQ-05` | `completed` | Changelog/docs are reconciled; the complete local quality, coverage, manifest, lock, Towncrier, Gitleaks, and diff gates pass. | -| `WQ-06` | `in_progress` | Push the complete history once, reconcile hosted CI, update Draft/issue coordination with exact evidence, and verify final state. | +| `WQ-06` | `in_progress` | Correct the PATH-effective OCR installation, run the single final real OCR review, remediate confirmed findings, and complete self-review/final feature gates. | +| `WQ-07` | `pending` | Reconcile exact feature head/checks/threads, mark PR ready, merge it, synchronize `main`, and verify its TestPyPI development publication. | +| `WQ-08` | `pending` | Prepare the signed `release/v0.8.5` repository state and merge its exact protected release PR after all gates. | +| `WQ-09` | `pending` | Monitor stable publication and independently read back bytes, provenance, attestations, installs, tag, immutable Release/receipt, issue receipts, milestone, main, and cleanup. | #### Locked Decisions @@ -179,7 +191,10 @@ state for this task: `release-deferred` at the green Draft PR boundary. notes, receipts, DLP signals, or approval inputs. - Lifecycle regressions prove previous review preservation, no failed-result findings, no approval, unchanged public classification, and strict/non-strict posting behavior. -- Final local gate once: `scripts/quality.sh check`, all coverage floors, +- One complete local OCR 1.10.2 review of `origin/main..HEAD` at concurrency `2`; inspect + its private result/manifest completeness and trace every candidate before accepting or + rejecting it. Do not post the result. +- Final local gate after remediation: `scripts/quality.sh check`, all coverage floors, `scripts/ocr_compat.py validate`, lock check, Towncrier draft, `scripts/gitleaks.sh`, and `git diff --check`. Hosted PR workflows own package/OS/Python/Security/CodeQL gates. @@ -218,6 +233,14 @@ state for this task: `release-deferred` at the green Draft PR boundary. compatibility, telemetry ownership, backlog, documentation, and complete feature diff self-reviews are complete. Exact temporary OCR/Gitleaks artifacts and the recoverable OCR 1.10.1 backup were removed after successful validation; local OCR remains 1.10.2. +- 2026-08-27 release-resume audit found that the PATH-effective executable still reports + 1.10.1, contradicting the earlier PATH-effective 1.10.2 claim. Release work therefore + reopens local OCR installation and the required real review gate before feature merge. +- 2026-08-27: the shadow executable was atomically replaced. `command -v ocr`, ordinary + `ocr --version`, and review help now resolve and report 1.10.2, and SHA-256 is the + official Darwin arm64 + `74fc7bcc0e6d0790c5ca033fd82a5474b6f05d443ed51a26a6f61c0cac6589fd`; the temporary + download and rollback copy were removed after verification. #### Risks And Recovery @@ -241,19 +264,16 @@ state for this task: `release-deferred` at the green Draft PR boundary. #### Resume Point -Finish `WQ-06`: commit this Draft-only handoff truth, push the locally reviewed history -with lease protection because the two implementation commits were autosquashed and -re-signed after gate corrections, wait for hosted PR workflows, and fix only evidenced -failures through the same self-review/commit gate. When green, update Draft PR #150 and -open issues #149/#151 with exact head/tree, hashes, validation, Added/Fixed/Changed/ -Unchanged summaries, and the stable-release resume action. Leave the PR Draft and both -issues plus milestone `v0.8.5` open; do not merge, tag, publish, or start a release PR. +Finish `WQ-06` from the exact Draft head: install and verify PATH-effective OCR 1.10.2, +run the single complete local review, remediate confirmed findings, perform holistic +self-review, and push the resulting signed feature state. Continue through `WQ-07` to +`WQ-09` without redefining stable delivery as complete before independent readback. #### Plan Fidelity Check - [x] Every approved outcome has a stable requirement and queue owner. -- [x] Release classification, target, Draft-only boundary, and deferred stable delivery - are explicit. +- [x] Release classification, target, superseded Draft-only boundary, and stable delivery + authorization are explicit. - [x] Inputs, exact hashes, schedule evidence, public/private data flow, backlog rules, non-goals, validation, recovery, and resume state are retained. - [x] DLP, receipt, telemetry, approval, GitLab summary, local OCR, and no-LLM boundaries @@ -278,23 +298,23 @@ issues plus milestone `v0.8.5` open; do not merge, tag, publish, or start a rele - [ ] Final quality, coverage, OCR validation, lock, Towncrier, Gitleaks, and diff gates are green on the exact final tree. - [ ] Hosted required checks are green and the Draft PR has no unresolved conversations. -- [ ] #149 and the OCR issue remain open in milestone `v0.8.5`; milestone remains open. -- [ ] Draft remains unmerged; no release PR, tag, registry publication, or stable closure - occurred. +- [ ] Exact feature and release PR heads passed required checks with no unresolved threads + and their merge trees match reviewed trees. +- [ ] Stable external bytes, provenance, attestations, installs, tag, immutable Release, + receipt, issue receipts/closure, milestone closure, synchronized main, and cleanup are + independently verified. #### Post-Close Delivery -- This task ends with a green Draft feature PR and an explicit `release-deferred` state. -- Merge, protected-main TestPyPI publication, `release/v0.8.5`, stable registries, tag, - immutable GitHub Release, issue receipts/closure, milestone closure, and independent - external reconciliation require a later owner instruction. -- Hosted package, OS/Python, Dependency Review, Security, and CodeQL jobs are required PR - evidence but are not duplicated locally. +- This task ends only after stable v0.8.5 publication and independent external + reconciliation. Hosted package, OS/Python, Dependency Review, Security, and CodeQL jobs + remain required PR evidence; macOS jobs are advisory under the documented policy. #### Handoff Notes - Final Draft body must identify toolkit target 0.8.5, OCR target 1.10.2, exact head/tree, exact OCR hashes and qualification run/issue, schedule rationale, Added/Fixed/Changed/ Unchanged behavior, backlog disposition, local no-LLM checks, and hosted validation. -- The next agent starts from the exact Draft head; it does not rerun completed local - development or publish stable 0.8.5 without explicit authorization. +- Release closure must retain the exact feature/release heads, merge/tree identities, + workflow run/attempt, artifact hashes, registry provenance, receipt hash, issue receipts, + and final cleanup evidence. From 278e678b73fd41cf4eb40a7852669e54547c73dd Mon Sep 17 00:00:00 2001 From: xeonvs <11463419+xeonvs@users.noreply.github.com> Date: Thu, 27 Aug 2026 11:45:08 +0200 Subject: [PATCH 6/7] Reconcile v0.8.5 release boundaries --- PLANS.md | 9 ++++++++- docs/compatibility.md | 4 ++-- docs/configuration.md | 2 +- docs/engineering/execution_history/releases.md | 2 +- docs/gitlab.md | 2 +- docs/operations.md | 2 +- tests/test_operations_docs.py | 4 +++- 7 files changed, 17 insertions(+), 8 deletions(-) diff --git a/PLANS.md b/PLANS.md index 2bb85c7..20a01c3 100644 --- a/PLANS.md +++ b/PLANS.md @@ -152,7 +152,7 @@ state for this task: full stable delivery authorized on 2026-08-27. | `WQ-03` | `completed` | OCR 1.10.2 pins/evidence, schedule, classifier correction, current docs/Rules, source audit, backlog reconciliation, and local no-LLM update are complete. | | `WQ-04` | `completed` | One closed provider-neutral projection renders the single numeric review log line; GitLab keeps only the prior reason with safer remediation. | | `WQ-05` | `completed` | Changelog/docs are reconciled; the complete local quality, coverage, manifest, lock, Towncrier, Gitleaks, and diff gates pass. | -| `WQ-06` | `in_progress` | Correct the PATH-effective OCR installation, run the single final real OCR review, remediate confirmed findings, and complete self-review/final feature gates. | +| `WQ-06` | `in_progress` | Correct the PATH-effective OCR installation, complete the required real OCR review, remediate confirmed findings, and complete self-review/final feature gates. | | `WQ-07` | `pending` | Reconcile exact feature head/checks/threads, mark PR ready, merge it, synchronize `main`, and verify its TestPyPI development publication. | | `WQ-08` | `pending` | Prepare the signed `release/v0.8.5` repository state and merge its exact protected release PR after all gates. | | `WQ-09` | `pending` | Monitor stable publication and independently read back bytes, provenance, attestations, installs, tag, immutable Release/receipt, issue receipts, milestone, main, and cleanup. | @@ -241,6 +241,13 @@ state for this task: full stable delivery authorized on 2026-08-27. official Darwin arm64 `74fc7bcc0e6d0790c5ca033fd82a5474b6f05d443ed51a26a6f61c0cac6589fd`; the temporary download and rollback copy were removed after verification. +- 2026-08-27: the first real exact-range OCR 1.10.2 invocation selected nine supported + files at concurrency `2` but failed before generation: all 13 logical requests received + HTTP 429 through 65 retries, token usage and findings stayed zero, and the complete + structured result was retained owner-only for audit. This proves the current binary and + review path executed but does not qualify semantic review or toolkit correctness. No + second configured backend exists; do not describe this outcome as a passing OCR review + or waive the required completed review. #### Risks And Recovery diff --git a/docs/compatibility.md b/docs/compatibility.md index d8a634d..66a0eef 100644 --- a/docs/compatibility.md +++ b/docs/compatibility.md @@ -43,7 +43,7 @@ OCR 1.10.0 was the only version accepted by toolkit 0.8.2 and 0.8.3 preflight an Group labels are model-produced and group keys contain sorted changed paths. Additive group/round result fields remain untrusted private diagnostics: the toolkit sanitizes them when needed but excludes them from findings, GitLab text, receipt v5, fingerprints, severity, lifecycle commands, toolkit telemetry, and approval. OCR's own opt-in telemetry can include these repository-derived high-cardinality values; see the [signal-ownership audit](engineering/review_signal_ownership.md). -OCR 1.10.0 also adds `--output`; `ocr-ci review` rejects `--output`, `--output=...`, `-o`, and attached short forms before preview because the toolkit retains sole ownership of the private result descriptor and atomic validation/cleanup. Improved upstream git errors remain private OCR stderr and do not widen GitLab diagnostics. With no toolkit completion-cap override, the qualified OpenAI wire request now uses `max_completion_tokens=16384`, down from 58,888 in OCR 1.9.10. `OCR_LLM_MAX_COMPLETION_TOKENS=4096` remains an explicit operator workaround for gateways that reserve spending against the requested output cap. +OCR 1.10.0 also adds `--output`; `ocr-ci review` rejects `--output`, `--output=...`, `-o`, and attached short forms before preview because the toolkit retains sole ownership of the private result descriptor and atomic validation/cleanup. Improved upstream git errors remain private OCR stderr and do not widen GitLab diagnostics. With no toolkit completion-cap override, the qualified OpenAI wire request now uses `max_completion_tokens=16384`, down from 58,888 in OCR 1.9.10. Qualification also proved that an explicit positive override reaches the wire; operators must select its value from their provider/model contract rather than a toolkit recommendation. Its max-tools CLI reports a minimum and normalization target of `50`, while the embedded review template owns `100` rounds and the CLI only raises that value. Exact-binary behavioral probes therefore observe effective `100` for omitted, sentinel `0`, `49`, and `50`, and effective `101` for explicit `101`; `--max-tools 49` still prints the `50` normalization target. Toolkit preview accepts only that exact diagnostic shape and emits a closed operator notice. It does not copy raw stderr into the OCR result or derive finding, receipt, DLP, telemetry, lifecycle, or approval authority from the notice. The public example now passes sentinel `0` instead of a stale release-specific positive value. @@ -65,7 +65,7 @@ Deploy toolkit 0.8.4 directly with OCR 1.10.1. The public Linux amd64 example ve OCR 1.10.2 is the only version accepted by the toolkit 0.8.5 development preflight and pinned by the current GitLab example. Hosted run `33055459209`, canonical issue #151, adjacent source review, and checksum-verified isolated Darwin checks preserve the OCR result, `ocr.run-manifest/v1`, default-medium two-round review, effective max-tools, completion-cap override, DLP, receipt v5, telemetry ownership, and approval contracts. -OCR now records grouping tasks and grace-round requests in its private session/retry state. The grouping request uses the review template's inherited OpenAI completion cap `16384` instead of the separate `4096` used by the predecessor; `OCR_LLM_MAX_COMPLETION_TOKENS=4096` still overlays every protocol request when an operator needs a fixed gateway cap. These private task records and group/path values do not enter toolkit findings, GitLab text, receipts, DLP signals, telemetry, or approval inputs. +OCR now records grouping tasks and grace-round requests in its private session/retry state. The grouping request uses the review template's inherited OpenAI completion cap `16384` instead of the separate `4096` used by the predecessor; an explicit positive `OCR_LLM_MAX_COMPLETION_TOKENS` still overlays every protocol request. Its value remains deployment-owned. These private task records and group/path values do not enter toolkit findings, GitLab text, receipts, DLP signals, telemetry, or approval inputs. Solidity (`.sol`) and Vyper (`.vy`) join the effective built-in Rules and reviewable-file contract. The upstream session-comparison command is not invoked by the toolkit. Upstream reusable GitHub Action checkpoint ranges cover only that Action's range selection; the GitLab toolkit keeps its own immutable range/snapshot and publication lifecycle, so the change only partially overlaps BL-021 and does not activate or complete a second forge adapter. diff --git a/docs/configuration.md b/docs/configuration.md index 3352d43..5112842 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -56,7 +56,7 @@ Since 0.8.0, `OCR_USE_ANTHROPIC` is not a compatibility alias. Any presence fail | `openai-responses` | `max_output_tokens` | | `anthropic` | `max_tokens` | -If `OCR_LLM_EXTRA_BODY` already owns that field, an exactly equal JSON integer is deduplicated. A different value, or a boolean, string, float, or null at that field, fails configuration with a migration error; remove the duplicate field or keep the same integer in both places. Other `OCR_LLM_EXTRA_BODY` members are preserved. For example, set `OCR_LLM_MAX_COMPLETION_TOKENS=4096` when a gateway accepts short probes but rejects a full review before generation because it reserves spending against the requested output cap. +If `OCR_LLM_EXTRA_BODY` already owns that field, an exactly equal JSON integer is deduplicated. A different value, or a boolean, string, float, or null at that field, fails configuration with a migration error; remove the duplicate field or keep the same integer in both places. Other `OCR_LLM_EXTRA_BODY` members are preserved. Select an explicit completion cap only from the deployment's provider/model contract when a gateway reserves spending against the requested output cap; the toolkit does not recommend a provider-specific value. The toolkit does not derive this value from `/models.max_completion_tokens`. That metadata is a model capability boundary, not an account spending limit or proof of how a gateway reserves request cost. diff --git a/docs/engineering/execution_history/releases.md b/docs/engineering/execution_history/releases.md index a9d0c94..e2bd76b 100644 --- a/docs/engineering/execution_history/releases.md +++ b/docs/engineering/execution_history/releases.md @@ -1055,7 +1055,7 @@ After that run, the focused trust-boundary suite passed with 369 tests and 158 s #### Qualification and feature validation receipt -- The active `/Users/xeon/.local/bin/ocr` reports 1.9.5 and has SHA-256 `459d3986e59fed5ed8ad6a97bc02d2eb995a89106b3fe6a6fcf74bb69cab1b73`, matching the official Darwin arm64 release metadata and `sha256sum.txt`. Hosted run `32000131436` verified every official asset and the Linux amd64 contracts. +- The PATH-effective OCR executable reports 1.9.5 and has SHA-256 `459d3986e59fed5ed8ad6a97bc02d2eb995a89106b3fe6a6fcf74bb69cab1b73`, matching the official Darwin arm64 release metadata and `sha256sum.txt`. Hosted run `32000131436` verified every official asset and the Linux amd64 contracts. - The real budget probe selected three files, completed two, retained their findings, and represented the third as `summary.budget_exceeded`, `token_budget_reached`, and manifest `failed(budget)` coverage normalized to `partial`. The evidence matrix records the real OCR/launcher/parser boundaries and the local model-peer non-claim. - PR #94's reviewed head `a0d55caad296f14de9839eb283947d754b1633be` passed all 13 hosted checks and is tree-equivalent to squash merge `13093602a0b40521641447c9d31ed61754e90aea`. - Feature validation passed clean Python 3.12.14, 3.13.15, and 3.14.7 suites with 813 tests plus 105 subtests and at least 81.20% coverage, including real nested-venv wheel/sdist and stdio-MCP paths. Ruff, mypy, Bandit, pip-audit, pinned Gitleaks, manifest, YAML/shell, Towncrier, deterministic packaging, Twine, and clean installed-artifact smokes passed. diff --git a/docs/gitlab.md b/docs/gitlab.md index 8c7ba86..310ffe8 100644 --- a/docs/gitlab.md +++ b/docs/gitlab.md @@ -18,7 +18,7 @@ The complete variable inventory, owner, requirement, exact default, and behavior The public pipeline stores the OCR binary checksum as the non-secret `OCR_SHA256` pin. Store actual credentials as masked, protected CI variables; do not place their values in YAML, command arguments, repository evidence, or the generated bootstrap. GitLab job tokens are not accepted for posting. -`OCR_REVIEW_LANGUAGE` defaults to `English`; `Russian` is one example of an explicit review language. The example pins its qualified OCR release and sets `OCR_REVIEW_EFFORT=medium`, allowing two review rounds; `low` and `high` explicitly select one or three. OCR may stop early when a round adds no finding. `OCR_MAX_TOOLS=0` delegates the per-file tool-call limit to the installed OCR template; use a positive value only after checking that release's behaviorally qualified minimum, normalization, and effective value. `OCR_MAX_TOKENS_BUDGET` defaults to `0`, meaning unlimited; a positive budget may stop dispatch and produce an explicitly partial, automatic-approval-ineligible review. `OCR_LLM_MAX_COMPLETION_TOKENS` defaults to unset and separately controls only the provider request's completion/output cap; `4096` is a practical explicit value for gateways that reserve spending against a larger requested maximum. +`OCR_REVIEW_LANGUAGE` defaults to `English`; `Russian` is one example of an explicit review language. The example pins its qualified OCR release and sets `OCR_REVIEW_EFFORT=medium`, allowing two review rounds; `low` and `high` explicitly select one or three. OCR may stop early when a round adds no finding. `OCR_MAX_TOOLS=0` delegates the per-file tool-call limit to the installed OCR template; use a positive value only after checking that release's behaviorally qualified minimum, normalization, and effective value. `OCR_MAX_TOKENS_BUDGET` defaults to `0`, meaning unlimited; a positive budget may stop dispatch and produce an explicitly partial, automatic-approval-ineligible review. `OCR_LLM_MAX_COMPLETION_TOKENS` defaults to unset and separately controls only the provider request's completion/output cap. Select an explicit value only from the deployment's provider/model contract; the toolkit does not recommend or hardcode a provider-specific cap. OCR 1.10.2 semantically groups related changed files before review and filters candidates per group. Grouping, filtering, and multiple rounds can increase provider requests, latency, and token cost, while the manifest and aggregate budget continue to report completeness. Grouping and grace-round requests are tracked inside OCR's private session/retry diagnostics. Group labels and path-derived keys remain untrusted: the toolkit does not publish or use them for severity, fingerprints, lifecycle commands, receipts, telemetry, or approval. OCR's separately configured telemetry may export those high-cardinality repository-derived values, so keep `OCR_TELEMETRY_ENABLED=false` unless the exporter and retention policy are approved. diff --git a/docs/operations.md b/docs/operations.md index 202acbd..7107858 100644 --- a/docs/operations.md +++ b/docs/operations.md @@ -34,7 +34,7 @@ suppressed. `OCR_MAX_TOKENS_BUDGET` can set an aggregate input-plus-output token ceiling for the OCR diff review. The default `0` is unlimited. A positive ceiling is approximate rather than a hard billing cutoff because already-running work may complete; when it stops further dispatch, completed findings remain publishable and unreviewed files stay explicit as budget-attributed failed coverage. Such a run is partial and cannot automatically approve. -This aggregate budget is separate from both OCR's prompt/context `max_tokens` ceiling and the provider request's completion/output cap. The toolkit does not add an environment alias for OCR's prompt/context control. `OCR_LLM_MAX_COMPLETION_TOKENS` defaults to unset and, when set, overlays only the protocol-specific output field. The inherited OpenAI value was 58,888 in OCR 1.9.10 and is 16,384 in OCR 1.10.0 through 1.10.2. OCR 1.10.2 also applies that template-owned cap to grouping requests; an explicit toolkit override applies to all requests. A gateway may reserve cost against that requested maximum before generation even when the eventual response would be short. The `/models` capability value does not reveal an account spending limit or reservation policy, so the toolkit never selects the cap from it automatically. Set an explicit cap when a deployment must not change with the qualified OCR version; `4096` is one practical operator-selected value, not a universal default. +This aggregate budget is separate from both OCR's prompt/context `max_tokens` ceiling and the provider request's completion/output cap. The toolkit does not add an environment alias for OCR's prompt/context control. `OCR_LLM_MAX_COMPLETION_TOKENS` defaults to unset and, when set, overlays only the protocol-specific output field. The inherited OpenAI value was 58,888 in OCR 1.9.10 and is 16,384 in OCR 1.10.0 through 1.10.2. OCR 1.10.2 also applies that template-owned cap to grouping requests; an explicit toolkit override applies to all requests. A gateway may reserve cost against that requested maximum before generation even when the eventual response would be short. The `/models` capability value does not reveal an account spending limit or reservation policy, so the toolkit never selects the cap from it automatically. Set an explicit cap only from the deployment's provider/model contract when that deployment must not change with the qualified OCR version; the toolkit does not recommend or hardcode a provider-specific value. `OCR_REVIEW_EFFORT=medium` is the toolkit default for the qualified OCR release and permits two review rounds; `low` permits one and `high` permits three. This is a review-depth choice, not one of the three token controls. OCR first groups related changed files and may make group-filter requests; additional rounds can add requests, latency, and cost, but may stop early when they add no finding. The published GitLab example separately passes `OCR_MAX_TOOLS=0`, delegating the effective per-file tool-call limit to the installed OCR template instead of carrying a release-specific minimum. A positive value can raise that limit, but operators should check the release's behavioral qualification because CLI help, normalization text, and effective template value can differ. Exhausting the effective limit, an aggregate budget stop, or incomplete manifest coverage remains explicit and approval-ineligible; increasing either effort or tools is never a way to hide partial coverage. diff --git a/tests/test_operations_docs.py b/tests/test_operations_docs.py index b56badc..4b04b40 100644 --- a/tests/test_operations_docs.py +++ b/tests/test_operations_docs.py @@ -200,9 +200,11 @@ def test_completion_cap_and_provider_failure_boundaries_are_public() -> None: for document in (configuration, operations, gitlab): assert "OCR_LLM_MAX_COMPLETION_TOKENS" in document - assert "4096" in document assert "OCR_MAX_TOKENS_BUDGET" in document assert "/models" in document + assert "provider-specific" in document + for document in (configuration, operations, gitlab): + assert "OCR_LLM_MAX_COMPLETION_TOKENS=4096" not in document for field in ("max_completion_tokens", "max_output_tokens", "max_tokens"): assert field in configuration for phrase in ( From 701bedd2e39b67a7433a63c0b2019354fc751089 Mon Sep 17 00:00:00 2001 From: xeonvs <11463419+xeonvs@users.noreply.github.com> Date: Thu, 27 Aug 2026 11:51:19 +0200 Subject: [PATCH 7/7] Record v0.8.5 feature validation --- PLANS.md | 28 ++++++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/PLANS.md b/PLANS.md index 20a01c3..a94dc35 100644 --- a/PLANS.md +++ b/PLANS.md @@ -152,7 +152,7 @@ state for this task: full stable delivery authorized on 2026-08-27. | `WQ-03` | `completed` | OCR 1.10.2 pins/evidence, schedule, classifier correction, current docs/Rules, source audit, backlog reconciliation, and local no-LLM update are complete. | | `WQ-04` | `completed` | One closed provider-neutral projection renders the single numeric review log line; GitLab keeps only the prior reason with safer remediation. | | `WQ-05` | `completed` | Changelog/docs are reconciled; the complete local quality, coverage, manifest, lock, Towncrier, Gitleaks, and diff gates pass. | -| `WQ-06` | `in_progress` | Correct the PATH-effective OCR installation, complete the required real OCR review, remediate confirmed findings, and complete self-review/final feature gates. | +| `WQ-06` | `completed` | Owner-approved OCR provider-failure waiver, holistic self-review, privacy remediation, and all deterministic final feature gates are complete. | | `WQ-07` | `pending` | Reconcile exact feature head/checks/threads, mark PR ready, merge it, synchronize `main`, and verify its TestPyPI development publication. | | `WQ-08` | `pending` | Prepare the signed `release/v0.8.5` repository state and merge its exact protected release PR after all gates. | | `WQ-09` | `pending` | Monitor stable publication and independently read back bytes, provenance, attestations, installs, tag, immutable Release/receipt, issue receipts, milestone, main, and cleanup. | @@ -248,6 +248,22 @@ state for this task: full stable delivery authorized on 2026-08-27. review path executed but does not qualify semantic review or toolkit correctness. No second configured backend exists; do not describe this outcome as a passing OCR review or waive the required completed review. +- 2026-08-27: the owner authorized one retry at concurrency `1` and an explicit release + waiver if it repeated the provider failure. That retry again selected all nine supported + files but every one of 13 logical requests received HTTP 429 through 65 retries, with + zero input/output tokens and zero findings. The semantic OCR gate is therefore waived + for v0.8.5 as an external provider-infrastructure failure, not passed; no further local + OCR invocation is authorized. Deterministic tests and holistic manual review remain + mandatory before merge. +- 2026-08-27: post-waiver validation passes 1,296 tests and 337 subtests at 86.51% + branch-aware total coverage; risk groups pass at 85%, 82%, 86%, and 87% against their + 80%, 80%, 85%, and 85% floors. The 78 current release-contract tests, focused docs and + provider/privacy suites, Ruff, strict MyPy, Bandit, manifest and lock validation, + Towncrier draft, pinned Gitleaks, public-diff privacy scan, and `git diff --check` pass. + Holistic review confirms that provider diagnostics remain one closed numeric local line + and cannot affect GitLab text, DLP, receipt, telemetry, findings, or approval. Public + guidance no longer recommends or hardcodes a provider-specific completion cap, and all + required configuration-table variables remain bold. #### Risks And Recovery @@ -271,9 +287,9 @@ state for this task: full stable delivery authorized on 2026-08-27. #### Resume Point -Finish `WQ-06` from the exact Draft head: install and verify PATH-effective OCR 1.10.2, -run the single complete local review, remediate confirmed findings, perform holistic -self-review, and push the resulting signed feature state. Continue through `WQ-07` to +Continue through `WQ-07`: commit and push the reviewed feature state, reconcile exact +hosted checks and review threads, mark PR #150 ready, merge its exact reviewed tree, and +verify protected-main TestPyPI development publication. Then complete `WQ-08` and `WQ-09` without redefining stable delivery as complete before independent readback. #### Plan Fidelity Check @@ -300,9 +316,9 @@ self-review, and push the resulting signed feature state. Continue through `WQ-0 #### Closure Gate - [ ] All requirements and queue items are `done` or explicitly `out_of_scope`. -- [ ] Every logical commit passed focused tests, self-review, boundary review, and +- [x] Every completed feature logical commit passed focused tests, self-review, boundary review, and `git diff --check`. -- [ ] Final quality, coverage, OCR validation, lock, Towncrier, Gitleaks, and diff gates +- [x] Final quality, coverage, OCR validation, lock, Towncrier, Gitleaks, and diff gates are green on the exact final tree. - [ ] Hosted required checks are green and the Draft PR has no unresolved conversations. - [ ] Exact feature and release PR heads passed required checks with no unresolved threads