Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,13 @@ Joltrin (formerly SOP) is an open-source Go library that checks an AI agent's ac
</a>
</p>

<table align="center">
<tr>
<td align="center"><a href="https://www.youtube.com/watch?v=F0jYkBHJluI"><img src="https://img.youtube.com/vi/F0jYkBHJluI/hqdefault.jpg" alt="Joltrin full demo on YouTube" width="360" /></a><br /><sub>Full demo</sub></td>
<td align="center"><a href="https://www.youtube.com/shorts/oS1bfq_ZDkg"><img src="https://img.youtube.com/vi/oS1bfq_ZDkg/hqdefault.jpg" alt="Joltrin short on YouTube" width="200" /></a><br /><sub>Short</sub></td>
</tr>
</table>

## A blocked step, then the safe order

A production database drop is blocked until the backup is taken and validated:
Expand Down
44 changes: 24 additions & 20 deletions tools/blocklog/blocklog.go
Original file line number Diff line number Diff line change
Expand Up @@ -364,28 +364,34 @@ func (l *Log) Stats(workflow, version string) Stats {
l.mu.Lock()
defer l.mu.Unlock()

type stepKey struct {
type ruleRun struct{ rule, trace string }
type blockKey struct {
rr ruleRun
step verify.StepID
missing verify.State
}
type ruleRun struct{ rule, trace string }
// counts is how many distinct blocked steps a rule had in one run, and how
// many of them recovered. Held by value so a run costs no allocation.
type counts struct{ blocked, recovered int }
cutoff := l.now().Add(-l.ttl)
runs := map[string]struct{}{}
blocked := map[ruleRun]map[stepKey]bool{} // true once the step recovered
recovered := make(map[blockKey]bool, len(l.entries)) // true once the step recovered
perRun := map[ruleRun]counts{}
for _, e := range l.entries {
if e.Workflow != workflow || e.Version != version || e.At.Before(cutoff) {
continue
}
rr, sk := ruleRun{e.BlockedBy, e.TraceID}, stepKey{e.Step, e.MissingState}
switch e.Kind {
case KindRun:
runs[e.TraceID] = struct{}{}
case "":
if blocked[rr] == nil {
blocked[rr] = map[stepKey]bool{}
}
if _, seen := blocked[rr][sk]; !seen {
blocked[rr][sk] = false
rr := ruleRun{e.BlockedBy, e.TraceID}
bk := blockKey{rr, e.Step, e.MissingState}
if _, seen := recovered[bk]; !seen {
recovered[bk] = false
c := perRun[rr]
c.blocked++
perRun[rr] = c
}
}
}
Expand All @@ -395,27 +401,25 @@ func (l *Log) Stats(workflow, version string) Stats {
if e.Kind != KindRecovered || e.Workflow != workflow || e.Version != version || e.At.Before(cutoff) {
continue
}
if steps := blocked[ruleRun{e.BlockedBy, e.TraceID}]; steps != nil {
sk := stepKey{e.Step, e.MissingState}
if _, ok := steps[sk]; ok {
steps[sk] = true
}
rr := ruleRun{e.BlockedBy, e.TraceID}
bk := blockKey{rr, e.Step, e.MissingState}
if done, ok := recovered[bk]; ok && !done {
recovered[bk] = true
c := perRun[rr]
c.recovered++
perRun[rr] = c
}
}

byRule := map[string]*RuleStats{}
for rr, steps := range blocked {
for rr, c := range perRun {
rs := byRule[rr.rule]
if rs == nil {
rs = &RuleStats{BlockedBy: rr.rule}
byRule[rr.rule] = rs
}
rs.BlockedRuns++
all := true
for _, recovered := range steps {
all = all && recovered
}
if all {
if c.recovered == c.blocked {
rs.RecoveredRuns++
}
}
Expand Down
Loading