@@ -132,6 +132,8 @@ private static function runActionability(array $payload): array
132132 default => 'not_needed ' ,
133133 };
134134
135+ $ taskProblem = ($ payload ['task_problem ' ] ?? false ) === true ;
136+
135137 return [
136138 'schema ' => self ::SCHEMA ,
137139 'version ' => self ::VERSION ,
@@ -140,9 +142,13 @@ private static function runActionability(array $payload): array
140142 'blocked_reason ' => $ blockedReason ,
141143 'status_bucket ' => $ statusBucket ,
142144 'closed_reason ' => $ closedReason ,
143- 'task_problem ' => ( $ payload [ ' task_problem ' ] ?? false ) === true ,
145+ 'task_problem ' => $ taskProblem ,
144146 'diagnostic_only_evidence ' => self ::hasDiagnosticOnlyEvidence ($ payload ),
145147 'actions ' => self ::runActions ($ payload , $ repairState , $ blockedReason ),
148+ 'badges ' => [
149+ 'repair ' => self ::repairBadge ($ repairState , $ blockedReason ),
150+ 'task_problem ' => self ::taskProblemBadge ($ taskProblem ),
151+ ],
146152 ];
147153 }
148154
@@ -192,6 +198,63 @@ private static function repairBlockedReason(string $repairState, ?string $blocke
192198 };
193199 }
194200
201+ /**
202+ * @return array{code: ?string, label: string, description: string, tone: string, badge_visible: bool, derived_from: string}
203+ */
204+ private static function repairBadge (string $ repairState , ?string $ blockedReason ): array
205+ {
206+ $ reason = self ::repairBlockedReason ($ repairState , $ blockedReason );
207+
208+ return [
209+ 'code ' => $ reason ,
210+ 'label ' => match ($ reason ) {
211+ 'unsupported_history ' => 'Replay Blocked ' ,
212+ 'selected_run_not_current ' => 'Run Not Current ' ,
213+ 'run_closed ' => 'Run Closed ' ,
214+ 'waiting_for_compatible_worker ' => 'Compatibility Wait ' ,
215+ 'repair_not_needed ' => 'Repair Not Needed ' ,
216+ 'repair_state_unknown ' => 'Repair Unknown ' ,
217+ default => $ repairState === 'repairable ' ? 'Repairable ' : 'Repair Blocked ' ,
218+ },
219+ 'description ' => match ($ reason ) {
220+ 'unsupported_history ' => 'Repair is blocked because only unsupported diagnostic history remains. ' ,
221+ 'selected_run_not_current ' => 'Repair is blocked because the selected run is not the current active run. ' ,
222+ 'run_closed ' => 'Repair is blocked because the run is already closed. ' ,
223+ 'waiting_for_compatible_worker ' => 'Repair is waiting for an active compatible worker. ' ,
224+ 'repair_not_needed ' => 'Repair is not needed for this run. ' ,
225+ 'repair_state_unknown ' => 'Waterline has not classified this run as repairable yet. ' ,
226+ default => $ repairState === 'repairable '
227+ ? 'This run has an actionable durable repair source. '
228+ : 'Repair is currently blocked. ' ,
229+ },
230+ 'tone ' => match ($ reason ) {
231+ 'unsupported_history ' , 'selected_run_not_current ' , 'run_closed ' => 'dark ' ,
232+ 'waiting_for_compatible_worker ' , 'repair_state_unknown ' => 'warning ' ,
233+ 'repair_not_needed ' => 'secondary ' ,
234+ default => $ repairState === 'repairable ' ? 'success ' : 'secondary ' ,
235+ },
236+ 'badge_visible ' => $ repairState === 'blocked ' ,
237+ 'derived_from ' => 'repair_state ' ,
238+ ];
239+ }
240+
241+ /**
242+ * @return array{code: ?string, label: string, description: string, tone: string, badge_visible: bool, derived_from: string}
243+ */
244+ private static function taskProblemBadge (bool $ taskProblem ): array
245+ {
246+ return [
247+ 'code ' => $ taskProblem ? 'task_problem ' : null ,
248+ 'label ' => $ taskProblem ? 'Task Problem ' : 'No Task Problem ' ,
249+ 'description ' => $ taskProblem
250+ ? 'This run recorded workflow-task problems. '
251+ : 'This run has not recorded workflow-task problems. ' ,
252+ 'tone ' => $ taskProblem ? 'warning ' : 'secondary ' ,
253+ 'badge_visible ' => $ taskProblem ,
254+ 'derived_from ' => self ::SCHEMA ,
255+ ];
256+ }
257+
195258 /**
196259 * @param array<string, mixed> $payload
197260 */
0 commit comments