Full Transcript

·YouTLDR

How Senior Engineers Actually Build With AI in 2026 | Build a Full Stack Systems Architecture App

3:58:11EnglishBy JavaScript MasteryTranscribed Jul 15, 2026
Analyze another video with Pro30-day money-back guarantee
0:00

It's 2026 and most of the senior

0:02

engineers I know aren't really writing

0:05

code anymore. They design the systems

0:07

and let AI handle the implementation.

0:09

And the gap between developers who can

0:11

do that and developers who can't is

0:14

dividing the industry right now. This

0:16

app is what that looks like when you do

0:18

it well. Real-time multiplayer SaaS, AI

0:21

agents running in the background, full

0:23

production code, and I didn't write a

0:26

single line of it. An agent built the

0:28

whole thing. And by the end of this

0:30

video, you'll have built it, too. The

0:32

same app, the same stack, the same way I

0:36

did. And here's what makes this

0:37

different from other AI build tutorials.

0:40

I built this app using the exact

0:42

methodology the app itself is designed

0:45

to teach. Specs first, architecture

0:48

defined, every feature planned before we

0:51

start building. I've been doing this

0:52

architecture work by hand before every

0:55

serious project for years. And at some

0:58

point, it hit me. I'm a developer. Why

1:01

am I doing all of this manually when I

1:03

could build the tool that does it with

1:05

me? So, I did. This is Ghost AI, a

1:09

real-time collaborative workspace where

1:11

you describe a system in plain English

1:14

and then AI agent maps it onto a shared

1:17

canvas live. Your team edits the design

1:20

together and when it's ready, the app

1:23

generates a complete technical

1:24

specification you can build from.

1:27

Now, if you've tried building anything

1:29

serious like this with AI, you already

1:31

know the wall.

1:33

The first few hours feel incredible and

1:35

then a week later, the agent has

1:37

forgotten every decision you've made,

1:39

one new feature breaks three others, and

1:42

the code base you were excited about

1:44

just starts fighting you.

1:45

That's not an AI problem. It's an

1:48

architecture problem. And it's the same

1:50

wall all of you are hitting in your

1:52

careers where the senior dev advice

1:54

online sounds great as long as you're

1:57

already senior. The developers who win

2:00

in this market aren't avoiding AI, and

2:03

they're not handing everything over to

2:05

it, either.

2:06

They're learning to think like senior

2:08

engineers, and then using AI to build at

2:11

the speed of one. And that's what this

2:13

video teaches. By the end, you'll know

2:16

how to design a system before writing

2:19

any code, how to use the six-file

2:21

context system I write before every

2:24

project, and how a senior engineer

2:27

actually thinks when working with AI.

2:29

Oh, and that six-file context system I

2:31

just mentioned, I've packaged it into a

2:34

free guide you can grab and use on any

2:36

of your upcoming projects, not just this

2:38

one. The link is down in the

2:39

description. Next, the stack is Next.js,

2:43

React 19, Liveblocks for real-time

2:46

collaboration with agents, Trigger.dev

2:49

for background work with AI agents,

2:51

Clerk for auth and user management,

2:54

Prisma and Postgres for data, Vercel

2:57

Blob for storage, all production grade

3:00

and deployed by the end of the video.

3:03

So, one person with the right system can

3:06

now build what used to take a team. And

3:09

by the end of the next few hours, that

3:11

person is you.

3:12

So,

3:13

let's build it.

3:17

Before we open up a single tool, I want

3:19

to spend the next 10 minutes on the part

3:22

of the video that I think is most

3:24

important, and that decides whether what

3:27

you're building ships or falls apart in

3:30

the third week. There's going to be no

3:32

syntax and no code in this section.

3:35

Rather, what I'm teaching you is the way

3:37

that I think before I write a single

3:40

prompt, and the way I plan a build

3:43

before I touch the agent, and the system

3:46

that I use to keep AI from drifting

3:48

halfway through a project.

3:50

So that by the end, you'll know the

3:52

conversations to have before you build

3:55

the six-file context system that turns

3:57

an AI agent from a guesser into a

4:00

developer who already knows your

4:02

codebase,

4:03

and how to break any project into units

4:06

the agent can ship cleanly one at a

4:08

time. So, if you've been frustrated by

4:11

AI breaking your code,

4:13

then this is the video that fixes it.

4:14

The intro touched on something I want to

4:17

sit with for a minute.

4:19

You heard me say senior engineers a lot.

4:22

And if you're early in your career, that

4:24

framing probably hit a nerve. So, let me

4:27

be direct about what I mean.

4:29

The job market for developers right now

4:32

is harder than it was 2 years ago.

4:35

Some entry-level work has been

4:37

automated. Clients who used to hire

4:39

freelancers for straightforward projects

4:42

are now doing it themselves. And a lot

4:44

of people who learn to prompt without

4:47

learning how to think are now flooding

4:49

the market. So, if you're worried about

4:51

that, you're not wrong to be worried.

4:54

But the developers getting squeezed

4:56

aren't the ones who learned deeply.

4:59

They're the ones who learned just enough

5:01

to execute without understanding the

5:03

system behind it. That was always a

5:06

fragile place to be.

5:08

And AI just made that fragility visible

5:11

faster.

5:12

So, the way through is to learn the kind

5:15

of thinking that AI cannot replace. The

5:18

architectural thinking, the

5:20

systems-level judgment, and then use AI

5:23

to build at the speed of someone twice

5:25

your experience. The clearer your

5:28

understanding of what you're building,

5:30

the better the AI output. Which means

5:32

that learning properly isn't a waste of

5:35

time in the AI era. It's the best

5:37

investment that makes everything else

5:39

possible.

5:41

So, when I say things like think like a

5:44

senior engineer in this video, I don't

5:46

mean you need 10 years of experience to

5:48

apply this.

5:50

These are learnable habits and you can

5:52

start building them today on this

5:54

project. And here's something most

5:56

developers outside of Big Tech don't

5:58

really know. At Google, Amazon, and

6:01

Netflix, before any serious project

6:04

starts, engineers spend weeks writing

6:07

documents and sometimes months.

6:10

Design docs, one-pagers, RFCs, the

6:14

format changes by company, but the

6:16

principle is the same.

6:18

Figure out what you're building before

6:21

you build it.

6:22

And senior engineers at these companies

6:24

sometimes go months without writing

6:26

production code. They're designing

6:28

systems, making architectural decisions,

6:31

reviewing what other engineers ship. So,

6:34

software engineering has never really

6:36

been about typing the most lines per

6:38

day.

6:39

It's always been about thinking clearly

6:41

about what should exist before you build

6:44

it.

6:45

And AI didn't invent that discipline. It

6:48

just made it the most important skill in

6:49

the room. And that's the whole

6:51

foundation for the spec-driven agentic

6:54

development course that I've been

6:55

developing for a long time now. So, if

6:58

you want to stay up-to-date on how

7:00

that's going and receive an occasional

7:01

email where I share my thoughts at the

7:03

current state of the industry, I'll

7:05

leave the link down below so you can

7:06

subscribe for the newsletter. But, let

7:08

me immediately make it concrete in this

7:10

video.

7:11

Here are two different prompts that two

7:14

different developers might write to

7:16

build the same feature.

7:18

The first one says, "Build me a SaaS app

7:21

with authentication and a real-time

7:23

canvas."

7:24

And the second one says,

7:25

"I'm adding a LiveBlocks room provider

7:28

to the workspace route.

7:29

Auth is already handled by Clerk

7:31

middleware. The canvas uses React Flow.

7:35

Room tokens should be issued only after

7:38

verifying project membership. Wire the

7:41

provider into the existing workspace

7:43

layout without touching the sidebar or

7:45

navbar. Both developers want roughly the

7:49

same thing.

7:50

But the second prompt reveals a

7:52

developer who knows their auth layer,

7:54

understands their component boundaries,

7:57

and knows what should and shouldn't be

7:58

touched. They've thought about the

8:00

system, so they're communicating

8:02

decisions and not wishes.

8:04

So, the AI isn't smarter when it reads

8:07

the second prompt. The developer is.

8:10

And that is the difference between vibe

8:12

coding and what I'm going to teach you,

8:14

which is spec-driven development, which

8:17

is also the premise behind that

8:19

spec-driven agentic development course

8:21

that I'm actively developing. Vibe

8:23

coding focuses on the outcome.

8:26

You describe what you want, let the

8:28

agent run, and react to whatever comes

8:31

out.

8:32

For a weekend prototype, that's fine.

8:35

But for anything you're going to

8:36

maintain, it just collapses.

8:39

New features break the old ones, the

8:42

code base starts contradicting itself,

8:44

and you spend more time untangling AI

8:46

mistakes than actually building.

8:49

Spec-driven keeps the thinking with you

8:52

and gives the agent a system to execute

8:55

against.

8:56

You stay the architect and the agent

8:59

becomes the implementation engine. So,

9:01

how do you actually build that system?

9:03

Because that's the part that nobody

9:05

really teaches. It starts before you

9:07

open up any AI coding tool, with a

9:10

conversation. When I get an idea for

9:12

something I want to build, I open up a

9:15

planning AI, ChatGPT, Claude, or Gemini,

9:18

whichever's on hand, and I talk through

9:20

it. What does this thing actually do?

9:22

Who uses it? What are the core flows?

9:25

Where are the complex patterns, and what

9:26

could go wrong?

9:28

I push back on the answers and let AI

9:31

pressure test my thinking until the

9:33

system becomes clear in my head. This

9:36

conversation is the work. It's what

9:38

senior engineers do before they build,

9:41

except they usually do it in their head

9:43

or on a whiteboard. But, doing it with

9:45

AI externalizes it and makes it faster.

9:49

When the system is clear, you write it

9:51

down. And that's where the six-file

9:53

context system comes from.

9:54

Not from sitting at a blank page trying

9:56

to write documentation, but from taking

9:59

the output of the architectural

10:01

conversation and organizing it into

10:03

documents that travel with the project

10:06

for its entire life. For Ghost AI, I

10:09

organized everything into one folder

10:11

called context.

10:13

Six files, and what matters is that

10:15

before your AI agent writes anything, it

10:18

already knows what you're building, how

10:20

it fits together, what the rules are,

10:22

and where things stand right now. That's

10:24

what you're going to learn about in this

10:26

video. But, very quickly, here's what

10:28

each one of them does at a glance.

10:31

The project overview covers what the

10:34

product is, who is it for, the core

10:36

flows, and what's deliberately out of

10:39

scope.

10:40

The architecture file defines the tech

10:42

stack, the boundaries between layers,

10:45

and the invariants the codebase must

10:47

never break. The code standards keeps

10:50

the agent consistent across every unit

10:52

of the build with shared TypeScript and

10:55

Next.js conventions. The AI workflow

10:58

rules keep the agent disciplined,

11:00

defining how to scope work and what to

11:02

do when something needs a decision.

11:05

The UI context holds design tokens and

11:08

component conventions, so the UI stays

11:11

coherent across every page the agent

11:13

ships.

11:14

And the progress tracker, which is the

11:16

one most developers skip and most need,

11:19

holds the current phase, what's in

11:21

progress, what's complete, and the

11:23

architectural decisions made along the

11:25

way. It's the only file that actually

11:27

updates constantly throughout the build.

11:30

And it's how the agent picks up exactly

11:32

where you left off in a single prompt.

11:34

These six files are what make the

11:37

difference an AI agent that drifts and

11:40

one that executes. And you don't have to

11:42

build them from scratch for every

11:44

project. I've put together a free blank

11:47

template of all six files with

11:49

step-by-step instructions on how I

11:51

generate them using AI for whichever

11:54

project I'm working on. And it's not

11:56

specific to Ghost AI. It works for any

11:58

application. Click the link down in the

12:00

description to get it so you can apply

12:02

this methodology to your own project

12:04

starting today.

12:06

We'll open up each one of these and I'll

12:08

walk you through what's actually inside

12:10

in the next lesson when we set up the

12:12

project architecture for real. But yeah,

12:14

once these six files exist, the build

12:17

runs in units. We're going to break down

12:19

the project into specific scoped pieces

12:22

before we start. Not vague phases like

12:25

build a dashboard, but concrete units

12:28

small enough to build in a single focus

12:31

session with clear conditions for what

12:33

done looks like. That means that

12:35

together for Ghost AI, we'll map out the

12:38

entire build. Each lesson will have its

12:41

own spec file. The spec defines the

12:44

goal, the design decisions, the

12:46

implementation details, dependencies,

12:48

and a checklist of what has to be true

12:51

before the unit is complete.

12:53

You write the spec in the same way you

12:55

write the context file through a

12:57

conversation with the planning AI.

12:59

Then you give the spec to your coding

13:01

agent in one prompt. Make sure to read

13:04

that spec. Mark that unit as in progress

13:07

in the progress tracker. Implement it

13:09

exactly as specified without going

13:12

beyond scope. The agent will read your

13:14

spec, read your context file, and build

13:17

against a defined system instead of

13:20

guessing.

13:21

You review it against the checklist and

13:24

if it passes, you close the unit, push

13:26

the code, and move to the next spec. If

13:29

something's off, you write a focused

13:32

corrective prompt, exactly what's wrong,

13:35

exactly what you expect, fix that

13:36

specific thing, and move on.

13:39

That's the entire workflow, the same one

13:41

I used to build Ghost AI, and the same

13:44

one you're about to use to build it with

13:47

me. Oh, and last thing before we begin.

13:49

These files take time to write. The

13:52

conversation, the architectural

13:54

decisions, the unit planning, all of it

13:57

is real upfront work. And a lot of

13:59

developers skip it because they want to

14:01

feel productive immediately. I mean,

14:04

I've done that as well. So, you open up

14:06

the agent, type a prompt, and start

14:09

watching code appear.

14:10

But, that's the trap. The time you save

14:14

by skipping this is the time you lose in

14:17

week three debugging AI output that's

14:20

drifted away from anything coherent. So,

14:23

do this work once, and you'll do it

14:25

faster every project after. And finally,

14:28

it's time to build.

14:32

Okay, enough talk, and let's build.

14:36

Open up your desktop and create a new

14:38

folder. Call it something like Ghost AI,

14:42

and then simply drag and drop it into

14:44

your favorite code editor. For this

14:46

video, I'll be using VS Code as we have

14:49

the file explorer on the left side, we

14:51

have the code in the middle, and then we

14:53

have the chat window which you can open

14:55

up by pressing command shift P, and then

14:58

just search for chat. Or, I think it's

15:00

just command shift I to open it as well.

15:03

No matter which agent you're using, is

15:05

it Copilot, Claude, Codex, or whatever,

15:07

the interface is more or less the same.

15:10

This is how it looks like on Codex, and

15:12

this is just general chat.

15:15

So, this video is completely agentic

15:17

tool agnostic. The thinking and the

15:20

specs are what matters. The tool is

15:23

completely your call. I'll personally be

15:25

using Claude code as that's what most

15:27

people are using and what's we're using

15:29

internally within JSM. If you want the

15:31

budget option, you can go with Codex.

15:34

There's the Go plan, which is super

15:35

cheap, the plus plan, which is also

15:37

cheap, and I think they're also offering

15:39

a month for free. And if you're already

15:41

paying for something like Cursor, Win

15:43

serve, or any other AI agent, just use

15:46

that. I'll make sure you don't spend a

15:48

lot of tokens, no matter what you're

15:49

using, and that you learn a ton. Okay,

15:52

so let's get started.

15:54

We'll start with a fresh Next.js

15:56

project. I'll do this manually with no

15:58

prompts and agents.

16:00

It's just a single command and it takes

16:02

a couple of seconds and that'll give us

16:04

a clean and predictable foundation to

16:06

build everything else in top of. So,

16:08

simply open up your integrated terminal

16:11

and run MPX create next app at latest

16:15

dot, which is going to create it in the

16:16

current repository. It'll ask you

16:18

whether you want to install the Next.js

16:20

installer, so you can just say Y, yes,

16:22

and proceed.

16:23

And I also want you to know that you

16:25

don't necessarily have to follow along

16:27

by using Next.js.

16:29

Of course, the majority of the context

16:31

for AI agents we'll be working with is

16:33

going to be featured around Next.js, but

16:36

if you want to build this in TanStack,

16:38

Angular, Vue, or anything else, the

16:40

concepts will still be as valuable.

16:43

So, let's just say Y for now. It's going

16:45

to ask you whether you would like to use

16:46

the defaults, so just press enter,

16:49

which is going to install React,

16:51

TypeScript, ESLint, Tailwind CSS, and

16:54

the app router. Let's give it a moment

16:56

until it finishes. Once the installation

16:58

finishes, we need to clear out the

17:00

default boilerplate.

17:01

That's going to be within app and then

17:04

page. Next.js ships with a lot of

17:06

placeholder content that, honestly, we

17:08

don't really need. Now, you could go

17:11

ahead and clean it up manually by

17:13

opening up your globals.css

17:15

and cleaning everything besides the

17:17

Tailwind CSS directives, deleting some

17:20

SVGs and other stuff within the public

17:22

folder, and replacing the page.tsx with

17:25

a minimal component.

17:27

Or we can use this as our first

17:29

interaction with the coding agent.

17:33

So, go ahead and open up your agent of

17:35

choice. I'll just press command shift I

17:38

to open up my chat sessions. And what I

17:40

love about VS Code is that it's not

17:43

shying away from the agnostic approach

17:45

to agents.

17:46

You can use their agents right here,

17:48

such as Copilot CLI,

17:51

or if you head over to extensions and

17:54

then install an extension like Claude

17:56

Code,

17:58

which has like 12 million downloads, or

18:01

something like CodeX, which has 8

18:03

million downloads,

18:05

and I installed both, you can actually

18:07

just navigate over to them by selecting

18:10

additional views,

18:11

and then choosing the extension you

18:13

want. Of course, alternatively, you can

18:15

also just run all of these agents within

18:17

the CLI by typing Claude, and you're in.

18:21

For the longest time, I've been using

18:23

Claude CLI, but the Claude VS Code

18:26

extension recently got so much better,

18:28

and honestly, it's working the same way

18:30

as the CLI does, but with a bit of a

18:32

nicer UI and a graphical interface that

18:35

allows us to use it in an easier way.

18:37

So, that's what I'll be proceeding with.

18:39

I'll expand it so we have more space to

18:41

work with, and you can notice that

18:43

there's a little microphone button right

18:44

here. So, you can either tap it or hold

18:47

the command D key to speak into it. With

18:50

CodeX and other agents, you can either

18:52

type it manually,

18:53

or what you can do is use a tool like

18:57

Whisper Flow, not sponsored by the way,

18:59

which is what I use to write prompts

19:02

when I speak with agents. Speaking is

19:04

just much faster. If you install it, you

19:06

can just press the command key

19:09

and start speaking into it. Like, clean

19:12

up this Next.js boilerplate. Strip

19:15

globals.css down to just the Tailwind

19:17

directives. Delete all SVGs in the

19:20

public folder, but keep the favicon.

19:23

Remove page.module.css.

19:26

Replace page.tsx with a minimal

19:28

component that just renders a centered

19:31

div saying Ghost AI.

19:34

And I can stop it right here, and you

19:36

can immediately see the output. Cool

19:38

stuff, right? Go ahead and write

19:40

something like this, and let's see how

19:42

well Agent handles it.

19:43

Also, just so we don't have to give it

19:46

permissions every single time whenever

19:47

we're doing something, for now I will

19:49

say edit manually. Oh, and of course, we

19:52

have to talk about the actual models

19:54

we'll be using. In this case, we're

19:56

using the default model, which is Opus 4

19:58

7 with a million token context, which is

20:00

amazing, but you're going to hit the

20:02

limits very soon. Instead, you'll be

20:04

able to follow along this entire build

20:06

with using Sonnet 4 6. It's fast, it's

20:10

inexpensive, and it sometimes get lost

20:13

unless you have the context files, which

20:16

I'll teach you how to build so you guide

20:17

it in a bit of a better way, and make it

20:20

work much more like Opus does. So,

20:23

that's what I'm going to select. If

20:24

you're working with Codex, you can also

20:26

go ahead and choose any model you'd

20:27

like, like 5 4, 5 3, or anything else.

20:31

Okay, let's give it a shot. It's going

20:33

to think for a couple of seconds, read

20:35

all the necessary files, and apply all

20:38

four changes.

20:39

Globals, just the import. Page, minimal

20:42

centered Ghost AI component. Deleted

20:44

five SVGs, but kept the favicon. And

20:47

this file that I tried to trick it with

20:48

doesn't really exist, so there's nothing

20:50

to delete.

20:51

You can see all the changes in the diff

20:53

right here, and you can also see which

20:55

files have been modified on the left

20:56

side.

20:58

There we go, just simple Ghost AI, and

21:01

just the import for Tailwind. Which

21:03

means that these classes right here

21:04

should work to center the div. Before we

21:06

run it, let's actually head over to

21:08

package.json.

21:10

Make sure that the project name is set

21:12

to Ghost AI, and let's run it by running

21:15

npm run dev.

21:18

It'll run it on localhost:3000. So, if

21:21

you open it up, you should be able to

21:23

see something that looks like this.

21:25

This means that our project is

21:26

initialized, it's cleaned up, and now we

21:29

are ready to move to the part that

21:31

actually determines just how well

21:33

everything goes. Setting up our context

21:36

files and planning the build before the

21:38

agent writes a single feature. So, let's

21:42

do that next.

21:45

Not that long ago, I talked about how

21:47

senior engineers spend weeks designing

21:50

systems before anyone writes a line of

21:53

code.

21:54

They write design docs, they define

21:56

boundaries, and they make decisions

21:59

early on so that everything else becomes

22:01

predictable.

22:02

So, let me show you how we are going to

22:04

approach that.

22:06

We're going to create a new folder right

22:08

here in the root of our application,

22:10

which we're going to call context.

22:12

Everything inside of it is what your

22:15

coding agent will read before it does

22:17

anything. This is how it knows your

22:19

project, and this is how it stays

22:21

consistent across every session, commit,

22:24

or unit of the build.

22:26

So, you can switch multiple agents and

22:28

share the context file with another

22:30

developer so he can continue working

22:32

with his agent from there. That's the

22:34

catch. We're going to have a couple of

22:36

different files within the context

22:37

folder. Now, I don't want you to do a

22:40

lot of copy-pasting, so I'll provide you

22:42

with the final zipped context folder so

22:44

we can easily review everything

22:46

together. So, delete the context folder

22:48

you just created. In the video kit link

22:51

down in the description, get the zipped

22:53

version of the folder, unzip it, and

22:55

then just drag and drop it in.

22:57

And here, you'll see six, well, seven

23:00

different files with this agents.md.

23:02

Might seem scary at first, but don't

23:05

worry as I walk you through every single

23:07

one of these files and show you how you

23:09

can create them for yourself in the

23:10

future. Let's start with the project

23:13

overview. And let me walk you through

23:16

why it's structured the way it is

23:18

because understanding this is more

23:20

valuable than just copying the file.

23:23

Currently, we're looking at the markdown

23:25

version of the file, but in VS Code, I

23:28

believe this is done by default, you can

23:30

also open the preview to the side by

23:32

pressing command K and then V or just

23:34

pressing this icon at the top and then

23:36

you can close the actual MD and just see

23:38

the formatted version. I think it's a

23:40

bit easier to read this way. I typically

23:42

open up these project overview documents

23:45

with a one paragraph summary and then a

23:48

numbered list of goals. This gives the

23:51

agent the big picture immediately. So,

23:54

when a requirement gets ambiguous 3

23:56

weeks into the build and it will, this

23:59

is where you resolve it. The agent uses

24:02

this constantly to understand intent

24:05

when a spec isn't specific enough.

24:08

Notice the goals are concrete and

24:10

measurable, not build a good canvas.

24:13

Instead, let authenticated users create

24:16

and manage architecture projects or let

24:19

AI generate an initial architecture from

24:21

a natural language prompt. The agent

24:24

knows exactly what success looks like.

24:27

And yeah, this is useful for us while we

24:29

are going through the process of

24:30

building the app. Just so you know what

24:32

we are building. Just so everybody in

24:34

the team, in this case me teaching you

24:36

and you following along and you building

24:38

it with me, are all on the same page. Go

24:42

stay eye is a real-time collaborative

24:44

system design workspace. Users describe

24:47

a system in plain English and then AI

24:49

agent maps that system onto a shared

24:51

canvas. Collaborators refine the

24:54

architecture and the app generates a

24:56

technical specification document from

24:58

the resulting graph.

25:00

Okay, great. We have the overview, we

25:03

have the goals, and then we have the

25:05

core user flow. So, let me zoom this in

25:08

and let's go through this together

25:10

because this sequence matters. The agent

25:13

can sometimes lose track of how features

25:16

connect to each other.

25:17

By defining a full flow from sign in to

25:21

the spec generation, we make the logical

25:23

sequence explicit. So, the agent won't

25:26

try to build the spec generation feature

25:28

on the login page because it knows the

25:30

user has to create a project and the

25:32

design architecture first. In the

25:34

features, we get specific about

25:37

technologies. And I want to take a

25:39

second on this because the tools I chose

25:41

for Ghost AI weren't random. I took a

25:44

lot of time to choose what actually

25:46

makes sense. Starting with

25:47

authentication. For user sign in, route

25:50

protection, project creation, ownership,

25:53

and collaborator access, we're using

25:55

Clerk. Could you have gone and created

25:57

the full auth from scratch? I mean,

25:59

sure, but it would take you a couple of

26:01

days up to a couple of weeks to do it

26:03

properly, even with AI.

26:06

And when you do it, it's never going to

26:07

be as secure as something like Clerk.

26:10

And nowadays, the speed of shipping

26:12

matters more than anything. If you have

26:15

a specific product you want to push, you

26:17

want to get it in front of potential

26:18

users as soon as possible. And that's

26:20

why for many of these agentic builds,

26:22

it's just a no-brainer to plug and play

26:25

Clerk into it. Especially considering

26:27

just how well Clerk works with your

26:30

agents. You can either just copy the

26:32

prompt or use Clerk skills with

26:36

whichever agent you're using.

26:38

That way, it'll immediately become a

26:40

professional developer at using Clerk

26:42

and it'll be able to implement it within

26:45

any project. And there's also the MCP,

26:48

which is a server that allows AI agents

26:51

like Claude,

26:52

or others

26:54

to access Clerk as the case snippets and

26:56

implementation patterns, and basically

26:58

implement everything for you. Oh, and

27:00

not to mention that Clerk CLI is also

27:03

being worked on. You'll just be able to

27:06

ask your agent to use the Clerk CLI to

27:08

add auth to your app, allowing you to

27:10

not even have to leave your terminal or

27:12

copy and paste any API keys. Clerk right

27:14

now really is the leader in agentic

27:17

development. And with a completely free

27:19

pricing of up to 50,000 monthly

27:23

recurring users, it's a no-brainer for

27:25

me to build all of my applications with

27:27

it. So, while we're here, I'll leave the

27:30

link down in the description. You can

27:31

click it, and then sign up so that we

27:33

can very soon more easily get started

27:35

with building. Okay, on top of auth, the

27:38

most important part of our application

27:40

is the collaborative canvas.

27:42

And in this case, I wanted to make it

27:45

real-time. So, for any kind of canvas,

27:47

it makes sense to use React Flow. And if

27:50

you want to make any part of your

27:51

application live with cursors, so you

27:54

can see what other people are doing,

27:55

presence indicators, and node or edge

27:58

editing, I mean, it just makes sense to

28:00

use Liveblocks. Liveblocks is the leader

28:03

for anything multiplayer, but not only

28:05

apps, agents as well. Let me show you

28:08

what I mean.

28:09

I mean, this is the app without

28:11

Liveblocks, and then if you add it, you

28:13

immediately get live avatars. You can

28:16

get the AI chat to generate something,

28:19

and you can also leave comments and

28:21

track what people are doing within your

28:22

app, which is super useful for the

28:24

collaborative canvas we're building.

28:26

But, what's even cooler is interacting

28:29

directly with AI assistants to do

28:32

something within our application. I

28:33

mean, if you try to build all of this

28:35

from scratch, it would definitely take

28:37

some time. But, with their AI assistants

28:39

feature, you can just watch an AI do it

28:42

for you. Oh, and this whole React Flow

28:44

thing you're seeing, that got released

28:46

recently, which means that in this

28:48

video, we're building the latest stuff

28:49

out there. Oh, and like Clerk and the

28:52

many other amazing dev tools that are

28:54

adapting to agentic development,

28:56

Liveblocks also offers the agent skills,

28:59

which you just have to install, and your

29:01

agent will immediately know what it has

29:02

to do to make the Liveblocks integration

29:05

work. I'll teach you about all of that

29:07

as we continue with the build. After the

29:09

canvas, we have the starter system

29:11

design, which I decided to have because

29:13

it's very difficult for people to start

29:16

with a blank canvas. So, I want to have

29:18

some kind of a curated library of

29:20

pre-built system design templates, where

29:23

users can import a starter template into

29:25

the canvas at any point during editing.

29:27

But also, if the template doesn't do

29:29

what you want it to do, we're going to

29:31

allow our users to generate a system

29:33

design from a prompt with AI. Then, that

29:37

output will be structured as canvas

29:39

nodes, and then we'll write it onto the

29:41

canvas. And finally, we'll take a look

29:43

at everything that is on the canvas, and

29:45

we'll convert it into a technical

29:47

specification in a markdown format. And

29:49

then users will be able to view and

29:51

download the generated specs. Oh, and an

29:54

additional thing that I want to teach

29:55

you is while we're generating stuff with

29:58

AI, that'll obviously take time. As the

30:00

user will likely provide a long idea of

30:03

how they want to architect their app,

30:05

and then the AI output is going to take

30:07

potentially a minute or two. And running

30:10

a more than 60-second AI generation call

30:13

inside a Next.js API route will just

30:16

time out in production. So, that's why

30:18

I'll also teach you how to use

30:19

Trigger.dev. It'll allow us to run

30:21

background tasks that can run as long as

30:24

they need to, with the retry logic and

30:26

status tracking built-in. The way in

30:28

which we'll combine Liveblocks and

30:30

Trigger.dev is pretty amazing. So, we'll

30:33

have to be very clear in letting our AI

30:35

agent understand that it doesn't have to

30:37

invent a custom web socket

30:39

implementation when Liveblocks is

30:41

already in stack, Or it won't try to run

30:44

AI generation inside a request handler

30:47

when trigger dev is defined as the layer

30:49

for that work. Naming the tools we're

30:51

going to use for the project in advance

30:53

is crucial. Oh, and trigger allows you

30:55

to do so much more. Recently, they've

30:57

been diving deeper into allowing you to

30:59

build and deploy AI agents, which is

31:01

something we can explore in an

31:02

additional video. But in this one, we'll

31:05

also focus on many of its features,

31:07

specifically running background tasks

31:09

and reporting back to the front end. So

31:11

while a long task is happening, we can

31:13

keep the user updated on what's

31:15

happening behind the scenes and allow

31:17

the user to continue doing whatever

31:19

they're doing on the application because

31:20

we're no longer blocking the front end

31:22

side while handling a specific task. So

31:24

I'll leave a special link pointing to

31:26

trigger as well as live blocks down in

31:29

the description so you can create your

31:31

accounts and then we'll be able to

31:32

immediately dive into the development.

31:34

Then we have the scope part which

31:36

contains the in scope and out of scope

31:38

features. And it's maybe the most

31:41

important section for keeping your build

31:43

focused. The out of scope section is

31:45

doing serious work right here.

31:48

Billing and subscription systems,

31:50

enterprise permissions, version

31:51

specification history and so on. This is

31:54

telling the agent, don't even think

31:56

about them. But we can add them later on

31:59

after we build the base of our

32:00

application. This is great because it

32:02

keeps every session focused on what we

32:04

are actually building.

32:06

And finally, there's success criteria.

32:09

Here, we define what actually matters.

32:12

These are the benchmarks that your agent

32:14

and you can verify against after each

32:17

major feature lands. Not, does it look

32:19

right? But, can a signed in user create

32:22

and open up a project? Can multiple

32:24

users collaborate? Can the graph be

32:26

converted into a persisted markdown

32:28

specification?

32:30

Very simple, yet concrete.

32:33

So that's our project overview. Written

32:35

in a way that makes sense to agents, but

32:38

also other team members working on the

32:40

project. Next, let's take a look at the

32:42

AI workflow rules. This file is

32:45

different from others in a way that it's

32:48

not about what we're building. It's

32:50

about how the agent behaves when

32:52

building it. And the most important rule

32:54

right here is to work on one feature

32:57

unit or subsystem at a time. We don't

33:00

want to combine unrelated system

33:01

boundaries in a single implementation

33:03

step. And that single rule prevents most

33:06

failures that the agents cause.

33:08

Basically, all of these rules right here

33:10

tell the agent, "Stay in your lane.

33:13

Focus on one part of what you're doing,

33:15

and then move on to the next one." After

33:17

that, we have the code standards. So,

33:20

you can open up that, and let's quickly

33:23

take a look. This file is what keeps the

33:25

code base consistent from our first all

33:28

the way to the last chapter. Without it,

33:31

the agent would drift away. Like,

33:33

specific patterns that it uses for API

33:35

routes when implementing feature five

33:37

might look different from feature 16. Or

33:40

maybe it's going to change some things

33:41

regarding the TypeScript types, or how

33:43

it uses Next.js, how it styles things.

33:46

But here, we add that consistency. Like,

33:49

we tell it, "Make sure to have strict

33:52

mode enabled, and avoid using any." Or

33:54

we're telling it to add use client only

33:57

when the component needs browser

33:59

interactivity. Or for styling, we're

34:01

telling it, "No raw Tailwind color

34:03

classes. Reference the tokens through

34:06

the Tailwind utility names." I think you

34:08

get the point. We want to stay

34:09

consistent. Then, we have the UI

34:12

context, which, as you can guess, dives

34:14

a bit deeper into theming. Like, when I

34:17

was initially coming up with a design

34:18

for this application, I wanted to have

34:20

something that is simple, yet functional

34:23

and modern. And I spoke with AI a bit to

34:26

generate this theme. Dark mode, no light

34:29

mode, Uh, all colors are already

34:31

defined, and we're telling the AI to use

34:34

these colors.

34:35

And again, if you're wondering how

34:37

exactly I created all six of these

34:40

documents by speaking with different AI

34:42

agents, that's something that I'll cover

34:45

in much more detail within the

34:47

spec-driven agentic development course.

34:49

So, you can join the waitlist in the

34:51

description. But, I think you get the

34:52

idea that I didn't just sit down and

34:54

handpick every color in the file. I

34:56

described the aesthetic that I wanted to

34:58

AI, dark, technical, precise, something

35:01

that feels like an engineering tool, and

35:04

then I went back and forth on the

35:06

palette, um, the token names, and this

35:08

is what it came up with. Also, some

35:10

font, border radiuses, and so on. That's

35:13

exactly how you should approach your own

35:15

UI. You don't need to be a designer, but

35:18

you need to know the feel you're going

35:20

for, and then AI can help you get there.

35:22

Oh, and while the project overview gave

35:25

some information about the project, the

35:28

architecture context will give you the

35:30

blueprint on how to build it. Here, I

35:32

specified the tech stack,

35:35

like every technology that we want to

35:36

use, alongside the role that it has

35:38

within the application. For auth, we're

35:40

using Clerk for user identity and route

35:42

protection. For databases, it's going to

35:44

be Prisma and Postgres. For canvas, it's

35:47

going to be Liveblocks and React Flow

35:50

for real-time collaborative canvas.

35:52

For background tasks, it's going to be

35:54

Trigger.dev, and for storage, we're

35:55

going to use Vercel Blobs. We also

35:57

defined some system boundaries, like

35:59

where we're going to put the request

36:01

handlers. Trigger is what we're going to

36:03

use for the long-running background

36:05

jobs, and some other folders that we're

36:07

going to use.

36:08

Then, we defined the storage model,

36:10

where and how we're going to save

36:12

something. And specifically here, I

36:14

decided to use a hybrid storage model,

36:16

which is another senior-level decision.

36:18

We aren't going to be stuffing massive

36:21

JSON blobs or three-page markdown files

36:23

into our database. Instead, we're going

36:25

to use Postgres only for metadata and

36:28

Vercel blob for the actual files. And

36:31

this keeps our database lean. And this

36:33

is important. We also need to tell it

36:35

how different tools are working

36:37

together.

36:38

Since we're using Clerk with Liveblocks,

36:40

we need to set a strict rule that

36:42

project memberships must be verified

36:45

before a Liveblocks token is ever

36:47

issued. This ensures that our Ghost AI

36:50

isn't just collaborative, but secure.

36:52

And finally, invariants are the rules

36:55

that the system must never violate. For

36:57

example, request handlers do not run

37:00

long-lived AI works. That belongs in

37:02

background tasks through trigger.dev.

37:05

Metadata and large artifacts are stored

37:07

in separate layers. Auth and ownership

37:10

are enforced at every mutation boundary.

37:12

Client components only used when needed.

37:15

And the canvas schema must remain

37:17

consistent. Of course, we'll dive much

37:19

deeper into this when we actually dive

37:21

into building the application. But I

37:23

wanted you to have a good idea of what

37:24

it is that we're building. And finally,

37:26

there's the progress tracker. This is

37:29

the only file in the context folder that

37:31

will look completely different by the

37:33

end of this video.

37:35

Right now, it is completely empty.

37:37

Intentionally, because it reflects the

37:40

actual state of the project. And right

37:42

now, nothing has been built yet. So, as

37:45

we complete each lesson, we're going to

37:47

update this file. The current phase,

37:49

what's in progress, what's complete, and

37:51

what's coming next. And remember what I

37:53

said in the beginning about agents

37:55

having no memory between sessions.

37:57

This file is the solution to that

37:59

problem.

38:00

At the start of every new session,

38:02

whether that's tomorrow, next week, or 6

38:04

months from now, one prompt is all it

38:06

takes to restore full context. Our agent

38:10

will read the progress tracker,

38:11

understand exactly where the project

38:13

stands, and pick up exactly where you

38:15

left off. So, you don't have to

38:17

re-explain yourself. Even though it's

38:19

going to be a small file, it does more

38:21

work than any other file in the project.

38:23

Oh, and finally, there's the agents.md

38:26

file. Within it, we're going to wire

38:29

everything together. When you installed

38:30

Next.js,

38:32

that file was already created for you

38:34

automatically at the root. This is the

38:36

entry point file, and every major coding

38:38

agent has one. They just name it

38:40

differently.

38:41

Claude calls it claude.md, Cursor, Win

38:44

Surf, and others use different names,

38:46

but the idea is always the same. It's

38:48

the first thing that the agent reads at

38:50

the start of every session.

38:52

So, Next.js has already added the first

38:54

section for you, and it tells the agent

38:56

that this is a recent version, and its

38:58

training data might be outdated, so read

39:00

the install documentation before writing

39:02

any code. Good default, and we can leave

39:04

it, but now we can add our own part

39:06

below it.

39:08

So, copy your agents.md, delete it from

39:10

the context, and instead move it right

39:13

here. It's not long, so let's see what

39:16

do we have within it. We're basically

39:17

instructing the agent to read all six

39:20

context files in order before

39:22

implementing anything, and then to

39:25

update the progress tracker after each

39:27

change. And you might think, "Isn't this

39:29

going to waste context and tokens?"

39:32

Well, I mean, sure, it has to read

39:33

through the files, but that's not even

39:35

1/10 of how many tokens you're going to

39:37

save because there's going to be less

39:39

back and forth, and less mistakes and

39:41

bug fixing and correcting while you're

39:44

implementing the features in the first

39:45

place. So, that's the system. And now

39:48

that you understand it, let's start

39:50

building with it.

39:53

Now that the context files are ready,

39:55

before we build a single feature,

39:57

there's one more thing we need to set

39:59

up, and skipping this one is the most

40:01

common mistake I see in AI-assisted

40:04

projects.

40:05

And that is the globals.css

40:08

file.

40:09

This one right here. We've defined all

40:11

of our color tokens within our UI

40:13

context right here. But, if we don't

40:16

translate those tokens into actual CSS

40:18

custom properties within the globals.css

40:21

file, well, it'll just write the inline

40:23

colors. So, instead of maybe saying

40:25

something like text faint, it'll just

40:28

write #505060.

40:31

And the moment you want to change it,

40:32

you have to change it across all places.

40:35

So, let's set it up correctly. And this

40:36

is also the first real demonstration of

40:39

the spec-driven workflow in action. So,

40:42

let's do it properly. Create a new

40:44

folder inside side of the context folder

40:47

and call it feature specs, like this.

40:51

And then inside of it, create the first

40:53

spec file.

40:55

01

40:56

design system.

40:59

md. And then within it, we want to tell

41:01

it something like read the agents file

41:04

before starting.

41:06

Then, we need to tell it what we're

41:07

doing here, like adding the design

41:09

system and the UI components.

41:12

Then, we're telling it to install and

41:15

configure shadcn-ui.

41:17

And then we want it to add the following

41:20

components.

41:21

I figured we're going to use these

41:23

across the rest of the application.

41:24

Whenever we wanted to modify these files

41:27

after the installation, so we can

41:29

specify that.

41:30

We can also ask it to install

41:32

lucid-react for icons

41:35

and create a lib-utils.ts

41:38

with a reusable class names helper for

41:40

merging tailwind class names. Although,

41:42

I think it would do this by default,

41:44

it's good to mention it.

41:46

Finally, we want to ensure that all of

41:49

the components match the existing dark

41:51

theme within globals.css.

41:54

And then, we want to apply some checks

41:56

when it is done.

41:58

For example, we want to make sure that

42:00

all components import without errors,

42:02

that the cn works properly, and that no

42:05

default light styling appears. This is

42:08

what we call a feature specification or

42:10

a feature spec.

42:12

Every unit we build from here on has

42:14

one.

42:15

The spec tells the agent exactly what to

42:18

do, exactly what not to touch, and how

42:21

to verify when it's done. No guessing.

42:24

So, let's open up our agent by pressing

42:26

command shift I. As I said, you can use

42:29

anyone. I'll use Claude code.

42:31

I'll start a new chat, and you can even

42:34

open up that file, which will

42:35

automatically put it within its context.

42:37

And then, you can tell it something like

42:39

read

42:41

add 01 design system update

42:45

the progress tracker MD file to mark

42:49

this as in progress.

42:51

And then, implement exactly as

42:55

specified. This is the same template

42:57

we'll use often, but the only thing

42:58

that's going to change is going to be

43:00

the spec of the feature we're

43:01

developing. So, let's go ahead and run

43:04

it, and notice what will happen before

43:06

the agent writes a single line of code.

43:08

It'll read the specification, and then

43:11

update the progress tracker. You can see

43:14

how it's going through all the files we

43:16

prepared for it, and only once it has

43:18

enough context, it'll write and save the

43:20

plan, and then execute it. And only when

43:23

it has a full plan, it will update the

43:25

progress tracker.md, and then start

43:27

doing it. So, we can already take a look

43:30

at the progress tracker,

43:32

and I'll open it up so we can see what

43:34

it is doing. And you can see that the

43:35

current phase is feature 01 system

43:38

design, the current goal to install and

43:40

configure shadcn with dark theme, and

43:43

that is currently in progress with the

43:45

feature 02 to be done. And it's also

43:48

adding the architectural decisions

43:50

needed for every step, as well as the

43:52

session notes. It's going to ask us

43:54

whether we want to run this command to

43:55

install shadcn, so I'll say yes, go

43:58

ahead. And after it installs everything,

44:00

it'll verify it with TypeScript. It

44:03

looks like it completed with zero

44:04

errors, and finally, it needs to update

44:06

the progress tracker to complete it.

44:09

So, we can already open it up right here

44:12

under progress tracker, and it should

44:14

move it from current phase over to

44:16

completed.

44:18

So, now at any point in our application,

44:20

if we come back 2 months later, it'll

44:23

know that it's using shadcn, Tailwind V4

44:26

with the following components, only

44:27

using dark theme,

44:29

and all the additional helpers,

44:31

as well as the architectural decisions.

44:33

So, once it finishes, the agent will

44:35

have configured shadcn, installed the

44:38

components, and verified that it all

44:39

works. So, what do you say that we test

44:41

it out?

44:42

Back on localhost 3000, the first good

44:45

sign should be that we're now officially

44:47

in dark mode. And if you head over to

44:49

the home page, that's going to be within

44:51

app page, we can try to use a shadcn

44:54

button component coming from components

44:57

UI button, and we can try to make it say

44:59

something like click me. You can see

45:01

it's coming from there,

45:03

and it follows our UI theme. That's it.

45:06

Our first feature, being the theming and

45:09

shadcn setup, is done. And that's the

45:11

spectrum workflow I was telling you

45:13

about. We define the specifications, we

45:16

run the prompt, we verify the output,

45:19

and then we move on. But just before we

45:21

move on to our second feature, I want to

45:24

add just one little extra step to this

45:26

whole workflow that's going to make our

45:28

codebase even more scalable,

45:30

predictable, and less error-prone. And

45:33

that is whenever we implement a specific

45:35

feature, let's also review it with Code

45:37

Rabbit. There's been a report that says

45:40

that AI code creates 1.7 times more

45:42

problems, which means that even though

45:44

we're faster, we're producing more

45:46

issues per PR. Also, the code becomes

45:50

less readable, naturally, and the error

45:52

handling isn't being done properly.

45:54

Security also suffers. So, let's add

45:57

that one additional line of defense.

45:59

Let's review every single feature that

46:01

we add to our project in the same way

46:03

that biggest teams, such as the

46:04

developers over at Nvidia, are doing it.

46:07

I'll leave the link down in the

46:08

description, so you can create your free

46:10

account and follow along.

46:12

You can log in with GitHub, and once

46:14

you're in, you'll be able to see that I

46:15

already gave it access to many of my

46:17

repos. But first, we got to push our

46:19

project over to GitHub. So, head over to

46:22

github.com/new

46:24

and create a new repo. You can call it

46:26

Ghost AI.

46:30

And just create it. Next, you can copy

46:32

these commands one by one, or you can

46:35

just copy all of them together,

46:37

open up your agent, and paste it. Then,

46:39

press enter. It's going to ask us

46:41

whether we're going to allow it to stage

46:43

all the changes. So, I'll say, "Yeah, go

46:45

ahead." And it'll also allow all future

46:47

commits, as that's going to help us

46:49

speed up the workflow, as well as adding

46:51

a remote, and finally push. You can see

46:54

that the changes have been pushed

46:55

successfully. So, if you come back and

46:57

reload,

46:59

you'll be able to see the current list

47:00

of changes over on the repo. It's always

47:03

good to make your project descriptive,

47:05

so you can remove the releases,

47:07

deployments, and packages, and add a

47:09

short description,

47:10

such as Ghost AI

47:13

is an interactive systems

47:16

architecture

47:19

builder.

47:20

We can route to the deployed website.

47:21

For now, I'll just route it to

47:23

jsmastery.com, and here we can put the

47:25

different topics, such as Next.js,

47:28

React, My Blocks, Clerk, Trigger.dev,

47:33

and even Code Rabbit, which we'll be

47:34

using for reviewing your code. And I

47:37

like how AI always adds nice commit

47:39

messages and not random gibberish that

47:41

I'm used to. But yeah, now back within

47:44

Code Rabbit, you can head over to add

47:46

repositories,

47:47

sign in, and give it access to all your

47:49

repos. And then, when you're back, you

47:52

can just find your project right here.

47:58

Which means that it's automatically

47:59

being tracked. So, as soon as we start

48:01

adding the real features to the app,

48:03

we'll be able to open up a PR for every

48:05

new feature as we're developing within a

48:07

large organization and then get it

48:09

reviewed and only when Code Rabbit gives

48:11

it a green light,

48:12

merge it over to main.

48:16

Now that our colors are in and the

48:18

initial Shards and components are in as

48:20

well, we are ready to start building the

48:22

actual application. And the first thing

48:24

that we need is the editor, including

48:27

the top navbar and the left sidebar

48:29

because they're the foundation of every

48:30

feature we're going to build from here

48:32

on. We're not yet touching off and we're

48:35

not building this project creation. We

48:37

just need to establish the layout so

48:39

when these features come, they have

48:41

somewhere to go. So, inside of feature

48:43

specs folder, create a new file called

48:46

02 editor.md.

48:49

And within it, we're going to follow a

48:50

similar structure we followed before. We

48:53

want to start with explaining what we

48:55

need,

48:56

such as the base chrome components that

48:58

frame every editor screen, the top

49:00

navbar and the left sidebar shell. These

49:04

will be reused and extended in every

49:06

chapter that follows. Then, we need to

49:08

focus on what are we actually creating?

49:11

That's going to be the editor navbar.

49:14

So, we wanted to create a new component

49:16

within the editor editor navbar file

49:19

and then we want to specify some

49:20

requirements that follow. We want this

49:23

navbar to be of a fixed height at the

49:25

top. We want it to have both left,

49:28

center, and right sections, which you

49:30

can see right here. On the left, we open

49:32

up the sidebar. In the middle, we have

49:34

the name and on the right side, we have

49:36

additional actions. That's exactly what

49:38

we explained right here.

49:41

And of course, it's going to be of a

49:42

dark background with a subtle bottom

49:44

border. Next, we want to explain what we

49:47

want to do with the project sidebar. So,

49:49

right below, we can say create a project

49:52

sidebar component.

49:54

It should float above the editor canvas.

49:57

Opening it should not push the page

49:59

content. So, you can see it remains

50:01

where it is.

50:03

And slides in from the left. It has the

50:05

header that says projects and title and

50:08

a close button right here. It can use

50:10

the Shazian tabs component. And both

50:13

tabs show empty placeholder state, full

50:16

width new project button at the bottom

50:18

with the plus icon. So, something like

50:20

this. Oh, and finally, when we click

50:23

create new project, we need this kind of

50:25

a dialogue. A new pop-up that shows.

50:29

So, for that, we can say something along

50:31

the lines of

50:33

dialogue pattern. Use the existing color

50:36

tokens from globals for dialogue

50:37

styling. It supports title, description,

50:40

and footer action, but don't build any

50:42

dialogues yet.

50:44

We just want to create the component for

50:46

it. Finally, to check whether it is

50:48

done, we can check whether new

50:50

components compile without TypeScript

50:52

errors, no lint errors, and diagram

50:54

pattern is ready for future use. So,

50:57

hopefully, you were typing this out with

50:59

me, or maybe you paused the screen and

51:01

typed it with your own words. But, this

51:03

course isn't at all about typing. It is

51:06

about understanding what we're doing

51:08

right here. So, if you don't really feel

51:10

like typing, nor you should, in the

51:12

video kit link down in the description,

51:14

I'll provide you with all the prompts

51:17

that we're going to use throughout the

51:18

rest of this course. So, if at any point

51:20

I'm going too fast when explaining them,

51:23

and you just want to have it on your end

51:25

and listen along, you can totally do

51:27

that. But, yeah, let me quickly walk you

51:29

through the structure because this is

51:31

the template that every spec file in

51:33

this project will follow. We start with

51:35

a goal. One or two sentences, what does

51:38

this unit produce when it is done? Then,

51:41

we go over some specific design

51:42

decisions. Is it visual or structural or

51:46

something specific to the component like

51:48

layout behavior, responsiveness, and

51:51

this is where we can refer to that UI

51:53

context file so that the agent isn't

51:55

guessing the colors.

51:56

Then we go over into implementation, and

51:59

in this case, I decided to separate it

52:01

into sections. So, we have the editor

52:03

navbar, the project sidebar, and the

52:06

dialog pattern, and finally the

52:08

verification checklist. So, let's open

52:10

up our agent. Whenever you're building a

52:12

new feature, always open up a new chat

52:15

and don't use one of the older chats.

52:17

That's because we don't want some stale

52:19

context lingering around. Only remain

52:21

within the same chat when what you're

52:23

about to do next is related to what

52:25

you've done before, such as when you

52:27

want to fix specific issues. You can see

52:29

that it already has access to the editor

52:31

file.

52:33

So, I'll say, "Read this file and update

52:37

the tasks on the progress

52:40

tracker and then

52:43

implement it

52:45

exactly as specified."

52:48

And press enter. And in about a minute,

52:51

it's all done.

52:52

It developed the editor navbar, the

52:54

sidebar, and also the dialog pattern.

52:58

TypeScript and ESLint both pass clean.

53:00

Progress tracker also got updated. So,

53:03

let's check the progress tracker first.

53:05

It'll be right here within the progress

53:07

tracker.

53:08

So, if you check feature two completed,

53:12

editor navbar and project sidebar both

53:15

implemented. You can see them right here

53:17

under editor, editor navbar, and project

53:20

sidebar.

53:21

We just created them so far, but they're

53:23

not yet being used within the layout.

53:25

And even though this wasn't part of the

53:27

checks,

53:28

I want to actually be able to see them.

53:30

I want to tell it to use the navbar and

53:33

the sidebar right now within the

53:34

project. So, I'll open up the chat in

53:37

the same context window and tell it to

53:39

use these two components within a

53:41

layout. And within a minute it put it to

53:44

use. It even created a placeholder page

53:46

so we can navigate over to the editor

53:49

and check it out. So, head over to

53:50

localhost:3000/editor,

53:53

and you can see a canvas coming soon,

53:55

but there is a top bar and a left

53:58

sidebar, which opens up the projects.

54:01

So, you can see that what we requested

54:03

indeed got implemented. Since we're not

54:05

yet at the point where we need that

54:06

because we don't even yet have the

54:08

editor, I actually want to show you how

54:10

you can undo the changes, at least right

54:12

here in Claude code. The only thing you

54:14

have to do is press this back arrow on

54:16

the message that you used to create

54:18

these components, and then say rewind

54:20

code to here. Which is going to bring us

54:22

back and only give us what the

54:24

specification wanted, and that is the

54:27

components that we can then use and that

54:29

compile, but we're not using them quite

54:32

yet. Perfect. So, now that we have the

54:33

navbar and a togglable sidebar, let's

54:36

quickly check them out. The editor

54:38

navbar is pretty straightforward, and

54:40

the sidebar accepts some props such as

54:42

is open and on close, and uses the

54:44

Shadsy and tabs to modify what's being

54:47

shown. But, when you're writing code

54:48

yourself, you have a natural

54:50

understanding of every decision you

54:52

made. Heck, you wrote it.

54:54

You know why a function is structured in

54:57

a specific way or why you used specific

54:59

props.

55:01

But, AI-generated code doesn't come with

55:04

that context.

55:05

The agent made decisions that that were

55:08

reasonable most of the time, but you

55:11

didn't make them. And that means that

55:13

reviewing AI output isn't optional. It's

55:16

the step that keeps you in control of

55:17

your own codebase. So, that's the

55:19

perfect use case to test out the Code

55:21

Rabbit edition that we added in the last

55:23

lesson. I'll open up the chat and tell

55:25

it to push all the current changes to a

55:29

new branch called development.

55:31

Typically, in real production databases,

55:33

you often have multiple branches such as

55:36

dev, staging, and only then main.

55:40

So, for now, we want to push this over

55:41

to the development branch, get it

55:43

reviewed, and only if it's good, merge

55:45

it over to main. So, by giving it this

55:47

quick message, it'll run a couple of Git

55:50

commands,

55:51

figure out that we're currently on main,

55:53

that we need to switch over and push to

55:55

that new branch. And that's a little pro

55:57

tip. Uh-huh, I mean, sure you could run

56:00

these commands through the terminal, but

56:02

I find it super easy to just stay in

56:04

flow and tell the agent to push the code

56:07

for me, which you can see it just did.

56:09

So, if you head back over to your repo,

56:11

you'll see that the development branch

56:13

had recent pushes 3 seconds ago.

56:15

So, let's go ahead and compare them and

56:17

open up the pull request. We have 333

56:20

new lines of code across five different

56:23

files. So, Code Rabbit immediately

56:25

hooked itself onto the PR, and we'll see

56:28

whether it'll be able to pull out some

56:29

bugs out of the hat.

56:31

Let's give it a minute, and then I'll be

56:33

right back. And we got back the

56:35

walk-through, where it says exactly what

56:38

we introduced in this project, such as

56:40

two new React components for an editor

56:42

UI Chrome, an editor navbar, and a

56:45

project sidebar. These are super simple,

56:47

as this was a simple review. Later on,

56:50

these are going to get much more

56:51

detailed, but yeah, let's check whether

56:54

we have some potential issues even on a

56:56

simple PR, such as this one. There's one

56:58

major issue that says, "Hide the

57:01

off-screen sidebar from focus order and

57:03

assistive tech when closed." So,

57:05

specifically, this is an accessibility

57:07

fix, which is definitely a good

57:09

implementation.

57:10

Since we haven't yet utilized this

57:12

component in our app, I'll leave this so

57:13

we can add it later. There's also a

57:15

minor issue, where the spec lists only

57:18

the is open for the sidebar, but the

57:21

implementation also requires on close.

57:23

So, actually, it's suggesting to change

57:25

the specification. This is interesting,

57:27

because sometimes you're going to miss

57:29

some stuff from the spec, and it's okay

57:31

if AI tries to fix it or add some stuff,

57:35

but it's equally as important for Code

57:37

Rabbit to flag it. Because the whole

57:39

reason why we're writing specs in the

57:41

first place is so we can have predictive

57:43

output. So basically, you can just copy

57:45

this part right here, go back to your

57:47

code base where we're saying accepts

57:50

and then it's going to be is open prop.

57:52

But we're going to say accepts both is

57:55

open and on close props. Little change,

57:59

I know, but now our code base is

58:01

consistent with the spec. And that's it

58:03

for this simple PR. As we continue

58:05

developing more components, the reviews

58:07

are going to get significantly more

58:09

detailed. So for now, I'm going to go

58:11

ahead and merge it, which means that we

58:13

are ready to continue developing the

58:15

next component.

58:18

For this type of application, we don't

58:21

really need a traditional homepage.

58:23

Most tools like this drop you straight

58:26

into the editor.

58:27

You sign in from a simple sign in page

58:30

and you manage everything from the

58:32

canvas. And that's exactly what we'll do

58:34

within our app.

58:36

But we need auth, sign in, sign up, and

58:39

other similar pages where you explain

58:41

how your application works

58:43

and then allow users to sign back in. So

58:46

to get that set up, click the Clerk link

58:48

down in the description and sign in. You

58:50

can sign in with Google or GitHub. And

58:53

once you're in, you can head over to

58:54

applications and create a new app on the

58:57

dashboard. You can give it a name such

58:59

as Ghost AI and choose the sign in

59:02

options

59:03

such as email, Google, and since this is

59:06

a development website, we can also do

59:08

GitHub. Then click create application.

59:11

We're building on Next.js, so what you

59:13

can do is just install @clerk/nextjs

59:17

by copying this command and paste it

59:20

straight into the terminal.

59:22

And while that is being installed, you

59:24

can also set up your Clerk API keys by

59:27

copying them from here

59:29

and creating a new file called

59:31

.env.local

59:34

and then paste them right here.

59:35

Now, before we hand anything over to the

59:38

agent, there are two things you need to

59:40

know about Clerk and Next.js 16

59:42

specifically, because if you skip this,

59:45

the agent will get it wrong. And it says

59:47

it right here. If you're using Next.js

59:49

15 or lower, name your file

59:52

middleware.ts instead of proxy.ts.

59:56

The code itself remains the same, only

59:58

the file name changes. But, because most

1:00:00

agents were trained on Next.js 14 and 15

1:00:03

code bases, it'll almost certainly

1:00:06

create a middleware.ts file by default.

1:00:09

So, we'll have to specify proxy.ts

1:00:11

explicitly in our spec so it doesn't

1:00:13

have to guess. Oh, and another important

1:00:15

thing is that just by adding middleware,

1:00:18

it doesn't automatically protect all

1:00:20

routes. As you can see right here, by

1:00:22

default, it leaves all the routes

1:00:24

public. And this catches a lot of

1:00:26

developers. You have to explicitly

1:00:28

define which routes are protected and

1:00:31

which are public, which means that we

1:00:32

have to configure everything

1:00:34

intentionally. And this is exactly why

1:00:36

reading the updated documentation before

1:00:38

building with AI matters. The agent

1:00:41

knows Clerk, not necessarily the version

1:00:43

of Clerk you just installed or the

1:00:45

version of Next.js you're running on.

1:00:46

The spec bridges that gap. Oh, but we

1:00:49

can also use agent skills.

1:00:52

As I told you at the start, most major

1:00:54

frameworks and libraries now publish

1:00:56

official skill packages specifically for

1:00:58

this problem. It gives your agent

1:01:00

up-to-date knowledge, current APIs, and

1:01:03

patterns, and the best practices of the

1:01:05

library you're using. So, if you search

1:01:07

on Google or within the docs, Clerk

1:01:09

agent skills, you'll be redirected to

1:01:11

this page. Then, simply copy the

1:01:14

installation command,

1:01:15

head back over to your code base and

1:01:17

paste it in your terminal. npx skills

1:01:20

add clerk skills. Press enter.

1:01:24

You might need to install the skills

1:01:25

package by saying Y and then enter. And

1:01:28

then, by pressing the arrow up and down

1:01:30

keys and the space key, you can select

1:01:33

specific packages, such as core clerk.

1:01:36

You can either select some additional

1:01:38

clerk features or some additional clerk

1:01:40

frameworks. Like in this case, I'm going

1:01:42

to go with clerk next js patterns. And

1:01:46

press enter.

1:01:48

Then, you can select additional agents

1:01:50

that you want to add it to. By default,

1:01:52

it's going to work on codex, cursor, and

1:01:54

anti-gravity. But, if you want to add

1:01:56

Claude code, you have to select it here

1:01:58

and press enter.

1:02:00

And we can install it in project scope

1:02:02

via symlink. That's the recommended way.

1:02:05

So, just proceed with installation.

1:02:07

Perfect. Our skill is installed and

1:02:09

we'll be able to invoke it later on once

1:02:12

we focus on implementing the auth

1:02:13

functionality.

1:02:15

So, now we are ready to write the spec.

1:02:19

Open up your context feature specs and

1:02:21

create a new file called 03 auth.md.

1:02:25

And once again, the full feature specs

1:02:28

files are linked in the description in

1:02:30

case you want to just copy them and then

1:02:31

follow along. Or, you can slowly type it

1:02:34

out with me.

1:02:35

Let's start by telling it that clerk is

1:02:38

already installed and connected. So, we

1:02:40

just need to wire it into the next js

1:02:42

app, provider, auth pages, redirects,

1:02:46

route protections, and the user menu.

1:02:49

When it comes to the design, we just

1:02:52

want to use the clerk's dark theme from

1:02:54

the @clerk/ui themes as the base, and

1:02:57

then we want to override the clerk

1:03:00

appearance variables using the app's

1:03:02

existing CSS variables.

1:03:05

With no hardcoded colors. For the sign

1:03:08

up and sign in pages, this is what we

1:03:11

want to develop. On large screens, we

1:03:14

just want to have a simple two-panel

1:03:16

layout. On the left side, a logo, a

1:03:19

tagline, and text-only features. On the

1:03:22

right side, a centered Clerk form, and

1:03:25

on small screens, forms only.

1:03:27

We don't want to have any kind of

1:03:29

gradients, as that's going to seem

1:03:30

AI-ish. No oversized hero sections,

1:03:33

cards, or scroll-heavy layouts. Keep the

1:03:36

layout minimal and professional.

1:03:38

And then we can dive into a bit more

1:03:40

details on the full implementation.

1:03:43

So, we can say

1:03:46

wrap the root layout with a Clerk

1:03:48

provider using the Clerk's dark theme.

1:03:52

Create sign-in and sign-up pages using

1:03:54

Clerk components. And as I told you

1:03:56

before, we have to be specific in

1:03:58

telling it that it should use the

1:04:00

proxy.ts file name at the project root

1:04:03

instead of the middleware.ts.

1:04:06

And then, we have to define public

1:04:07

routes using the existing sign-in and

1:04:10

sign-up environment variables. Protect

1:04:12

everything else by default. Which means

1:04:15

that we have to update our home page so

1:04:18

that when authenticated users visit it,

1:04:21

we redirect them to the editor. Or when

1:04:24

unauthenticated users visit it, we

1:04:26

redirect them to the sign-in page.

1:04:29

We also want to implement Clerk's

1:04:32

built-in user button to the editor

1:04:35

navbar right section for profile

1:04:37

settings and log out. We want to keep

1:04:39

Clerk's default user menu and profile

1:04:41

flows intact and not rebuild heavily

1:04:44

custom- ized Clerk internals, and want

1:04:46

to use existing Clerk environment

1:04:48

variables without renaming or inventing

1:04:51

new ones. Finally, we can specify

1:04:54

additional dependencies, such as Clerk

1:04:56

UI, if we need to install it. And then,

1:04:59

when it's done, we want to check that

1:05:00

proxy file is there, that all routes are

1:05:03

protected except public auth routes,

1:05:06

auth pages use CSS variables with no

1:05:09

hardcoded colors, Clerk provider wraps

1:05:12

the layout and the build passes. This is

1:05:15

our complete authentication

1:05:17

implementation. Now, you know the drill.

1:05:20

Go ahead and open up your agent,

1:05:22

give it access to this file, and tell it

1:05:25

to read this file and update the

1:05:28

progress tracker.md file accordingly.

1:05:31

Then, implement the auth feature exactly

1:05:34

as specified in the auth.md file. Okay?

1:05:38

You can see that the built-in microphone

1:05:40

feature right here still isn't perfect.

1:05:42

Let me try to do the same thing with

1:05:43

Whisper flow specified in auth.md file.

1:05:50

There we go. That's a bit better, and

1:05:52

specifically it's 03.auth.md.

1:05:55

Perfect. Let's go ahead and run it.

1:05:58

First, it's asking me to run some bash

1:06:00

commands to check whether Clerk has been

1:06:02

properly installed, and we'll say,

1:06:04

"Yeah, go ahead, you can run it." It

1:06:06

took him about 2 minutes or so to go

1:06:09

through all the context we shared, and

1:06:11

only then it'll start to implement

1:06:13

everything in parallel.

1:06:15

And this is much better than if it

1:06:17

started right away and then just ended

1:06:20

up with a bunch of mistakes. So, the

1:06:21

list of updates that it put right here

1:06:23

is to update the .env.local with Clerk

1:06:26

sign-in and sign-up URL vars, create a

1:06:29

proxy with Clerk middleware route

1:06:31

protection, update the app layout with a

1:06:33

Clerk provider and dark theme, create

1:06:36

sign-in and sign-up pages with a

1:06:38

two-panel layout,

1:06:39

update the app page to redirect based on

1:06:42

the auth state, add the user button to

1:06:44

editor navbar,

1:06:46

and create editor page, and then update

1:06:48

the progress tracker, and run npm run

1:06:50

build to verify. It's going to ask me

1:06:53

whether it has the ability to create

1:06:55

some directories, and I'll tell it,

1:06:57

"Yeah, go ahead."

1:06:58

And in the future we can give it some

1:07:00

more permissions so it can do things a

1:07:02

bit more freely. And after it came up

1:07:04

with the initial plan, it actually built

1:07:07

out everything pretty quickly. Maybe

1:07:09

even in less time than it used to think

1:07:12

how to approach it in the first place,

1:07:13

which shows you just how important the

1:07:16

initial context and a proper task are.

1:07:19

And there we go. The build passes, and

1:07:21

here's the summary of everything that

1:07:23

was implemented. I think all of these

1:07:25

things right here shouldn't come as a

1:07:27

surprise because we initially specified

1:07:30

them in the spec. Then it updated the

1:07:33

to-do's, and now it just did it. And

1:07:36

yeah, it's pretty interesting that it

1:07:37

even notes right here that it would have

1:07:39

gotten confused about this middleware

1:07:41

proxy thing if we didn't specify it

1:07:43

properly. But thankfully, it did it in

1:07:46

the right way thanks to the research

1:07:47

that we've done at the start.

1:07:49

So, what do you say that we take it for

1:07:51

a spin? The application is running on

1:07:53

localhost 3000, but whenever I make some

1:07:55

bigger changes, I like to rerun it. But

1:07:58

initially, if you head over to

1:07:59

localhost, you might get redirected to

1:08:02

this clerk handshake part, which is

1:08:04

going to lead to a broken page. But

1:08:06

after you reload, it should properly

1:08:09

redirect you back to the homepage. And

1:08:11

after that, it should just work.

1:08:14

This is something that we can polish and

1:08:16

fix up later on.

1:08:18

But yeah, this is looking interesting.

1:08:20

Definitely not quite as nice as the

1:08:23

original application that I've showed

1:08:25

you that's deployed.

1:08:26

So, what are some of the things that we

1:08:28

can do to make it look more similar to

1:08:31

that one? Well, step one is to search

1:08:34

some kind of design websites online like

1:08:36

Dribbble or award-winning websites, and

1:08:39

then just take a screenshot and try to

1:08:41

get it to match to that closer. Or in

1:08:43

this case, you can head over to the

1:08:45

deployed version of this application,

1:08:48

take a screenshot of this whole UI that

1:08:51

you can see right here,

1:08:52

and then we can feed it over into our

1:08:55

chat. So, go ahead and open up the chat.

1:08:57

Keep in mind that we can still stay

1:08:59

within the authentication window that we

1:09:01

worked on

1:09:02

because now we're fixing some parts

1:09:04

about that specific implementation. So,

1:09:06

what you can do is just click this plus

1:09:08

right here and upload from computer or

1:09:10

just drag and drop the screenshot. Then,

1:09:13

select it and then we can further point

1:09:15

out to some things that could be

1:09:17

improved, not just the layout. It seems

1:09:19

like it didn't properly read the fonts,

1:09:22

that the right side of the screen is

1:09:24

taking a larger portion, so maybe we can

1:09:26

split them 50/50, same as it is right

1:09:28

here, and make the font size a bit

1:09:31

larger. So, it's not just the layout. We

1:09:33

needed to review the screenshot and

1:09:35

update the UI of our current application

1:09:38

to look more like the one on the

1:09:39

screenshot. Means 50/50 left and right

1:09:43

side layout with some kind of a color on

1:09:45

the left side to differentiate it from a

1:09:47

dark background,

1:09:49

as well as we need to fix the fonts so

1:09:51

that it uses the ones outlined in our UI

1:09:55

guidelines. I think for now this is

1:09:56

going to be enough for it to get closer

1:09:59

to the design we want to get. So, press

1:10:01

enter and let's see how it handles it.

1:10:03

And there we go, the build passes, it

1:10:06

updated the globals from circular font

1:10:08

reference to the one that should be

1:10:10

correct right now. The body was pointing

1:10:12

at itself, so this font was never

1:10:15

actually applied to anything. Same fix

1:10:17

for the heading and it also implemented

1:10:19

some other fixes. Let's check it out.

1:10:21

The design now looks much closer to the

1:10:23

finished product. The fonts are being

1:10:26

properly applied and that makes a big

1:10:28

difference. So does the increase in font

1:10:30

size and this shift between the two

1:10:33

different background colors. Of course,

1:10:35

later on we can come up with a unique

1:10:37

logo that we can put right here, but for

1:10:39

now this is looking great.

1:10:41

And believe it or not, we have a fully

1:10:44

functional authentication system built

1:10:46

in right here. So, what do you say that

1:10:48

we go ahead and test it out? You can

1:10:50

head over to sign up to see whether that

1:10:52

works and it does. Later on, if you want

1:10:55

to, you can modify the contents on the

1:10:57

left side depending on whether you're in

1:10:59

sign-in or sign-up page. And then let's

1:11:01

use something like GitHub to sign in.

1:11:04

I'll authorize it and the redirect

1:11:06

redirects us to a page that currently

1:11:09

breaks. So, I'll try to head back over

1:11:12

to localhost:3000 one more time. And now

1:11:15

when we get back, it actually redirects

1:11:17

to the editor properly. You can see that

1:11:20

we have the sidebar, we have a space for

1:11:23

the canvas that's about to come in the

1:11:25

future. And then on top right, we see

1:11:27

all the information about our currently

1:11:29

logged-in account, which is beautiful. I

1:11:31

mean, we get complete user management

1:11:34

within a single prompt that we've done.

1:11:36

Let's also try to sign out for now, and

1:11:38

we do get one issue when we click that

1:11:40

button saying there's an unexpected

1:11:43

response received from the server. So,

1:11:45

if we head back over here and open up

1:11:47

the terminal, we can see an unhandled

1:11:49

rejection. Unexpected response was

1:11:51

received from the server. And then in

1:11:53

the terminal, we see something like

1:11:55

this, which doesn't really tell us much.

1:11:58

Now, what we could do is just copy this,

1:12:00

open up the chat window one more time by

1:12:03

heading over here, and then just pasting

1:12:06

this error and telling it to fix it.

1:12:08

But, I'm actually glad that this error

1:12:10

happened because I can teach you a bit

1:12:12

better and more precise way to handle

1:12:14

these issues and so that when you try to

1:12:16

fix them, you don't cause new ones.

1:12:19

So, instead of pasting this right into

1:12:21

the chat, we're actually going to open

1:12:22

up a new file right here within context

1:12:26

and call it current issues.md.

1:12:31

You can even do it within feature specs.

1:12:33

Either way works. Then, you can paste

1:12:35

any kind of errors that you have and

1:12:37

explain what's happening. So, I'll

1:12:39

explain that when I click the logout

1:12:43

button,

1:12:46

the following error appears. And now I

1:12:49

can paste this error message. And we can

1:12:51

also specify that sometimes when we log

1:12:54

in, we get redirected to this weird long

1:12:57

URL, which doesn't show anything on the

1:12:59

page.

1:13:00

And then we can just paste this URL

1:13:03

right here. Again, the errors on your

1:13:05

end might be a bit different from what

1:13:08

I'm seeing right here. Whatever they

1:13:10

are, I don't want to teach you how to

1:13:12

copy and paste. I want to teach you how

1:13:14

to solve the problems for yourself.

1:13:16

Explore the current issues file and

1:13:19

deeply analyze the problem.

1:13:22

Only when you have the analysis, give it

1:13:24

back to me with the idea of how you're

1:13:26

planning to solve it, and then wait for

1:13:28

me to give it the green light to execute

1:13:31

it. So yeah, writing something like this

1:13:33

makes sense because that way it doesn't

1:13:35

go into the spiral of trying to fix its

1:13:37

own bugs while breaking 10 other things.

1:13:39

You provided the error, it's going to

1:13:41

come back with the analysis. You're the

1:13:43

one deciding whether that analysis makes

1:13:45

sense and whether it can actually

1:13:47

execute it. So, let's run it and see

1:13:50

what it comes back with. And after some

1:13:52

thinking, it's back with the analysis.

1:13:55

And this is so much better and so much

1:13:58

more detailed than if we just told it to

1:14:00

fix it immediately. Now, it actually

1:14:03

tried it out and has a deep idea of

1:14:05

what's happening. The server log is the

1:14:07

key clue. Proxy took 373 milliseconds

1:14:11

out of a half a second total request.

1:14:13

Almost all time is spent in the proxy.

1:14:16

So, that already points it in the right

1:14:18

direction.

1:14:19

As a human person, I would never figure

1:14:22

this out on my own. At least not from

1:14:24

such a vague error message. But yeah,

1:14:27

here it figures out that this button

1:14:29

needs an after sign out URL. So, it's

1:14:32

just going to add it.

1:14:33

And a similar thing is happening with

1:14:35

the handshake URL. It basically needs to

1:14:38

configure that after sign up URL, and

1:14:41

that way it's going to route it

1:14:42

properly. So, it provided a two fix

1:14:44

plan. So, this looks plausible to me,

1:14:47

looks good. So, let's just tell it to

1:14:50

execute the plan and fix the issues.

1:14:52

There we go. So, the fix now seems very

1:14:55

apparent. We just needed this after sign

1:14:57

out URL. And if you want to learn more

1:14:59

tips and tricks just like this one about

1:15:02

actually analyzing and fixing the errors

1:15:04

and how I approach building these

1:15:06

production level applications,

1:15:08

definitely check out the spec driven

1:15:10

agentic development course. It's not out

1:15:12

yet as it's going to be super detailed

1:15:14

and it's going to follow the best

1:15:15

practices from the strongest developer

1:15:17

teams out there. But yeah, it'll be out

1:15:19

soon, but I'm still super glad that

1:15:21

while I'm developing that, you can still

1:15:24

learn how I develop these applications

1:15:26

with the agentic ways with this new

1:15:28

video that you're watching right now.

1:15:30

And you can let me know down in the

1:15:32

comments how you like this new type of

1:15:34

video. I get that it's completely

1:15:36

different from manual coding, but I

1:15:38

still think there's so much to learn and

1:15:41

we can have a predictable development

1:15:44

workflow even with AI doing the writing

1:15:47

for us. So, let's see whether this

1:15:49

actually fixes it. And the build passes.

1:15:51

The after sign out URL prop was removed

1:15:54

from the user button as it belongs in

1:15:56

clerk options and is set via environment

1:15:58

variables. So, we'll soon be able to

1:16:00

verify whether that actually fixes it.

1:16:03

But before, I want to open up my

1:16:05

terminal,

1:16:06

stop it from running and then rerun it

1:16:08

again on localhost 3000 because we

1:16:10

changed the ENVs, so we want to make

1:16:12

sure that they're read by the browser.

1:16:14

So now, heading back over to localhost

1:16:16

3000, maybe you're signed in already,

1:16:18

which is fine. You can simply sign out

1:16:20

now and that brings us to another error,

1:16:23

which is the same one we've had before.

1:16:25

And another thing you can do is head

1:16:27

over to inspect element, switch over to

1:16:29

the application tab, and then clear all

1:16:32

the cookies. So, find the cookies for

1:16:34

localhost 3000, clear them, and then

1:16:36

reload the page.

1:16:38

You'll be redirected back to the home

1:16:40

page and now we can retry with a clean

1:16:42

slate.

1:16:43

So, I'll sign in using GitHub the same

1:16:46

way I did before. That works. I

1:16:48

automatically get redirected over to the

1:16:50

editor, which is great. And now, I'll

1:16:52

sign out, and that worked. So, now head

1:16:55

back over within your terminal,

1:16:57

run git add dot, git commit {dash} m,

1:17:01

implement auth, and then git push. This

1:17:04

is going to push all the changes to

1:17:06

origin development, allowing us to open

1:17:08

up a pull request over to the main

1:17:10

branch. Then, you can open up a PR, and

1:17:14

let's wait for CodeRabbit to review it.

1:17:16

This time, we had many changes, but not

1:17:19

many of them are directly related to

1:17:21

what we did in the code. We just added

1:17:23

these agent skills so that everybody

1:17:25

else's agents working on this code base

1:17:28

also, well, become smart in the

1:17:30

technologies that we're using for the

1:17:32

project. And then, yeah, of course, we

1:17:34

implemented a couple of different files

1:17:36

that CodeRabbit will verify. But,

1:17:38

primarily, what we've done is right here

1:17:41

within app, sign in and sign up pages,

1:17:45

we have added some features that would

1:17:46

display on the left side. And then, the

1:17:48

sign-in page, where on the right side,

1:17:51

we just render the sign-in component

1:17:54

coming from Clerk.

1:17:56

Similar thing happens over to the

1:17:58

sign-up page. So, right here, we're just

1:18:01

rendering the sign-up UI.

1:18:03

Then,

1:18:04

over in the layout, we are wrapping

1:18:07

everything with a Clerk provider,

1:18:09

setting the theme to dark, and setting

1:18:11

some custom variables. That's it.

1:18:14

Everything else remains the same. And

1:18:15

then, within the editor page, we are

1:18:18

simply showing the nav bar and the

1:18:20

sidebar, as well as the rest of the

1:18:22

content.

1:18:23

In the nav bar, we display the Clerk

1:18:26

user button, allowing us to see more

1:18:27

info about the user, and allowing us to

1:18:30

log us out. Pretty straightforward so

1:18:32

far. As our components and features get

1:18:34

more detailed, we're going to do deeper

1:18:36

dives into the code base, but so far so

1:18:39

good. Let's wait for the review. And

1:18:41

quickly we're back with a full

1:18:43

walk-through. This pull request

1:18:45

integrates Clerk auth into a Next.js

1:18:47

application and establishes

1:18:49

comprehensive agent skills for Clerk

1:18:52

integration across multiple frameworks.

1:18:54

It also adds auth middleware, sign-in

1:18:57

and sign-up pages, Clerk provider setup,

1:18:59

and introduces five new skill

1:19:01

definitions for supporting scripts,

1:19:03

documentation, and so on. So, obviously

1:19:06

a lot of the checks right here from

1:19:08

CodeRabbit are going to be about the

1:19:09

skills that we set up, but we can skip

1:19:12

those for now and focus on the ones

1:19:14

about the actual files we implemented.

1:19:17

In this case, it looks like there's one

1:19:19

critical issue, and that is within the

1:19:21

context current issues.md.

1:19:24

Well, you never want to publish current

1:19:26

issues to GitHub anyways, because you

1:19:28

want people to see your code and not

1:19:30

your mistakes. Um what we're doing here

1:19:33

is even worse. We're exposing the

1:19:36

handshake or the JWT token from the

1:19:38

track docs. So, this is a real security

1:19:42

issue. So, what we need to do is delete

1:19:44

this file. Obviously, this won't delete

1:19:46

it from the Git history, but that's fine

1:19:49

for us because this JWT is no longer in

1:19:51

use.

1:19:53

So, right here over to getignore, I'm

1:19:54

going to add our {forward-slash} context

1:19:58

{forward-slash}, and that's going to be

1:20:00

the current issues

1:20:03

.md file.

1:20:05

And I'll also remove everything that is

1:20:07

within it.

1:20:09

Not that it matters right now, because

1:20:10

we're adding it to getignore anyway. So,

1:20:13

let's go ahead and push those changes by

1:20:14

saying get add {dot} get commit update

1:20:18

{dot} getignore.

1:20:20

And get push. Immediately the changes

1:20:23

will be recognized, which means that we

1:20:24

can merge this over to the main branch.

1:20:27

And while we're here, we can also head

1:20:29

over into context on the main branch and

1:20:32

remove the current issues file right

1:20:34

here from GitHub by simply deleting the

1:20:36

file.

1:20:38

And committing the changes.

1:20:40

We can also do the same thing on the

1:20:41

development branch by heading over to

1:20:43

context

1:20:45

and heading over to current issues, and

1:20:48

just removing the file

1:20:50

so that it's no longer here, and it's

1:20:51

not going to be pushed to GitHub any

1:20:53

longer because we added it to get

1:20:55

ignore.

1:20:56

Perfect. With that in mind, we've

1:20:58

successfully implemented the full UI and

1:21:01

functionality for the authentication

1:21:03

within our application.

1:21:07

Now that our authentication is done, and

1:21:09

we can actually sign into our

1:21:11

application, let's make the sidebar

1:21:14

actually do something. As right now, it

1:21:16

is just static. Before we hook up any

1:21:19

real data, we need the UI in place.

1:21:22

The create, rename, and delete

1:21:25

dialogues, plus the editor home state.

1:21:27

So, we're keeping this prompt focused on

1:21:29

UI only with no API calls yet. Head over

1:21:33

into context, feature specs, and add a

1:21:36

new file called 04 project dialogues.

1:21:40

Within it, we can specify that the goal

1:21:43

is to build the editor home screen and

1:21:45

add the project dialogues and sidebar

1:21:47

actions with no API calls yet. So, what

1:21:51

does this specifically mean?

1:21:52

Well, it means that on the home page, we

1:21:54

want to reuse the existing editor layout

1:21:58

without modifying the navbar or sidebar

1:22:00

behavior, but in the center of the page,

1:22:02

we want to add a heading, create a

1:22:04

project, or open up an existing one, and

1:22:07

a description. Start a new architecture

1:22:09

workspace or choose a project from the

1:22:11

sidebar, and a new project button with a

1:22:14

plus icon. Keeping the layout minimal

1:22:16

without wrapping this content in cards.

1:22:19

And then clicking new project should

1:22:21

open up the create project dialogue.

1:22:23

Let me actually show you what I mean by

1:22:26

all of this by heading over to the

1:22:27

finished version of the application, and

1:22:30

quickly signing in. Notice how right

1:22:32

here in the middle we have some text to

1:22:34

greeting us, even though the canvas is

1:22:36

empty, allowing us to create a new

1:22:38

project, which then opens up this

1:22:39

dialogue. That's exactly what we want to

1:22:42

achieve.

1:22:43

So, we'll have a dialogue for creating a

1:22:45

new project, as well as for editing one,

1:22:48

and deleting it. So, let's specify these

1:22:51

three dialogues below.

1:22:53

We're going to have one for creating a

1:22:54

project that takes in the project name

1:22:57

input, the live slug preview based on

1:23:00

the name, and preview updates as the

1:23:02

user types. Then, the one for the rename

1:23:05

with pre-filled project name input, and

1:23:08

the one for delete. Finally, on the

1:23:10

sidebar, we want to add the following

1:23:13

project item actions, rename and delete,

1:23:16

show actions only for the projects that

1:23:18

we own, and hide actions for the shared

1:23:21

or projects belonging to collaborators,

1:23:23

and on mobile, tapping outside the

1:23:25

sidebar closes it, and want to add some

1:23:27

kind of a backdrop scrim. Finally, let's

1:23:29

specify the implementation right here by

1:23:32

telling it to create a dedicated hook to

1:23:35

manage the dialogue state, the form

1:23:37

state, and the loading state, that way

1:23:39

we can reuse the functionality.

1:23:41

And then we want to wire the editor home

1:23:44

new project to create dialogue, the

1:23:47

sidebar create button to create dialogue

1:23:49

as well, sidebar rename to rename

1:23:52

dialogue, and sidebar delete to delete

1:23:54

dialogue. For now, we only want to use

1:23:56

mock project data with no API calls.

1:24:00

And to check when it is done, we need to

1:24:03

check that sidebar actions are wired,

1:24:05

that slug preview works, that no

1:24:07

TypeScript errors exist, and there's

1:24:09

also no linting errors.

1:24:11

So, let's open up Claude Code or your AI

1:24:14

agent of choice,

1:24:16

and then we can tell it to read this

1:24:19

file, update the progress-tracker.md

1:24:22

to mark this as in progress and then

1:24:24

implement it exactly as specified. And

1:24:28

let's run it.

1:24:29

And in about a minute or so, feature

1:24:31

four is done.

1:24:33

Here's what's built. New file hooks use

1:24:36

project dialogues, which centralizes the

1:24:38

dialogue type, form, including the name

1:24:41

and the slug, the loading state and mock

1:24:44

data, which exposes these different

1:24:46

functions to deal with the dialogues.

1:24:48

Then there's the dialogue component, as

1:24:50

well as within the project sidebar, we

1:24:53

also call the dialogues when needed.

1:24:56

So, let's quickly check this hooks file

1:24:58

that it implemented. You can see that it

1:25:00

is strictly typed right here with the

1:25:02

project interface, the dialogue type, to

1:25:05

slug, which takes in the name and turns

1:25:07

it into a human readable ID,

1:25:09

and it even created some mock three

1:25:11

projects that we can verify.

1:25:13

It came up with a lot of different use

1:25:15

states, keeping track of all the

1:25:17

projects, the dialogue type, the

1:25:20

selected project, the name, slug, and

1:25:22

loading.

1:25:23

And all of these are going to be used

1:25:25

within the dialogues themselves. Then it

1:25:27

returns the data, so we can actually use

1:25:29

them. Let's check where the dialogue is

1:25:31

being used. Most often, it is right here

1:25:33

within project dialogues,

1:25:35

where we have the actual code for how

1:25:37

the dialogue looks like. If the dialogue

1:25:40

is of a type create, then we show this

1:25:42

one. If it's of a type rename, we show

1:25:44

this one. And if it's delete, we show

1:25:47

the one below. Before we test it out,

1:25:49

let's check out the progress tracker

1:25:51

that says that the current phase is the

1:25:53

feature four dialogues.

1:25:55

It has the goal to do it. And it has

1:25:58

actually completed the project dialogues

1:26:00

with all of these different components.

1:26:02

So, now if you come back to the editor,

1:26:04

this is going to look much better. It's

1:26:06

no longer just a blank screen, but

1:26:08

rather in the middle, it says create a

1:26:10

project or open up an existing one,

1:26:12

start a new architectural workspace, or

1:26:14

choose is project from the sidebar. And

1:26:16

if you click create project, it actually

1:26:19

opens up a new project dialogue where

1:26:22

you can type something like my project.

1:26:26

It automatically creates a slug at the

1:26:28

bottom as well. And if you click create,

1:26:30

you saw that creating loading.

1:26:33

And of course, the data is currently

1:26:35

static, but you can see how it's going

1:26:37

to look once it actually picks the data

1:26:39

from the database.

1:26:41

So, we have Ghost AI Core, which you can

1:26:44

select

1:26:45

to edit its name

1:26:47

as well as to delete it. This means that

1:26:50

the UI for all of the dialogue

1:26:52

functionalities has now been implemented

1:26:54

alongside this centerpiece of the

1:26:56

application, which means that now that

1:26:58

the majority of the UI is done, in the

1:27:00

next lesson, we can start focusing on

1:27:03

implementing real data with a real

1:27:05

database to make our app come to life.

1:27:08

But before we dive into the database,

1:27:10

let's make sure that our current code is

1:27:12

good. And I want to show you another

1:27:14

Code Rabbit feature allowing you to

1:27:16

review your code directly within your VS

1:27:18

Code, which means that you don't even

1:27:20

have to create a PR and you can be that

1:27:22

much faster. You can install the Code

1:27:24

Rabbit extension, authenticate to your

1:27:26

account.

1:27:28

It'll notice the changes that you have

1:27:29

right now, so you can just click review

1:27:32

all changes. And the review will start

1:27:34

directly within your editor.

1:27:37

So, let's give it a minute. We'll check

1:27:39

the changes and if they're good, push

1:27:41

them. If not, we're going to fix them.

1:27:43

And within a minute, the review is in

1:27:45

and Code Rabbit left the comments

1:27:47

directly on our code base. So, you can

1:27:49

expand all the files and click on them

1:27:52

to see exactly what's happening.

1:27:55

For example, right here, specific

1:27:57

confirmation message and project name

1:27:58

display, the delete project dialogue

1:28:01

lacks key detail. What text should be

1:28:03

shown to the user? Should the dialogue

1:28:05

show which project is being deleted? And

1:28:08

should we show the cancel button as

1:28:09

well? These are some important

1:28:11

questions. And it's good because in this

1:28:13

case Code Rabbit is telling us that we

1:28:15

can be even more precise with our

1:28:17

feature specifications. But, let's see

1:28:19

if it has any comments within our code

1:28:21

base. It says right here that the

1:28:22

project item that is this one right here

1:28:25

appears clickable, but it lacks

1:28:27

interaction handling. It has a cursor

1:28:29

pointer, that is this one right here in

1:28:31

the sidebar, but when I click on it, it

1:28:34

doesn't do anything. That's fine for now

1:28:36

because later on we're going to make it

1:28:37

open the actual canvas of that project.

1:28:40

Since that is not implemented yet,

1:28:42

that's totally fine. Then, another

1:28:44

inconsistency, feature four is being

1:28:46

marked as both in the current phase as

1:28:49

well as finished right here at the

1:28:50

bottom. So, we need to modify the

1:28:52

progress tracker to say that the feature

1:28:54

five is to be done. And then, and then

1:28:57

modify the current goal to be determined

1:29:00

for feature five. Next up, feature five.

1:29:03

This is good. And finally, in the use

1:29:05

project dialog hooks, we have a couple

1:29:08

of comments as well.

1:29:09

First, we have missing validation for

1:29:12

empty slug edge case.

1:29:14

If the user enters a name containing

1:29:16

only special characters, it passes the

1:29:18

truthy check, but returns an empty

1:29:20

string, resulting in a project with an

1:29:22

empty slug. So, if I head back right

1:29:25

here and try to create something, take a

1:29:27

look. It's true.

1:29:29

I can actually create a new project, but

1:29:31

nothing gets added to the slug because

1:29:33

these are not valid slug characters. So,

1:29:36

we definitely have to fix this.

1:29:37

Thankfully, what we need to do is add

1:29:40

slug validation.

1:29:42

So, I'll just press accept right here

1:29:45

and it'll update the code for me. That's

1:29:47

another perk of using Code Rabbit within

1:29:49

VS Code. And I think there is one other

1:29:51

comment right here in this dialog, which

1:29:54

is the same issue right here.

1:29:56

Same as before, the rename operation has

1:29:58

the edge case where the slug could end

1:30:00

up empty. So, we definitely want to fix

1:30:02

the validation there as well.

1:30:05

Now, we can push the changes by saying

1:30:07

get add dot, get commit {dash} m

1:30:11

implement dialogues

1:30:14

and then get pull to pull the latest

1:30:16

changes and then run get push {dash}

1:30:19

{dash} force to push the latest changes.

1:30:22

Because remember, we deleted that file

1:30:24

directly on GitHub, but still we want to

1:30:25

push the local changes as well. So now

1:30:28

if you want to, you can manually open up

1:30:30

a PR by heading over to new pull request

1:30:33

from the development branch

1:30:34

over to the main branch

1:30:37

and we can immediately merge it because

1:30:39

we've already reviewed all the changes

1:30:41

with Code Rabbit with their VS Code

1:30:43

extension. So back within the

1:30:45

application, you can run get pull

1:30:48

and you can also check out to main and

1:30:50

run get pull there as well

1:30:53

to be up to date. But make sure to

1:30:55

switch back to the development branch

1:30:56

because that is where we're actively

1:30:58

developing new features.

1:31:02

Now that our project management UI is

1:31:04

wired up, we need the actual database

1:31:06

behind it. And for that, we're going to

1:31:08

use Prisma with Postgres.

1:31:11

If you prefer a different database, the

1:31:13

process is the same. Just swap the

1:31:15

provider in the schema.

1:31:17

Prisma supports Postgres, MySQL, SQLite,

1:31:20

and more. So let's start with a manual

1:31:23

installation right here within the

1:31:24

terminal. Stop the app from running and

1:31:27

then run npm install Prisma tsx {at}

1:31:31

types {forward-slash} pg {dash} {dash}

1:31:33

save {dash} dev. These are the dev

1:31:35

dependencies so that we have a nice

1:31:37

development workflow. After that is

1:31:39

done, you can install all the necessary

1:31:41

packages needed for us to set up our

1:31:43

database such as {at} Prisma

1:31:45

{forward-slash} client, Prisma adapter

1:31:48

pg, {dot} env, and pg itself. And press

1:31:52

enter. After that is done, initialize

1:31:54

Prisma with the correct output path for

1:31:57

the Next.js router.

1:31:59

You can do that by running npx Prisma

1:32:01

init dash dash output, data slash app

1:32:04

generated Prisma. This creates a new

1:32:07

Prisma folder with the schema.prisma

1:32:10

file and an ENV file at the root. So,

1:32:13

head over to the Prisma dashboard. I'll

1:32:15

leave the link down in the description

1:32:17

and create a new account. You can sign

1:32:19

in with GitHub or Google. And once

1:32:22

you're in, you can create a new project.

1:32:25

I'll call it Ghost AI and they'll give

1:32:28

you a one command setup, but in this

1:32:30

case we can proceed with the connection

1:32:32

string.

1:32:33

So, you can just copy it and then head

1:32:35

over into your .env and override the

1:32:38

current database URL with the new one

1:32:40

that you just got from the dashboard. As

1:32:42

a matter of fact, we can take this

1:32:43

database URL and put it within the

1:32:45

.env.local as that's what we're using

1:32:47

for our environment variables. Then,

1:32:49

open up your prisma.config.ts

1:32:53

and update the path to the schema. It's

1:32:55

just going to be prisma forward slash.

1:32:57

We want to remove this schema.prisma

1:33:00

because we'll make a separate Prisma

1:33:02

model.

1:33:03

So, just use prisma forward slash. Now,

1:33:06

before we run our spec, let's also

1:33:08

install Prisma Agent Skill. We did the

1:33:11

same thing with Clerk, so the same idea

1:33:13

applies. Just for a different library.

1:33:16

Open up your terminal and run MPX skills

1:33:19

add Prisma skills.

1:33:22

It'll ask you which ones you

1:33:24

specifically want to install and in this

1:33:26

case you can select all of them. CLI,

1:33:28

client API, database setup, Postgres,

1:33:31

Postgres setup, and upgrade V7 and press

1:33:33

enter.

1:33:35

You can install them for cloud code

1:33:37

within this project with simlink.

1:33:40

And we can proceed with the

1:33:41

installation. And now we are ready to

1:33:43

generate our schemas. So, let's create a

1:33:46

new file right here within our context

1:33:49

feature specs

1:33:51

05-prisma.md.

1:33:54

And one sentence update is that Prisma

1:33:56

is already installed, but we needed to

1:33:59

add the project data models, Prisma

1:34:01

client singleton, and the first

1:34:03

migration. So, then we have to start

1:34:05

specifying the models that we want to

1:34:07

install.

1:34:09

That we want to set up.

1:34:11

First, we're going to have the project

1:34:13

model.

1:34:14

So, we'll ask it to add project that has

1:34:19

to have an owner ID mapped to the Clerk

1:34:21

user, a name, an optional description, a

1:34:25

status enum, either draft or archived, a

1:34:28

canvas JSON path for future canvas blob

1:34:31

storage, timestamps, and indexes on

1:34:34

owner ID and creation date. So, we can

1:34:36

actually search through them. The second

1:34:38

model we want to have is going to be the

1:34:40

project collaborator, which is a project

1:34:43

relation with cascading delete. It

1:34:46

includes collaborator email, timestamp,

1:34:49

and some constraints on the project and

1:34:51

email, and indexes, so we can actually

1:34:54

map over it. Do not add any extra fields

1:34:56

unless required by Prisma. Finally, to

1:34:59

be able to use these models, we have to

1:35:01

set up Prisma client.

1:35:03

So, create a lib prisma.ts file as a

1:35:07

cached singleton,

1:35:08

branch by database URL. If it starts

1:35:11

with Prisma plus Postgres, then use

1:35:13

accelerate, else direct to add Prisma

1:35:16

adapter PG. In our case, it's going to

1:35:19

start with Postgres forward slash, which

1:35:22

means that it is actually hosted

1:35:24

somewhere. Finally, we want to ask it to

1:35:27

run the migrations and to generate that

1:35:29

Prisma client, and want to verify

1:35:31

whether it has installed all the

1:35:32

dependencies,

1:35:34

such as Prisma, Prisma client, Prisma

1:35:36

adapter PG, and PG itself, and some

1:35:39

checks are whether schema has both

1:35:41

models with correct relations and

1:35:43

indexes, the lib Prisma file exports one

1:35:46

cached Prisma instance,

1:35:48

the migration runs successfully, and the

1:35:50

NPM run build passes. So, let's open up

1:35:54

Claude Code, give it this file, and tell

1:35:56

it to read this file, update the

1:35:59

progress tracker, and implement it

1:36:01

exactly as specified. I think you get

1:36:04

the idea with these prompts.

1:36:06

The actual spec file is doing the heavy

1:36:08

lifting.

1:36:09

So, let's see how it approaches it.

1:36:11

It'll first read the Prisma spec,

1:36:14

the progress tracker, the architecture

1:36:16

context, and only then will it start

1:36:18

implementing it.

1:36:20

It'll first verify we have all the

1:36:22

necessary packages, and it might even

1:36:24

consult the Prisma skill. So, the agent

1:36:27

does a better way implementing the best

1:36:29

practices. So, let's give it some time,

1:36:31

and I'll be right back. Now, it's in the

1:36:33

process of creating the Prisma models

1:36:36

for the project and the project

1:36:37

collaborator.

1:36:39

Then, creating the Prisma singleton, so

1:36:41

we can actually have the client and run

1:36:43

it. It's asking us whether we can

1:36:45

actually run Prisma migrate. So, I'll

1:36:47

allow it to run it.

1:36:50

And then, it'll verify everything is

1:36:51

done, and we'll be able to check it all

1:36:53

out.

1:36:54

You can see that we have many files

1:36:56

changed, but once again, the majority of

1:36:58

these are coming from the agents that we

1:37:00

installed or the agent skills. The

1:37:02

actual generated files that we care

1:37:04

about are going to be within just a

1:37:06

couple of files, such as this Prisma

1:37:08

model right here,

1:37:10

and this Prisma client. That's it. But

1:37:13

okay, let's let it do its thing, and the

1:37:15

feature five is done. Here's what's

1:37:18

created, just two files that we need to

1:37:20

take a look at.

1:37:21

The first one is the project model, and

1:37:24

the second one is the Prisma client. So,

1:37:27

let's go ahead and check them out. I'll

1:37:28

first open up the model that is going to

1:37:31

be within models, project.prisma, and it

1:37:34

looks like I'm missing the Prisma syntax

1:37:37

highlighting. So, I'll head over into

1:37:39

extensions and install Prisma, which

1:37:42

would add the syntax highlighting,

1:37:43

formatting, auto completion, and more.

1:37:46

There we go. This now looks better. But

1:37:48

yeah, essentially we have created a new

1:37:50

model for the project with the ID, owner

1:37:53

ID, name, description, status, the

1:37:56

canvas, which is going to be attached to

1:37:57

a project very soon, and a potential

1:38:00

list of collaborators. And we also made

1:38:02

it indexable so we can search for these

1:38:04

projects.

1:38:05

Same thing happens with the project

1:38:07

collaborator. That's going to be a clerk

1:38:09

ID connected to it. And it's going to

1:38:12

have access to one or more projects.

1:38:15

Then, if we take a look at the actual

1:38:17

Prisma client, that's going to be within

1:38:20

lib Prisma,

1:38:22

we're just using the Prisma PG adapter

1:38:24

to create a new Prisma client and

1:38:27

connect it to it.

1:38:29

Then, we export this global Prisma

1:38:31

instance that we can use to make any

1:38:33

kind of database calls. We can't really

1:38:36

test a lot of stuff right here because

1:38:38

we've just built a database.

1:38:39

But in the next lesson, we can build a

1:38:41

couple of API routes that'll bring us

1:38:43

one step closer to actually making use

1:38:45

of this data.

1:38:47

So, for time being, let's just run git

1:38:49

add dot, git commit {dash} m, implement

1:38:54

Prisma,

1:38:55

and then git push.

1:38:59

Now that our schema is ready, we're

1:39:02

ready to build the API routes that sit

1:39:05

on top of it.

1:39:06

This is back end only.

1:39:08

We're not yet wiring the UI. That'll

1:39:11

come next. But right now, we need to

1:39:13

focus on one single thing.

1:39:15

A clean, secure set of routes for

1:39:18

creating, listing, renaming, and

1:39:21

deleting projects.

1:39:23

So, create a new spec right here under

1:39:26

context feature specs 06

1:39:30

project APIs.md.

1:39:33

And within it, we want to tell it that

1:39:35

the database schema is ready. So, we

1:39:38

need to build the back end project API

1:39:41

routes. The routes are as follows: the

1:39:44

REST endpoints for get API project,

1:39:46

which is going to list the current

1:39:48

user's projects, the post for API

1:39:51

projects, which creates a project, we

1:39:54

have the patch for renaming, and delete

1:39:57

for, obviously, deleting.

1:39:59

We can also give it a couple of rules,

1:40:02

such as use the authenticated clerk user

1:40:05

ID as an owner ID.

1:40:07

And when creating, default missing

1:40:10

project to untitled project and use the

1:40:12

schema's existing ID strategy without

1:40:15

adding sequential IDs. We also want to

1:40:18

tighten up the security a bit by telling

1:40:20

it that the unauthenticated requests

1:40:22

return 401, and only the project owner

1:40:26

can rename or delete. Non-owners

1:40:28

mutations return 403. Which means that

1:40:31

we're making our app secure not only on

1:40:33

the client side, but the server-side API

1:40:36

calls are also going to return invalid

1:40:38

responses if somebody tries to break

1:40:41

them. And again, we're not yet wiring

1:40:43

any UI.

1:40:44

And finally, to verify, we need to check

1:40:46

whether the routes exist, whether the

1:40:49

owner's checks are enforced, whether 401

1:40:52

and 403 responses are handled correctly,

1:40:54

and that the npm run dev build passes.

1:40:57

You know the drill. Open up Claude code,

1:40:59

tell it to read the file, and execute.

1:41:03

Read this file, update the progress

1:41:05

tracker, and implement it exactly as

1:41:07

specified. Let's see how it does.

1:41:10

The process of actually creating four

1:41:13

REST API routes would take us some time,

1:41:16

and you most likely already know how to

1:41:17

do that. But our agent is just going to

1:41:19

do it much more quickly for us. And then

1:41:22

when you take a look at it, you can

1:41:23

fully understand the structure, and

1:41:25

you'll be able to add any other

1:41:26

additional routes very easily, because

1:41:28

it's mostly boilerplate. So, let's give

1:41:30

it some time, and I'll be right back.

1:41:32

There we go. That was quick. Four routes

1:41:35

have been created, and the build is

1:41:37

clean.

1:41:38

All of them are right here within

1:41:40

project routes or project project ID

1:41:43

route. So, these are the general ones

1:41:46

and these are the ones for update and

1:41:48

delete.

1:41:49

Let's go ahead and check them out. They

1:41:51

are right here under app API route.ts

1:41:56

for general project routes where we have

1:41:59

a asynchronous get function where we

1:42:01

first get the user ID from auth

1:42:03

belonging to clerk.

1:42:06

We check whether the user ID doesn't

1:42:07

exist. In that case, we return a 401.

1:42:10

But, if it does exist, we try to find

1:42:13

all the projects that match with that

1:42:15

user and then we return them.

1:42:17

Similarly, if the user is trying to

1:42:19

create a post, we get all the data from

1:42:23

request.json. We take the name and

1:42:26

finally create it. Then, if we want to

1:42:28

update or delete them, you can head over

1:42:30

to the project ID route where we can

1:42:33

patch it based on the project ID. So, we

1:42:36

first check whether the user has access.

1:42:39

If they do, we take a look at the

1:42:40

context params,

1:42:42

find the project, parse the data,

1:42:45

update the project data, and then return

1:42:47

it.

1:42:48

And the same thing goes with the delete.

1:42:50

It's going to be even simpler. We find

1:42:52

it, we delete it, and we call it a day.

1:42:55

It's even returning a proper 204 status,

1:42:58

which means deleted. But now, in the

1:43:00

same lesson, I want to actually create

1:43:02

an additional feature spec, which is

1:43:04

going to be 07

1:43:06

wireeditorhome.md.

1:43:10

Where we want to wire the editor home

1:43:13

sidebar and dialogues to the real

1:43:16

project APIs we just created.

1:43:19

So, first, we got to deal with data

1:43:21

fetching because the editor home page is

1:43:24

a server component. So, we need to fetch

1:43:27

owned and shared projects server-side

1:43:30

using the existing project data helper

1:43:33

and pass both lists to the sidebar. And

1:43:35

I don't want to see any client-side

1:43:37

fetching for the initial load. Now, we

1:43:39

can do that by using the project

1:43:42

actions. So, I want to create a new hook

1:43:45

in the hooks folder that manages dialog

1:43:48

state and project mutation.

1:43:51

That's going to look something like

1:43:52

this.

1:43:53

Create manage create dialog state,

1:43:56

manage project name input, generate a

1:43:59

short unique suffix, slugify the name,

1:44:02

call the post API projects, and then

1:44:05

navigate over to the new workspace.

1:44:07

The project ID and Liveblocks room ID

1:44:09

should stay aligned, and we can do a

1:44:12

similar thing for rename and delete. The

1:44:15

rename simply has to store the target

1:44:17

project ID plus current name, and then

1:44:20

patch the name, and delete has to store

1:44:22

the target project ID, and then patch

1:44:24

it. Finally, we are ready to wire it all

1:44:27

together by connecting the hook to the

1:44:30

sidebar and dialogs. Create dialog will

1:44:32

show a room ID preview.

1:44:35

Rename dialog will prefill the current

1:44:37

name, and delete dialog will show the

1:44:39

project name. As usual, we want to run

1:44:42

some checks when we are done, and that

1:44:43

is to see whether the sidebar uses real

1:44:46

project data, not the fake dummy data it

1:44:48

uses right now, whether the create

1:44:50

actually navigates to the workspace,

1:44:52

whether the rename updates correctly,

1:44:55

and whether delete refreshes or

1:44:57

redirects correctly.

1:44:58

Finally, the build has to pass. So, this

1:45:01

is the moment that our sidebar goes from

1:45:03

mock data to real data.

1:45:06

So, let's open up our agent,

1:45:09

tell it to read the file,

1:45:12

update the progress tracker, and

1:45:14

implement it exactly as specified. So,

1:45:16

let's let it do its thing, and once it's

1:45:19

done, we can test it out in the browser.

1:45:21

And after some time, we are back. The

1:45:23

build passes clean, and it created a

1:45:26

couple of new files. The lib projects is

1:45:29

used for fetching the own projects and

1:45:31

shared projects. We can quickly check

1:45:33

that out right here under lib projects.

1:45:38

And you can see that this file simply

1:45:40

exports one function called get projects

1:45:43

for user, which calls all the projects

1:45:45

where the owner ID is the user ID that

1:45:48

is currently signed in, and then it

1:45:50

returns all the owned and shared

1:45:52

projects. It also created a hook that

1:45:54

deals with all the project actions.

1:45:57

And it modified some additional files

1:45:59

such as the editor, the projects route,

1:46:02

and some more types and props updates

1:46:04

over the sidebar and the dialogues. So,

1:46:06

don't forget to rerun your application

1:46:08

by running npm run dev. And then back on

1:46:11

localhost:3000, we are ready to test it

1:46:14

out by creating a new project. I'll give

1:46:17

it a name such as my system design.

1:46:22

And you can see that it's going to give

1:46:24

it an additional slug right here, and we

1:46:26

can click create project.

1:46:29

It's creating it, and we get redirected

1:46:31

to editor my system design. So, the

1:46:34

redirect is actually working, but

1:46:36

there's no route under that page. But,

1:46:39

that's to be expected because so far, we

1:46:41

just wanted to test whether we can wire

1:46:43

the database functions with the API

1:46:45

routes. So, if I head back over to the

1:46:47

editor and then open up the sidebar,

1:46:49

you'll see that there's a new my system

1:46:51

design project. And you can also rename

1:46:55

it to something like Ghost AI system

1:46:59

architecture, and you can see that it

1:47:01

updated it in real time. Or, you can

1:47:03

also just delete it. Let's test it out.

1:47:06

Yep, that works and it updates in real

1:47:08

time. So, this means that we have not

1:47:10

only implemented the dialogues to create

1:47:13

all of this, but also implemented the

1:47:16

API routes that handle the

1:47:17

functionality.

1:47:19

And if you're wondering why we didn't do

1:47:20

all of this in a single prompt? Well,

1:47:23

the API routes touch the back-end layer

1:47:26

and the UI wiring touches the front-end

1:47:28

and server components.

1:47:30

Combining them gives the agent too much

1:47:32

surface area to make assumptions across.

1:47:35

Instead, we had two focused prompts,

1:47:38

which means that we got back two clean

1:47:40

results without messing up stuff on

1:47:42

front-end and the back-end. So, before

1:47:44

we go ahead and review these changes,

1:47:46

head over to GitHub and merge the

1:47:48

previous PR, which contained

1:47:50

implementing Prisma and even more

1:47:52

importantly adding all of those

1:47:54

additional files for agent skills. So,

1:47:57

I'll just go ahead and merge it. So, now

1:48:00

when we push this over to GitHub, we'll

1:48:01

be able to review just those changes.

1:48:04

So, run git add dot,

1:48:06

git commit {dash} m, wire up Prisma

1:48:11

UI and REST APIs,

1:48:14

and run git push. You can head over to

1:48:16

your repo, open up a new pull request,

1:48:19

and do it specifically from the

1:48:20

development branch. This one will be

1:48:22

fairly quick as there's only 11 files

1:48:24

changed. So, let's give the Dr. Code

1:48:27

Rabbit some time to review it. In this

1:48:29

PR, we've finally introduced the REST

1:48:32

APIs and CRUD functionalities, making

1:48:35

our app, well, full stack. We've done

1:48:37

that across all the different files and

1:48:39

I always like when Code Rabbit thinks

1:48:42

the functionality is so detailed that it

1:48:44

actually gives us a diagram that we can

1:48:46

review. So, as the user clicks the new

1:48:49

project, the action that open create

1:48:51

dialog runs.

1:48:53

Then we initialize the dialog and

1:48:55

generate the room ID with a suffix.

1:48:58

We ask the user to enter the project

1:49:00

name and submit. After they submit, we

1:49:03

set the loading and we then make a post

1:49:05

request to our API projects, which is

1:49:08

our CRUD REST API route, which then

1:49:11

calls Prisma project create.

1:49:13

As soon as the project is created in the

1:49:15

database, we return the project data,

1:49:18

set the loading to false, and bring it

1:49:20

back and then navigate over to the new

1:49:22

project. It looks like we have one issue

1:49:25

where we need to import the use project

1:49:27

actions as a value and not as a type.

1:49:30

So, return type of requires the hook in

1:49:32

a value space, but import type is a type

1:49:35

only import that TypeScript erases from

1:49:38

the value namespace. So, what we can do

1:49:40

is just copy this import right here,

1:49:43

find where we're already importing a

1:49:45

type, specifically the use project

1:49:48

actions. That's going to be right here.

1:49:50

And instead of it, we can just import

1:49:52

the use project actions without the type

1:49:55

at the start.

1:49:57

That way, when it's referred right here,

1:49:59

we can say type of use project actions,

1:50:02

which is a function that TypeScript can

1:50:04

now actually understand. This is a

1:50:05

pretty nice save. And there's another

1:50:07

issue in the use project actions where

1:50:10

we need to handle the failed mutations

1:50:12

before closing or redirecting. So,

1:50:15

rename and delete always close the

1:50:17

dialogue and refresh push even if the

1:50:20

API calls fail. So, a server or off

1:50:23

error can look like a success. We need

1:50:25

to gate those transitions on res.ok and

1:50:28

keep the dialogue open on failure.

1:50:30

Thankfully, there's a quick fix right

1:50:31

here or we can copy just this part,

1:50:33

which is going to be the submit

1:50:35

function. So, copy the submit,

1:50:37

head over into the project actions or

1:50:41

use project actions.

1:50:44

And again, the these changes might be

1:50:46

different for you as they are for me.

1:50:48

So, if you have some other issues to

1:50:49

fix, you can definitely do that.

1:50:52

I'll remove the submit,

1:50:55

bring in the correct one, push the

1:50:57

changes by running git add dot, git

1:50:59

commit {dash} m, implement

1:51:03

CodeRabbit suggested

1:51:06

fixes

1:51:07

and run git push.

1:51:10

The changes will automatically be

1:51:12

recognized right here. So, we can go

1:51:14

ahead and merge it and we are ready to

1:51:16

continue.

1:51:19

Before we build the canvas, let me

1:51:21

quickly explain how Liveblocks works.

1:51:24

Because once you understand it, the

1:51:26

build order will make complete sense.

1:51:29

Liveblocks gives every project a shared

1:51:32

real-time room.

1:51:33

Think of it like a live session.

1:51:36

Everyone who opens the same project

1:51:38

connects to that room over web sockets.

1:51:41

And any change one person makes

1:51:43

instantly appears on everyone else's.

1:51:46

That canvas state, multiple users'

1:51:48

cursors, the AI drawing nodes, all of it

1:51:52

lives in that shared room and syncs in

1:51:54

real time. Liveblocks rooms are open by

1:51:57

default. So, technically, anyone could

1:51:59

connect if we let them.

1:52:01

But since Ghost AI is a project-based

1:52:04

app where only owners and collaborators

1:52:06

should have access, we need to control

1:52:09

who gets in with a sharing mechanism.

1:52:12

So, before Liveblocks connects a user to

1:52:14

a room, it has to call authentication

1:52:16

endpoints first.

1:52:18

And when we check whether that user

1:52:20

belongs to a specific project, only if

1:52:22

they do, we issue a token to let them

1:52:25

in.

1:52:26

That token endpoint is what we're

1:52:28

building in this chapter. And for it to

1:52:30

work, the access control and the

1:52:32

collaborator model have to exist. So,

1:52:34

we're doing it in this order. Workspace

1:52:37

access first, so each project has a

1:52:39

secure route only authorized users can

1:52:42

enter. And then we'll focus on the

1:52:44

collaborator model second, so the system

1:52:46

knows exactly who those authorized users

1:52:49

are.

1:52:50

Then Liveblocks and then the canvas.

1:52:53

So, head over into context, feature

1:52:55

specs, and create a new file called 08

1:52:59

editor-workspace-shell.md.

1:53:03

And within it, we'll explain exactly

1:53:05

what has to happen next. So, let's go

1:53:07

through it together. The goal of this

1:53:09

prompt is to build the editor room ID

1:53:12

workspace.

1:53:13

Remember, that's that page that we got

1:53:15

redirected to and we saw a 404. So, if I

1:53:19

create a new spec right here and we

1:53:22

navigate over to it, we get a 404. So,

1:53:25

this is the page we're building. Before

1:53:27

rendering, the unauthenticated users

1:53:29

will be redirected to sign in. The users

1:53:32

without project access will see the

1:53:34

access denied and non-existent projects

1:53:37

will also show access denied.

1:53:40

Then, we need to create that access

1:53:42

denied component with a centered layout,

1:53:44

lock icon, short message, and a link

1:53:47

back to the editor.

1:53:48

And we can also create some helpers that

1:53:50

are going to make it simpler for us to

1:53:52

reuse the access functionality

1:53:54

such as getting access to Clerk's

1:53:56

identity and checking whether they can

1:53:58

access a specific project.

1:54:01

When it comes to the layout, we want to

1:54:02

build a full viewport workspace layout

1:54:05

with a top bar showing the project name,

1:54:07

navbar actions at the top right, the

1:54:10

existing projects hyper on the left, the

1:54:13

current room highlighted in the sidebar,

1:54:16

central canvas placeholder with a dark

1:54:18

background and a centered message, and

1:54:19

the right sidebar placeholder for future

1:54:21

AI chat. This is exactly how the final

1:54:24

version of the application looks like.

1:54:26

We have the left sidebar with the

1:54:27

currently open project highlighted, the

1:54:30

top bar with the icons on the right, and

1:54:32

then we have the AI chat, which is going

1:54:34

to be coming soon. The canvas area, of

1:54:36

course, should fill the remaining space.

1:54:39

And in this case, we want to tell it to

1:54:41

not add any real canvas logic, live

1:54:44

blocks, AI chat, or sharing behavior

1:54:47

yet. That's the keyword right here.

1:54:50

When it's done, it should perform the

1:54:51

following checks. So, let's open it up

1:54:54

within our agent

1:54:55

and tell it to read the current file,

1:54:58

update the progress, and execute it

1:55:01

exactly as specified. Of course, make

1:55:03

sure that it knows what file you're

1:55:05

talking about. Let's run it and give it

1:55:07

some time to process it. And after a

1:55:10

couple of minutes, it is done. I

1:55:12

actually took Codex for a spin to see

1:55:14

how well it can handle it. And yeah, it

1:55:16

said that it implemented feature eight

1:55:18

as specified. The guarded workspace

1:55:20

route now lives under this page, stays

1:55:23

server-side, and the redirects

1:55:25

unauthenticated users to sign in. The

1:55:27

workspace shell component is right here,

1:55:30

and it includes a project aware top

1:55:32

navbar with buttons to share. Okay, so

1:55:35

let's go ahead and test it. Oh, this is

1:55:38

looking nice. It is a bit different from

1:55:39

the final design, but I actually love

1:55:42

it. You can see how the sidebar

1:55:44

collapses and then the central part

1:55:46

actually expands. Later on, we can play

1:55:48

a bit more with the design, but so far,

1:55:50

I love it. So, right now we are looking

1:55:52

the details of the new spec project

1:55:55

because you can see that we're on that

1:55:56

specific URL. And if you just head over

1:55:58

to the editor, you'll be able to see

1:56:00

something like this. You can expand the

1:56:02

sidebar and then navigate over to the

1:56:04

details page.

1:56:06

If the redirect isn't working for

1:56:07

whatever reason, send a small corrective

1:56:10

prompt describing exactly what's

1:56:12

happening, and it'll fix it. Now, let's

1:56:15

test access control.

1:56:17

Open up an incognito tab and then head

1:56:19

over to the same URL. You'll

1:56:21

automatically be redirected back to sign

1:56:23

in, which is the first good sign. Go

1:56:25

ahead and create a new account. I'll try

1:56:28

to use the email and password this time.

1:56:31

Let's go with contact js mastery pro and

1:56:34

a password. And it's good to see that

1:56:36

Clerk is trying to keep us safe, so it's

1:56:38

suggesting a stronger password. So, let

1:56:40

me do that. There we go. And for the

1:56:44

email, I'll use a secondary email that I

1:56:47

have because the first one is already

1:56:49

tied with GitHub. There we go. And we're

1:56:51

going to even have the email

1:56:53

verification. So, you're going to get an

1:56:55

email that looks something like this. Go

1:56:56

ahead and copy the verification code.

1:56:59

Then, paste it right here and you'll be

1:57:01

logged in and redirected to the editor.

1:57:04

So, if once again, you try to go to that

1:57:06

same project URL,

1:57:09

you should hit the access denied screen.

1:57:11

You don't have access to this workspace.

1:57:13

Hit back to your editor home to open up

1:57:15

a project you actually can access.

1:57:17

That's great. So, now, let's allow this

1:57:19

user to actually invite that

1:57:21

collaborator. To do that, I'll head over

1:57:24

within our context, feature specs, and

1:57:27

create a new file called 09 share

1:57:31

dialog.md.

1:57:33

Let's go through it together. In this

1:57:34

case, we're basically working on the

1:57:36

share button.

1:57:37

It's going to be within the editor

1:57:39

navbar and it's going to open up the

1:57:41

share dialog, allowing owners to invite

1:57:44

collaborators by email, view current

1:57:47

collaborators, remove collaborators, and

1:57:49

copy the project link with the temporary

1:57:52

copy feedback.

1:57:53

Collaborators can view the collaborator

1:57:55

list only and not invite, remove, or

1:57:59

manage access. It's also important that

1:58:01

we're going to use Clerk data for the

1:58:03

collaborator sharing system. So, they're

1:58:05

going to be stored by email in the

1:58:07

database and we'll use Clerk's backend

1:58:09

API to enrich the collaborator email

1:58:12

with display name and avatar image.

1:58:16

And if a Clerk user is not found for an

1:58:18

email, then we can fall back to showing

1:58:20

the email only. We want to add the

1:58:22

required API logic for listing,

1:58:24

inviting, and removing collaborators,

1:58:27

and we want to enforce ownership

1:58:28

server-side, not client-side, for

1:58:31

inviting and removing actions. Finally,

1:58:34

we have a couple of checks to see

1:58:35

whether everything's been done properly.

1:58:37

So, once again, let's open up Codex or

1:58:40

whichever agent you're using and tell it

1:58:42

to read the 09 share dialog,

1:58:47

update the progress tracker,

1:58:49

and implement it exactly as specified.

1:58:52

We can do it within the same chat

1:58:55

because this is somewhat related to the

1:58:56

functionality we just worked on. Let's

1:58:58

give it some time and I'll be right

1:59:00

back. And in about 5 minutes, feature

1:59:02

number nine got implemented exactly

1:59:05

within the specs scope. The workspace

1:59:08

navbar share button now opens a new

1:59:10

dialogue.

1:59:12

So, what do you say that we actually

1:59:13

test it out? Back within the browser,

1:59:15

but not the anonymous one, which doesn't

1:59:17

have the access. We have to go to the

1:59:19

one that is the owner of this workspace.

1:59:22

We can now click this share button,

1:59:24

which allows us to copy the workspace

1:59:25

link or invite them via email. And I

1:59:28

love this share interface. So, I'll

1:59:30

enter the email of my second account

1:59:34

and click invite. You can see that the

1:59:36

user has been invited as the

1:59:37

collaborator, and since we signed up via

1:59:40

email, this user doesn't have a profile

1:59:42

photo. So, now if you head back and

1:59:44

reload,

1:59:46

check this out.

1:59:48

The user now has access. And for this

1:59:50

user, it's not under my projects, but

1:59:52

under shared projects. And this user

1:59:55

doesn't have the permissions to update,

1:59:57

rename, or delete. Whereas for this

2:00:00

user, you can see that it's under my

2:00:01

projects and we have full permissions.

2:00:04

That's the full collaborator flow

2:00:06

working end-to-end.

2:00:08

Owner invites, collaborator receives the

2:00:10

invites, and nobody else can access it.

2:00:13

And before we push the changes, let's

2:00:15

review them with Code Rabbit. This time,

2:00:17

I'll do it directly within VS Code

2:00:19

through the Code Rabbit extension. And

2:00:21

as soon as the comments are in, we can

2:00:23

go ahead and check them out. Within the

2:00:25

use project share.ts file, that is the

2:00:28

hook that the share button borrows the

2:00:31

functionality from.

2:00:32

And right here, where we have the copy

2:00:35

link button, this does nothing else than

2:00:37

basically copy the URL to clipboard.

2:00:40

But currently, the copy link doesn't

2:00:42

handle the clipboard API failures, and

2:00:44

the timer lacks cleanup. So, if the

2:00:46

clipboard fails due to permissions or

2:00:48

something else, the set timeout will

2:00:51

still fire letting the user know that it

2:00:53

has been copied, whereas that's not

2:00:55

really true.

2:00:56

So, we have to set the error in case

2:00:58

something goes wrong. We can very easily

2:01:00

do that. I'll just copy this part right

2:01:04

here with a try and catch block, and we

2:01:06

have to replace everything from await

2:01:09

navigator. So, right here, await

2:01:11

navigator,

2:01:12

replace it with this part right here.

2:01:15

And we have to remove these plus signs

2:01:17

right here at the start. Okay, great.

2:01:20

So, this one has been fixed, and I

2:01:22

believe there's another one right here

2:01:24

where we have the reload functionality.

2:01:26

Reload silently swallows errors, which

2:01:29

may cause stale UI state. If the reload

2:01:32

function fails due to a network error,

2:01:34

the error is not captured, and the UI

2:01:36

may show outdated collaborator data

2:01:39

after a successful invite or remove,

2:01:41

which means that here we also have to

2:01:43

check for errors and display them if

2:01:45

there are any. So, once again, I'll copy

2:01:48

this part and replace the old one.

2:01:51

And don't forget to remove the plus

2:01:53

signs just to make sure it works.

2:01:55

Wonderful. So, two comments in this file

2:01:57

resolved. We have one more in the

2:01:59

project share dialogue. This is the

2:02:02

actual dialogue where we have a key

2:02:04

collision when collaborators share the

2:02:06

same email or display name. Well, that's

2:02:09

not really going to happen, right?

2:02:11

Because each one of the collaborators is

2:02:12

unique. So, this isn't unlikely, but

2:02:15

it's impossible. So, yeah, this one is

2:02:17

good. And finally, there is one more

2:02:19

under project collaborators where it

2:02:22

says batch emails in chunks of 500 to

2:02:25

respect Clerk's API limit. Well, yeah,

2:02:28

here where we're getting the users, uh

2:02:30

we definitely don't want to fetch more

2:02:31

than 500 users. This is great regardless

2:02:34

of Clerk's limit, but I'm glad that it

2:02:36

caught it because you never want to

2:02:38

fetch so many emails at the same time.

2:02:41

So, in this case, it's not just a copy

2:02:43

and paste that couple of lines of code.

2:02:45

What we can do here is use our agent to

2:02:48

fix it. So, if you click that button,

2:02:50

it's going to copy a little prompt and

2:02:53

put it into the chat that's going to

2:02:54

know exactly which part it has to fix.

2:02:56

So, let's give it a second to read it

2:02:58

and fix it. And it's done. The get clerk

2:03:01

email now batches emails into chunks of

2:03:03

500 so that it doesn't go over the

2:03:05

limit. But, instead of doing that, what

2:03:08

I actually want to do is never fetch

2:03:10

more than 500. I mean, that's too much.

2:03:13

So, I'll tell it

2:03:14

instead of chunking the emails, maybe we

2:03:17

just don't have to fetch so many. We can

2:03:19

just fetch fewer emails. So, let's see

2:03:21

how it handles a bit more vague

2:03:23

response. I mean, so far it has been

2:03:25

implementing everything so perfectly

2:03:28

because our prompts, or our specs,

2:03:31

should I say, were so precise. So, it

2:03:33

never had any issues.

2:03:35

But, yeah, in this case, it simplified

2:03:37

it. It's going to call fewer emails.

2:03:39

Good. So, we're going to keep this. And

2:03:41

now that all the issues have been fixed,

2:03:43

we actually fixed these two manually, we

2:03:45

can just go ahead and push all of these

2:03:47

changes by opening up our terminal and

2:03:50

running get add dot get commit {dash} m

2:03:54

implement share functionality.

2:03:58

And get push.

2:04:00

Perfect. Great job.

2:04:03

Now that the access control is done and

2:04:06

collaborators and sharing is enabled,

2:04:10

we are ready to finally build the

2:04:12

canvas. First, click the link down in

2:04:14

the description and head over to

2:04:16

Liveblocks.

2:04:18

If you haven't already, create a new

2:04:20

account, create a new project, call it

2:04:23

Ghost AI, and choose a region that is

2:04:25

closest to you. And the environment can

2:04:27

be set to development for now. Once the

2:04:29

project is created, select personalized

2:04:32

setup

2:04:33

and multiplayer. Then, select canvases

2:04:36

as we want to focus on workflow

2:04:38

diagrams, whiteboards, design tools, or

2:04:40

in this case, code architecture. And

2:04:43

then for the guide, you can select React

2:04:45

Flow with Next.js. That's exactly what

2:04:48

we're using. Then, copy the first

2:04:49

command that you can see right here, and

2:04:51

paste it within your terminal. You can

2:04:53

press enter, and here we're installing a

2:04:55

couple of things.

2:04:56

The Liveblocks client is the core client

2:04:59

that manages the WebSocket connection to

2:05:02

Liveblocks.

2:05:03

Then, there's the Liveblocks React,

2:05:05

which contains the React hooks for

2:05:07

accessing shared room state, presence,

2:05:10

and storage.

2:05:11

There's also the Liveblocks React UI,

2:05:13

containing the pre-built UI components.

2:05:16

There's also Liveblocks React Flow,

2:05:19

which is the bridge between Liveblocks

2:05:21

and React Flow, so canvases are synced

2:05:24

in real time across all the connected

2:05:26

users.

2:05:27

And finally, the XY Flow is the React

2:05:30

Flow itself, which handles the canvas,

2:05:32

nodes, edges, and interaction. Once that

2:05:35

is done, we can initialize the

2:05:37

Liveblocks config file. So, go ahead and

2:05:39

copy this command, and paste it into the

2:05:42

terminal,

2:05:43

and press enter.

2:05:45

This will generate a Liveblocks

2:05:47

config.ts file at the root of our

2:05:49

directory.

2:05:50

This is where we define the TypeScript

2:05:52

types for the shared room, what the

2:05:54

presence will look like, what user data

2:05:56

we want to attach to each connected

2:05:58

session. You can think of it as the

2:06:00

contract that describes everything

2:06:02

shared between users in real time.

2:06:05

And we'll configure it with AI. But

2:06:07

before we do, let's install Liveblocks

2:06:10

agent skills. Same pattern as with Clerk

2:06:13

and Prisma. We want to allow our agent

2:06:15

to be fluent in how Liveblocks works.

2:06:19

So, go ahead and copy this command, or

2:06:21

follow along with me,

2:06:23

and just run

2:06:26

npx skills add Liveblocks skills.

2:06:30

Say Y to update it, and then select

2:06:33

Liveblocks best practices, and we don't

2:06:35

need the second part. Press enter.

2:06:38

Select Claude or whichever agent you're

2:06:40

using,

2:06:41

and install it for the project and using

2:06:43

Simulink.

2:06:45

Perfect. This was quick. Now, I want to

2:06:47

split this specific feature

2:06:49

implementation into three separate spec

2:06:52

files. So, let's take it step by step,

2:06:55

and I'll walk you through everything.

2:06:56

First, let's dive into feature number

2:06:58

10, Liveblocks setup. Here, we want to

2:07:01

tell it to set up the real-time

2:07:03

collaboration infrastructure using

2:07:06

Liveblocks. This file will wire up the

2:07:09

entire collaboration infrastructure. The

2:07:11

config file will get the presence and

2:07:13

user metadata types, and then we'll

2:07:15

create a Liveblocks client. Finally,

2:07:18

we'll build the off endpoints like API

2:07:21

Liveblocks off, so we can verify that

2:07:23

people can actually access the room. So,

2:07:25

go ahead and open it with either Codex

2:07:28

or Claude Code. Tell it to read this

2:07:31

file,

2:07:32

update the progress,

2:07:34

and execute everything as specified, and

2:07:37

press enter. Let's give it some time to

2:07:39

do all the hard work for us while we

2:07:42

remain the architect and monitor exactly

2:07:45

what it is doing. And very soon, feature

2:07:47

10 is complete. It implemented just

2:07:50

three separate files. The Liveblocks

2:07:52

config, now with types for the presence

2:07:55

and user metadata, the lib Liveblocks

2:07:58

file, which allows us to fetch the

2:08:00

cached Liveblocks node client, so we can

2:08:03

use its instance, and we also have the

2:08:06

function that maps out a specific user

2:08:08

color. Most importantly, we have the

2:08:10

post endpoint that verifies whether the

2:08:12

user has been authenticated and whether

2:08:15

it has access to the project. If it

2:08:17

does, it returns all the data, and we're

2:08:20

one step closer to testing it out. But,

2:08:22

all of these were just some

2:08:23

functionalities that we need to use

2:08:25

within our code, but we're not using

2:08:27

them yet. So, instead of testing at this

2:08:29

point, what I want to do is head over to

2:08:32

feature number 11.

2:08:34

That's going to be the base canvas.md.

2:08:38

So, right here in lesson 11, we want to

2:08:41

replace the canvas placeholder with a

2:08:44

real LiveBlocks backed ReactFlow canvas.

2:08:48

Allowing multiple users to connect to a

2:08:51

LiveBlocks room and share the same

2:08:53

canvas state. That means that the nodes

2:08:56

and edges will sync in real time. And

2:08:59

after that, we can basically say that we

2:09:01

have a full collaborative canvas

2:09:03

application.

2:09:04

Not yet polished, but real. We don't

2:09:08

want to add any controls yet or custom

2:09:10

nodes or edges. We just want to create

2:09:12

that canvas.

2:09:14

So, let's open this up in a new chat. As

2:09:17

usual, I'll tell it to read the file,

2:09:21

update the progress,

2:09:23

and execute as specified. And I've just

2:09:27

went to grab a coffee, and feature 11 is

2:09:30

done. So, let's review it. It built out

2:09:32

the canvas types, so that our

2:09:34

application remains heavily typed.

2:09:37

Then, it added the storage part over to

2:09:40

the LiveBlocks config.

2:09:41

This matters right here because it knows

2:09:43

that we're going to be keeping track of

2:09:45

the presence of the cursors and whether

2:09:47

they're thinking,

2:09:48

but also the storage of different nodes

2:09:50

and edges and users on the screens.

2:09:52

Later on, we're going to fill up these

2:09:54

two.

2:09:55

using LiveBlocks provider and the room

2:09:57

provider, it created the canvas room.

2:10:00

And most importantly, the canvas

2:10:02

placeholder got replaced with that room.

2:10:05

So, what do you say that we go ahead and

2:10:07

test it out?

2:10:08

Back on localhost:3000, we got

2:10:10

connecting to a room, but there's one

2:10:13

issue. It says LiveBlocks authentication

2:10:16

failed, reason not provided. Maybe

2:10:18

because this project was created before

2:10:20

our whole LiveBlocks setup. So, what

2:10:22

I'll do is delete it, reload the page,

2:10:25

and create a new project.

2:10:27

I'll call it live blocks live room and

2:10:31

create. Unfortunately, we still get the

2:10:34

same issue. Thankfully, the error in the

2:10:36

terminal gives us a bit more info. It

2:10:39

looks like we're missing the live blocks

2:10:40

keys. So, if you head over to your

2:10:42

project under API keys,

2:10:44

you'll be able to see your public key

2:10:46

and the secret key. So, go ahead and

2:10:48

copy them and add them to your

2:10:50

.env.local

2:10:52

where we can first specify the live

2:10:54

blocks public key as well as the live

2:10:58

blocks secret key.

2:11:00

With these two keys in place, head back

2:11:02

over to your application and reload. And

2:11:05

after connecting to room, you'll be able

2:11:07

to see the actual canvas. This is the

2:11:10

React Flow canvas underneath that you

2:11:12

can move through. And there's even a

2:11:14

little window that you'll be able to

2:11:16

scroll through to very quickly access

2:11:18

specific parts of the workspace. This is

2:11:20

looking absolutely amazing. We can even

2:11:23

expand it by collapsing the left

2:11:24

sidebar. Oh, and the right sidebar is

2:11:27

collapsible, too. So, now we have a real

2:11:30

fully functional React Flow canvas. But,

2:11:34

of course, what is the canvas for if we

2:11:36

can't add any elements on top of it? So,

2:11:38

let's create a little bottom navigation

2:11:41

that allows us to add some shapes that

2:11:43

are going to act as specific parts of

2:11:45

the diagrams within our application.

2:11:47

We'll do that by creating a new feature

2:11:50

spec called 12 shape panel.md.

2:11:55

You can add it right here. This one will

2:11:56

be simple.

2:11:58

And what this does is it adds a floating

2:12:01

toolbar at the bottom of the canvas

2:12:03

where users can drag shapes onto the

2:12:06

canvas to create notes. Rectangle,

2:12:09

diamond shapes, circles, pills,

2:12:11

cylinders, and hexagons for databases.

2:12:14

And after this, users will actually be

2:12:17

able to start creating the architectural

2:12:20

diagrams. We want to allow the users to

2:12:22

drag a shape, including the shape name

2:12:24

and the default size. Then, they can

2:12:27

also drag and drop it.

2:12:29

And on drop, we need to read the dragged

2:12:31

shape payload, convert it to the screen

2:12:34

position, create a new node at that

2:12:36

position, use an empty label, and the

2:12:39

default color. We then want to give each

2:12:41

one of these shapes a node ID with their

2:12:44

shape name, timestamp, and a counter.

2:12:47

And we want to render it.

2:12:49

So, this is an exciting one. So, let's

2:12:51

get it built. Read the file, update the

2:12:54

progress, and execute it exactly as

2:12:56

specified. I'm sure there's an easier

2:12:58

way for me to just to read the file and

2:13:01

not have to repeat this sentence every

2:13:03

now and then. I mean, I could have put

2:13:05

it at the top of the file and just share

2:13:06

the file itself. That would have worked.

2:13:09

Uh but yeah, I don't mind it. It's the

2:13:11

actual work that you put before that

2:13:14

saves you so much time later down the

2:13:16

line. This one took a bit longer, but

2:13:18

feature 12 is done. It created the

2:13:21

canvas node renderer and a floating pill

2:13:23

toolbar at the bottom with six draggable

2:13:26

shapes. So, if you head back over to

2:13:28

your room,

2:13:30

take a look at the bottom.

2:13:31

You have the rectangle, which you can

2:13:33

drag and drop. Hopefully, nope. As I

2:13:37

dropped it, it didn't appear on the

2:13:39

screen.

2:13:40

Let's try with a second one.

2:13:42

It looks like I can drag and drop them,

2:13:44

but they're not actually showing up on

2:13:46

the screen, which makes it a great

2:13:47

opportunity to debug it the proper way

2:13:50

by specifying in detail the current

2:13:52

issue that we're experiencing. So, I

2:13:55

took a second to write all of the issues

2:13:57

that I believe we currently have with

2:13:59

the application. At least that I have on

2:14:01

my version of the app. Your agent might

2:14:04

have done something different. So, let's

2:14:05

go through everything together first,

2:14:07

and then you'll be able to tell your

2:14:09

agent to fix some issues that you see on

2:14:11

your end as well. Let's think of this as

2:14:13

a little corrective prompt.

2:14:15

So, review the editor canvas

2:14:16

implementation and fix the visual

2:14:18

issues. The canvas currently looks like

2:14:21

it's floating above the background

2:14:23

inside a border box. Instead of feeling

2:14:25

like a real design canvas. And I want to

2:14:28

teach you how we can also add images to

2:14:29

the context. So, right here within the

2:14:32

context, you can create a new folder

2:14:34

called screenshots.

2:14:36

And then you can screenshot the entire

2:14:37

design and simply drag and drop it in.

2:14:42

Of course, I'll rename it to image as I

2:14:44

specified right here within the

2:14:46

document. That way you can also give

2:14:48

your agent some visual feedback.

2:14:51

Also, read the current canvas component

2:14:53

code in the components editor. And here

2:14:55

are a couple of issues to look for.

2:14:57

Obviously and first of all, the drag and

2:15:00

drop issues, right? The canvas nodes

2:15:02

from the node panel cannot be dragged

2:15:05

and dropped onto the canvas. We need to

2:15:06

investigate the full drag and drop

2:15:09

pipeline. We need to confirm that the

2:15:11

draggable nodes in the node panel have

2:15:13

the correct draggable attribute, that

2:15:15

the canvas has the on drag over handler

2:15:19

and on drop allowing us to drop them.

2:15:21

That the drop handler reads the node

2:15:23

type from data transfer, calculates the

2:15:26

coordinates, and then actually creates a

2:15:28

new node at the drop position.

2:15:30

And while we're fixing the drag and drop

2:15:32

issues, I also thought we can fix some

2:15:34

visual inconsistencies such as the way

2:15:37

that the canvas and the left and the

2:15:39

right sidebar are positioned. So, the

2:15:41

left and the right sidebar should float

2:15:42

over the canvas, not push or shrink it.

2:15:45

Sidebars must use the fixed position or

2:15:48

absolute with a higher Z index and so

2:15:50

on. So, after documenting all issues, we

2:15:53

want to fix all of the above so that it

2:15:55

basically corrects what we specified.

2:15:58

So, let's actually open this up right

2:16:00

here. We can tell it to read this file

2:16:03

and fix all of the listed issues and

2:16:06

then run it.

2:16:08

So, we've done three things. First, we

2:16:11

gave Claude code some file references up

2:16:14

front and the screenshots right here.

2:16:16

This stops it from wandering around the

2:16:18

entire project trying to figure out

2:16:20

where things live.

2:16:21

We're pointing it exactly where to look.

2:16:23

We're listing every issue one by one and

2:16:26

mixing some technical hints alongside

2:16:28

them.

2:16:29

These technical details act as

2:16:31

shortcuts. They give the AI the stronger

2:16:34

starting point so it can find and fix

2:16:36

the root cause faster instead of

2:16:38

guessing. And third, at the bottom, we

2:16:40

describe exactly what success looks

2:16:42

like. The AI now knows not just what's

2:16:45

broken, but what done looks like. So,

2:16:48

let's see if we can manage to correct

2:16:50

itself and fix it. Quickly after reading

2:16:52

through some of these files, it has a

2:16:54

full picture of all the issues, so it's

2:16:57

going to use the to-do right to track

2:16:59

the fixes and then implement them. Fix

2:17:02

the canvas layout was the first thing,

2:17:04

removing the card styling and making it

2:17:06

a fill full viewport, fixing the left

2:17:09

sidebar not fully hiding when closed,

2:17:11

fixing the dotted canvas background

2:17:13

visibility, and finally and most

2:17:14

importantly, verifying the drag-and-drop

2:17:17

pipeline. And see how well we did. Uh

2:17:20

it's even thanking us,

2:17:21

uh specifying that the core issue is

2:17:23

clear from the screenshot of the code.

2:17:26

The canvas is boxed inside a padded main

2:17:29

with card styling and the layout shrinks

2:17:32

the canvas to accommodate sidebars

2:17:34

instead of floating over them. So, it's

2:17:36

going to fix it very easily.

2:17:38

Perfect. Hopefully, this video is

2:17:40

teaching you how you can approach

2:17:42

developing apps with AI. You are the

2:17:45

architect and agent is just a coder. And

2:17:49

there we go. All four files have been

2:17:51

updated, so let's test it out.

2:17:54

Back into the browser, I will reload.

2:17:57

You can see that now the canvas spans

2:17:59

across left and right and the left and

2:18:02

right sidebar appear to be floating on

2:18:04

top of it. So, if you want to hide it,

2:18:06

it just gets completely hidden away,

2:18:08

which is great. And the right sidebar

2:18:11

also gets completely hidden away, so we

2:18:14

have a complete canvas. This is a big

2:18:16

difference from what we had before,

2:18:18

because now if you hide the sidebars,

2:18:20

you have so much more space to work

2:18:22

with. And what happens if we try to drag

2:18:24

and drop an element?

2:18:26

It looks like that functionality is

2:18:28

still not working properly. And I think

2:18:30

that's because Claude focused mostly on

2:18:32

cosmetic changes and the layout. I don't

2:18:35

see too many mentions of the drag and

2:18:38

drop functionality being fixed. So, what

2:18:40

we can do now is head over into the

2:18:42

current issues and specify that we want

2:18:45

to fix the drag and drop functionality.

2:18:50

So, we're going to remove everything

2:18:52

else besides the drag and drop and keep

2:18:55

the last part saying nodes can be

2:18:57

dragged from the node panel and dropped

2:18:59

onto the canvas as correctly. So, now we

2:19:01

can say read the current issues file

2:19:04

again and fix the remaining issue and

2:19:07

press enter.

2:19:09

This time, it's all about the drag and

2:19:11

drop. And after some thinking, it looks

2:19:14

like it found the root cause. The React

2:19:17

Flow viewport has pointer events none,

2:19:20

and React Flow internally uses D3 zoom

2:19:23

or native pointer listeners that call

2:19:25

prevent default on pointer down. So,

2:19:27

when the shape panel is rendered inside

2:19:30

of the React Flow as a panel, those

2:19:32

pointer handlers interfere with the

2:19:34

browser's drag gesture recognition

2:19:36

before Dragster can fire. The standard

2:19:39

React Flow drag and drop pattern puts

2:19:41

drag sources outside of the React Flow

2:19:44

and puts on drop or on drag over on a

2:19:46

wrapper div, not on the React Flow

2:19:48

component. So, it's going to fix this

2:19:50

issue for both the canvas editor and the

2:19:52

shape panel. That's it. Note that there

2:19:55

is the fix is clean. We specified what

2:19:57

was wrong, and the fix was to move the

2:20:00

on drag over and on drop from the React

2:20:03

Flow component to the outer wrapper. And

2:20:05

also to replace the panel position from

2:20:07

bottom center, which is React internal

2:20:09

panel, with a plain absolutely

2:20:11

positioned div.

2:20:12

So, let's test it out. Back on localhost

2:20:15

3000 within a specific project, I'll

2:20:17

drag and drop this rectangle, and it

2:20:20

still doesn't do it. So, what I'm

2:20:22

thinking is maybe we can make use of

2:20:25

that Liveblocks agent skills that we

2:20:27

installed earlier.

2:20:28

I mean, this is the exact situation

2:20:30

they're built for. So, in the same

2:20:31

context window, let's simply tell the

2:20:34

agent to check the Liveblocks best

2:20:37

practices and fix the drag and drop

2:20:39

flow. Dragging shapes from the shape

2:20:41

panel should create new nodes on the

2:20:43

canvas. But, it's still not working.

2:20:46

Nodes should be draggable from the panel

2:20:49

and dropped onto the canvas correctly.

2:20:51

So, by telling it to check the

2:20:52

Liveblocks best practices, I'm hoping

2:20:54

that it's going to consult the

2:20:56

Liveblocks agent skill. So, let's give

2:20:58

it a shot, and you can see that that is

2:20:59

the first thing that it did. Liveblocks

2:21:01

best practices. After analyzing it, it

2:21:04

came back with the complete picture with

2:21:06

three things that are wrong.

2:21:08

The storage type declares top-level

2:21:10

nodes and edges, but use Liveblocks flow

2:21:13

actually stores everything under a

2:21:15

nested flow key. Within the canvas room,

2:21:17

the initial storage creates top-level

2:21:19

nodes and edges, once again, wrong

2:21:21

schema. And then the canvas editor add

2:21:23

node mutation calls storage. get nodes,

2:21:27

which writes to the wrong storage path.

2:21:30

Instead, it should use on nodes change

2:21:32

with a type of add and then the item

2:21:34

we're adding.

2:21:35

So, let's wait until it fixes all three,

2:21:38

and it says clean. So, let's actually

2:21:40

test it out. Back within the browser,

2:21:42

I'll reload the page, and I'll try drag

2:21:45

and dropping a node once again.

2:21:47

And you can see that this time we

2:21:49

actually get a a

2:21:50

Now, if you try drag and dropping

2:21:52

another shape, you'll see that it'll

2:21:54

just give you another rectangle. And yet

2:21:56

another rectangle of a different size.

2:21:59

You're only getting rectangles, and

2:22:01

that's okay for now. Because in the next

2:22:04

lessons, we're going to turn these

2:22:05

rectangles into different shapes. We'll

2:22:08

basically make it like a real

2:22:09

architecture canvas where you can

2:22:11

connect different nodes and give them

2:22:13

titles and make them make sense. But

2:22:16

thankfully, we fixed this shape drag and

2:22:18

dropping feature, which is one of the

2:22:19

bigger features in the app. And while

2:22:21

fixing it, I wanted to keep the full

2:22:23

process of me doing that. So, you're not

2:22:25

just watching me copy and paste from the

2:22:27

other screen.

2:22:28

We're actually debugging this together.

2:22:31

And in this case, the solution was to

2:22:33

invoke an agent skill. So, whenever

2:22:35

you're working with specific tools,

2:22:37

always verify that they also have their

2:22:39

agent skills, install them, and ask your

2:22:42

agent to use them.

2:22:44

Okay, great. So, let's actually get

2:22:46

these changes pushed by saying get add

2:22:48

dot, get commit {dash} m,

2:22:50

implement drag and drop canvas

2:22:54

functionality,

2:22:55

and then run get push. And I'll actually

2:22:57

open up a PR for this and get it merged

2:23:00

so that in the next lesson, we can focus

2:23:02

on adding more canvas features. For

2:23:05

these, we already reviewed most of the

2:23:07

changes before.

2:23:08

So, I won't be reviewing them again. And

2:23:11

we have to resolve the conflicts right

2:23:13

here within our current issues. And

2:23:15

there's a fix with Copilot thing right

2:23:16

here, resolve the merge conflicts in

2:23:18

this pull request by clearing the

2:23:22

current issues.md file.

2:23:25

We basically want to make it empty as we

2:23:27

said. We don't need to push whatever is

2:23:29

in the current issues file. This is also

2:23:31

a new feature by GitHub where the

2:23:33

Copilot can automatically fix it on our

2:23:35

behalf.

2:23:37

And once it does, we'll be able to merge

2:23:39

it back to the main branch and continue

2:23:41

developing. And there we go. It

2:23:42

basically cleared out the file, and

2:23:44

we're good to merge it. So now, back

2:23:46

within the editor, you can just run git

2:23:48

pull to pull the latest changes and

2:23:50

we're ready to continue with the next

2:23:52

feature.

2:23:55

In this lesson, the goal is to make the

2:23:57

canvas feel like a real product. So,

2:24:01

open up your feature specs and go ahead

2:24:04

and create another one that's going to

2:24:06

be 13 node shape.md.

2:24:09

In this lesson, we're going to actually

2:24:11

polish and add six features. Each one

2:24:14

will have a distinct layer of

2:24:16

interaction. So, that by the end, the

2:24:18

canvas will have proper shape rendering,

2:24:21

node colors, edge behavior, and keyboard

2:24:23

shortcuts. Everything that makes a

2:24:25

difference between a working prototype

2:24:27

and something that genuinely feels

2:24:29

polished to use. So, starting with the

2:24:32

first one, it's going to be the node

2:24:34

shape. It's a fairly simple one where we

2:24:36

just want to replace the placeholder

2:24:38

node renderer with proper shape

2:24:40

rendering and a drag preview.

2:24:43

So, instead of a placeholder node shape,

2:24:45

which is just a rectangle, we want to

2:24:47

render all of these different types of

2:24:48

shapes through SVGs.

2:24:51

They should scale with the node size and

2:24:53

we want to keep the border subtle. We

2:24:54

also want to add a shape drag preview

2:24:57

while we're dragging.

2:24:59

We want to keep the node rendering

2:25:00

connected to the existing collaborative

2:25:02

canvas state.

2:25:03

We don't want to rebuild anything.

2:25:06

And when we're done, we want to check

2:25:08

that the node renderer renders the

2:25:10

correct shape variant for each type.

2:25:13

So, you know the drill. Open up your

2:25:15

agent and let's ask it to read this

2:25:18

file, update the progress, and implement

2:25:20

it as specified. Let's give it some time

2:25:23

and I'll be right back. And a couple of

2:25:25

minutes after, feature 13 is done. It

2:25:28

replaced the single style placeholder

2:25:30

with a shape aware rendering and also

2:25:33

added the cursor tracking ghost preview.

2:25:35

I'll show you what that means quickly.

2:25:38

But, basically now, you can see that our

2:25:40

array of rectangles that we created

2:25:42

before is now a bunch of different

2:25:45

shapes. And we can actually move across

2:25:47

the canvas and drag and drop different

2:25:49

shapes. There's a mini map at the bottom

2:25:50

as well, which we can later on remove as

2:25:53

we don't need it as we'll be mostly

2:25:54

working within a single centralized

2:25:56

place. But yeah, we now have the shapes

2:25:59

and you can actually connect the shapes

2:26:01

together. But we still can't change the

2:26:03

size of the nodes, right?

2:26:05

They are always of the same size and we

2:26:07

can't edit their labels. Like currently

2:26:09

these are just shapes. They don't have

2:26:11

any data associated with them.

2:26:14

So what we can do is create another file

2:26:17

right here, another feature

2:26:19

14 node editing.md.

2:26:23

And within it, we essentially want to

2:26:25

add resizing and inline label editing to

2:26:28

the canvas. So that when we select a

2:26:30

specific node, we can resize it and

2:26:32

scale them however we need to. And if we

2:26:34

double click it, we'll be able to edit

2:26:37

its label. So this is the next feature

2:26:39

that we're going to develop. I'll tell

2:26:40

it to read the file, update the

2:26:43

progress,

2:26:45

and implement it exactly as specified.

2:26:49

And you know what? I will actually copy

2:26:51

this part right here. So in the future I

2:26:53

can just paste it into the prompt.

2:26:56

Okay, and after some wearing and

2:26:59

cogitating and thinking, let's see what

2:27:02

it'll actually come up with. And as it's

2:27:04

close to being done after running a

2:27:06

couple of minutes, um near the end, uh

2:27:09

when it tries to build and check for

2:27:11

errors, it almost always finds some type

2:27:15

issues. Like right here, it was

2:27:17

complaining about the canvas node. So it

2:27:20

expanded the types and made it a bit

2:27:22

more type safe. And only once that is

2:27:24

done, it actually updates the progress

2:27:26

tracker and then finishes with the

2:27:28

feature. So it's always great to ask it

2:27:30

to test the application and test the

2:27:32

output.

2:27:33

Okay, great. So the The

2:27:35

from XY flow now renders when a node is

2:27:38

selected and we can resize dimension

2:27:40

changes through live boxes on node

2:27:42

change automatically. Also,

2:27:44

double-clicking any node opens up a text

2:27:47

area.

2:27:48

So, let's go ahead and test it out. We

2:27:50

now have these labels, which is perfect.

2:27:53

But first, let's test the resizing. If I

2:27:56

now pull it at any of the borders

2:27:59

or even at any of the edges, you can see

2:28:02

that this is very precise and would take

2:28:05

us some time to implement properly. You

2:28:06

can now resize it both horizontally and

2:28:09

vertically or if you want to keep the

2:28:11

aspect ratio, you can resize it by the

2:28:13

edges. This is absolutely amazing.

2:28:17

And let's also test changing the label

2:28:19

by double-clicking right here and typing

2:28:21

something like start

2:28:23

of the app

2:28:25

and leaving it and you can see that it

2:28:27

looks good. It quickly brings the text

2:28:29

up in case you want to type more stuff

2:28:31

into it.

2:28:32

But

2:28:33

but we can fix that later on because the

2:28:35

text will mostly always stay centered.

2:28:38

So, if we pull some kind of a label, we

2:28:40

can now mark this as a post grass DB,

2:28:43

for example, and it's going to fall down

2:28:46

right here and we can now connect the

2:28:48

application that way. Perfect. Let's

2:28:50

just quickly tell it that even though

2:28:52

while we're typing into the field, we

2:28:54

want the text to stay in the middle.

2:28:56

Right now, it jumps to the start. So,

2:28:59

I'll take a quick screenshot while I am

2:29:01

typing

2:29:03

and drag and drop it into the chat and

2:29:05

tell it while the label editing is

2:29:07

focused, the text appears at the top of

2:29:10

the element instead of in the middle

2:29:12

where it falls back to when not focused

2:29:15

any longer.

2:29:16

Make it so that when we're typing, we're

2:29:18

also typing within the middle of the

2:29:20

element. Hopefully, this should be clear

2:29:22

enough for a little quick corrective fix

2:29:25

and very quickly, the build passes. It

2:29:27

just swapped the text area for the

2:29:29

content editable div. So, now if we head

2:29:32

back right here and start typing, you

2:29:34

can see that it remains in the middle

2:29:36

and you can say start, here we can say

2:29:40

label and as soon as we start typing, it

2:29:43

basically centers itself right in the

2:29:46

middle.

2:29:47

Wonderful. So, now that we can actually

2:29:49

type within these inputs, what do you

2:29:51

say that we implement the functionality

2:29:53

to also be able to differentiate them by

2:29:56

color? So, create a new feature spec

2:29:58

called 15 nodes color toolbar.md

2:30:03

and here we want to add a floating color

2:30:05

toolbar that appears when a node is

2:30:08

selected. We then want to allow the user

2:30:10

to pick a color and both the node

2:30:13

background and text color will update

2:30:15

instantly synced across all

2:30:17

collaborators in real-time. So, it'll

2:30:20

check our UI context for the node color

2:30:22

palette, include a background color as

2:30:25

well as a matching text color so that

2:30:27

the contrast is nice

2:30:28

and we want to reuse existing colors if

2:30:31

they exist in globals, otherwise it can

2:30:33

keep the palette in the canvas types.

2:30:35

Then we want to add a toolbar

2:30:38

above the selected node that'll only

2:30:40

show when the node is selected.

2:30:43

And when a color is selected, we want to

2:30:45

update the node background color and

2:30:47

text color. I think this is pretty

2:30:49

understandable.

2:30:51

So, let's actually build it by telling

2:30:53

it our usual secret sentence which is

2:30:56

super simple, but again, the spec file

2:30:58

does the heavy lifting. This one for

2:31:00

some reason was super quick. It added

2:31:03

the text color, and the ability to

2:31:05

choose between a predefined set of

2:31:07

colors and also the color background.

2:31:10

So, if we head back over here and select

2:31:12

an element, would you look at that? I

2:31:14

mean, this is really starting to come

2:31:15

into life and I'm not sure how much time

2:31:18

this would take me to manually code it

2:31:20

out. But this way, we just have to think

2:31:23

of an idea, write a fairly simple yet

2:31:26

descriptive uh spec file, and then it

2:31:29

does it instantly. And

2:31:32

take a look.

2:31:33

You can actually select the colors of

2:31:35

each one of the nodes

2:31:38

separately.

2:31:39

So, if I make this one purple, the

2:31:41

database, for example, can be green.

2:31:44

Uh yep, for example, this one can be

2:31:46

MongoDB in its classic green color.

2:31:49

Then, we can have some kind of an

2:31:50

algorithm right here going on, which can

2:31:53

be orange. And yeah, it is all working

2:31:56

wonderfully. So, on top of the colors,

2:31:59

what else is there to implement?

2:32:00

Currently, there's these custom edges

2:32:02

from all four sides. But no matter which

2:32:05

you select, it's always going to start

2:32:06

making a connection from the bottom, as

2:32:08

you can see right here.

2:32:09

And it'll connect it to the bottom or

2:32:11

the top as well. So, we want to fix that

2:32:13

edge behavior.

2:32:15

So, I'll create a new spec just for

2:32:17

that, and you can see how I'm being very

2:32:19

specific with the specs, only focusing

2:32:22

on the smallest thing possible. So, 16

2:32:26

edge behavior

2:32:28

.md. And the goal of this spec is to

2:32:31

just replace the default canvas edges

2:32:33

with custom edges that feel easier to

2:32:36

follow, easier to click, and support

2:32:39

inline labels. So, we want to add

2:32:41

connection handles to every node at the

2:32:43

top, right, bottom, and left sides, and

2:32:45

user should be able to connect from any

2:32:47

handle to any other handle. Oh, and

2:32:50

another thing you can notice is that

2:32:52

we're opening up a new chat for every

2:32:55

single one of these specs. This helps us

2:32:57

with lowering the overall context window

2:33:00

and the amount of tokens that we're

2:33:02

spending for each transaction, and it

2:33:04

also makes the agent that much more

2:33:06

focused on the task at hand. And all

2:33:09

these little things together, like the

2:33:11

separate feature spec.md files instead

2:33:13

of just regular messages,

2:33:15

or the current issues file, where we can

2:33:17

specify what's wrong, and these six

2:33:20

files that altogether make the system

2:33:22

work, just makes it a breeze to work

2:33:24

with an AI, which is typically not the

2:33:26

case because it starts imagining things

2:33:29

and creating stuff that we don't want or

2:33:30

breaking other things. But this way, it

2:33:33

is super predictable. And if you take a

2:33:35

look at the progress tracker, which we

2:33:38

haven't looked at in a long time now,

2:33:40

you'll see that everything we've done so

2:33:42

far is written right here. But what I

2:33:45

care more about is that it even kept

2:33:48

some additional architectural decision

2:33:50

so that it knows what we're doing. And

2:33:52

the session notes for specifically what

2:33:54

it thought is very important. Like the

2:33:56

specific versions of different tools

2:33:58

that we're using, the fact that the

2:33:59

Prisma config uses Prisma forward slash

2:34:02

and not some other path, and that it

2:34:04

reads the database URL from the .env.

2:34:07

It kept everything it needs and

2:34:09

everything that future sessions with AI

2:34:11

or other developers continuing to

2:34:13

develop this project need to do it in a

2:34:16

sane and happy way. And another build

2:34:19

passes.

2:34:20

It implemented the canvas edge data

2:34:22

pieces, allowing us to connect the

2:34:24

source and the end.

2:34:27

So if you head back over here, you would

2:34:29

see that we have plenty of different

2:34:30

lines happening. But now, if you select

2:34:32

it from the left side, you can see how

2:34:34

it creates a bit of a different kind of

2:34:36

connection, and you can now connect it

2:34:38

to another element. This line looks a

2:34:40

bit different from other more

2:34:42

organic-looking lines. Oh, and right

2:34:44

now, we cannot even delete the elements.

2:34:46

So we have plenty of elements on the

2:34:48

screen.

2:34:49

But I'll create a new one right here and

2:34:51

another database on the right.

2:34:54

Drag [snorts] the canvas so we can see

2:34:56

them right here. Now the mini map starts

2:34:58

making a bit more sense, as you can see.

2:35:00

But if you try to connect it, you can

2:35:02

now do that in a bit of a more

2:35:04

consistent and better way.

2:35:06

Now that the canvas is looking so good,

2:35:08

I guess we can focus on little

2:35:10

quality-of-life fixes.

2:35:12

Like under feature specs, we're going to

2:35:14

do 17 canvas ergonomics.md.

2:35:18

And within it we can focus on adding a

2:35:20

little floating control bar for zooming

2:35:23

in and out and undoing or redoing and

2:35:26

then wiring those actions to keyboard as

2:35:28

well. So if you press plus or equal it's

2:35:30

going to zoom in minus to zoom out

2:35:33

command or control Z or Y to undo and

2:35:36

redo and I also don't like that mini map

2:35:39

at the bottom right. I don't think our

2:35:41

app will need it. So simply say remove

2:35:44

the mini map at the bottom right.

2:35:47

It's just a personal preference. But

2:35:49

yeah, this is that finishing layer that

2:35:51

makes the canvas feel like a tool that

2:35:53

somebody would actually use every day.

2:35:54

So let's implement this one as well and

2:35:57

very quickly feature 17 is done as well.

2:36:00

We now have a new pill shaped floating

2:36:02

bar at the bottom left for zooming in

2:36:05

and out or fitting within the view. So

2:36:07

if we collapse the sidebars, you'll be

2:36:09

able to see it right here at the bottom.

2:36:11

Ooh, it it actually looks very nice and

2:36:14

if I move some things around, you can

2:36:16

see that the left arrow or the undo

2:36:19

actually starts shining and you can undo

2:36:22

previous actions or redo them. You can

2:36:25

also use command or control Z or Y to

2:36:28

actually go back and forth, which is

2:36:30

absolutely amazing. There's this fit in

2:36:32

view, which is going to fit all the

2:36:34

elements within the view. So if you're

2:36:36

working on something that is a bit

2:36:37

closer together, it's going to actually

2:36:39

increase the view, which is always nice.

2:36:41

And you can zoom in or you can zoom out,

2:36:43

but that's hidden by the next JS logo,

2:36:46

but that's only in development.

2:36:48

So these little things really do make a

2:36:50

big difference. And finally, I had an

2:36:52

idea that isn't even necessary, but I

2:36:55

want to make it work.

2:36:57

So let's create a new feature spec 18

2:37:00

starter template.md.

2:37:03

The goal of this one is to create a

2:37:05

template library that lets users start

2:37:08

from a pre-built diagram instead of

2:37:11

blank canvases. So we can start with

2:37:13

some ideas, like let's say microservices

2:37:17

or a CICD pipeline or an event-driven

2:37:19

system. Each one will load directly into

2:37:22

the shared canvas. That's going to make

2:37:24

it easier for users to figure out how to

2:37:26

actually use the app and what are the

2:37:28

different use cases. Because maybe

2:37:30

somebody's not going to even get the

2:37:31

idea of what they can use this app for.

2:37:34

So, the templates should give them a

2:37:35

pretty good idea. This one took a bit

2:37:37

longer, but feature 18 is done. We now

2:37:40

have the starter templates file.

2:37:43

So, if you head over here and collapse

2:37:46

everything, maybe we should start with a

2:37:48

blank canvas, but we can keep doing it

2:37:51

within our mess right here. Uh at the

2:37:53

top right, there's the templates button.

2:37:56

And if you click it, you'll see a couple

2:37:58

of different starter templates, which

2:38:00

aren't looking that good right now. They

2:38:02

look a bit too vertical, but yeah, as

2:38:05

soon as you click import, it'll actually

2:38:07

import a lot of stuff pre-labeled. Uh

2:38:10

but it may be better to do that within a

2:38:12

new project, so we can see it a bit

2:38:13

better. So, I'll call it CICD

2:38:16

and create a new project.

2:38:19

And then we'll start from a template by

2:38:21

going with maybe the CICD pipeline or

2:38:24

let's go with microservices.

2:38:27

And you can take a look that that

2:38:29

immediately tells you a bit about how we

2:38:31

can start structuring the application

2:38:33

infrastructure. But yeah, this right now

2:38:35

is looking a bit too vertical. The

2:38:38

design is not actually being displayed.

2:38:40

So, we want to take this screenshot and

2:38:42

we want to feed it into the chat by

2:38:44

holding the shift key and then drag and

2:38:46

dropping. And then we want to compare

2:38:47

that with how it looks like on the

2:38:49

finished product.

2:38:50

Where we can clearly see the full

2:38:53

template before we drag and drop it. So,

2:38:55

I'll also put that here.

2:38:57

And I'll say

2:38:59

I'll pass in the screenshot of how the

2:39:02

templates look right now. They look a

2:39:04

bit too vertical and the actual elements

2:39:07

are not clearly visible. Whereas, on

2:39:10

another screenshot, which is the

2:39:11

finished product, you can see how each

2:39:13

card gets its own space, and they show

2:39:16

the entire diagram or the entire

2:39:18

template that's going to be entered in

2:39:20

once clicked. Make it look more similar

2:39:22

to that. So, once again, this is a

2:39:24

little corrective prompt with two

2:39:26

different screenshots, one before and

2:39:28

one after. Let's see how well it does.

2:39:31

So, currently, it looks like this. So,

2:39:33

I'm going to snap my fingers, and we

2:39:35

come down to something that looks like

2:39:37

this.

2:39:38

Definitely not good.

2:39:40

Even though now it's a bit better, as

2:39:42

it's showing the full template that's

2:39:43

going to be inserted in, but I'm not

2:39:46

quite sure why it cannot get the widths

2:39:49

right. So, we could be a bit more

2:39:50

specific and tell it to increase the

2:39:52

width of the whole import template card,

2:39:54

and then to increase the width of the

2:39:56

cards within it, so that they are the

2:39:58

same as this.

2:40:00

And that's what I'll tell it for the

2:40:01

second time

2:40:03

by saying, "Increase the width of the

2:40:05

entire import template overlay, and then

2:40:08

increase the width of the card within it

2:40:10

as well, so it looks the same as on the

2:40:12

final screenshot." And I'm purposefully

2:40:15

being not as descriptive right here as

2:40:17

we are within our spec files, because I

2:40:19

want to show you how

2:40:21

it can cost you a lot by not being

2:40:23

specific from the start. A lot of

2:40:25

back-and-forth messages, conversations,

2:40:27

and corrective fixes. And very soon, the

2:40:30

fix is in, and now it actually looks

2:40:32

amazing. It increased the width, and

2:40:34

it's much clearer what it's actually

2:40:36

going to insert in. So, that means that

2:40:37

we now also officially have the

2:40:40

templates. So, we implemented a lot of

2:40:43

stuff across these five separate feature

2:40:45

specs, 18 files changed in total, which

2:40:48

means a lot of stuff to review. This

2:40:49

actually deserves its own pull request.

2:40:51

So, let's go ahead and push the changes

2:40:53

either via terminal or via the source

2:40:55

control right here. It's going to

2:40:57

auto-craft a nice message, such as

2:40:59

improve node shape, added resizing and

2:41:01

inline labels, and adding color

2:41:04

toolbars.

2:41:05

Then, once you commit and sync, back on

2:41:08

GitHub, you'll see that the development

2:41:09

branch had recent pushes 7 seconds ago.

2:41:13

So, let's just open up a new pull

2:41:15

request, and let's wait Code Rabbit to

2:41:17

provide us with a review. I think this

2:41:19

walkthrough will be particularly useful,

2:41:21

as we've covered many different

2:41:22

features.

2:41:24

So, this PR implements a comprehensive

2:41:26

canvas editing feature set, including

2:41:28

custom edge rendering with inline

2:41:30

labels, node resizing with color

2:41:33

toolbars, keyboard shortcuts for zooming

2:41:35

and undoing and redoing, a canvas

2:41:37

control bar, and a starter template

2:41:40

system with a modal dialogue and

2:41:42

SVG-based previews. We've made changes

2:41:45

across 19 different files, and here is

2:41:48

the sequence diagram. The user clicks

2:41:50

the templates button. It opens the

2:41:52

editor navbar, and then we open up the

2:41:54

templates. We display the templates with

2:41:57

card previews. The user imports them,

2:42:00

and then once they do, we pass the

2:42:02

pending template prop. Then, we forward

2:42:05

that pending template to the canvas

2:42:07

editor. Live Blocks clears the existing

2:42:09

nodes or edges, adds the templates,

2:42:12

syncs this new graph to the canvas,

2:42:14

triggers the fit view automatically, and

2:42:17

then once it's imported, we bring it

2:42:19

back to the user.

2:42:20

This is a nice diagram on the starter

2:42:22

templates feature. This was a complex

2:42:24

PR, so I'm assuming there's going to be

2:42:27

a lot of different things we can fix.

2:42:29

The first one points out to exposing an

2:42:31

accessible name on these icon-only

2:42:34

buttons.

2:42:35

Because title is not a dependable

2:42:37

accessible label. It's telling us to add

2:42:40

the area label title for the zoom, fit,

2:42:43

undo, redo buttons for people who are

2:42:46

using some assistive technologies. So,

2:42:47

if I head back over here and hover over

2:42:50

them, it will tell me that this is an

2:42:52

undo. But, for people with screen

2:42:54

readers, this is not going to do it, and

2:42:56

they will have no idea what these

2:42:58

buttons are all about. I mean, it's rare

2:43:01

that these types of people would be

2:43:02

using this kind of an application

2:43:04

anyway, but it's always great to

2:43:06

implement anything you're doing with

2:43:08

best practices in mind. So, in this

2:43:09

case, CodeRabbit provided me with a

2:43:11

prompt for AI agents to use, so we can

2:43:14

add those area labels. After that,

2:43:15

there's a major potential issue on

2:43:17

avoiding double committing the edge

2:43:20

label on enter or escape. So, when

2:43:22

pressing enter or escape, it'll call the

2:43:25

commit edit immediately, then the

2:43:27

input's on blur handler will call it

2:43:29

again when the element loses focus. This

2:43:31

sends two mutations through live blocks,

2:43:33

creating duplicate undo retries for a

2:43:36

single edit. That's interesting. So, the

2:43:38

fix is simply to call the prevent

2:43:40

default and prevent blur instead of

2:43:43

commit edit. So, let's find that. I'll

2:43:45

find where this commit edit is getting

2:43:47

called.

2:43:48

It is right here in canvas edge

2:43:50

component. Specifically, we're looking

2:43:52

at it on the enter key or the escape

2:43:55

key. So, right here, and instead of

2:43:58

that, we want to apply those two lines,

2:44:01

prevent blur and

2:44:03

prevent default.

2:44:05

And we also want to remove the commit

2:44:07

edit from the dependency array. After

2:44:09

that, there's a minor issue to move ref

2:44:12

updates into a sync effect to comply

2:44:14

with React 19 ref values. So, basically,

2:44:17

it's telling us to put it within a use

2:44:19

effect. That's super simple. Let's just

2:44:21

find where in the code this is.

2:44:23

And after that, you can see that even

2:44:25

it's complaining right here about the

2:44:26

code, but we didn't take a look at it

2:44:28

because we weren't looking at the actual

2:44:30

code.

2:44:31

It's always good to use the best

2:44:33

practices. So, we just need a use effect

2:44:36

right here below that's going to put all

2:44:38

of these node refs into edges and nodes

2:44:41

like this.

2:44:42

And everybody's happy. After that, we're

2:44:45

missing the enter key for handling the

2:44:46

label commit. So, if it says save the

2:44:49

label on blur, enter, or escape, but on

2:44:53

handle key down, we only add escape. So,

2:44:55

right here, we also have to add the

2:44:58

enter key. I think we searched for this

2:45:00

across the code base just before. That

2:45:02

was right here in the canvas edge.

2:45:05

But, not this instance, the instance

2:45:07

where we just had the escape. So, I'll

2:45:09

search for escape.

2:45:11

And it's right here. We have to replace

2:45:13

it with if key is escape or if is key is

2:45:16

enter. And once again, for you, these

2:45:19

suggestions might be completely

2:45:20

different from mine. And that's normal

2:45:22

because agentic development is not

2:45:24

predictable. So, for me, it's adding

2:45:26

some accessibility names or guarding

2:45:28

some colors. For you, it's going to be

2:45:30

something else. But, I always love

2:45:32

learning about everything that Code

2:45:34

Rabbit throws at me because the next

2:45:35

time, I can immediately do it better. Or

2:45:37

in this case, we can make our feature

2:45:39

specs more specific so that our agents

2:45:42

don't make these mistakes in the first

2:45:44

place. Oh, and this one is interesting.

2:45:46

When trying to undo or redo, it's

2:45:48

currently looking only at a lower letter

2:45:50

Z, but in case somebody has caps lock

2:45:52

turned on, we also want it to take a

2:45:54

look at the upper case Z.

2:45:56

This is interesting. So, let's go ahead

2:45:58

and push those Code Rabbit suggested

2:46:00

fixes.

2:46:02

All of them were within the canvas.

2:46:06

And then, once they're up, we can simply

2:46:09

go ahead and merge this PR.

2:46:12

Amazing job on adding all of these

2:46:14

little improvements that make our canvas

2:46:17

that much more interactive.

2:46:21

In this chapter, we'll add the

2:46:23

collaborators avatar. So, when somebody

2:46:25

else is moving something on the screen,

2:46:27

you can see who is messing with you.

2:46:29

We'll also complete the chat sidebar UI

2:46:32

and implement a canvas auto save, which

2:46:35

will save our canvas data to a Vercel

2:46:37

blob. So, when you create something, you

2:46:40

don't lose it. And I'll break down all

2:46:42

of these within three separate feature

2:46:45

spec files. Each one is independent, but

2:46:47

together they complete the collaborative

2:46:50

experience before AI generation enters

2:46:53

the picture. So, back within our

2:46:54

application, create a new 19 presence

2:46:59

avatars cursors.md

2:47:02

chat. The goal of this spec is to

2:47:04

implement presence avatars and cursors.

2:47:08

Because right now, multiple users can

2:47:10

edit the same canvas, but they can't see

2:47:13

each other.

2:47:14

And after this, every connected

2:47:16

collaborator will appear as a live

2:47:19

cursor directly on the canvas with their

2:47:22

name and color. So, we want to render

2:47:25

those collaborator avatars and add those

2:47:27

cursors to the canvas. And also, on the

2:47:30

top right, we can show a stacked group

2:47:33

of avatars where we can see everyone in

2:47:35

the room at one place. So, you know the

2:47:38

drill. Let's get it implemented. And the

2:47:40

presence feature has now been

2:47:42

implemented. There's a new component

2:47:44

rendered inside of the canvas wrapper

2:47:46

called the presence cursor as well as

2:47:49

the collaborators avatars. So, if you

2:47:51

head back over to a specific project, we

2:47:53

can stay within the CICD, open up this

2:47:56

project within a second tab. And check

2:47:58

this out. Right here on top, you should

2:48:00

see that Adrian is also within this

2:48:02

canvas. But in this case, both of us are

2:48:04

Adrians. So, you can see another

2:48:06

person's cursor. And if I put these tabs

2:48:09

side by side, take a look at this.

2:48:12

Using LiveBlocks, while one user is

2:48:15

doing something, all the other users in

2:48:17

the room can see exactly what they're

2:48:19

doing. So, this is a great first step

2:48:22

toward interactivity, but we can do

2:48:24

more.

2:48:25

Head over and create a new feature spec,

2:48:28

or just get it from the zip, called 20

2:48:30

AI sidebar.md.

2:48:34

The goal of this one is to create a

2:48:37

sidebar placeholder that'll later on get

2:48:39

replaced with a proper UI.

2:48:42

Two tabs, the AI architect and the

2:48:45

specs. The AI architect tab will have a

2:48:48

scrollable chat area, starter prompt

2:48:50

chips, message bubbles, and a composer

2:48:53

input. While the specs tab will have a

2:48:56

generate button and a placeholder spec

2:48:58

card.

2:49:00

No back-end logic yet, just the UI that

2:49:02

everything else will connect to.

2:49:04

So, let's run it and let's see how it

2:49:07

does. And after a couple of minutes, it

2:49:09

is done. In this case, it touched only

2:49:12

the AI sidebar, which is a new

2:49:14

standalone component, as well as the

2:49:16

editor workspace client, where it

2:49:18

replaced the old aside with this new AI

2:49:21

sidebar. So, now we got a new sidebar

2:49:24

that looks something like this. You can

2:49:26

see it on the right side. There's the AI

2:49:29

architect, which soon enough will allow

2:49:31

you to design full workflows and systems

2:49:34

within the canvas, which looks like a

2:49:36

chat because you're speaking with an

2:49:38

agent. And then there's a specs tab,

2:49:41

which allows you to generate a specific

2:49:43

specification and then later on download

2:49:45

it. This is just the UI for now, but

2:49:47

soon enough this will become real. You

2:49:49

can also type multiple messages by

2:49:51

pressing shift and then enter, which is

2:49:54

useful for those longer prompt. Oh, and

2:49:56

before we run the next spec, which is

2:49:58

all about auto-saving what we're

2:50:00

currently working on,

2:50:01

we need to set up Vercel blob storage.

2:50:04

Vercel blob is basically an object

2:50:06

storage service. Think of it like a

2:50:09

cloud drive for your app. So, instead of

2:50:11

storing large files directly in your

2:50:13

database, you upload them to the blob

2:50:16

and store only the URL reference in

2:50:18

Prisma.

2:50:19

We're using it for two different things

2:50:21

in Ghost AI.

2:50:22

Canvas snapshots as saved JSON and

2:50:25

generated specs that will be available

2:50:27

for download later on through markdown.

2:50:30

It's completely free to get started

2:50:32

with. You can just head over to

2:50:33

vercel.com. Then on the left side, click

2:50:36

storage and create a new database.

2:50:38

Choose blob for store name, enter

2:50:41

something like ghost AI, and we'll set

2:50:43

it to private because private storage

2:50:45

means that your blob URLs require a

2:50:48

token to access.

2:50:49

Without this, anyone who gets a hold of

2:50:52

your blob URL can read the file

2:50:53

directly, which means the canvas data

2:50:56

and generated specs would be publicly

2:50:58

readable. But private storage keeps

2:51:00

everything scoped to your application

2:51:02

only.

2:51:04

So, create it. And once it is created,

2:51:07

click on it, and then right here you'll

2:51:09

be able to find your .env.local, where

2:51:11

you can just copy the snippet. Then

2:51:13

within your .env.local,

2:51:15

you can simply add this new .env, blob

2:51:18

read write token, and we are ready to

2:51:21

execute our next feature spec

2:51:23

by adding a file called

2:51:25

21_canvas_autosave.md.

2:51:30

Before we add that AI generation, canvas

2:51:33

state needs to persist.

2:51:35

So, after this, every change to the

2:51:37

canvas is automatically saved to Vercel

2:51:39

blob, which we just set up. And then

2:51:41

we'll save the blob URL, store it in

2:51:43

Prisma, and our canvas data will be

2:51:46

saved securely. So, this spec is all

2:51:48

about saving the current canvas state.

2:51:51

It should be pretty simple for our agent

2:51:53

to implement, given that we gave it all

2:51:55

the information about the Vercel blob

2:51:57

and about the environment variable that

2:51:59

it can use to save data. And after a

2:52:01

couple of minutes, it is done. A Vercel

2:52:04

blob is installed, and blob read write

2:52:07

token is already present in the

2:52:09

.env.local.

2:52:10

But if we get back to the editor, at

2:52:12

least on my end, there seems to be a

2:52:14

bug. So, let me show you how I would

2:52:16

approach fixing it. To fix this issue,

2:52:19

you can just copy it, head back over to

2:52:21

our application,

2:52:23

specifically into the current issues.md,

2:52:27

and specify that this is the current

2:52:31

error that I'm seeing on the screen. And

2:52:35

you can paste it right here. It's going

2:52:36

to give it all the necessary

2:52:37

information, and you can say

2:52:40

fix it.

2:52:42

So, make sure that it has access to the

2:52:44

current issues, and say

2:52:46

fix it. Or you could have just pasted

2:52:48

that into the chat itself.

2:52:50

For just a singular issue like this, it

2:52:52

should be totally okay. And as it's

2:52:54

working through it, I can already see

2:52:55

what was the issue. Even though we ran

2:52:57

MPX Prisma generate and migrate, the

2:53:00

generated client is correct, but there's

2:53:03

a stale .next cache bundling the old

2:53:06

schema. So, it's going to clear the old

2:53:08

cache and then delete it, which forces

2:53:10

Turbopack to recompile from a freshly

2:53:13

generated Prisma client, which already

2:53:15

has the canvas blob URL. Then, we just

2:53:17

have to restart our server, and the

2:53:19

error will be gone. So, open up your

2:53:21

terminal and just to rerun it by saying

2:53:23

npm run dev. And then back on

2:53:25

localhost:3000, if you reload, the error

2:53:28

will be gone. So, now we are ready to

2:53:30

test that final feature we implemented.

2:53:33

Head over to one of the active projects,

2:53:35

add a couple of nodes right here,

2:53:37

refresh the page, and make sure it

2:53:39

remains at the exact space where you

2:53:40

left it at.

2:53:41

So, I'll try to change its name and move

2:53:44

it below the auth service, and just make

2:53:46

sure that it actually remains there,

2:53:47

which would mean that the auto save is

2:53:49

working.

2:53:51

Perfect. But even though it saved it, it

2:53:53

wasn't saved through a blob, because if

2:53:55

you head over to the Ghost AI blob

2:53:57

storage, you'll see that there is

2:53:59

absolutely nothing there. So, let's fix

2:54:01

this issue and uh couple others.

2:54:04

I went through the application, tested

2:54:06

it deeply, and came up with a couple of

2:54:08

different issues that I added in the

2:54:10

current issues.md file. You should

2:54:13

already have a file like this in your

2:54:15

zip, which you can just refer to. But

2:54:17

again, it's possible that for you,

2:54:19

you'll have to go through the app

2:54:20

yourself, try to find some specific

2:54:22

issues within your app, and then write

2:54:24

the prompt to fix them.

2:54:26

But yeah, in this case, let's go over

2:54:28

the most important one, which is the

2:54:30

saving button in the workspace navbar.

2:54:33

Uh the workspace navbar is missing a

2:54:35

save button. The auto save hook exists,

2:54:38

but we need to wire the button to it. It

2:54:41

should default to save. While saving, it

2:54:43

should switch over to saving. After

2:54:45

successful save, it should switch over

2:54:47

to save. And that's it. The most

2:54:50

important thing that I noticed right

2:54:51

here is in this canvas route.ts

2:54:55

file. If you take a look at the blob

2:54:58

storage, the access is set to public.

2:55:01

And remember what I said before, it has

2:55:03

to be set to private because we don't

2:55:05

want other people to be able to view the

2:55:07

changes we make within the canvas. So,

2:55:09

we need to set it to private. And we're

2:55:11

not asking it to change anything else

2:55:13

for now. For now, I just want to focus

2:55:15

on issue one and then later on go

2:55:17

through all of the other issues. So,

2:55:19

yeah, let's ask it to fix the save

2:55:21

button first. I'll open up a new chat

2:55:24

and tell it to read the current issue.md

2:55:27

file, check the issue one and fix it.

2:55:29

After fixing it, mark it as pending to

2:55:31

test. Let's run it and see how well it

2:55:34

does. And very quickly, issue one is

2:55:36

fully implemented and marked as pending

2:55:39

for tests. So, let's go ahead and test

2:55:41

it out. You can see the save button

2:55:43

right here. And as you move something on

2:55:45

the screen and change its color and

2:55:47

click save, you can see that the saving

2:55:50

indicator is not really changing. So,

2:55:52

you can quickly head over to the code

2:55:53

base, see there's no errors in the

2:55:55

terminal, but it's still not doing its

2:55:57

thing. So, let's send it one quick

2:55:59

corrective prompt by telling it that

2:56:02

issue one still hasn't been fully fixed.

2:56:05

The saving status indicator is not being

2:56:07

changed, and when I click the save

2:56:09

button, nothing is changing either.

2:56:11

Analyze the issue further and fix it.

2:56:14

And we can send it out. And it was doing

2:56:16

a bit of thinking, but it came back with

2:56:18

the response that the root cause was

2:56:20

React strict mode, Enabled by default in

2:56:23

next js app router. It double invokes

2:56:26

effects in development. So on initial

2:56:28

mount, the cleanup is intentionally

2:56:30

fired by the strict mode before remount,

2:56:33

and then remount effect only registers

2:56:35

the cleanup again, but it never resets.

2:56:38

So that means that the is mounted is

2:56:40

permanently set to false in development.

2:56:42

So the save exited every time. This fix

2:56:45

removes the is mounted ref entirely. Uh

2:56:48

because react 18 made this guard

2:56:50

unnecessary. Perfect. So if we head back

2:56:52

right here and reload, and if you move

2:56:55

something around, and then press save at

2:56:58

the top right,

2:57:00

you can see that it says saving, and

2:57:02

then we get an error. Save failed.

2:57:05

If I move it again a couple of times and

2:57:07

click save again,

2:57:08

it says save failed.

2:57:11

It's funny because when I tested it

2:57:12

initially, it actually said saving and

2:57:15

then saved. So back in the blob storage,

2:57:18

you can see that I have this new canvas,

2:57:20

and it actually stored the JSON data.

2:57:22

But then when I tried doing it again, it

2:57:25

failed. So if you open up the terminal,

2:57:27

you'll see that we have an error with

2:57:29

the vercel blob saying that the blob

2:57:31

already exists, so we should add allow

2:57:34

override is set to true if we want to

2:57:37

override it. So what we can do is simply

2:57:39

find this file. That's the canvas root.

2:57:42

So you can go over to canvas root, and

2:57:46

just add an additional prop of allow

2:57:49

override is set to true. This single

2:57:51

line should immediately allow us to save

2:57:55

every other time,

2:57:57

not just the first time. You can see it

2:58:00

says saved, and back when I did the

2:58:01

blob, you can see that it got modified

2:58:04

less than a minute ago, which means that

2:58:06

it is consistently saving data. And

2:58:08

right now the size of this JSON file is

2:58:11

6.11 kilobytes, but if we head back and

2:58:13

add a couple more labels and models and

2:58:17

shapes and some pieces of text within

2:58:19

them and then save again and then head

2:58:22

to the blob. You can see that now the

2:58:24

size increased, which means that it is

2:58:26

properly saving all the data. So, now

2:58:29

that our issue one has been fixed right

2:58:31

here,

2:58:32

uh we can ask it to continue fixing the

2:58:34

other issues.

2:58:35

Uh specifically, some issues that are

2:58:37

found are about deleting nodes and

2:58:40

edges. Uh so, right now we don't have a

2:58:43

way to delete anything. So, if I try

2:58:46

pressing backspace or the delete keys or

2:58:48

anything, I can't do it. So, the

2:58:51

solution is simple. We just need to add

2:58:53

a key down event listener to the canvas

2:58:56

that listens for the delete and

2:58:57

backspace keys, doesn't fire when the

2:59:00

event target is an input or text area,

2:59:02

but just gets fired when we're selecting

2:59:05

specific nodes, and then it deletes

2:59:07

them.

2:59:08

As the third issue, uh currently, we can

2:59:10

also just connect from top to the top of

2:59:15

the handle. You can see, even if I drag

2:59:17

from the right handle, it connects from

2:59:19

top to top. We want to be able to drag

2:59:22

and drop from any side of the element to

2:59:25

any other side. That will allow us to

2:59:27

make these graphics so much more

2:59:29

customizable.

2:59:30

So, we just want to allow it to be

2:59:32

connected from top, right, bottom, and

2:59:35

left.

2:59:36

Then, as the fourth issue, I noticed

2:59:38

that when I drag and drop specific

2:59:40

elements, they drop a bit lower than

2:59:43

where they're supposed to. So, I just

2:59:45

wanted to fix that offset.

2:59:48

And notice when I dragged and dropped

2:59:50

it, it actually said saving at the

2:59:52

bottom left right here and then saved,

2:59:54

which means that the auto save

2:59:55

functionality is working. And then, the

2:59:58

fifth issue is the auto zoom on first

3:00:01

node. We want to read the Liveblocks

3:00:03

agent skills before implementing it, and

3:00:05

then as soon as we drop it, we want to

3:00:06

zoom it in. And for six and seven, we

3:00:09

want to check the clerk skills, um and

3:00:11

we have some issues with the

3:00:12

collaborator avatar images. If the image

3:00:15

is coming from Clerk, we want to add it

3:00:17

to next config, so it gets properly

3:00:19

shown. And also, currently, we have two

3:00:23

different Clerk buttons. We just want to

3:00:25

keep the one and remove the other one.

3:00:27

So, let's ask it to fix the errors from

3:00:30

two all the way to seven. Okay, now that

3:00:33

the issue one in the current issues.md

3:00:36

file has been fixed, uh analyze and fix

3:00:39

issues from two to seven. Press enter.

3:00:43

And let's see how well it does. Okay, it

3:00:45

looks like all the remaining issues have

3:00:48

been implemented, and uh it marked them

3:00:51

for pending test, uh which is a good

3:00:53

thing for us to test right now. We have

3:00:55

to test deleting the nodes and edges,

3:00:58

um handling uh the positions or the

3:01:01

lines in between the elements, drop

3:01:04

position offset, auto zooming, and then

3:01:06

Clerk images.

3:01:08

So, if I head back right here and reload

3:01:11

the page, let's first test the drag and

3:01:13

drop. So, if I try to drag and drop it

3:01:15

here, you can see that it gets placed at

3:01:17

the exact position. This is pretty

3:01:19

crazy.

3:01:21

Take a look at once again. I'll try to

3:01:22

place it right here,

3:01:24

and it worked. Of course, it's going to

3:01:26

depend on the zoom position. I think if

3:01:28

we're at a normal regular zoom, and you

3:01:30

once again drag and drop it to a

3:01:32

specific position, that's exactly where

3:01:34

it places it. So, that has been fixed.

3:01:37

Also, now there's just one Clerk icon

3:01:39

right here, where you can see your

3:01:40

account.

3:01:42

Uh what else did we fix? The edges

3:01:45

connections. So, for example, let's try

3:01:47

to drag and drop the bottom of this edge

3:01:49

right here to the left side of the test

3:01:52

suite, and take a look at how well it

3:01:55

connects. It even places the arrow from

3:01:57

here to here. Or maybe when I go into

3:01:59

the package, that works as well, and the

3:02:02

arrows move. This makes our application

3:02:04

so much more usable, because now, for

3:02:06

example, we can connect the auth service

3:02:09

horizontally with the database

3:02:12

and then take the database output and do

3:02:14

something with it.

3:02:16

Again, this canvas is becoming a mess

3:02:18

right now because there's so much stuff

3:02:20

happening. So, it would make sense to

3:02:22

test out the delete. Select a couple of

3:02:24

elements and try to press the backspace

3:02:27

or the delete key and see whether it's

3:02:30

going to remove them. Because for me, it

3:02:33

doesn't. So, it looks like that's the

3:02:35

last issue that's still pending. So,

3:02:37

let's just tell it that issue two

3:02:40

remains unfixed. When I select an

3:02:42

element and press the backspace or the

3:02:44

delete key, the element is still there

3:02:47

and isn't being deleted from the canvas.

3:02:49

Analyze it and fix it. So, let's see if

3:02:52

we can just narrow it down to one issue,

3:02:54

whether it can finish that last one. And

3:02:57

again, this just shows you that whenever

3:02:58

you give it more stuff to fix,

3:03:01

it's easy for some of these to fall

3:03:04

through because it maybe didn't analyze

3:03:06

them as deeply in isolation.

3:03:09

So, you can see why these feature specs

3:03:11

worked so well so far because they were

3:03:14

super detailed and well-documented. But

3:03:17

as soon as you go deeper into the

3:03:18

conversation, the agent can easily get

3:03:20

lost.

3:03:22

So, this is its last chance. Let's see

3:03:24

how well it does.

3:03:27

It looks like it was able to find a root

3:03:29

cause in about 30 seconds. In apply no

3:03:32

changes inside of the live blocks react

3:03:35

flow source, we had a case of remove

3:03:38

which was not doing anything.

3:03:40

So, it was silently ignoring the remove

3:03:43

type changes, whereas live blocks

3:03:46

exposes a dedicated on delete function

3:03:49

that correctly deletes from the live

3:03:51

map. So, it fixed it and let's see how

3:03:54

well it does right now.

3:03:55

I'll reload the page and then I'll

3:03:58

select a couple of elements and press

3:04:00

the backspace key and now it deletes

3:04:02

them properly. Now, if you don't want to

3:04:04

delete them one by one, you can also

3:04:06

hold the shift key, and then drag and

3:04:09

drop over multiple, and remove them that

3:04:11

way.

3:04:12

Also, another thing that I didn't tell

3:04:13

you so far is that you can also add uh

3:04:16

text to the lines connecting different

3:04:19

elements. Like, we want to build and

3:04:23

then

3:04:24

run

3:04:25

the test suite. So, this way kind of it

3:04:28

makes more sense with the connection.

3:04:30

But, you can also use the text on the

3:04:31

lines for significantly more detailed

3:04:34

instructions when it comes to connecting

3:04:36

different elements. But, yeah, now that

3:04:38

all of these issues are fixed, we can go

3:04:40

ahead and push them. So, say get add

3:04:43

dot, get commit {dash} m, and this

3:04:45

lesson was all about canvas interaction.

3:04:48

So, say implement improved canvas

3:04:51

interaction.

3:04:53

And then run get push. Now, in the next

3:04:55

lesson, we'll focus on the AI

3:04:58

collaborative side of the application.

3:05:02

And now that we have the full canvas

3:05:05

right here, where we can drag and drop

3:05:07

elements, move things around, and create

3:05:10

our own architectural systems,

3:05:13

we're ready to help our users a bit by

3:05:16

implementing the AI workspace, where we

3:05:19

can allow our users to collaborate with

3:05:22

our AI agent within the application,

3:05:25

that I'll teach you how to implement

3:05:26

right now.

3:05:28

But, I want you to think about it a bit.

3:05:30

Like, before we start building the AI

3:05:32

generation features,

3:05:34

um you need to understand why these

3:05:36

can't run inside a normal API route.

3:05:40

See, when a user sends a prompt to Ghost

3:05:42

AI, something like this, to design an

3:05:45

e-commerce back end, the agent doesn't

3:05:48

just make one call. It analyzes the

3:05:50

request, runs multiple AI steps,

3:05:53

generates nodes and edges, and syncs

3:05:56

everything back to the canvas all

3:05:58

keeping the user updated. And that can

3:06:00

easily take 30 to 60 seconds, maybe even

3:06:04

a couple of minutes. And I mean, you've

3:06:05

already seen this pattern. When you

3:06:07

generate an image with ChatGPT or

3:06:09

Gemini, it doesn't happen instantly. The

3:06:12

work runs in the background, and you

3:06:14

keep receiving the updates until the

3:06:16

response is ready. And the API routes

3:06:18

within our application are not designed

3:06:20

for that. They have execution limits,

3:06:23

which means that long-running requests

3:06:24

can time out. And even if they didn't,

3:06:27

keeping a request open for that long

3:06:29

isn't how production systems are built.

3:06:31

That's what I'll teach you how to

3:06:33

implement Trigger.dev to solve this

3:06:35

problem.

3:06:36

In the final version of the application,

3:06:38

if you give it a detailed prompt and

3:06:40

send it, you'll be able to see direct

3:06:43

updates as it is working on the thing.

3:06:46

It'll first start with analyzing your

3:06:48

architecture request, and all of these

3:06:51

functions will run as durable background

3:06:54

tasks outside of the request life cycle,

3:06:57

which means that the work is running in

3:06:59

the background, and you'll be updated on

3:07:02

the process. So, the work of the API

3:07:04

route is to just trigger the task and

3:07:06

then return, while the heavy work

3:07:08

continues in the background. And that's

3:07:10

why both the AI design generation and

3:07:13

the spec generation in Ghost AI run

3:07:16

through Trigger.dev instead of directly

3:07:18

in API routes. And even before we used

3:07:20

Trigger on Ghost AI, I was already using

3:07:24

it within jsmastey.com platform, where

3:07:27

every time that users go through

3:07:28

different lessons, check out the

3:07:30

overviews, contents, or transcripts, we

3:07:32

can perform some actions on the back end

3:07:35

without slowing down or freezing what

3:07:37

the user can see on the front end, which

3:07:39

is super helpful for quizzes or sharing

3:07:41

the status of the AI interviewer that we

3:07:44

worked on. So, yeah, let me show you how

3:07:46

we can set up Trigger.dev in your

3:07:48

project.

3:07:49

First, click the link down in the

3:07:51

description to be able to follow along

3:07:53

and see exactly what I'm seeing.

3:07:55

And then create a new account. Once

3:07:58

you're in, you should be able to see

3:07:59

your dashboard that looks something like

3:08:01

this.

3:08:02

So, go ahead and create a new project.

3:08:04

I'll call it Ghost AI.

3:08:08

We are working on an AI agent, and under

3:08:11

technologies, I thought that Gemini

3:08:13

works the best. So, you can proceed with

3:08:15

Google Gemini. And what we're trying to

3:08:17

do with Trigger? Well, we're still

3:08:19

learning how Trigger works, as well as

3:08:21

potentially shipping a production

3:08:23

workflow. So, let's create it. Then,

3:08:25

once it is created, you can run this CLI

3:08:28

command to initialize it in the existing

3:08:31

project. So, just copy it.

3:08:33

Head back. Open up the terminal.

3:08:36

You can do it under a new tab.

3:08:39

And

3:08:40

just run NPX trigger.dev@latest

3:08:43

in it, and then pass your specific

3:08:45

project ID.

3:08:46

Say yes to install the Trigger.dev CLI.

3:08:49

And this will now install the

3:08:51

Trigger.dev SDK and initialize the

3:08:54

config file. But, it'll first ask you a

3:08:56

couple of questions, such as choose how

3:08:59

you want to initialize your project. In

3:09:01

this case, we'll proceed with the

3:09:02

Trigger.dev MCP, which allows you to

3:09:05

vibe your way to a new project. Well, in

3:09:08

this case, write detailed project

3:09:10

specifications to come up with a new

3:09:12

scalable project. It's going to ask you

3:09:14

whether you want to restrict the MCP

3:09:16

server to the dev environment only. I'll

3:09:18

say no.

3:09:19

And then, you'll be able to choose one

3:09:21

or more clients to install the MCP

3:09:23

server into. You can choose all the ones

3:09:26

that you're using, such as Claude Code,

3:09:28

VS Code, OpenAI Codex, or really

3:09:31

anything else that you want.

3:09:33

And then, press enter.

3:09:35

Where should the MCP server for Claude

3:09:37

Code be installed? I'll go over with the

3:09:40

project.

3:09:41

Same thing for all the other ones.

3:09:45

And that's it. We don't need to

3:09:47

our MCP clients. In your client, look

3:09:50

for a server named trigger, and then get

3:09:52

started with trigger dev by asking it to

3:09:55

add trigger to our project. So, let's do

3:09:58

just that. I'll open up our Claude code,

3:10:01

and that's going to be on a new chat

3:10:03

window, and I'll simply tell it to add

3:10:05

trigger dev to my project. And we can

3:10:07

now run it, but just before I do, I want

3:10:10

to share another thing with you, and

3:10:11

that is that trigger also supports agent

3:10:15

skills to teach any AI coding assistant

3:10:17

best practices for writing tasks,

3:10:20

agents, and workflows. So, we can

3:10:22

install it as we did for all the other

3:10:24

dev tools by opening up the terminal and

3:10:27

running MPX skills add trigger.dev

3:10:31

skills.

3:10:32

We can choose uh trigger agents, config,

3:10:36

and maybe setup. I think that's going to

3:10:38

be all we need for now, but you can just

3:10:40

go ahead and add all of them as well.

3:10:43

Choose your specific agents you want to

3:10:45

add it for.

3:10:47

Do it within the projects through

3:10:49

symlink, and then quickly install it.

3:10:52

Now, let's go ahead and ask it to add

3:10:54

trigger dev to my project. Then, let's

3:10:57

run it. It's going to explore the

3:10:58

project structure,

3:11:00

find all the relevant files, and the

3:11:02

architecture context, which matters a

3:11:03

lot, it's going to get the full picture,

3:11:06

and it'll start with configuring the

3:11:08

trigger config.ts,

3:11:10

creating a trigger directory with

3:11:12

example tasks, adding the trigger.dev

3:11:15

route handler, and finally updating the

3:11:17

progress tracker. And there we go, after

3:11:19

a couple of minutes, the trigger dev is

3:11:21

now set up. So, it first added the

3:11:24

trigger config.js.

3:11:27

So, let's quickly go over into it.

3:11:30

That's going to be within our

3:11:33

trigger.config.ts

3:11:34

in the root of the application,

3:11:36

and it looks something like this.

3:11:39

It is coming from trigger dev SDK.

3:11:41

We define a specific configuration, and

3:11:44

then we need to connect it to a project

3:11:46

reference.

3:11:48

This is coming from our environment

3:11:49

variables. So, very soon we'll have to

3:11:51

find it in our Trigger Dev dashboard,

3:11:53

and then add it to our .env's. Then,

3:11:56

below the project, we can add a runtime

3:11:59

and set the environment to node. When it

3:12:02

comes to directories, ./trigger is going

3:12:05

to be fine, or in this case, I think we

3:12:07

can just leave it to trigger. Now, max

3:12:09

duration stands for the max compute

3:12:12

seconds a task is allowed to run.

3:12:15

If the tasks exceed this duration, it'll

3:12:17

be stopped. And by default, we can set

3:12:20

it to something like 3600,

3:12:23

so that any task can run up to an hour

3:12:25

before it's stopped.

3:12:27

But again, that's not needed because

3:12:28

most of our AI tasks will be done in a

3:12:30

couple of minutes.

3:12:32

And then for the retries, if something

3:12:34

fails, Trigger Dev will retry it up to

3:12:37

three times with increasing delays

3:12:39

between each attempt. So, having that

3:12:41

built right in to make sure that if

3:12:43

something does go wrong, which it often

3:12:44

does with AI generation,

3:12:47

it retries it. So, when your user comes

3:12:49

back to your app, whatever they were

3:12:50

waiting for from an AI agent is waiting

3:12:52

for them there. Okay, so let's get those

3:12:54

keys.

3:12:55

I'll head back over to Trigger Dev,

3:12:57

specifically scroll down to API keys,

3:13:01

and right here you'll be able to find

3:13:02

your secret key. So, simply copy it.

3:13:05

Then, head over into your .env.local,

3:13:09

and here we can add, as it specifies,

3:13:13

the Trigger project ref,

3:13:16

as well as the Trigger secret key. So,

3:13:19

the secret key is the one we just

3:13:20

copied, and the project key you can find

3:13:22

right here by going over to the tasks

3:13:25

and copying it right here from the end

3:13:28

part of the CLI init command.

3:13:31

And then just paste it over here. And

3:13:33

the next part is to start your local dev

3:13:36

workflow. And I think this is the exact

3:13:39

second step from our 3-minute setup. NPX

3:13:42

trigger dev at latest dev. So, you can

3:13:45

open up your terminal, make sure that

3:13:47

it's not the one running your

3:13:48

application.

3:13:49

And before we run this, let me actually

3:13:51

explain what it does.

3:13:53

This is specific to local development.

3:13:55

Trigger dev background tasks don't run

3:13:58

on your machine automatically. They need

3:14:00

a local worker process running alongside

3:14:03

your Next.js dev server.

3:14:05

So, just to run this command, would you

3:14:07

like to install the Trigger dev code

3:14:09

agent rules? Well, yeah, go ahead.

3:14:11

We can select it for Claude code or

3:14:14

whichever other agent you're using.

3:14:16

And choose the rules you want to

3:14:17

install. Let's go with basic, advanced.

3:14:21

You know what? Let me go with all of

3:14:22

them.

3:14:24

Now, it's going to wait for you to log

3:14:26

in to your Trigger dev account, which

3:14:28

you can do by simply heading over to

3:14:30

this URL and then authenticating your

3:14:32

account. Once you're successfully

3:14:34

authenticated, you can head back and

3:14:36

you'll be able to see a screen that

3:14:38

looks something like this.

3:14:40

Logged in successfully, Trigger dev is

3:14:42

running, and the local worker is ready.

3:14:46

So, keep this running whenever you're

3:14:47

developing locally, because without it,

3:14:49

any task you trigger will queue, but

3:14:52

never execute.

3:14:53

And in production on Vercel, this isn't

3:14:55

needed because Trigger dev will handle

3:14:58

the worker infrastructure for you. So,

3:15:00

if you now head back over to your

3:15:01

dashboard, you'll be able to see two

3:15:03

different tasks generate design and

3:15:05

generate spec, which is absolutely

3:15:07

amazing. This was going to be one of my

3:15:10

upcoming project specifications to add,

3:15:12

but it looks like the MCP already

3:15:14

figured out exactly what we want, and it

3:15:17

created those tasks right here. So, soon

3:15:19

enough, you'll be able to see how those

3:15:21

tasks actually run and then see more

3:15:24

information about it. So, that was it

3:15:26

for the setup. And then in the next

3:15:28

lesson, let's build the AI generation

3:15:30

logic.

3:15:33

Okay, Trigger.dev and the agent together

3:15:36

already created for us the task

3:15:38

skeletons. But in this lesson, we'll

3:15:41

build the full AI generation flow. Now,

3:15:44

before we make our system design agent

3:15:46

logic, we'll have to install a couple

3:15:48

more packages. So, open up your

3:15:50

terminal. We'll have to go with the

3:15:52

third one right here.

3:15:54

And run npm install

3:15:56

@trigger.dev/react-hooks,

3:16:00

which lets the front-end subscribe to a

3:16:02

live Trigger.dev background run without

3:16:05

manually polling. So, think of it like

3:16:07

instead of asking is the job done yet

3:16:10

every few seconds, the hook will stream

3:16:12

in real-time status updates directly to

3:16:15

the UI as the task progresses.

3:16:18

Then, there's the @ai-sdk/google,

3:16:21

which is the Google provider for the

3:16:22

Vercel AI SDK. And in this case, we'll

3:16:25

be using Gemini for both design

3:16:27

generation and spec generation. But of

3:16:29

course, feel free to use any other agent

3:16:31

you prefer. And finally, AI is the core

3:16:34

Vercel AI SDK, which is going to give us

3:16:37

utilities for generating text or

3:16:39

generating different objects by working

3:16:41

consistently with any kind of AI

3:16:43

provider. So, go ahead and install these

3:16:46

three. And while they're getting

3:16:47

installed, head over to AI

3:16:49

Studio.google.com,

3:16:51

which we're going to connect with

3:16:53

Trigger.dev to infuse it with all sorts

3:16:55

of different functionalities. So, click

3:16:57

get started. Make sure to authenticate

3:16:59

with your account.

3:17:01

And Google AI Studio will give you free

3:17:03

credits in most regions, which is going

3:17:05

to be enough to test out the generation

3:17:07

flow throughout this build.

3:17:09

So, click get API key, then create a new

3:17:13

API key. You can call it something like

3:17:15

Ghost AI.

3:17:18

And then, you'll be given all your API

3:17:20

key details. So, first, copy the actual

3:17:22

API key

3:17:24

and paste it into your .env.local.

3:17:28

Specifically, that's going to be under

3:17:31

google_ai_api_key,

3:17:35

and it's going to be set to this key you

3:17:37

just copied. Or, if you want to get a

3:17:40

completely free alternative to Google

3:17:43

Gemini, OpenRouter has a pretty good

3:17:45

selection of free models. I recommend

3:17:47

this Nvidia Neumotron 3 Nano Omni.

3:17:51

That's a mouthful. Which is a free model

3:17:54

that has a 256,000 context window, and

3:17:58

it handles the architecture generation

3:18:00

pretty well. Just keep in mind that the

3:18:01

free tier models always have usage

3:18:03

limits. So, if you go that route, simply

3:18:06

swap that AI SDK Google thing for the

3:18:09

OpenRouter SDK in the generation task.

3:18:12

For this video, I'll personally use

3:18:13

Google AI Studio, but again, as with the

3:18:16

AI agents, follow along with whichever

3:18:18

one works for you. So, now with the

3:18:20

setup ready, we are ready to start

3:18:22

adding the Trigger.dev task spec files.

3:18:26

So, head over into our context feature

3:18:29

specs,

3:18:30

and find or create a new one called

3:18:33

22_design_agent_api.md.

3:18:38

The goal of this feature is to set up

3:18:40

the back-end flow for design generation

3:18:43

using Trigger.dev.

3:18:44

And keep in mind that when we're

3:18:45

referring to design here, we're

3:18:47

referring to the systems or architecture

3:18:49

design of our applications. So, we're

3:18:52

setting up this design agent API for the

3:18:54

back-end wiring. Starting with the

3:18:57

trigger route, where we need to create a

3:18:59

specific post route that should accept

3:19:01

the design prompt and some context, and

3:19:04

then trigger the design task through

3:19:06

Trigger.dev. Because, as I said, these

3:19:09

tasks and these AI prompts can take some

3:19:11

time to process. Then, we'll add task

3:19:14

tracking. This is going to be a special

3:19:16

model in Prisma to track Trigger.dev

3:19:19

runs and verify ownership.

3:19:21

Who created the run, when, and what is

3:19:24

it all about?

3:19:25

We should add the token route to verify

3:19:27

ownership, and then finally create the

3:19:30

actual design task. We want to check the

3:19:33

existing Trigger.dev setup and installed

3:19:35

agent features, reuse the existing

3:19:37

setup, export a minimal design task, and

3:19:40

for now not add any design logic yet.

3:19:44

Just the infrastructure that makes the

3:19:45

next spec possible.

3:19:47

You learned that already. Spec by spec,

3:19:50

we're getting closer to the final

3:19:51

solution.

3:19:53

So, let's go ahead and run it. As usual,

3:19:56

you can just tell it to read the current

3:19:58

file, update the context progress

3:20:00

tracker, and implement it exactly as

3:20:03

specified. And after some work, feature

3:20:06

22, the design agent API, has now been

3:20:09

completed. It created a new Prisma model

3:20:13

called task run with run ID, project ID,

3:20:16

user ID, so we know which projects are

3:20:18

being run over on Trigger.dev. Then,

3:20:20

there's the design agent itself, which

3:20:23

is a minimal task accepting a prompt and

3:20:25

a room ID, which logs and echoes the

3:20:28

inputs.

3:20:29

So, if you open it up, you'll see that

3:20:31

it looks like this. Currently, there's

3:20:34

no logic and functionality, but we'll

3:20:36

add it very soon.

3:20:38

Then, there's the post API AI design,

3:20:41

which requires authentication. And what

3:20:43

this one does is it basically figures

3:20:45

out whether the user is authenticated,

3:20:47

takes in the body, and finally creates a

3:20:50

new task run in the database.

3:20:53

But of course, before it hands it over

3:20:55

to Trigger.dev. But now, the actual task

3:20:58

is pretty empty. It's just console

3:21:00

logging design agent triggered, and it's

3:21:02

not doing anything else.

3:21:04

So, our next task is to implement the

3:21:07

full AI design agent logic, so a user

3:21:10

prompt results in real-time updates on

3:21:13

the canvas. To do that, head over into

3:21:15

our feature specs, and let's focus on

3:21:18

adding the 23rd spec, which is design

3:21:21

agent

3:21:23

logic.md.

3:21:25

And as I said, the goal here is to

3:21:27

implement the full AI design agent

3:21:29

logic.

3:21:31

So, first we need to update the file

3:21:32

which was just created.

3:21:34

Before, when I check the product

3:21:36

behavior of the system, check the live

3:21:38

blocks and trigger agent skills. This is

3:21:40

very important. Sometimes you have to

3:21:41

explicitly tell it to check the skills.

3:21:44

Follow the trigger dev setup and agent

3:21:46

patterns already set up. Reuse the

3:21:48

existing live blocks flow and presence

3:21:50

patterns instead of creating new ones,

3:21:52

and then implement use Gemini logic to

3:21:56

interpret the user prompt, update the

3:21:59

canvas using the collaborative flow

3:22:01

utilities, supporting actions like

3:22:03

adding nodes, moving them, resizing

3:22:05

them, updating them, deleting them.

3:22:07

Basically, everything that our

3:22:09

application is already doing.

3:22:11

Then, want to publish that AI activity

3:22:13

to the shared status feed, so all users

3:22:15

can see the progress.

3:22:17

Update the AI presence through cursors

3:22:19

and thinking state, and push clear

3:22:21

status messages at key steps. If you

3:22:24

think about it, this is a very important

3:22:28

and big feature spec. Maybe the biggest

3:22:31

one in our application so far, because

3:22:33

now we're essentially telling our AI

3:22:35

agent and trigger

3:22:37

to figure out the entirety of our

3:22:40

application

3:22:42

and to replicate its functionality on

3:22:44

its own while following the user prompt.

3:22:47

Like, the user is telling it to create

3:22:50

something on the canvas, and it should

3:22:52

create it.

3:22:53

So, let's go ahead and see how well it

3:22:55

does by opening up a new Claude code

3:22:57

window, giving it access to the file,

3:22:59

and asking it to read the spec file,

3:23:04

update the progress document, and

3:23:07

implement it exactly as specified. This

3:23:09

is a big one, so I'm super excited to

3:23:12

see how well it actually does it. And

3:23:14

feature 23 is now complete. This one

3:23:17

took a bit longer because it's a it's a

3:23:19

huge one. And we now have a full AI

3:23:23

agent built within our application

3:23:25

powered by trigger.dev.

3:23:27

It uses Gemini 2.0 flash via create

3:23:31

Google generative AI plus generate text

3:23:34

with output object to produce a typed

3:23:37

list of canvas actions for adding,

3:23:40

moving, resizing, updating, and deleting

3:23:42

nodes and edges. It sets the AI presence

3:23:46

by thinking is set to true. So, all

3:23:48

collaborators can see the AI cursor. It

3:23:51

broadcasts three different statuses:

3:23:53

start, thinking, and complete. And it

3:23:56

applies all mutations atomically in a

3:23:59

single mutate storage call.

3:24:01

Perfect. This is exactly what we wanted.

3:24:03

So, while we can't see it on the screen

3:24:06

just yet,

3:24:07

what we can do is head over to our

3:24:09

trigger.dev dashboard under tasks.

3:24:12

Currently, we have three separate tasks.

3:24:14

That's because our initial setup task

3:24:17

created the skeletons for generate

3:24:19

design and generate spec, but the new

3:24:21

one is right here under design agent.

3:24:23

So, what we can do is maybe we can clean

3:24:26

up the other two just so they're not

3:24:28

confusing us. So, under trigger, we can

3:24:30

remove the generate design and generate

3:24:32

spec, which are just empty placeholders.

3:24:36

And instead, we can just leave the

3:24:37

design agent right here. So, that's the

3:24:39

only one that remains.

3:24:41

Now, open it up

3:24:43

and perform a test run.

3:24:46

It's going to ask you to pass the data

3:24:48

as a regular user would through the

3:24:49

application.

3:24:50

And we can write something like room ID,

3:24:53

and you'll have to get this room ID

3:24:55

directly from one of your canvases.

3:24:57

So, head over here and create a new

3:24:59

project. Let's call it something like um

3:25:03

system design.

3:25:06

Created. And you can copy the project ID

3:25:08

directly from the URL right here at the

3:25:11

top.

3:25:13

Then, if you head back, you can update

3:25:15

it over here. And we can ask it to

3:25:17

architect a real-time chat application.

3:25:20

I need a web socket server for live

3:25:23

messaging, a presence service to track

3:25:25

online users, and a relational database

3:25:27

for message history. Connect the

3:25:29

services using a pub sub system like

3:25:31

Redis, so it can scale horizontally, and

3:25:33

include an S3 bucket for media

3:25:36

attachments.

3:25:37

I'll give you a prompt like this in the

3:25:39

video kit link down in the description,

3:25:41

so you can copy it and paste it.

3:25:43

But make sure to update the room ID. And

3:25:46

now, let's go ahead and run the test.

3:25:48

Oh, but before, make sure that your dev

3:25:51

server is connected right here. If it's

3:25:53

not, connect it, and then we'll be able

3:25:55

to run the test.

3:25:57

As you run it, you'll be able to see all

3:26:00

the information about this specific task

3:26:02

run. When it was triggered, when it was

3:26:05

queued, and when it has started. Now,

3:26:07

immediately, it'll fail after three

3:26:10

attempts, saying that we exceeded our

3:26:12

current quota, and we need to check our

3:26:14

Google billing plan. So, it's super

3:26:16

convenient to be able to test the runs

3:26:18

directly within the Trigger dashboard.

3:26:20

So, once you top it up or change your

3:26:22

providers, uh you can just reload your

3:26:24

Trigger dev server, and then replay the

3:26:26

run.

3:26:28

Hopefully, this time, it continues

3:26:30

running after just a couple of seconds.

3:26:32

And we get another little API error,

3:26:35

saying that models Gemini 2.0 Flash is

3:26:37

no longer available.

3:26:39

Um I wish they made those backwards

3:26:41

compatible, but it is what it is. We got

3:26:44

to find one that actually is compatible.

3:26:46

So, instead of using 2.0 Flash, I'll

3:26:49

head over into this design agent file,

3:26:52

that's going to be right here,

3:26:54

and I'll switch it over to 2.5

3:26:58

Flash. I believe that is the latest

3:27:00

running model. Then, when you change it,

3:27:03

don't forget to rerun your server.

3:27:06

Again, the Trigger.dev server by running

3:27:08

MPX Trigger.dev at latest dev. Then,

3:27:11

after it creates the local worker, go

3:27:13

ahead and replay the run. Third times

3:27:15

the charm. Or, apparently not, because

3:27:18

after about 20 seconds, we got a little

3:27:20

error saying, "No object generated.

3:27:23

Response did not match the schema."

3:27:27

So, open up a new chat, and let's tell

3:27:30

it something like this.

3:27:32

While implementing

3:27:35

at 23, that's going to be the 23rd

3:27:39

design spec

3:27:41

that we tried to do, the agent logic. Or

3:27:44

rather, we can say after implementing, I

3:27:46

tried to run a test. And then, I give it

3:27:49

some more information that we used for

3:27:51

the test itself, and then pasted the

3:27:53

error that we got, which you can get

3:27:54

from the web.

3:27:56

Finally, we can tell it something like

3:27:57

analyze trigger.dev and Liveblocks best

3:28:00

practices. Analyze this doc, and I

3:28:03

shared it a link to the doc of the

3:28:06

generating structured data part, because

3:28:08

so far, it generated it just as text.

3:28:11

But, what we need to do instead is have

3:28:14

some kind of a schema that we can

3:28:16

validate against. So, currently, it is

3:28:18

using this output, but what we can do is

3:28:21

use a so-called tool calling approach

3:28:24

with generate text with a tools object.

3:28:28

So, I'll tell it analyze this docs page

3:28:31

and the error, and then, I'll pass some

3:28:33

additional info which I think is the

3:28:35

reason for why it's failing.

3:28:37

So, that design agent is failing because

3:28:39

generate text is being used with output

3:28:42

object for structured output. That

3:28:45

experimental API is causing the runtime

3:28:47

error.

3:28:48

So, instead, replace it with a tool

3:28:50

calling approach using generate text

3:28:52

with a tools object. Each canvas action

3:28:55

should be its own tool. So, we have the

3:28:57

add node, move node, and so on. And this

3:29:00

way it's going to follow the schema

3:29:02

precisely. So, have a message that looks

3:29:04

like this and send it out.

3:29:08

This is also a useful approach to share

3:29:10

it a link to a specific documentation

3:29:13

page so that it can do a web fetch and

3:29:16

figure out how it works. Or

3:29:17

alternatively, there's also this context

3:29:20

seven, which is a library of the latest

3:29:22

documentations that you can directly

3:29:24

fuse with cursor, Claude, or other LLMs.

3:29:28

For example, it has the latest docs for

3:29:29

Next.js updated 9 hours ago. Same thing

3:29:32

for React and most likely for AI agents

3:29:36

as well. There we go. AI SDK. This is

3:29:39

what we're using. So, if we hook this up

3:29:41

with our agent, and you can do that very

3:29:43

easily just by copy and pasting this

3:29:45

thing right here, it would have all the

3:29:47

latest docs. But let's see what it can

3:29:49

do with just the URL. And very quickly,

3:29:51

the build passes. Here's what changed.

3:29:54

The output object in generate text

3:29:56

relies on the model emitting a single

3:29:59

structured JSON blob. But Gemini 2.5

3:30:02

Flash returned a response that didn't

3:30:04

match the schema, triggering the AI no

3:30:06

object generated. So, in this case,

3:30:08

we're replacing the output with a tool

3:30:11

approach where each canvas action is its

3:30:13

own named tool, like add node, move

3:30:16

node, and so on. So, after generation,

3:30:18

all tool calls are collected and then we

3:30:20

get the final canvas.

3:30:22

Okay, so let's go ahead and test it out.

3:30:25

Back on Trigger.dev, we can just replay

3:30:27

the run. And in about 15 seconds, it

3:30:30

finished. We got a successful output

3:30:32

with 11 actions applied and a summary of

3:30:36

what happened. Now, let's make it even

3:30:38

better by adding another feature spec

3:30:42

right here. That's going to be 24 AI

3:30:45

presence state.md.

3:30:48

And the goal of this one is to add a

3:30:51

shared activity indicator so that when

3:30:54

AI is generating, everyone in the room

3:30:56

will see it. A status indicator in the

3:30:59

sidebar, as well as a thinking spinner

3:31:02

cursor, and the input will be disabled

3:31:04

while generation is active. So, let's go

3:31:06

ahead and run it with the usual message.

3:31:11

And let's give it a minute. And I sped

3:31:13

up the process for you, but this one

3:31:15

definitely took some time, but

3:31:17

thankfully the build passes cleanly.

3:31:20

We added some new types for the

3:31:23

tasks.ts,

3:31:24

modified the liveblocks.config, and

3:31:27

added the presence cursors. But before

3:31:29

we actually go ahead and test this out,

3:31:31

I want to finish the implementation of

3:31:33

the sidebar so we can test everything

3:31:35

we've been working on together.

3:31:37

So, go ahead and open up a 25 sidebar

3:31:42

chat feed.md

3:31:44

file.

3:31:45

And within this one, the goal is to wire

3:31:49

up the AI chat liveblocks feed into the

3:31:52

sidebar.

3:31:53

See, liveblocks feeds are real-time

3:31:56

message channels. Think of them like a

3:31:59

group chat that is tied to a specific

3:32:01

project room. Anyone in that room can

3:32:04

send a message, and everyone else can

3:32:06

instantly see it. In this case, we're

3:32:08

using two separate feeds, the AI chat

3:32:11

feed and the AI status feed. The AI

3:32:14

status feed is simply for some kind of

3:32:17

progress signals, and AI chat is for the

3:32:20

actual conversation. This spec handles

3:32:22

the chat side where users can send

3:32:25

messages and make them appear across

3:32:27

every connected client. So, let's go

3:32:29

ahead and run it, and I'll speed it up

3:32:32

for you. And feature 25 is done. Here's

3:32:35

what was changed across three files.

3:32:37

First, added some additional types.

3:32:40

Then, in the config, we extended the

3:32:42

feed message data with optional chat

3:32:45

fields. So, now it also supports the

3:32:47

sender, role, content, and timestamp

3:32:50

alongside the existing AI status fields,

3:32:53

so they both coexist under the same

3:32:55

type. The key addition right here was

3:32:57

the chat feed ID constant. So, now we

3:33:01

support the chat alongside these status

3:33:03

updates as well. And finally, just one

3:33:06

more thing before we test it, we have to

3:33:09

wire everything together. So far, this

3:33:11

is just the UI. In the last couple of

3:33:13

prompts, we implemented the UI side of

3:33:15

things and Liveblocks side of

3:33:17

functionality. Just before that, we

3:33:19

implemented background tasks through

3:33:22

trigger.dev, but now we have to wire it

3:33:25

all together.

3:33:26

So, go ahead and open up 26

3:33:30

design agent

3:33:33

the goal here

3:33:36

is really to connect everything

3:33:38

together.

3:33:39

We are submitting a prompt from the AI

3:33:42

sidebar, then we're tracking the status

3:33:45

live through the use real-time run

3:33:48

function. We're also showing a status

3:33:51

strip while the task runs,

3:33:54

and we're letting Liveblocks handle the

3:33:56

updates of the canvas automatically.

3:33:59

So, let's go ahead and run it. And as

3:34:01

soon as it is finished, we're going to

3:34:03

test everything together.

3:34:05

And in a couple of minutes, definitely

3:34:07

much faster than it would take me to do

3:34:09

all of this, feature 26 is done. Again,

3:34:13

just modify some of the types,

3:34:16

modify the config to widen it just a

3:34:18

bit, and then most importantly, the

3:34:20

major rework happened within the AI

3:34:22

sidebar, where the handle send now

3:34:25

actually pushes the user message to AI

3:34:28

chat, calls our API endpoint to get the

3:34:32

run ID, and then the token as well to

3:34:34

get the public token, and then stores

3:34:36

both in state.

3:34:39

Finally, trigger.dev hooks run. We track

3:34:42

the run and once it reaches a final

3:34:44

status, it pushes the final AI message

3:34:46

to the AI chat, resetting the loading

3:34:48

state and clearing the presence. So,

3:34:51

what do you say that we head back over

3:34:53

to localhost:3000,

3:34:54

reload the page, and you'll see a

3:34:56

missing access token in trigger context

3:34:59

auth or use API client options. We can

3:35:02

quickly head back over to just a

3:35:04

localhost:3000 and editor. It is

3:35:07

possible that that's because of this

3:35:08

specific project. So, I'll create a new

3:35:11

project now.

3:35:12

I'll call it

3:35:14

testing

3:35:15

AI designs

3:35:18

and create it.

3:35:20

And once again, we immediately get the

3:35:22

missing access token in trigger auth

3:35:25

context or use API client options.

3:35:28

So, there's this handy little copy error

3:35:31

info click that you can copy, and then

3:35:33

you can directly paste the full error

3:35:35

right here. When I try to open a

3:35:37

specific canvas, this is the error that

3:35:40

I get. And then we can just display that

3:35:42

error.

3:35:43

Tell it to analyze it by following the

3:35:47

best practices from both Liveblocks and

3:35:50

Trigger.dev, and provide me your

3:35:53

reasoning and idea of what is wrong and

3:35:56

how to fix it. So, something like this

3:35:58

does. I noticed when you don't ask it to

3:36:00

fix it immediately, but rather you first

3:36:03

ask it to analyze it and give you the

3:36:05

idea of what it would do, that's not

3:36:07

cluttering the context as much, and it

3:36:10

gives you the ability to say whether you

3:36:13

would like it to execute what it

3:36:15

suggests or not. So, let's see what it

3:36:17

comes up with, and then we can

3:36:18

potentially execute it. And very

3:36:20

quickly, it was able to figure out the

3:36:22

reasoning, and I believe it actually

3:36:24

applied it because it was just a couple

3:36:26

of lines of code. The root cause was

3:36:28

that the use real-time run performs an

3:36:30

eager validation check for access token

3:36:33

on every render, and it doesn't wait for

3:36:36

a connection attempt. So, if we pass

3:36:38

empty string, it's the same as if we

3:36:40

don't pass it at all, so it immediately

3:36:42

throws the error on the first render.

3:36:44

But rather, we need to wait, and only

3:36:47

when there's an active run, then we need

3:36:49

to check for the token. So, now if we

3:36:50

head back, you can see that we can open

3:36:53

up a design, the new one as well as the

3:36:57

old one. So, now let's go within the new

3:36:59

project we created,

3:37:01

and we are ready to describe a new

3:37:03

system to AI.

3:37:06

In this case, you can really go ahead

3:37:07

and type anything. I'll write something

3:37:09

like this. You can also pause your

3:37:11

screen and write it with me. But the

3:37:13

goal here is to design a high-scale

3:37:16

e-commerce back-end architecture,

3:37:18

including an API gateway, a user service

3:37:21

with auth, a product catalog service

3:37:24

using a no SQL database,

3:37:26

and an order processing system that

3:37:28

communicates via a message queue. Also,

3:37:31

don't forget to add a Redis cache for

3:37:33

the catalog to handle high traffic.

3:37:36

And now, we can go ahead and click send.

3:37:39

You can see that Ghost AI is analyzing

3:37:42

your request in real time right now.

3:37:46

I'm going to zoom it in. So, this is the

3:37:48

first status that we're seeing. It says

3:37:50

working right here.

3:37:51

And then immediately after, we were able

3:37:54

to see a completed product. So, in this

3:37:57

case, we get the API gateway,

3:38:00

the user accessing it, browsing

3:38:03

different products, placing an order

3:38:05

through the product catalog service, and

3:38:07

that goes into the message queue, and

3:38:09

then the order is actually processed.

3:38:11

So, this is a great and pretty detailed

3:38:14

system architecture diagram, and we were

3:38:16

able to generate it using AI very

3:38:19

quickly. Because we already had the

3:38:21

underlying system for creating different

3:38:23

nodes and adding labels within the

3:38:25

elements and the edges.

3:38:28

And then we just created a system that

3:38:30

works in the background through

3:38:31

trigger.dev that makes use of all of

3:38:34

those functionalities within the app,

3:38:37

generates that JSON, and then we just

3:38:39

slap it onto the canvas. So, now that

3:38:41

everything is working so well, the last

3:38:43

missing piece of the puzzle is to

3:38:46

generate the specs. We need to take

3:38:49

everything that is on the canvas and

3:38:51

generate a markdown document that we can

3:38:53

then actually feed into a thinking or

3:38:57

planning AI agent, which is going to

3:38:58

make it the strongest possible starting

3:39:00

point for starting to develop any kind

3:39:03

of an application for real. But, the

3:39:06

first step is now done. It's all about

3:39:08

approaching the architecture from a

3:39:10

planning and very detailed way, like you

3:39:13

are an architect really, designing a

3:39:15

specification document. So, in the next

3:39:17

lesson, let's focus on generating that

3:39:20

spec.

3:39:22

The flow for generating the spec is

3:39:25

almost the same pattern as for

3:39:27

generating the design. A trigger route,

3:39:30

a background task, and a token route for

3:39:33

ownership tracking. Then, persistence to

3:39:36

Vercel blob, and finally, a piece of UI

3:39:39

to then view and download the result.

3:39:42

Because we've already built this pattern

3:39:44

once, these specs will move faster. So,

3:39:48

let's go ahead and build it one by one

3:39:50

by creating a feature spec number 27

3:39:53

called spec generation flow.md.

3:39:58

And yeah, as I told you, the goal of

3:40:01

this one is to build the full backend,

3:40:04

the trigger route, and the trigger.dev

3:40:06

task that calls Gemini and then

3:40:09

generates a markdown spec, but most

3:40:11

importantly, it generates it from the

3:40:14

canvas state and the chat history. You

3:40:18

can see that right here, spec generation

3:40:20

based on the project ID, room ID, chat

3:40:24

history, nodes, and edges. No backend

3:40:27

yet, We just wanted to execute that

3:40:29

task. So, let's go ahead and run it. 27

3:40:32

spec already. I'm not sure how to feel

3:40:35

about it. Is it too high or too low of a

3:40:37

number? I mean, considering what we've

3:40:39

implemented and what we've done so far,

3:40:42

like a full stack, full canvas

3:40:45

AI-powered application with templates

3:40:48

and everything else we've done. I mean,

3:40:50

I think this would take us months to

3:40:53

develop manually. And yeah, now that I

3:40:56

think about it, I'm sure. 27 spec files

3:41:00

is definitely not a lot for this

3:41:02

production-ready application we've

3:41:04

developed so far. We're very close, so I

3:41:07

believe we'll be able to get it done in

3:41:08

under 30. Let's make it run.

3:41:11

In this case, it looks like it's

3:41:13

actually running all three files in

3:41:15

parallel. This one was actually pretty

3:41:17

quick. Feature 27 is complete, and the

3:41:20

only thing we need to check from this

3:41:21

one is the trigger task called generate

3:41:24

spec. So, let's head over into that

3:41:26

file, and you'll notice that it's quite

3:41:29

similar to design agent. It takes all

3:41:31

the schemas, it builds the context,

3:41:34

returns it all, and here's a system

3:41:36

prompt saying that we are a ghost AI

3:41:39

senior technical architect whose job is

3:41:42

to generate a comprehensive markdown

3:41:44

technical specification document based

3:41:46

on the provider architecture canvas and

3:41:49

the conversation context. And we then

3:41:51

give it some information on how to

3:41:52

structure the spec. Finally, we call the

3:41:55

schema task coming from trigger, give it

3:41:58

an ID, the schema of what it needs to

3:42:01

do, the ability to retry it after

3:42:03

specific options, and then what is it

3:42:06

actually running? In this case, we're

3:42:07

calling the Google generative AI and

3:42:10

asking it to generate the spec, build

3:42:13

the context, generate it in text. Again,

3:42:16

we have to use 2.5 flash. 2.0 is no

3:42:20

longer there. And then finally, it gets

3:42:22

the spec and returns it. Before we go

3:42:25

ahead and test it, we have to first

3:42:27

implement a couple of functionalities

3:42:29

and then wire it to the sidebar. So, go

3:42:32

ahead and open feature specs and create

3:42:35

a unit 28 spec persistence

3:42:39

download.md.

3:42:41

The goal of this one is to save the

3:42:44

generated spec file to Vercel blob,

3:42:47

store the URL in project spec Prisma

3:42:49

model, and then add a secure download

3:42:52

route. Basically, same Prisma metadata

3:42:55

plus the blob content pattern that we

3:42:57

use for the canvas auto save. That's how

3:42:59

we want to save it.

3:43:00

And then finally, we want to ensure that

3:43:02

it's downloadable through this specific

3:43:05

API route. It should authenticate the

3:43:08

user, verify the access, verify the spec

3:43:11

belongs to that project, and then fetch

3:43:13

the file and return it as a downloadable

3:43:16

markdown file.

3:43:18

Go ahead and open this with your AI

3:43:20

agent. As usual, we'll tell it to read

3:43:23

the file, update our progress tracker,

3:43:25

and then implement it exactly as

3:43:27

specified.

3:43:29

And while it's doing its job, let's

3:43:31

quickly check how the progress tracker

3:43:33

is coming along. I mean, we've

3:43:34

implemented so many features so far, and

3:43:37

all of it is here for future developers

3:43:40

within your team or future agents

3:43:42

working on the project to check out and

3:43:44

then continue developing new features on

3:43:47

top of. All the architecture decisions,

3:43:49

session notes, and more.

3:43:52

So, yeah. Let's see how it approaches

3:43:54

this one. And feature 28 is done. A

3:43:57

summary right here is that it added a

3:43:59

project spec Prisma model with the ID,

3:44:03

project ID, file path, and created at.

3:44:05

And it's attached to a project. So, if

3:44:08

we delete it, it's going to be deleted

3:44:09

as well. Migrations have already been

3:44:11

applied for us. I love agents for doing

3:44:14

that automatically, so we don't have to

3:44:16

run Prisma generate and migrate on our

3:44:18

own. And finally, it updated the

3:44:20

generate spec so that after the markdown

3:44:23

is generated, it uploads it to a Vercel

3:44:25

blob so that we can download it later

3:44:28

on. And this route is responsible for

3:44:30

that. So now, just before we tested we

3:44:34

need to wire everything together. You

3:44:36

know how I like to make sure that these

3:44:38

spec files are really only handling one

3:44:40

thing at a time. So in 27, we handled

3:44:43

the spec generation flow. Then we made

3:44:46

it persist by storing it into Vercel

3:44:48

blob and creating a download route. And

3:44:51

now finally, in 29, spec UI

3:44:55

integration.md,

3:44:57

we are going to wire everything together

3:44:59

into the specs tab in the AI sidebar

3:45:03

through a list of generated specs. We

3:45:05

could have multiple, a preview model

3:45:07

that renders the markdown in case you

3:45:09

want to preview it in the browser, and

3:45:11

finally a download action to download it

3:45:14

on your device. So, go ahead and run it.

3:45:17

And let's give it some time to implement

3:45:18

it. And let's let it do its thing.

3:45:21

And now that feature 29 is fully

3:45:23

implemented and the build passes, which

3:45:25

is important, we'll soon be ready to

3:45:27

test it out. So, there's two new backend

3:45:30

routes. The first one gets access to all

3:45:33

the project specifications for a

3:45:34

specific project, and then the second

3:45:37

one actually creates a downloadable

3:45:39

markdown text. And the AI sidebar got

3:45:42

updated so we can test it all out. Back

3:45:45

within the browser, if you now head over

3:45:47

to specs and click generate spec, you'll

3:45:50

notice that, at least on my end,

3:45:52

nothing's happening.

3:45:54

On your end, it might be working. Maybe

3:45:56

your agent did a bit of a better job,

3:45:58

but at least here, it's not budging. But

3:46:01

thankfully, we are getting some errors

3:46:03

right here within the terminal.

3:46:05

So, I'll go ahead and copy these errors

3:46:08

and tell it when I click the generate

3:46:11

spec button, it doesn't do anything. And

3:46:14

this is what I see in the terminal. I

3:46:15

pasted it and then I'll say analyze it

3:46:19

and provide a solution.

3:46:21

Refer to trigger.dev and liveblocks

3:46:25

skills if needed and press enter.

3:46:28

I find it that sometimes it's great to

3:46:30

explicitly mention that it can refer to

3:46:32

specific skills if you think it's going

3:46:34

to be useful. Yeah, this definitely took

3:46:36

some time. But yeah, it says right here

3:46:39

just to restart your server. It did some

3:46:43

additional changes, but I think after

3:46:45

all the changes, it's necessary to just

3:46:47

reload it. So that's what I'm going to

3:46:49

do. Back on localhost 3000, it's going

3:46:51

to be active.

3:46:53

Make sure to reload the page and then

3:46:55

head over to specs.

3:46:58

Click generate spec

3:47:00

and it is generating. While it is

3:47:02

actually generating, you can head over

3:47:05

to trigger.dev under runs and you can

3:47:08

see generate spec is being executed as

3:47:10

we speak. So here you can see a bit more

3:47:13

information on exactly what is

3:47:14

happening. This is the payload that we

3:47:16

sent into it, all the different nodes,

3:47:19

all the information about the

3:47:20

architecture and more. And it looks like

3:47:23

it generated it and returned it. So now

3:47:25

if you head back, you'll be able to see

3:47:27

a new markdown file that when you click

3:47:30

on it, it'll be opened within a markdown

3:47:32

previewer right here in the browser.

3:47:36

There we go. It's a bit tiny. We can

3:47:38

definitely increase its width.

3:47:40

But yeah, I mean, take a look at this.

3:47:42

This architecture diagram, it was able

3:47:45

to generate a pretty hefty technical

3:47:47

specification document that outlines

3:47:50

this huge system that we can send over

3:47:52

to our planning agent and start

3:47:54

developing it. Let's test the download

3:47:56

functionality as well. I clicked the

3:47:58

button and it opened it up. It's a long

3:48:01

file, but if you've been following along

3:48:04

with this course, you know that

3:48:05

specifications have to be long,

3:48:08

especially if you're creating one spec

3:48:10

for the entire system. So, that's it.

3:48:13

That's the full Ghost AI feature set

3:48:15

working end to end. So, what do you say

3:48:18

that we test out the functionalities one

3:48:20

more time on two separate screens? At

3:48:22

least right now, I'm on two separate

3:48:24

tabs, so still logged in with the same

3:48:26

account, but that should be okay.

3:48:28

So, I'll say final test and create a new

3:48:32

project.

3:48:34

That project will immediately show up

3:48:36

right here as well after reloading the

3:48:39

page.

3:48:40

And now we are viewing it from both

3:48:42

sides.

3:48:44

Both of us are in the same room, and you

3:48:45

can see my name

3:48:48

and cursor pointing different locations.

3:48:51

Of course, if it were some other people

3:48:53

right here, you'll be able to see their

3:48:55

profiles as well.

3:48:57

And as soon as I drag and drop

3:48:58

something, you can see that it appears

3:49:01

immediately. It also auto zoomed in

3:49:03

right here, and we can continue creating

3:49:06

the diagram. You can see it happens in

3:49:09

real time. And that's the beauty of

3:49:11

Liveblocks. What we can do next is test

3:49:14

the AI functionalities. So, I'll open up

3:49:17

the AI window right here,

3:49:20

and let's go with something simple and

3:49:22

default like design an e-commerce

3:49:24

backend.

3:49:26

I'll first select all of these by

3:49:27

holding the shift key and then remove

3:49:29

them.

3:49:31

And design that e-commerce backend

3:49:33

by sending a new AI chat message. It'll

3:49:37

start working on it,

3:49:39

and very quickly it'll generate the full

3:49:42

UI right here. And that UI got generated

3:49:46

for the other user as well. Finally, we

3:49:48

can generate some specs out of it. And

3:49:50

of course, this is happening in the

3:49:52

background as a task that is being run

3:49:55

on the trigger.dev side of things. Or

3:49:58

specifically, we are running an agent,

3:50:00

in this case Gemini, to process

3:50:02

something. But, of course, you can run

3:50:04

all sorts of different AI agents, media

3:50:06

processing, media generation. Like,

3:50:08

there's tons of different use cases for

3:50:10

how Trigger can be used, and we can

3:50:12

definitely explore some more of it in

3:50:14

one of the upcoming videos.

3:50:16

But, yeah, the spec is here. We can

3:50:18

preview it, download it, and that's it.

3:50:22

That is the entire app. So, in the next

3:50:25

lesson, let's get it deployed.

3:50:30

Okay. Now that the app is complete,

3:50:32

before we deploy, we need to switch a

3:50:35

few services from development to

3:50:37

production.

3:50:39

Dev keys are rate-limited and aren't

3:50:41

meant for real traffic. So, we have to

3:50:44

update Liveblocks, Trigger.dev, and make

3:50:46

sure that every environment variable is

3:50:49

set correctly in Vercel. Let's start

3:50:51

with Liveblocks.

3:50:52

Create a new project and call it Ghost

3:50:56

AI production or something like that.

3:50:59

And set the environment to production.

3:51:02

Then, from the API key section, simply

3:51:05

copy the public key,

3:51:07

head over into your .env.local,

3:51:10

and paste it right here.

3:51:12

Liveblocks public key, you can override

3:51:15

the development one, or in case you want

3:51:17

to keep it, you can just comment it out.

3:51:19

But, again, make sure to add new

3:51:22

production ones right here. Or at least

3:51:24

when we're deploying the project on

3:51:25

Vercel, you'll have to put these new

3:51:27

updated ones.

3:51:29

So, yeah, let's go ahead and update it.

3:51:32

Uh the Liveblocks public key is going to

3:51:34

be this new prod key right here.

3:51:37

And also, let's generate a secret key,

3:51:40

which you can copy,

3:51:42

and then also override right here.

3:51:45

Step two is get the Trigger.dev

3:51:47

production API keys by heading over to

3:51:50

the dashboard, and then in the top

3:51:52

selector right here, where it says

3:51:55

environment, switch it over from

3:51:57

development to production. Then, from

3:52:01

the project settings right here under

3:52:03

API keys,

3:52:04

go ahead and copy the secret key and

3:52:07

replace the existing one right here

3:52:09

under ENV local.

3:52:11

Once again, I will comment out the

3:52:12

previous one and just update this one

3:52:15

that's going to have prod right here in

3:52:17

the middle. And below the API keys, you

3:52:19

can head over to general project

3:52:21

settings and copy the project ref, which

3:52:24

I believe should be the same. So, if you

3:52:27

duplicate it

3:52:29

and override it here, you'll see that

3:52:31

one is actually the same, so we can

3:52:33

leave it as it is. Now, go ahead and

3:52:35

copy all of your ENVs

3:52:38

and we have to push our changes.

3:52:40

So, run git add dot, git commit {dash}

3:52:44

m,

3:52:45

finalize the app and prepare it for

3:52:48

deployment, and then run git push.

3:52:51

Then, head over to Vercel, click add a

3:52:54

new project, and just import it from

3:52:56

git. Once pushed, it should have changes

3:52:59

a couple of seconds ago, not a couple of

3:53:01

hours ago. But, the reason it says 5

3:53:03

hours here is because we've pushed the

3:53:05

changes to our dev branch. So, for them

3:53:09

to show up on Vercel, we first have to

3:53:10

push them over to the main branch by

3:53:13

creating a new PR.

3:53:16

And in this case, there were so many

3:53:17

changes that we've done, most of which

3:53:19

are actually agent skills that we

3:53:22

installed. So, there's going to be 1.7

3:53:24

million lines of code to review. In this

3:53:27

case, what I'm going to do just to get

3:53:29

it deployed is I will first fix the

3:53:32

conflict we have with Copilot. I'll just

3:53:35

tell it to completely remove the current

3:53:38

issues.md file,

3:53:41

which should fix the conflict,

3:53:42

obviously.

3:53:44

That's because we're hoping not to have

3:53:46

any more issues. It's going to look into

3:53:48

it and start implementing it right away.

3:53:51

And as soon as it has been removed, we

3:53:53

are ready to merge the PR,

3:53:55

which means that the latest changes

3:53:57

under the Ghost AI project should be

3:53:59

reflected momentarily. And after a

3:54:02

reload, they say just now. So, we can go

3:54:05

ahead and import it.

3:54:07

And immediately add the environment

3:54:10

variables by pasting all of them into

3:54:12

this first key and value pair, and it'll

3:54:14

automatically populate all of them. So,

3:54:17

go ahead and scroll down and click

3:54:19

deploy.

3:54:20

Now, we've implemented so many

3:54:22

individual features within this

3:54:24

application,

3:54:25

integrated and wired up so many services

3:54:28

across front-end and back-end and Prisma

3:54:30

databases.

3:54:32

It would be a miracle if it worked first

3:54:34

try. So, just as I said that, in about 8

3:54:37

seconds, the initial build failed saying

3:54:40

that we can find a full log right here.

3:54:43

So, I'll go ahead and inspect the

3:54:44

deployment, and it looks like it just

3:54:47

failed on running npm install.

3:54:50

So, typically when we face an error this

3:54:52

early with the npm install, most likely

3:54:55

culprit is going to be the

3:54:57

package-lock.json.

3:54:59

So, what I like to do in this situation

3:55:01

is just make sure that we are on the

3:55:04

main branch, and we are, and want to

3:55:07

delete the package-lock.json entirely,

3:55:10

and then push to the main without it by

3:55:12

running git add dot, git commit {dash} m

3:55:15

remove package-lock.json,

3:55:19

and want to get push.

3:55:21

This is going to push it straight over

3:55:22

to main, which means that it should

3:55:24

automatically re-trigger another

3:55:26

deployment. And this time, it is

3:55:28

building past second eight, successfully

3:55:32

installing the dependencies. So, this

3:55:33

time we've gotten a bit further, but it

3:55:36

still breaks right here on Prisma.

3:55:39

Uh this is a typical procedure. Whenever

3:55:41

you're publishing a Next.js project on

3:55:44

Vercel with Prisma, you also have to add

3:55:47

an additional script to the

3:55:49

package.json.

3:55:50

That's going to be right here.

3:55:52

Right after lint, go ahead and add a

3:55:55

post

3:55:56

install script where you can run Prisma

3:56:00

generate. So, once again, you can make

3:56:03

that change

3:56:04

and push it over to the main branch.

3:56:08

As I told you, it's going to be unlikely

3:56:10

that the build is going to succeed from

3:56:12

the first try. As we've been developing

3:56:14

this project for so long locally and in

3:56:17

development version, and now we want to

3:56:19

run it in production.

3:56:21

But, don't worry, we're going to get

3:56:22

there. And third time's the charm

3:56:25

because the status is now ready and we

3:56:27

are live. So, go to the project overview

3:56:31

and visit your URL, which brings us back

3:56:34

to our auth and home page at the same

3:56:36

time, where after you sign in, you are

3:56:39

redirected to an empty project screen or

3:56:42

since we already logged in with the same

3:56:44

account, you can find all of your

3:56:45

previous projects. This means that

3:56:48

everything is working exactly as it's

3:56:50

supposed to, even on the deployed

3:56:52

version of the application. So,

3:56:54

congrats.

3:56:55

This was a long build and quite a

3:56:58

different one from the typical ones

3:57:00

where we manually code everything out.

3:57:02

So, I want to hear from you.

3:57:04

How did you like this new approach?

3:57:06

Would you like to dive even deeper

3:57:08

within the Agenty course that I'm

3:57:10

working on?

3:57:11

If so, you can join the waitlist. And if

3:57:13

you haven't already, also go ahead and

3:57:15

download that six-file contact system

3:57:17

for Agenty development that I prepared

3:57:19

for you alongside this video. That way,

3:57:22

you can use the same system we followed

3:57:24

along with this build and implement it

3:57:27

with your future builds. And yeah, huge

3:57:29

thanks to Trigger for amazing background

3:57:32

tasks and AI agent functionalities that

3:57:35

they allow you to add to your app. Also,

3:57:37

huge thanks to Liveblocks for allowing

3:57:39

us to make our applications interactive.

3:57:42

And finally, to Clerk for simplifying

3:57:44

authentication and user management.

3:57:47

Thank you not only for sponsoring this

3:57:49

video, but for developing such amazing

3:57:51

developer tools that I get to share with

3:57:54

you guys watching this video.

3:57:56

That was it for this one. I'll see you

3:57:58

within jsmastey.com

3:58:00

or if not there, within one of the

3:58:03

upcoming videos on the YouTube channel.

3:58:05

Once again, thank you for watching and

3:58:08

have a wonderful day.

Continue with YouTLDR

Analyze another video with Pro

Process a new video, search every timestamp, compare sources, and keep the result in your library.

Get Pro — $12/month30-day money-back guarantee

More transcripts

Explore other videos transcribed with YouTLDR.