> For the complete documentation index, see [llms.txt](https://v2.dataos.info/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://v2.dataos.info/references/dataos-resources/vulcan/core-concepts/signals.md).

# Signals

Vulcan's scheduler decides when to run a model from its `cron` schedule and whether upstream dependencies have finished. But real data does not always follow schedules: upstream systems lag, batch jobs run late. When that happens, a daily model might already have run for the day, and the late data waits until tomorrow.

Signals solve this by adding custom conditions that must be met before a model runs. A signal is an extra gate the scheduler checks, beyond "has enough time passed?" and "are upstream dependencies done?"

## What a signal is

The scheduler evaluates a model over specific time intervals, not the model as a whole. For incremental models that is a date range; for `FULL` and `VIEW` models the `cron` frequency determines the interval. The scheduler groups candidate intervals into batches (controlled by `batch_size`) and checks signals to see if those batches are ready.

A signal is a Python function that receives a batch of time intervals and returns one of:

* `True` if all intervals in the batch are ready.
* `False` if none are ready.
* A list of specific intervals if only some are ready.

{% hint style="info" %}
**One model, multiple signals.** You can attach several signals to a model. Vulcan requires that all of them agree an interval is ready before evaluating it, like an AND gate.
{% endhint %}

## Defining a signal

Add a `signals/` directory and define your function in `__init__.py` (you can split signals across multiple files). A signal function accepts a batch of intervals (`DatetimeRanges`), returns a boolean or a list of intervals, and uses the `@signal` decorator.

A simple example (random, for testing only):

```python
import random
import typing as t
from vulcan import signal, DatetimeRanges

@signal()
def random_signal(batch: DatetimeRanges, threshold: float) -> t.Union[bool, DatetimeRanges]:
    return random.random() > threshold
```

Attach it through the `signals` key in the `MODEL` block, passing arguments. Argument typing works the same way as typed [macros](/references/dataos-resources/vulcan/core-concepts/macros.md):

```sql
MODEL (
  name example.signal_model,
  kind FULL,
  signals (
    random_signal(threshold := 0.5),
  )
);

SELECT 1
```

## Returning specific intervals

For finer control, return only the intervals that are ready. This signal allows only intervals from at least a week ago, which is useful for waiting until late-arriving data has stabilized:

```python
import typing as t
from vulcan import signal, DatetimeRanges
from vulcan.utils.date import to_datetime

@signal()
def one_week_ago(batch: DatetimeRanges) -> t.Union[bool, DatetimeRanges]:
    dt = to_datetime("1 week ago")
    return [
        (start, end)
        for start, end in batch
        if start <= dt
    ]
```

```sql
MODEL (
  name example.signal_model,
  kind INCREMENTAL_BY_TIME_RANGE (
    time_column ds,
  ),
  start '2 week ago',
  signals (
    one_week_ago(),
  )
);

SELECT @start_ds AS ds
```

## Accessing the execution context

To check your database or read execution context, add a `context: ExecutionContext` parameter. It gives access to the engine adapter, so a signal can query the warehouse, check whether a table or partition exists, or verify data freshness:

```python
import typing as t
from vulcan import signal, DatetimeRanges, ExecutionContext

@signal()
def upstream_ready(batch: DatetimeRanges, context: ExecutionContext) -> t.Union[bool, DatetimeRanges]:
    return len(context.engine_adapter.fetchdf("SELECT 1")) > 1
```

## Testing signals

Signals evaluate only during `vulcan run` or the `check_intervals` command. To test signal logic without running models:

1. Deploy your changes to an environment: `vulcan plan my_dev`.
2. Check which intervals would be evaluated: `vulcan check_intervals my_dev`. Use `--select-model` to scope it, and `--no-signals` to see what would run without signal checks.
3. Iterate by changing the signal and redeploying.

`check_intervals` works only with models deployed to an environment, so local signal changes are not tested until you deploy them.

## Related docs

* [Macros](/references/dataos-resources/vulcan/core-concepts/macros.md): the typed-argument pattern signals share.
* [Observability](/references/dataos-resources/vulcan/observability.md): how `vulcan run` respects schedules and signals.
* [Model and kinds](/references/dataos-resources/vulcan/core-concepts/model-and-kinds.md): how intervals and `batch_size` define what a signal gates.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://v2.dataos.info/references/dataos-resources/vulcan/core-concepts/signals.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
