# Thresholds, scopes and overrides

URL: /rules/configure

Create a rule, change a rule's threshold, scope or override on the Rules page, or switch a rule on or off. Each saved change becomes a new rule version, and findings already produced keep theirs.



A rule's threshold decides what counts as a finding, and the right value depends on your vehicles. You change it on the **Rules** page. Each saved change becomes the next rule version, so findings already produced keep the version and the threshold that made them.

## Before you start [#before-you-start]

* You need the **Admin** role. **Change detection rules** is an admin permission in the roles matrix.
* Decide the new value from runs you trust. A vibration threshold for a tracked vehicle, for example, is a program choice set from known-good runs.
* Write the threshold as text, with its units. The field is free text, with no separate number or unit inputs.

## Read the Rules table [#read-the-rules-table]

Open **Rules** in the sidebar, under **Data**. The table lists every rule in these columns:

| Column               | What it shows                                                                                                       |
| -------------------- | ------------------------------------------------------------------------------------------------------------------- |
| **Rule**             | The icon of the source the rule reads, the name, and the rule ID and version underneath, such as `process_exit 1.2` |
| **Detects**          | What the rule looks for                                                                                             |
| **Threshold**        | The threshold, with the scope underneath                                                                            |
| **Results, 30 days** | How many results the rule produced in the last 30 days                                                              |
| **Enabled**          | A switch that turns the rule on or off                                                                              |

Select a row to open the rule's drawer, titled with its name and subtitled with its ID and version. It gives **Detection**, with the source the rule reads; **Threshold**, with **Applies to** and any **Override**; **Report wording**, the fixed wording every finding of the rule uses; and **Results**, with the count for the last 30 days and whether the rule is enabled. **Edit** sits at its foot. A note under the table explains versions under the heading &#x2A;*Rules observe. They never conclude.**

## Change a threshold, scope or override [#change-a-threshold-scope-or-override]

<Steps>
  <Step title="Open the rule's editor">
    Select the rule's row, then **Edit** at the foot of its drawer. The dialog **Edit** *rule name* opens, with the rule's **Detects** text as its subtitle. At the bottom left, **Saves as** shows the version the change will create, for example `memory_pressure 1.2`.
  </Step>

  <Step title="Change the threshold">
    **Threshold** is a single free-text field. Write the value with its units, the way the table shows it:

    ```text title="Threshold" caption="Example value, memory_pressure 1.1"
    RSS at or above 6,144 MiB for 3 samples
    ```

    The hint under the field reads "Results already produced keep version 1.1 and its threshold."
  </Step>

  <Step title="Set the scope">
    **Applies to** names what the rule covers, in free text. The example dataset uses **All vehicles**, **Q4 vehicles**, **T4 vehicles** and **All sources**.
  </Step>

  <Step title="Adjust the override, if the rule has one">
    **Override** appears only on a rule that has one. In the example dataset that is `memory_pressure`, whose override reads **T4 vehicles: 24,576 MiB**. `vibration_high` keeps its per-platform values inside the threshold text instead.
  </Step>

  <Step title="Check the wording and save">
    **Report wording** is read-only. Its hint reads "Fixed per rule, so every report describes the same detection the same way."

    Select **Save**. The toast confirms the new version, for example **Memory pressure saved as version 1.2**. The drawer closes and the table redraws.
  </Step>
</Steps>

A change to the scope or the override alone also saves a new version. If no field differs from the saved rule, the toast reads **Nothing changed** and the dialog closes without one.

## Create a rule [#create-a-rule]

<Steps>
  <Step title="Open New rule">
    Select **New rule** at the top right of the **Rules** page. The dialog's note: "A deterministic detection. It runs on every import from now on and never changes results already produced." At the bottom left it reads **Starts at version 1.0**.
  </Step>

  <Step title="Name it and say what it reads">
    Enter a **Name**, such as **Low pack voltage**. **Rule ID** fills in from the name, such as `low_pack_voltage`, and you can change it: "Lowercase, used in reports and exports."

    Under **Reads from**, pick the source: **Flight log**, **Companion journal**, **Process samples** or **Ground receive log**. **Signal** then asks for what the rule reads in that source:

    | Reads from             | Signal, as the hint gives it                                           |
    | ---------------------- | ---------------------------------------------------------------------- |
    | **Flight log**         | A uORB topic and field, for example `battery_status.voltage_v`         |
    | **Companion journal**  | A systemd unit or a kernel message, for example `perception.service`   |
    | **Process samples**    | A watched process and a counter, for example `perception_node rss_kib` |
    | **Ground receive log** | A MAVLink message from a system ID, for example `HEARTBEAT`            |
  </Step>

  <Step title="Set the threshold and the wording">
    Write **Threshold** as text with its units, such as `Below 21.0 V for 2 s`, and choose **Applies to**: **All vehicles**, **T4 vehicles** or **Q4 vehicles**. Say in **What it detects** what the rule looks for. **Report wording** starts with a fixed `Observed:`, and you write the rest, because "Rules observe. What an engineer concludes is written in the report, not here."
  </Step>

  <Step title="Create it">
    Select **Create rule**. The toast reads, for example, &#x2A;*Low pack voltage created. It runs on the next import.** The rule joins the table, enabled, and the audit log records **Created rule** with `Version 1.0` and the threshold. An ID already in use is refused. The example dataset already has `battery_sag`, so a new rule with that ID reads **A rule with the ID battery\_sag already exists**.
  </Step>
</Steps>

## Rules suggested from investigations [#rules-suggested-from-investigations]

An investigation often ends in a pattern that no single rule catches: a disconnect a second after a vibration spike, a telemetry gap after memory pressure. The [AI models](/concepts/ai-models) read the findings of every incident for two rules that fired in order on one vehicle, and propose a rule for the pair. The panel **Suggested from investigations** sits above the rules table while a suggestion is waiting: "Two rules that fired in order on one vehicle during an incident. A suggestion runs nowhere until an admin creates it."

| Suggestion                              | Fires when                                                                       | From                    | Backing                         |
| --------------------------------------- | -------------------------------------------------------------------------------- | ----------------------- | ------------------------------- |
| **Disconnect after vibration**          | `device_disconnect` within 3 s after `vibration_high`, same vehicle. T4 vehicles | INC-0143, 1.692 s apart | Both fired on 4 runs in 90 days |
| **Telemetry gap after memory pressure** | `telemetry_gap` within 10 s after `memory_pressure`, same vehicle. Q4 vehicles   | INC-0142, 3.708 s apart | Both fired on 2 runs in 90 days |
| **Failsafe after a telemetry gap**      | `failsafe_entered` within 15 s after `telemetry_gap`, same vehicle. All vehicles | INC-0143, 10.58 s apart | Both fired on 3 runs in 90 days |

The time apart is measured on the incident, and the runs are counted from the last 90 days. **Review** opens the suggestion with **Fires when**, **Applies to**, **Report wording**, **Why it was suggested** and **What backs it**, which lists the runs where both rules fired. Whether they fell inside the window on each run is checked when the rule is created. **Create rule** adds it at version 1.0, enabled, running on the next import: Foxborne confirms with &#x2A;*Disconnect after vibration created. It runs on the next import.** Its drawer says who created it and from which incident.

**Dismiss** removes the suggestion and confirms with **Suggestion dismissed**. The audit log records **Created rule** with **Version 1.0, from a suggestion on INC-0143**, or **Dismissed a rule suggestion**.

A suggested rule reads rule results only, so it adds no parsing and needs no new data. Like every rule, it observes and never concludes.

## Switch a rule on or off [#switch-a-rule-on-or-off]

Select the **Enabled** switch on the rule's row.

* The change takes effect at once, without asking for confirmation.
* The toast states when it applies, for example &#x2A;*Battery sag enabled. Applies to the next import.**
* The rule keeps its version. Only a saved edit creates a new one.
* `clock_uncertain` cannot be switched. Its switch is disabled, with the tooltip "Always on. Every ordering claim depends on it."

## Check the result [#check-the-result]

* **Rules table.** The row shows the new version under the name, with the new threshold, scope or override.
* **Audit log, after a save.** The entry reads **Changed rule**, with the rule ID as the object. Its detail gives the new version and threshold, such as `Version 1.2: RSS at or above 6,144 MiB for 3 samples`.
* **Audit log, after a switch.** The entry reads **Enabled rule** or **Disabled rule**, with the unchanged version as the detail. Both kinds of entry appear under the **Administration** filter.
* **Incidents.** Findings already produced keep their version and threshold. Runs imported after the change use the new version.

## Troubleshoot [#troubleshoot]

**The toast reads Nothing changed.** No field differs from the saved rule, so no version was created. Change the threshold, the scope or the override, then save again.

**Clock uncertain has no Edit, and its switch is disabled.** The rule is locked, because every ordering claim depends on it. Hold the pointer over the switch to read the tooltip.

**An incident still shows the old threshold.** Findings keep the version that made them. **Run rules again** on the run page reproduces them with the same versions.

**A rule you switched on shows no findings on older runs.** A switch applies to the next import. Runs imported earlier keep the findings they already have.

**The version went from 0.9 to 0.10.** The console raises the number after the dot by one, so 0.10 follows 0.9. It does not round up to 1.0.

## Next [#next]

* [How rules work](/rules) explains versions, scopes, the locked rule and dismissals.
* [Audit log](/security/audit-log) shows every rule change with its version.
* [Roles and permissions](/security/roles) lists who can change detection rules.
* [Memory pressure](/rules/memory-pressure) shows a rule with an override in use.
