Project 5: Steering Rules Engine
Enforcing Project Standards: Teach AI your team’s conventions automatically
Project Metadata
| Attribute | Value |
|---|---|
| Difficulty | Level 2: Intermediate |
| Time Estimate | 1 Week (15-20 hours) |
| Primary Language | Markdown/JSON |
| Alternative Languages | YAML, TypeScript |
| Prerequisites | Projects 1-4, understanding of coding standards |
| Main Reference | “Clean Code” by Robert C. Martin |
Learning Objectives
By completing this project, you will:
- Understand steering file types - product.md, tech.md, structure.md and their purposes
- Master scope resolution - how global and workspace steering files interact
- Write effective constraints - positive framing that guides AI behavior
- Design conditional loading - use front matter for context-aware rules
- Build validation tools - ensure steering files are effective
Deep Theoretical Foundation
What Is Steering?
Steering files are persistent instructions that guide AI behavior across all sessions. Unlike one-time prompts, steering rules are always present - they’re the “muscle memory” you teach Kiro about your project.
┌─────────────────────────────────────────────────────────────────────┐
│ STEERING vs PROMPTING │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ ONE-TIME PROMPT STEERING FILE │
│ ──────────────── ───────────── │
│ │
│ You: "Use TypeScript strict mode" .kiro/steering/tech.md: │
│ AI: "OK, I'll use strict mode" "Always use TypeScript │
│ ...later... strict mode for all files" │
│ You: "Add a new endpoint" │
│ AI: *forgets about strict mode* AI: *checks steering* │
│ AI: *applies strict mode* │
│ ...every time, forever... │
│ │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ PROMPT DECAY │ │
│ │ │ │
│ │ Turn 1: ████████████████████████ 100% remembered │ │
│ │ Turn 5: ████████████░░░░░░░░░░░░ 50% remembered │ │
│ │ Turn 10: ████░░░░░░░░░░░░░░░░░░░░ 20% remembered │ │
│ │ Turn 20: ░░░░░░░░░░░░░░░░░░░░░░░░ 0% (pushed out) │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ STEERING PERSISTENCE │ │
│ │ │ │
│ │ Turn 1: ████████████████████████ 100% applied │ │
│ │ Turn 5: ████████████████████████ 100% applied │ │
│ │ Turn 10: ████████████████████████ 100% applied │ │
│ │ Turn 20: ████████████████████████ 100% applied │ │
│ │ Forever: ████████████████████████ 100% applied │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────┘
The Steering File Taxonomy
Kiro recognizes different types of steering files, each with a specific purpose:
┌─────────────────────────────────────────────────────────────────────┐
│ STEERING FILE TYPES │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ product.md - WHAT we're building │ │
│ ├─────────────────────────────────────────────────────────────┤ │
│ │ • Product vision and goals │ │
│ │ • User personas and needs │ │
│ │ • Feature priorities │ │
│ │ • Business constraints │ │
│ │ │ │
│ │ Example: "We're building a B2B SaaS. Enterprise security │ │
│ │ is paramount. Performance is secondary to reliability." │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ tech.md - HOW we build │ │
│ ├─────────────────────────────────────────────────────────────┤ │
│ │ • Language and framework choices │ │
│ │ • Coding standards and conventions │ │
│ │ • Testing requirements │ │
│ │ • Forbidden patterns │ │
│ │ │ │
│ │ Example: "TypeScript strict mode. Zod for validation. │ │
│ │ No any type. Vitest for tests. 80% coverage minimum." │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ structure.md - WHERE things go │ │
│ ├─────────────────────────────────────────────────────────────┤ │
│ │ • Directory structure conventions │ │
│ │ • File naming patterns │ │
│ │ • Module organization │ │
│ │ • Import/export rules │ │
│ │ │ │
│ │ Example: "Components in src/components/<Name>/<Name>.tsx. │ │
│ │ Tests colocated as <Name>.test.tsx. Barrel exports." │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ security.md - Safety requirements │ │
│ ├─────────────────────────────────────────────────────────────┤ │
│ │ • Authentication/authorization rules │ │
│ │ • Data handling requirements │ │
│ │ • Forbidden operations │ │
│ │ • Compliance constraints │ │
│ │ │ │
│ │ Example: "Never log PII. Always use parameterized queries. │ │
│ │ JWT tokens expire after 1 hour. RBAC for all endpoints." │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ Custom files (api-standards.md, testing.md, etc.) │ │
│ ├─────────────────────────────────────────────────────────────┤ │
│ │ • Any domain-specific standards │ │
│ │ • Team conventions │ │
│ │ • Project-specific rules │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────┘
Scope Resolution: Global vs Workspace
Steering files can exist at two levels with specific priority rules:
┌─────────────────────────────────────────────────────────────────────┐
│ SCOPE RESOLUTION │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ ~/.kiro/steering/ (GLOBAL - applies to all projects) │
│ ├── tech.md "Always use ESLint" │
│ ├── security.md "Never commit secrets" │
│ └── personal.md "I prefer verbose comments" │
│ │
│ ~/projects/my-app/.kiro/steering/ (WORKSPACE - this project only) │
│ ├── tech.md "Use React 18 with Server Components" │
│ ├── api-standards.md "REST endpoints follow /api/v1/*" │
│ └── testing.md "E2E tests with Playwright" │
│ │
│ RESOLUTION ORDER: │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ 1. Workspace steering loaded first (highest priority) │ │
│ │ 2. Global steering loaded second (fills gaps) │ │
│ │ 3. Same-named files: Workspace OVERRIDES global │ │
│ │ 4. Different files: Both apply │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │
│ EFFECTIVE RULES FOR ~/projects/my-app: │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ • "Use React 18..." (workspace tech.md wins) │ │
│ │ • "Never commit secrets" (global security.md applies) │ │
│ │ • "I prefer verbose comments" (global personal.md applies) │ │
│ │ • "REST endpoints..." (workspace api-standards.md applies) │ │
│ │ • "E2E tests..." (workspace testing.md applies) │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────┘
Front Matter: Conditional Loading
Steering files support YAML front matter for conditional loading:
┌─────────────────────────────────────────────────────────────────────┐
│ FRONT MATTER OPTIONS │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ --- │
│ inclusion: always # Load for every session │
│ --- │
│ │
│ --- │
│ inclusion: agent # Only load for specific agents │
│ agents: [sec-auditor, code-reviewer] │
│ --- │
│ │
│ --- │
│ inclusion: manual # Only when explicitly requested │
│ --- │
│ │
│ --- │
│ inclusion: path # Load when working in matching paths │
│ paths: [src/api/**, routes/**] │
│ --- │
│ │
│ EXAMPLE: api-standards.md │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ --- │ │
│ │ inclusion: path │ │
│ │ paths: ["src/api/**", "src/routes/**"] │ │
│ │ --- │ │
│ │ │ │
│ │ # API Standards │ │
│ │ │ │
│ │ All API endpoints MUST: │ │
│ │ - Use REST conventions │ │
│ │ - Return JSON responses │ │
│ │ - Include proper status codes │ │
│ │ ... │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │
│ Result: API standards only consume context when working on APIs │
│ │
└─────────────────────────────────────────────────────────────────────┘
Writing Effective Constraints
The way you phrase steering rules dramatically affects AI compliance:
┌─────────────────────────────────────────────────────────────────────┐
│ POSITIVE vs NEGATIVE CONSTRAINTS │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ NEGATIVE (Less Effective) POSITIVE (More Effective) │
│ ──────────────────────── ───────────────────────── │
│ │
│ "Don't use any type" → "Use explicit types or unknown" │
│ │
│ "Never use console.log" → "Use the logger utility from │
│ @/lib/logger for all output" │
│ │
│ "Don't write long → "Functions should have a single │
│ functions" responsibility and fit in │
│ 50 lines or less" │
│ │
│ "Avoid callbacks" → "Use async/await for all │
│ asynchronous operations" │
│ │
│ WHY POSITIVE WORKS BETTER: │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ 1. Tells AI WHAT to do, not just what to avoid │ │
│ │ 2. Provides concrete alternatives │ │
│ │ 3. Measurable compliance (50 lines vs "not long") │ │
│ │ 4. Reduces ambiguity ("any" vs "explicit types") │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │
│ INCLUDE EXAMPLES: │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ ## Error Handling │ │
│ │ │ │
│ │ All async functions must use try/catch with structured │ │
│ │ logging: │ │
│ │ │ │
│ │ ```typescript │ │
│ │ async function fetchUser(id: string): Promise<User> { │ │
│ │ try { │ │
│ │ return await db.users.findUnique({ where: { id } }); │ │
│ │ } catch (error) { │ │
│ │ logger.error('Failed to fetch user', { id, error }); │ │
│ │ throw new AppError('USER_NOT_FOUND', error); │ │
│ │ } │ │
│ │ } │ │
│ │ ``` │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────┘
Real-World Analogy: The Style Guide
Think of steering files like a comprehensive style guide for a publication:
| Publication Element | Steering Equivalent |
|---|---|
| AP Stylebook | Global steering (org-wide) |
| Brand Guidelines | Workspace steering (project-specific) |
| Editorial Notes | Conditional steering (context-specific) |
| Examples in Guide | Code snippets in steering files |
Just as writers internalize style guides, Kiro internalizes your steering rules.
Complete Project Specification
What You Are Building
A Steering Rules Engine that:
- Provides Complete Templates: Ready-to-use steering files for TypeScript projects
- Validates Steering Files: Checks for effectiveness and conflicts
- Tests Rule Enforcement: Verifies AI follows steering rules
- Manages Scope: Tools for global vs. workspace steering
- Generates Documentation: Auto-document steering rules
Architecture Overview
┌─────────────────────────────────────────────────────────────────────┐
│ STEERING ENGINE ARCHITECTURE │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ CLI Interface │ │
│ │ ┌───────────────────────────────────────────────────────┐ │ │
│ │ │ steering-engine [command] │ │ │
│ │ │ │ │ │
│ │ │ Commands: │ │ │
│ │ │ init Setup steering for project │ │ │
│ │ │ validate Check steering files for issues │ │ │
│ │ │ test Verify rules are enforced │ │ │
│ │ │ list Show all active steering rules │ │ │
│ │ │ effective Show merged rules for current context │ │ │
│ │ │ share Export steering for team │ │ │
│ │ └───────────────────────────────────────────────────────┘ │ │
│ └──────────────────────────┬──────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ Steering Manager │ │
│ │ ┌───────────────────────────────────────────────────────┐ │ │
│ │ │ • Load global steering (~/.kiro/steering/) │ │ │
│ │ │ • Load workspace steering (.kiro/steering/) │ │ │
│ │ │ • Parse front matter │ │ │
│ │ │ • Resolve conflicts │ │ │
│ │ │ • Apply conditional loading │ │ │
│ │ └───────────────────────────────────────────────────────┘ │ │
│ └──────────────────────────┬──────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ Validators │ │
│ │ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐│ │
│ │ │ Syntax Checker │ │ Conflict Finder │ │ Quality Scorer ││ │
│ │ │ │ │ │ │ ││ │
│ │ │ • Front matter │ │ • Global vs │ │ • Positive ││ │
│ │ │ • Markdown │ │ workspace │ │ framing ││ │
│ │ │ • Code blocks │ │ • Rule overlap │ │ • Examples ││ │
│ │ └─────────────────┘ └─────────────────┘ └─────────────────┘│ │
│ └──────────────────────────┬──────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ Rule Tester │ │
│ │ ┌───────────────────────────────────────────────────────┐ │ │
│ │ │ • Generate test prompts that should violate rules │ │ │
│ │ │ • Send to Kiro with steering active │ │ │
│ │ │ • Check if AI refuses or complies with rules │ │ │
│ │ │ • Report enforcement rate │ │ │
│ │ └───────────────────────────────────────────────────────┘ │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────┘
Expected Deliverables
steering-engine/
├── src/
│ ├── index.ts # CLI entry point
│ ├── commands/
│ │ ├── init.ts # Setup steering
│ │ ├── validate.ts # Check files
│ │ ├── test.ts # Test enforcement
│ │ ├── list.ts # List rules
│ │ └── effective.ts # Show merged rules
│ ├── manager/
│ │ ├── SteeringLoader.ts # Load and parse
│ │ └── ScopeResolver.ts # Merge global/workspace
│ ├── validators/
│ │ ├── SyntaxChecker.ts # Validate format
│ │ ├── ConflictFinder.ts # Find conflicts
│ │ └── QualityScorer.ts # Rate effectiveness
│ └── templates/
│ ├── typescript/
│ │ ├── product.md
│ │ ├── tech.md
│ │ ├── structure.md
│ │ ├── security.md
│ │ ├── api-standards.md
│ │ └── testing.md
│ └── python/
│ ├── tech.md
│ └── ...
├── tests/
│ ├── loader.test.ts
│ └── validator.test.ts
├── package.json
└── README.md
# Creates in target project:
.kiro/steering/
├── product.md
├── tech.md
├── structure.md
├── security.md
├── api-standards.md
└── testing.md
Solution Architecture
Comprehensive Steering Templates
tech.md for TypeScript:
---
inclusion: always
---
# Technology Stack
## Languages & Frameworks
- **Language**: TypeScript 5.x in strict mode
- **Runtime**: Node.js 20+ or Bun 1.0+
- **Framework**: Express.js for APIs, React 18 for UI
- **Testing**: Vitest for unit tests, Playwright for E2E
## TypeScript Standards
### Type Safety
Use explicit types for all function parameters and return values:
```typescript
// CORRECT
function calculateTotal(items: CartItem[]): number {
return items.reduce((sum, item) => sum + item.price * item.quantity, 0);
}
// INCORRECT - implicit any
function calculateTotal(items) {
return items.reduce((sum, item) => sum + item.price * item.quantity, 0);
}
Validation
Use zod for runtime validation of external data:
import { z } from 'zod';
const UserSchema = z.object({
id: z.string().uuid(),
email: z.string().email(),
role: z.enum(['admin', 'user', 'guest'])
});
type User = z.infer<typeof UserSchema>;
function processUser(data: unknown): User {
return UserSchema.parse(data);
}
Error Handling
All async functions must use try/catch with structured logging:
import { logger } from '@/lib/logger';
import { AppError } from '@/lib/errors';
async function fetchUser(id: string): Promise<User> {
try {
const user = await db.users.findUnique({ where: { id } });
if (!user) {
throw new AppError('USER_NOT_FOUND', `User ${id} not found`);
}
return user;
} catch (error) {
logger.error('Failed to fetch user', { id, error });
throw error instanceof AppError ? error : new AppError('DB_ERROR', error);
}
}
Forbidden Patterns
- Do NOT use
anytype; useunknownand narrow with type guards - Do NOT use callbacks; use async/await for all asynchronous operations
- Do NOT use
console.login production code; use the logger utility - Do NOT use
var; useconstby default,letonly when reassignment needed - Do NOT commit
.envfiles; use.env.exampleas template ```
security.md:
---
inclusion: always
---
# Security Standards
## Authentication & Authorization
### Token Handling
- JWT tokens MUST expire after 1 hour
- Refresh tokens MUST be stored in httpOnly cookies
- NEVER store tokens in localStorage or sessionStorage
```typescript
// CORRECT: httpOnly cookie
res.cookie('refreshToken', token, {
httpOnly: true,
secure: process.env.NODE_ENV === 'production',
sameSite: 'strict',
maxAge: 7 * 24 * 60 * 60 * 1000 // 7 days
});
// INCORRECT: localStorage
localStorage.setItem('token', token); // NEVER do this
Authorization
All API endpoints MUST verify user permissions:
async function updateUser(req: Request, res: Response) {
const { userId } = req.params;
// ALWAYS check authorization
if (req.user.id !== userId && req.user.role !== 'admin') {
throw new ForbiddenError('Cannot update other users');
}
// Proceed with update...
}
Data Protection
Query Safety
ALWAYS use parameterized queries to prevent SQL injection:
// CORRECT: Parameterized
const user = await db.query(
'SELECT * FROM users WHERE id = $1',
[userId]
);
// INCORRECT: String concatenation
const user = await db.query(
`SELECT * FROM users WHERE id = '${userId}'` // SQL INJECTION RISK
);
Sensitive Data
- NEVER log PII (emails, names, addresses)
- NEVER include secrets in error messages
- ALWAYS sanitize user input before display (XSS prevention)
// Logging - mask sensitive data
logger.info('User action', {
userId: user.id,
email: maskEmail(user.email), // shows j***@example.com
action: 'login'
});
Forbidden Operations
- NEVER disable HTTPS in production
- NEVER use
eval()ornew Function() - NEVER expose stack traces to users
- NEVER store passwords in plain text
- NEVER commit credentials to git ```
Steering Validation Logic
┌─────────────────────────────────────────────────────────────────────┐
│ VALIDATION CHECKS │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ 1. SYNTAX VALIDATION │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ • Valid YAML front matter │ │
│ │ • Proper markdown structure │ │
│ │ • Code blocks have language tags │ │
│ │ • No broken links to other files │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │
│ 2. CONFLICT DETECTION │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ Global tech.md: "Use Express.js" │ │
│ │ Workspace tech.md: "Use Fastify" │ │
│ │ → WARNING: Conflicting framework requirements │ │
│ │ │ │
│ │ Global security.md: "Tokens expire in 1 hour" │ │
│ │ Workspace security.md: "Tokens expire in 24 hours" │ │
│ │ → WARNING: Security policy conflict │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │
│ 3. QUALITY SCORING │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ Criteria Points │ │
│ │ ───────────────────────────────────────────────────────── │ │
│ │ Has code examples +2 per example │ │
│ │ Uses positive framing +1 per rule │ │
│ │ Uses negative framing -1 per rule │ │
│ │ Has measurable criteria +1 per rule │ │
│ │ Has front matter +1 │ │
│ │ Has clear sections +1 │ │
│ │ │ │
│ │ Score interpretation: │ │
│ │ • 0-5: Needs improvement │ │
│ │ • 6-10: Adequate │ │
│ │ • 11-15: Good │ │
│ │ • 16+: Excellent │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────┘
Rule Enforcement Testing
┌─────────────────────────────────────────────────────────────────────┐
│ ENFORCEMENT TEST FLOW │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ Steering rule: "Never use console.log; use logger utility" │
│ │
│ TEST PROMPT: │
│ "Add a debug statement to print the user object" │
│ │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ EXPECTED RESPONSE (rule followed): │ │
│ │ │ │
│ │ "I'll add a debug statement using the logger utility: │ │
│ │ logger.debug('User object', { user });" │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ UNEXPECTED RESPONSE (rule violated): │ │
│ │ │ │
│ │ "I'll add a console.log: │ │
│ │ console.log(user);" │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │
│ AUTOMATED TEST: │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ function testRule(rule: Rule): TestResult { │ │
│ │ const prompt = generateViolationPrompt(rule); │ │
│ │ const response = await kiro.send(prompt); │ │
│ │ │ │
│ │ // Check if response violates rule │ │
│ │ const violated = checkForViolation(response, rule); │ │
│ │ │ │
│ │ return { │ │
│ │ rule: rule.name, │ │
│ │ passed: !violated, │ │
│ │ response: response │ │
│ │ }; │ │
│ │ } │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────┘
Phased Implementation Guide
Phase 1: Template Creation (3-4 hours)
Goal: Create comprehensive steering templates
What to Build:
- Complete tech.md template
- Security.md template
- Structure.md template
- At least 3 domain-specific templates
Hint 1: Start with your actual project standards:
# Document what you repeatedly tell AI
cat ~/.bash_history | grep -i "always use\|never use\|make sure" | sort | uniq
Hint 2: Include both rules and examples:
## Naming Conventions
### Files
- Components: `PascalCase.tsx` (e.g., `UserProfile.tsx`)
- Utilities: `camelCase.ts` (e.g., `formatDate.ts`)
- Constants: `SCREAMING_SNAKE_CASE.ts` (e.g., `API_ENDPOINTS.ts`)
### Variables
```typescript
// Components - PascalCase
const UserProfile = () => { ... };
// Hooks - camelCase starting with 'use'
const useUserData = () => { ... };
// Constants - SCREAMING_SNAKE_CASE
const MAX_RETRIES = 3;
Validation Checkpoint: You have 5+ steering files with examples.
Phase 2: Validation System (4-5 hours)
Goal: Build validators for steering files
What to Build:
- Front matter parser and validator
- Conflict detection between scopes
- Quality scoring algorithm
Hint 1: Parse YAML front matter:
import matter from 'gray-matter';
interface SteeringFrontMatter {
inclusion?: 'always' | 'agent' | 'manual' | 'path';
agents?: string[];
paths?: string[];
}
function parseSteeringFile(content: string): {
frontMatter: SteeringFrontMatter;
body: string;
} {
const { data, content: body } = matter(content);
return {
frontMatter: data as SteeringFrontMatter,
body
};
}
Hint 2: Detect conflicting rules:
function findConflicts(
global: SteeringFile[],
workspace: SteeringFile[]
): Conflict[] {
const conflicts: Conflict[] = [];
for (const ws of workspace) {
const globalMatch = global.find(g => g.name === ws.name);
if (globalMatch) {
// Same file exists in both scopes
const diffs = findDifferences(globalMatch.content, ws.content);
if (diffs.length > 0) {
conflicts.push({
file: ws.name,
global: globalMatch.content,
workspace: ws.content,
differences: diffs
});
}
}
}
return conflicts;
}
Hint 3: Score quality with heuristics:
function scoreQuality(content: string): QualityReport {
const score = {
total: 0,
breakdown: {} as Record<string, number>
};
// Check for code examples
const codeBlocks = (content.match(/```/g) || []).length / 2;
score.breakdown.codeExamples = Math.min(codeBlocks * 2, 10);
score.total += score.breakdown.codeExamples;
// Check for positive framing
const positivePatterns = /\b(use|prefer|always|should|must|ensure)\b/gi;
const negativePatterns = /\b(don't|never|avoid|do not|cannot)\b/gi;
const positive = (content.match(positivePatterns) || []).length;
const negative = (content.match(negativePatterns) || []).length;
score.breakdown.framing = Math.max(0, positive - negative);
score.total += Math.min(score.breakdown.framing, 5);
// Check for measurable criteria
const measurable = /\b(\d+ (lines?|characters?|%|percent|seconds?|ms))\b/gi;
score.breakdown.measurable = (content.match(measurable) || []).length;
score.total += Math.min(score.breakdown.measurable, 5);
return score;
}
Validation Checkpoint: Running steering-engine validate shows issues and quality scores.
Phase 3: Testing and CLI (4-5 hours)
Goal: Build enforcement testing and CLI
What to Build:
- Rule enforcement tester
- Full CLI interface
- Team sharing export
Hint 1: Generate violation prompts from rules:
function generateViolationPrompt(rule: string): string {
// Parse the rule to understand what it forbids
const patterns = {
'console.log': 'Add a debug statement to log the current state',
'any type': 'Add a function that accepts any argument',
'var ': 'Declare a counter variable',
'callback': 'Add an async operation to fetch data'
};
for (const [pattern, prompt] of Object.entries(patterns)) {
if (rule.toLowerCase().includes(pattern)) {
return prompt;
}
}
return 'Generate code that might violate the project standards';
}
Hint 2: Check responses for violations:
function checkForViolation(response: string, rule: Rule): boolean {
// Extract code from response
const codeBlocks = response.match(/```[\s\S]*?```/g) || [];
const code = codeBlocks.join('\n');
// Check for violation patterns
for (const pattern of rule.violationPatterns) {
if (new RegExp(pattern, 'i').test(code)) {
return true; // Violation found
}
}
return false; // No violation
}
Hint 3: Export for team sharing:
async function exportForTeam(outputPath: string): Promise<void> {
const steeringDir = '.kiro/steering';
const files = await glob(`${steeringDir}/**/*.md`);
// Create a single merged document
let output = '# Team Steering Rules\n\n';
output += `Generated: ${new Date().toISOString()}\n\n`;
for (const file of files) {
const content = await fs.readFile(file, 'utf8');
const { frontMatter, body } = parseSteeringFile(content);
output += `## ${path.basename(file)}\n\n`;
output += body + '\n\n---\n\n';
}
await fs.writeFile(outputPath, output);
console.log(`Exported to ${outputPath}`);
}
Validation Checkpoint: Full CLI works, tests pass, team export generates correctly.
Testing Strategy
Unit Tests
// tests/validator.test.ts
describe('Steering Validator', () => {
describe('Front Matter Parsing', () => {
it('parses valid front matter', () => {
const content = `---
inclusion: always
---
# Rules`;
const { frontMatter } = parseSteeringFile(content);
expect(frontMatter.inclusion).toBe('always');
});
it('handles missing front matter', () => {
const content = '# Rules\n\nSome rules here';
const { frontMatter, body } = parseSteeringFile(content);
expect(frontMatter).toEqual({});
expect(body).toContain('# Rules');
});
});
describe('Quality Scoring', () => {
it('scores higher for code examples', () => {
const withExamples = `# Rules\n\`\`\`typescript\nconst x = 1;\n\`\`\``;
const withoutExamples = '# Rules\n\nNo examples here';
const scoreWith = scoreQuality(withExamples);
const scoreWithout = scoreQuality(withoutExamples);
expect(scoreWith.total).toBeGreaterThan(scoreWithout.total);
});
it('penalizes negative framing', () => {
const positive = 'Always use strict mode';
const negative = 'Never use any type. Do not use var.';
const scorePos = scoreQuality(positive);
const scoreNeg = scoreQuality(negative);
expect(scorePos.breakdown.framing).toBeGreaterThan(scoreNeg.breakdown.framing);
});
});
});
Integration Tests
describe('Steering Integration', () => {
it('detects conflicts between global and workspace', async () => {
// Setup
await fs.writeFile(
path.join(HOME, '.kiro/steering/tech.md'),
'Use Express.js for APIs'
);
await fs.writeFile(
'.kiro/steering/tech.md',
'Use Fastify for APIs'
);
// Test
const result = await validateSteering();
expect(result.conflicts).toHaveLength(1);
expect(result.conflicts[0].file).toBe('tech.md');
});
it('merges non-conflicting rules correctly', async () => {
const effective = await getEffectiveRules();
// Global rules should apply when no workspace override
expect(effective).toContain('Global rule');
// Workspace rules should override
expect(effective).toContain('Workspace override');
});
});
Common Pitfalls and Debugging
Pitfall 1: Rules Not Applied
Symptom: AI ignores steering rules
Cause: Front matter misconfigured or file not in correct location
Debug:
# Check if steering files are loaded
kiro-cli /steering show
# Verify file locations
ls -la .kiro/steering/
ls -la ~/.kiro/steering/
Solution: Ensure files are in correct directory with proper front matter.
Pitfall 2: Too Many Tokens
Symptom: Context warnings, steering files consuming too much space
Cause: Steering files too verbose or all set to inclusion: always
Debug:
// Count tokens in steering
const files = await glob('.kiro/steering/*.md');
for (const file of files) {
const content = await fs.readFile(file, 'utf8');
const tokens = countTokens(content);
console.log(`${file}: ${tokens} tokens`);
}
Solution: Use conditional loading:
---
inclusion: path
paths: ["src/api/**"]
---
Pitfall 3: Conflicting Rules
Symptom: AI produces inconsistent output
Cause: Global and workspace rules contradict
Debug:
steering-engine effective # Show merged rules
steering-engine validate # Check for conflicts
Solution: Workspace should completely override global when they conflict:
<!-- workspace tech.md -->
---
override: true # Completely replaces global tech.md
---
Extensions and Challenges
Extension 1: Rule Inheritance
Create base templates that projects extend:
---
extends: "@company/base-typescript"
---
# Project-specific overrides
Extension 2: Dynamic Rules
Rules that change based on context:
---
inclusion: dynamic
condition: "git.branch === 'main'"
---
# Production Branch Rules
When on main branch, additional safety checks apply:
- All changes require tests
- No direct commits (PRs only)
Extension 3: Rule Analytics
Track which rules are most often violated:
interface RuleMetrics {
ruleId: string;
violations: number;
lastViolation: Date;
commonContexts: string[];
}
function trackViolation(rule: string, context: string): void {
// Log to analytics
}
Challenge: AI Rule Suggester
Use AI to suggest new steering rules based on code review feedback:
Input: "You used console.log again. Please use the logger."
Suggested Rule: "Use logger utility (@/lib/logger) instead of console.log for all output"
Real-World Connections
How Professionals Use This
- Enterprise Teams: Share steering configs via MDM/group policy
- Open Source: Include steering in repo for contributor consistency
- Consulting: Bring steering templates to client projects
- Education: Use steering to enforce learning patterns
Industry Patterns
Configuration as Code: Steering files are declarative configuration, similar to .eslintrc, tsconfig.json, or Dockerfile.
Policy as Code: Like AWS Config Rules or Open Policy Agent, steering encodes policies that govern behavior.
Documentation as Code: Living documentation that actively influences the system.
Self-Assessment Checklist
Understanding Verification
- Can you explain the difference between product.md and tech.md?
- product.md: What we’re building (vision, users, priorities)
- tech.md: How we build it (languages, patterns, tools)
- How does scope resolution work?
- Workspace steering has priority
- Same-named files: workspace overrides global
- Different files: both apply
- Why is positive framing more effective?
- Tells AI what TO do, not just what to avoid
- Provides concrete alternatives
- More measurable compliance
- When should you use conditional loading?
- Domain-specific rules (API, UI, tests)
- Large rule sets that don’t always apply
- Performance-sensitive contexts
Skill Demonstration
- I can create comprehensive steering files
- I can validate steering for quality and conflicts
- I can test rule enforcement
- I can manage global vs. workspace scope
- I can share steering configurations with teams
Interview Preparation
Be ready to answer:
- “How would you encode coding standards for an AI assistant?”
- “What’s the difference between positive and negative constraints?”
- “How do you balance flexibility with enforcement?”
- “How would you share coding standards across a team?”
Recommended Reading
| Topic | Resource | Why It Helps |
|---|---|---|
| Clean Code | “Clean Code” by Martin, Ch. 1-5 | Standards to encode |
| Configuration | “The Pragmatic Programmer” Ch. 4 | Config management patterns |
| Documentation | “Docs for Developers” by Gentle et al. | Writing effective docs |
| Policy | “Effective TypeScript” by Vanderkam | TypeScript conventions |
| Team Standards | Google Style Guides | Industry-standard patterns |
What Success Looks Like
When you complete this project, you will have:
- Comprehensive Steering: Full set of steering files for your stack
- Validation Tools: CLI to check and score steering quality
- Test Framework: Verify AI follows your rules
- Team Sharing: Export and distribute steering configs
- Deep Understanding: Know how to guide AI behavior persistently
Next Steps: Move to Project 6 (MCP Server Connector) to learn how to integrate external tools with Kiro.