beginStep validates preconditions, creates a debug step, and returns a handle plus an enriched context carrying StepContext and attemptSink. Returns (nil, original ctx) when debug recording should be skipped.
( ctx context.Context, svc *Service, opts RecorderOptions, op Operation, normalizedReq any, )
| 206 | // handle plus an enriched context carrying StepContext and attemptSink. |
| 207 | // Returns (nil, original ctx) when debug recording should be skipped. |
| 208 | func beginStep( |
| 209 | ctx context.Context, |
| 210 | svc *Service, |
| 211 | opts RecorderOptions, |
| 212 | op Operation, |
| 213 | normalizedReq any, |
| 214 | ) (*stepHandle, context.Context) { |
| 215 | if svc == nil { |
| 216 | return nil, ctx |
| 217 | } |
| 218 | |
| 219 | rc, ok := RunFromContext(ctx) |
| 220 | if !ok || rc.RunID == uuid.Nil { |
| 221 | return nil, ctx |
| 222 | } |
| 223 | |
| 224 | chatID := opts.ChatID |
| 225 | if chatID == uuid.Nil { |
| 226 | chatID = rc.ChatID |
| 227 | } |
| 228 | if !svc.IsEnabled(ctx, chatID, opts.OwnerID) { |
| 229 | return nil, ctx |
| 230 | } |
| 231 | |
| 232 | holder, reuseStep := reuseHolderFromContext(ctx) |
| 233 | if reuseStep { |
| 234 | holder.mu.Lock() |
| 235 | defer holder.mu.Unlock() |
| 236 | // Only reuse the cached handle if it belongs to the same run. |
| 237 | // A different RunContext means a new logical run, so we must |
| 238 | // create a fresh step to avoid cross-run attribution. |
| 239 | if holder.handle != nil && holder.handle.stepCtx.RunID == rc.RunID { |
| 240 | enriched := ContextWithStep(ctx, holder.handle.stepCtx) |
| 241 | enriched = withAttemptSink(enriched, holder.handle.sink) |
| 242 | return holder.handle, enriched |
| 243 | } |
| 244 | } |
| 245 | |
| 246 | stepNum := nextStepNumber(rc.RunID) |
| 247 | step, err := svc.CreateStep(ctx, CreateStepParams{ |
| 248 | RunID: rc.RunID, |
| 249 | ChatID: chatID, |
| 250 | StepNumber: stepNum, |
| 251 | Operation: op, |
| 252 | Status: StatusInProgress, |
| 253 | HistoryTipMessageID: rc.HistoryTipMessageID, |
| 254 | NormalizedRequest: normalizedReq, |
| 255 | }) |
| 256 | if err != nil { |
| 257 | svc.log.Warn(ctx, "failed to create chat debug step", |
| 258 | slog.Error(err), |
| 259 | slog.F("chat_id", chatID), |
| 260 | slog.F("run_id", rc.RunID), |
| 261 | slog.F("operation", op), |
| 262 | ) |
| 263 | return nil, ctx |
| 264 | } |
| 265 |