Repository navigation
fix(linear): surface truncation when a deduplicated status exceeds the state-id cap - #17342
Conversation
Since #16879 one status/label row expands to an id per team, and the renderer bounds that list to the 100-id transport cap. Any surviving id kept the row fully checked, so the picker claimed coverage the filter never had; show how many of the row's per-team ids are actually applied.
Greptile SummaryThe PR makes Linear status and label truncation visible and distributes the transport ID budget across selected metadata groups.
Confidence Score: 5/5The PR appears safe to merge. No blocking failure remains.
|
| Filename | Overview |
|---|---|
| src/renderer/src/components/linear-issue-attribute-filter-team-ids.ts | Adds deterministic group-aware ID capping and conservative coverage detection that addresses the previously silent fully truncated-group cases. |
| src/renderer/src/components/linear-issue-attribute-filter-coverage-notice.tsx | Renders explicit shortfall or exhausted-cap messaging from the bounded filter state. |
| src/renderer/src/components/linear-issue-attribute-filter-dropdowns.tsx | Applies group-aware capping before emitting filter changes and exposes partial coverage on active pills. |
| src/renderer/src/components/linear-issue-attribute-filter-sections.tsx | Adds partial section summaries and coverage notices for both grouped status and label pickers. |
| src/renderer/src/components/linear-issue-attribute-filter-pills.ts | Extracts pill helpers and derives partial coverage indicators for status and label facets. |
| src/renderer/src/components/linear-issue-attribute-filter-dropdowns.test.tsx | Covers partial notices, pills, section summaries, and fully starved selections at the transport cap. |
| src/renderer/src/components/linear-issue-attribute-filter-team-ids.test.ts | Verifies deterministic round-robin allocation and conservative at-cap coverage reporting. |
Flowchart
%%{init: {'theme': 'neutral'}}%%
flowchart LR
A[Selected grouped rows] --> B[Expand to per-team IDs]
B --> C[Round-robin IDs across groups]
C --> D[Apply 100-ID transport cap]
D --> E[Persist bounded filter]
E --> F[Derive selected rows and coverage]
F --> G{Coverage incomplete or cap reached?}
G -->|Yes| H[Show notice and partial markers]
G -->|No| I[Show normal selected state]
Reviews (5): Last reviewed commit: "Merge remote-tracking branch 'origin/mai..." | Re-trigger Greptile
|
Warning Review limit reachedNext included review available in 7 minutes. View limit detailsLimit details: You’ve used all 10 included reviews currently available. You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository. Review configuration: ⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (1)
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (1)
🚧 Files skipped from review as they are similar to previous changes (1)
Included review availability: Your plan provides up to 10 included reviews per hour; 0 remain after this review. 📝 WalkthroughWalkthroughLinear status and label filters now cap selected metadata IDs across teams. Coverage helpers calculate intended and applied IDs. Filter sections render localized partial-coverage notices, partial summaries, and partial pill markers. Shared status and label detail rendering handles facet-specific options and limits. Tests cover capped selections, changing coverage, full coverage, partial indicators, retained IDs, and label coverage. Merge Risk: 🔵 Low · up to The PR adds a truncation notice for oversized status and label selections, but at the exact ID limit it may incorrectly say that selected items were omitted, misleading users about filter coverage. This is a bounded UI correctness issue suitable for explicit owner awareness or follow-up rather than a release-blocking concern. 🚥 Pre-merge checks | ✅ 3 | ❌ 2❌ Failed checks (2 warnings)
✅ Passed checks (3 passed)
Full details: Title checkExplanation The title clearly describes the primary fix: exposing truncation when deduplicated Linear status IDs exceed the transport cap. It does not mention label coverage, but it remains specific and directly related to the changes. Full details: Description checkExplanation The description explains the problem, fix, linked issue, implementation details, and verification results. However, it does not follow the required template and omits the mandatory Visual Proof section for this UI change, along with several checklist and review sections. Resolution Update the description to use the repository template. Add a Visual Proof section with before-and-after screenshots or videos, or state N/A with a reason only if there is no visual or interaction change. Also add or complete the ELI5, What Changed, Why, Testing checkboxes, AI Disclosure, Review, Agent skill upstream boundary, Notes, and Checklist sections as applicable. ✨ Finishing Touches 💡 1📝 Generate docstrings 💡
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. Comment |
There was a problem hiding this comment.
🧹 Nitpick comments (1)
src/renderer/src/components/linear-issue-attribute-filter-coverage-notice.tsx (1)
26-39: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winUse named interpolation placeholders instead of
value0/value1.This file introduces intent-named localization IDs (
statusPartialTeamCoverage,labelsPartialTeamCoverage), which matches the repo convention for new strings. However, the interpolation placeholders still use the genericvalue0/value1names instead of descriptive ones likeselectedCount/totalCount.♻️ Proposed rename to named placeholders
- const counts = { value0: selectedIds.length, value1: groupIdCount } + const counts = { selectedCount: selectedIds.length, totalCount: groupIdCount } return ( <p className="border-t border-border/50 px-3 py-2 text-xs text-muted-foreground"> {facet === 'status' ? translate( 'auto.components.linear-issue-attribute-filter-coverage-notice.statusPartialTeamCoverage', - 'Filtering {{value0}} of {{value1}} team statuses — issues from the remaining teams are not included.', + 'Filtering {{selectedCount}} of {{totalCount}} team statuses — issues from the remaining teams are not included.', counts ) : translate( 'auto.components.linear-issue-attribute-filter-coverage-notice.labelsPartialTeamCoverage', - 'Filtering {{value0}} of {{value1}} team labels — issues from the remaining teams are not included.', + 'Filtering {{selectedCount}} of {{totalCount}} team labels — issues from the remaining teams are not included.', counts )} </p> )Fixing this file also lets the auto-synced
en.jsonentries pick up the named placeholders on the next catalog sync.Based on learnings: "use intent-named localization IDs with named interpolation placeholders" for newly added user-facing strings, treating hash-based IDs/placeholders as grandfathered only.
Source: Learnings
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro Plus
Run ID: a978716c-3f7e-4d33-916b-57d76d594932
📒 Files selected for processing (4)
src/renderer/src/components/linear-issue-attribute-filter-coverage-notice.tsxsrc/renderer/src/components/linear-issue-attribute-filter-dropdowns.test.tsxsrc/renderer/src/components/linear-issue-attribute-filter-sections.tsxsrc/renderer/src/i18n/locales/en.json
Included review availability: Your plan provides up to 10 included reviews per hour; 0 remain after this review.
There was a problem hiding this comment.
✅ No new issues found.
Reviewed changes — surfaces an inline notice when a deduplicated status or label filter is silently capped by the transport id limit.
- New
LinearFacetCoverageNoticecomponent — re-expands the checked group keys and compares the would-be full count against the actualselectedIds. Renders nothing under the cap, otherwise an inlineFiltering X of Y team statuses/labelsnote. - Wired into section details — rendered under the
MultiSelectListfor both status and labels inLinearIssueFilterSectionDetail. - English copy — two new
translatekeys with{{value0}}/{{value1}}interpolation. - Tests — four new cases covering status over-cap, labels over-cap, the shrunken second-status case, and a negative control under the cap.
The fix is correct and well-scoped: the notice is derived from the already-bounded value, so it stays accurate regardless of which caller applied the cap, and the selectedLinearMetadataGroupKeys → expandLinearMetadataGroupKeys round-trip guarantees it only fires when ids were actually dropped. No wire change, no cap change, and the four tests are exact enough to fail without the fix.
DeepSeek Pro (free via Pullfrog for OSS) | 𝕏
The cap sliced a lexicographically sorted id list, so a whole picked row could lose every id — reverting to unchecked with no notice, and dropping out of the coverage denominator that was supposed to explain it. Spread the cap across the picked rows, and carry the partial-coverage signal to the section menu and the pill, which are what the user reads once the detail panel is closed.
|
Pushed A05-2 — dishonest coverage denominator. Fixed at the source rather than in the display. A05-1 — truncation invisible outside the detail panel. Done, not deferred. The predicate the notice already computes is now shared ( A08-1 — duplicate test scaffolding. Verification: One residual, worth a follow-up ticket only if it ever shows up in practice: if a user checks more distinct status names than the 100-id cap allows, round-robin still cannot seat them all and the denominator under-reports again. That needs ~100 distinct picked status names. |
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
src/renderer/src/components/linear-issue-attribute-filter-coverage-notice.tsx (1)
20-20: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winUse semantic interpolation names in new localized strings.
Generic names such as
value0andvalue1hide the meaning of each value from translators. Use names such asapplied,intended, andsummary. Update the matching locale entries.
src/renderer/src/components/linear-issue-attribute-filter-coverage-notice.tsx#L20-L20: passappliedandintendedto the coverage notice translations.src/renderer/src/components/linear-issue-attribute-filter-sections.tsx#L151-L156: passsummaryto the partial-coverage suffix translation.Based on learnings: newly added user-facing strings must use intent-named localization IDs with named interpolation placeholders.
Source: Learnings
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 68bbdbdb-da94-4362-93f6-5410e6aa80c1
📒 Files selected for processing (7)
src/renderer/src/components/linear-issue-attribute-filter-coverage-notice.tsxsrc/renderer/src/components/linear-issue-attribute-filter-dropdowns.test.tsxsrc/renderer/src/components/linear-issue-attribute-filter-dropdowns.tsxsrc/renderer/src/components/linear-issue-attribute-filter-sections.tsxsrc/renderer/src/components/linear-issue-attribute-filter-team-ids.test.tssrc/renderer/src/components/linear-issue-attribute-filter-team-ids.tssrc/renderer/src/i18n/locales/en.json
🚧 Files skipped from review as they are similar to previous changes (1)
- src/renderer/src/i18n/locales/en.json
Included review availability: Your plan provides up to 10 included reviews per hour; 3 remain after this review.
There was a problem hiding this comment.
✅ No new issues found.
Reviewed changes — since the prior review, the picker now spreads the transport id cap across the picked rows instead of slicing the sorted id list, and flags partial coverage on both the pill and the collapsed section menu.
- Fair cap distribution — new
capLinearMetadataIdsAcrossGroupsround-robins one id per picked group per pass, so a picked row whose ids all sort past the cap stays checked instead of vanishing outright. - Shared coverage helpers — extracted
linearMetadataGroupCoverageandisLinearMetadataGroupSelectionPartial, and rewiredLinearFacetCoverageNoticeonto them. - Picked-filter path —
applyPickedFiltercaps state/label ids across groups beforecanonicalizeLinearIssueAttributeFilter+boundLinearIssueAttributeFilter. - Partial indicators —
ActivePillrenders apartialmarker with a tooltip, and collapsed section-menu summaries append· partial. - Section merge + tests — status and labels now share one detail branch; two new dropdown tests cover the all-sort-past-cap and pill/menu flag cases, plus unit tests for the cap helper.
I traced the round-robin helper, the applied/intended accounting, and the interaction with canonicalizeLinearIssueAttributeFilter + boundLinearIssueAttributeFilter — canonicalize sorts after the cap, so the bound slice is a no-op and no ids re-drop. Tests pass locally (30 component tests, 8 i18n tests).
DeepSeek Pro (free via Pullfrog for OSS) | 𝕏
More picked rows than the transport id cap cannot all be represented, and MultiSelectList.toggle appends the clicked key last — so the starved row was always the row the user had just clicked: it stayed unchecked, no notice fired, and coverage still reported a full 100 of 100. Coverage now takes the cap and reports a spent id budget as its own shortfall, so the picker says how much it is really carrying instead of claiming teams it never covered. Capping also bucketed by the click order the picker hands it, so the same visible selection could resolve to different ids between renders; it now buckets in metadata order, with ids from unloaded teams sorted after. boundLinear- IssueAttributeFilter stays the last word on the transport bound. The pill's `partial` marker moves from a bare title attribute to the Tooltip primitive, which keyboard and screen-reader users can actually reach. Pill labels and facet clearing move to their own module so sections stays under the max-lines cap.
|
Pushed 1. >100 picked rows produced a deterministic dead click that reported full coverage.
101 single-id rows cannot be represented by 100 ids no matter how the round-robin is arranged, so the fix is on the reporting side: Capping itself also went deterministic: it bucketed by the click order the picker hands it, so the same visible selection could resolve to different ids across renders (requirement 4). Buckets are now taken in metadata order, ids from unloaded teams sorted after; round 0 still gives every row one id before any row gets a second. Requirement 3 (don't starve the just-clicked row) is deliberately not met: the visible selection is a set, so favouring "most recent" requires click history, which is exactly the unstable ordering requirement 4 forbids. The click is still dead, but it is now announced. 2. The It was a bare Pill labels and facet clearing moved to Regression tests (each observed failing before the fix, passing after): exactly at the cap, one id over the cap, 101 single-id rows, one wide row alongside several single-id rows, click-order determinism, plus a React-level test that clicks the 101st status row and asserts the coverage notice and the Verification: |
There was a problem hiding this comment.
Actionable comments posted: 2
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 0631be57-35d5-4ac2-a640-68f0a183a6e9
📒 Files selected for processing (8)
src/renderer/src/components/linear-issue-attribute-filter-coverage-notice.tsxsrc/renderer/src/components/linear-issue-attribute-filter-dropdowns.test.tsxsrc/renderer/src/components/linear-issue-attribute-filter-dropdowns.tsxsrc/renderer/src/components/linear-issue-attribute-filter-pills.tssrc/renderer/src/components/linear-issue-attribute-filter-sections.tsxsrc/renderer/src/components/linear-issue-attribute-filter-team-ids.test.tssrc/renderer/src/components/linear-issue-attribute-filter-team-ids.tssrc/renderer/src/i18n/locales/en.json
🚧 Files skipped from review as they are similar to previous changes (2)
- src/renderer/src/i18n/locales/en.json
- src/renderer/src/components/linear-issue-attribute-filter-dropdowns.test.tsx
Included review availability: Your plan provides up to 10 included reviews per hour; 0 remain after this review.
| 'Filtering the most this can carry: {{value0}} team statuses. Rows picked past that are left out.', | ||
| { value0: max } | ||
| ) | ||
| : translate( | ||
| 'auto.components.linear-issue-attribute-filter-coverage-notice.labelsAtIdLimit', | ||
| 'Filtering the most this can carry: {{value0}} team labels. Rows picked past that are left out.', | ||
| { value0: max } |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Do not state that rows were omitted when the limit is only reached.
A filter with exactly max selected IDs sets atLimit, even when every selected ID is retained. These messages then state that rows “are left out.” Use conditional wording, and update the matching locale entries.
Proposed fix
- 'Filtering the most this can carry: {{value0}} team statuses. Rows picked past that are left out.'
+ 'This filter can include up to {{value0}} team statuses. Some selected rows may be left out.'
...
- 'Filtering the most this can carry: {{value0}} team labels. Rows picked past that are left out.'
+ 'This filter can include up to {{value0}} team labels. Some selected rows may be left out.'There was a problem hiding this comment.
Important
The new atLimit flag marks exactly-at-cap selections as partial even when nothing was dropped — a workspace with exactly 100 teams (or 25 teams × 4 statuses = 100 ids) gets a false "Rows picked past that are left out" notice and false "· partial" markers. Details inline.
Reviewed changes — since the prior Pullfrog review, this delta adds the at-limit coverage state, reworks the cap helper, and makes the pill partial marker keyboard/screen-reader reachable.
atLimitcoverage state —linearMetadataGroupCoveragenow returnsatLimit, and every partiality check (isLinearMetadataGroupSelectionPartial,facetSummary, the notice) takes the cap as an explicitmaxargument.- At-limit message — when the budget is spent with no surviving trace of the starved row, the notice renders "Filtering the most this can carry…" instead of the shortfall copy.
- Pill helpers extracted —
countLinearIssueAttributeFilters,clearLinearIssueAttributeFacet, andlinearIssueAttributeFilterPillLabelsmove to a newlinear-issue-attribute-filter-pills.ts. - Accessible partial marker — the pill
partialflag swaps itstitlespan for aTooltip+ focusable<button>. - Deterministic cap —
capLinearMetadataIdsAcrossGroupsbuckets by metadata order and round-robins one id per row, treating unloaded ids as their own sorted rows. - Tests — new unit tests for the over-subscribed-row cases and a dropdown test for the at-limit message.
DeepSeek Pro (free via Pullfrog for OSS) | 𝕏
| intended: expandLinearMetadataGroupKeys(groups, keys).length, | ||
| // Why: a row the cap could not fit leaves no trace in the ids, so once the budget is | ||
| // spent the honest claim is the exhausted budget itself — never full coverage (#17342). | ||
| atLimit: selectedIds.length >= max |
There was a problem hiding this comment.
atLimit: selectedIds.length >= max is true for any selection that lands exactly on the cap, so full-coverage selections are falsely reported as partial. A workspace with exactly 100 teams picking one status (or 25 teams × 4 statuses) yields applied === intended === 100, yet the notice claims "Rows picked past that are left out" and the pill/menu show "· partial". The starved-row case this flag exists for is indistinguishable from exact-fit in the bounded selectedIds alone — the fix needs the pre-cap signal from applyPickedFilter / capLinearMetadataIdsAcrossGroups, not an inference from selectedIds.length.
Technical details
# atLimit false-positives on exactly-at-cap selections
## Affected sites
- `src/renderer/src/components/linear-issue-attribute-filter-team-ids.ts:112` — `atLimit` is `selectedIds.length >= max` with no evidence anything was dropped.
- `src/renderer/src/components/linear-issue-attribute-filter-coverage-notice.tsx:51-56` — renders `idLimitMessage` ("Rows picked past that are left out") when `intended <= applied && atLimit`, which is the exact-fit case.
- `src/renderer/src/components/linear-issue-attribute-filter-pills.ts:71-75` and `src/renderer/src/components/linear-issue-attribute-filter-sections.tsx:50` — the same `atLimit` drives the `· partial` pill and section-menu suffix.
## Why it fires on exact fit
- 100 teams, one status picked: `capLinearMetadataIdsAcrossGroups` early-returns (`ids.length <= max`), `applied = 100`, `intended = 100`, `atLimit = true` → false "partial" on all three surfaces.
- 25 teams × 4 statuses = 100 ids: same result — the scenario the PR body itself calls out as triggering the cap.
## Required outcome
- No truncation → no notice and no `· partial`, even at exactly `max` ids.
- Truncation that fully starves a row (more picked rows than the cap can hold) → still surfaces, because the starved row leaves no trace in `selectedIds`.
## Suggested approach
- Carry the drop signal from where it is known: `capLinearMetadataIdsAcrossGroups` (or `applyPickedFilter`) can report whether any id was dropped (pre-cap count > post-cap count), which is exactly the "starved row" condition at the boundary. Thread that boolean into `linearMetadataGroupCoverage` / `isLinearMetadataGroupSelectionPartial` in place of `atLimit`.
## Open questions for the human
- Is a heuristic (e.g. `atLimit` only when picked-group count > max) acceptable, or should the drop flag be computed precisely at the capping step?The exactly-on-the-cap test capped at max=4 but asserted non-partial at max=5, so it never covered its own subject. Pin both: at the cap coverage warns (a starved row leaves no trace in the ids), below it stays quiet.
Readiness review complete — known residual, deliberately not fixedTwo independent adversarial review lenses cleared the branch (0 blocking findings) with empirical backing: 50k random selections × 4 click-order shuffles produced a single result set every time (the previous click-order bucketing diverged in 41,409 of them), and a 200k-case fuzz found zero inputs where ids were dropped while the UI reported full coverage. Both lenses independently flagged one false positive, which I am shipping as-is:
The bias is deliberately toward warning rather than claiming coverage we cannot prove, and the wording is true in both cases. But it does mean the marker can cry wolf, and that is worth fixing properly rather than papering over. The real fix is to record truncation at the moment it happens — Also fixed in Residuals carried from earlier rounds, unchanged: the round-robin cannot honour a click once more rows are picked than the cap can hold (101 single-id rows cannot fit in 100 ids — the notice now tells the user instead of silently claiming full coverage), and the |
There was a problem hiding this comment.
Important
The new test pins a false positive, and the rationale it records is contradicted by the shipped copy.
Reviewed changes — since the prior Pullfrog review, this delta is a single test change in linear-issue-attribute-filter-team-ids.test.ts.
- Pinned the at-cap partial flag — added an assertion that a selection sitting exactly on the cap reports
isLinearMetadataGroupSelectionPartialastrue, with a rationale comment stating coverage "warns rather than claim what it cannot prove."
The conservative intent is defensible — from the bounded selectedIds alone, exact-fit coverage really is indistinguishable from a starved row. But the shipped idLimitMessage copy does claim ("Rows picked past that are left out"), which is affirmatively false in the exactly-at-cap full-coverage case. Details inline.
DeepSeek Pro (free via Pullfrog for OSS) | 𝕏
| // Why: on the cap, full coverage is indistinguishable from a starved row, so coverage | ||
| // warns rather than claim what it cannot prove — below the cap it stays quiet (#17342). | ||
| expect(isLinearMetadataGroupSelectionPartial(groups, ids, 4)).toBe(true) |
There was a problem hiding this comment.
This assertion codifies a false positive: a selection exactly at the cap with full coverage (the PR body's own 25-teams × 4-statuses example = 100 ids) reports partial even though nothing was dropped, so the user sees "Filtering the most this can carry: 100 team statuses. Rows picked past that are left out" — an affirmatively false statement. The rationale comment says coverage "warns rather than claim what it cannot prove," but the shipped copy does claim, which is exactly what the tooltip already avoids ("Some teams may be left out"). Either hedge idLimitMessage to match the tooltip's wording, or thread the drop signal out of capLinearMetadataIdsAcrossGroups so the notice only fires when ids were actually dropped.
Technical details
# At-cap notice copy contradicts its own rationale
## Affected sites
- src/renderer/src/components/linear-issue-attribute-filter-team-ids.test.ts:177-179 — new test asserts exact-at-cap coverage is partial; rationale comment claims it only warns without claiming.
- src/renderer/src/components/linear-issue-attribute-filter-coverage-notice.tsx:29,34 — `idLimitMessage` copy "Rows picked past that are left out." fires in the exact-fit case (`intended <= applied && atLimit`).
- src/renderer/src/components/linear-issue-attribute-filter-pills.ts:71-75 / linear-issue-attribute-filter-sections.tsx:50 — same `atLimit` drives the `· partial` marker.
## Why it fires on exact fit
- 25 teams × 4 statuses = 100 ids, or 100 teams × 1 status: `capLinearMetadataIdsAcrossGroups` early-returns (nothing dropped), `applied === intended === 100`, `atLimit = true` → notice renders the definitive left-out claim and the pill shows `· partial`.
## Required outcome
- When no id was dropped, no notice and no `· partial`, even at exactly `max` ids — or, if the conservative warning is kept, copy that hedges ("may be left out") instead of asserting ("are left out").
## Suggested approach
- Option A (copy only): change `statusAtIdLimit`/`labelsAtIdLimit` to hedge, e.g. "Filtering the most this can carry: {{value0}} team statuses. Some teams may be left out." — consistent with the existing `partialCoverageTitle`.
- Option B (precise): have `capLinearMetadataIdsAcrossGroups` (or `applyPickedFilter`) report whether it dropped ids (pre-cap count > post-cap count) and thread that boolean into `linearMetadataGroupCoverage` in place of the `selectedIds.length >= max` inference.…ctions (#17376) * fix(linear): stop the filter coverage warning firing on complete selections #17342 inferred transport-cap truncation from the bounded filter after the fact, with `atLimit = selectedIds.length >= max`. A row the cap could not fit leaves no trace in the surviving ids, so at exactly the cap a complete selection and a trimmed one are indistinguishable from the value alone, and the inference biased toward always warning. A workspace with 20 teams x 5 status names expands to exactly 100 ids: picking all 5 rows is provably untruncated, yet the menu read "100 selected · partial". Record the trim where it happens instead. `applyPickedFilter` holds both the pre-cap expansion and the bounded result, so it stores the surviving ids as a truncation record; the notice, section-menu summary, and pill consume that flag. Keying the record on the ids it describes is what keeps it fresh — the moment the facet carries anything else (row toggle, pill clear, Clear all, the prune effect, a workspace switch) the record no longer matches and the warning goes away, which matters because the prune effect only ever removes ids. `intended > applied` stays as the fallback for restored filters that carry no record, and `boundLinearIssueAttributeFilter` is still the last word on the cap. Also moves the section-menu partial marker out of the `max-w-[120px] truncate` summary span, where "100 selected · partial" could clip. Refs STA-5996 * test(linear): cover the untouched-facet truncation guard The guard that keeps a recorded trim alive across an unrelated facet click was untested — the first attempt sat where intended > applied, so the value-derived shortfall answered for it and removing the guard left every test green. Move the scenario onto the cap, where only the record can speak. Also stop an empty record matching an empty facet: a filter carrying nothing is never truncated. * test(linear): pin set equality, not subset, on a truncation record A facet that grew past its record has refetched underneath it; matching by subset would keep warning about a trim that no longer describes the filter. Found by mutation: the subset mutant survived the whole suite. * test(linear): fuzz that the coverage pill and the section notice agree They are the same claim rendered twice; a pill reading partial over a silent section is a lie either way round. 20k random topologies, zero divergence.
…e state-id cap (stablyai#17342) * fix(linear): flag partially applied status and label filters (STA-5983) Since stablyai#16879 one status/label row expands to an id per team, and the renderer bounds that list to the 100-id transport cap. Any surviving id kept the row fully checked, so the picker claimed coverage the filter never had; show how many of the row's per-team ids are actually applied. * fix(linear): keep every picked status row inside the transport cap The cap sliced a lexicographically sorted id list, so a whole picked row could lose every id — reverting to unchecked with no notice, and dropping out of the coverage denominator that was supposed to explain it. Spread the cap across the picked rows, and carry the partial-coverage signal to the section menu and the pill, which are what the user reads once the detail panel is closed. * fix(linear): stop the status filter claiming coverage it cannot apply More picked rows than the transport id cap cannot all be represented, and MultiSelectList.toggle appends the clicked key last — so the starved row was always the row the user had just clicked: it stayed unchecked, no notice fired, and coverage still reported a full 100 of 100. Coverage now takes the cap and reports a spent id budget as its own shortfall, so the picker says how much it is really carrying instead of claiming teams it never covered. Capping also bucketed by the click order the picker hands it, so the same visible selection could resolve to different ids between renders; it now buckets in metadata order, with ids from unloaded teams sorted after. boundLinear- IssueAttributeFilter stays the last word on the transport bound. The pill's `partial` marker moves from a bare title attribute to the Tooltip primitive, which keyboard and screen-reader users can actually reach. Pill labels and facet clearing move to their own module so sections stays under the max-lines cap. * test(linear): assert coverage at the cap it actually caps to The exactly-on-the-cap test capped at max=4 but asserted non-partial at max=5, so it never covered its own subject. Pin both: at the cap coverage warns (a starved row leaves no trace in the ids), below it stays quiet.
…ctions (stablyai#17376) * fix(linear): stop the filter coverage warning firing on complete selections stablyai#17342 inferred transport-cap truncation from the bounded filter after the fact, with `atLimit = selectedIds.length >= max`. A row the cap could not fit leaves no trace in the surviving ids, so at exactly the cap a complete selection and a trimmed one are indistinguishable from the value alone, and the inference biased toward always warning. A workspace with 20 teams x 5 status names expands to exactly 100 ids: picking all 5 rows is provably untruncated, yet the menu read "100 selected · partial". Record the trim where it happens instead. `applyPickedFilter` holds both the pre-cap expansion and the bounded result, so it stores the surviving ids as a truncation record; the notice, section-menu summary, and pill consume that flag. Keying the record on the ids it describes is what keeps it fresh — the moment the facet carries anything else (row toggle, pill clear, Clear all, the prune effect, a workspace switch) the record no longer matches and the warning goes away, which matters because the prune effect only ever removes ids. `intended > applied` stays as the fallback for restored filters that carry no record, and `boundLinearIssueAttributeFilter` is still the last word on the cap. Also moves the section-menu partial marker out of the `max-w-[120px] truncate` summary span, where "100 selected · partial" could clip. Refs STA-5996 * test(linear): cover the untouched-facet truncation guard The guard that keeps a recorded trim alive across an unrelated facet click was untested — the first attempt sat where intended > applied, so the value-derived shortfall answered for it and removing the guard left every test green. Move the scenario onto the cap, where only the record can speak. Also stop an empty record matching an empty facet: a filter carrying nothing is never truncated. * test(linear): pin set equality, not subset, on a truncation record A facet that grew past its record has refetched underneath it; matching by subset would keep warning about a trim that no longer describes the filter. Found by mutation: the subset mutant survived the whole suite. * test(linear): fuzz that the coverage pill and the section notice agree They are the same claim rendered twice; a pill reading partial over a silent section is a lie either way round. 20k random topologies, zero divergence.

Problem
Since #16879 the Tasks filter collapses duplicate per-team status/label rows into one row that expands to one id per team. The renderer bounds that expansion to the transport cap (
LINEAR_ISSUE_ATTRIBUTE_FILTER_MAX_STATE_IDS/_MAX_LABEL_IDS, both 100) atlinear-issue-attribute-filter-dropdowns.tsx:290, because the IPC/RPC parser rejects an over-cap filter outright. The bound is silent:selectedLinearMetadataGroupKeysmaps any surviving id back to its group, so the row stays fully checked whilestate.id.in(src/main/linear/issue-list-filter.ts:49) quietly excludes issues from the teams whose ids were dropped. The cap is on the whole array, not per row, so checking a second status also shrinks the coverage of the first one — 120 teams x 2 statuses ends up 50/50 with both rows checked. Team selection defaults to every team in the workspace, so ~25 teams with 4 statuses checked is already enough to trigger it.Fix
New
LinearFacetCoverageNotice(src/renderer/src/components/linear-issue-attribute-filter-coverage-notice.tsx) re-expands the currently checked group keys and compares that count with the ids the filter actually carries. When the filter carries fewer, the status and labels section details render an inline note under the list: "Filtering 100 of 120 team statuses — issues from the remaining teams are not included." Derived from the rendered value, so it stays correct no matter which caller bounded it; reuses the existingexpandLinearMetadataGroupKeys/selectedLinearMetadataGroupKeyshelpers, the existingtranslate()pattern, and the sametext-xs text-muted-foregroundtreatment as the neighbouring team-required message. The bound itself is unchanged — no wire change, no cap raise.Verification
Confirmed the new tests fail without the fix (reverted
linear-issue-attribute-filter-sections.tsxonly):(the 4th new test is the negative control asserting no notice under the cap.)
Not done here (noted for follow-up): filtering by state/label name instead of id would remove the cap entirely, but that changes what a paired client and host exchange and needs the
docs/reference/remote-wire-compatibility.mdtreatment.Fixes STA-5983
https://linear.app/stably/issue/STA-5983/continuous-scan-linear-tasks-filter-deduplication-silently-caps-same