diff --git a/__tests__/components/build/JourneyBar-title-fallback.test.tsx b/__tests__/components/build/JourneyBar-title-fallback.test.tsx new file mode 100644 index 00000000..14a0c0b6 --- /dev/null +++ b/__tests__/components/build/JourneyBar-title-fallback.test.tsx @@ -0,0 +1,52 @@ +// @vitest-environment jsdom +import { describe, it, expect, vi, beforeAll, afterEach } from 'vitest' +import React from 'react' +import { createRoot, type Root } from 'react-dom/client' +import { act } from 'react' + +beforeAll(() => { + ;(globalThis as any).React = React + ;(globalThis as any).IS_REACT_ACT_ENVIRONMENT = true +}) + +vi.mock('next-auth/react', () => ({ useSession: () => ({ data: null }) })) +vi.mock('@/contexts/build-context', () => ({ + useBuild: () => ({ + state: { track: 'company', view: 'thesis', done: {}, auto: false }, + views: ['thesis', 'unmappedFutureView'], + dispatch: vi.fn(), + goView: vi.fn(), + woven: 0, + totalPrimitives: 10, + }), +})) +vi.mock('@/components/build/WorkspaceShell', () => ({ currentActIndex: () => 0 })) + +import { JourneyBar } from '@/components/build/JourneyBar' + +let host: HTMLElement +let root: Root +function render(node: React.ReactElement) { + host = document.createElement('div') + document.body.appendChild(host) + root = createRoot(host) + act(() => { root.render(node) }) +} +afterEach(() => { + act(() => { root?.unmount() }) + host?.remove() + vi.clearAllMocks() +}) + +describe('JourneyBar chip labels (#BLD-08)', () => { + it('shows the plain ARTIFACT_TITLES label, not the raw view id, for a mapped view', () => { + render(React.createElement(JourneyBar)) + expect(host.textContent).toContain('Your business thesis') + expect(host.textContent).not.toContain('>thesis<') + }) + + it('falls back to the raw view id for an unmapped view rather than rendering blank', () => { + render(React.createElement(JourneyBar)) + expect(host.textContent).toContain('unmappedFutureView') + }) +}) diff --git a/__tests__/components/build/JourneyBar.test.tsx b/__tests__/components/build/JourneyBar.test.tsx index f0593381..8baf42c0 100644 --- a/__tests__/components/build/JourneyBar.test.tsx +++ b/__tests__/components/build/JourneyBar.test.tsx @@ -64,16 +64,19 @@ describe('JourneyBar (#BLD-06.1)', () => { }) it('marks a done artifact chip as done, the current one as current', () => { + // #BLD-08 — chips now show the plain ARTIFACT_TITLES label ("Product + // Brief"/"Design System"), not the raw view id, so these lookups match + // the plain text instead of the id. render(React.createElement(JourneyBar)) - const briefChip = Array.from(host.querySelectorAll('.m-crumb')).find((el) => el.textContent === 'brief') as HTMLButtonElement + const briefChip = Array.from(host.querySelectorAll('.m-crumb')).find((el) => el.textContent === 'Product Brief') as HTMLButtonElement expect(briefChip.className).toContain('is-current') - const designChip = Array.from(host.querySelectorAll('.m-crumb')).find((el) => el.textContent === 'design') as HTMLButtonElement + const designChip = Array.from(host.querySelectorAll('.m-crumb')).find((el) => el.textContent === 'Design System') as HTMLButtonElement expect(designChip.className).toContain('is-done') }) it('clicking a done, non-current chip calls goView', () => { render(React.createElement(JourneyBar)) - const designChip = Array.from(host.querySelectorAll('.m-crumb')).find((el) => el.textContent === 'design') as HTMLButtonElement + const designChip = Array.from(host.querySelectorAll('.m-crumb')).find((el) => el.textContent === 'Design System') as HTMLButtonElement act(() => { designChip.click() }) expect(goViewMock).toHaveBeenCalledWith('design') }) diff --git a/__tests__/components/build/no-jargon-strings.test.ts b/__tests__/components/build/no-jargon-strings.test.ts new file mode 100644 index 00000000..fc49f434 --- /dev/null +++ b/__tests__/components/build/no-jargon-strings.test.ts @@ -0,0 +1,43 @@ +import { describe, it, expect } from 'vitest' +import { readFileSync } from 'fs' + +describe('no internal jargon in user-facing copy (#BLD-08)', () => { + it('Pricing.tsx feature list and tagline do not say "agent swarm" / "autonomous loop"', () => { + const content = readFileSync('components/build/screens/Pricing.tsx', 'utf-8') + expect(content).not.toContain('The nightly autonomous loop + real agent swarm') + expect(content).not.toContain('Full agent-swarm autonomy.') + }) + + it('Account.tsx billing copy does not say "swarm" in display strings', () => { + const content = readFileSync('components/build/screens/Account.tsx', 'utf-8') + expect(content).not.toContain('Custom domain, nightly loop & swarm on paid plans.') + expect(content).not.toContain("'agent swarm'") + expect(content).not.toContain('Upgrade to unlock custom domain, nightly loop, and the swarm.') + }) + + it('BuildOverlays.tsx headline/sub-copy do not say "swarm" or the internal "provision everything" phrasing', () => { + const content = readFileSync('components/build/BuildOverlays.tsx', 'utf-8') + expect(content).not.toContain("Cody's swarm is building the MVP") + expect(content).not.toContain('Provision everything, ask nothing') + }) + + it('AutoModePanel.tsx activity line does not say "swarm"', () => { + const content = readFileSync('components/build/AutoModePanel.tsx', 'utf-8') + expect(content).not.toContain('Cody is dispatching the swarm on') + }) + + it('DocumentsPanel.tsx empty-state copy does not say "swarm"', () => { + const content = readFileSync('components/build/DocumentsPanel.tsx', 'utf-8') + expect(content).not.toContain('what the swarm did, metrics, and next actions') + }) + + it('OnboardingVideo.tsx fallback copy does not say "swarm"', () => { + const content = readFileSync('components/build/OnboardingVideo.tsx', 'utf-8') + expect(content).not.toContain("Cody and the swarm.") + }) + + it('LiveTicker.tsx ticker line does not say "swarm"', () => { + const content = readFileSync('components/build/LiveTicker.tsx', 'utf-8') + expect(content).not.toContain('swarm ▸ composing artifacts') + }) +}) diff --git a/__tests__/lib/build/titles-plain-language.test.ts b/__tests__/lib/build/titles-plain-language.test.ts new file mode 100644 index 00000000..1bfb8106 --- /dev/null +++ b/__tests__/lib/build/titles-plain-language.test.ts @@ -0,0 +1,12 @@ +import { describe, it, expect } from 'vitest' +import { ARTIFACT_TITLES } from '@/lib/build/titles' + +describe('ARTIFACT_TITLES — plain language (#BLD-08)', () => { + it('dataModel, memoryPolicy, thesis, wedge no longer use internal jargon', () => { + expect(ARTIFACT_TITLES.dataModel).not.toBe('Data Model') + expect(ARTIFACT_TITLES.memoryPolicy).not.toBe('Memory Policy') + expect(ARTIFACT_TITLES.thesis).not.toBe('Venture Thesis') + expect(ARTIFACT_TITLES.wedge).not.toBe('Initial Wedge') + expect(ARTIFACT_TITLES.dataModel).toMatch(/your|the/i) + }) +}) diff --git a/components/build/AutoModePanel.tsx b/components/build/AutoModePanel.tsx index b76fae33..3a2601dc 100644 --- a/components/build/AutoModePanel.tsx +++ b/components/build/AutoModePanel.tsx @@ -173,7 +173,7 @@ export function AutoModePanel({ companyId, companyName, track = 'company', unloc {progress.dispatchesSoFar} task{progress.dispatchesSoFar === 1 ? '' : 's'} dispatched this run

- Current activity: Cody is dispatching the swarm on {companyName} — briefing → highest-leverage task → ship. + Current activity: Cody's team is working on {companyName} — briefing → highest-leverage task → ship.

) })} diff --git a/components/build/LiveTicker.tsx b/components/build/LiveTicker.tsx index 70e331ad..d14b998e 100644 --- a/components/build/LiveTicker.tsx +++ b/components/build/LiveTicker.tsx @@ -20,7 +20,7 @@ function buildLines(agents: number | null, tasks: number | null): string[] { const a = agents ?? 0 const lines = [ `${a} agents working across the platform right now`, - 'swarm ▸ composing artifacts from real AINative primitives', + "Cody's team ▸ composing artifacts from real AINative primitives", 'briefing ▸ nightly loop picked the highest-leverage task', 'RLHF ▸ scoring outcomes back into the lakehouse', ] diff --git a/components/build/OnboardingVideo.tsx b/components/build/OnboardingVideo.tsx index 518a129d..630a54bb 100644 --- a/components/build/OnboardingVideo.tsx +++ b/components/build/OnboardingVideo.tsx @@ -119,7 +119,7 @@ export function OnboardingVideo({ src, poster, className }: OnboardingVideoProps

{isPlaceholder - ? "A short walkthrough is on its way — it'll show you exactly how to get the most out of Cody and the swarm." + ? "A short walkthrough is on its way — it'll show you exactly how to get the most out of Cody and Cody's team." : 'Watch how Cody builds and runs companies autonomously while you focus on what matters.'}

diff --git a/components/build/screens/Account.tsx b/components/build/screens/Account.tsx index c784ea44..3000ca11 100644 --- a/components/build/screens/Account.tsx +++ b/components/build/screens/Account.tsx @@ -286,7 +286,7 @@ export function Account() {
Unlock - Custom domain, nightly loop & swarm on paid plans. + Custom domain, overnight work, and Cody's full team on paid plans.
Pricing @@ -346,8 +346,8 @@ export function Account() { {planLoading ? '—' : activePlan - ? [gates.customDomain && 'custom domain', gates.nightlyLoop && 'nightly loop', gates.swarm && 'agent swarm'].filter(Boolean).join(' · ') || '—' - : 'Upgrade to unlock custom domain, nightly loop, and the swarm.'} + ? [gates.customDomain && 'custom domain', gates.nightlyLoop && 'nightly loop', gates.swarm && "Cody's team"].filter(Boolean).join(' · ') || '—' + : "Upgrade to unlock custom domain, nightly loop, and Cody's team."}
diff --git a/components/build/screens/Pricing.tsx b/components/build/screens/Pricing.tsx index da88dce5..fd69facf 100644 --- a/components/build/screens/Pricing.tsx +++ b/components/build/screens/Pricing.tsx @@ -40,12 +40,12 @@ const TIERS = [ { id: 'pro', name: 'Pro', monthly: 49, tagline: 'Build it for real.', plan: 'launch' as const, featured: true, priceId: 'price_1TGUVdDP3OaRv4TyMwk7nnp1', priceIdYearly: '', // TODO(#258): create Stripe yearly price ($490/yr) and set its price_… here - features: ['Cody builds your app + company', 'The nightly autonomous loop + real agent swarm', '1M tokens · 50K API · 10GB', 'Real generation (Claude Sonnet 4.5)', 'Custom domain available'] }, + features: ['Cody builds your app + company', 'Cody keeps working overnight, with a full team of AI workers', '1M tokens · 50K API · 10GB', 'Real generation (Claude Sonnet 4.5)', 'Custom domain available'] }, { id: 'business', name: 'Business', monthly: 149, tagline: 'Cody runs it 24/7.', plan: 'company' as const, priceId: 'price_1TGUVeDP3OaRv4TyaqQG6lVT', priceIdYearly: '', // TODO(#258): create Stripe yearly price ($1,490/yr) and set its price_… here features: ['Everything in Pro', 'Sales pipeline · invoicing · helpdesk · voice', '5M tokens · 150K API · 50GB'] }, - { id: 'enterprise', name: 'Enterprise', monthly: 999, tagline: 'Full agent-swarm autonomy.', plan: 'company' as const, + { id: 'enterprise', name: 'Enterprise', monthly: 999, tagline: "Cody's full team works independently.", plan: 'company' as const, priceId: 'price_1Ti31LDP3OaRv4TytcjLbFPh', priceIdYearly: '', // TODO(#258): create Stripe yearly price ($9,990/yr) and set its price_… here features: ['Everything in Business', '20M tokens · 200GB · SSO', 'Priority support'] }, diff --git a/lib/build/titles.ts b/lib/build/titles.ts index a66d4780..968b31b4 100644 --- a/lib/build/titles.ts +++ b/lib/build/titles.ts @@ -1,11 +1,11 @@ /** Artifact display titles (04-SCREENS). Shared by ArtifactRouter + overlays. */ export const ARTIFACT_TITLES: Record = { design: 'Design System', brief: 'Product Brief', prd: 'Product Requirements', comp: 'AINative Composition Plan', - dataModel: 'Data Model', memoryPolicy: 'Memory Policy', agentDef: 'Agent Definition', + dataModel: 'Your data model', memoryPolicy: 'What Cody remembers', agentDef: 'Agent Definition', codingStandards: 'Engineering Standards', apiSpec: 'Integrations', backlog: 'Build Backlog', sprintPlan: 'Sprint Plan', swarm: 'The swarm', infra: 'Infrastructure', preview: 'Running Preview', - thesis: 'Venture Thesis', wedge: 'Initial Wedge', businessModel: 'Business Model', + thesis: 'Your business thesis', wedge: 'Your first customers', businessModel: 'Business Model', positioning: 'Positioning', landing: 'Landing Page', plan30: '30-Day Plan', pipeline: 'Sales Pipeline', 'rescope-intent': 'Re-scope the wedge', conflict: 'Dependency Conflict', graph: 'The artifact graph', }