Skip to main content

Crate hunyi

Crate hunyi 

Source
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§

AsyncExposureBoundary
An async-exposure boundary: a module’s public API must not declare an async fn. The implicit-existential complement of ImplTraitBoundary: an async fn leaks a compiler-inserted impl Future (and commits the seam’s contract to an async model), so where impl-trait forbids a written -> impl Future, this forbids the async fn sugar (observed from syn::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.
DynTraitBoundary
A dyn-trait boundary: a module’s public API must not expose trait-object (dyn) syntax. The type-shape complement of SemanticBoundary (signature-coupling): where that forbids an exposed named type, this forbids an exposed type shape — a dyn node at any depth in the governed public surface. Internal dyn is 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.
ForbiddenMarkerBoundary
A forbidden-marker boundary: types defined in a module subtree must not acquire a forbidden trait — by #[derive(T)] or a hand-written impl 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 not T-able” intent.
ImplTraitBoundary
An impl-trait boundary: a module’s public API must not return a written impl Trait (return-position impl Trait / RPIT). The existential complement of DynTraitBoundary: 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-position impl Trait (APIT) is universal (a caller-chosen generic), not an existential leak, and is never governed; async fn’s implicit impl Future is 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.
SemanticBoundaries
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_all evaluates them all with a single cargo metadata read.
SemanticBoundary
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.
TraitImplBoundary
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).
UnsafeBoundary
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. severity is the producing boundary’s severity, so the exit-code decision and the report can treat enforce and warn findings apart. baselined is set when baseline gating records the violation in a baseline; a baselined violation does not fail the reaction.
ViolationId
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.
VisibilityBoundary
A visibility boundary: a governed module must not declare any bare-pub items — a declared-visibility hygiene rule for an internal / impl-detail layer. The rule is syntactic (the pub keyword 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§

BoundaryKind
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. Enforce fails the reaction (exit 1); Warn reports the violation as advisory without failing — the first rung of adoption, so a dirty project can observe a boundary before enforcing it.
VisibilityCeiling
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. (Public is 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: unsafe is confined to the declared subtree(s).
VISIBILITY_MODULE_RULE
Visibility, Module ceiling: nothing more visible than module-private.
VISIBILITY_RULE
Visibility, Crate ceiling (the must_not_declare_pub sugar): no bare-pub items. Kept verbatim so the sugar’s findings and baselines never churn.
VISIBILITY_SUPER_RULE
Visibility, Super ceiling: nothing more visible than pub(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 metadata read, merging all findings into one outcome. A constitution error on any boundary supersedes (exit 2). The per-capability check/check_trait_impl_locality/ check_visibility entries 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.