| 1 | package event |
| 2 | |
| 3 | import ( |
| 4 | "testing" |
| 5 | |
| 6 | "reasonix/internal/evidence" |
| 7 | ) |
| 8 | |
| 9 | // capabilityRecorder accepts every optional sink capability and names the ones |
| 10 | // it received. |
| 11 | type capabilityRecorder struct { |
| 12 | got map[string]bool |
| 13 | } |
| 14 | |
| 15 | func newCapabilityRecorder() *capabilityRecorder { |
| 16 | return &capabilityRecorder{got: map[string]bool{}} |
| 17 | } |
| 18 | |
| 19 | func (c *capabilityRecorder) mark(name string) { c.got[name] = true } |
| 20 | func (c *capabilityRecorder) Emit(Event) { c.mark("emit") } |
| 21 | func (c *capabilityRecorder) RecordTurnCompletion() { c.mark("turn_completion") } |
| 22 | |
| 23 | func (c *capabilityRecorder) RecordReadinessAudit(evidence.ReadinessAudit) { |
| 24 | c.mark("readiness_audit") |
| 25 | } |
| 26 | func (c *capabilityRecorder) RecordAnchorSafetyAudit(AnchorSafetyAudit) { |
| 27 | c.mark("anchor_safety_audit") |
| 28 | } |
| 29 | func (c *capabilityRecorder) RecordContractShadow(ContractShadowAudit) { c.mark("contract_shadow") } |
| 30 | func (c *capabilityRecorder) RecordCompletionReport(CompletionReportAudit) { |
| 31 | c.mark("completion_report") |
| 32 | } |
| 33 | func (c *capabilityRecorder) RecordMemoryRecall(MemoryRecallAudit) { c.mark("memory_recall") } |
| 34 | func (c *capabilityRecorder) RecordDelegationAdmission(DelegationAdmissionAudit) { |
| 35 | c.mark("delegation_admission") |
| 36 | } |
| 37 | func (c *capabilityRecorder) RecordOutcomeProgress(evidence.OutcomeSample) { |
| 38 | c.mark("outcome_progress") |
| 39 | } |
| 40 | func (c *capabilityRecorder) RecordProtocolRecovery(ProtocolRecoveryAudit) { |
| 41 | c.mark("protocol_recovery") |
| 42 | } |
| 43 | func (c *capabilityRecorder) RecordDelegationAudit(evidence.DelegationAudit) { |
| 44 | c.mark("delegation_audit") |
| 45 | } |
| 46 | func (c *capabilityRecorder) RecordWorkspaceMutation(WorkspaceMutation) { |
| 47 | c.mark("workspace_mutation") |
| 48 | } |
| 49 | func (c *capabilityRecorder) RecordRunBudget(RunBudgetSample) { c.mark("run_budget") } |
| 50 | |
| 51 | // A wrapper that drops an optional capability silently truncates every recorder |
| 52 | // below it: the trajectory and stats recorders sit under the quoting sink, so |
| 53 | // this one dropping them meant real runs recorded no audits at all while every |
| 54 | // unit test — which wires recorders directly — stayed green. |
| 55 | func TestCostQuoteSinkPreservesEveryAuditCapability(t *testing.T) { |
| 56 | inner := newCapabilityRecorder() |
| 57 | // Sink, not *CostQuoteSink: the host reaches these channels through the |
| 58 | // package dispatchers, which type-assert and silently no-op on a wrapper |
| 59 | // that lost the capability. Calling the methods directly instead would turn |
| 60 | // this guard into a compile error that only fires when they vanish outright. |
| 61 | var s Sink = NewCostQuoteSink(inner, nil) |
| 62 | |
| 63 | s.Emit(Event{Kind: Notice}) |
| 64 | RecordTurnCompletion(s) |
| 65 | RecordReadinessAudit(s, evidence.ReadinessAudit{}) |
| 66 | RecordAnchorSafetyAudit(s, AnchorSafetyAudit{Mode: "shadow"}) |
| 67 | RecordContractShadow(s, ContractShadowAudit{}) |
| 68 | RecordCompletionReport(s, CompletionReportAudit{}) |
| 69 | RecordMemoryRecall(s, MemoryRecallAudit{}) |
| 70 | RecordDelegationAdmission(s, DelegationAdmissionAudit{}) |
| 71 | RecordOutcomeProgress(s, evidence.OutcomeSample{Round: 1}) |
| 72 | RecordProtocolRecovery(s, ProtocolRecoveryAudit{}) |
| 73 | RecordDelegationAudit(s, evidence.DelegationAudit{}) |
| 74 | RecordWorkspaceMutation(s, WorkspaceMutation{}) |
| 75 | RecordRunBudget(s, RunBudgetSample{}) |
| 76 | |
| 77 | for _, want := range []string{ |
| 78 | "emit", "turn_completion", "readiness_audit", "anchor_safety_audit", "contract_shadow", |
| 79 | "completion_report", "memory_recall", "delegation_admission", |
| 80 | "outcome_progress", "protocol_recovery", "delegation_audit", |
| 81 | "workspace_mutation", "run_budget", |
| 82 | } { |
| 83 | if !inner.got[want] { |
| 84 | t.Errorf("quoting sink swallowed %s; everything recorded below it loses that channel", want) |
| 85 | } |
| 86 | } |
| 87 | } |
| 88 |