Skip to content

ci(web): run the e2e walks as a required CI job - #483

Merged
eyalgolan merged 1 commit into
mainfrom
no-human/914a8bb8
Oct 9, 2026
Merged

eyalgolan merged 1 commit into
mainfrom
no-human/914a8bb8

Conversation

@eyalgolan

@eyalgolan eyalgolan commented Sep 16, 2026 •

Copy link
Copy Markdown
Contributor

Review passed (3 rounds) · Tests passed (1783 passed, 0 failed) · Merge policy ready

Evidence

Check Result
Independent review ✅ PASSED — 3 rounds · proof
Verifiers ✅ 2 of 2 satisfied · proof
Tests ✅ PASS — 1783 passed, 0 failed, 0 errors · proof
CI success
Merge policy ✅ ready — 6 of 6 rules satisfied · proof
Verifiers (2)
  • ✅ tests-assert-something — 1 file
  • ✅ board-uses-theme-tokens — 8 files
Merge-ready policy (6 rules, source: default)
  • ✅ review_passed — review PASSED on head
  • ✅ tests_ran_and_passed — tests: 0 failed of 1783 run
  • ✅ tamper_guard_clear — tamper guard did not fire
  • ✅ repro_gate — repro gate pass
  • ✅ verifiers_all_satisfied — 2 verifiers, none failed
  • ✅ ci — ci: success

Acceptance criteria

  • The web e2e walks are executed by automation on every change that touches web/, or a stated, justified subset of them is. If a subset, the PR body names which walks are excluded and why, and says what covers the excluded behaviour instead.
  • The two walks currently red on main are either fixed or explicitly quarantined with a reason - onboarding-consent-step.mjs (rail entry count 8 vs 7) and onboarding-summary-counts.mjs (TimeoutError on Continue, never fills the Email field). A green run that silently skips them does not satisfy this.
  • A red walk fails the job. Prove it: break one walk deliberately in a scratch branch or a dry run and show the job going red, with the command and its whole summary line. A suite that runs but cannot fail the build is the same gap in a new place.
  • No regex-over-source assertion is introduced as a substitute for executing a walk. State in the PR body how the chosen approach avoids that, since this repo has paid for source-text guards before.
  • The PR body reports the measured wall-clock cost of whatever is added to CI, and states whether it runs on every push, only on web/ changes, or on a schedule - with the reason for the choice.
  • Whole-suite green, with each command and its complete summary line including any failed count quoted in the PR body.

Changes

All evidence is now gathered and fresh. Final report:

Final Report

Summary: The 5-commits-behind merge blocker is resolved. git merge dbe45906ecc79fd22eb4ecb9f0679367d4e805a9 was run (not rebase); the three conflicting paths (RELEASE_MANIFEST.txt, web/e2e/run-all.mjs, web/src/Onboarding.jsx) were resolved and committed as 097bb776. dbe45906 is confirmed an ancestor of HEAD. The feature itself (a new web_e2e CI job wiring web/e2e/*.mjs Playwright walks into GitHub Actions) was implemented in prior commits (ea961e09, 5faed31a) and is fully verified fresh in this session.

Rest of the coder's report (3 more paragraphs)
  • MET — The web e2e walks are executed by automation on every change that touches web/, or a stated, justified subset. If a subset, the PR body names which walks are excluded and why, and what covers the excluded behaviour instead. — evidence: .github/workflows/ci.yml new web_e2e job (name: "Web UI gate (e2e walks)", runs-on: ubuntu-latest, timeout-minutes: 25, no if:/paths:/needs:, so it runs on exactly what the pre-existing web job runs on — every push/PR); steps run npx playwright install --with-deps chromium, npm run build, npm run e2e:ci. web/e2e/manifest.mjs is the single source of truth for lane membership: 25 walks are lane: "ci" (run by npm run e2e:ci → node e2e/run-all.mjs --lane=ci), 6 are lane: "manual" each with a named, ≥40-char reason enforced by web/src/e2eManifest.test.mjs (verified 4/4 pass: node --test src/e2eManifest.test.mjs). Excluded walks and why: drawer.mjs (real mobile CSS/layout defect, web/e2e/manifest.mjs:20-26), merge-progress.mjs (pre-existing undiagnosed navigation crash, manifest.mjs:28-35), mobile-nav.mjs (real CSS regression at web/styles.css:627, manifest.mjs:40-48), onboarding-summary-counts.mjs (see next criterion, manifest.mjs:51-58), live-flows.mjs/electron-smoke.mjs/sweep.mjs/persona-file-task.mjs (all need a live :8420 server and/or an installed desktop shell the web_e2e job intentionally never stands up, manifest.mjs:83-102). Nothing is silently skipped — test_ci_network_step_bounds.py::test_web_e2e_job_runs_the_ci_lane_unconditionally (line 213) asserts web_e2e has no if/needs and runs npm run e2e:ci; ran green: uv run pytest tests/test_ci_network_step_bounds.py -q → 12 passed in 7.31s.

  • MET — The two walks currently red on main are either fixed or explicitly quarantined with a reason. A green run that silently skips them does not satisfy this.

  • onboarding-consent-step.mjs (rail entry count 8 vs 7): FIXED, lane: "ci". Root cause was a stale hardcoded BASE_STEPS_COUNT = 8 in the walk itself; fixed to import the real BASE_STEPS from web/src/onboardingSteps.js (a file moved out of Onboarding.jsx for exactly this purpose) and assert against it — a behavioral DOM assertion, not a source-text regex. Confirmed base is red by extracting origin/main's version and running it: FAIL [never asked] rail entry count = BASE_STEPS (8) — got 7 ×3, 3 FAILURE(S), exit=1. Confirmed fixed version green: node e2e/onboarding-consent-step.mjs → ALL CHECKS PASSED, exit=0, rail labels = ["welcome","email","repositories","projects","integrations","community","launch"] (7 entries). Also re-confirmed green in the fresh full-suite run below (PASS onboarding consent step (6.7s) ALL CHECKS PASSED).

  • onboarding-summary-counts.mjs (TimeoutError on Continue): its own walk bug (a stale fixed hop-count loop) is fixed — it no longer times out — but fixing it exposed a separate, real, pre-existing app bug: launchReadiness() in web/src/onboardingProjects.js:116,118 hardcodes jumpTo: 1/jumpTo: 2 step indices that predate the "email" step landing at index 1, so both "Fix →" buttons jump one step early. That's an application-code fix, explicitly out of scope. QUARANTINED, `l

(summary truncated at 4000 characters — full report: nh task show <task-id>)

⚠️ Unresolved: mechanical derived-artefact conflict resolution failed: #483 step=regenerate

⚠️ Open question: PR #483 conflicts only in derived artefact(s) (RELEASE_MANIFEST.txt) but mechanical resolution failed at step 'regenerate'; detail: check_release_manifest --write failed (1): Traceback (most recent call last): File "/private/var/folders/1r/3r0rt1jd4j1456rsg_fh4d380000gn/T/nh-derived-0aw_t95k/scripts/check_release_manifest.py", line 410, in raise

⚠️ 2 assumptions made on your behalf — verify at review
  • Q: Do any web e2e walks require external service credentials, API keys, or network access to third-party services (e.g., email providers, payment processors, or other integrations) that are not available in CI? A: HUMAN-GATED: not self-answerable
  • Q: Do any web e2e walks perform persistent mutations—such as creating user accounts, modifying databases, or making API calls with irreversible side effects—in a way that would require resetting or recreating test state between runs? A: HUMAN-GATED: not self-answerable

How I verified this

Tests (3 runs, last shown) — cd /Users/eyalgolan/.<redacted>/worktrees/914a8bb88c4f469b9a11c853fcc5f9ad.6460.4754afe4 python3 scripts/check_release_manifest.py 2>&1 echo "---" uv run pytest tests/ -m repoguard -q 2>&1 | tail -20 · full log
OK: 1681 file(s) match RELEASE_MANIFEST.txt
---
..........................................................s.s.s.s.s.s.s. [ 34%]
s.s.s...............................................s................... [ 68%]
..........s.......................................................       [100%]
=============================== warnings summary ===============================
src/<redacted>/testing/test_layers.py:35
  /Users/eyalgolan/.<redacted>/worktrees/914a8bb88c4f469b9a11c853fcc5f9ad.6460.4754afe4/src/<redacted>/testing/test_layers.py:35: PytestCollectionWarning: cannot collect test class 'TestLayer' because it has a __init__ constructor (from: tests/test_test_layers.py)
    @dataclass

src/<redacted>/testing/test_layers.py:89
  /Users/eyalgolan/.<redacted>/worktrees/914a8bb88c4f469b9a11c853fcc5f9ad.6460.4754afe4/src/<redacted>/testing/test_layers.py:89: PytestCollectionWarning: cannot collect test class 'TestPlan' because it has a __init__ constructor (from: tests/test_test_layers.py)
    @dataclass

-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html
198 passed, 13 skipped, 13521 deselected, 2 warnings in 109.77s (0:01:49)
Build — pwd && npm run build 2>&1 | tail -40 · full log
/Users/eyalgolan/.<redacted>/worktrees/914a8bb88c4f469b9a11c853fcc5f9ad.6460.4754afe4/web

vite v6.4.3 building for production...
transforming...
✓ 406 modules transformed.
rendering chunks...
computing gzip size...
dist/index.html                                                                  0.66 kB │ gzip:   0.44 kB
dist/assets/-F63fjptAgt5VM-kVkqdyU8n1iAq131nj-otFQ-DKn25-tQ.woff2                4.00 kB
dist/assets/-F6qfjptAgt5VM-kVkqdyU8n3twJwl9FgsAXHNlYzg-B5e70VyC.woff2            4.04 kB
dist/assets/-F6qfjptAgt5VM-kVkqdyU8n3vAOwl9FgsAXHNlYzg-Dky8cY56.woff2            4.12 kB
dist/assets/-F6qfjptAgt5VM-kVkqdyU8n3vAOwl1FgsAXHNlYzg-hCF3fsXQ.woff2            4.32 kB
dist
[... 1,989 of 3,128 characters omitted from the middle ...]

dist/assets/index-fAMrYx0i.js                                                  722.65 kB │ gzip: 221.34 kB

(!) Some chunks are larger than 500 kB after minification. Consider:
- Using dynamic import() to code-split the application
- Use build.rollupOptions.output.manualChunks to improve chunking: https://rollupjs.org/configuration-options/#output-manualchunks
- Adjust chunk size limit for this warning via build.chunkSizeWarningLimit.
✓ built in 4.78s

excerpt — 3,126 characters of output in total

4 commands recorded while working. Full verification log: ~/.no_human/artifacts/914a8bb88c4f469b9a11c853fcc5f9ad/verification-attempt-3.md — nh logs 914a8bb8; the same log, every command with its captured output, is posted as this PR's How I verified this comment when posting succeeds. Whether it passed is in the Evidence table above: no entry here asserts a pass or a fail.

UI evidence

Visual proof skipped: playwright not installed - run nh doctor --fix-walks to enable


Opened by no_human (attempt 3 of 3, no-human/914a8bb8 → main). It never merges: review and merge this yourself, or run nh approve 914a8bb8.

@eyalgolan

Copy link
Copy Markdown
Contributor Author

How I verified this

11 commands recorded - as recorded (shortened, folded onto one line), grouped by kind. No entry asserts a pass or a fail: read the output. Not necessarily everything the session ran.

test

  • npm test 2>&1 | tail -20
...
# Subtest: a close during an in-flight snapshot cancels it and restarts backoff at 1s
ok 1762 - a close during an in-flight snapshot cancels it and restarts backoff at 1s
  ---
  duration_ms: 0.150625
  ...
# Subtest: stop() is idempotent and leaves no pending timer or open socket
ok 1763 - stop() is idempotent and leaves no pending timer or open socket
  ---
  duration_ms: 0.163209
  ...
1..1763
# tests 1763
# suites 0
# pass 1762
# fail 1
# cancelled 0
# skipped 0
# todo 0
# duration_ms 656.198458
  • npm test 2>&1 | grep -B2 -A 25 "^not ok"
...
# Subtest: the wizard's STEPS list really has lastIndex 6, so these cases are the real ones
not ok 1169 - the wizard's STEPS list really has lastIndex 6, so these cases are the real ones
  ---
  duration_ms: 0.795833
  location: '/Users/eyalgolan/.<redacted>/worktrees/914a8bb88c4f469b9a11c853fcc5f9ad.62504.2b528b22/web/src/onboardingNav.test.mjs:119:1'
  failureType: 'testCodeFailure'
  error: |-
    STEPS changed length — update LAST in this test so the launch case still tests the LAST step
    
    -1 !== 6
    
  code: 'ERR_ASSERTION'
  name: 'AssertionError'
  expected: 6
  actual: -1
  operator: 'strictEqual'
  stack: |-
    TestContext.<anonymous> (file:///Users/
[... 199 of 1,338 characters omitted from the middle ...]
st_runner/test:796:25)
    Test.processPendingSubtests (node:internal/test_runner/test:526:18)
    Test.postRun (node:internal/test_runner/test:889:19)
    Test.run (node:internal/test_runner/test:835:12)
    async Test.processPendingSubtests (node:internal/test_runner/test:526:7)
  ...
# Subtest: the step indicator renders BUTTONS that jump via setI, gated on canJumpTo
ok 1170 - the step indicator renders BUTTONS that jump via setI, gated on canJumpTo

excerpt - 1,334 characters of output in total

  • npm test 2>&1 | tail -10
...
1..1763
# tests 1763
# suites 0
# pass 1763
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 648.709916
  • uv run pytest tests/test_ci_network_step_bounds.py tests/test_desktop_job_follows_paths.py -q 2>&1 | tail -30
....................................                                     [100%]
36 passed in 6.41s
  • head -20 tests/test_test_lanes.py; echo ---; uv run pytest tests/test_test_lanes.py -q 2>&1 | tail -15
"""The two test lanes are spelled identically in both places, and they partition
the suite.

`.github/workflows/ci.yml` and `scripts/run_tests.sh` each choose tests with a
`-m` marker expression, and the two are maintained by hand. Both files already
argue for this guard in prose:

  ci.yml          "every node is in exactly one, which tests/test_test_lanes.py
                   pins and the collection counts reconcile"
  CONTRIBUTING.md "a mistyped marker expression drops a node out of *both*, and
                   a test that runs nowhere looks exactly like a test that
                   passes"

Until this file existed those two sentences named a test that was not in the
tree (issue #109): it lived only in the private repo this project was developed
in, where four of its assertions checked for files that do not exist here, so
the prose was exported and the test was not.

This is the public-tree version. It checks the two properties that matter and
nothing that depends on the other repository.
---
....                                                                     [100%]
4 passed in 15.53s
  • cd web && npm test 2>&1 | tail -20
...
# Subtest: a close during an in-flight snapshot cancels it and restarts backoff at 1s
ok 1762 - a close during an in-flight snapshot cancels it and restarts backoff at 1s
  ---
  duration_ms: 0.1985
  ...
# Subtest: stop() is idempotent and leaves no pending timer or open socket
ok 1763 - stop() is idempotent and leaves no pending timer or open socket
  ---
  duration_ms: 0.180209
  ...
1..1763
# tests 1763
# suites 0
# pass 1763
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 920.590459
  • cd /Users/eyalgolan/.<redacted>/worktrees/914a8bb88c4f469b9a11c853fcc5f9ad.62504.2b528b22 && uv run pytest tests/test_ci_network_step_bounds.py tests/test_desktop_job_follows_paths.py -q 2>&1 | tail -10
....................................                                     [100%]
36 passed in 4.59s

e2e

  • cd /Users/eyalgolan/.<redacted>/worktrees/914a8bb88c4f469b9a11c853fcc5f9ad.62504.2b528b22/web && ls node_modules >/dev/null 2>&1 && echo "node_modules exists" || echo "missing"; node --version; npx playwright --version 2>&1 | head -5
node_modules exists
v20.20.2
Version 1.61.1

build

  • npm run build 2>&1 | tail -20
dist/assets/-F63fjptAgt5VM-kVkqdyU8n1iEq131nj-otFQ-C05TWSE2.woff2                8.86 kB
dist/assets/-F6qfjptAgt5VM-kVkqdyU8n3vAOwl5FgsAXHNlYzg-BRMVj9uZ.woff2            8.96 kB
dist/assets/-F6pfjptAgt5VM-kVkqdyU8n1ioa23dgregdFOFh-DjXFaAjD.woff2              9.79 kB
dist/assets/-F63fjptAgt5VM-kVkqdyU8n1i8q131nj-o-BJoXLJYV.woff2                  10.05 kB
dist/assets/-F6qfjptAgt5VM-kVkqdyU8n3twJwlBFgsAXHNk-C820gu2e.woff2              10.06 kB
dist/assets/-F6qfjptAgt5VM-kVkqdyU8n3vAOwlBFgsAXHNk-DpGnXj3s.woff2              10.12 kB
dist/assets/-F6pfjptAgt5VM-kVkqdyU8n1ioa1XdgregdFA-BkxdLi3-.woff2               11.57 kB
dist/assets/rP2Wp2ywxg089UriCZaSExdy3sGt9zz86GPwyKK58UfivUw4
[... 509 of 1,648 characters omitted from the middle ...]

dist/assets/index-CzcXGG31.js                                                  721.67 kB │ gzip: 220.91 kB

(!) Some chunks are larger than 500 kB after minification. Consider:
- Using dynamic import() to code-split the application
- Use build.rollupOptions.output.manualChunks to improve chunking: https://rollupjs.org/configuration-options/#output-manualchunks
- Adjust chunk size limit for this warning via build.chunkSizeWarningLimit.
✓ built in 1.32s

excerpt - 1,648 characters of output in total

  • npm run build 2>&1 | tail -20
dist/assets/-F63fjptAgt5VM-kVkqdyU8n1iEq131nj-otFQ-C05TWSE2.woff2                8.86 kB
dist/assets/-F6qfjptAgt5VM-kVkqdyU8n3vAOwl5FgsAXHNlYzg-BRMVj9uZ.woff2            8.96 kB
dist/assets/-F6pfjptAgt5VM-kVkqdyU8n1ioa23dgregdFOFh-DjXFaAjD.woff2              9.79 kB
dist/assets/-F63fjptAgt5VM-kVkqdyU8n1i8q131nj-o-BJoXLJYV.woff2                  10.05 kB
dist/assets/-F6qfjptAgt5VM-kVkqdyU8n3twJwlBFgsAXHNk-C820gu2e.woff2              10.06 kB
dist/assets/-F6qfjptAgt5VM-kVkqdyU8n3vAOwlBFgsAXHNk-DpGnXj3s.woff2              10.12 kB
dist/assets/-F6pfjptAgt5VM-kVkqdyU8n1ioa1XdgregdFA-BkxdLi3-.woff2               11.57 kB
dist/assets/rP2Wp2ywxg089UriCZaSExdy3sGt9zz86GPwyKK58UfivUw4
[... 509 of 1,648 characters omitted from the middle ...]

dist/assets/index-C0zKT-8U.js                                                  721.67 kB │ gzip: 220.92 kB

(!) Some chunks are larger than 500 kB after minification. Consider:
- Using dynamic import() to code-split the application
- Use build.rollupOptions.output.manualChunks to improve chunking: https://rollupjs.org/configuration-options/#output-manualchunks
- Adjust chunk size limit for this warning via build.chunkSizeWarningLimit.
✓ built in 1.62s

excerpt - 1,648 characters of output in total

  • rm -f e2e/board.mjs.bak && npm run build > /tmp/build.out 2>&1; echo "BUILD_EXIT=$?"; tail -5 /tmp/build.out
BUILD_EXIT=0
(!) Some chunks are larger than 500 kB after minification. Consider:
- Using dynamic import() to code-split the application
- Use build.rollupOptions.output.manualChunks to improve chunking: https://rollupjs.org/configuration-options/#output-manualchunks
- Adjust chunk size limit for this warning via build.chunkSizeWarningLimit.
✓ built in 1.34s

Not verified: everything below is a limit of this section, listed whether or not it bit this attempt.

  • no command recognised as http, typecheck, lint was recorded
  • an entry shows that a command LINE was submitted to the shell and what came back - never that the check recognised inside it RAN, and never that it was the RIGHT command: pytest -k test_nothing prints a clean run, and a recorded command line may name a check the shell never reached yet is still counted - TEN SHAPES WERE DRIVEN against bash 3.2.57 with the check replaced by a marker-printing stub and the marker was absent in every one: a failed &&, a taken ||, an exit, an exec, an exit inside a sourced script, a syntax error that aborts the REST of the line, a multi-line if false, a case that matches nothing, set -e aborting an earlier command, and set -u on an unset variable; that list is MEASURED, NOT EXHAUSTIVE, because this module is not bash, so a kind this section does NOT list as missing is a kind some recorded line named, which is not the same as a kind that ran
  • the text is the coder's: the session chose the command string and, through echo/printf, can choose the output too. Both are shown as inert text, and no entry ASSERTS a pass, a fail, or an exit status - pytest -q | tail -3 exits with tail's status, Error: Exit code 1 is a line IN THE OUTPUT, and where the harness reported a timeout or an interruption instead of output that report is appended to the captured text in square brackets. Read the output
  • recognition reads the command line ONLY - it never looks inside what a command runs, so bash -c 'uv run pytest -q' leaves no receipt at all while make test leaves one that names make and not the recipe it ran; and the other way, a check merely NAMED in a heredoc body, or in a quoted string that happens to spell a shell separator, can be recorded as though it ran
  • commands run inside a spawned subagent are deliberately excluded, so delegated work leaves no receipt here; a command the harness refused to run (blocked, or permission denied) leaves none, because it never ran; and only a command the HARNESS backgrounded leaves no receipt at all - it hands back a task id instead of output. A trailing & YOU wrote is NOT that and is NOT excluded: pytest -q & is recorded and headed test, and may still have been running when the harness returned
  • the COMMAND and the output are both redacted and bounded before they are stored - an excerpt is not the full log, a credential-shaped string may have been masked out of either, a command over 400 characters is shortened in the middle, each command is displayed on ONE line with its newlines folded to spaces (so it may not re-run as written), and invisible and direction-changing characters are stripped before display; look-alike letters are NOT detected
  • nothing here checks that these commands exercise the diff - no receipt is compared against the files this PR changes; no interactive UI check was performed (no_human never drives a browser at your change except testing/ui_evidence.py's walk, reported as its own evidence, not a receipt; the only other page it drives is a CI server's login form, and the board it opens without driving, so an e2e entry is the project's harness printing its result, not a human-style walkthrough); and no_human's own test run, CI, and the independent review are separate signals - this section covers only the coder session's own commands
  • at most 200 receipts are recorded per attempt; past that the observer stops recording, and this section says so above when the limit was reached

See the PR body's Evidence table for the orchestrator's own test run.

@eyalgolan

Copy link
Copy Markdown
Contributor Author

Independent review — PASSED (1 round) on ea961e0

A different model, fresh context, commit, push and merge refused at the tool call, told to refute "done". This is the checklist the gate decided on; no_human never merges — a human does.

no blocking or passed findings recorded

4 advisory findings (low/nit — never blocking)
Severity Finding Where Note
❌ low merge-progress quarantined on an undiagnosed failure web/e2e/manifest.mjs:27 Of all the manual-lane exclusions this is the one I'd push back on — the others pin a real defect to a line of app code, but merge-progress is quarantined with
❌ nit excluded infra walks don't state what covers their behaviour web/e2e/manifest.mjs:84 The manual reasons cover the why well, but the criterion also asks what covers the excluded behaviour instead, and for the infra-blocked walks the answer is eff
❌ low tests: regex-over-source guards where the module is now importable web/src/onboardingConsent.test.mjs:140 Now that BASE_STEPS is its own importable module, these guards can just import { BASE_STEPS } and assert on the array directly instead of readFileSync + regex
❌ low maintainability: manifest section header contradicts entries under it web/e2e/manifest.mjs:18 The "ci lane — green, run on every push/PR" header sits directly above drawer, merge-progress, mobile-nav and onboarding-summary-counts, all of which are lane:"

@eyalgolan eyalgolan changed the title Nothing runs the web e2e walks, so their coverage is advisory ci(web): run the e2e walks as a required CI job Sep 23, 2026
Adds a `web_e2e` job ("Web UI gate (e2e walks)") that installs Chromium,
builds web/dist and runs `npm run e2e:ci` on every push to main and every
pull request (no `if:`/`paths:`/`needs:`). web/e2e/manifest.mjs is the one
list of walks: "ci" lane walks run in the job, "manual" lane walks carry a
written reason. The job is bounded at 25 minutes, every step has its own
timeout-minutes (pinned by tests/test_ci_network_step_bounds.py), and
run-all.mjs kills any single walk after 5 minutes and reports it FAILED by
name. A failing walk exits run-all non-zero and fails the job.

Quarantined walks are reported, not skipped silently: with --lane=ci,
run-all.mjs prints a "NOT RUN <walk> (lane: manual) - <reason>" line for
each one before running, and the summary line counts them. The job is not
yet in main's branch-protection required checks; adding "Web UI gate (e2e
walks)" there is a repository setting.

BASE_STEPS moves from Onboarding.jsx to web/src/onboardingSteps.js so the
consent walk imports the real step list. On main the walk asserts
BASE_STEPS_COUNT = 8 (web/e2e/onboarding-consent-step.mjs:55) against a
7-step rail and fails 3 checks; it now compares the rail labels to
BASE_STEPS and passes. This supersedes #399, which reaches the same list by
parsing Onboarding.jsx's source text.

Walks brought up to date with the app (walk bugs, now in the ci lane):
- drawer.mjs: mocks the per-attempt details endpoint, expects the "Approve
  and merge" label, and measures `.so-scroll` (the drawer's scroll region)
  instead of `.so-body` in [D2a] and [M4+].
- merge-progress.mjs: approve selectors scoped to `.slideover`, because the
  AI-config nudge's "Open Settings" CTA (src/App.jsx:1312) shares the
  `.btn-approve` class and comes first in the DOM; the events section is
  opened by its current "Event log" label.
- composer.mjs, form-order.mjs, mobile-nav.mjs: selectors no longer collide
  with the AI-config nudge or the board's empty-state button.
- integrations-help-validate.mjs, onboarding-summary-counts.mjs: walk
  through the email step and hop until the target step is visible.

Quarantined (manual lane) on app bugs, not fixed here:
- mobile-nav.mjs: "no nav label is clipped" fails at the small and
  mobile-short viewports, both themes ("In progress 64>62"). The narrow
  viewport override at web/src/styles.css:627 (`flex: 0 0 auto;
  max-width: 100%`) replaces the shrinkable `flex: 1; min-width: 0` from
  web/src/styles.css:379.
- onboarding-summary-counts.mjs: launchReadiness() hardcodes jumpTo: 1
  (web/src/onboardingProjects.js:116) and jumpTo: 2 (:118), indices from
  before the email step took index 1, so the Launch card's "Fix →" button
  (web/src/Onboarding.jsx setI(r.jumpTo)) opens one step early. "Fix →
  jumped to the Repositories step" fails in two scenarios.

Manual lane by design (need a live :8420 server or an installed desktop
shell that this job does not start): live-flows, electron-smoke, sweep,
persona-file-task.
@eyalgolan
eyalgolan merged commit 6592b53 into main Oct 9, 2026
13 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant