@@ -23,14 +23,24 @@ interface ParsedState {
2323 index : number ;
2424 tag ?: string ; // Optional tag associated with this state (e.g. transition/action label)
2525 fields : Record < string , unknown > ;
26+ failing ?: boolean ; // True if an assertion failed while executing the transition to this state
2627}
2728type Trace = ParsedState [ ] ;
2829
29- type ViolationKind = "safety_failure" | "deadlock" ;
30+ type ViolationKind = "safety_failure" | "deadlock" | "assertion_failure" ;
31+
32+ interface AssertionInfo {
33+ procedureName : string ;
34+ moduleName : string ;
35+ line : number ;
36+ column : number ;
37+ }
3038
3139interface Violation {
3240 kind : ViolationKind ;
3341 violates ?: string [ ] ; // Array of violated property names (only present for safety_failure)
42+ exception_id ?: number ; // Only present for assertion_failure
43+ assertion_info ?: AssertionInfo ; // Only present for assertion_failure
3444}
3545
3646interface EarlyTerminationCondition {
@@ -49,6 +59,7 @@ interface TraceData {
4959 index : number ;
5060 fields : Record < string , unknown > ;
5161 transition : unknown ;
62+ failing ?: boolean ;
5263 } > ;
5364 instantiation ?: Record < string , unknown > ;
5465 extraVals ?: Record < string , unknown > ;
@@ -191,6 +202,7 @@ const StateCard: React.FC<{
191202 onHideField ?: ( fieldName : string ) => void ;
192203 onResetForceOpen ?: ( ) => void ; // Called when user clicks header while forceOpen is active
193204} > = ( { st, highlighted = false , changes, showRemovals = false , forceOpen = null , hiddenFields, onHideField, onResetForceOpen } ) => {
205+ const isFailing = st . failing === true ;
194206 const [ localOpen , setLocalOpen ] = React . useState ( true ) ;
195207
196208 // Sync local state when forceOpen changes - this ensures that when we
@@ -218,11 +230,12 @@ const StateCard: React.FC<{
218230 const entries = Object . entries ( st . fields ) . filter ( ( [ k ] ) => ! hiddenFields ?. has ( k ) ) ;
219231
220232 return (
221- < div className = { `state-card ${ highlighted ? "is-highlighted" : "" } ` } >
233+ < div className = { `state-card ${ highlighted ? "is-highlighted" : "" } ${ isFailing ? "is-failing" : "" } ` } >
222234 < div className = "state-header" onClick = { handleHeaderClick } >
223235 < span className = "action-chip" title = { st . tag ?? '' } >
224236 { st . tag || '(no action)' }
225237 </ span >
238+ { isFailing && < span className = "failing-badge" > FAILED</ span > }
226239 < span className = "state-id" > (index: { st . index } )</ span >
227240 < div className = "state-toggle" > { open ? "▼" : "▶" } </ div >
228241 </ div >
@@ -281,8 +294,21 @@ const ResultHeader: React.FC<{
281294 terminationReason ?: TerminationReason ;
282295} > = ( { resultType, violation, exploredStates, terminationReason } ) => {
283296 if ( resultType === "found_violation" && violation ) {
284- const icon = violation . kind === "deadlock" ? "🔒" : "⚠️" ;
285- const label = violation . kind === "deadlock" ? "Deadlock Detected" : "Safety Violation Found" ;
297+ let icon : string ;
298+ let label : string ;
299+ switch ( violation . kind ) {
300+ case "deadlock" :
301+ icon = "🔒" ;
302+ label = "Deadlock Detected" ;
303+ break ;
304+ case "assertion_failure" :
305+ icon = "💥" ;
306+ label = "Assertion Failed" ;
307+ break ;
308+ default :
309+ icon = "⚠️" ;
310+ label = "Safety Violation Found" ;
311+ }
286312 return (
287313 < div className = "result-header result-violation" >
288314 < span className = "result-icon" > { icon } </ span >
@@ -292,6 +318,11 @@ const ResultHeader: React.FC<{
292318 < strong > Violated properties:</ strong > { violation . violates . join ( ", " ) }
293319 </ div >
294320 ) }
321+ { violation . kind === "assertion_failure" && violation . assertion_info && (
322+ < div className = "result-details" >
323+ < strong > Location:</ strong > { violation . assertion_info . moduleName } .{ violation . assertion_info . procedureName } (line { violation . assertion_info . line } , column { violation . assertion_info . column } )
324+ </ div >
325+ ) }
295326 </ div >
296327 ) ;
297328 }
@@ -341,6 +372,7 @@ function traceDataToStates(traceData: TraceData): ParsedState[] {
341372 index : st . index ,
342373 tag : formatActionLabel ( st . transition ) ,
343374 fields : st . fields ,
375+ failing : st . failing ,
344376 } ) ) ;
345377}
346378
@@ -458,6 +490,25 @@ const ModelCheckerView: React.FC<ModelCheckerViewProps> = ({
458490 outline: 2px solid var(--vscode-editor-selectionHighlightBorder);
459491 box-shadow: 0 0 0 3px var(--vscode-editor-selectionBackground);
460492 }
493+ .state-card.is-failing {
494+ outline: 2px solid var(--vscode-inputValidation-errorBorder);
495+ box-shadow: 0 0 0 3px var(--vscode-inputValidation-errorBackground);
496+ }
497+ .state-card.is-failing .state-header {
498+ background: var(--vscode-inputValidation-errorBackground);
499+ }
500+ .failing-badge {
501+ display: inline-block;
502+ font-family: var(--mono);
503+ font-size: 10px;
504+ font-weight: 600;
505+ background: var(--vscode-inputValidation-errorBorder);
506+ color: var(--vscode-editor-background);
507+ border-radius: 3px;
508+ padding: 2px 6px;
509+ text-transform: uppercase;
510+ letter-spacing: 0.5px;
511+ }
461512 .state-header {
462513 background: var(--header);
463514 border-bottom: 1px solid var(--border);
0 commit comments