Skip to content

feat(journeys): prescale ci and the Action replay journeys, with --stale - #11

Merged
pyjeebz merged 14 commits into
mainfrom
feat/journeys-in-ci
Nov 3, 2026
Merged

pyjeebz merged 14 commits into
mainfrom
feat/journeys-in-ci

Conversation

@pyjeebz

@pyjeebz pyjeebz commented Nov 2, 2026

Copy link
Copy Markdown
Owner

Stacked on #10. The base is #10's branch, so the diff shows only this slice. Paired with pyjeebz/prescale-web#2: the dashboard compares runs only with a baseline of the same mode.

When the checkout has prescale/journeys/, prescale ci (and so the GitHub Action) replays the journeys at load instead of the URL routes: each alone, then together. It uploads the run and keeps one PR comment with each journey's verdict, the step that breaks first, interference, and any journey that wasn't load-tested. A deploy event with no checkout stays a URL run.

Each journey that doesn't replay with one user gets an outcome:

  • stale: the app answered, but not as the journey expects (e.g. a 404 because a route was renamed). It's always listed on the PR. With the default --stale warn it's an ::warning::; with --stale fail / stale: fail it fails the check.
  • error (a 5xx with one user) or unreachable (no answer): it always fails the check, the same as URL mode with a down app.

New Action inputs: stale and allow-writes. They're passed to the CLI only when set, so a pinned older prescale-version keeps working. Journey secrets ({{env.NAME}}) come from the step's env:.

Fail-safe until the dashboard ships #2: a journey run only trusts the dashboard's comparison if it says mode: "journeys". Otherwise the PR comment shows the journey verdicts with no "regressed" headline, and --fail-on-regression doesn't fire. A journey run can't be judged against a URL baseline.

How it was verified

  • Real prescale ci --app "python examples/fragile-shop.py 8424", against a local stand-in for the dashboard that answers with a "regressed" comparison without mode, and with one journey file broken:
    • exit 0, even with --fail-on-regression
    • the comment reads "⚪ Capacity: survives ~20 users", explains why no change is shown, and says "Breaks first: checkout › POST /api/carts/{{cart_id}}/order"
    • a Journeys table, and "Not load-tested: browse (stale): step 2 (list products): returned 401"
    • the upload has config.mode: journeys
  • Secret repro: an app that echoes the request in a 422 (like FastAPI validation errors), with SHOP_PASSWORD=TopSecret123. The secret appears 0 times in the PR comment and the log. Locally, journeys verify shows the body with "password":"***".
  • Down app: prescale ci http://localhost:9 → exit 1 with ::error::… failed with a single user.
  • Action shell under bash -eo pipefail with a stub prescale:
    • warn/empty → no --stale flag
    • Fail → --stale fail
    • a.com, b.com → two --allow-writes
    • $(touch PWNED) * stays literal text
  • ruff check cli/src: clean · pytest cli/tests -q: 329 passed (15 new).

Risk

Tier: risky · Public surface:

  • action.yml inputs stale and allow-writes
  • ci flags --stale, --allow-writes, --dir
  • the PR comment format
  • the journey status error (in the schema)

Rollback: revert. The Action reaches users only when v1 moves (a release); if a release is wrong, move v1 back. Release order: ship pyjeebz/prescale-web#2 before moving v1, so URL runs never compare with a journey baseline either.

Fresh-context review blocked three real problems, all reproduced, fixed and pinned by tests:

  1. Secrets on the PR. Up to 160 characters of response body went into the comment and the uploaded Result, and validation errors often echo the request, including passwords. Bodies no longer reach CI output or uploads, and they're masked locally.
  2. A down app passed CI. "No answer" and a 5xx were treated as "stale". They now always fail.
  3. The Action always passed the new --stale flag, which would break users pinned to an older CLI.

Also from the review:

  • PR-comment text is neutralised: no @ notifications, no HTML, no breaking out of code spans
  • refusals and ramp failures get ::error:: annotations
  • --path is noted as ignored for journeys
  • deploy workflows with journeys that write are documented


Generated by Claude Code

@pyjeebz
pyjeebz changed the base branch from feat/journeys-everywhere to main November 3, 2026 07:27
@pyjeebz
pyjeebz merged commit 297000b into main Nov 3, 2026
3 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.

2 participants