Skip to content
published Visibility internal Owner erik@uvilo.com Approver _ Created _ Updated _

Multi-test cases

Some tools need more than one call to surface bugs that a single smoke test misses. Log each variant as its own report row (Tool (variant) in the Notes column).

When to multi-test

SignalAction
Tool accepts nested objects or arraysRun scalar-only call, then nested-object/array call (catches JSON-string serialization bugs)
Tool has optional relation fields (spaceId, childPrompts, variables)Run minimal body, then body with each optional field populated
List/search returned count=0 on first passRe-run after creating a sandbox entity with sandboxPrefix in name/handle/title
Mutating MCP tool has a paired read/restore toolRun full cycle (create → mutate → verify → restore) under tempDir
Agent tool has sync and async modesRun both; pair async with checkAgentJob / abortAgent

Mandatory invocations (not optional multi-test)

These were often loaded but not called in past runs. Include them in every full audit:

ToolWhen / how
readTodoList, readTodoItemAfter createTodoList / createTodoItem succeed
createTodoItem, updateTodoItemAfter createTodoList; complete todo CRUD chain
trashConvo, restoreConvoOldest non-trashed convo in DB (not current convo); trash then restore
trashPrompt, trashTodoList, trashTodoItemAfter creating prefixed sandbox entity; expect policy deny on production bots
forge-filesystem__trash_fileWrite temp file, trash it, verify in list_trash
forge-filesystem__untrash_file, untrash_directoryAfter trash step in cycle below
forge-discovery__get_projectAfter other discovery tools pass (full file payload vs phase-only)
playwright__* (all in ToolSet except skipped)One browser session per multi-test-cases; log each tool separately

Skip switchAgent unless continuation bot is configured (see SKILL.md).

Tool-specific variants

Native search / find (search*, find*)

VariantWhyHow
Pre-create emptyBaselineQuery with sandboxPrefix; assert count=0 and matching array length (native-readonly worker)
Post-create matchFilter + non-empty pathAfter creates in native-mutating worker, query same prefix; assert count≥1 and ids match created entities

Native create (createBot, createPrompt, createTodoList, createTodoItem)

ToolVariantWhyHow
createBotMinimalRelation wiringName, handle, ownerId only
createBotWith childPromptsNested create + Prisma delegateOne child prompt in array
createPromptWith variablesArray field on create{ name, type } variable entry
createPromptPublished vs draftpublishedAt branchOnce with publishedAt, once without
createTodoListMinimalUnblocks todo chainPrefix title only
createTodoItemLinkedForeign key connectUse list id from prior create

Native update (updateBot, updatePrompt, updateTodoList, updateBotPrompts, updateTodoItem, updateConvo)

ToolVariantWhyHow
updateBotScalarBaselineChange description only
updateBotNested JSONStringify risk on systemPromptValues, outputSchemaPass object values for those fields
updatePromptScalarBaselineChange description
updatePromptNestedStringify riskUpdate variables array or childPrompts
updateTodoListScalar vs nestedStringify risk on todoList bodyDescription only, then nested object
updateBotPromptsEmpty vs populatedArray stringify + clear-allchildPrompts: [], then one child
updateTodoItemScalarComplete todo chainChange title on sandbox item

Convo trash / restore (trashConvo, restoreConvo)

Do not create a sandbox convo for this cycle. Exception to the sandbox-prefix rule.

StepWhyHow
FindNeed an existing convosearchConvos (or findConvos) sorted by createdAt ascending; skip trashed rows and the current convo id; pick the oldest match
TrashSoft-delete pathtrashConvo on that id; verify convo no longer appears in default (non-trashed) search
RestoreUndo without data lossrestoreConvo on the same id; verify convo is searchable again

Log each step as its own row (trashConvo, restoreConvo). If no eligible convo exists, log both as SKIP with reason.

forge-filesystem (under tempDir, same workspace_root throughout)

VariantWhyHow
Trash cyclePath root mismatch between trash and listwrite_filetrash_file or trash_directorylist_trash (must list item) → untrash_*read_text_file confirms restore
empty_trashDestructive; skipped if never calledOnly after dedicated trash-only temp files; never on unrelated trash

forge-discovery

VariantWhyHow
List toolsMCP reconnect after idlelist_departments with small org; retry once on closed client
get_project_phase vs get_projectDifferent payload shapesUse department + project from a row returned earlier by list_projects or list_dept_projects in the same run — never infer from skill or sandbox names

forge-typesense

VariantWhyHow
Short queryBaseline search1–2 word query, limit: 3
Semantic queryRanking / longer inputFull sentence query
get_file_summaryPath-specific indexKnown indexed path from repo (e.g. AGENTS.md)

forge-bash (run)

VariantWhyHow
SimpleBaselinepwd, ls, echo
PipelineShell parsing / stdoutecho tool-test | wc -c or ls | head -3

Playwright (playwright__*)

Always run when Playwright is in the ToolSet. Use one session; log each tool as its own report row. Safe URLs only — never Ceres, login pages, or operator sites.

Always skip (log SKIP): browser_screenshot, browser_pdf_save.

PhaseTools (typical names)How
Openbrowser_navigateabout:blank, then https://example.com
Observebrowser_snapshot, browser_console_messages, browser_network_requestsAfter example.com load; assert snapshot has content
Navigatebrowser_navigate_back, browser_navigate_forward, browser_reloadBack to blank, forward to example.com, reload
Interactbrowser_click, browser_hover, browser_type, browser_press_key, browser_select_option, browser_check, browser_uncheck, browser_fill_form, browser_dragUse refs from snapshot on example.com (e.g. link, heading); minimal text only
Tabs / waitbrowser_tabs, browser_wait_forOpen tab, switch, close extra tab; wait for visible text
Layoutbrowser_resizeSmall viewport (e.g. 800×600)
Scriptbrowser_evaluateHarmless expression only (e.g. document.title); skip browser_run_code_unsafe unless ToolSet includes it
Dialog / uploadbrowser_handle_dialog, browser_file_uploadOnly if tool is in ToolSet; use temp file under tempDir for upload
Closebrowser_closeAlways end session

Retry once on MCP closed-client errors (same as other stdio servers). If a tool is absent from the ToolSet catalog, log SKIP with reason — do not fail the whole Playwright group.

Agent meta-tools

Tested in the agent-meta worker group only. The orchestrator already exercises spawnAgent async for each tool group — workers do not re-spawn group workers.

ToolVariantWhyHow
spawnAgentSyncBaselineMinimal bot, message Reply OK
checkAgentJobCompleted jobPoll pathPoll a sync job until terminal
abortAgentActive jobStop pathAsync spawn → checkAgentJobabortAgent

artifacts

VariantWhyHow
Inline markdownBaselinetype: text/markdown, panel display
markdown-fileFilesystem artifact pathWrite under tempDir, reference path; trash after