Files
foxhunt/docs/archive/wave_d/reports/OBSERVABILITY_FIX_VALIDATION.md
jgrusewski 433af5c25d chore: Major codebase cleanup - remove deprecated files and organize structure
- Docker: Delete 23 deprecated Dockerfiles, fix CI/CD to use Dockerfile.foxhunt-build
- Config: Remove 36 .env files, keep 4 essential, delete config/environments/
- Docs: Archive 614 Wave D files to docs/archive/wave_d/, 95% reduction in root
- Scripts: Delete 56 deprecated scripts, keep 58 production-critical (49% reduction)
- Python: Organize 37 scripts into scripts/python/ subdirectories, delete ml/python/
- Build: Remove 1GB artifacts, delete old venvs, clean Python cache from git
- Migrations: Delete deprecated directory (4,432 lines), remove duplicate database/migrations/
- Infrastructure: Delete deployment/ (61 files), docs/scripts/ (8 files)

Total impact: ~2,500 files cleaned, 750MB+ space freed, zero production impact
All deleted scripts backed up to archives. runpod/ and tests/runpod/ preserved.
data_acquisition_service retained per user request.
2025-10-30 01:02:34 +01:00

4.4 KiB

Observability Async Lifetime Fix - Validation Report

Date: 2025-10-23 Agent: Claude Code Task: Fix async lifetime error in correlation.rs line 235 Status: COMPLETE (Already committed in 28b4ca9e)


Summary

The async lifetime error at line 235 in /home/jgrusewski/Work/foxhunt/common/src/observability/correlation.rs has been successfully fixed. The error was:

error[E0726]: implicit elided lifetime not allowed here
   --> common/src/observability/correlation.rs:235:10

Root Cause

The set_correlation_id function uses tokio::task_local! storage and calls try_with with a closure that returns an async block. The compiler couldn't infer the lifetime relationship between the closure and the returned future without an explicit annotation.

Solution Applied

Changed the closure from an implicit async block to an explicit pinned future with lifetime annotation:

Before:

pub async fn set_correlation_id(correlation_id: CorrelationId) {
    CURRENT_CORRELATION_ID
        .try_with(|id| async move {
            let mut guard = id.write().await;
            *guard = Some(correlation_id);
        })
        .ok();
}

After:

pub async fn set_correlation_id(correlation_id: CorrelationId) {
    CURRENT_CORRELATION_ID
        .try_with(|id| -> std::pin::Pin<Box<dyn std::future::Future<Output = ()> + '_>> {
            Box::pin(async move {
                let mut guard = id.write().await;
                *guard = Some(correlation_id);
            })
        })
        .ok();
}

Key Changes

  1. Added explicit return type: -> std::pin::Pin<Box<dyn std::future::Future<Output = ()> + '_>>
  2. Wrapped async block: Box::pin(async move { ... })
  3. Added lifetime annotation: + '_ (borrows from the closure parameter)

Validation Results

Compilation

  • Status: PASSED
  • Command: cargo check -p common
  • Result: Clean compilation with 0 errors
  • Time: 6m 12s

Test Suite

  • Status: PASSED
  • Command: cargo test -p common --lib
  • Result: 158/158 tests passing (100%)
  • Time: 1.21s

Git History

  • Commit: 28b4ca9e - "fix(common): Fix async lifetime in correlation.rs line 263"
  • Note: The fix for line 235 was committed alongside the fix for line 263 (same pattern)

The same lifetime pattern was applied to both async functions in correlation.rs:

  1. set_correlation_id (line 233-242) - Sets correlation ID in task-local storage
  2. get_correlation_id (line 261-269) - Retrieves correlation ID from task-local storage

Both functions use tokio::task_local! storage and require explicit lifetime annotations for their closures.

Technical Context

Why the lifetime annotation is needed:

  • tokio::task_local! uses thread-local storage for async contexts
  • The closure passed to try_with must specify how long it borrows the task-local data
  • Without + '_, the compiler cannot determine if the future can safely reference the closure's parameters
  • The '_ lifetime means "borrow for the lifetime of the closure parameter"

Alternative approaches considered:

  • Using impl Future<Output = ()> + '_ directly (doesn't work with try_with)
  • Using async fn instead of async block (incompatible with closure context)
  • Avoiding Box::pin (required for dynamic trait object)

Impact Assessment

No Breaking Changes

  • Public API unchanged
  • Function signature remains the same
  • Existing code using these functions will continue to work
  • Tests pass without modification

Performance

  • Minimal overhead from Box::pin (one heap allocation per call)
  • Acceptable for observability/tracing use case
  • Not in hot path (used for request correlation, not per-operation)

Maintainability

  • Explicit lifetime makes code intent clearer
  • Easier to understand borrowing semantics
  • Follows Rust best practices for async closures

Conclusion

The async lifetime error has been successfully resolved. The fix:

  1. Compiles cleanly
  2. Passes all tests (158/158)
  3. Maintains backward compatibility
  4. Already committed to repository
  5. No production impact

Status: PRODUCTION READY - No further action required.


References

  • File: /home/jgrusewski/Work/foxhunt/common/src/observability/correlation.rs
  • Commit: 28b4ca9e
  • Error: E0726 (implicit elided lifetime not allowed here)
  • Rust Edition: 2021