{"name":"s3-matrix-check","lang":"python3","entry":"spec-s3-matrix.py","script":"#!/usr/bin/env python3\n\"\"\"\nspec-s3-matrix.py — S3 (conformance vectors + 3-engine results matrix) verifier.\n\nMetaTask v1.3 pilot spec script (protocol draft §4.3 CI-style verifier contract).\n\nArtifact contract (acceptance-sheet.md §S3): the submission attachment is a\nmetafile pointing at a .tar.gz with members (top level or one shared top dir):\n legacy-vectors.json byte copy of the announced v1.2.1 conformance set\n (canonJ sha256 must equal LEGACY_CANONICAL_SHA256)\n competitive-vectors.json the NEW competitive-mode vectors (>= 8), same\n set-file shape {protocolVersion, vectors: [...]}\n runner executable: `runner --engine \n --vectors ` prints `CANONICAL_SHA256 `\n matrix.md `| vector | python | go | ts |` table covering\n every vector id, plus one digest line per engine:\n `engine canonical sha256: <64hex>`\n engines/python.bundle, engines/go.bundle, engines/ts.bundle\n the three S2 winning git bundles\n\nEnvironment (§4.3, non-git node): METATASK_ARTIFACT_URI / METATASK_NODE /\nMETATASK_TASKID (+ METATASK_DOWNLOAD_BASE for metafile resolution).\n\nToolchain: python3 always. node+npx / go are used when present to reproduce the\nts / go matrix columns from scratch; an absent optional toolchain is recorded\nas SKIP evidence (the hard cross-engine invariant — digest equality — is\nchecked regardless). A reproduction mismatch is a fail, never a skip.\n\nExit codes: 0 pass · 1 fail · 2 invalid. Check counter asserted against\nEXPECTED_CHECKS on the pass path (enumeration closure self-check).\n\"\"\"\nimport hashlib\nimport io\nimport json\nimport os\nimport re\nimport subprocess\nimport sys\nimport tarfile\nimport zipfile\n\nEXPECTED_CHECKS = 11\nLEGACY_CANONICAL_SHA256 = \"106aa1f3bee8ebd48339ceb65f97a12e54247e1e831296a394a974c9cb22f2c4\"\nLEGACY_VECTOR_COUNT = 16\nMIN_COMPETITIVE_VECTORS = 8\n\nREQUIRED_MEMBERS = [\"legacy-vectors.json\", \"competitive-vectors.json\", \"runner\", \"matrix.md\"]\nENGINE_BUNDLES = [\"engines/python.bundle\", \"engines/go.bundle\", \"engines/ts.bundle\"]\nDIGEST_LINE_RE = re.compile(r\"^engine\\s+(python|go|ts)\\s+canonical sha256:\\s*([0-9a-f]{64})\\s*$\", re.IGNORECASE)\n\nchecks_done = 0\n\n\ndef note(line):\n print(line)\n\n\ndef emit(verdict, detail):\n print(json.dumps({\"verdict\": verdict, \"detail\": detail, \"checks\": checks_done}, ensure_ascii=False))\n\n\ndef invalid(detail):\n emit(\"invalid\", detail)\n sys.exit(2)\n\n\ndef fail(detail):\n emit(\"fail\", detail)\n print(\"FAIL: %s\" % detail, file=sys.stderr)\n sys.exit(1)\n\n\ndef check(name, ok, detail=\"\"):\n global checks_done\n checks_done += 1\n note(\"[check %d] %s: %s%s\" % (checks_done, name, \"ok\" if ok else \"FAIL\", (\" — \" + detail) if detail else \"\"))\n if not ok:\n fail(\"%s%s\" % (name, (\" — \" + detail) if detail else \"\"))\n\n\ndef gate(name, ok, detail=\"\"):\n global checks_done\n checks_done += 1\n note(\"[check %d] %s: %s%s\" % (checks_done, name, \"ok\" if ok else \"INVALID\", (\" — \" + detail) if detail else \"\"))\n if not ok:\n invalid(\"%s%s\" % (name, (\" — \" + detail) if detail else \"\"))\n\n\ndef resolve_artifact(uri):\n if uri.startswith(\"file://\"):\n return uri[len(\"file://\"):]\n if os.path.isfile(uri):\n return uri\n if uri.startswith(\"metafile://\") or uri.startswith(\"pin://\"):\n base = os.environ.get(\"METATASK_DOWNLOAD_BASE\", \"\").strip()\n if not base:\n note(\"[fetch] %s requires METATASK_DOWNLOAD_BASE (not set)\" % uri)\n return None\n from urllib.parse import quote\n from urllib.request import urlopen\n\n url = \"%s/file/%s\" % (base.rstrip(\"/\"), quote(uri, safe=\"\"))\n target = os.path.join(os.environ.get(\"TMPDIR\", \"/tmp\"), \"metatask-fetch-%s\" % hashlib.sha256(uri.encode()).hexdigest()[:16])\n try:\n with urlopen(url, timeout=120) as response:\n data = response.read()\n with open(target, \"wb\") as handle:\n handle.write(data)\n note(\"[fetch] %s → %s (%d bytes)\" % (uri, url, len(data)))\n return target\n except Exception as err:\n note(\"[fetch] %s via %s failed: %s\" % (uri, url, err))\n return None\n note(\"[fetch] unrecognized artifact URI scheme: %s\" % uri)\n return None\n\n\ndef open_archive(path):\n raw_members = {}\n with open(path, \"rb\") as handle:\n blob = handle.read()\n if tarfile.is_tarfile(path):\n with tarfile.open(fileobj=io.BytesIO(blob)) as tar:\n for member in tar.getmembers():\n if member.isfile():\n raw_members[member.name] = tar.extractfile(member).read()\n elif zipfile.is_zipfile(io.BytesIO(blob)):\n with zipfile.ZipFile(io.BytesIO(blob)) as zf:\n for name in zf.namelist():\n if not name.endswith(\"/\"):\n raw_members[name] = zf.read(name)\n else:\n fail(\"artifact is neither a tar archive nor a zip file\")\n prefixes = set(name.split(\"/\")[0] for name in raw_members if \"/\" in name)\n bare = [name for name in raw_members if \"/\" not in name]\n if not bare and len(prefixes) == 1:\n prefix = list(prefixes)[0] + \"/\"\n raw_members = {name[len(prefix):]: data for name, data in raw_members.items()}\n return raw_members\n\n\ndef canonJ(obj):\n return json.dumps(obj, ensure_ascii=False, sort_keys=True, separators=(\",\", \":\")).encode(\"utf-8\")\n\n\ndef main():\n artifact = os.environ.get(\"METATASK_ARTIFACT_URI\", \"\").strip()\n node = os.environ.get(\"METATASK_NODE\", \"\").strip()\n taskid = os.environ.get(\"METATASK_TASKID\", \"\").strip()\n note(\"[env] node=%s taskid=%s\" % (node or \"\", taskid or \"\"))\n note(\"[env] artifact=%s\" % (artifact or \"\"))\n note(\"[tool] python3: %s\" % sys.version.split()[0])\n\n gate(\"env contract present\", bool(artifact and node and taskid),\n \"METATASK_ARTIFACT_URI / METATASK_NODE / METATASK_TASKID must all be non-empty\")\n\n local = resolve_artifact(artifact)\n gate(\"artifact resolves\", local is not None, artifact)\n if os.path.getsize(local) == 0:\n invalid(\"artifact is an empty file (0 bytes): %s\" % artifact)\n\n members = open_archive(local)\n note(\"[archive] members: %s\" % \", \".join(sorted(members)))\n missing = [name for name in REQUIRED_MEMBERS + ENGINE_BUNDLES if name not in members]\n check(\"required members present (vectors×2, runner, matrix.md, engines/*.bundle)\",\n not missing, \"missing: %s\" % \", \".join(missing) if missing else \"all present\")\n if any(name in members and len(members[name].strip()) == 0 for name in REQUIRED_MEMBERS):\n invalid(\"a required member is empty\")\n\n # Legacy set identity: canonJ sha256 of the parsed member == the announced constant.\n legacy_ok = False\n legacy_detail = \"\"\n try:\n legacy = json.loads(members[\"legacy-vectors.json\"].decode(\"utf-8\"))\n digest = hashlib.sha256(canonJ(legacy)).hexdigest()\n count = len(legacy.get(\"vectors\", []))\n legacy_ok = digest == LEGACY_CANONICAL_SHA256 and count == LEGACY_VECTOR_COUNT\n legacy_detail = \"canonical sha256 %s, %d vectors\" % (digest, count)\n except Exception as err:\n legacy_detail = \"legacy-vectors.json does not parse: %s\" % err\n check(\"legacy v1.2.1 set included byte-identical (canonical equality)\", legacy_ok, legacy_detail)\n\n # New competitive vectors: >= 8, unique ids, each with events + expect.\n comp_detail = \"\"\n comp_ok = False\n comp_ids = []\n try:\n comp = json.loads(members[\"competitive-vectors.json\"].decode(\"utf-8\"))\n vectors = comp.get(\"vectors\", [])\n comp_ids = [v.get(\"id\") for v in vectors if isinstance(v, dict)]\n malformed = [i for i, v in enumerate(vectors)\n if not isinstance(v, dict) or not v.get(\"id\")\n or not isinstance(v.get(\"events\"), list) or not isinstance(v.get(\"expect\"), dict)]\n comp_ok = len(vectors) >= MIN_COMPETITIVE_VECTORS and len(set(comp_ids)) == len(comp_ids) and not malformed\n comp_detail = \"%d competitive vectors, ids unique: %s, malformed: %s\" % (\n len(vectors), len(set(comp_ids)) == len(comp_ids), malformed[:3] or \"none\")\n except Exception as err:\n comp_detail = \"competitive-vectors.json does not parse: %s\" % err\n check(\"competitive vector set: >= %d well-formed new vectors\" % MIN_COMPETITIVE_VECTORS, comp_ok, comp_detail)\n\n # Matrix digest lines.\n matrix_text = members[\"matrix.md\"].decode(\"utf-8\")\n digests = {}\n for line in matrix_text.splitlines():\n match = DIGEST_LINE_RE.match(line.strip())\n if match:\n digests[match.group(1).lower()] = match.group(2).lower()\n check(\"matrix digest lines parse (python/go/ts, 64-hex)\",\n sorted(digests) == [\"go\", \"python\", \"ts\"],\n \"found engines: %s\" % (\", \".join(sorted(digests)) or \"none\"))\n\n check(\"canonical sha256 equal across the three engines\",\n len(set(digests.values())) == 1 and len(digests) == 3,\n \"python=%s go=%s ts=%s\" % (digests.get(\"python\", \"?\")[:12], digests.get(\"go\", \"?\")[:12], digests.get(\"ts\", \"?\")[:12]))\n\n # Matrix table covers every vector id.\n legacy_ids = [v.get(\"id\") for v in legacy.get(\"vectors\", [])] if isinstance(legacy, dict) else []\n wanted = set(legacy_ids) | set(comp_ids)\n table_ids = set()\n in_table = False\n for line in matrix_text.splitlines():\n stripped = line.strip()\n if stripped.startswith(\"|\") and stripped.endswith(\"|\"):\n cells = [cell.strip() for cell in stripped.strip(\"|\").split(\"|\")]\n if cells and cells[0].lower() == \"vector\":\n in_table = True\n continue\n if in_table and cells and not set(cells[0]) <= set(\"-: \"):\n table_ids.add(cells[0])\n missing_rows = sorted(wanted - table_ids)\n check(\"matrix table covers every vector id (%d legacy + %d competitive)\" % (len(legacy_ids), len(comp_ids)),\n not missing_rows and len(wanted) > 0,\n \"missing rows: %s\" % \", \".join(missing_rows[:5]) if missing_rows else \"%d rows\" % len(table_ids))\n\n # Engine bundles verify.\n bundle_detail = []\n bundles_ok = True\n tmp = os.path.join(os.environ.get(\"TMPDIR\", \"/tmp\"), \"metatask-s3-%d\" % os.getpid())\n os.makedirs(tmp, exist_ok=True)\n for name in ENGINE_BUNDLES:\n path = os.path.join(tmp, name.replace(\"/\", \"-\"))\n with open(path, \"wb\") as handle:\n handle.write(members[name])\n proc = subprocess.run([\"git\", \"bundle\", \"verify\", path], capture_output=True, text=True)\n bundle_detail.append(\"%s:%s\" % (name.split(\"/\")[-1], \"ok\" if proc.returncode == 0 else \"BAD\"))\n bundles_ok = bundles_ok and proc.returncode == 0\n check(\"engines/*.bundle all pass git bundle verify\", bundles_ok, \", \".join(bundle_detail))\n\n # Matrix reproduction, toolchain-gated.\n vectors_dir = os.path.join(tmp, \"vectors\")\n os.makedirs(vectors_dir, exist_ok=True)\n for name in (\"legacy-vectors.json\", \"competitive-vectors.json\"):\n with open(os.path.join(vectors_dir, name), \"wb\") as handle:\n handle.write(members[name])\n runner_path = os.path.join(tmp, \"runner\")\n with open(runner_path, \"wb\") as handle:\n handle.write(members[\"runner\"])\n os.chmod(runner_path, 0o755)\n\n def reproduce(engine):\n proc = subprocess.run([runner_path, \"--engine\", engine, \"--vectors\", vectors_dir],\n capture_output=True, text=True, timeout=600)\n digest_line = [line for line in proc.stdout.splitlines() if line.startswith(\"CANONICAL_SHA256 \")]\n digest = digest_line[-1].split()[1] if digest_line else None\n return proc.returncode, digest\n\n rc, py_digest = reproduce(\"python\")\n check(\"runner reproduces the python column from scratch\",\n rc == 0 and py_digest == digests.get(\"python\"),\n \"runner exit=%d digest=%s matrix=%s\" % (rc, (py_digest or \"\")[:12], digests.get(\"python\", \"?\")[:12]))\n\n have_node = subprocess.run([\"bash\", \"-c\", \"command -v node\"], capture_output=True).returncode == 0\n have_go = subprocess.run([\"bash\", \"-c\", \"command -v go\"], capture_output=True).returncode == 0\n skip_note = []\n repro_ok = True\n for engine, present in ((\"ts\", have_node), (\"go\", have_go)):\n if not present:\n skip_note.append(\"%s toolchain absent\" % (\"node\" if engine == \"ts\" else \"go\"))\n note(\"[check 11] SKIP %s reproduction: %s toolchain absent (digest-equality check 7 still applies)\" % (engine, \"node\" if engine == \"ts\" else \"go\"))\n continue\n rc, digest = reproduce(engine)\n if not (rc == 0 and digest == digests.get(engine)):\n repro_ok = False\n note(\"[check 11] %s reproduction MISMATCH: exit=%d digest=%s matrix=%s\" % (engine, rc, (digest or \"\")[:12], digests.get(engine, \"?\")[:12]))\n check(\"runner reproduces ts/go columns under available toolchains\",\n repro_ok, \"skipped: %s\" % \", \".join(skip_note) if skip_note else \"ts + go reproduced\")\n\n if checks_done != EXPECTED_CHECKS:\n invalid(\"check-counter mismatch: ran %d, spec declares %d (script bug)\" % (checks_done, EXPECTED_CHECKS))\n emit(\"pass\", \"S3 green: legacy set byte-identical, %d competitive vectors, matrix digest %s across python/go/ts\" % (\n len(comp_ids), digests.get(\"python\", \"?\")[:16]))\n\n\nif __name__ == \"__main__\":\n main()\n","input":{"env":{"METATASK_ARTIFACT_URI":"metafile://","METATASK_NODE":"S3","METATASK_TASKID":"","METATASK_DOWNLOAD_BASE":"optional; metafile resolution base"},"toolchain":"python3 always; node (ts column) and go (go column) used for matrix reproduction when present, else SKIP evidence"},"output":{"stdout":"evidence log + final JSON line {\"verdict\",\"detail\",\"checks\"}","exitCode":"0 pass | 1 fail | 2 invalid"},"workspace":{"type":"metafile","notes":"S3 delivers one metafile bundle: legacy-vectors.json + competitive-vectors.json + runner + matrix.md + engines/*.bundle"},"validation":{"null_tolerance":true,"enumeration_closure":{"closure":"the vector enumeration closes over the registered legacy set (16 vectors, canonical sha256 pinned below) plus >= 8 new competitive vectors; the pass path asserts the check counter equals the declared expected_count","selfcheck":{"expected_count":11,"legacy_vector_count":16,"legacy_canonical_sha256":"106aa1f3bee8ebd48339ceb65f97a12e54247e1e831296a394a974c9cb22f2c4","min_competitive_vectors":8}},"proposition_fidelity":{"correspondence":"metafile://d7f836544764bf4ca68bd7903b7a6c7a978ff16e34a828e9b31c928cbec05ccci0.md","artifactPin":"metafile://d7f836544764bf4ca68bd7903b7a6c7a978ff16e34a828e9b31c928cbec05ccci0.md","artifactKey":"acceptance-sheet","coverage":["statement","definitions","proof-direction"],"note":"statement = S3 deliverable (vectors + runner + 3-engine matrix); definitions = the S3 artifact format (member list, matrix digest lines); proof-direction = the acceptance-sheet S3 table (digest equality is machine-checked; topic coverage is reviewed)."}}}