---
name: core/import-boundaries
description: >-
  Which store modules components may import (actions, selectors, types,
  and the initialized Store instance for dispatch/one-shot reads) and which
  are forbidden (saga files, operation modules, reducer internals, collection
  utils). Component files must never import typed-redux-saga or redux-saga.
  Services/non-component TS may import actions and selectors;
  sagas may import anything inside the store directory.
type: sub-skill
requires:
  - core
  - core/core-policy
triggers:
  - import boundaries
  - saga import rule
  - dispatch subpath
  - forbidden imports
---
# Import Boundaries

> Source: `../SKILL.md` §2; `package.json` exports map; public `@augmentcode/themis/svelte-store`; hidden root-entry implementation context; `@augmentcode/themis/docs/ARCHITECTURE.md` maintainer validation.

## Setup — the package export surface

The documented public surface is the approved subpackage interface plus explicit utility leaf subpaths. Do not teach consumers to import from the package root, from removed broad utility barrels, or from source-shaped deep paths.

The utility exports are explicit leaf entries, not wildcard domains. The package still does not export the root `themis`, `@augmentcode/themis/utils`, `@augmentcode/themis/utils/runtime/*`, selector implementation internals (`utils/svelte-selectors/*`, `utils/streaming-selectors/*`, or `utils/selector-core/*`), old `themis/components/*` paths, `themis/src/*`, or directory `index` barrels.

The public subpackages relevant to Store-family import boundaries include:

- `@augmentcode/themis/svelte-store` — canonical Svelte-readable `Store` class. Per-store operations go through the configured Store instance (`store.init`, `store.dispatch`, `store.state`, `store.createSelector`, `store.runSaga`, `store.dispose`). Utility helpers are not exported here; use the explicit utility leaf subpaths below.
- `@augmentcode/themis/streaming-store` — direct `StreamingStore` leaf for Kefir/observable selectors. Do not import streaming selector internals directly.
- `@augmentcode/themis/saga` — saga authoring helpers: `waitFor`, selector-channel helpers, and debounce/retry/streaming helpers.
- `@augmentcode/themis/types` — public TypeScript-only types such as `StoreState`, `PreloadedStoreState`, action, middleware, selector, reducer, and saga map types.
- `@augmentcode/themis/components-svelte/use-init-store`, `/use-run-saga` — optional Svelte lifecycle helper leaves. Do not import from old `components/*` paths or from a `components-svelte` directory barrel.
- `@augmentcode/themis/utils/collections/collection-utils` — approved direct collection utility leaf.
- `@augmentcode/themis/utils/store/create-action`, `/create-reducer`, `/boolean-preference`, `/domain-scoped` — the only package-level store utility leaf imports.
- `@augmentcode/themis/utils/sagas/debounce-saga`, `/retry-with-timeout`, `/wrap-async-generator`, `/selector-channel-effects` — approved direct saga utility leaves. Safe localStorage helpers are example/app-local utilities, not package exports.

Migration note: replace flat package-root examples, removed utilities-subpackage imports, and source-shaped deep imports with one of the public subpackages or approved utility leaf subpaths above.

## Core Patterns

### Components and component-level modules — allowed

- ✅ Actions from `*-slice.ts`
- ✅ Selectors from `*-selectors.ts`
- ✅ Types from `*-types.ts`
- ✅ The app's existing initialized `Store` instance for dispatch and one-time reads in event handlers (`store.dispatch`, `store.state`)

### Components — forbidden

- ❌ Saga files (`sagas/*.ts`) — never import saga source into a component
- ❌ Operation/utility modules that dispatch internally
- ❌ Reducer internals or store init/setup modules
- ❌ Collection utils directly (access collections through selectors)
- ❌ `typed-redux-saga` or `redux-saga` — saga effect libraries have no meaning in a component; importing them at minimum bloats the bundle and often drags the saga middleware into client code

### Services and non-component TS

Services may import actions, selectors, and the app's existing initialized `Store` instance captured outside callbacks. They may **not** import sagas directly — call `store.dispatch(triggeringAction())` instead.

### Sagas

Sagas may import anything within the store directory (actions, selectors, other sagas, reducer internals, collection utils, app-local safe localStorage helpers, `typed-redux-saga` effects). This is the only layer where `yield*` of `typed-redux-saga` effects is allowed.

## Examples

### 1. Public package subpaths and utility leaf imports

```ts
import { Store } from "<selected Store family package>";
import type { StoreState } from "@augmentcode/themis/types";
import { createAction } from "@augmentcode/themis/utils/store/create-action";
import { retryWithTimeout } from "@augmentcode/themis/saga";
import type { Collection } from "@augmentcode/themis/utils/collections/collection-utils";

type Todo = { id: string; title: string };
export type PublicState = StoreState & { todos?: { items: Collection<Todo, "id"> } };
export const loadTodos = createAction("todos/load");
export const publicApi = { Store, retryWithTimeout, loadTodos };
```

### 2. Component imports actions, selectors, types, and the initialized Store instance

```ts
import { store } from "$lib/store";
import { selectItems, selectIsLoading } from "./slices/items/items-selectors";
import { removeItem } from "./slices/items/items-slice";
import type { Item } from "./slices/items/items-types";

export const items = selectItems();
export const loading = selectIsLoading();

export function handleDelete(item: Item) {
  const currentItems = selectItems.select(store.state);
  if (currentItems.length > 1) store.dispatch(removeItem(item.id));
}
```

### 3. Service/non-component TS uses one-shot reads and dispatch, not saga source

```ts
import { store } from "$lib/store";
import { selectQueuedJob } from "$lib/store/slices/jobs/jobs-selectors";
import { startJob } from "$lib/store/slices/jobs/jobs-slice";

export function startQueuedJob(jobId: string) {
  const queuedJob = selectQueuedJob.select(store.state, jobId);
  if (queuedJob) store.dispatch(startJob(jobId));
}
```

### 4. Saga layer may import saga effects, selectors, actions, and saga utilities

```ts
import { call, delay, put, takeLatest } from "typed-redux-saga";
import { loadJobs, refreshJobs } from "../jobs-slice";
import { selectQueuedJob } from "../jobs-selectors";

declare const api: { runJob(id: string): Promise<void>; refreshJobs(): Promise<void> };

export function* jobsSaga() {
  yield* takeLatest(loadJobs, function* loadQueuedJob(action: { payload: [string] }) {
    const job = yield* selectQueuedJob.effect(action.payload[0]);
    if (job) yield* call(api.runJob, job.id);
    yield* put(refreshJobs());
  });
  yield* takeLatest(refreshJobs, function* refreshJobsAfterIdle() {
    yield* delay(250);
    yield* call(api.refreshJobs);
  });
}
```

## Common Mistakes

### ❌ Importing saga files from components

Pulls saga side-effect code into the component bundle and invites direct saga invocation, which breaks the saga lifecycle.

```ts
// BAD: component-adjacent code imports saga source and bypasses Store.runSaga lifecycle.
import { fetchItemsSaga } from "./slices/items/sagas/items-saga";

export function loadFromComponent() {
  const task = fetchItemsSaga();
  task.next();
}
```

```ts
import { store } from "$lib/store";
import { fetchItems } from "./slices/items/items-slice";

export function loadFromComponent() {
  store.dispatch(fetchItems());
}
```

Source: `../SKILL.md` §2 · **Priority: HIGH**

### ❌ Hiding dispatch inside service helpers

Components and services should dispatch through the configured app `Store` instance or receive a dispatch function explicitly. A helper that captures a hidden module-level dispatch creates an unreviewable second boundary even when all imports type-check.

```ts
// BAD: callers cannot see which Store instance owns this side effect.
import { store } from "$lib/store";
import { addItem } from "$lib/store/slices/items/items-slice";

export function queueItemFromAnywhere(id: string) {
  setTimeout(() => store.dispatch(addItem(id)), 0);
}
```

```ts
import type { Store } from "<selected Store family package>";
import { addItem } from "$lib/store/slices/items/items-slice";

export function queueItem(store: Store, id: string) {
  setTimeout(() => store.dispatch(addItem(id)), 0);
}
```

Source: `../SKILL.md` §2, §7 · **Priority: MEDIUM**

### ❌ Importing from `typed-redux-saga` in a component file

`typed-redux-saga` effects (`call`, `put`, `select`, `takeEvery`, ...) only have meaning inside saga generators. Importing them in a component has no runtime benefit, bloats the client bundle, and in test environments drags the saga mock (`core/testing`) into non-saga files.

```ts
// BAD: component code creates an effect descriptor that no saga middleware will run.
import { put } from "typed-redux-saga";
import { triggerEffect } from "./slices/effects/effects-slice";

export function handleClick() {
  return put(triggerEffect());
}
```

```ts
import { store } from "$lib/store";
import { triggerEffect } from "./slices/effects/effects-slice";

export function handleClick() {
  store.dispatch(triggerEffect());
}
```

Source: `../SKILL.md` §2 (Components must never import saga files / redux-saga effects) · **Priority: HIGH**

### Examples retained/added

| # | Example | Kind |
| ---: | --- | --- |
| 1 | Public package subpaths and utility leaf imports | Good |
| 2 | Component imports actions/selectors/types/Store instance | Good |
| 3 | Service one-shot selector read plus dispatch | Good |
| 4 | Saga-layer imports and typed-redux-saga usage | Good |
| 5 | Component importing saga source and direct generator stepping | Bad |
| 6 | Correct action dispatch replacement for saga import | Good |
| 7 | Hidden module-level dispatch helper in service code | Bad |
| 8 | Explicit Store-parameter dispatch helper | Good |
| 9 | Component importing typed-redux-saga effect descriptors | Bad |
| 10 | Correct component Store dispatch replacement | Good |

### Cases covered

| Case | Examples |
| --- | --- |
| Current public package API | 1 |
| Component import allowlist and Store-first dispatch/read | 2, 6, 8, 10 |
| Service/non-component one-shot access | 3, 7, 8 |
| Saga-only imports/effects and public saga utilities | 4, 5, 9 |
| Realistic boundary mistakes not limited to missing APIs | 5, 7, 9 |

## See also

- `core/core-policy/SKILL.md` — the policy these boundaries enforce
- Selected Store family skill — Store dispatch + selector usage inside components

