Skip to content

Phase 1.x: forcible script termination from boxen dialog Ctrl-C (deferred from #793) #799

Description

@jsavin

Problem

Phase 1 of the UI bridge (PR #793) shipped with cooperative cancel only: Esc/Ctrl-C closes the dialog modal and returns NULL/false to the dispatched script. The script handles the cancel however it wants — typically by aborting the operation.

The original design (planning/phase_c/BOXEN_USERTALK_UI_BRIDGE.md decision #3 v1) called for hard kill via flthreadkilled — Ctrl-C would cancel the dialog AND interrupt the running script. Investigation during /gate review (2026-06-23) revealed:

  • flthreadkilled is per-thread; boxen REPL dispatches scripts on the main thread, so setting it would also halt the main loop without careful reset semantics
  • The legacy Mac Frontier mechanism (Esc-kills-thread) was acknowledged-imprecise once the runtime became multi-threaded
  • All currently-known scripts (jump, keycodes, etc.) check the dialog return and abort gracefully on cancel

JES + AI decided 2026-06-23 to defer hard-kill and revisit when a concrete script demonstrates the need.

When to revisit

Open this issue when:

  • A dispatched script ignores dialog.* cancel returns and keeps running (waste of user's intent)
  • A long-running CPU-bound dispatched script needs to be killable via Ctrl-C
  • A user reports "I cancelled the dialog but the script kept going"

Implementation sketch (when we get to it)

Two options:

Option A: Per-modal abort flag. Bridge sets boxen_ui_script_abort_requested = true on Ctrl-C; a hook in headless_backgroundtask (or a new yield-point check) calls langerror(abortedscripterror) if the flag is set AND the current thread is the one that opened the modal. Per-thread tracking via a tracked_thread_id field on the bridge's state.

Option B: Spawn dispatched scripts on a dedicated thread. Then flthreadkilled on THAT thread works cleanly without touching the main loop. Bigger refactor; changes the GIL access pattern for dispatch.

Option A is smaller; Option B is more architecturally clean. Pick when the concrete need arrives.

Context

Acceptance (when implemented)

  • Ctrl-C in a bridge modal aborts the dispatched script (verified via test that the script's post-dialog code does NOT run)
  • Main REPL loop survives the abort cleanly (no need to re-enter)
  • Abort is targeted at the dispatched script's thread, not whichever thread happens to be holding the GIL
  • Test in test_06_ui_bridge.py exercises the abort path

Metadata

Metadata

Assignees

No one assigned

    Labels

    priority/p2Medium priority - nice to havescope/medium1-2 weeks, requires design workstatus/decision-neededArchitectural decision required before proceedingtype/featureNew feature or requestworkstream/verb-portingKernel verb implementation and bindings

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions