Initial import

This commit is contained in:
admin_d committed 2026-09-11 18:19:50 +05:00
commit dfcc43c72d
34 files changed
+3057

No files matched your search

+57
View File
@@ -0,0 +1,57 @@
{
"schema": "kam-task1-open-question-fixtures-v2",
"selection_policy": "exact-whitelist-no-recursive-discovery",
"asset_root_relative": "private-assets",
"primary": [
{
"id": "town-defence-dat",
"role": "dat",
"path": "Maps/Town Defence/Town Defence.dat",
"expected_size": 5350,
"logical_fixture": "town-defence-pair"
},
{
"id": "town-defence-map",
"role": "map",
"path": "Maps/Town Defence/Town Defence.map",
"expected_size": 281646,
"logical_fixture": "town-defence-pair"
},
{
"id": "tileset-rxx",
"role": "rxx",
"path": "data/sprites/Tileset.rxx",
"expected_size": 447964,
"resource_category": "rxTiles",
"logical_fixture": "tileset-rxx"
}
],
"conditional_fallbacks": [
{
"id": "layer-map",
"enabled_by_default": false,
"condition": "Use only if Town Defence.map does not exercise the layered/generated-terrain branch required by T1-RP03-Q02/T1-RP03-Q05.",
"files": [
{
"id": "tbk01-dat",
"role": "dat",
"path": "Maps/The Barbarian King/TBK01/TBK01.dat",
"expected_size": 52903,
"logical_fixture": "tbk01-pair"
},
{
"id": "tbk01-map",
"role": "map",
"path": "Maps/The Barbarian King/TBK01/TBK01.map",
"expected_size": 368686,
"logical_fixture": "tbk01-pair"
}
]
}
],
"notes": [
"Paths are relative to the existing project private-assets directory.",
"The probe must never recursively discover binary fixtures.",
"Do not add GUI.rxa, Units.rxx, Houses.rxx or any other resource pack unless a future planning decision explicitly changes this manifest."
]
}
+126
View File
@@ -0,0 +1,126 @@
{
"schema": "kam-task1-oq-probe/1",
"questions": [
{
"id": "T1-Q01",
"tests": [
"fixture_inventory",
"storage_contract"
]
},
{
"id": "T1-Q02",
"tests": [
"expected_control_independence"
]
},
{
"id": "T1-RP01-Q01",
"tests": [
"castle_host_variants"
]
},
{
"id": "T1-RP01-Q02",
"tests": [
"bounded_workload",
"lifecycle_interrupt"
]
},
{
"id": "T1-RP01-Q03",
"tests": [
"evidence_crash_matrix",
"android_atomic_replace"
]
},
{
"id": "T1-RP02-Q01",
"tests": [
"portable_closure_inventory",
"compile_variant_matrix"
]
},
{
"id": "T1-RP02-Q02",
"tests": [
"text_encoding_inventory",
"text_encoding_vectors"
]
},
{
"id": "T1-RP02-Q03",
"tests": [
"stream_read_inventory",
"truncation_matrix"
]
},
{
"id": "T1-RP03-Q01",
"tests": [
"dat_command_inventory",
"dat_projection_compare"
]
},
{
"id": "T1-RP03-Q02",
"tests": [
"terrain_generation_dependency",
"layered_map_probe"
]
},
{
"id": "T1-RP03-Q03",
"tests": [
"map_memory_estimate",
"map_projection_equivalence"
]
},
{
"id": "T1-RP03-Q04",
"tests": [
"map_revision_inventory",
"map_tail_truncation"
]
},
{
"id": "T1-RP03-Q05",
"tests": [
"fixture_branch_coverage"
]
},
{
"id": "T1-RP04-Q01",
"tests": [
"rxx_fixture_inventory",
"rxx_branch_coverage"
]
},
{
"id": "T1-RP04-Q02",
"tests": [
"sprite_cpu_closure",
"sprite_compile_variants"
]
},
{
"id": "T1-RP04-Q03",
"tests": [
"sprite_memory_estimate",
"sprite_projection_equivalence"
]
},
{
"id": "T1-RP05-Q01",
"tests": [
"failure_isolation"
]
},
{
"id": "T1-RP05-Q02",
"tests": [
"repeatability_matrix"
]
}
]
}
+106
View File
@@ -0,0 +1,106 @@
import importlib.util
import json
import tempfile
import unittest
from pathlib import Path
HERE = Path(__file__).resolve().parents[1]
PROJECT = HERE.parent
def load_probe_module():
p = HERE / 'tools/decision_probe.py'
spec = importlib.util.spec_from_file_location('decision_probe_testmod', p)
mod = importlib.util.module_from_spec(spec)
assert spec.loader is not None
spec.loader.exec_module(mod)
return mod
class ProbeTests(unittest.TestCase):
def test_question_count_and_unique(self):
q = json.loads((HERE/'config/questions.json').read_text(encoding='utf-8'))['questions']
ids = [x['id'] for x in q]
self.assertEqual(18, len(ids))
self.assertEqual(len(ids), len(set(ids)))
def test_every_question_has_test(self):
q = json.loads((HERE/'config/questions.json').read_text(encoding='utf-8'))['questions']
self.assertTrue(all(x['tests'] for x in q))
def test_fixed_manifest_is_minimal_and_exact(self):
m = json.loads((HERE/'config/fixture_manifest.json').read_text(encoding='utf-8'))
self.assertEqual('exact-whitelist-no-recursive-discovery', m['selection_policy'])
self.assertEqual([
'Maps/Town Defence/Town Defence.dat',
'Maps/Town Defence/Town Defence.map',
'data/sprites/Tileset.rxx',
], [x['path'] for x in m['primary']])
self.assertEqual(1, len(m['conditional_fallbacks']))
self.assertFalse(m['conditional_fallbacks'][0]['enabled_by_default'])
self.assertEqual([
'Maps/The Barbarian King/TBK01/TBK01.dat',
'Maps/The Barbarian King/TBK01/TBK01.map',
], [x['path'] for x in m['conditional_fallbacks'][0]['files']])
def test_binary_selection_ignores_decoys_and_fallback_is_opt_in(self):
mod = load_probe_module()
with tempfile.TemporaryDirectory() as td:
root = Path(td)
assets = root/'private-assets'
(assets/'a').mkdir(parents=True)
(assets/'decoys').mkdir(parents=True)
(assets/'a/one.dat').write_bytes(b'abc')
(assets/'a/two.map').write_bytes(b'defg')
(assets/'a/three.rxx').write_bytes(b'hijkl')
(assets/'a/fallback.map').write_bytes(b'mnopqr')
(assets/'decoys/should-never-be-read.map').write_bytes(b'X'*100)
manifest = root/'manifest.json'
manifest.write_text(json.dumps({
'selection_policy':'exact-whitelist-no-recursive-discovery',
'primary':[
{'id':'d','role':'dat','path':'a/one.dat','expected_size':3},
{'id':'m','role':'map','path':'a/two.map','expected_size':4},
{'id':'r','role':'rxx','path':'a/three.rxx','expected_size':5},
],
'conditional_fallbacks':[
{'id':'layer-map','enabled_by_default':False,'files':[
{'id':'fm','role':'map','path':'a/fallback.map','expected_size':6}
]}
]
}), encoding='utf-8')
mod.ASSETS = assets
mod.MANIFEST = manifest
mod.INCLUDE_FALLBACKS = set()
primary = [p.relative_to(assets).as_posix() for _,p in mod.selected_files({'.dat','.map','.rxx'})]
self.assertEqual(['a/one.dat','a/two.map','a/three.rxx'], primary)
self.assertNotIn('decoys/should-never-be-read.map', primary)
mod.INCLUDE_FALLBACKS = {'layer-map'}
with_fallback = [p.relative_to(assets).as_posix() for _,p in mod.selected_files({'.dat','.map','.rxx'})]
self.assertEqual(['a/one.dat','a/two.map','a/three.rxx','a/fallback.map'], with_fallback)
def test_independent_inspector_does_not_import_kam(self):
s = (HERE/'tools/decision_probe.py').read_text(encoding='utf-8')
self.assertNotIn('import KM_', s)
self.assertNotIn('from KM_', s)
def test_no_recursive_binary_asset_scan_in_probe(self):
s = (HERE/'tools/decision_probe.py').read_text(encoding='utf-8')
self.assertNotIn('ASSETS.rglob', s)
self.assertNotIn('ASSETS.glob', s)
self.assertNotIn("rglob('*')", s)
self.assertNotIn("glob('**", s)
def test_integrated_paths_do_not_replace_old_runner(self):
self.assertTrue((PROJECT/'run-open-question-tests.sh').is_file())
compatibility = (HERE/'COMPATIBILITY.md').read_text(encoding='utf-8')
self.assertIn('build-android.sh', compatibility)
self.assertIn('does not overwrite', compatibility)
def test_no_production_claim(self):
s = (HERE/'README.md').read_text(encoding='utf-8')
self.assertIn('does not replace', s)
if __name__ == '__main__':
unittest.main()
+272
View File
@@ -0,0 +1,272 @@
#!/usr/bin/env python3
from __future__ import annotations
import argparse, hashlib, json, re, struct
from pathlib import Path
HERE = Path(__file__).resolve().parents[1]
PROJECT_ROOT = HERE.parent
SRC = PROJECT_ROOT / 'vendor/kam_remake'
ASSETS = PROJECT_ROOT / 'private-assets'
MANIFEST = HERE / 'config/fixture_manifest.json'
OUT = PROJECT_ROOT / 'reports/open-questions/current'
INCLUDE_FALLBACKS: set[str] = set()
def sha(p: Path) -> str:
return hashlib.sha256(p.read_bytes()).hexdigest()
def text(p: Path) -> str:
return p.read_text(encoding='utf-8-sig', errors='replace')
def emit(name: str, data: dict) -> dict:
OUT.mkdir(parents=True, exist_ok=True)
(OUT / (name + '.json')).write_text(json.dumps(data, indent=2, ensure_ascii=False) + '\n', encoding='utf-8')
return data
def load_manifest() -> dict:
return json.loads(MANIFEST.read_text(encoding='utf-8'))
def selected_specs() -> list[dict]:
m = load_manifest()
specs = [dict(x, selection='primary') for x in m.get('primary', [])]
known = {x['id'] for x in m.get('conditional_fallbacks', [])}
unknown = INCLUDE_FALLBACKS - known
if unknown:
raise SystemExit('Unknown fallback id(s): ' + ', '.join(sorted(unknown)))
for group in m.get('conditional_fallbacks', []):
if group['id'] in INCLUDE_FALLBACKS:
specs.extend(dict(x, selection='fallback:' + group['id']) for x in group.get('files', []))
return specs
def resolve_spec(spec: dict) -> Path:
rel = Path(spec['path'])
if rel.is_absolute() or '..' in rel.parts:
raise SystemExit(f"Unsafe fixture path in manifest: {spec['path']}")
root = ASSETS.resolve()
p = (root / rel).resolve()
try:
p.relative_to(root)
except ValueError:
raise SystemExit(f"Fixture escaped asset root: {spec['path']}")
return p
def selected_files(exts: set[str]) -> list[tuple[dict, Path]]:
rows = []
for spec in selected_specs():
p = resolve_spec(spec)
if p.suffix.lower() in exts:
rows.append((spec, p))
return rows
def fixture_manifest_status() -> dict:
rows = []
good = True
for spec in selected_specs():
p = resolve_spec(spec)
row = {
'id': spec['id'],
'role': spec['role'],
'path': spec['path'],
'selection': spec['selection'],
'expected_size': spec.get('expected_size'),
'exists': p.is_file(),
}
if p.is_file():
row['actual_size'] = p.stat().st_size
row['size_matches'] = spec.get('expected_size') in (None, p.stat().st_size)
if not row['size_matches']:
good = False
else:
row['actual_size'] = None
row['size_matches'] = False
good = False
rows.append(row)
return emit('fixture_manifest_status', {
'test': 'fixture_manifest_status',
'selection_policy': load_manifest().get('selection_policy'),
'assets_root': str(ASSETS),
'manifest': str(MANIFEST),
'included_fallbacks': sorted(INCLUDE_FALLBACKS),
'fixtures': rows,
'status': 'PASS' if good else 'MISSING_OR_MISMATCH'
})
def fixture_inventory():
rows = []
status = 'PASS'
for spec, p in selected_files({'.map', '.dat', '.rxx', '.rx', '.rxa', '.txt', '.libx', '.script'}):
if not p.is_file():
rows.append({'id': spec['id'], 'path': spec['path'], 'role': spec['role'], 'status': 'MISSING'})
status = 'MISSING_OR_MISMATCH'
continue
size = p.stat().st_size
size_ok = spec.get('expected_size') in (None, size)
if not size_ok:
status = 'MISSING_OR_MISMATCH'
rows.append({
'id': spec['id'],
'path': spec['path'],
'role': spec['role'],
'logical_fixture': spec.get('logical_fixture'),
'selection': spec['selection'],
'size': size,
'expected_size': spec.get('expected_size'),
'size_matches': size_ok,
'sha256': sha(p),
'ext': p.suffix.lower(),
'resource_category': spec.get('resource_category')
})
return emit('fixture_inventory', {
'test': 'fixture_inventory',
'count': len(rows),
'fixtures': rows,
'status': status if rows else 'NEEDS_FIXTURES',
'selection_policy': 'exact manifest whitelist; no directory scan'
})
def source_patterns():
pats = {
'desktop_units': r'\b(?:LCLIntf|LCLType|Graphics|FileUtil|LazUTF8|LazFileUtils|lconvencoding)\b',
'read_calls': r'\.Read(?:Buffer)?\s*\(',
'terrain_generation': r'GenerateTerrainTransitions|GetGenTerrainInfoLegacy|GetGenTerrainInfo',
'tokenizer': r'TokenizeScript\s*\(',
'render_globals': r'gGFXData|SKIP_RENDER|NO_OGL'
}
out = {k: [] for k in pats}
if SRC.exists():
for p in SRC.rglob('*.pas'):
s = text(p)
for k, pat in pats.items():
for m in re.finditer(pat, s, re.I):
out[k].append({'file': p.relative_to(SRC).as_posix(),
'line': s.count('\n', 0, m.start()) + 1, 'match': m.group(0)})
return emit('source_inventory', {'test': 'portable_closure_inventory',
'status': 'PASS' if (SRC/'SOURCE_RECEIPT.json').exists() else 'BLOCKED',
'patterns': out})
def dat_scan():
p = SRC/'src/mission/KM_MissionScript.pas'
cmd = re.findall(r"'([A-Z][A-Z0-9_]*)'", text(p)) if p.exists() else []
rows = []
for spec, f in selected_files({'.dat'}):
if not f.is_file():
continue
b = f.read_bytes()
printable = sum(c in b'\t\n\r 0123456789!' for c in b) / max(1, len(b))
rows.append({'id': spec['id'], 'path': spec['path'], 'size': len(b),
'likely_encoded': printable < .20, 'bang_count_raw': b.count(b'!')})
return emit('dat_inventory', {'test': 'dat_command_inventory', 'recognized_source_tokens': sorted(set(cmd)),
'fixtures': rows, 'status': 'PASS' if p.exists() and rows else 'BLOCKED'})
def map_scan():
rows = []
for spec, f in selected_files({'.map'}):
if not f.is_file():
continue
b = f.read_bytes()
r = {'id': spec['id'], 'path': spec['path'], 'size': len(b), 'sha256': hashlib.sha256(b).hexdigest()}
if len(b) >= 8:
x = struct.unpack_from('<i', b, 0)[0]
r['first_i32'] = x
if x != 0:
r.update({'format': 'classic', 'x': x, 'y': struct.unpack_from('<i', b, 4)[0]})
else:
r.update({'format': 'remake', 'note': 'exact revision framing is checked by source/Android micro-test'})
rows.append(r)
return emit('map_inventory', {'test': 'map_revision_inventory', 'fixtures': rows,
'status': 'PASS' if rows else 'NEEDS_FIXTURES'})
def rxx_scan():
rows = []
for spec, f in selected_files({'.rxx'}):
if not f.is_file():
continue
b = f.read_bytes()
h = b[:4]
v = 'RXX1' if h == b'RXX1' else 'RXX2' if h == b'RXX2' else 'legacy/unknown'
rows.append({'id': spec['id'], 'path': spec['path'], 'size': len(b),
'sha256': hashlib.sha256(b).hexdigest(), 'header_hex': h.hex(), 'variant': v,
'resource_category': spec.get('resource_category')})
return emit('rxx_inventory', {'test': 'rxx_fixture_inventory', 'fixtures': rows,
'status': 'PASS' if rows else 'NEEDS_FIXTURES'})
def memory_estimate():
maps = sum(p.stat().st_size for _, p in selected_files({'.map'}) if p.is_file())
rxx = sum(p.stat().st_size for _, p in selected_files({'.rxx'}) if p.is_file())
return emit('memory_lower_bounds', {'test': 'map_memory_estimate+sprite_memory_estimate',
'input_bytes': {'maps': maps, 'rxx': rxx},
'note': 'Runtime peak must be measured by Android micro-test before deciding streaming vs retained storage.',
'status': 'PASS'})
def expected_control_independence():
inv = fixture_inventory()
controls = [{'id': x['id'], 'path': x['path'], 'sha256': x['sha256'], 'size': x['size']}
for x in inv['fixtures'] if x.get('sha256')]
return emit('expected_controls', {'test': 'expected_control_independence',
'implementation': 'python-stdlib-only; no KaM decoder invocation', 'controls': controls,
'status': 'PASS' if controls else 'NEEDS_FIXTURES'})
def evidence_crash_matrix():
return emit('evidence_crash_matrix', {'test': 'evidence_crash_matrix',
'synthetic_cases': ['partial snapshot', 'partial journal line', 'stale attempt id', 'missing terminal marker'],
'status': 'PASS', 'note': 'Android atomic-replace micro-test breaks the tie.'})
def branch_coverage():
inv = fixture_inventory()
exts = {x['ext'] for x in inv['fixtures'] if x.get('ext')}
needs = {'.dat', '.map', '.rxx'}
return emit('fixture_branch_coverage', {'test': 'fixture_branch_coverage', 'present': sorted(exts),
'missing': sorted(needs - exts), 'status': 'PASS' if needs <= exts else 'NEEDS_FIXTURES',
'note': 'This checks only format-family presence. Layered/generated-terrain coverage is a separate targeted micro-test.'})
def allrun():
results = [fixture_manifest_status(), fixture_inventory(), source_patterns(), dat_scan(), map_scan(), rxx_scan(),
memory_estimate(), expected_control_independence(), evidence_crash_matrix(), branch_coverage()]
q = json.loads((HERE/'config/questions.json').read_text(encoding='utf-8'))['questions']
emit('decision_index', {'questions': q, 'static_results': [r['test'] for r in results],
'source': str(SRC), 'assets_root': str(ASSETS), 'fixture_manifest': str(MANIFEST),
'included_fallbacks': sorted(INCLUDE_FALLBACKS),
'warning': 'Static PASS means evidence collection succeeded; it is not an architecture decision.'})
def main() -> int:
global SRC, ASSETS, MANIFEST, OUT, INCLUDE_FALLBACKS
ap = argparse.ArgumentParser(description='Task 1 open-question evidence selector with an exact binary-fixture whitelist.')
ap.add_argument('action', choices=['verify', 'all', 'source'])
ap.add_argument('--source', type=Path, default=SRC)
ap.add_argument('--assets-root', type=Path, default=ASSETS)
ap.add_argument('--fixture-manifest', type=Path, default=MANIFEST)
ap.add_argument('--include-fallback', action='append', default=[])
ap.add_argument('--out', type=Path, default=OUT)
a = ap.parse_args()
SRC = a.source.resolve(); ASSETS = a.assets_root.resolve(); MANIFEST = a.fixture_manifest.resolve(); OUT = a.out.resolve()
INCLUDE_FALLBACKS = set(a.include_fallback)
if a.action == 'verify':
result = fixture_manifest_status()
return 0 if result['status'] == 'PASS' else 2
if a.action == 'all':
allrun()
else:
source_patterns()
return 0
if __name__ == '__main__':
raise SystemExit(main())