specwright/templates/skills/dev-team/documenter/code-documentation/SKILL.md
# Code Documentation Skill > Skill: code-documentation > Created: 2026-01-09 > Agent: documenter > Category: Documentation ## Purpose Analyze code and add comprehensive inline documentation including comments, docstrings, type hints, and annotations. Makes code self-documenting and maintainable for future developers. ## When to Activate **Trigger Conditions:** - After new code implementation - When code lacks documentation - Before code review - When documenter agent documents codebase **A
npx skillsauth add michsindlinger/specwright specwright/templates/skills/dev-team/documenter/code-documentationInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Skill: code-documentation Created: 2026-01-09 Agent: documenter Category: Documentation
Analyze code and add comprehensive inline documentation including comments, docstrings, type hints, and annotations. Makes code self-documenting and maintainable for future developers.
Trigger Conditions:
Activation Pattern:
When: Code written/modified AND lacks documentation
Then: Analyze and add inline documentation
## Integration Points
- YARD format for docstrings
- RDoc for module documentation
- Rails-specific conventions
- Active Record associations
## Ruby Documentation Standards
# Class Documentation
##
# Manages user authentication and session handling.
#
# This service handles the complete authentication lifecycle including
# login, logout, token generation, and session validation.
#
# @example Authenticate a user
# result = AuthenticationService.new(email: '[email protected]', password: 'pass').authenticate
# if result.success?
# token = result.token
# end
#
# @author Development Team
# @since 1.0.0
class AuthenticationService
##
# Authenticates a user with email and password.
#
# @param email [String] User's email address
# @param password [String] User's password
# @return [AuthResult] Authentication result with token or errors
# @raise [AuthenticationError] When credentials are invalid
#
# @example Successful authentication
# service = AuthenticationService.new(email: '[email protected]', password: 'pass')
# result = service.authenticate
# result.token # => "eyJhbGc..."
#
def authenticate(email:, password:)
# Validate input parameters
raise ArgumentError, 'Email cannot be blank' if email.blank?
raise ArgumentError, 'Password cannot be blank' if password.blank?
# Find user by email (case-insensitive)
user = User.find_by('LOWER(email) = ?', email.downcase)
# Validate password using secure comparison
# Note: authenticate method uses bcrypt internally
return failure_result('Invalid credentials') unless user&.authenticate(password)
# Generate JWT token with 24-hour expiration
token = generate_token(user)
# Log successful authentication for security audit
log_authentication_event(user, :success)
success_result(user, token)
end
private
##
# Generates a JWT token for authenticated user.
#
# Token includes user ID, email, and expiration time.
# Uses HS256 algorithm with application secret.
#
# @param user [User] Authenticated user
# @return [String] JWT token
# @api private
def generate_token(user)
payload = {
user_id: user.id,
email: user.email,
exp: 24.hours.from_now.to_i
}
JWT.encode(payload, Rails.application.secret_key_base, 'HS256')
end
end
# Model Documentation
##
# Represents a registered user account.
#
# @!attribute [rw] email
# @return [String] User's email address (unique, case-insensitive)
# @!attribute [rw] name
# @return [String, nil] User's display name
# @!attribute [rw] password_digest
# @return [String] Encrypted password (never expose directly)
# @!attribute [r] created_at
# @return [ActiveSupport::TimeWithZone] Account creation timestamp
# @!attribute [r] updated_at
# @return [ActiveSupport::TimeWithZone] Last update timestamp
class User < ApplicationRecord
has_secure_password
# Associations
has_many :posts, dependent: :destroy
has_many :comments, dependent: :destroy
# Validations
validates :email, presence: true, uniqueness: { case_sensitive: false }
validates :email, format: { with: URI::MailTo::EMAIL_REGEXP }
validates :password, length: { minimum: 8 }, if: :password_required?
# Callbacks
before_save :normalize_email
private
##
# Normalizes email to lowercase for case-insensitive comparison.
# @api private
def normalize_email
self.email = email.downcase.strip
end
##
# Determines if password validation is required.
# @return [Boolean] true if new record or password is being changed
# @api private
def password_required?
new_record? || password.present?
end
end
## Integration Points
- JSDoc format for JavaScript
- TSDoc for TypeScript
- Type definitions in .d.ts files
- React component documentation
## JavaScript/TypeScript Standards
/**
* Authentication service for user login and session management.
*
* Handles the complete authentication lifecycle including login, logout,
* token generation, and session validation.
*
* @example
* ```typescript
* const authService = new AuthenticationService();
* const result = await authService.authenticate('[email protected]', 'password');
* if (result.success) {
* console.log('Token:', result.token);
* }
* ```
*
* @since 1.0.0
*/
export class AuthenticationService {
/**
* Authenticates a user with email and password.
*
* @param email - User's email address
* @param password - User's password
* @returns Promise resolving to authentication result with token or errors
* @throws {AuthenticationError} When credentials are invalid
* @throws {ValidationError} When email or password is missing
*
* @example
* ```typescript
* const result = await authService.authenticate('[email protected]', 'pass');
* if (result.success) {
* localStorage.setItem('token', result.token);
* }
* ```
*/
async authenticate(email: string, password: string): Promise<AuthResult> {
// Validate input parameters
if (!email || email.trim() === '') {
throw new ValidationError('Email cannot be blank');
}
if (!password || password.trim() === '') {
throw new ValidationError('Password cannot be blank');
}
try {
// Call authentication API endpoint
const response = await fetch('/api/auth/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ email, password })
});
// Handle authentication failure
if (!response.ok) {
const error = await response.json();
return { success: false, error: error.message };
}
// Parse successful response
const data = await response.json();
// Store token in secure storage
this.storeToken(data.token);
return { success: true, token: data.token, user: data.user };
} catch (error) {
// Log error for debugging (remove in production)
console.error('Authentication error:', error);
throw new AuthenticationError('Failed to authenticate user');
}
}
/**
* Securely stores authentication token.
*
* Uses localStorage for web, AsyncStorage for React Native.
* Token is stored with 24-hour expiration metadata.
*
* @param token - JWT authentication token
* @private
*/
private storeToken(token: string): void {
const expiresAt = new Date();
expiresAt.setHours(expiresAt.getHours() + 24);
const tokenData = {
token,
expiresAt: expiresAt.toISOString()
};
localStorage.setItem('auth_token', JSON.stringify(tokenData));
}
}
/**
* Authentication result returned from login attempt.
*/
interface AuthResult {
/** Whether authentication was successful */
success: boolean;
/** JWT token (only present on success) */
token?: string;
/** Authenticated user data (only present on success) */
user?: User;
/** Error message (only present on failure) */
error?: string;
}
/**
* User account information.
*/
interface User {
/** Unique user identifier */
id: number;
/** User's email address */
email: string;
/** User's display name (optional) */
name?: string;
/** Account creation timestamp */
createdAt: string;
}
// React Component Documentation
/**
* Login form component with email and password fields.
*
* Handles user authentication and displays validation errors.
* Redirects to dashboard on successful login.
*
* @component
* @example
* ```tsx
* <LoginForm
* onSuccess={(user) => navigate('/dashboard')}
* onError={(error) => showToast(error)}
* />
* ```
*/
interface LoginFormProps {
/** Callback invoked on successful authentication */
onSuccess?: (user: User) => void;
/** Callback invoked on authentication failure */
onError?: (error: string) => void;
/** Initial email value (optional) */
initialEmail?: string;
}
export const LoginForm: React.FC<LoginFormProps> = ({
onSuccess,
onError,
initialEmail = ''
}) => {
// Component implementation...
}
## Integration Points
- Docstring conventions (Google, NumPy, or Sphinx style)
- Type hints (PEP 484)
- Sphinx for API documentation
- Pydantic for data validation
## Python Documentation Standards
from typing import Optional, Dict, Any
from datetime import datetime, timedelta
class AuthenticationService:
"""
Manages user authentication and session handling.
This service handles the complete authentication lifecycle including
login, logout, token generation, and session validation.
Attributes:
secret_key: Secret key for JWT token generation
token_expiry: Token expiration time in hours (default: 24)
Example:
>>> service = AuthenticationService(secret_key='my-secret')
>>> result = service.authenticate('[email protected]', 'password')
>>> if result['success']:
... token = result['token']
Note:
This class is thread-safe and can be used across multiple requests.
Since:
1.0.0
"""
def __init__(self, secret_key: str, token_expiry: int = 24):
"""
Initialize authentication service.
Args:
secret_key: Secret key for JWT encoding/decoding
token_expiry: Token expiration time in hours
Raises:
ValueError: If secret_key is empty or None
"""
if not secret_key:
raise ValueError("Secret key cannot be empty")
self.secret_key = secret_key
self.token_expiry = token_expiry
def authenticate(
self,
email: str,
password: str
) -> Dict[str, Any]:
"""
Authenticate a user with email and password.
Validates credentials against the database and generates a JWT token
on successful authentication. Logs all authentication attempts for
security auditing.
Args:
email: User's email address (case-insensitive)
password: User's password (plain text, will be hashed for comparison)
Returns:
Dictionary containing authentication result:
{
'success': bool,
'token': Optional[str],
'user': Optional[Dict],
'error': Optional[str]
}
Raises:
ValueError: If email or password is empty
AuthenticationError: If database connection fails
Example:
>>> result = service.authenticate('[email protected]', 'pass123')
>>> if result['success']:
... print(f"Token: {result['token']}")
... else:
... print(f"Error: {result['error']}")
Note:
- Email comparison is case-insensitive
- Password is hashed using bcrypt before comparison
- Failed attempts are rate-limited (max 5 per hour)
See Also:
generate_token: For token generation logic
validate_token: For token validation
"""
# Validate input parameters
if not email or not email.strip():
raise ValueError("Email cannot be blank")
if not password or not password.strip():
raise ValueError("Password cannot be blank")
# Normalize email to lowercase
email = email.lower().strip()
try:
# Query user from database
user = self._find_user_by_email(email)
if not user:
# Return generic error to prevent email enumeration
return {
'success': False,
'error': 'Invalid credentials'
}
# Verify password using secure comparison
if not self._verify_password(password, user['password_hash']):
# Log failed attempt for security monitoring
self._log_failed_attempt(email)
return {
'success': False,
'error': 'Invalid credentials'
}
# Generate JWT token with user claims
token = self._generate_token(user)
# Log successful authentication
self._log_successful_auth(user['id'])
return {
'success': True,
'token': token,
'user': {
'id': user['id'],
'email': user['email'],
'name': user['name']
}
}
except DatabaseError as e:
# Log database error for ops team
logger.error(f"Database error during authentication: {e}")
raise AuthenticationError("Authentication service unavailable")
def _generate_token(self, user: Dict[str, Any]) -> str:
"""
Generate JWT token for authenticated user.
Creates a JWT token containing user ID, email, and expiration time.
Uses HS256 algorithm with the service's secret key.
Args:
user: User dictionary with 'id' and 'email' keys
Returns:
Encoded JWT token string
Note:
This is a private method and should not be called directly.
Token format: {"user_id": int, "email": str, "exp": timestamp}
"""
payload = {
'user_id': user['id'],
'email': user['email'],
'exp': datetime.utcnow() + timedelta(hours=self.token_expiry)
}
return jwt.encode(payload, self.secret_key, algorithm='HS256')
[MCP_TOOLS]
<!-- Populated during skill creation based on: 1. User's installed MCP servers 2. User's selection for this skill Recommended for this skill (examples): - mcp__code-analyzer - Analyze code structure and complexity - mcp__ast-parser - Parse abstract syntax trees - mcp__documentation-linter - Check doc format compliance Note: Skills work without MCP servers, but functionality may be limited -->Before Documenting:
Documentation Quality:
Format Compliance:
Completeness:
See [TECH_STACK_SPECIFIC] sections above for complete examples.
# Good Comments - Explain WHY
class PaymentProcessor
def process_refund(payment)
# Refunds must be processed within 90 days of original payment
# per merchant agreement and credit card network rules
raise RefundExpiredError if payment.created_at < 90.days.ago
# Use idempotency key to prevent duplicate refunds if request is retried
idempotency_key = "refund-#{payment.id}-#{Time.current.to_i}"
# Stripe refund API is eventually consistent, so we mark as pending
# and use webhooks to confirm completion
payment.update!(status: :refund_pending)
stripe_refund = Stripe::Refund.create(
payment_intent: payment.stripe_id,
metadata: { idempotency_key: idempotency_key }
)
payment.update!(refund_id: stripe_refund.id)
end
end
# Bad Comments - State the obvious
class PaymentProcessor
def process_refund(payment)
# Check if payment is older than 90 days
raise RefundExpiredError if payment.created_at < 90.days.ago
# Create idempotency key
idempotency_key = "refund-#{payment.id}-#{Time.current.to_i}"
# Update payment status
payment.update!(status: :refund_pending)
# Create Stripe refund
stripe_refund = Stripe::Refund.create(
payment_intent: payment.stripe_id,
metadata: { idempotency_key: idempotency_key }
)
# Update payment with refund ID
payment.update!(refund_id: stripe_refund.id)
end
end
def calculate_shipping_cost(weight_kg: float, distance_km: float, zone: str) -> float:
"""
Calculate shipping cost using zone-based tiered pricing.
Algorithm:
1. Determine base rate from zone pricing matrix
2. Apply weight multiplier (increases by 10% per kg over 5kg)
3. Apply distance surcharge (2% per 100km over 500km)
4. Round up to nearest $0.05 for payment processing
Zone Pricing Matrix:
- Local (< 100km): $5.00 base
- Regional (100-500km): $10.00 base
- National (> 500km): $15.00 base
Args:
weight_kg: Package weight in kilograms
distance_km: Shipping distance in kilometers
zone: Shipping zone ('local', 'regional', 'national')
Returns:
Final shipping cost in dollars
Raises:
ValueError: If weight is negative or zone is invalid
Example:
>>> calculate_shipping_cost(2.5, 150, 'regional')
10.0
>>> calculate_shipping_cost(7, 600, 'national')
17.95
Note:
This algorithm is based on carrier contract dated 2026-01-01.
Update base rates annually or when contract is renegotiated.
"""
# Validate inputs
if weight_kg < 0:
raise ValueError("Weight cannot be negative")
# Zone base rates (in dollars)
base_rates = {
'local': 5.00,
'regional': 10.00,
'national': 15.00
}
if zone not in base_rates:
raise ValueError(f"Invalid zone: {zone}")
# Start with base rate for zone
cost = base_rates[zone]
# Apply weight multiplier for packages over 5kg
# Each additional kg adds 10% to base rate
if weight_kg > 5:
excess_weight = weight_kg - 5
weight_multiplier = 1 + (excess_weight * 0.10)
cost *= weight_multiplier
# Apply distance surcharge for long-distance shipments
# 2% per 100km over 500km
if distance_km > 500:
excess_distance = distance_km - 500
distance_surcharge = (excess_distance / 100) * 0.02
cost *= (1 + distance_surcharge)
# Round up to nearest $0.05 for payment processing
# This avoids fractional cent issues
cost = round(cost * 20) / 20
return cost
DO:
DON'T:
1. Header Comments
2. Function/Method Comments
3. Inline Comments
4. Block Comments
Remember: Code is read more often than written. Good documentation makes maintenance faster and reduces bugs.
tools
Session Handoff: Erstellt eine vollständige Zusammenfassung der aktuellen Session für einen sauberen Kontextwechsel. NUR bei explizitem Aufruf (/session-handoff). NICHT automatisch auslösen. Geeignet wenn der User die Session resetten will, den Kontext aufräumen will, oder bei ~120k Tokens angelangt ist.
development
Pre-Mortem Risk Analysis: Strukturierte Prospective-Hindsight-Übung um launch-blocking Risiken vor Commitment aufzudecken. Team stellt sich vor, das Produkt sei 14 Tage nach Launch gefloppt, und arbeitet rückwärts. Klassifiziert Risiken in Tigers (echt), Paper Tigers (hypothetisch), Elephants (unausgesprochen). Nutze diesen Skill vor Build-Commitment, bei zu hoher Stakeholder-Confidence, vor Major-Releases, oder wenn das Team vage Sorgen nicht artikulieren kann. Trigger: /pre-mortem, 'pre-mortem', 'risk analysis', 'was könnte schiefgehen', 'risiken vor launch'.
testing
Six-Sigma Atomicity Validator for create-spec stories
tools
UX pattern definition guidance for navigation, user flows, interactions, and accessibility