From d5e4d5ece3c925a79aff75934e9a959c4b0a6721 Mon Sep 17 00:00:00 2001 From: kurihada Date: Thu, 19 Mar 2026 19:32:25 +0800 Subject: [PATCH] feat: add release baseline and workspace scene --- .changeset/README.md | 51 ++ .changeset/config.json | 17 + .gitignore | 1 + apps/docs/src/release-workspace.stories.tsx | 739 +++++++++++++++++ docs/releasing.md | 144 ++++ docs/rfcs/data-table.md | 439 ++++++++++ package.json | 3 + packages/ui/src/components/sheet.variants.ts | 7 +- pnpm-lock.yaml | 826 +++++++++++++++++-- 9 files changed, 2137 insertions(+), 90 deletions(-) create mode 100644 .changeset/README.md create mode 100644 .changeset/config.json create mode 100644 apps/docs/src/release-workspace.stories.tsx create mode 100644 docs/releasing.md create mode 100644 docs/rfcs/data-table.md diff --git a/.changeset/README.md b/.changeset/README.md new file mode 100644 index 0000000..c2ab957 --- /dev/null +++ b/.changeset/README.md @@ -0,0 +1,51 @@ +# Changesets In This Repo + +This directory is the release-intent ledger for Cadence UI. + +The repo is still an internal/private monorepo, so Changesets is used conservatively: + +- track version intent for releasable workspace packages +- keep release notes attached to code changes +- avoid auto-committing release files +- avoid tagging private packages during early internal iteration + +## What should get a changeset + +Create a changeset when a change affects: + +- `@ai-ui/ui` +- `@ai-ui/tokens` + +Do not create a changeset for docs-only work in `@ai-ui/docs` unless that work ships alongside +a package change that already needs one. The docs app is ignored in the config on purpose. + +## What kind of summary to write + +Keep the summary short and consumer-facing: + +- describe what changed +- mention the component, token, or contract surface +- avoid implementation detail unless it changes behavior + +Good: + +```md +Add the Sheet component for contextual side panels and bottom trays. +``` + +Less useful: + +```md +Refactor dialog wrapper internals and update story coverage. +``` + +## Current release posture + +The root repo is private and package publishing is not fully wired yet. Until the main release +flow is enabled, treat Changesets as the source of truth for: + +- which package versions should move +- which changes deserve release notes +- which internal dependency bumps should be coordinated + +See `docs/releasing.md` for the expected workflow around creating and consuming changesets. diff --git a/.changeset/config.json b/.changeset/config.json new file mode 100644 index 0000000..2c42d07 --- /dev/null +++ b/.changeset/config.json @@ -0,0 +1,17 @@ +{ + "$schema": "https://unpkg.com/@changesets/config@3.1.1/schema.json", + "changelog": false, + "commit": false, + "access": "restricted", + "baseBranch": "main", + "updateInternalDependencies": "patch", + "fixed": [], + "linked": [], + "ignore": [ + "@ai-ui/docs" + ], + "privatePackages": { + "version": true, + "tag": false + } +} diff --git a/.gitignore b/.gitignore index fbc903e..d4927f4 100644 --- a/.gitignore +++ b/.gitignore @@ -9,4 +9,5 @@ coverage .tmp-home playwright-report test-results +output .DS_Store diff --git a/apps/docs/src/release-workspace.stories.tsx b/apps/docs/src/release-workspace.stories.tsx new file mode 100644 index 0000000..71726c8 --- /dev/null +++ b/apps/docs/src/release-workspace.stories.tsx @@ -0,0 +1,739 @@ +import { + Badge, + Button, + Card, + CardContent, + CardDescription, + CardHeader, + CardTitle, + Dialog, + DialogContent, + DialogDescription, + DialogFooter, + DialogHeader, + DialogTitle, + EmptyState, + EmptyStateActions, + EmptyStateDescription, + EmptyStateEyebrow, + EmptyStateHeader, + EmptyStateMedia, + EmptyStateTitle, + Form, + FormControl, + FormDescription, + FormItem, + FormLabel, + FormMessage, + Input, + Popover, + PopoverArrow, + PopoverContent, + PopoverTrigger, + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, + Sheet, + SheetContent, + SheetDescription, + SheetFooter, + SheetHeader, + SheetTitle, + Switch, + Tabs, + TabsContent, + TabsList, + TabsTrigger, + Textarea, + Toast, + ToastAction, + ToastClose, + ToastDescription, + ToastProvider, + ToastTitle, + ToastViewport, + Tooltip, + TooltipContent, + TooltipProvider, + TooltipTrigger +} from "@ai-ui/ui"; +import type { Meta, StoryObj } from "@storybook/react"; +import { Controller, useForm } from "react-hook-form"; +import { useState } from "react"; + +type RoutingValues = { + lane: string; + notifications: boolean; + ownerEmail: string; + summary: string; +}; + +type ReleaseWorkspaceProps = { + quietMode?: boolean; +}; + +const reviewerColumns = [ + { + lane: "Editorial", + note: "Copy is locked. Waiting on final legal phrasing for the migration footnote.", + state: "Ready" + }, + { + lane: "Engineering", + note: "Canary checks are green. Queue the 10% wave after route owners sign off.", + state: "Watching" + }, + { + lane: "Support", + note: "No escalations yet. Macro pack and customer note are staged for handoff.", + state: "Quiet" + } +] as const; + +const timelineStops = [ + { + title: "Narrative lock", + window: "18:40", + note: "Release notes, migration callouts, and support macros align on one framing." + }, + { + title: "10% canary", + window: "19:15", + note: "Routing lane owners watch live traffic, rollback metrics, and conversion deltas." + }, + { + title: "Customer notice", + window: "20:05", + note: "Send the public note only after the queue remains stable for one quiet cycle." + } +] as const; + +function SignalGlyph() { + return ( +
+
+ + + +
+
+ + +
+
+ ); +} + +function MetricPanel({ + eyebrow, + tone = "default", + value, + children +}: { + children: React.ReactNode; + eyebrow: string; + tone?: "default" | "accent"; + value: string; +}) { + return ( +
+

+ {eyebrow} +

+

+ {value} +

+

{children}

+
+ ); +} + +function ReleaseWorkspaceScene({ quietMode = false }: ReleaseWorkspaceProps) { + const [dialogOpen, setDialogOpen] = useState(false); + const [sheetOpen, setSheetOpen] = useState(false); + const [toastOpen, setToastOpen] = useState(false); + const form = useForm({ + defaultValues: { + lane: "engineering", + notifications: true, + ownerEmail: "routing@cadence.dev", + summary: "Hold the customer note until the support lane stays quiet for one full cycle." + } + }); + + return ( + + +
+
+
+
+
+ +
+
+
+ March 24 / Controlled rollout + Workspace / Ops editorial + + + + + + Risk remains contained because support is quiet and rollback guardrails are staged. + + +
+ +
+

+ Cadence / Release operations board +

+

+ The launch is calm enough to move fast, but only if routing stays disciplined. +

+

+ This workspace treats release ops like an editorial desk: one narrative, + one routing owner, one quiet signal loop. The goal is not to ship more + controls. The goal is to keep a high-stakes rollout readable. +

+
+ +
+ + + + + + + +

+ Current signal blend +

+
+

Canary checks: stable for 28 minutes.

+

Support queue: zero new escalations in the current pass.

+

Legal note: one copy footnote remains under review.

+
+ +
+
+
+
+ +
+ + Queue the first wave after engineering and support both stay green. + + + Legal is the only lane still holding a sentence-level note. + + + {quietMode + ? "The board is almost empty, which is when routing discipline matters most." + : "Support is quiet, but the customer note should stay staged until the next check."} + +
+
+
+ +
+
+ +
+ + Overview + Routing + Audience + +

+ One workspace for the story, the gate, and the next decision. +

+
+ + +
+
+
+
+
+

+ Release narrative +

+

+ The migration is technically ready, but the customer story should still feel measured. +

+
+ 10% wave pending +
+ +

+ Engineering has reduced the technical risk. What remains is message + discipline: route the launch through one owner, keep the customer + note staged, and avoid turning a calm rollout into a noisy one. +

+ +
+ {timelineStops.map((item) => ( +
+
+

+ {item.title} +

+ + {item.window} + +
+

+ {item.note} +

+
+ ))} +
+
+
+ + {quietMode ? ( + + + + + + Quiet shift + No escalations are waiting on this desk. + + Keep the board sparse, hold the customer note, and use the next + quiet cycle to confirm routing before the wave is queued. + + + + + + + + ) : ( + + + Support pulse + + The queue is calm, but not calm enough to go unsupervised. + + + +
+
+
+

+ Customer note +

+ Staged +
+

+ Keep the note unpublished until the canary stays quiet for one + more cycle. +

+
+
+
+

+ Macro pack +

+ Ready +
+

+ Support copy aligns with the release narrative and rollback path. +

+
+
+ + + Escalations + No active escalations + + Stay disciplined anyway. Quiet boards are where rushed launches + usually create avoidable noise. + + + +
+
+ )} +
+
+ + +
+ {reviewerColumns.map((column, index) => ( + + +
+ {column.lane} + + {column.state} + +
+ {column.note} +
+
+ ))} +
+ +
+
+
+

+ Routing principle +

+

+ Keep one owner visible, one fallback explicit, and one customer note staged. +

+
+ +
+
+
+ + + + + Audience framing + + The internal narrative should be more detailed than the external one. + + + +
+
+

+ Internal viewers +

+

+ Mention rollback thresholds, reviewer routing, and support macro timing. +

+
+
+

+ Customers +

+

+ Keep the message short, calm, and reversible. Explain the value, not the pipeline. +

+
+
+
+
+ + + + Presenter note + + +

+ If the launch feels quiet in this tab, that is success. The board should + only get louder when a decision actually changes. +

+
+
+
+
+
+ + +
+
+ + + + + Queue the 10% wave? + + This sends the first controlled wave, not the full customer note. Use it only if + support stays quiet and the routing owner is live. + + + + + + + + + + + + + Routing lane settings + + Keep the lane owner explicit and the customer note disciplined before the wave is + queued. + + + +
+ { + setSheetOpen(false); + setToastOpen(true); + })} + > + + Lane owner email + + + + This person owns the final routing decision. + + + + ( + + Primary lane + + + + + The highlighted lane stays visible on the release desk. + + + + )} + /> + + + Routing note + +