# e2e-analyze Post-test failure-clustering action for the Go E2E suites. ## What it does Runs after `go-junit-report` has converted each suite's stdout into `junit-.xml`. For every JUnit file matched by `junit-glob`: 1. Parses each `` with a `` / `` child. 2. Strips ANSI escapes, then normalises dynamic noise (UUIDs, EC2 IDs, IPs, timestamps, request-ids, source line numbers) to produce a stable _signature_ per failure. 3. Picks the **earliest non-cascade** failure per suite as the likely root cause. "Cascade" = failure message matches a fixture-dependency marker (`must populate fix.`, `Should NOT be empty`, `Expected value not to be nil`). When every failure is a cascade (whole suite tripped on one upstream gap), the earliest cascade is the fallback root. 4. Buckets the remaining failures by signature and renders a markdown report. The report is appended to `$GITHUB_STEP_SUMMARY` and also written to `/analysis.md` so it travels with the uploaded artifact bundle. The action **never fails the job** — exit-code semantics stay with the existing "Fail job if any suite failed" gate. ## Inputs | Input | Required | Description | |--------------|----------|-------------| | `junit-glob` | yes | Shell glob for the JUnit XML files (e.g. `${ARTIFACT_DIR}/junit-*.xml`). | | `log-dir` | yes | Directory the rendered report is written into (`analysis.md`). | | `title` | no | Heading at the top of the summary section. Default `E2E failure analysis`. | ## Outputs None. The summary is appended to `$GITHUB_STEP_SUMMARY`; the same content lands at `/analysis.md`. ## Local invocation Outside Actions, the analyser prints to stdout instead of appending to a summary file: ```sh cd spinifex/.github/actions/e2e-analyze go run . -junit-glob "/tmp/artifacts/junit-*.xml" -log-dir /tmp/artifacts ``` ## Tests Golden-file tests live in `testdata/`. Each subdir holds a scenario: | Scenario | Source | |--------------------|-------------------------------------------| | `cascade-vpcs` | Synthetic: one Phase 5 root + 15 cascades | | `single-failure` | Real artifact from run `26037892507` | | `no-failures` | Synthetic: clean run | Run normally: ```sh go test ./.github/actions/e2e-analyze/ ``` When the report format changes intentionally, regenerate the snapshots: ```sh go test ./.github/actions/e2e-analyze/ -update ``` ## Per-failure bundles After parsing, the action materialises a bundle directory per failure under `/analysis/NN-/`. The report links to it from each entry so a reader on the Actions page can click straight through to the relevant log slice. Each bundle contains: | File | Source | |-------------------|-----------------------------------------------------------------------------------| | `failure.txt` | Synthesised summary: test, start, duration, file-site, error line. | | `journal.log` | `spinifex-journal.log` sliced to `[StartAt − 5s, StartAt + Duration + 5s]`. | | `test.log` | `test-.log` block between `=== RUN ` and the matching `--- FAIL`. | | `` | Anything the harness already dropped into `//` at run-time. | The journal slice relies on the workflow running `journalctl --output=short-iso` so timestamps are parseable. Lines without a parseable timestamp inherit the previous line's in-window/out-of-window classification (so multi-line records and stack traces stay contiguous). ## Roadmap - **Stage 1 (shipped):** first-failure surfacing + cascade clustering. - **Stage 2 (this update):** time-window correlation — per-failure bundle with sliced journal + test-log + any per-test artifacts. - **Stage 3:** rule-based root-cause classifier driven by `spinifex/tests/e2e/.failure-rules.yaml`. See `docs/development/improvements/e2e-go-failure-analysis.md` in the parent mulga repo.