module-lang 1.0.0

Module and import resolution for multi-file languages.
Documentation
# module-lang v1.0.0 — stable

**1.0.** The public API is frozen and stable. v0.1.0 stood up the scaffold and the
quality gates; v0.2.0 built the resolution core — the module graph, stable ids, and
the `define` / `import` / `resolve` surface with visibility and cycle detection.
This release ratifies that surface as the `1.0` contract: it follows Semantic
Versioning, carries no breaking changes before `2.0`, and ships with the full
property-test and benchmark suite verified on Linux, macOS, and Windows.

There is no new public API in `1.0.0` — by design. A `0.2.0` program compiles and
behaves identically against `1.0.0`. The freeze adds only what hardens the release:
a stricter lint profile, a `serde` round-trip test, a runnable example, and the
stability documentation.

## What is module-lang?

The module and import resolution layer of a compiler front-end. Given the modules a
front-end has already parsed — each exporting a set of named items — it builds the
module graph, resolves each import to its target, reports unresolved names and
import cycles as defined errors, and honours item visibility. It sits in the SEMA
tier above `symbol-lang`, which interns the names, and `source-lang`, which owns the
files those modules came from. It owns module and import resolution only.

```rust
use intern_lang::Interner;
use module_lang::{ModuleGraph, Visibility};
use source_lang::SourceMap;

let mut sources = SourceMap::new();
let mut names = Interner::new();
let helper = names.intern("helper");

let mut graph: ModuleGraph<&str> = ModuleGraph::new();
let app = graph.add_module(names.intern("app"), sources.add("app.lang", "")?);
let util = graph.add_module(names.intern("util"), sources.add("util.lang", "")?);

graph.define(util, helper, Visibility::Public, "fn helper")?;
graph.import(app, util, helper)?;

assert_eq!(graph.resolve(app, helper), Ok(&"fn helper"));
# Ok::<(), Box<dyn std::error::Error>>(())
```

## The 1.0 surface

The frozen contract is the resolution core:

- **Construction**`ModuleGraph::new`, `with_capacity`, `Default`.
- **Building**`add_module` (name + `SourceId`), `define` (with a `Visibility` and
  a payload), `import` (a name from another module, same spelling).
- **Resolution**`resolve` (a name as seen from a module, following imports to the
  definition), plus `len`, `is_empty`, `module_name`, and `module_source`.
- **Types** — the opaque `ModuleId`, the `Visibility` enum, the `#[non_exhaustive]`
  `ResolveError`, and the re-exported handle types `Symbol` (from `symbol-lang`) and
  `SourceId` (from `source-lang`).
- **Serialization**`serde` for `ModuleId` and `Visibility` under the `serde`
  feature.

The payload type `T` is the language's own — a definition id, an AST node, a type —
and carries no trait bounds.

## SemVer promise

The crate follows [Semantic Versioning](https://semver.org):

- No documented item is removed or changed in a breaking way within `1.x`; breaking
  changes wait for `2.0`.
- New functionality is additive and arrives in minor releases. `ResolveError` is
  `#[non_exhaustive]`, so a new error variant is a minor change. Renamed (`as`)
  imports and nested module paths, if they land, arrive the same way.
- The MSRV is Rust `1.85`; raising it is a minor change, never a patch.

The full statement lives in [`docs/API.md`](https://github.com/jamesgober/module-lang/blob/main/docs/API.md#semver-promise).

## Breaking changes

**None.** `1.0.0` is API-identical to `0.2.0`.

## Verification

Run on Windows x86_64 and Linux x86_64 (WSL2 Ubuntu), Rust stable and 1.85 MSRV; the
same commands run across the CI matrix on Linux, macOS, and Windows:

```bash
cargo fmt --all -- --check
cargo clippy --all-targets -- -D warnings
cargo clippy --all-targets --all-features -- -D warnings
cargo test
cargo test --all-features
cargo build --no-default-features
cargo +1.85 build --all-features
cargo build --examples
RUSTDOCFLAGS="-D warnings" cargo doc --no-deps --all-features
cargo deny check
cargo audit
```

All green. Counts at this tag:

- Default features: 15 unit + 2 integration + 16 doctests.
- `--all-features`: 15 unit + 4 integration (+2 serde round-trip) + 16 doctests.

The resolution invariants — unique/stable ids, agreement with a naive reference
resolver, duplicate rejection, visibility across import boundaries, and bounded
cycle detection — are held over a wide `proptest` input space, with a separate
4096-deep chain confirming the iterative walk does not overflow the stack. The
`resolve` hot path carries `criterion` benchmarks: a local-definition hit in
**under 5 ns**, a miss in **~2 ns**, and a 32-deep re-export chain in **~195 ns**.

## What's next

The `1.0` surface is stable until `2.0`. Future work is documentation, tests, and
internal optimisation that does not change the public contract — plus the additive
candidates the API leaves room for: renamed (`as`) imports and nested module paths,
each a minor release when it lands.

## Installation

```toml
[dependencies]
module-lang = "1.0"
```

With serialization:

```toml
[dependencies]
module-lang = { version = "1.0", features = ["serde"] }
```

MSRV: Rust 1.85.

## Documentation

- [README]https://github.com/jamesgober/module-lang/blob/main/README.md
- [API Reference]https://github.com/jamesgober/module-lang/blob/main/docs/API.md
- [CHANGELOG]https://github.com/jamesgober/module-lang/blob/main/CHANGELOG.md

---

**Full diff:** [`v0.2.0...v1.0.0`](https://github.com/jamesgober/module-lang/compare/v0.2.0...v1.0.0).
**Changelog:** [`CHANGELOG.md`](https://github.com/jamesgober/module-lang/blob/main/CHANGELOG.md#100---2026-06-29).