From c6b4fe832e2a25bc7787e14cde13e23bf4d62a3a Mon Sep 17 00:00:00 2001 From: PJC Date: Wed, 9 Sep 2026 16:24:59 +0900 Subject: [PATCH 1/3] Pin the VibeMon installers to the 0.2.13 binary they ask for The 0.2.12 and 0.2.13 scripts passed --profile messages to a pinned 0.2.11 binary, so every new install failed at pairing (report #86). The pins move with the workspace version and a test keeps them together. Co-Authored-By: Claude Fable 5.1 --- docs/migration/vibemon-install.ps1 | 10 ++++--- docs/migration/vibemon-install.sh | 13 +++++---- tests/installers/test_pins.py | 45 ++++++++++++++++++++++++++++++ 3 files changed, 59 insertions(+), 9 deletions(-) create mode 100644 tests/installers/test_pins.py diff --git a/docs/migration/vibemon-install.ps1 b/docs/migration/vibemon-install.ps1 index ea210d6..8d4a956 100644 --- a/docs/migration/vibemon-install.ps1 +++ b/docs/migration/vibemon-install.ps1 @@ -64,10 +64,12 @@ $ErrorActionPreference = "Stop" $DefaultServer = if ($env:VIBEMON_SYNC_URL) { $env:VIBEMON_SYNC_URL } else { "https://sync.vibemon.dev" } if ($Server -eq "") { $Server = $DefaultServer } $Server = $Server.TrimEnd("/") -# 0.2.11 configures local OTel collection with the agent hooks. -# A newer `attempt` already on the machine is kept. -$AttemptVersion = if ($env:ATTEMPTDB_VERSION) { $env:ATTEMPTDB_VERSION } else { "0.2.11" } -$InstallerVersion = "0.2.11+install.1" +# 0.2.13 knows the `messages` sync profile the connect step asks for; an +# older binary rejects `-Profile messages` and pairing fails. tests/installers +# pins this to the workspace version. A newer `attempt` already on the +# machine is kept. +$AttemptVersion = if ($env:ATTEMPTDB_VERSION) { $env:ATTEMPTDB_VERSION } else { "0.2.13" } +$InstallerVersion = "0.2.13+install.1" $env:ATTEMPTDB_VERSION = $AttemptVersion $Installer = if ($env:ATTEMPTDB_INSTALLER) { $env:ATTEMPTDB_INSTALLER } else { "https://raw.githubusercontent.com/nullarch/attemptdb/v$AttemptVersion/install.ps1" } $BinDir = if ($env:ATTEMPTDB_BIN_DIR) { $env:ATTEMPTDB_BIN_DIR } else { Join-Path $env:LOCALAPPDATA "AttemptDB\bin" } diff --git a/docs/migration/vibemon-install.sh b/docs/migration/vibemon-install.sh index 5c389a1..e4f5569 100755 --- a/docs/migration/vibemon-install.sh +++ b/docs/migration/vibemon-install.sh @@ -85,11 +85,14 @@ AUTO_MIGRATE=0 RUNTIME=service INSTALL_TMP="" # The installer hotfix and the binary have independent immutable pins. -# 0.2.11 configures local OTel collection with the agent hooks. -# A newer `attempt` already on the machine is kept. -ATTEMPTDB_VERSION="${ATTEMPTDB_VERSION:-0.2.11}" -INSTALLER_VERSION="0.2.11+install.1" -INSTALLER_REF="v0.2.11" +# 0.2.13 knows the `messages` sync profile the connect step asks for (and +# reads flushed conversation content back with the key); an older binary +# rejects `--profile messages` and pairing fails. tests/installers pins +# ATTEMPTDB_VERSION to the workspace version so a release cannot leave it +# behind again. A newer `attempt` already on the machine is kept. +ATTEMPTDB_VERSION="${ATTEMPTDB_VERSION:-0.2.13}" +INSTALLER_VERSION="0.2.13+install.1" +INSTALLER_REF="install-2026-09-09.1" ATTEMPTDB_INSTALLER="${ATTEMPTDB_INSTALLER:-https://raw.githubusercontent.com/nullarch/attemptdb/v${ATTEMPTDB_VERSION}/install.sh}" export ATTEMPTDB_VERSION diff --git a/tests/installers/test_pins.py b/tests/installers/test_pins.py new file mode 100644 index 0000000..8e03155 --- /dev/null +++ b/tests/installers/test_pins.py @@ -0,0 +1,45 @@ +"""The installers' binary pin follows the workspace version. + +The connect step passes `--profile messages`, which only 0.2.12+ binaries +know. On 2026-09-09 the 0.2.12 and 0.2.13 releases shipped installer scripts +that still downloaded 0.2.11, so every new install failed at pairing with +`unknown profile messages` (install report #86). A release that bumps +Cargo.toml must bump both scripts with it; this test is the reminder. +""" + +import re +import unittest +from pathlib import Path + +ROOT = Path(__file__).resolve().parents[2] + + +def workspace_version() -> str: + text = (ROOT / "Cargo.toml").read_text() + match = re.search(r'^version = "([0-9]+\.[0-9]+\.[0-9]+)"', text, re.M) + assert match, "workspace version not found in Cargo.toml" + return match.group(1) + + +class PinTests(unittest.TestCase): + def test_shell_installer_pins_the_workspace_version(self): + text = (ROOT / "docs/migration/vibemon-install.sh").read_text() + version = workspace_version() + self.assertIn(f'ATTEMPTDB_VERSION="${{ATTEMPTDB_VERSION:-{version}}}"', text) + self.assertRegex(text, rf'^INSTALLER_VERSION="{re.escape(version)}\+install\.[0-9]+"$') + + def test_powershell_installer_pins_the_workspace_version(self): + text = (ROOT / "docs/migration/vibemon-install.ps1").read_text() + version = workspace_version() + self.assertIn(f'else {{ "{version}" }}', text) + self.assertRegex(text, rf'^\$InstallerVersion = "{re.escape(version)}\+install\.[0-9]+"$') + + def test_messages_profile_needs_a_binary_that_knows_it(self): + sh = (ROOT / "docs/migration/vibemon-install.sh").read_text() + self.assertIn('PROFILE="messages"', sh) + major, minor, patch = (int(n) for n in workspace_version().split(".")) + self.assertGreaterEqual((major, minor, patch), (0, 2, 12)) + + +if __name__ == "__main__": + unittest.main() From 4a3156e932cc6194274f229dfbfa36ff537c05f9 Mon Sep 17 00:00:00 2001 From: PJC Date: Wed, 9 Sep 2026 16:28:26 +0900 Subject: [PATCH 2/3] Installer tests: the fake attempt reports the workspace version; pin regexes are multiline Co-Authored-By: Claude Fable 5.1 --- tests/installers/test_pins.py | 4 ++-- tests/installers/test_vibemon_install.py | 12 +++++++++++- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/tests/installers/test_pins.py b/tests/installers/test_pins.py index 8e03155..2c6f918 100644 --- a/tests/installers/test_pins.py +++ b/tests/installers/test_pins.py @@ -26,13 +26,13 @@ def test_shell_installer_pins_the_workspace_version(self): text = (ROOT / "docs/migration/vibemon-install.sh").read_text() version = workspace_version() self.assertIn(f'ATTEMPTDB_VERSION="${{ATTEMPTDB_VERSION:-{version}}}"', text) - self.assertRegex(text, rf'^INSTALLER_VERSION="{re.escape(version)}\+install\.[0-9]+"$') + self.assertRegex(text, rf'(?m)^INSTALLER_VERSION="{re.escape(version)}\+install\.[0-9]+"$') def test_powershell_installer_pins_the_workspace_version(self): text = (ROOT / "docs/migration/vibemon-install.ps1").read_text() version = workspace_version() self.assertIn(f'else {{ "{version}" }}', text) - self.assertRegex(text, rf'^\$InstallerVersion = "{re.escape(version)}\+install\.[0-9]+"$') + self.assertRegex(text, rf'(?m)^\$InstallerVersion = "{re.escape(version)}\+install\.[0-9]+"$') def test_messages_profile_needs_a_binary_that_knows_it(self): sh = (ROOT / "docs/migration/vibemon-install.sh").read_text() diff --git a/tests/installers/test_vibemon_install.py b/tests/installers/test_vibemon_install.py index 2dfa840..52d1baa 100644 --- a/tests/installers/test_vibemon_install.py +++ b/tests/installers/test_vibemon_install.py @@ -1,4 +1,5 @@ """Installer regressions with stubbed commands; no network or host changes.""" +import re import json import os from pathlib import Path @@ -20,7 +21,7 @@ elif name in ("systemctl", "launchctl"): sys.exit(int(os.environ.get("SERVICE_EXIT", "0"))) elif name == "attempt": - if args == ["--version"]: print("attempt 0.2.11") + if args == ["--version"]: print("attempt __WORKSPACE_VERSION__") elif args == ["sync", "status", "--json"]: print(json.dumps({"connected": os.environ.get("CONNECTED") == "1"})) elif args[:2] == ["daemon", "status"]: print(json.dumps({"endpoint": "unix:/fixture/daemon.sock", "running": True})) @@ -46,6 +47,15 @@ elif name == "powershell.exe": sys.exit(int(os.environ.get("NATIVE_EXIT", "0"))) ''' +# The fake `attempt` reports the workspace version — the same one the script +# pins — so the "present and recent enough; keeping it" path is exercised. +WORKSPACE_VERSION = re.search( + r'^version = "([0-9]+\.[0-9]+\.[0-9]+)"', + (Path(__file__).resolve().parents[2] / "Cargo.toml").read_text(), + re.M, +).group(1) +STUB = STUB.replace("__WORKSPACE_VERSION__", WORKSPACE_VERSION) + class MigrationTests(unittest.TestCase): def run_install(self, args=(), legacy=False, missing=(), **settings): From 3f5cbdc3973b0c7a1a1aad85a594da54c069a5ae Mon Sep 17 00:00:00 2001 From: PJC Date: Wed, 9 Sep 2026 16:39:27 +0900 Subject: [PATCH 3/3] Report test reads the pinned installer version from the script Co-Authored-By: Claude Fable 5.1 --- tests/installers/test_reports.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/installers/test_reports.py b/tests/installers/test_reports.py index a149c84..3117efa 100644 --- a/tests/installers/test_reports.py +++ b/tests/installers/test_reports.py @@ -1,4 +1,5 @@ """Full PowerShell installer failures against a loopback report receiver.""" +import re import http.server import json import os @@ -73,7 +74,8 @@ def test_unhandled_download_exception_reports_the_stage_once(self): self.assertEqual((reports[0]["ok"], reports[0]["step"]), (False, "binary")) self.assertTrue(reports[0]["error"]) self.assertTrue(reports[0]["unattended"]) - self.assertEqual(reports[0]["installer_version"], "0.2.11+install.1") + pinned = re.search(r'^\$InstallerVersion = "([^"]+)"', SCRIPT.read_text(), re.M).group(1) + self.assertEqual(reports[0]["installer_version"], pinned) def test_explicit_failure_is_not_reported_twice_by_the_trap(self): code, reports = self.invoke(preflight=410)