Implements #163 (RoleSchema), #164 (SpaceSpec), #165 (TemplateOwnership
+ TemplateReferenceMap), #106 (TriangleDef), #107 (intra-template
triangle generation), and #250 (triangle escalation system) as the
foundational Tier 2 template system per D-025.
New content/template module with YAML-deserializable schema types,
ECS components for ownership/triangle state, escalation system
running on game-minute boundaries, and TriangleCrisisEvent emission.
Sample YAML templates at server/data/templates/.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Adds TemplateOwnership to NpcSaveState and TemplateReferenceMap to
SaveStateV1 so cross-template links survive save/load and tier
eviction (D-025, D-026). Both fields use serde(default) for backward
compatibility with existing saves.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Registers tick_triangle_escalation and apply_resolve_triangle systems
in SimulationPlugin. Adds TriangleCrisisEventWire to ObserverSnapshot
(protocol v16) for future client rendering of triangle crises (#250,
D-087). Observer emits empty vec by default; escalation system will
populate when triangles reach Active phase.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Updates all client-side msgpack test fixtures and server test
harnesses to include the new triangle_crisis_events field added
in protocol v16.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Solid code quality throughout — determinism respected, good test coverage, clean Rust. Two issues block merge.
#
File
Severity
Issue
1
server/src/perception/observer/mod.rs:417
warning
triangle_crisis_events hardcoded to vec\![] — queue is populated by tick_triangle_escalation but never drained into snapshot. Protocol bumped to v16 advertising this field, but clients will always see empty.
2
server/src/simulation/save_state.rs + save_io.rs
warning
TriangleState components not persisted in SaveStateV1 — triangle phase/tension silently lost on save/load. No deferral comment like NpcMemory has.
3
server/src/content/template.rs:546-557
suggestion
Fallback NPC assignment uses O(n²) Vec scan; no test covers fallback assigning same NPC to two roles
4
server/src/bridge/types.rs:692-700
suggestion
TriangleCrisisEventWire.triangle_id is raw u64 with no From impl for the newtype — fragile inline .0 access when wiring is completed
5
server/tests/
suggestion
Near-identical tests in module #[cfg(test)] and integration tests — consider consolidating
Tyre (Architecture): REQUEST_CHANGES
Well-structured architectural layering, clean content/ECS boundary, D-010 determinism respected throughout. Same two blocking issues.
#
File
Severity
Issue
1
server/src/perception/observer/mod.rs:417
warning
Same as Hoshe #1 — triangle_crisis_events never populated despite v16 protocol bump. Either wire the queue drain or defer the bump to v15.
2
server/src/simulation/save_state.rs + save_io.rs
warning
Same as Hoshe #2 — TriangleState not persisted, no documented deferral. Needs either serialization or an explicit comment + ticket.
3
server/src/content/template.rs:747-762
suggestion
apply_resolve_triangle does O(N*M) scan — consider BTreeMap<TriangleId, Entity> index if triangle count grows
4
server/src/content/template.rs:506
suggestion
generate_intra_template_triangles takes &mut World — inherently single-threaded, consistent with spawn.rs but worth noting
5
server/src/content/template.rs:413-443
suggestion
TriangleDef.validate() doesn't catch dangling with_role references in relationship_constraints
Verdict: CHANGES REQUESTED
Both reviewers flag the same two warnings:
Crisis event queue not wired — triangle_crisis_events is always empty despite protocol v16 advertising it
TriangleState not persisted — triangle escalation state silently lost on save/load with no documented deferral
Options: wire the queue drain + add save state serialization, or document both as intentional deferrals with TODO comments and tickets (matching the NpcMemory pattern in save_state.rs).
## Review: server -> main (type: code)
### Hoshe (Code Quality): REQUEST_CHANGES
Solid code quality throughout — determinism respected, good test coverage, clean Rust. Two issues block merge.
| # | File | Severity | Issue |
|---|------|----------|-------|
| 1 | `server/src/perception/observer/mod.rs:417` | warning | `triangle_crisis_events` hardcoded to `vec\![]` — queue is populated by `tick_triangle_escalation` but never drained into snapshot. Protocol bumped to v16 advertising this field, but clients will always see empty. |
| 2 | `server/src/simulation/save_state.rs` + `save_io.rs` | warning | `TriangleState` components not persisted in `SaveStateV1` — triangle phase/tension silently lost on save/load. No deferral comment like `NpcMemory` has. |
| 3 | `server/src/content/template.rs:546-557` | suggestion | Fallback NPC assignment uses O(n²) Vec scan; no test covers fallback assigning same NPC to two roles |
| 4 | `server/src/bridge/types.rs:692-700` | suggestion | `TriangleCrisisEventWire.triangle_id` is raw `u64` with no `From` impl for the newtype — fragile inline `.0` access when wiring is completed |
| 5 | `server/tests/` | suggestion | Near-identical tests in module `#[cfg(test)]` and integration tests — consider consolidating |
### Tyre (Architecture): REQUEST_CHANGES
Well-structured architectural layering, clean content/ECS boundary, D-010 determinism respected throughout. Same two blocking issues.
| # | File | Severity | Issue |
|---|------|----------|-------|
| 1 | `server/src/perception/observer/mod.rs:417` | warning | Same as Hoshe #1 — `triangle_crisis_events` never populated despite v16 protocol bump. Either wire the queue drain or defer the bump to v15. |
| 2 | `server/src/simulation/save_state.rs` + `save_io.rs` | warning | Same as Hoshe #2 — `TriangleState` not persisted, no documented deferral. Needs either serialization or an explicit comment + ticket. |
| 3 | `server/src/content/template.rs:747-762` | suggestion | `apply_resolve_triangle` does O(N*M) scan — consider `BTreeMap<TriangleId, Entity>` index if triangle count grows |
| 4 | `server/src/content/template.rs:506` | suggestion | `generate_intra_template_triangles` takes `&mut World` — inherently single-threaded, consistent with spawn.rs but worth noting |
| 5 | `server/src/content/template.rs:413-443` | suggestion | `TriangleDef.validate()` doesn't catch dangling `with_role` references in `relationship_constraints` |
### Verdict: CHANGES REQUESTED
Both reviewers flag the same two warnings:
1. **Crisis event queue not wired** — `triangle_crisis_events` is always empty despite protocol v16 advertising it
2. **`TriangleState` not persisted** — triangle escalation state silently lost on save/load with no documented deferral
Options: wire the queue drain + add save state serialization, or document both as intentional deferrals with TODO comments and tickets (matching the `NpcMemory` pattern in `save_state.rs`).
The TriangleCrisisEventQueue was populated by tick_triangle_escalation
but never drained into ObserverSnapshot — clients always saw an empty
vec despite protocol v16 advertising the field. Now drains the queue
each tick and converts to TriangleCrisisEventWire.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Triangle phase and tension were silently lost on save/load. Now
serialized as triangle_states vec in SaveStateV1, sorted by
triangle_id for determinism (D-010). Dedicated triangle entities
are respawned on load.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
TriangleDef.validate() now rejects relationship constraints where
with_role references a role not in the triangle's three roles.
Catches authoring errors at load time instead of silently producing
broken constraint data.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Hoshe #3: replace O(n²) Vec scan in fallback NPC assignment with
BTreeSet; prevent same NPC assigned to two roles in one triangle
- Hoshe #4: add From impls for RoleId, TriangleId, StableId, and
TriangleCrisisEventWire — eliminate fragile .0 access on newtypes
- Hoshe #5: consolidate near-identical unit tests with integration
counterparts — keep only unique tests in #[cfg(test)] module
- Tyre #3: replace O(N*M) scan in apply_resolve_triangle with
BTreeMap<TriangleId, Entity> index for O(1) per-command lookup
- Tyre #4: document &mut World on generate_intra_template_triangles
- Observer snapshot uses TriangleCrisisEventWire::from instead of
manual field mapping
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
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 20 server deliverables — the Tier 2 social site template system and triangle escalation engine.
RoleSchemawith trait/skill/relationship constraints, YAML deserializationSpaceSpecwith sightline zones, privacy levels, traffic patternsTemplateOwnershipcomponent,TemplateReferenceMapresource, save/load integrationTriangleDefwith conflict types (D-087 T1-T5), FNV-1a deterministic IDsTriangleCrisisEventemission,ResolveTrianglestub (D-089 no cascade)Protocol bumped v15 → v16 (TriangleCrisisEventWire on ObserverSnapshot). All msgpack fixtures regenerated.
3,214 lines added across 33 files. 1,195 tests passing, 0 failures.
Key decisions implemented
Test plan
cargo test— 1,195 tests pass (1,061 lib + 25 template schema + 16 escalation + 51 serialization + 42 integration)Review: server -> main (type: code)
Hoshe (Code Quality): REQUEST_CHANGES
Solid code quality throughout — determinism respected, good test coverage, clean Rust. Two issues block merge.
server/src/perception/observer/mod.rs:417triangle_crisis_eventshardcoded tovec\![]— queue is populated bytick_triangle_escalationbut never drained into snapshot. Protocol bumped to v16 advertising this field, but clients will always see empty.server/src/simulation/save_state.rs+save_io.rsTriangleStatecomponents not persisted inSaveStateV1— triangle phase/tension silently lost on save/load. No deferral comment likeNpcMemoryhas.server/src/content/template.rs:546-557server/src/bridge/types.rs:692-700TriangleCrisisEventWire.triangle_idis rawu64with noFromimpl for the newtype — fragile inline.0access when wiring is completedserver/tests/#[cfg(test)]and integration tests — consider consolidatingTyre (Architecture): REQUEST_CHANGES
Well-structured architectural layering, clean content/ECS boundary, D-010 determinism respected throughout. Same two blocking issues.
server/src/perception/observer/mod.rs:417triangle_crisis_eventsnever populated despite v16 protocol bump. Either wire the queue drain or defer the bump to v15.server/src/simulation/save_state.rs+save_io.rsTriangleStatenot persisted, no documented deferral. Needs either serialization or an explicit comment + ticket.server/src/content/template.rs:747-762apply_resolve_triangledoes O(N*M) scan — considerBTreeMap<TriangleId, Entity>index if triangle count growsserver/src/content/template.rs:506generate_intra_template_trianglestakes&mut World— inherently single-threaded, consistent with spawn.rs but worth notingserver/src/content/template.rs:413-443TriangleDef.validate()doesn't catch danglingwith_rolereferences inrelationship_constraintsVerdict: CHANGES REQUESTED
Both reviewers flag the same two warnings:
triangle_crisis_eventsis always empty despite protocol v16 advertising itTriangleStatenot persisted — triangle escalation state silently lost on save/load with no documented deferralOptions: wire the queue drain + add save state serialization, or document both as intentional deferrals with TODO comments and tickets (matching the
NpcMemorypattern insave_state.rs).Review fixes applied
All 5 warnings and 5 suggestions from both reviewers addressed:
Warnings (blocking)
TriangleCrisisEventWirenow populated from queue drain (commit9bc5925)TriangleStatepersisted inSaveStateV1— phase + tension survive save/load (commit1ea3d9c)Suggestions
BTreeSet; prevents same NPC assigned to two roles in one triangleFromimpls added forRoleId,TriangleId,StableId,TriangleCrisisEventWire— no more raw.0accessapply_resolve_triangleO(N*M) scan replaced withBTreeMap<TriangleId, Entity>index — O(1) per command&mut Worlddocumented ongenerate_intra_template_triangles(consistent with spawn.rs)with_rolevalidation added toTriangleDef.validate()(commit3a555a2)All 1,195 tests passing. Ready for re-review.
Re-Review: server -> main (PR #72)
Round 2 — Fix verification: APPROVED
All three blocking warnings resolved:
triangle_crisis_eventsnow drained from queue into snapshotTriangleStatepersisted inSaveStateV1with#[serde(default)]TriangleDef.validate()catches danglingwith_rolereferencesRound 3 — Suggestion pass: APPROVED
3 of 6 suggestions addressed:
From<TriangleCrisisEvent>forTriangleCrisisEventWire— implementedRemaining suggestions (non-blocking):
server/src/content/template.rswith_rolevalidationserver/src/simulation/save_io.rstriangle_statesserver/src/perception/observer/mod.rs:394.drain().into_iter()redundant on VecVerdict: APPROVED
Pull request closed