From 9d50e7ce1e64a731d88cca8ae15ec2c45b1375df Mon Sep 17 00:00:00 2001 From: vks-archastro Date: Wed, 9 Sep 2026 16:36:52 -0700 Subject: [PATCH 1/3] fix: install CLI-only ArchDev releases without retired dashboard --- .github/workflows/installer-smoke-test.yml | 7 ++----- README.md | 5 ++--- install.ps1 | 10 ++++------ install.sh | 10 +++------- scripts/create-unix-fixtures.sh | 8 ++------ scripts/create-windows-fixtures.ps1 | 3 +-- 6 files changed, 14 insertions(+), 29 deletions(-) diff --git a/.github/workflows/installer-smoke-test.yml b/.github/workflows/installer-smoke-test.yml index fc8344d..7181479 100644 --- a/.github/workflows/installer-smoke-test.yml +++ b/.github/workflows/installer-smoke-test.yml @@ -79,7 +79,7 @@ jobs: sleep 1 done exit 1 - - name: Install CLI and dashboard + - name: Install CLI-only release env: ARCHDEV_RELEASE_BASE_URL: http://127.0.0.1:8123 ARCHDEV_VERSION: 0.31.0 @@ -90,7 +90,6 @@ jobs: mkdir -p "$HOME" ./install.sh test "$("$HOME/.local/bin/archdev" --version)" = 0.31.0 - test -x "$HOME/.local/bin/archdev-dashboard" test -f "$HOME/${{ matrix.completion_file }}" - name: Stop fixture server if: always() @@ -110,14 +109,12 @@ jobs: $server = Start-Process python -ArgumentList "-m", "http.server", "8123", "--bind", "127.0.0.1" -WorkingDirectory "$env:RUNNER_TEMP\release" -PassThru $server.Id | Set-Content "$env:RUNNER_TEMP\server.pid" Start-Sleep -Seconds 2 - - name: Install CLI and dashboard + - name: Install CLI-only release shell: pwsh run: | ./install.ps1 -Version 0.31.0 -BaseUrl http://127.0.0.1:8123 -InstallDir "$env:RUNNER_TEMP\bin" -SkipPathUpdate $cliVersion = & "$env:RUNNER_TEMP\bin\archdev.exe" --version if ($cliVersion.Trim() -ne "0.31.0") { throw "archdev.exe version mismatch" } - $sidecarVersion = & "$env:RUNNER_TEMP\bin\archdev-dashboard.exe" --version - if ($sidecarVersion.Trim() -ne "0.31.0") { throw "archdev-dashboard.exe is not runnable" } - name: Stop fixture server if: always() shell: pwsh diff --git a/README.md b/README.md index 3609d8f..749caa6 100644 --- a/README.md +++ b/README.md @@ -33,9 +33,8 @@ curl -fsSL https://raw.githubusercontent.com/ArchAstro/archdev/main/install.sh | irm https://raw.githubusercontent.com/ArchAstro/archdev/main/install.ps1 | iex ``` -The release archive installs both `archdev` and its `archdev-dashboard` -sidecar. The Unix installer also configures Bash, Zsh, or Fish completions for -the active shell. +The release archive installs `archdev`. The Unix installer also configures +Bash, Zsh, or Fish completions for the active shell. ## Install Rooms independently diff --git a/install.ps1 b/install.ps1 index 8fbe576..4a1ee42 100644 --- a/install.ps1 +++ b/install.ps1 @@ -55,11 +55,9 @@ try { $ActualHash = (Get-FileHash $ArchivePath -Algorithm SHA256).Hash if ($ActualHash.ToLowerInvariant() -ne $ExpectedHash.ToLowerInvariant()) { throw "Checksum mismatch for $AssetName" } Expand-Archive -Path $ArchivePath -DestinationPath $ExtractDir -Force - foreach ($Name in @("archdev.exe", "archdev-dashboard.exe")) { - $Source = Join-Path $ExtractDir $Name - if (-not (Test-Path $Source)) { throw "Archive is missing $Name" } - Copy-Item $Source (Join-Path $InstallDir $Name) -Force - } + $Source = Join-Path $ExtractDir "archdev.exe" + if (-not (Test-Path $Source)) { throw "Archive is missing archdev.exe" } + Copy-Item $Source (Join-Path $InstallDir "archdev.exe") -Force if (-not $SkipPathUpdate) { $CurrentUserPath = [Environment]::GetEnvironmentVariable("Path", "User") $Entries = if ($CurrentUserPath) { $CurrentUserPath -split ';' } else { @() } @@ -69,7 +67,7 @@ try { } } if (-not $SkipVerify) { & (Join-Path $InstallDir "archdev.exe") --version } - Write-Host "Installed archdev and archdev-dashboard to $InstallDir" + Write-Host "Installed archdev to $InstallDir" } finally { Remove-Item $TempRoot -Recurse -Force -ErrorAction SilentlyContinue } diff --git a/install.sh b/install.sh index 4759312..a0000bf 100755 --- a/install.sh +++ b/install.sh @@ -5,7 +5,6 @@ set -euo pipefail OWNER="ArchAstro" REPO="archdev" BINARY_NAME="archdev" -SIDECAR_NAME="archdev-dashboard" INSTALL_DIR="${ARCHDEV_INSTALL_DIR:-}" REQUESTED_VERSION="${ARCHDEV_VERSION:-latest}" RELEASE_BASE_URL="${ARCHDEV_RELEASE_BASE_URL:-}" @@ -112,7 +111,6 @@ asset_url=${ASSET_URL} checksum_url=${CHECKSUM_URL} install_dir=${INSTALL_DIR} binary_path=${INSTALL_DIR}/${BINARY_NAME} -sidecar_path=${INSTALL_DIR}/${SIDECAR_NAME} EOF exit 0 fi @@ -146,10 +144,8 @@ fi mkdir -p "$EXTRACT_DIR" tar -xzf "$ASSET_PATH" -C "$EXTRACT_DIR" -for executable in "$BINARY_NAME" "$SIDECAR_NAME"; do - [[ -f "$EXTRACT_DIR/$executable" ]] || { printf 'Archive is missing %s\n' "$executable" >&2; exit 1; } - install -m 0755 "$EXTRACT_DIR/$executable" "$INSTALL_DIR/$executable" -done +[[ -f "$EXTRACT_DIR/$BINARY_NAME" ]] || { printf 'Archive is missing %s\n' "$BINARY_NAME" >&2; exit 1; } +install -m 0755 "$EXTRACT_DIR/$BINARY_NAME" "$INSTALL_DIR/$BINARY_NAME" append_once() { local file="$1" line="$2" @@ -198,4 +194,4 @@ fi if [[ "$SKIP_VERIFY" != true ]]; then "$INSTALL_DIR/$BINARY_NAME" --version fi -printf 'Installed %s and %s to %s\n' "$BINARY_NAME" "$SIDECAR_NAME" "$INSTALL_DIR" +printf 'Installed %s to %s\n' "$BINARY_NAME" "$INSTALL_DIR" diff --git a/scripts/create-unix-fixtures.sh b/scripts/create-unix-fixtures.sh index aad9779..3a64a7c 100755 --- a/scripts/create-unix-fixtures.sh +++ b/scripts/create-unix-fixtures.sh @@ -22,12 +22,8 @@ if [ "\${1:-}" = "--version" ]; then printf '%s\n' '$VERSION'; exit 0; fi if [ "\${1:-}" = "completion" ]; then printf '# completion for %s\n' "\${2:-unknown}"; exit 0; fi exit 0 EOF - cat >"$fixture/archdev-dashboard" <<'EOF' -#!/usr/bin/env sh -exit 0 -EOF - chmod +x "$fixture/archdev" "$fixture/archdev-dashboard" - tar -C "$fixture" -czf "$OUTPUT_DIR/archdev-$target.tar.gz" archdev archdev-dashboard + chmod +x "$fixture/archdev" + tar -C "$fixture" -czf "$OUTPUT_DIR/archdev-$target.tar.gz" archdev rm -rf "$fixture" done ( diff --git a/scripts/create-windows-fixtures.ps1 b/scripts/create-windows-fixtures.ps1 index 19cd422..0d794b1 100644 --- a/scripts/create-windows-fixtures.ps1 +++ b/scripts/create-windows-fixtures.ps1 @@ -26,8 +26,7 @@ foreach ($Arch in @("arm64", "x64")) { $Fixture = Join-Path ([IO.Path]::GetTempPath()) ("archdev-fixture-" + [Guid]::NewGuid().ToString("N")) New-Item -ItemType Directory -Path $Fixture | Out-Null Copy-Item $FixtureBinary (Join-Path $Fixture "archdev.exe") - Copy-Item (Join-Path $Fixture "archdev.exe") (Join-Path $Fixture "archdev-dashboard.exe") - Compress-Archive -Path (Join-Path $Fixture "archdev.exe"), (Join-Path $Fixture "archdev-dashboard.exe") -DestinationPath (Join-Path $OutputDir "archdev-windows-$Arch.zip") + Compress-Archive -Path (Join-Path $Fixture "archdev.exe") -DestinationPath (Join-Path $OutputDir "archdev-windows-$Arch.zip") Remove-Item $Fixture -Recurse -Force } Remove-Item $FixtureBinaryRoot -Recurse -Force From 8dba35c88fdc382c382cbc547eafed31c9708c98 Mon Sep 17 00:00:00 2001 From: vks-archastro Date: Wed, 9 Sep 2026 16:38:47 -0700 Subject: [PATCH 2/3] fix(rooms): bootstrap Knowledge-ready CLI and guide evidence-backed recall --- .github/workflows/installer-smoke-test.yml | 3 + skills/rooms/SKILL.md | 49 ++++++++---- skills/rooms/scripts/bootstrap.ps1 | 17 +++-- skills/rooms/scripts/bootstrap.sh | 17 +++-- tests/rooms-bootstrap.ps1 | 89 ++++++++++++++++++++++ tests/rooms-skill.sh | 59 ++++++++++++-- 6 files changed, 202 insertions(+), 32 deletions(-) create mode 100644 tests/rooms-bootstrap.ps1 diff --git a/.github/workflows/installer-smoke-test.yml b/.github/workflows/installer-smoke-test.yml index 7181479..1d06bc1 100644 --- a/.github/workflows/installer-smoke-test.yml +++ b/.github/workflows/installer-smoke-test.yml @@ -100,6 +100,9 @@ jobs: runs-on: windows-latest steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + - name: Verify Rooms bootstrap compatibility + shell: pwsh + run: ./tests/rooms-bootstrap.ps1 - name: Build fixture release shell: pwsh run: ./scripts/create-windows-fixtures.ps1 -OutputDir "$env:RUNNER_TEMP\release" -Version 0.31.0 diff --git a/skills/rooms/SKILL.md b/skills/rooms/SKILL.md index cd3fc75..d53defb 100644 --- a/skills/rooms/SKILL.md +++ b/skills/rooms/SKILL.md @@ -45,23 +45,43 @@ Inspect the returned `delivery` object. Pending posts are restarted during the connection. If `failed` is nonzero, tell the user how many posts were rejected and give them `failedPath`; do not report those posts as delivered. -For a substantial session, immediately read the latest 15 messages with the -connected Room ID. This is the lightweight Room brief and catches current work -before planning begins. +For a substantial session, read the latest 15 messages and existing approved +records with the connected Room ID before planning: + +```sh +"$archdev" --json rooms messages "" --limit 15 +"$archdev" --json rooms records list "" --status approved +``` + +Recent messages show current work; approved records preserve decisions beyond +that window. If optional records are unavailable, continue with search and +recent messages; do not create schemas or treat the missing records as proof +that no decisions exist. ## Recall and answer -Before planning substantial work, search once for the subsystem, symptom, +Before planning substantial work, begin by searching for the subsystem, symptom, error, or behavior: ```sh "$archdev" --json rooms search "" ``` -The installed coding agent answers directly from the returned messages; no -resident agent is required. Cite supporting message IDs, senders, and -timestamps. Separate inference from facts. Empty, malformed, or failed results -are inconclusive, not proof that the team has no knowledge. +The installed coding agent answers from server Knowledge results; no resident +agent is required. Each hit's `content` contains indexed text. Read +`raw_content` for the original message's `user_id`, `inserted_at`, and +`metadata` (including `human`, `post_type`, and `refs` when present). Cite that +attribution, date, and available reference links. Knowledge `raw_content.id` +and `metadata.message_id` are internal provenance IDs, not public `msg_` IDs; +do not fabricate a message URL or public lookup from them. Public message IDs +come from message responses. Separate inference from facts and do not invent +missing evidence. + +If a successful query is empty or insufficient, try a broader query using the +subsystem, symptom, or exact error. New posts may not yet be indexed; use +`"$archdev" --json rooms search "" --messages` to check recent messages +directly. Empty, malformed, or failed results are inconclusive. Do not present +an unsuccessful lookup as proof that the team has no knowledge. Read recent activity at session start and again before committing or opening a PR: @@ -78,7 +98,10 @@ lesson or collision to the user, then verify locally. For substantial work, publish `start` after the scope is understood. Publish a `lesson` immediately for a reusable root cause or fix, and `abandoned` when an approach should not be repeated. Finish with `done` or a named `handoff`. -Questions and handoffs must begin with `@firstname`. +Questions and handoffs must begin with `@firstname`. State the symptom, cause, +decision or rejected approach, and verification when known; avoid routine +progress noise. Use full review URLs and repository file links in `-r` when +available, so another agent can inspect the evidence without guessing a repo. ```sh "$archdev" --json rooms start "Plain-English headline" -b "One concrete fact" -r "path or PR" @@ -106,9 +129,9 @@ For ordinary conversation only, use: "$archdev" --json rooms post "" "" ``` -If the repository supplies a harness-owned PR evidence publisher, follow that -repository's instructions before posting `done`; do not invent evidence or -replace the publisher with Room prose. Always include the review reference in -the structured `done` post. +When work has a PR, include its review link plus the intent, useful findings, +and actual verification in the structured `done` post. No separate evidence +capture package is required for Rooms. Follow any additional evidence +requirements of the repository without inventing evidence. Never post secrets, tokens, customer data, or unreviewed private content. diff --git a/skills/rooms/scripts/bootstrap.ps1 b/skills/rooms/scripts/bootstrap.ps1 index cf8c755..0d39abb 100644 --- a/skills/rooms/scripts/bootstrap.ps1 +++ b/skills/rooms/scripts/bootstrap.ps1 @@ -8,7 +8,7 @@ function Install-ArchDev { $installerUrl = if ($env:ARCHDEV_INSTALLER_URL) { $env:ARCHDEV_INSTALLER_URL } else { - "https://raw.githubusercontent.com/ArchAstro/archdev/7c16002d66a004b13812cf675042cb1c50fbf6df/install.ps1" + "https://raw.githubusercontent.com/ArchAstro/archdev/9d50e7ce1e64a731d88cca8ae15ec2c45b1375df/install.ps1" } $installDir = if ($env:ARCHDEV_INSTALL_DIR) { $env:ARCHDEV_INSTALL_DIR @@ -30,9 +30,15 @@ function Install-ArchDev { $existing = Get-Command archdev -ErrorAction SilentlyContinue $archdev = if ($existing) { Resolve-ArchDevPath $existing.Source } else { Install-ArchDev } -& $archdev rooms start --help *> $null -if ($LASTEXITCODE -ne 0) { - [Console]::Error.WriteLine("Updating ArchDev because this version lacks Rooms lifecycle commands.") +function Test-Rooms([string]$Binary) { + & $Binary rooms start --help *> $null + if ($LASTEXITCODE -ne 0) { return $false } + $helpText = & $Binary rooms search --help 2>$null + return ($LASTEXITCODE -eq 0 -and (($helpText -join "`n") -match '--messages')) +} + +if (-not (Test-Rooms $archdev)) { + [Console]::Error.WriteLine("Updating ArchDev because this version lacks Rooms lifecycle or Knowledge search commands.") $archdev = Install-ArchDev } @@ -41,6 +47,5 @@ if (-not (Test-Path -LiteralPath $archdev -PathType Leaf)) { } & $archdev --version *> $null if ($LASTEXITCODE -ne 0) { throw "ArchDev version verification failed" } -& $archdev rooms start --help *> $null -if ($LASTEXITCODE -ne 0) { throw "Installed ArchDev does not provide Rooms lifecycle commands" } +if (-not (Test-Rooms $archdev)) { throw "Installed ArchDev does not provide Rooms lifecycle and Knowledge search commands" } Write-Output $archdev diff --git a/skills/rooms/scripts/bootstrap.sh b/skills/rooms/scripts/bootstrap.sh index 04f0586..f0a1a1a 100755 --- a/skills/rooms/scripts/bootstrap.sh +++ b/skills/rooms/scripts/bootstrap.sh @@ -2,7 +2,7 @@ set -euo pipefail -installer_revision="7c16002d66a004b13812cf675042cb1c50fbf6df" +installer_revision="9d50e7ce1e64a731d88cca8ae15ec2c45b1375df" installer_url="${ARCHDEV_INSTALLER_URL:-https://raw.githubusercontent.com/ArchAstro/archdev/${installer_revision}/install.sh}" install_dir="${ARCHDEV_INSTALL_DIR:-$HOME/.local/bin}" @@ -29,8 +29,15 @@ else executable="$(absolute_path "$install_dir/archdev")" fi -if ! "$executable" rooms start --help >/dev/null 2>&1; then - printf 'Updating ArchDev because this version lacks Rooms lifecycle commands.\n' >&2 +supports_rooms() { + local help_text + "$1" rooms start --help >/dev/null 2>&1 || return 1 + help_text="$("$1" rooms search --help 2>/dev/null)" || return 1 + grep -Fq -- '--messages' <<<"$help_text" +} + +if ! supports_rooms "$executable"; then + printf 'Updating ArchDev because this version lacks Rooms lifecycle or Knowledge search commands.\n' >&2 install_archdev executable="$(absolute_path "$install_dir/archdev")" fi @@ -41,8 +48,8 @@ fi } "$executable" --version >&2 -"$executable" rooms start --help >/dev/null 2>&1 || { - printf 'Installed ArchDev does not provide Rooms lifecycle commands.\n' >&2 +supports_rooms "$executable" || { + printf 'Installed ArchDev does not provide Rooms lifecycle and Knowledge search commands.\n' >&2 exit 1 } printf '%s\n' "$executable" diff --git a/tests/rooms-bootstrap.ps1 b/tests/rooms-bootstrap.ps1 new file mode 100644 index 0000000..b28684c --- /dev/null +++ b/tests/rooms-bootstrap.ps1 @@ -0,0 +1,89 @@ +$ErrorActionPreference = 'Stop' +$root = Join-Path ([IO.Path]::GetTempPath()) ('rooms-bootstrap-' + [Guid]::NewGuid().ToString('N')) +$bootstrap = Join-Path $PSScriptRoot '../skills/rooms/scripts/bootstrap.ps1' +$originalPath = $env:PATH +$originalInstallDir = $env:ARCHDEV_INSTALL_DIR +$originalInstallerUrl = $env:ARCHDEV_INSTALLER_URL +New-Item -ItemType Directory $root | Out-Null + +try { + # Native process fixture: lifecycle-only releases and Knowledge releases + # both exit successfully for help; only the latter exposes --messages. + foreach ($kind in @('old', 'current')) { + $dir = Join-Path $root $kind + New-Item -ItemType Directory $dir | Out-Null + $binary = Join-Path $dir 'archdev.exe' + $searchHelp = if ($kind -eq 'current') { '--messages' } else { 'Usage: archdev rooms search ' } + if ($env:OS -eq 'Windows_NT') { + $source = @" +using System; +class Program { + static int Main(string[] args) { + string command = String.Join(" ", args); + if (command == "--version") { Console.WriteLine("fixture"); return 0; } + if (command == "rooms start --help") return 0; + if (command == "rooms search --help") { Console.WriteLine("$searchHelp"); return 0; } + return 2; + } +} +"@ + $sourcePath = Join-Path $dir 'Program.cs' + Set-Content $sourcePath $source + $compiler = Join-Path $env:WINDIR 'Microsoft.NET/Framework64/v4.0.30319/csc.exe' + & $compiler /nologo /target:exe "/out:$binary" $sourcePath + if ($LASTEXITCODE -ne 0) { throw 'Fixture compilation failed' } + } else { + Set-Content $binary @" +#!/bin/sh +case "`$*" in + '--version') echo fixture;; + 'rooms start --help') exit 0;; + 'rooms search --help') echo '$searchHelp';; + *) exit 2;; +esac +"@ + & chmod +x $binary + & ln -s $binary (Join-Path $dir 'archdev') + } + } + + # Replace only the installer-download boundary; the actual bootstrap runs + # the downloaded script and invokes the exact returned native executable. + $fixture = @{ Binary = (Join-Path $root 'current/archdev.exe'); Downloads = 0 } + function Invoke-WebRequest($Uri, $OutFile) { + $fixture.Downloads++ + Set-Content $OutFile @" +param([switch]`$SkipPathUpdate) +New-Item -ItemType Directory `$env:ARCHDEV_INSTALL_DIR -Force | Out-Null +Copy-Item '$($fixture.Binary)' (Join-Path `$env:ARCHDEV_INSTALL_DIR 'archdev.exe') -Force +"@ + } + $env:ARCHDEV_INSTALLER_URL = 'https://fixture.invalid/install.ps1' + $env:ARCHDEV_INSTALL_DIR = Join-Path $root 'installed' + $env:PATH = (Join-Path $root 'old') + [IO.Path]::PathSeparator + $originalPath + $result = & $bootstrap + $expected = Join-Path $env:ARCHDEV_INSTALL_DIR 'archdev.exe' + if ($result -ne $expected -or $fixture.Downloads -ne 1) { + throw 'Bootstrap accepted a lifecycle-only CLI without upgrading Knowledge search' + } + + # A current CLI needs no download; a still-incompatible installation fails + # without returning a misleading usable path. + $env:PATH = (Join-Path $root 'current') + [IO.Path]::PathSeparator + $originalPath + $result = & $bootstrap + $existingPath = (Get-Command archdev).Source + if ($result -ne $existingPath -or $fixture.Downloads -ne 1) { throw 'Current CLI was unnecessarily installed' } + $env:PATH = (Join-Path $root 'old') + [IO.Path]::PathSeparator + $originalPath + $fixture.Binary = Join-Path $root 'old/archdev.exe' + $failure = $null + try { $result = & $bootstrap } catch { $failure = $_ } + if (-not $failure -or "$failure" -notmatch 'Installed ArchDev does not provide') { + throw 'Bootstrap did not reject an incompatible installer result' + } + Write-Output 'Rooms PowerShell bootstrap upgrades old CLI, reuses current CLI, and rejects incompatible installs.' +} finally { + $env:PATH = $originalPath + $env:ARCHDEV_INSTALL_DIR = $originalInstallDir + $env:ARCHDEV_INSTALLER_URL = $originalInstallerUrl + Remove-Item $root -Recurse -Force +} diff --git a/tests/rooms-skill.sh b/tests/rooms-skill.sh index a6bafcb..2a1ce74 100755 --- a/tests/rooms-skill.sh +++ b/tests/rooms-skill.sh @@ -20,7 +20,7 @@ test -x "$root/home/.agents/skills/rooms/scripts/bootstrap.sh" --skill rooms --agent codex --yes --copy >/dev/null ) test -x "$root/project/.agents/skills/rooms/scripts/bootstrap.sh" -grep -F '7c16002d66a004b13812cf675042cb1c50fbf6df' \ +grep -F '9d50e7ce1e64a731d88cca8ae15ec2c45b1375df' \ "$root/project/.agents/skills/rooms/scripts/bootstrap.sh" >/dev/null if grep -Fq '/archdev/main/install' \ "$root/project/.agents/skills/rooms/scripts/bootstrap.sh"; then @@ -38,12 +38,16 @@ cat >"$ARCHDEV_INSTALL_DIR/archdev" <<'ARCHDEV' #!/usr/bin/env bash set -euo pipefail if [[ "${1:-}" == "--version" ]]; then - printf 'archdev test\n' + printf '0.35.4\n' exit 0 fi if [[ "${1:-}" == "rooms" && "${2:-}" == "start" && "${3:-}" == "--help" ]]; then exit 0 fi +if [[ "${1:-}" == "rooms" && "${2:-}" == "search" && "${3:-}" == "--help" ]]; then + printf 'Usage: archdev rooms search [options] \n --messages Search recent messages directly\n' + exit 0 +fi if [[ "${1:-}" == "auth" && "${2:-}" == "status" ]]; then [[ -f "$HOME/.archdev-test-authenticated" ]] exit @@ -63,7 +67,7 @@ if [[ "${1:-}" == "rooms" && "${2:-}" == "messages" && "${3:-}" == "tem_room" ]] exit 0 fi if [[ "${1:-}" == "rooms" && "${2:-}" == "search" ]]; then - printf '{"room":{"id":"tem_room"},"data":[{"id":"msg_fact","content":"The stable retry key survives response loss","user":"usr_teammate","agent":null,"created_at":"2026-09-07T17:00:00Z","similarity_score":0.9}]}\n' + printf '{"room":{"id":"tem_room"},"source":"cks_room","data":[{"id":"cki_fact","content":"The stable retry key survives response loss","raw_content":{"id":"11de9a9e-93ea-428a-9224-0a94f3ef5a54","user_id":"547ec22d-cccf-4413-a883-0a5071e9e502","inserted_at":"2026-09-07T17:00:00Z","metadata":{"human":"Teammate","post_type":"lesson","refs":["https://example.com/review"]}},"similarity_score":0.9}]}\n' exit 0 fi if [[ "${1:-}" == "rooms" && "${2:-}" == "lesson" ]]; then @@ -90,9 +94,48 @@ test "$binary" = "$expected" test -x "$binary" "$binary" rooms start --help -# First-use boundary: follow the public skill's actual happy path from an -# unauthenticated machine through login, connection, recall, Q&A evidence, and -# one durable structured post. +# Bootstrap compatibility: lifecycle commands alone are insufficient. An old +# CLI on PATH must be replaced, and the returned path must name the new binary. +mkdir -p "$root/old" +cat >"$root/old/archdev" <<'OLD_ARCHDEV' +#!/usr/bin/env bash +if [[ "$*" == '--version' ]]; then printf '0.34.0\n'; exit 0; fi +if [[ "$*" == 'rooms start --help' ]]; then exit 0; fi +if [[ "$*" == 'rooms search --help' ]]; then printf 'Usage: archdev rooms search \n'; exit 0; fi +printf 'Usage: archdev rooms [options] [command]\n' +exit 0 +OLD_ARCHDEV +chmod 0755 "$root/old/archdev" +upgraded="$(PATH="$root/old:/usr/bin:/bin" ARCHDEV_INSTALL_DIR="$root/upgraded" \ + ARCHDEV_INSTALLER_URL="file://$root/installer/install.sh" \ + bash "$root/project/.agents/skills/rooms/scripts/bootstrap.sh")" +test "$upgraded" = "$(cd -P "$root/upgraded" && pwd)/archdev" || { + printf 'Bootstrap accepted a lifecycle-only CLI without Knowledge search.\n' >&2 + exit 1 +} + +# A capable binary must not invoke the installer, even if another install +# directory is configured. A failed upgrade must not return the old binary. +existing="$(PATH="$root/bin:/usr/bin:/bin" ARCHDEV_INSTALL_DIR="$root/unused" \ + ARCHDEV_INSTALLER_URL="file://$root/missing-installer" \ + bash "$root/project/.agents/skills/rooms/scripts/bootstrap.sh")" +test "$existing" = "$expected" +cat >"$root/installer/old.sh" <<'OLD_INSTALLER' +#!/usr/bin/env bash +mkdir -p "$ARCHDEV_INSTALL_DIR" +cp "$OLD_ARCHDEV" "$ARCHDEV_INSTALL_DIR/archdev" +OLD_INSTALLER +if PATH="$root/old:/usr/bin:/bin" ARCHDEV_INSTALL_DIR="$root/rejected" \ + OLD_ARCHDEV="$root/old/archdev" ARCHDEV_INSTALLER_URL="file://$root/installer/old.sh" \ + bash "$root/project/.agents/skills/rooms/scripts/bootstrap.sh" >"$root/rejected-output" 2>"$root/rejected-error"; then + printf 'Bootstrap accepted an installer that still lacks Knowledge search.\n' >&2 + exit 1 +fi +test ! -s "$root/rejected-output" +grep -F 'Installed ArchDev does not provide' "$root/rejected-error" >/dev/null + +# Command-contract fixture only: this is not live authentication, Knowledge, +# or agent behavior. Those boundaries are verified separately. if HOME="$root/home" "$binary" auth status; then printf 'Expected the cold test user to start signed out.\n' >&2 exit 1 @@ -105,8 +148,8 @@ printf '%s' "$connected" | grep -F '"id":"tem_room"' >/dev/null recent="$(HOME="$root/home" "$binary" --json rooms messages tem_room --limit 15)" printf '%s' "$recent" | grep -F '"id":"msg_recent"' >/dev/null answer_sources="$(HOME="$root/home" "$binary" --json rooms search 'how do retries avoid duplicates')" -printf '%s' "$answer_sources" | grep -F '"id":"msg_fact"' >/dev/null +printf '%s' "$answer_sources" | grep -F '"id":"11de9a9e-93ea-428a-9224-0a94f3ef5a54"' >/dev/null published="$(HOME="$root/home" "$binary" --json rooms lesson 'Retry evidence is durable' -b 'Keep one stable key')" printf '%s' "$published" | grep -F '"queued":true' >/dev/null -printf 'Rooms installs in both scopes and completes login, join, recall, search, and publish.\n' +printf 'Rooms packaging passes both install scopes, CLI compatibility, and fixture command contracts.\n' From b04ea01454a0dc55a0b3dc3626e7bd0073f80f39 Mon Sep 17 00:00:00 2001 From: vks-archastro Date: Wed, 9 Sep 2026 16:44:10 -0700 Subject: [PATCH 3/3] test: keep Windows installer fixture ready through verification --- .github/workflows/installer-smoke-test.yml | 37 +++++++++++++--------- 1 file changed, 22 insertions(+), 15 deletions(-) diff --git a/.github/workflows/installer-smoke-test.yml b/.github/workflows/installer-smoke-test.yml index 1d06bc1..227677d 100644 --- a/.github/workflows/installer-smoke-test.yml +++ b/.github/workflows/installer-smoke-test.yml @@ -106,22 +106,29 @@ jobs: - name: Build fixture release shell: pwsh run: ./scripts/create-windows-fixtures.ps1 -OutputDir "$env:RUNNER_TEMP\release" -Version 0.31.0 - - name: Start fixture server - shell: pwsh - run: | - $server = Start-Process python -ArgumentList "-m", "http.server", "8123", "--bind", "127.0.0.1" -WorkingDirectory "$env:RUNNER_TEMP\release" -PassThru - $server.Id | Set-Content "$env:RUNNER_TEMP\server.pid" - Start-Sleep -Seconds 2 - name: Install CLI-only release shell: pwsh run: | - ./install.ps1 -Version 0.31.0 -BaseUrl http://127.0.0.1:8123 -InstallDir "$env:RUNNER_TEMP\bin" -SkipPathUpdate - $cliVersion = & "$env:RUNNER_TEMP\bin\archdev.exe" --version - if ($cliVersion.Trim() -ne "0.31.0") { throw "archdev.exe version mismatch" } - - name: Stop fixture server - if: always() - shell: pwsh - run: | - if (Test-Path "$env:RUNNER_TEMP\server.pid") { - Stop-Process -Id (Get-Content "$env:RUNNER_TEMP\server.pid") -Force -ErrorAction SilentlyContinue + $serverLog = Join-Path $env:RUNNER_TEMP "server.log" + $serverError = Join-Path $env:RUNNER_TEMP "server-error.log" + $server = Start-Process python -ArgumentList "-m", "http.server", "8123", "--bind", "127.0.0.1" -WorkingDirectory (Join-Path $env:RUNNER_TEMP "release") -RedirectStandardOutput $serverLog -RedirectStandardError $serverError -PassThru + try { + $ready = $false + for ($attempt = 0; $attempt -lt 20; $attempt++) { + if ($server.HasExited) { throw "Fixture server exited before becoming ready" } + try { + Invoke-WebRequest http://127.0.0.1:8123/SHA256SUMS -TimeoutSec 1 | Out-Null + $ready = $true + break + } catch { Start-Sleep -Milliseconds 250 } + } + if (-not $ready) { throw "Fixture server did not become ready" } + ./install.ps1 -Version 0.31.0 -BaseUrl http://127.0.0.1:8123 -InstallDir "$env:RUNNER_TEMP\bin" -SkipPathUpdate + $cliVersion = & "$env:RUNNER_TEMP\bin\archdev.exe" --version + if ($cliVersion.Trim() -ne "0.31.0") { throw "archdev.exe version mismatch" } + } catch { + Get-Content $serverLog, $serverError -ErrorAction SilentlyContinue + throw + } finally { + Stop-Process -Id $server.Id -Force -ErrorAction SilentlyContinue }