Freeze HD review libraries and checkpoint 441 approved frames
This commit is contained in:
1 parent
26ed9ccdbb
commit
aeca7501bb
7 files changed
+34070
-2385
No files matched your search
@@ -276,7 +276,7 @@ def markdown(report):
|
||||
lines.extend(["","| Generation job | Status |","| --- | --- |"])
|
||||
for job in report["jobs"]:
|
||||
lines.append(f"| {job['name']} | {job['status']} |")
|
||||
lines.extend(["",f"The catalog also contains **{report['cinematics']['frames']} AGX cinematic delta frames in {len(report['cinematics']['sequences'])} sequences** and **{len(report['fonts'])} bitmap fonts**. These have explicit native-art preservation policies: keep the classic cinematics and sharp original glyphs, validate complete playback/alignment, and never count them as generated HD replacements. The AGX frames are separate from standalone pictures.","","## Sequence priorities","","1. Keep all light/dark-player banking poses and firing-flash poses coherent as complete families. Preserve the exact firing-flash source mask, which differs from the light/dark mask for the straight pose.","2. Ground defenses, airborne enemies and complete boss animation sequences; share palettes and the original logical canvases, rather than treating each enemy frame as an unrelated illustration.","3. Original projectiles and pickup/equipment animations. Tiny projectiles need careful readability checks; per-asset nearest fallback remains temporary until a clean remaster passes review.","4. Explosions, flame/spark sequences and other effects. Check full animation timing, transparency and palette cycling, not only a still image.","5. All four terrain sets, including unnamed catalog tiles. Generate with neighboring context and verify joined seams before changing runtime coverage.","6. Every original menu background, portrait, HUD and retained unused picture. Gameplay UI text and button labels must remain readable and match their actions. Verify the preserved native cinematic/font paths separately.","","## Reproduce the audit","","```sh","python3 tools/hd_art_audit.py --data ../data \\"," --json ../data/hd/coverage-audit.json --markdown docs/hd-backlog.md","python3 tools/hd_job_queue.py --data ../data","```","","The JSON gives every picture's asset ID, path, logical dimensions, original header options, source hash and HD status, plus each declared animation/set. The [completion ledger](hd-completion.json) records every required picture and native-art verification target, including complete families, ownership and candidate/review/registration stages. Both contain metadata only. Original and original-derived artwork remains outside Git.","","Folder totals: " + ", ".join(f"`{name}/`: {count:,}" for name,count in report["by_folder"].items()) + ". These folders are not semantic categories; terrain and other pictures also live in `misc/`.",""])
|
||||
lines.extend(["",f"The catalog also contains **{report['cinematics']['frames']} AGX cinematic delta frames in {len(report['cinematics']['sequences'])} sequences** and **{len(report['fonts'])} bitmap fonts**. These have explicit native-art preservation policies: keep the classic cinematics and sharp original glyphs, validate complete playback/alignment, and never count them as generated HD replacements. The AGX frames are separate from standalone pictures.","","## Sequence priorities","","1. Keep all light/dark-player banking poses and firing-flash poses coherent as complete families. Preserve the exact firing-flash source mask, which differs from the light/dark mask for the straight pose.","2. Ground defenses, airborne enemies and complete boss animation sequences; share palettes and the original logical canvases, rather than treating each enemy frame as an unrelated illustration.","3. Original projectiles and pickup/equipment animations. Tiny projectiles need careful readability checks; per-asset nearest fallback remains temporary until a clean remaster passes review.","4. Explosions, flame/spark sequences and other effects. Check full animation timing, transparency and palette cycling, not only a still image.","5. All four terrain sets, including unnamed catalog tiles. Generate from exact canonical source tiles or a literal native object assembly; use neighboring context for review rather than importing foreign scenery into artwork. Verify joined seams and destroyed-state pairs before changing runtime coverage.","6. Every original menu background, portrait, HUD and retained unused picture. Gameplay UI text and button labels must remain readable and match their actions. Verify the preserved native cinematic/font paths separately.","","## Reproduce the audit","","```sh","python3 tools/hd_art_audit.py --data ../data \\"," --json ../data/hd/coverage-audit.json --markdown docs/hd-backlog.md","python3 tools/hd_job_queue.py --data ../data","```","","The JSON gives every picture's asset ID, path, logical dimensions, original header options, source hash and HD status, plus each declared animation/set. The [completion ledger](hd-completion.json) records every required picture and native-art verification target, including complete families, ownership and candidate/review/registration stages. Both contain metadata only. Working originals, source references and generation/review drafts under `../data` remain outside this repository. The bundled original catalog and approved HD runtime snapshot under `data/` are tracked in Git; the snapshot is refreshed from reviewed development artwork when packaging.","","Folder totals: " + ", ".join(f"`{name}/`: {count:,}" for name,count in report["by_folder"].items()) + ". These folders are not semantic categories; terrain and other pictures also live in `misc/`.",""])
|
||||
effects=next(row for row in report["by_category"] if row["category"]==CATEGORIES[4])
|
||||
effect_ranges=effects["declared_sequences_or_sets"]
|
||||
lines.extend(["## Secondary effect coverage", "", f"Native animation registrations, pickup masks and beam impact/power companion arrays contribute {effects['pictures']:,} distinct effect frames. The {effect_ranges} declared ranges include one five-frame descending-smoke subset of the nine-frame smoke family, leaving {effect_ranges-1} complete families. This includes muzzle flashes/casings, laser impacts, smoke trails, lightning, sparks and the super-shield glow as well as explosions. Pickup glow uses indexed light-table mask semantics and needs a different HD treatment from an ordinary color sprite. These assets are not UI artwork merely because they are absent from the content registries. Renderer event reachability needs its own check before claiming complete gameplay effect coverage.", ""])
|
||||
|
||||
@@ -67,6 +67,49 @@ def binding_digest(rows):
|
||||
return hashlib.sha256(json.dumps(rows, sort_keys=True, separators=(",", ":")).encode()).hexdigest()
|
||||
|
||||
|
||||
def freeze_library(project, library):
|
||||
"""Keep exact tested bytes and the original build time across Cargo rebuilds."""
|
||||
before = library.stat()
|
||||
payload = library.read_bytes()
|
||||
after = library.stat()
|
||||
identity = lambda value: (value.st_ino, value.st_size, value.st_mtime_ns)
|
||||
if identity(before) != identity(after):
|
||||
raise ValueError("Rust library changed while taking review snapshot; retry")
|
||||
digest = hashlib.sha256(payload).hexdigest()
|
||||
directory = project / "target/hd-review-libraries" / digest
|
||||
directory.mkdir(parents=True, exist_ok=True)
|
||||
frozen = directory / library.name
|
||||
if frozen.exists():
|
||||
if hashlib.sha256(frozen.read_bytes()).hexdigest() != digest:
|
||||
raise ValueError("review library snapshot is corrupt")
|
||||
if frozen.stat().st_mtime_ns != before.st_mtime_ns:
|
||||
# Identical bytes rebuilt later must not make an old snapshot look
|
||||
# fresh. Separate build times retain an honest source-age check.
|
||||
frozen = directory / (library.stem + "-build-" + str(before.st_mtime_ns) + library.suffix)
|
||||
if not frozen.exists():
|
||||
descriptor, temporary = tempfile.mkstemp(prefix="library-", dir=directory)
|
||||
try:
|
||||
with os.fdopen(descriptor, "wb") as output:
|
||||
output.write(payload)
|
||||
output.flush()
|
||||
os.fsync(output.fileno())
|
||||
os.utime(temporary, ns=(before.st_atime_ns, before.st_mtime_ns))
|
||||
os.chmod(temporary, 0o444)
|
||||
try:
|
||||
# Publish without replacing an identical snapshot another
|
||||
# reviewer may already be using. Concurrent first readers
|
||||
# must retain one stable inode as well as identical bytes.
|
||||
os.link(temporary, frozen)
|
||||
except FileExistsError:
|
||||
pass
|
||||
finally:
|
||||
if os.path.exists(temporary):
|
||||
os.unlink(temporary)
|
||||
if hashlib.sha256(frozen.read_bytes()).hexdigest() != digest or frozen.stat().st_mtime_ns != before.st_mtime_ns:
|
||||
raise ValueError("review library snapshot identity differs")
|
||||
return frozen, digest, before
|
||||
|
||||
|
||||
def main():
|
||||
project = Path(__file__).resolve().parents[1]
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
@@ -95,9 +138,10 @@ def main():
|
||||
libraries = [library for profile in profiles for library in (project / "target" / profile / "deps").glob("libraptor_replica-*.rlib")]
|
||||
if not libraries:
|
||||
raise ValueError("build the Rust game library before candidate review")
|
||||
library = max(libraries, key=lambda path: path.stat().st_mtime_ns)
|
||||
built_library = max(libraries, key=lambda path: path.stat().st_mtime_ns)
|
||||
dependency_directory = built_library.parent
|
||||
library, library_sha256, built_library_at_start = freeze_library(project, built_library)
|
||||
library_at_start = library.stat()
|
||||
dependency_directory = library.parent
|
||||
serde = max(dependency_directory.glob("libserde_json-*.rlib"), key=lambda path: path.stat().st_mtime_ns)
|
||||
output = (args.output or args.candidate.parent / "runtime-review").resolve()
|
||||
output.mkdir(parents=True, exist_ok=True)
|
||||
@@ -140,6 +184,9 @@ def main():
|
||||
raise ValueError("candidate renderer bindings changed during runtime review")
|
||||
qa["library"] = str(library.relative_to(project))
|
||||
qa["library_modified_ns"] = library_at_start.st_mtime_ns
|
||||
qa["library_sha256"] = library_sha256
|
||||
qa["library_build_origin"] = str(built_library.relative_to(project))
|
||||
qa["library_build_origin_modified_ns"] = built_library_at_start.st_mtime_ns
|
||||
qa["candidate_sha256"] = hashlib.sha256(candidate_at_finish).hexdigest()
|
||||
qa["candidate_sha256_at_start"] = hashlib.sha256(candidate_at_start).hexdigest()
|
||||
qa["candidate_notes_changed_during_review"] = candidate_at_start != candidate_at_finish
|
||||
|
||||
@@ -55,6 +55,8 @@ def approved_job(root, identity, project):
|
||||
library = (project / qa["library"]).resolve()
|
||||
if not library.is_relative_to(project / "target") or library.stat().st_mtime_ns != qa["library_modified_ns"]:
|
||||
raise ValueError(f"runtime library was replaced after review: {identity}")
|
||||
if qa.get("library_sha256") and hashlib.sha256(library.read_bytes()).hexdigest() != qa["library_sha256"]:
|
||||
raise ValueError(f"runtime library bytes changed after review: {identity}")
|
||||
for name in ["assets.rs", "hd.rs"]:
|
||||
if (project / "src" / name).stat().st_mtime_ns > qa["library_modified_ns"]:
|
||||
raise ValueError(f"asset/HD code changed after review: {identity}")
|
||||
|
||||
Reference in new issue
Block a user