Repository navigation
UN-4387 [FEAT] Dashboard API for failing pipelines and deployments, and failed pages by source - #2352
muhammad-ali-e wants to merge 5 commits into
Conversation
Adds GET /metrics/attention/ (failing pipelines and API deployments, with a provider registry so cloud apps add their own kinds) and GET /metrics/attribution/ (failed pages by source and by day, built from the same queryset as the failed_pages metric). JSON Schema contracts live in dashboard_metrics/contract/. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019Ja7fuC73jWYRnA1ttdLD1 Signed-off-by: Claude <noreply@anthropic.com>
|
|
muhammad-ali-e
left a comment
There was a problem hiding this comment.
Self-review of #2352 — round 1 (INITIAL), tip cb92b16
Verdict — REQUEST CHANGES
Summary — Critical: 0 · High: 1 · Medium: 9 · Low: 4 · Lenses run: 19/19 (5, 6, 11, 12, 14, 18, 19 N/A with reason)
Security deep-dive: done (no findings at confidence >= 8)
Findings
1. [High] [Lens 3] — An in-flight run hides a failing row from the feed
- Location:
backend/workflow_manager/workflow_v2/failing_streaks.py:L56-L70 - Failure mode:
recentis the last five runs of any status. A pipeline that has failed 20 times and has one PENDING/EXECUTING run drops out of the feed until that run ends, and_last_failurebecomes the in-flight run's time. - Suggested fix: restrict
recentto terminal statuses and fliptest_not_failing's in-flight case. - Confidence: High · VERIFIED — read the query; the existing test pins the behaviour.
2. [Medium] [Lens 3] — "Failures in a row" counts across STOPPED runs
- Location:
backend/workflow_manager/workflow_v2/failing_streaks.py:L63-L67 - Failure mode: the streak is every ERROR after the last COMPLETED, so ERROR, STOPPED, ERROR reports 2 in a row and
sincepredates the STOPPED run. - Suggested fix: bound the streak by the newest terminal non-ERROR run.
- Confidence: High · VERIFIED — read the filter.
3. [Medium] [Lens 2] — The failure rule differs from is_failure_run without saying why
- Location:
backend/workflow_manager/workflow_v2/failing_streaks.py:L1-L6 - Failure mode: the canonical rule (
unstract/core/data_models.py:626) counts STOPPED and partial-file failures; this counts ERROR only. A maintainer could "fix" one toward the other. - Suggested fix: document that this follows the list status dots, not
is_failure_run. - Confidence: Medium (product call) · VERIFIED — compared both definitions.
4. [Medium] [Lens 9] — Every active row pays five correlated subqueries; the range filter runs in Python
- Location:
failing_streaks.py:L68-L83;backend/dashboard_metrics/attention.py:L141-L148 - Failure mode: the uncached endpoint computes streak count/start (unbounded scans for never-succeeded rows) for healthy rows, then discards them in Python.
- Suggested fix: filter on the five-ERROR array and the range in SQL before the costly annotations.
- Confidence: Medium · UNVERIFIED — would
EXPLAIN ANALYZEon a large org confirm the cost?
5. [Medium] [Lens 8] — One bad provider item can 500 the whole feed
- Location:
attention.py:L113-L121;backend/dashboard_metrics/views.py:L980 - Failure mode:
to_dict()runs in the view, outside the per-provider guard;orderis never validated. - Suggested fix: serialize inside the try; validate
orderat registration. - Confidence: High · VERIFIED — read both sites.
6. [Medium] [Lens 10] — The provider-failure log has no org or range — attention.py:L119. Add both. VERIFIED.
7. [Medium] [Lens 16, 17] — "Shared with PB-T13" is false — failing_streaks.py:L8-L9, README.md:L728-L729. The lists still use get_last_run_statuses. Delete the claim. VERIFIED by grep.
8. [Medium] [Lens 7, 16] — Cloud kind names and the route registry are stated as existing — attention.py:L4-L6, L35-L36; README.md:L724, L730. reviews_waiting / challenges_unresolved exist nowhere; route keys exist only in unmerged #2343. VERIFIED by grep across both repos.
9. [Medium] [Lens 13] — Registry tests use real cloud kind names on a process-global registry and assert the full list — tests/test_attention_attribution.py:L222-L270. Once a cloud app registers reviews_waiting, setUp raises and cleanup removes the real provider. Use test-only kinds, snapshot/restore, relative order. VERIFIED.
10. [Medium] [Lens 13] — The streak's lower bound is untested — tests/test_attention_attribution.py:L110-L140. No fixture has ERRORs before the last success; the ordering test can't tell grouping from a global sort. VERIFIED.
11. [Low] [Lens 13] — Schema checks ignore format — tests/test_attention_attribution.py:L160. Pass format_checker. VERIFIED.
12. [Low] [Lens 13] — end_date bound untested — tests/test_attention_attribution.py:L213. VERIFIED.
13. [Low] [Lens 16] — README "same defaults and 365-day cap as above" has no antecedent — README.md:L691. VERIFIED.
14. [Low] [Lens 15] — ruff 0.3.4 would reformat the test file — VERIFIED with ruff format --check --diff.
Open questions
- Visibility is org-wide (any member sees every pipeline name), as in existing dashboard endpoints (
services.py:726-739). UNVERIFIED — is that intended for non-admin members?
Lens checklist
1 Clean · 2 #3 · 3 #1, #2 · 4 Clean (security pass done) · 5 N/A no writes · 6 N/A read-only · 7 #8 (no consumers yet) · 8 #5 · 9 #4 · 10 #6 · 11 N/A additive · 12 N/A no model calls · 13 #9–#12 · 14 N/A no new deps · 15 #14 · 16 #7, #8, #13 · 17 #7 · 18 N/A · 19 N/A
Fixes follow in the next push.
Generated by Claude Code
- Count only finished runs, so a queued retry no longer hides a failing row, and end a streak at any finished run that was not an ERROR (STOPPED too). - Run the failing test and the date range in SQL before the streak lookups. - Serialize and check each provider's items inside its guard, validate a provider's order at registration, and log the org and range on failure. - Document why the rule is ERROR only, drop the false PB-T13 and cloud kind claims, and mark the route keys as proposed in #2343. - Tests: streak lower bound, stops, unfinished runs, range bounds, grouped ordering, malformed items, test-only kinds on an isolated registry, and schema format checks. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019Ja7fuC73jWYRnA1ttdLD1
for more information, see https://pre-commit.ci
muhammad-ali-e
left a comment
There was a problem hiding this comment.
Self-review of #2352 — round 2 (FOLLOWUP), delta cb92b16..da7c2fe
Verdict — APPROVE WITH NITS
All round 1 findings are resolved:
- #1, #2: only finished runs count, and the streak ends at the newest finished non-ERROR run. Pinned by
test_unfinished_runs_are_skippedand the "a stop" subtest (old code gives 6 failures since 9h, new gives 5 since 7h). - #4: the five-ERROR array and range filters are in the WHERE clause (
str(qs.query)checked; Django casts tovarchar[]), so the streak subqueries run only for failing rows. - #3, #5–#14: fixed as described in 09a1272. Every new guard was mutation-checked.
- pre-commit.ci's pyupgrade rewrite (da7c2fe) is accepted as is.
New findings
1. [Medium] [Lens 13] — test_display_order passes even when providers are not sorted
- Location:
backend/dashboard_metrics/tests/test_attention_attribution.py:L339-L345 - Failure mode: every test provider is registered in ascending
order, so a registry that returned insertion order would pass. Replacingsortedwithlistkept all 19 tests green. - Suggested fix: register a low-order provider last and assert it sorts first.
- VERIFIED by mutation.
2. [Low] [Lens 16] — _FailingRowsProvider docstring still states the old rule — backend/dashboard_metrics/attention.py:L153. VERIFIED.
3. [Low] [Lens 16] — collect_attention docstring omits the count check; _serialize does not check id or since types — backend/dashboard_metrics/attention.py:L118-L150. VERIFIED.
Fixes follow in the next push.
Generated by Claude Code
- Check item id, name and since types before serializing, and say so in the collect_attention docstring. - Point the failing-rows provider docstring at failing_streaks for the rule. - Register a test provider out of order so the display-order test fails if the registry stops sorting. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019Ja7fuC73jWYRnA1ttdLD1
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019Ja7fuC73jWYRnA1ttdLD1
muhammad-ali-e
left a comment
There was a problem hiding this comment.
Self-review of #2352 — round 3 (FOLLOWUP), delta da7c2fe..6bd9bd6
Verdict — APPROVE
Round 2 findings resolved:
- #1: a test provider with
order = 5is registered last and must sort first. Replacingsortedwith insertion order now fails the test. - #2: the failing-rows provider docstring now points at
failing_streaksfor the rule. - #3:
_serializerejects a non-stringid, a non-stringname, and asincethat is not a datetime (adateincluded), and the docstring says so. Each check was mutation-tested.
No new findings at Medium or above. One nit fixed in 6bd9bd6: the type error now names both id and name.
Checks: dashboard_metrics 212 passed; the repo's pre-commit hooks pass on the PR's files.
The loop stops here; the PR stays in draft.
Generated by Claude Code
|



What
GET /api/v2/unstract/<org>/metrics/attention/lists the items that need attention on the dashboard. OSS ships two kinds:failing_pipeline: active ETL and Task pipelines whose last 5 finished runs all ended in ERROR.failing_deployment: active API deployments whose last 5 finished runs all ended in ERROR.GET /api/v2/unstract/<org>/metrics/attribution/gives failed pages for the range, split by source (API deployment, ETL or Task pipeline, workflow, deleted) and by day.backend/dashboard_metrics/contract/.Why
How
workflow_manager/workflow_v2/failing_streaks.pyfinds failing rows in one query. The failing test and the date range run in SQL first, on the(pipeline_id, -created_at)index, so the streak lookups only run for rows that are failing. For each failing row it reports:is_failure_run(which also counts STOPPED and partly failed runs). The module docstring says why.dashboard_metrics/attention.pyhas a provider registry. OSS registers its two providers inDashboardMetricsConfig.ready(). Cloud apps register their own kinds from their ownready()in a separate unstract-cloud PR. Each provider's items are checked against the contract's types and serialized inside its own guard. If a provider raises or returns a malformed item, the feed still returns, and that provider's kind appears infailed_kinds, so "nothing needs attention" is never a silent error.routeof{key, params}:etl_pipeline.detail,task_pipeline.detailandapi_deployment.detail, the keys proposed by the global search work (UN-4402 [FEAT] Global search API #2343, not yet merged).dashboard_metrics/attribution.pygroupsMetricsQueryService.failed_file_executions_with_pages().get_failed_pagesnow builds the same queryset with the same SQL, so attribution totals always equal thefailed_pagesseries.MetricsQuerySerializer, so they takestart_date/end_date(default 30 days, max 365), and keep the existing permission and throttle classes. Neither response is cached.Defaults chosen where the spec was open:
start_date/end_date, matching the other metrics endpoints, rather than the spec'sfrom/to.Can this PR break any existing features. If yes, please list possible items. If no, please explain why. (PS: Admins do not merge the PR without this section filled)
get_failed_pages, which now builds its queryset through a shared helper. The SQL is unchanged and every existing dashboard_metrics test passes.Database Migrations
Env Config
Relevant Docs
backend/dashboard_metrics/README.md, section "Attention and attribution (PB-T14)".Related Issues or PRs
Dependencies Versions
Notes on Testing
dashboard_metrics/tests/test_attention_attribution.pyadds 19 tests covering:get_failed_pages🤖 Generated with Claude Code
https://claude.ai/code/session_019Ja7fuC73jWYRnA1ttdLD1