Repository navigation
Add real-model browser E2E nightly tests with reusable scenarios #204
Description
Goal
Add a repeatable nightly E2E suite that exercises the real user flow:
Browser → FlyBot Web → DSH agent loop → real DeepSeek API → real tools → delivered files visible in the browser.
The existing .github/workflows/nightly.yml is a compatibility matrix. It does not cover this real-model user journey. Related: #30.
Keep scenario inputs and result assertions independent of the application so that Orbit can run the same scenarios through its own UI adapter. Orbit's current nightly implementation has not been inspected; integration with it remains to be validated.
Initial scenarios
Use one fresh, isolated test home and workspace per run. Drive task submission, attachment upload, follow-up messages, reload, and artifact preview through the browser. Read-only API polling and independent filesystem checks may observe completion and verify results; they must not perform the user's task.
Fixture:
order_id,region,status,amount
A001,华东,paid,100
A002,华北,paid,200
A003,华东,pending,300
A004,华东,paid,40
A005,华北,paid,60- Upload and process a file. Upload the CSV and ask for paid orders as
summary.json, withorders(full order details),count, and numerictotal. Independently assert IDs A001/A002/A004/A005, count 4, total 400, and every retained row's fields. - Correct the task in the same conversation. Ask to include only paid orders in 华东, retain the details/schema, save
summary-east.json, and keep the original unchanged. Assert IDs A001/A004, count 2, total 140, and byte-for-byte preservation of the original. - Restore the conversation after reload. Reload the browser and verify that the conversation and both delivered artifact cards remain available and both files remain correct.
- Open delivered artifacts. Click each artifact's sidebar preview through the UI and compare the displayed JSON with the independently verified output file. A filename echoed in the user's prompt is not evidence of delivery.
Different valid tool sequences and reply wording are acceptable; verify results and constraints rather than exact model text.
Local pilot evidence
A single real-model browser pilot passed all four checks on 2026-10-17 (Asia/Shanghai) at commit 0052df09b98ed868d590aa79bbebdd85296ea9c4:
- Runtime: Node 24.19.0; DSH 0.2.0-rc.2.
- Route:
deepseek-official/deepseek-flash; configured reasoning effortlow, output cap 4096. - The test profile loaded FlyBot core and tree; tree delegation itself was not exercised.
- Per-check elapsed times: 11.454 s, 8.911 s, 1.813 s, 0.551 s; sum 22.729 s, excluding setup, startup, debugging, and gaps between checks. These observations are not performance thresholds.
- UI-reported session usage: approximately 116K tokens, 87% cache hit, 3 turns / 8 steps. This is not a billing calculation.
- Screenshots, transcript, structured report, and output files were retained locally. The prototype runner is not committed yet.
- Test service/browser were stopped; the repository was unchanged.
- The report records per-check durations but does not yet record absolute run start/end timestamps.
This was one successful pilot, not evidence of nightly reliability.
Acceptance criteria
- Add a documented local command for the live browser E2E suite, using the supported DSH CLI/profile interfaces and the pinned runtime; do not patch DSH.
- Implement the four checks above, with fresh run IDs/data, isolated
DSH_HOME/workspace, bounded waits, and cleanup on success, failure, or interruption. Never use the user's normal DSH home. - Separate shared fixtures/scenarios/result assertions from FlyBot UI operations. Document the adapter contract and the remaining work needed to run the same cases on Orbit.
- Run the suite nightly and via manual dispatch, separately from the compatibility matrix and ordinary PR checks. Use the real model and tools; mock-based suites remain separate.
- Load
DEEPSEEK_API_KEYfrom CI secrets/environment. Missing credentials must be reported explicitly, never counted as a successful E2E run. Do not commit credentials or publish tokens/cookies in artifacts. - Record revision, runtime/model configuration, run ID, absolute start/end timestamps, per-check durations, outcomes, and available token/cache usage. Store machine-readable timestamps with an explicit timezone and show human-facing times in Asia/Shanghai.
- Retain useful failure evidence: screenshots, conversation, sanitized service diagnostics, result files, and a structured report. Distinguish task/assertion failures, provider failures, and test infrastructure failures.
- Set configurable time and usage/cost budgets; preserve failed attempts rather than allowing retries to hide regressions.
- Validate repeated independent runs before claiming the suite is stable;
npm run verifymust remain green for the final implementation.
Likely locations
core/dev/: existingsmoke.mjs(mock/API smoke),ui-shots.mjs(browser examples), and isolated bench conventions.core/dev/harness/package.json: pinned DSH test runtime.- A dedicated live-E2E workflow under
.github/workflows/. - Shared scenario/fixture modules and an application adapter interface, with their final location chosen during implementation.
Out of scope for the first version
Desktop, tree delegation, social/remote peers, multiple accounts, browser file downloads, and fault injection are not covered by the pilot or required for the initial suite. No production data or credentials should be used. Do not mark Orbit integration complete until its real entrypoint has been exercised.
Activity
hsliuustc0106 commentedon Oct 17, 2026
Review: readiness for an implementer
Automated review, not a human sign-off. Basis: this issue text as of 2026-10-18,
mainat4470c1e(the pilot's0052df0is an ancestor). I checked the claims against the repo instead of trusting them.Verdict: ready to schedule after four decisions. The scenario set and the result assertions are sound. Everything still undefined is about how the suite runs in CI, and each item changes the implementation shape — so decide them here, not in review.
What checks out
0052df0is onmain, and the runtime matches the pin in core/dev/harness/package.json (0.2.0-rc.2).deepseek-officialplus modeldeepseek-flashexist inllm-deepseek-api-key/llm-deepseek, and DSH readsDEEPSEEK_API_KEY(andDEEPSEEK_BASE_URL) from the environment — so no DSH patch is needed, which satisfies the hard rule.Decide before implementation
1. The launcher is coupled to a DSH checkout; CI installs DSH from npm.
bench.sh starts
pnpm -s dsh … --patch apps/web/tests/pin-browse-picker.overlay.ymlfrom adeepseek-harnesscheckout. That overlay is a checkout-only test fixture: it pins the in-browser directory picker, because the native OS chooser cannot be driven by a browser automation page. Scenario 1 (attachment upload) depends on it. CI installs@deepseek-ai/dshintocore/dev/harness, where neither that path norpnpmexists. Choose one and say so in this issue: (a) the workflow clones the harness at the pinned tag, or (b) the suite runs the npm-installeddshwith a FlyBot-owned overlay that disables and re-inserts the browse-picker rows.2. No browser driver is declared in this repo.
lib.mjs resolves
playwright-coreout of a DSH checkout's pnpm store and says outright that "ds-bot installs no browser tooling of its own". CI has Chrome onubuntu-latest, but no such store. Adding a pinnedplaywright-core(or driving Chrome over CDP) is a decision against that stated invariant, and it belongs here rather than in a review comment later.3. Secret provisioning and hygiene.
At review time this repository has zero Actions secrets configured (
GET /repos/.../actions/secrets→total_count: 0), soDEEPSEEK_API_KEYhas to be added before the first scheduled run — say who does that. The repo is public, so Actions logs and artifacts are world-readable and a leak is permanent. The issue should require, concretely: the secret-bearing job runs onmainonly (if: github.ref == 'refs/heads/main', neverpull_request),permissions: contents: read,web.logand diagnostics sanitized (?token=is written there — see bench.sh and the login regex in smoke.mjs), an explicit artifact allow-list, and no rawDSH_HOMEupload. Also add a hard guard that refuses to start whenDSH_HOMEis unset or resolves to~/.dsh; today that is prose only.4. Where the code lives — and therefore which gates the PR hits.
core/dev/is owned by ds-bot-core.md, so a live runner there forces a doc update and makes every edit flow through patch generation. Rootdev/is now an established home for dev-only harnesses (see below). State the choice; the two options have different review surfaces.Reuse the interop harness pattern (#201)
mainjust gained dev/interop/ (#201): a development-only, runtime-agnostic harness with a no-network tripwire, fake clock, fresh tmpdir per run, and keyless tests wired into rootnpm run verify. This issue describes the same shape — shared scenarios and assertions, application-specific adapter, separate live runner — and should follow it rather than invent a second convention:npm test/npm run verify(the core glob iscore/test/*.test.js);docs/interop/already holds the OpenCode pilot contract), and say how the two relate — otherwise two "adapter contract" documents will coexist.Tighten these ambiguities
summary.jsonlikewise unmodified (scenario 4 compares it).\uescaping will differ. Keep byte-for-byte only for the input CSV. Assertcountandtotalare numbers.DSH_HOMEand workspace per run.mock-key, and record the model id. That is what actually enforces "mock suites remain separate".pass | assertion_failed | provider_failed | infra_failed | budget_exceeded, mapped to exit codes, plus the report schema (run ID, ISO-8601 timestamps with offset, monotonic per-check durations, recorded config, usage).packagejob lints workflows in test.yml),concurrencywithcancel-in-progress: false, a UTC cron slot distinct from compat's 16:00Z, commented in Asia/Shanghai, not added totests-okor branch protection, and optionally the same "window check" gate so no tokens are spent whenmainhas no new commits.Suggested acceptance criteria additions
deepseek-harnesscheckout, or the workflow provisions one at the pinned tag — state which, and how the directory-picker pin is applied.DEEPSEEK_API_KEYis missing or a placeholder — never reported as a pass.DEEPSEEK_API_KEYis provisioned as a repository secret; the secret-bearing job runs onmainonly, and artifacts pass a sanitizer that rejectstoken=, key material and cookies.dev/interoppattern, sonpm run verifystays green and meaningful.No repository files were changed by this review.
hsliuustc0106 commentedon Oct 17, 2026
Decisions on the four blockers, per the readiness review (to be delivered by the implementation PR):
deepseek-harnesscheckout at the pinned tag (derived fromcore/dev/harness/package.json→dsh-v<version>), exactly like the local bench, and the upstreamapps/web/tests/pin-browse-picker.overlay.ymlapplies the directory-picker pin. No FlyBot-owned overlay; the suite never runs from the npm-installed CLI.playwright-coreresolves from that same harness checkout through the existingdev/desktop-linux/lib.mjshelper — the repo still declares no browser tooling of its own. Chrome:CHROME_BIN, elsegoogle-chrome/chromium(-browser) on PATH (ubuntu-latest ships Chrome), else playwright's download; documented in the suite README.DEEPSEEK_API_KEYis provisioned by the maintainer as a repository secret before the first scheduled run (this repo currently has zero secrets). The secret-bearing job runs onmainonly (schedule + dispatch guarded bygithub.ref == 'refs/heads/main'; neverpull_request),permissions: contents: read, evidence passes a sanitizer that fails ontoken=, the secret's value or cookie material, and only an explicit allow-list is uploaded (never a rawDSH_HOME). The runner fails closed when the key is missing/mock-key/placeholder, when the resolved base URL is loopback, or whenDSH_HOMEis unset or resolves under~/.dsh.dev/live-e2e/, besidedev/interop/(Add the offline cross-runtime collaboration harness #201): fixture/scenario/assertion modules with keyless fake-adapter tests wired into rootnpm run verify; the live runner (bench launcher, adapter, reporter) runs only via the documented command and the nightly/dispatch workflow. The adapter contract goes todocs/interop/next to the OpenCode pilot contract, with a section relating the two. Orbit integration is filed as a follow-up marked not exercised.Tightenings adopted: CSV immutability asserted by SHA-256 (the uploaded copy in the run workspace, and
summary.jsonacross later scenarios); JSON compared parsed withcount/totalrequired to be numbers, byte-for-byte only for the CSV; freshness = new run id + freshDSH_HOME/workspace with the fixture CSV fixed; the resolved route must be non-loopback and the key non-mock, recorded in the report; budgets default to per-check ≤ 180 s, job ≤ 20 min, ≤ 250K tokens/run, zero retries; outcome contractpass | assertion_failed | provider_failed | infra_failed | budget_exceededmapped to exit codes 0–4 with a structured report (run id, ISO-8601 timestamps with offset, monotonic per-check durations, config, usage); workflow: own file, actionlint-clean,concurrencywithcancel-in-progress: false, cron30 16 * * *UTC (00:30 Asia/Shanghai, clear of compat's 16:00Z), window gate, never added totests-ok.Maintainer asks (not blockers for the PR): attach the sanitized pilot report/transcript here, and add the
DEEPSEEK_API_KEYsecret before the first scheduled run.Add the real-model browser E2E suite of #204
Add the real-model browser E2E suite of #204