@@ -59,7 +59,9 @@ public Optional<JsonNode> generate(List<Benefit> benefits, List<FormPath> paths)
5959 buildPrompt (benefits , questionPaths ), responseSchema (questionPaths , optionValues ),
6060 16384 , Duration .ofSeconds (60 ));
6161 if (generated .isEmpty ()) return Optional .empty ();
62- return Optional .of (toFormSchema (objectMapper .readTree (generated .get ()), questionPaths , optionValues ));
62+ JsonNode form = toFormSchema (objectMapper .readTree (generated .get ()), questionPaths , optionValues );
63+ appendResultsComponents (form , benefits );
64+ return Optional .of (form );
6365 } catch (InterruptedException e ) {
6466 Thread .currentThread ().interrupt ();
6567 return Optional .empty ();
@@ -118,8 +120,11 @@ private String buildPrompt(List<Benefit> benefits, Map<String, FormPath> questio
118120 when needed. Do not over-explain commonly understood things. Keep dates and numeric
119121 inputs in their original units; do not replace a birth date or income with a yes/no proxy.
120122 Do not supply default answers, invent eligibility rules, or ask for configured parameters.
121- The runtime evaluates the actual checks, hides questions no longer needed, and displays
122- a clear 'Screening complete' message only when ALL benefits are eligible or ineligible.
123+ The runtime evaluates the actual checks and hides questions no longer needed.
124+ The server adds Next steps and Print summary components after your questions.
125+ Next steps shows each benefit as it is decided, and announces completion only when
126+ ALL benefits are eligible or ineligible. Do not write application instructions,
127+ application URLs, help contacts, or ineligibility notes; the server copies configured content.
123128 Do not include an unconditional completion message or try to calculate eligibility yourself.
124129 Return a questions object with one entry for EVERY required question ID in the mapping
125130 below. Each entry has label, description, and values (an array of
@@ -139,6 +144,25 @@ across ALL checks (e.g. every benefit enrollment code), using friendly option la
139144 """ .formatted (objectMapper .writeValueAsString (questionPaths ), objectMapper .writeValueAsString (context ));
140145 }
141146
147+ /** Trusted benefit content is copied after generation, never supplied or rewritten by Gemini. */
148+ private void appendResultsComponents (JsonNode form , List <Benefit > benefits ) {
149+ var components = (com .fasterxml .jackson .databind .node .ArrayNode ) form .path ("components" );
150+ ObjectNode nextSteps = components .addObject ();
151+ nextSteps .put ("id" , "Field_draft_next_steps" ).put ("type" , "next_steps" )
152+ .put ("showWhen" , "whenDecided" );
153+ nextSteps .putObject ("layout" ).put ("row" , "Row_draft_next_steps" );
154+ ObjectNode content = nextSteps .putObject ("benefitContent" );
155+ for (Benefit benefit : benefits ) {
156+ // Omit missing content so the editor can flag it for the analyst.
157+ if (benefit .getNextSteps () != null ) {
158+ content .set (benefit .getId (), objectMapper .valueToTree (benefit .getNextSteps ()));
159+ }
160+ }
161+ ObjectNode printSummary = components .addObject ();
162+ printSummary .put ("id" , "Field_draft_print_summary" ).put ("type" , "print_summary" );
163+ printSummary .putObject ("layout" ).put ("row" , "Row_draft_print_summary" );
164+ }
165+
142166 // Short question IDs keep provider property names simple. Actual form bindings are assigned
143167 // server-side from this mapping, never generated or rewritten by the model.
144168 Map <String , FormPath > questionPaths (List <FormPath > paths ) {
0 commit comments