Repository navigation
fix: plan_review raw-JSON leak, empty-response done bug, blocked status, gzip - #20
Merged
Merged
Conversation
The DB-restore path (a reloaded chat re-rendering a plan_review message from
its stored row) inserted the card with .before() in front of the message
bubble instead of replacing its content. The generic assistant-message branch
still rendered the raw JSON into that bubble first, so both stayed visible —
reported live with a screenshot ("что за странное сообщение под карточкой").
Folded plan_review into the same if/else-if chain agent_plan already uses,
replacing .msg's innerHTML directly instead of inserting before it.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Y2ZSHUdJHqVsvSNQ5ZeQ3f
…d status; gzip responses
Real incident (T-001, 2026-09-22): a research task hit the built-in
WebSearch's "connectors disabled" error a few times, eventually found real
results, then the CLI's final turn produced literally empty text — twice in
a row — and the run just stopped. subtype was still 'success', so the task
was marked 'done' with failure_reason:null: zero findings, no report, no
escalation, and a green checkmark on the board.
- lastTurnText tracks each loop iteration's OWN text contribution (not the
whole run's). isSuccess with an empty last turn is no longer 'done' —
it flows into the existing failure path as failure_reason:'empty_response'.
- report_result({blocked:true, reason}) is now the correct way to escalate:
a new 'blocked' status, distinct from both 'done' and 'cancelled', with
its own Kanban column/badge/chain-icon (5 languages) so a card that
needs a human does not get lost among finished work.
- gzip: found live while investigating a "kanban loads slowly" report —
kanban.html (~140KB) and /api/tasks (~190KB+ for 26 tasks, growing with
every task) went out completely uncompressed, verified with curl against
the real container. One line (compression middleware) fixes it.
Verified end-to-end against a real server + scripted `claude` binary
(test/task-blocked-and-empty-response.test.js), not just the source text.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Y2ZSHUdJHqVsvSNQ5ZeQ3f
…rn/blocked code CI caught it: 'API run receives effort' failed because the previous commit added ~700 chars ahead of cli.send(...) inside startTask, pushing its effort: field (19850 -> 20019) past the pinned test's fixed 20000-char window. Not a real behavioural regression — cli.send still passes effort exactly as before. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Y2ZSHUdJHqVsvSNQ5ZeQ3f
Same failure mode as the previous commit, different pinned window: the
'blocked' status work added a blockedInfo/blockedBadge pair ahead of the
footer template inside makeCard, pushing \${cfgBadges} (originally ~2450)
past the old 2600-char window. Full local suite (70 files) is green after
this — nothing else was hit.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Y2ZSHUdJHqVsvSNQ5ZeQ3f
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Three fixes from this session, all found from live use of the real deployed board:
1. Raw JSON under the plan_review card
A reloaded chat re-rendering a
plan_reviewmessage from its DB row inserted the cardin front of the raw-JSON message bubble instead of replacing it — both stayed visible.
Reported live with a screenshot. Now folded into the same if/else-if chain
agent_planalready uses.
2. A task can be marked "done" with a genuinely empty final answer
Real incident: task T-001 ("Спайк Easypanel API") hit the built-in WebSearch's
"connectors disabled" error a few times, found real results eventually, then its final
CLI turn produced literally empty text — twice in a row — and the run just stopped.
subtypewas still'success', so it was markeddonewithfailure_reason: null. Zerofindings, no report, no escalation to a human, green checkmark on the board.
lastTurnTexttracks each loop iteration's own text (not the whole run's cumulativetext, which can be non-empty from earlier turns/errors/notices). An empty last turn on
an otherwise-successful run is no longer
done— it becomesfailure_reason:'empty_response'.report_result({blocked:true, reason})is now the correct way for an agent to escalateinstead of ending silently or claiming success — a new
blockedstatus, distinct fromboth
doneandcancelled, with its own Kanban column/badge/chain-icon (5 languages)so a card that genuinely needs a human doesn't get lost among finished work.
3. Kanban/API responses were served fully uncompressed
Found live while investigating a "the board loads slowly" report:
kanban.html(~140KB)and
/api/tasks(~190KB+ for 26 tasks, growing with every task —description/contextdominate) went out with no
Content-Encodingat all, verified withcurl -H 'Accept-Encoding: gzip'directly against the running container. One line(
compressionmiddleware) fixes it.Testing
test/task-blocked-and-empty-response.test.js: boots a real server + a realscripted
claudebinary end to end (not source-text pattern matching) and covers allfour scenarios — empty final turn,
blocked, a normal run is unaffected, gzip headers.test/render/plan-review.test.mjsfor the raw-JSON-leak fix.npm testrun: everything green except one pre-existing tmux-timing flake(
test/engine-spawn-cmd.test.js, unrelated file, confirmed passing in isolation).🤖 Generated with Claude Code
https://claude.ai/code/session_01Y2ZSHUdJHqVsvSNQ5ZeQ3f