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.
Corelib API shape
Platform spec feature
Corelib API shape
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).
What this feature specifies
The corelib API-shape feature classifies every public corelib item into one of three stability tiers and pins the rules for how those tiers travel through the toolchain. It defines:
- The three tiers (Standard / Supported / Unstable), their semantic promises, and their default audience.
- How authors declare a tier on a module or item with a
@tier(...)doc directive. - How the compiler resolves tier metadata across the cascade (item-site → parent module → package default → workspace default) and writes the resulting value into
api.json. - The prelude rule that only Tier 1 modules may be re-exported through
beskid_corelib’s prelude. - The cross-version compatibility envelope each tier guarantees during a
v0.xminor bump and during avN → vN+1major.
Inputs and outputs
| Stage | Input | Output |
|---|---|---|
| Authoring | /// doc comments containing @tier(standard|supported|unstable) on modules, types, functions, contract methods | Source carries normative tier declarations |
Analysis (beskid_analysis::doc::api_tier) | ApiDocItem rows produced by beskid doc | Each row stamped with a resolved tier: Option<String> (omitted only when nothing resolves) |
Packaging (beskid pckg pack) | api.json containing tier-tagged items | .bpk bundle whose package.json points at the tier-tagged api.json |
Consumption (beskid_pckg, IDE) | api.json from the registry | Tier-aware UI badges, lint warnings on Tier 3 usage outside opt-in contexts |
State model
The tier classification is deterministic and side-effect free. The resolver applies the following cascade, stopping at the first match:
@tier(...)directive on the item itself.@tier(...)directive on the immediate parent module (inherited by all its members).- The package’s default tier declared in
Project.proj(when present). - The workspace default tier (currently
supported).
Any item that flows through the cascade without resolving stays as tier: None in api.json; consumers treat that as the workspace default tier.
Algorithms and flow
End-to-end pipeline:
flowchart LR src[".bd source + /// @tier(...)"] --> parse[beskid_analysis parser] parse --> snap[ApiDocItem rows] snap --> tier[resolve_item_tiers cascade] tier --> emit[beskid doc — api.json] emit --> pack[beskid pckg pack] pack --> registry[pckg registry] registry --> consumer[IDE / docs / lints]
See Flow and algorithm for the numbered algorithm and the parser repair contract.
Edge cases and errors
- Method declared
@tier(standard)on a Tier 3 type — see Contracts and edge cases. - Conflicting directives on the same item — the closest directive wins; the resolver does not merge.
- Unknown tier value — rejected at analysis time with a clear diagnostic; the item stays
tier: None. - Prelude leakage — the prelude validator refuses to re-export Tier 2 or Tier 3 modules.
Compatibility and versioning
- Tier 1 (Standard) — signature stable across the entire
v0.xline; breaking changes only at a major bump and only with a normative ADR. - Tier 2 (Supported) — signature stable within a
v0.xminor; behavior may evolve with deprecations. - Tier 3 (Unstable) — signature may change without notice; consumers opt in by reading the tier field.
Security and performance notes
Tier classification is metadata; it does not affect runtime semantics or codegen. The resolver runs in linear time over the ApiDocItem vector with no allocations beyond the resolved string. The tier field is omitted from api.json when None, keeping bundle size flat for legacy packages that have not yet annotated their surface.
Examples
See Examples for canonical Tier 1 / 2 / 3 annotations on Collections.Array.Len, Collections.Map.Count, and Collections.List.Get, plus the resulting api.json shape.
Verification and traceability
See Verification and traceability for the requirement-to-test matrix and the conformance commands.
Decisions
| ADR | Title |
|---|---|
| D-CORE-API-SHAPE-0001 | Three-tier classification with default supported |
| D-CORE-API-SHAPE-0002 | Corelib prelude re-exports only Tier 1 modules |
| D-CORE-API-SHAPE-0003 | Tier metadata travels inside api.json, not a parallel manifest |
Implementation anchors
- Tier resolver:
compiler/crates/beskid_analysis/src/doc/api_tier.rs - Tier field on
ApiDocItem:compiler/crates/beskid_analysis/src/doc/api_snapshot.rs - Tier resolution call site:
compiler/crates/beskid_cli/src/commands/doc.rs - Tier-tagged corelib sources:
compiler/corelib/packages/foundation/src/Collections/,compiler/corelib/packages/runtime/src/System/ - Tier conformance tests:
compiler/crates/beskid_tests/src/projects/corelib/layout.rs - Beskid-side coverage suites:
compiler/corelib/beskid_corelib/tests/corelib_tests/src/collections/,compiler/corelib/beskid_corelib/tests/corelib_tests/src/system/
Articles
- Three-tier classification with default `supported`Public corelib items classify as Tier 1 (Standard), Tier 2 (Supported), or Tier 3 (Unstable); missing directives default to `supported`.
- Contracts and edge casesNormative MUST/SHOULD/MAY rules for corelib API tiering and the edge cases authors and tools must handle deterministically.
- Design modelConceptual model for the corelib three-tier API-shape classification and how it propagates from sources to consumers.
- ExamplesCanonical 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 troubleshootingCommon questions about `@tier(...)` directives, prelude exposure, and how to debug tier drift between sources and `api.json`.
- Flow and algorithmHow a `@tier(...)` doc directive becomes a `tier` field on every `api.json` row consumed by IDEs, lints, and the registry.
- Verification and traceabilityMatrix mapping each API-shape contract to the Rust tests, Beskid test targets, and CLI commands that pin it down.