@utopia-studio-design/design-system-cli 0.6.1 → 0.7.0

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 (123) hide show
  1. package/README.md +28 -10
  2. package/bin/utopia-ds-mcp.mjs +6 -2
  3. package/bin/utopia-ds.mjs +94 -21
  4. package/data/docs/arabic-friendly.md +19 -2
  5. package/data/docs/billing-setup.md +80 -0
  6. package/data/docs/cer-003-032-033-round-8-repair.md +41 -0
  7. package/data/docs/cer-007-checkout.md +51 -0
  8. package/data/docs/cer-008-011-qa-repair.md +64 -0
  9. package/data/docs/cer-016-023-round-6-repair.md +92 -0
  10. package/data/docs/cer-024-031-round-7-repair.md +51 -0
  11. package/data/docs/cer-033-035-036-round-12-repair.md +37 -0
  12. package/data/docs/cer-035-037-round-10-repair.md +34 -0
  13. package/data/docs/charts.md +34 -3
  14. package/data/docs/chat-service-methodology.md +181 -0
  15. package/data/docs/code-block.md +20 -0
  16. package/data/docs/creative-production.md +149 -0
  17. package/data/docs/designer-agent-backend-contract.md +552 -0
  18. package/data/docs/email-resend.md +29 -0
  19. package/data/docs/foundations.md +46 -8
  20. package/data/docs/guide.md +9 -9
  21. package/data/docs/handover-billing-aiden.md +132 -0
  22. package/data/docs/logo-maker-methodology.md +109 -0
  23. package/data/docs/presentation-asset-repository.md +53 -0
  24. package/data/docs/product-architecture.md +42 -0
  25. package/data/docs/quick-start-ai.md +4 -4
  26. package/data/docs/shadcn-conversion.md +1 -1
  27. package/data/docs/sidebar-placement.md +56 -0
  28. package/data/docs/theme-authoring.md +6 -3
  29. package/data/manifests/catalog.json +42 -46
  30. package/data/manifests/components.json +317 -66
  31. package/data/manifests/logo-skills.json +174 -0
  32. package/data/manifests/motion-profiles.json +2 -2
  33. package/data/manifests/patterns.json +201 -7
  34. package/data/manifests/presentation-assets.schema.json +44 -0
  35. package/data/manifests/templates.json +87 -1
  36. package/data/manifests/theme-dextrum.json +7 -7
  37. package/data/manifests/theme-prism-mono.json +19 -0
  38. package/data/manifests/theme-utopia-cloudblur.json +135 -0
  39. package/data/manifests/theme-utopia-default.json +31 -31
  40. package/data/manifests/theme-vyapti.json +17 -1
  41. package/data/manifests/theme-y2k-pop.json +18 -0
  42. package/data/manifests/themes.json +285 -57
  43. package/data/templates/analytics-dashboard/main.tsx +14 -1
  44. package/data/templates/calendar-application/main.tsx +14 -1
  45. package/data/templates/chat-workspace/README.md +12 -0
  46. package/data/templates/chat-workspace/index.html +11 -0
  47. package/data/templates/chat-workspace/main.tsx +97 -0
  48. package/data/templates/chat-workspace/styles.css +19 -0
  49. package/data/templates/chat-workspace/template.manifest.json +13 -0
  50. package/data/templates/composition-registry-lab/main.tsx +12 -1
  51. package/data/templates/dashboard-composer/main.tsx +14 -1
  52. package/data/templates/database-workspace/main.tsx +14 -1
  53. package/data/templates/interaction-lab/main.tsx +12 -1
  54. package/data/templates/quantum-verification-story/README.md +12 -0
  55. package/data/templates/quantum-verification-story/components.json +21 -0
  56. package/data/templates/quantum-verification-story/eslint.config.mjs +13 -0
  57. package/data/templates/quantum-verification-story/next-env.d.ts +7 -0
  58. package/data/templates/quantum-verification-story/next.config.mjs +11 -0
  59. package/data/templates/quantum-verification-story/package-lock.json +7535 -0
  60. package/data/templates/quantum-verification-story/package.json +46 -0
  61. package/data/templates/quantum-verification-story/postcss.config.js +6 -0
  62. package/data/templates/quantum-verification-story/src/app/assessment/page.tsx +138 -0
  63. package/data/templates/quantum-verification-story/src/app/ceramic-theme.css +48 -0
  64. package/data/templates/quantum-verification-story/src/app/deployment/page.tsx +178 -0
  65. package/data/templates/quantum-verification-story/src/app/fonts/IBMPlexSansArabic-Bold.ttf +0 -0
  66. package/data/templates/quantum-verification-story/src/app/fonts/IBMPlexSansArabic-Medium.ttf +0 -0
  67. package/data/templates/quantum-verification-story/src/app/fonts/IBMPlexSansArabic-Regular.ttf +0 -0
  68. package/data/templates/quantum-verification-story/src/app/fonts/ReadexPro-Variable.ttf +0 -0
  69. package/data/templates/quantum-verification-story/src/app/fonts/TWKLausanne-350.ttf +0 -0
  70. package/data/templates/quantum-verification-story/src/app/fonts/TWKLausanne-500.ttf +0 -0
  71. package/data/templates/quantum-verification-story/src/app/fonts/TWKLausanne-700.ttf +0 -0
  72. package/data/templates/quantum-verification-story/src/app/globals.css +175 -0
  73. package/data/templates/quantum-verification-story/src/app/layout.tsx +45 -0
  74. package/data/templates/quantum-verification-story/src/app/not-found.tsx +39 -0
  75. package/data/templates/quantum-verification-story/src/app/page.tsx +24 -0
  76. package/data/templates/quantum-verification-story/src/app/products/page.tsx +132 -0
  77. package/data/templates/quantum-verification-story/src/app/utopia-default.css +312 -0
  78. package/data/templates/quantum-verification-story/src/components/RotatingText.css +36 -0
  79. package/data/templates/quantum-verification-story/src/components/RotatingText.tsx +189 -0
  80. package/data/templates/quantum-verification-story/src/components/brand-mark.tsx +14 -0
  81. package/data/templates/quantum-verification-story/src/components/console-preview-section.tsx +57 -0
  82. package/data/templates/quantum-verification-story/src/components/console-preview.tsx +72 -0
  83. package/data/templates/quantum-verification-story/src/components/fade-up.tsx +54 -0
  84. package/data/templates/quantum-verification-story/src/components/floating-network.tsx +125 -0
  85. package/data/templates/quantum-verification-story/src/components/footer.tsx +83 -0
  86. package/data/templates/quantum-verification-story/src/components/harvest-timeline.tsx +70 -0
  87. package/data/templates/quantum-verification-story/src/components/lattice-diagram.tsx +73 -0
  88. package/data/templates/quantum-verification-story/src/components/lattice-field.tsx +129 -0
  89. package/data/templates/quantum-verification-story/src/components/nav.tsx +104 -0
  90. package/data/templates/quantum-verification-story/src/components/presentation-layout.tsx +91 -0
  91. package/data/templates/quantum-verification-story/src/components/readiness-estimator.tsx +135 -0
  92. package/data/templates/quantum-verification-story/src/components/scramble-text.tsx +60 -0
  93. package/data/templates/quantum-verification-story/src/components/section-backdrop.tsx +38 -0
  94. package/data/templates/quantum-verification-story/src/components/sections/clients.tsx +140 -0
  95. package/data/templates/quantum-verification-story/src/components/sections/contact.tsx +125 -0
  96. package/data/templates/quantum-verification-story/src/components/sections/hero.tsx +92 -0
  97. package/data/templates/quantum-verification-story/src/components/sections/moat.tsx +73 -0
  98. package/data/templates/quantum-verification-story/src/components/sections/offer.tsx +113 -0
  99. package/data/templates/quantum-verification-story/src/components/sections/pilot.tsx +63 -0
  100. package/data/templates/quantum-verification-story/src/components/sections/problem.tsx +53 -0
  101. package/data/templates/quantum-verification-story/src/components/text-particle.tsx +204 -0
  102. package/data/templates/quantum-verification-story/src/components/ticker.tsx +27 -0
  103. package/data/templates/quantum-verification-story/src/components/ui/badge.tsx +29 -0
  104. package/data/templates/quantum-verification-story/src/components/ui/button.tsx +55 -0
  105. package/data/templates/quantum-verification-story/src/components/ui/card.tsx +46 -0
  106. package/data/templates/quantum-verification-story/src/components/ui/cursor-driven-particle-typography.tsx +288 -0
  107. package/data/templates/quantum-verification-story/src/components/ui/input.tsx +20 -0
  108. package/data/templates/quantum-verification-story/src/components/ui/label.tsx +21 -0
  109. package/data/templates/quantum-verification-story/src/components/ui/separator.tsx +25 -0
  110. package/data/templates/quantum-verification-story/src/components/ui/textarea.tsx +19 -0
  111. package/data/templates/quantum-verification-story/src/lib/utils.ts +6 -0
  112. package/data/templates/quantum-verification-story/tailwind.config.js +95 -0
  113. package/data/templates/quantum-verification-story/template.manifest.json +23 -0
  114. package/data/templates/quantum-verification-story/tsconfig.json +42 -0
  115. package/data/templates/saas-solution-homepage/main.tsx +91 -14
  116. package/data/templates/saas-solution-homepage/privacy/index.html +1 -0
  117. package/data/templates/saas-solution-homepage/styles.css +47 -2
  118. package/data/templates/saas-solution-homepage/team/index.html +1 -0
  119. package/data/templates/saas-solution-homepage/template.manifest.json +41 -0
  120. package/data/templates/saas-solution-homepage/terms/index.html +1 -0
  121. package/lib/api.mjs +52 -3
  122. package/lib/template-runtime.mjs +1 -0
  123. package/package.json +2 -2
package/README.md CHANGED
@@ -5,11 +5,14 @@ The same structured API powers terminal output, JSON automation, generated agent
5
5
  ```sh
6
6
  npm install -D @utopia-studio-design/design-system-cli
7
7
  npx utopia-ds init . --theme utopia-default --yes
8
+ npx utopia-ds room connect --id <room-id> --slug <room-slug> --name "Brand room" --brand "Brand" --source managed --client codex
8
9
  npx utopia-ds search "Arabic data table" --json
9
10
  npx utopia-ds component DataTable --json
10
11
  npx utopia-ds registry --list --json
11
12
  npx utopia-ds layout validate ./dashboard-layout.json --json
12
13
  npx utopia-ds layout serialize ./dashboard-layout.json
14
+ npx utopia-ds logo-skill --list
15
+ npx utopia-ds logo-skill combinedmark --json
13
16
  npx utopia-ds block interaction-workbench --copy ./src/interaction-workbench
14
17
  npx utopia-ds template template-saas-solution-homepage --copy ./saas-website
15
18
  npx utopia-ds template template-interaction-lab --copy ./interaction-lab
@@ -17,11 +20,35 @@ npx utopia-ds template template-dashboard-composer --copy ./dashboard-composer
17
20
  npx utopia-ds template template-saas-solution-homepage --theme dextrum --copy ./dextrum-website
18
21
  npx utopia-ds template validate ./my-community-template
19
22
  npx utopia-ds template submit ./my-community-template
20
- npx utopia-ds theme create nova
21
23
  npx utopia-ds manifest --json
22
24
  npx utopia-ds mcp
23
25
  ```
24
26
 
27
+ `room connect` writes a non-secret `.ceramic/room.json` manifest in the local
28
+ workspace. The MCP server exposes it through `get_room_context`, so Codex,
29
+ Claude Code, and Cursor can identify the exact Ceramic Brand Room while using
30
+ the same component, theme, template, and documentation contracts.
31
+ The initializer writes both Claude Code's project configuration (`.mcp.json`)
32
+ and Cursor's project configuration (`.cursor/mcp.json`). Codex uses its own
33
+ `codex mcp add` registration command shown by the Ceramic onboarding flow.
34
+
35
+ Logo Maker uses the same six-skill manifest in the browser and MCP. MCP clients
36
+ can call `list_logo_skills`, `get_logo_skill`, then
37
+ `prepare_logo_maker_run`. The prepared run includes Brand Room context, the
38
+ selected skill prompt, reference ids, review policy, and the next approval-safe
39
+ step. It also returns the shared Logo Quality Check gates from
40
+ `docs/design-system/logo-maker-methodology.md`; it never implies that a draft
41
+ was evaluated, approved, or published before artifact evidence exists.
42
+
43
+ `theme create` is a source-authoring operation rather than a consumer-project command. Run it from the root of the Ceramic Design System repository after installing the workspace dependencies:
44
+
45
+ ```sh
46
+ npm install
47
+ npm run ds -- theme create your-theme-name
48
+ ```
49
+
50
+ Replace `your-theme-name` with the lowercase kebab-case ID you want for the theme.
51
+
25
52
  Programmatic consumers can import `@utopia-studio-design/design-system-cli/api`.
26
53
  The Ceramic Composition Registry and versioned Layout JSON are the source of
27
54
  truth for agent and Studio dashboard composition. The Dashboard Composer's
@@ -40,12 +67,3 @@ Use `npm run build` before deployment. The generated project pins a compatible
40
67
  Ceramic package range and splits the optional Puck editor dependencies into
41
68
  separate production chunks; the validated Layout JSON and registry renderer
42
69
  remain usable without opening the visual editor.
43
-
44
- ## 0.6.1 patch
45
-
46
- Copied templates resolve their own navigation at the standalone project root.
47
- Dashboard Composer opens `/visual-editor/` and returns to `/`; links to another
48
- template use the hosted Ceramic preview. The seven-template catalog and Ceramic
49
- 0.8.x dependency range are unchanged. Official starter verification uses
50
- `npm run build` and runtime checks. `template validate` remains a community
51
- submission check requiring a separately authored `ceramic.template.json`.
@@ -1,11 +1,12 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
- capabilityManifest, getBlock, getBlockDefinition, getComponent, getDoc, getMotionProfile, getTemplate, getTheme,
4
- listBlockDefinitions, listBlocks, listComponents, listDocs, listMotionProfiles, listTemplates, listThemes,
3
+ capabilityManifest, getBlock, getBlockDefinition, getComponent, getDoc, getLogoSkill, getMotionProfile, getRoomContext, getTemplate, getTheme,
4
+ listBlockDefinitions, listBlocks, listComponents, listDocs, listLogoSkills, listMotionProfiles, listTemplates, listThemes, prepareLogoMakerRun,
5
5
  repositoryDoctor, search, validateAgentMutation, validateLayout,
6
6
  } from '../lib/api.mjs'
7
7
 
8
8
  const tools = [
9
+ ['get_room_context', 'Read the Brand Room context connected to this local workspace.', {}, () => getRoomContext()],
9
10
  ['search', 'Search components, docs, templates, themes, and motion profiles.', { query: { type: 'string' } }, ({ query }) => search(query)],
10
11
  ['list_components', 'List every Ceramic component contract.', {}, () => listComponents()],
11
12
  ['get_component', 'Get one component including imports, tokens, usage, and Arabic rules.', { name: { type: 'string' } }, ({ name }) => getComponent(name)],
@@ -21,6 +22,9 @@ const tools = [
21
22
  ['get_theme', 'Get a theme policy and semantic mappings.', { id: { type: 'string' } }, ({ id }) => getTheme(id)],
22
23
  ['list_motion_profiles', 'List theme-owned semantic motion personalities.', {}, () => listMotionProfiles()],
23
24
  ['get_motion_profile', 'Get a motion profile, its rules, contract, and compatible runtime adapters.', { id: { type: 'string' } }, ({ id }) => getMotionProfile(id)],
25
+ ['list_logo_skills', 'List the established Logo Maker skill structures and their governed guidance.', {}, () => listLogoSkills()],
26
+ ['get_logo_skill', 'Read one Logo Maker skill contract by id or name.', { id: { type: 'string' } }, ({ id }) => getLogoSkill(id)],
27
+ ['prepare_logo_maker_run', 'Prepare a Brand Room-aware Logo Maker chat run using the same skill contract as Ceramic UI. This does not approve or publish the result.', { skillId: { type: 'string' }, brandName: { type: 'string' }, brief: { type: 'string' }, referenceIds: { type: 'array', items: { type: 'string' }, maxItems: 3 } }, (input) => prepareLogoMakerRun(input)],
24
28
  ['list_docs', 'List AI-readable design-system documents.', {}, () => listDocs()],
25
29
  ['get_docs', 'Read one design-system document.', { topic: { type: 'string' } }, ({ topic }) => getDoc(topic)],
26
30
  ['doctor', 'Validate the Ceramic source of truth.', {}, () => repositoryDoctor()],
package/bin/utopia-ds.mjs CHANGED
@@ -1,19 +1,20 @@
1
1
  #!/usr/bin/env node
2
2
  import { cpSync, existsSync, mkdirSync, readFileSync, readdirSync, statSync, writeFileSync } from 'node:fs'
3
- import { dirname, join, resolve } from 'node:path'
3
+ import { basename, dirname, join, resolve } from 'node:path'
4
4
  import { spawn } from 'node:child_process'
5
5
  import {
6
- capabilityManifest, envelope, getBlock, getBlockDefinition, getComponent, getDoc, getMotionProfile, getTemplate, getTheme,
7
- listBlockDefinitions, listBlocks, listComponents, listDocs, listMotionProfiles, listTemplates, listThemes, mcpLaunch,
6
+ capabilityManifest, envelope, getBlock, getBlockDefinition, getComponent, getDoc, getLogoSkill, getMotionProfile, getTemplate, getTheme,
7
+ listBlockDefinitions, listBlocks, listComponents, listDocs, listLogoSkills, listMotionProfiles, listTemplates, listThemes, mcpLaunch,
8
8
  repositoryDoctor, search, serializeLayout, validateLayout,
9
9
  } from '../lib/api.mjs'
10
10
  import { createTemplateSubmissionUrl, validateTemplateSubmission } from '../lib/template-submission.mjs'
11
+ import { resolveTemplateContract, TEMPLATE_MODES } from '../lib/template-runtime.mjs'
11
12
  import { installCliTelemetry } from '../lib/telemetry.mjs'
12
13
 
13
14
  const args = process.argv.slice(2)
14
15
  const command = args.find((arg) => !arg.startsWith('--')) ?? 'help'
15
16
  const commandIndex = args.indexOf(command)
16
- const optionsWithValues = new Set(['--copy', '--theme'])
17
+ const optionsWithValues = new Set(['--copy', '--theme', '--mode', '--id', '--slug', '--name', '--brand', '--source', '--client', '--directory'])
17
18
  const values = commandIndex >= 0 ? positionalValues(args.slice(commandIndex + 1)) : []
18
19
  const json = args.includes('--json')
19
20
  const dense = args.includes('--dense')
@@ -72,6 +73,7 @@ Usage: utopia-ds <command> [options]
72
73
 
73
74
  Commands:
74
75
  init [directory] [--theme <id>] [--yes] Add Ceramic and agent instructions
76
+ room connect --id <id> --brand <name> Connect this workspace to a Brand Room
75
77
  search <query> Search every design-system domain
76
78
  component <Name>|--list Inspect component contracts
77
79
  registry <block-id>|--list Inspect agent-editable block definitions
@@ -79,13 +81,14 @@ Commands:
79
81
  layout serialize <file> Print canonical Ceramic Layout JSON
80
82
  block <id>|--list [--copy <directory>] Inspect or copy a composed interaction block
81
83
  template <id>|--list [--skeleton] Inspect starter structures
82
- template <id> --copy <directory> [--theme <id>]
84
+ template <id> --copy <directory> [--theme <id>] [--mode exact|adapt|explore]
83
85
  Generate a themed runnable project
84
86
  template validate [directory] Validate a community submission
85
87
  template submit [directory] Open a validated GitHub review request
86
88
  theme <id>|--list Inspect theme contracts
87
89
  theme create <id> [directory] Scaffold and register a theme
88
90
  motion <id>|--list Inspect motion personalities and adapters
91
+ logo-skill <id>|--list Inspect Logo Maker skill structures
89
92
  docs <topic>|--list Read design-system guidance
90
93
  manifest Print the self-describing CLI contract
91
94
  doctor Validate the design-system source
@@ -146,8 +149,8 @@ function scaffoldTheme() {
146
149
  '--motion-ease-standard': 'cubic-bezier(0.2, 0, 0, 1)', '--motion-ease-emphasized': 'cubic-bezier(0.16, 1, 0.3, 1)',
147
150
  '--motion-ease-icon': 'cubic-bezier(0.16, 1, 0.3, 1)', '--motion-press-scale': '0.985', '--motion-distance-page': '0px',
148
151
  '--motion-distance-reveal': '0px', '--font-sans': 'ui-sans-serif, sans-serif', '--font-display': 'ui-sans-serif, sans-serif', '--font-ui-support': 'ui-sans-serif, sans-serif',
149
- '--font-marketing-display': 'ui-sans-serif, sans-serif', '--font-arabic': 'IBM Plex Sans Arabic', '--font-arabic-body': 'IBM Plex Sans Arabic',
150
- '--font-arabic-display': 'IBM Plex Sans Arabic', '--font-weight-arabic-body': '400', '--font-weight-arabic-display': '700',
152
+ '--font-marketing-display': 'ui-sans-serif, sans-serif', '--font-arabic': 'Readex Pro', '--font-arabic-body': 'Readex Pro',
153
+ '--font-arabic-display': 'Readex Pro', '--font-weight-arabic-body': '300', '--font-weight-arabic-display': '500',
151
154
  '--font-size-display': 'clamp(2.75rem, 7vw, 5.5rem)', '--font-size-arabic-body': '16px', '--font-size-arabic-body-lg': '18px',
152
155
  '--font-size-arabic-display': 'clamp(2.625rem, 6.6vw, 5.25rem)', '--line-height-arabic': '1.75', '--line-height-arabic-body': '1.75',
153
156
  '--line-height-arabic-display': '1.14', '--tracking-arabic': '0', '--tracking-arabic-display': '0', '--sidebar-width': '17rem',
@@ -161,7 +164,7 @@ function scaffoldTheme() {
161
164
  motionProfile: 'precise',
162
165
  summary: `${name} neutral theme scaffold mapped to the Ceramic semantic contract; replace placeholder primitives before release.`,
163
166
  sourceFiles: [`packages/design-system/src/themes/${id}.css`, `packages/design-system/src/manifests/theme-${id}.json`],
164
- brandPrimitives: { colors: { background: themeValues['--background'], foreground: themeValues['--foreground'], accent: themeValues['--primary'] }, typography: { latin: 'Unassigned', arabic: 'IBM Plex Sans Arabic' }, geometry: { controlRadius: themeValues['--radius-control'], surfaceRadius: themeValues['--radius-surface'] } },
167
+ brandPrimitives: { colors: { background: themeValues['--background'], foreground: themeValues['--foreground'], accent: themeValues['--primary'] }, typography: { latin: 'Unassigned', arabic: 'Readex Pro' }, geometry: { controlRadius: themeValues['--radius-control'], surfaceRadius: themeValues['--radius-surface'] } },
165
168
  semanticMappings: Object.fromEntries(Object.entries(themeValues).map(([token, value]) => [token, value])),
166
169
  visualPolicy: { tone: ['neutral', 'restrained', 'unfinished-by-design'], allow: ['semantic color roles', 'logical layout properties', 'theme-owned brand expression'], avoid: ['shipping placeholder primitives', 'generic SaaS gradients', 'default purple accents', 'component-specific color literals', 'left/right-only layout APIs'] },
167
170
  arabicFriendly: { direction: 'Support dir="rtl" with logical CSS properties.', typography: 'Use the declared Arabic family without Latin tracking or casing.' },
@@ -211,11 +214,41 @@ function init() {
211
214
  write(join(target, '.cursor/rules/ceramic-design-system.mdc'), `---\ndescription: Ceramic Design System rules\nalwaysApply: true\n---\n\n${rules}`)
212
215
  write(join(target, '.github/copilot-instructions.md'), rules)
213
216
  write(join(target, '.ceramic/config.json'), `${JSON.stringify({ apiVersion: 1, theme, motionProfile: getTheme(theme)?.motionProfile, arabicFriendly: true, source: '@utopia-studio-design/design-system' }, null, 2)}\n`)
214
- write(join(target, '.mcp.json'), `${JSON.stringify({ mcpServers: { ceramic: mcpLaunch } }, null, 2)}\n`)
215
- output('init-result', { ok: true, target, theme, files: ['AGENTS.md', 'CLAUDE.md', '.cursor/rules/ceramic-design-system.mdc', '.github/copilot-instructions.md', '.ceramic/config.json', '.mcp.json'] },
217
+ const mcpConfiguration = `${JSON.stringify({ mcpServers: { ceramic: mcpLaunch } }, null, 2)}\n`
218
+ write(join(target, '.mcp.json'), mcpConfiguration)
219
+ write(join(target, '.cursor/mcp.json'), mcpConfiguration)
220
+ output('init-result', { ok: true, target, theme, files: ['AGENTS.md', 'CLAUDE.md', '.cursor/rules/ceramic-design-system.mdc', '.cursor/mcp.json', '.github/copilot-instructions.md', '.ceramic/config.json', '.mcp.json'] },
216
221
  (data) => `Ceramic initialized in ${data.target}.\nCreated ${data.files.join(', ')}.`)
217
222
  }
218
223
 
224
+ function connectRoom() {
225
+ if (values[0] !== 'connect') return fail('Room requires the connect subcommand.', 'ERR_ROOM_COMMAND')
226
+ const id = argAfter('--id', null)
227
+ const slug = argAfter('--slug', null)
228
+ const roomName = argAfter('--name', undefined)
229
+ const brandName = argAfter('--brand', undefined)
230
+ const sourceMode = argAfter('--source', null)
231
+ const client = argAfter('--client', null)
232
+ const directory = argAfter('--directory', process.cwd())
233
+ if ([id, slug, sourceMode, client, directory].some((value) => value == null)) return
234
+ if (!['managed', 'existing', 'blank'].includes(sourceMode)) return fail(`Unknown Brand Room source mode "${sourceMode}".`, 'ERR_ROOM_SOURCE')
235
+ if (!['browser', 'codex', 'claude', 'cursor'].includes(client)) return fail(`Unknown Ceramic client "${client}".`, 'ERR_ROOM_CLIENT')
236
+ const target = resolve(directory)
237
+ const room = {
238
+ apiVersion: 1,
239
+ id,
240
+ slug,
241
+ roomName: roomName ?? slug,
242
+ brandName: brandName ?? slug,
243
+ sourceMode,
244
+ client,
245
+ connectedAt: new Date().toISOString(),
246
+ }
247
+ write(join(target, '.ceramic', 'room.json'), `${JSON.stringify(room, null, 2)}\n`)
248
+ output('room-connect-result', { ok: true, target, room, file: '.ceramic/room.json' },
249
+ (data) => `Connected ${data.room.brandName} (${data.room.roomName}) to ${data.target}.\nMCP can now read this Room with get_room_context.`)
250
+ }
251
+
219
252
  function runMcp() {
220
253
  const child = spawn(process.execPath, [join(dirname(new URL(import.meta.url).pathname), 'utopia-ds-mcp.mjs')], { stdio: 'inherit' })
221
254
  child.on('exit', (code) => { process.exitCode = code ?? 0 })
@@ -229,24 +262,39 @@ function copyTemplateProject(entry) {
229
262
  const theme = argAfter('--theme', 'utopia-default')
230
263
  if (!theme) return
231
264
  if (!getTheme(theme)) return fail(`Unknown theme "${theme}".`, 'ERR_THEME', listThemes().map((item) => item.id))
265
+ const mode = argAfter('--mode', entry.executionContract?.defaultMode ?? 'adapt')
266
+ if (!mode) return
267
+ if (!TEMPLATE_MODES.includes(mode)) return fail(`Unknown template mode "${mode}".`, 'ERR_TEMPLATE_MODE', TEMPLATE_MODES)
268
+ const templateContract = entry.executionContract ? resolveTemplateContract(entry, { mode, theme }) : null
232
269
  if (existsSync(target) && !args.includes('--force')) return fail(`Target already exists: ${target}. Pass --force to overwrite it.`, 'ERR_TARGET_EXISTS')
233
270
  const source = resolve(dirname(new URL(import.meta.url).pathname), '..', 'data', entry.bundlePath)
234
271
  if (!existsSync(source)) return fail(`Runnable bundle is missing for "${entry.id}".`, 'ERR_TEMPLATE_BUNDLE')
235
- cpSync(source, target, { recursive: true, force: args.includes('--force') })
272
+ const generatedDirectories = new Set(['.next', 'node_modules', 'out'])
273
+ cpSync(source, target, {
274
+ recursive: true,
275
+ force: args.includes('--force'),
276
+ filter: (sourcePath) => !generatedDirectories.has(basename(sourcePath)),
277
+ })
236
278
 
237
279
  const replaceTheme = (directory) => {
238
280
  for (const name of readdirSync(directory)) {
239
281
  const path = join(directory, name)
240
282
  if (statSync(path).isDirectory()) replaceTheme(path)
241
- else if (/\.(html|tsx|md)$/.test(name)) {
242
- // Local links follow the copied template root; other templates stay hosted.
283
+ else if (/\.(css|html|tsx|md)$/.test(name)) {
284
+ let content = readFileSync(path, 'utf8')
285
+ // Resolve the copied template locally; other templates remain hosted previews.
243
286
  const templateSlug = entry.id.replace(/^template-/, '')
244
- const content = readFileSync(path, 'utf8')
245
- .replace(/(["'`])\/templates\/([a-z0-9-]+)\//g, (_match, quote, slug) =>
246
- slug === templateSlug ? `${quote}/` : `${quote}https://design.utopia-studio.co/templates/${slug}/`)
247
- .replace('Open live composer</a>', 'Open live composer (hosted preview)</a>')
248
- .replaceAll('/themes/utopia-default.css', `/themes/${theme}.css`)
287
+ content = content.replace(/(["'`])\/templates\/([a-z0-9-]+)\//g, (_match, quote, slug) =>
288
+ slug === templateSlug ? `${quote}/` : `${quote}https://design.utopia-studio.co/templates/${slug}/`)
289
+ content = content.replace('Open live composer</a>', 'Open live composer (hosted preview)</a>')
290
+ if (!content.includes("const defaultTheme: TemplateTheme = 'utopia-default'")) {
291
+ content = content
292
+ .replaceAll('/themes/utopia-default.css', `/themes/${theme}.css`)
293
+ .replaceAll("@import './utopia-default.css';", `@import '@utopia-studio-design/design-system/themes/${theme}.css';`)
294
+ }
295
+ content = content
249
296
  .replaceAll('data-theme="utopia-default"', `data-theme="${theme}"`)
297
+ .replaceAll("const defaultTheme: TemplateTheme = 'utopia-default'", `const defaultTheme: TemplateTheme = '${theme}'`)
250
298
  .replaceAll("const activeTheme = 'utopia-default'", `const activeTheme = '${theme}'`)
251
299
  write(path, content)
252
300
  }
@@ -263,6 +311,25 @@ function copyTemplateProject(entry) {
263
311
  const cliDependency = existsSync(join(workspaceCli, 'package.json'))
264
312
  ? `file:${workspaceCli}`
265
313
  : `^${cliVersion}`
314
+ if (entry.runtime === 'nextjs') {
315
+ const packagePath = join(target, 'package.json')
316
+ const packageDocument = JSON.parse(readFileSync(packagePath, 'utf8'))
317
+ packageDocument.name = packageName
318
+ packageDocument.dependencies = {
319
+ ...packageDocument.dependencies,
320
+ '@utopia-studio-design/design-system': designSystemDependency,
321
+ }
322
+ packageDocument.devDependencies = {
323
+ ...packageDocument.devDependencies,
324
+ '@utopia-studio-design/design-system-cli': cliDependency,
325
+ }
326
+ write(packagePath, `${JSON.stringify(packageDocument, null, 2)}\n`)
327
+ write(join(target, '.ceramic/config.json'), `${JSON.stringify({ apiVersion: 1, theme, template: entry.id, templateMode: mode, motionProfile: getTheme(theme)?.motionProfile, motionAdapter: 'framer-motion', arabicFriendly: true, source: '@utopia-studio-design/design-system' }, null, 2)}\n`)
328
+ if (templateContract) write(join(target, '.ceramic/template-contract.json'), `${JSON.stringify(templateContract, null, 2)}\n`)
329
+ const existingReadme = readFileSync(join(target, 'README.md'), 'utf8')
330
+ write(join(target, 'README.md'), `# Generated ${entry.title}\n\nActive theme: \`${theme}\`\n\n\`\`\`sh\nnpm install\nnpm run dev\n\`\`\`\n\nOpen \`http://localhost:3000/\`.\n\n${existingReadme}`)
331
+ return output('template-copy-result', { ok: true, id: entry.id, target, theme, runtime: 'nextjs', pages: entry.pages ?? [] }, (data) => `Generated ${data.id} with theme ${data.theme} in ${data.target}.\nNext: cd ${data.target} && npm install && npm run dev`)
332
+ }
266
333
  write(join(target, 'package.json'), `${JSON.stringify({
267
334
  name: packageName,
268
335
  private: true,
@@ -279,7 +346,8 @@ function copyTemplateProject(entry) {
279
346
  },
280
347
  devDependencies: { '@utopia-studio-design/design-system-cli': cliDependency, '@vitejs/plugin-react': '^4.3.4', typescript: '^5.7.2', vite: '^6.0.7' },
281
348
  }, null, 2)}\n`)
282
- write(join(target, '.ceramic/config.json'), `${JSON.stringify({ apiVersion: 1, theme, motionProfile: getTheme(theme)?.motionProfile, motionAdapter: 'framer-motion', arabicFriendly: true, source: '@utopia-studio-design/design-system' }, null, 2)}\n`)
349
+ write(join(target, '.ceramic/config.json'), `${JSON.stringify({ apiVersion: 1, theme, template: entry.id, templateMode: mode, motionProfile: getTheme(theme)?.motionProfile, motionAdapter: 'framer-motion', arabicFriendly: true, source: '@utopia-studio-design/design-system' }, null, 2)}\n`)
350
+ if (templateContract) write(join(target, '.ceramic/template-contract.json'), `${JSON.stringify(templateContract, null, 2)}\n`)
283
351
  const htmlEntries = []
284
352
  const collectHtml = (directory, prefix = '') => {
285
353
  for (const name of readdirSync(directory)) {
@@ -294,7 +362,7 @@ function copyTemplateProject(entry) {
294
362
  write(join(target, 'vite.config.ts'), `import { resolve } from 'node:path'\nimport react from '@vitejs/plugin-react'\nimport { defineConfig } from 'vite'\n\nexport default defineConfig({\n plugins: [react()],\n build: {\n rollupOptions: {\n input: {\n${viteInputs}\n },\n output: {\n manualChunks(id) {\n if (id.includes('node_modules/@dnd-kit/')) return 'puck-dnd'\n if (id.includes('node_modules/@tanstack/react-virtual')) return 'puck-virtual'\n if (id.includes('node_modules/@radix-ui/')) return 'radix'\n if (\n id.includes('node_modules/clsx')\n || id.includes('node_modules/class-variance-authority')\n || id.includes('node_modules/tailwind-merge')\n || id.includes('node_modules/tiny-invariant')\n ) return 'ui-utils'\n if (id.includes('node_modules/react-dom') || id.includes('node_modules/react/')) return 'react'\n return undefined\n },\n },\n },\n },\n})\n`)
295
363
  const existingReadme = readFileSync(join(target, 'README.md'), 'utf8')
296
364
  write(join(target, 'README.md'), `# Generated ${entry.title}\n\nActive theme: \`${theme}\`\n\n\`\`\`sh\nnpm install\nnpm run dev\n\`\`\`\n\nOpen \`http://localhost:5173/\`.\n\n${existingReadme}`)
297
- output('template-copy-result', { ok: true, id: entry.id, target, theme, pages: entry.pages ?? [] }, (data) => `Generated ${data.id} with theme ${data.theme} in ${data.target}.\nNext: cd ${data.target} && npm install && npm run dev`)
365
+ output('template-copy-result', { ok: true, id: entry.id, target, theme, mode, pages: entry.pages ?? [] }, (data) => `Generated ${data.id} in ${data.mode} mode with theme ${data.theme} in ${data.target}.\nNext: cd ${data.target} && npm install && npm run dev`)
298
366
  }
299
367
 
300
368
  function copyBlock(entry) {
@@ -434,11 +502,16 @@ else if (command === 'search') {
434
502
  const name = values[0]
435
503
  if (!name || args.includes('--list')) output('motion-profile-list', listMotionProfiles(), (rows) => rows.map((item) => `${item.id}|${item.themes.join(',')}|${item.label}|${item.description}`).join('\n'))
436
504
  else { const item = getMotionProfile(name); item ? output('motion-profile', item) : fail(`Unknown motion profile "${name}".`, 'ERR_MOTION_PROFILE', listMotionProfiles().map((item) => item.id)) }
505
+ } else if (command === 'logo-skill') {
506
+ const name = values[0]
507
+ if (!name || args.includes('--list')) output('logo-skill-list', listLogoSkills(), (rows) => rows.map((item) => `${item.id}|${item.name}|${item.tagline}|${item.example}`).join('\n'))
508
+ else { const item = getLogoSkill(name); item ? output('logo-skill', item) : fail(`Unknown logo skill "${name}".`, 'ERR_LOGO_SKILL', listLogoSkills().map((item) => item.id)) }
437
509
  } else if (command === 'docs') {
438
510
  const topic = values[0]
439
511
  if (!topic || args.includes('--list')) output('docs-list', listDocs(), (rows) => rows.join('\n'))
440
512
  else { const item = getDoc(topic); item ? output('docs', item, (doc) => dense ? doc.content.replace(/\n{2,}/g, '\n').trim() : doc.content) : fail(`Unknown docs topic "${topic}".`, 'ERR_DOCS', listDocs()) }
441
513
  } else if (command === 'doctor') {
442
514
  const result = repositoryDoctor(); output('doctor-result', result, (data) => data.ok ? `Ceramic doctor passed. ${data.checks.components} components, ${data.checks.blocks} blocks, ${data.checks.templates} templates, ${data.checks.themes} themes, ${data.checks.motionProfiles} motion profiles, MCP ready.` : `Ceramic doctor failed: ${data.missing.join(', ')}`); if (!result.ok) process.exitCode = 1
443
- } else if (command === 'mcp') runMcp()
515
+ } else if (command === 'room') connectRoom()
516
+ else if (command === 'mcp') runMcp()
444
517
  else fail(`Unknown command "${command}".`, 'ERR_COMMAND', capabilityManifest().commands.map((item) => item.name))
@@ -31,9 +31,26 @@ Arabic typography needs its own role contract. It is not Latin typography transl
31
31
  - Required display roles: `--font-size-arabic-display`, `--font-weight-arabic-display`, and `--tracking-arabic-display`.
32
32
  - Required body roles: `--font-size-arabic-body`, `--font-size-arabic-body-lg`, `--font-weight-arabic-body`, and `--tracking-arabic`.
33
33
  - Arabic display text must use scale, weight, rhythm, and Arabic display typeface rules. Do not map Latin all-caps styling to Arabic.
34
- - Arabic display size should track the Latin display scale at about 95%, rather than becoming visually larger than the Latin equivalent.
34
+ - Arabic display size should track the Latin display scale at about 90–92%. Arabic renders optically larger at equal px, so parity in numbers is not parity on screen.
35
+ - Arabic display line height must be at least 1.28. Below that, ل and ك ascenders collide with the ج and ي descenders on the line above. This is a hard floor, not a preference.
36
+ - Arabic display weight must not exceed 500. Bold Arabic closes the counters of ه, ع and ص at display size, and the Latin display face runs at 350.
35
37
  - Arabic contexts must force `text-transform: none`.
36
38
 
39
+ ## Arabic Orthography Requirements
40
+
41
+ Arabic copy in this repo is inconsistent about the tanwīn. Standardise:
42
+
43
+ - Write the tanwīn alif. `بدلًا`, not `بدلا`.
44
+ - Place the fatḥatān on the letter **before** the alif, never on the alif itself:
45
+ `مرجعًا` not `مرجعاً`, `معًا` not `معاً`, `أبدًا` not `أبداً`.
46
+ - Do not transliterate product or technical names. Ceramic, Utopia, MCP, CLI,
47
+ React, shadcn/ui, token, RTL stay Latin, inline, unquoted.
48
+ - Numerals stay Western (104, 6, v0.9.0) in both locales — the docs are full of
49
+ versions and counts.
50
+ - Use the Arabic ellipsis character `…`, not three periods.
51
+ - `الوكلاء` on its own reads as sales agents. First mention on any page:
52
+ `وكلاء الذكاء الاصطناعي`.
53
+
37
54
  ## RTL Layout Requirements
38
55
 
39
56
  Every reusable component and template must be written in logical layout terms.
@@ -69,4 +86,4 @@ The design system should include Arabic/RTL previews for:
69
86
  - Empty state.
70
87
  - Side navigation.
71
88
 
72
- Mockups may use clearly marked test placeholder Arabic. They must not invent production Arabic marketing copy.
89
+ Mockups may use clearly marked test placeholder Arabic. They must not invent production Arabic marketing copy. Reviewed strings live in `src/i18n.tsx` and the locale copy objects in each page — agents copy from there, they do not write new ones.
@@ -0,0 +1,80 @@
1
+ # Billing setup (Polar + Clerk + Convex)
2
+
3
+ Minimal path to recurring subscriptions. Matches [Studio Product Framework](https://github.com/The-Utopia-Studio/studio-product-framework) defaults.
4
+
5
+ ## 1. Clerk (StudioOS)
6
+
7
+ Use the **StudioOS** Clerk application (`included-seahorse-61`):
8
+
9
+ 1. Confirm JWT template named **`convex`** exists (Clerk → JWT templates). Issuer must be `https://included-seahorse-61.clerk.accounts.dev`.
10
+ 2. Copy publishable key → `VITE_UTOPIA_OS_CLERK_PUBLISHABLE_KEY`
11
+ 3. Issuer → Convex env `CLERK_JWT_ISSUER_DOMAIN` = `https://included-seahorse-61.clerk.accounts.dev`
12
+
13
+ ## 2. Convex (this repo)
14
+
15
+ Deployments (team **thestudio**, project **ceramic-design-system**):
16
+
17
+ | Env | Deployment | Cloud URL | Site URL (webhooks) |
18
+ |-----|------------|-----------|---------------------|
19
+ | Dev | `nautical-goldfish-225` | `https://nautical-goldfish-225.convex.cloud` | `https://nautical-goldfish-225.convex.site` |
20
+ | Prod | `mellow-fox-829` | `https://mellow-fox-829.convex.cloud` | `https://mellow-fox-829.convex.site` |
21
+
22
+ ```sh
23
+ npm install
24
+ npx convex dev # links dev deployment, generates convex/_generated
25
+ npx convex deploy --yes # production (mellow-fox-829)
26
+ ```
27
+
28
+ Set Convex environment variables:
29
+
30
+ | Variable | Purpose |
31
+ |----------|---------|
32
+ | `CLERK_JWT_ISSUER_DOMAIN` | Clerk JWT issuer |
33
+ | `POLAR_ACCESS_TOKEN` | Polar API token |
34
+ | `POLAR_ORGANIZATION_ID` | Polar org id |
35
+ | `POLAR_WEBHOOK_SECRET` | Webhook signing secret |
36
+ | `POLAR_SERVER` | `sandbox` or `production` |
37
+ | `FRONTEND_URL` | `https://design.utopia-studio.co` |
38
+
39
+ Point the Vite app at this deployment:
40
+
41
+ ```env
42
+ VITE_UTOPIA_OS_CONVEX_URL=https://YOUR.deployment.convex.cloud
43
+ VITE_UTOPIA_OS_CLERK_PUBLISHABLE_KEY=pk_...
44
+ VITE_CERAMIC_BILLING_ENABLED=true
45
+ VITE_UTOPIA_OS_FELLOW_ENABLED=false
46
+ ```
47
+
48
+ `VITE_UTOPIA_OS_FELLOW_ENABLED=false` skips Daedalus fellow queries when this Convex deployment only has Ceramic billing functions.
49
+
50
+ ## 3. Polar
51
+
52
+ 1. Create one active recurring **annual** Ceramic Membership product. V1 intentionally does not expose monthly prices.
53
+ 2. Register webhook URL:
54
+ - **Prod:** `https://mellow-fox-829.convex.site/polar/webhook`
55
+ - **Dev:** `https://nautical-goldfish-225.convex.site/polar/webhook`
56
+ (see `convex/http.ts`).
57
+ 3. Subscribe to subscription.* events.
58
+
59
+ ## 4. Vercel
60
+
61
+ Add the same `VITE_*` vars to the Vercel project. Convex env vars are set in the Convex dashboard, not Vercel.
62
+
63
+ ## Plans (v1)
64
+
65
+ | Plan | Entitlements (domain rules in `src/domain/entitlements.ts`) |
66
+ |------|---------------------------------------------------------------|
67
+ | `free` | Marketing, pricing, authentication, and legal pages only |
68
+ | `pro` | All Docs, components, themes, templates, MCP setup, and the member production journey |
69
+ | `team` | Same V1 content access; managed Brand Rooms remain a V2 feature |
70
+
71
+ Admins (`userType === 'admin'`) always get full access regardless of subscription.
72
+
73
+ After Polar confirms checkout, the customer returns to `#/onboarding-v2`. The managed Brand Room journey starts with project identity, brand sources, design agent, and review.
74
+
75
+ Checkout returns to `#/onboarding-v2` on the origin that opened it. The server
76
+ accepts the configured `FRONTEND_URL`; Polar sandbox additionally accepts HTTP
77
+ loopback origins (`localhost` and `127.0.0.1`) so local testing keeps its Clerk
78
+ session. Production checkout never accepts a client-provided external origin.
79
+
80
+ The browser gate protects rendered product routes. Do not treat client-bundled files or a publicly published npm package as a security boundary; authenticated remote content and MCP entitlement enforcement are required before private source material is distributed.
@@ -0,0 +1,41 @@
1
+ # Round 8 제작 흐름 수정
2
+
3
+ 2026-09-05. 전달된 Round 8 보고서의 CER-032·033, CER-003의 예제/실행 경계와 로컬 저장, CER-021의 접근 가능성 오표기를 수정했다.
4
+
5
+ ## 수정
6
+
7
+ - **CER-032:** 복사 버튼이 자신의 Claude/ChatGPT 대상을 명시적으로 프롬프트 빌더에 전달한다. 선택 버튼은 전체 프롬프트 미리보기의 대상을 제어한다. 실제 복사 → 텍스트 필드 붙여넣기로 Claude/ChatGPT 선택 × 두 복사 버튼의 네 조합을 검사했으며, 모두 버튼에 표시된 대상과 일치했다.
8
+ - **CER-033:** 프롬프트 조립을 `src/domain/presentation-prompt.ts`로 분리했다. 명시적인 브리프의 장수, 선택 브랜드, 산출물 설정을 우선한다. 고정 12–15장 지시를 없앴고, 사용자 브랜드에는 Utopia 색상·폰트·참조 저장소를 요구하지 않는다. PPTX/PDF 선택 시 index.html·웹 이미지 폴더·HTML 제작 스킬·브라우저 모션 검증을 제거했다. 기본 이미지 라이브러리 안내에도 같은 출력 조건을 적용했다. 아랍어 프롬프트의 누락된 브리프도 복원했다.
9
+ - **CER-003 경계:** 연결된 서버 룸·서비스·작업 세션이 없으면 로고 생성·수정·승인을 실행할 수 없다. 로컬 예제임을 입력 영역에서 먼저 알리고, 실제 생성/파일 수정/자산 등록을 하지 않는다고 명시한다. 정적인 마크를 생성 결과로 주장하지 않는다. 로고 가이드로 이동할 때 브랜드 문맥과 브리프를 전달한다.
10
+ - **CER-003 저장:** 새로 만든 preview room뿐 아니라 기존 로컬 예제 방에도 브리프·대화 상태 저장과 복원을 적용했다. 실제 서버 룸에는 기존 서버 저장 경로를 유지한다. 로컬 저장 오류는 안내한다. 개발용 예제에서 입력 후 reload하여 같은 브리프가 복원됨을 확인했다.
11
+ - **CER-021:** utopia-os-deck를 공개·접근 가능하다고 단정하지 않는다. 접근 실패 시 유효한 URL 또는 승인된 로컬 사본을 요청하며, 읽지 않은 참조를 확인했다고 주장하지 않는다. 사용자 브랜드에는 이 저장소를 필수 조건으로 붙이지 않는다.
12
+
13
+ ## 검증
14
+
15
+ - `npx vitest run src/domain/round-8-prompts.test.ts src/domain/theme-handoff.test.ts`: 16개 통과.
16
+ - TypeScript, semantic page ownership audit, production Vercel build 통과.
17
+ - 로컬 실제 클립보드 4개 조합 통과.
18
+ - 실제 Presentation Studio에서 exactly 3 slides, custom brand `/approved/qa-orbit`, PPTX/PDF를 선택하고 복사·붙여넣기: 브리프와 브랜드 포함, 고정 장수·Utopia 색상·index.html·Utopia 참조 저장소 필수 지시 없음.
19
+ - 개발용 로컬 Agent 예제: 생성 버튼 disabled, 사전 안내 표시, 브리프 reload 복원, 로고 가이드로 브리프 전달 확인. 실제 계정 권한이나 멤버십을 변경하지 않았다.
20
+ - Rams quick_review는 기존 30회 크레딧 소진으로 실행되지 않았다. 점수 없음.
21
+
22
+ ## 남은 검증과 차단
23
+
24
+ **CER-003의 실제 서버 생성·수정·다운로드·저장 재개 검증은 아직 완료하지 않았다.** 이 수정은 예제를 실제 생성처럼 보이게 하는 동작을 차단한 것이며, 서버 제작 파이프라인 전체 성공을 의미하지 않는다. 기존 실제 브랜드의 생성 작업은 새로 실행하거나 변경하지 않았다.
25
+
26
+ `gh repo view The-Utopia-Studio/utopia-os-deck`는 이번에도 resolve에 실패했다. 접근 가능한 PPT 참조 주소/로컬 사본을 사용자에게 요청했다. 실제 PPTX/PDF 제작·열기·등록, SVG 변환·대표 로고 지정·서버 공유 검증은 여전히 남아 있다. 권한 문제인지 주소 문제인지는 확정하지 않았다.
27
+
28
+ 이번 변경은 웹 앱의 페이지·도메인 프롬프트 코드이며 npm 공용 패키지 코드와 Convex 백엔드는 변경하지 않았다. 기존 공개 버전 design-system 0.9.0 / CLI 0.6.2는 유지한다.
29
+
30
+ ## HTML 우선 기준 및 Docs 후속 수정
31
+
32
+ 사용자의 후속 지시에 따라 애니메이션 HTML이 기본·최종 산출물이다. 위 PPTX/PDF 선택 검증은 선택형 내보내기 분기의 기록이며 기본 제작 경로의 수용 기준이 아니다. Templates의 기본 산출물, Brand Room의 제작 안내와 복사 프롬프트, 테마의 presentation 프롬프트에 HTML 우선을 적용했다. PPTX/PDF는 명시적으로 선택하거나 별도로 요청할 때만 만든다. 콘텐츠 진입 모션은 앞/뒤/직접 이동마다 재실행하며 배경·미디어·아이덴티티는 고정하도록 통일했다.
33
+
34
+ 로컬 검증용 독립 HTML 덱: `.codex-tmp/html-animation-qa/index.html`. 실제 브라우저에서 다음·이전·직접 이동, 재진입 시 stagger 모션, 정지 후 opacity 1/이동 0, 아이덴티티 transform none, 현재 뷰포트에서 가로 넘침 없음을 확인했다. 이 덱은 보조자가 만든 로컬 검증 산출물이며 서버 AI 생성 결과가 아니다. OS reduced-motion 설정과 전체 뷰포트 매트릭스, 서버 생성 전체 흐름 검증은 완료하지 않았으므로 전체 HTML 제작 QA PASS로 판정하지 않는다.
35
+
36
+ What's New의 접근 불가능한 GitHub 저장소·Releases·커밋·CHANGELOG·PR 링크와 GitHub API 요청을 제거했다. 공개 패키지 스냅샷의 버전과 내부 Getting Started 링크를 표시한다. 로컬 실제 페이지 main 영역에서 GitHub 링크 0개를 확인했다.
37
+
38
+ 멤버십 게이트에 숨겨진 Logo Guide/Designer Agent 레이아웃 클래스를 적용하지 않도록 분리했다. Logo Guide의 !important 상단 여백이 멤버십 게이트에 전파되던 원인을 제거했다. 개발용 free 멤버십 프리뷰에서 gate top=0, backdrop top=0, wrapper padding=0px 확인.
39
+
40
+ 16개 프롬프트 테스트, TypeScript, semantic audit, 프로덕션 빌드 통과. Rams는 크레딧 소진으로 리뷰 결과를 반환하지 않았다.
41
+ 배포: `dpl_9uR8cy9RvsgUL22d1qGbyYMszddP`, entry `/assets/main-G3tlN1ai.js`.
@@ -0,0 +1,51 @@
1
+ # CER-007 — checkout deployment mismatch
2
+
3
+ 2026-09-05. Status: deployed; free-account sandbox checkout entry verified in production. Membership activation and Room persistence acceptance remain pending.
4
+
5
+ Production `mellow-fox-829` logs match request `b3f7f6503948d806` exactly:
6
+ `ArgumentValidationError: Object contains extra field customerEmail that is not in the validator`.
7
+ The deployed validator accepts only `discountCode?` and `priceId`; the browser also sends
8
+ `customerEmail`, `locale`, and `returnOrigin`. Requests `ff523b7845474589` and
9
+ `f052833f39385683` fail identically. Execution time and database writes are zero;
10
+ the action handler and Polar were never reached. This evidence does not establish
11
+ whether Polar itself is correctly configured.
12
+
13
+ The working copy already has the expanded checkout validator and localized return URL.
14
+ Deploy the reviewed backend contract before the frontend. Do not deploy the whole
15
+ working directory blindly: it contains unrelated backend/schema and UI changes.
16
+
17
+ Before releasing the frontend, run:
18
+
19
+ ```sh
20
+ node scripts/check-billing-contract.mjs mellow-fox-829
21
+ ```
22
+
23
+ This authenticated CLI metadata read verifies the deployed argument and return validators;
24
+ it creates no membership, checkout, or user. CLI access to the target deployment is required.
25
+ It does not establish that Polar or the end-to-end member journey works.
26
+
27
+ Local regression coverage exercises the full browser payload with a new free identity
28
+ and mocked Polar responses. UI failures retain Convex's request ID for support and
29
+ only classify explicit discount validation errors as invalid codes.
30
+
31
+ Acceptance still requires the real Google free account to open sandbox checkout,
32
+ activate a test membership, create its first Room, and retain that Room after refresh
33
+ and logout/login. No entitlement bypass or real payment is part of this fix.
34
+
35
+ ## Verification in this repair
36
+
37
+ - Production contract check: FAIL; deployed fields are `discountCode`, `priceId`.
38
+ - Development contract check (`nautical-goldfish-225`): PASS.
39
+ - Checkout and error-classification tests: 11 passed (Polar mocked).
40
+ - Convex TypeScript and semantic page ownership audit: PASS.
41
+ - Whole-app TypeScript: blocked by existing `src/pages/BrandWorkspacePage.tsx:761`
42
+ (`assetLibraryForTheme` is undefined).
43
+ - Rams quick_review attempted; no review returned because workspace credits are exhausted.
44
+ - No deployment, entitlement change, checkout confirmation, or Room acceptance test performed.
45
+
46
+
47
+ ## Production deployment and retest
48
+
49
+ 2026-09-05: deployed the expanded checkout action to `mellow-fox-829`, preserving the existing production schema. The deployed metadata contract check now passes. Final frontend deployment: `dpl_2xoLyYTd8uBfYU31cERoxShxxFUL`, serving `https://design.utopia-studio.co`.
50
+
51
+ Using the previously supplied Yongwoo Google QA account, confirmed `Current plan: free`, clicked View annual membership and Start annual membership, and reached the real `sandbox.polar.sh/checkout/…` page. The screen explicitly states payments are not processed. The checkout capability URL is intentionally not recorded here. No payment fields were entered and Subscribe now was not activated. Membership activation, first Room creation, refresh and relogin persistence still require separate acceptance testing.
@@ -0,0 +1,64 @@
1
+ # CER-008–011 repair — 2026-09-05
2
+
3
+ Status: deployed on 2026-09-05; focused production retest passed as recorded below. Earlier local-only and user-reported reproduction results are retained as history. Existing CER-001 and CER-003 are not closed by this work; CER-007 has a separate checkout retest record.
4
+
5
+ | Issue | Confirmed source cause | Repair |
6
+ | --- | --- | --- |
7
+ | CER-008 | Hard-coded Docs TOC IDs drifted from rendered sections. | Derive links and labels from rendered sections/headings; observe lazy content and locale updates; remove duplicate Docs TOC definitions. |
8
+ | CER-009 | Team, Terms and Privacy HTML files existed but were absent from Vite's multi-page build input. | Register all three HTML entries, so direct requests and refreshes have real static documents. |
9
+ | CER-010 | Prism Mono and Y2K Pop policies were missing from the document resolver; misses silently used Utopia. | Register both policies; reject unknown policies; show an unavailable state for unsupported route brands. MD and AI briefs use the same resolved contract. |
10
+ | CER-011 | Studio linked to an unsupported Renacore theme detail route. | Link to the registered brand design-system route. |
11
+
12
+ Also repaired the stale BrandWorkspace asset-source accessor and brief argument to use the already registered BrandProductionSource API. These pre-existing mismatches blocked the application build and brand-page verification.
13
+
14
+ Validation:
15
+
16
+ - All 8 brand policy/source/export unit cases and unknown-brand rejection: 9 passed.
17
+ - English and Arabic Docs: all TOC links in the 10 reported documents resolve inside the current document; final link clicked in each document.
18
+ - All 8 brand documents: heading, MD source, browser clipboard AI brief, and reload passed on desktop and mobile Chromium in local preview (16 tests).
19
+ - Renacore admin: link destination, reload, back and forward passed on desktop and mobile Chromium in local preview (2 tests).
20
+ - Production static output (plain Python HTTP server, no SPA fallback): Team/Terms/Privacy client navigation, direct requests and refresh passed on desktop and mobile Chromium (6 tests), retaining the seed query.
21
+ - `npm run build`, `npm run lint`, and `npm run audit:semantic-pages` passed.
22
+ - Rams quick_review was attempted but returned only a credit-exhaustion message; no Rams review or score is claimed.
23
+
24
+ Reproduce the static-output checks after building:
25
+
26
+ ```sh
27
+ python3 -m http.server 4188 --bind 127.0.0.1 --directory dist
28
+ # In another terminal:
29
+ CERAMIC_STATIC_PREVIEW_URL=http://127.0.0.1:4188 npx playwright test tests/e2e/qa-template-build.spec.ts --workers=1
30
+ ```
31
+
32
+ Run the Docs/brand/admin checks with `npx playwright test tests/e2e/qa-round-3.spec.ts`.
33
+ These tests use the repository's existing local preview fixtures, not an authenticated production account. No external publishing, invitation, purchase, or AI generation was performed.
34
+
35
+ ## User-reported production interaction retest — 2026-09-05
36
+
37
+ The follow-up report used the existing administrator account on design.utopia-studio.co and actual menu/link navigation and controls. This is additional reproduction evidence for the same four issues, not four new defects and not a post-deployment regression test. The local repairs above have not been deployed by this task.
38
+
39
+ - CER-008: Color Usage, the eight foundation documents' Arabic/RTL targets, Arabic Friendly AI checklist, and Direction/RTL Install still have no target. Working targets such as Color Theme Swatches and foundation Tokens provide comparison evidence.
40
+ - CER-009: Team, Terms and Privacy render after client navigation, then actual browser reload produces Vercel 404 on all three paths.
41
+ - CER-010: selecting Prism Mono or Y2K Pop from the room menu, opening the full design system and invoking Copy AI brief still exposes the Utopia document and Markdown link content. This report does not establish OS clipboard bytes.
42
+ - CER-011: searching Renacore in Studio admin, opening the room, using asset controls and following Design system → Open room still leads to the default Utopia preview.
43
+
44
+ The report also records interactions across 22 Docs screens, command-copy feedback, logo brief edits and prompt review. Detailed click JSON for the first three Docs screens was lost to a tool timeout; final snapshots were retained. Scroll measurements taken during animation are not asserted as settled positions. The referenced screen-checks.json and sibling snapshots were not attached to this task and have not been independently inspected here.
45
+
46
+ All four issues remain Open in production. Next acceptance must follow deployment and repeat these same user paths with the real administrator account, including reload for all three SaaS routes and brand-specific MD/AI brief verification. No checkout, publishing, asset approval or upload was performed in the reported retest.
47
+
48
+
49
+ ## Production release
50
+
51
+ - Final deployment: `dpl_2xoLyYTd8uBfYU31cERoxShxxFUL` — https://utopia-design-system-29yn1l8iq-the-utopia-studio.vercel.app
52
+ - Canonical alias: https://design.utopia-studio.co (Vercel Ready).
53
+ - Convex: `mellow-fox-829`, existing schema retained; checkout and owner-scoped Room-by-slug query deployed. New Designer Agent tables/components were excluded. Its persistence calls remain development-only unless `VITE_CERAMIC_DESIGNER_AGENT_BACKEND_ENABLED=true` is explicitly configured after backend deployment.
54
+ - Production Clerk uses `clerk.utopia-studio.co`, matching Convex's existing issuer. A temporary development-key mismatch during deployment was detected by live verification and corrected in the final build and Vercel environment. The existing administrator session and Studio admin access worked in the final deployment.
55
+
56
+ Focused live checks with the real administrator account:
57
+
58
+ - Color: clicked the generated Assigning semantic color roles TOC link; actual implementation section appeared at approximately 162px from the viewport top, confirmed visually and via DOM geometry.
59
+ - Prism Mono and Y2K Pop: correct H1, CSS/manifest source and brand-specific MD link content. Y2K Copy AI brief returned the Y2K title in the browser clipboard. Prism's first immediate clipboard read was empty, so no live clipboard-byte success is claimed for that attempt (local clipboard tests passed).
60
+ - Renacore: searched in Studio admin, opened the room, selected Design system, followed Open room, and reloaded; the Renacore document remained visible.
61
+ - SaaS Team, Terms and Privacy: actual link clicks followed by browser reload retained each correct page and its seed/theme query, with no Vercel 404.
62
+ - General account checkout entry was independently rechecked; see `cer-007-checkout.md`.
63
+
64
+ This is focused production acceptance, not a repeat of all 22 Docs pages, every viewport, or the full membership/Room lifecycle. No Git push, public package release, asset approval, or payment confirmation was performed. The deployed backend source snapshot and hashes are retained locally under `.codex-tmp/qa-production-release-2026-09-05/`.