Script receivers 2/6: graph and checker take receivers from project.trb #513
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Slice 3 of
docs/design/SCRIPT-RECEIVERS.mdsection 12 (onmain). Part of the script-receivers epic #518 (step 2 of 6). Builds on slice 1 (#512, landed as43a2b45f: thescriptsline,Manifest.scripts, the pattern matchermatchesScriptPattern). Design PR: #519Graph and checker take receivers from the mapping where there is one, and from literal loads where there is not.
Goal
A file that a
scriptspattern of its project matches is a receiver script of its package: the graph adds it (also when nothing loads it), the checker checks it against the resolvedreceiver:/from:type, and the program's script table holds it. Unmapped files keep working exactly as today.Design record
SCRIPT-RECEIVERS.md section 4 (the receiver type), section 5 (what a mapped file is; the
ScriptReceivershape), section 6 (load sites, only the mapped/unmapped split), section 12 (slice 3). Owner decisions of 2026-10-26 kept as decided: the mapping is optional, unmapped scripts stay valid (typed by inference from literalSandbox.loadcalls, else untyped), and there is no strict mode.Scope
compiler/src/semantics/module.trb:ScriptReceiver.Family(package: Int, index: Int)(the package inWorkspace.packagesand the line in itsmanifest.scripts);LoadedandDeclaredstay.compiler/src/semantics/graph.trb,addReceiverScripts:project.trbas today; then for every package withscriptslines every file ofpackage.filesthatmatchesScriptPatternaccepts (path relative to the package directory) getsFamily, whether it is a plain script or an existing module (a file insrc/or a swept-in test directory, asLoadedalready handles). The file is parsed even if no import reaches it (reachedFilesOf). Then the literal-load walk (scriptLoadsOf) for files that no pattern maps, unchanged. A file that two lines map to different receivers (receiver:plusfrom:as written) gets noFamilyreceiver at all (checked against neither, D7 is reported by #514).*.test.trborsrc/lib.trbis ignored here (reported as D6 by #514);project.trb/project.lock.trbare never inpackage.files.compiler/src/semantics/checker/receiver.trb,scriptReceiverOf:Familyresolvesfrom:with the rules of auserelative to the directory of the project'sproject.trb(./,../inside the package, or a dependency's package name,resolveImportsemantics;project.trbitself does not import anything), then looks up top-levelreceiver:in that module: a nominaltypewithout type parameters, any top-level type if the module is of the project's own package,publiconly for another package. Anything else (no module, no type, a trait, generic, not public) givesNone: the file stays untyped, silent in this slice (D8-D13 are #514).scriptsOf(compiler/src/ir/lower/script.trb) and the embedded-sources loop incompiler/src/cli/host.trbtakeLoadedandFamily, soscriptBody<Value>/scriptImports<Value>hold every file mapped toValue, in the VM and in a native binary.addRuntimeScripts,vm/run.trb,vm/host.trb): a path that lands on a file a pattern maps keeps itsFamilyreceiver; the type the running program names is compared with it, and a differing type ends in the existing "There is no script ... in this program" error (D17 gets its text in #514). A path that no pattern maps is checked against the type argument as today (Declared).language-server/*only keeps compiling and passing; its features are #515),Sandbox.loadtype-argument checks against the mapping (D14), the "first load decides" rule for unmapped files (replaced by D18 in #514, see the decision below), docs beyond the design record's status line.Seed
Does not touch the seed: no syntax change, no new native, no driver flag, no new std name looked up by string (
scriptBody/scriptImportsalready are). The compiler sources stay on existing syntax and noproject.trbof this repository gets ascriptsline, so the bootstrap does not need a published seed that contains slice 1. Not a two-commit change in the seed sense.Acceptance
prot = 8080for aportfield) is reported bytorb checkat its position; the same file without the mistake checks clean.src/; ausethat names it is refused (the existing refusal for a receiver script).receiver:/from:resolve like ause:./-relative module, and a module of a dependency package with apublictype; a receiver that cannot be resolved leaves the file untyped without any diagnostic.scriptBody<Value>/scriptImports<Value>answer a mapped file that no literal names, and a program that picks the path at run time (non-literal) finds it, in the VM and in a native binary (conformance program with a mapped family).Sandbox.load<T>still checks againstT, and a project without anyscriptsline behaves as before (the existingreceivers.test.trb/sandbox.test.trbcases stay green unchanged).torb checkoption or project setting demands every script be mapped or typed (no strict mode).compiler/tests/receivers.test.trbandcompiler/tests/sandbox.test.trb; forge CI tier A green (tier B becausecompiler/src/iris touched).Size
One PR, two Conventional Commits:
feat(semantics): files a scripts line maps are receiver scripts(module, graph, receiver resolution, tests fortorb check) andfeat(ir): the script table holds the mapped family(table, host, runtime loader, tests).Moved to #514
"First load decides" stays for unmapped files in this slice and is replaced there by D18, because spotting two different types in two loads needs the checker's resolved types and the exact message belongs to the diagnostics catalogue. D14 (type argument differs from the mapping) and D18 move with it, with the D4-D13, D16 and D17 texts. The
examples/config-dslscriptsline belongs to the migration slice (#517).Amended 2026-10-26 (owner): the
scriptsmapping is optional; unmapped scripts stay valid and are typed by inference from literalSandbox.loadcalls, else untyped (SCRIPT-RECEIVERS.md sections 1, 5, 6).Decided 2026-10-26 (owner): no strict mode (see the acceptance list).
Design PR: #519 · Epic: #518
[loop:torbscript] intake: in scope (author orion, label
loop, slice 2 of the script-receivers epic #518, design record docs/design/SCRIPT-RECEIVERS.md); no duplicate, no collision. It builds on thescriptsline and static reader of slice 1 (#512, PR #557 in review). Just in time: refine and implement start when #512 has landed. next: waiting (#512 landed)[loop:torbscript] claim refine by session:loop-torbscript-20261029 until 2026-10-29T01:54:45Z
[loop:torbscript] decided by the loop: (1) Cut. Slice 3 as written in the design record (graph, resolution, table, runtime loader, D14, D18) is more than one PR; this issue keeps the part that makes mapped files work (graph,
torb check, resolution ofreceiver:/from:, script table, runtime loader) in two commits, and the diagnostics move to #514 where the record's slice 4 already puts D1-D18. (2) Bad mappings (unresolvablefrom:/receiver:, a match on an entry, test or library, two lines with different receivers) leave the file untyped and silent in this slice, because their exact texts D4-D13 belong to #514; nothing is guessed or ordered silently: a conflicting file is checked against neither receiver, as the record says. (3) "First load decides" stays for unmapped files until #514 replaces it with D18: detecting two different types needs the checker's resolved types, and removing the rule earlier would leave a silent gap. Mapped files never use it. (4) Table:scriptsOfselecting every module whose receiver resolves toValuegives "a program that loads aValueat all holds the whole family" without a new table kind. (5) Seed: not touched, no published seed with slice 1 needed (noproject.trbof this repository gets ascriptsline; the compiler readsManifest.scriptsfrom its own sources). Checked on main43a2b45f: slice 1 is in (Manifest.scripts,ScriptFamily,matchesScriptPattern,scriptProblemsOf).[loop:torbscript] refine: verified slice 1 on main
43a2b45f(scripts line,Manifest.scripts, matcher in place), read the graph, checker, script-table and runtime-loader code, and rewrote the body to the definition of ready: goal, nine testable acceptance criteria, design record sections, no-seed statement (no published seed with slice 1 needed), size one PR in two Conventional Commits. The diagnostic parts (D14, D18, removal of first-load for unmapped files, D4-D13, D16/D17) moved to #514 (noted there); decisions are in the comment above. Labelreadyset. Main CI on43a2b45fwas still running at 23:56Z (started 23:48Z, not red; the previous main commit is green), so implement should read it with ci-wait before pushing. Claim released. Tokens: about 90k. next: implement[loop:torbscript] claim implement by session:loop-torbscript-20261029 until 2026-10-29T06:08:38Z
[loop:torbscript] release implement: the worker was lost with its execution context after 13 min (gateway event, not a usage limit); check the branch loop/513-* for partial work. Respawning on the same profile, next: implement
[loop:torbscript] claim implement by session:loop-torbscript-20261029 until 2026-10-29T06:24:25Z
[loop:torbscript] release implement: the worker was lost with its execution context (gateway event, not a usage limit) after opening PR #562 (
ce8b1e0e); its forge CI is running. next: waiting (forge CI on PR #562); a red lane caused by this change gets a fix workerSandbox.readfor a script path known at run time (step 1 of #138) #576[loop:torbscript] claim fix by session:loop-torbscript-20261029 until 2026-10-29T14:55Z
[loop:torbscript] review: forge CI was red on
commit messagesbecause the branch had astyle(...)commit (styleis not an allowed Conventional Commits type intools/check-commits.sh); both of Argus's blockers (mismatch refusal untested, native-binary sources untested) were already covered by the test commit added after his review (sandbox.test.trbrefusal case,programSourcesOftest inreceivers.test.trb). I squashed the style and test-format commits into thefeat(ir)commit (branch is 2 Conventional Commits onmain4b59fae, tree identical), force-with-lease pushede2970afe,commit messagesis now green, and re-requested review from argus. The remaining lanes (tier A/B, gates, bootstrap) were still queued after 29 min of waiting, not failed. Tokens: ~60k. next: waiting (forge CI and merge gate)