Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 52 additions & 0 deletions __tests__/components/build/JourneyBar-title-fallback.test.tsx
Original file line number Diff line number Diff line change
@@ -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')
})
})
9 changes: 6 additions & 3 deletions __tests__/components/build/JourneyBar.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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')
})
Expand Down
43 changes: 43 additions & 0 deletions __tests__/components/build/no-jargon-strings.test.ts
Original file line number Diff line number Diff line change
@@ -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 &amp; 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&apos;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')
})
})
12 changes: 12 additions & 0 deletions __tests__/lib/build/titles-plain-language.test.ts
Original file line number Diff line number Diff line change
@@ -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)
})
})
2 changes: 1 addition & 1 deletion components/build/AutoModePanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ export function AutoModePanel({ companyId, companyName, track = 'company', unloc
{progress.dispatchesSoFar} task{progress.dispatchesSoFar === 1 ? '' : 's'} dispatched this run
</p>
<p className="m-task-meta" data-testid="auto-mode-activity">
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.
</p>
<button
className="btn-secondary"
Expand Down
4 changes: 2 additions & 2 deletions components/build/BuildOverlays.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ export function BuildOverlays() {
return (
<div className="m-overlay m-formin" role="status" aria-live="polite">
<span className="m-overlay-pill m-mono">SWARM WORKING</span>
<h1 className="m-artifact m-overlay-h">Cody&apos;s swarm is building the MVP</h1>
<h1 className="m-artifact m-overlay-h">Cody&apos;s team is building your MVP</h1>
<div className="m-agent-grid">
{SWARM_AGENTS.map((a) => {
const active = ribbon.toLowerCase().includes(a.name.toLowerCase())
Expand All @@ -80,7 +80,7 @@ export function BuildOverlays() {
<div className="m-overlay m-formin" role="status" aria-live="polite">
<span className="m-overlay-pill m-mono">PROVISIONING</span>
<h1 className="m-artifact m-overlay-h">Provisioning your infrastructure</h1>
<p className="m-sub">Provision everything, ask nothing — real primitives, spun up for you.</p>
<p className="m-sub">Setting everything up automatically — real primitives, spun up for you.</p>
<ul className="m-list m-checklist">
{INFRA_ITEMS.map(([n, d], i) => (
<li key={n}>
Expand Down
2 changes: 1 addition & 1 deletion components/build/DocumentsPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -364,7 +364,7 @@ export function DocumentsPanel({
<div data-testid="documents-empty">
<p className="m-mono m-task-empty">
{tab === 'report'
? `No reports yet. Each night Cody appends a dated operational report for ${companyName || 'your company'} — what the swarm did, metrics, and next actions.`
? `No reports yet. Each night Cody appends a dated operational report for ${companyName || 'your company'} — what Cody's team did, metrics, and next actions.`
: `No documents yet. As ${companyName || 'your company'} evolves, Cody builds a library — Research, Product Roadmap, Mission and Market Research — each grounded in your idea.`}
</p>
{/* Offer to generate the durable starter docs (never auto-faked). This is
Expand Down
3 changes: 2 additions & 1 deletion components/build/JourneyBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import { useState } from 'react'
import { useBuild } from '@/contexts/build-context'
import { useSession } from 'next-auth/react'
import { APP_ACT_LABELS, COMPANY_ACT_LABELS } from '@/lib/build/acts'
import { ARTIFACT_TITLES } from '@/lib/build/titles'
import type { Screen } from '@/lib/build/state'
import { AccountMenu } from '@/components/build/AccountMenu'
import { currentActIndex } from '@/components/build/WorkspaceShell'
Expand Down Expand Up @@ -74,7 +75,7 @@ export function JourneyBar() {
disabled={!clickable}
onClick={() => clickable && goView(v as never)}
>
{v}
{ARTIFACT_TITLES[v] ?? v}
</button>
)
})}
Expand Down
2 changes: 1 addition & 1 deletion components/build/LiveTicker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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',
]
Expand Down
2 changes: 1 addition & 1 deletion components/build/OnboardingVideo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ export function OnboardingVideo({ src, poster, className }: OnboardingVideoProps

<p className="m-live-card-body" style={{ marginTop: 10 }}>
{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.'}
</p>
</div>
Expand Down
6 changes: 3 additions & 3 deletions components/build/screens/Account.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -286,7 +286,7 @@ export function Account() {
</div>
<div className="m-sec-row">
<span>Unlock</span>
<span className="m-mono m-muted">Custom domain, nightly loop &amp; swarm on paid plans.</span>
<span className="m-mono m-muted">Custom domain, overnight work, and Cody's full team on paid plans.</span>
</div>
<div className="m-sec-row">
<span>Pricing</span>
Expand Down Expand Up @@ -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."}
</span>
</div>
<div className="m-sec-row">
Expand Down
4 changes: 2 additions & 2 deletions components/build/screens/Pricing.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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'] },
Expand Down
4 changes: 2 additions & 2 deletions lib/build/titles.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
/** Artifact display titles (04-SCREENS). Shared by ArtifactRouter + overlays. */
export const ARTIFACT_TITLES: Record<string, string> = {
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',
}
Loading