mirror of
https://github.com/tiennm99/tiennm99bot.git
synced 2026-10-11 03:13:46 +00:00
6.0 KiB
6.0 KiB
Phase 01 — Foundation
Context links
- Plan overview:
./plan.md - Module pattern reference:
src/modules/doantu/,src/modules/loldle/ - KV state pattern:
src/modules/doantu/state.js - Module contract:
CLAUDE.md§ "Module Contract" - Workers AI binding:
wrangler.toml [ai](already wired)
Overview
- Priority: P1 (foundation — blocks all other phases)
- Status: planned
- Description: Create the module scaffold, seed list, KV state layer, prompt templates, and environment wiring. No AI calls yet, no command handlers — just the data + structure pieces.
Key insights
- Workers AI binding
env.AIalready exists (used by semantle/doantu for embeddings). New module just callsenv.AI.run(modelId, ...). - Module folder name MUST equal the registry key MUST equal the
name:field. - KV is the only storage needed — no D1, no migrations, no cron.
- Seeds live in source (not KV) — small enough (~60 entries) and changes ship with deploy. Avoids cold-fetch latency on first round.
Requirements
Functional
- Seed list defines categories + objects. Each entry:
{ category, object, initialHint }. - Categories: instrument, animal, food, vehicle, sport, household.
- 8–12 objects per category (60–72 total).
- Each entry has a hand-curated initial hint that nudges without revealing.
- KV state per subject: active game + lifetime stats.
- Game record TTL: 7 days (matches doantu pattern).
Non-functional
- All files <200 LOC each (split if approaching).
- JSDoc typedefs for game/stats/seed shapes.
- No external network calls in this phase.
Architecture
src/modules/twentyq/
├── index.js # placeholder export — wired up fully in phase 3
├── seeds.js # SEEDS const + getRandomSeed(rng?)
├── state.js # loadGame, saveGame, clearGame, loadStats, recordResult
├── prompts.js # buildSystemPrompt(seed, history) + function-call schema
└── README.md # initial scaffold docs
KV layout (under twentyq: prefix):
| Key | Value |
|---|---|
game:<subject> |
{ category, target, initialHint, startedAt, solved, turns[] } (TTL 7d) |
stats:<subject> |
{ played, solved, totalTurns, bestTurnCount, lastResultAt } |
Each turns[] entry: { text, isGuess, answer: "yes" \| "no", hint, ts }.
Related code files
Create
src/modules/twentyq/index.js— minimal{ name: "twentyq", commands: [] }placeholdersrc/modules/twentyq/seeds.js—SEEDSarray +getRandomSeed(rng=Math.random)src/modules/twentyq/state.js— KV load/save/clear + stats recordingsrc/modules/twentyq/prompts.js—buildSystemPrompt(state)+ANSWER_FUNCTION_SCHEMAsrc/modules/twentyq/README.md— initial doc stub (filled out fully in phase 4)
Edit
src/modules/index.js— addtwentyq: () => import("./twentyq/index.js")wrangler.toml— append,twentyqtoMODULES.env.deploy.example— append,twentyqto documentedMODULESline
Implementation steps
- Create
src/modules/twentyq/seeds.js:- Export
SEEDSarray of{ category, target, initialHint }. Lowercasetarget. Initial hint must NOT contain target word or close cognates. - Export
getRandomSeed(rng = Math.random)returning one entry;rngparam enables deterministic tests.
- Export
- Create
src/modules/twentyq/state.js:- Constants:
GAME_TTL_SECONDS = 7 * 24 * 3600. gameKey(subject) => "game:" + subject,statsKey(subject) => "stats:" + subject.loadGame,saveGame,clearGame,loadStats— direct mirror ofdoantu/state.js, butturns[]instead ofguesses[].recordResult(db, subject, { solved, turnCount })— increments stats, tracksbestTurnCount(lowest among solved rounds).
- Constants:
- Create
src/modules/twentyq/prompts.js:buildSystemPrompt(state)— string template that injects:secret,category,initialHint, last 5 turns of{question, answer, hint}. Tells the model: judge truthfulness, setis_guesswhen input names a specific concrete noun matching/close tosecret, never revealsecretunlessis_guess && answer==="yes".ANSWER_FUNCTION_SCHEMA— JSON schema forsubmit_answertool withis_guess: boolean,answer: "yes"|"no",hint: string(max 120 chars).
- Create
src/modules/twentyq/index.js:- Minimal scaffold:
{ name: "twentyq", commands: [] }+ JSDoc header. - Phase 3 expands with real handlers.
- Minimal scaffold:
- Edit
src/modules/index.js— add the lazy loader line. - Edit
wrangler.toml[vars] MODULES— append,twentyq. - Edit
.env.deploy.example— match the comment update. - Run
npm run lintandnpx vitest runto confirm scaffold doesn't break anything (registry conflict check, etc.).
Todo list
seeds.js— SEEDS array + getRandomSeedstate.js— KV layer mirroring doantu pattern, withturns[]shapeprompts.js— system prompt builder + function schemaindex.js— minimal{ name, commands: [] }scaffold- Update
src/modules/index.jsregistry - Update
wrangler.tomlMODULES var - Update
.env.deploy.exampleMODULES comment npm run lint+npx vitest runpass
Success criteria
- New module loads without registry errors.
npx vitest runexits 0 (no new tests yet, but no regressions).npm run lintclean.wrangler devboots and/helpshows no twentyq commands yet (zero commands registered — expected).
Risk assessment
- Seed quality — if initial hints are too revealing or too vague, gameplay feels off. Mitigation: hand-curate; revise after manual test in phase 3.
- MODULES var drift —
wrangler.tomland.env.deployMUST match. Doc the requirement in commit message.
Security considerations
- Seeds live in source — no PII, no secrets.
- KV writes scoped to
twentyq:prefix viacreateStore— cannot leak across modules.
Next steps
→ Phase 02 — wrap Workers AI binding into a typed client + add input validator.