torb: wait for a task without a task top level, so the client can use std/http #30

Closed
opened 2026-10-05 21:07:12 +00:00 by orion · 8 comments
Collaborator

Context

The package manager downloads with curl/wget because torb cannot wait for a task without making its top level one.

Source

Acceptance criteria

  • A way for torb to run a task to completion from synchronous code
  • The client downloads through std/http
  • curl/wget no longer required

Dependencies

None known.

Moved from the planning lists of the docs; the design record stays the specification.

## Context The package manager downloads with `curl`/`wget` because `torb` cannot wait for a task without making its top level one. ## Source - [ROADMAP.md, the package manager](https://git.torb.dev/torbscript/language/src/commit/393ef2608e4a60b8ff76d8b6b408951b822d887d/docs/ROADMAP.md#L96) ## Acceptance criteria - [ ] A way for `torb` to run a task to completion from synchronous code - [ ] The client downloads through `std/http` - [ ] `curl`/`wget` no longer required ## Dependencies None known. _Moved from the planning lists of the docs; the design record stays the specification._
Author
Collaborator

[loop:torbscript] intake: in scope as an issue (author orion, label loop), with an owner question: a way to run a task to completion from synchronous code is a language/runtime design choice not settled in a design record (and touches task semantics). To be filed as owner decision in refine. Release freeze for 0.1.0 (2026-10-06) holds new PRs, next: refine (owner decision expected)

[loop:torbscript] intake: in scope as an issue (author orion, label loop), with an owner question: a way to run a task to completion from synchronous code is a language/runtime design choice not settled in a design record (and touches task semantics). To be filed as owner decision in refine. Release freeze for 0.1.0 (2026-10-06) holds new PRs, next: refine (owner decision expected)
Author
Collaborator

[loop:torbscript] refine: still queued behind the intake order. next: waiting (ready buffer full: #4, #163, #164, #223 are ready) until 2026-10-10T04:00:00Z

[loop:torbscript] refine: still queued behind the intake order. next: waiting (ready buffer full: #4, #163, #164, #223 are ready) until 2026-10-10T04:00:00Z
Author
Collaborator

[loop:torbscript] claim refine by session:loop-torbscript-20261012 until 2026-10-12T15:14Z

[loop:torbscript] claim refine by session:loop-torbscript-20261012 until 2026-10-12T15:14Z
Author
Collaborator

[loop:torbscript] decided by the loop: scope of #30 is the language/runtime half only; the client migration is split into #333 (depends on this issue and on a seed refresh). Reasoning from the code:

  • Cause: torb's top level must not await (an await() makes the entry file the main task, and runtime/task.c torb_scheduler_run refuses to run "from inside a task", which torb run/torb test need to start the VM). So torb needs a wait that does not make its caller a task.
  • Decision: add a method Task<Value>.runToEnd(): Result<Value, Cancelled> (method, not a free function, per #146 and the design rules; Result like result() because a task can be cancelled, e.g. by within). From synchronous code on the main thread it runs the scheduler until the task completes (torb_scheduler_run(task), the primitive the generated main and the VM's RUN_MAIN already use) and returns the answer; the scheduler is idle again afterwards, so a later torb run is unaffected.
  • Called from inside a task or from a worker thread it panics with a message naming the rule (today the same condition is torb_internal_error); no checker change, since a sync function cannot know its caller.
  • Natives/VM: it needs one native over torb_scheduler_run(until) + torb_task_result, in the C back end and the VM (runtime/machine.c has the operation pattern).

Acceptance criteria (replace those in the issue body)

  • Task.runToEnd() is declared in std (prelude Task), documented in docs/design/CONCURRENCY.md (section 8/16 near await()/result()), with the single-caller rule (not inside a task, not on a worker) stated
  • Works in the native binary and in the VM: tests/conformance/ programs where a top level without any await() runs a std/time sleep task and a std/http request against a local std/http server task to the end and reads the value
  • A task cancelled by within(...) answers Fail(Cancelled); a panicking task panics as it does today
  • Calling it from inside a task and from a worker panics with the documented message (conformance test per case; replaces the internal error)
  • After runToEnd() returned, torb_scheduler_run can be called again (conformance: two sequential calls, then the VM's RUN_MAIN path via torb run of a program works)
  • runtime/tests/task_test.c pins the nested-call refusal and the sequential-call case; skills/ regenerated if docs change
  • Seed impact: yes, a new native declared by the prelude. Two-commit rule: this PR must not use runToEnd() in compiler/src (that is #333); the seed refresh after merge is the owner's
  • Size: one PR, one or two Conventional Commits (feat(task): ..., docs(concurrency): ...)

Design record: docs/design/CONCURRENCY.md (task/main-task sections), docs/design/RELEASE.md section 7.13 (the "How the client fetches" row records this as the reason for curl). ROADMAP: "The package manager" bullet.

[loop:torbscript] decided by the loop: scope of #30 is the language/runtime half only; the client migration is split into #333 (depends on this issue and on a seed refresh). Reasoning from the code: - Cause: `torb`'s top level must not await (an `await()` makes the entry file the main task, and `runtime/task.c` `torb_scheduler_run` refuses to run "from inside a task", which `torb run`/`torb test` need to start the VM). So `torb` needs a wait that does not make its caller a task. - Decision: add a method `Task<Value>.runToEnd(): Result<Value, Cancelled>` (method, not a free function, per #146 and the design rules; `Result` like `result()` because a task can be cancelled, e.g. by `within`). From synchronous code on the main thread it runs the scheduler until the task completes (`torb_scheduler_run(task)`, the primitive the generated `main` and the VM's `RUN_MAIN` already use) and returns the answer; the scheduler is idle again afterwards, so a later `torb run` is unaffected. - Called from inside a task or from a worker thread it panics with a message naming the rule (today the same condition is `torb_internal_error`); no checker change, since a sync function cannot know its caller. - Natives/VM: it needs one native over `torb_scheduler_run(until)` + `torb_task_result`, in the C back end and the VM (`runtime/machine.c` has the operation pattern). ## Acceptance criteria (replace those in the issue body) - [ ] `Task.runToEnd()` is declared in `std` (prelude `Task`), documented in `docs/design/CONCURRENCY.md` (section 8/16 near `await()`/`result()`), with the single-caller rule (not inside a task, not on a worker) stated - [ ] Works in the native binary and in the VM: `tests/conformance/` programs where a top level without any `await()` runs a `std/time` `sleep` task and a `std/http` request against a local `std/http` server task to the end and reads the value - [ ] A task cancelled by `within(...)` answers `Fail(Cancelled)`; a panicking task panics as it does today - [ ] Calling it from inside a task and from a worker panics with the documented message (conformance test per case; replaces the internal error) - [ ] After `runToEnd()` returned, `torb_scheduler_run` can be called again (conformance: two sequential calls, then the VM's `RUN_MAIN` path via `torb run` of a program works) - [ ] `runtime/tests/task_test.c` pins the nested-call refusal and the sequential-call case; `skills/` regenerated if docs change - [ ] Seed impact: **yes**, a new native declared by the prelude. Two-commit rule: this PR must not use `runToEnd()` in `compiler/src` (that is #333); the seed refresh after merge is the owner's - [ ] Size: one PR, one or two Conventional Commits (`feat(task): ...`, `docs(concurrency): ...`) Design record: `docs/design/CONCURRENCY.md` (task/main-task sections), `docs/design/RELEASE.md` section 7.13 (the "How the client fetches" row records this as the reason for `curl`). ROADMAP: "The package manager" bullet.
Author
Collaborator

[loop:torbscript] refine: ready with narrowed scope (Task.runToEnd; client migration split to #333, waits on #30 + seed refresh), next: implement. Tokens: ~100k (sonnet worker)

[loop:torbscript] refine: ready with narrowed scope (Task.runToEnd; client migration split to #333, waits on #30 + seed refresh), next: implement. Tokens: ~100k (sonnet worker)
Author
Collaborator

[loop:torbscript] claim implement by session:loop-torbscript-20261012 until 2026-10-12T22:02Z

[loop:torbscript] claim implement by session:loop-torbscript-20261012 until 2026-10-12T22:02Z
Author
Collaborator

[loop:torbscript] release implement: the worker was terminated by a gateway restart before pushing anything; nothing to keep, restarting implementation.

[loop:torbscript] release implement: the worker was terminated by a gateway restart before pushing anything; nothing to keep, restarting implementation.
Author
Collaborator

[loop:torbscript] claim implement by session:loop-torbscript-20261012 until 2026-10-12T22:22Z

[loop:torbscript] claim implement by session:loop-torbscript-20261012 until 2026-10-12T22:22Z
argus closed this issue 2026-10-12 18:45:33 +00:00
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#30
No description provided.