@case-framework/survey-assistant 0.8.0 → 0.9.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 (114) hide show
  1. package/dist/{authoring-references-DbuOKerg.mjs → authoring-references-CmwLc_C8.mjs} +40 -32
  2. package/dist/authoring-references-CmwLc_C8.mjs.map +1 -0
  3. package/dist/capabilities-D55dq-fO.mjs +582 -0
  4. package/dist/capabilities-D55dq-fO.mjs.map +1 -0
  5. package/dist/capabilities-default.d.mts +4 -12
  6. package/dist/capabilities-default.d.mts.map +1 -1
  7. package/dist/capabilities-default.mjs +2 -2
  8. package/dist/capabilities-z95e1mti.d.mts +117 -0
  9. package/dist/capabilities-z95e1mti.d.mts.map +1 -0
  10. package/dist/{constants-B6HzpEsx.d.mts → constants-HL_klgJl.d.mts} +4 -4
  11. package/dist/{constants-B6HzpEsx.d.mts.map → constants-HL_klgJl.d.mts.map} +1 -1
  12. package/dist/{controller-proxy-DEeFl9IA.mjs → controller-proxy-BQ1YsVxJ.mjs} +4 -3
  13. package/dist/controller-proxy-BQ1YsVxJ.mjs.map +1 -0
  14. package/dist/{controller-proxy-D7uQfm5f.d.mts → controller-proxy-Cmzdr9tV.d.mts} +4 -4
  15. package/dist/{controller-proxy-D7uQfm5f.d.mts.map → controller-proxy-Cmzdr9tV.d.mts.map} +1 -1
  16. package/dist/default-D22AKTqE.mjs +233 -0
  17. package/dist/default-D22AKTqE.mjs.map +1 -0
  18. package/dist/{digest-C39WyAWG.d.mts → digest-FnTTDBq2.d.mts} +2 -2
  19. package/dist/digest-FnTTDBq2.d.mts.map +1 -0
  20. package/dist/digest.d.mts +1 -1
  21. package/dist/engine-D950TfvC.mjs +3098 -0
  22. package/dist/engine-D950TfvC.mjs.map +1 -0
  23. package/dist/engine.d.mts +4 -3
  24. package/dist/engine.mjs +3 -3
  25. package/dist/{index-WB5kfnz0.d.mts → index-B8TLBd1G.d.mts} +8 -6
  26. package/dist/index-B8TLBd1G.d.mts.map +1 -0
  27. package/dist/index-CaX3hZmr.d.mts +23813 -0
  28. package/dist/index-CaX3hZmr.d.mts.map +1 -0
  29. package/dist/{index-r5riv4md.d.mts → index-CeUlGkoU.d.mts} +27 -6
  30. package/dist/index-CeUlGkoU.d.mts.map +1 -0
  31. package/dist/{index-D7wT6P3t.d.mts → index-DYHdvSE8.d.mts} +83 -41
  32. package/dist/index-DYHdvSE8.d.mts.map +1 -0
  33. package/dist/lifecycle-DFzxVxpq.d.mts +54346 -0
  34. package/dist/lifecycle-DFzxVxpq.d.mts.map +1 -0
  35. package/dist/{memory-thread-repository-DAnOGIB3.mjs → memory-thread-repository-DmLadygY.mjs} +15 -9
  36. package/dist/{memory-thread-repository-DAnOGIB3.mjs.map → memory-thread-repository-DmLadygY.mjs.map} +1 -1
  37. package/dist/protocol-BQ7uIDYT.mjs +569 -0
  38. package/dist/protocol-BQ7uIDYT.mjs.map +1 -0
  39. package/dist/protocol.d.mts +4 -4
  40. package/dist/protocol.mjs +3 -2
  41. package/dist/{react-C_GY-Fsc.mjs → react-DybJ5pWw.mjs} +17 -11
  42. package/dist/react-DybJ5pWw.mjs.map +1 -0
  43. package/dist/react-integration.d.mts +1 -1
  44. package/dist/react-integration.mjs +1 -1
  45. package/dist/react.d.mts +2 -2
  46. package/dist/react.mjs +2 -2
  47. package/dist/references/assistant-operations.md +190 -215
  48. package/dist/references/core-rules.md +2 -2
  49. package/dist/references/element-types.md +189 -0
  50. package/dist/references/expressions.md +215 -238
  51. package/dist/references/follow-ups.md +151 -0
  52. package/dist/references/localization.md +26 -12
  53. package/dist/references/response-variables.md +3 -3
  54. package/dist/references/rich-text-content.md +22 -43
  55. package/dist/references/source-material-surveys.md +20 -25
  56. package/dist/references/survey-data-model.md +36 -117
  57. package/dist/{request-context-Dg4jSwN1.mjs → request-context-KVh8VjgH.mjs} +4 -4
  58. package/dist/request-context-KVh8VjgH.mjs.map +1 -0
  59. package/dist/server-agent.d.mts +28 -7
  60. package/dist/server-agent.d.mts.map +1 -1
  61. package/dist/server-agent.mjs +33 -25
  62. package/dist/server-agent.mjs.map +1 -1
  63. package/dist/server-runtime.d.mts +6 -5
  64. package/dist/server-runtime.d.mts.map +1 -1
  65. package/dist/server-runtime.mjs +3 -3
  66. package/dist/server-tasks.d.mts +1 -1
  67. package/dist/server-tasks.mjs +1 -1
  68. package/dist/server-tools.d.mts +1 -1
  69. package/dist/server-tools.mjs +1 -1
  70. package/dist/server.d.mts +7 -6
  71. package/dist/server.d.mts.map +1 -1
  72. package/dist/server.mjs +3 -3
  73. package/dist/{tasks-BZEiwgxA.mjs → tasks-Coh5N027.mjs} +16 -15
  74. package/dist/tasks-Coh5N027.mjs.map +1 -0
  75. package/dist/{tasks-B9h3LPam.d.mts → tasks-wb9M-bak.d.mts} +11 -11
  76. package/dist/{tasks-B9h3LPam.d.mts.map → tasks-wb9M-bak.d.mts.map} +1 -1
  77. package/dist/{thread-handlers-Bef0td9N.d.mts → thread-handlers-C3Iya92v.d.mts} +6 -5
  78. package/dist/thread-handlers-C3Iya92v.d.mts.map +1 -0
  79. package/dist/{tools-CEsv-WoO.mjs → tools-D0KBDolR.mjs} +435 -408
  80. package/dist/tools-D0KBDolR.mjs.map +1 -0
  81. package/dist/turn-survey-draft-B34lggSG.d.mts +185 -0
  82. package/dist/turn-survey-draft-B34lggSG.d.mts.map +1 -0
  83. package/dist/ui.d.mts +3 -3
  84. package/dist/ui.d.mts.map +1 -1
  85. package/dist/ui.mjs +35 -23
  86. package/dist/ui.mjs.map +1 -1
  87. package/package.json +7 -7
  88. package/dist/authoring-references-DbuOKerg.mjs.map +0 -1
  89. package/dist/capabilities-CmeeAjEc.d.mts +0 -192
  90. package/dist/capabilities-CmeeAjEc.d.mts.map +0 -1
  91. package/dist/capabilities-DCMA71PP.mjs +0 -58
  92. package/dist/capabilities-DCMA71PP.mjs.map +0 -1
  93. package/dist/controller-proxy-DEeFl9IA.mjs.map +0 -1
  94. package/dist/default-fhSon2RM.mjs +0 -760
  95. package/dist/default-fhSon2RM.mjs.map +0 -1
  96. package/dist/digest-C39WyAWG.d.mts.map +0 -1
  97. package/dist/engine-nXoCWYpQ.mjs +0 -6888
  98. package/dist/engine-nXoCWYpQ.mjs.map +0 -1
  99. package/dist/index-Ciq7vHIs.d.mts +0 -716
  100. package/dist/index-Ciq7vHIs.d.mts.map +0 -1
  101. package/dist/index-D7wT6P3t.d.mts.map +0 -1
  102. package/dist/index-WB5kfnz0.d.mts.map +0 -1
  103. package/dist/index-r5riv4md.d.mts.map +0 -1
  104. package/dist/lifecycle-Dm8u7QFh.d.mts +0 -545
  105. package/dist/lifecycle-Dm8u7QFh.d.mts.map +0 -1
  106. package/dist/protocol-CF4Bh_Ch.mjs +0 -1255
  107. package/dist/protocol-CF4Bh_Ch.mjs.map +0 -1
  108. package/dist/react-C_GY-Fsc.mjs.map +0 -1
  109. package/dist/references/embedded-forms.md +0 -146
  110. package/dist/references/item-types.md +0 -641
  111. package/dist/request-context-Dg4jSwN1.mjs.map +0 -1
  112. package/dist/tasks-BZEiwgxA.mjs.map +0 -1
  113. package/dist/thread-handlers-Bef0td9N.d.mts.map +0 -1
  114. package/dist/tools-CEsv-WoO.mjs.map +0 -1
@@ -1,70 +1,28 @@
1
1
  # Assistant Operations
2
2
 
3
- Use `survey_change` for typed survey mutations and `survey_expression` with
4
- `action: "prepare"` for expression mutations. Both tools submit deltas to one
5
- request-scoped draft. Only successful completion of the whole turn authorizes
6
- the latest cumulative change set for browser apply.
7
-
8
- ## Common Workflow
9
-
10
- 1. Load only the narrow authoring reference needed for the requested convention.
11
- 2. Use `survey_inspect` for revision, selected item, root group, locale, available item types, response slots, item details, or focused raw JSON.
12
- 3. For existing items, inspect item details or exact raw paths before editing.
13
- For image assets, use `survey_inspect` with `action: "assets"`; it returns each explicit `assetId`, metadata, and usage locations without embedded bytes.
14
- 4. Prefer typed `survey_change` operations for supported edits.
15
- 5. Use `survey_expression` for expression mutations on existing state.
16
- 6. Use `survey-json-patch` through `survey_change` only for assistant-editable fields not covered by
17
- typed or expression operations.
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 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
-
21
- ## `survey_change` Typed Operations
22
-
23
- For the canonical fields of one operation, call `survey_authoring_reference` again with
24
- `topic: "assistant-operations"` and that `operationKind`. The returned JSON Schema is generated
25
- from the same validator used by `survey_change`, so this reference does not duplicate a static
26
- operation catalog.
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
-
34
- `create-item`
35
-
36
- - Creates a new item under `target.parentItemId`.
37
- - Use exact root group ID for top-level items.
38
- - Put visible text in top-level `translations`, not inside `item`.
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.
41
- - Use the actual content locale in `translations[].locale`. Missing locales are introduced when translations are applied.
42
- - Put item configuration inside `item.config`, not beside `item`.
43
- - IDs must be unique. Duplicate editor names are allowed; response variable names must be unique survey-wide.
44
- - Use explicit item ids when you need to reference the new item in the same proposal.
45
-
46
- Minimal valid examples:
3
+ Use `survey_change` for typed survey edits and `survey_expression` for expression-bearing changes. Load this reference with `operationKind` and, when relevant, `elementType` to obtain the generated payload contract and installed capability guidance. Those contracts are authoritative; examples below show complete authoring patterns, not alternative schemas.
47
4
 
48
- ```json
49
- {
50
- "kind": "create-item",
51
- "target": {
52
- "parentItemId": "<root-group-id>"
53
- },
54
- "item": {
55
- "id": "intro_privacy",
56
- "itemType": "infoItem",
57
- "config": {}
58
- },
59
- "translations": [
60
- {
61
- "locale": "en",
62
- "contentKey": "content",
63
- "plainText": "This survey asks about your customer experience. Please do not include sensitive personal information."
64
- }
65
- ]
66
- }
67
- ```
5
+ ## Creation
6
+
7
+ `insert-preset`
8
+
9
+ - Use an advertised `elementType` and `presetId` for a toolbox starting point. `target: {parentItemId, index?}` creates a content item containing just that element; `target: {layoutId, index?}` puts the element into an existing layout. This is the ordinary way to work with one question at a time.
10
+ - Set the respondent locale explicitly. The registry allocates persistent IDs and unique response names. Inspect the resulting draft before changing labels, options, names or expressions; never guess generated IDs.
11
+ - A preset is an editable starting configuration, not completed respondent content. Add the actual prompt, labels and constraints requested by the user.
12
+
13
+ `create-item`, `insert-element`, `insert-blocks`
14
+
15
+ - Use `create-item` when the complete authored structure is already known. Flow items are `content`, `group`, or `page-break`. The target must be an existing group; create groups empty and children in parent-before-child order.
16
+ - A content item has `config.body.blocks`. Layout blocks have `kind: "layout"`, `id`, `layout` and `elements`. Elements have `kind: "element"`, `id`, `elementType` and their registered `config`. Do not put elements directly into the survey flow.
17
+ - Section blocks have `kind: "section"`, `id` and `body`. They occupy a full row and contain layouts, never sections. They are semantic groups within one item, distinct from flow groups. Translate their `title` role when they need an accessible group name.
18
+ - `insert-element` inserts a complete registered element into a layout. `insert-blocks` inserts `body.blocks` into a content item's, section's or triggering option's body identified by `bodyOwnerId`.
19
+ - Every newly owned ID is survey-wide unique. Allocate explicit IDs for owners you will translate or reference in the same call. Persistent owner IDs, slot IDs, variable names and option export codes are separate; do not humanize or reuse inspected identifiers.
20
+ - Include new wording in `translations: [{ownerId, role, locale, plainText|content}]` so structure and content are atomic. Supply a usable label for every input and option, or use a single input's unambiguous enclosing title. Do not repeat the same title as an element label unnecessarily.
21
+ - New owners may contain expressions in their creation payload. Create controlling slots before dependents; load expressions for a complete dependent-item example. Change expressions on existing state through `survey_expression`.
22
+
23
+ ### Full worked example: a single-choice question
24
+
25
+ Use the actual inspected root group ID in place of `<root-group-id>`. These explicit IDs illustrate new allocations; choose unused IDs in the current survey.
68
26
 
69
27
  ```json
70
28
  {
@@ -73,72 +31,103 @@ Minimal valid examples:
73
31
  "parentItemId": "<root-group-id>"
74
32
  },
75
33
  "item": {
76
- "id": "overall_satisfaction",
77
- "itemType": "choiceItem",
34
+ "id": "satisfaction",
35
+ "itemType": "content",
78
36
  "config": {
79
- "id": "overall_satisfaction",
80
- "maxSelection": 1,
81
- "shuffleOptions": false,
82
- "options": [
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"
37
+ "body": {
38
+ "blocks": [
39
+ {
40
+ "kind": "layout",
41
+ "id": "satisfaction-layout",
42
+ "layout": {
43
+ "mode": "automatic",
44
+ "maxColumns": 1
45
+ },
46
+ "elements": [
47
+ {
48
+ "kind": "element",
49
+ "id": "satisfaction-choice",
50
+ "elementType": "choice",
51
+ "config": {
52
+ "slotId": "satisfaction-slot",
53
+ "variableName": "overall_satisfaction",
54
+ "presentation": "radio",
55
+ "shuffleOptions": false,
56
+ "options": [
57
+ {
58
+ "id": "satisfaction-very_dissatisfied",
59
+ "code": "VERY_DISSATISFIED"
60
+ },
61
+ {
62
+ "id": "satisfaction-dissatisfied",
63
+ "code": "DISSATISFIED"
64
+ },
65
+ {
66
+ "id": "satisfaction-neutral",
67
+ "code": "NEUTRAL"
68
+ },
69
+ {
70
+ "id": "satisfaction-satisfied",
71
+ "code": "SATISFIED"
72
+ },
73
+ {
74
+ "id": "satisfaction-very_satisfied",
75
+ "code": "VERY_SATISFIED"
76
+ }
77
+ ]
78
+ }
79
+ }
80
+ ]
81
+ }
82
+ ]
83
+ }
105
84
  }
106
85
  },
107
86
  "translations": [
108
87
  {
88
+ "ownerId": "satisfaction",
89
+ "role": "title",
109
90
  "locale": "en",
110
- "contentKey": "title",
111
91
  "plainText": "Overall, how satisfied are you with your experience?"
112
92
  },
113
93
  {
94
+ "ownerId": "satisfaction-very_dissatisfied",
95
+ "role": "label",
114
96
  "locale": "en",
115
- "contentKey": "options.very_dissatisfied.label",
116
97
  "plainText": "Very dissatisfied"
117
98
  },
118
99
  {
100
+ "ownerId": "satisfaction-dissatisfied",
101
+ "role": "label",
119
102
  "locale": "en",
120
- "contentKey": "options.dissatisfied.label",
121
103
  "plainText": "Dissatisfied"
122
104
  },
123
105
  {
106
+ "ownerId": "satisfaction-neutral",
107
+ "role": "label",
124
108
  "locale": "en",
125
- "contentKey": "options.neutral.label",
126
109
  "plainText": "Neutral"
127
110
  },
128
111
  {
112
+ "ownerId": "satisfaction-satisfied",
113
+ "role": "label",
129
114
  "locale": "en",
130
- "contentKey": "options.satisfied.label",
131
115
  "plainText": "Satisfied"
132
116
  },
133
117
  {
118
+ "ownerId": "satisfaction-very_satisfied",
119
+ "role": "label",
134
120
  "locale": "en",
135
- "contentKey": "options.very_satisfied.label",
136
121
  "plainText": "Very satisfied"
137
122
  }
138
123
  ]
139
124
  }
140
125
  ```
141
126
 
127
+ ### Full worked example: a composed question with two inputs
128
+
129
+ Use ordinary elements in a shared layout, with independent persistent response slots. The automatic column count is a maximum, not a requirement to squeeze every input onto one row.
130
+
142
131
  ```json
143
132
  {
144
133
  "kind": "create-item",
@@ -146,61 +135,94 @@ Minimal valid examples:
146
135
  "parentItemId": "<root-group-id>"
147
136
  },
148
137
  "item": {
149
- "id": "personal_data",
150
- "itemType": "formItem",
138
+ "id": "personal-data",
139
+ "itemType": "content",
151
140
  "config": {
152
- "id": "personal_data",
153
- "authoringMode": "simple",
154
- "fieldGroups": [
155
- {
156
- "id": "main",
157
- "layout": {
158
- "base": 1,
159
- "xs": 1,
160
- "sm": 1,
161
- "md": 1,
162
- "xl": 1
163
- },
164
- "fields": [
165
- {
166
- "id": "email",
167
- "type": "input",
168
- "required": false,
169
- "controller": {
170
- "type": "input",
171
- "inputMode": "email"
141
+ "body": {
142
+ "blocks": [
143
+ {
144
+ "kind": "layout",
145
+ "id": "personal-data-layout",
146
+ "layout": {
147
+ "mode": "automatic",
148
+ "maxColumns": 2
149
+ },
150
+ "elements": [
151
+ {
152
+ "kind": "element",
153
+ "id": "profile-email",
154
+ "elementType": "text",
155
+ "config": {
156
+ "slotId": "profile-email-slot",
157
+ "variableName": "email",
158
+ "presentation": "short",
159
+ "purpose": "email",
160
+ "required": false,
161
+ "validations": [
162
+ {
163
+ "id": "profile-email-valid",
164
+ "type": "email"
165
+ }
166
+ ]
167
+ }
172
168
  },
173
- "validations": [
174
- {
175
- "id": "email",
176
- "type": "email"
169
+ {
170
+ "kind": "element",
171
+ "id": "profile-age",
172
+ "elementType": "number",
173
+ "config": {
174
+ "slotId": "profile-age-slot",
175
+ "variableName": "age_years",
176
+ "presentation": "input",
177
+ "min": 0,
178
+ "validations": [
179
+ {
180
+ "id": "profile-age-integer",
181
+ "type": "integer"
182
+ }
183
+ ]
177
184
  }
178
- ],
179
- "variableName": "email"
180
- }
181
- ]
182
- }
183
- ]
185
+ }
186
+ ]
187
+ }
188
+ ]
189
+ }
184
190
  }
185
191
  },
186
192
  "translations": [
187
193
  {
194
+ "ownerId": "personal-data",
195
+ "role": "title",
188
196
  "locale": "en",
189
- "contentKey": "title",
190
197
  "plainText": "Personal information"
191
198
  },
192
199
  {
200
+ "ownerId": "profile-email",
201
+ "role": "label",
193
202
  "locale": "en",
194
- "contentKey": "fields.email.label",
195
203
  "plainText": "Email address"
204
+ },
205
+ {
206
+ "ownerId": "profile-email-valid",
207
+ "role": "message",
208
+ "locale": "en",
209
+ "plainText": "Enter a valid email address."
210
+ },
211
+ {
212
+ "ownerId": "profile-age",
213
+ "role": "label",
214
+ "locale": "en",
215
+ "plainText": "Age in years"
196
216
  }
197
217
  ]
198
218
  }
199
219
  ```
200
220
 
221
+ ## Content, coding and editor presentation
222
+
201
223
  `update-response-settings`
202
224
 
203
- - Applies scalar names, matrix naming components, option export codes and slot export defaults atomically. Load response-variables.md for the exact shape.
225
+ - Applies scalar names, matrix naming components, option export codes and slot export defaults atomically. Load response-variables for the exact shape.
204
226
  - Preserve persistent IDs, expressions, labels and numeric scores. Shared validation rejects invalid names, collisions and direct matrix-cell renames.
205
227
 
206
228
  `update-item-label`
@@ -213,112 +235,65 @@ Minimal valid examples:
213
235
  - Changes the editor-only color stored at `metadata.editorItemColor`.
214
236
  - Use an editor palette value from item-types. This has no respondent-facing effect.
215
237
 
216
- `update-item-translation`
238
+ `update-content`, `update-survey-translation`
217
239
 
218
- - Changes respondent-visible content for one locale/contentKey.
219
- - Use this with a new locale code when adding translated content for a locale that is not yet present.
220
- - Use for titles, subtitles, top/bottom content, card footer, info content, and simple field/option labels when the key is known.
221
- - Use `plainText` for simple unformatted text.
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.
240
+ - Use `update-content` with `ref: {scope: "content", ownerId, role}` for respondent content, or a survey ref with `scope: "survey"`, `surface` and `contentKey`. A localized leaf belongs to its declared owner, independent of where it sits in the survey.
241
+ - Use `plainText` for ordinary unformatted copy. For complete formatted values, load rich-text-content and prefer `richTextBlocks`; it normalizes to canonical `content`. Full `content` is available for structures the shorthand cannot express. Markdown syntax does not request rich formatting.
242
+ - Provide exactly one representation. `content: null` clears the locale's value; never replace the surrounding owner, locale or translation map just to change a leaf.
243
+ - Use the actual requested locale even when absent. A real translation introduces it and reports a warning so accidental locale codes can be spotted. Do not write target-language text into an existing wrong locale.
223
244
 
224
245
  `update-placed-image`
225
246
 
226
- - Updates one image block already placed in an item's rich-text translation without replacing the surrounding content.
227
- - 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.
228
- - `changes.source` accepts either `{ "type": "asset", "assetId": "..." }` or `{ "type": "external", "url": "https://..." }`, so a placement can switch between local and external sources.
229
- - `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.
230
- - 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.
247
+ - Updates a placed image without replacing its surrounding rich text. Identify it with the owner/survey `ref`, `locale`, and zero-based `imageIndex` counting image blocks only. Include `expectedAssetId` for a local asset to detect stale placement.
248
+ - `changes.source` accepts `{type: "asset", assetId}` or `{type: "external", url}`. Other changes are `alt`, `caption`, `width`, `height`, `maxWidth`, `maxHeight`, and `alignment`. Dimensions use `{unit: "px" | "%", value: number}`; `null` removes an optional property.
249
+ - Placement changes affect the rendered image. `update-image-asset-metadata` changes reusable metadata and does not rewrite existing placements' ALT text or layout.
231
250
 
232
- `patch-item-config`
251
+ ## Configuration and composition
233
252
 
234
- - Updates one existing item's configuration without requiring its global survey array index.
235
- - Every JSON Patch path is relative to the inspected item config root. For example, `/max` targets `item.config.max`.
236
- - Load the item type's advertised authoring reference when one exists, inspect the current item, and preserve unrelated config fields.
237
- - If the item capability provides a validator, it validates the resulting config before the change can be applied.
238
- - Prefer this operation over `survey-json-patch` for custom-item settings and ordinary item-local configuration.
253
+ `patch-element-config`, `patch-item-config`
239
254
 
240
- `add-choice-option`, `update-choice-option-label`, `remove-choice-option`, `reorder-choice-options`
255
+ - Inspect the target and use JSON Pointers relative to its config root. `/options/0/code` on a choice element targets its first option's code; it is not a survey-root pointer. Array indices must come from the current draft.
256
+ - Prefer `patch-element-config` for choice options, matrix rows/cells, validation settings and custom element settings. `patch-item-config` handles flow-item configuration. Preserve unrelated fields and surviving IDs.
257
+ - Group related structural edits and new owner-role translations in the same operation. Matrix row/column changes must produce a complete rectangle; follow-up creation must include its layout, elements and visible content. Load the relevant element-types/follow-ups reference first.
258
+ - Do not copy an object's persistent IDs to duplicate it. New objects, nested options, validation rules and slots need fresh IDs; preserve translations in every existing locale.
259
+ - An existing answer ID cannot change value type, including through config or raw patches. A requested type change needs a fresh slot ID (a matrix cell's ID is also its slot ID) and explicit repair of all dependent expressions, prefills and export settings within the same turn. Inspect and preserve these dependencies first. Stage individually valid steps: clear dependent logic/prefills through `survey_expression`; remove old slot-keyed export settings with an exact leaf `survey-json-patch` (the settings operation does not remove entries); change the type with a fresh ID; then restore the authorized, adapted logic, prefills and settings against the new ID through their normal tools. Do not silently rebind references or discard settings. Verify the complete result before finalizing; the accepted turn applies as one undoable edit, and unresolved rejected proposals prevent application. If the intended repairs are unclear or unauthorized, report the dependencies without starting a partial repair.
260
+ - Use `survey_expression` for conditions, expression rules, prefills and template values on existing state. Ordinary validation constraints and their message translations are config/content edits.
241
261
 
242
- - Use for ordinary choice option edits on existing choice items.
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.
262
+ `update-layout`
245
263
 
246
- `upsert-form-field-group`, `remove-form-field-group`, `reorder-form-field-groups`, `upsert-form-field`, `remove-form-field`, `move-form-field`
264
+ - Targets an exact layout ID and replaces its layout policy without replacing its elements. Automatic policy is `{mode: "automatic", maxColumns: 1..12}`. Advanced breakpoint policies are described in the generated contract.
265
+ - All rows share column alignment. Full-width elements use a whole row; semantic sections remain vertical blocks. Do not simulate a semantic section by placing a titled container alongside another element.
247
266
 
248
- - Target a top-level form with `{ "itemId": "<form-id>" }` or a choice option's form with `{ "itemId": "<choice-id>", "optionId": "<option-id>" }`.
249
- - Add visible labels, placeholders, option labels, and validation messages in the operation's `translations` array so structure and content remain atomic.
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.
252
- - Simple forms contain one group and one field. Compatible updates to that same field may remain
253
- simple; structural changes automatically make the form custom. This applies to top-level and
254
- embedded forms and is intentionally one-way.
267
+ `move-item`, `reorder-items`, `move-element`, `move-block`
255
268
 
256
- Update an existing form field group's layout without replacing its fields:
269
+ - Indexes are zero-based; omission appends and `0` inserts first. Use flow item operations for survey structure, `move-element` for layout children, and `move-block` for layouts/sections in a body.
270
+ - `reorder-items` takes top-level `parentItemId` and the COMPLETE inspected `itemIds` list. Page inspection until all direct children are known. Root moves, cycles, duplicates and incomplete lists are rejected.
271
+ - Moves retain identities and translated content. If a move crosses validation ownership, the default `ruleTransfer: "reject"` protects existing rules. Use `"move-with-targets"` only for an intentional move whose complete target set moves together; inspect and resolve a split rule rather than dropping it.
272
+ - Verify the stored placement or child order. A shuffled group may show a different respondent order.
257
273
 
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
-
277
- `set-embedded-form`, `remove-embedded-form`
278
-
279
- - Add or remove a choice option's inline form using item and option ids, without raw array-index patches.
280
- - `set-embedded-form` accepts the full form config plus its visible translations in the same operation.
281
- - Set `form.authoringMode` to `simple` only for exactly one group with one field; otherwise use
282
- `custom`. Malformed simple input is normalized to custom without dropping form data.
283
- - Form layout values are responsive column counts from 1 to 4, not spans; one column makes a lone field full width.
274
+ `remove-item`, `remove-content`
284
275
 
285
- `move-item`, `reorder-items`
276
+ - `remove-item` removes a flow item; set `includeChildren: true` only for intentionally deleting a group subtree. `remove-content` removes an element or block by its owner ID.
277
+ - Before removing referenced owners/slots, inspect what depends on them. In the same turn, first update or clear dependent conditions, validation rules and prefills with `survey_expression`, then remove the content. Dependent content that should go too can be removed in the same `survey_change` as its target, in any order. A change that still leaves references to removed content is rejected with `deletion-has-dependencies` and removes nothing. Preserve the requested deletion when retrying; an unrelated edit cannot resolve its rejection.
278
+ - Authors may keep broken references while restructuring; the editor reports them as blocking diagnostics until they are repaired. Never leave new ones behind. Repair existing ones when the request concerns them or the user asks, choosing the intended replacement from current slots and options or removing a dependent that no longer applies.
286
279
 
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.
291
- - The validator rejects root moves, cycles, missing children, duplicates, and incomplete reorder lists.
280
+ `update-survey-metadata`, `update-survey-pagination`, `add-survey-locale`, `remove-survey-locale`
292
281
 
293
- `update-survey-metadata`, `update-survey-pagination`, `update-survey-translation`, `add-survey-locale`, `remove-survey-locale`
294
-
295
- - Use typed survey-level operations for metadata, page limits, survey-card/navigation/validation content, and locale lifecycle.
296
- - `add-survey-locale` registers an empty locale and is only for an explicitly requested empty registration. An ordinary request to add/localize/translate a language requires real respondent-facing translations; those translation operations introduce an absent locale themselves. `remove-survey-locale` is intentionally explicit and destructive: it deletes all survey-level and item-level translations stored for that locale.
282
+ - Use typed operations for survey metadata, page limits and locale lifecycle.
283
+ - `add-survey-locale` is only for an explicitly requested empty registration. An ordinary request to add/localize/translate a language requires real respondent-facing translations. `remove-survey-locale` explicitly deletes every translation in that locale; do not emulate it with leaf-by-leaf deletion or raw patches.
297
284
 
298
285
  `set-image-asset`, `update-image-asset-metadata`, `remove-image-asset`
299
286
 
300
287
  - Prefer `survey_asset import-attachment` for binary image imports so the model never copies attachment base64.
301
288
  - To use that import in a new item, follow it with a `survey_change` call and embed `{ "type": "image", "source": { "type": "asset", "assetId": "<returned-id>" } }` directly in the new item's rich-text translation. The import is retained automatically in the turn draft.
302
289
  - Metadata and removal are typed and validated. `survey_asset remove` refuses an asset that is still referenced in the current request-scoped draft.
303
- - 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.
304
-
305
- `remove-item`
290
+ - 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 information item and its asset is `[remove-item, remove-image-asset]` in one candidate.
306
291
 
307
- - Removes an item.
308
- - Set `includeChildren: true` only when intentionally deleting a group subtree.
292
+ ## Transaction behavior
309
293
 
310
- `survey-json-patch`
294
+ Successful operations accumulate in the request-scoped draft. Later tools see earlier accepted changes. The live editor applies the authorized final draft as one normal undoable action only after a successful turn. A rejected delta leaves the draft unchanged and blocks authorization until that same request is repaired. Do not announce unapplied edits as complete.
311
295
 
312
- - Universal escape hatch for raw survey edits.
313
- - Must be the only operation in a proposal, but may contain many patch entries.
314
- - Use it for assistant-editable raw survey fields that have no narrow typed or expression helper.
315
- - It cannot modify `/$schema`, bypass item capability constraints, or create item types that are not
316
- deterministically creatable.
317
- - Use it for advanced choice config and other inspected fields without typed coverage.
318
- - Use `survey_expression` for template values, display/disabled conditions, expression-based item
319
- validations, and prefills on existing state. Form-field validation configuration and translated
320
- validation messages are ordinary config/content, not expression mutations.
321
- - Use it for field controller internals and any existing custom-registry item config that is not covered by a typed helper.
296
+ `survey-json-patch` is an escape hatch for inspected survey-root fields without a typed operation or expression helper. It must be the only operation in its proposal, but can contain many patches. It cannot replace schema identity, bypass installed capabilities, replace translation containers or modify existing expressions.
322
297
 
323
298
  ## Raw Patch Example
324
299
 
@@ -15,6 +15,6 @@ Core rules:
15
15
  inserting/removing separators, splitting camelCase, or substituting a label/title/name. Visible
16
16
  prose belongs in translations or editor labels. When explaining an existing survey, quote the
17
17
  exact inspected identifier.
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.
18
+ - Load only the reference you need: survey-data-model.md, source-material-surveys.md, localization.md, rich-text-content.md, element-types.md, follow-ups.md, expressions.md, response-variables.md, or assistant-operations.md.
19
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.
20
+ Survey schema version 3 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 follow-up 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.