@choiceapps/communityservices-sdk 0.8.0 → 0.8.1

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.
package/API.md CHANGED
@@ -84,3 +84,248 @@ Both versions have the methods below. Import from `@choiceapps/communityservices
84
84
  | `tenants.update` | PUT | `/tenants/UpdateTenantRecord` | |
85
85
  | `tenants.updateByCode` | PUT | `/tenants/UpdateTenantRecord/{tenant_code}` | |
86
86
  | `tenants.delete` | DELETE | `/tenants/DeleteTenantRecord` | |
87
+
88
+ ## Enrollment workflow errors
89
+
90
+ This SDK-owned code inventory is generated from the workflow guards and stable reason map. These codes are separate from the REST Swagger operations above. Classify errors by `code`, never by message text. Existing codes remain stable when wording changes; later releases may add codes. Application-supplied constructor codes are outside this inventory.
91
+
92
+ `EnrollmentWorkflowError.operation` identifies the SDK operation. `committed: false` means that operation has not attempted a WellSky record write; it does not promise that no local journal exists or that another operation wrote nothing. `committed: "unknown"` preserves an attempted or unreadable prior outcome. Never choose a new key solely from an HTTP status or unchanged read.
93
+
94
+ A preflight refusal after enrollment creation can identify `step: "scalars"` while the enrollment is already saved. Keep the same key and reconcile the existing journal. A failed `resolveEntryCorrection` can have `committed: false` for resolution itself and `originalCommitted: "unknown"` for the original correction. See [recovery guidance](docs/troubleshooting.md#workflow-error-codes).
95
+
96
+ | Code | Reason or meaning |
97
+ | --- | --- |
98
+ | `ACTIVE_CLIENT_REQUIRED` | active client required |
99
+ | `ACTIVE_HOUSEHOLD_REQUIRED` | active household required |
100
+ | `ACTIVE_HOUSEHOLD_REQUIRED_FOR_ADOPTION` | active household required for adoption |
101
+ | `ACTIVE_OPERATIONAL_PROGRAM_REQUIRED` | active operational program required |
102
+ | `ADDED_ENROLLMENT_IDENTITY_CHANGED` | added enrollment identity changed |
103
+ | `ADDITION_REQUIRES_NEW_HOUSEHOLD_MEMBERS` | addition requires new household members |
104
+ | `ADDITIONAL_MEMBER_ALREADY_HAS_AN_INDEPENDENT_ENROLLMENT` | additional member already has an independent enrollment |
105
+ | `ADDITIONAL_MEMBER_ASSESSMENT_CONFIGURATION_CHANGED` | additional member assessment configuration changed |
106
+ | `ADDITIONAL_MEMBER_ENTRY_MUST_FOLLOW_ORIGINAL_ENTRY` | additional member entry must follow original entry |
107
+ | `ADOPTED_ENROLLMENT_IDENTITY_OR_CONFIGURATION_CHANGED` | adopted enrollment identity or configuration changed |
108
+ | `ADOPTION_COMPLETE_COHORT_REQUIRED` | adoption requires the complete linked enrollment cohort |
109
+ | `ADOPTION_ENROLLMENT_CONTEXT_MISMATCH` | adoption enrollment context mismatch |
110
+ | `ADOPTION_EXIT_PRECEDES_ENTRY` | adoption exit precedes entry |
111
+ | `ADOPTION_HOUSEHOLD_HEAD_MISMATCH` | adoption household head mismatch |
112
+ | `ADOPTION_HOUSEHOLD_MEMBERSHIP_MISMATCH` | adoption household membership mismatch |
113
+ | `ADOPTION_HOUSEHOLD_REQUIRED` | linked household enrollment requires adoptHouseholdEnrollment |
114
+ | `ADOPTION_KEY_CONFLICTS_WITH_PREVIOUS_INTENT` | adoption key conflicts with previous intent |
115
+ | `ADOPTION_REQUIRES_THE_ORIGINAL_ENROLLMENT_HEAD_RELATIONSHIP` | adoption requires the original enrollment head relationship |
116
+ | `AMBIGUOUS_ASSESSMENT_READ` | ambiguous assessment read |
117
+ | `AMBIGUOUS_CLOSURE_SNAPSHOT` | ambiguous closure snapshot |
118
+ | `AMBIGUOUS_CORRECTION_ASSESSMENT_READ` | ambiguous correction assessment read |
119
+ | `AMBIGUOUS_CORRECTION_ROW` | ambiguous correction row |
120
+ | `AMBIGUOUS_CORRECTION_ROW_CELL` | ambiguous correction row cell |
121
+ | `AMBIGUOUS_HOUSEHOLD_MEMBERSHIP` | ambiguous household membership |
122
+ | `ANOTHER_UNFINISHED_HOUSEHOLD_WORKFLOW_MUST_BE_RECONCILED_FIRST` | another unfinished household workflow must be reconciled first |
123
+ | `ANOTHER_UNFINISHED_WORKFLOW_MUST_BE_RECONCILED_FIRST` | another unfinished workflow must be reconciled first |
124
+ | `ANSWER_ENCODING_UNAVAILABLE` | answer encoding unavailable |
125
+ | `ASSESSMENT_FIELD_UNCOVERED` | assessment field uncovered |
126
+ | `ASSESSMENT_LOCATIONS_MUST_BE_A_MAP_BY_ENTRY_EXIT_TYPE` | assessment locations must be a map by Entry/Exit type |
127
+ | `ASSESSMENT_LOCATIONS_REQUIRED` | assessment locations required |
128
+ | `ASSESSMENT_METADATA_INCOMPLETE` | assessment metadata incomplete |
129
+ | `ASSESSMENT_PLAN_REQUIRED` | assessment plan required |
130
+ | `ATTEMPTED_CORRECTION_REQUIRED_FOR_RESOLUTION` | attempted correction required for resolution |
131
+ | `AUTHORITATIVE_ADOPTION_EXIT_DATE_REQUIRED` | authoritative adoption exit date required |
132
+ | `AUTHORITATIVE_ENROLLMENT_SESSION_REQUIRED_FOR_ADOPTION` | authoritative enrollment session required for adoption |
133
+ | `AUTHORITATIVE_EXIT_DETAILS_READER_REQUIRED` | authoritative exit details reader required |
134
+ | `AUTHORITATIVE_EXIT_READ_REQUIRED` | authoritative exit read required |
135
+ | `AUTHORITATIVE_HEAD_OF_HOUSEHOLD_EXIT_DATES_REQUIRED` | authoritative head of household exit dates required |
136
+ | `AUTHORITATIVE_INDIVIDUAL_EXIT_READ_REQUIRED` | authoritative individual exit read required |
137
+ | `CARRY_FORWARD_BIRTH_DATE_CHANGED_SINCE_ENROLLMENT` | carry-forward birth date changed since enrollment |
138
+ | `CARRY_FORWARD_BIRTH_DATE_IS_EMPTY_OR_FOLLOWS_ASSESSMENT_DATE` | carry-forward birth date is empty or follows assessment date |
139
+ | `CARRY_FORWARD_FIELD_IS_EMPTY` | carry-forward field is empty |
140
+ | `CLIENT_ID_REQUIRED` | client ID required |
141
+ | `CLIENT_PROVIDER_AND_SUPPORTED_ENTRY_EXIT_TYPE_REQUIRED` | client, provider and supported Entry/Exit type required |
142
+ | `CONFLICTING_FIELD_COVERAGE` | conflicting field coverage |
143
+ | `CORRECTION_ANSWER_COULD_NOT_BE_ENCODED` | correction answer could not be encoded |
144
+ | `CORRECTION_KEY_CONFLICTS_WITH_PREVIOUS_INTENT` | correction key conflicts with previous intent |
145
+ | `CORRECTION_LOOKUP_COULD_NOT_BE_RESOLVED` | correction lookup could not be resolved |
146
+ | `CORRECTION_REQUIRES_AN_EXISTING_ENTRY_ROW_KEY_AND_MATCHING_PARENT` | correction requires an existing entry row key and matching parent |
147
+ | `CORRECTION_ROW_METADATA_INCOMPLETE` | correction row metadata incomplete |
148
+ | `CORRECTION_SCALAR_READ_MISSING` | correction scalar read missing |
149
+ | `CORRECTION_STATE_CHANGED_DURING_RESOLUTION` | correction state changed during resolution |
150
+ | `CORRECTION_STATE_DIFFERS_FROM_ITS_PRE_WRITE_SNAPSHOT_NON_APPLICATION_CANNOT_BE_RESOLVED` | correction state differs from its pre-write snapshot; non-application cannot be resolved |
151
+ | `CORRECTION_SUBASSESSMENT_READ_MISSING` | correction subassessment read missing |
152
+ | `CORRECTION_WAS_RESOLVED_AS_NOT_APPLIED_USE_A_NEW_KEY_FOR_A_CORRECTED_INTENT` | correction was resolved as not applied; use a new key for a corrected intent |
153
+ | `DESTINATION_AND_REASON_FOR_LEAVING_REQUIRED` | destination and reason for leaving required |
154
+ | `DOB_AND_HOUSEHOLD_HEAD_CORRECTIONS_REQUIRE_THEIR_DEDICATED_WORKFLOWS` | DOB and household head corrections require their dedicated workflows |
155
+ | `DOB_HELPER_REQUIRED_FOR_BIRTH_DATE_ANSWERS_AND_CARRY_FORWARD` | DOB helper required for birth-date answers and carry-forward |
156
+ | `DOB_QUESTION_TYPE_CHANGED` | DOB question type changed |
157
+ | `DUPLICATE_ADOPTION_TARGETS` | duplicate adoption targets |
158
+ | `DUPLICATE_CORRECTION_JOURNAL_KEYS` | duplicate correction journal keys |
159
+ | `DUPLICATE_HOUSEHOLD_JOURNAL_KEYS` | duplicate household journal keys |
160
+ | `DUPLICATE_HOUSEHOLD_MEMBER_ENROLLMENT` | duplicate household member enrollment |
161
+ | `DUPLICATE_HOUSEHOLD_REVIEW_IDS` | duplicate household review IDs |
162
+ | `DUPLICATE_JOURNAL_KEYS` | duplicate journal keys |
163
+ | `DUPLICATE_MATCHING_ENROLLMENTS` | duplicate matching enrollments |
164
+ | `DUPLICATE_MATCHING_SUBASSESSMENT_ROWS` | duplicate matching subassessment rows |
165
+ | `DUPLICATE_SEMANTIC_SUBASSESSMENT_ROWS` | duplicate semantic subassessment rows |
166
+ | `DUPLICATE_SUBASSESSMENT_KEY` | duplicate subassessment key |
167
+ | `EMPTY_ENTRY_CORRECTION` | empty entry correction |
168
+ | `EMPTY_ROW_CORRECTION` | empty row correction |
169
+ | `END_DATE_PRECEDES_RECORD_START` | end date precedes record start |
170
+ | `END_DATE_TARGET_INTERVAL_MISSING` | end-date target interval missing |
171
+ | `END_DATE_TARGET_MISSING` | end-date target missing |
172
+ | `ENROLLMENT_ALREADY_JOURNALED_USE_ITS_EXISTING_ENTRY_KEY` | enrollment already journaled; use its existing entry key |
173
+ | `ENROLLMENT_COUNT_UNAVAILABLE_OR_CHANGED` | enrollment count unavailable or changed |
174
+ | `ENROLLMENT_LIST_CONTEXT_MISMATCH` | enrollment list context mismatch |
175
+ | `ENROLLMENT_LIST_EXCEEDS_LIMIT` | enrollment list exceeds limit |
176
+ | `ENROLLMENT_NOT_GROUPED` | standalone enrollment uses adoptEnrollment |
177
+ | `ENROLLMENT_READ_FAILED` | enrollment read failed |
178
+ | `ENTRY_AND_INTERIM_ASSESSMENT_LOCATIONS_REQUIRED_EXIT_LOCATION_MUST_BE_NONEMPTY_WHEN_SUPPLIED` | entry and interim assessment locations required; exit location must be nonempty when supplied |
179
+ | `ENTRY_CHANGED_SINCE_CORRECTION_PREFLIGHT` | entry changed since correction preflight |
180
+ | `ENTRY_CORRECTION_DID_NOT_VERIFY` | entry correction did not verify |
181
+ | `ENTRY_CORRECTION_ENROLLMENT_CONTEXT_MISMATCH` | entry correction enrollment context mismatch |
182
+ | `ENTRY_CORRECTION_MUST_PRECEDE_INTERIM_OR_EXIT_WORKFLOWS` | entry correction must precede interim or exit workflows |
183
+ | `ENTRY_CORRECTION_REJECTED` | correction was resolved as not applied; use a new key for a corrected intent<br>entry correction rejected |
184
+ | `ENTRY_CORRECTION_REQUIRES_AN_OPEN_ENROLLMENT_WITHOUT_REVIEWS` | entry correction requires an open enrollment without reviews |
185
+ | `ENTRY_CORRECTION_UNCERTAIN` | entry correction uncertain<br>uncertain entry correction requires reconciliation |
186
+ | `EXISTING_EXIT_CONFLICTS_WITH_REQUESTED_EXIT` | existing exit conflicts with requested exit |
187
+ | `EXISTING_HOUSEHOLD_REVIEW_CONFLICT` | existing household review conflict |
188
+ | `EXIT_ASSESSMENT_CONFIGURATION_CHANGED` | exit assessment configuration changed |
189
+ | `EXIT_IDENTITY_CHANGED` | exit identity changed |
190
+ | `EXIT_IDENTITY_MISSING` | exit identity missing |
191
+ | `EXIT_PRECEDES_AN_EXISTING_REVIEW` | exit precedes an existing review |
192
+ | `EXIT_SUBGROUP_IDENTITY_CHANGED` | exit subgroup identity changed |
193
+ | `EXIT_SUBGROUP_MEMBERSHIP_MISMATCH` | exit subgroup membership mismatch |
194
+ | `EXIT_UI_AND_AUTHORITATIVE_ENROLLMENT_SESSION_REQUIRED` | exit UI and authoritative enrollment session required |
195
+ | `EXIT_UI_DESTINATION_AND_REASON_FOR_LEAVING_REQUIRED` | exit UI, destination and reason for leaving required |
196
+ | `EXPLICIT_ENROLLMENT_TARGETS_REQUIRED` | explicit enrollment targets required |
197
+ | `HEAD_AND_HOUSEHOLD_MEMBERS_REQUIRED` | head and household members required |
198
+ | `HEAD_CLIENT_ID_REQUIRED` | head client ID required |
199
+ | `HEAD_OF_HOUSEHOLD_ASSESSMENT_OPTION_UNRESOLVED` | head of household assessment option unresolved |
200
+ | `HEAD_OF_HOUSEHOLD_CHANGE_IS_A_RED_FLAG_USE_A_SEPARATE_RETROACTIVE_CORRECTION_FOR_A_MEMBER_WITH` | head of household change is a red flag; use a separate retroactive correction for a member with the same first entry date, or exit all enrollments and create a fresh enrollment |
201
+ | `HOUSEHOLD_API_AND_AUTHORITATIVE_ENROLLMENT_SESSION_REQUIRED` | household API and authoritative enrollment session required |
202
+ | `HOUSEHOLD_DISCOVERY_AMBIGUOUS` | a unique current household matching the full cohort, head and dates is required |
203
+ | `HOUSEHOLD_DISCOVERY_COHORT_CHANGED` | household membership changed during discovery<br>linked enrollment cohort changed during discovery |
204
+ | `HOUSEHOLD_DISCOVERY_DETAIL_INVALID` | complete linked enrollment cohort required<br>group flag conflicts with linked cohort |
205
+ | `HOUSEHOLD_DISCOVERY_INPUT_INVALID` | head client and enrollment IDs required |
206
+ | `HOUSEHOLD_DISCOVERY_LIST_INCOMPLETE` | household list count unavailable or changed<br>incomplete household list<br>invalid household list |
207
+ | `HOUSEHOLD_DISCOVERY_NOT_FOUND` | a unique current household matching the full cohort, head and dates is required |
208
+ | `HOUSEHOLD_DISCOVERY_READERS_REQUIRED` | GWT detail and complete household readers required |
209
+ | `HOUSEHOLD_ENROLLMENT_CONTEXT_MISMATCH` | household enrollment context mismatch |
210
+ | `HOUSEHOLD_ENROLLMENT_IDENTITY_CHANGED` | household enrollment identity changed |
211
+ | `HOUSEHOLD_ENROLLMENT_MEMBERS_CHANGED` | household enrollment members changed |
212
+ | `HOUSEHOLD_EXITS_ARE_NOT_LINKED` | household exits are not linked |
213
+ | `HOUSEHOLD_HEAD_MISMATCH_HEAD_CHANGES_ARE_A_RED_FLAG_AND_REQUIRE_SEPARATE_RETROACTIVE` | household head mismatch; head changes are a red flag and require separate retroactive correction or a fresh enrollment |
214
+ | `HOUSEHOLD_KEY_CONFLICTS_WITH_PREVIOUS_INTENT` | household key conflicts with previous intent |
215
+ | `HOUSEHOLD_MEMBERS_UNAVAILABLE` | household members unavailable |
216
+ | `HOUSEHOLD_MEMBERSHIP_CHANGED_WHILE_ACQUIRING_LOCKS_RETRY` | household membership changed while acquiring locks; retry |
217
+ | `HOUSEHOLD_ORIGINAL_HEAD_AND_COMPLETE_ENROLLMENT_COHORT_REQUIRED` | household, original head and complete enrollment cohort required |
218
+ | `HOUSEHOLD_PROGRAM_AND_SUPPORTED_ENTRY_EXIT_TYPE_REQUIRED` | household, program and supported Entry/Exit type required |
219
+ | `HOUSEHOLD_REVIEW_IDENTITY_CHANGED` | household review identity changed |
220
+ | `HOUSEHOLD_REVIEW_MEMBERSHIP_MISMATCH` | household review membership mismatch |
221
+ | `HOUSEHOLD_REVIEWS_UNAVAILABLE` | household reviews unavailable |
222
+ | `INCOMPLETE_ENROLLMENT_LIST` | incomplete enrollment list |
223
+ | `INCOMPLETE_EXISTING_EXIT_METADATA` | incomplete existing exit metadata |
224
+ | `INDEPENDENT_CONFIRMATION_REQUIRED` | independent non-application confirmation required |
225
+ | `INDIVIDUAL_ENROLLMENT_CONTEXT_MISMATCH` | individual enrollment context mismatch |
226
+ | `INDIVIDUAL_EXIT_MEMBERSHIP_MISMATCH` | individual exit membership mismatch |
227
+ | `INTERIM_DID_NOT_VERIFY` | interim did not verify |
228
+ | `INVALID_ASSESSMENT_PLAN` | invalid assessment plan |
229
+ | `INVALID_CORRECTION_CLIENT_OR_ROWS` | invalid correction client or rows |
230
+ | `INVALID_CORRECTION_DATE` | invalid correction date |
231
+ | `INVALID_CORRECTION_EFFECTIVE_DATE` | invalid correction effective date |
232
+ | `INVALID_ENROLLMENT_DATA_ADOPTED_MEMBERS_REQUIRE_MATCHING_PROGRAM_AND_HEAD_COVERAGE` | invalid enrollment data: adopted members require matching program and head coverage |
233
+ | `INVALID_ENROLLMENT_DATA_ENROLLMENT_HAS_NO_HEAD_OF_HOUSEHOLD` | invalid enrollment data: enrollment has no head of household |
234
+ | `INVALID_ENROLLMENT_DATA_HEAD_OF_HOUSEHOLD_RELATIONSHIP_IS_MISSING_OR_CLEARED` | invalid enrollment data: head of household relationship is missing or cleared |
235
+ | `INVALID_ENROLLMENT_DATA_HOUSEHOLD_HAS_NO_ACTIVE_HEAD_OF_HOUSEHOLD` | invalid enrollment data: household has no active head of household |
236
+ | `INVALID_ENROLLMENT_DATA_MEMBER_ENROLLMENT_WOULD_EXIST_WITHOUT_AN_ENROLLED_HEAD_OF_HOUSEHOLD` | invalid enrollment data: member enrollment would exist without an enrolled head of household; the original head must remain enrolled through every member exit; reconcile the data or exit all members and create a fresh enrollment |
237
+ | `INVALID_ENROLLMENT_LIST` | invalid enrollment list |
238
+ | `INVALID_HOUSEHOLD_ENROLLMENT_MEMBERS` | invalid household enrollment members |
239
+ | `INVALID_HOUSEHOLD_JOURNAL` | invalid household journal |
240
+ | `INVALID_OR_CONFLICTING_CORRECTION_ANSWERS_AND_CLEARS` | invalid or conflicting correction answers and clears |
241
+ | `INVALID_OR_DUPLICATE_END_DATE_TARGET` | invalid or duplicate end-date target |
242
+ | `INVALID_OR_REUSED_SUBASSESSMENT_ID` | invalid or reused subassessment ID |
243
+ | `INVALID_STORED_CORRECTION_DATE` | invalid stored correction date |
244
+ | `INVALID_STORED_CORRECTION_NUMBER` | invalid stored correction number |
245
+ | `INVALID_SUBASSESSMENT_PLAN` | invalid subassessment plan |
246
+ | `INVALID_WORKFLOW_JOURNAL` | invalid workflow journal |
247
+ | `KEY_CONFLICTS_WITH_PREVIOUS_INTENT` | key conflicts with previous intent |
248
+ | `LINKED_HOUSEHOLD_ENROLLMENT_REQUIRES_EXITHOUSEHOLD` | linked household enrollment requires exitHousehold |
249
+ | `LOOKUP_WRITE_LABEL_UNAVAILABLE` | lookup write label unavailable |
250
+ | `MANDATORY_CLEAR_CONFLICT` | mandatory clear conflict |
251
+ | `MANDATORY_CORRECTION_FIELD_COULD_NOT_BE_ENCODED` | mandatory correction field could not be encoded |
252
+ | `MANDATORY_CORRECTION_FIELD_REQUIRES_AN_EXPLICIT_ANSWER_OR_CLEAR` | mandatory correction field requires an explicit answer or clear |
253
+ | `MANDATORY_CORRECTION_LOOKUP_LABEL_UNAVAILABLE` | mandatory correction lookup label unavailable |
254
+ | `MATCHING_CORRECTION_JOURNAL_REQUIRED` | matching correction journal required |
255
+ | `MEMBER_ALREADY_HAS_AN_INDEPENDENT_MATCHING_ENROLLMENT` | member already has an independent matching enrollment |
256
+ | `MEMBER_CLIENT_AND_ASSESSMENT_REQUIRED` | member client and assessment required |
257
+ | `MEMBER_ENROLLMENT_HAS_NOT_BEEN_ATTACHED` | member enrollment has not been attached |
258
+ | `MEMBER_ENTRY_DATE_PORT_REQUIRED` | member entry date port required |
259
+ | `MEMBER_EXIT_IDENTITY_MISSING` | member exit identity missing |
260
+ | `MEMBER_IS_NOT_ACTIVE_IN_HOUSEHOLD_AT_ENTRY` | member is not active in household at entry |
261
+ | `MOBILE_ANSWER_EXISTS` | mobile answer exists<br>mobile scalar answer already exists at the effective date |
262
+ | `MOBILE_PREFLIGHT_UNAVAILABLE` | mobile preflight unavailable<br>mobile scalar preflight unavailable |
263
+ | `MULTIPLE_END_DATE_OVERRIDES` | multiple end-date overrides |
264
+ | `NEW_MEMBER_MUST_REMAIN_ENROLLED` | new member must remain enrolled |
265
+ | `NOT_APPLICABLE_EXPLANATION_REQUIRED` | not-applicable explanation required |
266
+ | `OPEN_HOUSEHOLD_ENROLLMENT_REQUIRED_TO_ADD_A_MEMBER` | open household enrollment required to add a member |
267
+ | `PARTIAL_EXISTING_HOUSEHOLD_EXIT_REQUIRES_RECONCILIATION` | partial existing household exit requires reconciliation |
268
+ | `PARTIAL_HOUSEHOLD_REVIEW_REQUIRES_RECONCILIATION` | partial household review requires reconciliation |
269
+ | `PICKLIST_CATALOG_CONTEXT_MISMATCH` | picklist catalog client mismatch<br>picklist catalog context mismatch |
270
+ | `PROGRAM_AND_SUPPORTED_ENTRY_EXIT_TYPE_REQUIRED` | program and supported Entry/Exit type required |
271
+ | `PROGRAM_ASSESSMENT_CONFIGURATION_CHANGED` | program assessment configuration changed |
272
+ | `PROGRAM_ASSESSMENT_DEFAULT_UNRESOLVED` | program assessment default unresolved |
273
+ | `PROVIDER_DISCOVERY_MISMATCH` | provider discovery mismatch |
274
+ | `RECORD_ALREADY_HAS_A_DIFFERENT_END_DATE` | record already has a different end date |
275
+ | `RESOLUTION_EVIDENCE_REFERENCE_CHANGED` | resolution evidence reference changed |
276
+ | `REST_API_DISCOVERY_ENROLLMENT_HELPERS_AND_DURABLE_STORE_REQUIRED` | REST API, discovery, enrollment helpers and durable store required |
277
+ | `REVIEW_FOLLOWS_PROJECT_EXIT` | review follows project exit |
278
+ | `REVIEW_PRECEDES_ENTRY` | review precedes entry |
279
+ | `ROW_CLOSURE_MUST_FALL_BETWEEN_ENTRY_AND_ASSESSMENT` | row closure must fall between entry and assessment |
280
+ | `ROW_REGISTRATION_BINDING_CONFLICT` | row key or recordset is already bound differently |
281
+ | `ROW_REGISTRATION_CONTEXT_MISMATCH` | enrollment identity changed during row registration |
282
+ | `ROW_REGISTRATION_ENTRY_REQUIRED` | verified enrollment workflow required |
283
+ | `ROW_REGISTRATION_INPUT_INVALID` | unique row keys, parents and positive recordset IDs required<br>valid registration key, entry key, client and explicit rows required |
284
+ | `ROW_REGISTRATION_INTENT_CONFLICT` | registration key conflicts with previous intent |
285
+ | `ROW_REGISTRATION_PARENT_INVALID` | subassessment parent not in entry definition |
286
+ | `ROW_REGISTRATION_READ_UNAVAILABLE` | complete row registration read required |
287
+ | `ROW_REGISTRATION_READER_REQUIRED` | authoritative enrollment reader required |
288
+ | `ROW_REGISTRATION_ROW_MISSING` | selected row is missing or ambiguous at the entry date or current read |
289
+ | `SAVED_ENROLLMENT_MISSING` | saved enrollment missing |
290
+ | `SAVED_ENROLLMENT_MISSING_OR_CHANGED` | saved enrollment missing or changed |
291
+ | `SESSION_INTERRUPTED` | session interrupted<br>upstream workflow operation failed |
292
+ | `STABLE_CORRECTION_KEY_REQUIRED` | stable correction key required |
293
+ | `STABLE_KEY_REQUIRED` | stable key required |
294
+ | `STANDARD_ASSESSMENT_WRITES_AND_AUTHORITATIVE_ENROLLMENT_READER_REQUIRED_FOR_CORRECTIONS` | standard assessment writes and authoritative enrollment reader required for corrections |
295
+ | `STORED_EXIT_DETAILS_CONFLICT_WITH_REQUESTED_EXIT` | stored exit details conflict with requested exit |
296
+ | `SUBASSESSMENT_READ_MISSING` | subassessment read missing |
297
+ | `TIMEZONE_QUALIFIED_WHOLE_SECOND_DATE_REQUIRED` | timezone-qualified whole-second date required |
298
+ | `UNCERTAIN_ENTRY_CORRECTION_REQUIRES_RECONCILIATION` | uncertain entry correction requires reconciliation |
299
+ | `UNCERTAIN_HOUSEHOLD_EXIT_REQUIRES_RECONCILIATION` | uncertain household exit requires reconciliation |
300
+ | `UNCERTAIN_MEMBER_ENTRY_DATE_REQUIRES_RECONCILIATION` | uncertain member entry date requires reconciliation |
301
+ | `UNCERTAIN_WRITE_REQUIRES_RECONCILIATION` | uncertain write requires reconciliation |
302
+ | `UNFINISHED_HOUSEHOLD_WORKFLOW_MUST_BE_RECONCILED_FIRST` | unfinished household workflow must be reconciled first |
303
+ | `UNIQUE_MEMBERS_INCLUDING_HEAD_REQUIRED` | unique members including head required |
304
+ | `UNKNOWN_CARRY_FORWARD_FIELD` | unknown carry-forward field |
305
+ | `UNKNOWN_OR_INVALID_CORRECTION_FIELDS` | unknown or invalid correction fields |
306
+ | `UNKNOWN_OR_INVALID_INPUT_FIELDS` | unknown or invalid input fields |
307
+ | `UNSELECTED_HOUSEHOLD_MEMBER_CHANGED` | unselected household member changed |
308
+ | `UNSELECTED_MEMBER_EXIT_IDENTITY_MISSING` | unselected member exit identity missing |
309
+ | `UNSUPPORTED_CORRECTION_FIELD_TYPE` | unsupported correction field type |
310
+ | `UNSUPPORTED_ENTRY_EXIT_TYPE_IN_ASSESSMENT_LOCATIONS` | unsupported Entry/Exit type in assessment locations |
311
+ | `UNSUPPORTED_MANDATORY_CORRECTION_FIELD` | unsupported mandatory correction field |
312
+ | `UNSUPPORTED_MANDATORY_FIELD_TYPE` | unsupported mandatory field type |
313
+ | `UNSUPPORTED_YES_NO_CLEAR` | unsupported yes no clear<br>yes/no answers cannot be cleared |
314
+ | `VERIFIED_CORRECTION_CANNOT_BE_REJECTED` | verified correction cannot be rejected |
315
+ | `VERIFIED_ENROLLMENT_WORKFLOW_REQUIRED` | verified enrollment workflow required |
316
+ | `VERIFIED_ENTRY_CORRECTION_CHANGED` | verified entry correction changed |
317
+ | `VERIFIED_EXIT_MISSING` | verified exit missing |
318
+ | `VERIFIED_HOUSEHOLD_ENTRY_REQUIRED` | verified household entry required |
319
+ | `VERIFIED_HOUSEHOLD_ENTRY_WITH_IDENTICAL_MEMBERS_REQUIRED_UNKNOWN_SELECTED_MEMBER` | verified household entry with identical members required; unknown selected member |
320
+ | `VERIFIED_ROW_CORRECTION_CHANGED` | verified row correction changed |
321
+ | `VERIFIED_STEP_CHANGED` | verified step changed |
322
+ | `WORKFLOW_CONFIGURATION_INVALID` | invalid workflow configuration<br>workflow configuration invalid |
323
+ | `WORKFLOW_FAILED` | Unclassified workflow failure; inspect cause and commit markers. |
324
+ | `WORKFLOW_JOURNAL_INVALID` | invalid workflow journal |
325
+ | `WORKFLOW_PENDING` | unfinished household workflow must be reconciled first<br>unfinished workflow must be reconciled first |
326
+ | `WORKFLOW_SCHEMA_CHANGED` | program assessment configuration changed |
327
+ | `WORKFLOW_UPSTREAM_FAILURE` | upstream workflow operation failed<br>workflow upstream failure |
328
+ | `WORKFLOW_VALIDATION_FAILED` | upstream workflow operation failed<br>workflow validation failed |
329
+ | `YES_NO_ANSWERS_CANNOT_BE_CLEARED` | yes/no answers cannot be cleared |
330
+ | `YES_NO_WRITE_MODE_CHANGED_SINCE_THIS_WORKFLOW_STARTED` | yes/no write mode changed since this workflow started |
331
+ | `YESNOWRITES_MUST_BE_MOBILE_OR_STANDARD_WITH_WRITELABEL_AND_ASSESSMENTS_CREATE` | yesNoWrites must be mobile, or standard with writeLabel and assessments.create |
package/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.8.1 - 2026-11-11
4
+
5
+ - Recheck mobile scalar collisions immediately before the first scalar-stage attempt, including answers that appeared after enrollment or review creation. Refuse known collisions and unavailable evidence without sending the batch or marking that stage attempted; preserve earlier lifecycle writes and prior-attempt reconciliation.
6
+ - Give complete-cohort adoption, standalone adoption of grouped entries, and changed verified steps specific stable codes instead of `WORKFLOW_FAILED`.
7
+ - Generate the complete enrollment workflow error-code reference in `API.md`; fail generation for new constant refusal reasons without codes, including conditional branches.
8
+ - Clarify that household discovery verifies current membership, not the original historical association, and explicit IDs do not bypass active-membership validation.
9
+ - Record the additional reported adoption/interim/correction acceptance from GitLab issues #5–#7, and distinguish the issue #2 report from the successful follow-up batch controls. The upstream partial-commit cause remains unresolved; no score-specific batch splitting is introduced.
10
+
3
11
  ## 0.8.0 - 2026-11-11
4
12
 
5
13
  - Resolve full lookup write labels without existing answers using `enrollmentSession.readPicklistOptions` and `createCatalogWriteLabelResolver`.
@@ -251,20 +251,20 @@ export function createEnrollmentWorkflow({api,discovery,helpers,store,enrollment
251
251
  // assessment's own rows. Read at the later of now and the assessment instant.
252
252
  const readCurrent=async()=>questions(await api.assessments.get({path:{clientId:input.clientId,assessmentCode:code},query:{dateEffective:new Date(Math.max(Date.now(),Date.parse(at))).toISOString().slice(0,19)+'Z'}}));
253
253
  // A generic read at exactly the effective instant returns null for yes/no answers saved at
254
- // that instant; one second later returns them. Only the yes/no step needs this.
254
+ // that instant; one second later returns them. Scalar preflight and verification use this.
255
255
  const readSaved=async()=>questions(await api.assessments.get({path:{clientId:input.clientId,assessmentCode:code},query:{dateEffective:new Date(Date.parse(at)+1000).toISOString()}}));
256
- const initial=await read();
257
- if(!record&&plan.scalar.length){
258
- const saved=await readSaved();
256
+ const assertScalarInsertable=saved=>{
259
257
  const existing=plan.scalar.map(q=>({q,row:saved.find(r=>r.virt_field_name===q.virt_field_name)}));
260
- const complete=existing.every(({q,row})=>row&&sameAnswer(q,row.answer_value)&&restDate(row.date_effective)===restDate(at));
258
+ const complete=existing.every(({q,row})=>row&&row.question_type===q.question_type&&sameAnswer(q,row.answer_value)&&restDate(row.date_effective)===restDate(at));
261
259
  if(!complete){
262
260
  const unavailable=existing.filter(({q,row})=>!row||row.question_type!==q.question_type||row.date_effective!=null&&(!Number.isFinite(restDate(row.date_effective))||restDate(row.date_effective)>restDate(at))||row.date_effective==null&&row.answer_value!=null&&row.answer_value!=='').map(({q})=>q.virt_field_name);
263
- if(unavailable.length)fail('mobile scalar preflight unavailable',{code:'MOBILE_PREFLIGHT_UNAVAILABLE',fields:unavailable});
261
+ if(unavailable.length)fail('mobile scalar preflight unavailable',{code:'MOBILE_PREFLIGHT_UNAVAILABLE',step:'scalars',fields:unavailable});
264
262
  const collisions=existing.filter(({row})=>restDate(row.date_effective)===restDate(at)).map(({q})=>q.virt_field_name);
265
- if(collisions.length)fail('mobile scalar answer already exists at the effective date',{code:'MOBILE_ANSWER_EXISTS',fields:collisions});
263
+ if(collisions.length)fail('mobile scalar answer already exists at the effective date',{code:'MOBILE_ANSWER_EXISTS',step:'scalars',fields:collisions});
266
264
  }
267
- }
265
+ };
266
+ const initial=await read();
267
+ if(!record&&plan.scalar.length)assertScalarInsertable(await readSaved());
268
268
  if(expectedHead===input.clientId&&headQuestion)checkHeadAnswer(proposedHead?.answer_value??initial.find(q=>q.virt_field_name==='HUD_RELATIONTOHOH')?.answer_value);
269
269
  for(const field of plan.carry.filter(field=>field!=='SVPPROFDOB')){if(kind==='entry'&&record?.correctedScalars?.[field])continue;const q=initial.find(q=>q.virt_field_name===field);if(!q||(q.question_type==='sub_assessment'?!q.sub_assessment_answer_list?.length:!q.answer_value))fail('carry-forward field is empty');}
270
270
  // Resend mandatory answers with the standard yes/no write. A blank write must not hide a recorded value.
@@ -370,7 +370,13 @@ export function createEnrollmentWorkflow({api,discovery,helpers,store,enrollment
370
370
  },()=>api.subassessments.setEndDate({clientId:input.clientId,assessmentCode:code,effectiveDate:at,parentField:end.parentField,recordsetId:end.recordsetId,endDateField:end.endDateField,endDate:end.endDate}));
371
371
  }
372
372
  if(plan.scalar.length)await step('scalars',async()=>{
373
- const source=kind==='entry'&&record.correctedScalars?await readSaved():await read();return {found:plan.scalar.every(original=>{const q=kind==='entry'?record.correctedScalars?.[original.virt_field_name]??original:original,r=source.find(r=>r.virt_field_name===q.virt_field_name);return r&&sameAnswer(q,r.answer_value)&&(q.answer_value===''||restDate(r.date_effective)===restDate(at));}),value:plan.scalar.map(q=>q.virt_field_name)};
373
+ const source=await readSaved();
374
+ const found=plan.scalar.every(original=>{const q=kind==='entry'?record.correctedScalars?.[original.virt_field_name]??original:original,r=source.find(r=>r.virt_field_name===q.virt_field_name);return r&&r.question_type===q.question_type&&sameAnswer(q,r.answer_value)&&(q.answer_value===''||restDate(r.date_effective)===restDate(at));});
375
+ // Enrollment/review creation or another writer can populate shared questions after
376
+ // initial preflight. Refuse a known collision before journaling this first attempt.
377
+ // Prior attempts remain reconciliation-only, even when only part of a batch saved.
378
+ if(!found&&!record.steps.scalars)assertScalarInsertable(source);
379
+ return {found,value:plan.scalar.map(q=>q.virt_field_name)};
374
380
  },()=>api.assessmentsMobile.addAnswer({path:{clientId:input.clientId,assessmentCode:code},query:{dateEffective:at},body:plan.scalar}));
375
381
  // Standard writes update existing dated rows and change only the answers sent.
376
382
  if(plan.yesNo.length){const body=[...plan.yesNo,...mandatory.map(({expected,...q})=>q)],expected=[...plan.yesNo,...mandatory.map(({expected,...q})=>({...q,answer_value:expected}))];
@@ -1,5 +1,8 @@
1
1
  // Stable public codes. Keep code values when changing their associated messages.
2
2
  const codes = new Map([
3
+ ["adoption requires the complete linked enrollment cohort", "ADOPTION_COMPLETE_COHORT_REQUIRED"],
4
+ ["linked household enrollment requires adoptHouseholdEnrollment", "ADOPTION_HOUSEHOLD_REQUIRED"],
5
+ ["verified step changed", "VERIFIED_STEP_CHANGED"],
3
6
  ["DOB and household head corrections require their dedicated workflows", "DOB_AND_HOUSEHOLD_HEAD_CORRECTIONS_REQUIRE_THEIR_DEDICATED_WORKFLOWS"],
4
7
  ["DOB helper required for birth-date answers and carry-forward", "DOB_HELPER_REQUIRED_FOR_BIRTH_DATE_ANSWERS_AND_CARRY_FORWARD"],
5
8
  ["DOB question type changed", "DOB_QUESTION_TYPE_CHANGED"],
@@ -73,7 +73,7 @@ Reproduced on November 2, 2026 in a sandbox tenant with synthetic clients:
73
73
 
74
74
  - **Mobile `AddAssessmentAnswer` does not store yes/no values.** Requests with `"Yes"`, `"1"`/`"0"`, and `"true"`/`"false"` returned success and created a row at the intended effective date, but its value read back empty through both the generic and mobile reads. An integrator's 30-answer entry batch showed the same result.
75
75
  - **Mobile writes are insert-only per question and effective date.** A second mobile write to any question that already has a row at that date returned HTTP 400 `An error occurred while saving the entity changes`. This affected yes/no and integer questions alike, so a mobile retry cannot repair an empty yes/no row.
76
- - **A failed mobile batch can still save part of its answers.** On November 2, 2026, an integrator's 15-answer batch returned HTTP 400 a few seconds after the client's household enrollment and entry answers were created. Afterwards one of the 15 answers was stored and the others were empty. Its error body was not captured, and the client's rows just before the send were not read. Later sends of the batch failed with the insert-only error above because that row existed. On November 3, the same byte-identical batch returned 200 and read back completely on fresh synthetic clients with no enrollment, an enrolled single adult, and an enrolled household head; batches with and without the score total question, and in either order, also succeeded. The cause is unknown; a race with enrollment creation is possible but unproven. Treat a failed mobile write as uncertain and reconcile it from a read, as the enrollment workflow does; never resend it.
76
+ - **A failed mobile batch can still save part of its answers.** An integrator's November 2, 2026 report describes a 15-answer batch returning HTTP 400 with only the score total saved. GitLab issue #2 reports a captured entity-save error and no prior rows; separate retained notes describe a send shortly after household enrollment and do not retain a before-write snapshot. These accounts do not establish the same pre-write state. Later sends of the batch failed with the insert-only error above because that row existed. On November 3, the same byte-identical batch returned 200 and read back completely on fresh synthetic clients with no enrollment, an enrolled single adult, and an enrolled household head; batches with and without the score total question, and in either order, also succeeded. The cause is unknown; a race with enrollment creation is possible but unproven. Treat a failed mobile write as uncertain and reconcile it from a read, as the enrollment workflow does; never resend it.
77
77
  - **Standard `assessments.create` stores `"Yes"`/`"No"`.** It rejects `"true"`/`"false"`. It updates existing rows at the same effective date, including empty yes/no rows created by mobile writes, and changed only the answers it was sent in a full before/after comparison.
78
78
  - **Every standard write must include every mandatory answer**, even one already recorded at that date; otherwise it returns `All mandatory answers are not provided`. A conditionally mandatory lookup that does not apply can be sent as `""`; it is stored empty at that date.
79
79
  - **Generic reads omit yes/no answers at exactly their effective instant.** `assessments.get` with `dateEffective` equal to the saved instant returns `null` for those yes/no answers while other types read normally. One second later returns the saved value. The standard create response has the same omission. Mobile reads return the value, but they have no date filter and show only the latest row, so a later record hides the date being checked.
@@ -101,7 +101,9 @@ await writeMobileAnswersChecked(api, {
101
101
 
102
102
  `MobileAnswerPreflightError` has a stable `code`, affected `fields`, and `committed: false`. A collision uses `MOBILE_ANSWER_EXISTS`; unavailable evidence uses `MOBILE_PREFLIGHT_UNAVAILABLE`. The helper checks scalars, including questions shared across instruments, but does not detect duplicate subassessment rows. It makes one write after a successful preflight and does not retry or journal it. An error from that write retains its original type and possible uncertain outcome. Reads and writes are not atomic against other users. Pass the original instant; the SDK transport applies any configured mobile date correction.
103
103
 
104
- New enrollment workflow plans also check scalar collisions before creating an enrollment or sending a mobile batch. Existing pending journal operations retain their reconciliation behavior.
104
+ Enrollment workflow plans check scalar collisions before creating an enrollment and again immediately before the first scalar batch. An answer can appear during enrollment/review creation or from another writer after the earlier read. If the complete requested scalar state already matches, the workflow verifies it without another insert. If only some rows occupy the instant, or their values conflict, it refuses before marking the scalar stage attempted. Unavailable evidence also blocks that first attempt.
105
+
106
+ If an earlier enrollment, review or exit stage was already attempted, this refusal still has `committed: 'unknown'` for the overall operation and `step: 'scalars'`. Keep the existing key and journal; the enrollment may already exist. Reconcile the stored answers before resuming. Previously attempted scalar stages retain their normal uncertain-write reconciliation and are never resent or split automatically. No read-before-write check eliminates changes after the last read.
105
107
 
106
108
  ## Race and ethnicity combinations
107
109
 
@@ -96,7 +96,7 @@ The integrator reports these training-tenant results on November 10 and 11, 2026
96
96
  - Three pending 0.5.0 corrections were refused without confirmation and resolved as `rejected` with confirmation.
97
97
  - Earlier 0.5.0 runs verified individual/household adoption followed by exit, household re-enrollment after exit, and path corrections in both directions for individuals and households, with no-write same-key repeats.
98
98
 
99
- The integrator has not tested household adoption followed by `createHouseholdInterim`, scalar correction on an adopted enrollment, or a new correction key after resolution. Synthetic tests do not replace that acceptance.
99
+ Further integrator reports in GitLab issues #5, #6 and #7 confirm three additional 0.6.0 paths on November 11: household adoption followed by `createHouseholdInterim` (a session interruption reauthenticated, and a same-key retry verified without a duplicate review); scalar-only correction on an adopted enrollment (same-key retry wrote nothing); and a new correction key after the original correction was resolved as not applied. These are reported live results, not an independent SDK-team rerun. Row corrections on adopted entries require the later `registerEntryRows` API.
100
100
 
101
101
  The 0.7.0 label resolver, group flags, household discovery, row registration, error codes and mobile collision preflight have local regression coverage, not new live tenant acceptance. In 0.7.0, full write labels remained unresolved for unobserved options; the 0.8.0 catalog reader below removes that limitation when UI catalog access is available. Household discovery refuses ambiguous current-membership matches. Yes/no blanking remains unsupported; no alternative route has been verified.
102
102
 
@@ -280,7 +280,7 @@ const adopted = await workflow.adoptHouseholdEnrollment({
280
280
  });
281
281
  ```
282
282
 
283
- Discovery reads the linked enrollment cohort through GWT and matches it against complete current household membership reads. It requires one household containing the cohort, the same head, and membership dates covering each entry. It verifies that household again before returning `householdSource: 'verified-membership'`. This is a verified membership match, not a native historical household pointer on the enrollment. Zero matches, multiple matches, incomplete reads or changed relationships fail without adopting. Historical households that no longer match require the existing explicit adoption inputs and their validation. No record is written to WellSky during discovery or adoption.
283
+ Discovery reads the linked enrollment cohort through GWT and matches it against complete current household membership reads. It requires one household containing the cohort, the same head, and membership dates covering each entry. It verifies that household again before returning `householdSource: 'verified-membership'`. This is a verified membership match, not a native historical household pointer on the enrollment. Zero matches, multiple matches, incomplete reads or changed relationships fail without adopting. A unique current match does not prove the enrollment's original historical household association. If that original association is required, verify it independently; the SDK does not expose a native historical pointer. Explicit adoption inputs still require active household membership, the matching head and valid dates. Known historical IDs cannot bypass those guards, so inactive or changed historical households remain unsupported. No record is written to WellSky during discovery or adoption.
284
284
 
285
285
  ### Register existing entry rows
286
286
 
@@ -59,7 +59,7 @@ Enrollment workflow errors expose stable `code` values. Common actionable codes
59
59
  | `MOBILE_ANSWER_EXISTS` | A scalar row already occupies the requested mobile effective instant |
60
60
  | `MOBILE_PREFLIGHT_UNAVAILABLE` | The read cannot establish whether a scalar insert is safe |
61
61
 
62
- The [workflow code table](../browser/workflow-error-codes.mjs) maps existing reasons to codes; new guards supply explicit codes. `committed: false` identifies a pre-write refusal for the named operation. `'unknown'` requires journal/readback reconciliation. Resolution errors separately retain `originalCommitted: 'unknown'` for the original correction. HTTP status and causes are exposed where captured; they are not proof of non-application.
62
+ The complete [SDK-owned code inventory](../API.md#enrollment-workflow-errors) is generated from the guards and stable reason map. Generation rejects new constant refusal reasons that have no specific code, including conditional branches. `committed: false` identifies a pre-write refusal for the named operation. `'unknown'` requires journal/readback reconciliation. Resolution errors separately retain `originalCommitted: 'unknown'` for the original correction. HTTP status and causes are exposed where captured; they are not proof of non-application.
63
63
 
64
64
  Pending 0.5.0 corrections may have no saved `failure` kind or status. Keep your own original failure evidence for the confirmation callback. Do not repeat the write to recover missing diagnostics.
65
65
 
@@ -36,8 +36,16 @@ After a successful standard write, the generic assessment read with `dateEffecti
36
36
 
37
37
  ## A failed mobile batch saved one of its answers
38
38
 
39
- Observed by an integrator on November 2, 2026, with synthetic records. A 15-answer `AddAssessmentAnswer` batch (1 `date`, 13 `lookup`, 1 `int`) was sent a few seconds after the client's household enrollment and entry-assessment answers were created. It returned HTTP 400; the error body was not captured. Afterwards the `int` answer was stored at the correct effective date and the other 14 were empty. The client's rows for this assessment were not read just before the send, so the absence of prior rows is inferred from the integration's own writes. Resends failed as in [the existing-row case](#mobile-writes-fail-for-an-existing-row).
39
+ GitLab issue #2 reports a November 2, 2026 synthetic-client test: a combined 15-answer batch (1 `date`, 13 `lookup`, 1 `int`) returned HTTP 400 with `An error occurred while saving the entity changes. See the inner exception for details.` Only the integer score answer was stored afterwards. The issue says no row existed beforehand, that the error body was captured, and that the non-score batch and score-only batch each succeeded separately.
40
40
 
41
- On November 3, the byte-identical batch returned 200 and read back completely on fresh synthetic clients without an enrollment, an enrolled single adult, and an enrolled household head. Splitting the batch, reordering it, or omitting the `int` answer made no difference. The failure has not been reproduced.
41
+ Separate retained run notes describe a batch sent shortly after household enrollment and entry answers, without a retained before-write assessment snapshot or raw error body. These accounts differ in their stated evidence. The SDK team cannot establish that they describe identical attempts or independently confirm their pre-write state. An existing answer shared by two instruments is one possible collision source, not an established explanation of the reported failure.
42
42
 
43
- **Ask WellSky:** Can a mobile write that returns HTTP 400 commit part of its batch? Is a write to an assessment assigned to the provider affected by work the server does after enrollment creation?
43
+ On November 3, the byte-identical batch returned 200 and read back completely on fresh synthetic clients without an enrollment, an enrolled single adult, and an enrolled household head. Splitting the batch, reordering it, or omitting the `int` answer made no difference in those successful controls. The original failure remains unreproduced in that follow-up. These controls do not disprove the original report or establish a score-specific serialization rule.
44
+
45
+ ## SDK guard for an observed collision after preflight
46
+
47
+ A November 11 synthetic regression demonstrated a narrower SDK defect: enrollment creation populated a scalar after the initial preflight; the scalar-stage read observed that occupied instant, but the workflow still submitted the full mobile batch. SDK 0.8.1 now refuses that first scalar attempt before marking it attempted. This protects against an observed collision and unavailable evidence after an earlier lifecycle stage. It does not establish the cause of issue #2 or make mobile batches atomic.
48
+
49
+ A separate synthetic partial-commit control returned HTTP 400 after saving only one answer. Its stage stayed pending, same-key retries sent nothing, and later row stages did not run. Preserve that uncertain-write behavior. Do not split, replay, change the effective date, or replace the key to work around an already-attempted batch.
50
+
51
+ **Ask WellSky:** Can a mobile write that returns HTTP 400 commit part of its batch? Is a write to an assessment assigned to the provider affected by work the server does after enrollment creation? Which server-side diagnostic can identify the failing question and database constraint?
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@choiceapps/communityservices-sdk",
3
- "version": "0.8.0",
3
+ "version": "0.8.1",
4
4
  "description": "SDK for WellSky Community Services (HMIS), formerly ServicePoint, Client API V2 and V3",
5
5
  "type": "module",
6
6
  "main": "./dist/cjs/index.js",