Skip to content

ci(tui): run only python_agent_e2e for agent-only changes; keep the rust-cache key across image updates - #48

Merged
zy1233 merged 2 commits into
mainfrom
ci/tui-e2e-only-for-agent-changes
Oct 28, 2026
Merged

zy1233 merged 2 commits into
mainfrom
ci/tui-e2e-only-for-agent-changes

Conversation

@zy1233

@zy1233 zy1233 commented Oct 28, 2026 •

Copy link
Copy Markdown
Owner

Summary

Two changes, one commit each:

  1. A PR that changes only the Python agent no longer starts the whole TUI CI. It starts a new workflow that runs the one test target that reads the agent (2727c68).
  2. The cache key of the TUI CI no longer changes when GitHub updates the runner image (8125744, see "The cache key" below).
what the PR changes                                         starts before   starts after
tui/**, scripts/tui_baseline.{py,toml}, tui-ci.yml          TUI CI          TUI CI            (same)
pi_agent_cli/**, tests/_tool_agent.py                       TUI CI          TUI E2E           (changed)
both of the above                                           TUI CI          TUI CI + TUI E2E
 # .github/workflows/tui-ci.yml: on.push.paths and on.pull_request.paths
       - "tui/**"
       - "scripts/tui_baseline.py"
       - "scripts/tui_baseline.toml"
       - ".github/workflows/tui-ci.yml"
-      - "packages/pi-agent-cli/pi_agent_cli/**"
-      - "packages/pi-agent-cli/tests/_tool_agent.py"
.github/workflows/tui-e2e.yml   (new; job `python_agent_e2e (Linux x86_64)`)
  checkout, Python 3.12, pinned Rust toolchain, protoc, nextest 0.9.148
  rust-cache: the key of the `test` job of tui-ci.yml, restore only (save-if "false")
  pip install the agent; PI_E2E_PYTHON; PI_E2E_REQUIRED=1
  cargo nextest run --locked --no-fail-fast -p pi-pager --test python_agent_e2e

Evidence

  • Before: a PR that changed only pi_agent_cli/** ran the whole cargo nextest job: feat(cli): add --setup and offer it to ACP clients as a terminal login #46 took 25m38s, 32m1s (those two red, on a Rust test that feat(cli): add --setup and offer it to ACP clients as a terminal login #46 does not touch; fixed in fix(tui): read every age of the queue janitor before it deletes a file #47) and 35m17s.
    After: the new job python_agent_e2e of this PR took 12m25s, with no cache found (the new image, see below): the build 11m16s, then 14 tests run: 14 passed, 0 skipped in 10.9 s (so PI_E2E_REQUIRED=1 and the Python agent worked). This PR changes both workflow files, so both workflows run on it.
    Probe: #49 (a draft on top of this branch, closed after the check) changed one comment line in pi_agent_cli/__init__.py and nothing else. It started CI and TUI E2E. It did not start TUI CI: its five checks were Ruff, pytest (three Python versions) and python_agent_e2e.

  • All checks pass on both commits, with no cache each time (the key is new on the first commit, and on the second). 2727c68: Gates + cargo check 3m54s, cargo nextest 35m30s, python_agent_e2e 12m25s. 8125744: Gates + cargo check 5m39s, cargo nextest 24m10s, python_agent_e2e 7m35s. Ruff and pytest (three Python versions) pass on both. The release job runs only on a manual start (workflow_dispatch), so it is skipped. The same cold job took 35m30s and 24m10s: the runners are not equal.

  • actionlint 1.7.12: no finding in the two workflows (none in the old tui-ci.yml either).

  • Ten new tests in scripts/tests/test_tui_baseline.py keep the two workflows in line. Six are for the end-to-end workflow (the paths, the command that suite_command builds, the [test.env] variables, the install lines, the cache key). Four are for the toolchain step (the jobs are read, the step is the same in every job and comes before the cache step, and both workflows set the same variables that the cache key hashes). Windows: 60 passed, 3 skipped (this file). Linux: the four new tests passed in the pytest jobs of CI (3052 passed, 39 skipped on Python 3.12). Ruff check and format check pass. shellcheck finds nothing in the step, and a run of the step with stub tools (Git Bash) did what it should, also when an uninstall fails.

  • 13 changes of the workflow files, each one fails the test that is meant to catch it:

    agent path back in tui-ci.yml        -> a_change_of_the_python_agent_alone_starts_...
    renamed path / file does not watch itself (2)  -> same test
    flag removed / other target (2)      -> ..._flags_of_the_script
    other TERM / PI_HOME set (2)         -> ..._environment_of_the_manifest
    PI_E2E_REQUIRED=0 / other nextest / other pip line (3) -> ..._installs_the_agent_and_nextest...
    cache that saves / other key (2)     -> ..._never_saves_it
    changed event header                 -> the_trigger_paths_of_the_full_workflow_are_read
    
  • 8 more changes for the toolchain step, each one fails the test that is meant to catch it: the step renamed, the step after rust-cache, rustup default changed in one job, no working-directory in one job, no uninstall in one job, RUST_BACKTRACE changed in one workflow, RUSTFLAGS set in one workflow, and the jobs: header changed (a helper must not pass by finding nothing).

  • The branch rules of main (ruleset) have no required status check, so a PR without the TUI CI check is not blocked.

The cache key

The cache of the test job missed whenever GitHub changed the Rust toolchain that is pre-installed on the image. rust-cache puts the versions of all Rust toolchains on the runner into the key:

image 20261012.334.1: Rust 1.94.0 + 1.99.0  key ...-ae3ff0ac-5a0e89f6  hit (restore 2m21s); run took 18m28s
image 20261025.353.1: Rust 1.94.0 + 1.99.1  key ...-e5739716-5a0e89f6  miss; the runs on this image compiled everything

Only main saves the cache (save-if), so a miss stays until a run on main saves the new key.

The fix is one step, Keep only the pinned Rust toolchain, in every job that installs the pinned toolchain (the three jobs of tui-ci.yml and the job of tui-e2e.yml). It runs before rust-cache:

pinned="$(basename "$(rustc --print sysroot)")"
rustup default "$pinned"
for t in $(rustup toolchain list --quiet); do
  [ "$t" = "$pinned" ] || rustup toolchain uninstall "$t" || echo "::warning::could not uninstall the Rust toolchain $t"
done
rustup toolchain list

rustc --print sysroot runs in tui/, so it gives the toolchain that tui/rust-toolchain.toml selects. After the step, the hash holds one rustc version, and an image update does not change the key. The job does not fail when an uninstall fails (a warning only): the build does not need the uninstall.

Do not use add-rust-environment-hash-key: false for this. In rust-cache v2 (src/config.ts, src/restore.ts, src/save.ts) it also removes the hash of Cargo.lock from the key. The key would never change, every run would find an exact hit and skip the save (Cache up-to-date.), and the cache would go stale.

The first run on main after the merge builds everything once and saves the cache. The end-to-end job restores that cache from then on.

Check of the step in CI (run 40653787608, image 20261025.353.1). The logs of the jobs Gates + cargo check and python_agent_e2e show:

info: uninstalling toolchain stable-x86_64-unknown-linux-gnu
info: toolchain stable-x86_64-unknown-linux-gnu uninstalled
1.94.0-x86_64-unknown-linux-gnu (active, default)       <- `rustup toolchain list` after the step

rust-cache, "Environment considered":
  Rust Versions: 1.94.0 x86_64-unknown-linux-gnu 4a4ef493e3a1...      (before: 1.94.0 and 1.99.1)
  CARGO_INCREMENTAL, CARGO_NET_RETRY, CARGO_PROFILE_DEV_DEBUG, CARGO_TERM_COLOR, RUST_BACKTRACE

job check:  key v0-rust-tui-linux-x86_64-check-Linux-x64-52c92e90-5a0e89f6
job e2e:    key v0-rust-tui-linux-x86_64-test-Linux-x64-52c92e90-5a0e89f6   (before: ...-e5739716-5a0e89f6)

The environment hash (52c92e90) is the same in both jobs. It depends on the pinned rustc version and on five variables that the workflow sets, and on nothing from the image. Both jobs found no cache, because the key is new. python_agent_e2e passed (14 tests run, 14 passed). Its two runs with no cache took 12m25s (build 11m16s) and 7m35s (build 6m38s): the runners differ, so a cold time is a range, not one number.

The full run passed too: cargo nextest took 24m10s with no cache (ten suites, 8,491 tests run and passed, 22 skipped). Its key is the same as the key of the e2e job: v0-rust-tui-linux-x86_64-test-Linux-x64-52c92e90-5a0e89f6. So the first run on main saves one cache for both jobs.

Merge Danger

Door: two-way. Revert the two commits and the old workflows are back.

Blast Radius: CI only. No product code and no test of the Rust workspace change. A PR that changes only pi_agent_cli/** no longer runs the other nine suites and the Gates + cargo check job of the TUI CI. None of them reads the Python agent. Any other change of the Python tree (pi_agent_core, pi_agent_harness, other packages) did not start the TUI CI before and does not now.

The new step uninstalls the other Rust toolchains of the image on the runner of the job (a GitHub-hosted VM that is deleted after the job). No step of these two files uses another toolchain than the pinned one: there is no cargo +... and no rustup run. release-baseline starts only by hand.

A change that touches only `packages/pi-agent-cli/pi_agent_cli/**` or
`packages/pi-agent-cli/tests/_tool_agent.py` started the whole TUI CI, because the end-to-end
tests of the pager (`python_agent_e2e`) talk to the Python agent. The TUI CI builds and runs ten
suites and takes 18 to 35 minutes. A change of the Python agent can change only that one test
target. The runs of PR #46 (`--setup`, Python only) took 25 to 35 minutes, and the first two
failed on a Rust test that the PR did not touch (#47).

Change.
- New workflow `tui-e2e.yml` (job `python_agent_e2e`): the two paths above and its own file
  start it. It runs `cargo nextest run --locked --no-fail-fast -p pi-pager --test
  python_agent_e2e`: the command that `scripts/tui_baseline.py` builds for that target of the
  `pi-pager` suite, with the terminal variables of `[test.env]`. The Python agent is installed
  as in `tui-ci.yml`, and `PI_E2E_REQUIRED=1` stays, so a missing Python is a failure.
- `tui-ci.yml` no longer lists the two paths. It starts for `tui/**`, the baseline script, the
  manifest and its own file, and it still runs these tests. A change of both runs both workflows.
- Cache: the new job restores the cache of the `test` job (same key) and never saves. A cache
  with the same key but fewer artifacts would stop the `test` job from saving the whole one,
  because a key is saved once.
- Six tests in `scripts/tests/test_tui_baseline.py` keep the two workflows in line: the paths,
  the command, the environment, the install lines (agent, nextest) and the cache key.
- AGENTS.md (root and `pi-agent-cli`), `docs/baselines/tui.md`, the comment in
  `scripts/tui_baseline.toml` and CHANGELOG [Unreleased] say it.

Checked:
- actionlint 1.7.12: no finding for the two workflows (none for the old `tui-ci.yml` either).
- `scripts/tests/test_tui_baseline.py`: 56 passed, 3 skipped on Windows; 59 passed on Linux
  (WSL). `scripts/tests`: 230 passed, 11 skipped on Windows. Ruff check and format check pass.
- 13 changes of the two workflow files (the agent path back in `tui-ci.yml`, a renamed path, a
  file that does not watch itself, a flag removed, another target, another TERM, `PI_HOME` set,
  `PI_E2E_REQUIRED=0`, another nextest version, another pip line, a cache that saves, another
  key, a changed event header): each one fails the test that is meant to catch it.
- Not checked here: a run on GitHub. The CI of this PR runs both workflows (it changes both
  files). The first PR that changes only `pi_agent_cli/**` shows that the TUI CI stays away.
rust-cache puts the version of every Rust toolchain on the runner into its
cache key. The image has its own `stable`, and an image update changes it
(1.99.0 to 1.99.1 on 2026-10-25). The key changed, no cache matched, and the
`test` job built everything again (35 minutes, 31 of them for the build).

Add the step "Keep only the pinned Rust toolchain" after the toolchain install
in every job of tui-ci.yml and in tui-e2e.yml. It makes the toolchain of
tui/rust-toolchain.toml the default and uninstalls the others, so the key does
not depend on the image. `add-rust-environment-hash-key: false` is not a fix:
it also removes the hash of Cargo.lock, so the key never changes and the cache
is never saved again.

Tests (scripts/tests/test_tui_baseline.py) keep the step equal in every job
and before the cache step, and keep the variables that the key hashes equal in
both workflows (the end-to-end job restores the key of the `test` job).
Docs: CHANGELOG.md, docs/baselines/tui.md.
@zy1233 zy1233 changed the title ci(tui): run only python_agent_e2e when only the Python agent changes ci(tui): run only python_agent_e2e for agent-only changes; keep the rust-cache key across image updates Oct 28, 2026
@zy1233
zy1233 merged commit 9cdd8fc into main Oct 28, 2026
8 checks passed
@zy1233
zy1233 deleted the ci/tui-e2e-only-for-agent-changes branch October 28, 2026 08:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant