Gemini Rules Validator Skill
How to Use
When you want to validate code compliance:
/rule-validator src/features/myFeature /rule-validator <file-or-folder> /validate # Validate current selection
Validation Scope
1. Core Rules (src/*)
Applies to all code in the repo.
Rule 1: No Hardcoded Game Data
Forbidden patterns:
- •Plant names:
'Carrot','Tomato', etc. - •Item IDs: hardcoded numeric item IDs
- •Pet/mutation/shop data as strings/numbers
- •Sprite frame references as literals
Check for:
// ❌ BAD
const plantName = 'Carrot';
const itemId = 42;
const spriteName = 'plant_carrot_lvl1';
// ✅ GOOD
const plant = MGData.get('plants')['carrot'];
const item = MGData.get('items')[itemId];
const sprite = await MGSprite.show('plant', plantName);
What to report:
- •File + line number
- •The hardcoded value
- •Suggested fix: "Use MGData.get(...) instead"
Rule 2: Boundaries (DOM, WebSocket, State)
Forbidden:
- •DOM rendering outside
src/ui/ - •Direct WebSocket sends (
websocket.send()outsidesrc/websocket/api.ts) - •Direct state mutations (not via Store API)
- •Ad-hoc globals (
window.myGlobal = ...as second source of truth)
Check for:
// ❌ BAD - in src/features/myFeature/index.ts
document.createElement('div'); // DOM in features
websocket.send(message); // Direct WS send
window.myState = value; // Ad-hoc global
atom.value = newValue; // Direct mutation
// ✅ GOOD
// In src/ui/components/...
const root = document.createElement('div');
// In src/websocket/api.ts
export function sendAction(...) { websocket.send(...) }
// In src/features/...
const unsub = await Store.subscribe('myAtom', callback);
What to report:
- •File + line number
- •Pattern detected
- •Suggested fix: "Move DOM to src/ui/" or "Use Store API" etc.
Rule 3: Side Effects & Cleanup
Forbidden on import:
- •Event listeners without cleanup
- •Intervals/timeouts without cancellation
- •WebSocket subscriptions without unsubscribe
- •Audio playback on import
- •Patches or monkey-patching
Check for:
// ❌ BAD - at module level
window.addEventListener('click', handler); // No cleanup
setInterval(() => {...}, 1000); // Runs forever
const unsub = Store.subscribe(...); // Unsub lost
// ✅ GOOD
let unsub: (() => void) | null = null;
export function init() {
window.addEventListener('click', handler);
unsub = () => window.removeEventListener('click', handler);
}
export function destroy() {
unsub?.();
unsub = null;
}
What to report:
- •File + line number
- •Effect type (listener, interval, etc.)
- •Pattern: "Side effect at module level detected"
- •Suggestion: "Wrap in init()/destroy() functions"
Rule 4: Storage Namespacing
Forbidden:
- •Direct
GM_getValue/GM_setValuecalls - •Non-namespaced keys
- •Undefined storage keys
Check for:
// ❌ BAD
GM_setValue('myKey', value);
const key = 'random-key';
// ✅ GOOD
import { KEYS } from 'src/utils/storage';
GM_setValue(KEYS.MY_FEATURE, value); // Prefixed with gemini:
What to report:
- •File + line number
- •Key used (if visible)
- •Suggestion: "Use KEYS from src/utils/storage"
Rule 5: Code Quality (Files < 500 lines)
Check for:
- •Files exceeding 500 lines
- •Functions exceeding 50 lines (warning threshold)
- •Single-letter variable names (a, i, x outside loops)
- •Magic numbers/strings without constants
What to report:
- •File + line count
- •Suggestion: "Split by responsibility"
- •Magic numbers: "Extract to const CONSTANT_NAME"
Rule 6: TypeScript Strict (no any)
Forbidden:
- •
anytype usage - •Unconstrained generics
Check for:
// ❌ BAD
function handle(data: any) {}
// ✅ GOOD
function handle(data: unknown) {
if (typeof data === 'object' && data !== null) { ... }
}
What to report:
- •File + line number
- •The
anyusage - •Suggestion: "Use
unknown+ type narrowing"
2. Feature Rules (src/features/*)
Feature Structure Check
Expected files:
src/features/myFeature/
├── types.ts # ✅ Required
├── index.ts # ✅ Required
├── state.ts # ⚠️ If persistent state
├── ui.ts # ⚠️ If visual elements
├── middleware.ts # ⚠️ If WS outgoing
├── handler.ts # ⚠️ If WS incoming
└── logic/ # 📁 Recommended
├── core.ts
└── helpers.ts
Check for:
- •Missing
types.tsorindex.ts - •Config without
enabled: boolean - •Storage key not using FEATURE_KEYS prefix
Feature API Exposure
Check in src/features/index.ts:
// ✅ GOOD
export { MGMyFeature } from './myFeature';
export type { MyFeatureConfig } from './myFeature';
Check in src/api/index.ts:
// ✅ GOOD
Features: {
MyFeature: MGMyFeature,
}
Check in src/ui/loader/bootstrap.ts:
// ✅ GOOD
{ name: "MyFeature", init: MGMyFeature.init.bind(MGMyFeature) }
What to report:
- •Feature not exported from index.ts
- •Feature not in GeminiAPI
- •Feature not initialized in bootstrap
3. Module Rules (src/modules/*)
Module Structure
Modules MUST:
- •Always be active (no toggle)
- •Export named exports (not default)
- •Have proper initialization if needed
- •Be listed in core module list
Check for:
- •Modules with
enabledconfig (should be modules, not features) - •Toggles in module structure
4. UI Rules (src/ui/*)
Component Factory Pattern
Check for factory violations:
// ❌ BAD
export function MyButton() { return <JSX>... }
// ✅ GOOD
export interface Options { label: string; onClick?: () => void }
export interface Handle { root: HTMLElement; destroy(): void }
export function create(options: Options): Handle { ... }
What to report:
- •Missing Options/Handle interfaces
- •No destroy() method
- •Direct JSX exports
Shadow DOM Isolation
Check for:
- •Direct DOM queries outside Shadow DOM boundary
- •CSS not using CSS variables
- •Hardcoded colors (should be theme tokens)
5. WebSocket Rules (src/websocket/*)
File Responsibilities
Check locations:
- •Type definitions ONLY in
protocol.ts - •Transport logic ONLY in
connection.ts - •Public API ONLY in
api.ts - •Handlers ONLY in
handlers/ - •Middlewares ONLY in
middlewares/
What to report:
- •Message type defined outside protocol.ts
- •Logic mixed into wrong file
No Hardcoded Message Types
Check for:
// ❌ BAD
if (type === 'plant_action') { ... }
// ✅ GOOD
import { WS_TYPES } from './protocol';
if (type === WS_TYPES.PLANT_ACTION) { ... }
Validation Report Format
When violations are found, report as:
🚨 RULE VIOLATIONS FOUND (Gemini Mod)
[File: src/features/myFeature/index.ts]
├─ ❌ Rule 1: Hardcoded Game Data (line 42)
│ └─ Found: const plantName = 'Carrot'
│ └─ Fix: Use MGData.get('plants')['carrot']
│
├─ ⚠️ Rule 5: Code Quality (lines: 312)
│ └─ File exceeds 500 lines (actual: 612)
│ └─ Suggestion: Split by responsibility
│
└─ ❌ Rule 2: Boundaries (line 89)
└─ Found: Direct WebSocket send
└─ Fix: Use websocket/api.ts actions instead
✅ PASSED CHECKS:
├─ Feature structure (types.ts, index.ts present)
├─ Storage keys namespaced correctly
├─ TypeScript strict (no `any` found)
└─ Side effect cleanup present
📋 SUMMARY: 3 violations, 4 checks passed
💡 Next steps: Review suggested fixes above
Usage in Review Workflows
Before Commit
/rule-validator src/ # Validate entire src/ /rule-validator dist/ # Skip dist builds
Before PR
/rule-validator src/features/newFeature /rule-validator src/ui/components/NewComponent
Full Repo Audit
/rule-validator . # Entire project
Configuration
Ignore Patterns (Optional)
Can skip certain paths:
- •
dist/— Build output - •
node_modules/— Dependencies - •
GameSourceFiles/— Reference only - •
GameFilesExample/— Examples only
Severity Levels
- •🚨 Error — Must fix before commit
- •⚠️ Warning — Consider fixing
- •ℹ️ Info — Best practice suggestion
Common False Positives & How to Handle
| Detection | Reality | Action |
|---|---|---|
'Carrot' in comment | Actually just documentation | Ignore |
any in test file | Test-only, acceptable | Allow |
| 600-line file | Truly complex, can't split | Document why |
| WS send in tests | Mock data, OK | Allow |
When reporting: Flag but don't block if edge case is justified.
Rules Priority (Strict → Lenient)
🔴 Strict (Never compromise)
- •No hardcoded game data (MGData required)
- •No direct WebSocket sends
- •No side effects on import
- •TypeScript strict (no
any)
🟠 Medium (Be thoughtful)
- •Storage namespacing
- •File size (< 500 lines)
- •Cleanup discipline
🟡 Low (Guidance only)
- •Code organization
- •Naming conventions
- •Documentation
Integration with /clear Workflow
When you /clear before starting a new task:
/rule-validator src/ # Run full validation → Fix any violations → Commit with pre-commit-gate skill → Proceed with clean slate
This ensures you never accumulate debt across task boundaries.