docs: retain exact P18 preservation patch in verified gzip
This commit is contained in:
-506
@@ -1,506 +0,0 @@
|
||||
diff --git a/autoload/simulation_runtime.gd b/autoload/simulation_runtime.gd
|
||||
index 27bb139..82fa152 100644
|
||||
--- a/autoload/simulation_runtime.gd
|
||||
+++ b/autoload/simulation_runtime.gd
|
||||
@@ -1056,14 +1056,10 @@ func _restore_snapshot_internal(snapshot: Dictionary, recovery_mode: bool) -> bo
|
||||
return false
|
||||
var restored_relationships := RelationshipService.new()
|
||||
restored_relationships.configure_original_identity_validator(
|
||||
- func(payload: Dictionary) -> bool: return _original_relationship_identities_valid(payload, restored_staff)
|
||||
+ func(payload: Dictionary) -> bool: return _original_relationship_identities_valid(payload, restored_staff, restored_catalog)
|
||||
)
|
||||
if not restored_relationships.restore_snapshot(runtime_data[&"relationships"]):
|
||||
return false
|
||||
- for relationship: RelationshipState in restored_relationships.get_relationships():
|
||||
- for actor_id: StringName in [relationship.actor_a_id, relationship.actor_b_id]:
|
||||
- if actor_id != &"player" and not restored_staff.has_staff(actor_id) and not restored_guests.has_guest(actor_id):
|
||||
- return false
|
||||
var restored_events := EventService.new()
|
||||
for raw_definition: Variant in runtime_data[&"event_definitions"]:
|
||||
var definition := EventDefinition.from_variant(raw_definition)
|
||||
@@ -5387,7 +5383,7 @@ func _configure_relationship_event_boundaries() -> void:
|
||||
if relationship_service == null or event_service == null:
|
||||
return
|
||||
relationship_service.configure_original_identity_validator(
|
||||
- func(payload: Dictionary) -> bool: return _original_relationship_identities_valid(payload, staff_service)
|
||||
+ func(payload: Dictionary) -> bool: return _original_relationship_identities_valid(payload, staff_service, mvp_content_catalog)
|
||||
)
|
||||
relationship_service.configure_candidate_callback(
|
||||
Callable(self, "_on_relationship_threshold_candidate")
|
||||
@@ -6770,15 +6766,15 @@ func _execute_progress_staff_task(command: SimulationCommand) -> CommandResult:
|
||||
result.command_id = command.command_id
|
||||
return result
|
||||
|
||||
-func _original_relationship_identities_valid(payload: Dictionary, staff_registry: StaffService) -> bool:
|
||||
+func _original_relationship_identities_valid(payload: Dictionary, staff_registry: StaffService, catalog: MvpContentCatalog) -> bool:
|
||||
if payload.get("protagonist_id") != "player" or staff_registry == null:
|
||||
return false
|
||||
var subject := StringName(payload.get("subject_id", ""))
|
||||
var rival := StringName(payload.get("rival_id", ""))
|
||||
if subject == &"player" or rival == &"player" or subject == rival or not staff_registry.has_staff(subject):
|
||||
return false
|
||||
- var heroine_definition := mvp_content_catalog.get_entry(&"unique_characters", subject)
|
||||
- var rival_definition := mvp_content_catalog.get_entry(&"unique_characters", rival)
|
||||
+ var heroine_definition := catalog.get_entry(&"unique_characters", subject)
|
||||
+ var rival_definition := catalog.get_entry(&"unique_characters", rival)
|
||||
var subject_staff := staff_registry.get_staff(subject)
|
||||
return subject_staff.employment_kind == StaffRuntime.EmploymentKind.UNIQUE \
|
||||
and heroine_definition.get(&"role_id") == String(subject_staff.role_id) \
|
||||
diff --git a/docs/plans/original-completion-2026-10-02/execution/completion_marker_contracts.json b/docs/plans/original-completion-2026-10-02/execution/completion_marker_contracts.json
|
||||
index 5474439..6d5e0ae 100644
|
||||
--- a/docs/plans/original-completion-2026-10-02/execution/completion_marker_contracts.json
|
||||
+++ b/docs/plans/original-completion-2026-10-02/execution/completion_marker_contracts.json
|
||||
@@ -7,7 +7,7 @@
|
||||
"template": "Original relationship model: <checks> checks, <failures> failures",
|
||||
"required": true,
|
||||
"fixed_check_count_used": false,
|
||||
- "source_sha256_at_schema_audit": "ad509f5fb67ab91e1d1d99a08af51e4695e411b9a2541ea63ee22b1555cb0924",
|
||||
+ "source_sha256_at_schema_audit": "c74b243dbbb54dba9bbc165e29508cca910f0beb63e32d25ebbb8ea606dccce9",
|
||||
"authority": {
|
||||
"path": "docs/spec/acceptance/P18-01-code-r01.json",
|
||||
"scope": "new backend suite marker; native execution pending"
|
||||
@@ -18,7 +18,7 @@
|
||||
"template": "Original relationship runtime: <checks> checks, <failures> failures",
|
||||
"required": true,
|
||||
"fixed_check_count_used": false,
|
||||
- "source_sha256_at_schema_audit": "374f3d31a65d2f2b62dfef29721c8c838575b856f4fa5a0bca31c6ba4c7e09dd",
|
||||
+ "source_sha256_at_schema_audit": "349271cc8b8a4750ef339ecac6905852bd7a5d006cca68ee10d5d0c737f426de",
|
||||
"authority": {
|
||||
"path": "docs/spec/acceptance/P18-01-code-r01.json",
|
||||
"scope": "new backend suite marker; native execution pending"
|
||||
diff --git a/docs/plans/original-completion-2026-10-02/execution/p18-01-code/IMPLEMENTATION.md b/docs/plans/original-completion-2026-10-02/execution/p18-01-code/IMPLEMENTATION.md
|
||||
index 453eeb2..efc5cea 100644
|
||||
--- a/docs/plans/original-completion-2026-10-02/execution/p18-01-code/IMPLEMENTATION.md
|
||||
+++ b/docs/plans/original-completion-2026-10-02/execution/p18-01-code/IMPLEMENTATION.md
|
||||
@@ -1,6 +1,53 @@
|
||||
# P18-01-CODE implementation handoff
|
||||
|
||||
-Status: submitted for Runner validation; no acceptance or unit completion claimed.
|
||||
+Status: repair 4 implementation prepared for Runner; no acceptance or unit completion claimed. P02-01 representative inputs remain an unavailable context dependency, detailed below.
|
||||
+
|
||||
+## Repair 3 historical failure (2026-10-15)
|
||||
+
|
||||
+Historical record only: stopping independent repairs over decoder permissiveness was wrong. The commander producer-evidence interpretation supersedes this old policy concern; the following describes the failed repair-3 attempt, not the current policy.
|
||||
+
|
||||
+Inspected baseline d2a1f80a613e7c77b02b941026bc49c2d85db863 using read-only git show.
|
||||
+RelationshipState.from_snapshot delegates to configure; _value preserves canonical
|
||||
+key precedence. Numeric fields use int(), with clampi/maxi where applicable.
|
||||
+Thus finite fractional numbers are truncated as well as integral JSON floats;
|
||||
+numeric strings such as metrics.affection = "42" are also converted. The baseline
|
||||
+RelationshipService._parse_snapshot similarly converts version and candidate
|
||||
+sequence with int(). These are broader than a finite-integral-float-only policy.
|
||||
+No acceptance policy for these conflicting formats has been selected here.
|
||||
+
|
||||
+Baseline normalization also replaces non-Dictionary metrics, action_counts,
|
||||
+daily_metric_deltas, flags and crossed_thresholds with defaults, coerces flags
|
||||
+with bool(), and ignores non-Array threshold collections. The immutable spec
|
||||
+requires wrong types to reject, while the repair direction requires baseline
|
||||
+legitimate acceptance without a new restriction or loosening. In particular,
|
||||
+accepting or rejecting numeric strings cannot satisfy both without an explicit
|
||||
+policy decision. Non-numeric/non-finite input, wrong containers, future versions,
|
||||
+duplicates, invalid references, corrupt original records and projection mismatches
|
||||
+remain required rejections under the spec; this report does not waive any.
|
||||
+
|
||||
+Baseline Runtime restore delegates legacy relationships to RelationshipService
|
||||
+without the added actor-existence loop. Baseline GuestService.despawn_guest retains
|
||||
+the departed guest in persistent_records and removes it from active guests.
|
||||
+The held actor loop is therefore still an unresolved repair item. Candidate
|
||||
+catalog selection, lifecycle tests, mutation-through-alias tests, atomic rejection
|
||||
+coverage and source-pin updates are also still outstanding. The numeric Reader
|
||||
+helpers in SimulationSnapshot and SaveService accept bounded finite integral JSON
|
||||
+floats for their own envelope fields; they do not establish a policy overriding
|
||||
+RelationshipState's coercions. P02-01 representative-save applicability has not
|
||||
+been established; no fixture acceptance claim is made.
|
||||
+
|
||||
+Only this handoff and prepare-only evidence were changed. Production code, test
|
||||
+sources and marker/policy pins were left untouched pending policy resolution.
|
||||
+Prepare-only exited 0: 1,311 source files, 67,310,742 bytes; 3,384,279,040 free
|
||||
+bytes; 381,883,542 conservative required bytes; capacity available. Seven suites:
|
||||
+original_relationship_model, original_relationship_runtime,
|
||||
+relationship_event_service, runtime_relationship_events,
|
||||
+runtime_snapshot_serialization, runtime_save_lifecycle, staff_management.
|
||||
+308 future parse paths and nine future native phases; engines/stages = 0,
|
||||
+actual_checks = null, formal_acceptance = false. The first invocation omitted
|
||||
+required --output and exited 2; the corrected invocation used the allowed
|
||||
+p18-01-code/repair-3-prepare output path. Native checks were not run.
|
||||
+P18-01 remains 30/88, in progress, with no partial credit.
|
||||
|
||||
Scope: dedicated original relationship state, lossless legacy snapshot preservation,
|
||||
Runtime registration and read projection, and the contract-listed tests and collector.
|
||||
@@ -34,8 +81,8 @@ The acceptance specification without its final newline matches the contract SHA2
|
||||
catalog membership does not invent recruitment. No new original update policy,
|
||||
contact, route, attack, threshold or duel behavior is defined.
|
||||
- Snapshot restore validates candidates before replacing live services, then
|
||||
- rebinds the identity validator to live staff. Existing legacy actor references
|
||||
- must resolve against player, restored staff or restored guests.
|
||||
+ rebinds the identity validator to live staff. Legacy relationships retain the original acceptance of departed guests; original
|
||||
+ records separately validate staff/person/catalog references.
|
||||
- Every staff-management row exposes `original_relationship`. Missing records
|
||||
have `configured: false` and null values, distinct from explicit zero. Configured
|
||||
projections are copies, so reads and caller edits cannot change service state.
|
||||
@@ -77,3 +124,91 @@ in_progress: ordinary Japanese UI rendering/equality, approved production values
|
||||
all-seven recruitment, final full required roster and applicable Windows final
|
||||
acceptance remain outstanding. No git writes, other agents or Runner controls
|
||||
were used by worker.
|
||||
+
|
||||
+## Repair 4 producer interpretation and independent repairs
|
||||
+
|
||||
+The unchanged wrong-types/valid-legacy contract follows legitimate producers,
|
||||
+not corrupt-input coercions. RelationshipState.create_snapshot
|
||||
+(scripts/relationship/relationship_state.gd:132-152) emits String ids/route,
|
||||
+int numeric fields, named route_lock, Dictionary numeric/bool collections and
|
||||
+Array thresholds. Its validity check (:99-101) requires int metrics.
|
||||
+Read-only git log identifies 423fd6d as the only history entry for that file;
|
||||
+git show of that producer (:132-152) also emits the enum NAME String, not int.
|
||||
+
|
||||
+Actual writer scripts/core/save_service.gd:874-878 serializes the snapshot with
|
||||
+var_to_bytes and ZSTD; Reader :1021-1028 uses bytes_to_var. Thus world numeric
|
||||
+ints remain ints. JSON decoding (:970-978) applies to the manifest, not relationship
|
||||
+payloads. Integral JSON floats are not an established relationship save format
|
||||
+on this path. Accept int legacy values, named route_lock and producer collections.
|
||||
+Reject numeric strings, null, bool-as-number, fractional/nonfinite numbers, float
|
||||
+relationship numbers, int route_lock, wrong containers and invalid enum names.
|
||||
+Unknown extensions remain opaque; canonical values precede aliases, and aliases,
|
||||
+unknown facts and collection order remain raw retained facts. Neither old axis
|
||||
+configures original Affection/Lust/Netorare.
|
||||
+
|
||||
+relationship_service.gd _legacy_types_valid now also checks collection key types
|
||||
+and restricts route_lock to producer enum names. The pre-existing int/container
|
||||
+checks implement the producer policy. No conversion or rewrite of raw input occurs.
|
||||
+Runtime candidate validation now takes restored_catalog; command/service binding
|
||||
+uses the live catalog. Removed only the added active legacy actor loop.
|
||||
+GuestService.despawn_guest (scripts/guest/guest_service.gd:954-978) retains the
|
||||
+persistent record and removes the active guest, so that loop rejected valid saves.
|
||||
+Original identity/person validation remains in place.
|
||||
+Existing StaffService.remove_staff (:897-898) and retire_staff (:942-943) already
|
||||
+refuse UNIQUE departures before changes; tests assert refusal and saved original
|
||||
+record survival. No new departure rule or record deletion was added.
|
||||
+
|
||||
+The two new suites now cover real Service and Runtime affection/dependence
|
||||
+mutations from v1 affinity/obligation, canonical precedence, raw extensions and
|
||||
+order, null original values, v2 restore/rebind, departed persistent guest acceptance,
|
||||
+and atomic malformed candidates. Rejection checks retain service identity/state,
|
||||
+whole Runtime bytes and caller input. The five existing suites remain unchanged.
|
||||
+Fixtures are validation inputs, not production values or newly hired characters.
|
||||
+
|
||||
+Collector derives every sorted .gd path from the exact shared.source_refs map.
|
||||
+Verification binds parse-paths.json, the receipt count, the unique zero-failure
|
||||
+FULL_DEPENDENCY_PARSE marker, actual_parse_checks and the whole retained stage
|
||||
+to that same source map. No count-specific skips or other provenance changes.
|
||||
+This frozen map has 308 paths; expanded maps must verify their own full count.
|
||||
+
|
||||
+### Precise remaining context gap
|
||||
+
|
||||
+The P02-01 input manifest referenced by
|
||||
+docs/plans/original-completion-2026-10-02/execution/multi-agent-dev-handoff/delivery_contract_proposal.json:333
|
||||
+is absent at artifacts/original-completion-2026-10-02/p02-01-fixed-baseline-r01/input-manifest.json
|
||||
+in this worktree. Reading the exact production-root counterpart returns
|
||||
+Operation not permitted. Production-root listing/search is also denied.
|
||||
+No representative save bytes or decoded fixture are available in this checkout;
|
||||
+.review-material is absent. Accordingly their exact types/applicability have NOT
|
||||
+been verified, no older conflicting producer shape is asserted, and no replacement
|
||||
+representative fixture has been invented. Runner/owner must supply readable pinned
|
||||
+manifest and applicable save members to finish that mandatory evidence and test.
|
||||
+This gap did not stop the independent repairs. No native checks were executed.
|
||||
+UI, values, routes, contacts and duel remain untouched. P18-01 remains 30/88,
|
||||
+in progress. Native seven-suite frozen evidence, p18-model validation, separate
|
||||
+Opus review, latest-main integration and fresh merged-source evidence remain
|
||||
+Root/Runner work; no completion claim is made.
|
||||
+
|
||||
+### Worker static/prepare evidence (repair 4)
|
||||
+
|
||||
+Python AST parsed successfully. git diff --check passed. Both new suite SHA-256
|
||||
+pins match their final source in marker contracts and suite-policy; all other
|
||||
+rows are unchanged. The five existing suite files are byte-identical to HEAD.
|
||||
+No engine, native collector, default profile or full regression was run.
|
||||
+
|
||||
+Prepare-only roster (seven): original_relationship_model,
|
||||
+original_relationship_runtime, relationship_event_service,
|
||||
+runtime_relationship_events, runtime_snapshot_serialization,
|
||||
+runtime_save_lifecycle, staff_management. Full paths are the unchanged ROSTER in
|
||||
+tools/run_p18_relationship_tests.py:17-25.
|
||||
+Initial repair-4 preview: 1,311 files, 67,318,770 source bytes,
|
||||
+3,388,039,168 free bytes, conservative single-set admission 381,891,570 bytes,
|
||||
+308 parse paths, nine future phases, engines/stages zero, actual_checks null,
|
||||
+formal_acceptance false. A final preview after final edits is retained separately
|
||||
+as repair-4-prepare.json. No native pass is implied by this preview.
|
||||
+
|
||||
+Using the supplied old output estimate conservatively as 474 MiB per full set
|
||||
+(including 351 MiB engine), two fresh sets require 948 MiB = 994,050,048 bytes.
|
||||
+They fit the observed ~3.39 GB free space, with ~2.39 GB left before other new
|
||||
+consumers. This is capacity arithmetic, not a reservation or a measured new run;
|
||||
+actual output growth remains Root's admission responsibility.
|
||||
diff --git a/scripts/relationship/relationship_service.gd b/scripts/relationship/relationship_service.gd
|
||||
index 35d4670..d4a49ef 100644
|
||||
--- a/scripts/relationship/relationship_service.gd
|
||||
+++ b/scripts/relationship/relationship_service.gd
|
||||
@@ -543,7 +543,7 @@ func _parse_snapshot(snapshot: Dictionary) -> Dictionary:
|
||||
|
||||
static func _legacy_types_valid(raw: Dictionary) -> bool:
|
||||
for key: String in ["relationship_id", "actor_a_id", "actor_b_id"]:
|
||||
- if not (raw.get(key) is String or raw.get(key) is StringName):
|
||||
+ if not raw.get(key) is String:
|
||||
return false
|
||||
for key: String in ["shield_max", "shield_current", "last_event_at", "daily_attack_count", "attack_day", "metric_delta_day"]:
|
||||
if raw.has(key) and not raw[key] is int:
|
||||
@@ -559,9 +559,9 @@ static func _legacy_types_valid(raw: Dictionary) -> bool:
|
||||
for value: Variant in raw.get(key, {}).values():
|
||||
if not value is int:
|
||||
return false
|
||||
- for key: String in ["action_counts", "flags"]:
|
||||
+ for key: String in ["metrics", "action_counts", "daily_metric_deltas", "flags", "crossed_thresholds"]:
|
||||
for identity: Variant in raw.get(key, {}):
|
||||
- if not (identity is String or identity is StringName):
|
||||
+ if not identity is String:
|
||||
return false
|
||||
for value: Variant in raw.get("flags", {}).values():
|
||||
if not value is bool:
|
||||
@@ -572,10 +572,11 @@ static func _legacy_types_valid(raw: Dictionary) -> bool:
|
||||
for threshold: Variant in value:
|
||||
if not threshold is int:
|
||||
return false
|
||||
- if raw.has("route_id") and not (raw["route_id"] is String or raw["route_id"] is StringName):
|
||||
- return false
|
||||
- if raw.has("route_lock") and not (raw["route_lock"] is int or raw["route_lock"] is String or raw["route_lock"] is StringName):
|
||||
+ if raw.has("route_id") and not raw["route_id"] is String:
|
||||
return false
|
||||
+ if raw.has("route_lock"):
|
||||
+ if not raw["route_lock"] is String or not RelationshipStateType.RouteLock.keys().has(raw["route_lock"]):
|
||||
+ return false
|
||||
return true
|
||||
|
||||
|
||||
diff --git a/tests/s4/test_original_relationship_model.gd b/tests/s4/test_original_relationship_model.gd
|
||||
index d2a2c81..1f24b4f 100644
|
||||
--- a/tests/s4/test_original_relationship_model.gd
|
||||
+++ b/tests/s4/test_original_relationship_model.gd
|
||||
@@ -30,7 +30,50 @@ func _service() -> RelationshipService:
|
||||
)
|
||||
|
||||
|
||||
+static func legacy_fixture() -> Dictionary:
|
||||
+ return {"version": 1, "relationships": [
|
||||
+ {"relationship_id": "aliases", "actor_a_id": "heroine_anri", "actor_b_id": "player",
|
||||
+ "metrics": {"affinity": 41, "obligation": 19, "extension": {"opaque": [3, "keep"]}},
|
||||
+ "extra": ["raw", 2.5]},
|
||||
+ {"relationship_id": "second", "actor_a_id": "departed", "actor_b_id": "player",
|
||||
+ "metrics": {"trust": 7}}],
|
||||
+ "threshold_candidates": [], "next_candidate_sequence": 1, "extension": ["ordered", 7]}
|
||||
+
|
||||
+
|
||||
+func _alias_mutations() -> void:
|
||||
+ var service := _service()
|
||||
+ var legacy := legacy_fixture()
|
||||
+ var input := var_to_bytes(legacy)
|
||||
+ _expect(service.restore_snapshot(legacy), "int v1 aliases accepted")
|
||||
+ _expect(service.apply_value_change(&"aliases", &"affection", 2, 0).is_success(), "affection mutation through real service")
|
||||
+ _expect(service.apply_value_change(&"aliases", &"dependence", 3, 0).is_success(), "dependence mutation through real service")
|
||||
+ var saved := service.create_snapshot()
|
||||
+ var raw: Dictionary = saved[&"legacy"]["relationships"][0]
|
||||
+ _expect(raw["metrics"]["affection"] == 43 and raw["metrics"]["dependence"] == 22, "canonical changes merged")
|
||||
+ _expect(raw["metrics"]["affinity"] == 41 and raw["metrics"]["obligation"] == 19, "aliases remain old facts")
|
||||
+ _expect(raw["metrics"]["extension"] == legacy["relationships"][0]["metrics"]["extension"] and raw["extra"] == ["raw", 2.5], "unknown facts survive mutation")
|
||||
+ _expect(saved[&"legacy"]["relationships"][1] == legacy["relationships"][1], "record order preserved")
|
||||
+ var restored := _service()
|
||||
+ _expect(restored.restore_snapshot(saved), "mutated v2 restores")
|
||||
+ _expect(var_to_bytes(restored.create_snapshot()) == var_to_bytes(saved), "mutated v2 raw order and bytes preserved")
|
||||
+ _expect(restored.get_relationship(&"aliases").get_metric(&"affection") == 43 and restored.get_relationship(&"aliases").get_metric(&"dependence") == 22, "canonical values take precedence over raw aliases")
|
||||
+ for key: StringName in [&"affection", &"lust", &"netorare"]:
|
||||
+ _expect(restored.get_original_relationship(&"heroine_anri")[key] == null, "old axes never configure original " + String(key))
|
||||
+ _expect(var_to_bytes(legacy) == input, "mutation never changes input bytes")
|
||||
+ for value: Variant in ["50", null, true, 50.0, 1.5, INF, NAN]:
|
||||
+ var bad := legacy_fixture()
|
||||
+ bad["relationships"][0]["metrics"]["affinity"] = value
|
||||
+ _rejected(restored, bad)
|
||||
+ var bad := legacy_fixture()
|
||||
+ bad["relationships"][0]["metrics"] = []
|
||||
+ _rejected(restored, bad)
|
||||
+ bad = saved.duplicate(true)
|
||||
+ bad[&"relationships"] = []
|
||||
+ _rejected(restored, bad)
|
||||
+
|
||||
+
|
||||
func _run() -> void:
|
||||
+ _alias_mutations()
|
||||
var service := _service()
|
||||
var before := var_to_bytes(service.create_snapshot())
|
||||
var unset := service.get_original_relationship(&"heroine_anri")
|
||||
diff --git a/tests/s4/test_original_relationship_runtime.gd b/tests/s4/test_original_relationship_runtime.gd
|
||||
index f2aa656..7029ce1 100644
|
||||
--- a/tests/s4/test_original_relationship_runtime.gd
|
||||
+++ b/tests/s4/test_original_relationship_runtime.gd
|
||||
@@ -71,8 +71,90 @@ func _run() -> void:
|
||||
invalid["rival_id"] = "missing"
|
||||
_expect(not runtime.relationship_service.register_original_relationship(invalid).is_success(), "restore rebind keeps identity validator")
|
||||
_expect(runtime.relationship_service.register_original_relationship(fixture).is_success(), "restored service accepts correct identities")
|
||||
+ _expect(not runtime.staff_service.remove_staff(&"heroine_anri").is_success(), "registered UNIQUE heroine removal refused")
|
||||
+ _expect(not runtime.staff_service.retire_staff(&"heroine_anri").is_success(), "registered UNIQUE heroine retirement refused")
|
||||
+ var retained: Dictionary = runtime.create_snapshot()
|
||||
+ _expect(runtime.restore_snapshot(retained), "refused departures survive save and readback")
|
||||
+ _expect(runtime.relationship_service.get_original_relationship(&"heroine_anri")[&"configured"], "departure never deletes original record")
|
||||
+ _extra_coverage(runtime)
|
||||
runtime.free()
|
||||
clock.free()
|
||||
state.free()
|
||||
print("Original relationship runtime: %d checks, %d failures" % [_checks, _failures])
|
||||
quit(_failures)
|
||||
+
|
||||
+
|
||||
+func _reject_runtime(runtime: Variant, candidate: Dictionary) -> void:
|
||||
+ var before := var_to_bytes(runtime.create_snapshot())
|
||||
+ var service: Variant = runtime.relationship_service
|
||||
+ var service_bytes := var_to_bytes(service.create_snapshot())
|
||||
+ var input := var_to_bytes(candidate)
|
||||
+ _expect(not runtime.restore_snapshot(candidate), "malformed candidate rejected atomically")
|
||||
+ _expect(runtime.relationship_service == service and var_to_bytes(service.create_snapshot()) == service_bytes, "rejection preserves prior service identity and bytes")
|
||||
+ _expect(var_to_bytes(runtime.create_snapshot()) == before, "rejection preserves whole Runtime bytes")
|
||||
+ _expect(var_to_bytes(candidate) == input, "rejection preserves all input bytes")
|
||||
+
|
||||
+
|
||||
+func _extra_coverage(runtime: Variant) -> void:
|
||||
+ var candidate: Dictionary = runtime.create_snapshot()
|
||||
+ var legacy := Fixtures.legacy_fixture()
|
||||
+ candidate[&"systems"][&"runtime"][&"relationships"] = legacy
|
||||
+ # GuestService's legitimate departure representation, retained after active removal.
|
||||
+ candidate[&"systems"][&"runtime"][&"guests"][&"persistent_records"].append({
|
||||
+ &"guest_id": "departed", &"guest_type_id": "traveler", &"debt_gold": 0,
|
||||
+ &"unresolved_incident_ids": [], &"outstanding_reservation_ids": []})
|
||||
+ candidate[&"systems"][&"runtime"][&"progression"][&"metrics"][&"guest_lifetime"] += 1
|
||||
+ var input := var_to_bytes(candidate)
|
||||
+ _expect(runtime.restore_snapshot(candidate), "int v1 and departed persistent guest relationships accepted")
|
||||
+ _expect(var_to_bytes(candidate) == input, "accepted v1 Runtime input unchanged")
|
||||
+ for change: Dictionary in [{&"metric_id": &"affection", &"delta": 2}, {&"metric_id": &"dependence", &"delta": 3}]:
|
||||
+ change[&"relationship_id"] = &"aliases"
|
||||
+ _expect(runtime.submit_command(&"change_relationship", change).is_success(), "alias changes use public Runtime command")
|
||||
+ var saved: Dictionary = runtime.create_snapshot()
|
||||
+ var raw: Dictionary = saved[&"systems"][&"runtime"][&"relationships"][&"legacy"]["relationships"][0]
|
||||
+ _expect(raw["metrics"]["affection"] == 43 and raw["metrics"]["dependence"] == 22, "Runtime merges canonical mutations")
|
||||
+ _expect(raw["metrics"]["affinity"] == 41 and raw["metrics"]["obligation"] == 19, "Runtime keeps raw aliases")
|
||||
+ _expect(raw["extra"] == ["raw", 2.5] and raw["metrics"]["extension"] == {"opaque": [3, "keep"]}, "Runtime keeps unknown facts")
|
||||
+ var saved_bytes := var_to_bytes(saved)
|
||||
+ _expect(runtime.restore_snapshot(saved), "mutated v2 Runtime restores and rebinds")
|
||||
+ _expect(var_to_bytes(runtime.create_snapshot()) == saved_bytes, "v2 Runtime preserves raw bytes and order")
|
||||
+ _expect(runtime.relationship_service.get_relationship(&"aliases").get_metric(&"affection") == 43 and runtime.relationship_service.get_relationship(&"aliases").get_metric(&"dependence") == 22, "Runtime canonical precedence survives rebind")
|
||||
+ for key: StringName in [&"affection", &"lust", &"netorare"]:
|
||||
+ _expect(runtime.relationship_service.get_original_relationship(&"heroine_anri")[key] == null, "Runtime old axes leave original unset")
|
||||
+ _expect(runtime.relationship_service.register_original_relationship(Fixtures.fixture()).is_success(), "rebound service accepts original")
|
||||
+ saved = runtime.create_snapshot()
|
||||
+ for kind: String in ["numeric_string", "metrics_container", "duplicate", "version_type", "unknown_version", "projection", "non_heroine", "counterpart", "non_staff", "corrupt"]:
|
||||
+ var bad := saved.duplicate(true)
|
||||
+ var relationships: Dictionary = bad[&"systems"][&"runtime"][&"relationships"]
|
||||
+ match kind:
|
||||
+ "numeric_string", "metrics_container":
|
||||
+ if kind == "numeric_string":
|
||||
+ relationships[&"legacy"]["relationships"][0]["metrics"]["affection"] = "50"
|
||||
+ else:
|
||||
+ relationships[&"legacy"]["relationships"][0]["metrics"] = []
|
||||
+ relationships[&"relationships"] = relationships[&"legacy"]["relationships"].duplicate(true)
|
||||
+ "duplicate":
|
||||
+ relationships[&"original_relationships"].append(Fixtures.fixture())
|
||||
+ "version_type":
|
||||
+ relationships[&"version"] = "2"
|
||||
+ "unknown_version":
|
||||
+ relationships[&"version"] = 3
|
||||
+ "projection":
|
||||
+ relationships[&"relationships"] = []
|
||||
+ "non_heroine":
|
||||
+ var rival_staff := StaffRuntime.new().configure(&"rival_rai", &"influencer", {&"employment_kind": &"UNIQUE"})
|
||||
+ _expect(rival_staff.is_valid(), "non-heroine rejection fixture is valid UNIQUE staff")
|
||||
+ bad[&"systems"][&"runtime"][&"staff"][&"staff"].append(rival_staff.create_snapshot())
|
||||
+ relationships[&"original_relationships"][0]["subject_id"] = "rival_rai"
|
||||
+ relationships[&"original_relationships"][0]["rival_id"] = "heroine_anri"
|
||||
+ var registry := StaffService.new()
|
||||
+ _expect(registry.register_staff(rival_staff).is_success(), "non-heroine UNIQUE exists in identity fixture registry")
|
||||
+ _expect(Fixtures.Model.from_snapshot(relationships[&"original_relationships"][0]) != null, "non-heroine payload passes structural model validation")
|
||||
+ _expect(not runtime._original_relationship_identities_valid(relationships[&"original_relationships"][0], registry, runtime.mvp_content_catalog), "catalog rejects non-heroine UNIQUE subject specifically")
|
||||
+ "counterpart":
|
||||
+ relationships[&"original_relationships"][0]["rival_id"] = "rival_other"
|
||||
+ "non_staff":
|
||||
+ relationships[&"original_relationships"][0]["subject_id"] = "departed"
|
||||
+ "corrupt":
|
||||
+ relationships[&"original_relationships"][0].erase("lust")
|
||||
+ _reject_runtime(runtime, bad)
|
||||
diff --git a/tools/regression/suite-policy.json b/tools/regression/suite-policy.json
|
||||
index 63d34d8..6604a3d 100644
|
||||
--- a/tools/regression/suite-policy.json
|
||||
+++ b/tools/regression/suite-policy.json
|
||||
@@ -2,14 +2,14 @@
|
||||
"schema_version": 1,
|
||||
"suite_policies": {
|
||||
"tests/s4/test_original_relationship_model.gd": {
|
||||
- "source_sha256": "ad509f5fb67ab91e1d1d99a08af51e4695e411b9a2541ea63ee22b1555cb0924",
|
||||
+ "source_sha256": "c74b243dbbb54dba9bbc165e29508cca910f0beb63e32d25ebbb8ea606dccce9",
|
||||
"ordinary_cleanup_prepared": false,
|
||||
"ordinary_control_labels": [],
|
||||
"old_bounded_teardown": false,
|
||||
"new_audio_QA": false
|
||||
},
|
||||
"tests/s4/test_original_relationship_runtime.gd": {
|
||||
- "source_sha256": "374f3d31a65d2f2b62dfef29721c8c838575b856f4fa5a0bca31c6ba4c7e09dd",
|
||||
+ "source_sha256": "349271cc8b8a4750ef339ecac6905852bd7a5d006cca68ee10d5d0c737f426de",
|
||||
"ordinary_cleanup_prepared": false,
|
||||
"ordinary_control_labels": [],
|
||||
"old_bounded_teardown": false,
|
||||
diff --git a/tools/run_p18_relationship_tests.py b/tools/run_p18_relationship_tests.py
|
||||
index c520311..925ccca 100644
|
||||
--- a/tools/run_p18_relationship_tests.py
|
||||
+++ b/tools/run_p18_relationship_tests.py
|
||||
@@ -199,6 +199,7 @@ def seal(out, refs, engine, contract_sha):
|
||||
source = content_map(refs)
|
||||
shared.write(out / 'receipt.json', {
|
||||
'schema': 'guarded-p18-v2', 'source_map': source,
|
||||
+ 'parse_checks': len([name for name in refs if name.endswith('.gd')]),
|
||||
'source_map_sha256': digest(source),
|
||||
'project_settings_sha256': source['project.godot']['sha256'],
|
||||
'marker_contract_sha256': contract_sha,
|
||||
@@ -281,8 +282,10 @@ def verify(out, refs, markers, contracts, policy, contract_sha, parse_paths):
|
||||
require(engine and shared.sha(engine) == start['engine_sha256']
|
||||
== manifest['engine_sha256'] == files['engine.bin']['sha256'],
|
||||
'current native engine and retained bytes pinned')
|
||||
- require(read('parse-paths.json') == parse_paths and len(parse_paths) == 308,
|
||||
- 'all 308 exact source parse paths')
|
||||
+ derived_paths = ['res://' + name for name in sorted(refs) if name.endswith('.gd')]
|
||||
+ require(read('parse-paths.json') == parse_paths == derived_paths
|
||||
+ and manifest['parse_checks'] == len(derived_paths),
|
||||
+ 'all exact source parse paths and receipt count')
|
||||
require(end['passed'] is True and end['engine_ended'] is True and end['error'] is None
|
||||
and end['cleanup_errors'] == [] and end['guard_errors'] == [],
|
||||
'finished successful collection without guard/cleanup error')
|
||||
@@ -463,8 +466,9 @@ def verify(out, refs, markers, contracts, policy, contract_sha, parse_paths):
|
||||
else:
|
||||
require(row['controlled_environment'] == {}, 'fresh import environment')
|
||||
if phase == 'parse':
|
||||
- require(re.findall(r'(?m)^FULL_DEPENDENCY_PARSE checks=([0-9]+) failures=([0-9]+)\n', raw)
|
||||
- == [('308', '0')] and row['actual_parse_checks'] == 308, 'unique all-script parse marker')
|
||||
+ require(len(re.findall(r'(?m)^FULL_DEPENDENCY_PARSE\b', raw)) == 1
|
||||
+ and re.findall(r'(?m)^FULL_DEPENDENCY_PARSE checks=([0-9]+) failures=([0-9]+)\n', raw)
|
||||
+ == [(str(len(parse_paths)), '0')] and row['actual_parse_checks'] == len(parse_paths), 'unique all-script parse marker')
|
||||
if test:
|
||||
parsed = markers.parse_raw(test, raw, contracts, source[test]['sha256'])
|
||||
require(parsed['passed'] and row['completion'] == parsed
|
||||
@@ -492,7 +496,7 @@ def verify(out, refs, markers, contracts, policy, contract_sha, parse_paths):
|
||||
'whole raw evidence unchanged during verification')
|
||||
require(content_map(shared.source_refs(shared.HERE.parent)) == source,
|
||||
'whole current source unchanged during verification')
|
||||
- print(json.dumps({'verified': True, 'actual_suites': 7, 'parse_checks': 308,
|
||||
+ print(json.dumps({'verified': True, 'actual_suites': 7, 'parse_checks': len(parse_paths),
|
||||
'actual_checks': end['actual_all_roster_checks'], 'engines_started': 0,
|
||||
'formal_acceptance': False, 'original_unit_complete': False}))
|
||||
return 0
|
||||
@@ -536,7 +540,6 @@ def main(argv=None):
|
||||
require(contracts[test]['source_sha256_at_schema_audit'] == refs[test]['sha256'],
|
||||
'new strict source-hashed marker')
|
||||
parse_paths = ['res://' + name for name in sorted(refs) if name.endswith('.gd')]
|
||||
- require(len(parse_paths) == 308, 'exact expected parse count')
|
||||
if args.prepare_only:
|
||||
ancestor = out.parent
|
||||
while not ancestor.exists():
|
||||
@@ -545,7 +548,7 @@ def main(argv=None):
|
||||
size = sum(p['bytes'] for p in refs.values())
|
||||
print(json.dumps({'roster': ROSTER, 'source_files': len(refs), 'source_bytes': size,
|
||||
'free_bytes': free, 'conservative_required_bytes': size + shared.MIN_SPACE,
|
||||
- 'capacity_available': free >= size + shared.MIN_SPACE, 'parse_paths': 308,
|
||||
+ 'capacity_available': free >= size + shared.MIN_SPACE, 'parse_paths': len(parse_paths),
|
||||
'future_native_phases': 9, 'actual_checks': None, 'engines': 0, 'stages': 0,
|
||||
'formal_acceptance': False}))
|
||||
return 0
|
||||
BIN
Binary file not shown.
+11
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"original_path": "docs/plans/original-completion-2026-10-02/execution/p18-01-resume-20261015/PRESERVED_REPAIR4_DIFF.patch",
|
||||
"original_bytes": 32892,
|
||||
"original_sha256": "b4d570884c1d0eb90c6780c7346419cb7e3f43780ec590bb472822f087e3f72d",
|
||||
"compressed_path": "docs/plans/original-completion-2026-10-02/execution/p18-01-resume-20261015/PRESERVED_REPAIR4_DIFF.patch.gz",
|
||||
"compressed_bytes": 10186,
|
||||
"compressed_sha256": "73f67bd136833919e20bbc6d3e0dc1c31524e8956eb71a75d3ceafc94e55a402",
|
||||
"verified_content_preserved": true,
|
||||
"raw_original_also_in_git_commit": "1a6cc43f2efe5c0128d7677c6ee5dd17dcb15ce2",
|
||||
"reason": "Raw patch contains intentional diff context prefixes followed by tabs, which git diff --check misinterprets when patch is added as text. Preserve exact raw artifact in verified gzip, not whitespace-edit proof."
|
||||
}
|
||||
Reference in New Issue
Block a user