|
| 1 | +#!/usr/bin/env bash |
| 2 | +# Ecosystem verification for the design record |
| 3 | +# 2026-09-14-636-build-database-and-the-latest-xlings.md, against a PUBLISHED |
| 4 | +# mcpp and the xlings it pins. |
| 5 | +# |
| 6 | +# B64=$(base64 -w0 .agents/docs/2026-09-14-636-verify.sh) |
| 7 | +# xlings subos new verify-636 # once |
| 8 | +# xlings subos use verify-636 --sandbox --cmd \ |
| 9 | +# "echo $B64 | base64 -d > /tmp/v.sh && MCPP_VERIFY_VERSION=<mcpp> XLINGS_VERIFY_VERSION=<xlings> bash /tmp/v.sh" |
| 10 | +# |
| 11 | +# The sandbox starts from an empty $HOME and a fresh /tmp and shares the xlings |
| 12 | +# data directory, so the published mcpp is addressed by its store path. The mcpp |
| 13 | +# home is removed first: every section reads this release, not a previous run. |
| 14 | +# A section that cannot run says so and is listed again in the summary. |
| 15 | +set -u |
| 16 | + |
| 17 | +VER="${MCPP_VERIFY_VERSION:?set MCPP_VERIFY_VERSION}" |
| 18 | +XVER="${XLINGS_VERIFY_VERSION:?set XLINGS_VERIFY_VERSION}" |
| 19 | +STORE="${MCPP_VERIFY_BIN:-$HOME/.xlings/data/xpkgs/xim-x-mcpp/$VER/bin/mcpp}" |
| 20 | + |
| 21 | +fails=0 |
| 22 | +skipped="" |
| 23 | +fail() { printf 'ASSERT-FAIL: %s\n' "$1"; fails=$((fails + 1)); } |
| 24 | +ok() { printf 'ok: %s\n' "$1"; } |
| 25 | +section() { printf '\n== %s ==\n' "$1"; } |
| 26 | +skip() { printf 'NOT RUN: %s\n' "$1"; skipped="$skipped |
| 27 | + - $1"; } |
| 28 | + |
| 29 | +root=/tmp/verify-636 |
| 30 | +rm -rf "$root" "$HOME/.mcpp"; mkdir -p "$root" |
| 31 | +REG="$HOME/.mcpp/registry" |
| 32 | + |
| 33 | +section "A. the published mcpp, the CN mirror, and the xlings it vendors" |
| 34 | +if [ ! -x "$STORE" ]; then |
| 35 | + fail "no mcpp at $STORE"; printf '\nfails=%d (nothing else can run)\n' "$fails"; exit 1 |
| 36 | +fi |
| 37 | +got=$("$STORE" --version 2>&1 | head -1) |
| 38 | +case "$got" in *"$VER"*) ok "mcpp --version says $got" ;; *) fail "mcpp --version says '$got', expected $VER" ;; esac |
| 39 | +"$STORE" self config --mirror CN > "$root/mirror.log" 2>&1 && ok "mcpp self config --mirror CN" || { fail "mcpp self config --mirror CN"; tail -3 "$root/mirror.log"; } |
| 40 | +grep -q '"mirror": "CN"' "$REG/.xlings.json" 2>/dev/null && ok "the registry's xlings configuration reads mirror CN" || fail "the registry's .xlings.json does not read mirror CN" |
| 41 | +"$STORE" self env > "$root/env.log" 2>&1 |
| 42 | +grep -q "xlings pinned = $XVER" "$root/env.log" && ok "the pin is xlings $XVER" || { fail "mcpp self env does not pin xlings $XVER"; grep 'xlings pinned' "$root/env.log"; } |
| 43 | +vend=$("$REG/bin/xlings" --version 2>/dev/null | head -1) |
| 44 | +case "$vend" in *"$XVER"*) ok "the vendored xlings is $vend" ;; *) fail "the vendored xlings is '$vend', expected $XVER" ;; esac |
| 45 | + |
| 46 | +section "B. a hookless package after a hook package: its own archive, nothing else" |
| 47 | +d=$root/b; rm -rf "$d"; mkdir -p "$d/src" |
| 48 | +cat > "$d/mcpp.toml" <<'EOF' |
| 49 | +[package] |
| 50 | +name = "eco636" |
| 51 | +version = "0.1.0" |
| 52 | +standard = "c++23" |
| 53 | +
|
| 54 | +[dependencies] |
| 55 | +mcpplibs.cmdline = "0.0.1" |
| 56 | +EOF |
| 57 | +printf 'import std;\nimport mcpplibs.cmdline;\nint main() { std::println("1-2-3"); }\n' > "$d/src/main.cpp" |
| 58 | +if (cd "$d" && "$STORE" build > build.log 2>&1); then |
| 59 | + ok "a project with a toolchain install and a hookless mcpp-index dependency builds" |
| 60 | + swept=$(find "$REG/data/xpkgs" -mindepth 3 -maxdepth 3 -name '*.lock' 2>/dev/null) |
| 61 | + [ -z "$swept" ] && ok "no store payload carries a download sidecar" || { fail "store payloads carry download sidecars"; printf '%s\n' "$swept" | head -5; } |
| 62 | + cmd=$(find "$REG/data/xpkgs" -maxdepth 2 -path '*cmdline*' -type d | tail -1) |
| 63 | + if [ -n "$cmd" ]; then |
| 64 | + top=$(ls -A "$cmd" | grep -v '^\.' | tr '\n' ' ') |
| 65 | + case "$top" in |
| 66 | + *.tar.gz*|*.zip*) fail "the cmdline payload holds an archive: $top" ;; |
| 67 | + *) ok "the cmdline payload's top level is its own archive's entries: $top" ;; |
| 68 | + esac |
| 69 | + [ -n "$(find "$cmd" -mindepth 2 -maxdepth 2 -name mcpp.toml)" ] && ok "the top-level directory is kept, so */mcpp.toml matches" || fail "no */mcpp.toml under $cmd" |
| 70 | + else |
| 71 | + skip "B: the cmdline payload directory was not found" |
| 72 | + fi |
| 73 | + (cd "$d" && "$STORE" run > run.log 2>&1); grep -qx '1-2-3' "$d/run.log" && ok "mcpp run prints 1-2-3" || { fail "mcpp run"; tail -3 "$d/run.log"; } |
| 74 | +else |
| 75 | + fail "the dependency build failed"; tail -8 "$d/build.log" |
| 76 | +fi |
| 77 | + |
| 78 | +section "C. mcpp emit build-database writes nothing and describes the plan" |
| 79 | +d=$root/c; rm -rf "$d"; mkdir -p "$d/src" "$d/tests" |
| 80 | +cat > "$d/mcpp.toml" <<'EOF' |
| 81 | +[package] |
| 82 | +name = "hello" |
| 83 | +version = "0.1.0" |
| 84 | +standard = "c++23" |
| 85 | +EOF |
| 86 | +printf 'export module hello.greet;\nexport import :detail;\nimport std;\nexport std::string greet();\n' > "$d/src/greet.cppm" |
| 87 | +printf 'export module hello.greet:detail;\nexport int answer() { return 42; }\n' > "$d/src/detail.cppm" |
| 88 | +printf 'module hello.greet;\nstd::string greet() { return "hi"; }\n' > "$d/src/greet_impl.cpp" |
| 89 | +printf 'import hello.greet;\nimport std;\nint main() { std::println("{}", greet()); }\n' > "$d/src/main.cpp" |
| 90 | +printf 'int main() { return 0; }\n' > "$d/tests/test_smoke.cpp" |
| 91 | +before=$(cd "$d" && find . -type f | sort | xargs sha256sum | sha256sum) |
| 92 | +if (cd "$d" && "$STORE" emit build-database --format json > "$root/c.json" 2> "$root/c.err"); then |
| 93 | + after=$(cd "$d" && find . -type f | sort | xargs sha256sum | sha256sum) |
| 94 | + [ "$before" = "$after" ] && ok "the project tree is unchanged" || fail "the project tree changed" |
| 95 | + if python3 - "$root/c.json" <<'EOF' |
| 96 | +import json, os, sys |
| 97 | +e = json.load(open(sys.argv[1])) |
| 98 | +assert e["kind"] == "mcpp.build-database" and "write-project" not in e["effects"] |
| 99 | +db = e["data"]["database"] |
| 100 | +sets = {s["name"]: s for s in db["sets"]} |
| 101 | +assert set(sets) == {"hello", "hello:test", "mcpp:std"}, sorted(sets) |
| 102 | +roles = {os.path.basename(u["source"]): u["ide"]["role"] for u in sets["hello"]["translation-units"]} |
| 103 | +assert roles == {"greet.cppm": "module-interface", "detail.cppm": "module-partition-interface", |
| 104 | + "greet_impl.cpp": "module-implementation", "main.cpp": "non-module"}, roles |
| 105 | +std = sets["mcpp:std"]["translation-units"][0] |
| 106 | +assert std["provides"] == {"std": ""} and os.path.isfile(std["source"]), std |
| 107 | +print("sets", sorted(sets), "units", sum(len(s["translation-units"]) for s in db["sets"])) |
| 108 | +EOF |
| 109 | + then ok "the S1 document has the sets, roles and std unit SPEC-005 states" |
| 110 | + else fail "the S1 document"; fi |
| 111 | +else |
| 112 | + fail "emit build-database failed"; tail -5 "$root/c.err" |
| 113 | +fi |
| 114 | + |
| 115 | +section "D. xlings reports and repairs a payload that holds another package's download" |
| 116 | +cmd=$(find "$REG/data/xpkgs" -maxdepth 2 -path '*cmdline*' -type d | tail -1) |
| 117 | +if [ -n "$cmd" ]; then |
| 118 | + printf 'x' > "$cmd/intruder-1.0-linux-x86_64.tar.gz"; : > "$cmd/intruder-1.0-linux-x86_64.tar.gz.lock" |
| 119 | + XLINGS_HOME="$REG" "$REG/bin/xlings" self doctor > "$root/doctor.log" 2>&1 |
| 120 | + grep -qi 'intruder\|swept\|download' "$root/doctor.log" && ok "self doctor reports the swept payload" || { fail "self doctor did not report it"; tail -8 "$root/doctor.log"; } |
| 121 | + XLINGS_HOME="$REG" "$REG/bin/xlings" self doctor --fix > "$root/fix.log" 2>&1 |
| 122 | + [ ! -e "$cmd/intruder-1.0-linux-x86_64.tar.gz.lock" ] && ok "self doctor --fix leaves no download sidecar in the payload" || { fail "the sidecar is still there after --fix"; tail -8 "$root/fix.log"; } |
| 123 | +else |
| 124 | + skip "D: section B produced no cmdline payload to seed" |
| 125 | +fi |
| 126 | + |
| 127 | +section "E. Windows: the probe reaches no shell" |
| 128 | +skip "E: a Linux sandbox cannot run cmd.exe; e2e 687 and XlingsVersionPin.ProbeReadsStandardOutputThroughTheLauncher are the Windows criteria (CI)" |
| 129 | + |
| 130 | +printf '\n== summary ==\nfails=%d\n' "$fails" |
| 131 | +[ -n "$skipped" ] && printf 'not run:%s\n' "$skipped" |
| 132 | +[ "$fails" -eq 0 ] |
0 commit comments