From e506d3ff4ca5cf4e96b189bdad0c286526207aac Mon Sep 17 00:00:00 2001 From: Alexander Mills Date: Mon, 3 Aug 2026 01:02:31 -0500 Subject: [PATCH 01/61] test: add recursive install shared-home harness --- scripts/recursive_installs.py | 284 ++++++++++++++++++++++++++++++++++ 1 file changed, 284 insertions(+) create mode 100644 scripts/recursive_installs.py diff --git a/scripts/recursive_installs.py b/scripts/recursive_installs.py new file mode 100644 index 00000000..125c1b8e --- /dev/null +++ b/scripts/recursive_installs.py @@ -0,0 +1,284 @@ +#!/usr/bin/env python3 +"""Hermetic end-to-end coverage for recursive zed installs and shared-home locks.""" + +from __future__ import annotations + +import argparse +import os +import re +import subprocess +import sys +import tomllib +from concurrent.futures import ThreadPoolExecutor +from dataclasses import dataclass +from pathlib import Path +from threading import Barrier +from typing import Mapping, Sequence + +ORG = "zed-recursive-test" +VERSION = "1.0.0" +PACKAGE_GRAPH: dict[str, tuple[str, ...]] = { + "leaf": (), + "left": ("leaf",), + "right": ("leaf",), + "root": ("left", "right"), +} +PREFETCH_RE = re.compile( + r"recursive install prefetch: (?P\d+) package\(s\), " + r"up to (?P\d+) concurrent, (?P\d+) downloaded" +) + + +@dataclass(frozen=True) +class Completed: + command: tuple[str, ...] + returncode: int + output: str + + +def shell_quote(value: str) -> str: + if re.fullmatch(r"[A-Za-z0-9_./:@%+=,-]+", value): + return value + return "'" + value.replace("'", "'\"'\"'") + "'" + + +def run( + command: Sequence[str | Path], + *, + cwd: Path | None = None, + env: Mapping[str, str] | None = None, +) -> Completed: + argv = tuple(str(value) for value in command) + shown = " ".join(shell_quote(value) for value in argv) + print(f"\n$ (cd {cwd or Path.cwd()} && {shown})", flush=True) + completed = subprocess.run( + argv, + cwd=cwd, + env=dict(env) if env is not None else None, + text=True, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + check=False, + timeout=180, + ) + if completed.stdout: + print(completed.stdout, end="", flush=True) + return Completed(argv, completed.returncode, completed.stdout) + + +def require_success(completed: Completed) -> None: + if completed.returncode != 0: + shown = " ".join(shell_quote(value) for value in completed.command) + raise RuntimeError(f"command failed with exit code {completed.returncode}: {shown}") + + +def write_package(root: Path, name: str, dependencies: Sequence[str]) -> None: + root.mkdir(parents=True, exist_ok=True) + lines = [ + "[package]", + f'org = "{ORG}"', + f'name = "{name}"', + f'version = "{VERSION}"', + 'description = "recursive install e2e fixture"', + "", + "[package.repository]", + 'vcs = "git"', + 'url = "https://github.com/zed-pkg-test/zed-pkg-e2e"', + ] + if dependencies: + lines.extend(("", "[dependencies]")) + for dependency in dependencies: + lines.append(f'"{ORG}/{dependency}" = "={VERSION}"') + (root / ".zpkg.toml").write_text("\n".join(lines) + "\n", encoding="utf-8") + (root / "payload.txt").write_text(f"{ORG}/{name}@{VERSION}\n", encoding="utf-8") + + +def write_consumer(root: Path) -> None: + root.mkdir(parents=True, exist_ok=True) + (root / ".zpkg.toml").write_text( + "\n".join( + [ + "[package]", + 'org = "recursive-consumer"', + f'name = "{root.name}"', + 'version = "0.1.0"', + "", + "[package.repository]", + 'vcs = "git"', + 'url = "https://example.invalid/recursive-consumer"', + "", + "[dependencies]", + f'"{ORG}/root" = "={VERSION}"', + "", + ] + ), + encoding="utf-8", + ) + + +def zed_command(zed: Path, registry: Path, home: Path, *args: str) -> list[str | Path]: + return [ + zed, + "--registry", + f"file://{registry}", + "--home", + home, + *args, + ] + + +def assert_lock_graph(consumer: Path) -> None: + with (consumer / ".zpkg.lock").open("rb") as handle: + document = tomllib.load(handle) + packages = document.get("package") or [] + actual = {f"{package['org']}/{package['name']}" for package in packages} + expected = {f"{ORG}/{name}" for name in PACKAGE_GRAPH} + if actual != expected: + raise AssertionError(f"lock graph mismatch: expected {sorted(expected)}, got {sorted(actual)}") + + +def assert_symlink_graph(consumer: Path, store_root: Path) -> dict[str, Path]: + targets: dict[str, Path] = {} + for name in PACKAGE_GRAPH: + installed = consumer / "zed_modules" / ORG / name + if not installed.is_symlink(): + raise AssertionError(f"default install did not symlink {installed}") + target = installed.resolve(strict=True) + try: + target.relative_to(store_root) + except ValueError as error: + raise AssertionError(f"{installed} resolves outside the shared store: {target}") from error + if (target / "payload.txt").read_text(encoding="utf-8") != f"{ORG}/{name}@{VERSION}\n": + raise AssertionError(f"unexpected payload for {installed}") + targets[name] = target + return targets + + +def parse_prefetch(output: str) -> tuple[int, int, int]: + matches = list(PREFETCH_RE.finditer(output)) + if len(matches) != 1: + raise AssertionError(f"expected one recursive prefetch summary, got {len(matches)}\n{output}") + match = matches[0] + return tuple(int(match.group(field)) for field in ("resolved", "concurrency", "downloaded")) + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("--zed", type=Path, required=True) + parser.add_argument("--work-root", type=Path, required=True) + args = parser.parse_args() + + zed = args.zed.resolve() + root = args.work_root.resolve() + if not zed.is_file(): + raise RuntimeError(f"zed binary does not exist: {zed}") + if root.exists(): + raise RuntimeError(f"work root must be fresh: {root}") + + registry = root / "registry" + publish_home = root / "publish-home" + shared_home = root / "shared-home" + sources = root / "sources" + consumers = [root / "consumer-a", root / "consumer-b"] + registry.mkdir(parents=True) + + environment = os.environ.copy() + environment.update( + { + "CI": "true", + "GIT_TERMINAL_PROMPT": "0", + "RUST_BACKTRACE": "1", + "ZED_PKG_INSTALL_CONCURRENCY": "5", + } + ) + + for name, dependencies in PACKAGE_GRAPH.items(): + package_root = sources / name + write_package(package_root, name, dependencies) + completed = run( + zed_command( + zed, + registry, + publish_home, + "publish", + "--skip-vcs-checks", + ), + cwd=package_root, + env=environment, + ) + require_success(completed) + + for consumer in consumers: + write_consumer(consumer) + + start = Barrier(3) + + def install(consumer: Path) -> Completed: + start.wait() + return run( + zed_command( + zed, + registry, + shared_home, + "install", + "--adapter", + "none", + ), + cwd=consumer, + env=environment, + ) + + with ThreadPoolExecutor(max_workers=2, thread_name_prefix="zed-e2e") as executor: + futures = [executor.submit(install, consumer) for consumer in consumers] + start.wait() + completed_installs = [future.result(timeout=240) for future in futures] + + for completed in completed_installs: + require_success(completed) + + summaries = [parse_prefetch(completed.output) for completed in completed_installs] + if any(resolved != len(PACKAGE_GRAPH) for resolved, _, _ in summaries): + raise AssertionError(f"each recursive resolver must discover four packages: {summaries}") + if any(concurrency != 5 for _, concurrency, _ in summaries): + raise AssertionError(f"installer did not honor five-worker concurrency: {summaries}") + if sum(downloaded for _, _, downloaded in summaries) != len(PACKAGE_GRAPH): + raise AssertionError( + "shared-home installs did not deduplicate downloads across processes: " + f"{summaries}" + ) + + first_targets: dict[str, Path] | None = None + store_root = (shared_home / "store").resolve() + for consumer in consumers: + assert_lock_graph(consumer) + targets = assert_symlink_graph(consumer, store_root) + if first_targets is None: + first_targets = targets + elif targets != first_targets: + raise AssertionError( + f"consumers did not share identical store entries: {first_targets} != {targets}" + ) + + cache_files = sorted((shared_home / "cache").glob("*.tar.gz")) + if len(cache_files) != len(PACKAGE_GRAPH): + raise AssertionError(f"expected four cached artifacts, got {cache_files}") + temporary_downloads = list((shared_home / "cache").glob("**/artifact.download")) + if temporary_downloads: + raise AssertionError(f"temporary downloads leaked into the cache: {temporary_downloads}") + + lock_files = sorted((shared_home / "locks").glob("artifact-*.lock")) + if len(lock_files) != len(PACKAGE_GRAPH): + raise AssertionError(f"expected one artifact lock file per hash, got {lock_files}") + + print("\nrecursive install e2e passed", flush=True) + print(f"prefetch summaries: {summaries}", flush=True) + print(f"shared store: {store_root}", flush=True) + return 0 + + +if __name__ == "__main__": + try: + raise SystemExit(main()) + except Exception as error: # noqa: BLE001 - CI entry point must print context. + print(f"error: {error}", file=sys.stderr, flush=True) + raise From 93b4b39d975706fa02b742c9d87587caa43986c1 Mon Sep 17 00:00:00 2001 From: Alexander Mills Date: Mon, 3 Aug 2026 01:03:06 -0500 Subject: [PATCH 02/61] ci: run recursive install graph contract --- .github/workflows/recursive-installs.yml | 126 +++++++++++++++++++++++ 1 file changed, 126 insertions(+) create mode 100644 .github/workflows/recursive-installs.yml diff --git a/.github/workflows/recursive-installs.yml b/.github/workflows/recursive-installs.yml new file mode 100644 index 00000000..d2b2befe --- /dev/null +++ b/.github/workflows/recursive-installs.yml @@ -0,0 +1,126 @@ +name: recursive install graph + +on: + pull_request: + push: + branches: [main] + workflow_dispatch: + inputs: + zed_cli_ref: + description: Immutable zed-cli commit to test + required: false + default: bbca23825fd163faf4acd885009f147b08a3a3eb + +permissions: + contents: read + +concurrency: + group: recursive-install-graph-${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + +defaults: + run: + shell: bash + +env: + CARGO_TERM_COLOR: always + RUST_BACKTRACE: "1" + ZED_CLI_REF: ${{ github.event.inputs.zed_cli_ref || 'bbca23825fd163faf4acd885009f147b08a3a3eb' }} + ZED_INTERFACES_REF: dc0e0a0620b9462817950b552d3d334a184b1cb1 + +jobs: + recursive-install-e2e: + name: Recursive graph and shared-home lock + runs-on: ubuntu-24.04 + timeout-minutes: 30 + steps: + - name: Require immutable component pins + run: | + set -euo pipefail + [[ "$ZED_CLI_REF" =~ ^[0-9a-f]{40}$ ]] + [[ "$ZED_INTERFACES_REF" =~ ^[0-9a-f]{40}$ ]] + + - name: Check out e2e harness + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + path: harness + fetch-depth: 1 + persist-credentials: false + show-progress: false + + - name: Check out zed-cli + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + repository: zed-pkg/zed-cli + ref: ${{ env.ZED_CLI_REF }} + path: zed-cli + fetch-depth: 1 + persist-credentials: false + show-progress: false + + - name: Check out zed-interfaces sibling + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + repository: zed-pkg/zed-interfaces + ref: ${{ env.ZED_INTERFACES_REF }} + path: zed-interfaces + fetch-depth: 1 + persist-credentials: false + show-progress: false + + - name: Install the declared Rust toolchain + run: | + set -euo pipefail + rustup toolchain install stable --profile minimal --component rustfmt + rustup default stable + rustc --version --verbose + cargo --version --verbose + + - name: Format and unit-test recursive installer + run: | + set -euo pipefail + cargo fmt --all --check --manifest-path zed-cli/Cargo.toml + cargo test --locked --manifest-path zed-cli/Cargo.toml --lib install_graph::tests + + - name: Build immutable zed binary + run: | + set -euo pipefail + cargo build --locked --release --manifest-path zed-cli/Cargo.toml --bin zed + install -Dm755 zed-cli/target/release/zed "$RUNNER_TEMP/zed-bin/zed" + "$RUNNER_TEMP/zed-bin/zed" --version + + - name: Run recursive install and lock contract + env: + WORK_ROOT: ${{ runner.temp }}/zed-recursive-install + run: | + set -euo pipefail + test ! -e "$WORK_ROOT" + python3 harness/scripts/recursive_installs.py \ + --zed "$RUNNER_TEMP/zed-bin/zed" \ + --work-root "$WORK_ROOT" + + - name: Collect failure diagnostics + if: failure() + env: + WORK_ROOT: ${{ runner.temp }}/zed-recursive-install + run: | + set +e + mkdir -p "$WORK_ROOT/diagnostics" + find "$WORK_ROOT" -maxdepth 9 -printf '%y %P -> %l\n' \ + | LC_ALL=C sort > "$WORK_ROOT/diagnostics/work-tree.txt" 2>&1 + find "$WORK_ROOT/registry" -maxdepth 9 -type f -print0 2>/dev/null \ + | sort -z | xargs -0 -r sha256sum \ + > "$WORK_ROOT/diagnostics/registry-sha256.txt" 2>&1 + find "$WORK_ROOT/shared-home" -maxdepth 9 -type f -print0 2>/dev/null \ + | sort -z | xargs -0 -r sha256sum \ + > "$WORK_ROOT/diagnostics/shared-home-sha256.txt" 2>&1 + + - name: Upload failure diagnostics + if: failure() + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: recursive-install-diagnostics-${{ github.run_id }}-${{ github.run_attempt }} + path: ${{ runner.temp }}/zed-recursive-install/diagnostics + if-no-files-found: warn + retention-days: 14 + include-hidden-files: false From 6646359d449a8efe82935f72173c5eb4269d701a Mon Sep 17 00:00:00 2001 From: Alexander Mills Date: Mon, 3 Aug 2026 01:10:22 -0500 Subject: [PATCH 03/61] ci: advance recursive installer pin and preserve diagnostics --- .github/workflows/recursive-installs.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/recursive-installs.yml b/.github/workflows/recursive-installs.yml index d2b2befe..0c7d2c9e 100644 --- a/.github/workflows/recursive-installs.yml +++ b/.github/workflows/recursive-installs.yml @@ -9,7 +9,7 @@ on: zed_cli_ref: description: Immutable zed-cli commit to test required: false - default: bbca23825fd163faf4acd885009f147b08a3a3eb + default: 99ca21a363b38f42a3cd42e1021049019753c1c6 permissions: contents: read @@ -25,7 +25,7 @@ defaults: env: CARGO_TERM_COLOR: always RUST_BACKTRACE: "1" - ZED_CLI_REF: ${{ github.event.inputs.zed_cli_ref || 'bbca23825fd163faf4acd885009f147b08a3a3eb' }} + ZED_CLI_REF: ${{ github.event.inputs.zed_cli_ref || '99ca21a363b38f42a3cd42e1021049019753c1c6' }} ZED_INTERFACES_REF: dc0e0a0620b9462817950b552d3d334a184b1cb1 jobs: @@ -114,6 +114,7 @@ jobs: find "$WORK_ROOT/shared-home" -maxdepth 9 -type f -print0 2>/dev/null \ | sort -z | xargs -0 -r sha256sum \ > "$WORK_ROOT/diagnostics/shared-home-sha256.txt" 2>&1 + true - name: Upload failure diagnostics if: failure() From 4916453854d2d3e5fad2e7d85665862d06087441 Mon Sep 17 00:00:00 2001 From: Alexander Mills Date: Mon, 3 Aug 2026 01:17:28 -0500 Subject: [PATCH 04/61] ci: validate expanded recursive installer suite --- .github/workflows/recursive-installs.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/recursive-installs.yml b/.github/workflows/recursive-installs.yml index 0c7d2c9e..182cc733 100644 --- a/.github/workflows/recursive-installs.yml +++ b/.github/workflows/recursive-installs.yml @@ -9,7 +9,7 @@ on: zed_cli_ref: description: Immutable zed-cli commit to test required: false - default: 99ca21a363b38f42a3cd42e1021049019753c1c6 + default: c8be1792c12f2d894d28b7fa0aaa0c396052edc8 permissions: contents: read @@ -25,7 +25,7 @@ defaults: env: CARGO_TERM_COLOR: always RUST_BACKTRACE: "1" - ZED_CLI_REF: ${{ github.event.inputs.zed_cli_ref || '99ca21a363b38f42a3cd42e1021049019753c1c6' }} + ZED_CLI_REF: ${{ github.event.inputs.zed_cli_ref || 'c8be1792c12f2d894d28b7fa0aaa0c396052edc8' }} ZED_INTERFACES_REF: dc0e0a0620b9462817950b552d3d334a184b1cb1 jobs: From ea39e366776c942d6f656f4dd1337dd08ec2b20b Mon Sep 17 00:00:00 2001 From: Alexander Mills Date: Mon, 3 Aug 2026 01:20:50 -0500 Subject: [PATCH 05/61] ci: validate formatted expanded recursive installer suite --- .github/workflows/recursive-installs.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/recursive-installs.yml b/.github/workflows/recursive-installs.yml index 182cc733..d13f2bea 100644 --- a/.github/workflows/recursive-installs.yml +++ b/.github/workflows/recursive-installs.yml @@ -9,7 +9,7 @@ on: zed_cli_ref: description: Immutable zed-cli commit to test required: false - default: c8be1792c12f2d894d28b7fa0aaa0c396052edc8 + default: 58d688b1a1bfbbb9b1ff551a303b7360a8ca94be permissions: contents: read @@ -25,7 +25,7 @@ defaults: env: CARGO_TERM_COLOR: always RUST_BACKTRACE: "1" - ZED_CLI_REF: ${{ github.event.inputs.zed_cli_ref || 'c8be1792c12f2d894d28b7fa0aaa0c396052edc8' }} + ZED_CLI_REF: ${{ github.event.inputs.zed_cli_ref || '58d688b1a1bfbbb9b1ff551a303b7360a8ca94be' }} ZED_INTERFACES_REF: dc0e0a0620b9462817950b552d3d334a184b1cb1 jobs: From b81d163fa2baf9fd2adabee369dbb13179ccf841 Mon Sep 17 00:00:00 2001 From: Alexander Mills Date: Mon, 3 Aug 2026 01:36:26 -0500 Subject: [PATCH 06/61] test: cover recursive zed add and remove installs --- scripts/recursive_installs.py | 207 ++++++++++++++++++++++++++-------- 1 file changed, 162 insertions(+), 45 deletions(-) diff --git a/scripts/recursive_installs.py b/scripts/recursive_installs.py index 125c1b8e..53932921 100644 --- a/scripts/recursive_installs.py +++ b/scripts/recursive_installs.py @@ -93,27 +93,24 @@ def write_package(root: Path, name: str, dependencies: Sequence[str]) -> None: (root / "payload.txt").write_text(f"{ORG}/{name}@{VERSION}\n", encoding="utf-8") -def write_consumer(root: Path) -> None: +def write_consumer(root: Path, dependencies: Sequence[str]) -> None: root.mkdir(parents=True, exist_ok=True) - (root / ".zpkg.toml").write_text( - "\n".join( - [ - "[package]", - 'org = "recursive-consumer"', - f'name = "{root.name}"', - 'version = "0.1.0"', - "", - "[package.repository]", - 'vcs = "git"', - 'url = "https://example.invalid/recursive-consumer"', - "", - "[dependencies]", - f'"{ORG}/root" = "={VERSION}"', - "", - ] - ), - encoding="utf-8", - ) + lines = [ + "[package]", + 'org = "recursive-consumer"', + f'name = "{root.name}"', + 'version = "0.1.0"', + "", + "[package.repository]", + 'vcs = "git"', + 'url = "https://example.invalid/recursive-consumer"', + "", + "[dependencies]", + ] + for dependency in dependencies: + lines.append(f'"{ORG}/{dependency}" = "={VERSION}"') + lines.append("") + (root / ".zpkg.toml").write_text("\n".join(lines), encoding="utf-8") def zed_command(zed: Path, registry: Path, home: Path, *args: str) -> list[str | Path]: @@ -127,19 +124,40 @@ def zed_command(zed: Path, registry: Path, home: Path, *args: str) -> list[str | ] -def assert_lock_graph(consumer: Path) -> None: +def package_keys(names: Sequence[str]) -> set[str]: + return {f"{ORG}/{name}" for name in names} + + +def assert_manifest_dependencies(consumer: Path, expected_names: Sequence[str]) -> None: + with (consumer / ".zpkg.toml").open("rb") as handle: + document = tomllib.load(handle) + actual = set((document.get("dependencies") or {}).keys()) + expected = package_keys(expected_names) + if actual != expected: + raise AssertionError( + f"manifest dependency mismatch: expected {sorted(expected)}, got {sorted(actual)}" + ) + + +def assert_lock_packages(consumer: Path, expected_names: Sequence[str]) -> None: with (consumer / ".zpkg.lock").open("rb") as handle: document = tomllib.load(handle) packages = document.get("package") or [] actual = {f"{package['org']}/{package['name']}" for package in packages} - expected = {f"{ORG}/{name}" for name in PACKAGE_GRAPH} + expected = package_keys(expected_names) if actual != expected: - raise AssertionError(f"lock graph mismatch: expected {sorted(expected)}, got {sorted(actual)}") + raise AssertionError( + f"lock graph mismatch: expected {sorted(expected)}, got {sorted(actual)}" + ) -def assert_symlink_graph(consumer: Path, store_root: Path) -> dict[str, Path]: +def assert_symlink_packages( + consumer: Path, + store_root: Path, + expected_names: Sequence[str], +) -> dict[str, Path]: targets: dict[str, Path] = {} - for name in PACKAGE_GRAPH: + for name in expected_names: installed = consumer / "zed_modules" / ORG / name if not installed.is_symlink(): raise AssertionError(f"default install did not symlink {installed}") @@ -147,19 +165,64 @@ def assert_symlink_graph(consumer: Path, store_root: Path) -> dict[str, Path]: try: target.relative_to(store_root) except ValueError as error: - raise AssertionError(f"{installed} resolves outside the shared store: {target}") from error + raise AssertionError( + f"{installed} resolves outside the shared store: {target}" + ) from error if (target / "payload.txt").read_text(encoding="utf-8") != f"{ORG}/{name}@{VERSION}\n": raise AssertionError(f"unexpected payload for {installed}") targets[name] = target return targets +def assert_not_materialized(consumer: Path, names: Sequence[str]) -> None: + for name in names: + installed = consumer / "zed_modules" / ORG / name + if installed.exists() or installed.is_symlink(): + raise AssertionError(f"unexpected materialized package {installed}") + + +def assert_artifact_home(home: Path, expected_count: int) -> None: + cache_files = sorted((home / "cache").glob("*.tar.gz")) + if len(cache_files) != expected_count: + raise AssertionError( + f"expected {expected_count} cached artifacts in {home}, got {cache_files}" + ) + temporary_downloads = list((home / "cache").glob("**/artifact.download")) + if temporary_downloads: + raise AssertionError( + f"temporary downloads leaked into {home}: {temporary_downloads}" + ) + lock_files = sorted((home / "locks").glob("artifact-*.lock")) + if len(lock_files) != expected_count: + raise AssertionError( + f"expected {expected_count} artifact lock files in {home}, got {lock_files}" + ) + + def parse_prefetch(output: str) -> tuple[int, int, int]: matches = list(PREFETCH_RE.finditer(output)) if len(matches) != 1: - raise AssertionError(f"expected one recursive prefetch summary, got {len(matches)}\n{output}") + raise AssertionError( + f"expected one recursive prefetch summary, got {len(matches)}\n{output}" + ) match = matches[0] - return tuple(int(match.group(field)) for field in ("resolved", "concurrency", "downloaded")) + return tuple( + int(match.group(field)) + for field in ("resolved", "concurrency", "downloaded") + ) + + +def assert_prefetch( + completed: Completed, + *, + resolved: int, + downloaded: int, +) -> tuple[int, int, int]: + summary = parse_prefetch(completed.output) + expected = (resolved, 5, downloaded) + if summary != expected: + raise AssertionError(f"prefetch summary mismatch: expected {expected}, got {summary}") + return summary def main() -> int: @@ -178,8 +241,12 @@ def main() -> int: registry = root / "registry" publish_home = root / "publish-home" shared_home = root / "shared-home" + add_home = root / "add-home" + remove_home = root / "remove-home" sources = root / "sources" consumers = [root / "consumer-a", root / "consumer-b"] + add_consumer = root / "consumer-add" + remove_consumer = root / "consumer-remove" registry.mkdir(parents=True) environment = os.environ.copy() @@ -209,7 +276,7 @@ def main() -> int: require_success(completed) for consumer in consumers: - write_consumer(consumer) + write_consumer(consumer, ("root",)) start = Barrier(3) @@ -238,9 +305,13 @@ def install(consumer: Path) -> Completed: summaries = [parse_prefetch(completed.output) for completed in completed_installs] if any(resolved != len(PACKAGE_GRAPH) for resolved, _, _ in summaries): - raise AssertionError(f"each recursive resolver must discover four packages: {summaries}") + raise AssertionError( + f"each recursive resolver must discover four packages: {summaries}" + ) if any(concurrency != 5 for _, concurrency, _ in summaries): - raise AssertionError(f"installer did not honor five-worker concurrency: {summaries}") + raise AssertionError( + f"installer did not honor five-worker concurrency: {summaries}" + ) if sum(downloaded for _, _, downloaded in summaries) != len(PACKAGE_GRAPH): raise AssertionError( "shared-home installs did not deduplicate downloads across processes: " @@ -249,29 +320,75 @@ def install(consumer: Path) -> Completed: first_targets: dict[str, Path] | None = None store_root = (shared_home / "store").resolve() + all_packages = tuple(PACKAGE_GRAPH) for consumer in consumers: - assert_lock_graph(consumer) - targets = assert_symlink_graph(consumer, store_root) + assert_manifest_dependencies(consumer, ("root",)) + assert_lock_packages(consumer, all_packages) + targets = assert_symlink_packages(consumer, store_root, all_packages) if first_targets is None: first_targets = targets elif targets != first_targets: raise AssertionError( f"consumers did not share identical store entries: {first_targets} != {targets}" ) - - cache_files = sorted((shared_home / "cache").glob("*.tar.gz")) - if len(cache_files) != len(PACKAGE_GRAPH): - raise AssertionError(f"expected four cached artifacts, got {cache_files}") - temporary_downloads = list((shared_home / "cache").glob("**/artifact.download")) - if temporary_downloads: - raise AssertionError(f"temporary downloads leaked into the cache: {temporary_downloads}") - - lock_files = sorted((shared_home / "locks").glob("artifact-*.lock")) - if len(lock_files) != len(PACKAGE_GRAPH): - raise AssertionError(f"expected one artifact lock file per hash, got {lock_files}") + assert_artifact_home(shared_home, len(PACKAGE_GRAPH)) + + # `zed add` installs against an in-memory proposed manifest before writing + # it. This must enter the same recursive prefetch facade as a normal install. + write_consumer(add_consumer, ()) + add_completed = run( + zed_command( + zed, + registry, + add_home, + "add", + f"{ORG}/root@={VERSION}", + ), + cwd=add_consumer, + env=environment, + ) + require_success(add_completed) + add_summary = assert_prefetch( + add_completed, + resolved=len(PACKAGE_GRAPH), + downloaded=len(PACKAGE_GRAPH), + ) + assert_manifest_dependencies(add_consumer, ("root",)) + assert_lock_packages(add_consumer, all_packages) + assert_symlink_packages(add_consumer, (add_home / "store").resolve(), all_packages) + assert_artifact_home(add_home, len(PACKAGE_GRAPH)) + + # `zed remove` also installs an in-memory proposed manifest. Starting with + # root + left and removing root leaves left -> leaf, so exactly two packages + # should be recursively prefetched and materialized. + write_consumer(remove_consumer, ("root", "left")) + remove_completed = run( + zed_command( + zed, + registry, + remove_home, + "remove", + f"{ORG}/root", + ), + cwd=remove_consumer, + env=environment, + ) + require_success(remove_completed) + remove_summary = assert_prefetch(remove_completed, resolved=2, downloaded=2) + assert_manifest_dependencies(remove_consumer, ("left",)) + assert_lock_packages(remove_consumer, ("left", "leaf")) + assert_symlink_packages( + remove_consumer, + (remove_home / "store").resolve(), + ("left", "leaf"), + ) + assert_not_materialized(remove_consumer, ("root", "right")) + assert_artifact_home(remove_home, 2) print("\nrecursive install e2e passed", flush=True) - print(f"prefetch summaries: {summaries}", flush=True) + print(f"concurrent install summaries: {summaries}", flush=True) + print(f"add summary: {add_summary}", flush=True) + print(f"remove summary: {remove_summary}", flush=True) print(f"shared store: {store_root}", flush=True) return 0 From dbc3290f8d30edbf04d9d56e2aa77547a1337ca0 Mon Sep 17 00:00:00 2001 From: Alexander Mills Date: Mon, 3 Aug 2026 01:37:05 -0500 Subject: [PATCH 07/61] ci: validate recursive add and remove paths --- .github/workflows/recursive-installs.yml | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/.github/workflows/recursive-installs.yml b/.github/workflows/recursive-installs.yml index d13f2bea..dfb1d254 100644 --- a/.github/workflows/recursive-installs.yml +++ b/.github/workflows/recursive-installs.yml @@ -9,7 +9,7 @@ on: zed_cli_ref: description: Immutable zed-cli commit to test required: false - default: 58d688b1a1bfbbb9b1ff551a303b7360a8ca94be + default: 0b1e11ac2ae0af75fa31e969f8a58dda80be329e permissions: contents: read @@ -25,7 +25,7 @@ defaults: env: CARGO_TERM_COLOR: always RUST_BACKTRACE: "1" - ZED_CLI_REF: ${{ github.event.inputs.zed_cli_ref || '58d688b1a1bfbbb9b1ff551a303b7360a8ca94be' }} + ZED_CLI_REF: ${{ github.event.inputs.zed_cli_ref || '0b1e11ac2ae0af75fa31e969f8a58dda80be329e' }} ZED_INTERFACES_REF: dc0e0a0620b9462817950b552d3d334a184b1cb1 jobs: @@ -79,8 +79,10 @@ jobs: - name: Format and unit-test recursive installer run: | set -euo pipefail + python3 -m py_compile harness/scripts/recursive_installs.py cargo fmt --all --check --manifest-path zed-cli/Cargo.toml cargo test --locked --manifest-path zed-cli/Cargo.toml --lib install_graph::tests + cargo test --locked --manifest-path zed-cli/Cargo.toml --lib config::tests - name: Build immutable zed binary run: | @@ -111,9 +113,9 @@ jobs: find "$WORK_ROOT/registry" -maxdepth 9 -type f -print0 2>/dev/null \ | sort -z | xargs -0 -r sha256sum \ > "$WORK_ROOT/diagnostics/registry-sha256.txt" 2>&1 - find "$WORK_ROOT/shared-home" -maxdepth 9 -type f -print0 2>/dev/null \ + find "$WORK_ROOT" -path '*/cache/*.tar.gz' -type f -print0 2>/dev/null \ | sort -z | xargs -0 -r sha256sum \ - > "$WORK_ROOT/diagnostics/shared-home-sha256.txt" 2>&1 + > "$WORK_ROOT/diagnostics/all-cache-sha256.txt" 2>&1 true - name: Upload failure diagnostics From 4227fb277f294b2f384917541ffbdbc544607c6a Mon Sep 17 00:00:00 2001 From: Alexander Mills Date: Mon, 3 Aug 2026 02:02:16 -0500 Subject: [PATCH 08/61] test: stress recursive installs across four processes --- scripts/recursive_installs_stress.py | 421 +++++++++++++++++++++++++++ 1 file changed, 421 insertions(+) create mode 100644 scripts/recursive_installs_stress.py diff --git a/scripts/recursive_installs_stress.py b/scripts/recursive_installs_stress.py new file mode 100644 index 00000000..8b109af2 --- /dev/null +++ b/scripts/recursive_installs_stress.py @@ -0,0 +1,421 @@ +#!/usr/bin/env python3 +"""Stress recursive zed installs across processes and a manifestless frozen replay.""" + +from __future__ import annotations + +import argparse +import os +import re +import shutil +import subprocess +import sys +import tomllib +from concurrent.futures import ThreadPoolExecutor +from dataclasses import dataclass +from pathlib import Path +from threading import Barrier +from typing import Mapping, Sequence + +ORG = "zed-recursive-stress" +VERSION = "1.0.0" +LEAVES = tuple(f"leaf-{index:02d}" for index in range(12)) +ROOT = "root" +ALL_PACKAGES = (*LEAVES, ROOT) +PREFETCH_RE = re.compile( + r"recursive install prefetch: (?P\d+) package\(s\), " + r"up to (?P\d+) concurrent, (?P\d+) downloaded" +) + + +@dataclass(frozen=True) +class Completed: + command: tuple[str, ...] + returncode: int + output: str + + +def shell_quote(value: str) -> str: + if re.fullmatch(r"[A-Za-z0-9_./:@%+=,-]+", value): + return value + return "'" + value.replace("'", "'\"'\"'") + "'" + + +def run( + command: Sequence[str | Path], + *, + cwd: Path | None = None, + env: Mapping[str, str] | None = None, +) -> Completed: + argv = tuple(str(value) for value in command) + shown = " ".join(shell_quote(value) for value in argv) + print(f"\n$ (cd {cwd or Path.cwd()} && {shown})", flush=True) + completed = subprocess.run( + argv, + cwd=cwd, + env=dict(env) if env is not None else None, + text=True, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + check=False, + timeout=240, + ) + if completed.stdout: + print(completed.stdout, end="", flush=True) + return Completed(argv, completed.returncode, completed.stdout) + + +def require_success(completed: Completed) -> None: + if completed.returncode != 0: + shown = " ".join(shell_quote(value) for value in completed.command) + raise RuntimeError(f"command failed with exit code {completed.returncode}: {shown}") + + +def write_package(root: Path, name: str, dependencies: Sequence[str]) -> None: + root.mkdir(parents=True, exist_ok=True) + lines = [ + "[package]", + f'org = "{ORG}"', + f'name = "{name}"', + f'version = "{VERSION}"', + 'description = "recursive install stress fixture"', + "", + "[package.repository]", + 'vcs = "git"', + 'url = "https://github.com/zed-pkg-test/zed-pkg-e2e"', + ] + if dependencies: + lines.extend(("", "[dependencies]")) + for dependency in dependencies: + lines.append(f'"{ORG}/{dependency}" = "={VERSION}"') + (root / ".zpkg.toml").write_text("\n".join(lines) + "\n", encoding="utf-8") + (root / "payload.txt").write_text(f"{ORG}/{name}@{VERSION}\n", encoding="utf-8") + + +def write_consumer(root: Path) -> None: + root.mkdir(parents=True, exist_ok=True) + (root / ".zpkg.toml").write_text( + "\n".join( + ( + "[package]", + 'org = "recursive-stress-consumer"', + f'name = "{root.name}"', + 'version = "0.1.0"', + "", + "[package.repository]", + 'vcs = "git"', + 'url = "https://example.invalid/recursive-stress-consumer"', + "", + "[dependencies]", + f'"{ORG}/{ROOT}" = "={VERSION}"', + "", + ) + ), + encoding="utf-8", + ) + + +def zed_command(zed: Path, registry: Path, home: Path, *args: str) -> list[str | Path]: + return [ + zed, + "--registry", + f"file://{registry}", + "--home", + home, + *args, + ] + + +def package_keys(names: Sequence[str]) -> set[str]: + return {f"{ORG}/{name}" for name in names} + + +def assert_manifest_root_only(consumer: Path) -> None: + with (consumer / ".zpkg.toml").open("rb") as handle: + document = tomllib.load(handle) + actual = set((document.get("dependencies") or {}).keys()) + expected = package_keys((ROOT,)) + if actual != expected: + raise AssertionError( + f"manifest dependency mismatch: expected {sorted(expected)}, got {sorted(actual)}" + ) + + +def assert_lock_packages(consumer: Path, expected_names: Sequence[str]) -> None: + with (consumer / ".zpkg.lock").open("rb") as handle: + document = tomllib.load(handle) + packages = document.get("package") or [] + actual = {f"{package['org']}/{package['name']}" for package in packages} + expected = package_keys(expected_names) + if actual != expected: + raise AssertionError( + f"lock graph mismatch: expected {sorted(expected)}, got {sorted(actual)}" + ) + + +def assert_symlink_packages( + consumer: Path, + store_root: Path, + expected_names: Sequence[str], +) -> dict[str, Path]: + targets: dict[str, Path] = {} + for name in expected_names: + installed = consumer / "zed_modules" / ORG / name + if not installed.is_symlink(): + raise AssertionError(f"default install did not symlink {installed}") + target = installed.resolve(strict=True) + try: + target.relative_to(store_root) + except ValueError as error: + raise AssertionError( + f"{installed} resolves outside the shared store: {target}" + ) from error + payload = (target / "payload.txt").read_text(encoding="utf-8") + if payload != f"{ORG}/{name}@{VERSION}\n": + raise AssertionError(f"unexpected payload for {installed}: {payload!r}") + targets[name] = target + return targets + + +def assert_artifact_home(home: Path, expected_count: int) -> None: + cache = home / "cache" + cache_files = sorted(cache.glob("*.tar.gz")) + if len(cache_files) != expected_count: + raise AssertionError( + f"expected {expected_count} cached artifacts in {home}, got {cache_files}" + ) + leaked_directories = sorted(path for path in cache.iterdir() if path.is_dir()) + if leaked_directories: + raise AssertionError(f"temporary cache directories leaked: {leaked_directories}") + temporary_downloads = list(cache.glob("**/artifact.download")) + if temporary_downloads: + raise AssertionError(f"temporary downloads leaked: {temporary_downloads}") + lock_files = sorted((home / "locks").glob("artifact-*.lock")) + if len(lock_files) != expected_count: + raise AssertionError( + f"expected {expected_count} artifact locks in {home}, got {lock_files}" + ) + + +def parse_prefetch(output: str) -> tuple[int, int, int]: + matches = list(PREFETCH_RE.finditer(output)) + if len(matches) != 1: + raise AssertionError( + f"expected one recursive prefetch summary, got {len(matches)}\n{output}" + ) + match = matches[0] + return tuple( + int(match.group(field)) + for field in ("resolved", "concurrency", "downloaded") + ) + + +def assert_prefetch( + completed: Completed, + *, + resolved: int, + downloaded: int, +) -> tuple[int, int, int]: + summary = parse_prefetch(completed.output) + expected = (resolved, 5, downloaded) + if summary != expected: + raise AssertionError(f"prefetch summary mismatch: expected {expected}, got {summary}") + return summary + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("--zed", type=Path, required=True) + parser.add_argument("--work-root", type=Path, required=True) + args = parser.parse_args() + + zed = args.zed.resolve() + root = args.work_root.resolve() + if not zed.is_file(): + raise RuntimeError(f"zed binary does not exist: {zed}") + if root.exists(): + raise RuntimeError(f"work root must be fresh: {root}") + + registry = root / "registry" + publish_home = root / "publish-home" + shared_home = root / "shared-home" + frozen_home = root / "frozen-home" + sources = root / "sources" + consumers = [root / f"consumer-{index}" for index in range(4)] + frozen_consumer = root / "consumer-frozen" + registry.mkdir(parents=True) + + environment = os.environ.copy() + environment.update( + { + "CI": "true", + "GIT_TERMINAL_PROMPT": "0", + "RUST_BACKTRACE": "1", + "ZED_PKG_INSTALL_CONCURRENCY": "5", + } + ) + + for name in LEAVES: + package_root = sources / name + write_package(package_root, name, ()) + completed = run( + zed_command( + zed, + registry, + publish_home, + "publish", + "--skip-vcs-checks", + ), + cwd=package_root, + env=environment, + ) + require_success(completed) + + root_package = sources / ROOT + write_package(root_package, ROOT, LEAVES) + root_publish = run( + zed_command( + zed, + registry, + publish_home, + "publish", + "--skip-vcs-checks", + ), + cwd=root_package, + env=environment, + ) + require_success(root_publish) + + for consumer in consumers: + write_consumer(consumer) + + start = Barrier(len(consumers) + 1) + + def install(consumer: Path) -> Completed: + start.wait() + return run( + zed_command( + zed, + registry, + shared_home, + "install", + "--adapter", + "none", + ), + cwd=consumer, + env=environment, + ) + + with ThreadPoolExecutor( + max_workers=len(consumers), + thread_name_prefix="zed-recursive-stress", + ) as executor: + futures = [executor.submit(install, consumer) for consumer in consumers] + start.wait() + installs = [future.result(timeout=300) for future in futures] + + for completed in installs: + require_success(completed) + + summaries = [parse_prefetch(completed.output) for completed in installs] + expected_count = len(ALL_PACKAGES) + if any(resolved != expected_count for resolved, _, _ in summaries): + raise AssertionError(f"every process must resolve the complete graph: {summaries}") + if any(concurrency != 5 for _, concurrency, _ in summaries): + raise AssertionError(f"five-worker configuration was not honored: {summaries}") + if sum(downloaded for _, _, downloaded in summaries) != expected_count: + raise AssertionError( + "four processes did not deduplicate shared-home downloads: " + f"{summaries}" + ) + + first_targets: dict[str, Path] | None = None + shared_store = (shared_home / "store").resolve() + for consumer in consumers: + assert_manifest_root_only(consumer) + assert_lock_packages(consumer, ALL_PACKAGES) + targets = assert_symlink_packages(consumer, shared_store, ALL_PACKAGES) + if first_targets is None: + first_targets = targets + elif targets != first_targets: + raise AssertionError( + f"consumers did not share identical store entries: {first_targets} != {targets}" + ) + assert_artifact_home(shared_home, expected_count) + + # Replay the complete transitive lockfile in a fresh project with no + # manifest. This exercises the lock-only recursive prefetch facade and its + # normal symlink materialization path. + frozen_consumer.mkdir(parents=True) + source_lock = consumers[0] / ".zpkg.lock" + frozen_lock = frozen_consumer / ".zpkg.lock" + shutil.copyfile(source_lock, frozen_lock) + lock_before = frozen_lock.read_bytes() + frozen_install = run( + zed_command( + zed, + registry, + frozen_home, + "install", + "--frozen", + "--allow-no-manifest", + "--adapter", + "none", + ), + cwd=frozen_consumer, + env=environment, + ) + require_success(frozen_install) + frozen_summary = assert_prefetch( + frozen_install, + resolved=expected_count, + downloaded=expected_count, + ) + if (frozen_consumer / ".zpkg.toml").exists(): + raise AssertionError("manifestless frozen install wrote a new manifest") + if frozen_lock.read_bytes() != lock_before: + raise AssertionError("frozen install modified the lockfile") + assert_lock_packages(frozen_consumer, ALL_PACKAGES) + assert_symlink_packages( + frozen_consumer, + (frozen_home / "store").resolve(), + ALL_PACKAGES, + ) + assert_artifact_home(frozen_home, expected_count) + + frozen_warm = run( + zed_command( + zed, + registry, + frozen_home, + "install", + "--frozen", + "--allow-no-manifest", + "--adapter", + "none", + ), + cwd=frozen_consumer, + env=environment, + ) + require_success(frozen_warm) + frozen_warm_summary = assert_prefetch( + frozen_warm, + resolved=expected_count, + downloaded=0, + ) + if frozen_lock.read_bytes() != lock_before: + raise AssertionError("warm frozen install modified the lockfile") + + print("\nrecursive install stress passed", flush=True) + print(f"concurrent process summaries: {summaries}", flush=True) + print(f"frozen summary: {frozen_summary}", flush=True) + print(f"warm frozen summary: {frozen_warm_summary}", flush=True) + print(f"shared store: {shared_store}", flush=True) + return 0 + + +if __name__ == "__main__": + try: + raise SystemExit(main()) + except Exception as error: # noqa: BLE001 - CI entry point prints full context. + print(f"error: {error}", file=sys.stderr, flush=True) + raise From 26a3c610c7ff5dd30c9e3e9ae93e8dd116aad3bd Mon Sep 17 00:00:00 2001 From: Alexander Mills Date: Mon, 3 Aug 2026 02:03:09 -0500 Subject: [PATCH 09/61] ci: add five-worker recursive install stress contract --- .../workflows/recursive-installs-stress.yml | 131 ++++++++++++++++++ 1 file changed, 131 insertions(+) create mode 100644 .github/workflows/recursive-installs-stress.yml diff --git a/.github/workflows/recursive-installs-stress.yml b/.github/workflows/recursive-installs-stress.yml new file mode 100644 index 00000000..b8379a7b --- /dev/null +++ b/.github/workflows/recursive-installs-stress.yml @@ -0,0 +1,131 @@ +name: recursive install stress + +on: + pull_request: + push: + branches: [main] + workflow_dispatch: + inputs: + zed_cli_ref: + description: Immutable zed-cli commit to test + required: false + default: f84c36672f0fbc15616f256add197b5ca9cb81fe + +permissions: + contents: read + +concurrency: + group: recursive-install-stress-${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + +defaults: + run: + shell: bash + +env: + CARGO_TERM_COLOR: always + RUST_BACKTRACE: "1" + ZED_CLI_REF: ${{ github.event.inputs.zed_cli_ref || 'f84c36672f0fbc15616f256add197b5ca9cb81fe' }} + ZED_INTERFACES_REF: dc0e0a0620b9462817950b552d3d334a184b1cb1 + +jobs: + recursive-install-stress: + name: Five-worker and four-process stress + runs-on: ubuntu-24.04 + timeout-minutes: 40 + steps: + - name: Require immutable component pins + run: | + set -euo pipefail + [[ "$ZED_CLI_REF" =~ ^[0-9a-f]{40}$ ]] + [[ "$ZED_INTERFACES_REF" =~ ^[0-9a-f]{40}$ ]] + + - name: Check out e2e harness + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + path: harness + fetch-depth: 1 + persist-credentials: false + show-progress: false + + - name: Check out zed-cli + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + repository: zed-pkg/zed-cli + ref: ${{ env.ZED_CLI_REF }} + path: zed-cli + fetch-depth: 1 + persist-credentials: false + show-progress: false + + - name: Check out zed-interfaces sibling + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + repository: zed-pkg/zed-interfaces + ref: ${{ env.ZED_INTERFACES_REF }} + path: zed-interfaces + fetch-depth: 1 + persist-credentials: false + show-progress: false + + - name: Install the declared Rust toolchain + run: | + set -euo pipefail + rustup toolchain install stable --profile minimal --component rustfmt + rustup default stable + rustc --version --verbose + cargo --version --verbose + + - name: Prove five live HTTP artifact transfers + env: + ZED_PKG_INSTALL_CONCURRENCY: "5" + run: | + set -euo pipefail + python3 -m py_compile harness/scripts/recursive_installs_stress.py + cargo fmt --all --check --manifest-path zed-cli/Cargo.toml + cargo test --locked --manifest-path zed-cli/Cargo.toml \ + --test recursive_install_concurrency -- --nocapture + + - name: Build immutable zed binary + run: | + set -euo pipefail + cargo build --locked --release --manifest-path zed-cli/Cargo.toml --bin zed + install -Dm755 zed-cli/target/release/zed "$RUNNER_TEMP/zed-bin/zed" + "$RUNNER_TEMP/zed-bin/zed" --version + + - name: Run four-process and frozen stress contract + env: + WORK_ROOT: ${{ runner.temp }}/zed-recursive-stress + run: | + set -euo pipefail + test ! -e "$WORK_ROOT" + python3 harness/scripts/recursive_installs_stress.py \ + --zed "$RUNNER_TEMP/zed-bin/zed" \ + --work-root "$WORK_ROOT" + + - name: Collect failure diagnostics + if: failure() + env: + WORK_ROOT: ${{ runner.temp }}/zed-recursive-stress + run: | + set +e + mkdir -p "$WORK_ROOT/diagnostics" + find "$WORK_ROOT" -maxdepth 10 -printf '%y %P -> %l\n' \ + | LC_ALL=C sort > "$WORK_ROOT/diagnostics/work-tree.txt" 2>&1 + find "$WORK_ROOT/registry" -maxdepth 10 -type f -print0 2>/dev/null \ + | sort -z | xargs -0 -r sha256sum \ + > "$WORK_ROOT/diagnostics/registry-sha256.txt" 2>&1 + find "$WORK_ROOT" -path '*/cache/*.tar.gz' -type f -print0 2>/dev/null \ + | sort -z | xargs -0 -r sha256sum \ + > "$WORK_ROOT/diagnostics/all-cache-sha256.txt" 2>&1 + true + + - name: Upload failure diagnostics + if: failure() + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: recursive-install-stress-${{ github.run_id }}-${{ github.run_attempt }} + path: ${{ runner.temp }}/zed-recursive-stress/diagnostics + if-no-files-found: warn + retention-days: 14 + include-hidden-files: false From bb96ae414af25e4f96f9d0054fad38980aba0e56 Mon Sep 17 00:00:00 2001 From: Alexander Mills Date: Mon, 3 Aug 2026 02:05:58 -0500 Subject: [PATCH 10/61] ci: test formatted recursive installer head --- .github/workflows/recursive-installs-stress.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/recursive-installs-stress.yml b/.github/workflows/recursive-installs-stress.yml index b8379a7b..58e23ce2 100644 --- a/.github/workflows/recursive-installs-stress.yml +++ b/.github/workflows/recursive-installs-stress.yml @@ -9,7 +9,7 @@ on: zed_cli_ref: description: Immutable zed-cli commit to test required: false - default: f84c36672f0fbc15616f256add197b5ca9cb81fe + default: bceed2a1f21f2515a30c4517f855f3f54fa5a584 permissions: contents: read @@ -25,7 +25,7 @@ defaults: env: CARGO_TERM_COLOR: always RUST_BACKTRACE: "1" - ZED_CLI_REF: ${{ github.event.inputs.zed_cli_ref || 'f84c36672f0fbc15616f256add197b5ca9cb81fe' }} + ZED_CLI_REF: ${{ github.event.inputs.zed_cli_ref || 'bceed2a1f21f2515a30c4517f855f3f54fa5a584' }} ZED_INTERFACES_REF: dc0e0a0620b9462817950b552d3d334a184b1cb1 jobs: From 21e3855cde24a2305e81c6571ceb24180646cd40 Mon Sep 17 00:00:00 2001 From: Alexander Mills Date: Mon, 3 Aug 2026 02:06:32 -0500 Subject: [PATCH 11/61] ci: pin recursive graph checks to latest installer head --- .github/workflows/recursive-installs.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/recursive-installs.yml b/.github/workflows/recursive-installs.yml index dfb1d254..537b5ff8 100644 --- a/.github/workflows/recursive-installs.yml +++ b/.github/workflows/recursive-installs.yml @@ -9,7 +9,7 @@ on: zed_cli_ref: description: Immutable zed-cli commit to test required: false - default: 0b1e11ac2ae0af75fa31e969f8a58dda80be329e + default: bceed2a1f21f2515a30c4517f855f3f54fa5a584 permissions: contents: read @@ -25,7 +25,7 @@ defaults: env: CARGO_TERM_COLOR: always RUST_BACKTRACE: "1" - ZED_CLI_REF: ${{ github.event.inputs.zed_cli_ref || '0b1e11ac2ae0af75fa31e969f8a58dda80be329e' }} + ZED_CLI_REF: ${{ github.event.inputs.zed_cli_ref || 'bceed2a1f21f2515a30c4517f855f3f54fa5a584' }} ZED_INTERFACES_REF: dc0e0a0620b9462817950b552d3d334a184b1cb1 jobs: From ac4981967fa26edb7d96170855b669c6f9198ea3 Mon Sep 17 00:00:00 2001 From: Alexander Mills Date: Mon, 3 Aug 2026 10:32:25 -0500 Subject: [PATCH 12/61] test: pin recursive graph E2E to latest zed-cli head --- .github/workflows/recursive-installs.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/recursive-installs.yml b/.github/workflows/recursive-installs.yml index 537b5ff8..7a80a2a2 100644 --- a/.github/workflows/recursive-installs.yml +++ b/.github/workflows/recursive-installs.yml @@ -9,7 +9,7 @@ on: zed_cli_ref: description: Immutable zed-cli commit to test required: false - default: bceed2a1f21f2515a30c4517f855f3f54fa5a584 + default: c1421f609354bd0f070783597e67c4a23e9d5d97 permissions: contents: read @@ -25,7 +25,7 @@ defaults: env: CARGO_TERM_COLOR: always RUST_BACKTRACE: "1" - ZED_CLI_REF: ${{ github.event.inputs.zed_cli_ref || 'bceed2a1f21f2515a30c4517f855f3f54fa5a584' }} + ZED_CLI_REF: ${{ github.event.inputs.zed_cli_ref || 'c1421f609354bd0f070783597e67c4a23e9d5d97' }} ZED_INTERFACES_REF: dc0e0a0620b9462817950b552d3d334a184b1cb1 jobs: From 1093b532eb5f369a61f0e93acfb27f0e3e948d0f Mon Sep 17 00:00:00 2001 From: Alexander Mills Date: Mon, 3 Aug 2026 10:32:55 -0500 Subject: [PATCH 13/61] test: pin recursive stress E2E to latest zed-cli head --- .github/workflows/recursive-installs-stress.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/recursive-installs-stress.yml b/.github/workflows/recursive-installs-stress.yml index 58e23ce2..cd133993 100644 --- a/.github/workflows/recursive-installs-stress.yml +++ b/.github/workflows/recursive-installs-stress.yml @@ -9,7 +9,7 @@ on: zed_cli_ref: description: Immutable zed-cli commit to test required: false - default: bceed2a1f21f2515a30c4517f855f3f54fa5a584 + default: c1421f609354bd0f070783597e67c4a23e9d5d97 permissions: contents: read @@ -25,7 +25,7 @@ defaults: env: CARGO_TERM_COLOR: always RUST_BACKTRACE: "1" - ZED_CLI_REF: ${{ github.event.inputs.zed_cli_ref || 'bceed2a1f21f2515a30c4517f855f3f54fa5a584' }} + ZED_CLI_REF: ${{ github.event.inputs.zed_cli_ref || 'c1421f609354bd0f070783597e67c4a23e9d5d97' }} ZED_INTERFACES_REF: dc0e0a0620b9462817950b552d3d334a184b1cb1 jobs: From 3b3b277bedc127b8648adfabccbda2f8cab2ea1d Mon Sep 17 00:00:00 2001 From: Alexander Mills Date: Mon, 3 Aug 2026 10:49:25 -0500 Subject: [PATCH 14/61] test: validate unified artifact locking in recursive graph E2E --- .github/workflows/recursive-installs.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/recursive-installs.yml b/.github/workflows/recursive-installs.yml index 7a80a2a2..28ae5174 100644 --- a/.github/workflows/recursive-installs.yml +++ b/.github/workflows/recursive-installs.yml @@ -9,7 +9,7 @@ on: zed_cli_ref: description: Immutable zed-cli commit to test required: false - default: c1421f609354bd0f070783597e67c4a23e9d5d97 + default: 798468a2da66cf895adccde43fe77a1bc4f87381 permissions: contents: read @@ -25,7 +25,7 @@ defaults: env: CARGO_TERM_COLOR: always RUST_BACKTRACE: "1" - ZED_CLI_REF: ${{ github.event.inputs.zed_cli_ref || 'c1421f609354bd0f070783597e67c4a23e9d5d97' }} + ZED_CLI_REF: ${{ github.event.inputs.zed_cli_ref || '798468a2da66cf895adccde43fe77a1bc4f87381' }} ZED_INTERFACES_REF: dc0e0a0620b9462817950b552d3d334a184b1cb1 jobs: From 6436f177127cca32365364d7ac5b5ff9c8d8e7ea Mon Sep 17 00:00:00 2001 From: Alexander Mills Date: Mon, 3 Aug 2026 10:49:53 -0500 Subject: [PATCH 15/61] test: validate unified artifact locking under recursive stress --- .github/workflows/recursive-installs-stress.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/recursive-installs-stress.yml b/.github/workflows/recursive-installs-stress.yml index cd133993..b587ac84 100644 --- a/.github/workflows/recursive-installs-stress.yml +++ b/.github/workflows/recursive-installs-stress.yml @@ -9,7 +9,7 @@ on: zed_cli_ref: description: Immutable zed-cli commit to test required: false - default: c1421f609354bd0f070783597e67c4a23e9d5d97 + default: 798468a2da66cf895adccde43fe77a1bc4f87381 permissions: contents: read @@ -25,7 +25,7 @@ defaults: env: CARGO_TERM_COLOR: always RUST_BACKTRACE: "1" - ZED_CLI_REF: ${{ github.event.inputs.zed_cli_ref || 'c1421f609354bd0f070783597e67c4a23e9d5d97' }} + ZED_CLI_REF: ${{ github.event.inputs.zed_cli_ref || '798468a2da66cf895adccde43fe77a1bc4f87381' }} ZED_INTERFACES_REF: dc0e0a0620b9462817950b552d3d334a184b1cb1 jobs: From 77f933ad325e60aaac832e8b79a05f50c21f0b35 Mon Sep 17 00:00:00 2001 From: Alexander Mills Date: Mon, 3 Aug 2026 11:01:58 -0500 Subject: [PATCH 16/61] test: pin recursive graph E2E to worker-hardened core --- .github/workflows/recursive-installs.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/recursive-installs.yml b/.github/workflows/recursive-installs.yml index 28ae5174..2c13e7d2 100644 --- a/.github/workflows/recursive-installs.yml +++ b/.github/workflows/recursive-installs.yml @@ -9,7 +9,7 @@ on: zed_cli_ref: description: Immutable zed-cli commit to test required: false - default: 798468a2da66cf895adccde43fe77a1bc4f87381 + default: 6aa8edc4f8436dc48f1231001374ca6ef08887dc permissions: contents: read @@ -25,7 +25,7 @@ defaults: env: CARGO_TERM_COLOR: always RUST_BACKTRACE: "1" - ZED_CLI_REF: ${{ github.event.inputs.zed_cli_ref || '798468a2da66cf895adccde43fe77a1bc4f87381' }} + ZED_CLI_REF: ${{ github.event.inputs.zed_cli_ref || '6aa8edc4f8436dc48f1231001374ca6ef08887dc' }} ZED_INTERFACES_REF: dc0e0a0620b9462817950b552d3d334a184b1cb1 jobs: From 67ed242a5e28ede3603358ab3f063615af83cb06 Mon Sep 17 00:00:00 2001 From: Alexander Mills Date: Mon, 3 Aug 2026 11:03:27 -0500 Subject: [PATCH 17/61] test: pin recursive stress E2E to worker-hardened core --- .github/workflows/recursive-installs-stress.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/recursive-installs-stress.yml b/.github/workflows/recursive-installs-stress.yml index b587ac84..5e6af020 100644 --- a/.github/workflows/recursive-installs-stress.yml +++ b/.github/workflows/recursive-installs-stress.yml @@ -9,7 +9,7 @@ on: zed_cli_ref: description: Immutable zed-cli commit to test required: false - default: 798468a2da66cf895adccde43fe77a1bc4f87381 + default: 6aa8edc4f8436dc48f1231001374ca6ef08887dc permissions: contents: read @@ -25,7 +25,7 @@ defaults: env: CARGO_TERM_COLOR: always RUST_BACKTRACE: "1" - ZED_CLI_REF: ${{ github.event.inputs.zed_cli_ref || '798468a2da66cf895adccde43fe77a1bc4f87381' }} + ZED_CLI_REF: ${{ github.event.inputs.zed_cli_ref || '6aa8edc4f8436dc48f1231001374ca6ef08887dc' }} ZED_INTERFACES_REF: dc0e0a0620b9462817950b552d3d334a184b1cb1 jobs: From 569ae1bbe4ed1fd4e6978ea4069039a2c84cbd2d Mon Sep 17 00:00:00 2001 From: Alexander Mills Date: Mon, 3 Aug 2026 11:08:57 -0500 Subject: [PATCH 18/61] test: repin recursive graph E2E after rustfmt fix --- .github/workflows/recursive-installs.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/recursive-installs.yml b/.github/workflows/recursive-installs.yml index 2c13e7d2..04b13658 100644 --- a/.github/workflows/recursive-installs.yml +++ b/.github/workflows/recursive-installs.yml @@ -9,7 +9,7 @@ on: zed_cli_ref: description: Immutable zed-cli commit to test required: false - default: 6aa8edc4f8436dc48f1231001374ca6ef08887dc + default: c2e7fa8f7f4ea5a0acbdb51d6b4ac34f46b406ea permissions: contents: read @@ -25,7 +25,7 @@ defaults: env: CARGO_TERM_COLOR: always RUST_BACKTRACE: "1" - ZED_CLI_REF: ${{ github.event.inputs.zed_cli_ref || '6aa8edc4f8436dc48f1231001374ca6ef08887dc' }} + ZED_CLI_REF: ${{ github.event.inputs.zed_cli_ref || 'c2e7fa8f7f4ea5a0acbdb51d6b4ac34f46b406ea' }} ZED_INTERFACES_REF: dc0e0a0620b9462817950b552d3d334a184b1cb1 jobs: From 604804149ed9bcb620a971816a1d0edd32b9f822 Mon Sep 17 00:00:00 2001 From: Alexander Mills Date: Mon, 3 Aug 2026 11:09:23 -0500 Subject: [PATCH 19/61] test: repin recursive stress E2E after rustfmt fix --- .github/workflows/recursive-installs-stress.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/recursive-installs-stress.yml b/.github/workflows/recursive-installs-stress.yml index 5e6af020..3141523e 100644 --- a/.github/workflows/recursive-installs-stress.yml +++ b/.github/workflows/recursive-installs-stress.yml @@ -9,7 +9,7 @@ on: zed_cli_ref: description: Immutable zed-cli commit to test required: false - default: 6aa8edc4f8436dc48f1231001374ca6ef08887dc + default: c2e7fa8f7f4ea5a0acbdb51d6b4ac34f46b406ea permissions: contents: read @@ -25,7 +25,7 @@ defaults: env: CARGO_TERM_COLOR: always RUST_BACKTRACE: "1" - ZED_CLI_REF: ${{ github.event.inputs.zed_cli_ref || '6aa8edc4f8436dc48f1231001374ca6ef08887dc' }} + ZED_CLI_REF: ${{ github.event.inputs.zed_cli_ref || 'c2e7fa8f7f4ea5a0acbdb51d6b4ac34f46b406ea' }} ZED_INTERFACES_REF: dc0e0a0620b9462817950b552d3d334a184b1cb1 jobs: From b73e731c1428663c31d723e8bf1a6b98cf427041 Mon Sep 17 00:00:00 2001 From: Alexander Mills Date: Mon, 3 Aug 2026 12:15:50 -0500 Subject: [PATCH 20/61] test: certify full recursive-lock stack and owner-exit recovery --- .github/workflows/recursive-installs.yml | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/.github/workflows/recursive-installs.yml b/.github/workflows/recursive-installs.yml index 04b13658..3e5c709d 100644 --- a/.github/workflows/recursive-installs.yml +++ b/.github/workflows/recursive-installs.yml @@ -9,14 +9,14 @@ on: zed_cli_ref: description: Immutable zed-cli commit to test required: false - default: c2e7fa8f7f4ea5a0acbdb51d6b4ac34f46b406ea + default: 3a36b527de3264f616b32b84a2ca35bec554aaac permissions: contents: read concurrency: group: recursive-install-graph-${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} - cancel-in-progress: ${{ github.event_name == 'pull_request' }} + cancel-in-progress: true defaults: run: @@ -25,7 +25,7 @@ defaults: env: CARGO_TERM_COLOR: always RUST_BACKTRACE: "1" - ZED_CLI_REF: ${{ github.event.inputs.zed_cli_ref || 'c2e7fa8f7f4ea5a0acbdb51d6b4ac34f46b406ea' }} + ZED_CLI_REF: ${{ github.event.inputs.zed_cli_ref || '3a36b527de3264f616b32b84a2ca35bec554aaac' }} ZED_INTERFACES_REF: dc0e0a0620b9462817950b552d3d334a184b1cb1 jobs: @@ -76,12 +76,13 @@ jobs: rustc --version --verbose cargo --version --verbose - - name: Format and unit-test recursive installer + - name: Format and unit-test recursive installer and process locks run: | set -euo pipefail python3 -m py_compile harness/scripts/recursive_installs.py cargo fmt --all --check --manifest-path zed-cli/Cargo.toml cargo test --locked --manifest-path zed-cli/Cargo.toml --lib install_graph::tests + cargo test --locked --manifest-path zed-cli/Cargo.toml --lib store::tests cargo test --locked --manifest-path zed-cli/Cargo.toml --lib config::tests - name: Build immutable zed binary From c416a4ab61f704e185ab8d6b03213f3405f3f03c Mon Sep 17 00:00:00 2001 From: Alexander Mills Date: Mon, 3 Aug 2026 12:16:15 -0500 Subject: [PATCH 21/61] test: repin recursive stress to full blocking-lock stack --- .github/workflows/recursive-installs-stress.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/recursive-installs-stress.yml b/.github/workflows/recursive-installs-stress.yml index 3141523e..302f9162 100644 --- a/.github/workflows/recursive-installs-stress.yml +++ b/.github/workflows/recursive-installs-stress.yml @@ -9,14 +9,14 @@ on: zed_cli_ref: description: Immutable zed-cli commit to test required: false - default: c2e7fa8f7f4ea5a0acbdb51d6b4ac34f46b406ea + default: 3a36b527de3264f616b32b84a2ca35bec554aaac permissions: contents: read concurrency: group: recursive-install-stress-${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} - cancel-in-progress: ${{ github.event_name == 'pull_request' }} + cancel-in-progress: true defaults: run: @@ -25,7 +25,7 @@ defaults: env: CARGO_TERM_COLOR: always RUST_BACKTRACE: "1" - ZED_CLI_REF: ${{ github.event.inputs.zed_cli_ref || 'c2e7fa8f7f4ea5a0acbdb51d6b4ac34f46b406ea' }} + ZED_CLI_REF: ${{ github.event.inputs.zed_cli_ref || '3a36b527de3264f616b32b84a2ca35bec554aaac' }} ZED_INTERFACES_REF: dc0e0a0620b9462817950b552d3d334a184b1cb1 jobs: From d8fb20d624ee0d1f05c02d54b616b820c46dea5b Mon Sep 17 00:00:00 2001 From: Alexander Mills Date: Mon, 3 Aug 2026 12:32:02 -0500 Subject: [PATCH 22/61] test: treat shared-schema as a real package fixture --- scripts/lifecycle.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/lifecycle.py b/scripts/lifecycle.py index a0597007..101c230c 100644 --- a/scripts/lifecycle.py +++ b/scripts/lifecycle.py @@ -22,7 +22,7 @@ from typing import Mapping, Sequence ORG = "zed-pkg-test" -NON_PACKAGE_REPOS = {"shared-schema", "zed-pkg-e2e"} +NON_PACKAGE_REPOS = {"zed-pkg-e2e"} # Package -> (fixture repository, path containing .zpkg.toml). This is the # dependency graph boundary. Target packages all map to the one source repo From 63a83da9e9c07e8b882b6d54cb2816f22c5ae8d2 Mon Sep 17 00:00:00 2001 From: Alexander Mills Date: Mon, 3 Aug 2026 12:33:19 -0500 Subject: [PATCH 23/61] test: certify resolver hardening on the final recursive stack --- .github/workflows/recursive-installs.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/recursive-installs.yml b/.github/workflows/recursive-installs.yml index 3e5c709d..6221aeab 100644 --- a/.github/workflows/recursive-installs.yml +++ b/.github/workflows/recursive-installs.yml @@ -9,7 +9,7 @@ on: zed_cli_ref: description: Immutable zed-cli commit to test required: false - default: 3a36b527de3264f616b32b84a2ca35bec554aaac + default: cea860bec5fe87727d739a976aef977e5f4c1fef permissions: contents: read @@ -25,7 +25,7 @@ defaults: env: CARGO_TERM_COLOR: always RUST_BACKTRACE: "1" - ZED_CLI_REF: ${{ github.event.inputs.zed_cli_ref || '3a36b527de3264f616b32b84a2ca35bec554aaac' }} + ZED_CLI_REF: ${{ github.event.inputs.zed_cli_ref || 'cea860bec5fe87727d739a976aef977e5f4c1fef' }} ZED_INTERFACES_REF: dc0e0a0620b9462817950b552d3d334a184b1cb1 jobs: @@ -82,6 +82,7 @@ jobs: python3 -m py_compile harness/scripts/recursive_installs.py cargo fmt --all --check --manifest-path zed-cli/Cargo.toml cargo test --locked --manifest-path zed-cli/Cargo.toml --lib install_graph::tests + cargo test --locked --manifest-path zed-cli/Cargo.toml --lib install_graph::hardening_tests cargo test --locked --manifest-path zed-cli/Cargo.toml --lib store::tests cargo test --locked --manifest-path zed-cli/Cargo.toml --lib config::tests From 404d4b7b5fdd8b7465d3bfb5b6ca186abe5bed42 Mon Sep 17 00:00:00 2001 From: Alexander Mills Date: Mon, 3 Aug 2026 12:33:48 -0500 Subject: [PATCH 24/61] test: stress the final resolver-hardening commit --- .github/workflows/recursive-installs-stress.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/recursive-installs-stress.yml b/.github/workflows/recursive-installs-stress.yml index 302f9162..2ae53589 100644 --- a/.github/workflows/recursive-installs-stress.yml +++ b/.github/workflows/recursive-installs-stress.yml @@ -9,7 +9,7 @@ on: zed_cli_ref: description: Immutable zed-cli commit to test required: false - default: 3a36b527de3264f616b32b84a2ca35bec554aaac + default: cea860bec5fe87727d739a976aef977e5f4c1fef permissions: contents: read @@ -25,7 +25,7 @@ defaults: env: CARGO_TERM_COLOR: always RUST_BACKTRACE: "1" - ZED_CLI_REF: ${{ github.event.inputs.zed_cli_ref || '3a36b527de3264f616b32b84a2ca35bec554aaac' }} + ZED_CLI_REF: ${{ github.event.inputs.zed_cli_ref || 'cea860bec5fe87727d739a976aef977e5f4c1fef' }} ZED_INTERFACES_REF: dc0e0a0620b9462817950b552d3d334a184b1cb1 jobs: From 54303a0d33211eb0181737113cf1d53019f86eaa Mon Sep 17 00:00:00 2001 From: Alexander Mills Date: Mon, 3 Aug 2026 12:45:00 -0500 Subject: [PATCH 25/61] test: track shared-schema dependency and clean publish scratch output --- scripts/lifecycle.py | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/scripts/lifecycle.py b/scripts/lifecycle.py index 101c230c..29f05469 100644 --- a/scripts/lifecycle.py +++ b/scripts/lifecycle.py @@ -38,6 +38,7 @@ "zedtest/polyglot-lib-python": ("polyglot-lib", "."), "zedtest/polyglot-lib-golang": ("polyglot-lib", "."), "zedtest/polyglot-lib-rust": ("polyglot-lib", "."), + "zedtest/shared-schema": ("shared-schema", "."), "zedtest/ws-core": ("workspace-monorepo", "packages/core"), "zedtest/ws-utils": ("workspace-monorepo", "packages/utils"), "zedtest/ws-cli": ("workspace-monorepo", "apps/cli"), @@ -173,6 +174,33 @@ def assert_git_clean(self, root: Path, label: str) -> None: if output.strip(): raise AssertionError(f"{label} was mutated by lifecycle tests:\n{output}") + def remove_generated_pack_output(self, source: Path) -> None: + """Remove only the documented publish scratch directory. + + `zed publish` writes its deterministic archive below `.zed/pack` even + when the registry is isolated. Some fixtures ignore that directory and + some intentionally do not. Cleaning this exact generated path keeps the + source-mutation assertion portable without hiding any other `.zed` + output or any tracked-file mutation. + """ + + pack_dir = source / ".zed" / "pack" + if pack_dir.is_symlink(): + raise AssertionError(f"refusing to remove symlinked pack path: {pack_dir}") + if pack_dir.exists(): + if not pack_dir.is_dir(): + raise AssertionError(f"generated pack path is not a directory: {pack_dir}") + shutil.rmtree(pack_dir) + + zed_dir = source / ".zed" + if zed_dir.is_dir(): + try: + zed_dir.rmdir() + except OSError: + # Preserve and surface any unrelated `.zed` output through the + # normal git-clean assertion below. + pass + def source_root(self, repo: str) -> Path: if repo == self.repo: return self.fixture @@ -226,6 +254,7 @@ def publish_prerequisite(self, package: str) -> None: self.published_sources.add(key) for output in expected_packages(manifest): assert_registry_version(self.registry, output) + self.remove_generated_pack_output(source) self.assert_git_clean(self.source_root(repo), f"seed source {repo}") def seed_dependencies(self, manifest: dict) -> None: @@ -331,6 +360,7 @@ def run_unit(self, unit: Path, label: str, index: int) -> None: raise AssertionError( f"second publish changed registry bytes for {full_name}" ) + self.remove_generated_pack_output(unit) for output_index, output in enumerate(outputs): self.exercise_consumer(output, index, output_index) From 3ab5315e7839fca3589178df4758c74a051623b4 Mon Sep 17 00:00:00 2001 From: Alexander Mills Date: Mon, 3 Aug 2026 12:48:33 -0500 Subject: [PATCH 26/61] test: fail closed around publish scratch cleanup --- scripts/lifecycle.py | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/scripts/lifecycle.py b/scripts/lifecycle.py index 29f05469..c63fc0a5 100644 --- a/scripts/lifecycle.py +++ b/scripts/lifecycle.py @@ -175,7 +175,7 @@ def assert_git_clean(self, root: Path, label: str) -> None: raise AssertionError(f"{label} was mutated by lifecycle tests:\n{output}") def remove_generated_pack_output(self, source: Path) -> None: - """Remove only the documented publish scratch directory. + """Remove only untracked publish scratch output below `.zed/pack`. `zed publish` writes its deterministic archive below `.zed/pack` even when the registry is isolated. Some fixtures ignore that directory and @@ -184,15 +184,23 @@ def remove_generated_pack_output(self, source: Path) -> None: output or any tracked-file mutation. """ - pack_dir = source / ".zed" / "pack" + zed_dir = source / ".zed" + if zed_dir.is_symlink(): + raise AssertionError(f"refusing to traverse symlinked .zed path: {zed_dir}") + + pack_dir = zed_dir / "pack" if pack_dir.is_symlink(): raise AssertionError(f"refusing to remove symlinked pack path: {pack_dir}") if pack_dir.exists(): if not pack_dir.is_dir(): raise AssertionError(f"generated pack path is not a directory: {pack_dir}") + tracked = self.run(["git", "ls-files", "--", ".zed/pack"], cwd=source) + if tracked.strip(): + raise AssertionError( + f"refusing to remove tracked publish output below {pack_dir}:\n{tracked}" + ) shutil.rmtree(pack_dir) - zed_dir = source / ".zed" if zed_dir.is_dir(): try: zed_dir.rmdir() From a6b989c860ab126bd7353c58c1aeb11a02bde699 Mon Sep 17 00:00:00 2001 From: Alexander Mills Date: Mon, 3 Aug 2026 12:50:58 -0500 Subject: [PATCH 27/61] test: cover lifecycle dependency mapping and scratch cleanup safety --- tests/test_lifecycle_harness.py | 134 ++++++++++++++++++++++++++++++++ 1 file changed, 134 insertions(+) create mode 100644 tests/test_lifecycle_harness.py diff --git a/tests/test_lifecycle_harness.py b/tests/test_lifecycle_harness.py new file mode 100644 index 00000000..6db9ea03 --- /dev/null +++ b/tests/test_lifecycle_harness.py @@ -0,0 +1,134 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import argparse +import importlib.util +import os +import subprocess +import tempfile +import unittest +from pathlib import Path + + +REPOSITORY_ROOT = Path(__file__).resolve().parents[1] +LIFECYCLE_PATH = REPOSITORY_ROOT / "scripts" / "lifecycle.py" +SPEC = importlib.util.spec_from_file_location("zed_lifecycle_harness", LIFECYCLE_PATH) +if SPEC is None or SPEC.loader is None: + raise RuntimeError(f"could not load lifecycle harness from {LIFECYCLE_PATH}") +LIFECYCLE = importlib.util.module_from_spec(SPEC) +SPEC.loader.exec_module(LIFECYCLE) + + +class LifecycleHarnessTests(unittest.TestCase): + def setUp(self) -> None: + self.temporary = tempfile.TemporaryDirectory() + self.addCleanup(self.temporary.cleanup) + self.root = Path(self.temporary.name) + self.fixture = self.root / "fixture" + self.fixture.mkdir() + subprocess.run( + ["git", "init", "--quiet"], + cwd=self.fixture, + check=True, + text=True, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + ) + + fake_zed = self.root / "zed" + fake_zed.write_text("#!/bin/sh\nexit 0\n", encoding="utf-8") + fake_zed.chmod(0o755) + args = argparse.Namespace( + repo="fixture", + fixture_dir=self.fixture, + zed=fake_zed, + work_root=self.root / "work", + ) + self.harness = LIFECYCLE.Harness(args) + + def test_shared_schema_is_a_known_dependency_source(self) -> None: + self.assertEqual( + LIFECYCLE.PACKAGE_SOURCES["zedtest/shared-schema"], + ("shared-schema", "."), + ) + + def test_untracked_pack_output_is_removed(self) -> None: + archive = self.fixture / ".zed" / "pack" / "package.tar.gz" + archive.parent.mkdir(parents=True) + archive.write_bytes(b"fixture") + + self.harness.remove_generated_pack_output(self.fixture) + + self.assertFalse(archive.exists()) + self.assertFalse((self.fixture / ".zed").exists()) + + def test_unrelated_zed_state_is_preserved(self) -> None: + archive = self.fixture / ".zed" / "pack" / "package.tar.gz" + archive.parent.mkdir(parents=True) + archive.write_bytes(b"fixture") + unrelated = self.fixture / ".zed" / "keep.txt" + unrelated.write_text("keep\n", encoding="utf-8") + + self.harness.remove_generated_pack_output(self.fixture) + + self.assertFalse(archive.exists()) + self.assertEqual(unrelated.read_text(encoding="utf-8"), "keep\n") + + def test_tracked_pack_output_is_never_deleted(self) -> None: + tracked = self.fixture / ".zed" / "pack" / "tracked.tar.gz" + tracked.parent.mkdir(parents=True) + tracked.write_bytes(b"tracked") + subprocess.run( + ["git", "add", ".zed/pack/tracked.tar.gz"], + cwd=self.fixture, + check=True, + text=True, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + ) + + with self.assertRaisesRegex(AssertionError, "tracked publish output"): + self.harness.remove_generated_pack_output(self.fixture) + + self.assertEqual(tracked.read_bytes(), b"tracked") + + def test_symlinked_zed_directory_is_never_traversed(self) -> None: + outside = self.root / "outside-zed" + pack = outside / "pack" + pack.mkdir(parents=True) + sentinel = pack / "sentinel.tar.gz" + sentinel.write_bytes(b"outside") + os.symlink(outside, self.fixture / ".zed", target_is_directory=True) + + with self.assertRaisesRegex(AssertionError, "symlinked .zed path"): + self.harness.remove_generated_pack_output(self.fixture) + + self.assertEqual(sentinel.read_bytes(), b"outside") + + def test_symlinked_pack_directory_is_never_removed(self) -> None: + zed_dir = self.fixture / ".zed" + zed_dir.mkdir() + outside = self.root / "outside-pack" + outside.mkdir() + sentinel = outside / "sentinel.tar.gz" + sentinel.write_bytes(b"outside") + os.symlink(outside, zed_dir / "pack", target_is_directory=True) + + with self.assertRaisesRegex(AssertionError, "symlinked pack path"): + self.harness.remove_generated_pack_output(self.fixture) + + self.assertEqual(sentinel.read_bytes(), b"outside") + + def test_non_directory_pack_path_fails_closed(self) -> None: + pack = self.fixture / ".zed" / "pack" + pack.parent.mkdir() + pack.write_bytes(b"not-a-directory") + + with self.assertRaisesRegex(AssertionError, "not a directory"): + self.harness.remove_generated_pack_output(self.fixture) + + self.assertEqual(pack.read_bytes(), b"not-a-directory") + + +if __name__ == "__main__": + unittest.main(verbosity=2) From 2fb7605f81f793fa94b67647a8370f5ed5bf831f Mon Sep 17 00:00:00 2001 From: Alexander Mills Date: Mon, 3 Aug 2026 12:51:38 -0500 Subject: [PATCH 28/61] test: load lifecycle dataclasses through a registered module --- tests/test_lifecycle_harness.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/test_lifecycle_harness.py b/tests/test_lifecycle_harness.py index 6db9ea03..2ecb805e 100644 --- a/tests/test_lifecycle_harness.py +++ b/tests/test_lifecycle_harness.py @@ -5,6 +5,7 @@ import importlib.util import os import subprocess +import sys import tempfile import unittest from pathlib import Path @@ -16,6 +17,7 @@ if SPEC is None or SPEC.loader is None: raise RuntimeError(f"could not load lifecycle harness from {LIFECYCLE_PATH}") LIFECYCLE = importlib.util.module_from_spec(SPEC) +sys.modules[SPEC.name] = LIFECYCLE SPEC.loader.exec_module(LIFECYCLE) From ebff138b6e057f2d5baa9c9499db13f4f084df50 Mon Sep 17 00:00:00 2001 From: Alexander Mills Date: Mon, 3 Aug 2026 12:51:55 -0500 Subject: [PATCH 29/61] ci: add permanent lifecycle harness safety tests --- .github/workflows/lifecycle-harness-unit.yml | 49 ++++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 .github/workflows/lifecycle-harness-unit.yml diff --git a/.github/workflows/lifecycle-harness-unit.yml b/.github/workflows/lifecycle-harness-unit.yml new file mode 100644 index 00000000..f0ecb33a --- /dev/null +++ b/.github/workflows/lifecycle-harness-unit.yml @@ -0,0 +1,49 @@ +name: lifecycle harness unit + +on: + pull_request: + paths: + - scripts/lifecycle.py + - tests/test_lifecycle_harness.py + - .github/workflows/lifecycle-harness-unit.yml + push: + branches: [main] + paths: + - scripts/lifecycle.py + - tests/test_lifecycle_harness.py + - .github/workflows/lifecycle-harness-unit.yml + workflow_dispatch: + +permissions: + contents: read + +concurrency: + group: lifecycle-harness-unit-${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +jobs: + lifecycle-harness-unit: + name: Dependency map and safe scratch cleanup + runs-on: ubuntu-24.04 + timeout-minutes: 10 + steps: + - name: Check out the harness + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + fetch-depth: 1 + persist-credentials: false + show-progress: false + + - name: Show tool versions + run: | + set -euo pipefail + python3 --version + git --version + + - name: Compile the lifecycle harness and its tests + run: | + set -euo pipefail + python3 -m py_compile scripts/lifecycle.py tests/test_lifecycle_harness.py + + - name: Run dependency-map and cleanup safety tests + run: python3 tests/test_lifecycle_harness.py From 4cf4ff38f0a3a3b32d986f4dd21f98cad577ed31 Mon Sep 17 00:00:00 2001 From: Alexander Mills Date: Mon, 3 Aug 2026 14:48:29 -0500 Subject: [PATCH 30/61] test: repin recursive install coverage to synchronized zed-cli head --- .github/workflows/recursive-installs.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/recursive-installs.yml b/.github/workflows/recursive-installs.yml index 6221aeab..e4dc7ace 100644 --- a/.github/workflows/recursive-installs.yml +++ b/.github/workflows/recursive-installs.yml @@ -9,7 +9,7 @@ on: zed_cli_ref: description: Immutable zed-cli commit to test required: false - default: cea860bec5fe87727d739a976aef977e5f4c1fef + default: 67c01b83c834ea14af996613f1a3dc0802a2fbd3 permissions: contents: read @@ -25,7 +25,7 @@ defaults: env: CARGO_TERM_COLOR: always RUST_BACKTRACE: "1" - ZED_CLI_REF: ${{ github.event.inputs.zed_cli_ref || 'cea860bec5fe87727d739a976aef977e5f4c1fef' }} + ZED_CLI_REF: ${{ github.event.inputs.zed_cli_ref || '67c01b83c834ea14af996613f1a3dc0802a2fbd3' }} ZED_INTERFACES_REF: dc0e0a0620b9462817950b552d3d334a184b1cb1 jobs: From 78e22722c51ff3a615beb8798d4f56c7dafb6f61 Mon Sep 17 00:00:00 2001 From: Alexander Mills Date: Mon, 3 Aug 2026 14:48:53 -0500 Subject: [PATCH 31/61] test: repin recursive stress coverage to synchronized zed-cli head --- .github/workflows/recursive-installs-stress.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/recursive-installs-stress.yml b/.github/workflows/recursive-installs-stress.yml index 2ae53589..7d7bccd0 100644 --- a/.github/workflows/recursive-installs-stress.yml +++ b/.github/workflows/recursive-installs-stress.yml @@ -9,7 +9,7 @@ on: zed_cli_ref: description: Immutable zed-cli commit to test required: false - default: cea860bec5fe87727d739a976aef977e5f4c1fef + default: 67c01b83c834ea14af996613f1a3dc0802a2fbd3 permissions: contents: read @@ -25,7 +25,7 @@ defaults: env: CARGO_TERM_COLOR: always RUST_BACKTRACE: "1" - ZED_CLI_REF: ${{ github.event.inputs.zed_cli_ref || 'cea860bec5fe87727d739a976aef977e5f4c1fef' }} + ZED_CLI_REF: ${{ github.event.inputs.zed_cli_ref || '67c01b83c834ea14af996613f1a3dc0802a2fbd3' }} ZED_INTERFACES_REF: dc0e0a0620b9462817950b552d3d334a184b1cb1 jobs: From cf1ebf8bd56ae836148aa317d29a186ca580da55 Mon Sep 17 00:00:00 2001 From: Alexander Mills Date: Mon, 3 Aug 2026 14:51:06 -0500 Subject: [PATCH 32/61] test: add install recovery rollback and idempotency matrix --- scripts/install_recovery_matrix.py | 659 +++++++++++++++++++++++++++++ 1 file changed, 659 insertions(+) create mode 100644 scripts/install_recovery_matrix.py diff --git a/scripts/install_recovery_matrix.py b/scripts/install_recovery_matrix.py new file mode 100644 index 00000000..0512b97f --- /dev/null +++ b/scripts/install_recovery_matrix.py @@ -0,0 +1,659 @@ +#!/usr/bin/env python3 +"""Hermetic install recovery, rollback, and idempotency coverage for zed.""" + +from __future__ import annotations + +import argparse +import json +import os +import re +import shutil +import subprocess +import sys +import tomllib +from concurrent.futures import ThreadPoolExecutor +from dataclasses import dataclass +from pathlib import Path +from threading import Barrier +from typing import Mapping, Sequence + +ORG = "zed-install-recovery" +VERSION = "1.0.0" +BASE_GRAPH: dict[str, tuple[str, ...]] = { + "leaf-a": (), + "leaf-b": (), + "root": ("leaf-a", "leaf-b"), +} +PREFETCH_RE = re.compile( + r"recursive install prefetch: (?P\d+) package\(s\), " + r"up to (?P\d+) concurrent, (?P\d+) downloaded" +) +PACKAGE_BLOCK_RE = re.compile( + r"(?ms)^\[\[package\]\]\n.*?(?=^\[\[package\]\]|\Z)" +) +SHA_ASSIGNMENT_RE = re.compile(r'(?m)^sha256 = "[0-9a-f]{64}"$') + + +@dataclass(frozen=True) +class Completed: + command: tuple[str, ...] + returncode: int + output: str + + +def shell_quote(value: str) -> str: + if re.fullmatch(r"[A-Za-z0-9_./:@%+=,-]+", value): + return value + return "'" + value.replace("'", "'\"'\"'") + "'" + + +def run( + command: Sequence[str | Path], + *, + cwd: Path | None = None, + env: Mapping[str, str] | None = None, + timeout: int = 240, +) -> Completed: + argv = tuple(str(value) for value in command) + shown = " ".join(shell_quote(value) for value in argv) + print(f"\n$ (cd {cwd or Path.cwd()} && {shown})", flush=True) + completed = subprocess.run( + argv, + cwd=cwd, + env=dict(env) if env is not None else None, + text=True, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + check=False, + timeout=timeout, + ) + if completed.stdout: + print(completed.stdout, end="", flush=True) + return Completed(argv, completed.returncode, completed.stdout) + + +def require_success(completed: Completed) -> None: + if completed.returncode != 0: + shown = " ".join(shell_quote(value) for value in completed.command) + raise RuntimeError(f"command failed with exit code {completed.returncode}: {shown}") + + +def require_failure(completed: Completed, *expected_fragments: str) -> None: + if completed.returncode == 0: + shown = " ".join(shell_quote(value) for value in completed.command) + raise AssertionError(f"command unexpectedly succeeded: {shown}") + for fragment in expected_fragments: + if fragment not in completed.output: + raise AssertionError( + f"failed command output did not contain {fragment!r}:\n{completed.output}" + ) + + +def zed_command(zed: Path, registry: Path, home: Path, *args: str) -> list[str | Path]: + return [ + zed, + "--registry", + f"file://{registry}", + "--home", + home, + *args, + ] + + +def write_package(root: Path, name: str, dependencies: Sequence[str]) -> None: + root.mkdir(parents=True, exist_ok=True) + lines = [ + "[package]", + f'org = "{ORG}"', + f'name = "{name}"', + f'version = "{VERSION}"', + 'description = "install recovery fixture"', + "", + "[package.repository]", + 'vcs = "git"', + 'url = "https://github.com/zed-pkg-test/zed-pkg-e2e"', + ] + if dependencies: + lines.extend(("", "[dependencies]")) + for dependency in dependencies: + lines.append(f'"{ORG}/{dependency}" = "={VERSION}"') + (root / ".zpkg.toml").write_text("\n".join(lines) + "\n", encoding="utf-8") + (root / "payload.txt").write_text(f"{ORG}/{name}@{VERSION}\n", encoding="utf-8") + + +def write_consumer(root: Path, dependencies: Sequence[str]) -> None: + root.mkdir(parents=True, exist_ok=True) + lines = [ + "[package]", + 'org = "install-recovery-consumer"', + f'name = "{root.name}"', + 'version = "0.1.0"', + "", + "[package.repository]", + 'vcs = "git"', + 'url = "https://example.invalid/install-recovery-consumer"', + "", + "[dependencies]", + ] + for dependency in dependencies: + lines.append(f'"{ORG}/{dependency}" = "={VERSION}"') + lines.append("") + (root / ".zpkg.toml").write_text("\n".join(lines), encoding="utf-8") + + +def manifest_dependencies(project: Path) -> set[str]: + with (project / ".zpkg.toml").open("rb") as handle: + document = tomllib.load(handle) + return set((document.get("dependencies") or {}).keys()) + + +def lock_packages(project: Path) -> list[dict[str, object]]: + with (project / ".zpkg.lock").open("rb") as handle: + document = tomllib.load(handle) + packages = document.get("package") or [] + if not isinstance(packages, list): + raise AssertionError("lockfile package table is not a list") + return packages + + +def lock_keys(project: Path) -> set[str]: + return { + f"{package['org']}/{package['name']}" + for package in lock_packages(project) + } + + +def expected_keys(names: Sequence[str]) -> set[str]: + return {f"{ORG}/{name}" for name in names} + + +def assert_lock(project: Path, names: Sequence[str]) -> None: + actual = lock_keys(project) + expected = expected_keys(names) + if actual != expected: + raise AssertionError( + f"lock graph mismatch for {project}: expected {sorted(expected)}, got {sorted(actual)}" + ) + + +def assert_symlink_install( + project: Path, + home: Path, + names: Sequence[str], +) -> dict[str, Path]: + store = (home / "store").resolve() + targets: dict[str, Path] = {} + for name in names: + installed = project / "zed_modules" / ORG / name + if not installed.is_symlink(): + raise AssertionError(f"expected symlink materialization: {installed}") + target = installed.resolve(strict=True) + try: + target.relative_to(store) + except ValueError as error: + raise AssertionError(f"{installed} resolves outside {store}: {target}") from error + payload = (target / "payload.txt").read_text(encoding="utf-8") + if payload != f"{ORG}/{name}@{VERSION}\n": + raise AssertionError(f"unexpected payload through {installed}: {payload!r}") + targets[name] = target + return targets + + +def assert_copy_install(project: Path, names: Sequence[str]) -> None: + modules = project / "zed_modules" + for name in names: + installed = modules / ORG / name + if installed.is_symlink() or not installed.is_dir(): + raise AssertionError(f"expected copied package directory: {installed}") + payload = (installed / "payload.txt").read_text(encoding="utf-8") + if payload != f"{ORG}/{name}@{VERSION}\n": + raise AssertionError(f"unexpected copied payload in {installed}: {payload!r}") + leaked = [path for path in modules.rglob("*") if path.is_symlink()] + if leaked: + raise AssertionError(f"copy install leaked symlinks: {leaked}") + + +def assert_not_materialized(project: Path, names: Sequence[str]) -> None: + for name in names: + installed = project / "zed_modules" / ORG / name + if installed.exists() or installed.is_symlink(): + raise AssertionError(f"unexpected materialized package: {installed}") + + +def assert_no_transaction_leaks(project: Path) -> None: + staging = project / ".zpkg-staging" + if staging.exists(): + entries = list(staging.iterdir()) if staging.is_dir() else [staging] + raise AssertionError(f"transaction staging leaked in {project}: {entries}") + + +def assert_no_cache_staging(home: Path) -> None: + cache = home / "cache" + if not cache.exists(): + return + leaked_directories = sorted(path for path in cache.iterdir() if path.is_dir()) + leaked_downloads = sorted(cache.glob("**/artifact.download")) + if leaked_directories or leaked_downloads: + raise AssertionError( + f"cache staging leaked in {home}: dirs={leaked_directories}, downloads={leaked_downloads}" + ) + + +def parse_prefetch(output: str) -> tuple[int, int, int]: + matches = list(PREFETCH_RE.finditer(output)) + if len(matches) != 1: + raise AssertionError( + f"expected one recursive prefetch summary, got {len(matches)}:\n{output}" + ) + match = matches[0] + return tuple( + int(match.group(field)) + for field in ("resolved", "concurrency", "downloaded") + ) + + +def assert_prefetch( + completed: Completed, + *, + resolved: int, + downloaded: int, +) -> tuple[int, int, int]: + actual = parse_prefetch(completed.output) + expected = (resolved, 5, downloaded) + if actual != expected: + raise AssertionError(f"prefetch mismatch: expected {expected}, got {actual}") + return actual + + +def registry_version_path(registry: Path, name: str) -> Path: + return registry / "packages" / ORG / name / "versions" / f"{VERSION}.json" + + +def registry_artifact(registry: Path, name: str) -> tuple[str, Path]: + metadata_path = registry_version_path(registry, name) + payload = json.loads(metadata_path.read_text(encoding="utf-8")) + sha256 = str(payload["sha256"]) + if not re.fullmatch(r"[0-9a-f]{64}", sha256): + raise AssertionError(f"invalid registry SHA for {name}: {sha256!r}") + artifact = registry / "artifacts" / f"{sha256}.tar.gz" + if not artifact.is_file(): + raise AssertionError(f"registry artifact missing before test: {artifact}") + return sha256, artifact + + +def tamper_first_sha(lock_text: str) -> str: + tampered, count = SHA_ASSIGNMENT_RE.subn( + 'sha256 = "' + ("0" * 64) + '"', + lock_text, + count=1, + ) + if count != 1: + raise AssertionError("could not locate first SHA assignment in lockfile") + return tampered + + +def duplicate_first_package(lock_text: str) -> str: + match = PACKAGE_BLOCK_RE.search(lock_text) + if match is None: + raise AssertionError("could not locate a package block in lockfile") + return lock_text.rstrip() + "\n\n" + match.group(0).rstrip() + "\n" + + +def publish( + zed: Path, + registry: Path, + home: Path, + source: Path, + env: Mapping[str, str], +) -> None: + completed = run( + zed_command(zed, registry, home, "publish", "--skip-vcs-checks"), + cwd=source, + env=env, + ) + require_success(completed) + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("--zed", type=Path, required=True) + parser.add_argument("--work-root", type=Path, required=True) + args = parser.parse_args() + + zed = args.zed.resolve() + root = args.work_root.resolve() + if not zed.is_file(): + raise RuntimeError(f"zed binary does not exist: {zed}") + if root.exists(): + raise RuntimeError(f"work root must be fresh: {root}") + + registry = root / "registry" + publish_home = root / "publish-home" + shared_home = root / "shared-home" + copy_home = root / "copy-home" + tamper_home = root / "tamper-home" + duplicate_home = root / "duplicate-home" + corrupt_home = root / "corrupt-home" + sources = root / "sources" + project = root / "same-project" + copy_project = root / "copy-project" + tamper_project = root / "tamper-project" + duplicate_project = root / "duplicate-project" + corrupt_project = root / "corrupt-project" + registry.mkdir(parents=True) + + environment = os.environ.copy() + environment.update( + { + "CI": "true", + "GIT_TERMINAL_PROMPT": "0", + "RUST_BACKTRACE": "1", + "ZED_PKG_INSTALL_CONCURRENCY": "5", + } + ) + + for name, dependencies in BASE_GRAPH.items(): + source = sources / name + write_package(source, name, dependencies) + publish(zed, registry, publish_home, source, environment) + + write_consumer(project, ("root",)) + start = Barrier(3) + + def concurrent_install() -> Completed: + start.wait() + return run( + zed_command(zed, registry, shared_home, "install", "--adapter", "none"), + cwd=project, + env=environment, + timeout=300, + ) + + with ThreadPoolExecutor(max_workers=2, thread_name_prefix="zed-same-project") as executor: + futures = [executor.submit(concurrent_install) for _ in range(2)] + start.wait() + concurrent = [future.result(timeout=360) for future in futures] + + for completed in concurrent: + require_success(completed) + summaries = [parse_prefetch(completed.output) for completed in concurrent] + if any(summary[:2] != (len(BASE_GRAPH), 5) for summary in summaries): + raise AssertionError(f"same-project resolver summaries are wrong: {summaries}") + if sum(summary[2] for summary in summaries) != len(BASE_GRAPH): + raise AssertionError( + f"same-project processes did not deduplicate downloads: {summaries}" + ) + + all_base = tuple(BASE_GRAPH) + assert_lock(project, all_base) + initial_targets = assert_symlink_install(project, shared_home, all_base) + assert_no_transaction_leaks(project) + assert_no_cache_staging(shared_home) + lock_before = (project / ".zpkg.lock").read_bytes() + manifest_before = (project / ".zpkg.toml").read_bytes() + + warm = run( + zed_command(zed, registry, shared_home, "install", "--adapter", "none"), + cwd=project, + env=environment, + ) + require_success(warm) + assert_prefetch(warm, resolved=len(BASE_GRAPH), downloaded=0) + if (project / ".zpkg.lock").read_bytes() != lock_before: + raise AssertionError("warm ordinary install changed lockfile bytes") + if assert_symlink_install(project, shared_home, all_base) != initial_targets: + raise AssertionError("warm ordinary install changed immutable store targets") + + broken = project / "zed_modules" / ORG / "leaf-a" + broken.unlink() + repair = run( + zed_command( + zed, + registry, + shared_home, + "install", + "--frozen", + "--adapter", + "none", + ), + cwd=project, + env=environment, + ) + require_success(repair) + assert_prefetch(repair, resolved=len(BASE_GRAPH), downloaded=0) + if (project / ".zpkg.lock").read_bytes() != lock_before: + raise AssertionError("frozen repair changed lockfile bytes") + assert_symlink_install(project, shared_home, all_base) + + selective_uninstall = run( + zed_command(zed, registry, shared_home, "uninstall", f"{ORG}/root"), + cwd=project, + env=environment, + ) + require_success(selective_uninstall) + assert_not_materialized(project, ("root",)) + assert_symlink_install(project, shared_home, ("leaf-a", "leaf-b")) + if (project / ".zpkg.lock").read_bytes() != lock_before: + raise AssertionError("selective uninstall changed lockfile bytes") + selective_restore = run( + zed_command( + zed, + registry, + shared_home, + "install", + "--frozen", + "--adapter", + "none", + ), + cwd=project, + env=environment, + ) + require_success(selective_restore) + assert_prefetch(selective_restore, resolved=len(BASE_GRAPH), downloaded=0) + assert_symlink_install(project, shared_home, all_base) + + uninstall_all = run( + zed_command(zed, registry, shared_home, "uninstall"), + cwd=project, + env=environment, + ) + require_success(uninstall_all) + assert_not_materialized(project, all_base) + if (project / ".zpkg.lock").read_bytes() != lock_before: + raise AssertionError("full uninstall changed lockfile bytes") + if (project / ".zpkg.toml").read_bytes() != manifest_before: + raise AssertionError("full uninstall changed manifest bytes") + full_restore = run( + zed_command( + zed, + registry, + shared_home, + "install", + "--frozen", + "--adapter", + "none", + ), + cwd=project, + env=environment, + ) + require_success(full_restore) + assert_prefetch(full_restore, resolved=len(BASE_GRAPH), downloaded=0) + assert_symlink_install(project, shared_home, all_base) + + write_consumer(copy_project, ("root",)) + copy_install = run( + zed_command( + zed, + registry, + copy_home, + "install", + "--install-mode", + "copy", + "--adapter", + "none", + ), + cwd=copy_project, + env=environment, + ) + require_success(copy_install) + assert_prefetch(copy_install, resolved=len(BASE_GRAPH), downloaded=len(BASE_GRAPH)) + assert_lock(copy_project, all_base) + assert_copy_install(copy_project, all_base) + copy_lock = (copy_project / ".zpkg.lock").read_bytes() + copy_warm = run( + zed_command( + zed, + registry, + copy_home, + "install", + "--frozen", + "--install-mode", + "copy", + "--adapter", + "none", + ), + cwd=copy_project, + env=environment, + ) + require_success(copy_warm) + assert_prefetch(copy_warm, resolved=len(BASE_GRAPH), downloaded=0) + if (copy_project / ".zpkg.lock").read_bytes() != copy_lock: + raise AssertionError("warm copy-mode frozen install changed lockfile bytes") + assert_copy_install(copy_project, all_base) + + good_lock_text = (project / ".zpkg.lock").read_text(encoding="utf-8") + write_consumer(tamper_project, ("root",)) + (tamper_project / ".zpkg.lock").write_text( + tamper_first_sha(good_lock_text), encoding="utf-8" + ) + tampered_before = (tamper_project / ".zpkg.lock").read_bytes() + tampered = run( + zed_command( + zed, + registry, + tamper_home, + "install", + "--frozen", + "--adapter", + "none", + ), + cwd=tamper_project, + env=environment, + ) + require_failure(tampered, "registry artifact", "changed") + if (tamper_project / ".zpkg.lock").read_bytes() != tampered_before: + raise AssertionError("failed tampered-lock install changed lockfile bytes") + assert_not_materialized(tamper_project, all_base) + assert_no_transaction_leaks(tamper_project) + assert_no_cache_staging(tamper_home) + + write_consumer(duplicate_project, ("root",)) + (duplicate_project / ".zpkg.lock").write_text( + duplicate_first_package(good_lock_text), encoding="utf-8" + ) + duplicate_before = (duplicate_project / ".zpkg.lock").read_bytes() + duplicate = run( + zed_command( + zed, + registry, + duplicate_home, + "install", + "--frozen", + "--adapter", + "none", + ), + cwd=duplicate_project, + env=environment, + ) + require_failure(duplicate, "duplicate package") + if (duplicate_project / ".zpkg.lock").read_bytes() != duplicate_before: + raise AssertionError("failed duplicate-lock install changed lockfile bytes") + assert_not_materialized(duplicate_project, all_base) + assert_no_transaction_leaks(duplicate_project) + + # A new direct dependency whose registry artifact is unavailable must not + # damage the already-installed graph or rewrite its last known-good lock. + extra_source = sources / "extra" + write_package(extra_source, "extra", ()) + publish(zed, registry, publish_home, extra_source, environment) + extra_sha, extra_artifact = registry_artifact(registry, "extra") + extra_bytes = extra_artifact.read_bytes() + extra_artifact.unlink() + write_consumer(project, ("root", "extra")) + preserved_targets = assert_symlink_install(project, shared_home, all_base) + unavailable = run( + zed_command(zed, registry, shared_home, "install", "--adapter", "none"), + cwd=project, + env=environment, + ) + require_failure(unavailable) + if (project / ".zpkg.lock").read_bytes() != lock_before: + raise AssertionError("missing-artifact failure replaced the good lockfile") + if assert_symlink_install(project, shared_home, all_base) != preserved_targets: + raise AssertionError("missing-artifact failure changed the installed graph") + assert_not_materialized(project, ("extra",)) + assert_no_transaction_leaks(project) + assert_no_cache_staging(shared_home) + if (shared_home / "cache" / f"{extra_sha}.tar.gz").exists(): + raise AssertionError("missing artifact produced a final cache entry") + + extra_artifact.write_bytes(extra_bytes) + recovered = run( + zed_command(zed, registry, shared_home, "install", "--adapter", "none"), + cwd=project, + env=environment, + ) + require_success(recovered) + assert_prefetch(recovered, resolved=len(BASE_GRAPH) + 1, downloaded=1) + extended = (*all_base, "extra") + assert_lock(project, extended) + assert_symlink_install(project, shared_home, extended) + assert_no_cache_staging(shared_home) + + # Corrupt bytes must fail before project mutation, leave no final cache + # entry for the bad hash, then recover by downloading exactly that hash. + write_consumer(corrupt_project, ("root",)) + corrupt_sha, corrupt_artifact = registry_artifact(registry, "leaf-b") + corrupt_bytes = corrupt_artifact.read_bytes() + corrupt_artifact.write_bytes(b"not a valid zed artifact\n") + corrupt = run( + zed_command(zed, registry, corrupt_home, "install", "--adapter", "none"), + cwd=corrupt_project, + env=environment, + ) + require_failure(corrupt, "artifact hash mismatch") + if (corrupt_project / ".zpkg.lock").exists(): + raise AssertionError("corrupt-artifact failure wrote a lockfile") + assert_not_materialized(corrupt_project, all_base) + assert_no_transaction_leaks(corrupt_project) + assert_no_cache_staging(corrupt_home) + if (corrupt_home / "cache" / f"{corrupt_sha}.tar.gz").exists(): + raise AssertionError("corrupt artifact was published to the final cache path") + + corrupt_artifact.write_bytes(corrupt_bytes) + corrupt_recovery = run( + zed_command(zed, registry, corrupt_home, "install", "--adapter", "none"), + cwd=corrupt_project, + env=environment, + ) + require_success(corrupt_recovery) + recovery_summary = parse_prefetch(corrupt_recovery.output) + if recovery_summary[:2] != (len(BASE_GRAPH), 5) or recovery_summary[2] < 1: + raise AssertionError(f"unexpected corrupt-artifact recovery summary: {recovery_summary}") + assert_lock(corrupt_project, all_base) + assert_symlink_install(corrupt_project, corrupt_home, all_base) + assert_no_cache_staging(corrupt_home) + + print("\ninstall recovery matrix passed", flush=True) + print(f"same-project concurrent summaries: {summaries}", flush=True) + print(f"corrupt recovery summary: {recovery_summary}", flush=True) + print(f"extended manifest dependencies: {sorted(manifest_dependencies(project))}", flush=True) + return 0 + + +if __name__ == "__main__": + try: + raise SystemExit(main()) + except Exception as error: # noqa: BLE001 - CI boundary must print full context. + print(f"error: {error}", file=sys.stderr, flush=True) + raise From cb9bf8f007083f9803d3219e8d25292bafa6c7fd Mon Sep 17 00:00:00 2001 From: Alexander Mills Date: Mon, 3 Aug 2026 14:51:30 -0500 Subject: [PATCH 33/61] ci: run same-project install recovery and rollback matrix --- .github/workflows/install-recovery.yml | 135 +++++++++++++++++++++++++ 1 file changed, 135 insertions(+) create mode 100644 .github/workflows/install-recovery.yml diff --git a/.github/workflows/install-recovery.yml b/.github/workflows/install-recovery.yml new file mode 100644 index 00000000..fe163e00 --- /dev/null +++ b/.github/workflows/install-recovery.yml @@ -0,0 +1,135 @@ +name: install recovery matrix + +on: + pull_request: + paths: + - scripts/install_recovery_matrix.py + - .github/workflows/install-recovery.yml + push: + branches: [main] + paths: + - scripts/install_recovery_matrix.py + - .github/workflows/install-recovery.yml + workflow_dispatch: + inputs: + zed_cli_ref: + description: Immutable zed-cli commit to test + required: false + default: 67c01b83c834ea14af996613f1a3dc0802a2fbd3 + +permissions: + contents: read + +concurrency: + group: install-recovery-${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +defaults: + run: + shell: bash + +env: + CARGO_TERM_COLOR: always + RUST_BACKTRACE: "1" + ZED_CLI_REF: ${{ github.event.inputs.zed_cli_ref || '67c01b83c834ea14af996613f1a3dc0802a2fbd3' }} + ZED_INTERFACES_REF: dc0e0a0620b9462817950b552d3d334a184b1cb1 + +jobs: + install-recovery: + name: Same-project races, rollback, repair, and tamper failures + runs-on: ubuntu-24.04 + timeout-minutes: 45 + steps: + - name: Require immutable component pins + run: | + set -euo pipefail + [[ "$ZED_CLI_REF" =~ ^[0-9a-f]{40}$ ]] + [[ "$ZED_INTERFACES_REF" =~ ^[0-9a-f]{40}$ ]] + + - name: Check out e2e harness + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + path: harness + fetch-depth: 1 + persist-credentials: false + show-progress: false + + - name: Check out zed-cli + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + repository: zed-pkg/zed-cli + ref: ${{ env.ZED_CLI_REF }} + path: zed-cli + fetch-depth: 1 + persist-credentials: false + show-progress: false + + - name: Check out pinned zed-interfaces sibling + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + repository: zed-pkg/zed-interfaces + ref: ${{ env.ZED_INTERFACES_REF }} + path: zed-interfaces + fetch-depth: 1 + persist-credentials: false + show-progress: false + + - name: Install stable Rust + run: | + set -euo pipefail + rustup toolchain install stable --profile minimal --component rustfmt + rustup default stable + rustc --version --verbose + cargo --version --verbose + + - name: Compile the recovery harness and check Rust formatting + run: | + set -euo pipefail + python3 -m py_compile harness/scripts/install_recovery_matrix.py + cargo fmt --all --check --manifest-path zed-cli/Cargo.toml + + - name: Build immutable zed binary + run: | + set -euo pipefail + cargo build --locked --release --manifest-path zed-cli/Cargo.toml --bin zed + install -Dm755 zed-cli/target/release/zed "$RUNNER_TEMP/zed-bin/zed" + "$RUNNER_TEMP/zed-bin/zed" --version + + - name: Run install recovery and rollback matrix + env: + WORK_ROOT: ${{ runner.temp }}/zed-install-recovery + run: | + set -euo pipefail + test ! -e "$WORK_ROOT" + python3 harness/scripts/install_recovery_matrix.py \ + --zed "$RUNNER_TEMP/zed-bin/zed" \ + --work-root "$WORK_ROOT" + + - name: Collect failure diagnostics + if: failure() + env: + WORK_ROOT: ${{ runner.temp }}/zed-install-recovery + run: | + set +e + mkdir -p "$WORK_ROOT/diagnostics" + find "$WORK_ROOT" -maxdepth 11 -printf '%y %P -> %l\n' \ + | LC_ALL=C sort > "$WORK_ROOT/diagnostics/work-tree.txt" 2>&1 + find "$WORK_ROOT/registry" -maxdepth 10 -type f -print0 2>/dev/null \ + | sort -z | xargs -0 -r sha256sum \ + > "$WORK_ROOT/diagnostics/registry-sha256.txt" 2>&1 + find "$WORK_ROOT" -path '*/cache/*.tar.gz' -type f -print0 2>/dev/null \ + | sort -z | xargs -0 -r sha256sum \ + > "$WORK_ROOT/diagnostics/cache-sha256.txt" 2>&1 + find "$WORK_ROOT" -path '*/.zpkg.lock' -type f -print -exec sed -n '1,240p' {} \; \ + > "$WORK_ROOT/diagnostics/lockfiles.txt" 2>&1 + true + + - name: Upload failure diagnostics + if: failure() + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: install-recovery-${{ github.run_id }}-${{ github.run_attempt }} + path: ${{ runner.temp }}/zed-install-recovery/diagnostics + if-no-files-found: warn + retention-days: 14 + include-hidden-files: false From b1ffa05e3510921053216f608bf042517bc0cb99 Mon Sep 17 00:00:00 2001 From: Alexander Mills Date: Mon, 3 Aug 2026 14:54:18 -0500 Subject: [PATCH 34/61] test: add real zed-pkg-test app and workspace install matrix --- scripts/org_app_installs.py | 708 ++++++++++++++++++++++++++++++++++++ 1 file changed, 708 insertions(+) create mode 100644 scripts/org_app_installs.py diff --git a/scripts/org_app_installs.py b/scripts/org_app_installs.py new file mode 100644 index 00000000..c06859af --- /dev/null +++ b/scripts/org_app_installs.py @@ -0,0 +1,708 @@ +#!/usr/bin/env python3 +"""Install real zed-pkg-test app fixtures and verify adapter/workspace behavior.""" + +from __future__ import annotations + +import argparse +import json +import os +import re +import shutil +import subprocess +import sys +import tomllib +from dataclasses import dataclass +from pathlib import Path +from typing import Mapping, Sequence + +GITHUB_ORG = "zed-pkg-test" +APP_REPOS = ( + "node-app", + "rust-app", + "go-app", + "python-app", + "dart-app", + "gleam-app", + "polyglot-node-app", + "polyglot-go-app", +) +PACKAGE_SOURCES: dict[str, tuple[str, str]] = { + "zed-pkg-test/node-lib": ("node-lib", "."), + "zed-pkg-test/rust-lib": ("rust-lib", "."), + "zed-pkg-test/go-lib": ("go-lib", "."), + "zed-pkg-test/python-lib": ("python-lib", "."), + "zed-pkg-test/dart-lib": ("dart-lib", "."), + "zed-pkg-test/gleam-lib": ("gleam-lib", "."), + "zedtest/polyglot-lib-nodejs": ("polyglot-lib", "."), + "zedtest/polyglot-lib-python": ("polyglot-lib", "."), + "zedtest/polyglot-lib-golang": ("polyglot-lib", "."), + "zedtest/polyglot-lib-rust": ("polyglot-lib", "."), + "zedtest/shared-schema": ("shared-schema", "."), +} +EXPECTED_ADAPTER = { + "node-app": "node", + "rust-app": "rust", + "go-app": "go", + "python-app": "python", + "dart-app": "dart", + "gleam-app": "none", + "polyglot-node-app": "node", + "polyglot-go-app": "go", +} +PREFETCH_RE = re.compile( + r"recursive install prefetch: (?P\d+) package\(s\), " + r"up to (?P\d+) concurrent, (?P\d+) downloaded" +) + + +@dataclass(frozen=True) +class PackageRef: + full_name: str + version: str + + @property + def org(self) -> str: + return self.full_name.split("/", 1)[0] + + @property + def name(self) -> str: + return self.full_name.split("/", 1)[1] + + +@dataclass(frozen=True) +class Completed: + command: tuple[str, ...] + returncode: int + output: str + + +def shell_quote(value: str) -> str: + if re.fullmatch(r"[A-Za-z0-9_./:@%+=,-]+", value): + return value + return "'" + value.replace("'", "'\"'\"'") + "'" + + +def run( + command: Sequence[str | Path], + *, + cwd: Path | None = None, + env: Mapping[str, str] | None = None, + timeout: int = 300, +) -> Completed: + argv = tuple(str(value) for value in command) + shown = " ".join(shell_quote(value) for value in argv) + print(f"\n$ (cd {cwd or Path.cwd()} && {shown})", flush=True) + completed = subprocess.run( + argv, + cwd=cwd, + env=dict(env) if env is not None else None, + text=True, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + check=False, + timeout=timeout, + ) + if completed.stdout: + print(completed.stdout, end="", flush=True) + return Completed(argv, completed.returncode, completed.stdout) + + +def require_success(completed: Completed) -> None: + if completed.returncode != 0: + shown = " ".join(shell_quote(value) for value in completed.command) + raise RuntimeError(f"command failed with exit code {completed.returncode}: {shown}") + + +def zed_command(zed: Path, registry: Path, home: Path, *args: str) -> list[str | Path]: + return [ + zed, + "--registry", + f"file://{registry}", + "--home", + home, + *args, + ] + + +def read_manifest(directory: Path) -> dict: + with (directory / ".zpkg.toml").open("rb") as handle: + return tomllib.load(handle) + + +def dependencies(manifest: dict) -> dict[str, str]: + section = manifest.get("dependencies") or {} + if not isinstance(section, dict): + raise AssertionError("[dependencies] must be a table") + return {str(key): str(value) for key, value in section.items()} + + +def expected_packages(manifest: dict) -> list[PackageRef]: + package = manifest["package"] + org = str(package["org"]) + base_name = str(package["name"]) + version = str(package["version"]) + targets = manifest.get("targets") or {} + if not targets: + return [PackageRef(f"{org}/{base_name}", version)] + result: list[PackageRef] = [] + for target_name, target in sorted(targets.items()): + target_table = target or {} + name = str(target_table.get("name") or f"{base_name}-{target_name}") + result.append(PackageRef(f"{org}/{name}", version)) + return result + + +def modules_dir(manifest: dict) -> str: + install = manifest.get("install") or {} + return str(install.get("dir") or "zed_modules") + + +def clone_repo(root: Path, repo: str, clones: dict[str, Path]) -> Path: + if repo in clones: + return clones[repo] + destination = root / repo + completed = run( + [ + "git", + "clone", + "--depth", + "1", + "--recurse-submodules", + "--shallow-submodules", + f"https://github.com/{GITHUB_ORG}/{repo}.git", + destination, + ], + timeout=300, + ) + require_success(completed) + clones[repo] = destination + return destination + + +def copy_fixture(source: Path, destination: Path) -> None: + def ignore(directory: str, names: list[str]) -> set[str]: + ignored = {name for name in names if name in {".git", ".zed", ".zpkg-staging"}} + if Path(directory) == source: + ignored.update(name for name in names if name == ".zpkg.lock") + return ignored + + shutil.copytree(source, destination, symlinks=True, ignore=ignore) + + +def registry_metadata_path(registry: Path, package: PackageRef) -> Path: + return ( + registry + / "packages" + / package.org + / package.name + / "versions" + / f"{package.version}.json" + ) + + +def assert_registry_package(registry: Path, package: PackageRef) -> None: + metadata_path = registry_metadata_path(registry, package) + if not metadata_path.is_file(): + raise AssertionError(f"missing registry metadata: {metadata_path}") + metadata = json.loads(metadata_path.read_text(encoding="utf-8")) + sha256 = str(metadata.get("sha256") or "") + if not re.fullmatch(r"[0-9a-f]{64}", sha256): + raise AssertionError(f"invalid SHA in {metadata_path}: {sha256!r}") + artifact = registry / "artifacts" / f"{sha256}.tar.gz" + if not artifact.is_file(): + raise AssertionError(f"missing registry artifact: {artifact}") + + +def parse_lock(project: Path) -> list[dict[str, object]]: + with (project / ".zpkg.lock").open("rb") as handle: + document = tomllib.load(handle) + packages = document.get("package") or [] + if not isinstance(packages, list): + raise AssertionError(f"invalid lock package table in {project}") + return packages + + +def package_key(package: Mapping[str, object]) -> str: + return f"{package['org']}/{package['name']}" + + +def materialized_path(project: Path, install_dir: str, package: Mapping[str, object]) -> Path: + return project / install_dir / str(package["org"]) / str(package["name"]) + + +def assert_symlink_graph( + project: Path, + home: Path, + install_dir: str, + packages: Sequence[Mapping[str, object]], +) -> dict[str, Path]: + store = (home / "store").resolve() + targets: dict[str, Path] = {} + for package in packages: + key = package_key(package) + installed = materialized_path(project, install_dir, package) + if not installed.is_symlink(): + raise AssertionError(f"{project.name}: expected symlink for {key}: {installed}") + target = installed.resolve(strict=True) + try: + target.relative_to(store) + except ValueError as error: + raise AssertionError(f"{installed} resolves outside {store}: {target}") from error + if not (target / ".zpkg.toml").is_file(): + raise AssertionError(f"installed artifact has no manifest: {target}") + targets[key] = target + return targets + + +def assert_paths_index(project: Path, keys: Sequence[str]) -> None: + path = project / ".zed" / "paths.json" + if not path.is_file(): + raise AssertionError(f"missing adapter-independent path index: {path}") + text = path.read_text(encoding="utf-8") + json.loads(text) + for key in keys: + if key not in text: + raise AssertionError(f"{path} does not mention installed package {key}") + + +def assert_adapter( + project: Path, + adapter: str, + install_dir: str, + packages: Sequence[Mapping[str, object]], +) -> None: + keys = [package_key(package) for package in packages] + assert_paths_index(project, keys) + zed_dir = project / ".zed" + + if adapter == "node": + node_path = zed_dir / "node_path" + if node_path.read_text(encoding="utf-8").strip() != install_dir: + raise AssertionError(f"unexpected node_path in {project}: {node_path.read_text()!r}") + for package in packages: + node_link = ( + project + / "node_modules" + / f"@{package['org']}" + / str(package["name"]) + ) + if not node_link.is_symlink(): + raise AssertionError(f"missing Node adapter symlink: {node_link}") + if node_link.resolve(strict=True) != materialized_path( + project, install_dir, package + ).resolve(strict=True): + raise AssertionError(f"Node adapter target mismatch: {node_link}") + elif adapter == "go": + wiring = zed_dir / "go.work" + if not wiring.is_file(): + raise AssertionError(f"missing Go wiring: {wiring}") + text = wiring.read_text(encoding="utf-8") + for package in packages: + if str(package["name"]) not in text: + raise AssertionError(f"{wiring} omits {package_key(package)}") + elif adapter == "python": + wiring = zed_dir / "pythonpath" + if not wiring.is_file(): + raise AssertionError(f"missing Python wiring: {wiring}") + text = wiring.read_text(encoding="utf-8") + for package in packages: + if str(package["name"]) not in text: + raise AssertionError(f"{wiring} omits {package_key(package)}") + elif adapter == "rust": + wiring = zed_dir / "cargo-paths.toml" + if not wiring.is_file(): + raise AssertionError(f"missing Rust wiring: {wiring}") + text = wiring.read_text(encoding="utf-8") + for package in packages: + if str(package["name"]) not in text: + raise AssertionError(f"{wiring} omits {package_key(package)}") + elif adapter == "dart": + wiring = zed_dir / "pub-deps.yaml" + if not wiring.is_file(): + raise AssertionError(f"missing Dart wiring: {wiring}") + text = wiring.read_text(encoding="utf-8") + for package in packages: + if str(package["name"]) not in text: + raise AssertionError(f"{wiring} omits {package_key(package)}") + elif adapter != "none": + raise AssertionError(f"unknown expected adapter: {adapter}") + + +def assert_not_materialized( + project: Path, + install_dir: str, + packages: Sequence[Mapping[str, object]], +) -> None: + for package in packages: + path = materialized_path(project, install_dir, package) + if path.exists() or path.is_symlink(): + raise AssertionError(f"uninstall left {path}") + node_path = ( + project + / "node_modules" + / f"@{package['org']}" + / str(package["name"]) + ) + if node_path.exists() or node_path.is_symlink(): + raise AssertionError(f"uninstall left Node adapter path {node_path}") + + +def assert_no_staging(project: Path) -> None: + staging = project / ".zpkg-staging" + if staging.exists(): + raise AssertionError(f"transaction staging leaked in {project}: {list(staging.iterdir())}") + + +def parse_prefetch(output: str) -> tuple[int, int, int]: + matches = list(PREFETCH_RE.finditer(output)) + if len(matches) != 1: + raise AssertionError(f"expected one prefetch summary, got {len(matches)}:\n{output}") + match = matches[0] + return tuple( + int(match.group(field)) + for field in ("resolved", "concurrency", "downloaded") + ) + + +def publish_prerequisite( + package_key_value: str, + *, + zed: Path, + registry: Path, + publish_home: Path, + clone_root: Path, + clones: dict[str, Path], + published_sources: set[tuple[str, str]], + environment: Mapping[str, str], +) -> None: + try: + repo, relative = PACKAGE_SOURCES[package_key_value] + except KeyError as error: + raise AssertionError( + f"no source mapping for dependency {package_key_value!r}" + ) from error + source_root = clone_repo(clone_root, repo, clones) + source = (source_root / relative).resolve() + source_manifest = read_manifest(source) + for dependency in dependencies(source_manifest): + publish_prerequisite( + dependency, + zed=zed, + registry=registry, + publish_home=publish_home, + clone_root=clone_root, + clones=clones, + published_sources=published_sources, + environment=environment, + ) + + source_key = (repo, relative) + if source_key in published_sources: + return + completed = run( + zed_command( + zed, + registry, + publish_home, + "publish", + "--skip-vcs-checks", + ), + cwd=source, + env=environment, + ) + require_success(completed) + published_sources.add(source_key) + for output in expected_packages(source_manifest): + assert_registry_package(registry, output) + + +def exercise_app( + repo: str, + *, + zed: Path, + registry: Path, + shared_home: Path, + clone_root: Path, + app_root: Path, + clones: dict[str, Path], + published_sources: set[tuple[str, str]], + environment: Mapping[str, str], +) -> tuple[int, set[str]]: + source = clone_repo(clone_root, repo, clones) + source_manifest = read_manifest(source) + direct = dependencies(source_manifest) + if not direct: + raise AssertionError(f"app fixture {repo} has no Zed dependencies") + for dependency in direct: + publish_prerequisite( + dependency, + zed=zed, + registry=registry, + publish_home=app_root.parent / "publish-home", + clone_root=clone_root, + clones=clones, + published_sources=published_sources, + environment=environment, + ) + + project = app_root / repo + copy_fixture(source, project) + manifest_bytes = (project / ".zpkg.toml").read_bytes() + install_dir = modules_dir(source_manifest) + + cold = run( + zed_command(zed, registry, shared_home, "install"), + cwd=project, + env=environment, + ) + require_success(cold) + packages = parse_lock(project) + keys = {package_key(package) for package in packages} + if not set(direct).issubset(keys): + raise AssertionError( + f"{repo}: lock omits direct dependencies {sorted(set(direct) - keys)}" + ) + summary = parse_prefetch(cold.output) + if summary[:2] != (len(packages), 5): + raise AssertionError(f"{repo}: unexpected cold prefetch summary {summary}") + + targets = assert_symlink_graph(project, shared_home, install_dir, packages) + assert_adapter(project, EXPECTED_ADAPTER[repo], install_dir, packages) + assert_no_staging(project) + lock_bytes = (project / ".zpkg.lock").read_bytes() + + warm = run( + zed_command(zed, registry, shared_home, "install", "--frozen"), + cwd=project, + env=environment, + ) + require_success(warm) + if parse_prefetch(warm.output) != (len(packages), 5, 0): + raise AssertionError(f"{repo}: warm frozen install downloaded bytes") + if (project / ".zpkg.lock").read_bytes() != lock_bytes: + raise AssertionError(f"{repo}: warm frozen install rewrote the lockfile") + if assert_symlink_graph(project, shared_home, install_dir, packages) != targets: + raise AssertionError(f"{repo}: warm frozen install changed store targets") + + first = packages[0] + materialized_path(project, install_dir, first).unlink() + repaired = run( + zed_command(zed, registry, shared_home, "install", "--frozen"), + cwd=project, + env=environment, + ) + require_success(repaired) + if parse_prefetch(repaired.output) != (len(packages), 5, 0): + raise AssertionError(f"{repo}: repair downloaded bytes") + assert_symlink_graph(project, shared_home, install_dir, packages) + assert_adapter(project, EXPECTED_ADAPTER[repo], install_dir, packages) + + uninstalled = run( + zed_command(zed, registry, shared_home, "uninstall"), + cwd=project, + env=environment, + ) + require_success(uninstalled) + assert_not_materialized(project, install_dir, packages) + if (project / ".zpkg.lock").read_bytes() != lock_bytes: + raise AssertionError(f"{repo}: uninstall rewrote the retained lockfile") + if (project / ".zpkg.toml").read_bytes() != manifest_bytes: + raise AssertionError(f"{repo}: uninstall rewrote the manifest") + assert_no_staging(project) + + restored = run( + zed_command(zed, registry, shared_home, "install", "--frozen"), + cwd=project, + env=environment, + ) + require_success(restored) + if parse_prefetch(restored.output) != (len(packages), 5, 0): + raise AssertionError(f"{repo}: post-uninstall restore downloaded bytes") + assert_symlink_graph(project, shared_home, install_dir, packages) + assert_adapter(project, EXPECTED_ADAPTER[repo], install_dir, packages) + assert_no_staging(project) + + return summary[2], {str(package["sha256"]) for package in packages} + + +def exercise_workspace( + *, + zed: Path, + registry: Path, + home: Path, + clone_root: Path, + workspace_root: Path, + clones: dict[str, Path], + environment: Mapping[str, str], +) -> None: + source = clone_repo(clone_root, "workspace-monorepo", clones) + + symlink_repo = workspace_root / "symlink" + copy_fixture(source, symlink_repo) + symlink_project = symlink_repo / "apps" / "cli" + symlink_install = run( + zed_command(zed, registry, home, "install"), + cwd=symlink_project, + env=environment, + ) + require_success(symlink_install) + + expected_sources = { + "ws-utils": symlink_repo / "packages" / "utils", + "ws-core": symlink_repo / "packages" / "core", + } + for name, expected_source in expected_sources.items(): + installed = symlink_project / "zed_modules" / "zedtest" / name + if not installed.is_symlink(): + raise AssertionError(f"workspace dependency was not symlinked: {installed}") + if installed.resolve(strict=True) != expected_source.resolve(strict=True): + raise AssertionError( + f"workspace link target mismatch: {installed} -> {installed.resolve()}" + ) + node_link = symlink_project / "node_modules" / "@zedtest" / name + if not node_link.is_symlink() or node_link.resolve(strict=True) != expected_source.resolve( + strict=True + ): + raise AssertionError(f"workspace Node adapter mismatch: {node_link}") + + live_sentinel = symlink_repo / "packages" / "core" / "live-sentinel.txt" + live_sentinel.write_text("visible after install\n", encoding="utf-8") + visible = ( + symlink_project + / "zed_modules" + / "zedtest" + / "ws-core" + / "live-sentinel.txt" + ) + if visible.read_text(encoding="utf-8") != "visible after install\n": + raise AssertionError("workspace symlink did not expose a post-install source edit") + + copy_repo = workspace_root / "copy" + copy_fixture(source, copy_repo) + copy_source_sentinel = copy_repo / "packages" / "core" / "copy-sentinel.txt" + copy_source_sentinel.write_text("before install\n", encoding="utf-8") + copy_project = copy_repo / "apps" / "cli" + copy_install = run( + zed_command( + zed, + registry, + home, + "install", + "--install-mode", + "copy", + ), + cwd=copy_project, + env=environment, + ) + require_success(copy_install) + copied = copy_project / "zed_modules" / "zedtest" / "ws-core" + if copied.is_symlink() or not copied.is_dir(): + raise AssertionError(f"workspace copy mode did not copy {copied}") + copied_sentinel = copied / "copy-sentinel.txt" + if copied_sentinel.read_text(encoding="utf-8") != "before install\n": + raise AssertionError("workspace copy omitted source content") + copy_source_sentinel.write_text("after install\n", encoding="utf-8") + if copied_sentinel.read_text(encoding="utf-8") != "before install\n": + raise AssertionError("workspace copy remained coupled to mutable source") + leaked = [path for path in (copy_project / "zed_modules").rglob("*") if path.is_symlink()] + if leaked: + raise AssertionError(f"workspace copy mode leaked symlinks: {leaked}") + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("--zed", type=Path, required=True) + parser.add_argument("--work-root", type=Path, required=True) + args = parser.parse_args() + + zed = args.zed.resolve() + root = args.work_root.resolve() + if not zed.is_file(): + raise RuntimeError(f"zed binary does not exist: {zed}") + if root.exists(): + raise RuntimeError(f"work root must be fresh: {root}") + + registry = root / "registry" + publish_home = root / "publish-home" + shared_home = root / "shared-home" + clone_root = root / "clones" + app_root = root / "apps" + workspace_root = root / "workspace" + for directory in (registry, clone_root, app_root, workspace_root): + directory.mkdir(parents=True, exist_ok=True) + + environment = os.environ.copy() + environment.update( + { + "CI": "true", + "GIT_TERMINAL_PROMPT": "0", + "RUST_BACKTRACE": "1", + "ZED_PKG_INSTALL_CONCURRENCY": "5", + } + ) + + clones: dict[str, Path] = {} + published_sources: set[tuple[str, str]] = set() + total_downloaded = 0 + unique_shas: set[str] = set() + per_app: dict[str, int] = {} + + for repo in APP_REPOS: + downloaded, shas = exercise_app( + repo, + zed=zed, + registry=registry, + shared_home=shared_home, + clone_root=clone_root, + app_root=app_root, + clones=clones, + published_sources=published_sources, + environment=environment, + ) + total_downloaded += downloaded + unique_shas.update(shas) + per_app[repo] = downloaded + + if total_downloaded != len(unique_shas): + raise AssertionError( + "shared-home app installs did not download each immutable hash exactly once: " + f"downloaded={total_downloaded}, unique_shas={len(unique_shas)}, per_app={per_app}" + ) + + exercise_workspace( + zed=zed, + registry=registry, + home=shared_home, + clone_root=clone_root, + workspace_root=workspace_root, + clones=clones, + environment=environment, + ) + + cache_files = sorted((shared_home / "cache").glob("*.tar.gz")) + if len(cache_files) != len(unique_shas): + raise AssertionError( + f"shared cache cardinality mismatch: {len(cache_files)} != {len(unique_shas)}" + ) + temporary = sorted((shared_home / "cache").glob("**/artifact.download")) + cache_directories = sorted( + path for path in (shared_home / "cache").iterdir() if path.is_dir() + ) + if temporary or cache_directories: + raise AssertionError( + f"shared cache leaked staging: downloads={temporary}, dirs={cache_directories}" + ) + + print("\nzed-pkg-test app install matrix passed", flush=True) + print(f"apps: {', '.join(APP_REPOS)}", flush=True) + print(f"cold downloads by app: {per_app}", flush=True) + print(f"unique immutable artifacts: {len(unique_shas)}", flush=True) + return 0 + + +if __name__ == "__main__": + try: + raise SystemExit(main()) + except Exception as error: # noqa: BLE001 - CI boundary must print full context. + print(f"error: {error}", file=sys.stderr, flush=True) + raise From 5ef45b3c71af398f8c0f4a84a25ddf04b3ba2369 Mon Sep 17 00:00:00 2001 From: Alexander Mills Date: Mon, 3 Aug 2026 14:54:44 -0500 Subject: [PATCH 35/61] ci: install real zed-pkg-test apps and workspace members --- .github/workflows/org-app-installs.yml | 136 +++++++++++++++++++++++++ 1 file changed, 136 insertions(+) create mode 100644 .github/workflows/org-app-installs.yml diff --git a/.github/workflows/org-app-installs.yml b/.github/workflows/org-app-installs.yml new file mode 100644 index 00000000..f17e9c00 --- /dev/null +++ b/.github/workflows/org-app-installs.yml @@ -0,0 +1,136 @@ +name: zed-pkg-test app installs + +on: + pull_request: + paths: + - scripts/org_app_installs.py + - .github/workflows/org-app-installs.yml + push: + branches: [main] + paths: + - scripts/org_app_installs.py + - .github/workflows/org-app-installs.yml + workflow_dispatch: + inputs: + zed_cli_ref: + description: Immutable zed-cli commit to test + required: false + default: 67c01b83c834ea14af996613f1a3dc0802a2fbd3 + +permissions: + contents: read + +concurrency: + group: zed-pkg-test-app-installs-${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +defaults: + run: + shell: bash + +env: + CARGO_TERM_COLOR: always + RUST_BACKTRACE: "1" + ZED_CLI_REF: ${{ github.event.inputs.zed_cli_ref || '67c01b83c834ea14af996613f1a3dc0802a2fbd3' }} + ZED_INTERFACES_REF: dc0e0a0620b9462817950b552d3d334a184b1cb1 + +jobs: + org-app-installs: + name: Real app fixtures, adapters, uninstall/restore, and workspaces + runs-on: ubuntu-24.04 + timeout-minutes: 60 + steps: + - name: Require immutable component pins + run: | + set -euo pipefail + [[ "$ZED_CLI_REF" =~ ^[0-9a-f]{40}$ ]] + [[ "$ZED_INTERFACES_REF" =~ ^[0-9a-f]{40}$ ]] + + - name: Check out e2e harness + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + path: harness + fetch-depth: 1 + persist-credentials: false + show-progress: false + + - name: Check out zed-cli + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + repository: zed-pkg/zed-cli + ref: ${{ env.ZED_CLI_REF }} + path: zed-cli + fetch-depth: 1 + persist-credentials: false + show-progress: false + + - name: Check out pinned zed-interfaces sibling + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + repository: zed-pkg/zed-interfaces + ref: ${{ env.ZED_INTERFACES_REF }} + path: zed-interfaces + fetch-depth: 1 + persist-credentials: false + show-progress: false + + - name: Install stable Rust + run: | + set -euo pipefail + rustup toolchain install stable --profile minimal --component rustfmt + rustup default stable + rustc --version --verbose + cargo --version --verbose + git --version + python3 --version + + - name: Compile the org install harness and check Rust formatting + run: | + set -euo pipefail + python3 -m py_compile harness/scripts/org_app_installs.py + cargo fmt --all --check --manifest-path zed-cli/Cargo.toml + + - name: Build immutable zed binary + run: | + set -euo pipefail + cargo build --locked --release --manifest-path zed-cli/Cargo.toml --bin zed + install -Dm755 zed-cli/target/release/zed "$RUNNER_TEMP/zed-bin/zed" + "$RUNNER_TEMP/zed-bin/zed" --version + + - name: Install real applications from zed-pkg-test + env: + WORK_ROOT: ${{ runner.temp }}/zed-pkg-test-app-installs + run: | + set -euo pipefail + test ! -e "$WORK_ROOT" + python3 harness/scripts/org_app_installs.py \ + --zed "$RUNNER_TEMP/zed-bin/zed" \ + --work-root "$WORK_ROOT" + + - name: Collect failure diagnostics + if: failure() + env: + WORK_ROOT: ${{ runner.temp }}/zed-pkg-test-app-installs + run: | + set +e + mkdir -p "$WORK_ROOT/diagnostics" + find "$WORK_ROOT" -maxdepth 12 -printf '%y %P -> %l\n' \ + | LC_ALL=C sort > "$WORK_ROOT/diagnostics/work-tree.txt" 2>&1 + find "$WORK_ROOT/registry" -maxdepth 10 -type f -print0 2>/dev/null \ + | sort -z | xargs -0 -r sha256sum \ + > "$WORK_ROOT/diagnostics/registry-sha256.txt" 2>&1 + find "$WORK_ROOT" -path '*/.zpkg.lock' -type f -print -exec sed -n '1,260p' {} \; \ + > "$WORK_ROOT/diagnostics/lockfiles.txt" 2>&1 + find "$WORK_ROOT" -path '*/.zed/*' -type f -print -exec sed -n '1,200p' {} \; \ + > "$WORK_ROOT/diagnostics/adapter-files.txt" 2>&1 + true + + - name: Upload failure diagnostics + if: failure() + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: zed-pkg-test-app-installs-${{ github.run_id }}-${{ github.run_attempt }} + path: ${{ runner.temp }}/zed-pkg-test-app-installs/diagnostics + if-no-files-found: warn + retention-days: 14 + include-hidden-files: false From e88e43002521f8cdb039ba22e886938df1e1a390 Mon Sep 17 00:00:00 2001 From: Alexander Mills Date: Mon, 3 Aug 2026 15:07:52 -0500 Subject: [PATCH 36/61] test: stage immutable workspace-install head repin --- .github/repin_workspace_install_head.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 .github/repin_workspace_install_head.py diff --git a/.github/repin_workspace_install_head.py b/.github/repin_workspace_install_head.py new file mode 100644 index 00000000..7c7300b6 --- /dev/null +++ b/.github/repin_workspace_install_head.py @@ -0,0 +1,18 @@ +from pathlib import Path + + +OLD = "67c01b83c834ea14af996613f1a3dc0802a2fbd3" +NEW = "56e597b35deccc82bff7deee807f459a1def537d" +WORKFLOWS = [ + Path(".github/workflows/recursive-installs.yml"), + Path(".github/workflows/recursive-installs-stress.yml"), + Path(".github/workflows/install-recovery.yml"), + Path(".github/workflows/org-app-installs.yml"), +] + +for path in WORKFLOWS: + text = path.read_text(encoding="utf-8") + occurrences = text.count(OLD) + if occurrences != 2: + raise RuntimeError(f"{path}: expected two immutable pin occurrences, found {occurrences}") + path.write_text(text.replace(OLD, NEW), encoding="utf-8") From 9206d534348302b241525ab93e050630ced8ab4a Mon Sep 17 00:00:00 2001 From: Alexander Mills Date: Mon, 3 Aug 2026 15:08:06 -0500 Subject: [PATCH 37/61] ci: repin all install suites to workspace implementation --- .../apply-workspace-install-repin.yml | 73 +++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 .github/workflows/apply-workspace-install-repin.yml diff --git a/.github/workflows/apply-workspace-install-repin.yml b/.github/workflows/apply-workspace-install-repin.yml new file mode 100644 index 00000000..6cb23942 --- /dev/null +++ b/.github/workflows/apply-workspace-install-repin.yml @@ -0,0 +1,73 @@ +name: apply workspace install head repin + +on: + push: + branches: + - agent/recursive-install-e2e + paths: + - .github/repin_workspace_install_head.py + - .github/workflows/apply-workspace-install-repin.yml + workflow_dispatch: + +permissions: + contents: read + +concurrency: + group: apply-workspace-install-head-repin + cancel-in-progress: true + +jobs: + repin: + if: github.actor != 'github-actions[bot]' + runs-on: ubuntu-24.04 + timeout-minutes: 10 + permissions: + contents: write + steps: + - name: Check out E2E branch + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + ref: agent/recursive-install-e2e + fetch-depth: 0 + persist-credentials: false + show-progress: false + + - name: Apply and validate immutable pins + run: | + set -euo pipefail + python3 .github/repin_workspace_install_head.py + python3 - <<'PY' + from pathlib import Path + pin = "56e597b35deccc82bff7deee807f459a1def537d" + for path in [ + Path(".github/workflows/recursive-installs.yml"), + Path(".github/workflows/recursive-installs-stress.yml"), + Path(".github/workflows/install-recovery.yml"), + Path(".github/workflows/org-app-installs.yml"), + ]: + text = path.read_text() + assert text.count(pin) == 2, (path, text.count(pin)) + PY + git diff --check + + - name: Publish the validated workflow pins + env: + GH_TOKEN: ${{ github.token }} + run: | + set -euo pipefail + git add \ + .github/workflows/recursive-installs.yml \ + .github/workflows/recursive-installs-stress.yml \ + .github/workflows/install-recovery.yml \ + .github/workflows/org-app-installs.yml + git diff --cached --check + if git diff --cached --quiet; then + echo 'workspace implementation head already pinned' + exit 0 + fi + git config user.name 'github-actions[bot]' + git config user.email '41898282+github-actions[bot]@users.noreply.github.com' + git commit -m 'test: pin workspace install implementation' + git push \ + "https://x-access-token:${GH_TOKEN}@github.com/${GITHUB_REPOSITORY}.git" \ + HEAD:agent/recursive-install-e2e From 54767ce976b171e4a25ed1de5c044560e8b422d2 Mon Sep 17 00:00:00 2001 From: Alexander Mills Date: Mon, 3 Aug 2026 15:09:23 -0500 Subject: [PATCH 38/61] ci: trigger exact workspace install head repin --- .github/repin_workspace_install_head.py | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/repin_workspace_install_head.py b/.github/repin_workspace_install_head.py index 7c7300b6..0e31249e 100644 --- a/.github/repin_workspace_install_head.py +++ b/.github/repin_workspace_install_head.py @@ -1,6 +1,7 @@ from pathlib import Path +# One-shot exact-head repin for the workspace install implementation. OLD = "67c01b83c834ea14af996613f1a3dc0802a2fbd3" NEW = "56e597b35deccc82bff7deee807f459a1def537d" WORKFLOWS = [ From 032aa015a6cd02a69c711650a7aa5ae5babf7eda Mon Sep 17 00:00:00 2001 From: Alexander Mills Date: Mon, 3 Aug 2026 15:10:06 -0500 Subject: [PATCH 39/61] ci: trigger immutable install repin from the E2E PR --- .github/workflows/apply-workspace-install-repin.yml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/.github/workflows/apply-workspace-install-repin.yml b/.github/workflows/apply-workspace-install-repin.yml index 6cb23942..ff54ac07 100644 --- a/.github/workflows/apply-workspace-install-repin.yml +++ b/.github/workflows/apply-workspace-install-repin.yml @@ -7,13 +7,19 @@ on: paths: - .github/repin_workspace_install_head.py - .github/workflows/apply-workspace-install-repin.yml + pull_request: + branches: + - main + paths: + - .github/repin_workspace_install_head.py + - .github/workflows/apply-workspace-install-repin.yml workflow_dispatch: permissions: contents: read concurrency: - group: apply-workspace-install-head-repin + group: apply-workspace-install-head-repin-${{ github.event.pull_request.number || github.ref }} cancel-in-progress: true jobs: From 2d676501f09c51a06cdc4a6d12d914418fc78991 Mon Sep 17 00:00:00 2001 From: Alexander Mills Date: Mon, 3 Aug 2026 15:11:02 -0500 Subject: [PATCH 40/61] test: pin real org installs to workspace implementation --- .github/workflows/org-app-installs.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/org-app-installs.yml b/.github/workflows/org-app-installs.yml index f17e9c00..50e1c22d 100644 --- a/.github/workflows/org-app-installs.yml +++ b/.github/workflows/org-app-installs.yml @@ -15,7 +15,7 @@ on: zed_cli_ref: description: Immutable zed-cli commit to test required: false - default: 67c01b83c834ea14af996613f1a3dc0802a2fbd3 + default: 56e597b35deccc82bff7deee807f459a1def537d permissions: contents: read @@ -31,7 +31,7 @@ defaults: env: CARGO_TERM_COLOR: always RUST_BACKTRACE: "1" - ZED_CLI_REF: ${{ github.event.inputs.zed_cli_ref || '67c01b83c834ea14af996613f1a3dc0802a2fbd3' }} + ZED_CLI_REF: ${{ github.event.inputs.zed_cli_ref || '56e597b35deccc82bff7deee807f459a1def537d' }} ZED_INTERFACES_REF: dc0e0a0620b9462817950b552d3d334a184b1cb1 jobs: From 6c9b7aa2e160ef7d37a62257b0dd9d53b3557679 Mon Sep 17 00:00:00 2001 From: Alexander Mills Date: Mon, 3 Aug 2026 15:11:29 -0500 Subject: [PATCH 41/61] test: pin recursive graph certification to workspace implementation --- .github/workflows/recursive-installs.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/recursive-installs.yml b/.github/workflows/recursive-installs.yml index e4dc7ace..a351a25a 100644 --- a/.github/workflows/recursive-installs.yml +++ b/.github/workflows/recursive-installs.yml @@ -9,7 +9,7 @@ on: zed_cli_ref: description: Immutable zed-cli commit to test required: false - default: 67c01b83c834ea14af996613f1a3dc0802a2fbd3 + default: 56e597b35deccc82bff7deee807f459a1def537d permissions: contents: read @@ -25,7 +25,7 @@ defaults: env: CARGO_TERM_COLOR: always RUST_BACKTRACE: "1" - ZED_CLI_REF: ${{ github.event.inputs.zed_cli_ref || '67c01b83c834ea14af996613f1a3dc0802a2fbd3' }} + ZED_CLI_REF: ${{ github.event.inputs.zed_cli_ref || '56e597b35deccc82bff7deee807f459a1def537d' }} ZED_INTERFACES_REF: dc0e0a0620b9462817950b552d3d334a184b1cb1 jobs: From b85d4dacb8886463971d313c62deff5521c36c3d Mon Sep 17 00:00:00 2001 From: Alexander Mills Date: Mon, 3 Aug 2026 15:11:52 -0500 Subject: [PATCH 42/61] test: pin recursive stress certification to workspace implementation --- .github/workflows/recursive-installs-stress.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/recursive-installs-stress.yml b/.github/workflows/recursive-installs-stress.yml index 7d7bccd0..b701d17b 100644 --- a/.github/workflows/recursive-installs-stress.yml +++ b/.github/workflows/recursive-installs-stress.yml @@ -9,7 +9,7 @@ on: zed_cli_ref: description: Immutable zed-cli commit to test required: false - default: 67c01b83c834ea14af996613f1a3dc0802a2fbd3 + default: 56e597b35deccc82bff7deee807f459a1def537d permissions: contents: read @@ -25,7 +25,7 @@ defaults: env: CARGO_TERM_COLOR: always RUST_BACKTRACE: "1" - ZED_CLI_REF: ${{ github.event.inputs.zed_cli_ref || '67c01b83c834ea14af996613f1a3dc0802a2fbd3' }} + ZED_CLI_REF: ${{ github.event.inputs.zed_cli_ref || '56e597b35deccc82bff7deee807f459a1def537d' }} ZED_INTERFACES_REF: dc0e0a0620b9462817950b552d3d334a184b1cb1 jobs: From 9dee5893ba754497e5aac0e0ae79aa4c4f31ac14 Mon Sep 17 00:00:00 2001 From: Alexander Mills Date: Mon, 3 Aug 2026 15:12:15 -0500 Subject: [PATCH 43/61] test: pin recovery matrix to workspace implementation --- .github/workflows/install-recovery.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/install-recovery.yml b/.github/workflows/install-recovery.yml index fe163e00..be32698d 100644 --- a/.github/workflows/install-recovery.yml +++ b/.github/workflows/install-recovery.yml @@ -15,7 +15,7 @@ on: zed_cli_ref: description: Immutable zed-cli commit to test required: false - default: 67c01b83c834ea14af996613f1a3dc0802a2fbd3 + default: 56e597b35deccc82bff7deee807f459a1def537d permissions: contents: read @@ -31,7 +31,7 @@ defaults: env: CARGO_TERM_COLOR: always RUST_BACKTRACE: "1" - ZED_CLI_REF: ${{ github.event.inputs.zed_cli_ref || '67c01b83c834ea14af996613f1a3dc0802a2fbd3' }} + ZED_CLI_REF: ${{ github.event.inputs.zed_cli_ref || '56e597b35deccc82bff7deee807f459a1def537d' }} ZED_INTERFACES_REF: dc0e0a0620b9462817950b552d3d334a184b1cb1 jobs: From 8f8b8d5cfe026fabfb78054dfead179cd1897ef4 Mon Sep 17 00:00:00 2001 From: Alexander Mills Date: Mon, 3 Aug 2026 15:12:23 -0500 Subject: [PATCH 44/61] ci: remove completed install-head repin helper --- .github/repin_workspace_install_head.py | 19 ------------------- 1 file changed, 19 deletions(-) delete mode 100644 .github/repin_workspace_install_head.py diff --git a/.github/repin_workspace_install_head.py b/.github/repin_workspace_install_head.py deleted file mode 100644 index 0e31249e..00000000 --- a/.github/repin_workspace_install_head.py +++ /dev/null @@ -1,19 +0,0 @@ -from pathlib import Path - - -# One-shot exact-head repin for the workspace install implementation. -OLD = "67c01b83c834ea14af996613f1a3dc0802a2fbd3" -NEW = "56e597b35deccc82bff7deee807f459a1def537d" -WORKFLOWS = [ - Path(".github/workflows/recursive-installs.yml"), - Path(".github/workflows/recursive-installs-stress.yml"), - Path(".github/workflows/install-recovery.yml"), - Path(".github/workflows/org-app-installs.yml"), -] - -for path in WORKFLOWS: - text = path.read_text(encoding="utf-8") - occurrences = text.count(OLD) - if occurrences != 2: - raise RuntimeError(f"{path}: expected two immutable pin occurrences, found {occurrences}") - path.write_text(text.replace(OLD, NEW), encoding="utf-8") From 38e288b4eff4c2cc288a1d488353c7a340353bfb Mon Sep 17 00:00:00 2001 From: Alexander Mills Date: Mon, 3 Aug 2026 15:12:32 -0500 Subject: [PATCH 45/61] ci: remove completed install-head repin workflow --- .../apply-workspace-install-repin.yml | 79 ------------------- 1 file changed, 79 deletions(-) delete mode 100644 .github/workflows/apply-workspace-install-repin.yml diff --git a/.github/workflows/apply-workspace-install-repin.yml b/.github/workflows/apply-workspace-install-repin.yml deleted file mode 100644 index ff54ac07..00000000 --- a/.github/workflows/apply-workspace-install-repin.yml +++ /dev/null @@ -1,79 +0,0 @@ -name: apply workspace install head repin - -on: - push: - branches: - - agent/recursive-install-e2e - paths: - - .github/repin_workspace_install_head.py - - .github/workflows/apply-workspace-install-repin.yml - pull_request: - branches: - - main - paths: - - .github/repin_workspace_install_head.py - - .github/workflows/apply-workspace-install-repin.yml - workflow_dispatch: - -permissions: - contents: read - -concurrency: - group: apply-workspace-install-head-repin-${{ github.event.pull_request.number || github.ref }} - cancel-in-progress: true - -jobs: - repin: - if: github.actor != 'github-actions[bot]' - runs-on: ubuntu-24.04 - timeout-minutes: 10 - permissions: - contents: write - steps: - - name: Check out E2E branch - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - with: - ref: agent/recursive-install-e2e - fetch-depth: 0 - persist-credentials: false - show-progress: false - - - name: Apply and validate immutable pins - run: | - set -euo pipefail - python3 .github/repin_workspace_install_head.py - python3 - <<'PY' - from pathlib import Path - pin = "56e597b35deccc82bff7deee807f459a1def537d" - for path in [ - Path(".github/workflows/recursive-installs.yml"), - Path(".github/workflows/recursive-installs-stress.yml"), - Path(".github/workflows/install-recovery.yml"), - Path(".github/workflows/org-app-installs.yml"), - ]: - text = path.read_text() - assert text.count(pin) == 2, (path, text.count(pin)) - PY - git diff --check - - - name: Publish the validated workflow pins - env: - GH_TOKEN: ${{ github.token }} - run: | - set -euo pipefail - git add \ - .github/workflows/recursive-installs.yml \ - .github/workflows/recursive-installs-stress.yml \ - .github/workflows/install-recovery.yml \ - .github/workflows/org-app-installs.yml - git diff --cached --check - if git diff --cached --quiet; then - echo 'workspace implementation head already pinned' - exit 0 - fi - git config user.name 'github-actions[bot]' - git config user.email '41898282+github-actions[bot]@users.noreply.github.com' - git commit -m 'test: pin workspace install implementation' - git push \ - "https://x-access-token:${GH_TOKEN}@github.com/${GITHUB_REPOSITORY}.git" \ - HEAD:agent/recursive-install-e2e From 1e283f54e062bf954fa728626dedc4b327d6aefb Mon Sep 17 00:00:00 2001 From: Alexander Mills Date: Mon, 3 Aug 2026 15:13:41 -0500 Subject: [PATCH 46/61] test: stage frozen workspace and adapter-copy coverage --- .github/augment_org_workspace_installs.py | 40 +++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 .github/augment_org_workspace_installs.py diff --git a/.github/augment_org_workspace_installs.py b/.github/augment_org_workspace_installs.py new file mode 100644 index 00000000..35801534 --- /dev/null +++ b/.github/augment_org_workspace_installs.py @@ -0,0 +1,40 @@ +from pathlib import Path + + +path = Path("scripts/org_app_installs.py") +text = path.read_text(encoding="utf-8") + + +def replace_once(old: str, new: str, label: str) -> None: + global text + if new in text: + return + count = text.count(old) + if count != 1: + raise RuntimeError(f"{label}: expected one match, found {count}") + text = text.replace(old, new, 1) + + +replace_once( + "import argparse\nimport json\n", + "import argparse\nimport hashlib\nimport json\n", + "add hashlib import", +) + +replace_once( + '''def require_success(completed: Completed) -> None:\n if completed.returncode != 0:\n shown = " ".join(shell_quote(value) for value in completed.command)\n raise RuntimeError(f"command failed with exit code {completed.returncode}: {shown}")\n\n\n''', + '''def require_success(completed: Completed) -> None:\n if completed.returncode != 0:\n shown = " ".join(shell_quote(value) for value in completed.command)\n raise RuntimeError(f"command failed with exit code {completed.returncode}: {shown}")\n\n\ndef require_failure(completed: Completed, *fragments: str) -> None:\n if completed.returncode == 0:\n shown = " ".join(shell_quote(value) for value in completed.command)\n raise AssertionError(f"command unexpectedly succeeded: {shown}")\n for fragment in fragments:\n if fragment not in completed.output:\n raise AssertionError(\n f"failed command output did not contain {fragment!r}:\\n{completed.output}"\n )\n\n\n''', + "add failure assertion", +) + +replace_once( + '''def zed_command(zed: Path, registry: Path, home: Path, *args: str) -> list[str | Path]:\n''', + '''def tree_fingerprint(root: Path) -> tuple[tuple[str, int, str], ...]:\n if not root.exists():\n return ()\n rows: list[tuple[str, int, str]] = []\n for candidate in sorted(root.rglob("*")):\n if candidate.is_file() and not candidate.is_symlink():\n data = candidate.read_bytes()\n rows.append(\n (\n candidate.relative_to(root).as_posix(),\n len(data),\n hashlib.sha256(data).hexdigest(),\n )\n )\n return tuple(rows)\n\n\ndef zed_command(zed: Path, registry: Path, home: Path, *args: str) -> list[str | Path]:\n''', + "add immutable tree fingerprint", +) + +start = text.index("def exercise_workspace(\n") +end = text.index("\ndef main() -> int:\n", start) +replacement = '''def exercise_workspace(\n *,\n zed: Path,\n registry: Path,\n home: Path,\n clone_root: Path,\n workspace_root: Path,\n clones: dict[str, Path],\n environment: Mapping[str, str],\n) -> None:\n source = clone_repo(clone_root, "workspace-monorepo", clones)\n\n def expected_sources(repo_root: Path) -> dict[str, Path]:\n return {\n "ws-utils": repo_root / "packages" / "utils",\n "ws-core": repo_root / "packages" / "core",\n }\n\n def assert_projection(repo_root: Path, project: Path, mode: str) -> None:\n expected = expected_sources(repo_root)\n path_index = project / ".zed" / "paths.json"\n if not path_index.is_file():\n raise AssertionError(f"workspace install omitted {path_index}")\n index_text = path_index.read_text(encoding="utf-8")\n json.loads(index_text)\n for name, expected_source in expected.items():\n key = f"zedtest/{name}"\n installed = project / "zed_modules" / "zedtest" / name\n node_link = project / "node_modules" / "@zedtest" / name\n for projection in (installed, node_link):\n if mode == "symlink":\n if not projection.is_symlink():\n raise AssertionError(\n f"workspace dependency was not symlinked: {projection}"\n )\n if projection.resolve(strict=True) != expected_source.resolve(\n strict=True\n ):\n raise AssertionError(\n f"workspace target mismatch: {projection} -> {projection.resolve()}"\n )\n else:\n if projection.is_symlink() or not projection.is_dir():\n raise AssertionError(\n f"workspace dependency was not copied: {projection}"\n )\n if not (projection / ".zpkg.toml").is_file():\n raise AssertionError(\n f"workspace copied projection has no manifest: {projection}"\n )\n if key not in index_text or '"version": "workspace"' not in index_text:\n raise AssertionError(f"workspace path index omits {key}: {index_text}")\n\n node_path = project / ".zed" / "node_path"\n if node_path.read_text(encoding="utf-8").strip() != "zed_modules":\n raise AssertionError(f"unexpected workspace node_path: {node_path.read_text()!r}")\n\n symlink_repo = workspace_root / "symlink"\n copy_fixture(source, symlink_repo)\n symlink_project = symlink_repo / "apps" / "cli"\n symlink_install = run(\n zed_command(zed, registry, home, "install"),\n cwd=symlink_project,\n env=environment,\n )\n require_success(symlink_install)\n if PREFETCH_RE.search(symlink_install.output):\n raise AssertionError(\n "workspace-only install unexpectedly reported registry artifact prefetch"\n )\n if parse_lock(symlink_project):\n raise AssertionError("workspace packages must not be written as registry lock entries")\n symlink_lock = (symlink_project / ".zpkg.lock").read_bytes()\n assert_projection(symlink_repo, symlink_project, "symlink")\n\n live_sentinel = symlink_repo / "packages" / "core" / "live-sentinel.txt"\n live_sentinel.write_text("visible after install\\n", encoding="utf-8")\n visible = (\n symlink_project\n / "zed_modules"\n / "zedtest"\n / "ws-core"\n / "live-sentinel.txt"\n )\n if visible.read_text(encoding="utf-8") != "visible after install\\n":\n raise AssertionError("workspace symlink did not expose a post-install source edit")\n\n registry_before_frozen = tree_fingerprint(registry)\n cache_before_frozen = tree_fingerprint(home / "cache")\n for projection in [\n symlink_project / "zed_modules" / "zedtest" / "ws-utils",\n symlink_project / "zed_modules" / "zedtest" / "ws-core",\n symlink_project / "node_modules" / "@zedtest" / "ws-utils",\n symlink_project / "node_modules" / "@zedtest" / "ws-core",\n ]:\n projection.unlink()\n\n frozen_repair = run(\n zed_command(zed, registry, home, "install", "--frozen"),\n cwd=symlink_project,\n env=environment,\n )\n require_success(frozen_repair)\n if PREFETCH_RE.search(frozen_repair.output):\n raise AssertionError("frozen workspace repair accessed registry artifacts")\n if (symlink_project / ".zpkg.lock").read_bytes() != symlink_lock:\n raise AssertionError("frozen workspace repair rewrote the empty artifact lock")\n if tree_fingerprint(registry) != registry_before_frozen:\n raise AssertionError("frozen workspace repair mutated the registry")\n if tree_fingerprint(home / "cache") != cache_before_frozen:\n raise AssertionError("frozen workspace repair mutated the artifact cache")\n assert_projection(symlink_repo, symlink_project, "symlink")\n\n manifest_path = symlink_project / ".zpkg.toml"\n manifest_before = manifest_path.read_text(encoding="utf-8")\n incompatible = re.sub(\n r'("zedtest/ws-utils"\\s*=\\s*)"[^"]+"',\n r'\\1"^2"',\n manifest_before,\n count=1,\n )\n if incompatible == manifest_before:\n raise AssertionError("could not rewrite workspace requirement for drift test")\n manifest_path.write_text(incompatible, encoding="utf-8")\n version_drift = run(\n zed_command(zed, registry, home, "install", "--frozen"),\n cwd=symlink_project,\n env=environment,\n )\n require_failure(version_drift, "ws-utils", "does not satisfy `^2`")\n if (symlink_project / ".zpkg.lock").read_bytes() != symlink_lock:\n raise AssertionError("workspace version-drift failure rewrote the lock")\n assert_projection(symlink_repo, symlink_project, "symlink")\n manifest_path.write_text(manifest_before, encoding="utf-8")\n\n copy_repo = workspace_root / "copy"\n copy_fixture(source, copy_repo)\n copy_source_sentinel = copy_repo / "packages" / "core" / "copy-sentinel.txt"\n copy_source_sentinel.write_text("before install\\n", encoding="utf-8")\n copy_project = copy_repo / "apps" / "cli"\n copy_install = run(\n zed_command(\n zed,\n registry,\n home,\n "install",\n "--install-mode",\n "copy",\n ),\n cwd=copy_project,\n env=environment,\n )\n require_success(copy_install)\n if PREFETCH_RE.search(copy_install.output):\n raise AssertionError("workspace copy install accessed registry artifacts")\n if parse_lock(copy_project):\n raise AssertionError("workspace copy install wrote registry lock entries")\n copy_lock = (copy_project / ".zpkg.lock").read_bytes()\n assert_projection(copy_repo, copy_project, "copy")\n\n copied_sentinels = [\n copy_project / "zed_modules" / "zedtest" / "ws-core" / "copy-sentinel.txt",\n copy_project\n / "node_modules"\n / "@zedtest"\n / "ws-core"\n / "copy-sentinel.txt",\n ]\n for copied_sentinel in copied_sentinels:\n if copied_sentinel.read_text(encoding="utf-8") != "before install\\n":\n raise AssertionError(f"workspace copy omitted source content: {copied_sentinel}")\n copy_source_sentinel.write_text("after install\\n", encoding="utf-8")\n for copied_sentinel in copied_sentinels:\n if copied_sentinel.read_text(encoding="utf-8") != "before install\\n":\n raise AssertionError(\n f"workspace copy remained coupled to mutable source: {copied_sentinel}"\n )\n\n for projection in [\n copy_project / "zed_modules" / "zedtest" / "ws-core",\n copy_project / "node_modules" / "@zedtest" / "ws-core",\n ]:\n shutil.rmtree(projection)\n copy_repair = run(\n zed_command(\n zed,\n registry,\n home,\n "install",\n "--frozen",\n "--install-mode",\n "copy",\n ),\n cwd=copy_project,\n env=environment,\n )\n require_success(copy_repair)\n if PREFETCH_RE.search(copy_repair.output):\n raise AssertionError("frozen workspace copy repair accessed registry artifacts")\n if (copy_project / ".zpkg.lock").read_bytes() != copy_lock:\n raise AssertionError("frozen workspace copy repair rewrote the lock")\n assert_projection(copy_repo, copy_project, "copy")\n leaked = [\n candidate\n for root in (copy_project / "zed_modules", copy_project / "node_modules")\n for candidate in root.rglob("*")\n if candidate.is_symlink()\n ]\n if leaked:\n raise AssertionError(f"workspace copy mode leaked symlinks: {leaked}")\n\n''' +text = text[:start] + replacement + text[end:] +path.write_text(text, encoding="utf-8") From ac8b5b41dc4e28db8c33997cf4763e6a8e576f73 Mon Sep 17 00:00:00 2001 From: Alexander Mills Date: Mon, 3 Aug 2026 15:13:58 -0500 Subject: [PATCH 47/61] ci: publish frozen workspace install coverage --- .../apply-org-workspace-coverage.yml | 67 +++++++++++++++++++ 1 file changed, 67 insertions(+) create mode 100644 .github/workflows/apply-org-workspace-coverage.yml diff --git a/.github/workflows/apply-org-workspace-coverage.yml b/.github/workflows/apply-org-workspace-coverage.yml new file mode 100644 index 00000000..1edf6e14 --- /dev/null +++ b/.github/workflows/apply-org-workspace-coverage.yml @@ -0,0 +1,67 @@ +name: apply org workspace install coverage + +on: + pull_request: + branches: + - main + paths: + - .github/augment_org_workspace_installs.py + - .github/workflows/apply-org-workspace-coverage.yml + push: + branches: + - agent/recursive-install-e2e + paths: + - .github/augment_org_workspace_installs.py + - .github/workflows/apply-org-workspace-coverage.yml + workflow_dispatch: + +permissions: + contents: read + +concurrency: + group: apply-org-workspace-install-coverage-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +jobs: + apply: + if: github.actor != 'github-actions[bot]' + runs-on: ubuntu-24.04 + timeout-minutes: 10 + permissions: + contents: write + steps: + - name: Check out E2E branch + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + ref: agent/recursive-install-e2e + fetch-depth: 0 + persist-credentials: false + show-progress: false + + - name: Apply and validate workspace coverage + run: | + set -euo pipefail + python3 .github/augment_org_workspace_installs.py + python3 -m py_compile scripts/org_app_installs.py + grep -F 'frozen workspace repair accessed registry artifacts' scripts/org_app_installs.py + grep -F 'does not satisfy `^2`' scripts/org_app_installs.py + grep -F 'workspace copy mode leaked symlinks' scripts/org_app_installs.py + git diff --check + + - name: Publish validated workspace coverage + env: + GH_TOKEN: ${{ github.token }} + run: | + set -euo pipefail + git add scripts/org_app_installs.py + git diff --cached --check + if git diff --cached --quiet; then + echo 'workspace coverage already published' + exit 0 + fi + git config user.name 'github-actions[bot]' + git config user.email '41898282+github-actions[bot]@users.noreply.github.com' + git commit -m 'test: harden workspace install and frozen repair coverage' + git push \ + "https://x-access-token:${GH_TOKEN}@github.com/${GITHUB_REPOSITORY}.git" \ + HEAD:agent/recursive-install-e2e From 78f8def85fed747c7e6888428fa24ea079d9c14e Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 3 Aug 2026 20:14:07 +0000 Subject: [PATCH 48/61] test: harden workspace install and frozen repair coverage --- scripts/org_app_installs.py | 211 +++++++++++++++++++++++++++++++----- 1 file changed, 184 insertions(+), 27 deletions(-) diff --git a/scripts/org_app_installs.py b/scripts/org_app_installs.py index c06859af..9766383a 100644 --- a/scripts/org_app_installs.py +++ b/scripts/org_app_installs.py @@ -4,6 +4,7 @@ from __future__ import annotations import argparse +import hashlib import json import os import re @@ -113,6 +114,34 @@ def require_success(completed: Completed) -> None: raise RuntimeError(f"command failed with exit code {completed.returncode}: {shown}") +def require_failure(completed: Completed, *fragments: str) -> None: + if completed.returncode == 0: + shown = " ".join(shell_quote(value) for value in completed.command) + raise AssertionError(f"command unexpectedly succeeded: {shown}") + for fragment in fragments: + if fragment not in completed.output: + raise AssertionError( + f"failed command output did not contain {fragment!r}:\n{completed.output}" + ) + + +def tree_fingerprint(root: Path) -> tuple[tuple[str, int, str], ...]: + if not root.exists(): + return () + rows: list[tuple[str, int, str]] = [] + for candidate in sorted(root.rglob("*")): + if candidate.is_file() and not candidate.is_symlink(): + data = candidate.read_bytes() + rows.append( + ( + candidate.relative_to(root).as_posix(), + len(data), + hashlib.sha256(data).hexdigest(), + ) + ) + return tuple(rows) + + def zed_command(zed: Path, registry: Path, home: Path, *args: str) -> list[str | Path]: return [ zed, @@ -537,6 +566,51 @@ def exercise_workspace( ) -> None: source = clone_repo(clone_root, "workspace-monorepo", clones) + def expected_sources(repo_root: Path) -> dict[str, Path]: + return { + "ws-utils": repo_root / "packages" / "utils", + "ws-core": repo_root / "packages" / "core", + } + + def assert_projection(repo_root: Path, project: Path, mode: str) -> None: + expected = expected_sources(repo_root) + path_index = project / ".zed" / "paths.json" + if not path_index.is_file(): + raise AssertionError(f"workspace install omitted {path_index}") + index_text = path_index.read_text(encoding="utf-8") + json.loads(index_text) + for name, expected_source in expected.items(): + key = f"zedtest/{name}" + installed = project / "zed_modules" / "zedtest" / name + node_link = project / "node_modules" / "@zedtest" / name + for projection in (installed, node_link): + if mode == "symlink": + if not projection.is_symlink(): + raise AssertionError( + f"workspace dependency was not symlinked: {projection}" + ) + if projection.resolve(strict=True) != expected_source.resolve( + strict=True + ): + raise AssertionError( + f"workspace target mismatch: {projection} -> {projection.resolve()}" + ) + else: + if projection.is_symlink() or not projection.is_dir(): + raise AssertionError( + f"workspace dependency was not copied: {projection}" + ) + if not (projection / ".zpkg.toml").is_file(): + raise AssertionError( + f"workspace copied projection has no manifest: {projection}" + ) + if key not in index_text or '"version": "workspace"' not in index_text: + raise AssertionError(f"workspace path index omits {key}: {index_text}") + + node_path = project / ".zed" / "node_path" + if node_path.read_text(encoding="utf-8").strip() != "zed_modules": + raise AssertionError(f"unexpected workspace node_path: {node_path.read_text()!r}") + symlink_repo = workspace_root / "symlink" copy_fixture(source, symlink_repo) symlink_project = symlink_repo / "apps" / "cli" @@ -546,24 +620,14 @@ def exercise_workspace( env=environment, ) require_success(symlink_install) - - expected_sources = { - "ws-utils": symlink_repo / "packages" / "utils", - "ws-core": symlink_repo / "packages" / "core", - } - for name, expected_source in expected_sources.items(): - installed = symlink_project / "zed_modules" / "zedtest" / name - if not installed.is_symlink(): - raise AssertionError(f"workspace dependency was not symlinked: {installed}") - if installed.resolve(strict=True) != expected_source.resolve(strict=True): - raise AssertionError( - f"workspace link target mismatch: {installed} -> {installed.resolve()}" - ) - node_link = symlink_project / "node_modules" / "@zedtest" / name - if not node_link.is_symlink() or node_link.resolve(strict=True) != expected_source.resolve( - strict=True - ): - raise AssertionError(f"workspace Node adapter mismatch: {node_link}") + if PREFETCH_RE.search(symlink_install.output): + raise AssertionError( + "workspace-only install unexpectedly reported registry artifact prefetch" + ) + if parse_lock(symlink_project): + raise AssertionError("workspace packages must not be written as registry lock entries") + symlink_lock = (symlink_project / ".zpkg.lock").read_bytes() + assert_projection(symlink_repo, symlink_project, "symlink") live_sentinel = symlink_repo / "packages" / "core" / "live-sentinel.txt" live_sentinel.write_text("visible after install\n", encoding="utf-8") @@ -577,6 +641,54 @@ def exercise_workspace( if visible.read_text(encoding="utf-8") != "visible after install\n": raise AssertionError("workspace symlink did not expose a post-install source edit") + registry_before_frozen = tree_fingerprint(registry) + cache_before_frozen = tree_fingerprint(home / "cache") + for projection in [ + symlink_project / "zed_modules" / "zedtest" / "ws-utils", + symlink_project / "zed_modules" / "zedtest" / "ws-core", + symlink_project / "node_modules" / "@zedtest" / "ws-utils", + symlink_project / "node_modules" / "@zedtest" / "ws-core", + ]: + projection.unlink() + + frozen_repair = run( + zed_command(zed, registry, home, "install", "--frozen"), + cwd=symlink_project, + env=environment, + ) + require_success(frozen_repair) + if PREFETCH_RE.search(frozen_repair.output): + raise AssertionError("frozen workspace repair accessed registry artifacts") + if (symlink_project / ".zpkg.lock").read_bytes() != symlink_lock: + raise AssertionError("frozen workspace repair rewrote the empty artifact lock") + if tree_fingerprint(registry) != registry_before_frozen: + raise AssertionError("frozen workspace repair mutated the registry") + if tree_fingerprint(home / "cache") != cache_before_frozen: + raise AssertionError("frozen workspace repair mutated the artifact cache") + assert_projection(symlink_repo, symlink_project, "symlink") + + manifest_path = symlink_project / ".zpkg.toml" + manifest_before = manifest_path.read_text(encoding="utf-8") + incompatible = re.sub( + r'("zedtest/ws-utils"\s*=\s*)"[^"]+"', + r'\1"^2"', + manifest_before, + count=1, + ) + if incompatible == manifest_before: + raise AssertionError("could not rewrite workspace requirement for drift test") + manifest_path.write_text(incompatible, encoding="utf-8") + version_drift = run( + zed_command(zed, registry, home, "install", "--frozen"), + cwd=symlink_project, + env=environment, + ) + require_failure(version_drift, "ws-utils", "does not satisfy `^2`") + if (symlink_project / ".zpkg.lock").read_bytes() != symlink_lock: + raise AssertionError("workspace version-drift failure rewrote the lock") + assert_projection(symlink_repo, symlink_project, "symlink") + manifest_path.write_text(manifest_before, encoding="utf-8") + copy_repo = workspace_root / "copy" copy_fixture(source, copy_repo) copy_source_sentinel = copy_repo / "packages" / "core" / "copy-sentinel.txt" @@ -595,16 +707,61 @@ def exercise_workspace( env=environment, ) require_success(copy_install) - copied = copy_project / "zed_modules" / "zedtest" / "ws-core" - if copied.is_symlink() or not copied.is_dir(): - raise AssertionError(f"workspace copy mode did not copy {copied}") - copied_sentinel = copied / "copy-sentinel.txt" - if copied_sentinel.read_text(encoding="utf-8") != "before install\n": - raise AssertionError("workspace copy omitted source content") + if PREFETCH_RE.search(copy_install.output): + raise AssertionError("workspace copy install accessed registry artifacts") + if parse_lock(copy_project): + raise AssertionError("workspace copy install wrote registry lock entries") + copy_lock = (copy_project / ".zpkg.lock").read_bytes() + assert_projection(copy_repo, copy_project, "copy") + + copied_sentinels = [ + copy_project / "zed_modules" / "zedtest" / "ws-core" / "copy-sentinel.txt", + copy_project + / "node_modules" + / "@zedtest" + / "ws-core" + / "copy-sentinel.txt", + ] + for copied_sentinel in copied_sentinels: + if copied_sentinel.read_text(encoding="utf-8") != "before install\n": + raise AssertionError(f"workspace copy omitted source content: {copied_sentinel}") copy_source_sentinel.write_text("after install\n", encoding="utf-8") - if copied_sentinel.read_text(encoding="utf-8") != "before install\n": - raise AssertionError("workspace copy remained coupled to mutable source") - leaked = [path for path in (copy_project / "zed_modules").rglob("*") if path.is_symlink()] + for copied_sentinel in copied_sentinels: + if copied_sentinel.read_text(encoding="utf-8") != "before install\n": + raise AssertionError( + f"workspace copy remained coupled to mutable source: {copied_sentinel}" + ) + + for projection in [ + copy_project / "zed_modules" / "zedtest" / "ws-core", + copy_project / "node_modules" / "@zedtest" / "ws-core", + ]: + shutil.rmtree(projection) + copy_repair = run( + zed_command( + zed, + registry, + home, + "install", + "--frozen", + "--install-mode", + "copy", + ), + cwd=copy_project, + env=environment, + ) + require_success(copy_repair) + if PREFETCH_RE.search(copy_repair.output): + raise AssertionError("frozen workspace copy repair accessed registry artifacts") + if (copy_project / ".zpkg.lock").read_bytes() != copy_lock: + raise AssertionError("frozen workspace copy repair rewrote the lock") + assert_projection(copy_repo, copy_project, "copy") + leaked = [ + candidate + for root in (copy_project / "zed_modules", copy_project / "node_modules") + for candidate in root.rglob("*") + if candidate.is_symlink() + ] if leaked: raise AssertionError(f"workspace copy mode leaked symlinks: {leaked}") From 1fdc7992082767a9a73affeab4b140ec56242259 Mon Sep 17 00:00:00 2001 From: Alexander Mills Date: Mon, 3 Aug 2026 15:14:44 -0500 Subject: [PATCH 49/61] ci: trigger fail-closed workspace coverage publication --- .github/workflows/apply-org-workspace-coverage.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/apply-org-workspace-coverage.yml b/.github/workflows/apply-org-workspace-coverage.yml index 1edf6e14..e77a8c9c 100644 --- a/.github/workflows/apply-org-workspace-coverage.yml +++ b/.github/workflows/apply-org-workspace-coverage.yml @@ -41,6 +41,7 @@ jobs: - name: Apply and validate workspace coverage run: | set -euo pipefail + # The helper is intentionally exact and fail-closed before publishing. python3 .github/augment_org_workspace_installs.py python3 -m py_compile scripts/org_app_installs.py grep -F 'frozen workspace repair accessed registry artifacts' scripts/org_app_installs.py From 32c8c375e81fbb193be4af4f9bbcfc13f21b4891 Mon Sep 17 00:00:00 2001 From: Alexander Mills Date: Mon, 3 Aug 2026 15:15:16 -0500 Subject: [PATCH 50/61] ci: remove completed workspace coverage patch helper --- .github/augment_org_workspace_installs.py | 40 ----------------------- 1 file changed, 40 deletions(-) delete mode 100644 .github/augment_org_workspace_installs.py diff --git a/.github/augment_org_workspace_installs.py b/.github/augment_org_workspace_installs.py deleted file mode 100644 index 35801534..00000000 --- a/.github/augment_org_workspace_installs.py +++ /dev/null @@ -1,40 +0,0 @@ -from pathlib import Path - - -path = Path("scripts/org_app_installs.py") -text = path.read_text(encoding="utf-8") - - -def replace_once(old: str, new: str, label: str) -> None: - global text - if new in text: - return - count = text.count(old) - if count != 1: - raise RuntimeError(f"{label}: expected one match, found {count}") - text = text.replace(old, new, 1) - - -replace_once( - "import argparse\nimport json\n", - "import argparse\nimport hashlib\nimport json\n", - "add hashlib import", -) - -replace_once( - '''def require_success(completed: Completed) -> None:\n if completed.returncode != 0:\n shown = " ".join(shell_quote(value) for value in completed.command)\n raise RuntimeError(f"command failed with exit code {completed.returncode}: {shown}")\n\n\n''', - '''def require_success(completed: Completed) -> None:\n if completed.returncode != 0:\n shown = " ".join(shell_quote(value) for value in completed.command)\n raise RuntimeError(f"command failed with exit code {completed.returncode}: {shown}")\n\n\ndef require_failure(completed: Completed, *fragments: str) -> None:\n if completed.returncode == 0:\n shown = " ".join(shell_quote(value) for value in completed.command)\n raise AssertionError(f"command unexpectedly succeeded: {shown}")\n for fragment in fragments:\n if fragment not in completed.output:\n raise AssertionError(\n f"failed command output did not contain {fragment!r}:\\n{completed.output}"\n )\n\n\n''', - "add failure assertion", -) - -replace_once( - '''def zed_command(zed: Path, registry: Path, home: Path, *args: str) -> list[str | Path]:\n''', - '''def tree_fingerprint(root: Path) -> tuple[tuple[str, int, str], ...]:\n if not root.exists():\n return ()\n rows: list[tuple[str, int, str]] = []\n for candidate in sorted(root.rglob("*")):\n if candidate.is_file() and not candidate.is_symlink():\n data = candidate.read_bytes()\n rows.append(\n (\n candidate.relative_to(root).as_posix(),\n len(data),\n hashlib.sha256(data).hexdigest(),\n )\n )\n return tuple(rows)\n\n\ndef zed_command(zed: Path, registry: Path, home: Path, *args: str) -> list[str | Path]:\n''', - "add immutable tree fingerprint", -) - -start = text.index("def exercise_workspace(\n") -end = text.index("\ndef main() -> int:\n", start) -replacement = '''def exercise_workspace(\n *,\n zed: Path,\n registry: Path,\n home: Path,\n clone_root: Path,\n workspace_root: Path,\n clones: dict[str, Path],\n environment: Mapping[str, str],\n) -> None:\n source = clone_repo(clone_root, "workspace-monorepo", clones)\n\n def expected_sources(repo_root: Path) -> dict[str, Path]:\n return {\n "ws-utils": repo_root / "packages" / "utils",\n "ws-core": repo_root / "packages" / "core",\n }\n\n def assert_projection(repo_root: Path, project: Path, mode: str) -> None:\n expected = expected_sources(repo_root)\n path_index = project / ".zed" / "paths.json"\n if not path_index.is_file():\n raise AssertionError(f"workspace install omitted {path_index}")\n index_text = path_index.read_text(encoding="utf-8")\n json.loads(index_text)\n for name, expected_source in expected.items():\n key = f"zedtest/{name}"\n installed = project / "zed_modules" / "zedtest" / name\n node_link = project / "node_modules" / "@zedtest" / name\n for projection in (installed, node_link):\n if mode == "symlink":\n if not projection.is_symlink():\n raise AssertionError(\n f"workspace dependency was not symlinked: {projection}"\n )\n if projection.resolve(strict=True) != expected_source.resolve(\n strict=True\n ):\n raise AssertionError(\n f"workspace target mismatch: {projection} -> {projection.resolve()}"\n )\n else:\n if projection.is_symlink() or not projection.is_dir():\n raise AssertionError(\n f"workspace dependency was not copied: {projection}"\n )\n if not (projection / ".zpkg.toml").is_file():\n raise AssertionError(\n f"workspace copied projection has no manifest: {projection}"\n )\n if key not in index_text or '"version": "workspace"' not in index_text:\n raise AssertionError(f"workspace path index omits {key}: {index_text}")\n\n node_path = project / ".zed" / "node_path"\n if node_path.read_text(encoding="utf-8").strip() != "zed_modules":\n raise AssertionError(f"unexpected workspace node_path: {node_path.read_text()!r}")\n\n symlink_repo = workspace_root / "symlink"\n copy_fixture(source, symlink_repo)\n symlink_project = symlink_repo / "apps" / "cli"\n symlink_install = run(\n zed_command(zed, registry, home, "install"),\n cwd=symlink_project,\n env=environment,\n )\n require_success(symlink_install)\n if PREFETCH_RE.search(symlink_install.output):\n raise AssertionError(\n "workspace-only install unexpectedly reported registry artifact prefetch"\n )\n if parse_lock(symlink_project):\n raise AssertionError("workspace packages must not be written as registry lock entries")\n symlink_lock = (symlink_project / ".zpkg.lock").read_bytes()\n assert_projection(symlink_repo, symlink_project, "symlink")\n\n live_sentinel = symlink_repo / "packages" / "core" / "live-sentinel.txt"\n live_sentinel.write_text("visible after install\\n", encoding="utf-8")\n visible = (\n symlink_project\n / "zed_modules"\n / "zedtest"\n / "ws-core"\n / "live-sentinel.txt"\n )\n if visible.read_text(encoding="utf-8") != "visible after install\\n":\n raise AssertionError("workspace symlink did not expose a post-install source edit")\n\n registry_before_frozen = tree_fingerprint(registry)\n cache_before_frozen = tree_fingerprint(home / "cache")\n for projection in [\n symlink_project / "zed_modules" / "zedtest" / "ws-utils",\n symlink_project / "zed_modules" / "zedtest" / "ws-core",\n symlink_project / "node_modules" / "@zedtest" / "ws-utils",\n symlink_project / "node_modules" / "@zedtest" / "ws-core",\n ]:\n projection.unlink()\n\n frozen_repair = run(\n zed_command(zed, registry, home, "install", "--frozen"),\n cwd=symlink_project,\n env=environment,\n )\n require_success(frozen_repair)\n if PREFETCH_RE.search(frozen_repair.output):\n raise AssertionError("frozen workspace repair accessed registry artifacts")\n if (symlink_project / ".zpkg.lock").read_bytes() != symlink_lock:\n raise AssertionError("frozen workspace repair rewrote the empty artifact lock")\n if tree_fingerprint(registry) != registry_before_frozen:\n raise AssertionError("frozen workspace repair mutated the registry")\n if tree_fingerprint(home / "cache") != cache_before_frozen:\n raise AssertionError("frozen workspace repair mutated the artifact cache")\n assert_projection(symlink_repo, symlink_project, "symlink")\n\n manifest_path = symlink_project / ".zpkg.toml"\n manifest_before = manifest_path.read_text(encoding="utf-8")\n incompatible = re.sub(\n r'("zedtest/ws-utils"\\s*=\\s*)"[^"]+"',\n r'\\1"^2"',\n manifest_before,\n count=1,\n )\n if incompatible == manifest_before:\n raise AssertionError("could not rewrite workspace requirement for drift test")\n manifest_path.write_text(incompatible, encoding="utf-8")\n version_drift = run(\n zed_command(zed, registry, home, "install", "--frozen"),\n cwd=symlink_project,\n env=environment,\n )\n require_failure(version_drift, "ws-utils", "does not satisfy `^2`")\n if (symlink_project / ".zpkg.lock").read_bytes() != symlink_lock:\n raise AssertionError("workspace version-drift failure rewrote the lock")\n assert_projection(symlink_repo, symlink_project, "symlink")\n manifest_path.write_text(manifest_before, encoding="utf-8")\n\n copy_repo = workspace_root / "copy"\n copy_fixture(source, copy_repo)\n copy_source_sentinel = copy_repo / "packages" / "core" / "copy-sentinel.txt"\n copy_source_sentinel.write_text("before install\\n", encoding="utf-8")\n copy_project = copy_repo / "apps" / "cli"\n copy_install = run(\n zed_command(\n zed,\n registry,\n home,\n "install",\n "--install-mode",\n "copy",\n ),\n cwd=copy_project,\n env=environment,\n )\n require_success(copy_install)\n if PREFETCH_RE.search(copy_install.output):\n raise AssertionError("workspace copy install accessed registry artifacts")\n if parse_lock(copy_project):\n raise AssertionError("workspace copy install wrote registry lock entries")\n copy_lock = (copy_project / ".zpkg.lock").read_bytes()\n assert_projection(copy_repo, copy_project, "copy")\n\n copied_sentinels = [\n copy_project / "zed_modules" / "zedtest" / "ws-core" / "copy-sentinel.txt",\n copy_project\n / "node_modules"\n / "@zedtest"\n / "ws-core"\n / "copy-sentinel.txt",\n ]\n for copied_sentinel in copied_sentinels:\n if copied_sentinel.read_text(encoding="utf-8") != "before install\\n":\n raise AssertionError(f"workspace copy omitted source content: {copied_sentinel}")\n copy_source_sentinel.write_text("after install\\n", encoding="utf-8")\n for copied_sentinel in copied_sentinels:\n if copied_sentinel.read_text(encoding="utf-8") != "before install\\n":\n raise AssertionError(\n f"workspace copy remained coupled to mutable source: {copied_sentinel}"\n )\n\n for projection in [\n copy_project / "zed_modules" / "zedtest" / "ws-core",\n copy_project / "node_modules" / "@zedtest" / "ws-core",\n ]:\n shutil.rmtree(projection)\n copy_repair = run(\n zed_command(\n zed,\n registry,\n home,\n "install",\n "--frozen",\n "--install-mode",\n "copy",\n ),\n cwd=copy_project,\n env=environment,\n )\n require_success(copy_repair)\n if PREFETCH_RE.search(copy_repair.output):\n raise AssertionError("frozen workspace copy repair accessed registry artifacts")\n if (copy_project / ".zpkg.lock").read_bytes() != copy_lock:\n raise AssertionError("frozen workspace copy repair rewrote the lock")\n assert_projection(copy_repo, copy_project, "copy")\n leaked = [\n candidate\n for root in (copy_project / "zed_modules", copy_project / "node_modules")\n for candidate in root.rglob("*")\n if candidate.is_symlink()\n ]\n if leaked:\n raise AssertionError(f"workspace copy mode leaked symlinks: {leaked}")\n\n''' -text = text[:start] + replacement + text[end:] -path.write_text(text, encoding="utf-8") From 6d716c1d6ebcb680627aed3396202fe52ac9499a Mon Sep 17 00:00:00 2001 From: Alexander Mills Date: Mon, 3 Aug 2026 15:15:25 -0500 Subject: [PATCH 51/61] ci: remove completed workspace coverage publishing workflow --- .../apply-org-workspace-coverage.yml | 68 ------------------- 1 file changed, 68 deletions(-) delete mode 100644 .github/workflows/apply-org-workspace-coverage.yml diff --git a/.github/workflows/apply-org-workspace-coverage.yml b/.github/workflows/apply-org-workspace-coverage.yml deleted file mode 100644 index e77a8c9c..00000000 --- a/.github/workflows/apply-org-workspace-coverage.yml +++ /dev/null @@ -1,68 +0,0 @@ -name: apply org workspace install coverage - -on: - pull_request: - branches: - - main - paths: - - .github/augment_org_workspace_installs.py - - .github/workflows/apply-org-workspace-coverage.yml - push: - branches: - - agent/recursive-install-e2e - paths: - - .github/augment_org_workspace_installs.py - - .github/workflows/apply-org-workspace-coverage.yml - workflow_dispatch: - -permissions: - contents: read - -concurrency: - group: apply-org-workspace-install-coverage-${{ github.event.pull_request.number || github.ref }} - cancel-in-progress: true - -jobs: - apply: - if: github.actor != 'github-actions[bot]' - runs-on: ubuntu-24.04 - timeout-minutes: 10 - permissions: - contents: write - steps: - - name: Check out E2E branch - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - with: - ref: agent/recursive-install-e2e - fetch-depth: 0 - persist-credentials: false - show-progress: false - - - name: Apply and validate workspace coverage - run: | - set -euo pipefail - # The helper is intentionally exact and fail-closed before publishing. - python3 .github/augment_org_workspace_installs.py - python3 -m py_compile scripts/org_app_installs.py - grep -F 'frozen workspace repair accessed registry artifacts' scripts/org_app_installs.py - grep -F 'does not satisfy `^2`' scripts/org_app_installs.py - grep -F 'workspace copy mode leaked symlinks' scripts/org_app_installs.py - git diff --check - - - name: Publish validated workspace coverage - env: - GH_TOKEN: ${{ github.token }} - run: | - set -euo pipefail - git add scripts/org_app_installs.py - git diff --cached --check - if git diff --cached --quiet; then - echo 'workspace coverage already published' - exit 0 - fi - git config user.name 'github-actions[bot]' - git config user.email '41898282+github-actions[bot]@users.noreply.github.com' - git commit -m 'test: harden workspace install and frozen repair coverage' - git push \ - "https://x-access-token:${GH_TOKEN}@github.com/${GITHUB_REPOSITORY}.git" \ - HEAD:agent/recursive-install-e2e From 3b7713efa427cb165df1adc5199379860099e3ed Mon Sep 17 00:00:00 2001 From: Alexander Mills Date: Mon, 3 Aug 2026 15:29:10 -0500 Subject: [PATCH 52/61] test: stage workspace uninstall and adapter cleanup coverage --- .../augment_workspace_uninstall_coverage.py | 189 ++++++++++++++++++ 1 file changed, 189 insertions(+) create mode 100644 .github/augment_workspace_uninstall_coverage.py diff --git a/.github/augment_workspace_uninstall_coverage.py b/.github/augment_workspace_uninstall_coverage.py new file mode 100644 index 00000000..bd1076c4 --- /dev/null +++ b/.github/augment_workspace_uninstall_coverage.py @@ -0,0 +1,189 @@ +from pathlib import Path + + +path = Path("scripts/org_app_installs.py") +text = path.read_text(encoding="utf-8") + + +def replace_once(old: str, new: str, label: str) -> None: + global text + if new in text: + return + count = text.count(old) + if count != 1: + raise RuntimeError(f"{label}: expected one match, found {count}") + text = text.replace(old, new, 1) + + +replace_once( + '''PREFETCH_RE = re.compile( + r"recursive install prefetch: (?P\\d+) package\\(s\\), " + r"up to (?P\\d+) concurrent, (?P\\d+) downloaded" +) +''', + '''PREFETCH_RE = re.compile( + r"recursive install prefetch: (?P\\d+) package\\(s\\), " + r"up to (?P\\d+) concurrent, (?P\\d+) downloaded" +) +GENERATED_WIRING_FILES = ( + "paths.json", + "node_path", + "classpath", + "go.work", + "pythonpath", + "cargo-paths.toml", + "pub-deps.yaml", +) +''', + "add generated wiring contract", +) + +replace_once( + '''def assert_no_staging(project: Path) -> None: + staging = project / ".zpkg-staging" + if staging.exists(): + raise AssertionError(f"transaction staging leaked in {project}: {list(staging.iterdir())}") +''', + '''def assert_generated_wiring_absent(project: Path) -> None: + leaked = [ + project / ".zed" / name + for name in GENERATED_WIRING_FILES + if (project / ".zed" / name).exists() + or (project / ".zed" / name).is_symlink() + ] + if leaked: + raise AssertionError(f"uninstall left generated adapter wiring: {leaked}") + + +def assert_no_staging(project: Path) -> None: + staging = project / ".zpkg-staging" + if staging.exists(): + raise AssertionError(f"transaction staging leaked in {project}: {list(staging.iterdir())}") +''', + "add generated wiring absence assertion", +) + +replace_once( + ''' require_success(uninstalled) + assert_not_materialized(project, install_dir, packages) + if (project / ".zpkg.lock").read_bytes() != lock_bytes: +''', + ''' require_success(uninstalled) + assert_not_materialized(project, install_dir, packages) + assert_generated_wiring_absent(project) + if (project / ".zpkg.lock").read_bytes() != lock_bytes: +''', + "assert registry app wiring cleanup", +) + +replace_once( + ''' assert_projection(symlink_repo, symlink_project, "symlink") + manifest_path.write_text(manifest_before, encoding="utf-8") + + copy_repo = workspace_root / "copy" +''', + ''' assert_projection(symlink_repo, symlink_project, "symlink") + manifest_path.write_text(manifest_before, encoding="utf-8") + + symlink_uninstall = run( + zed_command(zed, registry, home, "uninstall"), + cwd=symlink_project, + env=environment, + ) + require_success(symlink_uninstall) + for projection in [ + symlink_project / "zed_modules" / "zedtest" / "ws-utils", + symlink_project / "zed_modules" / "zedtest" / "ws-core", + symlink_project / "node_modules" / "@zedtest" / "ws-utils", + symlink_project / "node_modules" / "@zedtest" / "ws-core", + ]: + if projection.exists() or projection.is_symlink(): + raise AssertionError(f"workspace uninstall left projection: {projection}") + assert_generated_wiring_absent(symlink_project) + assert_no_staging(symlink_project) + if (symlink_project / ".zpkg.lock").read_bytes() != symlink_lock: + raise AssertionError("workspace uninstall rewrote the retained lock") + + symlink_restore = run( + zed_command(zed, registry, home, "install", "--frozen"), + cwd=symlink_project, + env=environment, + ) + require_success(symlink_restore) + if PREFETCH_RE.search(symlink_restore.output): + raise AssertionError("workspace post-uninstall restore accessed registry artifacts") + if (symlink_project / ".zpkg.lock").read_bytes() != symlink_lock: + raise AssertionError("workspace post-uninstall restore rewrote the lock") + assert_projection(symlink_repo, symlink_project, "symlink") + assert_no_staging(symlink_project) + + copy_repo = workspace_root / "copy" +''', + "add symlink workspace uninstall and frozen restore", +) + +replace_once( + ''' if leaked: + raise AssertionError(f"workspace copy mode leaked symlinks: {leaked}") + + +def main() -> int: +''', + ''' if leaked: + raise AssertionError(f"workspace copy mode leaked symlinks: {leaked}") + + copy_uninstall = run( + zed_command(zed, registry, home, "uninstall"), + cwd=copy_project, + env=environment, + ) + require_success(copy_uninstall) + for projection in [ + copy_project / "zed_modules" / "zedtest" / "ws-utils", + copy_project / "zed_modules" / "zedtest" / "ws-core", + copy_project / "node_modules" / "@zedtest" / "ws-utils", + copy_project / "node_modules" / "@zedtest" / "ws-core", + ]: + if projection.exists() or projection.is_symlink(): + raise AssertionError(f"workspace copy uninstall left projection: {projection}") + assert_generated_wiring_absent(copy_project) + assert_no_staging(copy_project) + if (copy_project / ".zpkg.lock").read_bytes() != copy_lock: + raise AssertionError("workspace copy uninstall rewrote the retained lock") + + copy_restore = run( + zed_command( + zed, + registry, + home, + "install", + "--frozen", + "--install-mode", + "copy", + ), + cwd=copy_project, + env=environment, + ) + require_success(copy_restore) + if PREFETCH_RE.search(copy_restore.output): + raise AssertionError("workspace copy post-uninstall restore accessed registry artifacts") + if (copy_project / ".zpkg.lock").read_bytes() != copy_lock: + raise AssertionError("workspace copy post-uninstall restore rewrote the lock") + assert_projection(copy_repo, copy_project, "copy") + assert_no_staging(copy_project) + leaked = [ + candidate + for root in (copy_project / "zed_modules", copy_project / "node_modules") + for candidate in root.rglob("*") + if candidate.is_symlink() + ] + if leaked: + raise AssertionError(f"workspace restored copy mode leaked symlinks: {leaked}") + + +def main() -> int: +''', + "add copied workspace uninstall and frozen restore", +) + +path.write_text(text, encoding="utf-8") From db12c6930f1ec3843a9067313d14bd10d40cc47c Mon Sep 17 00:00:00 2001 From: Alexander Mills Date: Mon, 3 Aug 2026 15:32:07 -0500 Subject: [PATCH 53/61] ci: publish workspace uninstall and frozen-restore certification --- .../apply-workspace-uninstall-coverage.yml | 100 ++++++++++++++++++ 1 file changed, 100 insertions(+) create mode 100644 .github/workflows/apply-workspace-uninstall-coverage.yml diff --git a/.github/workflows/apply-workspace-uninstall-coverage.yml b/.github/workflows/apply-workspace-uninstall-coverage.yml new file mode 100644 index 00000000..b25fcfae --- /dev/null +++ b/.github/workflows/apply-workspace-uninstall-coverage.yml @@ -0,0 +1,100 @@ +name: apply workspace uninstall coverage + +on: + pull_request: + branches: + - main + paths: + - .github/augment_workspace_uninstall_coverage.py + - .github/workflows/apply-workspace-uninstall-coverage.yml + push: + branches: + - agent/recursive-install-e2e + paths: + - .github/augment_workspace_uninstall_coverage.py + - .github/workflows/apply-workspace-uninstall-coverage.yml + workflow_dispatch: + +permissions: + contents: read + +concurrency: + group: apply-workspace-uninstall-coverage-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +jobs: + apply: + if: github.actor != 'github-actions[bot]' + runs-on: ubuntu-24.04 + timeout-minutes: 15 + permissions: + contents: write + steps: + - name: Check out E2E branch + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + ref: agent/recursive-install-e2e + fetch-depth: 0 + persist-credentials: false + show-progress: false + + - name: Apply uninstall coverage and immutable implementation pin + run: | + set -euo pipefail + python3 .github/augment_workspace_uninstall_coverage.py + python3 - <<'PY' + from pathlib import Path + + old = "56e597b35deccc82bff7deee807f459a1def537d" + new = "8cc6745c75fd46dabf2e383cbcfc14db5ff933da" + workflows = [ + Path(".github/workflows/recursive-installs.yml"), + Path(".github/workflows/recursive-installs-stress.yml"), + Path(".github/workflows/install-recovery.yml"), + Path(".github/workflows/org-app-installs.yml"), + ] + for path in workflows: + text = path.read_text(encoding="utf-8") + if text.count(old) != 2: + raise RuntimeError(f"{path}: expected two old pins, found {text.count(old)}") + updated = text.replace(old, new) + if updated.count(new) != 2: + raise RuntimeError(f"{path}: expected two new pins, found {updated.count(new)}") + path.write_text(updated, encoding="utf-8") + PY + python3 -m py_compile scripts/org_app_installs.py + grep -F 'workspace uninstall left projection' scripts/org_app_installs.py + grep -F 'workspace post-uninstall restore accessed registry artifacts' scripts/org_app_installs.py + grep -F 'workspace restored copy mode leaked symlinks' scripts/org_app_installs.py + git diff --check + + - name: Commit and publish the validated E2E changes + env: + GH_TOKEN: ${{ github.token }} + run: | + set -euo pipefail + git add \ + scripts/org_app_installs.py \ + .github/workflows/recursive-installs.yml \ + .github/workflows/recursive-installs-stress.yml \ + .github/workflows/install-recovery.yml \ + .github/workflows/org-app-installs.yml + git diff --cached --check + if git diff --cached --quiet; then + echo 'workspace uninstall coverage is already published' + exit 0 + fi + git config user.name 'github-actions[bot]' + git config user.email '41898282+github-actions[bot]@users.noreply.github.com' + git commit -m 'test: certify workspace uninstall and frozen restore' + git fetch --no-tags origin agent/recursive-install-e2e + git rebase origin/agent/recursive-install-e2e + python3 -m py_compile scripts/org_app_installs.py + grep -F '8cc6745c75fd46dabf2e383cbcfc14db5ff933da' \ + .github/workflows/recursive-installs.yml \ + .github/workflows/recursive-installs-stress.yml \ + .github/workflows/install-recovery.yml \ + .github/workflows/org-app-installs.yml + git push \ + "https://x-access-token:${GH_TOKEN}@github.com/${GITHUB_REPOSITORY}.git" \ + HEAD:agent/recursive-install-e2e From 5873d494fc68329b39b154061235ff6192fb59d9 Mon Sep 17 00:00:00 2001 From: Alexander Mills Date: Mon, 3 Aug 2026 15:33:19 -0500 Subject: [PATCH 54/61] ci: publish workspace uninstall tests without workflow writes --- .../apply-workspace-uninstall-coverage.yml | 36 ++----------------- 1 file changed, 3 insertions(+), 33 deletions(-) diff --git a/.github/workflows/apply-workspace-uninstall-coverage.yml b/.github/workflows/apply-workspace-uninstall-coverage.yml index b25fcfae..a212a2e9 100644 --- a/.github/workflows/apply-workspace-uninstall-coverage.yml +++ b/.github/workflows/apply-workspace-uninstall-coverage.yml @@ -38,47 +38,22 @@ jobs: persist-credentials: false show-progress: false - - name: Apply uninstall coverage and immutable implementation pin + - name: Apply and validate workspace uninstall coverage run: | set -euo pipefail python3 .github/augment_workspace_uninstall_coverage.py - python3 - <<'PY' - from pathlib import Path - - old = "56e597b35deccc82bff7deee807f459a1def537d" - new = "8cc6745c75fd46dabf2e383cbcfc14db5ff933da" - workflows = [ - Path(".github/workflows/recursive-installs.yml"), - Path(".github/workflows/recursive-installs-stress.yml"), - Path(".github/workflows/install-recovery.yml"), - Path(".github/workflows/org-app-installs.yml"), - ] - for path in workflows: - text = path.read_text(encoding="utf-8") - if text.count(old) != 2: - raise RuntimeError(f"{path}: expected two old pins, found {text.count(old)}") - updated = text.replace(old, new) - if updated.count(new) != 2: - raise RuntimeError(f"{path}: expected two new pins, found {updated.count(new)}") - path.write_text(updated, encoding="utf-8") - PY python3 -m py_compile scripts/org_app_installs.py grep -F 'workspace uninstall left projection' scripts/org_app_installs.py grep -F 'workspace post-uninstall restore accessed registry artifacts' scripts/org_app_installs.py grep -F 'workspace restored copy mode leaked symlinks' scripts/org_app_installs.py git diff --check - - name: Commit and publish the validated E2E changes + - name: Publish the validated script change env: GH_TOKEN: ${{ github.token }} run: | set -euo pipefail - git add \ - scripts/org_app_installs.py \ - .github/workflows/recursive-installs.yml \ - .github/workflows/recursive-installs-stress.yml \ - .github/workflows/install-recovery.yml \ - .github/workflows/org-app-installs.yml + git add scripts/org_app_installs.py git diff --cached --check if git diff --cached --quiet; then echo 'workspace uninstall coverage is already published' @@ -90,11 +65,6 @@ jobs: git fetch --no-tags origin agent/recursive-install-e2e git rebase origin/agent/recursive-install-e2e python3 -m py_compile scripts/org_app_installs.py - grep -F '8cc6745c75fd46dabf2e383cbcfc14db5ff933da' \ - .github/workflows/recursive-installs.yml \ - .github/workflows/recursive-installs-stress.yml \ - .github/workflows/install-recovery.yml \ - .github/workflows/org-app-installs.yml git push \ "https://x-access-token:${GH_TOKEN}@github.com/${GITHUB_REPOSITORY}.git" \ HEAD:agent/recursive-install-e2e From 26df1bdaa7309eb37916f1e8591976afd1682995 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 3 Aug 2026 20:33:26 +0000 Subject: [PATCH 55/61] test: certify workspace uninstall and frozen restore --- scripts/org_app_installs.py | 101 ++++++++++++++++++++++++++++++++++++ 1 file changed, 101 insertions(+) diff --git a/scripts/org_app_installs.py b/scripts/org_app_installs.py index 9766383a..c20709cb 100644 --- a/scripts/org_app_installs.py +++ b/scripts/org_app_installs.py @@ -54,6 +54,15 @@ r"recursive install prefetch: (?P\d+) package\(s\), " r"up to (?P\d+) concurrent, (?P\d+) downloaded" ) +GENERATED_WIRING_FILES = ( + "paths.json", + "node_path", + "classpath", + "go.work", + "pythonpath", + "cargo-paths.toml", + "pub-deps.yaml", +) @dataclass(frozen=True) @@ -376,6 +385,17 @@ def assert_not_materialized( raise AssertionError(f"uninstall left Node adapter path {node_path}") +def assert_generated_wiring_absent(project: Path) -> None: + leaked = [ + project / ".zed" / name + for name in GENERATED_WIRING_FILES + if (project / ".zed" / name).exists() + or (project / ".zed" / name).is_symlink() + ] + if leaked: + raise AssertionError(f"uninstall left generated adapter wiring: {leaked}") + + def assert_no_staging(project: Path) -> None: staging = project / ".zpkg-staging" if staging.exists(): @@ -533,6 +553,7 @@ def exercise_app( ) require_success(uninstalled) assert_not_materialized(project, install_dir, packages) + assert_generated_wiring_absent(project) if (project / ".zpkg.lock").read_bytes() != lock_bytes: raise AssertionError(f"{repo}: uninstall rewrote the retained lockfile") if (project / ".zpkg.toml").read_bytes() != manifest_bytes: @@ -689,6 +710,38 @@ def assert_projection(repo_root: Path, project: Path, mode: str) -> None: assert_projection(symlink_repo, symlink_project, "symlink") manifest_path.write_text(manifest_before, encoding="utf-8") + symlink_uninstall = run( + zed_command(zed, registry, home, "uninstall"), + cwd=symlink_project, + env=environment, + ) + require_success(symlink_uninstall) + for projection in [ + symlink_project / "zed_modules" / "zedtest" / "ws-utils", + symlink_project / "zed_modules" / "zedtest" / "ws-core", + symlink_project / "node_modules" / "@zedtest" / "ws-utils", + symlink_project / "node_modules" / "@zedtest" / "ws-core", + ]: + if projection.exists() or projection.is_symlink(): + raise AssertionError(f"workspace uninstall left projection: {projection}") + assert_generated_wiring_absent(symlink_project) + assert_no_staging(symlink_project) + if (symlink_project / ".zpkg.lock").read_bytes() != symlink_lock: + raise AssertionError("workspace uninstall rewrote the retained lock") + + symlink_restore = run( + zed_command(zed, registry, home, "install", "--frozen"), + cwd=symlink_project, + env=environment, + ) + require_success(symlink_restore) + if PREFETCH_RE.search(symlink_restore.output): + raise AssertionError("workspace post-uninstall restore accessed registry artifacts") + if (symlink_project / ".zpkg.lock").read_bytes() != symlink_lock: + raise AssertionError("workspace post-uninstall restore rewrote the lock") + assert_projection(symlink_repo, symlink_project, "symlink") + assert_no_staging(symlink_project) + copy_repo = workspace_root / "copy" copy_fixture(source, copy_repo) copy_source_sentinel = copy_repo / "packages" / "core" / "copy-sentinel.txt" @@ -765,6 +818,54 @@ def assert_projection(repo_root: Path, project: Path, mode: str) -> None: if leaked: raise AssertionError(f"workspace copy mode leaked symlinks: {leaked}") + copy_uninstall = run( + zed_command(zed, registry, home, "uninstall"), + cwd=copy_project, + env=environment, + ) + require_success(copy_uninstall) + for projection in [ + copy_project / "zed_modules" / "zedtest" / "ws-utils", + copy_project / "zed_modules" / "zedtest" / "ws-core", + copy_project / "node_modules" / "@zedtest" / "ws-utils", + copy_project / "node_modules" / "@zedtest" / "ws-core", + ]: + if projection.exists() or projection.is_symlink(): + raise AssertionError(f"workspace copy uninstall left projection: {projection}") + assert_generated_wiring_absent(copy_project) + assert_no_staging(copy_project) + if (copy_project / ".zpkg.lock").read_bytes() != copy_lock: + raise AssertionError("workspace copy uninstall rewrote the retained lock") + + copy_restore = run( + zed_command( + zed, + registry, + home, + "install", + "--frozen", + "--install-mode", + "copy", + ), + cwd=copy_project, + env=environment, + ) + require_success(copy_restore) + if PREFETCH_RE.search(copy_restore.output): + raise AssertionError("workspace copy post-uninstall restore accessed registry artifacts") + if (copy_project / ".zpkg.lock").read_bytes() != copy_lock: + raise AssertionError("workspace copy post-uninstall restore rewrote the lock") + assert_projection(copy_repo, copy_project, "copy") + assert_no_staging(copy_project) + leaked = [ + candidate + for root in (copy_project / "zed_modules", copy_project / "node_modules") + for candidate in root.rglob("*") + if candidate.is_symlink() + ] + if leaked: + raise AssertionError(f"workspace restored copy mode leaked symlinks: {leaked}") + def main() -> int: parser = argparse.ArgumentParser() From 72d80e795cf4aace87d5f1abf33413c0cc36f3a8 Mon Sep 17 00:00:00 2001 From: Alexander Mills Date: Mon, 3 Aug 2026 15:33:58 -0500 Subject: [PATCH 56/61] test: pin recursive graph suite to workspace uninstall head --- .github/workflows/recursive-installs.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/recursive-installs.yml b/.github/workflows/recursive-installs.yml index a351a25a..b3abcd74 100644 --- a/.github/workflows/recursive-installs.yml +++ b/.github/workflows/recursive-installs.yml @@ -9,7 +9,7 @@ on: zed_cli_ref: description: Immutable zed-cli commit to test required: false - default: 56e597b35deccc82bff7deee807f459a1def537d + default: 8cc6745c75fd46dabf2e383cbcfc14db5ff933da permissions: contents: read @@ -25,7 +25,7 @@ defaults: env: CARGO_TERM_COLOR: always RUST_BACKTRACE: "1" - ZED_CLI_REF: ${{ github.event.inputs.zed_cli_ref || '56e597b35deccc82bff7deee807f459a1def537d' }} + ZED_CLI_REF: ${{ github.event.inputs.zed_cli_ref || '8cc6745c75fd46dabf2e383cbcfc14db5ff933da' }} ZED_INTERFACES_REF: dc0e0a0620b9462817950b552d3d334a184b1cb1 jobs: From 7feb1e5b07dcfdd3802b5ef669b58dbd73950570 Mon Sep 17 00:00:00 2001 From: Alexander Mills Date: Mon, 3 Aug 2026 15:34:32 -0500 Subject: [PATCH 57/61] test: pin recursive stress suite to workspace uninstall head --- .github/workflows/recursive-installs-stress.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/recursive-installs-stress.yml b/.github/workflows/recursive-installs-stress.yml index b701d17b..20a29b84 100644 --- a/.github/workflows/recursive-installs-stress.yml +++ b/.github/workflows/recursive-installs-stress.yml @@ -9,7 +9,7 @@ on: zed_cli_ref: description: Immutable zed-cli commit to test required: false - default: 56e597b35deccc82bff7deee807f459a1def537d + default: 8cc6745c75fd46dabf2e383cbcfc14db5ff933da permissions: contents: read @@ -25,7 +25,7 @@ defaults: env: CARGO_TERM_COLOR: always RUST_BACKTRACE: "1" - ZED_CLI_REF: ${{ github.event.inputs.zed_cli_ref || '56e597b35deccc82bff7deee807f459a1def537d' }} + ZED_CLI_REF: ${{ github.event.inputs.zed_cli_ref || '8cc6745c75fd46dabf2e383cbcfc14db5ff933da' }} ZED_INTERFACES_REF: dc0e0a0620b9462817950b552d3d334a184b1cb1 jobs: From 39a2d986d3a840dc19b90abd511f9646c7fc5d77 Mon Sep 17 00:00:00 2001 From: Alexander Mills Date: Mon, 3 Aug 2026 15:35:05 -0500 Subject: [PATCH 58/61] test: pin recovery matrix to workspace uninstall head --- .github/workflows/install-recovery.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/install-recovery.yml b/.github/workflows/install-recovery.yml index be32698d..c7d20b5c 100644 --- a/.github/workflows/install-recovery.yml +++ b/.github/workflows/install-recovery.yml @@ -15,7 +15,7 @@ on: zed_cli_ref: description: Immutable zed-cli commit to test required: false - default: 56e597b35deccc82bff7deee807f459a1def537d + default: 8cc6745c75fd46dabf2e383cbcfc14db5ff933da permissions: contents: read @@ -31,7 +31,7 @@ defaults: env: CARGO_TERM_COLOR: always RUST_BACKTRACE: "1" - ZED_CLI_REF: ${{ github.event.inputs.zed_cli_ref || '56e597b35deccc82bff7deee807f459a1def537d' }} + ZED_CLI_REF: ${{ github.event.inputs.zed_cli_ref || '8cc6745c75fd46dabf2e383cbcfc14db5ff933da' }} ZED_INTERFACES_REF: dc0e0a0620b9462817950b552d3d334a184b1cb1 jobs: From 7fd66784a418cfe8c4ba39fd3bdd8e5d01b490a1 Mon Sep 17 00:00:00 2001 From: Alexander Mills Date: Mon, 3 Aug 2026 15:35:31 -0500 Subject: [PATCH 59/61] test: pin real org installs to workspace uninstall head --- .github/workflows/org-app-installs.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/org-app-installs.yml b/.github/workflows/org-app-installs.yml index 50e1c22d..90ac9c13 100644 --- a/.github/workflows/org-app-installs.yml +++ b/.github/workflows/org-app-installs.yml @@ -15,7 +15,7 @@ on: zed_cli_ref: description: Immutable zed-cli commit to test required: false - default: 56e597b35deccc82bff7deee807f459a1def537d + default: 8cc6745c75fd46dabf2e383cbcfc14db5ff933da permissions: contents: read @@ -31,7 +31,7 @@ defaults: env: CARGO_TERM_COLOR: always RUST_BACKTRACE: "1" - ZED_CLI_REF: ${{ github.event.inputs.zed_cli_ref || '56e597b35deccc82bff7deee807f459a1def537d' }} + ZED_CLI_REF: ${{ github.event.inputs.zed_cli_ref || '8cc6745c75fd46dabf2e383cbcfc14db5ff933da' }} ZED_INTERFACES_REF: dc0e0a0620b9462817950b552d3d334a184b1cb1 jobs: From a65f56c47cf8bc594c62c5e4d68c51e21ad47f83 Mon Sep 17 00:00:00 2001 From: Alexander Mills Date: Mon, 3 Aug 2026 15:36:08 -0500 Subject: [PATCH 60/61] ci: remove completed workspace uninstall coverage helper --- .../augment_workspace_uninstall_coverage.py | 189 ------------------ 1 file changed, 189 deletions(-) delete mode 100644 .github/augment_workspace_uninstall_coverage.py diff --git a/.github/augment_workspace_uninstall_coverage.py b/.github/augment_workspace_uninstall_coverage.py deleted file mode 100644 index bd1076c4..00000000 --- a/.github/augment_workspace_uninstall_coverage.py +++ /dev/null @@ -1,189 +0,0 @@ -from pathlib import Path - - -path = Path("scripts/org_app_installs.py") -text = path.read_text(encoding="utf-8") - - -def replace_once(old: str, new: str, label: str) -> None: - global text - if new in text: - return - count = text.count(old) - if count != 1: - raise RuntimeError(f"{label}: expected one match, found {count}") - text = text.replace(old, new, 1) - - -replace_once( - '''PREFETCH_RE = re.compile( - r"recursive install prefetch: (?P\\d+) package\\(s\\), " - r"up to (?P\\d+) concurrent, (?P\\d+) downloaded" -) -''', - '''PREFETCH_RE = re.compile( - r"recursive install prefetch: (?P\\d+) package\\(s\\), " - r"up to (?P\\d+) concurrent, (?P\\d+) downloaded" -) -GENERATED_WIRING_FILES = ( - "paths.json", - "node_path", - "classpath", - "go.work", - "pythonpath", - "cargo-paths.toml", - "pub-deps.yaml", -) -''', - "add generated wiring contract", -) - -replace_once( - '''def assert_no_staging(project: Path) -> None: - staging = project / ".zpkg-staging" - if staging.exists(): - raise AssertionError(f"transaction staging leaked in {project}: {list(staging.iterdir())}") -''', - '''def assert_generated_wiring_absent(project: Path) -> None: - leaked = [ - project / ".zed" / name - for name in GENERATED_WIRING_FILES - if (project / ".zed" / name).exists() - or (project / ".zed" / name).is_symlink() - ] - if leaked: - raise AssertionError(f"uninstall left generated adapter wiring: {leaked}") - - -def assert_no_staging(project: Path) -> None: - staging = project / ".zpkg-staging" - if staging.exists(): - raise AssertionError(f"transaction staging leaked in {project}: {list(staging.iterdir())}") -''', - "add generated wiring absence assertion", -) - -replace_once( - ''' require_success(uninstalled) - assert_not_materialized(project, install_dir, packages) - if (project / ".zpkg.lock").read_bytes() != lock_bytes: -''', - ''' require_success(uninstalled) - assert_not_materialized(project, install_dir, packages) - assert_generated_wiring_absent(project) - if (project / ".zpkg.lock").read_bytes() != lock_bytes: -''', - "assert registry app wiring cleanup", -) - -replace_once( - ''' assert_projection(symlink_repo, symlink_project, "symlink") - manifest_path.write_text(manifest_before, encoding="utf-8") - - copy_repo = workspace_root / "copy" -''', - ''' assert_projection(symlink_repo, symlink_project, "symlink") - manifest_path.write_text(manifest_before, encoding="utf-8") - - symlink_uninstall = run( - zed_command(zed, registry, home, "uninstall"), - cwd=symlink_project, - env=environment, - ) - require_success(symlink_uninstall) - for projection in [ - symlink_project / "zed_modules" / "zedtest" / "ws-utils", - symlink_project / "zed_modules" / "zedtest" / "ws-core", - symlink_project / "node_modules" / "@zedtest" / "ws-utils", - symlink_project / "node_modules" / "@zedtest" / "ws-core", - ]: - if projection.exists() or projection.is_symlink(): - raise AssertionError(f"workspace uninstall left projection: {projection}") - assert_generated_wiring_absent(symlink_project) - assert_no_staging(symlink_project) - if (symlink_project / ".zpkg.lock").read_bytes() != symlink_lock: - raise AssertionError("workspace uninstall rewrote the retained lock") - - symlink_restore = run( - zed_command(zed, registry, home, "install", "--frozen"), - cwd=symlink_project, - env=environment, - ) - require_success(symlink_restore) - if PREFETCH_RE.search(symlink_restore.output): - raise AssertionError("workspace post-uninstall restore accessed registry artifacts") - if (symlink_project / ".zpkg.lock").read_bytes() != symlink_lock: - raise AssertionError("workspace post-uninstall restore rewrote the lock") - assert_projection(symlink_repo, symlink_project, "symlink") - assert_no_staging(symlink_project) - - copy_repo = workspace_root / "copy" -''', - "add symlink workspace uninstall and frozen restore", -) - -replace_once( - ''' if leaked: - raise AssertionError(f"workspace copy mode leaked symlinks: {leaked}") - - -def main() -> int: -''', - ''' if leaked: - raise AssertionError(f"workspace copy mode leaked symlinks: {leaked}") - - copy_uninstall = run( - zed_command(zed, registry, home, "uninstall"), - cwd=copy_project, - env=environment, - ) - require_success(copy_uninstall) - for projection in [ - copy_project / "zed_modules" / "zedtest" / "ws-utils", - copy_project / "zed_modules" / "zedtest" / "ws-core", - copy_project / "node_modules" / "@zedtest" / "ws-utils", - copy_project / "node_modules" / "@zedtest" / "ws-core", - ]: - if projection.exists() or projection.is_symlink(): - raise AssertionError(f"workspace copy uninstall left projection: {projection}") - assert_generated_wiring_absent(copy_project) - assert_no_staging(copy_project) - if (copy_project / ".zpkg.lock").read_bytes() != copy_lock: - raise AssertionError("workspace copy uninstall rewrote the retained lock") - - copy_restore = run( - zed_command( - zed, - registry, - home, - "install", - "--frozen", - "--install-mode", - "copy", - ), - cwd=copy_project, - env=environment, - ) - require_success(copy_restore) - if PREFETCH_RE.search(copy_restore.output): - raise AssertionError("workspace copy post-uninstall restore accessed registry artifacts") - if (copy_project / ".zpkg.lock").read_bytes() != copy_lock: - raise AssertionError("workspace copy post-uninstall restore rewrote the lock") - assert_projection(copy_repo, copy_project, "copy") - assert_no_staging(copy_project) - leaked = [ - candidate - for root in (copy_project / "zed_modules", copy_project / "node_modules") - for candidate in root.rglob("*") - if candidate.is_symlink() - ] - if leaked: - raise AssertionError(f"workspace restored copy mode leaked symlinks: {leaked}") - - -def main() -> int: -''', - "add copied workspace uninstall and frozen restore", -) - -path.write_text(text, encoding="utf-8") From 4b1143e63eb6353a6839bffa6ddcb68873ffe222 Mon Sep 17 00:00:00 2001 From: Alexander Mills Date: Mon, 3 Aug 2026 15:36:14 -0500 Subject: [PATCH 61/61] ci: remove completed workspace uninstall coverage workflow --- .../apply-workspace-uninstall-coverage.yml | 70 ------------------- 1 file changed, 70 deletions(-) delete mode 100644 .github/workflows/apply-workspace-uninstall-coverage.yml diff --git a/.github/workflows/apply-workspace-uninstall-coverage.yml b/.github/workflows/apply-workspace-uninstall-coverage.yml deleted file mode 100644 index a212a2e9..00000000 --- a/.github/workflows/apply-workspace-uninstall-coverage.yml +++ /dev/null @@ -1,70 +0,0 @@ -name: apply workspace uninstall coverage - -on: - pull_request: - branches: - - main - paths: - - .github/augment_workspace_uninstall_coverage.py - - .github/workflows/apply-workspace-uninstall-coverage.yml - push: - branches: - - agent/recursive-install-e2e - paths: - - .github/augment_workspace_uninstall_coverage.py - - .github/workflows/apply-workspace-uninstall-coverage.yml - workflow_dispatch: - -permissions: - contents: read - -concurrency: - group: apply-workspace-uninstall-coverage-${{ github.event.pull_request.number || github.ref }} - cancel-in-progress: true - -jobs: - apply: - if: github.actor != 'github-actions[bot]' - runs-on: ubuntu-24.04 - timeout-minutes: 15 - permissions: - contents: write - steps: - - name: Check out E2E branch - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - with: - ref: agent/recursive-install-e2e - fetch-depth: 0 - persist-credentials: false - show-progress: false - - - name: Apply and validate workspace uninstall coverage - run: | - set -euo pipefail - python3 .github/augment_workspace_uninstall_coverage.py - python3 -m py_compile scripts/org_app_installs.py - grep -F 'workspace uninstall left projection' scripts/org_app_installs.py - grep -F 'workspace post-uninstall restore accessed registry artifacts' scripts/org_app_installs.py - grep -F 'workspace restored copy mode leaked symlinks' scripts/org_app_installs.py - git diff --check - - - name: Publish the validated script change - env: - GH_TOKEN: ${{ github.token }} - run: | - set -euo pipefail - git add scripts/org_app_installs.py - git diff --cached --check - if git diff --cached --quiet; then - echo 'workspace uninstall coverage is already published' - exit 0 - fi - git config user.name 'github-actions[bot]' - git config user.email '41898282+github-actions[bot]@users.noreply.github.com' - git commit -m 'test: certify workspace uninstall and frozen restore' - git fetch --no-tags origin agent/recursive-install-e2e - git rebase origin/agent/recursive-install-e2e - python3 -m py_compile scripts/org_app_installs.py - git push \ - "https://x-access-token:${GH_TOKEN}@github.com/${GITHUB_REPOSITORY}.git" \ - HEAD:agent/recursive-install-e2e