Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
90 commits
Select commit Hold shift + click to select a range
116b70e
chore: prepare v3.68.0 release (#875)
taltas Jul 11, 2026
61f0b7f
chore(deps): update dependency json-stream-stringify to v3.1.7 (#807)
renovate[bot] Jul 11, 2026
5751a16
chore(deps): update dependency reconnecting-eventsource to v1.6.5 (#808)
renovate[bot] Jul 11, 2026
01c1c99
feat(openai): add GPT-5.6 family (sol, terra, luna) support (#876)
navedmerchant Jul 11, 2026
4c9fd5b
chore(deps): update dependency remove-markdown to v0.6.4 (#809)
renovate[bot] Jul 11, 2026
f07ae73
chore(deps): update dependency source-map to v0.7.6 (#811)
renovate[bot] Jul 11, 2026
0d8c6fb
chore(deps): update dependency sanitize-filename to v1.6.4 (#810)
renovate[bot] Jul 11, 2026
a441e02
chore(deps): update dependency tree-sitter-wasms to v0.1.13 (#812)
renovate[bot] Jul 11, 2026
9e218a5
fix(ci): reconcile labels against latest check run per name (#270)
roomote[bot] Jul 13, 2026
7d0879b
fix(terminal): fix output loss on cold terminals by moving read() int…
edelauna Jul 14, 2026
e482d11
feat(kenari): add Kenari as a first-class provider (#793)
doedja Jul 15, 2026
c9ac484
chore(deps): update dependency tsup to v8.5.1 (#813)
renovate[bot] Jul 15, 2026
0566607
fix(deps): update dependency @vscode/codicons to ^0.0.45 (#817)
renovate[bot] Jul 15, 2026
b29af3b
chore(deps): update dependency zod-to-json-schema to v3.25.2 (#814)
renovate[bot] Jul 15, 2026
b3bfdc6
chore(deps): update dependency @changesets/cli to v2.31.0 (#819)
renovate[bot] Jul 15, 2026
042a3ab
fix(deps): update ai sdks and providers (#816)
renovate[bot] Jul 15, 2026
50801da
fix(deps): update frontend and ui stack (#818)
renovate[bot] Jul 15, 2026
e07d776
fix(zoo-gateway): enable image attach when model supports vision (#897)
JamesRobert20 Jul 15, 2026
db2a991
chore(deps): update dependency @copilotkit/aimock to v1.35.0 (#820)
renovate[bot] Jul 15, 2026
ce39ee5
[Chore] Add weekly Discord release reminder (#900)
zoomote[bot] Jul 15, 2026
f5077cc
chore(deps): update dependency zustand to v5.0.14 (#815)
renovate[bot] Jul 15, 2026
5959abf
chore(deps): update dependency @eslint/js to v9.39.4 (#822)
renovate[bot] Jul 16, 2026
d25926c
chore(deps): update dependency @next/eslint-plugin-next to v15.5.20 (…
renovate[bot] Jul 16, 2026
dba8f8b
chore(deps): update dependency @dotenvx/dotenvx to v1.75.1 (#821)
renovate[bot] Jul 16, 2026
e6f2a0e
chore(deps): update dependency date-fns to v4.4.0 (#828)
renovate[bot] Jul 16, 2026
4a40c69
chore(deps): update dependency @vscode/ripgrep to v1.18.0 (#826)
renovate[bot] Jul 16, 2026
1a46e05
chore(deps): update dependency @tanstack/react-query to v5.101.2 (#824)
renovate[bot] Jul 16, 2026
f645f02
chore(deps): update dependency csstype to v3.2.3 (#827)
renovate[bot] Jul 16, 2026
c39535e
chore(deps): update dependency @trpc/client to v11.18.0 (#825)
renovate[bot] Jul 16, 2026
537cede
feat: add Grok 4.5 model support and fix xAI reasoning format (#867)
navedmerchant Jul 16, 2026
367013f
feat(webview): surface condense button and context progress bar in co…
awschmeder Jul 17, 2026
3a4006a
chore: prepare v3.70.0 release (#930)
edelauna Jul 18, 2026
5f582a5
feat(api): add CompletePromptOptions parameter to completePrompt meth…
easonLiangWorldedtech Jul 18, 2026
c322f3c
ci(code-qa): upload coverage lcov reports to GitHub artifacts (#939)
zoomote[bot] Jul 18, 2026
6e05ae9
fix(task-history): route invalidate() and invalidateAll() through wit…
morgan-coded Jul 19, 2026
8c17b41
test(task-lifecycle): add regression coverage for issue #566 (interru…
edelauna Jul 20, 2026
45968c0
fix(webview): dismiss welcome after Zoo Gateway sign-in (#961) (#962)
JamesRobert20 Jul 21, 2026
1b372ce
feat: add MiniMax-M3 model (#946)
zoomote[bot] Jul 21, 2026
b132a6b
feat: enable image support for DeepSeek V4 models (#963)
grizmin Jul 21, 2026
f2bdcb6
feat(moonshot): implement provider with model picker, API fetching, s…
grizmin Jul 21, 2026
3024768
feat(gemini): add Gemini 3.6 Flash support (#975)
zoomote[bot] Jul 21, 2026
cc2588b
fix(litellm): preserve reasoning_content for known reasoning model fa…
daewoongoh Jul 21, 2026
71a58a1
chore(deps): update dependency esbuild-wasm to v0.28.1 (#829)
renovate[bot] Jul 22, 2026
badb82c
fix(webview): sync cachedState on mode changes in SettingsView (#925)
easonLiangWorldedtech Jul 22, 2026
c6279d8
Fix codebase indexing for plain text files (#938)
WebMad Jul 22, 2026
a7d7968
fix(mcp): remove obsolete server creation translations (#943)
WebMad Jul 22, 2026
c24a8c5
feat(types): centralize provider identifiers (#952)
WebMad Jul 22, 2026
ae89dbd
feat(subtasks): abandon interrupted subtask — sever parent-child link…
edelauna Jul 23, 2026
7af1a8a
refactor(types): use canonical provider identifiers in categories (#989)
WebMad Jul 23, 2026
6fec173
[Feat] Add Kimi K3 model to Moonshot and OpenCode Go providers (#996)
zoomote[bot] Jul 24, 2026
78c1410
[Feat] Add Kimi Code provider with OAuth device flow (#945)
taltas Jul 24, 2026
e0e7fb9
[Fix] GPT-5.6 Luna requests rejected by Codex backend when using Chat…
taltas Jul 24, 2026
2634d5c
Add Dart support to codebase indexing (#941)
WebMad Jul 24, 2026
d5a8c4a
feat: implement Claude Opus 5 support across all providers (#1010)
zoomote[bot] Jul 24, 2026
d1f3999
Release v3.72.0 (#1013)
navedmerchant Jul 25, 2026
116acfd
[Fix] Flaky mocked e2e subtasks test when parent resume is served the…
zoomote[bot] Jul 25, 2026
933ee98
fix(settings): buffer Save-managed settings in cachedState until Save…
JunyongParkDev Jul 25, 2026
2d2a238
fix(ollama): always respond to model refresh, surface errors, and use…
navedmerchant Jul 25, 2026
4e9543f
Fix bedrock DNS resolution when behind a corporate proxy (#906)
LouisClt Jul 25, 2026
e479b7e
chore: update Node.js to v22 LTS (22.23.1) (#743)
renovate[bot] Jul 25, 2026
dfb8d69
fix(architect): instruct plans directory as workspace-relative ./plan…
zoomote[bot] Jul 25, 2026
ff17dcb
feat(TaskRegistry): introduce TaskRegistry and migrate clineStack (#1…
edelauna Jul 25, 2026
66120b4
chore(deps): update dependency shell-quote to v1.9.0 [security] (#986)
renovate[bot] Jul 26, 2026
1ceb4e6
test(webview): add Playwright visual regression harness (#526)
edelauna Jul 26, 2026
772e716
refactor(api): use canonical provider identifiers (#1012)
WebMad Jul 26, 2026
c52f118
refactor(types): use canonical identifiers for default models (#991)
WebMad Jul 26, 2026
2db2af0
refactor(api): use canonical model cache provider identifiers (#1020)
WebMad Jul 26, 2026
7710513
refactor(shared): use canonical profile provider identifiers (#1019)
WebMad Jul 26, 2026
782e28e
refactor(core): use canonical provider identifiers (#1022)
WebMad Jul 26, 2026
d27153a
fix(ripgrep): support @vscode/ripgrep >=1.18 platform-package layout …
edelauna Jul 26, 2026
488732e
feat(friendli): add reasoning params support (#886)
Lee-Si-Yoon Jul 28, 2026
85f6f27
chore(lint): add core/webview to no-floating-promises ratchet (#950)
morgan-coded Jul 29, 2026
ded7575
fix(branding): replace remaining user-facing "Roo" strings with "Zoo"…
rrewll Jul 29, 2026
2c987fc
[Fix] Zoo prompts about command output when short foreground commands…
zoomote[bot] Jul 29, 2026
1ae8b5b
feat(TaskScheduler): introduce semaphore-based task scheduler (Epic 3…
edelauna Jul 29, 2026
dcaa3cb
[Chore] Harden E2E workflow against VS Code binary download failures …
zoomote[bot] Jul 29, 2026
088b7ea
refactor(webview): use canonical provider identifiers (#1023)
WebMad Jul 30, 2026
c378193
refactor: finish canonical provider identifier audit (#1030)
WebMad Jul 30, 2026
569b43d
refactor(api): centralize service-tier primitives (#1040)
WebMad Jul 30, 2026
cd29243
fix(openai-compatible): add max reasoning effort option (#882) (#1051)
ivanarifin Jul 31, 2026
5971caa
feat(openai-codex): persist and send Fast priority mode (#1063)
WebMad Jul 31, 2026
eace967
[Fix] Subtask e2e suite can inherit a cancelled delayed mock stream f…
zoomote[bot] Jul 31, 2026
ca9b60f
fix(router-provider): fetch model metadata before context management …
JamesRobert20 Jul 31, 2026
19bbfdf
chore: prepare v3.74.0 release
taltas Jul 31, 2026
498a71f
docs: announce Zoo Gateway in v3.74.0 README
taltas Jul 31, 2026
b5d405e
docs: add Zoo Gateway announcement to localized v3.74.0 READMEs
taltas Jul 31, 2026
6c23a1c
fix: update Announcement spec to v3.74.0 and unindent README details …
roomote Jul 31, 2026
60f056b
Merge upstream v3.74.0 into local/daily-driver
Aug 1, 2026
6c186ff
test(providers): assert Opus 5 effort-shape (not budget/binary)
Aug 1, 2026
ba8a4c2
docs(fork): record the v3.74.0 sync
Aug 1, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
5 changes: 5 additions & 0 deletions .changeset/itchy-moles-thank.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"zoo-code": patch
---

Fix bedrock DNS resolution when behind corporate proxy
2 changes: 1 addition & 1 deletion .github/actions/setup-node-pnpm/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ inputs:
node-version:
description: "Node.js version to use"
required: false
default: "20.20.2"
default: "22.23.1"
pnpm-version:
description: "pnpm version to use"
required: false
Expand Down
27 changes: 24 additions & 3 deletions .github/pull_request_template.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,14 +38,35 @@ Detail the steps to test your changes. This helps reviewers verify your work.
- [ ] **Scope**: My changes are focused on the linked issue (one major feature/fix per PR).
- [ ] **Self-Review**: I have performed a thorough self-review of my code.
- [ ] **Testing**: New and/or updated tests have been added to cover my changes (if applicable).
- [ ] **Visual Snapshot** (UI changes only): If a user would notice this change at a glance (layout, theme tokens, brand elements, empty/error states), I've added or updated a `*.visual.tsx` snapshot in `webview-ui/`. See `webview-ui/AGENTS.md` → "When a UI change needs a snapshot".
- [ ] **Documentation Impact**: I have considered if my changes require documentation updates (see "Documentation Updates" section below).
- [ ] **Contribution Guidelines**: I have read and agree to the [Contributor Guidelines](/CONTRIBUTING.md).

### Screenshots / Videos
### Visual Snapshots

<!--
For UI changes, please provide before-and-after screenshots or a short video of the *actual results*.
This greatly helps in understanding the visual impact of your changes.
For UI changes to static rendered state, the primary artifact is a committed
Playwright CT snapshot (`*.visual.tsx` in `webview-ui/`) — that baseline
becomes durable regression coverage for the surface. See
`webview-ui/AGENTS.md` for what deserves a snapshot.

Before/after screenshots pasted here are welcome as a review aid but are not a
substitute for the committed baseline. If a snapshot is possible, prefer the
snapshot.
-->

### Videos (interaction / animation only)

<!--
Snapshots cannot capture motion or multi-step flows. Attach a short screen
recording here when reviewers need to see:
- A new interactive flow (dropdown, form, dialog progression)
- Animation, transition, or timing behavior
- A regression that only manifests during interaction

Videos are a review aid, not regression coverage. If the *result* of the
interaction has a distinct rendered state worth protecting, still commit a
`*.visual.tsx` snapshot of that end-state alongside the video.
-->

### Documentation Updates
Expand Down
29 changes: 25 additions & 4 deletions .github/workflows/code-qa.yml
Original file line number Diff line number Diff line change
Expand Up @@ -135,25 +135,32 @@ jobs:
# lanes for behavioral confidence, but duplicating coverage uploads
# there mostly adds Codecov overhead without changing pass/fail
# behavior.
# Coverage is uploaded in three separate steps so each LCOV gets the
# Coverage is uploaded in separate steps so each LCOV gets the
# correct flag set. Codecov double-counts overlapping lines when a
# single upload carries multiple flags whose paths overlap, so the
# two core lanes (which both cover packages/core/src/**) must be
# uploaded individually with their own lane flag.
# core lanes and webview lane must be uploaded individually with
# their own flag.
# See https://docs.codecov.com/docs/flags
- name: Upload non-core coverage to Codecov
if: matrix.upload-coverage
uses: codecov/codecov-action@b9fd7d16f6d7d1b5d2bec1a2887e65ceed900238 # v4
with:
files: >-
src/coverage/lcov.info,
webview-ui/coverage/lcov.info,
packages/cloud/coverage/lcov.info,
packages/telemetry/coverage/lcov.info,
apps/cli/coverage/lcov.info
disable_search: true
flags: ${{ matrix.codecov-flag }}
token: ${{ secrets.CODECOV_TOKEN }}
- name: Upload webview JSDOM coverage to Codecov
if: matrix.upload-coverage
uses: codecov/codecov-action@b9fd7d16f6d7d1b5d2bec1a2887e65ceed900238 # v4
with:
files: webview-ui/coverage/lcov.info
disable_search: true
flags: webview-ui
token: ${{ secrets.CODECOV_TOKEN }}
- name: Upload core unit coverage to Codecov
if: matrix.upload-coverage
uses: codecov/codecov-action@b9fd7d16f6d7d1b5d2bec1a2887e65ceed900238 # v4
Expand All @@ -170,3 +177,17 @@ jobs:
disable_search: true
flags: ${{ matrix.codecov-flag }},core-integration
token: ${{ secrets.CODECOV_TOKEN }}
- name: Upload coverage reports to GitHub
if: matrix.upload-coverage
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
with:
name: coverage-reports-${{ matrix.name }}
path: |
src/coverage/lcov.info
webview-ui/coverage/lcov.info
packages/cloud/coverage/lcov.info
packages/telemetry/coverage/lcov.info
apps/cli/coverage/lcov.info
packages/core/coverage/unit/lcov.info
packages/core/coverage/integration/lcov.info
retention-days: 7
105 changes: 101 additions & 4 deletions .github/workflows/e2e.yml
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,100 @@ jobs:
VERSION=$(node -p 'require("./apps/vscode-e2e/package.json").devDependencies["@types/vscode"]')
echo "version=$VERSION" >> $GITHUB_OUTPUT

- name: Cache VS Code test binary
- name: Restore VS Code test binary cache
if: github.event_name != 'pull_request' || steps.e2e-marker.outputs.cache-hit != 'true'
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4
id: vscode-cache
uses: actions/cache/restore@0057852bfaa89a56745cba8c7296529d2fc39830 # v4
with:
path: |
apps/vscode-e2e/.vscode-test/
key: vscode-test-${{ runner.os }}-${{ steps.vscode-ver.outputs.version }}-v1
# Fall back to the most recent stale binary so a version bump or cache
# eviction doesn't force a download when the VS Code CDN is unreachable.
restore-keys: |
vscode-test-${{ runner.os }}-

- name: Probe VS Code download endpoints
if: (github.event_name != 'pull_request' || steps.e2e-marker.outputs.cache-hit != 'true') && steps.vscode-cache.outputs.cache-hit != 'true'
id: vscode-probe
# The version-resolution API and the archive CDN are different hosts. The
# archive URL 302-redirects to the CDN, so a HEAD probe that follows the
# redirect covers both. The fallback engages when either is unreachable.
run: |
API_OK=false
CDN_OK=false
if curl -sf --max-time 10 https://update.code.visualstudio.com/api/releases/stable > /dev/null; then
API_OK=true
fi
if curl -sfIL --max-time 20 -o /dev/null "https://update.code.visualstudio.com/${{ steps.vscode-ver.outputs.version }}/linux-x64/stable"; then
CDN_OK=true
fi
if [ "$API_OK" = "true" ] && [ "$CDN_OK" = "true" ]; then
echo "reachable=true" >> "$GITHUB_OUTPUT"
else
echo "reachable=false" >> "$GITHUB_OUTPUT"
echo "::warning::VS Code download endpoints unreachable (api=$API_OK, cdn=$CDN_OK); will try the stale cached binary"
fi

# @vscode/test-electron only skips its live version-resolution request when the
# requested version already exists in .vscode-test/. On an exact-key miss it calls
# the update API before its download retry loop, so an unreachable CDN fails the
# job before any test runs. When the endpoints are down, point the runner at the
# stale binary restored above (runTest.ts honors VSCODE_VERSION) so the suite
# still runs.
- name: Fall back to stale VS Code binary when CDN is unreachable
if: (github.event_name != 'pull_request' || steps.e2e-marker.outputs.cache-hit != 'true') && steps.vscode-cache.outputs.cache-hit != 'true' && steps.vscode-probe.outputs.reachable == 'false'
id: vscode-fallback
run: |
STALE=$(ls -d apps/vscode-e2e/.vscode-test/vscode-linux-x64-* 2>/dev/null | sort -V | tail -n 1 || true)
if [ -n "$STALE" ]; then
echo "VSCODE_VERSION=${STALE##*-}" >> "$GITHUB_ENV"
echo "used=true" >> "$GITHUB_OUTPUT"
echo "VS Code download endpoints are unreachable; falling back to cached VS Code ${STALE##*-}"
else
echo "::warning::VS Code download endpoints are unreachable and no cached binary is available; the download step will retry but may fail"
fi

# Retry only the binary download: version resolution and the archive fetch are
# the network-fragile parts. Genuine test failures should fail fast, so the
# test step itself is deliberately not retried.
- name: Download VS Code test binary
if: github.event_name != 'pull_request' || steps.e2e-marker.outputs.cache-hit != 'true'
id: vscode-download
run: |
cd apps/vscode-e2e
for attempt in 1 2 3; do
if node -e "const { downloadAndUnzipVSCode } = require('@vscode/test-electron'); const version = process.env.VSCODE_VERSION || require('./package.json').devDependencies['@types/vscode']; downloadAndUnzipVSCode(version).catch((err) => { console.error(err); process.exit(1); });"; then
exit 0
fi
echo "VS Code download attempt ${attempt} failed"
if [ "$attempt" -lt 3 ]; then
sleep 15
fi
done
exit 1

# restore-keys can bring back older binaries alongside the one just
# downloaded; keep only the effective version so each saved cache entry
# stays at one binary instead of growing with every version bump.
- name: Prune stale VS Code binaries
if: github.event_name != 'pull_request' || steps.e2e-marker.outputs.cache-hit != 'true'
run: |
EFFECTIVE="${VSCODE_VERSION:-${{ steps.vscode-ver.outputs.version }}}"
for dir in apps/vscode-e2e/.vscode-test/vscode-linux-x64-*; do
[ -e "$dir" ] || continue
if [ "$(basename "$dir")" != "vscode-linux-x64-$EFFECTIVE" ]; then
echo "Pruning stale VS Code binary $(basename "$dir")"
rm -rf "$dir"
fi
done

# Skip the save when the stale-binary fallback ran: the pinned-version key
# must not be populated with an older binary.
- name: Save VS Code test binary cache
if: (github.event_name != 'pull_request' || steps.e2e-marker.outputs.cache-hit != 'true') && steps.vscode-cache.outputs.cache-hit != 'true' && steps.vscode-fallback.outputs.used != 'true'
continue-on-error: true
uses: actions/cache/save@0057852bfaa89a56745cba8c7296529d2fc39830 # v4
with:
path: |
apps/vscode-e2e/.vscode-test/
Expand All @@ -59,12 +150,18 @@ jobs:
if: github.event_name != 'pull_request' || steps.e2e-marker.outputs.cache-hit != 'true'
run: xvfb-run -a pnpm --filter @roo-code/vscode-e2e test:ci:mock

- name: Explain skipped mocked E2E pass marker
if: steps.e2e-marker.outputs.cache-hit != 'true' && steps.run-e2e.outcome == 'success' && steps.vscode-fallback.outputs.used == 'true'
run: echo "Skipping mocked E2E pass marker because tests ran against a stale cached VS Code binary (VS Code download endpoints unreachable)."

- name: Write mocked E2E pass marker
if: steps.e2e-marker.outputs.cache-hit != 'true' && steps.run-e2e.outcome == 'success'
# Skip when the stale-binary fallback ran: a pass against an older VS Code
# must not mint a marker for the intended-version source hash.
if: steps.e2e-marker.outputs.cache-hit != 'true' && steps.run-e2e.outcome == 'success' && steps.vscode-fallback.outputs.used != 'true'
run: mkdir -p .cache/e2e-pass && date -u > .cache/e2e-pass/passed

- name: Save mocked E2E pass marker
if: steps.e2e-marker.outputs.cache-hit != 'true' && steps.run-e2e.outcome == 'success'
if: steps.e2e-marker.outputs.cache-hit != 'true' && steps.run-e2e.outcome == 'success' && steps.vscode-fallback.outputs.used != 'true'
continue-on-error: true
uses: actions/cache/save@0057852bfaa89a56745cba8c7296529d2fc39830 # v4
with:
Expand Down
21 changes: 20 additions & 1 deletion .github/workflows/label-pr-review-state.yml
Original file line number Diff line number Diff line change
Expand Up @@ -153,9 +153,28 @@ jobs:
}),
]);

// listForRef returns every check run ever recorded on the ref, including
// stale superseded ones (e.g. a failed run later re-run green). Branch
// protection and the PR UI only consider the latest run per check name, so
// reduce to that before evaluating — otherwise a single stale failure makes
// ciFailed true forever and state labels never come back. See issue #884.
//
// Unlike listReviews (which documents oldest-first order), listForRef's
// ordering is unspecified, so we pick the latest by run.id — GitHub assigns
// monotonically increasing IDs, and id is never null (a freshly re-queued
// run can have started_at: null, which would lose a string comparison
// against an older completed run's timestamp).
const latestByName = new Map();
for (const run of checkRuns) {
const prev = latestByName.get(run.name);
if (!prev || run.id > prev.id) {
latestByName.set(run.name, run);
}
}

// Filter to required checks only (or all checks if rules unavailable).
// Always exclude this workflow's own run to avoid self-referential loops.
const relevantRuns = checkRuns.filter(run => {
const relevantRuns = [...latestByName.values()].filter(run => {
if (run.name === 'Reconcile PR review state labels') return false;
return requiredCheckNames ? requiredCheckNames.has(run.name) : true;
});
Expand Down
68 changes: 68 additions & 0 deletions .github/workflows/release-reminder.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
name: Post weekly release reminder

# Requires repo secret DISCORD_RELEASE_WEBHOOK_URL — Discord webhook for the
# release-reminders channel. The reminder job fails (noisily) if it is unset.

on:
schedule:
# Friday at 09:00 UTC
- cron: "0 9 * * 5"
workflow_dispatch:

permissions: {}

concurrency:
group: release-reminder
# A re-run produces an identical message, so cancel a stale/duplicate run
# rather than risk posting twice.
cancel-in-progress: true

jobs:
remind:
runs-on: ubuntu-latest
steps:
- name: Post release reminder to Discord
env:
DISCORD_RELEASE_WEBHOOK_URL: ${{ secrets.DISCORD_RELEASE_WEBHOOK_URL }}
run: |
set -euo pipefail
owners=("Elliott" "Navad" "Toray")
anchor_date="2026-07-17"
seconds_since_anchor=$(($(date -u +%s) - $(date -u -d "$anchor_date" +%s)))
weeks_since_anchor=$((seconds_since_anchor / 604800))
owner="${owners[$((weeks_since_anchor % ${#owners[@]}))]}"

message=$(cat <<EOF
**Weekly release reminder**

**${owner}** is on deck for this week's Zoo Code release.

1. Create the release branch and prepare the release with \`.roo/commands/release.md\`.
2. Get the release PR approved.
3. Tag the release branch to trigger the Marketplace publish workflow.
4. Approve the deployment when the \`marketplace-production\` environment requests it.
5. After publishing succeeds, add the release PR to the merge queue.

Avoid merging or rebasing the release branch before publishing, so the approved tag keeps the intended commit SHA.

More detail: https://discord.com/channels/1497384592494297201/1498024845945081999/1512914847124291667
EOF
)

payload=$(jq -n --arg content "$message" '{content: $content}')
curl --fail-with-body --silent --show-error \
--header 'Content-Type: application/json' \
--data "$payload" \
"$DISCORD_RELEASE_WEBHOOK_URL"

- name: Notify on failure
if: failure()
env:
DISCORD_RELEASE_WEBHOOK_URL: ${{ secrets.DISCORD_RELEASE_WEBHOOK_URL }}
run: |
set -euo pipefail
payload=$(jq -nc --arg content "⚠️ release-reminder workflow failed — see the Actions runs." '{content: $content}')
curl --fail-with-body --silent --show-error \
--header 'Content-Type: application/json' \
--data "$payload" \
"$DISCORD_RELEASE_WEBHOOK_URL" || true
Loading
Loading