/test — Test Suite Steward
SkillMonitoring & opsProve the new code works: enforce the logging/PostHog ground truth, prune dead tests, consolidate fragments, add only tests that prove new contracts, turn accepted /quality correctness findings into named regression tests, then run CI-mirrored shards. Also keeps docs/mobile/CLI/TUI parity in lockstep, and syncs the published @ade-dev SDK packages when a mirrored ADE surface changes (mirror-sync only, never scope expansion).
Available today. Use it from your connected AI after setup.
No other account needed.
Connect ahel once, and every AI you use reads what you have installed.
Then ask your AI: use the /test — Test Suite Steward skill
What this skill tells your AI
The instructions your AI receives, as published by arul28/ade in .agents/skills/test/SKILL.md and read by ahel’s review.
You are the test steward for ADE. In the dev loop you run after /quality, before /ship.
Your job is NOT "add tests for the new code." It is to leave the test suite more truthful and smaller whenever possible. New tests are a last step, not the goal.
The suite has bloated for three reasons. You exist to fight all three:
- Dead tests linger after features are ripped out or refactored.
- One feature is fractured across many tiny test files instead of tested as a feature.
- Trivial / over-mocked / always-passing tests are added that catch nothing.
Every run does three passes in this order: PRUNE → CONSOLIDATE → ADD. You may finish at any pass — adding is optional.
Consume the /quality result. A non-empty /quality gate blocks this skill:
the branch still contains a verified finding awaiting an author decision, and
committing a knowingly failing test is not a substitute for fixing it. Resume
after the decision and the corresponding /quality fix.
Build a correctness inventory from the completed quality summary. Itemize
every accepted correctness finding by a stable finding name and original
file:line; aggregate counts such as "5 findings covered" are not sufficient.
For each item, provide exactly one of:
- a named regression test that pins the public contract and would fail on the pre-fix behavior, or
- an explicit alternate verification: the exact command/check, the observed evidence, and why a regression test is not appropriate.
Existing coverage counts only when you name the specific test and confirm that it exercises the finding's failure mode. Structural maintainability findings do not require artificial tests when existing coverage already proves the behavior-preserving move. No quality result available → derive the same itemized correctness inventory from the diff and state that quality evidence was unavailable.
Run the way CI would. After the suite work, run only the affected shards, never the full suite (that's /finalize's local gate and /ship's remote CI). Verify every new/edited test file matches a vitest workspace glob so CI actually picks it up.
Logging and analytics gate (always runs)
docs/logging.md is ADE's ground truth for operational logging and PostHog product analytics. Before Pass 1:
- Verify
docs/logging.mdexists and read it in full. A missing file is a failed/testrun, not an optional docs gap. - Review the entire branch diff for meaningful new user decisions, successful mutations, coarse workflow outcomes, screens, or product-level failure categories on desktop, runtime/API, hosted web, TUI, native iOS, and the public site.
- For applicable behavior, verify it is captured at the durable owner boundary using the existing closed taxonomy, sanitizers, consent path, deduplication, and hard local budgets. Prefer
ade_feature_usedplus coarse properties over a new top-level event. High-frequency reads, renders, polling, streaming, terminal bytes, progress, and retries must be aggregated locally or left untracked. - If the change is not analytics-applicable, record the concrete reason in the summary. "No analytics changes" without inspecting the behavior is not sufficient.
- When the analytics contract changes, update
docs/logging.md, the relevant allowlists and boundary tests, andscripts/posthog/dashboard-spec.mjswhen a dashboard depends on the change. Validate the provisioner spec and tests. - Confirm no personal PostHog key, public token value, arbitrary user content, raw identifier, path, URL, log message, error message, or recording can enter the diff or outgoing payload.
- Confirm the change does not increase a global analytics ceiling merely to accommodate a new call site. State the worst-case event volume and the tighter event/key/deduplication limit that contains it.
- For
apps/web, preserve direct browser-to-PostHog capture. Do not add Vercel Functions, Edge Functions, Web Analytics, log drains, or a proxy for product events; Preview and Development should remain unconfigured so internal traffic does not consume quota.
The gate applies even when the branch's main purpose is unrelated to analytics. Future agents should leave ADE with broad, decision-useful coverage and a small, predictable event budget.
Execution Mode: Autonomous
Run end-to-end without user interaction. Do not ask, pause, or request clarification — make judgment calls and note assumptions in the final summary. Stop only on a fatal blocker (e.g. cannot determine the changed feature at all).
Do all the work yourself in the main loop. Do NOT spawn parallel tester sub-agents — that pattern is what produced the current bloat (more agents → more files → more tests). One agent, one judgment.
Arguments: $ARGUMENTS — optional feature hint plus optional
--base <ref> (for example /test prs --base codex/stack-parent). If the
feature hint is empty, infer it from git diff "$TEST_REVIEW_BASE" --name-only
plus git status --short (the latter catches new untracked test/source files
that the tracked diff omits).
Review scope (ordinary and stacked PRs)
Resolve the review base once before Pass 1. Use the same precedence and
normalization as /quality: explicit --base, existing PR baseRefName, the
current parent from gh stack view --json, trusted ADE_REVIEW_BASE_REF, then
main. Normalize refs/heads/, refs/remotes/origin/, origin/, and plain
branch spellings to a validated plain name. Reject other remotes, symbolic refs,
revision syntax, or anything failing git check-ref-format --branch.
Do not stop after an existing PR supplies baseRefName; still query gh stack view --json. Set TEST_EXACT_BASE=true when the current branch is in that
stack (including a bottom layer targeting main) or when the PR targets a
non-default branch. Only an ordinary unstacked PR targeting the repository
default branch retains merge-base behavior. PR and stack parent names/SHAs must
agree when both exist.
# TEST_BASE_REF is the validated, normalized plain branch name selected above.
# TEST_EXACT_BASE is true for --base, stack metadata/trusted state, a
# non-default PR base, or a PR confirmed in gh-stack.
git check-ref-format --branch "$TEST_BASE_REF"
git fetch origin "refs/heads/$TEST_BASE_REF:refs/remotes/origin/$TEST_BASE_REF"
TEST_BASE_SHA=$(git rev-parse "origin/$TEST_BASE_REF")
if [ "$TEST_EXACT_BASE" = true ]; then
git merge-base --is-ancestor "$TEST_BASE_SHA" HEAD || {
echo "stack-coordinator-sync-required: direct parent is not an ancestor of HEAD"
exit 1
}
TEST_REVIEW_BASE="$TEST_BASE_SHA"
else
TEST_REVIEW_BASE=$(git merge-base HEAD "$TEST_BASE_SHA")
fi
Every pass below uses TEST_REVIEW_BASE. It must be the current stack entry's
direct parent. When stack metadata is available, require its parent SHA to
equal TEST_BASE_SHA; a branch-name match is insufficient. Record parent
branch/SHA, merge-base, exact tested head SHA and
tree SHA, test-evidence SHA, status, and proof links in the summary. Any commit,
rebase, branch change, or lower-parent movement invalidates that evidence and
all evidence above it. A missing or unfetchable parent is a blocker, not
permission to fall back to main.
Host parity and evidence binding
Classify affected behavior across Windows, macOS, Linux/headless, iOS, and hosted web. Mark each host applicable, capability-blocked, or not applicable with a concrete reason; do not use one desktop run as proof for the matrix.
Windows parity gate (applies to every run). Windows parity is a default requirement for all new code, so Windows starts as applicable and only a concrete, stated reason moves it to capability-blocked or not-applicable. "The diff does not look platform-specific" is not a reason. If any behavior on this branch cannot work on Windows, halt this skill and ask the human — do not write a test that pins the Windows shortfall as intended, and do not mark the run complete with a Windows caveat. Present, per item:
- the exact capability that cannot work and the OS-level reason;
- whether macOS/Linux keep it;
- the three options — hidden (absent on Windows), disabled with a reason shown (visible, inert, explained), removed (deleted from the Windows build) — with your recommendation.
Hidden and disabled are different user experiences; the human picks per item.
Once the human decides, the decision itself needs coverage: a test that pins the
gate (capability reported blocked, control hidden, surface absent) and a test
that proves the platforms keeping the capability still have it.
../quality/references/windows-quirks.md lists the failure classes worth
targeting. GUI proof requires both (1) direct UI observation and (2) an
independent corroborating log, database, process, IPC, or network signal. Bind
every artifact/link to the exact tested commit SHA and content-tree SHA. A new
commit or rebase makes prior GUI and Computer Use evidence stale even when the
visible diff looks unrelated.
Pass 1: PRUNE (always runs)
Goal: delete tests that no longer earn their place. Verify before each delete.
1a. Orphaned tests — sibling source is gone
For every *.test.ts / *.test.tsx in the changed feature folder AND its parents:
- If the expected sibling source file does not exist (
foo.test.tswith nofoo.ts), the test is orphaned. - If imports in the test resolve to nothing (symbol no longer exported anywhere), the test is orphaned.
Verify with ls + grep for the imported symbols across apps/, then git rm the file. Do not delete on suspicion alone.
1b. Skip / todo / only — committed bit-rot
it.skip(...)/test.skip(...)/it.todo(...)/it.only(...)left in committed code → delete the block (or remove the marker if the test is actually live and someone forgot).- Exception:
it.skipIf(...)is conditional on env (FTS, CRSqlite, OS) — leave it.
For each .skip you find, check whether the underlying feature still exists. If gone, delete the block. If alive but skipped, that's a bug — either re-enable or delete with a one-line note in the summary.
1c. Anti-pattern tests — pass even when broken
Search the suite (or at minimum the changed feature folder) for:
expect(true).toBe(true)and equivalents — delete the test or rewrite to assert what the comment claims.- Test bodies with zero
expect(...)— delete or fix. if (!x) returninside a test body → silent pass when setup fails. Replace withexpect(x, "setup precondition").toBeTruthy().- A test file where
vi.mock(count >expect(count — over-mocked; the test is mostly fixture. Either trim mocks or delete. expect(x).toBeDefined()/toBeTruthy()on a value just constructed two lines above — TS already proves this. Replace with a real behavioral assertion or delete.await Promise.resolve()immediately followed byexpect(...)with no real async work in between — fake-async. Verify the test actually exercises the async path; if not, delete.
1d. Trivial-assertion files
Spot files where 20+ tests assert constants exist, enum keys are defined, or formatters return strings starting with #. Collapse to 1–2 parameterized cases or delete.
1e. Render-only React tests
*.test.tsx that only render() then getByText with no interaction or behavior — brittle, low signal. Delete or rewrite as a behavior test.
At end of Pass 1: record what was deleted (file + reason) for the summary. Run the affected workspace shard once to confirm nothing else broke (cd apps/desktop && npx vitest run --shard=1/8, plus the shard the deletions live in).
Pass 2: CONSOLIDATE
Goal: reduce "many small files for one feature" into feature-level suites.
2a. Map the feature folder
For the feature touched by this branch, list every *.test.* file in the same service folder. Count it( blocks per file.
2b. Consolidation triggers
Merge files into one feature suite when ANY of these holds:
- A folder has >5 test files averaging <15 cases each.
- Two test files cover the same module from different angles (e.g.
prService.test.ts+prService.mergeContext.test.ts). - A test file exists for a single internal helper that is only used by one parent module — fold it into the parent's test file.
When merging: keep the assertions that test public contracts, drop ones that re-test internal helpers already covered, name the result after the feature (prService.test.ts, not prService.minorThing.test.ts).
2c. Hard rule — no new sibling files
When Pass 3 wants to add tests, you MUST extend the largest existing test file in the feature folder if one covers the same module. Create a new file ONLY if no existing file covers the module.
2d. Anti-fragmentation budget per folder
A feature folder gets ONE test file per major contract. Use this budget:
| Folder size (source files) | Max test files |
|---|---|
| 1–5 source files | 1 test file |
| 6–15 source files | up to 3 test files (only if contracts genuinely diverge) |
| 16+ source files | up to 1 test file per major subsystem (read the README.md "Source file map") |
If you exceed budget, you MUST consolidate before finishing — do not leave the folder over budget. Naming pattern:
{service}.test.ts— top-level service contract{service}{Subsystem}.test.ts— only if Subsystem is a distinct contract (e.g.prGithubStack.test.ts,ctoWorkerLifecycle.test.ts)
Forbidden naming patterns (these are fragmentation signals):
{service}.{minorThing}.test.ts— folds a minor concern into its own file. Merge into{service}.test.ts.{helper}.test.tsfor a helper used by only one parent — fold into the parent's test file.
Pass 3: ADD (only if needed)
Goal: prove the feature's public contract. Not its internals.
3a. What to test
Identify the contracts the new feature introduces:
- New exported function → one test of its happy path, plus the realistic failure modes a caller will hit.
- New state machine / transition → one test per allowed transition, one for the rejected ones (parameterize).
- New IPC handler → request shape in, response shape out, one error path.
- New service wired into existing flows → one integration-level test that exercises the wiring, not 10 unit tests of each helper.
3b. Hard caps (override only with a one-line justification in the summary)
- Max 1 new test file per feature. Prefer extending an existing file. If extending would push that file past 300
it(blocks, that file itself needs consolidation review — flag it but still extend. - Max 15 new
it(blocks total for the whole feature. If you want more, you're testing internals. - Min 3 meaningful assertions per test (not
toBeDefined× 3). - No test of a private/internal helper unless it has non-obvious branching that the public API can't easily reach.
- No render-only React tests. If the change is purely visual, do not add a test — say so in the summary.
- Respect the per-folder file budget from Pass 2d. Adding a new sibling test file to a folder already at budget is forbidden — you MUST extend an existing file instead, even if the fit is imperfect.
3c. Patterns
Before writing anything, read 1–2 existing tests in the same folder to copy: imports, mocking, setup/teardown, assertion style.
Rules:
- Colocated naming:
{module}.test.tsnext to{module}.ts. - Never mock the module under test.
- Mock only at process boundaries: file system, network, child processes, Electron APIs, IPC.
- Tests must FAIL LOUDLY — assert preconditions explicitly.
- Use
nodeenvironment unless DOM is genuinely required. - Wait on events, receipts, resolved promises, or fake timers — never on wall-clock sleeps. A test that needs a real
sleepor a raised timeout to pass is testing a race, not a contract; fix the seam (expose the completion signal) instead of padding the wait.
3d. Run as you write
After each test file is created or extended:
cd apps/desktop && npx vitest run <file>
Fix until passing before moving to the next.
Parity Passes (4–8)
After the test-suite work above, run five parity reviewers that keep docs, iOS, the CLI, the TUI, and the published SDK packages in lockstep with the desktop changes on this branch. They are independent of one another and of Passes 1–3.
Preferred: TeamCreate for these five passes so progress is tracked and a single completion event surfaces the batch. Per the global git-worktrees policy, do not pass worktree isolation. Fallback: parallel Agent calls in a single tool-call round if TeamCreate is unavailable.
Pass 4: DOCS
The internal docs live under docs/ with this structure (rebuilt; do NOT confuse with the public Mintlify site at repo root docs.json + *.mdx):
docs/
├── README.md # navigation map
├── PRD.md # product entry point — links to every feature
├── ARCHITECTURE.md # consolidated system architecture
├── OPTIMIZATION_OPPORTUNITIES.md # backlog (append-only)
└── features/
├── agents/ ├── memory/
├── automations/ ├── onboarding-and-settings/
├── chat/ ├── personal-chats/
├── computer-use/ ├── pull-requests/
├── conflicts/ ├── sync-and-multi-device/
├── context-packs/ ├── terminals-and-sessions/
├── cto/ ├── workspace-graph/
├── files-and-editor/
├── history/
├── lanes/
├── linear-integration/
└── sdk/
Each features/<name>/ contains a README.md (overview + source file map at top) plus 1–4 detail *.md files.
Spawn a general-purpose agent with this prompt:
You are the documentation updater for the ADE project.
Analyze all changes on the current branch vs the resolved review base and update relevant internal
docs under `docs/`. The public Mintlify site (docs.json + root-level .mdx files)
is out of scope **except the SDK tab**: if the branch touches `packages/sdk`,
`packages/chat-ui`, `apps/desktop/src/shared/callerMcpServers.ts`, or the
embedded runtime profile / parent-death watchdog, also update `sdk/*.mdx` and
the SDK entries in `docs.json`. Keep the MCP honesty table aligned across
`sdk/mcp.mdx`, `packages/sdk/README.md`, and `docs/features/sdk/README.md`.
Both npm READMEs must link to https://www.ade-app.dev/docs/sdk/overview.
Do not rewrite unrelated Mintlify product pages (chat/, tools/, changelog/, …).
Step 1: Get changed files
git diff "$TEST_REVIEW_BASE" --name-only
git diff "$TEST_REVIEW_BASE" --stat | tail -30
Step 2: Map changed source to internal docs
| Source Directory | Doc Location |
|----------------------------------------------------|----------------------------------------------------|
| apps/desktop/src/main/services/projects/ | docs/features/onboarding-and-settings/ |
| apps/desktop/src/main/services/proof/ | docs/features/proof.md |
| apps/desktop/src/main/services/review/ | docs/features/pull-requests/ |
| apps/desktop/src/main/services/prs/ | docs/features/pull-requests/ |
| apps/desktop/src/main/services/lanes/ | docs/features/lanes/ |
| apps/desktop/src/main/services/memory/ | docs/features/memory/ |
| apps/desktop/src/main/services/cto/ | docs/features/cto/ (+ linear-integration/) |
| apps/desktop/src/main/services/ai/ | docs/features/chat/ + features/agents/ |
| apps/desktop/src/main/services/chat/ | docs/features/chat/ |
| apps/desktop/src/main/services/automations/ | docs/features/automations/ |
| apps/desktop/src/main/services/computerUse/ | docs/features/computer-use/ |
| apps/desktop/src/main/services/context/ | docs/features/context-packs/ |
| apps/desktop/src/main/services/conflicts/ | docs/features/conflicts/ |
| apps/desktop/src/main/services/files/ | docs/features/files-and-editor/ |
| apps/desktop/src/main/services/history/ | docs/features/history/ |
| apps/desktop/src/main/services/onboarding/ | docs/features/onboarding-and-settings/ |
| apps/desktop/src/main/services/pty/ | docs/features/terminals-and-sessions/ |
| apps/desktop/src/main/services/sessions/ | docs/features/terminals-and-sessions/ |
| apps/desktop/src/main/services/processes/ | docs/features/terminals-and-sessions/ |
| apps/desktop/src/main/services/sync/ | docs/features/sync-and-multi-device/ |
| apps/desktop/src/main/services/config/ | docs/features/onboarding-and-settings/ |
| apps/desktop/src/main/services/ipc/ | docs/ARCHITECTURE.md (IPC section) |
| apps/desktop/src/main/services/git/ | docs/ARCHITECTURE.md (Git engine section) + lanes/ |
| apps/desktop/src/preload/ | docs/ARCHITECTURE.md (IPC contract) |
| apps/desktop/src/shared/ | docs/ARCHITECTURE.md + touching feature's doc |
| apps/desktop/src/renderer/components/<area>/ | docs/features/<same-area>/ |
| apps/desktop/src/renderer/state/ | docs/ARCHITECTURE.md (UI framework) |
| apps/ade-cli/src/tuiClient/ | docs/features/ade-code/README.md + docs/ARCHITECTURE.md (ADE CLI / Build/Test/Deploy) |
| apps/ade-cli/ | docs/ARCHITECTURE.md (ADE CLI / Build/Test/Deploy) + docs/features/agents/ |
| .github/workflows/ | docs/ARCHITECTURE.md (Build/Test/Deploy) |
| apps/ios/ | docs/features/sync-and-multi-device/ios-companion.md |
| apps/web/ | docs/ARCHITECTURE.md (Apps & Processes) |
| packages/sdk/ | sdk/*.mdx + packages/sdk/README.md + docs/features/sdk/ |
| packages/chat-ui/ | sdk/chat-ui.mdx + packages/chat-ui/README.md + docs/features/sdk/ |
| apps/desktop/src/shared/callerMcpServers.ts | sdk/mcp.mdx + packages/sdk/README.md + docs/features/sdk/ (honesty table) |
Step 3: Update docs in place
- Prefer editing existing docs over creating new ones.
- If a feature gets a genuinely new sub-concept worth its own page, add a new detail doc inside the existing features/<name>/ folder.
- Keep each README.md's "Source file map" section current — it is the primary way an agent orients itself.
- Rewrite prose to reflect current reality (not a changelog of what changed).
- Remove outdated information.
- Do NOT add changelog sections, "Updated on X" notes, or dated markers.
- Do NOT modify docs/OPTIMIZATION_OPPORTUNITIES.md via this agent — it is append-only and human-curated.
Step 4: Run doc validation
node scripts/validate-docs.mjs
This validator only covers the Mintlify site. For internal docs, self-check:
- Every features/<name>/README.md still has a "Source file map" section.
- PRD.md links resolve (grep for broken relative links).
Report what docs were updated and what was changed.
Pass 5: MOBILE parity
Spawn a general-purpose agent with this prompt:
You are the mobile parity reviewer for the ADE project.
Shortened here. Read the whole file on GitHub.
Signals
- GitHub stars
- 104
- Forks
- 12
- Last commit
- Sep 2026
Advanced
- Catalog kind
- skill
- Gateway key
test-arul28- Source
- github.com/arul28/ade