Phase (e) of docs/DATA-ANALYSIS.md (§7.6) plus contingent possible values. Stacked on #8 → #6 → #5. Draft until those merge after RC.6.
Watches (data_analysis_watch) watch either a saved analysis or a table, filter and metric (COUNT, SUM or AVG of a field, the null share, or the category set) per HOUR, DAY, WEEK or MONTH bucket.
Detectors (pure functions):
SPIKE / DROP: robust z from the median and MAD of the last k buckets, falling back to relative change when MAD = 0;
OUT_OF_RANGE;
NEW_CATEGORY;
NULL_RATE_CREEP.
Each writes a plain message ("Orders per day dropped to 12 (usual 40–55).") and a drill on the latest bucket.
Evaluator: the hidden scheduled process data_analysis_evaluate_watches (every 15 minutes) evaluates each watch as its owner. Without an owner session, or without access, the watch is skipped with a message and never falls back to a system session.
On firing it calls QAlerts.raise, with type data.analysis.anomaly (core doesn't allow underscores in type names), declared as QAlertTypeMetaData.
On recovery it calls QAlerts.resolve.
Without the Alerts qbit, the watch row is the record.
Record actions: Evaluate now, Enable and Disable, as standard streamed-ETL Review/Result lines. POST /watches/{id}/preview returns the series with the baseline band and the verdict (extension 1.3.0).
Labels: pivot and crosstab show Yes/No for booleans and human measure labels.
Contingent fields (owner requirement):
data_analysis_watch.saved_id is filtered by ${input.table_name};
every table_name uses core's tables PVS;
analyzable_fields and excluded_fields pick from the chosen table's fields through a custom provider, in the qbit-quick-search resolveTable(otherValues) style. Storage stays comma-separated, since core has no multi-select PVS field yet.
Each field has a possibleValueSourceDependsOn TODO for when core adds it.
Test plan
./mvnw verify with Docker: green on memory, H2 and Postgres (~285 tests). JaCoCo ~97% instructions / 100% classes.
Detectors on hand-made series (MAD = 0, too little history); the evaluator with real rows raises and then resolves through a test AlertSinkInterface; owner session used; a user without READ is skipped with a message
Record actions' Review/Result lines; preview endpoint over HTTP
One test per contingent pair: saved_id filtered by table, the tables PVS lists the instance tables, the field picker lists only the chosen table's fields
## Summary
Phase (e) of `docs/DATA-ANALYSIS.md` (§7.6) plus contingent possible values. **Stacked on #8 → #6 → #5.** Draft until those merge after RC.6.
- **Watches** (`data_analysis_watch`) watch either a saved analysis or a table, filter and metric (COUNT, SUM or AVG of a field, the null share, or the category set) per HOUR, DAY, WEEK or MONTH bucket.
- **Detectors** (pure functions):
- SPIKE / DROP: robust z from the median and MAD of the last k buckets, falling back to relative change when MAD = 0;
- OUT_OF_RANGE;
- NEW_CATEGORY;
- NULL_RATE_CREEP.
Each writes a plain message ("Orders per day dropped to 12 (usual 40–55).") and a drill on the latest bucket.
- **Evaluator:** the hidden scheduled process `data_analysis_evaluate_watches` (every 15 minutes) evaluates each watch **as its owner**. Without an owner session, or without access, the watch is skipped with a message and never falls back to a system session.
- On firing it calls `QAlerts.raise`, with type `data.analysis.anomaly` (core doesn't allow underscores in type names), declared as `QAlertTypeMetaData`.
- On recovery it calls `QAlerts.resolve`.
- Without the Alerts qbit, the watch row is the record.
- **Record actions:** Evaluate now, Enable and Disable, as standard streamed-ETL Review/Result lines. `POST /watches/{id}/preview` returns the series with the baseline band and the verdict (extension 1.3.0).
- **Labels:** pivot and crosstab show Yes/No for booleans and human measure labels.
- **Contingent fields** (owner requirement):
- `data_analysis_watch.saved_id` is filtered by `${input.table_name}`;
- every `table_name` uses core's `tables` PVS;
- `analyzable_fields` and `excluded_fields` pick from the chosen table's fields through a custom provider, in the qbit-quick-search `resolveTable(otherValues)` style. Storage stays comma-separated, since core has no multi-select PVS field yet.
- Each field has a `possibleValueSourceDependsOn` TODO for when core adds it.
## Test plan
- [x] `./mvnw verify` with Docker: green on memory, H2 and Postgres (~285 tests). JaCoCo ~97% instructions / 100% classes.
- [x] Detectors on hand-made series (MAD = 0, too little history); the evaluator with real rows raises and then resolves through a test `AlertSinkInterface`; owner session used; a user without READ is skipped with a message
- [x] Record actions' Review/Result lines; preview endpoint over HTTP
- [x] One test per contingent pair: saved_id filtered by table, the tables PVS lists the instance tables, the field picker lists only the chosen table's fields
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Summary
Phase (e) of
docs/DATA-ANALYSIS.md(§7.6) plus contingent possible values. Stacked on #8 → #6 → #5. Draft until those merge after RC.6.data_analysis_watch) watch either a saved analysis or a table, filter and metric (COUNT, SUM or AVG of a field, the null share, or the category set) per HOUR, DAY, WEEK or MONTH bucket.Each writes a plain message ("Orders per day dropped to 12 (usual 40–55).") and a drill on the latest bucket.
data_analysis_evaluate_watches(every 15 minutes) evaluates each watch as its owner. Without an owner session, or without access, the watch is skipped with a message and never falls back to a system session.QAlerts.raise, with typedata.analysis.anomaly(core doesn't allow underscores in type names), declared asQAlertTypeMetaData.QAlerts.resolve.POST /watches/{id}/previewreturns the series with the baseline band and the verdict (extension 1.3.0).data_analysis_watch.saved_idis filtered by${input.table_name};table_nameuses core'stablesPVS;analyzable_fieldsandexcluded_fieldspick from the chosen table's fields through a custom provider, in the qbit-quick-searchresolveTable(otherValues)style. Storage stays comma-separated, since core has no multi-select PVS field yet.possibleValueSourceDependsOnTODO for when core adds it.Test plan
./mvnw verifywith Docker: green on memory, H2 and Postgres (~285 tests). JaCoCo ~97% instructions / 100% classes.AlertSinkInterface; owner session used; a user without READ is skipped with a message🤖 Generated with Claude Code