slither-sim 0.5.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (113) hide show
  1. slither_sim-0.5.0/CHANGELOG.md +462 -0
  2. slither_sim-0.5.0/CONTRIBUTING.md +152 -0
  3. slither_sim-0.5.0/Cargo.lock +678 -0
  4. slither_sim-0.5.0/Cargo.toml +90 -0
  5. slither_sim-0.5.0/LICENSE +21 -0
  6. slither_sim-0.5.0/PKG-INFO +49 -0
  7. slither_sim-0.5.0/PYPI.md +34 -0
  8. slither_sim-0.5.0/README.md +139 -0
  9. slither_sim-0.5.0/assets/logo.svg +8 -0
  10. slither_sim-0.5.0/build.rs +15 -0
  11. slither_sim-0.5.0/docs/README.md +40 -0
  12. slither_sim-0.5.0/docs/bot-api.md +390 -0
  13. slither_sim-0.5.0/docs/c-api.md +96 -0
  14. slither_sim-0.5.0/docs/client-api.md +216 -0
  15. slither_sim-0.5.0/docs/configuration.md +484 -0
  16. slither_sim-0.5.0/docs/evidence-format.md +66 -0
  17. slither_sim-0.5.0/docs/maintainers/releasing.md +144 -0
  18. slither_sim-0.5.0/docs/performance-workloads.md +331 -0
  19. slither_sim-0.5.0/docs/performance.md +205 -0
  20. slither_sim-0.5.0/docs/provenance.md +75 -0
  21. slither_sim-0.5.0/docs/python.md +137 -0
  22. slither_sim-0.5.0/docs/raw-server.md +249 -0
  23. slither_sim-0.5.0/docs/research-workflow.md +439 -0
  24. slither_sim-0.5.0/docs/server-rules.md +192 -0
  25. slither_sim-0.5.0/examples/basic.rs +42 -0
  26. slither_sim-0.5.0/examples/bot.mjs +76 -0
  27. slither_sim-0.5.0/examples/bot.py +85 -0
  28. slither_sim-0.5.0/examples/cautious_bot.rs +75 -0
  29. slither_sim-0.5.0/examples/ffi_bot.py +149 -0
  30. slither_sim-0.5.0/examples/host_load.rs +175 -0
  31. slither_sim-0.5.0/examples/portable_bot.py +99 -0
  32. slither_sim-0.5.0/examples/portable_bot.rs +64 -0
  33. slither_sim-0.5.0/pyproject.toml +30 -0
  34. slither_sim-0.5.0/python/Cargo.toml +19 -0
  35. slither_sim-0.5.0/python/LICENSE +21 -0
  36. slither_sim-0.5.0/python/PYPI.md +34 -0
  37. slither_sim-0.5.0/python/examples/bot.py +47 -0
  38. slither_sim-0.5.0/python/examples/timing.py +145 -0
  39. slither_sim-0.5.0/python/slither_sim.pyi +166 -0
  40. slither_sim-0.5.0/python/src/lib.rs +500 -0
  41. slither_sim-0.5.0/python/tests/run.py +37 -0
  42. slither_sim-0.5.0/python/tests/test_session.py +585 -0
  43. slither_sim-0.5.0/slither_sim.pyi +166 -0
  44. slither_sim-0.5.0/src/bot/data/a1-overrides.json +31 -0
  45. slither_sim-0.5.0/src/bot/data/a4-mean-overrides.json +56 -0
  46. slither_sim-0.5.0/src/bot/data/resolved.json +171 -0
  47. slither_sim-0.5.0/src/bot/kernel.rs +2945 -0
  48. slither_sim-0.5.0/src/bot/mod.rs +83 -0
  49. slither_sim-0.5.0/src/bot/params.rs +150 -0
  50. slither_sim-0.5.0/src/bot/tests.rs +1453 -0
  51. slither_sim-0.5.0/src/boundary.rs +99 -0
  52. slither_sim-0.5.0/src/bounds_index.rs +183 -0
  53. slither_sim-0.5.0/src/client/challenge.rs +85 -0
  54. slither_sim-0.5.0/src/client/encode.rs +1039 -0
  55. slither_sim-0.5.0/src/client/fast_hash.rs +35 -0
  56. slither_sim-0.5.0/src/client/fixtures.rs +102 -0
  57. slither_sim-0.5.0/src/client/forager/conformance.rs +311 -0
  58. slither_sim-0.5.0/src/client/forager/tests.rs +175 -0
  59. slither_sim-0.5.0/src/client/forager.rs +113 -0
  60. slither_sim-0.5.0/src/client/hud.rs +171 -0
  61. slither_sim-0.5.0/src/client/latency.rs +689 -0
  62. slither_sim-0.5.0/src/client/minimap.rs +460 -0
  63. slither_sim-0.5.0/src/client/mod.rs +203 -0
  64. slither_sim-0.5.0/src/client/network.rs +540 -0
  65. slither_sim-0.5.0/src/client/presentation.rs +571 -0
  66. slither_sim-0.5.0/src/client/protocol.rs +1811 -0
  67. slither_sim-0.5.0/src/client/received.rs +507 -0
  68. slither_sim-0.5.0/src/client/server/bot_tests.rs +152 -0
  69. slither_sim-0.5.0/src/client/server.rs +3074 -0
  70. slither_sim-0.5.0/src/client/simulation/bot_tests.rs +135 -0
  71. slither_sim-0.5.0/src/client/simulation/broadcast.rs +774 -0
  72. slither_sim-0.5.0/src/client/simulation/config.rs +159 -0
  73. slither_sim-0.5.0/src/client/simulation/connection.rs +584 -0
  74. slither_sim-0.5.0/src/client/simulation/food.rs +446 -0
  75. slither_sim-0.5.0/src/client/simulation/markers.rs +108 -0
  76. slither_sim-0.5.0/src/client/simulation/pit_tests.rs +374 -0
  77. slither_sim-0.5.0/src/client/simulation/remote.rs +258 -0
  78. slither_sim-0.5.0/src/client/simulation/snakes.rs +463 -0
  79. slither_sim-0.5.0/src/client/simulation/tests.rs +3573 -0
  80. slither_sim-0.5.0/src/client/simulation.rs +582 -0
  81. slither_sim-0.5.0/src/client/transport.rs +231 -0
  82. slither_sim-0.5.0/src/client/visibility.rs +291 -0
  83. slither_sim-0.5.0/src/error.rs +231 -0
  84. slither_sim-0.5.0/src/food_grid.rs +603 -0
  85. slither_sim-0.5.0/src/json.rs +704 -0
  86. slither_sim-0.5.0/src/kiosk.rs +376 -0
  87. slither_sim-0.5.0/src/lib.rs +37 -0
  88. slither_sim-0.5.0/src/main.rs +142 -0
  89. slither_sim-0.5.0/src/model.rs +222 -0
  90. slither_sim-0.5.0/src/observation/mod.rs +558 -0
  91. slither_sim-0.5.0/src/parallel.rs +111 -0
  92. slither_sim-0.5.0/src/physics.rs +521 -0
  93. slither_sim-0.5.0/src/prey.rs +175 -0
  94. slither_sim-0.5.0/src/profiling.rs +68 -0
  95. slither_sim-0.5.0/src/simulator/checkpoint.rs +1261 -0
  96. slither_sim-0.5.0/src/simulator/initialization.rs +717 -0
  97. slither_sim-0.5.0/src/simulator/initialization_tests.rs +327 -0
  98. slither_sim-0.5.0/src/simulator/observation_tests.rs +275 -0
  99. slither_sim-0.5.0/src/simulator.rs +2957 -0
  100. slither_sim-0.5.0/src/spawn/safety.rs +468 -0
  101. slither_sim-0.5.0/src/spawn.rs +124 -0
  102. slither_sim-0.5.0/src/transport/frame.rs +721 -0
  103. slither_sim-0.5.0/src/transport/tests.rs +83 -0
  104. slither_sim-0.5.0/src/transport.rs +858 -0
  105. slither_sim-0.5.0/tests/fixtures/client-food-interest.json +122 -0
  106. slither_sim-0.5.0/tests/fixtures/client-hud-leaderboard.json +208 -0
  107. slither_sim-0.5.0/tests/fixtures/client-presentation.json +2572 -0
  108. slither_sim-0.5.0/tests/fixtures/client-reporting-cadence-near-server.json +68 -0
  109. slither_sim-0.5.0/tests/fixtures/client-reporting-cadence.json +29 -0
  110. slither_sim-0.5.0/tests/fixtures/forager-conformance.json +1 -0
  111. slither_sim-0.5.0/tests/fixtures/forager-native-parity.json +1 -0
  112. slither_sim-0.5.0/tests/fixtures/forager-oracle-linux-x86_64-gnu.json +1 -0
  113. slither_sim-0.5.0/tests/fixtures/forager-oracle-windows-x86_64-msvc.json +1 -0
@@ -0,0 +1,462 @@
1
+ # Changelog
2
+
3
+ ## Unreleased
4
+
5
+ ## [0.5.0](https://github.com/NikitaMGrimm/slither-sim/compare/v0.4.0...v0.5.0) - 2026-10-26
6
+
7
+ ### Breaking
8
+
9
+ - Host settings are grouped: `maxClients` → `players.max`, `bots` →
10
+ `bots.count`, `botThreads` → `bots.threads` and `simulation.latency` →
11
+ `bots.latency`. Sockets no longer inherit the simulation's latency;
12
+ `players.latency` sets their added link delay, with zero processing. Flat keys
13
+ and a non-default host `simulation.latency` are rejected
14
+ ([HostConfig](docs/configuration.md#hostconfig)).
15
+ - Official worlds keep their initial radius, sized for their population. The
16
+ provisional dynamic controller, which did not match official servers, is
17
+ opt-in. Hosts, resets and simulation backends size official worlds for all
18
+ their snakes, scaling food with area and keeping the official prey density.
19
+ Fixed radii, sandbox and Battledome are unchanged
20
+ ([population sizing](docs/configuration.md#population-sizing)).
21
+ - `SimulationConfig.broadcastTicks` is removed: gameplay updates go out every
22
+ tick, and configs that set it are rejected
23
+ ([SimulationConfig](docs/configuration.md#simulationconfig)).
24
+ - `LatencyConfig.jitterSeconds` and `congestion` are replaced by `variation`
25
+ (lognormal per-message delay with a slow correlated part) and `pauses` (short
26
+ delivery holds per direction); the old fields are rejected.
27
+ `LatencyConfig::measured_route()` is fitted to one home connection to an
28
+ official server; near-server captures attribute its pauses to that connection
29
+ and its collector, not the server
30
+ ([variation and pauses](docs/configuration.md#variation-and-pauses)).
31
+ - The portable `client` JSON protocol is now `slither-client-json-v2`, because
32
+ v0.4.0 requests with the removed broadcast and latency fields are rejected.
33
+ The frame contract stays `slither-client-v2`
34
+ ([portable bots](docs/client-api.md#interface-versions)).
35
+ - Sessions tell a rejected step (state unchanged) from a terminal failure (all
36
+ worlds cleared; reset required). For example, a pickup that would overflow a
37
+ snake's mass completes its tick without the gain and stops the world. `serve`
38
+ uses `slither-sim-json-v3` with `sessionEnded` in error replies, Rust returns
39
+ `transport::StepError::{Rejected, Ended}`, C returns `SLITHER_ENDED` (4) and
40
+ Python raises `SessionEndedError`. Successful replies are unchanged
41
+ ([session errors](docs/bot-api.md)).
42
+ - C ABI `slither-sim-abi-v2` (`SLITHER_ABI_VERSION` 2) adds `snake_kill_score`
43
+ after `snake_kills` in `slither_frame` and the `SLITHER_DEATH_PIT` death code;
44
+ rebuild against the new header ([C ABI](docs/c-api.md#compatibility)). Rust
45
+ adds `DeathReason::Pit`, and `transport::REASONS` has a third entry
46
+ ([frames](docs/bot-api.md#frames)).
47
+ - With recording off, `NetworkBackend::drain_records` and the JSON `wire` op
48
+ return an error, as simulation already did
49
+ ([recording](docs/client-api.md#recording-and-network-bounds)).
50
+ - Arbitrary caps are gone; each [limit](docs/configuration.md#limits) is a
51
+ representation bound, a warning or a named policy. Hosts and simulated clients
52
+ take up to 65,535 agents (was 128) and 65,536 prey (was 10,000). The caps on
53
+ `foodCount`, ambient refill, `serve` agents and worlds, decision ticks, and
54
+ latency, processing and network intervals are removed; `info` reports the
55
+ `serve` maxima. `radius` and `radiusUpdates[].radius` reject values above
56
+ `1e150`, matching checkpoints. Validation messages changed.
57
+ - Sliding boundaries need `dt` below 16 / 437.5 s (about 36.57 ms), so a
58
+ boosting minimum-size snake cannot pass its contact lead; this replaces the
59
+ 32 ms cap. Hard boundaries accept steps up to about 6.3 × 10¹³ s.
60
+ `maxPerCell: 0` admits no food, and kiosk rates, prey speeds, prey turn rate
61
+ and nutrition factor may be zero ([limits](docs/configuration.md#limits)).
62
+ - Ticks long enough for a snake to outrun the incremental body window warn
63
+ instead of failing, and such snakes are resent in full. Incremental updates
64
+ carry up to three new anchors, up from two
65
+ ([packet timing](docs/server-rules.md#packet-timing)).
66
+ - Simulated round trips are no longer clamped to 2 s, and pings slower than 2 s
67
+ are reported instead of dropped
68
+ ([timing](docs/configuration.md#timing-and-ping)).
69
+ - The minimum Rust version is 1.99 (was 1.86).
70
+ - `Host::poll()` is removed. `Host::bind(config)?.start()?` returns a
71
+ `RunningHost` with load counters, `wait()` and `stop()`; dropping it stops the
72
+ host, and `Host::run()` starts and waits. Physics, sockets and bots run on
73
+ separate threads ([embedding](docs/raw-server.md#embedding-and-load-checks)).
74
+ - Rust `Simulator` state is private, with borrowed read accessors and checked
75
+ lifecycle, replay and food/prey edits. `WorldOptions` selects an empty,
76
+ dormant or placed initial population; `Placement` defaults to the existing
77
+ test ring. `add`, public `update_mass` and the two-call speed import are
78
+ removed. Typed `Session::prepare_reset`/`commit_reset` keeps rejected resets
79
+ atomic, and Python resets skip a full-frame JSON round trip. Config keys, wire
80
+ replies and contract IDs are unchanged
81
+ ([Rust API](docs/bot-api.md#rust-library)).
82
+ - Continuation uses validated `Simulator::checkpoint`, `from_checkpoint` and
83
+ atomic `restore` instead of Simulator/runtime serde. Checkpoints use
84
+ `slither-sim-checkpoint-v1` / `slither-sim-rules-v1`; debug snapshots cannot
85
+ restore worlds.
86
+ - Core calls return `SimError`; `step`/`step_profiled` return
87
+ `CoreStepError::{Rejected, Stopped}`, and `transport::StepError` carries
88
+ `SimError` causes. Match `kind` or `code()` rather than message text, and read
89
+ `Simulator::status()` for the original stop cause. `ErrorCode`, `ErrorKind`
90
+ and `Constraint` are non-exhaustive, so matches need a wildcard arm. Wire
91
+ errors are unchanged ([Rust errors](docs/bot-api.md#rust-library)).
92
+ - `physics::crossed_anchor` drops its unused argument,
93
+ `physics::append_client_anchor` is removed, and `Minimap::from_snakes` returns
94
+ `Result` for an invalid side.
95
+
96
+ ### Added
97
+
98
+ - A `bot` feature, on by default through `networking`, adds the frozen Forager
99
+ policy (`bot::Forager`) and `client::ForagerBot`. The default policy is A4
100
+ (A4-mean); A1 stays selectable. The [adoption evidence](https://github.com/NikitaMGrimm/slither-sim/blob/main/research/bots/2026-10-26-a4-adoption/REPORT.md)
101
+ records gameplay, cost and the maintainer's mean-cost gate. The
102
+ [`cautious_bot`](examples/cautious_bot.rs) example runs it.
103
+ - `pip install slither-sim`: the Python package is published to PyPI with each
104
+ crate release. Stable-ABI wheels cover CPython 3.11+ on Linux (x86-64, ARM64),
105
+ macOS (Apple silicon, Intel) and Windows (x86-64); other platforms build from
106
+ source with Rust 1.99+ ([Python guide](docs/python.md)).
107
+ - [Release archives](README.md#download-a-release) hold the CLI, the C ABI
108
+ library and header, and `SHA256SUMS`, for Linux (x86-64, ARM64; glibc 2.17+),
109
+ macOS (Apple silicon, Intel) and Windows (x86-64). They are unsigned.
110
+ - [Battledome](docs/configuration.md#battledome), our own mode:
111
+ `Config::battledome()` or `"preset": "battledome"` gives a fixed 3,584-unit
112
+ arena with a one-way pit, a permanent 400-point size floor (boosting at it is
113
+ free and drops nothing; death food returns only mass above it), a 15-minute
114
+ rolling kill score, no prey, and ambient refill that counts all food. Newborns
115
+ start in an outer 2,684–3,400-unit band once body, head path and departure are
116
+ clear; waiting socket players go before background bots. Other rules follow
117
+ the official preset. New world settings: `pit`, `spawnRadius`,
118
+ `spawnRadiusMax`, `killScoreSeconds`, `minScore` and
119
+ `ambientRefillCountsAllFood`.
120
+ - The stock browser shows a pit's edge as a ring of decorative pellets. Physics
121
+ and bot food are unchanged; socket bots get real food only by sending the
122
+ `X-SlitherSim-Food: physical` upgrade header
123
+ ([Battledome](docs/configuration.md#battledome)).
124
+ - World configs take `"preset": "official" | "sandbox" | "battledome"` in
125
+ `serve` and Python resets, `host` config files and `client` simulation
126
+ requests; explicit fields override the preset
127
+ ([presets](docs/configuration.md#presets)).
128
+ - Frames carry each snake's rolling kill score: `snakes.killScore` in `serve`
129
+ JSON (an additive `slither-sim-json-v3` column), `kill_score` in `FrameRef`,
130
+ `snake_kill_score` in Python and the C ABI, and `Simulator::kill_score` in
131
+ Rust ([frames](docs/bot-api.md#frames)).
132
+ - `Config::warnings`, `SimulationConfig::warnings` and `HostConfig::warnings`
133
+ flag valid but unmeasured settings: a `dt` outside the measured 6.94–7.07 ms
134
+ loop, or ticks long enough to outrun the incremental body window
135
+ (`dt × 437.5 ≥ 126`). `serve` returns them on reset, the CLI prints them, and
136
+ Python's `Session.reset` issues `UserWarning`s
137
+ ([limits](docs/configuration.md#limits)).
138
+ - Host `bots.threads` (default 2) runs background bots on their own threads,
139
+ apart from physics and sockets. The `host_load` example measures simulation
140
+ load, deferred ticks and extra delivery delay
141
+ ([hosting](docs/raw-server.md#embedding-and-load-checks)).
142
+ - `SimulationConfig.delivery` chooses when visible state is sent. The default,
143
+ `responsive`, is our own design and sends steering, speed and mass changes in
144
+ the next frame; `official` keeps the measured pacing. Train bots meant for
145
+ official servers with `official`
146
+ ([SimulationConfig](docs/configuration.md#simulationconfig)).
147
+ - A localhost-only host/capture/compare command records transport baselines and
148
+ checks them against official-reference gates
149
+ ([transport parity](docs/research-workflow.md#transport-parity)).
150
+
151
+ ### Changed
152
+
153
+ - Gameplay updates go out every physics tick (7 ms by default). `official`
154
+ delivery uses measured per-family pacing: motion and standalone mass changes
155
+ at least 6 and 15 ticks apart per snake, anchors at once, ambient food on the
156
+ shared 504 ms maintenance cycle and prey corrections on one clock per prey.
157
+ These are partial models
158
+ ([packet timing](docs/server-rules.md#packet-timing)).
159
+ - Minimaps and leaderboards follow server-wide clocks at the official mean
160
+ periods (`minimapIntervalSeconds` 2.504, `leaderboardIntervalSeconds` 3.008).
161
+ A join waits for each clock's next tick instead of getting an immediate board
162
+ ([packet timing](docs/server-rules.md#packet-timing)).
163
+ - Input and pings that arrive during a tick take effect in that tick's frame,
164
+ and pongs lead it, removing a tick from input delay and measured ping
165
+ ([timing](docs/configuration.md#timing-and-ping)).
166
+ - Host and native background bots and both Rust bot examples use the shared
167
+ default Forager, and boost-only changes are now sent
168
+ ([portable bots](docs/client-api.md#rust)).
169
+ - Transport checks compare against timing recorded near the official servers
170
+ instead of home captures, whose delay spikes came from the home route. New
171
+ fixtures cover rare server timing exceptions; simulator cadence is unchanged
172
+ ([reference audit](https://github.com/NikitaMGrimm/slither-sim/blob/main/research/fidelity/2026-10-26-server-timing-references/REPORT.md)).
173
+ - New S-shaped snake logo and square project icon, paired with SlitherClient's
174
+ C-shaped snake.
175
+
176
+ ### Fixed
177
+
178
+ - A default `host` no longer refuses every player with `No clear spawn
179
+ position`. Host arenas start sized for all seats, and a crowded world places a
180
+ join at the most open spot ([hosting](docs/raw-server.md#presets)).
181
+ - A host no longer drops a healthy client when a message near the 1 MiB limit
182
+ follows a small one that is still waiting for its batch to fill.
183
+ - The host bounds raw socket reads per poll, so one long fragmented message
184
+ cannot stall other connections. A background bot whose decoder fails
185
+ reconnects alone, and stopping no longer waits out a long physics tick.
186
+ - A new world starts at its configured radius instead of one population step
187
+ smaller, so resetting with the returned config reproduces the first frame.
188
+ `Placement::TestRing` rejects initial bodies that extend outside a small
189
+ arena, and a refused hosted join says the candidate search was exhausted
190
+ rather than implying the arena is full.
191
+ - Prey respawn is no longer capped at one per tick, and a replacement waits a
192
+ full `respawnSeconds` after the catch. With a coarse `dt`, pickup follows each
193
+ prey motion step instead of the straight line between endpoints, and prey
194
+ motion that overflows ends the world with an error instead of producing NaN
195
+ positions.
196
+ - Prey whose IDs share the low 16 bits (such as 0 and 65536) no longer hide each
197
+ other on the wire; all 65,536 wire IDs can be live
198
+ ([prey settings](docs/configuration.md#prey)).
199
+ - Prey catches can no longer make `fam` or `length` infinite: settings where one
200
+ catch overflows are rejected, and accumulated overflow stops the world
201
+ ([limits](docs/configuration.md#limits)).
202
+ - Kiosk shrinkage no longer deletes pending growth: it removes only mass the
203
+ score shows, and a zero shrink rate changes nothing.
204
+ - A tiny food `cellSize` no longer merges distinct
205
+ [admission cells](docs/configuration.md#food-admission-limits).
206
+ - Native commands with processing delay apply in the tick they arrive instead of
207
+ one tick later. Cancelling or non-finite one-way legs fail validation, and
208
+ latency scheduling returns an error instead of hanging or producing
209
+ non-finite times.
210
+ - Native recording failures leave pending decisions and client clocks intact,
211
+ and rejected sends no longer suppress a later boost change.
212
+ - Unrepresentable `serve` columns, `worlds × agents` products and host client
213
+ counts return errors instead of panicking. A failed reset keeps the previous
214
+ session; a failed step frame ends it ([session errors](docs/bot-api.md)).
215
+ - A world stopped by mass overflow stays stopped after checkpoint restore
216
+ ([limits](docs/configuration.md#limits)).
217
+ - `Config::deserialize` called directly from Rust accepts presets, like JSON
218
+ parsing ([presets](docs/configuration.md#presets)).
219
+ - Python `Session.reset` issues its warnings before replacing worlds, so a
220
+ warning raised as an error keeps the previous session
221
+ ([Python](docs/python.md)).
222
+
223
+ ### Performance
224
+
225
+ - The host uses about 5% less CPU per tick at 250 and 500 socket clients, with
226
+ byte-identical output. Unchanged food subscriptions stay in place, recipients
227
+ with the same history of a snake share one encoded delta, and delivered
228
+ frames reach the socket without an extra copy. 250 clients now narrowly meet
229
+ the 10 ms delivery grade on one core; 500 still fall behind
230
+ ([study](https://github.com/NikitaMGrimm/slither-sim/blob/main/research/performance/2026-10-26-host-efficiency/REPORT.md)).
231
+ - The host hands players' frames to the socket thread before encoding bots'
232
+ frames. With 480 localhost bots, mean ping fell from about 8.3 to 7.1 ms and
233
+ p99 from 17–19 to 11–13 ms
234
+ ([host load checks](docs/raw-server.md#embedding-and-load-checks)).
235
+ - Crowded hosts spend far less time choosing spawn positions: candidates stop
236
+ being scored once they cannot win, and the chosen pose is unchanged. A
237
+ 64-seat Battledome host drops from about 45 to 3.5 ms per tick. Background
238
+ bots refused a spawn wait one second before retrying; socket players keep
239
+ their own join timing
240
+ ([study](https://github.com/NikitaMGrimm/slither-sim/blob/main/research/performance/2026-10-25-battledome-spawn-cost/REPORT.md)).
241
+ - Batch observations (`observe_all_into`, `observe_selected_into`, transport
242
+ frames and `serve`) are 16–19% faster sequentially and 5–12% faster in
243
+ parallel in the 96- and 128-snake benchmark worlds, with identical results.
244
+ Synthetic worst cases are slower: up to 15% when food clusters away from
245
+ observers, 2.5–4% with sparse or narrow food
246
+ ([study](https://github.com/NikitaMGrimm/slither-sim/blob/main/research/performance/2026-10-24-observation-food-index/REPORT.md)).
247
+ - A [native Linux capacity study](https://github.com/NikitaMGrimm/slither-sim/blob/main/research/performance/2026-10-26-host-capacity/REPORT.md)
248
+ finds that one core keeps official-like delivery for 100 socket clients and
249
+ real time for 500 background bots, and profiles where the host's CPU goes.
250
+
251
+ ## [0.4.0](https://github.com/NikitaMGrimm/slither-sim/compare/v0.3.1...v0.4.0) - 2026-10-24
252
+
253
+ ### Added
254
+
255
+ - *(python)* add an in-process Python package over transport sessions ([#64](https://github.com/NikitaMGrimm/slither-sim/pull/64))
256
+ - *(tools)* measure serve transport cost at Python and Node controllers ([#62](https://github.com/NikitaMGrimm/slither-sim/pull/62))
257
+ - *(transport)* drive sessions in-process and add a C ABI ([#63](https://github.com/NikitaMGrimm/slither-sim/pull/63))
258
+ - *(serve)* [**breaking**] replace slither-sim-json-v1 with columnar multi-world frames ([#61](https://github.com/NikitaMGrimm/slither-sim/pull/61))
259
+ - *(serve)* [**breaking**] add --parallel and pass ServeOptions to transport::serve ([#59](https://github.com/NikitaMGrimm/slither-sim/pull/59))
260
+
261
+ ### Other
262
+
263
+ - cache Rust builds, lint once and add macOS jobs ([#67](https://github.com/NikitaMGrimm/slither-sim/pull/67))
264
+ - streamline user and contributor guides ([#65](https://github.com/NikitaMGrimm/slither-sim/pull/65))
265
+
266
+ ### Breaking
267
+
268
+ - `slither-sim serve` speaks `slither-sim-json-v2`, and v1 is removed. Replies
269
+ carry one frame per world: everything any living slot sees, once, in columns,
270
+ with each slot's view as row indices into them. With 128 living snakes, replies
271
+ are about 12× smaller, the server answers about twice as fast and Python's
272
+ `json.loads` is about 11× faster.
273
+ - To migrate, send one `{"heading":[...],"boost":[...]}` step entry per world
274
+ (`null` headings for dead slots) and read `result.worlds[w].views[slot]`.
275
+ `observe` and `elapsedSeconds` are removed (use `ticks × config.dt`), and
276
+ `snapshot` takes `world`. See the [bot API](docs/bot-api.md).
277
+ - Frames round coordinates to 0.01 world units, headings to 0.0001 rad and speeds
278
+ to 0.01 units/s; other numbers stay exact. Real players mostly receive coarser
279
+ protocol-19 data, and the worst error is 0.05% of the smallest snake radius;
280
+ see the [observation precision study](https://github.com/NikitaMGrimm/slither-sim/blob/main/research/fidelity/2026-10-24-observation-precision/REPORT.md).
281
+ The simulation, snapshots, the portable client and the WebSocket host keep full
282
+ precision, so hosted players are unaffected.
283
+
284
+ ### Added
285
+
286
+ - One `serve` session runs many worlds: `reset` takes `worlds` (up to 1,024),
287
+ world `w` uses seed `seed + w`, and worlds step in parallel on every core.
288
+ `slither-sim serve --threads N` caps the threads; replies do not depend on it.
289
+ - `transport::Session` drives `serve` sessions in-process. `request` answers a
290
+ JSON request line with the exact reply line `serve` writes. `step` takes
291
+ heading and boost slices for every slot of every world and writes no JSON;
292
+ `frame` then reads each world's frame as typed, read-only columns with the
293
+ values the JSON frame prints. See [in-process sessions](docs/bot-api.md#in-process-sessions).
294
+ - `slither-sim-abi-v1`, a C ABI over these sessions, in the unpublished
295
+ `slither-sim-ffi` workspace crate (`ffi/`, with a hand-written header). Any
296
+ language that can call C can reset through JSON, then step from typed arrays
297
+ and read frame columns through pointers. `examples/ffi_bot.py` drives it with
298
+ Python's standard-library `ctypes`. See the [C ABI guide](docs/c-api.md).
299
+ - The `slither-sim` Python package (`python/`, import `slither_sim`; not yet on
300
+ PyPI) runs these sessions in-process. `Session.step` takes float64 and
301
+ bool/uint8 buffers, such as numpy arrays, or plain sequences, and releases the
302
+ GIL; `Session.frame` returns the columns as `array.array` copies. With 128
303
+ agents, a Python decision loop took 0.27 ms per step against 0.56 ms through
304
+ JSON requests. It shares the crate's version, as does `slither-sim-ffi`, through
305
+ `[workspace.package]`. See the [Python guide](docs/python.md).
306
+
307
+ ## [0.3.1](https://github.com/NikitaMGrimm/slither-sim/compare/v0.3.0...v0.3.1) - 2026-10-24
308
+
309
+ ### Changed
310
+
311
+ - *(simulator)* reuse per-step buffers between steps ([#57](https://github.com/NikitaMGrimm/slither-sim/pull/57))
312
+ - *(transport)* write JSON replies without building a Value tree ([#55](https://github.com/NikitaMGrimm/slither-sim/pull/55))
313
+ - *(client)* share food sector listings and index known snakes by slot ([#54](https://github.com/NikitaMGrimm/slither-sim/pull/54))
314
+ - *(observation)* index batch observation food in a 2D grid ([#53](https://github.com/NikitaMGrimm/slither-sim/pull/53))
315
+ - *(simulator)* track food occupancy and pickup candidates across a step ([#52](https://github.com/NikitaMGrimm/slither-sim/pull/52))
316
+ - *(simulator)* add exact broad phases for collisions and prey pickup ([#51](https://github.com/NikitaMGrimm/slither-sim/pull/51))
317
+
318
+ ### Added
319
+
320
+ - List benchmark workloads with `perf_baseline --list` from one registration table.
321
+ New synthetic steady-state workloads cover sandbox, official worlds at 30–500
322
+ snakes, a dense no-prey arena standing in for a future Battle Dome, and an
323
+ official in-process client path, with deaths, death food and respawns inside the
324
+ measured and traced windows. The six historical cases and their traces are unchanged.
325
+ See [performance workloads](docs/performance-workloads.md).
326
+ - Compare a base ref with the working tree in one command:
327
+ `tools/perf_compare.py --base REF` freezes both source snapshots, builds them
328
+ in separate targets, checks full-state trace parity, records phase profiles and
329
+ runs paired timings. Runners and PR comparisons take workloads, sample sizes
330
+ and trace windows from the catalog; PR CI times and traces its `ci` suite.
331
+ - Observe on several cores: `Simulator::set_parallel(true)` splits large
332
+ `observe_all` and `observe_selected` batches over the rayon thread pool, with
333
+ identical results. Off by default; needs the new default `parallel` feature.
334
+ `perf_baseline --parallel` times it.
335
+ - Refill a caller-owned buffer with `Simulator::observe_all_into` and
336
+ `observe_selected_into`. Observations are identical to `observe_all` and
337
+ `observe_selected`, but reuse the previous call's vectors; on Windows, bot
338
+ loops that observe every decision ran 1.8–3.5× faster than with `observe_all`.
339
+ `slither-sim serve` keeps one such buffer per session.
340
+
341
+ ## [0.3.0](https://github.com/NikitaMGrimm/slither-sim/compare/v0.2.1...v0.3.0) - 2026-10-23
342
+
343
+ ### Maintenance
344
+
345
+ - publish manual releases and reset the automatic timer
346
+ - clarify fidelity gaps and simplify navigation ([#46](https://github.com/NikitaMGrimm/slither-sim/pull/46))
347
+ - measure PRs only when measured inputs change ([#44](https://github.com/NikitaMGrimm/slither-sim/pull/44))
348
+ - separate maintained guides from research records ([#41](https://github.com/NikitaMGrimm/slither-sim/pull/41))
349
+
350
+ ### Changed
351
+
352
+ - **Breaking Rust API:** replace `Config::realistic(n)` with `Config::official(n)`.
353
+ `Config::default()` and omitted JSON world settings now select the official
354
+ approximation, with population-driven radius and nonzero prey. Existing kiosk,
355
+ food-admission, sliding-boundary and retained-speed models remain enabled.
356
+ Use `Config::sandbox()` or explicit JSON settings for the previous compact,
357
+ fixed-radius scene. Model estimates and their limitations remain documented;
358
+ no exact official-server equivalence is claimed.
359
+
360
+ ## [0.2.1](https://github.com/NikitaMGrimm/slither-sim/compare/v0.2.0...v0.2.1) - 2026-10-23
361
+
362
+ ### Changes
363
+
364
+ - Bound food-sector clearing in bundled client frames, including preflight
365
+ validation, to avoid repeatedly scanning unrelated retained food.
366
+ - Bound minimap rasterization for extreme geometry while preserving ordinary
367
+ output, and reject frames exceeding the aggregate minimap decoding work budget.
368
+ - Reject protocol-19 simulation-client prey sizes that cannot fit the wire format.
369
+ - Index collision body bounds by stable snake slot and skip observation-index
370
+ construction when no selected snake is alive. Add reproducible offline
371
+ workloads, full-state parity traces and [fixture-specific performance reports](docs/performance.md).
372
+ - Add isolated observation-phase diagnostics, retained-client workloads and
373
+ optional whole-comparison deadlines with retained incomplete-run evidence.
374
+ - Automate compatible patch-release preparation and publication with a 12-hour
375
+ cooldown and protected checks for release-bot PRs; see [releasing](docs/maintainers/releasing.md).
376
+ - Add informational performance and fidelity comparisons to PR CI. Synthetic
377
+ comparisons do not establish official-server fidelity or general speedups.
378
+ - Reduce PR-comparison overhead and report reconnect work separately in
379
+ retained-client measurements.
380
+
381
+ ## 0.2.0 (2026-10-23)
382
+
383
+ - Rust compatibility: `SimulationConfig` adds `local_records`, defaulting to
384
+ false. Update exhaustive struct literals or use `..Default::default()`.
385
+ The JSON protocol identifiers remain unchanged.
386
+ - Publish the current evidence-constrained host defaults: finite 1,500-unit
387
+ snake/prey coverage, retained food sectors, 136-cell minimaps and independent
388
+ minimap/leaderboard clocks. The compact default arena and provisional ecology remain
389
+ explicit; no exact official-server preset or overall accuracy is claimed.
390
+ - Retain paired-snake targets through bounded stale-pose pauses without granting
391
+ clean-route credit, and screen food-route corridors against complete received
392
+ bodies. Add controlled snake/food collectors, ordered sector diagnostics and
393
+ explicit paired capture budgets up to 3,600 seconds.
394
+
395
+ - Run declared paired-prey routes through the managed evidence collector and
396
+ verify route settings against the plan; retain process outcomes across audit errors.
397
+
398
+ - Move crossing acquisition's near waypoint inward and hold achieved endpoints
399
+ for observation; retain commanded hold offsets and release timing in metadata.
400
+
401
+ - Require fresh reference geometry before crossing endpoints advance; separate
402
+ the initial approach from completed legs and retain exact endpoint witnesses.
403
+ Record the paired visibility recheck's model failures without retuning physics.
404
+
405
+ - Hash and decode the same replay-source snapshot; retain protocol and tool
406
+ identities and distinguish replay-window checks from a full evidence audit.
407
+
408
+ - Compare verified evidence batches with bounded permutation tests and declared
409
+ exposure/effect gates; add manual, read-only CI intake for pinned bundles.
410
+ Keep measured calibration separate from synthetic prey/radius behavior tests.
411
+
412
+ - Keep empty food subscriptions internal and send per-connection minimap XOR
413
+ deltas when shorter. Add fixed diagonal collection routes and clarify the
414
+ source/evidence boundary, ecology models and offline collision rationale.
415
+
416
+ - Match the default prey turn setup to physics; give leaderboards independent
417
+ deadlines and bound prey reporting on gameplay broadcasts. Add an offline packet
418
+ census, source-pinned cadence fixture and generic localhost capture workflow;
419
+ keep recording ping timing through post-death observation and capture shutdown.
420
+ - Keep correcting predicted prey positions even when authoritative grid/motion
421
+ is unchanged, preventing drifting ghosts and later snap-backs.
422
+ - Use shared finite 1,500-unit snake/prey coverage and retained food-sector interest;
423
+ restore 136-cell minimaps with an independent clock. Require safe whole-body
424
+ admission for hosted startup, joins and respawns, with no cramped fallback.
425
+ - Make synthetic world-run records/victory entry opt-in (`localRecords`, default false).
426
+
427
+ - Restore source-linked pellet palettes, persistent ambient colors, birth types
428
+ and explicit pickup attribution from the earlier client presentation layer.
429
+
430
+ - Keep answering post-death pings so the stock browser's death fade and victory
431
+ entry do not trigger lag slowdown; document a local endpoint that survives Play Again.
432
+ - Continue opponent, food and prey updates through the stock death animation,
433
+ preserving broadcast cadence while suppressing repeated death and minimap reports.
434
+
435
+ - Keep configured prey sizes positive and within their bounds, ambient food
436
+ inside tiny arenas, and finite extreme respawn poses numerically usable.
437
+ - Disambiguate initial protocol timing headers and bound retained client bodies
438
+ and food with atomic frame rejection.
439
+ - Require the original v1 evidence source minimum and report replay targets
440
+ left unprocessed after early simulator death.
441
+
442
+ ## 0.1.0 (2026-10-22)
443
+
444
+ - Deterministic headless Rust simulator with configurable worlds, geometric
445
+ observations and mechanics regression fixtures.
446
+ - Core-only library builds, selected-slot observations, supplied-body respawn
447
+ and runtime ambient refill-rate control.
448
+ - Portable bot frames (`slither-client-v2`) reconstructed by a shared
449
+ protocol-19 decoder, with simulator and WebSocket backends, receipt ages,
450
+ measured ping and simulated latency.
451
+ - JSON-lines interfaces for portable bots (`slither-client-json-v1`) and direct
452
+ world control (`slither-sim-json-v1`), with Rust, Python and JavaScript examples.
453
+ - Bounded shared-world WebSocket host with profiles, leaderboards, kill reports,
454
+ record messages and optional connection identifiers.
455
+ - Evidence tools for explicit-endpoint captures, decoding, auditing, recorded-input
456
+ replay and bundles containing frozen plans, attempt records and source snapshots.
457
+ - Offline worked study, documented fidelity limits, extraction provenance and
458
+ manual checks on Windows and Linux.
459
+ - Automatic pull-request checks, shared local/CI check commands, contributor
460
+ task guidance and protocol-labelled regression fixtures.
461
+
462
+ Interfaces remain experimental.
@@ -0,0 +1,152 @@
1
+ # Contributing
2
+
3
+ Keep each PR focused on one problem. Discuss larger features or interface changes
4
+ with the maintainer before starting; small fixes can go straight to a PR.
5
+ Use one task, branch and worktree; preserve others' work.
6
+ Start with the [API guides](README.md#choose-an-interface) and
7
+ [rules and limits](docs/server-rules.md), or use the [documentation map](docs/README.md)
8
+ for research or maintenance tasks.
9
+
10
+ ## Find the change
11
+
12
+ Paths are repository-relative. Read the guide, trace the source owner and callers,
13
+ then run the matching test.
14
+
15
+ | Task | Guide and source / tests | Focused check |
16
+ | --- | --- | --- |
17
+ | Mechanics, motion, food, prey | [Rules, limits and next steps](docs/server-rules.md#packaged-regression-evidence); `src/simulator.rs`, `src/physics.rs`, `src/prey.rs`; matching `tests/*.rs` and `tests/fixtures/` | `cargo test --locked --test movement_turns` (choose the affected test) |
18
+ | Custom Battledome rules and presets | [Battledome](docs/configuration.md#battledome); `src/simulator.rs`, `src/spawn.rs`; `tests/battledome.rs` | `cargo test --locked --test battledome` |
19
+ | Bot JSON API and lifecycle | [Client API](docs/client-api.md), [direct API](docs/bot-api.md); `src/main.rs`, `src/client/mod.rs`, `src/client/received.rs`; `tests/client_contract.rs`, `tests/client_runner.rs`, `tests/transport.rs` | `cargo test --locked --test client_contract` |
20
+ | In-process C ABI | [C ABI](docs/c-api.md); `ffi/src/lib.rs`, `ffi/include/slither_sim.h`, `examples/ffi_bot.py`; `ffi/tests/abi.rs` | `cargo test --locked -p slither-sim-ffi` |
21
+ | Python package | [Python package](docs/python.md); `python/src/lib.rs`, `python/slither_sim.pyi`, `python/examples/`; `python/tests/test_session.py` | `uv run --locked --only-group tools python -m maturin build --locked -m python/Cargo.toml`, then `python python/tests/run.py target/wheels` |
22
+ | Wire decoder and local host | [Server guide](docs/raw-server.md); `src/client/protocol.rs`, `src/client/server.rs`; `tests/client_protocol.rs`, `tests/client_server.rs` | `cargo test --locked --test client_protocol` |
23
+ | Capture, audit and recorded replay | [Research workflow](docs/research-workflow.md); `tools/fidelity/evidence.py`, `tools/fidelity/prepare-fidelity-replay.py` and adjacent tests; `examples/fidelity_replay.rs` | Matching Python/Node test; `cargo test --locked --example fidelity_replay` for Rust replay |
24
+ | Core library / WASM consumer | [Configuration](docs/configuration.md), `examples/basic.rs`; `src/lib.rs`, `src/observation/mod.rs`; `tests/core_consumer.rs` | `cargo test --locked --no-default-features --test core_consumer`; explicit `wasm` group for target changes |
25
+ | Simulator or client performance | [Performance](docs/performance.md), [workloads](docs/performance-workloads.md); `examples/perf_baseline.rs`, `tools/perf_compare.py`, `tools/perf_build.py`; `tools/test_perf_*.py` | `cargo test --locked --example perf_baseline`, or the matching Python test; measure when claiming a speed change |
26
+ | Documentation | Relevant guide and its linked source/examples | `python -B -m unittest discover -s tools -p test_documentation.py`; no research dependencies |
27
+
28
+ ## Setup and checks
29
+
30
+ Run focused tests first, then the relevant group from the repository root.
31
+ Rust needs Rust 1.99+ and platform build tools; the check runner uses
32
+ standard-library Python 3.11+. Documentation-only changes need accuracy/link
33
+ review and the documentation test above, without a Rust build.
34
+
35
+ | Group | Command | Coverage / prerequisites |
36
+ | --- | --- | --- |
37
+ | Rust | `python tools/check.py rust` | `lint` (formatting; Clippy on default, core-only and single-feature builds) then `test` (default/core-only/bot-only/networking/C ABI tests, doctests and a Python crate build); no uv or Node |
38
+ | Tools | `uv run --locked --only-group tools python tools/check.py tools` | Python/Node tests, local bot/C ABI smoke tests and Python package tests; Python 3.12, uv, Node 22+ (24 recommended) and Rust |
39
+ | Package | `python tools/check.py package` | Local crate verification; no registry publication |
40
+ | WASM | `python tools/check.py wasm` | Core-only and bot-only target checks; install `wasm32-unknown-unknown` first |
41
+
42
+ Before the tools group, run `uv sync --locked --only-group tools`. `all` runs
43
+ `rust`, `tools` and `package`; WASM is explicit. Append `--list` to inspect commands
44
+ without running them. Commands report their time; CI adds a job-summary timing
45
+ table. Tests use doubles or loopback, never outside game servers.
46
+ PR CI runs the same runner automatically: `lint`,
47
+ WASM and package once on Linux, `test` on Linux, Windows, macOS and Rust 1.99,
48
+ and `tools` on Linux, Windows and macOS. The weekly
49
+ [extended checks](https://github.com/NikitaMGrimm/slither-sim/blob/main/.github/workflows/extended.yml) add release-profile tests,
50
+ Linux ARM tests and a RustSec audit; they block nothing and can be dispatched.
51
+
52
+ The tools group's fidelity suite includes transport measurement regressions
53
+ (`tools/fidelity/test_transport_parity.py`). Run them alone with
54
+ `uv run --locked --only-group tools python -B -m unittest discover -s tools/fidelity -p test_transport_parity.py`.
55
+ The [local transport check](docs/research-workflow.md#transport-parity) is an
56
+ explicit measurement, separate from unit tests. Record current-host gate failures
57
+ as baselines; do not loosen official tolerances.
58
+
59
+ ## Evidence proportional to the change
60
+
61
+ - Explain the problem and resulting behavior with a reproducer, relevant tests
62
+ and, when useful, a short before/after example such as a request and response.
63
+ - **Documentation:** review accuracy and links against current sources.
64
+ - **Tool/API fixes:** minimal reproducer and regression using existing evidence
65
+ or clearly labelled synthetic inputs; no fresh capture required.
66
+ - **Behavior-preserving optimizations:** demonstrate output parity on relevant
67
+ cases; provide matched before/after measurements only when claiming performance.
68
+ - **Mechanics corrections:** cite supporting evidence and add a focused regression
69
+ fixture. Collect more only if existing evidence cannot support the correction.
70
+ - **New empirical or fitted claims:** follow the [research workflow](docs/research-workflow.md)
71
+ with the complete relevant study, failed/inconclusive attempts, selection rules,
72
+ uncertainty and applicable held-out validation. Explain inapplicable methods;
73
+ do not add ceremonial requirements.
74
+
75
+ The [evidence comparison workflow](docs/research-workflow.md#compare-contributed-evidence)
76
+ screens contributed bundles for declared reporting differences; its statistical
77
+ report supports review without approving physics changes or replacing fixtures.
78
+
79
+ Keep bounded, selected [regression fixtures](https://github.com/NikitaMGrimm/slither-sim/blob/main/tests/fixtures/README.md#selecting-and-updating-cases)
80
+ in Git; keep full recordings, credentials and generated outputs outside it.
81
+ Use Conventional Commits and LF; avoid unrelated formatting. Maintainers review
82
+ ownership, callers, contracts and evidence independently of contributor tests;
83
+ passing tests alone is not acceptance. Collection or publication needs separate
84
+ authorization.
85
+
86
+ Preserve third-party license notices. For external data, note its source and any
87
+ sharing restrictions in `REPORT.md`.
88
+
89
+ A maintainer reviews the current revision and evidence before merging. Required
90
+ CI must pass with latest main; new commits need relevant checks and renewed review.
91
+ Auto-merge can then finish an approved PR. Independent agent review is useful
92
+ evidence, but agents cannot approve their GitHub account's own PR. The owner-authorized
93
+ [release automation](https://github.com/NikitaMGrimm/slither-sim/blob/main/docs/maintainers/releasing.md) can merge compatible patch release PRs
94
+ containing only version/changelog metadata after required CI. Other release
95
+ bumps require maintainer review and merge; publication then runs automatically.
96
+
97
+ For geometry or motion changes, optional before/after plots, diagrams or clips
98
+ can help. Attach or link PR-only media; do not commit it.
99
+
100
+ ## Rationale and evidence
101
+
102
+ Keep non-obvious reasons, units, invariants and model limits beside the owning
103
+ code. Link formulas/defaults to their declarations instead of repeating them in
104
+ an agent guide. Brief comments should label rules as measured,
105
+ protocol/client-derived or provisional and link canonical notes/regressions when useful.
106
+
107
+ Keep current instructions in `docs/`. Put dated studies and selected results in
108
+ `research/<topic>/YYYY-MM-DD-<question>/REPORT.md`; follow the
109
+ [research index](https://github.com/NikitaMGrimm/slither-sim/blob/main/research/README.md) for placement and access labels.
110
+ Evidence notes/fixtures own methods, source/archive hashes, selected windows,
111
+ failed cases and uncertainty. Tests consume selected fixtures; synthetic cases
112
+ establish their stated contract. Change a rule's owner, linked evidence and
113
+ focused regression together. Preserve historical plans and contradictory results;
114
+ do not regenerate expectations to agree with new code.
115
+
116
+ ## AI assistance
117
+
118
+ Review and verify AI work. End the PR description with a short footer naming
119
+ actual models, harnesses and roles, including material delegated work.
120
+ Use display names; report review and checks accurately. Example:
121
+
122
+ > [!NOTE]
123
+ > 🤖 **Agent assistance: GPT 6.1 Sol via Codex** — implementation; supporting work (verification).
124
+
125
+ Group credits by harness in one closing note. Neither transcripts nor a reviewer
126
+ quota are required.
127
+
128
+ ## Interfaces and packaging
129
+
130
+ Interfaces are experimental. Incompatible bot changes require a new
131
+ [contract identifier](docs/client-api.md#interface-versions), updated examples
132
+ and the JSON contract fixture. Keep units, field meanings and lifecycle behavior
133
+ consistent; readers ignore additional response fields. Physics changes can alter
134
+ trajectories without changing the API.
135
+
136
+ The root `Cargo.toml`'s `[workspace.package]` owns the simulator version, inherited
137
+ by the C ABI crate and Python package; `pyproject.toml` owns the tools version.
138
+ Within `0.x`, incompatible Rust API changes bump the minor version. Group
139
+ unreleased changes in the changelog; no permanent freeze or multi-version support
140
+ is promised.
141
+
142
+ Write one user-facing changelog entry per change, with a link to its PR when
143
+ useful. Avoid repeating generated commit titles in a second prose list.
144
+
145
+ The `package` check builds the local crate. It ships the library, binary,
146
+ user guides and examples, the release guide and the fixtures its unit tests read;
147
+ integration tests, other fixtures, evidence tools, schemas and other maintainer
148
+ files require the Git checkout.
149
+ [Historical provenance](docs/provenance.md) records imported components and adaptations;
150
+ routine edits do not refresh import hashes. Per-study source pins remain required.
151
+ Do not publish releases, change repository visibility,
152
+ collaborators or licensing without the owner's authorization.