Expand description
渾儀 (Húnyí) — the semantic observation dimension of Tianheng.
Where the gnomon 圭表 observes imports (does domain import infra?), 渾儀
observes meaning via the AST (syn): does a module’s public API expose a
forbidden type? That is the complement of import-governance — a type imported for
internal use is fine; a type named in a pub signature is a leak, and one named via a
fully-qualified path with no use is invisible to a token scanner but caught here.
Declare a SemanticBoundary in Rust, check it against a Cargo workspace, and get
an Outcome with the same exit-code contract (0/1/2) and reaction model as the static
dimension — both express findings in the shared 璇璣 (xuanji) crate. The heavy syn
dependency is quarantined to this crate, never the core (self_governance.rs).
Observed surface and its honest bounds. The exposed surface of a module anchor is
its pub free functions (params/returns), pub struct/enum/union field types, pub
type-alias targets, pub const/static types, pub trait method signatures and
associated-type bounds (and supertraits), the generic bounds / where-clauses of those
items, the pub methods of inherent impl blocks, and named public re-exports
(a pub use crate::infra::X; republishes the forbidden type on the module’s surface — observed
by default; a glob re-export reacts when its root is in/under the forbidden set). A trait
impl block’s impl-site-authored positions are out of scope by default but observable via the opt-in
.including_trait_impls() depth (semantic-trait-impl-exposure): the trait ref’s generic
args, the Self type, associated-type bindings, the impl’s own generics/where-clause, and
the method return type as written (its params/receiver stay trait-dictated). Out of scope
(stated bounds, not silent passes): a type reachable only through a glob import or a
macro; and a type knowable only through inference (a return-position impl Trait that
hides a concrete type, or a complex-target or generic type alias — type X = Vec<Db> /
type X<T> = …). A resolvable single-path type X = a::b::C; alias chain, by contrast, is
followed to its defining path and reacts (v0.1.4). Within the resolved scope there is no false
negative: a forbidden type that is resolvable always reacts.
Govern by reaction, not instruction.
Layout. Each semantic capability is a self-contained reaction module
(check_<cap> → check_<cap>_boundary → <cap>_findings); check_all composes the eight
with a single cargo metadata read. The shared reaction spine lives in the driver module
and the canonical rule labels in rules, below every capability so none depends on another.
Structs§
- Async
Exposure Boundary - An async-exposure boundary: a module’s public API must not declare an
async fn. The implicit-existential complement ofImplTraitBoundary: anasync fnleaks a compiler-insertedimpl Future(and commits the seam’s contract to an async model), so where impl-trait forbids a written-> impl Future, this forbids theasync fnsugar (observed fromsyn::Signature.asyncness). Governs public free fns, public inherent methods, and public trait method declarations; trait-impl methods (asyncness dictated by the trait) and private items are excluded. Declarative intent by anchor scoping — “this declared seam is synchronous” (a sync-core/async-edges layering), not a blanket “no async”. - Baseline
- A recorded set of accepted violations — a generated observation snapshot, not policy. The gate fails only on violations absent from it.
- DynTrait
Boundary - A dyn-trait boundary: a module’s public API must not expose trait-object (
dyn) syntax. The type-shape complement ofSemanticBoundary(signature-coupling): where that forbids an exposed named type, this forbids an exposed type shape — adynnode at any depth in the governed public surface. Internaldynis never a violation — this governs exposure across the declared seam, not internal dynamic dispatch, so it is intent (by anchor scoping), not a lint. Declared in Rust and composed with the other dimensions at the gate. - Forbidden
Marker Boundary - A forbidden-marker boundary: types defined in a module subtree must not acquire a
forbidden trait — by
#[derive(T)]or a hand-writtenimpl T for <a subtree type>. Declared in Rust and composed at the gate. The complement to exposure, impl-locality, and visibility; it delivers the “this layer is notT-able” intent. - Impl
Trait Boundary - An impl-trait boundary: a module’s public API must not return a written
impl Trait(return-positionimpl Trait/ RPIT). The existential complement ofDynTraitBoundary: where that forbids the dynamic-dispatch shape (dyn), this forbids the existential shape — an unnameable type the caller cannot name, store without boxing, or rely on beyond its declared bounds. Governs return positions only: argument-positionimpl Trait(APIT) is universal (a caller-chosen generic), not an existential leak, and is never governed;async fn’s implicitimpl Futureis a distinct compiler-inserted existential, out of scope. Declared in Rust and composed with the other dimensions at the gate. - Report
- All violations from one evaluation.
- Semantic
Boundaries - The 渾儀 (semantic) dimension’s boundaries, gathered so the shell takes the dimension as
one unit rather than one parameter per capability. Each field is one capability’s
boundaries;
check_allevaluates them all with a singlecargo metadataread. - Semantic
Boundary - A semantic boundary: the public API of a module must not expose any forbidden type. Declared in Rust (the single source of truth), alongside — and composed with — the static constitution at the gate. Each dimension owns its own declaration DSL and expresses findings in the shared 璇璣 model; the shell merges them into one reaction.
- Trait
Impl Boundary - A trait-impl-locality boundary: within a target crate, the named trait may be
implemented only inside the declared allowed module location(s). An
impl <Trait> for <Type>block outside them is a violation. Declared in Rust (the single source of truth) and composed with the other dimensions at the gate. This governs impl locality — the complement of exposure (SemanticBoundary) and of the static import boundary. It governs only the target crate’s own impl sites; it makes no claim about downstream crates (that would be external trait sealing, an essential gap). - Unsafe
Boundary - An unsafe-confinement boundary: within a target crate,
unsafe(blocks,unsafe fn/impl/trait,unsafe extern) may appear only under the declared subtree(s); a site outside all of them reacts. Declared in Rust (the single source of truth), composed at the gate. - Violation
- One violated boundary.
severityis the producing boundary’s severity, so the exit-code decision and the report can treat enforce and warn findings apart.baselinedis set when baseline gating records the violation in a baseline; a baselined violation does not fail the reaction. - Violation
Id - A violation’s identity for baseline matching:
(target, rule, finding). Reason and severity are excluded so editing them does not turn a known violation new. - Visibility
Boundary - A visibility boundary: a governed module must not declare any bare-
pubitems — a declared-visibility hygiene rule for an internal / impl-detail layer. The rule is syntactic (thepubkeyword on the module’s own direct items), not crate- reachability:pub(crate)/pub(super)/pub(in …)/private are allowed, and attribute- derived public surface (#[macro_export],#[no_mangle]) is out of scope (the deferred attribute capability’s domain). Declared in Rust and composed with the other dimensions at the gate.
Enums§
- Boundary
Kind - Which kind of boundary produced a violation — surfaced in the JSON report so a
consumer need not reverse-engineer the rule string. Not part of the baseline
identity (
ViolationId), so adding it does not invalidate existing baselines.#[non_exhaustive]: a further dimension (e.g. runtime) adds its own kind without breaking consumers. - Outcome
- The reaction’s outcome. Exit codes separate architectural drift (1) from a misconfiguration (2), so a mistyped target is not reported as drift.
- Polarity
- The repair direction a boundary-drift violation points to — a different axis from
BoundaryKind, which names which dimension saw it. Derived from the producing rule’s type (known at the reaction site), never observed from code and never declared by the adopter.#[non_exhaustive]: the axis is deliberately two-valued, but a future rung stays additive. - Severity
- How strongly a boundary reacts.
Enforcefails the reaction (exit 1);Warnreports the violation as advisory without failing — the first rung of adoption, so a dirty project can observe a boundary before enforcing it. - Visibility
Ceiling - The maximum declared visibility a governed module’s direct items may carry. An item whose
declared-visibility rank is strictly above the ceiling reacts; at or below it passes.
(
Publicis deliberately not a ceiling — it would never react.)
Constants§
- ASYNC_
EXPOSURE_ RULE - Async-exposure: a module’s public API must not declare an
async fn. - DYN_
TRAIT_ RULE - Dyn-trait: a module’s public API must not expose trait-object (
dyn) syntax. - FORBIDDEN_
MARKER_ RULE - Forbidden-marker: a subtree’s types must not acquire a forbidden trait.
- IMPL_
TRAIT_ RULE - Impl-trait: a module’s public API must not return a written
impl Trait(RPIT). - SIGNATURE_
RULE - Signature-coupling: a module’s public API must not expose a forbidden type.
- TRAIT_
IMPL_ RULE - Trait-impl-locality: a trait may be implemented only in its declared location(s).
- UNSAFE_
CONFINEMENT_ RULE - Unsafe-confinement:
unsafeis confined to the declared subtree(s). - VISIBILITY_
MODULE_ RULE - Visibility,
Moduleceiling: nothing more visible than module-private. - VISIBILITY_
RULE - Visibility,
Crateceiling (themust_not_declare_pubsugar): no bare-pubitems. Kept verbatim so the sugar’s findings and baselines never churn. - VISIBILITY_
SUPER_ RULE - Visibility,
Superceiling: nothing more visible thanpub(super).
Functions§
- apply_
baseline - Mark each violation the baseline records as
baselined, so it no longer fails the reaction; violations absent from the baseline are left as new. - check
- Run the semantic boundaries against the Cargo workspace at
manifest_path. - check_
all - Evaluate every declared semantic boundary against the workspace with a single
cargo metadataread, merging all findings into one outcome. A constitution error on any boundary supersedes (exit 2). The per-capabilitycheck/check_trait_impl_locality/check_visibilityentries remain for direct use; the shell composes via this. - check_
async_ exposure - Run the async-exposure boundaries against the Cargo workspace at
manifest_path. - check_
dyn_ trait - Run the dyn-trait boundaries against the Cargo workspace at
manifest_path. - check_
forbidden_ marker - Run the forbidden-marker boundaries against the Cargo workspace at
manifest_path. - check_
impl_ trait - Run the impl-trait boundaries against the Cargo workspace at
manifest_path. - check_
trait_ impl_ locality - Run the trait-impl-locality boundaries against the Cargo workspace at
manifest_path. - check_
unsafe_ confinement - Run the unsafe-confinement boundaries against the Cargo workspace at
manifest_path. - check_
visibility - Run the visibility boundaries against the Cargo workspace at
manifest_path.