Skip to content

feat(play-ui): HARD/EXPERT adaptive hidden-rule builder (+ stacked difficulty features)#1

Open
irregular6612 wants to merge 20 commits into
mainfrom
worktree-signal-game-difficulty-arena
Open

feat(play-ui): HARD/EXPERT adaptive hidden-rule builder (+ stacked difficulty features)#1
irregular6612 wants to merge 20 commits into
mainfrom
worktree-signal-game-difficulty-arena

Conversation

@irregular6612

Copy link
Copy Markdown
Collaborator

개요

웹 아레나 Play 플로우의 히든 룰 추측 UI를 HARD·EXPERT 난이도에서도 정답 룰을 구조적으로 표현할 수 있게 확장한다. 기존에는 EASY 단일 속성 4-칩 빌더에 고정되어, HARD(2속성 논리곱)·EXPERT(+히스토리 override)에서 플레이어가 정답 룰을 입력할 칸 자체가 없었다.

백엔드 무변경 — 채점기 SignalGameModule.score_probe는 이미 3개 문법(EASY 4슬롯 / HARD 7슬롯 / EXPERT 10슬롯)을 지원하므로 프론트엔드만 확장했다.

참고: 이 브랜치는 앞선 두 feature(Arena 난이도 셀렉터, Human-Play 난이도 + per-game DB tag)를 함께 스택으로 담고 있어, main 병합 시 전체가 통합된다. 세 feature 모두 subagent-driven 리뷰에서 READY TO MERGE 판정을 받았다.

이번 세션 변경 (HARD/EXPERT 룰 빌더)

  • 계약 테스트 (tests/unit/test_signal_game_probe_contract.py, 신규): 프론트가 emit할 룰 문자열 포맷을 Python 상수(FRONTEND_HARD_FORMAT/FRONTEND_EXPERT_FORMAT)로 못박고, 그 문자열이 실제 score_probe에서 100점을 받는지 검증. 프론트↔백엔드 문법 드리프트(조용한 0점)를 CI에서 차단하는 SSOT.
  • web/app.js: 신규 상태 변수 4개(probeAttr2/probeValue2/probeActionPartial/probeOverride), getter valueOptions2/attr2Choices, method setAttr2, helper _hardClause, 난이도 분기 assembledRule(단일 속성 / 2속성 논리곱 / override+논리곱 문법 emit). EASY/MEDIUM 경로 무변경, medium은 방어적으로 EASY 경로.
  • web/index.html + web/styles.css: 기존 단일 속성 빌더를 easy/medium x-if로 감싸고(내부 마크업 무변경), hard/expert x-if 블록 추가 — 2속성 논리곱 + partial 절의 attr_1/val_1 읽기 전용 echo(.chip-echo) + EXPERT 전용 히스토리 override 칩 행. gate 힌트 난이도 분기.

문법 계약

  • HARD: If <a1> is <v1> and <a2> is <v2> then <both>; if only <a1> is <v1> then <partial>; otherwise <default>.
  • EXPERT: If your previous action was correct then <override>; otherwise follow this rule: + HARD

검증

  • 유닛: test_signal_game_probe_contract + test_signal_game_v3 = 43 passed.
  • 문법 3면 대조 (최종 opus 리뷰): 계약 상수 ↔ app.js _hardClause/assembledRule ↔ scorer 정규식(_score_medium_template/_score_hard_template) byte 일치. HARD→medium, EXPERT→hard dispatch 정확.
  • localhost E2E (Playwright): EASY/HARD/EXPERT 각각 빌더 렌더 + distinct attr_2 + 읽기전용 echo + override 행 + 정확한 "Submitting:" 미리보기 + POST /api/new_game·/api/action 모두 HTTP 200(400/500 없음), Alpine 파싱 에러 없음.

알려진 비차단 Minor (연기)

  • attr2Choices가 3속성 목록 하드코딩(현재 ATTR_VALUES가 정확히 그 3개 → active bug 아님, 4번째 속성 추가 시 latent drift).
  • 계약 테스트 seed=1만 커버(문법 배선 가드로는 충분).
  • echo 칩에 aria-hidden 없음(비상호작용 span, 기존 display-only 칩과 일관).
  • (기존 백엔드 설계, 범위 밖) rule_match_scoreActionResponse에 미반환 — 서버측 task_metadata에만 계산.

🤖 Generated with Claude Code

irregular6612 and others added 20 commits July 6, 2026 07:03
Thread easy/hard/expert into the signal_game task_config; validate
against VALID_DIFFICULTIES (medium excluded). Default easy preserves
current behavior. num_few_shot stays fixed at 2.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
3-phase design: rebase worktree onto latest main (main untouched),
add difficulty column to sessions table (sqlite+postgres, backfill
'easy'), wire campaign-level difficulty selector into the human Play
UI, and fix /api/new_game to return 400 (not 500) on unknown difficulty.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
7-task plan: rebase onto main (Task 0), SQLite+Postgres difficulty
column with idempotent migration (Task 1-2), tag write-sites (Task 3),
/api/new_game 400 validation (Task 4), Play UI selector + campaign-level
state + checkpoint (Task 5-6), regression + browser smoke (Task 7).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ration

Add SessionRecord.difficulty (default easy). SQLite schema, idempotent
ADD COLUMN migration (backfill 'easy'), INSERT and row-map wired.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Column, idempotent ADD COLUMN IF NOT EXISTS migration, INSERT, both
SELECT column lists, and positional row-map unpack all carry difficulty.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
LLM (seeding.build_session_record) reads season['difficulty']; human
(api.new_game finish) reads SeasonResult.difficulty.value. Both flow
into sessions.difficulty.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Validate req.difficulty against VALID_DIFFICULTIES before constructing
HumanGameSession, matching /api/arena/run's style.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…point

playScreen.difficulty (default easy) sent in /api/new_game body and
persisted in the resume checkpoint (schema v2, easy fallback for v1).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Reuses squidArenaHelpers.difficultyOptions + cond-card classes; lives
inside the !started setup card so difficulty is locked once a campaign
begins.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Rule-inference probe UI is currently EASY-only (single-attribute chip
builder with no difficulty branching); HARD (2-attr conjunction) and
EXPERT (+history override) players cannot express the correct rule.
Backend score_probe already supports all three grammars, so the fix is
frontend-only. Spec covers adaptive inline-sentence builder, exact
grammar contract, state model, and a Python contract regression test.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Dq7Rij3bV64YNZKLMBYkzX
4 tasks: (1) Python grammar contract test, (2) app.js difficulty-aware
state + assembledRule, (3) index.html adaptive inline builder, (4) E2E
localhost verification. Backend untouched.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Dq7Rij3bV64YNZKLMBYkzX
Subagent-driven execution prompt. Reuses the existing worktree (no new
one), pins localhost E2E server commands, and restates global constraints.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Dq7Rij3bV64YNZKLMBYkzX
HARD/EXPERT probe strings the web rule-builder will emit must score 100
against score_probe. Constants are the SSOT for web/app.js assembledRule.
Add HARD/EXPERT probe slots (attr_2, val_2, partial action, override),
valueOptions2/attr2Choices/setAttr2, and a difficulty-branched
assembledRule emitting the conjunction / history-override grammars.
Format kept in sync with the probe grammar contract test.
Wrap the single-attribute builder in an easy/medium branch and add
hard/expert branches: two-attribute conjunction with a read-only
attr_1 echo in the partial clause, plus an EXPERT history-override chip
row. Gate hint now difficulty-aware.
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.

1 participant