Skip to content

compat-check

compat-check #29

Workflow file for this run

name: compat-check
# Monitors upstream ACP wrapper packages (@agentclientprotocol/claude-agent-acp,
# @agentclientprotocol/codex-acp) for breaking changes. Runs daily and on
# manual dispatch. If a wrapper stops producing correct output (spawn + prompt
# smoke test fails), opens or updates a tracking GitHub Issue.
#
# To avoid burning API quota on unchanged versions, the check caches the last
# successfully-tested version of each wrapper (.compat-versions.json, persisted
# across runs via actions/cache). When the npm latest version matches the
# cached version, the expensive spawn+prompt is skipped (reported CACHED).
#
# Two ways to provide credentials (configure under Settings → Secrets → Actions):
#
# Option A — unified router gateway (recommended, single secret pair):
# UNIFIED_ROUTER_BASE_URL — e.g. https://your-router/v1
# UNIFIED_ROUTER_KEY — router API key
# CLAUDE_GATEWAY_MODEL — (optional) model for claude, e.g. glm-5.2
# CODEX_GATEWAY_MODEL — (optional) model for codex, defaults to deepseek-chat
#
# Option B — direct provider keys:
# ANTHROPIC_API_KEY — for the claude-agent-acp smoke test
# OPENAI_API_KEY — for the codex-acp smoke test (CODEX_API_KEY also works)
#
# Agents whose key is absent (and no gateway) are SKIPPED (not failed).
on:
schedule:
# Daily at 06:00 UTC (14:00 Beijing time).
- cron: "0 6 * * *"
workflow_dispatch:
permissions:
issues: write
jobs:
check:
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version: "1.24.x"
cache: true
- uses: actions/setup-node@v4
with:
node-version: "lts/*"
# Persist the version cache across runs so unchanged versions are not
# re-tested (saves API quota). The cache key is fixed so each run
# read+writes the same slot; only the file content evolves.
- name: Restore version cache
id: cache-restore
uses: actions/cache/restore@v4
with:
path: .compat-versions.json
key: compat-versions-cache
- name: Run compat check
id: check
env:
# Option A: unified router gateway (single secret pair covers both agents)
UNIFIED_ROUTER_BASE_URL: ${{ secrets.UNIFIED_ROUTER_BASE_URL }}
UNIFIED_ROUTER_KEY: ${{ secrets.UNIFIED_ROUTER_KEY }}
CLAUDE_GATEWAY_MODEL: ${{ secrets.CLAUDE_GATEWAY_MODEL }}
CODEX_GATEWAY_MODEL: ${{ secrets.CODEX_GATEWAY_MODEL }}
# Option B: direct provider keys (alternative to gateway)
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
run: |
# Capture output to a file so the issue-creation step can embed it.
set +e
go run ./cmd/acp-compat-check > compat-output.txt 2>&1
code=$?
echo "exit_code=$code" >> "$GITHUB_OUTPUT"
cat compat-output.txt
echo "---"
echo "exit code: $code"
exit 0 # always succeed here; the issue step decides what to do
# Save the (possibly updated) cache. run-always so a cache miss on the
# first run still populates it. save always succeeds even if the restore
# missed, because the file is written by the check when versions change.
- name: Save version cache
if: always()
uses: actions/cache/save@v4
with:
path: .compat-versions.json
key: compat-versions-cache
- name: Open or update regression Issue
if: steps.check.outputs.exit_code == '1'
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
set -euo pipefail
LABEL="compat-regression"
TITLE="[compat-check] ACP wrapper compatibility regression detected"
# Render the captured output as a fenced code block in the issue body.
BODY=$(cat <<'BODY_EOF'
A scheduled compatibility check detected that at least one ACP wrapper
stopped producing the expected output against this runtime. This usually
means an upstream wrapper release introduced a breaking change.
**Action required:** investigate the failing agent(s), pin or update the
wrapper, and re-run the check.
---
#### Check output
```text
BODY_EOF
)
BODY="${BODY}$(cat compat-output.txt)"
BODY="${BODY}"$'\n```\n\n'"CI run: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}"
# Check for an existing open issue with the same title to avoid dupes.
EXISTING=$(gh issue list --state open --search "${TITLE} in:title" --limit 1 --json number --jq '.[0].number' || true)
if [ -n "$EXISTING" ]; then
echo "Updating existing issue #$EXISTING with a new failure comment."
gh issue comment "$EXISTING" --body "## Regression still detected (${{ github.run_id }})
${BODY}"
else
echo "Opening new regression issue."
# Create the label if it does not exist (ignore error if it does).
gh label create "$LABEL" --description "Upstream ACP wrapper compatibility regression" --color D73A4A 2>/dev/null || true
gh issue create \
--title "${TITLE}" \
--body "${BODY}" \
--label "$LABEL"
fi
- name: Close stale regression Issue (check passed)
if: steps.check.outputs.exit_code == '0'
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
set -euo pipefail
TITLE="[compat-check] ACP wrapper compatibility regression detected"
EXISTING=$(gh issue list --state open --search "${TITLE} in:title" --limit 1 --json number --jq '.[0].number' || true)
if [ -n "$EXISTING" ]; then
echo "Check passed; closing resolved issue #$EXISTING."
gh issue comment "$EXISTING" --body "✅ Compatibility check passed on run ${{ github.run_id }}. Closing this issue."
gh issue close "$EXISTING"
else
echo "Check passed; no open regression issue to close."
fi