Iter 13d: rustdoc polish for ailang-core + new docwriter agent

Adds ailang-docwriter to /agents/ — a recurring role for keeping
crate-, module-, and pub-item-level rustdoc accurate. First mission:
ailang-core. Crate root, every module root, every pub item documented;
intra-doc links throughout; Iter-13a additions (TypeDef.vars, Type::Con.args)
get an explicit backwards-compat note. Two stale broken-link warnings in
ailang-check fixed in passing. cargo doc --no-deps now warning-free across
the workspace; promoted to verification invariant 6 in DESIGN.md.
This commit is contained in:
2026-05-07 15:02:35 +02:00
parent 3d9fbc68c6
commit c90926dbba
12 changed files with 536 additions and 36 deletions
+63 -3
View File
@@ -1,7 +1,48 @@
//! AILang core data model.
//! Foundation crate of the AILang toolchain.
//!
//! The source of an AILang translation unit is a `Module` as JSON. This
//! crate defines the schema, serialization, and content-addressed hashing.
//! `core` owns the data model that every other crate consumes:
//!
//! - **AST** — see [`ast`]. The source of an AILang translation unit is an
//! [`ast::Module`] expressed as JSON; the types here mirror that schema
//! one-to-one via serde.
//! - **Canonical JSON** — see [`canonical`]. A deterministic byte form
//! (no whitespace, lexicographically sorted object keys) used as the
//! pre-image for hashing.
//! - **Content-addressed hashes** — see [`hash`] and [`def_hash`]. Each
//! definition gets a stable 16-hex-char BLAKE3 prefix derived from its
//! canonical bytes, which is how the rest of the pipeline refers to it.
//! - **Pretty-printing** — see [`pretty`]. AST → human-readable text for
//! diff/review. The JSON form remains the source of truth.
//! - **Workspace loading** — see [`workspace`] and [`load_workspace`].
//! Resolves transitive imports of an entry module and returns a
//! [`Workspace`].
//!
//! ## Place in the pipeline
//!
//! `core` → `check` (typecheck) → `codegen` (LLVM emission) → `ail` CLI.
//! `core` has no upstream dependencies inside the workspace; everything
//! else depends on it.
//!
//! ## Central invariant
//!
//! Canonical JSON is **deterministic**: the byte output of
//! [`canonical::to_bytes`] for a given value never changes across runs,
//! versions of `serde_json`, or platforms. Hashes are
//! **content-addressed**: equal canonical bytes ⇒ equal hash. Schema
//! identifier is `ailang/v0`; a [`Module`] whose `schema` field disagrees
//! is rejected at load time. Schema extensions added in later iterations
//! (e.g. parameterised ADTs in Iter 13a) are written so that all
//! pre-existing on-disk hashes remain bit-identical — see
//! [`ast::TypeDef::vars`] and the `args` field of [`ast::Type::Con`].
//!
//! ## Entry points
//!
//! - Read a single module from disk: [`load_module`].
//! - Read an entry module plus all imports: [`load_workspace`].
//! - Hash a [`Def`]: [`def_hash`]. Hash a whole [`Module`]:
//! [`workspace::module_hash`].
//! - Render a [`Module`] for review: [`pretty::module`] /
//! [`pretty::manifest`].
pub mod ast;
pub mod canonical;
@@ -15,22 +56,41 @@ pub use ast::{
pub use hash::def_hash;
pub use workspace::{load_workspace, module_hash, Workspace, WorkspaceLoadError};
/// Errors raised while loading a single module file.
///
/// Workspace-level loading uses a richer enum that wraps this one; see
/// [`WorkspaceLoadError`].
#[derive(Debug, thiserror::Error)]
pub enum Error {
/// Module file parses as JSON but its `schema` field does not match
/// [`SCHEMA`].
#[error("schema mismatch: expected {expected:?}, got {got:?}")]
SchemaMismatch { expected: String, got: String },
/// Module file failed to parse as JSON or did not match the AST
/// schema (missing/unexpected fields).
#[error("json: {0}")]
Json(#[from] serde_json::Error),
/// File could not be read.
#[error("io: {0}")]
Io(#[from] std::io::Error),
}
/// Result alias scoped to the crate-level [`Error`].
pub type Result<T> = std::result::Result<T, Error>;
/// Schema identifier this version of `core` accepts. Every loaded
/// [`Module`] must carry exactly this string in its `schema` field, or
/// loading fails with [`Error::SchemaMismatch`].
pub const SCHEMA: &str = "ailang/v0";
/// Load a single module file, validate its schema, and deserialize it
/// into a [`Module`].
///
/// This is the lowest-level entry point and does **not** resolve imports
/// — for the transitive closure use [`load_workspace`]. Errors are
/// returned as [`Error::Io`] / [`Error::Json`] / [`Error::SchemaMismatch`].
pub fn load_module(path: &std::path::Path) -> Result<Module> {
let bytes = std::fs::read(path)?;
let module: Module = serde_json::from_slice(&bytes)?;