From 776365fdf775aa7ed64382e37636f364f86e1129 Mon Sep 17 00:00:00 2001 From: Codex Automation Date: Mon, 24 Aug 2026 20:49:06 -0500 Subject: [PATCH] fix(agent-runs): gate the editable install on a DECLARED distribution, not on pyproject.toml existing All four agent-run workflows did: if [ -f pyproject.toml ]; then python -m pip install -e ".[dev]" || python -m pip install -e . fi Gated on the FILE'S PRESENCE, which is the same defect `reusable-10-ci-python.yml` already fixed for the CI lane with `pyproject_declares_distribution()`. This applies that existing helper to the agent lane; it invents nothing. WHY PRESENCE IS THE WRONG TEST. This fleet actively supports repos that use pyproject.toml purely as TOOL CONFIG -- ruff/mypy/pytest settings with no `[project]` and no build backend. stranske/Orchestrator is deliberately one: it has 99 top-level modules and declaring `[project]` would demand a hand-maintained module list that goes stale on every rename. AND IT DOES NOT FAIL LOUDLY, WHICH IS THE ACTUAL PROBLEM. On such a repo `pip install -e .` succeeds: setuptools falls back to a legacy build and produces `UNKNOWN.egg-info/` beside the sources -- named UNKNOWN precisely because no distribution was declared. So the job believes it installed the project and has installed something anonymous, and anything downstream that assumes an importable installed package is running on a false premise. Then the debris gets COMMITTED. Observed on Orchestrator 2026-08-24: the agent run's own `git add -A` staged all four generated files and the autofix bot pushed them to the PR -- twice, on two different PRs within an hour, because a job that stages whatever the working tree contains turns an unignored generated file into a matter of timing. CodeRabbit caught it; nothing in the repo's own hygiene suite did. MEASURED BLAST RADIUS, because this is shared fleet CI. The guard was run against the real repos rather than reasoned about: Orchestrator SKIP <- the only behaviour change, and the repo that was broken Workflows INSTALL Trend_Model_Project INSTALL Counter_Risk INSTALL trip-planner INSTALL Synthetic shapes: tool-config-only -> SKIP; `[project]` + `[build-system]` -> INSTALL; setup.py only -> INSTALL; no python config -> SKIP. So every repo that installs today still installs, and the only repo whose behaviour changes is the one generating the debris. The skip is ANNOUNCED rather than silent -- it echoes which declarations were absent, so a repo that genuinely wanted an editable install and forgot `[build-system]` sees why it was skipped instead of a mysteriously missing dependency. Fails toward a stated reason, not silence. All four files still parse as YAML. Co-Authored-By: Claude Opus 5 --- .github/workflows/reusable-claude-run.yml | 22 +++++++++++++++++++++- .github/workflows/reusable-codex-run.yml | 22 +++++++++++++++++++++- .github/workflows/reusable-cursor-run.yml | 22 +++++++++++++++++++++- .github/workflows/reusable-gemini-run.yml | 22 +++++++++++++++++++++- 4 files changed, 84 insertions(+), 4 deletions(-) diff --git a/.github/workflows/reusable-claude-run.yml b/.github/workflows/reusable-claude-run.yml index fb94fa9b79..fbb67f423f 100644 --- a/.github/workflows/reusable-claude-run.yml +++ b/.github/workflows/reusable-claude-run.yml @@ -451,8 +451,28 @@ jobs: echo "scripts/requirements.txt not found; skipping scripts requirements." fi - if [ -f pyproject.toml ]; then + # GATED ON A DECLARED DISTRIBUTION, not on the file's presence. `[ -f pyproject.toml ]` + # is true for a repo that uses pyproject.toml purely as TOOL CONFIG (ruff/mypy/pytest + # settings, no `[project]`, no build backend) -- a shape this fleet actively supports and + # `reusable-10-ci-python.yml` already guards for with the same helper below. On such a + # repo `pip install -e .` does not fail loudly; setuptools runs a legacy build and emits + # `UNKNOWN.egg-info/` beside the sources, so CI believes it installed the project and has + # in fact installed something anonymous. Observed on stranske/Orchestrator 2026-08-24, + # where the agent run's own `git add -A` then COMMITTED all four generated files to the + # PR. Two costs, one silent: wasted install time on every agent run, and build debris in + # the working tree of a job that stages whatever it finds. + pyproject_declares_distribution() { + [ -f pyproject.toml ] || return 1 + grep -Eq \ + '^[[:space:]]*\[[[:space:]]*(project|build-system|tool\.poetry)[[:space:]]*\]' \ + pyproject.toml + } + + if pyproject_declares_distribution || [ -f setup.cfg ] || [ -f setup.py ]; then python -m pip install -e ".[dev]" || python -m pip install -e . + elif [ -f pyproject.toml ]; then + echo "pyproject.toml present but declares no distribution ([project]/[build-system]/" \ + "[tool.poetry]) and no setup.py/setup.cfg; skipping editable install." fi - name: Install Workflows repo LLM dependencies diff --git a/.github/workflows/reusable-codex-run.yml b/.github/workflows/reusable-codex-run.yml index 52e93037dd..d10a3c144d 100644 --- a/.github/workflows/reusable-codex-run.yml +++ b/.github/workflows/reusable-codex-run.yml @@ -506,8 +506,28 @@ jobs: echo "scripts/requirements.txt not found; skipping scripts requirements." fi - if [ -f pyproject.toml ]; then + # GATED ON A DECLARED DISTRIBUTION, not on the file's presence. `[ -f pyproject.toml ]` + # is true for a repo that uses pyproject.toml purely as TOOL CONFIG (ruff/mypy/pytest + # settings, no `[project]`, no build backend) -- a shape this fleet actively supports and + # `reusable-10-ci-python.yml` already guards for with the same helper below. On such a + # repo `pip install -e .` does not fail loudly; setuptools runs a legacy build and emits + # `UNKNOWN.egg-info/` beside the sources, so CI believes it installed the project and has + # in fact installed something anonymous. Observed on stranske/Orchestrator 2026-08-24, + # where the agent run's own `git add -A` then COMMITTED all four generated files to the + # PR. Two costs, one silent: wasted install time on every agent run, and build debris in + # the working tree of a job that stages whatever it finds. + pyproject_declares_distribution() { + [ -f pyproject.toml ] || return 1 + grep -Eq \ + '^[[:space:]]*\[[[:space:]]*(project|build-system|tool\.poetry)[[:space:]]*\]' \ + pyproject.toml + } + + if pyproject_declares_distribution || [ -f setup.cfg ] || [ -f setup.py ]; then python -m pip install -e ".[dev]" || python -m pip install -e . + elif [ -f pyproject.toml ]; then + echo "pyproject.toml present but declares no distribution ([project]/[build-system]/" \ + "[tool.poetry]) and no setup.py/setup.cfg; skipping editable install." fi - name: Install Workflows repo LLM dependencies diff --git a/.github/workflows/reusable-cursor-run.yml b/.github/workflows/reusable-cursor-run.yml index d824367b4c..1d5a98c38b 100644 --- a/.github/workflows/reusable-cursor-run.yml +++ b/.github/workflows/reusable-cursor-run.yml @@ -458,8 +458,28 @@ jobs: echo "scripts/requirements.txt not found; skipping scripts requirements." fi - if [ -f pyproject.toml ]; then + # GATED ON A DECLARED DISTRIBUTION, not on the file's presence. `[ -f pyproject.toml ]` + # is true for a repo that uses pyproject.toml purely as TOOL CONFIG (ruff/mypy/pytest + # settings, no `[project]`, no build backend) -- a shape this fleet actively supports and + # `reusable-10-ci-python.yml` already guards for with the same helper below. On such a + # repo `pip install -e .` does not fail loudly; setuptools runs a legacy build and emits + # `UNKNOWN.egg-info/` beside the sources, so CI believes it installed the project and has + # in fact installed something anonymous. Observed on stranske/Orchestrator 2026-08-24, + # where the agent run's own `git add -A` then COMMITTED all four generated files to the + # PR. Two costs, one silent: wasted install time on every agent run, and build debris in + # the working tree of a job that stages whatever it finds. + pyproject_declares_distribution() { + [ -f pyproject.toml ] || return 1 + grep -Eq \ + '^[[:space:]]*\[[[:space:]]*(project|build-system|tool\.poetry)[[:space:]]*\]' \ + pyproject.toml + } + + if pyproject_declares_distribution || [ -f setup.cfg ] || [ -f setup.py ]; then python -m pip install -e ".[dev]" || python -m pip install -e . + elif [ -f pyproject.toml ]; then + echo "pyproject.toml present but declares no distribution ([project]/[build-system]/" \ + "[tool.poetry]) and no setup.py/setup.cfg; skipping editable install." fi - name: Install Workflows repo LLM dependencies diff --git a/.github/workflows/reusable-gemini-run.yml b/.github/workflows/reusable-gemini-run.yml index 06ebaf1bc4..0f3f5e66c2 100644 --- a/.github/workflows/reusable-gemini-run.yml +++ b/.github/workflows/reusable-gemini-run.yml @@ -466,8 +466,28 @@ jobs: echo "scripts/requirements.txt not found; skipping scripts requirements." fi - if [ -f pyproject.toml ]; then + # GATED ON A DECLARED DISTRIBUTION, not on the file's presence. `[ -f pyproject.toml ]` + # is true for a repo that uses pyproject.toml purely as TOOL CONFIG (ruff/mypy/pytest + # settings, no `[project]`, no build backend) -- a shape this fleet actively supports and + # `reusable-10-ci-python.yml` already guards for with the same helper below. On such a + # repo `pip install -e .` does not fail loudly; setuptools runs a legacy build and emits + # `UNKNOWN.egg-info/` beside the sources, so CI believes it installed the project and has + # in fact installed something anonymous. Observed on stranske/Orchestrator 2026-08-24, + # where the agent run's own `git add -A` then COMMITTED all four generated files to the + # PR. Two costs, one silent: wasted install time on every agent run, and build debris in + # the working tree of a job that stages whatever it finds. + pyproject_declares_distribution() { + [ -f pyproject.toml ] || return 1 + grep -Eq \ + '^[[:space:]]*\[[[:space:]]*(project|build-system|tool\.poetry)[[:space:]]*\]' \ + pyproject.toml + } + + if pyproject_declares_distribution || [ -f setup.cfg ] || [ -f setup.py ]; then python -m pip install -e ".[dev]" || python -m pip install -e . + elif [ -f pyproject.toml ]; then + echo "pyproject.toml present but declares no distribution ([project]/[build-system]/" \ + "[tool.poetry]) and no setup.py/setup.cfg; skipping editable install." fi - name: Install Workflows repo LLM dependencies