@@ -103,39 +103,45 @@ public static String report(ExecuteResponseData result) {
103103
104104 sb .append (" Stages: " )
105105 .append (result .getTotalStages ()).append (" total — " )
106- .append (result .getReachedStages ()).append (" reached, " )
107106 .append (result .getCompletedStages ()).append (" completed, " )
108- .append (result .getFailedStages ()).append (" failed" )
107+ .append (result .getFailedStages ()).append (" failed, " )
108+ .append (result .getUpToDateStages ()).append (" up to date, " )
109+ .append (result .getNotReachedStages ()).append (" not reached" )
109110 .append (NEWLINE );
110111 sb .append (" Changes: " )
111112 .append (result .getTotalChanges ()).append (" total — " )
112- .append (result .getReachedChanges ()).append (" reached, " )
113113 .append (result .getAppliedChanges ()).append (" applied, " )
114- .append (result .getSkippedChanges ()).append (" skipped , " )
114+ .append (result .getSkippedChanges ()).append (" already at target state , " )
115115 .append (result .getFailedChanges ()).append (" failed" )
116116 .append (NEWLINE );
117117
118- // Per-stage breakdown: only stages the executor actually opened. Stages the planner
119- // short-circuited past are surfaced separately in the "Not reached" section below (when
120- // the coverage is partial — a fully up-to-date run shows neither, the headline carries it) .
118+ // Per-stage breakdown: include any stage the operation touched (state != NOT_STARTED) OR
119+ // the planner verdicted UP_TO_DATE. Stages with verdict NEEDS_WORK / NOT_EVALUATED and
120+ // still-NOT_STARTED state fall into the "Not reached" section .
121121 List <StageResult > stages = nonNullStages (result );
122- List <StageResult > reached = stages .stream ()
123- .filter (StageResult ::isWasExecuted )
122+ List <StageResult > inBreakdown = stages .stream ()
123+ .filter (s -> !s .getState ().isNotStarted ()
124+ || s .getPlannerVerdict () == PlannerVerdict .UP_TO_DATE )
124125 .collect (Collectors .toList ());
125- if (!reached .isEmpty ()) {
126+ if (!inBreakdown .isEmpty ()) {
126127 sb .append (NEWLINE ).append (" Per-stage breakdown:" ).append (NEWLINE ).append (NEWLINE );
127- for (StageResult stage : reached ) {
128+ for (StageResult stage : inBreakdown ) {
128129 appendStageBlock (sb , stage );
129130 sb .append (NEWLINE );
130131 }
131132 }
132133
133- // "Not reached" only appears when there's a partial: some stages ran, some didn't. The
134- // all-up-to-date case (0 reached) is communicated by the NO_CHANGES headline alone.
134+ // "Not reached" lists stages neither operation-touched nor verdicted UP_TO_DATE. Omitted
135+ // when nothing was reached AND nothing was up to date (catastrophic-failure shape; the
136+ // headline conveys it).
135137 List <StageResult > notReached = stages .stream ()
136- .filter (s -> !s .isWasExecuted ())
138+ .filter (s -> s .getState ().isNotStarted ()
139+ && s .getPlannerVerdict () != PlannerVerdict .UP_TO_DATE )
137140 .collect (Collectors .toList ());
138- if (!notReached .isEmpty () && result .getReachedStages () > 0 ) {
141+ if (!notReached .isEmpty ()
142+ && (result .getCompletedStages () > 0
143+ || result .getFailedStages () > 0
144+ || result .getUpToDateStages () > 0 )) {
139145 sb .append (NEWLINE ).append (" Not reached (" ).append (notReached .size ()).append ("):" ).append (NEWLINE );
140146 for (StageResult stage : notReached ) {
141147 String name = stage .getStageName () != null ? stage .getStageName () : "(unnamed)" ;
@@ -160,6 +166,26 @@ private static void appendTimeBlock(StringBuilder sb, ExecuteResponseData result
160166 private static void appendStageBlock (StringBuilder sb , StageResult stage ) {
161167 String label = stageLabel (stage );
162168 String name = stage .getStageName () != null ? stage .getStageName () : "(unnamed)" ;
169+
170+ // [UP TO DATE] rows render without a duration (the executor never ran). Community has
171+ // per-change ALREADY_APPLIED records (planner-populated from audit); cloud doesn't —
172+ // we emit either a per-change line or a count-only line depending on what's available.
173+ boolean isUpToDateOnly = stage .getState ().isNotStarted ()
174+ && stage .getPlannerVerdict () == PlannerVerdict .UP_TO_DATE ;
175+
176+ if (isUpToDateOnly ) {
177+ int alreadyAppliedCount = stage .getChanges () != null
178+ ? (int ) stage .getChanges ().stream ()
179+ .filter (c -> c != null && c .isAlreadyApplied ())
180+ .count ()
181+ : 0 ;
182+ int reportableCount = alreadyAppliedCount > 0 ? alreadyAppliedCount : stage .getTotalChanges ();
183+ sb .append (" " ).append (label ).append (' ' ).append (name )
184+ .append (" (" ).append (reportableCount ).append (" changes already at target state)" )
185+ .append (NEWLINE );
186+ return ;
187+ }
188+
163189 sb .append (" " ).append (label ).append (' ' ).append (name )
164190 .append (" (" ).append (stage .getDurationMs ()).append (" ms)" ).append (NEWLINE );
165191
@@ -171,7 +197,7 @@ private static void appendStageBlock(StringBuilder sb, StageResult stage) {
171197 int failed = (int ) changes .stream ().filter (c -> c != null && (c .isFailed () || c .isRolledBack ())).count ();
172198 sb .append (" changes: " )
173199 .append (applied ).append (" applied, " )
174- .append (skipped ).append (" skipped , " )
200+ .append (skipped ).append (" already at target state , " )
175201 .append (failed ).append (" failed" )
176202 .append (NEWLINE );
177203
@@ -209,6 +235,8 @@ private static void appendErrorBlock(StringBuilder sb, ErrorInfo err) {
209235 }
210236
211237 private static String stageLabel (StageResult stage ) {
238+ // Two-dimensional resolution. State (operation-owned) wins whenever it's moved off
239+ // NOT_STARTED. While state is still NOT_STARTED, the planner verdict decides.
212240 StageState state = stage .getState ();
213241 if (state == null ) {
214242 return "[UNKNOWN] " ;
@@ -225,10 +253,11 @@ private static String stageLabel(StageResult stage) {
225253 if (state .isStarted ()) {
226254 return "[STARTED] " ;
227255 }
228- if (state .isNotStarted ()) {
229- return "[PENDING] " ;
256+ // state.isNotStarted() — verdict decides.
257+ if (stage .getPlannerVerdict () == PlannerVerdict .UP_TO_DATE ) {
258+ return "[UP TO DATE]" ;
230259 }
231- return "[UNKNOWN] " ;
260+ return "[NOT REACHED] " ;
232261 }
233262
234263 private static String statusLabel (ExecuteResponseData result ) {
0 commit comments