Skip to content

Commit 8315da2

Browse files
committed
Merge remote-tracking branch 'vm/codex/m2-shard-prep' into integration/v2-full
2 parents 7f9d4d7 + 24f340e commit 8315da2

9 files changed

Lines changed: 5078 additions & 31 deletions

File tree

‎server/crates/ferrumc-sim/Cargo.toml‎

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,9 +40,10 @@ thiserror = { workspace = true }
4040

4141
[dev-dependencies]
4242
# The load-or-generate flow is `async` because `WorldStore` is. Tests drive it
43-
# with `#[tokio::test]`; tokio is dev-only here (the library pulls in no runtime)
44-
# and is already the workspace's async runtime.
45-
tokio = { workspace = true }
43+
# with `#[tokio::test]`; `test-util` lets deterministic replay tests pause the
44+
# clock rather than observe wall time. Tokio remains dev-only here (the library
45+
# pulls in no runtime) and is already the workspace's async runtime.
46+
tokio = { workspace = true, features = ["test-util"] }
4647

4748
[lints]
4849
workspace = true

‎server/crates/ferrumc-sim/INVARIANTS.md‎

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,3 +48,50 @@
4848
- A logical region may have at most one ownership claim.
4949
- Shard lifecycle is strictly `Created -> Active -> Draining -> Stopped`.
5050
Rejected transitions leave the prior state unchanged.
51+
- The scheduler defaults to the existing one-shard inline path. Its multi-shard
52+
worker mode is crate-internal, explicitly selected, and not wired into the
53+
application.
54+
- A shadow tick visits each runnable shard exactly once in canonical `ShardId`
55+
order. Persistent workers receive owned, disjoint shard batches through
56+
capacity-one command/result channels; every shard is returned before the next
57+
tick, and worker completion order never changes output publication order.
58+
- Only `Active` shards accept new scheduler input. `Draining` shards continue
59+
ticking admitted work, reject new input, and cannot become `Stopped` until
60+
their tick work is quiescent.
61+
- A worker failure after dispatch permanently poisons that scheduler. A partial
62+
tick is fail-stop and must never be retried.
63+
- Cross-shard workers emit destination-only intents; only the scheduler may
64+
stamp their source, source-local sequence, and completed tick. Workers never
65+
access or send directly to another shard.
66+
- Accepted cross-shard envelopes produced in tick N apply exactly once as a
67+
separate prefix to the destination's tick N+1 execution. They never compete
68+
with the ordinary shard inbox. The prefix can enter `SimShard` only through
69+
an unforgeable scheduler-owned tick capability; sibling modules have no
70+
constructor or mid-tick mutation path.
71+
- Central admission is bounded (1,024 envelopes by default), nonblocking, and
72+
reject-newest after sorting by destination `ShardId`, source `ShardId`, then
73+
source-local FIFO sequence. Rejections return the intact owned envelope.
74+
- Boundary preparation is non-mutating. Tick overflow or pre-dispatch failure
75+
leaves ready envelopes queued; a successful tick commits exactly the
76+
prepared scheduler metadata identities, never floating-point payload
77+
equality. A draining destination cannot stop while an admitted envelope still
78+
targets it.
79+
- Before a multi-shard tick advances, every resident full `ChunkKey`
80+
(world/dimension/typed chunk position) must occur in exactly one registered
81+
shard container. The scan includes clean chunks, so aliases cannot become
82+
competing persist records on a later tick; conflicts leave tick, inbox, and
83+
dirty state unchanged. Spatial region membership is not enforced by this
84+
packet; unique out-of-region residency remains valid until that separate
85+
architecture step lands.
86+
- A successful scheduler tick transfers nonempty per-shard
87+
`ChunkOverlayRecord` batches in canonical `ShardId`/`ChunkPos` order only
88+
after every fallible worker and cross-shard phase succeeds. Each batch is
89+
capped at storage's `MAX_SAVE_BATCH` (4,096); a canonical overflow tail stays
90+
persist-dirty and is emitted by a later tick without blocking or loss. A
91+
deterministic per-map continuation cursor visits that tail before wrapping,
92+
so repeatedly re-dirtying lower chunk keys cannot starve it.
93+
- Persist output is collected from every registered lifecycle state, including
94+
a stopped shard with a deferred tail. Clearing a dirty mask transfers
95+
ownership only into the move-owned scheduler outcome; the simulation still
96+
holds no database handle and mutation-journal output remains a separate
97+
bounded seam.

0 commit comments

Comments
 (0)