mirror of
https://github.com/priyanshujain/sanderling.git
synced 2026-10-05 04:27:09 +00:00
feat(runner): thread violation witnesses, finalize, skip marker into trace
This commit is contained in:
1 parent
3cf668e59a
commit
17f4c7e600
1 file changed
+54
-5
@@ -166,6 +166,8 @@ func Run(ctx context.Context, options Options) (Summary, error) {
|
|||||||
// progressing.
|
// progressing.
|
||||||
var violations []string
|
var violations []string
|
||||||
var extractorChanges map[string]trace.ExtractorChange
|
var extractorChanges map[string]trace.ExtractorChange
|
||||||
|
var witnesses map[string]trace.Witness
|
||||||
|
skippedVerification := false
|
||||||
if !transitional {
|
if !transitional {
|
||||||
if err := options.Verifier.PushSnapshot(verifier.SnapshotInput{
|
if err := options.Verifier.PushSnapshot(verifier.SnapshotInput{
|
||||||
Tree: tree,
|
Tree: tree,
|
||||||
@@ -186,13 +188,10 @@ func Run(ctx context.Context, options Options) (Summary, error) {
|
|||||||
}
|
}
|
||||||
options.Verifier.EvaluateProperties()
|
options.Verifier.EvaluateProperties()
|
||||||
violations = options.Verifier.NewlyViolatedProperties()
|
violations = options.Verifier.NewlyViolatedProperties()
|
||||||
for _, name := range violations {
|
witnesses = collectWitnesses(options.Verifier, violations, logger, stepIndex)
|
||||||
if predicateErr := options.Verifier.PredicateError(name); predicateErr != nil {
|
|
||||||
logger.Warn("predicate error", "step", stepIndex, "property", name, "err", predicateErr)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
extractorChanges = encodeExtractorChanges(options.Verifier.ChangedExtractors())
|
extractorChanges = encodeExtractorChanges(options.Verifier.ChangedExtractors())
|
||||||
} else {
|
} else {
|
||||||
|
skippedVerification = true
|
||||||
logger.Warn("transitional tree after retry budget; skipping verifier",
|
logger.Warn("transitional tree after retry budget; skipping verifier",
|
||||||
"step", stepIndex, "screen", screen, "nodes", treeSize)
|
"step", stepIndex, "screen", screen, "nodes", treeSize)
|
||||||
}
|
}
|
||||||
@@ -250,6 +249,8 @@ func Run(ctx context.Context, options Options) (Summary, error) {
|
|||||||
Metrics: metrics,
|
Metrics: metrics,
|
||||||
ExtractorChanges: extractorChanges,
|
ExtractorChanges: extractorChanges,
|
||||||
Transitional: transitional,
|
Transitional: transitional,
|
||||||
|
SkippedVerification: skippedVerification,
|
||||||
|
Witnesses: witnesses,
|
||||||
}
|
}
|
||||||
if err := options.TraceWriter.WriteStep(step); err != nil {
|
if err := options.TraceWriter.WriteStep(step); err != nil {
|
||||||
return summary, fmt.Errorf("step %d trace: %w", stepIndex, err)
|
return summary, fmt.Errorf("step %d trace: %w", stepIndex, err)
|
||||||
@@ -276,6 +277,27 @@ func Run(ctx context.Context, options Options) (Summary, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Finalize each evaluator once the loop ends so liveness obligations that
|
||||||
|
// never discharged (an unbounded eventually that never fired, a strong
|
||||||
|
// next with no successor) are reported as violations rather than silently
|
||||||
|
// left pending. Properties already violated mid-run are not re-reported.
|
||||||
|
if ended := options.Verifier.Finalize(); len(ended) > 0 {
|
||||||
|
witnesses := collectWitnesses(options.Verifier, ended, logger, stepIndex)
|
||||||
|
summary.Violations = append(summary.Violations, ViolationRecord{
|
||||||
|
StepIndex: stepIndex,
|
||||||
|
Properties: ended,
|
||||||
|
})
|
||||||
|
finalStep := trace.Step{
|
||||||
|
Index: stepIndex,
|
||||||
|
Timestamp: time.Now(),
|
||||||
|
Violations: ended,
|
||||||
|
Witnesses: witnesses,
|
||||||
|
}
|
||||||
|
if err := options.TraceWriter.WriteStep(finalStep); err != nil {
|
||||||
|
return summary, fmt.Errorf("finalize trace: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
summary.EndTime = time.Now()
|
summary.EndTime = time.Now()
|
||||||
return summary, nil
|
return summary, nil
|
||||||
}
|
}
|
||||||
@@ -803,6 +825,33 @@ func nextActionFromV8(ctx context.Context, web driver.WebDriver) (verifier.Actio
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// collectWitnesses gathers the violation witness for each newly-violated
|
||||||
|
// property, logs its cause, and returns them keyed by property name for the
|
||||||
|
// trace. Properties without a captured witness are skipped.
|
||||||
|
func collectWitnesses(verifierInstance *verifier.Verifier, properties []string, logger *slog.Logger, stepIndex int) map[string]trace.Witness {
|
||||||
|
if len(properties) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
witnesses := map[string]trace.Witness{}
|
||||||
|
for _, name := range properties {
|
||||||
|
witness := verifierInstance.Witness(name)
|
||||||
|
if witness == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
logger.Warn("property violated",
|
||||||
|
"step", stepIndex, "property", name, "reason", witness.Reason, "error", witness.IsError)
|
||||||
|
witnesses[name] = trace.Witness{
|
||||||
|
Reason: witness.Reason,
|
||||||
|
IsError: witness.IsError,
|
||||||
|
Extractors: witness.Extractors,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(witnesses) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return witnesses
|
||||||
|
}
|
||||||
|
|
||||||
func encodeExtractorChanges(changes map[string]verifier.ExtractorChange) map[string]trace.ExtractorChange {
|
func encodeExtractorChanges(changes map[string]verifier.ExtractorChange) map[string]trace.ExtractorChange {
|
||||||
if len(changes) == 0 {
|
if len(changes) == 0 {
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
Reference in new issue
Block a user