Nodus v4.1.1
Fixes
-
ASYNC-MOD-003 (#339): closures passed to a module function inside a container
ran against the wrong bytecode. A module function is dispatched in a detached
VM whosecodeis the module's.invoke_functionwrapped top-levelClosure
arguments in a_ClosureProxyso they dispatch back through the caller — but a
closure nested inside a list, map, or record was never wrapped, so its
fn.addr(an index into the caller's bytecode) was executed against the
module's instructions. Under the CLI this raisedStack underflowor
'NoneType' object is not subscriptable; underNodusRuntimeit was worse — the
task body silently never ran andokwas stillTrue.This broke every library, not just the stdlib: any
.ndmodule taking a list of
callbacks was affected (fn call_nested(fns) { return fns[0]() }→Stack underflow).The fix identifies a foreign closure by
FunctionInfoidentity against the
module's ownfunctionstable (which includes mangled anonymous entries such as
__anon_1__fn2) and routes it back through the caller VM at three points:
CALL_VALUEdispatch,coroutine()creation, andspawn(). Coroutines now pin
the context their closure was compiled against at creation time rather than
inheriting the spawning VM's.std:async.parallelandstd:async.serieswork as documented as a result,
including with pre-built coroutines and with tasks thatsleep()— a sleeping
task suspends and the others continue, rather than blocking. Regression tests:
tests/test_async_module_boundary.py(12 tests, CLI and embedded for each
behavior, since the two modes failed differently)._caller_vmis now initialized inVM.__init__so theCALL_VALUEhot path is
a plain attribute read; without that, the added check cost ~1s of CLI startup.
Known bugs
std:async.worker_poolandstd:async.pipelineare still broken
(issue 339 remains open for
these two). Both spawn coroutines inside the module and return a channel for the
caller to drive; those coroutines land on the detached VM's own scheduler, which
nothing runs, so work is silently dropped. Sharing the caller's scheduler is not
sufficient — builtins close over the VM that registered them, so resuming a
module coroutine on the caller VM installs the wrong builtins. Fixing it needs
VM-agnostic builtins, tracked with the design gap in
issue 157. The guide
documents an inline workaround.
Documentation
- Doc sweep against the shipped 4.1.0 surface. Re-pointed every
4.0.8version
claim (README,llms.txt,llms-full.txt,CLAUDE.md, the Claude and Codex
skills) and reconciled the companion-package count, which read 29 / 33 / 35 in
different files, against PyPI: 32 companion packages, 33 projects including
nodus-lang. - Corrected stdlib documentation errors:
std:string→std:strings(the
singular form does not exist);std:timeno longer documented as having
now_ms()orsleep()(the latter is instd:async);std:mathno longer
claimed to provide trig functions;std:asyncsignatures corrected
(queue()takes no arguments,worker_pool(worker, count),parallel/series
return nil rather than a list of results). break,continue, andmatchdocumented in the AI-assistant assets. Both
skills still stated "Nobreakorcontinue" and the Claude skill's operator
table contradicted itself on+=. README,llms-full.txt, and both skills now
cover the 4.1.0 control-flow surface, including the two compile-time errors
('break' outside a loop,'break' cannot cross a try/catch/finally boundary).- README: added the missing stdlib modules (
std:collections,std:path,
std:env,std:utils,std:runtime,std:async,std:tools/std:agent),
documented the[http]/[schema]/[retry]extras alongside[server], noted
thatstd:httprequires the[http]extra, and linked the user guide,
standard-library reference, embedding guide, and ecosystem guide. llms.txt: removed a link todocs/guide/build-a-library.md, a page that was
never written and had been advertised to AI crawlers since the discoverability
commit.docs/guide/ecosystem.md: added the missingnodus-jupyterand
nodus-mcp-serverentries..nodusgate-allow: re-pointed 8 line-number suppressions whose blocks moved.
Gate verified green afterward (static 132/132, runtime 229/229).