my prefect server setup prefect-metrics.waow.tech
python orchestration
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

enforce 4-section 2x2 grid for briefing cards

- curate prompt: exactly 4 sections, 4-6 items each, no highlights,
all normal priority
- frontend: cap sections at 4, remove orphan/highlight/colSpan logic

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

+13 -38
+7 -16
flows/curate.py
··· 16 16 SYSTEM_PROMPT = """\ 17 17 you are a dashboard curator for a software developer's issue tracker. 18 18 given a list of scored items from github and tangled.org, produce a briefing 19 - with 2-5 themed sections. group by actionability, not by source. 19 + with exactly 4 themed sections. group by actionability, not by source. 20 + 21 + the layout is a 2x2 grid, so always produce exactly 4 sections. 22 + keep each section to 4-6 items max — be selective, not exhaustive. 20 23 21 24 section titles should be lowercase, short, action-oriented: 22 25 "needs review", "going stale", "quick wins", "watching" ··· 26 29 27 30 ## visual styling 28 31 29 - each section has accent, icon, and priority fields to control presentation. 32 + each section has accent and priority fields to control presentation. 30 33 31 34 accent colors — pick the one that matches the section's mood: 32 35 - red: urgent, blocked, overdue items ··· 35 38 - sky: informational, watching, low-urgency tracking 36 39 - violet: features, enhancements, new ideas 37 40 38 - icons — pick one per section: 39 - - alert: urgent/blocked sections 40 - - clock: stale or time-sensitive sections 41 - - check: ready/completed/quick-win sections 42 - - eye: watching/tracking sections 43 - - bolt: quick wins, fast actions 44 - - bookmark: reference, docs, informational 41 + priority — all sections should use "normal" for the 2x2 grid layout. 45 42 46 - priority — controls layout size: 47 - - high: most important section, spans full width (use at most 1 per briefing) 48 - - normal: default column layout 49 - - low: compact, de-emphasized 50 - 51 - set highlight: true on at most ~3 items total across all sections. 52 - use it for the single most actionable or urgent item in a section. 43 + do not set highlight on any items. 53 44 """ 54 45 55 46 def make_agent(api_key: str) -> PrefectAgent[Briefing]:
+1 -5
web/src/lib/briefing-styles.ts
··· 8 8 * bg-red-950/40 bg-amber-950/40 bg-emerald-950/40 bg-sky-950/40 bg-violet-950/40 9 9 * bg-red-400 bg-amber-400 bg-emerald-400 bg-sky-400 bg-violet-400 10 10 * border-l-2 border-l-3 border-l-4 11 - * sm:col-span-2 bg-white/5 12 11 */ 13 12 14 13 export const ACCENT_STYLES: Record< ··· 60 59 61 60 export const PRIORITY_LAYOUT: Record< 62 61 SectionPriority, 63 - { colSpan: string; titleSize: string; padding: string; borderWidth: string } 62 + { titleSize: string; padding: string; borderWidth: string } 64 63 > = { 65 64 high: { 66 - colSpan: 'sm:col-span-2', 67 65 titleSize: 'text-base', 68 66 padding: 'px-5 py-4', 69 67 borderWidth: 'border-l-4' 70 68 }, 71 69 normal: { 72 - colSpan: '', 73 70 titleSize: 'text-sm', 74 71 padding: 'px-5 py-4', 75 72 borderWidth: 'border-l-3' 76 73 }, 77 74 low: { 78 - colSpan: '', 79 75 titleSize: 'text-xs', 80 76 padding: 'px-4 py-3', 81 77 borderWidth: 'border-l-2'
+5 -17
web/src/lib/components/Briefing.svelte
··· 8 8 9 9 let cardMap = $derived(new Map(cards.map((c) => [c.id, c]))); 10 10 11 - /** sections that don't already span full width */ 12 - let normalSections = $derived( 13 - briefing?.sections.filter((s) => (s.priority ?? 'normal') !== 'high') ?? [] 14 - ); 15 - let lastNormalTitle = $derived( 16 - normalSections.length % 2 === 1 ? normalSections[normalSections.length - 1]?.title : null 17 - ); 11 + /** cap at 4 sections for 2x2 grid */ 12 + let sections = $derived(briefing?.sections.slice(0, 4) ?? []); 18 13 19 14 function urlFor(item: BriefingItem): string | null { 20 15 return cardMap.get(item.item_id)?.url ?? null; ··· 63 58 <h2 class="text-xl font-semibold text-gray-100">{briefing.headline}</h2> 64 59 65 60 <div class="grid gap-4 sm:grid-cols-2"> 66 - {#each briefing.sections as section (section.title)} 61 + {#each sections as section (section.title)} 67 62 {@const accent = ACCENT_STYLES[section.accent ?? 'sky']} 68 63 {@const layout = PRIORITY_LAYOUT[section.priority ?? 'normal']} 69 - {@const isOrphan = section.title === lastNormalTitle} 70 - {@const colSpan = layout.colSpan || (isOrphan ? 'sm:col-span-2' : '')} 71 64 72 65 <div 73 - class="rounded-lg {accent.bgTint} {accent.border} {layout.borderWidth} {layout.padding} {colSpan} space-y-3" 66 + class="rounded-lg {accent.bgTint} {accent.border} {layout.borderWidth} {layout.padding} space-y-3" 74 67 > 75 68 <div class="flex items-center gap-2"> 76 69 <span class="h-2 w-2 shrink-0 rounded-full {accent.dot}"></span> ··· 86 79 {#each section.items as item (item.item_id)} 87 80 {@const url = urlFor(item)} 88 81 {@const parsed = parseItemId(item.item_id)} 89 - {@const highlighted = item.highlight ?? false} 90 - <li 91 - class="text-sm flex items-center gap-2 {highlighted 92 - ? 'bg-white/5 rounded px-1.5 py-0.5 -mx-1.5 text-gray-100 font-medium' 93 - : 'text-gray-300'}" 94 - > 82 + <li class="text-sm flex items-center gap-2 text-gray-300"> 95 83 {#if parsed} 96 84 <a 97 85 href={repoUrl(parsed)}