feat: 통일 대기 시계를 원자적 reconciliation으로 전환

This commit is contained in:
2026-09-03 10:18:44 +00:00
parent a9f23703d9
commit 69ac6028df
22 changed files with 1300 additions and 251 deletions
+10 -5
View File
@@ -163,11 +163,16 @@
},
{
"key": "unification-wait",
"policy": "FORBID",
"authorityFields": ["world_state.meta.isunited", "world_state.meta.lastTurnTime"],
"projectionFields": [],
"policy": "REBUILD",
"authorityFields": [
"world_state.meta.isunited",
"world_state.meta.unificationClockSuspensionId",
"clock_suspension.cut_tick",
"clock_suspension.cut_wall_at"
],
"projectionFields": ["world_state.meta.lastTurnTime"],
"owner": "game-engine/unification",
"migration": "Replace the lastTurnTime workaround with a durable UNIFICATION_WAIT suspension."
"migration": "Implemented as one exact alignment, optional rate change, invader creation, and revisioned outbox transaction."
},
{
"key": "clock-operation-ledger",
@@ -190,7 +195,7 @@
{
"keyPattern": "sammo:{profile}:clock:active-revision",
"policy": "REBUILD",
"status": "planned"
"status": "implemented-with-db-phase-and-deadline-generation-fence"
},
{
"keyPattern": "sammo:{profile}:auction:timer",
+21 -6
View File
@@ -50,6 +50,23 @@ The authoritative registry is
architecture gate rejects a new tick/revision field that is absent from that
inventory.
## Unification wait
A unification month with an invader choice changes `RUNNING -> SUSPENDED` and
persists a deterministic `UNIFICATION_WAIT` suspension in the same transaction
as the archive, prompts, and final unification state. Only a `raiseInvader`
message response tied to that active suspension may pass the suspended command
queue; all other gameplay remains pending.
The response transaction verifies daemon authority, performs the exact
alignment, applies all participant shifts, optionally changes the turn rate,
then creates the invader nation, deterministic general IDs/RNG results, first
turns, and the final target-revision outbox. The optional rate change refreshes
the outbox with the final base/rate before commit. DB remains `RECONCILING`
until the daemon projection worker applies Redis and verifies the target
revision/generation. Games without an invader choice move directly to
`COMPLETED`.
## DB to Redis boundary
The database transaction leaves the phase `RECONCILING` and creates exactly one
@@ -114,9 +131,7 @@ future anchored realtime profiles as `PREOPEN`, and other profiles as
`RUNNING`. Existing DateTime columns remain projections while tick columns are
authoritative.
Exact reconciliation stays disabled while an active registry participant is
`FORBID`. Tournament writes now carry tick/revision/generation coordinates and
are revision-fenced in Redis. The remaining unification wait participant must
be moved from its `lastTurnTime` workaround to a durable suspension before that
workflow can reach `RUNNING`. Removing this guard to make a partial operation
pass is prohibited.
No active participant remains `FORBID`. Tournament writes carry
tick/revision/generation coordinates and are revision-fenced in Redis.
Unification wait uses the same durable ledger and outbox boundary; the former
temporary `lastTurnTime` save/restore workaround is not part of the workflow.
@@ -46,10 +46,10 @@ mean deployment or production validation.
- [x] All durable input events record accepted tick and accepted revision.
- [x] Processing converts accepted coordinates across revisions or fails closed.
- [x] Gateway pause/resume/open orchestration writes the DB clock phase.
- [ ] Unification wait becomes a durable `UNIFICATION_WAIT` suspension.
- [ ] Alignment, optional rate change, invader IDs/RNG, creation, first schedule,
- [x] Unification wait becomes a durable `UNIFICATION_WAIT` suspension.
- [x] Alignment, optional rate change, invader IDs/RNG, creation, first schedule,
outbox, verification, and RUNNING transition form one retry-safe workflow.
- [ ] Multi-host drift and general-access/clock-operation deadlock tests.
- [x] Multi-host drift and general-access/clock-operation deadlock tests.
## Milestone 5 - test-branch release gate
@@ -57,11 +57,11 @@ mean deployment or production validation.
integration suites.
- [ ] Dedicated PostgreSQL/Redis conditional integration suite with skip count
recorded.
- [ ] Recovery runbook exercised from each incomplete status.
- [x] Recovery runbook exercised from each incomplete status.
- [x] Admin status/readiness exposes revision, phase, participant checksums, and
incomplete outbox state.
- [ ] User-test deployment evidence is recorded separately from Git push.
- [ ] All `FORBID` inventory entries are removed by typed migrations or proven
- [x] All `FORBID` inventory entries are removed by typed migrations or proven
inactive preconditions.
## Evidence log
@@ -112,3 +112,17 @@ mean deployment or production validation.
to `RUNNING`; the Redis clock phase is revision/generation fenced.
- `pnpm --filter @sammo-ts/gateway-api test` passed 313 tests with 35
environment-conditional skips. Gateway typecheck and target lint passed.
### 2026-09-03 - atomic unification wait
- A unification flush now commits the finalization, actionable prompts, and one
deterministic `UNIFICATION_WAIT` suspension together. A late archive failure
rolls the whole boundary back; retry creates one ledger.
- The suspended command queue admits only an invader decision tied to the
active ledger. The daemon-authorized command transaction applies a 36-hour
exact gap, preserves participant positions, changes the fixture rate from 10
to 20 minutes, creates one invader nation and ten deterministic generals with
future first turns, and writes one final-rate projection outbox.
- The dedicated PostgreSQL/Redis fixture reached `RUNNING@2/2` only after the
Redis projection. DB-wall versus a mocked 12-hour host drift and concurrent
general-access lock acquisition completed without drift or deadlock.
+11 -2
View File
@@ -28,6 +28,13 @@ service. The service must re-read participant checksums and either return the
already-applied result or resume the pending outbox. Never create a replacement
revision to hide a failed target revision.
For `UNIFICATION_WAIT`, never rerun invader creation as a separate repair.
The input event, aligned schedules, optional rate, deterministic invader IDs,
reserved turns, and outbox committed together. A committed command with a
`RECONCILING` world therefore needs only the same outbox retry. If the command
transaction rolled back, the original prompt and source revision remain and
the same response can be retried without changing IDs or RNG results.
When an outbox row is `FAILED`, the profile must remain `RECONCILING`. A retry
is safe in both crash locations:
@@ -48,5 +55,7 @@ If participant verification shows an unexpected mutation, stop the profile,
retain the ledger/outbox evidence, and restore the whole game schema from that
backup. Redis projections are then rebuilt from the restored DB revision.
The implementation-plan release gate remains open until these steps have an
automated fixture and an operator-facing status endpoint.
The conditional clock suite exercises `SUSPENDED`, `RECONCILING/PENDING`,
`RECONCILING/FAILED` before and after Redis commit, recovered `APPLIED`, and
final `RUNNING`. The admin status endpoint exposes the phase, revision,
participant checksums, and outbox error needed to choose the matching step.