@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.
- package/all/deletions.json +5 -1
- package/package.json +1 -1
- package/plugins/lisa/.claude-plugin/plugin.json +21 -1
- package/plugins/lisa/hooks/inject-rules.sh +22 -0
- package/{all/copy-overwrite/.claude → plugins/lisa}/rules/base-rules.md +6 -3
- package/{all/copy-overwrite/.claude → plugins/lisa}/rules/verification.md +10 -0
- package/plugins/lisa-cdk/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-expo/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails/.claude-plugin/plugin.json +23 -1
- package/plugins/lisa-rails/hooks/inject-rules.sh +22 -0
- package/plugins/lisa-typescript/.claude-plugin/plugin.json +1 -1
- package/plugins/src/base/.claude-plugin/plugin.json +4 -0
- package/plugins/src/base/hooks/inject-rules.sh +22 -0
- package/plugins/src/base/rules/base-rules.md +91 -0
- package/plugins/src/base/rules/coding-philosophy.md +428 -0
- package/plugins/src/base/rules/intent-routing.md +126 -0
- package/plugins/src/base/rules/security-audit-handling.md +30 -0
- package/plugins/src/base/rules/verification.md +93 -0
- package/plugins/src/rails/.claude-plugin/plugin.json +6 -0
- package/plugins/src/rails/hooks/inject-rules.sh +22 -0
- package/plugins/src/rails/rules/rails-conventions.md +176 -0
- package/rails/deletions.json +2 -1
- /package/{all/copy-overwrite/.claude → plugins/lisa}/rules/coding-philosophy.md +0 -0
- /package/{all/copy-overwrite/.claude → plugins/lisa}/rules/intent-routing.md +0 -0
- /package/{all/copy-overwrite/.claude → plugins/lisa}/rules/security-audit-handling.md +0 -0
- /package/{rails/copy-overwrite/.claude → plugins/lisa-rails}/rules/rails-conventions.md +0 -0
package/all/deletions.json
CHANGED
|
@@ -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.
|
|
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.
|
|
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.
|
|
6
|
-
2.
|
|
7
|
-
3.
|
|
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,11 +1,33 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lisa-rails",
|
|
3
|
-
"version": "1.
|
|
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}'
|
|
@@ -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
|
+
```
|
package/rails/deletions.json
CHANGED
|
@@ -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
|
}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|