Kill screen fixtures: live/empty screens + one demo snapshot for e2e
The app is now fixture-free. All product screens render live data (via the reconcile bridge + view builders) or an EmptyState — no hardcoded demo data, metrics, or prose anywhere in the UI. - Removed the `?? FIXTURE` fallbacks and embedded fake strings (fixed dates, 'reconcile 3.2s', 'ahead of forecast', mock forecast prose) from all 10 product screens; each now shows an EmptyState when its live data is absent. - directives-screen: fully live off pmstate.directives() (was seeded from a fixture with a fake consequence-diff card) — loading + empty states, no seed. - use-chat: drop the CHAT seed (the model-status effect already sets the greeting). - data/fixtures.ts → data/view-types.ts: stripped every const design fixture, kept only the view-model TYPES (the builder↔screen contract). Repointed imports. Testing fixture (no live gitea), per the user's call: - New apps/desktop/src/main/demo-snapshot.ts — one deterministic, gitea-shaped snapshot (issues/milestones/deps/timelines + capacity + directives + collaborators). Served by the reconcile/boot/collaborators/capacity/directives handlers when COMMITEA_E2E=1, so e2e renders it through the REAL builders with no token. - smoke.spec rewritten to assert on the demo snapshot through the live render path; onboarding/offline-toggle assertions dropped (wizard is live → live-onboarding; offline is real state now). 12/12 smoke pass. Verify: core tsc clean · desktop tsc clean · 12/12 smoke green (perf#32 benchmark flakes under machine load — unrelated, passes in isolation). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
152
apps/desktop/src/main/demo-snapshot.ts
Normal file
152
apps/desktop/src/main/demo-snapshot.ts
Normal file
@@ -0,0 +1,152 @@
|
||||
/**
|
||||
* 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<number, LifecycleEvent[]> = 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,
|
||||
}
|
||||
Reference in New Issue
Block a user