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",
|
"active_plan": "/home/m3tam3re/p/AI/AGENTS/.sisyphus/plans/rules-system.md",
|
||||||
"started_at": "2026-02-14T04:43:37.746Z",
|
"started_at": "2026-02-17T17:50:08.922Z",
|
||||||
"session_ids": [
|
"session_ids": [
|
||||||
"ses_3a5a47a05ffeoNYfz2RARYsHX9"
|
"ses_393691db2ffe4YZvieMFehJe54"
|
||||||
],
|
],
|
||||||
"plan_name": "opencode-memory",
|
"plan_name": "rules-system",
|
||||||
"agent": "atlas"
|
"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