From eefed58e1946aa757903ead281864deae91c785a Mon Sep 17 00:00:00 2001 From: Helmut Hoffer von Ankershoffen Date: Sat, 25 Apr 2026 11:17:36 +0200 Subject: [PATCH 1/2] chore(ci): emit claude:review:passed/failed label from automated PR review [PYSDK-105] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds a machine-readable verdict from the automated Claude PR review so branch-protection rules can gate `auto-merge` on the review outcome without human intervention. Changes: * `.github/labels.yml`: two new mutually-exclusive labels - `claude:review:passed` (green) — no blocking findings on current head - `claude:review:failed` (red) — blocking findings on current head * `.github/workflows/claude-code-automation-pr-review.yml`: extend prompt with a mandatory "Machine-Readable Verdict" final step. Claude must emit PASS or FAIL based on the existing CRITICAL CHECKS criteria (test markers, coverage ≥85%, lint clean, conventional commits, architecture/security) and apply the corresponding label via `gh pr edit --add-label ... --remove-label ...`. The opposite label is always removed, so the labels stay mutually exclusive across re-reviews on subsequent pushes. Re-emission: every push to a PR re-runs the review (existing `synchronize` trigger), so the verdict is always tied to the current head commit — stale verdicts cannot persist. Co-Authored-By: Claude Opus 4.7 (1M context) --- .github/labels.yml | 8 ++++ .../claude-code-automation-pr-review.yml | 43 +++++++++++++++++++ 2 files changed, 51 insertions(+) diff --git a/.github/labels.yml b/.github/labels.yml index 237c12332..b316634cd 100644 --- a/.github/labels.yml +++ b/.github/labels.yml @@ -117,6 +117,14 @@ description: Trigger Claude Code automation color: "b41d8f" +- name: claude:review:passed + description: Automated Claude PR review found no blocking issues on the current head commit + color: "0e8a16" + +- name: claude:review:failed + description: Automated Claude PR review found blocking issues on the current head commit + color: "b60205" + - name: copilot description: GitHub Copilot related color: "e6dac6" diff --git a/.github/workflows/claude-code-automation-pr-review.yml b/.github/workflows/claude-code-automation-pr-review.yml index 55d5f5929..dfbeb2bb1 100644 --- a/.github/workflows/claude-code-automation-pr-review.yml +++ b/.github/workflows/claude-code-automation-pr-review.yml @@ -214,6 +214,49 @@ jobs: Use `gh pr comment` with your Bash tool to leave your comprehensive review as a comment on the PR. + ## Machine-Readable Verdict (MANDATORY) + + After posting your review comment, you MUST emit a single-label verdict on the PR. This label is consumed by branch-protection rules to gate auto-merge — it is the only deterministic signal of your review outcome. + + **Verdict criteria** (all must hold for PASS): + + - No blocking findings under "CRITICAL CHECKS" — i.e. no missing test markers, no coverage drop below 85%, no `make lint` failures, no conventional-commit violations. + - No blocking architecture or security violations under "Repository-Specific Review Areas". + - Suggestions / nice-to-haves do NOT block the verdict. + + If any blocking finding remains: verdict is **FAIL**. + Otherwise: verdict is **PASS**. + + **Apply the label** (the two labels are mutually exclusive — always remove the opposite one): + + ```bash + # PASS: + gh pr edit ${{ github.event.pull_request.number }} \ + --add-label "claude:review:passed" \ + --remove-label "claude:review:failed" + + # FAIL: + gh pr edit ${{ github.event.pull_request.number }} \ + --add-label "claude:review:failed" \ + --remove-label "claude:review:passed" + ``` + + Note: `--remove-label` is a no-op if the label is not present, so it is safe to always include it. + + Also include the verdict as the final line of your sticky review comment, formatted exactly as: + + ``` + **Verdict**: ✅ claude:review:passed + ``` + + or + + ``` + **Verdict**: ❌ claude:review:failed + ``` + + This makes the verdict visible to humans without scrolling through all findings. + --- **Remember**: This is medical device software. Insist on highest standards. Be thorough, actionable, and kind. From ea9b8d9dbe60332397fdc2d72a6d8745dbdb33fe Mon Sep 17 00:00:00 2001 From: Helmut Hoffer von Ankershoffen Date: Sat, 25 Apr 2026 11:25:28 +0200 Subject: [PATCH 2/2] fix(deps): bump pygments lower bound to >=2.20.0 for CVE-2026-4539 [PYSDK-106] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Re-adds the `pygments>=2.20.0` lower bound to `[project].dependencies` transitive overrides in pyproject.toml. The bound was deliberately removed in PYSDK-104 (PR #592) to give the daily audit-vulnerabilities routine a real gap to remediate; this PR closes that gap. The locked version in uv.lock (pygments 2.20.0) already protects our dev/CI env from CVE-2026-4539 (Pygments AdlLexer ReDoS, CVSS 4.8 Medium). This PR closes the remaining downstream-consumer gap so a fresh `pip install aignostics` / `uv add aignostics` / `uvx aignostics` cannot resolve `pygments<2.20.0` transitively via rich. The new lower bound (>=2.20.0) is <= the currently-locked version (2.20.0), so no dependency is upgraded — uv.lock diff is metadata-only (adds the new specifier and the dependency edge); no [[package]] version block changed. Resolves PYSDK-106. Co-Authored-By: Claude Opus 4.7 --- pyproject.toml | 1 + uv.lock | 2 ++ 2 files changed, 3 insertions(+) diff --git a/pyproject.toml b/pyproject.toml index 28d48a282..c1ed2d37f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -138,6 +138,7 @@ dependencies = [ "lxml>=6.1.0", # CVE-2026-41066 (Renovate #556); also required for python 3.14 pre-built wheels "filelock>=3.20.3", # CVE-2025-68146 (>=3.20.1); CVE-2026-22701 (>=3.20.3, Renovate #387) "marshmallow>=3.26.2", # CVE-2025-68480 + "pygments>=2.20.0", # CVE-2026-4539 (>=2.20.0); transitive via rich "cryptography>=46.0.7", # CVE-2026-39892 (>=46.0.7); transitive via pyjwt[crypto] "pydicom>=3.0.2", # CVE-2026-32711 (>=3.0.2); transitive via dicomweb-client/wsidicom/highdicom "pyasn1>=0.6.3", # CVE-2026-30922 (>=0.6.3); transitive via cryptography diff --git a/uv.lock b/uv.lock index 5ee497cd8..a0f806f6d 100644 --- a/uv.lock +++ b/uv.lock @@ -69,6 +69,7 @@ dependencies = [ { name = "pyasn1" }, { name = "pydantic-settings" }, { name = "pydicom" }, + { name = "pygments" }, { name = "pyjwt", extra = ["crypto"] }, { name = "python-dateutil" }, { name = "python-multipart" }, @@ -215,6 +216,7 @@ requires-dist = [ { name = "pyasn1", specifier = ">=0.6.3" }, { name = "pydantic-settings", specifier = ">=2.12.0,<3" }, { name = "pydicom", specifier = ">=3.0.2" }, + { name = "pygments", specifier = ">=2.20.0" }, { name = "pyinstaller", marker = "extra == 'pyinstaller'", specifier = ">=6.14.0,<7" }, { name = "pyjwt", extras = ["crypto"], specifier = ">=2.12.0,<3" }, { name = "python-dateutil", specifier = ">=2.9.0.post0,<3" },