6 min read
project_eval: An IEx Swiss Army Knife You and Your AI Share
How Tidewave's project_eval Turns Claude Output Into a Shared IEx Workflow

Hello Curious Coders,

You hand Claude the plan, ask it to build the context layer, and watch it work. Files appear, tests run, and then the terminal fills with something like this:

"# Test the full flow\nalias Wordle.{Repo, Dictionaries, Games}\nalias Wordle.Dictionaries.{Dictionary, DictionaryWord}\n{:ok, dict} = %Dictionary{theme: \"demo\"} |> Dictionary.changeset(%{}) |> Repo.insert()\n..."

Everything is jammed onto one line. Newlines show up as \n. Quotes are escaped inside quotes. Your eye slides right off it, because it looks like machine exhaust.

Don’t scroll past.

That block is Claude talking to you. It just ran real code against your real application and pasted back what happened. That’s project_eval, one of the tools Tidewave exposes through MCP, and it is the one that changes the shape of the workflow.

First, Choose the Surface

Before the tool matters, the interface matters.

You can run Tidewave in the browser with Claude Code as its provider, or you can run Claude Code in the terminal and add Tidewave as an MCP server. If the work is browser-heavy, especially LiveView UI work, Bruce’s rule is simple: use Tidewave in the browser. That integration is built for what you’re doing.

Outside the browser, the choice is not about which version of Claude thinks better. It is about what the model can see.

“The difference between Claude Code and Claude Code with Tidewave isn’t how it thinks. It’s what it knows, because the MCP server provides all this layered information that makes a much more powerful base for reasoning.”

– Bruce Tate

Plain Elixir that follows common web patterns may be fine in regular Claude Code. But sticky domain logic, context boundaries, functional core code, or flows that don’t look like the usual database-backed web app need more project context.

That’s why Bruce splits the work. He builds the contexts and functional core in Claude Code with Tidewave MCP, then moves to browser Tidewave for the LiveView slice, where the UI and framework behavior matter most.

🎯 Join Groxio's Newsletter

Weekly lessons on Elixir, system design, and AI-assisted development — plus stories from our training and mentoring sessions.

We respect your privacy. No spam, unsubscribe anytime.

What the Trace Proves

In the Wordle project, Claude does more than write files. It uses project_eval to interrogate the system as it builds.

It recompiles modules and checks that compilation succeeds. It aliases the dictionaries and games contexts, then calls exports to inspect the public API it just created. It writes tests, runs them, and surfaces warnings. Bruce’s reaction to those warnings is the rule worth keeping: you are not done until the warnings are out of the code.

Then comes the useful trace.

Unescaped, it reads like an ordinary IEx session. Claude converts a dictionary, builds a changeset as the bridge from the functional core to the boundary, inserts it, and gets a tagged tuple back. It adds words like crane, steer, slate, and trace. It validates a word, fetches the dictionary by theme, filters to five-letter words, and takes three.

Then it moves through the game API: create a game against the demo dictionary, make a guess, make another guess, render the board. The solution stays hidden because the word has not been guessed yet.

That’s the point. Claude did not claim the context layer worked. It demonstrated the context layer working inside your application, with your data, and showed you the transcript.

Turn the Noise Back Into IEx

Here is the practical move.

When project_eval gives you an escaped one-line string, copy the chunk into a scratch file. Trim the stray whitespace, wrap the whole thing in outer quotes if needed, and pass it to IO.puts in IEx.

Before:

"# Test the full flow\nalias Wordle.{Repo, Dictionaries, Games}\n{:ok, dict} = %Dictionary{theme: \"demo\"} |> Dictionary.changeset(%{}) |> Repo.insert()\nGames.new_game(dict, \"crane\")\n"

Run:

iex> IO.puts "# Test the full flow\nalias Wordle.{Repo, Dictionaries, Games}\n{:ok, dict} = %Dictionary{theme: \"demo\"} |> Dictionary.changeset(%{}) |> Repo.insert()\nGames.new_game(dict, \"crane\")\n"

After:

# Test the full flow
alias Wordle.{Repo, Dictionaries, Games}
{:ok, dict} = %Dictionary{theme: "demo"} |> Dictionary.changeset(%{}) |> Repo.insert()
Games.new_game(dict, "crane")

You do not need to repair every character by hand. You need enough shape to read what ran, what returned, and where the application disagreed.

If the terminal session is gone, claude --resume lists recent sessions so you can reopen the one with the trace.

A Shared REPL

Once you see this, project_eval stops looking like a random MCP command. It becomes a shared REPL.

“Both you and Claude can use project_eval to understand what’s going on in your project. Claude uses it to communicate with you, and you can use it to communicate with Claude. All three of these use models are valid and super powerful.”

– Bruce Tate

You can call project_eval with no arguments and let it prompt you. You can send 1 + 1. You can ask for h Enum.map because IEx helpers come along. You can use flush to inspect messages. For multi-line input, a trailing \ lets the tool assemble the full expression before running it.

Bruce puts the role of the tool plainly:

project_eval is IEx and mix for Claude Code. It provides direct context for what’s going on in the application proper.”

– Bruce Tate

That does not prove Tidewave automatically improves code quality. It proves something more practical: the conversation gets better when both you and the model can ask the running system what is true.

You stop accepting claims about behavior and start asking for demonstrations. That shift matters, because the next step is slower and more human: we open the context code Claude built, inspect the boundary patterns, look at the tagged tuples, and find the domain requirement the model missed.


🤖 Turn AI Claims Into Demonstrations

This comes from Bruce's AI Agents course — the anti-vibe-coding curriculum. Learn structured oversight with the Ask → Plan → Agent framework, so you can make AI prove behavior inside the running system without losing architecture decisions or letting it become a crutch. Available via monthly subscription — try it for one month.

— Paulo & Bruce

Bruce Tate's avatar
Bruce Tate
System architecture expert and author of 10+ Elixir books.
Paulo Valim's avatar
Paulo Valim
Full-stack Elixir developer and educator teaching modern Elixir and AI-assisted development.