Repository navigation
feat(report): write a sealed .evidence pack for every run - #754
siddhant573 wants to merge 27 commits into
Conversation
7e5a921 to
2a529f0
Compare
There was a problem hiding this comment.
All reported issues were addressed across 85 files
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
There was a problem hiding this comment.
All reported issues were addressed across 23 files (changes from recent commits).
Tip: cubic used a learning from your PR history. Let your coding agent read cubic learnings directly with the cubic MCP.
Re-trigger cubic
42d9148 to
bf64420
Compare
There was a problem hiding this comment.
All reported issues were addressed across 6 files (changes from recent commits).
Requires human review: Auto-approval blocked because this review re-detected 1 unresolved issue already reported by Cubic.
Tip: Review your code locally with the cubic CLI to iterate faster.
Re-trigger cubic
|
Hi @okwasniewski, At TestMu AI we've built a framework-agnostic format for test-run evidence: the open Packs from this branch, uploaded as Actions artifacts (the viewer asks for a GitHub sign-in to read an artifact): Locally we also ran the agent suite live on an OpenAI model (24 of 24 passed) and the mobile benchmark on an iOS simulator. Details are under Verified in the description. This PR builds on #753 (the Thanks! |
|
Thanks for making the cost visible in the PR — that makes the tradeoff much easier to reason about. One design question before making evidence default-on: should the invariant be that diagnostic/evidence settings do not materially perturb execution timing unless explicitly requested? Here the pack itself is cheap, but turning it on also changes the screenshot fallback to Could the default pack consume whatever evidence the run already produced, while |
There was a problem hiding this comment.
All reported issues were addressed across 17 files (changes from recent commits).
Tip: Review your code locally with the cubic CLI to iterate faster.
Re-trigger cubic
|
Thanks @kvnloo, that's a good invariant and I've adopted it in d0dcfec. Evidence is still on by default, but it no longer touches the screenshot default. The new numbers come from the same machine and the same specs (
That leaves about 0.37 s for writing, sealing and validating the pack at the end of the run, with no per-step cost. I've updated the description to match. |
d9f4f91 to
1ee4b27
Compare
…o frames of an engine without screenshots
…exclude the frame
…result id, and guard outDir like output
…p fields into the pack
…rsor and element box into the pack
…ame records for placing boxes on the image
…pack on an early stop, and surface pack write failures
…th; mobile retries write no pack
…, never its source
The pack holds the frames a run already takes; a frame on every step is screenshot: 'every-step', asked for explicitly. An observability setting should not change how a run executes or how long it takes.
1ee4b27 to
76a55bd
Compare
|
Thanks for the PR and the thorough work here! We're headed in a different direction: run output stays in the e2e-native report and failure pages, and we won't take a default-on dependency on an external format or viewer. If you want to keep this going, it can ship as a standalone package through the |
|
Thanks @okwasniewski for taking the time to look at this, and for the clear direction. Keeping run output e2e-native makes sense, and the We took your suggestion:
Thanks again, and thanks @kvnloo for the review. |
Builds on #753 (the
screenshotmode); review that first. Until it merges this PR shows its commits too.What and why
.evidencepack,<output>/evidence/<runId>.evidence, in the open, framework-agnostic format of@testmuai/evidence-cli(Apache-2.0, on npm). The same format other test tools produce, so an e2e run opens in the same viewers and tooling, as one file.evidence: false,E2E_EVIDENCE=0, or--no-evidenceturn it off. Evidence takes no screenshot of its own and never changes how a run executes: the pack holds the frames the run already takes (on-failureby default).screenshot: 'every-step'(or--screenshot every-step, from feat(run): screenshot after every step with screenshot: 'every-step' #753) gives every step its frame.packages/e2e/src/report/evidence/), so it runs after the run and can never change its status or exit code; a pack that fails to write or validate is a line in the summary.seal.tsis the only importer), so this PR does not re-implement its rules.How a run maps:
run.yamltests/t-<id16>/result.yamlflaky, tags, final attempt's steps (kind= api, label, duration, cache mode)steps/<n>-<attempt>-<index>/step.jsonandscreenshot.pngfailure.yamlagent.assertinstruction vs its explanation), locator, URL, failure frame, screen textlogs/steps.ndjson,agent.ndjson(turns), the engine's trace when its redaction allowsargument, what it was given beside its target (typed text, a key, an assertion's expected value). A secret is<secret:name>. Text typed into a secure field is<withheld>: the locator engine reads the resolved node once before typing and records the text only when that read says the field is not secure, so it fails closed. Schema, both fixtures, anddebugging.mdx/security.mdxchange together.target, where it acted in CSS pixels: theboxof the node a locator action resolved to (read from the node the locator already fetched, so no extra engine call), thepointof a positioned tap ortapAt, and for an agent step its last action's node.step.jsoncarries the fields the evidence viewer reads (summary,kind,status,duration_ms,urlonapp.openand failing steps,coordinatesandelement_rectfor the cursor and outline) with the report's step record undere2e. Titles readFill getByLabel("Username") with "admin",Expect getByRole("alert") to have text "Welcome back",Act: agree to the terms (check checkbox "Agree to terms" …).e2e/engine):EngineArtifacts.screenshotmay return{ path, viewport }in place of a path. The session normalizes it, so every existing caller still gets a path; the step frame records the viewport and the pixel scale (read from the PNG header) onstep.viewport, which the pack writes asenvironment.resolution.@e2e-dev/webreports the page's CSS viewport,@e2e-dev/mobilethe screen in points, so a step's box lands on a device screenshot taken at a higher density.spec), web benchmark and web agent (benchmark), and the four mobile jobs (mobile) upload their pack as its own artifact (evidence-*, 7 days, SHA-pinnedupload-artifact) and write a viewer link to the job summary. The hosted viewer opens an Actions artifact URL directly (after a GitHub sign-in), so every CI run on this PR has a clickable pack. Both steps arecontinue-on-error, like the existing report uploads.test.json: the test's id, title path, file, line, and tags from the already-redacted report. It is never the test's source, which can hold a secret value as a plain string (the case fix(e2e): redact plain-string secret values in titles, labels, and agent input #727 now guards;plain-string-secrets.test.tspasses with the pack on).failed(the app was wrong) frombroken(the check could not decide). OnlyASSERTION_FAILEDisfailed.completeornot-requiredare copied, so no video, binary download,command.log, or console output goes in;agent.extractvalues stay out.contentsUnder(the helper every secret-scanning integration test uses) now opens.evidencepacks and the zips inside them, soengine-secrets,agent-policy, and the newevidence.test.tsall assert the secret is in no pack entry.artifacts/keeps the latest run's files);evidence.outDiris refused inside the cache directory or a directory a run clears.e2e explorewrites none.e2e initand the repo.gitignoreignore.e2e/evidence/.@testmuai/evidence-cliis imported lazily inseal.ts, so commands other thanrunnever load it.Verified
Ran it locally: yes
What was tested
pnpm teston the branch rebased onmain--no-cache, OpenAIgpt-6-lunaagent.assertthat must failexpected= instruction,actual= model's explanationOpen the evidence (GitHub Actions artifacts from a run on GitHub-hosted runners; the viewer asks for a GitHub sign-in to read an artifact, kept 30 days):
Testbed suite, 71 passed, tests named by title
Web benchmark agent suite from committed recordings, 25 passed, 7 broken for lack of a model key
testbed,
pnpm --filter @e2e-dev/testbed run testwith defaults: 71 passed, 1 skipped; the summary printsEvidence .e2e/evidence/<runId>.evidence. The format's own CLI accepts it:CI=1 … --config e2e.agent.config.ts --strict-cache, no model key): the pack validates at L1; replayedagent.actsteps map with their instruction as label and cache mode (self-finalized); steps that needed a model with none available map tobrokenwithMODEL_PROVIDER_FAILED. Liveagent.assertjudgments (failed vs expected/actual) were not exercised: they need a model key.--no-cacheon OpenAIgpt-6-luna(openai()from@ai-sdk/openai), 24 of 24 passed, pack valid at L1. Agent steps readAct: <instruction> (<what it did>), carry the cursor at the last action's element, keep turns inlogs/agent.ndjson, and the run names the model. A planted failingagent.assertproducedexpected= the instruction andactual= the model's explanation ("The todo list shows one list item, not exactly three."). No secret value in any pack entry;extractkeeps no extracted data.tests/forms.e2e.ts tests/todos.e2e.ts,--workers 1, 3 runs each:--no-evidenceThe pack (write, seal, validate) adds about 0.37 s here, and about 2 s for the full testbed's 34 MB pack with traces on. Asking for
every-stepframes adds about 32 ms a step on top (#753). The full parallel testbed suite runs in 27 s either way.evidence-cliseals in memory and synchronously, so a very large pack blocks the event loop while it seals; a streaming seal belongs in that library.expo run:ios --configuration Release:--no-evidence(2 runs)infinite scrollandsticky chromefail with evidence off too (known simulator flakes).toBeFocused follows the focusfailed in both evidence-on full runs and passed in both evidence-off ones, but passed 6 of 6 shorter evidence-on runs with the same tests ahead of it, so it is not reproduced on demand. It checks a field the app focuses one frame after mounting; a step frame's accessibility snapshot right after the tap is a plausible cause. Open, and worth CI's mobile job once approved. The pack validates at L1 withresolution: 402x874on every test and cursor boxes in points.Checklist
pnpm changeset) for any change to a published package, or the change is testbed, docs, or CI only.evidence: falserestores both.