Compares the workflow_dispatch scope choice dropdowns in
publish-release.yml, stable-release.yml, and canary.yml against the
authoritative .scopes keys in release.config.json, failing CI on drift.
Also validates that every explicitly-named arm in publish-release.yml's
notify-job npm-url case statement is a valid scope (catch-all makes
full coverage unnecessary). Parsers fail loud and distinct on shape
changes (anchored case detection, loose-vs-strict cross-check,
zero-options and zero-block guards) rather than silently passing.
Ported from ag-ui-protocol/ag-ui (PR #1914 wiring); config lives at
the repo root in CopilotKit.
Round-2 review fixes for the GITHUB_OUTPUT helper and the release scripts
that emit through it.
emitGithubOutputs (scripts/release/lib/github-output.ts):
- Replace the key newline/CR check with a full GitHub-Actions-safe charset
check: /^[A-Za-z_][A-Za-z0-9_-]*$/. A key containing "=" or whitespace
would silently corrupt the key=value line; rejecting up-front is
strictly safer. Value validation (single-line) is unchanged — "=" in
values is legal because GitHub splits on the first "=".
- Update the docblock accordingly.
prerelease.ts:
- Remove the dead `?? getCurrentVersion(scope)` fallback. The empty-list
guard above makes packages[0] guaranteed, and the fallback would have
masked a package.json missing its version field by emitting a version
divergent from what the loop publishes. Fail loudly with an explicit
exit instead.
- Drop the now-unused getCurrentVersion import.
- Add a comment above the dry-run emitGithubOutputs call explaining that
emitting in dry-run is safe — the publish workflow gates publish + the
verify guard on inputs.dry-run != true, so the dry-run emission only
serves local/e2e contract verification.
publish-release.ts:
- Hoist getPackagesForScope + empty-list guard above the prerelease-suffix
and registry checks. A misconfigured scope now fails with the clear
"no packages found" error instead of a misleading "not greater than
published" one. Loop is unchanged.
github-output.test.ts:
- Loosen the key-newline assertion from the JSON.stringify-coupled
/bad\\nkey/ to the stable /alphanumeric/ phrase from the new message.
- Add tests: "=" in key throws, space in key throws, empty key throws,
and "=" in value is accepted and written verbatim (note=a=b).
- Move vi.restoreAllMocks() to the top of afterEach so spies cannot leak
into env restore + rmSync cleanup.
Call sites audited:
- emitGithubOutputs: only ever called with {version, scope} (prerelease,
publish-release) — all valid under the new charset.
- publishVersion derivation: only used inside prerelease.ts main().
- getCurrentVersion: still imported by publish-release.ts, bump-prerelease.ts,
prepare-release.ts; only the prerelease.ts import was removed.
- getPackagesForScope hoist in publish-release.ts: `packages` was only
read inside the publish loop below; nothing earlier depended on it.
Hardens the new GITHUB_OUTPUT emission path so a malformed value can't smuggle
extra `key=value` lines into the workflow's step outputs, and so the workflow's
"Verify publish step emitted version" guard can't be fooled by a publish that
did nothing.
emitGithubOutputs now validates every key/value for `\n`/`\r` BEFORE the
GITHUB_OUTPUT early-return — a malformed value is a caller bug and should fail
loudly even when running locally. A multi-line value would need the heredoc
form, which this helper deliberately does not support.
prerelease.ts and publish-release.ts now fail loud when getPackagesForScope
returns an empty list. Without this, the new GITHUB_OUTPUT emission would make
the workflow's "Verify publish step emitted version" guard pass on a run that
published nothing — previously the missing output made such a run fail. The
guard runs BEFORE the dry-run branch in prerelease.ts. In publish-release.ts,
the inline iteration of getPackagesForScope(scope) is hoisted to a `packages`
const so the same guard fires before the publish loop.
The "no-op when GITHUB_OUTPUT is unset" test now spies on fs.appendFileSync
and asserts it wasn't called (the previous read of the unrelated temp file
was vacuously true). New tests cover newline/CR in value and newline in key.
The prerelease.ts usage string previously advertised `[--suffix <label>]`,
but the script never parses --suffix (suffix handling lives in
bump-prerelease.ts per the header comment). Removed.
Call sites enumerated:
- emitGithubOutputs: prerelease.ts (dry-run + post-publish), publish-release.ts
- getPackagesForScope: prerelease.ts, publish-release.ts (this commit);
bump-prerelease.ts, prepare-release.ts, versions.ts (not changed — out of
scope for this hardening)
Verification:
- npx vitest run --config scripts/release/vitest.config.mts → 91 passed
- Red-green for the newline validation: temporarily removed the validation,
the 3 new newline/CR tests failed (assertion: expected fn to throw); restored,
back to green.
- E2E: GITHUB_OUTPUT="$OUT" pnpm release:prerelease:dry succeeded and the
output file contained `version=1.59.5` and `scope=monorepo`.
Note: Fix 2's empty-list guard fires only on a misconfigured scope (no unit
test reachable — prerelease.ts is outside the vitest include glob and the
guard is boundary validation against a misconfigured scope, not a behavior
worth contriving a test harness for).
prerelease.ts published canaries successfully but never wrote the
version output the publish-release workflow's "Verify publish step
emitted version" guard reads, so every canary dispatch ended red after
a successful publish. Extract the GITHUB_OUTPUT append (previously
inline in publish-release.ts) into a shared lib/github-output.ts helper
and call it from both publish scripts.
Call-site enumeration:
- emitGithubOutputs: declared lib/github-output.ts; called from
prerelease.ts (dry-run path + after publish) and publish-release.ts
(replaces the inline appendFileSync block, same version=/scope= keys).
- No symbols removed; fs import in publish-release.ts still used (3
remaining call sites).
Conflict resolutions:
- contract test: keep the rollout's parameterized version and add
strands-python to migratedIntegrations/appRoots (60/60 passing) in
place of her bespoke MIGRATED_INSTANCES file
- parity manifest: rollout's version with strands' three threads-shield
allowances removed (mirrors the langgraph-fastapi migration); parity
verify green — strands now 88 tracked files, zero drift
- package-lock: regenerated at 1.59.3 (a2ui-renderer stays 1.56.5,
the family-wide pin shared with the north-star)
Also rides: her react-core A2UIMessageRenderer test flake fix
(act -> waitFor), kept intentionally.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Conflict resolution: take the rollout's contract test and append the
agentcore describe block (CDK lambda runtime gate, Vite frontend with
import.meta.env gate, docker env wiring). Bump both agentcore
package.jsons 1.59.1 -> 1.59.3 + regen lockfiles. 54/54.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Conflict resolution: take the rollout's parameterized contract test and
append the a2a-a2ui bespoke tests (namespaced helper, 1.59.3 pins).
Bump a2a-a2ui @copilotkit/* 1.59.1 -> 1.59.3 + regen lockfile. 49/49.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- bump a2a-middleware, mcp-apps, agent-spec from 1.59.1 (their verified
pre-revert state) to 1.59.3 to match the starters
- regenerate package-lock.json for the 11 examples whose package.json
changed (drawer deps re-added on starters, version bumps on the three)
- update the migration contract test's version assertions to 1.59.3
(43/43 passing); oxfmt pass
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Restores #5151 (north-star + batch 1 + crewai-flows + llamaindex),
#5196 (pydantic-ai), #5205 (a2a-middleware), #5211 (mcp-apps), reconciled
onto the current main baseline rather than the pre-revert tree:
- keep main's 1.59.3 pins, AGENT_URL normalization, default agent keys,
useConfigureSuggestions, available:false, useRenderTool
status/parameters API, call-time agent.state reads, and crewai-crews'
rebuilt page (not yet threads-migrated)
- graft the threads layer (drawer/gate/provider, env-gated route
intelligence block, next.config gate, env docs, drawer deps) on top
- drop threads-era sidebar suggestions props where main now registers
suggestions via useConfigureSuggestions (or omits them)
- fix the stale pydantic-ai doc link main reintroduced in ms-af-dotnet
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add a pure builder that renders one concise #oss-alerts Slack message
per release from a unit-tested truth table: suppresses canary/dry-run
runs, gates npm/PyPI failure arms on event-derived release intent, and
pages on uncertain state. The CLI wrapper serializes env input to the
builder and writes the result to GITHUB_OUTPUT, failing loud on any
malformed or missing input.
Post-merge fix: add the missing examples/integrations/mcp-apps/.env.example
(+ .gitignore negation, crewai-flows precedent) — the contract test
'mcp-apps documents the local Intelligence environment' was failing
because the file was never added. 37/37 passing on the merged tree.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Conflict resolution: take main's 4-entry migratedIntegrations array and
keep the a2a-middleware-specific contract tests + helper (a2a-middleware
uses app/ root + custom chat, so it doesn't fit the parameterized
src/app contract). 31/31 passing.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Conflict + post-merge resolutions:
- integration test (add/add): fold into the parameterized batch-2 contract
test; migratedIntegrations now includes langgraph-fastapi (24/24 pass).
- _parity/manifest.json: keep her langgraph-fastapi allowance removals
(threads-drawer/page.tsx/next.config now fully tracked), but drop the
stale strands-python shielding her branch added (example-layout +
docker-route-override divergence, @copilotkit 1.56.5 packageJsonOverrides)
— strands is already parity-synced at 1.59.1 on main; those overrides
would have made the next parity:sync rewrite it back to 1.56.5.
- parity verify on the merged tree: langgraph-js 82 ok / langgraph-fastapi
91 ok / strands-python 83 ok, zero drift.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Conflict resolutions:
- page.tsx: keep the threads layout; swap the dead v1 sidebar labels
(title/initial) for the v2 keys (modalHeaderTitle 'Popup Assistant' +
welcomeMessageText) the starter smoke asserts, and carry over main's
disableSystemMessage + clickOutsideToClose. Keep her useRenderTool
render({ parameters }) — that is the actual v2 RenderToolProps shape;
main's render({ args }) was a latent runtime bug (args is not a prop).
- package.json: keep the drawer UI deps (class-variance-authority, clsx).
- integration test: fold into the parameterized batch-2 contract test;
migratedIntegrations now crewai-flows + llamaindex + pydantic-ai (18/18).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Conflict resolutions:
- page.tsx: keep the threads layout (drawer + gate + provider) and restore
the v2 sidebar labels (modalHeaderTitle 'Popup Assistant' +
welcomeMessageText) the starter smoke asserts, plus clickOutsideToClose.
- layout.tsx: keep the REST-transport explainer comment.
- integration test: keep the parameterized batch-2 contract test from
crewai-flows and add llamaindex to migratedIntegrations (12/12 pass).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Right-pad version tuples so 0.2 and 0.2.0 compare equal (PEP 440), avoiding a
duplicate-version publish that PyPI rejects. Exclude fully-yanked releases when
computing the published max so a yanked high version can't block real bumps.
Fail loud when a 200 response lacks a releases key instead of assuming the
package is new. Surface curl transport errors and add red-green coverage.
Compare against the max numeric version in PyPI `releases` rather than
`info.version` (latest-uploaded, not highest). Apply strict dotted-numeric
validation to the LOCAL version only; non-numeric published versions
(prereleases) are filtered out instead of aborting the script. Add curl
--max-time/--retry hardening and red-green test coverage for both cases.
publish-release.ts now checks npm for each package before publishing
and skips versions that already exist. Makes publish idempotent —
safe to retry after partial failures. Removes the publish-remaining
one-shot workflow that's no longer needed.
Replace pnpm publish with pnpm pack + npx npm@11.15.0 publish to
enable OIDC authentication. Set NODE_AUTH_TOKEN='' to prevent the
expired secret from blocking OIDC. Removes test workflow.