RFD 0022 — Package-path addressing (pkg) and the build evaluability gate
- State: committed
- Opened: 2026-06-07
- Decides: two surface/contract decisions settled by building the real-package-layout module-resolution work — (1) how a path addresses modules, including the self-reference anchor and restricted visibility (
pkg::/pub(pkg), notcrate::/pub(crate)), and (2) thatox buildrefuses to emit an artifact containing a rule the runtime cannot evaluate, rather than silently dropping it. Built and merged across PR #128 (resolver anchoring, re-export propagation,pub(pkg), the build gate). Relates to Modules and the loudness stance of RFD 0019.
This RFD gives a home to two decisions the module-resolution PR made that change the surface (crate→pkg) and the build contract (warn→error). Per AGENTS.md surface changes route through an RFD; these are recorded as built.
Question
The corpus examples were hand-flattened into a single namespace, so module resolution had never been exercised on a real nested package. Two questions surfaced when it was:
- How does a path name a module — and how does a package refer to itself? Rust uses
crate::for self-reference andpub(crate)for package-wide visibility. Argon is not Rust; it has packages, not crates. What is the canonical self-anchor and restricted-visibility spelling? - What does
ox builddo with a rule the runtime cannot evaluate? The lowering admits rule shapes the executor’scompile_rulethen refuses (aforall/existsquantifier → OE1315,not <aggregate>→ OE1313, an unsupported aggregate kind → OE1312, …). Such a rule is silently dropped from evaluation.
Context
Resolution was filesystem-relative to the importing file, with no notion of the package root: pkg::a::b, super::, and any reference from a file deep in the tree all failed; only flat-relative paths from the package root happened to work (where relative coincides with absolute). The real overlay emitted ~190 OE0101 errors as a result.
Separately, ox build lowered every rule, loaded the artifact, and warned (non-fatally) on rules the runtime couldn’t evaluate — then wrote the artifact anyway. A consumer querying such a model gets wrong (under-derived) answers with no runtime error, the build warning easy to miss.
Decision
D1 — Path addressing: pkg is the sole self-anchor; no crate
A qualified path’s leading segment selects a root (Name resolution):
pkg— the current package’s root. The only way a package refers to itself; a package never names itself by its own package name.pkg::a::b::XnamesXin modulea::b. Rename-safe: changing the package’s name inox.tomldoesn’t break internal paths.self— the current module;super(repeatable) — an ancestor module.- A dependency package name (
[dependencies]), and the always-availablestdroot. - Otherwise the leading segment resolves against the scope chain (a submodule of the current module, or a
use-imported name).
Restricted visibility is pub(pkg) (package-wide), not pub(crate). There is no crate keyword anywhere in the surface. pub(<anything-but-pkg>) is a parse error (OE0001) — it is not silently widened to pub.
pub use … ; / pub use … ::*; re-export (transitively); a plain use is a private import and does not re-export. Under the v0 world-assumption simplification pub(pkg) re-exports identically to pub (package boundaries aren’t yet modeled as a visibility cut).
D2 — ox build refuses to emit an un-evaluable artifact
If any rule in the lowered program is one the runtime’s compile_rule refuses, ox build fails and writes no artifact (it re-runs the runtime’s own rule compiler as the oracle, before write_oxbin). A built .oxbin therefore evaluates every rule it contains, or it does not exist.
Rationale
- One obvious self-anchor. Supporting both
pkg::and the package’s own name would make every self-reference a silent style choice and blur the inside/outside boundary (a reader couldn’t assume a package-name path is a dependency).pkg::is unambiguous and rename-safe; the package name stays the dependent-facing absolute path. cratemeans nothing in Argon. The unit of distribution is a package ([package]inox.toml); borrowing Rust’scratevocabulary would be a false cognate.- Loud over silently-wrong (D2). A knowledge system that returns a plausible-but-wrong answer is worse than one that refuses — the same instinct as RFD 0019. A dropped rule is a silent under-derivation; refusing the artifact moves the failure to build time where it’s visible. The full examples corpus builds clean under this gate, so no real model relied on the warn-only behaviour.
Alternatives
- Support both
pkg::and package-name self-reference (referential transparency: a symbol’s absolute path is identical inside and outside). Rejected: the cosmetic upside is outweighed by two-ways-to-say-it and rename-fragility; the external absolute path is still expressible. - Keep
pub(crate)(Rust-familiar). Rejected:crateis not an Argon concept. - Warn, don’t fail, on un-evaluable rules (the prior behaviour). Rejected: it ships silently-incomplete artifacts.
- A new diagnostic code for the visibility error. Deferred: reusing
OE0001with a clear message is sufficient; a dedicated code can come later if needed.
Consequences
Visibility::pubPackage(LeanSyntax/Decl.lean),Visibility::Package(Rustoxc-db), and the book §3.1/§3.4 are aligned onpub(pkg). There is nooxc-protocolVisibilitymirror, so the rename is maintained by hand, not the drift gate.self::as a path-start segment does not yet parse (selfis a lexer keyword forself.field); resolver support is in place. Acceptingself/super/pkgas first-class path-root keywords is a follow-up.- Build-time refusal currently keys on the runtime rule compiler; an un-evaluable rule fails the whole build (no partial artifact). This is intentional for v0.
Open questions
- Should
pub(pkg)become a true visibility cut (distinct frompub) once package boundaries are modeled, rather than the v0Package == Publicsimplification? - Should the unknown-
pub(...)rejection get its own diagnostic code (vs the reusedOE0001)? - Should the build gate ever support a partial/
--allow-unevaluablemode for iterative authoring, or is whole-program evaluability the permanent contract?