The extension is a leaf node in the monorepo dependency graph (nothing
depends on it) and has its own independent release cycle. The standalone
repo at CopilotKit/vscode-extension has full git history, working CI,
verified OIDC publishing, and branch protection.
## Summary
Adds `.github/workflows/ghcr_unlinked_packages.yml` — a scheduled
GitHub Actions workflow that audits all CopilotKit org container
packages daily and Slack-alerts when any are unlinked from a source
repository (`repository: null` on the GHCR API).
## Why
When a GHCR container package is unlinked from a repo, workflow
builds in `CopilotKit/CopilotKit` get `403 Forbidden` on push to GHCR
— the workflow `GITHUB_TOKEN` only has package-write permissions
when the package is linked to the actor's repo. We hit this twice in
quick succession:
- `showcase-ops` — caught manually after a failed deploy
- `showcase-pocketbase` — caught by a preemptive scan
There is **no GitHub API to programmatically link a package to a
repo** — it is UI-only. So the only way to prevent future surprises
is to detect drift early via a scheduled audit + Slack alert.
## Behavior
- Runs daily at 14:00 UTC, plus `workflow_dispatch` for ad-hoc runs.
- Lists every container package in the `CopilotKit` org via
`gh api /orgs/CopilotKit/packages?package_type=container`.
- Filters for `repository == null`.
- If any are unlinked, posts a Slack message that includes the count,
a bulleted list with deep links to each package's settings page,
the exact UI fix steps, and a footer noting the failure mode.
- Exits 0 in all non-error cases. The Slack message IS the alert;
failing the workflow on drift would create noisy red CI checks.
## Required setup before this workflow can fire
Two new repo secrets must be added:
1. **`ORG_READ_PACKAGES_PAT`** — a fine-grained PAT with
`read:packages` scope, org-scoped to `CopilotKit`. The default
`secrets.GITHUB_TOKEN` does NOT have org-package-list scope. The
workflow fails loudly if this is missing.
2. **`SLACK_WEBHOOK_GHCR_DRIFT`** — a CopilotKit-internal Slack
incoming-webhook URL for an alerts channel. If missing, the audit
still runs and logs a warning; only the Slack post is skipped.
Without these secrets the workflow will either fail loudly (PAT) or
log-only (webhook). It will not silently mask drift.
## Test plan
- [ ] Add `ORG_READ_PACKAGES_PAT` and `SLACK_WEBHOOK_GHCR_DRIFT` repo
secrets.
- [ ] Trigger via `workflow_dispatch` on `main` post-merge.
- [ ] Confirm the audit lists packages and reports the unlinked count.
- [ ] If drift exists, confirm Slack receives the alert with working
deep links.
- [ ] If no drift, confirm workflow exits 0 with a "no drift" log line
and skips the Slack post.
Adds a scheduled GitHub Actions workflow that detects when CopilotKit
org container packages drift into an unlinked state (`repository: null`
on the GHCR API) and posts a Slack alert with deep links to the UI fix.
This drift breaks future workflow builds with `403 Forbidden` on push
to GHCR — the workflow `GITHUB_TOKEN` only has package-write
permissions when the package is linked to the actor's repo. We hit
this twice in quick succession: `showcase-ops` (caught manually after
a failed deploy) and `showcase-pocketbase` (caught by a preemptive
scan). There is no GitHub API to programmatically link a package to
a repo — it is UI-only — so the only way to prevent future surprises
is to detect drift early.
Schedule: daily at 14:00 UTC, plus `workflow_dispatch` for ad-hoc
runs. Exits 0 on drift (the Slack message IS the alert; failing the
workflow on a schedule would create noisy red CI checks).
Requires two new repo secrets:
- `ORG_READ_PACKAGES_PAT` (read:packages, org-scoped to CopilotKit)
- `SLACK_WEBHOOK_GHCR_DRIFT` (CopilotKit-internal alerts webhook)
Workflow fails loudly if the PAT is missing; logs a warning and
continues if the webhook is missing.
CR R1 follow-ups on top of the /api/ops proxy fix.
Bucket (a) — must-fix:
- Dockerfile: declare ARG/ENV OPS_BASE_URL in the builder stage. next.config.ts
evaluates rewrites() at build time and throws if OPS_BASE_URL is unset, which
aborted `next build` in CI. Mirrors the existing NEXT_PUBLIC_SHELL_URL /
NEXT_PUBLIC_POCKETBASE_URL pattern.
- showcase_deploy.yml: pipe OPS_BASE_URL through to docker build for the
shell-dashboard matrix entry, defaulting to the production
showcase-ops-production.up.railway.app URL.
- next.config.ts: strip trailing slashes from OPS_BASE_URL before constructing
the rewrite destination, matching the same normalization in
src/lib/ops-api.ts:resolveBaseUrl so server-side rewrite and client-side
fetch agree on the URL shape.
- src/lib/ops-api.ts: treat empty / whitespace NEXT_PUBLIC_OPS_BASE_URL as
"no override". `??` only short-circuits on null/undefined, so an env var set
to "" silently produced baseUrl="" and URLs of the form "/probes" with no
/api/ops prefix.
- use-probes.integration.test.tsx: snapshot+restore process.env.NEXT_PUBLIC_OPS_BASE_URL
in beforeEach/afterEach so tests never leak env state. Strengthen the proxy
contract assertions: lock toHaveBeenCalledTimes(1), assert method=GET,
cache=no-store, accept JSON header, and signal is an AbortSignal. Tighten
the 404 regression to assert the canonical ensureOk message shape so a
refactor that changes the format trips the test.
Bucket (b) — applied since the diff stayed focused:
- triggerProbe: add cache:"no-store" for parity with the GET fetches.
- fetchProbeDetail / triggerProbe: throw early when id is empty so callers
get a clean error instead of a request to /probes//... .
- ensureOk: bump body-truncation cap from 200 to 500 chars and append a
`[truncated, N bytes total]` marker so operators can see they're missing
tail bytes when the server returns a long HTML/stack-trace body.
- ops-api.ts: drop dev-loop review-cycle tag prefixes (R2-C.3, R3-C, R3-D.1)
from comments. Keep the actual rationale.
- ops-api.ts header docstring: clarify that NEXT_PUBLIC_OPS_BASE_URL is read
live at runtime in this codebase (SSR + tests), not just statically inlined
into the client bundle.
Verified:
- Tests: vitest run — 26 files, 293 passed, 1 skipped (no test count change).
- Typecheck: tsc --noEmit clean.
- Lint + format: oxlint + oxfmt clean on changed files.
- Local Docker build: `docker build --build-arg OPS_BASE_URL=https://...` succeeds.
Without --build-arg the build fails with "OPS_BASE_URL must be set" as
expected, confirming the fix is load-bearing.
showcase-ops was excluded from .github/workflows/showcase_deploy.yml, so
commits touching showcase/ops/** never produced a fresh GHCR image. PR
#4293 (Status tab + /api/probes route) merged to main on 2026-04-26 but
no rebuild fired — the deployed Railway image is stale and /api/probes
404s in production.
Adding showcase-ops as a first-class matrix entry:
- dispatch_name: showcase-ops (workflow_dispatch option + filter_key)
- paths-filter: showcase/ops/**, plus shared/scripts/manifests
(showcase-ops's Dockerfile bundles all four into the runtime image
via build-stage COPY + generate-registry.ts)
- context: '.' (repo root) so the Dockerfile can COPY from
pnpm-workspace.yaml + packages/ + showcase/{ops,shared,packages,scripts}
- dockerfile: showcase/ops/Dockerfile
- image: showcase-ops -> ghcr.io/copilotkit/showcase-ops:latest
- railway_id: 3a14bfed-0537-4d71-897b-7c593dca161d
- health_path: /health (matches Dockerfile HEALTHCHECK + Hono route)
- timeout: 20 (heavier build than shells: pnpm deploy + chromium
install via playwright --with-deps)
- lfs: false (no Git LFS assets in showcase/ops)
- linux/amd64 platform inherited from existing build step (Depot)
Resulting matrix: 39 services (was 38). dispatch_name uniqueness +
JSON validity verified locally; actionlint/yamllint surface only
pre-existing findings on the workflow.
oxfmt 0.36 supports .md but not .mdx — including .mdx in the file-list
glob causes MDX-only PRs to fail with "Expected at least one target
file" because oxfmt drops every input as an unknown target and then
errors on the empty target set. Mixed PRs (.mdx + .tsx/.json/etc) pass
because the non-MDX files keep the target set non-empty, which is why
this has only surfaced now on a shell-docs-only sync PR.
Removing .mdx from the glob lets MDX-only PRs hit the existing count=0
skip path and pass cleanly. Add .mdx back when oxfmt ships MDX support.
The test at integration-smoke.spec.ts:21 imports registry.json, which
is gitignored (generated at build time). After PR #4236 removed it
from tracking, every CI run fails with "Cannot find module
'../../shell/src/data/registry.json'" — producing the repeating
"Starter Deployed Smoke Test Failed — 0 failure(s) — job-level error"
Slack alerts in #oss-alerts.
Adds a generate-registry step before the Playwright test run.
Three fixes batched to minimize PR churn:
1. Dockerfile: add ARG NEXT_PUBLIC_POCKETBASE_URL so the PB URL gets
baked into the Next.js bundle at build time. Without this, pb.ts
resolves to the sentinel URL and the dashboard shows "unavailable"
on every tab. Pre-existing bug exposed by fresh deploys.
2. showcase_deploy.yml: pass NEXT_PUBLIC_POCKETBASE_URL and
NEXT_PUBLIC_SHELL_URL as build args for the shell-dashboard service
in the CI matrix. Neither was ever passed before.
3. cell-matrix.tsx + parity-matrix.tsx: flatten nested table pattern
that caused column misalignment. Category rows used colSpan with
an inner <table> whose columns floated independently of the header.
Replaced with useCollapsible hook + flat sibling <tr> rows.
Also regenerates package-lock.json for the plugin-react downgrade
from PR #4241 (npm ci was failing in Docker).
Local Docker build verified with --build-arg for both NEXT_PUBLIC vars.
- deploy workflow: add shared/scripts/manifest paths to shell-dashboard
and shell-docs filters (previously triggered implicitly by committed
JSON diffs in those directories)
- capture-previews: add generate-registry step before capture; use
git add -f for the gitignored registry.json
- e2e smoke test: document generator dependency in import comment
Two bugs in the earlier version, both surfaced once main advanced and
was merged into the branch:
- `fetch-depth: ${{ ... && 0 || 1 }}` evaluated to `1` on PRs because
the short-circuit treats `0` as falsy, so the base SHA was missing
locally and `git diff` exited 128.
- Diffing against the PR's stored `base.sha` includes every file main
touched since the PR opened once main is merged into the branch, which
defeats the whole point of this change. Diff against the current tip
of the base branch instead.
The static / quality "format" job ran oxfmt across the whole tree on
every PR. When main advanced with stale files, unrelated PRs picked up
those re-formats and ended up with a noisy `style: auto-fix formatting`
commit.
Now the job collects `git diff --name-only base...HEAD` for oxfmt-
supported extensions and passes only those paths to oxfmt. Push events
on main continue to check the whole repo so the baseline is still
enforced.
aimock is no longer a Docker-built showcase service. Railway
pulls the pre-built upstream image directly from GHCR. Remove:
- aimock from workflow_dispatch service options
- aimock paths-filter (showcase/aimock/**)
- aimock entry from ALL_SERVICES matrix
The capture job can take ~30 minutes, during which other commits
routinely land on main. Without a rebase-and-retry loop, the
devops-bot push loses the race and fails with "fetch first".
Observed in runs 24799601181 and 24809331709 on 2026-04-22,
both failing at the same step with identical remote-rejected-push
output. Bounded to 5 attempts so a persistent failure still
surfaces rather than looping forever.
## Summary
- Change `shell-dojo` matrix entry: `context: "."` + `dockerfile:
"showcase/shell-dojo/Dockerfile"`. Matches shell-dashboard pattern.
## Why
Build fails with `failed to walk /tmp/buildkit-mount.../showcase: no
such file or directory`. `showcase/shell-dojo/Dockerfile` has `COPY
showcase/scripts/...`, `COPY showcase/shared/...`, `COPY
showcase/packages/...` — expects repo root as build context. Matrix had
`context: showcase/shell-dojo` which scopes the context to just
shell-dojo's own directory, making those COPYs fail.
## Test plan
- [ ] Next deploy of shell-dojo succeeds
## Summary
- Replace `jq -n --rawfile text … > \$(mktemp)` + `payload-file-path`
with inline `payload:` + `toJSON(format(...))` on the scheduled
Slack-alert step in `test_smoke-starter.yml`.
- Pattern already in use in `test_smoke-starter-deployed.yml` (PR #4068)
and `showcase_validate.yml` — this brings the last holdout in line.
## Why
Every scheduled run of `test / smoke / starter` that legitimately fails
a matrix leg (upstream outage, floating-dep breakage, etc.) has been
crashing the downstream alert step with:
```
##[error]Invalid input! Failed to parse contents of the provided payload file
```
Two compounding causes:
1. `slackapi/slack-github-action@v2.1.0` rejects payload files whose
name does not end in `.json`/`.yaml`/`.yml`. `mktemp` produces
extensionless files.
2. `jq -n --rawfile text "$SLACK_MSG"` under `set -e` can abort on
edge-case summary input (missing tmpfile, non-UTF-8 bytes surviving the
sanitizer), leaving an empty or missing payload.
Either way, the notifier failed before firing, so genuine outages went
unreported in `#oss-alerts`. The user reported this as "Showcase: Drift
Detection" — that specific workflow was already retired to showcase-ops
on 2026-04-22 (commit `89eb0734b`), but the same
jq-rawfile-then-payload-file-path pattern still lived in
`test_smoke-starter.yml`.
Inline `payload:` with `toJSON(format(...))` sidesteps both failure
modes: quotes, backslashes, and newlines in the sanitized summary are
safely JSON-encoded at template-eval time, and there is no intermediate
file to mishandle.
Audit of remaining file-based payloads left intentionally unchanged:
- `test_smoke-starter-deployed.yml` recovery step writes to
`/tmp/starter-smoke-recovery.json` (valid extension, simple `jq --arg`
scalar) — safe.
- `showcase_docs-sync.yml` writes to `slack-payloads/*.json` (valid
extensions, `jq --arg` scalars) — safe.
## Test plan
- [ ] `actionlint .github/workflows/test_smoke-starter.yml` reports no
new findings (confirmed locally — one pre-existing line-64 warning
unrelated to this diff)
- [ ] Next scheduled run on `main` that has a genuinely failing matrix
leg posts a well-formed red alert to `#oss-alerts` instead of `Invalid
input! Failed to parse contents of the provided payload file`
- [ ] A scheduled run where all matrix legs pass remains silent (no
behavior change on the green path)
The failure-alert Slack step crashed every scheduled run with
'Invalid input! Failed to parse contents of the provided payload file'.
Two compounding causes: slackapi/slack-github-action@v2.1.0 rejects
payload files without a .json/.yaml/.yml extension (mktemp produces
extensionless files), and 'jq -n --rawfile' under 'set -e' can abort
on edge-case summary input, leaving an empty/missing payload.
Switch to the inline 'payload:' + toJSON(format(...)) pattern already
used in test_smoke-starter-deployed.yml (PR #4068) and
showcase_validate.yml. Summary text is sanitized into $GITHUB_ENV via
heredoc (handles embedded =, quotes, newlines), then JSON-encoded by
toJSON at template-eval time — no intermediate file, no jq crash path,
genuine outages surface in #oss-alerts instead of being silently
suppressed by a broken notifier.
test_smoke-starter-deployed.yml recovery path (/tmp/...-recovery.json,
jq --arg) and showcase_docs-sync.yml (static slack-payloads/*.json)
use safe patterns already — left unchanged.
GitHub's "Create a merge commit" merge option builds the commit message
from the PR body. When the body contains markdown lists or blank lines
(e.g. PR #4113 → merge 5ed233f01), commitlint parses subsequent
paragraphs as additional commit subjects and fails with subject-empty /
type-empty.
Two-layer fix:
- commitlint.config.js: ignore standard "Merge " prefixed messages.
- static_quality.yml: guard the push-path `--last` step with a
parent-count check so true merge commits (which keep the PR-title
header and thus don't match the "Merge " prefix) are skipped
before commitlint runs at all.
The workflow runs exclusively on main-branch pushes, completed
"Showcase: Build & Deploy" runs, and manual dispatch — all production
events where silent failures (e.g. the GH013 PROTECT_OUR_MAIN
regression that motivated PR #4159) must surface in #oss-alerts
rather than getting buried in the Actions tab.
Mirrors the failure-alert pattern from showcase_validate.yml:
- Hoist SLACK_WEBHOOK_OSS_ALERTS into a job-level env var so
step-level `if:` expressions can reference it (secrets.* is
not a valid named-value inside `if:`).
- Best-effort "Extract failure details" step pulls the failed
step name and first meaningful error line from the jobs API +
`gh run view --log-failed`, truncated to 300 chars.
- `slackapi/slack-github-action@v2.1.0` with toJSON(format(...))
wrapping to safely JSON-encode any dynamic values.
- Fallback `::warning::` log when the webhook secret is unset so
the gap is still visible in the workflow output.
Gated on `failure() && env.SLACK_WEBHOOK != ''`. No `github.event_name`
filter needed — this workflow has no pull_request trigger, so every
failure is an actionable production event.
Adds a new step that runs validate-fixture-tool-surface.ts on every PR
and push to main. Sits alongside the existing validate-parity /
validate-workflow-starters / validate-pins steps and follows the same
pnpm-exec-tsx pattern.
Without this, the drift validator only runs locally or via the
vitest suite (which only catches bugs in the validator itself, not
drift in the real fixture/demo state). The CLI invocation against the
committed tree is what would have caught the 2026-04-22 regression
before it reached prod.
## Summary
- **CI workflow consolidation**: Merged commitlint into static_quality,
added concurrency groups to test workflows, added path filters to
publish-commit, fixed static_danger matrix bug and python-sdk
concurrency
- **Showcase deploy refactor**: Replaced 23 duplicate jobs with a
dynamic matrix strategy (~1000 lines removed)
- **Preview capture → MP4**: Switched from GIF to optimized MP4 (H.264,
CRF 28, faststart), uploads to GitHub Release `showcase-previews`
instead of committing blobs to the repo. DemoCard uses `<video autoplay
muted loop>` for hover previews.
## Test plan
- [x] Local MP4 capture test passed (langgraph-python, 14KB, 8s, H.264
400x300 @ 10fps)
- [ ] CI workflows pass on this branch
- [ ] Capture workflow produces and uploads MP4 to release
- [ ] DemoCard hover shows video preview when URL is populated
🤖 Generated with [Claude Code](https://claude.com/claude-code)
## CI hygiene fixes
Four direct-fix items surfaced during the 2026-04-16 QA/E2E blitz,
bundled as one PR with one commit per fix:
1. **`test_unit.yml` paths-ignore** — add `showcase/**` +
`sdk-python/**` (prevents spurious TS unit matrix runs on showcase-only
or sdk-python-only PRs). `sdk-python-old/**` was mentioned in the plan
but doesn't exist on main; only the two existing dirs are added.
2. **`test_doc-examples.yml`** — scope PR trigger to `branches: [main]`
(matches convention of other workflows).
3. **`e2e_dojo.yml`** — symmetric `.changeset` path filter on push+PR
(was asymmetric — already in the `dorny/paths-filter` step's `ts:` list,
so this aligns the trigger).
4. **`starter-smoke.yml`** — rename internal job id (`starter-smoke` →
`smoke-starter`) and artifact name pattern (cosmetic; matches the new
`test_<layer>-<target>` / `smoke-<layer>` naming convention; no external
consumers).
### Fix skipped
**`showcase_smoke-monitor.yml` slug normalization** (item #10 in the
Notion page) — inspecting the file, it already uses the
`showcase/packages/` slug convention (`ms-agent-python`,
`ms-agent-dotnet`, `strands`). The apparent inconsistency is actually in
`starter-smoke.yml`, whose matrix keys must stay as
`ms-agent-framework-*` / `strands-python` because they are literal
directory names in `examples/integrations/` (used as `working-directory:
examples/integrations/${{ matrix.starter }}`). So there is no actionable
change here — the two files use different slug conventions *by
necessity*, because they target different directories (deployed
`showcase/packages/*` vs. local `examples/integrations/*`). Flagging for
the author of the blitz notes in case the actual concern was something
else.
Refs: [Bugs Found During
Blitz](https://www.notion.so/3443aa381852812fb595c5118dd68818) items #3,
#8, #9, #12.
## Summary
- Rewrite starter Dockerfile templates (`typescript`, `python`, `java`,
`dotnet`) as true multi-stage: builder has dev tools, runner is minimal.
No `pip`/`npm`/`pnpm`/`tsx`/`*-cli dev` in runtime stages.
- Encode prod-mode behaviors for `claude-sdk-typescript`, `mastra`,
`langgraph-typescript` directly in the TS template + generator
conditionals (folds in the pattern from #4132 for langgraph-ts; adds
parallel migration for claude-sdk-ts + mastra).
- Regenerate all 17 starter Dockerfiles.
- Rewrite all 17 package Dockerfiles to match the template shape
(multi-stage, venv builder for Python, 3-stage Python runtime, `USER
app`, etc.).
- Cold-start instrumentation on `claude-sdk-typescript` package
(absorbed from closed PR #4133).
- Pin `platforms: linux/amd64` in the showcase deploy workflow
(`docker/build-push-action` step) to match what Railway + GHCR require.
- Template `README.md` documenting multi-stage conventions, size target,
platform mandate, and the `dockerfile_hygiene` probe.
- `generate-starters.ts` strips the `langgraph-typescript` starter's
dead `server.mjs` + its `start` script (both reference deps only needed
in the package's prod-mode path).
## Why
Railway cold starts exceed the 180s watchdog grace when the runtime
stage boots via `langgraph-cli dev` / `npx mastra dev` / `npx tsx` — the
dev-mode work (TS compile, Rollup bundle, native libsql init) blocks the
health probe. #4132 proved the fix is to move this to image build time
for langgraph-ts. This PR generalizes that shape to all TS starters that
need it and reshapes every Dockerfile in the tree so runners no longer
carry dev toolchains.
Secondary outcomes:
- Smaller runtime images (pip/npm toolchain moved to builder only).
- Consistent `--platform linux/amd64` pinning end-to-end.
- No `pnpm install --frozen-lockfile` in the TS starter template
(starters are no-lockfile by design).
- `validate-pins` ratchet re-baselined to match the regenerated spec
strings.
## Test plan
- [ ] `docker build --platform linux/amd64` green for every starter (17)
and package (17) locally
- [ ] `pnpm -C showcase/scripts` vitest: 1085/1085 pass
- [ ] `tsc --noEmit -p showcase/scripts/tsconfig.json` clean
- [ ] `oxfmt --check` + `oxlint` clean on touched TS files
- [ ] validate-pins: count 110, hash matches updated baseline
- [ ] CI Showcase Validate green on PR HEAD
- [ ] Post-merge: showcase deploy for mastra + claude-sdk-typescript
goes green (currently flapping)
## Why
Adds an interim GitHub Actions cron that curls `/api/health` on each of
the 17 showcase starter services every 5 minutes. The goal is to keep
the agent's in-memory state warm — JIT tiers, module cache, connection
pools. Railway Pro tier doesn't sleep containers, but warm-state still
decays over idle periods.
This is a stopgap until the `showcase-ops` service (see Notion proposal
§2a) lands. Its smoke probe runs at the same cadence and will subsume
this workflow entirely.
## What
New file: `.github/workflows/showcase_keep-alive.yml`
- Schedule: `*/5 * * * *` (every 5 minutes) + manual `workflow_dispatch`
- Matrix over all 17 starter slugs (`ag2`, `agno`, `claude-sdk-python`,
`claude-sdk-typescript`, `crewai-crews`, `google-adk`,
`langgraph-fastapi`, `langgraph-python`, `langgraph-typescript`,
`langroid`, `llamaindex`, `mastra`, `ms-agent-dotnet`,
`ms-agent-python`, `pydantic-ai`, `spring-ai`, `strands`)
- Each cell: `curl -fsS --max-time 15
https://showcase-<slug>-production.up.railway.app/api/health`
- `fail-fast: false` + `continue-on-error: true` so one starter being
down doesn't cancel the others
- No Slack notification — `showcase_smoke-monitor.yml` already covers
alerting. This workflow is strictly keep-alive, not observability.
- Header comment marks it INTERIM and references the showcase-ops
cutover
## Interim marker
Remove this workflow once `showcase-ops` ships its smoke probe at the
same 5-min cadence (tracked in the Notion showcase-ops proposal).
## Test plan
- [ ] Verify workflow parses in the Actions UI after merge
- [ ] Trigger `workflow_dispatch` once manually; confirm all 17 cells
run and pass for healthy starters
- [ ] Confirm no Slack noise generated by this workflow
- [ ] Remove when showcase-ops cutover lands
Add platforms: linux/amd64 to the depot/build-push-action invocation in
showcase_deploy.yml. Railway and GHCR serve x86 hosts, so an arm64-only
image crashes on pull. Matches the platform requirement enforced for
local docker build invocations (documented in
showcase/starters/template/README.md).
Verify Marketplace credential expects an active Azure session via azure/login, but login was gated only on published==false. When dry_run=true and version is already published, the verify step ran without a session and failed. Widening the guard to include dry_run.
The dry-run summary referenced scripts/release/vscode-extension-release.sh,
which was removed in 17c4daee3 (drop release helper script). Point
maintainers at the new CHANGELOG-edit flow instead.
Adds .github/workflows/vscode-extension-changelog-sync.yml: on PRs that
touch packages/vscode-extension/CHANGELOG.md, read the top '## X.Y.Z'
entry and bump packages/vscode-extension/package.json to match via an
auto-committed 'chore: release vX.Y.Z' on the PR branch.
Collapses the release maintainer flow to a single CHANGELOG edit.
Forks are skipped (GITHUB_TOKEN can't push to fork branches). A
guardrail rejects CHANGELOG entries older than the current package.json
version (catches history edits vs. prepends).
Lets us validate the Marketplace-OIDC and Open VSX auth chains end-to-end
without actually publishing. workflow_dispatch now accepts a dry_run input
(default true); when set, the job runs checkout, Azure login,
vsce verify-pat, and a new Open VSX /api/user token introspection, then
stops before any publish, tag, release, or Slack success notify.
Push trigger is unchanged — dry_run is only meaningful for manual runs.
- Use slackapi/slack-github-action@v2.1.0 matching 8+ existing workflows
- Switch from SLACK_WEBHOOK to SLACK_WEBHOOK_OSS_ALERTS (the secret
actually wired up in the repo, routed to #oss-alerts)
- Guard with env.SLACK_WEBHOOK_OSS_ALERTS != '' for repo-fork safety
- Add failure-path notification with failed job + per-registry outcomes
+ run URL (follows memory rule: red alerts carry actionable detail)
- Expose publisher as a step output to construct Open VSX URL cleanly
(https://open-vsx.org/extension/<publisher>/<name>) instead of inline
tr substitution
Replace tag-triggered publish with push-to-main + version-on-Marketplace
self-gate, matching CopilotKit/aimock. CI reads version from package.json,
queries vsce show for that version, and no-ops if already published. On a
new version it builds once, dual-publishes (Marketplace + Open VSX with
the existing retry wrappers and idempotent 'already exists' handling),
tags vscode-extension-vX.Y.Z, cuts a GitHub Release from the CHANGELOG
section, and posts to SLACK_WEBHOOK if configured. Path-scoped to
packages/vscode-extension/** so unrelated pushes don't trigger the job.
Wrap both registry publish steps in a bash retry helper that retries up
to 5 times with staggered backoff (10s/20s/40s/60s/90s) on transient
conditions (5xx, timeouts, connection resets, DNS). Auth and validation
errors still fail fast with no retry.
Critically, 'version already exists' is treated as idempotent success:
if attempt N-1 landed on the registry but its response was lost to a
502 after commit, attempt N sees the already-published version and
short-circuits rather than failing the job.
Motivated by Open VSX /publish returning intermittent 502 Bad Gateway
errors from Eclipse Foundation infra. Each attempt is wrapped in
::group:: markers so per-attempt logs are collapsible in the Actions
UI. Reconciliation step updated to reflect retry semantics and to
tell the operator to rerun the job (not bump the version) on exhausted
retries. RELEASING.md gets a 'Transient registry failures' section
documenting the behavior for both CI and manual publish paths.