/design-sync skill: reconciliation-delete/no-anchor logic can destroy content added by claude.ai/design's in-app "Generate" step

Open 💬 0 comments Opened Jul 3, 2026 by addisonlynch

Summary

The /design-sync skill's protocol for re-syncing and closing out an upload (reconciliation deletes, "no anchor → treat unrecognized files as orphans to delete") assumes the skill's DesignSync API uploads are the only writer to a claude.ai/design project. That assumption breaks once the in-app "Generate" feature (see #74037) is used on the same project — a legitimate, intended workflow that adds real content the sync tool has no way to know about, and would delete or overwrite if it ever ran its documented reconciliation logic.

Background

This follows directly from #74037. After filing that issue, we clicked "Generate" on the project (per the outcome discussed there) and it correctly built on top of our uploaded components rather than replacing them — good. But in doing so it wrote substantial new content directly into the same project:

  • guidelines/*.html — 16 brand/color/type/spacing guideline pages
  • fonts/general-sans.css — it sourced the brand font itself
  • ui_kits/web/{home.html,index.html} — UI mockups composed from the real synced components
  • carriers/*.svg, source-logo SVGs, screenshots/*.png
  • Edits to README.md and a new SKILL.md

None of this exists in, or is derivable from, the local repo/build the /design-sync skill runs from. It's purely a product of the in-app agent session.

The gap

The bundled skill (design-sync/non-storybook/SKILL.md, package shape, section "## 5. Upload") documents two paths that both become unsafe once a project has out-of-band content like the above:

  1. Anchored re-sync: deletes should come "verbatim from .sync-diff.json's upload.deletePaths" — fine in isolation, but the anchor (_ds_sync.json) only ever describes files the sync tool uploaded. It has no representation of guidelines/, fonts/, ui_kits/, etc., so nothing about the anchor protects them; they're simply invisible to it either way (not itself dangerous, but see point 2).
  2. No-anchor case (a re-adopted or not-yet-anchored project being fully re-verified): the skill instructs reviewing the project's list_files output "for files this build doesn't produce" and putting those into the upload plan's deletes. Followed literally here, that instruction says to delete all 16 guideline pages, the font file, the UI kit pages, the logo/carrier SVGs, and the screenshots — real, valuable, intentionally-created content — because the local build "doesn't produce" them. There is no guidance anywhere in the skill for distinguishing "orphan file from a prior sync" from "legitimate content the in-app agent added."
  3. Separately, the skill's documented default upload writes list always includes README.md (regenerated from scratch on every local build) with no guard against the in-app agent having already edited/enriched that same file server-side. A routine future component-batch upload would silently clobber those edits.

Suggested fix

Something that lets a sync distinguish "files this tool has previously uploaded/owns" from "everything else in the project," independent of whether an anchor exists yet, so the no-anchor fallback doesn't default to "delete anything unrecognized." Options that would each resolve this:

  • Track sync-owned paths explicitly (e.g. a manifest of paths written by any given sync tool identity), so orphan-detection only ever considers that tool's own prior paths, never the whole project.
  • Scope the skill's default writes/deletes guidance to an explicit allowlist of directories the package/storybook conversion is defined to own (components/, _preview/, _vendor/, the root bundle/css/sentinel files) and exclude everything else (guidelines/, fonts/, ui_kits/, screenshots/, carriers/, README.md after first upload, SKILL.md, _ds_manifest.json, _adherence.oxlintrc.json) from any automatic delete or default-overwrite logic, since those are known to be self-check/in-app-agent-owned or -enrichable.
  • At minimum, call this interaction out explicitly in the skill's docs so an agent following it knows to stop and ask a human before running the no-anchor orphan-delete step on a project that has ever been opened/used in the app.

Related

#74037 — the UX issue that led us to click "Generate" on an API-populated project in the first place. That interaction is what produced the out-of-band content described here.

Environment

  • /design-sync skill, package (non-Storybook) shape
  • Project populated via DesignSync API tool, then opened and built on via the in-app claude.ai/design "Generate" flow

View original on GitHub ↗