Skip to content

feat(liquidity-launcher-sdk): instant-launch pool shape — the 2026-08-05 redeploy mints at tick spacing 25 #855

feat(liquidity-launcher-sdk): instant-launch pool shape — the 2026-08-05 redeploy mints at tick spacing 25

feat(liquidity-launcher-sdk): instant-launch pool shape — the 2026-08-05 redeploy mints at tick spacing 25 #855

name: PR Review
# AI review using @uniswap/review-cli (private GitHub Packages).
#
# Replicates the setup running in Uniswap/universe and Uniswap/backend,
# matched to internal-tools' canonical template
# (packages/review-cli/templates/workflows/review.yml) in its 1.3.0+
# two-job shape: triage → review, with react/reply acknowledgements on
# comment triggers.
#
# Documentation: https://www.notion.so/uniswaplabs/New-AI-Review-System-356c52b2548b80a1a615c9692312e517
# Support: #pod-apps-infra (Slack) — owners of the review-cli rollout.
#
# Job split:
# triage — reads GITHUB_EVENT_PATH, decides run/skip, posts the 👀
# ack + "Reviewing now" threaded reply on comment triggers,
# and exports outputs (pr_number, trigger_source, note,
# commenter, thread_anchor, comment_id, reply_id) for review.
# review — checks out the PR head, runs the multi-agent pipeline,
# posts findings, then updates the ack reaction (✅/❌) and
# edits the threaded reply with the terminal verdict.
#
# Verb separation: `review` analyzes and persists; `review post` pushes
# the saved analysis to GitHub. Splitting the verbs means posting is
# the only verb that writes — `review` itself is structurally
# read-only (the AnalyzeDeps shape doesn't allow constructing a
# writer). Posts/resolves review threads idempotently across
# force-pushes; skips drafts; re-runs on every push.
#
# Triggers:
# pull_request — automatic on opened/synchronize/reopened/
# ready_for_review (the common case).
# issue_comment — fires when a PR comment contains
# `@request-claude-review`. Lets developers
# re-run the bot without pushing new commits.
# pull_request_review_comment — same trigger phrase on inline review
# comments. Anchors the steering note to a
# specific file:line.
# workflow_dispatch — manual fire from the Actions tab with a
# `pr_number` input.
#
# Event parsing lives in the cli: `triage --from-github-actions`
# resolves PR number + trigger source + steering note + commenter +
# thread anchor from `GITHUB_EVENT_PATH`. The workflow never shell-
# interpolates PR-author-controlled fields like `comment.body`;
# everything rides through the event JSON straight into JS strings.
on:
pull_request:
types: [opened, synchronize, reopened, ready_for_review]
issue_comment:
types: [created]
pull_request_review_comment:
types: [created]
workflow_dispatch:
inputs:
pr_number:
description: 'PR number to review'
required: true
type: string
concurrency:
# PR-number expression resolves at job-creation time, before any
# step runs. Each event surface exposes the PR number on a
# different path:
# pull_request / pull_request_review_comment → event.pull_request.number
# issue_comment → event.issue.number
# workflow_dispatch → inputs.pr_number
# The `||` chain picks the first non-empty value, so the
# concurrency key is stable across all event sources.
#
# Comment events that DON'T contain the trigger phrase get a
# per-run-unique group suffix so they can't cancel an in-flight
# `pull_request` run that's actually doing work. Without this,
# any `issue_comment` on a PR (bot chatter, generic discussion)
# fires a sibling workflow run that grabs the `review-<N>` group,
# cancels the in-flight review via `cancel-in-progress`, and then
# immediately skips itself via the job-level `if:` below.
# Concurrency is evaluated at run-creation time, BEFORE the job
# `if:`, so the skip doesn't save us.
group: >-
review-${{ github.event.pull_request.number || github.event.issue.number || inputs.pr_number }}${{
(github.event_name == 'issue_comment' || github.event_name == 'pull_request_review_comment')
&& !contains(github.event.comment.body, '@request-claude-review')
&& format('-skip-{0}', github.run_id)
|| ''
}}
cancel-in-progress: true
# Permissions are scoped per-job. Workflow-level permissions trip
# zizmor's `excessive-permissions` rule — the triage job only needs
# read + reaction-write; only the review job needs contents:write for
# the resolveReviewThread GraphQL mutation.
jobs:
triage:
name: Triage
runs-on: ubuntu-latest
# Coarse event-level gate — cheap early-exit so untrusted commenters
# or unrelated comments don't even spin up the runner. The cli's
# `triage --from-github-actions` step below is the authoritative
# policy lookup (draft skip, association threshold, bot self-mention
# guard, trigger-phrase parse); this `if:` just avoids paying the
# runner-startup cost for events that obviously don't apply.
if: |
(github.event_name == 'pull_request' && !github.event.pull_request.draft) ||
(github.event_name == 'issue_comment'
&& github.event.issue.pull_request != null
&& contains(github.event.comment.body, '@request-claude-review')
&& github.event.comment.user.type != 'Bot'
&& contains(fromJSON('["OWNER","MEMBER","COLLABORATOR"]'), github.event.comment.author_association)) ||
(github.event_name == 'pull_request_review_comment'
&& contains(github.event.comment.body, '@request-claude-review')
&& github.event.comment.user.type != 'Bot'
&& contains(fromJSON('["OWNER","MEMBER","COLLABORATOR"]'), github.event.comment.author_association)) ||
github.event_name == 'workflow_dispatch'
permissions:
contents: read # checkout for the local install_review_cli action
packages: read # read @uniswap/review-cli from GH packages
issues: write # 👀 reaction on issue_comment
pull-requests: write # 👀 reaction on pull_request_review_comment
outputs:
run: ${{ steps.gate.outputs.run }}
pr_number: ${{ steps.gate.outputs.pr_number }}
comment_id: ${{ steps.gate.outputs.comment_id }}
trigger_source: ${{ steps.gate.outputs.trigger_source }}
note: ${{ steps.gate.outputs.note }}
commenter: ${{ steps.gate.outputs.commenter }}
thread_anchor: ${{ steps.gate.outputs.thread_anchor }}
# Threaded-reply id (when a running-state reply was posted in this
# job). The Post step uses it to PATCH the reply with the terminal
# verdict. Empty when the trigger wasn't a comment (push-driven
# runs have no comment to reply to).
reply_id: ${{ steps.post_reply.outputs.id }}
steps:
# Minimal checkout — the Install review-cli step below uses the
# local composite action at .github/actions/install_review_cli,
# so action.yml needs to be on disk. fetch-depth: 1 is enough
# (no git history is consulted in triage). `persist-credentials:
# false` because nothing in this job pushes or fetches from git.
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
fetch-depth: 1
persist-credentials: false
- name: Install review-cli
id: install-review-cli
uses: ./.github/actions/install_review_cli
with:
# `vars.REVIEW_CLI_VERSION` is the authoritative pin; update
# it in repo settings (Secrets and variables → Actions →
# Variables) to roll forward without a commit. The fallback
# below only matters if the variable is unset or deleted.
# Must be >= 1.4.3: earlier versions leave a transitive
# @modelcontextprotocol/server dependency unpinned, so a
# fresh `bun add` resolves a newer build whose exports
# changed and the CLI crashes at load ("Export named
# 'StdioServerTransport' not found"). Never `@latest` —
# would silently absorb upstream changes mid-PR.
version: ${{ vars.REVIEW_CLI_VERSION || '1.6.0' }}
github-token: ${{ secrets.GITHUB_TOKEN }}
# Gate — `review-cli triage --from-github-actions` reads
# GITHUB_EVENT_NAME + GITHUB_EVENT_PATH itself and writes the
# decision to GITHUB_OUTPUT. No user-controlled data ever
# touches the shell. `--skip-config` because we haven't checked
# out the PR head yet; the cli's built-in defaults cover the
# common cases.
- name: Decide whether to run
id: gate
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
REVIEW_CLI_BIN: ${{ steps.install-review-cli.outputs.bin-path }}
run: '"$REVIEW_CLI_BIN/review-cli" triage --from-github-actions --skip-config'
# Acknowledge a comment trigger with 👀. Only fires when the
# gate accepted the comment — bot-self-mentions, untrusted
# commenters, and missing-trigger-phrase comments don't get the
# ack (the comment id is empty in those cases).
- name: Acknowledge comment trigger (👀)
if: |
steps.gate.outputs.run == 'true' &&
steps.gate.outputs.comment_id != ''
continue-on-error: true
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GH_REPO: ${{ github.repository }}
COMMENT_ID: ${{ steps.gate.outputs.comment_id }}
EVENT_NAME: ${{ github.event_name }}
REVIEW_CLI_BIN: ${{ steps.install-review-cli.outputs.bin-path }}
run: |
"$REVIEW_CLI_BIN/review-cli" react \
--repo "$GH_REPO" \
--comment-id "$COMMENT_ID" \
--event "$EVENT_NAME" \
--reaction ack
# Post a "Reviewing now" reply right under the trigger comment so
# the requester gets visible feedback while the review job spins
# up. The reaction (👀) is glanceable; this reply is the
# responsive one — it carries the run URL so the requester can
# click through to logs without scrolling back to the sticky.
# Best-effort: a failed post doesn't break the run, the worst
# case is silence until the terminal ✅/❌ reaction.
- name: Post running-state reply
id: post_reply
if: |
steps.gate.outputs.run == 'true' &&
steps.gate.outputs.comment_id != ''
continue-on-error: true
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GH_REPO: ${{ github.repository }}
PR_NUMBER: ${{ steps.gate.outputs.pr_number }}
COMMENT_ID: ${{ steps.gate.outputs.comment_id }}
EVENT_NAME: ${{ github.event_name }}
RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
REPLY_BODY: |
↻ **Reviewing now** · [view run ↗](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})
This comment will update when the review completes. Findings will appear in the sticky summary above.
REVIEW_CLI_BIN: ${{ steps.install-review-cli.outputs.bin-path }}
run: |
OUT="$RUNNER_TEMP/reply-id.txt"
"$REVIEW_CLI_BIN/review-cli" reply \
--repo "$GH_REPO" \
--pr "$PR_NUMBER" \
--event "$EVENT_NAME" \
--in-reply-to "$COMMENT_ID" \
--body "$REPLY_BODY" \
--out-id "$OUT"
if [ -s "$OUT" ]; then
echo "id=$(cat "$OUT")" >> "$GITHUB_OUTPUT"
fi
review:
name: AI review
needs: triage
if: needs.triage.outputs.run == 'true'
runs-on: ubuntu-latest
# `contents: write` (not `read`) is required for the GraphQL
# `resolveReviewThread` mutation — counterintuitive but documented.
# `pull-requests: write` alone returns "Resource not accessible by
# integration". https://github.com/orgs/community/discussions/44650
#
# review-cli doesn't push to git (`persist-credentials: false` on
# checkout). The elevated content scope is purely for the
# review-thread resolve mutation.
permissions:
contents: write
packages: read # pull @uniswap/review-cli
pull-requests: write # post review comments + resolve threads
issues: write # comment reactions + sticky on PRs
# Hard cap: agent_budget_usd in .claude/review.yml is the soft cap;
# this is the safety net if the CLI hangs.
timeout-minutes: 20
env:
# Exposes "is the DD key configured?" as a job-level env so the
# optional DD CI Visibility step can gate on it. `secrets.*` can't
# be referenced directly from a step-level `if:`, and step-level
# `env:` isn't applied before `if:` is evaluated — but job-level
# env IS, so this is the standard workaround.
HAS_DATADOG_API_KEY: ${{ secrets.DD_API_KEY != '' }}
steps:
# Pin the checkout to the PR head. Required for comment-trigger
# variants — `issue_comment` and `pull_request_review_comment`
# events default GITHUB_REF to the repo's default branch, so an
# un-pinned checkout would run the review against main and skip
# on "all hunks identical". The triage job already extracted
# pr_number from the event payload; reuse it here so the review
# runs against the PR's actual code regardless of which event
# triggered.
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
# Full history so review-cli can diff against the PR base.
fetch-depth: 0
persist-credentials: false
ref: refs/pull/${{ needs.triage.outputs.pr_number }}/head
# NOTE: deliberately not running the monorepo's own bun install.
# The install_review_cli action below sets up Bun pinned to
# review-cli's engines requirement and installs the cli into an
# isolated $RUNNER_TEMP dir. A repo-wide install would be wasted
# work, since review-cli reads the diff via `gh pr diff` and
# never imports from the repo's node_modules.
- name: Install review-cli
id: install-review-cli
uses: ./.github/actions/install_review_cli
with:
version: ${{ vars.REVIEW_CLI_VERSION || '1.6.0' }}
github-token: ${{ secrets.GITHUB_TOKEN }}
# Fail fast if the OAuth token is missing — without it
# agent-query-factory falls back to a mock, which is confusing.
# We deliberately only forward CLAUDE_CODE_OAUTH_TOKEN (subscription
# auth, no metered billing) — never ANTHROPIC_API_KEY.
- name: Verify Claude OAuth token present
env:
CLAUDE_CODE_OAUTH_TOKEN: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
run: |
if [ -z "$CLAUDE_CODE_OAUTH_TOKEN" ]; then
echo "::error::Set CLAUDE_CODE_OAUTH_TOKEN as a repo secret to enable AI review."
exit 1
fi
# Install the Claude Code native binary.
#
# As of @anthropic-ai/claude-agent-sdk@0.2.113 the SDK spawns a
# per-platform native binary shipped via optional deps. Bun on Linux
# installs the package metadata for every variant but only extracts the
# tarball matching the host — and the SDK's resolver probes
# `linux-<arch>-musl` first, so `require.resolve` returns a path to a
# binary that doesn't exist on disk and the SDK errors out with
# "Claude Code native binary not found at .../musl/claude".
#
# Sidestep the optional-dep dance by installing the binary via the
# official script and pointing the SDK at it via
# CLAUDE_CODE_EXECUTABLE_PATH (honored by review-cli).
#
# SECURITY: this is the one un-pinned external fetch in the
# workflow. SHA-pinning `curl|bash` doesn't help here — the
# installer is a per-host bootstrapper that selects a binary for
# the runner's libc/arch at install time, so a fixed hash on a
# multi-target script provides no integrity guarantee for the
# binary that actually lands on disk. Pinning the hash of
# `install.sh` itself isn't worth the operational cost either —
# Anthropic rotates the bootstrapper independently of the binary
# versions, so the hash would churn without correlating to
# meaningful supply-chain events. Compensating controls:
# - CLAUDE_CODE_OAUTH_TOKEN is a subscription-billed token —
# not metered API spend — and is scoped to Claude Code only.
# - We never forward ANTHROPIC_API_KEY to this job.
# The asymmetry with the rest of the hash-pinned workflow is
# intentional, not an oversight.
- name: Install Claude Code binary
run: |
set -euo pipefail
curl -fsSL https://claude.ai/install.sh | bash
for candidate in "$HOME/.local/bin/claude" "$HOME/.claude/bin/claude" "$HOME/.npm-global/bin/claude"; do
if [ -x "$candidate" ]; then
CLAUDE_BIN="$candidate"; break
fi
done
if [ -z "${CLAUDE_BIN:-}" ]; then
CLAUDE_BIN="$(command -v claude || true)"
fi
if [ -z "$CLAUDE_BIN" ] || [ ! -x "$CLAUDE_BIN" ]; then
echo "::error::claude binary not found after install"; exit 1
fi
echo "Installed claude at: $CLAUDE_BIN"
echo "CLAUDE_CODE_EXECUTABLE_PATH=$CLAUDE_BIN" >> "$GITHUB_ENV"
echo "$(dirname "$CLAUDE_BIN")" >> "$GITHUB_PATH"
# Pre — running sticky placeholder so the PR shows in-progress
# state from t=0. Best-effort: if this fails Post creates the
# sticky from scratch.
- name: Pre — running sticky placeholder
# Best-effort: a flaky GitHub API blip on the placeholder
# upsert must not skip Analyze. Without `continue-on-error`, a
# non-zero exit here would short-circuit the rest of the job
# (default `if: success()` on subsequent steps), and Post
# would have no `last-run.json` to recover from.
continue-on-error: true
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GH_REPO: ${{ github.repository }}
PR_NUMBER: ${{ needs.triage.outputs.pr_number }}
REVIEW_CLI_BIN: ${{ steps.install-review-cli.outputs.bin-path }}
run: '"$REVIEW_CLI_BIN/review-cli" post "$PR_NUMBER" --repo "$GH_REPO" --pre'
# Analyze — reads the diff, runs the agents, persists
# `~/.review-cli/<owner>/<repo>/pr-<n>/last-run.json`. Never
# writes to GitHub — but it READS the diff via `gh pr diff`,
# which needs GITHUB_TOKEN. Without it, gh fails silently, the
# diff comes back empty, and the run short-circuits with
# "PR has no changes".
#
# Trigger source + steering note + commenter + thread anchor
# flow from the triage job's outputs via env vars (NOT shell-
# interpolated) so a malicious comment body can't break out
# into shell. The thread anchor is JSON-encoded.
- name: Analyze
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
CLAUDE_CODE_OAUTH_TOKEN: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
REVIEW_NOTE: ${{ needs.triage.outputs.note }}
REVIEW_TRIGGER_SOURCE: ${{ needs.triage.outputs.trigger_source }}
REVIEW_COMMENTER: ${{ needs.triage.outputs.commenter }}
REVIEW_THREAD_ANCHOR: ${{ needs.triage.outputs.thread_anchor }}
# Pin where the cli writes the run artifact so the Upload
# and DD CI Stamp steps below can read it back from
# `${{ runner.temp }}` (cli default is `./` which would
# silently drop both downstream consumers).
REVIEW_CLI_ARTIFACT_PATH: ${{ runner.temp }}/review-cli-output.json
# Per-agent cost record stream — picked up by the
# agent-scorecard aggregator (Uniswap/internal-tools)
# alongside the existing review-feedback engagement artifact.
REVIEW_CLI_TOKENS_ARTIFACT_PATH: ${{ runner.temp }}/agent-tokens.jsonl
# Datadog telemetry the cli emits during the run (synthesis
# + finding events, run cost / duration). Required for npm-
# installed consumers — the published binary is what
# `bunx --bun @uniswap/review-cli` runs from, and that's the
# SOURCE TypeScript (no `--define` bake). Only the standalone
# GitHub-Releases binary has the build-time bake. Without
# this env passthrough the DD client is a silent no-op.
DD_API_KEY: ${{ secrets.DD_API_KEY }}
DD_SITE: ${{ vars.DATADOG_SITE || 'datadoghq.com' }}
GH_REPO: ${{ github.repository }}
PR_NUMBER: ${{ needs.triage.outputs.pr_number }}
REVIEW_CLI_BIN: ${{ steps.install-review-cli.outputs.bin-path }}
run: |
# Bash-array build for the optional flags. ${VAR:+--flag "$VAR"}
# would word-split after the expansion (the inner quotes are
# literal text in the result, not shell syntax), turning a
# multi-word note into multiple positional args and erroring
# out the cli.
args=()
[ -n "$REVIEW_NOTE" ] && args+=(--note "$REVIEW_NOTE")
[ -n "$REVIEW_COMMENTER" ] && args+=(--commenter "$REVIEW_COMMENTER")
[ -n "$REVIEW_THREAD_ANCHOR" ] && args+=(--thread-anchor "$REVIEW_THREAD_ANCHOR")
"$REVIEW_CLI_BIN/review-cli" review "$PR_NUMBER" \
--repo "$GH_REPO" \
--trigger-source "$REVIEW_TRIGGER_SOURCE" \
"${args[@]}"
# Post — reads `last-run.json` and pushes findings, resolves
# threads, upserts the sticky. Always run after Analyze, even
# on failure, so Post can recover the most-recent saved state.
- name: Post
if: success() || failure()
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GH_REPO: ${{ github.repository }}
PR_NUMBER: ${{ needs.triage.outputs.pr_number }}
REVIEW_CLI_BIN: ${{ steps.install-review-cli.outputs.bin-path }}
run: '"$REVIEW_CLI_BIN/review-cli" post "$PR_NUMBER" --repo "$GH_REPO"'
- name: Upload run artifact
# Run even when review-cli failed — the artifact still has
# whatever events it captured up to the failure, which is
# the most valuable thing you can read post-hoc.
if: always()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: review-cli-run-${{ needs.triage.outputs.pr_number }}-${{ github.run_attempt }}
path: ${{ runner.temp }}/review-cli-output.json
if-no-files-found: warn
retention-days: 30
# Per-run agent cost record stream — name MUST be exactly
# `agent-tokens` so the agent-scorecard aggregator finds it
# across consumer repos with one `gh run download` call.
- name: Upload agent-tokens artifact
if: always()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: agent-tokens
path: ${{ runner.temp }}/agent-tokens.jsonl
if-no-files-found: ignore
retention-days: 30
overwrite: true
# Optional — stamp the CI Visibility pipeline span with the review
# outcome so platform engineers looking at the GH Actions run in
# Datadog's CI page can filter by verdict / team / depth. This is
# additive: review-cli already ships rich metrics + logs via HTTPS;
# this just makes the CI page itself useful (team filtering on the
# CI page requires a `team:` tag on the pipeline span).
#
# Configure via repo secrets / vars:
# secrets.DD_API_KEY — required; absence skips this step
# vars.DATADOG_SITE — optional, defaults to datadoghq.com
# vars.REVIEW_DD_TEAM — optional, the DD Team slug to tag
#
# `if:` gates on `env.HAS_DATADOG_API_KEY`, computed at job level
# from `secrets.DD_API_KEY`. `secrets.*` isn't a valid named
# value in step-level `if:` (the workflow won't even parse), and
# step-level `env:` isn't applied before `if:` is evaluated —
# job-level env IS, hence the indirection.
- name: Stamp DD CI Visibility with review outcome
if: always() && env.HAS_DATADOG_API_KEY == 'true'
env:
DATADOG_API_KEY: ${{ secrets.DD_API_KEY }}
DATADOG_SITE: ${{ vars.DATADOG_SITE || 'datadoghq.com' }}
REVIEW_TEAM: ${{ vars.REVIEW_DD_TEAM || '' }}
ARTIFACT: ${{ runner.temp }}/review-cli-output.json
run: |
set -uo pipefail
if [ ! -s "$ARTIFACT" ]; then
echo "no review-cli artifact at $ARTIFACT — skipping DD CI tagging"
exit 0
fi
VERDICT=$(jq -r '.review.verdict // "unknown"' "$ARTIFACT")
DEPTH=$(jq -r '.review.depth // "unknown"' "$ARTIFACT")
FINDINGS=$(jq -r '(.review.findings // []) | length' "$ARTIFACT")
# `review.clean`, not `review.skipped`. The artifact has no
# pipeline-skip signal — review-cli emits `review.run.skipped`
# only as a metric event when the run is actually skipped
# (e.g. trivial-diff guard fired), and that path doesn't write
# an artifact at all. APPROVE ∧ zero findings means the run
# completed cleanly, not that it was skipped. Tagging it as
# `skipped` here conflates the two — particularly here, where
# `trivial_threshold: 0` in .claude/review.yml disables the
# size-skip path entirely, so every artifact is from a real
# review.
CLEAN=$([ "$FINDINGS" = 0 ] && [ "$VERDICT" = "APPROVE" ] && echo true || echo false)
TAGS=(
--tags "service:review-cli"
--tags "review.verdict:$VERDICT"
--tags "review.depth:$DEPTH"
--tags "review.clean:$CLEAN"
)
if [ -n "$REVIEW_TEAM" ]; then
TAGS+=(--tags "team:$REVIEW_TEAM")
fi
bunx @datadog/datadog-ci@5.15.0 tag --level pipeline "${TAGS[@]}" \
|| echo "datadog-ci tag failed (non-fatal)"
bunx @datadog/datadog-ci@5.15.0 measure --level pipeline \
--measures "review.findings:$FINDINGS" \
|| echo "datadog-ci measure failed (non-fatal)"
# Swap the 👀 ack for ✅/❌ on comment-triggered runs.
# `--clear-prior-ack` removes the eyes reaction first so the
# comment ends up with one terminal signal.
- name: Update reaction (✅ / ❌) on comment trigger
if: |
always() &&
needs.triage.outputs.comment_id != ''
continue-on-error: true
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
OUTCOME: ${{ job.status }}
GH_REPO: ${{ github.repository }}
COMMENT_ID: ${{ needs.triage.outputs.comment_id }}
EVENT_NAME: ${{ github.event_name }}
REVIEW_CLI_BIN: ${{ steps.install-review-cli.outputs.bin-path }}
run: |
REACTION=$([ "$OUTCOME" = "success" ] && echo "success" || echo "failure")
"$REVIEW_CLI_BIN/review-cli" react \
--repo "$GH_REPO" \
--comment-id "$COMMENT_ID" \
--event "$EVENT_NAME" \
--reaction "$REACTION" \
--clear-prior-ack
# Edit the running-state reply with the terminal verdict so the
# requester sees responsive feedback without scrolling back up to
# the sticky. Only fires when the triage step posted a reply
# (reply_id non-empty) AND the trigger was a comment.
- name: Update threaded reply with terminal state
if: |
always() &&
needs.triage.outputs.reply_id != ''
continue-on-error: true
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
OUTCOME: ${{ job.status }}
GH_REPO: ${{ github.repository }}
REPLY_ID: ${{ needs.triage.outputs.reply_id }}
EVENT_NAME: ${{ github.event_name }}
RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
REVIEW_CLI_BIN: ${{ steps.install-review-cli.outputs.bin-path }}
run: |
if [ "$OUTCOME" = "success" ]; then
BODY="✅ **Reviewed** · [view run ↗](${RUN_URL}) · scroll up for the full summary."
else
BODY="⚠ **Review failed** · [view run ↗](${RUN_URL}) · scroll up for the partial state, or comment \`@request-claude-review\` to retry."
fi
"$REVIEW_CLI_BIN/review-cli" reply \
--repo "$GH_REPO" \
--event "$EVENT_NAME" \
--edit-reply "$REPLY_ID" \
--body "$BODY"