@case-framework/survey-assistant 0.6.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 (135) hide show
  1. package/README.md +67 -7
  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-CW38pYzK.mjs → authoring-references-DbuOKerg.mjs} +13 -4
  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-DCMA71PP.mjs +58 -0
  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 -414
  13. package/dist/{constants-BMGTgfjv.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-DMTqer6T.mjs → controller-proxy-DEeFl9IA.mjs} +29 -26
  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-CQwOAcwK.mjs → engine-nXoCWYpQ.mjs} +2821 -2018
  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-D6nqkz0O.d.mts → index-Ciq7vHIs.d.mts} +121 -197
  34. package/dist/index-Ciq7vHIs.d.mts.map +1 -0
  35. package/dist/{index-DvTqML-0.d.mts → index-D7wT6P3t.d.mts} +294 -63
  36. package/dist/index-D7wT6P3t.d.mts.map +1 -0
  37. package/dist/index-WB5kfnz0.d.mts +348 -0
  38. package/dist/index-WB5kfnz0.d.mts.map +1 -0
  39. package/dist/{index-B0lxsAzx.d.mts → index-r5riv4md.d.mts} +50 -29
  40. package/dist/index-r5riv4md.d.mts.map +1 -0
  41. package/dist/{lifecycle-BmGAIUlv.d.mts → lifecycle-Dm8u7QFh.d.mts} +40 -18
  42. package/dist/lifecycle-Dm8u7QFh.d.mts.map +1 -0
  43. package/dist/{memory-thread-repository-CjFGVNZh.mjs → memory-thread-repository-DAnOGIB3.mjs} +89 -34
  44. package/dist/memory-thread-repository-DAnOGIB3.mjs.map +1 -0
  45. package/dist/{protocol-DJUP4gc0.mjs → protocol-CF4Bh_Ch.mjs} +182 -50
  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-C_GY-Fsc.mjs +1727 -0
  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 +143 -68
  56. package/dist/references/core-rules.md +6 -3
  57. package/dist/references/embedded-forms.md +34 -14
  58. package/dist/references/expressions.md +238 -48
  59. package/dist/references/fair-by-design.md +9 -9
  60. package/dist/references/item-types.md +306 -72
  61. package/dist/references/localization.md +5 -4
  62. package/dist/references/response-variables.md +38 -0
  63. package/dist/references/rich-text-content.md +60 -27
  64. package/dist/references/source-material-surveys.md +13 -5
  65. package/dist/references/survey-data-model.md +32 -9
  66. package/dist/{request-context-BjTHNcDd.mjs → request-context-Dg4jSwN1.mjs} +4 -10
  67. package/dist/request-context-Dg4jSwN1.mjs.map +1 -0
  68. package/dist/{request-guardrails-TmgQtqp4.mjs → request-guardrails-C0ugBOp0.mjs} +6 -5
  69. package/dist/request-guardrails-C0ugBOp0.mjs.map +1 -0
  70. package/dist/server-agent.d.mts +53 -44
  71. package/dist/server-agent.d.mts.map +1 -1
  72. package/dist/server-agent.mjs +48 -27
  73. package/dist/server-agent.mjs.map +1 -1
  74. package/dist/server-runtime.d.mts +36 -49
  75. package/dist/server-runtime.d.mts.map +1 -1
  76. package/dist/server-runtime.mjs +17 -12
  77. package/dist/server-runtime.mjs.map +1 -1
  78. package/dist/server-tasks.d.mts +2 -2
  79. package/dist/server-tasks.mjs +2 -313
  80. package/dist/server-tools.d.mts +1 -1
  81. package/dist/server-tools.mjs +1 -1
  82. package/dist/server.d.mts +13 -36
  83. package/dist/server.d.mts.map +1 -1
  84. package/dist/server.mjs +5 -5
  85. package/dist/server.mjs.map +1 -1
  86. package/dist/storage-postgres.d.mts +9 -25
  87. package/dist/storage-postgres.d.mts.map +1 -1
  88. package/dist/storage-postgres.mjs +5 -4
  89. package/dist/storage-postgres.mjs.map +1 -1
  90. package/dist/{tasks-DxzD25Zc.d.mts → tasks-B9h3LPam.d.mts} +19 -35
  91. package/dist/tasks-B9h3LPam.d.mts.map +1 -0
  92. package/dist/tasks-BZEiwgxA.mjs +396 -0
  93. package/dist/tasks-BZEiwgxA.mjs.map +1 -0
  94. package/dist/thread-documents-DrHbaXqP.d.mts +119 -0
  95. package/dist/thread-documents-DrHbaXqP.d.mts.map +1 -0
  96. package/dist/{thread-handlers-AbQbt4sX.d.mts → thread-handlers-Bef0td9N.d.mts} +10 -40
  97. package/dist/thread-handlers-Bef0td9N.d.mts.map +1 -0
  98. package/dist/{tools-CFh35R4d.mjs → tools-CEsv-WoO.mjs} +705 -578
  99. package/dist/tools-CEsv-WoO.mjs.map +1 -0
  100. package/dist/ui.css +1 -1
  101. package/dist/ui.d.mts +17 -55
  102. package/dist/ui.d.mts.map +1 -1
  103. package/dist/ui.mjs +759 -171
  104. package/dist/ui.mjs.map +1 -1
  105. package/docs/integration.md +51 -1
  106. package/package.json +49 -42
  107. package/dist/authoring-references-CW38pYzK.mjs.map +0 -1
  108. package/dist/capabilities-DVTfogjv.d.mts.map +0 -1
  109. package/dist/capabilities-default.mjs.map +0 -1
  110. package/dist/constants-BMGTgfjv.d.mts.map +0 -1
  111. package/dist/controller-proxy-DMTqer6T.mjs.map +0 -1
  112. package/dist/controller-proxy-mRvez2qw.d.mts +0 -85
  113. package/dist/controller-proxy-mRvez2qw.d.mts.map +0 -1
  114. package/dist/digest-SJmoYVaK.d.mts.map +0 -1
  115. package/dist/engine-CQwOAcwK.mjs.map +0 -1
  116. package/dist/form-authoring-kQDgC5oz.mjs +0 -155
  117. package/dist/form-authoring-kQDgC5oz.mjs.map +0 -1
  118. package/dist/index-B0lxsAzx.d.mts.map +0 -1
  119. package/dist/index-BS-Xjk1h.d.mts +0 -224
  120. package/dist/index-BS-Xjk1h.d.mts.map +0 -1
  121. package/dist/index-D6nqkz0O.d.mts.map +0 -1
  122. package/dist/index-DvTqML-0.d.mts.map +0 -1
  123. package/dist/lifecycle-BmGAIUlv.d.mts.map +0 -1
  124. package/dist/memory-thread-repository-CjFGVNZh.mjs.map +0 -1
  125. package/dist/protocol-DJUP4gc0.mjs.map +0 -1
  126. package/dist/react-Dqlmasrs.mjs +0 -768
  127. package/dist/react-Dqlmasrs.mjs.map +0 -1
  128. package/dist/request-context-BjTHNcDd.mjs.map +0 -1
  129. package/dist/request-guardrails-TmgQtqp4.mjs.map +0 -1
  130. package/dist/server-tasks.mjs.map +0 -1
  131. package/dist/tasks-DxzD25Zc.d.mts.map +0 -1
  132. package/dist/thread-documents-rmd6nyX9.d.mts +0 -107
  133. package/dist/thread-documents-rmd6nyX9.d.mts.map +0 -1
  134. package/dist/thread-handlers-AbQbt4sX.d.mts.map +0 -1
  135. package/dist/tools-CFh35R4d.mjs.map +0 -1
@@ -16,7 +16,7 @@ the latest cumulative change set for browser apply.
16
16
  6. Use `survey-json-patch` through `survey_change` only for assistant-editable fields not covered by
17
17
  typed or expression operations.
18
18
  7. Mutation calls submit deltas to one request-scoped draft. Each successful call automatically accumulates with earlier successful calls, and later calls may target ids or assets introduced by the draft.
19
- 8. A rejected delta leaves the validated draft unchanged and blocks turn authorization. Correct the same target in another mutation call; an unrelated valid edit does not clear the block. Successful turn completion authorizes only the latest cumulative draft for one editor apply.
19
+ 8. A rejected delta leaves the validated draft unchanged and blocks turn authorization. Correct every rejected operation and surface; unrelated valid edits do not clear the block. `pendingRepairs` reports outstanding obligations after each mutation. When `requiresExplicitRetry` is true, supply the rejected change id in `retryOfChangeIds` with the complete corrected scope. Correct unavailable or invalid target identities. Only when `allowsAcknowledgment` is true, inspect the validator-confirmed no-op and acknowledge it with `survey_change` using `operations: []` and `retryOfChangeIds`; do not invent an unrelated edit. Acknowledgments may be batched and cannot discharge identifiable pending edits. Malformed operations, invalid targets, and failed creations require a linked non-empty corrected retry. An ordinary empty call is invalid and must also be reconciled by a linked non-empty retry. Successful completion authorizes only the latest cumulative draft for one editor apply; acknowledgment alone never creates an empty editor commit.
20
20
 
21
21
  ## `survey_change` Typed Operations
22
22
 
@@ -25,16 +25,22 @@ For the canonical fields of one operation, call `survey_authoring_reference` aga
25
25
  from the same validator used by `survey_change`, so this reference does not duplicate a static
26
26
  operation catalog.
27
27
 
28
+ Generated operation schemas describe the canonical post-normalization contract. The model-facing
29
+ `survey_change` input also accepts the compact `richTextBlocks` shorthand documented in
30
+ `rich-text-content` wherever a typed translation accepts structured `content`; it is normalized to
31
+ canonical `content` before these schemas run. Never combine shorthand with `plainText` or `content`
32
+ in the same translation.
33
+
28
34
  `create-item`
29
35
 
30
36
  - Creates a new item under `target.parentItemId`.
31
- - Use root group id/key for top-level items.
37
+ - Use exact root group ID for top-level items.
32
38
  - 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, prefer putting the `richText` value with the image block directly in `create-item.translations[].content`.
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
+ - 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.
35
41
  - Use the actual content locale in `translations[].locale`. Missing locales are introduced when translations are applied.
36
42
  - Put item configuration inside `item.config`, not beside `item`.
37
- - 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.
38
44
  - Use explicit item ids when you need to reference the new item in the same proposal.
39
45
 
40
46
  Minimal valid examples:
@@ -42,10 +48,11 @@ Minimal valid examples:
42
48
  ```json
43
49
  {
44
50
  "kind": "create-item",
45
- "target": { "parentItemId": "<root-group-id-or-key>" },
51
+ "target": {
52
+ "parentItemId": "<root-group-id>"
53
+ },
46
54
  "item": {
47
55
  "id": "intro_privacy",
48
- "key": "intro_privacy",
49
56
  "itemType": "infoItem",
50
57
  "config": {}
51
58
  },
@@ -62,22 +69,39 @@ Minimal valid examples:
62
69
  ```json
63
70
  {
64
71
  "kind": "create-item",
65
- "target": { "parentItemId": "<root-group-id-or-key>" },
72
+ "target": {
73
+ "parentItemId": "<root-group-id>"
74
+ },
66
75
  "item": {
67
76
  "id": "overall_satisfaction",
68
- "key": "overall_satisfaction",
69
77
  "itemType": "choiceItem",
70
78
  "config": {
71
79
  "id": "overall_satisfaction",
72
80
  "maxSelection": 1,
73
81
  "shuffleOptions": false,
74
82
  "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
- ]
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"
81
105
  }
82
106
  },
83
107
  "translations": [
@@ -91,10 +115,26 @@ Minimal valid examples:
91
115
  "contentKey": "options.very_dissatisfied.label",
92
116
  "plainText": "Very dissatisfied"
93
117
  },
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" }
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
+ }
98
138
  ]
99
139
  }
100
140
  ```
@@ -102,10 +142,11 @@ Minimal valid examples:
102
142
  ```json
103
143
  {
104
144
  "kind": "create-item",
105
- "target": { "parentItemId": "<root-group-id-or-key>" },
145
+ "target": {
146
+ "parentItemId": "<root-group-id>"
147
+ },
106
148
  "item": {
107
149
  "id": "personal_data",
108
- "key": "personal_data",
109
150
  "itemType": "formItem",
110
151
  "config": {
111
152
  "id": "personal_data",
@@ -113,15 +154,29 @@ Minimal valid examples:
113
154
  "fieldGroups": [
114
155
  {
115
156
  "id": "main",
116
- "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
+ },
117
164
  "fields": [
118
165
  {
119
166
  "id": "email",
120
- "key": "EMAIL",
121
167
  "type": "input",
122
168
  "required": false,
123
- "controller": { "type": "input", "inputMode": "email" },
124
- "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"
125
180
  }
126
181
  ]
127
182
  }
@@ -129,15 +184,24 @@ Minimal valid examples:
129
184
  }
130
185
  },
131
186
  "translations": [
132
- { "locale": "en", "contentKey": "title", "plainText": "Personal information" },
133
- { "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
+ }
134
197
  ]
135
198
  }
136
199
  ```
137
200
 
138
- `update-item-key`
201
+ `update-response-settings`
139
202
 
140
- - 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.
141
205
 
142
206
  `update-item-label`
143
207
 
@@ -155,7 +219,7 @@ Minimal valid examples:
155
219
  - Use this with a new locale code when adding translated content for a locale that is not yet present.
156
220
  - Use for titles, subtitles, top/bottom content, card footer, info content, and simple field/option labels when the key is known.
157
221
  - Use `plainText` for simple unformatted text.
158
- - Use `content` for structured survey content. For formatted text, set `content.type` to `richText`; do not use markdown syntax to request formatting.
222
+ - For a complete formatted replacement, prefer top-level `richTextBlocks`; it normalizes to canonical `content`. Use canonical `content` directly when needed. Do not use markdown syntax to request formatting.
159
223
 
160
224
  `update-placed-image`
161
225
 
@@ -176,18 +240,40 @@ Minimal valid examples:
176
240
  `add-choice-option`, `update-choice-option-label`, `remove-choice-option`, `reorder-choice-options`
177
241
 
178
242
  - Use for ordinary choice option edits on existing choice items.
179
- - For option identifiers, prefer exact id; validation can also normalize key or visible label.
180
- - 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.
181
245
 
182
246
  `upsert-form-field-group`, `remove-form-field-group`, `reorder-form-field-groups`, `upsert-form-field`, `remove-form-field`, `move-form-field`
183
247
 
184
248
  - Target a top-level form with `{ "itemId": "<form-id>" }` or a choice option's form with `{ "itemId": "<choice-id>", "optionId": "<option-id>" }`.
185
249
  - Add visible labels, placeholders, option labels, and validation messages in the operation's `translations` array so structure and content remain atomic.
186
250
  - Group and field identifiers are stable ids; reorder operations require the complete current id set.
251
+ - To update only an existing field group's responsive layout, use `upsert-form-field-group` with the existing group id and a `group` containing `id` and `layout`. Omitted fields and other existing group properties are preserved.
187
252
  - Simple forms contain one group and one field. Compatible updates to that same field may remain
188
253
  simple; structural changes automatically make the form custom. This applies to top-level and
189
254
  embedded forms and is intentionally one-way.
190
255
 
256
+ Update an existing form field group's layout without replacing its fields:
257
+
258
+ ```json
259
+ {
260
+ "kind": "upsert-form-field-group",
261
+ "target": {
262
+ "itemId": "profile"
263
+ },
264
+ "group": {
265
+ "id": "main",
266
+ "layout": {
267
+ "base": 1,
268
+ "xs": 1,
269
+ "sm": 2,
270
+ "md": 2,
271
+ "xl": 3
272
+ }
273
+ }
274
+ }
275
+ ```
276
+
191
277
  `set-embedded-form`, `remove-embedded-form`
192
278
 
193
279
  - Add or remove a choice option's inline form using item and option ids, without raw array-index patches.
@@ -198,7 +284,10 @@ Minimal valid examples:
198
284
 
199
285
  `move-item`, `reorder-items`
200
286
 
201
- - 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.
202
291
  - The validator rejects root moves, cycles, missing children, duplicates, and incomplete reorder lists.
203
292
 
204
293
  `update-survey-metadata`, `update-survey-pagination`, `update-survey-translation`, `add-survey-locale`, `remove-survey-locale`
@@ -231,53 +320,39 @@ Minimal valid examples:
231
320
  validation messages are ordinary config/content, not expression mutations.
232
321
  - Use it for field controller internals and any existing custom-registry item config that is not covered by a typed helper.
233
322
 
234
- ## Raw Patch Examples
323
+ ## Raw Patch Example
235
324
 
236
- Update a form field-group layout:
325
+ Use a raw patch only for an existing, inspected survey-root extension that has no typed operation,
326
+ `patch-item-config` helper, or `survey_expression` mutation. Obtain the exact path and current value
327
+ from `survey_inspect` first. A guarded replacement has this shape:
237
328
 
238
329
  ```json
239
330
  {
240
331
  "kind": "survey-json-patch",
241
332
  "patches": [
333
+ {
334
+ "op": "test",
335
+ "path": "/<inspected-extension>/<leaf>",
336
+ "value": "<current-value>"
337
+ },
242
338
  {
243
339
  "op": "replace",
244
- "path": "/surveyItems/3/config/fieldGroups/0/layout",
245
- "value": { "base": 1, "xs": 1, "sm": 2, "md": 2, "xl": 3 }
340
+ "path": "/<inspected-extension>/<leaf>",
341
+ "value": "<requested-value>"
246
342
  }
247
343
  ]
248
344
  }
249
345
  ```
250
346
 
251
- Only when the user explicitly requests an empty registration, add a missing locale shell after inspection confirms that the locale does not exist:
252
-
253
- ```json
254
- {
255
- "kind": "add-survey-locale",
256
- "locale": "nl"
257
- }
258
- ```
259
-
260
- Never use this whole-branch operation for an existing locale. Patch the required leaf key instead,
261
- or use `update-item-translation`, so sibling translations are preserved.
262
-
263
- Add a form field and matching label in one proposal:
264
-
265
- - Patch the form item config fieldGroups array.
266
- - 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.
267
- - Include any placeholder, unit, option labels, or validation messages that the new field needs.
268
-
269
- Survey-level translation branches:
270
-
271
- - `/translations/<locale>/surveyCardContent` stores survey-card copy such as title/description content.
272
- - `/translations/<locale>/navigationContent` stores participant navigation labels.
273
- - `/translations/<locale>/validationMessages` stores survey-level validation copy such as `invalidResponse`.
274
- - Use `survey-json-patch` for these branches today.
275
-
276
- Add an embedded input to an existing choice option:
347
+ The placeholders describe the shape only; never send them literally or invent an extension path.
348
+ Keep every related raw edit in the same `patches` array, and keep this `survey-json-patch` operation
349
+ as the proposal's only operation.
277
350
 
278
- - 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.
279
- - Patch the target option object under `/surveyItems/<choiceIndex>/config/options/<optionIndex>/embeddedForm`.
280
- - 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.
281
- - Do not patch `allowOther`; it is not part of the current choice config.
351
+ ### Raw Patch Rules
282
352
 
283
- Before raw patches, use `survey_inspect` with raw JSON paths for the exact item index and current object shape.
353
+ - Paths are survey-root JSON Pointers from current raw inspection. Each `/` separates path segments; within a segment, encode `~` as `~0` and `/` as `~1`.
354
+ - Patches run sequentially, so each operation sees the result of the previous operations. Parent paths must already exist; create missing containers explicitly in dependency order when the inspected extension contract permits them.
355
+ - Use `add` to create or replace an object member, insert before an existing array index, or append to an array with `/-`. Use `replace`, `remove`, or `test` only when the target path already exists.
356
+ - `move` and `copy` require an existing `from` path and use `path` as an add destination. A move removes the source before resolving the destination, so later array indexes refer to the already-modified array.
357
+ - A `test` compares the complete JSON value and type. Preserve booleans, numbers, `null`, arrays, and objects as those JSON types rather than converting them to strings. Put a matching `test` immediately before an overwrite, removal, or move when current inspection provides the expected value.
358
+ - Never patch `/$schema`, translation roots or whole existing locale branches, expression-owned paths, or another protected location rejected by the validator. Use the specialized operation or tool named by validation instead.
@@ -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.