NPC 결정에서 당시 정책 버전을 조회하고 기존 상세 화면 재사용
This commit is contained in:
@@ -61,7 +61,7 @@ const decision = {
|
||||
coverage: 'PROCEDURES',
|
||||
clockRevision: 1,
|
||||
codeVersion: null,
|
||||
policyRefs: {},
|
||||
policyRefs: { DEFENCE: 'a'.repeat(64) },
|
||||
requestedAction: '휴식',
|
||||
selectedAction: 'che_징병',
|
||||
selectedReason: '징병 선택',
|
||||
@@ -1154,3 +1154,30 @@ test('NPC decision detail retry preserves history and other general information'
|
||||
expect(requests.filter((r) => r.operation === 'playAudit.decisionHistory')).toHaveLength(count);
|
||||
await capture(page, 'desktop-npc-decision');
|
||||
});
|
||||
|
||||
test('NPC decision opens its immutable policy without querying policy history', async ({ page }) => {
|
||||
const requests = await install(page);
|
||||
await page.setViewportSize({ width: 390, height: 844 });
|
||||
await page.goto(gamePath(`/play-audit?tab=generals&general=1&decision=${decision.id}`));
|
||||
await expect(page.getByRole('list', { name: '판단 절차' })).toBeVisible();
|
||||
expect(requests.some((r) => r.operation === 'playAudit.policyVersion')).toBe(false);
|
||||
const before = requests.length;
|
||||
await page.getByText('당시 정책 참조', { exact: true }).click();
|
||||
await page.getByRole('button', { name: '국방 설정 당시 버전 조회', exact: true }).click();
|
||||
await expect(page.getByText(/국가 #2 · 버전 1/)).toBeVisible();
|
||||
expect(requests.slice(before).map((r) => r.operation)).toEqual(['playAudit.policyVersion']);
|
||||
expect(requests.at(-1)?.input).toEqual({ id: 'a'.repeat(64) });
|
||||
await expect(page.getByRole('button', { name: '이전 정책 버전', exact: true })).toHaveCount(0);
|
||||
expect(await page.evaluate(() => Object.hasOwn(window, 'auditInjected'))).toBe(false);
|
||||
await capture(page, 'mobile-decision-policy');
|
||||
await page.reload();
|
||||
await expect(page.getByText(/국가 #2 · 버전 1/)).toBeVisible();
|
||||
expect(requests.some((r) => r.operation === 'playAudit.policyHistory')).toBe(false);
|
||||
await page.getByRole('button', { name: '정책 상세 닫기', exact: true }).click();
|
||||
await expect(page.getByRole('heading', { name: /선택 정책 버전/ })).toHaveCount(0);
|
||||
const policyReads = requests.filter((r) => r.operation === 'playAudit.policyVersion').length;
|
||||
await page.goto(gamePath(`/play-audit?tab=generals&general=1&decision=${decision.id}&policy=${'b'.repeat(64)}`));
|
||||
await expect(page.getByRole('list', { name: '판단 절차' })).toBeVisible();
|
||||
expect(requests.filter((r) => r.operation === 'playAudit.policyVersion')).toHaveLength(policyReads);
|
||||
await expect(page.getByRole('heading', { name: /선택 정책 버전/ })).toHaveCount(0);
|
||||
});
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
import { computed, ref, watch } from 'vue';
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
import { trpc } from '../../utils/trpc';
|
||||
import AuditPolicyVersion from './AuditPolicyVersion.vue';
|
||||
const props = defineProps<{ generalId: number; month?: { year: number; month: number } }>();
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
@@ -17,6 +18,19 @@ const detailLoading = ref(false);
|
||||
let generation = 0;
|
||||
let detailGeneration = 0;
|
||||
const selected = computed(() => (typeof route.query.decision === 'string' ? route.query.decision : null));
|
||||
const policyLabels = {
|
||||
NPC_VALUES: 'NPC 설정 값',
|
||||
NPC_NATION_PRIORITY: '수뇌 우선순위',
|
||||
NPC_GENERAL_PRIORITY: '개인 우선순위',
|
||||
DEFENCE: '국방 설정',
|
||||
};
|
||||
const selectedPolicy = computed(() => {
|
||||
const id = route.query.policy;
|
||||
return typeof id === 'string' && Object.values(detail.value?.decision.summary.policyRefs ?? {}).includes(id)
|
||||
? id
|
||||
: null;
|
||||
});
|
||||
const selectPolicy = (id: string | null) => router.push({ query: { ...route.query, policy: id ?? undefined } });
|
||||
const message = (cause: unknown) => (cause instanceof Error ? cause.message : 'NPC 결정 기록을 조회하지 못했습니다.');
|
||||
const load = async (more = false) => {
|
||||
if (loading.value) return;
|
||||
@@ -191,8 +205,19 @@ watch(
|
||||
<details>
|
||||
<summary>당시 정책 참조</summary>
|
||||
<p v-if="!Object.keys(detail.decision.summary.policyRefs).length">확보된 정책 참조가 없습니다.</p>
|
||||
<p v-for="(id, area) in detail.decision.summary.policyRefs" :key="area">{{ area }}: {{ id }}</p>
|
||||
<p>선택 당시 저장된 국가 설정입니다. NPC별 합성 유효 값과 다를 수 있습니다.</p>
|
||||
<p v-for="(id, area) in detail.decision.summary.policyRefs" :key="area">
|
||||
<button class="legacy-button" @click="selectPolicy(id ?? null)">
|
||||
{{ policyLabels[area] }} 당시 버전 조회
|
||||
</button>
|
||||
</p>
|
||||
</details>
|
||||
<AuditPolicyVersion
|
||||
v-if="selectedPolicy"
|
||||
:id="selectedPolicy"
|
||||
:allow-previous="false"
|
||||
@select="selectPolicy"
|
||||
/>
|
||||
<ol aria-label="판단 절차">
|
||||
<template v-for="chunk in detail.chunks" :key="chunk.ordinal"
|
||||
><li v-for="step in chunk.steps" :key="step.sequence" :value="step.sequence + 1">
|
||||
@@ -221,6 +246,9 @@ watch(
|
||||
min-width: 0;
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
.audit-decisions > section {
|
||||
min-width: 0;
|
||||
}
|
||||
.table-scroll {
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
import { computed, ref, watch } from 'vue';
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
import { trpc } from '../../utils/trpc';
|
||||
import AuditPolicyVersion from './AuditPolicyVersion.vue';
|
||||
|
||||
const props = defineProps<{
|
||||
nationId: number;
|
||||
@@ -10,47 +11,13 @@ const props = defineProps<{
|
||||
to: { year: number; month: number };
|
||||
}>();
|
||||
type History = Awaited<ReturnType<typeof trpc.playAudit.policyHistory.query>>;
|
||||
type Detail = Awaited<ReturnType<typeof trpc.playAudit.policyVersion.query>>;
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
const data = ref<History | null>(null);
|
||||
const detail = ref<Detail | null>(null);
|
||||
const error = ref('');
|
||||
const detailError = ref('');
|
||||
const loading = ref(false);
|
||||
const detailLoading = ref(false);
|
||||
let generation = 0;
|
||||
let detailGeneration = 0;
|
||||
const selected = computed(() => (typeof route.query.policy === 'string' ? route.query.policy : null));
|
||||
const fieldLabels: Record<string, string> = {
|
||||
reqNationGold: '국가 권장 금',
|
||||
reqNationRice: '국가 권장 쌀',
|
||||
reqHumanWarUrgentGold: '유저전투장 긴급포상 금',
|
||||
reqHumanWarUrgentRice: '유저전투장 긴급포상 쌀',
|
||||
reqHumanWarRecommandGold: '유저전투장 권장 금',
|
||||
reqHumanWarRecommandRice: '유저전투장 권장 쌀',
|
||||
reqHumanDevelGold: '유저내정장 권장 금',
|
||||
reqHumanDevelRice: '유저내정장 권장 쌀',
|
||||
reqNPCWarGold: 'NPC전투장 권장 금',
|
||||
reqNPCWarRice: 'NPC전투장 권장 쌀',
|
||||
reqNPCDevelGold: 'NPC내정장 권장 금',
|
||||
reqNPCDevelRice: 'NPC내정장 권장 쌀',
|
||||
minimumResourceActionAmount: '포상/몰수/헌납/삼/팜 최소 단위',
|
||||
maximumResourceActionAmount: '포상/몰수/헌납/삼/팜 최대 단위',
|
||||
minWarCrew: '최소 전투 가능 병력 수',
|
||||
minNPCRecruitCityPopulation: 'NPC 최소 징병 가능 인구 수',
|
||||
safeRecruitCityPopulationRatio: '제자리 징병 허용 인구율 (비율)',
|
||||
minNPCWarLeadership: 'NPC 전투 참여 통솔 기준',
|
||||
properWarTrainAtmos: '훈련/사기진작 목표치',
|
||||
cureThreshold: '요양 기준',
|
||||
CombatForce: '전투 부대 편성',
|
||||
SupportForce: '지원 부대 편성',
|
||||
DevelopForce: '내정 부대 편성',
|
||||
priority: '행동 우선순위',
|
||||
war: '전쟁 금지 설정',
|
||||
scout: '임관 권유 설정',
|
||||
secretlimit: '기밀 공개 기준 (년)',
|
||||
};
|
||||
const labels = { BASELINE: '최초 관측', CHANGE: '실제 변경', OBSERVED_GAP: '관측 누락 이후 기준' };
|
||||
const message = (cause: unknown) => (cause instanceof Error ? cause.message : '정책 이력을 조회하지 못했습니다.');
|
||||
const load = async (append = false) => {
|
||||
@@ -75,22 +42,6 @@ const load = async (append = false) => {
|
||||
if (request === generation) loading.value = false;
|
||||
}
|
||||
};
|
||||
const loadDetail = async () => {
|
||||
const request = ++detailGeneration;
|
||||
detail.value = null;
|
||||
detailError.value = '';
|
||||
detailLoading.value = false;
|
||||
if (!selected.value) return;
|
||||
detailLoading.value = true;
|
||||
try {
|
||||
const response = await trpc.playAudit.policyVersion.query({ id: selected.value });
|
||||
if (request === detailGeneration) detail.value = response;
|
||||
} catch (cause) {
|
||||
if (request === detailGeneration) detailError.value = message(cause);
|
||||
} finally {
|
||||
if (request === detailGeneration) detailLoading.value = false;
|
||||
}
|
||||
};
|
||||
const select = (id: string | null) => router.push({ query: { ...route.query, policy: id ?? undefined } });
|
||||
watch(
|
||||
[
|
||||
@@ -106,13 +57,6 @@ watch(
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
watch(
|
||||
selected,
|
||||
() => {
|
||||
void loadDetail();
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -153,72 +97,7 @@ watch(
|
||||
다음 정책 50개
|
||||
</button>
|
||||
</template>
|
||||
<section v-if="selected" aria-label="선택 정책 버전">
|
||||
<h3>선택 정책 버전 <button class="legacy-button" @click="select(null)">정책 상세 닫기</button></h3>
|
||||
<p v-if="detailLoading" role="status">정책 버전 조회 중…</p>
|
||||
<p v-if="detailError" role="alert">
|
||||
{{ detailError }} <button class="legacy-button" @click="loadDetail">버전 다시 조회</button>
|
||||
</p>
|
||||
<template v-if="detail">
|
||||
<p>
|
||||
국가 #{{ detail.version.nationId }} · 버전 {{ detail.version.revision }} ·
|
||||
{{ labels[detail.version.source] }} · {{ detail.version.year }}년 {{ detail.version.month }}월
|
||||
</p>
|
||||
<p>
|
||||
기록 시각 {{ detail.version.createdAt }} · tick {{ detail.version.tick ?? '미상' }} · 순번
|
||||
{{ detail.version.ordinal }}
|
||||
</p>
|
||||
<p v-if="detail.version.actor">
|
||||
{{ detail.version.actor.name }} (#{{ detail.version.actor.generalId }}) · 당시 국가 #{{
|
||||
detail.version.actor.nationId
|
||||
}}
|
||||
· 직책 {{ detail.version.actor.officerLevel }}
|
||||
</p>
|
||||
<p v-if="detail.version.source !== 'CHANGE'">
|
||||
이 버전은 관측 기준입니다. 이전 값과 변경 주체를 추정하지 않습니다.
|
||||
</p>
|
||||
<p>
|
||||
null은 개별 설정이 없음을 뜻합니다. 설정값을 기록하며 당시 NPC별 유효 값은 여기서 재계산하지
|
||||
않습니다.
|
||||
</p>
|
||||
<div class="table-scroll" tabindex="0" aria-label="정책 전후 값">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>설정</th>
|
||||
<th>변경 전</th>
|
||||
<th>변경 후</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="field in detail.version.fields" :key="field.key">
|
||||
<th scope="row">
|
||||
{{ fieldLabels[field.key] ?? field.key }} <span v-if="field.changed">(변경)</span>
|
||||
</th>
|
||||
<td>
|
||||
<pre>{{ field.beforeJson ?? '관측하지 않음' }}</pre>
|
||||
</td>
|
||||
<td>
|
||||
<pre>{{ field.afterJson }}</pre>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<button
|
||||
v-if="detail.version.previousId"
|
||||
class="legacy-button"
|
||||
@click="select(detail.version.previousId)"
|
||||
>
|
||||
이전 정책 버전
|
||||
</button>
|
||||
<details>
|
||||
<summary>요청 연결</summary>
|
||||
<p>요청 {{ detail.version.requestId ?? '해당 없음' }}</p>
|
||||
<p>입력 순번 {{ detail.version.inputSequence ?? '해당 없음' }}</p>
|
||||
</details>
|
||||
</template>
|
||||
</section>
|
||||
<AuditPolicyVersion v-if="selected" :id="selected" @select="select" />
|
||||
</section>
|
||||
</template>
|
||||
|
||||
|
||||
@@ -0,0 +1,165 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, watch } from 'vue';
|
||||
import { trpc } from '../../utils/trpc';
|
||||
const props = withDefaults(defineProps<{ id: string; allowPrevious?: boolean }>(), { allowPrevious: true });
|
||||
const emit = defineEmits<{ select: [id: string | null] }>();
|
||||
type Detail = Awaited<ReturnType<typeof trpc.playAudit.policyVersion.query>>;
|
||||
const detail = ref<Detail | null>(null);
|
||||
const detailError = ref('');
|
||||
const detailLoading = ref(false);
|
||||
let detailGeneration = 0;
|
||||
const select = (id: string | null) => emit('select', id);
|
||||
const message = (cause: unknown) => (cause instanceof Error ? cause.message : '정책 버전을 조회하지 못했습니다.');
|
||||
const fieldLabels: Record<string, string> = {
|
||||
reqNationGold: '국가 권장 금',
|
||||
reqNationRice: '국가 권장 쌀',
|
||||
reqHumanWarUrgentGold: '유저전투장 긴급포상 금',
|
||||
reqHumanWarUrgentRice: '유저전투장 긴급포상 쌀',
|
||||
reqHumanWarRecommandGold: '유저전투장 권장 금',
|
||||
reqHumanWarRecommandRice: '유저전투장 권장 쌀',
|
||||
reqHumanDevelGold: '유저내정장 권장 금',
|
||||
reqHumanDevelRice: '유저내정장 권장 쌀',
|
||||
reqNPCWarGold: 'NPC전투장 권장 금',
|
||||
reqNPCWarRice: 'NPC전투장 권장 쌀',
|
||||
reqNPCDevelGold: 'NPC내정장 권장 금',
|
||||
reqNPCDevelRice: 'NPC내정장 권장 쌀',
|
||||
minimumResourceActionAmount: '포상/몰수/헌납/삼/팜 최소 단위',
|
||||
maximumResourceActionAmount: '포상/몰수/헌납/삼/팜 최대 단위',
|
||||
minWarCrew: '최소 전투 가능 병력 수',
|
||||
minNPCRecruitCityPopulation: 'NPC 최소 징병 가능 인구 수',
|
||||
safeRecruitCityPopulationRatio: '제자리 징병 허용 인구율 (비율)',
|
||||
minNPCWarLeadership: 'NPC 전투 참여 통솔 기준',
|
||||
properWarTrainAtmos: '훈련/사기진작 목표치',
|
||||
cureThreshold: '요양 기준',
|
||||
CombatForce: '전투 부대 편성',
|
||||
SupportForce: '지원 부대 편성',
|
||||
DevelopForce: '내정 부대 편성',
|
||||
priority: '행동 우선순위',
|
||||
war: '전쟁 금지 설정',
|
||||
scout: '임관 권유 설정',
|
||||
secretlimit: '기밀 공개 기준 (년)',
|
||||
};
|
||||
const labels = { BASELINE: '최초 관측', CHANGE: '실제 변경', OBSERVED_GAP: '관측 누락 이후 기준' };
|
||||
const loadDetail = async () => {
|
||||
const request = ++detailGeneration;
|
||||
detail.value = null;
|
||||
detailError.value = '';
|
||||
detailLoading.value = false;
|
||||
if (!props.id) return;
|
||||
detailLoading.value = true;
|
||||
try {
|
||||
const response = await trpc.playAudit.policyVersion.query({ id: props.id });
|
||||
if (request === detailGeneration) detail.value = response;
|
||||
} catch (cause) {
|
||||
if (request === detailGeneration) detailError.value = message(cause);
|
||||
} finally {
|
||||
if (request === detailGeneration) detailLoading.value = false;
|
||||
}
|
||||
};
|
||||
watch(
|
||||
() => props.id,
|
||||
() => {
|
||||
void loadDetail();
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
</script>
|
||||
<template>
|
||||
<section aria-label="선택 정책 버전">
|
||||
<h3>선택 정책 버전 <button class="legacy-button" @click="select(null)">정책 상세 닫기</button></h3>
|
||||
<p v-if="detailLoading" role="status">정책 버전 조회 중…</p>
|
||||
<p v-if="detailError" role="alert">
|
||||
{{ detailError }} <button class="legacy-button" @click="loadDetail">버전 다시 조회</button>
|
||||
</p>
|
||||
<template v-if="detail">
|
||||
<p>
|
||||
국가 #{{ detail.version.nationId }} · 버전 {{ detail.version.revision }} ·
|
||||
{{ labels[detail.version.source] }} · {{ detail.version.year }}년 {{ detail.version.month }}월
|
||||
</p>
|
||||
<p>
|
||||
기록 시각 {{ detail.version.createdAt }} · tick {{ detail.version.tick ?? '미상' }} · 순번
|
||||
{{ detail.version.ordinal }}
|
||||
</p>
|
||||
<p v-if="detail.version.actor">
|
||||
{{ detail.version.actor.name }} (#{{ detail.version.actor.generalId }}) · 당시 국가 #{{
|
||||
detail.version.actor.nationId
|
||||
}}
|
||||
· 직책 {{ detail.version.actor.officerLevel }}
|
||||
</p>
|
||||
<p v-if="detail.version.source !== 'CHANGE'">
|
||||
이 버전은 관측 기준입니다. 이전 값과 변경 주체를 추정하지 않습니다.
|
||||
</p>
|
||||
<p>
|
||||
null은 개별 설정이 없음을 뜻합니다. 설정값을 기록하며 당시 NPC별 유효 값은 여기서 재계산하지 않습니다.
|
||||
</p>
|
||||
<div class="table-scroll" tabindex="0" aria-label="정책 전후 값">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>설정</th>
|
||||
<th>변경 전</th>
|
||||
<th>변경 후</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="field in detail.version.fields" :key="field.key">
|
||||
<th scope="row">
|
||||
{{ fieldLabels[field.key] ?? field.key }} <span v-if="field.changed">(변경)</span>
|
||||
</th>
|
||||
<td>
|
||||
<pre>{{ field.beforeJson ?? '관측하지 않음' }}</pre>
|
||||
</td>
|
||||
<td>
|
||||
<pre>{{ field.afterJson }}</pre>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<button
|
||||
v-if="allowPrevious && detail.version.previousId"
|
||||
class="legacy-button"
|
||||
@click="select(detail.version.previousId)"
|
||||
>
|
||||
이전 정책 버전
|
||||
</button>
|
||||
<details>
|
||||
<summary>요청 연결</summary>
|
||||
<p>요청 {{ detail.version.requestId ?? '해당 없음' }}</p>
|
||||
<p>입력 순번 {{ detail.version.inputSequence ?? '해당 없음' }}</p>
|
||||
</details>
|
||||
</template>
|
||||
</section>
|
||||
</template>
|
||||
<style scoped>
|
||||
.table-scroll {
|
||||
overflow-x: auto;
|
||||
}
|
||||
table {
|
||||
width: 100%;
|
||||
min-width: 640px;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
th,
|
||||
td {
|
||||
border: 1px solid gray;
|
||||
padding: 6px;
|
||||
text-align: left;
|
||||
}
|
||||
pre {
|
||||
white-space: pre-wrap;
|
||||
overflow-wrap: anywhere;
|
||||
max-width: 400px;
|
||||
font: inherit;
|
||||
margin: 0;
|
||||
}
|
||||
h3 {
|
||||
font-size: var(--sammo-font-size-normal);
|
||||
}
|
||||
[role='alert'] {
|
||||
color: #ffb9b9;
|
||||
}
|
||||
details {
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
</style>
|
||||
@@ -23,6 +23,10 @@ migration58은 기존 장수 인덱스를 `(server, general, year, month, tick,
|
||||
버튼·표 스타일을 재사용한다. 열기/상세 선택/더 보기는 명시적으로 수행하고 polling하지
|
||||
않는다. 결정 URL 복원과 상세 재시도는 상위 장수 목록을 다시 읽지 않는다.
|
||||
절차 coverage와 미수집 코드 버전을 표시하며 전체 후보 조건·유효 정책 연결은 남는다.
|
||||
당시 정책 참조는 공용 `AuditPolicyVersion`으로 연결했다. 기존 정책 이력의 버전 표시·
|
||||
필드 한국어 이름·실패 재시도를 재사용하며 클릭 시 버전1건만 읽는다. 결정의 참조 ID에
|
||||
포함되지 않은 URL policy는 해당 결정의 정책으로 읽거나 표시하지 않는다.
|
||||
국가의 저장 설정과 NPC별 합성 유효 값은 구분하며 현재 설정으로 보충하지 않는다.
|
||||
|
||||
### NPC 결정 저장·복구 기반
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
| 도시 | 현재/월말 소유·내정 상태와 국가별 주둔 장수 상세 연결 | 월말 주둔은 그달 모든 방문자가 아님. 지도 기반 탐색은 미완성 |
|
||||
| 외교 | 국가쌍·기간별 문서 제안/승인/철회/파기, 즉시 합의와 월간·명령 관계 전이, 생성/소멸 관계 | 문서 내용과 실제 관계는 별개. 최초 관측 이전 사건은 복원하지 않음 |
|
||||
| NPC 결정 | 장수별 개인·수뇌 판단, 절차 시도/차단, 관측한 RNG 결과와 선택·실제 실행 | 새 실행부터 수집하며 후보 내부 조건 전체는 미완성 |
|
||||
| 정책 | NPC 국가 값·국가/장수 우선순위·국방 설정의 기준 버전과 변경 전후, 당시 주체 | 수뇌용 공개 화면과 NPC 결정의 정책 참조 연결은 아직 없음 |
|
||||
| 정책 | NPC 국가 값·국가/장수 우선순위·국방 설정의 기준 버전과 변경 전후, 당시 주체 | 수뇌용 공개 화면은 아직 없음. NPC 결정에서 저장된 당시 버전을 직접 조회 가능 |
|
||||
|
||||
목록/그래프와 선택 상세를 분리해 읽는다. 표는 기본50건이며 더 보기를 명시적으로
|
||||
누른다. 현재 상태는 수동으로 조회하며 백그라운드 polling은 하지 않는다. 필터·월·선택
|
||||
@@ -91,7 +91,7 @@ Gateway의 새 진입/권한 catalog도 사용하려면 같은 commit의 Gateway
|
||||
- NPC/유저 자동턴의 개인·수뇌 절차, 정책 차단, RNG utility 결과와 최종 실행 결과는
|
||||
migration 이후 새 실행부터 저장한다. 후보 내부 조건 전체는 아직 없으며 `PROCEDURES`
|
||||
coverage로 구분한다. 장수 상세의 **NPC 결정 기록 조회**에서 선택 월의 목록과 순서별 상세를 읽는다. 과거 결정은 역산하지 않는다.
|
||||
- 결정은 당시 확보된 정책 참조를 보존한다. 합성된 유효 정책 상세와 코드 버전 연결은
|
||||
- 결정은 당시 확보된 정책 참조를 보존하며 **당시 정책 참조**에서 해당 불변 버전을 바로 조회한다. 합성된 유효 정책 상세와 코드 버전 연결은
|
||||
아직 미완성이다. 코드 버전이 주입되지 않은 실행은 null로 남기며 현재 버전으로 메우지 않는다.
|
||||
- 계정/IP HMAC 조사, 상세 자원 이동, 예약 변경/실행 연결, 실패·rollback 조사와 알려진
|
||||
버그 사례 조회는 미완성이다. 자동 탐지·자동 제재 기능도 제공하지 않는다.
|
||||
|
||||
Reference in New Issue
Block a user