Skip to content

chore: CodeRabbit에 3-File Pattern 컨텍스트 연동#66

Open
USKTEA wants to merge 5 commits intomainfrom
feat/3file-tetris-with-context
Open

chore: CodeRabbit에 3-File Pattern 컨텍스트 연동#66
USKTEA wants to merge 5 commits intomainfrom
feat/3file-tetris-with-context

Conversation

@USKTEA
Copy link
Owner

@USKTEA USKTEA commented Feb 18, 2026

Summary

  • .coderabbit.yaml 추가: knowledge_base.filePatterns.claude/docs/**/*.md 글로브를 설정하여 브랜치별 3-File Pattern 문서를 자동 참조
  • path_instructions에 3-File Pattern 기반 범용 리뷰 원칙 정의
  • CodeRabbit이 프로젝트 특화 리뷰를 하는지 실험 목적

Test plan

  • 이 PR의 CodeRabbit 리뷰가 이전 PR과 달리 프로젝트 컨텍스트를 반영하는지 비교

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Complete console Tetris: gameplay loop, scoring, levels, next-piece preview, ghost piece, and SRS rotation with wall kicks
    • Keyboard controls: left/right, rotate, soft/hard drop, and quit
    • ANSI-based double-buffered console renderer for reduced flicker and game-over display
  • Documentation

    • Added project workflow, architecture, planning files, progress/tasks/findings, and review/configuration docs

kimsuktae and others added 3 commits February 18, 2026 20:42
SRS 회전, 월킥, 더블 버퍼링 렌더링, 레벨 시스템을 포함한
콘솔 테트리스를 외부 의존성 없이 순수 Kotlin으로 구현.

CLAUDE.md와 AGENTS.md를 포함하여 CodeRabbit이
프로젝트 컨텍스트 없이 리뷰하는 baseline을 측정.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
기술적 의사결정 근거, 시행착오 이력, 세션별 작업 내역을
.claude/docs/PL-001-tetris/에 추가하여 CodeRabbit이
프로젝트 컨텍스트를 참조한 리뷰를 하는지 비교 측정.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
knowledge_base.filePatterns에 .claude/docs/**/*.md 글로브를 추가하여
브랜치별 계획 문서(tasks.md, findings.md, progress.md)를 자동 참조하도록 설정.
path_instructions에 3-File Pattern 기반 리뷰 원칙을 범용으로 정의.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@coderabbitai
Copy link

coderabbitai bot commented Feb 18, 2026

No actionable comments were generated in the recent review. 🎉


📝 Walkthrough

Walkthrough

Adds a complete console Tetris implementation in Kotlin (Board, Piece, Game, Score, Input, Renderer, Main) plus project planning and workflow documentation (CLAUDE.md, README, AGENTS.md, .claude/docs/*, .coderabbit.yaml). Features SRS rotation with wall kicks, ANSI double-buffered rendering, and terminal raw-mode input handling.

Changes

Cohort / File(s) Summary
Project Planning docs
.claude/docs/PL-001-tetris/findings.md, .claude/docs/PL-001-tetris/progress.md, .claude/docs/PL-001-tetris/tasks.md
New three-file pattern documents: requirements, decisions, session logs, errors, review findings and progress for the Tetris project.
Workflow & governance
CLAUDE.md, README.md, AGENTS.md, .coderabbit.yaml
Adds file-based planning workflow, agent architecture notes, README, and CodeRabbit CI/review config (Korean review instructions). Review docs for policy/consistency.
Core game model
src/tetris/Piece.kt, src/tetris/Board.kt, src/tetris/Score.kt
New game models: Piece (types, SRS rotations, wall-kick maps, random spawn), Board (10×20 grid, collision checks, lock/clear lines, ghostY), Score (scoring, leveling, dropInterval). Public APIs added.
Game runtime & input
src/tetris/Game.kt, src/tetris/Input.kt, src/tetris/Main.kt
Game loop, state, action handling (move/rotate/soft/hard drop), SRS rotation with kicks, spawn/lock logic, input daemon thread parsing escape sequences, and main entry that toggles terminal raw mode and restoration. Attention: terminal stty calls and shutdown hook.
Rendering
src/tetris/Renderer.kt
ANSI-based double-buffered console renderer: diff-based frame updates, ghost visualization, side panel, render/cleanup APIs. Focus: cursor control, color codes, flicker minimization.

Sequence Diagram

sequenceDiagram
    participant Main
    participant Game
    participant Input
    participant Board
    participant Renderer
    participant Score

    Main->>Game: run()
    activate Game
    Game->>Renderer: renderInitial()
    Renderer-->>Game: initial frame drawn
    Game->>Input: input.start()
    Input-->>Game: (daemon) queued Actions

    loop Game loop (frame)
        Game->>Input: poll() -> Action
        alt Action != NONE
            Game->>Board: isValidPosition / ghostY / lockPiece
            Board-->>Game: validity / landingY / linesCleared
            Game->>Score: addSoftDrop/addHardDrop/addLineClear
        end
        Game->>Game: rotate/move/spawn logic (SRS + kicks)
        Game->>Renderer: render(board, currentPiece, nextPiece, score)
        Renderer-->>Game: frame updated
        Game->>Game: sleep(frameDelay)
    end

    Game->>Renderer: renderGameOver(score)
    Game->>Input: input.stop()
    deactivate Game
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~75 minutes

Poem

🐰
I hopped in code where tetrominoes fall,
Rotations spin and ghost pieces call,
ANSI lights blink in buffered streams,
A rabbit cheers for Kotlin dreams—
Blocks align; the console sings!

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 16.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title directly describes the main change: adding the 3-File Pattern context integration to CodeRabbit configuration, which is the primary focus of the changeset.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/3file-tetris-with-context

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (8)
20260218-3files/src/tetris/Input.kt (2)

47-49: Consider logging unexpected exceptions instead of silently swallowing.

While ignoring IOException on stream close is reasonable, catching all Exception types silently can hide unexpected errors during development.

♻️ Proposed fix
-                } catch (e: Exception) {
-                    // 입력 스트림 종료 시 무시
+                } catch (e: java.io.IOException) {
+                    // 입력 스트림 종료 시 루프 종료
+                    break
                 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@20260218-3files/src/tetris/Input.kt` around lines 47 - 49, The current catch
block in Input.kt that does "catch (e: Exception) { // 입력 스트림 종료 시 무시 }"
swallows all exceptions; change it to specifically catch IOException for
expected stream-close behavior (e.g., "catch (e: IOException) { /* ignore */ }")
and add a separate catch for other exceptions that logs or surfaces them (e.g.,
"catch (e: Exception) { logger.error(\"Unexpected error in input loop\", e)" or
at minimum call e.printStackTrace()) so unexpected errors are not silently
ignored; update any relevant function or class in Input.kt where this try/catch
appears.

86-88: Note: stop() won't immediately terminate the blocked reader thread.

Since System.in.read() is blocking, the thread will remain blocked until input arrives or the stream closes. As a daemon thread, this is acceptable for a game that exits the JVM, but be aware if reusing this class in other contexts.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@20260218-3files/src/tetris/Input.kt` around lines 86 - 88, The stop() method
only flips the running flag but does not unblock the reader thread stuck on
System.in.read(); update stop() to also interrupt and close the reader so it
terminates promptly: store the reader thread (e.g., inputThread) when started,
and in stop() call inputThread.interrupt() and attempt to close System.in (or
the wrapped InputStream used by your reader) so the blocking read
throws/returns; ensure the reader loop in the reading function checks
Thread.currentThread().isInterrupted() and handles IOException from stream
closure to exit cleanly.
20260218-3files/CLAUDE.md (1)

28-34: Add language specifiers to code blocks for better rendering.

Multiple code blocks are missing language specifiers, which can affect rendering and accessibility. Consider adding appropriate identifiers:

  • Lines 28-34 and 40-46 (workflow diagrams): Add text identifier
  • Lines 87-92 and 145-151 (directory structures): Add text or bash identifier
  • Lines 315-320 (error protocol): Add text identifier
📝 Proposed fixes for code block language specifiers

Lines 28-34:

-```
+```text
 /oh-my-claudecode:plan 호출

Lines 40-46:

-```
+```text
 Phase 1: 계획 수립 및 검증 (4단계) ─── 계획을 세우고, 세 번 검토한다

Lines 87-92:

-```
+```text
 프로젝트루트/

Lines 145-151:

-```
+```text
 .claude/docs/

Lines 315-320:

-```
+```text
 ATTEMPT 1: 진단 및 수정 - 에러 분석, 근본 원인 파악, 대상 수정

As per static analysis hints (markdownlint MD040 warnings).

Also applies to: 40-46, 87-92, 145-151, 315-320

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@20260218-3files/CLAUDE.md` around lines 28 - 34, Update the fenced code
blocks noted in the review so they include language specifiers (e.g., add "text"
or "bash") to satisfy markdownlint MD040: add ```text before the workflow block
containing "/oh-my-claudecode:plan 호출" and the "Phase 1: 계획 수립..." block, add
```text or ```bash before the directory examples "프로젝트루트/" and ".claude/docs/",
and add ```text before the error protocol block starting with "ATTEMPT 1: 진단 및
수정"; ensure each corresponding closing ``` remains and that only the fence
markers are changed so rendering and accessibility improve without altering
content.
20260218-3files/src/tetris/Renderer.kt (1)

139-141: Empty renderBorder() method could be removed.

This method is a no-op since borders are drawn in renderInitial(). Consider removing it to reduce confusion, or add a comment explaining why it exists (e.g., for API consistency).

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@20260218-3files/src/tetris/Renderer.kt` around lines 139 - 141, The
renderBorder() method is a no-op because borders are drawn in renderInitial();
either remove the renderBorder() function entirely to avoid confusion or keep it
but add a clear comment explaining it's intentionally empty for API
compatibility (e.g., "no-op: borders handled in renderInitial()") so other
developers understand its presence; update references to renderBorder() if
removed, or ensure its signature remains stable if kept for compatibility.
20260218-3files/src/tetris/Main.kt (1)

31-39: Consider using ProcessBuilder for better error handling.

Runtime.exec() works but ProcessBuilder provides better error stream handling and is the modern approach.

♻️ Optional ProcessBuilder alternative
private fun setRawMode() {
    ProcessBuilder("/bin/sh", "-c", "stty -icanon min 1 -echo < /dev/tty")
        .inheritIO()
        .start()
        .waitFor()
}

private fun restoreTerminal() {
    ProcessBuilder("/bin/sh", "-c", "stty sane < /dev/tty")
        .inheritIO()
        .start()
        .waitFor()
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@20260218-3files/src/tetris/Main.kt` around lines 31 - 39, The calls in
setRawMode and restoreTerminal use Runtime.getRuntime().exec(...) which lacks
convenient error/IO handling; replace them with ProcessBuilder for both
setRawMode() and restoreTerminal(), build the process with the same command
("/bin/sh", "-c", "stty ... < /dev/tty"), start it, connect or inherit IO so
stderr/stdout are visible (or capture and log the error stream), waitFor the
process to complete and check its exit value, and throw or log a descriptive
error including the process output if the exit code is non-zero to improve
diagnostics.
20260218-3files/src/tetris/Score.kt (1)

41-47: Consider validating non-negative row counts in drop methods.

addSoftDrop and addHardDrop don't validate that rows is non-negative. Negative values would incorrectly reduce the score.

🛡️ Optional validation
 fun addSoftDrop(rows: Int) {
+    if (rows <= 0) return
     score += rows
 }

 fun addHardDrop(rows: Int) {
+    if (rows <= 0) return
     score += rows * 2
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@20260218-3files/src/tetris/Score.kt` around lines 41 - 47, addSoftDrop and
addHardDrop don't validate that the rows parameter is non-negative, allowing
negative values to decrease score; update both functions (addSoftDrop and
addHardDrop) to check rows >= 0 and reject invalid input (e.g., throw
IllegalArgumentException with a clear message) or clamp to 0 before updating the
score variable so negative rows cannot reduce score. Ensure the validation
occurs before modifying score and keep error text descriptive (mention function
name and invalid rows).
20260218-3files/src/tetris/Piece.kt (1)

59-64: Data class with mutable properties.

Piece is a data class with var properties (x, y, rotation). While functional, this can lead to unintended mutations. The copy() method at line 74 is redundant since data classes auto-generate copy().

Consider using val properties and relying on the auto-generated copy(x = newX, y = newY, rotation = newRotation) for immutable state transitions, which is more idiomatic Kotlin.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@20260218-3files/src/tetris/Piece.kt` around lines 59 - 64, The Piece data
class currently declares mutable properties (var x, var y, var rotation) and
also defines a redundant manual copy() — change these properties to immutable
vals (val x, val y, val rotation) and remove the custom copy() implementation so
callers use the data class's auto-generated copy(x = ..., y = ..., rotation =
...) for state transitions; update any call sites that relied on in-place
mutation of Piece to instead reassign the Piece reference using piece.copy(...)
(identify class Piece, its properties x/y/rotation and the custom copy() method
to modify).
20260218-3files/src/tetris/Game.kt (1)

15-17: Consider restricting visibility of board and score.

Exposing board and score as public properties allows external mutation of game state. If external read access is needed (e.g., for testing), consider exposing read-only views or making them private with getters.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@20260218-3files/src/tetris/Game.kt` around lines 15 - 17, The Game class
currently exposes mutable properties board and score publicly; make them private
(e.g., private val board: Board, private val score: Score) and add controlled
accessors as needed — either read-only getters (fun getBoard(): BoardView or val
boardView: BoardView) or simple public getters that return immutable/read-only
interfaces of Board and Score so external code cannot mutate internal state
directly; update any callers to use the new getters or view types and adjust
tests to depend on the read-only interfaces.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@20260218-3files/AGENTS.md`:
- Around line 17-29: Update the fenced code blocks in AGENTS.md that currently
lack a language tag by changing their opening fences from ``` to a labeled fence
(e.g., ```text) so markdownlint MD040 is satisfied; specifically add `text` (or
another appropriate language) to the ASCII diagram block (the box/arrow diagram)
and to the file-tree block (the src/ tetris listing) as well as the other
identified fenced block ranges (the ones flagged at 33-43 and 87) so each fenced
block begins with ```text.

In `@20260218-3files/src/tetris/Game.kt`:
- Around line 26-50: Wrap the main game logic in Game.run() with a try-finally
so renderer.cleanup() and input.stop() always run on exit or exception: after
calling input.start() and renderer.renderInitial(), put the game loop and
renderer.renderGameOver(score) inside a try block, and in the finally block call
renderer.cleanup() and input.stop(); ensure you reference the run() method,
renderer.cleanup(), input.stop(), and renderer.renderGameOver(score) when making
the change so the input thread is stopped and renderer resources are released
even if an exception occurs.

In `@20260218-3files/src/tetris/Input.kt`:
- Around line 33-34: When System.`in`.read() returns -1 (EOF) the current code
does "if (b == -1) continue" which spins; change the EOF handling in the input
loop (where System.`in`.read() and variable b are used) to break out of the loop
or set running = false and then break so the loop stops cleanly (update the loop
controlling variable named running and the place that reads b accordingly).

In `@20260218-3files/src/tetris/Renderer.kt`:
- Around line 17-19: Renderer's prevBuffer is initialized to -1 which collides
with GHOST_COLOR and prevents ghost cells from rendering on the first frame;
change the initial sentinel used for prevBuffer (in the Renderer class, the
prevBuffer property) to a value that can never be a valid color (e.g.,
Int.MIN_VALUE or a new constant like INITIAL_SENTINEL) and update any references
to GHOST_COLOR only if they currently rely on -1; ensure comparisons still use
frame[row][col] != prevBuffer[row][col] so the ghost will be drawn on the first
frame.

---

Nitpick comments:
In `@20260218-3files/CLAUDE.md`:
- Around line 28-34: Update the fenced code blocks noted in the review so they
include language specifiers (e.g., add "text" or "bash") to satisfy markdownlint
MD040: add ```text before the workflow block containing "/oh-my-claudecode:plan
호출" and the "Phase 1: 계획 수립..." block, add ```text or ```bash before the
directory examples "프로젝트루트/" and ".claude/docs/", and add ```text before the
error protocol block starting with "ATTEMPT 1: 진단 및 수정"; ensure each
corresponding closing ``` remains and that only the fence markers are changed so
rendering and accessibility improve without altering content.

In `@20260218-3files/src/tetris/Game.kt`:
- Around line 15-17: The Game class currently exposes mutable properties board
and score publicly; make them private (e.g., private val board: Board, private
val score: Score) and add controlled accessors as needed — either read-only
getters (fun getBoard(): BoardView or val boardView: BoardView) or simple public
getters that return immutable/read-only interfaces of Board and Score so
external code cannot mutate internal state directly; update any callers to use
the new getters or view types and adjust tests to depend on the read-only
interfaces.

In `@20260218-3files/src/tetris/Input.kt`:
- Around line 47-49: The current catch block in Input.kt that does "catch (e:
Exception) { // 입력 스트림 종료 시 무시 }" swallows all exceptions; change it to
specifically catch IOException for expected stream-close behavior (e.g., "catch
(e: IOException) { /* ignore */ }") and add a separate catch for other
exceptions that logs or surfaces them (e.g., "catch (e: Exception) {
logger.error(\"Unexpected error in input loop\", e)" or at minimum call
e.printStackTrace()) so unexpected errors are not silently ignored; update any
relevant function or class in Input.kt where this try/catch appears.
- Around line 86-88: The stop() method only flips the running flag but does not
unblock the reader thread stuck on System.in.read(); update stop() to also
interrupt and close the reader so it terminates promptly: store the reader
thread (e.g., inputThread) when started, and in stop() call
inputThread.interrupt() and attempt to close System.in (or the wrapped
InputStream used by your reader) so the blocking read throws/returns; ensure the
reader loop in the reading function checks
Thread.currentThread().isInterrupted() and handles IOException from stream
closure to exit cleanly.

In `@20260218-3files/src/tetris/Main.kt`:
- Around line 31-39: The calls in setRawMode and restoreTerminal use
Runtime.getRuntime().exec(...) which lacks convenient error/IO handling; replace
them with ProcessBuilder for both setRawMode() and restoreTerminal(), build the
process with the same command ("/bin/sh", "-c", "stty ... < /dev/tty"), start
it, connect or inherit IO so stderr/stdout are visible (or capture and log the
error stream), waitFor the process to complete and check its exit value, and
throw or log a descriptive error including the process output if the exit code
is non-zero to improve diagnostics.

In `@20260218-3files/src/tetris/Piece.kt`:
- Around line 59-64: The Piece data class currently declares mutable properties
(var x, var y, var rotation) and also defines a redundant manual copy() — change
these properties to immutable vals (val x, val y, val rotation) and remove the
custom copy() implementation so callers use the data class's auto-generated
copy(x = ..., y = ..., rotation = ...) for state transitions; update any call
sites that relied on in-place mutation of Piece to instead reassign the Piece
reference using piece.copy(...) (identify class Piece, its properties
x/y/rotation and the custom copy() method to modify).

In `@20260218-3files/src/tetris/Renderer.kt`:
- Around line 139-141: The renderBorder() method is a no-op because borders are
drawn in renderInitial(); either remove the renderBorder() function entirely to
avoid confusion or keep it but add a clear comment explaining it's intentionally
empty for API compatibility (e.g., "no-op: borders handled in renderInitial()")
so other developers understand its presence; update references to renderBorder()
if removed, or ensure its signature remains stable if kept for compatibility.

In `@20260218-3files/src/tetris/Score.kt`:
- Around line 41-47: addSoftDrop and addHardDrop don't validate that the rows
parameter is non-negative, allowing negative values to decrease score; update
both functions (addSoftDrop and addHardDrop) to check rows >= 0 and reject
invalid input (e.g., throw IllegalArgumentException with a clear message) or
clamp to 0 before updating the score variable so negative rows cannot reduce
score. Ensure the validation occurs before modifying score and keep error text
descriptive (mention function name and invalid rows).

Comment on lines +17 to +29
```
┌──────────┐ ┌──────────┐ ┌──────────┐
│ Input │────▶│ Game │────▶│ Renderer │
│(키보드) │ │ (루프) │ │(콘솔출력) │
└──────────┘ └──────────┘ └──────────┘
┌───────┼───────┐
▼ ▼ ▼
┌────────┐ ┌─────┐ ┌───────┐
│ Board │ │Piece│ │ Score │
│(10x20) │ │(SRS)│ │(레벨) │
└────────┘ └─────┘ └───────┘
```
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Add language identifiers to fenced blocks (MD040).

Markdownlint flags missing language identifiers for fenced code blocks; specify a language like text to satisfy lint and improve tooling.

🔧 Proposed fix
-```
+```text
 ┌──────────┐     ┌──────────┐     ┌──────────┐
 │  Input   │────▶│   Game   │────▶│ Renderer │
 │(키보드)   │     │ (루프)    │     │(콘솔출력) │
 └──────────┘     └──────────┘     └──────────┘
                       │
               ┌───────┼───────┐
               ▼       ▼       ▼
          ┌────────┐ ┌─────┐ ┌───────┐
          │ Board  │ │Piece│ │ Score │
          │(10x20) │ │(SRS)│ │(레벨)  │
          └────────┘ └─────┘ └───────┘

- +text
src/
└── tetris/
├── Main.kt # 진입점, 터미널 설정, 게임 시작
├── Game.kt # 게임 루프, 상태 관리, 타이머
├── Board.kt # 10x20 그리드, 충돌 감지, 라인 클리어
├── Piece.kt # 7종 테트로미노 정의, SRS 회전, 월킥
├── Input.kt # 터미널 raw 모드, 키 입력 처리
├── Renderer.kt # ANSI escape code 렌더링, 더블 버퍼링
└── Score.kt # 점수 계산, 레벨 시스템

Also applies to: 33-43, 87-87

🧰 Tools
🪛 markdownlint-cli2 (0.21.0)

[warning] 28-28: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@20260218-3files/AGENTS.md` around lines 17 - 29, Update the fenced code
blocks in AGENTS.md that currently lack a language tag by changing their opening
fences from ``` to a labeled fence (e.g., ```text) so markdownlint MD040 is
satisfied; specifically add `text` (or another appropriate language) to the
ASCII diagram block (the box/arrow diagram) and to the file-tree block (the src/
tetris listing) as well as the other identified fenced block ranges (the ones
flagged at 33-43 and 87) so each fenced block begins with ```text.

Comment on lines +26 to +50
fun run() {
input.start()
renderer.renderInitial()

while (state == GameState.PLAYING) {
val action = input.poll()
handleAction(action)

// 자동 낙하
val now = System.currentTimeMillis()
if (now - lastDropTime >= score.dropInterval) {
if (!moveDown()) {
lockAndSpawn()
}
lastDropTime = now
}

renderer.render(board, currentPiece, nextPiece, score)

Thread.sleep(FRAME_DELAY_MS)
}

renderer.renderGameOver(score)
input.stop()
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

# Check if Renderer class has a cleanup method
fd -t f '\.(kt|java)$' | xargs grep -l 'class Renderer' | head -5

Repository: USKTEA/coding-life

Length of output: 394


🏁 Script executed:

# Also search for any cleanup methods in the codebase
rg 'fun cleanup|def cleanup' --type kt --type java

Repository: USKTEA/coding-life

Length of output: 88


🏁 Script executed:

# Check Input class for cleanup/stop methods
fd -t f '\.(kt|java)$' | xargs grep -l 'class Input\|interface Input' | head -5

Repository: USKTEA/coding-life

Length of output: 391


🏁 Script executed:

cat -n 20260218-3files/src/tetris/Renderer.kt

Repository: USKTEA/coding-life

Length of output: 6670


🏁 Script executed:

cat -n 20260218-3files/src/tetris/Input.kt

Repository: USKTEA/coding-life

Length of output: 3039


🏁 Script executed:

cat -n 20260218-3files/src/tetris/Game.kt

Repository: USKTEA/coding-life

Length of output: 5454


Add try-finally block to ensure renderer cleanup and input stop on any exception.

The renderer.cleanup() method exists but is not called in the game loop. Additionally, if an exception occurs during gameplay, input.stop() won't be executed, leaving the input thread running unnecessarily until JVM exit (though it is a daemon thread). Wrap the game logic in try-finally to ensure both cleanup operations complete regardless of how the loop exits.

🛡️ Proposed fix
     fun run() {
         input.start()
-        renderer.renderInitial()
-
-        while (state == GameState.PLAYING) {
-            val action = input.poll()
-            handleAction(action)
-
-            // 자동 낙하
-            val now = System.currentTimeMillis()
-            if (now - lastDropTime >= score.dropInterval) {
-                if (!moveDown()) {
-                    lockAndSpawn()
+        try {
+            renderer.renderInitial()
+
+            while (state == GameState.PLAYING) {
+                val action = input.poll()
+                handleAction(action)
+
+                // 자동 낙하
+                val now = System.currentTimeMillis()
+                if (now - lastDropTime >= score.dropInterval) {
+                    if (!moveDown()) {
+                        lockAndSpawn()
+                    }
+                    lastDropTime = now
                 }
-                lastDropTime = now
-            }
 
-            renderer.render(board, currentPiece, nextPiece, score)
+                renderer.render(board, currentPiece, nextPiece, score)
 
-            Thread.sleep(FRAME_DELAY_MS)
-        }
+                Thread.sleep(FRAME_DELAY_MS)
+            }
 
-        renderer.renderGameOver(score)
-        input.stop()
+            renderer.renderGameOver(score)
+        } finally {
+            input.stop()
+            renderer.cleanup()
+        }
     }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
fun run() {
input.start()
renderer.renderInitial()
while (state == GameState.PLAYING) {
val action = input.poll()
handleAction(action)
// 자동 낙하
val now = System.currentTimeMillis()
if (now - lastDropTime >= score.dropInterval) {
if (!moveDown()) {
lockAndSpawn()
}
lastDropTime = now
}
renderer.render(board, currentPiece, nextPiece, score)
Thread.sleep(FRAME_DELAY_MS)
}
renderer.renderGameOver(score)
input.stop()
}
fun run() {
input.start()
try {
renderer.renderInitial()
while (state == GameState.PLAYING) {
val action = input.poll()
handleAction(action)
// 자동 낙하
val now = System.currentTimeMillis()
if (now - lastDropTime >= score.dropInterval) {
if (!moveDown()) {
lockAndSpawn()
}
lastDropTime = now
}
renderer.render(board, currentPiece, nextPiece, score)
Thread.sleep(FRAME_DELAY_MS)
}
renderer.renderGameOver(score)
} finally {
input.stop()
renderer.cleanup()
}
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@20260218-3files/src/tetris/Game.kt` around lines 26 - 50, Wrap the main game
logic in Game.run() with a try-finally so renderer.cleanup() and input.stop()
always run on exit or exception: after calling input.start() and
renderer.renderInitial(), put the game loop and renderer.renderGameOver(score)
inside a try block, and in the finally block call renderer.cleanup() and
input.stop(); ensure you reference the run() method, renderer.cleanup(),
input.stop(), and renderer.renderGameOver(score) when making the change so the
input thread is stopped and renderer resources are released even if an exception
occurs.

Comment on lines +33 to +34
val b = System.`in`.read()
if (b == -1) continue
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

EOF handling causes CPU spin loop.

When read() returns -1 (end of stream), continuing the loop will cause repeated -1 returns, creating a busy-wait that wastes CPU cycles. Should break out of the loop or set running = false.

🐛 Proposed fix
                     val b = System.`in`.read()
-                    if (b == -1) continue
+                    if (b == -1) break
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
val b = System.`in`.read()
if (b == -1) continue
val b = System.`in`.read()
if (b == -1) break
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@20260218-3files/src/tetris/Input.kt` around lines 33 - 34, When
System.`in`.read() returns -1 (EOF) the current code does "if (b == -1)
continue" which spins; change the EOF handling in the input loop (where
System.`in`.read() and variable b are used) to break out of the loop or set
running = false and then break so the loop stops cleanly (update the loop
controlling variable named running and the place that reads b accordingly).

Comment on lines +17 to +19
class Renderer {
private val prevBuffer = Array(Board.HEIGHT) { IntArray(Board.WIDTH) { -1 } }
private val sb = StringBuilder(4096)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Potential first-frame ghost rendering issue.

prevBuffer is initialized with -1, which equals GHOST_COLOR. On the first frame, if a ghost piece occupies a cell, frame[row][col] will be -1 and prevBuffer[row][col] is also -1, so the comparison frame[row][col] != prevBuffer[row][col] will be false, and the ghost won't render.

Consider using a different sentinel value for the initial buffer state:

🐛 Proposed fix
 class Renderer {
-    private val prevBuffer = Array(Board.HEIGHT) { IntArray(Board.WIDTH) { -1 } }
+    private val prevBuffer = Array(Board.HEIGHT) { IntArray(Board.WIDTH) { Int.MIN_VALUE } }
     private val sb = StringBuilder(4096)

And update GHOST_COLOR if needed:

 companion object {
-    private const val GHOST_COLOR = -1
+    private const val GHOST_COLOR = -1  // OK since prevBuffer uses Int.MIN_VALUE
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
class Renderer {
private val prevBuffer = Array(Board.HEIGHT) { IntArray(Board.WIDTH) { -1 } }
private val sb = StringBuilder(4096)
class Renderer {
private val prevBuffer = Array(Board.HEIGHT) { IntArray(Board.WIDTH) { Int.MIN_VALUE } }
private val sb = StringBuilder(4096)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@20260218-3files/src/tetris/Renderer.kt` around lines 17 - 19, Renderer's
prevBuffer is initialized to -1 which collides with GHOST_COLOR and prevents
ghost cells from rendering on the first frame; change the initial sentinel used
for prevBuffer (in the Renderer class, the prevBuffer property) to a value that
can never be a valid color (e.g., Int.MIN_VALUE or a new constant like
INITIAL_SENTINEL) and update any references to GHOST_COLOR only if they
currently rely on -1; ensure comparisons still use frame[row][col] !=
prevBuffer[row][col] so the ghost will be drawn on the first frame.

kimsuktae and others added 2 commits February 19, 2026 00:37
knowledge_base.code_guidelines는 default branch(main) 파일만 인덱싱하므로
feature branch의 3-file 문서를 참조하지 못하는 문제를 해결한다.

변경사항:
- tone_instructions에 한국어 리뷰 명시적 강제
- path_instructions에 tasks.md/findings.md 핵심 내용 직접 임베딩
- 의도적 기술 결정, 제약조건, 해결 완료 이슈를 인라인으로 제공
- knowledge_base는 main 머지 후 자동 인덱싱용으로 유지

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
fix: CodeRabbit 한국어 리뷰 및 3-File Pattern 컨텍스트 임베딩
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants