|
| 1 | +--- |
| 2 | +title: "Crawl progress is a black box — 'crawling the web, this takes a minute…' needs traces the operator can watch" |
| 3 | +lede: "A didi crawl is a minute-plus of silence with one static status line. The model is actually narrating as it works — 'searching for Quell Foundation LinkedIn…' — and those traces exist in the response stream today; they just never leave prompt-runner. The operator should watch the agent work, not stare at an ellipsis." |
| 4 | +date_created: 2026-07-24 |
| 5 | +date_modified: 2026-07-24 |
| 6 | +authors: |
| 7 | + - Michael Staton |
| 8 | +augmented_with: |
| 9 | + - Claude Code on Claude Fable 5 |
| 10 | +semantic_version: 0.0.0.1 |
| 11 | +tags: |
| 12 | + - Issue |
| 13 | + - Usability |
| 14 | + - Augment-It |
| 15 | + - Didi-Crawl |
| 16 | + - Search-And-Add |
| 17 | + - Observability |
| 18 | + - Prompt-Runner |
| 19 | +status: Open · Jotted |
| 20 | +--- |
| 21 | + |
| 22 | +# Crawl progress needs visible traces |
| 23 | + |
| 24 | +## The symptom (screenshot-confirmed, 2026-07-24) |
| 25 | + |
| 26 | +Fire a crawl → "didi crawl · identity links · crawling the web — this takes |
| 27 | +a minute…" and a disabled "crawling…" button. Then nothing changes for |
| 28 | +60–90 seconds. No indication of what didi is searching, how many searches |
| 29 | +have run, whether it's stuck, or how close it is. The operator can't tell a |
| 30 | +healthy crawl from a hung one — the same live/not-live blindness |
| 31 | +[[Live-Not-Live-Indicator-Tooling-And-Cross-Service-Error-Surfacing]] names, |
| 32 | +now on the agentic path where waits are longest. |
| 33 | + |
| 34 | +## The traces already exist |
| 35 | + |
| 36 | +The Anthropic response with server-side web search interleaves the model's |
| 37 | +running narration ("I'll search for X…") with `server_tool_use` / |
| 38 | +`tool_result` blocks — `prompt-runner`'s `extractText` deliberately SKIPS |
| 39 | +past all of it to the final answer (`anthropic.ts` — "the actual answer is |
| 40 | +the text AFTER the last non-text block"). The material for a progress feed |
| 41 | +is being received and thrown away. What's missing is a channel: |
| 42 | + |
| 43 | +1. **prompt-runner**: run the crawl via the streaming API (or at minimum |
| 44 | + emit per-`pause_turn`-continuation beats), publishing progress frames to |
| 45 | + a NATS subject (`organization.crawl.progress` with a crawl id) — the |
| 46 | + `pack.fan_out.completed` publish + the run/apply `*.completed` events |
| 47 | + are the in-house precedent for fire-and-forget progress publishes. |
| 48 | +2. **workspace**: forward those frames to the browser as WS event frames — |
| 49 | + the event-frame machinery (`ServerFrame`/`EventFrame`, job events) |
| 50 | + already exists; crawl progress is a new event kind riding it. |
| 51 | +3. **search-and-add (and the chat rail later)**: render a rolling trace |
| 52 | + line under the crawl bar — search queries as they fire, "N candidates so |
| 53 | + far", the narration snippets. Even a heartbeat ("still working · |
| 54 | + third search") beats the frozen ellipsis. |
| 55 | + |
| 56 | +## Open questions |
| 57 | + |
| 58 | +- [ ] Streaming SDK call vs. coarse beats (per web-search tool_use block vs. |
| 59 | + per pause_turn continuation) — coarse is a fraction of the work and may |
| 60 | + be enough; streaming gives the real "didi is typing" feel. |
| 61 | +- [ ] Does the trace persist (part of the crawl's reply, reviewable after) |
| 62 | + or is it ephemeral display only? Lean: last N lines ephemeral, final |
| 63 | + summary line kept. |
| 64 | +- [ ] Same channel should serve the team crawl on the workbench and the |
| 65 | + chat door — one progress subject, every surface subscribes. |
0 commit comments