Skip to content

acc: soft-fail acceptance goldens that drift outside our control#5931

Open
janniklasrose wants to merge 10 commits into
mainfrom
janniklasrose/soft-fail-acceptance-tests
Open

acc: soft-fail acceptance goldens that drift outside our control#5931
janniklasrose wants to merge 10 commits into
mainfrom
janniklasrose/soft-fail-acceptance-tests

Conversation

@janniklasrose

Copy link
Copy Markdown
Contributor

Why

Some acceptance goldens drift for reasons outside our control: a live backend rewords a message, a response grows a field we don't assert, or a remotely-hosted template (mlops-stacks) is updated upstream. None are regressions, yet each turns PRs and nightlies red. This adds a way to downgrade such drift to a recorded, non-blocking SOFTFAIL marker while keeping real regressions red.

What

Four commits, smallest blast radius first:

  1. Per-file SoftFailFiles — opt-in shield for named goldens. output.txt can never be shielded (a hard config error), entries must start with out, and Badness is required. The doComparison hook emits SOFTFAIL <file> + unified diff via t.Logf and returns green, before AssertEqualTexts (whose testify side effect would otherwise mark the test failed). Content-diff only: panics, unexpected exit codes, and missing/extra files stay hard. Drift is reported to the GitHub step summary via tools/softfail_report.py (greps the gotestsum JSON), so it's visible even though the build is green.

  2. Whole-test SoftFail — a blunt shield covering every golden including output.txt, for tests whose behavior end-to-end depends on a runtime-fetched artifact (so an upstream change breaks the run itself, not just captured output). Only appropriate when the CLI behavior under test is also covered by a hermetic local test. Also gated on Badness.

  3. mlops-stacks opt-out — applies whole-test SoftFail to bundle/deploy/mlops-stacks (fetches + deploys the mlops-stacks template from github at runtime). bundle init/bundle deploy are covered by local tests.

  4. dashboards/detect-change split — demonstrates the preferred per-file approach: split bundle plan -o json along a volatility seam. Planning decisions (action/reason/old/new) stay strict in out.plan.$engine.json; the backend-mirroring remote view (remote_state + each change's remote) is projected into a soft-failed out.plan.remote.direct.json.

Toolbox ordering

Soft-fail is the last resort. Reach for Repls masks and field projection first (both keep the golden strict); use SoftFailFiles only for undescribable drift, and whole-test SoftFail only when no seam can be isolated. See acceptance/README.md.

Testing

  • Unit: TestValidateSoftFailFiles (Badness required, output.txt/non-out rejected, whole-test variant).
  • TestSoftFailComparison drives doComparison directly (an acceptance selftest can't: -update would heal the mismatch). Covers listed-file-green, unlisted/output.txt-still-red, and whole-test-green.
  • Verified end-to-end on detect-change: simulated remote drift stays green + emits the marker; a drift in the strict planning file still fails.

This pull request and its description were written by Isaac.

janniklasrose and others added 6 commits July 15, 2026 11:40
Some acceptance goldens drift for reasons outside our control: a live
backend rewords a message, a response grows a field we don't assert, or
a remotely-hosted template is updated upstream. None are regressions,
yet each turns PRs and nightlies red.

SoftFailFiles downgrades a content diff in a named golden from a hard
failure to a recorded, non-blocking SOFTFAIL marker:

- Per-file and opt-in, surfaced in out.test.toml so reviewers see the
  shield on every PR. output.txt can never be shielded (a hard config
  error), so a regression in our own logic still turns the test red.
- Requires Badness, documenting why each file is shielded.
- Only a content diff is downgraded; panics, unexpected exit codes, and
  missing/extra files stay hard failures.
- The doComparison branch runs before AssertEqualTexts, whose testify
  side effect would otherwise mark the test failed.

Drift is reported to the GitHub step summary via tools/softfail_report.py
(greps the gotestsum JSON for SOFTFAIL markers) so oncall can refresh the
golden with ./task test-update on a cadence without the build turning red.

Co-authored-by: Isaac
Some tests depend end-to-end on a runtime-fetched artifact outside our
control, so an upstream change breaks not just captured output but the
run itself. For those there is no volatility seam to split into a
per-file SoftFailFiles shield: output.txt is the drift.

Add a whole-test SoftFail flag that downgrades a content diff in ANY of
the test's goldens (including output.txt) to a non-blocking SOFTFAIL
marker. It is a blunt instrument, gated on Badness and intended only for
tests whose CLI behavior is also covered by a hermetic local test, so
shielding the e2e test can't hide a real regression. Structural failures
(panics, unexpected/missing files) stay hard.

Co-authored-by: Isaac
This test initializes and deploys the mlops-stacks template fetched from
github at runtime, which is updated out of band. An upstream change
breaks not just the captured output but the deploy itself, and there is
no volatility seam to isolate: output.txt is the drift. bundle init and
bundle deploy are covered by hermetic local tests, so shielding this
e2e test cannot hide a real CLI regression.

Co-authored-by: Isaac
…mote view

The detect-change test dumps the full `bundle plan -o json` into one golden.
That plan mixes our planning decisions (action/reason/old/new) with the
remote view: remote_state (the full backend read) and each change's `remote`
value. On cloud the remote view mirrors the dashboards API response, whose
output-only fields (create_time, update_time, etag, lifecycle_state, path,
...) are populated by the backend and change out of our control, so the
whole golden drifts red for reasons unrelated to the CLI.

Split along that volatility seam: the planning decisions stay strict in
out.plan.$engine.json, and the remote view is projected into its own
out.plan.remote.$engine.json. Only the direct-engine remote file is listed
in SoftFailFiles (terraform exposes no remote view here; its file is
structurally empty and stays strict, so a regression that populated it would
turn the test red).

Co-authored-by: Isaac
Requiring Badness whenever SoftFailFiles/SoftFail is set overloaded an
unrelated documentary field and coupled two independent concepts. The
shield is already self-documenting: a comment in test.toml explains why
the file drifts, and the flag is surfaced in out.test.toml for reviewers.

Remove the requirement (no SoftFailReason replacement — it isn't needed).
The structural guards stay: output.txt can never be shielded and per-file
entries must start with "out". Restore the mlops-stacks Badness to its
original text (it documents the template's validation warnings, unrelated
to soft-fail).

Co-authored-by: Isaac
…ff report

Two review fixes:

- Ignore SoftFailFiles/SoftFail on local runs. A local testserver run is
  deterministic and cannot drift, so a diff there is a real regression. The
  shield now takes effect only when CLOUD_ENV is set, so a Local+Cloud test
  (e.g. dashboards/detect-change) keeps its local golden strict while still
  shielding cloud API drift.

- Close a SOFTFAIL diff block on go test status lines (--- PASS:, etc.) in
  softfail_report.py. go test -json emits the "--- PASS:" status line as an
  output event before the pass action, so it was landing inside the reported
  diff block. The unified diff's own "--- <path>" headers lack the colon and
  are not affected.

Co-authored-by: Isaac
@janniklasrose janniklasrose deployed to test-trigger-is July 15, 2026 16:00 — with GitHub Actions Active
@janniklasrose janniklasrose marked this pull request as ready for review July 15, 2026 16:14
@github-actions

Copy link
Copy Markdown
Contributor

Approval status: pending

/acceptance/bundle/ - needs approval

9 files changed
Suggested: @pietern
Also eligible: @denik, @shreyas-goenka, @anton-107, @andrewnester, @lennartkats-db

General files (require maintainer)

9 files changed
Based on git history:

  • @pietern -- recent work in .github/workflows/, tools/, acceptance/

Any maintainer (@andrewnester, @anton-107, @denik, @pietern, @shreyas-goenka, @simonfaltum, @renaudhartert-db, @lennartkats-db) can approve all areas.
See OWNERS for ownership rules.

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