@andwhy/e2e-pipeline 0.12.0 → 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.0",
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",
@@ -39,6 +39,16 @@ negotiable:
39
39
  touch `.github/`, `.claude/`, `package.json`, lockfiles, or any
40
40
  `*.config.*` file.
41
41
  4. **Never run git commit, push, or any GitHub CLI/API command.**
42
+ 5. **Never delete, clean, or recreate the `.checkly-heal/` directory** — the
43
+ workflow reads your outcome from it after you finish, and destroying it
44
+ turns your entire run into an unexplained failure. No `rm -rf`, no
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.
42
52
 
43
53
  ## Procedure
44
54
 
@@ -47,19 +57,29 @@ negotiable:
47
57
  Read the context and result files. Identify the failing spec file, test, and
48
58
  step. Read the spec. Resolve the production `BASE_URL` (override 1).
49
59
 
60
+ If the check name from the context matches **nothing** in
61
+ `checkly.config.{ts,mjs}` — a webhook test, a renamed check, a synthetic
62
+ dispatch — there is nothing to reproduce: write the outcome immediately with
63
+ classification `not-reproducible` and a summary saying the check name is
64
+ unknown to this repo, then stop. Do not skip the outcome file because the
65
+ run "wasn't real".
66
+
50
67
  ### 2. Reproduce
51
68
 
52
- 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:
53
73
 
54
74
  ```bash
55
- 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
56
76
  ```
57
77
 
58
78
  - **Fails** → continue to triage.
59
- - **Passes** → run it again with `--repeat-each=3`. All green means the
60
- failure was transient on Checkly's side: classification `not-reproducible`,
61
- stop, change nothing. Intermittent failures mean the test is **flaky** —
62
- 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.
63
83
 
64
84
  ### 3. Triage — before any edit
65
85
 
@@ -73,9 +93,10 @@ the live page:
73
93
  behavior hides an outage.
74
94
  - **The test is flaky.** The outcome works, but the test fails
75
95
  intermittently — races, missing waits for dynamic content, animation
76
- timing, order-dependent assertions. Classification `flaky` — proceed to
77
- step 4 and fix the root cause per the robustness rules; blanket timeout
78
- 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.
79
100
  - **The test is stale.** Production changed legitimately (new markup, changed
80
101
  copy, moved element) and the test's selectors or assertions no longer
81
102
  describe it, while the actual user outcome still works. Classification
@@ -102,10 +123,11 @@ that make the test robust.
102
123
 
103
124
  ### 5. Verify
104
125
 
105
- - **Test-only fixes:** re-run the spec against production until green — for a
106
- flaky fix, prove stability with `--repeat-each=5`, not a single pass. Then
107
- run the full e2e suite once the same way to catch collateral damage, and
108
- 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:
109
131
 
110
132
  ```bash
111
133
  pnpm checkly pw-test --env BASE_URL="<production-url>" -- <failing-spec>
@@ -163,28 +163,57 @@ jobs:
163
163
  CHECKLY_ACCOUNT_ID: ${{ secrets.CHECKLY_ACCOUNT_ID }}
164
164
  run: |
165
165
  npm install -g @anthropic-ai/claude-code@2
166
+ # Transcript lives OUTSIDE .checkly-heal so it survives whatever the
167
+ # agent does to its working directory, and outside the repo tree so
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).
166
171
  claude -p "$(cat .github/prompts/checkly-heal.md)" \
167
172
  --model claude-opus-5 \
168
173
  --allowedTools "Bash,Read,Edit,Write,Glob,Grep" \
169
174
  --permission-mode dontAsk \
170
175
  --max-turns 150 \
171
- --output-format json \
172
- > .checkly-heal/agent-output.json || true
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.
180
+ {
181
+ echo "### Agent step diagnostics"
182
+ echo '```'
183
+ ls -la .checkly-heal 2>&1 || echo ".checkly-heal missing"
184
+ tail -c 2000 /tmp/checkly-heal-agent-output.json 2>/dev/null || echo "no agent output"
185
+ echo '```'
186
+ } | tee -a "$GITHUB_STEP_SUMMARY"
173
187
 
174
188
  # The guard is deterministic on purpose: the PR path requires an outcome
175
189
  # file claiming a fix AND a diff clear of protected paths. App-source
176
190
  # changes (test hooks) are allowed but derived from the diff, not the
177
191
  # agent's claim, and flagged downstream. Everything else — protected
178
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.
179
195
  - name: Read the outcome and guard the diff
180
- if: steps.guard.outputs.mode == 'heal'
196
+ if: always() && steps.guard.outputs.mode == 'heal'
181
197
  id: outcome
198
+ env:
199
+ AGENT_CONCLUSION: ${{ steps.agent.conclusion }}
182
200
  run: |
183
201
  classification="agent-failed"
184
202
  summary="The heal agent did not produce an outcome."
185
- 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
186
204
  classification=$(jq -r '.classification // "agent-failed"' .checkly-heal/outcome.json)
187
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"
188
217
  fi
189
218
 
190
219
  files=$(git status --porcelain | awk '{print $NF}' | grep -v "^\.checkly-heal/" || true)
@@ -319,6 +348,11 @@ jobs:
319
348
  uses: actions/upload-artifact@v4
320
349
  with:
321
350
  name: checkly-heal-${{ github.run_id }}
322
- path: .checkly-heal/
351
+ path: |
352
+ .checkly-heal/
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
323
357
  if-no-files-found: ignore
324
358
  retention-days: 14