Skip to content
cccskills
FreePublished on GitHub

tdd

Use when implementing any new feature or fixing any bug in this project — both Rust (src-tauri) and TypeScript/Svelte 5 (src). Triggers on phrases like "add X", "fix Y", "implement Z", "change how W works".

The English summary is being prepared. Showing the original description.

Xoshbin/asyar946 stars(GitHub 946 + cccskills 0)

Run score Not tested

See how to install

Files included (1)

  • SKILL.md7.0 KB

SKILL.md (original)

Review the instructions the agent will receive before you install.

Test-Driven Development

Write a failing test first. Then write only enough code to make it pass. No exceptions.

The Iron Law

NO IMPLEMENTATION BEFORE A FAILING TEST.

Write code before a test? Delete it. Start over.

No exceptions:

  • Not for "trivial" functions
  • Not for "obvious" logic
  • Not for bug fixes ("I just need to change one line")
  • Don't keep it as "reference" while writing tests
  • Delete means delete

RED-GREEN-REFACTOR

RED   → Write test → run → watch it FAIL (required)
GREEN → Write minimal code → run → watch it PASS
REFACTOR → Clean up → run → still passes

Never skip RED. A test that passes without you writing any implementation is not a TDD test.

Verification Loops: Inner Loop vs. Outer Loop

To maintain maximum velocity during TDD without context bloat or verification taxes:

  • Inner Loop (Iterative Red-Green-Refactor):
    • Run targeted single-test or single-file test runners (cargo test <test_name> or pnpm test).
    • Or run domain-scoped checks:
      • Frontend changes: pnpm check:ci:frontend
      • Rust changes: pnpm check:ci:rust
      • Changed files only: pnpm check:ci:changed
    • Never run the monolithic verification matrix on every minor edit.
  • Outer Loop (Task Conclusion / Hand-off / Pre-Push):
    • Run the full verification matrix (pnpm check:ci) only after tests pass and implementation is complete, or when modifying cross-domain bindings, IPC contracts, or migrations.

Rust (src-tauri)

Test location: inline #[cfg(test)] module at the bottom of the same .rs file.

Commands:

# Run all tests
cd asyar-launcher/src-tauri && cargo test

# Run tests for one file/module
cargo test <module_name>

# See output from passing tests too
cargo test -- --nocapture

Pattern:

pub fn some_function(input: &str) -> Result<String, AppError> {
    // ← implement AFTER tests are written and failing
    todo!()
}

#[cfg(test)]
mod tests {
    use super::*;

    // Step 1: Write this first, run cargo test, watch it fail
    #[test]
    fn test_some_function_happy_path() {
        let result = some_function("valid input").unwrap();
        assert_eq!(result, "expected output");
    }

    #[test]
    fn test_some_function_error_case() {
        let result = some_function("");
        assert!(result.is_err());
    }

    // Use factory helpers for complex types
    fn make_test_manifest(id: &str) -> ExtensionManifest {
        ExtensionManifest { id: id.to_string(), ..Default::default() }
    }
}

TypeScript / Svelte 5 (src)

Test location: colocated *.test.ts file (e.g. src/services/foo/FooService.test.ts).

Commands:

cd asyar-launcher

# Watch mode during development
pnpm test

# Single run (CI / verify before done)
pnpm test:run

Pattern:

// Step 1: Write this file first, run pnpm test:run, watch it fail
import { describe, it, expect, vi, beforeEach } from 'vitest';

// Mock dependencies BEFORE importing the module under test
vi.mock('@tauri-apps/api/core', () => ({ invoke: vi.fn() }));
vi.mock('../log/logService', () => ({
  logService: { debug: vi.fn(), info: vi.fn(), warn: vi.fn(), error: vi.fn() },
}));

// Import AFTER mocks are declared
import { FooService } from './FooService';
import { invoke } from '@tauri-apps/api/core';

describe('FooService', () => {
  beforeEach(() => vi.clearAllMocks());

  it('calls invoke with correct command', async () => {
    vi.mocked(invoke).mockResolvedValueOnce({ id: '1', name: 'Test' });

    const result = await FooService.getItem('1');

    expect(invoke).toHaveBeenCalledWith('get_item', { id: '1' });
    expect(result.name).toBe('Test');
  });

  it('throws on invoke error', async () => {
    vi.mocked(invoke).mockRejectedValueOnce(new Error('not found'));
    await expect(FooService.getItem('missing')).rejects.toThrow('not found');
  });
});

Cross-Module & Registry Integration Tests

Unit tests with heavy mocking often hide multi-module bugs (e.g. duplicate action IDs, conflicting registrations across ActionService and ExtensionLoader).

When a feature interacts across registration boundaries:

  1. Write an integration test that boots the real modules together (e.g. a built-in feature registering its view actions into ActionService).
  2. Assert that no duplicate IDs, duplicate shortcuts, or clashing UI labels are produced in the shared registry.

Quick Reference

TaskCommandLocation
Run Rust testscargo test (in src-tauri/)*.rs inline #[cfg(test)]
Run launcher TS tests (watch)pnpm test (in asyar-launcher/)src/**/*.test.ts
Run launcher TS tests (once)pnpm test:run (in asyar-launcher/)src/**/*.test.ts
Run SDK tests (once)pnpm test:run (in asyar-sdk/)src/**/*.test.ts
Mock Tauri invokevi.mock('@tauri-apps/api/core', ...)top of test file
Mock globals/envvi.stubGlobal() / vi.stubEnv()inside test, restore in afterEach
Inner loop frontend checkspnpm check:ci:frontendrepo root
Inner loop Rust checkspnpm check:ci:rustrepo root
Inner loop git-changed checkspnpm check:ci:changedrepo root
Outer loop full verificationpnpm check:cirepo root

Common Mistakes

MistakeFix
Writing implementation firstDelete it. Write test first.
Test passes without implementationYou wrote the wrong test. It must fail first.
Forgetting vi.mock() hoistingDeclare vi.mock() before any imports
Not clearing mocksAdd beforeEach(() => vi.clearAllMocks())
Testing in Svelte component directlyExtract logic to .ts file, test that
Not running cargo test after Rust changesAlways verify: cd src-tauri && cargo test

Red Flags — STOP and Start Over

  • "I'll write the test after, it's a small change"
  • "This is just a refactor, tests aren't needed"
  • "The bug fix is obvious, I don't need a test"
  • "I already know it works"
  • "Let me just implement it and then add the test"

All of these mean: write the test first. No exceptions.

Reviews

No reviews yet. Tell others what you thought.

Comments

No login needed. Share how it worked for you, ask a question, or note where it did not run.

No comments yet. Be the first to write one.

Comments are published right away. Do not include personal information such as email addresses or phone numbers. Up to 2 URLs.

0 / 1,000 characters

This form is protected by reCAPTCHA, and the Google Privacy Policy and Terms of Service apply.

More skills from this repository

Use before planning or implementing ANY feature, bug fix, refactor, or API change that touches Asyar's architecture — extension system, service layer, IPC contracts, Tauri commands, manifest contributions, or cross-layer data flow. Triggers on new features, new extension APIs, new services, bug fixes involving multiple layers, refactors of infrastructure code, adding new contribution points, and any work that could affect how built-in features or third-party extensions interact with the platform. Also triggers on any task where you need to decide which architectural layer owns the logic, whether Tier 2 extensions can use a capability, or whether a change requires a manifest/permission update. Does NOT trigger for pure CSS/styling fixes (use design-language), isolated unit tests, documentation updates, version bumps, mechanical renames, or answering informational questions about the codebase.

The English summary is being prepared. Showing the original description.

Xoshbin/asyar946Updated Oct 27, 2026

Use when building, modifying, or fixing any frontend UI in the Asyar launcher — new components, new views, layout changes, styling decisions, new built-in features, visual bug fixes, and anything in settings or onboarding. Answers "what do I use here?" for fonts, colour, typography, spacing, motion, components, and layout.

The English summary is being prepared. Showing the original description.

Xoshbin/asyar946Updated Oct 27, 2026

Reference for the Asyar monorepo structure, SDK workspace linking, lockfile discipline, CI workflows, and release flow. Use this skill whenever working on anything related to pnpm workspace setup, the asyar-sdk dependency, lockfile errors, SDK or launcher version bumps, CI configuration, release workflow, or questions about how local dev differs from CI. Also triggers for "how does the workspace work", "how do I update the SDK version", "where do I run pnpm install", or any confusion about the monorepo layout.

The English summary is being prepared. Showing the original description.

Xoshbin/asyar946Updated Oct 27, 2026

Never hand-edit or hand-copy generated content. Use when touching permissions.rs, error.rs, AppError/SearchError variants, runtimes/catalog.fallback.json, any `#[derive(specta::Type)]` struct, any file with an AUTO-GENERATED banner (kinds.ts, gatedPermissions.ts, knownRuntimes.ts, bindings.ts, emoji data), or whenever you are about to write a TypeScript list/type that mirrors a Rust source.

The English summary is being prepared. Showing the original description.

Xoshbin/asyar946Updated Oct 27, 2026

Audit IPC message contracts between extensions and the Asyar host. Use when adding a new SDK service, adding a new proxy method, changing a postMessage type string, or reviewing permission gate coverage.

The English summary is being prepared. Showing the original description.

Xoshbin/asyar946Updated Oct 27, 2026

See all skills by Xoshbin

Report a problem with this skill