@case-framework/survey-assistant 0.7.0 → 0.8.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 (130) hide show
  1. package/README.md +58 -3
  2. package/dist/{attachments-B0hg3vpT.mjs → attachments-BuNni5vB.mjs} +1 -1
  3. package/dist/{attachments-B0hg3vpT.mjs.map → attachments-BuNni5vB.mjs.map} +1 -1
  4. package/dist/{authoring-references-BJQ_1nMX.mjs → authoring-references-DbuOKerg.mjs} +12 -3
  5. package/dist/authoring-references-DbuOKerg.mjs.map +1 -0
  6. package/dist/{capabilities-DVTfogjv.d.mts → capabilities-CmeeAjEc.d.mts} +3 -4
  7. package/dist/capabilities-CmeeAjEc.d.mts.map +1 -0
  8. package/dist/{capabilities-1KNnNO1_.mjs → capabilities-DCMA71PP.mjs} +4 -19
  9. package/dist/capabilities-DCMA71PP.mjs.map +1 -0
  10. package/dist/capabilities-default.d.mts +13 -12
  11. package/dist/capabilities-default.d.mts.map +1 -1
  12. package/dist/capabilities-default.mjs +2 -411
  13. package/dist/{constants-Be91Gay9.d.mts → constants-B6HzpEsx.d.mts} +7 -28
  14. package/dist/constants-B6HzpEsx.d.mts.map +1 -0
  15. package/dist/{constants-0UHGFcRJ.mjs → constants-Ct-vv9cu.mjs} +1 -1
  16. package/dist/{constants-0UHGFcRJ.mjs.map → constants-Ct-vv9cu.mjs.map} +1 -1
  17. package/dist/controller-proxy-D7uQfm5f.d.mts +65 -0
  18. package/dist/controller-proxy-D7uQfm5f.d.mts.map +1 -0
  19. package/dist/{controller-proxy-BH05e-C2.mjs → controller-proxy-DEeFl9IA.mjs} +10 -20
  20. package/dist/controller-proxy-DEeFl9IA.mjs.map +1 -0
  21. package/dist/default-fhSon2RM.mjs +760 -0
  22. package/dist/default-fhSon2RM.mjs.map +1 -0
  23. package/dist/{digest-SJmoYVaK.d.mts → digest-C39WyAWG.d.mts} +2 -3
  24. package/dist/digest-C39WyAWG.d.mts.map +1 -0
  25. package/dist/digest.d.mts +1 -1
  26. package/dist/digest.mjs.map +1 -1
  27. package/dist/{engine-Bo85Oyj0.mjs → engine-nXoCWYpQ.mjs} +755 -590
  28. package/dist/engine-nXoCWYpQ.mjs.map +1 -0
  29. package/dist/engine.d.mts +3 -3
  30. package/dist/engine.mjs +3 -3
  31. package/dist/{fair-guidance-CRYCRxQm.mjs → fair-guidance-BmO4PswW.mjs} +1 -1
  32. package/dist/{fair-guidance-CRYCRxQm.mjs.map → fair-guidance-BmO4PswW.mjs.map} +1 -1
  33. package/dist/{index-Tnqv-yKW.d.mts → index-Ciq7vHIs.d.mts} +80 -141
  34. package/dist/index-Ciq7vHIs.d.mts.map +1 -0
  35. package/dist/{index-nhrgbgO5.d.mts → index-D7wT6P3t.d.mts} +277 -60
  36. package/dist/index-D7wT6P3t.d.mts.map +1 -0
  37. package/dist/{index-l_EK4yK7.d.mts → index-WB5kfnz0.d.mts} +15 -71
  38. package/dist/index-WB5kfnz0.d.mts.map +1 -0
  39. package/dist/{index-x0qprHgK.d.mts → index-r5riv4md.d.mts} +33 -24
  40. package/dist/index-r5riv4md.d.mts.map +1 -0
  41. package/dist/{lifecycle-Bx0aq_4S.d.mts → lifecycle-Dm8u7QFh.d.mts} +34 -18
  42. package/dist/lifecycle-Dm8u7QFh.d.mts.map +1 -0
  43. package/dist/{memory-thread-repository-KWHQWFEW.mjs → memory-thread-repository-DAnOGIB3.mjs} +51 -27
  44. package/dist/memory-thread-repository-DAnOGIB3.mjs.map +1 -0
  45. package/dist/{protocol-BhzQbx81.mjs → protocol-CF4Bh_Ch.mjs} +118 -36
  46. package/dist/protocol-CF4Bh_Ch.mjs.map +1 -0
  47. package/dist/protocol.d.mts +4 -4
  48. package/dist/protocol.mjs +3 -3
  49. package/dist/{react-B3ybjJ-X.mjs → react-C_GY-Fsc.mjs} +146 -39
  50. package/dist/react-C_GY-Fsc.mjs.map +1 -0
  51. package/dist/react-integration.d.mts +2 -2
  52. package/dist/react-integration.mjs +2 -2
  53. package/dist/react.d.mts +3 -3
  54. package/dist/react.mjs +3 -3
  55. package/dist/references/assistant-operations.md +100 -31
  56. package/dist/references/core-rules.md +6 -3
  57. package/dist/references/embedded-forms.md +34 -14
  58. package/dist/references/expressions.md +220 -58
  59. package/dist/references/fair-by-design.md +9 -9
  60. package/dist/references/item-types.md +305 -71
  61. package/dist/references/response-variables.md +38 -0
  62. package/dist/references/source-material-surveys.md +4 -3
  63. package/dist/references/survey-data-model.md +23 -8
  64. package/dist/{request-context-CG4SWijt.mjs → request-context-Dg4jSwN1.mjs} +4 -10
  65. package/dist/request-context-Dg4jSwN1.mjs.map +1 -0
  66. package/dist/{request-guardrails-TmgQtqp4.mjs → request-guardrails-C0ugBOp0.mjs} +6 -5
  67. package/dist/request-guardrails-C0ugBOp0.mjs.map +1 -0
  68. package/dist/server-agent.d.mts +37 -39
  69. package/dist/server-agent.d.mts.map +1 -1
  70. package/dist/server-agent.mjs +45 -20
  71. package/dist/server-agent.mjs.map +1 -1
  72. package/dist/server-runtime.d.mts +23 -42
  73. package/dist/server-runtime.d.mts.map +1 -1
  74. package/dist/server-runtime.mjs +12 -12
  75. package/dist/server-runtime.mjs.map +1 -1
  76. package/dist/server-tasks.d.mts +2 -2
  77. package/dist/server-tasks.mjs +2 -313
  78. package/dist/server-tools.d.mts +1 -1
  79. package/dist/server-tools.mjs +1 -1
  80. package/dist/server.d.mts +13 -36
  81. package/dist/server.d.mts.map +1 -1
  82. package/dist/server.mjs +5 -5
  83. package/dist/server.mjs.map +1 -1
  84. package/dist/storage-postgres.d.mts +9 -25
  85. package/dist/storage-postgres.d.mts.map +1 -1
  86. package/dist/storage-postgres.mjs +5 -4
  87. package/dist/storage-postgres.mjs.map +1 -1
  88. package/dist/{tasks-CYqrcaRO.d.mts → tasks-B9h3LPam.d.mts} +18 -34
  89. package/dist/tasks-B9h3LPam.d.mts.map +1 -0
  90. package/dist/tasks-BZEiwgxA.mjs +396 -0
  91. package/dist/tasks-BZEiwgxA.mjs.map +1 -0
  92. package/dist/thread-documents-DrHbaXqP.d.mts +119 -0
  93. package/dist/thread-documents-DrHbaXqP.d.mts.map +1 -0
  94. package/dist/{thread-handlers-DBAaU7BA.d.mts → thread-handlers-Bef0td9N.d.mts} +8 -40
  95. package/dist/thread-handlers-Bef0td9N.d.mts.map +1 -0
  96. package/dist/{tools-BvSovehT.mjs → tools-CEsv-WoO.mjs} +296 -105
  97. package/dist/tools-CEsv-WoO.mjs.map +1 -0
  98. package/dist/ui.css +1 -1
  99. package/dist/ui.d.mts +15 -54
  100. package/dist/ui.d.mts.map +1 -1
  101. package/dist/ui.mjs +172 -137
  102. package/dist/ui.mjs.map +1 -1
  103. package/docs/integration.md +11 -1
  104. package/package.json +40 -35
  105. package/dist/authoring-references-BJQ_1nMX.mjs.map +0 -1
  106. package/dist/capabilities-1KNnNO1_.mjs.map +0 -1
  107. package/dist/capabilities-DVTfogjv.d.mts.map +0 -1
  108. package/dist/capabilities-default.mjs.map +0 -1
  109. package/dist/constants-Be91Gay9.d.mts.map +0 -1
  110. package/dist/controller-proxy-BH05e-C2.mjs.map +0 -1
  111. package/dist/controller-proxy-Bd0ZHBPF.d.mts +0 -85
  112. package/dist/controller-proxy-Bd0ZHBPF.d.mts.map +0 -1
  113. package/dist/digest-SJmoYVaK.d.mts.map +0 -1
  114. package/dist/engine-Bo85Oyj0.mjs.map +0 -1
  115. package/dist/index-Tnqv-yKW.d.mts.map +0 -1
  116. package/dist/index-l_EK4yK7.d.mts.map +0 -1
  117. package/dist/index-nhrgbgO5.d.mts.map +0 -1
  118. package/dist/index-x0qprHgK.d.mts.map +0 -1
  119. package/dist/lifecycle-Bx0aq_4S.d.mts.map +0 -1
  120. package/dist/memory-thread-repository-KWHQWFEW.mjs.map +0 -1
  121. package/dist/protocol-BhzQbx81.mjs.map +0 -1
  122. package/dist/react-B3ybjJ-X.mjs.map +0 -1
  123. package/dist/request-context-CG4SWijt.mjs.map +0 -1
  124. package/dist/request-guardrails-TmgQtqp4.mjs.map +0 -1
  125. package/dist/server-tasks.mjs.map +0 -1
  126. package/dist/tasks-CYqrcaRO.d.mts.map +0 -1
  127. package/dist/thread-documents-rmd6nyX9.d.mts +0 -107
  128. package/dist/thread-documents-rmd6nyX9.d.mts.map +0 -1
  129. package/dist/thread-handlers-DBAaU7BA.d.mts.map +0 -1
  130. package/dist/tools-BvSovehT.mjs.map +0 -1
@@ -34,13 +34,13 @@ in the same translation.
34
34
  `create-item`
35
35
 
36
36
  - Creates a new item under `target.parentItemId`.
37
- - Use root group id/key for top-level items.
37
+ - Use exact root group ID for top-level items.
38
38
  - Put visible text in top-level `translations`, not inside `item`.
39
39
  - Each canonical translation contains exactly one of `plainText` or a complete structured `content` value. Model-authored `survey_change` input may use `richTextBlocks` instead as described above.
40
40
  - When a new item must contain an image, prefer `richTextBlocks` and place the canonical image block inside that array. Use canonical `content` for the whole translation only when the compact interface is unsuitable.
41
41
  - Use the actual content locale in `translations[].locale`. Missing locales are introduced when translations are applied.
42
42
  - Put item configuration inside `item.config`, not beside `item`.
43
- - Item key must be unique among siblings.
43
+ - IDs must be unique. Duplicate editor names are allowed; response variable names must be unique survey-wide.
44
44
  - Use explicit item ids when you need to reference the new item in the same proposal.
45
45
 
46
46
  Minimal valid examples:
@@ -48,10 +48,11 @@ Minimal valid examples:
48
48
  ```json
49
49
  {
50
50
  "kind": "create-item",
51
- "target": { "parentItemId": "<root-group-id-or-key>" },
51
+ "target": {
52
+ "parentItemId": "<root-group-id>"
53
+ },
52
54
  "item": {
53
55
  "id": "intro_privacy",
54
- "key": "intro_privacy",
55
56
  "itemType": "infoItem",
56
57
  "config": {}
57
58
  },
@@ -68,22 +69,39 @@ Minimal valid examples:
68
69
  ```json
69
70
  {
70
71
  "kind": "create-item",
71
- "target": { "parentItemId": "<root-group-id-or-key>" },
72
+ "target": {
73
+ "parentItemId": "<root-group-id>"
74
+ },
72
75
  "item": {
73
76
  "id": "overall_satisfaction",
74
- "key": "overall_satisfaction",
75
77
  "itemType": "choiceItem",
76
78
  "config": {
77
79
  "id": "overall_satisfaction",
78
80
  "maxSelection": 1,
79
81
  "shuffleOptions": false,
80
82
  "options": [
81
- { "id": "very_dissatisfied", "key": "VERY_DISSATISFIED" },
82
- { "id": "dissatisfied", "key": "DISSATISFIED" },
83
- { "id": "neutral", "key": "NEUTRAL" },
84
- { "id": "satisfied", "key": "SATISFIED" },
85
- { "id": "very_satisfied", "key": "VERY_SATISFIED" }
86
- ]
83
+ {
84
+ "id": "very_dissatisfied",
85
+ "code": "VERY_DISSATISFIED"
86
+ },
87
+ {
88
+ "id": "dissatisfied",
89
+ "code": "DISSATISFIED"
90
+ },
91
+ {
92
+ "id": "neutral",
93
+ "code": "NEUTRAL"
94
+ },
95
+ {
96
+ "id": "satisfied",
97
+ "code": "SATISFIED"
98
+ },
99
+ {
100
+ "id": "very_satisfied",
101
+ "code": "VERY_SATISFIED"
102
+ }
103
+ ],
104
+ "variableName": "overall_satisfaction"
87
105
  }
88
106
  },
89
107
  "translations": [
@@ -97,10 +115,26 @@ Minimal valid examples:
97
115
  "contentKey": "options.very_dissatisfied.label",
98
116
  "plainText": "Very dissatisfied"
99
117
  },
100
- { "locale": "en", "contentKey": "options.dissatisfied.label", "plainText": "Dissatisfied" },
101
- { "locale": "en", "contentKey": "options.neutral.label", "plainText": "Neutral" },
102
- { "locale": "en", "contentKey": "options.satisfied.label", "plainText": "Satisfied" },
103
- { "locale": "en", "contentKey": "options.very_satisfied.label", "plainText": "Very satisfied" }
118
+ {
119
+ "locale": "en",
120
+ "contentKey": "options.dissatisfied.label",
121
+ "plainText": "Dissatisfied"
122
+ },
123
+ {
124
+ "locale": "en",
125
+ "contentKey": "options.neutral.label",
126
+ "plainText": "Neutral"
127
+ },
128
+ {
129
+ "locale": "en",
130
+ "contentKey": "options.satisfied.label",
131
+ "plainText": "Satisfied"
132
+ },
133
+ {
134
+ "locale": "en",
135
+ "contentKey": "options.very_satisfied.label",
136
+ "plainText": "Very satisfied"
137
+ }
104
138
  ]
105
139
  }
106
140
  ```
@@ -108,10 +142,11 @@ Minimal valid examples:
108
142
  ```json
109
143
  {
110
144
  "kind": "create-item",
111
- "target": { "parentItemId": "<root-group-id-or-key>" },
145
+ "target": {
146
+ "parentItemId": "<root-group-id>"
147
+ },
112
148
  "item": {
113
149
  "id": "personal_data",
114
- "key": "personal_data",
115
150
  "itemType": "formItem",
116
151
  "config": {
117
152
  "id": "personal_data",
@@ -119,15 +154,29 @@ Minimal valid examples:
119
154
  "fieldGroups": [
120
155
  {
121
156
  "id": "main",
122
- "layout": { "base": 1, "xs": 1, "sm": 1, "md": 1, "xl": 1 },
157
+ "layout": {
158
+ "base": 1,
159
+ "xs": 1,
160
+ "sm": 1,
161
+ "md": 1,
162
+ "xl": 1
163
+ },
123
164
  "fields": [
124
165
  {
125
166
  "id": "email",
126
- "key": "EMAIL",
127
167
  "type": "input",
128
168
  "required": false,
129
- "controller": { "type": "input", "inputMode": "email" },
130
- "validations": [{ "id": "email", "type": "email" }]
169
+ "controller": {
170
+ "type": "input",
171
+ "inputMode": "email"
172
+ },
173
+ "validations": [
174
+ {
175
+ "id": "email",
176
+ "type": "email"
177
+ }
178
+ ],
179
+ "variableName": "email"
131
180
  }
132
181
  ]
133
182
  }
@@ -135,15 +184,24 @@ Minimal valid examples:
135
184
  }
136
185
  },
137
186
  "translations": [
138
- { "locale": "en", "contentKey": "title", "plainText": "Personal information" },
139
- { "locale": "en", "contentKey": "fields.email.label", "plainText": "Email address" }
187
+ {
188
+ "locale": "en",
189
+ "contentKey": "title",
190
+ "plainText": "Personal information"
191
+ },
192
+ {
193
+ "locale": "en",
194
+ "contentKey": "fields.email.label",
195
+ "plainText": "Email address"
196
+ }
140
197
  ]
141
198
  }
142
199
  ```
143
200
 
144
- `update-item-key`
201
+ `update-response-settings`
145
202
 
146
- - Changes item coding key. This affects fullKey paths and response naming.
203
+ - Applies scalar names, matrix naming components, option export codes and slot export defaults atomically. Load response-variables.md for the exact shape.
204
+ - Preserve persistent IDs, expressions, labels and numeric scores. Shared validation rejects invalid names, collisions and direct matrix-cell renames.
147
205
 
148
206
  `update-item-label`
149
207
 
@@ -182,8 +240,8 @@ Minimal valid examples:
182
240
  `add-choice-option`, `update-choice-option-label`, `remove-choice-option`, `reorder-choice-options`
183
241
 
184
242
  - Use for ordinary choice option edits on existing choice items.
185
- - For option identifiers, prefer exact id; validation can also normalize key or visible label.
186
- - 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.
243
+ - Use exact option IDs. Codes and labels are not target aliases.
244
+ - For `add-choice-option`, use `option: { id?, code?, 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.
187
245
 
188
246
  `upsert-form-field-group`, `remove-form-field-group`, `reorder-form-field-groups`, `upsert-form-field`, `remove-form-field`, `move-form-field`
189
247
 
@@ -200,10 +258,18 @@ Update an existing form field group's layout without replacing its fields:
200
258
  ```json
201
259
  {
202
260
  "kind": "upsert-form-field-group",
203
- "target": { "itemId": "profile" },
261
+ "target": {
262
+ "itemId": "profile"
263
+ },
204
264
  "group": {
205
265
  "id": "main",
206
- "layout": { "base": 1, "xs": 1, "sm": 2, "md": 2, "xl": 3 }
266
+ "layout": {
267
+ "base": 1,
268
+ "xs": 1,
269
+ "sm": 2,
270
+ "md": 2,
271
+ "xl": 3
272
+ }
207
273
  }
208
274
  }
209
275
  ```
@@ -218,7 +284,10 @@ Update an existing form field group's layout without replacing its fields:
218
284
 
219
285
  `move-item`, `reorder-items`
220
286
 
221
- - Move an existing item to a group/index or set a group's complete direct-child order.
287
+ - `create-item.target.index` and `move-item.target.index` are zero-based; omission appends and `0` inserts first. Move uses a different parent; change sibling order with `reorder-items`.
288
+ - Reorder shape: `{ kind: "reorder-items", parentItemId, itemIds }`; `parentItemId` is top-level, not inside `target`.
289
+ - Inspect the parent with `survey_inspect` action `current-context`, scopes `["outline"]`, `outlineMode: "children"`, and `itemRef` before reordering. Page until the complete child list is retrieved.
290
+ - Verify position against the returned stored placement or child order. A shuffled group can display a different respondent order.
222
291
  - The validator rejects root moves, cycles, missing children, duplicates, and incomplete reorder lists.
223
292
 
224
293
  `update-survey-metadata`, `update-survey-pagination`, `update-survey-translation`, `add-survey-locale`, `remove-survey-locale`
@@ -3,15 +3,18 @@
3
3
  Use this skill whenever you create, edit, validate, inspect, or explain CASE survey definitions.
4
4
 
5
5
  Core rules:
6
+
6
7
  - Static survey knowledge comes from this skill. Do not inspect an existing survey merely to learn the data model.
7
8
  - Live survey state still comes from tools. Inspect the current context, item details, raw paths, locales, and response slots before editing existing content.
8
9
  - 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
10
  - 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
11
  - 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
- - Treat every identifier as exact data across every item type: item ids/keys, response-slot ids,
12
- option ids/keys, field and group ids/keys, component keys, validation keys, prefill ids, template
12
+ - Treat every identifier as exact data across every item type: item IDs, response-slot IDs and variable names,
13
+ option IDs/codes, field and group IDs, component keys, validation keys, prefill ids, template
13
14
  keys, content keys, and custom-capability identifiers. Never humanize them by changing case,
14
15
  inserting/removing separators, splitting camelCase, or substituting a label/title/name. Visible
15
16
  prose belongs in translations or editor labels. When explaining an existing survey, quote the
16
17
  exact inspected identifier.
17
- - 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.
18
+ - 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, response-variables.md, or assistant-operations.md.
19
+
20
+ Survey schema version 2 separates identity from naming. Use exact item IDs for mutations. Discover items by resolved editor name or breadcrumb with search; duplicated names are legal. Response variables, including embedded inputs and matrix computed slots, come from the shared declarations. Use structured `{slotId,method}` references and `update-response-settings` for coding. Moving, relabeling or translating an item does not change its response identity.
@@ -36,27 +36,36 @@ breakpoint for one full-width field. Supported counts are `1` through `4`.
36
36
  ```json
37
37
  {
38
38
  "id": "other",
39
- "key": "other",
40
39
  "embeddedForm": {
41
40
  "id": "other",
42
41
  "authoringMode": "simple",
43
42
  "fieldGroups": [
44
43
  {
45
44
  "id": "other_details",
46
- "layout": { "base": 1, "xs": 1, "sm": 1, "md": 1, "xl": 1 },
45
+ "layout": {
46
+ "base": 1,
47
+ "xs": 1,
48
+ "sm": 1,
49
+ "md": 1,
50
+ "xl": 1
51
+ },
47
52
  "fields": [
48
53
  {
49
54
  "id": "other_text",
50
- "key": "other_text",
51
55
  "type": "input",
52
56
  "required": true,
53
- "controller": { "type": "input", "inputMode": "text" },
54
- "validations": []
57
+ "controller": {
58
+ "type": "input",
59
+ "inputMode": "text"
60
+ },
61
+ "validations": [],
62
+ "variableName": "other_text"
55
63
  }
56
64
  ]
57
65
  }
58
66
  ]
59
- }
67
+ },
68
+ "code": "other"
60
69
  }
61
70
  ```
62
71
 
@@ -71,19 +80,31 @@ inside a choice option.
71
80
  "fieldGroups": [
72
81
  {
73
82
  "id": "details",
74
- "layout": { "base": 1, "xs": 1, "sm": 1, "md": 1, "xl": 1 },
83
+ "layout": {
84
+ "base": 1,
85
+ "xs": 1,
86
+ "sm": 1,
87
+ "md": 1,
88
+ "xl": 1
89
+ },
75
90
  "fields": [
76
91
  {
77
92
  "id": "contact_name",
78
- "key": "contact_name",
79
93
  "type": "input",
80
- "controller": { "type": "input", "inputMode": "text" }
94
+ "controller": {
95
+ "type": "input",
96
+ "inputMode": "text"
97
+ },
98
+ "variableName": "contact_name"
81
99
  },
82
100
  {
83
101
  "id": "contact_note",
84
- "key": "contact_note",
85
102
  "type": "textarea",
86
- "controller": { "type": "textarea", "rows": 3 }
103
+ "controller": {
104
+ "type": "textarea",
105
+ "rows": 3
106
+ },
107
+ "variableName": "contact_note"
87
108
  }
88
109
  ]
89
110
  }
@@ -94,7 +115,7 @@ inside a choice option.
94
115
  Use an explicit, stable option id. The config option id, translation prefix, and embedded response
95
116
  reference must agree exactly. When editing an existing option, copy its current `id` exactly,
96
117
  including case. The `embeddedForms.<optionId>` translation segment always comes from the containing
97
- choice option's `id`; it does not come from the option `key` or the nested `embeddedForm.id`.
118
+ choice option's `id`; it does not come from the option `code` or the nested `embeddedForm.id`.
98
119
 
99
120
  ## Required Content
100
121
 
@@ -122,5 +143,4 @@ specify”, use option label “Other” and field label “Please specify”.
122
143
  6. Before adding/reordering groups or fields on a simple follow-up, let the typed mutation promote
123
144
  it to custom; do not construct multi-field data with `authoringMode: "simple"`.
124
145
 
125
- Embedded response slots use `embedded.<optionId>.<fieldKey>`, for example
126
- `embedded.other.other_text`.
146
+ Embedded response slots are the persistent field IDs, exactly as declared in the shared slot registry. They are not reconstructed from option IDs or variable names. Every field has a survey-wide unique `variableName`. Inspect declarations before authoring expressions; translations keep the `embeddedForms.<optionId>.fields.<fieldId>` prefix.