Long-term tracking issue for showing progress during a long /review / /describe run (deliberately not built now — see rationale). Filed after an investigation prompted by the pr-agent 0.45.0 upgrade; recording the findings so the next attempt does not re-derive them.
The problem
A run takes ~60s. The running card shows an inferred phase (inferPhase, chat/utils/format.ts) plus an elapsed timer, but all four phases it recognizes occur before the model is called:
| Phase |
Log marker |
| Init config |
response language |
| Parse diff |
PR main language |
| Assemble prompt |
Reviewing PR / Generating a PR description / Answering a PR question |
| Waiting on the LLM |
returning full diff / Tokens: N |
So the indicator reaches its last phase within seconds and then sits still for the rest of the run — static exactly where reassurance matters most. All six markers still exist in 0.45.0, so this is not upgrade breakage.
Findings (measured, not inferred)
- pr-agent emits no logs during
chat_completion. It is waiting on an HTTP response. Without changing the shim there is no new information to display during the long tail — structural, not a matter of looking harder.
- codex
exec --json has no token-level delta. The answer arrives whole inside item.completed / agent_message. A multi-step task does emit several such events as it goes (3 in a file-reading test), so paragraph-level progress exists — but the shim calls proc.communicate() (waits for exit) and then keeps only the last agent_message, so those intermediate events are currently discarded.
- claude
-p --output-format stream-json --include-partial-messages does have token-level deltas (content_block_delta). The two CLIs therefore differ in granularity and in event shape.
- Chunked review does not provide a progress sequence.
enable_large_pr_chunking defaults to false, so Number of PR chunk calls: N never appears; and even enabled, the chunks run under a single asyncio.gather — concurrent, not sequential. There is no 1/N to count through.
Why it is not built now
The only readily available signal is codex''s event stream, and consuming it directly would bind progress display to one CLI''s output protocol. Providers are pluggable here: API mode goes through litellm, CLI mode is driven by the _CLI_SPECS table, and the two adapted CLIs already disagree on event shape. Building it for codex means every other provider silently has no progress at all — the per-provider variation that spec table exists to remove.
The shape a real fix would take
Define the progress contract at the chat_completion layer, not at any one provider''s output format:
- one "progress event" contract that layer emits, carried on the existing stderr sentinel channel (the same path
@@MEEBOX_USAGE@@ and @@MEEBOX_LLM_ERROR@@ already use, so main → renderer needs no new plumbing);
- API mode satisfies it from litellm''s stream (0.45.0 ships
_handle_streaming_response in litellm_helpers.py, and the shim already wraps _get_completion, so the interception point exists);
- each CLI satisfies it from its own spec-provided parser;
- a provider that cannot produce events degrades to none, rather than the feature existing only for whoever happens to be selected.
That is a structural change, worth doing when the benefit is concrete rather than to fill a progress bar.
Close condition
Cheap improvements that do not need any of the above
Noted for completeness; none were applied, since each is static and the elapsed timer already covers "it is not stuck":
- surface the
Tokens: N figure already parsed for the "waiting" phase, as a sense of input size.
Long-term tracking issue for showing progress during a long
/review//describerun (deliberately not built now — see rationale). Filed after an investigation prompted by the pr-agent 0.45.0 upgrade; recording the findings so the next attempt does not re-derive them.The problem
A run takes ~60s. The running card shows an inferred phase (
inferPhase,chat/utils/format.ts) plus an elapsed timer, but all four phases it recognizes occur before the model is called:response languagePR main languageReviewing PR/Generating a PR description/Answering a PR questionreturning full diff/Tokens: NSo the indicator reaches its last phase within seconds and then sits still for the rest of the run — static exactly where reassurance matters most. All six markers still exist in 0.45.0, so this is not upgrade breakage.
Findings (measured, not inferred)
chat_completion. It is waiting on an HTTP response. Without changing the shim there is no new information to display during the long tail — structural, not a matter of looking harder.exec --jsonhas no token-level delta. The answer arrives whole insideitem.completed/agent_message. A multi-step task does emit several such events as it goes (3 in a file-reading test), so paragraph-level progress exists — but the shim callsproc.communicate()(waits for exit) and then keeps only the lastagent_message, so those intermediate events are currently discarded.-p --output-format stream-json --include-partial-messagesdoes have token-level deltas (content_block_delta). The two CLIs therefore differ in granularity and in event shape.enable_large_pr_chunkingdefaults to false, soNumber of PR chunk calls: Nnever appears; and even enabled, the chunks run under a singleasyncio.gather— concurrent, not sequential. There is no 1/N to count through.Why it is not built now
The only readily available signal is codex''s event stream, and consuming it directly would bind progress display to one CLI''s output protocol. Providers are pluggable here: API mode goes through litellm, CLI mode is driven by the
_CLI_SPECStable, and the two adapted CLIs already disagree on event shape. Building it for codex means every other provider silently has no progress at all — the per-provider variation that spec table exists to remove.The shape a real fix would take
Define the progress contract at the
chat_completionlayer, not at any one provider''s output format:@@MEEBOX_USAGE@@and@@MEEBOX_LLM_ERROR@@already use, so main → renderer needs no new plumbing);_handle_streaming_responseinlitellm_helpers.py, and the shim already wraps_get_completion, so the interception point exists);That is a structural change, worth doing when the benefit is concrete rather than to fill a progress bar.
Close condition
chat_completionlayer, with at least API mode and both adapted CLIs satisfying it (or degrading explicitly).Cheap improvements that do not need any of the above
Noted for completeness; none were applied, since each is static and the elapsed timer already covers "it is not stuck":
Tokens: Nfigure already parsed for the "waiting" phase, as a sense of input size.