Files
commitea/docs/design/ui_kits/app/data.js
Christian LeDoux 7a5cacc54c 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>
2026-07-07 20:42:46 -04:00

204 lines
14 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// CommiTea fixture data — human-authored intent (gitea) + machine-derived state (sidecar)
window.CT_DATA = {
today: 'Tuesday 7 July 2026',
milestone: {
name: 'Beta',
due: '2026-03-15',
hard: false,
forecast: { p80: 'Mar 312', p50: 'Mar 58', drift: '+0d' },
},
focus: {
now: {
id: 87, title: 'Fix lifecycle inference on merge events',
labels: ['est/2d', 'p/1'], steeping: '4d',
rationale: 'It blocks #91 and #92. I\u2019d take it first \u2014 the critical path agrees with me.',
},
next: {
id: 91, title: 'Webhook listener: reconcile on reconnect',
labels: ['est/3d', 'p/2'],
rationale: 'Ready the moment #87 lands. The estimate is yours; history says add a day.',
},
later: {
id: 78, title: 'Calibration store cold-start distributions',
labels: ['est/5d', 'p/3'],
rationale: 'Nothing depends on it yet. It can steep.',
},
},
columns: [
{ id: 'diagnosis', label: 'Diagnosis', issues: [
{ id: 102, title: 'Scheduler ignores standing allocation slices', labels: ['p/2'], who: 'SM' },
{ id: 103, title: 'Dark theme: cone fill too faint', labels: ['p/4'], who: 'SM' },
]},
{ id: 'triage', label: 'Triage', issues: [
{ id: 99, title: 'Directive log: conflict-free merge test', labels: ['est/1d', 'p/2'], who: 'SM' },
{ id: 96, title: 'Label schema bootstrap for new repos', labels: ['est/2d', 'p/3'], who: 'AK' },
{ id: 78, title: 'Calibration store cold-start distributions', labels: ['est/5d', 'p/3'], who: 'SM' },
]},
{ id: 'steeping', label: 'Steeping', issues: [
{ id: 87, title: 'Fix lifecycle inference on merge events', labels: ['est/2d', 'p/1'], who: 'SM', blocked: false, days: '4d' },
{ id: 84, title: 'Capacity model: focus factor per person', labels: ['est/3d', 'p/2'], who: 'AK', days: '1d' },
]},
{ id: 'review', label: 'In review', issues: [
{ id: 92, title: 'Monte Carlo engine: percentile bands', labels: ['est/5d', 'p/1'], who: 'SM', pr: '#141' },
]},
{ id: 'done', label: 'Done', issues: [
{ id: 71, title: 'Gitea client: token auth + retries', labels: ['est/2d', 'p/2'], who: 'SM' },
{ id: 69, title: 'pm-state repo bootstrap', labels: ['est/1d', 'p/1'], who: 'SM' },
{ id: 65, title: 'Electron shell + window state', labels: ['est/3d', 'p/2'], who: 'AK' },
]},
],
runway: [
{ name: 'Beta', due: 'Mar 15', hard: false, p80: 'Mar 312', pos: 0.62, spread: 0.18, tone: 'ok', note: 'ahead' },
{ name: 'Pilot-ready', due: 'Apr 30', hard: true, p80: 'Apr 21 May 9', pos: 0.94, spread: 0.26, tone: 'warn', note: 'drifting' },
{ name: 'v1.0', due: 'Jun 12', hard: false, p80: 'May 30 Jun 20', pos: 0.88, spread: 0.3, tone: 'ok', note: 'on watch' },
],
capacity: [
{ who: 'Stephen', hours: '5.2h/day', focus: 0.65, slices: 'dev 70% · pilots 20% · compliance 10%' },
{ who: 'Ana K.', hours: '3.8h/day', focus: 0.8, slices: 'dev 100%' },
],
chat: [
{ from: 'agent', text: 'Morning service. Two things drifted overnight; one needs your opinion.' },
{ from: 'agent', text: '#84 grew a dependency on #92 \u2014 I\u2019ve reordered. And #87 has been steeping for four days; it blocks two others. Worth a look.' },
{ from: 'user', text: 'Push the calibration work to next week, pilots come first' },
{ from: 'agent', text: 'Done \u2014 #78 moves to next week. Milestone Beta is unmoved; the cone doesn\u2019t care for calibration either. Logged as a directive.' },
],
cannedReply: 'Noted and logged as a directive. The scheduler is re-running \u2014 I\u2019ll show you the consequence diff in a moment.',
standup: {
date: 'Tuesday 7 July 2026',
drift: [
{ tone: 'warn', text: '#84 grew a dependency on #92 overnight.', delta: 'reordered \u00b7 no date impact' },
{ tone: 'warn', text: '#92 has sat in review for two days.', delta: 'Beta 80% +1d if idle past Thu' },
{ tone: 'ok', text: 'Webhook outage 02:14\u201302:31; full reconcile ran.', delta: 'nothing lost' },
],
plan: [
{ who: 'Stephen', pick: '#87', title: 'Fix lifecycle inference on merge events', why: 'It blocks two others and the critical path runs straight through it.' },
{ who: 'Ana K.', pick: '#84', title: 'Capacity model: focus factor per person', why: 'Already steeping \u2014 finish it before anything new is poured.' },
],
nag: { id: 87, days: '4d', blocks: ['#91', '#92'], text: 'Four days is a long steep. It blocks #91 and #92 \u2014 worth a look before it stains.' },
},
calibration: {
n: 27,
active: true,
labels: [
{ label: 'est/1d', n: 8, median: '1.1d', bias: 8 },
{ label: 'est/2d', n: 9, median: '2.4d', bias: 18 },
{ label: 'est/3d', n: 6, median: '3.7d', bias: 22 },
{ label: 'est/5d', n: 3, median: '6.5d', bias: 30 },
{ label: 'est/8d', n: 1, median: '8.5d', bias: null },
],
people: [
{ who: 'Stephen', n: 19, bias: 21, note: 'optimism grows with ticket size' },
{ who: 'Ana K.', n: 8, bias: 9, note: 'close to honest \u2014 suspicious' },
],
scatter: [
[1, 1], [1, 1.2], [1, 0.9], [1, 1.5], [1, 1.1], [1, 0.8], [1, 1.3], [1, 1.2],
[2, 2], [2, 2.5], [2, 3.1], [2, 2.2], [2, 2.6], [2, 1.8], [2, 2.4], [2, 2.9], [2, 2.3],
[3, 3.5], [3, 4.1], [3, 3.1], [3, 4.6], [3, 3.6], [3, 3.9],
[5, 6.1], [5, 7.2], [5, 6.4],
[8, 8.5],
],
fit: 1.18,
effect: { raw: '42d of estimates', banded: '80% band 46\u201353d', p50: '48d' },
},
inbox: [
{ id: 1, day: 'Today', type: 'drift', icon: 'chart-line', tone: 'warn', text: 'Beta\u2019s 80% window moved', detail: 'Mar 3\u201312 \u2192 Mar 5\u201314 \u00b7 directive #007', time: '09:14', unread: true, to: 'directives' },
{ id: 2, day: 'Today', type: 'mention', icon: 'message-square', tone: 'info', who: 'Ana K.', text: 'mentioned you on #92', detail: '\u201cpercentile bands ready for a second pair of eyes\u201d', time: '08:52', unread: true, issue: { id: 92, title: 'Monte Carlo engine: percentile bands', labels: ['est/5d', 'p/1'] } },
{ id: 3, day: 'Today', type: 'nag', icon: 'clock', tone: 'warn', text: '#87 is steeping \u00b7 4d', detail: 'blocks #91 and #92 \u2014 worth a look before it stains', time: '07:00', unread: true, issue: { id: 87, title: 'Fix lifecycle inference on merge events', labels: ['est/2d', 'p/1'], days: '4d' } },
{ id: 4, day: 'Yesterday', type: 'review', icon: 'git-pull-request', tone: 'info', text: 'PR #141 awaits review', detail: '#92 \u00b7 idle two days', time: '16:20', unread: false, issue: { id: 92, title: 'Monte Carlo engine: percentile bands', labels: ['est/5d', 'p/1'] } },
{ id: 5, day: 'Yesterday', type: 'assignment', icon: 'user', tone: 'neutral', who: 'Ana K.', text: 'took #96 from the pot', detail: 'Label schema bootstrap for new repos', time: '11:03', unread: false, issue: { id: 96, title: 'Label schema bootstrap for new repos', labels: ['est/2d', 'p/3'] } },
{ id: 6, day: 'Yesterday', type: 'system', icon: 'refresh-cw', tone: 'ok', text: 'Webhook outage 02:14\u201302:31', detail: 'full reconcile ran \u00b7 nothing lost', time: '02:31', unread: false },
{ id: 7, day: 'Yesterday', type: 'milestone', icon: 'milestone', tone: 'ok', text: 'P2 \u2014 Scheduler closed two days early', detail: 'the calibration noticed. So did I.', time: '09:40', unread: false },
],
issueDetail: {
87: {
state: 'steeping',
assignee: 'Stephen',
milestone: 'Beta',
body: 'Squash-merges emit events out of order when CI runs long: the PR-closed webhook lands before the merge event, so lifecycle inference marks deploy before work-end and the actuals go negative. Regular merges infer correctly.',
comments: [
{ who: 'Ana K.', when: 'Feb 8 \u00b7 14:12', text: 'Repro: squash-merge only. Regular merges infer fine.' },
{ who: 'Stephen', when: 'Feb 9 \u00b7 09:30', text: 'Confirmed \u2014 the event order flips whenever CI takes more than ~5 minutes.' },
],
lifecycle: [
{ stage: 'Diagnosis', event: 'issue opened', when: 'Feb 2 \u00b7 09:14', icon: 'circle-dot', done: true },
{ stage: 'Triage', event: 'labeled est/2d \u00b7 milestoned Beta', when: 'Feb 3 \u00b7 10:02', icon: 'tag', done: true },
{ stage: 'Work start', event: 'first commit ref a41f09', when: 'Feb 6 \u00b7 11:47', icon: 'git-commit-horizontal', done: true },
{ stage: 'Deploy', event: 'PR merged', when: 'pending', icon: 'git-merge', done: false },
{ stage: 'Complete', event: 'issue closed', when: 'pending', icon: 'circle-check', done: false },
],
forecast: { p80: 'done Feb 11\u201313', note: 'from your est/2d history \u00b7 n=14' },
blocks: [91, 92],
blockedBy: [],
note: 'It blocks #91 and #92. I\u2019d take it first \u2014 the critical path agrees with me.',
},
},
directives: {
pending: {
seq: 7,
who: 'Stephen',
when: 'today 09:12',
what: 'Pilots before calibration \u2014 push #78 to next week.',
diff: [
{ tone: 'info', change: '#78 Calibration store', from: 'this week', to: 'wk of Feb 23' },
{ tone: 'warn', change: 'Beta \u00b7 80% window', from: 'Mar 3\u201312', to: 'Mar 5\u201314' },
{ tone: 'ok', change: "Today's plan", from: '#87', to: '#87 \u00b7 unchanged' },
],
},
entries: [
{ seq: 6, who: 'Stephen', when: 'Feb 8 \u00b7 16:40', what: 'Ana takes nothing new until #84 lands.', why: 'context thrash', status: 'applied', consequence: 'WIP capped \u00b7 v1.0 unmoved' },
{ seq: 5, who: 'Stephen', when: 'Feb 6 \u00b7 09:03', what: 'Ship Beta a week early.', why: 'board meeting', status: 'withdrawn', consequence: '80% would need scope \u22129d \u2014 withdrawn after diff' },
{ seq: 4, who: 'Stephen', when: 'Feb 3 \u00b7 11:21', what: 'deadline/hard on Pilot-ready.', why: 'contract date', status: 'applied', consequence: 'label applied \u00b7 runway flag raised' },
{ seq: 3, who: 'Stephen', when: 'Jan 28 \u00b7 08:47', what: 'Webhook work ahead of UI polish.', why: '', status: 'applied', consequence: '#91 +2 ranks \u00b7 Beta unmoved' },
{ seq: 2, who: 'Stephen', when: 'Jan 20 \u00b7 14:02', what: 'Estimates in days, never hours.', why: 'sanity', status: 'applied', consequence: 'label schema est/* confirmed' },
{ seq: 1, who: 'Stephen', when: 'Jan 19 \u00b7 09:00', what: 'CommiTea manages its own backlog.', why: 'dogfood', status: 'applied', consequence: 'stephen/commitea under management' },
],
},
gantt: {
// day offsets from Feb 2; chart spans 42 days (Feb 2 Mar 16)
days: 42,
weeks: [
{ at: 0, label: 'Feb 2' }, { at: 7, label: 'Feb 9' }, { at: 14, label: 'Feb 16' },
{ at: 21, label: 'Feb 23' }, { at: 28, label: 'Mar 2' }, { at: 35, label: 'Mar 9' }, { at: 41, label: 'Mar 15' },
],
today: 8,
band: { from: 29, to: 38, label: '80% · Mar 312' },
due: { at: 41, label: 'Beta due' },
rows: [
{ id: 71, title: 'Gitea client: token auth + retries', who: 'SM', state: 'done', start: 0, end: 4 },
{ id: 69, title: 'pm-state repo bootstrap', who: 'SM', state: 'done', start: 0, end: 1 },
{ id: 87, title: 'Fix lifecycle inference on merge events', who: 'SM', state: 'steeping', start: 4, end: 9, p80: 11, crit: true },
{ id: 92, title: 'Monte Carlo engine: percentile bands', who: 'SM', state: 'review', start: 9, end: 18, p80: 21, crit: true },
{ id: 91, title: 'Webhook listener: reconcile on reconnect', who: 'AK', state: 'scheduled', start: 9, end: 14, p80: 16 },
{ id: 99, title: 'Directive log: conflict-free merge test', who: 'SM', state: 'scheduled', start: 10, end: 11 },
{ id: 96, title: 'Label schema bootstrap for new repos', who: 'AK', state: 'scheduled', start: 14, end: 16 },
{ id: 84, title: 'Capacity model: focus factor per person', who: 'AK', state: 'scheduled', start: 18, end: 23, p80: 26, crit: true },
{ id: 102, title: 'Scheduler ignores standing allocation slices', who: 'SM', state: 'scheduled', start: 23, end: 25, p80: 28, crit: true },
{ id: 78, title: 'Calibration store cold-start distributions', who: 'SM', state: 'scheduled', start: 25, end: 30 },
],
},
deps: {
nodes: [
{ id: 71, title: 'Gitea client: token auth + retries', tags: ['est/2d'], state: 'done', col: 0, row: 0.2 },
{ id: 69, title: 'pm-state repo bootstrap', tags: ['est/1d'], state: 'done', col: 0, row: 1.9 },
{ id: 87, title: 'Fix lifecycle inference on merge events', tags: ['est/2d', 'p/1'], state: 'steeping', days: '4d', col: 1, row: 0.7, rationale: 'It blocks #91 and #92. I\u2019d take it first \u2014 the critical path agrees with me.' },
{ id: 99, title: 'Directive log: conflict-free merge test', tags: ['est/1d', 'p/2'], state: 'triage', col: 1, row: 1.9 },
{ id: 91, title: 'Webhook listener: reconcile on reconnect', tags: ['est/3d', 'p/2'], state: 'triage', col: 2, row: 0 },
{ id: 92, title: 'Monte Carlo engine: percentile bands', tags: ['est/5d', 'p/1'], state: 'review', col: 2, row: 1.4 },
{ id: 84, title: 'Capacity model: focus factor per person', tags: ['est/3d', 'p/2'], state: 'steeping', days: '1d', col: 3, row: 1.4 },
{ id: 102, title: 'Scheduler ignores standing allocation slices', tags: ['p/2'], state: 'diagnosis', col: 4, row: 1.4 },
],
milestone: { name: 'Beta', due: 'Mar 15', col: 5, row: 1.4 },
edges: [
{ from: 71, to: 87 },
{ from: 69, to: 99 },
{ from: 87, to: 91 },
{ from: 87, to: 92, crit: true },
{ from: 92, to: 84, crit: true },
{ from: 84, to: 102, crit: true },
{ from: 102, to: 'ms', crit: true },
],
critical: [87, 92, 84, 102],
unattached: [78, 96, 103, 65],
},
};