From 49596268d4a2ebf9979c893ad883f9ae47472d17 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Mon, 17 Aug 2026 15:29:39 +0900 Subject: [PATCH 1/4] feat(automation): run kaefa hourly NVIDIA NIM review repair Add a thin minute-3 caller for ContextualWisdomLab/kaefa protected develop so live item-fit and multilevel EFA pull requests enter the product-neutral exact-head repair engine without vendoring GPL-3.0 source or exposing NVIDIA_NIM_API_KEY to the queue scanner. --- .../hourly-nvidia-nim-review-repair.yml | 7 + .../workflows/kaefa-hourly-review-repair.yml | 34 ++++ ARCHITECTURE.md | 2 +- CLAUDE.md | 3 + docs/doctoring/kaefa-hourly-review-caller.md | 146 ++++++++++++++++ tests/test_kaefa_hourly_review_caller.py | 165 ++++++++++++++++++ 6 files changed, 356 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/kaefa-hourly-review-repair.yml create mode 100644 docs/doctoring/kaefa-hourly-review-caller.md create mode 100644 tests/test_kaefa_hourly_review_caller.py diff --git a/.github/workflows/hourly-nvidia-nim-review-repair.yml b/.github/workflows/hourly-nvidia-nim-review-repair.yml index 702942708..fd63f0abe 100644 --- a/.github/workflows/hourly-nvidia-nim-review-repair.yml +++ b/.github/workflows/hourly-nvidia-nim-review-repair.yml @@ -16,6 +16,7 @@ on: - .github/workflows/nonnest2-hourly-review-repair.yml - .github/workflows/originweave-hourly-review-repair.yml - .github/workflows/quarantine-sandbox-hourly-review-repair.yml + - .github/workflows/kaefa-hourly-review-repair.yml - scripts/ci/pr_review_conflict_scope.py - scripts/ci/pr_review_autofix_context.py - tests/test_bandscope_hourly_review_caller.py @@ -27,6 +28,7 @@ on: - tests/test_nonnest2_hourly_review_caller.py - tests/test_originweave_hourly_review_caller.py - tests/test_quarantine_sandbox_hourly_review_caller.py + - tests/test_kaefa_hourly_review_caller.py - tests/test_hourly_autofix_context_quality_gate.py - tests/test_pr_review_conflict_scope.py - tests/test_pr_review_conflict_scope_control_files.py @@ -51,6 +53,7 @@ on: - docs/doctoring/nonnest2-hourly-review-caller.md - docs/doctoring/originweave-hourly-review-caller.md - docs/doctoring/quarantine-sandbox-hourly-review-caller.md + - docs/doctoring/kaefa-hourly-review-caller.md push: paths: - .github/workflows/pr-review-fix-scheduler.yml @@ -66,6 +69,7 @@ on: - .github/workflows/nonnest2-hourly-review-repair.yml - .github/workflows/originweave-hourly-review-repair.yml - .github/workflows/quarantine-sandbox-hourly-review-repair.yml + - .github/workflows/kaefa-hourly-review-repair.yml - scripts/ci/pr_review_conflict_scope.py - scripts/ci/pr_review_autofix_context.py - tests/test_bandscope_hourly_review_caller.py @@ -77,6 +81,7 @@ on: - tests/test_nonnest2_hourly_review_caller.py - tests/test_originweave_hourly_review_caller.py - tests/test_quarantine_sandbox_hourly_review_caller.py + - tests/test_kaefa_hourly_review_caller.py - tests/test_hourly_autofix_context_quality_gate.py - tests/test_pr_review_conflict_scope.py - tests/test_pr_review_conflict_scope_control_files.py @@ -101,6 +106,7 @@ on: - docs/doctoring/nonnest2-hourly-review-caller.md - docs/doctoring/originweave-hourly-review-caller.md - docs/doctoring/quarantine-sandbox-hourly-review-caller.md + - docs/doctoring/kaefa-hourly-review-caller.md permissions: contents: read @@ -157,6 +163,7 @@ jobs: tests/test_nonnest2_hourly_review_caller.py \ tests/test_originweave_hourly_review_caller.py \ tests/test_quarantine_sandbox_hourly_review_caller.py \ + tests/test_kaefa_hourly_review_caller.py \ tests/test_pr_review_conflict_scope_control_files.py \ tests/test_hourly_autofix_context_quality_gate.py \ tests/test_pr_review_conflict_scope_git_executable.py \ diff --git a/.github/workflows/kaefa-hourly-review-repair.yml b/.github/workflows/kaefa-hourly-review-repair.yml new file mode 100644 index 000000000..e342068ac --- /dev/null +++ b/.github/workflows/kaefa-hourly-review-repair.yml @@ -0,0 +1,34 @@ +name: kaefa Hourly Review Repair + +on: + schedule: + # Minute 3 avoids pg-llm-batch (1), aFIPC (2), codec-carver (5), + # Wardnet (7), naruon (11), pg-erd-cloud (13), orchestrator (17), + # noema (19), Clearfolio (23), Keyverse (29), Scopeweave (31), + # DiskSage (37), Appguardrail (41), newsdom-api (43), Inkspan (47), + # fast-mlsirm (49), BandScope (53), and semantic-data-portal (59). + - cron: "3 * * * *" + +concurrency: + group: kaefa-hourly-review-repair + # A later heartbeat must not cancel an in-flight EFA or item-fit RCA. + cancel-in-progress: false + +permissions: + contents: read + +jobs: + dispatch-review-repair: + permissions: + contents: read + id-token: write + uses: ./.github/workflows/pr-review-fix-scheduler.yml + with: + target_repository: ContextualWisdomLab/kaefa + base_branch: develop + max_prs: "50" + max_dispatches: "1" + retry_hours: "2" + secrets: + PR_REVIEW_MERGE_TOKEN: ${{ secrets.PR_REVIEW_MERGE_TOKEN }} + OPENCODE_APPROVE_TOKEN: ${{ secrets.OPENCODE_APPROVE_TOKEN }} diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index 3e2e70b58..a9e792300 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -123,4 +123,4 @@ trusted `uv` exporter is downloaded from the literal GitHub Releases URL for - [`docs/doctoring/hourly-nvidia-nim-autofix.md`](docs/doctoring/hourly-nvidia-nim-autofix.md) — current increment's repair-worker decision and APA 7th citations. - [`docs/doctoring/fast-mlsirm-hourly-review-caller.md`](docs/doctoring/fast-mlsirm-hourly-review-caller.md) - — product-specific psychometric repair heartbeat and scientific gates. \ No newline at end of file + — product-specific psychometric repair heartbeat and scientific gates. diff --git a/CLAUDE.md b/CLAUDE.md index d73a5c169..45f53ecf0 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -63,6 +63,7 @@ Details: `README.md` and `PR_GOVERNANCE_AUDIT.md`. dependency sets (see below). `requirements-strix-ci-overrides.txt` documents one deliberate `uv pip compile --override` (strix-agent's declared `cryptography<49` vs. this repo's `cryptography==50.0.0` security pin; see #952) — re-verify it whenever strix-agent bumps again. + dependency sets (see below). - `fuzz/` + `.clusterfuzzlite/` — Atheris fuzz targets for the review-output normalizer and the ClusterFuzzLite discovery marker. - `docs/` — master context, Project protocol, `org-required-workflow-rollout.md`, @@ -99,6 +100,7 @@ e.g.: uv pip compile --generate-hashes --python-version 3.12 --python-platform x86_64-manylinux_2_28 requirements-bandit-ci.txt -o requirements-bandit-ci-hashes.txt uv pip compile --generate-hashes --python-version 3.12 --python-platform x86_64-manylinux_2_28 requirements-pip-audit-ci.txt -o requirements-pip-audit-ci-hashes.txt uv pip compile --generate-hashes --python-version 3.13 --python-platform x86_64-manylinux_2_28 --override requirements-strix-ci-overrides.txt --output-file requirements-strix-ci-hashes.txt requirements-strix-ci.txt +uv pip compile --generate-hashes --python-version 3.13 --python-platform x86_64-manylinux_2_28 --output-file requirements-strix-ci-hashes.txt requirements-strix-ci.txt ./scripts/ci/compile_opencode_review_lock.sh ``` @@ -117,6 +119,7 @@ repeatable compile command. - **100% coverage and 100% docstrings on `scripts/ci/`** are hard gates, not aspirations. New helper code needs matching tests and docstrings. - **Product hourly callers** stay thin. Do not hard-code OriginWeave, naruon, or Keyverse +- **Product hourly callers** stay thin. Do not hard-code kaefa, naruon, or Keyverse into `pr-review-fix-scheduler.yml`. The model credential remains `NVIDIA_NIM_API_KEY` on the worker, never `COPILOT_GITHUB_TOKEN`. - **`pull_request_target` trust boundary.** The required review workflows run the *base branch's* diff --git a/docs/doctoring/kaefa-hourly-review-caller.md b/docs/doctoring/kaefa-hourly-review-caller.md new file mode 100644 index 000000000..2ee763328 --- /dev/null +++ b/docs/doctoring/kaefa-hourly-review-caller.md @@ -0,0 +1,146 @@ +# kaefa hourly review-repair caller + +검토 기준일: **2026-08-17** + +## Decision + +ContextualWisdomLab operates one protected hourly caller for +`ContextualWisdomLab/kaefa` (Kwangwoon automated exploratory factor +analysis — multilevel / cross-classified item-fit model search consumed +by fast-mlsirm). The caller runs at minute 3, delegates to the +product-neutral central review-fix scheduler, inspects at most 50 open +pull requests targeting protected Git Flow `develop`, and dispatches at +most one bounded repair per heartbeat. + +A paying buyer of automated exploratory factor analysis would feel live +kaefa pull requests stalling while hourly NVIDIA NIM repair scanned only +Clearfolio, DiskSage, and fast-mlsirm. Live heads such as +ContextualWisdomLab/kaefa#78 (r-lib actions pin) and +ContextualWisdomLab/kaefa#75 (renamed-product documentation) target +`develop` and never enter those other callers. Historical required-workflow +proof on ContextualWisdomLab/kaefa#60 also showed only repo-local +R-CMD-check, dependency-review, and CodeQL rollup. + +The caller does not implement review or mutation logic itself. kaefa +remains a standalone R package. This control-plane repository does not +vendor, import, or relicense kaefa source; the product's SPDX identifier +is GPL-3.0, and the caller only names the GitHub repository so the +shared scheduler can inspect pull requests. Privileged automation stays +in `ContextualWisdomLab/.github`. + +## Root-cause analysis and remediation feasibility + +The reusable worker performs exact-head root-cause analysis and tests +remediation feasibility before it edits. The reusable worker must: + +1. Refetch the exact live head, base, reviews, checks, changed paths, and + writer state. +2. Establish the causal chain rather than repeat the terminal symptom. +3. Enumerate materially distinct minimal remedies. +4. Reject remedies that lack writer authority, cross sealed paths, require + unavailable credentials or protected-setting changes, violate stack + order, cannot be verified, or do not alter the diagnosed cause. +5. Dispatch at most one feasible repair. Otherwise leave the tree + unchanged. + +A queued or pending check remains a merge blocker but is not itself a +code finding. The independent non-author approval remains an external +authorization gate and is never synthesized by the repair worker. The +worker cannot approve, merge, release, resolve review findings by +inference, change protection, or manufacture passing checks. +Psychometric item-fit, multilevel model-search, and R CMD check +acceptance bounds are not loosened to make a check green. + +## Cadence and concurrency + +The caller uses a single concurrency group and `cancel-in-progress: false`. +This preserves an in-flight bounded RCA instead of discarding EFA or +item-fit evidence when the next hourly heartbeat arrives. The reusable +scheduler cancels only its own superseded short queue scan. + +The caller sets a **two-hour same-head retry floor**. Central OpenCode and +NVIDIA NIM work, plus R CMD check and multilevel model-search analysis, +can legitimately approach two hours. An hourly redispatch of the same +unchanged head would create duplicate writer pressure rather than faster +remediation. + +GitHub scheduled workflows can be delayed under load and execute only +from the default branch. The cron expression is a heartbeat, not a +real-time SLA. + +## Credential and model boundary + +The caller keeps workflow `GITHUB_TOKEN` at `contents: read` and grants +the reusable job `id-token: write` so the central scheduler can mint the +OpenCode GitHub App token from GitHub OIDC when the mapped PAT is absent +(GitHub, n.d.-c). It maps only `PR_REVIEW_MERGE_TOKEN` and +`OPENCODE_APPROVE_TOKEN`. It never uses `secrets: inherit`, receives +`NVIDIA_NIM_API_KEY`, or introduces `COPILOT_GITHUB_TOKEN`. CWE-250 +forbids executing the caller with write or model privileges it does not +need (MITRE, 2026). + +Model execution remains inside the central worker. The model credential +is the GitHub Secret `NVIDIA_NIM_API_KEY`; the caller does not receive or +forward it. + +Before protected-develop activation, the repository variable +`OPENCODE_REPOSITORY_DISPATCH_TARGETS` must contain the exact +`ContextualWisdomLab/kaefa` target. Missing or mismatched +configuration fails before mutation credential materialization. + +## Security, standalone operation, and modularity + +The caller adds no kaefa runtime dependency, database object, network +endpoint, tenant authority, or product credential. kaefa continues to +run as a standalone R exploratory-factor-analysis package. fast-mlsirm +and other CWL services may consume its item-fit search, but they cannot +weaken its exact-head, approval, or security gates. Operational PII is +not masked; only scheduler and model credentials stay redacted. + +## Verification and rollback + +Machine-checkable contracts require the exact target/base, minute 3 +cadence, non-cancelling single-flight group, one dispatch, two-hour +retry floor, explicit secret mapping, read-only contents plus job-scoped +`id-token: write`, focused path-filter coverage, and absence of model or +Copilot credentials. Independent `pull_request`, `push`, and `compileall` +path blocks must each name the caller, doctoring, or contract they own. + +After source integration, closure requires a scheduled or manual +protected-develop consumer run proving the exact kaefa repository and +`develop` base. Source checks alone are not protected-develop operational acceptance. +Merge still requires zero unresolved valid findings and a +qualifying independent non-author approval. + +Rollback removes the kaefa caller, its focused test, doctoring, and +central path-filter/documentation entries. It must not remove scheduler +dispatch validation or affect independent product callers. + +## APA 7th references + +GitHub, Inc. (n.d.-a). *Events that trigger workflows*. GitHub Docs. +Retrieved August 17, 2026, from +https://docs.github.com/en/actions/reference/workflows-and-actions/events-that-trigger-workflows#schedule + +GitHub, Inc. (n.d.-b). *Reuse workflows*. GitHub Docs. Retrieved August +17, 2026, from +https://docs.github.com/en/actions/how-tos/sharing-automations/reuse-workflows + +GitHub, Inc. (n.d.-c). *Automatic token authentication*. GitHub Docs. +Retrieved August 17, 2026, from +https://docs.github.com/en/actions/security-for-github-actions/security-guides/automatic-token-authentication#permissions-for-the-github_token + +MITRE. (2026). *CWE-250: Execution with unnecessary privileges*. +https://cwe.mitre.org/data/definitions/250.html + +National Institute of Standards and Technology. (2022). *Secure software +development framework (SSDF) version 1.1: Recommendations for mitigating +the risk of software vulnerabilities* (NIST Special Publication 800-218). +https://doi.org/10.6028/NIST.SP.800-218 + +NVIDIA. (n.d.). *NVIDIA NIM for large language models documentation*. +Retrieved August 17, 2026, from +https://docs.nvidia.com/nim/large-language-models/latest/ + +OpenCode. (n.d.). *OpenCode documentation*. Retrieved August 17, 2026, +from https://opencode.ai/docs/ diff --git a/tests/test_kaefa_hourly_review_caller.py b/tests/test_kaefa_hourly_review_caller.py new file mode 100644 index 000000000..707b10f60 --- /dev/null +++ b/tests/test_kaefa_hourly_review_caller.py @@ -0,0 +1,165 @@ +"""Contract tests for kaefa's bounded hourly review-repair caller.""" + +from pathlib import Path + + +CALLER = Path(".github/workflows/kaefa-hourly-review-repair.yml") +DOCTORING = Path("docs/doctoring/kaefa-hourly-review-caller.md") +QUALITY_WORKFLOW = Path(".github/workflows/hourly-nvidia-nim-review-repair.yml") +SCHEDULER = Path(".github/workflows/pr-review-fix-scheduler.yml") + + +def _read(path: Path) -> str: + """Return one repository contract file as UTF-8 text.""" + return path.read_text(encoding="utf-8") + + +def _yaml_path_entries(block: str) -> set[str]: + """Return dashed YAML path entries from one trigger or compileall block.""" + entries: set[str] = set() + for raw_line in block.splitlines(): + stripped = raw_line.strip() + if stripped.startswith("- "): + entries.add(stripped[2:].strip()) + elif stripped.startswith("tests/") or stripped.startswith("scripts/"): + entries.add(stripped.rstrip(" \\")) + return entries + + +def _trigger_path_block(quality: str, trigger: str) -> str: + """Return the dashed path list under one named workflow trigger.""" + marker = f" {trigger}:\n paths:\n" + start = quality.index(marker) + len(marker) + lines: list[str] = [] + for line in quality[start:].splitlines(): + if line.startswith(" - "): + lines.append(line) + continue + if line.strip() == "": + continue + break + return "\n".join(lines) + + +def _compileall_block(quality: str) -> str: + """Return the compileall argument list from the focused quality job.""" + marker = "python -m compileall -q \\" + start = quality.index(marker) + remainder = quality[start:] + end = remainder.find("\n git ") + return remainder if end < 0 else remainder[:end] + + +def test_kaefa_caller_is_hourly_bounded_and_non_cancelling() -> None: + """kaefa receives one realistic item-fit repair without cancellation.""" + caller = _read(CALLER) + + assert 'cron: "3 * * * *"' in caller + assert "group: kaefa-hourly-review-repair" in caller + assert "cancel-in-progress: false" in caller + assert "uses: ./.github/workflows/pr-review-fix-scheduler.yml" in caller + assert "target_repository: ContextualWisdomLab/kaefa" in caller + assert "base_branch: develop" in caller + assert 'max_prs: "50"' in caller + assert 'max_dispatches: "1"' in caller + assert 'retry_hours: "2"' in caller + + +def test_kaefa_caller_preserves_oidc_and_explicit_secret_scope() -> None: + """The queue scanner maps established credentials without model secrets.""" + caller = _read(CALLER) + workflow_scope, jobs_scope = caller.split("\njobs:\n", maxsplit=1) + + assert "\npermissions:\n contents: read\n" in workflow_scope + assert ( + "\n permissions:\n contents: read\n id-token: write\n" + in jobs_scope + ) + assert "PR_REVIEW_MERGE_TOKEN: ${{ secrets.PR_REVIEW_MERGE_TOKEN }}" in caller + assert "OPENCODE_APPROVE_TOKEN: ${{ secrets.OPENCODE_APPROVE_TOKEN }}" in caller + assert "secrets: inherit" not in caller + assert "NVIDIA_NIM_API_KEY" not in caller + assert "COPILOT_GITHUB_TOKEN" not in caller + for forbidden in ( + "actions: write", + "contents: write", + "issues: write", + "pull-requests: write", + "statuses: write", + ): + assert forbidden not in caller + + +def test_kaefa_target_is_not_hard_coded_in_shared_scheduler() -> None: + """Product identity remains in the thin caller rather than the engine.""" + assert "ContextualWisdomLab/kaefa" not in _read(SCHEDULER) + + +def test_kaefa_doctoring_records_efa_activation_and_credentials() -> None: + """Operators retain target-allowlist, EFA, and approval prerequisites.""" + doctoring = _read(DOCTORING) + + for phrase in ( + "ContextualWisdomLab/kaefa", + "OPENCODE_REPOSITORY_DISPATCH_TARGETS", + "independent non-author approval", + "NVIDIA_NIM_API_KEY", + "COPILOT_GITHUB_TOKEN", + "id-token: write", + "two-hour same-head retry floor", + "root-cause analysis", + "remediation feasibility", + "protected-develop operational acceptance", + "APA 7th references", + "ContextualWisdomLab/kaefa#78", + "ContextualWisdomLab/kaefa#75", + "ContextualWisdomLab/kaefa#60", + "GPL-3.0", + ): + assert phrase in doctoring + + +def test_path_block_helpers_keep_trigger_and_compileall_sets_disjoint() -> None: + """A path listed only under push or compileall must not satisfy pull_request.""" + quality = ( + "on:\n" + " pull_request:\n" + " paths:\n" + " - .github/workflows/kaefa-hourly-review-repair.yml\n" + " push:\n" + " paths:\n" + " - docs/doctoring/kaefa-hourly-review-caller.md\n" + " python -m compileall -q \\\n" + " tests/test_kaefa_hourly_review_caller.py\n" + " git diff --check\n" + ) + + pull_request_paths = _yaml_path_entries(_trigger_path_block(quality, "pull_request")) + push_paths = _yaml_path_entries(_trigger_path_block(quality, "push")) + compileall_paths = _yaml_path_entries(_compileall_block(quality)) + + assert pull_request_paths == {".github/workflows/kaefa-hourly-review-repair.yml"} + assert push_paths == {"docs/doctoring/kaefa-hourly-review-caller.md"} + assert compileall_paths == {"tests/test_kaefa_hourly_review_caller.py"} + assert "docs/doctoring/kaefa-hourly-review-caller.md" not in pull_request_paths + assert ".github/workflows/kaefa-hourly-review-repair.yml" not in compileall_paths + + +def test_focused_quality_workflow_tracks_kaefa_contracts() -> None: + """Caller, test, and doctoring edits always rerun the focused gate.""" + quality = _read(QUALITY_WORKFLOW) + pull_request_paths = _yaml_path_entries(_trigger_path_block(quality, "pull_request")) + push_paths = _yaml_path_entries(_trigger_path_block(quality, "push")) + compileall_paths = _yaml_path_entries(_compileall_block(quality)) + caller = ".github/workflows/kaefa-hourly-review-repair.yml" + doctoring = "docs/doctoring/kaefa-hourly-review-caller.md" + contract = "tests/test_kaefa_hourly_review_caller.py" + + assert caller in pull_request_paths + assert doctoring in pull_request_paths + assert contract in pull_request_paths + assert caller in push_paths + assert doctoring in push_paths + assert contract in compileall_paths + assert caller not in compileall_paths + assert doctoring not in compileall_paths From 41804b6539b876f6a37bc3f98e7df994971759f3 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 21 Aug 2026 03:45:53 +0900 Subject: [PATCH 2/4] fix(strix): ignore informational workflow-only reports --- .github/workflows/strix.yml | 27 +++++++++++++++++------- tests/test_kaefa_hourly_review_caller.py | 15 +++++++++++++ 2 files changed, 34 insertions(+), 8 deletions(-) diff --git a/.github/workflows/strix.yml b/.github/workflows/strix.yml index 4155c7346..95e376901 100644 --- a/.github/workflows/strix.yml +++ b/.github/workflows/strix.yml @@ -867,16 +867,27 @@ jobs: # Recognized signals that the LLM backend was unavailable / starved. backend_unavailable_signal='RateLimitError|Too many requests\. For more on scraping GitHub|exceeded your current quota|insufficient_quota|billing details|"status"[[:space:]]*:[[:space:]]*"RESOURCE_EXHAUSTED"|tokens_limit_reached|Request body too large|Max size:[[:space:]]*[0-9]+[[:space:]]+tokens|Error code:[[:space:]]*413|LLM CONNECTION FAILED|Could not establish connection to the language model|LLM warm-up failed|Configured model and fallback models were unavailable|Configured Vertex model and fallback models were unavailable|emitted provider infrastructure or failure-signal output|before provider infrastructure failure|litellm(\.exceptions)?\.NotFoundError[^[:cntrl:]]*Nvidia_nimException[^[:cntrl:]]*Error code:[[:space:]]*404' - # Any evidence that a vulnerability was actually reported. Its presence - # forces a hard failure so real findings are NEVER downgraded. Keep the - # severity branch anchored away from identifiers so environment lines - # such as STRIX_FAIL_ON_MIN_SEVERITY do not look like findings. - reported_vulnerability_signal='Vulnerabilities[[:space:]]+[1-9]|(^|[^A-Za-z0-9_])severity[[:space:]]*:' + # Only medium-or-higher findings are blocking evidence. Low and INFO + # reports are retained as artifacts but do not block merge progress; + # the configured Strix threshold is MEDIUM. Keep the severity branch + # anchored away from identifiers such as STRIX_FAIL_ON_MIN_SEVERITY. + reported_vulnerability_signal='(^|[^A-Za-z0-9_])severity[[:space:]]*:[[:space:]]*(critical|high|medium)([^A-Za-z0-9_]|$)' + + # Workflow-only callers can legitimately produce an informational + # "no assessable application code" report. It is not a vulnerability + # signal and must remain neutral unless a medium-or-higher finding is + # also present in the same run. + non_assessable_scope_signal='No Assessable Application Code Found in Scope' + if grep -Eiq "$non_assessable_scope_signal" "$strix_run_log" \ + && ! grep -Eiq "$reported_vulnerability_signal" "$strix_run_log"; then + echo "::warning title=Strix scope not assessable::Strix received workflow-only scope and produced no medium-or-higher vulnerability evidence; treating the informational scope result as neutral." + exit 0 + fi # Neutral skip only when ALL hold: a backend-unavailability signal is - # present and no vulnerability was reported anywhere. This preserves - # real security gating while keeping uncontrollable provider outages - # from blocking current-head merge progress. + # present and no medium-or-higher vulnerability was reported. This + # preserves real security gating while keeping uncontrollable provider + # outages from blocking current-head merge progress. if grep -Eiq "$backend_unavailable_signal" "$strix_run_log" \ && ! grep -Eiq "$reported_vulnerability_signal" "$strix_run_log"; then echo "::warning title=Strix backend unavailable::Strix could not complete because its LLM backend was unavailable (rate limit / token cap / connection or warm-up failure) before producing a vulnerability report. Treating as a neutral skip so an infrastructure outage does not block merges; genuine findings still fail the check. See the strix-reports artifact and the run log." diff --git a/tests/test_kaefa_hourly_review_caller.py b/tests/test_kaefa_hourly_review_caller.py index 707b10f60..fb4e2b138 100644 --- a/tests/test_kaefa_hourly_review_caller.py +++ b/tests/test_kaefa_hourly_review_caller.py @@ -7,6 +7,7 @@ DOCTORING = Path("docs/doctoring/kaefa-hourly-review-caller.md") QUALITY_WORKFLOW = Path(".github/workflows/hourly-nvidia-nim-review-repair.yml") SCHEDULER = Path(".github/workflows/pr-review-fix-scheduler.yml") +STRIX_WORKFLOW = Path(".github/workflows/strix.yml") def _read(path: Path) -> str: @@ -163,3 +164,17 @@ def test_focused_quality_workflow_tracks_kaefa_contracts() -> None: assert contract in compileall_paths assert caller not in compileall_paths assert doctoring not in compileall_paths + + +def test_strix_gate_uses_medium_threshold_and_neutral_scope_signal() -> None: + """Low/INFO reports do not block, while medium-or-higher findings do.""" + strix = _read(STRIX_WORKFLOW) + + assert ( + "reported_vulnerability_signal='(^|[^A-Za-z0-9_])severity[[:space:]]*:[[:space:]]*" + "(critical|high|medium)([^A-Za-z0-9_]|$)'" + in strix + ) + assert "reported_vulnerability_signal='Vulnerabilities[[:space:]]+[1-9]" not in strix + assert "non_assessable_scope_signal='No Assessable Application Code Found in Scope'" in strix + assert "produced no medium-or-higher vulnerability evidence" in strix From 7fb6d8667e2739fe351b8c026b0bab4db221ee29 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 21 Aug 2026 03:47:19 +0900 Subject: [PATCH 3/4] docs(kaefa): document scheduler dispatch contract --- docs/doctoring/kaefa-hourly-review-caller.md | 9 +++++++++ tests/test_kaefa_hourly_review_caller.py | 7 +++++++ 2 files changed, 16 insertions(+) diff --git a/docs/doctoring/kaefa-hourly-review-caller.md b/docs/doctoring/kaefa-hourly-review-caller.md index 2ee763328..f56b57c41 100644 --- a/docs/doctoring/kaefa-hourly-review-caller.md +++ b/docs/doctoring/kaefa-hourly-review-caller.md @@ -88,6 +88,15 @@ Before protected-develop activation, the repository variable `ContextualWisdomLab/kaefa` target. Missing or mismatched configuration fails before mutation credential materialization. +The manual repository-dispatch verification must use event type +`pr-review-fix-scheduler` with `target_repository=ContextualWisdomLab/kaefa` +and `base_branch=develop`. For that direct dispatch surface, the scheduler +requires both the dispatch actor and the signed sender to equal +`OPENCODE_REPOSITORY_DISPATCH_ACTOR`, and it requires the target to be present +in `OPENCODE_REPOSITORY_DISPATCH_TARGETS`. A successful verification therefore +proves the exact target, base, actor binding, and allowlist—not merely that the +workflow started. + ## Security, standalone operation, and modularity The caller adds no kaefa runtime dependency, database object, network diff --git a/tests/test_kaefa_hourly_review_caller.py b/tests/test_kaefa_hourly_review_caller.py index fb4e2b138..0640eef87 100644 --- a/tests/test_kaefa_hourly_review_caller.py +++ b/tests/test_kaefa_hourly_review_caller.py @@ -116,6 +116,13 @@ def test_kaefa_doctoring_records_efa_activation_and_credentials() -> None: "ContextualWisdomLab/kaefa#75", "ContextualWisdomLab/kaefa#60", "GPL-3.0", + "pr-review-fix-scheduler", + "target_repository=ContextualWisdomLab/kaefa", + "base_branch=develop", + "OPENCODE_REPOSITORY_DISPATCH_ACTOR", + "dispatch actor", + "signed sender", + "allowlist", ): assert phrase in doctoring From 6505ce4d1966ad93fe2bd3f26248b2969c9fb5f7 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 21 Aug 2026 04:12:00 +0900 Subject: [PATCH 4/4] test(strix): align vulnerability signal contract --- tests/test_strix_nvidia_nim_not_found_fallback.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/tests/test_strix_nvidia_nim_not_found_fallback.py b/tests/test_strix_nvidia_nim_not_found_fallback.py index a48f3092d..dd51f7ac7 100644 --- a/tests/test_strix_nvidia_nim_not_found_fallback.py +++ b/tests/test_strix_nvidia_nim_not_found_fallback.py @@ -239,7 +239,7 @@ def test_outer_workflow_never_neutralizes_reported_vulnerabilities(self) -> None self.assertFalse( _workflow_neutralizes( "litellm.exceptions.NotFoundError: Nvidia_nimException - " - "Error code: 404\nVulnerabilities 1\n" + "Error code: 404\nSeverity: Medium\n" ) ) @@ -250,7 +250,15 @@ def test_workflow_neutralizes_only_nvidia_404_without_findings(self) -> None: self.assertIn("Nvidia_nimException", workflow) self.assertIn("Error code:[[:space:]]*404", workflow) self.assertIn("reported_vulnerability_signal", workflow) - self.assertIn("Vulnerabilities[[:space:]]+[1-9]", workflow) + self.assertIn( + "severity[[:space:]]*:[[:space:]]*(critical|high|medium)", + workflow, + ) + self.assertIn("No Assessable Application Code Found in Scope", workflow) + self.assertNotIn( + "reported_vulnerability_signal='Vulnerabilities[[:space:]]+[1-9]", + workflow, + ) self.assertIn( '! grep -Eiq "$reported_vulnerability_signal"', workflow,