OGT Docs - Rules
Root skill for managing project rules and standards. Rules define enforceable conventions that all contributors (human and AI) must follow.
Overview
Rules are the codified standards of a project. Unlike guidelines (suggestions) or documentation (information), rules are enforceable and verifiable. They live in docs/rules/ and are organized by domain.
flowchart TB
subgraph rules ["docs/rules/"]
direction TB
CODE["code/"]
GIT["git/"]
INFRA["infra/"]
DOCS["docs/"]
STYLE["style/"]
end
subgraph skills ["Specialized Skills"]
S1["ogt-docs-rules-code"]
S2["ogt-docs-rules-git"]
S3["ogt-docs-rules-code-front"]
S4["ogt-docs-rules-code-back"]
S5["ogt-docs-rules-code-infra"]
end
CODE --> S1
CODE --> S3
CODE --> S4
CODE --> S5
GIT --> S2
INFRA --> S5
When to Use
- Creating new project rules or standards
- Updating existing rules
- Looking up rules for a specific domain
- Enforcing rules during code review
- Setting up rules for a new project
Sub-Skills
| Skill | Domain | Use When |
|---|---|---|
ogt-docs-rules-code | Coding standards | TypeScript, naming, patterns, error handling |
ogt-docs-rules-code-front | Frontend code | React, components, state, styling |
ogt-docs-rules-code-back | Backend code | API design, database, services |
ogt-docs-rules-code-infra | Infrastructure | Docker, CI/CD, deployment |
ogt-docs-rules-git | Git workflow | Commits, branches, PRs, reviews |
Folder Structure
docs/rules/
├── code/ # Coding standards
│ ├── general/ # Cross-cutting code rules
│ │ ├── rule.md # Primary rule definition
│ │ ├── examples.md # Good/bad examples
│ │ ├── .version # Schema version
│ │ └── .enforced_by # How it's enforced (eslint, tsc, etc.)
│ │
│ ├── typescript/ # TypeScript-specific rules
│ │ ├── rule.md
│ │ ├── examples.md
│ │ └── .enforced_by
│ │
│ ├── naming/ # Naming conventions
│ │ ├── rule.md
│ │ ├── examples.md
│ │ └── .enforced_by
│ │
│ ├── front/ # Frontend-specific rules
│ │ ├── components/
│ │ ├── state/
│ │ ├── styling/
│ │ └── routing/
│ │
│ ├── back/ # Backend-specific rules
│ │ ├── api/
│ │ ├── database/
│ │ └── services/
│ │
│ └── infra/ # Infrastructure rules
│ ├── docker/
│ ├── ci/
│ └── deployment/
│
├── git/ # Git workflow rules
│ ├── commits/ # Commit message format
│ │ ├── rule.md
│ │ ├── examples.md
│ │ └── .enforced_by
│ │
│ ├── branches/ # Branch naming
│ │ ├── rule.md
│ │ └── examples.md
│ │
│ ├── pull_requests/ # PR requirements
│ │ ├── rule.md
│ │ └── template.md
│ │
│ └── reviews/ # Code review standards
│ ├── rule.md
│ └── checklist.md
│
├── docs/ # Documentation rules
│ ├── structure/ # Folder organization
│ ├── formatting/ # Markdown standards
│ └── comments/ # Code comments
│
└── style/ # Style guides
├── ui/ # UI/UX consistency
├── api/ # API style
└── naming/ # Naming conventions
Rule Definition Structure
Every rule is a folder containing at minimum a rule.md file.
rule.md Template
# Rule: {Rule Name}
## Summary
One sentence stating the rule clearly and unambiguously.
## Rationale
Why this rule exists. What problems it prevents. What benefits it provides.
## The Rule
Clear, specific statement of what MUST, SHOULD, or MUST NOT be done.
Use RFC 2119 keywords:
- **MUST** / **REQUIRED** - Absolute requirement
- **MUST NOT** / **SHALL NOT** - Absolute prohibition
- **SHOULD** / **RECOMMENDED** - Recommended but exceptions exist
- **SHOULD NOT** - Not recommended but exceptions exist
- **MAY** / **OPTIONAL** - Truly optional
## Examples
### Correct
{Show correct usage}
### Incorrect
{Show incorrect usage with explanation}
## Exceptions
Documented cases where this rule may be relaxed.
## Enforcement
How this rule is enforced:
- Automated (linter, type checker, CI)
- Manual (code review)
- Both
## References
- Related rules
- External standards
- Tooling documentation
Example: docs/rules/code/typescript/
Complete example of a TypeScript rules folder.
Folder Structure
docs/rules/code/typescript/
├── rule.md
├── examples.md
├── .version
└── .enforced_by
rule.md
# Rule: TypeScript Strict Mode
## Summary
All TypeScript code MUST compile with strict mode enabled.
## Rationale
Strict mode catches common errors at compile time:
- Implicit any types
- Null/undefined access
- Unused variables
- Missing return types
This prevents runtime errors and improves code quality.
## The Rule
1. **MUST** enable `"strict": true` in tsconfig.json
2. **MUST NOT** use `@ts-ignore` without explanatory comment
3. **MUST NOT** use `any` type except in documented exceptions
4. **SHOULD** prefer `unknown` over `any` for unknown types
5. **MUST** fix all TypeScript errors before committing
## Examples
### Correct
```typescript
// Explicit types
function calculateTotal(items: CartItem[]): number {
return items.reduce((sum, item) => sum + item.price, 0);
}
// Unknown instead of any
function parseJSON(text: string): unknown {
return JSON.parse(text);
}
// Type guards for unknown
function isUser(value: unknown): value is User {
return typeof value === "object" && value !== null && "id" in value;
}
```
Incorrect
// Implicit any - TS7006
function process(data) {
// ERROR: Parameter 'data' implicitly has 'any' type
return data.value;
}
// Using any
function handleResponse(response: any) {
// AVOID: Use unknown or specific type
return response.data;
}
// ts-ignore without comment
// @ts-ignore // BAD: No explanation
const result = brokenLibrary.call();
Exceptions
- Third-party library definitions that require
any - Migration of legacy JavaScript (with tracking issue)
- Test mocks where type safety is less critical
When using exceptions, add comment:
// Exception: Legacy API returns untyped response (see #123)
const data = legacyApi.fetch() as any;
Enforcement
- Automated:
tsc --noEmitin pre-commit hook and CI - Automated: ESLint
@typescript-eslint/no-explicit-any - Manual: Code review for exception documentation
References
- TypeScript Strict Mode
- docs/rules/code/general/ - General coding standards
### examples.md
```markdown
# TypeScript Examples
Extended examples for TypeScript rules.
## Type Inference vs Explicit Types
### When to Use Explicit Types
```typescript
// Function return types - ALWAYS explicit
function fetchUser(id: string): Promise<User> {
return api.get(`/users/${id}`);
}
// Exported constants - ALWAYS explicit
export const DEFAULT_TIMEOUT: number = 5000;
// Complex objects - ALWAYS explicit
const config: AppConfig = {
apiUrl: process.env.API_URL,
timeout: 5000,
};
When Inference is Acceptable
// Local variables with obvious types
const count = 0; // Inferred as number
const name = "test"; // Inferred as string
const items = [1, 2, 3]; // Inferred as number[]
// Arrow functions in callbacks
users.filter((user) => user.active); // Parameter type inferred from array
Union Types
Discriminated Unions
// CORRECT: Use discriminated unions
type Result<T> = { success: true; data: T } | { success: false; error: Error };
function handleResult<T>(result: Result<T>) {
if (result.success) {
console.log(result.data); // TypeScript knows data exists
} else {
console.error(result.error); // TypeScript knows error exists
}
}
Avoid Loose Unions
// INCORRECT: Loose union without discriminant
type Response = {
data?: User;
error?: Error;
};
// Problem: Both could be undefined, or both could be set
Generic Constraints
// CORRECT: Constrain generics appropriately
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
return obj[key];
}
// INCORRECT: Unconstrained generic
function getValue<T>(obj: T, key: string): any {
// Returns any!
return (obj as any)[key];
}
### .version
```json
{ "schema": "1.0", "created": "2026-01-15T10:00:00Z", "updated": "2026-02-01T14:00:00Z" }
.enforced_by
tsc --noEmit
eslint --ext .ts,.tsx
pre-commit hook
CI pipeline
Example: docs/rules/git/commits/
Complete example of commit message rules.
Folder Structure
docs/rules/git/commits/
├── rule.md
├── examples.md
├── .version
└── .enforced_by
rule.md
# Rule: Commit Message Format
## Summary
All commit messages MUST follow Conventional Commits format.
## Rationale
Consistent commit messages enable:
- Automated changelog generation
- Semantic versioning automation
- Easy history navigation
- Clear communication of changes
## The Rule
### Format
<type>(<scope>): <description>
[optional body]
[optional footer(s)]
### Types
| Type | Use When |
|------|----------|
| `feat` | New feature |
| `fix` | Bug fix |
| `docs` | Documentation only |
| `style` | Formatting, no code change |
| `refactor` | Code change that neither fixes nor adds |
| `perf` | Performance improvement |
| `test` | Adding/updating tests |
| `chore` | Maintenance tasks |
| `ci` | CI/CD changes |
### Requirements
1. **MUST** use lowercase type
2. **MUST** use imperative mood in description ("add" not "added")
3. **MUST** limit first line to 72 characters
4. **SHOULD** include scope for clarity
5. **MUST** reference issue number in footer for bug fixes
6. **MUST** mark breaking changes with `!` or `BREAKING CHANGE:` footer
## Examples
### Correct
feat(auth): add Steam OAuth provider
Implement Steam OpenID authentication flow. Follows existing Google/Discord pattern.
Closes #456
fix(api): handle null response from legacy endpoint
The /v0/users endpoint can return null for deleted users. Add null check and return 404 instead of 500.
Fixes #789
refactor(components): extract CardBase from entity cards
DRY refactor - all entity cards now extend CardBase. No functional changes.
feat(api)!: change authentication header format
BREAKING CHANGE: Authorization header now requires "Bearer " prefix. Migration guide: docs/guides/auth_migration.md
### Incorrect
Fixed bug // No type, no scope, vague
FEAT: Add new feature // Wrong case
feat(auth): Added Steam OAuth // Past tense
feat: implement the new user authentication system with Steam support and update all related components // Too long
## Exceptions
- Merge commits may use default message
- Revert commits may use git's default format
## Enforcement
- **Automated**: commitlint in pre-commit hook
- **Automated**: CI check on PR
- **Manual**: PR review
## References
- [Conventional Commits](https://www.conventionalcommits.org/)
- [Angular Commit Guidelines](https://github.com/angular/angular/blob/main/CONTRIBUTING.md#commit)
examples.md
# Commit Message Examples
## Feature Commits
feat(creatures): add CR filtering to creature list
Allow filtering creatures by Challenge Rating range. Uses dual-handle slider component.
Related: #234
feat(search): implement fuzzy search with MiniSearch
Replace substring matching with indexed fuzzy search. Results ranked by relevance score.
Performance: <16ms for 10k entries
Closes #567
## Bug Fix Commits
fix(cards): prevent image flash on card hover
Card images were reloading on every hover due to missing key prop in map. Add stable key based on entity slug.
Fixes #890
fix(auth): handle expired refresh tokens gracefully
When refresh token expires, redirect to login instead of showing error page. Clear local storage on redirect.
Fixes #891 Fixes #892
## Refactor Commits
refactor(services): extract API client from services
Move HTTP logic to dedicated ApiClient class. Services now use dependency injection.
No functional changes. Improves testability.
## Documentation Commits
docs(readme): add Docker setup instructions
Document docker-compose workflow for new developers. Include troubleshooting section for common issues.
## Breaking Change Commits
feat(api)!: rename /monsters to /creatures
BREAKING CHANGE: All /api/monsters/_ endpoints now at /api/creatures/_
Migration:
- Update all fetch calls
- /monsters/:slug -> /creatures/:slug
- Search params unchanged
Deprecation notice sent 2026-01-01. Old endpoints removed 2026-02-01.
.enforced_by
commitlint
husky pre-commit
GitHub Actions CI
Creating New Rules
flowchart TD
A[Identify Need for Rule] --> B{Rule Type?}
B -->|Code| C[docs/rules/code/]
B -->|Git| D[docs/rules/git/]
B -->|Docs| E[docs/rules/docs/]
B -->|Style| F[docs/rules/style/]
C --> G[Create Rule Folder]
D --> G
E --> G
F --> G
G --> H[Write rule.md]
H --> I[Add examples.md]
I --> J[Set .version]
J --> K[Document .enforced_by]
K --> L{Automated Enforcement?}
L -->|Yes| M[Configure Tooling]
L -->|No| N[Add to Review Checklist]
M --> O[Add to CI]
N --> O
O --> P[Announce to Team]
Steps
- Choose domain: code/, git/, docs/, style/
- Create folder:
docs/rules/{domain}/{rule_name}/ - Write rule.md: Use template above
- Add examples.md: Comprehensive good/bad examples
- Create .version: Schema versioning
- Document .enforced_by: List enforcement mechanisms
- Configure automation: ESLint rules, commit hooks, CI checks
- Announce: Communicate new rule to team
Signal Files Reference
| Signal | Type | Content | Purpose |
|---|---|---|---|
.version | Content | JSON with schema, created, updated | Track rule version |
.enforced_by | Content | List of tools/processes | Document enforcement |
.deprecated | Empty | - | Mark rule as deprecated |
.superseded_by | Content | Path to new rule | Point to replacement |
.approved_by_{name} | Empty | - | Track approval |
Rule Quality Checklist
Before finalizing a rule:
- Summary is one clear sentence
- Rationale explains WHY (not just what)
- Uses RFC 2119 keywords correctly
- Has at least 2 correct examples
- Has at least 2 incorrect examples with explanations
- Exceptions are documented
- Enforcement mechanism is specified
- Examples compile/work if code
- No ambiguity in requirements
- Cross-referenced with related rules