sandbox: a script fetched through a Storage (URI gap 13) #69

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

Context

The std/fs half of gap 13 is built; the Sandbox half is not: a script fetched through a Storage has no member to hand it to.

Source

Acceptance criteria

  • Sandbox loads a script from a Storage
  • Tests

Dependencies

None known.

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

## Context The `std/fs` half of gap 13 is built; the `Sandbox` half is not: a script fetched through a `Storage` has no member to hand it to. ## Source - [URI.md gap 13](https://git.torb.dev/torbscript/language/src/commit/393ef2608e4a60b8ff76d8b6b408951b822d887d/docs/design/URI.md#L1749) ## Acceptance criteria - [ ] `Sandbox` loads a script from a `Storage` - [ ] Tests ## Dependencies None known. _Moved from the planning lists of the docs; the design record stays the specification._
Author
Collaborator

[loop:torbscript] intake: in scope (author orion, label loop), source URI.md gap 13, not a duplicate, no collision with a design record. A Sandbox member in the std, likely no seed impact; sonnet/medium. next: refine, waiting (ready buffer full: #4, #163, #164, #223 are ready) until 2026-10-10T04:00:00Z

[loop:torbscript] intake: in scope (author orion, label loop), source URI.md gap 13, not a duplicate, no collision with a design record. A `Sandbox` member in the std, likely no seed impact; sonnet/medium. next: refine, 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-20261023 until 2026-10-23T10:16:00Z

[loop:torbscript] claim refine by session:loop-torbscript-20261023 until 2026-10-23T10:16:00Z
Author
Collaborator

[loop:torbscript] release refine: the worker failed to start (all CLI live sessions busy); no work done, back to the backlog. next: refine

[loop:torbscript] release refine: the worker failed to start (all CLI live sessions busy); no work done, back to the backlog. next: refine
Author
Collaborator

[loop:torbscript] claim refine by session:loop-torbscript-20261023 until 2026-10-23T10:32:00Z

[loop:torbscript] claim refine by session:loop-torbscript-20261023 until 2026-10-23T10:32:00Z
Author
Collaborator

[loop:torbscript] decided by the loop: Sandbox.loadText<Value>(text: String, name: String, capabilities); std/sandbox does not learn Storage.

Why:

  • URI.md section "Where Resource, Sandbox and the manifest's sources sit" keeps Sandbox off the Storage registry on purpose (it stays on Path/Resource/EmbeddedText), and gap 13 only asks for a member a script fetched through a Storage can be handed to. The smallest such member takes the text: the caller does storage.readText(uri).await()? and passes the result. No std/sandbox -> std/storage dependency, and the grant story (RESOURCES.md section 5: std/fs/std/storage stay the imports that mean "this program reads what the user named") is untouched.
  • The slice-7 machinery already does the hard part for a file read at run time (loadScript / load.script, host.trb: tree.withFile absolute, text, then check, lower, continuation; "a file loaded again with the same text is the script loaded before"). The new member is the same path with the text handed in instead of read from disk; there is no write-to-file detour (the gap's complaint).
  • Naming follows Sandbox.load / Storage.readText. RESOURCES.md section 5's planned rename (load -> read, embedded, load(Resource)) is not built; if it lands, this member follows the verb for "a run-time source" (readText) in that PR. name is the script's display name in SandboxError messages and the module identity (not a path, not resolved against anything); it is not part of the cache key, the text is.
  • Seed: expected none. Precedent is 7aef9a54 (loadScript native + load.script instruction + std change in one feat commit, no seed refresh), and the compiler's own code does not call the new member. The implementer confirms in CI bootstrap; if the published seed cannot compile the new native fn declaration, fall back to the two-commit rule (teach, then migrate) and the PR waits on Torben's seed refresh.
  • Native binary (slice 8): in scope. hostLoadScript reads a file in the host; a text variant passes the text instead (the host already works on tree.withFile path, text). If that turns out to exceed one PR, the implementer lands the VM half, makes the native-binary call answer a documented SandboxError (as slice 7 did before slice 8) and says so in the PR; the rest is then a follow-up issue (~1 h+) filed at that point, not before.
[loop:torbscript] decided by the loop: **`Sandbox.loadText<Value>(text: String, name: String, capabilities)`; `std/sandbox` does not learn `Storage`.** Why: - URI.md section "Where `Resource`, `Sandbox` and the manifest's sources sit" keeps `Sandbox` off the `Storage` registry on purpose (it stays on `Path`/`Resource`/`EmbeddedText`), and gap 13 only asks for a member a script fetched through a `Storage` can be handed to. The smallest such member takes the text: the caller does `storage.readText(uri).await()?` and passes the result. No `std/sandbox` -> `std/storage` dependency, and the grant story (RESOURCES.md section 5: `std/fs`/`std/storage` stay the imports that mean "this program reads what the user named") is untouched. - The slice-7 machinery already does the hard part for a file read at run time (`loadScript` / `load.script`, `host.trb`: `tree.withFile absolute, text`, then check, lower, continuation; "a file loaded again with the same text is the script loaded before"). The new member is the same path with the text handed in instead of read from disk; there is no write-to-file detour (the gap's complaint). - Naming follows `Sandbox.load` / `Storage.readText`. RESOURCES.md section 5's planned rename (`load` -> `read`, `embedded`, `load(Resource)`) is not built; if it lands, this member follows the verb for "a run-time source" (`readText`) in that PR. `name` is the script's display name in `SandboxError` messages and the module identity (not a path, not resolved against anything); it is not part of the cache key, the text is. - Seed: expected none. Precedent is 7aef9a54 (`loadScript` native + `load.script` instruction + std change in one `feat` commit, no seed refresh), and the compiler's own code does not call the new member. The implementer confirms in CI bootstrap; if the published seed cannot compile the new `native fn` declaration, fall back to the two-commit rule (teach, then migrate) and the PR waits on Torben's seed refresh. - Native binary (slice 8): in scope. `hostLoadScript` reads a file in the host; a text variant passes the text instead (the host already works on `tree.withFile path, text`). If that turns out to exceed one PR, the implementer lands the VM half, makes the native-binary call answer a documented `SandboxError` (as slice 7 did before slice 8) and says so in the PR; the rest is then a follow-up issue (~1 h+) filed at that point, not before.
Author
Collaborator

[loop:torbscript] refined spec (supersedes the two bullets of the issue body; the body is left as filed).

Goal. A program that fetched a script through a Storage hands it to the sandbox without writing it to a file first (URI.md gap 13, Sandbox half; the std/fs half is done).

Design point. docs/design/URI.md section 13 gap 13 and the section "Where Resource, Sandbox and the manifest's sources sit"; docs/design/RESOURCES.md section 5 (Sandbox verbs) and docs/design/SCRIPTS.md slices 7 and 8. Decision: previous comment.

Acceptance criteria.

  • Sandbox.loadText<Value>(text, name, capabilities): Result<Script<Value>, SandboxError> in std/sandbox, same checks, grants, limits and error shape as Sandbox.load; SandboxError.line counts lines of text; the message names name where load names the path.
  • Works in the VM (torb run), and in a native binary through the script host (or, per the fallback in the decision, answers a documented SandboxError there).
  • std/sandbox takes no dependency on std/storage; the doc example fetches with MemoryStorage.readText(...).await()? and loads the result.
  • Tests, a conformance program tests/conformance/vm-only/sandbox-storage.trb (+ .expected) that stores scripts in a MemoryStorage, reads them back and loads them: a configuration applied to a receiver; the same text twice (one script); a type error with its line; an ungranted and a granted import; a panic with its line; a text that is not UTF-8 stays a StorageFailure.NotText of the caller (never reaches the sandbox). If the native binary is covered, a case in tests/conformance/sandbox-crossing.trb for both back ends.
  • Docs in the same change: URI.md gap 13 marked answered and the Sandbox section of RESOURCES.md gains the member (URI.md section 14 "The prose" asks for exactly this), docs/standard-library/sandbox.md and docs/language/configuration/the-sandbox.md; skills regenerated (torb docs skill docs skills, --check clean).

Seed. Not touching it by design (no syntax change, no driver flag, no std name the compiler looks up by string); a new native (loadScriptText next to loadScript in compiler/src/backend/c/natives.trb) is covered by the seed note in the decision comment. Never touch seed/.

Size. One PR, one feat(sandbox): ... commit (two only if the seed fallback applies: feat(vm): teach ... then feat(sandbox): ...). Touches std/sandbox/src/lib.trb, compiler/src/vm/{interpret,run,host}.trb, compiler/src/backend/{c/natives,bytecode/emit,bytecode/format}.trb, compiler/src/ir/lower/call.trb, docs, tests. Implementer: sonnet/high (compiler/src, std/). Gates a and b (it touches compiler/src/ir/lower, bytecode backend); forge CI only.

[loop:torbscript] refined spec (supersedes the two bullets of the issue body; the body is left as filed). **Goal.** A program that fetched a script through a `Storage` hands it to the sandbox without writing it to a file first (URI.md gap 13, `Sandbox` half; the `std/fs` half is done). **Design point.** `docs/design/URI.md` section 13 gap 13 and the section "Where `Resource`, `Sandbox` and the manifest's sources sit"; `docs/design/RESOURCES.md` section 5 (`Sandbox` verbs) and `docs/design/SCRIPTS.md` slices 7 and 8. Decision: previous comment. **Acceptance criteria.** - [ ] `Sandbox.loadText<Value>(text, name, capabilities): Result<Script<Value>, SandboxError>` in `std/sandbox`, same checks, grants, limits and error shape as `Sandbox.load`; `SandboxError.line` counts lines of `text`; the message names `name` where `load` names the path. - [ ] Works in the VM (`torb run`), and in a native binary through the script host (or, per the fallback in the decision, answers a documented `SandboxError` there). - [ ] `std/sandbox` takes no dependency on `std/storage`; the doc example fetches with `MemoryStorage.readText(...).await()?` and loads the result. - [ ] Tests, a conformance program `tests/conformance/vm-only/sandbox-storage.trb` (+ `.expected`) that stores scripts in a `MemoryStorage`, reads them back and loads them: a configuration applied to a receiver; the same text twice (one script); a type error with its line; an ungranted and a granted import; a panic with its line; a text that is not UTF-8 stays a `StorageFailure.NotText` of the caller (never reaches the sandbox). If the native binary is covered, a case in `tests/conformance/sandbox-crossing.trb` for both back ends. - [ ] Docs in the same change: URI.md gap 13 marked answered and the `Sandbox` section of RESOURCES.md gains the member (URI.md section 14 "The prose" asks for exactly this), `docs/standard-library/sandbox.md` and `docs/language/configuration/the-sandbox.md`; skills regenerated (`torb docs skill docs skills`, `--check` clean). **Seed.** Not touching it by design (no syntax change, no driver flag, no std name the compiler looks up by string); a new native (`loadScriptText` next to `loadScript` in `compiler/src/backend/c/natives.trb`) is covered by the seed note in the decision comment. Never touch `seed/`. **Size.** One PR, one `feat(sandbox): ...` commit (two only if the seed fallback applies: `feat(vm): teach ...` then `feat(sandbox): ...`). Touches `std/sandbox/src/lib.trb`, `compiler/src/vm/{interpret,run,host}.trb`, `compiler/src/backend/{c/natives,bytecode/emit,bytecode/format}.trb`, `compiler/src/ir/lower/call.trb`, docs, tests. Implementer: sonnet/high (`compiler/src`, `std/`). Gates a and b (it touches `compiler/src/ir/lower`, bytecode backend); forge CI only.
Author
Collaborator

[loop:torbscript] refine: checked against URI.md gap 13, RESOURCES.md section 5, SCRIPTS.md slices 7/8, CONCEPT and CLAUDE.md; sharpened goal and acceptance criteria, decided the API (Sandbox.loadText, no std/storage dependency), seed impact (none expected, fallback stated) and size (one PR, one commit), label ready set. Worker usage ~75k tokens. next: implement

[loop:torbscript] refine: checked against URI.md gap 13, RESOURCES.md section 5, SCRIPTS.md slices 7/8, CONCEPT and CLAUDE.md; sharpened goal and acceptance criteria, decided the API (`Sandbox.loadText`, no `std/storage` dependency), seed impact (none expected, fallback stated) and size (one PR, one commit), label `ready` set. Worker usage ~75k tokens. next: implement
Author
Collaborator

[loop:torbscript] claim implement by session:loop-torbscript-20261023 until 2026-10-23T14:44:00Z

[loop:torbscript] claim implement by session:loop-torbscript-20261023 until 2026-10-23T14:44:00Z
argus closed this issue 2026-10-23 10:19:13 +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#69
No description provided.