- 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>
204 lines
14 KiB
JavaScript
204 lines
14 KiB
JavaScript
// 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 3–12', p50: 'Mar 5–8', 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 3–12', 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 3–12' },
|
||
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],
|
||
},
|
||
};
|