Skip to content

feat(observability): auto-construct TrajectoryRecorder for every chat() call #29

Description

@justrach

Background

Issue #28 landed the trajectory_events table, the TrajectoryRecorder type, the orchestrator hook, and the /trace + /resume slash commands. But the recorder is never actually constructed in the production code path — it sits as Option<Arc<TrajectoryRecorder>> defaulting to None on every Orchestrator built in `crates/forge_app/src/app.rs`.

Net effect: `/trace` always reports "No trajectory events recorded" because nothing populates the table.

What's blocking

The recorder needs an `Arc`. The natural place to build it is at the same site where the Orchestrator is constructed (`crates/forge_app/src/app.rs:175-184`). But `ForgeApp` is bounded on `S: Services + EnvironmentInfra<...>`, not `+ TrajectoryRepo`. Adding the bound has too much blast radius — `agent_executor.rs` and other call sites of `ForgeApp::chat` would each need updating.

Proposed fix

Inject the recorder from one layer up — `crates/forge_api/src/forge_api.rs`, where `self.infra: Arc` is already bounded on `+ TrajectoryRepo` (added in #28). Add a builder method on ForgeApp that takes `Option<Arc>`, and have ForgeAPI::chat construct it before calling ForgeApp::chat.

Sketch:

```rust
// In forge_api.rs::API::chat:
let trajectory_repo: Arc = self.infra.clone();
let initial_seq = trajectory_repo
.next_seq_for(&chat.conversation_id.to_string(), agent_id.as_str())
.await
.unwrap_or(0);
let recorder = Arc::new(TrajectoryRecorder::new(
trajectory_repo,
chat.conversation_id.to_string(),
agent_id.as_str().to_string(),
None,
initial_seq,
));
self.app().chat_with_recorder(agent_id, chat, Some(recorder)).await
```

Acceptance

  • After a tool call in the REPL, `/trace` shows the rows it produced
  • Running `/resume ` then issuing a message continues the seq numbering instead of restarting at 0
  • No new bound on `ForgeApp` (callers of `ForgeApp::chat` unchanged)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions