js/php: the capability table per target #65

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

Context

Which std package is real, simulated or a compile error at its import, per target. The table itself is decided: JAVASCRIPT-AND-PHP.md section 2b has the browser and PHP columns (PHP floor 5.6, section 2d, closed #62). This issue is the implementation of that table, not a new decision.

Today compiler/src/project/capability.trb has one browser column (the playground's browser-wasm64, which hosts the VM) and ignores the architecture, and the only enforcement is missingCapabilitiesOf in compiler/src/vm/run.trb (the VM, host target). torb build --target browser-js and torb check --every-target check no import at all.

Source

  • ROADMAP.md, milestone 9
  • Design record: docs/design/JAVASCRIPT-AND-PHP.md section 2b ("It is the input of the checker rule of #65"), docs/design/JAVASCRIPT-BACKEND.md section 8 row 8
  • Touches the seed: no (no syntax change, no new native, no driver flag, no std name looked up by string; plain TorbScript in compiler/src that the current seed already compiles). Do not touch seed/.

Scope (decided by the loop)

One PR, two Conventional Commits: feat(compiler): ... (table, enforcement, tests) and docs: ... (user-facing table, regenerated skills/).

  • Columns implemented now: browser-wasm64 (unchanged except std/dns, see below) and browser-js. The PHP column is documented in the user-facing table but not enforced yet: there is no PHP BuildTarget (design step: #702). Its enforcement is added with the PHP target, as a just-in-time follow-up issue then (not filed up front, per the contract).
  • std/dns is Real on both browser architectures (decided by the loop: the package is pure, no natives, the table in section 2b says "#65 should align that"; only its transport, std/network, is missing, and that stays a compile error).
  • browser-wasm64 otherwise stays as it is (section 2b): std/sandbox and std/machine stay Real there because the playground hosts the VM.

Changes

  1. compiler/src/project/capability.trb
    • capabilityOf(target, package): for .Browser, branch on target.architecture: .Wasm64 keeps browserCapabilityOf (remove "std/dns" from the Missing arm), .JavaScript calls a new browserJsCapabilityOf.
    • browserJsCapabilityOf(package) encodes the Browser column of section 2b, per package (standardPackageOf is the key, never a module below it):
      • Missing(why): std/process, std/sandbox, std/machine, std/network, std/tls, std/http (each why is the row's reason in one short sentence, e.g. "a program in the browser starts no processes", "a program compiled to JavaScript has neither the front end nor the VM", "a page has no sockets").
      • Simulated(how): std/fs and std/archive (the origin private file system; extract follows std/fs), std/os (target constants answer, the rest fails with OsError.Unsupported), std/io (sink to the console, standard input at its end), std/parallel (one chunk after another on the event loop).
      • Real: everything else, including std/console, std/time, std/task, std/random, std/digest/cipher/signature, std/resource, std/storage, std/dns and the pure packages of the last table row.
    • Keep the doc comment of the file in step (it still says "the browser" singular).
  2. The unsupported-import diagnostic: reuse missingCapabilitiesOf / MissingCapability (now in compiler/src/vm/run.trb). Make its note depend on target.architecture (the playground sentence stays for .Wasm64; for .JavaScript: a program compiled to JavaScript runs in a browser, and a pointer to the capability table page of docs/tooling/). The message stays `std/process` does not exist in the browser: <why>. If build.trb importing vm/run creates a cycle or drags the VM in, the worker may move the function and type unchanged to a neutral module (suggested: compiler/src/project/missing-capability.trb), re-export or update the imports in vm/run.trb and compiler/tests/capability.test.trb.
  3. Enforcement points:
    • compiler/src/cli/build.trb: for toJavaScript (around line 317, before lowering), call the check, print each diagnostic like reportMissingCapabilities does in vm/run.trb, fail with buildFailedCode and the existing problem-count line; nothing is built.
    • compiler/src/cli/targets.trb (problemsOfEveryTarget) and its caller in compiler/src/cli/check.trb: for every target whose capabilityOf is Missing for an import of the program's own files, report the import under that target (so torb check --every-target shows browser-js: / browser-wasm64: with the error). std/ packages are not asked (same rule as the VM). Check how LoweredProblem is rendered and add the diagnostic there rather than a second reporter if that is simpler; the output format of existing problems must not change.
    • torb run/VM path (vm/run.trb) is unchanged (host target, now std/dns is no longer refused in the playground).
  4. Docs (the public table):
    • New page docs/tooling/target-capabilities.md (frontmatter like docs/tooling/package-capabilities.md, order next to it, source: listing compiler/src/project/capability.trb), the table of section 2b as a user-facing page: one row per package, columns Browser (JavaScript) and PHP (5.6 floor), Real / Simulation / Compile error, one short reason, how a compile error looks, that the PHP column is documented and enforced once the PHP target exists. Link it from docs/tooling/torb-build.md ("### --target") and docs/tooling/torb-check.md (--every-target), and from the playground page for the wasm64 difference (std/sandbox, std/machine real there).
    • Update the status lines in docs/design/JAVASCRIPT-AND-PHP.md section 2b and JAVASCRIPT-BACKEND.md section 8 row 8 only where they say #65 is open; do not change the decided table.
    • Regenerate skills/: torb docs skill docs skills, then torb docs skill docs skills --check clean.

Acceptance criteria

  • capabilityOf(BuildTarget(.Browser, .JavaScript), p) equals the Browser column of section 2b for every package in the table (six Missing, the simulations above, the rest Real); every machine target stays Real for all.
  • capabilityOf(BuildTarget(.Browser, .Wasm64), "std/dns") is Real; its other rows, and std/sandbox/std/machine as Real, are unchanged.
  • torb build --target browser-js on a program that imports std/process (or std/sandbox, std/machine, std/network, std/tls, std/http) fails with an error at the import statement, `std/process` does not exist in the browser: ..., prints no .mjs, and exits with the build-failed code; an import of a Simulated package (std/fs) still builds.
  • torb check --every-target <program importing std/process> reports the error under browser-js: and browser-wasm64: and under no machine target; programs without such imports and check --every-target std/os (the gate) are unchanged.
  • Each missing package is reported once per import, in the file that imports it; std/ packages are not asked.
  • docs/tooling/target-capabilities.md exists with both columns, is linked from torb-build.md and torb-check.md, and agrees with the code (the test below checks the Browser column).
  • The playground (browser-wasm64, torb run) behaves as before except that std/dns imports are no longer refused.

Test plan

  • compiler/tests/capability.test.trb (existing; extend):
    • a browserJs constant next to browser; table-driven test over the section-2b Browser column: a list of (package, kind) pairs (missing/simulated/real) asserted against kindOf(capabilityOf(browserJs, package));
    • std/dns is real on both browser targets (adjust the existing lists that put std/dns under missing);
    • refusedIn(importsProcess, browserJs) has the one expected message; refusedIn(source with std/fs, browserJs) is empty; "names every missing package once" for browserJs;
    • the note differs per architecture (playground sentence for .Wasm64).
  • A table-against-docs test in the style of tools/registry/tests/capability-table.test.trb: parse the Browser column of docs/tooling/target-capabilities.md and compare it with capabilityOf(browserJs, ...), so the page and the code cannot drift.
  • Command line: one case for torb build --target browser-js on a program that imports std/process (stderr and exit pinned) in tools/command-line.sh / tests/command-line/arguments.expected style (--update regenerates), and one torb check --every-target case.
  • CI only: sh tools/gates.sh a (and b if CLAUDE.md asks) in the forge CI; locally only torb format --check on touched files and the one capability test. tools/check-commits.sh clean.

Dependencies

  • #62 is closed (PHP floor 5.6, PR #686). No open dependency.

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

## Context Which `std` package is real, simulated or a compile error at its import, per target. The table itself is decided: [JAVASCRIPT-AND-PHP.md section 2b](https://git.torb.dev/torbscript/language/src/branch/main/docs/design/JAVASCRIPT-AND-PHP.md) has the browser and PHP columns (PHP floor 5.6, section 2d, closed #62). This issue is the implementation of that table, not a new decision. Today `compiler/src/project/capability.trb` has one browser column (the playground's `browser-wasm64`, which hosts the VM) and ignores the architecture, and the only enforcement is `missingCapabilitiesOf` in `compiler/src/vm/run.trb` (the VM, host target). `torb build --target browser-js` and `torb check --every-target` check no import at all. ## Source - [ROADMAP.md, milestone 9](https://git.torb.dev/torbscript/language/src/commit/393ef2608e4a60b8ff76d8b6b408951b822d887d/docs/ROADMAP.md#L129) - Design record: `docs/design/JAVASCRIPT-AND-PHP.md` section 2b ("It is the input of the checker rule of #65"), `docs/design/JAVASCRIPT-BACKEND.md` section 8 row 8 - Touches the seed: **no** (no syntax change, no new native, no driver flag, no `std` name looked up by string; plain TorbScript in `compiler/src` that the current seed already compiles). Do not touch `seed/`. ## Scope (decided by the loop) One PR, two Conventional Commits: `feat(compiler): ...` (table, enforcement, tests) and `docs: ...` (user-facing table, regenerated `skills/`). - Columns implemented now: `browser-wasm64` (unchanged except `std/dns`, see below) and `browser-js`. The **PHP column is documented in the user-facing table but not enforced yet**: there is no PHP `BuildTarget` (design step: #702). Its enforcement is added with the PHP target, as a just-in-time follow-up issue then (not filed up front, per the contract). - `std/dns` is `Real` on both browser architectures (decided by the loop: the package is pure, no natives, the table in section 2b says "#65 should align that"; only its transport, `std/network`, is missing, and that stays a compile error). - `browser-wasm64` otherwise stays as it is (section 2b): `std/sandbox` and `std/machine` stay `Real` there because the playground hosts the VM. ## Changes 1. `compiler/src/project/capability.trb` - `capabilityOf(target, package)`: for `.Browser`, branch on `target.architecture`: `.Wasm64` keeps `browserCapabilityOf` (remove `"std/dns"` from the `Missing` arm), `.JavaScript` calls a new `browserJsCapabilityOf`. - `browserJsCapabilityOf(package)` encodes the **Browser** column of section 2b, per package (`standardPackageOf` is the key, never a module below it): - `Missing(why)`: `std/process`, `std/sandbox`, `std/machine`, `std/network`, `std/tls`, `std/http` (each `why` is the row's reason in one short sentence, e.g. "a program in the browser starts no processes", "a program compiled to JavaScript has neither the front end nor the VM", "a page has no sockets"). - `Simulated(how)`: `std/fs` and `std/archive` (the origin private file system; `extract` follows `std/fs`), `std/os` (target constants answer, the rest fails with `OsError.Unsupported`), `std/io` (sink to the console, standard input at its end), `std/parallel` (one chunk after another on the event loop). - `Real`: everything else, including `std/console`, `std/time`, `std/task`, `std/random`, `std/digest`/`cipher`/`signature`, `std/resource`, `std/storage`, `std/dns` and the pure packages of the last table row. - Keep the doc comment of the file in step (it still says "the browser" singular). 2. The unsupported-import diagnostic: reuse `missingCapabilitiesOf` / `MissingCapability` (now in `compiler/src/vm/run.trb`). Make its note depend on `target.architecture` (the playground sentence stays for `.Wasm64`; for `.JavaScript`: a program compiled to JavaScript runs in a browser, and a pointer to the capability table page of `docs/tooling/`). The message stays `` `std/process` does not exist in the browser: <why> ``. If `build.trb` importing `vm/run` creates a cycle or drags the VM in, the worker may move the function and type unchanged to a neutral module (suggested: `compiler/src/project/missing-capability.trb`), re-export or update the imports in `vm/run.trb` and `compiler/tests/capability.test.trb`. 3. Enforcement points: - `compiler/src/cli/build.trb`: for `toJavaScript` (around line 317, before lowering), call the check, print each diagnostic like `reportMissingCapabilities` does in `vm/run.trb`, fail with `buildFailedCode` and the existing problem-count line; nothing is built. - `compiler/src/cli/targets.trb` (`problemsOfEveryTarget`) and its caller in `compiler/src/cli/check.trb`: for every target whose `capabilityOf` is `Missing` for an import of the program's own files, report the import under that target (so `torb check --every-target` shows `browser-js:` / `browser-wasm64:` with the error). `std/` packages are not asked (same rule as the VM). Check how `LoweredProblem` is rendered and add the diagnostic there rather than a second reporter if that is simpler; the output format of existing problems must not change. - `torb run`/VM path (`vm/run.trb`) is unchanged (host target, now `std/dns` is no longer refused in the playground). 4. Docs (the public table): - New page `docs/tooling/target-capabilities.md` (frontmatter like `docs/tooling/package-capabilities.md`, `order` next to it, `source:` listing `compiler/src/project/capability.trb`), the table of section 2b as a user-facing page: one row per package, columns Browser (JavaScript) and PHP (5.6 floor), Real / Simulation / Compile error, one short reason, how a compile error looks, that the PHP column is documented and enforced once the PHP target exists. Link it from `docs/tooling/torb-build.md` ("### --target") and `docs/tooling/torb-check.md` (`--every-target`), and from the playground page for the wasm64 difference (`std/sandbox`, `std/machine` real there). - Update the status lines in `docs/design/JAVASCRIPT-AND-PHP.md` section 2b and `JAVASCRIPT-BACKEND.md` section 8 row 8 only where they say #65 is open; do not change the decided table. - Regenerate `skills/`: `torb docs skill docs skills`, then `torb docs skill docs skills --check` clean. ## Acceptance criteria - [ ] `capabilityOf(BuildTarget(.Browser, .JavaScript), p)` equals the Browser column of section 2b for every package in the table (six `Missing`, the simulations above, the rest `Real`); every machine target stays `Real` for all. - [ ] `capabilityOf(BuildTarget(.Browser, .Wasm64), "std/dns")` is `Real`; its other rows, and `std/sandbox`/`std/machine` as `Real`, are unchanged. - [ ] `torb build --target browser-js` on a program that imports `std/process` (or `std/sandbox`, `std/machine`, `std/network`, `std/tls`, `std/http`) fails with an error at the import statement, `` `std/process` does not exist in the browser: ... ``, prints no `.mjs`, and exits with the build-failed code; an import of a `Simulated` package (`std/fs`) still builds. - [ ] `torb check --every-target <program importing std/process>` reports the error under `browser-js:` and `browser-wasm64:` and under no machine target; programs without such imports and `check --every-target std/os` (the gate) are unchanged. - [ ] Each missing package is reported once per import, in the file that imports it; `std/` packages are not asked. - [ ] `docs/tooling/target-capabilities.md` exists with both columns, is linked from `torb-build.md` and `torb-check.md`, and agrees with the code (the test below checks the Browser column). - [ ] The playground (`browser-wasm64`, `torb run`) behaves as before except that `std/dns` imports are no longer refused. ## Test plan - `compiler/tests/capability.test.trb` (existing; extend): - a `browserJs` constant next to `browser`; table-driven test over the section-2b Browser column: a list of `(package, kind)` pairs (`missing`/`simulated`/`real`) asserted against `kindOf(capabilityOf(browserJs, package))`; - `std/dns` is `real` on both browser targets (adjust the existing lists that put `std/dns` under `missing`); - `refusedIn(importsProcess, browserJs)` has the one expected message; `refusedIn(source with std/fs, browserJs)` is empty; "names every missing package once" for `browserJs`; - the note differs per architecture (playground sentence for `.Wasm64`). - A table-against-docs test in the style of `tools/registry/tests/capability-table.test.trb`: parse the Browser column of `docs/tooling/target-capabilities.md` and compare it with `capabilityOf(browserJs, ...)`, so the page and the code cannot drift. - Command line: one case for `torb build --target browser-js` on a program that imports `std/process` (stderr and `exit` pinned) in `tools/command-line.sh` / `tests/command-line/arguments.expected` style (`--update` regenerates), and one `torb check --every-target` case. - CI only: `sh tools/gates.sh a` (and `b` if `CLAUDE.md` asks) in the forge CI; locally only `torb format --check` on touched files and the one capability test. `tools/check-commits.sh` clean. ## Dependencies - #62 is closed (PHP floor 5.6, PR #686). No open dependency. _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 ROADMAP.md milestone 9, not a duplicate. A docs table plus a checker rule per target; depends on #62. next: waiting (dependency #62) until 2026-10-10T04:00:00Z

[loop:torbscript] intake: in scope (author orion, label loop), source ROADMAP.md milestone 9, not a duplicate. A docs table plus a checker rule per target; depends on #62. next: waiting (dependency #62) until 2026-10-10T04:00:00Z
Author
Collaborator

[loop:torbscript] refine: re-checked 2026-10-29T20:10Z: still depends on #62 (PHP version floor; owner decision asked in the TorbScript group 2026-10-29). The capability table per target needs the floor.
next: waiting (dependency #62 owner decision)

[loop:torbscript] refine: re-checked 2026-10-29T20:10Z: still depends on #62 (PHP version floor; owner decision asked in the TorbScript group 2026-10-29). The capability table per target needs the floor. next: waiting (dependency #62 owner decision)
Author
Collaborator

[loop:torbscript] claim refine by session:loop-torbscript-20261030 @anthropic:tkoehn until 2026-10-30T23:24Z

[loop:torbscript] claim refine by session:loop-torbscript-20261030 @anthropic:tkoehn until 2026-10-30T23:24Z
Author
Collaborator

[loop:torbscript] decided by the loop: (1) one PR, two commits, no split: the table, the build/check enforcement and the tests are one small change in capability.trb, build.trb, targets.trb plus docs. (2) The PHP column is documented now and enforced with the PHP target (none exists yet, #702); its follow-up issue is filed just in time, not up front. (3) std/dns is Real on both browser architectures: the package is pure and section 2b asks #65 to align it; the transport (std/network) stays a compile error. (4) browser-wasm64 keeps its other rows (the playground hosts the VM); browser-js gets its own function keyed on the architecture. (5) The diagnostic reuses missingCapabilitiesOf with an architecture-specific note. No owner decision is needed: all of it follows the decided design record.

[loop:torbscript] decided by the loop: (1) one PR, two commits, no split: the table, the build/check enforcement and the tests are one small change in `capability.trb`, `build.trb`, `targets.trb` plus docs. (2) The PHP column is documented now and enforced with the PHP target (none exists yet, #702); its follow-up issue is filed just in time, not up front. (3) `std/dns` is Real on both browser architectures: the package is pure and section 2b asks #65 to align it; the transport (`std/network`) stays a compile error. (4) `browser-wasm64` keeps its other rows (the playground hosts the VM); `browser-js` gets its own function keyed on the architecture. (5) The diagnostic reuses `missingCapabilitiesOf` with an architecture-specific note. No owner decision is needed: all of it follows the decided design record.
Author
Collaborator

[loop:torbscript] refine: issue body rewritten (goal, files, acceptance criteria, test plan, no seed touch), one PR, label ready set; tokens used ~120000
next: implementation

[loop:torbscript] refine: issue body rewritten (goal, files, acceptance criteria, test plan, no seed touch), one PR, label `ready` set; tokens used ~120000 next: implementation
Author
Collaborator

[loop:torbscript] claim implementation by session:loop-torbscript-20261030 @anthropic:tkoehn until 2026-10-31T03:42Z

[loop:torbscript] claim implementation by session:loop-torbscript-20261030 @anthropic:tkoehn until 2026-10-31T03:42Z
Author
Collaborator

[loop:torbscript] release implementation by session:loop-torbscript-20261030 @anthropic:tkoehn: the worker run was cancelled after opening PR #704 (head efd7f065, mergeable, argus requested); CI on that head is pending. Release of the claim.
next: waiting (CI and review of PR #704)

[loop:torbscript] release implementation by session:loop-torbscript-20261030 @anthropic:tkoehn: the worker run was cancelled after opening PR #704 (head efd7f065, mergeable, argus requested); CI on that head is pending. Release of the claim. next: waiting (CI and review of PR #704)
argus closed this issue 2026-10-31 00:58:35 +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#65
No description provided.