Adopts the visual language from the featured-Accordion work — accent panel,
terminal mark, eyebrow, prominent copy action — for the shared Channels entry
point, and drops the disclosure it was attached to. The accordion existed
because the payload was twenty lines; the payload is now one sentence, so
hiding it behind "Open & copy prompt" costs a click and buys nothing.
The panel renders exactly the text the button copies. Two earlier shapes were
wrong in instructive ways: a full monospace paragraph wrapped like a rendering
bug, and a code block with the ask underneath read as a shell command with a
footnote, which made a button labelled "Copy prompt" look like it was lying.
Only the command is monospace now; the prose around it wraps like prose.
The featured Accordion variant stays in mdx-components as a shared opt-in
capability, unused for the moment.
## Summary
- add an opt-in featured treatment to the existing docs `Accordion`
component
- use that treatment for the coding-agent starter prompts at the top of
both the Slack and Microsoft Teams Channels overview pages
- replace the easily missed accordion label with a branded prompt panel,
a terminal icon, platform-specific supporting copy, and a prominent
**Open & copy prompt** action
- preserve the existing native `details` interaction and code-block copy
control inside the expanded prompt
- update the Channels documentation regression test for the new featured
markup and provider-specific descriptions
## Why
The existing “copy this prompt” accordion looked like ordinary
supporting content and was easy to skip at the top of the overview
pages. This change gives the starter prompt a clear visual hierarchy and
action without changing the prompt content or introducing a new
navigation concept.
The visual treatment deliberately uses a terminal icon rather than a
generic sparkle so it reads as a developer workflow for a coding agent.
## User impact
People landing on either Channels overview page can now immediately
recognize that there is a ready-to-use setup prompt. Slack and Teams
retain their own provider-specific instructions, while the interaction
remains familiar:
1. Open the featured prompt.
2. Review the provider-specific setup instructions.
3. Use the existing copy control on the prompt code block.
Unrelated accordions retain their current appearance because the new
treatment is opt-in.
## Implementation notes
- reuses the existing CopilotKit docs design tokens for color, spacing,
borders, radii, focus states, and dark mode
- keeps the native `details`/`summary` semantics and keyboard behavior
- adapts the header and action layout at narrower widths
- changes the action label to **Close prompt** while expanded
- leaves the published Slack and Teams prompt bodies unchanged apart
from replacing two em dashes with colons for readability
- adds no new dependencies
## Validation
- `npm test -- src/lib/__tests__/channels-docs.test.ts --maxWorkers=1
--no-file-parallelism` — 29 tests passed
- `npm run typecheck`
- `pnpm exec oxfmt --check
showcase/shell-docs/src/components/mdx-components.tsx
showcase/shell-docs/src/content/docs/channels/index.mdx
showcase/shell-docs/src/lib/__tests__/channels-docs.test.ts`
- `git diff --check`
- pre-commit lint and commit-message hooks passed
- Impeccable design detector returned no findings
- manually reviewed Slack and Teams in collapsed and expanded states,
light and dark themes, and a narrower responsive viewport
- confirmed no browser console errors during local review
## Scope
This PR is intentionally limited to the shared opt-in accordion
treatment, the two Channels overview prompt instances, and their focused
regression coverage. It does not restyle existing documentation
accordions or alter the underlying setup guidance.
Keeps the featured Accordion treatment from #6356 as a shared opt-in component.
Its two consumers — the Slack and Teams starter prompts on the Channels
overview — are replaced here by the shared entry-point component, because the
payload those accordions concealed is now one line and there is nothing left to
disclose.
The Channels overview page, the docs landing activation strip, the website's
/channels strip, and the channels-sdk README each carried their own copy of the
onboarding workflow — six versions across three repos. They drifted, and each
went stale against the CLI independently: developers were told to install
unnamed skills, run `copilotkit channels` (which the published CLI does not
have), and use a bare `npx copilotkit` that a cached older binary shadows.
Every surface now emits the same two sentences naming one skill, and the
workflow itself lives in that skill. Shipped here:
- `buildChannelsActivationPrompt` returns a pointer, not a workflow, built from
a single `CHANNELS_ONBOARDING_SKILL` constant.
- The overview page's two 20-line prompts — one Slack, one Teams, both hidden in
accordions — collapse into the shared `<ChannelsStartPrompt />`.
- The prompt text renders on screen instead of living only in a clipboard
payload. That is why it was missable: a copy button with an invisible payload
reads as decoration. At one line there is nothing left to hide behind an
accordion.
- Both surfaces emit `promptCopied` with a `surface` property, so the funnel can
answer which road people actually take.
Tests pin the corrections that drift produced: the skill is named, the install
is non-interactive, the CLI is `@latest`, and the overview cannot re-embed a
workflow.
Corrects the repo-local `reskin` skill (`.claude/skills/reskin/`), which
in several places teaches guidance that produces a broken or subtly
defective skin when followed literally.
Docs only — no application source changes.
## Why now
The `logistics` skin (#6302) was built by following this skill and then
debugged against a live app. Every fix below is a defect that build
actually surfaced, or that an audit of the skill against the three
shipped skins turned up. Where the skill and `src/skins/logistics/**`
disagreed, the skill was the one that was wrong.
## The two that shipped a visibly broken skin
**The layout template taught `min-h-screen`.** That is a *minimum*, so
on any page taller than the viewport the container grows, the whole
document scrolls, and the pinned nav scrolls away with it — and
`<main>`'s `overflow-y-auto` goes inert, because its parent is
unbounded. Now `h-screen overflow-hidden` on the root plus `h-full` on
the `<aside>`.
**The template never published `--nw-nav-inset-left` /
`--nw-nav-inset-right`.** Every shipped skin does. Without them the
shell's floating skin selector docks on top of the new skin's nav.
Added, including the cleanup that removes both — a missing cleanup leaks
the inset into whatever skin the user switches to next.
## The one that is invisible to every gate
**No mention of the dependency array** on `useComponent` /
`useFrontendTool` / `useHumanInTheLoop`. Omit it and the render closure
captures whatever the data was at registration time — for a REST-backed
skin, the empty array from before the first fetch — permanently.
This is the nastiest bug in the app because it **compiles, lints, and
passes every test**. The symptom is the agent narrating confidently over
a broken surface:
```
agent: "PO-88213 trade-offs are on screen."
UI: "No shipment matches that reference."
```
Banking already documented the trap in a code comment; the skill never
did. Every template registration now closes with its deps array.
## Also fixed
- **Meta-utility strip** — new section documenting that the presenter
reset / theme toggle / help controls are *skin-authored*, not
shell-provided. Covers the `usePresenterReset()` gate, that
`ThemeToggle` is a shared component (fair to import) while
`useAskCopilot` must be *ported* (a skin may not import from another
skin), and that the skin's own `dev/reset` route must gate on
`presenterResetEnabled() || NODE_ENV !== "production"` so the button and
the endpoint agree.
- **`--nw-dark-capable`** — previously unmentioned. A skin that ships a
`.dark .theme-<id>` block but omits the flag stays stuck in light,
because the shell's reconciler forces it. Documented as an explicit
opt-in, and paired with the note that a theme toggle without a dark
palette is a dead control.
- **a2ui surface ops must be emitted from a server `defineTool`** —
previously silent. A client `useFrontendTool` result never produces the
in-stream `TOOL_CALL_RESULT` the a2ui middleware needs, so the canvas
stays permanently blank. This one cost real debugging time on #6302.
- **Parameterized `useComponent` render signature** — receives the
schema output *directly*, not wrapped in `{ args }`, unlike HITL /
frontend-tool renders. Airline has no parameterized example to learn
from, so the skill now shows one and contrasts the two.
- **`nav` is not the segment validator** — the skill claimed it was "the
source of truth for which segments are valid"; the frozen contract says
the opposite and names `resolvePage`. Following the skill got you a 404.
- **Airline `useData` contradiction** — SKILL.md listed `data/` among
the slots airline omits; airline sets it, and templates.md already said
so.
- Two nits: the `tools.tsx` template would NPE for a no-data skin
(`useSkinData` returns `undefined`), and `skin.tsx` left a dangling
`use<Id>Data` import for skins that omit the slot.
## Verification
Every code sample was diffed against the corresponding real file in
`src/skins/logistics/`, and every API-signature claim against the
installed types in `packages/react-core/dist/`. Markdown only, so no
build/lint/test surface changes.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
The reskin authoring skill taught several patterns that ship a broken skin when
followed literally. Corrected against the debugged logistics skin (the frozen
contract wins on conflict):
- layout: h-screen overflow-hidden (not min-h-screen) + h-full aside, so the nav
stays pinned and <main> scrolls inside it
- layout: publish --nw-nav-inset-left/right with cleanup, so the floating skin
selector never docks on the nav and the inset does not leak between skins
- layout: document the meta-utility strip (Reset/ThemeToggle/Help) as
skin-authored chrome, with the reset-route gating coupling
- theme: document --nw-dark-capable as the dark-mode opt-in + a .dark .theme-<id>
example
- tools: every useComponent/useFrontendTool/useHumanInTheLoop registration must
close with a deps array, or the closure captures empty pre-fetch data forever
- tools: a parameterized useComponent render receives the schema output directly,
not { args }
- a2ui: a CanvasSurface must be fed by a server defineTool, never a client
useFrontendTool
- contract: nav is display-only; resolvePage is the sole segment validator
- fix airline useData contradiction; NPE-safe no-data tools shape; skin.tsx
dangling-import note
Co-Authored-By: Claude <noreply@anthropic.com>
Adds a third skin to `examples/showcases/reskinnable-demo`: **Meridian**
(`/logistics`), a supply-chain control tower built at the **maximal**
end of the frozen `Skin` contract.
## Why a third skin
The two shipped skins bracket the contract — `airline` is in-memory with
four optional slots, `banking` is REST-backed with nearly all of them. A
third earns its place only if it demonstrates something, so this one
runs a **different domain rule** through the same machinery.
Banking's most valuable property isn't the HTTP (its store is a
module-scope object seeded from JSON). It's that a **server-side rule
the client cannot bypass** rejects a write, returns a symptom, and
forces the agent to learn the failure and route around it. Meridian
reproduces that shape with **role-scoped approval authority** instead of
a policy budget.
## The headline beat, verified end-to-end over HTTP
| Attempt | Result |
| --- | --- |
| Rosa (authority $5,000) expedites `PO-88213` at $8,400 | `403
OVER_AUTHORITY` |
| Same request, body claims `costUsd: 1` | `403` — server recomputes;
client cost is never trusted |
| `PATCH {weightKg: 1}` then expedite | `422 FORBIDDEN_FIELD` — pricing
inputs are not writable |
| Approve a **non-justifying** escalation, then expedite | `403` — still
blocked |
| Approve `LINE_DOWN_RISK`, then expedite | `200`, logged at $8,400 |
| Director expedites with no escalation | `200` |
`PATCH` is an **allow-list** (`status`, `etaCurrent`) rather than a
deny-list, precisely so no pricing input becomes a side channel around
the gate. There is a named regression test for that bypass.
## What it exercises
- **REST substrate** — `/api/logistics/v1/*` over a seeded store, with
the authority gate and a two-step escalation recovery under a closed
code catalogue.
- **Identity triad** — `RuntimeProviders` (auth above the provider) +
`useRuntimeProperties` + a server-safe `identifyUser`, scoping runs per
planner.
- **a2ui canvas** — a Decision Brief from a deterministic op-builder; no
figures travel in the ops, every number binds client-side.
- **OGUI sandbox** — five functions, each projecting to an allowlisted
DTO so no internal field reaches iframe-authored JS.
- **Theme** — warm signal-amber on graphite at a tight radius, against
banking's violet and airline's teal. Pure token re-valuing; no component
edits, no `globals.css` change.
Deliberately omits `chatHeaderActions` and `onSuggestionSelect` (they
exist in banking only to serve its PDF-attachment beat) and `useData`
(components read REST directly, like banking).
## Verification
`pnpm test:unit` 22 files / 110 tests · `pnpm lint` 0 issues · `pnpm
build` compiles.
Driven in a real browser: all four routes render; theme applies; the
exception board sorts worst-first on real data; switching skins restores
banking with no theme or nav-inset leak.
**Not verified:** the chat agent itself — no API key was available, so
gen-UI, the HITL cards, `renderBrief` on the canvas, and OGUI are
unexercised at runtime. A live smoke test of those paths is the main
thing a reviewer should add.
## Known follow-ups (non-blocking)
Unused parameter in two private lane helpers · duplicate enum selections
in `buildBriefOps` could emit two components sharing an id ·
`getMitigationOptions` returns its type raw while the other four sandbox
handlers project · `req.json()` unguarded (malformed body → 500 not 400)
· `ModeSplitChart` uses two tones across four modes.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
The shell root used `min-h-screen`, which is a MINIMUM: on a page taller
than the viewport the container grew with the content, so the whole
document scrolled and the sidebar scrolled away with it. It also left
`<main>`'s own `overflow-y-auto` inert, because an unbounded parent gives
it no height to overflow against.
Switch the root to `h-screen overflow-hidden` and give the aside `h-full`,
mirroring banking's layout. The shell is now exactly one viewport tall, the
nav stays pinned, and `<main>` scrolls internally.
Measured on /logistics/lanes at a 700px viewport: document scrollHeight
700 (page no longer scrolls), aside top 0 / bottom 700, main scrollHeight
1472 vs clientHeight 700, and the aside stays at top 0 after scrolling
main by 700px.
Port banking's sidebar-footer utility controls into Meridian: a Reset
(presenter-gated), the shared ThemeToggle, and a copilot Help shortcut, placed
directly above the existing "On duty" planner switcher. Give the skin a warm-
graphite dark palette (--nw-dark-capable + a .dark .theme-logistics block) so
the theme toggle is a live control. Port useAskCopilot into the skin (no cross-
skin import) and widen the reset endpoint's gate to allow presenter/booth
deployments (presenterResetEnabled OR non-production).
Co-Authored-By: Claude <noreply@anthropic.com>
Applies the final whole-branch review wave for the logistics skin.
- renderBrief: move from a client useFrontendTool to a server defineTool on the
BuiltInAgent (mirrors banking's render_report). A client frontend-tool result
never yields an in-stream TOOL_CALL_RESULT, so the a2ui middleware never
emitted the a2ui-surface activity and the brief canvas stayed blank. Tool name
kept exactly "renderBrief". build-brief-ops + catalog/definitions confirmed
server-safe (plain Zod, no React/.tsx), so agent.ts stays server-safe.
- createDecisionRecord: implement the missing tool (globally registered) to log
a decision NOT executed through commitMitigation, wiring the previously-dead
fileDecision + POST /decisions path. Harden the route: require/resolve
plannerId, derive decidedBy/role server-side (never from body), 404 unknown
shipmentId, clamp costUsd. Client forwards plannerId only. Prompt + toolLabels
updated. Adds a decisions route test (planner-derived identity vs body decoys,
404, 400).
- greeting: drop the wrong "six lanes / three shipments" figures (seed has 10
lanes, 4 exception shipments); quantitative claims bind to data, not prose.
Co-Authored-By: Claude <noreply@anthropic.com>
## Summary
- add provider-aware coding-agent entry points to the Channels overviews
at `/slack` and `/teams`
- present both onboarding flows in the site’s existing collapsed
`Accordion` pattern, with fenced text blocks that provide the standard
`Copy Text` control
- make each prompt start from an empty directory instead of assuming an
existing CopilotKit agent
- use the Slack onboarding skill as the Slack provider source of truth
and the published Teams documentation as the Teams provider source of
truth
- add regression coverage for provider scoping, onboarding order, safety
requirements, and end-to-end verification gates
## User experience
Both Channels overview routes now open with **Start building with your
coding agent — copy this prompt**. The row stays collapsed until
selected, keeping the existing overview readable while giving builders a
complete prompt they can copy into a local coding agent.
The prompts share the same start-from-scratch foundation:
1. Read `copilotkit-setup` for new-application and managed-Intelligence
onboarding.
2. Read `copilotkit-channels` for the CLI-scaffolded Channel declaration
and long-running host.
3. Stop when a required generic skill is unavailable and direct the user
to `npx copilotkit@latest skills install`.
4. Use the CopilotKit CLI through `npx copilotkit@latest`; neither
prompt assumes or requires a global CLI installation.
5. Run `npx copilotkit@latest create`, asking the user for the project
name and framework rather than silently choosing them.
6. Select managed Intelligence, since Channels are unavailable in
self-hosted SSE mode.
7. Preserve the CLI-generated `channel-host.mts` and `channels.mts` as
the code half instead of creating a duplicate Channel declaration or
placing it in a serverless route.
8. Inspect actual browser capabilities and either drive the browser with
confirmation before consequential live-account changes or guide the user
one step at a time.
9. Keep credentials out of the conversation and report success only
after the provider installation, managed runtime, and real-message path
are verified.
## Slack prompt
The `/slack` prompt additionally:
- reads `setup-slack-channel` before planning
- delegates the Slack app, managed provider connection, and workspace
verification to that skill
- verifies Node.js 20 or newer
- protects Slack tokens, signing secrets, and Intelligence API keys
- requires all three `setup-slack-channel` gates before reporting
success
## Microsoft Teams prompt
The `/teams` prompt mirrors the complete start-from-scratch flow without
inventing or requiring a Teams-specific onboarding skill. It instead:
- treats `https://docs.copilotkit.ai/teams` as the provider source of
truth, following the documentation-based prompt pattern already used by
the docs-root Channels CTA
- tells the agent to select the user’s backend in the docs and follow
the linked Intelligence configuration and Teams connection pages in
order
- rejects substituting Slack setup instructions or relying on remembered
Azure Bot and Teams app setup
- verifies Node.js 22 or newer, matching the published managed Teams
runner requirement
- requires confirmation before consequential Azure, Intelligence, and
Teams account changes
- protects tokens, client secrets, and API keys
- requires the app to be installed in the intended Teams scope, the
Intelligence Channel to be Online, and a real Teams message or mention
to receive a reply before reporting success
## Provider scoping
The Channels overview source is shared by both routes. Separate
`FrontendOnly` blocks ensure that:
- `/slack` receives only the Slack skill-driven prompt
- `/teams` receives only the Teams documentation-driven prompt
- neither route leaks the other provider’s onboarding source or
instructions
## Research basis
The final flow follows responsibilities and requirements already
documented in the repository:
- `copilotkit-setup` owns new-project and managed-Intelligence
onboarding
- `copilotkit-channels` owns the generated Channels runtime and warns
against duplicate/manual Channel wiring
- `setup-slack-channel` owns the Slack provider flow and its three
success gates
- the docs-root Channels CTA already produces provider-specific
coding-agent prompts that point to the selected Shell Docs guide URL
- the Teams guide documents Node.js 22+, managed Intelligence
configuration, Azure Bot setup, installation in Teams, Online runtime
status, and real-message verification
- the CLI docs consistently use `npx copilotkit@latest`; they do not
require a global installation
- `npx copilotkit@latest skills install` is the documented skill
installer, and `npx copilotkit@latest create` is the documented
new-project scaffold
## Scope
This PR intentionally changes only:
- `showcase/shell-docs/src/content/docs/channels/index.mdx`
- `showcase/shell-docs/src/lib/__tests__/channels-docs.test.ts`
It does not add a component, alter navigation, change either provider
tutorial, modify the Intelligence browser setup flow, or claim a
Teams-specific onboarding skill exists.
The branch is intentionally presented as one commit:
- `docs(channels): add copyable onboarding prompts`
## Validation
- full shell-docs suite: `npm test -- --maxWorkers=1
--no-file-parallelism`
- full Vitest suite confirmation: `npx vitest run --maxWorkers=1
--no-file-parallelism --reporter=dot`
- focused Channels docs suite: 29 tests passed
- `pnpm exec oxfmt --check
showcase/shell-docs/src/lib/__tests__/channels-docs.test.ts
showcase/shell-docs/src/content/docs/channels/index.mdx`
- `git diff --check`
- rendered Slack verification at `http://localhost:3003/slack`: one
provider-specific prompt with `setup-slack-channel`, no Teams docs URL,
and one `Copy Text` control
- rendered Teams verification at `http://localhost:3003/teams`: one
provider-specific prompt with the Teams docs URL, no
`setup-slack-channel` reference, and one `Copy Text` control
- pre-commit and commit-message hooks passed
All test runs used one worker with file parallelism disabled to keep
memory use bounded.
`Agent.to_ag_ui()`, `AGUIApp`, and the `pydantic_ai.ag_ui` shim were removed in
Pydantic AI v2, so the example only ran on v1.
- pin `pydantic-ai-slim[ag-ui,openai]>=2.0.0` and `ag-ui-protocol>=0.1.19`
- import `StateDeps` from `pydantic_ai.ui`
- serve via `AGUIAdapter.dispatch_request` on a Starlette route
- give each request its own `deps` copy so state no longer leaks between threads
## Summary
Brings the `setup-slack-channel` skill into the set that `copilotkit
skills install` distributes, and narrows both it and
`copilotkit-channels` so they stop matching the same request.
## Why
The skill was merged into
[CopilotKit/channels-sdk#7](https://github.com/CopilotKit/channels-sdk/pull/7),
which makes it reachable from a channels-sdk checkout and nowhere else.
`copilotkit skills install` reads `CopilotKit/CopilotKit/skills`, so
until the skill lives here, no CLI user can get it.
No CLI change is needed to pick it up: skill names are free-form
(`--skill` is validated by regex only, with no allowlist) and the
default install is `--skill *`.
## How
**The skill is copied from the merged channels-sdk source**, with one
addition: a `version: 1.0.0` frontmatter field, so it matches the other
standalone skills in this repo (every one of them carries a version;
only the package-mirrored skills omit it). The body, references, and
manifest asset are otherwise unchanged. It stays browser-first and makes
no reference to the `copilotkit channels` commands, per the call to
defer those to the web app until they have more real-world usage.
**Both descriptions are narrowed** so the trigger overlap is gone:
| Skill | Owns | Hands off |
| --- | --- | --- |
| `copilotkit-channels` | The code half — declaring, wiring, and
customising a Channel. Assumes the provider app exists | First-time
Slack setup → `setup-slack-channel` |
| `setup-slack-channel` | The provider half — the Slack app, its tokens,
attaching it to a Channel | Code questions → `copilotkit-channels` |
Before this, both matched "connect my agent to Slack" and the agent
picked whichever it read first. That collision was invisible in
channels-sdk, where `setup-slack-channel` is the only skill present; it
becomes live the moment both ship in the installed set.
## Notes for reviewers
- **Scope is Slack only.** A Teams sibling is deliberately a separate
pass.
- **No existing skill content changes** — the `copilotkit-channels` diff
is its frontmatter description and nothing else.
- Two known follow-ups are tracked and intentionally not addressed here:
[channels-sdk#9](https://github.com/CopilotKit/channels-sdk/issues/9)
(CLI-capability claims, deferred by decision) and
[channels-sdk#2](https://github.com/CopilotKit/channels-sdk/issues/2)
(`build-channels-bot` staleness, unrelated).
Refs CopilotKit/channels-sdk#10
## Added during the `main` merge
Resolving the conflict surfaced a second, unrelated problem that had to
be fixed for this PR to be safe to land.
`skills/setup-slack-channel` is a **standalone** skill — it has no
`packages/*/skills/` source. `scripts/sync-plugin-skills.ts` treats any
such directory as an orphan unless it is listed in
`RESERVED_LIFECYCLE_SLUGS`, which this one was not. Verified against the
pre-fix script:
- `pnpm check:plugin-skills` → exit 1, `orphan file(s) in mirror:
skills/setup-slack-channel`
- `pnpm sync:plugin-skills` (write mode) → **recursively deleted all 8
files of the new skill**
The `plugin-skills-check` workflow's path filter does not match
`skills/setup-slack-channel/**`, so this PR would not have caught it —
it would have gone red on the next unrelated PR that touched the script
or a package skill, or silently eaten the skill on the next sync run.
Fix is two lines: add the slug to `RESERVED_LIFECYCLE_SLUGS`, and move
the paired `size` assertion in
`scripts/__tests__/sync-plugin-skills.test.ts` from 9 to 10. The test
file already documents this exact hazard in a comment.
### Conflict resolution
The only conflict was the `copilotkit-channels` frontmatter description,
which #6320 rewrote in parallel. The two sides disagreed about Teams:
this branch said the skill "assumes the provider app already exists",
while #6320 established that Teams provider setup **is** this skill's
job because the CLI or dashboard wizard performs it. The resolution
keeps this branch's code-half framing and the `setup-slack-channel`
handoff, but scopes that handoff to *first-time Slack app creation* only
— so it contradicts neither #6320's Teams sections nor the Slack
provider troubleshooting that stays in this file. Took #6320's `version:
1.1.0`.
## Release angular v0.3.1
**Scope:** `angular` | **Bump:** `patch`
---
### How this release process works
1. **This PR was created automatically** by the "release / create-pr"
workflow.
It bumped the `angular` packages to `0.3.1`
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 `angular` packages to npm at version `0.3.1`
- Creates git tag `angular/v0.3.1`
- 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.