@easysociable/cli 0.1.2 → 0.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (148) hide show
  1. package/README.md +32 -21
  2. package/{skills/easysociable-content → content-engine}/scripts/starter.mjs +3 -1
  3. package/{skills/easysociable-content → content-engine}/scripts/workbench.mjs +13 -8
  4. package/{skills/easysociable-content → content-engine}/starter/CLAUDE.md +217 -128
  5. package/{skills/easysociable-content → content-engine}/starter/engine.json +3 -0
  6. package/content-engine/starter/needs/README.md +38 -0
  7. package/content-engine/starter/needs/_index.md +31 -0
  8. package/content-engine/starter/needs/_template.md +24 -0
  9. package/content-engine/starter/needs/entries/README.md +5 -0
  10. package/content-engine/starter/products/README.md +38 -0
  11. package/content-engine/starter/products/_index.md +24 -0
  12. package/content-engine/starter/products/_template.md +56 -0
  13. package/content-engine/starter/products/entries/P-001-easysociable.md +150 -0
  14. package/content-engine/starter/profiles/_index.md +16 -0
  15. package/{skills/easysociable-content → content-engine}/starter/profiles/default/README.md +4 -1
  16. package/content-engine/starter/profiles/default/boundaries.md +14 -0
  17. package/content-engine/starter/profiles/default/expression.md +18 -0
  18. package/content-engine/starter/profiles/default/lanes.md +12 -0
  19. package/content-engine/starter/profiles/default/stories.md +15 -0
  20. package/{skills/easysociable-content → content-engine}/starter/profiles/default/voice.md +14 -0
  21. package/content-engine/starter/recommendations/README.md +21 -0
  22. package/content-engine/starter/recommendations/_index.md +21 -0
  23. package/content-engine/starter/recommendations/_template.md +44 -0
  24. package/{skills/easysociable-content → content-engine}/starter/runs/_template/brief.md +1 -1
  25. package/{skills/easysociable-content → content-engine}/starter/runs/_template/pack.md +15 -7
  26. package/{skills/easysociable-content → content-engine}/starter/runs/_template/packet.md +6 -0
  27. package/{skills/easysociable-content → content-engine}/starter/swipe/_template.md +3 -1
  28. package/content-engine/starter/topics/README.md +21 -0
  29. package/{skills/easysociable-content → content-engine}/starter/topics/_index.md +12 -5
  30. package/content-engine/starter/topics/_template-item.md +103 -0
  31. package/{skills/easysociable-content → content-engine}/starter/topics/scoring.md +55 -4
  32. package/content-engine/workbench/__pycache__/build.cpython-310.pyc +0 -0
  33. package/{skills/easysociable-content → content-engine}/workbench/build.py +306 -97
  34. package/{skills/easysociable-content → content-engine}/workbench/serve.py +0 -0
  35. package/{skills/easysociable-content → content-engine}/workbench/template.html +173 -27
  36. package/dist/easysociable.js +629 -142
  37. package/dist/easysociable.js.map +1 -1
  38. package/package.json +15 -15
  39. package/skills/ONBOARDING.md +21 -18
  40. package/skills/easysociable-content/SKILL.md +104 -98
  41. package/skills/easysociable-content/workflows/INTERVIEW.md +70 -0
  42. package/skills/easysociable-content/workflows/NEEDS.md +82 -0
  43. package/skills/easysociable-slideshow/SKILL.md +79 -44
  44. package/skills/easysociable-slideshow/references/ARC.md +44 -0
  45. package/skills/easysociable-slideshow/references/CONTENT_PACK.md +34 -6
  46. package/skills/easysociable-slideshow/workflows/CREATE_RUN.md +24 -5
  47. package/skills/easysociable-slideshow/workflows/OUT_OF_SCOPE.md +3 -0
  48. package/skills/easysociable-template/SKILL.md +29 -30
  49. package/skills/easysociable-template/references/CATALOG_METADATA.md +23 -20
  50. package/skills/easysociable-template/references/DESIGN_V2_1.md +53 -2
  51. package/skills/easysociable-template/references/GATEWAY.md +7 -2
  52. package/skills/easysociable-template/references/PLATFORM_SPECS.md +11 -3
  53. package/skills/easysociable-template/references/THEME_TOKENS.md +1 -1
  54. package/skills/easysociable-template/references/fixtures/image-reference.design-plan.json +74 -0
  55. package/skills/easysociable-template/references/fixtures/image-reference.visual-qa.json +28 -0
  56. package/skills/easysociable-template/scripts/generate-theme-from-hex.mjs +0 -0
  57. package/skills/easysociable-template/scripts/validate-design-v2.mjs +97 -0
  58. package/skills/easysociable-template/workflows/ASSET_PLAN.md +2 -1
  59. package/skills/easysociable-template/workflows/AUTHORING_FROM_BRIEF.md +4 -4
  60. package/skills/easysociable-template/workflows/AUTHORING_FROM_IMAGE.md +66 -16
  61. package/skills/easysociable-template/workflows/AUTHORING_STUDIO.md +28 -15
  62. package/skills/easysociable-template/workflows/DESIGN_BRIEF.md +4 -3
  63. package/skills/easysociable-template/workflows/FILL_AND_RENDER.md +4 -83
  64. package/skills/easysociable-template/workflows/MULTI_PLATFORM_FAMILY.md +81 -0
  65. package/skills/easysociable-template/workflows/OUT_OF_SCOPE.md +3 -2
  66. package/skills/easysociable-template/workflows/PUBLISH_FROM_DRAFT.md +1 -1
  67. package/skills/easysociable-template/workflows/VISUAL_QA.md +10 -0
  68. package/skills/easysociable-content/INSTALL.md +0 -60
  69. package/skills/easysociable-content/platforms/claude.md +0 -4
  70. package/skills/easysociable-content/platforms/codex.md +0 -4
  71. package/skills/easysociable-content/platforms/hermes.md +0 -4
  72. package/skills/easysociable-content/platforms/openclaw.md +0 -4
  73. package/skills/easysociable-content/references/CLI.md +0 -40
  74. package/skills/easysociable-content/references/CONTRACT_SYNC.md +0 -42
  75. package/skills/easysociable-content/references/MCP.md +0 -15
  76. package/skills/easysociable-content/references/PAGE_PACK.md +0 -129
  77. package/skills/easysociable-content/references/PLATFORM_GUIDE.md +0 -36
  78. package/skills/easysociable-content/references/RUN_LAYOUT.md +0 -73
  79. package/skills/easysociable-content/references/TROUBLESHOOTING.md +0 -13
  80. package/skills/easysociable-content/references/VAULT.md +0 -40
  81. package/skills/easysociable-content/references/VOICE.md +0 -16
  82. package/skills/easysociable-content/scripts/package-skill.mjs +0 -49
  83. package/skills/easysociable-content/scripts/setup.mjs +0 -200
  84. package/skills/easysociable-content/scripts/setup.test.mjs +0 -82
  85. package/skills/easysociable-content/starter/discovery/README.md +0 -18
  86. package/skills/easysociable-content/starter/discovery/_index.md +0 -27
  87. package/skills/easysociable-content/starter/discovery/_template.md +0 -41
  88. package/skills/easysociable-content/starter/profiles/_index.md +0 -13
  89. package/skills/easysociable-content/starter/topics/README.md +0 -25
  90. package/skills/easysociable-content/starter/topics/WATCHLIST.md +0 -65
  91. package/skills/easysociable-content/starter/topics/_template-item.md +0 -90
  92. package/skills/easysociable-content/starter/topics/watchlists/README.md +0 -10
  93. package/skills/easysociable-content/starter/topics/watchlists/_shared.md +0 -14
  94. package/skills/easysociable-content/starter/topics/watchlists/default.md +0 -6
  95. package/skills/easysociable-content/workflows/ANALYSIS_AND_COVERAGE.md +0 -17
  96. package/skills/easysociable-content/workflows/CONTENT_EXPORT.md +0 -24
  97. package/skills/easysociable-content/workflows/GETTING_STARTED.md +0 -40
  98. package/skills/easysociable-content/workflows/MCP_FIRST.md +0 -5
  99. package/skills/easysociable-content/workflows/OUT_OF_SCOPE.md +0 -18
  100. package/skills/easysociable-content/workflows/PACK_AUTHORING.md +0 -42
  101. package/skills/easysociable-content/workflows/SWIPE_CAPTURE.md +0 -18
  102. package/skills/easysociable-content/workflows/TOPIC_DRAFT.md +0 -29
  103. package/skills/easysociable-content/workflows/WORKING_DRAFT.md +0 -19
  104. /package/{skills/easysociable-content → content-engine}/fixtures/golden-run-en/draft.md +0 -0
  105. /package/{skills/easysociable-content → content-engine}/fixtures/golden-run-en/idea.md +0 -0
  106. /package/{skills/easysociable-content → content-engine}/fixtures/golden-run-en/pack-tiktok.json +0 -0
  107. /package/{skills/easysociable-content → content-engine}/scripts/contracts.mjs +0 -0
  108. /package/{skills/easysociable-content → content-engine}/scripts/export-contracts.mjs +0 -0
  109. /package/{skills/easysociable-content → content-engine}/scripts/fixture.test.mjs +0 -0
  110. /package/{skills/easysociable-content → content-engine}/scripts/validate-export.mjs +0 -0
  111. /package/{skills/easysociable-content → content-engine}/scripts/validate-pack.mjs +0 -0
  112. /package/{skills/easysociable-content → content-engine}/scripts/validate-run.mjs +0 -0
  113. /package/{skills/easysociable-content → content-engine}/scripts/validate.test.mjs +0 -0
  114. /package/{skills/easysociable-content → content-engine}/starter/accounts/README.md +0 -0
  115. /package/{skills/easysociable-content → content-engine}/starter/accounts/_index.md +0 -0
  116. /package/{skills/easysociable-content → content-engine}/starter/accounts/overrides/README.md +0 -0
  117. /package/{skills/easysociable-content → content-engine}/starter/exports/README.md +0 -0
  118. /package/{skills/easysociable-content → content-engine}/starter/inbox/README.md +0 -0
  119. /package/{skills/easysociable-content → content-engine}/starter/inbox/_index.md +0 -0
  120. /package/{skills/easysociable-content → content-engine}/starter/inbox/entries/README.md +0 -0
  121. /package/{skills/easysociable-content → content-engine}/starter/platforms/README.md +0 -0
  122. /package/{skills/easysociable-content → content-engine}/starter/platforms/ig.md +0 -0
  123. /package/{skills/easysociable-content → content-engine}/starter/platforms/instagram.md +0 -0
  124. /package/{skills/easysociable-content → content-engine}/starter/platforms/linkedin.md +0 -0
  125. /package/{skills/easysociable-content → content-engine}/starter/platforms/threads.md +0 -0
  126. /package/{skills/easysociable-content → content-engine}/starter/platforms/tiktok.md +0 -0
  127. /package/{skills/easysociable-content → content-engine}/starter/platforms/x.md +0 -0
  128. /package/{skills/easysociable-content → content-engine}/starter/platforms/xiaohongshu.md +0 -0
  129. /package/{skills/easysociable-content → content-engine}/starter/profiles/default/audience.md +0 -0
  130. /package/{skills/easysociable-content → content-engine}/starter/profiles/default/pillars.md +0 -0
  131. /package/{skills/easysociable-content → content-engine}/starter/published/_index.md +0 -0
  132. /package/{skills/easysociable-content → content-engine}/starter/raw/README.md +0 -0
  133. /package/{skills/easysociable-content → content-engine}/starter/raw/_index.md +0 -0
  134. /package/{skills/easysociable-content → content-engine}/starter/runs/README.md +0 -0
  135. /package/{skills/easysociable-content → content-engine}/starter/runs/_index.md +0 -0
  136. /package/{skills/easysociable-content → content-engine}/starter/runs/_template/draft.md +0 -0
  137. /package/{skills/easysociable-content → content-engine}/starter/runs/_template/editor.md +0 -0
  138. /package/{skills/easysociable-content → content-engine}/starter/runs/_template/feedback.md +0 -0
  139. /package/{skills/easysociable-content → content-engine}/starter/runs/_template/idea.md +0 -0
  140. /package/{skills/easysociable-content → content-engine}/starter/runs/_template/rubric.md +0 -0
  141. /package/{skills/easysociable-content → content-engine}/starter/swipe/_index.md +0 -0
  142. /package/{skills/easysociable-content → content-engine}/starter/topics/items/README.md +0 -0
  143. /package/{skills/easysociable-content → content-engine}/starter/wiki/README.md +0 -0
  144. /package/{skills/easysociable-content → content-engine}/starter/wiki/atoms/_index.md +0 -0
  145. /package/{skills/easysociable-content → content-engine}/starter/wiki/atoms/_template.md +0 -0
  146. /package/{skills/easysociable-content → content-engine}/starter/wiki/claims/_index.md +0 -0
  147. /package/{skills/easysociable-content → content-engine}/starter/wiki/claims/_template.md +0 -0
  148. /package/{skills/easysociable-content → content-engine}/starter/workbench/README.md +0 -0
@@ -8,15 +8,20 @@ easysociable auth status --json
8
8
  easysociable templates list --json
9
9
  easysociable templates get --id <id> --json
10
10
  easysociable templates create-draft --file ./draft.json --json
11
- easysociable templates update --id <id> --file ./patch.json --json
11
+ easysociable templates update-draft --id <id> --file ./patch.json --json
12
12
  easysociable templates validate --id <id> --json
13
13
  easysociable templates publish --id <id> --json
14
+ easysociable templates families get --id <familyId> --json
15
+ easysociable templates families derive --id <familyId> --presets 4:5,9:16,1:1 --json
16
+ easysociable templates families migrate-legacy --json
14
17
  easysociable icons search --query "arrow" --json
15
- easysociable icons get --icon-id <id> --idempotency-key <key> --json
18
+ easysociable icons get --id <id> --idempotency-key <key> --json
16
19
  easysociable renders create --template-id <id> --slots ./slots.json --format png --idempotency-key render_1 --json
17
20
  easysociable renders wait --id <id> --json
18
21
  ```
19
22
 
23
+ Multi-platform create flow (derive + hand-tune): `workflows/MULTI_PLATFORM_FAMILY.md`.
24
+
20
25
  All requests go through Gateway. The client receives public summaries, slot
21
26
  contracts, stable Image references and render Manifests only. It never receives
22
27
  AST, Binding Maps, private RenderPlans or provider credentials.
@@ -120,12 +120,20 @@ No platform chrome assumptions. Still pick a concrete preset (`1:1`, `4:5`, `3:4
120
120
 
121
121
  ---
122
122
 
123
+ ## Layout Family (multi-platform create)
124
+
125
+ Creating a template **defaults** to a Layout Family with one master page + derived presets — see `workflows/MULTI_PLATFORM_FAMILY.md`.
126
+
127
+ Same-preset platforms share one variant tree (**Instagram + Threads → `4:5`**). Do not invent a second 4:5 page for Threads.
128
+
129
+ Typical vertical bucket when master is `3:4` (Xiaohongshu): also derive `4:5`, `9:16`, `1:1`. Absolute layout cannot share one AST across those presets — hand-tune each variant after derive.
130
+
123
131
  ## Selection rules for Agents
124
132
 
125
- 1. If user names one platform → set `platforms: [that]` and use that platform’s **Recommended** row.
126
- 2. If user names several → intersection of workable presets; if empty, ask them to pick a primary platform or accept letterboxing.
133
+ 1. If user names one platform only → set master `platforms: [that]` + that platform’s **Recommended** row; skip family derive unless they later ask for more.
134
+ 2. If user names several **or** does not limit platforms → pick a master Recommended canvas, then derive the default vertical targets (`MULTI_PLATFORM_FAMILY.md`).
127
135
  3. Never mix presets across slides of one deck without an explicit user choice (IG/Threads/TikTok crop to first slide).
128
- 4. Put platform + preset into P0 `selectionMetadata` before Publish.
136
+ 4. Each family variant’s P0 `selectionMetadata` must list only platforms that match that page’s `canvasPreset`.
129
137
  5. Specs above are **practical authoring targets** (2026 guides + Meta help for IG). Re-verify if a platform announces breaking crop changes.
130
138
 
131
139
  ## Sources (research snapshot)
@@ -15,7 +15,7 @@ Theme is the **skin** (color / font / surface). The page template is the **struc
15
15
 
16
16
  ## Gateway payload (`theme_schema_v1`)
17
17
 
18
- Gateway persists `themeTokens` in `TemplateTheme.tokens`. When `palette` is present, `theme_schema_version` is `theme_schema_v2`. The `colors` object includes six semantic roles: `canvas`, `text`, `accent`, `muted`, `primary`, `border`.
18
+ Authoring persists Studio preview tokens on **`TemplateLayoutFamily.preview_tokens`** (not a TemplateTheme row). Fill/render uses Brand `themeSnapshot` first, then family preview tokens, then a service default (ADR-0033). When `palette` is present, treat tokens as `theme_schema_v2`. The `colors` object includes six semantic roles: `canvas`, `text`, `accent`, `muted`, `primary`, `border`.
19
19
 
20
20
  ```ts
21
21
  {
@@ -0,0 +1,74 @@
1
+ {
2
+ "schemaVersion": "design_plan_v2_1",
3
+ "sourceKind": "image_reference",
4
+ "intent": {
5
+ "jobToBeDone": "Rebuild soft-card landing convert poster from reference",
6
+ "primaryPlatform": "instagram",
7
+ "canvas": { "width": 1080, "height": 1350 }
8
+ },
9
+ "philosophy": {
10
+ "movementName": "Soft Ledger",
11
+ "space": "Stacked rounded cards with generous outer margin",
12
+ "color": "Warm cream canvas, dark ink, soft peach accents",
13
+ "rhythm": "Even card gaps; one tip band; bottom CTA",
14
+ "composition": "Title → dual soft cards with figures → tip → CTA",
15
+ "typographyAccent": "Bold title; quieter body in cards",
16
+ "craftBar": "Match reference geometry; image slots for figures; no CSS fake people"
17
+ },
18
+ "concepts": [
19
+ {
20
+ "id": "soft-ledger-rebuild",
21
+ "name": "Soft ledger rebuild",
22
+ "rationale": "Reference already fixes composition",
23
+ "philosophyExpression": "Same stack and figure wells as referenceSpec",
24
+ "visualMotif": "soft rounded cards",
25
+ "layout": "title, two cards, tip, cta",
26
+ "hierarchy": "title > card copy > tip > cta",
27
+ "typography": "bold title, medium body",
28
+ "colorStrategy": "cream + ink from reference",
29
+ "assetStrategy": "two image slots for person illustrations",
30
+ "risks": ["figure crops"]
31
+ }
32
+ ],
33
+ "selectedConceptId": "soft-ledger-rebuild",
34
+ "selectionReason": "Single reconstruction of the supplied reference",
35
+ "referenceSpec": {
36
+ "stackOrder": ["title", "soft-card-1", "soft-card-2", "tip", "cta"],
37
+ "margins": "~48px outer",
38
+ "gaps": "~24px between cards",
39
+ "radii": "cards 16px; figure wells 12px",
40
+ "typeScale": "title ~42/bold; body ~22; tip ~18"
41
+ },
42
+ "referenceImageInventory": [
43
+ {
44
+ "label": "Left soft-card person illustration",
45
+ "slotKey": "image_0",
46
+ "imageRole": "hero",
47
+ "boxApprox": "left mid ~280×320"
48
+ },
49
+ {
50
+ "label": "Right soft-card person illustration",
51
+ "slotKey": "image_1",
52
+ "imageRole": "hero",
53
+ "boxApprox": "right mid ~280×320"
54
+ }
55
+ ],
56
+ "assetPlan": [
57
+ {
58
+ "role": "hero",
59
+ "source": "user_upload",
60
+ "required": true,
61
+ "slotKey": "image_0",
62
+ "description": "Left soft-card person illustration — empty image-slot frame",
63
+ "treatment": "Match reference box; placeholder until account imageId fill"
64
+ },
65
+ {
66
+ "role": "hero",
67
+ "source": "user_upload",
68
+ "required": true,
69
+ "slotKey": "image_1",
70
+ "description": "Right soft-card person illustration — empty image-slot frame",
71
+ "treatment": "Match reference box; placeholder until account imageId fill"
72
+ }
73
+ ]
74
+ }
@@ -0,0 +1,28 @@
1
+ {
2
+ "schemaVersion": "visual_qa_v2_1",
3
+ "draftId": "draft_fixture_ref",
4
+ "iteration": 0,
5
+ "sourceKind": "image_reference",
6
+ "screenshotEvidence": "/tmp/studio-render-fixture.png",
7
+ "referenceCompare": {
8
+ "performed": true,
9
+ "geometryMatch": true,
10
+ "imageSlotsMatchInventory": true,
11
+ "notes": "Fixture — stack and image_0/image_1 frames match referenceSpec"
12
+ },
13
+ "hardFailures": [],
14
+ "scores": {
15
+ "hierarchy": 18,
16
+ "composition": 14,
17
+ "spacingAlignment": 14,
18
+ "typography": 14,
19
+ "colorContrast": 9,
20
+ "assetRelevance": 9,
21
+ "contentResilience": 8,
22
+ "editability": 5
23
+ },
24
+ "total": 91,
25
+ "verdict": "pass",
26
+ "issues": [],
27
+ "changesMade": []
28
+ }
@@ -126,6 +126,67 @@ function validateThemeTokensBlock(tokens, label) {
126
126
  errors.push(`${label}.surfaces.shadow is required`)
127
127
  }
128
128
 
129
+ const REFERENCE_SPEC_FIELDS = ['margins', 'gaps', 'radii', 'typeScale']
130
+ const IMAGE_INVENTORY_ROLES = new Set(['hero', 'screenshot', 'avatar', 'product'])
131
+ const IMAGE_SLOT_SOURCES = new Set(['user_upload', 'brand_kit', 'external_photo', 'generated'])
132
+ const SLOT_KEY_RE = /^image_\d+$/
133
+
134
+ function validateReferenceImageFields(plan, assetPlan) {
135
+ const spec = plan.referenceSpec
136
+ if (!spec || typeof spec !== 'object') {
137
+ errors.push('referenceSpec is required when sourceKind is image_reference')
138
+ }
139
+ else {
140
+ if (!Array.isArray(spec.stackOrder) || spec.stackOrder.length < 1)
141
+ errors.push('referenceSpec.stackOrder must be a non-empty array')
142
+ else {
143
+ spec.stackOrder.forEach((entry, index) => {
144
+ if (!nonEmpty(entry))
145
+ errors.push(`referenceSpec.stackOrder[${index}] must be a non-empty string`)
146
+ })
147
+ }
148
+ REFERENCE_SPEC_FIELDS.forEach((field) => {
149
+ if (!nonEmpty(spec[field]))
150
+ errors.push(`referenceSpec.${field} is required`)
151
+ })
152
+ }
153
+
154
+ const inventory = plan.referenceImageInventory
155
+ if (!Array.isArray(inventory)) {
156
+ errors.push('referenceImageInventory must be an array when sourceKind is image_reference (empty if no pictures)')
157
+ return
158
+ }
159
+
160
+ const seenSlotKeys = new Set()
161
+ inventory.forEach((item, index) => {
162
+ if (!nonEmpty(item?.label))
163
+ errors.push(`referenceImageInventory[${index}].label is required`)
164
+ if (!SLOT_KEY_RE.test(item?.slotKey ?? ''))
165
+ errors.push(`referenceImageInventory[${index}].slotKey must match image_N`)
166
+ else if (seenSlotKeys.has(item.slotKey))
167
+ errors.push(`referenceImageInventory duplicate slotKey: ${item.slotKey}`)
168
+ else
169
+ seenSlotKeys.add(item.slotKey)
170
+ if (!IMAGE_INVENTORY_ROLES.has(item?.imageRole))
171
+ errors.push(`referenceImageInventory[${index}].imageRole must be hero|screenshot|avatar|product`)
172
+ if (!nonEmpty(item?.boxApprox))
173
+ errors.push(`referenceImageInventory[${index}].boxApprox is required`)
174
+ })
175
+
176
+ if (!Array.isArray(assetPlan))
177
+ return
178
+
179
+ seenSlotKeys.forEach((slotKey) => {
180
+ const match = assetPlan.find((asset) => asset?.slotKey === slotKey)
181
+ if (!match) {
182
+ errors.push(`assetPlan must include an entry with slotKey ${slotKey} for each inventoried picture`)
183
+ return
184
+ }
185
+ if (!IMAGE_SLOT_SOURCES.has(match.source))
186
+ errors.push(`assetPlan entry for ${slotKey} must use user_upload|brand_kit|external_photo|generated (not css_svg stand-in)`)
187
+ })
188
+ }
189
+
129
190
  function validateDesignPlan(plan) {
130
191
  const allowedSourceKinds = new Set(['text_brief', 'image_reference'])
131
192
  const allowedRoles = new Set(['background', 'hero', 'decorative', 'avatar', 'logo', 'icon'])
@@ -206,8 +267,13 @@ function validateDesignPlan(plan) {
206
267
  errors.push(`assetPlan[${index}].required must be boolean`)
207
268
  if (!nonEmpty(asset?.description))
208
269
  errors.push(`assetPlan[${index}].description is required`)
270
+ if (asset?.slotKey !== undefined && !SLOT_KEY_RE.test(asset.slotKey))
271
+ errors.push(`assetPlan[${index}].slotKey must match image_N when present`)
209
272
  })
210
273
  }
274
+
275
+ if (plan.sourceKind === 'image_reference')
276
+ validateReferenceImageFields(plan, plan.assetPlan)
211
277
  }
212
278
 
213
279
  function validateThemeTokens(theme) {
@@ -252,6 +318,33 @@ function validateThemeTokens(theme) {
252
318
  }
253
319
  }
254
320
 
321
+ function validateReferenceCompare(qa) {
322
+ const compare = qa.referenceCompare
323
+ if (qa.sourceKind !== 'image_reference') {
324
+ if (compare !== undefined && (compare === null || typeof compare !== 'object'))
325
+ errors.push('referenceCompare must be an object when present')
326
+ return
327
+ }
328
+ if (!compare || typeof compare !== 'object') {
329
+ errors.push('referenceCompare is required when sourceKind is image_reference')
330
+ return
331
+ }
332
+ if (typeof compare.performed !== 'boolean')
333
+ errors.push('referenceCompare.performed must be boolean')
334
+ if (typeof compare.geometryMatch !== 'boolean')
335
+ errors.push('referenceCompare.geometryMatch must be boolean')
336
+ if (typeof compare.imageSlotsMatchInventory !== 'boolean')
337
+ errors.push('referenceCompare.imageSlotsMatchInventory must be boolean')
338
+ if (qa.verdict === 'pass') {
339
+ if (compare.performed !== true)
340
+ errors.push('pass verdict for image_reference requires referenceCompare.performed true')
341
+ if (compare.geometryMatch !== true)
342
+ errors.push('pass verdict for image_reference requires referenceCompare.geometryMatch true')
343
+ if (compare.imageSlotsMatchInventory !== true)
344
+ errors.push('pass verdict for image_reference requires referenceCompare.imageSlotsMatchInventory true')
345
+ }
346
+ }
347
+
255
348
  function validateVisualQa(qa) {
256
349
  const maxima = {
257
350
  hierarchy: 20,
@@ -278,6 +371,8 @@ function validateVisualQa(qa) {
278
371
  errors.push('changesMade must be an array')
279
372
  if (!['pass', 'revise'].includes(qa.verdict))
280
373
  errors.push('verdict must be pass or revise')
374
+ if (qa.sourceKind !== undefined && !['text_brief', 'image_reference'].includes(qa.sourceKind))
375
+ errors.push('sourceKind must be text_brief or image_reference when present')
281
376
 
282
377
  let calculatedTotal = 0
283
378
  for (const [field, maximum] of Object.entries(maxima)) {
@@ -298,6 +393,8 @@ function validateVisualQa(qa) {
298
393
  }
299
394
  if (qa.verdict === 'revise' && calculatedTotal >= 80 && qa.hardFailures?.length === 0)
300
395
  errors.push('revise verdict conflicts with a passing score and no hardFailures')
396
+
397
+ validateReferenceCompare(qa)
301
398
  }
302
399
 
303
400
  if (value?.schemaVersion === 'design_plan_v2_1')
@@ -24,7 +24,8 @@ Choose the first adequate option:
24
24
  - Roles: `background | hero | decorative | avatar | logo | icon`.
25
25
  - For `external_photo` / `template_icon_catalog` / `gradient_catalog`: discovery → bounded showcase → **user confirm** → materialize. Unselected → no EasySociable write.
26
26
  - **`role: icon` ⇒ `source` MUST be `template_icon_catalog`** (unless catalog failed and user approved degrade). Never invent path SVG for icons “to go faster”.
27
- - Never put third-party temporary URLs into `authoringSource`.
27
+ - **Reference-image path:** every `referenceImageInventory` entry MUST have a matching `assetPlan` row with the same `slotKey`, source `user_upload|brand_kit|external_photo|generated`, and role suited to the picture — not `css_svg` fake people/illustrations. Geometric chrome only for non-pictorial panels/lines/anonymous washes.
28
+ - Never put third-party temporary image URLs into `authoringSource`.
28
29
  - Use generated imagery only when it supports the selected concept. Prefer transparent, text-free, crop-safe elements for independent decorations.
29
30
  - Never ask image generation to render the final poster, headline, metric, avatar name, or other replaceable content.
30
31
  - Keep generated prompts and returned asset IDs/paths as provenance when available.
@@ -4,12 +4,12 @@ Primary intake when the user describes a single-page template in words (no refer
4
4
 
5
5
  ## Goal
6
6
 
7
- One publishable **single-page** template draft: Design Brief → Design Philosophy → 2–3 Concepts → Asset Plan → Theme lock → slot table + absolute layout → screenshot QA → Authoring Studio.
7
+ One publishable **single-page** template **Family** (master canvas + multi-platform variants by default): Design Brief → Design Philosophy → 2–3 Concepts → Asset Plan → Theme lock → master layout → Authoring Studio → `MULTI_PLATFORM_FAMILY.md`.
8
8
 
9
9
  ## Steps
10
10
 
11
- 1. Confirm this is **one page**, not a slideshow. If they want a multi-slide deck, stop and hand off (`OUT_OF_SCOPE.md`).
12
- 2. Read `references/PLATFORM_SPECS.md` and lock platforms + **Recommended** canvas (preset + pixels).
11
+ 1. Confirm this is **one page**, not a slideshow. If they want a multi-slide deck, stop and hand off (`OUT_OF_SCOPE.md`). Multi-platform variants of the same page are expected — that is not a slideshow.
12
+ 2. Read `references/PLATFORM_SPECS.md`. Lock **master** platform + recommended canvas, and the default vertical derive targets unless the user limited platforms (`MULTI_PLATFORM_FAMILY.md`).
13
13
  3. **Mandatory — V2.1 design plan first** (no reference image):
14
14
  - Follow `workflows/DESIGN_BRIEF.md`
15
15
  - Follow `workflows/DESIGN_PHILOSOPHY.md`; lock shared `philosophy` (+ companion `design-philosophy.md`)
@@ -30,7 +30,7 @@ One publishable **single-page** template draft: Design Brief → Design Philosop
30
30
  - `authoringSource` per `references/AUTHORING_SOURCE.md` (flat tree + text-nesting exception)
31
31
  - Theme per `references/THEME_TOKENS.md` — use `themeTokens` from validated `theme-tokens.json` for `theme.create`
32
32
  5. Run `scripts/validate-authoring-source.mjs <authoring-source.json>` and fix every error.
33
- 6. Continue with `workflows/AUTHORING_STUDIO.md` (`source.kind = text_brief`); its Visual QA loop is required before handoff.
33
+ 6. Continue with `workflows/AUTHORING_STUDIO.md` (`source.kind = text_brief`) → **`MULTI_PLATFORM_FAMILY.md`** (derive + hand-tune + QA each variant) before handoff.
34
34
 
35
35
  ## Checklist pages
36
36
 
@@ -4,48 +4,98 @@ Intake when the user provides a poster / card image to turn into a **single-page
4
4
 
5
5
  ## Goal
6
6
 
7
- Vision/LLM understanding stays **local**. Output is a structured candidate (`authoringSource` + slot table + Theme), not a claim of pixel-perfect clone. User always finishes in Authoring Studio.
7
+ Vision/LLM understanding stays **local**. Rebuild the page as a slotted `authoringSource` that **follows the reference as a layout and visual specification**, not as a loose mood board.
8
+
9
+ User always finishes in Authoring Studio. Do **not** claim a pixel-perfect clone is guaranteed under absolute layout — but do **not** treat approximation as permission to invent a different composition.
10
+
11
+ ## Non-negotiable (reference path)
12
+
13
+ When `source.kind = image_reference` (user gave a reference image):
14
+
15
+ 1. **Spec over vibes.** Target function is **minimize visual distance to the reference** under slot/Theme/flat-tree constraints — not “ship any educational card that validates.”
16
+ 2. **Measure before inventing.** Extract and record **`referenceSpec`** in `design-plan.json` (required fields: `stackOrder`, `margins`, `gaps`, `radii`, `typeScale` — contract in `references/DESIGN_V2_1.md`). Validator rejects plans without it.
17
+ 3. **Inventory every pictorial region.** Before building nodes, list all **bitmap-like content** in the reference into **`referenceImageInventory`** (see § Image inventory). Each must become an **`image` slot** (or Icon Catalog for true icons — see below). **Forbidden:** replacing those regions with CSS fake faces, emoji, or invented SVG “because it’s faster.”
18
+ 4. **Editable copy stays nodes.** Rebuild typography and numbers as text/number slots. Never flatten replaceable text into a baked background image.
19
+ 5. **Visual QA must compare to the reference.** Studio screenshot alone is not enough — side-by-side against the reference is required before handoff (`VISUAL_QA.md` § Reference compare). `visual-qa.json` must set `sourceKind: image_reference` and a passing `referenceCompare`.
20
+
21
+ ## Image inventory (what must become an image slot)
22
+
23
+ Treat as **pictorial / bitmap content** → **`image` slot** (`image_0`, `image_1`, …) with `imageRoles` set:
24
+
25
+ | Include as image slot | Examples |
26
+ |-----------------------|----------|
27
+ | Photos | People, product, lifestyle |
28
+ | Illustrations / drawn characters | Soft person illustrations, mascots |
29
+ | Screenshots / UI captures | App frames, dashboards |
30
+ | Avatars / logos used as content | Profile photos, brand marks used as imagery |
31
+ | Any non-geometric picture inside a frame | Rounded photo wells, poster insets |
32
+
33
+ **Not** image slots (CSS / Theme / Icon Catalog instead):
34
+
35
+ | Keep as chrome | How |
36
+ |----------------|-----|
37
+ | Solid/gradient panels, hairlines, anonymous discs used only as wash | CSS decoration |
38
+ | Number badges that are pure typography in a circle | decoration + fixed or `text`/`number` if variable |
39
+ | Recognizable **icons/glyphs** (arrows, checks, UI symbols) | **Icon Catalog** (`SURFACE_REVIEW.md` § Icons) — not hand-drawn; not a photo slot unless the reference literally uses a photo there |
40
+
41
+ If unsure whether a region is “illustration” vs “icon”: prefer **image slot** when it looks like artwork/photo; use Icon Catalog when it is a standard UI glyph.
42
+
43
+ ### Image slot rules on this path
44
+
45
+ - Each inventoried picture → one `image` slot; set `requiresImage` / `imageRoles` / `slotSummary.image` accordingly.
46
+ - Authoring nodes for those slots: absolute `img` (or image host) with `data-variable="image_N"` — **empty placeholder frame** matching reference geometry (size, radius, position). Do not fill with CSS person-shapes.
47
+ - At create time, placeholders may be empty; fill later via account `imageId` (`FILL_AND_RENDER.md`). Optional: import a cropped stand-in from the reference only when the user supplies/exports that crop as an upload — never keep third-party temp URLs in Authoring Source.
48
+ - Write inventory into `design-plan.json` → `referenceImageInventory` **and** matching `assetPlan[]` rows with the same `slotKey` (see `DESIGN_V2_1.md`). Do not leave inventory only in chat.
8
49
 
9
50
  ## Steps
10
51
 
11
52
  1. Confirm single-page intent (not “rebuild this whole carousel as one Agent pack”).
12
- 2. Read `references/PLATFORM_SPECS.md` when mapping detected aspect → platform defaults.
13
- 3. Inspect the image and extract:
53
+ 2. Read `references/PLATFORM_SPECS.md` when mapping detected aspect → platform defaults. Prefer the platform the user named; otherwise infer from aspect and **confirm**.
54
+ 3. **Read the reference pixels with a vision-capable tool** (`read_file` on the image path, or equivalent). Do **not** invent geometry or image inventory from a filename, caption, or memory alone. Then extract **into a written checklist** (show the user before heavy authoring when geometry is ambiguous):
14
55
  - Canvas aspect / preset + suggested pixel size; likely `platforms`
15
56
  - Likely **pageRoles** (1–2), contentFamily / archetypes, density, visualStyle
16
- - Layout regions (title, metric, image well, footer, …)
17
- - **Variable vs fixed**: only variable regions become slots
18
- - Theme candidates (canvas/text/accent/muted, font feel)
19
- 4. Reverse-engineer a short shared philosophy (`workflows/DESIGN_PHILOSOPHY.md`) from the reference — movement name + space/color/rhythm/composition/type/craftBar. Write it into `design-plan.json` `philosophy`.
20
- 5. Treat the reconstruction direction as the selected concept (one concept is enough), with `philosophyExpression` describing how the rebuilt zones express that philosophy. Then follow `workflows/ASSET_PLAN.md` and `workflows/SURFACE_REVIEW.md` when the rebuild needs wash, slot photography, or icons:
21
- - Reuse the supplied bitmap only when it is meant to remain a background/hero asset
22
- - Rebuild editable typography, data, and simple decoration with nodes/CSS/SVG
57
+ - Layout regions and **stack order** (title, cards, tip, CTA, …)
58
+ - **`referenceSpec`:** margins, gaps, radii, typeScale, stackOrder
59
+ - **`referenceImageInventory`:** every pictorial region → `image_N` + `imageRole` + `boxApprox`
60
+ - **Variable vs fixed:** copy/data → slots; pure chrome → decoration; pictures → image slots; glyphs → Icon Catalog
61
+ - Theme candidates (canvas/text/accent/muted, font feel) sampled from the reference
62
+ 4. Reverse-engineer a short shared philosophy (`workflows/DESIGN_PHILOSOPHY.md`) from the reference — movement name + space/color/rhythm/composition/type/craftBar. Write it into `design-plan.json` `philosophy`. Philosophy must **serve the measured layout**, not excuse a different grid.
63
+ 5. Treat the reconstruction as the selected concept (one concept is enough), with `philosophyExpression` describing how the rebuilt zones express that philosophy **and** match the reference zones. Then follow `workflows/ASSET_PLAN.md` and `workflows/SURFACE_REVIEW.md`:
64
+ - Pictorial regions → `image` slots (`user_upload` / import) with `assetPlan[].slotKey`, not `css_svg` stand-ins
65
+ - Icons/glyphs → Icon Catalog confirm → `get_icon`
66
+ - Rebuild editable typography, data, and **geometric** decoration with nodes/CSS
23
67
  - Never flatten replaceable text or data into a generated image
24
68
  - Never keep third-party temporary image URLs in Authoring Source
25
- - Write and validate `design-plan.json`; `image_reference` may contain one concept
69
+ - Write `referenceSpec` + `referenceImageInventory` + `assetPlan`, then `node scripts/validate-design-v2.mjs design-plan.json` (must pass); `image_reference` may contain one concept
26
70
  - When co-creating Theme, run `THEME_FROM_BRAND.md` (sample colors from reference → `brandHex`) and validate `theme-tokens.json`
27
71
  6. Build **P0 selection metadata** (`references/CATALOG_METADATA.md`) — infer, then confirm with the user.
28
72
  7. Build an explicit **slot table** (`references/SLOTS.md`):
29
73
  - Types: `text` | `number` | `image` only
30
74
  - Keys may be `text_0`, `image_0`, …
31
75
  - Every `text` slot includes `maxLines` (+ `maxCharacters` when known)
76
+ - Every inventoried picture has a corresponding `image_*` entry
32
77
  - No `list` type — repeated lines → `text_1`…`text_n` + `fixedItemCount` when checklist/steps
33
- 8. Map regions to **flat absolutely positioned** canvas siblings (no absolute-inside-absolute groups; card chrome = decoration sibling). Set `bindingKey` on fillable nodes only. Put exact geometry and critical typography in inline `style={{...}}`; do not use Tailwind arbitrary-value layout classes. Fill `slotSummary` / `requiresImage` / `imageRoles`.
78
+ 8. Map regions to **flat absolutely positioned** canvas siblings (no absolute-inside-absolute groups; card chrome = decoration sibling). Set `data-variable` / binding on fillable nodes only. **Match the geometry spec** (positions/sizes within a tight tolerance of the measured reference, scaled to the chosen canvas). Put exact geometry and critical typography in inline `style={{...}}`; do not use Tailwind arbitrary-value layout classes. Fill `slotSummary` / `requiresImage` / `imageRoles`.
34
79
  9. Propose Theme reuse or `ThemeTokens` create from `theme-tokens.json` (`references/THEME_TOKENS.md`).
35
- 10. Show the user: selection metadata, slots, Theme, and honesty about approximation limits. Proceed when they accept.
80
+ 10. Show the user: geometry summary, **image inventory → slot map**, selection metadata, Theme. Call out any region you could not match and why. Proceed when they accept.
36
81
  11. Run `scripts/validate-authoring-source.mjs <authoring-source.json>` and fix every error.
37
- 12. Continue with `workflows/AUTHORING_STUDIO.md` (`source.kind = image_reference`, brief = path or short note); run Visual QA before handoff.
82
+ 12. Continue with `workflows/AUTHORING_STUDIO.md` (`source.kind = image_reference`, brief = path or short note) → **`MULTI_PLATFORM_FAMILY.md`** (derive + hand-tune other presets). Run Visual QA **including reference compare on the master** before handoff; still QA each derived variant for fit/clipping.
38
83
 
39
84
  ## Limits (state upfront when relevant)
40
85
 
41
- - Complex photography collages, heavy effects, or handwritten type → structure + Theme approximation; Studio required.
86
+ - Complex photography collages, heavy effects, or handwritten type may still need Studio polish; still inventory pictures as image slots and match overall geometry.
42
87
  - Absolute layout: do not promise empty lines will pack upward.
43
88
  - Never Publish from Agent until the user has Saved in Studio and explicitly asked to publish (`PUBLISH_FROM_DRAFT.md`).
44
89
 
45
90
  ## Do not
46
91
 
47
- - Treat the reference bitmap as the template (must become slotted authoring source)
92
+ - Treat the reference bitmap as the whole template (must become slotted authoring source)
93
+ - Use the reference as a **mood board** while shipping a different composition/density
94
+ - Replace pictorial regions with CSS fake people, emoji, or invented illustrations
95
+ - Skip vision read of the reference / invent inventory from filename or memory alone
96
+ - Skip `referenceSpec` / `referenceImageInventory` or skip image slots for detected pictures
48
97
  - Skip reverse-engineered philosophy / `philosophyExpression`
98
+ - Skip reference-compare Visual QA (`sourceKind` + `referenceCompare` on `visual-qa.json`)
49
99
  - Copy private Binding/AST formats into Gateway requests
50
100
  - Use runtime Tailwind arbitrary-value classes for critical geometry or typography
51
101
  - Nest absolute frames inside wrapper cards/groups (flatten to canvas siblings instead)
@@ -15,6 +15,15 @@ Complete intake first:
15
15
  - Optional draft-stage `selectionMetadata` (P0 can be completed at publish time)
16
16
  - References: `SLOTS.md`, `AUTHORING_SOURCE.md`, `THEME_TOKENS.md`, `CATALOG_METADATA.md`
17
17
 
18
+ ## Layout Family (multi-platform)
19
+
20
+ New drafts create a **Layout Family** (`familyId`) plus a master canvas page.
21
+ **Default create path:** after the master draft exists, follow `MULTI_PLATFORM_FAMILY.md` (derive + hand-tune variants) before handoff unless the user asked for one platform only.
22
+
23
+ - Authoring URL: `/authoring/{draftId}?platform=…`. Top bar **platform chips** switch variants (do not resize one AST).
24
+ - Preview skin = Family `preview_tokens` from `theme.create`; fill/render uses Brand snapshot first (ADR-0033).
25
+ - Legacy drafts without `familyId` auto-wrap on `getDraft` / `listDrafts`. Bulk: `easysociable templates families migrate-legacy --json`.
26
+
18
27
  ## Flow
19
28
 
20
29
  1. Run the required local preflights (resolve scripts relative to this Skill directory):
@@ -36,48 +45,52 @@ The JSON file contains the public `name`, `source`, `authoringSource`, optional
36
45
  `selectionMetadata`, and optional `theme`/`themeId` fields. Do not pass private
37
46
  AST or Binding Maps.
38
47
 
39
- 3. Read `data.id` / `data.templateId` / `data.themeId` / `data.authoringStudioUrl`.
48
+ 3. Read `data.id` / `data.templateId` / `data.familyId` / `data.authoringStudioUrl` (and `data.variants` when present).
49
+
50
+ 4. **Multi-platform (default):** run `workflows/MULTI_PLATFORM_FAMILY.md` — derive target presets, hand-tune each variant, QA each canvas. Skip only when the user asked for a single platform.
40
51
 
41
- 4. **Before handoff, run `workflows/VISUAL_QA.md`.** Open the actual Studio render with available browser control and inspect the screenshot, not only source code. When the draft fails, fix `authoring-source.json`, validate it, update the same draft, and re-render:
52
+ 5. **Before handoff, run `workflows/VISUAL_QA.md`** on the master (and each variant from step 4). Open the actual Studio render; inspect screenshots, not only source. When a draft fails, fix authoring JSON, validate, update that page id, and re-render:
42
53
 
43
54
  ```bash
44
55
  node scripts/validate-authoring-source.mjs ./authoring-source.json
45
- easysociable templates update --id <id> --file ./draft-patch.json --json
56
+ easysociable templates update-draft --id <id> --file ./draft-patch.json --json
46
57
  ```
47
58
 
48
- Stop after two automatic revisions unless the user explicitly requests more. Validate the passing QA artifact:
59
+ Stop after two automatic revisions per page unless the user explicitly requests more. Validate the passing QA artifact:
49
60
 
50
61
  ```bash
51
62
  node scripts/validate-design-v2.mjs ./visual-qa.json
52
63
  ```
53
64
 
54
- 5. **Required user handoff message** (paste only after QA — customize the URL):
65
+ 6. **Required user handoff message** (paste only after QA — customize URLs):
55
66
 
56
67
  ```text
57
- 已创建草稿,请打开 Authoring Studio 预览/编辑:
68
+ 已创建多平台模板 Family(master + 变体)。请打开 Authoring Studio:
58
69
  {authoringStudioUrl}
59
70
 
60
- 可直接在页面上改布局、文案占位和 Theme。满意后点 Save(页面上没有 Publish)。
61
- 保存完成后回复我「publish」或「发布该模板」,我会分析画布、补齐选型元数据并发布到 Catalog。
71
+ 顶栏平台芯片可切换画布(如小红书 3:4 / IG·Threads 4:5 / TikTok 9:16 / LinkedIn 1:1)。
72
+ 可直接改当前变体的布局、文案和 Theme。满意后点 Save(页面上没有 Publish)。
73
+ 保存完成后回复我「publish」或「发布该模板」,我会补齐选型元数据并发布。
62
74
  ```
63
75
 
64
76
  English equivalent when the session is EN:
65
77
 
66
78
  ```text
67
- Draft created. Open Authoring Studio to preview/edit:
79
+ Multi-platform template Family created (master + variants). Open Authoring Studio:
68
80
  {authoringStudioUrl}
69
81
 
70
- Edit layout, placeholders, and Theme directly on the page. When satisfied, click Save (there is no Publish button).
71
- Then reply “publish” and I will analyze the draft, complete catalog metadata, and publish to the Catalog.
82
+ Use the top-bar platform chips to switch canvases (e.g. XHS 3:4 / IG·Threads 4:5 / TikTok 9:16 / LinkedIn 1:1).
83
+ Edit the active variant’s layout, copy, and Theme. When satisfied, click Save (no Publish button).
84
+ Then reply “publish” and I will complete catalog metadata and publish.
72
85
  ```
73
86
 
74
- 6. User previews/edits in the **slim** Authoring Studio:
87
+ 7. User previews/edits in the **slim** Authoring Studio:
75
88
  - Canvas + right property panel
76
- - Left: Theme tokens + optional Catalog hints
77
- - Top bar: **Save** and **Fork** only. Final PNG rendering is delegated to the unified CLI/MCP after the user confirms the saved draft.
89
+ - Left: Theme / preview tokens + optional Catalog hints
90
+ - Top bar: **platform chips**, **Save**, **Fork**. Final PNG rendering is delegated to the unified CLI/MCP after the user confirms the saved draft.
78
91
  - **No** Publish button; **No** left SidebarMenu (no AI / select-template / typeset rail)
79
92
 
80
- 7. When the user says publish → run `workflows/PUBLISH_FROM_DRAFT.md`.
93
+ 8. When the user says publish → run `workflows/PUBLISH_FROM_DRAFT.md`.
81
94
 
82
95
  ## SDK shape
83
96
 
@@ -16,11 +16,11 @@ Skip this workflow when `AUTHORING_FROM_IMAGE.md` applies and a reference image
16
16
 
17
17
  ### 1. Gather intent (batch questions; skip known answers)
18
18
 
19
- - Platforms (multi-select) + canvas from `references/PLATFORM_SPECS.md`
19
+ - **Master** platform + canvas from `references/PLATFORM_SPECS.md`, plus default multi-platform derive targets (`MULTI_PLATFORM_FAMILY.md`) unless the user wants one platform only
20
20
  - Page roles (1–2) + contentFamily / archetypes / density (`CATALOG_METADATA.md`)
21
21
  - Content job: what must the viewer understand in one glance?
22
22
  - Must-have slots: title / metric / image / CTA / fixed N checklist lines…
23
- - Theme: reuse `themeId` or co-create direction (swiss / editorial / bold_promo / minimal / dark). If co-creating, note brand hex or archetype for `DESIGN_INTELLIGENCE.md` palette seeding.
23
+ - Theme/preview direction (swiss / editorial / bold_promo / minimal / dark). Note brand hex or archetype for `DESIGN_INTELLIGENCE.md` palette seeding → Family `preview_tokens`
24
24
 
25
25
  ### 2. Write the Design Brief
26
26
 
@@ -31,7 +31,8 @@ Use this structure (Chinese or English to match the user):
31
31
 
32
32
  ## 1. Job to be done
33
33
  - One-sentence purpose of this page
34
- - Primary platform(s) + canvas (preset + pixels) — cite PLATFORM_SPECS recommended row
34
+ - Master platform + canvas (preset + pixels) — cite PLATFORM_SPECS recommended row
35
+ - Multi-platform targets (default vertical bucket unless user limited) — `MULTI_PLATFORM_FAMILY.md`
35
36
 
36
37
  ## 2. Visual constraints
37
38
  - Desired/forbidden moods or brand rules