Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .claude/agent-memory/main/MEMORY.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@
- [Registry deps coupling](feedback_registry_deps_coupling.md) — new registry item with new peer dep requires updating contract-test.mjs install list in lockstep
- [Design learnings repo](project_design_learnings.md) — knowledge base + working monorepo for deessejs/ui registry at ui.deessejs.com (hosted on Vercel)
- [Phase 4 validated](project_phase4_validated.md) — external install end-to-end confirmed 2026-07-29; Phase 6 (official shadcn index submission) gate is now lifted
- [Studio decisions](project_studio_decisions.md) — 9 locked decisions (2026-07-30): agents author into a DB, PR publishing, dual tree collapsed
2 changes: 1 addition & 1 deletion .claude/agent-memory/main/project_design_learnings.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ This repo (`design/`) is both a **knowledge base** and the **working monorepo**
- `learnings/` — research notes (Tailwind, shadcn, layout, page-content, marketing-ui, agent-system). Source URL + verification date convention.
- `apps/web/` — Next.js 16 showcase site. Header, footer, nav, cards, code-block (Shiki), pager (previous/next), all on shadcn/Base UI + Tailwind v4.
- `packages/ui/` — shadcn primitives (Base UI, not Radix), tokens, globals.css. Don't touch — this is the foundation.
- `packages/registry/` — deessejs registry components. Currently has Button (re-export) + IconButton + ColoredBadge (real impl). Each component has `index.tsx` (component + Demo export) and `meta.ts` (ComponentMeta).
- `packages/registry/` — deessejs registry components. 8 components (button, colored-badge, icon-button, breadcrumb, empty, tabs, input, textarea) + 1 block (empty-state), as of 2026-07-30. Each has `index.tsx` (component + Demo export) and `meta.ts` (ComponentMeta).
- `apps/web/lib/registry/` — types, sources, aggregator. The seam for future DB-backed registry.
- `apps/web/scripts/build-sources.mjs` — build-time codegen that reads `packages/registry/src/**/*.tsx` and emits `apps/web/lib/registry/sources.generated.ts`.

Expand Down
47 changes: 47 additions & 0 deletions .claude/agent-memory/main/project_studio_decisions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
---
name: project-studio-decisions
description: Nine locked decisions (2026-07-30) for the agent-authored draft/preview studio — DB as authoring surface, PR publishing, collapsed dual tree
metadata:
type: project
---

Nine decisions locked on **2026-07-30** for the draft/preview/admin layer. Full reasoning, per-subsystem,
lives in `docs/reports/studio/` — start at its `README.md`. The original single-file report at
`docs/reports/2026-07-30-draft-preview-admin-architecture.md` is now a stub pointing there.

**Why this exists:** components in this repo are authored by **AI agents, not by hand**. The
maintainer is the reviewer. The friction being removed is having to open an editor, launch an
agent, and run `next dev` (~1 GB RAM) just to see whether a component looks right.

## The decisions

1. The agent runs **locally**. No server-side agent orchestration — Studio is an API + a preview surface.
2. The **database is the authoring surface**. The agent writes no files.
3. Publishing **opens a PR**. The public registry stays static JSON — no Postgres on the consumer install path.
4. The agent contract is **both a CLI and an MCP server** over one core library.
5. The **dual tree collapses into one** (see [[project-design-learnings]] — this reverses a locked decision).
Arbitration is **per item**, not blanket: `icon-button` and `colored-badge` are the two where the
consumer copy is the degraded one, so the workspace copy wins there.
6. **One account**, `emailAndPassword.disableSignUp: true`. No `admin()` plugin, no roles, no email.
7. Migrate the repo to **pnpm 11 + catalogs** (the saas-template's model).
8. `registryDependencies` **always resolve to the published version** — a draft block whose dependency is still a draft cannot preview.
9. CSS is **compiled server-side** per draft version, not by `@tailwindcss/browser`.

## Consequences that are easy to forget

- **Live rendering is the product, not a later phase.** If the agent writes and the human only
reviews, the human's entire job is visual.
- **Write-path validators are the only gate.** With no PR between generated code and stored state,
token discipline / import allow-list / self-containment / required `Demo` must be enforced in the
oRPC mutation or they cease to exist.
- **Three origins are mandatory**, not stylistic: `ui.` (public), `studio.` (session), `preview.`
(cookieless, runs untrusted code). Same-origin preview = session theft.
- **Tailwind's `compile()` is undocumented internal API.** Maintainers said so on record; the return
field was renamed in a *minor* (4.1.0). Pin the exact version, contract-test the output, isolate
behind one module.
- **Decision 8 imposes a sequence**: a new primitive must be published (PR + CI + redeploy, ~4 min)
before a block using it can preview. Version pinning is the escape hatch if that becomes common.

**How to apply:** treat these as settled — do not re-derive or re-litigate them. Phase 0 (decoupling
the hardcoded item lists, deleting the drift apparatus, pnpm migration, Next → 16.2.12) is blocking
for everything else. Related: [[project-design-learnings]], [[feedback-registry-deps-coupling]].
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,11 @@ node_modules

# testing
coverage
.contract-test/ # ephemeral shim project created by apps/web/scripts/contract-test.mjs
# ephemeral shim project created by apps/web/scripts/contract-test.mjs
.contract-test/
temp/sandbox-validate
# vendored reference checkout, has its own .git — never commit
temp/saas-template

# next.js
.next/
Expand Down
124 changes: 124 additions & 0 deletions docs/plans/2026-07-29-shadcn-registry-adoption.md
Original file line number Diff line number Diff line change
Expand Up @@ -353,3 +353,127 @@ The deferred items from the **Phase 7** section (multi-style, multi-base, blocks
- **Buggy monorepo surface in the shadcn CLI.** Multiple open 2026 bugs (#9239, #11002, related). Mitigation: authored consumer sources ship as-is — no runtime transform of workspace imports. The decoupling is the defense.
- **Drift between `packages/registry/src/components/<id>/index.tsx` (showcase) and `registry/base-nova/ds-<id>/ds-<id>.tsx` (registry).** Mitigation: drift-check script added if drift shows up in Phase 5.
- **Schema evolution.** The `registry.json` schema may grow new required fields. The validator in Phase 5 catches drift per CI run.

---

# Addendum — 2026-07-30: the dual-tree decision is reopened and reversed

**Status of this addendum:** supersedes the "Existing showcase tree" entry in the decisions log
above. Everything else in this plan stands.

## What was locked

From the decisions log (2026-07-29):

> **Existing showcase tree:** `packages/registry/src/components/<id>/` and its `meta.ts` files stay
> untouched. […] `registry.json` is a parallel manifest for the shadcn CLI. **Duplication is
> accepted; drift is caught by PR review.**

And from the Risks section:

> **Drift between `packages/registry/src/components/<id>/index.tsx` (showcase) and
> `registry/base-nova/ds-<id>/ds-<id>.tsx` (registry).** Mitigation: drift-check script added if
> drift shows up in Phase 5.

That mitigation shipped: `apps/web/scripts/check-registry-drift.mjs`, `docs/registry/audit-2026-07-29.json`,
and the CI `drift` job.

## What materially changed

The decisions log opens with *"no more re-litigation unless something materially changes the
picture."* Two things did:

1. **Components are now authored by AI agents writing to an API, not by humans writing files.**
See `docs/reports/2026-07-30-draft-preview-admin-architecture.md`.
2. **PR review is no longer in the authoring loop.** The locked decision's stated mitigation —
*"drift is caught by PR review"* — no longer has a PR to run in. Its premise is gone, not merely
inconvenient.

Duplication was acceptable when a human read both files before merge. With an agent writing both,
it doubles the surface where generated code can diverge silently, and the only remaining detector is
a script whose per-item assertions are hardcoded to three item names.

## What the divergence actually was

Measured on 2026-07-30 by reading all three files:

| File | Content |
|---|---|
| `packages/ui/src/components/button.tsx` | Full `cva`; `cn` from `@workspace/ui/lib/utils`; exports `Button` / `buttonVariants` |
| `registry/base-nova/ds-button/ds-button.tsx` | **Identical `cva` body**; `cn` from `@/lib/utils`; exports `DsButton` / `dsButtonVariants` |
| `packages/registry/src/components/button/index.tsx` | Three-line re-export of the workspace Button, plus `ButtonDemo` |

The `cva` bodies are byte-equivalent — same 607-character base string, same six variants, same eight
sizes. The differences are the `cn` import specifier, the export names, defaults expressed by
destructuring vs `defaultVariants`, and a named `DsButtonProps` interface on the consumer side. **For
`ds-button` there is no design divergence.**

**`ds-button` is not representative.** Reading all nine items on 2026-07-30 rather than only the three
above: four are thin re-exports (`button`, `breadcrumb`, `tabs`, `empty`) and **five carry real
implementation** (`colored-badge`, `icon-button`, `input`, `textarea`, `blocks/empty-state`). For
`input`, `textarea`, `colored-badge` and `icon-button` there is no `packages/ui` primitive to re-export
at all — `packages/ui/src/components/` holds only five files.

Two items genuinely diverge, and in both the consumer copy is the degraded one: `ds-icon-button` inlines
a **strict subset** of the button base string (missing `group/button`, `text-sm`, the active translate,
every `aria-invalid:*` and every `[&_svg]` rule), and `ds-colored-badge` inlines a stale `Badge` snapshot
while widening its props type with `className`.

The drift apparatus caught neither, because its per-item assertions are hardcoded to three names while
three other items get zero assertions and the run still reports green. That — not the absence of
divergence — is the argument for deleting it.

## The new decision

**The two trees collapse into one.** The self-contained, consumer-facing source becomes the single
source of truth. The showcase imports it rather than maintaining a parallel version.

Arbitration is **per item**, not blanket:

- The consumer copy wins for `button`, `breadcrumb`, `tabs`, `empty`, `input`, `textarea` and
`blocks/empty-state`. It is the artifact that actually ships, that `contract-test.mjs` type-checks, and
that the Phase 4 sandbox validated end-to-end against the deployed registry on 2026-07-29. It is
already self-contained; the only workspace coupling is the `cn` import, which the `@/lib/utils` alias
exists to resolve.
- **The workspace copy wins for `icon-button` and `colored-badge`**, where the consumer copy is the
degraded one. Adopting it wholesale would publish a trimmed button base string as the single source of
truth — the anti-slop principle inverted. Those two implementations must be rewritten into
self-contained form before they can become the source, which is real work rather than a file move.

Per-item measurement and the full arbitration table: `docs/reports/studio/02-single-tree.md`.

Mechanism: a two-line bridge at `apps/web/lib/utils.ts` re-exporting `cn` from
`@workspace/ui/lib/utils`. `apps/web/tsconfig.json` already maps `@/*` to the app root. The preview
origin's import map points the same specifier at a vendored `cn`. Showcase, preview, and consumer
then execute **the same source text**.

## What this does not change

- **`packages/ui/src/components/button.tsx` stays.** The site chrome (`app-header`, `app-footer`,
cards, pager) imports `@workspace/ui/components/button`. That package is the showcase's own UI
kit; `ds-*` is the distributed artifact. Two legitimate consumers, two legitimate files.
- **The catalog / install-artifact split stays.** `registry.json` carries no `content`; per-item
JSONs do. Requirement #4 of the Phase 6 submission is unaffected.
- **The `Demo` stays out of the shipped artifact.** Consumers do not receive demos; the Demo becomes
a separate field alongside the source, not part of `files[]`.
- **Phases 1–5 and the Phase 6 submission entry stand as written.**

## Consequences

Deleted as obsolete:

- `apps/web/scripts/check-registry-drift.mjs`
- `docs/registry/audit-2026-07-29.json` and `docs/registry/audit-2026-07-29.md`
- the CI `drift` job

Superseded:

- `docs/plans/2026-07-29-drift-detection.md` — its tolerance policy no longer has two trees to
tolerate a difference between. The parts describing *what counts as a meaningful divergence*
should be recycled into the write-path validators in `docs/reports/studio/agent/01-validators.md`.

The OSS survey finding recorded in this plan — *"Nobody shares files between the showcase site and
the installable registry"* — remains accurate about those registries. It is knowingly departed from
here, because those registries are authored by humans through pull requests and this one is not.

Full context: `docs/reports/studio/` — start at its `README.md`.
64 changes: 64 additions & 0 deletions docs/reports/2026-07-30-draft-preview-admin-architecture.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
---
title: Draft components, admin accounts, and live preview without redeploy
date: 2026-07-30
status: superseded — decomposed into ./studio/
---

# Superseded

This report has been decomposed into **[`./studio/`](./studio/)**, one document per subsystem.

It was a single 653-line document covering the data model, the agent contract, the preview renderer, the
publish path, the shadcn registry contract, the security model, and the changes needed in the existing
monorepo. That is seven subsystems, and keeping them in one file meant every reader loaded all of it to
answer one question.

**Start at [`./studio/README.md`](./studio/README.md)** — it carries the problem statement, the nine locked
decisions, the topology, and an index of the rest.

## Where each section went

| Was | Is now |
|---|---|
| §1 the problem, §2 locked decisions, §3.1 topology | [studio/README.md](./studio/README.md) |
| §3.2 data model | [studio/01-data-model.md](./studio/01-data-model.md) |
| §4, §9 the dual tree and the `ds-button` arbitration | [studio/02-single-tree.md](./studio/02-single-tree.md) |
| §3.3 the write loop, decision 4 | [studio/agent/README.md](./studio/agent/README.md), [cli](./studio/agent/02-cli.md), [mcp](./studio/agent/03-mcp.md) |
| §6 write-path validators | [studio/agent/01-validators.md](./studio/agent/01-validators.md) |
| §5.1–5.2 transpiling and imports | [studio/preview/01-transpile-and-imports.md](./studio/preview/01-transpile-and-imports.md) |
| §5.3, §7 Tailwind `compile()` | [studio/preview/02-css-compile.md](./studio/preview/02-css-compile.md) |
| §5.4 client boundaries | [studio/preview/README.md](./studio/preview/README.md) |
| §11 security model | [studio/preview/03-security.md](./studio/preview/03-security.md) |
| §3.4 the publish loop | [studio/publish/README.md](./studio/publish/README.md) |
| the shadcn registry contract | [studio/publish/01-shadcn-registry.md](./studio/publish/01-shadcn-registry.md) |
| decision 6, the admin surface | [studio/admin/README.md](./studio/admin/README.md), [auth](./studio/admin/01-auth.md) |
| §13 Phase 0 | [studio/repo/01-decoupling.md](./studio/repo/01-decoupling.md) |
| §8, §18 Next.js versions and CVEs, §10 migration deltas | [studio/repo/02-pnpm-and-versions.md](./studio/repo/02-pnpm-and-versions.md) |
| §12 Next.js 16 mechanics | [studio/repo/03-nextjs-16.md](./studio/repo/03-nextjs-16.md) |
| §10 template reuse | [studio/repo/04-template-reuse.md](./studio/repo/04-template-reuse.md) |
| §14 frictions, §15 costs, §17 unverified | [studio/99-frictions-and-costs.md](./studio/99-frictions-and-costs.md) |
| §16 sources | distributed — each document cites its own, with verification dates |

## What changed in the content

The decomposition is not a copy. Every factual claim was re-measured against the repo on 2026-07-30, and
several did not survive:

- **§4's arbitration was too broad.** "The showcase file is a re-export with no design content to lose"
holds for 4 items of 9. Five carry real implementation, and for `input`, `textarea`, `colored-badge` and
`icon-button` there is no `packages/ui` primitive to re-export at all. The arbitration is now per item,
and for `icon-button` and `colored-badge` the workspace copy wins — the consumer copies are the degraded
ones.
- **The drift apparatus was not guarding "one import specifier."** It asserts on 3 items of 9 and reports
green, while a real divergence sits in `ds-icon-button`'s base class string.
- **§5.4's "all nine items begin with `use client`"** is true of the showcase tree and false of the shipped
one, where it is 5 of 9.
- **§13's `contract-test.mjs` item list is already dynamic** (9/9). Only its npm install list is hardcoded,
and that list is currently complete.
- **§8's claim that the v16.2.6 release page lists the CVE fix** was corrected — the fix is on the v16.2.5
page; v16.2.6 is a Turbopack follow-up. The recommendation to move to 16.2.12 is unchanged. §18 of this
report noted this but left §8 uncorrected.
- **The button cva has 8 sizes, not nine.**

`docs/plans/2026-07-29-shadcn-registry-adoption.md` carries an addendum reversing its dual-tree decision.
That addendum repeats the first two points above and needs the same correction.
Loading
Loading