@codyswann/lisa 1.76.6 → 1.78.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (27) hide show
  1. package/all/deletions.json +5 -1
  2. package/package.json +1 -1
  3. package/plugins/lisa/.claude-plugin/plugin.json +21 -1
  4. package/plugins/lisa/hooks/inject-rules.sh +22 -0
  5. package/{all/copy-overwrite/.claude → plugins/lisa}/rules/base-rules.md +6 -3
  6. package/{all/copy-overwrite/.claude → plugins/lisa}/rules/verification.md +10 -0
  7. package/plugins/lisa-cdk/.claude-plugin/plugin.json +1 -1
  8. package/plugins/lisa-expo/.claude-plugin/plugin.json +1 -1
  9. package/plugins/lisa-nestjs/.claude-plugin/plugin.json +1 -1
  10. package/plugins/lisa-rails/.claude-plugin/plugin.json +23 -1
  11. package/plugins/lisa-rails/hooks/inject-rules.sh +22 -0
  12. package/plugins/lisa-typescript/.claude-plugin/plugin.json +1 -1
  13. package/plugins/src/base/.claude-plugin/plugin.json +4 -0
  14. package/plugins/src/base/hooks/inject-rules.sh +22 -0
  15. package/plugins/src/base/rules/base-rules.md +91 -0
  16. package/plugins/src/base/rules/coding-philosophy.md +428 -0
  17. package/plugins/src/base/rules/intent-routing.md +126 -0
  18. package/plugins/src/base/rules/security-audit-handling.md +30 -0
  19. package/plugins/src/base/rules/verification.md +93 -0
  20. package/plugins/src/rails/.claude-plugin/plugin.json +6 -0
  21. package/plugins/src/rails/hooks/inject-rules.sh +22 -0
  22. package/plugins/src/rails/rules/rails-conventions.md +176 -0
  23. package/rails/deletions.json +2 -1
  24. /package/{all/copy-overwrite/.claude → plugins/lisa}/rules/coding-philosophy.md +0 -0
  25. /package/{all/copy-overwrite/.claude → plugins/lisa}/rules/intent-routing.md +0 -0
  26. /package/{all/copy-overwrite/.claude → plugins/lisa}/rules/security-audit-handling.md +0 -0
  27. /package/{rails/copy-overwrite/.claude → plugins/lisa-rails}/rules/rails-conventions.md +0 -0
@@ -125,12 +125,16 @@
125
125
  ".claude/hooks/track-plan-sessions.sh",
126
126
  ".claude/hooks/verify-completion.sh",
127
127
  ".claude/hooks/README.md",
128
- ".claude/rules/coding-philosophy.md",
129
128
  ".claude/rules/verfication.md",
130
129
  ".coderabbit.yml",
131
130
  "scripts/setup-deploy-key.sh",
132
131
  "HUMAN.md",
133
132
  ".claude/rules/lisa.md",
133
+ ".claude/rules/base-rules.md",
134
+ ".claude/rules/coding-philosophy.md",
135
+ ".claude/rules/intent-routing.md",
136
+ ".claude/rules/security-audit-handling.md",
137
+ ".claude/rules/verification.md",
134
138
  ".lisa-manifest"
135
139
  ],
136
140
  "keep": [
package/package.json CHANGED
@@ -74,7 +74,7 @@
74
74
  "flatted": "^3.4.2"
75
75
  },
76
76
  "name": "@codyswann/lisa",
77
- "version": "1.76.6",
77
+ "version": "1.78.0",
78
78
  "description": "Claude Code governance framework that applies guardrails, guidance, and automated enforcement to projects",
79
79
  "main": "dist/index.js",
80
80
  "exports": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa",
3
- "version": "1.76.6",
3
+ "version": "1.78.0",
4
4
  "description": "Universal governance — agents, skills, commands, hooks, and rules for all projects",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -96,6 +96,15 @@
96
96
  }
97
97
  ]
98
98
  },
99
+ {
100
+ "matcher": "",
101
+ "hooks": [
102
+ {
103
+ "type": "command",
104
+ "command": "${CLAUDE_PLUGIN_ROOT}/hooks/inject-rules.sh"
105
+ }
106
+ ]
107
+ },
99
108
  {
100
109
  "matcher": "",
101
110
  "hooks": [
@@ -115,6 +124,17 @@
115
124
  ]
116
125
  }
117
126
  ],
127
+ "SubagentStart": [
128
+ {
129
+ "matcher": "",
130
+ "hooks": [
131
+ {
132
+ "type": "command",
133
+ "command": "${CLAUDE_PLUGIN_ROOT}/hooks/inject-rules.sh"
134
+ }
135
+ ]
136
+ }
137
+ ],
118
138
  "SessionEnd": [
119
139
  {
120
140
  "matcher": "",
@@ -0,0 +1,22 @@
1
+ #!/usr/bin/env bash
2
+ # Reads all .md files from the plugin's rules/ directory and injects them
3
+ # into the session context via additionalContext.
4
+ # Used by SessionStart and SubagentStart hooks.
5
+ set -euo pipefail
6
+
7
+ RULES_DIR="${CLAUDE_PLUGIN_ROOT}/rules"
8
+
9
+ # Bail silently if no rules directory
10
+ [ -d "$RULES_DIR" ] || exit 0
11
+
12
+ CONTEXT=""
13
+ for file in "$RULES_DIR"/*.md; do
14
+ [ -f "$file" ] || continue
15
+ CONTEXT+="$(cat "$file")"$'\n\n'
16
+ done
17
+
18
+ # Bail if no rules found
19
+ [ -n "$CONTEXT" ] || exit 0
20
+
21
+ # Output as JSON — jq handles escaping
22
+ jq -n --arg ctx "$CONTEXT" '{"additionalContext": $ctx}'
@@ -2,12 +2,15 @@ Requirement Verification:
2
2
 
3
3
  Never assume the person providing instructions has given you complete, correct, or technically precise requirements. Treat every request as potentially underspecified. Before starting any work:
4
4
 
5
- 1. Verify the request is specific enough to produce a verifiable outcome. If it is not, stop and ask for clarification.
6
- 2. Verify you can empirically prove the work is done when complete (e.g. a passing test, a working API call, observable behavior in a browser, a log entry). If you cannot define how to prove it, stop and ask for clarification.
7
- 3. If a request contradicts existing code, architecture, or conventions, do not silently comply. Raise the contradiction and confirm intent before proceeding.
5
+ 1. Identify any ambiguities in the request that would prevent you from completing the work. If any exist, stop and ask for clarification.
6
+ 2. Identify any open questions whose answers would change your approach. If any exist, stop and ask.
7
+ 3. Define how you will empirically verify the work is complete — not by running tests or linters, but by using the resulting software the way a user would. If you cannot define this, stop and ask for clarification.
8
+ 4. If a request contradicts existing code, architecture, or conventions, do not silently comply. Raise the contradiction and confirm intent before proceeding.
8
9
 
9
10
  DO NOT START WORK if any of the above are unclear. Asking a clarifying question is always cheaper than implementing the wrong thing.
10
11
 
12
+ Do not begin a task if there are any blockers, ambiguities, access requirements, unanswered questions, or unknowns that would prevent you from completing it. Identify these before starting — not during implementation. If you cannot confirm that you have everything needed to finish the work end-to-end, stop and surface what is missing.
13
+
11
14
  Project Discovery:
12
15
  - Determine the project's package manager before installing or running anything.
13
16
  - Read the project manifest (e.g. package.json, pyproject.toml, Cargo.toml, go.mod) to understand available scripts and dependencies.
@@ -16,6 +16,16 @@ No agent may claim success without evidence from runtime verification.
16
16
 
17
17
  Never assume something works because the code "looks correct." Run a command, observe the output, compare to expected result.
18
18
 
19
+ **Verification is not linting, typechecking, or testing.** Those are quality checks. Verification is using the resulting software the way a user would — interacting with the UI, calling the API, running the CLI command, observing the behavior. Tests pass in isolation; verification proves the system works as a whole. Passing tests, linting, and typechecking does not constitute verification.
20
+
21
+ Verification is mandatory. Never skip it, defer it, or claim it was unnecessary. Every task must be verified before claiming completion.
22
+
23
+ Before starting implementation, state your verification plan — how you will use the resulting software to prove it works. Do not begin implementation until the plan is confirmed.
24
+
25
+ After verifying a change empirically, encode that verification as automated tests. The manual proof that something works should become a repeatable regression test that catches future regressions. Every verification should answer: "How do I turn this into a test?"
26
+
27
+ Every pull request must include step-by-step instructions for reviewers to independently replicate the verification. These are not test commands — they are the exact steps a human would follow to use the software and confirm the change works. If a reviewer cannot reproduce your verification from the PR description alone, the PR is incomplete.
28
+
19
29
  ---
20
30
 
21
31
  ## Roles
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-cdk",
3
- "version": "1.76.6",
3
+ "version": "1.78.0",
4
4
  "description": "AWS CDK-specific plugin",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-expo",
3
- "version": "1.76.6",
3
+ "version": "1.78.0",
4
4
  "description": "Expo/React Native-specific skills, agents, rules, and MCP servers",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-nestjs",
3
- "version": "1.76.6",
3
+ "version": "1.78.0",
4
4
  "description": "NestJS-specific skills (GraphQL, TypeORM)",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,11 +1,33 @@
1
1
  {
2
2
  "name": "lisa-rails",
3
- "version": "1.76.6",
3
+ "version": "1.78.0",
4
4
  "description": "Ruby on Rails-specific hooks — RuboCop linting/formatting and ast-grep scanning on edit",
5
5
  "author": {
6
6
  "name": "Cody Swann"
7
7
  },
8
8
  "hooks": {
9
+ "SessionStart": [
10
+ {
11
+ "matcher": "",
12
+ "hooks": [
13
+ {
14
+ "type": "command",
15
+ "command": "${CLAUDE_PLUGIN_ROOT}/hooks/inject-rules.sh"
16
+ }
17
+ ]
18
+ }
19
+ ],
20
+ "SubagentStart": [
21
+ {
22
+ "matcher": "",
23
+ "hooks": [
24
+ {
25
+ "type": "command",
26
+ "command": "${CLAUDE_PLUGIN_ROOT}/hooks/inject-rules.sh"
27
+ }
28
+ ]
29
+ }
30
+ ],
9
31
  "PostToolUse": [
10
32
  {
11
33
  "matcher": "Write|Edit",
@@ -0,0 +1,22 @@
1
+ #!/usr/bin/env bash
2
+ # Reads all .md files from the plugin's rules/ directory and injects them
3
+ # into the session context via additionalContext.
4
+ # Used by SessionStart and SubagentStart hooks.
5
+ set -euo pipefail
6
+
7
+ RULES_DIR="${CLAUDE_PLUGIN_ROOT}/rules"
8
+
9
+ # Bail silently if no rules directory
10
+ [ -d "$RULES_DIR" ] || exit 0
11
+
12
+ CONTEXT=""
13
+ for file in "$RULES_DIR"/*.md; do
14
+ [ -f "$file" ] || continue
15
+ CONTEXT+="$(cat "$file")"$'\n\n'
16
+ done
17
+
18
+ # Bail if no rules found
19
+ [ -n "$CONTEXT" ] || exit 0
20
+
21
+ # Output as JSON — jq handles escaping
22
+ jq -n --arg ctx "$CONTEXT" '{"additionalContext": $ctx}'
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-typescript",
3
- "version": "1.76.6",
3
+ "version": "1.78.0",
4
4
  "description": "TypeScript-specific hooks — Prettier formatting, ESLint linting, and ast-grep scanning on edit",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -58,9 +58,13 @@
58
58
  ],
59
59
  "SessionStart": [
60
60
  { "matcher": "startup", "hooks": [{ "type": "command", "command": "${CLAUDE_PLUGIN_ROOT}/hooks/install-pkgs.sh" }] },
61
+ { "matcher": "", "hooks": [{ "type": "command", "command": "${CLAUDE_PLUGIN_ROOT}/hooks/inject-rules.sh" }] },
61
62
  { "matcher": "", "hooks": [{ "type": "command", "command": "${CLAUDE_PLUGIN_ROOT}/hooks/setup-jira-cli.sh" }] },
62
63
  { "matcher": "", "hooks": [{ "type": "command", "command": "command -v entire >/dev/null 2>&1 && entire hooks claude-code session-start || true" }] }
63
64
  ],
65
+ "SubagentStart": [
66
+ { "matcher": "", "hooks": [{ "type": "command", "command": "${CLAUDE_PLUGIN_ROOT}/hooks/inject-rules.sh" }] }
67
+ ],
64
68
  "SessionEnd": [
65
69
  { "matcher": "", "hooks": [{ "type": "command", "command": "command -v entire >/dev/null 2>&1 && entire hooks claude-code session-end || true" }] }
66
70
  ]
@@ -0,0 +1,22 @@
1
+ #!/usr/bin/env bash
2
+ # Reads all .md files from the plugin's rules/ directory and injects them
3
+ # into the session context via additionalContext.
4
+ # Used by SessionStart and SubagentStart hooks.
5
+ set -euo pipefail
6
+
7
+ RULES_DIR="${CLAUDE_PLUGIN_ROOT}/rules"
8
+
9
+ # Bail silently if no rules directory
10
+ [ -d "$RULES_DIR" ] || exit 0
11
+
12
+ CONTEXT=""
13
+ for file in "$RULES_DIR"/*.md; do
14
+ [ -f "$file" ] || continue
15
+ CONTEXT+="$(cat "$file")"$'\n\n'
16
+ done
17
+
18
+ # Bail if no rules found
19
+ [ -n "$CONTEXT" ] || exit 0
20
+
21
+ # Output as JSON — jq handles escaping
22
+ jq -n --arg ctx "$CONTEXT" '{"additionalContext": $ctx}'
@@ -0,0 +1,91 @@
1
+ Requirement Verification:
2
+
3
+ Never assume the person providing instructions has given you complete, correct, or technically precise requirements. Treat every request as potentially underspecified. Before starting any work:
4
+
5
+ 1. Identify any ambiguities in the request that would prevent you from completing the work. If any exist, stop and ask for clarification.
6
+ 2. Identify any open questions whose answers would change your approach. If any exist, stop and ask.
7
+ 3. Define how you will empirically verify the work is complete — not by running tests or linters, but by using the resulting software the way a user would. If you cannot define this, stop and ask for clarification.
8
+ 4. If a request contradicts existing code, architecture, or conventions, do not silently comply. Raise the contradiction and confirm intent before proceeding.
9
+
10
+ DO NOT START WORK if any of the above are unclear. Asking a clarifying question is always cheaper than implementing the wrong thing.
11
+
12
+ Do not begin a task if there are any blockers, ambiguities, access requirements, unanswered questions, or unknowns that would prevent you from completing it. Identify these before starting — not during implementation. If you cannot confirm that you have everything needed to finish the work end-to-end, stop and surface what is missing.
13
+
14
+ Project Discovery:
15
+ - Determine the project's package manager before installing or running anything.
16
+ - Read the project manifest (e.g. package.json, pyproject.toml, Cargo.toml, go.mod) to understand available scripts and dependencies.
17
+ - Read the project's linting and formatting configuration to understand its standards.
18
+ - Regenerate the lockfile after adding, removing, or updating dependencies.
19
+ - Ignore build output directories (dist, build, out, target, etc.) unless specified otherwise.
20
+ - Ignore configuration linter hints/warnings — only fix actual unused exports/dependencies reported as errors.
21
+
22
+ Code Quality:
23
+ - Make atomic commits with clear conventional commit messages.
24
+ - Create clear documentation preambles for new code. Update preambles when modifying existing code.
25
+ - Document the "why", not the "what". Code explains what it does; documentation explains why it exists.
26
+ - Always add new imports and their first usage in the same edit. The lint-on-edit hook runs `eslint --fix` after every Edit, which auto-removes unused imports. If you add an import in one edit and plan to use it in a second edit, the hook will strip the import before the second edit runs.
27
+ - Add language specifiers to fenced code blocks in Markdown.
28
+ - Use project-relative paths rather than absolute paths in documentation and Markdown.
29
+ - Delete old code completely when replacing it. No deprecation unless specifically requested.
30
+ - Fix bugs and issues properly. Never cover them up or work around them.
31
+ - Test empirically to confirm something worked. Never assume.
32
+ - Never assume test expectations before verifying actual implementation behavior. Run tests to learn the behavior, then adjust expectations to match.
33
+ - Always provide a solution. Never dismiss something as "not related to our changes" or "not relevant to this task".
34
+
35
+ Git Discipline:
36
+ - Prefix git push with `GIT_SSH_COMMAND="ssh -o ServerAliveInterval=30 -o ServerAliveCountMax=5"`.
37
+ - Never commit directly to an environment branch (dev, staging, main).
38
+ - Never use --no-verify or attempt to bypass a git hook.
39
+ - Never stash changes you cannot commit. Either fix whatever is preventing the commit or fail out and let the human know why.
40
+ - Never add "BREAKING CHANGE" to a commit message unless there is actually a breaking change.
41
+ - When opening a PR, watch the PR. If any status checks fail, fix them. For all bot code reviews, if the feedback is valid, implement it and push the change to the PR. Then resolve the feedback. If the feedback is not valid, reply to the feedback explaining why it's not valid and then resolve the feedback. Do this in a loop until the PR is able to be merged and then merge it.
42
+ - When merging a PR into an environment branch (dev, staging, main), watch the resultant deploy until it fully succeeds. If it fails for any reason, fix the failure and then open a new PR with the fix.
43
+ - When referencing a PR in a response, always include the url
44
+
45
+ Testing Discipline:
46
+ - Never skip or disable any tests or quality checks.
47
+ - Never add skip directives to a test unless explicitly asked to.
48
+ - Never lower thresholds to pass a pre-push hook. Increase test coverage to make it pass.
49
+ - Never duplicate test helper functions without appropriate lint suppression when duplication is intentional for test isolation.
50
+
51
+ JIRA Discipline:
52
+ - If working on a JIRA issue, make sure the branch you're working on references and is added to the JIRA issue.
53
+ - If working on a JIRA issue, update the issue as you work through it. For example, if working on a Bug Triage, update the issue with your questions/feedback/suggestions.
54
+
55
+ Agent Behavior:
56
+ - Never handle tasks yourself when working in a team of agents. Always delegate to a specialized agent.
57
+
58
+ NEVER:
59
+ - Modify this file directly. To add a memory or learning, use the project's rules file or create a skill.
60
+ - Directly modify files inside dependency directories (e.g. node_modules, .venv, vendor, target).
61
+ - Delete anything that is not tracked in git.
62
+ - Delete anything outside of this project's directory.
63
+ - Create placeholder implementations.
64
+ - Create TODOs.
65
+ - Create versioned copies of files or functions (e.g. V2, Optimized, processNew, handleOld).
66
+ - Write migration code unless explicitly requested.
67
+ - Write functions or methods unless they are needed.
68
+ - Write unhelpful comments like "removed code" or "old implementation".
69
+ - Update CHANGELOG.
70
+
71
+ ASK FIRST:
72
+ - Before adding a lint suppression comment (e.g. eslint-disable, noqa, #[allow(...)], @SuppressWarnings). These should be a last resort.
73
+ - Before adding a type-checking suppression comment (e.g. ts-ignore, ts-expect-error, ts-nocheck, type: ignore).
74
+ - Lint suppression in test files is acceptable without asking only when comprehensive test coverage requires it (e.g. file length limits) or when intentional duplication improves test isolation. Include matching re-enable comments where applicable.
75
+
76
+ Multi-Repository Awareness:
77
+
78
+ When working in a microservices architecture, the code you need may span multiple repositories. Watch for these signals that you're missing context:
79
+
80
+ 1. Import paths or package references that don't resolve in the current repository
81
+ 2. API calls to internal services where you can't find the contract, schema, or handler
82
+ 3. Shared libraries, SDKs, or proto/OpenAPI definitions referenced but not present
83
+ 4. Environment variables or config referencing service names you don't have code for
84
+ 5. Error messages or stack traces pointing to code outside the current repo
85
+ 6. JIRA issues or documentation referencing components in other repositories
86
+
87
+ When you detect any of the above:
88
+ 1. Do NOT guess or make assumptions about what the external code does
89
+ 2. Identify which repository contains the missing code
90
+ 3. Add that repository to your current session before proceeding
91
+ 4. If you cannot determine which repository contains the code, ask — do not proceed without it
@@ -0,0 +1,428 @@
1
+ # Coding Philosophy
2
+
3
+ This rule enforces the core coding philosophy: **immutability**, **predictable structure**, **functional transformations**, **test-driven development**, **clean deletion**, and **simplicity**.
4
+
5
+ ## Guiding Principles: YAGNI + SOLID + DRY + KISS
6
+
7
+ Follow these software engineering principles, **deferring to Occam's Razor/KISS whenever principles conflict**:
8
+
9
+ ### KISS (Keep It Simple, Stupid) - The Tiebreaker
10
+
11
+ When principles conflict, **always choose the simpler solution**.
12
+
13
+ ```typescript
14
+ // KISS: Simple direct approach
15
+ const isAdmin = user.role === "admin";
16
+
17
+ // Over-engineered: Abstraction without value
18
+ const isAdmin = RoleChecker.getInstance().checkRole(user, RoleTypes.ADMIN);
19
+ ```
20
+
21
+ ### YAGNI (You Ain't Gonna Need It)
22
+
23
+ Don't build features, abstractions, or flexibility you don't need **right now**.
24
+
25
+ ```typescript
26
+ // Correct: Solve today's problem
27
+ const formatDate = (date: Date) => date.toISOString().split("T")[0];
28
+
29
+ // Wrong: Building for hypothetical future needs
30
+ const formatDate = (date: Date, format?: string, locale?: string, timezone?: string) => {
31
+ // 50 lines handling cases that may never be used
32
+ };
33
+ ```
34
+
35
+ ### DRY (Don't Repeat Yourself) - With KISS Constraint
36
+
37
+ Extract duplication only when:
38
+ 1. The same logic appears **3+ times**
39
+ 2. The abstraction is **simpler** than the duplication
40
+ 3. The extracted code has a **clear single purpose**
41
+
42
+ ### SOLID Principles - Applied Pragmatically
43
+
44
+ | Principle | Apply When | Skip When |
45
+ | ------------------------- | --------------------------------------------- | --------------------------------------- |
46
+ | **S**ingle Responsibility | Function does 2+ unrelated things | Splitting adds complexity |
47
+ | **O**pen/Closed | Extension points have clear use cases | No foreseeable extensions |
48
+ | **L**iskov Substitution | Using inheritance hierarchies | Using composition (preferred) |
49
+ | **I**nterface Segregation | Consumers need different subsets | Interface is already small |
50
+ | **D**ependency Inversion | Testing requires mocking external services | Direct dependency is simpler |
51
+
52
+ ### Decision Framework
53
+
54
+ When unsure, ask in order:
55
+ 1. **Do I need this now?** (YAGNI) - If no, don't build it
56
+ 2. **Is there a simpler way?** (KISS) - Choose the simpler option
57
+ 3. **Am I repeating myself 3+ times?** (DRY) - Extract if the abstraction is simpler
58
+ 4. **Does this function do one thing?** (SOLID-SRP) - Split only if clearer
59
+
60
+ ---
61
+
62
+ ## Core Principles
63
+
64
+ ### 1. Immutability First
65
+
66
+ Never mutate data. Always create new references.
67
+
68
+ ```typescript
69
+ // Correct - spread creates new object
70
+ const updated = { ...user, name: "New Name" };
71
+
72
+ // Incorrect - mutation
73
+ user.name = "New Name";
74
+ ```
75
+
76
+ ### 2. Function Structure Ordering
77
+
78
+ All functions, hooks, and components follow a strict ordering:
79
+
80
+ ```text
81
+ 1. Variable definitions and derived state (const, useState, useMemo, useCallback)
82
+ 2. Side effects (useEffect, function calls with no return value)
83
+ 3. Return statement
84
+ ```
85
+
86
+ ### 3. Functional Transformations
87
+
88
+ Use `map`, `filter`, `reduce` instead of imperative loops and mutations.
89
+
90
+ ```typescript
91
+ // Correct - functional transformation
92
+ const names = users.map(u => u.name);
93
+
94
+ // Incorrect - imperative mutation
95
+ const names = [];
96
+ users.forEach(u => names.push(u.name));
97
+ ```
98
+
99
+ ### 4. Test-Driven Development (TDD)
100
+
101
+ **Always write failing tests before implementation code.** This is mandatory, not optional.
102
+
103
+ ```text
104
+ TDD Cycle:
105
+ 1. RED: Write a failing test that defines expected behavior
106
+ 2. GREEN: Write the minimum code to make the test pass
107
+ 3. REFACTOR: Clean up while keeping tests green
108
+ ```
109
+
110
+ ### 5. Clean Deletion
111
+
112
+ **Delete old code completely.** No deprecation warnings, migration shims, or backward-compatibility layers unless explicitly requested.
113
+
114
+ ```typescript
115
+ // Correct: Remove the old code entirely
116
+ const calculateScore = (player: Player): number => player.stats.overall;
117
+
118
+ // Wrong: Keeping deprecated versions around
119
+ /** @deprecated Use calculateScore instead */
120
+ const getPlayerScore = (player: Player): number => calculateScore(player);
121
+ ```
122
+
123
+ **Clean deletion rules:**
124
+ - When replacing code, delete the old version completely
125
+ - Never create `V2`, `New`, or `Old` suffixed functions/variables
126
+ - Never add `@deprecated` comments - just remove the code
127
+ - Never write migration code unless explicitly asked
128
+ - Trust git history for recovery if needed
129
+
130
+ ---
131
+
132
+ ## Quick Reference
133
+
134
+ ### Variable Declaration
135
+
136
+ | Pattern | Status | Example |
137
+ | ------- | --------- | ----------------------------- |
138
+ | `const` | Required | `const value = calculate();` |
139
+ | `let` | Forbidden | Use ternary or reduce instead |
140
+ | `var` | Forbidden | Never use |
141
+
142
+ ### Array Operations
143
+
144
+ | Instead of | Use |
145
+ | ----------------------- | -------------------------------------------- |
146
+ | `arr.push(item)` | `[...arr, item]` |
147
+ | `arr.pop()` | `arr.slice(0, -1)` |
148
+ | `arr.splice(i, 1)` | `arr.filter((_, idx) => idx !== i)` |
149
+ | `arr.sort()` | `[...arr].sort()` |
150
+ | `arr[i] = value` | `arr.map((v, idx) => idx === i ? value : v)` |
151
+ | `forEach` with mutation | `reduce` or `map` |
152
+
153
+ ### Object Operations
154
+
155
+ | Instead of | Use |
156
+ | ------------------------- | ----------------------------- |
157
+ | `obj.key = value` | `{ ...obj, key: value }` |
158
+ | `delete obj.key` | `({ key: _, ...rest } = obj)` |
159
+ | `Object.assign(obj, ...)` | `{ ...obj, ...other }` |
160
+
161
+ ### Building Lookup Objects
162
+
163
+ ```typescript
164
+ // Correct - reduce with spread
165
+ const lookup = items.reduce(
166
+ (acc, item) => ({ ...acc, [item.id]: item }),
167
+ {} as Record<string, Item>
168
+ );
169
+
170
+ // Incorrect - forEach with Map.set
171
+ const lookup = new Map();
172
+ items.forEach(item => lookup.set(item.id, item));
173
+ ```
174
+
175
+ ### Conditional Values
176
+
177
+ ```typescript
178
+ // Correct - ternary expression
179
+ const status = isComplete ? "done" : "pending";
180
+
181
+ // Incorrect - let with reassignment
182
+ let status = "pending";
183
+ if (isComplete) {
184
+ status = "done";
185
+ }
186
+ ```
187
+
188
+ ---
189
+
190
+ ## Hook Structure Example
191
+
192
+ ```typescript
193
+ export const usePlayerData = (playerId: string) => {
194
+ // 1. VARIABLES & STATE (first)
195
+ const [isLoading, setIsLoading] = useState(true);
196
+ const { data } = useQuery(GetPlayerDocument, { variables: { playerId } });
197
+
198
+ const playerName = useMemo(() => data?.player?.name ?? "Unknown", [data]);
199
+
200
+ const handleRefresh = useCallback(() => {
201
+ refetch();
202
+ }, [refetch]);
203
+
204
+ // 2. SIDE EFFECTS (second)
205
+ useEffect(() => {
206
+ console.log("Player loaded:", playerName);
207
+ }, [playerName]);
208
+
209
+ // 3. RETURN (last)
210
+ return { playerName, isLoading, handleRefresh };
211
+ };
212
+ ```
213
+
214
+ ## Container Component Example
215
+
216
+ ```typescript
217
+ const PlayerCardContainer: React.FC<Props> = ({ playerId }) => {
218
+ // 1. VARIABLES & STATE
219
+ const { data, loading } = useQuery(GetPlayerDocument, { variables: { playerId } });
220
+ const { colors } = useTheme();
221
+
222
+ const formattedStats = useMemo(
223
+ () => data?.stats?.map(s => ({ ...s, display: formatStat(s) })) ?? [],
224
+ [data?.stats]
225
+ );
226
+
227
+ const handlePress = useCallback(() => {
228
+ router.push(`/players/${playerId}`);
229
+ }, [playerId]);
230
+
231
+ // 2. SIDE EFFECTS (none in this example)
232
+
233
+ // 3. RETURN
234
+ return (
235
+ <PlayerCardView
236
+ stats={formattedStats}
237
+ colors={colors}
238
+ loading={loading}
239
+ onPress={handlePress}
240
+ />
241
+ );
242
+ };
243
+ ```
244
+
245
+ ## Utility Function Example
246
+
247
+ ```typescript
248
+ export const calculateTeamRankings = (
249
+ players: readonly Player[]
250
+ ): readonly TeamRanking[] => {
251
+ // 1. VARIABLES & DERIVED VALUES
252
+ const validPlayers = players.filter(p => p.team && p.score != null);
253
+
254
+ const teamScores = validPlayers.reduce(
255
+ (acc, player) => ({
256
+ ...acc,
257
+ [player.team.id]: {
258
+ teamId: player.team.id,
259
+ totalScore: (acc[player.team.id]?.totalScore ?? 0) + player.score,
260
+ count: (acc[player.team.id]?.count ?? 0) + 1,
261
+ },
262
+ }),
263
+ {} as Record<string, { teamId: string; totalScore: number; count: number }>
264
+ );
265
+
266
+ const rankings = Object.values(teamScores).map(t => ({
267
+ teamId: t.teamId,
268
+ avgScore: t.totalScore / t.count,
269
+ }));
270
+
271
+ const sorted = [...rankings].sort((a, b) => b.avgScore - a.avgScore);
272
+
273
+ // 2. NO SIDE EFFECTS IN PURE FUNCTIONS
274
+
275
+ // 3. RETURN
276
+ return sorted;
277
+ };
278
+ ```
279
+
280
+ ---
281
+
282
+ ## Anti-Patterns to Avoid
283
+
284
+ ### Never use `let` for conditional assignment
285
+
286
+ ```typescript
287
+ // Wrong
288
+ let result;
289
+ if (condition) {
290
+ result = valueA;
291
+ } else {
292
+ result = valueB;
293
+ }
294
+
295
+ // Correct
296
+ const result = condition ? valueA : valueB;
297
+ ```
298
+
299
+ ### Never mutate arrays
300
+
301
+ ```typescript
302
+ // Wrong
303
+ const items = [];
304
+ data.forEach(d => items.push(transform(d)));
305
+
306
+ // Correct
307
+ const items = data.map(d => transform(d));
308
+ ```
309
+
310
+ ### Never use Map when Record suffices
311
+
312
+ ```typescript
313
+ // Wrong
314
+ const lookup = new Map<string, User>();
315
+ users.forEach(u => lookup.set(u.id, u));
316
+ const user = lookup.get(userId);
317
+
318
+ // Correct
319
+ const lookup = users.reduce(
320
+ (acc, u) => ({ ...acc, [u.id]: u }),
321
+ {} as Record<string, User>
322
+ );
323
+ const user = lookup[userId];
324
+ ```
325
+
326
+ ### Never sort in place
327
+
328
+ ```typescript
329
+ // Wrong - mutates original
330
+ const sorted = items.sort((a, b) => a.value - b.value);
331
+
332
+ // Correct - creates new array
333
+ const sorted = [...items].sort((a, b) => a.value - b.value);
334
+ ```
335
+
336
+ ### Never place useEffect before variable definitions
337
+
338
+ ```typescript
339
+ // Wrong
340
+ useEffect(() => {
341
+ /* ... */
342
+ }, [value]);
343
+ const value = useMemo(() => calculate(), [dep]);
344
+
345
+ // Correct
346
+ const value = useMemo(() => calculate(), [dep]);
347
+ useEffect(() => {
348
+ /* ... */
349
+ }, [value]);
350
+ ```
351
+
352
+ ---
353
+
354
+ ## Immutable Patterns Reference
355
+
356
+ ### Building Lookup Objects with Reduce
357
+
358
+ ```typescript
359
+ const colorMap =
360
+ edges?.reduce(
361
+ (acc, edge) => (edge.color ? { ...acc, [edge.tagId]: edge.color } : acc),
362
+ {} as Record<string, string>
363
+ ) ?? {};
364
+ ```
365
+
366
+ ### Accumulating Multiple Properties
367
+
368
+ ```typescript
369
+ const teamGprAccumulator = validPlayers.reduce(
370
+ (acc, player) => {
371
+ const teamId = player.team?.id;
372
+ if (!teamId) return acc;
373
+
374
+ const existing = acc[teamId];
375
+ return {
376
+ ...acc,
377
+ [teamId]: {
378
+ teamId,
379
+ teamName: player.team.name,
380
+ gprSum: (existing?.gprSum ?? 0) + player.gpr,
381
+ playerCount: (existing?.playerCount ?? 0) + 1,
382
+ },
383
+ };
384
+ },
385
+ {} as Record<string, { teamId: string; teamName: string; gprSum: number; playerCount: number }>
386
+ );
387
+ ```
388
+
389
+ ### Nested Object Updates
390
+
391
+ ```typescript
392
+ const updated = {
393
+ ...state,
394
+ user: {
395
+ ...state.user,
396
+ profile: {
397
+ ...state.user.profile,
398
+ avatar: newAvatar,
399
+ },
400
+ },
401
+ };
402
+ ```
403
+
404
+ ### Conditional Property Addition
405
+
406
+ ```typescript
407
+ const result = {
408
+ ...baseObj,
409
+ ...(condition && { optionalProp: value }),
410
+ };
411
+ ```
412
+
413
+ ### Ternary Chain for Multiple Conditions
414
+
415
+ ```typescript
416
+ const priority = score > 90 ? "high" : score > 70 ? "medium" : "low";
417
+ ```
418
+
419
+ ### Readonly Types for Function Parameters
420
+
421
+ ```typescript
422
+ export const calculateTeamGprRank = (
423
+ leaguePlayers: readonly (PlayerWithScores | null | undefined)[],
424
+ myTeamId: string | null | undefined
425
+ ): number | null => {
426
+ // ...
427
+ };
428
+ ```
@@ -0,0 +1,126 @@
1
+ # Intent Routing
2
+
3
+ Classify the user's request and execute the matching flow. Each flow is a sequence of agents. Sub-flows can be invoked by any flow.
4
+
5
+ ## Flows
6
+
7
+ ### Fix
8
+ When: Bug reports, broken behavior, error messages, JIRA bug tickets.
9
+
10
+ Sequence:
11
+ 1. `git-history-analyzer` — understand why affected code exists, find related past fixes/reverts
12
+ 2. `debug-specialist` — reproduce the bug, prove root cause with evidence
13
+ 3. `architecture-specialist` — assess fix risk, identify files to change, check for ripple effects
14
+ 4. `test-specialist` — design regression test strategy
15
+ 5. `bug-fixer` — implement fix via TDD (reproduction becomes failing test)
16
+ 6. **Verify sub-flow**
17
+ 7. **Ship sub-flow**
18
+ 8. `learner` — capture discoveries for future sessions
19
+
20
+ ### Build
21
+ When: New features, stories, tasks, JIRA story/task tickets.
22
+
23
+ Sequence:
24
+ 1. `product-specialist` — define acceptance criteria, user flows, error states
25
+ 2. `architecture-specialist` — research codebase, design approach, map dependencies
26
+ 3. `test-specialist` — design test strategy (coverage, edge cases, TDD sequence)
27
+ 4. `builder` — implement via TDD (acceptance criteria become tests)
28
+ 5. **Verify sub-flow**
29
+ 6. **Review sub-flow**
30
+ 7. **Ship sub-flow**
31
+ 8. `learner` — capture discoveries
32
+
33
+ ### Investigate
34
+ When: "Why is this happening?", triage requests, JIRA spike tickets.
35
+
36
+ Sequence:
37
+ 1. `git-history-analyzer` — understand code evolution, find related changes
38
+ 2. `debug-specialist` — reproduce, trace execution, prove root cause
39
+ 3. `ops-specialist` — check logs, errors, health (if runtime issue)
40
+ 4. Report findings with evidence, recommend next action (Fix, Build, or escalate)
41
+
42
+ ### Plan
43
+ When: "Break this down", epic planning, large scope work, JIRA epic tickets.
44
+
45
+ Sequence:
46
+ 1. `product-specialist` — define acceptance criteria for the whole scope
47
+ 2. `architecture-specialist` — understand scope, map dependencies, identify cross-cutting concerns
48
+ 3. Break down into ordered tasks, each with: acceptance criteria, verification type, dependencies
49
+
50
+ ### Verify
51
+ When: Pre-ship quality gate. Used as a sub-flow by Fix and Build.
52
+
53
+ Sequence:
54
+ 1. Run full test suite — all tests must pass before proceeding
55
+ 2. Run quality checks — lint, typecheck, and format
56
+ 3. `verification-specialist` — verify acceptance criteria are met empirically
57
+
58
+ ### Ship
59
+ When: Code is ready to deploy. Used as a sub-flow by Fix, Build, and Improve.
60
+
61
+ Sequence:
62
+ 1. Commit — atomic conventional commits via `git-commit` skill
63
+ 2. PR — create/update pull request via `git-submit-pr` skill
64
+ 3. **Review sub-flow** (if not already done)
65
+ 4. PR Watch Loop (repeat until mergeable):
66
+ - If status checks fail → fix and push
67
+ - If merge conflicts → resolve and push
68
+ - If bot review feedback (CodeRabbit, etc.):
69
+ - Valid feedback → implement fix, push, resolve comment
70
+ - Invalid feedback → reply explaining why, resolve comment
71
+ - Repeat until all checks pass and all comments are resolved
72
+ 5. Merge the PR
73
+ 6. `ops-specialist` — deploy to target environment
74
+ 7. `verification-specialist` — post-deploy health check and smoke test
75
+ 8. `ops-specialist` — monitor for errors in first minutes
76
+
77
+ ### Review
78
+ When: Code review requests, PR review, quality assessment. Used as a sub-flow by Build.
79
+
80
+ Sequence:
81
+ 1. Run in parallel: `quality-specialist`, `security-specialist`, `performance-specialist`
82
+ 2. `product-specialist` — verify acceptance criteria are met empirically
83
+ 3. `test-specialist` — verify test coverage and quality
84
+ 4. Consolidate findings, ranked by severity
85
+
86
+ ### Improve
87
+ When: Refactoring, optimization, coverage improvement, complexity reduction.
88
+
89
+ Sequence:
90
+ 1. `architecture-specialist` — identify target, measure baseline, plan approach
91
+ 2. `test-specialist` — ensure existing test coverage before refactoring (safety net)
92
+ 3. `builder` — implement improvements via TDD
93
+ 4. `verification-specialist` — measure again, prove improvement
94
+ 5. **Ship sub-flow**
95
+ 6. `learner` — capture discoveries
96
+
97
+ #### Improve: Test Quality
98
+ When: "Improve tests", "strengthen test suite", "fix weak tests", test quality improvement.
99
+
100
+ Sequence:
101
+ 1. `test-specialist` — scan tests, identify weak/brittle tests, rank by improvement impact
102
+ 2. `builder` — implement test improvements
103
+ 3. **Verify sub-flow**
104
+ 4. **Ship sub-flow**
105
+ 5. `learner` — capture discoveries
106
+
107
+ ### Monitor
108
+ When: "Check the logs", "Any errors?", health checks, production monitoring.
109
+
110
+ Sequence:
111
+ 1. `ops-specialist` — health checks, log inspection, error monitoring, performance analysis
112
+ 2. Report findings, escalate if action needed
113
+
114
+ ## JIRA Entry Point
115
+
116
+ When the request references a JIRA ticket (ticket ID like PROJ-123 or a JIRA URL):
117
+
118
+ 1. Hand off to `jira-agent`
119
+ 2. `jira-agent` reads the ticket, validates structural quality, and runs analytical triage
120
+ 3. If triage finds unresolved ambiguities, `jira-agent` posts findings and STOPS — no work begins
121
+ 4. `jira-agent` determines intent and delegates to the appropriate flow above
122
+ 5. `jira-agent` syncs progress at milestones and posts evidence at completion
123
+
124
+ ## Sub-flow Usage
125
+
126
+ Flows reference sub-flows by name. When a flow says "Ship sub-flow", execute the full Ship sequence. When it says "Review sub-flow", execute the full Review sequence. Sub-flows can be nested (e.g., Ship includes Review).
@@ -0,0 +1,30 @@
1
+ # Security Audit Handling
2
+
3
+ If `git push` fails because the pre-push hook reports security vulnerabilities, follow these steps. Never use `--no-verify` to bypass the security audit.
4
+
5
+ ## Node.js Projects (GHSA advisories)
6
+
7
+ 1. Note the GHSA ID(s), affected package(s), and advisory URL from the error output
8
+ 2. Check the advisory URL to determine if a patched version of the vulnerable package exists
9
+ 3. If a patched version exists: add a resolution/override in package.json to force the patched version (add to both `resolutions` and `overrides` sections), then run the package manager install command to regenerate the lockfile, commit the changes, and retry the push
10
+ 4. If no patched version exists and the vulnerability is safe for this project (e.g., transitive dependency with no untrusted input, devDeps only, or build tool only): add an exclusion entry to `audit.ignore.local.json` with the format `{"id": "GHSA-xxx", "package": "pkg-name", "reason": "why this is safe for this project"}`, then commit and retry the push
11
+
12
+ ### Critical: Override the vulnerable package, not its parent
13
+
14
+ When the audit output shows a dependency chain like `@expo/cli › glob › minimatch`, the vulnerable package is **minimatch**, not glob. Always override the **leaf package** that has the actual vulnerability.
15
+
16
+ **Never override a parent package to force a lower major version** — other packages in the project may depend on a newer major version, and a resolution/override forces ALL installations to the specified version. For example, overriding `glob` to `^8.1.0` will break `@expo/cli` which requires `glob@^13.0.0`, causing `expo prebuild` to fail with `files.map is not a function`.
17
+
18
+ Before adding a resolution/override, verify:
19
+ - You are targeting the **actually vulnerable package**, not a parent in the chain
20
+ - The override version is **compatible with all dependents** (check with `bun why <package>` or `npm ls <package>`)
21
+ - The override does not **downgrade** a package across a major version boundary that other dependencies require
22
+
23
+ ## Rails Projects (bundler-audit)
24
+
25
+ 1. Note the advisory ID, affected gem, and advisory URL from the error output
26
+ 2. Check if a patched version of the gem exists
27
+ 3. If a patched version exists:
28
+ - If the gem is a **direct dependency** (listed in Gemfile): update its version constraint in Gemfile, run `bundle update <gem>`, commit the changes, and retry the push
29
+ - If the gem is a **transitive dependency** (not in Gemfile, only in Gemfile.lock): run `bundle update <gem>` to pull the patched version without changing the Gemfile, commit the lockfile change, and retry the push
30
+ 4. If no patched version exists and the vulnerability is safe for this project: document the exception and retry the push
@@ -0,0 +1,93 @@
1
+ # Empirical Verification
2
+
3
+ This repository supports AI agents as first-class contributors.
4
+
5
+ This file is the operational contract that defines how agents plan work, execute changes, verify outcomes, and escalate when blocked.
6
+
7
+ If anything here conflicts with other repo docs, treat this file as authoritative for agent behavior.
8
+
9
+ ---
10
+
11
+ ## Core Principle
12
+
13
+ Agents must close the loop between code changes and observable system behavior.
14
+
15
+ No agent may claim success without evidence from runtime verification.
16
+
17
+ Never assume something works because the code "looks correct." Run a command, observe the output, compare to expected result.
18
+
19
+ **Verification is not linting, typechecking, or testing.** Those are quality checks. Verification is using the resulting software the way a user would — interacting with the UI, calling the API, running the CLI command, observing the behavior. Tests pass in isolation; verification proves the system works as a whole. Passing tests, linting, and typechecking does not constitute verification.
20
+
21
+ Verification is mandatory. Never skip it, defer it, or claim it was unnecessary. Every task must be verified before claiming completion.
22
+
23
+ Before starting implementation, state your verification plan — how you will use the resulting software to prove it works. Do not begin implementation until the plan is confirmed.
24
+
25
+ After verifying a change empirically, encode that verification as automated tests. The manual proof that something works should become a repeatable regression test that catches future regressions. Every verification should answer: "How do I turn this into a test?"
26
+
27
+ Every pull request must include step-by-step instructions for reviewers to independently replicate the verification. These are not test commands — they are the exact steps a human would follow to use the software and confirm the change works. If a reviewer cannot reproduce your verification from the PR description alone, the PR is incomplete.
28
+
29
+ ---
30
+
31
+ ## Roles
32
+
33
+ ### Builder Agent
34
+
35
+ Implements the change in code and infrastructure.
36
+
37
+ ### Verifier Agent
38
+
39
+ Acts as the end user (human user, API client, operator, attacker, or system) and produces proof artifacts.
40
+
41
+ Verifier Agent must be independent from Builder Agent when possible.
42
+
43
+ ### Human Overseer
44
+
45
+ Approves risky operations, security boundary crossings, and any work the agents cannot fully verify.
46
+
47
+ ---
48
+
49
+ ## Verification Levels
50
+
51
+ Agents must label every task outcome with exactly one of these:
52
+
53
+ - **FULLY VERIFIED**: Verified in the target environment with end-user simulation and captured artifacts.
54
+ - **PARTIALLY VERIFIED**: Verified in a lower-fidelity environment or with incomplete surfaces, with explicit gaps documented.
55
+ - **UNVERIFIED**: Verification blocked, human action required, no claim of correctness permitted.
56
+
57
+ ---
58
+
59
+ ## Verification Types
60
+
61
+ Every change requires one or more verification types. Classify the change first, then verify each applicable type.
62
+
63
+ ### Always Required
64
+
65
+ | Type | What to prove | Acceptable proof |
66
+ |------|---------------|------------------|
67
+ | **Test** | Unit and integration tests pass for all changed code paths | Test runner output showing all relevant tests green with no skips |
68
+ | **Type Safety** | No type errors introduced by the change | Type checker exits clean on the full project |
69
+ | **Lint/Format** | Code meets project style and quality rules | Linter and formatter exit clean on changed files |
70
+
71
+ ### Conditional
72
+
73
+ | Type | When it applies | What to prove | Acceptable proof |
74
+ |------|----------------|---------------|------------------|
75
+ | **UI** | Change affects user-visible interface | Feature renders correctly and interactions work as expected | Automated session recording or screenshots showing correct states; for cross-platform changes, evidence from each platform or explicit gap documentation |
76
+ | **API** | Change affects HTTP/GraphQL/RPC endpoints | Endpoint returns correct status, headers, and body for success and error cases | Request/response capture showing schema and data match expectations |
77
+ | **Database** | Change involves schema, migrations, or queries | Schema is correct after migration; data integrity is maintained | Query output showing expected schema and data state |
78
+ | **Auth** | Change affects authentication or authorization | Correct access for allowed roles; rejection for disallowed roles | Request traces showing enforcement across at least two roles |
79
+ | **Security** | Change involves input handling, secrets, or attack surfaces | Exploit is prevented; safe handling is enforced | Reproduction of attack pre-fix failing post-fix, or evidence of sanitization/rejection |
80
+ | **Performance** | Change claims performance improvement or affects hot paths | Measurable improvement in latency, throughput, or resource usage | Before/after benchmarks with methodology documented |
81
+ | **Infrastructure** | Change affects deployment, scaling, or cloud resources | Resources are created/updated correctly; system is stable | Infrastructure state output showing expected configuration; stability metrics during transition |
82
+ | **Observability** | Change affects logging, metrics, or tracing | Events are emitted with correct structure and correlation | Log or metric output showing expected entries with correlation IDs |
83
+ | **Background Jobs** | Change affects queues, workers, or scheduled tasks | Job enqueues, processes, and reaches terminal state correctly | Evidence of enqueue, processing, and final state; idempotency check when relevant |
84
+ | **Configuration** | Change affects config files, feature flags, or environment variables | Configuration is loaded and applied correctly at runtime | Application output showing the configuration taking effect |
85
+ | **Documentation** | Change affects documentation content or structure | Documentation is accurate and matches implementation | Content inspection confirming accuracy against actual behavior |
86
+ | **Cache** | Change affects caching behavior or invalidation | Cache hits, misses, and invalidation work as expected | Evidence of cache behavior (hit/miss logs, TTL verification, key inspection) |
87
+ | **Email/Notification** | Change affects outbound messages | Message is sent with correct content to correct recipient | Captured message content or delivery log showing expected output |
88
+ | **PR** | Shipping code (always applies when opening a PR) | PR includes goal summary, verification level, proof artifacts, and reproduction steps | PR description containing all required sections |
89
+ | **Deploy** | Shipping code to an environment | Deployment completes and application is healthy in the target environment | Deployment output and health check evidence from the target environment |
90
+
91
+ ---
92
+
93
+ For the full verification lifecycle (classify, check tooling, plan, execute, loop), surfaces, escalation protocol, and proof artifact requirements, see the `verification-lifecycle` skill loaded by the `verification-specialist` agent.
@@ -4,6 +4,12 @@
4
4
  "description": "Ruby on Rails-specific hooks — RuboCop linting/formatting and ast-grep scanning on edit",
5
5
  "author": { "name": "Cody Swann" },
6
6
  "hooks": {
7
+ "SessionStart": [
8
+ { "matcher": "", "hooks": [{ "type": "command", "command": "${CLAUDE_PLUGIN_ROOT}/hooks/inject-rules.sh" }] }
9
+ ],
10
+ "SubagentStart": [
11
+ { "matcher": "", "hooks": [{ "type": "command", "command": "${CLAUDE_PLUGIN_ROOT}/hooks/inject-rules.sh" }] }
12
+ ],
7
13
  "PostToolUse": [
8
14
  {
9
15
  "matcher": "Write|Edit",
@@ -0,0 +1,22 @@
1
+ #!/usr/bin/env bash
2
+ # Reads all .md files from the plugin's rules/ directory and injects them
3
+ # into the session context via additionalContext.
4
+ # Used by SessionStart and SubagentStart hooks.
5
+ set -euo pipefail
6
+
7
+ RULES_DIR="${CLAUDE_PLUGIN_ROOT}/rules"
8
+
9
+ # Bail silently if no rules directory
10
+ [ -d "$RULES_DIR" ] || exit 0
11
+
12
+ CONTEXT=""
13
+ for file in "$RULES_DIR"/*.md; do
14
+ [ -f "$file" ] || continue
15
+ CONTEXT+="$(cat "$file")"$'\n\n'
16
+ done
17
+
18
+ # Bail if no rules found
19
+ [ -n "$CONTEXT" ] || exit 0
20
+
21
+ # Output as JSON — jq handles escaping
22
+ jq -n --arg ctx "$CONTEXT" '{"additionalContext": $ctx}'
@@ -0,0 +1,176 @@
1
+ # Rails Coding Conventions
2
+
3
+ This rule enforces Rails-specific coding standards for consistency, maintainability, and performance.
4
+
5
+ ## Fat Models, Skinny Controllers
6
+
7
+ Controllers handle HTTP concerns only. Business logic belongs in models or service objects.
8
+
9
+ ```ruby
10
+ # Correct — controller delegates to model
11
+ class OrdersController < ApplicationController
12
+ def create
13
+ @order = Order.place(order_params, current_user)
14
+ redirect_to @order
15
+ end
16
+ end
17
+
18
+ # Wrong — business logic in controller
19
+ class OrdersController < ApplicationController
20
+ def create
21
+ @order = Order.new(order_params)
22
+ @order.user = current_user
23
+ @order.total = @order.line_items.sum(&:price)
24
+ @order.apply_discount(current_user.discount_rate)
25
+ @order.save!
26
+ OrderMailer.confirmation(@order).deliver_later
27
+ redirect_to @order
28
+ end
29
+ end
30
+ ```
31
+
32
+ ## Service Objects
33
+
34
+ Extract complex business logic into service objects when a model method would be too large or spans multiple models.
35
+
36
+ ```ruby
37
+ # app/services/order_placement_service.rb
38
+ class OrderPlacementService
39
+ def initialize(user:, params:)
40
+ @user = user
41
+ @params = params
42
+ end
43
+
44
+ def call
45
+ order = Order.new(@params)
46
+ order.user = @user
47
+ order.calculate_total
48
+ order.save!
49
+ OrderMailer.confirmation(order).deliver_later
50
+ order
51
+ end
52
+ end
53
+ ```
54
+
55
+ ## Concerns
56
+
57
+ Use concerns to share behavior across models or controllers. Keep concerns focused on a single responsibility.
58
+
59
+ ```ruby
60
+ # app/models/concerns/searchable.rb
61
+ module Searchable
62
+ extend ActiveSupport::Concern
63
+
64
+ included do
65
+ scope :search, ->(query) { where("name ILIKE ?", "%#{query}%") }
66
+ end
67
+ end
68
+ ```
69
+
70
+ ## ActiveRecord Patterns
71
+
72
+ ### Scopes over class methods for chainable queries
73
+
74
+ ```ruby
75
+ # Correct — scope
76
+ scope :active, -> { where(active: true) }
77
+ scope :recent, -> { order(created_at: :desc) }
78
+
79
+ # Wrong — class method for simple query
80
+ def self.active
81
+ where(active: true)
82
+ end
83
+ ```
84
+
85
+ ### Validations
86
+
87
+ ```ruby
88
+ # Use built-in validators
89
+ validates :email, presence: true, uniqueness: true, format: { with: URI::MailTo::EMAIL_REGEXP }
90
+ validates :age, numericality: { greater_than: 0 }
91
+ ```
92
+
93
+ ### Callbacks — use sparingly
94
+
95
+ Prefer explicit service objects over callbacks for complex side effects. Callbacks are acceptable for simple data normalization.
96
+
97
+ ```ruby
98
+ # Acceptable — simple normalization
99
+ before_validation :normalize_email
100
+
101
+ private
102
+
103
+ def normalize_email
104
+ self.email = email&.downcase&.strip
105
+ end
106
+ ```
107
+
108
+ ## N+1 Query Prevention
109
+
110
+ Always use `includes`, `preload`, or `eager_load` to prevent N+1 queries. The Bullet gem is included to detect these in development.
111
+
112
+ ```ruby
113
+ # Correct — eager loading
114
+ @posts = Post.includes(:author, :comments).where(published: true)
115
+
116
+ # Wrong — N+1 query
117
+ @posts = Post.where(published: true)
118
+ @posts.each { |post| post.author.name } # N+1!
119
+ ```
120
+
121
+ ## Strong Parameters
122
+
123
+ Always use strong parameters in controllers. Never use `permit!`.
124
+
125
+ ```ruby
126
+ # Correct
127
+ def order_params
128
+ params.require(:order).permit(:product_id, :quantity, :notes)
129
+ end
130
+
131
+ # Wrong — permits everything
132
+ def order_params
133
+ params.require(:order).permit!
134
+ end
135
+ ```
136
+
137
+ ## Database Migrations
138
+
139
+ - Use `strong_migrations` gem constraints (included via Gemfile.lisa)
140
+ - Never modify `db/schema.rb` directly
141
+ - Always add indexes for foreign keys and commonly queried columns
142
+ - Use `change` method when the migration is reversible; use `up`/`down` when it is not
143
+
144
+ ```ruby
145
+ class AddIndexToOrdersUserId < ActiveRecord::Migration[7.2]
146
+ def change
147
+ add_index :orders, :user_id
148
+ end
149
+ end
150
+ ```
151
+
152
+ ## Testing with RSpec
153
+
154
+ - Use `let` and `let!` for test setup
155
+ - Use `described_class` instead of repeating the class name
156
+ - Use `factory_bot` for test data, not fixtures
157
+ - Use `shoulda-matchers` for model validation tests
158
+ - Keep tests focused — one assertion concept per example
159
+
160
+ ```ruby
161
+ RSpec.describe Order, type: :model do
162
+ describe "validations" do
163
+ it { is_expected.to validate_presence_of(:user) }
164
+ it { is_expected.to validate_numericality_of(:total).is_greater_than(0) }
165
+ end
166
+
167
+ describe ".recent" do
168
+ it "returns orders in descending creation order" do
169
+ old_order = create(:order, created_at: 1.day.ago)
170
+ new_order = create(:order, created_at: 1.hour.ago)
171
+
172
+ expect(described_class.recent).to eq([new_order, old_order])
173
+ end
174
+ end
175
+ end
176
+ ```
@@ -8,6 +8,7 @@
8
8
  ".claude/skills/plan-fix-linter-error",
9
9
  ".claude/skills/plan-lower-code-complexity",
10
10
  ".claude/skills/plan-reduce-max-lines",
11
- ".claude/skills/plan-reduce-max-lines-per-function"
11
+ ".claude/skills/plan-reduce-max-lines-per-function",
12
+ ".claude/rules/rails-conventions.md"
12
13
  ]
13
14
  }