A molten sword being forged from a rough wireframe into a finished blade, over a dark ember-lit field

DDS Vibe Academy · Masterclass

From Prototype to Published

It didn't compile. It shipped its own API key. It had zero tests. Then it became Ashaveth — a tested, published AAA browser game. This is the exact, verified method, with the receipts.

Free class Advanced ~2 hours Built with the 2026 stack

A worked, end-to-end case study · Every number re-derived from a build · The game is free at ashaveth.ddsboston.com (18+).

Quick answer

This is a free, advanced masterclass that reconstructs — with verified evidence — how the browser RPG Ashaveth went from a prototype that did not compile to a tested, published game. You learn the 2026 web-game stack (React 19, TypeScript, Vite, a Cloudflare Worker in front of Gemini, Cloud Run) and a repeatable prototype-to-published pipeline: audit honestly, make it testable, ship AI without the key, and verify a release that can actually fail.

Key takeaways

  • The prototype did not compile, shipped its API key in the browser bundle, had 0 tests, and packed 13 screens plus all logic into a single 1,543-line file.
  • The v1.0 release compiles with 0 TypeScript errors, keeps the key server-side, and ships 154 unit tests covering the game logic — with that same file cut to 181 lines.
  • A 12-step repair roadmap, drawn from an honest audit, was executed to 12/12 complete.
  • The entry bundle is 327 kB, and the game ships 103 hand-generated illustrations at zero AI cost per play.
  • The API key never reaches the browser — a build-time scan fails the build if it ever does, and the guard was proven by poisoning a build until it blocked.
  • Publishing is 8 steps; the production build is step 3, and not the one most likely to break.
  • It was built with a multi-agent stack — Cowork, Opus 4.8, and Antigravity — under a Vibe Coding loop of intent, constraints, acceptance criteria, and an audit gate per phase.

The toolchain

The 2026 stack

Ashaveth is built on the current web-game stack: React 19 and TypeScript 5.8 in strict mode, bundled by Vite and tested with Vitest. AI dialogue runs through the Google Gemini API behind a Cloudflare Worker; the game ships as a static bundle in a multi-stage Docker image served by nginx on Google Cloud Run. None of it is exotic — the discipline is in how it fits together.

React

19.2

View layer, lazy-loaded one screen per chunk — 20 code-split bundles.

TypeScript

5.8 · strict

The gate: tsc --noEmit at 0 errors before anything ships.

Vite

6.2

Build and code-split. A 327 kB entry bundle, built in about two seconds.

Vitest

4.1

154 unit tests over pure game logic, green in roughly a second.

Gemini + Cloudflare Worker

@google/genai 1.30

AI dialogue, with the key held server-side as a Worker secret.

Cloud Run + nginx

node 22 → nginx

Multi-stage Docker: build with Node, serve static with nginx. No source in the runtime image.

Pre-generated art

103 .webp

Illustrations generated offline and committed — zero AI calls at play time.

Multi-agent Vibe Coding

Cowork · Opus 4.8 · Antigravity

Intent and constraints defined; agents execute; an audit gate closes each phase.

The delta

Prototype vs v1.0

Ten days separated a prototype audit from a shipped game. The prototype demoed convincingly for weeks — and did not compile, shipped its API key, and had no tests. The gap between "feels finished" and "is finished" is the entire subject of this class, and it is measurable.

0→154Unit tests
1543→181App.tsx lines
327 kBEntry bundle
12/12Roadmap steps
MeasurePrototype (Jul 10)v1.0.0 (measured)
Compiles?No — 4 raw > in JSXYes, exit 0, ~2.3 s
Source files1467
Lines of code~6,06211,938
App.tsx1,543 lines181 lines
Tests0154 across 7 files
TypeScriptnon-strict, 16 any at the AI edgestrict, 0 errors
API key in bundleYes — present in distNoproxied-by-worker
Confirmed defects~45 broken behaviorspinned by regression tests
Architecture grade (audit's own)C−shipped
The audit produced a 12-step roadmap. Every step verifies as complete against the current code — 12 of 12. The rest of this class is that roadmap, generalized into a method you can run on your own prototype.

1 Establish ground truth

Audit the prototype honestly

Before you scale a prototype, measure what you actually have. The Ashaveth build began with a report-only audit — no code changed — that catalogued every defect with a file-and-line citation and a total count. That document became the plan: a 12-step roadmap, later verified complete.

The audit's rule was strict: find out the real state of the code before improving it. It counted roughly 45 broken behaviors, recorded that the prototype did not compile (four raw > characters in JSX, fatal to the compiler), and graded the architecture C− — a single 1,543-line file holding thirteen screens, all game logic, and persistence. None of that was visible in a demo. The prototype ran under a tolerant sandbox and looked finished for weeks.

Two ground-truth checks come first, and both caught real failures on this project. Verify your substrate: copy the repo out and byte-compare it, then run a structural check that never references the source — parse every JSON file, confirm every block closes. A byte-comparison alone is blind to a corrupt original; only the structural pass found a truncated file that a checksum had declared clean. And initialize version control on day zero: here, git init happened at v1.0, so the entire prototype-to-release journey is unrecoverable from history. Your story is a business asset — do not lose it.

The rule

A prototype that demos is not a prototype that ships. Measure it — with citations and a count — before you scale it.

The Ashaveth title screen, the finished product the audit worked toward
The finished title screen. The audit is the unglamorous first step that makes it reachable.

2 Rebrand and clearance

The IP sweep you must finish

The prototype carried a pre-rebrand title — a decades-old dungeon-RPG name with obvious trademark exposure — which is exactly why it became Ashaveth. But a rename is a sweep, not a switch, and this one stopped early. The unfinished hits became permanent constraints on what the studio is allowed to say about its own game.

The rebrand changed the marketing and the metadata and then stopped before the boss object, the save-file keys, and several monster names. The consequence is concrete: the final boss still renders on screen under its old name. So the studio cannot name that boss in marketing without shipping a contradiction — and the antagonist is referred to everywhere, including on the game's own page, simply as the Red, the first fire.

There is a trap inside the fix. Each monster's image cache key is its name — so renaming the off-tone holdovers deletes their portraits. A one-line find-and-replace becomes an art-regeneration project. This is why the sweep must be planned and finished up front, before a single asset depends on a name you intend to change.

The durable output of all this is a banned-claims list shipped beside the safe-claims list. Every public asset — this masterclass included — is diffed against it before it goes out, so the code and the marketing can never contradict each other.

A few live entries from the discipline — describe, don't repeat, the wrong version.
Instead ofSay this — and why
Naming the final boss on screen"the Red" — the boss object still renders its old name
Overstating the game's test coverage"154 unit tests covering the game logic" — components aren't machine-tested
Rounding the bundle down to a rounder number"327 kB" — the measured value; never round down past a measurement
Saying the AI dialogue is "always" live"AI-driven dialogue with an offline fallback" — it degrades silently
An unqualified "0.05% relic drop""0.05% when the AI is live" — the fallback path can't drop them
The rule

Finish the IP sweep before you write a word of marketing. Every hit you leave behind becomes a banned claim — and banned claims outlive the launch, the page, and the person who remembers why.

3 Make it correct

Architecture that's testable by design

Testability is not something you add later; it is an architecture choice you make first. The prototype's 1,543-line file couldn't be tested because logic, view, and I/O were fused. v1.0 separates code by what a thing is, with dependencies pointing exactly one way — so the logic becomes pure, and pure logic can be checked.

components → state → services → data. Nothing points back up. That single rule is what makes the logic testable.

Two decisions carry the whole design. First, the core is pure: the combat engine takes its randomness as an injected rng parameter and calls no Date.now and no Math.random. That is the only reason 35 combat tests can exist and run in milliseconds. Second, the reducer distrusts its caller: a stale render is a hostile caller, so every state transition re-checks its own rules — advancing the forge re-runs the gate rather than trusting the button that dispatched it. The prototype's worst exploits — a cooldown wipe, a gold-minting bank, an infinite daily reset — were all the same bug: an invariant enforced only in a component.

The finale earns its own mention here: its prose was pulled out of the view into a pure function of the save file, precisely so it could be tested. That refactor is the difference between an ending that can be verified and one that contradicts itself in front of players.

The rule

No clock, no random, no I/O in the core. It becomes a pure function; a pure function can be tested; and correctness becomes checkable instead of hoped for.

4 Make it feel expensive

Systems that feel AAA

"AAA feel" here does not mean a big engine. It means hand-made density and a few well-tested rules: a wall of original art that costs nothing at play time, a boss ladder that never cheats, five distinct biomes, relationships tracked on six axes, and an ending that reads the save you actually earned.

The art pipeline: density at zero runtime cost

Ashaveth ships 103 original illustrations — 72 monsters, 26 characters, 5 locations — generated offline once and committed to the repository. At play time the game makes zero AI calls for art; it serves static files. Each image is cached on a stable key, which is where the earlier IP lesson bites: because a monster's key is its name, the sweep and the art are coupled forever.

Bog Hag concept art
Bog Hag
Obsidian Gargoyle concept art
Obsidian Gargoyle
Fallen Angel concept art
Fallen Angel
Necromancer concept art
Necromancer
Dryad Matriarch concept art
Dryad Matriarch
Plague Rat King concept art
Plague Rat King
Lava Elemental concept art
Lava Elemental
Void Seer concept art
Void Seer
Succubus concept art
Succubus
Harpy Matron concept art
Harpy Matron
Cinder Knight concept art
Cinder Knight
Ancient Wyrm concept art
Ancient Wyrm

The bestiary climbs — one honest stat block each

Five bosses gate the five biomes. The escalation is in the numbers, not in hidden tricks: each boss is one unbroken stat block with no surprise second phase. The design proves that pacing can come from tuning alone.

Arachne, the Broodmother

Arachne, the Broodmother

The Whispering Woods
1,200 HP
Golgoth, the Rot Giant

Golgoth, the Rot Giant

The Drowned Host
4,000 HP
High Cultist Malakor

High Cultist Malakor

The Obsidian Peaks
9,000 HP
Xel'nath, the Void Walker

Xel'nath, the Void Walker

The Void Edge
20,000 HP
Concept art of the Red, the first fire and final boss of Ashaveth

the Red — the first fire

The Dragon's Domain · final boss
25,000 HP

Five biomes, five moods

The Whispering Woods
Region 1The Whispering Woods
The Drowned Host
Region 2The Drowned Host
The Obsidian Peaks
Region 3The Obsidian Peaks
The Void Edge
Region 4The Void Edge
The Dragon's Domain
Region 5The Dragon's Domain

Relationships on six axes — and an AI that remembers

Companions are not a favor bar. Each is tracked on six independent axes, and their portrait and their written journal both change as the bond deepens. The dialogue is AI-driven, with an offline fallback; the journal entries are stored per character, and the ending will read them back to you.

AffectionTrustRespectFearAttractionManipulation
Osric, a companion
Companion

Osric

A warrior who can travel with you, with a journal that fills in as trust grows.

Wren, a companion
Companion

Wren

A second bond on the same six axes — a different voice, a different record.

Vosk, the smith
The smith

Vosk

Keeper of the forge, where the blade that matters against the Red is made.

The forge, and an ending that reads your save

One mechanic is coupled tightly to the theme. At Vosk's forge you raise Godsbane through four stages; fully forged, it strikes for triple damage against the Red, and its quench heals exactly 10% of max HP — once, enforced by the reducer, not the button. Then the finale reads the save you actually built: three endings, up to three companion journals quoted back verbatim, and no score — because being recorded is not the same as being bonded.

Vosk's forge, where Godsbane is raised through four stages
The forge screen. The mechanic that decides the final fight is the one the ending remembers.
The rule

Depth is felt at the end. Couple one mechanic tightly to your theme, make the finale a pure function of the save, and let a few well-tested rules do the work an engine can't.

5 Make it safe

Ship AI without shipping the key

The prototype's most expensive defect was a single string: its API key, sitting in the browser bundle for anyone to lift. The fix is architectural — the key never touches the client — and, crucially, the fix is proven, not assumed.

Every model call routes through a Cloudflare Worker that holds the key as a Worker secret. The browser bundle ships the literal apiKey:"proxied-by-worker" and no real credential. But "we moved it" is a claim, and claims decay — so a postbuild step scans the built bundle for credential patterns and fails the build if one appears. Then the team did the thing most teams skip: they poisoned a build on purpose to confirm the guard actually blocks it.

control experiment — can the guard be defeated?
# deliberately poison: inline a fake key, leave the proxy URL unset
$ npm run build
  vite v6.2.0 building for production...
   214 modules transformed  →  dist/assets/index-9f2a.js  327.46 kB
$ node scripts/check-secrets.mjs
  scanning dist/ for credential patterns...
  ✗ FAIL  credential pattern found in dist/assets/index-9f2a.js
  build aborted — a key must never reach the client
$ echo $?
  1

That experiment exposed a subtle truth the class calls "conditionally dead, not dead." The build tool inlines the key only when the proxy URL is unset — so the dangerous path isn't deleted, it's dormant. One empty environment variable re-enables it. A guard that can be switched off by a config mistake is exactly the guard you must keep testing.

Spend is bounded in five layers: an origin allowlist, a model allowlist, a per-player quota, a per-IP quota, and — the only one that actually bounds the bill — a global ceiling of 5,000 requests per day. A subtlety worth teaching: there are two independent clocks. The Worker's quota resets at 00:00 UTC; the in-game daily reset is 12:01 local time. Treating them as one clock is a bug in waiting.

The honest caveat, published on purpose: the game degrades to pre-written dialogue when the AI is unavailable, and on that fallback path the advertised 0.05% relic drop cannot occur at all. So the claim ships qualified — "when the AI is live" — never bare.
The rule

Get the key out of the client, then prove the guard fires by trying to defeat it. A security check is worth exactly as much as the last time you watched it fail on purpose.

6 Prove it works

Tests named after the bugs

The 154 unit tests are not a coverage number to brag about; they are a memory of every way the code was once wrong. Many test blocks are literally named (bug 1), (bug 2), and so on — each one pins a specific prototype failure so it can never return quietly.

The cautionary tale is the ending. It shipped in the prototype with a clean compiler and 96 passing tests: the victory screen printed three companion journal quotes and then declared that no one had written the hero down. Both could be true at once because the dev fixture seeded an empty journal for every character — so the test harness could only ever exercise the empty path. The contradiction was invisible to the tests and perfectly visible to players.

A fixture that cannot reach a state will certify that state as working — right up until a player sees it.The lesson the ending bug taught

The fix was architectural, not cosmetic: the ending prose was pulled out of the view into a pure function of the save file, and 36 tests now pin the invariant that being recorded is not being bonded. The forge got the same treatment — a hostile-caller suite proves that skipping a stage, spamming the button, replaying an old action, or passing an out-of-range value are all no-ops, and that the quench heal is applied exactly once.

The rule

Write the regression test before the fix, then interrogate your fixtures: for every invariant, ask what state would make this check fail — and confirm your fixture can actually reach it. A check that cannot fail is not a test.

7 Publish and verify

Publishing is 8 steps; the build is step 3

"It builds" is a small part of "it's live." Shipping Ashaveth means a proxy Worker with a hand-set secret, a quota store, a container image, a Cloud Run service, DNS, a TLS certificate, a storefront page — and a smoke test that can actually fail. The production build is one step of eight, and not the riskiest.

Set the Worker secret

Deploy the proxy + quota store

npm run build

Build the Docker image

Deploy to Cloud Run

Point DNS at the service

Wait for the TLS cert

Smoke-test the real origin

The topology is a multi-stage Docker build — node:22-alpine compiles, nginx:alpine serves — deployed to Cloud Run. DNS is a CNAME; the TLS certificate is issued by Google Trust Services and can take fifteen minutes to a few hours to go live. The storefront lives on Shopify.

Now the deepest trap in the whole project. The AI proxy rejects the temporary *.run.app origin by design (it's not on the allowlist), and the game falls back to pre-written dialogue that is deliberately indistinguishable from live AI. So a developer who tests on the Cloud Run URL sees a beautiful, fully playable game whose signature feature is completely dead — and ships it. The only proof the AI is live is a 200 from the proxy in the network tab, on the real origin.

That is why the deploy gate is a sequence of falsifiable checks that hard-stop on failure. Before the image is even built, the gate verifies the proxy returns 400 with no player id, 403 from a bad origin, and 200 from the real one — in that order. And a near-miss to remember: the .dockerignore once excluded scripts/, which would have stripped the secret scanner out of the build environment entirely. A guard that doesn't exist where the build runs is not a guard.

The rule

Make every deploy step a command with a pass condition, and hard-stop on the first failure. Your smoke test must be a specific observable on the real origin — a 200 from the proxy — not "the game works."

8 The method behind it

The multi-agent Vibe Coding loop

All of the above was produced with a small multi-agent stack — Cowork, Opus 4.8, and Antigravity — run under one repeating loop. The loop, not the tools, is the transferable part.

Each phase runs the same four beats: define intent (what "done" means), set constraints (the rules the result must obey), write acceptance criteria (a check that can fail), then let the agents execute — and close the phase at an audit gate that can send the work back. The gate is the load-bearing part. It caught the ending bug's root cause, and it is what turned "we moved the key" into a poisoned-build proof.

One honesty note the class insists on: the repository records a single git author with no per-commit trailers, so which agent wrote which line is unrecoverable. This masterclass therefore teaches the method and never claims that a specific model wrote a specific file. Attribution you cannot verify is not a fact you get to publish.

The rule

Define intent, constraints, and acceptance criteria; let agents execute; then run an audit gate that can actually stop the next phase. The loop is the product — the model roster is an implementation detail.

The reproducible pipeline

From your prototype to published

Tool-agnostic, and every step is a scar. Each names the decision, the acceptance test — a command with a pass condition, because if it cannot fail it is not a test — and the failure mode it exists to prevent. Run it in order; then keep the checks running on every build.

Phase I · Establish ground truth

  1. Initialize version control today

    Every prototype gets git init on day zero.

    Accept: git rev-list --count HEAD ≥ 1 before the first feature.

    Failure: Ashaveth's entire prototype-to-v1.0 history is unrecoverable — init happened at v1.0.

  2. Verify your substrate

    Never trust a disk or a mount.

    Accept: copy out, byte-compare, then a structural check that never reads the source.

    Failure: a checksum that "passes" because it compared a copy to a corrupt original.

  3. Audit honestly — report only

    Find out what you have before you scale it.

    Accept: a document with file:line citations for every finding and a defect count.

    Failure: scaling a codebase whose real state you have never measured.

Phase II · Make it buildable and safe

  1. Build with the production toolchain

    The playground is not the toolchain.

    Accept: npm run build → exit 0; tsc --noEmit → 0 errors.

    Failure: Ashaveth's prototype demoed for weeks and never compiled.

  2. Get the key out — and prove it

    All model calls go through a proxy you control.

    Accept: 0 credential matches in dist as a build-failing step, then poison a build and watch it block.

    Failure: a key in the bundle; worst-case spend, unbounded.

  3. Cap spend in layers

    Origin, model, per-user, per-IP, and a global daily ceiling.

    Accept: the proxy returns 400 → 403 → 200, in that order.

    Failure: per-user limits don't bound your bill; only the global ceiling does.

Phase III · Make it correct

  1. Extract the pure core

    Game math has no clock, no RNG, no I/O. Randomness is injected.

    Accept: the engine imports nothing stateful; its tests run in milliseconds.

    Failure: an engine that calls Math.random can't be tested, so it won't be, so its bugs ship.

  2. Put every transition behind a re-validating reducer

    Assume the dispatch site is hostile. A stale render is hostile.

    Accept: a test per invariant, named after the exploit — "skipping a stage is a no-op."

    Failure: an invariant enforced only in a component is an exploit.

  3. Test the bug you have; interrogate fixtures

    Your first test is a regression test, not a happy path.

    Accept: confirm your fixture can actually reach the failing state.

    Failure: the one that got Ashaveth — journal:[] hid the ending bug from 96 green tests.

Phase IV · Make it feel expensive

  1. Generate offline; ship it static

    Anything expensive and non-interactive is built, not run.

    Accept: per-session AI cost is zero; the assets are committed.

    Failure: paying per session for content that never changes.

  2. Key caches on stable IDs, never display names

    Display names change; keys must not.

    Accept: rename an entity and confirm no asset is orphaned.

    Failure: a find-and-replace becomes an art-regeneration project.

  3. Let the ending read the save

    Depth is felt at the end; make the finale read the real save.

    Accept: the finale is a pure function of the save file, and therefore unit-testable.

    Failure: an ending assembled inline in a view that contradicts itself.

Phase V · Clear, publish, verify

  1. Finish the IP sweep before any marketing exists

    Grep the old name across everything — data, keys, asset filenames, deploy config.

    Accept: zero player-visible hits.

    Failure: a final boss you cannot legally name in your own marketing.

  2. Ship the runtime, not the toolchain — keep your guards

    Multi-stage build; the final image has no source and no env files.

    Accept: inspect the final layer, and confirm the build-time guards survive into it.

    Failure: a .dockerignore that quietly deletes your secret scanner.

  3. Falsifiable deploy steps; hard-stop on failure

    Each step is a command with a pass condition. Nothing proceeds on a warning.

    Accept: a specific observable — a 200 from the proxy in the network tab, on the real origin.

    Failure: shipping a game whose signature feature is silently, invisibly dead.

  4. Re-derive every public number at release

    No number reaches a marketing asset without a command behind it.

    Accept: each claim carries a command and its output.

    Failure: a bundle quoted at a rounder number than the 327 kB it actually is.

  5. Publish the banned-claims list

    The constraints are a deliverable, not a footnote.

    Accept: every marketing asset is diffed against the banned list before it ships.

    Failure: re-publishing a claim the code contradicts.

Audit before you build; get the key out and prove it; make the core pure and the reducer authoritative; generate the expensive things offline; and never trust a green signal you haven't tried to break.The pipeline in one sentence

Receipts

Every number, from a build

No claim on this page is decorative. Each one has a command behind it — this is the discipline the class asks you to adopt.

  • 154 unit tests, 0 failuresvitest run
  • 0 TypeScript errors, stricttsc --noEmit
  • 327 kB entry bundlestat dist/assets/index-*.js → 327,462 bytes
  • App.tsx is 181 lineswc -l src/App.tsx
  • No key in the bundle — credential scan of dist/ → 0 matches
  • 103 art assets, 0 runtime callsls public/pregen | wc -l
  • Global spend ceiling 5,000/dayproxy/cloudflare-worker.js
  • 12 of 12 roadmap steps complete — verified against current code

Questions

Frequently asked

What does this masterclass teach?

It reconstructs, with verified evidence, how the browser RPG Ashaveth went from a prototype that did not compile to a tested, published game. You learn the 2026 web-game stack and a repeatable prototype-to-published pipeline: honest auditing, testable architecture, safe AI integration, and a falsifiable release process.

Who is this masterclass for?

Developers who can already build a prototype and want to ship one. It assumes comfort with React, TypeScript, and the command line, and it is written at an advanced level. It is free and self-paced at the DDS Vibe Academy.

What is the 2026 stack used to build Ashaveth?

React 19.2 and TypeScript 5.8 in strict mode, built with Vite 6.2 and tested with Vitest. AI dialogue runs through the Google Gemini API behind a Cloudflare Worker. The game ships as a static bundle in a multi-stage Docker image served by nginx on Google Cloud Run, with a Shopify storefront page.

How different was the prototype from the shipped game?

The prototype did not compile, shipped its API key in the browser bundle, had zero tests, and packed 13 screens plus all logic into a single 1,543-line file. The v1.0 release compiles with zero TypeScript errors, keeps the key server-side, ships 154 unit tests covering the game logic, and reduces that file to 181 lines.

How do you ship AI features without exposing the API key?

All model calls route through a Cloudflare Worker that holds the key as a Worker secret, so the browser bundle ships the string apiKey:proxied-by-worker and no real credential. A build-time scan fails the build if a key is ever inlined, and it was proven by deliberately poisoning a build until the guard blocked it.

How is a text-forward game tested?

The game logic is written as pure functions — the combat engine has no clock and no random source; randomness is injected. That purity is what makes 154 unit tests possible. Many tests are named after specific prototype bugs, so each one pins a way the code was once wrong.

Why does the masterclass say to test the bug first?

Because happy-path tests certify happy paths. A shipped bug in Ashaveth's ending survived a clean compiler and 96 passing tests, because the test fixture seeded an empty journal and could only ever exercise the empty path. The lesson: a fixture that cannot reach a state certifies that state as working.

How much of publishing is actually the build step?

The production build is one of eight publishing steps, and not the one most likely to break. Publishing also means a proxy Worker with a hand-set secret, quota storage, a container image, a Cloud Run service, DNS, a TLS certificate, a storefront page, and a smoke test on the real origin that can actually fail.

What is the invisible-fallback trap in deployment?

The AI proxy rejects requests from the temporary Cloud Run URL by design, and the game silently falls back to pre-written dialogue that is deliberately indistinguishable from live AI. A developer can test the temporary URL, see a perfect game, and ship with the AI completely dead. The only proof is a 200 from the proxy in the browser network tab on the real origin.

How was the game's art made without high runtime cost?

The 103 illustrations were generated offline once and committed to the repository, so art costs zero AI calls at play time. Each image is cached by a stable key. The lesson is to generate expensive, non-interactive content at build time and ship it static.

What multi-agent workflow built Ashaveth?

It was built with a multi-agent stack — Cowork, Opus 4.8, and Antigravity — under a Vibe Coding loop: define intent, constraints, and acceptance criteria; let agents execute; then pass an audit gate before the next phase. The repository does not record which agent wrote which code, so the class teaches the method rather than assigning authorship.

Is Ashaveth free, and who can play it?

Ashaveth is free to play in the browser with no download and no signup, at ashaveth.ddsboston.com. It is a grimdark RPG intended for adults and is self-rated 18+ by Design Delight Studio; it carries no ESRB or PEGI rating.

Why publish a banned-claims list?

Because the constraints are a deliverable, not a footnote. An unfinished rename left the final boss rendering under its old name, which permanently limits what the studio can claim. Every public asset is diffed against a banned-claims list before it ships, so the code and the marketing never contradict each other.

Where can I take this masterclass and see the game?

The masterclass is free at the DDS Vibe Academy. You can play the finished game free at ashaveth.ddsboston.com and read its details on the Ashaveth page at ddsboston.com.

Bottom line

The distance between a prototype and a product is not more features — it is signals that can fail, and that you have watched fail. Audit honestly, make the core pure, get the key out and prove it, generate the expensive things offline, and verify the release on the real origin. Ashaveth is the proof that the method works end to end; running it on your own prototype is the assignment.

Play the proof. Then build yours.

Ashaveth is free in the browser — no download, no signup. Then take the method into your own build at the DDS Vibe Academy.

Free to play · 18+ · Built and published by Design Delight Studio, Boston.