| description | Autogoal-backed fork-aware status, planning, review, dashboard, apply, and tracking for syncing Plate UI registry components into downstream custom apps such as Potion. Use when the user asks for `sync-plate-ui`, `sync-plate-ui status`, `sync-plate-ui plan`, `sync-plate-ui review`, `sync-plate-ui dashboard`, `sync-plate-ui apply`, to sync or migrate one Plate UI component from the component changelog, to compare Plate UI registry source with a downstream repo, or to smart-merge local forked Plate UI components. |
| name | sync-plate-ui |
| metadata | {"skiller":{"source":".agents/rules/sync-plate-ui.mdc"}} |
Sync Plate UI
Handle $ARGUMENTS.
Goal: sync Plate UI registry components from this repo into a downstream app
without flattening that app's product forks. The workflow reads Plate registry
source, generated changelog JSON, and downstream local files; classifies
upstream-owned versus target-owned hunks; writes reviewable artifacts; then
applies only accepted sync rows.
This is not sync-shadcn. sync-shadcn compares upstream shadcn docs with
Plate docs. sync-plate-ui compares Plate UI registry source with a target app
that copied and customized Plate UI, such as ../potion.
This skill is consumer-side only. It reads generated Plate UI changelog JSON;
it does not create upstream Plate changelog entries. Upstream Plate devs author
those through the changeset skill and
tooling/data/plate-ui-changelog.mdx.
Core Take
Downstream Plate UI files are usually mixed ownership. One hunk can be a local
product fork, the next hunk can be a Plate bugfix the target should receive.
Never treat an entire file as forked or safe to overwrite unless the source
evidence proves it.
The right model is a three-way sync:
base: the last Plate UI source known to be applied to the target
upstream: current Plate UI source in this repo
local: current target repo file
If the base is unknown, run bootstrap planning and stop. Do not pretend an LLM
can safely reconstruct fork ownership from vibes.
Autogoal Dependency
This skill depends on
$autogoal. Load
autogoal before writing target sync state, run artifacts, dashboards, or
implementation changes.
Default goal template:
node .agents/skills/autogoal/scripts/create-goal-scratchpad.mjs \
--template sync-plate-ui \
--title "sync plate ui <target> <scope>"
autogoal owns lifecycle, completion, blocked semantics, and final
check-complete.mjs. sync-plate-ui owns downstream sync policy, target repo
mapping, fork classification, hunk decisions, and apply semantics.
Flow Modes
Planning mode is the default. It may read the target repo and write artifacts
under the target's .plate-ui-sync/**, but it must not mutate target source.
Apply mode starts only when a later user message accepts a named plan, dashboard
payload, component, or row. Then apply only the accepted rows.
Collaborative planning is for policy decisions, such as whether a downstream
app should keep a local UX fork or follow Plate.
No direct micro-merge exception by default. For downstream product repos,
"small" is not enough. Exact-match upstream-only files can be applied in apply
mode without user-by-hunk review, but planning still records the row first.
Command Parsing
Supported commands:
status: read-only summary of target sync state and likely next step
plan: write a component or range sync plan
review: re-audit an existing plan against current Plate and target source
dashboard: write a review board from open plan rows
apply: apply accepted rows from a copied dashboard payload or named plan
If the first token is a command, dispatch to that command. Otherwise treat the
argument as planning scope. Examples:
sync-plate-ui ../potion code-block-node
sync-plate-ui plan ../potion code-block-node
sync-plate-ui status ../potion
sync-plate-ui apply ../potion
If the target repo is omitted and cannot be inferred from the current working
directory, ask one focused question. Do not silently choose ../potion unless
the user named Potion or the active plan already names it.
Target State
The target repo owns its sync state:
<target>/.plate-ui-sync/status.json
<target>/.plate-ui-sync/forks/<component>.json
<target>/.plate-ui-sync/runs/<date>-<scope>/
<target>/.plate-ui-sync/dashboard.json
<target>/.plate-ui-sync/dashboard.md
status.json tracks what has actually landed:
{
"sourceRepo": "/Users/zbeyens/git/plate",
"targetRepo": "/Users/zbeyens/git/potion",
"lastReviewedAt": "YYYY-MM-DD",
"components": {
"code-block-node": {
"lastAppliedPr": 4989,
"lastAppliedSourceRef": "<plate-git-sha>",
"lastAppliedSourceHash": "<sha256-of-transformed-source>",
"targetFiles": ["src/registry/ui/code-block-node.tsx"],
"forks": ["toolbar-style"]
}
}
}
If status.json is missing, bootstrap from direct source evidence:
- target
components.json
- target
package.json
- target copied registry paths
- Plate registry item definitions
- target imports from copied registry files
- optional user-provided base ref or prior run artifact
Bootstrap mode may write a plan and fork ledger. It must not apply changes until
the user accepts the baseline.
Plate Source Inputs
Prefer generated changelog JSON:
/registry/changelog/index.json
/registry/changelog/components.json
/registry/changelog/<event-id>.json
When working from the local Plate repo, read the same files at
apps/www/src/registry/changelog. components.json maps registry item ids to
event JSON. Each event should expose id, change source, release, affected
registry items, source files, migration notes, and diagnostics.
If generated JSON is missing or incomplete, treat that as weak evidence. Prove
the sync from Plate registry source and PR-level or merge-base diffs before
recommending apply. Prose alone is not enough.
Plate registry sources to inspect first:
apps/www/src/registry/registry-ui.ts
apps/www/src/registry/registry-kits.ts
apps/www/src/registry/registry-examples.ts
apps/www/src/registry/ui/**
apps/www/src/registry/components/**
apps/www/src/registry/hooks/**
apps/www/src/registry/lib/**
apps/www/src/registry/app/**
Do not run build:registry. Do not edit generated registry output.
Target Mapping
Read target config before path assumptions:
components.json
package.json
- lockfile and package manager
- aliases in
tsconfig.json
- copied registry folders, commonly
src/registry/**
- app-owned wrappers that import from copied registry files
For Potion-like targets, expect:
- copied Plate UI under
src/registry/**
- app-level wrappers under
src/components/**
- pinned
@platejs/* and platejs versions
- local import aliases that differ from Plate docs
- existing sync scripts that may be stale
Existing target scripts are evidence, not authority. Audit them before relying
on them.
Three-Way Classification
For every component/file row, compute:
base -> upstream
base -> local
upstream -> local
Apply target transforms before comparing when the target uses different import
aliases. Record every transform in the plan.
Classify at hunk or symbol level:
upstream-only: Plate changed, local still matches base
local-only: target fork changed, Plate did not
same-change: both changed to equivalent result
conflict: both changed the same area differently
local-delete: target removed the Plate file or symbol
rename-map: target renamed the file or export
unknown-base: no trustworthy base
Never collapse these into a single file-level decision unless every hunk has
the same classification.
Decisions
Use these decision labels:
pull-upstream: apply the Plate change to the target
keep-fork: preserve target-owned behavior
smart-merge: apply part of Plate while preserving named target behavior
reject-upstream: do not apply this Plate change to this target
delete-target-residue: remove obsolete target fork code
package-only: update package versions without copying UI source
needs-question: user decision required
no-op: no target-owned change needed
Every smart-merge row must state:
- what comes from Plate
- what stays target-owned
- what local tests or source audits prove it
- what would break if the whole file were overwritten
Planning Mode
Planning mode may:
- read Plate source
- read target source
- create or update target
.plate-ui-sync/** artifacts
- write a plan under
.plate-ui-sync/runs/**
- write dashboard JSON/Markdown
- update this goal plan
Planning mode must not:
- patch target source files
- update target packages or lockfiles
- run broad generated registry builds
- mark a component as applied
- treat a recommendation as user acceptance
Planning artifacts:
<run>/plan.md
<run>/inventory.json
<run>/inventory.md
<run>/component-diffs.md
<run>/target-files.txt
<run>/decision-counts.json
Do not persist .patch files. Inspect focused diffs and summarize the relevant
hunks.
Plan Shape
Every plan must include:
# Sync Plate UI <target> <scope>
## Range
- Plate source ref
- target repo
- component scope
- changelog entries or fallback evidence
## Target Config
- package manager
- registry paths
- aliases
- existing sync scripts and whether they are trusted
## Inventory
| Component | Plate file | Target file | Base | Class | Decision | Evidence |
## Fork Ledger
| Component | File | Hunk/symbol | Keep/Pull/Ask | Reason |
## Apply Rows
| Row | Action | Files | Why | Verification |
## Questions
## Verification Plan
## Status Update Rule
Dashboard Mode
Dashboard mode renders unresolved rows for user review. It writes:
<target>/.plate-ui-sync/dashboard.json
<target>/.plate-ui-sync/dashboard.md
If a target later adds an HTML dashboard script, use it. Do not invent a local
browser dependency for the first version.
Dashboard rows support these actions:
Pull: apply Plate row
Keep Fork: preserve target row
Smart Merge: apply with named fork preservation
Reject: mark upstream row rejected for this target
Ask: copy a question-only payload
Copied apply payload:
$sync-plate-ui apply ../potion
Rows:
- code-block-node/4989/language-label: Pull note: safe upstream-only render hunk
- code-block-node/4989/toolbar-style: Keep Fork note: Potion toolbar owns layout
Questions:
- ai-menu/streaming: Should Potion keep its custom stream controls?
Rows mutate state only under Rows. Question rows are answered in chat and do
not mutate structured state.
Apply Mode
Apply mode may mutate target source only for accepted rows.
Before applying:
- Re-read the plan and target files.
- Recompute the row classification.
- Confirm the row is still valid against current Plate and target source.
- Record target package manager and verification commands.
Apply rules:
pull-upstream: copy or patch the upstream hunk after target transforms.
keep-fork: update fork ledger only; do not change target source unless the
accepted row asks to harden the fork.
smart-merge: patch only the named Plate-owned hunks and preserve named
target-owned hunks.
reject-upstream: record the rejection with reason.
delete-target-residue: delete only named obsolete target code.
package-only: update target package versions with the target package
manager and lockfile.
Never overwrite a whole target file unless all of these are true:
- every local hunk still matches base after transforms
- the target file has no local-only hunks
- the plan marks the row
pull-upstream
- the accepted payload names that file or component
After applying:
- update
.plate-ui-sync/status.json
- update fork ledgers
- run focused target verification
- record verification in the run plan
- leave unrelated target files alone
Status Mode
Status mode is read-only. It summarizes:
- target repo and detected registry paths
- current Plate source ref if available
- known component sync state
- open fork decisions
- stale or missing baseline rows
- recommended next command
Output shape:
Status: <fresh | needs-plan | stale-source | stale-target | bootstrap-needed | blocked>
Target: <path>
Components tracked: <count>
Open decisions: <count>
Next: <command>
Review Mode
Review mode checks whether an existing plan is still true.
It may:
- re-read Plate source
- re-read target source
- recompute classifications
- write
<run>/review.md
It must not apply changes.
Verdicts:
fresh: plan rows still match current Plate and target source
stale-plate: Plate source changed since the plan
stale-target: target files changed since the plan
stale-state: .plate-ui-sync/status.json contradicts the plan
blocked: a ref/file/base cannot be proven
Potion Example
Potion is a good stress target because it contains copied registry files under
src/registry/**, app wrappers under src/components/**, and pinned Plate
packages. Its existing sync:plate and sync:potion-ui scripts must be read
before use; do not assume they sync Plate UI into Potion.
For ../potion code-block-node, inspect at minimum:
../potion/components.json
../potion/package.json
../potion/src/registry/ui/code-block-node.tsx
../potion/src/registry/ui/code-block-node-static.tsx
- related Potion editor imports
- Plate
apps/www/src/registry/ui/code-block-node.tsx
- Plate
apps/www/src/registry/ui/code-block-node-static.tsx
- component changelog entries for
code-block-node and related components
Expected result is a plan that says which hunks to pull, which Potion hunks to
keep, and what verification Potion owns.
Agent-Native Requirements
The workflow must be agent-actionable:
- commands are discoverable from this skill frontmatter and body
- component ids, PR ids, file paths, and decision rows are structured
- apply payloads are copyable from dashboard output
- target state lives in the target repo, not in chat
- all mutation rows can be resumed after context loss
- questions do not mutate state
- final handoff names exact artifacts and commands
If the component changelog is prose-only, record that as a blocker for perfect
agent-native sync and fall back to source diffs plus explicit user acceptance.
Start Gates
Resolve these before broad planning:
autogoal loaded and active goal checked or created.
- Target repo path resolved.
- Plate source ref recorded, or current checkout recorded as uncommitted source.
- Target
components.json and package manager read.
- Target
.plate-ui-sync/status.json read or bootstrap mode recorded.
- Component changelog entry read or fallback evidence recorded.
- Plate registry item and source files mapped.
- Target local files mapped.
- Output budget strategy recorded.
- Planning versus apply mode decided.
Completion Gates
Planning completion requires:
- run artifacts written under target
.plate-ui-sync/runs/**
- inventory covers every scoped component/file row
- every row has hunk-level classification or an explicit unknown-base blocker
- every row has a decision
- real questions are isolated
- dashboard artifacts written when requested
- final handoff asks the user to accept rows before apply
check-complete.mjs passes for the active goal plan
Apply completion requires:
- accepted rows revalidated before mutation
- target source/status/fork ledgers updated only for accepted rows
- target-owned verification command run or blocked with evidence
- package manager and cwd named for every command
- remaining open decisions recorded
check-complete.mjs passes for the active goal plan
Final Output
Planning output:
Target: <path>
Scope: <component/range>
Plan: <target>/.plate-ui-sync/runs/.../plan.md
Dashboard: <path or N/A>
| Decision | Count |
| --- | ---: |
| pull-upstream | ... |
| keep-fork | ... |
| smart-merge | ... |
| needs-question | ... |
Next: review the plan, then invoke `sync-plate-ui apply <target>` with accepted rows.
Apply output:
Applied: <rows>
Kept forks: <rows>
Target verification: <commands/results>
Open decisions: <count or none>
Status: <target>/.plate-ui-sync/status.json updated