Document question authoring workflow

This commit is contained in:
2026-06-27 11:22:16 +03:00
parent 9e19931d8b
commit 85168b8d1e
3 changed files with 189 additions and 5 deletions
+15 -5
View File
@@ -28,12 +28,17 @@ npm run check # проверка типов (svelte-check)
Чем дороже карточка, тем сложнее вопрос.
3. Ведущий открывает вопрос, затем «Показать ответ», и решает:
- **Засчитать +N** — команде идут очки;
- **Засчитать частично +N/2** — для «почти правильного» ответа ведущий может
дать половину стоимости вопроса;
- **Не засчитывать** — без очков, но вопрос закрыт.
- Штрафов и минусов нет.
4. Для творческих вопросов (type `creative`) кнопка мягче:
4. У некоторых вопросов есть необязательная **подсказка** 💡. Ведущий может
показать её по желанию. Если подсказка показана — ответ засчитывается только
за половину стоимости: мягкая помощь без давления.
5. Для творческих вопросов (type `creative`) кнопка мягче:
«Засчитать творческий ответ +N».
5. Сыгранный вопрос затемняется и больше не выбирается.
6. Когда сыграны все вопросы — экран результата с мягкой финальной фразой.
6. Сыгранный вопрос затемняется и больше не выбирается.
7. Когда сыграны все вопросы — экран результата с мягкой финальной фразой.
Прогресс сохраняется автоматически — после перезагрузки игра продолжится с того же места.
@@ -41,6 +46,11 @@ npm run check # проверка типов (svelte-check)
Архитектура задумана так, чтобы наборы добавлялись без правок UI.
**Структура набора:** ровно **5 категорий**, в каждой ровно **5 вопросов** с
номиналами **100 / 200 / 300 / 500 / 1000** (от простого к сложному). Вопросы
бывают `strict` (однозначный ответ) и `creative` (творческий). У вопроса может
быть необязательное поле `optionalHint` — подсказка для ведущего.
1. Создайте файл `src/lib/question-sets/<name>.ts`:
```ts
@@ -57,14 +67,14 @@ npm run check # проверка типов (svelte-check)
title: 'Категория',
questions: [
{ id: 'q1', points: 100, question: '...', answer: '...', type: 'strict' },
{ id: 'q2', points: 200, question: '...', answer: '...', type: 'strict' },
{ id: 'q2', points: 200, question: '...', answer: '...', optionalHint: '...', type: 'strict' },
{ id: 'q3', points: 300, question: '...', answer: '...', type: 'strict' },
{ id: 'q4', points: 500, question: '...', answer: '...', type: 'creative' },
{ id: 'q5', points: 1000, question: '...', answer: '...', type: 'strict' }
// ровно 5 вопросов с очками 100/200/300/500/1000
]
}
// …остальные категории
// …ещё 4 категории (всего 5)
]
};
```
+1
View File
@@ -10,6 +10,7 @@ Shared references for Codex and other AI coding agents in this project.
| Autonomous agent harness | [autonomous-agent-harness.md](autonomous-agent-harness.md) |
| Worker loop command | [scripts/run-agent-loop.mjs](scripts/run-agent-loop.mjs) |
| Pi worker wrapper | [scripts/run-pi-worker.mjs](scripts/run-pi-worker.mjs) |
| Question authoring | [question-authoring.md](question-authoring.md) |
Use `npm run agent:loop -- --task "<concrete task>"` when Codex delegates
implementation to Pi. Codex remains the orchestrator, verifier, reviewer, and
@@ -0,0 +1,173 @@
# Question Authoring Guide (for AI agents)
How to create or extend a question set for the calm family quiz **without
touching code, state, or UI**. Question sets are pure data files.
> You are a bounded worker. Read [AGENTS.md](../../../AGENTS.md) and
> [autonomous-agent-harness.md](autonomous-agent-harness.md) first. This guide
> covers **content data only**. Do not change UI components, the game store, the
> types (beyond what content needs — usually nothing), or add backend / auth /
> editor / multiplayer unless your task explicitly asks.
## TL;DR workflow
1. Create `src/lib/question-sets/<name>.ts` exporting a `QuestionSet`.
2. Register it in `src/lib/question-sets/index.ts` (import + add to the
`questionSets` array).
3. Run `npm run check` to confirm types compile.
4. (Only if your task asks) smoke-test in the browser with `npm run dev`.
The selector screen lists sets from `questionSets` automatically — no UI edit
needed.
## Hard structure rules (non-negotiable)
Every set must match this shape **exactly**:
- **Exactly 5 categories** per set (`set.categories.length === 5`).
- **Exactly 5 questions** per category.
- **Fixed point values per category, in order:** `100, 200, 300, 500, 1000`.
- These values come from `POINT_VALUES` in `src/lib/types.ts`. Never invent
other numbers (no 400, no 750).
## Exact data shape
Mirror of `Question` / `Category` / `QuestionSet` in `src/lib/types.ts`:
```ts
import type { QuestionSet } from '$lib/types';
export const mySet: QuestionSet = {
id: 'my-set', // globally unique, kebab-case, stable forever
title: 'Моя тема',
description: 'Короткое тёплое описание.',
ageRange: '6+ лет',
categories: [
{
id: 'cat-1', // unique WITHIN this set, stable
title: 'Категория',
questions: [
{ id: 'q1', points: 100, question: '…', answer: '…', type: 'strict' },
{ id: 'q2', points: 200, question: '…', answer: '…', optionalHint: '…', type: 'strict' },
{ id: 'q3', points: 300, question: '…', answer: '…', type: 'strict' },
{ id: 'q4', points: 500, question: '…', answer: '…', type: 'creative' },
{ id: 'q5', points: 1000, question: '…', answer: '…', type: 'strict' }
]
}
// …4 more categories (5 total)
]
};
```
## Unique ids (stability matters)
- `set.id` — unique across **all** sets (e.g. `animals`, `cozy`, `space`).
Lowercase kebab-case. It is persisted in `localStorage`, so never rename it
after release.
- `category.id` — unique **within the set**. Stable. Used in the played-question
key `categoryId:questionId`.
- `question.id` — unique **within the category** (typically `q1..q5`). Stable.
Changing any id after release breaks saved progress for players mid-game. Treat
ids as immutable once shipped.
## Difficulty progression (100 → 1000)
Within each category, questions run simple → hard. The number only labels it;
the **wording** carries the difficulty:
- **100** — obvious warm-up; most answer instantly.
- **200** — easy; may need a second's thought.
- **300** — solid mid-level.
- **500** — needs real knowledge or reasoning.
- **1000** — the hard capstone; satisfying to land.
## Question type: `strict` vs `creative`
From `QuestionType` in `src/lib/types.ts`:
- `strict` — there is one correct answer. Host button reads «Засчитать».
Use for facts: «Какое животное…?», «Где живут белые медведи?».
- `creative` — open-ended, many acceptable answers. Host button reads
«Засчитать творческий ответ» (softer framing, no "wrong" feeling). Use for
imagination prompts: «Придумай…», «Опиши…».
A category of facts is mostly/entirely `strict`; a category of imagination is
mostly/entirely `creative`. A set can mix freely. The example sets
(`animals.ts`, `cozy.ts`) keep roughly one `creative` category per set for
balance.
## `optionalHint` — scaffold, don't reveal
`optionalHint?: string` is an optional nudge the host can show on demand.
- **Why it costs points:** showing a hint means the answer can then only be
awarded at **half** value (see Boundaries). So it's a fair, pressure-free
trade — omit it whenever no scaffold genuinely helps.
- **Do** point at a category, sense, rhyme, or well-known association:
- «Его часто называют царём зверей.» (for «Лев»)
- «Мама у него — кобыла, а папа — жеребец.» (for «Жеребёнок»)
- **Don't** restate or obviously give away the answer:
- ❌ «Это лев.»
- ❌ «Ответ начинается на букву „Л“.»
- **For `creative` questions**, the hint can loosen imagination instead of
narrowing it («Чем нелепее и добрее — тем лучше.»).
- Keep it to **one short sentence**. Omit the field entirely when no scaffold
helps — it is genuinely optional.
## Tone: calm, warm, Russian, family-friendly
All user-facing text (`title`, `description`, `question`, `answer`,
`optionalHint`) is in **Russian** and must stay calm and family-safe:
- Soft, friendly phrasing; no words implying timers, pressure, or rush.
- No scary, crude, political, or adult content.
- Friendly to kids and grandparents together; avoid slang that ages badly.
- Match the cozy register of the existing sets (`animals.ts`, `cozy.ts`).
When unsure, read the existing sets and mirror their voice. Aim for a warm
bedtime-evening mood.
## Register the set
In `src/lib/question-sets/index.ts`:
```ts
import { mySet } from './my-set';
export const questionSets = [animalsSet, cozySet, mySet];
```
Order in the array is the order on the selector screen — new sets usually go
last.
## Validation checklist (run before reporting done)
- [ ] File is `src/lib/question-sets/<name>.ts`; its export is a `QuestionSet`.
- [ ] `set.id` is unique across all sets and kebab-case.
- [ ] Exactly 5 categories; each `category.id` unique within the set.
- [ ] Exactly 5 questions per category; each `question.id` unique within its category.
- [ ] Each category's points are exactly `100, 200, 300, 500, 1000` in order.
- [ ] Every question has `type` of `strict` or `creative`.
- [ ] `optionalHint` (if present) scaffolds without revealing the answer; ≤ 1 sentence.
- [ ] All copy is Russian, calm, family-friendly; matches the existing sets' tone.
- [ ] Set is imported and added to `questionSets` in `index.ts`.
- [ ] `npm run check` passes (types + svelte-check).
## Boundaries — do NOT (unless your task explicitly says so)
- Add backend, database, auth, accounts, or multiplayer.
- Add a question editor UI or any new component / route.
- Edit the game store (`src/lib/stores/game.ts`) or the `GameState` shape.
- Change scoring, hint, or partial-credit mechanics — they already exist and are
fixed by the UI/state contract:
- Full points: host «Засчитать +N».
- Half points: host «Засчитать частично +N/2» (shown only when the hint was
**not** revealed; `halfPoints = Math.round(points / 2)`).
- Hint revealed → the answer can only be awarded at half value, and the
partial-credit button is hidden.
- Refactor `src/lib/types.ts` beyond what content needs (usually nothing).
- Commit, push, deploy, or edit `AGENTS.md` and other files under
`development/_reference/ai/`.
If a needed change falls outside these boundaries, stop and report it; do not
expand scope on your own.