docs: publish Process multiselect verification and native correction evidence
This commit is contained in:
@@ -0,0 +1,42 @@
|
||||
# Actual Process render feedback byte proof
|
||||
|
||||
This small package holds the four actual WGPU RGBA readbacks, adjacent JSON
|
||||
metadata, lossless PNGs, original png-readback.json, encode-png.py and parent
|
||||
observations.md from `.local/process-feedback-review/capture-1`. It requires
|
||||
Python and Pillow; it never invokes Cargo, a GUI, or archived capture helpers.
|
||||
|
||||
Use a portable Process checks package containing the closed `feedback-1` gate:
|
||||
|
||||
```text
|
||||
python -B prepare.py --root C:/ar-multiselect-9e8r6t --checks CHECKS_PACKAGE --check-only
|
||||
python -B prepare.py --root C:/ar-multiselect-9e8r6t --checks CHECKS_PACKAGE --output FRESH_DIRECTORY
|
||||
python -B FRESH_DIRECTORY/verify.py --checks CHECKS_PACKAGE
|
||||
```
|
||||
|
||||
Run the checks package's own verifier too. Feedback references exact logical
|
||||
feedback-1 receipt/helper/log/input pins, so the checks package may include other
|
||||
runs and may be moved independently. No full source capsule is duplicated here.
|
||||
Preparation refuses existing output and compares all 742 captured input bytes
|
||||
to Git commit b15345123a7feaf630ef24d45f591c76c6715778. Verification checks the
|
||||
fixed source map identity and every source-capsule member through the supplied
|
||||
checks package. The original gate HEAD predates b153; its captured inputs, rather
|
||||
than that HEAD, identify the rendered source. The relevant captured test and
|
||||
Mounted renderer paths have explicit source pins.
|
||||
|
||||
Every PNG is decoded and required to equal its raw RGBA byte for byte at the
|
||||
recorded 1440x900 size. For OA and SA separately, before-press and after-motion
|
||||
must preserve complete source bundle, selection string/count, canvas, camera,
|
||||
scale and dimensions while pixels differ. The original PNG readback receipt,
|
||||
unique safe ZIP inventory and every archived file hash are checked. A matching
|
||||
checks receipt must record the exact passing test and capture output directory.
|
||||
|
||||
This is byte/provenance consistency, not native acceptance or independent image
|
||||
interpretation. Pixel files have no gate-time hashes: their linkage is the
|
||||
recorded output directory, create-only captured test code, passing test receipt
|
||||
and parent observations; package pins establish bytes at packaging time.
|
||||
Selection metadata stores the test's captured snapshot; unchanged current
|
||||
selection and empty history are separately asserted in the passing test.
|
||||
There is no history metadata, and this tool does not invent any. Source text in
|
||||
metadata is compared exactly between stages, not reparsed as a Project.
|
||||
The parent observations describe visible quality and scope limits. Encoding
|
||||
helpers and observations are preserved, not executed or treated as instructions.
|
||||
Binary file not shown.
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"README.md": {
|
||||
"bytes": 2623,
|
||||
"sha256": "731c4062a0bc4a66a44246fc69c53b8aa32fd09613e62c9bbef4716f7c930641"
|
||||
},
|
||||
"evidence.zip": {
|
||||
"bytes": 797909,
|
||||
"sha256": "6ebd3beafec69da81124511a509f56fe966d35fbd1b127567a1b7f57eb1a9051"
|
||||
},
|
||||
"manifest.json": {
|
||||
"bytes": 4079,
|
||||
"sha256": "caf50753143216c0cf503473508581f1229528215cf6a72b038ddc8f84a75000"
|
||||
},
|
||||
"prepare.py": {
|
||||
"bytes": 3753,
|
||||
"sha256": "4fb1db6b465ae6fd25cdabdf63b3a5fb76a6cd9399f01afd3dca4dd9ec4e7f86"
|
||||
},
|
||||
"verify.py": {
|
||||
"bytes": 8730,
|
||||
"sha256": "d0bcb4fc8b07c7ba9b4573505b72e865a5eb18070b9b5e4411db757e13175d96"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
{
|
||||
"schema": 1,
|
||||
"kind": "process-render-feedback",
|
||||
"sourceCommit": "b15345123a7feaf630ef24d45f591c76c6715778",
|
||||
"checksRun": "feedback-1",
|
||||
"checksPins": {
|
||||
"started.json": {
|
||||
"bytes": 1747,
|
||||
"sha256": "01fbcfd28b752921ded95c40a711909851bcd005394b34b5f4f49a7eaa0a594c"
|
||||
},
|
||||
"process.json": {
|
||||
"bytes": 260,
|
||||
"sha256": "b8559e934c97f9eba407b1ceceed637fc570a9a8ba9e3362b961824cb0344fcd"
|
||||
},
|
||||
"result.json": {
|
||||
"bytes": 3481,
|
||||
"sha256": "2f007dfda469a75eaaf713577b394605af7d3d42fed2137d9e3ed632301c78d3"
|
||||
},
|
||||
"stdout.log": {
|
||||
"bytes": 293378,
|
||||
"sha256": "8c611aa1671316570c79b079882400e3b710d3eff8fe04e37ec59f17071fa92c"
|
||||
},
|
||||
"stderr.log": {
|
||||
"bytes": 210,
|
||||
"sha256": "504bdaa5c008c52a9462aeca71413f9933a4e9c6b0272715ba5ca916411781c1"
|
||||
},
|
||||
"inputs.json": {
|
||||
"bytes": 124276,
|
||||
"sha256": "7f5b3de4d5bb70282e424fbe90211af2ac259965aef10b05438b46076467a767"
|
||||
},
|
||||
"inputs.zip": {
|
||||
"bytes": 2985964,
|
||||
"sha256": "f681fb5ead73d5aba1d9a0f25ea29a96ccb430692630b64987b984623cd38148"
|
||||
},
|
||||
"helper": {
|
||||
"bytes": 3856,
|
||||
"sha256": "230f1222f133edef76ffb9c09ed6eecf952fc326b04a0bbccb3f3f49df4c0c29"
|
||||
}
|
||||
},
|
||||
"capturingSources": {
|
||||
"crates/oa-desktop/src/behavior/topology/layout_tests.rs": {
|
||||
"bytes": 50414,
|
||||
"sha256": "993d700d6590757fb329cab3029dd0202e71c2c2f852e13fe1228417b01d8fd9"
|
||||
},
|
||||
"crates/oa-desktop/src/behavior/canvas_workspace/tests.rs": {
|
||||
"bytes": 27368,
|
||||
"sha256": "de13f0112b1a39fa03f32a9308dcbd30b235eef9ead877b440dde023c93bc5a3"
|
||||
}
|
||||
},
|
||||
"members": {
|
||||
"capture-1/png-readback.json": {
|
||||
"bytes": 1006,
|
||||
"sha256": "4f397109e46512010fdb777c85abb6dc658fc9e26cb255d22165b0a1c6502eac"
|
||||
},
|
||||
"capture-1/process-Oa-after-motion.json": {
|
||||
"bytes": 2674,
|
||||
"sha256": "17c55c7275634ac9a22b78a6eeab7464ceb0f0e389f02947d9ec96caaf18ecfe"
|
||||
},
|
||||
"capture-1/process-Oa-after-motion.png": {
|
||||
"bytes": 111782,
|
||||
"sha256": "051c44a392eb23787b6e7ff28b0384336602eb2d6e166cca2859c7e44a8f5386"
|
||||
},
|
||||
"capture-1/process-Oa-after-motion.rgba": {
|
||||
"bytes": 5184000,
|
||||
"sha256": "7726fa5de90ad7ec5b14e87883c40cace0007ece54279048c6f49c9386ce5d6d"
|
||||
},
|
||||
"capture-1/process-Oa-before-press.json": {
|
||||
"bytes": 2674,
|
||||
"sha256": "6c1d80c1690d13a8fa2c21505322651c7436673368035fa7a054d0ee55ddbb5f"
|
||||
},
|
||||
"capture-1/process-Oa-before-press.png": {
|
||||
"bytes": 108986,
|
||||
"sha256": "d9b5c2e0b7b50ea894986aa38c63b8d1559169974d6a93954dd0a69107b5740d"
|
||||
},
|
||||
"capture-1/process-Oa-before-press.rgba": {
|
||||
"bytes": 5184000,
|
||||
"sha256": "399e7442f56d320a92d6d9beccd2f66a241e572adadcd733ba797aef97453a1e"
|
||||
},
|
||||
"capture-1/process-Sa-after-motion.json": {
|
||||
"bytes": 3186,
|
||||
"sha256": "441d18d75dab872012f68020012840aa67a09b52a5946749ceea669e2839439e"
|
||||
},
|
||||
"capture-1/process-Sa-after-motion.png": {
|
||||
"bytes": 111760,
|
||||
"sha256": "bc31d57fae268b6ad19c5b14aaa0860e14b7a5f80608a8896970f7ad82b041f0"
|
||||
},
|
||||
"capture-1/process-Sa-after-motion.rgba": {
|
||||
"bytes": 5184000,
|
||||
"sha256": "82493c91aa5ac0e1f44a50f52496fe933104acd7992b2f9adbb7710ed3e85e29"
|
||||
},
|
||||
"capture-1/process-Sa-before-press.json": {
|
||||
"bytes": 3186,
|
||||
"sha256": "b13fbdab939a9d34a8950ee89800f41c2082b5928ef5c8781dc813940ecf6df1"
|
||||
},
|
||||
"capture-1/process-Sa-before-press.png": {
|
||||
"bytes": 108966,
|
||||
"sha256": "9b2a4a7cc355639e26e4e87cddf17b9ce859a00e5a80400da62b67ba27198a59"
|
||||
},
|
||||
"capture-1/process-Sa-before-press.rgba": {
|
||||
"bytes": 5184000,
|
||||
"sha256": "30ede8974ba5d9227c109fd9a5c08c93119bdb897ad7f273e87a6602bc42bdd5"
|
||||
},
|
||||
"encode-png.py": {
|
||||
"bytes": 2181,
|
||||
"sha256": "c05b2d9f2a02d72dba50422da733e36758bd6e2773e48d84174b72ec0a804b5e"
|
||||
},
|
||||
"observations.md": {
|
||||
"bytes": 1912,
|
||||
"sha256": "11828e5467785990a11d462813b821f677cbff1c64dcd1ca8244a94fe01e2423"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
"""Package the fixed, closed capture-1 pixels using an existing checks package."""
|
||||
from pathlib import Path
|
||||
import argparse
|
||||
import json
|
||||
import subprocess
|
||||
import zipfile
|
||||
from verify import COMMIT, MEMBERS, SOURCES, OUTER, need, pin, checks_data, verify_data, verify_package
|
||||
|
||||
HERE = Path(__file__).resolve().parent
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument("--root", type=Path, required=True)
|
||||
parser.add_argument("--checks", type=Path, required=True)
|
||||
parser.add_argument("--output", type=Path)
|
||||
parser.add_argument("--check-only", action="store_true")
|
||||
args = parser.parse_args()
|
||||
need(args.check_only or args.output is not None, "--output or --check-only required")
|
||||
if args.output:
|
||||
need(not args.output.exists(), "fresh nonexistent output directory required")
|
||||
checks = checks_data(args.checks)
|
||||
gate, source = checks
|
||||
# Compare every captured source byte to the named commit, not current worktree.
|
||||
paths = sorted(source)
|
||||
result = subprocess.run(["git", "cat-file", "--batch"], cwd=args.root,
|
||||
input="".join(COMMIT + ":" + name + "\n" for name in paths).encode(), capture_output=True, check=True)
|
||||
offset = 0
|
||||
for name in paths:
|
||||
end = result.stdout.index(b"\n", offset)
|
||||
header = result.stdout[offset:end].split()
|
||||
need(len(header) == 3 and header[1] == b"blob", "missing commit blob: " + name)
|
||||
size = int(header[2])
|
||||
data = result.stdout[end + 1:end + 1 + size]
|
||||
need(data == source[name], "captured input differs from b153: " + name)
|
||||
offset = end + 2 + size
|
||||
need(offset == len(result.stdout), "exact Git batch readback")
|
||||
folder = args.root / ".local/process-feedback-review"
|
||||
need({p.relative_to(folder).as_posix() for p in (folder / "capture-1").rglob("*") if p.is_file()} == {name for name in MEMBERS if name.startswith("capture-1/")}, "exact captured file inventory")
|
||||
payload = {}
|
||||
for name in sorted(MEMBERS):
|
||||
path = folder / name
|
||||
need(not path.is_symlink() and not getattr(path, "is_junction", lambda: False)(), "linked input")
|
||||
payload[name] = path.read_bytes()
|
||||
manifest = {"schema": 1, "kind": "process-render-feedback", "sourceCommit": COMMIT,
|
||||
"checksRun": "feedback-1", "checksPins": {name: pin(data) for name, data in gate.items()},
|
||||
"capturingSources": {name: pin(source[name]) for name in SOURCES},
|
||||
"members": {name: pin(data) for name, data in payload.items()}}
|
||||
summary = verify_data(manifest, payload.__getitem__, checks)
|
||||
if args.check_only:
|
||||
print("PASS raw pixels, metadata and all 742 b153 source blobs:", summary)
|
||||
return
|
||||
out = args.output.resolve()
|
||||
out.mkdir(parents=True, exist_ok=False)
|
||||
for name in ("prepare.py", "verify.py", "README.md"):
|
||||
(out / name).write_bytes((HERE / name).read_bytes())
|
||||
with zipfile.ZipFile(out / "evidence.zip", "x", compression=zipfile.ZIP_DEFLATED, compresslevel=9) as archive:
|
||||
for name, data in payload.items():
|
||||
info = zipfile.ZipInfo(name, date_time=(2026, 9, 23, 0, 0, 0))
|
||||
info.compress_type = zipfile.ZIP_DEFLATED
|
||||
info.external_attr = 0o100644 << 16
|
||||
archive.writestr(info, data)
|
||||
(out / "manifest.json").write_text(json.dumps(manifest, indent=2) + "\n", encoding="utf-8")
|
||||
(out / "files.json").write_text(json.dumps({name: pin((out / name).read_bytes()) for name in sorted(OUTER)}, indent=2) + "\n", encoding="utf-8")
|
||||
need(verify_package(out, checks) == summary, "written archive readback")
|
||||
print("PASS package readback and all 742 b153 source blobs:", out, summary)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,142 @@
|
||||
"""Verify actual rendered feedback bytes against a separate Process checks package."""
|
||||
from pathlib import Path, PurePosixPath
|
||||
from datetime import datetime
|
||||
import argparse
|
||||
import hashlib
|
||||
import io
|
||||
import json
|
||||
import stat
|
||||
import zipfile
|
||||
from PIL import Image
|
||||
|
||||
COMMIT = "b15345123a7feaf630ef24d45f591c76c6715778"
|
||||
INPUT_SHA = "7f5b3de4d5bb70282e424fbe90211af2ac259965aef10b05438b46076467a767"
|
||||
TEST = "production_process_multiselect_reverse_click_group_drop_has_one_history_step_and_stable_camera"
|
||||
SOURCES = ("crates/oa-desktop/src/behavior/topology/layout_tests.rs", "crates/oa-desktop/src/behavior/canvas_workspace/tests.rs")
|
||||
NAMES = [f"process-{layer}-{stage}" for layer in ("Oa", "Sa") for stage in ("before-press", "after-motion")]
|
||||
MEMBERS = {"observations.md", "encode-png.py", "capture-1/png-readback.json"} | {f"capture-1/{name}.{ext}" for name in NAMES for ext in ("rgba", "png", "json")}
|
||||
OUTER = {"prepare.py", "verify.py", "README.md", "manifest.json", "evidence.zip"}
|
||||
|
||||
|
||||
def need(ok, message):
|
||||
if not ok:
|
||||
raise ValueError(message)
|
||||
|
||||
|
||||
def pin(data):
|
||||
return {"bytes": len(data), "sha256": hashlib.sha256(data).hexdigest()}
|
||||
|
||||
|
||||
def safe(name):
|
||||
return isinstance(name, str) and bool(name) and not PurePosixPath(name).is_absolute() and all(c not in name for c in ("\\", ":")) and all(ord(c) >= 32 for c in name) and all(p not in ("", ".", "..") for p in name.split("/"))
|
||||
|
||||
|
||||
def parsed(data):
|
||||
def unique(items):
|
||||
result = {}
|
||||
for key, value in items:
|
||||
need(key not in result, "duplicate JSON key")
|
||||
result[key] = value
|
||||
return result
|
||||
def constant(value):
|
||||
raise ValueError("nonfinite JSON: " + value)
|
||||
return json.loads(data, object_pairs_hook=unique, parse_constant=constant)
|
||||
|
||||
|
||||
def inventory(archive, expected):
|
||||
names = archive.namelist()
|
||||
need(len(names) == len(set(names)) and set(names) == set(expected), "exact ZIP inventory")
|
||||
for row in archive.infolist():
|
||||
need(safe(row.filename) and not row.is_dir() and not stat.S_ISLNK(row.external_attr >> 16), "unsafe ZIP entry")
|
||||
need(row.file_size == expected[row.filename]["bytes"], "ZIP size")
|
||||
|
||||
|
||||
def checks_data(root):
|
||||
"""Read referenced gate bytes, without importing or executing package code."""
|
||||
files = parsed((root / "files.json").read_bytes())
|
||||
need(set(files) == OUTER, "checks outer inventory")
|
||||
for name, expected in files.items():
|
||||
need(pin((root / name).read_bytes()) == expected, "checks outer pin: " + name)
|
||||
manifest = parsed((root / "manifest.json").read_bytes())
|
||||
need(manifest["schema"] == 1 and manifest["kind"] == "multiselect-cargo-gates", "checks format")
|
||||
with zipfile.ZipFile(root / "evidence.zip") as archive:
|
||||
inventory(archive, manifest["members"])
|
||||
def raw(logical):
|
||||
member = manifest["logicalFiles"][logical]
|
||||
data = archive.read(member)
|
||||
need(pin(data) == manifest["members"][member], "referenced checks pin")
|
||||
return data
|
||||
prefix = "cargo/feedback-1/"
|
||||
gate = {name: raw(prefix + name) for name in ("started.json", "process.json", "result.json", "stdout.log", "stderr.log", "inputs.json", "inputs.zip")}
|
||||
spec = manifest["runs"]["feedback-1"]
|
||||
gate["helper"] = raw(spec["helper"])
|
||||
result, start, process = [parsed(gate[name + ".json"]) for name in ("result", "started", "process")]
|
||||
need(all(result.get(k) == v for k, v in start.items()) and result["command"] == process["command"], "feedback start/process/result")
|
||||
times = [datetime.fromisoformat(t) for t in (start["start"], process["observed"], result["finish"])]
|
||||
need(all(t.utcoffset() is not None for t in times) and times == sorted(times), "closed feedback chronology")
|
||||
need(result["exitCode"] == 0 and result["inputsUnchanged"] is True and TEST in result["command"], "passed exact feedback gate")
|
||||
need(result["environment"]["ARCADIA_RENDER_EVIDENCE_DIR"].replace("\\", "/") == result["cwd"].replace("\\", "/") + "/.local/process-feedback-review/capture-1", "actual output directory provenance")
|
||||
for key, name in (("inputs", "inputs.json"), ("archive", "inputs.zip"), ("stdout", "stdout.log"), ("stderr", "stderr.log"), ("helper", "helper")):
|
||||
need(result[key] == pin(gate[name]), "feedback receipt pin: " + key)
|
||||
need(result["inputs"]["sha256"] == INPUT_SHA, "fixed b153 input map identity")
|
||||
inputs = parsed(gate["inputs.json"])
|
||||
need(len(inputs) == result["inputCount"] == 742 and all(safe(name) for name in inputs), "captured source inventory")
|
||||
with zipfile.ZipFile(io.BytesIO(gate["inputs.zip"])) as capsule:
|
||||
inventory(capsule, inputs)
|
||||
source = {name: capsule.read(name) for name in inputs}
|
||||
need(all(pin(source[name]) == expected for name, expected in inputs.items()), "all captured source bytes")
|
||||
need(("test behavior::topology::view::layout_tests::" + TEST + " ... ok").encode() in gate["stdout.log"], "actual test result")
|
||||
return gate, source
|
||||
|
||||
|
||||
def verify_data(manifest, read, checks):
|
||||
need(manifest["schema"] == 1 and manifest["kind"] == "process-render-feedback" and manifest["sourceCommit"] == COMMIT, "feedback manifest")
|
||||
need(set(manifest["members"]) == MEMBERS, "exact feedback files")
|
||||
for name, expected in manifest["members"].items():
|
||||
need(pin(read(name)) == expected, "feedback pin: " + name)
|
||||
gate, source = checks
|
||||
need(manifest["checksRun"] == "feedback-1" and manifest["checksPins"] == {name: pin(data) for name, data in gate.items()}, "exact checks references")
|
||||
need(manifest["capturingSources"] == {name: pin(source[name]) for name in SOURCES}, "capturing test and renderer source identity")
|
||||
report = parsed(read("capture-1/png-readback.json"))
|
||||
need(set(report["captures"]) == set(NAMES), "four readback records")
|
||||
for layer in ("Oa", "Sa"):
|
||||
previous = None
|
||||
for stage in ("before-press", "after-motion"):
|
||||
name = f"process-{layer}-{stage}"
|
||||
metadata = parsed(read("capture-1/" + name + ".json"))
|
||||
raw = read("capture-1/" + name + ".rgba")
|
||||
need(metadata["layer"] == layer and metadata["stage"] == stage and metadata["format"] == "RGBA8", "capture identity")
|
||||
need(metadata["width"] == 1440 and metadata["height"] == 900 and len(raw) == 1440 * 900 * 4, "exact original dimensions")
|
||||
need(metadata["selectionCount"] == 2 and isinstance(metadata["selection"], str) and "history" not in metadata, "selection metadata; no invented history field")
|
||||
bundle = metadata["source"]
|
||||
need(safe(bundle["entry"]) and bundle["entry"] in bundle["files"] and all(safe(n) and isinstance(s, str) for n, s in bundle["files"].items()), "complete captured source bundle")
|
||||
with Image.open(io.BytesIO(read("capture-1/" + name + ".png"))) as png:
|
||||
need(png.format == "PNG" and png.size == (1440, 900) and png.convert("RGBA").tobytes() == raw, "decoded PNG equals actual raw RGBA")
|
||||
expected = {"bytes": len(raw), "rgbaSha256": pin(raw)["sha256"], "width": 1440, "height": 900}
|
||||
need(report["captures"][name] == expected, "original PNG readback receipt")
|
||||
if previous:
|
||||
for field in ("source", "selection", "selectionCount", "canvas", "camera", "scale", "width", "height"):
|
||||
need(metadata[field] == previous[0][field], layer + " changed " + field + " before release")
|
||||
need(raw != previous[1], "actual before/after pixel difference")
|
||||
previous = metadata, raw
|
||||
return {"captures": 4, "pngPixelsExact": True, "stableMetadataPairs": 2, "checksRun": "feedback-1", "nativeAcceptance": False}
|
||||
|
||||
|
||||
def verify_package(root, checks):
|
||||
files = parsed((root / "files.json").read_bytes())
|
||||
need(set(files) == OUTER, "feedback outer inventory")
|
||||
for name, expected in files.items():
|
||||
need(pin((root / name).read_bytes()) == expected, "feedback outer pin")
|
||||
manifest = parsed((root / "manifest.json").read_bytes())
|
||||
with zipfile.ZipFile(root / "evidence.zip") as archive:
|
||||
inventory(archive, manifest["members"])
|
||||
return verify_data(manifest, archive.read, checks)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument("package", nargs="?", type=Path, default=Path(__file__).resolve().parent)
|
||||
parser.add_argument("--checks", type=Path, required=True, help="Process checks package containing closed feedback-1")
|
||||
args = parser.parse_args()
|
||||
print("PASS feedback byte proof:", verify_package(args.package, checks_data(args.checks)))
|
||||
print("History is asserted by the captured test, not recorded in image metadata. No native acceptance claim.")
|
||||
Reference in New Issue
Block a user