@@ -177,6 +177,19 @@ export interface CliTestStep {
177177 * that don't emit the field still type-check.
178178 */
179179 outcomeContributesToFailure ?: boolean | null ;
180+ /**
181+ * Per-step failure text, carried from `RunStepDto.error` on the run-scoped
182+ * endpoint (`GET /runs/{id}?includeSteps=true`). Only present on `--run-id`
183+ * responses; the cumulative `/tests/{id}/steps` rows do not carry it, so the
184+ * field stays optional (additive, non-breaking for existing consumers).
185+ */
186+ error ?: string | null ;
187+ /**
188+ * Wire step kind from `RunStepDto.type` on the run-scoped endpoint. Same
189+ * availability rules as `error`. Named `stepType` to avoid colliding with
190+ * the free-form `action` label above.
191+ */
192+ stepType ?: 'action' | 'assertion' ;
180193}
181194
182195/**
@@ -3627,6 +3640,12 @@ function mapRunStepToCliTestStep(step: RunStepDto, run: RunResponse): CliTestSte
36273640 // non-contributors. (Per the CliTestStep contract: null ≠ false.)
36283641 outcomeContributesToFailure :
36293642 run . failedStepIndex === null ? null : numericIndex === run . failedStepIndex ,
3643+ // Carry the per-step failure text and the wire step kind through instead
3644+ // of dropping them: the agent asking "why did this step fail?" would
3645+ // otherwise have to download the whole artifact bundle to read a string
3646+ // this very response already contained.
3647+ error : step . error ,
3648+ stepType : step . type ,
36303649 } ;
36313650}
36323651
@@ -3947,6 +3966,14 @@ const RUN_HISTORY_TABLE_COL_WIDTHS = {
39473966 */
39483967const DESC_COL_MAX = 60 ;
39493968
3969+ /**
3970+ * Cap, in chars, for the one-line `error:` sub-line under a failed step row
3971+ * in `renderStepsText`. Long enough for a full assertion message, short
3972+ * enough that a stack-trace blob can't flood the table. Full text is in
3973+ * `--output json`.
3974+ */
3975+ const ERROR_SUBLINE_MAX = 200 ;
3976+
39503977/** Max chars to show in the TARGETURL sub-line (excess truncated with …). */
39513978const HISTORY_TARGET_URL_MAX = 80 ;
39523979
@@ -8221,16 +8248,29 @@ function renderStepsText(page: Page<CliTestStep>): string {
82218248 ' ' +
82228249 'UPDATED' ;
82238250
8224- const rows = page . items . map ( s => {
8251+ const rows = page . items . flatMap ( s => {
82258252 const marker = s . outcomeContributesToFailure === true ? '* ' : ' ' ;
8226- return [
8253+ const row = [
82278254 marker ,
82288255 pad ( String ( s . stepIndex ) , indexWidth ) ,
82298256 pad ( s . action , actionWidth ) ,
82308257 pad ( s . status ?? '—' , statusWidth ) ,
82318258 pad ( descOf ( s ) , descWidth ) ,
82328259 s . updatedAt ,
82338260 ] . join ( ' ' ) ;
8261+ // Run-scoped rows carry the per-step failure text; surface it as an
8262+ // indented sub-line under failed rows (mirrors the history table's
8263+ // `targetUrl:` sub-line). Collapsed to one line and capped so a huge
8264+ // stack blob can't wreck the table; full text ships in --output json.
8265+ if ( s . status === 'failed' && typeof s . error === 'string' && s . error . length > 0 ) {
8266+ const oneLine = s . error . replace ( / \s + / g, ' ' ) . trim ( ) ;
8267+ const shown =
8268+ oneLine . length > ERROR_SUBLINE_MAX
8269+ ? `${ oneLine . slice ( 0 , ERROR_SUBLINE_MAX - 1 ) } …`
8270+ : oneLine ;
8271+ return [ row , ` error: ${ shown } ` ] ;
8272+ }
8273+ return [ row ] ;
82348274 } ) ;
82358275
82368276 const lines : string [ ] = [ header , ...rows , '' ] ;
0 commit comments