Extract WTA prompt building and turn metrics - #529
Conversation
Move ACP prompt assembly, pane context resolution, and prompt audit logging into focused prompt modules. Move per-turn timing and response telemetry into a neutral metrics module while preserving session and callback orchestration in the ACP client. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: f0b2a188-f62a-48b7-b316-6fc31ed3880c
There was a problem hiding this comment.
Pull request overview
This PR refactors the WTA ACP client implementation by extracting prompt construction/context resolution and per-turn timing/telemetry into dedicated modules, reducing client.rs size and clarifying module dependencies within tools/wta/src/protocol/acp/.
Changes:
- Added
prompt_builder.rsto centralize prompt template selection, per-session template memoization, prompt assembly, and per-turn audit logging. - Expanded
prompt_context.rsto own pane/context resolution (active vs. source pane for autofix), shell identity detection, and shared terminal-buffer capture helpers/providers. - Added
turn_metrics.rsto own prompt timing state, timing logs, and response-latency telemetry; updated call sites and tests accordingly.
Reviewed changes
Copilot reviewed 8 out of 8 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| tools/wta/src/protocol/acp/turn_metrics.rs | New module for per-turn timing state/logs and response-latency telemetry. |
| tools/wta/src/protocol/acp/prompt_context.rs | Moves terminal context assembly and pane/shell resolution helpers into a focused module with tests. |
| tools/wta/src/protocol/acp/prompt_builder.rs | New module for prompt template memoization, prompt assembly, and turn audit logging. |
| tools/wta/src/protocol/acp/mod.rs | Wires new prompt/timing modules into the ACP module tree. |
| tools/wta/src/protocol/acp/mock_agent_tests.rs | Updates imports to use the new prompt/timing modules. |
| tools/wta/src/protocol/acp/client.rs | Switches prompt assembly/timing responsibilities to the extracted modules. |
| tools/wta/src/app.rs | Updates import of prompt_timing_log to the new module. |
| doc/specs/wta-architecture-refactor.md | Updates the refactor plan/status to reflect the new module boundaries. |
Use bounded iterators for prompt snippets, compact session IDs, and pane-output truncation while preserving the existing output. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: f0b2a188-f62a-48b7-b316-6fc31ed3880c
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 8 out of 8 changed files in this pull request and generated no new comments.
Comments suppressed due to low confidence (2)
tools/wta/src/protocol/acp/prompt_builder.rs:243
log_turn_tracecallsprompt_text.chars().count()to decide whether to include a tail snippet. Since prompts can be large (template + captured context), this forces an O(n) full scan of the prompt on every turn just for logging. You can keep the same behavior with a bounded scan by checking whether there is any character afterHEAD_LEN + TAIL_LEN.
let tail = if prompt_text.chars().count() > HEAD_LEN + TAIL_LEN {
snippet(prompt_text, TAIL_LEN, false)
} else {
String::new()
};
tools/wta/src/protocol/acp/prompt_context.rs:38
truncate_for_promptalways allocatestruncatedand then (when the input is already within budget) allocates again viatext.to_string(). Returningtruncatedin the non-truncation case avoids an extra allocation/copy on every call.
if chars.next().is_none() {
text.to_string()
} else {
format!("{truncated}\n...<truncated>")
}
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 8 out of 8 changed files in this pull request and generated no new comments.
Comments suppressed due to low confidence (2)
tools/wta/src/protocol/acp/prompt_context.rs:38
truncate_for_promptalways allocatestruncated, and when the input is already within budget it allocates again viatext.to_string(). This doubles the work on the common non-truncation path; you can returntruncateddirectly (it already contains the full text when no extra char remains).
fn truncate_for_prompt(text: &str, max_chars: usize) -> String {
let mut chars = text.chars();
let truncated: String = chars.by_ref().take(max_chars).collect();
if chars.next().is_none() {
text.to_string()
} else {
format!("{truncated}\n...<truncated>")
}
tools/wta/src/protocol/acp/prompt_builder.rs:243
log_turn_traceusesprompt_text.chars().count()to decide whether to include a tail snippet, which scans the entire (potentially large) prompt every turn. You can make this check bounded by looking for the (HEAD_LEN+TAIL_LEN+1)th character viaskip(...).next()instead.
let head = snippet(prompt_text, HEAD_LEN, true);
let tail = if prompt_text.chars().count() > HEAD_LEN + TAIL_LEN {
snippet(prompt_text, TAIL_LEN, false)
} else {
String::new()
};
Summary
turn_metrics.rsclienttoprompt_buildertoprompt_context, while keeping callbacks and session orchestration inclient.rsclient.rsby roughly 1,670 lines and update the architecture refactor planValidation
cargo test --manifest-path tools/wta/Cargo.toml --quiet(1213 passed)cargo build --manifest-path tools/wta/Cargo.toml --quietcargo clippy --manifest-path tools/wta/Cargo.toml --all-targets --quietremains blocked by the pre-existingclippy::never_looperror insrc/ui/recommendations.rs; this PR does not modify that file