인사부 도시 관직 자원과 기본 처리 및 수뇌 알림 추가

This commit is contained in:
2026-09-29 09:41:01 +00:00
parent cd6ded4970
commit 5bafdfc1e7
36 changed files with 2238 additions and 20 deletions
@@ -135,3 +135,10 @@ fence를 가진 채 ENGINE 결과를 기다리던 교착을 제거했다. 투표
`adjustAccountIconForUser`, `requestImmediateAction`, `requestJoinCreateCommand`,
`requestNpcPossessionCommand` caller 검색을 함께 실행해 helper 경유 route를 놓치지
않는다.
### 도시 관직 자원 (2026-09-29)
`nation.cityOfficeRequest`는 `engineAuthedProcedure`로 session actor를 전달한다.
`cityOfficeRequests.ts`의 ENGINE handler가 현재 소유자·소속·수뇌 권한·대상과
기한을 다시 검사하고 요청/임명/결과 로그를 같은 durable transaction으로 저장한다.
세부 기본값과 clock 정책은 [도시 관직 자원](../design/city-office-requests.md)을 따른다.
@@ -3,7 +3,7 @@
## 범위와 판정 규칙
`app/game-api/src/router/**`에서 `.mutation()`으로 선언하고 실제 `appRouter`에 mount한
87개 route를 2026-08-24 기준으로 전수 분류한다. 이 목록은 “mutation transport를
89개 route를 2026-09-29 기준으로 전수 분류한다. 이 목록은 “mutation transport를
사용한다”와 “game DB를 변경한다”를 구분한다. 신규 route가 추가되거나 선언한 router가
mount되지 않으면
`app/game-api/test/directMutationJournalInventory.test.ts`가 실패하므로 소유권과
@@ -37,7 +37,7 @@ writer reconciliation을 포함한다. rolling deployment가 끝난 뒤에만
| durable journal | 19 | `betting.bet`; `diplomacy.destroyLetter`, `diplomacy.respondLetter`, `diplomacy.rollbackLetter`, `diplomacy.sendLetter`; `inherit.checkOwner`; `messages.delete`, `messages.respond`, `messages.send`; `turns.reserved.repeatGeneral`, `turns.reserved.setGeneral`, `turns.reserved.setGeneralBulk`, `turns.reserved.setNation`, `turns.reserved.setNationBulk`, `turns.reserved.shiftGeneral`; `vote.closePoll`, `vote.createPoll`, `vote.submitVote`, `vote.updatePoll` |
| separate access journal | 1 | `public.recordAccess` |
| explicit no realtime consumer | 7 | `board.writeArticle`, `board.writeComment`; `join.listPossessCandidates`; `messages.readLatest`; `turns.reserved.repeatNation`, `turns.reserved.shiftNation`; `vote.addComment` |
| engine owned | 38 | `auction.bidBuyRice`, `auction.bidSellRice`, `auction.bidUnique`, `auction.openBuyRice`, `auction.openSellRice`, `auction.openUnique`; `general.adjustIcon`, `general.buildNationCandidate`, `general.dieOnPrestart`, `general.dropItem`, `general.ensureDieOnPrestartStatus`, `general.instantRetreat`, `general.setMySetting`, `general.vacation`; `inherit.openUniqueAuction`; `join.createGeneral`, `join.getSelectionPool`, `join.possessGeneral`, `join.reselectPoolGeneral`, `join.selectPoolGeneral`; `nation.appoint`, `nation.changePermission`, `nation.kick`, `nation.setBill`, `nation.setBlockScout`, `nation.setBlockWar`, `nation.setNotice`, `nation.setRate`, `nation.setScoutMsg`, `nation.setSecretLimit`; `npc.setGeneralPriority`, `npc.setNationPolicy`, `npc.setNationPriority`; `troop.create`, `troop.exit`, `troop.join`, `troop.kick`, `troop.rename` |
| engine owned | 39 | `auction.bidBuyRice`, `auction.bidSellRice`, `auction.bidUnique`, `auction.openBuyRice`, `auction.openSellRice`, `auction.openUnique`; `general.adjustIcon`, `general.buildNationCandidate`, `general.dieOnPrestart`, `general.dropItem`, `general.ensureDieOnPrestartStatus`, `general.instantRetreat`, `general.setMySetting`, `general.vacation`; `inherit.openUniqueAuction`; `join.createGeneral`, `join.getSelectionPool`, `join.possessGeneral`, `join.reselectPoolGeneral`, `join.selectPoolGeneral`; `nation.appoint`, `nation.changePermission`, `nation.cityOfficeRequest`, `nation.kick`, `nation.setBill`, `nation.setBlockScout`, `nation.setBlockWar`, `nation.setNotice`, `nation.setRate`, `nation.setScoutMsg`, `nation.setSecretLimit`; `npc.setGeneralPriority`, `npc.setNationPolicy`, `npc.setNationPriority`; `troop.create`, `troop.exit`, `troop.join`, `troop.kick`, `troop.rename` |
| mixed saga | 9 | `inherit.buyHiddenBuff`, `inherit.buyRandomUnique`, `inherit.resetSpecialWar`, `inherit.resetStat`, `inherit.resetTurnTime`, `inherit.setNextSpecialWar`; `tournament.cancel`, `tournament.join`, `tournament.placeBet` |
| Redis projection | 6 | `tournament.patchState`, `tournament.seedParticipants`, `tournament.setBettingEntries`, `tournament.setMatches`, `tournament.setParticipants`, `tournament.setState` |
| operational | 3 | `turnDaemon.pause`, `turnDaemon.resume`, `turnDaemon.run` |
@@ -95,3 +95,7 @@ public dashboard event로 내보내지 않는다. browser wake-up은 정밀 enti
4. 따라서 dashboard/map coverage v1 코드 조건은 충족했지만 migration은 rolling deploy
안전을 위해 0을 유지한다. 전체 writer binary 배포가 확인된 뒤에만 activation command를
실행하며, 문제 시 meta를 0으로 내려 즉시 full-compute fallback한다.
도시 관직 자원은 ENGINE 입력 이벤트로 접수·처리하며, `general.meta.cityOfficeRequest`
변경을 front-status journal/SSE에 연결한다. [처리 정책](../design/city-office-requests.md)을
따르며, HTTP actor 검증과 실제 ENGINE PostgreSQL rollback/reload를 별도로 확인한다.
@@ -44,6 +44,7 @@ messages.respond durable-journal direct-endpoint session-user-db-general dynamic
messages.send durable-journal direct-endpoint session-user-db-general endpoint-unit app/game-api/test/messagesRouter.test.ts no-route-actual-db
nation.appoint engine-owned domain-command session-user-engine-general endpoint-unit app/game-api/test/nationPersonnelRouter.test.ts no-route-actual-db
nation.changePermission engine-owned domain-command session-user-engine-general endpoint-unit app/game-api/test/nationPersonnelRouter.test.ts no-route-actual-db
nation.cityOfficeRequest engine-owned core-only session-user-engine-general actual-db app/game-engine/test/cityOfficeRequestPersistence.integration.test.ts no-route-actual-db
nation.kick engine-owned domain-command session-user-engine-general endpoint-unit app/game-api/test/nationPersonnelRouter.test.ts no-route-actual-db
nation.setBill engine-owned domain-command session-user-engine-general endpoint-unit app/game-api/test/nationSettingRouter.test.ts no-route-actual-db
nation.setBlockScout engine-owned domain-command session-user-engine-general endpoint-unit app/game-api/test/scoutBlockRouter.test.ts no-route-actual-db
1 route owner_boundary ref_basis actor_source strongest_evidence evidence_path remaining_gap
44 messages.send durable-journal direct-endpoint session-user-db-general endpoint-unit app/game-api/test/messagesRouter.test.ts no-route-actual-db
45 nation.appoint engine-owned domain-command session-user-engine-general endpoint-unit app/game-api/test/nationPersonnelRouter.test.ts no-route-actual-db
46 nation.changePermission engine-owned domain-command session-user-engine-general endpoint-unit app/game-api/test/nationPersonnelRouter.test.ts no-route-actual-db
47 nation.cityOfficeRequest engine-owned core-only session-user-engine-general actual-db app/game-engine/test/cityOfficeRequestPersistence.integration.test.ts no-route-actual-db
48 nation.kick engine-owned domain-command session-user-engine-general endpoint-unit app/game-api/test/nationPersonnelRouter.test.ts no-route-actual-db
49 nation.setBill engine-owned domain-command session-user-engine-general endpoint-unit app/game-api/test/nationSettingRouter.test.ts no-route-actual-db
50 nation.setBlockScout engine-owned domain-command session-user-engine-general endpoint-unit app/game-api/test/scoutBlockRouter.test.ts no-route-actual-db
@@ -289,6 +289,21 @@
],
"projectionFields": [],
"owner": "game-engine/clock-operation"
},
{
"key": "city-office-request-occurrence",
"policy": "KEEP",
"authorityFields": ["general.meta.cityOfficeRequest.createdTick"],
"projectionFields": [],
"owner": "game-engine/city-office-requests"
},
{
"key": "city-office-request-deadline",
"policy": "SHIFT",
"authorityFields": ["general.meta.cityOfficeRequest.dueTick"],
"projectionFields": [],
"rowScope": "pending requests at or after the reconciliation cut",
"owner": "game-engine/city-office-requests"
}
],
"redis": [