THE SPECS

The Specs Given to AutoDev LG

This is all AutoDev LG gets.
From this text, the local LLMs do the design, the tests and the implementation.

Claude Code writes the spec from a one-line human request (e.g. “Make me an Invaders game”). In the future, writing the spec will be handed to AutoDev LG itself, too. It's just a bulleted list: no screen design, no instructions on how to write the code. The detailed rules about saving and errors in the second half exist so the app the local LLMs build can be checked strictly by automated tests. The originals are in Japanese; English translations are shown below.

taskboardINVADERS

taskboarda task board

✅ 3 PASSes in a row
65 lines · 1,499 characters (original) · SPEC_LG1_DEMO.md · See this app →
LG1 Demo App: Mini Task Board

Build a small desktop app for Windows 11 that uses only the Python standard library.

UI

  • Tkinter GUI
  • Add a task
  • Delete a task
  • Change status: todo / doing / done
  • Show a list
  • The list shows ID / Title / Status
  • After selecting a task, delete it or change its status with a button
  • Report operation errors to the user with tkinter.messagebox

Domain

  • title is str only
  • title is stripped before saving
  • ValueError if it is empty after strip
  • status is only todo / doing / done
  • An invalid status, including an unhashable value, raises ValueError
  • An unknown task id raises KeyError
  • Do not treat bool as an integer ID
  • list is sorted by ascending ID
  • list does not return the internal list itself

Persistence

  • JSON
  • Saved to data/tasks.json
  • UTF-8
  • Keeps Japanese titles
  • State survives a restart
  • IDs start at 1
  • Deleted IDs are never reused
  • next_id is persisted
  • An existing empty JSON file is a StoreError
  • Malformed JSON is a StoreError
  • Invalid UTF-8 is a StoreError
  • Strict schema validation
  • Reject duplicate IDs
  • next_id is greater than every existing ID

Atomic save

  • Temporary file in the same directory as the destination
  • flush
  • os.fsync
  • os.replace
  • Handle failures of mkstemp/write/fsync/replace properly
  • A failure before replace does not damage the existing destination
  • Clean up the temporary file

Transaction contract

If an exception occurs while add_task / set_status / delete_task is persisting:

  • Fully roll back TaskService's in-memory state to before the operation
  • For add_task, restore both tasks and next_id
  • For set_status, restore the original status
  • For delete_task, restore the deleted task
  • Re-raise the original exception from the persistence layer as is, without converting it
  • After rollback, the state TaskService exposes matches the state that the next save would write

Structure

At least:

  • task_model.py
  • task_store.py
  • task_service.py
  • app.py
  • test_task_manager.py

TaskStore is mainly responsible for persistence and must not co-own the domain's mutable state with TaskService.

Constraints

  • Python standard library only
  • The app itself does no network communication
  • The app itself does not run subprocesses
  • No special branches just for tests
Original (Japanese)
LG1 Demo App: Mini Task Board

Windows 11で動くPython標準ライブラリのみの小規模デスクトップアプリを作成する。

UI

  • Tkinter GUI
  • タスク追加
  • タスク削除
  • status変更: todo / doing / done
  • 一覧表示
  • 一覧には ID / Title / Status を表示する
  • タスク選択後にボタン操作で削除・status変更する
  • 操作エラーは tkinter.messagebox でユーザーへ通知する

Domain

  • titleはstrのみ
  • titleはstripして保存
  • strip後空ならValueError
  • statusは todo / doing / done のみ
  • 不正statusは、unhashable値を含めValueError
  • unknown task idはKeyError
  • boolをinteger IDとして扱わない
  • listはID昇順
  • listの返り値は内部listそのものではない

Persistence

  • JSON
  • 保存先は data/tasks.json
  • UTF-8
  • 日本語タイトルを保持
  • 再起動後も状態保持
  • IDは1から開始
  • 削除済みIDを再利用しない
  • next_idは永続化する
  • 既存の空JSONファイルはStoreError
  • malformed JSONはStoreError
  • invalid UTF-8はStoreError
  • strict schema validation
  • duplicate IDを拒否
  • next_idは全既存IDより大きい

Atomic save

  • 保存先と同一ディレクトリにtemporary file
  • flush
  • os.fsync
  • os.replace
  • mkstemp/write/fsync/replace失敗を適切に扱う
  • replace前の失敗では既存destinationを破壊しない
  • temporary fileをcleanupする

Transaction contract

add_task / set_status / delete_task の永続化処理中に例外が発生した場合:

  • TaskServiceのin-memory stateを操作前へ完全rollbackする
  • add_taskではtasksとnext_idの両方を戻す
  • set_statusでは元statusへ戻す
  • delete_taskでは削除したtaskを戻す
  • persistence層から発生した元例外を別の例外へ変更せず、そのまま再raiseする
  • rollback後もTaskServiceが公開する状態と次回save対象状態が一致する

Structure

最低限:

  • task_model.py
  • task_store.py
  • task_service.py
  • app.py
  • test_task_manager.py

TaskStoreは永続化を主責務とし、domainのmutable stateをTaskServiceと二重所有しないこと。

Constraints

  • Python標準ライブラリのみ
  • アプリ本体はネットワーク通信しない
  • アプリ本体はsubprocessを実行しない
  • テスト専用の特殊分岐を実装しない

INVADERSan Invaders game

🚧 In progress
65 lines · 1,913 characters (original) · SPEC_LG1_INVADERS.md · See this app →
LG1 Demo App: Mini Invaders

Build a small desktop game (Invaders-style) for Windows 11 that uses only the Python standard library.

UI

  • Tkinter GUI
  • Draw the game state on a Canvas
  • Left / Right keys move the player
  • Space fires a shot
  • P pauses / resumes
  • R restarts the game
  • Show the score and lives in Labels
  • Show a message in a Label on game over / clear
  • Drawing only reads the game state and never changes it
  • Time advances by calling tick at a fixed interval with tkinter's after. Tests can call tick directly without relying on after

Domain

  • The board is a grid of integer cells, 20 wide and 15 high
  • The player is on the bottom row, with x from 0 to 19. It does not move out of range (it stops at the edge)
  • The move direction is only left or right. Anything else is a ValueError
  • Initially the invaders are 4 rows × 8 columns, left-aligned, starting on the second row from the top
  • The invader group moves one cell sideways per tick; when it reaches an edge it moves down one row and reverses direction
  • Only one player shot at a time. It moves up one cell per tick. Firing while a shot is in flight is a no-op
  • When a shot enters the same cell as an invader, remove that invader, add 10 points to the score, and remove the shot
  • Which invader drops a bomb is decided by an injected random.Random(seed). The same seed gives the same result
  • Bombs move down one cell per tick. When a bomb enters the player's cell, lose one life and remove the bomb
  • Game over when lives reach 0 or the invaders reach the player's row
  • Clear when all invaders are destroyed
  • After game over / clear, and while paused, operations and tick do not change the state
  • A state snapshot is not the internal list itself
  • Do not treat bool as an integer coordinate

Persistence

  • The high score is JSON
  • Saved to data/highscore.json
  • UTF-8
  • Format: {"high_score": an integer of 0 or more}
  • An existing empty JSON file is a StoreError
  • Malformed JSON is a StoreError
  • Invalid UTF-8 is a StoreError
  • Strict schema validation (reject unknown keys, negative numbers, bool, non-integers)
  • If the file does not exist, treat it as high_score 0

Atomic save

  • Temporary file in the same directory as the destination
  • flush
  • os.fsync
  • os.replace
  • Handle failures of mkstemp/write/fsync/replace properly
  • A failure before replace does not damage the existing destination
  • Clean up the temporary file

Transaction contract

If an exception occurs while persisting the high-score update at the end of a game:

  • Fully roll back the game's in-memory state (score, game_over, high_score) to before the operation
  • Re-raise the original exception from the persistence layer as is, without converting it
  • After rollback, the state the game exposes matches the state that the next save would write

Structure

At least:

  • game_model.py (data classes for the state, and StoreError)
  • game_engine.py (pure game logic that does not depend on Tk)
  • score_store.py (high-score persistence)
  • app.py (Tkinter GUI)
  • test_game.py

ScoreStore is mainly responsible for persistence and must not co-own the game's mutable state with GameEngine.

Constraints

  • Python standard library only
  • The app itself does no network communication
  • The app itself does not run subprocesses
  • No special branches just for tests
  • game_engine.py does not import tkinter
Original (Japanese)
LG1 Demo App: Mini Invaders

Windows 11で動くPython標準ライブラリのみの小規模デスクトップゲーム(インベーダー風)を作成する。

UI

  • Tkinter GUI
  • Canvasにゲーム状態を描画する
  • 左キー / 右キーで自機を移動する
  • Spaceキーで弾を発射する
  • Pキーで一時停止 / 再開する
  • Rキーでゲームをリスタートする
  • スコアとライフをLabelで表示する
  • ゲームオーバー / クリア時にメッセージをLabelで表示する
  • 描画はゲーム状態を読み取るだけで、状態を変更しない
  • 時間進行はtkinterのafterで一定間隔にtickを呼ぶ。テストからはafterに頼らずtickを直接呼べる

Domain

  • ゲーム盤は整数セルの格子で、幅は20、高さは15
  • 自機は最下行にいて、xは0以上19以下。範囲外へは移動しない(端で止まる)
  • 移動方向はleftまたはrightのみ。それ以外はValueError
  • インベーダーは初期状態で4行×8列。最上段から2行目の位置に左寄せで並ぶ
  • インベーダー群はtickごとに1セル横へ進み、端に達したら1行下がって向きを反転する
  • 自機の弾は同時に1発まで。tickごとに1セル上へ進む。発射済みの間の発射はno-op
  • 弾がインベーダーと同じセルに入ったらそのインベーダーを削除し、スコアを10点加算し、弾を消す
  • インベーダーの爆弾は、注入されたrandom.Random(seed)でどのインベーダーが撃つかを決める。同じseedなら同じ結果になる
  • 爆弾はtickごとに1セル下へ進む。自機と同じセルに入ったらライフを1減らし、爆弾を消す
  • ライフが0になるか、インベーダーが自機の行に到達したらゲームオーバー
  • インベーダーを全滅させたらクリア
  • ゲームオーバー / クリア後および一時停止中の操作とtickは状態を変更しない
  • 状態のスナップショットは内部のlistそのものではない
  • boolを整数座標として扱わない

Persistence

  • ハイスコアはJSON
  • 保存先は data/highscore.json
  • UTF-8
  • 形式は {"high_score": 0以上の整数}
  • 既存の空JSONファイルはStoreError
  • malformed JSONはStoreError
  • invalid UTF-8はStoreError
  • strict schema validation(未知のキー、負数、bool、整数以外を拒否)
  • ファイルが存在しない場合はhigh_score 0として扱う

Atomic save

  • 保存先と同一ディレクトリにtemporary file
  • flush
  • os.fsync
  • os.replace
  • mkstemp/write/fsync/replace失敗を適切に扱う
  • replace前の失敗では既存destinationを破壊しない
  • temporary fileをcleanupする

Transaction contract

ゲーム終了時のハイスコア更新の永続化処理中に例外が発生した場合:

  • ゲームのin-memory state(score, game_over, high_score)を操作前へ完全rollbackする
  • persistence層から発生した元例外を別の例外へ変更せず、そのまま再raiseする
  • rollback後もゲームが公開する状態と次回save対象状態が一致する

Structure

最低限:

  • game_model.py (状態を表すデータクラスとStoreError)
  • game_engine.py (Tkに依存しない純粋なゲームロジック)
  • score_store.py (ハイスコアの永続化)
  • app.py (Tkinter GUI)
  • test_game.py

ScoreStoreは永続化を主責務とし、ゲームのmutable stateをGameEngineと二重所有しないこと。

Constraints

  • Python標準ライブラリのみ
  • アプリ本体はネットワーク通信しない
  • アプリ本体はsubprocessを実行しない
  • テスト専用の特殊分岐を実装しない
  • game_engine.pyはtkinterをimportしない