Skip to content

feat(series): conditional early termination and custom completion message#1393

Merged
joshunrau merged 6 commits into
DouglasNeuroInformatics:mainfrom
joshunrau:dev
Jul 8, 2026
Merged

feat(series): conditional early termination and custom completion message#1393
joshunrau merged 6 commits into
DouglasNeuroInformatics:mainfrom
joshunrau:dev

Conversation

@joshunrau

Copy link
Copy Markdown
Collaborator

Summary

Adds conditional early-termination support to SERIES instruments, along with the supporting renderer, gateway, and API changes needed to make it work end-to-end (in-person and remote assignments).

Series feature

  • terminate predicate (params.terminate): called after each item is submitted; returning true ends the series early so the remaining items are never administered. The terminating item's own record is still saved. Example use: a consent form as the first item that, when declined, skips the rest.
  • completionMessage (params.completionMessage): returns a custom localized ({ en, fr }) message for the completion screen, or null for the default. Receives { terminated, itemName } so the message can differ on early exit (e.g. "the questionnaire was not administered because you did not consent").
  • Typed authoring via generics: defineSeriesInstrument now infers the item-name literal union, so terminate/completionMessage receive a strongly-typed itemName. Item data is a generic defaulting to any, so it can be annotated inline without a cast.

Renderer / gateway

  • SeriesInstrumentRenderer evaluates terminate, jumps to the completion screen on early exit, renders the custom completionMessage, and emits a new complete flag on the submit payload.
  • Gateway (Root.tsx) marks a remote assignment COMPLETE via the complete flag instead of comparing against the last item's id, so early termination completes the assignment correctly.

API synchronizer (gateway.synchronizer.ts)

  • Fix: a completed series may now contain fewer records than items (an early-terminated prefix); previously the sync threw on a strict length mismatch. Validation now accepts a non-empty prefix and checks cipher-text/symmetric-key counts against each other.
  • Better error logging: per-step failure logs (decrypt / parse / create) that identify the failing instrument and include the relevant payload, replacing an unreadable dump of the whole assignment (which also leaked the private key and large base64 blobs).

Consent form

  • DNP_GENERAL_CONSENT_FORM: changed the consent field from a checkbox to an accept/decline radio, and relaxed its schema from z.literal(true) to z.boolean() so a decline is submittable (which drives the new early-exit demo in DNP_HAPPINESS_QUESTIONNAIRE_WITH_CONSENT).

Docs / tests

  • Documented terminate and completionMessage in packages/instrument-guidelines/AGENTS.md.
  • Added schema tests for the new params (instrument.series.test.ts).
  • Added CLAUDE.md.

Verification

  • pnpm lint and pnpm test pass (36 test files, 244 passed / 1 pre-existing skip).

🤖 Generated with Claude Code

@joshunrau joshunrau merged commit 5282206 into DouglasNeuroInformatics:main Jul 8, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant