Repository navigation
diagnose(shell): trace input acceptance and writes (#73) - #196
Conversation
InputQueue exposes an opt-in trace hook; the shell installs it alongside the output trace so a stalled scenario shows whether keystrokes were accepted but never written, or written with no output following.
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 1f0ce20302
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| g_output_trace = &trace_output_; | ||
| { std::lock_guard<std::mutex> lock(g_trace_mutex); g_trace_ids[&input_] = identity_; } | ||
| input_.set_trace_hook(&trace_input); |
There was a problem hiding this comment.
Keep the trace sink alive independently of individual tabs
With TERMFORGE_OUTPUT_TRACE enabled, each new ShellWindow overwrites this global pointer with its own member stream, but its destructor never unregisters that stream. In the hosted workspace, closing the most recently created tab destroys it through Workspace::collect_child (workspace_interactions.hpp), while older tabs remain usable. Their next input trace dereferences the destroyed stream in trace_input, causing undefined behavior and potentially crashing the diagnostic run. Use a lifetime-managed shared sink or route each hook to its owning window's live stream.
Useful? React with 👍 / 👎.
| *g_output_trace << "{\"schema\":\"termforge-input-trace-v1\",\"terminal\":" << terminal | ||
| << ",\"phase\":\"" << (written ? "write" : "enqueue") | ||
| << "\",\"t_ms\":" << GetTickCount64() << ",\"bytes\":" << bytes | ||
| << ",\"accepted\":" << (accepted ? "true" : "false") << "}\n"; | ||
| g_output_trace->flush(); |
There was a problem hiding this comment.
Synchronize output records with input trace writes
With tracing enabled, the input writer thread writes and flushes this stream under g_trace_mutex, but ShellWindow::tick() writes output records to the same trace_output_ stream without acquiring that mutex. Even with a single terminal, typing while output is consumed can therefore concurrently mutate the same std::ofstream, producing a data race and corrupting the JSONL evidence this change is intended to collect. Protect both input and output record emission with the same synchronization mechanism.
Useful? React with 👍 / 👎.
Registering the trace stream per window removed a dangling global stream pointer that could be used by a still-running writer thread after another window closed; the map entry is erased before the stream is destroyed.
内容
把输出消费轨迹扩展为双向:
TERMFORGE_OUTPUT_TRACE现在同时记录输入路径:{"schema":"termforge-input-trace-v1","terminal":1,"phase":"enqueue","t_ms":...,"bytes":1,"accepted":true} {"schema":"termforge-input-trace-v1","terminal":1,"phase":"write","t_ms":...,"bytes":1,"accepted":true}enqueue:UI 侧把按键交给输入队列时(accepted 反映是否被拒);write:写线程把它真正写入会话之后;GetTickCount64),可直接排出"输入已接受但无写入"或"写入成功但无输出"的时间线。动机(#73)
上一轮已把"首记录前超时"定位到:输入注入后约 30 秒两个终端都没有消费任何输出,最后各收到 16 字节即到期。缺的正是"输入侧那几秒发生了什么",本 PR 补上。
本地验证
边界
Refs #73 #194