Programming Language

Nocter

A self-contained systems language built around simplicity, encapsulation, and foolproof design.

/releases/v0.49.0.md

Nocter v0.49.0 Release Notes

Nocter v0.49.0 completes structured process I/O as one synchronous and executor-safe facility. Applications can launch an owned child, configure inherited, null, or piped standard streams, transfer pipe endpoints into generic byte I/O, observe exact termination, request termination, and abandon unfinished children without blocking an executor or leaking zombies.

This release replaces the previous terminal subprocess sessions. Synchronous operations now use the explicit _blocking suffix, while their unqualified counterparts are asynchronous. No compatibility aliases are retained.

Owned Children and Standard Streams

Command.spawn and Command.spawn_blocking return a Child that owns any requested ChildStdin, ChildStdout, and ChildStderr values until they are taken. Stdio selects inheritance, the null device, or a pipe independently for each standard stream. Every endpoint has one owner and every untaken endpoint closes automatically.

use std/process.{Command, ProcessIo, Stdio}

var command = Command.new("/usr/bin/printf")?
command.arg("hello")?
var io = ProcessIo.inherit()
io.stdout(Stdio.pipe)

var child = await command.spawn(move io)?
let stdout = child.take_stdout()
let status = await child.wait()?

The public API does not expose process identifiers, descriptors, signal numbers, or a caller-side reaping obligation. Child.try_wait observes without waiting and caches terminal status; terminate and kill request graceful and forced termination without consuming the child.

Generic Pipe Transfer

Process pipes implement the ordinary Reader, Writer, BlockingReader, and BlockingWriter contracts. Existing generic buffers therefore work without process-specific adapters. io.copy and io.copy_blocking transfer bytes between arbitrary implementations of those interfaces through one checked policy.

The subprocess-pipeline example connects two child processes with io.copy, concurrently drains both stderr streams and the consumer output, and observes both children under one structured timeout. Its payload exceeds ordinary pipe capacity, so successful execution proves concurrent progress rather than relying on small buffered output.

Closed Process Operations

Command.status and Command.output are canonical executor-safe conveniences over the same spawn, endpoint, and observation model. status_blocking and output_blocking provide their synchronous twins. Output capture handles finite input and simultaneous stdout and stderr without a second launch path or wait-status decoder.

Interrupted observation, a still-running child, normal exit, signal exit, native failure, and an invalid returned identity have one shared classification. The asynchronous runtime waits for an exact process-exit event rather than periodically probing a process identifier.

Cancellation and Abandonment

Destroying an unfinished child transfers its exact ownership identity to compiler-owned cleanup. The caller does not block while cleanup terminates and reaps the child. Cancelling asynchronous spawn or wait destroys each future-owned endpoint exactly once and preserves the same abandonment obligation.

The public contracts contain no source-name registry or compiler recognition for process types. Editor hover, completion, signature help, navigation, semantic tokens, and inlay hints consume ordinary checked declarations and interface evidence.

Documentation Deployment

Generated website HTML is no longer committed to the repository. GitHub Actions validates authored documentation, builds the complete website outside the source tree, records its exact source commit, and deploys one GitHub Pages artifact. The repository retains only authored Markdown, checked Nocter sources, static assets, and generation machinery.

Migration and Non-goals

Code using synchronous status or output must select status_blocking or output_blocking. Unqualified operations return futures and require await. Code that needs streaming I/O should use spawn or spawn_blocking and take the requested endpoints from the returned process value.

v0.49.0 does not add shell parsing, implicit PATH search, process groups, terminal control, pseudo-terminals, daemonization, arbitrary numeric signal APIs, asynchronous regular-file I/O, asynchronous item iteration, or targets other than arm64-darwin.

Release Qualification

Release-content commit 63ce5e0c09175f0488ef03fbbf43131b848e8f12 passed the complete disposable compiler gate. Two independent optimized builds produced byte-identical archives and recursively identical installed homes. The retained archive is 8,992,090 bytes with SHA-256 3d494ab5dde7b6fda0e70a47e15a9de45ddf6115a2f2fe6c62d453d092a57852 and contains exactly 284 standard-library files. A fresh extraction passed version and installation diagnosis, locked/offline package workflows, native build and execution, every public example, structured subprocess behavior, framed LSP analysis, immutability checks, and compiler and standard-library tamper rejection. Publication reuses this retained archive without rebuilding it.