Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 43 additions & 11 deletions .github/workflows/noema-review.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ on:
default: ""
type: string
canonical_ref:
description: Ref of ContextualWisdomLab/.github to use for trusted review scripts
description: Deprecated; trusted review scripts are resolved from this workflow's trusted source SHA
required: false
default: main
type: string
Expand Down Expand Up @@ -76,23 +76,55 @@ jobs:
if: env.PR_NUMBER != ''
id: trusted_source
env:
INPUT_CANONICAL_REF: ${{ github.event.inputs.canonical_ref || '' }}
WORKFLOW_REF: ${{ github.workflow_ref }}
JOB_CONTEXT_JSON: ${{ toJSON(job) }}
GITHUB_CONTEXT_JSON: ${{ toJSON(github) }}
run: |
set -euo pipefail
trusted_ref="${INPUT_CANONICAL_REF:-main}"
case "$WORKFLOW_REF" in
ContextualWisdomLab/.github/.github/workflows/noema-review.yml@*)
trusted_ref="${WORKFLOW_REF##*@}"
;;
esac
printf 'ref=%s\n' "$trusted_ref" >>"$GITHUB_OUTPUT"
python3 <<'PY' >>"$GITHUB_OUTPUT"
import json
import os
import re
import sys

try:
job_context = json.loads(os.environ.get("JOB_CONTEXT_JSON") or "{}")
github_context = json.loads(os.environ.get("GITHUB_CONTEXT_JSON") or "{}")
except json.JSONDecodeError as exc:
print(f"::error::Could not parse GitHub workflow context JSON: {exc}", file=sys.stderr)
raise SystemExit(1)

trusted_repository = str(
job_context.get("workflow_repository") or "ContextualWisdomLab/.github"
).strip()
trusted_ref = str(
job_context.get("workflow_sha") or github_context.get("workflow_sha") or ""
).strip()
workflow_ref = str(
job_context.get("workflow_ref") or github_context.get("workflow_ref") or ""
).strip()

if not trusted_ref:
trusted_ref = "main"
prefix = "ContextualWisdomLab/.github/.github/workflows/noema-review.yml@"
if workflow_ref.startswith(prefix):
trusted_ref = workflow_ref.split("@", 1)[1]

if trusted_repository != "ContextualWisdomLab/.github":
print("::error::Trusted Noema workflow repository resolved outside ContextualWisdomLab/.github.", file=sys.stderr)
raise SystemExit(1)
if not re.fullmatch(r"[0-9a-fA-F]{40}|refs/[^\s]+|[A-Za-z0-9._/-]+", trusted_ref):
print("::error::Trusted Noema workflow ref resolved to an invalid value.", file=sys.stderr)
raise SystemExit(1)

print(f"repository={trusted_repository}")
print(f"ref={trusted_ref}")
PY

- name: Checkout trusted Noema review gate
if: env.PR_NUMBER != ''
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
repository: ContextualWisdomLab/.github
repository: ${{ steps.trusted_source.outputs.repository }}
ref: ${{ steps.trusted_source.outputs.ref }}
fetch-depth: 1
persist-credentials: false
Expand Down
24 changes: 23 additions & 1 deletion .github/workflows/opencode-review.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1564,7 +1564,29 @@ jobs:
FAILED_CHECK_EVIDENCE_SLEEP_SECONDS: "15"
run: |
set -euo pipefail
printf 'OPENCODE_CHANGED_FILES_FILE=%s\n' "$OPENCODE_CHANGED_FILES_FILE" >>"$GITHUB_ENV"

case "${GH_REPOSITORY:-}" in
*/*) ;;
*)
echo "::error::Invalid GH_REPOSITORY for OpenCode evidence collection."
exit 1
;;
esac
case "${PR_NUMBER:-}" in
''|*[!0-9]*)
echo "::error::Invalid PR_NUMBER for OpenCode evidence collection."
exit 1
;;
esac
for candidate_sha in "$PR_BASE_SHA" "$PR_HEAD_SHA" "$HEAD_SHA"; do
case "$candidate_sha" in
[0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F]) ;;
*)
echo "::error::Invalid commit SHA for OpenCode evidence collection."
exit 1
;;
esac
done

current_peer_checks_still_running() {
local owner="${GH_REPOSITORY%%/*}"
Expand Down
55 changes: 44 additions & 11 deletions .github/workflows/pr-review-merge-scheduler.yml
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ on:
default: ""
type: string
canonical_ref:
description: Ref of ContextualWisdomLab/.github to use for scheduler code
description: Deprecated; scheduler code is resolved from this workflow's trusted source SHA
required: false
default: "main"
type: string
Expand Down Expand Up @@ -247,24 +247,57 @@ jobs:
- name: Resolve trusted scheduler source ref
id: trusted_source
env:
INPUT_CANONICAL_REF: ${{ inputs.canonical_ref || '' }}
WORKFLOW_REF: ${{ github.workflow_ref }}
JOB_CONTEXT_JSON: ${{ toJSON(job) }}
GITHUB_CONTEXT_JSON: ${{ toJSON(github) }}
run: |
set -euo pipefail
trusted_ref="${INPUT_CANONICAL_REF:-main}"
case "$WORKFLOW_REF" in
ContextualWisdomLab/.github/.github/workflows/pr-review-merge-scheduler.yml@*)
trusted_ref="${WORKFLOW_REF##*@}"
;;
esac
printf 'ref=%s\n' "$trusted_ref" >>"$GITHUB_OUTPUT"
python3 <<'PY' >>"$GITHUB_OUTPUT"
import json
import os
import re
import sys

try:
job_context = json.loads(os.environ.get("JOB_CONTEXT_JSON") or "{}")
github_context = json.loads(os.environ.get("GITHUB_CONTEXT_JSON") or "{}")
except json.JSONDecodeError as exc:
print(f"::error::Could not parse GitHub workflow context JSON: {exc}", file=sys.stderr)
raise SystemExit(1)

trusted_repository = str(
job_context.get("workflow_repository") or "ContextualWisdomLab/.github"
).strip()
trusted_ref = str(
job_context.get("workflow_sha") or github_context.get("workflow_sha") or ""
).strip()
workflow_ref = str(
job_context.get("workflow_ref") or github_context.get("workflow_ref") or ""
).strip()

if not trusted_ref:
trusted_ref = "main"
prefix = "ContextualWisdomLab/.github/.github/workflows/pr-review-merge-scheduler.yml@"
if workflow_ref.startswith(prefix):
trusted_ref = workflow_ref.split("@", 1)[1]

if trusted_repository != "ContextualWisdomLab/.github":
print("::error::Trusted scheduler workflow repository resolved outside ContextualWisdomLab/.github.", file=sys.stderr)
raise SystemExit(1)
if not re.fullmatch(r"[0-9a-fA-F]{40}|refs/[^\s]+|[A-Za-z0-9._/-]+", trusted_ref):
print("::error::Trusted scheduler workflow ref resolved to an invalid value.", file=sys.stderr)
raise SystemExit(1)

print(f"repository={trusted_repository}")
print(f"ref={trusted_ref}")
PY

- name: Checkout trusted scheduler
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
repository: ContextualWisdomLab/.github
repository: ${{ steps.trusted_source.outputs.repository }}
ref: ${{ steps.trusted_source.outputs.ref }}
fetch-depth: 1
persist-credentials: false

- name: Self-test scheduler
run: python3 scripts/ci/pr_review_merge_scheduler.py --self-test
Expand Down
22 changes: 22 additions & 0 deletions .github/workflows/secret-scan.yml
Original file line number Diff line number Diff line change
Expand Up @@ -78,9 +78,14 @@ jobs:
HEAD_SHA: ${{ github.event.pull_request.head.sha }}
run: |
set +e
config_args=()
if [ -f .gitleaks.toml ]; then
config_args=(--config .gitleaks.toml)
fi
if [ "${IS_PR}" = "true" ]; then
# Diff-scoped: only the commits this PR introduces.
./gitleaks git . \
"${config_args[@]}" \
--log-opts="${BASE_SHA}..${HEAD_SHA}" \
--redact \
--report-format sarif \
Expand All @@ -89,13 +94,30 @@ jobs:
else
# Full git history on schedule / push to a protected branch.
./gitleaks git . \
"${config_args[@]}" \
--redact \
--report-format sarif \
--report-path gitleaks-results.sarif \
--exit-code 2
fi
echo "rc=$?" >> "$GITHUB_OUTPUT"
set -e
- name: Summarize redacted gitleaks findings
if: always() && hashFiles('gitleaks-results.sarif') != ''
run: |
set -euo pipefail
count="$(jq '[.runs[].results[]?] | length' gitleaks-results.sarif)"
if [ "$count" = "0" ]; then
echo "::notice::gitleaks completed with no findings."
exit 0
fi
echo "::error::gitleaks reported ${count} redacted finding(s). Rule, path, and line summary follows; secret values are not printed."
jq -r '
.runs[].results[]?
| "- rule: `" + (.ruleId // "unknown") + "`"
+ ", path: `" + (.locations[0].physicalLocation.artifactLocation.uri // "unknown") + "`"
+ ", line: `" + ((.locations[0].physicalLocation.region.startLine // "unknown") | tostring) + "`"
' gitleaks-results.sarif | sort | uniq -c
- name: Upload gitleaks SARIF to code scanning
if: always() && hashFiles('gitleaks-results.sarif') != ''
continue-on-error: true
Expand Down
17 changes: 17 additions & 0 deletions .gitleaks.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
title = "ContextualWisdomLab central gitleaks configuration"

[extend]
useDefault = true

[allowlist]
description = "False-positive token-like strings used by scheduler scrubber regression tests only."
regexTarget = "match"
paths = [
'''^\.gitleaks\.toml$''',
'''(^|/)__pycache__/''',
'''\.pyc$''',
]
regexes = [
'''gh[pousr]_(?:1234567890abcdef(?:1234)?|abcdef1234567890abcdef1234567890abcdef|installation_token_value|user_token_value|server_token_value|runner_token_value|placeholder_token_with_underscores_123)''',
'''github_pat_11AAAAA_(?:abcdefg|abcdefg1234567890)''',
]
24 changes: 24 additions & 0 deletions .gitleaksignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Historical false-positive GitHub-token fixtures from scheduler secret-scrubbing tests.
# The live tests now construct these token-like strings at runtime; new findings remain blocking.
123bddb83426e21367f61affa004f9259b101fe8:tests/test_pr_review_merge_scheduler.py:github-pat:224
123bddb83426e21367f61affa004f9259b101fe8:tests/test_pr_review_merge_scheduler.py:github-pat:227
14655a9304c2dbe1e2ad6c963af4a2a0b7bd4b89:tests/test_pr_review_merge_scheduler.py:github-pat:2770
14655a9304c2dbe1e2ad6c963af4a2a0b7bd4b89:tests/test_pr_review_merge_scheduler.py:github-pat:2778
14655a9304c2dbe1e2ad6c963af4a2a0b7bd4b89:tests/test_pr_review_merge_scheduler.py:github-pat:2793
14655a9304c2dbe1e2ad6c963af4a2a0b7bd4b89:tests/test_pr_review_merge_scheduler.py:github-pat:2798
2ce72097a608290af5828c205f009c430bde7f9e:tests/test_pr_review_merge_scheduler.py:github-pat:2488
2ce72097a608290af5828c205f009c430bde7f9e:tests/test_pr_review_merge_scheduler.py:github-pat:2496
2ce72097a608290af5828c205f009c430bde7f9e:tests/test_pr_review_merge_scheduler.py:github-pat:2511
2ce72097a608290af5828c205f009c430bde7f9e:tests/test_pr_review_merge_scheduler.py:github-pat:2516
3909b97ae8e42b5e7a0ea6ba78b6f4f4e4a294d4:tests/test_pr_review_merge_scheduler.py:github-pat:2770
3909b97ae8e42b5e7a0ea6ba78b6f4f4e4a294d4:tests/test_pr_review_merge_scheduler.py:github-pat:2778
3909b97ae8e42b5e7a0ea6ba78b6f4f4e4a294d4:tests/test_pr_review_merge_scheduler.py:github-pat:2793
3909b97ae8e42b5e7a0ea6ba78b6f4f4e4a294d4:tests/test_pr_review_merge_scheduler.py:github-pat:2798
697d44d6a06d148e086340cd25fd026d5d603899:tests/test_pr_review_merge_scheduler.py:github-pat:2488
697d44d6a06d148e086340cd25fd026d5d603899:tests/test_pr_review_merge_scheduler.py:github-pat:2496
697d44d6a06d148e086340cd25fd026d5d603899:tests/test_pr_review_merge_scheduler.py:github-pat:2511
697d44d6a06d148e086340cd25fd026d5d603899:tests/test_pr_review_merge_scheduler.py:github-pat:2516
79885ae2741c2c8f44b73ba7f7c7b20e7b675966:tests/test_pr_review_merge_scheduler.py:github-pat:697
79885ae2741c2c8f44b73ba7f7c7b20e7b675966:tests/test_pr_review_merge_scheduler.py:github-pat:701
79885ae2741c2c8f44b73ba7f7c7b20e7b675966:tests/test_pr_review_merge_scheduler.py:github-pat:718
79885ae2741c2c8f44b73ba7f7c7b20e7b675966:tests/test_pr_review_merge_scheduler.py:github-pat:722
17 changes: 17 additions & 0 deletions scripts/ci/strix_quick_gate.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2550,6 +2550,18 @@ is_transient_same_model_retry_error() {
return 1
}

github_models_rate_limit_should_skip_same_model_retry() {
local model="$1"

if ! is_rate_limit_error; then
return 1
fi
if ! is_github_models_api_compatible_model "$model"; then
return 1
fi
github_models_api_base_is_active
}

run_strix_with_transient_retry() {
local model="$1"
local max_attempts=$((STRIX_TRANSIENT_RETRY_PER_MODEL + 1))
Expand Down Expand Up @@ -2578,6 +2590,11 @@ run_strix_with_transient_retry() {
return 1
fi

if github_models_rate_limit_should_skip_same_model_retry "$model"; then
echo "GitHub Models rate limit detected for model '$model'; skipping same-model retry and moving directly to fallback models or current-head neutral classification." >&2
return 1
fi

if ! is_transient_same_model_retry_error "$model"; then
return 1
fi
Expand Down
53 changes: 48 additions & 5 deletions scripts/ci/test_strix_quick_gate.sh
Original file line number Diff line number Diff line change
Expand Up @@ -1161,9 +1161,10 @@ assert_pr_review_merge_scheduler_uses_github_actions_bot_token() {
assert_file_contains "$workflow_file" "--review-dispatch-limit" "scheduler passes the dispatch budget to the canonical script"
assert_file_contains "$workflow_file" 'GH_TOKEN: ${{ github.token }}' "scheduler uses the caller workflow token so mutations are attributed to GitHub Actions in the target repository"
assert_file_contains "$workflow_file" "Resolve trusted scheduler source ref" "scheduler required workflow resolves the central trusted source ref"
assert_file_contains "$workflow_file" "github.workflow_ref" "scheduler required workflow can reuse the required-workflow source ref"
assert_file_contains "$workflow_file" 'repository: ContextualWisdomLab/.github' "scheduler checks out the canonical implementation instead of relying on repo-local copies"
assert_file_contains "$workflow_file" "workflow_sha" "scheduler required workflow pins trusted source checkout to the job workflow commit SHA when available"
assert_file_contains "$workflow_file" 'repository: ${{ steps.trusted_source.outputs.repository }}' "scheduler checks out the validated central implementation instead of relying on repo-local copies"
assert_file_contains "$workflow_file" 'ref: ${{ steps.trusted_source.outputs.ref }}' "scheduler checks out the resolved central ref"
assert_file_contains "$workflow_file" "persist-credentials: false" "scheduler trusted checkout does not persist workflow credentials into the checkout"
assert_file_contains "$workflow_file" "contents: write" "scheduler has write permission for GitHub Actions bot branch updates"
assert_file_contains "$workflow_file" "pull-requests: write" "scheduler has pull-request write permission for update-branch and auto-merge"
assert_file_not_contains "$workflow_file" "format('pr-{0}-{1}', github.event.pull_request.number, github.event.pull_request.head.sha)" "scheduler does not keep stale head-specific concurrency groups"
Expand Down Expand Up @@ -5126,6 +5127,17 @@ PY
"scenario=$scenario does not rewrite logs through symlinked report directories"
fi

if [ "$scenario" = "github-models-primary-ratelimit-fallback-success" ]; then
assert_file_contains \
"$output_log" \
"GitHub Models rate limit detected for model 'openai/gpt-5'; skipping same-model retry and moving directly to fallback models or current-head neutral classification." \
"scenario=$scenario logs why same-model retry was skipped"
assert_file_not_contains \
"$output_log" \
"Retrying model 'openai/gpt-5' due to rate limit" \
"scenario=$scenario does not sleep in same-model retry after GitHub Models rate limiting"
fi

if [ "$scenario" = "pr-changed-scope-full-set" ]; then
assert_internal_pr_scope_targets "$target_log" "$repo_root_dir" "$expected_calls"
fi
Expand Down Expand Up @@ -5274,6 +5286,37 @@ run_filtered_gate_case_if_requested() {
"pull_request" \
"frontend/src/components/CalendarLayout.tsx"
;;
github-models-primary-ratelimit-fallback-success)
run_gate_case "github-models-primary-ratelimit-fallback-success" \
"openai/gpt-5" \
"" \
"0" \
"REGEX:Strix quick scan succeeded with fallback model 'deepseek/deepseek-r1-0528' in [0-9]+s\\." \
"2" \
"openai/gpt-5|openai/deepseek/deepseek-r1-0528" \
"https://models.github.ai/inference|https://models.github.ai/inference" \
"openai" \
"https://models.github.ai/inference" \
"" \
"2" \
"CRITICAL" \
"0" \
"" \
"" \
"1200" \
"0" \
"" \
"" \
"" \
"" \
"0" \
"" \
"" \
"" \
"__SAME_AS_FALLBACK_MODELS__" \
"deepseek/deepseek-r1-0528 deepseek/deepseek-v3-0324" \
"1"
;;
github-models-fallback-baseline-vulnerability-before-next-success-continues)
run_gate_case "github-models-fallback-baseline-vulnerability-before-next-success-continues" \
"openai/gpt-5" \
Expand Down Expand Up @@ -8244,9 +8287,9 @@ run_gate_case "github-models-primary-ratelimit-fallback-success" \
"" \
"0" \
"REGEX:Strix quick scan succeeded with fallback model 'deepseek/deepseek-r1-0528' in [0-9]+s\\." \
"4" \
"openai/gpt-5|openai/gpt-5|openai/gpt-5|openai/deepseek/deepseek-r1-0528" \
"https://models.github.ai/inference|https://models.github.ai/inference|https://models.github.ai/inference|https://models.github.ai/inference" \
"2" \
"openai/gpt-5|openai/deepseek/deepseek-r1-0528" \
"https://models.github.ai/inference|https://models.github.ai/inference" \
"openai" \
"https://models.github.ai/inference" \
"" \
Expand Down
Loading
Loading