Skip to content

v0.3.62 release#276

Merged
bradhe merged 10 commits into
mainfrom
develop
May 14, 2026
Merged

v0.3.62 release#276
bradhe merged 10 commits into
mainfrom
develop

Conversation

@bradhe
Copy link
Copy Markdown
Contributor

@bradhe bradhe commented May 13, 2026

  • Remove debug logging from Otel SDK
  • Extend setuptools<82 retry logic to pyproject.toml
  • Add starting state to our upstream tools
  • Bump version to v0.3.62
  • Add pagination and make it easier to use on the CLI

Summary by CodeRabbit

  • Release

    • Version bumped to 0.3.62
  • New Features

    • Update app-environment endpoint
    • Organization usage time‑series reports
    • New "starting" run status; run-parameter override field
  • Improvements

    • Runs now include starting timestamps/counts; cancellations report cancelled-child counts
    • CLI: environment selection for app commands and full paginated listings
  • Removed

    • Authenticator management, password‑reset, and email verification endpoints/models

sammuti and others added 3 commits May 5, 2026 15:34
The legacy-setuptools retry only fired for requirements.txt projects, leaving pyproject.toml apps with no recovery path when their builds needed pkg_resources. The retry now drops out of uv sync into uv pip install with the project source (. for pyproject, -r requirements.txt otherwise) plus a setuptools<82 arg, so both kinds of project get the same fallback.

Removes the should_use_legacy_setuptools_pin predicate and the spawn_requirements_install helper — the call site in tower-runtime is now just 'if res != 0 { retry }', and the file-type branch lives inline where the args are built.
* chore: Upgrade to the latest API spec (from staging, for now)

* chore: RunParameter becomes RunParameterInput

* chore: Build failure prevented python client generation

* chore: Refactor how we track run status and add `starting`.

Fix some broken test infra for the new starting properties, too.

* chore: Fix formatting

* chore: Add `starting` to the rust side, too

* chore: Regenerate API clients against prod schema

Refresh Python and Rust clients from api.tower.dev now that the
starting state has shipped. Adapt tower-cmd call sites: revert
RunParameterInput -> RunParameter (prod kept the original name) and
pass environment: None for the new optional DescribeAppParams field.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(tests): add new starting fields to mock API server

The OpenAPI spec bump to v0.10.24 made `Run.starting_at` and
`RunResults.starting` required (the former is `Option::deserialize`,
so the JSON key must be present even if null). The mock server
wasn't emitting them, so the new generated client deserialization
fell into `UnknownValue` and surfaced as 204/"wasn't able to
understand" errors across the integration suite.

* chore: Add cancelled_child_runs to mock cancel response

Regenerated CancelRunResponse now has a required cancelled_child_runs
field; mock server was returning only {"run": ...} which broke
deserialization and produced empty CLI output in integration tests.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 13, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Bumps workspace/project versions and regenerates OpenAPI clients (v0.10.24); removes authenticator/password-reset/verify-email endpoints; adds update-app-environment and organization-usage time-series APIs/models; extends run models (starting timestamps/counters/status); adds client-side pagination aggregation; threads environment/all_environments through deploy/describe; refactors UV/setup retry; updates CLI, mock server, tests, and fixtures.

Changes

API regeneration, clients, runtime, and CLI (single cohesive cohort)

Layer / File(s) Summary
Version, workspace deps, and build config
Cargo.toml, pyproject.toml, .github/workflows/build-binaries.yml
Workspace/package version bumped to 0.3.62; workspace reqwest TLS feature switched to rustls-tls-webpki-roots-no-provider; rustls workspace dependency features adjusted; manylinux container and cargo env tweak in CI.
OpenAPI regen: spec + exports
crates/tower-api/README.md, crates/tower-api/src/apis/*.rs, crates/tower-api/src/models/*, crates/tower-api/src/models/mod.rs
Updated OpenAPI doc comments to v0.10.24; regenerated models and API surface: removed several authenticator/password-reset/verify-email models/endpoints and added new models/endpoints (update_app_environment, generate_organization_usage_time_series, usage metric point).
Rust & Python generated client changes
crates/tower-api/src/apis/default_api.rs, src/tower/tower_api_client/api/default/*, src/tower/tower_api_client/models/*, src/tower/tower_api_client/models/__init__.py
Added endpoints: update_app_environment (PUT /apps/{name}/environments/{environment}) and generate_organization_usage_time_series (/usage/time-series); removed old authenticator/password-reset/verify-email client modules and models; updated signatures and (de)serializers in both Rust and Python generated clients.
Run model surface updates
crates/tower-api/src/models/run*.rs, crates/tower-api/src/models/run_parameter*.rs, crates/tower-api/src/models/run_results*.rs, src/tower/tower_api_client/models/run*.py, src/tower/tower_api_client/models/run_parameter.py, src/tower/tower_api_client/models/run_results.py
Added starting_at to Run/RunAttempt, added Status::Starting / STARTING enum values, added tri-state is_override on RunParameter, added starting counters to results/timeseries, and added cancelled_child_runs to cancel responses; constructors and (de)serializers updated accordingly.
Client pagination aggregation & API wrappers
crates/tower-cmd/src/api.rs, crates/tower-cmd/src/*
Introduced PaginatedResponse trait and fetch_all_pages helper; listing helpers rewired to aggregate pages and return Vec (apps, catalogs, secrets, teams, environments, schedules); updated unwrap error handling to attempt deserialization and truncate long responses.
CLI wiring: environment flags & list behavior
crates/tower-cmd/src/apps.rs, crates/tower-cmd/src/*, skills/tower/SKILL.md
Threaded optional --environment/-e and all_environments through deploy/describe; apps list/apps show accept -e and pass env into API; CLI handlers updated to use aggregated Vec results; skill doc updated.
Mock API server and integration tests
tests/mock-api-server/main.py, tests/mock-api-server/pyproject.toml, tests/integration/features/*, tests/integration/features/steps/cli_steps.py, tests/*
Mock server added pagination support, seed/reset test endpoints, updated run payloads (starting/starting_at/cancelled_child_runs); integration Behave features/steps added for pagination; fixtures/tests updated to new run fields.
UV/setup retry & runtime local execution refactor
crates/tower-uv/src/lib.rs, crates/tower-runtime/src/local.rs, crates/tower-runtime/src/lib.rs, crates/tower-runtime/tests/*
Consolidated pip install into pip_install helper and rewrote legacy setuptools-pin retry to use it (removed should_use_legacy_setuptools_pin); local runtime: added AppFailure enum, Status::Cancelled, changed Failed payload to AppFailure, introduced AppCompletion channel, wrapped execution in catch_unwind, made cancellation explicit (Error::Cancelled), added run_setup_child helper and adjusted retry and mapping semantics; tests adapted.
Python client run-status utilities & small UX fixes
src/tower/_client.py, crates/tower-cmd/src/*, crates/tower-package/*, other small files
Added RunStatus import and module-level status sets for predicates; many minor refactors/formatting, logging filter tweak, small API consumer wiring updates (rustls provider installation in CLI/uv install paths), and non-functional reflows across crates.
sequenceDiagram
    participant CLI
    participant RustAPI as "crates/tower-cmd/api.rs"
    participant GenClient as "generated client (rust/python)"
    participant Server as "API / mock server"

    CLI->>RustAPI: list_apps(--environment?)
    RustAPI->>GenClient: fetch_all_pages(page=1..N)
    GenClient->>Server: GET /v1/apps?page=1&page_size=...
    Server-->>GenClient: { apps: [...], pages: N }
    GenClient-->>RustAPI: aggregated Vec<AppSummary>
    RustAPI-->>CLI: render table / json
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested reviewers

  • socksy

"🐰 I bumped the version, regenerated the spec,
pruned old auth, added env-update and metric pick,
runs gain a 'starting' beat and counters too,
CLI pages all results — tests seed a hundred plus two! 🥕"

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch develop

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@crates/tower-cmd/src/apps.rs`:
- Around line 625-630: The test test_terminal_statuses_explicit defines a local
non_terminal array that omits Status::Starting, so add Status::Starting to that
non_terminal list ( alongside Status::Scheduled, Status::Pending,
Status::Running, Status::Retrying ) to ensure is_run_finished is verified to
treat Starting as non-terminal; update the non_terminal declaration in that test
to include Status::Starting.

In `@crates/tower-runtime/src/local.rs`:
- Around line 267-279: The retry logic currently retries on any non-zero exit
(res) including cancellation (-1); update the conditional around the retry (the
block that sends the Output and calls uv.sync_with_legacy_setuptools_pin and
run_setup_child) to skip retry when res indicates cancellation (check for res ==
-1 or the appropriate cancelled sentinel) so that you only retry when res != 0
&& res != -1; keep the existing output/send behavior for real failures but do
not spawn retry_child or call run_setup_child when cancel_token/cancelled exit
is detected.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 34cd20a9-b6ab-40ee-ba74-cace531eb8ba

📥 Commits

Reviewing files that changed from the base of the PR and between 18c175e and bad0d74.

⛔ Files ignored due to path filters (2)
  • Cargo.lock is excluded by !**/*.lock
  • uv.lock is excluded by !**/*.lock
📒 Files selected for processing (279)
  • Cargo.toml
  • crates/tower-api/README.md
  • crates/tower-api/src/apis/configuration.rs
  • crates/tower-api/src/apis/default_api.rs
  • crates/tower-api/src/apis/feature_flags_api.rs
  • crates/tower-api/src/models/account.rs
  • crates/tower-api/src/models/acknowledge_alert_response.rs
  • crates/tower-api/src/models/acknowledge_all_alerts_response.rs
  • crates/tower-api/src/models/alert.rs
  • crates/tower-api/src/models/api_key.rs
  • crates/tower-api/src/models/app.rs
  • crates/tower-api/src/models/app_statistics.rs
  • crates/tower-api/src/models/app_summary.rs
  • crates/tower-api/src/models/app_version.rs
  • crates/tower-api/src/models/authentication_context.rs
  • crates/tower-api/src/models/batch_schedule_params.rs
  • crates/tower-api/src/models/batch_schedule_response.rs
  • crates/tower-api/src/models/cancel_run_response.rs
  • crates/tower-api/src/models/catalog.rs
  • crates/tower-api/src/models/catalog_property.rs
  • crates/tower-api/src/models/claim_device_login_ticket_params.rs
  • crates/tower-api/src/models/claim_device_login_ticket_response.rs
  • crates/tower-api/src/models/create_account_params.rs
  • crates/tower-api/src/models/create_account_params_flags_struct.rs
  • crates/tower-api/src/models/create_account_response.rs
  • crates/tower-api/src/models/create_api_key_params.rs
  • crates/tower-api/src/models/create_api_key_response.rs
  • crates/tower-api/src/models/create_app_params.rs
  • crates/tower-api/src/models/create_app_response.rs
  • crates/tower-api/src/models/create_catalog_params.rs
  • crates/tower-api/src/models/create_catalog_response.rs
  • crates/tower-api/src/models/create_device_login_ticket_response.rs
  • crates/tower-api/src/models/create_environment_params.rs
  • crates/tower-api/src/models/create_environment_response.rs
  • crates/tower-api/src/models/create_guest_params.rs
  • crates/tower-api/src/models/create_guest_response.rs
  • crates/tower-api/src/models/create_sandbox_secrets_params.rs
  • crates/tower-api/src/models/create_sandbox_secrets_response.rs
  • crates/tower-api/src/models/create_schedule_params.rs
  • crates/tower-api/src/models/create_schedule_response.rs
  • crates/tower-api/src/models/create_secret_params.rs
  • crates/tower-api/src/models/create_secret_response.rs
  • crates/tower-api/src/models/create_session_params.rs
  • crates/tower-api/src/models/create_session_response.rs
  • crates/tower-api/src/models/create_team_params.rs
  • crates/tower-api/src/models/create_team_response.rs
  • crates/tower-api/src/models/create_webhook_params.rs
  • crates/tower-api/src/models/create_webhook_response.rs
  • crates/tower-api/src/models/delete_api_key_params.rs
  • crates/tower-api/src/models/delete_api_key_response.rs
  • crates/tower-api/src/models/delete_app_response.rs
  • crates/tower-api/src/models/delete_catalog_response.rs
  • crates/tower-api/src/models/delete_guest_output_body.rs
  • crates/tower-api/src/models/delete_schedule_params.rs
  • crates/tower-api/src/models/delete_schedule_response.rs
  • crates/tower-api/src/models/delete_secret_response.rs
  • crates/tower-api/src/models/delete_session_params.rs
  • crates/tower-api/src/models/delete_session_response.rs
  • crates/tower-api/src/models/delete_team_invitation_params.rs
  • crates/tower-api/src/models/delete_team_invitation_response.rs
  • crates/tower-api/src/models/delete_team_params.rs
  • crates/tower-api/src/models/delete_team_response.rs
  • crates/tower-api/src/models/delete_webhook_response.rs
  • crates/tower-api/src/models/deploy_app_request.rs
  • crates/tower-api/src/models/deploy_app_response.rs
  • crates/tower-api/src/models/describe_account_body.rs
  • crates/tower-api/src/models/describe_app_response.rs
  • crates/tower-api/src/models/describe_app_version_response.rs
  • crates/tower-api/src/models/describe_authentication_context_body.rs
  • crates/tower-api/src/models/describe_catalog_response.rs
  • crates/tower-api/src/models/describe_device_login_session_response.rs
  • crates/tower-api/src/models/describe_email_preferences_body.rs
  • crates/tower-api/src/models/describe_plan_response.rs
  • crates/tower-api/src/models/describe_run_graph_response.rs
  • crates/tower-api/src/models/describe_run_links.rs
  • crates/tower-api/src/models/describe_run_logs_response.rs
  • crates/tower-api/src/models/describe_run_response.rs
  • crates/tower-api/src/models/describe_secrets_key_response.rs
  • crates/tower-api/src/models/describe_session_response.rs
  • crates/tower-api/src/models/describe_team_response.rs
  • crates/tower-api/src/models/describe_webhook_response.rs
  • crates/tower-api/src/models/email_subscriptions.rs
  • crates/tower-api/src/models/encrypted_catalog_property.rs
  • crates/tower-api/src/models/environment.rs
  • crates/tower-api/src/models/error_detail.rs
  • crates/tower-api/src/models/error_model.rs
  • crates/tower-api/src/models/event_alert.rs
  • crates/tower-api/src/models/event_error.rs
  • crates/tower-api/src/models/event_log.rs
  • crates/tower-api/src/models/event_shouldertap.rs
  • crates/tower-api/src/models/event_warning.rs
  • crates/tower-api/src/models/export_catalogs_params.rs
  • crates/tower-api/src/models/export_catalogs_response.rs
  • crates/tower-api/src/models/export_secrets_params.rs
  • crates/tower-api/src/models/export_secrets_response.rs
  • crates/tower-api/src/models/exported_catalog.rs
  • crates/tower-api/src/models/exported_catalog_property.rs
  • crates/tower-api/src/models/exported_secret.rs
  • crates/tower-api/src/models/feature.rs
  • crates/tower-api/src/models/featurebase_identity.rs
  • crates/tower-api/src/models/generate_app_statistics_response.rs
  • crates/tower-api/src/models/generate_organization_usage_time_series_response.rs
  • crates/tower-api/src/models/generate_run_statistics_response.rs
  • crates/tower-api/src/models/generate_runner_credentials_response.rs
  • crates/tower-api/src/models/get_feature_flag_response_body.rs
  • crates/tower-api/src/models/guest.rs
  • crates/tower-api/src/models/invite_team_member_params.rs
  • crates/tower-api/src/models/invite_team_member_response.rs
  • crates/tower-api/src/models/leave_team_response.rs
  • crates/tower-api/src/models/list_alerts_response.rs
  • crates/tower-api/src/models/list_api_keys_response.rs
  • crates/tower-api/src/models/list_app_environments_response.rs
  • crates/tower-api/src/models/list_app_versions_response.rs
  • crates/tower-api/src/models/list_apps_response.rs
  • crates/tower-api/src/models/list_catalogs_response.rs
  • crates/tower-api/src/models/list_environments_response.rs
  • crates/tower-api/src/models/list_guests_response.rs
  • crates/tower-api/src/models/list_my_team_invitations_response.rs
  • crates/tower-api/src/models/list_runners_response.rs
  • crates/tower-api/src/models/list_runs_response.rs
  • crates/tower-api/src/models/list_schedules_response.rs
  • crates/tower-api/src/models/list_secret_environments_response.rs
  • crates/tower-api/src/models/list_secrets_response.rs
  • crates/tower-api/src/models/list_team_invitations_response.rs
  • crates/tower-api/src/models/list_team_members_response.rs
  • crates/tower-api/src/models/list_teams_response.rs
  • crates/tower-api/src/models/list_webhooks_response.rs
  • crates/tower-api/src/models/mod.rs
  • crates/tower-api/src/models/organization.rs
  • crates/tower-api/src/models/organization_usage.rs
  • crates/tower-api/src/models/pagination.rs
  • crates/tower-api/src/models/parameter.rs
  • crates/tower-api/src/models/plan.rs
  • crates/tower-api/src/models/refresh_session_params.rs
  • crates/tower-api/src/models/refresh_session_response.rs
  • crates/tower-api/src/models/regenerate_guest_login_url_params.rs
  • crates/tower-api/src/models/regenerate_guest_login_url_response.rs
  • crates/tower-api/src/models/remove_team_member_params.rs
  • crates/tower-api/src/models/remove_team_member_response.rs
  • crates/tower-api/src/models/resend_team_invitation_params.rs
  • crates/tower-api/src/models/resend_team_invitation_response.rs
  • crates/tower-api/src/models/run.rs
  • crates/tower-api/src/models/run_app_initiator_data.rs
  • crates/tower-api/src/models/run_app_params.rs
  • crates/tower-api/src/models/run_app_response.rs
  • crates/tower-api/src/models/run_attempt.rs
  • crates/tower-api/src/models/run_failure_alert.rs
  • crates/tower-api/src/models/run_graph_node.rs
  • crates/tower-api/src/models/run_graph_run_id.rs
  • crates/tower-api/src/models/run_initiator.rs
  • crates/tower-api/src/models/run_initiator_details.rs
  • crates/tower-api/src/models/run_log_line.rs
  • crates/tower-api/src/models/run_parameter.rs
  • crates/tower-api/src/models/run_parameter_input.rs
  • crates/tower-api/src/models/run_results.rs
  • crates/tower-api/src/models/run_retry_policy.rs
  • crates/tower-api/src/models/run_run_initiator_details.rs
  • crates/tower-api/src/models/run_statistics.rs
  • crates/tower-api/src/models/run_timeseries_point.rs
  • crates/tower-api/src/models/runner.rs
  • crates/tower-api/src/models/runner_credentials.rs
  • crates/tower-api/src/models/schedule.rs
  • crates/tower-api/src/models/schedule_run_initiator_details.rs
  • crates/tower-api/src/models/search_runs_response.rs
  • crates/tower-api/src/models/secret.rs
  • crates/tower-api/src/models/server_sent_events_inner.rs
  • crates/tower-api/src/models/server_sent_events_inner_1.rs
  • crates/tower-api/src/models/server_sent_events_inner_2.rs
  • crates/tower-api/src/models/session.rs
  • crates/tower-api/src/models/shoulder_tap.rs
  • crates/tower-api/src/models/sse_warning.rs
  • crates/tower-api/src/models/statistics_settings.rs
  • crates/tower-api/src/models/team.rs
  • crates/tower-api/src/models/team_invitation.rs
  • crates/tower-api/src/models/team_membership.rs
  • crates/tower-api/src/models/test_webhook_response.rs
  • crates/tower-api/src/models/token.rs
  • crates/tower-api/src/models/update_account_params.rs
  • crates/tower-api/src/models/update_account_response.rs
  • crates/tower-api/src/models/update_app_environment_params.rs
  • crates/tower-api/src/models/update_app_environment_response.rs
  • crates/tower-api/src/models/update_app_params.rs
  • crates/tower-api/src/models/update_app_response.rs
  • crates/tower-api/src/models/update_catalog_params.rs
  • crates/tower-api/src/models/update_catalog_response.rs
  • crates/tower-api/src/models/update_email_preferences_body.rs
  • crates/tower-api/src/models/update_environment_params.rs
  • crates/tower-api/src/models/update_environment_response.rs
  • crates/tower-api/src/models/update_my_team_invitation_params.rs
  • crates/tower-api/src/models/update_my_team_invitation_response.rs
  • crates/tower-api/src/models/update_organization_params.rs
  • crates/tower-api/src/models/update_organization_response.rs
  • crates/tower-api/src/models/update_plan_params.rs
  • crates/tower-api/src/models/update_plan_response.rs
  • crates/tower-api/src/models/update_schedule_params.rs
  • crates/tower-api/src/models/update_schedule_response.rs
  • crates/tower-api/src/models/update_secret_params.rs
  • crates/tower-api/src/models/update_secret_response.rs
  • crates/tower-api/src/models/update_team_member_params.rs
  • crates/tower-api/src/models/update_team_member_response.rs
  • crates/tower-api/src/models/update_team_params.rs
  • crates/tower-api/src/models/update_team_response.rs
  • crates/tower-api/src/models/update_user_params.rs
  • crates/tower-api/src/models/update_user_response.rs
  • crates/tower-api/src/models/update_webhook_params.rs
  • crates/tower-api/src/models/update_webhook_response.rs
  • crates/tower-api/src/models/usage_limit.rs
  • crates/tower-api/src/models/usage_metric_time_series_point.rs
  • crates/tower-api/src/models/user.rs
  • crates/tower-api/src/models/webhook.rs
  • crates/tower-cmd/src/api.rs
  • crates/tower-cmd/src/apps.rs
  • crates/tower-cmd/src/deploy.rs
  • crates/tower-cmd/src/mcp.rs
  • crates/tower-cmd/src/run.rs
  • crates/tower-cmd/src/schedules.rs
  • crates/tower-cmd/src/secrets.rs
  • crates/tower-cmd/src/session.rs
  • crates/tower-cmd/src/skill.rs
  • crates/tower-cmd/src/teams.rs
  • crates/tower-cmd/src/util/apps.rs
  • crates/tower-cmd/src/util/deploy.rs
  • crates/tower-package/src/core.rs
  • crates/tower-package/src/native.rs
  • crates/tower-package/src/towerfile.rs
  • crates/tower-package/src/wasm.rs
  • crates/tower-package/tests/package_test.rs
  • crates/tower-runtime/src/lib.rs
  • crates/tower-runtime/src/local.rs
  • crates/tower-telemetry/src/logging.rs
  • crates/tower-uv/src/lib.rs
  • crates/tower-uv/tests/sync_test.rs
  • pyproject.toml
  • src/tower/_client.py
  • src/tower/tower_api_client/api/default/create_password_reset.py
  • src/tower/tower_api_client/api/default/delete_authenticator.py
  • src/tower/tower_api_client/api/default/deploy_app.py
  • src/tower/tower_api_client/api/default/describe_app.py
  • src/tower/tower_api_client/api/default/describe_organization_usage.py
  • src/tower/tower_api_client/api/default/generate_authenticator.py
  • src/tower/tower_api_client/api/default/generate_organization_usage_time_series.py
  • src/tower/tower_api_client/api/default/resend_email_verification.py
  • src/tower/tower_api_client/api/default/update_app_environment.py
  • src/tower/tower_api_client/api/default/update_password_reset.py
  • src/tower/tower_api_client/api/default/verify_email.py
  • src/tower/tower_api_client/models/__init__.py
  • src/tower/tower_api_client/models/cancel_run_response.py
  • src/tower/tower_api_client/models/create_authenticator_params.py
  • src/tower/tower_api_client/models/create_authenticator_response.py
  • src/tower/tower_api_client/models/create_catalog_params_type.py
  • src/tower/tower_api_client/models/create_password_reset_params.py
  • src/tower/tower_api_client/models/create_password_reset_response.py
  • src/tower/tower_api_client/models/create_schedule_params.py
  • src/tower/tower_api_client/models/delete_authenticator_response.py
  • src/tower/tower_api_client/models/generate_authenticator_response.py
  • src/tower/tower_api_client/models/generate_organization_usage_time_series_response.py
  • src/tower/tower_api_client/models/generate_run_statistics_status_item.py
  • src/tower/tower_api_client/models/list_authenticators_response.py
  • src/tower/tower_api_client/models/list_runs_status_item.py
  • src/tower/tower_api_client/models/run.py
  • src/tower/tower_api_client/models/run_attempt.py
  • src/tower/tower_api_client/models/run_parameter.py
  • src/tower/tower_api_client/models/run_results.py
  • src/tower/tower_api_client/models/run_status.py
  • src/tower/tower_api_client/models/run_timeseries_point.py
  • src/tower/tower_api_client/models/schedule.py
  • src/tower/tower_api_client/models/search_runs_status_item.py
  • src/tower/tower_api_client/models/unverified_authenticator.py
  • src/tower/tower_api_client/models/update_app_environment_params.py
  • src/tower/tower_api_client/models/update_app_environment_response.py
  • src/tower/tower_api_client/models/update_password_reset_params.py
  • src/tower/tower_api_client/models/update_schedule_params.py
  • src/tower/tower_api_client/models/usage_metric_time_series_point.py
  • src/tower/tower_api_client/models/usage_metric_time_series_point_name.py
  • src/tower/tower_api_client/models/verified_authenticator.py
  • src/tower/tower_api_client/models/verify_email_params.py
  • src/tower/tower_api_client/models/verify_email_response.py
  • tests/mock-api-server/main.py
  • tests/tower/test_client.py
💤 Files with no reviewable changes (18)
  • src/tower/tower_api_client/models/update_password_reset_params.py
  • src/tower/tower_api_client/api/default/generate_authenticator.py
  • src/tower/tower_api_client/models/generate_authenticator_response.py
  • src/tower/tower_api_client/models/create_password_reset_response.py
  • src/tower/tower_api_client/models/verify_email_response.py
  • src/tower/tower_api_client/models/unverified_authenticator.py
  • src/tower/tower_api_client/api/default/create_password_reset.py
  • src/tower/tower_api_client/models/verified_authenticator.py
  • src/tower/tower_api_client/models/create_authenticator_response.py
  • src/tower/tower_api_client/api/default/update_password_reset.py
  • src/tower/tower_api_client/models/verify_email_params.py
  • src/tower/tower_api_client/models/delete_authenticator_response.py
  • src/tower/tower_api_client/models/create_authenticator_params.py
  • src/tower/tower_api_client/api/default/delete_authenticator.py
  • src/tower/tower_api_client/models/list_authenticators_response.py
  • src/tower/tower_api_client/api/default/verify_email.py
  • src/tower/tower_api_client/models/create_password_reset_params.py
  • src/tower/tower_api_client/api/default/resend_email_verification.py

Comment on lines +625 to +630
let non_terminal = [
Status::Scheduled,
Status::Pending,
Status::Running,
Status::Retrying,
];
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Add Status::Starting to non-terminal coverage in test_terminal_statuses_explicit.

Line 625’s non_terminal set omits Status::Starting, so a regression where is_run_finished incorrectly treats Starting as terminal would not be caught here.

Proposed fix
         let non_terminal = [
             Status::Scheduled,
             Status::Pending,
+            Status::Starting,
             Status::Running,
             Status::Retrying,
         ];
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
let non_terminal = [
Status::Scheduled,
Status::Pending,
Status::Running,
Status::Retrying,
];
let non_terminal = [
Status::Scheduled,
Status::Pending,
Status::Starting,
Status::Running,
Status::Retrying,
];
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/tower-cmd/src/apps.rs` around lines 625 - 630, The test
test_terminal_statuses_explicit defines a local non_terminal array that omits
Status::Starting, so add Status::Starting to that non_terminal list ( alongside
Status::Scheduled, Status::Pending, Status::Running, Status::Retrying ) to
ensure is_run_finished is verified to treat Starting as non-terminal; update the
non_terminal declaration in that test to include Status::Starting.

Comment thread crates/tower-runtime/src/local.rs
* fix: fetch all pages in list commands via reusable pagination helper

All list commands (apps, teams, secrets, catalogs, environments,
schedules) only fetched the first page from the API, silently
truncating results. Added a PaginatedResponse trait and generic
fetch_all_pages helper that iterates through all pages. Both CLI
and MCP tools benefit since they share the same api.rs layer.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* test: add pagination integration tests and mock server support

- Mock server now respects page/page_size query params on all list endpoints
- Added paginate() helper and mock_max_page_size override for testing
- Added /test/seed-apps and /test/reset endpoints for test data setup
- Added cli_pagination.feature with scenarios verifying multi-page fetching

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: pagination test accounts for pre-existing test app

The mock server re-adds predeployed-test-app on reset, so JSON mode
returns 26 (25 seeded + 1 pre-existing). Changed assertion to "at least 25".
Also pinned mock server to Python <3.14 (pyo3 compat).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Fix python 3.14 is too new for pyo3 bindings

* style: fix black formatting in test files

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: avoid race condition in pagination test by using natural page overflow

Instead of capping page_size server-side (shared mutable state across
parallel workers), seed 105 apps so pagination naturally exceeds the
CLI's page_size=100, producing 2 pages without any global state changes.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: improve error diagnostics for pagination deserialization failures

Include truncated response content in error messages to help debug
CI-only deserialization failures. Remove unused debug step from tests.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: include serde deserialization error in diagnostic message

This will reveal the exact field/type mismatch causing CI pagination
test failures.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: run pagination tests serially to avoid shared mock state races

Tag cli_pagination.feature with @serial and split the test runner into
two phases: parallel features first (excluding @serial), then serial
features without --jobs.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: strip run_results from mock list response to match real API

The real Tower API does not include run_results in list app responses.
Including it in the mock caused deserialization failures on CI where a
cached wheel expected a newer RunResults schema with a 'starting' field.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* style: fix black formatting in mock server

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@tests/integration/run_tests.py`:
- Around line 199-210: serial_args currently only removes separate "--jobs N" /
"-j N" tokens, allowing inline forms like "--jobs=2" or "-j2" through; update
the serial_args construction in tests/integration/run_tests.py to also filter
out tokens that start with "--jobs=" and tokens that match the "-j<digits>"
pattern (e.g. "-j2" or "-j10"), or equivalently normalize args first and drop
any arg that matches r"^--jobs(=.*)?$" or r"^-j\d+$"; ensure you remove the
initial unused list comprehension and keep the single loop or a single
comprehension that excludes ("--jobs","-j"), their inline forms, and
numeric-only tokens so that `@serial` runs never see any jobs flags.

In `@tests/mock-api-server/main.py`:
- Around line 801-809: The run_results dict created by the /test/reset handler
omits the starting field, causing inconsistency with the initial app definition;
locate the run_results dictionary in the reset endpoint (the block that
re-creates the predeployed-test-app) and add the "starting": 0 key/value
alongside cancelled, crashed, errored, exited, pending, retrying, and running so
the reset payload matches the original definition.
- Around line 760-768: Seeded app dictionaries are missing the new "starting"
key inside their "run_results" dict; update the seeded app entries that define
"run_results" (the same dict structure used for the pre-populated app and newly
created apps) to include "starting": 0 so the shape matches the pre-populated
app and new app creation. Locate the seeded apps in
tests/mock-api-server/main.py where "run_results" is constructed for seed data
and add the "starting" field with a zero value to each run_results object to
ensure consistency with the app created in the other code paths.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 65a8ef6c-144d-46cc-9024-0e4f50490583

📥 Commits

Reviewing files that changed from the base of the PR and between bad0d74 and d44c67c.

⛔ Files ignored due to path filters (1)
  • tests/mock-api-server/uv.lock is excluded by !**/*.lock
📒 Files selected for processing (14)
  • crates/tower-cmd/src/api.rs
  • crates/tower-cmd/src/apps.rs
  • crates/tower-cmd/src/catalogs.rs
  • crates/tower-cmd/src/environments.rs
  • crates/tower-cmd/src/mcp.rs
  • crates/tower-cmd/src/schedules.rs
  • crates/tower-cmd/src/secrets.rs
  • crates/tower-cmd/src/teams.rs
  • tests/integration/features/cli_pagination.feature
  • tests/integration/features/steps/cli_steps.py
  • tests/integration/run_tests.py
  • tests/mock-api-server/main.py
  • tests/mock-api-server/pyproject.toml
  • tests/mock-api-server/tower_mock_api.egg-info/PKG-INFO
✅ Files skipped from review due to trivial changes (4)
  • tests/mock-api-server/tower_mock_api.egg-info/PKG-INFO
  • tests/mock-api-server/pyproject.toml
  • crates/tower-cmd/src/catalogs.rs
  • crates/tower-cmd/src/teams.rs
🚧 Files skipped from review as they are similar to previous changes (1)
  • crates/tower-cmd/src/apps.rs

Comment on lines +199 to +210
serial_args = [a for a in args if a not in ("--jobs", "-j") and not a.isdigit()]
# Remove --jobs N pair from args
serial_args = []
skip_next = False
for a in args:
if skip_next:
skip_next = False
continue
if a in ("--jobs", "-j"):
skip_next = True
continue
serial_args.append(a)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Handle inline jobs flags when building serial_args.

serial_args removes --jobs N / -j N, but inline forms (for example --jobs=2 or -j2) still pass through. That can keep parallelization enabled during the @serial phase.

Suggested patch
-        serial_args = [a for a in args if a not in ("--jobs", "-j") and not a.isdigit()]
-        # Remove --jobs N pair from args
+        # Remove all parallelism flags from args for `@serial` run.
         serial_args = []
         skip_next = False
         for a in args:
             if skip_next:
                 skip_next = False
                 continue
             if a in ("--jobs", "-j"):
                 skip_next = True
                 continue
+            if a.startswith("--jobs="):
+                continue
+            if a.startswith("-j") and len(a) > 2 and a[2:].isdigit():
+                continue
             serial_args.append(a)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/integration/run_tests.py` around lines 199 - 210, serial_args currently
only removes separate "--jobs N" / "-j N" tokens, allowing inline forms like
"--jobs=2" or "-j2" through; update the serial_args construction in
tests/integration/run_tests.py to also filter out tokens that start with
"--jobs=" and tokens that match the "-j<digits>" pattern (e.g. "-j2" or "-j10"),
or equivalently normalize args first and drop any arg that matches
r"^--jobs(=.*)?$" or r"^-j\d+$"; ensure you remove the initial unused list
comprehension and keep the single loop or a single comprehension that excludes
("--jobs","-j"), their inline forms, and numeric-only tokens so that `@serial`
runs never see any jobs flags.

Comment thread tests/mock-api-server/main.py Outdated
Comment thread tests/mock-api-server/main.py Outdated
codingcyclist and others added 4 commits May 14, 2026 09:14
* feat: add environment support to apps list/show CLI and MCP tools

The Tower API scopes apps to environments but the CLI and MCP tools
were not fully exposing this. This commit:

- Adds --environment/-e flag to `tower apps list` and `tower apps show`
  (defaulting to "default")
- Adds environment parameter to MCP tools: tower_deploy, tower_apps_list,
  tower_apps_show, and tower_run_remote
- Adds new MCP tools: tower_catalogs_list and tower_catalogs_show
- Includes CLI arg parsing tests for the new flags

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: default apps list to no environment filter

The API returns all apps when environment is omitted, and only filters
when explicitly provided. Updated list_apps to take Option<&str> so
the default behavior shows all apps across environments. The environment
param is still available for explicit filtering. Also added version
field to MCP apps list output.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: codingcyclist <41942954+codingcyclist@users.noreply.github.com>
…28 (#277)

* ci: bump aarch64 wheel base from manylinux2014 to manylinux_2_28

The aarch64 linux-cross job built wheels in the CentOS 7 / GCC 4.8
toolchain that ships with manylinux2014. Switching to manylinux_2_28
(AlmaLinux 8, GCC 8+) gives the build a newer assembler, which avoids
a class of miscompilations in crates that ship hand-written aarch64
asm.

* fix: switch rustls CryptoProvider from ring to aws-lc-rs

reqwest's rustls-tls feature implicitly enables rustls/ring, which
pulls ring v0.17 into the TLS path. ring's aarch64 build produces
TLS signature-verify failures on some glibc builds of our wheels.

Swap every reqwest TLS feature to the *-no-provider variant and
install aws-lc-rs as the process-wide default CryptoProvider at CLI
startup. The install is also called at the tower-uv reqwest entry
point so its standalone unit test (which bypasses App::new) has a
provider installed before the first TLS handshake.

After this change, `cargo tree -i ring` is empty.

* fix: lock in tower-api template + drop dead aarch64 CFLAGS

scripts/rust-client-templates/Cargo.mustache hardcodes `rustls-tls` in
the generated reqwest dep. Patch the template so the next
generate-rust-api-client.sh run preserves the aws-lc-rs feature
choice instead of reverting tower-api's Cargo.toml.

The CFLAGS_aarch64_unknown_linux_gnu workaround in build-binaries.yml
existed only to coax ring's build script into detecting ARMv8. With
ring gone from the dep tree, the workaround is dead.
…umbing (#274)

`LocalApp::start` previously spawned `execute_local_app` with a
`oneshot::Sender<i32>`; every `?` early-return in that task dropped the
sender silently, and `LocalApp::status()` reported the resulting closed
channel as `Error::WaiterClosed` — losing the actual cause (env join
errors, `tower_uv::Error` variants, panics, etc.).

Replace the channel payload with an internal `AppCompletion` enum and a
new `Status::Failed(AppFailure)` shape that preserves the structured
error. Highlights:

  - `AppCompletion` (internal) has `Exit(i32)`, `Cancelled`,
    `Failed(AppFailure)` variants. The spawn wrapper in `LocalApp::start`
    runs `inner_execute_local_app` inside `AssertUnwindSafe(...).catch_unwind()`
    and always sends an `AppCompletion` before exiting, so a closed waiter
    channel can no longer mask a real failure.

  - `Status::Failed(AppFailure)` replaces the old
    `Status::Failed { error_code, error_message }` strings.
    `AppFailure` has:
      * `Runtime(Error)` — structured runtime error
      * `Panic(String)` — `catch_unwind` payload
      * `Platform { error_code, error_message }` — opaque strings for
        callers that construct `Status::Failed` from outside
        `tower_runtime` (e.g. Kubernetes pod-status reconciliation).
    Stringification (`error_code` / `error_message`) is the consumer's
    responsibility, not the runtime's.

  - `Status::Cancelled` is a new terminal variant for explicit
    cancellation via the `CancellationToken`. Previously cancellation
    was represented as `Crashed { code: -1 }`, indistinguishable from
    an app that legitimately exits with code 255 / -1 or an IO error
    in `wait_for_process`. `inner_execute_local_app` now returns
    `Err(Error::Cancelled)` on every cancellation path (4 prologue
    checks + 3 post-`wait_for_process` checks that disambiguate
    cancellation from a child's genuine non-zero exit), and the spawn
    wrapper special-cases that into `AppCompletion::Cancelled`.

  - `Error` gains `Clone + PartialEq + Eq` (all variants are unit, so
    this is free) so `Status` can keep deriving `Clone + PartialEq`,
    which is required by status caching in `LocalApp::status()` and by
    existing integration tests.

  - `tower-cmd` (the only other in-crate consumer of `Status::Failed`)
    is updated for the new variant shape and prints a dedicated
    "cancelled" message for `Status::Cancelled`.

  - The integration test `test_abort_on_dependency_installation_failure`
    keeps asserting that `uv sync` non-zero exits surface as
    `Status::Crashed { code }` — `Status::Failed` is reserved for
    platform failures where the child never ran.

`Error::WaiterClosed` now only fires when the spawn was aborted or the
runtime was dropped — both real bugs worth surfacing as-is.
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
crates/tower-runtime/tests/local_test.rs (1)

363-380: ⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Fix the stale Status::Failed pattern in this match.

Line 379 uses Status::Failed { .. }, but Status defines Failed(AppFailure) as a tuple variant, causing a compile error.

Proposed fix
-        Status::Failed { .. } => {
+        Status::Failed(_) => {
             panic!("App should have crashed, not failed with a platform error");
         }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/tower-runtime/tests/local_test.rs` around lines 363 - 380, The match
arm uses the outdated struct-style pattern Status::Failed { .. } which doesn't
match the enum's tuple variant; update the match to use the tuple pattern (e.g.,
Status::Failed(_) or Status::Failed(_err)) in the match inside the test (the
match over Status in tests/local_test.rs) so it compiles against the enum
definition (Status::Failed(AppFailure)).
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@crates/tower-cmd/src/run.rs`:
- Around line 254-270: Update monitor_cli_status() to match the new Status enum
shape and treat cancellations as terminal: replace the old pattern
Status::Failed { .. } with the tuple/variant pattern Status::Failed(_) in the
match arm that handles failures (so it compiles against the new
tower_runtime::Status), and add a branch that treats Status::Cancelled as a
terminal state (exiting the polling loop and returning/propagating the
cancellation path so the existing cancellation handling in the other function
runs). Ensure you reference the same match in monitor_cli_status() and update
any control flow there to break/return when encountering Status::Cancelled.

In `@crates/tower-runtime/src/local.rs`:
- Around line 135-146: The code is treating any error from
wait_for_process()/child.wait() as the sentinel Ok(-1) via run_setup_child(),
which hides real I/O runtime failures and turns them into Status::Crashed {
code: -1 } instead of Status::Failed(AppFailure::Runtime(_)); change
run_setup_child() and inner_execute_local_app() so that CancellationToken
cancellation still maps to Ok(-1) but genuine child.wait()/wait_for_process()
I/O errors are propagated as Err(Error::Runtime(...)) (or the crate's runtime
failure variant) rather than converted to an exit-code integer—specifically,
detect token.is_cancelled() to return the -1 path but return Err for any Err
returned from wait_for_process()/child.wait(), and update callers that currently
expect Ok(i32) from run_setup_child() to handle Err(Error) accordingly so the
failure surfaces as AppFailure::Runtime.

---

Outside diff comments:
In `@crates/tower-runtime/tests/local_test.rs`:
- Around line 363-380: The match arm uses the outdated struct-style pattern
Status::Failed { .. } which doesn't match the enum's tuple variant; update the
match to use the tuple pattern (e.g., Status::Failed(_) or Status::Failed(_err))
in the match inside the test (the match over Status in tests/local_test.rs) so
it compiles against the enum definition (Status::Failed(AppFailure)).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: f99ac759-8a6d-4c98-a976-548e5457dd01

📥 Commits

Reviewing files that changed from the base of the PR and between df1a41b and fb2ced6.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (6)
  • crates/tower-cmd/src/run.rs
  • crates/tower-runtime/Cargo.toml
  • crates/tower-runtime/src/errors.rs
  • crates/tower-runtime/src/lib.rs
  • crates/tower-runtime/src/local.rs
  • crates/tower-runtime/tests/local_test.rs
✅ Files skipped from review due to trivial changes (2)
  • crates/tower-runtime/Cargo.toml
  • crates/tower-runtime/src/errors.rs

Comment thread crates/tower-cmd/src/run.rs
Comment thread crates/tower-runtime/src/local.rs
* fix: align pagination with API semantics and make it configurable

The CLI pagination loop added in #275 was 0-indexed but the API treats
page=0 (or null) as 'return everything in one response' and uses
1-indexed pages otherwise. The loop happened to work against real prod
only because the first request asked for 'all' and immediately exited
the loop. The mock server was 0-indexed and so diverged from the real
API's semantics.

Changes:

- Add four hidden global flags for paginated list commands:
  --page-size, --page, --no-paginate, --max-items. Modeled on the AWS
  CLI's pagination knobs but matched to our page-number API
  (--starting-token would be cursor-shaped, which our API does not use).
- Thread page_size / paginate / page / max_items through Config.
- Fix fetch_all_pages: page=0 means 'all in one response and stop';
  page>=1 iterates 1..=num_pages.
- Align the mock /v1/apps/etc. pagination to the same semantics so the
  mock and prod behave the same way.
- Rewrite cli_pagination.feature to create apps via the real CLI and
  delete them in after_scenario. With --page 1 --page-size 2 against
  three apps, the test forces a real two-page traversal against any
  backend.
- Drop the mock-only /test/seed-apps and /test/reset endpoints that the
  old pagination feature relied on.

* chore: Reformat the things

* chore: Clean up old, broken rust that comes with Homebrew

* chore: Go the other way with it--clobber the Homebrew rustup

* chore: One more attempt to fix this broken rust toolchain in macos15 images

* chore: Another attempt at resolving the issue with the upstream

---------

Co-authored-by: Brad Heller <brad@tower.dev>
@bradhe bradhe merged commit 8963908 into main May 14, 2026
33 checks passed
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.

4 participants