Skip to content

fix(CODEWIKI-004): CU-86akbhg0r 17 review findings across 11 files #75

fix(CODEWIKI-004): CU-86akbhg0r 17 review findings across 11 files

fix(CODEWIKI-004): CU-86akbhg0r 17 review findings across 11 files #75

# Generated by the Flamingo hub — do not edit by hand.
# Per-repo settings live in the hub admin (/admin/code-review); this file is
# byte-identical across every reviewed repository AND every deployment, which
# is what makes drift detectable by comparison. The hub's address arrives in
# the dispatch payload (doc-orchestrator style) or, for pull_request runs, the
# org Actions variable FLAMINGO_HUB_BASE_URL.
# Named like its sibling pipeline ('🦩 Flamingo Code Documentation') so both
# read as one product family in the Actions sidebar.
name: 🦩 Flamingo Code Review
on:
# Push trigger - registers workflow with GitHub Actions (required for
# workflow_dispatch API). Only fires when this file itself changes; the job
# skips it — same pattern as the doc-orchestrator workflow.
push:
paths:
- '.github/workflows/flamingo-code-review.yml'
# ENTERS-REVIEW ONLY (2026-08-12; corrected 2026-09-07). The automatic
# triggers are the moments a pull request ENTERS review, and nothing else.
#
# 'synchronize' (per-push) stays deliberately OFF. Reviewing every commit on
# an open PR is the largest avoidable cost in an AI review pipeline, and it
# trains authors to tune the bot out; the 2026 industry default is to review
# at review moments and offer an explicit re-review on demand.
#
# 'opened' and 'reopened' were OFF between 2026-08-15 and 2026-09-07, on the
# assumption that PRs are opened as drafts and later promoted. They are not
# — PRs here are born non-draft, GitHub NEVER fires ready_for_review for
# those, and so the automatic review effectively never ran. Drafts stay
# excluded: an 'opened' event for a draft is dropped by the job's
# draft == false guard, and that PR is reviewed later, on its
# ready_for_review.
#
# 'labeled' is the on-demand re-review — the affordance that makes the
# no-per-push default liveable. Commits pushed AFTER the first review are not
# auto-reviewed, so adding the flamingo-review label is how a human
# asks for another pass. A SEPARATE job removes it the moment the event
# arrives, so adding it again asks again, and it can never strand itself on a
# pull request the review gate declines. Every other label name is dropped by
# the job's if — a skipped run, zero billable minutes.
#
# A 'synchronize' carrying that label is treated as the same request. GitHub
# runs the workflow from the pull request's HEAD, so a label applied to a
# branch whose head predates this file fires an event no workflow was there
# to handle: the label lands, nothing runs, nothing consumes it, and because
# it is now already present, adding it again fires NOTHING AT ALL. Honouring
# it on the next push is what breaks that stall — the request gets served
# once and consumed, instead of sitting on the pull request looking applied.
#
# 'synchronize' (every push) is otherwise here ONLY to serve the
# flamingo-review-always label, and the job's if drops it on every pull
# request that does not carry it. That is the per-PR escalation: subscribe the
# risky refactor to continuous review, leave everything else on one review per
# pull request. A skipped push run costs no billable minutes.
#
# Every repeat pass diffs only the delta since the last reviewed head — see
# the incremental anchor in code-review-review.mjs.
pull_request:
types: [opened, ready_for_review, reopened, labeled, synchronize]
repository_dispatch:
types: [flamingo-code-review]
# Lets the hub target a SETUP BRANCH before the install PR merges — the same
# test-before-merge flow the doc pipeline uses. repository_dispatch only ever
# fires on the default branch.
workflow_dispatch:
# ═══════════════════════════════════════════════════════════════════════════
# GENERATED FROM SINGLE SOURCE OF TRUTH: CODE_REVIEW_PARAMS (this module)
# This section is auto-generated when creating workflow PRs
# ═══════════════════════════════════════════════════════════════════════════
inputs:
run_id:
description: 'Hub run-record id binding this execution to a code_review_runs row. Empty for pull_request runs — the hub creates the row from the callback.'
required: false
default: ''
run_token:
description: 'Per-run token minted by the hub at dispatch. Binds the callback to THIS run — the org-wide secret alone would let any repo report against another repo’s run.'
required: false
default: ''
mode:
description: 'Review mode: pr (diff-scoped), sweep (whole-repo scanner) or mine (rule mining over the full checkout).'
required: false
default: 'sweep'
hub_base_url:
description: 'Absolute hub origin. Dispatches carry it doc-orchestrator style; pull_request runs fall back to the org/repo Actions variable FLAMINGO_HUB_BASE_URL (shipped by Sync Secrets).'
required: false
default: ''
review_budget_chars:
description: 'Sweep corpus budget in characters for this run (review_config.sweep_budget_chars). Empty = UNCAPPED — the whole eligible corpus is reviewed in ~80K batches. The local twin defaults to 80000 instead; set the per-repo value to cap CI spend.'
required: false
default: ''
# ═══════════════════════════════════════════════════════════════════════════
# END GENERATED SECTION
# ═══════════════════════════════════════════════════════════════════════════
# GENERATED FROM SINGLE SOURCE OF TRUTH: CODE_REVIEW_PARAMS (this module)
# The doc-orchestrator's parameter chain for EVERY dispatch param: payload
# first, workflow_dispatch input second, then the param's runtime fallback.
# Workflow-level, so every dispatch var reaches every step and every
# downloaded script. SECURITY: only NON-SENSITIVE values live here — secrets
# are passed per-step, same as the doc workflow.
env:
RUN_ID: ${{ github.event.client_payload.run_id || github.event.inputs.run_id || '' }}
MODE: ${{ github.event.client_payload.mode || github.event.inputs.mode || (github.event_name == 'pull_request' && 'pr' || 'sweep') }}
HUB_BASE_URL: ${{ github.event.client_payload.hub_base_url || github.event.inputs.hub_base_url || vars.FLAMINGO_HUB_BASE_URL }}
REVIEW_BUDGET_CHARS: ${{ github.event.client_payload.review_budget_chars || github.event.inputs.review_budget_chars || '' }}
# Superseding a PR cancels the in-flight review of the stale head SHA; the
# fresh run reviews (and reports on) the new one. The always() report step
# still fires on the cancelled run, so its row records 'cancelled' rather
# than dangling.
concurrency:
group: flamingo-code-review-${{ github.event.pull_request.number || github.run_id }}
cancel-in-progress: true
permissions:
contents: read
pull-requests: write
checks: write
jobs:
# Consuming the label is its OWN job, gated on nothing but "that label was
# added". Living inside the review job meant a label applied to a pull request the
# review gate declines — a bot PR, a fork — was never consumed: the label sat
# there permanently, and because it was already present, adding it again did
# nothing. A silent dead end with no feedback. Split out, the request is
# always consumed, whether or not a review follows it.
#
# It does NOT remove flamingo-review-always: that label is durable state
# ("keep reviewing this PR"), not a one-shot request.
consume-review-request:
# The fork clause is not a token guard — a fork pull_request run gets a
# READ-ONLY token whatever this file declares, so the DELETE below could
# not succeed there even without it. It is here because a job that
# provably cannot do its work should not start: on a fork the label
# strands either way, and burning a runner to fail silently only hides
# that. Same clause, same reason, as the review job below.
if: >-
github.event_name == 'pull_request' &&
github.event.pull_request.head.repo.full_name == github.repository &&
((github.event.action == 'labeled' &&
github.event.label.name == 'flamingo-review') ||
(github.event.action == 'synchronize' &&
contains(github.event.pull_request.labels.*.name, 'flamingo-review')))
runs-on: ubuntu-latest
permissions:
pull-requests: write
steps:
- name: Consume the on-demand review label
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
REPO_FULL: ${{ github.repository }}
PR_NUMBER: ${{ github.event.pull_request.number }}
# Best-effort: losing this race must never fail anything.
run: |
CURL_CFG=$(mktemp) && chmod 600 "$CURL_CFG"
printf 'header = "Authorization: Bearer %s"\n' "$GITHUB_TOKEN" > "$CURL_CFG"
curl -sS --max-time 30 -K "$CURL_CFG" -X DELETE \
"https://api.github.com/repos/$REPO_FULL/issues/$PR_NUMBER/labels/flamingo-review" > /dev/null || true
rm -f "$CURL_CFG"
review:
# Skip actual work when triggered by push (push only registers the
# workflow with GitHub, which is what lets workflow_dispatch target a
# setup branch before the install PR merges).
# Bots and forks never run, on EVERY event. A fork PR's token is read-only,
# so the run could not post a comment or a check even if it finished; the
# bot clause is what stops a hub-opened ai-fix PR from being reviewed into
# another fix PR, and no label may waive it — "only a human applies a
# label" is an assumption, not an invariant, since anything holding
# pull-requests: write can label. To review a bot PR, dispatch it from the
# hub admin, which is an authenticated, recorded decision.
#
# DRAFT is different, and only the on-demand label waives it: an explicit
# request says "review this now" and means it whether or not the PR is
# finished, which is how every comparable tool treats its manual trigger.
# Automatic events and subscribed pushes still skip drafts.
# NOTE: there is deliberately no vars. kill switch here. The hub's
# per-repo enabled dial already covers it and answers 409 REVIEW_DISABLED,
# which produces a recorded run. A second switch living in GitHub would be
# invisible to the admin screen and would produce no callback at all.
if: >-
github.event_name != 'push' &&
(github.event_name == 'repository_dispatch' ||
github.event_name == 'workflow_dispatch' ||
(github.event.pull_request.user.type != 'Bot' &&
github.event.pull_request.head.repo.full_name == github.repository &&
((github.event.action == 'labeled' &&
github.event.label.name == 'flamingo-review') ||
(github.event.action == 'synchronize' &&
(contains(github.event.pull_request.labels.*.name, 'flamingo-review') ||
(github.event.pull_request.draft == false &&
contains(github.event.pull_request.labels.*.name, 'flamingo-review-always')))) ||
(github.event.action != 'labeled' &&
github.event.action != 'synchronize' &&
github.event.pull_request.draft == false))))
runs-on: ubuntu-latest
# NO custom timeout-minutes — deliberately. GitHub's 6h hosted-runner
# ceiling is the only clock: on hitting it the always() report step still
# runs, so a sweep ships its per-batch checkpoint home and the NEXT sweep
# resumes from the covered files (nothing is lost, nothing re-paid). Hung
# runs are the hub reaper's job (run-safety-net-utils.ts, no-report cutoff
# sized past the platform ceiling) — a second hand-tuned ceiling here was
# one more number to keep in sync for no added safety.
steps:
# Fail LOUD, not silent: a pull_request run on a repo whose org never set
# FLAMINGO_HUB_BASE_URL would otherwise curl an empty origin and die with
# an unrelated error. Also normalizes a trailing slash ONCE for every
# downstream consumer (a value of https://hub.example/ would otherwise
# yield //api double-slash paths in four places).
- name: Validate configuration
run: |
if [ -z "$HUB_BASE_URL" ]; then
echo "::error::HUB_BASE_URL is empty — set the org Actions variable FLAMINGO_HUB_BASE_URL (or pass hub_base_url in the dispatch payload)."
exit 1
fi
echo "HUB_BASE_URL=${HUB_BASE_URL%/}" >> "$GITHUB_ENV"
# REPORT CAPABILITY FIRST. Only workflow-helpers.sh + code-review-report.sh
# download here — before checkout, cache, or any other script — so every
# later failure (including the download step for the REST of the scripts
# 404ing or hash-mismatching, the class that produced phantom 'running'
# rows) still has a verified report script to call home with.
- name: Download the report script
env:
WEBHOOK_SECRET: ${{ secrets.DOC_ORCH_WEBHOOK_SECRET }}
HASH_WORKFLOW_HELPERS: "3df9f07c408d987a44e9df7ba1584f01ff57502a596f013dd3218d3b4c30fd4c"
HASH_REPORT: "479b45311e6e91e7dc36b90d6a32db57fae25b215fdcd35c2889fb5f0618df07"
run: |
set -euo pipefail
SCRIPTS_BASE_URL="${HUB_BASE_URL}/api/doc-orchestrator/scripts"
download_and_verify() {
local script_name="$1"
local expected_hash="$2"
local output_path="/tmp/$script_name"
curl -fsSL "$SCRIPTS_BASE_URL/$script_name" \
-H "Authorization: Bearer $WEBHOOK_SECRET" \
-o "$output_path"
local actual_hash=$(shasum -a 256 "$output_path" | cut -d' ' -f1)
if [ "$actual_hash" != "$expected_hash" ]; then
echo "❌ HASH MISMATCH for $script_name!"
echo " Expected: $expected_hash"
echo " Actual: $actual_hash"
echo " This could indicate tampering or an outdated hash."
exit 1
fi
# Make shell scripts executable
if [[ "$script_name" == *.sh ]]; then
chmod +x "$output_path"
fi
echo "✅ $script_name verified (hash: ${actual_hash:0:16}...)"
}
download_and_verify "workflow-helpers.sh" "$HASH_WORKFLOW_HELPERS"
download_and_verify "code-review-report.sh" "$HASH_REPORT"
# Early liveness ping — the FIRST real step after report capability is
# secured. Stamps workflow_run_id + status 'running' on the hub's run row,
# so "dispatch accepted but nothing ever ran" (no ping — reaped fast) is
# distinguishable from "started, then crashed" (pinged — longer deadline).
# Best-effort by design: the script exits 0 regardless; the always()
# report below is the authoritative callback.
- name: Tell the hub the run started
env:
WEBHOOK_SECRET: ${{ secrets.DOC_ORCH_WEBHOOK_SECRET }}
RUN_TOKEN: ${{ github.event.client_payload.run_token || github.event.inputs.run_token || '' }}
HEAD_SHA: ${{ github.event.pull_request.head.sha || github.sha }}
WF_RUN_ID: ${{ github.run_id }}
REPO_FULL: ${{ github.repository }}
PR_NUMBER: ${{ github.event.pull_request.number }}
run: /tmp/code-review-report.sh --started
# v5 = the Node 24 drop-in (v4 targets EOL Node 20 and warns on every run).
- name: Check out the code under review
uses: actions/checkout@v5
with:
fetch-depth: 0
# The review job must never hold a push credential.
persist-credentials: false
# The corpus hash has to SURVIVE between runs, or the hash param is empty
# and the 304 path the hub implements is unreachable — the corpus would be
# transferred in full on every run in both modes.
# v5 = the Node 24 drop-in (v4 targets EOL Node 20 and warns on every run).
- name: Restore the last corpus hash
uses: actions/cache@v5
with:
# BOTH files. Caching only the hash meant a 304 left rules.json
# truncated to zero bytes while the run continued as if it had a
# corpus.
path: |
.rules-hash
rules.json
key: flamingo-rules-hash-${{ github.repository }}-${{ github.run_id }}
restore-keys: |
flamingo-rules-hash-${{ github.repository }}-
# The SAME download_and_verify function as the doc-orchestrator workflow
# (single source: lib/config/workflow-scripts-bootstrap.ts, parity with
# the doc template asserted at build time), fetching from the SAME
# scripts endpoint. This bootstrap (and the report-capability step above,
# which shares it) is the ONLY inline bash in the file — every other step
# runs a verified unified script, so a script change ships from the hub
# without touching this caller. Helpers + report already downloaded above;
# this step fetches the rest, and its failure is REPORTABLE.
- name: Download and verify scripts
env:
WEBHOOK_SECRET: ${{ secrets.DOC_ORCH_WEBHOOK_SECRET }}
HASH_FETCH_RULES: "a3adb1976b782a6bfe405b68c7adc52f6650535e0d9fbc2ea427c3567f2c9943"
HASH_RUN: "e4ebfe017a09ed3f783c8aea995a079be46b4add3b3f92db1918291d1b3e7412"
HASH_MINE: "c12e6fd532bab44c95e855046efef3b45940fbde87d449d5577d198a0d3c4f62"
HASH_REVIEW: "fb7fa7802d31c7af69af300a0d7f5898f054b78efd44cd1262e880d1ad85c9b7"
HASH_LIB: "adeba0f5d1c68ee2531e6c23f4a2ec206f3dc33f67a2b8228ade99509c6a6149"
HASH_POST: "75404d6cf735f9a9f1332e65efb6e055ccaf18fe678b1fb3d74d0b0b996c6f2d"
run: |
set -euo pipefail
SCRIPTS_BASE_URL="${HUB_BASE_URL}/api/doc-orchestrator/scripts"
download_and_verify() {
local script_name="$1"
local expected_hash="$2"
local output_path="/tmp/$script_name"
curl -fsSL "$SCRIPTS_BASE_URL/$script_name" \
-H "Authorization: Bearer $WEBHOOK_SECRET" \
-o "$output_path"
local actual_hash=$(shasum -a 256 "$output_path" | cut -d' ' -f1)
if [ "$actual_hash" != "$expected_hash" ]; then
echo "❌ HASH MISMATCH for $script_name!"
echo " Expected: $expected_hash"
echo " Actual: $actual_hash"
echo " This could indicate tampering or an outdated hash."
exit 1
fi
# Make shell scripts executable
if [[ "$script_name" == *.sh ]]; then
chmod +x "$output_path"
fi
echo "✅ $script_name verified (hash: ${actual_hash:0:16}...)"
}
download_and_verify "code-review-fetch-rules.sh" "$HASH_FETCH_RULES"
download_and_verify "code-review-run.sh" "$HASH_RUN"
download_and_verify "code-review-mine.mjs" "$HASH_MINE"
download_and_verify "code-review-review.mjs" "$HASH_REVIEW"
download_and_verify "code-review-lib.mjs" "$HASH_LIB"
download_and_verify "code-review-post.mjs" "$HASH_POST"
- name: Fetch the rule corpus
id: rules
env:
WEBHOOK_SECRET: ${{ secrets.DOC_ORCH_WEBHOOK_SECRET }}
# PR runs identify the pull request so the hub can answer with the
# last successfully reviewed head SHA (X-Last-Reviewed-Sha) — the
# incremental-review anchor persisted as .last-reviewed-sha.
PR_NUMBER: ${{ github.event.pull_request.number }}
run: /tmp/code-review-fetch-rules.sh
# Stage checkpoints are their OWN credentialed steps — the review step
# deliberately never holds the webhook secret or run token (it processes
# untrusted PR content), so pings happen at step boundaries, not inside.
- name: Report progress — reviewing
env:
WEBHOOK_SECRET: ${{ secrets.DOC_ORCH_WEBHOOK_SECRET }}
RUN_TOKEN: ${{ github.event.client_payload.run_token || github.event.inputs.run_token || '' }}
WF_RUN_ID: ${{ github.run_id }}
REPO_FULL: ${{ github.repository }}
PR_NUMBER: ${{ github.event.pull_request.number }}
run: /tmp/code-review-report.sh --progress review
- name: Review
# The id lets the report step read the reviewer's own degraded output
# (e.g. skipped_trivial_diff) alongside the rules step's.
id: review
if: steps.rules.outputs.skip != 'true'
# MODE / RUN_ID arrive via the generated workflow-level env; only the
# secret is scoped to the step (doc-workflow security discipline).
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
run: /tmp/code-review-run.sh
# Post the findings to the PULL REQUEST itself and publish the
# 'Flamingo Code Review' check run. pull_request events only — a sweep
# has no PR to post to (its findings live in the hub dashboard). Uses the
# runner's own GITHUB_TOKEN under the workflow's minimal grant
# (pull-requests: write for the comments, checks: write for the check
# run). One UPSERTED summary comment + inline comments where the diff
# supports them; already-posted fingerprints are never re-commented, so
# a new push adds only NEW findings. The check run is 'neutral' unless
# the repo's served mode is 'blocking' AND action_required findings
# exist. Writes posted.json (fingerprint -> comment id) for the report
# step's callback — the reaction-learning loop reads it.
- name: Report progress — finalizing
env:
WEBHOOK_SECRET: ${{ secrets.DOC_ORCH_WEBHOOK_SECRET }}
RUN_TOKEN: ${{ github.event.client_payload.run_token || github.event.inputs.run_token || '' }}
WF_RUN_ID: ${{ github.run_id }}
REPO_FULL: ${{ github.repository }}
PR_NUMBER: ${{ github.event.pull_request.number }}
run: /tmp/code-review-report.sh --progress post
- name: Post to the pull request
id: post
if: github.event_name == 'pull_request' && steps.rules.outputs.skip != 'true'
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
DEGRADED: ${{ steps.review.outputs.degraded }}
PR_NUMBER: ${{ github.event.pull_request.number }}
HEAD_SHA: ${{ github.event.pull_request.head.sha }}
run: node /tmp/code-review-post.mjs
# A crashed Review step must still leave a trace ON THE PR: default
# step-skip semantics silently skip the post step on failure, so the
# developer saw no signal at all that a review was attempted — the
# failure lived only on the hub's internal dashboard. Best-effort (||
# true): a failed courtesy comment must not mask the real failure state.
- name: Post crash notice to the pull request
if: failure() && github.event_name == 'pull_request' && steps.review.outcome == 'failure'
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
PR_NUMBER: ${{ github.event.pull_request.number }}
REPO_FULL: ${{ github.repository }}
WF_RUN_ID: ${{ github.run_id }}
run: |
BODY="🦩 **Flamingo Code Review crashed on this push.** No findings were posted — this is an infrastructure failure, not a clean review. See the [workflow run](https://github.com/$REPO_FULL/actions/runs/$WF_RUN_ID)."
CURL_CFG=$(mktemp) && chmod 600 "$CURL_CFG"
printf 'header = "Authorization: Bearer %s"\n' "$GITHUB_TOKEN" > "$CURL_CFG"
curl -s -K "$CURL_CFG" -X POST "https://api.github.com/repos/$REPO_FULL/issues/$PR_NUMBER/comments" \
-H "Content-Type: application/json" \
--data "$(printf '%s' "$BODY" | jq -Rs '{body: .}')" > /dev/null || true
rm -f "$CURL_CFG"
# The report script downloads FIRST (dedicated step above), so a missing
# /tmp/code-review-report.sh here means the report-capability bootstrap
# itself failed (endpoint down, hash mismatch before anything else ran).
# The guarded inline curl below still posts a minimal failure callback for
# exactly that case; the hub's tiered reaper remains the last resort.
- name: Report back to the hub
if: always() && env.HUB_BASE_URL != ''
# RUN_ID / MODE / HUB_BASE_URL arrive via the generated workflow-level
# env. RUN_TOKEN is SENSITIVE (per-run bearer) so it is step-scoped
# HERE — the only consumer — and masked by the script; the review step
# that processes untrusted PR content never sees it. Remaining step env
# is the secret plus values that exist nowhere but the GitHub context —
# which enters through env, never interpolated into a script body.
env:
WEBHOOK_SECRET: ${{ secrets.DOC_ORCH_WEBHOOK_SECRET }}
RUN_TOKEN: ${{ github.event.client_payload.run_token || github.event.inputs.run_token || '' }}
JOB_STATUS: ${{ job.status }}
# Either step can degrade a run: the rules step (corpus unavailable)
# or the reviewer itself (trivial-diff skip, unresolvable PR base).
DEGRADED: ${{ steps.rules.outputs.degraded || steps.review.outputs.degraded }}
# Dispatch events carry no pull_request context — github.sha is the
# tip of the checked-out ref, so sweep runs record the commit too.
HEAD_SHA: ${{ github.event.pull_request.head.sha || github.sha }}
WF_RUN_ID: ${{ github.run_id }}
REPO_FULL: ${{ github.repository }}
PR_NUMBER: ${{ github.event.pull_request.number }}
# The ONE justified inline-bash exception beyond the bootstrap: when the
# report script itself never downloaded, no unified script exists to
# report the failure — so a minimal guarded curl posts it. Kept tiny on
# purpose; all real reporting logic stays in code-review-report.sh.
run: |
if [ ! -x /tmp/code-review-report.sh ]; then
echo "::error::code-review-report.sh missing — sending bootstrap-failure callback"
# Secrets NEVER ride argv (ps-visible): the bearer goes through a
# 0600 curl config file and RUN_TOKEN through jq's env — the same
# transport rule the report script itself follows.
CURL_CFG=$(mktemp) && chmod 600 "$CURL_CFG"
printf 'header = "Authorization: Bearer %s"\n' "$WEBHOOK_SECRET" > "$CURL_CFG"
jq -n --arg run_id "${RUN_ID:-}" \
--arg wf "$WF_RUN_ID" --arg repo "$REPO_FULL" --arg mode "$MODE" \
'{run_id: $run_id, run_token: ($ENV.RUN_TOKEN // ""), status: "failure",
degraded_reason: "bootstrap_failed", workflow_run_id: $wf,
repo_full_name: $repo, mode: $mode,
error_message: "Script bootstrap failed: the report script never downloaded from the hub."}' |
curl -sS --max-time 30 -K "$CURL_CFG" -X POST "$HUB_BASE_URL/api/code-review/webhook" \
-H "Content-Type: application/json" -d @- || true
rm -f "$CURL_CFG"
exit 1
fi
/tmp/code-review-report.sh