Compare commits
4 Commits
6fceea7460
...
5b204c95e4
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5b204c95e4 | ||
|
|
4e9da366e4 | ||
|
|
8910413315 | ||
|
|
d475dde398 |
@@ -1,9 +1,9 @@
|
||||
{
|
||||
"active_plan": "/home/m3tam3re/p/AI/AGENTS/.sisyphus/plans/opencode-memory.md",
|
||||
"started_at": "2026-02-14T04:43:37.746Z",
|
||||
"active_plan": "/home/m3tam3re/p/AI/AGENTS/.sisyphus/plans/rules-system.md",
|
||||
"started_at": "2026-02-17T17:50:08.922Z",
|
||||
"session_ids": [
|
||||
"ses_3a5a47a05ffeoNYfz2RARYsHX9"
|
||||
"ses_393691db2ffe4YZvieMFehJe54"
|
||||
],
|
||||
"plan_name": "opencode-memory",
|
||||
"plan_name": "rules-system",
|
||||
"agent": "atlas"
|
||||
}
|
||||
1
.sisyphus/evidence/final-qa/scenario1-empty-config.json
Normal file
1
.sisyphus/evidence/final-qa/scenario1-empty-config.json
Normal file
@@ -0,0 +1 @@
|
||||
{"instructions":[".opencode-rules/concerns/coding-style.md",".opencode-rules/concerns/naming.md",".opencode-rules/concerns/documentation.md",".opencode-rules/concerns/testing.md",".opencode-rules/concerns/git-workflow.md",".opencode-rules/concerns/project-structure.md"],"shellHook":"# Create/update symlink to AGENTS rules directory\nln -sfn /nix/store/wsqzf0z3hg8mhpq484f24fm72qp4k6sg-AGENTS/rules .opencode-rules\n\n# Generate opencode.json configuration file\ncat > opencode.json <<'OPENCODE_EOF'\n{\"$schema\":\"https://opencode.ai/config.json\",\"instructions\":[\".opencode-rules/concerns/coding-style.md\",\".opencode-rules/concerns/naming.md\",\".opencode-rules/concerns/documentation.md\",\".opencode-rules/concerns/testing.md\",\".opencode-rules/concerns/git-workflow.md\",\".opencode-rules/concerns/project-structure.md\"]}\nOPENCODE_EOF\n"}
|
||||
@@ -0,0 +1 @@
|
||||
{"instructions":[".opencode-rules/concerns/coding-style.md",".opencode-rules/concerns/naming.md",".opencode-rules/concerns/documentation.md",".opencode-rules/concerns/testing.md",".opencode-rules/concerns/git-workflow.md",".opencode-rules/concerns/project-structure.md",".opencode-rules/languages/python.md"],"shellHook":"# Create/update symlink to AGENTS rules directory\nln -sfn /nix/store/4li05383sgf4z0l6bxv8hmvgs600y56x-AGENTS/rules .opencode-rules\n\n# Generate opencode.json configuration file\ncat > opencode.json <<'OPENCODE_EOF'\n{\"$schema\":\"https://opencode.ai/config.json\",\"instructions\":[\".opencode-rules/concerns/coding-style.md\",\".opencode-rules/concerns/naming.md\",\".opencode-rules/concerns/documentation.md\",\".opencode-rules/concerns/testing.md\",\".opencode-rules/concerns/git-workflow.md\",\".opencode-rules/concerns/project-structure.md\",\".opencode-rules/languages/python.md\"]}\nOPENCODE_EOF\n"}
|
||||
@@ -0,0 +1 @@
|
||||
{"instructions":[".opencode-rules/concerns/coding-style.md",".opencode-rules/concerns/naming.md",".opencode-rules/concerns/documentation.md",".opencode-rules/concerns/testing.md",".opencode-rules/concerns/git-workflow.md",".opencode-rules/concerns/project-structure.md",".opencode-rules/languages/python.md",".opencode-rules/languages/typescript.md",".opencode-rules/languages/nix.md",".opencode-rules/languages/shell.md"],"shellHook":"# Create/update symlink to AGENTS rules directory\nln -sfn /nix/store/qzsdn3m85qwarpd43x8k28sja40r21p7-AGENTS/rules .opencode-rules\n\n# Generate opencode.json configuration file\ncat > opencode.json <<'OPENCODE_EOF'\n{\"$schema\":\"https://opencode.ai/config.json\",\"instructions\":[\".opencode-rules/concerns/coding-style.md\",\".opencode-rules/concerns/naming.md\",\".opencode-rules/concerns/documentation.md\",\".opencode-rules/concerns/testing.md\",\".opencode-rules/concerns/git-workflow.md\",\".opencode-rules/concerns/project-structure.md\",\".opencode-rules/languages/python.md\",\".opencode-rules/languages/typescript.md\",\".opencode-rules/languages/nix.md\",\".opencode-rules/languages/shell.md\"]}\nOPENCODE_EOF\n"}
|
||||
@@ -0,0 +1 @@
|
||||
{"instructions":[".opencode-rules/concerns/coding-style.md",".opencode-rules/concerns/naming.md",".opencode-rules/concerns/documentation.md",".opencode-rules/concerns/testing.md",".opencode-rules/concerns/git-workflow.md",".opencode-rules/concerns/project-structure.md",".opencode-rules/languages/python.md",".opencode-rules/frameworks/n8n.md"],"shellHook":"# Create/update symlink to AGENTS rules directory\nln -sfn /nix/store/55brjhy9m1vcgrnd100vmwf9bycjpzpi-AGENTS/rules .opencode-rules\n\n# Generate opencode.json configuration file\ncat > opencode.json <<'OPENCODE_EOF'\n{\"$schema\":\"https://opencode.ai/config.json\",\"instructions\":[\".opencode-rules/concerns/coding-style.md\",\".opencode-rules/concerns/naming.md\",\".opencode-rules/concerns/documentation.md\",\".opencode-rules/concerns/testing.md\",\".opencode-rules/concerns/git-workflow.md\",\".opencode-rules/concerns/project-structure.md\",\".opencode-rules/languages/python.md\",\".opencode-rules/frameworks/n8n.md\"]}\nOPENCODE_EOF\n"}
|
||||
@@ -0,0 +1 @@
|
||||
{"instructions":[".opencode-rules/concerns/coding-style.md",".opencode-rules/concerns/naming.md",".opencode-rules/concerns/documentation.md",".opencode-rules/concerns/testing.md",".opencode-rules/concerns/git-workflow.md",".opencode-rules/concerns/project-structure.md",".opencode-rules/languages/python.md",".opencode-rules/custom.md"],"shellHook":"# Create/update symlink to AGENTS rules directory\nln -sfn /nix/store/r8yfirsyyii9x05qd5kfdvzcqv7sx6az-AGENTS/rules .opencode-rules\n\n# Generate opencode.json configuration file\ncat > opencode.json <<'OPENCODE_EOF'\n{\"$schema\":\"https://opencode.ai/config.json\",\"instructions\":[\".opencode-rules/concerns/coding-style.md\",\".opencode-rules/concerns/naming.md\",\".opencode-rules/concerns/documentation.md\",\".opencode-rules/concerns/testing.md\",\".opencode-rules/concerns/git-workflow.md\",\".opencode-rules/concerns/project-structure.md\",\".opencode-rules/languages/python.md\",\".opencode-rules/custom.md\"]}\nOPENCODE_EOF\n"}
|
||||
153
.sisyphus/evidence/final-qa/summary.md
Normal file
153
.sisyphus/evidence/final-qa/summary.md
Normal file
@@ -0,0 +1,153 @@
|
||||
# Opencode Rules Nix Module - Manual QA Results
|
||||
|
||||
## Test Summary
|
||||
Date: 2025-02-17
|
||||
Module: `/home/m3tam3re/p/NIX/nixpkgs/lib/opencode-rules.nix`
|
||||
Test Type: Manual QA (nix eval)
|
||||
|
||||
---
|
||||
|
||||
## Scenario Results
|
||||
|
||||
### Scenario 1: Empty Config (Defaults Only)
|
||||
**Command**: `nix eval --impure --json --expr 'let pkgs = import <nixpkgs> {}; m3taLib = import /home/m3tam3re/p/NIX/nixpkgs/lib {lib = pkgs.lib;}; in m3taLib.opencode-rules.mkOpencodeRules { agents = /home/m3tam3re/p/AI/AGENTS; }'`
|
||||
|
||||
**Results**:
|
||||
- ✅ Valid JSON output
|
||||
- ✅ Has `$schema` field in embedded opencode.json
|
||||
- ✅ Has `instructions` field
|
||||
- ✅ Correct instruction count: 6 (default concerns only)
|
||||
|
||||
**Expected Instructions**:
|
||||
1. `.opencode-rules/concerns/coding-style.md`
|
||||
2. `.opencode-rules/concerns/naming.md`
|
||||
3. `.opencode-rules/concerns/documentation.md`
|
||||
4. `.opencode-rules/concerns/testing.md`
|
||||
5. `.opencode-rules/concerns/git-workflow.md`
|
||||
6. `.opencode-rules/concerns/project-structure.md`
|
||||
|
||||
---
|
||||
|
||||
### Scenario 2: Single Language (Python)
|
||||
**Command**: `nix eval --impure --json --expr 'let pkgs = import <nixpkgs> {}; m3taLib = import /home/m3tam3re/p/NIX/nixpkgs/lib {lib = pkgs.lib;}; in m3taLib.opencode-rules.mkOpencodeRules { agents = /home/m3tam3re/p/AI/AGENTS; languages = ["python"]; }'`
|
||||
|
||||
**Results**:
|
||||
- ✅ Valid JSON output
|
||||
- ✅ Has `$schema` field in embedded opencode.json
|
||||
- ✅ Has `instructions` field
|
||||
- ✅ Correct instruction count: 7 (6 concerns + 1 language)
|
||||
|
||||
**Expected Instructions**:
|
||||
- All 6 default concerns
|
||||
- `.opencode-rules/languages/python.md`
|
||||
|
||||
---
|
||||
|
||||
### Scenario 3: Multi-Language
|
||||
**Command**: `nix eval --impure --json --expr 'let pkgs = import <nixpkgs> {}; m3taLib = import /home/m3tam3re/p/NIX/nixpkgs/lib {lib = pkgs.lib;}; in m3taLib.opencode-rules.mkOpencodeRules { agents = /home/m3tam3re/p/AI/AGENTS; languages = ["python" "typescript" "nix" "shell"]; }'`
|
||||
|
||||
**Results**:
|
||||
- ✅ Valid JSON output
|
||||
- ✅ Has `$schema` field in embedded opencode.json
|
||||
- ✅ Has `instructions` field
|
||||
- ✅ Correct instruction count: 10 (6 concerns + 4 languages)
|
||||
|
||||
**Expected Instructions**:
|
||||
- All 6 default concerns
|
||||
- `.opencode-rules/languages/python.md`
|
||||
- `.opencode-rules/languages/typescript.md`
|
||||
- `.opencode-rules/languages/nix.md`
|
||||
- `.opencode-rules/languages/shell.md`
|
||||
|
||||
---
|
||||
|
||||
### Scenario 4: With Frameworks
|
||||
**Command**: `nix eval --impure --json --expr 'let pkgs = import <nixpkgs> {}; m3taLib = import /home/m3tam3re/p/NIX/nixpkgs/lib {lib = pkgs.lib;}; in m3taLib.opencode-rules.mkOpencodeRules { agents = /home/m3tam3re/p/AI/AGENTS; languages = ["python"]; frameworks = ["n8n"]; }'`
|
||||
|
||||
**Results**:
|
||||
- ✅ Valid JSON output
|
||||
- ✅ Has `$schema` field in embedded opencode.json
|
||||
- ✅ Has `instructions` field
|
||||
- ✅ Correct instruction count: 8 (6 concerns + 1 language + 1 framework)
|
||||
|
||||
**Expected Instructions**:
|
||||
- All 6 default concerns
|
||||
- `.opencode-rules/languages/python.md`
|
||||
- `.opencode-rules/frameworks/n8n.md`
|
||||
|
||||
---
|
||||
|
||||
### Scenario 5: Extra Instructions
|
||||
**Command**: `nix eval --impure --json --expr 'let pkgs = import <nixpkgs> {}; m3taLib = import /home/m3tam3re/p/NIX/nixpkgs/lib {lib = pkgs.lib;}; in m3taLib.opencode-rules.mkOpencodeRules { agents = /home/m3tam3re/p/AI/AGENTS; languages = ["python"]; extraInstructions = [".opencode-rules/custom.md"]; }'`
|
||||
|
||||
**Results**:
|
||||
- ✅ Valid JSON output
|
||||
- ✅ Has `$schema` field in embedded opencode.json
|
||||
- ✅ Has `instructions` field
|
||||
- ✅ Correct instruction count: 8 (6 concerns + 1 language + 1 custom)
|
||||
|
||||
**Expected Instructions**:
|
||||
- All 6 default concerns
|
||||
- `.opencode-rules/languages/python.md`
|
||||
- `.opencode-rules/custom.md`
|
||||
|
||||
---
|
||||
|
||||
## Content Quality Spot Checks
|
||||
|
||||
### 1. coding-style.md (Concern Rule)
|
||||
**Assessment**: ✅ High Quality
|
||||
- Clear critical rules with "Always/Never" directives
|
||||
- Good vs. bad code examples
|
||||
- Comprehensive coverage: formatting, patterns, error handling, type safety, function design, SOLID
|
||||
- Well-structured sections
|
||||
|
||||
### 2. python.md (Language Rule)
|
||||
**Assessment**: ✅ High Quality
|
||||
- Modern toolchain recommendations (uv, ruff, pyright, pytest, hypothesis)
|
||||
- Common idioms with practical examples
|
||||
- Anti-patterns with explanations
|
||||
- Project setup structure
|
||||
- Clear, actionable code snippets
|
||||
|
||||
### 3. n8n.md (Framework Rule)
|
||||
**Assessment**: ✅ High Quality
|
||||
- Concise workflow design principles
|
||||
- Clear naming conventions
|
||||
- Error handling patterns
|
||||
- Security best practices
|
||||
- Actionable testing guidelines
|
||||
|
||||
---
|
||||
|
||||
## Issues Encountered
|
||||
|
||||
### Socket File Issue
|
||||
**Issue**: `nix eval` failed with `error: file '/home/m3tam3re/p/AI/AGENTS/.beads/bd.sock' has an unsupported type`
|
||||
|
||||
**Workaround**: Temporarily moved `.beads` directory outside the AGENTS tree during testing
|
||||
|
||||
**Root Cause**: Nix attempts to evaluate/store the `agents` path recursively and encounters unsupported socket files (Unix domain sockets)
|
||||
|
||||
**Recommendation**: Consider adding `.beads` to `.gitignore` and excluding it from path evaluation if possible, or document this limitation for users
|
||||
|
||||
---
|
||||
|
||||
## Final Verdict
|
||||
|
||||
```
|
||||
Scenarios [5/5 pass] | VERDICT: OKAY
|
||||
```
|
||||
|
||||
### Summary
|
||||
- All 5 test scenarios executed successfully
|
||||
- All JSON outputs are valid and properly structured
|
||||
- All embedded `opencode.json` configurations have required `$schema` and `instructions` fields
|
||||
- Instruction counts match expected values for each scenario
|
||||
- Rule content quality is high across concern, language, and framework rules
|
||||
- Shell hook properly generates symlink and configuration file
|
||||
|
||||
### Notes
|
||||
- Socket file issue requires workaround (documented)
|
||||
- Module correctly handles default concerns, multiple languages, frameworks, and custom instructions
|
||||
- Code examples in rules are clear and actionable
|
||||
6
.sisyphus/evidence/task-17-budget.txt
Normal file
6
.sisyphus/evidence/task-17-budget.txt
Normal file
@@ -0,0 +1,6 @@
|
||||
=== Context Budget ===
|
||||
Concerns: 751
|
||||
Python: 224
|
||||
Total (concerns + python): 975
|
||||
Limit: 1500
|
||||
RESULT: PASS (under 1500)
|
||||
1
.sisyphus/evidence/task-17-full-integration.txt
Normal file
1
.sisyphus/evidence/task-17-full-integration.txt
Normal file
@@ -0,0 +1 @@
|
||||
[".opencode-rules/concerns/coding-style.md",".opencode-rules/concerns/naming.md",".opencode-rules/concerns/documentation.md",".opencode-rules/concerns/testing.md",".opencode-rules/concerns/git-workflow.md",".opencode-rules/concerns/project-structure.md",".opencode-rules/languages/python.md",".opencode-rules/languages/typescript.md",".opencode-rules/languages/nix.md",".opencode-rules/languages/shell.md",".opencode-rules/frameworks/n8n.md"]
|
||||
16
.sisyphus/evidence/task-17-inventory.txt
Normal file
16
.sisyphus/evidence/task-17-inventory.txt
Normal file
@@ -0,0 +1,16 @@
|
||||
=== Task 17 Integration Test ===
|
||||
|
||||
File Line Counts:
|
||||
163 /home/m3tam3re/p/AI/AGENTS/rules/concerns/coding-style.md
|
||||
149 /home/m3tam3re/p/AI/AGENTS/rules/concerns/documentation.md
|
||||
118 /home/m3tam3re/p/AI/AGENTS/rules/concerns/git-workflow.md
|
||||
105 /home/m3tam3re/p/AI/AGENTS/rules/concerns/naming.md
|
||||
82 /home/m3tam3re/p/AI/AGENTS/rules/concerns/project-structure.md
|
||||
134 /home/m3tam3re/p/AI/AGENTS/rules/concerns/testing.md
|
||||
129 /home/m3tam3re/p/AI/AGENTS/rules/languages/nix.md
|
||||
224 /home/m3tam3re/p/AI/AGENTS/rules/languages/python.md
|
||||
100 /home/m3tam3re/p/AI/AGENTS/rules/languages/shell.md
|
||||
150 /home/m3tam3re/p/AI/AGENTS/rules/languages/typescript.md
|
||||
42 /home/m3tam3re/p/AI/AGENTS/rules/frameworks/n8n.md
|
||||
1396 total
|
||||
RESULT: All 11 files present
|
||||
13
.sisyphus/evidence/task-17-paths-resolve.txt
Normal file
13
.sisyphus/evidence/task-17-paths-resolve.txt
Normal file
@@ -0,0 +1,13 @@
|
||||
=== Path Resolution Check ===
|
||||
OK: rules/concerns/coding-style.md exists
|
||||
OK: rules/concerns/naming.md exists
|
||||
OK: rules/concerns/documentation.md exists
|
||||
OK: rules/concerns/testing.md exists
|
||||
OK: rules/concerns/git-workflow.md exists
|
||||
OK: rules/concerns/project-structure.md exists
|
||||
OK: rules/languages/python.md exists
|
||||
OK: rules/languages/typescript.md exists
|
||||
OK: rules/languages/nix.md exists
|
||||
OK: rules/languages/shell.md exists
|
||||
OK: rules/frameworks/n8n.md exists
|
||||
RESULT: All paths resolve
|
||||
60
.sisyphus/notepads/rules-system/learnings.md
Normal file
60
.sisyphus/notepads/rules-system/learnings.md
Normal file
@@ -0,0 +1,60 @@
|
||||
# Rules System - Learnings
|
||||
|
||||
## 2026-02-17T17:50 Session Start
|
||||
|
||||
### Architecture Pattern
|
||||
- Nix helper lives in nixpkgs repo (not AGENTS) - follows ports.nix pattern
|
||||
- AGENTS repo stays pure content (markdown rule files only)
|
||||
- Pattern: `{lib}: { mkOpencodeRules = ...; }`
|
||||
|
||||
### Key Files
|
||||
- nixpkgs: `/home/m3tam3re/p/NIX/nixpkgs/lib/ports.nix` (reference pattern)
|
||||
- nixos-config: `/home/m3tam3re/p/NIX/nixos-config/home/features/coding/opencode.nix` (deployment)
|
||||
- AGENTS: `rules/` directory (content)
|
||||
|
||||
### mkOpencodeRules Signature
|
||||
```nix
|
||||
mkOpencodeRules {
|
||||
agents = inputs.agents; # Non-flake input path
|
||||
languages = [ "python" "typescript" ];
|
||||
concerns ? [ "coding-style" "naming" "documentation" "testing" "git-workflow" "project-structure" ];
|
||||
frameworks ? [ "n8n" ];
|
||||
extraInstructions ? [];
|
||||
}
|
||||
```
|
||||
|
||||
### Consumption Pattern
|
||||
```nix
|
||||
let
|
||||
m3taLib = inputs.m3ta-nixpkgs.lib.${system};
|
||||
rules = m3taLib.opencode-rules.mkOpencodeRules {
|
||||
agents = inputs.agents;
|
||||
languages = [ "python" ];
|
||||
};
|
||||
in pkgs.mkShell { shellHook = rules.shellHook; }
|
||||
```
|
||||
|
||||
### Wave 1: Directory Structure (2026-02-17T18:54)
|
||||
- Successfully created rules/ directory with subdirectories: concerns/, languages/, frameworks/
|
||||
- Added .gitkeep files to each subdirectory (git needs at least one file to track empty directories)
|
||||
- Pattern reference: followed skills/ directory structure convention
|
||||
- USAGE.md already existed in rules/ (created by previous wave)
|
||||
- AGENTS repo stays pure content - no Nix files added (as planned)
|
||||
- Verification: ls confirms all three .gitkeep files exist in proper locations
|
||||
|
||||
### Wave 2: Nix Helper Implementation (2026-02-17T19:02)
|
||||
- Successfully created `/home/m3tam3re/p/NIX/nixpkgs/lib/opencode-rules.nix`
|
||||
- Followed ports.nix pattern EXACTLY: `{lib}: { mkOpencodeRules = ...; }`
|
||||
- Function signature: `{ agents, languages ? [], concerns ? [...], frameworks ? [], extraInstructions ? [] }`
|
||||
- Returns: `{ shellHook, instructions }`
|
||||
- Instructions list built using map functions for each category (concerns, languages, frameworks, extra)
|
||||
- ShellHook creates symlink `.opencode-rules` → `${agents}/rules` and generates `opencode.json` with `$schema`
|
||||
- JSON generation uses `builtins.toJSON opencodeConfig` where opencodeConfig = `{ "$schema" = "..."; inherit instructions; }`
|
||||
- Comprehensive doc comments added matching ports.nix style (multi-line comments with usage examples)
|
||||
- All paths relative to project root via `.opencode-rules/` prefix
|
||||
- Verification passed:
|
||||
- `nix eval --impure` shows file loads and exposes `mkOpencodeRules`
|
||||
- Function returns `{ instructions, shellHook }`
|
||||
- Instructions list builds correctly (concerns + languages + frameworks + extra)
|
||||
- `nix-instantiate --parse` validates syntax is correct
|
||||
- ShellHook contains both symlink creation and JSON generation (heredoc pattern)
|
||||
804
.sisyphus/plans/rules-system.md
Normal file
804
.sisyphus/plans/rules-system.md
Normal file
@@ -0,0 +1,804 @@
|
||||
# Centralized Rules & Per-Project Context Injection System
|
||||
|
||||
## TL;DR
|
||||
|
||||
> **Quick Summary**: Create a `rules/` directory in the AGENTS repository containing modular AI coding rules (per-concern + per-language), deployed centrally via Home Manager. A `mkOpencodeRules` Nix helper function lives in the nixpkgs repo (following the existing `ports.nix` → `mkPortHelpers` pattern), generating per-project `opencode.json` via devShell activation.
|
||||
>
|
||||
> **Deliverables**:
|
||||
> - 6 concern rule files (coding-style, naming, documentation, testing, git-workflow, project-structure)
|
||||
> - 5 language/framework rule files (python, typescript, nix, shell, n8n)
|
||||
> - `lib/opencode-rules.nix` in nixpkgs repo — `mkOpencodeRules` helper function
|
||||
> - Updated `lib/default.nix` in nixpkgs repo — imports opencode-rules
|
||||
> - Updated `opencode.nix` in nixos-config — deploys rules/ alongside existing skills
|
||||
> - `rules/USAGE.md` — per-project adoption documentation
|
||||
>
|
||||
> **Repos Touched**: 3 (AGENTS, nixpkgs, nixos-config)
|
||||
> **Estimated Effort**: Medium (11 rule files + 3 nix changes + 1 doc)
|
||||
> **Parallel Execution**: YES — 4 waves
|
||||
> **Critical Path**: T1-T3 (foundation) → T6-T16 (content) → T17 (verification)
|
||||
|
||||
---
|
||||
|
||||
## Context
|
||||
|
||||
### Original Request
|
||||
User wants to streamline their Agent workflow by centrally managing language-specific and framework-specific coding rules in the AGENTS repository, while allowing project-specific overrides. Rules should be injected per-project using Nix flakes + direnv.
|
||||
|
||||
### Interview Summary
|
||||
**Key Discussions**:
|
||||
- **Loading strategy**: Always loaded (not lazy) — rules always in context when project activates
|
||||
- **Composition mechanism**: Nix flake devShell — each project declares languages/frameworks needed
|
||||
- **Rule granularity**: Per concern with separate language files for deep patterns
|
||||
- **Override strategy**: Project-level AGENTS.md overrides central rules (OpenCode's native precedence)
|
||||
- **opencode.json**: No project-specific one exists yet — devShell generates it entirely
|
||||
- **Nix helper location**: Lives in `m3ta-nixpkgs` repo at `lib/opencode-rules.nix` (follows `ports.nix` pattern)
|
||||
- **AGENTS repo stays pure content**: No Nix code — only markdown rule files
|
||||
|
||||
**Research Findings**:
|
||||
- OpenCode `instructions` field in `opencode.json` loads external .md files as always-on context
|
||||
- Anthropic guide: progressive disclosure, composability, 500-line max, use TOCs for long files
|
||||
- Best practices: 100-200 lines per file, imperative language, micro-examples (correct/incorrect)
|
||||
- Rule files benefit from sandwich principle: critical constraints at START and END
|
||||
|
||||
### Metis Review
|
||||
**Identified Gaps** (addressed):
|
||||
- **Rule update strategy**: When rules change in AGENTS repo, projects run `nix flake update agents`. Standard Nix flow.
|
||||
- **Multi-language projects**: `mkOpencodeRules { languages = [ "python" "typescript" ]; }` — list multiple.
|
||||
- **Context window budget**: ~800-1300 lines total. Well under 1500-line budget.
|
||||
- **Empty rules selection**: `mkOpencodeRules {}` loads only concern files (defaults to all 6).
|
||||
|
||||
### Architecture Decision: Nix Helper Location
|
||||
**Decision**: `mkOpencodeRules` lives in **nixpkgs repo** (`/home/m3tam3re/p/NIX/nixpkgs/lib/opencode-rules.nix`), NOT in AGENTS repo.
|
||||
|
||||
**Rationale**:
|
||||
- nixpkgs already has `lib/ports.nix` → `mkPortHelpers` as an identical pattern
|
||||
- nixpkgs is already consumed by all configs: `inputs.m3ta-nixpkgs.lib.${system}`
|
||||
- AGENTS repo stays pure content (markdown + configs), no Nix code
|
||||
- Projects already have `m3ta-nixpkgs` as a flake input — no new input needed for the helper
|
||||
|
||||
**Consumption pattern** (per-project):
|
||||
```nix
|
||||
let
|
||||
m3taLib = inputs.m3ta-nixpkgs.lib.${system};
|
||||
rules = m3taLib.opencode-rules.mkOpencodeRules {
|
||||
agents = inputs.agents; # Non-flake input with rule content
|
||||
languages = [ "python" ];
|
||||
};
|
||||
in pkgs.mkShell { shellHook = rules.shellHook; }
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Work Objectives
|
||||
|
||||
### Core Objective
|
||||
Create a centralized, modular AI coding rules system managed in the AGENTS repo, with a Nix helper in nixpkgs for per-project injection via devShell + direnv.
|
||||
|
||||
### Concrete Deliverables
|
||||
- `rules/concerns/{coding-style,naming,documentation,testing,git-workflow,project-structure}.md` — in AGENTS repo
|
||||
- `rules/languages/{python,typescript,nix,shell}.md` — in AGENTS repo
|
||||
- `rules/frameworks/n8n.md` — in AGENTS repo
|
||||
- `rules/USAGE.md` — adoption documentation in AGENTS repo
|
||||
- `lib/opencode-rules.nix` — in nixpkgs repo (`/home/m3tam3re/p/NIX/nixpkgs/`)
|
||||
- Updated `lib/default.nix` — in nixpkgs repo (add import)
|
||||
- Updated `opencode.nix` — in nixos-config repo (`/home/m3tam3re/p/NIX/nixos-config/home/features/coding/`)
|
||||
|
||||
### Definition of Done
|
||||
- [ ] All 11 rule files exist and are under 250 lines each
|
||||
- [ ] `lib/opencode-rules.nix` in nixpkgs exports `mkOpencodeRules` following `ports.nix` pattern
|
||||
- [ ] `opencode.nix` deploys `rules/` to `~/.config/opencode/rules/`
|
||||
- [ ] A project can use `m3taLib.opencode-rules.mkOpencodeRules` in devShell
|
||||
|
||||
### Must Have
|
||||
- All rule files use imperative language ("Always use...", "Never...")
|
||||
- Every rule includes micro-examples (correct vs incorrect, 2-3 lines each)
|
||||
- Concern files are language-agnostic; language subsections are brief pointers
|
||||
- Language files go deep into toolchain, idioms, anti-patterns
|
||||
- `mkOpencodeRules` accepts: `{ agents, languages ? [], concerns ? [...], frameworks ? [], extraInstructions ? [] }`
|
||||
- `mkOpencodeRules` follows `ports.nix` pattern: `{lib}: { mkOpencodeRules = ...}`
|
||||
- shellHook creates `.opencode-rules` symlink + generates `opencode.json`
|
||||
- Both `.opencode-rules` and `opencode.json` must be gitignored (documented in USAGE.md)
|
||||
|
||||
### Must NOT Have (Guardrails)
|
||||
- Rule files MUST NOT exceed 250 lines
|
||||
- Total loaded rules MUST NOT exceed 1500 lines for any realistic config
|
||||
- Concern files MUST NOT contain language-specific implementation details
|
||||
- MUST NOT put Nix code in AGENTS repo — AGENTS stays pure content
|
||||
- MUST NOT add rule versioning, testing framework, or generator CLI
|
||||
- MUST NOT create rules for docker, k8s, terraform — out of scope
|
||||
- MUST NOT modify existing skills, agents, prompts, or commands
|
||||
- MUST NOT use generic advice ("write clean code", "follow best practices")
|
||||
|
||||
---
|
||||
|
||||
## Verification Strategy (MANDATORY)
|
||||
|
||||
> **ZERO HUMAN INTERVENTION** — ALL verification is agent-executed. No exceptions.
|
||||
|
||||
### Test Decision
|
||||
- **Infrastructure exists**: NO (config/documentation repos)
|
||||
- **Automated tests**: NO
|
||||
- **Framework**: none
|
||||
|
||||
### QA Policy
|
||||
Every task MUST include agent-executed QA scenarios.
|
||||
Evidence saved to `.sisyphus/evidence/task-{N}-{scenario-slug}.{ext}`.
|
||||
|
||||
| Deliverable Type | Verification Tool | Method |
|
||||
|------------------|-------------------|--------|
|
||||
| Markdown rule files | Bash (wc, grep) | Line count, micro-examples, imperative language |
|
||||
| Nix expressions | Bash (nix eval) | Evaluate, check errors |
|
||||
| Shell integration | Bash | Verify symlink + opencode.json generated |
|
||||
| Cross-repo | Bash (grep) | Verify entries in correct files |
|
||||
|
||||
---
|
||||
|
||||
## Execution Strategy
|
||||
|
||||
### Parallel Execution Waves
|
||||
|
||||
```
|
||||
Wave 1 (Foundation — 5 tasks, all parallel):
|
||||
├── Task 1: Create rules/ directory structure in AGENTS repo [quick]
|
||||
├── Task 2: Create lib/opencode-rules.nix in nixpkgs repo [quick]
|
||||
├── Task 3: Update lib/default.nix in nixpkgs repo [quick]
|
||||
├── Task 4: Update opencode.nix in nixos-config repo [quick]
|
||||
└── Task 5: Create rules/USAGE.md in AGENTS repo [quick]
|
||||
|
||||
Wave 2 (Content — 11 rule files, all parallel):
|
||||
├── Task 6: concerns/coding-style.md [writing]
|
||||
├── Task 7: concerns/naming.md [writing]
|
||||
├── Task 8: concerns/documentation.md [writing]
|
||||
├── Task 9: concerns/testing.md [writing]
|
||||
├── Task 10: concerns/git-workflow.md [writing]
|
||||
├── Task 11: concerns/project-structure.md [writing]
|
||||
├── Task 12: languages/python.md [writing]
|
||||
├── Task 13: languages/typescript.md [writing]
|
||||
├── Task 14: languages/nix.md [writing]
|
||||
├── Task 15: languages/shell.md [writing]
|
||||
└── Task 16: frameworks/n8n.md [writing]
|
||||
|
||||
Wave 3 (Verification):
|
||||
└── Task 17: End-to-end integration test [deep]
|
||||
|
||||
Wave FINAL (Review — 4 parallel):
|
||||
├── Task F1: Plan compliance audit (oracle)
|
||||
├── Task F2: Code quality review (unspecified-high)
|
||||
├── Task F3: Real manual QA (unspecified-high)
|
||||
└── Task F4: Scope fidelity check (deep)
|
||||
|
||||
Critical Path: T1-T3 → T6-T16 (parallel) → T17 → F1-F4
|
||||
Max Concurrent: 11 (Wave 2)
|
||||
```
|
||||
|
||||
### Dependency Matrix
|
||||
|
||||
| Task | Depends On | Blocks | Wave |
|
||||
|------|------------|--------|------|
|
||||
| 1 | — | 5, 6-16, 17 | 1 |
|
||||
| 2, 3 | — | 17 | 1 |
|
||||
| 4 | — | 17 | 1 |
|
||||
| 5 | 1, 2 | 17 | 1 |
|
||||
| 6-16 | 1 | 17 | 2 |
|
||||
| 17 | 2-5, 6-16 | F1-F4 | 3 |
|
||||
| F1-F4 | 17 | — | FINAL |
|
||||
|
||||
### Agent Dispatch Summary
|
||||
|
||||
| Wave | # Parallel | Tasks and Agent Category |
|
||||
|------|------------|------------------------|
|
||||
| 1 | **5** | T1-T5 → `quick` |
|
||||
| 2 | **11** | T6-T16 → `writing` |
|
||||
| 3 | **1** | T17 → `deep` |
|
||||
| FINAL | **4** | F1 → `oracle`, F2,F3 → `unspecified-high`, F4 → `deep` |
|
||||
|
||||
---
|
||||
|
||||
## TODOs
|
||||
|
||||
- [x] 1. Create rules/ directory structure in AGENTS repo
|
||||
|
||||
**What to do**:
|
||||
- Create directory structure in `/home/m3tam3re/p/AI/AGENTS/`: `rules/concerns/`, `rules/languages/`, `rules/frameworks/`
|
||||
- Add `.gitkeep` files to each directory so they're tracked before content is added
|
||||
- This is the CONTENT repo only — NO Nix code goes here
|
||||
|
||||
**Must NOT do**:
|
||||
- Do not create any Nix files in AGENTS repo
|
||||
- Do not create rule content files (those are Wave 2)
|
||||
|
||||
**Recommended Agent Profile**:
|
||||
- **Category**: `quick`
|
||||
- **Skills**: []
|
||||
|
||||
**Parallelization**:
|
||||
- **Can Run In Parallel**: YES
|
||||
- **Parallel Group**: Wave 1 (with Tasks 2-5)
|
||||
- **Blocks**: Tasks 5, 6-16, 17
|
||||
- **Blocked By**: None
|
||||
|
||||
**References**:
|
||||
- `/home/m3tam3re/p/AI/AGENTS/skills/` — existing directory structure pattern
|
||||
|
||||
**Acceptance Criteria**:
|
||||
|
||||
**QA Scenarios (MANDATORY):**
|
||||
|
||||
```
|
||||
Scenario: Directory structure exists
|
||||
Tool: Bash
|
||||
Preconditions: None
|
||||
Steps:
|
||||
1. Run `ls /home/m3tam3re/p/AI/AGENTS/rules/concerns/.gitkeep /home/m3tam3re/p/AI/AGENTS/rules/languages/.gitkeep /home/m3tam3re/p/AI/AGENTS/rules/frameworks/.gitkeep`
|
||||
Expected Result: All 3 .gitkeep files exist
|
||||
Failure Indicators: "No such file or directory"
|
||||
Evidence: .sisyphus/evidence/task-1-dirs.txt
|
||||
|
||||
Scenario: No Nix files in AGENTS repo rules/
|
||||
Tool: Bash
|
||||
Preconditions: Dirs created
|
||||
Steps:
|
||||
1. Run `find /home/m3tam3re/p/AI/AGENTS/rules/ -name '*.nix' | wc -l`
|
||||
Expected Result: Count is 0
|
||||
Failure Indicators: Count > 0
|
||||
Evidence: .sisyphus/evidence/task-1-no-nix.txt
|
||||
```
|
||||
|
||||
**Commit**: YES
|
||||
- Message: `feat(rules): add rules directory structure`
|
||||
- Files: `rules/concerns/.gitkeep`, `rules/languages/.gitkeep`, `rules/frameworks/.gitkeep`
|
||||
|
||||
---
|
||||
|
||||
- [x] 2. Create `lib/opencode-rules.nix` in nixpkgs repo
|
||||
|
||||
**What to do**:
|
||||
- Create `/home/m3tam3re/p/NIX/nixpkgs/lib/opencode-rules.nix`
|
||||
- Follow the EXACT pattern of `lib/ports.nix`: `{lib}: { mkOpencodeRules = ...; }`
|
||||
- The function must accept: `{ agents, languages ? [], concerns ? [ "coding-style" "naming" "documentation" "testing" "git-workflow" "project-structure" ], frameworks ? [], extraInstructions ? [] }`
|
||||
- `agents` parameter = the non-flake input (path to AGENTS repo in Nix store)
|
||||
- It must return: `{ shellHook = "..."; instructions = [...]; }`
|
||||
- `shellHook` must: (a) create `.opencode-rules` symlink to `${agents}/rules`, (b) generate `opencode.json` with `$schema` and `instructions` fields using `builtins.toJSON`
|
||||
- `instructions` = list of paths relative to project root via `.opencode-rules/` symlink
|
||||
- Include comprehensive Nix doc comments (matching ports.nix style)
|
||||
|
||||
**Must NOT do**:
|
||||
- Do not deviate from ports.nix pattern
|
||||
- Do not put any code in AGENTS repo
|
||||
|
||||
**Recommended Agent Profile**:
|
||||
- **Category**: `quick`
|
||||
- Reason: One Nix file following established pattern
|
||||
- **Skills**: []
|
||||
|
||||
**Parallelization**:
|
||||
- **Can Run In Parallel**: YES
|
||||
- **Parallel Group**: Wave 1 (with Tasks 1, 3-5)
|
||||
- **Blocks**: Tasks 5, 17
|
||||
- **Blocked By**: None
|
||||
|
||||
**References**:
|
||||
|
||||
**Pattern References**:
|
||||
- `/home/m3tam3re/p/NIX/nixpkgs/lib/ports.nix` — MUST follow this exact pattern: `{lib}: { mkPortHelpers = portsConfig: let ... in { ... }; }`
|
||||
- `/home/m3tam3re/p/NIX/nixpkgs/lib/default.nix` — shows how lib modules are imported: `import ./ports.nix {inherit lib;}`
|
||||
- `/home/m3tam3re/p/NIX/nixpkgs/flake.nix:73-77` — shows how lib is exposed: `lib = forAllSystems (system: ... import ./lib {lib = pkgs.lib;});`
|
||||
|
||||
**External References**:
|
||||
- OpenCode rules docs: `https://opencode.ai/docs/rules/` — `instructions` field accepts relative paths
|
||||
|
||||
**WHY Each Reference Matters**:
|
||||
- `ports.nix` is the canonical pattern for lib functions in this repo — `{lib}:` signature, doc comments, nested `let ... in`
|
||||
- `default.nix` shows how the new module gets wired in
|
||||
- `flake.nix` confirms how consumers access it: `m3ta-nixpkgs.lib.${system}.opencode-rules.mkOpencodeRules`
|
||||
|
||||
**Acceptance Criteria**:
|
||||
|
||||
**QA Scenarios (MANDATORY):**
|
||||
|
||||
```
|
||||
Scenario: opencode-rules.nix evaluates without errors
|
||||
Tool: Bash
|
||||
Preconditions: File created
|
||||
Steps:
|
||||
1. Run `nix eval --impure --expr 'let pkgs = import <nixpkgs> {}; lib = (import /home/m3tam3re/p/NIX/nixpkgs/lib/opencode-rules.nix {lib = pkgs.lib;}); in builtins.attrNames lib' 2>&1`
|
||||
Expected Result: Output contains "mkOpencodeRules"
|
||||
Failure Indicators: "error:" in output
|
||||
Evidence: .sisyphus/evidence/task-2-eval.txt
|
||||
|
||||
Scenario: mkOpencodeRules generates correct paths
|
||||
Tool: Bash
|
||||
Preconditions: File created
|
||||
Steps:
|
||||
1. Run `nix eval --impure --json --expr 'let pkgs = import <nixpkgs> {}; lib = (import /home/m3tam3re/p/NIX/nixpkgs/lib/opencode-rules.nix {lib = pkgs.lib;}); in (lib.mkOpencodeRules { agents = /home/m3tam3re/p/AI/AGENTS; languages = ["python" "typescript"]; frameworks = ["n8n"]; }).instructions'`
|
||||
Expected Result: JSON array with 9 paths (6 concerns + 2 languages + 1 framework), all starting with ".opencode-rules/"
|
||||
Failure Indicators: Wrong count, wrong prefix, error
|
||||
Evidence: .sisyphus/evidence/task-2-paths.txt
|
||||
|
||||
Scenario: Default (empty languages) works
|
||||
Tool: Bash
|
||||
Preconditions: File created
|
||||
Steps:
|
||||
1. Run `nix eval --impure --json --expr 'let pkgs = import <nixpkgs> {}; lib = (import /home/m3tam3re/p/NIX/nixpkgs/lib/opencode-rules.nix {lib = pkgs.lib;}); in (lib.mkOpencodeRules { agents = /home/m3tam3re/p/AI/AGENTS; }).instructions'`
|
||||
Expected Result: JSON array with 6 paths (concerns only)
|
||||
Failure Indicators: Extra paths, error
|
||||
Evidence: .sisyphus/evidence/task-2-defaults.txt
|
||||
|
||||
Scenario: shellHook generates valid JSON
|
||||
Tool: Bash
|
||||
Preconditions: File created
|
||||
Steps:
|
||||
1. Run `nix eval --impure --raw --expr 'let pkgs = import <nixpkgs> {}; lib = (import /home/m3tam3re/p/NIX/nixpkgs/lib/opencode-rules.nix {lib = pkgs.lib;}); in (lib.mkOpencodeRules { agents = /home/m3tam3re/p/AI/AGENTS; languages = ["python"]; }).shellHook' | sh -c 'eval "$(cat)"' && python3 -m json.tool opencode.json`
|
||||
Expected Result: Valid JSON output with "$schema" and "instructions" fields
|
||||
Failure Indicators: JSON parse error, missing fields
|
||||
Evidence: .sisyphus/evidence/task-2-json.txt
|
||||
```
|
||||
|
||||
**Commit**: YES
|
||||
- Message: `feat(lib): add opencode-rules helper for per-project rule injection`
|
||||
- Files: `lib/opencode-rules.nix`
|
||||
- Pre-commit: `nix eval --impure --expr '...'`
|
||||
|
||||
---
|
||||
|
||||
- [x] 3. Update `lib/default.nix` in nixpkgs repo
|
||||
|
||||
**What to do**:
|
||||
- Add one line to `/home/m3tam3re/p/NIX/nixpkgs/lib/default.nix` to import opencode-rules:
|
||||
`opencode-rules = import ./opencode-rules.nix {inherit lib;};`
|
||||
- Place it after the existing `ports = import ./ports.nix {inherit lib;};` line
|
||||
- Update the comment at line 10 to remove it (it's a placeholder)
|
||||
|
||||
**Must NOT do**:
|
||||
- Do not modify the ports import
|
||||
- Do not change the function signature `{lib}:`
|
||||
|
||||
**Recommended Agent Profile**:
|
||||
- **Category**: `quick`
|
||||
- **Skills**: []
|
||||
|
||||
**Parallelization**:
|
||||
- **Can Run In Parallel**: YES (but logically pairs with Task 2)
|
||||
- **Parallel Group**: Wave 1
|
||||
- **Blocks**: Task 17
|
||||
- **Blocked By**: Task 2 (opencode-rules.nix must exist first)
|
||||
|
||||
**References**:
|
||||
- `/home/m3tam3re/p/NIX/nixpkgs/lib/default.nix:6-12` — current file content, add after line 8
|
||||
|
||||
**Acceptance Criteria**:
|
||||
|
||||
**QA Scenarios (MANDATORY):**
|
||||
|
||||
```
|
||||
Scenario: default.nix imports opencode-rules
|
||||
Tool: Bash
|
||||
Preconditions: Both files updated
|
||||
Steps:
|
||||
1. Run `grep 'opencode-rules' /home/m3tam3re/p/NIX/nixpkgs/lib/default.nix`
|
||||
Expected Result: Line shows `opencode-rules = import ./opencode-rules.nix {inherit lib;};`
|
||||
Failure Indicators: No match
|
||||
Evidence: .sisyphus/evidence/task-3-import.txt
|
||||
|
||||
Scenario: Full lib evaluates
|
||||
Tool: Bash
|
||||
Preconditions: Both files updated
|
||||
Steps:
|
||||
1. Run `nix eval --impure --expr 'let pkgs = import <nixpkgs> {}; m3taLib = import /home/m3tam3re/p/NIX/nixpkgs/lib {lib = pkgs.lib;}; in builtins.attrNames m3taLib' 2>&1`
|
||||
Expected Result: Output includes both "ports" and "opencode-rules"
|
||||
Failure Indicators: Missing "opencode-rules" or error
|
||||
Evidence: .sisyphus/evidence/task-3-full-lib.txt
|
||||
```
|
||||
|
||||
**Commit**: YES (groups with Task 2)
|
||||
- Message: `feat(lib): add opencode-rules helper for per-project rule injection`
|
||||
- Files: `lib/default.nix`, `lib/opencode-rules.nix`
|
||||
|
||||
---
|
||||
|
||||
- [x] 4. Update opencode.nix in nixos-config repo
|
||||
|
||||
**What to do**:
|
||||
- Add `rules/` deployment to `xdg.configFile` in `/home/m3tam3re/p/NIX/nixos-config/home/features/coding/opencode.nix`
|
||||
- Add entry: `"opencode/rules" = { source = "${inputs.agents}/rules"; recursive = true; };`
|
||||
- Place it alongside existing entries for commands, context, prompts, skills (lines 2-18)
|
||||
|
||||
**Must NOT do**:
|
||||
- Do not modify any existing entries
|
||||
- Do not change agents, MCP, providers, or oh-my-opencode config
|
||||
- Do not run `home-manager switch`
|
||||
|
||||
**Recommended Agent Profile**:
|
||||
- **Category**: `quick`
|
||||
- **Skills**: []
|
||||
|
||||
**Parallelization**:
|
||||
- **Can Run In Parallel**: YES
|
||||
- **Parallel Group**: Wave 1
|
||||
- **Blocks**: Task 17
|
||||
- **Blocked By**: None
|
||||
|
||||
**References**:
|
||||
- `/home/m3tam3re/p/NIX/nixos-config/home/features/coding/opencode.nix:2-18` — existing xdg.configFile entries
|
||||
|
||||
**Acceptance Criteria**:
|
||||
|
||||
**QA Scenarios (MANDATORY):**
|
||||
|
||||
```
|
||||
Scenario: opencode.nix contains rules entry
|
||||
Tool: Bash
|
||||
Preconditions: File updated
|
||||
Steps:
|
||||
1. Run `grep -c 'opencode/rules' /home/m3tam3re/p/NIX/nixos-config/home/features/coding/opencode.nix`
|
||||
2. Run `grep -c 'opencode/commands\|opencode/context\|opencode/prompts\|opencode/skills' /home/m3tam3re/p/NIX/nixos-config/home/features/coding/opencode.nix`
|
||||
Expected Result: Rules count is 1, existing count is 4 (all preserved)
|
||||
Failure Indicators: Count mismatch
|
||||
Evidence: .sisyphus/evidence/task-4-opencode-nix.txt
|
||||
```
|
||||
|
||||
**Commit**: YES
|
||||
- Message: `feat(opencode): deploy rules/ to ~/.config/opencode/rules/ via home-manager`
|
||||
- Files: `opencode.nix`
|
||||
|
||||
---
|
||||
|
||||
- [x] 5. Create `rules/USAGE.md` in AGENTS repo
|
||||
|
||||
**What to do**:
|
||||
- Document how to use `mkOpencodeRules` in a project's `flake.nix`
|
||||
- Show the nixpkgs consumption pattern: `m3taLib.opencode-rules.mkOpencodeRules { agents = inputs.agents; languages = ["python"]; }`
|
||||
- Complete example `flake.nix` devShell snippet showing: `inputs.agents` + `inputs.m3ta-nixpkgs` + `mkOpencodeRules` + `shellHook`
|
||||
- Document `.gitignore` additions: `.opencode-rules` and `opencode.json`
|
||||
- Explain project-level `AGENTS.md` overrides
|
||||
- Explain update flow: `nix flake update agents`
|
||||
- Keep concise: max 100 lines
|
||||
|
||||
**Must NOT do**:
|
||||
- Do not create a README.md (repo anti-pattern)
|
||||
- Do not reference `rules/default.nix` — the helper lives in nixpkgs, not AGENTS
|
||||
|
||||
**Recommended Agent Profile**:
|
||||
- **Category**: `quick`
|
||||
- **Skills**: []
|
||||
|
||||
**Parallelization**:
|
||||
- **Can Run In Parallel**: YES
|
||||
- **Parallel Group**: Wave 1
|
||||
- **Blocks**: Task 17
|
||||
- **Blocked By**: Tasks 1, 2 (needs to reference both structures)
|
||||
|
||||
**References**:
|
||||
- `/home/m3tam3re/p/AI/AGENTS/AGENTS.md` — repo documentation style (concise, code-heavy)
|
||||
- `/home/m3tam3re/p/NIX/nixpkgs/lib/ports.nix:1-42` — the doc comment style used for lib functions
|
||||
- OpenCode rules docs: `https://opencode.ai/docs/rules/` — `instructions` field
|
||||
|
||||
**Acceptance Criteria**:
|
||||
|
||||
**QA Scenarios (MANDATORY):**
|
||||
|
||||
```
|
||||
Scenario: USAGE.md has required content
|
||||
Tool: Bash
|
||||
Preconditions: File created
|
||||
Steps:
|
||||
1. Run `wc -l /home/m3tam3re/p/AI/AGENTS/rules/USAGE.md`
|
||||
2. Run `grep -c 'm3ta-nixpkgs\|mkOpencodeRules\|gitignore\|AGENTS.md\|nix flake update' /home/m3tam3re/p/AI/AGENTS/rules/USAGE.md`
|
||||
Expected Result: Under 100 lines, key terms >= 5
|
||||
Failure Indicators: Over 100 lines or missing key concepts
|
||||
Evidence: .sisyphus/evidence/task-5-usage.txt
|
||||
```
|
||||
|
||||
**Commit**: YES (groups with T1)
|
||||
- Message: `feat(rules): add rules directory structure and usage documentation`
|
||||
- Files: `rules/USAGE.md`, `rules/concerns/.gitkeep`, `rules/languages/.gitkeep`, `rules/frameworks/.gitkeep`
|
||||
|
||||
---
|
||||
|
||||
- [x] 6. Create `rules/concerns/coding-style.md`
|
||||
|
||||
**What to do**:
|
||||
- Write coding style rules: code formatting, patterns/anti-patterns, error handling, type safety, function design, DRY/SOLID
|
||||
- Imperative language ("Always...", "Never...", "Prefer..."), micro-examples (`Correct:` / `Incorrect:`)
|
||||
- Keep under 200 lines, sandwich principle (critical rules at start and end)
|
||||
|
||||
**Must NOT do**: No language-specific toolchain details, no generic advice ("write clean code"), max 200 lines
|
||||
|
||||
**Recommended Agent Profile**: `writing`, Skills: []
|
||||
**Parallelization**: Wave 2, parallel with T7-T16. Blocks T17. Blocked by T1.
|
||||
|
||||
**References**:
|
||||
- `/home/m3tam3re/p/AI/AGENTS/skills/skill-creator/SKILL.md` — documentation density example
|
||||
- Awesome Cursorrules: `https://github.com/PatrickJS/awesome-cursorrules`
|
||||
|
||||
**Acceptance Criteria**:
|
||||
```
|
||||
Scenario: Quality check
|
||||
Tool: Bash
|
||||
Steps:
|
||||
1. `wc -l` → under 200
|
||||
2. `grep -c 'Correct:\|Incorrect:\|Always\|Never\|Prefer'` → >= 10
|
||||
3. `grep -c '```'` → >= 6 (3+ example pairs)
|
||||
4. `grep -ic 'write clean code\|follow best practices'` → 0
|
||||
Evidence: .sisyphus/evidence/task-6-coding-style.txt
|
||||
```
|
||||
**Commit**: NO (groups with Wave 2 commit in T17)
|
||||
|
||||
---
|
||||
|
||||
- [x] 7. Create `rules/concerns/naming.md`
|
||||
|
||||
**What to do**:
|
||||
- Naming conventions: files, variables, functions, classes, modules, constants
|
||||
- Per-language table (Python=snake_case, TS=camelCase, Nix=camelCase, Shell=UPPER_SNAKE)
|
||||
- Keep under 150 lines
|
||||
|
||||
**Must NOT do**: No toolchain details, max 150 lines
|
||||
|
||||
**Recommended Agent Profile**: `writing`, Skills: []
|
||||
**Parallelization**: Wave 2. Blocks T17. Blocked by T1.
|
||||
|
||||
**References**: `/home/m3tam3re/p/AI/AGENTS/AGENTS.md:58-62` — existing naming conventions
|
||||
|
||||
**Acceptance Criteria**:
|
||||
```
|
||||
Scenario: `wc -l` → under 150, `grep -c 'snake_case\|camelCase\|PascalCase\|UPPER_SNAKE'` → >= 4
|
||||
Evidence: .sisyphus/evidence/task-7-naming.txt
|
||||
```
|
||||
**Commit**: NO
|
||||
|
||||
---
|
||||
|
||||
- [x] 8. Create `rules/concerns/documentation.md`
|
||||
|
||||
**What to do**: When to document, docstring formats, inline comment philosophy (WHY not WHAT), README standards. Under 150 lines.
|
||||
**Recommended Agent Profile**: `writing`
|
||||
**Parallelization**: Wave 2. Blocked by T1.
|
||||
**References**: `/home/m3tam3re/p/AI/AGENTS/AGENTS.md` — repo's own style
|
||||
**Acceptance Criteria**: `wc -l` < 150, `grep -c 'WHY\|WHAT\|Correct:\|Incorrect:'` >= 4
|
||||
**Commit**: NO
|
||||
|
||||
---
|
||||
|
||||
- [x] 9. Create `rules/concerns/testing.md`
|
||||
|
||||
**What to do**: Arrange-act-assert, behavior vs implementation testing, mocking philosophy, coverage, TDD. Under 200 lines.
|
||||
**Recommended Agent Profile**: `writing`
|
||||
**Parallelization**: Wave 2. Blocked by T1.
|
||||
**References**: `/home/m3tam3re/p/AI/AGENTS/AGENTS.md:73-82` — existing test philosophy
|
||||
**Acceptance Criteria**: `wc -l` < 200, `grep -ic 'arrange\|act\|assert\|mock\|behavior'` >= 4
|
||||
**Commit**: NO
|
||||
|
||||
---
|
||||
|
||||
- [x] 10. Create `rules/concerns/git-workflow.md`
|
||||
|
||||
**What to do**: Conventional commits, branch naming, PR descriptions, squash vs merge. Under 120 lines.
|
||||
**Recommended Agent Profile**: `writing`, Skills: [`git-master`]
|
||||
**Parallelization**: Wave 2. Blocked by T1.
|
||||
**References**: `https://www.conventionalcommits.org/en/v1.0.0/`
|
||||
**Acceptance Criteria**: `wc -l` < 120, `grep -c 'feat\|fix\|refactor\|docs\|chore'` >= 5
|
||||
**Commit**: NO
|
||||
|
||||
---
|
||||
|
||||
- [x] 11. Create `rules/concerns/project-structure.md`
|
||||
|
||||
**What to do**: Directory layout, module organization, entry points, config placement. Per-type: Python (src layout), TS (src/), Nix (modules/). Under 120 lines.
|
||||
**Recommended Agent Profile**: `writing`
|
||||
**Parallelization**: Wave 2. Blocked by T1.
|
||||
**References**: `/home/m3tam3re/p/AI/AGENTS/AGENTS.md:24-38` — repo structure
|
||||
**Acceptance Criteria**: `wc -l` < 120
|
||||
**Commit**: NO
|
||||
|
||||
---
|
||||
|
||||
- [x] 12. Create `rules/languages/python.md`
|
||||
|
||||
**What to do**:
|
||||
- Deep Python patterns: `uv` (pkg mgmt), `ruff` (lint/fmt), `pyright` (types), `pytest` + `hypothesis`, Pydantic for data boundaries
|
||||
- Idioms: comprehensions, context managers, generators, f-strings
|
||||
- Anti-patterns: bare except, mutable defaults, global state, star imports
|
||||
- Project setup: `pyproject.toml`, src layout
|
||||
- Under 250 lines with micro-examples
|
||||
|
||||
**Must NOT do**: No general coding style (covered in concerns/), no Django/Flask/FastAPI, max 250 lines
|
||||
|
||||
**Recommended Agent Profile**: `writing`
|
||||
**Parallelization**: Wave 2. Blocked by T1.
|
||||
|
||||
**References**:
|
||||
- `/home/m3tam3re/p/AI/AGENTS/AGENTS.md:60` — existing Python conventions (shebang, docstrings)
|
||||
- Ruff docs: `https://docs.astral.sh/ruff/`, uv docs: `https://docs.astral.sh/uv/`
|
||||
|
||||
**Acceptance Criteria**: `wc -l` < 250, `grep -c 'ruff\|uv\|pytest\|pydantic\|pyright'` >= 4, `grep -c '```python'` >= 5, no "pythonic"/"best practice"
|
||||
**Commit**: NO
|
||||
|
||||
---
|
||||
|
||||
- [x] 13. Create `rules/languages/typescript.md`
|
||||
|
||||
**What to do**:
|
||||
- Strict mode (`strict: true`, `noUncheckedIndexedAccess`), discriminated unions, branded types, `satisfies`, `as const`
|
||||
- Modern: `using`, `Promise.withResolvers()`, `Object.groupBy()`
|
||||
- Toolchain: `bun`/`tsx`, `biome`/`eslint`
|
||||
- Anti-patterns: `as any`, `@ts-ignore`, `!` assertion, `enum` (prefer union)
|
||||
- Under 250 lines
|
||||
|
||||
**Must NOT do**: No React/Next.js, max 250 lines
|
||||
|
||||
**Recommended Agent Profile**: `writing`
|
||||
**Parallelization**: Wave 2. Blocked by T1.
|
||||
**Acceptance Criteria**: `wc -l` < 250, `grep -c 'strict\|as any\|ts-ignore\|discriminated\|satisfies'` >= 4, `grep -c '```ts'` >= 5
|
||||
**Commit**: NO
|
||||
|
||||
---
|
||||
|
||||
- [x] 14. Create `rules/languages/nix.md`
|
||||
|
||||
**What to do**:
|
||||
- Flake structure, module patterns (`{ config, lib, pkgs, ... }:`), `mkIf`/`mkMerge`
|
||||
- Formatting: `alejandra`, naming: camelCase
|
||||
- Anti-patterns: `with pkgs;`, `builtins.fetchTarball`, impure ops
|
||||
- Home Manager patterns, overlays
|
||||
- Under 200 lines
|
||||
|
||||
**Recommended Agent Profile**: `writing`
|
||||
**Parallelization**: Wave 2. Blocked by T1.
|
||||
|
||||
**References**:
|
||||
- `/home/m3tam3re/p/NIX/nixos-config/home/features/coding/opencode.nix` — user's actual Nix style
|
||||
- `/home/m3tam3re/p/NIX/nixpkgs/lib/ports.nix` — well-structured Nix code example
|
||||
|
||||
**Acceptance Criteria**: `wc -l` < 200, `grep -c 'flake\|mkShell\|alejandra\|with pkgs\|overlay'` >= 4
|
||||
**Commit**: NO
|
||||
|
||||
---
|
||||
|
||||
- [x] 15. Create `rules/languages/shell.md`
|
||||
|
||||
**What to do**: `set -euo pipefail`, shellcheck, quoting, local vars, POSIX portability, `#!/usr/bin/env bash`. Under 120 lines.
|
||||
**Recommended Agent Profile**: `writing`
|
||||
**Parallelization**: Wave 2. Blocked by T1.
|
||||
**References**: `/home/m3tam3re/p/AI/AGENTS/AGENTS.md:61`, `/home/m3tam3re/p/AI/AGENTS/scripts/test-skill.sh`
|
||||
**Acceptance Criteria**: `wc -l` < 120, `grep -c 'set -euo pipefail\|shellcheck\|#!/usr/bin/env'` >= 2
|
||||
**Commit**: NO
|
||||
|
||||
---
|
||||
|
||||
- [x] 16. Create `rules/frameworks/n8n.md`
|
||||
|
||||
**What to do**: Workflow design, node patterns, naming, Error Trigger, data patterns, security. Under 120 lines.
|
||||
**Recommended Agent Profile**: `writing`
|
||||
**Parallelization**: Wave 2. Blocked by T1.
|
||||
**References**: n8n docs: `https://docs.n8n.io/`
|
||||
**Acceptance Criteria**: `wc -l` < 120, `grep -c 'workflow\|node\|Error Trigger\|webhook\|credential'` >= 4
|
||||
**Commit**: NO
|
||||
|
||||
---
|
||||
|
||||
- [x] 17. End-to-end integration test + commits
|
||||
|
||||
**What to do**:
|
||||
1. Verify all 11 rule files exist and meet line count limits
|
||||
2. Verify `lib/opencode-rules.nix` in nixpkgs evaluates correctly for: empty, single-lang, multi-lang, with-frameworks
|
||||
3. Verify full lib import works: `m3taLib.opencode-rules.mkOpencodeRules`
|
||||
4. Verify generated `opencode.json` is valid JSON with correct `instructions` paths
|
||||
5. Verify all instruction paths resolve to real files in AGENTS repo rules/
|
||||
6. Verify total context budget: all concerns + 1 language < 1500 lines
|
||||
7. Verify `opencode.nix` has the rules deployment entry
|
||||
8. Commit all Wave 2 rule files as a single commit in AGENTS repo
|
||||
|
||||
**Must NOT do**: Do not run `home-manager switch`, do not modify files, do not create test projects
|
||||
|
||||
**Recommended Agent Profile**: `deep`, Skills: [`git-master`]
|
||||
**Parallelization**: Wave 3 (sequential). Blocks F1-F4. Blocked by T2-T5, T6-T16.
|
||||
|
||||
**References**:
|
||||
- `/home/m3tam3re/p/NIX/nixpkgs/lib/opencode-rules.nix` — Nix helper to evaluate
|
||||
- `/home/m3tam3re/p/NIX/nixos-config/home/features/coding/opencode.nix` — deployment config
|
||||
|
||||
**Acceptance Criteria**:
|
||||
|
||||
**QA Scenarios (MANDATORY):**
|
||||
|
||||
```
|
||||
Scenario: All rule files exist and meet limits
|
||||
Tool: Bash
|
||||
Steps:
|
||||
1. For each of 11 files: `wc -l` and verify under limit
|
||||
Expected Result: All 11 files present, all under limits
|
||||
Evidence: .sisyphus/evidence/task-17-inventory.txt
|
||||
|
||||
Scenario: Full lib integration
|
||||
Tool: Bash
|
||||
Steps:
|
||||
1. Run `nix eval --impure --json --expr 'let pkgs = import <nixpkgs> {}; m3taLib = import /home/m3tam3re/p/NIX/nixpkgs/lib {lib = pkgs.lib;}; in (m3taLib.opencode-rules.mkOpencodeRules { agents = /home/m3tam3re/p/AI/AGENTS; languages = ["python" "typescript" "nix" "shell"]; frameworks = ["n8n"]; }).instructions'`
|
||||
Expected Result: JSON array with 11 paths (6 concerns + 4 langs + 1 framework)
|
||||
Failure Indicators: Wrong count, error
|
||||
Evidence: .sisyphus/evidence/task-17-full-integration.txt
|
||||
|
||||
Scenario: All paths resolve to real files
|
||||
Tool: Bash
|
||||
Steps:
|
||||
1. For each path in instructions output: verify the corresponding file exists under `rules/`
|
||||
Expected Result: All paths resolve, none missing
|
||||
Evidence: .sisyphus/evidence/task-17-paths-resolve.txt
|
||||
|
||||
Scenario: Total context budget
|
||||
Tool: Bash
|
||||
Steps:
|
||||
1. `cat /home/m3tam3re/p/AI/AGENTS/rules/concerns/*.md | wc -l`
|
||||
2. `wc -l < /home/m3tam3re/p/AI/AGENTS/rules/languages/python.md`
|
||||
3. Sum must be < 1500
|
||||
Expected Result: Total under 1500
|
||||
Evidence: .sisyphus/evidence/task-17-budget.txt
|
||||
```
|
||||
|
||||
**Commit**: YES
|
||||
- Message: `feat(rules): add initial rule files for all concerns, languages, and frameworks`
|
||||
- Files: all `rules/**/*.md` files (11 total)
|
||||
- Repo: AGENTS
|
||||
|
||||
---
|
||||
|
||||
## Final Verification Wave (MANDATORY — after ALL implementation tasks)
|
||||
|
||||
> 4 review agents run in PARALLEL. ALL must APPROVE. Rejection → fix → re-run.
|
||||
|
||||
- [x] F1. **Plan Compliance Audit** — `oracle`
|
||||
For each "Must Have": verify implementation exists. For each "Must NOT Have": search for violations. Check evidence files. Compare deliverables across all 3 repos.
|
||||
Output: `Must Have [N/N] | Must NOT Have [N/N] | Tasks [N/N] | VERDICT`
|
||||
|
||||
- [x] F2. **Code Quality Review** — `unspecified-high`
|
||||
Rule files: no generic advice, has examples, consistent tone, under limits. Nix: valid syntax, correct paths, edge cases. USAGE.md: accurate.
|
||||
Output: `Files [N clean/N issues] | VERDICT`
|
||||
|
||||
- [x] F3. **Real Manual QA** — `unspecified-high`
|
||||
Run `nix eval` on opencode-rules.nix via full lib import with various configs. Verify JSON. Check rule content quality. Save to `.sisyphus/evidence/final-qa/`.
|
||||
Output: `Scenarios [N/N pass] | VERDICT`
|
||||
|
||||
- [x] F4. **Scope Fidelity Check** — `deep`
|
||||
For each task: "What to do" vs actual file. 1:1 match. No creep. Check "Must NOT do". Flag unaccounted changes across all 3 repos.
|
||||
Output: `Tasks [N/N compliant] | Unaccounted [CLEAN/N files] | VERDICT`
|
||||
|
||||
---
|
||||
|
||||
## Commit Strategy
|
||||
|
||||
| After Task(s) | Repo | Message | Files |
|
||||
|---------------|------|---------|-------|
|
||||
| 1, 5 | AGENTS | `feat(rules): add rules directory structure and usage documentation` | `rules/USAGE.md`, `rules/{concerns,languages,frameworks}/.gitkeep` |
|
||||
| 2, 3 | nixpkgs | `feat(lib): add opencode-rules helper for per-project rule injection` | `lib/opencode-rules.nix`, `lib/default.nix` |
|
||||
| 4 | nixos-config | `feat(opencode): deploy rules/ to ~/.config/opencode/rules/` | `opencode.nix` |
|
||||
| 17 | AGENTS | `feat(rules): add initial rule files for concerns, languages, and frameworks` | all `rules/**/*.md` (11 files) |
|
||||
|
||||
---
|
||||
|
||||
## Success Criteria
|
||||
|
||||
### Verification Commands
|
||||
```bash
|
||||
# All rule files exist (AGENTS repo)
|
||||
ls rules/concerns/*.md rules/languages/*.md rules/frameworks/*.md
|
||||
|
||||
# Context budget
|
||||
cat rules/concerns/*.md rules/languages/python.md | wc -l # Expected: < 1500
|
||||
|
||||
# Nix helper via full lib (nixpkgs)
|
||||
nix eval --impure --json --expr 'let pkgs = import <nixpkgs> {}; m3taLib = import /path/to/nixpkgs/lib {lib = pkgs.lib;}; in (m3taLib.opencode-rules.mkOpencodeRules { agents = /path/to/AGENTS; languages = ["python"]; }).instructions'
|
||||
|
||||
# opencode.nix has rules entry (nixos-config)
|
||||
grep 'opencode/rules' /home/m3tam3re/p/NIX/nixos-config/home/features/coding/opencode.nix
|
||||
```
|
||||
|
||||
### Final Checklist
|
||||
- [ ] All 11 rule files present and under line limits
|
||||
- [ ] All rule files use imperative language with micro-examples
|
||||
- [ ] `lib/opencode-rules.nix` in nixpkgs follows ports.nix pattern exactly
|
||||
- [ ] `lib/default.nix` imports opencode-rules
|
||||
- [ ] `opencode.nix` deploys rules/ alongside skills/commands/context/prompts
|
||||
- [ ] `rules/USAGE.md` documents nixpkgs consumption pattern correctly
|
||||
- [ ] No Nix code in AGENTS repo
|
||||
- [ ] No existing files modified (except lib/default.nix +1 line, opencode.nix +3 lines)
|
||||
- [ ] Total loaded context under 1500 lines for any realistic configuration
|
||||
62
rules/USAGE.md
Normal file
62
rules/USAGE.md
Normal file
@@ -0,0 +1,62 @@
|
||||
# Opencode Rules Usage
|
||||
|
||||
Add AI coding rules to your project via `mkOpencodeRules`.
|
||||
|
||||
## flake.nix Setup
|
||||
|
||||
```nix
|
||||
{
|
||||
inputs = {
|
||||
nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
|
||||
m3ta-nixpkgs.url = "git+https://code.m3ta.dev/m3tam3re/nixpkgs";
|
||||
agents = {
|
||||
url = "git+https://code.m3ta.dev/m3tam3re/AGENTS";
|
||||
flake = false;
|
||||
};
|
||||
};
|
||||
|
||||
outputs = { self, nixpkgs, m3ta-nixpkgs, agents, ... }:
|
||||
let
|
||||
system = "x86_64-linux";
|
||||
pkgs = nixpkgs.legacyPackages.${system};
|
||||
m3taLib = m3ta-nixpkgs.lib.${system};
|
||||
in {
|
||||
devShells.${system}.default = let
|
||||
rules = m3taLib.opencode-rules.mkOpencodeRules {
|
||||
inherit agents;
|
||||
languages = [ "python" "typescript" ];
|
||||
frameworks = [ "n8n" ];
|
||||
};
|
||||
in pkgs.mkShell {
|
||||
shellHook = rules.shellHook;
|
||||
};
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
## Parameters
|
||||
|
||||
- `agents` (required): Path to AGENTS repo flake input
|
||||
- `languages` (optional): List of language names (e.g., `["python" "typescript"]`)
|
||||
- `concerns` (optional): Rule categories (default: all standard concerns)
|
||||
- `frameworks` (optional): List of framework names (e.g., `["n8n" "django"]`)
|
||||
- `extraInstructions` (optional): Additional instruction file paths
|
||||
|
||||
## .gitignore
|
||||
|
||||
Add to your project's `.gitignore`:
|
||||
```
|
||||
.opencode-rules
|
||||
opencode.json
|
||||
```
|
||||
|
||||
## Project Overrides
|
||||
|
||||
Create `AGENTS.md` in your project root to override central rules. OpenCode applies project-level rules with precedence over central ones.
|
||||
|
||||
## Updating Rules
|
||||
|
||||
When central rules are updated:
|
||||
```bash
|
||||
nix flake update agents
|
||||
```
|
||||
0
rules/concerns/.gitkeep
Normal file
0
rules/concerns/.gitkeep
Normal file
163
rules/concerns/coding-style.md
Normal file
163
rules/concerns/coding-style.md
Normal file
@@ -0,0 +1,163 @@
|
||||
# Coding Style
|
||||
|
||||
## Critical Rules (MUST follow)
|
||||
|
||||
Always prioritize readability over cleverness. Never write code that requires mental gymnastics to understand.
|
||||
Always fail fast and explicitly. Never silently swallow errors or hide exceptions.
|
||||
Always keep functions under 20 lines. Never create monolithic functions that do multiple things.
|
||||
Always validate inputs at function boundaries. Never trust external data implicitly.
|
||||
|
||||
## Formatting
|
||||
|
||||
Prefer consistent indentation throughout the codebase. Never mix tabs and spaces.
|
||||
Prefer meaningful variable names over short abbreviations. Never use single letters except for loop counters.
|
||||
|
||||
### Correct:
|
||||
```lang
|
||||
const maxRetryAttempts = 3;
|
||||
const connectionTimeout = 5000;
|
||||
|
||||
for (let attempt = 1; attempt <= maxRetryAttempts; attempt++) {
|
||||
// process attempt
|
||||
}
|
||||
```
|
||||
|
||||
### Incorrect:
|
||||
```lang
|
||||
const m = 3;
|
||||
const t = 5000;
|
||||
|
||||
for (let i = 1; i <= m; i++) {
|
||||
// process attempt
|
||||
}
|
||||
```
|
||||
|
||||
## Patterns and Anti-Patterns
|
||||
|
||||
Never repeat yourself. Always extract duplicated logic into reusable functions.
|
||||
Prefer composition over inheritance. Never create deep inheritance hierarchies.
|
||||
Always use guard clauses to reduce nesting. Never write arrow-shaped code.
|
||||
|
||||
### Correct:
|
||||
```lang
|
||||
def process_user(user):
|
||||
if not user:
|
||||
return None
|
||||
if not user.is_active:
|
||||
return None
|
||||
return user.calculate_score()
|
||||
```
|
||||
|
||||
### Incorrect:
|
||||
```lang
|
||||
def process_user(user):
|
||||
if user:
|
||||
if user.is_active:
|
||||
return user.calculate_score()
|
||||
else:
|
||||
return None
|
||||
else:
|
||||
return None
|
||||
```
|
||||
|
||||
## Error Handling
|
||||
|
||||
Always handle specific exceptions. Never use broad catch-all exception handlers.
|
||||
Always log error context, not just the error message. Never let errors vanish without trace.
|
||||
|
||||
### Correct:
|
||||
```lang
|
||||
try:
|
||||
data = fetch_resource(url)
|
||||
return parse_data(data)
|
||||
except NetworkError as e:
|
||||
log_error(f"Network failed for {url}: {e}")
|
||||
raise
|
||||
except ParseError as e:
|
||||
log_error(f"Parse failed for {url}: {e}")
|
||||
return fallback_data
|
||||
```
|
||||
|
||||
### Incorrect:
|
||||
```lang
|
||||
try:
|
||||
data = fetch_resource(url)
|
||||
return parse_data(data)
|
||||
except Exception:
|
||||
pass
|
||||
```
|
||||
|
||||
## Type Safety
|
||||
|
||||
Always use type annotations where supported. Never rely on implicit type coercion.
|
||||
Prefer explicit type checks over duck typing for public APIs. Never assume type behavior.
|
||||
|
||||
### Correct:
|
||||
```lang
|
||||
function calculateTotal(price: number, quantity: number): number {
|
||||
return price * quantity;
|
||||
}
|
||||
```
|
||||
|
||||
### Incorrect:
|
||||
```lang
|
||||
function calculateTotal(price, quantity) {
|
||||
return price * quantity;
|
||||
}
|
||||
```
|
||||
|
||||
## Function Design
|
||||
|
||||
Always write pure functions when possible. Never mutate arguments unless required.
|
||||
Always limit function parameters to 3 or fewer. Never pass objects to hide parameter complexity.
|
||||
|
||||
### Correct:
|
||||
```lang
|
||||
def create_user(name: str, email: str) -> User:
|
||||
return User(name=name, email=email, created_at=now())
|
||||
```
|
||||
|
||||
### Incorrect:
|
||||
```lang
|
||||
def create_user(config: dict) -> User:
|
||||
return User(
|
||||
name=config['name'],
|
||||
email=config['email'],
|
||||
created_at=config['timestamp']
|
||||
)
|
||||
```
|
||||
|
||||
## SOLID Principles
|
||||
|
||||
Never let classes depend on concrete implementations. Always depend on abstractions.
|
||||
Always ensure classes are open for extension but closed for modification. Never change working code to add features.
|
||||
Prefer many small interfaces over one large interface. Never force clients to depend on methods they don't use.
|
||||
|
||||
### Correct:
|
||||
```lang
|
||||
class EmailSender {
|
||||
send(message: Message): void {
|
||||
// implementation
|
||||
}
|
||||
}
|
||||
|
||||
class NotificationService {
|
||||
constructor(private sender: EmailSender) {}
|
||||
}
|
||||
```
|
||||
|
||||
### Incorrect:
|
||||
```lang
|
||||
class NotificationService {
|
||||
sendEmail(message: Message): void { }
|
||||
sendSMS(message: Message): void { }
|
||||
sendPush(message: Message): void { }
|
||||
}
|
||||
```
|
||||
|
||||
## Critical Rules (REPEAT)
|
||||
|
||||
Always write self-documenting code. Never rely on comments to explain complex logic.
|
||||
Always refactor when you see code smells. Never let technical debt accumulate.
|
||||
Always test edge cases explicitly. Never assume happy path only behavior.
|
||||
Never commit commented-out code. Always remove it or restore it.
|
||||
149
rules/concerns/documentation.md
Normal file
149
rules/concerns/documentation.md
Normal file
@@ -0,0 +1,149 @@
|
||||
# Documentation Rules
|
||||
|
||||
## When to Document
|
||||
|
||||
**Document public APIs**. Every public function, class, method, and module needs documentation. Users need to know how to use your code.
|
||||
**Document complex logic**. Algorithms, state machines, and non-obvious implementations need explanations. Future readers will thank you.
|
||||
**Document business rules**. Encode domain knowledge directly in comments. Don't make anyone reverse-engineer requirements from code.
|
||||
**Document trade-offs**. When you choose between alternatives, explain why. Help future maintainers understand the decision context.
|
||||
**Do NOT document obvious code**. Comments like `// get user` add noise. Delete them.
|
||||
|
||||
## Docstring Formats
|
||||
|
||||
### Python (Google Style)
|
||||
|
||||
```python
|
||||
def calculate_price(quantity: int, unit_price: float, discount: float = 0.0) -> float:
|
||||
"""Calculate total price after discount.
|
||||
Args:
|
||||
quantity: Number of items ordered.
|
||||
unit_price: Price per item in USD.
|
||||
discount: Decimal discount rate (0.0 to 1.0).
|
||||
Returns:
|
||||
Final price in USD.
|
||||
Raises:
|
||||
ValueError: If quantity is negative.
|
||||
"""
|
||||
```
|
||||
|
||||
### JavaScript/TypeScript (JSDoc)
|
||||
|
||||
```javascript
|
||||
/**
|
||||
* Validates user input against security rules.
|
||||
* @param {string} input - Raw user input from form.
|
||||
* @param {Object} rules - Validation constraints.
|
||||
* @param {number} rules.maxLength - Maximum allowed length.
|
||||
* @returns {boolean} True if input passes all rules.
|
||||
* @throws {ValidationError} If input violates security constraints.
|
||||
*/
|
||||
function validateInput(input, rules) {
|
||||
```
|
||||
|
||||
### Bash
|
||||
|
||||
```bash
|
||||
#!/usr/bin/env bash
|
||||
# Deploy application to production environment.
|
||||
#
|
||||
# Usage: ./deploy.sh [environment]
|
||||
#
|
||||
# Args:
|
||||
# environment: Target environment (staging|production). Default: staging.
|
||||
#
|
||||
# Exits:
|
||||
# 0 on success, 1 on deployment failure.
|
||||
```
|
||||
|
||||
## Inline Comments: WHY Not WHAT
|
||||
|
||||
**Incorrect:**
|
||||
```python
|
||||
# Iterate through all users
|
||||
for user in users:
|
||||
# Check if user is active
|
||||
if user.active:
|
||||
# Increment counter
|
||||
count += 1
|
||||
```
|
||||
|
||||
**Correct:**
|
||||
```python
|
||||
# Count only active users to calculate monthly revenue
|
||||
for user in users:
|
||||
if user.active:
|
||||
count += 1
|
||||
```
|
||||
|
||||
**Incorrect:**
|
||||
```javascript
|
||||
// Set timeout to 5000
|
||||
setTimeout(() => {
|
||||
// Show error message
|
||||
alert('Error');
|
||||
}, 5000);
|
||||
```
|
||||
|
||||
**Correct:**
|
||||
```javascript
|
||||
// 5000ms delay prevents duplicate alerts during rapid retries
|
||||
setTimeout(() => {
|
||||
alert('Error');
|
||||
}, 5000);
|
||||
```
|
||||
|
||||
**Incorrect:**
|
||||
```bash
|
||||
# Remove temporary files
|
||||
rm -rf /tmp/app/*
|
||||
```
|
||||
|
||||
**Correct:**
|
||||
```bash
|
||||
# Clear temp directory before batch import to prevent partial state
|
||||
rm -rf /tmp/app/*
|
||||
```
|
||||
|
||||
**Rule:** Describe the intent and context. Never describe what the code obviously does.
|
||||
|
||||
## README Standards
|
||||
|
||||
Every project needs a README at the top level.
|
||||
|
||||
**Required sections:**
|
||||
1. **What it does** - One sentence summary
|
||||
2. **Installation** - Setup commands
|
||||
3. **Usage** - Basic example
|
||||
4. **Configuration** - Environment variables and settings
|
||||
5. **Contributing** - How to contribute
|
||||
|
||||
**Example structure:**
|
||||
|
||||
```markdown
|
||||
# Project Name
|
||||
|
||||
One-line description of what this project does.
|
||||
|
||||
## Installation
|
||||
```bash
|
||||
npm install
|
||||
```
|
||||
|
||||
## Usage
|
||||
```bash
|
||||
npm start
|
||||
```
|
||||
|
||||
## Configuration
|
||||
|
||||
Create `.env` file:
|
||||
```
|
||||
API_KEY=your_key_here
|
||||
```
|
||||
|
||||
## Contributing
|
||||
|
||||
See [CONTRIBUTING.md](./CONTRIBUTING.md).
|
||||
```
|
||||
|
||||
**Keep READMEs focused**. Link to separate docs for complex topics. Don't make the README a tutorial.
|
||||
118
rules/concerns/git-workflow.md
Normal file
118
rules/concerns/git-workflow.md
Normal file
@@ -0,0 +1,118 @@
|
||||
# Git Workflow Rules
|
||||
|
||||
## Conventional Commits
|
||||
|
||||
Format: `<type>(<scope>): <subject>`
|
||||
|
||||
### Commit Types
|
||||
|
||||
- **feat**: New feature
|
||||
- `feat(auth): add OAuth2 login flow`
|
||||
- `feat(api): expose user endpoints`
|
||||
|
||||
- **fix**: Bug fix
|
||||
- `fix(payment): resolve timeout on Stripe calls`
|
||||
- `fix(ui): button not clickable on mobile`
|
||||
|
||||
- **refactor**: Code refactoring (no behavior change)
|
||||
- `refactor(utils): extract date helpers`
|
||||
- `refactor(api): simplify error handling`
|
||||
|
||||
- **docs**: Documentation only
|
||||
- `docs(readme): update installation steps`
|
||||
- `docs(api): add endpoint examples`
|
||||
|
||||
- **chore**: Maintenance tasks
|
||||
- `chore(deps): update Node to 20`
|
||||
- `chore(ci): add GitHub actions workflow`
|
||||
|
||||
- **test**: Tests only
|
||||
- `test(auth): add unit tests for login`
|
||||
- `test(e2e): add checkout flow tests`
|
||||
|
||||
- **style**: Formatting, no logic change
|
||||
- `style: sort imports alphabetically`
|
||||
|
||||
### Commit Rules
|
||||
|
||||
- Subject max 72 chars
|
||||
- Imperative mood ("add", not "added")
|
||||
- No period at end
|
||||
- Reference issues: `Closes #123`
|
||||
|
||||
## Branch Naming
|
||||
|
||||
Pattern: `<type>/<short-description>`
|
||||
|
||||
### Branch Types
|
||||
|
||||
- `feature/add-user-dashboard`
|
||||
- `feature/enable-dark-mode`
|
||||
- `fix/login-redirect-loop`
|
||||
- `fix/payment-timeout-error`
|
||||
- `refactor/extract-user-service`
|
||||
- `refactor/simplify-auth-flow`
|
||||
- `hotfix/security-vulnerability`
|
||||
|
||||
### Branch Rules
|
||||
|
||||
- Lowercase and hyphens
|
||||
- Max 50 chars
|
||||
- Delete after merge
|
||||
|
||||
## Pull Requests
|
||||
|
||||
### PR Title
|
||||
|
||||
Follow Conventional Commit format:
|
||||
- `feat: add user dashboard`
|
||||
- `fix: resolve login redirect loop`
|
||||
|
||||
### PR Description
|
||||
|
||||
```markdown
|
||||
## What
|
||||
Brief description
|
||||
|
||||
## Why
|
||||
Reason for change
|
||||
|
||||
## How
|
||||
Implementation approach
|
||||
|
||||
## Testing
|
||||
Steps performed
|
||||
|
||||
## Checklist
|
||||
- [ ] Tests pass
|
||||
- [ ] Code reviewed
|
||||
- [ ] Docs updated
|
||||
```
|
||||
|
||||
## Merge Strategy
|
||||
|
||||
### Squash Merge
|
||||
|
||||
- Many small commits
|
||||
- One cohesive feature
|
||||
- Clean history
|
||||
|
||||
### Merge Commit
|
||||
|
||||
- Preserve commit history
|
||||
- Distinct milestones
|
||||
- Detailed history preferred
|
||||
|
||||
### When to Rebase
|
||||
|
||||
- Before opening PR
|
||||
- Resolving conflicts
|
||||
- Keeping current with main
|
||||
|
||||
## General Rules
|
||||
|
||||
- Pull latest from main before starting
|
||||
- Write atomic commits
|
||||
- Run tests before pushing
|
||||
- Request peer review before merge
|
||||
- Never force push to main/master
|
||||
105
rules/concerns/naming.md
Normal file
105
rules/concerns/naming.md
Normal file
@@ -0,0 +1,105 @@
|
||||
# Naming Conventions
|
||||
|
||||
Use consistent naming across all code. Follow language-specific conventions.
|
||||
|
||||
## Language Reference
|
||||
|
||||
| Type | Python | TypeScript | Nix | Shell |
|
||||
|------|--------|------------|-----|-------|
|
||||
| Variables | snake_case | camelCase | camelCase | UPPER_SNAKE |
|
||||
| Functions | snake_case | camelCase | camelCase | lower_case |
|
||||
| Classes | PascalCase | PascalCase | - | - |
|
||||
| Constants | UPPER_SNAKE | UPPER_SNAKE | camelCase | UPPER_SNAKE |
|
||||
| Files | snake_case | camelCase | hyphen-case | hyphen-case |
|
||||
| Modules | snake_case | camelCase | - | - |
|
||||
|
||||
## General Rules
|
||||
|
||||
**Files**: Use hyphen-case for documentation, snake_case for Python, camelCase for TypeScript. Names should describe content.
|
||||
|
||||
**Variables**: Use descriptive names. Avoid single letters except loop counters. No Hungarian notation.
|
||||
|
||||
**Functions**: Use verb-noun pattern. Name describes what it does, not how it does it.
|
||||
|
||||
**Classes**: Use PascalCase with descriptive nouns. Avoid abbreviations.
|
||||
|
||||
**Constants**: Use UPPER_SNAKE with descriptive names. Group related constants.
|
||||
|
||||
## Examples
|
||||
|
||||
Python:
|
||||
```python
|
||||
# Variables
|
||||
user_name = "alice"
|
||||
is_authenticated = True
|
||||
|
||||
# Functions
|
||||
def get_user_data(user_id):
|
||||
pass
|
||||
|
||||
# Classes
|
||||
class UserProfile:
|
||||
pass
|
||||
|
||||
# Constants
|
||||
MAX_RETRIES = 3
|
||||
API_ENDPOINT = "https://api.example.com"
|
||||
```
|
||||
|
||||
TypeScript:
|
||||
```typescript
|
||||
// Variables
|
||||
const userName = "alice";
|
||||
const isAuthenticated = true;
|
||||
|
||||
// Functions
|
||||
function getUserData(userId: string): User {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Classes
|
||||
class UserProfile {
|
||||
private name: string;
|
||||
}
|
||||
|
||||
// Constants
|
||||
const MAX_RETRIES = 3;
|
||||
const API_ENDPOINT = "https://api.example.com";
|
||||
```
|
||||
|
||||
Nix:
|
||||
```nix
|
||||
# Variables
|
||||
let
|
||||
userName = "alice";
|
||||
isAuthenticated = true;
|
||||
in
|
||||
# ...
|
||||
```
|
||||
|
||||
Shell:
|
||||
```bash
|
||||
# Variables
|
||||
USER_NAME="alice"
|
||||
IS_AUTHENTICATED=true
|
||||
|
||||
# Functions
|
||||
get_user_data() {
|
||||
echo "Getting data"
|
||||
}
|
||||
|
||||
# Constants
|
||||
MAX_RETRIES=3
|
||||
API_ENDPOINT="https://api.example.com"
|
||||
```
|
||||
|
||||
## File Naming
|
||||
|
||||
Use these patterns consistently. No exceptions.
|
||||
|
||||
- Skills: `hyphen-case`
|
||||
- Python: `snake_case.py`
|
||||
- TypeScript: `camelCase.ts` or `hyphen-case.ts`
|
||||
- Nix: `hyphen-case.nix`
|
||||
- Shell: `hyphen-case.sh`
|
||||
- Markdown: `UPPERCASE.md` or `sentence-case.md`
|
||||
82
rules/concerns/project-structure.md
Normal file
82
rules/concerns/project-structure.md
Normal file
@@ -0,0 +1,82 @@
|
||||
# Project Structure
|
||||
|
||||
## Python
|
||||
|
||||
Use src layout for all projects. Place application code in `src/<project>/`, tests in `tests/`.
|
||||
|
||||
```
|
||||
project/
|
||||
├── src/myproject/
|
||||
│ ├── __init__.py
|
||||
│ ├── main.py # Entry point
|
||||
│ └── core/
|
||||
│ └── module.py
|
||||
├── tests/
|
||||
│ ├── __init__.py
|
||||
│ └── test_module.py
|
||||
├── pyproject.toml # Config
|
||||
├── README.md
|
||||
└── .gitignore
|
||||
```
|
||||
|
||||
**Rules:**
|
||||
- One module per directory file
|
||||
- `__init__.py` in every package
|
||||
- Entry point in `src/myproject/main.py`
|
||||
- Config in root: `pyproject.toml`, `requirements.txt`
|
||||
|
||||
## TypeScript
|
||||
|
||||
Use `src/` for source, `dist/` for build output.
|
||||
|
||||
```
|
||||
project/
|
||||
├── src/
|
||||
│ ├── index.ts # Entry point
|
||||
│ ├── core/
|
||||
│ │ └── module.ts
|
||||
│ └── types.ts
|
||||
├── tests/
|
||||
│ └── module.test.ts
|
||||
├── package.json # Config
|
||||
├── tsconfig.json
|
||||
└── README.md
|
||||
```
|
||||
|
||||
**Rules:**
|
||||
- One module per file
|
||||
- Index exports from `src/index.ts`
|
||||
- Entry point in `src/index.ts`
|
||||
- Config in root: `package.json`, `tsconfig.json`
|
||||
|
||||
## Nix
|
||||
|
||||
Use `modules/` for NixOS modules, `pkgs/` for packages.
|
||||
|
||||
```
|
||||
nix-config/
|
||||
├── modules/
|
||||
│ ├── default.nix # Module list
|
||||
│ └── my-service.nix
|
||||
├── pkgs/
|
||||
│ └── my-package/
|
||||
│ └── default.nix
|
||||
├── flake.nix # Entry point
|
||||
├── flake.lock
|
||||
└── README.md
|
||||
```
|
||||
|
||||
**Rules:**
|
||||
- One module per file in `modules/`
|
||||
- One package per directory in `pkgs/`
|
||||
- Entry point in `flake.nix`
|
||||
- Config in root: `flake.nix`, shell.nix
|
||||
|
||||
## General
|
||||
|
||||
- Use hyphen-case for directories
|
||||
- Use kebab-case for file names
|
||||
- Config files in project root
|
||||
- Tests separate from source
|
||||
- Docs in root: README.md, CHANGELOG.md
|
||||
- Hidden configs: .env, .gitignore
|
||||
134
rules/concerns/testing.md
Normal file
134
rules/concerns/testing.md
Normal file
@@ -0,0 +1,134 @@
|
||||
# Testing Rules
|
||||
|
||||
## Arrange-Act-Assert Pattern
|
||||
|
||||
Structure every test in three distinct phases:
|
||||
|
||||
```python
|
||||
# Arrange: Set up the test data and conditions
|
||||
user = User(name="Alice", role="admin")
|
||||
session = create_test_session(user.id)
|
||||
|
||||
# Act: Execute the behavior under test
|
||||
result = grant_permission(session, "read_documents")
|
||||
|
||||
# Assert: Verify the expected outcome
|
||||
assert result.granted is True
|
||||
assert result.permissions == ["read_documents"]
|
||||
```
|
||||
|
||||
Never mix phases. Comment each phase clearly for complex setups. Keep Act phase to one line if possible.
|
||||
|
||||
## Behavior vs Implementation Testing
|
||||
|
||||
Test behavior, not implementation details:
|
||||
|
||||
```python
|
||||
# GOOD: Tests the observable behavior
|
||||
def test_user_can_login():
|
||||
response = login("alice@example.com", "password123")
|
||||
assert response.status_code == 200
|
||||
assert "session_token" in response.cookies
|
||||
|
||||
# BAD: Tests internal implementation
|
||||
def test_login_sets_database_flag():
|
||||
login("alice@example.com", "password123")
|
||||
user = User.get(email="alice@example.com")
|
||||
assert user._logged_in_flag is True # Private field
|
||||
```
|
||||
|
||||
Focus on inputs and outputs. Test public contracts. Refactor internals freely without breaking tests.
|
||||
|
||||
## Mocking Philosophy
|
||||
|
||||
Mock external dependencies, not internal code:
|
||||
|
||||
```python
|
||||
# GOOD: Mock external services
|
||||
@patch("requests.post")
|
||||
def test_sends_notification_to_slack(mock_post):
|
||||
send_notification("Build complete!")
|
||||
mock_post.assert_called_once_with(
|
||||
"https://slack.com/api/chat.postMessage",
|
||||
json={"text": "Build complete!"}
|
||||
)
|
||||
|
||||
# BAD: Mock internal methods
|
||||
@patch("NotificationService._format_message")
|
||||
def test_notification_formatting(mock_format):
|
||||
# Don't mock private methods
|
||||
send_notification("Build complete!")
|
||||
```
|
||||
|
||||
Mock when:
|
||||
- Dependency is slow (database, network, file system)
|
||||
- Dependency is unreliable (external APIs)
|
||||
- Dependency is expensive (third-party services)
|
||||
|
||||
Don't mock when:
|
||||
- Testing the dependency itself
|
||||
- The dependency is fast and stable
|
||||
- The mock becomes more complex than real implementation
|
||||
|
||||
## Coverage Expectations
|
||||
|
||||
Write tests for:
|
||||
- Critical business logic (aim for 90%+)
|
||||
- Edge cases and error paths (aim for 80%+)
|
||||
- Public APIs and contracts (aim for 100%)
|
||||
|
||||
Don't obsess over:
|
||||
- Trivial getters/setters
|
||||
- Generated code
|
||||
- One-line wrappers
|
||||
|
||||
Coverage is a floor, not a ceiling. A test suite at 100% coverage that doesn't verify behavior is worthless.
|
||||
|
||||
## Test-Driven Development
|
||||
|
||||
Follow the red-green-refactor cycle:
|
||||
1. Red: Write failing test for new behavior
|
||||
2. Green: Write minimum code to pass
|
||||
3. Refactor: improve code while tests stay green
|
||||
|
||||
Write tests first for new features. Write tests after for bug fixes. Never refactor without tests.
|
||||
|
||||
## Test Organization
|
||||
|
||||
Group tests by feature or behavior, not by file structure. Name tests to describe the scenario:
|
||||
|
||||
```python
|
||||
class TestUserAuthentication:
|
||||
def test_valid_credentials_succeeds(self):
|
||||
pass
|
||||
|
||||
def test_invalid_credentials_fails(self):
|
||||
pass
|
||||
|
||||
def test_locked_account_fails(self):
|
||||
pass
|
||||
```
|
||||
|
||||
Each test should stand alone. Avoid shared state between tests. Use fixtures or setup methods to reduce duplication.
|
||||
|
||||
## Test Data
|
||||
|
||||
Use realistic test data that reflects production scenarios:
|
||||
|
||||
```python
|
||||
# GOOD: Realistic values
|
||||
user = User(
|
||||
email="alice@example.com",
|
||||
name="Alice Smith",
|
||||
age=28
|
||||
)
|
||||
|
||||
# BAD: Placeholder values
|
||||
user = User(
|
||||
email="test@test.com",
|
||||
name="Test User",
|
||||
age=999
|
||||
)
|
||||
```
|
||||
|
||||
Avoid magic strings and numbers. Use named constants for expected values that change often.
|
||||
0
rules/frameworks/.gitkeep
Normal file
0
rules/frameworks/.gitkeep
Normal file
42
rules/frameworks/n8n.md
Normal file
42
rules/frameworks/n8n.md
Normal file
@@ -0,0 +1,42 @@
|
||||
# n8n Workflow Automation Rules
|
||||
|
||||
## Workflow Design
|
||||
- Start with a clear trigger: Webhook, Schedule, or Event source
|
||||
- Keep workflows under 20 nodes for maintainability
|
||||
- Group related logic with sub-workflows
|
||||
- Use the "Switch" node for conditional branching
|
||||
- Add "Wait" nodes between rate-limited API calls
|
||||
|
||||
## Node Naming
|
||||
- Use verb-based names: `Fetch Users`, `Transform Data`, `Send Email`
|
||||
- Prefix data nodes: `Get_`, `Set_`, `Update_`
|
||||
- Prefix conditionals: `Check_`, `If_`, `When_`
|
||||
- Prefix actions: `Send_`, `Create_`, `Delete_`
|
||||
- Add version suffix to API nodes: `API_v1_Users`
|
||||
|
||||
## Error Handling
|
||||
- Always add an Error Trigger node
|
||||
- Route errors to a "Notify Failure" branch
|
||||
- Log error details: `$json.error.message`, `$json.node.name`
|
||||
- Send alerts on critical failures
|
||||
- Add "Continue On Fail" for non-essential nodes
|
||||
|
||||
## Data Flow
|
||||
- Use "Set" nodes to normalize output structure
|
||||
- Reference previous nodes: `{{ $json.field }}`
|
||||
- Use "Merge" node to combine multiple data sources
|
||||
- Apply "Code" node for complex transformations
|
||||
- Clean data before sending to external APIs
|
||||
|
||||
## Credential Security
|
||||
- Store all secrets in n8n credentials manager
|
||||
- Never hardcode API keys or tokens
|
||||
- Use environment-specific credential sets
|
||||
- Rotate credentials regularly
|
||||
- Limit credential scope to minimum required permissions
|
||||
|
||||
## Testing
|
||||
- Test each node independently with "Execute Node"
|
||||
- Verify data structure at each step
|
||||
- Mock external dependencies during development
|
||||
- Log workflow execution for debugging
|
||||
0
rules/languages/.gitkeep
Normal file
0
rules/languages/.gitkeep
Normal file
129
rules/languages/nix.md
Normal file
129
rules/languages/nix.md
Normal file
@@ -0,0 +1,129 @@
|
||||
# Nix Code Conventions
|
||||
|
||||
## Formatting
|
||||
|
||||
- Use `alejandra` for formatting
|
||||
- camelCase for variables, `PascalCase` for types
|
||||
- 2 space indentation (alejandra default)
|
||||
- No trailing whitespace
|
||||
|
||||
## Flake Structure
|
||||
|
||||
```nix
|
||||
{
|
||||
description = "Description here";
|
||||
inputs = {
|
||||
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
|
||||
flake-utils.url = "github:numtide/flake-utils";
|
||||
};
|
||||
outputs = { self, nixpkgs, flake-utils, ... }:
|
||||
flake-utils.lib.eachDefaultSystem (system:
|
||||
let
|
||||
pkgs = nixpkgs.legacyPackages.${system};
|
||||
in {
|
||||
packages.default = pkgs.hello;
|
||||
devShells.default = pkgs.mkShell {
|
||||
buildInputs = [ pkgs.hello ];
|
||||
};
|
||||
}
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## Module Patterns
|
||||
|
||||
Standard module function signature:
|
||||
|
||||
```nix
|
||||
{ config, lib, pkgs, ... }:
|
||||
{
|
||||
options.myService.enable = lib.mkEnableOption "my service";
|
||||
config = lib.mkIf config.myService.enable {
|
||||
services.myService.enable = true;
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
## Conditionals and Merging
|
||||
|
||||
- Use `mkIf` for conditional config
|
||||
- Use `mkMerge` to combine multiple config sets
|
||||
- Use `mkOptionDefault` for defaults that can be overridden
|
||||
|
||||
```nix
|
||||
config = lib.mkMerge [
|
||||
(lib.mkIf cfg.enable { ... })
|
||||
(lib.mkIf cfg.extraConfig { ... })
|
||||
];
|
||||
```
|
||||
|
||||
## Anti-Patterns (AVOID)
|
||||
|
||||
### `with pkgs;`
|
||||
Bad: Pollutes namespace, hard to trace origins
|
||||
```nix
|
||||
{ pkgs, ... }:
|
||||
{
|
||||
packages = with pkgs; [ vim git ];
|
||||
}
|
||||
```
|
||||
|
||||
Good: Explicit references
|
||||
```nix
|
||||
{ pkgs, ... }:
|
||||
{
|
||||
packages = [ pkgs.vim pkgs.git ];
|
||||
}
|
||||
```
|
||||
|
||||
### `builtins.fetchTarball`
|
||||
Use flake inputs instead. `fetchTarball` is non-reproducible.
|
||||
|
||||
### Impure operations
|
||||
Avoid `import <nixpkgs>` in flakes. Always use inputs.
|
||||
|
||||
### `builtins.getAttr` / `builtins.hasAttr`
|
||||
Use `lib.attrByPath` or `lib.optionalAttrs` instead.
|
||||
|
||||
## Home Manager Patterns
|
||||
|
||||
```nix
|
||||
{ config, pkgs, lib, ... }:
|
||||
{
|
||||
home.packages = with pkgs; [ ripgrep fd ];
|
||||
programs.zsh.enable = true;
|
||||
xdg.configFile."myapp/config".text = "...";
|
||||
}
|
||||
```
|
||||
|
||||
## Overlays
|
||||
|
||||
```nix
|
||||
{ config, lib, pkgs, ... }:
|
||||
let
|
||||
myOverlay = final: prev: {
|
||||
myPackage = prev.myPackage.overrideAttrs (old: { ... });
|
||||
};
|
||||
in
|
||||
{
|
||||
nixpkgs.overlays = [ myOverlay ];
|
||||
}
|
||||
```
|
||||
|
||||
## Imports and References
|
||||
|
||||
- Use flake inputs for dependencies
|
||||
- `lib` is always available in modules
|
||||
- Reference packages via `pkgs.packageName`
|
||||
- Use `callPackage` for complex package definitions
|
||||
|
||||
## File Organization
|
||||
|
||||
```
|
||||
flake.nix # Entry point
|
||||
modules/ # NixOS modules
|
||||
services/
|
||||
my-service.nix
|
||||
overlays/ # Package overrides
|
||||
default.nix
|
||||
```
|
||||
224
rules/languages/python.md
Normal file
224
rules/languages/python.md
Normal file
@@ -0,0 +1,224 @@
|
||||
# Python Language Rules
|
||||
|
||||
## Toolchain
|
||||
|
||||
### Package Management (uv)
|
||||
```bash
|
||||
uv init my-project --package
|
||||
uv add numpy pandas
|
||||
uv add --dev pytest ruff pyright hypothesis
|
||||
uv run python -m pytest
|
||||
uv lock --upgrade-package numpy
|
||||
```
|
||||
|
||||
### Linting & Formatting (ruff)
|
||||
```toml
|
||||
[tool.ruff]
|
||||
line-length = 100
|
||||
target-version = "py311"
|
||||
|
||||
[tool.ruff.lint]
|
||||
select = ["E", "F", "W", "I", "N", "UP"]
|
||||
ignore = ["E501"]
|
||||
|
||||
[tool.ruff.format]
|
||||
quote-style = "double"
|
||||
```
|
||||
|
||||
### Type Checking (pyright)
|
||||
```toml
|
||||
[tool.pyright]
|
||||
typeCheckingMode = "strict"
|
||||
reportMissingTypeStubs = true
|
||||
reportUnknownMemberType = true
|
||||
```
|
||||
|
||||
### Testing (pytest + hypothesis)
|
||||
```python
|
||||
import pytest
|
||||
from hypothesis import given, strategies as st
|
||||
|
||||
@given(st.integers(), st.integers())
|
||||
def test_addition_commutative(a, b):
|
||||
assert a + b == b + a
|
||||
|
||||
@pytest.fixture
|
||||
def user_data():
|
||||
return {"name": "Alice", "age": 30}
|
||||
|
||||
def test_user_creation(user_data):
|
||||
user = User(**user_data)
|
||||
assert user.name == "Alice"
|
||||
```
|
||||
|
||||
### Data Validation (Pydantic)
|
||||
```python
|
||||
from pydantic import BaseModel, Field, validator
|
||||
|
||||
class User(BaseModel):
|
||||
name: str = Field(min_length=1, max_length=100)
|
||||
age: int = Field(ge=0, le=150)
|
||||
email: str
|
||||
|
||||
@validator('email')
|
||||
def email_must_contain_at(cls, v):
|
||||
if '@' not in v:
|
||||
raise ValueError('must contain @')
|
||||
return v
|
||||
```
|
||||
|
||||
## Idioms
|
||||
|
||||
### Comprehensions
|
||||
```python
|
||||
# List comprehension
|
||||
squares = [x**2 for x in range(10) if x % 2 == 0]
|
||||
|
||||
# Dict comprehension
|
||||
word_counts = {word: text.count(word) for word in unique_words}
|
||||
|
||||
# Set comprehension
|
||||
unique_chars = {char for char in text if char.isalpha()}
|
||||
```
|
||||
|
||||
### Context Managers
|
||||
```python
|
||||
# Built-in context managers
|
||||
with open('file.txt', 'r') as f:
|
||||
content = f.read()
|
||||
|
||||
# Custom context manager
|
||||
from contextlib import contextmanager
|
||||
|
||||
@contextmanager
|
||||
def timer():
|
||||
start = time.time()
|
||||
yield
|
||||
print(f"Elapsed: {time.time() - start:.2f}s")
|
||||
```
|
||||
|
||||
### Generators
|
||||
```python
|
||||
def fibonacci():
|
||||
a, b = 0, 1
|
||||
while True:
|
||||
yield a
|
||||
a, b = b, a + b
|
||||
|
||||
def read_lines(file_path):
|
||||
with open(file_path) as f:
|
||||
for line in f:
|
||||
yield line.strip()
|
||||
```
|
||||
|
||||
### F-strings
|
||||
```python
|
||||
name = "Alice"
|
||||
age = 30
|
||||
|
||||
# Basic interpolation
|
||||
msg = f"Name: {name}, Age: {age}"
|
||||
|
||||
# Expression evaluation
|
||||
msg = f"Next year: {age + 1}"
|
||||
|
||||
# Format specs
|
||||
msg = f"Price: ${price:.2f}"
|
||||
msg = f"Hex: {0xFF:X}"
|
||||
```
|
||||
|
||||
## Anti-Patterns
|
||||
|
||||
### Bare Except
|
||||
```python
|
||||
# AVOID: Catches all exceptions including SystemExit
|
||||
try:
|
||||
risky_operation()
|
||||
except:
|
||||
pass
|
||||
|
||||
# USE: Catch specific exceptions
|
||||
try:
|
||||
risky_operation()
|
||||
except ValueError as e:
|
||||
log_error(e)
|
||||
except KeyError as e:
|
||||
log_error(e)
|
||||
```
|
||||
|
||||
### Mutable Defaults
|
||||
```python
|
||||
# AVOID: Default argument created once
|
||||
def append_item(item, items=[]):
|
||||
items.append(item)
|
||||
return items
|
||||
|
||||
# USE: None as sentinel
|
||||
def append_item(item, items=None):
|
||||
if items is None:
|
||||
items = []
|
||||
items.append(item)
|
||||
return items
|
||||
```
|
||||
|
||||
### Global State
|
||||
```python
|
||||
# AVOID: Global mutable state
|
||||
counter = 0
|
||||
|
||||
def increment():
|
||||
global counter
|
||||
counter += 1
|
||||
|
||||
# USE: Class-based state
|
||||
class Counter:
|
||||
def __init__(self):
|
||||
self.count = 0
|
||||
|
||||
def increment(self):
|
||||
self.count += 1
|
||||
```
|
||||
|
||||
### Star Imports
|
||||
```python
|
||||
# AVOID: Pollutes namespace, unclear origins
|
||||
from module import *
|
||||
|
||||
# USE: Explicit imports
|
||||
from module import specific_function, MyClass
|
||||
import module as m
|
||||
```
|
||||
|
||||
## Project Setup
|
||||
|
||||
### pyproject.toml Structure
|
||||
```toml
|
||||
[project]
|
||||
name = "my-project"
|
||||
version = "0.1.0"
|
||||
requires-python = ">=3.11"
|
||||
dependencies = [
|
||||
"pydantic>=2.0",
|
||||
"httpx>=0.25",
|
||||
]
|
||||
|
||||
[project.optional-dependencies]
|
||||
dev = ["pytest", "ruff", "pyright", "hypothesis"]
|
||||
|
||||
[build-system]
|
||||
requires = ["hatchling"]
|
||||
build-backend = "hatchling.build"
|
||||
```
|
||||
|
||||
### src Layout
|
||||
```
|
||||
my-project/
|
||||
├── pyproject.toml
|
||||
└── src/
|
||||
└── my_project/
|
||||
├── __init__.py
|
||||
├── main.py
|
||||
└── utils/
|
||||
├── __init__.py
|
||||
└── helpers.py
|
||||
```
|
||||
100
rules/languages/shell.md
Normal file
100
rules/languages/shell.md
Normal file
@@ -0,0 +1,100 @@
|
||||
# Shell Scripting Rules
|
||||
|
||||
## Shebang
|
||||
|
||||
Always use `#!/usr/bin/env bash` for portability. Never hardcode `/bin/bash`.
|
||||
|
||||
```bash
|
||||
#!/usr/bin/env bash
|
||||
```
|
||||
|
||||
## Strict Mode
|
||||
|
||||
Enable strict mode in every script.
|
||||
|
||||
```bash
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
```
|
||||
|
||||
- `-e`: Exit on error
|
||||
- `-u`: Error on unset variables
|
||||
- `-o pipefail`: Return exit status of last failed pipe command
|
||||
|
||||
## Shellcheck
|
||||
|
||||
Run shellcheck on all scripts before committing.
|
||||
|
||||
```bash
|
||||
shellcheck script.sh
|
||||
```
|
||||
|
||||
## Quoting
|
||||
|
||||
Quote all variable expansions and command substitutions. Use arrays instead of word-splitting strings.
|
||||
|
||||
```bash
|
||||
# Good
|
||||
"${var}"
|
||||
files=("file1.txt" "file2.txt")
|
||||
for f in "${files[@]}"; do
|
||||
process "$f"
|
||||
done
|
||||
|
||||
# Bad
|
||||
$var
|
||||
files="file1.txt file2.txt"
|
||||
for f in $files; do
|
||||
process $f
|
||||
done
|
||||
```
|
||||
|
||||
## Functions
|
||||
|
||||
Define with parentheses, use `local` for variables.
|
||||
|
||||
```bash
|
||||
my_function() {
|
||||
local result
|
||||
result=$(some_command)
|
||||
echo "$result"
|
||||
}
|
||||
```
|
||||
|
||||
## Command Substitution
|
||||
|
||||
Use `$()` not backticks. Nests cleanly.
|
||||
|
||||
```bash
|
||||
# Good
|
||||
output=$(ls "$dir")
|
||||
|
||||
# Bad
|
||||
output=`ls $dir`
|
||||
```
|
||||
|
||||
## POSIX Portability
|
||||
|
||||
Write POSIX-compliant scripts when targeting `/bin/sh`.
|
||||
|
||||
- Use `[[` only for bash scripts
|
||||
- Use `printf` instead of `echo -e`
|
||||
- Avoid `[[`, `((`, `&>` in sh scripts
|
||||
|
||||
## Error Handling
|
||||
|
||||
Use `trap` for cleanup.
|
||||
|
||||
```bash
|
||||
cleanup() {
|
||||
rm -f /tmp/lockfile
|
||||
}
|
||||
trap cleanup EXIT
|
||||
```
|
||||
|
||||
## Readability
|
||||
|
||||
- Use 2-space indentation
|
||||
- Limit lines to 80 characters
|
||||
- Add comments for non-obvious logic
|
||||
- Separate sections with blank lines
|
||||
150
rules/languages/typescript.md
Normal file
150
rules/languages/typescript.md
Normal file
@@ -0,0 +1,150 @@
|
||||
# TypeScript Patterns
|
||||
|
||||
## Strict tsconfig
|
||||
|
||||
Always enable strict mode and key safety options:
|
||||
|
||||
```json
|
||||
{
|
||||
"compilerOptions": {
|
||||
"strict": true,
|
||||
"noUncheckedIndexedAccess": true,
|
||||
"noImplicitReturns": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Discriminated Unions
|
||||
|
||||
Use discriminated unions for exhaustive type safety:
|
||||
|
||||
```ts
|
||||
type Result =
|
||||
| { success: true; data: string }
|
||||
| { success: false; error: Error };
|
||||
|
||||
function handleResult(result: Result): string {
|
||||
if (result.success) {
|
||||
return result.data;
|
||||
}
|
||||
throw result.error;
|
||||
}
|
||||
```
|
||||
|
||||
## Branded Types
|
||||
|
||||
Prevent type confusion with nominal branding:
|
||||
|
||||
```ts
|
||||
type UserId = string & { readonly __brand: unique symbol };
|
||||
type Email = string & { readonly __brand: unique symbol };
|
||||
|
||||
function createUserId(id: string): UserId {
|
||||
return id as UserId;
|
||||
}
|
||||
|
||||
function sendEmail(email: Email, userId: UserId) {}
|
||||
```
|
||||
|
||||
## satisfies Operator
|
||||
|
||||
Use `satisfies` for type-safe object literal inference:
|
||||
|
||||
```ts
|
||||
const config = {
|
||||
port: 3000,
|
||||
host: "localhost",
|
||||
} satisfies {
|
||||
port: number;
|
||||
host: string;
|
||||
debug?: boolean;
|
||||
};
|
||||
|
||||
config.port; // number
|
||||
config.host; // string
|
||||
```
|
||||
|
||||
## as const Assertions
|
||||
|
||||
Freeze literal types with `as const`:
|
||||
|
||||
```ts
|
||||
const routes = {
|
||||
home: "/",
|
||||
about: "/about",
|
||||
contact: "/contact",
|
||||
} as const;
|
||||
|
||||
type Route = typeof routes[keyof typeof routes];
|
||||
```
|
||||
|
||||
## Modern Features
|
||||
|
||||
```ts
|
||||
// Promise.withResolvers()
|
||||
const { promise, resolve, reject } = Promise.withResolvers<string>();
|
||||
|
||||
// Object.groupBy()
|
||||
const users = [
|
||||
{ name: "Alice", role: "admin" },
|
||||
{ name: "Bob", role: "user" },
|
||||
];
|
||||
const grouped = Object.groupBy(users, u => u.role);
|
||||
|
||||
// using statement for disposables
|
||||
class Resource implements Disposable {
|
||||
async [Symbol.asyncDispose]() {
|
||||
await this.cleanup();
|
||||
}
|
||||
}
|
||||
async function withResource() {
|
||||
using r = new Resource();
|
||||
}
|
||||
```
|
||||
|
||||
## Toolchain
|
||||
|
||||
Prefer modern tooling:
|
||||
- Runtime: `bun` or `tsx` (no `tsc` for execution)
|
||||
- Linting: `biome` (preferred) or `eslint`
|
||||
- Formatting: `biome` (built-in) or `prettier`
|
||||
|
||||
## Anti-Patterns
|
||||
|
||||
Avoid these TypeScript patterns:
|
||||
|
||||
```ts
|
||||
// NEVER use as any
|
||||
const data = response as any;
|
||||
|
||||
// NEVER use @ts-ignore
|
||||
// @ts-ignore
|
||||
const value = unknownFunction();
|
||||
|
||||
// NEVER use ! assertion (non-null)
|
||||
const element = document.querySelector("#foo")!;
|
||||
|
||||
// NEVER use enum (prefer union)
|
||||
enum Status { Active, Inactive } // ❌
|
||||
|
||||
// Prefer const object or union
|
||||
type Status = "Active" | "Inactive"; // ✅
|
||||
const Status = { Active: "Active", Inactive: "Inactive" } as const; // ✅
|
||||
```
|
||||
|
||||
## Indexed Access Safety
|
||||
|
||||
With `noUncheckedIndexedAccess`, handle undefined:
|
||||
|
||||
```ts
|
||||
const arr: string[] = ["a", "b"];
|
||||
const item = arr[0]; // string | undefined
|
||||
|
||||
const item2 = arr.at(0); // string | undefined
|
||||
|
||||
const map = new Map<string, number>();
|
||||
const value = map.get("key"); // number | undefined
|
||||
```
|
||||
Reference in New Issue
Block a user