PathManager.CollectObstaclesInPlace writes obstacles into the caller-provided Span<Obstacle> and updates _obstacleBufferCount, but never copies into _obstacleArrayBuffer.
GetCachedObstacles() returns _obstacleArrayBuffer.AsSpan(0, _obstacleBufferCount), so after a successful CollectObstaclesInPlace the span length is non-zero but every element is default/default(Obstacle) (zeros).
Grep confirms _obstacleArrayBuffer is only declared and read — never written.
Repro
varmanager=newPathManager(...planets:()=>[planet]...);Span<Obstacle>buf=newObstacle[8];varcount=manager.CollectObstaclesInPlace(craft,buf);// buf has planet; manager.GetCachedObstacles() has length==count but Position/Size all zero
Expected
Either CollectObstaclesInPlace should also populate _obstacleArrayBuffer (so GetCachedObstacles matches), or GetCachedObstacles should expose the same data the in-place path just collected.
Found during coverage wave (PathManager residual).
## Summary
`PathManager.CollectObstaclesInPlace` writes obstacles into the caller-provided `Span<Obstacle>` and updates `_obstacleBufferCount`, but never copies into `_obstacleArrayBuffer`.
`GetCachedObstacles()` returns `_obstacleArrayBuffer.AsSpan(0, _obstacleBufferCount)`, so after a successful `CollectObstaclesInPlace` the span length is non-zero but every element is default/`default(Obstacle)` (zeros).
Grep confirms `_obstacleArrayBuffer` is only declared and read — never written.
## Repro
```csharp
var manager = new PathManager(...planets: () => [planet]...);
Span<Obstacle> buf = new Obstacle[8];
var count = manager.CollectObstaclesInPlace(craft, buf);
// buf has planet; manager.GetCachedObstacles() has length==count but Position/Size all zero
```
## Expected
Either `CollectObstaclesInPlace` should also populate `_obstacleArrayBuffer` (so `GetCachedObstacles` matches), or `GetCachedObstacles` should expose the same data the in-place path just collected.
Found during coverage wave (PathManager residual).
Audited against origin/master — STILL BROKEN, but the notes need one correction: this is latent, not live.
The defect is exactly as described.SpikerSoft.GameServer/Controllers/PathManager.cs:53 declares _obstacleArrayBuffer; CollectObstaclesInPlace (:294-341) writes only into the caller's output span and _obstacleBufferCount; GetCachedObstacles (:348-350) returns _obstacleArrayBuffer.AsSpan(0, _obstacleBufferCount). The field is still never written, so the accessor returns a window over a zeroed array sized by an unrelated count.
The correction — GetCachedObstacles has no production caller. It appears only at its own declaration (:348) and in PathManagerLifecycleTests.cs:316 and :330. The ticket reads as though pathfinding is currently consuming empty obstacle data, which would be live gameplay corruption. It isn't — nothing calls it outside tests.
That's worth recording for two reasons. It changes the priority (this is a latent trap waiting for the first caller, not an active bug), and it changes the fix: whoever wires up a caller will get silently-empty obstacles unless the buffer is filled first, so the ticket should be resolved before anything starts using it rather than in response to a symptom.
Prioritisation context from the same sweep. I checked reachability across eleven small-bug tickets. Three are latent with no production caller — this one, #792 and #840. The rest are live: #811 (broken AES on a real network path, security-relevant), #793 and #795 (live gameplay/transport), #794, #797, #799. If these are being worked as a batch, the live set is where the user-visible impact is; this one is best treated as cleanup-before-use.
Notes are otherwise accurate — line numbers still match current code.
Audited against `origin/master` — **STILL BROKEN, but the notes need one correction: this is latent, not live.**
**The defect is exactly as described.** `SpikerSoft.GameServer/Controllers/PathManager.cs:53` declares `_obstacleArrayBuffer`; `CollectObstaclesInPlace` (`:294-341`) writes only into the caller's `output` span and `_obstacleBufferCount`; `GetCachedObstacles` (`:348-350`) returns `_obstacleArrayBuffer.AsSpan(0, _obstacleBufferCount)`. The field is still never written, so the accessor returns a window over a zeroed array sized by an unrelated count.
**The correction — `GetCachedObstacles` has no production caller.** It appears only at its own declaration (`:348`) and in `PathManagerLifecycleTests.cs:316` and `:330`. The ticket reads as though pathfinding is currently consuming empty obstacle data, which would be live gameplay corruption. It isn't — nothing calls it outside tests.
That's worth recording for two reasons. It changes the priority (this is a latent trap waiting for the first caller, not an active bug), and it changes the fix: whoever wires up a caller will get silently-empty obstacles unless the buffer is filled first, so the ticket should be resolved *before* anything starts using it rather than in response to a symptom.
**Prioritisation context from the same sweep.** I checked reachability across eleven small-bug tickets. Three are latent with no production caller — this one, **#792** and **#840**. The rest are live: **#811** (broken AES on a real network path, security-relevant), **#793** and **#795** (live gameplay/transport), **#794**, **#797**, **#799**. If these are being worked as a batch, the live set is where the user-visible impact is; this one is best treated as cleanup-before-use.
Notes are otherwise accurate — line numbers still match current code.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Summary
PathManager.CollectObstaclesInPlacewrites obstacles into the caller-providedSpan<Obstacle>and updates_obstacleBufferCount, but never copies into_obstacleArrayBuffer.GetCachedObstacles()returns_obstacleArrayBuffer.AsSpan(0, _obstacleBufferCount), so after a successfulCollectObstaclesInPlacethe span length is non-zero but every element is default/default(Obstacle)(zeros).Grep confirms
_obstacleArrayBufferis only declared and read — never written.Repro
Expected
Either
CollectObstaclesInPlaceshould also populate_obstacleArrayBuffer(soGetCachedObstaclesmatches), orGetCachedObstaclesshould expose the same data the in-place path just collected.Found during coverage wave (PathManager residual).
Audited against
origin/master— STILL BROKEN, but the notes need one correction: this is latent, not live.The defect is exactly as described.
SpikerSoft.GameServer/Controllers/PathManager.cs:53declares_obstacleArrayBuffer;CollectObstaclesInPlace(:294-341) writes only into the caller'soutputspan and_obstacleBufferCount;GetCachedObstacles(:348-350) returns_obstacleArrayBuffer.AsSpan(0, _obstacleBufferCount). The field is still never written, so the accessor returns a window over a zeroed array sized by an unrelated count.The correction —
GetCachedObstacleshas no production caller. It appears only at its own declaration (:348) and inPathManagerLifecycleTests.cs:316and:330. The ticket reads as though pathfinding is currently consuming empty obstacle data, which would be live gameplay corruption. It isn't — nothing calls it outside tests.That's worth recording for two reasons. It changes the priority (this is a latent trap waiting for the first caller, not an active bug), and it changes the fix: whoever wires up a caller will get silently-empty obstacles unless the buffer is filled first, so the ticket should be resolved before anything starts using it rather than in response to a symptom.
Prioritisation context from the same sweep. I checked reachability across eleven small-bug tickets. Three are latent with no production caller — this one, #792 and #840. The rest are live: #811 (broken AES on a real network path, security-relevant), #793 and #795 (live gameplay/transport), #794, #797, #799. If these are being worked as a batch, the live set is where the user-visible impact is; this one is best treated as cleanup-before-use.
Notes are otherwise accurate — line numbers still match current code.