fleet-commander-ai 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +202 -0
- package/README.md +159 -0
- package/bin/fleet-commander-mcp.js +27 -0
- package/bin/fleet-commander.js +22 -0
- package/dist/client/assets/index-CHukC8Hq.js +188 -0
- package/dist/client/assets/index-CHukC8Hq.js.map +1 -0
- package/dist/client/assets/index-DvMjcYbg.css +1 -0
- package/dist/client/index.html +13 -0
- package/dist/server/config.d.ts +51 -0
- package/dist/server/config.d.ts.map +1 -0
- package/dist/server/config.js +104 -0
- package/dist/server/config.js.map +1 -0
- package/dist/server/db.d.ts +388 -0
- package/dist/server/db.d.ts.map +1 -0
- package/dist/server/db.js +1524 -0
- package/dist/server/db.js.map +1 -0
- package/dist/server/index.d.ts +2 -0
- package/dist/server/index.d.ts.map +1 -0
- package/dist/server/index.js +162 -0
- package/dist/server/index.js.map +1 -0
- package/dist/server/mcp/index.d.ts +2 -0
- package/dist/server/mcp/index.d.ts.map +1 -0
- package/dist/server/mcp/index.js +112 -0
- package/dist/server/mcp/index.js.map +1 -0
- package/dist/server/mcp/tools/add-project.d.ts +9 -0
- package/dist/server/mcp/tools/add-project.d.ts.map +1 -0
- package/dist/server/mcp/tools/add-project.js +58 -0
- package/dist/server/mcp/tools/add-project.js.map +1 -0
- package/dist/server/mcp/tools/get-team-timeline.d.ts +9 -0
- package/dist/server/mcp/tools/get-team-timeline.d.ts.map +1 -0
- package/dist/server/mcp/tools/get-team-timeline.js +48 -0
- package/dist/server/mcp/tools/get-team-timeline.js.map +1 -0
- package/dist/server/mcp/tools/get-team.d.ts +9 -0
- package/dist/server/mcp/tools/get-team.d.ts.map +1 -0
- package/dist/server/mcp/tools/get-team.js +48 -0
- package/dist/server/mcp/tools/get-team.js.map +1 -0
- package/dist/server/mcp/tools/get-usage.d.ts +9 -0
- package/dist/server/mcp/tools/get-usage.d.ts.map +1 -0
- package/dist/server/mcp/tools/get-usage.js +33 -0
- package/dist/server/mcp/tools/get-usage.js.map +1 -0
- package/dist/server/mcp/tools/launch-team.d.ts +8 -0
- package/dist/server/mcp/tools/launch-team.d.ts.map +1 -0
- package/dist/server/mcp/tools/launch-team.js +49 -0
- package/dist/server/mcp/tools/launch-team.js.map +1 -0
- package/dist/server/mcp/tools/list-issues.d.ts +9 -0
- package/dist/server/mcp/tools/list-issues.d.ts.map +1 -0
- package/dist/server/mcp/tools/list-issues.js +47 -0
- package/dist/server/mcp/tools/list-issues.js.map +1 -0
- package/dist/server/mcp/tools/list-projects.d.ts +9 -0
- package/dist/server/mcp/tools/list-projects.d.ts.map +1 -0
- package/dist/server/mcp/tools/list-projects.js +32 -0
- package/dist/server/mcp/tools/list-projects.js.map +1 -0
- package/dist/server/mcp/tools/list-teams.d.ts +9 -0
- package/dist/server/mcp/tools/list-teams.d.ts.map +1 -0
- package/dist/server/mcp/tools/list-teams.js +43 -0
- package/dist/server/mcp/tools/list-teams.js.map +1 -0
- package/dist/server/mcp/tools/restart-team.d.ts +8 -0
- package/dist/server/mcp/tools/restart-team.d.ts.map +1 -0
- package/dist/server/mcp/tools/restart-team.js +46 -0
- package/dist/server/mcp/tools/restart-team.js.map +1 -0
- package/dist/server/mcp/tools/send-message.d.ts +9 -0
- package/dist/server/mcp/tools/send-message.d.ts.map +1 -0
- package/dist/server/mcp/tools/send-message.js +48 -0
- package/dist/server/mcp/tools/send-message.js.map +1 -0
- package/dist/server/mcp/tools/stop-team.d.ts +8 -0
- package/dist/server/mcp/tools/stop-team.d.ts.map +1 -0
- package/dist/server/mcp/tools/stop-team.js +46 -0
- package/dist/server/mcp/tools/stop-team.js.map +1 -0
- package/dist/server/mcp/tools/system-health.d.ts +9 -0
- package/dist/server/mcp/tools/system-health.d.ts.map +1 -0
- package/dist/server/mcp/tools/system-health.js +32 -0
- package/dist/server/mcp/tools/system-health.js.map +1 -0
- package/dist/server/middleware/error-handler.d.ts +32 -0
- package/dist/server/middleware/error-handler.d.ts.map +1 -0
- package/dist/server/middleware/error-handler.js +65 -0
- package/dist/server/middleware/error-handler.js.map +1 -0
- package/dist/server/routes/events.d.ts +16 -0
- package/dist/server/routes/events.d.ts.map +1 -0
- package/dist/server/routes/events.js +164 -0
- package/dist/server/routes/events.js.map +1 -0
- package/dist/server/routes/issues.d.ts +4 -0
- package/dist/server/routes/issues.d.ts.map +1 -0
- package/dist/server/routes/issues.js +198 -0
- package/dist/server/routes/issues.js.map +1 -0
- package/dist/server/routes/project-groups.d.ts +4 -0
- package/dist/server/routes/project-groups.d.ts.map +1 -0
- package/dist/server/routes/project-groups.js +124 -0
- package/dist/server/routes/project-groups.js.map +1 -0
- package/dist/server/routes/projects.d.ts +4 -0
- package/dist/server/routes/projects.d.ts.map +1 -0
- package/dist/server/routes/projects.js +319 -0
- package/dist/server/routes/projects.js.map +1 -0
- package/dist/server/routes/prs.d.ts +4 -0
- package/dist/server/routes/prs.d.ts.map +1 -0
- package/dist/server/routes/prs.js +186 -0
- package/dist/server/routes/prs.js.map +1 -0
- package/dist/server/routes/query.d.ts +4 -0
- package/dist/server/routes/query.d.ts.map +1 -0
- package/dist/server/routes/query.js +82 -0
- package/dist/server/routes/query.js.map +1 -0
- package/dist/server/routes/state-machine.d.ts +4 -0
- package/dist/server/routes/state-machine.d.ts.map +1 -0
- package/dist/server/routes/state-machine.js +86 -0
- package/dist/server/routes/state-machine.js.map +1 -0
- package/dist/server/routes/stream.d.ts +18 -0
- package/dist/server/routes/stream.d.ts.map +1 -0
- package/dist/server/routes/stream.js +62 -0
- package/dist/server/routes/stream.js.map +1 -0
- package/dist/server/routes/system.d.ts +4 -0
- package/dist/server/routes/system.d.ts.map +1 -0
- package/dist/server/routes/system.js +225 -0
- package/dist/server/routes/system.js.map +1 -0
- package/dist/server/routes/teams.d.ts +4 -0
- package/dist/server/routes/teams.d.ts.map +1 -0
- package/dist/server/routes/teams.js +570 -0
- package/dist/server/routes/teams.js.map +1 -0
- package/dist/server/routes/usage.d.ts +4 -0
- package/dist/server/routes/usage.d.ts.map +1 -0
- package/dist/server/routes/usage.js +80 -0
- package/dist/server/routes/usage.js.map +1 -0
- package/dist/server/schema.sql +267 -0
- package/dist/server/services/cc-query.d.ts +20 -0
- package/dist/server/services/cc-query.d.ts.map +1 -0
- package/dist/server/services/cc-query.js +352 -0
- package/dist/server/services/cc-query.js.map +1 -0
- package/dist/server/services/cleanup.d.ts +15 -0
- package/dist/server/services/cleanup.d.ts.map +1 -0
- package/dist/server/services/cleanup.js +232 -0
- package/dist/server/services/cleanup.js.map +1 -0
- package/dist/server/services/diagnostics-service.d.ts +85 -0
- package/dist/server/services/diagnostics-service.d.ts.map +1 -0
- package/dist/server/services/diagnostics-service.js +242 -0
- package/dist/server/services/diagnostics-service.js.map +1 -0
- package/dist/server/services/event-collector.d.ts +125 -0
- package/dist/server/services/event-collector.d.ts.map +1 -0
- package/dist/server/services/event-collector.js +299 -0
- package/dist/server/services/event-collector.js.map +1 -0
- package/dist/server/services/event-service.d.ts +22 -0
- package/dist/server/services/event-service.d.ts.map +1 -0
- package/dist/server/services/event-service.js +53 -0
- package/dist/server/services/event-service.js.map +1 -0
- package/dist/server/services/github-poller.d.ts +68 -0
- package/dist/server/services/github-poller.d.ts.map +1 -0
- package/dist/server/services/github-poller.js +563 -0
- package/dist/server/services/github-poller.js.map +1 -0
- package/dist/server/services/issue-fetcher.d.ts +231 -0
- package/dist/server/services/issue-fetcher.d.ts.map +1 -0
- package/dist/server/services/issue-fetcher.js +1053 -0
- package/dist/server/services/issue-fetcher.js.map +1 -0
- package/dist/server/services/issue-service.d.ts +102 -0
- package/dist/server/services/issue-service.d.ts.map +1 -0
- package/dist/server/services/issue-service.js +279 -0
- package/dist/server/services/issue-service.js.map +1 -0
- package/dist/server/services/message-template-service.d.ts +39 -0
- package/dist/server/services/message-template-service.d.ts.map +1 -0
- package/dist/server/services/message-template-service.js +87 -0
- package/dist/server/services/message-template-service.js.map +1 -0
- package/dist/server/services/pr-service.d.ts +73 -0
- package/dist/server/services/pr-service.d.ts.map +1 -0
- package/dist/server/services/pr-service.js +231 -0
- package/dist/server/services/pr-service.js.map +1 -0
- package/dist/server/services/project-group-service.d.ts +64 -0
- package/dist/server/services/project-group-service.d.ts.map +1 -0
- package/dist/server/services/project-group-service.js +149 -0
- package/dist/server/services/project-group-service.js.map +1 -0
- package/dist/server/services/project-service.d.ts +161 -0
- package/dist/server/services/project-service.d.ts.map +1 -0
- package/dist/server/services/project-service.js +623 -0
- package/dist/server/services/project-service.js.map +1 -0
- package/dist/server/services/service-error.d.ts +25 -0
- package/dist/server/services/service-error.d.ts.map +1 -0
- package/dist/server/services/service-error.js +49 -0
- package/dist/server/services/service-error.js.map +1 -0
- package/dist/server/services/sse-broker.d.ts +144 -0
- package/dist/server/services/sse-broker.d.ts.map +1 -0
- package/dist/server/services/sse-broker.js +111 -0
- package/dist/server/services/sse-broker.js.map +1 -0
- package/dist/server/services/startup-recovery.d.ts +10 -0
- package/dist/server/services/startup-recovery.d.ts.map +1 -0
- package/dist/server/services/startup-recovery.js +122 -0
- package/dist/server/services/startup-recovery.js.map +1 -0
- package/dist/server/services/stuck-detector.d.ts +20 -0
- package/dist/server/services/stuck-detector.d.ts.map +1 -0
- package/dist/server/services/stuck-detector.js +167 -0
- package/dist/server/services/stuck-detector.js.map +1 -0
- package/dist/server/services/stuck-detector.test.d.ts +2 -0
- package/dist/server/services/stuck-detector.test.d.ts.map +1 -0
- package/dist/server/services/stuck-detector.test.js +363 -0
- package/dist/server/services/stuck-detector.test.js.map +1 -0
- package/dist/server/services/team-manager.d.ts +188 -0
- package/dist/server/services/team-manager.d.ts.map +1 -0
- package/dist/server/services/team-manager.js +1869 -0
- package/dist/server/services/team-manager.js.map +1 -0
- package/dist/server/services/team-service.d.ts +251 -0
- package/dist/server/services/team-service.d.ts.map +1 -0
- package/dist/server/services/team-service.js +707 -0
- package/dist/server/services/team-service.js.map +1 -0
- package/dist/server/services/usage-service.d.ts +42 -0
- package/dist/server/services/usage-service.d.ts.map +1 -0
- package/dist/server/services/usage-service.js +101 -0
- package/dist/server/services/usage-service.js.map +1 -0
- package/dist/server/services/usage-tracker.d.ts +68 -0
- package/dist/server/services/usage-tracker.d.ts.map +1 -0
- package/dist/server/services/usage-tracker.js +220 -0
- package/dist/server/services/usage-tracker.js.map +1 -0
- package/dist/server/utils/build-timeline.d.ts +32 -0
- package/dist/server/utils/build-timeline.d.ts.map +1 -0
- package/dist/server/utils/build-timeline.js +142 -0
- package/dist/server/utils/build-timeline.js.map +1 -0
- package/dist/server/utils/find-git-bash.d.ts +10 -0
- package/dist/server/utils/find-git-bash.d.ts.map +1 -0
- package/dist/server/utils/find-git-bash.js +46 -0
- package/dist/server/utils/find-git-bash.js.map +1 -0
- package/dist/server/utils/hook-installer.d.ts +20 -0
- package/dist/server/utils/hook-installer.d.ts.map +1 -0
- package/dist/server/utils/hook-installer.js +90 -0
- package/dist/server/utils/hook-installer.js.map +1 -0
- package/dist/server/utils/process-utils.d.ts +10 -0
- package/dist/server/utils/process-utils.d.ts.map +1 -0
- package/dist/server/utils/process-utils.js +33 -0
- package/dist/server/utils/process-utils.js.map +1 -0
- package/dist/server/utils/resolve-claude-path.d.ts +4 -0
- package/dist/server/utils/resolve-claude-path.d.ts.map +1 -0
- package/dist/server/utils/resolve-claude-path.js +66 -0
- package/dist/server/utils/resolve-claude-path.js.map +1 -0
- package/dist/server/utils/resolve-message.d.ts +10 -0
- package/dist/server/utils/resolve-message.d.ts.map +1 -0
- package/dist/server/utils/resolve-message.js +27 -0
- package/dist/server/utils/resolve-message.js.map +1 -0
- package/dist/shared/message-templates.d.ts +9 -0
- package/dist/shared/message-templates.d.ts.map +1 -0
- package/dist/shared/message-templates.js +88 -0
- package/dist/shared/message-templates.js.map +1 -0
- package/dist/shared/state-machine.d.ts +28 -0
- package/dist/shared/state-machine.d.ts.map +1 -0
- package/dist/shared/state-machine.js +282 -0
- package/dist/shared/state-machine.js.map +1 -0
- package/dist/shared/types.d.ts +404 -0
- package/dist/shared/types.d.ts.map +1 -0
- package/dist/shared/types.js +5 -0
- package/dist/shared/types.js.map +1 -0
- package/hooks/DESIGN.md +562 -0
- package/hooks/on_notification.sh +9 -0
- package/hooks/on_post_tool_use.sh +9 -0
- package/hooks/on_pre_compact.sh +10 -0
- package/hooks/on_session_end.sh +8 -0
- package/hooks/on_session_start.sh +8 -0
- package/hooks/on_stop.sh +8 -0
- package/hooks/on_stop_failure.sh +8 -0
- package/hooks/on_subagent_start.sh +8 -0
- package/hooks/on_subagent_stop.sh +8 -0
- package/hooks/on_teammate_idle.sh +8 -0
- package/hooks/on_tool_error.sh +8 -0
- package/hooks/send_event.sh +101 -0
- package/hooks/settings.json.example +120 -0
- package/package.json +93 -0
- package/prompts/default-prompt.md +16 -0
- package/scripts/install.ps1 +22 -0
- package/scripts/install.sh +229 -0
- package/scripts/launch.js +64 -0
- package/scripts/uninstall.ps1 +22 -0
- package/scripts/uninstall.sh +123 -0
- package/templates/agents/fleet-dev.md +162 -0
- package/templates/agents/fleet-planner.md +263 -0
- package/templates/agents/fleet-reviewer.md +309 -0
- package/templates/archive/fleet-coordinator.md +128 -0
- package/templates/archive/fleet-dev-csharp.md +74 -0
- package/templates/archive/fleet-dev-devops.md +76 -0
- package/templates/archive/fleet-dev-fsharp.md +83 -0
- package/templates/archive/fleet-dev-generic.md +64 -0
- package/templates/archive/fleet-dev-python.md +75 -0
- package/templates/archive/fleet-dev-typescript.md +76 -0
- package/templates/guides/api-design.md +159 -0
- package/templates/guides/csharp-conventions.md +182 -0
- package/templates/guides/devops-conventions.md +192 -0
- package/templates/guides/fsharp-conventions.md +201 -0
- package/templates/guides/python-conventions.md +146 -0
- package/templates/guides/sql-database.md +125 -0
- package/templates/guides/testing-strategies.md +123 -0
- package/templates/guides/typescript-conventions.md +130 -0
- package/templates/workflow.md +513 -0
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
# DevOps / Infrastructure Conventions
|
|
2
|
+
|
|
3
|
+
> Applies to: `.github/workflows/*.yml`, `Dockerfile`, `docker-compose.yml`, `*.sh`, `*.ps1`, `Makefile`
|
|
4
|
+
> Last updated: 2026-03-18
|
|
5
|
+
|
|
6
|
+
## Architecture
|
|
7
|
+
|
|
8
|
+
Infrastructure code typically lives in these locations:
|
|
9
|
+
|
|
10
|
+
```
|
|
11
|
+
.github/workflows/ -- GitHub Actions CI/CD pipelines
|
|
12
|
+
docker/ -- Dockerfiles and compose files (or project root)
|
|
13
|
+
scripts/ -- Build, deploy, and utility scripts
|
|
14
|
+
k8s/ or deploy/ -- Kubernetes manifests, Helm charts
|
|
15
|
+
infra/ -- Terraform, Pulumi, or other IaC
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
Read `CLAUDE.md` and existing CI config before making changes. Match the project's
|
|
19
|
+
existing patterns for pipeline structure, script conventions, and deployment approach.
|
|
20
|
+
|
|
21
|
+
## CI/CD — GitHub Actions
|
|
22
|
+
|
|
23
|
+
### Pinned versions
|
|
24
|
+
|
|
25
|
+
Always use pinned action versions, not `@main` or floating tags:
|
|
26
|
+
|
|
27
|
+
```yaml
|
|
28
|
+
# RIGHT
|
|
29
|
+
uses: actions/checkout@v4
|
|
30
|
+
uses: actions/setup-node@v4
|
|
31
|
+
|
|
32
|
+
# WRONG
|
|
33
|
+
uses: actions/checkout@main
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
### Permissions block
|
|
37
|
+
|
|
38
|
+
Set explicit `permissions` on every workflow. Use least-privilege:
|
|
39
|
+
|
|
40
|
+
```yaml
|
|
41
|
+
permissions:
|
|
42
|
+
contents: read
|
|
43
|
+
pull-requests: write
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
### Matrix strategies
|
|
47
|
+
|
|
48
|
+
Use matrices for multi-version testing. Include `fail-fast: false` when you want
|
|
49
|
+
all combinations to run:
|
|
50
|
+
|
|
51
|
+
```yaml
|
|
52
|
+
strategy:
|
|
53
|
+
fail-fast: false
|
|
54
|
+
matrix:
|
|
55
|
+
node-version: [18, 20, 22]
|
|
56
|
+
os: [ubuntu-latest, windows-latest]
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### Secrets
|
|
60
|
+
|
|
61
|
+
- Never hardcode secrets in workflows -- use `${{ secrets.NAME }}`.
|
|
62
|
+
- Never echo or log secret values.
|
|
63
|
+
- Use environment-level secrets for deployment-specific values.
|
|
64
|
+
|
|
65
|
+
## Docker
|
|
66
|
+
|
|
67
|
+
### Multi-stage builds
|
|
68
|
+
|
|
69
|
+
Use multi-stage builds to minimize image size:
|
|
70
|
+
|
|
71
|
+
```dockerfile
|
|
72
|
+
FROM node:20-alpine AS builder
|
|
73
|
+
WORKDIR /app
|
|
74
|
+
COPY package*.json ./
|
|
75
|
+
RUN npm ci
|
|
76
|
+
COPY . .
|
|
77
|
+
RUN npm run build
|
|
78
|
+
|
|
79
|
+
FROM node:20-alpine
|
|
80
|
+
WORKDIR /app
|
|
81
|
+
COPY --from=builder /app/dist ./dist
|
|
82
|
+
COPY --from=builder /app/node_modules ./node_modules
|
|
83
|
+
CMD ["node", "dist/index.js"]
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### Layer caching
|
|
87
|
+
|
|
88
|
+
Order Dockerfile instructions from least-changing to most-changing. Copy
|
|
89
|
+
dependency files before source code so dependency installation is cached.
|
|
90
|
+
|
|
91
|
+
### .dockerignore
|
|
92
|
+
|
|
93
|
+
Always maintain a `.dockerignore` file. Include at minimum:
|
|
94
|
+
`node_modules`, `.git`, `*.md`, `tests/`, `.env*`.
|
|
95
|
+
|
|
96
|
+
## Scripts
|
|
97
|
+
|
|
98
|
+
### Cross-platform compatibility
|
|
99
|
+
|
|
100
|
+
When the project supports both Windows and Linux:
|
|
101
|
+
|
|
102
|
+
- Provide both `.sh` and `.ps1` versions, or use a tool that runs on both (Node.js, Python).
|
|
103
|
+
- Use forward slashes in paths within scripts -- bash on Windows handles them.
|
|
104
|
+
- Avoid platform-specific commands without alternatives (`sed` on Windows needs Git Bash).
|
|
105
|
+
- Ensure shell scripts use LF line endings -- CRLF breaks shebang lines on Linux.
|
|
106
|
+
|
|
107
|
+
### Idempotency
|
|
108
|
+
|
|
109
|
+
Scripts must be safe to run multiple times. Check for existing state before
|
|
110
|
+
creating resources:
|
|
111
|
+
|
|
112
|
+
```bash
|
|
113
|
+
# RIGHT -- idempotent
|
|
114
|
+
mkdir -p "$TARGET_DIR"
|
|
115
|
+
[ -f "$CONFIG" ] || cp template.conf "$CONFIG"
|
|
116
|
+
|
|
117
|
+
# WRONG -- fails on second run
|
|
118
|
+
mkdir "$TARGET_DIR"
|
|
119
|
+
cp template.conf "$CONFIG"
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
### Error handling
|
|
123
|
+
|
|
124
|
+
Use `set -euo pipefail` in bash scripts. Check exit codes for critical operations.
|
|
125
|
+
|
|
126
|
+
## Anti-Patterns to Avoid
|
|
127
|
+
|
|
128
|
+
### Hardcoded paths and URLs
|
|
129
|
+
|
|
130
|
+
Never hardcode environment-specific values. Use environment variables or
|
|
131
|
+
configuration files:
|
|
132
|
+
|
|
133
|
+
```bash
|
|
134
|
+
# WRONG
|
|
135
|
+
curl https://api.prod.example.com/deploy
|
|
136
|
+
|
|
137
|
+
# RIGHT
|
|
138
|
+
curl "${DEPLOY_URL}/deploy"
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### Skipping YAML validation
|
|
142
|
+
|
|
143
|
+
Always validate YAML syntax before committing pipeline changes. A typo in a
|
|
144
|
+
workflow file can break CI for the entire team.
|
|
145
|
+
|
|
146
|
+
### Database migrations without rollback
|
|
147
|
+
|
|
148
|
+
Database migrations must be reversible. Always provide a down/rollback migration.
|
|
149
|
+
Test migrations against a fresh database to catch ordering issues.
|
|
150
|
+
|
|
151
|
+
## Testing Infrastructure Changes
|
|
152
|
+
|
|
153
|
+
- **GitHub Actions**: Use `act` for local testing when possible, or create a
|
|
154
|
+
draft PR to trigger the workflow.
|
|
155
|
+
- **Docker**: Build and run locally before pushing. Test with `docker build --no-cache`.
|
|
156
|
+
- **Scripts**: Run scripts in a clean environment to verify they work without
|
|
157
|
+
pre-existing state.
|
|
158
|
+
|
|
159
|
+
## Build & Run
|
|
160
|
+
|
|
161
|
+
```bash
|
|
162
|
+
# GitHub Actions
|
|
163
|
+
act -j build # Run locally with act
|
|
164
|
+
gh workflow run ci.yml # Trigger manually
|
|
165
|
+
|
|
166
|
+
# Docker
|
|
167
|
+
docker build -t app:test . # Build image
|
|
168
|
+
docker compose up -d # Start services
|
|
169
|
+
|
|
170
|
+
# Scripts
|
|
171
|
+
bash scripts/install.sh # Run install script
|
|
172
|
+
shellcheck scripts/*.sh # Lint shell scripts
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
## Common Pitfalls
|
|
176
|
+
|
|
177
|
+
### GitHub Actions caching
|
|
178
|
+
|
|
179
|
+
Cache keys must include the lockfile hash. Stale caches cause mysterious build
|
|
180
|
+
failures. Use `hashFiles('**/package-lock.json')` or equivalent.
|
|
181
|
+
|
|
182
|
+
### Docker build context size
|
|
183
|
+
|
|
184
|
+
A missing `.dockerignore` sends the entire repo (including `node_modules` and
|
|
185
|
+
`.git`) as build context, making builds slow. Check context size if builds are
|
|
186
|
+
unexpectedly slow.
|
|
187
|
+
|
|
188
|
+
### Script encoding on Windows
|
|
189
|
+
|
|
190
|
+
Git on Windows may convert LF to CRLF. Bash scripts with CRLF line endings fail
|
|
191
|
+
with cryptic errors (`$'\r': command not found`). Use `.gitattributes` to force
|
|
192
|
+
LF for shell scripts: `*.sh text eol=lf`.
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
# F# Conventions
|
|
2
|
+
|
|
3
|
+
> Applies to: `*.fs`, `*.fsi`, `*.fsx`, `*.fsproj`
|
|
4
|
+
> Last updated: 2026-03-18
|
|
5
|
+
|
|
6
|
+
## Architecture
|
|
7
|
+
|
|
8
|
+
F# projects use a layered module structure. Files compile top-to-bottom in the
|
|
9
|
+
order listed in the `.fsproj` file. This is enforced by the compiler -- there is
|
|
10
|
+
no way around it.
|
|
11
|
+
|
|
12
|
+
Typical layer ordering (top of .fsproj = compiled first):
|
|
13
|
+
|
|
14
|
+
```
|
|
15
|
+
Domain.Types.fs -- Discriminated unions, value objects, domain types
|
|
16
|
+
Domain.Events.fs -- Domain events
|
|
17
|
+
Domain.Services.fs -- Pure domain logic, no IO
|
|
18
|
+
Application.Commands.fs -- Use-case orchestration
|
|
19
|
+
Application.Queries.fs -- Read-side projections
|
|
20
|
+
Infrastructure.Db.fs -- Database access (side effects live here)
|
|
21
|
+
Api.Handlers.fs -- HTTP handlers (thin, delegate to Application)
|
|
22
|
+
Program.fs -- Composition root, DI wiring, app startup (always last)
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Each file can only reference types and functions defined in files listed above it.
|
|
26
|
+
|
|
27
|
+
## Naming Conventions
|
|
28
|
+
|
|
29
|
+
| Element | Convention | Example |
|
|
30
|
+
|---------|-----------|---------|
|
|
31
|
+
| Modules | PascalCase, noun | `module OrderProcessing` |
|
|
32
|
+
| Functions | camelCase, verb | `let calculateTotal items = ...` |
|
|
33
|
+
| Types (DUs, records) | PascalCase | `type OrderStatus = Pending \| Shipped` |
|
|
34
|
+
| DU cases | PascalCase | `Pending`, `Shipped`, `Cancelled` |
|
|
35
|
+
| Parameters | camelCase | `orderId`, `customerName` |
|
|
36
|
+
| Private helpers | prefix with underscore or nest in local scope | `let _validate x = ...` |
|
|
37
|
+
| Test functions | backtick names describing behavior | `` let `calculateTotal returns zero for empty list` () = `` |
|
|
38
|
+
|
|
39
|
+
## Patterns to Follow
|
|
40
|
+
|
|
41
|
+
### Discriminated unions for domain modeling
|
|
42
|
+
|
|
43
|
+
Prefer DUs over class hierarchies. Encode business rules in the type system so
|
|
44
|
+
invalid states are unrepresentable:
|
|
45
|
+
|
|
46
|
+
```fsharp
|
|
47
|
+
type Email = private Email of string
|
|
48
|
+
module Email =
|
|
49
|
+
let create (s: string) =
|
|
50
|
+
if s.Contains("@") then Ok (Email s)
|
|
51
|
+
else Error "Invalid email"
|
|
52
|
+
let value (Email s) = s
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### Railway-oriented programming
|
|
56
|
+
|
|
57
|
+
Use `Result<'T, 'E>` for operations that can fail. Chain with `Result.bind`
|
|
58
|
+
or a `result {}` computation expression:
|
|
59
|
+
|
|
60
|
+
```fsharp
|
|
61
|
+
let processOrder orderId =
|
|
62
|
+
validateOrder orderId
|
|
63
|
+
|> Result.bind checkInventory
|
|
64
|
+
|> Result.bind chargePayment
|
|
65
|
+
|> Result.bind shipOrder
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### Pipeline style
|
|
69
|
+
|
|
70
|
+
Prefer pipelines over nested function calls:
|
|
71
|
+
|
|
72
|
+
```fsharp
|
|
73
|
+
// Good
|
|
74
|
+
orders
|
|
75
|
+
|> List.filter (fun o -> o.Status = Active)
|
|
76
|
+
|> List.sortBy (fun o -> o.CreatedAt)
|
|
77
|
+
|> List.take 10
|
|
78
|
+
|
|
79
|
+
// Avoid
|
|
80
|
+
List.take 10 (List.sortBy (fun o -> o.CreatedAt) (List.filter (fun o -> o.Status = Active) orders))
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### Computation expressions
|
|
84
|
+
|
|
85
|
+
Use `async {}` for legacy async, `task {}` for .NET Task interop. Match whichever
|
|
86
|
+
the project already uses -- do not mix styles within a module.
|
|
87
|
+
|
|
88
|
+
```fsharp
|
|
89
|
+
let fetchOrder (id: OrderId) = task {
|
|
90
|
+
let! result = db.QueryAsync<Order>(id)
|
|
91
|
+
return result |> Option.ofObj
|
|
92
|
+
}
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
## Anti-Patterns to Avoid
|
|
96
|
+
|
|
97
|
+
### Mutable state in domain logic
|
|
98
|
+
|
|
99
|
+
Never use `mutable` in domain types or domain service functions. Mutation is
|
|
100
|
+
acceptable only in infrastructure code (e.g., filling a buffer) and must be
|
|
101
|
+
isolated behind a pure interface.
|
|
102
|
+
|
|
103
|
+
### Classes for domain models
|
|
104
|
+
|
|
105
|
+
Do not create classes with inheritance hierarchies for domain concepts. Use
|
|
106
|
+
discriminated unions and records. Classes are acceptable for infrastructure
|
|
107
|
+
(e.g., a repository implementation) but not for domain types.
|
|
108
|
+
|
|
109
|
+
### Ignoring Result errors
|
|
110
|
+
|
|
111
|
+
Never discard a `Result` value or convert it to an exception unless you are at
|
|
112
|
+
an application boundary (e.g., an HTTP handler returning 400/500):
|
|
113
|
+
|
|
114
|
+
```fsharp
|
|
115
|
+
// WRONG -- silently ignores failure
|
|
116
|
+
let _ = processOrder orderId
|
|
117
|
+
|
|
118
|
+
// RIGHT -- handle both cases
|
|
119
|
+
match processOrder orderId with
|
|
120
|
+
| Ok order -> handleSuccess order
|
|
121
|
+
| Error e -> handleFailure e
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
### Float/double for financial values
|
|
125
|
+
|
|
126
|
+
Never use `float` or `double` for money, prices, quantities, or financial
|
|
127
|
+
calculations. Use `decimal` exclusively. Floating-point rounding errors
|
|
128
|
+
compound silently in financial contexts.
|
|
129
|
+
|
|
130
|
+
```fsharp
|
|
131
|
+
// WRONG
|
|
132
|
+
let total: float = price * quantity
|
|
133
|
+
|
|
134
|
+
// RIGHT
|
|
135
|
+
let total: decimal = price * quantity
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
## Dependencies & Imports
|
|
139
|
+
|
|
140
|
+
- Open modules explicitly -- avoid `[<AutoOpen>]` on new modules unless the
|
|
141
|
+
project already uses it extensively.
|
|
142
|
+
- Prefer `open` at the top of the file, not inside functions.
|
|
143
|
+
- Group opens: FSharp.Core / System first, then third-party, then project modules.
|
|
144
|
+
- Do not add NuGet packages without confirming they are needed for the task.
|
|
145
|
+
|
|
146
|
+
## Testing
|
|
147
|
+
|
|
148
|
+
- **Framework**: Match whatever the project uses (Expecto, xUnit+FsUnit, NUnit).
|
|
149
|
+
- **Property-based tests**: Use FsCheck for functions with well-defined input/output
|
|
150
|
+
contracts (e.g., `serialize >> deserialize = id`).
|
|
151
|
+
- **Assertions**: Prefer Unquote (`test <@ expr @>`) or FsUnit matchers over
|
|
152
|
+
raw `Assert.Equal` when the project supports them.
|
|
153
|
+
- **Test file placement**: Mirror the source file in a parallel test project.
|
|
154
|
+
If source is `Domain.Orders.fs`, test is `Domain.OrdersTests.fs`.
|
|
155
|
+
- Run `dotnet test` and fix all failures before committing.
|
|
156
|
+
|
|
157
|
+
## Build & Run
|
|
158
|
+
|
|
159
|
+
```bash
|
|
160
|
+
dotnet build # Compile -- catches type errors and .fsproj ordering issues
|
|
161
|
+
dotnet test # Run all tests
|
|
162
|
+
dotnet run # Run the application (if applicable)
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
Always run `dotnet build` immediately after adding a new `.fs` file to catch
|
|
166
|
+
compilation order errors early. If you get "not defined" or "not recognized"
|
|
167
|
+
errors, the problem is almost certainly file ordering in `.fsproj`.
|
|
168
|
+
|
|
169
|
+
## Common Pitfalls
|
|
170
|
+
|
|
171
|
+
### .fsproj file ordering (CRITICAL)
|
|
172
|
+
|
|
173
|
+
F# compiles files strictly top-to-bottom as listed in the `.fsproj` `<Compile>`
|
|
174
|
+
items. When adding a new file:
|
|
175
|
+
|
|
176
|
+
1. **Read the `.fsproj` first** -- understand the current order.
|
|
177
|
+
2. **Insert the new `<Compile Include="..." />` at the correct position** --
|
|
178
|
+
after its dependencies, before its dependents.
|
|
179
|
+
3. **Run `dotnet build` immediately** -- do not write more code until the build passes.
|
|
180
|
+
|
|
181
|
+
If build fails with "The type 'X' is not defined" or "The namespace 'Y' is not
|
|
182
|
+
defined", check `.fsproj` order FIRST. This is the most common F# build failure.
|
|
183
|
+
|
|
184
|
+
### Module vs namespace
|
|
185
|
+
|
|
186
|
+
- `namespace Foo` -- groups types, cannot contain `let` bindings at top level.
|
|
187
|
+
- `module Foo` -- can contain `let` bindings, types, and nested modules.
|
|
188
|
+
|
|
189
|
+
Match whatever the project uses. Do not switch conventions within a project.
|
|
190
|
+
|
|
191
|
+
### Partial application surprises
|
|
192
|
+
|
|
193
|
+
Be explicit with parameter counts. A function `let add x y = x + y` partially
|
|
194
|
+
applied as `add 1` returns a function, not a value. This is correct behavior
|
|
195
|
+
but can surprise when passing to higher-order functions that expect a value.
|
|
196
|
+
|
|
197
|
+
### Equality semantics
|
|
198
|
+
|
|
199
|
+
F# records and DUs have structural equality by default. Classes do not. If you
|
|
200
|
+
wrap a class in a record, the record's equality will use reference equality for
|
|
201
|
+
that field. Use `[<CustomEquality; CustomComparison>]` if needed.
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
# Python Conventions
|
|
2
|
+
|
|
3
|
+
> Applies to: `*.py`, `pyproject.toml`, `requirements.txt`, `setup.cfg`
|
|
4
|
+
> Last updated: 2026-03-18
|
|
5
|
+
|
|
6
|
+
## Architecture
|
|
7
|
+
|
|
8
|
+
Python projects vary widely. Read `CLAUDE.md` and the project's entry point to
|
|
9
|
+
understand the framework and structure before making changes.
|
|
10
|
+
|
|
11
|
+
Common patterns:
|
|
12
|
+
|
|
13
|
+
```
|
|
14
|
+
src/
|
|
15
|
+
app/ -- Application code
|
|
16
|
+
models/ -- Database models / domain entities
|
|
17
|
+
services/ -- Business logic
|
|
18
|
+
api/ -- Route handlers / views
|
|
19
|
+
schemas/ -- Pydantic models / serializers
|
|
20
|
+
tests/ -- Test files (mirror src structure)
|
|
21
|
+
migrations/ -- Database migrations (Alembic / Django)
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Naming Conventions
|
|
25
|
+
|
|
26
|
+
| Element | Convention | Example |
|
|
27
|
+
|---------|-----------|---------|
|
|
28
|
+
| Modules | snake_case | `order_service.py`, `user_repository.py` |
|
|
29
|
+
| Classes | PascalCase | `OrderService`, `UserRepository` |
|
|
30
|
+
| Functions | snake_case | `get_order_by_id`, `calculate_total` |
|
|
31
|
+
| Constants | UPPER_SNAKE_CASE | `MAX_RETRIES`, `DEFAULT_TIMEOUT` |
|
|
32
|
+
| Private | underscore prefix | `_validate_input`, `_cache` |
|
|
33
|
+
| Type variables | single uppercase or descriptive | `T`, `ReturnType` |
|
|
34
|
+
|
|
35
|
+
## Patterns to Follow
|
|
36
|
+
|
|
37
|
+
### Type hints
|
|
38
|
+
|
|
39
|
+
Add type hints to all new function signatures. Use `typing` module types for
|
|
40
|
+
complex signatures:
|
|
41
|
+
|
|
42
|
+
```python
|
|
43
|
+
from typing import Optional, Sequence
|
|
44
|
+
|
|
45
|
+
def get_orders(user_id: int, limit: Optional[int] = None) -> Sequence[Order]:
|
|
46
|
+
...
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### Framework patterns
|
|
50
|
+
|
|
51
|
+
- **Django**: Use class-based views when they fit, function-based for simple endpoints.
|
|
52
|
+
Create migrations with `makemigrations` -- never edit migration files manually.
|
|
53
|
+
- **Flask**: Use blueprints for route organization. Register extensions in the
|
|
54
|
+
application factory.
|
|
55
|
+
- **FastAPI**: Use Pydantic models for request/response schemas. Use dependency
|
|
56
|
+
injection for services and database sessions.
|
|
57
|
+
|
|
58
|
+
### Async patterns
|
|
59
|
+
|
|
60
|
+
- Use `async/await` with asyncio when the framework supports it (FastAPI, aiohttp).
|
|
61
|
+
- Use `async with` for context managers that manage connections or sessions.
|
|
62
|
+
- Do not mix sync and async code without explicit bridges (`asyncio.to_thread`
|
|
63
|
+
for sync-in-async, `asyncio.run` for async-in-sync at boundaries only).
|
|
64
|
+
|
|
65
|
+
## Anti-Patterns to Avoid
|
|
66
|
+
|
|
67
|
+
### Mutable default arguments
|
|
68
|
+
|
|
69
|
+
```python
|
|
70
|
+
# WRONG -- shared mutable default
|
|
71
|
+
def add_item(item: str, items: list[str] = []) -> list[str]:
|
|
72
|
+
items.append(item)
|
|
73
|
+
return items
|
|
74
|
+
|
|
75
|
+
# RIGHT -- use None sentinel
|
|
76
|
+
def add_item(item: str, items: list[str] | None = None) -> list[str]:
|
|
77
|
+
if items is None:
|
|
78
|
+
items = []
|
|
79
|
+
items.append(item)
|
|
80
|
+
return items
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### Bare except clauses
|
|
84
|
+
|
|
85
|
+
Never catch `Exception` or use bare `except:` unless you are at an application
|
|
86
|
+
boundary (middleware, CLI entry point). Catch specific exception types.
|
|
87
|
+
|
|
88
|
+
### os.path for file operations
|
|
89
|
+
|
|
90
|
+
Prefer `pathlib.Path` over `os.path` for all file operations:
|
|
91
|
+
|
|
92
|
+
```python
|
|
93
|
+
# Prefer
|
|
94
|
+
from pathlib import Path
|
|
95
|
+
config_path = Path("config") / "settings.json"
|
|
96
|
+
|
|
97
|
+
# Avoid
|
|
98
|
+
import os
|
|
99
|
+
config_path = os.path.join("config", "settings.json")
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
## Dependencies & Imports
|
|
103
|
+
|
|
104
|
+
- Activate the project's virtualenv before running any Python commands.
|
|
105
|
+
- Import order: stdlib, third-party, local (match existing style or use isort).
|
|
106
|
+
- Do not add packages without confirming they are needed for the task.
|
|
107
|
+
- Check for the project's dependency tool: `requirements.txt`, `pyproject.toml`,
|
|
108
|
+
`poetry`, or `uv`.
|
|
109
|
+
|
|
110
|
+
## Testing
|
|
111
|
+
|
|
112
|
+
- **Framework**: pytest (fixtures, parametrize, conftest) or unittest -- match the project.
|
|
113
|
+
- **Naming**: `test_function_name_condition_expected_result` or the project's convention.
|
|
114
|
+
- **Fixtures**: Use `conftest.py` for shared fixtures. Prefer fixture factories
|
|
115
|
+
over complex fixture chains.
|
|
116
|
+
- **Parametrize**: Use `@pytest.mark.parametrize` for testing multiple inputs.
|
|
117
|
+
- **Mocking**: Use `unittest.mock.patch` or `monkeypatch` -- mock at boundaries only.
|
|
118
|
+
- Run `pytest` (or project-specific command) and fix all failures before committing.
|
|
119
|
+
|
|
120
|
+
## Build & Run
|
|
121
|
+
|
|
122
|
+
```bash
|
|
123
|
+
python -m pytest # Run tests
|
|
124
|
+
python -m mypy . # Type check (if project uses mypy)
|
|
125
|
+
python -m black --check . # Format check (if project uses black)
|
|
126
|
+
pip install -e . # Install in development mode
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
## Common Pitfalls
|
|
130
|
+
|
|
131
|
+
### Import errors from circular dependencies
|
|
132
|
+
|
|
133
|
+
Python resolves imports at module load time. Circular imports cause
|
|
134
|
+
`ImportError` or `AttributeError`. Fix by moving shared types to a separate
|
|
135
|
+
module or using `TYPE_CHECKING` guards for type-only imports.
|
|
136
|
+
|
|
137
|
+
### Django migration conflicts after rebase
|
|
138
|
+
|
|
139
|
+
If another branch added a migration, yours may conflict. Delete your migration
|
|
140
|
+
and recreate with `python manage.py makemigrations`. Never manually merge
|
|
141
|
+
migration files.
|
|
142
|
+
|
|
143
|
+
### Forgetting to close resources
|
|
144
|
+
|
|
145
|
+
Use context managers (`with` statements) for files, database connections, and
|
|
146
|
+
HTTP sessions. Do not rely on garbage collection to close resources.
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
# SQL & Database Conventions
|
|
2
|
+
|
|
3
|
+
> Applies to: `*.sql`, migration files, ORM model definitions, database access code
|
|
4
|
+
> Last updated: 2026-03-18
|
|
5
|
+
|
|
6
|
+
## Migration Discipline
|
|
7
|
+
|
|
8
|
+
- Always use the project's migration framework (Alembic, Django migrations,
|
|
9
|
+
EF Core migrations, Flyway, Knex, Prisma). Never run raw DDL against production.
|
|
10
|
+
- Migrations must be reversible -- provide both up and down operations.
|
|
11
|
+
- Never edit a migration that has already been applied to any environment.
|
|
12
|
+
- Test migrations against a fresh database to catch ordering issues.
|
|
13
|
+
- After rebase, if migration files conflict, delete yours and regenerate against
|
|
14
|
+
the rebased model state. Never manually merge migration files.
|
|
15
|
+
|
|
16
|
+
## Schema Conventions
|
|
17
|
+
|
|
18
|
+
### Naming
|
|
19
|
+
|
|
20
|
+
| Element | Convention | Example |
|
|
21
|
+
|---------|-----------|---------|
|
|
22
|
+
| Tables | snake_case, plural | `orders`, `pull_requests`, `usage_snapshots` |
|
|
23
|
+
| Columns | snake_case | `created_at`, `issue_number`, `ci_status` |
|
|
24
|
+
| Primary keys | `id` (integer auto-increment or UUID) | `id INTEGER PRIMARY KEY` |
|
|
25
|
+
| Foreign keys | `{referenced_table_singular}_id` | `team_id`, `project_id` |
|
|
26
|
+
| Indexes | `idx_{table}_{columns}` | `idx_events_team_id_created_at` |
|
|
27
|
+
| Boolean columns | `is_` or `has_` prefix | `is_active`, `has_hooks` |
|
|
28
|
+
| Timestamp columns | `_at` suffix | `created_at`, `merged_at`, `stopped_at` |
|
|
29
|
+
|
|
30
|
+
### Required columns
|
|
31
|
+
|
|
32
|
+
Every table should have:
|
|
33
|
+
- A primary key (`id`)
|
|
34
|
+
- `created_at` timestamp (set on insert, never updated)
|
|
35
|
+
- `updated_at` timestamp (set on insert, updated on every modification) -- optional
|
|
36
|
+
for append-only tables like events or logs
|
|
37
|
+
|
|
38
|
+
## Query Optimization
|
|
39
|
+
|
|
40
|
+
### Avoid N+1 queries
|
|
41
|
+
|
|
42
|
+
Never fetch a list of items and then query related data in a loop. Use JOINs,
|
|
43
|
+
subqueries, or the ORM's eager loading:
|
|
44
|
+
|
|
45
|
+
```sql
|
|
46
|
+
-- WRONG: N+1
|
|
47
|
+
SELECT * FROM teams;
|
|
48
|
+
-- then for each team:
|
|
49
|
+
SELECT * FROM events WHERE team_id = ?;
|
|
50
|
+
|
|
51
|
+
-- RIGHT: single query
|
|
52
|
+
SELECT t.*, e.* FROM teams t
|
|
53
|
+
LEFT JOIN events e ON e.team_id = t.id;
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### Index frequently filtered columns
|
|
57
|
+
|
|
58
|
+
Add indexes for columns that appear in WHERE, JOIN, ORDER BY, or GROUP BY clauses.
|
|
59
|
+
Foreign key columns should always be indexed.
|
|
60
|
+
|
|
61
|
+
### Use EXPLAIN
|
|
62
|
+
|
|
63
|
+
Before committing complex queries, run `EXPLAIN` (or `EXPLAIN ANALYZE`) to verify
|
|
64
|
+
the query plan uses indexes and does not perform full table scans on large tables.
|
|
65
|
+
|
|
66
|
+
## Transaction Safety
|
|
67
|
+
|
|
68
|
+
- Use explicit transactions for multi-step operations. Do not rely on auto-commit
|
|
69
|
+
for operations that must be atomic.
|
|
70
|
+
- Keep transactions short -- do not hold locks while performing external API calls
|
|
71
|
+
or expensive computations.
|
|
72
|
+
- Handle deadlocks gracefully -- retry with backoff when appropriate.
|
|
73
|
+
|
|
74
|
+
## ORM Patterns
|
|
75
|
+
|
|
76
|
+
### Lazy vs eager loading
|
|
77
|
+
|
|
78
|
+
- Use eager loading (JOINs / includes) for data you know you will need.
|
|
79
|
+
- Use lazy loading only when you rarely need the related data.
|
|
80
|
+
- Never traverse lazy relationships inside a loop -- this causes N+1.
|
|
81
|
+
|
|
82
|
+
### Connection pooling
|
|
83
|
+
|
|
84
|
+
- Use the ORM's built-in connection pool. Never open connections manually unless
|
|
85
|
+
the framework requires it.
|
|
86
|
+
- Set pool size appropriate for the workload. For SQLite, a pool size of 1 is
|
|
87
|
+
typical (single-writer constraint).
|
|
88
|
+
|
|
89
|
+
## SQLite-Specific
|
|
90
|
+
|
|
91
|
+
When working with SQLite (better-sqlite3 or similar):
|
|
92
|
+
|
|
93
|
+
- Enable WAL mode for concurrent reads: `PRAGMA journal_mode=WAL;`
|
|
94
|
+
- Use synchronous API -- better-sqlite3 is synchronous by design. Do not wrap
|
|
95
|
+
calls in async wrappers unnecessarily.
|
|
96
|
+
- SQLite has no native DATETIME type -- store timestamps as ISO 8601 strings
|
|
97
|
+
or Unix epoch integers. Be consistent within the project.
|
|
98
|
+
- Use `INSERT OR REPLACE` or `ON CONFLICT` for upserts.
|
|
99
|
+
- Foreign keys are off by default -- ensure `PRAGMA foreign_keys = ON` is set
|
|
100
|
+
at connection time.
|
|
101
|
+
|
|
102
|
+
## Common Pitfalls
|
|
103
|
+
|
|
104
|
+
### String interpolation in queries
|
|
105
|
+
|
|
106
|
+
Never interpolate user input into SQL strings. Always use parameterized queries:
|
|
107
|
+
|
|
108
|
+
```sql
|
|
109
|
+
-- WRONG (SQL injection risk)
|
|
110
|
+
SELECT * FROM users WHERE name = '${userName}';
|
|
111
|
+
|
|
112
|
+
-- RIGHT (parameterized)
|
|
113
|
+
SELECT * FROM users WHERE name = ?;
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
### Missing NOT NULL constraints
|
|
117
|
+
|
|
118
|
+
Default to `NOT NULL` for all columns unless NULL has a meaningful semantic
|
|
119
|
+
(e.g., `stopped_at` is NULL while a team is still running). Nullable columns
|
|
120
|
+
require NULL checks everywhere they are used.
|
|
121
|
+
|
|
122
|
+
### Large transactions in SQLite
|
|
123
|
+
|
|
124
|
+
SQLite uses a single-writer model. Long-running write transactions block all
|
|
125
|
+
other writers. Keep write transactions as short as possible.
|