CI & Build / Python lint (push) Successful in 4s
CI & Build / Plugin hooks (push) Successful in 39s
CI & Build / TypeScript typecheck (push) Successful in 41s
CI & Build / integration (push) Successful in 1m47s
CI & Build / Python tests (push) Successful in 2m25s
CI & Build / Build & push image (push) Successful in 1m31s
Roundtable's card rendered ~35 milestone bars and ran several viewport-heights tall, so one tile dwarfed the grid and stopped being scannable — which is the whole job of a card (#2391). Now 10 bars, ordered OPEN WORK FIRST and newest first within each group, with "+25 more milestones" beneath. Ordering by recency alone would have been wrong, and the operator's call was to lead with open work: a long-running project's oldest milestones are usually its finished ones, so the ten most recent could easily have been ten completed bars while the three in flight were the ones hidden. A card answers "what is happening", not "what happened". Three details that are the actual work: - The palette index is captured from the FULL list before slicing. Colour keyed to visible position would have recoloured every bar on the card each time a milestone closed or was added. - Computed once per load into a Map rather than called from the template. A helper invoked inside v-for re-runs on every render, and this one sorts. - The overflow notice is plain text, not a link. The whole card already navigates to the project, and a link nested inside a clickable region is a trap for keyboard and screen-reader users. Saying the count matters more than the cap: a list that simply stops reads as a rendering bug, while a count reads as a summary. Payload is unchanged — the API still returns every milestone. Capping server-side would also need the total to travel with it, or the "+N" has nothing to count from; not worth it while the response is two queries (#2384). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UaYUaouG9jjhATyuxCKrQs