Agent testing workflow research
Scope and method#
The audit was performed against v0.2.0-alpha.1 (02ee4e87) on 25 July
2026. The worktree was clean and detached before the investigation began.
Names such as FileTaskStore below describe that audited revision. Current
persistence ownership is documented in
docs/architecture/PERSISTENCE_ARCHITECTURE.md.
The repository instructions and the required product, runtime, preview, workflow, protocol, seeding, provider-smoke, and design documents were read before implementation. The audit also covered test configuration, CI, renderer tests, Electron and browser-development boundaries, the task service, agent orchestration, ACP and Codex adapters, worktree creation, preview lifecycle, test support, provider smoke tests, and Git history.
Verified current state#
Test layers that already work#
- Vitest is the fast default. Node tests cover core, storage, Git, process supervision, runtime adapters, protocol mapping, workflow projection, transport, and most renderer model logic. JSDOM renderer tests run through a separate configuration.
- CI runs architecture and protocol checks, type checking, Node and renderer tests, the build, package verification, and packaged-runtime smoke checks on macOS, Linux, and Windows.
src/testSupport/taskMonkiScenario.tscreates a real temporary Git repository,FileTaskStore,TaskManagerService, and worktree root. Many service tests use it successfully.- That helper injects
ScriptedAgentRuntimeAdapter. The adapter writes provider-like state directly toFileTaskStore; it does not launch a provider subprocess, negotiate a protocol, stream stdio, or prove process shutdown. - ACP adapter tests already contain the important lower-level mechanism that
the missing workflow needs: a Node child implementing ACP over stdio, driven
through the production
AcpRuntimeAdapterandAcpStdioSupervisor. - The development HTTP server exposes the renderer's typed
TaskManagerServiceoperations.GET /api/boardreturns compact board and Inbox truth, whileGET /api/tasks/:taskIdreturns fresh selected-task detail; mutations such as task creation, worktree preparation, run start/cancel/retry/continue, review, and evidence refresh use the same service methods as the app. - The HTTP surface is loopback-only, token-protected, host/origin/fetch-site checked, and intentionally prevents browser-development agent turns from escaping its network boundary.
- Renderer DOM tests are appropriate for component behavior and selectors, but they do not start Electron, Git, a provider process, or a real worktree.
Seeds#
npm run dev:seedbuilds 87 stable scenarios for renderer and workflow inspection. Scenario slugs make visual states easy to find.- The generated data exercises real store APIs while being created, but the resulting application is projected state. It deliberately disables provider startup in browser development.
- A semantic browser interaction against
[seed:delivery-checks-failed]successfully opened the task and displayed the expected failed PR checks. This proves the seed is valuable for visual state inspection, not provider lifecycle. - From an installed workspace, a full
npm run dev:seedtook 49.76 s (19.60 suser,15.43 ssystem) on the audit machine. Before dependencies were installed it failed immediately becausetscwas unavailable. - Starting the seeded API and renderer required two long-lived commands. A sandboxed loopback bind failed until explicitly allowed. One API start attempted preview reconciliation and shutdown later reported four preview generations with unverified cleanup residue. The renderer also emitted a proxy error when the API stopped first.
These observations are evidence that seeds should stay focused on broad, stable UI states rather than becoming the common agent execution harness.
Provider smoke testing#
npm run smoke:providersis explicitly opt-in. It requires both--confirm-throwawayand--confirm-provider-usage, a clean caller-created repository with a commit and no remotes, and a real installed provider.- It uses the real
TaskManagerService, model discovery, runtime adapter, sentinel output, worktree Git inspection, and model-selection attestation. It rejects unresolved interactions and checks that the source repository was unchanged. - It intentionally retains its state root, worktrees, branches, protocol evidence, and report for diagnosis. It does not create the throwaway repository or clean it up.
- Its tests cover prerequisites, timeout/cancel behavior, provider claims versus Git evidence, model attestation, and report authority.
This is the correct real-provider boundary, but its cost, credentials, network dependency, and retained state make it unsuitable as the default post-change loop.
During implementation verification, the installed authenticated Codex runtime
was first cataloged without executing a model, then one explicitly selected
model (codex:gpt-5.4-mini) passed the existing smoke in 31.91 s wall time.
The authoritative report recorded provider-confirmed selection, a completed
run, verified sentinel output in the worktree, and an unchanged clean source
repository. That is deliberately separate from deterministic runtime proof.
The proof cleanup removed its throwaway live repository, smoke state, and Codex
rollout. Codex 0.144.6 then returned a state-database cleanup error because the
local database lacks its expected agent_jobs table. Exact-CWD thread listing
returns no session, but Codex still logs that it dropped a stale database row
from the result. The user-owned Codex database was deliberately not edited
directly; that stale metadata row is an external CLI cleanup defect, not Task
Monki test evidence.
Git and worktree behavior#
TaskManagerService.prepareWorktree()calls the productionWorktreeService, records the iteration/worktree first, creates the real Git worktree, verifies it, and refreshes Git evidence.- Post-run evidence is independently observed after terminal provider events. Provider output is telemetry; it does not establish that a file changed.
- Existing test helpers create local repositories, but there was no reusable developer command that also creates a local bare remote, executes a provider subprocess, reports structured workflow state, and owns cleanup.
History#
Git history did not contain a removed, proven Task Monki agent-testing driver to restore.
- The initial browser-development work ignored
.playwright-mcp/but did not contain a Playwright dependency or application E2E suite. - A removed showcase capture script used headless Chromium and careful screenshot/process cleanup. It was coupled to legacy showcase data and Remotion, not to current Task Monki workflow execution.
- An unmerged historical provider lifecycle smoke covered paid-provider cancel/retry/continue cases. It never became the product baseline.
- The current scripted scenario helper predates the latest refactors; it has never exercised provider process supervision.
External implementations and transferable lessons#
The sources below are current primary documentation or source code. The useful pattern is consistent: keep a fast deterministic boundary, run real processes where process behavior is the subject, isolate filesystem/user state, use semantic UI control sparingly, and separate live external-service checks.
Electron, Playwright, and WebdriverIO#
- Electron's automated testing guide presents WebdriverIO and Playwright rather than prescribing an Electron-owned framework.
- Playwright's Electron API is explicitly experimental. It can launch an app, evaluate in the main process, obtain the first window, interact, and capture screenshots. Native dialogs still need main-process substitution.
- Playwright recommends role, label, and other user-facing locators with auto-waiting instead of coordinate or DOM-structure selectors. Its trace viewer retains action, DOM, console, error, and network context for failures.
- WebdriverIO's Electron service adds Electron binary discovery, API mocking, main/renderer log capture, window tracking, and headless support, but requires a second substantial runner stack.
Transfer: use semantic browser interaction and screenshots when rendering, focus, accessibility, or actual interaction is the requirement. Do not add a second browser stack to validate domain workflows already exposed through the service boundary.
VS Code#
- VS Code keeps unit tests in its fast contributor loop and has a separate smoke suite for Electron and web builds.
- The smoke guide calls out shared state, global ports/IPC handles, focus, timing, and arbitrary sleeps as specific flake risks. Tests wait for the DOM or active element instead of hoping that a UI transition finished.
- Release smoke uses separate user-data and extension directories and a known repository, as described in the official smoke-test workflow.
Transfer: Task Monki needs isolated state roots and semantic waits. A full Electron smoke suite is valuable only for Electron-specific boundaries; it is too costly for every agent-domain change.
GitHub Desktop#
- GitHub Desktop's current Playwright Electron fixtures launch one app per worker, replace user/config directories, disable SSH, capture console/page errors, record traces/video, close the app, and join owned services.
- Its test repository helper deletes only a known temp path, initializes a repository, configures a test identity, commits known content, and independently queries Git status, branch, and commit message.
Transfer: exact temp ownership and independent Git assertions belong in Task Monki's runner. Launching Electron is not necessary to prove service, worktree, provider, and projection behavior.
Codex#
- Codex app-server integration tests launch a real child and communicate over
its production stdio protocol through
TestAppServer. The harness owns a temporaryCODEX_HOME, time-bounded reads, structured JSON logs, and graceful shutdown. - The same suite substitutes the paid model boundary with a local ordered SSE
server in
mock_model_server.rs, including exact expected call counts.
Transfer: the deterministic substitute should sit behind the real subprocess and protocol boundary. It must not be described as proof of OpenAI or another provider service.
Cline#
- Cline's
headless tests
execute the CLI as a child and assert visible output and exit status.
JSON/headless fixtures provide repeatability, while provider-dependent cases
are tagged
@live. - Its live persisted-message contract uses a fresh temporary session-data directory and checks structured artifacts independently of terminal text.
Transfer: one structured command is cheaper for coding agents than repeatedly rediscovering the UI, while separately named live checks preserve honesty.
Zed#
- Zed has fake-filesystem worktree coverage in
fake_git_repo_tests.rsand shared agent-server E2E behavior ine2e_tests.rs, including message, tool, permission, and cancellation behavior.
Transfer: reusable behavioral contracts are useful, but Task Monki must also run real local Git because Git observation is one of its authoritative product boundaries.
Candidate comparison#
Scores are relative to Task Monki's common post-change loop: high is favorable for speed, coverage, and diagnostics; low is favorable only in the maintenance-cost column.
| Candidate | Speed | Real service/Git/process coverage | UI | Live provider | Diagnostic quality | Maintenance cost | Decision |
|---|---|---|---|---|---|---|---|
| Extend seeds | Low setup; fast once running | Low | High for projected states | None | Medium | Medium/high if distorted | Preserve, do not extend into execution |
| Developer CLI/scenario command | High | High | Opens a URL when needed | Can delegate to existing smoke | High structured output | Low/medium | Selected |
| MCP server | Medium | Same as backing API | None by itself | Same as backing API | High | High: service lifecycle and schema | Reject now |
| Existing HTTP API alone | Medium/manual startup | High once composed correctly | High | Possible but unsafe in browser dev | High | Low | Reuse under the command for UI |
| New test-only driver API | High | Risks divergent behavior | Optional | None | High | High | Reject |
| Playwright browser build | Medium | Service path only if backend is real | High | None | High with traces | Medium plus browser install | Defer as a small UI suite |
| Playwright/WDIO Electron | Low for inner loop | Highest Electron coverage | Highest | None | High | High | Defer to Electron-specific needs |
| Renderer DOM tests | Highest | None outside renderer | Medium | None | High | Existing | Keep |
| In-process fake adapter | Highest | Misses protocol/process boundary | None | None | Medium | Existing | Keep for focused service tests |
| Recorded provider messages | High | Mapper/replay only; timing can drift | None | None | Medium | Fixture drift | Use only for protocol regressions |
| Local deterministic ACP subprocess | High | High through current production adapter | None | Explicitly not live | High journals/state | Low because ACP support exists | Selected |
| Opt-in provider smoke | Low/costly | Highest provider coverage | None | Yes | High, retained report | Existing | Keep separate |
| Temp repo + local bare remote factory | High | Real local Git and upstream state | None | None | High | Low | Selected inside runner |
Why MCP is not selected#
The current problem is repeatable verification, not an ongoing multi-client
conversation with Task Monki. MCP would need process startup, tool schemas,
state and cleanup semantics, authentication, and another compatibility
surface. Its tools would ultimately call the same TaskManagerService methods
already reachable from a synchronous Node command and the development HTTP
API. It adds no present coverage.
MCP should be reconsidered only if coding agents need to attach to and control an already-running user-owned Task Monki instance across many independent interactive turns. That would be a product capability with a new security boundary, not a test helper.
Conclusion#
The smallest useful addition is a developer scenario command that:
- owns a temporary root, real repository, local bare remote, store, and worktree root;
- injects the production
AcpRuntimeAdapterconnected to a deterministic local ACP child; - runs representative complete, failed, and canceled workflows through
TaskManagerService; - inspects authoritative snapshots, Git evidence, provider journals, and runtime activity;
- prints a structured report and joins/removes everything it owns; and
- optionally serves that exact completed environment through the existing authenticated development HTTP/Vite path for semantic browser interaction.
This fills the missing middle layer without changing product behavior. Seeds, Vitest/JSDOM, browser interaction, Electron-specific testing, and real-provider smoke each retain a clear boundary.
Assumptions and open questions#
- Verified after implementation: the deterministic workflow, UI
interaction, failure diagnostic, timings, and exact cleanup proof are
recorded in
STRATEGY.md. - Assumption: three representative outcomes are enough to establish the common pattern. More scenarios should be added only when a real regression cannot be expressed by existing focused tests plus these outcomes.
- Deferred question: whether repeated Electron-only regressions justify a very small Playwright Electron suite. No current failure evidence justifies that dependency and startup cost.
- Deferred question: whether provider-smoke should eventually create and remove its own throwaway repository. Its intentional evidence retention and explicit paid-provider safety contract require a separate design decision.