# API Stability This document defines which `flydsl` APIs may be relied on across releases and the compatibility commitments that apply when they are changed. Its scope is limited to `python/flydsl/`. Below, `fx` refers to `flydsl.expr`. A release means a minor-version increment (for example, `0.3` → `0.4`). A patch release must not break a stable API. ## 1. Summary and general rules - **stable**: backward compatible across minor releases. Previously valid call paths, signatures, and semantics must continue to work. - **unstable**: everything else. These APIs may change or be removed in any minor release without notice. `deprecated` is not a third stability level. It is a lifecycle marker for a stable API: during its deprecation window, it remains stable and protected by this document's compatibility rules; it may be removed only after that window ends. ### Returned-object rule An object returned by a stable API has a stable public result interface: its non-underscore members and Python special methods are stable when reached through that result. This rule applies recursively to objects returned by those members. The concrete implementation class, its constructor, and its import path are not promised by this rule. They may change if the replacement preserves the result interface's public members, signatures, and semantics. For example, `flyc.from_torch_tensor(x).mark_shape_dynamic(0)` is stable, while `flyc.from_torch_tensor(x)._ensure_spec()` is unstable. Determine stability by the mechanical process below. A public export from an `expr` direct-child module has several equivalent access forms: `fx.`, `fx..`, and `from flydsl.expr. import ` have the same stability. Otherwise, classify the path that a caller actually uses, not the object's identity. ## 2. Determining stability Apply the following branches by path prefix: 1. A module-global name or namespace segment beginning with `_` is unstable. On stable types and returned objects, only `__...__` Python special methods may begin with `_`; all other underscore-prefixed members are unstable. 2. A path under `flydsl.expr`: classify it only under §2.1; it is unstable if it does not qualify. 3. A path under `flydsl.compiler`: direct members in `compiler.__all__`, and members in `compiler.protocol.__all__`, are stable. Other deep paths are stable only when listed exactly in §2.3. 4. Every other path is stable only when listed exactly in §2.3. For a member reached through a returned object, first classify its producing API under these branches, then apply the returned-object rule in §1. ### 2.1 `flydsl.expr` `expr/__init__.py` is the sole top-level aggregation manifest. It has no `__all__` of its own; stability is determined exclusively by the following two export chains. #### Symbols exported from direct-child modules Subject to the first branch above, `` in a direct-child module `` is stable if and only if: 1. `expr/__init__.py` aggregates that direct-child module through `from . import *`; 2. `` is in that child module's `__all__`. Names satisfying these conditions have identical stability when accessed via `fx.`, `fx..`, or a direct import. Those direct-child modules and their public export paths are part of the compatibility commitment. Therefore, both `fx.Int32` and `fx.numeric.Int32` are stable. Members omitted from `__all__` remain unstable; `fx.arith._to_raw` is unstable under the first branch even if it was listed in a historical `__all__`. #### Backend entry points and recursive child namespaces `fx....` is stable if and only if: 1. the first-level `` appears in `_BACKEND_MODULES` in `expr/__init__.py`; 2. every following child namespace appears in the `__all__` of its direct parent package; and 3. the final `` appears in the `__all__` of its owning module or package. The first-level backend entry point, and intermediate child namespaces that satisfy condition 2, are stable namespaces as well. This rule applies recursively and has no path-depth limit. For example, `fx.rocdl.cdna3.s_waitcnt` satisfies the complete export chain and is stable, while `fx.rocdl.cluster.*` is unstable when `cluster` is not listed in `rocdl.__all__`. An upstream-MLIR ODS builder that is re-exported but omitted from the final `__all__` is unstable as well. `__all__` is not access control: an attribute may exist and be callable while still failing the stability test above. ### 2.2 `flydsl.compiler` Only direct members of `flydsl.compiler.__all__` are stable. For example, if `jit` is in that manifest, `flydsl.compiler.jit` is stable. `flydsl.compiler.protocol` is an exception: every non-underscore `flydsl.compiler.protocol.` in its `__all__` is stable. It is the public extension namespace for user implementations of JIT / DSL-value protocols. This rule is otherwise not recursive: `flydsl.compiler..` does not become stable merely because `` is importable; it must be listed explicitly in §2.3. ### 2.3 Other explicitly stable APIs The following full paths are stable. This table is the only exception list; a new commitment must add an explicit row. | API | Description | |---|---| | `flydsl.runtime.device.get_rocm_arch` | Query the target ROCm architecture | | `flydsl.runtime.device.is_rdna_arch` | Choose between CDNA and RDNA paths | ### 2.4 All other APIs Every API that does not satisfy §2.1 or §2.2, and is not listed exactly in §2.3, is unstable. This includes undeclared `flydsl.*` submodules, `flydsl._mlir.*`, and every underscore-prefixed name. Direct invocation of an upstream MLIR dialect op is allowed but unstable: its name, arguments, and semantics are controlled by upstream MLIR, and FlyDSL provides no compatibility commitment for it. ### Stable API catalog Generate the complete non-deprecated stable API catalog from the checked-out source: ```bash python3 scripts/list_stable_apis.py python3 scripts/list_stable_apis.py --format json ``` The script reads export manifests without importing FlyDSL. It lists canonical module paths, omitting equivalent top-level `flydsl.expr` aliases. Deprecated APIs remain compatible but are intentionally excluded from this catalog. ## 3. Stable but deprecated The APIs in the table below satisfy the stable rules in §2 but are marked as deprecated. They remain stable during the §5 window; new code must not use them, and a replacement must be provided before removal. For direct-child module exports under §2.1, the deprecated status in this table also applies to `fx.`, `fx..`, and direct-import forms. This table is maintained separately as the compatibility-debt list and is excluded from the catalog above. | API | Replacement or required work before removal | Declared removal release | |---|---|---| | `fx.get` | `fx.get_().unpack()` or `IntTuple[].unpack()` | v0.4 | | `fx.index_cast` | `fx.Index(x)` | v0.4 | | `fx.constant_vector` | `Numeric` and `Vector` member functions | v0.4 | | `fx.tdm_ops` and content reached through this alias | `fx.rocdl.tdm_ops`; the latter is a target-specific unstable path | v0.4 | | `fx.Numeric.maximumf`, `fx.Numeric.minimumf` | `fx.arith.maximumf(x, y)`, `fx.arith.minimumf(x, y)` | v0.4 | | `fx.Numeric.shrui`, `fx.Numeric.addf` | `fx.arith.shrui(x, amount)`, `x + y` with a `fastmath` context | v0.4 | | `fx.Numeric.exp2` | `fx.math.exp2(x)` | v0.4 | | `fx.Numeric.shuffle_xor` | `fx.gpu.shuffle_xor(x, offset, width)` | v0.4 | ## 4. What counts as a breaking change For a stable API, each of the following is a breaking change: - removing a stable API or breaking its export chain: removing an entry from a direct-child module's `__all__`, `flydsl.compiler.__all__`, or `flydsl.compiler.protocol.__all__`; or removing a §2.3 entry without a new rule that covers it; - removing an argument, renaming an argument that may be passed by keyword, reordering positional arguments, removing a default, or changing a default; - narrowing accepted types, architectures, or value ranges; - changing a returned scalar or value type, tuple arity, or a returned object's stable public result interface (including non-private members and `__...__` Python special methods), or the numerical, layout, or emitted-op semantics for previously valid input. The following are not breaking changes: - adding an API, or adding an optional keyword argument whose default preserves existing behavior; - widening accepted input; - improving an error message, converting undefined behavior into a clear error, or changing the exception type on failure; - replacing a returned object's concrete implementation class while preserving its stable public result interface; - changing only unstable APIs. ## 5. Changing or retiring a stable API 1. Provide a replacement first, normally exposing it from an appropriate stable path. 2. Mark the original API as deprecated at its definition or export declaration, and add the API and replacement to §3. 3. Retain the old API in the release where it is marked, `N`, and in the next minor release, `N+1`. 4. Remove the old API no earlier than `N+2`, and remove its corresponding §3 entry at the same time.