"""Auditable observation coverage. Missing lifecycle hooks remain explicit gaps.""" import hashlib import json from pathlib import Path from . import native from .debug_conditions import FIELDS def registry(): root=native.ROOT source=lambda name: dict(path=str(root/'scripts'/name),sha256=hashlib.sha256((root/'scripts'/name).read_bytes()).hexdigest()) hooks=[] for id,category,filename,method,stage,scope,evidence in ( ('H01','actor_state','encounters.ws.in','PDCombatBatch','explicit_getter','Native health, target, CombatData flags, exposed ticket owners; unavailable getters stay marked.','encounter-validation.json'), ('H02','attack_attempt','observations.ws.in','CActor.DoAttack','entry','Attack invocation and animation name, not AI move selection or commitment.','step-equivalence-validation.json'), ('H03','contact_damage','observations.ws.in','W3DamageManagerProcessor.ProcessAction','entry/calculation/exit','Contact and recorded damage channels/health; asynchronous effects may lack attack correlation.','step-equivalence-validation.json'), ): hooks.append(dict(id=id,category=category,status='qualified',source=source(filename),method=method, stage=stage,granularity='actor/call',clock='native engine time and script tick; same-tick cross-hook order not inferred', passive_proof='Existing observer code forwards the original wrapped call once; no eligibility/random sampling added.', forward_owner='existing installed observer',scope=scope,evidence=[str(root/evidence)])) candidates=( ('H04','move_activation','tasks/btTaskAttack.ws','CBTTaskAttack.OnActivate', 'Task-instance identity and activation ordinal are not exposed by the installed bridge.', ['move initiation','move-use frequency','state-to-choice pairs']), ('H05','projectile_emission','tasks/combat/btTaskProjectileAttack.ws','CBTTaskProjectileAttack.CreateAndShootProjectile', 'Original wasShot/projectile/resource result has no shipped passive observer.', ['mud emission success','projectile attribution']), ('H06','move_termination','tasks/btTaskAttack.ws','CBTTaskAttack.OnDeactivate', 'Deactivation does not prove success; native/latent completion needs separately qualified correlation.', ['move completion','commitment duration']), ('H07','teleport_submerge','tasks/movement/btTaskTeleport.ws','concrete CBTTaskTeleport subclass and bound submerged-variable handler', 'Callback-only wrappers compiled and one completion notification was observed on build 6c476883c5187960; animation callback remains unexercised. See teleport-callback-validation.json. They expose neither relocation result nor concrete class/tree identity. Actual teleport result remains unqualified.', ['submerge initiation','submerge completion']), ('H08','eligibility_cooldown',None,'original-produced selector/cooldown result only', 'No passive result observation is qualified. IsAvailable/score/chance functions must not be called for inspection.', ['native choice probability','cooldown availability']), ) for id,category,relative,method,reason,blocked in candidates: path=native.legacy.manage.GAME/'content/content0/scripts/game/behavior_tree'/relative if relative else None hooks.append(dict(id=id,category=category,status='source_candidate' if path and path.exists() else 'unsupported', source=dict(path=str(path) if path else None,sha256=hashlib.sha256(path.read_bytes()).hexdigest() if path and path.exists() else None), method=method,stage='not_enabled',granularity='unavailable',clock='unavailable', passive_proof=None,forward_owner=None,reason=reason,blocked_analyses=blocked, narrow_next_hook=method,evidence=['model/experiments/hag_attack_activation_probe.ws','model/hag-sim/native-assets/hag-attack-script-completion-path.json'])) value=dict(schema_version=1,kind='debugger_capabilities',backend='host_step',exact_native_breakpoint=False, crossbow_input=dict(status='unqualified', transport='virtual_right_shoulder', scope='unchanged_standard_on_foot_bindings_and_equipped_selected_crossbow', ticks_minimum=2,ticks_maximum=120, selection_setup='explicit_quickslot_receipt_not_equipment', input_is_not_shot=True, impact_available=False, evidence=['CROSSBOW_INPUT.md']), request_algorithm=dict( status='unqualified', algorithms=['approach'], modes=['observe','score'], selected_actor_minimum=1, selected_actor_maximum=8, record_limit=128, original_invocation='natural_once', rejection_preserved=True, count_mode_available=False, effective_requested_count=None, binding_marker='pure_literal_accessor_on_actual_instance_not_native_resource_identity', concrete_class=None, runtime_node_id=None, final_allocator_importance=None, source=source('request_algorithm.ws.in'), evidence=['REQUEST_ALGORITHM.md']), conditions={k:sorted(v) for k,v in FIELDS.items()},hooks=hooks, observation_modes={'light-v1':'boundary getters plus retained attempt/contact events', 'decision-v1':'partial_decision_coverage: existing periodic state sampler; H04-H08 unavailable'}, native_tick_limit=600,input_tick_range=[2,120],event_ring_capacity=128,sample_capacity=301, no_probability_inference=True) value['sha256']=hashlib.sha256(json.dumps(value,sort_keys=True,separators=(',',':')).encode()).hexdigest() return value