# Sync Shadcn

> Autogoal-backed planning, status, review, dashboard, apply, and tracking for upstream shadcn docs syncs into Plate docs. Use when the user asks for `sync-shadcn`, `sync-shadcn status`, `sync-shadcn review`, `sync-shadcn dashboard`, `sync-shadcn apply`, a scoped `sync-shadcn <feature>` lane, to sync shadcn docs, audit newer shadcn docs changes, compare `../shadcn/apps/v4` with `apps/www`, update the shadcn sync baseline, or decide what to adopt, fork, defer, or exclude from upstream shadcn.

- Skill: `lofcz/sync-shadcn` (Agent Skill)
- Install (CLI): `npx skillmds@latest add lofcz/sync-shadcn`
- Raw SKILL.md: https://api.skillmd.com/api/skills/lofcz/sync-shadcn/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Security
- Author: lofcz (https://skillmd.com/u/lofcz)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/lofcz/sync-shadcn

---


# Sync Shadcn

Handle $ARGUMENTS.

Goal: compare the tracked upstream shadcn docs baseline with the current
`../shadcn/apps/v4` target, inventory every added/modified/deleted upstream
change, map each change to Plate's docs app, classify the merge decision, write
a reviewable plan under `docs/sync/shadcn`, directly merge any qualifying tiny
overlap fixes, then stop for user review of the remaining slices. A later
explicit user acceptance starts implementation mode for a named plan and slice.

If $ARGUMENTS starts with a command name, dispatch to that command before the
default planning/implementation flow. If $ARGUMENTS names a feature, product
surface, or slice, run a scoped planning lane for that scope. The default
full-range planning lane applies only when no command or scope is mentioned.

This skill exists because Plate's docs app is a forked product surface, not a
generic shadcn mirror. Upstream owns the Fumadocs/shadcn docs architecture.
Plate owns Plate docs content, editor demos, registry content, API MDX, CN docs,
MCP, Plate Plus hooks, GA, and a small set of intentional forks.

## Autogoal Dependency

This skill depends on
[$autogoal](/Users/zbeyens/git/plate/.agents/skills/autogoal/SKILL.md). Load
`autogoal` before mutable sync state, upstream pull/fetch, run artifact writes,
status JSON edits, or implementation delegation.

`sync-shadcn` is a derived `autogoal` workflow and a two-phase lane:

- Planning mode is the default. It writes the range plan, updates
  `lastPlannedCommit`, directly applies qualifying micro-overlap merges,
  asks the user to review the remaining plan, and stops.
- Implementation mode starts only after the user explicitly accepts a plan and
  slice in a later instruction, except for the micro-overlap direct merge
  exception below.
- Default flow mode: one-shot execution for planning mode and one-shot
  execution for accepted implementation mode. They are separate activations.
- Use collaborative planning only when the user is explicitly deciding policy
  before a range plan is written.
- Primary template:
  `docs/plans/templates/sync-shadcn.md`.
- Default packs: none. Add `docs` if docs/content pages are edited during an
  accepted implementation, `browser` if visible docs UI is edited, and
  `agent-native` if `.agents/**`, `.claude/**`, `.codex/**`, skills, commands,
  prompts, or user-action tooling are edited.
- Required evidence types: `command`, `source-audit`, `artifact`, and `N/A`
  rows. Add `browser` evidence when a planning scope or accepted
  implementation touches visible docs UI.
- Visual sync scopes must capture comparable screenshots of the upstream
  shadcn page and the Plate page before making or closing a visual parity call.
  Save only screenshots and notes, not broad upstream patch files.
- `autogoal` owns goal lifecycle, blocked/completion semantics, active-goal
  conflict handling, plan instantiation, output-budget discipline, and
  `check-complete.mjs`.
- `sync-shadcn` owns shadcn range policy, commit accounting, upstream inventory
  classification, Plate fork/exclusion decisions, status JSON semantics, and
  merge-slice handoff.

## Micro-Overlap Direct Merge Exception

The default review boundary is still real. Do not use it as an excuse to miss
obvious tiny upstream fixes on components Plate already mirrors.

During planning, directly merge a change when all of these are true:

- The upstream row maps to a retained Plate component, primitive, hook, or
  utility with a clear local owner path, not to upstream product content.
- The parent surface is already `synced`, an accepted partial sync, or an
  obvious overlapping primitive such as `Button`, `Badge`, `Input`, `Command`,
  `Tooltip`, `PageHeader`, or the copied docs-shell components.
- The diff is tiny: one local file, one behavior/class/token/prop/import fix,
  no new files, no deleted files, no new dependency, no route, no data model,
  no generated output, and no package or lockfile edit.
- The local Plate file still has the old value or an equivalent local variant
  that should receive the same fix.
- The change does not touch settled exclusions: v0, create, charts, colors,
  Rhea/theme/customizer/style-registry product surfaces, upstream docs prose,
  external registry directory content, or generated `public/r/**` output.
- No product judgment is needed. If the change changes layout, UX, copy,
  route shape, docs concepts, registry semantics, or multi-file architecture,
  it is not a micro-merge.

Examples of direct merges:

- Replace one stale utility class on Plate's copied `Button` because upstream
  fixed the same class on every style variant.
- Apply one bugfix conditional to a copied `Command` or `CopyButton` primitive
  when Plate has the same bug and no Plate product requirement changes.
- Remove one dead prop/import from a synced component when upstream removed it
  and Plate has no local dependency on it.

Examples that still require review:

- Package bumps, lockfile changes, registry schema/route changes, docs concept
  additions, new components, deleted components, multi-file UI chunks, visual
  layout rewrites, sidebar/search behavior changes, generated registry output,
  and anything touching a deferred or rejected product surface.

When a micro-overlap merge is found:

1. Record it in the run plan under `## Micro Auto-Merges` with upstream path,
   Plate path, focused diff summary, why it qualifies, and verification.
2. Patch the Plate owner file directly in the same activation.
3. Run the smallest meaningful verification: focused eslint/typecheck/source
   audit; add browser proof when the changed component is browser-visible and a
   stable route exists.
4. Add a `partialSyncs` entry if the full baseline does not advance. Keep
   `lastSyncedCommit` unchanged unless the whole range is complete.
5. Continue to stop for review on every non-micro slice.

## Commands

Supported commands:

- `status`: summarize current shadcn sync state, partial syncs, deferred
  decisions, and recommended next step without writing sync artifacts.
- `dashboard`: regenerate the structured feature-delta dashboard under
  `docs/sync/shadcn` for visual review of synced, deferred, rejected, forked,
  and pending shadcn sync decisions.
- `apply`: apply a copied dashboard review payload to
  `docs/sync/shadcn/deltas.json`, answer question-only rows without mutation,
  implement rows targeting `synced`, then regenerate the dashboard when rows
  changed.
- `review`: re-audit the current tracked shadcn range against `../shadcn` and
  the current Plate checkout before trusting an existing plan.

Feature-scoped planning:

- Any non-command $ARGUMENTS value is a user-named scope. Treat the full
  argument string as the scope label instead of hardcoding allowed feature names.
- Scope labels can describe UI surfaces, routes, product areas, registry
  groups, content families, or implementation slices.
- If the scope is too vague to map to upstream and Plate files, ask one focused
  question before writing artifacts.

Command parsing:

- The first $ARGUMENTS token is the command when it matches a supported command.
- The full $ARGUMENTS string is the scope when the first token is not a
  supported command.
- If no command or scope is present, use the default full-range
  planning/implementation flow.
- Reserved commands are `status`, `dashboard`, `apply`, and `review`; do not
  treat them as scope labels.
- Scopes are planning lanes, not broad implementation permission. A scoped
  plan still stops for user review before non-micro `apps/www` work.

### Scoped Planning

Use `sync-shadcn <scope>` when the user wants to sync only one named surface
before reviewing broader docs sync work.

Purpose:

- compare the tracked upstream range, but inventory and classify only changes
  that affect the named scope
- write a reviewable scope-specific plan under `docs/sync/shadcn`
- avoid the default full-range lane unless the user invokes `sync-shadcn`
  without a command or scope

Scope discovery:

- Translate the user-named scope into likely upstream files under
  `../shadcn/apps/v4` and likely Plate files under `apps/www`.
- Search names, route paths, component names, config keys, registry names, and
  docs paths that match the scope.
- Include all matching hunks that directly affect the scope.
- Exclude adjacent changes that only share a file but belong to another surface;
  classify those as out-of-scope.

Scoped planning rules:

- Save artifacts in a scope-named run directory or plan name, for example
  `docs/sync/shadcn/runs/<date>-<base>-to-<target>-<scope-slug>/`.
- Use upstream diffs/logs for the same baseline and target as the default lane,
  but filter inventory rows to scope-matching files and patch hunks.
- Also record an out-of-scope count for upstream rows in the range so it is
  clear the scoped plan cannot advance `lastSyncedCommit` alone.
- If the scope is visual or route-owned, capture upstream and Plate screenshots
  for the matching route(s) at the same viewport before finalizing the plan.
  Use `docs/sync/shadcn/runs/<range>/screenshots/` for committed evidence, and
  include screenshot paths plus the visible deltas in the plan.
- Do not update `lastSyncedCommit` from a scoped plan. Scoped sync can add a
  `partialSyncs` entry after accepted implementation, but the baseline advances
  only when the full range is accounted for.
- The plan's recommended slices must stay inside the named scope. If a file also
  contains unrelated changes, classify the scope hunk as `smart-merge` and the
  unrelated hunks as out-of-scope for this lane.
- Final planning output must say that the default full sync lane remains
  pending for the out-of-scope rows.

### `status`

Use `sync-shadcn status` when the user wants a quick checkpoint before choosing
the next sync step.

Purpose:

- show the current baseline, latest planned target, current upstream target, and
  whether the tracked plan looks fresh enough for decision-making
- list accepted partial syncs already landed
- list deferred decisions so the user can choose what to do next
- list reviewable Plate-vs-shadcn differences that are still intentionally
  undecided or deferred, especially visual parity gaps from scoped sync plans
- recommend the next command or decision

Status mode may:

- read `docs/sync/shadcn/status.json`
- read `lastPlan`, the linked `inventory.md`, and the latest run directory when
  present
- resolve `../shadcn` refs and fetch `origin main --tags` when the user asks for
  current upstream freshness; if fetch fails, report freshness as unverified
- count upstream commits and file-status rows for
  `lastPlannedCommit..origin/main`
- summarize `partialSyncs[*].slices`, `partialSyncs[*].deferred`,
  and plan `Questions`
- when the user supplies words after `status`, treat the remaining argument as
  a status scope filter, for example `status our /editors vs shadcn /blocks`;
  prefer the matching `partialSyncs[*].plan`, `lastPlan`, or run directory
  before falling back to global status
- summarize remaining reviewable differences from the matched plan's
  `Recommended Merge Slices`, `Visual Evidence`, `Implementation Result`, and
  `partialSyncs[*].deferred`

Status mode must not:

- patch `apps/www`
- write `docs/sync/shadcn/runs/**`
- write review artifacts
- change `lastSyncedCommit`, `lastPlannedCommit`, `lastPlan`, or
  `partialSyncs`
- delegate implementation to `task`
- treat listed deferred items as accepted decisions
- list settled exclusions or preserved Plate forks as if they still need
  action; those belong in `review` evidence or the plan, not routine status
  output

Status must distinguish three things:

- `Landed`: accepted partial sync slices already applied.
- `Reviewable differences`: Plate still differs from upstream and the
  difference is not settled policy. These are the items the user can re-decide.
  Include the upstream behavior, current Plate behavior, likely owner files,
  and the smallest next command or decision.
- `Settled differences`: explicit exclusions and preserved Plate forks. Do not
  list these by default unless the user asks to re-open them.

Deferred item sources, in order:

1. `docs/sync/shadcn/status.json` `partialSyncs[*].deferred`
2. the selected plan's `Questions` section
3. recommended merge slices marked `defer`, `needs-question`, or not yet
   implemented
4. visual evidence rows where the plan says a Plate-vs-upstream difference
   still needs work
5. implementation-result notes that explicitly leave a follow-up slice open
6. status update notes that say `lastSyncedCommit` cannot advance

For visual scoped status, do not stop at "fresh" when visible deltas remain.
Report them as reviewable differences even when the scoped implementation was
verified. Example:

```md
Reviewable differences:
- BlockViewer toolbar: upstream `/blocks` has compact device controls,
  refresh, separated command pill, and v0 action; Plate `/editors` still keeps
  its current toolbar density and excludes v0. Decision: sync toolbar spacing
  only, keep Plate install/source behavior and no v0; or leave as Plate fork.
```

Collapse upstream product/theme/style noise into its owning deferred decision.
For example, Rhea/style/theme/generated style registry rows are part of the
upstream create/theming product surface; do not list them as separate status
items unless the user explicitly asks to review that product surface.

Status checks:

```bash
node -e '
const fs = require("fs");
const status = JSON.parse(fs.readFileSync("docs/sync/shadcn/status.json", "utf8"));
console.log(JSON.stringify({
  lastSyncedCommit: status.lastSyncedCommit,
  lastPlannedCommit: status.lastPlannedCommit,
  lastPlan: status.lastPlan,
  partialSyncs: status.partialSyncs ?? []
}, null, 2));
'

git -C ../shadcn fetch origin main --tags
TARGET=$(git -C ../shadcn rev-parse origin/main)
PLANNED=$(node -e 'console.log(JSON.parse(require("fs").readFileSync("docs/sync/shadcn/status.json", "utf8")).lastPlannedCommit || "")')
git -C ../shadcn log --oneline --decorate "$PLANNED..$TARGET" -- apps/v4
git -C ../shadcn diff --name-status --find-renames "$PLANNED..$TARGET" -- apps/v4
```

Status output shape:

```md
Status: <fresh | upstream-ahead | no-plan | blocked-ref | unverified>
Baseline: <lastSynced-short>
Planned: <lastPlanned-short>
Current upstream: <target-short>
Plan: <path or none>

Partial syncs:
- <date/range>: <landed slices>

Reviewable differences:
- <surface>: upstream <behavior>; Plate <behavior>; decision needed <adopt |
  smart-merge | keep fork>; files <paths>

Deferred decisions:
- <item>

Next: <run review | rerun planning | decide deferred item | implement accepted slice | advance baseline>
```

Keep status concise, but do not hide reviewable differences behind a generic
"deferred" label. If the user wants full evidence, tell them to run
`sync-shadcn review`.

### `apply`

Use `sync-shadcn apply` when the user pastes a dashboard review payload.

Purpose:

- apply rows listed directly under `$sync-shadcn apply` or under an optional
  `Rows` heading
- answer rows listed under `Questions` in chat without mutating
  `deltas.json`
- keep the copied dashboard prompt short by storing the mutation contract in
  this command

Apply mode may:

- read and update `docs/sync/shadcn/deltas.json`
- patch `apps/www`, `content/docs`, and related source files when a listed row
  targets `synced` or `fork`
- delegate a coherent implementation slice to `task` when a `synced` or `fork`
  row needs more than a tiny local edit
- remove screenshot refs for rows applied to `synced` or `rejected`
- delete unreferenced local screenshot files under `docs/sync/shadcn/runs/**`
- run `pnpm sync-shadcn dashboard` after any JSON mutation

Apply mode must not:

- write `docs/sync/shadcn/runs/**` except deleting unreferenced screenshots
- change `lastSyncedCommit`, `lastPlannedCommit`, `lastPlan`, or
  `partialSyncs`
- infer decisions for rows not listed in the pasted payload
- mutate `deltas.json` for question-only rows
- mark a row `synced` or `fork` before implementation is present and verified
- ask for review when the row has clear files, suggestion, and acceptance
  criteria; ask one focused question only when the target behavior is unclear

Payload shape:

```md
$sync-shadcn apply

- command-menu/command-footer-shortcuts (Command Menu / Footer shortcuts and copy payloads): defer -> pending note: Add copy only for components and editor kits.

Questions:
- registry/registry-review (Registry / Full registry review): Which registry rows should include copy shortcuts?
```

Apply rules:

- A direct bullet row under `$sync-shadcn apply`, or under an optional `Rows`
  heading, is the only mutation unit.
- Set each listed item state to the requested target state.
- Target states `pending`, `defer`, and `rejected` are metadata decisions.
- Target state `synced` is implementation mode: inspect the row files and
  suggestion, implement the requested change, run focused verification, then
  update `deltas.json` to `synced`.
- Target state `fork` is also implementation mode when the row's note,
  suggestion, or files imply work in Plate. Inspect the row files and
  suggestion, implement or verify the intentional Plate-owned fork, run focused
  verification, then update `deltas.json` to `fork`.
- A `fork` update may be metadata-only only when the Plate fork already exists,
  the row note does not ask for code/content work, and the source evidence is
  checked before updating `deltas.json`.
- Use notes to update `decision`, `suggestion`, or summary text when
  appropriate.
- If a note is only a question, keep it under `Questions`, answer it in chat,
  and do not mutate JSON.
- When applying `synced` or `rejected`, remove that row's `screenshots` object
  and delete unreferenced local screenshot files.
- Keep screenshots for `fork` rows.
- If any row is applied, run `pnpm sync-shadcn dashboard`.

### `dashboard`

Use `sync-shadcn dashboard` when the user wants a visual decision board for
the shadcn sync delta instead of prose status output.

Purpose:

- render feature-owned sync deltas grouped by product surface, such as header,
  home, editors, releases, command menu, registry, create, preview routes, and
  sidebar
- show review states that can be re-decided later: `pending`, `defer`,
  `fork`, `rejected`, and `synced`
- keep settled exclusions visible in the dashboard without polluting routine
  `status` output
- make user review possible through a local static HTML artifact
- include browser-only state controls, note textareas, and a copy button that
  builds a concise `$sync-shadcn apply` payload
- include quick row actions in two rows: `Ask`, `Defer`, then `Sync`,
  `Reject`, `Fork`; `pending` is a state/filter, not a row action
- keep note textareas locked until the user chooses an explicit row intent:
  `Ask`, a quick apply action, or a changed state value
- render review actions as selected controls; `Sync` copies an
  `$sync-shadcn apply` row targeting `synced`, which means implementation mode
  for that row
- render `Fork` as an implementation-capable action too: copied payloads
  targeting `fork` mean "make or verify the Plate-owned fork", not "metadata
  only"
- use `suggestion` as the durable item field for Plate-owned recommendation
  text, rendered as `Suggestion` in the dashboard with an explicit `Apply`
  action that copies that suggestion into `Note`
- treat `Ask` as question-only: it keeps the current row state, writes the row
  under `Questions`, and must not mutate `deltas.json`
- treat question-only notes as questions to answer in chat, not as decisions to
  apply to `deltas.json`; only rows under `Rows` in a `$sync-shadcn apply`
  payload should mutate structured state
- open on the actionable filter by default:
  `pending` and `defer`; keep `synced`, `rejected`, and `fork` available
  behind explicit filters
- split state filters into an Action row and a Done row so active review work
  is visually separate from settled policy/history
- render Suggestion and shadcn screenshot columns for non-`synced` review rows when
  row screenshot paths exist, with click-to-zoom and close behavior in the
  static HTML page
- remove screenshot refs for `synced` and `rejected` rows during dashboard
  generation, and delete unreferenced local screenshot files; keep screenshot
  evidence for `fork` rows because forked differences stay useful
- render one card per delta item instead of a wide table, with Suggestion vs shadcn
  text in two responsive columns and empty screenshot areas hidden
- do not use an item-level `next` field in `deltas.json`; status and workflow
  guidance should come from item state, decision, suggestion, and group summary

Dashboard mode may:

- read `docs/sync/shadcn/status.json`
- read and update `docs/sync/shadcn/deltas.json`
- write `docs/sync/shadcn/dashboard.json`
- write `docs/sync/shadcn/dashboard.html`

Dashboard mode must not:

- patch `apps/www`
- write `docs/sync/shadcn/runs/**`
- change `lastSyncedCommit`, `lastPlannedCommit`, `lastPlan`, or
  `partialSyncs`
- delegate implementation to `task`
- treat a dashboard item as user acceptance to implement

State meanings:

- `synced`: the slice was accepted, implemented, and verified.
- `defer`: the item is acknowledged but intentionally postponed.
- `pending`: the item has no final decision yet, including rows waiting on a
  user decision.
- `fork`: Plate intentionally keeps a different implementation, and that
  implementation is present or has been verified before the row is marked fork.
- `rejected`: upstream behavior is explicitly excluded.

Dashboard source:

- `docs/sync/shadcn/deltas.json` is the editable structured decision source.
- `docs/sync/shadcn/dashboard.json` and
  `docs/sync/shadcn/dashboard.html` are generated views.
- When adding a landed implementation slice, update `status.json` first, then
  add or update the matching `deltas.json` feature row, then regenerate the
  dashboard.
- When rejecting, deferring, or forking a feature, record the upstream behavior,
  suggestion, owner files, and state rationale in `deltas.json`.
- For non-`synced` visual rows, add screenshot paths under
  `screenshots.suggestion` and `screenshots.shadcn` when available. Leave
  source-only rows blank.
- The HTML review controls are intentionally not persistent. The user can mark
  many rows, copy the generated prompt, and send it back; Codex applies the
  JSON edits and regenerates the dashboard.

Dashboard command:

```bash
pnpm sync-shadcn dashboard
```

This regenerates `dashboard.html` and opens it in the local browser. Use
`--no-open` or `SYNC_SHADCN_NO_OPEN=1` when running in a non-GUI check.

Dashboard output shape:

```md
Dashboard: docs/sync/shadcn/dashboard.html
Data: docs/sync/shadcn/dashboard.json
Opened: file:///.../docs/sync/shadcn/dashboard.html

| Feature | State | Items |
| --- | --- | ---: |
| Registry | defer | 2 |
```

### `review`

Use `sync-shadcn review` when the user asks whether the latest sync plan is
still fresh, whether a previously written plan can still be implemented, or
whether current `apps/www` drift changed the merge posture.

Purpose:

- prove whether `docs/sync/shadcn/status.json`, the latest plan artifacts, the
  current `../shadcn/apps/v4` target, and the current Plate docs checkout still
  describe the same sync problem
- find new upstream commits since `lastPlannedCommit`
- find stale inventory artifacts for the `lastSyncedCommit..target` range
- re-run local Plate owner/source evidence for actionable rows before
  implementation

Inputs:

- optional plan path or run directory; default to `lastPlan` from
  `docs/sync/shadcn/status.json`
- optional target ref; default to `origin/main` in `../shadcn`
- baseline from `lastSyncedCommit`
- planned target from `lastPlannedCommit`

Review mode may:

- fetch `../shadcn` and resolve exact refs
- read `docs/sync/shadcn/status.json`, the selected plan, `inventory.md`, and
  `upstream-name-status.tsv`
- recompute upstream name-status/numstat/log data into a temporary review file
  under the same run directory or under `docs/sync/shadcn/reviews/`
- compare recomputed inventories with the stored run artifacts
- re-run scoped `rg`/file-existence checks for Plate owner paths, explicit
  exclusions, preserved forks, partial sync entries, and recommended slices
- write a dated `review.md` artifact with the verdict and evidence

Review mode must not:

- patch `apps/www`
- change `lastSyncedCommit`, `lastPlannedCommit`, `lastPlan`, or
  `partialSyncs`
- delegate implementation to `task`
- create a new implementation plan or advance the baseline
- treat a fresh review as user acceptance to implement

Review checks:

```bash
git -C ../shadcn fetch origin main --tags

node -e '
const fs = require("fs");
const status = JSON.parse(fs.readFileSync("docs/sync/shadcn/status.json", "utf8"));
console.log(JSON.stringify({
  lastSyncedCommit: status.lastSyncedCommit,
  lastPlannedCommit: status.lastPlannedCommit,
  lastPlan: status.lastPlan,
  partialSyncs: status.partialSyncs?.length ?? 0
}, null, 2));
'

BASE=$(node -e 'console.log(JSON.parse(require("fs").readFileSync("docs/sync/shadcn/status.json", "utf8")).lastSyncedCommit || "")')
PLANNED=$(node -e 'console.log(JSON.parse(require("fs").readFileSync("docs/sync/shadcn/status.json", "utf8")).lastPlannedCommit || "")')
TARGET=$(git -C ../shadcn rev-parse origin/main)

git -C ../shadcn merge-base --is-ancestor "$BASE" "$TARGET"
git -C ../shadcn log --oneline --decorate "$PLANNED..$TARGET" -- apps/v4
git -C ../shadcn diff --name-status --find-renames "$BASE..$TARGET" -- apps/v4
```

Staleness verdicts:

- `fresh`: current upstream target equals `lastPlannedCommit`, recomputed
  upstream inventory matches the selected run artifact, required Plate owner
  paths/source evidence still exist, and no partial-sync/status contradiction is
  found.
- `stale-upstream`: `origin/main` has newer `apps/v4` commits than
  `lastPlannedCommit` or the recomputed upstream inventory differs from the
  selected run artifact.
- `stale-local`: Plate owner paths or local search evidence used by the plan no
  longer match the current checkout.
- `stale-status`: `status.json` contradicts the selected plan, for example the
  selected plan target differs from `lastPlannedCommit` without an explicit
  override, or `partialSyncs` claim work that the local checkout no longer
  shows.
- `blocked-ref`: the baseline, planned target, selected target, or ancestry
  cannot be proven.

The review artifact must include:

- selected plan/run directory
- base, planned target, and current target SHAs
- upstream commit count and file-status count for `PLANNED..TARGET`
- inventory comparison result for `BASE..TARGET`
- local Plate owner/source evidence summary
- explicit exclusions and preserved forks checked
- status semantics checked
- verdict and the next action

Review output shape:

```md
Review: <fresh | stale-upstream | stale-local | stale-status | blocked-ref>
Range: <base-short>..<target-short>
Plan: <path>
Report: <path>

| Check | Result | Evidence |
| --- | --- | --- |
| upstream target | ... | ... |
| inventory | ... | ... |
| Plate owners | ... | ... |
| status semantics | ... | ... |

Next: <use the existing plan | rerun planning | fix local drift | resolve refs>
```

Before substantive work:

```bash
node .agents/skills/autogoal/scripts/create-goal-scratchpad.mjs \
  --template sync-shadcn \
  --title "sync shadcn <short range or target>"
```

Fill the generated plan immediately. It must name the objective, flow mode,
completion threshold, verification surface, constraints, boundaries, output
budget strategy, blocked condition, and planned run directory. Do not replace it
with a smaller ad hoc plan.

Completion requires the named sync evidence plus:

```bash
node .agents/skills/autogoal/scripts/check-complete.mjs <docs/plans/path>
```

Never mark the active goal complete just because a range plan was written if
the goal also required implementation, baseline advancement, or user acceptance.

## User Review Boundary

This mirrors the important part of `slate-plan`: plan first, stop, then execute
only after explicit acceptance.

Planning mode may:

- fetch/pull `../shadcn`
- create the active goal plan
- write `docs/sync/shadcn/runs/<range>/` artifacts
- update `lastPlannedCommit` and `lastPlan`
- directly apply qualifying micro-overlap merges and record them as partial
  syncs
- ask one review/decision question

Planning mode must not:

- patch `apps/www` except for qualifying micro-overlap direct merges
- delegate to `task`
- advance `lastSyncedCommit`
- treat "recommended first slice" as accepted
- combine plan creation and non-micro implementation in the same activation

The final planning response must say:

```md
Review the plan. I directly merged these micro-overlap fixes: <list or none>.
To implement the remaining slices, invoke `sync-shadcn` again with the accepted
plan path and slice.
```

Implementation mode requires a later user message that names or clearly accepts
the plan/slice. Then create or continue an implementation-shaped goal for that
accepted slice and proceed through `task`.

## Hard Rules

- Use evidence, not vibes. Read upstream commits, file status, focused diffs,
  local Plate files, prior decisions, screenshots for visual surfaces, and
  relevant solution notes.
- Track exact commits. Never say "latest shadcn" without recording the target
  SHA.
- Treat `docs/sync/shadcn/status.json` as the durable baseline. Do not advance
  `lastSyncedCommit` until every upstream change in the planned range is
  accounted for as adopted, smart-merged, intentionally forked, or explicitly
  excluded.
- Planning is the default output. Do not patch `apps/www` unless the row
  qualifies as a micro-overlap direct merge or the user accepts a merge slice in
  a later instruction after reviewing a written plan.
- Do not edit generated registry output, templates output, or generated skill
  mirrors by hand.
- Do not run `build:registry`; local registry generation is CI-owned in this
  repo.
- Do not delegate to `task` in the same activation that creates or materially
  updates a sync plan. Direct micro-overlap merges stay in-thread and tiny; all
  bigger work waits for review.
- Keep the active `sync-shadcn` goal plan current after every meaningful
  decision, artifact write, classification pass, status JSON edit, accepted
  implementation slice, verification run, or blocker.
- Prefer deleting old Plate fork residue over preserving compatibility layers
  when upstream already owns the better model.
- Prefer upstream docs infrastructure unless Plate has a real product or
  registry reason to diverge.
- Keep output comprehensive. If a diff is too large for the chat, save complete
  TSV inventories under `docs/sync/shadcn/runs/<range>/` and summarize the
  artifact paths in the response.
- Do not persist `.patch` files in the repo. Inspect focused diffs on demand
  with capped `git diff` commands, summarize the relevant hunks in
  `inventory.md` or `plan.md`, and leave broad upstream patches out of
  committed sync artifacts.

## Start Gates

These gates must be resolved in the active `sync-shadcn` goal plan before broad
exploration:

- `autogoal` loaded and active goal checked or created.
- `docs/sync/shadcn/status.json` read.
- `docs/sync/shadcn/decisions.md` read.
- Prior migration plans and solution notes checked when relevant.
- Output budget strategy recorded before running upstream diff/log commands.
- `../shadcn` clone state known and fetched/pulled intentionally.
- Base and target refs resolved to exact SHAs.
- Base ancestry proven, or the ref problem recorded before stopping.
- Planning-only versus implementation mode decided.
- User-review boundary recorded: planning mode stops, implementation mode
  requires later explicit acceptance.

## Completion Gates

These gates belong in `docs/plans/templates/sync-shadcn.md` and must be closed
in the instantiated goal plan:

- Upstream range artifacts exist and are non-empty, or a target-only bootstrap
  exception is recorded.
- `inventory.md` accounts for every row in `upstream-name-status.tsv`.
- Decision counts cover every upstream row.
- Source-backed Plate mapping exists for every actionable adoption, fork,
  exclusion, or question group.
- `docs/sync/shadcn/status.json` parses and its `lastPlannedCommit` /
  `lastSyncedCommit` semantics match the work actually completed.
- Planning-only runs prove no `apps/www` implementation patch was made, or
  record and verify every qualifying micro-overlap direct merge.
- Accepted implementation runs include focused verification for the touched
  Plate surface.
- Browser proof exists when browser-visible docs UI changed, or when a
  planning scope is visual and needs Plate-vs-shadcn parity evidence.
- Visual sync scopes include screenshots of both upstream shadcn and Plate
  pages at matching viewport(s), plus written deltas such as background,
  spacing, disabled/gray controls, nav/header items, and first-viewport
  framing.
- `lastSyncedCommit` advances only after full-row accounting, verification, and
  user acceptance.
- Planning-mode final handoff lists any direct micro-overlap merges, asks the
  user to review the remaining plan, and invokes `sync-shadcn` again with the
  accepted plan path and slice for bigger work.
- `check-complete.mjs` passes for the active goal plan.

## Durable Policy

Read these before making decisions:

- `docs/sync/shadcn/status.json`
- `docs/sync/shadcn/decisions.md`
- `docs/plans/2026-05-23-shadcn-docs-restart-comparison.md`
- `docs/plans/2026-05-24-shadcn-base-migration-progress.md`
- `.agents/rules/shadcn-parity.mdc`
- `docs/solutions/best-practices/2026-05-23-shadcn-docs-restart-comparison.md`
- `docs/solutions/developer-experience/2026-05-27-shadcn-docs-sidebar-parity-needs-source-and-dom-metrics.md`
- `docs/solutions/developer-experience/2026-05-24-plate-init-routes-should-return-shadcn-registry-base-items.md`
- `docs/solutions/developer-experience/2026-05-24-shadcn-v4-registry-schema-needs-source-only-validation.md`
- `docs/solutions/developer-experience/2026-05-24-shadcn-registry-install-commands-should-use-configured-namespaces.md`
- `docs/solutions/developer-experience/2026-05-24-fumadocs-page-tree-search-needs-locale-safe-metadata.md`

Default durable decisions:

- Discard upstream v0 surfaces.
- Discard upstream `/create`, `/charts`, `/colors`, and public directory-style
  product pages unless the user explicitly asks for a Plate version.
- Discard Plate theme/customizer/project/lift-mode residue.
- Keep Plate docs content under `content/docs/**`.
- Keep committed Fumadocs metadata as the docs navigation authority.
- Keep Plate API MDX vocabulary and generated API docs support.
- Keep Plate registry content and docs-registry generation, aligned to shadcn v4
  schema/resolver semantics.
- Keep Plate editor demos, `/view/[name]`, and registry preview/source display.
- Keep lazy code-view source loading through `/api/registry-source/[name]` for
  bandwidth, but do not treat it as a public registry API.
- Keep CN docs, MCP docs/dialog, Plate Plus/Pro hooks, GA, Plate home page, and
  the Slate-to-HTML special page.
- Keep Plate's sidebar accordion/filter UX only as an intentional fork rebuilt
  on Fumadocs/upstream sidebar primitives.

## 1. Establish Upstream Clone And Refs

Do this only after the `autogoal` start gates are satisfied and the active
`sync-shadcn` plan records the output budget strategy.

Use `../shadcn` as the upstream clone. Create it only if missing:

```bash
test -d ../shadcn/.git || gh repo clone shadcn-ui/ui ../shadcn
git -C ../shadcn fetch origin main --tags
test -d ../shadcn/apps/v4
```

Read the tracked baseline:

```bash
node -e '
const fs = require("fs");
const status = JSON.parse(fs.readFileSync("docs/sync/shadcn/status.json", "utf8"));
console.log(JSON.stringify(status, null, 2));
'
```

Resolve refs:

```bash
BASE=$(node -e 'console.log(JSON.parse(require("fs").readFileSync("docs/sync/shadcn/status.json", "utf8")).lastSyncedCommit || "")')
TARGET=$(git -C ../shadcn rev-parse origin/main)
git -C ../shadcn log -1 --format='%H%n%ci%n%s' "$TARGET"
```

If $ARGUMENTS names a base or target ref, prove it exists and use it:

```bash
git -C ../shadcn rev-parse <base-or-target-ref>
```

If `BASE` is empty, run a bootstrap audit:

- compare the whole `../shadcn/apps/v4` source against Plate
- write a plan
- ask the user before setting `lastSyncedCommit`
- do not silently set the baseline

If `BASE` is present, prove ancestry when possible:

```bash
git -C ../shadcn merge-base --is-ancestor "$BASE" "$TARGET"
git -C ../shadcn log --oneline --decorate "$BASE..$TARGET" -- apps/v4
```

If `BASE` is not an ancestor of `TARGET`, stop and explain the ref problem
before planning. Do not produce a misleading change list.

## 2. Create A Run Artifact Directory

Use a range-keyed directory so the full evidence survives beyond chat context:

```bash
RUN_DIR="docs/sync/shadcn/runs/$(date +%Y-%m-%d)-${BASE:0:7}-to-${TARGET:0:7}"
mkdir -p "$RUN_DIR"
```

Save complete inventories:

```bash
git -C ../shadcn diff --name-status --find-renames "$BASE..$TARGET" -- apps/v4 \
  > "$RUN_DIR/upstream-name-status.tsv"

git -C ../shadcn diff --numstat "$BASE..$TARGET" -- apps/v4 \
  > "$RUN_DIR/upstream-numstat.tsv"

git -C ../shadcn log --oneline --decorate "$BASE..$TARGET" -- apps/v4 \
  > "$RUN_DIR/upstream-commits.txt"
```

For bootstrap audits without a base ref, use `target-only` in the directory
name and save a full current upstream file list instead:

```bash
git -C ../shadcn ls-files apps/v4 > "$RUN_DIR/upstream-files.txt"
```

Do not stream huge diffs into chat and do not write `.patch` files. Inspect
focused diffs on demand with capped commands, then summarize only the relevant
hunks in `inventory.md` or `plan.md`:

```bash
git -C ../shadcn diff --stat "$BASE..$TARGET" -- apps/v4/app apps/v4/components apps/v4/lib
git -C ../shadcn diff "$BASE..$TARGET" -- apps/v4/app/(app)/(root)/page.tsx | sed -n '1,220p'
```

If the focused diff is still too large, narrow by path/function/search term and
record the command plus summary instead of saving the diff body.

## 3. Classify Upstream Changes

Every upstream changed file must be assigned to one subsystem:

- `docs-engine`: Fumadocs source, page tree, MDX compilation, raw markdown
- `routing`: Next routes, rewrites, metadata, layout groups
- `shell-nav-sidebar`: header, footer, sidebar, mobile nav, command/search UI
- `mdx-code`: MDX components, code blocks, copy-page, source viewer, RSS/OG
- `registry-contract`: shadcn schema, resolver semantics, namespace behavior,
  init/base item, local-file install
- `registry-build`: registry scripts, generated source indexes, validation
- `preview-view`: block/component preview routes and iframe/source display
- `product-page`: create, charts, colors, blocks gallery, directory, examples
  pages that are shadcn product surfaces
- `theme-style`: global CSS, theme providers, tokens, active theme, customizer
- `deps-config`: package, lock, tsconfig, eslint, Next config
- `tests`: upstream app tests and fixtures
- `assets`: public assets, manifest, images, fonts
- `other`: only with an explanation

For each file, record:

- upstream status: added, modified, deleted, renamed
- upstream path
- subsystem
- nearest Plate owner path, or `none`
- local search evidence
- default decision
- confidence

Useful local mapping heuristics:

| Upstream path | Plate path to inspect |
| --- | --- |
| `apps/v4/app/**` | `apps/www/src/app/**` |
| `apps/v4/components/**` | `apps/www/src/components/**` |
| `apps/v4/lib/**` | `apps/www/src/lib/**` |
| `apps/v4/hooks/**` | `apps/www/src/hooks/**` |
| `apps/v4/content/docs/**` | `content/docs/**` |
| `apps/v4/registry/**` | `apps/www/src/registry/**` |
| `apps/v4/scripts/**` | `apps/www/script

…(truncated)
