refactor(workshop): "meet your agent" framing for Phase 2

- Phase 1 proceed button → "Meet your agent".
- Phase strip label + Phase 2 heading: "Meet your node" → "Meet your agent".
- Phase 2 intro now describes the APESS agent (Claude on the edge,
  drives the board's devices, on-board fallback).
- "Open your node" card → "Open your agent to explore" (same local
  ZeroClaw dashboard link); Pick-your-domain card kept below it.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
This commit is contained in:
Omar Sobh
2026-07-22 01:01:34 -07:00
co-authored by Claude Opus 4.8
parent 66723ebd35
commit e0bcf51b08
6 changed files with 18 additions and 16 deletions
+4 -4
View File
@@ -24,7 +24,7 @@ describe('PhaseStrip', () => {
expect(phases).toHaveLength(5) expect(phases).toHaveLength(5)
expect(phases.map((p) => p.textContent)).toEqual([ expect(phases.map((p) => p.textContent)).toEqual([
expect.stringContaining('Team reg'), expect.stringContaining('Team reg'),
expect.stringContaining('Meet your node'), expect.stringContaining('Meet your agent'),
expect.stringContaining('Module 1'), expect.stringContaining('Module 1'),
expect.stringContaining('Module 2'), expect.stringContaining('Module 2'),
expect.stringContaining('Module 3'), expect.stringContaining('Module 3'),
@@ -42,7 +42,7 @@ describe('PhaseStrip', () => {
useSession.getState().completePhase('setup') useSession.getState().completePhase('setup')
renderAt('/workshop/module1', 'm1') renderAt('/workshop/module1', 'm1')
expect(screen.getByText('Team reg').closest('a')).toHaveAttribute('data-state', 'done') expect(screen.getByText('Team reg').closest('a')).toHaveAttribute('data-state', 'done')
expect(screen.getByText('Meet your node').closest('a')).toHaveAttribute('data-state', 'done') expect(screen.getByText('Meet your agent').closest('a')).toHaveAttribute('data-state', 'done')
expect(screen.getByText('Module 1').closest('a')).toHaveAttribute('data-state', 'active') expect(screen.getByText('Module 1').closest('a')).toHaveAttribute('data-state', 'active')
expect(screen.getByText('Module 2').closest('a')).toHaveAttribute('data-state', 'pending') expect(screen.getByText('Module 2').closest('a')).toHaveAttribute('data-state', 'pending')
}) })
@@ -54,13 +54,13 @@ describe('PhaseStrip', () => {
useSession.getState().completePhase('setup') useSession.getState().completePhase('setup')
renderAt('/workshop', 'reg') renderAt('/workshop', 'reg')
expect(screen.getByText('Team reg').closest('a')).toHaveAttribute('data-state', 'active') expect(screen.getByText('Team reg').closest('a')).toHaveAttribute('data-state', 'active')
expect(screen.getByText('Meet your node').closest('a')).toHaveAttribute('data-state', 'pending') expect(screen.getByText('Meet your agent').closest('a')).toHaveAttribute('data-state', 'pending')
}) })
it('links each phase to its workshop sub-route', () => { it('links each phase to its workshop sub-route', () => {
renderAt('/workshop') renderAt('/workshop')
expect(screen.getByText('Team reg').closest('a')).toHaveAttribute('href', '/workshop') expect(screen.getByText('Team reg').closest('a')).toHaveAttribute('href', '/workshop')
expect(screen.getByText('Meet your node').closest('a')).toHaveAttribute('href', '/workshop/setup') expect(screen.getByText('Meet your agent').closest('a')).toHaveAttribute('href', '/workshop/setup')
expect(screen.getByText('Module 1').closest('a')).toHaveAttribute('href', '/workshop/module1') expect(screen.getByText('Module 1').closest('a')).toHaveAttribute('href', '/workshop/module1')
expect(screen.getByText('Module 2').closest('a')).toHaveAttribute('href', '/workshop/module2') expect(screen.getByText('Module 2').closest('a')).toHaveAttribute('href', '/workshop/module2')
expect(screen.getByText('Module 3').closest('a')).toHaveAttribute('href', '/workshop/add') expect(screen.getByText('Module 3').closest('a')).toHaveAttribute('href', '/workshop/add')
+1 -1
View File
@@ -10,7 +10,7 @@ interface PhaseMeta {
const PHASES: PhaseMeta[] = [ const PHASES: PhaseMeta[] = [
{ key: 'reg', name: 'Team reg', to: '/workshop' }, { key: 'reg', name: 'Team reg', to: '/workshop' },
{ key: 'setup', name: 'Meet your node', to: '/workshop/setup' }, { key: 'setup', name: 'Meet your agent', to: '/workshop/setup' },
{ key: 'm1', name: 'Module 1', to: '/workshop/module1' }, { key: 'm1', name: 'Module 1', to: '/workshop/module1' },
{ key: 'm2', name: 'Module 2', to: '/workshop/module2' }, { key: 'm2', name: 'Module 2', to: '/workshop/module2' },
{ key: 'add', name: 'Module 3', to: '/workshop/add' }, { key: 'add', name: 'Module 3', to: '/workshop/add' },
+2 -2
View File
@@ -17,7 +17,7 @@ function connect(nodeUrl: string | null = 'http://192.168.1.7:8080') {
useSession.getState().setDevice({ connected: true, port: 'board · crimson-otter', uptimeS: 0, nodeUrl }) useSession.getState().setDevice({ connected: true, port: 'board · crimson-otter', uptimeS: 0, nodeUrl })
} }
describe('EnvSetup — Meet your node', () => { describe('EnvSetup — Meet your agent', () => {
beforeEach(() => { beforeEach(() => {
useSession.getState().reset() useSession.getState().reset()
sessionStorage.clear() sessionStorage.clear()
@@ -26,7 +26,7 @@ describe('EnvSetup — Meet your node', () => {
it('renders the phase strip set to setup and the heading', () => { it('renders the phase strip set to setup and the heading', () => {
renderPage() renderPage()
expect(screen.getByTestId('phase-strip')).toBeInTheDocument() expect(screen.getByTestId('phase-strip')).toBeInTheDocument()
expect(screen.getByRole('heading', { name: /meet your node/i })).toBeInTheDocument() expect(screen.getByRole('heading', { name: /meet your agent/i })).toBeInTheDocument()
expect( expect(
screen.getByTestId('phase-strip').querySelector('[data-phase="setup"]'), screen.getByTestId('phase-strip').querySelector('[data-phase="setup"]'),
).toHaveAttribute('data-state', 'active') ).toHaveAttribute('data-state', 'active')
+8 -6
View File
@@ -39,17 +39,19 @@ export function EnvSetup() {
<Badge variant="secondary" className="font-mono text-[10px] tracking-widest uppercase mb-2"> <Badge variant="secondary" className="font-mono text-[10px] tracking-widest uppercase mb-2">
Phase 2 of 5 · ~15 min Phase 2 of 5 · ~15 min
</Badge> </Badge>
<h1 className="text-3xl font-bold tracking-tight">Meet your node</h1> <h1 className="text-3xl font-bold tracking-tight">Meet your agent</h1>
<p className="text-sm text-muted-foreground mt-2 max-w-xl"> <p className="text-sm text-muted-foreground mt-2 max-w-2xl leading-relaxed">
Open your node&rsquo;s own dashboard and name the domain it&rsquo;s for — then Your board now runs the <span className="font-medium text-foreground">APESS agent</span> — a
you&rsquo;re clear for Module 1. Claude-powered agent living on the edge. It reasons about your domain, drives the board&rsquo;s
own devices, and keeps working when the cloud drops by falling back to an on-board model.
Open it to explore, then name the domain it&rsquo;s for.
</p> </p>
</div> </div>
{/* 1 — Open your node (hero) */} {/* 1 — Open your agent (hero) */}
<Card> <Card>
<CardHeader> <CardHeader>
<CardTitle className="text-base">Open your node</CardTitle> <CardTitle className="text-base">Open your agent to explore</CardTitle>
</CardHeader> </CardHeader>
<CardContent> <CardContent>
<OpenYourNode variant="hero" /> <OpenYourNode variant="hero" />
+2 -2
View File
@@ -39,7 +39,7 @@ describe('TeamRegistration', () => {
it('gates the Proceed button until name + member + board are ready', async () => { it('gates the Proceed button until name + member + board are ready', async () => {
const user = userEvent.setup() const user = userEvent.setup()
renderPage() renderPage()
const proceed = screen.getByRole('button', { name: /proceed/i }) const proceed = screen.getByRole('button', { name: /meet your agent/i })
expect(proceed).toBeDisabled() expect(proceed).toBeDisabled()
await user.type(screen.getByLabelText(/team name/i), 'team_x') await user.type(screen.getByLabelText(/team name/i), 'team_x')
@@ -94,7 +94,7 @@ describe('TeamRegistration', () => {
await user.type(screen.getByLabelText(/team name/i), 'team_x') await user.type(screen.getByLabelText(/team name/i), 'team_x')
await user.type(screen.getByLabelText('Member 1'), 'A. Rossi') await user.type(screen.getByLabelText('Member 1'), 'A. Rossi')
act(() => useSession.getState().setDevice({ connected: true, port: 'board · KIT-01', uptimeS: 0 })) act(() => useSession.getState().setDevice({ connected: true, port: 'board · KIT-01', uptimeS: 0 }))
await user.click(screen.getByRole('button', { name: /proceed/i })) await user.click(screen.getByRole('button', { name: /meet your agent/i }))
expect(useSession.getState().phases.reg).toBe(true) expect(useSession.getState().phases.reg).toBe(true)
}) })
+1 -1
View File
@@ -141,7 +141,7 @@ export function TeamRegistration() {
<div className="flex justify-end pt-4"> <div className="flex justify-end pt-4">
<Button size="lg" disabled={!ready} onClick={onProceed}> <Button size="lg" disabled={!ready} onClick={onProceed}>
Proceed to environment setup → Meet your agent →
</Button> </Button>
</div> </div>
</section> </section>