Each task is incredibly small, testable, and has a clear start + end. Follow in order and verify the Test before proceeding.
- Start: Empty project folder.
- End: Run
git init;.gitdirectory exists. - Test:
git statusoutputs clean working tree.
-
Start: No
package.jsonat root. -
End: Create root
package.jsonwith:{ "private": true, "workspaces": ["apps/*","contracts","sdk"] } -
Test: Running
yarn installcompletes;yarn workspaces infolists workspaces.
-
Start:
apps/frontendexists and empty. -
End: Inside
apps/frontend, run:npx create-next-app@latest . --typescript --eslint --app -
Test:
cd apps/frontend && yarn devserves default page athttp://localhost:3000.
-
Start: Clean Next.js TS project.
-
End: Run:
yarn add -D tailwindcss postcss autoprefixer npx tailwindcss init -p
Update
globals.csswith Tailwind directives. -
Test: Add
<h1 className="text-green-500">Tailwind</h1>toapp/page.tsx; text appears green.
-
Start: No Supabase config.
-
End: Create project on supabase.io; in
apps/frontend/.env.localadd:NEXT_PUBLIC_SUPABASE_URL=... NEXT_PUBLIC_SUPABASE_ANON_KEY=...
-
Test:
console.log(process.env.NEXT_PUBLIC_SUPABASE_URL)inapp/page.tsxprints URL.
-
Start: No Supabase client code.
-
End: Create
src/utils/supabaseClient.tsexporting:import { createClient } from '@supabase/supabase-js'; export const supabase = createClient( process.env.NEXT_PUBLIC_SUPABASE_URL!, process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY! );
-
Test: Import and call
await supabase.from('profiles').select()ingetServerSideProps; no errors.
- Start: Auth providers disabled.
- End: In Supabase dashboard, enable Email/Password under Auth > Settings.
- Test: In console
await supabase.auth.signInWithPassword({ email, password })returns session.
- Start: No
/loginroute. - End: Create
src/app/login/page.tsxwith email/password form that callssupabase.auth.signInWithPassword. - Test: Navigate to
/login, submit valid creds, redirect to/.
- Start: No React context for auth.
- End: Create
src/context/AuthContext.tsxprovidinguser,signOut(); wraplayout.tsx. - Test: In
app/page.tsx, accessuseContext(AuthContext).user; logs user object.
-
Start:
contracts/moveexists but empty. -
End: Add
Move.tomlandsrc/TuriCheck.movestub with:module 0x1::TuriCheck { public entry fun mint_badge(session_id: u64, correct: bool) {} }
-
Test: Run
cd contracts/move && sui move build; build succeeds.
-
Start: No deployment script.
-
End: Create
scripts/deploy-move-local.shthat:- Starts Sui localnet (if needed).
- Publishes
TuriCheckmodule. - Prints module address.
-
Test: Run script; output shows valid module address.
-
Start: No wallet libs.
-
End: In frontend, run:
yarn add @mysten/wallet-adapter-react @mysten/wallet-adapter-wallets
-
Test: Wrap
<WalletProvider>inlayout.tsx;useWallet()returns connectors.
- Start: No tx builder.
- End: Create
src/utils/suiTx.tsexportingbuildMintBadgeTx(sessionId: number, correct: boolean)that returns aTransactionBlockcallingmint_badge. - Test: Import and call with dummy values; returns
TransactionBlockinstance.
- Start: No AI API code.
- End: Create
src/utils/llmClient.tswithasync function queryAI(prompt: string): Promise<string>calling external API. - Test: Call
await queryAI('Hello')in console; returns mock AI response (or actual API output).
- Start: No chat context.
- End: Create
src/context/ChatContext.tsxwith state:sessionId,messages,setMessages,endChat(). - Test: Wrap app; in a test component, update
messagesand see re-render.
- Start: No chat UI.
- End: Create
components/ChatBox.tsxthat rendersmessagesand input box; on submit, callsqueryAIand appends both messages to context. - Test: Render
<ChatBox>; type message; see user and AI messages appear.
- Start: No session tracking.
- End: On first user message, call Supabase RPC Edge Function
create_sessionto insert intosessionstable and setsessionIdin context. - Test: Submit first message; new row in Supabase
sessionswith correctsession_id.
- Start: No guess UI.
- End: Create
components/GuessForm.tsxwith radio buttons “Human”/“AI” and “Submit” button. - Test: Render
<GuessForm>; select option; internal state updates.
- Start: GuessForm unconnected.
- End: On submit, call
buildMintBadgeTx(sessionId, correct)→signAndExecuteTransaction, then show success toast. - Test: Make a guess; wallet popup appears; on-chain badge minted (verify on localnet explorer).
- Start: No badge UI.
- End: Create
components/BadgeCard.tsxdisplaying badge image, sessionId, result. - Test: Pass mock props; badge card renders correctly.
- Start: No hook.
- End: Create
src/hooks/useBadges.tsthat fetches Sui NFTs by module and returns badge data. - Test: Call in test component; returns empty array or mock data without errors.
- Start: No
/profileroute. - End: Create
app/profile/page.tsxthat usesuseBadges()to render a grid of<BadgeCard>s. - Test: Navigate to
/profile; see zero or more badge cards.
- Start: No Walrus code.
- End: Create
src/utils/walrusClient.tsstub for storing/retrieving badge metadata. - Test: Call stub; returns placeholder.
-
Start: No CI file.
-
End: Add
.github/workflows/ci.ymlthat:- Runs
yarn install. - Builds Move (
sui move build). - Builds frontend (
yarn workspace apps-frontend build).
- Runs
-
Test: Push commit; GitHub Actions runs and passes.
- Start: No deployment.
- End: Connect
apps/frontendto Vercel for auto-deploy onmain. - Test: Merge to
main; live demo updates.
Completing these ~25 tasks yields a fully functional TuriCheck MVP: login → chat with AI/human → guess → mint Badge → view profile.