Tests should protect current behavior: runtime tools, session guards, project and file operations, Git and shell dispatch, Runner transports, MCP, and OAuth scope policy. Test count alone proves neither useful coverage nor unnecessary complexity. Trace a test to a current entry point, consumer, or real boundary; a passing test of an unused configuration parser or always-empty policy does not justify keeping that mechanism.
When retiring a concept, remove its dedicated tests and fixtures with it. Keep coverage of the surviving public behavior and safety boundaries, rather than replacing removed assertions with source-text checks that freeze another implementation detail. Also review layering, global state leakage, bounded waits, and the cost of each test lane.
Run python3 scripts/check_production_warnings.py (python on Windows) separately
from cargo test or cargo check --all-targets. It checks every workspace binary
with the dogfood profile and default production features, without test/dev feature
unification. Workspace warnings fail the gate; dependency lint policy is unchanged.
Compiler diagnostics stream to the console and a bounded result is saved under
target/ci-build-reports/. Each invocation runs once: it never retries a failure.
Linux tooling and the native Windows/macOS core lanes own this check. To validate
actual linkable binaries too, use cargo build --locked --workspace --bins --profile dogfood.
For a flaky failure, retain the original run/job log and distinguish an invalid
fixture assumption from a product race. Synchronize on observable acknowledgements
rather than sleeps; never accept generic lost/timeout outcomes merely to turn CI
green. The ACP prompt/cancel race has three fenced outcomes: pre-handoff cancellation,
acknowledged prompt plus terminal cancellation, and unacknowledged handoff with the
specific coding_agent_prompt_write_uncertain outcome. An external provider log is
not a writer acknowledgement. Both writer orderings have deterministic tests.
Executable-format probes use a checked-in executable fixture so unrelated concurrent
forks cannot inherit a test-created writable handle to that executable.
On macOS, bash scripts/macos_finalize_dmg.sh --preflight exercises native image
create/attach/read/detach before expensive builds in ordinary Desktop CI, extended
native readiness, and release jobs. It does not sign, install, publish, or use keys.
Finalization leaves native command diagnostics visible (no -quiet), reports the
failing stage and exit status, and preserves the original DMG until its verified
replacement is ready. Small stage/disk-space reports live in target/dmg-diagnostics/
and release jobs upload them even on failure. They never contain signing argv or
environment dumps. Successful preflight does not guarantee later disk space,
notarization, or signing availability; final checks remain authoritative.
| Lane | Purpose | Default resources | Typical command |
|---|---|---|---|
| fast unit | Pure parsing, validation, helpers, local state machines, small fixtures. | No network, no global env mutation, no long sleeps. | cargo test -p webcodex --lib tool_call |
| contract/schema | Keep metadata, registry, MCP tools/list, and runtime tool names synchronized. |
No external network; in-process services are preferred. | cargo test -p webcodex --lib metadata; cargo test -p webcodex --lib mcp |
| local integration | Exercise HTTP handlers, runtime dispatch, sessions, local agent registry, temp dirs, loopback listeners, and database fixtures. | Loopback only, isolated temp dirs, bounded waits, no shared mutable state without a lock. | cargo test -p webcodex --lib runtime_http -- --nocapture; cargo test -p webcodex --lib session -- --nocapture |
| Runner/LSP real-process | Process-tree ownership, real shell timeout/stop, polling dispatch timing, Plugin startup, validation/Git ManagedChild, JobManager descendant cleanup, and native LSP child lifecycle. Runner coverage is gated by runner-real-process-tests, which also enables the LSP crate's real-process-tests feature; these tests are ignored by default execution and share the runner_real_process_ name prefix. |
Real local child processes only; no external network. Run serially because the assertions intentionally exercise OS scheduling and process teardown. | cargo test --locked -p webcodex-runner -p webcodex-lsp --features runner-real-process-tests runner_real_process -- --ignored --test-threads=1 |
| Process lifecycle real-process | ManagedChild ownership, graceful/forced termination, descendants, EOF, liveness, and reaping. Most lifecycle tests in the integration target are ignored; pure type/spawn-error smoke remains ordinary. |
Real local helper processes and OS liveness probes. | cargo test --locked -p webcodex-process --test managed_child -- --ignored --test-threads=1 |
| Persistent-shell timing | Timeout, concurrent busy-state, close-vs-exec, idle expiry, descendant teardown, and heavy adversarial PowerShell timing/status coverage. Fast state/error/exit smoke remains ordinary. | Real shell processes; serial execution only. | cargo test --locked -p webcodex-persistent-shell -- --ignored --test-threads=1 |
| Desktop Windows real-process | Windows Desktop stdin-EOF shutdown and bounded-command process-tree reclamation. These tests are ignored by the ordinary Desktop suite and share the desktop_real_process_windows_ name prefix. |
Real local child processes only; no external network. Run serially so PowerShell startup and process teardown do not compete with the ordinary Desktop libtest pool. | cargo test --locked --manifest-path apps/desktop/src-tauri/Cargo.toml desktop_real_process_windows_ -- --ignored --test-threads=1 |
| Session ledger scale/recovery | Construct actual retained Session snapshots, restore exact Active/Closed identities, measure full-ledger writes and bounded reads at 100/1,000/10,000 minimal Sessions. The small smoke and damaged-copy checks are ordinary unit tests. | Disposable temporary files; larger experiment explicitly ignored, serial, no production data or timing threshold. | cargo test --locked -p webcodex-workflow-session --lib session_store_scale_and_recovery -- --ignored --test-threads=1 --nocapture |
| slow/manual ignored | Valuable coverage that is local but slow, serial, large-input, or global-state-sensitive. | Explicit operator opt-in; often --ignored and --test-threads=1. |
Run the specific ignored test/filter documented by its subsystem. |
| e2e/deployment smoke | Prove that binaries, local services, MCP schema, artifact transfer, and an agent can work together. | Temporary local services and loopback ports; real deployment only when explicitly requested. | bash scripts/e2e_zero_config_ws.sh; bash scripts/smoke_deployment.sh; bash scripts/smoke_artifact_transfer.sh |
| reconnect continuity | Runner disconnect/reconnect layer independence, stale-not-ready observations, reconciliation-aware recovering/lost transitions, server-restart durable Session plus explicit-session continuity, meaningful-activity scoping, and version-mismatch diagnostics. | In-process fixtures, no external network. | cargo test -p webcodex --lib reconnect |
| trusted smoke | Disposable git fixture full chain (start → edit → failing shell validation → fix → pass → git review → finish) asserting zero approval interruptions under trusted_agent authority, resolved failure evidence, dirty-worktree advisory-only, and bounded payloads; prints baseline counters. |
Temp git fixture, no external network. | cargo test -p webcodex --lib trusted_smoke |
| real-process reconnect harness | Boot a real server plus reconciliation-capable runner, assert layered connection observations, crash the runner (layers degrade independently; running job enters recovering), restart with a new runner instance (old job is fenced to terminal lost with runner_instance_replaced, no server restart), then restart the server and verify runner auto-reconnect plus durable Session lookup and continuation by the original explicit session_id. It also prints post-deploy smoke facts (server version/commit, authority mode, version compatibility, runner shell dialect). |
Local processes and loopback ports. | bash scripts/e2e_reconnect_ws.sh |
| real-process Session/model continuity | Real MCP gateway, Server, WebSocket Runner and model API adapter: save decisions/progress, restart Server, discover and explicitly resume the same Session with a second same-user credential, deny foreign-owner/missing-scope calls, complete both Responses and chat-completions SSE Runs with recovered context, fence exact/changed initiation retries, and retain closed history. | Temporary users, credentials, Git repository, XDG/state roots and loopback model server; no paid model or production services. Build dogfood Server/Runner binaries first. | python3 scripts/e2e_session_continuity_ws.py; see adapter validation. |
| real-process hosted-connect harness | Build the real Server, Runner, and CLI; start a shared-key-enabled loopback Server; run webcodex connect; verify same-key project visibility and a read, cross-key isolation, detached Runner survival, repeated-connect PID reuse, hosted runner status, explicit stop, secret-safe output/log/state, and an untouched Git checkout. |
Local processes, isolated XDG config/state roots, a temp Git project, bounded curl and outer timeout, trap cleanup; never production. | bash scripts/e2e_hosted_connect.sh |
| real-process job reconciliation harness | Boot a real server plus a WebSocket runner that advertises job_state_reconciliation. Scenario A keeps a raw async Job running across a SERVER-only restart and asserts the SAME runner instance, original job_id, preserved ownership/project/session, non-regressing sequence/log cursors, recovered_after_server_restart, original-process stop, and one side-effect set. Scenario B lets a Job complete while the Server is offline and reconciles the terminal result without duplicate logs or execution. Scenario C forces run_process past its synchronous grace window, then proves the handed-off structured Job survives a Server restart and an old Server-epoch observation token refreshes immediately for the same job_id. Scenario D uses a delayed Cargo fixture to force a real cargo_check validation handoff past its sync window, then proves the same restart/token-refresh/stop contract with the validation command started exactly once. Ordinary Runner-owned Jobs keep the Runner process alive for these scenarios; run_detached_process restart survival is a separate supervisor-ownership contract covered by its focused Runner suites and production dogfood. |
Local processes, temp dirs/ports/tokens, and a temp project; no production services or QUIC certs. Scenario D intentionally takes roughly the validation sync window plus restart time. | bash scripts/e2e_job_reconciliation_ws.sh |
| real-process job recovery failure/non-reconciliation harness | Cover the failure and non-reconciliation paths the happy-path reconciliation harness omits, using WEBCODEX_JOB_RECOVERY_GRACE_SECS=10 (clamped, above the 5s floor) so the deadline is bounded without waiting the 120s default. Scenario C: kill the runner only (server stays up), let the job enter recovering, and assert the non-request-triggered recovery-timeout sweep transitions it to lost with runner_recovery_deadline_exceeded, ended_at set once, one list record, stop-on-lost stable, and the command never re-executes. Scenario D: instance B replaces instance A (same client_id, new agent_instance_id); A's job becomes lost with runner_instance_replaced, B starts its own new job, A's late update is rejected, first ended_at/reason preserved. Scenario E: a generation-2 Runner registered with WEBCODEX_RUNNER_DISABLE_JOB_STATE_RECONCILIATION=1 (no capability, no inventory) dispatches a job and, on disconnect, deterministically fences it to lost with runner_disconnected_without_reconciliation (never recovering); after a server restart its public lost receipt remains observable within retention, and a same-client new no-reconciliation instance cannot revive execution. Scenario F: a long job across three server restarts keeps the same job_id, runs the command once, keeps last_update_seq/log cursors non-regressing and markers non-duplicating, and reaches a terminal stopped that survives a third restart with ended_at unchanged by terminal inventory replay. |
Local processes, temp dirs/ports/tokens, and a temp project; no production services or QUIC certs. | bash scripts/e2e_job_recovery_failures_ws.sh |
| security auth matrix | Cover OAuth, scope policy, shared-key behavior, token classes, read-only session guards, and denied mutations. | No external identity provider by default; use local fixtures and synthetic tokens. | cargo test -p webcodex --lib oauth -- --nocapture; cargo test -p webcodex --lib scope -- --nocapture; cargo test -p webcodex --lib metadata -- --nocapture |
| MCP conformance baseline | Exercise the real Salvo /mcp handlers through a disposable loopback fixture against a pinned upstream referee, then gate on check-level coverage/classification rather than process exit alone. |
Loopback product fixture; immutable external referee source/dependencies fetched only by the explicit script/CI lane; no production credentials. | bash scripts/mcp_conformance.sh; see MCP_CONFORMANCE.md. |
The MCP Apps' DOM/message-order regression tests run without browser or npm
dependencies: node --test src/mcp_tests/*.test.mjs. They execute the embedded
HTML scripts with deterministic Host messages and timers, covering initialization
ordering, terminal Goal convergence, foreground dispatch, finish retries, and
successive continuation Attempts. Rust projection and capability tests use
cargo test --locked -p webcodex --lib result_app,
cargo test --locked -p webcodex --lib goal, and
cargo test --locked -p webcodex --lib agent_continuation.
cargo test --locked -p webcodex --lib read_cache covers bounded Session
snapshot retention and read-only singleflight ownership. Run
cargo test --locked -p webcodex --lib read_files for the canonical read path,
range planning, byte-limit fallback, revision fences, and Runner replacement.
These use deterministic in-memory Runner fixtures, not deployed Runners.
The Session cache is a transfer optimization: every hit validates the current full-file SHA through a one-line read on the owning Runner. It does not skip the Runner's full-file scan, use TTL/mtime as freshness evidence, or make a revision an immutable filesystem snapshot. A changed SHA discards the cached ranges: fenced reads reject a stale revision immediately, while unfenced reads read the requested range again. Concurrent identical physical reads share only pending work within the same authority, Session, exact Project and Runner incarnation; each canonical call still authorizes, records and projects its own result. Without an explicit authorized Session, only in-flight sharing is enabled. Snapshot retention is bounded to 64 entries / 8 MiB per runtime, and the in-flight index to 128 entries (excess reads bypass sharing).
When several linked Git worktrees independently run Rust checks on Windows,
their default per-worktree target/ directories can duplicate large amounts of
dependency build output. This is developer build-time/disk overhead, not a
WebCodex Runtime or end-to-end task latency claim. The opt-in
scripts/cargo_shared_worktree_target.ps1
helper selects the primary checkout's existing target/ directory for a
Cargo invocation from a linked worktree of the same Git repository.
Use PowerShell from the selected worktree:
& ./scripts/cargo_shared_worktree_target.ps1 `
-WorktreePath . `
-CargoArgs @('test', '--locked', '-p', 'webcodex', '--lib', 'tool_call') `
-DryRunInspect the reported worktree, primary_checkout, shared_target and
cargo_args. Omit -DryRun only when a focused compilation/test is actually
needed. No Cargo build or target directory is created during -DryRun.
Ordinary direct cargo commands remain unchanged. This helper is deliberately
not wired into CI, automatic tool dispatch, desktop release packaging or any
Server/Runner launch path.
The helper verifies a shared Git common directory with the script's own
repository, requires both worktrees to contain a workspace-root Cargo manifest,
rejects a caller-selected target/config/manifest/cwd override, and refuses to
replace an existing CARGO_TARGET_DIR. It restores the caller's working
directory and environment whether Cargo succeeds or fails. Cargo's own build
cache locks and fingerprints remain authoritative: a different Rust toolchain,
features, profile, compiler flags or changed workspace sources can still
require recompilation, and simultaneous builds may serialize on the shared
target. The helper is not a safety boundary for untrusted Cargo packages:
build scripts run with the existing caller's authority.
Run the independent no-Cargo smoke test:
& ./scripts/tests/test_cargo_shared_worktree_target.ps1It uses a disposable fake Cargo executable to validate arguments, shared path, dry-run behavior, foreign-repository rejection, error propagation and environment restoration. It never runs a real Cargo compile. Do not delete existing per-worktree targets automatically; operators should review disk usage and worktree ownership before any manual cleanup.
Plain cargo test must work without a special environment variable or wrapper,
including when launched from an interactive console. On high-core developer or shared
hosts, bash scripts/test_server.sh is an optional resource-control entry point for the
complete Server package lane. It runs the same package command as ordinary Linux CI
(cargo test --locked -p webcodex) but, when RUST_TEST_THREADS is unset, caps
libtest fan-out at the smaller of the detected logical CPU count and 32. Set
RUST_TEST_THREADS explicitly when intentionally testing another concurrency level.
Cargo -j / CARGO_BUILD_JOBS controls compilation, not libtest execution concurrency.
A passing limited-concurrency run does not prove a console-only failure was caused by
parallelism; keep the original failing entry point in the final regression evidence.
Cloudflare's isolated real Server/Runner/CLI acceptance harness is
python3 scripts/e2e_cloudflare_oauth.py --bin-dir target/debug; its required
native-TLS-root build and verified ephemeral-CA topology are documented in
Cloudflare connections.
Run cargo test --locked -p webcodex --lib cloudflare_transport -- --ignored --test-threads=1
for the new Unix process-tree/pipe/version cancellation fixtures. Real
Cloudflare/ChatGPT and unexecuted native platforms must be reported separately.
Ordinary cargo test and ordinary CI intentionally skip ignored timing/real-process
coverage. Run the smallest relevant group locally when changing one of these boundaries:
cargo test --locked -p webcodex-runner -p webcodex-lsp --features runner-real-process-tests runner_real_process -- --ignored --test-threads=1
cargo test --locked -p webcodex-process --test managed_child -- --ignored --test-threads=1
cargo test --locked -p webcodex-persistent-shell -- --ignored --test-threads=1
cargo test --locked -p webcodex --lib tool_runtime_real_process_ -- --ignored --test-threads=1
cargo test --locked -p webcodex-runner profile_preparation_obeys_total_job_deadline -- --ignored --test-threads=1
cargo test --locked --manifest-path apps/desktop/src-tauri/Cargo.toml desktop_real_process_windows_ -- --ignored --test-threads=1These commands are evidence for the boundary they exercise, not a routine pre-commit check. Prefer one exact ignored test while iterating, then the relevant group when the change is ready for review.
Before compiling Rust after changing model-facing tool guidance or its literal
test requirements, run python3 scripts/check_builtin_guidance_contract.py
(Windows: python). It reads the canonical Direct and Host guidance
contributors, the corresponding Rust test phrase arrays, and the Rust-owned
item-count/character limits. It fails closed on unexpected source layouts or
missing literal phrases; no Cargo command or network access is involved.
The ordinary Linux Server CI Rust shard runs this cheap preflight before the package compile. It catches straightforward guidance drift before the expensive Rust suite, but does not replace that suite or prove the dynamic standard startup payload is below 16 KiB. All existing Server, experimental Code Mode, permission, schema, and platform gates still run after a successful preflight. Changing a valid Rust guidance representation may require updating this intentionally strict static parser and its fixtures together.
Run python3 -m unittest scripts.tests.test_check_builtin_guidance_contract
for focused non-compiling negative cases. They cover over-budget guidance,
missing text required by Rust tests, malformed literals, and pre-Cargo CI
ordering. The verifier also reproduced three historical failed PR guidance
revisions in less than two milliseconds each, while accepting the corrected
revision; actual CI savings depend on where the failing build would have
stopped otherwise.
The lanes above define test semantics; workflows decide when to run them.
.github/workflows/ci.ymlis the ordinary repository gate. Its cheapchangesjob classifies the exact PR or merge-group base...head path set before native scheduling, while thecontractjob remains mandatory for every configured pull request, every merge-queue candidate, and every push tomain. The classifier is deterministic and local to Git: it does not use commit messages or PR titles, and it emits frontend, per-platform, and package-lane requirements. For changed Rust/Cargo files it searches only bounded platform-marker lines from both the base and head file versions, so body-only changes inside an existing platform cfg remain visible without serializing near-complete file diffs. If that marker scan exceeds its bound, CI fails closed to native core plus architecture compilation while preserving path-derived package/Desktop decisions; only an untrustworthy changed-path inventory falls back to the complete native matrix. The contract lane always owns workspace-boundary self-test/checks, formatting, the heuristic test-inventory self-test/report (without count thresholds), and focused registry/MCP schema and metadata parity. Main and Desktop frontend dependency installation/type/test/build steps run only when the classifier selects their respective frontend surface; full-native invocations select both.- The heavy Linux Rust matrix
test-linux-rustand Linux tooling lanetest-linux-toolingrun for every pull request, every merge-queue candidate, and every push tomain, including owner-authored PRs. They start in parallel withcontractrather than waiting for unrelated frontend/static work. Native child lanes likewise wait only for the cheapchangesclassifier, while the stable macOS/Windows/native aggregates retain the mandatorycontractgate. Merge-group candidates use the exact GitHub-provided synthetic base/head range and fetch the synthetic head by ref when needed; an unavailable merge-group diff fails closed to the complete native matrix. Pushes tomain, external-contributor PRs, and owner PRs carryingrun-cialso force the complete deterministic native matrix. Real-process and timing-sensitive ignored tests are deliberately outside ordinary CI, including full-native overrides: run them explicitly when changing their lifecycle boundary or investigating platform behavior. Computer, platform-specific, Desktop, npm, packaging, signing, and release surfaces retain their existing independent deterministic lanes. The stabletest-macos,test-windows, andtest-nativeaggregates always resolve and verify each child lane issuccesswhen required orskippedwhen not required, avoiding a skipped required-check context that could leave branch protection pending. The stabletestandtest-nativecontexts are also emitted formerge_group. GitHub currently exposes Merge Queue only for eligible organization-owned repositories, so this path remains dormant in this personal repository; it is retained as migration-ready CI support if the repository later moves to an eligible organization. - MCP dated-revision evidence has its own bounded
mcp-conformancelane. It pins and freshly builds the upstream referee, runs the2026-07-28and2025-11-25server requirements against a test-only loopback WebCodex endpoint, validates the report gate itself, and uploads raw reports on success or failure.test-nativerequires this lane to succeed, so missing per-scenario verdicts, abnormal/infrastructure runs, stale or changed classifications, and unclassified new failures are merge-blocking. SeeMCP_CONFORMANCE.mdfor baseline semantics. - v0.5 removes the Action adapter and its compatibility feature/lane. Canonical contract tests and HTTP negative tests verify that retired tool names and routes remain unavailable; MCP and Runtime API authority/provenance tests stay active.
- Linux Rust execution remains package-sharded: the server package
webcodex, the Runner/LSP packages, and the remaining workspace crates run in parallel. The Runner/LSP shard compiles with--features runner-real-process-teststo prevent bitrot while ordinary local runs skip compiling manual real-process test bodies; ordinary libtest execution does not execute ignored tests. The remainder shard uses--workspace --exclude webcodex --exclude webcodex-runner --exclude webcodex-lsp, so newly added workspace members enter CI automatically rather than depending on a hand-maintained package list. The split changes scheduling, not process-ownership coverage. - Linux tooling runs in parallel with the Rust shards and retains
release-verification tooling, Markdown-link validation, and npm package-smoke
tooling on every PR. It also runs the dependency-free MCP App DOM/message-order
tests with its existing Node installation, independently of frontend path
classification. The complete
cargo check --workspace --all-targetspass is reserved for pushes tomain, external-contributor PRs, and explicitrun-ciPRs; ordinary owner PRs already pay for the package-sharded Rust test compilation and do not repeat that broad compile-only pass. macOS and Windows native jobs compile Runner, LSP, and Computer with--features runner-real-process-testsand keep deterministic native coverage, but they do not execute ignored real-process groups. Process-tree, detached-supervisor, shell timeout/stop, PowerShell stdin EOF, fake-SSH lifecycle, selected Plugin shutdown, and similar OS-scheduling-sensitive coverage is retained as explicit local evidence. The local-sshdSSH integration fixture remains Linux-only and manual because it depends on Linux daemon account/auth configuration. - Exact-source release acceptance is a separate trust boundary from ordinary CI.
Every selected source first needs successful exact-source push CI. A
release/v*push additionally precomputes scarce-native, E2E/eval, and disposable Server-image evidence inrelease-readiness.yml; the later operator dispatch binds and revalidates those immutable run attempts rather than rebuilding them. Amain-source release keeps the slower dispatch-time evidence fallback, so ordinary main pushes stay cheap. Manual real-process evidence remains separate and must never be inferred from a passing CI run. FollowRELEASE_CHECKLIST.mdand.github/workflows/release-readiness.yml.
- No external network by default. Tests that need HTTP should use in-process clients or loopback mock servers. Real internet, real cloud services, and real deployment targets belong in explicitly named manual smoke workflows.
- Local mock server tests must be isolated. Bind to
127.0.0.1:0, avoid fixed ports, scope URL rewrites to the test fixture, reset global overrides even on failure paths, and stop spawned tasks when the fixture drops. - Prefer explicit config inputs or child-local
Command::env/env_removeover mutating the parallel test process's environment. Existing tests that must mutate process environment requireTEST_ENV_LOCKor an equivalent shared guard and panic-safe restoration of previous values. A lock used only by writers does not isolate unguarded readers or children inheriting temporary values; use an isolated child process when those readers cannot be controlled. Do not print token values while diagnosing these tests. - Tests that touch HTTP/auth behavior must use
AuthEnvGuardor an equivalentTEST_ENV_LOCKguard for auth mode env, especiallyWEBCODEX_SHARED_KEY_ENABLED,WEBCODEX_ALLOW_ANONYMOUS, andWEBCODEX_OAUTH2_SHARED_KEY_BRIDGE. Managed-token rejection tests should explicitly disable direct shared-key fallback and open anonymous mode before asserting that an unknown or wrong bearer returns 401. - Keep the auth-mode semantics separate in tests:
WEBCODEX_SHARED_KEY_ENABLEDis direct Bearer shared-key fallback, whileWEBCODEX_OAUTH2_SHARED_KEY_BRIDGEis only the OAuth authorize bridge. Quick-start shared-key mode intentionally accepts an unknown non-wc_Bearer as a lightweight shared-key principal, but invalidwc_managed-token prefixes and empty or whitespace Bearer values must still be rejected. - Sleep, timeout, and polling tests must be bounded. Positive readiness uses one
absolute deadline created once for the whole wait and never reset after partial
progress; prefer channels, notifications, or direct state inspection. A test whose
correctness materially depends on real process startup, scheduler timing, a long
sleep, or a wall-clock timeout belongs in ignored explicit evidence rather than the
default suite. Short deterministic negative probes, semantic grace windows, and
exact count/protocol iterations may remain when they are the contract.
Async Runner shutdown waits are notification-driven by
ShutdownCoordinator; tests should signal that state directly rather than sleep for a presumed polling interval. - Ignored tests are not dead tests. Each ignored test should have a reason and a
documented command for running it intentionally. Ordinary CI never opts into
--ignored:runner_real_process_,desktop_real_process_windows_, slow timing regressions, and real Codex/LSP dogfood remain explicit local/operator evidence.
The local Runner fixture is an adapter, not a second execution contract. Keep its
stdin, output capture, exit-status, deadline, and cleanup behavior aligned with the
production Runner. In particular, Command::spawn() inherits stdin by default whereas
Command::output() closes it by default; switching between them requires an explicit
I/O decision. In the common test helper:
Nonemust configureStdio::null(), overriding any preconfigured stdin.Some(b"")must also produce EOF, while a nonempty payload supplies exactly its bytes followed by EOF. No ordinary fixture reads the developer's terminal.- Capture both output streams without waiting on undrained bounded pipes, preserve exit status, and retain bounded waits plus cleanup of fixture-owned processes. Explicit input larger than pipe capacity must not deadlock the harness either.
- Keep cwd, temporary repositories, child environment, and input handles local to the fixture. Do not change process-global stdin or environment to simulate a console.
src/tool_runtime/tests/runner_fixtures.rs provides ordinary deterministic coverage
for missing, empty, and large explicit input, plus a subprocess regression of the
existing unborn-repository workflow. That test re-executes one exact libtest case with
finite invalid parent input: an incorrect fixture lets git mktree consume the input
and fail immediately, even in headless CI. It verifies that the child actually runs
one test. With an interactive console or an open pipe, the same defect instead waits
for EOF and surfaces as a misleading Runner command timeout. These tests assert I/O
semantics, not scheduler latency, so they belong in the ordinary lane:
cargo test --locked -p webcodex --lib runner_fixturesFor a newly reported console-only or cross-suite failure, use this sequence:
- Record the exact revision, command, test target, executed count, input mode, and
relevant concurrency settings. Separate compile time from test execution time.
Compare the same case with closed stdin and with a parent-owned pipe whose writer
stays open. Do not use
communicate()for that probe: it closes the child's stdin and can hide an EOF bug. Bound the probe and clean up only its owned processes. - Make the smallest deterministic regression fail on the old implementation before changing the helper. Use finite poisoned input or controlled local state where possible, rather than making every CI run wait for the original timeout.
- After the fix, run the focused regression and repeat the original failing mode.
For changes to a shared fixture or cross-suite behavior, also run the affected
package at default concurrency; a filtered/serial pass alone is insufficient.
Let Cargo select/build the current test binary rather than guessing a potentially
stale executable under
target/.
As related test areas are changed, improve them incrementally: move pure projection, parsing, and policy assertions out of real Git/shell workflows; retain a small set of real process-boundary contract tests; inject settings rather than adding new global environment guards; and replace scheduler sleeps with owned readiness signals and one absolute deadline. Keep actual lifecycle/timing evidence in the existing explicit lanes. Do not weaken assertions, ignore a deterministic failure, extend deadlines, or serialize the entire suite merely to obtain a green run.
Conversation-import tests use bounded loopback fixtures in the ordinary local-integration surface. Legacy ignored HTTP fixtures for redirect and download-size limits were retired after equivalent boundary coverage moved to the current MCP import path; source-name fallback is covered directly at the import-name helper. Keep new coverage on the current transport/runtime path instead of preserving duplicate historical fixtures.
Run the current heuristic inventory with:
bash scripts/test_inventory.shThe script is intentionally heuristic. It scans all Git-tracked Rust files across
the workspace, so crate-local tests (including Runner tests) are included. Using
the Git index as the source set excludes ordinary untracked target/ output and
scratch files without maintaining a second ignore list. It does not access the
network or modify the workspace. The output includes a
stable tab-separated area summary for the root webcodex package and each
crates/* member, plus sanitized risk clues. Use
bash scripts/test_inventory.sh --details for a full sanitized file/line list,
and bash scripts/test_inventory.sh --self-test to exercise the inventory
contract against a temporary Git fixture.
Recent structure work moved large test groups out of production roots:
- OAuth HTTP endpoint tests are rooted at
src/oauth_http/tests.rsand grouped by endpoint/domain undersrc/oauth_http/tests/*. - CLI tests are grouped under
crates/webcodex-cli/src/webcodex_cli/tests/*. - CLI help smoke coverage lives with the CLI test modules and covers common help entry points, so new command help should extend that smoke coverage.
- Runtime HTTP tests live under
src/runtime_http/tests/*; historical ignored import fixtures should not be retained once equivalent current-path coverage exists. - Tool runtime tests live under
src/tool_runtime/tests/*by domain.
Do not add large ordinary test blocks to production facade files when one of
these tests/ module trees already exists. Exact full-suite pass counts should
come from a fresh cargo test -p webcodex --lib run; this document should not be
treated as the source of truth for exact counts.
Run python scripts/tests/test_runner_observability_contract.py for the exact-name
projection guard used by CI. It covers observation producers and readers while
leaving Runner registration wire keys and durable Agent APIs unchanged. Behavior
coverage lives in the metadata, runtime_http, runtime_console_http,
admin_http, runner_capabilities, and startup_runner_tests Server filters,
and the CLI ops and server::status filters. Run
pwsh -NoProfile -File scripts/test_windows_runner_readiness.ps1 for Windows
readiness parsing. These checks do not replace Linux socket-activation E2E.