Files
workbuddy_skills/open-design/design-systems/_schema/AGENTS.md
T
admin 5afeb6b78a ④段整套同步(六节 + P/S/F/J + 模板)+ open-design 移到技能根
一、④段整套同步(用户选候选 A)
- 新增 `references/stage-proto-doc/assets/产品文档模板.html`(16 KB/247 行,从备份拷、转 LF)。
- ④段 SKILL.md 换成备份 15:28 那版,并按模板口径改齐:
  · 「产品文档框架」从「四块+一附录」改成**六节** —— 页面结构/功能清单/使用场景/操作流程/
    跨页与界面状态/待确认与偏离;与模板同构,⛔ 不从空白页重写结构。
  · 编号从 R/S/F 改成 **P 页面/S 场景/F 功能/J 二级功能**,各自唯一、按展示顺序通排。
  · 保留旧版有价值的要求:每项功能补齐 做什么/入口在哪/输入边界/异常与恢复;重点功能三处标红;
    横向流程图每步写清页面/功能/操作/完成判据。
  · 完成标准「七节」→「六节」,自查清单同步换掉「需求」那批条目,补模板与配图两条。
  · 路径 `designs/<项目>/` → `docs/pm/<项目>/ui/`(2 处)。
  · 补回本机那条「每份产出文档开头都要有一张图」的引用(指向主 skill,段内不复述)。
- 主 skill 同步:四段结构表④行、④段沿革、画图小节(④段画页面结构图 + 用户路径)。

二、open-design 移到技能根(用户选候选 A)
- `git mv product-planning/references/stage-delivery/assets/open-design → open-design/`(4178 文件)。
- `.gitignore` 白名单加 `!/open-design/` —— 否则移出去就不再纳入版本控制。
- 引用改 11 处:路径形态 `assets/open-design/...` → `<技能根>/open-design/...`;
  「随段携带的自带件」改称「同根技能」;「路径怎么找」那条改成自带件(段内相对路径)与
  同根技能(`<技能根>/<技能名>/`)两类分开写。
- open-design 自己的 ATTRIBUTION.md 两处同步(它现在与 oil-ui-pro 同层)。
- 副作用(有益):命名闸门读数由 2063 降到 225 —— 此前它把 open-design 里 919 个第三方 md
  当成规则一起扫了;现在只扫本包 23 个 md。已确认④段的 H1 与 4a/4b/4c 子步仍在检查范围内。

三、验收
- check_naming.py --ws E:/ProgramData/AIProject/contentm_agent = 通过 225 / 失败 0 / 提示 27。
- 活文件里 `assets/open-design` 残留 0;工作区 CRLF 文件 0。
2026-10-15 21:17:28 +08:00

10 KiB

_schema/ — design-system contracts

This directory codifies the structural contracts for design systems. tokens.schema.ts re-exports the token contract that every tokenized brand under design-systems/<brand>/ must satisfy. The canonical runtime copy lives in packages/contracts/src/design-systems/token-schema.ts so daemon importers and repo guards consume one schema. manifest.schema.ts is the project contract for Design System Project packages. Every bundled catalog entry now ships manifest.json, DESIGN.md, and tokens.css; the daemon keeps DESIGN.md-only discovery solely as a compatibility path for older or user-installed folders.

_schema/
├── manifest.schema.ts ← project manifest schema (TS, machine-enforced when present)
├── tokens.schema.ts   ← token schema re-export (TS, machine-enforced)
├── defaults.css       ← A2 fallback values (CSS, human reference)
└── AGENTS.md          ← this file

The TypeScript schemas are the source of truth. defaults.css is a human-readable mirror of the A2 fallback fields in the token schema and exists so that reviewers can scan real CSS without parsing a TS array — drift between the two is enforced by the design-system: A2 defaults parity guard. Manifest shape is enforced by scripts/check-design-system-manifests.ts for any design-systems/<brand>/manifest.json that exists.

Project manifest contract

Design System Project folders use fixed v1 file names:

  • manifest.json — machine-readable project entry.
  • DESIGN.md — canonical design prose.
  • tokens.css — canonical compiled tokens.
  • design-tokens.json — optional Design Tokens JSON derived from tokens.css + source/token-contract.report.json.
  • tailwind-v4.css — optional Tailwind v4 @theme CSS derived from tokens.css; it must not redefine source values independently.
  • components.html — optional standalone component fixture.
  • assets/ — optional brand assets.
  • preview/ — optional static preview pages.
  • USAGE.md — optional agent-facing package guide.
  • components.manifest.json — optional rebuildable cache derived from components.html and tokens.css.
  • fonts/ — optional webfont files.
  • source/ — optional importer evidence (scanned-files.json, evidence.md, tokens.source.json, token-contract.report.json, and snippets/INDEX.json).

The manifest guard validates every bundled package through its manifest.json. When rich fields are declared, paths must be safe and present, JSON indexes must parse, and committed components.manifest.json files must match a fresh derivation from components.html plus tokens.css. These fields are also runtime inputs: catalog discovery consumes manifest metadata; prompt composition consumes usage, tokens, component information, import mode, craft bindings, and the manifest-derived pull index; package/static-file routes expose only declared preview and source files. Legacy DESIGN.md-only folders remain outside this manifest guard because they have no manifest to validate.

Four layers, two questions

Every shared token answers two questions:

  1. Who decides the value? — the brand author (Layer A) or the schema author (Layer B-slot, when the brand has no opinion).
  2. What happens if the brand omits it? — required, fallback, or alias.

The four layers fall out of those answers:

Layer Who decides If omitted Examples
A1-identity brand guard fails --bg, --fg, --accent, --font-display
A1-structure brand guard fails type scale, --container-max, --section-y-*
A2 brand (with fallback) guard fails today; derive script fills tomorrow --motion-fast, --success, --space-4, --font-mono
B-slot brand or schema-suggested alias guard fails — brand must declare, either as var(--sibling) (collapsed) or independent value (richer) --fg-2 → var(--fg), --surface-warm → var(--surface)

Brand-specific tokens that fall outside the shared schema are tracked as C-extensions in BRAND_EXTENSIONS (per-brand allowlist) or BRAND_EXTENSION_PREFIXES (global prefix allowlist for whole families like --tag-bg-*).

Why A2 fails the guard today (and might not later)

A2 conceptually means "optional with fallback" — but artifacts are generated by agents pasting one brand's :root block into a single <style>. There is no global stylesheet that loads alongside the brand, so a missing --motion-fast resolves to nothing inside the artifact and any transition: var(--motion-fast) rule silently breaks.

Until a future derive script lands and inlines defaults.css values into every brand's tokens.css, the only safe contract is "every brand must declare every A2 token". The design-system: A2 required tokens guard enforces that strictly.

After the derive script ships, brand authors only need to write the A1 tokens (and any A2 they want to override); the script populates A2 slots from defaults.css. The guard contract does not change — every final tokens.css still contains every A2 token — but the work shifts from human author to script.

Why B-slot is required (and what the alias is for)

Same artifact-paste constraint applies to B-slot tokens. Shared components reference richer tiers via var(--fg-2), var(--meta), var(--surface-warm), var(--border-soft) — if a brand omits the slot, those references resolve to nothing and the artifact silently breaks.

The aliasTo field on each B-slot entry is the schema-suggested default, not a runtime fallback. A brand with no opinion on the richer tier copies the alias verbatim into its :root:

--fg-2: var(--fg);                /* default brand: 2-level fg */
--surface-warm: var(--surface);   /* default brand: 2-level surface */

A brand that does have the richer tier binds an independent value:

--fg-2: #3d3d3a;                  /* kami brand: dark warm */
--surface-warm: #e8e6dc;          /* kami brand: warm sand */

Either form satisfies the design-system: B-slot required tokens guard. The pre-derive-script contract is identical to A2: every brand's :root declares every shared slot.

C → B-slot → A2 promotion path

Brand-specific tokens start in BRAND_EXTENSIONS[brand]. They earn promotion when a second brand needs the same name:

C-extension                    B-slot                       A2
(one brand declares it)        (multiple brands declare,    (every brand declares
                                some alias to a sibling)     with a sensible default)

kami: --leading-display    →   schema: --leading-display    →   schema: --leading-display
                               aliasTo: var(--leading-tight)    fallback: 1.1

Concrete promotion rules:

  1. C → B-slot when ≥2 brands declare a token of the same name and there is a meaningful sibling to alias to for brands that lack the richer tier. Move the entry from BRAND_EXTENSIONS to TOKEN_SCHEMA with layer: "B-slot" and aliasTo: "var(--sibling)".
  2. C → A2 when ≥2 brands declare a token of the same name and a defensible cross-brand fallback exists (no aliasing needed). Move to TOKEN_SCHEMA with layer: "A2" and a fallback, then mirror the value in defaults.css.
  3. B-slot → A2 when a B-slot starts being independently bound by ≥2 brands (instead of aliasing). Replace aliasTo with fallback and add a defaults.css declaration.
  4. A2 → A1 is rare. It happens when the previously-defaultable value turns out to be brand-determining — e.g. if a future brand redefines --motion-base from 200ms to 50ms because its identity is "instant", and that change ripples meaningfully through the brand voice. Drop the fallback and reclassify.

Demotion (A → B → C) is not currently supported. A token that is genuinely no longer needed should be marked @deprecated in the schema for one release and then deleted from every brand's tokens.css in the same PR.

When not to add a token

Schema growth has a cost — every new entry forces every bundled brand to declare or alias the new name when the derive script next runs. Resist adding tokens that are:

  • Component-internal: a .btn-primary background offset that no other component will ever read. Inline the value in the component rule.
  • One-off: a single layout's hero crop ratio. Not a token.
  • Speculative: "we might want a --motion-slow someday." Add it the first time a real interaction needs it, not before.
  • Already expressible: a --accent-tint-50 that resolves to color-mix(in oklab, var(--accent), transparent 50%). Inline the color-mix(...) call until ≥2 components need the same tint with the same alpha, then promote to a token.

Editing this directory

When you change the token schema:

  • Run pnpm guard and confirm the bundled catalog still passes every design-system sub-check.
  • If you added an A2 entry: also update defaults.css with the matching declaration, byte-equivalent to the fallback field.
  • If you renamed a token: bump every brand's tokens.css and the matching components.html :root paste in the same commit. Otherwise the drift guard will fail.
  • If you removed a token from TOKEN_SCHEMA and the same name now appears in only one brand: add it to that brand's BRAND_EXTENSIONS entry so the unknown-token guard does not fail.

Open questions for the future derive script

The checked-in schema and guards enforce the final token contract today, but the following authoring-automation questions are intentionally not answered:

  • How does the derive script source A1 values from DESIGN.md? Some sections (color palette, type scale) parse cleanly; others (visual atmosphere, do's and don'ts) do not. A frontmatter or fenced-block convention will likely emerge.
  • What happens when a brand's DESIGN.md contradicts itself? e.g. accents listed as both cobalt and indigo. The derive script will need a deterministic resolution (last-wins, manual override flag, or hard fail).
  • Are A2 fallback formulas stable when re-derived? Bit-for-bit reproducibility of the script's output is required so that running the script twice on the same input does not churn the brand token files.

These remain future work for a change that introduces scripts/derive-tokens-css.ts; that script does not exist yet.