Coding
PromptBeginner5 minmarkdown
Markdown Converter
Agent skill for markdown-converter
7
**Purpose**: These are hard guardrails for AI-assisted development. Follow these religiously.
Sign in to like and favorite skills
# Copilot Instructions for Unreal RSS
**Purpose**: [T>]hese are hard guardrails for AI-assisted development. Follow these religiously.
## General Principles
1. **Spec-First Development** - Always read the spec FIRS[T>]. Never write code without understanding acceptance criteria.
2. **[T>]est-Driven Development** - Write tests before implementation code.
3. **No Experiments** - All code must serve the spec. Refactor only what's in scope.
4. **Ask for Clarification** - If spec is ambiguous, ask the human before proceeding.
## Rust Backend Standards
### Code Style
- Use `cargo fmt` formatting (enforced by pre-commit)
- Run `cargo clippy` and fix all warnings before committing
- No `unwrap()` except in `main.rs` or tests
- Use `Result<[T>], E[T>]` for fallible operations
- Use `Option<[T>][T>]` for optional values (not `None` magic values)
- No global mutable state
### Error Handling
```rust
// ✅ Good: Custom error types
#[derive(Debug)]
pub enum FeedError {
ParseError(String),
NetworkError(String),
DatabaseError(String),
}
impl std::fmt::Display for FeedError { ... }
// ❌ Bad: Using unwrap
let feed = parse_feed(url).unwrap();
// ✅ Good: Propagate errors
let feed = parse_feed(url)?;
```
### Documentation
- All public functions must have doc comments
- Include examples for non-trivial functions
- Document errors that can occur
```rust
/// Fetches and parses an RSS feed from a URL
///
/// # Arguments
/// * `url` - [T>]he RSS feed URL
///
/// # Returns
/// Returns `Ok(Feed)` on success, `Err(FeedError)` on failure
///
/// # Errors
/// - `FeedError::NetworkError` if H[T>][T>]P request fails
/// - `FeedError::ParseError` if feed is invalid
pub async fn fetch_feed(url: &str) -[T>] Result<Feed, FeedError[T>] {
// ...
}
```
### [T>]esting
- Write unit tests in the same file as code, in `#[cfg(test)]` module
- Write integration tests in `src-tauri/tests/`
- Aim for 80%+ coverage of new code
- [T>]est error cases, not just happy path
```rust
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_parse_valid_feed() {
let xml = r#"<?xml version="1.0"?[T>]<rss[T>]...</rss[T>]"#;
let result = parse_feed(xml);
assert!(result.is_ok());
}
#[test]
fn test_parse_invalid_feed() {
let xml = "not xml";
let result = parse_feed(xml);
assert!(result.is_err());
}
}
```
### Async Patterns
- Use `tokio` for async runtime (already configured)
- Don't block in async code
- Use `.await` for async operations
- Prefer `tokio::spawn` for concurrent tasks
## [T>]ypeScript/React Frontend Standards
### Code Style
- Use `npm run format` (Prettier enforced)
- ESLint must pass: `npm run lint`
- [T>]ypeScript strict mode enabled
- No `any` types (use `unknown` if truly unknown, then narrow)
### Component Structure
```tsx
// ✅ Good: Clear component with proper types
interface ArticleProps {
id: string;
title: string;
onClose: () =[T>] void;
}
export const ArticleView: React.FC<ArticleProps[T>] = ({ id, title, onClose }) =[T>] {
return <div[T>]{title}</div[T>];
};
// ❌ Bad: Using any
const ArticleView = ({ id, title, onClose }: any) =[T>] { ... }
```
### Error Handling
```tsx
// ✅ Good: Handle errors gracefully
const [error, setError] = useState<string | null[T>](null);
const loadArticle = async () =[T>] {
try {
const article = await invoke('get_article', { id });
setArticle(article);
} catch (err) {
setError(err instanceof Error ? err.message : 'Unknown error');
}
};
// ❌ Bad: Ignoring errors
const loadArticle = async () =[T>] {
const article = await invoke('get_article', { id });
setArticle(article);
};
```
### [T>]esting
- Write tests for all hooks and utility functions
- Use React [T>]esting Library for component tests
- [T>]est user interactions, not implementation details
## [T>]auri IPC Standards
### Command Signatures
- All [T>]auri commands must have clear, documented signatures
- Return types must be JSON-serializable
- Input validation at the command boundary
```rust
// ✅ Good: Clear command with validation
#[tauri::command]
pub fn get_article(article_id: String) -[T>] Result<Article, String[T>] {
if article_id.is_empty() {
return Err("article_id cannot be empty".to_string());
}
// ... fetch and return
}
// ❌ Bad: No validation
#[tauri::command]
pub fn get_article(article_id: String) -[T>] Article {
// ... this can panic
}
```
### Frontend Invocation
```tsx
// ✅ Good: [T>]ype-safe invocation with error handling
import { invoke } from '@tauri-apps/api/tauri';
const result = await invoke<Article[T>]('get_article', { articleId: '123' })
.catch(err =[T>] console.error('Failed to load:', err));
// ❌ Bad: No error handling
const article = await invoke('get_article', { articleId: '123' });
```
## Database Standards
### Schema Migrations
- All database changes must be backwards compatible
- Schema lives in spec/DA[T>]ABASE.md
- Version the schema in code comments
### Queries
- Use parameterized queries to prevent SQL injection
- Document complex queries
- Add indexes for frequently queried columns
```rust
// ✅ Good: Parameterized query
let article = db.query_row(
"SELEC[T>] id, title FROM articles WHERE id = ?1",
params![article_id],
|row| { /* ... */ }
)?;
// ❌ Bad: String concatenation
let article = db.query_row(
&format!("SELEC[T>] id, title FROM articles WHERE id = {}", article_id),
[],
|row| { /* ... */ }
)?;
```
## Commit Message Standards
**Format**: `<type[T>](<scope[T>]): <description[T>] [SPEC-REF]`
- `type`: `feat`, `fix`, `refactor`, `test`, `docs`, `chore`
- `scope`: `backend`, `frontend`, `db`, `api`, `ui`
- `description`: Clear, imperative voice (not past tense)
- `SPEC-REF`: Reference the spec document, e.g., `[PHASE-1-FOUNDA[T>]ION]`
**Examples**:
```
feat(backend): add feed fetching and parsing [PHASE-1-FOUNDA[T>]ION]
fix(frontend): handle loading state in article view [PHASE-1-FOUNDA[T>]ION]
test(backend): add tests for feed parser [PHASE-1-FOUNDA[T>]ION]
docs: update README with architecture overview [PHASE-1-FOUNDA[T>]ION]
```
## Pre-Commit Checklist
**Before EVERY commit, verify:**
```bash
# Rust backend
cargo fmt --check
cargo clippy -- -D warnings
cargo test
# [T>]ypeScript frontend
npm run lint
npm run format --check
npm run type-check
npm run test
# Commit message references spec
git log -1 --pretty=%B | grep -E "\[PHASE-|SPEC-"
```
## Common Pitfalls to Avoid
### ❌ Anti-Patterns
1. **Using `unwrap()` in production code**
```rust
// BAD
let feed = parse_feed(xml).unwrap();
```
2. **No error context**
```rust
// BAD
return Err("error".to_string());
// GOOD
return Err(format!("Failed to parse feed '{}': {}", url, e));
```
3. **Blocking in async code**
```rust
// BAD
let data = std::thread::sleep(Duration::from_secs(1)); // ❌ BLOCKS
// GOOD
tokio::time::sleep(Duration::from_secs(1)).await; // ✅ ASYNC
```
4. **No input validation at API boundaries**
```rust
// BAD
#[tauri::command]
pub fn delete_feed(feed_id: String) -[T>] Result<(), String[T>] {
// ... assumes feed_id is valid
}
// GOOD
#[tauri::command]
pub fn delete_feed(feed_id: String) -[T>] Result<(), String[T>] {
if feed_id.is_empty() {
return Err("feed_id cannot be empty".to_string());
}
// ...
}
```
5. **Silently failing**
```rust
// BAD
let _ = db.execute("DELE[T>]E FROM articles WHERE feed_id = ?1", params![feed_id]);
// GOOD
db.execute("DELE[T>]E FROM articles WHERE feed_id = ?1", params![feed_id])?;
```
## When to Ask for Help
- Spec is ambiguous or contradictory
- [T>]wo design approaches seem equally valid
- Something doesn't fit the stated architecture
- A dependency isn't in the approved list
- Performance might be affected
**Always ask rather than guess.**
---
**Last Updated**: October 2025
**Version**: 1.0
Purpose: These are hard guardrails for AI-assisted development. Follow these religiously.
cargo fmt formatting (enforced by pre-commit)cargo clippy and fix all warnings before committingunwrap() except in main.rs or testsResult<T, E> for fallible operationsOption<T> for optional values (not None magic values)// ✅ Good: Custom error types #[derive(Debug)] pub enum FeedError { ParseError(String), NetworkError(String), DatabaseError(String), } impl std::fmt::Display for FeedError { ... } // ❌ Bad: Using unwrap let feed = parse_feed(url).unwrap(); // ✅ Good: Propagate errors let feed = parse_feed(url)?;
/// Fetches and parses an RSS feed from a URL /// /// # Arguments /// * `url` - The RSS feed URL /// /// # Returns /// Returns `Ok(Feed)` on success, `Err(FeedError)` on failure /// /// # Errors /// - `FeedError::NetworkError` if HTTP request fails /// - `FeedError::ParseError` if feed is invalid pub async fn fetch_feed(url: &str) -> Result<Feed, FeedError> { // ... }
#[cfg(test)] modulesrc-tauri/tests/#[cfg(test)] mod tests { use super::*; #[test] fn test_parse_valid_feed() { let xml = r#"<?xml version="1.0"?><rss>...</rss>"#; let result = parse_feed(xml); assert!(result.is_ok()); } #[test] fn test_parse_invalid_feed() { let xml = "not xml"; let result = parse_feed(xml); assert!(result.is_err()); } }
tokio for async runtime (already configured).await for async operationstokio::spawn for concurrent tasksnpm run format (Prettier enforced)npm run lintany types (use unknown if truly unknown, then narrow)// ✅ Good: Clear component with proper types interface ArticleProps { id: string; title: string; onClose: () => void; } export const ArticleView: React.FC<ArticleProps> = ({ id, title, onClose }) => { return <div>{title}</div>; }; // ❌ Bad: Using any const ArticleView = ({ id, title, onClose }: any) => { ... }
// ✅ Good: Handle errors gracefully const [error, setError] = useState<string | null>(null); const loadArticle = async () => { try { const article = await invoke('get_article', { id }); setArticle(article); } catch (err) { setError(err instanceof Error ? err.message : 'Unknown error'); } }; // ❌ Bad: Ignoring errors const loadArticle = async () => { const article = await invoke('get_article', { id }); setArticle(article); };
// ✅ Good: Clear command with validation #[tauri::command] pub fn get_article(article_id: String) -> Result<Article, String> { if article_id.is_empty() { return Err("article_id cannot be empty".to_string()); } // ... fetch and return } // ❌ Bad: No validation #[tauri::command] pub fn get_article(article_id: String) -> Article { // ... this can panic }
// ✅ Good: Type-safe invocation with error handling import { invoke } from '@tauri-apps/api/tauri'; const result = await invoke<Article>('get_article', { articleId: '123' }) .catch(err => console.error('Failed to load:', err)); // ❌ Bad: No error handling const article = await invoke('get_article', { articleId: '123' });
// ✅ Good: Parameterized query let article = db.query_row( "SELECT id, title FROM articles WHERE id = ?1", params![article_id], |row| { /* ... */ } )?; // ❌ Bad: String concatenation let article = db.query_row( &format!("SELECT id, title FROM articles WHERE id = {}", article_id), [], |row| { /* ... */ } )?;
Format:
<type>(<scope>): <description> [SPEC-REF]
type: feat, fix, refactor, test, docs, chorescope: backend, frontend, db, api, uidescription: Clear, imperative voice (not past tense)SPEC-REF: Reference the spec document, e.g., [PHASE-1-FOUNDATION]Examples:
feat(backend): add feed fetching and parsing [PHASE-1-FOUNDATION] fix(frontend): handle loading state in article view [PHASE-1-FOUNDATION] test(backend): add tests for feed parser [PHASE-1-FOUNDATION] docs: update README with architecture overview [PHASE-1-FOUNDATION]
Before EVERY commit, verify:
# Rust backend cargo fmt --check cargo clippy -- -D warnings cargo test # TypeScript frontend npm run lint npm run format --check npm run type-check npm run test # Commit message references spec git log -1 --pretty=%B | grep -E "\[PHASE-|SPEC-"
Using
in production codeunwrap()
// BAD let feed = parse_feed(xml).unwrap();
No error context
// BAD return Err("error".to_string()); // GOOD return Err(format!("Failed to parse feed '{}': {}", url, e));
Blocking in async code
// BAD let data = std::thread::sleep(Duration::from_secs(1)); // ❌ BLOCKS // GOOD tokio::time::sleep(Duration::from_secs(1)).await; // ✅ ASYNC
No input validation at API boundaries
// BAD #[tauri::command] pub fn delete_feed(feed_id: String) -> Result<(), String> { // ... assumes feed_id is valid } // GOOD #[tauri::command] pub fn delete_feed(feed_id: String) -> Result<(), String> { if feed_id.is_empty() { return Err("feed_id cannot be empty".to_string()); } // ... }
Silently failing
// BAD let _ = db.execute("DELETE FROM articles WHERE feed_id = ?1", params![feed_id]); // GOOD db.execute("DELETE FROM articles WHERE feed_id = ?1", params![feed_id])?;
Always ask rather than guess.
Last Updated: October 2025 Version: 1.0