Programming Language

Nocter

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

/development/milestones/v0.17.0.md

v0.17.0: Practical Application Foundations

Status: published (2026-08-25). Phase 0 returned development to ordinary application APIs. Phase 1 reconstructed the authored standard library before that public surface grows further. No later v0.17.0 phase is active. Exact source, artifact, and fresh-install evidence is recorded in the release-preparation record.

Goal

Make common local applications expressible through small, composable standard contracts. Public modules must own portable meaning, target-specific standard internals must own raw OS facts, and compiler primitives must remain limited to operations that Nocter source cannot express safely.

Phase 0: Practical Filesystem Operations

Phase 0 introduces std/fs as the path-oriented filesystem module. It complements rather than duplicates the existing modules:

  • std/io owns open byte streams and the File, Reader, and Writer contracts;
  • std/path owns validated UTF-8 path values and lexical path operations;
  • std/fs owns one-shot path operations and portable metadata;
  • std/internal/os owns dependency-free Darwin syscall numbers, errno classification, and native metadata layout;
  • std/internal/io owns the stable public-I/O error policy shared by stream and filesystem APIs;
  • std/internal/path owns the allocator-backed target path argument shared by stream and filesystem operations.

The public surface is:

use std/fs

let text = fs.read_to_string("input.txt")?
fs.write_text("copy.txt", &text)?
let details = fs.metadata("copy.txt")?
if details.is_file() && details.len() != 0 {
    fs.rename("copy.txt", "complete.txt")?
}

read and read_to_string return independently owned values. write and write_text create or truncate their destination and complete the entire write before returning success. metadata follows symbolic links and reports a portable file kind plus a u64 byte length. exists returns false only when the path is absent; permissions and every other OS failure remain recoverable errors. remove_file removes a non-directory entry, including a symbolic link itself. rename uses the target's single rename operation and does not simulate cross-device movement with a copy-and-delete fallback.

All borrowed paths reject embedded NUL before entering the target boundary. The same owned, temporary target path representation is shared by std/process, std/io, and std/fs; none may author a second UTF-8-to-C-path conversion. It lives above the dependency-free OS fact layer so the raw OS module never imports allocation policy. OS-originated failures use one package-internal I/O mapping to the stable std.io.* error classification already exposed by file streams.

Completion Gate

Phase 0 is complete only when all of the following hold:

  • the public std/fs contract provides read, read_to_string, write, write_text, metadata, exists, remove_file, and rename plus opaque portable Metadata and FileType APIs;
  • whole-file functions compose the existing File, Reader, and Writer contracts rather than repeating descriptor read/write loops;
  • Darwin syscall numbers, stat buffer size and offsets, and file-mode values have exactly one source owner under std/internal/os and do not appear in std/fs, compiler semantics, Machine, or ARM64 lowering;
  • std/process, std/io, and std/fs share one target path argument encoder, while std/io and std/fs share one OS-error classifier; neither policy introduces an internal/os dependency on allocation or public I/O behavior;
  • missing-path exists performs no built-in error construction and returns false, while every non-missing failure remains a T! failure;
  • native acceptance through the physical bundled standard library covers text and byte writes, reads, UTF-8 rejection, metadata, existence, rename, removal, missing-path classification, and final cleanup;
  • ordinary discovery and editor analysis compile the complete bundled standard library with the new module; no synthetic filesystem symbol or special editor lookup is added;
  • the complete workspace tests, warnings-denied Clippy, Rust formatting, generated documentation, and repository whitespace gates pass;
  • a final responsibility review finds no duplicated syscall loop, path encoder, errno mapping, metadata ABI table, or compiler dependency on a public filesystem declaration name.

Non-goals

Phase 0 does not add directory traversal, directory creation, recursive operations, permissions, timestamps, symbolic-link creation, canonicalization, memory mapping, asynchronous I/O, Windows or Linux backends, or atomic multi-file transactions. Those require separate contracts rather than optional flags on the Phase 0 functions.

Completion Evidence

Completed on 2026-08-24:

  • std/fs exposes the complete Phase 0 contract and implements whole-file operations by composing the existing stream interfaces.
  • A native fixture crosses the physical bundled standard library and Darwin boundary for text, bytes, invalid UTF-8, metadata, existence, replacement rename, symbolic-link behavior, removal, missing-path classification, and cleanup.
  • std/process, std/io, and std/fs use the single target path representation in std/internal/path; the Darwin representation is target-gated rather than assumed portable.
  • std/internal/io is the sole OS-classification-to-std.io.* policy owner, while std/internal/os remains the sole syscall, errno, and metadata-layout fact owner.
  • Namespace-qualified callable resolution now records one visibility-checked semantic identity in name resolution. Checking consumes that identity without repeating module lookup, including for callable re-exports and body type/constant module segments. Editor hover and navigation consume the same source projection.
  • The final responsibility search found no second descriptor read/write loop, target path encoder, I/O error mapper, metadata ABI table, or compiler dependency on a filesystem API name.
  • cargo test --workspace --quiet, warnings-denied workspace Clippy, Rust formatting, generated documentation, and git diff --check passed for the completed Phase 0 tree.

Phase 1: Standard-Library Reconstruction

Phase 1 makes the authored standard library demonstrate the source and module architecture that Nocter asks package authors to use. It changes ownership and dependency structure rather than adding practical APIs.

The public root of a standard module is its user contract. Package-only declarations do not belong there merely because implementation sources need them. A representation-independent internal subsystem owns an explicit std/internal/* module. Because Nocter forbids implementation sources from adding restricted-visible declarations, a package contract inseparable from a public type's private representation may remain in that type's root only as a reviewed exception.

The intended dependency direction is from target and runtime facts, through pointer and allocation foundations, borrowed views and iteration contracts, owned values and streams, and finally adapters and application services. A higher layer must not become the implementation owner of a lower layer's algorithms. In particular, borrowed UTF-8 behavior belongs to std/str; std/string owns allocated String storage and mutation.

Completion Gate

Phase 1 is complete only when:

  • every standard root contains the complete user-visible contract and no implementation body;
  • independent package-only contracts no longer obscure public roots and have an explicit internal module owner; every representation-bound root exception is named and reviewed;
  • every compiler primitive has one reviewed domain owner, and public modules expose only the primitives that are intentionally user-facing;
  • target ABI constants and syscalls live under a target-specific internal contract rather than a target-neutral root;
  • borrowed string search, ranges, and iterators are owned by std/str, while std/string owns only allocated string construction, storage, mutation, and UTF-8 validation needed for construction;
  • the standard dependency graph has no unreviewed reverse edge or duplicated implementation owner;
  • source navigation distinguishes public contracts, package contracts, and implementations without synthetic editor behavior;
  • the authored standard-library checks, native acceptance, public examples, complete workspace tests, warnings-denied Clippy, formatting, generated documentation, and whitespace gates pass;
  • a final responsibility review confirms that every source depends only on another responsibility's declared contract, never its private representation.

Non-goals

Phase 1 does not add collection, text, filesystem, networking, concurrency, or asynchronous APIs. It does not preserve obsolete internal paths with aliases or compatibility declarations. Public API changes are limited to removing accidental or contradictory surface discovered by the ownership review; new application capabilities belong to a later phase.

Completion Evidence

Completed on 2026-08-25:

  • std/ptr now contains only the three intentionally public address-observation primitives. Address projection, memory copying, raw view construction, and typed value movement have the single package-internal owner std/internal/ptr; allocation abort has the separate owner std/internal/mem.
  • Target-neutral std/internal/os contains only portable OS facts. Darwin syscall declarations, errno classification, file flags, mmap flags, path limits, and metadata layout live in the target-specific std/internal/os/darwin module. No Darwin syscall number remains outside its implementation source.
  • Borrowed search, ranges, byte projection, SplitIter, and LinesIter moved from std/string to std/str. The owned split materialization edge is isolated in std/str/owned.nct, while std/string is divided into storage, construction, UTF-8 validation, and mutation sources.
  • std/mem and std/vec are likewise divided by stable responsibility. Their roots remain the sole contract authority; implementation sources add no visible declarations. The only reviewed restricted contracts in user-facing roots are the four std/mem operations inseparable from private RawBuffer representation.
  • std/process reuses the public UTF-8 validator instead of carrying a second implementation. Upper modules consume allocator, buffer, string, reader, and writer methods through declared contracts rather than package-visible function aliases.
  • The authored-standard test now rejects global public surface in std/internal, independent package plumbing in user-facing roots, and any unreviewed resolved module dependency edge. Its checks consume parsed declarations, resolved imports, and checked semantic references rather than reparsing source spellings.
  • The source tree contains 65 responsibility-named files and 4,905 lines, down from 4,984 lines before reconstruction despite the finer split. Standard roots contain 1,152 lines, down from 1,199, and no implementation source declares package visibility.
  • The complete workspace tests, all 61 language-server tests, warnings-denied workspace Clippy, Rust formatting, generated documentation, and repository whitespace checks passed on the final Phase 1 tree.