Skip to content

test(data): spike check for collab activity vs per-user trailing baseline (#1321)#1355

Open
SharedQA wants to merge 6 commits into
constructorfabric:mainfrom
SharedQA:claude/dbt-collab-activity-anomaly
Open

test(data): spike check for collab activity vs per-user trailing baseline (#1321)#1355
SharedQA wants to merge 6 commits into
constructorfabric:mainfrom
SharedQA:claude/dbt-collab-activity-anomaly

Conversation

@SharedQA

@SharedQA SharedQA commented Jun 17, 2026

Copy link
Copy Markdown
Contributor

Follow-up to the silver non-negative business-rule test. It guards the "too high" case the non-negative check can't see: a count that looks fine on its own but is wildly out of line with the same user's own recent history — the kind of jump a feeder double-count or a unit change produces.

Not a fixed limit. The baseline is computed per (tenant, user, product, metric) from each series' own trailing 28 observed days (excluding the current day). A user-day is flagged only when all of these hold:

  • at least 14 prior observations exist (cold-start guard)
  • the baseline has non-zero spread (avoids divide-by-noise on flat series)
  • value > mean + 5*stddev (z-score outlier)
  • value > 10*mean (massive relative jump)

The z-score and the relative-jump condition together keep it to genuinely large spikes, so it stays quiet on normal variation.

Safe by construction. It joins the data_quality catalog as severity=warn + store_failures — advisory and monitored, never blocks a build. Reads FINAL.

Why draft. The thresholds (28-day window, 14-day minimum, 5σ, 10×) are conservative starting points, not yet calibrated against real tenant data — there's no warehouse available locally to run dbt. The intent is to tune them from the first scheduled-run findings before marking ready. Review welcome on the method and the threshold choices.

Summary by CodeRabbit

  • New Features
    • Added automated anomaly detection for collaboration document activity spikes. The system continuously monitors activity patterns and generates real-time alerts when unusual increases are detected in collaboration metrics. This provides visibility into potential anomalies across your workspace, enabling proactive oversight of collaboration activities, usage patterns, and team productivity metrics.

…line (constructorfabric#1321)

Follow-up to the non-negative business-rule test. Adds a data_quality catalog
check for the "too high" case the non-negative test cannot catch: a count that
is plausible on its own but wildly out of line with the same user's own recent
history (e.g. a feeder double-count turning a few hundred views into millions).

Deliberately not a fixed upper limit. The baseline is derived per
(tenant, user, product, metric) from each series' own trailing 28 observed days
(excluding the current day). A day is flagged only when all hold: >=14 prior
observations (cold-start guard), non-zero baseline spread, value > mean + 5*sd,
and value > 10*mean. The z-score and the relative-jump condition together keep
it to genuinely large spikes.

severity=warn + store_failures (advisory, monitored, non-blocking) per the
data_quality convention, so a noisy or mis-tuned threshold can never fail a
build while we calibrate it. Reads FINAL.

Note: thresholds are conservative starting points, not yet calibrated against
real tenant data (no warehouse available locally to run dbt). They are meant to
be tuned from the first scheduled-run findings.

Signed-off-by: Kenan Salim <kenan.salim@gmail.com>
…onstructorfabric#1321)

Addresses two review points on the trailing-baseline spike check:

- Performance: cap the input to the last 120 days (new `recent` CTE) and the
  output to the last 3 days. Filtering only the output would stop re-alerting on
  old spikes but the window function still sorts/scans all history; bounding the
  input is what actually keeps the cost flat as history grows. 120 days
  comfortably covers a 28-observed-day baseline for any active user; a user with
  <14 active days in the window isn't evaluated (the prior_n guard).
- Stale baseline: the window is ROWS-based (observed days, not calendar days),
  so a user returning from a long leave would be compared against a months-old
  baseline. Add `date - prev_date <= 35` (prev_date = most recent prior observed
  day) so the baseline must itself be recent, removing that false-positive class.

Still advisory (severity=warn) and uncalibrated; thresholds to be tuned from the
first scheduled-run findings.

Signed-off-by: Kenan Salim <kenan.salim@gmail.com>
@SharedQA

Copy link
Copy Markdown
Contributor Author

Good catches — applied both, with one adjustment on the performance fix.

1. Full-table-scan / re-alerting. Filtering only the final SELECT does stop re-alerting on old spikes, but the window function still has to sort and scan the whole history first, so the compute cost you flagged doesn't actually go away. So I bounded both ends:

  • input capped to the last 120 days (a new recent CTE) — a 28-observed-day baseline for any active user fits inside that, so the window sort never grows with total history;
  • output capped to date >= today() - 3 — the scheduled daily run only reports newly-appearing spikes.

A user with fewer than 14 active days inside the 120-day window simply isn't evaluated, which the prior_n >= 14 guard already enforces.

2. Observed vs calendar days. You're right that ROWS means observed days, and the return-from-leave case is the real risk. Rather than leave it as a documented caveat, I added a staleness guard: date - prev_date <= 35, where prev_date is the most recent prior observed day. So a baseline that is itself months old won't be used, which removes that false-positive class. I kept the ROWS window (vs ClickHouse's clunky RANGE BETWEEN INTERVAL) — the guard gives the calendar-recency property without the syntax pain.

Still severity=warn and uncalibrated; the 120/3/35-day and 5σ/10× values are starting points to tune from the first scheduled-run findings.

@coderabbitai

coderabbitai Bot commented Jun 17, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@SharedQA, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 56 minutes and 52 seconds. Learn how PR review limits work.

To continue reviewing without waiting, enable usage-based billing in the billing tab.

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based credits.

🚦 How do rate limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan refill rate.

For paid Pro and Pro+ PR reviews, CodeRabbit uses rolling per-developer review limits. Reviews become available again as older review attempts age out of the rolling limit window.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 2559c6ed-323c-4ef3-b9c2-959600ab8d14

📥 Commits

Reviewing files that changed from the base of the PR and between 09e8474 and e1d4575.

📒 Files selected for processing (1)
  • src/ingestion/dbt/tests/collaboration/assert_collab_document_counts_no_spike.sql
📝 Walkthrough

Walkthrough

A new dbt SQL test assert_collab_document_counts_no_spike.sql is added. It queries class_collab_document_activity over a 120-day window, unpivots five activity counters into metric rows, computes a 28-day trailing baseline (mean, stddev), and emits warn-severity findings for values exceeding both a z-score and a 10× relative threshold within the last 3 days.

Changes

Collaboration Document Spike Detection Test

Layer / File(s) Summary
Test config and methodology docs
src/ingestion/dbt/tests/collaboration/assert_collab_document_counts_no_spike.sql
dbt config block sets tags, severity = 'warn', store_failures = true, and descriptive metadata; SQL comments document the trailing-window spike-detection approach and advisory behavior.
Data scoping and unpivot CTEs
src/ingestion/dbt/tests/collaboration/assert_collab_document_counts_no_spike.sql
recent CTE limits source data to the last 120 days using FINAL; unpivoted CTE array-joins five activity counter columns into (metric, value) rows, dropping nulls.
Baseline stats and spike filter
src/ingestion/dbt/tests/collaboration/assert_collab_document_counts_no_spike.sql
baselined CTE applies window functions per (tenant_id, person_key, product, data_source, metric) to compute prior_n, base_mean, base_sd, and prev_date over the 28 prior observed rows; final SELECT restricts to dates in the last 3 days and applies four qualifying conditions: prior_n ≥ 14, base_sd > 0, date − prev_date ≤ 35, and value exceeding both base_mean + 5 × base_sd and base_mean × 10.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐇 Hop, hop, through the data stream,
Counting documents — a noisy dream!
Z-scores checked, the baseline set,
Ten-times spikes? Not a chance, I bet.
With 28 days of trailing sight,
This bunny keeps the data right! 🌟

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately and specifically describes the main change: a spike detection test for collaborative document activity using per-user trailing baselines.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@SharedQA
SharedQA marked this pull request as ready for review June 17, 2026 10:48
@SharedQA
SharedQA requested a review from a team as a code owner June 17, 2026 10:48

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@src/ingestion/dbt/tests/collaboration/assert_collab_document_counts_no_spike.sql`:
- Line 115: In the WHERE clause of the spike detection query in
assert_collab_document_counts_no_spike.sql, replace the filter condition that
uses activity date (the line with AND date >= today() - 3) with a filter on the
collection timestamp instead. Change the filter to use collected_at >= today() -
3 to ensure late-arriving backfill data is properly included in the spike
detection window. If collected_at is not available in the silver schema, use
_version >= today() - 3 as an alternative to capture the ReplacingMergeTree
update timestamp.
- Around line 43-96: The spike detection test is missing `insight_source_id`
from its partitioning grain, which allows activity from different M365 source
instances within the same tenant to share a contaminated baseline. Add
`insight_source_id` to the SELECT clause in each of the three CTEs (recent,
unpivoted, and baselined) and include it in the PARTITION BY clause of the
WINDOW w definition alongside the existing partition keys (tenant_id,
person_key, product, data_source, metric). This ensures each source instance
maintains its own isolated adaptive baseline for accurate spike detection.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 250adc65-335a-4768-959d-0ec51ee3c04e

📥 Commits

Reviewing files that changed from the base of the PR and between c32821d and 09e8474.

📒 Files selected for processing (1)
  • src/ingestion/dbt/tests/collaboration/assert_collab_document_counts_no_spike.sql

Comment thread src/ingestion/dbt/tests/collaboration/assert_collab_document_counts_no_spike.sql Outdated
SharedQA added a commit to SharedQA/insight that referenced this pull request Jun 17, 2026
…ion time (constructorfabric#1321)

Address CodeRabbit review on PR constructorfabric#1355:

- Add insight_source_id to the spike-detection grain (recent/unpivoted/
  baselined CTEs, WINDOW PARTITION BY, and stored-failure output). Without
  it, multiple M365 source instances for one tenant share a contaminated
  baseline and failure rows can't identify which source to inspect.
- Cap output by collected_at (row arrival) instead of activity date, so a
  late-arriving backfill landing today for an older activity date is still
  flagged — exactly the feeder/backfill anomaly class this check targets —
  rather than being silently dropped by the date filter.

Both columns are present in class_collab_document_activity FINAL
(insight_source_id is a documented not_null column; collected_at flows
through from the m365 feeders via SELECT *).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
SharedQA added a commit to SharedQA/insight that referenced this pull request Jun 17, 2026
…ion time (constructorfabric#1321)

Address CodeRabbit review on PR constructorfabric#1355:

- Add insight_source_id to the spike-detection grain (recent/unpivoted/
  baselined CTEs, WINDOW PARTITION BY, and stored-failure output). Without
  it, multiple M365 source instances for one tenant share a contaminated
  baseline and failure rows can't identify which source to inspect.
- Cap output by collected_at (row arrival) instead of activity date, so a
  late-arriving backfill landing today for an older activity date is still
  flagged — exactly the feeder/backfill anomaly class this check targets —
  rather than being silently dropped by the date filter.

Both columns are present in class_collab_document_activity FINAL
(insight_source_id is a documented not_null column; collected_at flows
through from the m365 feeders via SELECT *).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: Kenan Salim <kenan.salim@rolos.com>
@SharedQA
SharedQA force-pushed the claude/dbt-collab-activity-anomaly branch from 1033dc4 to 40065c7 Compare June 17, 2026 12:24
…ion time (constructorfabric#1321)

Address CodeRabbit review on PR constructorfabric#1355:

- Add insight_source_id to the spike-detection grain (recent/unpivoted/
  baselined CTEs, WINDOW PARTITION BY, and stored-failure output). Without
  it, multiple M365 source instances for one tenant share a contaminated
  baseline and failure rows can't identify which source to inspect.
- Cap output by collected_at (row arrival) instead of activity date, so a
  late-arriving backfill landing today for an older activity date is still
  flagged — exactly the feeder/backfill anomaly class this check targets —
  rather than being silently dropped by the date filter.

Both columns are present in class_collab_document_activity FINAL
(insight_source_id is a documented not_null column; collected_at flows
through from the m365 feeders via SELECT *).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: Kenan Salim <kenan.salim@gmail.com>
SharedQA pushed a commit to SharedQA/insight that referenced this pull request Jun 17, 2026
…ion time (constructorfabric#1321)

Address CodeRabbit review on PR constructorfabric#1355:

- Add insight_source_id to the spike-detection grain (recent/unpivoted/
  baselined CTEs, WINDOW PARTITION BY, and stored-failure output). Without
  it, multiple M365 source instances for one tenant share a contaminated
  baseline and failure rows can't identify which source to inspect.
- Cap output by collected_at (row arrival) instead of activity date, so a
  late-arriving backfill landing today for an older activity date is still
  flagged — exactly the feeder/backfill anomaly class this check targets —
  rather than being silently dropped by the date filter.

Both columns are present in class_collab_document_activity FINAL
(insight_source_id is a documented not_null column; collected_at flows
through from the m365 feeders via SELECT *).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: Kenan Salim <kenan.salim@gmail.com>
@SharedQA
SharedQA force-pushed the claude/dbt-collab-activity-anomaly branch 2 times, most recently from 3a97e98 to d7e676a Compare June 17, 2026 12:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants