fix: 전투 이펙트와 차등 하네스 호환성을 복원한다

Ref 기준의 판정 순서, 난수 소비, 아이템·특기 이펙트와 로그 형식을 맞춘다.

전체 전투 결과와 이벤트·상태·난수 trace를 엄격 비교하고 fixture 및 출병 예약 회귀를 보강한다.
This commit is contained in:
2026-08-24 03:37:56 +00:00
parent 32bab36320
commit 95cf68dffd
21 changed files with 1499 additions and 319 deletions
@@ -0,0 +1,23 @@
# Battle differential fixtures
`basic-infantry.json` is the tracked, deterministic smoke fixture for the Ref ↔ Core battle comparator. Every fixture must explicitly provide a positive integer `city` for the attacker and every defender. The attacker city must equal `attackerCity.city`; each defender city must equal `defenderCity.city`. The runner rejects omitted or inconsistent current-city state before invoking either engine.
The captured corpus test is intentionally conditional. It is skipped unless `BATTLE_CORPUS_PATH` points to an existing JSONL fixture corpus; this repository does not generate or silently substitute a corpus. Each corpus row is validated by the same city contract.
Reference execution can use an already instrumented container through `REF_COMPARE_CONTAINER`, or an instrumentation checkout through `REF_COMPARE_SOURCE_ROOT`. Source-root execution creates only a temporary bind-mounted copy. It discovers the single network of the official reference Compose PHP service, or accepts an existing network named by `REF_COMPARE_NETWORK`. Missing or ambiguous networks fail closed. The runner never removes Compose containers, networks, volumes, or databases.
The Ref trace runner seeds `year`, `month`, and `startyear` only in KVStorage's process-local cache. This is required for legacy battle items that read the game clock from `game_env`; it keeps their fixture time deterministic without writing to the shared reference database.
Precomputed traces are accepted only when `BATTLE_REFERENCE_TRACE_PATH` is accompanied by `BATTLE_REFERENCE_MANIFEST_PATH`, or by a sibling `<trace>.manifest.json`. Manifest schema version 1 requires:
```json
{
"schemaVersion": 1,
"fixtureCount": 0,
"fixtureJsonlSha256": "sha256 of normalized fixture JSONL",
"traceCount": 0,
"traceJsonlSha256": "sha256 of the exact trace file bytes"
}
```
Counts and hashes must match exactly. Every trace row must also carry `fixtureIdentity.schemaVersion`, the exact fixture `seed`, and the SHA-256 of that fixture row. Missing, reordered, truncated, appended, or stale trace data is rejected.
@@ -6,7 +6,7 @@
"repeatCnt": 1,
"action": "battle",
"attackerGeneral": {
"no": 1, "name": "공격자", "nation": 1, "turntime": "2026-01-01 00:00:00",
"no": 1, "name": "공격자", "nation": 1, "city": 1, "turntime": "2026-01-01 00:00:00",
"personal": "che_안전", "special2": "che_징병", "crew": 1000, "crewtype": 1100,
"atmos": 100, "train": 100, "intel": 70, "intel_exp": 0, "book": "None",
"strength": 70, "strength_exp": 0, "weapon": "None", "injury": 0,
@@ -28,7 +28,7 @@
"name": "공격국", "gold": 1000, "rice": 10000, "gennum": 1
},
"defenderGenerals": [{
"no": 2, "name": "수비자", "nation": 2, "turntime": "2026-01-01 00:00:00",
"no": 2, "name": "수비자", "nation": 2, "city": 2, "turntime": "2026-01-01 00:00:00",
"personal": "che_안전", "special2": "che_징병", "crew": 1000, "crewtype": 1100,
"atmos": 100, "train": 100, "intel": 60, "intel_exp": 0, "book": "None",
"strength": 60, "strength_exp": 0, "weapon": "None", "injury": 0,