Document 12 · Legend Studios Platform

Legend World Slice 002 — Facility Commitment

v1.1Active

Legend World Vertical Slice 002 — Facility Commitment

Status: v1.1 — reconciled and finalized. All items are confirmed architecture decisions: the §8.2 cache-key correction, deterministic duplicate-field_path rejection, Step 11 all-or-nothing atomic commitment, failed_commitment visibility until explicit owner action, Step 11 re-validation, and the renovation/expansion/facility-upgrade/replacement category distinctions. One internal contradiction (the proposed_at field comment vs. §3.3) was found and fixed during finalization — see §3.2. No unresolved ownership conflicts remain. Role boundary: Logic, contracts, invariants, and tests only — no code, no database migrations, no UI, no Supabase/Lovable access attempted, per explicit instruction. Authoritative inputs: DL-MASTER (v1.1, Step 11), DL-SPEC-014 (v1.4, §8–§10), LS-SPEC-005 (v1.2), LS-SPEC-006 (v1.4, including the confirmed §8.2 cache-key correction), LEGEND-WORLD-VERTICAL-SLICE-001 (v2.0). Scope discipline: Strictly stadium/world architecture — existing stadium → proposed change → pending queue → validation → Step 11 atomic commitment → World Manifest update if required → persistent history. No league, franchise-marketplace, valuation, advertising, or unrelated system content appears anywhere below.


0. Cache-Key Correction — Confirmed

LS-SPEC-006 §8.2's World Manifest cache key is (settlement_id, visual_year, site_id, orientation_bucket), confirmed as an authoritative architecture decision. orientation_bucket is derived only from venue orientation — no other component of venue_footprint is included in the cache key unless a future engineering pass demonstrates it actually affects generation. §6 and §7 below rely on this directly.


1. Facility Commitment Flow, Mapped to Owning Spec

1. Existing Stadium (live state)              → DL-SPEC-014 §11 (existing)
2. Proposed Change (renovation/expansion/
   replacement/upgrade)                       → DL-SPEC-014 §7–§9 (existing, made concrete here)
3. Pending Facility Queue                     → DL-SPEC-014 §8.2 (existing, made concrete here)
4. Validation (structural + cost)             → DL-SPEC-014 §8.3 + DL-SPEC-008 (existing boundary)
5. Step 11 Atomic Commitment                  → DL-MASTER Step 11 + DL-SPEC-014 §8.5/INV-7/INV-8 (existing)
6. Changed venue_footprint → new VenueWorldRequest → LS-SPEC-006 §1.1 (existing)
7. Updated World Manifest                     → LS-SPEC-006 §6 (existing, §8.2 fix per §0)
8. Persistent Stadium/World History            → DL-SPEC-014 §9.3/§10 + LS-SPEC-005 §10 (existing, composed)

2. Stage 1 — Existing Stadium (Baseline State)

2.1 Contract

StadiumState                          -- the live record, DL-SPEC-014 §11, read at proposal time
  stadium_id: string
  franchise_id: string
  status: 'active' | 'renovated' | 'replaced' | 'demolished' | 'preserved'
  lifecycle_stage: 'original' | 'renovated'
  field_geometry: FieldGeometryInput & FieldGeometryFixed    -- per VERTICAL-SLICE-001 §5.1
  venue_footprint: VenueFootprint                            -- per VERTICAL-SLICE-001 §5.3
  seating_configuration: SeatingDeck[]
  suite_configuration: SuiteConfig[]
  facility_levels: { bullpen_quality, clubhouse_quality, training_complex_level: 0-4 }
  commercial_facilities: { concessions_quality, team_store_quality, parking_capacity }
  scoreboard_type: string                -- era-gated, this stadium's own technology (distinct from
                                          -- LS-SPEC-006's manifest "lighting" field — see §2.2 note)
  lighting_type: string
  capacity: number                       -- confirmed DL-SPEC-008 contract
  revenue_modifier: number               -- confirmed DL-SPEC-008 contract
  assessed_value: number                 -- confirmed DL-SPEC-008 contract
  annual_debt_service: number | null     -- confirmed DL-SPEC-008 contract
  suite_revenue: number                  -- confirmed DL-SPEC-008 contract
  training_complex_level: 0-4            -- confirmed DL-SPEC-010 contract

2.2 Naming Note, Not a Conflict — Flagged for Clarity Only

lighting_type here (a stadium's own lighting infrastructure) and the World Manifest's lighting field (LS-SPEC-006 §6.1, ambient/environmental lighting for the surrounding world) are different concepts sharing a similar name. This is not a functional conflict — nothing in either spec confuses the two operationally — but it's a documentation clarity risk worth naming so implementers don't conflate them. No action taken here beyond this note; a future documentation pass could rename one of the two fields for precision.


3. Stage 2 — Proposed Facility Change

3.1 Change Categories, Defined Precisely (Resolving an Imprecise Boundary in DL-SPEC-014)

DL-MASTER Step 11 names four categories — renovations, expansions, replacement-stadium openings, facility upgrades — but DL-SPEC-014 never precisely distinguished "renovation" from "expansion." Resolved here, within DL-SPEC-014's own existing ownership (not a new-owner situation, just a needed precision):

Category Definition
Renovation A change to existing infrastructure that does not increase capacity or alter the field footprint's overall scale — e.g., new scoreboard, new lighting technology, wall material change, facility-level upgrade
Expansion A change that increases capacity and/or physically alters the footprint's scale — e.g., an added deck, an enlarged seating bowl, changed fence distances
Replacement An entirely new stadium record, per DL-SPEC-014 §9 — never a modification of the existing one
Facility Upgrade A change to facility_levels/commercial_facilities only — no geometry or capacity impact (training complex, concessions, team store, parking)

A single proposal may combine changes from multiple categories (e.g., a renovation-and-facility-upgrade bundle) — categorization is per-field-change, not per-proposal, per §4's structure.

3.2 Contract

FacilityChangeProposal
  stadium_id: string
  franchise_id: string
  proposed_at: timestamp                 -- any point within the offseason sequence (DL-MASTER Steps 1-10);
                                          -- NOT gated by DL-SPEC-004's season_start_day/season_end_day —
                                          -- that boundary applies only to Step 11 commitment, see §3.3
  field_changes: FieldChange[]

FieldChange
  field_path: string                     -- e.g. "seating_configuration[deck_id=3].section_count"
  category: 'renovation' | 'expansion' | 'facility_upgrade'    -- 'replacement' is never a FieldChange, see §9
  new_value: any
  affects_venue_footprint: boolean        -- see §6.1 for the definitive field-to-footprint mapping

3.3 When Proposals Are Accepted

Corrected timing, more precise than DL-SPEC-014 v1.4's original statement. DL-SPEC-014 §8.2/§8.5 previously implied proposals are accepted only outside the season window. That's imprecise: proposals may be submitted at any point in the offseason sequence (DL-MASTER Steps 1–10), not merely "outside the season." What's actually gated by season_start_day/season_end_day is commitment (Step 11), not proposal. This document corrects that imprecision — a proposal submitted, say, during Coaching Evaluation (Step 6) is valid; it simply queues (§4) until Step 11.


4. Stage 3 — Pending Facility Queue

4.1 Contract

PendingFacilityChange
  id: string
  stadium_id: string
  proposal: FacilityChangeProposal
  status: 'pending_validation' | 'pending_commitment' | 'committed' | 'failed_commitment' | 'withdrawn'
  cost_estimate: { amount: number, financing_impact: object }   -- from DL-SPEC-008, read-only here
  queued_at: timestamp

4.2 What Remains Unchanged Until Step 11

Nothing in StadiumState (§2.1) is modified by a proposal entering the queue. Every confirmed field-level contract — capacity, revenue_modifier, assessed_value, annual_debt_service, suite_revenue, training_complex_level, venue_footprint, status, lifecycle_stage — remains at its pre-proposal value for the entire offseason, until Step 11 either commits or fails the batch. A PendingFacilityChange is purely additive queue state; it never shadows or partially overrides the live StadiumState.

4.3 Multiple Pending Changes to One Stadium — Combination Rule

Deterministic merge order, conflict rejection at proposal time. Per LS-SPEC-005/006's project-wide determinism requirement, extended here: pending changes for a stadium combine in queued_at order (earliest first). Two simultaneously-pending changes may never target the same field_path — a second proposal referencing a field already claimed by an existing pending_validation/pending_commitment change is rejected at proposal time (§8, failure state), not silently merged or allowed to create ambiguity about which value wins. This is a hard rule, not a preference: it eliminates any need for a "last write wins" tiebreak that could otherwise vary by implementation detail.


5. Stage 4 — Validation

5.1 Structural Validation (At Proposal Time)

  • field_path must reference a real, existing field on StadiumState.
  • If category = 'expansion' and the resulting geometry would exceed the stadium's site's available area (per LS-SPEC-005 site data, read only through LS-SPEC-006 — this document does not query LS-SPEC-005 directly, preserving the one-door rule end to end), the proposal is rejected — an expansion that needs more land than the site has must go through the Replacement path (§9), never a silent relocation.
  • No field_path may target a fixed-by-rule geometry field (base_path_distance, pitching_distance) — inherited unchanged from VERTICAL-SLICE-001 §5.2/INV-3.

5.2 Cost Validation (DL-SPEC-008's Boundary, Unchanged)

Every PendingFacilityChange requires a cost_estimate from DL-SPEC-008 before advancing to pending_commitment — this document never computes a dollar figure, per DL-SPEC-014 §8.3.

5.3 Re-Validation at Step 11 (New Precision, Within DL-SPEC-014's Existing Scope)

Cost and structural validity are re-checked at Step 11 commit time, not assumed valid from proposal time. An offseason spans multiple steps (draft, free agency, coaching decisions) during which a franchise's financial position can change — a change affordable when proposed in Step 3 is not guaranteed affordable by Step 11. This is a workflow-timing decision within DL-SPEC-014's own ownership (it already owns when validation happens; DL-SPEC-008 still owns how cost is computed), not a new responsibility for either spec.


6. Stage 5 — Step 11 Atomic Commitment

6.1 Field-to-Footprint Mapping (Definitive)

Field Affects venue_footprint?
foul_line_distance_left/right, center_field_distance, wall_height_by_section Yes — geometry
home_plate_orientation Yes — orientation, and therefore the manifest cache's orientation_bucket (§0)
seating_configuration, suite_configuration No
scoreboard_type, lighting_type No
facility_levels, commercial_facilities No
Replacement (§9) Always — a full new footprint, potentially a full new site/settlement

6.2 Commitment Contract

FacilityCommitmentBatch                -- one per stadium, assembled at Step 11
  stadium_id: string
  changes: PendingFacilityChange[]      -- all with status = 'pending_commitment', in queued_at order

FacilityCommitmentResult
  stadium_id: string
  outcome: 'committed' | 'failed'
  updated_stadium_state: StadiumState | null    -- null if failed
  world_manifest_regenerated: boolean
  failure_reason: string | null

6.3 Atomicity — All-or-Nothing, Precisely Defined

If re-validation (§5.3) fails for any single change in the batch, the entire batch fails — no partial commitment. This is the literal meaning of DL-SPEC-014 INV-8 ("no partial commitment, no per-change independent application"), made operational: StadiumState is not modified at all unless every change in the batch passes re-validation. Individual changes are not silently dropped to let the rest through.

6.4 Determinism

Given an identical FacilityCommitmentBatch (same changes, same queued_at order) applied to an identical pre-commit StadiumState, the resulting updated_stadium_state must be identical every time — extending LS-SPEC-005 §14 INV-6 and LS-SPEC-006 §2.2a/§6.4 through this commitment process. No randomness in merge order (already excluded by §4.3's conflict-rejection rule), no randomness in re-validation outcome.


7. Stage 6 — World Manifest Update If Required

7.1 Regeneration vs. Reuse

Per §6.1's mapping: if any committed change has affects_venue_footprint = true, a new VenueWorldRequest is sent to LS-SPEC-006 §1.1 as part of the same Step 11 pass, and the response's manifest is cached under the corrected key from LS-SPEC-006 §8.2 (settlement_id, visual_year, site_id, orientation_bucket). If no committed change affects venue_footprint, the existing cached manifest is reused unmodified — no regeneration, no new request.

7.2 Replacement Always Regenerates

A committed Replacement (§9) always sends a full new VenueWorldRequest, including — where relocation is involved — a full Stage 2/3 candidate-site-generation and selection pass (§9.2) rather than any shortcut.


8. Failure States

Failure Trigger Behavior
Duplicate field-path proposal §4.3, two pending changes target the same field Second proposal rejected at proposal time with a specific error naming the conflicting change
Structural validation failure §5.1 Proposal rejected before it ever reaches pending_validation
Expansion exceeds site area §5.1 Rejected — a recommendation to use the Replacement path is returned, not a silent workaround
Cost re-validation failure at Step 11 §5.3 Entire batch fails per §6.3 — FacilityCommitmentResult.outcome = 'failed', all changes in the batch revert to status = 'failed_commitment', none silently discarded — the owner must revise or withdraw before the next offseason's Step 11
World Manifest regeneration failure §7.1, LS-SPEC-006 unable to produce a manifest Per VERTICAL-SLICE-001 §8, this is treated as a defect against LS-SPEC-005/006's guaranteed-fallback design, not a normal failure this document handles gracefully — the whole batch fails (§6.3) rather than committing a stadium state with no corresponding valid world
Fixed-geometry override attempt §5.1 Rejected explicitly, same as VERTICAL-SLICE-001 §8

No auto-cancellation. A failed batch's changes are never silently discarded — they remain failed_commitment, visible to the owner, requiring explicit revision or withdrawal, consistent with the project-wide principle that an owner's decision is never overridden without their knowledge.


9. Replacement Stadiums

9.1 Never a FieldChange

Per §3.2's note: a Replacement is never expressed as a FieldChange on the existing stadium. It's a distinct proposal type:

ReplacementProposal
  predecessor_stadium_id: string
  franchise_id: string
  new_location: settlement_id | 'same_as_predecessor'
  new_site: SiteSelectionResponse | 'candidate_pending'    -- see §9.2
  new_field_geometry: FieldGeometryInput
  preserve_predecessor_as: 'demolished' | 'preserved'      -- DL-SPEC-014 §10, owner's narrative choice

9.2 Relocation Reuses the Existing Candidate-Site Pipeline

If new_location differs from the predecessor's settlement, or a new site within the same settlement is desired, this proposal triggers the identical VERTICAL-SLICE-001 Stage 2/3 flow (CandidateSiteRequest → LS-SPEC-006 §1.1a → LS-SPEC-005 §1.3a → owner selection) — not a shortcut, not a simplified version. A ReplacementProposal with new_site: 'candidate_pending' is not eligible for Step 11 commitment until a SiteSelectionResponse has been captured, the same as initial franchise creation.

9.3 Commitment

At Step 11, a committed ReplacementProposal creates a new stadiums record (predecessor_stadium_id set), transitions the predecessor's status to preserve_predecessor_as's value, and always triggers a full new VenueWorldRequest (§7.2) — never a partial update to the predecessor's record.


10. Persistent Stadium/World History

10.1 What's Written at Commitment

Every committed FacilityCommitmentBatch writes an immutable stadium_renovation_history entry per DL-SPEC-014 §8.1, containing the full before/after StadiumState snapshot, the batch's changes, and — for multiplayer franchises — a corresponding DL-SPEC-013 League Event Log entry per DL-SPEC-014 §8.6. A failed batch also writes a history entry (status failed_commitment, with failure_reason) — failures are part of the permanent record too, not silently omitted.

10.2 Old Stadiums Never Deleted

Unchanged from DL-SPEC-014 §9.3 — a replaced/demolished/preserved stadium's record persists permanently.


11. Invariants

  • INV-1: No StadiumState field changes outside of a Step 11 commitment — proposal and queuing never mutate live state (§4.2).
  • INV-2: Two simultaneously-pending changes on one stadium never target the same field_path (§4.3).
  • INV-3: Facility Commitment is all-or-nothing per stadium per Step 11 pass — no partial application (§6.3, restating DL-SPEC-014 INV-8 operationally).
  • INV-4: A FacilityCommitmentBatch applied to identical pre-commit state always produces an identical result (§6.4).
  • INV-5: Any committed change with affects_venue_footprint = true always triggers a new VenueWorldRequest; no such change ever commits while reusing a stale cached manifest (§7.1, per the confirmed §0 cache-key correction).
  • INV-6: A Replacement is never expressed as a FieldChange; it is always a distinct ReplacementProposal (§9.1).
  • INV-7: An expansion that cannot fit its site is rejected, never silently relocated (§5.1, §8).
  • INV-8: A failed commitment batch is never silently discarded — it persists as failed_commitment with a reason, requiring explicit owner action (§8, §10.1).
  • INV-9: Fixed-by-rule geometry fields are never accepted as a FieldChange target, under any category (§5.1, inherited from VERTICAL-SLICE-001 INV-3).

12. Acceptance Tests

Test Validates
Propose a renovation (scoreboard change) mid-offseason, confirm it queues without altering live StadiumState §4.2, INV-1
Propose two changes targeting the same field_path Second is rejected — INV-2
Propose a facility upgrade (training complex) and confirm no VenueWorldRequest is sent at commitment §7.1 — reuse path
Propose a fence-distance expansion and confirm a VenueWorldRequest is sent, manifest regenerated, cache key includes the new orientation_bucket if orientation changed §6.1, §7.1, §0 (confirmed cache-key correction)
Propose an expansion exceeding site area Rejected, Replacement recommended — §5.1, INV-7
Queue three valid changes, then fail cost re-validation on one at Step 11 Entire batch fails, all three revert to failed_commitment — §6.3, INV-3, INV-8
Commit a Replacement with relocation to a new settlement Full candidate-site pipeline (VERTICAL-SLICE-001 Stage 2/3) is exercised, not bypassed — §9.2
Commit a Replacement preserving the predecessor Predecessor status = 'preserved', never deleted, new stadium's predecessor_stadium_id set — §9.3, §10.2
Run identical FacilityCommitmentBatch twice against identical starting state Byte-identical result both times — INV-4
Attempt a FieldChange on base_path_distance Rejected — INV-9

13. Explicit Non-Goals

This document does not address: league-level effects of stadium changes, franchise marketplace or valuation logic beyond the already-confirmed assessed_value/annual_debt_service field contracts, advertising/cosmetic stadium skins (DL-SPEC-017 §3's territory, untouched here), or any Living World era-transition mechanics beyond what's needed for a single Step 11 pass — all explicitly out of scope per your instruction.


This document and LEGEND-WORLD-VERTICAL-SLICE-001 together fully specify stadium creation and stadium lifecycle change, with no outstanding ownership items. The next stadium/world task is Living World mode's era-transition contracts (LS-SPEC-006 §4.2), which both vertical slices have consistently scoped out as a separate concern.