Materialize computes the Intent-Runtime Diff for a run and persists it. It is idempotent: prior diff rows and edges for the run are cleared first. It first runs the command-match attribution (hooksbridge.CorrelateSyscalls) so runtime effects are tied to the agent whose tool call caused them -- the e
(db *sql.DB, runID string)
| 35 | // normally pinned by the sensor to the record-scope tool call, not the agent's |
| 36 | // hook tool call, and this bridge closes that gap. |
| 37 | func Materialize(db *sql.DB, runID string) (Result, error) { |
| 38 | if runID == "" { |
| 39 | return Result{}, fmt.Errorf("intent materialize: run id is required") |
| 40 | } |
| 41 | res := Result{RunID: runID, ByStatus: map[string]int{}} |
| 42 | eng := security.DefaultEngine() |
| 43 | |
| 44 | // Build the command-match attribution edges only when the run has none yet. |
| 45 | // CorrelateSyscalls is destructive (it clears then rebuilds agent_syscall |
| 46 | // edges); skipping it when edges already exist protects an imported signed |
| 47 | // bundle that shipped with good attribution from being degraded by a |
| 48 | // re-derivation that may fail on truncated argv. |
| 49 | var existing int |
| 50 | _ = db.QueryRow(`SELECT COUNT(*) FROM graph_edges WHERE run_id = ? AND edge_type = 'agent_syscall'`, runID).Scan(&existing) |
| 51 | if existing == 0 { |
| 52 | if _, err := hooksbridge.CorrelateSyscalls(db, runID); err != nil { |
| 53 | return res, fmt.Errorf("intent materialize: correlate syscalls: %w", err) |
| 54 | } |
| 55 | } |
| 56 | |
| 57 | effects, err := normalizeEffects(db, runID, eng) |
| 58 | if err != nil { |
| 59 | return res, fmt.Errorf("intent materialize: normalize effects: %w", err) |
| 60 | } |
| 61 | res.Effects = len(effects) |
| 62 | |
| 63 | contracts, err := ExtractContracts(db, runID, DefaultProfiles()) |
| 64 | if err != nil { |
| 65 | return res, fmt.Errorf("intent materialize: extract contracts: %w", err) |
| 66 | } |
| 67 | res.Contracts = len(contracts) |
| 68 | |
| 69 | diffs := Diff(contracts, effects) |
| 70 | res.Diffs = len(diffs) |
| 71 | |
| 72 | if err := persistDiffs(db, runID, diffs); err != nil { |
| 73 | return res, err |
| 74 | } |
| 75 | if err := emitSignals(db, runID, diffs); err != nil { |
| 76 | return res, err |
| 77 | } |
| 78 | for _, d := range diffs { |
| 79 | res.ByStatus[d.Status]++ |
| 80 | if d.Status == StatusMismatch || d.Status == StatusRefusedBypass { |
| 81 | res.Mismatches++ |
| 82 | } |
| 83 | if d.Status == StatusCoverageGap { |
| 84 | res.CoverageGaps++ |
| 85 | } |
| 86 | } |
| 87 | return res, nil |
| 88 | } |
| 89 | |
| 90 | // emitSignals projects the diffs into the unified signal model as a new |
| 91 | // intent_conformance dimension -- so a mismatch composes with the behavior/cost/ |
no test coverage detected