@case-framework/survey-assistant 0.1.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 (98) hide show
  1. package/README.md +212 -0
  2. package/dist/authoring-references-Cy8J-DWt.d.mts +48 -0
  3. package/dist/authoring-references-Cy8J-DWt.d.mts.map +1 -0
  4. package/dist/authoring-references-X6uRaWuf.mjs +68 -0
  5. package/dist/authoring-references-X6uRaWuf.mjs.map +1 -0
  6. package/dist/capabilities-B6tvE7fK.mjs +66 -0
  7. package/dist/capabilities-B6tvE7fK.mjs.map +1 -0
  8. package/dist/capabilities-DvSKtjAw.d.mts +182 -0
  9. package/dist/capabilities-DvSKtjAw.d.mts.map +1 -0
  10. package/dist/capabilities-default.d.mts +15 -0
  11. package/dist/capabilities-default.d.mts.map +1 -0
  12. package/dist/capabilities-default.mjs +347 -0
  13. package/dist/capabilities-default.mjs.map +1 -0
  14. package/dist/constants-CFUWrRtl.mjs +7 -0
  15. package/dist/constants-CFUWrRtl.mjs.map +1 -0
  16. package/dist/constants-Jw0k8ojS.d.mts +67 -0
  17. package/dist/constants-Jw0k8ojS.d.mts.map +1 -0
  18. package/dist/controller-proxy-BRDusFiO.d.mts +57 -0
  19. package/dist/controller-proxy-BRDusFiO.d.mts.map +1 -0
  20. package/dist/controller-proxy-Duxo2IFE.mjs +92 -0
  21. package/dist/controller-proxy-Duxo2IFE.mjs.map +1 -0
  22. package/dist/digest-BsoVq49o.d.mts +9 -0
  23. package/dist/digest-BsoVq49o.d.mts.map +1 -0
  24. package/dist/digest.d.mts +2 -0
  25. package/dist/digest.mjs +20 -0
  26. package/dist/digest.mjs.map +1 -0
  27. package/dist/engine-sPvuixWg.mjs +4857 -0
  28. package/dist/engine-sPvuixWg.mjs.map +1 -0
  29. package/dist/engine.d.mts +3 -0
  30. package/dist/engine.mjs +4 -0
  31. package/dist/index-BWNHkpkh.d.mts +187 -0
  32. package/dist/index-BWNHkpkh.d.mts.map +1 -0
  33. package/dist/index-CUqbBjkc.d.mts +641 -0
  34. package/dist/index-CUqbBjkc.d.mts.map +1 -0
  35. package/dist/index-MpEMQzTm.d.mts +123 -0
  36. package/dist/index-MpEMQzTm.d.mts.map +1 -0
  37. package/dist/index-TFtvBiSQ.d.mts +1149 -0
  38. package/dist/index-TFtvBiSQ.d.mts.map +1 -0
  39. package/dist/memory-thread-repository-CJkkVE2-.mjs +718 -0
  40. package/dist/memory-thread-repository-CJkkVE2-.mjs.map +1 -0
  41. package/dist/protocol-DV0ka9WT.mjs +1032 -0
  42. package/dist/protocol-DV0ka9WT.mjs.map +1 -0
  43. package/dist/protocol.d.mts +3 -0
  44. package/dist/protocol.mjs +2 -0
  45. package/dist/react-VuYe-cPh.mjs +521 -0
  46. package/dist/react-VuYe-cPh.mjs.map +1 -0
  47. package/dist/react-integration.d.mts +2 -0
  48. package/dist/react-integration.mjs +3 -0
  49. package/dist/react.d.mts +3 -0
  50. package/dist/react.mjs +4 -0
  51. package/dist/references/assistant-operations.md +287 -0
  52. package/dist/references/core-rules.md +11 -0
  53. package/dist/references/embedded-forms.md +80 -0
  54. package/dist/references/expressions.md +301 -0
  55. package/dist/references/item-types.md +364 -0
  56. package/dist/references/localization.md +25 -0
  57. package/dist/references/rich-text-content.md +193 -0
  58. package/dist/references/source-material-surveys.md +60 -0
  59. package/dist/references/survey-data-model.md +109 -0
  60. package/dist/references/survey-quality.md +23 -0
  61. package/dist/request-context-jcmXT_Q2.mjs +44 -0
  62. package/dist/request-context-jcmXT_Q2.mjs.map +1 -0
  63. package/dist/request-guardrails-C59IWnH8.mjs +73 -0
  64. package/dist/request-guardrails-C59IWnH8.mjs.map +1 -0
  65. package/dist/server-agent.d.mts +131 -0
  66. package/dist/server-agent.d.mts.map +1 -0
  67. package/dist/server-agent.mjs +229 -0
  68. package/dist/server-agent.mjs.map +1 -0
  69. package/dist/server-runtime.d.mts +123 -0
  70. package/dist/server-runtime.d.mts.map +1 -0
  71. package/dist/server-runtime.mjs +296 -0
  72. package/dist/server-runtime.mjs.map +1 -0
  73. package/dist/server-tasks.d.mts +78 -0
  74. package/dist/server-tasks.d.mts.map +1 -0
  75. package/dist/server-tasks.mjs +253 -0
  76. package/dist/server-tasks.mjs.map +1 -0
  77. package/dist/server-tools.d.mts +2 -0
  78. package/dist/server-tools.mjs +2 -0
  79. package/dist/server.d.mts +52 -0
  80. package/dist/server.d.mts.map +1 -0
  81. package/dist/server.mjs +6 -0
  82. package/dist/storage-postgres.d.mts +37 -0
  83. package/dist/storage-postgres.d.mts.map +1 -0
  84. package/dist/storage-postgres.mjs +167 -0
  85. package/dist/storage-postgres.mjs.map +1 -0
  86. package/dist/thread-documents-DamIq9Od.d.mts +91 -0
  87. package/dist/thread-documents-DamIq9Od.d.mts.map +1 -0
  88. package/dist/thread-handlers-DpynUc64.d.mts +112 -0
  89. package/dist/thread-handlers-DpynUc64.d.mts.map +1 -0
  90. package/dist/tools-Hz2p7vyv.mjs +1879 -0
  91. package/dist/tools-Hz2p7vyv.mjs.map +1 -0
  92. package/dist/ui.css +2 -0
  93. package/dist/ui.d.mts +63 -0
  94. package/dist/ui.d.mts.map +1 -0
  95. package/dist/ui.mjs +2238 -0
  96. package/dist/ui.mjs.map +1 -0
  97. package/package.json +166 -0
  98. package/ui.css.d.ts +3 -0
@@ -0,0 +1,287 @@
1
+ # Assistant Operations
2
+
3
+ Use `survey_change` for ordinary survey mutations. Use `survey_expression` with
4
+ `action: "prepare"` for expression mutations on items and survey state that already exist in the
5
+ immutable turn snapshot. Both tools validate their input and register candidate change sets in the
6
+ same turn collector. Only successful completion of the whole turn authorizes the collector's final
7
+ candidate ids for browser apply.
8
+
9
+ Expressions for items created in the current turn are the deliberate exception: embed them in the
10
+ new item's raw structure inside the same `survey_change` proposal. A later tool call cannot inspect
11
+ an item that has not been applied yet.
12
+
13
+ ## Common Workflow
14
+
15
+ 1. Load only the narrow authoring reference needed for the requested convention.
16
+ 2. Use `survey_inspect` for revision, selected item, root group, locale, available item types, response slots, item details, or focused raw JSON.
17
+ 3. For existing items, inspect item details or exact raw paths before editing.
18
+ For image assets, use `survey_inspect` with `action: "assets"`; it returns each explicit `assetId`, metadata, and usage locations without embedded bytes.
19
+ 4. Prefer typed `survey_change` operations for common edits.
20
+ 5. Use `survey_expression` for expression mutations on existing state.
21
+ 6. Use `survey-json-patch` through `survey_change` only for assistant-editable fields not covered by
22
+ typed or expression operations.
23
+ 7. Treat a validated candidate as intended for apply after successful turn completion, never as a dry run. Omit `continuationMode` only on the first mutation call in the response. On every later mutation call across either mutation tool, use `continuationMode: "append"` for independent additional work or `continuationMode: "replace"` for a complete correction that should discard currently active candidates.
24
+ 8. Any rejected mutation blocks the entire turn and authorizes no survey changes. Recover by retrying the complete intended proposal with `continuationMode: "replace"`; only a validated replacement clears the block, and only then may later independent chunks use `append`. A rejected or superseded candidate is not applied. Before claiming completion, reconcile every requested item with the final validated candidates; if any requested work was rejected or discarded, either re-emit it or report the result as incomplete.
25
+
26
+ ## `survey_change` Typed Operations
27
+
28
+ `create-item`
29
+
30
+ - Creates a new item under `target.parentItemId`.
31
+ - Use root group id/key for top-level items.
32
+ - Put visible text in top-level `translations`, not inside `item`.
33
+ - Each translation accepts either `plainText` or a complete structured `content` value.
34
+ - When a new item must contain an image, put a `richText` value with the image block directly in `create-item.translations[].content`. Do not create placeholder content and call `update-placed-image` afterward: new items do not exist in the immutable turn snapshot.
35
+ - Use the actual content locale in `translations[].locale`. Missing locales are introduced when translations are applied.
36
+ - Put item configuration inside `item.config`, not beside `item`.
37
+ - Item key must be unique among siblings.
38
+ - Use explicit item ids when you need to reference the new item in the same proposal.
39
+
40
+ Minimal valid examples:
41
+
42
+ ```json
43
+ {
44
+ "kind": "create-item",
45
+ "target": { "parentItemId": "<root-group-id-or-key>" },
46
+ "item": {
47
+ "id": "intro_privacy",
48
+ "key": "intro_privacy",
49
+ "itemType": "infoItem",
50
+ "config": {}
51
+ },
52
+ "translations": [
53
+ {
54
+ "locale": "en",
55
+ "contentKey": "content",
56
+ "plainText": "This survey asks about your customer experience. Please do not include sensitive personal information."
57
+ }
58
+ ]
59
+ }
60
+ ```
61
+
62
+ ```json
63
+ {
64
+ "kind": "create-item",
65
+ "target": { "parentItemId": "<root-group-id-or-key>" },
66
+ "item": {
67
+ "id": "overall_satisfaction",
68
+ "key": "overall_satisfaction",
69
+ "itemType": "choiceItem",
70
+ "config": {
71
+ "id": "overall_satisfaction",
72
+ "maxSelection": 1,
73
+ "shuffleOptions": false,
74
+ "options": [
75
+ { "id": "very_dissatisfied", "key": "VERY_DISSATISFIED" },
76
+ { "id": "dissatisfied", "key": "DISSATISFIED" },
77
+ { "id": "neutral", "key": "NEUTRAL" },
78
+ { "id": "satisfied", "key": "SATISFIED" },
79
+ { "id": "very_satisfied", "key": "VERY_SATISFIED" }
80
+ ]
81
+ }
82
+ },
83
+ "translations": [
84
+ {
85
+ "locale": "en",
86
+ "contentKey": "title",
87
+ "plainText": "Overall, how satisfied are you with your experience?"
88
+ },
89
+ {
90
+ "locale": "en",
91
+ "contentKey": "options.very_dissatisfied.label",
92
+ "plainText": "Very dissatisfied"
93
+ },
94
+ { "locale": "en", "contentKey": "options.dissatisfied.label", "plainText": "Dissatisfied" },
95
+ { "locale": "en", "contentKey": "options.neutral.label", "plainText": "Neutral" },
96
+ { "locale": "en", "contentKey": "options.satisfied.label", "plainText": "Satisfied" },
97
+ { "locale": "en", "contentKey": "options.very_satisfied.label", "plainText": "Very satisfied" }
98
+ ]
99
+ }
100
+ ```
101
+
102
+ ```json
103
+ {
104
+ "kind": "create-item",
105
+ "target": { "parentItemId": "<root-group-id-or-key>" },
106
+ "item": {
107
+ "id": "personal_data",
108
+ "key": "personal_data",
109
+ "itemType": "formItem",
110
+ "config": {
111
+ "id": "personal_data",
112
+ "fieldGroups": [
113
+ {
114
+ "id": "main",
115
+ "layout": { "base": 1, "xs": 1, "sm": 1, "md": 1, "xl": 1 },
116
+ "fields": [
117
+ {
118
+ "id": "email",
119
+ "key": "EMAIL",
120
+ "type": "input",
121
+ "required": false,
122
+ "controller": { "type": "input", "inputMode": "email" },
123
+ "validations": [{ "id": "email", "type": "email" }]
124
+ }
125
+ ]
126
+ }
127
+ ]
128
+ }
129
+ },
130
+ "translations": [
131
+ { "locale": "en", "contentKey": "title", "plainText": "Personal information" },
132
+ { "locale": "en", "contentKey": "fields.email.label", "plainText": "Email address" }
133
+ ]
134
+ }
135
+ ```
136
+
137
+ `update-item-key`
138
+
139
+ - Changes item coding key. This affects fullKey paths and response naming.
140
+
141
+ `update-item-label`
142
+
143
+ - Changes internal editor label at `metadata.itemLabel`.
144
+ - This is what the UI means by item header "Add label...".
145
+
146
+ `update-item-color`
147
+
148
+ - Changes the editor-only color stored at `metadata.editorItemColor`.
149
+ - Use an editor palette value from item-types. This has no respondent-facing effect.
150
+
151
+ `update-item-translation`
152
+
153
+ - Changes respondent-visible content for one locale/contentKey.
154
+ - Use this with a new locale code when adding translated content for a locale that is not yet present.
155
+ - Use for titles, subtitles, top/bottom content, card footer, info content, and simple field/option labels when the key is known.
156
+ - Use `plainText` for simple unformatted text.
157
+ - Use `content` for structured survey content. For formatted text, set `content.type` to `richText`; do not use markdown syntax to request formatting.
158
+
159
+ `update-placed-image`
160
+
161
+ - Updates one image block already placed in an item's rich-text translation without replacing the surrounding content.
162
+ - Identify the placement with `itemId`, `locale`, `contentKey`, and a zero-based `imageIndex` that counts image blocks only. Include `expectedAssetId` when the current source is a local asset so stale content cannot cause the wrong image to be edited.
163
+ - `changes.source` accepts either `{ "type": "asset", "assetId": "..." }` or `{ "type": "external", "url": "https://..." }`, so a placement can switch between local and external sources.
164
+ - `changes` can also set or clear `alt`, `caption`, `width`, `height`, `maxWidth`, `maxHeight`, and `alignment`. Dimensions use `{ "unit": "px" | "%", "value": number }`; use `null` to remove an optional property.
165
+ - This changes the rendered placement. `update-image-asset-metadata` changes only reusable global asset metadata and does not rewrite ALT text or layout on existing placements.
166
+
167
+ `patch-item-config`
168
+
169
+ - Updates one existing item's configuration without requiring its global survey array index.
170
+ - Every JSON Patch path is relative to the inspected item config root. For example, `/max` targets `item.config.max`.
171
+ - Load the item type's advertised authoring reference when one exists, inspect the current item, and preserve unrelated config fields.
172
+ - If the item capability provides a validator, it validates the resulting config before the change can be applied.
173
+ - Prefer this operation over `survey-json-patch` for custom-item settings and ordinary item-local configuration.
174
+
175
+ `add-choice-option`, `update-choice-option-label`, `remove-choice-option`, `reorder-choice-options`
176
+
177
+ - Use for ordinary choice option edits on existing choice items.
178
+ - For option identifiers, prefer exact id; validation can also normalize key or visible label.
179
+ - For `add-choice-option`, use `option: { id?, key?, label: "visible text" }`. The label is a plain string, not a nested translation object; the operation writes its matching `options.<optionId>.label` translation automatically. `update-choice-option-label.label` is also a plain string.
180
+
181
+ `upsert-form-field-group`, `remove-form-field-group`, `reorder-form-field-groups`, `upsert-form-field`, `remove-form-field`, `move-form-field`
182
+
183
+ - Target a top-level form with `{ "itemId": "<form-id>" }` or a choice option's form with `{ "itemId": "<choice-id>", "optionId": "<option-id>" }`.
184
+ - Add visible labels, placeholders, option labels, and validation messages in the operation's `translations` array so structure and content remain atomic.
185
+ - Group and field identifiers are stable ids; reorder operations require the complete current id set.
186
+
187
+ `set-embedded-form`, `remove-embedded-form`
188
+
189
+ - Add or remove a choice option's inline form using item and option ids, without raw array-index patches.
190
+ - `set-embedded-form` accepts the full form config plus its visible translations in the same operation.
191
+
192
+ `move-item`, `reorder-items`
193
+
194
+ - Move an existing item to a group/index or set a group's complete direct-child order.
195
+ - The validator rejects root moves, cycles, missing children, duplicates, and incomplete reorder lists.
196
+
197
+ `update-survey-metadata`, `update-survey-pagination`, `update-survey-translation`, `remove-survey-locale`
198
+
199
+ - Use typed survey-level operations for metadata, page limits, survey-card/navigation/validation content, and locale removal.
200
+ - Removing the final locale is rejected. Locale removal is intentionally explicit and destructive.
201
+
202
+ `set-image-asset`, `update-image-asset-metadata`, `remove-image-asset`
203
+
204
+ - Prefer `survey_asset import-attachment` for binary image imports so the model never copies attachment base64.
205
+ - To use that import in a new item, follow it with one `survey_change` call using `continuationMode: "append"` and embed `{ "type": "image", "source": { "type": "asset", "assetId": "<returned-id>" } }` directly in the new item's rich-text translation.
206
+ - Metadata and removal are typed and validated. `survey_asset remove` refuses an asset that is still referenced in the immutable snapshot.
207
+ - To remove referenced images and their asset atomically, use one `survey_change` call whose `operations` contain every reference removal first and `{ "kind": "remove-image-asset", "assetId": "..." }` last. For example, deleting an image-only info card and its asset is `[remove-item, remove-image-asset]` in one candidate.
208
+
209
+ `remove-item`
210
+
211
+ - Removes an item.
212
+ - Set `includeChildren: true` only when intentionally deleting a group subtree.
213
+
214
+ `survey-json-patch`
215
+
216
+ - Universal escape hatch for raw survey edits.
217
+ - Must be the only operation in a proposal, but may contain many patch entries.
218
+ - Use it for assistant-editable raw survey fields that have no narrow typed or expression helper.
219
+ - It cannot modify `/$schema`, bypass item capability constraints, or create item types that are not
220
+ deterministically creatable.
221
+ - Use it for advanced choice config and other inspected fields without typed coverage.
222
+ - Use `survey_expression` for template values, display/disabled conditions, expression-based item
223
+ validations, and prefills on existing state. Form-field validation configuration and translated
224
+ validation messages are ordinary config/content, not expression mutations.
225
+ - Use it for field controller internals and any existing custom-registry item config that is not covered by a typed helper.
226
+
227
+ ## Raw Patch Examples
228
+
229
+ Update a form field-group layout:
230
+
231
+ ```json
232
+ {
233
+ "kind": "survey-json-patch",
234
+ "patches": [
235
+ {
236
+ "op": "replace",
237
+ "path": "/surveyItems/3/config/fieldGroups/0/layout",
238
+ "value": { "base": 1, "xs": 1, "sm": 2, "md": 2, "xl": 3 }
239
+ }
240
+ ]
241
+ }
242
+ ```
243
+
244
+ Add a missing locale shell only after inspection confirms that the locale does not exist:
245
+
246
+ ```json
247
+ {
248
+ "kind": "survey-json-patch",
249
+ "patches": [
250
+ {
251
+ "op": "add",
252
+ "path": "/translations/nl",
253
+ "value": {
254
+ "itemTranslations": {},
255
+ "surveyCardContent": {},
256
+ "navigationContent": {},
257
+ "validationMessages": {}
258
+ }
259
+ }
260
+ ]
261
+ }
262
+ ```
263
+
264
+ Never use this whole-branch operation for an existing locale. Patch the required leaf key instead,
265
+ or use `update-item-translation`, so sibling translations are preserved.
266
+
267
+ Add a form field and matching label in one proposal:
268
+
269
+ - Patch the form item config fieldGroups array.
270
+ - Patch `/translations/<locale>/itemTranslations/<itemId>/fields.<fieldId>.label`. This is REQUIRED, not optional — a field with no label translation shows no visible label to respondents.
271
+ - Include any placeholder, unit, option labels, or validation messages that the new field needs.
272
+
273
+ Survey-level translation branches:
274
+
275
+ - `/translations/<locale>/surveyCardContent` stores survey-card copy such as title/description content.
276
+ - `/translations/<locale>/navigationContent` stores participant navigation labels.
277
+ - `/translations/<locale>/validationMessages` stores survey-level validation copy such as `invalidResponse`.
278
+ - Use `survey-json-patch` for these branches today.
279
+
280
+ Add an embedded input to an existing choice option:
281
+
282
+ - Inspect the choice item with `survey_inspect action=raw-json` and use its returned `/surveyItems/<choiceIndex>` path; item-details alone is not sufficient for JSON Pointer construction.
283
+ - Patch the target option object under `/surveyItems/<choiceIndex>/config/options/<optionIndex>/embeddedForm`.
284
+ - Patch `/translations/<locale>/itemTranslations/<choiceItemId>/embeddedForms.<optionId>.fields.<fieldId>.label` with a complete Content value such as `{ "type": "md", "content": "Please specify" }`. This is REQUIRED, not optional — a field with no label translation shows no visible label to respondents (only its raw control type, e.g. "Input"). Never add, replace, or remove the surrounding locale or itemTranslations container; it would discard sibling text.
285
+ - Do not patch `allowOther`; it is not part of the current choice config.
286
+
287
+ Before raw patches, use `survey_inspect` with raw JSON paths for the exact item index and current object shape.
@@ -0,0 +1,11 @@
1
+ # CASE Survey Authoring
2
+
3
+ Use this skill whenever you create, edit, validate, inspect, or explain CASE survey definitions.
4
+
5
+ Core rules:
6
+ - Static survey knowledge comes from this skill. Do not inspect an existing survey merely to learn the data model.
7
+ - Live survey state still comes from tools. Inspect the current context, item details, raw paths, locales, and response slots before editing existing content.
8
+ - For long surveys, do not rely on the compact outline alone. Use survey_inspect action=current-context with scope=outline, cursor/limit, and outlineMode=full for survey-wide order; outlineMode=children with itemRef for a specific parent; search-items for text lookup. Never guess append indexes or references from an outline that says it is truncated.
9
+ - Use typed `survey_change` operations for common edits and `survey_expression` for expressions on existing state. Use raw JSON Patch only for assistant-editable fields not covered by either specialized path.
10
+ - Always validate/propose edits through `survey_change` or `survey_expression` prepare mode. A validated candidate is not proof of browser application; never present rejected, superseded, or incomplete work as completed.
11
+ - Load only the reference you need: survey-data-model.md, source-material-surveys.md, localization.md, rich-text-content.md, item-types.md, embedded-forms.md, expressions.md, or assistant-operations.md.
@@ -0,0 +1,80 @@
1
+ # Choice Option Embedded Forms
2
+
3
+ Use this reference when a choice option needs an inline input such as “Other, please specify”,
4
+ “Yes, explain”, or an option-specific text, number, date, or select field.
5
+
6
+ ## Tool And Transaction Choice
7
+
8
+ - There is no `allowOther` flag. The option config field is `embeddedForm`.
9
+ - For a newly created choice item, put the option and its `embeddedForm` directly in the same
10
+ `create-item` operation, with every required translation in `create-item.translations`.
11
+ - For an existing choice item, inspect the exact item and option with `survey_inspect`, including
12
+ its survey-root raw path. Then make one `survey_change` call containing exactly one
13
+ `survey-json-patch` operation. Put the config patch and every translation patch together in that
14
+ operation's single plural `patches` array.
15
+ - Never combine a `survey-json-patch` operation with typed translation operations in the same
16
+ proposal. A raw patch must be the only operation.
17
+
18
+ ## Exact Config Shape
19
+
20
+ The config key is singular `embeddedForm`, but translation content-key prefixes are plural
21
+ `embeddedForms.<optionId>`. Do not interchange them.
22
+
23
+ `embeddedForm` is a flat object containing `id` and `fieldGroups`. Fields belong inside each field
24
+ group; there is no `itemType`, `config`, or top-level `fields` wrapper.
25
+
26
+ ```json
27
+ {
28
+ "id": "other",
29
+ "key": "other",
30
+ "embeddedForm": {
31
+ "id": "other",
32
+ "fieldGroups": [
33
+ {
34
+ "id": "other_details",
35
+ "layout": { "base": 1, "xs": 1, "sm": 1, "md": 1, "xl": 1 },
36
+ "fields": [
37
+ {
38
+ "id": "other_text",
39
+ "key": "other_text",
40
+ "type": "input",
41
+ "required": true,
42
+ "controller": { "type": "input", "inputMode": "text" },
43
+ "validations": []
44
+ }
45
+ ]
46
+ }
47
+ ]
48
+ }
49
+ }
50
+ ```
51
+
52
+ Use an explicit, stable option id. The config option id, translation prefix, and embedded response
53
+ reference must agree exactly.
54
+
55
+ ## Required Content
56
+
57
+ Every embedded field requires a visible label translation in the same atomic proposal:
58
+
59
+ - option label: `options.other.label`;
60
+ - field label: `embeddedForms.other.fields.other_text.label`;
61
+ - optional placeholder: `embeddedForms.other.fields.other_text.placeholder`;
62
+ - validation message, when configured:
63
+ `embeddedForms.other.fields.other_text.validations.<validationId>.message`;
64
+ - select option label, when applicable:
65
+ `embeddedForms.other.fields.other_text.options.<selectOptionId>.label`.
66
+
67
+ Do not duplicate completion wording in both the option and field labels. For “Other, please
68
+ specify”, use option label “Other” and field label “Please specify”.
69
+
70
+ ## Existing-Item Raw Patch Checklist
71
+
72
+ 1. Inspect the existing choice item and identify the exact option index/id and item JSON Pointer.
73
+ 2. Add or replace `<itemPath>/config/options/<optionIndex>/embeddedForm`.
74
+ 3. Add each required dotted content-key leaf under
75
+ `/translations/<locale>/itemTranslations/<itemId>/...`.
76
+ 4. Preserve the existing locale and item-translation containers; never replace either whole branch.
77
+ 5. Submit all entries as one `survey-json-patch` operation in one `survey_change` call.
78
+
79
+ Embedded response slots use `embedded.<optionId>.<fieldKey>`, for example
80
+ `embedded.other.other_text`.