Skip to content

fix: bug-hunt round — lifecycle, client framing, TUI state, input and layout fixes - #156

Merged
jkyberneees merged 16 commits into
mainfrom
fix/bug-hunt-round-3
Oct 17, 2026
Merged

jkyberneees merged 16 commits into
mainfrom
fix/bug-hunt-round-3

Conversation

@jkyberneees

Copy link
Copy Markdown
Contributor

Third bug-hunt round. Five parallel hunters covered client/stores, server/lifecycle/update, TUI event ingestion, input and rendering. Each confirmed finding has its own commit and a regression test. An adversarial review pass then found regressions and gaps in some of those fixes; they are fixed here too.

Server / CLI

  • Crash on a scheme-less --url: --url 127.0.0.1:8080 panicked with a nil request. Bare host:port now defaults to http://. Anything that is not http(s) with a host fails fast, with no retry-card loop.
  • Watchdog zombie: Stop left the exited watchdog unreaped when the child had already exited.
  • Token precedence: --token no longer overrides the token a spawned odek prints.
  • Signals: the handler is released once the TUI exits, so the first Ctrl+C during the shutdown countdown counts. Bubble Tea winning the SIGINT race is now a clean exit. -h prints usage once and exits 0.
  • Update check: a +dirty build version now sees available upgrades.

Client

  • Frame size: frames go out without HTML escaping, and one over odek's 8 MiB inbound cap is refused with a reason. odek drops the connection on such a frame, so the prompt used to vanish behind "connection lost".
  • Close: Close moves the write deadline in, so it can't wedge the update loop for up to 60s behind a stuck write.
  • Jobs 404: a "session not found" 404 on /api/jobs no longer switches the jobs watcher off permanently. The plan route keeps its permanent 404, because odek < v1.27 answers the missing route with that same body.

TUI state

  • Queued prompts: sending a queued prompt (or alt+r) no longer wipes the draft being typed.
  • Jobs watcher: a session frame inside a drained batch no longer kills the watcher chain.
  • Clear confirm: an armed ^L confirm refuses once a wake/remote turn has started.
  • Shutdown: a failed shutdown no longer blocks later reconnects. A shutdown mid-turn marks the turn interrupted.
  • Plan strip: a plan call cut off before its tool_result confirms or reverts its optimistic patch.
  • Jobs tab: the selection is anchored by job id.

TUI input

  • Approvals: with the find bar or a drawer editor open, a bare a/d/t is swallowed with a hint and never decides an approval. Esc then restores the bare keys, and Alt always works.
  • Slash commands: an unknown slash name keeps the draft, and typing an exact command name (/run) selects it over longer prefixes (/runs).
  • Find and palette: the find bar accepts spaces, and the palette keeps multi-rune reads and pastes.
  • Attachments: two files with the same base name both stay attached.
  • /run: it keeps a pending /model choice for the next turn.
  • Queue clicks: glyphs inside a prompt's text, or on the overflow tail, are no longer read as controls.

Layout

  • Fits the terminal: the stats sheet, drawer detail views and long clarify questions now fit, at 40/80/120 columns and with queue rows present. Long clarify questions page with Alt+PgUp/Alt+PgDn.
  • Detail scrolling: the scroll limit uses the sheet height, so the end of long details is reachable.
  • Character counts: episode prompts count characters, not bytes.

Not in this PR

  • Windows graceful stop: a CTRL_BREAK version was dropped after review. CREATE_NEW_PROCESS_GROUP disables console Ctrl+C for the child, and Windows has no watchdog. It needs a Job Object guard first.
  • Suggestion for an odek session: odek's 10 MB attachment limit exceeds its own 8 MiB WS frame cap.

Checks

make fmt, make vet, make lint (0 issues) and make test (race, all packages green).

🤖 Generated with Claude Code

jkyberneees and others added 16 commits October 17, 2026 16:06
…okens

A scheme-less --url (127.0.0.1:8080) reached client.Do with a nil
request and panicked; localhost:8080 parsed as scheme "localhost" and
waited out the ready timeout. Attach URLs now default to http:// and
anything that is not http(s) with a host is refused up front.

Stop returned before retiring the orphan watchdog when the child had
already been reaped, leaving the exited guard as a zombie (one per TTY
retry). --token no longer overrides the token a spawned odek prints.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The signal handler stayed registered when the user quit from inside the
TUI, swallowing the first Ctrl+C of the shutdown countdown. Bubble
Tea winning the SIGINT race exited 1 with "program was interrupted",
and -h printed usage twice before exiting 1.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A +dirty build (v1.2.0+dirty) failed the numeric parse and never saw
an available upgrade.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
sendPrompt reset the composer on every path, so a queue drain, a
queue-strip send or alt+r erased whatever the user was typing (and
persisted the empty draft). submit already clears it before sending.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
With the find bar, a drawer editor or the @ / slash popup on screen, a
bare a/d/t meant for that field decided an approval that had just
arrived (t trusts the whole risk class). Only Alt+A/D/T decide there.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
An unknown slash name erased the draft (a prompt like /etc/hosts …
was lost unrecorded); /run + enter ran /runs because the popup kept
registry order; the find bar dropped spaces (KeySpace); the palette
discarded multi-rune reads and pastes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A session frame inside a drained batch bumped jobsWatchSeq but its tick
rode a dropped per-event cmd, silencing background completion notes.
The armed ^L confirm wiped a wake turn that started meanwhile, along
with the approval odek was waiting on. A failed shutdown left
shutdownReq set, so a later real drop never reconnected; a successful
one mid-turn now marks the turn interrupted and seals live sub-agents.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A plan tool_call cancelled before its tool_result left planDirty set
with no confirm ever issued, so every later reply was dropped and the
never-written plan stayed on the strip.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
applyJobs only clamped panelSel, so a job reaped above the selected
row shifted the detail header and the stop gate onto a different job
while the output pane stayed on the old one.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Restaging keyed on the base name, so attaching b/README.md after
a/README.md silently replaced it; restage now matches the path and a
colliding name is qualified by its parent directory. /run dropped a
pending /model choice the header still showed for the next turn. A
queue-strip click located controls by the first glyph, so a ✕ in the
prompt text armed delete.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The stats sheet appended its body to a box whose lipgloss Height is a
minimum, not a clip; opening or closing a drawer detail never re-ran
layout; and the clarify question was painted in full. Each pushed View
past the terminal and the header off-screen. The detail scroll limit
used the terminal height instead of the sheet's, leaving the end of
long config / fact / job details unreachable. Episode prompts counted
bytes as chars.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
odek drops the connection on an inbound frame over 8 MiB, and bodek
allowed 10 MiB of attachments that json.Marshal grew further (HTML
escaping turns each < into six bytes): the prompt vanished behind
"connection lost". Frames now go out unescaped and an oversized one
is refused with a reason. Close moved no deadline, so it waited up to
60s behind a write stuck on a half-open link, freezing the TUI's
update loop on reconnect. A "session not found" 404 read as a missing
route, permanently switching off the jobs watcher.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
odek < v1.27 has no plan route: its session handler parses "{id}/plan"
as the id and answers "session not found", the same body as a deleted
session. Mapping it to ErrSessionNotFound left those engines polling the
plan every second (charging odek's session lookup limiter) behind a
forever "loading plan…". Only the jobs route distinguishes the two.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The attach URL error fell into the startup diagnosis card, whose
"retry: ⏎" re-ran Connect with the same bad value forever.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The new text-field gate also refused bare a/d/t under a stale slash
popup the approval composer never re-syncs, leaving Alt-less terminals
without a binding. The gate now covers only the find bar and drawer
editors, and swallows the letter (with a hint that esc closes the field)
instead of letting it type into the draft. A click on the queue strip's
overflow tail, which echoes a hidden prompt, still scanned it for
controls and could delete a different prompt.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
At 40 columns the stats overflow marker and the clarify hint wrapped to
two rows the budgets counted as one, and the clarify budget ignored the
shelf chip and queue strip — each left View one or more rows taller
than the terminal.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@cloudflare-workers-and-pages

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Preview URL Updated (UTC)
✅ Deployment successful!
View logs
bodek 20b3936 Commit Preview URL

Branch Preview URL
Oct 17 2026, 02:37 PM

@jkyberneees
jkyberneees merged commit ca94ad5 into main Oct 17, 2026
9 checks passed
@jkyberneees
jkyberneees deleted the fix/bug-hunt-round-3 branch October 17, 2026 14:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant