Files
jakub 6a667d8deb Capture current mission, AI, map and administration development
Include the current addon, terrain integrations, tools, regression tests and recorded development evidence.
2026-10-13 22:52:53 +02:00

122 lines
7.0 KiB
Python

#!/usr/bin/env python3
"""Archive the two stopped observer runs; verify launch sources and never overwrite."""
from collections import Counter
from pathlib import Path
import hashlib
import json
import re
import shutil
import sys
HERE = Path(__file__).resolve().parent
ROOT = HERE.parents[2]
sys.path.insert(0, str(ROOT / 'tools'))
from native_provenance import archive_source, encoded, read_launch
from native_report import analyze
RELEVANT = [
'addons/core/Scripts/Game/ERR/00_Core/ERR_Observer.c',
'addons/core/Scripts/Game/ERR/00_Core/ERR_ObserverTargets.c',
'addons/core/Scripts/Game/ERR/00_Core/ERR_ObserverPlayerView.c',
'addons/core/Scripts/Game/ERR/00_Core/ERR_PlayerMedical.c',
'addons/core/Scripts/Game/ERR/00_Core/ERR_UnconsciousCameraProbe.c',
'addons/core/Prefabs/ERR/SpectatorCamera.et',
'addons/core/Configs/ERR/ObserverInput.conf',
'addons/core/Configs/ERR/Tests/LegacySites.conf',
]
def digest(path):
return hashlib.sha256(path.read_bytes()).hexdigest()
def copy(source, destination):
destination.parent.mkdir(parents=True, exist_ok=True)
with destination.open('xb') as stream:
stream.write(source.read_bytes())
assert digest(source) == digest(destination)
def normalized(errors):
return Counter(re.sub(r'\s+', ' ', re.sub(r'^\d\d:\d\d:\d\d\.\d+\s+', '', line)) for line in errors)
runs = {}
for tag in ('a01', 'a02'):
profile = Path('/tmp/erri-observer-orbit-run-' + tag)
source = Path('/tmp/erri-observer-orbit-source-' + tag)
sessions = list((profile / 'logs').glob('logs_*'))
assert len(sessions) == 1
session = sessions[0]
manifest, metadata, runner = read_launch(session)
assert runner['cleanup_confirmed'] is True
assert runner['external_addons_unchanged_after_run'] is True
manifest_bytes, provenance, launched = archive_source(session, source)
assert launched and metadata['source_root'] == str(source)
destination = HERE / tag
destination.mkdir(exist_ok=False)
for name in ('console.log', 'script.log', 'error.log', 'runner.json',
'external-addons-inputs.json', 'headless-launch.log'):
copy(session / name, destination / (name + '.txt'))
for name in ('launch-source-sha256.json', 'launch-source.json'):
copy(session / name, destination / name)
(destination / 'source-sha256.json').write_bytes(manifest_bytes)
(destination / 'source-provenance.json').write_bytes(encoded(provenance))
for capture in sorted([*session.glob('err-*.png'), *session.glob('err-*.bmp')]):
copy(capture, destination / capture.name)
if (profile / 'fixture-inputs.json').exists():
copy(profile / 'fixture-inputs.json', destination / 'fixture-inputs.json')
runner_output = Path('/tmp/erri-observer-orbit-' + tag + '-runner.log')
if runner_output.exists():
copy(runner_output, destination / 'runner-output.log.txt')
source_matches = {}
for name in RELEVANT:
assert digest(source / name) == manifest[name]
copy(source / name, destination / 'source' / name)
source_matches[name] = {
'launch_sha256': manifest[name],
'archived_sha256': digest(destination / 'source' / name),
'current_repository_matches_launch': digest(ROOT / name) == manifest[name],
}
report = analyze((destination / 'console.log.txt').read_text(errors='replace'),
str((destination / 'console.log.txt').relative_to(ROOT)))
report['scope'] = 'Local Workbench; native action values and production frame output, then actual camera application. No physical mouse, remote streaming or multiplayer qualification.'
(destination / 'report.json').write_bytes(encoded(report))
(destination / 'relevant-source-matches.json').write_bytes(encoded(source_matches))
runs[tag] = {'manifest': manifest, 'report': report, 'runner': runner}
baseline = Path('/tmp/err-observer-orbit-before.c')
copy(baseline, HERE / 'baseline/ERR_Observer.c')
copy(ROOT / 'tests/test_observer_orbit.py', HERE / 'regression/test_observer_orbit.py')
copy(Path('/tmp/erri-observer-orbit-check-unsandboxed.log'), HERE / 'full-check.log.txt')
one, two = runs['a01'], runs['a02']
changed = sorted(path for path in set(one['manifest']) | set(two['manifest'])
if one['manifest'].get(path) != two['manifest'].get(path))
assert changed == ['addons/core/Configs/ERR/Tests/LegacySites.conf']
before, after = normalized(one['report']['engine_errors']), normalized(two['report']['engine_errors'])
review = {
'finding': 'Orbit input and pose calculation were inside the selected-target branch, freezing the fallback anchor when alone or all surviving players were unconscious.',
'fix': 'Keep yaw, pitch and zoom around the authorized fallback anchor without requiring a locally resolved target; preserve collision, input and player-view restrictions.',
'native_a01': {'result': one['runner']['result'], 'assertions': one['report']['assertions_passed'],
'reason': 'Historical hillside extraction test marker has no second US rescue landing footprint; mission preparation fails before camera regression.'},
'native_a02': {'result': two['runner']['result'], 'assertions': two['report']['assertions_passed'],
'completions': two['report']['smoke_completions'],
'script_or_runtime_failure_count': len(two['report']['script_or_runtime_failures']),
'engine_error_count': len(two['report']['engine_errors'])},
'a01_to_a02_changed_launch_sources': changed,
'a02_setup_only_arrangement': 'Move only the legacy fixture extraction marker from [7000,0,7500] to previously surveyed flat airport grounds [4776.55,0,11528.9]. No camera or casualty source changes between native runs.',
'a02_engine_error_signatures_absent_from_a01': sorted(set(after) - set(before)),
'a02_additional_occurrences_of_existing_engine_errors': dict(after - before),
'baseline_source': {'path': 'baseline/ERR_Observer.c', 'sha256': digest(baseline),
'qualification': 'Pre-fix source retained for host regression reproduction; neither a01 nor a02 is a native pre-fix run.'},
'regression_snapshot': {'path': 'regression/test_observer_orbit.py',
'sha256': digest(ROOT / 'tests/test_observer_orbit.py'),
'qualification': 'Host regression copied at archive time, outside native launch manifest.'},
'full_check_limitation': 'Full check stops at the existing ERRM_AtlasLeaf.et missing saved root ID. Exact failure output retained in full-check.log.txt.',
'cleanup_source_and_external_inputs': 'Both runners confirm cleanup and unchanged launched addon and external sources; source manifests and exact raw logs verified at archive time.',
'limitations': 'Synthetic native action/frame inputs, not physical mouse delivery. One local player and a physical stand-in; no dedicated multiplayer target streaming or live-server deployment.',
}
(HERE / 'review.json').write_bytes(encoded(review))
print(json.dumps(review, indent=2))