mirror of
https://github.com/priyanshujain/sanderling.git
synced 2026-10-02 11:07:10 +00:00
fix(runner): record a witness's detection step in the trace
Claude-Session: https://claude.ai/code/session_01Fj4wJUikdABuMQEETwW55J
This commit is contained in:
1 parent
16134f2584
commit
eb492e53de
3 files changed
+21
-7
No files matched your search
@@ -1006,7 +1006,9 @@ func violationRecords(properties []string, witnesses map[string]trace.Witness, d
|
||||
|
||||
// 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.
|
||||
// trace. Properties without a captured witness are skipped. stepIndex is the
|
||||
// trace line the witness lands on, and stands in as the detection step for a
|
||||
// verifier that observed no labeled step (a run-end finalize).
|
||||
func collectWitnesses(verifierInstance *verifier.Verifier, properties []string, logger *slog.Logger, stepIndex int) map[string]trace.Witness {
|
||||
if len(properties) == 0 {
|
||||
return nil
|
||||
@@ -1017,13 +1019,18 @@ func collectWitnesses(verifierInstance *verifier.Verifier, properties []string,
|
||||
if witness == nil {
|
||||
continue
|
||||
}
|
||||
detectedStep := witness.DetectedStep
|
||||
if detectedStep == 0 {
|
||||
detectedStep = stepIndex
|
||||
}
|
||||
logger.Warn("property violated",
|
||||
"step", witness.Step, "detected_step", stepIndex,
|
||||
"step", witness.Step, "detected_step", detectedStep,
|
||||
"property", name, "reason", witness.Reason, "error", witness.IsError)
|
||||
witnesses[name] = trace.Witness{
|
||||
Reason: witness.Reason,
|
||||
IsError: witness.IsError,
|
||||
Step: witness.Step,
|
||||
DetectedStep: detectedStep,
|
||||
Extractors: witness.Extractors,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -363,6 +363,11 @@ globalThis.actions = actions(() => []);
|
||||
if witness.Step != 2 {
|
||||
t.Errorf("witness step: got %d, want 2 (causing step)", witness.Step)
|
||||
}
|
||||
// The two indices the witness spans are recorded separately: the
|
||||
// extractor snapshot it carries is step 3's state, not step 2's.
|
||||
if witness.DetectedStep != 3 {
|
||||
t.Errorf("witness detected step: got %d, want 3", witness.DetectedStep)
|
||||
}
|
||||
}
|
||||
if err := scanner.Err(); err != nil {
|
||||
t.Fatalf("scan trace: %v", err)
|
||||
|
||||
@@ -40,16 +40,18 @@ type Step struct {
|
||||
Witnesses map[string]Witness `json:"witnesses,omitempty"`
|
||||
}
|
||||
|
||||
// Witness is the trace-side record of a property violation: why it fired and a
|
||||
// snapshot of every extractor's value at the violating step.
|
||||
// Witness is the trace-side record of a property violation: why it fired, the
|
||||
// two steps a deferred obligation spans, and the extractor values behind it.
|
||||
type Witness struct {
|
||||
Reason string `json:"reason,omitempty"`
|
||||
IsError bool `json:"is_error,omitempty"`
|
||||
// Step is the step the failed obligation originated at: the step that
|
||||
// caused the violation. For a deferred obligation (a next, an eventually)
|
||||
// this is earlier than the step whose record carries the witness, which is
|
||||
// where the failure was detected.
|
||||
// armed it. For a deferred obligation (a next, an eventually) this is
|
||||
// earlier than the step at which the failure was detected.
|
||||
Step int `json:"step,omitempty"`
|
||||
// DetectedStep is the observation whose evaluation produced the violation.
|
||||
// Extractors is that step's state, not Step's.
|
||||
DetectedStep int `json:"detected_step,omitempty"`
|
||||
Extractors map[string]json.RawMessage `json:"extractors,omitempty"`
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user