fix(telemetry): name the deliberate hold before a reply lands #656

Merged
coilyco-ops merged 1 commit from fix/name-the-deliberate-hold-claude into main 2026-08-13 18:03:09 +00:00
Member

closes #652

The nine seconds are not missing work. They are a sleep.

func (p *turnProgress) Settle(ctx context.Context) {
	remaining := p.settleDelay()   // remainder of the 10s progress beat
	...
	select {
	case <-ctx.Done():
	case <-timer.C:
	}
}

turnProgressEvery is 10 seconds, and settleDelay returns beat - (elapsed % beat). The 9.02s in your trace is that remainder. The constant states the reason:

the grid every later message releases on, so an edit, a reply, and a failure notice all land on the same beat

So the gap was a decision, and nothing in the trace said so. That is the whole defect — three investigations this week read that region and inferred what was in it.

A span, not an event

The point is the duration, and an event carries no bar in a timeline. turn.progress.settle wraps the wait with hold_ms and beat_ms, so a reader sees both what was waited and what grid it was waiting for.

It is skipped when the hold is zero, so a turn that lands on the beat gains nothing to read rather than a zero-width span.

I predicted the wrong cause

I recorded on the issue that I expected slow Discord REST calls with no spans around them, and named react and settleFromContext as the suspects. It is neither. Worth stating because that prior would have produced a fix instrumenting three functions that are fast, and left the gap exactly where it was — a plausible change with a green gate and no effect, which is the shape #343 taught me to check for.

Tests cover the hold, the zero-hold skip, that the span context still carries the turn's progress handle, and that a turn with no progress reports no hold rather than panicking.

ward exec gate green.

closes #652 **The nine seconds are not missing work. They are a sleep.** ```go func (p *turnProgress) Settle(ctx context.Context) { remaining := p.settleDelay() // remainder of the 10s progress beat ... select { case <-ctx.Done(): case <-timer.C: } } ``` `turnProgressEvery` is 10 seconds, and `settleDelay` returns `beat - (elapsed % beat)`. The 9.02s in your trace is that remainder. The constant states the reason: > the grid every later message releases on, so an edit, a reply, and a failure notice all land on the same beat **So the gap was a decision, and nothing in the trace said so.** That is the whole defect — three investigations this week read that region and inferred what was in it. ## A span, not an event The point is the duration, and an event carries no bar in a timeline. `turn.progress.settle` wraps the wait with `hold_ms` and `beat_ms`, so a reader sees both what was waited and what grid it was waiting for. It is skipped when the hold is zero, so a turn that lands on the beat gains nothing to read rather than a zero-width span. ## I predicted the wrong cause I recorded on the issue that I expected slow Discord REST calls with no spans around them, and named `react` and `settleFromContext` as the suspects. **It is neither.** Worth stating because that prior would have produced a fix instrumenting three functions that are fast, and left the gap exactly where it was — a plausible change with a green gate and no effect, which is the shape #343 taught me to check for. Tests cover the hold, the zero-hold skip, that the span context still carries the turn's progress handle, and that a turn with no progress reports no hold rather than panicking. `ward exec gate` green.
fix(telemetry): name the deliberate hold before a reply lands
All checks were successful
ci / image-build (pull_request) Successful in 28s
ci / test (pull_request) Successful in 38s
ci / publish-echo-image (pull_request) Has been skipped
ci / publish-observed (pull_request) Has been skipped
ae308ca426
The nine seconds between the last model response and reply composition are not
missing work. turnProgress.Settle waits out the remainder of the ten second
progress beat, so an edit, a reply, and a failure notice all land on the same
grid. That is the constant's stated purpose and it had no span.

So a trace showed a gap and nothing said the gap was a decision. Three separate
investigations read that region this week and inferred what happened in it.

A child span rather than an event, because the point is the duration and an
event carries no bar in a timeline. It is skipped entirely when the hold is
zero, so a turn landing on the beat gains nothing to read.

I predicted this was slow Discord calls with no spans around them. It is a
sleep. Recorded because the wrong prior would have produced a fix that
instrumented the wrong three functions and left the gap exactly where it was.

Co-authored-by: Kai Siren <coilysiren@gmail.com>
Co-authored-by: Claude <noreply@anthropic.com>
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
coilyco-gaming/sirens-echo!656
No description provided.