The corelib workspace exposes hundreds of public items across packages/foundation, packages/runtime, packages/console, packages/concurrency, and packages/compiler-sdk. Some items (Collections.Array.Len, System.Output.Write) are battle-tested and already part of the prelude; others (Collections.List.Get, System.FS.WriteAllText) ship as shape-only stubs while the runtime catches up. Without a tier classification, downstream consumers cannot tell which APIs are safe to depend on and which may break in the next minor release.
FAQ and troubleshooting
Platform spec article
FAQ and troubleshooting
Spec standingStandard
-
Public corelib items classify as Tier 1 (Standard), Tier 2 (Supported), or Tier 3 (Unstable); missing directives default to `supported`.
Context
Decision
Rule Detail Tier vocabulary Tier 1 — standard, Tier 2 —supported, Tier 3 —unstableAuthoring directive /// @tier(<value>)on the declaring doc comment of any public itemAliases Tier1/Standard/standard,Tier2/Supported/supported,Tier3/Unstable/unstable(case-insensitive)Default Public items without a directive resolve to supportedCascade Item-site → parent → package default → workspace default Serialization Lowercase string under camelCase tierfield inApiDocItem; omitted when unresolvedConsequences
- Downstream tooling can badge every documented item by tier without consulting an external manifest.
- Authors get a one-line annotation surface; existing items default to a meaningful tier (
supported) without a sweeping refactor. - The default of
supportedis the safest "no commitment, no claim of unstable" choice. Future tightening to require explicit directives is possible without a compatibility break.
Verification anchors
compiler/crates/beskid_analysis/src/doc/api_tier.rs(resolver + tests)compiler/crates/beskid_analysis/src/doc/api_snapshot.rs(tierfield)compiler/crates/beskid_cli/src/commands/doc.rs::execute(CLI emission)compiler/crates/beskid_tests/src/projects/corelib/layout.rs::checked_in_corelib_tier_metadata_round_trips_through_api_json
-
The aggregate corelib prelude `Prelude.bd` must re-export Tier-1 modules only; Tier 2 / Tier 3 require explicit `use` imports.
Context
Every Beskid project gets the corelib prelude injected by default (see Corelib injection and resolution). Adding a module to the prelude makes it transitively visible to all downstream code. Including Tier 2 / Tier 3 modules in the prelude would expose unstable surfaces by default and violate the tier compatibility table.
Decision
Rule Detail Membership rule compiler/corelib/beskid_corelib/src/Prelude.bdmust re-export only modules whose declarations resolve tostandardEnforcement compiler/crates/beskid_tests/src/projects/corelib/layout.rs::corelib_prelude_only_re_exports_tier1_modulescross-checks everypub mod ...;line inPrelude.bdagainst the resolved per-module tierPromotion path Promoting a Tier 2 module to Tier 1 requires (a) updating the directive to @tier(standard)on the module's package source and (b) adding a row to the hub's## Decisionssection pointing at this ADR or a new follow-up ADRDemotion path Demoting a Tier 1 prelude item requires a normative ADR under adr/and a compatibility alias for at least one minor releaseConsequences
- The prelude stays small and predictable: only a vetted surface ships transitively.
- Tier 2 / Tier 3 modules remain reachable via explicit
use System.FS;(etc.); no expressivity lost. - CI catches accidental prelude growth before merge, keeping the v0.3 → v1.0 compatibility budget intact.
Verification anchors
compiler/corelib/beskid_corelib/src/Prelude.bdcompiler/crates/beskid_tests/src/projects/corelib/layout.rs::corelib_prelude_only_re_exports_tier1_modulescompiler/crates/beskid_tests/src/projects/corelib/compile.rs::checked_in_corelib_prelude_exports_mvp_modules
-
The corelib API-shape tier classification is serialized as a `tier` field on every `ApiDocItem` row; tooling must not consult a parallel JSON / TOML file for tiers.
Context
Tier classification needs to flow from corelib sources to consumers (pckg dashboard, IDE tooling, conformance fixtures). Two paths were considered: (a) emit a sibling
tiers.jsonnext toapi.json, or (b) attach the tier directly to each item row inapi.json. Path (a) creates a second source of truth and risks drift when items rename or move; path (b) keeps the contract in one place but requires aapi.jsonschema bump.Decision
Rule Detail Field placement tier: Option<String>onApiDocItemincompiler/crates/beskid_analysis/src/doc/api_snapshot.rsSerialization camelCase tier; lowercase value; omitted when unresolvedSchema version API_JSON_SCHEMA_VERSIONstays at4; thetierfield is additive and consumers that ignore unknown fields are unaffectedAnti-pattern Tooling must not consult a parallel manifest file ( tiers.json,Tier.toml, …) for tier classificationConsequences
- One source of truth: every tooling consumer reads from
api.jsonand gets tier alongside signature, doc markdown, and module path. - Future schema changes (e.g., adding
tierReasonorsince) can layer onto the same row without splitting the contract. - Authors discover tier drift the moment
beskid docruns locally; there is no second file to forget to update.
Verification anchors
compiler/crates/beskid_analysis/src/doc/api_snapshot.rs::ApiDocItemcompiler/crates/beskid_analysis/src/doc/api_tier.rscompiler/crates/beskid_cli/src/commands/doc.rs::executecompiler/crates/beskid_tests/src/projects/corelib/layout.rs::api_doc_root_advertises_v4_schema_for_tier_metadata
- One source of truth: every tooling consumer reads from
- Contracts and edge cases Normative MUST/SHOULD/MAY rules for corelib API tiering and the edge cases authors and tools must handle deterministically.
- Design model Conceptual model for the corelib three-tier API-shape classification and how it propagates from sources to consumers.
- Examples Canonical Tier 1 / Tier 2 / Tier 3 annotations on `Collections.Array.Len`, `Collections.Map.Count`, and `Collections.List.Get`, plus the resulting `api.json` rows.
- FAQ and troubleshooting Common questions about `@tier(...)` directives, prelude exposure, and how to debug tier drift between sources and `api.json`.
- Flow and algorithm How a `@tier(...)` doc directive becomes a `tier` field on every `api.json` row consumed by IDEs, lints, and the registry.
- Verification and traceability Matrix mapping each API-shape contract to the Rust tests, Beskid test targets, and CLI commands that pin it down.
0 revisions (git unavailable at build; counts may be empty)
No commits recorded for this path.
| Section id | Required | Found |
|---|---|---|
what-this-feature-specifies | yes | yes |
implementation-anchors | yes | yes |
Full tree: run pnpm verify:platform-spec-layout (writes src/generated/platform-spec-layout-report.json).
Why is my @tier(...) directive not showing up in api.json?
Section titled “Why is my @tier(...) directive not showing up in api.json?”Three common causes:
- The directive is on a non-public item. The resolver only stamps
tieron items whosevisibilityispublic. Promote the item visibility or annotate the wrappingpubitem. - The directive sits in a detached doc block before a blank line. The parser binds
///runs to the next declaration; a blank line breaks the binding and the directive is dropped silently. Move the directive into the doc comment that touches the item. beskid docwas run on a different package than the one publishingapi.json. The resolver runs afterassign_declaring_packages, so items in a path-only dependency staytier: Noneeven when their source carries a directive. Runbeskid docinside the owning package.
I promoted a module to Tier 1 but the prelude validator still fails
Section titled “I promoted a module to Tier 1 but the prelude validator still fails”The prelude validator inspects the pub mod ...; list and the effective tier of every re-exported module. If one nested module is still Tier 2 / Tier 3, the publish job fails. Either promote the nested module or stop re-exporting the parent through the prelude — explicit use ...; imports are the correct way to access non-Tier-1 surface.
How do I debug tier drift between sources and api.json?
Section titled “How do I debug tier drift between sources and api.json?”- Run
beskid doc --package <name> --output api.jsonand grep for"tier"to see what landed. - Compare the count of
@tier(lines in the package sources with the count of"tier"keys inapi.json. A gap means the cascade did not match — usually a non-public item or a detached doc block. - Run
cargo test -p beskid_tests projects::corelib::layout::checked_in_corelib_tier_metadata_round_trips_through_api_jsonto confirm the resolver’s behavior in isolation.
What if I disagree with the workspace default of supported?
Section titled “What if I disagree with the workspace default of supported?”Open an ADR under corelib-api-shape/adr/ proposing the new default. Until accepted, supported is the de jure workspace default per D-CORE-API-SHAPE-0001 and the resolver leaves untagged items as tier: None so consumers apply that default uniformly.
Why does the prelude only re-export Tier 1 modules?
Section titled “Why does the prelude only re-export Tier 1 modules?”D-CORE-API-SHAPE-0002 explains the decision. Short version: the prelude is the path of least resistance for new authors; offering them an item that may change shape between minors would silently degrade the upgrade experience.
Where do I report Tier 1 / Tier 2 contract breaches?
Section titled “Where do I report Tier 1 / Tier 2 contract breaches?”Open an issue against the owning crate and attach:
- The
qualifiedNameand currenttierfromapi.json. - The PR or commit that introduced the breach.
- The corresponding test from the verification matrix that should have caught it but did not — that is the regression-proofing target.
Does the tier field affect runtime behavior?
Section titled “Does the tier field affect runtime behavior?”No. The classification is metadata only. Tier 3 items compile and execute identically to Tier 1 items; the tier signals stability promises to humans and tooling, not the compiler’s lowering or codegen.