diff --git a/docs/BUILDING.md b/docs/BUILDING.md index 506df82..ad2f6de 100644 --- a/docs/BUILDING.md +++ b/docs/BUILDING.md @@ -154,6 +154,14 @@ same normalized TAR members, file checksums and executable modes. XZ uses the standard library's preset 6. The extraction smoke accepts either format. A smaller download does not change runtime behavior or ABI requirements. +Always use the current normal runtime directory, not a copied historical +desktop staging cache. A valid old checksum inventory alone does not prove +compatibility with the current foreground query protocol. Before packaging, +exercise actual query-role inference with the selected worker; then run the +full extracted archive smoke. A legacy worker lacking `--role query` can build +document vectors while failing foreground search. The archive smoke retains +captured CLI error output when a command exits unsuccessfully. + The optional Linux missing-loader check requires explicit root execution and util-linux mount namespaces/chroot. It never escalates privileges and verifies a private namespace before creating an isolated filesystem: diff --git a/tools/linux_archive_smoke.py b/tools/linux_archive_smoke.py index afda532..c0b0108 100644 --- a/tools/linux_archive_smoke.py +++ b/tools/linux_archive_smoke.py @@ -90,8 +90,16 @@ def main(): def run(label, arguments, json_output=False): started = time.perf_counter() - output = subprocess.run([str(cli), *map(str, arguments)], cwd=installed, env=environment, - check=True, capture_output=True, text=True, encoding="utf-8", timeout=180) + try: + output = subprocess.run([str(cli), *map(str, arguments)], cwd=installed, env=environment, + check=True, capture_output=True, text=True, encoding="utf-8", timeout=180) + except subprocess.CalledProcessError as error: + args.output.parent.mkdir(parents=True, exist_ok=True) + with args.output.open("x", encoding="utf-8") as evidence: + json.dump({"complete": False, "failed_phase": label, + "exit_code": error.returncode, "stderr": error.stderr[-8192:], + "stdout": error.stdout[-8192:], "command_elapsed_ms": timings}, evidence, indent=2) + raise RuntimeError(f"{label} failed: {error.stderr[-4096:]}") from error timings[label] = (time.perf_counter() - started) * 1000 return json.loads(output.stdout) if json_output else output.stdout @@ -115,7 +123,7 @@ def main(): "--extension", "pdf", "--json", *model_arguments], True) if restricted: raise RuntimeError("Installed semantic search violated extension filter") - evidence = {"platform": "linux-x86_64", "cpu_only": True, + evidence = {"complete": True, "platform": "linux-x86_64", "cpu_only": True, "archive_sha256": sha256(args.archive), "archive_bytes": args.archive.stat().st_size, "verified_files": len(manifest["files"]), "extraction_path_contains_spaces": True, "automatic_media_helper_discovery": "passed", "embedding_report": report,