Programming Language

Nocter

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

/development/milestones/v0.28.0.md

v0.28.0: Practical Text, Formatting, and Output

Status: complete, published, and externally audited (2026-09-02). v0.28.0 completes the text-production boundary needed by ordinary command-line applications: precise ASCII trimming, bounded owned text transformations, one private scalar-formatting authority, line-oriented writers, and symmetric standard-output and standard-error conveniences. It does not add standard input, Unicode property tables, format specifiers, terminal styling, subprocesses, or another target.

Completion Boundary

  • str exposes allocation-free ASCII trim views with exact provenance and byte classification;
  • repeat checks complete capacity before mutation and produces independently owned UTF-8 text;
  • replace_all defines non-overlapping left-to-right replacement and rejects an empty pattern with the stable std.str.empty_pattern error;
  • Format requires one recoverable try_format_into implementation and derives aborting format_into as the standard default used by interpolation;
  • one bool/integer spelling authority serves Format and standard diagnostics, while the same integer paths serve type-owned string conversion rather than a public closed helper matrix;
  • Writer.write_line appends exactly one LF under ordinary partial-output failure semantics;
  • stdout and stderr each provide exact-text and line-text functions with the same write policy;
  • direct process-stream functions and borrowed trim operations publish noalloc without implying nonblocking, atomic, or realtime behavior;
  • formatter, hover, completion, navigation, authored-standard checks, native tests, public examples, and an installed-home path consume the same source contracts;
  • complete workspace, documentation, and responsibility-boundary review gates pass.

Contract Separation

std/str owns UTF-8-safe borrowed scanning and the public transformation algorithms. It may use ASCII byte classification because every ASCII boundary is a UTF-8 boundary; it does not acquire a Unicode database or filesystem responsibility. Owned output is built through std/string, which remains the sole owner of growable UTF-8 storage and capacity mutation.

std/fmt.Format remains the source-facing representation contract required by string interpolation. Its required try_format_into method appends to &+String with recoverable destination growth; its standard format_into default converts that one failure policy to allocation abort for interpolation. Conforming types implement representation once. This removes the public try_append_i8 through try_append_usize matrix without duplicating decimal generation or removing generic recoverable formatting.

std/io.Writer remains the recoverably fallible byte-transport contract. Format and Writer are not merged: formatting failure concerns mutation of an owned String, while a writer may fail after publishing a prefix outside the process. Well-formed &str is their only shared value boundary. Output helpers perform no formatting dispatch; users format explicitly with string interpolation.

std/io owns stdout/stderr selection and portable complete-write policy. The existing Darwin adapter owns raw descriptor syscalls and interruption observations. Line functions may issue a text write followed by an LF write and make no atomicity promise. No compiler role, syntax form, type identity, MIR operation, target ABI, or new syscall is required.

Phase 0: Contract and Feasibility

Status: complete. The practical-standard-library specification fixes the exact text methods, ASCII byte set, replacement progress rule, output names, LF behavior, failure boundaries, noalloc guarantees, and ownership split above.

Feasibility inspection confirms that trim views can use the existing checked UTF-8 range projection, owned transformations can use current String storage operations, and all output functions can reuse the existing complete descriptor writer. Compiler interpolation already selects the exact source-backed Format interface and its &+String method once. It continues to select format_into; the method becomes a standard-library default backed by try_format_into. The standard semantic validator must accept that default body, but checked interpolation, static dispatch, MIR lowering, allocation selection, and ABI need no new operation or representation.

The descriptor writer and stable I/O-error constructors perform no Nocter allocation today, but their private declarations do not yet publish that fact transitively. Phase 3 must annotate and verify the existing helper chain before the public process-stream functions claim noalloc; it must not special-case the public wrappers in effect analysis.

The current public type-specific fmt.try_append_* declarations have only standard-package consumers in std/num and std/json. Those consumers can call the relevant scalar's try_format_into method while the existing decimal-generation algorithm remains the single private implementation. No compatibility wrapper or forwarding public alias will remain.

Phase 1: Text Views and Owned Transformations

Status: complete. std/str/trim.nct now owns one ASCII byte predicate and the directional edge scans used by trim_ascii_start, trim_ascii_end, and trim_ascii. All three produce unchecked subviews only after their own scan has established ASCII byte boundaries; they neither repeat the general UTF-8 boundary algorithm nor allocate. The all-whitespace result is an empty view at the source end and retains source provenance.

std/str/transform.nct owns the two materializing algorithms. repeat uses the package-internal checked multiplication authority before constructing a result. replace_all rejects an empty pattern before allocation, performs one sizing pass through the existing find_from authority, allocates the exact result capacity, then performs one emission pass. The second pass is required to copy matches without allocating a separate match table; it does not own another search algorithm. Both transformations mutate only String, leaving storage ownership and growth policy inside std/string.

The public declarations remain in std/str/index.nct. A separate source test file covers named edges, UTF-8 preservation, empty views, repeat, growing and shrinking replacement, non-overlap, non-recursion, missing matches, and the stable empty-pattern error. A complete native-session test executes the public API and observes the all-whitespace pointer position. LSP hover tests consume the root contracts and show the semantic &str receiver, noalloc, provenance, parameters, result ownership, and fallibility without reading implementation source.

Validation passed the authored-standard declaration/body/dependency review, the focused native execution, the focused LSP presentation test, and the complete workspace library suite. The suite included all 80 language-server and 28 native-session tests. Rust formatting, warnings-denied Clippy for every changed Rust crate, and repository diff checks pass. The source formatter currently declines commented source by contract, so the new standard files were checked through syntax discovery and canonical authored style rather than a comment-destroying rewrite.

Phase 2: Formatting Authority

Status: complete. Format now requires only recoverable try_format_into; its one format_into default converts destination growth failure to the package's allocation-abort policy. Every built-in integer, bool, str, and String implements the recoverable requirement. A project-owned type therefore implements its representation once, while interpolation and explicit aborting formatting share the standard default.

std/fmt/scalar.nct is the only decimal-generation authority. Signed and unsigned entry paths share the same file and destination protocol, preserve the complete 64-bit boundaries, and append through String.try_push_str. The separate signed magnitude path is required because Nocter does not define a same-width signed-to-unsigned conversion; it is not a copied public formatter. std/num now calls each scalar's Format methods for both owned conversion policies, and JSON offset diagnostics call usize.try_format_into. This removed the num -> internal/mem dependency.

The entire public fmt.try_append_str, try_append_string, try_append_bool, and typed integer matrix was deleted without aliases or compatibility wrappers. std/fmt now publishes the interface only. Default policy, scalar spelling, implementations, and tests live in separate source files with the root contract as their only public authority.

The compiler still assigns a semantic role only to format_into, because that is the operation interpolation selects. It does not assign compiler meaning to the ordinary recoverable interface requirement. Standard-role validation now requires format_into to own a default body, and a focused negative test rejects the former bodyless contract. Concrete interpolation records an InterfaceDefault edge; generic interpolation retains its evidence-based interface edge. Existing default-method specialization lowers both without a new MIR operation or ABI rule.

Validation passed the authored-standard body and exact dependency review, standard formatting, all integer conversion, recoverable allocation failure, JSON diagnostics, the public custom-format example, compiler interpolation and MIR tests, and semantic hover for both methods. The complete workspace library suite passed, including 497 checking, 81 language-server, and 29 native-session tests. The comment-free public example also passed the source formatter unchanged.

Phase 3: Symmetric Text Output

Status: complete. Every Writer now derives write_line from its existing exact write_text adapter. The default writes the complete text view and then one LF view; it does not allocate a combined buffer, select a platform newline, hide a flush, or claim atomicity across the two writes. An external recording writer proves exact empty-line behavior and preserves the destination's failure after an observable text prefix.

stdout, stderr, print, println, eprint, and eprintln now publish truthful noalloc contracts. Exact and line helpers select the two process descriptors symmetrically and reach the same complete descriptor-write loop used by File.write. They do not route through an abstract Writer requirement whose implementations may allocate. File lifecycle and input remain in io/file.nct; descriptor-output policy and process conveniences now live in io/output.nct.

The descriptor loop no longer interprets a raw syscall result inline. One private WriteProgress transition classifies interruption as retry, valid partial progress as an exact advance, zero or oversized progress as stable library failures, and every other errno through std/internal/io. The transition, error mapping, descriptor helpers, and process functions form one checked noalloc call graph. Deterministic standard-source tests cover interruption, partial progress, zero progress, invalid progress, and stable OS classification without replacing the real syscall primitive. A native process test independently captures stdout and stderr, including empty lines.

Validation passed the authored-standard contract, both source policy tests, the external Writer default tests, and captured process-stream execution. The complete workspace library suite passed, including 497 checking, 81 language-server, 47 command, and 31 native-session tests.

Phase 4: Tooling and Practical Integration

Status: complete. The text-banner package composes the completed surface in one ordinary command-line report: it trims a borrowed argument, replaces and repeats into owned strings, interpolates the transformed value and its integer byte length, writes the report to stdout by lines, and sends its usage failure to stderr. Its status, stdout, and stderr are part of the shared public-package execution contract rather than a bespoke example test. The same registry proves that every package example has a native target-session path and an exact process contract.

The source formatter owns a focused reporting-pipeline case and still verifies every runnable example byte-for-byte against canonical output. The language server opens the real implementation source as part of its package, then resolves trim and output hover, output navigation, and incomplete module-member completion through the published std/str and std/io declarations. A shared package-source test harness now establishes complete analysis before any application query, so individual editor tests do not silently rely on different initialization assumptions.

The authored-standard review accepted the integration without changing its exact cross-module dependency graph: the example composes public modules, while no new dependency entered the standard implementation. Release qualification now runs text-banner from the extracted distribution with NOCTER_HOME removed and compares exact stdout and stderr. This verifies that the packaged compiler and packaged standard library support the same native scenario without consulting repository-local toolchain state.

Focused formatter, language-server, command execution, native target-session, and authored- standard tests pass. The complete workspace library suite and warnings-denied all-target Clippy also pass, including 497 checking, 82 language-server, 47 command, and 31 native-session tests. Documentation generation, shell syntax, Rust formatting, and repository diff checks pass. Packaged-home qualification remains a Phase 5 release gate; Phase 4 establishes its scenario but does not record an unqualified candidate as a distribution result.

Phase 5: Qualification and Review

Status: complete and reviewed. Complete workspace, warnings-denied Clippy, no-default-features, formatter, documentation, native, authored-standard, and repository gates pass. The Phase 5 review found one trim-boundary authority, one search authority, one decimal renderer, one recoverable-to-aborting formatting policy, and one complete descriptor-write loop. It found no open duplicate authority, public internal helper, hidden allocation or buffering, failure-policy conflation, target leakage, obsolete wrapper, or cross-responsibility caller-discipline assumption.

Release identity, reproducible archive generation, extracted-home qualification, publication, and public re-download evidence are recorded by the release-preparation and immutable release records; they are not claimed by the implementation review.