/test — Test Suite Steward

SkillMonitoring & ops

Prove 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.

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:

  1. Dead tests linger after features are ripped out or refactored.
  2. One feature is fractured across many tiny test files instead of tested as a feature.
  3. 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:

  1. Verify docs/logging.md exists and read it in full. A missing file is a failed /test run, not an optional docs gap.
  2. 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.
  3. 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_used plus 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.
  4. If the change is not analytics-applicable, record the concrete reason in the summary. "No analytics changes" without inspecting the behavior is not sufficient.
  5. When the analytics contract changes, update docs/logging.md, the relevant allowlists and boundary tests, and scripts/posthog/dashboard-spec.mjs when a dashboard depends on the change. Validate the provisioner spec and tests.
  6. 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.
  7. 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.
  8. 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:

  1. the exact capability that cannot work and the OS-level reason;
  2. whether macOS/Linux keep it;
  3. 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.ts with no foo.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) return inside a test body → silent pass when setup fails. Replace with expect(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 by expect(...) 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 files1 test file
6–15 source filesup to 3 test files (only if contracts genuinely diverge)
16+ source filesup 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.ts for 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.ts next 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 node environment 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 sleep or 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