diff --git a/app/game-frontend/e2e/inGameInfo.spec.ts b/app/game-frontend/e2e/inGameInfo.spec.ts
index b18cf61a..fa3c70b6 100644
--- a/app/game-frontend/e2e/inGameInfo.spec.ts
+++ b/app/game-frontend/e2e/inGameInfo.spec.ts
@@ -419,28 +419,52 @@ const install = async (
: [{ action: 'che_징병', args: { crewType: 1, amount: 300 } }],
},
...(denseCurrentCity
- ? Array.from({ length: 12 }, (_, index) => ({
- id: index + 2,
- name: `NPC장수이름이긴${index + 1}`,
- npcState: 2,
- picture: null,
- imageServer: 0,
- nationId: 1,
- nationName: '아국',
- leadership: 60,
- strength: 60,
- intelligence: 60,
- injury: 0,
- officerLevel: 1,
- leadershipBonus: 0,
- defenceTrain: 80,
- crewTypeId: 1,
- crewTypeName: '보병',
- crew: 500,
- train: 90,
- atmos: 90,
- turns: [],
- }))
+ ? [
+ ...Array.from({ length: 12 }, (_, index) => ({
+ id: index + 2,
+ name: `NPC장수이름이긴${index + 1}`,
+ npcState: 2,
+ picture: null,
+ imageServer: 0,
+ nationId: 1,
+ nationName: '아국',
+ leadership: 60,
+ strength: 60,
+ intelligence: 60,
+ injury: 0,
+ officerLevel: 1,
+ leadershipBonus: 0,
+ defenceTrain: 80,
+ crewTypeId: 1,
+ crewTypeName: '보병',
+ crew: 500,
+ train: 90,
+ atmos: 90,
+ turns: [],
+ })),
+ {
+ id: 14,
+ name: '적국NPC',
+ npcState: 2,
+ picture: null,
+ imageServer: 0,
+ nationId: 2,
+ nationName: '적국',
+ leadership: 60,
+ strength: 60,
+ intelligence: 60,
+ injury: 0,
+ officerLevel: 1,
+ leadershipBonus: 0,
+ defenceTrain: null,
+ crewTypeId: null,
+ crewTypeName: null,
+ crew: 500,
+ train: null,
+ atmos: null,
+ turns: [],
+ },
+ ]
: []),
],
forceSummary: {
@@ -1213,6 +1237,7 @@ test('current-city wraps dense general names and only shrinks reserved turns', a
const rows = page.locator('.generals tbody tr');
const reservedTurns = rows.nth(0).locator('.turns');
const npcTurns = rows.nth(1).locator('.turns');
+ const enemyNpcTurns = rows.nth(13).locator('.turns');
await expect(reservedTurns).toContainText('1 : 【보병】 300명 징병');
await expect(reservedTurns).toContainText('2 : 【업】에 화계실행');
await expect(reservedTurns.locator('.turn-line').nth(0)).toHaveAttribute('title', '【보병】 300명 징병');
@@ -1220,6 +1245,8 @@ test('current-city wraps dense general names and only shrinks reserved turns', a
await expect(reservedTurns).toHaveClass(/turns--reserved/);
await expect(npcTurns).toHaveText('NPC 장수');
await expect(npcTurns).not.toHaveClass(/turns--reserved/);
+ await expect(enemyNpcTurns).toHaveText('【적국】 장수');
+ await expect(enemyNpcTurns).not.toHaveClass(/turns--reserved/);
const turnFontSizes = await Promise.all([
reservedTurns.evaluate((element) => getComputedStyle(element).fontSize),
diff --git a/app/game-frontend/src/views/CurrentCityView.vue b/app/game-frontend/src/views/CurrentCityView.vue
index beddbb04..fa3ddaf8 100644
--- a/app/game-frontend/src/views/CurrentCityView.vue
+++ b/app/game-frontend/src/views/CurrentCityView.vue
@@ -310,10 +310,10 @@ const commandBrief = (command: ReservedCommand): string =>
>{{ index + 1 }} : {{ commandBrief(turn) }}
- NPC 장수
{{ general.nationId === 0 ? '재 야' : `【${general.nationName}】 장수` }}
+ NPC 장수
diff --git a/docs/frontend-legacy-parity.md b/docs/frontend-legacy-parity.md
index 5dc44c3e..9c0f3406 100644
--- a/docs/frontend-legacy-parity.md
+++ b/docs/frontend-legacy-parity.md
@@ -221,6 +221,9 @@ instead. The Ref row contract still applies to commands: only a non-NPC
general's reserved turns use `general_turn_text`/`x-small`; `NPC 장수`, foreign
nation, and wanderer labels stay at the table's normal font size. The summary
general-name cell must remain wrappable when many generals share a city.
+The row ownership check precedes the NPC check: an NPC belonging to the viewer's
+nation is labeled `NPC 장수`, while a foreign NPC is labeled with its nation as
+`【국가명】 장수` so its allegiance is not hidden.
징병·모병의 Ref 화면은 다음 collector로 1000/500px DOM, 이미지 natural size,
불가능 병종 toggle과 hover/focus를 수집합니다. 기본 모드는 현재 Ref session을