@case-framework/survey-assistant 0.7.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 (142) 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-CmwLc_C8.mjs} +49 -32
  5. package/dist/authoring-references-CmwLc_C8.mjs.map +1 -0
  6. package/dist/capabilities-D55dq-fO.mjs +582 -0
  7. package/dist/capabilities-D55dq-fO.mjs.map +1 -0
  8. package/dist/capabilities-default.d.mts +5 -12
  9. package/dist/capabilities-default.d.mts.map +1 -1
  10. package/dist/capabilities-default.mjs +2 -411
  11. package/dist/capabilities-z95e1mti.d.mts +117 -0
  12. package/dist/capabilities-z95e1mti.d.mts.map +1 -0
  13. package/dist/{constants-0UHGFcRJ.mjs → constants-Ct-vv9cu.mjs} +1 -1
  14. package/dist/{constants-0UHGFcRJ.mjs.map → constants-Ct-vv9cu.mjs.map} +1 -1
  15. package/dist/{constants-Be91Gay9.d.mts → constants-HL_klgJl.d.mts} +7 -28
  16. package/dist/constants-HL_klgJl.d.mts.map +1 -0
  17. package/dist/{controller-proxy-BH05e-C2.mjs → controller-proxy-BQ1YsVxJ.mjs} +12 -21
  18. package/dist/controller-proxy-BQ1YsVxJ.mjs.map +1 -0
  19. package/dist/controller-proxy-Cmzdr9tV.d.mts +65 -0
  20. package/dist/controller-proxy-Cmzdr9tV.d.mts.map +1 -0
  21. package/dist/default-D22AKTqE.mjs +233 -0
  22. package/dist/default-D22AKTqE.mjs.map +1 -0
  23. package/dist/{digest-SJmoYVaK.d.mts → digest-FnTTDBq2.d.mts} +2 -3
  24. package/dist/digest-FnTTDBq2.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-D950TfvC.mjs +3098 -0
  28. package/dist/engine-D950TfvC.mjs.map +1 -0
  29. package/dist/engine.d.mts +4 -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-l_EK4yK7.d.mts → index-B8TLBd1G.d.mts} +18 -72
  34. package/dist/index-B8TLBd1G.d.mts.map +1 -0
  35. package/dist/index-CaX3hZmr.d.mts +23813 -0
  36. package/dist/index-CaX3hZmr.d.mts.map +1 -0
  37. package/dist/{index-x0qprHgK.d.mts → index-CeUlGkoU.d.mts} +58 -28
  38. package/dist/index-CeUlGkoU.d.mts.map +1 -0
  39. package/dist/{index-nhrgbgO5.d.mts → index-DYHdvSE8.d.mts} +347 -88
  40. package/dist/index-DYHdvSE8.d.mts.map +1 -0
  41. package/dist/lifecycle-DFzxVxpq.d.mts +54346 -0
  42. package/dist/lifecycle-DFzxVxpq.d.mts.map +1 -0
  43. package/dist/{memory-thread-repository-KWHQWFEW.mjs → memory-thread-repository-DmLadygY.mjs} +60 -30
  44. package/dist/memory-thread-repository-DmLadygY.mjs.map +1 -0
  45. package/dist/protocol-BQ7uIDYT.mjs +569 -0
  46. package/dist/protocol-BQ7uIDYT.mjs.map +1 -0
  47. package/dist/protocol.d.mts +4 -4
  48. package/dist/protocol.mjs +4 -3
  49. package/dist/{react-B3ybjJ-X.mjs → react-DybJ5pWw.mjs} +159 -46
  50. package/dist/react-DybJ5pWw.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 +224 -180
  56. package/dist/references/core-rules.md +6 -3
  57. package/dist/references/element-types.md +189 -0
  58. package/dist/references/expressions.md +307 -168
  59. package/dist/references/fair-by-design.md +9 -9
  60. package/dist/references/follow-ups.md +151 -0
  61. package/dist/references/localization.md +26 -12
  62. package/dist/references/response-variables.md +38 -0
  63. package/dist/references/rich-text-content.md +22 -43
  64. package/dist/references/source-material-surveys.md +20 -24
  65. package/dist/references/survey-data-model.md +36 -102
  66. package/dist/{request-context-CG4SWijt.mjs → request-context-KVh8VjgH.mjs} +4 -10
  67. package/dist/request-context-KVh8VjgH.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 +62 -43
  71. package/dist/server-agent.d.mts.map +1 -1
  72. package/dist/server-agent.mjs +73 -40
  73. package/dist/server-agent.mjs.map +1 -1
  74. package/dist/server-runtime.d.mts +24 -42
  75. package/dist/server-runtime.d.mts.map +1 -1
  76. package/dist/server-runtime.mjs +12 -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 +14 -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-Coh5N027.mjs +397 -0
  91. package/dist/tasks-Coh5N027.mjs.map +1 -0
  92. package/dist/{tasks-CYqrcaRO.d.mts → tasks-wb9M-bak.d.mts} +25 -41
  93. package/dist/tasks-wb9M-bak.d.mts.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-DBAaU7BA.d.mts → thread-handlers-C3Iya92v.d.mts} +9 -40
  97. package/dist/thread-handlers-C3Iya92v.d.mts.map +1 -0
  98. package/dist/{tools-BvSovehT.mjs → tools-D0KBDolR.mjs} +677 -459
  99. package/dist/tools-D0KBDolR.mjs.map +1 -0
  100. package/dist/turn-survey-draft-B34lggSG.d.mts +185 -0
  101. package/dist/turn-survey-draft-B34lggSG.d.mts.map +1 -0
  102. package/dist/ui.css +1 -1
  103. package/dist/ui.d.mts +15 -54
  104. package/dist/ui.d.mts.map +1 -1
  105. package/dist/ui.mjs +203 -156
  106. package/dist/ui.mjs.map +1 -1
  107. package/docs/integration.md +11 -1
  108. package/package.json +41 -36
  109. package/dist/authoring-references-BJQ_1nMX.mjs.map +0 -1
  110. package/dist/capabilities-1KNnNO1_.mjs +0 -73
  111. package/dist/capabilities-1KNnNO1_.mjs.map +0 -1
  112. package/dist/capabilities-DVTfogjv.d.mts +0 -193
  113. package/dist/capabilities-DVTfogjv.d.mts.map +0 -1
  114. package/dist/capabilities-default.mjs.map +0 -1
  115. package/dist/constants-Be91Gay9.d.mts.map +0 -1
  116. package/dist/controller-proxy-BH05e-C2.mjs.map +0 -1
  117. package/dist/controller-proxy-Bd0ZHBPF.d.mts +0 -85
  118. package/dist/controller-proxy-Bd0ZHBPF.d.mts.map +0 -1
  119. package/dist/digest-SJmoYVaK.d.mts.map +0 -1
  120. package/dist/engine-Bo85Oyj0.mjs +0 -6723
  121. package/dist/engine-Bo85Oyj0.mjs.map +0 -1
  122. package/dist/index-Tnqv-yKW.d.mts +0 -777
  123. package/dist/index-Tnqv-yKW.d.mts.map +0 -1
  124. package/dist/index-l_EK4yK7.d.mts.map +0 -1
  125. package/dist/index-nhrgbgO5.d.mts.map +0 -1
  126. package/dist/index-x0qprHgK.d.mts.map +0 -1
  127. package/dist/lifecycle-Bx0aq_4S.d.mts +0 -529
  128. package/dist/lifecycle-Bx0aq_4S.d.mts.map +0 -1
  129. package/dist/memory-thread-repository-KWHQWFEW.mjs.map +0 -1
  130. package/dist/protocol-BhzQbx81.mjs +0 -1173
  131. package/dist/protocol-BhzQbx81.mjs.map +0 -1
  132. package/dist/react-B3ybjJ-X.mjs.map +0 -1
  133. package/dist/references/embedded-forms.md +0 -126
  134. package/dist/references/item-types.md +0 -407
  135. package/dist/request-context-CG4SWijt.mjs.map +0 -1
  136. package/dist/request-guardrails-TmgQtqp4.mjs.map +0 -1
  137. package/dist/server-tasks.mjs.map +0 -1
  138. package/dist/tasks-CYqrcaRO.d.mts.map +0 -1
  139. package/dist/thread-documents-rmd6nyX9.d.mts +0 -107
  140. package/dist/thread-documents-rmd6nyX9.d.mts.map +0 -1
  141. package/dist/thread-handlers-DBAaU7BA.d.mts.map +0 -1
  142. package/dist/tools-BvSovehT.mjs.map +0 -1
@@ -1,149 +1,229 @@
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 root group id/key 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
- - Item key must be unique among siblings.
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": { "parentItemId": "<root-group-id-or-key>" },
52
- "item": {
53
- "id": "intro_privacy",
54
- "key": "intro_privacy",
55
- "itemType": "infoItem",
56
- "config": {}
57
- },
58
- "translations": [
59
- {
60
- "locale": "en",
61
- "contentKey": "content",
62
- "plainText": "This survey asks about your customer experience. Please do not include sensitive personal information."
63
- }
64
- ]
65
- }
66
- ```
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.
67
26
 
68
27
  ```json
69
28
  {
70
29
  "kind": "create-item",
71
- "target": { "parentItemId": "<root-group-id-or-key>" },
30
+ "target": {
31
+ "parentItemId": "<root-group-id>"
32
+ },
72
33
  "item": {
73
- "id": "overall_satisfaction",
74
- "key": "overall_satisfaction",
75
- "itemType": "choiceItem",
34
+ "id": "satisfaction",
35
+ "itemType": "content",
76
36
  "config": {
77
- "id": "overall_satisfaction",
78
- "maxSelection": 1,
79
- "shuffleOptions": false,
80
- "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
- ]
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
+ }
87
84
  }
88
85
  },
89
86
  "translations": [
90
87
  {
88
+ "ownerId": "satisfaction",
89
+ "role": "title",
91
90
  "locale": "en",
92
- "contentKey": "title",
93
91
  "plainText": "Overall, how satisfied are you with your experience?"
94
92
  },
95
93
  {
94
+ "ownerId": "satisfaction-very_dissatisfied",
95
+ "role": "label",
96
96
  "locale": "en",
97
- "contentKey": "options.very_dissatisfied.label",
98
97
  "plainText": "Very dissatisfied"
99
98
  },
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" }
99
+ {
100
+ "ownerId": "satisfaction-dissatisfied",
101
+ "role": "label",
102
+ "locale": "en",
103
+ "plainText": "Dissatisfied"
104
+ },
105
+ {
106
+ "ownerId": "satisfaction-neutral",
107
+ "role": "label",
108
+ "locale": "en",
109
+ "plainText": "Neutral"
110
+ },
111
+ {
112
+ "ownerId": "satisfaction-satisfied",
113
+ "role": "label",
114
+ "locale": "en",
115
+ "plainText": "Satisfied"
116
+ },
117
+ {
118
+ "ownerId": "satisfaction-very_satisfied",
119
+ "role": "label",
120
+ "locale": "en",
121
+ "plainText": "Very satisfied"
122
+ }
104
123
  ]
105
124
  }
106
125
  ```
107
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
+
108
131
  ```json
109
132
  {
110
133
  "kind": "create-item",
111
- "target": { "parentItemId": "<root-group-id-or-key>" },
134
+ "target": {
135
+ "parentItemId": "<root-group-id>"
136
+ },
112
137
  "item": {
113
- "id": "personal_data",
114
- "key": "personal_data",
115
- "itemType": "formItem",
138
+ "id": "personal-data",
139
+ "itemType": "content",
116
140
  "config": {
117
- "id": "personal_data",
118
- "authoringMode": "simple",
119
- "fieldGroups": [
120
- {
121
- "id": "main",
122
- "layout": { "base": 1, "xs": 1, "sm": 1, "md": 1, "xl": 1 },
123
- "fields": [
124
- {
125
- "id": "email",
126
- "key": "EMAIL",
127
- "type": "input",
128
- "required": false,
129
- "controller": { "type": "input", "inputMode": "email" },
130
- "validations": [{ "id": "email", "type": "email" }]
131
- }
132
- ]
133
- }
134
- ]
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
+ }
168
+ },
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
+ ]
184
+ }
185
+ }
186
+ ]
187
+ }
188
+ ]
189
+ }
135
190
  }
136
191
  },
137
192
  "translations": [
138
- { "locale": "en", "contentKey": "title", "plainText": "Personal information" },
139
- { "locale": "en", "contentKey": "fields.email.label", "plainText": "Email address" }
193
+ {
194
+ "ownerId": "personal-data",
195
+ "role": "title",
196
+ "locale": "en",
197
+ "plainText": "Personal information"
198
+ },
199
+ {
200
+ "ownerId": "profile-email",
201
+ "role": "label",
202
+ "locale": "en",
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"
216
+ }
140
217
  ]
141
218
  }
142
219
  ```
143
220
 
144
- `update-item-key`
221
+ ## Content, coding and editor presentation
145
222
 
146
- - Changes item coding key. This affects fullKey paths and response naming.
223
+ `update-response-settings`
224
+
225
+ - Applies scalar names, matrix naming components, option export codes and slot export defaults atomically. Load response-variables for the exact shape.
226
+ - Preserve persistent IDs, expressions, labels and numeric scores. Shared validation rejects invalid names, collisions and direct matrix-cell renames.
147
227
 
148
228
  `update-item-label`
149
229
 
@@ -155,101 +235,65 @@ Minimal valid examples:
155
235
  - Changes the editor-only color stored at `metadata.editorItemColor`.
156
236
  - Use an editor palette value from item-types. This has no respondent-facing effect.
157
237
 
158
- `update-item-translation`
238
+ `update-content`, `update-survey-translation`
159
239
 
160
- - Changes respondent-visible content for one locale/contentKey.
161
- - Use this with a new locale code when adding translated content for a locale that is not yet present.
162
- - Use for titles, subtitles, top/bottom content, card footer, info content, and simple field/option labels when the key is known.
163
- - Use `plainText` for simple unformatted text.
164
- - 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.
165
244
 
166
245
  `update-placed-image`
167
246
 
168
- - Updates one image block already placed in an item's rich-text translation without replacing the surrounding content.
169
- - 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.
170
- - `changes.source` accepts either `{ "type": "asset", "assetId": "..." }` or `{ "type": "external", "url": "https://..." }`, so a placement can switch between local and external sources.
171
- - `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.
172
- - 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.
173
250
 
174
- `patch-item-config`
251
+ ## Configuration and composition
175
252
 
176
- - Updates one existing item's configuration without requiring its global survey array index.
177
- - Every JSON Patch path is relative to the inspected item config root. For example, `/max` targets `item.config.max`.
178
- - Load the item type's advertised authoring reference when one exists, inspect the current item, and preserve unrelated config fields.
179
- - If the item capability provides a validator, it validates the resulting config before the change can be applied.
180
- - Prefer this operation over `survey-json-patch` for custom-item settings and ordinary item-local configuration.
253
+ `patch-element-config`, `patch-item-config`
181
254
 
182
- `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.
183
261
 
184
- - 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.
262
+ `update-layout`
187
263
 
188
- `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.
189
266
 
190
- - Target a top-level form with `{ "itemId": "<form-id>" }` or a choice option's form with `{ "itemId": "<choice-id>", "optionId": "<option-id>" }`.
191
- - Add visible labels, placeholders, option labels, and validation messages in the operation's `translations` array so structure and content remain atomic.
192
- - Group and field identifiers are stable ids; reorder operations require the complete current id set.
193
- - 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.
194
- - Simple forms contain one group and one field. Compatible updates to that same field may remain
195
- simple; structural changes automatically make the form custom. This applies to top-level and
196
- embedded forms and is intentionally one-way.
267
+ `move-item`, `reorder-items`, `move-element`, `move-block`
197
268
 
198
- 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.
199
273
 
200
- ```json
201
- {
202
- "kind": "upsert-form-field-group",
203
- "target": { "itemId": "profile" },
204
- "group": {
205
- "id": "main",
206
- "layout": { "base": 1, "xs": 1, "sm": 2, "md": 2, "xl": 3 }
207
- }
208
- }
209
- ```
210
-
211
- `set-embedded-form`, `remove-embedded-form`
274
+ `remove-item`, `remove-content`
212
275
 
213
- - Add or remove a choice option's inline form using item and option ids, without raw array-index patches.
214
- - `set-embedded-form` accepts the full form config plus its visible translations in the same operation.
215
- - Set `form.authoringMode` to `simple` only for exactly one group with one field; otherwise use
216
- `custom`. Malformed simple input is normalized to custom without dropping form data.
217
- - Form layout values are responsive column counts from 1 to 4, not spans; one column makes a lone field full width.
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.
218
279
 
219
- `move-item`, `reorder-items`
280
+ `update-survey-metadata`, `update-survey-pagination`, `add-survey-locale`, `remove-survey-locale`
220
281
 
221
- - Move an existing item to a group/index or set a group's complete direct-child order.
222
- - The validator rejects root moves, cycles, missing children, duplicates, and incomplete reorder lists.
223
-
224
- `update-survey-metadata`, `update-survey-pagination`, `update-survey-translation`, `add-survey-locale`, `remove-survey-locale`
225
-
226
- - Use typed survey-level operations for metadata, page limits, survey-card/navigation/validation content, and locale lifecycle.
227
- - `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.
228
284
 
229
285
  `set-image-asset`, `update-image-asset-metadata`, `remove-image-asset`
230
286
 
231
287
  - Prefer `survey_asset import-attachment` for binary image imports so the model never copies attachment base64.
232
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.
233
289
  - Metadata and removal are typed and validated. `survey_asset remove` refuses an asset that is still referenced in the current request-scoped draft.
234
- - 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.
235
-
236
- `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.
237
291
 
238
- - Removes an item.
239
- - Set `includeChildren: true` only when intentionally deleting a group subtree.
292
+ ## Transaction behavior
240
293
 
241
- `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.
242
295
 
243
- - Universal escape hatch for raw survey edits.
244
- - Must be the only operation in a proposal, but may contain many patch entries.
245
- - Use it for assistant-editable raw survey fields that have no narrow typed or expression helper.
246
- - It cannot modify `/$schema`, bypass item capability constraints, or create item types that are not
247
- deterministically creatable.
248
- - Use it for advanced choice config and other inspected fields without typed coverage.
249
- - Use `survey_expression` for template values, display/disabled conditions, expression-based item
250
- validations, and prefills on existing state. Form-field validation configuration and translated
251
- validation messages are ordinary config/content, not expression mutations.
252
- - 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.
253
297
 
254
298
  ## Raw Patch Example
255
299
 
@@ -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, element-types.md, follow-ups.md, expressions.md, response-variables.md, or assistant-operations.md.
19
+
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.