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.
Contracts and edge cases
Platform spec article
Contracts and edge cases
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).
Normative contracts
Section titled “Normative contracts”| ID | Rule | Level |
|---|---|---|
| API-SHAPE-001 | Every public corelib item SHALL receive a resolved tier through the cascade or stay as tier: None (which consumers MUST treat as supported). | MUST |
| API-SHAPE-002 | @tier(...) directives SHALL only carry the values standard, supported, unstable, or their Tier1 / Tier2 / Tier3 aliases (case-insensitive). Unknown values are a hard parse-time diagnostic. | MUST |
| API-SHAPE-003 | The resolver SHALL stop at the closest directive in the cascade (item → parent module → package default → workspace default). Conflicting directives are not merged. | MUST |
| API-SHAPE-004 | The beskid_corelib prelude SHALL only re-export modules whose effective tier is standard. | MUST |
| API-SHAPE-005 | api.json SHALL emit tier as a lowercase string at the schema’s documented camelCase position, and SHALL omit the field when the resolver leaves it as None. | MUST |
| API-SHAPE-006 | api.json schema version SHALL be at least 4 whenever any item carries a tier field. | MUST |
| API-SHAPE-007 | Tier 1 (Standard) signatures SHALL stay stable for the full v0.x line; breaking changes require a major version bump and a normative ADR. | MUST |
| API-SHAPE-008 | Tier 2 (Supported) signatures SHALL stay stable within a v0.x minor; behavior MAY evolve when preceded by a deprecation cycle. | MUST |
| API-SHAPE-009 | Tier 3 (Unstable) items SHALL NOT be re-exported from any prelude or surfaced as default IDE completions; signatures MAY change without notice. | MUST |
| API-SHAPE-010 | Consumers (IDE / registry / lints) SHOULD render a tier badge next to each documented item so authors see the stability promise without leaving the docs surface. | SHOULD |
Edge cases
Section titled “Edge cases”Method on a Tier 3 type marked Tier 1
Section titled “Method on a Tier 3 type marked Tier 1”A function with @tier(standard) declared on a parameter of a Tier 3 type is still classified Tier 1 by the resolver — the cascade only looks at the item and its module, not the types it references. Authors SHOULD avoid this pattern; the verification suite calls it out as a warning so reviewers can either drop the function to Tier 3 or escalate the type to Tier 2.
Conflicting directives
Section titled “Conflicting directives”If both an item and its parent module carry @tier(...), the item directive wins. The resolver never merges conflicting values and never emits a diagnostic for the override; this is intentional so authors can demote a single function (for example Collections.Map.ContainsKey is unstable inside a supported module).
Prelude leakage
Section titled “Prelude leakage”The prelude validator inspects beskid_corelib/src/Prelude.bd (and the per-package preludes) for pub mod ...; re-exports. Every re-exported module’s effective tier MUST be standard; otherwise the publish job fails with a deterministic diagnostic citing the offending module and its resolved tier.
Unknown / misspelled directive
Section titled “Unknown / misspelled directive”@tier(stable) (no such tier), @tier() (empty), or @tier(standard, supported) (multi-value) are rejected at analysis time. The resolver does not silently default; the item stays tier: None and a diagnostic surfaces the offending span so the author can fix it.
Generated mirror surfaces
Section titled “Generated mirror surfaces”compiler/corelib/packages/compiler-sdk/ mirrors the compiler’s internal syntax tree and is regenerated by tooling. Its modules carry @tier(unstable) at the module level so the cascade hands every generated row to consumers as Tier 3 without per-item annotation.
Failure handling expectations
Section titled “Failure handling expectations”When a tier diagnostic fires, the message SHALL include:
- The fully qualified item name (
Sample::Module::Item). - The source span of the offending directive.
- The current effective tier (or
Nonewhen no cascade level matched). - A pointer to this article so newcomers can read the cascade rules.