@andwhy/e2e-pipeline 0.12.1 → 0.12.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@andwhy/e2e-pipeline",
3
- "version": "0.12.1",
3
+ "version": "0.12.2",
4
4
  "description": "Scaffolds the full e2e testing pipeline: agent-assisted Playwright tests, CI runs against Vercel previews, and Checkly monitors that follow production deploys",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -43,6 +43,12 @@ negotiable:
43
43
  workflow reads your outcome from it after you finish, and destroying it
44
44
  turns your entire run into an unexplained failure. No `rm -rf`, no
45
45
  `git clean`. Only add files to it.
46
+ 6. **Runtime is metered — stop the moment the classification is certain.**
47
+ You run under a hard 35-minute step timeout, and every minute is paid CI
48
+ and API time. Run the narrowest thing that answers the current question,
49
+ never the full suite, and never re-run a test whose answer you already
50
+ have. If the deadline nears, write an honest partial outcome instead of
51
+ pushing on — a run killed by the timeout reports nothing.
46
52
 
47
53
  ## Procedure
48
54
 
@@ -60,17 +66,20 @@ run "wasn't real".
60
66
 
61
67
  ### 2. Reproduce
62
68
 
63
- Run only the failing spec against production:
69
+ Identify the narrowest reproduction: the failing spec and test named by
70
+ `result.json` when present; without a result file, the spec files belonging
71
+ to the failing check. Run that once against production, stopping at the
72
+ first failure:
64
73
 
65
74
  ```bash
66
- BASE_URL=<production-url> pnpm playwright test <failing-spec> --project=Chrome
75
+ BASE_URL=<production-url> pnpm playwright test <failing-spec> --project=Chrome --max-failures=1
67
76
  ```
68
77
 
69
78
  - **Fails** → continue to triage.
70
- - **Passes** → run it again with `--repeat-each=3`. All green means the
71
- failure was transient on Checkly's side: classification `not-reproducible`,
72
- stop, change nothing. Intermittent failures mean the test is **flaky** —
73
- continue to triage with that hypothesis.
79
+ - **Passes** → classification `not-reproducible`. Write the outcome and stop
80
+ immediately — no repeat runs, no wider investigation. Checkly saw it fail
81
+ and it passes now, so the blip was transient; a genuinely unstable test
82
+ will alert again, and that repetition is the signal a human acts on.
74
83
 
75
84
  ### 3. Triage — before any edit
76
85
 
@@ -84,9 +93,10 @@ the live page:
84
93
  behavior hides an outage.
85
94
  - **The test is flaky.** The outcome works, but the test fails
86
95
  intermittently — races, missing waits for dynamic content, animation
87
- timing, order-dependent assertions. Classification `flaky` — proceed to
88
- step 4 and fix the root cause per the robustness rules; blanket timeout
89
- increases are not a fix.
96
+ timing, order-dependent assertions. When the trace alone doesn't settle
97
+ it, repeating the one failing test is the only permitted repeat run.
98
+ Classification `flaky` — proceed to step 4 and fix the root cause per the
99
+ robustness rules; blanket timeout increases are not a fix.
90
100
  - **The test is stale.** Production changed legitimately (new markup, changed
91
101
  copy, moved element) and the test's selectors or assertions no longer
92
102
  describe it, while the actual user outcome still works. Classification
@@ -113,10 +123,11 @@ that make the test robust.
113
123
 
114
124
  ### 5. Verify
115
125
 
116
- - **Test-only fixes:** re-run the spec against production until green — for a
117
- flaky fix, prove stability with `--repeat-each=5`, not a single pass. Then
118
- run the full e2e suite once the same way to catch collateral damage, and
119
- validate on Checkly's infrastructure:
126
+ - **Test-only fixes:** re-run the fixed spec against production until green —
127
+ for a flaky fix, prove stability with `--repeat-each=5` on that one spec,
128
+ not a single pass. Do not run the full suite (override 6): the heal PR gets
129
+ its own `pr-playwright-tests` preview run, and that is where collateral
130
+ damage surfaces. Validate the fixed spec on Checkly's infrastructure:
120
131
 
121
132
  ```bash
122
133
  pnpm checkly pw-test --env BASE_URL="<production-url>" -- <failing-spec>
@@ -165,36 +165,55 @@ jobs:
165
165
  npm install -g @anthropic-ai/claude-code@2
166
166
  # Transcript lives OUTSIDE .checkly-heal so it survives whatever the
167
167
  # agent does to its working directory, and outside the repo tree so
168
- # the diff guard never counts it.
168
+ # the diff guard never counts it. stream-json writes each event as it
169
+ # happens — a run killed by the timeout still leaves the full history
170
+ # (plain json buffers everything and a kill leaves an empty file).
169
171
  claude -p "$(cat .github/prompts/checkly-heal.md)" \
170
172
  --model claude-opus-5 \
171
173
  --allowedTools "Bash,Read,Edit,Write,Glob,Grep" \
172
174
  --permission-mode dontAsk \
173
175
  --max-turns 150 \
174
- --output-format json \
175
- > /tmp/checkly-heal-agent-output.json || echo "claude exited non-zero: $?" >> "$GITHUB_STEP_SUMMARY"
176
+ --output-format stream-json --verbose \
177
+ > /tmp/checkly-heal-agent-output.json || echo "claude exited non-zero: $?" | tee -a "$GITHUB_STEP_SUMMARY"
178
+ # Diagnostics go to the job log AND the summary: the log is readable
179
+ # over the API, the summary only in the browser.
176
180
  {
177
181
  echo "### Agent step diagnostics"
178
182
  echo '```'
179
183
  ls -la .checkly-heal 2>&1 || echo ".checkly-heal missing"
180
184
  tail -c 2000 /tmp/checkly-heal-agent-output.json 2>/dev/null || echo "no agent output"
181
185
  echo '```'
182
- } >> "$GITHUB_STEP_SUMMARY"
186
+ } | tee -a "$GITHUB_STEP_SUMMARY"
183
187
 
184
188
  # The guard is deterministic on purpose: the PR path requires an outcome
185
189
  # file claiming a fix AND a diff clear of protected paths. App-source
186
190
  # changes (test hooks) are allowed but derived from the diff, not the
187
191
  # agent's claim, and flagged downstream. Everything else — protected
188
192
  # paths, missing outcome, other classifications — escalates.
193
+ # always(): a killed agent step (timeout, cancel) must still produce a
194
+ # classification, or Slack reports an unexplained failure.
189
195
  - name: Read the outcome and guard the diff
190
- if: steps.guard.outputs.mode == 'heal'
196
+ if: always() && steps.guard.outputs.mode == 'heal'
191
197
  id: outcome
198
+ env:
199
+ AGENT_CONCLUSION: ${{ steps.agent.conclusion }}
192
200
  run: |
193
201
  classification="agent-failed"
194
202
  summary="The heal agent did not produce an outcome."
195
- if [ -f .checkly-heal/outcome.json ] && jq -e . .checkly-heal/outcome.json > /dev/null 2>&1; then
203
+ if [ "$AGENT_CONCLUSION" = "success" ] && [ -f .checkly-heal/outcome.json ] && jq -e . .checkly-heal/outcome.json > /dev/null 2>&1; then
196
204
  classification=$(jq -r '.classification // "agent-failed"' .checkly-heal/outcome.json)
197
205
  summary=$(jq -r '.summary // ""' .checkly-heal/outcome.json)
206
+ else
207
+ # No trustworthy outcome — say WHY, so the Slack message diagnoses
208
+ # itself. An outcome written before a kill is unverified: escalate
209
+ # rather than open a PR for it.
210
+ if [ "$AGENT_CONCLUSION" = "skipped" ]; then
211
+ summary="The heal agent never ran — a setup step failed before it."
212
+ elif [ "$AGENT_CONCLUSION" != "success" ]; then
213
+ summary="The heal agent was killed before finishing (step conclusion: ${AGENT_CONCLUSION:-unknown} — usually the 35-minute timeout)."
214
+ fi
215
+ agent_error=$(jq -r 'select(.is_error == true) | .result // empty' /tmp/checkly-heal-agent-output.json 2>/dev/null | head -c 300)
216
+ [ -n "$agent_error" ] && summary="$summary Agent error: $agent_error"
198
217
  fi
199
218
 
200
219
  files=$(git status --porcelain | awk '{print $NF}' | grep -v "^\.checkly-heal/" || true)
@@ -332,5 +351,8 @@ jobs:
332
351
  path: |
333
352
  .checkly-heal/
334
353
  /tmp/checkly-heal-agent-output.json
354
+ # .checkly-heal is a dot-directory; without this it is silently
355
+ # excluded and the debug artifact loses context/outcome/result.
356
+ include-hidden-files: true
335
357
  if-no-files-found: ignore
336
358
  retention-days: 14