@explorer02/cfm-survey-sdk 0.2.3 → 0.2.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 (94) hide show
  1. package/dist/cli/index.js +140 -33
  2. package/dist/cli/index.mjs +140 -33
  3. package/dist/index.js +1 -1
  4. package/dist/index.mjs +1 -1
  5. package/package.json +8 -1
  6. package/postinstall.js +8 -5
  7. package/templates/AGENT.md +60 -19
  8. package/templates/deploy-wizard-to-s3.sh +52 -0
  9. package/templates/docs/00-integration/agent-execution-flow.md +281 -0
  10. package/templates/docs/00-integration/agent-operating-contract.md +260 -0
  11. package/templates/docs/00-integration/apply-ui-config.md +365 -0
  12. package/templates/docs/00-integration/canonical-survey-fixtures.md +94 -0
  13. package/templates/docs/00-integration/client-integration-guide.md +9 -7
  14. package/templates/docs/00-integration/client-lib-folder.md +115 -0
  15. package/templates/docs/00-integration/component-checklist.md +88 -62
  16. package/templates/docs/00-integration/constraints.md +13 -5
  17. package/templates/docs/00-integration/custom-field-logic-and-navigation.md +56 -0
  18. package/templates/docs/00-integration/progress.md +4 -2
  19. package/templates/docs/00-integration/setup.md +12 -0
  20. package/templates/docs/00-integration/ui-customization-wizard.md +144 -0
  21. package/templates/docs/00-integration/useSurveySDK.md +35 -1
  22. package/templates/docs/00-integration/wizard-api.md +134 -0
  23. package/templates/docs/00-integration/wizard-config-handoff.md +246 -0
  24. package/templates/docs/00-integration/wizard-preview-build-guide.md +400 -0
  25. package/templates/docs/00-integration/wizard-question-type-styling.md +195 -0
  26. package/templates/docs/01-components/01-survey-page.md +18 -8
  27. package/templates/docs/01-components/02-question.md +2 -2
  28. package/templates/docs/01-components/03-rating-scale.md +12 -18
  29. package/templates/docs/01-components/05-csat-matrix-scale.md +18 -26
  30. package/templates/docs/01-components/06-likert-matrix-scale.md +19 -39
  31. package/templates/docs/01-components/07-slider-matrix-scale.md +48 -33
  32. package/templates/docs/01-components/09-custom-slider-track.md +15 -25
  33. package/templates/docs/01-components/10-header-footer.md +23 -4
  34. package/templates/docs/01-components/11-progress-bar.md +14 -6
  35. package/templates/docs/01-components/19-survey-sticky-chrome.md +79 -0
  36. package/templates/docs/01-components/README.md +2 -2
  37. package/templates/docs/02-reference/question-types/10-slider-matrix.md +38 -26
  38. package/templates/docs/02-reference/routing-table.md +1 -0
  39. package/templates/docs/02-reference/value-derivation.md +1 -1
  40. package/templates/docs/03-ui-specs/00-question-shell.md +10 -0
  41. package/templates/docs/03-ui-specs/01-rating.md +18 -0
  42. package/templates/docs/03-ui-specs/02-radio.md +16 -1
  43. package/templates/docs/03-ui-specs/03-text.md +12 -1
  44. package/templates/docs/03-ui-specs/04-csat.md +84 -50
  45. package/templates/docs/03-ui-specs/06-slider.md +3 -1
  46. package/templates/docs/03-ui-specs/07-matrix-cfm.md +71 -48
  47. package/templates/docs/03-ui-specs/08-matrix-csat-rating.md +48 -18
  48. package/templates/docs/03-ui-specs/09-slider-matrix.md +59 -32
  49. package/templates/docs/03-ui-specs/10-file-upload.md +11 -0
  50. package/templates/docs/03-ui-specs/11-text-and-media.md +4 -0
  51. package/templates/docs/03-ui-specs/12-survey-chrome.md +54 -5
  52. package/templates/docs/03-ui-specs/13-heatmap.md +12 -1
  53. package/templates/docs/03-ui-specs/14-rank-order.md +11 -0
  54. package/templates/docs/03-ui-specs/README.md +4 -2
  55. package/templates/docs/03-ui-specs/shared/matrix-dropdown.md +1 -1
  56. package/templates/docs/MANIFEST.json +162 -6
  57. package/templates/docs/index.md +116 -117
  58. package/templates/docs/templates/CsatMatrixScale.tsx +637 -0
  59. package/templates/docs/templates/FileUploadScale.tsx +262 -0
  60. package/templates/docs/templates/HeatmapScale.tsx +114 -0
  61. package/templates/docs/templates/LikertMatrixScale.tsx +414 -0
  62. package/templates/docs/templates/Question.tsx +3 -0
  63. package/templates/docs/templates/RatingScale.tsx +129 -0
  64. package/templates/docs/templates/SliderMatrixScale.tsx +248 -0
  65. package/templates/docs/templates/SurveyStickyChrome.tsx +24 -0
  66. package/templates/docs/templates/customFieldValues.ts +36 -0
  67. package/templates/docs/templates/implementation_plan.md +56 -9
  68. package/templates/docs/templates/labelStyles.ts +33 -0
  69. package/templates/docs/templates/selectionStyles.ts +100 -0
  70. package/templates/docs/templates/surveyUiIcons.tsx +11 -0
  71. package/templates/docs/templates/surveyUiScaleUtils.ts +51 -0
  72. package/templates/docs/templates/verify-agent-build.sh +119 -0
  73. package/templates/preview-harness/preview-bridge.inline.js +201 -0
  74. package/templates/preview-harness/previewPages.js +108 -0
  75. package/templates/preview-harness/previewPages.ts +298 -0
  76. package/templates/preview-harness/vite-app/src/PreviewConfigContext.tsx +67 -0
  77. package/templates/preview-harness/vite-app/src/QuestionPreview.tsx +122 -0
  78. package/templates/preview-harness/vite-app/src/SurveyPagePreview.tsx +75 -0
  79. package/templates/preview-harness/vite-app/src/fixtures/questions.ts +311 -0
  80. package/templates/preview-harness/vite-app/src/globals.css +16 -0
  81. package/templates/preview-harness/vite-app/src/mount.tsx +9 -0
  82. package/templates/preview-harness/vite-app/src/preview-live-overrides.css +101 -0
  83. package/templates/preview-harness/vite-app/stubs/next-dynamic.tsx +25 -0
  84. package/templates/preview-harness/vite-app/stubs/next-image.tsx +29 -0
  85. package/templates/previewBridge.ts +153 -0
  86. package/templates/survey-theme.css +295 -0
  87. package/templates/survey-ui-config.schema.json +213 -0
  88. package/templates/wizard-dist/assets/PreviewMock-DgHfrVeb.js +1 -0
  89. package/templates/wizard-dist/assets/TypePanel-CFVC3Ptn.js +1 -0
  90. package/templates/wizard-dist/assets/index-BhWM50Yu.css +1 -0
  91. package/templates/wizard-dist/assets/index-DYK3X1e5.js +34 -0
  92. package/templates/wizard-dist/assets/vendor-BwkXDkd3.js +17 -0
  93. package/templates/wizard-dist/index.html +20 -0
  94. package/templates/wizard.html +13 -1129
@@ -1,78 +1,105 @@
1
1
  # UI Spec: Slider Matrix (`type: 'SLIDER_MATRIX'`)
2
2
 
3
- > **Implement from this spec** (npm installs). Monorepo reference: `apps/client/src/components/SliderMatrixScale.tsx`
3
+ > **COPY TEMPLATE FIRST:** [`templates/SliderMatrixScale.tsx`](../templates/SliderMatrixScale.tsx) + [`CustomSliderTrack.tsx`](../templates/CustomSliderTrack.tsx) + [`surveyUiScaleUtils.ts`](../templates/surveyUiScaleUtils.ts)
4
+ >
5
+ > **Anti-pattern:** Do NOT build custom range overlay sliders. Lone pink circle with no track = broken implementation.
6
+ >
4
7
  > **SDK type:** `SliderMatrixQuestion` — `fetchSurvey/types/sliderMatrix.ts`
5
8
  > **Component blueprint:** `01-components/07-slider-matrix-scale.md`
6
- > **Shared:** [`shared/custom-slider-track.md`](shared/custom-slider-track.md)
7
9
 
8
10
  ## Config Inventory
9
11
 
10
12
  | Field | Implemented | UI impact |
11
13
  |-------|-------------|-----------|
12
- | `rows[]` (min/max/step) | ✅ | Per-row slider range |
13
- | `labels[]` | ✅ | Header anchor labels |
14
- | `tickValues[]` | ✅ | Colored badges above track |
15
- | `ticks` | ✅ | Discrete marks + htmlStep |
16
- | `sliderType: 'graphics'` | ✅ | Plain text tick labels; emoji thumb |
14
+ | `statementRows[]` (min/max/step) | ✅ | Per-row slider + **25% statement column** |
15
+ | `scaleAnchorLabels?` | ✅ | Text labels above tick badge row |
16
+ | `ticks.count` + `ticks.marks[]` | ✅ | Colored header badges (`tickPosition`, `tickLabel`) |
17
+ | `sliderStyle: 'graphics'` | ✅ | Plain text tick labels; emoji thumb |
17
18
  | `enableInputBox` | ✅ | 80px input + stepper buttons |
18
19
  | `displayValues` | ✅ | Passed to CustomSliderTrack |
19
- | `enableNotApplicable` | ✅ | Checkbox; value `'N/A'` string |
20
+ | `hasNotApplicableOption` | ✅ | Checkbox; answer value **`'N/A'`** string |
20
21
 
21
22
  ## Layout Anatomy
22
23
 
23
24
  ```
24
- [header: labels[] + tickValues badges OR computed marks]
25
+ [header: scaleAnchorLabels? + colored tick badges from ticks.marks]
25
26
  ┌─ row band (#f3f4f6, rounded-xl) ─────────────────────────────┐
26
- │ 25% statement │ CustomSliderTrack │ 25% rightText? │ input? │ N/A? │
27
+ │ 25% statementText │ CustomSliderTrack │ 80px input? │ N/A? │
27
28
  └───────────────────────────────────────────────────────────────┘
28
29
  ```
29
30
 
30
- ## Header Tick Positioning
31
+ ## Statement text (CRITICAL)
32
+
33
+ Every row MUST show `statementRows[].statementText` in the left 25% column:
34
+
35
+ ```tsx
36
+ <div style={{ flex: '0 0 25%', paddingRight: '16px' }}>
37
+ {row.statementText && row.statementText !== 'Question' && (
38
+ <span dangerouslySetInnerHTML={{ __html: row.statementText }} />
39
+ )}
40
+ </div>
41
+ ```
42
+
43
+ Agents that hide statement text or merge it into the question title break multi-row sliders.
44
+
45
+ ## Header tick badges
31
46
 
32
47
  ```typescript
33
- percentage = ((tv.value - min) / (max - min)) * 100
34
- sliceWidth = tickValues.length > 1 ? 100 / (tickValues.length - 1) : 100
35
- // Colored badge: tv.color bg, white text, rounded 4px
36
- // graphics: plain text label instead of colored badge
48
+ tickValues = ticks.marks.map((mark, i) => ({
49
+ value: mark.tickPosition,
50
+ label: mark.tickLabel,
51
+ color: tickColorFromIndex(i, ticks.count),
52
+ }));
53
+ percentage = ((tv.value - min) / (max - min)) * 100;
54
+ htmlStep = ticks.count > 1 ? (max - min) / (ticks.count - 1) : row.step;
37
55
  ```
38
56
 
39
- ## CustomSliderTrack per row — scale labels
57
+ ## CustomSliderTrack per row
40
58
 
41
59
  ```typescript
42
60
  <CustomSliderTrack
43
61
  min={row.min}
44
62
  max={row.max}
45
- htmlStep={row.htmlStep}
63
+ htmlStep={htmlStep}
46
64
  value={rowVal as number}
47
- tickLabels={row.tickLabels ?? scaleColumns.map(c => c.label)}
48
- ticks={row.ticks}
65
+ disabled={rowVal === 'N/A'}
66
+ hasSelectedValue={typeof rowVal === 'number'}
67
+ displayValues={displayValues}
49
68
  sliderType={question.sliderStyle === 'graphics' ? 'graphics' : undefined}
69
+ ticks={ticks.count}
50
70
  onChange={v => updateRow(row.id, v)}
51
71
  />
52
72
  ```
53
73
 
54
- Pass **`scaleColumns[].label`** (or row tick labels) as `tickLabels` — required for correct tooltip text (Day 19 fix).
55
-
56
74
  ## N/A Behavior
57
75
 
58
- - Checked → `rowVal === 'N/A'`; slider disabled, input grayed
76
+ - Checked → `rowVal === 'N/A'`; slider disabled
59
77
  - Unchecked → reset to `defaultValue ?? min`
60
78
 
61
- ## Input Box
62
-
63
- - Clamp to row.min / row.max on change
64
- - Steppers use `htmlStep` increment/decrement
65
-
66
79
  ## Answer Mutation
67
80
 
68
81
  ```typescript
69
- onSelect(Record<string, number | null | 'N/A'>)
82
+ onSelect(Record<string, number | 'N/A'>)
83
+ onAction({ type: 'CHANGE', payload: { questionId, answerValue: MatrixRowAnswers } })
70
84
  ```
71
85
 
72
86
  ## Agent Checklist
73
87
 
74
- - [ ] Shared header from first row min/max/step
75
- - [ ] Gray row bands with 25% statement columns
88
+ - [ ] Copied portable templates did not build slider from scratch
89
+ - [ ] Shared header with colored 0–10 badges (even single-row questions)
90
+ - [ ] Gray row bands with **visible** statement text in 25% column
76
91
  - [ ] CustomSliderTrack per row with correct htmlStep
77
- - [ ] tickValues colored badges (non-graphics)
78
- - [ ] N/A mutex disables slider + input
92
+ - [ ] N/A uses `'N/A'` string, not null
93
+
94
+ ## Wizard customization (Phase 5 — required)
95
+
96
+ | Control | CSS vars | Apply in |
97
+ |---------|----------|----------|
98
+ | Tick label items | `--cfm-slider-tick-label-color`, `--cfm-slider-tick-label-bg` | Header badge row — `sliderTickLabelStyle()` |
99
+ | Row band | `--cfm-slider-row-bg` | Gray statement band |
100
+ | Thumb selection | `--cfm-matrix-selected`, `--cfm-input-focus-ring` | `CustomSliderTrack` thumb ring + annulus + dot |
101
+
102
+ ### Wizard checklist
103
+
104
+ - [ ] Colored tick badges use `--cfm-number-*` when traffic-light mode applies
105
+ - [ ] `labelStyles.ts` imported for tick label row
@@ -73,3 +73,14 @@ onAction({ type: 'CHANGE', payload: { questionId, answerValue: UploadedFileAnswe
73
73
  - [ ] Final answer shape: `UploadedFileAnswer[]` with `id` + `mediaUrl`
74
74
  - [ ] Remove clears answer or passes updated array
75
75
  - [ ] Matrix row: [`question-type-sdk-matrix.md#file_upload`](../00-integration/question-type-sdk-matrix.md#file_upload)
76
+
77
+ ## Wizard customization (Phase 5 — required)
78
+
79
+ | Control | CSS vars | Apply in |
80
+ |---------|----------|----------|
81
+ | Dropzone border | `--cfm-upload-border-color` | Dashed border on upload area |
82
+ | Focus / drag-over | `--cfm-input-focus-ring` | Active dropzone outline |
83
+
84
+ ### Wizard checklist
85
+
86
+ - [ ] Dropzone border uses `var(--cfm-upload-border-color)` not hardcoded hex
@@ -47,3 +47,7 @@ Container-level media (survey chrome) is separate — see `03-ui-specs/12-survey
47
47
 
48
48
  - [ ] No CHANGE action — display-only
49
49
  - [ ] Matrix row: [`question-type-sdk-matrix.md#text_and_media`](../00-integration/question-type-sdk-matrix.md#text_and_media)
50
+
51
+ ## Wizard customization (Phase 5 — required)
52
+
53
+ Display-only — no selection tokens. Media caption may use `--cfm-question-title-color` for consistency.
@@ -1,7 +1,7 @@
1
1
  # UI Spec: Survey Chrome
2
2
 
3
- > **Reference:** `SurveyPage.tsx`, `Header.tsx`, `Footer.tsx`, `ProgressBar.tsx`, `LanguageSelector.tsx`, `IntroPage.tsx`, `EndPage.tsx`, `PausedPage.tsx`
4
- > **Component blueprints:** `01-components/01-survey-page.md`, `10-header-footer.md`, `11-progress-bar.md`, `12-language-selector.md`, `14-intro-page.md`, `15-end-page.md`, `16-paused-page.md`
3
+ > **Reference:** `SurveyPage.tsx`, `SurveyStickyChrome.tsx`, `Header.tsx`, `Footer.tsx`, `ProgressBar.tsx`, `LanguageSelector.tsx`, `IntroPage.tsx`, `EndPage.tsx`, `PausedPage.tsx`
4
+ > **Component blueprints:** `01-components/01-survey-page.md`, `10-header-footer.md`, `11-progress-bar.md`, `19-survey-sticky-chrome.md`, `12-language-selector.md`, `14-intro-page.md`, `15-end-page.md`, `16-paused-page.md`
5
5
 
6
6
  ## SurveyPage — Phase Router
7
7
 
@@ -21,9 +21,8 @@
21
21
  ## Active Layout
22
22
 
23
23
  ```
24
- Header
24
+ SurveyStickyChrome (sticky top-0 — Header + ProgressBar)
25
25
  main (max-w-4xl)
26
- ProgressBar(progressPercentage)
27
26
  LanguageSelector (hidden if ≤1 language)
28
27
  Questions (space-y-12)
29
28
  Submit error (if any)
@@ -31,6 +30,25 @@ main (max-w-4xl)
31
30
  Footer
32
31
  ```
33
32
 
33
+ ### Sticky chrome (required on content phase)
34
+
35
+ Long pages push the last question below the fold. Wrap **Header + ProgressBar** in one sticky container so progress stays visible while answering.
36
+
37
+ ```tsx
38
+ <div className="sticky top-0 z-30 bg-white shadow-[0_2px_8px_rgba(0,0,0,0.06)]">
39
+ <Header embedded />
40
+ <div className="mx-auto w-full max-w-4xl px-4 pb-5 pt-4 sm:px-6 lg:px-8">
41
+ <ProgressBar progressPercentage={state.progressPercentage} />
42
+ </div>
43
+ </div>
44
+ ```
45
+
46
+ **Spacing:** `pt-4` (16px) between logo block and progress track; `pb-5` (20px) below the track before scrollable content. Do not flush the bar against the logo square.
47
+
48
+ See [`01-components/19-survey-sticky-chrome.md`](../01-components/19-survey-sticky-chrome.md). Copy [`templates/SurveyStickyChrome.tsx`](../templates/SurveyStickyChrome.tsx).
49
+
50
+ Use standalone `Header` (no sticky wrapper) on intro, end, paused, loading, and terminal phases.
51
+
34
52
  ## Navigation Labels
35
53
 
36
54
  German when `survey.language.startsWith('de')`:
@@ -51,6 +69,14 @@ German when `survey.language.startsWith('de')`:
51
69
 
52
70
  Pink fill bar. Bound to `state.progressPercentage` (path-scoped — see `00-integration/progress.md`).
53
71
 
72
+ Render inside **`SurveyStickyChrome`** (sticky header + progress) on the content phase — not inside scrollable `<main>`. Clamp width to `0–100`.
73
+
74
+ | Token | Value | Role |
75
+ |-------|-------|------|
76
+ | Gap above track | `pt-4` (16px) | Space between logo block and progress bar |
77
+ | Gap below track | `pb-5` (20px) | Breathing room before question content |
78
+ | Track height | `h-3` (12px) | Full-width rounded pill |
79
+
54
80
  ## LanguageSelector
55
81
 
56
82
  - Hidden when `languages.length <= 1`
@@ -97,8 +123,31 @@ Disable primary Next/Submit when `submitSurveyResults.isLoading`; label "Submitt
97
123
  - [ ] `onAction({ type: 'START' })` on intro
98
124
  - [ ] `onAction({ type: 'CHANGE' })` wired per question with `allAnswers` / `allQuestions`
99
125
  - [ ] Back uses `state.canGoBack`; primary uses `state.primaryButtonType`
100
- - [ ] ProgressBar receives `state.progressPercentage`
126
+ - [ ] ProgressBar receives `state.progressPercentage` inside sticky chrome (`SurveyStickyChrome`)
127
+ - [ ] Sticky `top-0 z-30` wrapper keeps progress visible on long pages
128
+ - [ ] `pt-4` / `pb-5` spacing between logo, progress track, and question content
101
129
  - [ ] Header/Footer on every state branch
102
130
  - [ ] `questionNumber` prefix when present on content questions
103
131
  - [ ] Intro / Paused / End / terminal phase branches
104
132
  - [ ] Submit button disabled while `submitSurveyResults.isLoading`
133
+
134
+ ## Wizard customization (Phase 5 — required)
135
+
136
+ > Full chrome contract: [`wizard-preview-build-guide.md`](../00-integration/wizard-preview-build-guide.md).
137
+
138
+ | Element | `data-cfm-*` / CSS vars |
139
+ |---------|-------------------------|
140
+ | Logo | `data-cfm-logo` or `data-cfm-logo-text` |
141
+ | Company | `data-cfm-company` |
142
+ | Survey title | `data-cfm-survey-title` |
143
+ | Thank you | `data-cfm-thank-you` |
144
+ | Next / Back | `data-cfm-btn-next`, `data-cfm-btn-back` |
145
+ | Footer | `data-cfm-copyright`, `data-cfm-footer-links` |
146
+ | Progress | `--cfm-progress-track`, `--cfm-progress-fill` |
147
+ | Header bg | `--cfm-header-bg`, `--cfm-header-text` |
148
+
149
+ ### Wizard checklist
150
+
151
+ - [ ] All editable chrome strings use `data-cfm-*` attributes
152
+ - [ ] Buttons and progress use `var(--cfm-*)` — not hardcoded Telekom hex
153
+ - [ ] Logo: text/CSS **or** local `public/` image — never external URL (see `constraints.md` §4)
@@ -1,6 +1,6 @@
1
1
  # UI Spec: Heatmap (`type: 'HEATMAP'`)
2
2
 
3
- > **Implement from this spec** (npm installs). Monorepo reference: `apps/client/src/components/HeatmapScale.tsx`
3
+ > **Implement from this spec.** Portable template: `docs/templates/HeatmapScale.tsx`
4
4
  > **SDK type:** `HeatmapQuestion` — `fetchSurvey/types/heatmap.ts`
5
5
  > **Component blueprint:** `01-components/17-heatmap-scale.md`
6
6
 
@@ -78,3 +78,14 @@ Copy coord helpers from [`templates/heatmapCoords.ts`](../templates/heatmapCoord
78
78
  - [ ] Wait for `onLoad` before accepting clicks
79
79
  - [ ] Percentage-positioned spots (survives resize)
80
80
  - [ ] Do NOT render region overlays on respondent UI
81
+
82
+ ## Wizard customization (Phase 5 — required)
83
+
84
+ | Control | CSS vars | Apply in |
85
+ |---------|----------|----------|
86
+ | Spot color | `--cfm-heatmap-spot-color` | Click markers |
87
+ | Spot border | `--cfm-heatmap-spot-border` | Marker outline |
88
+
89
+ ### Wizard checklist
90
+
91
+ - [ ] Spot markers use `--cfm-heatmap-spot-*` vars
@@ -126,3 +126,14 @@ RANK_ORDER operators (ranked as/above/below) — see [`logic-fields-catalog.md`]
126
126
  - [ ] `option.imageUrl` before `previewImageUrl`
127
127
  - [ ] No client-side re-shuffle when `shuffleOptions` is true
128
128
  - [ ] No stub placeholder text
129
+
130
+ ## Wizard customization (Phase 5 — required)
131
+
132
+ | Control | CSS vars | Apply in |
133
+ |---------|----------|----------|
134
+ | Rank badge | `--cfm-rank-badge-bg`, `--cfm-rank-badge-color` | Drag handle / rank number |
135
+ | Option border | `--cfm-rank-option-border` | List item cards |
136
+
137
+ ### Wizard checklist
138
+
139
+ - [ ] Rank badges and option borders use `--cfm-rank-*` vars
@@ -1,9 +1,11 @@
1
1
  # UI Specifications — Reference Implementation Guide
2
2
 
3
3
  > **For npm installs:** implement from these specs + [`templates/Question.tsx`](../templates/Question.tsx).
4
- > **For monorepo devs:** Reference client at `apps/client/src/components/` aligns with SDK typestrust `03-ui-specs/`, `templates/Question.tsx`, and exported types.
4
+ > **Shared lib utilities** (NPS colors, custom fields, heatmap coords) live in [`templates/`](../templates/) see [`client-lib-folder.md`](../00-integration/client-lib-folder.md), not in this ui-specs folder.
5
5
  > Read the spec for **each question type present** in the fetched survey before writing React code.
6
6
 
7
+ > **Wizard customization:** When the client may use the web wizard, hardcoded hex in § Design Tokens below are **defaults only**. Wire `var(--cfm-*)` per [`wizard-question-type-styling.md`](../00-integration/wizard-question-type-styling.md) — especially `--cfm-matrix-selected` (cell annulus) and `--cfm-input-focus-ring` (outer border + inner dot). Portable scale templates in `docs/templates/` are layout/logic baselines; extend with `selectionStyles.ts` + `labelStyles.ts` for live preview.
8
+
7
9
  ## How to Read a UI Spec
8
10
 
9
11
  Each file in this folder follows the same structure:
@@ -75,7 +77,7 @@ Use accent colors from SDK options — never invent colors for NPS:
75
77
 
76
78
  ## When to Read
77
79
 
78
- After Phase 3 in [`index.md`](../index.md), for each question in the fetched survey:
80
+ After Phase 3 in [`index.md`](../index.md) and [`agent-execution-flow.md`](../00-integration/agent-execution-flow.md), for each question in the fetched survey:
79
81
 
80
82
  1. Read [`02-reference/question-types/`](../02-reference/question-types/) for data shape
81
83
  2. Read matching spec from this folder (see [`MANIFEST.json`](../MANIFEST.json) `questionTypes.{type}.uiSpec`)
@@ -1,6 +1,6 @@
1
1
  # UI Spec: Matrix Dropdown (shared primitive)
2
2
 
3
- > **Implement from this spec** (npm installs). Monorepo reference: `apps/client/src/components/MatrixDropdown.tsx`
3
+ > **Implement from this spec.** Portable template: `docs/templates/MatrixDropdown.tsx`
4
4
  > **Used by:** `LikertMatrixScale`, `CsatMatrixScale` vertical list
5
5
 
6
6
  ## Modes
@@ -1,5 +1,5 @@
1
1
  {
2
- "docsSchemaVersion": "2.4.0",
2
+ "docsSchemaVersion": "2.6.0",
3
3
  "sdkTypesVersion": "0.2.1",
4
4
  "packageNames": {
5
5
  "npm": "@explorer02/cfm-survey-sdk",
@@ -13,8 +13,28 @@
13
13
  "clientInputs": [
14
14
  "initialPrompt",
15
15
  "instanceIdJwt",
16
+ "customizationWizardOptional",
16
17
  "deployConfirmation"
17
18
  ],
19
+ "surveyUiConfig": {
20
+ "artifact": "survey-ui-config.json",
21
+ "schema": "templates/survey-ui-config.schema.json",
22
+ "cliCommand": "npx cfm-sdk customize-ui",
23
+ "cliCommands": {
24
+ "aws": "npx cfm-sdk customize-ui",
25
+ "local": "npx cfm-sdk customize",
26
+ "exportPreviews": "npx cfm-sdk export-previews"
27
+ },
28
+ "wizardApiDoc": "00-integration/wizard-api.md",
29
+ "applyDoc": "00-integration/apply-ui-config.md",
30
+ "wizardDoc": "00-integration/ui-customization-wizard.md",
31
+ "previewBuildDoc": "00-integration/wizard-preview-build-guide.md",
32
+ "questionTypeStylingDoc": "00-integration/wizard-question-type-styling.md",
33
+ "agentExecutionFlowDoc": "00-integration/agent-execution-flow.md",
34
+ "agentOperatingContractDoc": "00-integration/agent-operating-contract.md",
35
+ "wizardConfigHandoffDoc": "00-integration/wizard-config-handoff.md",
36
+ "previewBridgeTemplate": "templates/previewBridge.ts"
37
+ },
18
38
  "hookContract": {
19
39
  "answersField": "state.answers",
20
40
  "customFieldValuesField": "options.customFieldValues",
@@ -44,6 +64,14 @@
44
64
  ]
45
65
  },
46
66
  "integrationDocs": [
67
+ {
68
+ "path": "00-integration/agent-operating-contract.md",
69
+ "purpose": "Anti-hallucination rules — read-before-write gates, doc routing, forbidden inventions (read FIRST)"
70
+ },
71
+ {
72
+ "path": "00-integration/agent-execution-flow.md",
73
+ "purpose": "Authoritative agent pipeline — client questions, phase order, wizard-ready build (read with index.md)"
74
+ },
47
75
  {
48
76
  "path": "00-integration/client-integration-guide.md",
49
77
  "purpose": "Master wiring map — start here for full client integration"
@@ -119,6 +147,38 @@
119
147
  {
120
148
  "path": "00-integration/file-upload-aws.md",
121
149
  "purpose": "Presigned S3 upload flow for FILE_UPLOAD submit contract"
150
+ },
151
+ {
152
+ "path": "00-integration/canonical-survey-fixtures.md",
153
+ "purpose": "Page-by-page UI expectations — matrix labels, slider header, template copy list"
154
+ },
155
+ {
156
+ "path": "00-integration/client-lib-folder.md",
157
+ "purpose": "src/lib layout — customFieldValues + surveyUi portable templates"
158
+ },
159
+ {
160
+ "path": "00-integration/wizard-preview-build-guide.md",
161
+ "purpose": "Build client components for wizard live preview — --cfm-* vars and data-cfm-* hooks (Phase 5)"
162
+ },
163
+ {
164
+ "path": "00-integration/wizard-question-type-styling.md",
165
+ "purpose": "Per-type wizard styling — selection colors, label items, all 11 question types (Phase 5)"
166
+ },
167
+ {
168
+ "path": "00-integration/wizard-config-handoff.md",
169
+ "purpose": "Wait for client submit, review artifacts (.final.json, .diff.md), confirm-ui-config order (Phase 6b→6c)"
170
+ },
171
+ {
172
+ "path": "00-integration/ui-customization-wizard.md",
173
+ "purpose": "Optional web wizard flow, CLI commands, wizard steps"
174
+ },
175
+ {
176
+ "path": "00-integration/apply-ui-config.md",
177
+ "purpose": "Apply survey-ui-config.json tokens after wizard (Phase 6c)"
178
+ },
179
+ {
180
+ "path": "00-integration/wizard-api.md",
181
+ "purpose": "EC2 /wizard/* API for customize-ui and preview hosting"
122
182
  }
123
183
  ],
124
184
  "templateScaleFiles": [
@@ -126,7 +186,15 @@
126
186
  "templates/MatrixDropdown.tsx",
127
187
  "templates/CustomSliderTrack.tsx",
128
188
  "templates/surveyUiIcons.tsx",
129
- "templates/heatmapCoords.ts"
189
+ "templates/surveyUiScaleUtils.ts",
190
+ "templates/SliderMatrixScale.tsx",
191
+ "templates/RatingScale.tsx",
192
+ "templates/LikertMatrixScale.tsx",
193
+ "templates/CsatMatrixScale.tsx",
194
+ "templates/HeatmapScale.tsx",
195
+ "templates/FileUploadScale.tsx",
196
+ "templates/heatmapCoords.ts",
197
+ "templates/customFieldValues.ts"
130
198
  ],
131
199
  "deployTargets": {
132
200
  "aws": {
@@ -134,6 +202,8 @@
134
202
  "script": "templates/deploy-to-aws.sh",
135
203
  "apiBaseEnv": "CFM_DEPLOY_API_BASE",
136
204
  "apiBaseDefault": "http://43.204.26.213:3000",
205
+ "wizardClientUrlPattern": "{CFM_DEPLOY_API_BASE}/wizard-app/index.html?session={sessionId}&secret={sessionSecret}",
206
+ "wizardClientUrlNote": "Printed by customize-ui after component previews upload to S3 — agent copies link and sends to client",
137
207
  "instanceIdEnv": "CFM_INSTANCE_ID",
138
208
  "outDirEnv": "OUT_DIR",
139
209
  "healthPath": "/health",
@@ -190,7 +260,10 @@
190
260
  "name": "orient",
191
261
  "read": [
192
262
  "index.md",
193
- "../AGENT.md"
263
+ "../AGENT.md",
264
+ "00-integration/agent-operating-contract.md",
265
+ "00-integration/agent-execution-flow.md",
266
+ "MANIFEST.json"
194
267
  ],
195
268
  "actions": [
196
269
  "detectFramework",
@@ -229,6 +302,7 @@
229
302
  "00-integration/display-logic-and-navigation.md",
230
303
  "00-integration/answer-logic-and-navigation.md",
231
304
  "00-integration/custom-field-logic-and-navigation.md",
305
+ "00-integration/client-lib-folder.md",
232
306
  "00-integration/question-display-variants.md",
233
307
  "00-integration/question-numbering.md",
234
308
  "00-integration/placeholders-and-tokens.md",
@@ -244,7 +318,12 @@
244
318
  "id": 4,
245
319
  "name": "planAndIngest",
246
320
  "readAlways": [
321
+ "00-integration/agent-operating-contract.md",
322
+ "00-integration/agent-execution-flow.md",
247
323
  "00-integration/component-checklist.md",
324
+ "00-integration/canonical-survey-fixtures.md",
325
+ "00-integration/client-lib-folder.md",
326
+ "00-integration/custom-field-logic-and-navigation.md",
248
327
  "00-integration/question-type-sdk-matrix.md",
249
328
  "00-integration/logic-fields-catalog.md",
250
329
  "03-ui-specs/00-question-shell.md",
@@ -266,7 +345,12 @@
266
345
  "id": 5,
267
346
  "name": "build",
268
347
  "read": [
348
+ "00-integration/agent-operating-contract.md",
349
+ "00-integration/agent-execution-flow.md",
269
350
  "00-integration/constraints.md",
351
+ "00-integration/component-checklist.md",
352
+ "00-integration/wizard-preview-build-guide.md",
353
+ "00-integration/wizard-question-type-styling.md",
270
354
  "02-reference/routing-table.md"
271
355
  ],
272
356
  "onDemand": "02-reference/question-types"
@@ -275,7 +359,10 @@
275
359
  "id": 6,
276
360
  "name": "verify",
277
361
  "read": [
278
- "00-integration/constraints.md"
362
+ "00-integration/agent-operating-contract.md",
363
+ "00-integration/agent-execution-flow.md",
364
+ "00-integration/constraints.md",
365
+ "00-integration/component-checklist.md"
279
366
  ],
280
367
  "script": "templates/verify-agent-build.sh",
281
368
  "checklist": [
@@ -295,8 +382,76 @@
295
382
  "outIndexHtmlExists",
296
383
  "outFileCountUnder500",
297
384
  "rankOrderScaleTemplateCopied",
385
+ "sliderMatrixScaleTemplateCopied",
386
+ "ratingScaleTemplateCopied",
387
+ "matrixScaleTemplatesCopied",
388
+ "heatmapScaleTemplateCopied",
298
389
  "dndKitWhenRankOrder",
299
- "fileUploadPresignedFlowWhenFileUpload"
390
+ "fileUploadPresignedFlowWhenFileUpload",
391
+ "surveyThemeCssPresent",
392
+ "selectionStylesCopied",
393
+ "labelStylesCopied",
394
+ "chromeDataCfmHooksPresent",
395
+ "draftSurveyUiConfigPresent"
396
+ ],
397
+ "postVerify": [
398
+ "startDevServer",
399
+ "askCustomizationWizard"
400
+ ]
401
+ },
402
+ {
403
+ "id": "6b",
404
+ "name": "optionalUiCustomization",
405
+ "optional": true,
406
+ "requiresClientConfirmation": true,
407
+ "read": [
408
+ "00-integration/wizard-config-handoff.md",
409
+ "00-integration/ui-customization-wizard.md",
410
+ "00-integration/wizard-preview-build-guide.md",
411
+ "00-integration/wizard-question-type-styling.md",
412
+ "00-integration/wizard-api.md"
413
+ ],
414
+ "cliCommand": "npx cfm-sdk customize-ui",
415
+ "cliCommandLocal": "npx cfm-sdk customize",
416
+ "cliConfirmCommand": "npx cfm-sdk confirm-ui-config",
417
+ "output": "survey-ui-config.final.json",
418
+ "outputArtifacts": [
419
+ "survey-ui-config.seed.json",
420
+ "survey-ui-config.final.json",
421
+ "survey-ui-config.diff.json",
422
+ "survey-ui-config.diff.md"
423
+ ],
424
+ "agentMustWait": true,
425
+ "waitUntil": "cliExit0ConfigurationReady",
426
+ "clientHandoff": "shareDeployedWizardUrlAfterS3Upload",
427
+ "prerequisites": [
428
+ "phase6VerifyPassed",
429
+ "devServerRunning"
430
+ ]
431
+ },
432
+ {
433
+ "id": "6c",
434
+ "name": "applyUiConfig",
435
+ "optional": true,
436
+ "when": "wizardUsedOrSurveyUiConfigExists",
437
+ "read": [
438
+ "00-integration/wizard-config-handoff.md",
439
+ "00-integration/apply-ui-config.md",
440
+ "00-integration/wizard-preview-build-guide.md",
441
+ "00-integration/wizard-question-type-styling.md"
442
+ ],
443
+ "inputFile": "survey-ui-config.final.json",
444
+ "inputFileLocal": "survey-ui-config.json",
445
+ "actions": [
446
+ "readDiffMdCompletely",
447
+ "validateAgainstSchema",
448
+ "regenerateSurveyThemeCssFromFinal",
449
+ "applyDataCfmContentHooks",
450
+ "applyLayoutToggles",
451
+ "applyQuestionTypesTokens",
452
+ "runConfirmUiConfigAws",
453
+ "crossCheckEveryDiffPath",
454
+ "reRunVerify"
300
455
  ]
301
456
  },
302
457
  {
@@ -305,7 +460,8 @@
305
460
  "requiresClientConfirmation": true,
306
461
  "selectTargetFromPrompt": {
307
462
  "awsKeywords": ["aws", "cloudfront", "s3", "ec2", "sprinklr"],
308
- "default": "vercel"
463
+ "default": "vercel",
464
+ "fallbackFromConfig": "survey-ui-config.json deploy.target"
309
465
  },
310
466
  "targets": {
311
467
  "aws": {