From 206e4254dec23d74edeee5337012b02d0ef7cf94 Mon Sep 17 00:00:00 2001 From: Omkhar Arasaratnam Date: Sun, 5 Jul 2026 13:59:25 -0400 Subject: [PATCH 1/3] ^R Extract host-detection into scripts/lib/launcher/host-detect.sh (byte-identical move, shellcheck/shfmt/bash-n/manifest green and outputs verified equal; launcher is user-visible but behavior is unchanged) First increment of roadmap item D4 (modularize the launcher). Moves the five host-detection helpers verbatim out of scripts/workcell into a new sourced module scripts/lib/launcher/host-detect.sh: - support_matrix_host_override_allowed - detected_host_os - detected_host_arch - detected_host_distro - detected_host_distro_version The functions are self-contained (uname/ps/PPID/env vars and each other only) and are sourced before their first call site alongside the other scripts/lib modules. Registers the new file in the validate-repo.sh and dev-quick-check.sh shell gates and regenerates the control-plane manifest hash for scripts/workcell. No logic change; the extracted block is byte-identical to the original. Co-Authored-By: Claude Opus 4.8 --- runtime/container/control-plane-manifest.json | 2 +- scripts/dev-quick-check.sh | 1 + scripts/lib/launcher/host-detect.sh | 130 ++++++++++++++++++ scripts/validate-repo.sh | 1 + scripts/workcell | 119 +--------------- 5 files changed, 135 insertions(+), 118 deletions(-) create mode 100755 scripts/lib/launcher/host-detect.sh diff --git a/runtime/container/control-plane-manifest.json b/runtime/container/control-plane-manifest.json index 75ca983d..08cd5bc3 100644 --- a/runtime/container/control-plane-manifest.json +++ b/runtime/container/control-plane-manifest.json @@ -2,7 +2,7 @@ "host_artifacts": [ { "repo_path": "scripts/workcell", - "sha256": "4895f0722e5bd532542fda4472aef36b5c809c0816017879ea3f8a047c7c537b" + "sha256": "b637943a4c428a591afe37647358cf8af9fdd54d091c5d1254a9986354b57fe5" }, { "repo_path": "scripts/check-publish-commit-signatures.sh", diff --git a/scripts/dev-quick-check.sh b/scripts/dev-quick-check.sh index 8243da42..3c8934c0 100755 --- a/scripts/dev-quick-check.sh +++ b/scripts/dev-quick-check.sh @@ -50,6 +50,7 @@ shell_files=( "${ROOT_DIR}/scripts/ci/run-validate-in-validator.sh" "${ROOT_DIR}/scripts/lib/extract_direct_mounts" "${ROOT_DIR}/scripts/lib/go-run-env.sh" + "${ROOT_DIR}/scripts/lib/launcher/host-detect.sh" "${ROOT_DIR}/scripts/lib/manage_injection_policy" "${ROOT_DIR}/scripts/lib/pty_transcript" "${ROOT_DIR}/scripts/lib/render_injection_bundle" diff --git a/scripts/lib/launcher/host-detect.sh b/scripts/lib/launcher/host-detect.sh new file mode 100755 index 00000000..45322012 --- /dev/null +++ b/scripts/lib/launcher/host-detect.sh @@ -0,0 +1,130 @@ +#!/usr/bin/env -S BASH_ENV= ENV= bash +# shellcheck shell=bash +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2026 Omkhar Arasaratnam +# +# scripts/lib/launcher/host-detect.sh — host-detection module extracted +# from scripts/workcell as the first increment of the launcher +# decomposition (roadmap item D4). These helpers normalise the host OS, +# architecture, and (on Linux) distribution/version into the lowercased +# values the launcher's support-matrix logic consumes. They depend only +# on uname/ps/PPID/env vars and each other — no other launcher function — +# so they are a self-contained, behaviour-preserving unit. See +# docs/launcher-contract.md for the module contract. + +support_matrix_host_override_allowed() { + local parent_cmdline="" + + [[ "${WORKCELL_VERIFY_INVARIANTS_SANITIZED_ENTRYPOINT:-0}" == "1" ]] || return 1 + if [[ -r "/proc/${PPID}/cmdline" ]]; then + parent_cmdline="$(tr '\0' ' ' <"/proc/${PPID}/cmdline" 2>/dev/null || true)" + elif command -v ps >/dev/null 2>&1; then + parent_cmdline="$(ps -p "${PPID}" -o command= 2>/dev/null || true)" + fi + case "${parent_cmdline}" in + *"scripts/verify-invariants.sh"* | \ + *"tests/scenarios/shared/test-copilot-session-dry-run.sh"* | \ + *"tests/scenarios/shared/test-compat-target-dry-run.sh"* | \ + *"tests/scenarios/shared/test-gcp-remote-vm-dry-run.sh"* | \ + *"tests/scenarios/shared/test-aws-remote-vm-dry-run.sh"*) + return 0 + ;; + esac + return 1 +} + +detected_host_os() { + local host_os="" + + # verify-invariants uses a reserved harness-only host override so Linux + # validation runners can still exercise launch assembly without weakening the + # real operator-facing support boundary. + if support_matrix_host_override_allowed && [[ -n "${WORKCELL_TEST_SUPPORT_MATRIX_HOST_OS:-}" ]]; then + printf '%s\n' "${WORKCELL_TEST_SUPPORT_MATRIX_HOST_OS}" + return 0 + fi + + host_os="$(uname -s 2>/dev/null || true)" + case "${host_os}" in + Darwin) + printf 'macos\n' + ;; + Linux) + printf 'linux\n' + ;; + MINGW* | MSYS* | CYGWIN* | Windows_NT) + printf 'windows\n' + ;; + *) + printf '%s\n' "$(printf '%s' "${host_os}" | tr '[:upper:]' '[:lower:]')" + ;; + esac +} + +detected_host_arch() { + local host_arch="" + + if support_matrix_host_override_allowed && [[ -n "${WORKCELL_TEST_SUPPORT_MATRIX_HOST_ARCH:-}" ]]; then + printf '%s\n' "${WORKCELL_TEST_SUPPORT_MATRIX_HOST_ARCH}" + return 0 + fi + + host_arch="$(uname -m 2>/dev/null || true)" + case "${host_arch}" in + arm64 | aarch64) + printf 'arm64\n' + ;; + x86_64 | amd64) + printf 'amd64\n' + ;; + *) + printf '%s\n' "$(printf '%s' "${host_arch}" | tr '[:upper:]' '[:lower:]')" + ;; + esac +} + +detected_host_distro() { + local host_distro="" + + if support_matrix_host_override_allowed && [[ -n "${WORKCELL_TEST_SUPPORT_MATRIX_HOST_DISTRO:-}" ]]; then + printf '%s\n' "${WORKCELL_TEST_SUPPORT_MATRIX_HOST_DISTRO}" + return 0 + fi + + if [[ "$(detected_host_os)" != "linux" ]]; then + printf 'none\n' + return 0 + fi + if [[ -r /etc/os-release ]]; then + # shellcheck disable=SC1091 + . /etc/os-release + host_distro="${ID:-}" + fi + if [[ -z "${host_distro}" ]]; then + host_distro="unknown" + fi + printf '%s\n' "$(printf '%s' "${host_distro}" | tr '[:upper:]' '[:lower:]')" +} + +detected_host_distro_version() { + local host_distro_version="" + + if support_matrix_host_override_allowed && [[ -n "${WORKCELL_TEST_SUPPORT_MATRIX_HOST_DISTRO_VERSION:-}" ]]; then + printf '%s\n' "${WORKCELL_TEST_SUPPORT_MATRIX_HOST_DISTRO_VERSION}" + return 0 + fi + + if [[ "$(detected_host_os)" != "linux" ]]; then + printf 'none\n' + return 0 + fi + if [[ -r /etc/os-release ]]; then + # shellcheck disable=SC1091 + . /etc/os-release + host_distro_version="${VERSION_ID:-${VERSION_CODENAME:-}}" + fi + if [[ -z "${host_distro_version}" ]]; then + host_distro_version="unknown" + fi + printf '%s\n' "$(printf '%s' "${host_distro_version}" | tr '[:upper:]' '[:lower:]')" +} diff --git a/scripts/validate-repo.sh b/scripts/validate-repo.sh index db4f4fbf..07f9912d 100755 --- a/scripts/validate-repo.sh +++ b/scripts/validate-repo.sh @@ -155,6 +155,7 @@ shell_files=( "${ROOT_DIR}/scripts/lint-dockerfiles.sh" "${ROOT_DIR}/scripts/lib/extract_direct_mounts" "${ROOT_DIR}/scripts/lib/go-run-env.sh" + "${ROOT_DIR}/scripts/lib/launcher/host-detect.sh" "${ROOT_DIR}/scripts/lib/trusted-entrypoint.sh" "${ROOT_DIR}/scripts/lib/manage_injection_policy" "${ROOT_DIR}/scripts/lib/pty_transcript" diff --git a/scripts/workcell b/scripts/workcell index 5ddddc74..5e0d80e6 100755 --- a/scripts/workcell +++ b/scripts/workcell @@ -31,123 +31,6 @@ scrub_host_process_env() { scrub_host_process_env -support_matrix_host_override_allowed() { - local parent_cmdline="" - - [[ "${WORKCELL_VERIFY_INVARIANTS_SANITIZED_ENTRYPOINT:-0}" == "1" ]] || return 1 - if [[ -r "/proc/${PPID}/cmdline" ]]; then - parent_cmdline="$(tr '\0' ' ' <"/proc/${PPID}/cmdline" 2>/dev/null || true)" - elif command -v ps >/dev/null 2>&1; then - parent_cmdline="$(ps -p "${PPID}" -o command= 2>/dev/null || true)" - fi - case "${parent_cmdline}" in - *"scripts/verify-invariants.sh"* | \ - *"tests/scenarios/shared/test-copilot-session-dry-run.sh"* | \ - *"tests/scenarios/shared/test-compat-target-dry-run.sh"* | \ - *"tests/scenarios/shared/test-gcp-remote-vm-dry-run.sh"* | \ - *"tests/scenarios/shared/test-aws-remote-vm-dry-run.sh"*) - return 0 - ;; - esac - return 1 -} - -detected_host_os() { - local host_os="" - - # verify-invariants uses a reserved harness-only host override so Linux - # validation runners can still exercise launch assembly without weakening the - # real operator-facing support boundary. - if support_matrix_host_override_allowed && [[ -n "${WORKCELL_TEST_SUPPORT_MATRIX_HOST_OS:-}" ]]; then - printf '%s\n' "${WORKCELL_TEST_SUPPORT_MATRIX_HOST_OS}" - return 0 - fi - - host_os="$(uname -s 2>/dev/null || true)" - case "${host_os}" in - Darwin) - printf 'macos\n' - ;; - Linux) - printf 'linux\n' - ;; - MINGW* | MSYS* | CYGWIN* | Windows_NT) - printf 'windows\n' - ;; - *) - printf '%s\n' "$(printf '%s' "${host_os}" | tr '[:upper:]' '[:lower:]')" - ;; - esac -} - -detected_host_arch() { - local host_arch="" - - if support_matrix_host_override_allowed && [[ -n "${WORKCELL_TEST_SUPPORT_MATRIX_HOST_ARCH:-}" ]]; then - printf '%s\n' "${WORKCELL_TEST_SUPPORT_MATRIX_HOST_ARCH}" - return 0 - fi - - host_arch="$(uname -m 2>/dev/null || true)" - case "${host_arch}" in - arm64 | aarch64) - printf 'arm64\n' - ;; - x86_64 | amd64) - printf 'amd64\n' - ;; - *) - printf '%s\n' "$(printf '%s' "${host_arch}" | tr '[:upper:]' '[:lower:]')" - ;; - esac -} - -detected_host_distro() { - local host_distro="" - - if support_matrix_host_override_allowed && [[ -n "${WORKCELL_TEST_SUPPORT_MATRIX_HOST_DISTRO:-}" ]]; then - printf '%s\n' "${WORKCELL_TEST_SUPPORT_MATRIX_HOST_DISTRO}" - return 0 - fi - - if [[ "$(detected_host_os)" != "linux" ]]; then - printf 'none\n' - return 0 - fi - if [[ -r /etc/os-release ]]; then - # shellcheck disable=SC1091 - . /etc/os-release - host_distro="${ID:-}" - fi - if [[ -z "${host_distro}" ]]; then - host_distro="unknown" - fi - printf '%s\n' "$(printf '%s' "${host_distro}" | tr '[:upper:]' '[:lower:]')" -} - -detected_host_distro_version() { - local host_distro_version="" - - if support_matrix_host_override_allowed && [[ -n "${WORKCELL_TEST_SUPPORT_MATRIX_HOST_DISTRO_VERSION:-}" ]]; then - printf '%s\n' "${WORKCELL_TEST_SUPPORT_MATRIX_HOST_DISTRO_VERSION}" - return 0 - fi - - if [[ "$(detected_host_os)" != "linux" ]]; then - printf 'none\n' - return 0 - fi - if [[ -r /etc/os-release ]]; then - # shellcheck disable=SC1091 - . /etc/os-release - host_distro_version="${VERSION_ID:-${VERSION_CODENAME:-}}" - fi - if [[ -z "${host_distro_version}" ]]; then - host_distro_version="unknown" - fi - printf '%s\n' "$(printf '%s' "${host_distro_version}" | tr '[:upper:]' '[:lower:]')" -} - resolve_self_path() { local source_path="${BASH_SOURCE[0]}" local source_dir="" @@ -181,6 +64,8 @@ source "${ROOT_DIR}/scripts/lib/go-run-env.sh" source "${ROOT_DIR}/scripts/lib/shellproto.sh" # shellcheck source=/dev/null source "${ROOT_DIR}/scripts/lib/sessionctl-shim.sh" +# shellcheck source=/dev/null +source "${ROOT_DIR}/scripts/lib/launcher/host-detect.sh" resolve_fixed_host_tool() { local name="$1" shift From 94feadb15718bc0f19690c067967c16e94ff0a8f Mon Sep 17 00:00:00 2001 From: Omkhar Arasaratnam Date: Sun, 5 Jul 2026 13:59:34 -0400 Subject: [PATCH 2/3] ^d Add docs/launcher-contract.md host-detection section (doc only, no code execution impact; supporting documentation for the D4 refactor) New contract doc for the launcher decomposition (roadmap item D4). This first increment documents the host-detection module: what each of the five functions returns, the WORKCELL_TEST_SUPPORT_MATRIX_HOST_* harness- only overrides gated by support_matrix_host_override_allowed, and that values are emitted lowercased and normalized. Links the doc from its existing mention in the D4 section of the improvement-tracks plan. The doc will grow as more launcher modules are extracted. Co-Authored-By: Claude Opus 4.8 --- .../improvement-tracks-implementation-plan.md | 4 +- docs/launcher-contract.md | 73 +++++++++++++++++++ 2 files changed, 75 insertions(+), 2 deletions(-) create mode 100644 docs/launcher-contract.md diff --git a/docs/improvement-tracks-implementation-plan.md b/docs/improvement-tracks-implementation-plan.md index e31d44c8..bd8c36ee 100644 --- a/docs/improvement-tracks-implementation-plan.md +++ b/docs/improvement-tracks-implementation-plan.md @@ -387,8 +387,8 @@ isolation would be a stronger and lighter boundary than one shared Colima VM. - Steps: split `scripts/workcell` (8,910 lines) into sourced modules (host detection, environment scrubbing, wrapper assembly, dispatch); write - `docs/launcher-contract.md` (required tools, environment expectations, - exit codes, test override flags). + [`launcher-contract.md`](launcher-contract.md) (required tools, environment + expectations, exit codes, test override flags). - Exit gates: behavior-identical on the scenario suite; contract doc matches implementation. - Validation: scenario suite before/after; bats coverage for extracted diff --git a/docs/launcher-contract.md b/docs/launcher-contract.md new file mode 100644 index 00000000..8f037dba --- /dev/null +++ b/docs/launcher-contract.md @@ -0,0 +1,73 @@ +# Launcher contract + +`scripts/workcell` is the host-side launcher. Historically it was a single +monolithic script; roadmap item D4 decomposes it into small, sourced, +behaviour-preserving modules under `scripts/lib/launcher/`. The launcher +sources each module from its `source "${ROOT_DIR}/scripts/lib/..."` block +(after `ROOT_DIR` is resolved) so every extracted helper is defined before its +first call site. + +This document records the contract of each extracted module. It will grow as +further modules are pulled out of the launcher. + +## Host detection (`host-detect.sh`) + +`scripts/lib/launcher/host-detect.sh` normalises the host environment into the +lowercased values the launcher's support-matrix logic consumes. Every helper +depends only on `uname`/`ps`/`PPID`/environment variables and each other — no +other launcher function — which makes the module self-contained. + +All emitted values are lowercased and newline-terminated. Values are normalised +to canonical tokens where recognised, and otherwise passed through lowercased. + +### `support_matrix_host_override_allowed()` + +Gate that decides whether the harness-only host overrides (below) are honoured. +Returns `0` (allowed) only when **both** hold: + +- `WORKCELL_VERIFY_INVARIANTS_SANITIZED_ENTRYPOINT` is `1`; and +- the parent process command line (read from `/proc/${PPID}/cmdline`, falling + back to `ps -p "${PPID}"`) matches one of the recognised validation harness + entrypoints (`scripts/verify-invariants.sh` or the shared dry-run scenario + scripts). + +Returns non-zero otherwise. This keeps the overrides usable by Linux +validation runners exercising launch assembly without weakening the real, +operator-facing support boundary. + +### `detected_host_os()` + +Prints the normalised host operating system: `macos` (Darwin), `linux` +(Linux), or `windows` (MINGW/MSYS/CYGWIN/`Windows_NT`). Any other `uname -s` +value is passed through lowercased. + +### `detected_host_arch()` + +Prints the normalised host architecture: `arm64` (`arm64`/`aarch64`) or `amd64` +(`x86_64`/`amd64`). Any other `uname -m` value is passed through lowercased. + +### `detected_host_distro()` + +On non-Linux hosts prints `none`. On Linux, prints the lowercased `ID` from +`/etc/os-release`, or `unknown` when it cannot be determined. + +### `detected_host_distro_version()` + +On non-Linux hosts prints `none`. On Linux, prints the lowercased `VERSION_ID` +(falling back to `VERSION_CODENAME`) from `/etc/os-release`, or `unknown` when +it cannot be determined. + +### Harness-only overrides + +When `support_matrix_host_override_allowed` returns `0`, each detector first +honours a reserved override environment variable if it is non-empty, printing +its value verbatim: + +- `WORKCELL_TEST_SUPPORT_MATRIX_HOST_OS` +- `WORKCELL_TEST_SUPPORT_MATRIX_HOST_ARCH` +- `WORKCELL_TEST_SUPPORT_MATRIX_HOST_DISTRO` +- `WORKCELL_TEST_SUPPORT_MATRIX_HOST_DISTRO_VERSION` + +These variables are reserved for the validation harness. The launcher scrubs +them from the host process environment unless the sanitized-entrypoint marker +is set, so they cannot be smuggled in by an operator to spoof the detected host. From 8c5c6b5a71642ebf94dfda493abe49e4e3dcbea1 Mon Sep 17 00:00:00 2001 From: Omkhar Arasaratnam Date: Sun, 5 Jul 2026 14:08:27 -0400 Subject: [PATCH 3/3] ^b Track host-detect.sh in the control-plane manifest generator (P2) (the extracted scripts/lib/launcher/host-detect.sh is now sourced launcher code, but GenerateControlPlaneManifest in internal/metadatautil/core.go only listed scripts/workcell + the older lib helpers, so the new module was outside the signed/provenance-verified manifest and host-detection changes wouldn't be caught; add it to hostArtifacts and regenerate control-plane-manifest.json) (manifest verify + go test green; supporting the D4 host-detect extraction - closes the provenance gap the move opened) --- internal/metadatautil/core.go | 1 + runtime/container/control-plane-manifest.json | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/internal/metadatautil/core.go b/internal/metadatautil/core.go index e71a7646..298792ec 100644 --- a/internal/metadatautil/core.go +++ b/internal/metadatautil/core.go @@ -467,6 +467,7 @@ func GenerateControlPlaneManifest(rootDir, outputPath string) error { "scripts/workcell", "scripts/check-publish-commit-signatures.sh", "scripts/lib/extract_direct_mounts", + "scripts/lib/launcher/host-detect.sh", "scripts/lib/render_injection_bundle", "scripts/lib/sessionctl-shim.sh", "scripts/lib/shellproto.sh", diff --git a/runtime/container/control-plane-manifest.json b/runtime/container/control-plane-manifest.json index 08cd5bc3..449ca760 100644 --- a/runtime/container/control-plane-manifest.json +++ b/runtime/container/control-plane-manifest.json @@ -12,6 +12,10 @@ "repo_path": "scripts/lib/extract_direct_mounts", "sha256": "b7dc19f58db225fb695b87ada176c65be9b13edc5ecf43138eda8d01aa6eb879" }, + { + "repo_path": "scripts/lib/launcher/host-detect.sh", + "sha256": "318ac47025849d41e98b775e8d0d4209ae4ba80e1eeebfa393244ebc24d36fbe" + }, { "repo_path": "scripts/lib/render_injection_bundle", "sha256": "f1057951d6f65e12d82a7bc2b5002afde5664670d8f4ed7d9bc289bbec5f5901"