From 9e69eeca9a3fe12abfbac9a65d26a37c0dff2795 Mon Sep 17 00:00:00 2001 From: "Mark E. DeYoung" <1854350+mark-e-deyoung@users.noreply.github.com> Date: Wed, 9 Sep 2026 23:49:32 +0200 Subject: [PATCH 01/26] lab: add public OSS organ qualification probe --- tools/oss-organ-qualify.py | 272 +++++++++++++++++++++++++++++++++++++ 1 file changed, 272 insertions(+) create mode 100644 tools/oss-organ-qualify.py diff --git a/tools/oss-organ-qualify.py b/tools/oss-organ-qualify.py new file mode 100644 index 0000000..a6e6fa6 --- /dev/null +++ b/tools/oss-organ-qualify.py @@ -0,0 +1,272 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import hashlib +import json +import os +import platform +import shutil +import stat +import subprocess +import sys +import tarfile +import tempfile +import time +import urllib.request +import zipfile +from pathlib import Path + +VERSIONS = { + "task": ("go-task/task", "v3.53.1"), + "just": ("casey/just", "1.58.0"), + "nu": ("nushell/nushell", "0.115.1"), + "minizinc": ("MiniZinc/libminizinc", "2.10.1"), +} + +def platform_key() -> tuple[str, str]: + system = platform.system().lower() + raw_arch = platform.machine().lower() + if raw_arch in {"x86_64", "amd64"}: + arch = "x64" + elif raw_arch in {"arm64", "aarch64"}: + arch = "arm64" + else: + raise RuntimeError(f"unsupported architecture: {raw_arch}") + if system not in {"linux", "darwin", "windows"}: + raise RuntimeError(f"unsupported operating system: {system}") + return system, arch + +def asset_name(tool: str, system: str, arch: str) -> str: + if tool == "task": + os_name = {"linux": "linux", "darwin": "darwin", "windows": "windows"}[system] + arch_name = {"x64": "amd64", "arm64": "arm64"}[arch] + ext = "zip" if system == "windows" else "tar.gz" + return f"task_{os_name}_{arch_name}.{ext}" + if tool == "just": + target = { + ("linux", "x64"): "x86_64-unknown-linux-musl", + ("linux", "arm64"): "aarch64-unknown-linux-musl", + ("darwin", "x64"): "x86_64-apple-darwin", + ("darwin", "arm64"): "aarch64-apple-darwin", + ("windows", "x64"): "x86_64-pc-windows-msvc", + ("windows", "arm64"): "aarch64-pc-windows-msvc", + }[(system, arch)] + ext = "zip" if system == "windows" else "tar.gz" + return f"just-1.58.0-{target}.{ext}" + if tool == "nu": + target = { + ("linux", "x64"): "x86_64-unknown-linux-gnu", + ("linux", "arm64"): "aarch64-unknown-linux-gnu", + ("darwin", "x64"): "x86_64-apple-darwin", + ("darwin", "arm64"): "aarch64-apple-darwin", + ("windows", "x64"): "x86_64-pc-windows-msvc", + ("windows", "arm64"): "aarch64-pc-windows-msvc", + }[(system, arch)] + ext = "zip" if system == "windows" else "tar.gz" + return f"nu-0.115.1-{target}.{ext}" + if tool == "minizinc": + target = { + ("linux", "x64"): "x86_64-linux-gnu", + ("linux", "arm64"): "aarch64-linux-gnu", + ("darwin", "x64"): "x86_64-apple-darwin", + ("darwin", "arm64"): "aarch64-apple-darwin", + ("windows", "x64"): "x86_64-windows", + ("windows", "arm64"): "aarch64-windows", + }[(system, arch)] + ext = "zip" if system == "windows" else "tar.gz" + return f"MiniZinc-2.10.1-{target}.{ext}" + raise KeyError(tool) + +def request_json(url: str) -> dict: + req = urllib.request.Request( + url, + headers={ + "Accept": "application/vnd.github+json", + "User-Agent": "agent-dispatch-oss-organ-qualification/1", + "X-GitHub-Api-Version": "2022-11-28", + }, + ) + with urllib.request.urlopen(req, timeout=30) as response: + return json.load(response) + +def download(url: str, destination: Path) -> None: + req = urllib.request.Request(url, headers={"User-Agent": "agent-dispatch-oss-organ-qualification/1"}) + last_error = None + for attempt in range(3): + try: + with urllib.request.urlopen(req, timeout=60) as response, destination.open("wb") as out: + shutil.copyfileobj(response, out) + return + except Exception as exc: + last_error = exc + if attempt == 2: + break + time.sleep(2 ** attempt) + raise RuntimeError(f"download failed after retries: {url}: {last_error}") + +def verify_sha256(path: Path, digest_field: str | None) -> str: + if not digest_field or not digest_field.startswith("sha256:"): + raise RuntimeError(f"release asset has no GitHub SHA-256 digest: {path.name}") + expected = digest_field.split(":", 1)[1].lower() + h = hashlib.sha256() + with path.open("rb") as f: + for chunk in iter(lambda: f.read(1024 * 1024), b""): + h.update(chunk) + actual = h.hexdigest() + if actual != expected: + raise RuntimeError(f"SHA-256 mismatch for {path.name}: expected {expected}, got {actual}") + return actual + +def extract(archive: Path, destination: Path) -> None: + destination.mkdir(parents=True, exist_ok=True) + root = destination.resolve() + if archive.suffix.lower() == ".zip": + with zipfile.ZipFile(archive) as z: + for member in z.infolist(): + target = (destination / member.filename).resolve() + if root not in target.parents and target != root: + raise RuntimeError(f"unsafe archive path in {archive.name}: {member.filename}") + z.extractall(destination) + else: + with tarfile.open(archive, "r:gz") as t: + for member in t.getmembers(): + target = (destination / member.name).resolve() + if root not in target.parents and target != root: + raise RuntimeError(f"unsafe archive path in {archive.name}: {member.name}") + t.extractall(destination) + +def find_executable(root: Path, basename: str, system: str) -> Path: + expected = basename + (".exe" if system == "windows" else "") + candidates = [p for p in root.rglob(expected) if p.is_file()] + if not candidates: + raise RuntimeError(f"could not find {expected} under {root}") + candidates.sort(key=lambda p: (len(p.parts), len(str(p)))) + exe = candidates[0] + if system != "windows": + exe.chmod(exe.stat().st_mode | stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH) + return exe + +def run(argv: list[str], cwd: Path | None = None) -> subprocess.CompletedProcess[str]: + result = subprocess.run(argv, cwd=cwd, text=True, capture_output=True, timeout=120) + if result.returncode != 0: + raise RuntimeError( + f"command failed ({result.returncode}): {argv}\nstdout:\n{result.stdout}\nstderr:\n{result.stderr}" + ) + return result + +def install_tool(tool: str, root: Path, system: str, arch: str) -> tuple[Path, dict]: + repo, tag = VERSIONS[tool] + metadata = request_json(f"https://api.github.com/repos/{repo}/releases/tags/{tag}") + name = asset_name(tool, system, arch) + match = next((a for a in metadata.get("assets", []) if a.get("name") == name), None) + if match is None: + available = [a.get("name") for a in metadata.get("assets", [])] + raise RuntimeError(f"{repo}@{tag} has no expected asset {name}; available={available}") + archive = root / name + download(match["browser_download_url"], archive) + digest = verify_sha256(archive, match.get("digest")) + unpacked = root / f"{tool}-unpacked" + extract(archive, unpacked) + exe_name = {"task": "task", "just": "just", "nu": "nu", "minizinc": "minizinc"}[tool] + exe = find_executable(unpacked, exe_name, system) + return exe, { + "repo": repo, + "tag": tag, + "asset": name, + "sha256": digest, + "executable": str(exe), + } + +def exercise_task(exe: Path, root: Path) -> dict: + work = root / "task-work" + work.mkdir() + (work / "Taskfile.yml").write_text( + "version: '3'\n" + "tasks:\n" + " hello:\n" + " desc: portable qualification probe\n" + " cmds:\n" + " - echo task-ok\n", + encoding="utf-8", + ) + version = run([str(exe), "--version"]).stdout.strip() + listing = run([str(exe), "--list", "--json"], cwd=work).stdout + parsed = json.loads(listing) + if "hello" not in json.dumps(parsed): + raise RuntimeError(f"Task JSON discovery did not expose hello: {listing}") + execution = run([str(exe), "hello"], cwd=work) + if "task-ok" not in execution.stdout: + raise RuntimeError(f"Task execution missing marker: {execution.stdout}") + return {"version": version, "structured_discovery": True, "execution": "task-ok"} + +def exercise_just(exe: Path, root: Path) -> dict: + work = root / "just-work" + work.mkdir() + justfile = work / "justfile" + justfile.write_text("hello:\n @echo just-ok\n", encoding="utf-8") + version = run([str(exe), "--version"]).stdout.strip() + summary = run([str(exe), "--justfile", str(justfile), "--summary"], cwd=work).stdout + if "hello" not in summary: + raise RuntimeError(f"just summary did not expose hello: {summary}") + execution = run([str(exe), "--justfile", str(justfile), "hello"], cwd=work) + if "just-ok" not in execution.stdout: + raise RuntimeError(f"just execution missing marker: {execution.stdout}") + return {"version": version, "static_discovery": True, "execution": "just-ok"} + +def exercise_nu(exe: Path) -> dict: + version = run([str(exe), "--version"]).stdout.strip() + result = run([str(exe), "-c", "print (([1 2 3] | math sum) == 6)"]).stdout.strip().lower() + if "true" not in result: + raise RuntimeError(f"Nushell structured-pipeline probe failed: {result}") + return {"version": version, "structured_pipeline": True} + +def exercise_minizinc(exe: Path, root: Path) -> dict: + version_result = run([str(exe), "--version"]) + version = (version_result.stdout + version_result.stderr).strip() + work = root / "minizinc-work" + work.mkdir() + model = work / "probe.mzn" + model.write_text( + "var 0..10: x;\n" + "constraint x >= 7;\n" + "solve minimize x;\n" + "output [show(x)];\n", + encoding="utf-8", + ) + solved = run([str(exe), "--solver", "gecode", str(model)], cwd=work) + if "7" not in solved.stdout: + raise RuntimeError(f"MiniZinc optimization probe did not return expected optimum 7: {solved.stdout}") + return {"version": version, "optimization_probe": "optimal x=7"} + +def main() -> int: + system, arch = platform_key() + report = { + "schema": 1, + "system": system, + "architecture": arch, + "python": sys.version, + "tools": {}, + } + with tempfile.TemporaryDirectory(prefix="oss-organ-qualification-") as tmp: + root = Path(tmp) + executables = {} + for tool in ("task", "just", "nu", "minizinc"): + exe, evidence = install_tool(tool, root, system, arch) + executables[tool] = exe + report["tools"][tool] = evidence + + report["tools"]["task"]["probe"] = exercise_task(executables["task"], root) + report["tools"]["just"]["probe"] = exercise_just(executables["just"], root) + report["tools"]["nu"]["probe"] = exercise_nu(executables["nu"]) + report["tools"]["minizinc"]["probe"] = exercise_minizinc(executables["minizinc"], root) + + out_dir = Path("qualification-results") + out_dir.mkdir(exist_ok=True) + out = out_dir / f"report-{system}-{arch}.json" + out.write_text(json.dumps(report, indent=2, sort_keys=True) + "\n", encoding="utf-8") + print(json.dumps(report, indent=2, sort_keys=True)) + print(f"wrote {out}") + return 0 + +if __name__ == "__main__": + raise SystemExit(main()) From cec650fd19f9a631fb2459fecfac9f116888bd2d Mon Sep 17 00:00:00 2001 From: "Mark E. DeYoung" <1854350+mark-e-deyoung@users.noreply.github.com> Date: Wed, 9 Sep 2026 23:49:45 +0200 Subject: [PATCH 02/26] lab: add six-family public qualification matrix --- .github/workflows/oss-organ-qualification.yml | 55 +++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 .github/workflows/oss-organ-qualification.yml diff --git a/.github/workflows/oss-organ-qualification.yml b/.github/workflows/oss-organ-qualification.yml new file mode 100644 index 0000000..e5dbbbb --- /dev/null +++ b/.github/workflows/oss-organ-qualification.yml @@ -0,0 +1,55 @@ +name: OSS organ six-family qualification + +on: + pull_request: + paths: + - ".github/workflows/oss-organ-qualification.yml" + - "tools/oss-organ-qualify.py" + workflow_dispatch: + +permissions: + contents: read + +jobs: + qualify: + name: ${{ matrix.name }} + strategy: + fail-fast: false + matrix: + include: + - name: linux-x64 + runner: ubuntu-24.04 + - name: linux-arm64 + runner: ubuntu-24.04-arm + - name: windows-x64 + runner: windows-2025 + - name: windows-arm64 + runner: windows-11-arm + - name: macos-x64 + runner: macos-15-intel + - name: macos-arm64 + runner: macos-15 + + runs-on: ${{ matrix.runner }} + timeout-minutes: 30 + + steps: + - name: Check out public probe only + uses: actions/checkout@v5 + with: + persist-credentials: false + + - name: Record runner identity + run: python -c "import json,platform; print(json.dumps({'system':platform.system(),'machine':platform.machine(),'python':platform.python_version()}, indent=2))" + + - name: Qualify pinned public artifacts + run: python tools/oss-organ-qualify.py + + - name: Retain structured qualification evidence + if: always() + uses: actions/upload-artifact@v4 + with: + name: oss-organ-${{ matrix.name }} + path: qualification-results/*.json + if-no-files-found: warn + retention-days: 14 From 424fa1b3fd656c149226cc95c90c6ba708079eae Mon Sep 17 00:00:00 2001 From: "Mark E. DeYoung" <1854350+mark-e-deyoung@users.noreply.github.com> Date: Wed, 9 Sep 2026 23:51:29 +0200 Subject: [PATCH 03/26] lab: authenticate metadata lookup without exposing token to candidates --- tools/oss-organ-qualify.py | 29 ++++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/tools/oss-organ-qualify.py b/tools/oss-organ-qualify.py index a6e6fa6..0de1666 100644 --- a/tools/oss-organ-qualify.py +++ b/tools/oss-organ-qualify.py @@ -78,14 +78,15 @@ def asset_name(tool: str, system: str, arch: str) -> str: raise KeyError(tool) def request_json(url: str) -> dict: - req = urllib.request.Request( - url, - headers={ - "Accept": "application/vnd.github+json", - "User-Agent": "agent-dispatch-oss-organ-qualification/1", - "X-GitHub-Api-Version": "2022-11-28", - }, - ) + headers = { + "Accept": "application/vnd.github+json", + "User-Agent": "agent-dispatch-oss-organ-qualification/1", + "X-GitHub-Api-Version": "2022-11-28", + } + token = os.environ.get("QUAL_GITHUB_TOKEN") + if token: + headers["Authorization"] = f"Bearer {token}" + req = urllib.request.Request(url, headers=headers) with urllib.request.urlopen(req, timeout=30) as response: return json.load(response) @@ -147,7 +148,17 @@ def find_executable(root: Path, basename: str, system: str) -> Path: return exe def run(argv: list[str], cwd: Path | None = None) -> subprocess.CompletedProcess[str]: - result = subprocess.run(argv, cwd=cwd, text=True, capture_output=True, timeout=120) + clean_env = os.environ.copy() + clean_env.pop("QUAL_GITHUB_TOKEN", None) + clean_env.pop("GITHUB_TOKEN", None) + result = subprocess.run( + argv, + cwd=cwd, + text=True, + capture_output=True, + timeout=120, + env=clean_env, + ) if result.returncode != 0: raise RuntimeError( f"command failed ({result.returncode}): {argv}\nstdout:\n{result.stdout}\nstderr:\n{result.stderr}" From b362c6e5d40f43e49f8addb09d5dd2a9ef66d36f Mon Sep 17 00:00:00 2001 From: "Mark E. DeYoung" <1854350+mark-e-deyoung@users.noreply.github.com> Date: Wed, 9 Sep 2026 23:51:39 +0200 Subject: [PATCH 04/26] lab: use read-only metadata token with candidate isolation --- .github/workflows/oss-organ-qualification.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/oss-organ-qualification.yml b/.github/workflows/oss-organ-qualification.yml index e5dbbbb..235121a 100644 --- a/.github/workflows/oss-organ-qualification.yml +++ b/.github/workflows/oss-organ-qualification.yml @@ -43,6 +43,8 @@ jobs: run: python -c "import json,platform; print(json.dumps({'system':platform.system(),'machine':platform.machine(),'python':platform.python_version()}, indent=2))" - name: Qualify pinned public artifacts + env: + QUAL_GITHUB_TOKEN: ${{ github.token }} run: python tools/oss-organ-qualify.py - name: Retain structured qualification evidence From d639ee096ba313812927a44d0cb047f76bb05bb7 Mon Sep 17 00:00:00 2001 From: "Mark E. DeYoung" <1854350+mark-e-deyoung@users.noreply.github.com> Date: Thu, 10 Sep 2026 04:46:40 +0200 Subject: [PATCH 05/26] lab: add v1 artifact lifecycle fixture --- lab-fixtures/candidate-v1/__main__.py | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 lab-fixtures/candidate-v1/__main__.py diff --git a/lab-fixtures/candidate-v1/__main__.py b/lab-fixtures/candidate-v1/__main__.py new file mode 100644 index 0000000..674aaa9 --- /dev/null +++ b/lab-fixtures/candidate-v1/__main__.py @@ -0,0 +1,9 @@ +import argparse + +p = argparse.ArgumentParser() +p.add_argument("--version", action="store_true") +args = p.parse_args() +if args.version: + print("candidate-probe 1.0") +else: + print("candidate-probe:v1:ok") From 30c7c09aa32e1083e1deb6abe48f14daa538930a Mon Sep 17 00:00:00 2001 From: "Mark E. DeYoung" <1854350+mark-e-deyoung@users.noreply.github.com> Date: Thu, 10 Sep 2026 04:46:45 +0200 Subject: [PATCH 06/26] lab: add v2 artifact lifecycle fixture --- lab-fixtures/candidate-v2/__main__.py | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 lab-fixtures/candidate-v2/__main__.py diff --git a/lab-fixtures/candidate-v2/__main__.py b/lab-fixtures/candidate-v2/__main__.py new file mode 100644 index 0000000..76a30a0 --- /dev/null +++ b/lab-fixtures/candidate-v2/__main__.py @@ -0,0 +1,9 @@ +import argparse + +p = argparse.ArgumentParser() +p.add_argument("--version", action="store_true") +args = p.parse_args() +if args.version: + print("candidate-probe 2.0") +else: + print("candidate-probe:v2:ok") From 15d6dc0b2bdc5dc9f06ead77aadc0540495aad89 Mon Sep 17 00:00:00 2001 From: "Mark E. DeYoung" <1854350+mark-e-deyoung@users.noreply.github.com> Date: Thu, 10 Sep 2026 04:47:56 +0200 Subject: [PATCH 07/26] lab: add comparative execution and lifecycle reps --- tools/oss-organ-reps.py | 361 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 361 insertions(+) create mode 100644 tools/oss-organ-reps.py diff --git a/tools/oss-organ-reps.py b/tools/oss-organ-reps.py new file mode 100644 index 0000000..20cfdff --- /dev/null +++ b/tools/oss-organ-reps.py @@ -0,0 +1,361 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import hashlib +import importlib.util +import itertools +import json +import os +import random +import shutil +import statistics +import subprocess +import sys +import tempfile +import time +import zipapp +from pathlib import Path + +ROOT = Path(__file__).resolve().parents[1] +QUAL_PATH = ROOT / "tools" / "oss-organ-qualify.py" +spec = importlib.util.spec_from_file_location("oss_qual", QUAL_PATH) +qual = importlib.util.module_from_spec(spec) +assert spec and spec.loader +spec.loader.exec_module(qual) + + +def clean_env() -> dict[str, str]: + env = os.environ.copy() + env.pop("QUAL_GITHUB_TOKEN", None) + env.pop("GITHUB_TOKEN", None) + return env + + +def raw(argv: list[str], cwd: Path | None = None, timeout: int = 120, env: dict[str, str] | None = None): + return subprocess.run( + argv, + cwd=cwd, + text=True, + capture_output=True, + timeout=timeout, + env=env or clean_env(), + ) + + +def digest(path: Path) -> str: + h = hashlib.sha256() + with path.open("rb") as f: + for chunk in iter(lambda: f.read(1024 * 1024), b""): + h.update(chunk) + return h.hexdigest() + + +def timed(call, repeats: int = 5) -> dict: + samples = [] + for _ in range(repeats): + t0 = time.perf_counter() + call() + samples.append(time.perf_counter() - t0) + return { + "repeats": repeats, + "median_seconds": statistics.median(samples), + "min_seconds": min(samples), + "max_seconds": max(samples), + } + + +def e004(exes: dict[str, Path], root: Path, system: str) -> dict: + work = root / "e004" + work.mkdir() + (work / "workload.py").write_text( + "from pathlib import Path\n" + "import sys\n" + "mode=sys.argv[1]\n" + "if mode=='fail':\n" + " print('intentional-failure', file=sys.stderr); raise SystemExit(7)\n" + "Path('result.txt').write_text('alpha\\nbeta\\ngamma\\n', encoding='utf-8')\n" + "print('workload-ok')\n", + encoding="utf-8", + ) + (work / "Taskfile.yml").write_text( + "version: '3'\ntasks:\n" + " success:\n cmds:\n - python workload.py success\n" + " fail:\n cmds:\n - python workload.py fail\n", + encoding="utf-8", + ) + (work / "justfile").write_text( + "success:\n python workload.py success\n\n" + "fail:\n python workload.py fail\n", + encoding="utf-8", + ) + + def check_success(argv: list[str]): + r = raw(argv, cwd=work) + if r.returncode != 0 or "workload-ok" not in r.stdout: + raise RuntimeError(f"success workload failed: {argv}: {r.returncode} {r.stdout} {r.stderr}") + p = work / "result.txt" + if p.read_text(encoding="utf-8") != "alpha\nbeta\ngamma\n": + raise RuntimeError("non-deterministic workload result") + return digest(p) + + if system == "windows": + native_success = ["cmd", "/d", "/s", "/c", "python workload.py success"] + native_fail = ["cmd", "/d", "/s", "/c", "python workload.py fail"] + else: + native_success = ["/bin/sh", "-c", "python workload.py success"] + native_fail = ["/bin/sh", "-c", "python workload.py fail"] + + commands = { + "native": (native_success, native_fail), + "task": ([str(exes["task"]), "success"], [str(exes["task"]), "fail"]), + "just": ([str(exes["just"]), "--justfile", str(work / "justfile"), "success"], [str(exes["just"]), "--justfile", str(work / "justfile"), "fail"]), + "nushell": ([str(exes["nu"]), "-c", "^python workload.py success"], [str(exes["nu"]), "-c", "^python workload.py fail"]), + } + + result = {"workload": "write deterministic result; deliberate exit 7 failure", "engines": {}} + expected_digest = None + for name, (success, fail) in commands.items(): + first = check_success(success) + second = check_success(success) + if first != second: + raise RuntimeError(f"{name} failed idempotence digest check") + if expected_digest is None: + expected_digest = first + elif first != expected_digest: + raise RuntimeError(f"{name} produced different semantic result") + fr = raw(fail, cwd=work) + if fr.returncode == 0: + raise RuntimeError(f"{name} swallowed intentional failure") + result["engines"][name] = { + "idempotent_digest": first, + "failure_returncode": fr.returncode, + "failure_visible": "intentional-failure" in (fr.stdout + fr.stderr), + "startup_workload_timing": timed(lambda argv=success: check_success(argv)), + } + + task_list = raw([str(exes["task"]), "--list", "--json"], cwd=work) + result["engines"]["task"]["machine_discovery"] = task_list.returncode == 0 and "success" in task_list.stdout + just_list = raw([str(exes["just"]), "--justfile", str(work / "justfile"), "--summary"], cwd=work) + result["engines"]["just"]["machine_discovery"] = just_list.returncode == 0 and "success" in just_list.stdout + result["engines"]["nushell"]["structured_values_native"] = True + result["engines"]["native"]["machine_discovery"] = False + return result + + +def brute_opt(cost, eligible, capacity): + t_count = len(cost) + a_count = len(capacity) + best = None + used = [0] * a_count + + order = sorted(range(t_count), key=lambda t: sum(eligible[t])) + + def visit(i: int, total: int): + nonlocal best + if best is not None and total >= best: + return + if i == t_count: + best = total + return + t = order[i] + for a in range(a_count): + if eligible[t][a] and used[a] < capacity[a]: + used[a] += 1 + visit(i + 1, total + cost[t][a]) + used[a] -= 1 + + visit(0, 0) + return best + + +def c003(minizinc: Path, root: Path) -> dict: + work = root / "c003" + work.mkdir() + model = work / "allocation.mzn" + model.write_text( + "int: T; int: A;\n" + "set of int: Tasks=1..T; set of int: Actors=1..A;\n" + "array[Tasks,Actors] of 0..1: eligible;\n" + "array[Actors] of int: capacity;\n" + "array[Tasks,Actors] of int: cost;\n" + "array[Tasks] of var Actors: assign;\n" + "constraint forall(t in Tasks)(eligible[t,assign[t]] = 1);\n" + "constraint forall(a in Actors)(sum(t in Tasks)(bool2int(assign[t]=a)) <= capacity[a]);\n" + "var int: objective = sum(t in Tasks)(cost[t,assign[t]]);\n" + "solve minimize objective;\n" + "output [\"objective=\", show(objective)];\n", + encoding="utf-8", + ) + rng = random.Random(20260910) + cases = [] + for idx in range(20): + T, A = 8, 4 + cost = [[rng.randint(1, 20) for _ in range(A)] for _ in range(T)] + eligible = [[0] * A for _ in range(T)] + capacity = [0] * A + if idx % 5 == 0: + for t in range(T): + eligible[t][0] = 1 + capacity = [T - 1, T, T, T] + else: + base = [rng.randrange(A) for _ in range(T)] + counts = [base.count(a) for a in range(A)] + capacity = [counts[a] + rng.randint(0, 2) for a in range(A)] + for t, a0 in enumerate(base): + eligible[t][a0] = 1 + for a in range(A): + if rng.random() < 0.55: + eligible[t][a] = 1 + expected = brute_opt(cost, eligible, capacity) + flat_e = ",".join(str(x) for row in eligible for x in row) + flat_c = ",".join(str(x) for row in cost for x in row) + dzn = work / f"case-{idx:02d}.dzn" + dzn.write_text( + f"T={T}; A={A};\n" + f"eligible=array2d(1..T,1..A,[{flat_e}]);\n" + f"capacity=[{','.join(map(str,capacity))}];\n" + f"cost=array2d(1..T,1..A,[{flat_c}]);\n", + encoding="utf-8", + ) + t0 = time.perf_counter() + r = raw([str(minizinc), "--solver", "gecode", str(model), str(dzn)], cwd=work, timeout=120) + elapsed = time.perf_counter() - t0 + text = r.stdout + r.stderr + if expected is None: + observed = None if "UNSATISFIABLE" in text else "unexpected-feasible" + agreement = observed is None + else: + marker = "objective=" + if marker not in text: + observed = None + else: + tail = text.split(marker, 1)[1] + num = "".join(ch for ch in tail.splitlines()[0] if ch in "-0123456789") + observed = int(num) if num else None + agreement = observed == expected + if not agreement: + raise RuntimeError(f"MiniZinc disagreement case {idx}: expected={expected}, observed={observed}, output={text}") + cases.append({"case": idx, "expected": expected, "observed": observed, "seconds": elapsed, "dzn_bytes": dzn.stat().st_size}) + return { + "seed": 20260910, + "cases": len(cases), + "feasible": sum(c["expected"] is not None for c in cases), + "infeasible": sum(c["expected"] is None for c in cases), + "agreement": sum(c["expected"] == c["observed"] for c in cases), + "model_bytes": model.stat().st_size, + "mean_dzn_bytes": statistics.mean(c["dzn_bytes"] for c in cases), + "median_solver_seconds": statistics.median(c["seconds"] for c in cases), + "max_solver_seconds": max(c["seconds"] for c in cases), + } + + +def a002(root: Path) -> dict: + work = root / "a002" + work.mkdir() + v1 = work / "candidate-v1.pyz" + v2 = work / "candidate-v2.pyz" + zipapp.create_archive(ROOT / "lab-fixtures" / "candidate-v1", target=v1) + zipapp.create_archive(ROOT / "lab-fixtures" / "candidate-v2", target=v2) + install = work / "install" + install.mkdir() + installed = install / "candidate.pyz" + + def version(): + r = raw([sys.executable, str(installed), "--version"]) + if r.returncode != 0: + raise RuntimeError(r.stderr) + return r.stdout.strip() + + shutil.copy2(v1, installed) + first_digest = digest(installed) + if version() != "candidate-probe 1.0": + raise RuntimeError("v1 install failed") + shutil.copy2(v1, installed) + reinstall_digest = digest(installed) + if reinstall_digest != first_digest or version() != "candidate-probe 1.0": + raise RuntimeError("idempotent reinstall failed") + shutil.copy2(v2, installed) + second_digest = digest(installed) + if second_digest == first_digest or version() != "candidate-probe 2.0": + raise RuntimeError("upgrade failed") + installed.unlink() + if installed.exists(): + raise RuntimeError("remove failed") + return { + "source_commit": os.environ.get("GITHUB_SHA"), + "artifact_format": "python-zipapp-lab-fixture", + "v1_sha256": first_digest, + "reinstall_same_digest": first_digest == reinstall_digest, + "v2_sha256": second_digest, + "upgrade_changed_digest": second_digest != first_digest, + "remove_verified": not installed.exists(), + } + + +def d002(root: Path) -> dict: + target = root / "xa11y-site" + target.mkdir() + install = raw( + [sys.executable, "-m", "pip", "install", "--disable-pip-version-check", "--only-binary=:all:", "--no-deps", "--target", str(target), "xa11y==0.14.0"], + timeout=180, + ) + result = { + "version": "0.14.0", + "wheel_installable": install.returncode == 0, + "install_stderr_tail": install.stderr[-1200:], + "claim_scope": "packaging/import/error semantics only; not interactive desktop dogfood", + } + if install.returncode != 0: + return result + env = clean_env() + env["PYTHONPATH"] = str(target) + probe_code = ( + "import json, xa11y\n" + "d={'imported':True,'module':xa11y.__name__}\n" + "try:\n" + " xa11y.App.by_name('__agent_dispatch_definitely_missing__', timeout=0)\n" + " d['missing_app']='unexpected-success'\n" + "except Exception as e:\n" + " d['missing_app_exception']=type(e).__name__; d['missing_app_message']=str(e)[:500]\n" + "print(json.dumps(d))\n" + ) + probe = raw([sys.executable, "-c", probe_code], timeout=60, env=env) + result["probe_returncode"] = probe.returncode + result["probe_stdout"] = probe.stdout[-1500:] + result["probe_stderr"] = probe.stderr[-1500:] + if probe.returncode == 0: + try: + result["probe"] = json.loads(probe.stdout.strip().splitlines()[-1]) + except Exception: + pass + return result + + +def main() -> int: + system, arch = qual.platform_key() + report = {"schema": 1, "system": system, "architecture": arch, "reps": {}} + with tempfile.TemporaryDirectory(prefix="oss-organ-reps-") as tmp: + root = Path(tmp) + exes = {} + install_evidence = {} + for tool in ("task", "just", "nu", "minizinc"): + exe, evidence = qual.install_tool(tool, root, system, arch) + exes[tool] = exe + install_evidence[tool] = evidence + report["pinned_tools"] = install_evidence + report["reps"]["E-004"] = e004(exes, root, system) + report["reps"]["C-003"] = c003(exes["minizinc"], root) + report["reps"]["A-002"] = a002(root) + report["reps"]["D-002"] = d002(root) + + out_dir = ROOT / "qualification-results" + out_dir.mkdir(exist_ok=True) + out = out_dir / f"reps-{system}-{arch}.json" + out.write_text(json.dumps(report, indent=2, sort_keys=True) + "\n", encoding="utf-8") + print(json.dumps(report, indent=2, sort_keys=True)) + print(f"wrote {out}") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) From e049ef3eeb7643fc7ba11786d36b6d6e0e9c1163 Mon Sep 17 00:00:00 2001 From: "Mark E. DeYoung" <1854350+mark-e-deyoung@users.noreply.github.com> Date: Thu, 10 Sep 2026 04:48:09 +0200 Subject: [PATCH 08/26] lab: execute E-004 C-003 A-002 D-002 on six-family matrix --- .github/workflows/oss-organ-qualification.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.github/workflows/oss-organ-qualification.yml b/.github/workflows/oss-organ-qualification.yml index 235121a..0d28158 100644 --- a/.github/workflows/oss-organ-qualification.yml +++ b/.github/workflows/oss-organ-qualification.yml @@ -5,6 +5,8 @@ on: paths: - ".github/workflows/oss-organ-qualification.yml" - "tools/oss-organ-qualify.py" + - "tools/oss-organ-reps.py" + - "lab-fixtures/**" workflow_dispatch: permissions: @@ -47,6 +49,11 @@ jobs: QUAL_GITHUB_TOKEN: ${{ github.token }} run: python tools/oss-organ-qualify.py + - name: Run bounded comparative reps + env: + QUAL_GITHUB_TOKEN: ${{ github.token }} + run: python tools/oss-organ-reps.py + - name: Retain structured qualification evidence if: always() uses: actions/upload-artifact@v4 From 0c7aa9d177c0a29200afde240a03cefd1f4eaac2 Mon Sep 17 00:00:00 2001 From: "Mark E. DeYoung" <1854350+mark-e-deyoung@users.noreply.github.com> Date: Thu, 10 Sep 2026 04:52:08 +0200 Subject: [PATCH 09/26] lab: add Task discovery and reproducibility followup reps --- tools/oss-organ-followup.py | 77 +++++++++++++++++++++++++++++++++++++ 1 file changed, 77 insertions(+) create mode 100644 tools/oss-organ-followup.py diff --git a/tools/oss-organ-followup.py b/tools/oss-organ-followup.py new file mode 100644 index 0000000..dcee76b --- /dev/null +++ b/tools/oss-organ-followup.py @@ -0,0 +1,77 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import hashlib +import importlib.util +import json +import os +import tempfile +import zipapp +from pathlib import Path + +ROOT = Path(__file__).resolve().parents[1] +spec = importlib.util.spec_from_file_location("oss_qual", ROOT / "tools" / "oss-organ-qualify.py") +qual = importlib.util.module_from_spec(spec) +assert spec and spec.loader +spec.loader.exec_module(qual) + + +def sha(path: Path) -> str: + h = hashlib.sha256() + h.update(path.read_bytes()) + return h.hexdigest() + + +def main() -> int: + system, arch = qual.platform_key() + report = {"schema": 1, "system": system, "architecture": arch, "reps": {}} + with tempfile.TemporaryDirectory(prefix="oss-organ-followup-") as tmp: + root = Path(tmp) + task, evidence = qual.install_tool("task", root, system, arch) + work = root / "task" + work.mkdir() + (work / "Taskfile.yml").write_text( + "version: '3'\ntasks:\n" + " success:\n desc: successful bounded workload\n cmds:\n - python -c \"print('ok')\"\n" + " fail:\n desc: intentional failure workload\n cmds:\n - python -c \"raise SystemExit(7)\"\n", + encoding="utf-8", + ) + listing = qual.run([str(task), "--list-all", "--json"], cwd=work) + parsed = json.loads(listing.stdout) + task_discovery = "success" in json.dumps(parsed) and "fail" in json.dumps(parsed) + if not task_discovery: + raise RuntimeError(f"Task JSON discovery failed after fixture correction: {listing.stdout}") + report["reps"]["E-005"] = { + "purpose": "correct E-004 fixture error; distinguish tool behavior from undescribed-task listing semantics", + "task_version": evidence["tag"], + "list_all_json": True, + "both_tasks_discovered": task_discovery, + } + + source = ROOT / "lab-fixtures" / "candidate-v1" / "__main__.py" + a = root / "a.pyz" + b = root / "b.pyz" + zipapp.create_archive(source.parent, target=a) + zipapp.create_archive(source.parent, target=b) + report["reps"]["A-003"] = { + "purpose": "separate same-runner rebuild determinism from cross-runner artifact divergence", + "source_sha256": sha(source), + "source_bytes": source.stat().st_size, + "build1_sha256": sha(a), + "build2_sha256": sha(b), + "same_runner_rebuild_equal": sha(a) == sha(b), + "source_commit": os.environ.get("GITHUB_SHA"), + } + if not report["reps"]["A-003"]["same_runner_rebuild_equal"]: + raise RuntimeError("same-runner zipapp rebuild was not deterministic") + + out_dir = ROOT / "qualification-results" + out_dir.mkdir(exist_ok=True) + out = out_dir / f"followup-{system}-{arch}.json" + out.write_text(json.dumps(report, indent=2, sort_keys=True) + "\n", encoding="utf-8") + print(json.dumps(report, indent=2, sort_keys=True)) + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) From 8308231bd4cbaf9a87c3ee510d71fe554a7cc378 Mon Sep 17 00:00:00 2001 From: "Mark E. DeYoung" <1854350+mark-e-deyoung@users.noreply.github.com> Date: Thu, 10 Sep 2026 04:52:22 +0200 Subject: [PATCH 10/26] lab: run corrective Task and artifact reproducibility reps --- .github/workflows/oss-organ-qualification.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/oss-organ-qualification.yml b/.github/workflows/oss-organ-qualification.yml index 0d28158..e13ca8b 100644 --- a/.github/workflows/oss-organ-qualification.yml +++ b/.github/workflows/oss-organ-qualification.yml @@ -6,6 +6,7 @@ on: - ".github/workflows/oss-organ-qualification.yml" - "tools/oss-organ-qualify.py" - "tools/oss-organ-reps.py" + - "tools/oss-organ-followup.py" - "lab-fixtures/**" workflow_dispatch: @@ -54,6 +55,11 @@ jobs: QUAL_GITHUB_TOKEN: ${{ github.token }} run: python tools/oss-organ-reps.py + - name: Run corrective follow-up reps + env: + QUAL_GITHUB_TOKEN: ${{ github.token }} + run: python tools/oss-organ-followup.py + - name: Retain structured qualification evidence if: always() uses: actions/upload-artifact@v4 From 55fbcf210d79730738487757665bd0c966367d3d Mon Sep 17 00:00:00 2001 From: "Mark E. DeYoung" <1854350+mark-e-deyoung@users.noreply.github.com> Date: Thu, 10 Sep 2026 04:55:38 +0200 Subject: [PATCH 11/26] lab: tolerate disposable Windows temp cleanup races --- tools/oss-organ-followup.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tools/oss-organ-followup.py b/tools/oss-organ-followup.py index dcee76b..b53d643 100644 --- a/tools/oss-organ-followup.py +++ b/tools/oss-organ-followup.py @@ -25,7 +25,10 @@ def sha(path: Path) -> str: def main() -> int: system, arch = qual.platform_key() report = {"schema": 1, "system": system, "architecture": arch, "reps": {}} - with tempfile.TemporaryDirectory(prefix="oss-organ-followup-") as tmp: + # Windows hosted runners can retain a short-lived executable file handle after + # Task exits. The runner is disposable, so cleanup failure must not overwrite + # successful experiment evidence. Rep assertions still fail normally. + with tempfile.TemporaryDirectory(prefix="oss-organ-followup-", ignore_cleanup_errors=True) as tmp: root = Path(tmp) task, evidence = qual.install_tool("task", root, system, arch) work = root / "task" From 0b1af7c960c5350238407583db9443bd0cb023ab Mon Sep 17 00:00:00 2001 From: "Mark E. DeYoung" <1854350+mark-e-deyoung@users.noreply.github.com> Date: Thu, 10 Sep 2026 05:33:11 +0200 Subject: [PATCH 12/26] lab: stage xa11y Windows ARM64 Python wheel candidate --- .../README.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 candidates/xa11y-windows-arm64-python-wheel/README.md diff --git a/candidates/xa11y-windows-arm64-python-wheel/README.md b/candidates/xa11y-windows-arm64-python-wheel/README.md new file mode 100644 index 0000000..89b4501 --- /dev/null +++ b/candidates/xa11y-windows-arm64-python-wheel/README.md @@ -0,0 +1,19 @@ +# xa11y Windows ARM64 Python wheel candidate + +Public candidate experiment against upstream `xa11y/xa11y` release `v0.14.0`, commit `7e623f3d4d24264dd9a56399090a8099020b46ea`. + +## Publicly reconstructable problem + +`xa11y` publishes Python wheels for Linux x86_64/aarch64 and macOS x86_64/aarch64, but its Windows Python-wheel job targets x86_64 only. The same public release workflow already builds the JavaScript native binding for `aarch64-pc-windows-msvc`. A native Windows ARM64 GitHub-hosted runner cannot obtain `xa11y==0.14.0` with binary-only pip installation. + +## Candidate + +Extend the Windows Python-wheel build to include a native Windows ARM64 runner and `aarch64` maturin target while preserving the existing x86_64 build. + +The adjacent `publish.patch` is the intended minimal upstream-shaped change. The lab workflow builds from the exact unmodified upstream release commit, using the additional target/runner implied by that patch, then installs and probes the exact generated wheel on native Windows ARM64. + +## Qualification claim + +A passing lab proves only that the missing Python wheel can be built, installed, imported, and exercised on the native Windows ARM64 hosted runner. It does not prove interactive desktop accessibility behavior; that remains a separate physical dogfood tier. + +No private source, fixtures, endpoints, rationale, or credentials are used. \ No newline at end of file From 0cb17102560c8c26b648a9f5dc64d95901af62a1 Mon Sep 17 00:00:00 2001 From: "Mark E. DeYoung" <1854350+mark-e-deyoung@users.noreply.github.com> Date: Thu, 10 Sep 2026 05:33:18 +0200 Subject: [PATCH 13/26] lab: add xa11y Windows ARM64 publish patch --- .../publish.patch | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 candidates/xa11y-windows-arm64-python-wheel/publish.patch diff --git a/candidates/xa11y-windows-arm64-python-wheel/publish.patch b/candidates/xa11y-windows-arm64-python-wheel/publish.patch new file mode 100644 index 0000000..c32e247 --- /dev/null +++ b/candidates/xa11y-windows-arm64-python-wheel/publish.patch @@ -0,0 +1,37 @@ +diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml +--- a/.github/workflows/publish.yml ++++ b/.github/workflows/publish.yml +@@ + build-wheels-windows: +- name: Build wheels (Windows) ++ name: Build wheels (Windows ${{ matrix.target }}) + needs: [preflight, version-bump] + if: always() && needs.preflight.result == 'success' + && (needs.version-bump.result == 'success' || needs.version-bump.result == 'skipped') +- runs-on: windows-latest ++ strategy: ++ fail-fast: false ++ matrix: ++ include: ++ - runner: windows-latest ++ target: x86_64 ++ - runner: windows-11-arm ++ target: aarch64 ++ runs-on: ${{ matrix.runner }} + steps: + - uses: actions/checkout@v7 + with: + ref: main +@@ + - uses: PyO3/maturin-action@v1 + with: +- target: x86_64 ++ target: ${{ matrix.target }} + args: --release --out dist + working-directory: xa11y-python + + - uses: actions/upload-artifact@v7 + with: +- name: wheels-windows-x86_64 ++ name: wheels-windows-${{ matrix.target }} + path: xa11y-python/dist From e24973d36a90df52213ae951111d77482d0d4c2b Mon Sep 17 00:00:00 2001 From: "Mark E. DeYoung" <1854350+mark-e-deyoung@users.noreply.github.com> Date: Thu, 10 Sep 2026 05:33:39 +0200 Subject: [PATCH 14/26] lab: build and dogfood xa11y Windows ARM64 Python candidate --- .../xa11y-windows-arm64-candidate.yml | 102 ++++++++++++++++++ 1 file changed, 102 insertions(+) create mode 100644 .github/workflows/xa11y-windows-arm64-candidate.yml diff --git a/.github/workflows/xa11y-windows-arm64-candidate.yml b/.github/workflows/xa11y-windows-arm64-candidate.yml new file mode 100644 index 0000000..8a61a5d --- /dev/null +++ b/.github/workflows/xa11y-windows-arm64-candidate.yml @@ -0,0 +1,102 @@ +name: xa11y Windows ARM64 Python candidate + +on: + pull_request: + paths: + - ".github/workflows/xa11y-windows-arm64-candidate.yml" + - "candidates/xa11y-windows-arm64-python-wheel/**" + workflow_dispatch: + +permissions: + contents: read + +jobs: + build-install-probe: + name: native Windows ARM64 wheel + runs-on: windows-11-arm + timeout-minutes: 45 + + steps: + - name: Check out exact public upstream release source + uses: actions/checkout@v5 + with: + repository: xa11y/xa11y + ref: 7e623f3d4d24264dd9a56399090a8099020b46ea + persist-credentials: false + + - name: Verify source identity + shell: pwsh + run: | + $sha = (git rev-parse HEAD).Trim() + if ($sha -ne '7e623f3d4d24264dd9a56399090a8099020b46ea') { throw "unexpected source $sha" } + Write-Host "source=$sha" + + - name: Set up native ARM64 Python + uses: actions/setup-python@v7 + with: + python-version: "3.12" + architecture: arm64 + + - name: Build candidate wheel using upstream release machinery + uses: PyO3/maturin-action@v1 + with: + target: aarch64 + args: --release --out dist + working-directory: xa11y-python + + - name: Install and exercise the exact generated wheel + shell: pwsh + run: | + $wheel = Get-ChildItem xa11y-python/dist/*.whl | Select-Object -First 1 + if (-not $wheel) { throw 'candidate wheel was not produced' } + Write-Host "wheel=$($wheel.Name)" + python -m pip install --disable-pip-version-check --no-index --no-deps --only-binary=:all: --find-links xa11y-python/dist xa11y==0.14.0 + @' + import importlib.metadata + import json + import platform + import xa11y + + evidence = { + "source_commit": "7e623f3d4d24264dd9a56399090a8099020b46ea", + "package_version": importlib.metadata.version("xa11y"), + "system": platform.system(), + "machine": platform.machine(), + "python": platform.python_version(), + "imported": xa11y.__name__ == "xa11y", + "app_api_present": hasattr(xa11y, "App") and hasattr(xa11y.App, "by_name"), + } + try: + xa11y.App.by_name("__agent_dispatch_candidate_missing_app__", timeout=0) + evidence["missing_app"] = "unexpected-success" + except Exception as exc: + evidence["missing_app_exception"] = type(exc).__name__ + evidence["missing_app_message"] = str(exc)[:1000] + evidence["missing_app_is_xa11y_error"] = isinstance(exc, xa11y.XA11yError) + + print(json.dumps(evidence, indent=2, sort_keys=True)) + if evidence["package_version"] != "0.14.0": + raise SystemExit("wrong package version") + if not evidence["imported"] or not evidence["app_api_present"]: + raise SystemExit("candidate API probe failed") + if evidence.get("missing_app") == "unexpected-success": + raise SystemExit("missing-app negative control unexpectedly succeeded") + if not evidence.get("missing_app_is_xa11y_error", False): + raise SystemExit("missing-app failure was not surfaced as xa11y error") + with open("candidate-evidence.json", "w", encoding="utf-8") as f: + json.dump(evidence, f, indent=2, sort_keys=True) + f.write("\n") + '@ | Set-Content -Encoding utf8 candidate_probe.py + python candidate_probe.py + xa11y --help + + - name: Retain installable public candidate and evidence + if: always() + uses: actions/upload-artifact@v7 + with: + name: xa11y-0.14.0-windows-arm64-candidate + path: | + xa11y-python/dist/*.whl + candidate-evidence.json + if-no-files-found: warn + retention-days: 14 From 6a3adca259fbe92b5903abde8ed0a03f34fdaf01 Mon Sep 17 00:00:00 2001 From: "Mark E. DeYoung" <1854350+mark-e-deyoung@users.noreply.github.com> Date: Thu, 10 Sep 2026 05:36:14 +0200 Subject: [PATCH 15/26] lab: use release-synchronized xa11y source for ARM64 wheel dogfood --- .github/workflows/xa11y-windows-arm64-candidate.yml | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/.github/workflows/xa11y-windows-arm64-candidate.yml b/.github/workflows/xa11y-windows-arm64-candidate.yml index 8a61a5d..757c6fe 100644 --- a/.github/workflows/xa11y-windows-arm64-candidate.yml +++ b/.github/workflows/xa11y-windows-arm64-candidate.yml @@ -17,18 +17,18 @@ jobs: timeout-minutes: 45 steps: - - name: Check out exact public upstream release source + - name: Check out exact public release-synchronized source uses: actions/checkout@v5 with: repository: xa11y/xa11y - ref: 7e623f3d4d24264dd9a56399090a8099020b46ea + ref: 44594a9705a3f3213a9b58bc205f4e6335c9606b persist-credentials: false - name: Verify source identity shell: pwsh run: | $sha = (git rev-parse HEAD).Trim() - if ($sha -ne '7e623f3d4d24264dd9a56399090a8099020b46ea') { throw "unexpected source $sha" } + if ($sha -ne '44594a9705a3f3213a9b58bc205f4e6335c9606b') { throw "unexpected source $sha" } Write-Host "source=$sha" - name: Set up native ARM64 Python @@ -50,6 +50,7 @@ jobs: $wheel = Get-ChildItem xa11y-python/dist/*.whl | Select-Object -First 1 if (-not $wheel) { throw 'candidate wheel was not produced' } Write-Host "wheel=$($wheel.Name)" + if ($wheel.Name -notmatch 'win_arm64') { throw "candidate is not a Windows ARM64 wheel: $($wheel.Name)" } python -m pip install --disable-pip-version-check --no-index --no-deps --only-binary=:all: --find-links xa11y-python/dist xa11y==0.14.0 @' import importlib.metadata @@ -58,7 +59,7 @@ jobs: import xa11y evidence = { - "source_commit": "7e623f3d4d24264dd9a56399090a8099020b46ea", + "source_commit": "44594a9705a3f3213a9b58bc205f4e6335c9606b", "package_version": importlib.metadata.version("xa11y"), "system": platform.system(), "machine": platform.machine(), From d4828807c901b7e8cecb00c94d2e7d594526f6be Mon Sep 17 00:00:00 2001 From: "Mark E. DeYoung" <1854350+mark-e-deyoung@users.noreply.github.com> Date: Thu, 10 Sep 2026 05:38:54 +0200 Subject: [PATCH 16/26] lab: record qualified xa11y Windows ARM64 candidate --- .../README.md | 22 +++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/candidates/xa11y-windows-arm64-python-wheel/README.md b/candidates/xa11y-windows-arm64-python-wheel/README.md index 89b4501..f8c82c9 100644 --- a/candidates/xa11y-windows-arm64-python-wheel/README.md +++ b/candidates/xa11y-windows-arm64-python-wheel/README.md @@ -1,6 +1,6 @@ # xa11y Windows ARM64 Python wheel candidate -Public candidate experiment against upstream `xa11y/xa11y` release `v0.14.0`, commit `7e623f3d4d24264dd9a56399090a8099020b46ea`. +Public candidate experiment for upstream `xa11y/xa11y` release 0.14.0. ## Publicly reconstructable problem @@ -10,10 +10,24 @@ Public candidate experiment against upstream `xa11y/xa11y` release `v0.14.0`, co Extend the Windows Python-wheel build to include a native Windows ARM64 runner and `aarch64` maturin target while preserving the existing x86_64 build. -The adjacent `publish.patch` is the intended minimal upstream-shaped change. The lab workflow builds from the exact unmodified upstream release commit, using the additional target/runner implied by that patch, then installs and probes the exact generated wheel on native Windows ARM64. +The adjacent `publish.patch` is the intended minimal upstream-shaped change. -## Qualification claim +## Public qualification -A passing lab proves only that the missing Python wheel can be built, installed, imported, and exercised on the native Windows ARM64 hosted runner. It does not prove interactive desktop accessibility behavior; that remains a separate physical dogfood tier. +The successful candidate run used exact public release-synchronized upstream commit `44594a9705a3f3213a9b58bc205f4e6335c9606b` on native `windows-11-arm` with ARM64 CPython 3.12.10 and the same `PyO3/maturin-action@v1` build mechanism used upstream. + +It produced: + +`xa11y-0.14.0-cp39-abi3-win_arm64.whl` + +The job then installed exactly that locally generated wheel with pip using `--no-index --only-binary=:all:`, imported `xa11y`, confirmed `App.by_name`, and verified a missing-application negative control surfaced as `SelectorNotMatchedError`, an `XA11yError`. + +The retained Actions artifact is `xa11y-0.14.0-windows-arm64-candidate` from candidate workflow run 34433964860. + +An earlier control against annotated tag target `7e623f3d4d24264dd9a56399090a8099020b46ea` successfully built a native ARM64 wheel but exposed an important release-process detail: that tagged commit still carried Python binding version 0.13.0. Upstream's subsequent release-synchronization commits advance the Python package to 0.14.0. The successful qualification therefore pins the exact release-synchronized public source state rather than silently overriding package metadata. + +## Claim boundary + +This proves the missing Python wheel can be built, installed, imported, and exercised on native Windows ARM64. It does not claim interactive desktop accessibility behavior; that remains a separate physical dogfood tier. No private source, fixtures, endpoints, rationale, or credentials are used. \ No newline at end of file From c7cd100065ac3d0dd4a8fd4f72829fe138492e50 Mon Sep 17 00:00:00 2001 From: "Mark E. DeYoung" <1854350+mark-e-deyoung@users.noreply.github.com> Date: Thu, 10 Sep 2026 08:55:16 +0200 Subject: [PATCH 17/26] lab: separate xa11y ARM64 cross-build from native dogfood --- .../xa11y-windows-arm64-candidate.yml | 74 ++++++++++++++++--- 1 file changed, 62 insertions(+), 12 deletions(-) diff --git a/.github/workflows/xa11y-windows-arm64-candidate.yml b/.github/workflows/xa11y-windows-arm64-candidate.yml index 757c6fe..1f59bee 100644 --- a/.github/workflows/xa11y-windows-arm64-candidate.yml +++ b/.github/workflows/xa11y-windows-arm64-candidate.yml @@ -11,9 +11,9 @@ permissions: contents: read jobs: - build-install-probe: - name: native Windows ARM64 wheel - runs-on: windows-11-arm + build-wheel: + name: cross-build Windows ARM64 wheel + runs-on: windows-latest timeout-minutes: 45 steps: @@ -31,27 +31,74 @@ jobs: if ($sha -ne '44594a9705a3f3213a9b58bc205f4e6335c9606b') { throw "unexpected source $sha" } Write-Host "source=$sha" - - name: Set up native ARM64 Python + - name: Set up host Python uses: actions/setup-python@v7 with: python-version: "3.12" - architecture: arm64 + architecture: x64 - - name: Build candidate wheel using upstream release machinery + - name: Cross-build candidate wheel using upstream release machinery uses: PyO3/maturin-action@v1 with: target: aarch64 args: --release --out dist working-directory: xa11y-python - - name: Install and exercise the exact generated wheel + - name: Verify and describe produced wheel shell: pwsh run: | $wheel = Get-ChildItem xa11y-python/dist/*.whl | Select-Object -First 1 if (-not $wheel) { throw 'candidate wheel was not produced' } - Write-Host "wheel=$($wheel.Name)" if ($wheel.Name -notmatch 'win_arm64') { throw "candidate is not a Windows ARM64 wheel: $($wheel.Name)" } - python -m pip install --disable-pip-version-check --no-index --no-deps --only-binary=:all: --find-links xa11y-python/dist xa11y==0.14.0 + $digest = (Get-FileHash -Algorithm SHA256 $wheel.FullName).Hash.ToLowerInvariant() + @{ + source_commit = '44594a9705a3f3213a9b58bc205f4e6335c9606b' + build_host = 'windows-latest-x64' + maturin_target = 'aarch64' + wheel = $wheel.Name + wheel_sha256 = $digest + } | ConvertTo-Json | Set-Content -Encoding utf8 build-evidence.json + Get-Content build-evidence.json + + - name: Publish exact candidate wheel for dogfood job + uses: actions/upload-artifact@v7 + with: + name: xa11y-crossbuilt-windows-arm64-wheel + path: | + xa11y-python/dist/*.whl + build-evidence.json + if-no-files-found: error + retention-days: 14 + + dogfood-wheel: + name: dogfood exact wheel on native Windows ARM64 + needs: build-wheel + runs-on: windows-11-arm + timeout-minutes: 20 + + steps: + - name: Set up native ARM64 Python + uses: actions/setup-python@v7 + with: + python-version: "3.12" + architecture: arm64 + + - name: Download exact cross-built candidate + uses: actions/download-artifact@v8 + with: + name: xa11y-crossbuilt-windows-arm64-wheel + path: candidate + + - name: Install and exercise exact downloaded wheel + shell: pwsh + run: | + $wheel = Get-ChildItem candidate/*.whl | Select-Object -First 1 + if (-not $wheel) { throw 'candidate wheel was not downloaded' } + if ($wheel.Name -notmatch 'win_arm64') { throw "candidate is not a Windows ARM64 wheel: $($wheel.Name)" } + $digest = (Get-FileHash -Algorithm SHA256 $wheel.FullName).Hash.ToLowerInvariant() + $build = Get-Content candidate/build-evidence.json | ConvertFrom-Json + if ($digest -ne $build.wheel_sha256) { throw "artifact digest changed in transfer" } + python -m pip install --disable-pip-version-check --no-index --no-deps --only-binary=:all: --find-links candidate xa11y==0.14.0 @' import importlib.metadata import json @@ -78,6 +125,8 @@ jobs: print(json.dumps(evidence, indent=2, sort_keys=True)) if evidence["package_version"] != "0.14.0": raise SystemExit("wrong package version") + if evidence["machine"].upper() != "ARM64": + raise SystemExit("dogfood did not run on native ARM64") if not evidence["imported"] or not evidence["app_api_present"]: raise SystemExit("candidate API probe failed") if evidence.get("missing_app") == "unexpected-success": @@ -91,13 +140,14 @@ jobs: python candidate_probe.py xa11y --help - - name: Retain installable public candidate and evidence + - name: Retain qualified installable public candidate and evidence if: always() uses: actions/upload-artifact@v7 with: - name: xa11y-0.14.0-windows-arm64-candidate + name: xa11y-0.14.0-windows-arm64-qualified-candidate path: | - xa11y-python/dist/*.whl + candidate/*.whl + candidate/build-evidence.json candidate-evidence.json if-no-files-found: warn retention-days: 14 From 1b9e7e107e55dcb26131127c564ad3f076f0dae5 Mon Sep 17 00:00:00 2001 From: "Mark E. DeYoung" <1854350+mark-e-deyoung@users.noreply.github.com> Date: Thu, 10 Sep 2026 08:58:12 +0200 Subject: [PATCH 18/26] lab: fix nested xa11y candidate artifact dogfood path --- .github/workflows/xa11y-windows-arm64-candidate.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/xa11y-windows-arm64-candidate.yml b/.github/workflows/xa11y-windows-arm64-candidate.yml index 1f59bee..f0834c3 100644 --- a/.github/workflows/xa11y-windows-arm64-candidate.yml +++ b/.github/workflows/xa11y-windows-arm64-candidate.yml @@ -92,13 +92,13 @@ jobs: - name: Install and exercise exact downloaded wheel shell: pwsh run: | - $wheel = Get-ChildItem candidate/*.whl | Select-Object -First 1 + $wheel = Get-ChildItem candidate -Recurse -Filter *.whl | Select-Object -First 1 if (-not $wheel) { throw 'candidate wheel was not downloaded' } if ($wheel.Name -notmatch 'win_arm64') { throw "candidate is not a Windows ARM64 wheel: $($wheel.Name)" } $digest = (Get-FileHash -Algorithm SHA256 $wheel.FullName).Hash.ToLowerInvariant() $build = Get-Content candidate/build-evidence.json | ConvertFrom-Json if ($digest -ne $build.wheel_sha256) { throw "artifact digest changed in transfer" } - python -m pip install --disable-pip-version-check --no-index --no-deps --only-binary=:all: --find-links candidate xa11y==0.14.0 + python -m pip install --disable-pip-version-check --no-index --no-deps --only-binary=:all: $wheel.FullName @' import importlib.metadata import json @@ -146,7 +146,7 @@ jobs: with: name: xa11y-0.14.0-windows-arm64-qualified-candidate path: | - candidate/*.whl + candidate/**/*.whl candidate/build-evidence.json candidate-evidence.json if-no-files-found: warn From f9462dfeff903101a9d439335c396f280cf8d68a Mon Sep 17 00:00:00 2001 From: "Mark E. DeYoung" <1854350+mark-e-deyoung@users.noreply.github.com> Date: Thu, 10 Sep 2026 09:07:56 +0200 Subject: [PATCH 19/26] lab: simplify xa11y ARM64 wheel patch to cross-build target matrix --- .../xa11y-windows-arm64-python-wheel/publish.patch | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/candidates/xa11y-windows-arm64-python-wheel/publish.patch b/candidates/xa11y-windows-arm64-python-wheel/publish.patch index c32e247..cdc65a2 100644 --- a/candidates/xa11y-windows-arm64-python-wheel/publish.patch +++ b/candidates/xa11y-windows-arm64-python-wheel/publish.patch @@ -8,16 +8,11 @@ diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml needs: [preflight, version-bump] if: always() && needs.preflight.result == 'success' && (needs.version-bump.result == 'success' || needs.version-bump.result == 'skipped') -- runs-on: windows-latest + runs-on: windows-latest + strategy: + fail-fast: false + matrix: -+ include: -+ - runner: windows-latest -+ target: x86_64 -+ - runner: windows-11-arm -+ target: aarch64 -+ runs-on: ${{ matrix.runner }} ++ target: [x86_64, aarch64] steps: - uses: actions/checkout@v7 with: From d67902eef393631f437f750affbbfc1a6f3d311d Mon Sep 17 00:00:00 2001 From: "Mark E. DeYoung" <1854350+mark-e-deyoung@users.noreply.github.com> Date: Thu, 10 Sep 2026 09:08:30 +0200 Subject: [PATCH 20/26] lab: add cargo-dist Windows npm extraction candidate rationale --- .../README.md | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 candidates/cargo-dist-npm-windows-extraction/README.md diff --git a/candidates/cargo-dist-npm-windows-extraction/README.md b/candidates/cargo-dist-npm-windows-extraction/README.md new file mode 100644 index 0000000..1b3def8 --- /dev/null +++ b/candidates/cargo-dist-npm-windows-extraction/README.md @@ -0,0 +1,30 @@ +# cargo-dist Windows npm extraction/error candidate + +Public candidate experiment against `axodotdev/cargo-dist` issue #2437 and public `main` commit `c65a1a932e2661e05d6640716850d36b0f47efd7`. + +## Publicly reconstructable problem + +The generated npm binary installer invokes Windows PowerShell `Expand-Archive` without an execution-policy override and treats process exit status `0` as extraction success. Under a Restricted PowerShell policy the Archive module can fail to load, and failures inside the command block are not guaranteed to produce a trustworthy non-zero process result. The installer may therefore claim success while no binary was extracted. + +Upstream issue: `axodotdev/cargo-dist#2437`. + +## Candidate hypothesis + +Keep the existing Windows PowerShell extraction path, but: + +1. invoke it with `-ExecutionPolicy Bypass` so a Restricted local policy does not prevent the built-in archive operation; +2. make `Expand-Archive` failure terminating with `-ErrorAction Stop`; +3. catch the failure and explicitly `exit 1`, preserving the existing Node-side non-zero rejection path. + +The adjacent patch is intentionally limited to `cargo-dist/templates/installer/npm/binary-install.js`. + +## Qualification + +The public lab should establish separately that: + +- the unmodified upstream shape fails or misreports under a simulated Restricted policy; +- the candidate extracts a valid Windows zip under the same policy; +- a genuinely invalid zip returns non-zero under the candidate rather than being reported as successful; +- no private fixtures, credentials, or rationale are required. + +This is candidate evidence only. It is not an upstream PR and does not imply maintainer acceptance. From e89527398dd90f7e2b755cdcd971e440e35ce9c0 Mon Sep 17 00:00:00 2001 From: "Mark E. DeYoung" <1854350+mark-e-deyoung@users.noreply.github.com> Date: Thu, 10 Sep 2026 09:08:38 +0200 Subject: [PATCH 21/26] lab: add cargo-dist Windows extraction candidate patch --- .../candidate.patch | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 candidates/cargo-dist-npm-windows-extraction/candidate.patch diff --git a/candidates/cargo-dist-npm-windows-extraction/candidate.patch b/candidates/cargo-dist-npm-windows-extraction/candidate.patch new file mode 100644 index 0000000..d67cf46 --- /dev/null +++ b/candidates/cargo-dist-npm-windows-extraction/candidate.patch @@ -0,0 +1,23 @@ +diff --git a/cargo-dist/templates/installer/npm/binary-install.js b/cargo-dist/templates/installer/npm/binary-install.js +--- a/cargo-dist/templates/installer/npm/binary-install.js ++++ b/cargo-dist/templates/installer/npm/binary-install.js +@@ + result = spawnSync("powershell.exe", [ + "-NoProfile", + "-NonInteractive", ++ "-ExecutionPolicy", ++ "Bypass", + "-Command", + `& { + param([string]$LiteralPath, [string]$DestinationPath) +- Expand-Archive -LiteralPath $LiteralPath -DestinationPath $DestinationPath -Force ++ try { ++ Expand-Archive -LiteralPath $LiteralPath -DestinationPath $DestinationPath -Force -ErrorAction Stop ++ } catch { ++ Write-Error $_ ++ exit 1 ++ } + }`, + tempFile, + this.installDirectory, + ]); From 788acdcc7342a7b19d85bfdf523e6358a90c6755 Mon Sep 17 00:00:00 2001 From: "Mark E. DeYoung" <1854350+mark-e-deyoung@users.noreply.github.com> Date: Thu, 10 Sep 2026 09:09:21 +0200 Subject: [PATCH 22/26] lab: add cargo-dist Windows npm extraction candidate rep --- ...-dist-npm-windows-extraction-candidate.yml | 154 ++++++++++++++++++ 1 file changed, 154 insertions(+) create mode 100644 .github/workflows/cargo-dist-npm-windows-extraction-candidate.yml diff --git a/.github/workflows/cargo-dist-npm-windows-extraction-candidate.yml b/.github/workflows/cargo-dist-npm-windows-extraction-candidate.yml new file mode 100644 index 0000000..a1bfd18 --- /dev/null +++ b/.github/workflows/cargo-dist-npm-windows-extraction-candidate.yml @@ -0,0 +1,154 @@ +name: cargo-dist Windows npm extraction candidate + +on: + pull_request: + paths: + - ".github/workflows/cargo-dist-npm-windows-extraction-candidate.yml" + - "candidates/cargo-dist-npm-windows-extraction/**" + workflow_dispatch: + +permissions: + contents: read + +jobs: + restricted-policy-and-error-propagation: + runs-on: windows-2025 + timeout-minutes: 20 + + steps: + - name: Check out public lab inputs + uses: actions/checkout@v7 + with: + path: lab + persist-credentials: false + + - name: Check out exact public cargo-dist source + uses: actions/checkout@v7 + with: + repository: axodotdev/cargo-dist + ref: c65a1a932e2661e05d6640716850d36b0f47efd7 + path: upstream + persist-credentials: false + + - name: Verify upstream source identity + shell: pwsh + run: | + $sha = (git -C upstream rev-parse HEAD).Trim() + if ($sha -ne 'c65a1a932e2661e05d6640716850d36b0f47efd7') { throw "unexpected source $sha" } + Copy-Item upstream/cargo-dist/templates/installer/npm/binary-install.js upstream/baseline-binary-install.js + git -C upstream apply --check ../lab/candidates/cargo-dist-npm-windows-extraction/candidate.patch + + - name: Set up Node + uses: actions/setup-node@v7 + with: + node-version: '24' + + - name: Set up Python for local fixture server + uses: actions/setup-python@v7 + with: + python-version: '3.12' + architecture: x64 + + - name: Prepare ZIP fixtures and Package.install probe + shell: pwsh + run: | + New-Item -ItemType Directory -Force lab/fixture/valid | Out-Null + Set-Content -Encoding ascii lab/fixture/valid/fixture.exe 'fixture' + Compress-Archive -Path lab/fixture/valid/fixture.exe -DestinationPath lab/fixture/valid.zip -Force + Set-Content -Encoding ascii lab/fixture/invalid.zip 'not-a-zip' + @' + const path = require('path'); + + async function main() { + const modulePath = path.resolve(process.argv[2]); + const url = process.argv[3]; + const filename = process.argv[4]; + const { Package } = require(modulePath); + const pkg = new Package( + { artifactName: 'x86_64-pc-windows-msvc' }, + 'fixture', + url, + filename, + '.zip', + { fixture: 'fixture.exe' }, + ); + await pkg.install(true); + console.log(JSON.stringify({ install_resolved: true, exists_after: pkg.exists() })); + } + + main().catch((err) => { + console.error(err); + process.exit(90); + }); + '@ | Set-Content -Encoding utf8 lab/package-install-probe.js + + - name: Exercise baseline and candidate under Restricted policy + shell: pwsh + run: | + $server = Start-Process python -ArgumentList '-m','http.server','8123','--bind','127.0.0.1','--directory','lab/fixture' -PassThru -WindowStyle Hidden + Start-Sleep -Seconds 2 + $prior = (powershell.exe -NoProfile -NonInteractive -Command 'Get-ExecutionPolicy -Scope CurrentUser').Trim() + try { + powershell.exe -NoProfile -NonInteractive -Command 'Set-ExecutionPolicy -Scope CurrentUser Restricted -Force' + + Remove-Item upstream/node_modules/.bin_real -Recurse -Force -ErrorAction SilentlyContinue + node lab/package-install-probe.js upstream/baseline-binary-install.js http://127.0.0.1:8123/valid.zip valid.zip *> baseline.log + $baselineExit = $LASTEXITCODE + $baselineExists = Test-Path upstream/node_modules/.bin_real/fixture.exe + + git -C upstream apply ../lab/candidates/cargo-dist-npm-windows-extraction/candidate.patch + Remove-Item upstream/node_modules/.bin_real -Recurse -Force -ErrorAction SilentlyContinue + node lab/package-install-probe.js upstream/cargo-dist/templates/installer/npm/binary-install.js http://127.0.0.1:8123/valid.zip valid.zip *> candidate-valid.log + $candidateExit = $LASTEXITCODE + $candidateExists = Test-Path upstream/node_modules/.bin_real/fixture.exe + + if ($candidateExit -ne 0) { Get-Content candidate-valid.log; throw "candidate failed under Restricted policy: exit $candidateExit" } + if (-not $candidateExists) { Get-Content candidate-valid.log; throw 'candidate reported success but did not extract fixture' } + if ($baselineExit -eq 0 -and $baselineExists) { Get-Content baseline.log; throw 'baseline unexpectedly installed successfully under Restricted policy' } + + @{ + upstream_commit = 'c65a1a932e2661e05d6640716850d36b0f47efd7' + baseline_exit = $baselineExit + baseline_extracted = $baselineExists + candidate_exit = $candidateExit + candidate_extracted = $candidateExists + simulated_policy = 'CurrentUser Restricted' + } | ConvertTo-Json | Set-Content -Encoding utf8 restricted-policy-evidence.json + } + finally { + powershell.exe -NoProfile -NonInteractive -Command "Set-ExecutionPolicy -Scope CurrentUser $prior -Force" + Stop-Process -Id $server.Id -Force -ErrorAction SilentlyContinue + } + + - name: Candidate must propagate genuine corrupt-ZIP failure + shell: pwsh + run: | + $server = Start-Process python -ArgumentList '-m','http.server','8124','--bind','127.0.0.1','--directory','lab/fixture' -PassThru -WindowStyle Hidden + Start-Sleep -Seconds 2 + try { + Remove-Item upstream/node_modules/.bin_real -Recurse -Force -ErrorAction SilentlyContinue + node lab/package-install-probe.js upstream/cargo-dist/templates/installer/npm/binary-install.js http://127.0.0.1:8124/invalid.zip invalid.zip *> corrupt-zip.log + $exit = $LASTEXITCODE + if ($exit -eq 0) { Get-Content corrupt-zip.log; throw 'candidate masked corrupt ZIP extraction failure' } + @{ + corrupt_zip_exit = $exit + failure_propagated = $true + } | ConvertTo-Json | Set-Content -Encoding utf8 corrupt-zip-evidence.json + } + finally { + Stop-Process -Id $server.Id -Force -ErrorAction SilentlyContinue + } + + - name: Retain public candidate evidence + if: always() + uses: actions/upload-artifact@v7 + with: + name: cargo-dist-npm-windows-extraction-candidate-evidence + path: | + restricted-policy-evidence.json + corrupt-zip-evidence.json + baseline.log + candidate-valid.log + corrupt-zip.log + if-no-files-found: warn + retention-days: 14 From bf41c6eae114593a6708ebb47e0c54d9ab746e6e Mon Sep 17 00:00:00 2001 From: "Mark E. DeYoung" <1854350+mark-e-deyoung@users.noreply.github.com> Date: Thu, 10 Sep 2026 09:13:48 +0200 Subject: [PATCH 23/26] lab: fix cargo-dist candidate patch hunk --- candidates/cargo-dist-npm-windows-extraction/candidate.patch | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/candidates/cargo-dist-npm-windows-extraction/candidate.patch b/candidates/cargo-dist-npm-windows-extraction/candidate.patch index d67cf46..6235dc1 100644 --- a/candidates/cargo-dist-npm-windows-extraction/candidate.patch +++ b/candidates/cargo-dist-npm-windows-extraction/candidate.patch @@ -1,7 +1,7 @@ diff --git a/cargo-dist/templates/installer/npm/binary-install.js b/cargo-dist/templates/installer/npm/binary-install.js --- a/cargo-dist/templates/installer/npm/binary-install.js +++ b/cargo-dist/templates/installer/npm/binary-install.js -@@ +@@ -1,10 +1,17 @@ result = spawnSync("powershell.exe", [ "-NoProfile", "-NonInteractive", @@ -20,4 +20,3 @@ diff --git a/cargo-dist/templates/installer/npm/binary-install.js b/cargo-dist/t }`, tempFile, this.installDirectory, - ]); From b700f5324c9225136df9808fb0eeed74fa8fcef4 Mon Sep 17 00:00:00 2001 From: "Mark E. DeYoung" <1854350+mark-e-deyoung@users.noreply.github.com> Date: Thu, 10 Sep 2026 09:15:08 +0200 Subject: [PATCH 24/26] lab: align cargo-dist patch hunk to exact upstream source --- candidates/cargo-dist-npm-windows-extraction/candidate.patch | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/candidates/cargo-dist-npm-windows-extraction/candidate.patch b/candidates/cargo-dist-npm-windows-extraction/candidate.patch index 6235dc1..d1992c8 100644 --- a/candidates/cargo-dist-npm-windows-extraction/candidate.patch +++ b/candidates/cargo-dist-npm-windows-extraction/candidate.patch @@ -1,7 +1,7 @@ diff --git a/cargo-dist/templates/installer/npm/binary-install.js b/cargo-dist/templates/installer/npm/binary-install.js --- a/cargo-dist/templates/installer/npm/binary-install.js +++ b/cargo-dist/templates/installer/npm/binary-install.js -@@ -1,10 +1,17 @@ +@@ -270,10 +270,17 @@ result = spawnSync("powershell.exe", [ "-NoProfile", "-NonInteractive", From 2f9becfe303286f8d22bf5b18c8add1dc2295980 Mon Sep 17 00:00:00 2001 From: "Mark E. DeYoung" <1854350+mark-e-deyoung@users.noreply.github.com> Date: Thu, 10 Sep 2026 09:16:59 +0200 Subject: [PATCH 25/26] lab: make cargo-dist probe assert Package.exists directly --- ...-dist-npm-windows-extraction-candidate.yml | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/.github/workflows/cargo-dist-npm-windows-extraction-candidate.yml b/.github/workflows/cargo-dist-npm-windows-extraction-candidate.yml index a1bfd18..3056956 100644 --- a/.github/workflows/cargo-dist-npm-windows-extraction-candidate.yml +++ b/.github/workflows/cargo-dist-npm-windows-extraction-candidate.yml @@ -73,7 +73,9 @@ jobs: { fixture: 'fixture.exe' }, ); await pkg.install(true); - console.log(JSON.stringify({ install_resolved: true, exists_after: pkg.exists() })); + const existsAfter = pkg.exists(); + console.log(JSON.stringify({ install_resolved: true, exists_after: existsAfter })); + if (!existsAfter) process.exit(91); } main().catch((err) => { @@ -94,24 +96,21 @@ jobs: Remove-Item upstream/node_modules/.bin_real -Recurse -Force -ErrorAction SilentlyContinue node lab/package-install-probe.js upstream/baseline-binary-install.js http://127.0.0.1:8123/valid.zip valid.zip *> baseline.log $baselineExit = $LASTEXITCODE - $baselineExists = Test-Path upstream/node_modules/.bin_real/fixture.exe git -C upstream apply ../lab/candidates/cargo-dist-npm-windows-extraction/candidate.patch - Remove-Item upstream/node_modules/.bin_real -Recurse -Force -ErrorAction SilentlyContinue + Remove-Item upstream/cargo-dist/templates/installer/npm/node_modules/.bin_real -Recurse -Force -ErrorAction SilentlyContinue node lab/package-install-probe.js upstream/cargo-dist/templates/installer/npm/binary-install.js http://127.0.0.1:8123/valid.zip valid.zip *> candidate-valid.log $candidateExit = $LASTEXITCODE - $candidateExists = Test-Path upstream/node_modules/.bin_real/fixture.exe if ($candidateExit -ne 0) { Get-Content candidate-valid.log; throw "candidate failed under Restricted policy: exit $candidateExit" } - if (-not $candidateExists) { Get-Content candidate-valid.log; throw 'candidate reported success but did not extract fixture' } - if ($baselineExit -eq 0 -and $baselineExists) { Get-Content baseline.log; throw 'baseline unexpectedly installed successfully under Restricted policy' } + if ($baselineExit -eq 0) { Get-Content baseline.log; throw 'baseline unexpectedly installed successfully under Restricted policy' } @{ upstream_commit = 'c65a1a932e2661e05d6640716850d36b0f47efd7' baseline_exit = $baselineExit - baseline_extracted = $baselineExists + baseline_silent_success_detected = ($baselineExit -eq 91) candidate_exit = $candidateExit - candidate_extracted = $candidateExists + candidate_install_verified_by_package_exists = $true simulated_policy = 'CurrentUser Restricted' } | ConvertTo-Json | Set-Content -Encoding utf8 restricted-policy-evidence.json } @@ -126,10 +125,10 @@ jobs: $server = Start-Process python -ArgumentList '-m','http.server','8124','--bind','127.0.0.1','--directory','lab/fixture' -PassThru -WindowStyle Hidden Start-Sleep -Seconds 2 try { - Remove-Item upstream/node_modules/.bin_real -Recurse -Force -ErrorAction SilentlyContinue + Remove-Item upstream/cargo-dist/templates/installer/npm/node_modules/.bin_real -Recurse -Force -ErrorAction SilentlyContinue node lab/package-install-probe.js upstream/cargo-dist/templates/installer/npm/binary-install.js http://127.0.0.1:8124/invalid.zip invalid.zip *> corrupt-zip.log $exit = $LASTEXITCODE - if ($exit -eq 0) { Get-Content corrupt-zip.log; throw 'candidate masked corrupt ZIP extraction failure' } + if ($exit -eq 0 -or $exit -eq 91) { Get-Content corrupt-zip.log; throw "candidate did not propagate corrupt ZIP as extraction failure: exit $exit" } @{ corrupt_zip_exit = $exit failure_propagated = $true From 513180509b05cac956080c45e6cbfe68488d3e77 Mon Sep 17 00:00:00 2001 From: "Mark E. DeYoung" <1854350+mark-e-deyoung@users.noreply.github.com> Date: Thu, 10 Sep 2026 09:19:11 +0200 Subject: [PATCH 26/26] lab: normalize expected corrupt-zip probe exit after assertion --- .../workflows/cargo-dist-npm-windows-extraction-candidate.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/cargo-dist-npm-windows-extraction-candidate.yml b/.github/workflows/cargo-dist-npm-windows-extraction-candidate.yml index 3056956..73b273f 100644 --- a/.github/workflows/cargo-dist-npm-windows-extraction-candidate.yml +++ b/.github/workflows/cargo-dist-npm-windows-extraction-candidate.yml @@ -137,6 +137,9 @@ jobs: finally { Stop-Process -Id $server.Id -Force -ErrorAction SilentlyContinue } + # The non-zero Node status above is the expected oracle. After recording it, + # do not let that expected child status become the workflow step status. + $global:LASTEXITCODE = 0 - name: Retain public candidate evidence if: always()