DEV Community

Cover image for πŸ€–How to make AI follow your instructions more for free (OpenSpec)πŸ“
Web Developer Hyper
Web Developer Hyper

Posted on

πŸ€–How to make AI follow your instructions more for free (OpenSpec)πŸ“

Intro

I have been using AI tools such as Claude Code and Cursor at work for a while. And I learned that Serena MCP can make AI work more efficiently.

🧠How to use AI more efficiently for free (Serena MCP)🧐
https://dev.to/webdeveloperhyper/how-to-use-ai-more-efficiently-for-free-serena-mcp-5gj6

However, there are still problems using them. It is that they don't follow my instructions, like small children, even when I write specific markdown files for them.πŸ™… I spent a long time making a detailed TODO list before coding, but when they started coding, they just ignored the TODO list and messed up the code.🀦 So, my long journey to make AI follow my words began.πŸš€

What is OpenSpec

I looked for a tool to solve this problem and learned the term Spec-Driven Development. To explain it briefly, it means making a detail specification document before AI starts coding. GitHub Spec Kit and AWS Kiro are well-known for this approach. But I finally chose OpenSpec because it looked simpler and easier. Maybe, I'll try the others another time and compare the differences.

There are only 3 main steps to use OpenSpec, Proposal, Apply, and Archive.
1️⃣ Proposal: Make a markdown specification document for AI to code.
2️⃣ Apply: AI codes according to markdown specification document.
3️⃣ Archive: Archive the markdown specification document when the tasks are finished.

You can use OpenSpec with Claude Code, Codex, Cursor, Cline and other AI tools. Also, OpenSpec is an Open Source Software (OSS), so you can download from GitHub and use it for free.πŸ€‘ You can use OpenSpec for both purposes, making new features (0 β†’ 1) and updating existing features (1 β†’ n).

OpenSpec official website ↓
https://github.com/Fission-AI/OpenSpec

How to use OpenSpec

Let's try OpenSpec step by step and see how it works. I'll use my brain training app, Muscle Brain, as an example. This time, I'd like to add a Japanese version to the app since it currently only has an English version.

Install

npm install -g @fission-ai/openspec@latest
Enter fullscreen mode Exit fullscreen mode

Initialization

openspec init
Enter fullscreen mode Exit fullscreen mode

↓ Select which AI tool to use.

Openspec folder and file below are made in your project.
↓ openspec\AGENTS.md

# OpenSpec Instructions

Instructions for AI coding assistants using OpenSpec for spec-driven development.

## TL;DR Quick Checklist

- Search existing work: `openspec spec list --long`, `openspec list` (use `rg` only for full-text search)
- Decide scope: new capability vs modify existing capability
- Pick a unique `change-id`: kebab-case, verb-led (`add-`, `update-`, `remove-`, `refactor-`)
- Scaffold: `proposal.md`, `tasks.md`, `design.md` (only if needed), and delta specs per affected capability
- Write deltas: use `## ADDED|MODIFIED|REMOVED|RENAMED Requirements`; include at least one `#### Scenario:` per requirement
- Validate: `openspec validate [change-id] --strict` and fix issues
- Request approval: Do not start implementation until proposal is approved

## Three-Stage Workflow

### Stage 1: Creating Changes
Create proposal when you need to:
- Add features or functionality
- Make breaking changes (API, schema)
- Change architecture or patterns  
- Optimize performance (changes behavior)
- Update security patterns

Triggers (examples):
- "Help me create a change proposal"
- "Help me plan a change"
- "Help me create a proposal"
- "I want to create a spec proposal"
- "I want to create a spec"

Loose matching guidance:
- Contains one of: `proposal`, `change`, `spec`
- With one of: `create`, `plan`, `make`, `start`, `help`

Skip proposal for:
- Bug fixes (restore intended behavior)
- Typos, formatting, comments
- Dependency updates (non-breaking)
- Configuration changes
- Tests for existing behavior

**Workflow**
1. Review `openspec/project.md`, `openspec list`, and `openspec list --specs` to understand current context.
2. Choose a unique verb-led `change-id` and scaffold `proposal.md`, `tasks.md`, optional `design.md`, and spec deltas under `openspec/changes/<id>/`.
3. Draft spec deltas using `## ADDED|MODIFIED|REMOVED Requirements` with at least one `#### Scenario:` per requirement.
4. Run `openspec validate <id> --strict` and resolve any issues before sharing the proposal.

### Stage 2: Implementing Changes
Track these steps as TODOs and complete them one by one.
1. **Read proposal.md** - Understand what's being built
2. **Read design.md** (if exists) - Review technical decisions
3. **Read tasks.md** - Get implementation checklist
4. **Implement tasks sequentially** - Complete in order
5. **Confirm completion** - Ensure every item in `tasks.md` is finished before updating statuses
6. **Update checklist** - After all work is done, set every task to `- [x]` so the list reflects reality
7. **Approval gate** - Do not start implementation until the proposal is reviewed and approved

### Stage 3: Archiving Changes
After deployment, create separate PR to:
- Move `changes/[name]/` β†’ `changes/archive/YYYY-MM-DD-[name]/`
- Update `specs/` if capabilities changed
- Use `openspec archive [change] --skip-specs --yes` for tooling-only changes
- Run `openspec validate --strict` to confirm the archived change passes checks

## Before Any Task

**Context Checklist:**
- [ ] Read relevant specs in `specs/[capability]/spec.md`
- [ ] Check pending changes in `changes/` for conflicts
- [ ] Read `openspec/project.md` for conventions
- [ ] Run `openspec list` to see active changes
- [ ] Run `openspec list --specs` to see existing capabilities

**Before Creating Specs:**
- Always check if capability already exists
- Prefer modifying existing specs over creating duplicates
- Use `openspec show [spec]` to review current state
- If request is ambiguous, ask 1–2 clarifying questions before scaffolding

### Search Guidance
- Enumerate specs: `openspec spec list --long` (or `--json` for scripts)
- Enumerate changes: `openspec list` (or `openspec change list --json` - deprecated but available)
- Show details:
  - Spec: `openspec show <spec-id> --type spec` (use `--json` for filters)
  - Change: `openspec show <change-id> --json --deltas-only`
- Full-text search (use ripgrep): `rg -n "Requirement:|Scenario:" openspec/specs`

## Quick Start

### CLI Commands

bash
# Essential commands
openspec list                  # List active changes
openspec list --specs          # List specifications
openspec show [item]           # Display change or spec
openspec diff [change]         # Show spec differences
openspec validate [item]       # Validate changes or specs
openspec archive [change] [--yes|-y]      # Archive after deployment (add --yes for non-interactive runs)

# Project management
openspec init [path]           # Initialize OpenSpec
openspec update [path]         # Update instruction files

# Interactive mode
openspec show                  # Prompts for selection
openspec validate              # Bulk validation mode

# Debugging
openspec show [change] --json --deltas-only
openspec validate [change] --strict

### Command Flags

- `--json` - Machine-readable output
- `--type change|spec` - Disambiguate items
- `--strict` - Comprehensive validation
- `--no-interactive` - Disable prompts
- `--skip-specs` - Archive without spec updates
- `--yes`/`-y` - Skip confirmation prompts (non-interactive archive)

## Directory Structure

openspec/
β”œβ”€β”€ project.md              # Project conventions
β”œβ”€β”€ specs/                  # Current truth - what IS built
β”‚   └── [capability]/       # Single focused capability
β”‚       β”œβ”€β”€ spec.md         # Requirements and scenarios
β”‚       └── design.md       # Technical patterns
β”œβ”€β”€ changes/                # Proposals - what SHOULD change
β”‚   β”œβ”€β”€ [change-name]/
β”‚   β”‚   β”œβ”€β”€ proposal.md     # Why, what, impact
β”‚   β”‚   β”œβ”€β”€ tasks.md        # Implementation checklist
β”‚   β”‚   β”œβ”€β”€ design.md       # Technical decisions (optional; see criteria)
β”‚   β”‚   └── specs/          # Delta changes
β”‚   β”‚       └── [capability]/
β”‚   β”‚           └── spec.md # ADDED/MODIFIED/REMOVED
β”‚   └── archive/            # Completed changes

## Creating Change Proposals

### Decision Tree

New request?
β”œβ”€ Bug fix restoring spec behavior? β†’ Fix directly
β”œβ”€ Typo/format/comment? β†’ Fix directly  
β”œβ”€ New feature/capability? β†’ Create proposal
β”œβ”€ Breaking change? β†’ Create proposal
β”œβ”€ Architecture change? β†’ Create proposal
└─ Unclear? β†’ Create proposal (safer)

### Proposal Structure

1. **Create directory:** `changes/[change-id]/` (kebab-case, verb-led, unique)

2. **Write proposal.md:**
markdown
## Why
[1-2 sentences on problem/opportunity]

## What Changes
- [Bullet list of changes]
- [Mark breaking changes with **BREAKING**]

## Impact
- Affected specs: [list capabilities]
- Affected code: [key files/systems]

3. **Create spec deltas:** `specs/[capability]/spec.md`
markdown
## ADDED Requirements
### Requirement: New Feature
The system SHALL provide...

#### Scenario: Success case
- **WHEN** user performs action
- **THEN** expected result

## MODIFIED Requirements
### Requirement: Existing Feature
[Complete modified requirement]

## REMOVED Requirements
### Requirement: Old Feature
**Reason**: [Why removing]
**Migration**: [How to handle]

If multiple capabilities are affected, create multiple delta files under `changes/[change-id]/specs/<capability>/spec.md`β€”one per capability.

4. **Create tasks.md:**
markdown
## 1. Implementation
- [ ] 1.1 Create database schema
- [ ] 1.2 Implement API endpoint
- [ ] 1.3 Add frontend component
- [ ] 1.4 Write tests

5. **Create design.md when needed:**
Create `design.md` if any of the following apply; otherwise omit it:
- Cross-cutting change (multiple services/modules) or a new architectural pattern
- New external dependency or significant data model changes
- Security, performance, or migration complexity
- Ambiguity that benefits from technical decisions before coding

Minimal `design.md` skeleton:
markdown
## Context
[Background, constraints, stakeholders]

## Goals / Non-Goals
- Goals: [...]
- Non-Goals: [...]

## Decisions
- Decision: [What and why]
- Alternatives considered: [Options + rationale]

## Risks / Trade-offs
- [Risk] β†’ Mitigation

## Migration Plan
[Steps, rollback]

## Open Questions
- [...]

## Spec File Format

### Critical: Scenario Formatting

**CORRECT** (use #### headers):
markdown
#### Scenario: User login success
- **WHEN** valid credentials provided
- **THEN** return JWT token

**WRONG** (don't use bullets or bold):
markdown
- **Scenario: User login**  ❌
**Scenario**: User login     ❌
### Scenario: User login      ❌

Every requirement MUST have at least one scenario.

### Requirement Wording
- Use SHALL/MUST for normative requirements (avoid should/may unless intentionally non-normative)

### Delta Operations

- `## ADDED Requirements` - New capabilities
- `## MODIFIED Requirements` - Changed behavior
- `## REMOVED Requirements` - Deprecated features
- `## RENAMED Requirements` - Name changes

Headers matched with `trim(header)` - whitespace ignored.

#### When to use ADDED vs MODIFIED
- ADDED: Introduces a new capability or sub-capability that can stand alone as a requirement. Prefer ADDED when the change is orthogonal (e.g., adding "Slash Command Configuration") rather than altering the semantics of an existing requirement.
- MODIFIED: Changes the behavior, scope, or acceptance criteria of an existing requirement. Always paste the full, updated requirement content (header + all scenarios). The archiver will replace the entire requirement with what you provide here; partial deltas will drop previous details.
- RENAMED: Use when only the name changes. If you also change behavior, use RENAMED (name) plus MODIFIED (content) referencing the new name.

Common pitfall: Using MODIFIED to add a new concern without including the previous text. This causes loss of detail at archive time. If you aren’t explicitly changing the existing requirement, add a new requirement under ADDED instead.

Authoring a MODIFIED requirement correctly:
1) Locate the existing requirement in `openspec/specs/<capability>/spec.md`.
2) Copy the entire requirement block (from `### Requirement: ...` through its scenarios).
3) Paste it under `## MODIFIED Requirements` and edit to reflect the new behavior.
4) Ensure the header text matches exactly (whitespace-insensitive) and keep at least one `#### Scenario:`.

Example for RENAMED:
markdown
## RENAMED Requirements
- FROM: `### Requirement: Login`
- TO: `### Requirement: User Authentication`

## Troubleshooting

### Common Errors

**"Change must have at least one delta"**
- Check `changes/[name]/specs/` exists with .md files
- Verify files have operation prefixes (## ADDED Requirements)

**"Requirement must have at least one scenario"**
- Check scenarios use `#### Scenario:` format (4 hashtags)
- Don't use bullet points or bold for scenario headers

**Silent scenario parsing failures**
- Exact format required: `#### Scenario: Name`
- Debug with: `openspec show [change] --json --deltas-only`

### Validation Tips

bash
# Always use strict mode for comprehensive checks
openspec validate [change] --strict

# Debug delta parsing
openspec show [change] --json | jq '.deltas'

# Check specific requirement
openspec show [spec] --json -r 1

## Happy Path Script

bash
# 1) Explore current state
openspec spec list --long
openspec list
# Optional full-text search:
# rg -n "Requirement:|Scenario:" openspec/specs
# rg -n "^#|Requirement:" openspec/changes

# 2) Choose change id and scaffold
CHANGE=add-two-factor-auth
mkdir -p openspec/changes/$CHANGE/{specs/auth}
printf "## Why\n...\n\n## What Changes\n- ...\n\n## Impact\n- ...\n" > openspec/changes/$CHANGE/proposal.md
printf "## 1. Implementation\n- [ ] 1.1 ...\n" > openspec/changes/$CHANGE/tasks.md

# 3) Add deltas (example)
cat > openspec/changes/$CHANGE/specs/auth/spec.md << 'EOF'
## ADDED Requirements
### Requirement: Two-Factor Authentication
Users MUST provide a second factor during login.

#### Scenario: OTP required
- **WHEN** valid credentials are provided
- **THEN** an OTP challenge is required
EOF

# 4) Validate
openspec validate $CHANGE --strict

## Multi-Capability Example

openspec/changes/add-2fa-notify/
β”œβ”€β”€ proposal.md
β”œβ”€β”€ tasks.md
└── specs/
    β”œβ”€β”€ auth/
    β”‚   └── spec.md   # ADDED: Two-Factor Authentication
    └── notifications/
        └── spec.md   # ADDED: OTP email notification

auth/spec.md
markdown
## ADDED Requirements
### Requirement: Two-Factor Authentication
...

notifications/spec.md
markdown
## ADDED Requirements
### Requirement: OTP Email Notification
...

## Best Practices

### Simplicity First
- Default to <100 lines of new code
- Single-file implementations until proven insufficient
- Avoid frameworks without clear justification
- Choose boring, proven patterns

### Complexity Triggers
Only add complexity with:
- Performance data showing current solution too slow
- Concrete scale requirements (>1000 users, >100MB data)
- Multiple proven use cases requiring abstraction

### Clear References
- Use `file.ts:42` format for code locations
- Reference specs as `specs/auth/spec.md`
- Link related changes and PRs

### Capability Naming
- Use verb-noun: `user-auth`, `payment-capture`
- Single purpose per capability
- 10-minute understandability rule
- Split if description needs "AND"

### Change ID Naming
- Use kebab-case, short and descriptive: `add-two-factor-auth`
- Prefer verb-led prefixes: `add-`, `update-`, `remove-`, `refactor-`
- Ensure uniqueness; if taken, append `-2`, `-3`, etc.

## Tool Selection Guide

| Task | Tool | Why |
|------|------|-----|
| Find files by pattern | Glob | Fast pattern matching |
| Search code content | Grep | Optimized regex search |
| Read specific files | Read | Direct file access |
| Explore unknown scope | Task | Multi-step investigation |

## Error Recovery

### Change Conflicts
1. Run `openspec list` to see active changes
2. Check for overlapping specs
3. Coordinate with change owners
4. Consider combining proposals

### Validation Failures
1. Run with `--strict` flag
2. Check JSON output for details
3. Verify spec file format
4. Ensure scenarios properly formatted

### Missing Context
1. Read project.md first
2. Check related specs
3. Review recent archives
4. Ask for clarification

## Quick Reference

### Stage Indicators
- `changes/` - Proposed, not yet built
- `specs/` - Built and deployed
- `archive/` - Completed changes

### File Purposes
- `proposal.md` - Why and what
- `tasks.md` - Implementation steps
- `design.md` - Technical decisions
- `spec.md` - Requirements and behavior

### CLI Essentials
bash
openspec list              # What's in progress?
openspec show [item]       # View details
openspec diff [change]     # What's changing?
openspec validate --strict # Is it correct?
openspec archive [change] [--yes|-y]  # Mark complete (add --yes for automation)

Remember: Specs are truth. Changes are proposals. Keep them in sync.
`
Enter fullscreen mode Exit fullscreen mode

↓ openspec\project.md (before)

# Project Context

## Purpose
[Describe your project's purpose and goals]

## Tech Stack
- [List your primary technologies]
- [e.g., TypeScript, React, Node.js]

## Project Conventions

### Code Style
[Describe your code style preferences, formatting rules, and naming conventions]

### Architecture Patterns
[Document your architectural decisions and patterns]

### Testing Strategy
[Explain your testing approach and requirements]

### Git Workflow
[Describe your branching strategy and commit conventions]

## Domain Context
[Add domain-specific knowledge that AI assistants need to understand]

## Important Constraints
[List any technical, business, or regulatory constraints]

## External Dependencies
[Document key external services, APIs, or systems]
Enter fullscreen mode Exit fullscreen mode

openspec/project.md is a template and not completed, so ask your AI to fill it out.

Please read openspec/project.md and help me fill out with details about my project, tech stack, and conventions.
Enter fullscreen mode Exit fullscreen mode

↓ openspec\project.md (after)

# Project Context

## Purpose
**Muscle Brain** is a web-based cognitive training game designed to improve working memory through N-back style digit manipulation exercises. The game features "Mr. Brain," an animated character who lifts weights rhythmically to motivate players during training sessions.

**Key Goals:**
- Provide scientifically-validated working memory training
- Offer an engaging alternative to traditional N-back exercises
- Support progressive difficulty levels for continuous improvement
- Deliver smooth, responsive UX with keyboard navigation
- Track player progress and personal best scores

## Tech Stack

### Core Framework
- **Next.js 15.5.3** (App Router)
- **React 19.1.0**
- **TypeScript 5** (strict mode)

### UI & Styling
- **KendoReact v12** - Enterprise UI component library
  - Buttons, Inputs, Dialogs, Dropdowns, Grid, Layout, Notification
- **Tailwind CSS v4** - Utility-first styling
- **CSS-in-JS** - Component-specific styles

### Additional Libraries
- **canvas-confetti** - Celebration animations
- **@tailwindcss/postcss** - PostCSS integration

### Development
- **TypeScript strict mode** enabled
- **ES2017** target for modern browser support
- Path aliases: `@/*` β†’ `./src/*`

## Project Conventions

### Code Style
- **TypeScript strict mode** - No implicit any, strict null checks
- **Path aliases** - Use `@/` for imports from `src/` directory
- **Component organization** - React functional components with hooks
- **Naming conventions:**
  - PascalCase for components and types
  - camelCase for functions and variables
  - SCREAMING_SNAKE_CASE for constants

### Architecture Patterns
- **Next.js App Router** - File-system based routing in `src/app/`
- **Component-based architecture** - Reusable React components
- **State management** - React hooks (useState, useEffect)
- **Client-side only game logic** - No server state required
- **Monolithic page structure** - Single-page game in `page.tsx`

### Testing Strategy
Currently no automated testing infrastructure. Future consideration for:
- Jest + React Testing Library for component tests
- E2E tests for game flow validation

### Git Workflow
- **Main branch:** `master`
- **Commit style:** Descriptive messages focusing on "why" over "what"
- Recent commits show iterative feature development approach

## Domain Context

### N-back Cognitive Training
This game implements a variant of the **N-back task**, a scientifically validated method for improving working memory. Benefits include:
- Increased working memory capacity
- Improved fluid intelligence
- Enhanced attention and focus
- Better performance in complex cognitive tasks

### Game Mechanics
**Shift Operations:**
- **Left Shift:** Remove leftmost digit(s), add new digit(s) at the end
- **Right Shift:** Remove rightmost digit(s), add new digit(s) at the beginning
- **Shift Count:** Number of digits to shift (1-3)

**Game Modes:**
- **Practice Mode:** Shows correct answer, no scoring
- **Normal Mode:** No hints, tracks score and best score

**Difficulty Settings:**
- Number Length: 4-8 digits
- Shift Direction: Left or Right
- Shift Count: 1-3 digits per shift

**Example:** `2548` + Left Shift 1 + `3` β†’ `5483`

### User Experience Patterns
- **Auto-submit** - Game submits when number is complete
- **Auto-focus** - Input field always ready
- **Keyboard navigation** - Space for toggles, Enter to start
- **Monospace fonts** - For number clarity
- **1-second rhythm** - Mr. Brain animation timing

## Important Constraints
- **Browser-only** - No backend required, all logic client-side
- **Session-based scoring** - Best scores don't persist across browser sessions
- **No authentication** - Single-player experience only
- **Responsive design** - Must work on mobile and desktop
- **Performance** - Smooth animations at 60fps critical for UX

## External Dependencies
- **KendoReact** - Commercial UI library (requires license for production)
- **Next.js** - Framework updates may require migration effort
- **React 19** - Latest version, may have breaking changes from v18
Enter fullscreen mode Exit fullscreen mode

↓ AGENTS.md

<!-- OPENSPEC:START -->
# OpenSpec Instructions

These instructions are for AI assistants working in this project.

Always open `@/openspec/AGENTS.md` when the request:
- Mentions planning or proposals (words like proposal, spec, change, plan)
- Introduces new capabilities, breaking changes, architecture shifts, or big performance/security work
- Sounds ambiguous and you need the authoritative spec before coding

Use `@/openspec/AGENTS.md` to learn:
- How to create and apply change proposals
- Spec format and conventions
- Project structure and guidelines

Keep this managed block so 'openspec update' can refresh the instructions.

<!-- OPENSPEC:END -->
Enter fullscreen mode Exit fullscreen mode

Draf proposal (1️⃣ Proposal)

Ask your AI to create a markdown specification document (proposal) for AI coding.

Create an OpenSpec change proposal for Japanese version.
 (Shortcut for tools with slash commands: /openspec:proposal Create Japanese version)
Enter fullscreen mode Exit fullscreen mode

The files below were made.

↓ openspec\changes\add-japanese-localization\specs\i18n\spec.md

# Internationalization (i18n) Specification

## ADDED Requirements

### Requirement: Language Selection

The system SHALL provide a language selection mechanism allowing users to switch between English and Japanese.

#### Scenario: Initial language detection
- **WHEN** a user visits the application for the first time
- **THEN** the system detects the browser language (navigator.language)
- **AND** defaults to Japanese if browser locale starts with 'ja'
- **AND** defaults to English for all other locales

#### Scenario: Manual language switching
- **WHEN** a user clicks the language toggle control in settings
- **THEN** the UI immediately updates all text to the selected language
- **AND** no page reload occurs
- **AND** the preference is saved to localStorage

#### Scenario: Language preference persistence
- **WHEN** a user returns to the application
- **THEN** their previously selected language is restored from localStorage
- **AND** overrides browser language detection

### Requirement: Translation System

The system SHALL implement a translation system that provides localized text for all user-facing content.

#### Scenario: Translation key lookup
- **WHEN** the application requests a translation key (e.g., "settings.title")
- **THEN** the system returns the text in the current language
- **AND** falls back to English if the key is missing in the selected language

#### Scenario: Dynamic text updates
- **WHEN** the user switches languages
- **THEN** all visible text updates immediately
- **AND** state values (settings, scores) remain unchanged

#### Scenario: Parameterized translations
- **WHEN** a translation requires dynamic values (e.g., "Score: {score}")
- **THEN** the system supports parameter substitution
- **AND** correctly formats the final string

### Requirement: Japanese Translation Quality

The system SHALL provide accurate, natural Japanese translations for all UI text.

#### Scenario: Game instructions translation
- **WHEN** displaying game rules and instructions
- **THEN** Japanese text clearly explains N-back mechanics
- **AND** uses appropriate cognitive training terminology
- **AND** maintains the motivational tone of the original

#### Scenario: Button and label translations
- **WHEN** displaying interactive controls
- **THEN** Japanese labels are concise and actionable
- **AND** match standard Japanese UI conventions
- **AND** fit within existing layout constraints

#### Scenario: Notification messages
- **WHEN** showing success or error notifications
- **THEN** Japanese messages are grammatically correct
- **AND** convey the same level of encouragement as English

### Requirement: UI Integration

The system SHALL integrate language selection into the existing settings interface without disrupting the layout.

#### Scenario: Language toggle placement
- **WHEN** viewing the settings screen
- **THEN** a language toggle control is visible near the top
- **AND** shows current language with flag emoji or label
- **AND** clicking toggles between English and Japanese

#### Scenario: Responsive text layout
- **WHEN** switching to Japanese
- **THEN** all text containers accommodate different string lengths
- **AND** no text overflow or layout breaks occur
- **AND** maintains visual hierarchy and spacing

### Requirement: Performance and Bundle Size

The system SHALL implement i18n without significantly impacting load time or bundle size.

#### Scenario: Translation loading strategy
- **WHEN** the application loads
- **THEN** only the active language dictionary is loaded
- **AND** no external i18n libraries are added
- **AND** translation objects are included in the main bundle

#### Scenario: Language switching performance
- **WHEN** the user switches languages
- **THEN** the UI updates within 100ms
- **AND** no network requests are made
- **AND** no visible lag or flicker occurs

### Requirement: Accessibility

The system SHALL ensure language selection and translated content meet accessibility standards.

#### Scenario: Screen reader support
- **WHEN** using screen readers
- **THEN** language toggle is properly labeled
- **AND** language change is announced
- **AND** translated content maintains semantic structure

#### Scenario: Keyboard navigation
- **WHEN** navigating settings with keyboard
- **THEN** language toggle is reachable via Tab key
- **AND** can be activated with Space or Enter
- **AND** follows existing keyboard interaction patterns
Enter fullscreen mode Exit fullscreen mode

↓ openspec\changes\add-japanese-localization\design.md

# Japanese Localization - Technical Design

## Context

Muscle Brain currently has all UI text hardcoded in English within `src/app/page.tsx`. The application is a client-side React 19 app using Next.js 15 App Router with no backend. Users interact with various text elements including game instructions, button labels, settings, notifications, and dynamic content (e.g., shift instructions).

**Constraints:**
- No backend for translation management
- Must maintain current performance (60fps animations)
- Should not increase bundle size significantly
- Cannot introduce complex dependencies
- Must work offline (PWA-like experience)

**Stakeholders:**
- Japanese-speaking users seeking cognitive training
- English-speaking users (must not regress)
- Future translators (ease of adding more languages)

## Goals / Non-Goals

**Goals:**
- βœ… Support English and Japanese languages
- βœ… Allow runtime language switching without reload
- βœ… Persist user's language preference
- βœ… Auto-detect browser language on first visit
- βœ… Maintain type safety for translation keys
- βœ… Keep implementation simple and maintainable

**Non-Goals:**
- ❌ Support for 10+ languages (optimize for 2-3)
- ❌ Dynamic translation loading from API
- ❌ Pluralization or complex ICU message syntax
- ❌ Right-to-left (RTL) language support
- ❌ Translation management UI for non-developers
- ❌ Server-side rendering (SSR) with locale routing

## Decisions

### 1. No External i18n Library

**Decision:** Build a lightweight custom i18n solution instead of using libraries like `react-i18next`, `next-intl`, or `react-intl`.

**Rationale:**
- Current needs are simple: 2 languages, static translations
- Existing libraries add 20-50KB+ to bundle size
- No need for ICU message formatting, pluralization, or dynamic loading
- Custom solution gives full control and understanding
- Easier to maintain for small team/solo dev

**Alternatives considered:**
- **react-i18next** - Most popular, but 43KB minified. Overkill for 2 languages.
- **next-intl** - Next.js optimized, but adds routing complexity we don't need
- **react-intl** - Powerful, but heavy (47KB) and requires FormatJS ecosystem

**Trade-offs:**
- βž• Minimal bundle increase (~2KB for custom hook + translations)
- βž• No learning curve for library-specific APIs
- βž• Complete control over behavior
- βž– Must implement language switching manually
- βž– No built-in pluralization (not needed currently)

### 2. Translation Storage Strategy

**Decision:** Store translations as TypeScript objects in `src/lib/i18n/translations.ts` with compile-time type checking.

**Structure:**
typescript
// translations.ts
export const translations = {
  en: {
    'game.title': 'Muscle Brain',
    'game.start': 'Start Game',
    'settings.shiftDirection': 'Shift Direction',
    // ...
  },
  ja: {
    'game.title': 'γƒžγƒƒγ‚Ήγƒ«γƒ»γƒ–γƒ¬γ‚€γƒ³',
    'game.start': 'γ‚²γƒΌγƒ ι–‹ε§‹',
    'settings.shiftDirection': 'γ‚·γƒ•γƒˆζ–Ήε‘',
    // ...
  }
} as const

**Rationale:**
- TypeScript `as const` enables autocomplete for translation keys
- All translations bundled at compile time (no runtime loading)
- Easy to review and diff in version control
- Simple to add new languages (just add new key to object)

**Alternatives considered:**
- **JSON files** - Loses type safety, requires dynamic import
- **Separate files per language** - Harder to keep keys in sync
- **Enum-based keys** - Verbose, harder to read in code

**Trade-offs:**
- βž• Full TypeScript type safety
- βž• Autocomplete in IDE
- βž• Compile-time validation of keys
- βž• No runtime errors for missing keys
- βž– All translations loaded upfront (acceptable for 2 languages)
- βž– Requires rebuild to change translations

### 3. Translation Key Naming Convention

**Decision:** Use dot-notation namespacing: `category.subcategory.key`

**Examples:**
- `game.start` - "Start Game"
- `game.practice` - "Practice Mode"
- `settings.title` - "Settings"
- `settings.shiftDirection` - "Shift Direction"
- `notifications.correct` - "Correct!"
- `history.title` - "Best Scores History"

**Rationale:**
- Provides clear organization by feature area
- Easy to find related translations
- Scales well as translation count grows
- Common pattern in i18n libraries

**Trade-offs:**
- βž• Clear organization and discoverability
- βž• Prevents key collisions
- βž– Slightly more verbose than flat keys

### 4. React Hook API for Translations

**Decision:** Implement a `useTranslation()` hook that returns a `t()` function.

**Usage:**
typescript
const { t, language, setLanguage } = useTranslation()

return (
  <Button>{t('game.start')}</Button>
)

**Rationale:**
- Familiar API for developers who've used react-i18next
- Hooks are the React 19 standard for shared state
- Enables type-safe translation function
- Easy to test in isolation

**Alternatives considered:**
- **Context Consumer Component** - More verbose JSX
- **HOC pattern** - Outdated in React 19
- **Global `t()` function** - Doesn't react to language changes

**Trade-offs:**
- βž• Clean, minimal API
- βž• Component re-renders on language change
- βž• Easy to mock in tests
- βž– Must call hook in every component using translations

### 5. Language State Management

**Decision:** Use React Context + localStorage for language state.

**Implementation:**
typescript
// useTranslation.ts
const LanguageContext = createContext<LanguageContextType>(defaultValue)

export function useTranslation() {
  const context = useContext(LanguageContext)

  const t = (key: keyof typeof translations.en) => {
    return translations[context.language][key] || translations.en[key]
  }

  return { t, language: context.language, setLanguage: context.setLanguage }
}

**Rationale:**
- Context provides global state without prop drilling
- localStorage persists preference across sessions
- Simple enough to not need Redux/Zustand
- Follows React best practices

**Alternatives considered:**
- **URL query param** - Loses preference on navigation
- **Global variable** - Doesn't trigger re-renders
- **Redux** - Overkill for single state value

**Trade-offs:**
- βž• Persists across page reloads
- βž• No prop drilling
- βž• Triggers re-renders automatically
- βž– Adds Context provider to component tree
- βž– Must handle localStorage access carefully (SSR-safe)

### 6. Browser Language Detection

**Decision:** Detect browser language on first visit using `navigator.language`.

**Logic:**
typescript
const getDefaultLanguage = (): 'en' | 'ja' => {
  if (typeof window === 'undefined') return 'en'

  const saved = localStorage.getItem('muscleBrain_language')
  if (saved) return saved as 'en' | 'ja'

  const browserLang = navigator.language.toLowerCase()
  return browserLang.startsWith('ja') ? 'ja' : 'en'
}

**Rationale:**
- Provides good UX for Japanese users (no manual selection needed)
- Saved preference takes precedence (user control)
- Falls back to English for unknown locales
- Standard pattern in web apps

**Trade-offs:**
- βž• Seamless experience for Japanese users
- βž• English as safe default
- βž• User choice always wins
- βž– No support for regional variants (ja-JP vs ja)

### 7. Dynamic Content Translation

**Decision:** Support simple parameter substitution for dynamic content.

**Examples:**
typescript
// Translation dictionary
'game.shiftInstruction': '{direction} Shift {count}'
'score.current': 'Score: {score}'

// Usage
t('game.shiftInstruction', { direction: 'Left', count: 1 })
// β†’ "Left Shift 1"

t('score.current', { score: 15 })
// β†’ "Score: 15"

**Implementation:**
typescript
function t(key: string, params?: Record<string, string | number>): string {
  let text = translations[language][key] || translations.en[key]

  if (params) {
    Object.entries(params).forEach(([key, value]) => {
      text = text.replace(`{${key}}`, String(value))
    })
  }

  return text
}

**Rationale:**
- Handles common use cases (scores, settings values)
- Simple regex-free implementation
- No need for complex ICU message syntax

**Trade-offs:**
- βž• Supports most dynamic content needs
- βž• Easy to understand and use
- βž– No pluralization support
- βž– No number/date formatting

## Risks / Trade-offs

### Risk: Japanese Text Overflow

**Mitigation:**
- Review all Japanese translations in UI during testing
- Use shorter synonyms where needed (Japanese can be more concise)
- Add `overflow-wrap: break-word` to text containers as fallback
- Test on mobile (smallest viewport)

### Risk: Translation Quality

**Mitigation:**
- Use native Japanese speaker for initial translation review
- Test with Japanese users for naturalness
- Reference similar cognitive training apps in Japanese market
- Keep translations in version control for easy updates

### Risk: Future Language Additions

**Mitigation:**
- Design translation structure to support additional languages
- Document translation process clearly
- Use TypeScript to enforce key consistency
- Consider external translation files if scaling beyond 3 languages

### Risk: Performance Impact

**Mitigation:**
- Translations are plain objects (minimal overhead)
- No external library bundle size
- Context re-renders only when language changes (rare)
- Measure bundle size before/after

## Migration Plan

### Phase 1: Infrastructure (No User Impact)
1. Create `src/lib/i18n/` directory and files
2. Add English translations (matching current text)
3. Implement `useTranslation` hook
4. Add LanguageContext provider to root

### Phase 2: Gradual UI Migration
1. Replace strings in non-critical areas (settings labels)
2. Replace game instructions
3. Replace notifications and dynamic content
4. Replace remaining strings

### Phase 3: Japanese Translation
1. Add Japanese translation dictionary
2. Professional translation review
3. UI layout testing

### Phase 4: Language Selection UI
1. Add language toggle to settings
2. Implement localStorage persistence
3. Add browser language detection

### Rollback Plan
If issues arise:
1. All changes are in feature branch (not deployed)
2. Can revert commits before merge
3. No database migrations or API changes
4. Worst case: remove LanguageContext, revert to hardcoded English

## Open Questions

- **Q:** Should we support more languages in the future (Chinese, Korean, Spanish)?
  - **A:** Design allows it, but wait for user demand before investing in translations

- **Q:** Should language preference sync across devices?
  - **A:** No - requires backend. localStorage per-device is acceptable.

- **Q:** How to handle Japanese character width in monospace number displays?
  - **A:** Number displays use digits only (universal). Only labels/instructions affected.

- **Q:** Should we provide English/Japanese side-by-side for learning?
  - **A:** Out of scope. Users pick one language at a time.
Enter fullscreen mode Exit fullscreen mode

↓ openspec\changes\add-japanese-localization\proposal.md

# Add Japanese Localization

## Why

Muscle Brain aims to reach Japanese-speaking users who want to improve their working memory through cognitive training. Adding Japanese language support will make the game accessible and usable for this audience, expanding the user base beyond English speakers.

## What Changes

- **Add i18n framework** - Implement lightweight internationalization system with language detection and switching
- **Create language selection UI** - Add language toggle in settings area (English ⇔ Japanese)
- **Translate all UI text** - Provide complete Japanese translations for:
  - Game instructions and rules
  - Button labels and actions
  - Settings labels and options
  - Notification messages
  - Score display text
  - Dialog content
- **Persist language preference** - Save selected language to localStorage
- **Default to browser language** - Auto-detect Japanese browser locale and default to Japanese

## Impact

**Affected specs:**
- `i18n` (new capability)

**Affected code:**
- `src/app/page.tsx` - All hardcoded English strings replaced with translation keys
- `src/lib/i18n/` (new) - Translation dictionaries and language utilities
- `src/types/` (optional) - Type definitions for translations
- `package.json` - No new dependencies (using built-in solution)

**User experience:**
- No breaking changes for existing English users
- Seamless language switching without page reload
- Browser language auto-detection for new users
Enter fullscreen mode Exit fullscreen mode

↓ openspec\changes\add-japanese-localization\tasks.md

# Implementation Tasks

## 1. Set Up i18n Infrastructure

- [ ] 1.1 Create `src/lib/i18n/` directory
- [ ] 1.2 Create `src/lib/i18n/translations.ts` with English and Japanese dictionaries
- [ ] 1.3 Create `src/lib/i18n/useTranslation.ts` hook for accessing translations
- [ ] 1.4 Create `src/lib/i18n/types.ts` for TypeScript translation key types
- [ ] 1.5 Implement browser language detection utility
- [ ] 1.6 Implement localStorage persistence for language preference

## 2. Create Translation Dictionaries

- [ ] 2.1 Extract all English strings from `page.tsx` into translation keys
- [ ] 2.2 Create English translation dictionary (en.ts)
- [ ] 2.3 Create Japanese translation dictionary (ja.ts)
- [ ] 2.4 Translate game instructions and "How to Play" dialog
- [ ] 2.5 Translate button labels (Start Game, Practice Mode, How to Play)
- [ ] 2.6 Translate settings labels (Settings, Shift Direction, Shift Count, Number Length, Rhythm Mode)
- [ ] 2.7 Translate notification messages (Correct!, Wrong!, etc.)
- [ ] 2.8 Translate score display text (Score, Best Score, Practice Mode)
- [ ] 2.9 Translate history dialog content (medals, table headers)
- [ ] 2.10 Translate shift instructions (e.g., "Left Shift 1 +3")

## 3. Add Language Selection UI

- [ ] 3.1 Add language state to page.tsx
- [ ] 3.2 Create language toggle control in settings section
- [ ] 3.3 Add language label/flag indicator (πŸ‡ΊπŸ‡Έ English / πŸ‡―πŸ‡΅ ζ—₯本θͺž)
- [ ] 3.4 Implement language toggle handler
- [ ] 3.5 Wire up language change to localStorage save
- [ ] 3.6 Position language control appropriately in settings layout

## 4. Integrate Translations Into UI

- [ ] 4.1 Replace hardcoded strings in game instructions with translation keys
- [ ] 4.2 Replace hardcoded strings in button labels
- [ ] 4.3 Replace hardcoded strings in settings labels
- [ ] 4.4 Replace hardcoded strings in notifications
- [ ] 4.5 Replace hardcoded strings in score display
- [ ] 4.6 Replace hardcoded strings in history dialog
- [ ] 4.7 Replace hardcoded strings in shift instruction text
- [ ] 4.8 Update dynamic text generation (e.g., "Left Shift 1 +3")

## 5. Handle Dynamic Content

- [ ] 5.1 Implement parameter substitution for translations (e.g., {score}, {digits})
- [ ] 5.2 Update shift direction display logic for Japanese
- [ ] 5.3 Update number length options for Japanese
- [ ] 5.4 Handle medal counts and completion status in Japanese

## 6. Testing and Quality Assurance

- [ ] 6.1 Test language toggle switches all visible text
- [ ] 6.2 Test browser language detection (ja locale β†’ Japanese, others β†’ English)
- [ ] 6.3 Test language preference persists across page reloads
- [ ] 6.4 Test all game flows in Japanese (start, play, practice, settings)
- [ ] 6.5 Verify Japanese text fits in all UI containers
- [ ] 6.6 Test keyboard navigation with language toggle
- [ ] 6.7 Verify no layout breaks or text overflow in Japanese
- [ ] 6.8 Test fallback to English for missing keys
- [ ] 6.9 Review Japanese translations for accuracy and naturalness
- [ ] 6.10 Test on mobile and desktop viewports

## 7. Documentation

- [ ] 7.1 Update README or project docs to mention bilingual support
- [ ] 7.2 Document translation key naming conventions
- [ ] 7.3 Document how to add new translations
Enter fullscreen mode Exit fullscreen mode

After creating the necessary files, AI automatically checked whether they were correct.

$ openspec list    # Confirm the change folder exists
$ openspec validate add-japanese-localization    # Validate spec formatting
$ openspec show add-japanese-localization    # Review proposal, tasks, and spec delta
Enter fullscreen mode Exit fullscreen mode

Complete proposal

Check the draft proposal and ask your AI to make any needed updates, then create the final proposal for coding.

AI coding (2️⃣ Apply)

Ask your AI to code according to the proposal.

The specs look good. Let's implement this change.
(Shortcut for tools with slash commands: /openspec:apply add-japanese-localization)
Enter fullscreen mode Exit fullscreen mode

There were some words that were not translated into Japanese and also a build error, so I asked my AI to fix these issues.

↓ Finaly, Muscle Brain Japanese version was completed.

Archive proposal (3️⃣ Archive)

After coding, ask your AI to archive the proposal.

Please archive the change.
(Shortcut for tools with slash commands: /openspec:archive add-japanese-localization)
Enter fullscreen mode Exit fullscreen mode

Or run the command yourself in terminal:

openspec archive add-japanese-localization --yes
Enter fullscreen mode Exit fullscreen mode

All of the checkboxes in the TODO list were checked, and files were moved to the archive folder.

Muscle Brain for Free on Microsoft Store

Now let me promote my brain training app, Muscle Brain a little.

πŸ’ͺ🧠How to Boost your Brain for Free (Muscle Brain - Ultimate Edition)

Muscle Brain is an ultimate working memory training game that stimulates your brain. Based on scientifically-proven N-back methodology, this game challenges you to remember, manipulate, and recall number sequences. No need to be afraid of the hard training. Mr. Brain, hero for everybody will support you alongside the game and save the day! πŸ˜€

With high Working Memory you can:
πŸ™† Handle complex tasks easily
πŸ™† Improve multitasking and focus
πŸ™† Boost creative thinking and problem solving

Game Features:
🧠 Unlimited Mode - Train your brain until your limit and burn out
πŸ‘Ά Practice Mode - See the answers as you learn the mechanics of the game
🎡 Rhythm Mode - Time your inputs to the rhythm of Mr. Brain
βš™οΈ Settings - Adjust shift direction, shift count, and number length for maximum effect
πŸ’― History - Collect all 60 gold medals and become a Muscle Brain Legend!

100% Free - No Ads, No Tracking, No Sign-Up Required

Feedback Welcome:
I would love to hear about your experience using my app.πŸ‘‚ If you have any ideas for the next update, bug reports, or any other feedback, pleaes let me know.

Free on Microsoft Store:
https://apps.microsoft.com/detail/9NG572QMV56M

Outro

By using OpenSpec, I could easily create a markdown file to control AI better. The output markdown file guided AI more effectively than the one I made manually.πŸ‘

I hope you learned something from this post.
Thank you for reading.
You're absolutely right!πŸ€–

Top comments (2)

Collapse
 
devin-rosario profile image
Devin Rosario

NICE

Collapse
 
webdeveloperhyper profile image
Web Developer Hyper

Thank you!πŸ˜ƒ