@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.
|
|
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
|
-
|
|
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** →
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
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.
|
|
77
|
-
|
|
78
|
-
|
|
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 —
|
|
106
|
-
flaky fix, prove stability with `--repeat-each=5
|
|
107
|
-
|
|
108
|
-
|
|
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
|
-
>
|
|
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:
|
|
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
|