fix(campaign): a record always says how many actions named no producer

An omitted count reads the same as a run recorded before actions carried a
source, so the two cannot be told apart by anything downstream.
This commit is contained in:
pj committed 2026-08-18 19:45:49 +05:30
1 parent 153f857431
commit e0180f891a
2 files changed
+14 -6

No files matched your search

+9 -4
View File
@@ -138,14 +138,19 @@ func TestRunCampaign_RecordsDispatchedActionsNotSteps(t *testing.T) {
} }
// A run that never produced a readable trace still has to carry the field, so // A run that never produced a readable trace still has to carry the field, so
// analysis can tell a zero-action run from a file written before the count. // analysis can tell a zero-action run from a file written before the count. The
func TestRunRecord_AlwaysCarriesTheActionCount(t *testing.T) { // same holds for the unattributed count: a run whose every action named its
// producer says so with a zero, and a file that says nothing is one recorded
// before actions named one at all.
func TestRunRecord_AlwaysCarriesTheActionCounts(t *testing.T) {
body, err := json.Marshal(runRecord{Seed: 7, TraceError: "no run directory with meta.json"}) body, err := json.Marshal(runRecord{Seed: 7, TraceError: "no run directory with meta.json"})
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
if !strings.Contains(string(body), `"actions":0`) { for _, field := range []string{`"actions":0`, `"unattributed_actions":0`} {
t.Errorf("record %s omits the action count", body) if !strings.Contains(string(body), field) {
t.Errorf("record %s omits %s", body, field)
}
} }
} }
+5 -2
View File
@@ -31,8 +31,11 @@ type traceSummary struct {
// UnattributedActions counts the dispatched steps whose action names no // UnattributedActions counts the dispatched steps whose action names no
// producer, which only a trace recorded before actions carried one can do. // producer, which only a trace recorded before actions carried one can do.
// Such a run's Actions is the count it was already reported with rather than // Such a run's Actions is the count it was already reported with rather than
// a setup-excluding one, and this is what says so. // a setup-excluding one, and this is what says so. It is written even when
UnattributedActions int `json:"unattributed_actions,omitempty"` // it is zero, because a record that omits it is one the analysis has to read
// as unattributable and a run where every action named a producer is the
// opposite of that.
UnattributedActions int `json:"unattributed_actions"`
FirstViolationOriginStep *int `json:"first_violation_origin_step"` FirstViolationOriginStep *int `json:"first_violation_origin_step"`
FirstViolationDetectedStep *int `json:"first_violation_detected_step"` FirstViolationDetectedStep *int `json:"first_violation_detected_step"`
FirstViolationProperties []string `json:"first_violation_properties,omitempty"` FirstViolationProperties []string `json:"first_violation_properties,omitempty"`