playground-design-system/docs/vendoring-and-re-sync.md
Kjell Tore Guttormsen 7448cec304 feat(sync): restore sync-design-system.mjs with an explicit source boundary
The script that produced the existing vendored copies was deleted with the
monorepo layout in 2026-06 and recovered from catalog's history
(e84dffd^:scripts/sync-design-system.mjs). It could not be restored unchanged:
it walks its source directory and copies everything, and this repo's root is no
longer a directory holding only the design system. Run verbatim it vendored 109
files instead of 27, STATE.md and the whole of .git/ among them - and STATE.md
is gitignored precisely because this repo's remote is public.

The boundary is an explicit allowlist (DELIVERED_FILES) rather than a dist/
directory: one definition, read directly by the tests, with no copy step to go
stale. The allowlist's own failure mode - a new design-system file forgotten
from the list ships nothing, and --check stays green because it hashes a
consumer's tree against that consumer's own MANIFEST - is closed by scanning
the source for unlisted root-level *.css, fonts/* and schemas/* and refusing to
run.

tests/sync-source-boundary.test.mjs pins both directions. It was red on the
verbatim script for the right reason (STATE.md present in the target, with
tokens.css as the known-positive control) before the boundary was written.

--target is the consumer's repo root; the script appends
playground/vendor/playground-design-system/ itself. --check verified read-only
against both consumers: MANIFEST OK (27 files) for ms-ai-architect and
llm-security alike. No re-sync was run, so both MANIFEST.json files are
untouched and the stale source_commit is still theirs to inherit at the first
real re-sync.

README and docs/vendoring-and-re-sync.md now publish the command, and warn that
a plain diff/cmp against this repo reports all nine stylesheets as drifted
because the sync injects a generated-header line the source does not have -
17/27 raw, 26/27 with line 1 stripped. Found by ms-ai-architect, re-measured
here.
2026-08-27 00:45:58 +02:00

12 KiB

Vendoring and re-sync

Status: decision recorded 2026-08-27; the re-sync script was restored the same day with an explicit source boundary. See Current state for the published command.

This document answers three questions raised by a consumer repo on 2026-08-26: whether a current re-sync path exists, whether vendoring is still the recommended consumer model, and where MANIFEST.json's source field should point now that the marketplace is a polyrepo.

Every number below was measured against ground truth on 2026-08-27, not recalled. The measurements are listed in Verification log so a reader can re-run them.

Background

Until 2026-06-18 the marketplace was a monorepo. This design system lived at shared/playground-design-system/, and scripts/sync-design-system.mjs vendored it into each plugin's playground/vendor/playground-design-system/. Commit e84dffd ("thin catalog to manifest + docs (polyrepo migration complete)") deleted both paths in the same change. This repo was extracted as a sibling with the CSS files at its root and a fresh 17-commit history.

Consumer documentation kept pointing at the monorepo layout, so the instructions it gave named a directory and a script that no longer existed anywhere.

Decision

This is settled first, because the other two answers depend on it.

The measurement supports keeping it. Three and a half months after the last sync, in both consumers, 26 of 27 vendored files are byte-identical to this repo's current content, and each MANIFEST.json still matches its own files exactly — no consumer has edited a vendored file locally. The one file that differs is this repo's own README.md, which documents the system rather than being part of what it delivers.

Vendoring did what it promised: the consumers stayed standalone and kept rendering correctly when the source moved out from under them. What broke was the documentation pointing at the mechanism, not the mechanism.

Nothing replaces it. A git submodule or an npm package would contradict the Non-goals in README.md: no build step, no bundler, no package registry, and a Playground that opens from file:// with only the CSS files alongside it.

2. Is there a current re-sync path? Yes, since 2026-08-27.

scripts/sync-design-system.mjs was absent from the entire marketplace tree. It survived in full in the catalog repo's history, which carries the pre-migration monorepo history (977 commits), and was read back with:

git -C <marketplace>/catalog show e84dffd^:scripts/sync-design-system.mjs

It is 225 lines, has no npm dependencies, and — this matters — it was already prepared for the polyrepo split before the split happened. It accepts --source <dir> and --target <dir>, both of which override the hardcoded monorepo paths, and its own comment says the --target flag exists "so the script still works once plugins/<name> no longer exists in-repo (post-migration / extracted repo)". It also has a --check mode that re-hashes a consumer's vendored tree against its committed MANIFEST.json and exits non-zero on drift, needing no source at all.

So restoring it is recovery, not new development, and it belongs in this repo — the mechanism vendors from the design system, so the design system owns it.

It could not be restored unchanged. The script walked its source directory and copied everything it found. In the monorepo, shared/playground-design-system/ contained only the design system. This repo's root contains the design system plus the repo apparatus: STATE.md, LICENSE, SECURITY.md, .git/, docs/, and playground-examples/. That is not a hypothetical: run verbatim against this repo's root, the recovered script vendored 109 files instead of 27, STATE.md and the whole of .git/ among them — and STATE.md is deliberately gitignored because this repo's remote is public.

The boundary chosen is an explicit allowlist (DELIVERED_FILES in the script) rather than a dist/ directory. A dist/ would duplicate every stylesheet and insert a copy step that can silently go stale; the allowlist names the delivered set in one place that the tests read directly, so there is no second definition to drift from.

An allowlist has one failure mode of its own: a file added to the design system but not to the list would silently stop shipping, and --check would never notice, because it hashes a consumer's tree against that consumer's own MANIFEST.json and never against the source. This repo has already lived through that shape once — components-tier4-project-view.css was added in v0.6.0 and was missing from the README's own directory tree until 2026-08-18. So the script scans the source for design-system files the allowlist does not name (root-level *.css, plus everything under fonts/ and schemas/) and refuses to run, naming the file. Both directions are covered by tests/sync-source-boundary.test.mjs.

3. Where should MANIFEST.json's source point? Here — but not yet.

This is not the consumer's business alone: the field is written by this repo's script and describes this repo's source, so the value is ours to define.

The important finding is that source_commit is not merely unreachable — it is false. Both consumers record source_commit: c1b7bad3899c5cfe9ff90663003609b018aa79a0, but the vendored tree contains components-tier4-project-view.css, and that file demonstrably did not exist in c1b7bad. The sync labelled its output with the marketplace's HEAD while copying the working tree, which at that moment held a v0.6.0 file that had not been committed yet.

The consequence for repair: do not fix the field by translating the path. Rewriting shared/playground-design-system/ to this repo's name while keeping c1b7bad would turn an obviously stale pointer into a plausible false one, and a plausible false pointer is worse — it stops inviting the check that would catch it.

The correct order is: restore the script with a source boundary, run one re-sync, and let the script write a truthful manifest in which source_commit is this repo's HEAD — reachable from this repo, unlike c1b7bad. Until then, a visibly stale field is the more honest state.

One correction to the premise this question arrived with: the lineage is not severed. c1b7bad is unreachable from this repo and from either consumer, but it is alive in catalog. "Nobody can compute how far behind a vendored copy is" does not hold — it was computed, and the answer is one file.

Current state

Re-sync, run from a checkout of this repo with the consumer checked out alongside it:

node scripts/sync-design-system.mjs <consumer-name> --target ../<consumer-repo>

Integrity check, which needs no source checkout and writes nothing to the consumer:

node scripts/sync-design-system.mjs <consumer-name> --target ../<consumer-repo> --check

--target is the consumer's repo root; the script appends playground/vendor/playground-design-system/ itself. Pointing --target at the vendor directory instead makes --check look one level too deep, find no MANIFEST.json, and report drift that does not exist.

One trap worth naming for anyone checking by hand instead. The sync injects /* Code generated by sync-design-system.mjs; DO NOT EDIT. */ as line 1 of every .css, and the source has no such line, so a plain diff or cmp against this repo reports all nine stylesheets as drifted when none of them is: raw cmp matches 17 of 27 files, and stripping line 1 from the vendored .css files puts it back at 26 of 27 with this repo's README the only real difference. --check is immune, because it hashes the vendored tree against MANIFEST.json, which was built after the header was injected. This was found by ms-ai-architect on their own copy and re-measured here (see the Verification log).

MANIFEST.json should still be left alone. This session restored the script but ran no re-sync, so the fields described in § 3 above are unchanged and the reasoning there still holds: the first real re-sync is what rewrites them truthfully.

Verification log

Measured 2026-08-27 against ground truth. <marketplace> is the polyrepo root.

Claim How it was checked Result
sync-design-system.mjs absent from the marketplace find over the tree, maxdepth 5, .git/node_modules pruned 0 hits. Denominator: the same query returned 836 .mjs files; known-positive control found catalog/scripts/check-versions.mjs, so the query can find.
shared/ absent find -type d -name 'shared*', maxdepth 3 0 hits
Two consumers vendor the system find -type d -name playground-design-system, maxdepth 4 ms-ai-architect and llm-security, 27 files + MANIFEST.json each
c1b7bad unreachable from this repo git cat-file -t fatal: could not get object info. Known-positive control on this repo's HEAD returned commit.
c1b7bad reachable from catalog git -C catalog cat-file -t commit — 2026-05-15, monorepo history, 977 commits
The monorepo commit held both deleted paths git -C catalog ls-tree -r c1b7bad shared/playground-design-system/ (26 files) and scripts/sync-design-system.mjs both present
Both paths died in the migration git -C catalog log --diff-filter=D e84dffd, 2026-06-18, deleted both
source_commit is false git -C catalog cat-file -e c1b7bad:shared/playground-design-system/components-tier4-project-view.css path does not exist in c1b7bad. Known-positive control on components-tier3.css in the same commit succeeded.
No local drift in either consumer SHA-256 of each vendored file vs its own MANIFEST.json both MANIFEST OK, 27/27
Drift vs this repo SHA-256 vs this repo's root, generated-header stripped from .css 26/27 identical in both consumers; README.md the only difference
The recovered script supports polyrepo read of git show e84dffd^:scripts/sync-design-system.mjs --source, --target, --check all present and honoured in main()
The verbatim script leaks this repo's apparatus ran the recovered script unchanged, --source <repo root>, into a temp target 109 files vendored instead of 27; STATE.md, .git, .gitignore, docs, playground-examples, LICENSE, SECURITY.md all present. tests/sync-source-boundary.test.mjs red on this build, with tokens.css present as the known-positive control proving the assertions read the right path.
The allowlisted script does not leak node --test tests/sync-source-boundary.test.mjs after the boundary was added 2/2 pass: 27 files + MANIFEST.json, none of the seven apparatus entries present
An unlisted design-system file fails the sync test adds components-tier5.css to a complete synthetic source non-zero exit, filename named in stderr. Known-positive control: the same source without the extra file syncs cleanly.
The published --check command works against both consumers node scripts/sync-design-system.mjs <name> --target ../<name> --check MANIFEST OK (27 files, source_commit c1b7bad3899c5cfe9ff90663003609b018aa79a0), exit 0, for ms-ai-architect and llm-security alike
A raw cmp misreports the CSS as drifted cmp per file vs this repo's root, then cmp with line 1 stripped raw: 17/27 match, the 10 differing being 9 .css + README.md; line-1-stripped: 26/27, README.md alone. Independently re-measured here; matches ms-ai-architect's finding exactly.

Not verified: whether catalog's history is itself mirrored anywhere off this machine. The recovery path above assumes a local catalog checkout.