language: a panic that ends its task, not the process (WEB F9) #107

Open
opened 2026-10-05 21:07:21 +00:00 by orion · 3 comments
Collaborator

Goal

A panic inside a task ends that task, not the process, so a server survives one bad handler. This reverses the rule
"a panic inside a task ends the process" and changes CONCURRENCY.md section 8 ("Panics are unchanged") with it.

Design record

  • WEB.md F9 (section 8) and section 3.8.
  • WEB.md decision D10 (owner decisions, section 12/13): decided (a) for v1, "a panic in a handler ends the
    process and a supervisor restarts it". Option (b) is this issue; the record says it "deserves the numbers of a
    running server before it is made".
  • PANICS.md (section 2.7 table, "Not a recoverable panic"): sandbox and torb test are the only recovered panics.

Status: not ready (waiting)

  1. Owner-reserved. D10 is an owner decision that was settled the other way for v1; flipping it changes a language
    rule that programs and documentation rely on. The loop does not decide it. The owner question is filed when (2) is
    met, with the measured numbers.
  2. No numbers yet. std/web does not exist on main (std/ has http, network, task, no web); #94 (core)
    is open. There is no running server to measure what a panicking handler leaks.

What exists already (analysis of main, runtime)

  • runtime/panic.c: torb_begin_recovery / torb_end_recovery, a per-worker recovery point (worker->recovery);
    a panic with a point set jumps to it, otherwise exits 101.
  • runtime/task.c: a task of a test is resumed under a recovery point (torb_resume_recovered, selected in
    torb_run_one by task->test != 0); torb_task_panicked records the failure, cancels the test's tasks, calls
    torb_region_abandon (windows regions) and torb_complete(self, task, false), so the awaiter reads
    Fail(Cancelled) and pool counts stay exact. What the frame held stays allocated (CONCURRENCY.md, "A test owns the
    tasks it starts" and "A recovered panic gives up what it held", #258).
  • So the native recovery mechanism is built for tests; F9 generalizes the selector (a task marked "supervised"
    instead of test != 0) and the policy for a panic outside a test (today !attributed ends the process).

Acceptance criteria (once decided; testable)

  • A task started as supervised (the spelling is the decision's: a Task.start option or a std/web server loop)
    that panics completes as ended; the process continues and a sibling task finishes normally. Conformance test
    tests/conformance/task-panic-recovered.trb: stdout of the sibling, exit code 0.
  • The awaiter sees the documented outcome (Cancelled or a new Panicked case, decided with the rule), with the
    panic message and site available to the handler/logging; a test per outcome.
  • A panic in a task that is not supervised still prints its trace and exits 101 (existing
    tests/conformance/task-panic-worker.trb unchanged).
  • What the synchronous callees held leaks and is counted: the leak report names the figure; a test fixes it
    (blocks leaked by one recovered panic).
  • Works on every worker (Workers.count() > 1) and in a region (windows) as the test scope does
    (runtime/tests/pool_test.c case).
  • Both back ends: native (C runtime) and the VM (compiler/src/vm/interpret.trb) run the same conformance files
    with the same output.
  • CONCURRENCY.md section 8 and PANICS.md rewritten; WEB.md F9/D10 marked done; skills/ regenerated.

Seed impact

Probably no seed change if the supervised marker is a runtime/std API on existing natives; yes (two-commit
rule, seed refresh stays the owner's) if it needs a new native or syntax. Settled when the rule is decided.

Size

Two Conventional Commits in one PR: (1) feat(runtime): recover a panic in a supervised task (runtime, native back
end, tests), (2) feat(vm): ... with the docs. Larger than "ready" only because of the owner decision, not the code.

## Goal A panic inside a task ends that task, not the process, so a server survives one bad handler. This reverses the rule "a panic inside a task ends the process" and changes CONCURRENCY.md section 8 ("Panics are unchanged") with it. ## Design record - [WEB.md F9](https://git.torb.dev/torbscript/language/src/branch/main/docs/design/WEB.md) (section 8) and section 3.8. - WEB.md decision **D10** (owner decisions, section 12/13): decided **(a)** for v1, "a panic in a handler ends the process and a supervisor restarts it". Option (b) is this issue; the record says it "deserves the numbers of a running server before it is made". - PANICS.md (section 2.7 table, "Not a recoverable panic"): sandbox and `torb test` are the only recovered panics. ## Status: not ready (waiting) 1. **Owner-reserved.** D10 is an owner decision that was settled the other way for v1; flipping it changes a language rule that programs and documentation rely on. The loop does not decide it. The owner question is filed when (2) is met, with the measured numbers. 2. **No numbers yet.** `std/web` does not exist on `main` (`std/` has `http`, `network`, `task`, no `web`); #94 (core) is open. There is no running server to measure what a panicking handler leaks. ## What exists already (analysis of `main`, runtime) - `runtime/panic.c`: `torb_begin_recovery` / `torb_end_recovery`, a per-worker recovery point (`worker->recovery`); a panic with a point set jumps to it, otherwise exits 101. - `runtime/task.c`: a task of a test is resumed under a recovery point (`torb_resume_recovered`, selected in `torb_run_one` by `task->test != 0`); `torb_task_panicked` records the failure, cancels the test's tasks, calls `torb_region_abandon` (`windows` regions) and `torb_complete(self, task, false)`, so the awaiter reads `Fail(Cancelled)` and pool counts stay exact. What the frame held stays allocated (CONCURRENCY.md, "A test owns the tasks it starts" and "A recovered panic gives up what it held", #258). - So the native recovery mechanism is built for tests; F9 generalizes the selector (a task marked "supervised" instead of `test != 0`) and the policy for a panic outside a test (today `!attributed` ends the process). ## Acceptance criteria (once decided; testable) - [ ] A task started as supervised (the spelling is the decision's: a `Task.start` option or a `std/web` server loop) that panics completes as ended; the process continues and a sibling task finishes normally. Conformance test `tests/conformance/task-panic-recovered.trb`: stdout of the sibling, exit code 0. - [ ] The awaiter sees the documented outcome (`Cancelled` or a new `Panicked` case, decided with the rule), with the panic message and site available to the handler/logging; a test per outcome. - [ ] A panic in a task that is not supervised still prints its trace and exits 101 (existing `tests/conformance/task-panic-worker.trb` unchanged). - [ ] What the synchronous callees held leaks and is counted: the leak report names the figure; a test fixes it (blocks leaked by one recovered panic). - [ ] Works on every worker (`Workers.count() > 1`) and in a region (`windows`) as the test scope does (`runtime/tests/pool_test.c` case). - [ ] Both back ends: native (C runtime) and the VM (`compiler/src/vm/interpret.trb`) run the same conformance files with the same output. - [ ] CONCURRENCY.md section 8 and PANICS.md rewritten; WEB.md F9/D10 marked done; `skills/` regenerated. ## Seed impact Probably **no seed change** if the supervised marker is a runtime/std API on existing natives; **yes** (two-commit rule, seed refresh stays the owner's) if it needs a new native or syntax. Settled when the rule is decided. ## Size Two Conventional Commits in one PR: (1) `feat(runtime): recover a panic in a supervised task` (runtime, native back end, tests), (2) `feat(vm): ...` with the docs. Larger than "ready" only because of the owner decision, not the code.
Author
Collaborator

[loop:torbscript] intake: in scope (author orion, label loop), source WEB.md F9, not a duplicate. A language rule that changes CONCURRENCY.md section 8: needs owner sign-off before implementation (owner decision to be filed at refine). next: waiting (refine queued, ready buffer full) until 2026-10-10T04:00:00Z

[loop:torbscript] intake: in scope (author orion, label loop), source WEB.md F9, not a duplicate. A language rule that changes CONCURRENCY.md section 8: needs owner sign-off before implementation (owner decision to be filed at refine). next: waiting (refine queued, ready buffer full) until 2026-10-10T04:00:00Z
Author
Collaborator

[loop:torbscript] claim refine by session:loop-torbscript-20261027 until 2026-10-27T03:00Z

[loop:torbscript] claim refine by session:loop-torbscript-20261027 until 2026-10-27T03:00Z
Author
Collaborator

[loop:torbscript] refine: body rewritten with goal, acceptance criteria, size and seed impact; not ready. D10 in WEB.md was decided (a) by the owner for v1 and reserves (b) = this issue until "the numbers of a running server" exist; std/web (#94) is not on main, so there is nothing to measure. The owner question (flip the panic rule) is filed once those numbers exist, not now. The native recovery mechanism already exists for tests (task.c torb_resume_recovered/torb_task_panicked), so the code part is about two commits. next: waiting (owner-reserved D10 decision needs the measurements of a running std/web server, #94). Worker usage: about 60k tokens (sonnet).

[loop:torbscript] refine: body rewritten with goal, acceptance criteria, size and seed impact; not `ready`. D10 in WEB.md was decided (a) by the owner for v1 and reserves (b) = this issue until "the numbers of a running server" exist; `std/web` (#94) is not on main, so there is nothing to measure. The owner question (flip the panic rule) is filed once those numbers exist, not now. The native recovery mechanism already exists for tests (task.c `torb_resume_recovered`/`torb_task_panicked`), so the code part is about two commits. next: waiting (owner-reserved D10 decision needs the measurements of a running std/web server, #94). Worker usage: about 60k tokens (sonnet).
Sign in to join this conversation.
No labels
loop
ready
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
torbscript/language#107
No description provided.