{"name":"s2c-ts-adapter","lang":"bash","entry":"spec-s2c-ts.sh","script":"#!/usr/bin/env bash\n# spec-s2c-ts.sh — S2c (TS adapter over the vendored reference engine) verifier.\n#\n# MetaTask v1.3 pilot spec script (protocol draft §4.3 CI-style verifier contract).\n# Template family: spec-s2-engine (see spec-s2a-python.sh for the shared\n# skeleton documentation). S2c adds the adapter-discipline gates: the pinned\n# base bundle is cloned and vendor/metatask-engine/ must be BYTE-IDENTICAL\n# (the node is an adapter task; engine edits belong to an S4 report).\n#\n# Environment (§4.3): METATASK_ARTIFACT_URI / METATASK_COMMIT /\n# METATASK_BASE_COMMIT / METATASK_NODE / METATASK_TASKID /\n# METATASK_DOWNLOAD_BASE / METATASK_VECTOR_SET_URI (override).\n#\n# Toolchain: git + node + npx REQUIRED (this node's spec is allowed node+npx\n# per the pilot task sheet); a machine without them reports invalid (exit 2),\n# never fail. pnpm is resolved as: pnpm on PATH → corepack pnpm → npx pnpm\n# (the resolution path is recorded in the evidence log).\n#\n# The embedded VECTOR_SET_URI / BASE_BUNDLE_URI below are PUBLISH-TIME\n# PLACEHOLDERS: unresolved placeholder → invalid (exit 2), never fail.\n#\n# Exit codes: 0 pass · 1 fail · 2 invalid. Check counter asserted against\n# EXPECTED_CHECKS on the pass path (enumeration closure self-check).\nset -uo pipefail\n\nreadonly VECTOR_SET_URI=\"metafile://71f09271a6be857a0f40200498381decab99d9d7f7049f32f69f2f0df11bc58ei0.gz\" # PLACEHOLDER — backfilled with metafile://… at publish\nreadonly BASE_BUNDLE_URI=\"metafile://fc2e9d25dda75d92adedcb73fc83b7f192b8a60e2050593cfd540df5e76aa0a5i0.bundle\" # PLACEHOLDER — backfilled with the s2c-ts-harness-base bundle metafile://… at publish\nEXPECTED_CHECKS=20\n\nCHECKS=0\n\njson_line() { python3 -c 'import json,sys; print(json.dumps({\"verdict\":sys.argv[1],\"detail\":sys.argv[2],\"checks\":int(sys.argv[3])}))' \"$1\" \"$2\" \"$CHECKS\"; }\nnote() { printf '%s\\n' \"$1\"; }\nfail() { json_line fail \"$1\"; printf 'FAIL: %s\\n' \"$1\" >&2; exit 1; }\ninvalid() { json_line invalid \"$1\"; exit 2; }\ncheck() {\n CHECKS=$((CHECKS+1))\n if [ \"$2\" = \"0\" ]; then note \"[check $CHECKS] $1: ok${3:+ — $3}\"; else note \"[check $CHECKS] $1: FAIL${3:+ — $3}\"; fail \"$1${3:+ — $3}\"; fi\n}\ngate() {\n CHECKS=$((CHECKS+1))\n if [ \"$2\" = \"0\" ]; then note \"[check $CHECKS] $1: ok${3:+ — $3}\"; else note \"[check $CHECKS] $1: INVALID${3:+ — $3}\"; invalid \"$1${3:+ — $3}\"; fi\n}\ntool() {\n if command -v \"$1\" >/dev/null 2>&1; then\n note \"[tool] $1: $(\"$@\" 2>&1 | head -1)\"\n return 0\n fi\n note \"[tool] $1: MISSING\"\n return 1\n}\n\nfetch_artifact() {\n local uri=\"$1\" dest=\"$2\"\n case \"$uri\" in\n file://*) cp \"${uri#file://}\" \"$dest\" 2>/dev/null && return 0 ;;\n metafile://*|pin://*)\n local base=\"${METATASK_DOWNLOAD_BASE:-}\"\n if [ -z \"$base\" ]; then note \"[fetch] $uri requires METATASK_DOWNLOAD_BASE (not set)\"; return 1; fi\n if FETCH_URI=\"$uri\" FETCH_BASE=\"$base\" FETCH_DEST=\"$dest\" python3 - <<'PYEOF'\nimport os, sys\nfrom urllib.parse import quote\nfrom urllib.request import urlopen\nuri, base, dest = os.environ[\"FETCH_URI\"], os.environ[\"FETCH_BASE\"].rstrip(\"/\"), os.environ[\"FETCH_DEST\"]\nurl = \"%s/file/%s\" % (base, quote(uri, safe=\"\"))\ntry:\n with urlopen(url, timeout=120) as response:\n data = response.read()\n with open(dest, \"wb\") as handle:\n handle.write(data)\n print(\"[fetch] %s → %s (%d bytes)\" % (uri, url, len(data)))\nexcept Exception as err:\n print(\"[fetch] %s via %s failed: %s\" % (uri, url, err))\n sys.exit(1)\nPYEOF\n then return 0; else return 1; fi ;;\n *) [ -f \"$uri\" ] && cp \"$uri\" \"$dest\" && return 0 ;;\n esac\n return 1\n}\n\nARTIFACT_URI=\"${METATASK_ARTIFACT_URI:-}\"\nCOMMIT=\"${METATASK_COMMIT:-}\"\nBASE_COMMIT=\"${METATASK_BASE_COMMIT:-}\"\nNODE=\"${METATASK_NODE:-}\"\nTASKID=\"${METATASK_TASKID:-}\"\n\nnote \"[env] node=${NODE:-} taskid=${TASKID:-}\"\nnote \"[env] artifact=${ARTIFACT_URI:-} commit=${COMMIT:-} base=${BASE_COMMIT:-}\"\n\nenv_bad=1\n[ -n \"$ARTIFACT_URI\" ] && [ -n \"$COMMIT\" ] && [ -n \"$NODE\" ] && [ -n \"$TASKID\" ] && env_bad=0\ngate \"env contract present (ARTIFACT_URI/COMMIT/NODE/TASKID)\" \"$env_bad\"\ncommit_bad=1\nprintf '%s' \"$COMMIT\" | grep -qE '^[0-9a-fA-F]{40}$' && commit_bad=0\ngate \"commit under test is 40-hex\" \"$commit_bad\" \"$COMMIT\"\n\ntools_bad=1\ntool git git --version && tool node node --version && tool npx npx --version && tool python3 python3 --version && tools_bad=0\ngate \"toolchain: git + node + npx (+ python3 plumbing)\" \"$tools_bad\" \"spec-s2c requires git and node+npx; without them the verdict is invalid, never fail\"\n\nPNPM=\"\"\nif command -v pnpm >/dev/null 2>&1; then PNPM=\"pnpm\"\nelif command -v corepack >/dev/null 2>&1 && corepack pnpm --version >/dev/null 2>&1; then PNPM=\"corepack pnpm\"\nelif command -v npx >/dev/null 2>&1; then PNPM=\"npx --yes pnpm\"\nfi\npnpm_version=\"$($PNPM --version 2>/dev/null || echo unavailable)\"\nnote \"[tool] pnpm resolved as: ${PNPM:-none} ($pnpm_version)\"\n\nVECTOR_URI=\"${METATASK_VECTOR_SET_URI:-$VECTOR_SET_URI}\"\nvec_bad=0\ncase \"$VECTOR_URI\" in *VECTOR_SET_URI*|\"\") vec_bad=1 ;; esac\ngate \"vector set reference resolved\" \"$vec_bad\" \"VECTOR_SET_URI placeholder not backfilled and no METATASK_VECTOR_SET_URI override\"\n\nBASE_URI=\"${METATASK_BASE_BUNDLE_URI:-$BASE_BUNDLE_URI}\"\nbase_bad=0\ncase \"$BASE_URI\" in *BASE_BUNDLE_URI*|\"\") base_bad=1 ;; esac\ngate \"base bundle reference resolved\" \"$base_bad\" \"BASE_BUNDLE_URI placeholder not backfilled and no METATASK_BASE_BUNDLE_URI override\"\n\nWORK=\"$(mktemp -d \"${TMPDIR:-/tmp}/metatask-s2c.XXXXXX\")\"\ntrap 'rm -rf \"$WORK\"' EXIT\n\nfetch_artifact \"$ARTIFACT_URI\" \"$WORK/submission.bundle\"\ngate \"artifact fetched\" \"$([ -s \"$WORK/submission.bundle\" ] && echo 0 || echo 1)\" \"$ARTIFACT_URI\"\n\ngit bundle verify \"$WORK/submission.bundle\" >/dev/null 2>&1 && git clone -q \"$WORK/submission.bundle\" \"$WORK/repo\" 2>/dev/null\ncheck \"git bundle verifies and clones\" \"$([ -d \"$WORK/repo/.git\" ] && echo 0 || echo 1)\" \"$ARTIFACT_URI\"\n\ngit -C \"$WORK/repo\" checkout -q \"$COMMIT\" 2>/dev/null\ncheck \"checkout METATASK_COMMIT\" \"$([ \"$(git -C \"$WORK/repo\" rev-parse HEAD 2>/dev/null)\" = \"$(printf '%s' \"$COMMIT\" | tr 'A-F' 'a-f')\" ] && echo 0 || echo 1)\" \"$COMMIT\"\n\nif [ -z \"$BASE_COMMIT\" ] || [ \"$BASE_COMMIT\" = \"null\" ]; then\n CHECKS=$((CHECKS+1)); note \"[check $CHECKS] base-commit ancestry: ok — greenfield node (baseCommit null), ancestry skipped per draft §4.4\"\nelse\n git -C \"$WORK/repo\" merge-base --is-ancestor \"$BASE_COMMIT\" \"$COMMIT\" 2>/dev/null\n check \"declared baseCommit is an ancestor of commit\" \"$([ $? -eq 0 ] && echo 0 || echo 1)\" \"base=$BASE_COMMIT\"\nfi\n\nfetch_artifact \"$BASE_URI\" \"$WORK/base.bundle\"\ngate \"base bundle fetched\" \"$([ -s \"$WORK/base.bundle\" ] && echo 0 || echo 1)\" \"$BASE_URI\"\n\ngit bundle verify \"$WORK/base.bundle\" >/dev/null 2>&1 && git clone -q \"$WORK/base.bundle\" \"$WORK/base\" 2>/dev/null\ncheck \"base bundle verifies and clones\" \"$([ -d \"$WORK/base/.git\" ] && echo 0 || echo 1)\" \"$BASE_URI\"\n\nvendor_diff=\"$(diff -r \"$WORK/base/vendor/metatask-engine\" \"$WORK/repo/vendor/metatask-engine\" 2>&1)\"\ncheck \"vendored engine byte-untouched (vendor/metatask-engine diff vs pinned base)\" \"$([ -z \"$vendor_diff\" ] && [ -d \"$WORK/repo/vendor/metatask-engine\" ] && echo 0 || echo 1)\" \"$(printf '%s' \"$vendor_diff\" | head -2)\"\n\ninstall_rc=0\nif [ -f \"$WORK/repo/pnpm-lock.yaml\" ]; then\n ( cd \"$WORK/repo\" && $PNPM install --frozen-lockfile ) > \"$WORK/install.log\" 2>&1\n install_rc=$?\nelif [ -f \"$WORK/repo/package-lock.json\" ]; then\n ( cd \"$WORK/repo\" && npm ci ) > \"$WORK/install.log\" 2>&1\n install_rc=$?\nelif [ -f \"$WORK/repo/package.json\" ]; then\n ( cd \"$WORK/repo\" && $PNPM install ) > \"$WORK/install.log\" 2>&1\n install_rc=$?\nelse\n echo \"no package.json\" > \"$WORK/install.log\"\n install_rc=3\nfi\ncheck \"dependency install (frozen when a lockfile ships)\" \"$install_rc\" \"$(tail -1 \"$WORK/install.log\")\"\n\nbuild_rc=0\nif [ -f \"$WORK/repo/package.json\" ] && python3 -c 'import json,sys; sys.exit(0 if \"build\" in (json.load(open(sys.argv[1])).get(\"scripts\") or {}) else 1)' \"$WORK/repo/package.json\"; then\n ( cd \"$WORK/repo\" && $PNPM run build ) > \"$WORK/build.log\" 2>&1\n build_rc=$?\n note \"[build] npm run build exit=$build_rc\"\nelif ( cd \"$WORK/repo\" && $PNPM run typecheck ) > \"$WORK/build.log\" 2>&1; then\n note \"[build] pnpm run typecheck clean\"\nelse\n build_rc=$?\n note \"[build] typecheck exit=$build_rc: $(tail -1 \"$WORK/build.log\")\"\nfi\ncheck \"build/typecheck clean\" \"$build_rc\" \"$(tail -1 \"$WORK/build.log\" 2>/dev/null)\"\n\n[ -f \"$WORK/repo/run-vectors.sh\" ]\ncheck \"run-vectors.sh present\" \"$?\" \"contract: README.md vector runner\"\n\nfetch_artifact \"$VECTOR_URI\" \"$WORK/vector-set.tar.gz\"\n[ -s \"$WORK/vector-set.tar.gz\" ] && mkdir -p \"$WORK/vectors\" && tar -xzf \"$WORK/vector-set.tar.gz\" -C \"$WORK/vectors\" 2>/dev/null\ngate \"vector set fetched and extracted\" \"$([ -d \"$WORK/vectors\" ] && ls \"$WORK/vectors\"/*.json >/dev/null 2>&1 && echo 0 || echo 1)\" \"$VECTOR_URI\"\n\n( cd \"$WORK/repo\" && bash ./run-vectors.sh \"$WORK/vectors\" ) > \"$WORK/run1.log\" 2>&1\nrun1_rc=$?\nnote \"[run 1] exit=$run1_rc\"; sed 's/^/[run 1] /' \"$WORK/run1.log\" | tail -6\ncheck \"vector run 1 green\" \"$run1_rc\" \"run-vectors.sh exit code\"\n\n( cd \"$WORK/repo\" && bash ./run-vectors.sh \"$WORK/vectors\" ) > \"$WORK/run2.log\" 2>&1\nrun2_rc=$?\nnote \"[run 2] exit=$run2_rc\"; sed 's/^/[run 2] /' \"$WORK/run2.log\" | tail -3\ncheck \"vector run 2 green\" \"$run2_rc\" \"run-vectors.sh exit code\"\n\nd1=\"$(grep -E '^CANONICAL_SHA256 [0-9a-f]{64}$' \"$WORK/run1.log\" | tail -1 | awk '{print $2}')\"\nd2=\"$(grep -E '^CANONICAL_SHA256 [0-9a-f]{64}$' \"$WORK/run2.log\" | tail -1 | awk '{print $2}')\"\ncheck \"determinism: two runs byte-identical canonical digest\" \"$([ -n \"$d1\" ] && [ \"$d1\" = \"$d2\" ] && echo 0 || echo 1)\" \"run1=${d1:-} run2=${d2:-}\"\n\nengine_line=\"$(grep -E '^ENGINE \\S+ idbots-metatask-engine/' \"$WORK/run1.log\" | tail -1)\"\nengine_bad=1\nprintf '%s' \"$engine_line\" | grep -qE ' idbots-metatask-engine/1\\.3\\.0$' && engine_bad=0\ncheck \"engineAlgoVersion reported correctly (idbots-metatask-engine/1.3.0 on the competitive set)\" \"$engine_bad\" \"${engine_line:-}\"\n\nif [ \"$CHECKS\" != \"$EXPECTED_CHECKS\" ]; then\n invalid \"check-counter mismatch: ran $CHECKS, spec declares $EXPECTED_CHECKS (script bug)\"\nfi\njson_line pass \"S2c green: vendor engine byte-untouched, install+build ok; vector set $VECTOR_URI passed twice; canonical digest ${d1}\"\nexit 0\n","input":{"env":{"METATASK_ARTIFACT_URI":"metafile://","METATASK_COMMIT":"submission tip commit (40-hex)","METATASK_BASE_COMMIT":"3bad3f45186a645be68f90f10f09850feda3449b (pinned base tip)","METATASK_NODE":"S2c","METATASK_TASKID":"","METATASK_VECTOR_SET_URI":"offline override","METATASK_BASE_BUNDLE_URI":"offline override for the vendor byte-diff base bundle","METATASK_DOWNLOAD_BASE":"optional; metafile resolution base"},"toolchain":"git + node + npx required (pilot task sheet allows node+npx for this node)"},"output":{"stdout":"evidence log + final JSON line {\"verdict\",\"detail\",\"checks\"}","exitCode":"0 pass | 1 fail | 2 invalid"},"workspace":{"type":"git","baseRef":"metafile://fc2e9d25dda75d92adedcb73fc83b7f192b8a60e2050593cfd540df5e76aa0a5i0.bundle","baseCommit":"3bad3f45186a645be68f90f10f09850feda3449b","notes":"harness skeleton with the vendored engine snapshot (vendor/metatask-engine, read-only; snapshot source commit 7f1e3336cc01a737e6d20f23f195d99eb593edc5)"},"validation":{"null_tolerance":true,"enumeration_closure":{"closure":"every machine-checkable S2c acceptance item is one numbered check; the pass path asserts the check counter equals the declared expected_count","selfcheck":{"expected_count":20}},"proposition_fidelity":{"correspondence":"metafile://d7f836544764bf4ca68bd7903b7a6c7a978ff16e34a828e9b31c928cbec05ccci0.md","artifactPin":"metafile://d7f836544764bf4ca68bd7903b7a6c7a978ff16e34a828e9b31c928cbec05ccci0.md","artifactKey":"acceptance-sheet","coverage":["statement","definitions","proof-direction"],"note":"statement = S2c deliverable (thin adapter over the vendored reference engine); definitions = the vendor snapshot read-only discipline + byte-diff gate; proof-direction = the acceptance-sheet S2c table."}}}