Capture current mission, AI, map and administration development

Include the current addon, terrain integrations, tools, regression tests and recorded development evidence.
This commit is contained in:
2026-10-13 22:52:53 +02:00
parent a951a96edc
commit 6a667d8deb
8104 changed files with 2124945 additions and 2061 deletions
@@ -0,0 +1,18 @@
{
"addons/core/Scripts/Game/ERR/00_Core/ERR_Observer.c": {
"working_sha256": "4acbd2b23803f1fde3701fbddffd56edd83693196f0be2fe99f3be9b79a12b3d",
"index_sha256": "31d08123fa9f49dde380354bd839d1a9deb175cdb940311b4a64dd56eeb04ed7"
},
"tests/test_hint_delivery.py": {
"working_sha256": "1ec462cea5890379ad33ea3f16591896e085d1a30fd1af4a3411b14e5dc8acc3",
"index_sha256": "1ec462cea5890379ad33ea3f16591896e085d1a30fd1af4a3411b14e5dc8acc3"
},
"tests/test_observer_ownership.py": {
"working_sha256": "879c6185d834c03f8d62e7e35359c8bb5f6821f6dde03c73957be2ba614947c6",
"index_sha256": "879c6185d834c03f8d62e7e35359c8bb5f6821f6dde03c73957be2ba614947c6"
},
"addons/core/Scripts/Game/ERR/00_Core/ERR_UnconsciousCameraProbe.c": {
"working_sha256": "7d5ff3389ee806ac213db5be3a68288b3399142ccc29806df0d65b34a0b2fc3c",
"index_sha256": "7b9308c9c35413fa76a7bdc8923f99785fb393ad9d06ba4b36df4d8aa29e574b"
}
}
@@ -0,0 +1,159 @@
--- a/addons/core/Scripts/Game/ERR/00_Core/ERR_Observer.c
+++ b/addons/core/Scripts/Game/ERR/00_Core/ERR_Observer.c
@@ -228,6 +228,7 @@
protected bool m_ERRObserverReady;
protected ERR_ObserverReason m_ERRShownObserverReason;
protected bool m_ERRShownObserverAvailable;
+ protected string m_ERRShownRecoveryStatus;
protected float m_ERRLastCycle = -1000;
protected IEntity m_ERRRecoveryBody;
protected float m_ERRRecoveryDeadline = -1;
@@ -385,14 +386,18 @@
// The server-selected replicated ID distinguishes no target from a target
// whose local entity is still streaming. Never imply no survivors from delay.
bool survivorAvailable = m_ERRObservedId != RplId.Invalid();
- if (ERR_ObserverGuidance.Refresh(m_ERRObserverReady, m_ERRShownObserverReason, m_ERRObserverReason, m_ERRShownObserverAvailable, survivorAvailable))
+ string recovery = mode.RecoveryStatus();
+ if (ERR_ObserverGuidance.Refresh(m_ERRObserverReady, m_ERRShownObserverReason, m_ERRObserverReason, m_ERRShownObserverAvailable, survivorAvailable) || m_ERRShownRecoveryStatus != recovery)
{
SCR_HintManagerComponent hints = SCR_HintManagerComponent.GetInstance();
if (!hints) return; // retry on the existing monitor; do not lose the first hint
- if (!hints.ShowCustom(ERR_ObserverGuidance.Message(m_ERRObserverReason, survivorAvailable), "Spectating", 12)) return;
+ string message = ERR_ObserverGuidance.Message(m_ERRObserverReason, survivorAvailable);
+ if (!recovery.IsEmpty()) message += "\n" + recovery;
+ if (!hints.ShowCustom(message, "Spectating", 12)) return;
m_ERRObserverReady = true;
m_ERRShownObserverReason = m_ERRObserverReason;
m_ERRShownObserverAvailable = survivorAvailable;
+ m_ERRShownRecoveryStatus = recovery;
}
}
// Track the actual local body even when an editor/other camera owns the view.
@@ -423,6 +428,7 @@
protected void ERR_CloseObserverCamera()
{
m_ERRObserverReady = false;
+ m_ERRShownRecoveryStatus = "";
if (!m_ERRObserverCamera) return;
if (GetGame().GetPlayerController() == this) m_ERRObserverCamera.TrySwitchToControlledEntityCamera();
m_ERRObserverCamera.Terminate();
--- a/tests/test_hint_delivery.py
+++ b/tests/test_hint_delivery.py
@@ -4,6 +4,7 @@
import os
GUIDE=Path(os.environ.get('ERR_GUIDE_SOURCE', ROOT/'addons/core/Scripts/Game/ERR/00_Core/ERR_PlayerGuide.c'))
OBSERVER=Path(os.environ.get('ERR_OBSERVER_SOURCE', ROOT/'addons/core/Scripts/Game/ERR/00_Core/ERR_Observer.c'))
+PROBE=Path(os.environ.get('ERR_OBSERVER_PROBE_SOURCE', ROOT/'addons/core/Scripts/Game/ERR/00_Core/ERR_UnconsciousCameraProbe.c'))
def cpp(text):
text=text.replace(' = null;', ' = nullptr;').replace('protected ', '').replace('override ', '').replace('super.OnControlledEntityChanged(from, to);', 'nativeChanges++;')
for name in ('ERR_GameMode','ERR_Phase','ERR_ObserverReason','ERR_Actors','ERR_ObjectiveText','SCR_HintManagerComponent','ERR_ObserverGuidance','ERR_ObserverRules','Resource','ETransformMode','SCR_ManualCamera','SCR_RespawnSystemComponent','RplId'):
@@ -16,6 +17,7 @@
text=text.replace('GetGame().','GetGame()->').replace('EntitySpawnParams params = new EntitySpawnParams();','EntitySpawnParams params;')
return text
methods=cpp(body(GUIDE,'protected void ERR_StartGuide(')).replace('ERR_StartGuide()', 'StartActual()')+'\n'+cpp(body(GUIDE,'override void OnControlledEntityChanged('))+'\n'+cpp(body(GUIDE,'protected void ERR_UpdateGuide('))+'\n'+cpp(body(OBSERVER,'protected void ERR_UpdateObserverClient('))
+methods+='\n'+cpp(body(PROBE,'string ERR_ProbeAcceptedRecoveryStatus('))
guidance=cpp(body(OBSERVER,'static string Message('))+'\n'+cpp(body(OBSERVER,'static bool Refresh('))
run(r'''#include <cassert>
#include <string>
@@ -25,7 +27,7 @@
namespace ERR_ObserverReason {enum {NONE,SPENT_LIFE,LATE_EXTRACTION,SPAWN_UNAVAILABLE,UNCONSCIOUS};}
struct Actor {bool alive=true;bool unconscious=false;} actor;using IEntity=Actor*;
struct ERR_Actors {static bool Alive(Actor* a){return a&&a->alive;}static bool CanFight(Actor* a){return Alive(a)&&!a->unconscious;}};
-struct ERR_GameMode {int phase=ERR_Phase::PRISON;bool active=true;string message="prison";static ERR_GameMode* current;static ERR_GameMode* Get(){return current;}int Phase(){return phase;}bool Active(){return active;}} mode;
+struct ERR_GameMode {string recovery;string RecoveryStatus(){return recovery;}int phase=ERR_Phase::PRISON;bool active=true;string message="prison";static ERR_GameMode* current;static ERR_GameMode* Get(){return current;}int Phase(){return phase;}bool Active(){return active;}} mode;
ERR_GameMode* ERR_GameMode::current=&mode;
struct ERR_ObjectiveText {static string Description(ERR_GameMode* m,bool){return m->message;}static string Title(int){return "objective";}};
struct SCR_HintManagerComponent {static SCR_HintManagerComponent* current;bool accepts=true;int attempts=0,shown=0;string last;static SCR_HintManagerComponent* GetInstance(){return current;}bool ShowCustom(string text,string,int){attempts++;if(!accepts)return false;shown++;last=text;return true;}} hints;
@@ -57,9 +59,10 @@
Actor* controlled=&actor;int m_ERRObserverReason=ERR_ObserverReason::NONE;
SCR_ManualCamera* m_ERRObserverCamera=nullptr;Vec m_ERRObserverAnchor;
int m_ERRObservedId=1;bool m_ERRObserverReady=false;
+ string m_ERRShownRecoveryStatus;
int m_ERRShownObserverReason=0;bool m_ERRShownObserverAvailable=false;int closes=0;
Actor* GetControlledEntity(){return controlled;}int ERR_ObserverState(){return m_ERRObserverReason;}
- void ERR_CloseObserverCamera(){closes++;m_ERRObserverReady=false;m_ERRObserverCamera=nullptr;}
+ void ERR_CloseObserverCamera(){closes++;m_ERRObserverReady=false;m_ERRShownRecoveryStatus="";m_ERRObserverCamera=nullptr;}
'''+methods+r'''
};
void reset(){queue=Queue();gameAvailable=true;mode=ERR_GameMode();ERR_GameMode::current=&mode;hints=SCR_HintManagerComponent();SCR_HintManagerComponent::current=&hints;actor=Actor();Resource::current=&resource;manager.current=&camera;}
@@ -135,7 +138,30 @@
reset();Controller casualty;actor.unconscious=true;casualty.m_ERRObserverReason=ERR_ObserverReason::UNCONSCIOUS;
casualty.ERR_UpdateObserverClient();assert(casualty.m_ERRObserverCamera==&camera&&casualty.controlled==&actor&&hints.last.find("You are unconscious")!=string::npos);
casualty.ERR_UpdateObserverClient();assert(hints.shown==1);
- actor.unconscious=false;casualty.ERR_UpdateObserverClient();assert(!casualty.m_ERRObserverCamera&&casualty.closes==1&&hints.shown==1);
+ // The phase-guide explicitly excludes this server-designated unconscious
+ // observer; the existing observer monitor must deliver recovery/defeat facts.
+ int attemptsBeforeGuide=hints.attempts;casualty.ERR_UpdateGuide();assert(hints.attempts==attemptsBeforeGuide);
+ mode.recovery="Everyone is unconscious with no treatment or recovery progressing. The mission fails after 30 uninterrupted seconds in this state.";
+ casualty.ERR_UpdateObserverClient();assert(hints.shown==2&&hints.last.find(mode.recovery)!=string::npos&&casualty.ERR_ProbeAcceptedRecoveryStatus()==mode.recovery);
+ casualty.ERR_UpdateObserverClient();assert(hints.shown==2); // stable status never repeats
+ mode.recovery="Treatment or natural recovery is possible; the round continues.";hints.accepts=false;
+ casualty.ERR_UpdateObserverClient();assert(hints.shown==2&&casualty.m_ERRShownRecoveryStatus.find("30 uninterrupted")!=string::npos);
+ hints.accepts=true;casualty.ERR_UpdateObserverClient();assert(hints.shown==3&&hints.last.find(mode.recovery)!=string::npos);
+ mode.recovery="A conscious prisoner's reconnection may provide help.";SCR_HintManagerComponent::current=nullptr;
+ casualty.ERR_UpdateObserverClient();assert(hints.shown==3&&casualty.m_ERRShownRecoveryStatus.find("natural recovery")!=string::npos);
+ SCR_HintManagerComponent::current=&hints;casualty.ERR_UpdateObserverClient();assert(hints.shown==4&&hints.last.find(mode.recovery)!=string::npos);
+ mode.recovery="";casualty.ERR_UpdateObserverClient();assert(hints.shown==5&&casualty.m_ERRShownRecoveryStatus.empty()&&hints.last.find("reconnection")==string::npos);
+ casualty.ERR_UpdateObserverClient();assert(hints.shown==5);
+ // Waking closes the view; a new status cannot keep a recovered observer hint alive.
+ mode.recovery="remaining casualty warning";actor.unconscious=false;casualty.ERR_UpdateObserverClient();assert(!casualty.m_ERRObserverCamera&&casualty.closes==1&&hints.shown==5&&casualty.ERR_ProbeAcceptedRecoveryStatus().empty());
+ // An old controller cannot consume the replacement's recovery-status update.
+ reset();Controller oldCasualty;actor.unconscious=true;oldCasualty.m_ERRObserverReason=ERR_ObserverReason::UNCONSCIOUS;oldCasualty.ERR_UpdateObserverClient();
+ Controller newCasualty;newCasualty.m_ERRObserverReason=ERR_ObserverReason::UNCONSCIOUS;mode.recovery="recovery warning after reconnect";int beforeOld=hints.shown;
+ oldCasualty.ERR_UpdateObserverClient();assert(hints.shown==beforeOld&&oldCasualty.m_ERRShownRecoveryStatus.empty());
+ newCasualty.ERR_UpdateObserverClient();assert(hints.shown==beforeOld+1&&hints.last.find(mode.recovery)!=string::npos);
+ mode.active=false;mode.recovery="old round recovery";newCasualty.ERR_UpdateObserverClient();assert(hints.shown==beforeOld+1&&!newCasualty.m_ERRObserverReady&&newCasualty.m_ERRShownRecoveryStatus.empty());
+ // A later active round's first observer hint contains only its current fact.
+ mode.active=true;mode.recovery="new round recovery";newCasualty.ERR_UpdateObserverClient();assert(hints.shown==beforeOld+2&&hints.last.find("new round recovery")!=string::npos&&hints.last.find("old round recovery")==string::npos);
}
''')
for path,signature in ((GUIDE,'protected void ERR_UpdateGuide('),(OBSERVER,'protected void ERR_UpdateObserverClient(')):
--- a/tests/test_observer_ownership.py
+++ b/tests/test_observer_ownership.py
@@ -23,10 +23,10 @@
run(r'''#include <cassert>
#include <set>
#include <string>
-using string=std::string;
+struct string:std::string {using std::string::string;using std::string::operator=;string(std::string s):std::string(s){}bool IsEmpty()const{return empty();}};
struct Actor {bool alive=true,conscious=true;};using IEntity=Actor*;
struct ERR_Actors {static bool Alive(IEntity a){return a&&a->alive;}static bool CanFight(IEntity a){return Alive(a)&&a->conscious;}};
-struct ERR_GameMode {bool active=true;static ERR_GameMode*current;static ERR_GameMode*Get(){return current;}bool Active(){return active;}} mode;
+struct ERR_GameMode {string RecoveryStatus(){return "";}bool active=true;static ERR_GameMode*current;static ERR_GameMode*Get(){return current;}bool Active(){return active;}} mode;
ERR_GameMode*ERR_GameMode::current=&mode;
struct ERR_ObserverRules {static bool View(int reason,bool active,bool alive,bool conscious){return active&&((reason==4&&alive&&!conscious)||(reason!=0&&!alive));}};
struct ERR_ObserverGuidance {static bool Refresh(bool ready,int oldReason,int reason,bool oldAvailable,bool available){return !ready||oldReason!=reason||oldAvailable!=available;}static string Message(int,bool){return "spectating";}};
@@ -58,6 +58,7 @@
struct Controller{
IEntity actor=nullptr,m_ERRRecoveryBody=nullptr;float m_ERRRecoveryDeadline=-1;
bool m_ERRObserverMonitor=false,m_ERRObserverReady=false,m_ERRShownObserverAvailable=false;
+ string m_ERRShownRecoveryStatus;
int m_ERRObserverReason=0,m_ERRShownObserverReason=0,m_ERRObservedId=1,starts=0;
Vec m_ERRObserverAnchor;SCR_ManualCamera*m_ERRObserverCamera=nullptr;CameraBase*playerCamera=nullptr;
IEntity GetControlledEntity(){return actor;}CameraBase*GetPlayerCamera(){return playerCamera;}
--- a/addons/core/Scripts/Game/ERR/00_Core/ERR_UnconsciousCameraProbe.c
+++ b/addons/core/Scripts/Game/ERR/00_Core/ERR_UnconsciousCameraProbe.c
@@ -4,6 +4,7 @@
modded class SCR_PlayerController
{
protected IEntity m_ERRCameraProbeTarget;
+ string ERR_ProbeAcceptedRecoveryStatus() { return m_ERRShownRecoveryStatus; }
void ERR_ProbeCameraTarget(IEntity target) { m_ERRCameraProbeTarget = target; }
override IEntity ERR_ObservedEntity()
{
@@ -91,6 +92,7 @@
if (!ERR_CameraCheck(pc.GetControlledEntity() == m_ERRCameraBody && ERR_Actors.Alive(m_ERRCameraBody) && !ERR_Actors.CanFight(m_ERRCameraBody), "possession and living unconscious body unchanged")) return;
if (!ERR_CameraCheck(!pc.ERR_ObservedEntity() && vector.DistanceXZ(pc.ERR_ObserverAnchor(), m_ERRCameraBody.GetOrigin()) < 1, "no teammate falls back to the casualty body")) return;
if (!ERR_CameraCheck(playerInventory.Contains(m_ERRCameraWeapon), "enabled retention keeps the exact rifle with the unconscious player")) return;
+ if (!ERR_CameraCheck(!RecoveryStatus().IsEmpty() && RecoveryStatus().Contains("Treatment or natural recovery") && pc.ERR_ProbeAcceptedRecoveryStatus() == RecoveryStatus(), "accepted observer hint contains the current ongoing recovery status")) return;
vector point = ERR_Actors.Ground(m_ERRCameraBody.GetOrigin() + "25 0 25");
m_ERRCameraTarget = ERR_Actors.Spawn(Factions().PrisonerPrefab, point); Own(m_ERRCameraTarget);
if (!ERR_CameraCheck(m_ERRCameraTarget != null, "physical follow-target stand-in created")) return;
@@ -165,6 +167,7 @@
{
if (!ERR_CameraCheck(ERR_Actors.CanFight(m_ERRCameraBody) && pc.GetControlledEntity() == m_ERRCameraBody && pc.ERR_ObserverState() == ERR_ObserverReason.NONE && !pc.ERR_HasObserverCamera(), "native healing closes spectator and retains the original conscious player")) return;
if (!ERR_CameraCheck(GetGame().GetCameraManager().CurrentCamera() == pc.GetPlayerCamera(), "normal player camera restored")) return;
+ if (!ERR_CameraCheck(pc.ERR_ProbeAcceptedRecoveryStatus().IsEmpty(), "awakening clears the accepted observer recovery-status cache")) return;
if (m_ERRCameraOther) { m_ERRCameraOther.Terminate(); delete m_ERRCameraOther; m_ERRCameraOther = null; }
if (!ERR_CameraCheck(character.GetWeaponManagerComponent().GetCurrentWeapon() && character.GetWeaponManagerComponent().GetCurrentWeapon().GetOwner() == m_ERRCameraWeapon, "revival equips the same rifle without ground pickup")) return;
if (!ERR_CameraCheck(Settings().Set("keepWeaponOnUnconscious", 0), "retention disabled through persistent setting")) return;
@@ -0,0 +1 @@
PASS: actual guide ownership and scoped timer cancellation, current-controller preservation/reacquisition, hint retry/dedup and observer boundaries; native UI not exercised
@@ -0,0 +1 @@
PASS: actual guide ownership and scoped timer cancellation, current-controller preservation/reacquisition, hint retry/dedup and observer boundaries; native UI not exercised
@@ -0,0 +1 @@
PASS: actual observer monitor/recovery/cleanup rejects retained old controller, preserves new camera/editor/hints/timer, restarts once and retains normal recovery; native effects modeled from SDK, no runtime claim
@@ -0,0 +1,83 @@
{
"verdict": "PASS for bounded native accepted-hint lifecycle proof; retained engine errors prevent error-free-engine claim",
"evidence": "/home/jakub/devel/escapistsrereintroduced/.build/recovery-warning-native-1130/docs/evidence/native-2026-10-02-recovery-warning",
"native_report": {
"log": "docs/evidence/native-2026-10-02-recovery-warning/console.log.txt",
"smoke_completions": 1,
"assertions_passed": 29,
"script_or_runtime_failures": [],
"engine_errors": [
"11:40:50.703 RESOURCES (E): Wrong GUID/name for resource @\"{0AFDD005CD669892}Language/rhs_localization.st\" in property \"StringTableSource\"",
"11:40:50.703 RESOURCES (E): Wrong GUID/name for resource @\"{41FA2A08B6971D4C}Language/wcs_armament_localization.st\" in property \"StringTableSource\"",
"11:40:50.703 RESOURCES (E): Wrong GUID/name for resource @\"{84270E09F7A4A7A0}Language/wcs_spacecore_localization.st\" in property \"StringTableSource\"",
"11:40:50.704 RESOURCES (E): Wrong GUID/name for resource @\"{E50E557B468C7073}Language/wcs_bmp1_localization.st\" in property \"StringTableSource\"",
"11:41:29.086 WORLD (E): Unknown keyword/data 'Parent' at offset 35955(0x8c73)",
"11:41:29.086 WORLD (E): Unknown keyword/data 'Parent' at offset 36915(0x9033)",
"11:41:29.087 WORLD (E): Unknown keyword/data 'Parent' at offset 42646(0xa696)",
"11:41:29.087 WORLD (E): Unknown keyword/data 'Parent' at offset 43645(0xaa7d)",
"11:41:29.087 WORLD (E): Unknown keyword/data 'Parent' at offset 44617(0xae49)",
"11:41:29.087 WORLD (E): Unknown keyword/data 'Parent' at offset 45590(0xb216)",
"11:41:29.181 WORLD (E): Unknown keyword/data 'SlidingTrackMaterial' at offset 19441(0x4bf1)",
"11:41:35.590 WORLD (E): Unknown keyword/data 'm_HardLandingVelocity' at offset 133(0x85)",
"11:41:35.590 WORLD (E): Unknown keyword/data 'm_DeathLandingVelocity' at offset 163(0xa3)"
],
"missing_end_screen": [],
"scope": "scripted local Workbench integration only; no multiplayer, piloting or FPS claim"
},
"assertion_witnesses": [
{
"line": 578,
"text": "11:41:52.276 SCRIPT : ERR SMOKE PASS: unconscious camera accepted observer hint contains the current ongoing recovery status"
},
{
"line": 599,
"text": "11:42:03.883 SCRIPT : ERR SMOKE PASS: unconscious camera awakening clears the accepted observer recovery-status cache"
}
],
"source": {
"manifest_entries": 407,
"launch_end_manifests_equal": true,
"frozen_manifest_mismatches": [],
"proposal_observer_and_probe_match_frozen_and_current": true
},
"cleanup": {
"result": "smoke-completed",
"result_before_cleanup": "smoke-completed",
"cleanup_confirmed": true,
"cleanup_report": {
"cleanup_confirmed": true,
"idle_verified": true,
"errors": [],
"orphan_cleanup": {
"attempted": true,
"report": {
"scope": "/tmp/erri-headless-compat/pfx",
"candidates": [],
"term_sent": [],
"kill_sent": [],
"cleared": true
}
}
},
"source_unchanged_after_run": true,
"external_addons_unchanged_after_run": true,
"runner_error": null
},
"focused_tests": {
"test_observer_ownership.py": {
"exit_code": 0,
"log": "/tmp/erri-observer-recovery-warning/native-review-test_observer_ownership.py.log"
},
"test_hint_delivery.py": {
"exit_code": 0,
"log": "/tmp/erri-observer-recovery-warning/native-review-test_hint_delivery.py.log"
}
},
"ownership_test_review": "Only string IsEmpty adapter, empty RecoveryStatus mode stub and cache field added. Existing ownership cases/assertions unchanged. Actual StartMonitor/UpdateObserverClient/UpdateRecoveryCamera/CloseObserverCamera and binder still exercised.",
"limitations": [
"Local Workbench scripted fixture only; no multiplayer proof.",
"Accepted ShowCustom path and current replicated recovery status verified, not full-resolution readability.",
"Thirteen retained external resource/world engine errors remain in report.",
"Snapshot predates subsequent launcher changes; no claim those changes were covered."
]
}
@@ -0,0 +1 @@
PASS: actual observer monitor/recovery/cleanup rejects retained old controller, preserves new camera/editor/hints/timer, restarts once and retains normal recovery; native effects modeled from SDK, no runtime claim
@@ -0,0 +1,19 @@
check: /tmp/err-policy-59mygi6e/check.cpp:239: int main(): Assertion `hints.shown==2&&hints.last.find(mode.recovery)!=string::npos&&casualty.ERR_ProbeAcceptedRecoveryStatus()==mode.recovery' failed.
Traceback (most recent call last):
File "/tmp/erri-observer-recovery-warning/tests/test_hint_delivery.py", line 22, in <module>
run(r'''#include <cassert>
~~~^^^^^^^^^^^^^^^^^^^^^^^
#include <string>
^^^^^^^^^^^^^^^^^
...<141 lines>...
}
^
''')
^^^^
File "/home/jakub/devel/escapistsrereintroduced/tests/policy_harness.py", line 14, in run
subprocess.run([str(p/'check')],check=True)
~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/lib/python3.14/subprocess.py", line 578, in run
raise CalledProcessError(retcode, process.args,
output=stdout, stderr=stderr)
subprocess.CalledProcessError: Command '['/tmp/err-policy-59mygi6e/check']' died with <Signals.SIGABRT: 6>.
@@ -0,0 +1,55 @@
{
"verdict": "Concrete source presentation defect reproduced; minimal proposal fixes it",
"before": "All-unconscious status already replicated from RecoveryService contains30s defeat/recovery/reconnect facts. PlayerGuide skips server-designated observers; Observer ignores RecoveryStatus and deduplicates only reason/target availability, so no automatic warning reaches affected unconscious players.",
"after": "Existing observer hint appends current replicated recovery status and refreshes only when reason/availability/status changed. Cache advances only on accepted ShowCustom; close clears status; existing ownership and active/conscious gates remain unchanged.",
"production_files": [
"addons/core/Scripts/Game/ERR/00_Core/ERR_Observer.c"
],
"test_files": [
"tests/test_hint_delivery.py",
"tests/test_observer_ownership.py"
],
"baseline_red": "red.log:actual method compiles, fails assertion requiring updated medical warning while observer reason/target unchanged",
"proposal_green": [
"green.log",
"ownership-green.log"
],
"coverage": [
"stable message dedup",
"status changes stalled\u2192recovering\u2192reserved help\u2192clear",
"rejected hint/missing manager retry without cache advance",
"ordinary guide cannot deliver this unconscious state",
"wake closes presentation",
"old controller cannot display replacement status",
"inactive round stops hints and clears cache",
"new round uses only current status"
],
"medical_policy_changed": false,
"native_launched": false,
"target_changed": false,
"native_limits": "Getter proves accepted native hint path/state only; no claim of full-resolution readability, manual UI or multiplayer. Existing fixture itself arranges treatment/knockout, unchanged.",
"manifest": {
"addons/core/Scripts/Game/ERR/00_Core/ERR_Observer.c": {
"working_sha256": "4acbd2b23803f1fde3701fbddffd56edd83693196f0be2fe99f3be9b79a12b3d",
"index_sha256": "31d08123fa9f49dde380354bd839d1a9deb175cdb940311b4a64dd56eeb04ed7",
"proposal_sha256": "682aa5549470c69fc72d261fbfcf9f7a04a41a66e0101b269ccc00bc282ad05d"
},
"tests/test_hint_delivery.py": {
"working_sha256": "1ec462cea5890379ad33ea3f16591896e085d1a30fd1af4a3411b14e5dc8acc3",
"index_sha256": "1ec462cea5890379ad33ea3f16591896e085d1a30fd1af4a3411b14e5dc8acc3",
"proposal_sha256": "6066e2b00e54553ac298a3f9b37060a23851f5ca5d4f98eb25e502b2c44240a6"
},
"tests/test_observer_ownership.py": {
"working_sha256": "879c6185d834c03f8d62e7e35359c8bb5f6821f6dde03c73957be2ba614947c6",
"index_sha256": "879c6185d834c03f8d62e7e35359c8bb5f6821f6dde03c73957be2ba614947c6",
"proposal_sha256": "8e485c3d2e6ae90df82cbc7829996138db8e9a5a4a42a5e2bcaa3bffa61368de"
},
"addons/core/Scripts/Game/ERR/00_Core/ERR_UnconsciousCameraProbe.c": {
"working_sha256": "7d5ff3389ee806ac213db5be3a68288b3399142ccc29806df0d65b34a0b2fc3c",
"index_sha256": "7b9308c9c35413fa76a7bdc8923f99785fb393ad9d06ba4b36df4d8aa29e574b",
"proposal_sha256": "66b1c4591b0ecc67cb08418a115bb02ed2b0168b7ae970f1e0a1ea335c59dc5b"
}
},
"apply_check": true,
"workbench_only_fixture": "ERR_UnconsciousCameraProbe adds accepted-cache getter and2assertions: stage1 nonempty actual RecoveryStatus containing treatment/natural-recovery branch equals accepted hint cache; stage4 awakening clears cache. No synthetic policy/timing change."
}
@@ -0,0 +1,22 @@
{
"addons/core/Scripts/Game/ERR/00_Core/ERR_Observer.c": {
"working_sha256": "4acbd2b23803f1fde3701fbddffd56edd83693196f0be2fe99f3be9b79a12b3d",
"index_sha256": "31d08123fa9f49dde380354bd839d1a9deb175cdb940311b4a64dd56eeb04ed7",
"proposal_sha256": "682aa5549470c69fc72d261fbfcf9f7a04a41a66e0101b269ccc00bc282ad05d"
},
"tests/test_hint_delivery.py": {
"working_sha256": "1ec462cea5890379ad33ea3f16591896e085d1a30fd1af4a3411b14e5dc8acc3",
"index_sha256": "1ec462cea5890379ad33ea3f16591896e085d1a30fd1af4a3411b14e5dc8acc3",
"proposal_sha256": "6066e2b00e54553ac298a3f9b37060a23851f5ca5d4f98eb25e502b2c44240a6"
},
"tests/test_observer_ownership.py": {
"working_sha256": "879c6185d834c03f8d62e7e35359c8bb5f6821f6dde03c73957be2ba614947c6",
"index_sha256": "879c6185d834c03f8d62e7e35359c8bb5f6821f6dde03c73957be2ba614947c6",
"proposal_sha256": "8e485c3d2e6ae90df82cbc7829996138db8e9a5a4a42a5e2bcaa3bffa61368de"
},
"addons/core/Scripts/Game/ERR/00_Core/ERR_UnconsciousCameraProbe.c": {
"working_sha256": "7d5ff3389ee806ac213db5be3a68288b3399142ccc29806df0d65b34a0b2fc3c",
"index_sha256": "7b9308c9c35413fa76a7bdc8923f99785fb393ad9d06ba4b36df4d8aa29e574b",
"proposal_sha256": "66b1c4591b0ecc67cb08418a115bb02ed2b0168b7ae970f1e0a1ea335c59dc5b"
}
}