Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
76015be
feat: align health score v2 with community feedback commitments (IN-1…
gaspergrom Aug 10, 2026
9c0e94d
fix: correct node/schema/table mismatches in health score v2 pipes (I…
gaspergrom Aug 10, 2026
7cd4c47
fix: collapse snapshot pipe to single node, align schema aliases (IN-…
gaspergrom Aug 10, 2026
629db04
fix: avoid nested window fn in release-cadence subquery (IN-1226)
gaspergrom Aug 10, 2026
f0807eb
fix: dedupe repos table joins in snapshot pipe to prevent row fan-out…
gaspergrom Aug 10, 2026
310860e
fix: correct vulnerability severity filter from MODERATE to MEDIUM (I…
gaspergrom Aug 10, 2026
9a48dbf
Merge branch 'main' into feat/CM-IN-1226-health-score-v2-compliance
gaspergrom Aug 11, 2026
6a22872
fix: address PR #4460 review comments on Health Score v2 pipelines (I…
gaspergrom Aug 11, 2026
315ed55
fix: resolve Tinybird query compilation and logic errors (CM-IN-1226)
gaspergrom Aug 11, 2026
cdf6cf2
fix: remove invalid UNIQUE_KEY from snapshot datasource (CM-IN-1226)
gaspergrom Aug 11, 2026
35f1009
fix: add age gate to abandoned lifecycle branch, cover PRs (CM-IN-1226)
gaspergrom Aug 11, 2026
1f6e6f8
fix: apply canonical tb fmt to Tinybird files (CM-IN-1226)
gaspergrom Aug 11, 2026
ae0703e
fix: prevent pre-commit hook from failing on unrelated commits (CM-IN…
gaspergrom Aug 11, 2026
d27053f
fix: skip tb fmt in pre-commit when tinybird venv is not set up (CM-I…
gaspergrom Aug 11, 2026
5f8cbc3
fix: gate tb fmt on pip install actually succeeding (CM-IN-1226)
gaspergrom Aug 11, 2026
6a97bd8
Merge branch 'main' into feat/CM-IN-1226-health-score-v2-compliance
gaspergrom Aug 11, 2026
107c005
fix: address PR #4460 review findings (CM-IN-1226)
gaspergrom Aug 11, 2026
85b568a
fix: lifecycle age threshold, snapshot idempotency and unanswered cou…
gaspergrom Aug 11, 2026
7b940b8
fix: gate abandoned on both commit signals to cover lastCommitAt data…
gaspergrom Aug 11, 2026
ac5672b
fix: exclude PR author self-reviews from first-response timestamps (C…
gaspergrom Aug 11, 2026
d108a07
revert: drop tb fmt pre-commit hook (CM-IN-1226)
gaspergrom Aug 11, 2026
afecdfb
docs: document non-author guard convention with prod evidence (CM-IN-…
gaspergrom Aug 11, 2026
3bb299e
fix: apply non-author review filter to incremental and bootstrap PR a…
gaspergrom Aug 11, 2026
0b77c68
fix: point pull_request_analysis_copy_pipe at bucket_union datasource…
gaspergrom Aug 11, 2026
87ba957
fix: disable pull_request_analysis_copy_pipe schedule pending investi…
gaspergrom Aug 11, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions services/libs/tinybird/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -339,6 +339,34 @@ tb sql "SELECT count() FROM activities_backup FINAL"
If both pairs match, the backup is **logically consistent** with the source dataset.


---

## Health Score v2 Configuration

### Excluding Repositories from Health Score

Repositories can be marked as excluded from health scoring by setting the `repositories.excluded` flag to `1`. This is the official mechanism for marking experimental, sandbox, meta, or otherwise out-of-scope repositories.

**Use cases for exclusion:**
- `.github` meta repositories (configuration-only, no product code)
- Archived experiments or prototypes
- Forks created for testing purposes
- Any repository where activity metrics are meaningless or undesired

**Behavior when excluded:**
- Health Score v2 categorizes excluded repos as `unavailable` for responsiveness scoring (never scored 0 for missing PR/issue data)
- Other category signals continue to be scored normally
- Project-level health scores (when a project contains multiple repos) exclude the marked repo from aggregation

**Setting the flag:**
```sql
UPDATE repositories SET excluded = TRUE WHERE url = 'https://github.com/org/repo-meta';
```

The flag is read by `project_insights_copy.pipe` and `health_score_v2_raw_inputs_snapshot.pipe` as part of their graceful-degradation and audit logic.

---

## Glossary

- **CDP (Community Data Platform)**: Customer data operations and management pipelines
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,8 @@ SCHEMA >
`prMergeAvailable` UInt8,
`merged12m` Nullable(UInt64),
`closedUnmerged12m` Nullable(UInt64),
`medianMergeS` Nullable(Float64)
`medianMergeS` Nullable(Float64),
`methodologyVersion` String

ENGINE MergeTree
ENGINE_SORTING_KEY repoUrl
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,14 @@ DESCRIPTION >
- `health_score_v2_lifecycle_ds` holds the per-repo Lifecycle state for Health Score v2.
Populated by `health_score_v2_lifecycle.pipe`.
- `repoUrl` is the repository URL — the join key back to `repositories`.
- `lifecycleLabelV2` — one of active/stable/declining/abandoned/archived, or NULL when the
repo has no usable activity signal at all (2026-07-24, IN-1196 — see
- `lifecycleLabelV2` — one of active/stable/declining/inert/abandoned/archived, or NULL when
the repo has no usable activity signal at all (2026-07-24, IN-1196 — see
health_score_v2_lifecycle.pipe for the exact condition).

SCHEMA >
`repoUrl` String,
`lifecycleLabelV2` Nullable(String)
`lifecycleLabelV2` Nullable(String),
`methodologyVersion` String

ENGINE MergeTree
ENGINE_SORTING_KEY repoUrl
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@ SCHEMA >
`medianIssueResponseS` Nullable(Float64),
`isGerrit` UInt8,
`isExcluded` UInt8,
`coveredWeight` UInt8
`coveredWeight` UInt8,
`methodologyVersion` String

ENGINE MergeTree
ENGINE_SORTING_KEY repoUrl
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
DESCRIPTION >
- `health_score_v2_raw_inputs_snapshot_ds` captures raw measured inputs (not computed scores) for
Health Score v2 at periodic intervals for validation and historical analysis.
- Populated by `health_score_v2_raw_inputs_snapshot.pipe` (append-mode, monthly cadence).
- Each row is keyed by `repoUrl + snapshotDate + methodologyVersion` and captures all the atomic
measurement signals used to compute the three health score categories:
- Maintainer signals: bus factor (curated maintainer count), observed review/merge actors,
organization diversity (distinct org count), PR/issue response times (medians).
- Security signals: open CVE counts (critical/high/moderate), OpenSSF Scorecard score, security
practices flags (policy/branch-protection state), vulnerable dependencies count.
- Development signals: release recency/cadence, commit counts (6m windows), issue counts/closure
times, PR counts/merge times.
- Responsiveness/lifecycle signals: unanswered aged issue/PR counts and 18mo opened totals
(the per-repo unanswered count/ratio the compliance spec item 6 requires, and the historical
input the abandoned-threshold-sweep validation analysis runs against). Definitions mirror
health_score_v2_lifecycle.pipe: open, no non-author response, aged 90+ days, opened within
the 18mo window.
- Does NOT include computed category/overall scores — those are derived from these raw inputs via
the scoring logic in the respective pipes. This datasource is for tracing where scores come from.
- snapshotId = `toStartOfInterval(now(), INTERVAL 1 day)` at pipe execution time.
- Partitioned by year(snapshotDate) and month(snapshotDate) for efficient time-based queries.
- TTL set to 24 months (enough for trend analysis and validation studies).
- ReplacingMergeTree keyed on (snapshotDate, repoUrl, methodologyVersion): a same-day manual
re-run of the copy converges to one row per key after background merges instead of
accumulating duplicates (UNIQUE_KEY is not a thing in Tinybird — attempted and reverted in
cdf6cf263). Validation queries must still read with FINAL (or dedupe via argMax/LIMIT BY)
since replacement is asynchronous.

SCHEMA >
`repoUrl` String,
`snapshotDate` DateTime,
`methodologyVersion` String,
`busFactorCuratedCount` Nullable(UInt64),
`busFactorObservedActorsCount` Nullable(UInt64),
`orgDiversityCount` Nullable(UInt64),
`medianPrResponseSeconds` Nullable(Float64),
`medianIssueResponseSeconds` Nullable(Float64),
`openCriticalVulns` Nullable(UInt64),
`openHighVulns` Nullable(UInt64),
`openModerateVulns` Nullable(UInt64),
`scorecardScore` Nullable(String),
`securityPolicyEnabled` Nullable(UInt8),
`branchProtectionEnabled` Nullable(UInt8),
`branchProtectionRequiredReviews` Nullable(Int32),
`branchProtectionRequiresStatusChecks` Nullable(UInt8),
`branchProtectionAllowsForcePush` Nullable(UInt8),
`vulnerableDeps` Nullable(UInt64),
`daysSinceLatestRelease` Nullable(Int64),
`daysBetweenRecentReleases` Nullable(Int64),
`commitsLast6m` Nullable(UInt64),
`commitsPrior6m` Nullable(UInt64),
`lastCommitAt` Nullable(DateTime64(3)),
`issuesClosedLast12m` Nullable(UInt64),
`issuesOpenedLast12m` Nullable(UInt64),
`medianIssueCloseSeconds` Nullable(Float64),
`prsMergedLast12m` Nullable(UInt64),
`prsClosedUnmergedLast12m` Nullable(UInt64),
`medianPrMergeSeconds` Nullable(Float64),
`excluded` Nullable(UInt8),
`trackedPackageCount` Nullable(UInt64),
`unansweredIssuesAged90d` Nullable(UInt64),
`unansweredPrsAged90d` Nullable(UInt64),
`issuesOpenedLast18m` Nullable(UInt64),
`prsOpenedLast18m` Nullable(UInt64)

ENGINE ReplacingMergeTree
ENGINE_PARTITION_KEY toYYYYMM(snapshotDate)
ENGINE_SORTING_KEY (snapshotDate, repoUrl, methodologyVersion)
ENGINE_TTL snapshotDate + INTERVAL 24 MONTH
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,24 @@ DESCRIPTION >
across unrelated packages), supply chain integrity (hardcoded 0 — provenance/2FA data not yet piped).
- `developmentActivityScoreV2` (0-25) — release cadence, commit activity, issue resolution, PR merge health.
- `healthScoreV2` (0-100) is the sum of the three categories above, clamped to 100.
- `lifecycleLabelV2` — per-repo lifecycle state (active/stable/declining/abandoned/archived), computed
via the spec's decision tree (archived flag > abandoned > declining > stable > active, first match
wins), or NULL when the repo has zero commits and zero issues/PRs in every window checked (2026-07-24,
IN-1196 — no usable activity signal at all). Project-level rollup uses best-state-wins precedence in
project_insights_copy.pipe.
- `lifecycleLabelV2` — per-repo lifecycle state (active/stable/declining/inert/abandoned/archived),
computed via the spec's decision tree (archived flag > abandoned > inert > declining > stable >
active, first match wins), or NULL when the repo has zero commits and zero issues/PRs in every window
checked (2026-07-24, IN-1196 — no usable activity signal at all). Project-level rollup uses
best-state-wins precedence in project_insights_copy.pipe.
- `impactScore` (0-100) — MAX(packages.impact) * 100 over packages published by this repo. NULL when
the repo has no linked packages.
- No graceful-degradation/signal-coverage redistribution yet — a repo with zero signal in a category
scores 0 there rather than having points redistributed from available categories.
- Graceful degradation (spec Layer 1+2): per-category pipes emit NULL when covered sub-signal weight
is <40% of that category's max pts (Layer 1); the aggregate health_score_v2 applies Layer 2 rescaling
(`SUM(available categories) * (100 / SUM(available category weights))`) so only actually-available
scores are summed. If all three categories are unavailable, healthScoreV2 itself is NULL (see
health_score_v2.pipe DESCRIPTION for the full spec).
- Known temporary gap: security-practices score's spec-stated max is 8 points (includes +1 for
`security_contact_email IS NOT NULL` flag), but no `security_contact_email` column exists yet in
the GitHub enrichment datasource, so the current maximum achievable is 7 points. This gap is
intentional and temporary — do not fabricate the missing column or change scoring. When the email
field becomes available in repos, security-practices score will automatically rise to its full 8-point
potential.

SCHEMA >
`repoUrl` String,
Expand All @@ -30,7 +39,8 @@ SCHEMA >
`developmentActivityScoreV2` Nullable(UInt8),
`healthScoreV2` Nullable(UInt8),
`lifecycleLabelV2` Nullable(String),
`impactScore` Nullable(UInt8)
`impactScore` Nullable(UInt8),
`methodologyVersion` String

ENGINE MergeTree
ENGINE_SORTING_KEY repoUrl
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,8 @@ SCHEMA >
`branchProtectionAllowsForcePush` Nullable(UInt8),
`dependencyHealthScore` UInt8,
`dependencyHealthAvailable` UInt8,
`vulnerableDeps` Nullable(UInt64)
`vulnerableDeps` Nullable(UInt64),
`methodologyVersion` String

ENGINE MergeTree
ENGINE_SORTING_KEY repoUrl
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,8 @@ SCHEMA >
`prMergeAvailable` Nullable(UInt8),
`merged12m` Nullable(UInt64),
`closedUnmerged12m` Nullable(UInt64),
`medianMergeS` Nullable(Float64)
`medianMergeS` Nullable(Float64),
`methodologyVersion` String

ENGINE MergeTree
ENGINE_SORTING_KEY repoUrl
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ DESCRIPTION >
- Maintainer signals: `busFactorScore` (avg), `busFactorAvailable` (max), `busFactorCount` (max),
`orgDiversityScore` (avg), `orgDiversityAvailable` (max), `orgCount` (max),
`responsivenessScore` (avg), `responsivenessAvailable` (max), `medianPrResponseS` (avg),
`medianIssueResponseS` (avg), `isGerrit` (min), `isExcluded` (min).
`medianIssueResponseS` (avg), `isGerrit` (min), `isExcluded` (min),
`busFactorScoreActivityWeightedMean` (activity-weighted mean of bus-factor score across repos).
- Security signals: `openVulnScore` (avg), `openCriticals`/`openHighs`/`openModerates` (max),
`scorecardScorePts` (avg), `scorecardAvailable` (max), `scorecardScore` (avg, cast from the raw
String), `securityPracticesScore` (avg), `securityPracticesAvailable` (max),
Expand Down Expand Up @@ -75,7 +76,8 @@ SCHEMA >
`prMergeAvailable` Nullable(UInt8),
`merged12m` Nullable(UInt64),
`closedUnmerged12m` Nullable(UInt64),
`medianMergeS` Nullable(Float64)
`medianMergeS` Nullable(Float64),
`busFactorScoreActivityWeightedMean` Nullable(Float64)

ENGINE MergeTree
ENGINE_SORTING_KEY slug, projectId
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ DESCRIPTION >
- `cveIds` is an array of CVE identifiers associated with this vulnerability (empty array default).
- `ghsaIds` is an array of GitHub Security Advisory identifiers.
- `otherIds` is an array of other vulnerability identifiers.
- `severity` is the vulnerability severity level (UNKNOWN, LOW, MODERATE, HIGH, CRITICAL).
- `severity` is the vulnerability severity level (UNKNOWN, LOW, MEDIUM, HIGH, CRITICAL).
- `cvssScore` is the raw CVSS score (nullable, null when score is unavailable).
- `summary` is a short description of the vulnerability.
- `details` contains detailed information about the vulnerability.
Expand Down
3 changes: 2 additions & 1 deletion services/libs/tinybird/pipes/health_score_v2.pipe
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,8 @@ SQL >
lifecycleLabelV2,
if(
impactScoreRaw IS NULL, NULL, toNullable(toUInt8(round(least(impactScoreRaw, 100))))
) AS impactScore
) AS impactScore,
'2.0.0' AS methodologyVersion
FROM
(
SELECT
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,8 @@ SQL >
prMergeAvailable,
merged12m,
closedUnmerged12m,
medianMergeS
medianMergeS,
'2.0.0' AS methodologyVersion
FROM
(
SELECT
Expand Down
76 changes: 71 additions & 5 deletions services/libs/tinybird/pipes/health_score_v2_lifecycle.pipe
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
DESCRIPTION >
Per-repo Lifecycle state, computed via the spec's decision tree (first match wins):
archived (repos.archived) > abandoned (no commits in 18mo AND no issue/PR activity in 18mo) >
archived (repos.archived) > abandoned (an issue or PR open and unanswered for 90+ days AND no
commits in 12mo) > inert (no commits in 18mo AND no issue/PR activity in 18mo) >
declining (commits down >50% vs prior 6mo AND issues opened up vs prior 6mo) > stable (release
within 12mo, <50 open issues, no open critical vulns, commits down >50% vs prior 6mo) > active
(fallback) — but NULL overrides all of the above when there is no usable signal at all (see
Expand All @@ -27,13 +28,51 @@ DESCRIPTION >
commitsLast6m=0 (down from 1895 prior), because it still has 3 real PRs in the 18mo window —
real activity, just not enough to clear any of the other branches' thresholds. That's a
legitimate 'active' classification under the existing spec, not the bug being fixed here.
- `r.lastCommitAt IS NULL` added to the NULL-guard condition (2026-08-11, per Joana's PR
review): the guard as written (zero commits in both 6mo windows + zero issue/PR activity in
18mo) was a strict superset of the `inert` branch's own condition (no commits in 18mo + zero
issue/PR activity), since zero commits across the trailing 12mo already implies
`lastCommitAt` is either NULL or older than 18mo. That made the guard fire first on every repo
that could otherwise have matched `inert`, so `inert` was dead code — never actually emitted.
Restricting the guard to the `lastCommitAt IS NULL` case (matching the blocknetdx/blocknet
example this branch was originally written for) lets repos with a known, stale last-commit
timestamp fall through into the real decision tree, where `inert` can now actually fire.

NODE health_score_v2_lifecycle_calc
DESCRIPTION >
- Precedence (first match wins): archived > abandoned > inert > declining > stable > active.
- Threshold note (provisional, pending validation analysis): abandoned requires at least one
issue OR PR that has sat unanswered for 90+ days, plus a 12-month no-commit gate. The age
gate is a single threshold, NOT a closed 90-180 band: a band would let a repo exit
'abandoned' once its ignored issues age past the upper bound (they still block 'inert' via
issuesInWindow18m, so the repo would flip back to 'active'). The spec's "~90-180 days" is
the tuning range for this one threshold — the abandoned-threshold-sweep validation analysis
picks the final value. The no-commit gate requires BOTH commit signals to be silent: zero
authored-commit activity in both trailing 6mo windows (covers repos where lastCommitAt is
NULL/stale from the upstream `repos` data gap but real commits exist in activityRelations)
AND repos.lastCommitAt NULL or older than 12mo (covers the reverse gap). Both signals are
last-commit-by-anyone, not maintainer-specific activity; whether maintainer review/comment
activity should also block 'abandoned' is an open question on the PR.
- Inert: lastCommitAt older than 18mo AND zero issues/PRs opened in the 18mo window.
lastCommitAt IS NULL deliberately does NOT qualify: a NULL-with-no-signal repo is already
caught by the NULL guard above, and a NULL-with-recent-commits repo (upstream data gap in
`repos`) must not be mislabeled inert just because one timestamp field is missing.
- Unanswered issue: opened in the 18mo window, no non-author comment, still open, and has been
open 90+ days.
- Unanswered PR: opened in the 18mo window, no reviewer activity (reviewedAt IS NULL), still
open (closedAt IS NULL), and has been open 90+ days. Mirrors the issue-side signal using
pull_requests_analyzed.reviewedAt, which is already the first-response timestamp for PRs
(first review / changes-requested / Gerrit patchset approval) — no new datasource field needed.
- Declining: commit count down >50% vs prior 6mo AND issues opened up vs prior 6mo.
- Stable: release within 12mo, <50 open issues, no open critical vulns, AND commits down
>50% vs prior 6mo.

SQL >
SELECT
r.url AS repoUrl,
if(
r.archived != 1
AND r.lastCommitAt IS NULL
AND coalesce(c.commitsLast6m, 0) = 0
AND coalesce(c.commitsPrior6m, 0) = 0
AND coalesce(w.issuesInWindow18m, 0) + coalesce(p.prsInWindow18m, 0) = 0,
Expand All @@ -42,9 +81,14 @@ SQL >
multiIf(
r.archived = 1,
'archived',
(r.lastCommitAt < now() - INTERVAL 18 MONTH)
AND coalesce(w.issuesInWindow18m, 0) + coalesce(p.prsInWindow18m, 0) = 0,
(coalesce(w.unansweredAged90d, 0) > 0 OR coalesce(pw.unansweredAged90d, 0) > 0)
Comment thread
cursor[bot] marked this conversation as resolved.
AND coalesce(c.commitsLast6m, 0) = 0
AND coalesce(c.commitsPrior6m, 0) = 0
AND (r.lastCommitAt IS NULL OR r.lastCommitAt < now() - INTERVAL 12 MONTH),
Comment thread
cursor[bot] marked this conversation as resolved.
'abandoned',
Comment thread
cursor[bot] marked this conversation as resolved.
r.lastCommitAt < now() - INTERVAL 18 MONTH
AND coalesce(w.issuesInWindow18m, 0) + coalesce(p.prsInWindow18m, 0) = 0,
'inert',
Comment thread
gaspergrom marked this conversation as resolved.
Comment thread
cursor[bot] marked this conversation as resolved.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

CRITICAL — inert branch is unreachable.

The outer no-signal guard fires before multiIf, and its condition

commitsLast6m = 0 AND commitsPrior6m = 0 AND issuesInWindow18m + prsInWindow18m = 0

is a strict superset of the inert branch condition

(lastCommitAt IS NULL OR lastCommitAt < now() - 18 MONTH) AND issuesInWindow18m + prsInWindow18m = 0

Any repo qualifying for inert also has zero commits in both 6-month windows by definition, so the guard returns NULL and inert is never emitted in practice. Copilot + Cursor Bugbot flagged this on commit 310860e and it still stands.

Fix options:

  • Tighten the no-signal guard to also require r.lastCommitAt IS NULL — treat "unknown timestamp" as the only true no-signal case and let known-stale repos fall through to inert.
  • Or move the inert check ahead of the outer NULL guard.

coalesce(c.commitsLast6m, 0) < coalesce(c.commitsPrior6m, 0) * 0.5
AND coalesce(w.issuesOpenedLast6m, 0) > coalesce(w.issuesOpenedPrior6m, 0),
'declining',
Expand All @@ -56,7 +100,8 @@ SQL >
'active'
)
)
) AS lifecycleLabelV2
) AS lifecycleLabelV2,
'2.0.0' AS methodologyVersion
FROM
(
SELECT base.url AS url, base.archived AS archived, rc.lastCommitAt AS lastCommitAt
Expand Down Expand Up @@ -95,7 +140,13 @@ SQL >
countIf(
openedAt <= now() - INTERVAL 6 MONTH AND openedAt > now() - INTERVAL 12 MONTH
) AS issuesOpenedPrior6m,
countIf(openedAt > now() - INTERVAL 18 MONTH) AS issuesInWindow18m
countIf(openedAt > now() - INTERVAL 18 MONTH) AS issuesInWindow18m,
countIf(
openedAt > now() - INTERVAL 18 MONTH
AND respondedInSeconds IS NULL
AND closedAt IS NULL
AND openedAt <= now() - INTERVAL 90 DAY
) AS unansweredAged90d
FROM issues_analyzed
GROUP BY channel
) AS w
Expand All @@ -107,6 +158,21 @@ SQL >
GROUP BY channel
) AS p
ON p.repoUrl = r.url
LEFT JOIN
(
SELECT
channel AS repoUrl,
countIf(
openedAt > now() - INTERVAL 18 MONTH
AND reviewedAt IS NULL
AND approvedAt IS NULL
AND closedAt IS NULL
AND openedAt <= now() - INTERVAL 90 DAY
) AS unansweredAged90d
FROM pull_requests_analyzed
GROUP BY channel
) AS pw
ON pw.repoUrl = r.url
LEFT JOIN
(
SELECT repoUrl, countIf(status = 'OPEN' AND severity = 'CRITICAL') AS openCriticals
Expand Down
Loading
Loading