feat(module2): chat with your agent — three canned prompts, then Layers 2/3

Module 2 is now a chat straight to the agent instead of Build & flash.
Removed the "Open your node" links and the Live board feed card.

New AgentChat component: three imperative canned prompts —
- List the I2C devices on the bus
- Count to 100 and print the value once a second in the LED matrix
- Scroll GO CLAWS on the LED matrix
Each sends to the agent (fire-and-forget, cloud) and streams its
activity (tools/flash/reply) back into the transcript. A prompt is marked
done on its first terminal step (flash/response); an error resets it to
retry. Prompts run one at a time.

Once all three have run successfully, Module 2 reveals "What's next" —
the ADD Layer 2 (Skills) + Layer 3 (Policies & failure) capture — and
Proceed gates on all-three-tried AND L2 + L3 filled.

Note: BuildFlash / LiveBoardFeed / ActorMap are now orphaned (kept for
possible reuse).

Co-Authored-By: Claude Opus 4.8 <[email protected]>
This commit is contained in:
Omar Sobh
2026-07-22 01:39:25 -07:00
co-authored by Claude Opus 4.8
parent c2ef338888
commit 7780903278
4 changed files with 342 additions and 85 deletions
+63 -73
View File
@@ -1,27 +1,22 @@
import { useState } from 'react'
import { Link, useNavigate } from 'react-router-dom'
import { Button } from '@/components/ui/button'
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
import { Badge } from '@/components/ui/badge'
import { PhaseStrip } from '@/components/PhaseStrip'
import { LiveBoardFeed } from '@/components/LiveBoardFeed'
import { BuildFlash } from '@/components/BuildFlash'
import { AgentChat } from '@/components/AgentChat'
import { AddLayerForm } from '@/components/AddLayerForm'
import { OpenYourNode } from '@/components/OpenYourNode'
import { useNodeFeed } from '@/lib/useNodeFeed'
import { useSession } from '@/store/session'
export function Module2() {
const navigate = useNavigate()
const teamId = useSession((s) => s.teamId)
const feed = useNodeFeed(teamId, true)
const l2 = useSession((s) => s.add.L2)
const l3 = useSession((s) => s.add.L3)
const completePhase = useSession((s) => s.completePhase)
const [tried, setTried] = useState(0)
// The board acting on a prompt (via Build & flash below) shows up in its
// activity feed — that's proof the loop was exercised.
const exercised = feed.activity.length > 0
const ready = exercised && l2.trim().length > 0 && l3.trim().length > 0
const allTried = tried >= 3
const ready = allTried && l2.trim().length > 0 && l3.trim().length > 0
const onProceed = () => {
completePhase('m2')
@@ -42,77 +37,72 @@ export function Module2() {
<PhaseStrip active="m2" />
<section className="px-8 py-10 max-w-5xl mx-auto space-y-6">
<section className="px-8 py-10 max-w-3xl mx-auto space-y-6">
<div>
<Badge variant="secondary" className="font-mono text-[10px] tracking-widest uppercase mb-2">
Phase 4 of 5 · ~90 min
</Badge>
<h1 className="text-3xl font-bold tracking-tight">Module 2 · Skills &amp; policies</h1>
<p className="text-sm text-muted-foreground mt-2 max-w-xl">
Your node ships with expert skills already. Decide which domain skills it needs, try them in the
node dashboard, then capture Layers 2 and 3 — the skills it can invoke and the actuation gate
that governs them.
Your agent already ships with expert skills — hardware, the MCU bridge, the LED matrix,
flashing, and more. Try them from the chat below: each prompt makes the agent use its
skills and tools on your real board.
</p>
<div className="mt-3">
<OpenYourNode variant="inline" />
</div>
<AgentChat onProgress={(done) => setTried(done)} />
{/* What's next — revealed once all three prompts have run successfully. */}
{allTried ? (
<div className="space-y-6" data-testid="whats-next">
<div className="pt-2">
<h2 className="text-lg font-semibold tracking-tight">What&rsquo;s next</h2>
<p className="text-sm text-muted-foreground mt-1 max-w-xl">
You just watched the agent enumerate a bus and drive the matrix using its built-in
skills. Now capture <span className="font-medium text-foreground">your domain&rsquo;s</span>{' '}
skills and the policy that governs them — Layers 2 and 3 of your Agent Design Document.
</p>
</div>
<AddLayerForm
layer="L2"
title="ADD · Layer 2 — Skills"
description="What skills can the agent invoke to act on its domain?"
placeholder="Flash a sketch to the MCU; scroll a message; drive the damper; sample the IMU."
/>
<AddLayerForm
layer="L3"
title="ADD · Layer 3 — Policies & failure"
description="The actuation gate — and what happens when things break. For each failure you can name, which way does it fail? A fail-safe must never quietly report 'normal'."
placeholder={
'Autonomous: log + alert. Needs approval: drive the actuator. E-stop: operator halts actuation at any time.\n\n' +
'Failure states → response:\n' +
'• sensor disconnected / stuck value / drifting → mark UNKNOWN, never "nominal"\n' +
'• reading older than 60 s → treat as no reading\n' +
'• cloud unreachable → decide on-board, flag reduced confidence\n' +
'• agent unsure → escalate to a human, do not actuate'
}
/>
<div className="flex justify-end pt-2">
<Button size="lg" disabled={!ready} onClick={onProceed}>
Proceed to ADD builder →
</Button>
</div>
</div>
</div>
<Card>
<CardHeader>
<CardTitle className="text-base">Skills reference</CardTitle>
</CardHeader>
<CardContent className="space-y-3">
<p className="text-sm text-muted-foreground leading-relaxed">
The node already ships with expert skills — <span className="font-mono text-xs text-foreground">uno-q-hardware</span>,{' '}
<span className="font-mono text-xs text-foreground">bridge</span>,{' '}
<span className="font-mono text-xs text-foreground">led-matrix</span>,{' '}
<span className="font-mono text-xs text-foreground">flashing</span>, and more. Layer 2 is not
about re-implementing those: it&rsquo;s deciding which <em>domain</em> skills your node needs, then
trying them live in the dashboard.
</p>
<OpenYourNode variant="inline" />
</CardContent>
</Card>
<Card>
<CardHeader>
<CardTitle className="text-base">Live board feed</CardTitle>
</CardHeader>
<CardContent className="space-y-4">
<LiveBoardFeed feed={feed} />
</CardContent>
</Card>
<BuildFlash />
<div className="grid lg:grid-cols-2 gap-6">
<AddLayerForm
layer="L2"
title="ADD · Layer 2 — Skills"
description="What skills can the node invoke to act on its domain?"
placeholder="Flash a sketch to the MCU; scroll a message; drive the damper; sample the IMU."
/>
<AddLayerForm
layer="L3"
title="ADD · Layer 3 — Policies & failure"
description="The actuation gate — and what happens when things break. For each failure you can name, which way does it fail? A fail-safe must never quietly report 'normal'."
placeholder={
'Autonomous: log + alert. Needs approval: drive the actuator. E-stop: operator halts actuation at any time.\n\n' +
'Failure states → response:\n' +
'• sensor disconnected / stuck value / drifting → mark UNKNOWN, never "nominal"\n' +
'• reading older than 60 s → treat as no reading\n' +
'• cloud unreachable → decide on-board, flag reduced confidence\n' +
'• agent unsure → escalate to a human, do not actuate'
}
/>
</div>
<div className="flex justify-end pt-2">
<Button size="lg" disabled={!ready} onClick={onProceed}>
Proceed to ADD builder →
</Button>
</div>
) : (
<Card>
<CardHeader>
<CardTitle className="text-base text-muted-foreground">What&rsquo;s next</CardTitle>
</CardHeader>
<CardContent>
<p className="text-sm text-muted-foreground leading-relaxed">
Try all three prompts above. Once your agent has run each one successfully, we&rsquo;ll
capture your domain&rsquo;s skills and policies here.
</p>
</CardContent>
</Card>
)}
</section>
</main>
)