Docs/dolt plan clarifications #1

Merged
dennisthiessen merged 34 commits from docs/dolt-plan-clarifications into main 2026-07-23 13:27:08 +02:00
Showing only changes of commit 103b182598 - Show all commits
@@ -10,39 +10,31 @@ interface FundamentalsPanelProps {
data: FundamentalResponse; data: FundamentalResponse;
} }
/** Positive / neutral / negative styling, always paired with the read text. */ /** Favorable / neutral / adverse — always paired with the read text. */
type Tone = 'up' | 'flat' | 'down'; type Tone = 'good' | 'flat' | 'bad';
const TONE_TEXT: Record<Tone, string> = { // Horizon tokens (native to the rest of the product).
up: 'text-emerald-400', const HZ = {
flat: 'text-gray-400', text: '#EDEEF3',
down: 'text-rose-400', muted: '#9AA0B0',
}; track: '#5D6373',
const TONE_CELL: Record<Tone, string> = { fav: '#6EC9DB', // cyan
up: 'bg-emerald-400/10 text-emerald-200 ring-emerald-400/20', adv: '#EF9182', // coral
flat: 'bg-white/5 text-gray-200 ring-white/10',
down: 'bg-rose-400/10 text-rose-200 ring-rose-400/20',
};
const TONE_BAR: Record<Tone, string> = {
up: 'bg-emerald-400',
flat: 'bg-gray-400',
down: 'bg-rose-400',
}; };
const toneColor = (t: Tone) => (t === 'good' ? HZ.fav : t === 'bad' ? HZ.adv : HZ.muted);
const POSITIVE_READS = new Set([ const POSITIVE_READS = new Set([
'accelerating', 'improving', 'above peers', 'buying back', 'attractively valued', 'accelerating', 'improving', 'above peers', 'above own average', 'buying back',
'conservative leverage', 'attractively valued', 'conservative leverage',
]); ]);
const NEGATIVE_READS = new Set([ const NEGATIVE_READS = new Set([
'decelerating', 'deteriorating', 'priced above peers', 'elevated leverage', 'decelerating', 'deteriorating', 'priced above peers', 'elevated leverage', 'below peers',
'below peers',
]); ]);
function readTone(read: string | null | undefined): Tone { function readTone(read: string | null | undefined): Tone {
if (!read) return 'flat'; if (!read) return 'flat';
if (POSITIVE_READS.has(read)) return 'up'; if (POSITIVE_READS.has(read)) return 'good';
if (NEGATIVE_READS.has(read)) return 'down'; if (NEGATIVE_READS.has(read)) return 'bad';
if (read.includes('dilution')) return 'down'; if (read.includes('dilution')) return 'bad';
return 'flat'; return 'flat';
} }
@@ -62,9 +54,11 @@ function money(v: number | null | undefined): string {
if (abs >= 1e6) return `$${(v / 1e6).toFixed(1)}M`; if (abs >= 1e6) return `$${(v / 1e6).toFixed(1)}M`;
return `$${v.toFixed(0)}`; return `$${v.toFixed(0)}`;
} }
function signedPp(v: number): string {
return `${v >= 0 ? '+' : ''}${Math.round(v * 10) / 10}pp`;
}
/** Parse a YYYY-MM-DD string as a LOCAL calendar date (avoids the UTC-midnight /** Parse YYYY-MM-DD as a LOCAL calendar date (no UTC off-by-one west of UTC). */
* off-by-one that shows the previous day west of UTC). */
function parseLocalDate(s: string): Date { function parseLocalDate(s: string): Date {
const [y, m, d] = s.split('-').map(Number); const [y, m, d] = s.split('-').map(Number);
return new Date(y, (m ?? 1) - 1, d ?? 1); return new Date(y, (m ?? 1) - 1, d ?? 1);
@@ -81,22 +75,22 @@ export function FundamentalsPanel({ data }: FundamentalsPanelProps) {
const reads = data.reads?.by_key ?? {}; const reads = data.reads?.by_key ?? {};
const val = data.valuation; const val = data.valuation;
const earnings = data.earnings; const earnings = data.earnings;
// per-metric provenance/freshness (all SEC snapshot metrics share the latest filing)
const provenance = (data.metrics ?? []).find((m) => m.period_end) ?? null; const provenance = (data.metrics ?? []).find((m) => m.period_end) ?? null;
const hasAny = (data.metrics ?? []).some((m) => m.value != null) || !!val || !!earnings?.next const hasAny = (data.metrics ?? []).some((m) => m.value != null) || !!val || !!earnings?.next
|| (earnings?.recent?.length ?? 0) > 0; || (earnings?.recent?.length ?? 0) > 0;
const tapeRows = [ // Operating trend: value vs a truthful reference (prior quarter / prior avg / zero).
{ key: 'revenue_growth_yoy', label: 'Revenue growth', fmt: pct }, const trendRows: { key: string; label: string; kind: 'growth' | 'margin' | 'share' }[] = [
{ key: 'eps_growth_yoy', label: 'EPS growth', fmt: pct }, { key: 'revenue_growth_yoy', label: 'Revenue growth', kind: 'growth' },
{ key: 'operating_margin', label: 'Operating margin', fmt: pct }, { key: 'eps_growth_yoy', label: 'EPS growth', kind: 'growth' },
{ key: 'fcf_margin', label: 'FCF margin', fmt: pct }, { key: 'operating_margin', label: 'Operating margin', kind: 'margin' },
{ key: 'share_count_change_yoy', label: 'Share count', fmt: pct }, { key: 'fcf_margin', label: 'FCF margin', kind: 'margin' },
{ key: 'share_count_change_yoy', label: 'Share count', kind: 'share' },
]; ];
const valuationRows: { // Valuation & balance: favorable percentile vs peer median.
const valueRows: {
label: string; value: number | null; industry: MetricIndustry | null; label: string; value: number | null; industry: MetricIndustry | null;
readKey: string; fmt: (v: number | null) => string; readKey: string; fmt: (v: number | null) => string;
}[] = [ }[] = [
@@ -110,41 +104,37 @@ export function FundamentalsPanel({ data }: FundamentalsPanelProps) {
return ( return (
<section className="glass p-5" aria-label="Fundamentals"> <section className="glass p-5" aria-label="Fundamentals">
<div className="mb-1 flex flex-wrap items-baseline justify-between gap-x-3 gap-y-0.5"> <div className="mb-1 flex flex-wrap items-baseline justify-between gap-x-3 gap-y-0.5">
<h3 className="text-xs font-medium uppercase tracking-widest text-gray-500">Fundamentals</h3> <h3 className="text-xs font-medium uppercase tracking-widest" style={{ color: HZ.muted }}>Fundamentals</h3>
{data.reads?.header && ( {data.reads?.header && (
<p className="text-[11.5px] text-gray-400 sm:text-right">{data.reads.header}</p> <p className="text-[11.5px] sm:text-right" style={{ color: HZ.muted }}>{data.reads.header}</p>
)} )}
</div> </div>
{!hasAny ? ( {!hasAny ? (
<p className="mt-3 text-sm text-gray-500">No fundamentals reported yet.</p> <p className="mt-3 text-sm" style={{ color: HZ.muted }}>No fundamentals reported yet.</p>
) : ( ) : (
<> <>
<EarningsStrip earnings={earnings} /> <EarningsStrip earnings={earnings} />
{/* Quarter tape — the panel's signature device */} <div className="mt-4 grid gap-x-8 gap-y-5 sm:grid-cols-2">
<div className="mt-4"> <div>
<div className="mb-1.5 flex items-baseline justify-between"> <SectionLabel>Operating trend</SectionLabel>
<span className="text-[10px] font-medium uppercase tracking-widest text-gray-500">Quarter tape</span> <div className="mt-2 space-y-3.5">
<span className="hidden text-[10px] uppercase tracking-widest text-gray-600 sm:inline">Latest · read</span> {trendRows.map((r) => (
</div> <TrendRow key={r.key} label={r.label} kind={r.kind}
<div className="space-y-2 sm:space-y-1.5"> metric={metrics[r.key]} read={reads[r.key]} />
{tapeRows.map((row) => (
<TapeRow key={row.key} label={row.label} metric={metrics[row.key]}
read={reads[row.key]} fmt={row.fmt} />
))} ))}
</div> </div>
</div> </div>
<div>
{/* Balance & valuation — restrained peer strips */} <SectionLabel>Valuation &amp; balance</SectionLabel>
<div className="mt-4"> <div className="mt-2 space-y-3.5">
<span className="text-[10px] font-medium uppercase tracking-widest text-gray-500">Balance &amp; valuation</span> {valueRows.map((r) => (
<div className="mt-1.5 space-y-2.5 sm:space-y-2"> <ValueRow key={r.label} {...r} read={reads[r.readKey]} />
{valuationRows.map((row) => (
<ValuationRow key={row.label} {...row} read={reads[row.readKey]} />
))} ))}
</div> </div>
</div> </div>
</div>
<Provenance provenance={provenance} priceDate={val?.price_date ?? null} <Provenance provenance={provenance} priceDate={val?.price_date ?? null}
marketCap={val?.market_cap_est ?? null} /> marketCap={val?.market_cap_est ?? null} />
@@ -154,90 +144,147 @@ export function FundamentalsPanel({ data }: FundamentalsPanelProps) {
); );
} }
function TapeRow({ label, metric, read, fmt }: { function SectionLabel({ children }: { children: React.ReactNode }) {
label: string; metric: MetricItem | undefined; read: string | null | undefined; return <span className="text-[10px] font-medium uppercase tracking-widest" style={{ color: HZ.muted }}>{children}</span>;
fmt: (v: number | null) => string; }
// ---- operating-trend row: value vs reference, shown as a delta rail ----------
function TrendRow({ label, kind, metric, read }: {
label: string; kind: 'growth' | 'margin' | 'share';
metric: MetricItem | undefined; read: string | null | undefined;
}) { }) {
const history = metric?.history ?? [];
const tone = readTone(read); const tone = readTone(read);
const cells = ( const value = metric?.value ?? null;
<div className="flex justify-end gap-1" role="img" const history = (metric?.history ?? []).map((h) => h.value).filter((v): v is number => v != null);
aria-label={`${label} last ${history.length}: ${history.map((h) => fmt(h.value)).join(', ') || 'no data'}`}>
{history.length === 0 && <span className="num text-xs text-gray-600"></span>} // reference + neutral band per the backend's deterministic rules
{history.map((h, i) => { let ref: number | null = null;
const latest = i === history.length - 1; let refLabel = '';
let halfRange = 8;
let neutral = 2;
if (kind === 'growth') {
ref = history.length >= 2 ? history[history.length - 2] : null;
refLabel = ref != null ? `prior ${pct(ref)}` : '';
halfRange = 8; neutral = 2;
} else if (kind === 'margin') {
const prior = history.slice(0, -1);
ref = prior.length ? prior.reduce((a, b) => a + b, 0) / prior.length : null;
refLabel = ref != null ? `avg ${pct(ref)}` : '';
halfRange = 4; neutral = 1;
} else {
ref = 0; refLabel = 'vs zero'; halfRange = 5; neutral = 1;
}
const delta = value != null && ref != null ? value - ref : null;
return ( return (
<span key={i} <div>
className={`num rounded px-1.5 py-0.5 text-[11px] ring-1 ${latest ? TONE_CELL[tone] : 'bg-white/5 text-gray-400 ring-white/5'}`}> <div className="flex items-baseline justify-between gap-2">
{fmt(h.value)} <span className="truncate text-sm" style={{ color: HZ.muted }}>{label}</span>
</span> <span className="num text-sm" style={{ color: HZ.text }}>{pct(value)}</span>
);
})}
</div> </div>
); <div className="text-[11.5px] font-medium" style={{ color: toneColor(tone) }}>{read ?? '—'}</div>
return ( {delta != null ? (
<div className="sm:grid sm:grid-cols-[minmax(0,7rem)_1fr_auto] sm:items-center sm:gap-3"> <div className="mt-1 flex items-center gap-2">
{/* mobile: label + read on one line, cells below (avoids narrow-width overflow) */} <span className="w-20 shrink-0 text-[10px]" style={{ color: HZ.muted }}>{refLabel}</span>
<div className="flex items-baseline justify-between gap-2 sm:hidden"> <DeltaRail delta={delta} halfRange={halfRange} neutral={neutral} tone={tone}
<span className="text-sm text-gray-400">{label}</span> ariaLabel={`${label} ${pct(value)}, ${refLabel || 'reference'}, change ${signedPp(delta)}, ${read ?? 'no read'}`} />
<span className={`text-[11.5px] font-medium ${TONE_TEXT[tone]}`}>{read ?? '—'}</span> <span className="num w-14 shrink-0 text-right text-[10px]" style={{ color: toneColor(tone) }}>{signedPp(delta)}</span>
</div> </div>
<span className="hidden truncate text-sm text-gray-400 sm:block">{label}</span> ) : (
<div className="mt-1 sm:mt-0">{cells}</div> value == null && <div className="mt-1 text-[10px]" style={{ color: HZ.track }}>n/a</div>
<span className={`hidden text-right text-[11.5px] font-medium sm:block sm:w-24 ${TONE_TEXT[tone]}`}> )}
{read ?? '—'}
</span>
</div> </div>
); );
} }
function ValuationRow({ label, value, industry, read, fmt }: { /** A comparison rail centered on a reference line (not a progress bar): a dot at
* the current delta with a bar connecting it back to the reference. */
function DeltaRail({ delta, halfRange, neutral, tone, ariaLabel }: {
delta: number; halfRange: number; neutral: number; tone: Tone; ariaLabel: string;
}) {
const clamped = Math.max(-halfRange, Math.min(halfRange, delta));
const pos = 50 + (clamped / halfRange) * 50; // %
const barLeft = Math.min(50, pos);
const barWidth = Math.abs(pos - 50);
const bandHalf = (neutral / halfRange) * 50;
const color = toneColor(tone);
return (
<span className="relative block h-1.5 flex-1 rounded-full" role="img" aria-label={ariaLabel}
style={{ background: 'rgba(93,99,115,0.22)' }}>
<span className="absolute inset-y-0 rounded-full" aria-hidden
style={{ left: `${50 - bandHalf}%`, width: `${2 * bandHalf}%`, background: 'rgba(93,99,115,0.4)' }} />
<span className="absolute inset-y-[-2px] w-px" aria-hidden style={{ left: '50%', background: 'rgba(237,238,243,0.55)' }} />
<span className="absolute inset-y-0 rounded-full" aria-hidden style={{ left: `${barLeft}%`, width: `${barWidth}%`, background: color }} />
<span className="absolute top-1/2 h-2.5 w-2.5 -translate-x-1/2 -translate-y-1/2 rounded-full" aria-hidden
style={{ left: `${pos}%`, background: color, boxShadow: '0 0 0 2px #11131C' }} />
</span>
);
}
// ---- valuation/balance row: favorable percentile vs peer median -------------
function ValueRow({ label, value, industry, read, fmt }: {
label: string; value: number | null; industry: MetricIndustry | null; label: string; value: number | null; industry: MetricIndustry | null;
read: string | null | undefined; fmt: (v: number | null) => string; read: string | null | undefined; fmt: (v: number | null) => string;
}) { }) {
const tone = readTone(read); const tone = readTone(read);
return ( return (
<div className="grid grid-cols-[minmax(0,8.5rem)_auto_1fr] items-center gap-x-3 gap-y-0.5"> <div>
<span className="truncate text-sm text-gray-400">{label}</span> <div className="flex items-baseline justify-between gap-2">
<span className="num text-sm text-gray-100">{fmt(value)}</span> <span className="truncate text-sm" style={{ color: HZ.muted }}>{label}</span>
<div className="flex items-center justify-end gap-2 text-right"> <span className="num text-sm" style={{ color: HZ.text }}>{fmt(value)}</span>
{industry ? ( </div>
{value == null ? (
<div className="mt-1 text-[10px]" style={{ color: HZ.track }}>n/a</div>
) : industry ? (
<> <>
{/* visible compact peer context (also the only peer info on mobile) */} <div className="text-[11.5px] font-medium" style={{ color: toneColor(tone) }}>{read ?? 'in line'}</div>
<span className="text-[11px] text-gray-500">med {fmt(industry.median)} · {industry.peer_count}p</span> <div className="mt-1 flex items-center gap-2">
<PercentileStrip industry={industry} tone={tone} label={label} fmt={fmt} /> <span className="w-3 text-[9px]" style={{ color: HZ.track }}>0</span>
<span className={`text-[11.5px] font-medium ${TONE_TEXT[tone]}`}>{read ?? 'in line'}</span> <PercentileRail percentile={industry.favorable_percentile} tone={tone}
ariaLabel={`${label}: ${industry.favorable_percentile}th favorable percentile vs ${industry.label}, median ${fmt(industry.median)}, ${industry.peer_count} peers`} />
<span className="w-6 text-right text-[9px]" style={{ color: HZ.track }}>100</span>
</div>
<div className="mt-0.5 pl-5 text-[10px]" style={{ color: HZ.muted }}>
median {fmt(industry.median)} · {industry.peer_count} peers
</div>
</> </>
) : ( ) : (
<span className="text-[11px] text-gray-600">peers n/a</span> <div className="text-[11px]" style={{ color: HZ.track }}>peers n/a</div>
)} )}
</div> </div>
</div>
); );
} }
function PercentileStrip({ industry, tone, label, fmt }: { /** Percentile rail 0-100 with the peer median fixed at 50; right = more favorable
industry: MetricIndustry; tone: Tone; label: string; fmt: (v: number | null) => string; * (the percentile is already polarity-aware). */
function PercentileRail({ percentile, tone, ariaLabel }: {
percentile: number; tone: Tone; ariaLabel: string;
}) { }) {
const p = Math.max(0, Math.min(100, industry.favorable_percentile)); const p = Math.max(0, Math.min(100, percentile));
const barLeft = Math.min(50, p);
const barWidth = Math.abs(p - 50);
const color = toneColor(tone);
return ( return (
<span <span className="relative block h-1.5 flex-1 rounded-full" role="img" aria-label={ariaLabel}
className="relative hidden h-1.5 w-16 rounded-full bg-white/10 sm:block" style={{ background: 'rgba(93,99,115,0.22)' }}>
role="img" <span className="absolute inset-y-[-2px] w-px" aria-hidden style={{ left: '50%', background: 'rgba(237,238,243,0.55)' }} />
aria-label={`${label}: ${industry.favorable_percentile}th favorable percentile vs ${industry.label}, median ${fmt(industry.median)}, ${industry.peer_count} peers`} <span className="absolute inset-y-0 rounded-full" aria-hidden style={{ left: `${barLeft}%`, width: `${barWidth}%`, background: color }} />
> <span className="absolute top-1/2 h-2.5 w-2.5 -translate-x-1/2 -translate-y-1/2 rounded-full" aria-hidden
<span className={`absolute inset-y-0 left-0 rounded-full ${TONE_BAR[tone]}`} style={{ width: `${p}%` }} /> style={{ left: `${p}%`, background: color, boxShadow: '0 0 0 2px #11131C' }} />
<span className="absolute inset-y-[-2px] left-1/2 w-px bg-white/40" aria-hidden />
</span> </span>
); );
} }
// ---- earnings + provenance (unchanged behavior) ----------------------------
function Provenance({ provenance, priceDate, marketCap }: { function Provenance({ provenance, priceDate, marketCap }: {
provenance: MetricItem | null; priceDate: string | null; marketCap: number | null; provenance: MetricItem | null; priceDate: string | null; marketCap: number | null;
}) { }) {
if (!provenance && !priceDate) return null; if (!provenance?.period_end && !priceDate) return null;
return ( return (
<p className="mt-4 border-t border-white/10 pt-2 text-[10px] leading-relaxed text-gray-600"> <p className="mt-4 border-t border-white/10 pt-2 text-[10px] leading-relaxed" style={{ color: HZ.track }}>
{provenance?.period_end && ( {provenance?.period_end && (
<>SEC filings · latest {shortDate(provenance.period_end)} <>SEC filings · latest {shortDate(provenance.period_end)}
{provenance.filed_date && <> (filed {shortDate(provenance.filed_date)})</>}</> {provenance.filed_date && <> (filed {shortDate(provenance.filed_date)})</>}</>
@@ -252,27 +299,25 @@ function Provenance({ provenance, priceDate, marketCap }: {
function EarningsStrip({ earnings }: { earnings: FundamentalResponse['earnings'] }) { function EarningsStrip({ earnings }: { earnings: FundamentalResponse['earnings'] }) {
const next = earnings?.next; const next = earnings?.next;
const recent = earnings?.recent ?? []; const recent = earnings?.recent ?? [];
const when = next const when = next ? (next.days_until === 0 ? 'today' : `${next.days_until}d`) : null;
? next.days_until === 0 ? 'today' : `in ${next.days_until}d`
: null;
return ( return (
<div className="mt-2 flex flex-wrap items-center justify-between gap-x-4 gap-y-1"> <div className="mt-2 flex flex-wrap items-center justify-between gap-x-4 gap-y-1">
<span className="text-sm text-gray-300"> <span className="text-sm" style={{ color: HZ.text }}>
<span className="text-gray-500">Next earnings </span> <span style={{ color: HZ.muted }}>Next earnings </span>
{next ? ( {next ? (
<> <>
{shortDate(next.date)} {shortDate(next.date)}
{' · '} {' · '}
<span className="uppercase">{next.session === 'unknown' ? 'TBD' : next.session}</span> <span className="uppercase">{next.session === 'unknown' ? 'TBD' : next.session}</span>
<span className="text-gray-500"> · {when}</span> <span style={{ color: HZ.muted }}> · {when}</span>
</> </>
) : ( ) : (
<span className="text-gray-500">no date</span> <span style={{ color: HZ.muted }}>no date</span>
)} )}
</span> </span>
{recent.length > 0 && ( {recent.length > 0 && (
<span className="flex items-center gap-1.5"> <span className="flex items-center gap-1.5">
<span className="text-[10px] uppercase tracking-widest text-gray-600">Last {recent.length}</span> <span className="text-[10px] uppercase tracking-widest" style={{ color: HZ.track }}>Last {recent.length}</span>
{recent.slice().reverse().map((e, i) => <EarningsBar key={i} e={e} />)} {recent.slice().reverse().map((e, i) => <EarningsBar key={i} e={e} />)}
</span> </span>
)} )}
@@ -282,15 +327,13 @@ function EarningsStrip({ earnings }: { earnings: FundamentalResponse['earnings']
function EarningsBar({ e }: { e: EarningsRecent }) { function EarningsBar({ e }: { e: EarningsRecent }) {
const beat = e.eps_actual != null && e.eps_estimate != null ? e.eps_actual - e.eps_estimate : null; const beat = e.eps_actual != null && e.eps_estimate != null ? e.eps_actual - e.eps_estimate : null;
const tone: Tone = beat == null ? 'flat' : beat > 0 ? 'up' : beat < 0 ? 'down' : 'flat'; const tone: Tone = beat == null ? 'flat' : beat > 0 ? 'good' : beat < 0 ? 'bad' : 'flat';
const label = beat == null ? 'n/a' : beat > 0 ? 'beat' : beat < 0 ? 'miss' : 'in line'; const label = beat == null ? 'n/a' : beat > 0 ? 'beat' : beat < 0 ? 'miss' : 'in line';
const arrow = beat == null ? '·' : beat > 0 ? '▲' : beat < 0 ? '▼' : ''; const arrow = beat == null ? '·' : beat > 0 ? '▲' : beat < 0 ? '▼' : '';
return ( return (
<span <span className="num text-xs" style={{ color: toneColor(tone) }}
className={`num rounded px-1 text-[10px] ring-1 ${TONE_CELL[tone]}`}
title={`${e.announce_date}: ${label}${e.surprise_pct != null ? ` ${e.surprise_pct > 0 ? '+' : ''}${e.surprise_pct}%` : ''}`} title={`${e.announce_date}: ${label}${e.surprise_pct != null ? ` ${e.surprise_pct > 0 ? '+' : ''}${e.surprise_pct}%` : ''}`}
aria-label={`${e.announce_date} ${label}`} aria-label={`${e.announce_date} ${label}`}>
>
{arrow} {arrow}
</span> </span>
); );