fix(runner): a guard-skipped step is no longer a silent log line

The strict echo-skip left only a logger.Warn, so a step the guard discarded was
indistinguishable in the trace from a picker that legitimately declined. Any
yield or actions-per-hour figure computed from model traces mixed the two.
Every path that ends a step without a model-chosen action now records its own
outcome.

Claude-Session: https://claude.ai/code/session_01A5KmftdEJ49A9z5mF5ESrX
This commit is contained in:
pj committed 2026-08-12 22:57:06 +05:30
1 parent a97c09f6dc
commit 0b70dd1659
3 files changed
+457 -33

No files matched your search

+11 -4
View File
@@ -15,9 +15,11 @@ import (
// ActionSource resolves the next action for a step. Both runtimes (the goja
// picker and the web/V8 picker) implement it so the runner loop has one path
// and no per-step driver type assertion. NextAction returns verifier.ErrNoAction
// when the picker declined to act this tick.
// when the picker declined to act this tick. stepIndex is the trace line the
// decision belongs to, so a source that keeps its own records can key them to it
// rather than counting steps a second time.
type ActionSource interface {
NextAction(ctx context.Context) (verifier.Action, error)
NextAction(ctx context.Context, stepIndex int) (verifier.Action, error)
}
// ExtractorSource yields per-step extractor overrides the runner applies after
@@ -34,7 +36,7 @@ type gojaSource struct {
verifier *verifier.Verifier
}
func (s gojaSource) NextAction(context.Context) (verifier.Action, error) {
func (s gojaSource) NextAction(context.Context, int) (verifier.Action, error) {
return s.verifier.NextAction()
}
@@ -49,7 +51,7 @@ type webSource struct {
web driver.WebDriver
}
func (s webSource) NextAction(ctx context.Context) (verifier.Action, error) {
func (s webSource) NextAction(ctx context.Context, _ int) (verifier.Action, error) {
raw, err := s.web.NextActionFromV8(ctx)
if err != nil {
return verifier.Action{}, fmt.Errorf("v8 next action: %w", err)
@@ -108,5 +110,10 @@ func pickSources(options Options) (ActionSource, ExtractorSource, error) {
logger: logger,
history: newActionHistory(llmHistorySize),
}
// Assigned only when present so the interface field stays nil rather than
// holding a typed nil pointer that would panic on the first record.
if options.TraceWriter != nil {
action.recorder = options.TraceWriter
}
return action, extractor, nil
}