Skip to content

Draft blank screener forms with AI - #545

Merged
prestoncabe merged 11 commits into
mainfrom
544-draft-screener-form-with-ai
Oct 10, 2026
Merged

prestoncabe merged 11 commits into
mainfrom
544-draft-screener-form-with-ai

Conversation

@prestoncabe

Copy link
Copy Markdown
Collaborator

Summary

Closes #544.

Blank screener forms now offer Draft with AI after benefits and checks are configured. Gemini drafts one question per unique required input, with server-assigned bindings and field types. The draft stays editable and unsaved for review; incomplete drafts are rejected, and unsupported or unconfigured checks report an actionable error before calling Gemini. Choice values are constrained to schema enums or configured check values.

Preview and published screeners show Screening complete only after every benefit has an eligibility decision for the latest answers. Failed evaluations preserve the last results, display an error, and suppress completion. Alias generation and form drafting share the Gemini client, with a separate form-drafting feature toggle.

Verification

  • Frontend: npm test — 178 tests passed.
  • Frontend: npm run build — passed.
  • Backend: focused ScreenerFormDraftServiceTest, ScreenerResourceTest, and EligibilityCheckAliasServiceTest — 25 tests passed.
  • Docs: npm run build — passed after the final manual documentation edits.
  • Live local Gemini drafting verified that all 16 required inputs were satisfied before the follow-up review fixes.

Documentation impact

  • Updated the user guide to introduce AI drafting before form editing, emphasize that it is a starting point requiring review and testing, and describe screening completion. Updated .env.example with the drafting configuration.

prestoncabe and others added 11 commits October 9, 2026 14:17
Require complete input bindings, preserve drafts for review, and announce completion once all benefits have decisions. Include regression coverage and user documentation.

Refs #544
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Both services now send structured-output requests through GeminiClient, so the endpoint, auth header, status handling and response parsing live in one place. Alias generation also gains the finishReason check that form drafting already had.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The controller already extracts the input paths to check that a draft is possible, so it now passes them to the draft service instead of having it walk every check schema again. The question ID mapping is built once and shared by the prompt, response schema and ordering, so all three always use the same ID-to-path mapping.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Field keys come from the server's question ID mapping, never from the model, so the separate ordering step and the key existence, duplicate, count and reserved-segment checks could never fail. toFormSchema now reads the model's keyed questions directly, and its remaining checks are the ones that guard model output: coverage, ordering, labels, descriptions and option values.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Custom check inputs without a declared type are extracted as "any", and the example screener's own custom checks use them. Drafting previously failed for these after the full Gemini call and blamed the Gemini configuration. The model now picks an answer type (yes_no, number, date or text) for each untyped input, and array:any inputs become checklists like array:string. Each question gets its own response schema, so choice arrays must have at least one option and typed scalar questions cannot have options.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A check without an input definition, or an input type drafting cannot build a field for (such as array:number), used to surface as a 503 telling the builder to check the Gemini configuration, which retrying never fixed. A missing input definition failed before any Gemini call; an unsupported type failed only after the full call. The draft service now checks both up front and throws UndraftableFormException, which the endpoint returns as a 400 naming the check or inputs to fix.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Radio and checklist option values from the model were only checked for being non-blank and unique, so a value like "Spouse" for the enum value "spouse", or "Homestead" for the code "PhlHomesteadExemption", passed validation and silently made the check never match. Option values now come from the input's schema enum or, for arrays without one such as enrollments, from the string parameters of the checks that read the input. Gemini's response schema restricts option values to that list, and the server rejects drafts that use any other value or leave such a question without choices.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
form-js assigns a random key such as textfield_k3j9a before formField.add fires, so the !field.key guard stopped the handler from ever setting a new field's key to its ID. The guard was meant to keep keys during draft imports, which also emit formField.add, so the handler now skips events while a draft is being imported instead.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
onDataChange set resultsLoading, which also drives the Results panel, so the panel switched to "Loading results..." on every edit and stayed that way through the debounce. A separate resultsStale signal now hides the completion message from the first edit until results for the latest answers arrive, and resultsLoading is only set while an evaluation request is in flight.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A single failed evaluation cleared the results, which removed the public results panel and brought back every hidden question with no explanation. The public screener and Preview now keep the last successful results and hidden questions, show an alert that the latest answers could not be evaluated, and keep the completion message hidden until an evaluation of the latest answers succeeds.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@prestoncabe prestoncabe linked an issue Oct 9, 2026 that may be closed by this pull request
@prestoncabe
prestoncabe merged commit b134369 into main Oct 10, 2026
5 checks passed
@prestoncabe
prestoncabe deleted the 544-draft-screener-form-with-ai branch October 10, 2026 01:52
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.

Draft Screener Form with AI

1 participant