Preserve approved pixel tower prototype as development baseline

This commit is contained in:
Codex
2026-09-09 07:51:16 +09:00
commit 423fd6dd2e
915 changed files with 95193 additions and 0 deletions
+342
View File
@@ -0,0 +1,342 @@
extends SceneTree
const CatalogType := preload("res://scripts/data/mvp_content_catalog.gd")
var _checks := 0
var _failures := 0
func _initialize() -> void:
call_deferred("_run")
func _run() -> void:
_test_baseline_catalog()
_test_guest_combinations()
_test_six_need_schema()
_test_staff_hire_boundary()
_test_unique_pair_and_defensive_copies()
_test_invalid_content_is_fail_closed()
_test_type_coercion_is_rejected()
_test_fixed_ids_namespaces_and_resource_paths()
_test_unique_character_safety_contract()
_test_numeric_and_container_limits()
_test_json_key_collisions()
_test_error_order_is_deterministic()
if _failures == 0:
print("PASS: MvpContentCatalog (%d checks)" % _checks)
else:
push_error("FAIL: MvpContentCatalog (%d of %d checks failed)" % [_failures, _checks])
quit(_failures)
func _test_baseline_catalog() -> void:
var catalog := CatalogType.new()
var report: Dictionary = catalog.rebuild()
_expect(bool(report[&"valid"]), "MVP character catalog passes strict schema validation")
_expect(int(report[&"error_count"]) == 0, "baseline character data has no validation errors")
_expect(int(report[&"counts"]["guest_classes"]) == 8, "all eight guest classes are indexed")
_expect(int(report[&"counts"]["species"]) == 4, "all four independent species are indexed")
_expect(int(report[&"counts"]["staff_candidates"]) == 20, "all twenty free-hire candidates are indexed")
_expect(int(report[&"counts"]["unique_characters"]) == 2, "one heroine and one rival are indexed")
_expect(catalog.get_guest_class_ids() == [
&"cleric", &"diplomat", &"mage", &"merchant", &"noble", &"outsider", &"rogue", &"warrior",
], "guest classes use stable lexical order independent of JSON order")
_expect(catalog.get_species_ids() == [
&"beastfolk", &"demonfolk", &"elf", &"human",
], "species use stable lexical order")
_expect(catalog.get_staff_candidate_ids().front() == &"staff_akane", "candidate IDs are stable and namespaced")
_expect(catalog.get_staff_candidate_ids().back() == &"staff_tessa", "all candidate IDs participate in deterministic order")
func _test_guest_combinations() -> void:
var catalog := CatalogType.new()
catalog.rebuild()
var definitions := catalog.build_all_guest_definitions()
_expect(definitions.size() == 32, "eight classes and four species produce 32 independent archetypes")
var ids: Array[StringName] = []
for definition in definitions:
ids.append(definition.guest_type_id)
_expect(definition.is_valid(), "generated archetype %s satisfies GuestDefinition invariants" % definition.guest_type_id)
_expect(definition.adult_verified, "generated archetype %s is adult-content gated" % definition.guest_type_id)
var unique_ids: Dictionary = {}
for definition_id: StringName in ids:
unique_ids[definition_id] = true
_expect(unique_ids.size() == 32 and not unique_ids.has(&""), "all generated archetype IDs are non-empty and unique")
var warrior_elf = catalog.build_guest_definition(&"warrior", &"elf")
_expect(warrior_elf != null and warrior_elf.guest_type_id == &"warrior_elf", "class and species IDs form a reproducible archetype ID")
_expect(warrior_elf.wealth_min == 330 and warrior_elf.wealth_max == 990, "species wealth multiplier uses integer basis points")
_expect(warrior_elf.base_patience == 70 and warrior_elf.risk_tolerance == 70, "species modifiers compose with class values once")
_expect(&"combatant" in warrior_elf.tags and &"elf" in warrior_elf.tags, "class and species tags remain separate and both survive composition")
_expect(warrior_elf.preference_weights[&"quiet"] == 8, "species preferences extend class preferences")
_expect(warrior_elf.need_rates[&"sleep"] == 5, "species Need modifiers compose on the canonical Sleep axis")
_expect(catalog.build_guest_definition(&"missing", &"human") == null, "unknown class fails closed")
_expect(catalog.build_guest_definition(&"warrior", &"missing") == null, "unknown species fails closed")
func _test_six_need_schema() -> void:
var catalog := CatalogType.new()
catalog.rebuild()
var definition = catalog.build_guest_definition(&"diplomat", &"demonfolk")
var need_ids: Array = definition.need_rates.keys()
need_ids.sort_custom(func(a: Variant, b: Variant) -> bool: return String(a) < String(b))
_expect(need_ids == [&"hunger", &"hygiene", &"intimacy", &"leisure", &"medical", &"sleep"], "generated definitions expose exactly the canonical six Need IDs")
_expect(not definition.need_rates.has(&"safety") and not definition.need_rates.has(&"social"), "Safety and social context are not persisted as Needs")
_expect(definition.need_rates[&"leisure"] == 5, "species Leisure modifier composes without being misread as Medical")
var documents := _load_documents()
documents[&"guest_classes"]["entries"][0]["need_rates"].erase("hygiene")
documents[&"guest_classes"]["entries"][0]["need_rates"]["safety"] = 1
var report := catalog.rebuild_from_documents(documents)
_expect(&"unknown_map_key" in _issue_codes(report[&"errors"]), "legacy Need names are rejected by the current content schema")
documents = _load_documents()
documents[&"guest_classes"]["entries"][0]["need_rates"].erase("hygiene")
report = catalog.rebuild_from_documents(documents)
_expect(&"missing_map_key" in _issue_codes(report[&"errors"]), "all six class Need rates are required")
documents = _load_documents()
documents[&"species"]["entries"][0]["need_rate_modifiers"]["rest"] = -1
report = catalog.rebuild_from_documents(documents)
_expect(&"unknown_map_key" in _issue_codes(report[&"errors"]), "species modifiers cannot reintroduce a migration-only alias")
func _test_staff_hire_boundary() -> void:
var catalog := CatalogType.new()
catalog.rebuild()
for candidate_id: StringName in catalog.get_staff_candidate_ids():
var candidate := catalog.get_entry(&"staff_candidates", candidate_id)
_expect(bool(candidate[&"adult_verified"]) and int(candidate[&"age"]) >= 18, "%s carries explicit adult verification" % candidate_id)
_expect(int(candidate[&"daily_wage"]) >= 0 and int(candidate[&"signing_cost"]) >= 0, "%s has non-negative employment costs" % candidate_id)
_expect((candidate[&"skills"] as Dictionary).size() == 4, "%s defines all four dispatch skills" % candidate_id)
var payload := catalog.staff_hire_payload(&"staff_klaus")
_expect(payload[&"staff_id"] == &"staff_klaus" and payload[&"role_id"] == &"guard", "hire payload preserves candidate and role IDs")
_expect(payload[&"adult_verified"] and int(payload[&"age"]) == 36, "hire payload carries the adult gate across the runtime boundary")
_expect(payload[&"preferred_shift"] == &"day", "hire payload exposes the validated preferred shift to the scheduler adapter")
_expect(payload[&"options"][&"daily_wage"] == 155 and payload[&"options"][&"employment_kind"] == &"FREE_HIRE", "runtime options preserve wage liability and ordinary employment kind")
_expect(payload[&"options"][&"skills"][&"combat"] == 82, "hire payload maps static skills into StaffRuntime options")
_expect(&"security" in payload[&"options"][&"permitted_task_tags"], "hire payload preserves permitted task tags")
var runtime := StaffRuntime.new().configure(payload[&"staff_id"], payload[&"role_id"], payload[&"options"])
_expect(runtime.is_valid() and runtime.daily_wage == 155 and runtime.get_employment_kind_name() == &"FREE_HIRE", "hire payload constructs an exact payroll-bearing StaffRuntime")
_expect(catalog.staff_hire_payload(&"missing").is_empty(), "unknown candidate cannot produce a hire payload")
func _test_unique_pair_and_defensive_copies() -> void:
var catalog := CatalogType.new()
catalog.rebuild()
var heroine := catalog.get_entry(&"unique_characters", &"heroine_anri")
var rival := catalog.get_entry(&"unique_characters", &"rival_rai")
_expect(heroine[&"character_kind"] == &"heroine" and rival[&"character_kind"] == &"rival", "unique characters have distinct mechanical kinds")
_expect(heroine[&"counterpart_id"] == &"rival_rai" and rival[&"counterpart_id"] == &"heroine_anri", "heroine and rival references are reciprocal")
_expect(heroine[&"adult_verified"] and rival[&"adult_verified"], "both relationship participants are explicitly adult-verified")
_expect(heroine[&"mechanical_effects"][&"assigned_floor_patience_loss_multiplier_bp"] == 9000, "heroine baseline effect is represented in integer basis points")
heroine[&"display_name"] = "tampered"
heroine[&"mechanical_effects"][&"assigned_floor_patience_loss_multiplier_bp"] = 1
var pristine := catalog.get_entry(&"unique_characters", &"heroine_anri")
_expect(pristine[&"display_name"] == "アンリ", "returned definitions cannot mutate catalog identity")
_expect(pristine[&"mechanical_effects"][&"assigned_floor_patience_loss_multiplier_bp"] == 9000, "returned nested data is also a defensive copy")
func _test_invalid_content_is_fail_closed() -> void:
var documents := _load_documents()
var staff_entries: Array = documents[&"staff_candidates"]["entries"]
staff_entries[0]["adult_verified"] = false
staff_entries[1]["age"] = 17
staff_entries[2]["traits"].append("unknown_trait")
staff_entries[3]["typo_field"] = 1
var guest_entries: Array = documents[&"guest_classes"]["entries"]
guest_entries[1]["id"] = guest_entries[0]["id"]
var catalog := CatalogType.new()
var report: Dictionary = catalog.rebuild_from_documents(documents)
_expect(not bool(report[&"valid"]), "invalid character data rejects the whole catalog")
_expect(catalog.get_staff_candidate_ids().is_empty(), "fail-closed rebuild exposes no partially valid candidates")
_expect(catalog.build_all_guest_definitions().is_empty(), "fail-closed rebuild cannot generate partial archetypes")
var codes := _issue_codes(report[&"errors"])
_expect(&"adult_verification_required" in codes, "missing adult verification has a stable error code")
_expect(&"adult_age_required" in codes, "underage data has a stable error code")
_expect(&"unknown_tag" in codes, "unknown trait is rejected instead of silently ignored")
_expect(&"unknown_field" in codes, "misspelled data fields are rejected")
_expect(&"duplicate_id" in codes, "duplicate stable IDs are rejected")
func _test_error_order_is_deterministic() -> void:
var documents := _load_documents()
documents[&"species"]["schema_version"] = 99
documents[&"unique_characters"]["entries"][0]["counterpart_id"] = "missing_actor"
var catalog := CatalogType.new()
var first: Dictionary = catalog.rebuild_from_documents(documents)
var second: Dictionary = catalog.rebuild_from_documents(documents)
_expect(first[&"errors"] == second[&"errors"], "content error order is deterministic across rebuilds")
_expect(not bool(second[&"valid"]), "unsupported schema and orphan references remain fatal")
_expect(&"unsupported_schema" in _issue_codes(second[&"errors"]), "schema rejection has a stable code")
_expect(&"missing_counterpart" in _issue_codes(second[&"errors"]), "orphan counterpart has a stable code")
func _test_type_coercion_is_rejected() -> void:
var documents := _load_documents()
documents[&"species"]["schema_version"] = "1"
documents[&"guest_classes"]["entries"][0]["wealth_min"] = "300"
documents[&"staff_candidates"]["entries"][0]["adult_verified"] = "true"
documents[&"staff_candidates"]["entries"][1]["age"] = "38"
documents[&"staff_candidates"]["entries"][2]["daily_wage"] = "145"
documents[&"unique_characters"]["entries"][0]["character_kind"] = 1
var catalog := CatalogType.new()
var report: Dictionary = catalog.rebuild_from_documents(documents)
var codes := _issue_codes(report[&"errors"])
_expect(not bool(report[&"valid"]), "stringified booleans and numbers do not pass strict content validation")
_expect(&"unsupported_schema" in codes, "string schema version is not coerced")
_expect(&"invalid_wealth_range" in codes, "string wealth is not coerced")
_expect(&"adult_verification_required" in codes, "string adult flag is not truthy-coerced")
_expect(&"adult_age_required" in codes, "string age is not coerced")
_expect(&"negative_value" in codes, "string employment cost is not coerced")
_expect(&"unknown_character_kind" in codes, "numeric character kind is not coerced")
func _test_fixed_ids_namespaces_and_resource_paths() -> void:
var documents := _load_documents()
documents[&"guest_classes"]["entries"][0]["id"] = "replacement_class"
var catalog := CatalogType.new()
var report := catalog.rebuild_from_documents(documents)
_expect(&"mvp_id_set_mismatch" in _issue_codes(report[&"errors"]), "same-count replacement cannot silently change the fixed MVP ID set")
_expect(not bool(report[&"valid"]), "fixed-ID mismatch fails closed")
documents = _load_documents()
documents[&"staff_candidates"]["entries"][0]["id"] = "akane"
report = catalog.rebuild_from_documents(documents)
_expect(&"invalid_namespace" in _issue_codes(report[&"errors"]), "free-hire actor IDs must retain the staff namespace")
documents = _load_documents()
documents[&"species"]["entries"][0]["tags"] = ["elf"]
report = catalog.rebuild_from_documents(documents)
_expect(&"species_identity_mismatch" in _issue_codes(report[&"errors"]), "species identity cannot be changed by swapping its tag")
var paths := CatalogType.DEFAULT_PATHS.duplicate(true)
paths[&"species"] = "res://data/characters/../characters/mvp_species.json"
report = catalog.rebuild(paths)
_expect(&"invalid_resource_path" in _issue_codes(report[&"errors"]), "resource traversal segments are rejected before file parsing")
_expect(catalog.get_species_ids().is_empty(), "invalid resource paths expose no stale prior catalog")
paths = CatalogType.DEFAULT_PATHS.duplicate(true)
paths.erase(&"species")
report = catalog.rebuild(paths)
_expect(&"missing_content_type" in _issue_codes(report[&"errors"]), "custom path sets must name every required document")
func _test_unique_character_safety_contract() -> void:
var documents := _load_documents()
var heroine: Dictionary = documents[&"unique_characters"]["entries"][0]
heroine["traits"].erase("unsellable")
var catalog := CatalogType.new()
var report := catalog.rebuild_from_documents(documents)
_expect(&"unique_safety_traits_required" in _issue_codes(report[&"errors"]), "unique actors cannot lose the unsellable safety marker")
documents = _load_documents()
documents[&"staff_candidates"]["entries"][0]["traits"].append("unique")
report = catalog.rebuild_from_documents(documents)
_expect(&"reserved_trait" in _issue_codes(report[&"errors"]), "free-hire candidates cannot impersonate unique actors")
documents = _load_documents()
heroine = documents[&"unique_characters"]["entries"][0]
heroine["mechanical_effects"]["arbitrary_runtime_hook"] = 1
report = catalog.rebuild_from_documents(documents)
var codes := _issue_codes(report[&"errors"])
_expect(&"invalid_mechanical_effect" in codes, "unknown mechanical effect keys are rejected")
_expect(&"effect_kind_mismatch" in codes, "effects cannot cross the heroine/rival capability boundary")
documents = _load_documents()
documents[&"unique_characters"]["entries"][0]["mechanical_effects"]["assigned_floor_patience_loss_multiplier_bp"] = 2_000
report = catalog.rebuild_from_documents(documents)
_expect(&"invalid_mechanical_effect" in _issue_codes(report[&"errors"]), "basis-point effects below the global multiplier floor are rejected")
documents = _load_documents()
documents[&"unique_characters"]["entries"][1]["mechanical_effects"]["base_shield_attack"] = 101
report = catalog.rebuild_from_documents(documents)
_expect(&"invalid_mechanical_effect" in _issue_codes(report[&"errors"]), "relationship attack data cannot exceed its bounded runtime scale")
func _test_numeric_and_container_limits() -> void:
var documents := _load_documents()
documents[&"staff_candidates"]["entries"][0]["age"] = CatalogType.MAX_AGE + 1
var catalog := CatalogType.new()
var report := catalog.rebuild_from_documents(documents)
_expect(&"adult_age_required" in _issue_codes(report[&"errors"]), "implausibly large ages do not enter persistent actor data")
documents = _load_documents()
documents[&"guest_classes"]["entries"][0]["wealth_max"] = CatalogType.MAX_GOLD_VALUE + 1
report = catalog.rebuild_from_documents(documents)
_expect(&"invalid_wealth_range" in _issue_codes(report[&"errors"]), "money values are bounded before basis-point multiplication")
documents = _load_documents()
documents[&"staff_candidates"]["entries"][0]["daily_wage"] = INF
report = catalog.rebuild_from_documents(documents)
_expect(&"negative_value" in _issue_codes(report[&"errors"]), "non-finite numeric input is never accepted as an integer")
documents = _load_documents()
documents[&"staff_candidates"]["entries"][0]["display_name"] = "unsafe\nname"
report = catalog.rebuild_from_documents(documents)
_expect(&"missing_display_name" in _issue_codes(report[&"errors"]), "control characters are rejected from player-visible names")
documents = _load_documents()
for raw_document: Variant in documents.values():
for raw_entry: Variant in raw_document["entries"]:
for index in 40:
raw_entry["unexpected_%02d" % index] = index
report = catalog.rebuild_from_documents(documents)
_expect(int(report[&"error_count"]) == CatalogType.MAX_ERRORS, "adversarial documents cannot amplify validation reports beyond the error cap")
_expect(bool(report[&"errors_truncated"]), "capped validation reports explicitly disclose truncation")
_expect(not bool(report[&"valid"]), "error-report bounding never weakens fail-closed validation")
func _test_json_key_collisions() -> void:
var catalog := CatalogType.new()
_expect(
catalog._validate_json_key_structure("{\"id\":1,\"id\":2}") == &"duplicate_json_key",
"duplicate JSON object keys are rejected before the parser can overwrite one value"
)
_expect(
catalog._validate_json_key_structure("{\"id\":1,\"\\u0069d\":2}") == &"duplicate_json_key",
"escaped and literal forms of the same JSON key collide canonically"
)
_expect(
catalog._validate_json_key_structure("{\"outer\":{\"age\":1,\"age\":2}}") == &"duplicate_json_key",
"duplicate-key detection covers nested content maps"
)
_expect(
catalog._validate_json_key_structure("[".repeat(CatalogType.MAX_JSON_DEPTH + 1) + "0" + "]".repeat(CatalogType.MAX_JSON_DEPTH + 1)) == &"json_structure_limit_exceeded",
"deeply nested JSON is rejected before recursive validation can exhaust the stack"
)
for path: String in CatalogType.DEFAULT_PATHS.values():
var file := FileAccess.open(path, FileAccess.READ)
_expect(catalog._validate_json_key_structure(file.get_as_text()).is_empty(), "%s has a unique bounded JSON key structure" % path.get_file())
var documents := _load_documents()
documents[&"species"]["entries"][0][42] = "not_a_field_name"
var report := catalog.rebuild_from_documents(documents)
_expect(&"invalid_key_type" in _issue_codes(report[&"errors"]), "non-string object keys are rejected without coercion")
func _load_documents() -> Dictionary:
var result: Dictionary = {}
for content_type: StringName in [&"guest_classes", &"species", &"staff_candidates", &"unique_characters"]:
var path := String(CatalogType.DEFAULT_PATHS[content_type])
var file := FileAccess.open(path, FileAccess.READ)
result[content_type] = JSON.parse_string(file.get_as_text())
return result
func _issue_codes(raw_issues: Variant) -> Array[StringName]:
var result: Array[StringName] = []
if raw_issues is Array:
for raw_issue: Variant in raw_issues:
if raw_issue is Dictionary:
var code := StringName(raw_issue.get(&"code", raw_issue.get("code", "")))
if not result.has(code):
result.append(code)
return result
func _expect(condition: bool, label: String) -> void:
_checks += 1
if condition:
print(" ✓ %s" % label)
return
_failures += 1
push_error(" ✗ %s" % label)
@@ -0,0 +1 @@
uid://ce2yaa87mm67b
File diff suppressed because it is too large Load Diff
@@ -0,0 +1 @@
uid://cbf77pwt7oqjy
+415
View File
@@ -0,0 +1,415 @@
extends SceneTree
const ProgressionType := preload("res://scripts/progression/progression_service.gd")
var _checks := 0
var _failures := 0
func _initialize() -> void:
call_deferred("_run")
func _run() -> void:
_test_rank_bounds_and_evidence()
_test_metrics_are_atomic_and_lifetime_monotonic()
_test_rank_one_through_seven()
_test_external_transaction_and_idempotency()
_test_failed_rollback_quarantines_progression()
_test_callback_reentry_and_internal_tamper()
_test_snapshot_round_trip_and_atomic_rejection()
_test_promotion_evidence_and_legacy_migration()
_test_input_and_history_bounds()
if _failures == 0:
print("PASS: ProgressionService (%d checks)" % _checks)
else:
push_error("FAIL: ProgressionService (%d of %d checks failed)" % [_failures, _checks])
quit(_failures)
func _test_rank_bounds_and_evidence() -> void:
var service := ProgressionType.new()
_expect(service.configure(), "progression starts at Rank 1")
_expect(service.current_rank == 1, "configured rank is authoritative")
_expect(service.get_rank_bounds(1) == {&"min_x":-12, &"max_x":12, &"min_y":0, &"max_y":6}, "Rank 1 bounds match the system specification")
_expect(service.get_rank_bounds(3) == {&"min_x":-24, &"max_x":24, &"min_y":-8, &"max_y":16}, "Rank 3 MVP bounds include eight underground levels")
_expect(service.get_rank_bounds(7) == {&"min_x":-60, &"max_x":60, &"min_y":-40, &"max_y":56}, "Rank 7 bounds match the final expansion")
_expect(service.get_rank_bounds(8).is_empty(), "unknown rank has no implicit clamped bounds")
var report := service.evaluate_rank(2)
_expect(not report[&"eligible"] and report[&"reason"] == &"REQUIREMENTS_NOT_MET", "Rank 2 reports unmet requirements")
_expect(_requirement_ids(report) == [&"reputation", &"asset_gold"], "Rank 2 evidence order is stable and player-readable")
_expect(report[&"requirements"][0][&"current"] == 0 and report[&"requirements"][0][&"required"] == 25, "eligibility evidence includes current and required values")
var skip := service.evaluate_rank(3)
_expect(not skip[&"eligible"] and skip[&"reason"] == &"TARGET_MUST_BE_NEXT_RANK", "rank skipping is rejected before metric evaluation")
func _test_metrics_are_atomic_and_lifetime_monotonic() -> void:
var service := ProgressionType.new()
service.configure()
_expect(service.update_metrics({&"reputation": 25, &"asset_gold": 10_000, &"faith_lifetime": 100}), "valid metric patch commits atomically")
var before := service.metrics.duplicate(true)
_expect(not service.update_metrics({&"reputation": 101, &"asset_gold": 20_000}), "out-of-range percentage rejects the whole patch")
_expect(service.metrics == before, "failed metric patch preserves every prior metric")
_expect(not service.update_metrics({&"faith_lifetime": 99}), "lifetime Faith cannot decrease after spending current Faith")
_expect(not service.update_metrics({&"guest_lifetime": -1}), "lifetime guest count cannot become negative")
_expect(not service.update_metrics({&"unknown": 1}), "unknown progression metric is rejected")
_expect(not service.update_metrics({&"asset_gold": "10000"}), "string numbers are not coerced into progression facts")
_expect(service.metrics == before, "all rejected patches leave progression facts unchanged")
_expect(service.complete_quest(&"important_rank_5"), "valid stable quest ID records once")
_expect(service.complete_quest(&"important_rank_5"), "completing the same quest is idempotent")
_expect(service.completed_quest_ids.size() == 1, "idempotent quest completion creates no duplicate")
_expect(not service.complete_quest(&"Bad Quest"), "invalid quest ID cannot enter persistent progression")
func _test_rank_one_through_seven() -> void:
var service := ProgressionType.new()
service.configure()
service.update_metrics({&"reputation":25, &"asset_gold":10_000})
_expect(service.request_rank_up(&"rank_up_2", 2).is_success(), "Rank 2 unlocks at Reputation 25 and assets 10,000G")
service.update_metrics({&"faith_lifetime":100, &"guest_lifetime":300})
_expect(service.request_rank_up(&"rank_up_3", 3).is_success(), "Rank 3 unlocks at lifetime Faith 100 and 300 guests")
service.update_metrics({
&"reputation":45,
&"asset_gold":50_000,
&"active_above_ground_facilities":1,
&"active_underground_facilities":1,
})
_expect(service.request_rank_up(&"rank_up_4", 4).is_success(), "Rank 4 additionally requires active facilities above and below ground")
service.update_metrics({&"faith_lifetime":500})
var rank_five_missing := service.evaluate_rank(5)
_expect(not rank_five_missing[&"eligible"], "Rank 5 does not unlock from numbers alone")
service.complete_quest(&"important_rank_5")
_expect(service.request_rank_up(&"rank_up_5", 5).is_success(), "Rank 5 requires its explicit important quest")
service.update_metrics({&"underground_influence":65})
var rank_six := service.evaluate_rank(6)
_expect(rank_six[&"eligible"], "Rank 6 accepts underground influence as the specified Reputation alternative")
_expect(service.request_rank_up(&"rank_up_6", 6).is_success(), "Rank 6 commits through the alternative branch")
service.update_metrics({&"faith_lifetime":1500, &"reputation":60})
var rank_seven_missing := service.evaluate_rank(7)
_expect(not rank_seven_missing[&"eligible"], "Rank 7 still requires the main chapter flag")
service.complete_quest(&"main_chapter_complete")
_expect(service.request_rank_up(&"rank_up_7", 7).is_success(), "Rank 7 requires Faith, both progress axes, dual operation and chapter completion")
_expect(service.current_rank == 7, "rank progression is monotonic through the final rank")
_expect(not service.request_rank_up(&"rank_up_8", 8).is_success(), "Rank 8 is rejected")
func _test_external_transaction_and_idempotency() -> void:
var service := _eligible_rank_two_service()
var external := {&"rank":1, &"bounds":service.get_rank_bounds(1), &"apply_calls":0, &"rollback_calls":0}
var capture := func(_current: int, _target: int) -> Dictionary:
return {&"ok":true, &"snapshot":{&"rank":external[&"rank"], &"bounds":external[&"bounds"].duplicate(true)}}
var apply := func(target: int, bounds: Dictionary) -> Dictionary:
external[&"apply_calls"] += 1
external[&"rank"] = target
external[&"bounds"] = bounds.duplicate(true)
return {&"ok":true}
var rollback := func(snapshot: Dictionary) -> Dictionary:
external[&"rollback_calls"] += 1
external[&"rank"] = snapshot[&"rank"]
external[&"bounds"] = snapshot[&"bounds"].duplicate(true)
return {&"ok":true}
var result := service.request_rank_up(&"atomic_rank_2", 2, capture, apply, rollback)
_expect(result.is_success(), "eligible rank-up commits through capture/apply boundary")
_expect(service.current_rank == 2 and external[&"rank"] == 2, "internal rank and external world commit together")
_expect(external[&"bounds"] == service.get_rank_bounds(2), "external world receives exact target bounds")
var replay := service.request_rank_up(&"atomic_rank_2", 2)
_expect(replay.is_success() and replay.is_replay, "same request ID and target returns its recorded success")
_expect(external[&"apply_calls"] == 1, "idempotent replay does not apply external expansion twice")
var conflict := service.request_rank_up(&"atomic_rank_2", 3)
_expect(not conflict.is_success() and conflict.code == CommandResult.Code.DUPLICATE_IDEMPOTENCY_KEY, "same request ID with another target conflicts")
var partial := service.request_rank_up(&"partial_callbacks", 3, capture, Callable(), rollback)
_expect(not partial.is_success() and partial.code == CommandResult.Code.INVALID_PAYLOAD, "partial callback set is rejected before mutation")
var strict_callbacks := _eligible_rank_two_service()
var bad_capture := func(_current: int, _target: int) -> Dictionary:
return {&"ok":"true", &"snapshot":{}}
var bad_capture_result := strict_callbacks.request_rank_up(&"bad_capture_type", 2, bad_capture, apply, rollback)
_expect(not bad_capture_result.is_success() and external[&"apply_calls"] == 1, "truthy string capture status is rejected before external apply")
var string_apply := func(_target: int, _bounds: Dictionary) -> Dictionary:
return {&"ok":"true"}
var strict_rollback := func(_snapshot: Dictionary) -> Dictionary:
return {&"ok":true}
var bad_apply_result := strict_callbacks.request_rank_up(&"bad_apply_type", 2, capture, string_apply, strict_rollback)
_expect(not bad_apply_result.is_success() and strict_callbacks.current_rank == 1, "truthy string apply status is treated as failure and rolled back")
func _test_failed_rollback_quarantines_progression() -> void:
var rolled_back := _eligible_rank_two_service()
var external_rank := 1
var capture := func(_current: int, _target: int) -> Dictionary:
return {&"ok":true, &"snapshot":external_rank}
var failed_apply := func(target: int, _bounds: Dictionary) -> Dictionary:
external_rank = target
return {&"ok":false}
var successful_rollback := func(snapshot: int) -> Dictionary:
external_rank = snapshot
return {&"ok":true}
var rolled_back_result := rolled_back.request_rank_up(
&"failed_but_rolled_back", 2, capture, failed_apply, successful_rollback
)
_expect(not rolled_back_result.is_success(), "failed external expansion rejects rank-up")
_expect(rolled_back.current_rank == 1 and external_rank == 1, "successful rollback restores both sides to Rank 1")
_expect(not rolled_back.quarantined, "verified rollback keeps progression available for a later request")
var quarantined := _eligible_rank_two_service()
external_rank = 1
var failed_rollback := func(_snapshot: int) -> Dictionary:
return {&"ok":false}
var quarantined_result := quarantined.request_rank_up(
&"rollback_failure", 2, capture, failed_apply, failed_rollback
)
_expect(not quarantined_result.is_success() and quarantined_result.code == CommandResult.Code.TRANSACTION_CLOSED, "rollback failure closes the progression boundary")
_expect(quarantined.quarantined and quarantined.quarantine_reason == &"RANK_ROLLBACK_FAILED", "rollback failure records a durable quarantine reason")
_expect(not quarantined.update_metrics({&"reputation":30}), "quarantine blocks later metric mutation")
_expect(not quarantined.request_rank_up(&"retry_after_quarantine", 2).is_success(), "quarantine blocks retry and prevents double expansion")
func _test_callback_reentry_and_internal_tamper() -> void:
var service := _eligible_rank_two_service()
var external_rank := 1
var observations := {&"metric_update":true, &"nested":null}
var capture := func(_current: int, _target: int) -> Dictionary:
return {&"ok":true, &"snapshot":external_rank}
var reentrant_apply := func(target: int, _bounds: Dictionary) -> Dictionary:
observations[&"metric_update"] = service.update_metrics({&"reputation":99})
observations[&"nested"] = service.request_rank_up(&"nested_rank_request", 2)
external_rank = target
return {&"ok":true}
var rollback := func(snapshot: int) -> Dictionary:
external_rank = snapshot
return {&"ok":true}
var result := service.request_rank_up(&"guarded_rank_request", 2, capture, reentrant_apply, rollback)
_expect(result.is_success(), "blocked reentry cannot prevent an otherwise valid outer promotion")
_expect(not bool(observations[&"metric_update"]), "rank callback cannot mutate eligibility metrics reentrantly")
var nested := observations[&"nested"] as CommandResult
_expect(nested != null and not nested.is_success() and nested.code == CommandResult.Code.TRANSACTION_CLOSED, "nested promotion is rejected while the outer transaction is active")
_expect(service.metrics[&"reputation"] == 25 and service.current_rank == 2, "blocked callback reentry leaves one exact promotion and original evidence")
_expect(service.create_snapshot()[&"processed_requests"].size() == 1, "reentry guard creates no hidden second idempotency record")
var tampered := _eligible_rank_two_service()
external_rank = 1
var tampering_apply := func(target: int, _bounds: Dictionary) -> Dictionary:
external_rank = target
tampered.metrics[&"reputation"] = 99
return {&"ok":true}
result = tampered.request_rank_up(&"tampered_apply", 2, capture, tampering_apply, rollback)
_expect(not result.is_success() and result.code == CommandResult.Code.TRANSACTION_APPLY_FAILED, "direct internal mutation by an apply callback invalidates the transaction")
_expect(tampered.current_rank == 1 and tampered.metrics[&"reputation"] == 25 and external_rank == 1, "tampered apply rolls both internal and external state back exactly")
_expect(not tampered.quarantined, "verified rollback after callback tamper keeps later promotion available")
var rollback_tampered := _eligible_rank_two_service()
external_rank = 1
var failed_apply := func(target: int, _bounds: Dictionary) -> Dictionary:
external_rank = target
return {&"ok":false}
var mutating_rollback := func(snapshot: int) -> Dictionary:
external_rank = snapshot
rollback_tampered.current_rank = 7
return {&"ok":true}
result = rollback_tampered.request_rank_up(&"tampered_rollback", 2, capture, failed_apply, mutating_rollback)
_expect(not result.is_success() and rollback_tampered.current_rank == 1, "internal state is restored even when a successful rollback callback tampers with it")
_expect(external_rank == 1 and not rollback_tampered.quarantined, "successful external rollback remains authoritative after internal tamper repair")
var valid_capture_tampered := _eligible_rank_two_service()
var callback_counts := {&"apply":0, &"rollback":0}
var mutating_capture := func(_current: int, _target: int) -> Dictionary:
valid_capture_tampered.metrics[&"reputation"] = 99
return {&"ok":true, &"snapshot":1}
var counted_apply := func(_target: int, _bounds: Dictionary) -> Dictionary:
callback_counts[&"apply"] += 1
return {&"ok":true}
var counted_rollback := func(_snapshot: int) -> Dictionary:
callback_counts[&"rollback"] += 1
return {&"ok":true}
result = valid_capture_tampered.request_rank_up(&"valid_capture_tamper", 2, mutating_capture, counted_apply, counted_rollback)
_expect(not result.is_success() and callback_counts[&"apply"] == 0, "valid-looking capture tamper is detected before external apply")
_expect(callback_counts[&"rollback"] == 1 and valid_capture_tampered.metrics[&"reputation"] == 25, "capture tamper invokes one rollback and restores internal eligibility")
var capture_tampered := _eligible_rank_two_service()
var bad_capture := func(_current: int, _target: int) -> Dictionary:
capture_tampered.current_rank = 7
return {&"ok":false}
result = capture_tampered.request_rank_up(&"tampered_capture", 2, bad_capture, reentrant_apply, rollback)
_expect(not result.is_success() and result.code == CommandResult.Code.TRANSACTION_CLOSED, "invalid capture that mutates progression enters quarantine")
_expect(capture_tampered.current_rank == 1 and capture_tampered.quarantine_reason == &"RANK_CALLBACK_TAMPERED", "capture tamper restores the last valid rank and records the uncertainty reason")
func _test_snapshot_round_trip_and_atomic_rejection() -> void:
var source := _eligible_rank_two_service()
source.complete_quest(&"important_rank_5")
source.request_rank_up(&"snapshot_rank_2", 2)
var snapshot := source.create_snapshot()
var restored := ProgressionType.new()
_expect(restored.restore_snapshot(snapshot), "valid progression snapshot restores")
_expect(restored.create_snapshot() == snapshot, "progression snapshot round trip is exact")
var json_snapshot: Variant = JSON.parse_string(JSON.stringify(snapshot))
var json_restored := ProgressionType.new()
var json_ok := json_snapshot is Dictionary and json_restored.restore_snapshot(json_snapshot)
_expect(json_ok, "JSON-decoded string-key snapshot restores through the same strict boundary (%s)" % json_restored.last_error)
_expect(json_restored.create_snapshot() == snapshot, "JSON transport preserves all promotion evidence exactly")
var before := restored.create_snapshot()
var missing_metric := snapshot.duplicate(true)
missing_metric[&"metrics"].erase("asset_gold")
_expect(not restored.restore_snapshot(missing_metric), "snapshot missing one progression metric is rejected")
_expect(restored.create_snapshot() == before, "missing-metric rejection is atomic")
var bad_fingerprint := snapshot.duplicate(true)
bad_fingerprint[&"processed_requests"][0][&"fingerprint"] = "rank:7"
_expect(not restored.restore_snapshot(bad_fingerprint), "request fingerprint inconsistent with recorded rank is rejected")
_expect(restored.create_snapshot() == before, "fingerprint rejection preserves live progression")
var bad_bounds := snapshot.duplicate(true)
bad_bounds[&"processed_requests"][0][&"details"][&"bounds"][&"max_x"] = 999
_expect(not restored.restore_snapshot(bad_bounds), "derived rank bounds cannot be tampered in a snapshot")
_expect(restored.create_snapshot() == before, "derived-bounds rejection is atomic")
var bad_quarantine := snapshot.duplicate(true)
bad_quarantine[&"quarantined"] = true
bad_quarantine[&"quarantine_reason"] = ""
_expect(not restored.restore_snapshot(bad_quarantine), "quarantine boolean requires a non-empty reason")
_expect(restored.create_snapshot() == before, "quarantine mismatch rejection is atomic")
var string_rank := snapshot.duplicate(true)
string_rank[&"current_rank"] = "2"
_expect(not restored.restore_snapshot(string_rank), "string rank is not coerced during restore")
_expect(restored.create_snapshot() == before, "type rejection preserves live progression")
var string_version := snapshot.duplicate(true)
string_version[&"version"] = "1"
_expect(not restored.restore_snapshot(string_version), "string schema version is not coerced during restore")
_expect(restored.create_snapshot() == before, "schema type rejection is atomic")
var unknown_root := snapshot.duplicate(true)
unknown_root[&"unexpected"] = true
_expect(not restored.restore_snapshot(unknown_root), "unknown root snapshot field is rejected")
_expect(restored.create_snapshot() == before, "unknown-field rejection preserves live progression")
var duplicate_rank_history := snapshot.duplicate(true)
var duplicate_record: Dictionary = duplicate_rank_history[&"processed_requests"][0].duplicate(true)
duplicate_record[&"request_id"] = "snapshot_rank_2_b"
duplicate_record[&"details"][&"request_id"] = &"snapshot_rank_2_b"
duplicate_rank_history[&"processed_requests"].append(duplicate_record)
_expect(not restored.restore_snapshot(duplicate_rank_history), "two successful request IDs cannot claim the same one-time rank transition")
_expect(restored.create_snapshot() == before, "duplicate-transition rejection is atomic")
func _test_promotion_evidence_and_legacy_migration() -> void:
var source := _eligible_rank_two_service()
source.request_rank_up(&"evidence_rank_2", 2)
var snapshot := source.create_snapshot()
var evidence: Dictionary = snapshot[&"processed_requests"][0][&"details"][&"eligibility"]
_expect(snapshot[&"version"] == 2, "promotion evidence is stored in Progression Snapshot 2")
_expect(evidence[&"eligible"] and evidence[&"current_rank"] == 1 and evidence[&"target_rank"] == 2, "saved eligibility header binds the exact one-step transition")
_expect(_requirement_ids(evidence) == [&"reputation", &"asset_gold"], "saved eligibility retains deterministic condition order")
var restored := ProgressionType.new()
_expect(restored.configure(), "evidence rejection target starts from a known valid state")
var before := restored.create_snapshot()
var below_threshold := snapshot.duplicate(true)
below_threshold[&"processed_requests"][0][&"details"][&"eligibility"][&"requirements"][0][&"current"] = 24
_expect(not restored.restore_snapshot(below_threshold), "promotion history below its threshold is rejected")
_expect(restored.create_snapshot() == before, "invalid promotion evidence rejection is atomic")
var changed_requirement := snapshot.duplicate(true)
changed_requirement[&"processed_requests"][0][&"details"][&"eligibility"][&"requirements"][0][&"required"] = 24
_expect(not restored.restore_snapshot(changed_requirement), "saved threshold definitions cannot be weakened")
_expect(restored.create_snapshot() == before, "threshold tamper preserves the live progression")
var missing_history := snapshot.duplicate(true)
missing_history[&"processed_requests"] = []
_expect(not restored.restore_snapshot(missing_history), "Rank 2 cannot be restored without its Rank 2 promotion record")
_expect(restored.create_snapshot() == before, "incomplete-history rejection is atomic")
var rank_three := _rank_three_service()
var lifetime_tamper := rank_three.create_snapshot()
lifetime_tamper[&"metrics"]["faith_lifetime"] = 99
_expect(not restored.restore_snapshot(lifetime_tamper), "current lifetime Faith cannot fall below its saved Rank 3 evidence")
_expect(restored.create_snapshot() == before, "lifetime-history mismatch preserves the live progression")
var rank_five := _rank_five_service()
var quest_tamper := rank_five.create_snapshot()
quest_tamper[&"completed_quest_ids"].erase("important_rank_5")
_expect(not restored.restore_snapshot(quest_tamper), "a monotonic quest used for Rank 5 cannot disappear from the snapshot")
_expect(restored.create_snapshot() == before, "quest-history mismatch is rejected atomically")
var legacy_source := ProgressionType.new()
legacy_source.configure(1, {&"reputation":10}, [&"tutorial_complete"])
var legacy_rank_one := legacy_source.create_snapshot()
legacy_rank_one[&"version"] = 1
_expect(restored.restore_snapshot(legacy_rank_one), "unambiguous legacy Rank 1 snapshot migrates safely")
_expect(restored.create_snapshot()[&"version"] == 2 and restored.current_rank == 1, "legacy restore is immediately re-emitted as Snapshot 2")
var legacy_progressed := snapshot.duplicate(true)
legacy_progressed[&"version"] = 1
legacy_progressed[&"processed_requests"][0][&"details"].erase(&"eligibility")
var migrated_before := restored.create_snapshot()
_expect(not restored.restore_snapshot(legacy_progressed), "legacy progressed snapshot without condition evidence is rejected as ambiguous")
_expect(restored.create_snapshot() == migrated_before, "ambiguous legacy rejection preserves the migrated Rank 1 state")
func _test_input_and_history_bounds() -> void:
var service := ProgressionType.new()
_expect(not service.configure(2), "configuration cannot bypass the one-rank-at-a-time promotion boundary")
_expect(service.configure(), "valid Rank 1 configuration remains available after rejection")
_expect(not service.update_metrics({&"asset_gold":ProgressionType.MAX_SAFE_INTEGER + 1}), "metrics above the JSON-safe integer range are rejected")
_expect(not service.update_metrics({&"asset_gold":INF}), "non-finite metrics are rejected")
var oversized_quests: Array = []
for index in ProgressionType.MAX_COMPLETED_QUESTS + 1:
oversized_quests.append("quest_%03d" % index)
_expect(not service.configure(1, {}, oversized_quests), "oversized initial quest input is rejected before per-entry work")
service = _eligible_rank_two_service()
var invalid_id := service.request_rank_up(&"rank request with spaces", 2)
_expect(not invalid_id.is_success() and invalid_id.code == CommandResult.Code.INVALID_PAYLOAD, "idempotency keys must use stable lowercase IDs")
_expect(service.current_rank == 1, "invalid idempotency key cannot mutate rank")
var impossible := service.create_snapshot()
impossible[&"current_rank"] = 7
var before := service.create_snapshot()
_expect(not service.restore_snapshot(impossible), "fabricated high rank without contiguous promotion history is rejected")
_expect(service.create_snapshot() == before, "fabricated-rank rejection is atomic")
func _eligible_rank_two_service() -> ProgressionService:
var service := ProgressionType.new()
service.configure(1, {&"reputation":25, &"asset_gold":10_000})
return service
func _rank_three_service() -> ProgressionService:
var service := _eligible_rank_two_service()
service.request_rank_up(&"history_rank_2", 2)
service.update_metrics({&"faith_lifetime":100, &"guest_lifetime":300})
service.request_rank_up(&"history_rank_3", 3)
return service
func _rank_five_service() -> ProgressionService:
var service := _rank_three_service()
service.update_metrics({
&"reputation":45,
&"asset_gold":50_000,
&"active_above_ground_facilities":1,
&"active_underground_facilities":1,
})
service.request_rank_up(&"history_rank_4", 4)
service.update_metrics({&"faith_lifetime":500})
service.complete_quest(&"important_rank_5")
service.request_rank_up(&"history_rank_5", 5)
return service
func _requirement_ids(report: Dictionary) -> Array[StringName]:
var result: Array[StringName] = []
for requirement: Dictionary in report[&"requirements"]:
result.append(StringName(requirement[&"id"]))
return result
func _expect(condition: bool, label: String) -> void:
_checks += 1
if condition:
print(" ✓ %s" % label)
return
_failures += 1
push_error(" ✗ %s" % label)
@@ -0,0 +1 @@
uid://dt1eshc6c6rrf
@@ -0,0 +1,500 @@
extends SceneTree
class DenyingOperationsCoordinator:
extends OperationsCoordinator
func can_start_construction_or_investment() -> Dictionary:
return {
&"allowed": false,
&"reason": &"ARREARS_REMAIN",
&"arrears_total": 25,
&"consecutive_insolvency_count": 1,
}
var _checks := 0
var _failures := 0
var _clock: Variant
var _state: Variant
var _runtime: Variant
var _direct_command_id := 900_000
func _initialize() -> void:
call_deferred("_run")
func _run() -> void:
print("MVP: Runtime content, hiring, and progression integration")
_test_catalog_bootstrap_and_queries()
_test_progression_resource_and_guest_facts()
_test_catalog_staff_hire_transaction()
_test_rank_two_three_and_atomic_failure()
_test_v3_migration_and_v4_tamper_guards()
_test_twenty_four_hour_replay()
_cleanup()
if _failures == 0:
print("PASS: %d checks" % _checks)
else:
push_error("FAIL: %d of %d checks failed" % [_failures, _checks])
quit(_failures)
func _reset_runtime(seed := 82_001) -> void:
_cleanup()
_clock = load("res://autoload/game_clock.gd").new()
_state = load("res://autoload/game_state.gd").new()
_runtime = load("res://autoload/simulation_runtime.gd").new()
_clock.reset(1, 6, 0)
_state.reset()
var report: Dictionary = _runtime.initialize(_clock, _state, seed)
_clock.set_paused(true)
_expect(bool(report.get(&"valid", false)), "Runtime initializes with validated content and Progression")
func _cleanup() -> void:
if _runtime != null:
_runtime.free()
if _clock != null:
_clock.free()
if _state != null:
_state.free()
_runtime = null
_clock = null
_state = null
func _test_catalog_bootstrap_and_queries() -> void:
_reset_runtime(82_001)
var report: Dictionary = _runtime.get_mvp_content_report()
_expect(bool(report[&"valid"]), "MVP catalog report is valid")
_expect(int(report[&"counts"]["guest_classes"]) == 8, "catalog exposes eight Guest classes")
_expect(int(report[&"counts"]["species"]) == 4, "catalog exposes four independent species")
_expect(int(report[&"counts"]["staff_candidates"]) == 20, "catalog exposes twenty fixed Staff candidates")
_expect(int(report[&"counts"]["unique_characters"]) == 2, "catalog exposes both unique characters")
var catalog_ids: Array[StringName] = _runtime.get_mvp_guest_definition_ids()
_expect(catalog_ids.size() == 32, "Runtime registers every class/species Guest definition")
_expect(_runtime.guest_service.get_definitions().size() == 35, "legacy three Guest definitions coexist with the 32 catalog definitions")
var sorted_ids := catalog_ids.duplicate()
sorted_ids.sort_custom(func(left: StringName, right: StringName) -> bool: return String(left) < String(right))
_expect(catalog_ids == sorted_ids, "Runtime catalog Guest IDs use stable canonical order")
var all_registered := true
for guest_type_id: StringName in catalog_ids:
if _runtime.guest_service.get_definition(guest_type_id) == null:
all_registered = false
break
_expect(all_registered, "all 32 catalog Guest definitions are queryable by stable ID")
report[&"counts"]["guest_classes"] = 0
_expect(int(_runtime.get_mvp_content_report()[&"counts"]["guest_classes"]) == 8, "content report query is a defensive copy")
var candidate: Dictionary = _runtime.get_mvp_staff_candidate_payload(&"staff_akane")
(candidate[&"options"][&"skills"] as Dictionary)[&"service"] = -1
_expect(
int(_runtime.get_mvp_staff_candidate_payload(&"staff_akane")[&"options"][&"skills"][&"service"]) == 68,
"Staff candidate payload query is a defensive copy"
)
var progression_report: Dictionary = _runtime.get_progression_report()
(progression_report[&"snapshot"][&"metrics"] as Dictionary)["asset_gold"] = 0
_expect(int(_runtime.progression_service.metrics[&"asset_gold"]) == 5_000, "Progression report query is a defensive copy")
func _test_progression_resource_and_guest_facts() -> void:
_reset_runtime(82_002)
var faith: CommandResult = _runtime.submit_command(
&"change_resources", {&"deltas": {&"faith": 40}},
&"player", &"", 50, -1, &"mvp.progression.faith"
)
_expect(faith.is_success(), "positive Faith resource change succeeds")
_expect(_state.get_resource(&"faith") == 40, "positive Faith changes live GameState")
_expect(int(_runtime.progression_service.metrics[&"faith_lifetime"]) == 40, "positive Faith increments lifetime progression atomically")
var spawned: CommandResult = _runtime.submit_command(
&"spawn_guest",
{
&"guest_id": &"catalog_guest_one",
&"guest_type_id": _runtime.get_mvp_guest_definition_ids()[0],
&"gold": 50,
&"options": {&"current_cell": Vector3i.ZERO},
},
&"world", &"catalog_guest_one", 40, -1, &"mvp.progression.guest_one"
)
_expect(spawned.is_success(), "catalog Guest can spawn through Runtime")
_expect(int(_runtime.progression_service.metrics[&"guest_lifetime"]) == 1, "successful Guest spawn increments lifetime Guest count once")
var duplicate: CommandResult = _runtime.submit_command(
&"spawn_guest",
{
&"guest_id": &"catalog_guest_one",
&"guest_type_id": _runtime.get_mvp_guest_definition_ids()[0],
&"options": {&"current_cell": Vector3i.ZERO},
},
&"world", &"catalog_guest_one", 40, -1, &"mvp.progression.guest_duplicate"
)
_expect(not duplicate.is_success(), "duplicate Guest spawn is rejected")
_expect(int(_runtime.progression_service.metrics[&"guest_lifetime"]) == 1, "failed Guest spawn cannot increment lifetime progression")
_expect(
_runtime.progression_service.update_metrics({&"faith_lifetime": ProgressionService.MAX_SAFE_INTEGER}),
"overflow rollback fixture reaches the maximum lifetime Faith"
)
var state_before: Dictionary = _state.create_snapshot()
var economy_before: Dictionary = _runtime.economy_service.create_snapshot()
var progression_before: Dictionary = _runtime.progression_service.create_snapshot()
var overflow: CommandResult = _runtime._execute_change_resources(_direct_command(
&"change_resources", {&"deltas": {&"faith": 1}}
))
_expect(not overflow.is_success(), "lifetime Faith overflow rejects the resource command")
_expect(_state.create_snapshot() == state_before, "failed lifetime update restores GameState exactly")
_expect(_runtime.economy_service.create_snapshot() == economy_before, "failed lifetime update restores Economy exactly")
_expect(_runtime.progression_service.create_snapshot() == progression_before, "failed lifetime update restores Progression exactly")
func _test_catalog_staff_hire_transaction() -> void:
_reset_runtime(82_003)
var bypass: CommandResult = _runtime.submit_command(
&"register_staff",
{
&"staff_id": &"staff_akane",
&"role_id": &"guard",
&"options": {&"current_cell": Vector3i.ZERO},
},
&"player", &"staff_akane", 80, -1, &"mvp.hire.bypass"
)
_expect(not bypass.is_success(), "generic Staff registration cannot bypass a reserved catalog candidate")
var cash_before: int = _runtime.economy_service.get_balance(_runtime.PLAYER_CASH_ACCOUNT)
var hired: CommandResult = _runtime.submit_command(
&"hire_staff_candidate",
{&"candidate_id": &"staff_akane", &"current_cell": Vector3i.ZERO},
&"player", &"staff_akane", 80, -1, &"mvp.hire.akane"
)
_expect(hired.is_success(), "validated Staff candidate hires atomically")
var staff: StaffRuntime = _runtime.staff_service.get_staff(&"staff_akane")
var shift: ShiftInstance = _runtime.staff_service.get_shift(StringName(hired.details[&"shift_id"]))
_expect(staff != null and staff.role_id == &"receptionist", "hire uses the catalog role as the sole source of truth")
_expect(staff != null and staff.daily_wage == 120, "FREE_HIRE retains its catalog daily wage")
_expect(staff != null and staff.get_employment_kind_name() == &"FREE_HIRE", "ordinary catalog hire records explicit FREE_HIRE employment")
_expect(staff != null and int(staff.skills[&"service"]) == 68, "hire copies catalog skills without caller inference")
_expect(staff != null and staff.permitted_task_tags == [&"reception", &"service"], "hire copies canonical permitted Task tags")
_expect(shift != null and shift.template_id == &"DAY", "hire creates the candidate's preferred Shift")
_expect(_runtime.staff_service.get_daily_payroll_total() == 120, "hired candidate participates in deterministic daily payroll")
_expect(
_runtime.economy_service.get_balance(_runtime.PLAYER_CASH_ACCOUNT) == cash_before - 180,
"signing cost is paid exactly once through the Gold ledger"
)
_expect(_state.get_resource(&"gold") == cash_before - 180, "ledger signing cost synchronizes GameState Gold")
_expect(_runtime.path_service.has_actor_projection(&"staff:staff_akane"), "hired Staff is projected into Path traffic")
_expect(_runtime.get_hired_staff_candidate_ids() == [&"staff_akane"], "Runtime records the candidate as permanently hired")
var saw_hire_event := false
for event: SimulationDomainEvent in _runtime.domain_events.recent_events():
if event.event_type == &"STAFF_CANDIDATE_HIRED" and &"staff_akane" in event.entity_ids:
saw_hire_event = true
_expect(saw_hire_event, "successful hire publishes a structured DomainEvent")
var replay_cash: int = _runtime.economy_service.get_balance(_runtime.PLAYER_CASH_ACCOUNT)
var replay: CommandResult = _runtime.submit_command(
&"hire_staff_candidate",
{&"candidate_id": &"staff_akane", &"current_cell": Vector3i.ZERO},
&"player", &"staff_akane", 80, -1, &"mvp.hire.akane"
)
_expect(not replay.is_success() and replay.code == CommandResult.Code.DUPLICATE_IDEMPOTENCY_KEY, "resent hire command is recognized by its idempotency key")
_expect(_runtime.economy_service.get_balance(_runtime.PLAYER_CASH_ACCOUNT) == replay_cash, "resent hire never charges the signing cost twice")
var duplicate: CommandResult = _runtime.submit_command(
&"hire_staff_candidate",
{&"candidate_id": &"staff_akane", &"current_cell": Vector3i.ZERO},
&"player", &"staff_akane", 80, -1, &"mvp.hire.akane.other"
)
_expect(not duplicate.is_success(), "same candidate cannot be hired under a different request")
_expect(_runtime.economy_service.get_balance(_runtime.PLAYER_CASH_ACCOUNT) == replay_cash, "duplicate-candidate rejection spends no Gold")
var original_coordinator: Variant = _runtime.operations_coordinator
var original_operations_enabled: bool = bool(_runtime._operations_enabled)
var staff_before_gate: Dictionary = _runtime.staff_service.create_snapshot()
var economy_before_gate: Dictionary = _runtime.economy_service.create_snapshot()
var hired_before_gate: Array[StringName] = _runtime.get_hired_staff_candidate_ids()
_runtime.operations_coordinator = DenyingOperationsCoordinator.new()
_runtime._operations_enabled = true
var gated: CommandResult = _runtime._execute_hire_staff_candidate(_direct_command(
&"hire_staff_candidate",
{&"candidate_id": &"staff_tessa", &"current_cell": Vector3i.ZERO}
))
_runtime.operations_coordinator = original_coordinator
_runtime._operations_enabled = original_operations_enabled
_expect(
not gated.is_success()
and StringName(gated.details.get(&"reason", &"")) == &"ARREARS_REMAIN",
"Operations arrears gate rejects a new wage-bearing Staff contract"
)
_expect(
_runtime.staff_service.create_snapshot() == staff_before_gate
and _runtime.economy_service.create_snapshot() == economy_before_gate
and _runtime.get_hired_staff_candidate_ids() == hired_before_gate,
"rejected Operations-gated hire spends nothing and mutates no Staff history"
)
var staff_before: Dictionary = _runtime.staff_service.create_snapshot()
var economy_before: Dictionary = _runtime.economy_service.create_snapshot()
var game_state_before: Dictionary = _state.create_snapshot()
var path_before: Dictionary = _runtime.path_service.create_snapshot()
var credits_before: Dictionary = _runtime._movement_credits.duplicate(true)
var events_before: Dictionary = _runtime.domain_events.create_snapshot()
var ids_before: Dictionary = _runtime.id_generator.create_snapshot()
var hired_ids_before: Array[StringName] = _runtime.get_hired_staff_candidate_ids()
var callback := Callable(_runtime, "_on_staff_registered")
_runtime.staff_service.staff_registered.disconnect(callback)
var forced_failure: CommandResult = _runtime._execute_hire_staff_candidate(_direct_command(
&"hire_staff_candidate",
{&"candidate_id": &"staff_bardo", &"current_cell": Vector3i.ZERO}
))
_runtime.staff_service.staff_registered.connect(callback)
_expect(not forced_failure.is_success(), "missing Path projection forces a post-payment hire failure")
_expect(_runtime.staff_service.create_snapshot() == staff_before, "failed hire restores Staff and Shift state exactly")
_expect(_runtime.economy_service.create_snapshot() == economy_before, "failed hire restores the ledger exactly")
_expect(_state.create_snapshot() == game_state_before, "failed hire restores GameState exactly")
_expect(_runtime.path_service.create_snapshot() == path_before, "failed hire restores Path exactly")
_expect(_runtime._movement_credits == credits_before, "failed hire restores movement credits exactly")
_expect(_runtime.domain_events.create_snapshot() == events_before, "failed hire restores DomainEvents exactly")
_expect(_runtime.id_generator.create_snapshot() == ids_before, "failed hire restores stable ID counters exactly")
_expect(_runtime.get_hired_staff_candidate_ids() == hired_ids_before, "failed hire leaves candidate history unchanged")
var canonical_save: Dictionary = _runtime.create_snapshot()
var wage_tamper: Dictionary = canonical_save.duplicate(true)
for raw_staff: Dictionary in wage_tamper[&"systems"][&"runtime"][&"staff"][&"staff"]:
if String(raw_staff[&"staff_id"]) == "staff_akane":
raw_staff[&"daily_wage"] = 0
break
var live_before: Dictionary = _runtime.create_snapshot()
_expect(not _runtime.restore_snapshot(wage_tamper), "v4 restore rejects a hired candidate whose catalog wage was altered")
_expect(_runtime.create_snapshot() == live_before, "catalog Staff crosslink rejection is atomic")
func _test_rank_two_three_and_atomic_failure() -> void:
_reset_runtime(82_004)
var rank_before: Dictionary = _runtime.progression_service.create_snapshot()
var world_before: Dictionary = _runtime.world_service.create_snapshot()
var unmet: CommandResult = _runtime._execute_set_world_tree_rank(_direct_command(
&"set_world_tree_rank", {&"rank": 2, &"request_id": &"rank_two_unmet"}
))
_expect(not unmet.is_success(), "Rank 2 rejects unmet reputation and asset requirements")
_expect(_runtime.progression_service.create_snapshot() == rank_before, "failed eligibility check leaves Progression exact")
_expect(_runtime.world_service.create_snapshot() == world_before, "failed eligibility check leaves World exact")
_expect(_runtime._execute_update_progression_metrics(_direct_command(
&"update_progression_metrics", {&"metrics": {&"reputation": 25}}
)).is_success(), "Rank 2 reputation fact updates")
_expect(_runtime._execute_change_resources(_direct_command(
&"change_resources", {&"deltas": {&"gold": 5_000}}
)).is_success(), "Rank 2 asset threshold is funded")
var promoted_two: CommandResult = _runtime._execute_set_world_tree_rank(_direct_command(
&"set_world_tree_rank", {&"rank": 2, &"request_id": &"rank_two"}
))
_expect(promoted_two.is_success(), "eligible Rank 2 promotion succeeds")
_expect(_runtime.progression_service.current_rank == 2 and _state.world_tree_rank == 2, "Progression and GameState commit Rank 2 together")
_expect(_runtime.world_service.get_bounds() == ProgressionService.RANK_BOUNDS[2], "Rank 2 commits canonical World bounds")
var replay_before: Dictionary = _runtime.create_snapshot()
var replay: CommandResult = _runtime._execute_set_world_tree_rank(_direct_command(
&"set_world_tree_rank", {&"rank": 2, &"request_id": &"rank_two"}
))
_expect(replay.is_success() and replay.is_replay, "same rank request ID replays the original success")
_expect(_runtime.create_snapshot() == replay_before, "rank request replay is completely read-only")
var skip: CommandResult = _runtime._execute_set_world_tree_rank(_direct_command(
&"set_world_tree_rank", {&"rank": 4, &"request_id": &"rank_four_skip"}
))
_expect(not skip.is_success() and _runtime.progression_service.current_rank == 2, "rank progression cannot skip Rank 3")
var rank_three_guests_ok := true
var rank_guest_definition: StringName = _runtime.get_mvp_guest_definition_ids()[0]
for index in range(300):
var guest_id := StringName("rank_three_guest_%03d" % index)
var spawned: CommandResult = _runtime._execute_spawn_guest(_direct_command(
&"spawn_guest",
{
&"guest_id": guest_id,
&"guest_type_id": rank_guest_definition,
&"gold": 0,
&"options": {
&"current_cell": Vector3i.ZERO,
&"planned_exit_at": 10_000,
},
}
))
rank_three_guests_ok = rank_three_guests_ok and spawned.is_success()
var lifetime_updated: CommandResult = _runtime._execute_update_progression_metrics(_direct_command(
&"update_progression_metrics",
{&"metrics": {&"faith_lifetime": 100}}
))
_expect(
rank_three_guests_ok and lifetime_updated.is_success() \
and int(_runtime.progression_service.metrics[&"guest_lifetime"]) == 300,
"Rank 3 lifetime facts derive from 300 authoritative Guest records"
)
var promoted_three: CommandResult = _runtime._execute_set_world_tree_rank(_direct_command(
&"set_world_tree_rank", {&"rank": 3, &"request_id": &"rank_three"}
))
_expect(promoted_three.is_success(), "eligible Rank 3 promotion succeeds")
_expect(_runtime.progression_service.current_rank == 3 and _state.world_tree_rank == 3, "Progression and GameState commit Rank 3 together")
var saw_rank_event := false
for event: SimulationDomainEvent in _runtime.domain_events.recent_events():
if event.event_type == &"WORLD_TREE_RANK_ADVANCED" and int(event.payload.get(&"rank", 0)) == 3:
saw_rank_event = true
_expect(saw_rank_event, "successful promotion publishes WORLD_TREE_RANK_ADVANCED")
_reset_runtime(82_005)
_runtime.progression_service.update_metrics({&"reputation": 25})
_runtime._execute_change_resources(_direct_command(&"change_resources", {&"deltas": {&"gold": 5_000}}))
_expect(_runtime._expand_world_to_rank(3), "rank apply-failure fixture expands World and Path coherently")
var atomic_before := {
&"progression": _runtime.progression_service.create_snapshot(),
&"world": _runtime.world_service.create_snapshot(),
&"path": _runtime.path_service.create_snapshot(),
&"state": _state.create_snapshot(),
&"credits": _runtime._movement_credits.duplicate(true),
&"events": _runtime.domain_events.create_snapshot(),
&"ids": _runtime.id_generator.create_snapshot(),
}
var apply_failure: CommandResult = _runtime._execute_set_world_tree_rank(_direct_command(
&"set_world_tree_rank", {&"rank": 2, &"request_id": &"rank_two_apply_failure"}
))
_expect(not apply_failure.is_success(), "World/Path apply failure rejects promotion")
_expect(_runtime.progression_service.create_snapshot() == atomic_before[&"progression"], "failed rank apply restores Progression exactly")
_expect(_runtime.world_service.create_snapshot() == atomic_before[&"world"], "failed rank apply restores World exactly")
_expect(_runtime.path_service.create_snapshot() == atomic_before[&"path"], "failed rank apply restores Path exactly")
_expect(_state.create_snapshot() == atomic_before[&"state"], "failed rank apply restores GameState exactly")
_expect(_runtime._movement_credits == atomic_before[&"credits"], "failed rank apply restores movement credits exactly")
_expect(_runtime.domain_events.create_snapshot() == atomic_before[&"events"], "failed rank apply restores DomainEvents exactly")
_expect(_runtime.id_generator.create_snapshot() == atomic_before[&"ids"], "failed rank apply restores stable IDs exactly")
func _test_v3_migration_and_v4_tamper_guards() -> void:
_reset_runtime(82_006)
_runtime._execute_change_resources(_direct_command(&"change_resources", {&"deltas": {&"faith": 12}}))
for index in range(2):
var guest_id := StringName("legacy_guest_%d" % index)
var spawned: CommandResult = _runtime._execute_spawn_guest(_direct_command(
&"spawn_guest",
{
&"guest_id": guest_id,
&"guest_type_id": _runtime.get_mvp_guest_definition_ids()[index],
&"gold": 0,
&"options": {&"current_cell": Vector3i.ZERO},
}
))
_expect(spawned.is_success(), "legacy migration fixture Guest %d spawns" % index)
_runtime.guest_service.despawn_guest(&"legacy_guest_1", _clock.absolute_minute)
var v3: Dictionary = _runtime.create_snapshot()
var v3_runtime: Dictionary = v3[&"systems"][&"runtime"]
v3_runtime[&"runtime_schema_version"] = 3
v3_runtime.erase(&"operations")
v3_runtime.erase(&"catalog_manifest")
v3_runtime.erase(&"progression")
v3_runtime.erase(&"hired_staff_candidate_ids")
for infrastructure_key: StringName in [
&"excavation", &"spatial_subslots", &"route_projection",
&"facility_infrastructure",
]:
v3_runtime.erase(infrastructure_key)
_expect(_runtime.restore_snapshot(v3), "Runtime v3 Rank 1 snapshot migrates without guessed promotion evidence")
_expect(_runtime.progression_service.current_rank == 1, "v3 migration creates fresh Rank 1 Progression")
_expect(int(_runtime.progression_service.metrics[&"asset_gold"]) == _runtime.economy_service.get_balance(_runtime.PLAYER_CASH_ACCOUNT), "v3 migration derives asset Gold from the authoritative ledger")
_expect(int(_runtime.progression_service.metrics[&"faith_lifetime"]) == 12, "v3 migration derives lifetime Faith from current Faith")
_expect(int(_runtime.progression_service.metrics[&"guest_lifetime"]) == 2, "v3 migration derives lifetime Guests from active plus persistent records")
_expect(_runtime.get_mvp_guest_definition_ids().size() == 32, "v3 migration registers the current 32 catalog definitions")
var migrated: Dictionary = _runtime.create_snapshot()
_expect(int(migrated[&"systems"][&"runtime"][&"runtime_schema_version"]) == 6, "v3 migration emits canonical Runtime schema v6")
var digest := SimulationDigest.compute(migrated)
_expect(_runtime.restore_snapshot(migrated), "canonical v5 snapshot restores")
_expect(SimulationDigest.compute(_runtime.create_snapshot()) == digest, "canonical v5 restore is exact")
var progressed_legacy: Dictionary = v3.duplicate(true)
progressed_legacy[&"game_state"][&"world_tree_rank"] = 2
var live_before: Dictionary = _runtime.create_snapshot()
_expect(not _runtime.restore_snapshot(progressed_legacy), "v3 Rank 2 snapshot is rejected because promotion evidence is absent")
_expect(_runtime.create_snapshot() == live_before, "rejected progressed v3 migration is atomic")
var corruptions: Array[Dictionary] = []
var manifest: Dictionary = migrated.duplicate(true)
manifest[&"systems"][&"runtime"][&"catalog_manifest"][&"guest_definition_ids"][0] = "tampered_guest"
corruptions.append({&"label": "catalog manifest", &"snapshot": manifest})
var definition: Dictionary = migrated.duplicate(true)
var catalog_id := String(_runtime.get_mvp_guest_definition_ids()[0])
for raw_definition: Dictionary in definition[&"systems"][&"runtime"][&"guests"][&"definitions"]:
if String(raw_definition[&"guest_type_id"]) == catalog_id:
raw_definition[&"wealth_min"] = int(raw_definition[&"wealth_min"]) + 1
break
corruptions.append({&"label": "catalog Guest definition", &"snapshot": definition})
var progression: Dictionary = migrated.duplicate(true)
progression[&"systems"][&"runtime"][&"progression"][&"current_rank"] = 2
corruptions.append({&"label": "Progression history", &"snapshot": progression})
var rank: Dictionary = migrated.duplicate(true)
rank[&"game_state"][&"world_tree_rank"] = 2
corruptions.append({&"label": "GameState rank", &"snapshot": rank})
var bounds: Dictionary = migrated.duplicate(true)
bounds[&"systems"][&"runtime"][&"world"][&"bounds"][&"max_x"] = 13
corruptions.append({&"label": "World rank bounds", &"snapshot": bounds})
var hired_manifest: Dictionary = migrated.duplicate(true)
hired_manifest[&"systems"][&"runtime"][&"hired_staff_candidate_ids"] = ["not_a_candidate"]
corruptions.append({&"label": "hired candidate manifest", &"snapshot": hired_manifest})
for corruption: Dictionary in corruptions:
live_before = _runtime.create_snapshot()
_expect(
not _runtime.restore_snapshot(corruption[&"snapshot"])
and _runtime.create_snapshot() == live_before,
"%s tamper is rejected atomically" % corruption[&"label"]
)
func _test_twenty_four_hour_replay() -> void:
_reset_runtime(82_007)
var spawn: CommandResult = _runtime._execute_spawn_guest(_direct_command(
&"spawn_guest",
{
&"guest_id": &"replay_guest",
&"guest_type_id": _runtime.get_mvp_guest_definition_ids()[0],
&"gold": 100,
&"options": {&"current_cell": Vector3i.ZERO, &"planned_exit_at": _clock.absolute_minute + 2_000},
}
))
var hire: CommandResult = _runtime._execute_hire_staff_candidate(_direct_command(
&"hire_staff_candidate", {&"candidate_id": &"staff_akane", &"current_cell": Vector3i.ZERO}
))
_expect(spawn.is_success() and hire.is_success(), "24-hour replay fixture creates catalog Guest and Staff")
var start: Dictionary = _runtime.create_snapshot()
var second_clock = load("res://autoload/game_clock.gd").new()
var second_state = load("res://autoload/game_state.gd").new()
var second_runtime = load("res://autoload/simulation_runtime.gd").new()
second_clock.reset(1, 6, 0)
second_state.reset()
second_runtime.initialize(second_clock, second_state, 82_007)
second_clock.set_paused(true)
_expect(second_runtime.restore_snapshot(start), "24-hour replay clone restores exact v4 start")
_clock.set_paused(false)
second_clock.set_paused(false)
_clock.advance_minutes(1_440)
second_clock.advance_minutes(1_440)
_expect(
SimulationDigest.compute(_runtime.create_snapshot()) \
== SimulationDigest.compute(second_runtime.create_snapshot()),
"catalog, Staff hire, Progression, and six-Need state replay exactly for 24 hours"
)
second_runtime.free()
second_clock.free()
second_state.free()
func _direct_command(command_type: StringName, payload: Dictionary) -> SimulationCommand:
_direct_command_id += 1
return SimulationCommand.new(
_direct_command_id,
_clock.absolute_minute,
_clock.absolute_minute,
100,
command_type,
&"test",
&"",
payload,
&""
)
func _expect(condition: bool, label: String) -> void:
_checks += 1
if condition:
print(" ✓ %s" % label)
return
_failures += 1
push_error(" ✗ %s" % label)
@@ -0,0 +1 @@
uid://b46fufnam07fa
@@ -0,0 +1,484 @@
extends SceneTree
const FOURTEEN_DAYS_MINUTES := 14 * SimulationKernel.MINUTES_PER_DAY
const RUNTIME_SNAPSHOT_VERSION := 6
var _checks := 0
var _failures := 0
func _initialize() -> void:
call_deferred("_run")
func _run() -> void:
print("MVP: production Operations acceptance")
_test_waiting_choice_and_command_exactly_once()
_test_twenty_four_hour_actual_tick_smoke()
_test_full_partial_payroll_bankruptcy_and_v6_tamper()
_test_fourteen_day_boundary_snapshot_determinism()
await process_frame
if _failures == 0:
print("PASS: %d checks" % _checks)
else:
push_error("FAIL: %d of %d checks failed" % [_failures, _checks])
quit(_failures)
func _new_fixture(seed: int, enable_operations := true) -> Dictionary:
var clock: Variant = load("res://autoload/game_clock.gd").new()
var state: Variant = load("res://autoload/game_state.gd").new()
var runtime: Variant = load("res://autoload/simulation_runtime.gd").new()
clock.reset(1, 6, 0)
state.reset()
var initialized: Dictionary = runtime.initialize(clock, state, seed)
clock.set_paused(true)
_expect(bool(initialized.get(&"valid", false)), "fixture Runtime initializes")
var initial_snapshot: Dictionary = runtime.create_snapshot()
_expect(
int(initial_snapshot[&"systems"][&"runtime"].get(
&"runtime_schema_version", -1
)) == RUNTIME_SNAPSHOT_VERSION,
"fixture emits Runtime snapshot schema v6"
)
if enable_operations:
var enabled: Dictionary = runtime.enable_production_operations()
_expect(bool(enabled.get(&"ok", false)), "fixture enables production Operations")
return {&"clock": clock, &"state": state, &"runtime": runtime}
func _free_fixture(fixture: Dictionary) -> void:
var runtime: Variant = fixture.get(&"runtime")
var clock: Variant = fixture.get(&"clock")
var state: Variant = fixture.get(&"state")
if runtime != null:
runtime.free()
if clock != null:
clock.free()
if state != null:
state.free()
# Long acceptance slices deliberately exercise the production Operations
# adapter without claiming to execute unrelated actor AI on all 20,160 minutes.
# Aggregate clocks are aligned solely to preserve the Runtime transaction
# precondition; the separate 24-hour smoke above covers the actual Tick pipeline.
# One final rich snapshot (and the midpoint restore) is the Operations oracle.
func _advance_operations_boundary_slice(fixture: Dictionary, amount: int) -> int:
var clock: Variant = fixture[&"clock"]
var runtime: Variant = fixture[&"runtime"]
var advanced := 0
while advanced < amount:
if runtime.operations_coordinator != null \
and runtime.operations_coordinator.is_terminal():
break
# Operations has no recurring boundary finer than 30 minutes. The
# coordinator still performs its own unthinned internal minute traversal;
# unrelated Runtime AI is intentionally outside this accelerated oracle.
var step := mini(30, amount - advanced)
var target: int = clock.absolute_minute + step
clock.absolute_minute = target
clock.call("_sync_calendar_fields")
clock.snapshot_version += step
runtime.call("_advance_operations_time", target)
if bool(runtime.get_operations_report().get(&"runtime_failure_latched", false)):
break
if runtime.operations_coordinator.is_terminal():
# A terminal daily close commits TIME_BOUNDARIES and deliberately skips
# the Runtime suffix; its other aggregates therefore remain at X - 1.
_align_operations_fixture_clocks(runtime, target - 1)
advanced += step
break
# These acceptance fixtures contain no Facilities, logistics, raids, or
# Staff tasks. Their clocks are aligned for Operations cross-link checks;
# their skipped actor/Shift behavior is explicitly outside this fast oracle.
_align_operations_fixture_clocks(runtime, target)
advanced += step
return advanced
func _align_operations_fixture_clocks(runtime: Variant, minute: int) -> void:
runtime.inventory_service.current_minute = minute
runtime.facility_service.current_minute = minute
runtime.guest_service.current_minute = minute
runtime.staff_service.current_minute = minute
runtime.authority_service.current_minute = minute
runtime.defense_service.current_minute = minute
runtime.construction_service.current_minute = minute
runtime.excavation_service.current_minute = minute
func _operations_slice_is_healthy(fixture: Dictionary) -> bool:
var runtime: Variant = fixture[&"runtime"]
var report: Dictionary = runtime.get_operations_report()
var foreground: EventInstance = runtime.event_service.get_foreground_instance()
return not bool(report.get(&"runtime_failure_latched", true)) \
and int(report.get(&"deferred_runtime_minute", -2)) == -1 \
and not runtime.operations_coordinator.pause_requested \
and not runtime.operations_coordinator.is_terminal() \
and not runtime.operations_coordinator.is_external_quarantined() \
and (foreground == null or foreground.state != EventInstance.State.WAITING_CHOICE)
func _test_waiting_choice_and_command_exactly_once() -> void:
var fixture := _new_fixture(73_001)
var runtime: Variant = fixture[&"runtime"]
var clock: Variant = fixture[&"clock"]
var state: Variant = fixture[&"state"]
var queued: CommandResult = runtime.event_service.enqueue_candidate(
&"relationship.threshold",
[],
clock.absolute_minute,
{&"deduplication_key": &"operations-acceptance-choice"}
)
var started: CommandResult = runtime.event_service.start_next_event(
clock.absolute_minute, {}
)
_expect(
queued.is_success() and started.is_success()
and bool(started.details.get(&"waiting_for_choice", false)),
"foreground Event enters WAITING_CHOICE"
)
clock.set_paused(false)
var before_choice_minute: int = clock.absolute_minute
clock.advance_minutes(5)
var foreground: EventInstance = runtime.event_service.get_foreground_instance()
_expect(
clock.paused and clock.absolute_minute == before_choice_minute + 1,
"WAITING_CHOICE pauses at the first committed minute and stops catch-up"
)
_expect(
foreground != null and foreground.state == EventInstance.State.WAITING_CHOICE,
"paused foreground ownership remains intact"
)
if foreground != null:
var choice_result: CommandResult = runtime.submit_command(
&"submit_event_choice",
{
&"event_instance_id": foreground.event_instance_id,
&"choice_instance_id": foreground.choice_instance_id,
&"choice_id": &"acknowledge",
},
&"player",
foreground.event_instance_id,
100,
-1,
&"operations.acceptance.choice"
)
_expect(
choice_result.is_success()
and runtime.event_service.get_foreground_instance() == null
and not clock.paused,
"submitting the stable Choice token resolves the Event and resumes time"
)
clock.set_paused(true)
var gold_before: int = state.get_resource(&"gold")
var first: CommandResult = runtime.submit_command(
&"change_resources",
{&"deltas": {&"gold": 17}},
&"player",
&"",
50,
-1,
&"operations.acceptance.exactly-once"
)
var gold_after_first: int = state.get_resource(&"gold")
var retry: CommandResult = runtime.submit_command(
&"change_resources",
{&"deltas": {&"gold": 17}},
&"player",
&"",
50,
-1,
&"operations.acceptance.exactly-once"
)
_expect(
first.is_success() and gold_after_first == gold_before + 17,
"first public command commits its resource delta once"
)
_expect(
not retry.is_success()
and retry.code == CommandResult.Code.DUPLICATE_IDEMPOTENCY_KEY
and state.get_resource(&"gold") == gold_after_first,
"transport retry with the same idempotency key cannot apply twice"
)
var command_snapshot: Dictionary = runtime.create_snapshot()
_expect(
runtime.restore_snapshot(command_snapshot),
"command history survives an exact v6 snapshot round trip"
)
var restored_retry: CommandResult = runtime.submit_command(
&"change_resources",
{&"deltas": {&"gold": 17}},
&"player",
&"",
50,
-1,
&"operations.acceptance.exactly-once"
)
_expect(
not restored_retry.is_success()
and restored_retry.code == CommandResult.Code.DUPLICATE_IDEMPOTENCY_KEY
and state.get_resource(&"gold") == gold_after_first,
"restored idempotency history still rejects the same command"
)
_free_fixture(fixture)
func _test_twenty_four_hour_actual_tick_smoke() -> void:
var fixture := _new_fixture(73_004)
var runtime: Variant = fixture[&"runtime"]
var clock: Variant = fixture[&"clock"]
var start_minute: int = clock.absolute_minute
clock.advance_minutes(SimulationKernel.MINUTES_PER_DAY)
_expect(
clock.absolute_minute - start_minute == SimulationKernel.MINUTES_PER_DAY,
"actual SimulationKernel ticks complete an uninterrupted 24-hour smoke slice"
)
_expect(
_operations_slice_is_healthy(fixture)
and runtime.operations_coordinator.current_minute == clock.absolute_minute
and runtime.operations_coordinator.get_day_summaries().size() == 1,
"24-hour actual-tick slice has no Choice, deferred minute, quarantine, or terminal stop"
)
var snapshot: Dictionary = runtime.create_snapshot()
_expect(
runtime.restore_snapshot(snapshot)
and runtime.latest_digest == SimulationDigest.compute(snapshot),
"24-hour actual-tick v6 snapshot round trip is exact"
)
_free_fixture(fixture)
func _test_full_partial_payroll_bankruptcy_and_v6_tamper() -> void:
var fixture := _new_fixture(73_002, false)
var runtime: Variant = fixture[&"runtime"]
var clock: Variant = fixture[&"clock"]
var state: Variant = fixture[&"state"]
var candidate: Dictionary = runtime.get_mvp_staff_candidate_payload(&"staff_akane")
var daily_wage := int(candidate[&"options"][&"daily_wage"])
var hired: CommandResult = runtime.submit_command(
&"hire_staff_candidate",
{&"candidate_id": &"staff_akane", &"current_cell": Vector3i.ZERO},
&"player",
&"staff_akane",
80,
-1,
&"operations.acceptance.hire"
)
_expect(hired.is_success() and daily_wage > 50, "payroll fixture hires one wage-bearing Staff member")
var enabled: Dictionary = runtime.enable_production_operations()
_expect(bool(enabled.get(&"ok", false)), "payroll fixture enables Operations after the pre-opening hire")
var full_advanced := _advance_operations_boundary_slice(
fixture, SimulationKernel.MINUTES_PER_DAY
)
var full: Dictionary = runtime.staff_service.get_payroll_settlement(&"payroll-day-00000001")
_expect(
full_advanced == SimulationKernel.MINUTES_PER_DAY
and _operations_slice_is_healthy(fixture)
and clock.absolute_minute == SimulationKernel.MINUTES_PER_DAY + 6 * 60
and int(full.get(&"new_wages_total", -1)) == daily_wage
and int(full.get(&"paid_total", -1)) == daily_wage
and int(full.get(&"unpaid_after_total", -1)) == 0,
"Day 1 payroll is paid in full and closes at exactly 06:00"
)
var cash_now: int = runtime.economy_service.get_balance(runtime.PLAYER_CASH_ACCOUNT)
var drained: CommandResult = runtime.submit_command(
&"change_resources",
{&"deltas": {&"gold": -(cash_now - 50)}},
&"player",
&"",
90,
-1,
&"operations.acceptance.cash-drain"
)
_expect(
drained.is_success()
and runtime.economy_service.get_balance(runtime.PLAYER_CASH_ACCOUNT) == 50,
"fixture leaves exactly 50 Gold for a deterministic partial payroll"
)
var partial_advanced := _advance_operations_boundary_slice(
fixture, SimulationKernel.MINUTES_PER_DAY
)
var partial: Dictionary = runtime.staff_service.get_payroll_settlement(&"payroll-day-00000002")
_expect(
partial_advanced == SimulationKernel.MINUTES_PER_DAY
and _operations_slice_is_healthy(fixture)
and clock.absolute_minute == 2 * SimulationKernel.MINUTES_PER_DAY + 6 * 60
and int(partial.get(&"new_wages_total", -1)) == daily_wage
and int(partial.get(&"paid_total", -1)) == 50
and int(partial.get(&"unpaid_after_total", -1)) == daily_wage - 50,
"Day 2 payroll pays available Gold only and carries exact arrears"
)
var terminal_advanced := _advance_operations_boundary_slice(
fixture, 2 * SimulationKernel.MINUTES_PER_DAY
)
var coordinator: OperationsCoordinator = runtime.operations_coordinator
var last_three_negative := true
for day in [2, 3, 4]:
var summary: Dictionary = coordinator.get_day_summary(day)
last_three_negative = last_three_negative \
and int(summary.get(&"net_profit", 0)) < 0 \
and int((summary.get(&"settlement", {}) as Dictionary).get(
&"arrears_total", 0
)) > 0
_expect(
terminal_advanced == 2 * SimulationKernel.MINUTES_PER_DAY
and clock.absolute_minute == 4 * SimulationKernel.MINUTES_PER_DAY + 6 * 60
and last_three_negative
and coordinator.get_consecutive_insolvency_count() == 3
and coordinator.get_terminal_state() == &"BANKRUPT",
"three consecutive negative closes with arrears enter BANKRUPT at the third boundary"
)
var terminal_snapshot: Dictionary = runtime.create_snapshot()
var terminal_digest := SimulationDigest.compute(terminal_snapshot)
_expect(
runtime.restore_snapshot(terminal_snapshot),
"BANKRUPT terminal v6 snapshot restores without clearing terminal state"
)
clock.advance_minutes(180)
_expect(
clock.absolute_minute == 4 * SimulationKernel.MINUTES_PER_DAY + 6 * 60
and SimulationDigest.compute(runtime.create_snapshot()) == terminal_digest,
"BANKRUPT is terminal: later time requests are exact no-ops"
)
_expect(
not bool(runtime.acknowledge_operations_pause().get(&"ok", true)),
"BANKRUPT cannot be acknowledged away"
)
_assert_v6_tamper_rejected(
runtime,
terminal_snapshot,
func(tampered: Dictionary) -> void:
tampered[&"systems"][&"runtime"][&"operations"][&"personal_action_points"] = 99,
"v6 rejects an out-of-range Operations action-point total"
)
_assert_v6_tamper_rejected(
runtime,
terminal_snapshot,
func(tampered: Dictionary) -> void:
tampered[&"systems"][&"runtime"][&"next_command_id"] = int(
tampered[&"systems"][&"runtime"][&"next_command_id"]
) + 1,
"v6 rejects command high-water gaps"
)
_assert_v6_tamper_rejected(
runtime,
terminal_snapshot,
func(tampered: Dictionary) -> void:
var accepted: Array = tampered[&"systems"][&"runtime"][&"command_queue"][&"accepted"]
if not accepted.is_empty():
accepted[0][&"idempotency_key"] = &"tampered.accepted.key",
"v6 rejects accepted-command/idempotency cross-link tampering"
)
_assert_v6_tamper_rejected(
runtime,
terminal_snapshot,
func(tampered: Dictionary) -> void:
var summaries: Array = tampered[&"systems"][&"runtime"][&"operations"][&"coordinator"][&"day_summaries"]
summaries[0][&"category_totals"][&"wages"] = int(
summaries[0][&"category_totals"][&"wages"]
) + 1,
"v6 rejects DaySummary/payroll wage reconciliation tampering"
)
_assert_v6_tamper_rejected(
runtime,
terminal_snapshot,
func(tampered: Dictionary) -> void:
tampered[&"systems"][&"runtime"][&"unexpected_v6_field"] = true,
"v6 rejects unknown Runtime fields"
)
_assert_v6_tamper_rejected(
runtime,
terminal_snapshot,
func(tampered: Dictionary) -> void:
tampered[&"systems"][&"runtime"][&"operations"][&"coordinator"][&"unexpected_field"] = true,
"v6 rejects unknown Coordinator fields"
)
_free_fixture(fixture)
func _assert_v6_tamper_rejected(
runtime: Variant,
canonical: Dictionary,
mutator: Callable,
label: String
) -> void:
var before: Dictionary = runtime.create_snapshot()
var tampered: Dictionary = canonical.duplicate(true)
mutator.call(tampered)
var rejected: bool = not runtime.restore_snapshot(tampered)
_expect(rejected and runtime.create_snapshot() == before, label + " atomically")
func _test_fourteen_day_boundary_snapshot_determinism() -> void:
var seed := 73_003
var continuous := _new_fixture(seed)
var continuous_clock: Variant = continuous[&"clock"]
var continuous_runtime: Variant = continuous[&"runtime"]
var continuous_start: int = continuous_clock.absolute_minute
var first_advanced := _advance_operations_boundary_slice(
continuous, FOURTEEN_DAYS_MINUTES / 2
)
var midpoint: Dictionary = continuous_runtime.create_snapshot()
_expect(
first_advanced == FOURTEEN_DAYS_MINUTES / 2
and continuous_clock.absolute_minute == continuous_start + FOURTEEN_DAYS_MINUTES / 2
and _operations_slice_is_healthy(continuous),
"boundary-accelerated midpoint is captured after seven healthy days"
)
var second_advanced := _advance_operations_boundary_slice(
continuous, FOURTEEN_DAYS_MINUTES / 2
)
var continuous_snapshot: Dictionary = continuous_runtime.create_snapshot()
var continuous_digest := SimulationDigest.compute(continuous_snapshot)
_expect(
first_advanced + second_advanced == FOURTEEN_DAYS_MINUTES
and continuous_clock.absolute_minute - continuous_start == FOURTEEN_DAYS_MINUTES
and _operations_slice_is_healthy(continuous),
"continuous boundary-accelerated Operations advances exactly 14 days = 20,160 minutes"
)
_expect(
continuous_runtime.operations_coordinator.get_day_summaries().size() == 14,
"continuous boundary run commits all fourteen daily closes"
)
var resumed := _new_fixture(seed, false)
var resumed_clock: Variant = resumed[&"clock"]
var resumed_runtime: Variant = resumed[&"runtime"]
var restored: bool = resumed_runtime.restore_snapshot(midpoint)
_expect(restored, "fresh Runtime restores the enabled v6 midpoint snapshot")
var resumed_advanced := 0
if restored:
resumed_advanced = _advance_operations_boundary_slice(
resumed, FOURTEEN_DAYS_MINUTES / 2
)
var resumed_snapshot: Dictionary = resumed_runtime.create_snapshot()
_expect(
restored and resumed_advanced == FOURTEEN_DAYS_MINUTES / 2
and resumed_clock.absolute_minute == 6 * 60 + FOURTEEN_DAYS_MINUTES
and _operations_slice_is_healthy(resumed),
"restored boundary-accelerated Operations completes seven healthy days"
)
_expect(
restored and SimulationDigest.compute(resumed_snapshot) == continuous_digest
and resumed_snapshot == continuous_snapshot,
"continuous and midpoint-restored 14-day boundary runs have identical snapshot and Digest"
)
_free_fixture(continuous)
_free_fixture(resumed)
func _expect(condition: bool, label: String) -> void:
_checks += 1
if condition:
print(" ✓ %s" % label)
return
_failures += 1
push_error(" ✗ %s" % label)
@@ -0,0 +1 @@
uid://cmfsedkuddbhg
@@ -0,0 +1,381 @@
extends SceneTree
var _checks := 0
var _failures := 0
var _clock: Variant
var _state: Variant
var _runtime: Variant
func _initialize() -> void:
call_deferred("_run")
func _run() -> void:
print("MVP: Runtime operations integration")
_reset_runtime()
_test_disabled_compatibility_and_enable()
_test_enabled_snapshot_round_trip()
_test_seed_424242_second_arrival_boundary()
_test_after_mutation_notifications_are_atomic()
_test_deferred_failure_backlog_and_exact_retry()
_test_daily_payroll_financials_and_crosslink_tamper()
_cleanup()
if _failures == 0:
print("PASS: %d checks" % _checks)
else:
push_error("FAIL: %d of %d checks failed" % [_failures, _checks])
quit(_failures)
func _reset_runtime(seed: int = 20260827) -> void:
_cleanup()
_clock = load("res://autoload/game_clock.gd").new()
_state = load("res://autoload/game_state.gd").new()
_runtime = load("res://autoload/simulation_runtime.gd").new()
_clock.reset(1, 6, 0)
_state.reset()
var report: Dictionary = _runtime.initialize(_clock, _state, seed)
_expect(bool(report.get(&"valid", false)), "Runtime initializes before Operations opt-in")
_clock.set_paused(true)
func _cleanup() -> void:
if _runtime != null:
_runtime.free()
if _clock != null:
_clock.free()
if _state != null:
_state.free()
_runtime = null
_clock = null
_state = null
func _test_disabled_compatibility_and_enable() -> void:
var disabled: Dictionary = _runtime.get_operations_report()
_expect(not disabled[&"enabled"], "Operations remain disabled for existing Runtime tests")
var disabled_snapshot: Dictionary = _runtime.create_snapshot()
var disabled_runtime: Dictionary = disabled_snapshot[&"systems"][&"runtime"]
_expect(int(disabled_runtime[&"runtime_schema_version"]) == 6, "Runtime snapshot advances to schema v6")
_expect(
disabled_runtime[&"operations"] == {
&"wrapper_version": 1,
&"enabled": false,
&"personal_action_points": 0,
&"runtime_failure_latched": false,
&"runtime_failure_report": {},
&"deferred_runtime_minute": -1,
&"coordinator": {},
},
"disabled Operations wrapper has one canonical shape"
)
var enabled: Dictionary = _runtime.enable_production_operations()
var enabled_ok := bool(enabled.get(&"ok", false))
_expect(enabled_ok, "Production Operations opt in succeeds only at Day 1 06:00")
if not enabled_ok:
print(" enable diagnostic: %s" % JSON.stringify(enabled, "", true, true))
return
_expect(
_runtime.operations_coordinator.current_minute == 360,
"enable processes the 06:00 boundary from minute 359 exactly once"
)
_expect(
int(_runtime.get_operations_report()[&"personal_action_points"]) == 2,
"first morning boundary restores two personal action points"
)
func _test_enabled_snapshot_round_trip() -> void:
if _runtime.operations_coordinator == null:
return
var snapshot: Dictionary = _runtime.create_snapshot()
var expected_digest := SimulationDigest.compute(snapshot)
_expect(_runtime.restore_snapshot(snapshot), "enabled Runtime v5 snapshot restores")
_expect(_runtime.latest_digest == expected_digest, "enabled Runtime v5 round trip is exact")
var operations: Dictionary = _runtime.get_operations_report()
_expect(
operations[&"enabled"] and _runtime.operations_coordinator.current_minute == _clock.absolute_minute,
"restored Coordinator clock cross-link remains exact"
)
func _test_seed_424242_second_arrival_boundary() -> void:
_reset_runtime(424242)
var enabled: Dictionary = _runtime.enable_production_operations()
_expect(bool(enabled.get(&"ok", false)), "seed 424242 enables production Operations")
if not bool(enabled.get(&"ok", false)):
return
var before_count: int = _runtime.guest_service.guest_count()
_clock.advance_minutes(30)
var report: Dictionary = _runtime.get_operations_report()
_expect(
_clock.absolute_minute == 390 \
and int(report.get(&"deferred_runtime_minute", -2)) == -1 \
and not bool(report.get(&"runtime_failure_latched", true)) \
and not _runtime.operations_coordinator.is_external_quarantined(),
"seed 424242 completes the minute 390 arrival boundary without quarantine"
)
_expect(
_runtime.operations_coordinator.get_population_count() \
== _runtime.guest_service.guest_count() \
and _runtime.guest_service.guest_count() >= before_count,
"second arrival keeps Coordinator and Guest population cross-linked"
)
func _test_after_mutation_notifications_are_atomic() -> void:
_reset_runtime(424242)
_expect(
bool(_runtime.enable_production_operations().get(&"ok", false)),
"after-mutation rollback fixture enables Operations"
)
_clock.advance_minutes(29)
var before := {
&"economy": _runtime.economy_service.create_snapshot(),
&"guests": _runtime.guest_service.create_snapshot(),
&"path": _runtime.path_service.create_snapshot(),
&"movement": _runtime._movement_credits.duplicate(true),
&"progression": _runtime.progression_service.create_snapshot(),
&"events": _runtime.domain_events.create_snapshot(),
&"ids": _runtime.id_generator.create_snapshot(),
}
var signals := {&"guest": 0, &"economy": 0, &"arrival": 0}
_runtime.guest_service.guest_spawned.connect(
func(_guest: GuestRuntime) -> void: signals[&"guest"] += 1
)
_runtime.economy_service.transaction_posted.connect(
func(_transaction_id: StringName, _entries: Array) -> void:
signals[&"economy"] += 1
)
_runtime.operations_coordinator.arrivals_committed.connect(
func(_report: Dictionary) -> void: signals[&"arrival"] += 1
)
_runtime.set_operations_test_boundary_hook(
&"ARRIVAL_PLAN",
func(_payload: Dictionary, phase: StringName, _token: Variant) -> Dictionary:
return {
&"ok": phase != _runtime.OPERATIONS_TEST_AFTER_FIRST_MUTATION_PHASE,
&"reason": &"FORCED_AFTER_MUTATION_FAILURE",
}
)
_clock.advance_minutes(1)
var after := {
&"economy": _runtime.economy_service.create_snapshot(),
&"guests": _runtime.guest_service.create_snapshot(),
&"path": _runtime.path_service.create_snapshot(),
&"movement": _runtime._movement_credits.duplicate(true),
&"progression": _runtime.progression_service.create_snapshot(),
&"events": _runtime.domain_events.create_snapshot(),
&"ids": _runtime.id_generator.create_snapshot(),
}
_expect(
_clock.absolute_minute == 390 and _clock.paused and before == after,
"failure after the first arrival mutation restores every external domain exactly"
)
_expect(
int(signals[&"guest"]) == 0 and int(signals[&"economy"]) == 0 \
and int(signals[&"arrival"]) == 0,
"rolled-back arrival leaks no Guest, Economy, or Coordinator success signal"
)
_runtime.set_operations_test_boundary_hook(&"ARRIVAL_PLAN", Callable())
var retried: Dictionary = _runtime.acknowledge_operations_pause()
_expect(
bool(retried.get(&"ok", false)) and int(signals[&"guest"]) > 0 \
and int(signals[&"economy"]) > 0 and int(signals[&"arrival"]) == 1,
"successful retry flushes each staged service notification after commit"
)
func _test_deferred_failure_backlog_and_exact_retry() -> void:
_reset_runtime(424242)
_expect(
bool(_runtime.enable_production_operations().get(&"ok", false)),
"deferred retry fixture enables Operations"
)
_clock.advance_minutes(29)
var scheduled: CommandResult = _runtime.submit_command(
&"spawn_guest",
{
&"guest_id": &"scheduled_boundary_guest",
&"guest_type_id": _runtime.get_mvp_guest_definition_ids()[0],
&"gold": 25,
&"options": {
&"current_cell": Vector3i.ZERO,
&"planned_exit_at": 2_000,
},
},
&"test", &"scheduled_boundary_guest", 100, 390,
&"operations.test.scheduled.spawn"
)
_expect(scheduled.is_success(), "minute 390 command is queued before the boundary")
var counts := {&"guest": 0, &"arrival": 0, &"hook": 0}
_runtime.guest_service.guest_spawned.connect(
func(_guest: GuestRuntime) -> void: counts[&"guest"] += 1
)
_runtime.operations_coordinator.arrivals_committed.connect(
func(_report: Dictionary) -> void: counts[&"arrival"] += 1
)
_runtime.set_operations_test_boundary_hook(
&"ARRIVAL_PLAN",
func(_payload: Dictionary, phase: StringName, _token: Variant) -> Dictionary:
counts[&"hook"] += 1
return {
&"ok": false,
&"reason": &"FORCED_PREPARE_FAILURE",
&"phase": phase,
}
)
_clock.set_speed(4)
var processed: int = _clock.process_real_delta(5.0)
var failed_report: Dictionary = _runtime.get_operations_report()
_expect(
processed == 1 and _clock.absolute_minute == 390 and _clock.paused,
"boundary failure stops an eight-tick backlog at the failed minute"
)
_expect(
int(failed_report[&"deferred_runtime_minute"]) == 390 \
and failed_report[&"runtime_failure_latched"] \
and _runtime.operations_coordinator.current_minute == 389,
"failed boundary persists one canonical deferred shape"
)
_expect(
_runtime.guest_service.has_guest(&"scheduled_boundary_guest") \
and int(counts[&"guest"]) == 1 \
and int(counts[&"arrival"]) == 0,
"pre-boundary command commits once while failed callback emits no arrival notification"
)
var interrupted: Dictionary = _runtime.create_snapshot()
_expect(
_runtime.restore_snapshot(interrupted),
"deferred v5 snapshot restores before acknowledgement"
)
_runtime.set_operations_test_boundary_hook(&"ARRIVAL_PLAN", Callable())
var retry_guest_signals := {&"count": 0}
_runtime.guest_service.guest_spawned.connect(
func(_guest: GuestRuntime) -> void: retry_guest_signals[&"count"] += 1
)
var acknowledged: Dictionary = _runtime.acknowledge_operations_pause()
_expect(
bool(acknowledged.get(&"ok", false)) \
and bool(acknowledged.get(&"suffix_replayed", false)) \
and _clock.absolute_minute == 390 \
and _runtime.operations_coordinator.current_minute == 390,
"acknowledgement retries the same boundary and suffix exactly once"
)
var completion_count := 0
for event: SimulationDomainEvent in _runtime.domain_events.recent_events():
if event.event_type == &"COMMAND_COMPLETED" \
and event.causation_command_id == scheduled.command_id:
completion_count += 1
_expect(
completion_count == 1 \
and _runtime.command_queue.processed_count() == 1 \
and _runtime.guest_service.current_minute == 390 \
and int(retry_guest_signals[&"count"]) \
== _runtime.operations_coordinator.get_arrival_reports()[-1][&"total_count"],
"retry never replays the command and advances the skipped Runtime suffix once"
)
var completed: Dictionary = _runtime.create_snapshot()
_expect(_runtime.restore_snapshot(completed), "completed deferred retry round-trips exactly")
func _test_daily_payroll_financials_and_crosslink_tamper() -> void:
_reset_runtime(811_004)
var hired: CommandResult = _runtime.submit_command(
&"hire_staff_candidate",
{&"candidate_id": &"staff_akane", &"current_cell": Vector3i.ZERO},
&"player", &"staff_akane", 100, -1, &"operations.payroll.hire.akane"
)
_expect(hired.is_success(), "daily payroll fixture hires one catalog Staff member")
_expect(
bool(_runtime.enable_production_operations().get(&"ok", false)),
"daily payroll fixture enables Operations after hiring"
)
_clock.advance_minutes(1_440)
var summary: Dictionary = _runtime.operations_coordinator.get_day_summary(1)
var settlement: Dictionary = summary.get(&"settlement", {})
_expect(
_clock.absolute_minute == 1_800 \
and int(summary.get(&"closed_at", -1)) == 1_800 \
and int(summary[&"category_totals"][&"wages"]) == 120,
"06:00 closes Day 1 once and accrues only the new daily wage"
)
_expect(
int(settlement.get(&"paid_total", -1)) == 120 \
and int(settlement.get(&"arrears_total", -1)) == 0 \
and _runtime.staff_service.get_staff(&"staff_akane").unpaid_wages == 0,
"sufficient Gold pays the full lexical payroll with no arrears"
)
var wage_transaction: StringName = _runtime.economy_service.get_transaction_id_for_idempotency_key(
&"operations.wage.day.00000001"
)
var wage_entries: Array[LedgerEntry] = _runtime.economy_service.get_transaction_entries(wage_transaction)
_expect(
wage_entries.size() == 1 \
and wage_entries[0].category == &"WAGE" \
and wage_entries[0].absolute_minute == 1_800 \
and wage_entries[0].amount == 120,
"paid payroll posts one canonical WAGE ledger line at the closing boundary"
)
var canonical: Dictionary = _runtime.create_snapshot()
_expect(_runtime.restore_snapshot(canonical), "Day 1 payroll/P&L v5 snapshot round-trips")
var signing_category: Dictionary = canonical.duplicate(true)
for raw_entry: Dictionary in signing_category[&"systems"][&"runtime"][&"economy"][&"entries"]:
if String(raw_entry[&"category"]) == "STAFF_SIGNING_COST":
raw_entry[&"category"] = "TRANSFER"
break
_expect(
_rejects_atomically(signing_category),
"closed non-WAGE ledger category tamper is rejected atomically"
)
var wage_category: Dictionary = canonical.duplicate(true)
for raw_entry: Dictionary in wage_category[&"systems"][&"runtime"][&"economy"][&"entries"]:
if String(raw_entry[&"category"]) == "WAGE":
raw_entry[&"category"] = "TRANSFER"
break
_expect(
_rejects_atomically(wage_category),
"Staff settlement cannot outlive its canonical WAGE ledger evidence"
)
var lifetime: Dictionary = canonical.duplicate(true)
lifetime[&"systems"][&"runtime"][&"progression"][&"metrics"][&"guest_lifetime"] += 1
_expect(
_rejects_atomically(lifetime),
"Progression lifetime Guest inflation is rejected against active plus persistent Guests"
)
var plan_class: Dictionary = canonical.duplicate(true)
var arrival_reports: Array = plan_class[&"systems"][&"runtime"][&"operations"][&"coordinator"][&"arrival_reports"]
var changed_plan := false
for report: Dictionary in arrival_reports:
if not (report[&"plans"] as Array).is_empty():
var plan: Dictionary = report[&"plans"][0]
plan[&"guest_class"] = "noble" if String(plan[&"guest_class"]) != "noble" else "laborer"
changed_plan = true
break
_expect(
changed_plan and _rejects_atomically(plan_class),
"arrival plan class cannot diverge from its persisted Guest definition"
)
var downgraded: Dictionary = canonical.duplicate(true)
downgraded[&"systems"][&"runtime"][&"runtime_schema_version"] = 4
_expect(
_rejects_atomically(downgraded),
"relabelled enabled v5 cannot discard Operations history through v4 migration"
)
func _rejects_atomically(candidate: Dictionary) -> bool:
var before: Dictionary = _runtime.create_snapshot()
return not _runtime.restore_snapshot(candidate) \
and _runtime.create_snapshot() == before
func _expect(condition: bool, label: String) -> void:
_checks += 1
if condition:
print(" ✓ %s" % label)
return
_failures += 1
push_error(" ✗ %s" % label)
@@ -0,0 +1 @@
uid://dxchxjl371ti