← Commit history

Release tiers, corrected to the adom/insiders convention: manifest at adom/insiders pup-bridge/version.json, bytes as releases here, pkg published every ship; promote/rollback require a human attestation

John Lauer ·e08b5e387e ·1mo ago ·parent 524070d
4 files changed +92−97
CLAUDE.md+3−3
@@ -46,8 +46,8 @@ via the `updateManifestUrl` in `bridge.json`. (Extracted from `adom-bridge/plugi  ## The publish/reinstall loop — TWO TIERS (adopted 2026-09-02, pup-bridge#86) -`tools/ship.sh <ver> "<changelog>"` ships to the **INSIDERS** tier: the private page-`adom/pup-bridge-insiders`, whose READERS are the tier (John's desktops today). Desktops on ab ≥2.0.99+`tools/ship.sh <ver> "<changelog>"` ships to the **INSIDERS** tier: the shared private page+`adom/insiders` (its repo carries ONLY `<product>/version.json`; pup's is `pup-bridge/version.json`), whose READERS are the tier (John's desktops today). Desktops on ab ≥2.0.99 probe `insidersManifestUrl` (declared in `src/bridge.json`) first with the user's own wiki login and follow it when they can read it; everyone else silently follows the PUBLIC manifest on `adom/pup-bridge`, which ship.sh never touches. The public tier moves ONLY through@@ -58,7 +58,7 @@ the skills pkg (pkg tiering waits on adom/wiki#168, so pkg = public tier). `tool the public manifest at an earlier promoted build on the same rule. One plain semver per build in both tiers; never `-beta` suffixes. An AI passes the human's words through verbatim or does not promote. Test an insiders build on a desktop with `adom-bridge --target <box> bridge_install {"manifestUrl":-".../pup-bridge-insiders/files/adom-bridge-puppeteer-insiders-manifest.json","force":true}` (needs+"https://wiki.adom.inc/api/v1/pages/insiders/files/pup-bridge/version.json","force":true}` (needs that desktop's login to read the page). Wiki = canonical. Full recipe: `PUBLISHING.md`.  **ALWAYS reload/recreate every pup window after a deploy (standing rule from John, 2026-08-11):**
PUBLISHING.md+2−2
@@ -87,8 +87,8 @@ adom-bridge-cli bridge_install '{"manifestUrl":"https://wiki.adom.inc/api/v1/pag  ## Release tiers (2026-09-02, pup-bridge#86) -- `tools/ship.sh <ver> "<log>"` → INSIDERS (private page `adom/pup-bridge-insiders`; readers = tier).-  Archives `dist/<ver>/{zip, insiders-manifest.json}`. Public tier untouched. No pkg publish.+- `tools/ship.sh <ver> "<log>"` → INSIDERS (shared private page `adom/insiders`, file `pup-bridge/version.json`; readers = tier; the zip is a release on adom/pup-bridge).+  Archives `dist/<ver>/{zip, insiders-manifest.json}`. Public MANIFEST untouched. The skills pkg IS published every ship so the page's top version row always carries a tarball (release rows and pkg rows share a namespace; a release-only row breaks `pkg install`, #66). - `tools/promote.sh <ver> "<human's exact words>" --by "<name>"` → PUBLIC. Refuses without the words;   same bytes (sha-checked); records `promotion_attestation`/`promoted_by`/`promoted_at` in the public   manifest; pushes source; publishes the skills pkg; verifies the top row carries the tarball.
tools/promote.sh+31−49
@@ -1,27 +1,26 @@ #!/usr/bin/env bash # promote.sh — move an INSIDERS build to the PUBLIC tier, on a human's exact words. #-#   tools/promote.sh <version> "<the human's exact attestation words>" [--by "<name>"]+#   tools/promote.sh <version> "<the human's exact attestation words>" --by "<name>" #-# pup-bridge#86 / ab 2.0.99 release tiers: the public page (adom/pup-bridge) moves ONLY through-# this script, and this script REFUSES without an attestation. An AI passes the human's words-# through verbatim or does not promote — never on its own judgment, never because a sweep was-# green. The words travel IN the public manifest (promotion_attestation / promoted_by /-# promoted_at) so the record of who approved the rollout rides with the release.+# pup-bridge#86 / ab 2.0.99 release tiers: the public manifest (adom-bridge-puppeteer-manifest.json,+# the updateManifestUrl every non-insider desktop follows) moves ONLY through this script, and this+# script REFUSES without an attestation. An AI passes the human's words through verbatim or does+# not promote — never on its own judgment. The words travel IN the public manifest+# (promotion_attestation / promoted_by / promoted_at). #-# Same signed bytes in both tiers: the zip archived by tools/ship.sh under dist/<ver>/ is-# uploaded as-is and its sha256 is checked against the insiders manifest before anything moves.-# The skills pkg is published HERE (not at insiders-ship time): registry dist-tag tiering is-# adom/wiki#168, so `pkg latest` is effectively the public tier.+# The bytes are ALREADY on the product page: tools/ship.sh uploaded the zip as release <ver> on+# adom/pup-bridge and archived it under dist/<ver>/ with the insiders manifest. Promote just+# re-points the public manifest at that same asset (sha re-checked) and pushes it. Source + skills+# pkg were pushed/published at ship time. set -euo pipefail-VER="${1:?usage: tools/promote.sh <version> \"<attestation words>\" [--by name]}"+VER="${1:?usage: tools/promote.sh <version> \"<attestation words>\" --by <name>}" ATTEST="${2:-}" shift 2 || true BY="${ADOM_PROMOTED_BY:-}" while [ $# -gt 0 ]; do case "$1" in --by) BY="$2"; shift 2;; *) shift;; esac; done REPO="$(cd "$(dirname "$0")/.." && pwd)"; cd "$REPO" PUBLIC="adom/pup-bridge"-INSIDERS="adom/pup-bridge-insiders" ZIP="adom-bridge-puppeteer-v${VER}.zip" ARCH="dist/${VER}" @@ -29,7 +28,7 @@ echo "== 0. attestation gate ==" A="$(printf '%s' "$ATTEST" | tr -s '[:space:]' ' ' | sed 's/^ //;s/ $//')" if [ "${#A}" -lt 8 ] || printf '%s' "$A" | grep -qiE '^(ok|yes|promote|ship it|go|approved|test|placeholder|x+)$'; then   echo "REFUSED: promoting to the PUBLIC tier needs the human's exact words (>= 8 chars, not a placeholder)."-  echo "         e.g. tools/promote.sh $VER \"John: 2.0.380 is good on all three of my boxes, ship it to everyone\" --by \"John Lauer\""+  echo "         e.g. tools/promote.sh $VER \"John: $VER is good on all three of my boxes, ship it to everyone\" --by \"John Lauer\""   exit 2 fi [ -n "$BY" ] || { echo "REFUSED: say who promoted (--by \"<name>\" or ADOM_PROMOTED_BY)."; exit 2; }@@ -41,53 +40,36 @@ echo "== 1. the archived insiders build ==" [ -f "$ARCH/insiders-manifest.json" ] || { echo "MISSING $ARCH/insiders-manifest.json — the insiders record for $VER"; exit 1; } SHA_LOCAL=$(python3 -c "import hashlib,sys;print(hashlib.sha256(open(sys.argv[1],'rb').read()).hexdigest())" "$ARCH/$ZIP") SHA_INS=$(python3 -c "import json,sys;print(json.load(open(sys.argv[1]))['sha256'])" "$ARCH/insiders-manifest.json")-[ "$SHA_LOCAL" = "$SHA_INS" ] || { echo "SHA MISMATCH: archived zip $SHA_LOCAL vs insiders manifest $SHA_INS — refusing to promote different bytes"; exit 1; }-echo "   $ZIP sha256=$SHA_LOCAL matches the insiders manifest (same signed bytes)"-ON_DISK=$(cat src/BRIDGE_VERSION)-[ "$ON_DISK" = "$VER" ] || { echo "TREE MISMATCH: working tree is at $ON_DISK, promoting $VER — the public source push must match the promoted runtime. Check out / re-ship $VER first."; exit 1; }+[ "$SHA_LOCAL" = "$SHA_INS" ] || { echo "SHA MISMATCH: archived zip $SHA_LOCAL vs insiders manifest $SHA_INS — refusing"; exit 1; }+URL=$(python3 -c "import json,sys;print(json.load(open(sys.argv[1]))['url'])" "$ARCH/insiders-manifest.json")+REMOTE=$(curl -sL "$URL" | python3 -c "import hashlib,sys;print(hashlib.sha256(sys.stdin.buffer.read()).hexdigest())")+[ "$REMOTE" = "$SHA_LOCAL" ] || { echo "REMOTE ASSET MISMATCH: $URL serves sha $REMOTE, archive is $SHA_LOCAL — refusing to point the public tier at it"; exit 1; }+echo "   $ZIP sha256=$SHA_LOCAL matches the insiders record AND the served release asset" -echo "== 2. public release =="-adom-wiki release create "$PUBLIC" "$VER" >/dev/null 2>&1 || true-UP=$(adom-wiki release upload "$PUBLIC" "$VER" "$ARCH/$ZIP" --json 2>&1 | tail -c 4000)-python3 - "$UP" "$VER" "$ZIP" "$ARCH" "$A" "$BY" <<'PY'-import json, sys, datetime, hashlib, os-raw, ver, zipname, arch, words, by = sys.argv[1:7]-i = raw.find('{'); d = json.loads(raw[i:]) if i >= 0 else {}-a = d.get('data') or d.get('asset') or d-url = a.get('download_url') or f"https://wiki.adom.inc/download/adom/pup-bridge/{ver}/{zipname}"-if not url.startswith('http'): url = 'https://wiki.adom.inc' + url-sha = hashlib.sha256(open(os.path.join(arch, zipname), 'rb').read()).hexdigest(); size = os.path.getsize(os.path.join(arch, zipname))+echo "== 2. public manifest =="+python3 - "$VER" "$ARCH" "$A" "$BY" <<'PY'+import json, sys, datetime, os+ver, arch, words, by = sys.argv[1:5] ins = json.load(open(os.path.join(arch, 'insiders-manifest.json'))) now = datetime.datetime.now(datetime.timezone.utc).isoformat().replace('+00:00', 'Z') m = json.load(open('adom-bridge-puppeteer-manifest.json'))-m.update({'version': ver, 'url': url, 'sha256': sha, 'size': size, 'released_at': now,+m.update({'version': ver, 'url': ins['url'], 'sha256': ins['sha256'], 'size': ins['size'], 'released_at': now,           'insiders_released_at': ins.get('released_at'),           'promotion_attestation': words, 'promoted_by': by, 'promoted_at': now})-m.pop('rollback_attestation', None); m.pop('rolled_back_by', None); m.pop('rolled_back_at', None)+m.pop('tier', None)+for k in ('rollback_attestation', 'rolled_back_by', 'rolled_back_at'): m.pop(k, None) json.dump(m, open('adom-bridge-puppeteer-manifest.json', 'w'), indent=2); open('adom-bridge-puppeteer-manifest.json', 'a').write('\n') json.dump(m, open(os.path.join(arch, 'public-manifest.json'), 'w'), indent=2)-print(f"   public manifest -> {url}\n   sha256={sha} size={size}\n   archived {arch}/public-manifest.json")+print(f"   public manifest -> {ins['url']}\n   archived {arch}/public-manifest.json") PY -echo "== 3. push the public source + manifest =="-adom-wiki repo push "$PUBLIC" \-  --files adom-bridge-puppeteer-manifest.json src/bridge.json src/BRIDGE_VERSION src/*.js src/extension/*.js src/extension/*.json SKILL.md package.json \-  -m "v${VER}: promoted to public — ${BY}: ${A}" >/dev/null 2>&1 || {+echo "== 3. push the public manifest =="+adom-wiki repo push "$PUBLIC" --files adom-bridge-puppeteer-manifest.json \+  -m "v${VER}: promoted to PUBLIC — ${BY}: ${A}" >/dev/null 2>&1 || {     [ "${FORCE_PUSH:-0}" = "1" ] || { echo "   PUSH FAILED (set FORCE_PUSH=1 to retry)"; exit 1; }   } -echo "== 4. skills pkg (public tier = pkg latest until adom/wiki#168) =="-PUBOUT=$(adom-wiki pkg publish --org adom 2>&1); PUBRC=$?-echo "$PUBOUT" | tail -3-[ $PUBRC -eq 0 ] || { echo "PKG PUBLISH FAILED (exit $PUBRC) — top row would have no tarball (pup-bridge#66)"; echo "$PUBOUT" | tail -15; exit 1; }-adom-wiki pkg info adom/pup-bridge 2>/dev/null | python3 -c "-import sys,json-raw=sys.stdin.read()-d,_=json.JSONDecoder().raw_decode(raw[raw.find('{'):])-v=d['versions'][0]-assert v.get('integrity') and v.get('size_bytes'), f\"top row {v['version']} has NO tarball\"-print(f\"   pkg tarball verified on top row {v['version']} ({v['size_bytes']} bytes)\")" || { echo "PKG TARBALL VERIFY FAILED"; exit 1; }--echo "== 5. DIFF-VERIFY =="+echo "== 4. DIFF-VERIFY ==" MAN=$(python3 -c "import json;print(json.load(open('adom-bridge-puppeteer-manifest.json'))['version'])")-[ "$MAN" = "$VER" ] && echo "PROMOTED $VER to PUBLIC (attested by $BY)" || { echo "PUBLIC MANIFEST NOT AT $VER (got $MAN)"; exit 1; }+LIVE=$(curl -s https://wiki.adom.inc/api/v1/pages/pup-bridge/files/adom-bridge-puppeteer-manifest.json | python3 -c "import json,sys;print(json.load(sys.stdin).get('version'))" 2>/dev/null || echo '?')+[ "$MAN" = "$VER" ] && echo "PROMOTED $VER to PUBLIC (attested by $BY); live public manifest reads $LIVE" || { echo "PUBLIC MANIFEST NOT AT $VER (got $MAN)"; exit 1; }
tools/ship.sh+56−43
@@ -1,37 +1,36 @@ #!/usr/bin/env bash # ship.sh — THE publish pipeline for this bridge, living IN THE REPO. #-# It used to live in /tmp and evaporated with every /tmp clear (twice now, mid-session), so the-# recipe in PUBLISHING.md had to be hand-run from memory. It belongs here.-# #   tools/ship.sh <version> "<changelog sentence>"            # -> INSIDERS tier (the default) #   tools/promote.sh <version> "<human's words>" --by "<name>"  # -> PUBLIC tier, attested #   tools/rollback.sh <version> "<human's words>" --by "<name>" # re-point PUBLIC at an older build #-# RELEASE TIERS (pup-bridge#86, ab 2.0.99 insidersManifestUrl, adopted 2026-09-02 on John's word):-#   Every ship lands on the PRIVATE page adom/pup-bridge-insiders. Desktops whose wiki login can-#   read that page (ab >= 2.0.99 probes its manifest first with the user's own session) follow it-#   automatically; everyone else keeps following the PUBLIC manifest on adom/pup-bridge, which this-#   script never touches. The public tier moves only via tools/promote.sh, which REFUSES without-#   the human's exact words and records them in the public manifest. One plain semver per build-#   in both tiers; promote moves a version, it never renames one. The skills pkg publishes at-#   PROMOTE time (registry dist-tag tiering is adom/wiki#168), so insiders ships are runtime-only.+# RELEASE TIERS (pup-bridge#86, ab 2.0.99 insidersManifestUrl; adopted 2026-09-02 on John's word,+# and corrected the same hour when he caught the first cut creating a NEW private page: that is+# wiki-bloat. The fleet convention is ONE shared private page, adom/insiders, whose repo carries+# ONLY <product>/version.json manifests; the bytes live as releases on the product's own page.) #-# Steps: verify the tree parses -> bump BRIDGE_VERSION + bridge.json + package.json (lockstep) ->-# zip src/ at the zip ROOT (no node_modules) -> release create/upload on the INSIDERS page ->-# write the insiders manifest with the asset's REAL sha256/size -> archive zip+manifest under-# dist/<ver>/ (promote + rollback work from this record) -> repo push the text files to the-# insiders page -> DIFF-VERIFY the version actually landed.+#   - Every ship: the zip becomes a release on adom/pup-bridge (like ab's installer on+#     adom/adom-bridge), the INSIDERS manifest is written to adom/insiders as+#     pup-bridge/version.json (bridge-manifest shape), the source text files are pushed to the+#     public page repo (source is open), and the skills pkg is published so the page's top+#     version row always carries a tarball (release rows + pkg rows share one namespace; a+#     release-only row breaks `pkg install`, #66). The PUBLIC manifest+#     (adom-bridge-puppeteer-manifest.json, updateManifestUrl) is NEVER touched here.+#   - Desktops on ab >= 2.0.99 whose login can read adom/insiders follow the insiders manifest;+#     everyone else follows the public manifest and never learns the tier exists.+#   - The public manifest moves ONLY via tools/promote.sh, which REFUSES without the human's exact+#     words and records them in the manifest. One plain semver per build in both tiers. # # Env: FORCE_PUSH=1 pushes even when the working tree looks unchanged.-#      TIER=public is NOT accepted here on purpose — use tools/promote.sh. set -euo pipefail VER="${1:?usage: tools/ship.sh <version> \"<changelog>\"}" LOG="${2:?changelog sentence required}"-if [ "${3:-}" != "" ] && [ "${3:-}" != "1" ]; then echo "note: the old PUBLISH_PKG arg is ignored — the skills pkg now publishes at promote time (tools/promote.sh)"; fi REPO="$(cd "$(dirname "$0")/.." && pwd)"-PAGE="adom/pup-bridge-insiders"-MANIFEST="adom-bridge-puppeteer-insiders-manifest.json"+PAGE="adom/pup-bridge"+INSIDERS_PAGE="adom/insiders"+INSIDERS_DIR="dist/insiders-repo"          # working copy shaped like the adom/insiders repo+INSIDERS_REL="pup-bridge/version.json"      # the convention: <product>/version.json ZIP="adom-bridge-puppeteer-v${VER}.zip" ARCH="dist/${VER}" cd "$REPO"@@ -42,9 +41,6 @@ node --check src/chrome.js node --check src/card_vault.js node tests/check_dashboard_script.js >/dev/null node tests/check_card_vault.js >/dev/null-# 2.0.339: the dock moved to the bridge-served dashboard and the tab reverted to the old filled-# tile, because the favicon belongs to the PAGE, not the app. This gate keeps the two copies of-# the drawing identical and refuses currentColor, which renders black in an isolated document. node tests/check_icon.js >/dev/null echo "   server/chrome/card_vault parse; dashboard script parses; card vault checks; dock favicon matches" @@ -56,11 +52,11 @@ v = sys.argv[1] p = 'src/bridge.json' d = json.load(open(p)) d['version'] = v-assert d.get('insidersManifestUrl'), 'bridge.json must declare insidersManifestUrl (pup-bridge#86)'+assert d.get('insidersManifestUrl', '').endswith('/pages/insiders/files/pup-bridge/version.json'), 'bridge.json insidersManifestUrl must point at adom/insiders pup-bridge/version.json (pup-bridge#86)' json.dump(d, open(p, 'w'), indent=2) open(p, 'a').write('\n') print(f"   src/BRIDGE_VERSION + src/bridge.json -> {v}")-p2 = 'package.json'   # the SKILLPACK descriptor — pkg publish (at promote time) reads THIS version.+p2 = 'package.json' d2 = json.load(open(p2)) d2['version'] = v json.dump(d2, open(p2, 'w'), indent=2)@@ -82,46 +78,63 @@ with zipfile.ZipFile(out, 'w', zipfile.ZIP_DEFLATED) as z: print(f"   {out}  sha256={hashlib.sha256(open(out,'rb').read()).hexdigest()}  size={os.path.getsize(out)}") PY -echo "== 3. INSIDERS release ($PAGE) =="+echo "== 3. release on the product page ($PAGE) ==" adom-wiki release create "$PAGE" "$VER" >/dev/null 2>&1 || true UP=$(adom-wiki release upload "$PAGE" "$VER" "$ARCH/$ZIP" --json 2>&1 | tail -c 4000)-python3 - "$UP" "$VER" "$ZIP" "$ARCH" "$MANIFEST" <<'PY'+mkdir -p "$INSIDERS_DIR/pup-bridge"+python3 - "$UP" "$VER" "$ZIP" "$ARCH" "$INSIDERS_DIR/$INSIDERS_REL" <<'PY' import json, sys, datetime, hashlib, os-raw, ver, zipname, arch, manifest = sys.argv[1:6]+raw, ver, zipname, arch, ins_path = sys.argv[1:6] i = raw.find('{') d = json.loads(raw[i:]) if i >= 0 else {} a = d.get('data') or d.get('asset') or d sha, size = a.get('sha256'), a.get('size')-url = a.get('download_url') or f"https://wiki.adom.inc/download/adom/pup-bridge-insiders/{ver}/{zipname}"+url = a.get('download_url') or f"https://wiki.adom.inc/download/adom/pup-bridge/{ver}/{zipname}" if not url.startswith('http'): url = 'https://wiki.adom.inc' + url zp = os.path.join(arch, zipname)-if not (sha and size):   # fall back to the local file (same bytes we just uploaded)+if not (sha and size):     sha = hashlib.sha256(open(zp, 'rb').read()).hexdigest(); size = os.path.getsize(zp)-# The insiders manifest is the same SHAPE as the public one; seed it from the public manifest the-# first time so name/entrypoint/etc. stay identical between tiers.-base = json.load(open(manifest)) if os.path.exists(manifest) else json.load(open('adom-bridge-puppeteer-manifest.json'))+# Insiders manifest = the public manifest's SHAPE (name/entrypoint/etc. identical between tiers).+base = json.load(open('adom-bridge-puppeteer-manifest.json')) for k in ('promotion_attestation', 'promoted_by', 'promoted_at', 'rollback_attestation', 'rolled_back_by', 'rolled_back_at', 'insiders_released_at'):     base.pop(k, None) base.update({'version': ver, 'url': url, 'sha256': sha, 'size': size, 'tier': 'insiders',              'released_at': datetime.datetime.now(datetime.timezone.utc).isoformat().replace('+00:00', 'Z')})-json.dump(base, open(manifest, 'w'), indent=2); open(manifest, 'a').write('\n')+json.dump(base, open(ins_path, 'w'), indent=2); open(ins_path, 'a').write('\n') json.dump(base, open(os.path.join(arch, 'insiders-manifest.json'), 'w'), indent=2)-print(f"   insiders manifest -> {url}\n   sha256={sha} size={size}\n   archived {arch}/ (zip + insiders-manifest.json)")+print(f"   release asset -> {url}\n   sha256={sha} size={size}\n   insiders manifest staged at {ins_path}; archived {arch}/") PY -echo "== 4. push the text files to the INSIDERS page =="+echo "== 4. insiders manifest -> $INSIDERS_PAGE:$INSIDERS_REL =="+( cd "$INSIDERS_DIR" && adom-wiki repo push "$INSIDERS_PAGE" --files "$INSIDERS_REL" -m "pup-bridge ${VER} (insiders): ${LOG}" >/dev/null 2>&1 ) || {+  [ "${FORCE_PUSH:-0}" = "1" ] || { echo "   INSIDERS PUSH FAILED (set FORCE_PUSH=1 to retry)"; exit 1; }+}++echo "== 5. push the source text files to the product page (source is open; public manifest untouched) ==" adom-wiki repo push "$PAGE" \-  --files "$MANIFEST" src/bridge.json src/BRIDGE_VERSION src/*.js src/extension/*.js src/extension/*.json SKILL.md package.json \+  --files src/bridge.json src/BRIDGE_VERSION src/*.js src/extension/*.js src/extension/*.json SKILL.md package.json \   -m "v${VER} (insiders): ${LOG}" >/dev/null 2>&1 || {     [ "${FORCE_PUSH:-0}" = "1" ] || { echo "   PUSH FAILED (set FORCE_PUSH=1 to retry)"; exit 1; }   } -echo "== 5. DIFF-VERIFY (a no-op ship is the classic silent failure) =="+echo "== 6. skills pkg (keeps the top version row carrying a tarball, #66) =="+PUBOUT=$(adom-wiki pkg publish --org adom 2>&1); PUBRC=$?+echo "$PUBOUT" | tail -2+[ $PUBRC -eq 0 ] || { echo "PKG PUBLISH FAILED (exit $PUBRC)"; echo "$PUBOUT" | tail -15; exit 1; }+adom-wiki pkg info adom/pup-bridge 2>/dev/null | python3 -c "+import sys,json+raw=sys.stdin.read()+d,_=json.JSONDecoder().raw_decode(raw[raw.find('{'):])+v=d['versions'][0]+assert v.get('integrity') and v.get('size_bytes'), f\"top row {v['version']} has NO tarball\"+print(f\"   pkg tarball verified on top row {v['version']} ({v['size_bytes']} bytes)\")" || { echo "PKG TARBALL VERIFY FAILED"; exit 1; }++echo "== 7. DIFF-VERIFY ==" ON_DISK=$(cat src/BRIDGE_VERSION)-MAN=$(python3 -c "import json,sys;print(json.load(open(sys.argv[1]))['version'])" "$MANIFEST")+MAN=$(python3 -c "import json,sys;print(json.load(open(sys.argv[1]))['version'])" "$INSIDERS_DIR/$INSIDERS_REL") BJ=$(python3 -c "import json;print(json.load(open('src/bridge.json'))['version'])")+PUB=$(python3 -c "import json;print(json.load(open('adom-bridge-puppeteer-manifest.json'))['version'])") [ "$ON_DISK" = "$VER" ] && [ "$MAN" = "$VER" ] && [ "$BJ" = "$VER" ] \-  && echo "VERIFIED $VER on INSIDERS (BRIDGE_VERSION + bridge.json + insiders manifest in lockstep)" \-  || { echo "LOCKSTEP BROKEN: disk=$ON_DISK bridge.json=$BJ manifest=$MAN"; exit 1; }-echo "   PUBLIC tier untouched. To roll this out to everyone, on the human's words:"-echo "   tools/promote.sh $VER \"<their exact words>\" --by \"<name>\""+  && echo "VERIFIED $VER on INSIDERS (BRIDGE_VERSION + bridge.json + adom/insiders pup-bridge/version.json in lockstep); PUBLIC manifest still at $PUB" \+  || { echo "LOCKSTEP BROKEN: disk=$ON_DISK bridge.json=$BJ insiders=$MAN"; exit 1; }+echo "   To roll $VER out to everyone, on the human's words: tools/promote.sh $VER \"<their exact words>\" --by \"<name>\""