Repository-owned kernel boundary
Status: Accepted
Context
Section titled “Context”Components intentionally exposes React and browser DOM contracts at its public component surface. That does not make React or the DOM appropriate dependencies for every internal computation. Layout, filtering, selection, coercion, schema paths, and conversation transforms are easier to test and reuse when they remain plain TypeScript.
A folder name cannot establish this boundary. Components has mixed directories where a pure helper sits next to React components, browser focus behavior, pointer normalization, animation-frame scheduling, Web Workers, or Pixi rendering. Classifying a whole mixed folder would either reject valid UI code or quietly allow kernel code to acquire UI dependencies.
The boundary therefore needs a repository-owned inventory of verified modules, a source rule, and an emitted-graph assertion that follows each module’s transitive runtime and declaration dependencies.
Decision
Section titled “Decision”Components maintains one canonical kernel inventory in ESLint/lib/kernelBoundary.js. The root ESLint config enables @cratis/components/no-react-in-kernel only for those paths. The package-graph gate derives emitted .js and .d.ts entries from the same inventory and walks both closures.
A declared kernel module must not:
- import, require, dynamically import, or re-export
react,react-dom,react-aria-components, or any subpath of those packages; - reference browser DOM globals, including DOM element/event types and browser runtime services;
- reach one of those package dependencies through its emitted runtime or declaration closure; or
- reach a browser DOM global through its emitted runtime or declaration closure.
This boundary is separate from the existing Pixi, renderer-vendor, renderer export, and private renderer/coreSlots package-graph assertions. Those rules remain unchanged.
Included modules
Section titled “Included modules”The accepted kernel inventory is exact. A directory not listed here is not implicitly included.
PivotViewer engine
Section titled “PivotViewer engine”Source/PivotViewer/constants.tsSource/PivotViewer/engine/layout.tsSource/PivotViewer/engine/requestCorrelator.tsSource/PivotViewer/engine/store.tsSource/PivotViewer/engine/types.ts
Canvas computation
Section titled “Canvas computation”Source/Canvas/canvasGesture.tsSource/Canvas/canvasTransformActivity.tsSource/Canvas/panMomentum.tsSource/Canvas/pinchGesture.tsSource/Canvas/shapes/Region/regionContainment.ts
Filter state and histogram computation
Section titled “Filter state and histogram computation”Source/Filter/types.tsSource/Filter/utils.ts
DataTables paging, filtering, and selection
Section titled “DataTables paging, filtering, and selection”Source/DataTables/DataTableFilterMatcherRegistry.tsSource/DataTables/DataTableFilterMeta.tsSource/DataTables/paginatorRange.tsSource/DataTables/selectionKeys.ts
Command transforms and value mapping
Section titled “Command transforms and value mapping”Source/CommandDialog/applyBeforeExecute.tsSource/CommandForm/commandFormMarkers.tsSource/CommandForm/fields/chipValues.tsSource/CommandForm/fields/fieldValueFromEvent.ts
Schema paths and validation
Section titled “Schema paths and validation”Source/SchemaEditor/schemaHelpers.ts
Chat reducers and mention segmentation
Section titled “Chat reducers and mention segmentation”Source/Chat/isTopicUnnamed.tsSource/Chat/shouldRequestTopicName.tsSource/Chat/topicsByActivity.tsSource/Chat/Kit/findOwnReaction.tsSource/Chat/Kit/reactionsExcludingUser.tsSource/Chat/Kit/Mentions/MentionCandidate.tsSource/Chat/Kit/Mentions/MentionQuery.tsSource/Chat/Kit/Mentions/activeMentionQuery.tsSource/Chat/Kit/Mentions/applyMention.tsSource/Chat/Kit/Mentions/extractMentions.tsSource/Chat/Kit/Mentions/findMentionRanges.tsSource/Chat/Kit/Mentions/matchCandidates.tsSource/Chat/Kit/Mentions/mentionSegments.ts
Explicit exclusions
Section titled “Explicit exclusions”The initial inventory deliberately excludes candidates that are not React-free and DOM-free today:
Source/PivotViewer/engine/pivot.worker.tsuses the Web Workerselfand message-event runtime. PivotViewer components, hooks, Pixi sprite/animation code, and environment observers remain outside the kernel.- Canvas focus and selection guards, pointer-target and scrollable-content normalization,
zoomMechanism.ts,selfSuspendingFrameLoop.ts,noteFont.ts, every React hook, every TSX component, and Pixi-backed code remain outside.Canvas/*.tsis not a safe folder-wide pattern. Source/Filter/useFilterState.tsis a React hook.FilterEditorProps.tscarries the DOM-aware publicChangeHandlermetadata contract. Filter editors and histogram UI components remain outside; only their state and histogram computations are included.Source/DataTables/DataTableSelectionChangeEvent.tscarries React’sSyntheticEvent. Table components, keyboard/focus behavior, and renderer adapters remain outside.Source/CommandDialog/stepChildren.tsuses React’s runtime child traversal. Dialog, stepper, focus, portal, and component modules remain outside.Source/CommandForm/FieldTypeProvider.tsexposes ReactComponentType, sofieldTypeProviderRegistry.tsreaches React through its declaration closure. Default providers and every field component also remain outside. The initial inventory includes only marker, coercion, and validation-mapping helpers with clean closures.- Notifications are not included.
toast.tsexposes React nodes and browser button types, while timeout scheduling lives inToaster.tsx; there is no independent React-free queue contract behind an existing clock boundary to declare yet. - SchemaEditor React cells and editor composition remain outside; only
schemaHelpers.tsis included. - Chat components, hooks, anchored overlays, focus/portal behavior, and React-valued action descriptors remain outside.
FailedReplyis a React component and the current source has no independent pure retry reducer to declare.
An excluded module receives no broad allowlist. It can enter the inventory only after its dependency closure is genuinely clean, using a type-only dependency or an explicit port at the UI boundary without changing behavior.
Ratchet policy
Section titled “Ratchet policy”The inventory is grow-never-shrink. New verified computation modules should be added. An existing entry must not be removed merely to make a new React or DOM dependency pass. Extract the dependency behind a UI-owned port or keep the new behavior outside the kernel.
A source rename or deletion may update the literal path, but it must preserve or increase the protected architectural surface. Any exceptional reduction requires a superseding architecture decision that explains why the module is no longer kernel code; a routine lint suppression or package-graph allowlist is not acceptable.
Consequences
Section titled “Consequences”Kernel computations receive an immediate source diagnostic when React or browser behavior crosses the boundary. The packed plugin carries the same rule implementation, while the Components root config owns activation and scope.
The package graph catches transitive and declaration-only React and browser-DOM edges that a direct-import rule cannot see. It checks both emitted runtime references and declaration types, so compilation or bundling cannot silently introduce a platform dependency.
Mixed folders remain mixed. This decision creates no source moves, renderer facades, component behavior changes, or consumer contract changes. The inventory records only boundaries the current source already satisfies.