| name | core/wait-for |
| description | Concise agent rules for waitFor(selector, argsTuple, predicate, timeoutMs?). Use when a saga must pause until a named selector reaches a predicate. It checks the current value first, subscribes through createChannelFromSelector, returns true on match and false on timeout, and closes the channel in finally. |
| type | sub-skill |
| requires | ["core","core/sagas"] |
| triggers | ["waitFor selector","suspend saga","wait predicate","timeout wait"] |
waitFor — agent implementation rules
Use this skill for one-shot saga waits on selector state. Keep conceptual/API prose and examples in docs/WAITFOR.md; this skill should stay concise and operational.
Canonical references
- Human guide:
docs/WAITFOR.md
- Public API:
waitFor from @augmentcode/themis/saga.
- Channel helpers:
@augmentcode/themis/utils/sagas/selector-channel-effects or aggregate @augmentcode/themis/saga.
- Related skills:
core/sagas, core/selector-channels, core/testing, plus the selected Store family selector skill for family-specific selector authoring.
Use when
- A saga must wait for a selector value before continuing.
- The condition is one-shot: proceed after the predicate matches or timeout handling runs.
- The selector already exists as a named selector, authored with
store.createSelector(...) for app-local selectors when a configured Store exists.
Do
- Call as
waitFor(selector, argsTuple, predicate, timeoutMs?).
- Pass
[] for no-arg selectors and [arg1, arg2] for stable selector arguments.
- Prefer primitive scalar args in the tuple; pass object/function args only when
their identity is stable and intentional.
- Keep the predicate pure: no dispatches, API calls, mutations, timers, or state reads.
- Add a timeout for production waits unless an indefinite wait is explicitly safe.
- Check the boolean return when a timeout is supplied.
- Guard previous-value comparisons because the immediate check passes
undefined as prevVal, and the first selector-channel emission can pass null.
Don't
- Do not pass inline selectors or closures; create/reuse a named selector.
- Do not swap the args tuple and predicate.
- Do not ignore
false from timed waits.
- Do not use
waitFor for polling or continuous reactions; use takeLatestFromSelector, takeEveryFromSelector, or a selector channel instead.
- Do not duplicate the implementation's channel/race logic in app code.
- Do not put fresh object, array, or function literals in the args tuple; split to
scalar args or pass a stable intentional reference.
Implementation cues
- Import from
@augmentcode/themis/saga unless an existing app-local barrel already re-exports it.
- For boolean readiness, predicate on the desired final value rather than assuming truthiness.
- For status transitions, include the terminal states in the predicate and branch after
waitFor if different terminal outcomes matter.
- For timeout paths, dispatch or return an explicit timeout outcome before continuing with state-dependent work.
- If a saga needs every future selector change, route to
core/selector-channels instead.
Examples
1. Wait for a no-arg boolean selector and handle timeout
import { put } from "typed-redux-saga";
import { waitFor } from "@augmentcode/themis/saga";
function* loadWhenReady() {
const ready = yield* waitFor(selectIsReady, [], (value) => value === true, 5_000);
if (!ready) {
yield* put(loadTimedOut());
return;
}
yield* put(loadData());
}
2. Pass selector arguments as the required args tuple
import { waitFor } from "@augmentcode/themis/saga";
function* processItem(itemId: string) {
const ready = yield* waitFor(selectItemStatus, [itemId], (status) => status === "ready", 10_000);
if (ready) yield* startItemProcessing(itemId);
}
Use scalar args like itemId. Avoid args tuples such as [{ id: itemId }]
unless that object is a stable, intentional selector key reused across calls.
3. Guard previous-value comparisons on the immediate check
import { waitFor } from "@augmentcode/themis/saga";
function* waitForReconnect() {
const reconnected = yield* waitFor(
selectConnectionStatus,
[],
(value, prevVal) => prevVal === "connecting" && value === "connected",
15_000
);
return reconnected;
}
4. Branch after terminal-state waits
import { put } from "typed-redux-saga";
import { waitFor } from "@augmentcode/themis/saga";
function* waitForUpload(uploadId: string) {
const finished = yield* waitFor(selectUploadPhase, [uploadId], (phase) => phase === "done" || phase === "failed", 60_000);
if (!finished) return yield* put(uploadTimedOut(uploadId));
const phase = yield* selectUploadPhase.effect(uploadId);
if (phase === "failed") yield* put(uploadFailed(uploadId));
}
5. Use waitFor inside an action worker before state-dependent work
import { call, put } from "typed-redux-saga";
import { waitFor } from "@augmentcode/themis/saga";
function* publishAfterAuthWorker(action: ReturnType<typeof publishDocument>) {
const authenticated = yield* waitFor(selectAuthReady, [], Boolean, 5_000);
if (!authenticated) return yield* put(publishRejected(action.payload[0], "auth-timeout"));
yield* call(api.publishDocument, action.payload[0]);
}
6. ❌ Bad: ignoring false and treating prevVal as present on the first check
function* publishAfterAnyStatusChange(documentId: string) {
yield* waitFor(selectDocumentStatus, [documentId], (value, prevVal) => value !== prevVal, 5_000);
yield* call(api.publishDocument, documentId);
}
function* publishAfterRealStatusChange(documentId: string) {
const changed = yield* waitFor(
selectDocumentStatus,
[documentId],
(value, prevVal) => prevVal !== undefined && prevVal !== null && value !== prevVal,
5_000
);
if (changed) yield* call(api.publishDocument, documentId);
}
Verification cues
- Test immediate-match, later-match, and timeout paths when changing
waitFor usage.
- In saga tests, provide selector
.effect(...) values and channel/timer effects through existing test helpers.
- Run the smallest relevant saga tests when docs/skills examples change.
Common mistakes to prevent
waitFor(selectReady, predicate) — missing [] args tuple.
waitFor(selectItem, [{ id }], predicate) — fresh object selector arg.
- Acting after a timed wait without checking the returned boolean.
- Predicate change detection that treats initial
prevVal === undefined or first-emission prevVal === null as a real transition.
- Replacing one-shot waits with hand-rolled selector channels.
See also
docs/WAITFOR.md — full signature, behavior walkthrough, and examples.
docs/SAGAS.md — selector-channel and saga orchestration context.
core/selector-channels — continuous selector watchers.
- Selected Store family selector lifecycle skill — selector call modes.