/** * Deterministic demo snapshot — the ONLY test fixture, served by the main process * when COMMITEA_E2E=1 so the e2e suite (and a no-token first look) runs WITHOUT a * live Gitea. It's gitea-shaped on purpose: the reconcile handlers return it and * the app renders it through the exact same view builders (schedule, forecast, * lifecycle, standup, runway) as real data. There are no screen-level mocks — this * is the single source of demo truth. Not shipped behavior; test infrastructure. */ import { type CapacityMember, type DependencyEdge, type DirectiveRecord, extractLabelFacts, type GiteaIssue, type GiteaMilestone, type LifecycleEvent, } from '@commitea/core' const HOST = 'https://gitea.demo.local' const OWNER = 'teatime' const REPO = 'commitea' /** Fixed clock so lifecycle math + forecasts are stable across runs. */ export const DEMO_TODAY = '2026-03-02T09:00:00Z' interface Seed { number: number title: string labels: string[] state?: 'open' | 'closed' milestone?: number assignee?: string events?: LifecycleEvent[] closedAt?: string createdAt?: string } const MILESTONES_RAW = [ { id: 1, title: 'Beta', description: 'Pilot-ready', dueOn: '2026-03-12T00:00:00Z', state: 'open' as const }, { id: 2, title: 'v1.0', description: 'General availability', dueOn: '2026-05-01T00:00:00Z', state: 'open' as const }, ] const MREF = (id: number) => { const m = MILESTONES_RAW.find((x) => x.id === id)! return { id: m.id, title: m.title, dueOn: m.dueOn } } // A small, believable backlog for a team building CommiTea. Timelines place issues // across the five lifecycle columns: bare → diagnosis, labeled → triage, a commit // ref → steeping, a PR ref → review, closed → done. const SEEDS: Seed[] = [ { number: 42, title: 'Fix lifecycle inference on merge events', labels: ['est/3d', 'p/1', 'deadline/hard'], milestone: 1, assignee: 'stephen', events: [{ type: 'triage', at: '2026-02-24T10:00:00Z' }, { type: 'commit', at: '2026-02-27T14:00:00Z' }] }, { number: 41, title: 'Monte Carlo forecast bands on the runway', labels: ['est/5d', 'p/1'], milestone: 1, assignee: 'ana', events: [{ type: 'triage', at: '2026-02-23T09:00:00Z' }, { type: 'commit', at: '2026-02-26T09:00:00Z' }, { type: 'pull', at: '2026-03-01T16:00:00Z' }] }, { number: 40, title: 'Encrypted token store in the main process', labels: ['est/2d', 'p/2'], milestone: 1, assignee: 'stephen', events: [{ type: 'triage', at: '2026-02-22T11:00:00Z' }] }, { number: 39, title: 'Deterministic scheduler with capacity lanes', labels: ['est/8d', 'p/2'], milestone: 1, assignee: 'ana', events: [{ type: 'triage', at: '2026-02-20T09:00:00Z' }, { type: 'commit', at: '2026-02-28T09:00:00Z' }] }, { number: 38, title: 'Standup ritual: overnight drift + today’s plan', labels: ['est/3d', 'p/2'], milestone: 1, assignee: 'stephen', events: [{ type: 'triage', at: '2026-02-25T09:00:00Z' }] }, { number: 37, title: 'Offline write-queue with replay on reconnect', labels: ['est/5d', 'p/3'], milestone: 2, assignee: 'ana', events: [{ type: 'triage', at: '2026-02-19T09:00:00Z' }] }, { number: 36, title: 'Calibration from closed-issue actuals', labels: ['est/3d', 'p/3'], milestone: 2, assignee: 'stephen', events: [{ type: 'triage', at: '2026-02-18T09:00:00Z' }] }, { number: 35, title: 'Reginald: agent loop over query_project', labels: ['est/8d', 'p/2'], milestone: 2, assignee: 'ana', events: [{ type: 'triage', at: '2026-02-17T09:00:00Z' }] }, { number: 34, title: 'Board columns from lifecycle events', labels: [], // bare → diagnosis events: [] }, { number: 33, title: 'Deep-link URIs across orgs', labels: [], // bare → diagnosis events: [] }, // shipped work — closed → done, feeds calibration actuals { number: 30, title: 'SQLite cache bootstrap', labels: ['est/3d', 'p/2'], milestone: 1, assignee: 'ana', state: 'closed', createdAt: '2026-02-10T09:00:00Z', closedAt: '2026-02-14T17:00:00Z', events: [{ type: 'triage', at: '2026-02-10T09:00:00Z' }, { type: 'commit', at: '2026-02-11T09:00:00Z' }, { type: 'pull', at: '2026-02-13T09:00:00Z' }, { type: 'close', at: '2026-02-14T17:00:00Z' }] }, { number: 29, title: 'Connection + target-repo config', labels: ['est/2d', 'p/2'], milestone: 1, assignee: 'stephen', state: 'closed', createdAt: '2026-02-09T09:00:00Z', closedAt: '2026-02-12T15:00:00Z', events: [{ type: 'triage', at: '2026-02-09T09:00:00Z' }, { type: 'commit', at: '2026-02-10T09:00:00Z' }, { type: 'close', at: '2026-02-12T15:00:00Z' }] }, { number: 24, title: 'Unified apply_changes mutation tool', labels: ['est/5d', 'p/1'], milestone: 1, assignee: 'ana', state: 'closed', createdAt: '2026-02-05T09:00:00Z', closedAt: '2026-02-11T12:00:00Z', events: [{ type: 'triage', at: '2026-02-05T09:00:00Z' }, { type: 'commit', at: '2026-02-06T09:00:00Z' }, { type: 'pull', at: '2026-02-10T09:00:00Z' }, { type: 'close', at: '2026-02-11T12:00:00Z' }] }, ] function toIssue(s: Seed): GiteaIssue { const state = s.state ?? 'open' return { number: s.number, title: s.title, body: '', state, labels: s.labels, facts: extractLabelFacts(s.labels), milestone: s.milestone ? MREF(s.milestone) : null, assignee: s.assignee ?? null, assignees: s.assignee ? [s.assignee] : [], createdAt: s.createdAt ?? '2026-02-20T09:00:00Z', updatedAt: DEMO_TODAY, closedAt: s.closedAt ?? null, url: `${HOST}/${OWNER}/${REPO}/issues/${s.number}`, } } export const DEMO_ISSUES: GiteaIssue[] = SEEDS.map(toIssue) export const DEMO_MILESTONES: GiteaMilestone[] = MILESTONES_RAW.map((m) => ({ id: m.id, title: m.title, description: m.description, dueOn: m.dueOn, state: m.state, openIssues: DEMO_ISSUES.filter((i) => i.milestone?.id === m.id && i.state === 'open').length, closedIssues: DEMO_ISSUES.filter((i) => i.milestone?.id === m.id && i.state === 'closed').length, })) // #41 (forecast bands) depends on #39 (scheduler); #42 (lifecycle) depends on #34 (columns). export const DEMO_DEPS: DependencyEdge[] = [ { issue: 41, dependsOn: 39 }, { issue: 42, dependsOn: 34 }, ] export const DEMO_TIMELINES: Record = Object.fromEntries( SEEDS.map((s) => [s.number, s.events ?? []]), ) export const DEMO_CAPACITY: CapacityMember[] = [ { person: 'stephen', focusFactor: 0.6, allocation: 1 }, { person: 'ana', focusFactor: 1, allocation: 0.8 }, ] export const DEMO_COLLABORATORS: { login: string; name: string }[] = [ { login: 'stephen', name: 'Stephen Mann' }, { login: 'ana', name: 'Ana Koval' }, ] export const DEMO_DIRECTIVES: DirectiveRecord[] = [ { seq: 1, id: 'demo-1', ts: '2026-02-26T10:00:00Z', status: 'accepted', kind: 'reprioritize', quote: 'Bump the lifecycle fix — it’s blocking the board.', target: { issue: 42 }, params: { priority: 1 }, rationale: 'Board columns are wrong until #42 lands.' }, { seq: 2, id: 'demo-2', ts: '2026-02-24T15:00:00Z', status: 'accepted', kind: 'capacity', quote: 'Stephen’s at 60% this sprint — half on support.', params: { person: 'stephen', focusFactor: 0.6 }, rationale: 'Support rotation.' }, { seq: 3, id: 'demo-3', ts: '2026-02-22T09:00:00Z', status: 'amended', kind: 'set-deadline', quote: 'Beta ships the 12th, hard.', target: { milestone: 1 }, params: { due: '2026-03-12' } }, ] /** The full gitea-shaped snapshot the reconcile handlers return in demo mode. */ export const DEMO_SNAPSHOT = { issues: DEMO_ISSUES, milestones: DEMO_MILESTONES, deps: DEMO_DEPS, timelines: DEMO_TIMELINES, }