You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
- Rename internals.ts to experimental.ts with updated header comment
- Rename docs/content/docs/internals.md to experimental.md with updated content
- Update package.json exports from ./internals to ./experimental
- Update sidebar link from Internals to Experimental
- Rewrite P14 in PHILOSOPHY.md as 'Experimental carries no promise'
- Added promotion criteria: generic (no backend deps), used in 2/3 apps, stable API
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
**Rule:** Code reached through the `frappe-ui/internals` subpath is private and **exempt from P13**. It can change shape or be removed in any release — including minor/patch — with no deprecation window. Use it from first-party Frappe libraries without expecting stability.
457
+
**Rule:** Code reached through the `frappe-ui/experimental` subpath is private and **exempt from P13**. It can change shape or be removed in any release — including minor/patch — with no deprecation window. Use it from first-party Frappe libraries without expecting stability.
458
458
459
-
**Why:** First-party libraries need to reuse internal building blocks, composables like `useInputLabeling`, class helpers, headless logic — without every one being promoted to the public API and frozen under P13. `internals` is that escape hatch: the framework gets to consume internals while the *public* surface stays small and the cost of evolving internals stays zero. The alternative, re-exporting each helper from the public API, or opening a `./src/*` wildcard either freezes everything under P13 or exposes everything forever. `internals` is the deliberate middle: a small, curated, explicitly-unstable surface.
459
+
**What lives here:** two kinds of export. (1) *Internal building blocks* — composables, class helpers, and headless logic that frappe-ui's own components share (`useInputLabeling`, `inputFontSizeClasses`, the input-labeling components). (2) *New components* whose shape is still being discovered in real apps. Both are unstable on purpose: an internal because freezing it would freeze the components built on it, a new component because its API hasn't earned a stable contract yet.
460
+
461
+
**Why:** First-party libraries need to reuse these without every one being promoted to the public API and frozen under P13. `experimental` is that escape hatch: the framework gets to consume and iterate on building blocks and new components while the *public* surface stays small and the cost of evolving them stays zero. The alternative — re-exporting each helper from the public API, or opening a `./src/*` wildcard — either freezes everything under P13 or exposes everything forever. `experimental` is the deliberate middle: a small, curated, explicitly-unstable surface.
460
462
461
463
**Mechanics:**
462
-
1. Exposed through a single curated barrel (`internals.ts`) behind the `./internals` export **not** a `./src/*` wildcard. Re-export only what a first-party consumer actually needs.
464
+
1. Exposed through a single curated barrel (`experimental.ts`) behind the `./experimental` export **not** a `./src/*` wildcard. Re-export only what a first-party consumer actually needs.
463
465
2. The barrel header restates the no-promise contract at the point of use.
464
-
3. "Private" is by convention, `exports` can't scope visibility to a specific consumer so the contract is the disclaimer, not enforcement. Product/third-party code is told not to import it.
465
-
4. To make an internal stable, deliberately promote it to a public entry point (and thus under P13). Until then, no guarantees.
466
+
3. "Private" is by convention, `exports` can't scope visibility to a specific consumer so the contract is the disclaimer, not enforcement. Product/third-party code is told not to import it; if a product needs one of these and wants stability, it copies the code into its own app rather than depending on this subpath.
467
+
4. To make an experimental export stable, deliberately promote it to a public entry point (and thus under P13). Until then, no guarantees.
468
+
469
+
**Promotion — moving a component out of `experimental` into the core public API.** A component graduates only when **all** of these hold:
470
+
1.**Generic, not backend-dependent.** It solves a UI problem in the abstract — no coupling to a specific DocType, API shape, or app-specific data model. If it only makes sense inside one product, it stays in that product.
471
+
2.**Proven across apps.** It is actually used in **two or three** of the Frappe apps (Helpdesk, CRM, Gameplan, Drive, …). Real reuse — not anticipated reuse — is the evidence that the abstraction holds.
472
+
3.**Stable API.** Its props, events, slots, and behavior have settled; you no longer expect to reshape them. Promotion is the moment the API freezes under P13, so it must be ready to carry that promise.
473
+
474
+
Until a component clears all three, it lives in `experimental` and any consumer that needs guarantees keeps its own copy.
0 commit comments