Initial import
This commit is contained in:
commit
dfcc43c72d
34 files changed
+3057
No files matched your search
Executable
+57
@@ -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."
|
||||
]
|
||||
}
|
||||
Executable
+126
@@ -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"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
Executable
+106
@@ -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()
|
||||
Executable
+272
@@ -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())
|
||||
Reference in new issue
Block a user