cargo test — all passing (1148 unit + 3 integration)
Archetype round-trip: smuggler serializes/deserializes, missing field defaults to detective
Tell escalation: TriangleActivated → RoutineDeviation inserted, expires after 300 ticks
Ticker: deterministic rotation via SimRng, zone-gated to bar
Opening monologue: smuggler and detective get different pools
Msgpack fixtures regenerated for protocol v19
Full E2E playthrough (TeleportToLocation debug command — deferred, not v0.1 blocker)
## Summary
Sprint 24 server delivery — capstone sprint for v0.1 vertical slice.
- **#587** Character archetype in `StartupMessage` with `#[serde(default)]` backward compat. `PROTOCOL_VERSION` bumped to 19.
- **#589** `escalate_tells_on_activation()` system: `TriangleActivated` → `RoutineDeviation` on triangle NPCs with 300-tick TTL.
- **#591** `TickerPool` resource with deterministic `SimRng` rotation. `current_ticker` emitted in snapshot when player is in bar zone.
- **#594** D-113: Tile data model design (tile palette + sparse override, zero-migration path).
- **#595** Archetype→monologue gating verified end-to-end (7 regression tests).
- **#593** v0.1 playthrough proof: 3 live integration tests against real server binary (Layer 3).
## Breaking Changes
- `PROTOCOL_VERSION` 18 → 19 — client #588 must match on merge.
- `ObserverSnapshotWire` gains `current_ticker: Option<TickerLine>` field.
## Test Plan
- [x] `cargo test` — all passing (1148 unit + 3 integration)
- [x] Archetype round-trip: smuggler serializes/deserializes, missing field defaults to detective
- [x] Tell escalation: TriangleActivated → RoutineDeviation inserted, expires after 300 ticks
- [x] Ticker: deterministic rotation via SimRng, zone-gated to bar
- [x] Opening monologue: smuggler and detective get different pools
- [x] Msgpack fixtures regenerated for protocol v19
- [ ] Full E2E playthrough (TeleportToLocation debug command — deferred, not v0.1 blocker)
- Add character_archetype to StartupMessage with serde default (Detective)
- Bump PROTOCOL_VERSION to 19
- Add escalate_tells_on_activation() and expire_routine_deviations() systems
- RoutineDeviation inserted on triangle NPCs with 300-tick TTL
- Add TickerPool resource with deterministic SimRng rotation (200 ticks)
- Emit current_ticker in ObserverSnapshot when player is in bar zone
- Load ticker YAML from district content directories
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- 7 archetype→monologue regression tests (smuggler/detective pool partitioning)
- 3 tell escalation unit tests (RoutineDeviation insertion + expiry)
- 6 news ticker tests (pool loading, SimRng rotation, zone gating)
- 3 live integration tests against real server binary (Layer 3)
- Update existing tests for current_ticker field and protocol v19
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Clean separation of concerns, solid test coverage. Three features well-scoped with proper #[ignore] flagging for pending work.
#
File:line
Severity
Issue
1
simulation/ticker.rs:64–69
warning
Rotation uses sliding-window (last_rotated_tick = current_tick) but comment says "every 200 ticks". If modulus-aligned was intended, use current_tick % TICKER_ROTATION_TICKS == 0. Document which behavior is desired.
2
main.rs:402
warning
setup_proof_room hardcodes MovementProfile::smuggler() regardless of archetype. Detective sessions in proof-room mode get smuggler movement. Latent bug on --test-mode without gauntlet.
3
storyteller/mod.rs:565–570
suggestion
f32 epsilon tie-break may produce non-deterministic-feeling results at large observation tick counts. Consider exact equality since all inputs are discrete integers.
4
perception/observer/mod.rs:443–453
suggestion
.map(...).flatten() → .and_then(...) for clippy strictness.
Dangling editorial comment before is_comment_only_file looks misplaced.
7
tests/tell_escalation.rs:195–230
suggestion
Ignored test spins 302 updates — add note about frame limiter when un-ignored.
Tyre (Architecture): APPROVE (with polish items)
All new code respects D-010 (determinism via SimRng), D-012 (information boundaries — no triangle identity leaks), and established ECS patterns. Protocol v19 backward-compatible via #[serde(default)]. D-113 tile palette design is sound.
#
File:line
Severity
Issue
1
simulation/ticker.rs:19
warning
LAST_SHIFT_ZONE_ID = 1 is hardcoded, but Gauntlet assigns zone IDs sequentially (zone 1 = Fog Theater, not The Last Shift). No compile-time or load-time validation against content YAML. Suggest loading zone ID from ticker YAML or adding a Gauntlet assertion.
- Ticker rotation: document sliding-window semantics (vs modulus-aligned)
- Ticker zone ID: add warning about Gauntlet vs production zone ID mismatch
- Proof-room movement profile: respect archetype instead of hardcoding smuggler
- Storyteller tie-break: use exact f32 equality (inputs are discrete integers)
- Observer: .map().flatten() → .and_then() (clippy strict)
- Content loader: remove dangling doc comment before section header
- Tests: replace assert!(false, ...) with TODO comments in ignored tests
- Tests: add frame limiter note on 302-update loop in tell expiry test
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
✅ Ticker tests: assert!(false) replaced with TODO comments
✅ Loader: dangling doc comment removed
✅ Tell escalation: perf note added for 302-update loop
Tyre (Architecture): APPROVE
f32 exact-equality tie-break is sound — IEEE 754 guarantees identical operation sequences on same integer inputs produce identical results. CharacterArchetype match is exhaustive (compiler enforces future variant handling). Zone ID warning doc is appropriate; loader validation deferred to production YAML.
Verdict: APPROVED ✅
Both reviewers approve. Ready for merge.
## Re-review: `server` → `main` (PR #85, round 2)
### Hoshe (Code Quality): APPROVE
All 8 round-1 items verified resolved:
1. ✅ Ticker rotation: sliding-window semantics documented
2. ✅ Proof-room: movement profile matches archetype (exhaustive match, no wildcard)
3. ✅ Zone ID: WARNING doc added for Gauntlet mismatch
4. ✅ Storyteller: exact f32 equality replaces epsilon comparison
5. ✅ Observer: `.and_then()` replaces `.map().flatten()`
6. ✅ Ticker tests: `assert!(false)` replaced with TODO comments
7. ✅ Loader: dangling doc comment removed
8. ✅ Tell escalation: perf note added for 302-update loop
### Tyre (Architecture): APPROVE
f32 exact-equality tie-break is sound — IEEE 754 guarantees identical operation sequences on same integer inputs produce identical results. `CharacterArchetype` match is exhaustive (compiler enforces future variant handling). Zone ID warning doc is appropriate; loader validation deferred to production YAML.
### Verdict: APPROVED ✅
Both reviewers approve. Ready for merge.
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
Sprint 24 server delivery — capstone sprint for v0.1 vertical slice.
StartupMessagewith#[serde(default)]backward compat.PROTOCOL_VERSIONbumped to 19.escalate_tells_on_activation()system:TriangleActivated→RoutineDeviationon triangle NPCs with 300-tick TTL.TickerPoolresource with deterministicSimRngrotation.current_tickeremitted in snapshot when player is in bar zone.Breaking Changes
PROTOCOL_VERSION18 → 19 — client #588 must match on merge.ObserverSnapshotWiregainscurrent_ticker: Option<TickerLine>field.Test Plan
cargo test— all passing (1148 unit + 3 integration)Review:
server→main(PR #85, type: code)Hoshe (Code Quality): APPROVE (with polish items)
Clean separation of concerns, solid test coverage. Three features well-scoped with proper
#[ignore]flagging for pending work.simulation/ticker.rs:64–69last_rotated_tick = current_tick) but comment says "every 200 ticks". If modulus-aligned was intended, usecurrent_tick % TICKER_ROTATION_TICKS == 0. Document which behavior is desired.main.rs:402setup_proof_roomhardcodesMovementProfile::smuggler()regardless of archetype. Detective sessions in proof-room mode get smuggler movement. Latent bug on--test-modewithout gauntlet.storyteller/mod.rs:565–570perception/observer/mod.rs:443–453.map(...).flatten()→.and_then(...)for clippy strictness.tests/news_ticker.rs:202–250assert!(false, "Implement: ...")— consider comment blocks instead.content/loader.rs:347–350is_comment_only_filelooks misplaced.tests/tell_escalation.rs:195–230Tyre (Architecture): APPROVE (with polish items)
All new code respects D-010 (determinism via SimRng), D-012 (information boundaries — no triangle identity leaks), and established ECS patterns. Protocol v19 backward-compatible via
#[serde(default)]. D-113 tile palette design is sound.simulation/ticker.rs:19LAST_SHIFT_ZONE_ID = 1is hardcoded, but Gauntlet assigns zone IDs sequentially (zone 1 = Fog Theater, not The Last Shift). No compile-time or load-time validation against content YAML. Suggest loading zone ID from ticker YAML or adding a Gauntlet assertion.perception/observer/mod.rs:445–446.map(...).flatten()→.and_then(...)(clippy strict).content/mod.rs:99–118TickerPool::from_content_store()if ticker gains location filtering later.Verdict: APPROVED — with 3 warnings for polish
Warnings to address before merge:
ticker.rs:64)main.rs:402)LAST_SHIFT_ZONE_IDhas no content-side validation (ticker.rs:19)Suggestions are optional but recommended for code hygiene.
Re-review:
server→main(PR #85, round 2)Hoshe (Code Quality): APPROVE
All 8 round-1 items verified resolved:
.and_then()replaces.map().flatten()assert!(false)replaced with TODO commentsTyre (Architecture): APPROVE
f32 exact-equality tie-break is sound — IEEE 754 guarantees identical operation sequences on same integer inputs produce identical results.
CharacterArchetypematch is exhaustive (compiler enforces future variant handling). Zone ID warning doc is appropriate; loader validation deferred to production YAML.Verdict: APPROVED ✅
Both reviewers approve. Ready for merge.
Pull request closed