Commit Graph

11487 Commits

Author SHA1 Message Date
Alem Tuzlak 69a4d4baea ci: pin Node 22 via root .nvmrc for the workspace-source build
The Railway slack-example build now compiles the @copilotkit/* libs from
source (tsdown/rolldown). rolldown needs Node >=20.12 (uses
`node:util`'s `styleText`), but the builder defaulted to Node 18 from the
root `engines.node: ">=18"` — failing with "does not provide an export
named 'styleText'". Pin the build/dev Node to 22 (read by the Railway
builder and nvm/fnm). CI is unaffected: no workflow reads .nvmrc, and the
packages' published `engines` runtime contract is left as-is (this is a
build-environment pin, not a runtime requirement change).
2026-06-18 16:51:43 +02:00
Alem Tuzlak 93074e1c6e ci(examples/slack): drop the standalone lockfile (root workspace lock is authoritative)
`examples/slack/pnpm-lock.yaml` only existed for the old isolated deploy
(root dir `/examples/slack`, `pnpm install --ignore-workspace
--frozen-lockfile` resolving the @copilotkit/* deps from npm). Now that the
example is a workspace member built from source (`workspace:*`, root-dir
`/`), pnpm uses the single root `pnpm-lock.yaml`; the per-example lock is
never consulted and was left stale — it still pins the published `~0.0.2`
versions, which contradicts the `workspace:*` package.json and would break
any `--frozen-lockfile` install.
2026-06-18 16:16:08 +02:00
Alem Tuzlak 2434e36453 ci(examples/slack): build from workspace source to decouple Railway deploy from npm publish
The example declared its sibling @copilotkit/* packages as npm version
ranges, so the Railway service (which builds examples/slack in isolation)
resolved them from the registry — forcing a "publish first, then bump the
example" dance on every PR, with a broken deploy window in between.

Switch those deps to the workspace:* protocol (the example is private, so
it never affects publishing) so the example always builds from in-repo
source, and add a graph-aware `build` script that compiles the workspace
libs it imports (and their deps) via Nx. README documents the Railway
settings (root dir / build / start / watch paths) and the copy-out caveat.

Result: a packages/** change redeploys the example with the new code
immediately, and npm publishing becomes an independent manual step.
2026-06-18 15:39:04 +02:00
Alem Tuzlak c2fdb00c69 fix(examples): regenerate slack example lockfile for @copilotkit/bot-discord (#5530)
## What

Regenerates `examples/slack/pnpm-lock.yaml` so it matches the example's
`package.json`.

## Why

The `@copilotkit/bot-discord` dependency was added to
`examples/slack/package.json` (in #5524 / the "run Slack and Discord
from one bot app" change) but the standalone lockfile was never
regenerated, leaving it out of sync. A `--frozen-lockfile` install in
`examples/slack` would fail.

## How

Ran `pnpm@10.33.4 install --lockfile-only --ignore-workspace` inside
`examples/slack` (matching the repo's pinned pnpm and the example's
standalone lockfile setup). The diff is purely additive — it adds
`@copilotkit/bot-discord` and its `discord.js` subtree; the
`@ai-sdk/mcp` override and all existing entries are preserved. Verified
with a `--frozen-lockfile` check.
2026-06-18 11:08:34 +02:00
Alem Tuzlak b703809a1a fix(examples): regenerate slack lockfile for @copilotkit/bot-discord 2026-06-18 11:04:28 +02:00
Tyler Slaton 406df02991 feat(bot-discord): Discord PlatformAdapter for @copilotkit/bot (#5524)
## Summary

Adds **`@copilotkit/bot-discord`** — a Discord `PlatformAdapter` for
`@copilotkit/bot`, built on **discord.js v14**, mirroring the existing
`@copilotkit/bot-slack`. It lets users build Discord bots on the shared
`@copilotkit/bot` + `@copilotkit/bot-ui` primitives, running on agents
via the AG-UI protocol.

Ships three things: the **package**, a runnable **example**, and
**docs**.

## Package (`packages/bot-discord`)

- **Gateway ingress** via discord.js — intents `Guilds`,
`GuildMessages`, `MessageContent` (privileged), `DirectMessages`,
`GuildMembers` (privileged). Both privileged intents must be enabled in
the Discord Developer Portal; `GuildMembers` powers user lookup.
- **Components V2 egress** — IR → `ContainerBuilder` with
`MessageFlags.IsComponentsV2`; full
`Message`/`Header`/`Section`/`Markdown`/`Fields`/`Context`/`Actions`/`Button`/`Select`/`Image`/`Divider`/`Table`
mapping, budget-clamped via `DISCORD_LIMITS`.
- **Streaming replies** — 1100 ms edit throttle; rollover at a 1900-char
soft limit / 2000 hard limit.
- **Ack-first interactions** (3000 ms deadline) — slash commands ack
with an ephemeral reply; buttons/selects ack via `deferUpdate`.
- **Slash-command registration** — per-guild (instant) when `guildId` is
set, else global (propagates within ~1h).
- **HITL**, **typing + reactions**, **built-in tools**
(`lookup_discord_user`) + **context**, in-memory conversation store.
- Capabilities: `{ supportsModals: false, supportsTyping: true,
supportsReactions: true, supportsStreaming: true, maxBlocksPerMessage:
40 }`.

## Example (`examples/discord`)

Runnable Discord bot — `BuiltInAgent` + Linear/Notion MCP (model
`openai/gpt-5.5`), app tools/components/commands/context/HITL, plus a
**`DISCORD_E2E`-gated manual e2e harness**.

```
DISCORD_E2E=1 pnpm --filter discord-example exec tsx e2e/run.ts
```

## Docs

- `showcase/shell-docs` reference under `reference/bot/discord/` —
`index`, `defaultDiscordTools`, `defaultDiscordContext`,
`renderComponents`, `DISCORD_LIMITS`; bot reference index + nav.
- Package `README.md` + `ARCHITECTURE.md`.

## Review summary

Built and reviewed via a multi-module adversarial CR process:

- **Module 1 (package):** 4 CR rounds, ~22 bugs fixed, **156 tests**.
- **Module 2 (example):** 1 round, ~10 fixes (stale Slack-port residue +
missing error guards), **38 tests**.
- **Module 3 (docs):** 1 round — fixed a blocker (doc invented three
non-existent adapter options) + majors (missing `GuildMembers`, wrong
streaming throttle/rollover numbers).
- **Module 4 (cross-cutting, 7 lenses + promotion audit):** caught &
fixed a **blocker — slash-command interactions were never
acknowledged**, so every slash command showed the user Discord's "The
application did not respond" error. Now acked within the 3 s window.
Also fixed: frozen `_thinking…_` placeholder on mid-stream throw, an
unhandled-rejection window in the chunked stream, an accidental NUL byte
that made a test file binary, and `ARCHITECTURE.md` intent-list
accuracy.

## Deferred follow-ups (not blocking)

- **True modals / multi-step forms** — `supportsModals: false` in v1.
- **Inbound-attachment auto-wiring** — `buildFileContentParts` is
exported + tested but not yet wired into the listener, so a user
attaching a file gets nothing delivered to the agent. Two reviewers
rated this blocker-severity; the promotion audit confirmed there's no
false promise (README claims no upload support) and nothing depends on
it. **Strong fast-follow candidate.**
- **`@copilotkit/bot-slack` port** — bot-discord fixed a latent
`flushNow` ordering bug and a select `custom_id` JSON encode/decode
asymmetry that still exist in bot-slack.
- **Latent (not reachable by shipped example):** select-value JSON
round-trip type coercion; `>5` action-row silent drop (no overflow
marker).
- **Adapter-glue test coverage** —
`post`/`update`/`delete`/`lookupUser`/`getMessages`/`postFile`/ack-ordering/`interruptEventNames`/`custom_id`
round-trip are covered indirectly; direct tests would harden them.
- **Minor:** `getMessages` placeholder filtering (shared with
bot-slack); `buildFileContentParts` config-key/default divergence vs
bot-slack; type-cleanliness (`ChannelLike` `as never` bridge,
barrel-surface trim).
- **e2e harness** is a `DISCORD_E2E`-gated **manual** tool — its
synthetic interaction lacks an Ed25519 signature, so it works only
against a test shim, not real Discord.

## Caveat (pre-existing, unrelated to this PR)

`@copilotkit/core` (tsc error) and `@copilotkit/runtime` (check-types
JS-heap OOM) are already broken on `main`. They are not touched by this
branch; typecheck/build were scoped to `@copilotkit/bot-discord` +
`discord-example`.

## Test plan

- [x] `nx run @copilotkit/bot-discord:test` — 156 passing
- [x] `nx run discord-example:test` — 38 passing
- [x] `tsc --noEmit` (both `tsconfig.json` + `tsconfig.check.json`)
clean
- [x] `nx run @copilotkit/bot-discord:build` succeeds
- [x] oxfmt `--check` clean, oxlint 0 errors, `pnpm-lock.yaml` in sync
- [ ] Manual smoke against a real Discord app (mention, slash command,
button, streaming reply)
2026-06-18 01:52:30 -07:00
Alem Tuzlak 29d5a9d61d chore(discord): remove unrelated PR changes 2026-06-17 18:32:26 -07:00
Jordan Ritter e8234849cb fix(showcase/aimock): add generate_a2ui d6 fixtures for 8 slugs (Sales Dashboard probe) (#5528)
## Summary

Closes the production 503 gap on aimock-staging for the **"Show me my
sales dashboard for this quarter."** userMessage across 8 non-LGP slugs.
Mirrors LGP's `generate_a2ui` outer-emit fixture pattern.

Empirical gap (from `/tmp/staging-journal-diff.md`):

| Shape | Traffic | Status on main | Why |
|---|---|---|---|
| Shape A | model=gpt-4o-mini, no tools key, curl/8.7.1 | 503 | (out of
scope) |
| Shape B | model=gpt-4.1, stream=true, tools=[generate_a2ui],
AsyncOpenAI/Python | **503** | **No fixture matched** (this PR's target)
|

LGP has both `render_a2ui` AND `generate_a2ui` fixtures for this
userMessage → stays 200. The 8 slugs below were missing the
`generate_a2ui` entry → 503.

## Slugs patched

8 file edits, one entry each in
`showcase/aimock/d6/<slug>/gen-ui-declarative.json`:

- llamaindex / built-in-agent / ag2 / langroid / claude-sdk-typescript /
claude-sdk-python / ms-agent-dotnet / ms-agent-python

Each entry mirrors LGP's sales-dashboard outer entry — match by
`userMessage` + `context: "<slug>"`, response is a `generate_a2ui`
toolcall with no args and a per-slug unique toolCallId.

## Red-Green Proof (local aimock against
`ghcr.io/copilotkit/aimock:latest`)

Replayed the Shape-B production request body (model=gpt-4.1,
stream=true, tools=[generate_a2ui], X-AIMock-Context per slug) against
local aimock booted with unmodified `main` fixtures (RED) then this
branch's fixtures (GREEN).

**RED (baseline main, all 8 slugs):** `HTTP=404 ERROR=no_fixture_match`
(aimock's 404 `no_fixture_match` is what surfaces as 503 on prod
aimock-staging.)

**GREEN (this branch, all 8 slugs):** `HTTP=200 tool=generate_a2ui
id=call_d6_decl_dash_outer_<slug>_001`

**LGP regression (baseline → fix branch):** `HTTP=200` (unchanged).

Logs: `/tmp/genfix-RED-baseline.log`, `/tmp/genfix-value-test.log`,
`/tmp/genfix-lgp-regression.log`.

## Aimock fixture validation

`pnpm --filter @copilotkit/showcase-scripts test aimock-fixtures` →
**737/737 tests pass**.

## Scope

Intentionally narrow per CLAUDE.md "scope PRs to originally-flagged
findings":
- Closes Shape-B `tools=[generate_a2ui]` 503s for ONE userMessage.
- Out of scope: Shape-A 503s (no `tools` key, `curl/8.7.1` probe
traffic) and other userMessage gaps — separate follow-ups.

## Test plan

- [x] Local aimock RED→GREEN proof across all 8 slugs (Shape-B request
body)
- [x] LGP regression check (unchanged)
- [x] Aimock fixture validation suite (737/737)
- [ ] CI green
- [ ] Post-deploy staging replay (8 slugs → 200)
2026-06-17 16:10:50 -07:00
Jordan Ritter 14138b7d18 fix(showcase/aimock): add generate_a2ui d6 fixtures for 8 slugs (Sales Dashboard probe)
Mirrors LGP's `generate_a2ui` outer-emit fixture into 8 non-LGP slugs to close
the production 503 gap on the "Show me my sales dashboard for this quarter."
userMessage. Per /tmp/staging-journal-diff.md, aimock-staging returns 503 on
shape-B traffic (model=gpt-4.1, stream=true, tools=[generate_a2ui],
UA=AsyncOpenAI/Python) for all 8 slugs because no fixture matched.

Slugs patched (one entry each in d6/<slug>/gen-ui-declarative.json):
  - llamaindex
  - built-in-agent
  - ag2
  - langroid
  - claude-sdk-typescript
  - claude-sdk-python
  - ms-agent-dotnet
  - ms-agent-python

Each entry matches `userMessage` + `context: "<slug>"` and emits a
`generate_a2ui` toolcall with no args, mirroring LGP's sales-dashboard outer
entry. Per-slug unique toolCallId.

Red-green proof (local aimock, ghcr.io/copilotkit/aimock:latest):
  RED (baseline main, 8 slugs):  HTTP=404 no_fixture_match
  GREEN (this branch, 8 slugs):  HTTP=200 tool=generate_a2ui id=call_d6_decl_dash_outer_<slug>_001
  LGP regression (baseline+fix): HTTP=200 (unchanged)

aimock fixture validation: 737/737 tests pass.

PR scope is intentionally narrow per CLAUDE.md "Scope PRs to flagged
findings": this closes ONE userMessage gap. Shape-A 503s (no tools key) and
other userMessage gaps remain as separate follow-ups.
2026-06-17 16:02:37 -07:00
Jordan Ritter ae8a3f6c27 fix(showcase/aimock): add ag2 Excalidraw create_view d6 fixture (#5527)
## The gap

Staging strict-mode replay against `aimock-staging.up.railway.app`
confirmed
**ag2** was the only integration missing a fixture for:

- `userMessage: "Open Excalidraw and sketch a system diagram..."`
- `toolName: "create_view"`
- `context: "ag2"`

All 11 other contexts that share this userMessage (`langgraph-python`,
`google-adk`, `ms-agent-{dotnet,python}`, `strands`, `llamaindex`,
`built-in-agent`, `claude-sdk-{typescript,python}`, `langroid`,
`mastra`)
return 200 with `tools=[create_view]` declared. Only `ag2` returns
`{"code":"no_fixture_match"}`.

Evidence: a staging-only audit on the originating workstation
(`/tmp/aimock-staging-fixture-verify.md`).

## The fix

One fixture entry added to `showcase/aimock/d6/ag2/mcp-apps.json`,
mirroring the LGP gold-standard at

`showcase/aimock/d6/langgraph-python/tool-rendering-reasoning-chain.json`
(same fixture id `call_d5_mcp_apps_create_view_001`, same arguments
payload,
same `chunkSize: 9999`). The entry sits next to the existing
`hasToolResult: true` turn-2 sibling for the same userMessage.

## Red-green proof

**RED (pre-fix, against staging):**
```
$ curl -sS -X POST https://aimock-staging.up.railway.app/v1/chat/completions \
    -H "X-AIMock-Context: ag2" -H "X-AIMock-Strict: true" \
    -H "Content-Type: application/json" \
    -d '{"model":"gpt-4o-mini","messages":[{"role":"user","content":"Open Excalidraw and sketch a system diagram..."}],"tools":[{"type":"function","function":{"name":"create_view","parameters":{"type":"object","properties":{}}}}]}'

{"error":{"message":"Strict mode: no fixture matched","type":"invalid_request_error","param":null,"code":"no_fixture_match"}}
```

**GREEN (post-fix, against local iso9 aimock with the new fixture
mounted):**
```
$ curl -sS -X POST http://localhost:6010/v1/chat/completions \
    -H "X-AIMock-Context: ag2" -H "X-AIMock-Strict: true" \
    -H "Content-Type: application/json" \
    -d '{ ...same payload... }'

200 OK
content: "Sketching a client → server → database diagram in Excalidraw."
tool_calls[0].id: "call_d5_mcp_apps_create_view_001"
tool_calls[0].function.name: "create_view"
```

Local aimock `/__aimock/journal` confirms the match keys:
```
status=200 fixture={"userMessage":"Open Excalidraw and sketch a system diagram","toolName":"create_view","context":"ag2"}
```

**LGP regression check:** the same probe with
`X-AIMock-Context: langgraph-python` against the local iso9 aimock
returns 200
with the same canonical tool_call payload — LGP unaffected.

## Scope

Intentionally narrow per "scope PRs to the originally-flagged findings".
This
PR closes ONLY the one genuine fixture gap identified by the staging
verification audit. The 9 other 503s in that audit (sales-dashboard
across 9
slugs + google-adk Excalidraw) are NOT fixture gaps — staging replays
show
those fixtures DO load and DO match when `tools=[create_view]` is
declared.
The 503s come from the backend omitting the tool name from the outbound
`/v1/chat/completions` request's `tools` array, a separate effort.

## Post-merge verification

After `showcase_deploy` rebuilds `aimock-staging`, the same staging
probe
above will be re-run; it must return 200 (not 503).
2026-06-17 15:35:09 -07:00
Jordan Ritter 6652552d32 fix(showcase/aimock/d6/ag2): add Excalidraw create_view turn-1 fixture
Staging strict-mode replay confirmed ag2 was the only integration missing a
fixture for `Open Excalidraw and sketch a system diagram...` + `create_view`
(the D5 mcp-apps probe's turn-1). All 11 other contexts (langgraph-python,
google-adk, ms-agent-{dotnet,python}, strands, llamaindex, built-in-agent,
claude-sdk-{typescript,python}, langroid, mastra) returned 200 against
aimock-staging with the same payload; ag2 returned 503
`{"code":"no_fixture_match"}`.

Add the missing turn-1 entry to ag2/mcp-apps.json, mirroring the LGP
gold-standard at langgraph-python/tool-rendering-reasoning-chain.json (same
fixture id `call_d5_mcp_apps_create_view_001`, same arguments payload, same
chunkSize). Match keys: userMessage + toolName + context.

Local GREEN proof: iso9 aimock (port 6010) with new fixture mounted returns
200 with the canonical tool_call payload for the exact staging-replay request
body. LGP regression check (same probe with X-AIMock-Context: langgraph-python)
stays green.

Scope intentionally narrow per "scope PRs to the originally-flagged findings":
this PR closes only the one genuine fixture gap identified by the staging
verification audit. The 9 other 503s in that audit (sales-dashboard across 9
slugs + google-adk Excalidraw) are NOT fixture gaps — they are backend
tool-array forwarding issues and are tracked separately.
2026-06-17 15:28:44 -07:00
Alem Tuzlak f4e00eab8b chore(discord): merge main into discord branch 2026-06-17 11:58:33 -07:00
Jordan Ritter c540734143 chore(showcase): bump @copilotkit/* + @ag-ui/* across all integrations (#5523)
## Summary

Aligns showcase integration dependencies to current released minor
versions for the **1.60.2** cycle. Closes the version-coherence gap that
was blocking the `react-core@1.60.2` resume-path / gen-ui-interrupt
fixes from taking effect on staging.

## Package families bumped

| Family | From | To | Scope |
|---|---|---|---|
| `@copilotkit/{a2ui-renderer, react-core, react-ui, runtime, shared,
sdk-js, voice}` | `1.59.4` (18 integrations) / `1.57.2`
(ms-agent-harness-dotnet) | **`1.60.2`** | 19 integrations |
| `@ag-ui/{client, core, encoder}` | `0.0.55` | **`0.0.57`** | 15
integrations |
| `@ag-ui/mastra` | `0.2.1-beta.2` | **`0.2.4`** | mastra only |

`@ag-ui/mastra@1.0.x` (major) **deliberately not** bumped — major jump
held back per the broad-scope dep-bump policy.

## Integrations covered (19/19)

`ag2`, `agno`, `built-in-agent`, `claude-sdk-python`,
`claude-sdk-typescript`, `crewai-crews`, `google-adk`,
`langgraph-fastapi`, `langgraph-python`, `langgraph-typescript`,
`langroid`, `llamaindex`, `mastra`, `ms-agent-dotnet`,
**`ms-agent-harness-dotnet`** (newly added — was missed by the prior
18-integration staging and jumps two minor lines), `ms-agent-python`,
`pydantic-ai`, `spring-ai`, `strands`.

`built-in-agent`, `langgraph-fastapi`, `langgraph-python`,
`langgraph-typescript` carry no `@ag-ui/*` deps directly (the langgraph
trio gets the protocol via `@copilotkit/runtime`'s nested resolution,
which has been verified at `0.0.57` post-install).

## Coherence note

`@copilotkit/react-core@1.60.2` does not declare a hard peer-dep on
`@ag-ui/core` at the package-manifest level; it bundles its own copy via
nested `node_modules`. Lockfile inspection confirms nested
`@copilotkit/{react-core,runtime,shared}/node_modules/@ag-ui/client`
resolved at `0.0.57` across every integration that ships them,
satisfying the `feedback_agui_client_bump_scope` rule (`@ag-ui/core >=
0.0.48`).

## Reconciliation method

Per-integration `npm install --package-lock-only --legacy-peer-deps` (no
`node_modules` mutation). The `--legacy-peer-deps` flag is required to
step past a **pre-existing** `cmdk@0.2.1` ↔ `react@^19` peer conflict
that long predates this bump; lockfile contents are otherwise unchanged
in shape (only dep-version touchups). 30 files in the visible diff
because 8 of the 38 changed files were identical between staged-index
and working-tree.

## Red / Green proof

**RED** (pre-merge staging):
- `gen-ui-interrupt` cells on the langgraph trio (LGP / LGTS /
LG-FastAPI) and across the broader integration matrix are RED on the
showcase dashboard pending the `@copilotkit/react-core@1.60.2`
resume-path fix landing on every integration container.

**GREEN** (expected post-merge):
- `showcase_deploy` will rebuild every integration container on push to
`main`. The dashboard `gen-ui-interrupt` + `resume-path` cells should
flip GREEN once those containers redeploy.
- Per-cell empirical value-test (`bin/showcase test
<slug>:gen-ui-interrupt --d6 --direct`) on ≥3 cells across LGP / LGTS /
LG-FastAPI is queued for the post-merge verification window.

## Out of scope

- No source-code changes (TS/Python/.NET/Java/Go).
- No fixture changes.
- No frontend page changes.
- No `packages/`, `examples/`, `showcase/shell-*` touched.
- No Railway worker restart (separate effort).
- No langgraph-typescript backend agent changes (separate effort).

## Test plan

- [ ] CI green on this PR (lint / format / build / publint / attw on the
affected workspaces)
- [ ] Admin-merge once cr-loop converges to zero findings
- [ ] Post-merge: confirm `showcase_deploy` rebuilds integration
containers
- [ ] Post-merge value-test: `gen-ui-interrupt` + `resume-path` cells
flip GREEN on staging dashboard for LGP, LGTS, LG-FastAPI (≥3 cells)
2026-06-17 11:55:22 -07:00
Jordan Ritter 0f1fbeac23 Showcase promote-notify workflow + canonical fixtures (PR1) (#5522)
## Summary
- New `.github/workflows/showcase_promote_notify.yml` — Slack notify
workflow (workflow_dispatch only) for promote results. Posts initiation
+ threaded reply to `#team-showcase`; cross-posts to `#oss-alerts` on
partial/total failure.
- New `showcase_promote_notify.dry-run.sh` — local render-logic mirror
(no Slack API calls); validates payload + emits expected Slack messages
for manual review.
- New `showcase/test-fixtures/promote-notify/` — three canonical
fixtures (success / partial / total-failure), a strict schema validator,
and a README.
- New `docs/runbooks/showcase-promote-notify-pr1-checklist.md` —
pre-merge runbook with Slack-membership checks, dispatch-fixture
commands, and schema-mismatch test.

## Why this is PR1
PR1 lands the notify workflow with no callers. The CLI (`bin/railway
promote --notify`) lands in PR2. Splitting is required because `gh
workflow run` resolves the workflow file from the repo's default branch
— PR2's CLI cannot dispatch a workflow that doesn't yet exist on `main`.

## Test plan
- [ ] Pre-merge checklist passes (see
`docs/runbooks/showcase-promote-notify-pr1-checklist.md`)
- [ ] Required `SLACK_BOT_TOKEN` secret is set with scopes `chat:write`,
`chat:write.public`, `users:read.email`
- [ ] Bot is a member of `#team-showcase` AND `#oss-alerts` (or scope
sufficient for public posts)
- [ ] All 3 canonical fixtures dispatched via `gh workflow run --ref
<pr-branch>` produce the expected Slack messages
- [ ] Schema-mismatch test (step 5 of runbook) produces `::warning::`
annotation with NO Slack API call

## Follow-up
A bucket(d) list of defensive-hardening items was deferred to a
follow-up PR (notify workflow defensive hardening). The CR loop (4
rounds, 7 unbiased agents each) identified ~50 items across categories:
silent-Slack-failure exit codes, validator strictness gaps (ISO-8601
fractional seconds, enum constraints), dry-run/workflow parity
(lookup-failure simulation, permalink-empty fallback), payload
defensive-validation (cross-field, run_id source-of-truth). Per cr-loop
convergence-audit: these are PR1-adjacent but their own subject; they'll
land in a focused follow-up PR.

Additionally, local `actionlint` (not currently in CI for this workflow)
flags one SC2034 (`failed_count` retained for workflow/dry-run parity
but not echoed) and two SC2016 (intentional single-quoted Slack-mrkdwn
backticks in `trigger_label`). Both fold into the bucket(d) follow-up.

## CR rounds
4 rounds × 7 agents = 28 reviews. Final convergence: ALL bucket(a)
findings fixed; bucket(b)/(c)/(d) preserved for follow-up. Integration
HEAD: `b65b8452d9b847f73c885854c146547ce82c3706`.
2026-06-17 11:54:10 -07:00
Jordan Ritter c308ddff16 chore(showcase): ratchet canonical pin set to 1.60.2, revert @ag-ui/mastra bump
- showcase-canonical-pins.json: bump canonicalCopilotKitVersion 1.59.4 -> 1.60.2;
  remove ms-agent-harness-dotnet override (caught up to canonical in prior commit).
- fail-baseline.json: re-ratchet validatePinsFailCount 39 -> 38 and hash to match
  the one-item drop (ms-agent-harness-dotnet override no longer counted).
- @ag-ui/mastra: revert 0.2.4 -> 0.2.1-beta.2. 0.2.4 imports
  '@mastra/core/runtime-context' which the pinned @mastra/core@1.41.0 does not
  export, breaking 'next build' (failing mastra build-check in CI). Holding
  @ag-ui/mastra at the prior pin until a coordinated @mastra/core upgrade lands.
2026-06-17 11:46:19 -07:00
Alem Tuzlak 6cec41bf64 feat(examples): run Slack and Discord from one bot app 2026-06-17 11:37:38 -07:00
Alem Tuzlak 817f8c718d docs(bot-discord): add Discord adapter reference docs 2026-06-17 11:36:42 -07:00
Alem Tuzlak d1f64763d5 feat(bot-discord): add Discord platform adapter 2026-06-17 11:36:24 -07:00
Jordan Ritter b957f955e0 chore(showcase): align @copilotkit/* + @ag-ui/* deps across integrations
Aligns dependency versions across all 19 showcase integrations to current
released minor versions for the 1.60.2 release cycle.

Package families:
- @copilotkit/{a2ui-renderer, react-core, react-ui, runtime, shared, sdk-js, voice}
  1.59.4 -> 1.60.2 (18 integrations already staged; ms-agent-harness-dotnet
  catches up from 1.57.2)
- @ag-ui/{client, core, encoder} 0.0.55 -> 0.0.57
- @ag-ui/mastra 0.2.1-beta.2 -> 0.2.4 (stable on 0.x; 1.0.x major held back)

Includes the previously-missed ms-agent-harness-dotnet integration in the
@copilotkit/* bump, plus the @copilotkit/web-inspector override pin.

Lockfile-only reconciliation via npm install --package-lock-only
--legacy-peer-deps (cmdk@0.2.1 pre-existing react^18 peer-dep is unaffected).
2026-06-17 11:33:43 -07:00
github-actions[bot] 076a572c56 style: auto-fix formatting 2026-06-17 18:30:10 +00:00
Jordan Ritter b65b8452d9 docs(showcase): add promote-notify PR1 pre-merge runbook
Pre-merge checklist for the promote-notify workflow: Slack-membership
checks (#team-showcase + #oss-alerts), SLACK_BOT_TOKEN scope verification,
gh workflow run dispatch commands for all three canonical fixtures, and
a schema-mismatch test that verifies the workflow emits a ::warning::
annotation with NO Slack API call.
2026-06-17 11:28:58 -07:00
Jordan Ritter a70236391c test(showcase): add promote-notify canonical fixtures + schema validator
Adds three canonical fixtures under showcase/test-fixtures/promote-notify/:
success.json (29 services green), partial.json (26 green / 3 red with
mixed exit codes + categories), and total-failure.json (29 red fleet
abort with truncation-suffix sentinel).

Includes validate.sh — strict schema enforcement (rc propagation, enum +
regex assertions for run_id, pre_staging, abort_reason, category, exit
codes). And a README documenting the fixture contract + how to dispatch
them via gh workflow run.
2026-06-17 11:28:52 -07:00
Jordan Ritter 6130c1acf0 ci(showcase): add promote-notify Slack workflow + dry-run harness
Adds .github/workflows/showcase_promote_notify.yml — workflow_dispatch-only
Slack notify workflow for promote results. Posts initiation + threaded reply
to #team-showcase; cross-posts to #oss-alerts on partial/total failure.

Also adds showcase_promote_notify.dry-run.sh — a local render-logic mirror
that decodes the same payload and prints the messages the workflow would
send to Slack, without making any Slack API calls. Used by the pre-merge
runbook and by future CI fixture tests.
2026-06-17 11:28:41 -07:00
Mike Ryan 3a0e90ecaf feat(angular): Add component and feature parity with React implementation (#5516)
Mirror of #5321 from Soverius-AI:feat/ng-a2ui-exp, opened from the
MikeRyanDev fork to get a full maintainer-owned CI run.\n\nSource PR:
https://github.com/CopilotKit/CopilotKit/pull/5321\nMirrored head:
3064fb982a9bb7702fd3acc8e88abe636a86b1e5
2026-06-17 10:55:17 -07:00
Mike Ryan 8e27de4c7d fix: configure openrouter demo provider explicitly 2026-06-17 10:49:30 -07:00
Mike Ryan 843928b645 fix: align a2ui renderer export types 2026-06-17 10:49:30 -07:00
Mike Ryan e6bf906f76 fix: regenerate slack lockfile peer resolution 2026-06-17 10:49:30 -07:00
Rainer Hahnekamp 10245a35fa fix(a2ui-renderer): guard missing catalog renderer lookup
Throw when a catalog definition has no matching renderer so createCatalog
satisfies strict check-types under Angular's tsconfig.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-17 10:49:30 -07:00
Rainer Hahnekamp 8bdc16dc5d fix(angular): use bundler moduleResolution for check-types
Modern @a2ui/web_core and @angular/cdk subpath exports require bundler
resolution so angular:check-types can resolve the new A2UI imports.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-17 10:49:30 -07:00
Rainer Hahnekamp e4ce502ce9 fix(a2ui-renderer): resolve web-components check-types failures
Align surface operation normalization and catalog test fixtures with the
RendererProps contract so the changed A2UI web-components pass tsc.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-17 10:49:30 -07:00
Rainer Hahnekamp 0a887b2eda fix(angular): let ng-packagr own exports types condition
Remove the hand-written types entry from exports["."] so ng-packagr can
generate deterministic published package metadata without warnings.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-17 10:49:30 -07:00
Rainer Hahnekamp f16e9ea25c fix: remove agent configs 2026-06-17 10:49:30 -07:00
Murat Sari ebcbb19ead feat(chat): enhance chat functionality with transcription support and UI improvements
- Implemented audio transcription capabilities with error handling.
- Refactored CopilotChat component to utilize a directive for handling attachments.
- Improved CopilotChatReasoningMessage to manage streaming state and elapsed time more efficiently.
- Added new scroll view component for better message display and auto-scrolling behavior.
- Updated styles for A2UI surface components to enhance layout and scrolling.
- Enhanced tests for OpenGenerativeUIRenderer to ensure proper height measurement.
2026-06-17 10:49:30 -07:00
Rainer Hahnekamp 29acda7a87 fix(angular): address PR review feedback on component metadata
Revert open-generative-ui-tool-renderer and drop empty imports arrays.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-17 10:49:30 -07:00
Rainer Hahnekamp b5fa76d5f1 fix(angular): modernise Angular patterns in a2ui branch (#1)
Apply the common Angular CLI schematics to code which has been added
by Soverius.
We can do a follow-up for the high amount of effects, but we have to be
careful. We need tests first to do a refactoring for that.
2026-06-17 10:49:30 -07:00
Rainer Hahnekamp 2208c90e37 chore: install official Angular agent skills from angular.dev
Adds angular-developer and angular-new-app skills via `npx skills add`
from https://github.com/angular/skills into .agents/skills/.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-17 10:49:30 -07:00
Murat Sari 6a768ab7d0 feat(angular): add a2ui for angular 2026-06-17 10:49:30 -07:00
Murat Sari 800b55dacf feat: add openrouter support 2026-06-17 10:49:30 -07:00
Murat Sari 8b13fbcb7d build: update ng 2026-06-17 10:49:30 -07:00
Mike Ryan 237a176fbf chore: release monorepo v1.60.2 (#5517)
## Release monorepo v1.60.2

**Scope:** `monorepo` | **Bump:** `patch`

---

### How this release process works

1. **This PR was created automatically** by the "release / create-pr"
workflow.
   It bumped the `monorepo` packages to `1.60.2`
   and generated AI-enhanced release notes.

2. **CI runs on this PR** — the full test suite (unit tests, lint, type
checks, build)
   must pass before merging. This is the review gate.

3. **Review the release notes** in `release-notes.md` in this PR.
If a Notion draft was created, you can edit the release notes there
before merging.

4. **When this PR is merged**, the `release / publish` workflow
automatically:
   - Builds all packages
   - Publishes the `monorepo` packages to npm at version `1.60.2`
   - Creates git tag `monorepo/v1.60.2`
   - Creates a GitHub Release with the final release notes

### Before merging

- [ ] CI is green (tests, lint, types, build)
- [ ] Version bumps look correct
- [ ] Release notes are accurate (edit in Notion if a draft was created)

---

> **Do not merge until CI is fully green.** The full test suite runs
automatically on this PR.
v1.60.2
2026-06-17 10:46:56 -07:00
Sam Julien 5345f8f8fd docs: clarify shell-docs hybrid authoring (#5519)
## Summary
- document the shell-docs hybrid docs architecture and `docs_mode`
meanings
- add a shell-docs README with npm local dev, validation commands, and
authoring recipes
- keep retired top-level `docs/` guidance intact while making shell-docs
the clear source of truth

## Verification
- `git diff --check -- .claude/docs/documentation.md
showcase/shell-docs/README.md`
- pre-commit: `nx run-many -t test --projects=packages/**` passed after
rerun; Nx flagged `@copilotkit/react-core:test` as flaky from an earlier
timeout
- pre-commit: `nx run-many -t publint,attw --projects=packages/**`
passed

## Notes
- Archive refs were created before this branch:
`archive/docs-save-do-not-prune` and `archive/docs-retired-2026-06-17`.
- This PR intentionally does not delete the retired top-level `docs/`
tree; that should be a follow-up cleanup PR.
2026-06-17 10:44:48 -07:00
Sam Julien 2d3bafd61c docs: clarify shell-docs hybrid authoring 2026-06-17 10:18:13 -07:00
Tyler Slaton 506997f4f9 showcase(docs): update premium features to be enterprise (#5511)
Updating the premium features section to instead be "enterprise" with
some reworked documentation pages.
archive/docs-retired-2026-06-17
2026-06-17 10:06:04 -07:00
davidmckayv 692e52244c chore: release monorepo v1.60.2 2026-06-17 16:59:44 +00:00
Tyler Slaton 8ec7d0d4e5 docs(shell-docs): restore enterprise intelligence product name 2026-06-17 09:16:29 -07:00
Jordan Ritter 2842e39f8e fix(showcase/harness): retry+cached-catalog producer enumerate + 3-tick family-silence threshold (#5515)
## Summary

Harden the showcase harness producer against transient Railway-GQL 429 /
Cloudflare-WAF flaps. Today's incident: a ~25-min Cloudflare WAF
burst-block on `backboard.railway.com/graphql/v2` caused the producer's
catalog-enumerate to hard-fail every cron tick, zeroing out D4/D5/D6
writes and turning the entire staging dashboard red within one tick
window.

Three discrete behavior changes, one PR:

1. **Retry with exponential backoff in `source.enumerate`** — three
retries at 1s/4s/16s on HTTP 429, 5xx, Cloudflare 1015/1020/1022
markers, or transport-level errors. Does NOT retry on
`DiscoverySourceAuthError`, non-429 4xx, or schema errors
(operator-actionable, fail loud). Lives in
`showcase/harness/src/fleet/control-plane/catalog-enumerator.ts` (the
seam every family enumerator passes through).
2. **Cached-catalog fallback** — per-enumerator in-memory cache of the
last successful `services[]`. On persistent failure (all retries
exhausted) AND a cache present, the wrapper logs
`fleet.producer.enumerate-failed-using-cache` (warn, with `services`
count, `ageMs`, and `reason`) and returns the cached catalog. With NO
cache (fresh-boot first enumerate fails), the wrapper re-throws so the
producer's `enumerate-failed` short-circuit still runs — without a
catalog there's nothing to enqueue.
3. **3-tick family-silence threshold** —
`SILENCE_CONSECUTIVE_TICK_THRESHOLD = 3` layered ON TOP of the existing
`3 × period` elapsed-time gate. The silence alert now requires BOTH:
`now - lastSuccessAt > 3 × period` AND three consecutive evaluation
cycles observed silent. A single bad tick on a stale `lastSuccessAt` can
no longer page every family at once.

## Red-Green proof

**RED on main (`5a62acbf`)** — observed BEFORE the fix. The RED file
asserts the BUG (`calls === 1` after a 429 throw; `posts.length === 1`
after a single silent tick):

```
> vitest run src/fleet/control-plane/red-baseline-railway-gql-resilience.test.ts
RUN  v3.2.4 .../showcase/harness
✓ src/fleet/control-plane/red-baseline-railway-gql-resilience.test.ts (2 tests) 3ms
  ✓ [BUG] one 429 + Cloudflare 1015 from the source aborts the whole enumerate (no retry)
  ✓ [BUG] silence alert posts on the FIRST silent evaluation tick (no consecutive-tick threshold)
Test Files  1 passed (1)
     Tests  2 passed (2)
```

**GREEN on fix branch** — observed AFTER the fix (assertions inverted to
the fixed behavior; `calls === 4` after retries; `posts === []` until
the third silent tick):

```
> vitest run src/fleet/control-plane/red-baseline-railway-gql-resilience.test.ts src/fleet/control-plane/catalog-enumerator.test.ts src/fleet/control-plane/family-silence-monitor.test.ts
RUN  v3.2.4 .../showcase/harness
✓ src/fleet/control-plane/red-baseline-railway-gql-resilience.test.ts (2 tests) 4ms
✓ src/fleet/control-plane/family-silence-monitor.test.ts (19 tests) 8ms
✓ src/fleet/control-plane/catalog-enumerator.test.ts (29 tests) 6ms
Test Files  3 passed (3)
     Tests  50 passed (50)
```

Full harness suite — **131 files / 2812 tests pass** (`pnpm -F
@copilotkit/showcase-harness test`).

## Test plan

- [x] RED proof captured on `main` (single-attempt enumerate;
single-tick silence alert)
- [x] GREEN proof on this branch (retry to 4 calls; cached fallback;
3-tick threshold)
- [x] `pnpm -F @copilotkit/showcase-harness test` — 2812 passed
- [x] `pnpm -F @copilotkit/showcase-harness typecheck` — clean
- [x] `pnpm -F @copilotkit/showcase-harness build` — clean
- [x] `oxfmt --write` applied; `oxlint` clean
- [ ] CI green
- [ ] Staging redeploy verified via Railway CLI + Playwright dashboard
snapshot

## Files touched

- `showcase/harness/src/fleet/control-plane/catalog-enumerator.ts`
(+296, -3): retry+cache wrapper, exports `ENUMERATE_RETRY_BACKOFF_MS` +
`isRetryableEnumerateError` + `SleepFn`
- `showcase/harness/src/fleet/control-plane/catalog-enumerator.test.ts`
(+295): retry/cache/auth-not-retried/backoff-SSOT tests
- `showcase/harness/src/fleet/control-plane/family-silence-monitor.ts`
(+61): `SILENCE_CONSECUTIVE_TICK_THRESHOLD = 3`, per-family counter,
counter reset on healthy
-
`showcase/harness/src/fleet/control-plane/family-silence-monitor.test.ts`
(+218, -39): 3-tick threshold + counter-reset gate tests; updated
existing tests to advance through 3 silent ticks
-
`showcase/harness/src/fleet/control-plane/red-baseline-railway-gql-resilience.test.ts`
(new, +212): the literal RED→GREEN gate

## Operational notes

- No new env vars, feature flags, or backward-compat shims (per scope
directive).
- The cached-catalog warn surfaces in observability via
`fleet.producer.enumerate-failed-using-cache` (services count, ageMs,
reason).
- `SLACK_WEBHOOK_OSS_ALERTS` is intentionally unset (user config); not
touched.

Pre-existing repo-wide lefthook failures (`@copilotkit/core`,
`@copilotkit/runtime`, `@copilotkit/shared` etc.) reproduce on `main`
without my changes and are unrelated to harness code; harness-scoped
quality gates all passed before commit.
2026-06-17 08:28:49 -07:00
Jordan Ritter 5f00cb9771 ci(release): one-click canary publish orchestrator + release-pipeline lint guards (#5370)
## Summary

Ports
[ag-ui-protocol/ag-ui#1914](https://github.com/ag-ui-protocol/ag-ui/pull/1914)
to CopilotKit — plus the two supporting guard files ag-ui already had:

- **`.github/workflows/canary.yml`** — discoverable **`canary /
publish`** `workflow_dispatch` orchestrator. Any maintainer can publish
a prerelease of the branch they're on straight from the Actions tab. It
is a thin orchestrator — it does **not** publish to npm itself:
  1. Guards against `main` and non-branch refs.
2. Mints the devops-bot App token (app-id `1108748`,
`DEVOPS_BOT_PRIVATE_KEY`) with scoped `contents:write` +
`actions:write`.
3. Mirrors the dispatched ref to a unique
`canary/<slug>-<run_id>-<attempt>` branch via the GitHub API (no
checkout).
4. Dispatches **`publish-release.yml --ref canary/<slug> -f
mode=prerelease …`**, locates the run, and waits (`gh run watch
--exit-status` + explicit conclusion check).
5. Deletes the canary ref — status-gated (never yanks the ref under a
still-running delegated run) with a fresh cleanup token (90-min job
ceiling exceeds the 1h App-token TTL).
- **`scripts/release/verify-release-scope-dropdowns.sh`** — drift guard:
the hand-maintained `scope` dropdowns in `publish-release.yml` /
`stable-release.yml` / `canary.yml` must exactly match
`release.config.json`'s `.scopes` keys. Parsers fail loud and distinct
on structural changes instead of silently passing.
- **`.github/workflows/lint-release-workflows.yml`** — actionlint +
shellcheck + the dropdown-sync job over the release pipelines.

### Why a separate orchestrator (and not a flag in publish-release.yml)
- A GitHub Environment's deployment-branch policy is evaluated against
the ref a run is **triggered on** — not branches created mid-run. The
orchestrator exists to get the publish run *onto* a `canary/*` ref.
- `publish-release.yml` holds the **single npm OIDC trusted-publisher
binding**; a second publishing entry point would break OIDC for every
`@copilotkit/*` package. The orchestrator never touches npm.
- The cross-workflow dispatch uses the **App token, not `GITHUB_TOKEN`**
— `GITHUB_TOKEN`-authenticated events never start new workflow runs.

**Note:** the `npm` environment currently has *no* deployment-branch
policy, so the orchestrator is a convenience wrapper today. Tightening
the policy to `main` + `canary/*` + `release/publish/*` (matching
ag-ui's security posture) is being applied as repo configuration
alongside this PR — requires admin. This PR includes the prerequisite:
`publish-commit.yml` (pkg-pr-new) is removed from the `npm` environment,
since it runs on every PR and would be blocked by the policy (it
publishes to pkg.pr.new, not npm, and uses no environment secrets).

## Testing done
- Drift guard: positive run against all three real workflows; negative
tests (scope removed → drift FAIL with diff; bogus scope → FAIL; `case
"${SCOPE}"` quoting refactor → loud parser-degradation FAIL; whole case
block deleted → loud zero-block FAIL; quoted arm `"angular")` →
accepted; blank/comment lines inside `options:` → still parsed; prose
comments mentioning case/SCOPE/in → no false positive).
- `shellcheck` clean at all severities; `bash -n` on every workflow
`run:` block; YAML parses.
- 3 rounds of 7-agent code review converged to zero load-bearing
findings.

## ⚠️ Still to verify before first real use
- [ ] devops-bot App (id 1108748) has **Actions: write** — required for
the in-workflow `gh workflow run`. Safe first test: dispatch once with
`dry_run=true`.
- [ ] First `dry_run=false` run clears the `npm` environment end-to-end
via the App token once the deployment-branch policy is tightened.

🤖 Generated with [Claude Code](https://claude.com/claude-code)


## Post-merge follow-ups (maintainer action required)

These need repo **admin** rights and must happen **in this order**:

1. **Merge this PR first.** `main`'s current `publish-commit.yml`
(pkg-pr-new) still sits in the `npm` environment and runs on every PR
touching `packages/**` — applying the branch policy before this PR lands
would block every snapshot publish. This PR removes that environment
association.

2. **Tighten the `npm` environment's deployment-branch policy** to
`main` + `canary/*` + `release/publish/*` (matching ag-ui). With an
admin-scoped token:

   ```bash
   gh api --method PUT repos/CopilotKit/CopilotKit/environments/npm \
     -F "deployment_branch_policy[protected_branches]=false" \
     -F "deployment_branch_policy[custom_branch_policies]=true"
gh api --method POST
repos/CopilotKit/CopilotKit/environments/npm/deployment-branch-policies
-f name="main" -f type=branch
gh api --method POST
repos/CopilotKit/CopilotKit/environments/npm/deployment-branch-policies
-f name="canary/*" -f type=branch
gh api --method POST
repos/CopilotKit/CopilotKit/environments/npm/deployment-branch-policies
-f name="release/publish/*" -f type=branch
   ```

Or via UI: Settings → Environments → npm → Deployment branches and tags
→ "Selected branches and tags" → add the three patterns above.

Why these three: `main` covers stable `workflow_dispatch` retries and
`stable-release.yml`; `release/publish/*` covers the merged-release-PR
runs (the run's head branch is the release PR branch); `canary/*` covers
the orchestrator's delegated prerelease runs. After this, direct
`mode=prerelease` dispatches from arbitrary feature branches stop
working — the `canary / publish` orchestrator becomes the one-click path
(by design).

3. **Verify the devops-bot App (id `1108748`) has `Actions: write`**
(org/App settings). The orchestrator's `gh workflow run` dispatch 403s
without it. Safe end-to-end test, after step 2: Actions tab → **canary /
publish** → pick any feature branch, any scope, **`dry_run=true`** →
confirm the delegated `release / publish` run is created, watched, and
the `canary/*` ref is deleted afterward.

4. **First real canary** (`dry_run=false`) confirms the npm OIDC publish
clears the environment gate end-to-end on a `canary/*` ref.
2026-06-17 08:22:44 -07:00
Jordan Ritter c8a9053bee test(showcase/harness): red-green gate for Railway-GQL resilience + 3-tick silence threshold
Adds an integration-style test file that pins the BOTH layers of the
2026-06-17 Cloudflare-WAF-burst incident fix together:
  - the enumerator retries 3× on a 429+Cloudflare-1015 burst before
    bubbling (the original bug let one 429 abort the whole enumerate);
  - the silence monitor requires THREE consecutive silent evaluation
    cycles before posting an alert (the original bug fired on tick #1).

These assertions were the LITERAL red proof on `main`:
  - on main both `it` blocks PASSED while asserting the buggy behavior
    (calls === 1, posts.length === 1 after a single silent tick),
  - on this branch the same gates re-pin the fixed behavior (calls === 4
    after retries; posts === [] until the third silent tick).

Run-time output captured for the PR body confirms the inversion.
2026-06-17 08:21:01 -07:00
Jordan Ritter 94f6d87f54 fix(showcase/harness): require 3 consecutive silent ticks before family-silence alert
Layer a per-family consecutive-silent-tick counter ON TOP of the existing
3×period elapsed-time gate (`SILENCE_PERIOD_MULTIPLIER`). The silence
alert now requires BOTH:
  - `now - lastSuccessAt > 3 × period` (existing elapsed-time gate), AND
  - `SILENCE_CONSECUTIVE_TICK_THRESHOLD = 3` consecutive evaluation cycles
    observed silent (new — the counter resets on any successful evaluation).

Without the new gate a single bad cron tick on a family whose
`lastSuccessAt` was already stale (e.g. after a long quiet window or a
deploy gap) tripped the alert immediately — the failure mode the
2026-06-17 Cloudflare-WAF-burst incident exposed where one ~25 min flap
on backboard.railway.com/graphql/v2 paged every family at once.

The counter is NOT incremented during boot grace, so a cold-start cycle
can't alone push it to threshold. The meta-alert (`family-silence-eval`)
path keeps its own clock and is unaffected. Existing tests advance
through three consecutive silent ticks before asserting the post.
2026-06-17 08:20:51 -07:00
Jordan Ritter 7f118c5955 fix(showcase/harness): retry+cached-catalog producer enumerate (Railway-GQL resilience)
Three-retry exponential backoff (1s/4s/16s) on `source.enumerate` against
Railway-GQL when the underlying error is transient (HTTP 429, 5xx, or a
Cloudflare 1015/1020/1022 WAF marker, or a transport-level reject). On
persistent failure, fall back to the last successful catalog from a
per-enumerator in-memory cache — LOUDLY logged via
`fleet.producer.enumerate-failed-using-cache` so the cache-use shows up
in observability.

A fresh-boot process with no cached entry preserves the current
hard-fail behavior (the producer's `enumerate-failed` short-circuit) —
without a catalog there is nothing to enqueue. Real config errors
(`DiscoverySourceAuthError`, non-429 4xx, schema rot) are NOT retried so
operator-actionable failures surface immediately.

Context: 2026-06-17 Cloudflare WAF burst-blocked
backboard.railway.com/graphql/v2 for ~25 min, hard-failing the producer
enumerate on every cron tick and zeroing out D4/D5/D6 writes — the
entire staging dashboard went red within one tick. Retries + cache ride
out the burst on the same tick and preserve job production across
longer outages.

Pre-existing repo-wide lefthook test failures (@copilotkit/core,
@copilotkit/runtime, @copilotkit/shared, etc.) are unrelated to the
harness; verified by stashing my changes and running the same hook on
main with identical failures. Harness suite (131 files / 2812 tests),
typecheck, and build pass on this branch.
2026-06-17 08:20:40 -07:00