replay ui fix (#56)

* refactor: rename inspect to replay across the codebase

Renames inspect-ui/ to replay-ui/, internal/inspect/ to internal/replay/,
the CLI subcommand from `sanderling inspect` to `sanderling replay`, and
updates all references in docs, Makefile, README, and Go comments.

* feat(replay-ui): show spec filename with full path on hover

RunList and RunDetail now render the basename of spec_path (e.g.
login.spec.ts) with the full path available as a title tooltip.
This commit is contained in:
pj authored and GitHub committed 2026-06-03 16:17:26 +05:30
1 parent a6f43e15b2
commit b44077afde
71 files changed
+104 -92

No files matched your search

+1 -1
View File
@@ -265,7 +265,7 @@ func violated() reduceResult { return reduceResult{status: statusViolated} }
// violatedWith reports a violation that originates at the given sub-formula
// with the given reason. The reason distinguishes a thrown predicate from a
// plain false so callers (and the inspect UI) can render the cause.
// plain false so callers (and the replay UI) can render the cause.
func violatedWith(formula Formula, reason string) reduceResult {
return reduceResult{
status: statusViolated,
+1 -1
View File
@@ -23,7 +23,7 @@ type PredicateLabel interface {
// ErrorFormula represents a thunk that threw during evaluation. The verifier
// substitutes one of these into the residual when MarshalJSON would otherwise
// have to encode an opaque thunk that already errored. It exists so that the
// inspect UI can render "predicate threw" inline.
// replay UI can render "predicate threw" inline.
type ErrorFormula struct {
Message string
}
@@ -1,5 +1,5 @@
// Package inspect serves the embedded web UI for browsing recorded runs.
package inspect
// Package replay serves the embedded web UI for browsing recorded runs.
package replay
import (
"embed"
@@ -11,11 +11,11 @@ var distFS embed.FS
// Assets returns the embedded SPA bundle rooted at the dist directory.
// In Stage 2 this contains a stub index.html. Stage 4 wires the real
// bundle in via Makefile (copy inspect-ui/dist -> internal/inspect/dist).
// bundle in via Makefile (copy replay-ui/dist -> internal/replay/dist).
func Assets() fs.FS {
sub, err := fs.Sub(distFS, "dist")
if err != nil {
panic("inspect: dist embed missing: " + err.Error())
panic("replay: dist embed missing: " + err.Error())
}
return sub
}
@@ -1,4 +1,4 @@
package inspect
package replay
import (
"net/http"
File renamed without changes.
@@ -1,4 +1,4 @@
package inspect
package replay
import (
"errors"
@@ -1,4 +1,4 @@
package inspect
package replay
import (
"bufio"
@@ -1,4 +1,4 @@
package inspect
package replay
import (
"bufio"
@@ -1,4 +1,4 @@
package inspect
package replay
import (
"encoding/json"
@@ -1,4 +1,4 @@
package inspect
package replay
import (
"encoding/json"
@@ -22,7 +22,7 @@ type ServerOptions struct {
AssetsFS fs.FS
}
// Server holds the HTTP handlers for `sanderling inspect`.
// Server holds the HTTP handlers for `sanderling replay`.
type Server struct {
options ServerOptions
cache *Cache
@@ -1,4 +1,4 @@
package inspect
package replay
import (
"context"
@@ -1,4 +1,4 @@
package inspect
package replay
import (
"context"
@@ -1,4 +1,4 @@
package inspect
package replay
import (
"testing"
+1 -1
View File
@@ -172,7 +172,7 @@ func Run(ctx context.Context, options Options) (Summary, error) {
// one would poison the verifier's previous/current extractor
// advance, so the next clean step would compare against this
// transient state and emit false-positive violations. We still
// record the step (hierarchy + screenshot) for inspect-side
// record the step (hierarchy + screenshot) for replay-side
// debugging, but skip the verifier entirely and pick the next
// action against the unchanged prior state to keep the loop
// progressing.
+1 -1
View File
@@ -50,7 +50,7 @@ type Witness struct {
// ExtractorChange records the prev/curr JSON values of an extractor whose
// observation differed between two consecutive steps. Surfaced under
// violation rows in the inspect UI as a "what changed at this step"
// violation rows in the replay UI as a "what changed at this step"
// breadcrumb.
type ExtractorChange struct {
Prev json.RawMessage `json:"prev"`
+1 -1
View File
@@ -53,7 +53,7 @@ type Exception struct {
// ExtractorChange records a single extractor's value transition across one
// step. Used to surface "what changed at this step" breadcrumbs at violation
// markers in the inspect UI.
// markers in the replay UI.
type ExtractorChange struct {
Prev []byte
Curr []byte
+2 -2
View File
@@ -321,7 +321,7 @@ func encodeExtractorValue(value goja.Value) []byte {
// ChangedExtractors returns the named extractors whose value changed between
// the prior PushSnapshot and the current one. The map is keyed by extractor
// name; unnamed extractors (extractor_N fallback) are included so the inspect
// name; unnamed extractors (extractor_N fallback) are included so the replay
// UI can still display them under a numeric label. The very first snapshot
// emits every non-null extractor as a change (Prev=null, Curr=current) since
// the runner can otherwise misread "no diff yet" as "nothing initialized".
@@ -525,7 +525,7 @@ func (v *Verifier) NewlyViolatedProperties() []string {
// Residuals returns the residual formula for each registered property after
// the most recent EvaluateProperties call. Properties whose violation was
// caused by a thrown predicate surface as ErrorFormula, sourced from the
// captured witness, so the inspect UI can render "predicate threw" inline.
// captured witness, so the replay UI can render "predicate threw" inline.
func (v *Verifier) Residuals() map[string]ltl.Formula {
residuals := map[string]ltl.Formula{}
for name, evaluator := range v.evaluators {