Before acting on the first substantial user request in a fresh repository session, check INITIAL_CUSTOMIZATION_STATUS.md.
- If
status: pending, recommenddocs/INITIAL_CUSTOMIZATION.mdbefore doing other feature work. - If the user is explicitly asking to perform the initial customization, do that work instead of repeating the reminder.
- Once the branding cleanup is done, update
INITIAL_CUSTOMIZATION_STATUS.mdtostatus: completewith the real app name so future sessions do not repeat the recommendation.
This reminder exists because the template ships with NebulaKit branding, default share assets, and template documentation links that should usually be replaced before normal development.
Dev Server is Always Running (Local Copilot Chat Only)
⚠️ This section applies ONLY to GitHub Copilot Chat on local workstations, NOT to the Copilot Coding Agent (remote/cloud agent).
When using Copilot Chat locally:
- Assume
npm run devis already running in a separate terminal on port 4277 - Do NOT start the dev server when performing tasks
- Do NOT run
npm run dev,vite dev, or similar commands - When testing locally, assume the app is already accessible at
http://localhost:4277 - If you need to verify the app is running, check the existing terminal output rather than starting a new instance
When using the Copilot Coding Agent (remote):
- The coding agent should manage its own dev server as needed
- Normal startup commands are expected in that context
Always use the .llm-outputs/ directory for any temporary local files (test output logs, coverage dumps, debug traces, scratch notes, etc.). This folder is in .gitignore so nothing placed there will end up in the repository.
- DO: Write command output, test results, coverage reports, or any other throwaway files to
.llm-outputs/ - DO NOT: Create
.txt,.log, or other scratch files in the project root or any other tracked directory - The folder already exists with a
.gitkeep; just drop files directly into it
Test-Driven Development (TDD) is MANDATORY
- Never start a feature or bug fix without writing tests first
- Write failing tests, then implement code to make them pass
- Maintain 95%+ code coverage across all modules — coverage must NEVER drop below 95%
- Tests are not optional—they are part of the definition of "done"
- Always optimize for Cloudflare Workers runtime
- Use Cloudflare services as the default choice:
- D1 for database operations
- KV for key-value storage
- R2 for object storage
- Queues for background jobs
- Turnstile for CAPTCHA
- Workers AI for AI/ML operations
- Avoid external services that duplicate Cloudflare functionality
- Consider edge computing patterns and cold start optimization
- Build, don't buy - Implement features in-house whenever feasible
- Avoid external packages for:
- WYSIWYG editors (build custom)
- User management (use built-in auth)
- SSO integrations (implement directly with Auth.js/SvelteKit)
- UI components (extend internal component library)
- Form validation (custom implementations)
- State management (use Svelte stores)
- Only add external packages when:
- The functionality is extremely complex (e.g., cryptography)
- It's a Cloudflare-native integration
- It's a core framework requirement (SvelteKit, Vite)
- The package is well-maintained, lightweight, and has no alternatives
// tests/unit/feature.test.ts
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
describe('Feature Name', () => {
beforeEach(() => {
// Setup test environment
});
afterEach(() => {
// Cleanup
});
it('should do X when Y happens', () => {
// Arrange
// Act
// Assert
});
});- Minimum 95% coverage on all modules — this is a hard floor, never allow it to drop below 95%
- 100% coverage on critical paths (auth, payments, data mutations)
- Before finishing any task, run
npm run test:coverageand verify coverage has not decreased - If your changes would reduce coverage below 95%, you MUST add additional tests before considering the task complete
- Every new feature must include:
- Unit tests for business logic
- Integration tests for API endpoints
- Component tests for UI elements
- E2E tests for critical user flows
-
Unit Tests (
tests/unit/)- Pure functions and utilities
- Individual Svelte components
- Store logic
- Database queries (mocked)
-
Integration Tests (
tests/integration/)- API endpoints with SvelteKit
- Database operations with test D1 instance
- Service layer interactions
-
E2E Tests (
tests/e2e/)- Complete user workflows
- Authentication flows
- Critical business processes
# Always ensure dev environment works
npm run dev
# Create feature branch
git checkout -b feature/feature-name
# Write tests FIRST
# Create test file before implementation file- RED: Write a failing test that defines desired behavior
- GREEN: Write minimal code to make the test pass
- REFACTOR: Improve code quality while keeping tests green
- REPEAT: Continue for each small piece of functionality
# Run all tests
npm run test
# Check coverage
npm run test:coverage
# Type checking
npm run check
# Ensure dev still works
npm run devNebulaKit/
├── src/
│ ├── lib/
│ │ ├── components/ # Svelte components (with .test.ts files)
│ │ ├── stores/ # Svelte stores (with .test.ts files)
│ │ ├── utils/ # Utility functions (with .test.ts files)
│ │ ├── services/ # Business logic (with .test.ts files)
│ │ └── types/ # TypeScript types
│ ├── routes/ # SvelteKit routes (with .test.ts files)
│ └── app.html
├── tests/
│ ├── unit/ # Unit tests
│ ├── integration/ # Integration tests
│ ├── e2e/ # End-to-end tests
│ └── fixtures/ # Test data and mocks
├── migrations/ # D1 database migrations
└── wrangler.toml # Cloudflare configuration
<script lang="ts">
// Use TypeScript always
import type { ComponentProps } from './types';
// Props with types
export let data: ComponentProps;
// Reactive statements for derived state
$: derivedValue = computeValue(data);
</script>
<!-- Template with proper accessibility -->
<div role="region" aria-label="descriptive-label">
{#if condition}
<p>{derivedValue}</p>
{/if}
</div>
<style>
/* Component-scoped styles */
div {
/* Use CSS custom properties for theming */
color: var(--text-primary);
}
</style>// Always use parameterized queries
export async function getUser(platform: Platform, userId: string) {
return await platform.env.DB.prepare('SELECT * FROM users WHERE id = ?')
.bind(userId)
.first<User>();
}
// Use transactions for related operations
export async function createUserWithProfile(platform: Platform, userData: UserData) {
return await platform.env.DB.batch([
platform.env.DB.prepare('INSERT INTO users (name) VALUES (?)').bind(userData.name),
platform.env.DB.prepare('INSERT INTO profiles (user_id) VALUES (?)').bind(userData.id)
]);
}// src/routes/api/resource/+server.ts
import type { RequestHandler } from './$types';
import { json, error } from '@sveltejs/kit';
export const GET: RequestHandler = async ({ platform, locals }) => {
// Check authentication
if (!locals.user) {
throw error(401, 'Unauthorized');
}
try {
// Use Cloudflare bindings via platform
const data = await platform.env.DB.prepare('SELECT * FROM table').all();
return json(data);
} catch (err) {
throw error(500, 'Internal server error');
}
};- Always include proper ARIA labels
- Ensure keyboard navigation works
- Test with screen readers
- Maintain proper heading hierarchy
- Color contrast ratios must meet WCAG AA
- Mobile-first approach
- Test on mobile, tablet, and desktop
- Use relative units (rem, em, %)
- Implement proper touch targets (44x44px minimum)
CRITICAL: All colors MUST use CSS custom properties (CSS variables)
- NEVER hardcode color values - Always reference theme variables from
app.css - NEVER use
#hex,rgb(),hsl(), or named colors directly in components - ALWAYS use
var(--color-*)for any color value - This applies to ALL styling:
- Background colors
- Text colors
- Border colors
- Shadow colors (use CSS variables with alpha)
- SVG fill/stroke colors
- Pseudo-element colors (::before, ::after)
- Hover/focus/active states
Create CSS variable overrides for ALL browser elements to ensure consistent theming:
Forms & Inputs:
input,
textarea,
select {
background-color: var(--color-surface);
color: var(--color-text);
border-color: var(--color-border);
}
input:focus,
textarea:focus,
select:focus {
border-color: var(--color-primary);
outline-color: var(--color-primary);
}
input::placeholder {
color: var(--color-text-secondary);
}
/* Autofill states */
input:-webkit-autofill {
-webkit-text-fill-color: var(--color-text);
-webkit-box-shadow: 0 0 0 1000px var(--color-surface) inset;
}Scrollbars:
::-webkit-scrollbar {
background: var(--color-surface);
}
::-webkit-scrollbar-thumb {
background: var(--color-border);
}
::-webkit-scrollbar-thumb:hover {
background: var(--color-text-secondary);
}Selection:
::selection {
background-color: var(--color-primary);
color: var(--color-background);
}Other Elements:
- Checkboxes, radio buttons, range sliders
- Progress bars, meters
- Dialog backdrops
- Table borders and stripes
- Code blocks and syntax highlighting
- Toast notifications, modals
- Clean, uncluttered interfaces - Remove unnecessary decorations
- Generous whitespace - Use
var(--spacing-*)consistently - Subtle borders - Prefer
1pxborders withvar(--color-border) - Minimal shadows - Use
var(--shadow-sm)orvar(--shadow-md)sparingly - Simple animations - Use
var(--transition-*)for smooth, not flashy - Typography hierarchy - Let font size and weight create visual structure
- Icon-first design - Use clear, simple icons over text when appropriate
- Consistent spacing - Maintain rhythm with spacing scale
- All text/background combinations MUST meet WCAG AA standards
- Minimum contrast ratio: 4.5:1 for normal text, 3:1 for large text
- Automated contrast checking runs on build/test
- Use the
checkContrast()utility when creating new color variables - If contrast fails, adjust colors or provide alternative combinations
<style>
.button {
/* ✅ CORRECT */
background-color: var(--color-primary);
color: var(--color-background);
border: 1px solid var(--color-border);
border-radius: var(--radius-md);
padding: var(--spacing-sm) var(--spacing-md);
transition: background-color var(--transition-fast);
}
.button:hover {
background-color: var(--color-primary-hover);
}
/* ❌ WRONG */
.bad-button {
background-color: #0066cc; /* Never hardcode colors! */
color: white; /* Never use color keywords! */
border: 1px solid #ddd; /* Never use hex codes! */
}
</style>- Check if appropriate CSS variables exist in
app.css - If missing, suggest adding to
app.cssfirst - Ensure both light and dark theme values are defined
- Run contrast validation for new color combinations
- Always use variables in component styles
Migrations are immutable once committed to main.
This project uses Cloudflare D1's built-in migration tracking. Applied migrations are recorded in a d1_migrations table and are never re-run. See migrations/README.md for full details.
- NEVER edit or delete an existing migration file - They are immutable once committed
- Always create a new migration file with the next sequence number (
NNNN_description.sql) - Use
ALTER TABLEto modify existing tables, notCREATE TABLEwith changes - Test locally first:
npm run db:migrate:local - Check status:
npm run db:migrate:list
# 1. Create a new file with the next number
# migrations/0002_add_user_preferences.sql
# 2. Write your SQL
# ALTER TABLE users ADD COLUMN preferences TEXT;
# 3. Test locally
npm run db:migrate:local
# 4. Apply to production
npm run db:migratenpm run db:migrate- Apply pending migrations to remote D1npm run db:migrate:local- Apply pending migrations to local D1npm run db:migrate:list- Show migration status (applied/pending)
- Validate all user input
- Use parameterized queries (never string concatenation)
- Implement CSRF protection
- Use Cloudflare Turnstile for forms
- Sanitize output to prevent XSS
- Follow principle of least privilege
- Store secrets in Cloudflare Workers secrets (never in code)
- Use explicit types (avoid
any) - Prefer interfaces over types for objects
- Use readonly where appropriate
- Document public APIs with JSDoc
- Components: PascalCase (
UserProfile.svelte) - Files: kebab-case (
user-service.ts) - Variables/functions: camelCase (
getUserData) - Constants: UPPER_SNAKE_CASE (
MAX_RETRY_COUNT) - Types/Interfaces: PascalCase (
User,ApiResponse)
- Write self-documenting code first
- Comment "why", not "what"
- Use JSDoc for public APIs
- Keep comments up-to-date
- Don't skip tests - Tests are not optional
- Don't add dependencies without justification - Build first
- Don't forget Cloudflare Workers limitations:
- No filesystem access
- 128MB memory limit
- 50ms CPU time for free tier
- Consider cold starts
- Don't use Node.js-specific APIs - Use Web APIs instead
- Don't ignore TypeScript errors - Fix them, don't suppress them
- Don't commit without testing locally first
- Don't hardcode configuration - Use environment variables
feature/description- New featuresfix/description- Bug fixestest/description- Test improvementsrefactor/description- Code refactoringdocs/description- Documentation updates
type(scope): short description
Longer explanation if needed
- Bullet points for details
- Reference issues: Fixes #123
Types: feat, fix, test, refactor, docs, style, chore
- Tests written first (TDD)
- All tests passing
- Coverage ≥ 95% (hard minimum — never allow regression below this)
- TypeScript checks pass
- Local dev environment works
- Code reviewed
- Documentation updated
- No new external dependencies (or justified)
GitHub Copilot, when generating code:
- Always consider test implications first
- Suggest the test before the implementation
- Use Cloudflare Workers APIs and patterns
- Avoid suggesting external npm packages
- Follow the TDD cycle
- Include proper TypeScript types
- Consider edge runtime limitations
- Optimize for cold start performance
- Use SvelteKit and Svelte best practices
- Ensure accessibility standards
Remember: Quality over speed. Write tests first. Build instead of importing. Optimize for Cloudflare.