Scaffold CommiTea: yarn workspaces, Electron shell, core label schema, design system

- apps/desktop: electron-vite + React + Tailwind mapped onto design tokens
  (preflight off; tokens/base.css owns the reset); boots to a Reginald
  placeholder proving fonts/tokens/core wiring
- packages/core: pure TS; gitea label schema (est/*, p/*, deadline/hard)
  with pessimistic conflict resolution + 15 unit tests
- docs/design: full design handoff (tokens, 16 component contracts,
  interactive 14-screen prototype, Reginald voice rules)
- docs/PLAN.md: product plan (purity rule, pm-state repo, deterministic
  scheduler + Monte Carlo, directive log)
- Deliberate deviation from novelpad stack: no ElectricSQL/PGlite — local
  store is a rebuildable cache over gitea REST/webhooks (better-sqlite3
  in main process, arriving in P1)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Christian LeDoux
2026-07-07 20:42:46 -04:00
commit 7a5cacc54c
268 changed files with 15766 additions and 0 deletions

View File

@@ -0,0 +1,98 @@
// Milestone detail — scope, cone, issues; forecasts stay ranges
function MilestoneScreen({ onBack, onOpenIssue }) {
const DS = window.CommiTeaDesignSystem_20e63b;
const { Card, Tag, Badge, Button, Icon } = DS;
const cols = window.CT_DATA.columns;
const byState = (ids) => cols.flatMap((c) => c.issues.map((i) => ({ ...i, col: c.label }))).filter((i) => ids.includes(i.id));
const groups = [
{ label: 'Steeping', issues: byState([87, 84]) },
{ label: 'In review', issues: byState([92]) },
{ label: 'Queued', issues: byState([102, 103, 99, 96, 78]) },
{ label: 'Done', issues: byState([71, 69, 65]), muted: true },
];
const Stat = ({ label, value, tone }) => (
<div style={{ flex: 1, padding: '12px 18px', borderRight: '1px solid var(--line-1)' }}>
<div style={{ font: 'var(--text-overline)', letterSpacing: 'var(--letter-spacing-wide)', textTransform: 'uppercase', color: 'var(--ink-3)', marginBottom: 5 }}>{label}</div>
<div style={{ font: `500 14px var(--font-mono)`, color: tone || 'var(--ink-1)', whiteSpace: 'nowrap' }}>{value}</div>
</div>
);
return (
<div style={{ display: 'flex', flexDirection: 'column', gap: 16 }}>
<div>
<button type="button" onClick={onBack} style={{
display: 'inline-flex', alignItems: 'center', gap: 6, background: 'none', border: 'none',
font: '500 12.5px var(--font-sans)', color: 'var(--ink-2)', cursor: 'pointer', padding: '2px 0', marginBottom: 10,
}}>
<Icon name="arrow-left" size={14} /> Runway
</button>
<header style={{ borderBottom: 'var(--rule-double)', paddingBottom: 14, display: 'flex', alignItems: 'flex-start', gap: 16 }}>
<div style={{ flex: 1, minWidth: 0 }}>
<p style={{ font: '400 12px var(--font-mono)', color: 'var(--ink-3)', margin: '0 0 6px', display: 'inline-flex', alignItems: 'center', gap: 6 }}>
<Icon name="milestone" size={13} /> milestone · due Mar 15 · soft — scope may flex
</p>
<h1 style={{ font: 'var(--text-display)', color: 'var(--ink-1)', margin: 0 }}>Beta</h1>
<div style={{ display: 'flex', alignItems: 'center', gap: 8, marginTop: 10 }}>
<Badge tone="ok" dot>ahead of forecast</Badge>
<span style={{ font: '400 12px var(--font-mono)', color: 'var(--ink-2)', whiteSpace: 'nowrap' }}>80% Mar 312</span>
</div>
</div>
<Button variant="secondary" icon="arrow-up-right">Open in Gitea</Button>
</header>
</div>
{/* stats strip */}
<Card flush>
<div style={{ display: 'flex' }}>
<Stat label="Scope" value="42 issues · est 61d" />
<Stat label="Done" value="24 · 57%" />
<Stat label="Forecast" value="80% Mar 312" />
<div style={{ flex: 1, padding: '12px 18px' }}>
<div style={{ font: 'var(--text-overline)', letterSpacing: 'var(--letter-spacing-wide)', textTransform: 'uppercase', color: 'var(--ink-3)', marginBottom: 5 }}>Drift · 7d</div>
<div style={{ font: '500 14px var(--font-mono)', color: 'var(--ok)', whiteSpace: 'nowrap' }}>2d · cone narrowed</div>
</div>
</div>
</Card>
<div style={{ display: 'grid', gridTemplateColumns: '1.2fr 1fr', gap: 14, alignItems: 'start' }}>
<Card overline="Burn-up" title={<>80% this lands <span style={{ whiteSpace: 'nowrap' }}>Mar 312</span></>} jade>
<window.BurnUpCone />
<p style={{ font: 'var(--text-agent)', color: 'var(--ink-2)', margin: '10px 0 0' }}>
Comfortably ahead. Beta needs #87 more than it needs my commentary.
</p>
</Card>
<Card overline="Issues" flush>
<div>
{groups.map((g) => (
<div key={g.label}>
<div style={{ display: 'flex', alignItems: 'center', gap: 7, padding: '10px 20px 6px' }}>
<span style={{ font: 'var(--text-overline)', letterSpacing: 'var(--letter-spacing-wide)', textTransform: 'uppercase', color: 'var(--ink-3)' }}>{g.label}</span>
<span style={{ font: '400 11px var(--font-mono)', color: 'var(--ink-3)' }}>{g.issues.length}</span>
</div>
{g.issues.map((i) => (
<div key={i.id}
onClick={() => onOpenIssue({ id: i.id, title: i.title, labels: i.labels, days: i.days })}
style={{
display: 'flex', alignItems: 'center', gap: 8, padding: '7px 20px', cursor: 'pointer',
opacity: g.muted ? 0.6 : 1,
}}
onMouseEnter={(e) => { e.currentTarget.style.background = 'var(--paper-2)'; }}
onMouseLeave={(e) => { e.currentTarget.style.background = 'transparent'; }}
>
<span style={{ font: '400 11px var(--font-mono)', color: 'var(--ink-3)', width: 34, flexShrink: 0 }}>#{i.id}</span>
<span style={{ font: 'var(--text-small)', color: 'var(--ink-1)', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', flex: 1 }}>{i.title}</span>
{(i.labels || []).filter((l) => l.startsWith('est/')).map((l) => <Tag key={l} label={l} />)}
</div>
))}
</div>
))}
</div>
</Card>
</div>
</div>
);
}
Object.assign(window, { MilestoneScreen });