@ryuenn3123/agentic-senior-core 1.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (78) hide show
  1. package/.agent-context/blueprints/api-nextjs.md +184 -0
  2. package/.agent-context/blueprints/aspnet-api.md +247 -0
  3. package/.agent-context/blueprints/ci-github-actions.md +226 -0
  4. package/.agent-context/blueprints/ci-gitlab.md +200 -0
  5. package/.agent-context/blueprints/fastapi-service.md +210 -0
  6. package/.agent-context/blueprints/go-service.md +217 -0
  7. package/.agent-context/blueprints/graphql-grpc-api.md +51 -0
  8. package/.agent-context/blueprints/infrastructure-as-code.md +62 -0
  9. package/.agent-context/blueprints/kubernetes-manifests.md +76 -0
  10. package/.agent-context/blueprints/laravel-api.md +223 -0
  11. package/.agent-context/blueprints/nestjs-logic.md +247 -0
  12. package/.agent-context/blueprints/observability.md +227 -0
  13. package/.agent-context/blueprints/spring-boot-api.md +218 -0
  14. package/.agent-context/policies/llm-judge-threshold.json +20 -0
  15. package/.agent-context/profiles/platform.md +13 -0
  16. package/.agent-context/profiles/regulated.md +13 -0
  17. package/.agent-context/profiles/startup.md +13 -0
  18. package/.agent-context/prompts/init-project.md +86 -0
  19. package/.agent-context/prompts/refactor.md +45 -0
  20. package/.agent-context/prompts/review-code.md +47 -0
  21. package/.agent-context/review-checklists/architecture-review.md +70 -0
  22. package/.agent-context/review-checklists/frontend-usability.md +33 -0
  23. package/.agent-context/review-checklists/performance-audit.md +65 -0
  24. package/.agent-context/review-checklists/pr-checklist.md +97 -0
  25. package/.agent-context/review-checklists/release-operations.md +29 -0
  26. package/.agent-context/review-checklists/security-audit.md +113 -0
  27. package/.agent-context/rules/api-docs.md +186 -0
  28. package/.agent-context/rules/architecture.md +198 -0
  29. package/.agent-context/rules/database-design.md +202 -0
  30. package/.agent-context/rules/efficiency-vs-hype.md +143 -0
  31. package/.agent-context/rules/error-handling.md +234 -0
  32. package/.agent-context/rules/event-driven.md +226 -0
  33. package/.agent-context/rules/frontend-architecture.md +66 -0
  34. package/.agent-context/rules/git-workflow.md +200 -0
  35. package/.agent-context/rules/microservices.md +174 -0
  36. package/.agent-context/rules/naming-conv.md +141 -0
  37. package/.agent-context/rules/performance.md +168 -0
  38. package/.agent-context/rules/realtime.md +47 -0
  39. package/.agent-context/rules/security.md +195 -0
  40. package/.agent-context/rules/testing.md +178 -0
  41. package/.agent-context/stacks/csharp.md +149 -0
  42. package/.agent-context/stacks/go.md +181 -0
  43. package/.agent-context/stacks/java.md +135 -0
  44. package/.agent-context/stacks/php.md +178 -0
  45. package/.agent-context/stacks/python.md +153 -0
  46. package/.agent-context/stacks/ruby.md +80 -0
  47. package/.agent-context/stacks/rust.md +86 -0
  48. package/.agent-context/stacks/typescript.md +317 -0
  49. package/.agent-context/state/architecture-map.md +25 -0
  50. package/.agent-context/state/dependency-map.md +32 -0
  51. package/.agent-override.md +36 -0
  52. package/.agents/workflows/init-project.md +29 -0
  53. package/.agents/workflows/refactor.md +29 -0
  54. package/.agents/workflows/review-code.md +29 -0
  55. package/.cursorrules +140 -0
  56. package/.gemini/instructions.md +97 -0
  57. package/.github/ISSUE_TEMPLATE/v1.7-frontend-work-item.yml +54 -0
  58. package/.github/copilot-instructions.md +104 -0
  59. package/.github/workflows/benchmark-detection.yml +38 -0
  60. package/.github/workflows/frontend-usability-gate.yml +36 -0
  61. package/.github/workflows/release-gate.yml +32 -0
  62. package/.github/workflows/sbom-compliance.yml +32 -0
  63. package/.windsurfrules +106 -0
  64. package/AGENTS.md +131 -0
  65. package/CONTRIBUTING.md +136 -0
  66. package/LICENSE +21 -0
  67. package/README.md +239 -0
  68. package/bin/agentic-senior-core.js +1147 -0
  69. package/mcp.json +29 -0
  70. package/package.json +50 -0
  71. package/scripts/detection-benchmark.mjs +138 -0
  72. package/scripts/frontend-usability-audit.mjs +87 -0
  73. package/scripts/generate-sbom.mjs +61 -0
  74. package/scripts/init-project.ps1 +105 -0
  75. package/scripts/init-project.sh +131 -0
  76. package/scripts/llm-judge.mjs +664 -0
  77. package/scripts/release-gate.mjs +116 -0
  78. package/scripts/validate.mjs +554 -0
@@ -0,0 +1,200 @@
1
+ # Blueprint: GitLab CI/CD Pipeline
2
+
3
+ > Same principles as GitHub Actions, adapted for GitLab's pipeline model.
4
+
5
+ ## Tech Stack
6
+
7
+ | Layer | Tool |
8
+ |-------|------|
9
+ | CI/CD | GitLab CI/CD |
10
+ | Config | `.gitlab-ci.yml` |
11
+ | Runners | Shared runners or dedicated (Docker executor) |
12
+ | Registry | GitLab Container Registry |
13
+ | Caching | GitLab CI cache (per-branch, per-job) |
14
+
15
+ ## Pipeline Architecture
16
+
17
+ ```yaml
18
+ stages:
19
+ - validate # Lint + type check
20
+ - test # Unit + integration tests
21
+ - security # Dependency audit + SAST
22
+ - build # Compile, bundle, containerize
23
+ - judge # LLM-as-a-Judge checklist gate
24
+ - deploy # Staging → Production
25
+ ```
26
+
27
+ ## Pipeline Template (`.gitlab-ci.yml`)
28
+
29
+ ```yaml
30
+ default:
31
+ image: node:22-alpine
32
+ cache:
33
+ key:
34
+ files: [package-lock.json]
35
+ paths: [node_modules/]
36
+ policy: pull-push
37
+
38
+ stages:
39
+ - validate
40
+ - test
41
+ - security
42
+ - build
43
+ - judge
44
+ - deploy
45
+
46
+ # ─── VALIDATE ───────────────────────────────────────────
47
+ lint:
48
+ stage: validate
49
+ script:
50
+ - npm ci --prefer-offline
51
+ - npm run lint
52
+ - npm run type-check
53
+ rules:
54
+ - if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
55
+ - if: '$CI_COMMIT_BRANCH == "main"'
56
+
57
+ # ─── TEST ───────────────────────────────────────────────
58
+ test:unit:
59
+ stage: test
60
+ script:
61
+ - npm ci --prefer-offline
62
+ - npm run test -- --coverage
63
+ coverage: '/All files\s*\|\s*(\d+\.?\d*)\s*\|/'
64
+ artifacts:
65
+ reports:
66
+ coverage_report:
67
+ coverage_format: cobertura
68
+ path: coverage/cobertura-coverage.xml
69
+ paths: [coverage/]
70
+ expire_in: 7 days
71
+
72
+ test:integration:
73
+ stage: test
74
+ services:
75
+ - postgres:16-alpine
76
+ variables:
77
+ POSTGRES_DB: test_db
78
+ POSTGRES_USER: test_user
79
+ POSTGRES_PASSWORD: test_pass
80
+ DATABASE_URL: "postgresql://test_user:test_pass@postgres:5432/test_db"
81
+ script:
82
+ - npm ci --prefer-offline
83
+ - npm run test:integration
84
+
85
+ # ─── SECURITY ───────────────────────────────────────────
86
+ audit:
87
+ stage: security
88
+ script:
89
+ - npm audit --audit-level=high
90
+ allow_failure: false
91
+
92
+ sast:
93
+ stage: security
94
+ # GitLab's built-in SAST template
95
+ include:
96
+ - template: Security/SAST.gitlab-ci.yml
97
+
98
+ # ─── BUILD ──────────────────────────────────────────────
99
+ build:
100
+ stage: build
101
+ script:
102
+ - npm ci --prefer-offline
103
+ - npm run build
104
+ artifacts:
105
+ paths: [dist/]
106
+ expire_in: 1 day
107
+ rules:
108
+ - if: '$CI_COMMIT_BRANCH == "main"'
109
+
110
+ # ─── LLM JUDGE ──────────────────────────────────────────
111
+ llm:judge:
112
+ stage: judge
113
+ image: node:22-alpine
114
+ variables:
115
+ OPENAI_API_KEY: $OPENAI_API_KEY
116
+ ANTHROPIC_API_KEY: $ANTHROPIC_API_KEY
117
+ GEMINI_API_KEY: $GEMINI_API_KEY
118
+ # CI_MERGE_REQUEST_DIFF_BASE_SHA and CI_COMMIT_SHA are set automatically
119
+ # by GitLab for merge request pipelines — no manual configuration needed.
120
+ before_script:
121
+ - git fetch --unshallow || true # Ensure full history for git diff
122
+ script:
123
+ - node scripts/llm-judge.mjs
124
+ artifacts:
125
+ when: always
126
+ paths:
127
+ - .agent-context/state/llm-judge-report.json
128
+ expire_in: 7 days
129
+ rules:
130
+ - if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
131
+
132
+ # ─── DEPLOY ─────────────────────────────────────────────
133
+ deploy:staging:
134
+ stage: deploy
135
+ environment:
136
+ name: staging
137
+ url: https://staging.example.com
138
+ script:
139
+ - echo "Deploy to staging"
140
+ # Add your deployment commands
141
+ rules:
142
+ - if: '$CI_COMMIT_BRANCH == "main"'
143
+
144
+ deploy:production:
145
+ stage: deploy
146
+ environment:
147
+ name: production
148
+ url: https://example.com
149
+ script:
150
+ - echo "Deploy to production"
151
+ # Add your deployment commands
152
+ rules:
153
+ - if: '$CI_COMMIT_BRANCH == "main"'
154
+ when: manual # Require manual approval
155
+ needs: [deploy:staging]
156
+ ```
157
+
158
+ ## Key Differences from GitHub Actions
159
+
160
+ | Concern | GitHub Actions | GitLab CI |
161
+ |---------|---------------|-----------|
162
+ | Config file | `.github/workflows/*.yml` | `.gitlab-ci.yml` (single file) |
163
+ | Jobs linkage | `needs:` | `stages:` (sequential) + `needs:` (DAG) |
164
+ | Secrets | GitHub Secrets | CI/CD Variables (masked + protected) |
165
+ | Caching | `actions/cache` | Built-in `cache:` directive |
166
+ | Services | Docker Compose / service containers | `services:` directive |
167
+ | Environments | Environment protection rules | Environment + `when: manual` |
168
+ | Includes | Reusable workflows | `include:` with `template:` |
169
+
170
+ ## Security Rules
171
+
172
+ 1. **Protected variables** — mark secrets as Protected + Masked
173
+ 2. **Protected branches** — only deploy from protected branches
174
+ 3. **Include GitLab SAST/DAST** templates for automated scanning
175
+ 4. **Limit runner access** — use tags to route jobs to appropriate runners
176
+ 5. **Artifact expiration** — set `expire_in` on all artifacts
177
+ 6. **Limit LLM input scope** — send only merge diff + checklist context
178
+
179
+ ## Scaffolding Checklist
180
+
181
+ - [ ] Create `.gitlab-ci.yml` with validate, test, security, build, deploy stages
182
+ - [ ] Configure CI/CD variables for secrets (masked + protected)
183
+ - [ ] Set up caching for package manager lockfile
184
+ - [ ] Add coverage reporting with Cobertura format
185
+ - [ ] Configure environments (staging, production) with manual approval
186
+ - [ ] Include SAST template for security scanning
187
+ - [ ] Set up merge request pipelines with `rules:`
188
+ - [ ] Configure branch protection rules
189
+ - [ ] Add `timeout` to long-running jobs
190
+ - [ ] Use `needs:` for DAG optimization where possible
191
+ - [ ] Add `llm:judge` stage that enforces `pr-checklist.md`
192
+
193
+ ## LLM Judge Annotation Contract
194
+
195
+ For MR annotations and dashboards, parse either:
196
+
197
+ - Log line: `JSON_REPORT: { ... }`
198
+ - Artifact: `.agent-context/state/llm-judge-report.json`
199
+
200
+ Severity values are normalized by the judge to: `critical`, `high`, `medium`, `low`.
@@ -0,0 +1,210 @@
1
+ # Blueprint: FastAPI Service
2
+
3
+ > Python backend API service using FastAPI, Pydantic v2, SQLAlchemy 2.0, and Alembic.
4
+
5
+ ## Tech Stack
6
+
7
+ | Layer | Technology |
8
+ |-------|-----------|
9
+ | Framework | FastAPI |
10
+ | Validation | Pydantic v2 |
11
+ | ORM | SQLAlchemy 2.0 (async) |
12
+ | Migration | Alembic |
13
+ | Testing | pytest + pytest-asyncio + httpx |
14
+ | Logging | structlog |
15
+ | Linting | ruff (lint + format) |
16
+ | Type checking | pyright (strict) |
17
+ | Env config | pydantic-settings |
18
+
19
+ ---
20
+
21
+ ## Project Structure
22
+
23
+ ```
24
+ project-name/
25
+ ├── src/
26
+ │ ├── __init__.py
27
+ │ ├── main.py
28
+ │ ├── config.py
29
+ │ │
30
+ │ ├── modules/
31
+ │ │ └── user/
32
+ │ │ ├── __init__.py
33
+ │ │ ├── router.py
34
+ │ │ ├── service.py
35
+ │ │ ├── repository.py
36
+ │ │ ├── schemas.py
37
+ │ │ ├── models.py
38
+ │ │ └── exceptions.py
39
+ │ │
40
+ │ └── shared/
41
+ │ ├── __init__.py
42
+ │ ├── database.py
43
+ │ ├── errors.py
44
+ │ ├── logger.py
45
+ │ └── middleware.py
46
+
47
+ ├── tests/
48
+ │ ├── conftest.py
49
+ │ ├── factories.py
50
+ │ └── modules/
51
+ │ └── user/
52
+ │ └── test_user_service.py
53
+
54
+ ├── alembic/
55
+ │ ├── env.py
56
+ │ └── versions/
57
+
58
+ ├── pyproject.toml
59
+ ├── alembic.ini
60
+ ├── .env.example
61
+ ├── Dockerfile
62
+ └── Makefile
63
+ ```
64
+
65
+ ---
66
+
67
+ ## File Patterns
68
+
69
+ ### config.py — Environment Validation
70
+ ```python
71
+ from pydantic_settings import BaseSettings
72
+ from pydantic import Field
73
+
74
+ class Settings(BaseSettings):
75
+ database_url: str = Field(
76
+ description="PostgreSQL connection string"
77
+ )
78
+ jwt_secret: str = Field(min_length=32)
79
+ app_env: str = Field(default="development", pattern="^(development|staging|production)$")
80
+ cors_origins: list[str] = Field(default=["http://localhost:3000"])
81
+ log_level: str = Field(default="INFO")
82
+
83
+ model_config = {"env_file": ".env", "env_file_encoding": "utf-8"}
84
+
85
+ settings = Settings()
86
+ ```
87
+
88
+ ### main.py — Application Entry
89
+ ```python
90
+ from contextlib import asynccontextmanager
91
+ from fastapi import FastAPI
92
+ from src.config import settings
93
+ from src.shared.middleware import setup_middleware
94
+ from src.shared.logger import setup_logging
95
+ from src.modules.user.router import router as user_router
96
+
97
+ @asynccontextmanager
98
+ async def lifespan(app: FastAPI):
99
+ setup_logging(settings.log_level)
100
+ # startup: connect DB, warm caches
101
+ yield
102
+ # shutdown: close connections
103
+
104
+ app = FastAPI(
105
+ title="Service Name",
106
+ version="1.0.0",
107
+ lifespan=lifespan,
108
+ )
109
+
110
+ setup_middleware(app, settings)
111
+ app.include_router(user_router, prefix="/api/v1")
112
+
113
+ @app.get("/health")
114
+ async def health() -> dict[str, str]:
115
+ return {"status": "ok"}
116
+ ```
117
+
118
+ ### router.py — Transport Layer
119
+ ```python
120
+ from fastapi import APIRouter, HTTPException, status
121
+ from src.modules.user.schemas import CreateUserRequest, UserResponse
122
+ from src.modules.user.service import UserService
123
+
124
+ router = APIRouter(prefix="/users", tags=["users"])
125
+
126
+ @router.post("/", response_model=UserResponse, status_code=status.HTTP_201_CREATED)
127
+ async def create_user(request: CreateUserRequest, service: UserService) -> UserResponse:
128
+ return await service.create(request)
129
+ ```
130
+
131
+ ### schemas.py — Validation Boundaries
132
+ ```python
133
+ from pydantic import BaseModel, EmailStr, Field
134
+ from datetime import datetime
135
+ from uuid import UUID
136
+
137
+ class CreateUserRequest(BaseModel):
138
+ name: str = Field(min_length=1, max_length=100)
139
+ email: EmailStr
140
+ age: int = Field(ge=13, le=150)
141
+
142
+ model_config = {"strict": True}
143
+
144
+ class UserResponse(BaseModel):
145
+ id: UUID
146
+ name: str
147
+ email: str
148
+ created_at: datetime
149
+ ```
150
+
151
+ ### service.py — Business Logic
152
+ ```python
153
+ import structlog
154
+ from src.modules.user.schemas import CreateUserRequest, UserResponse
155
+ from src.modules.user.repository import UserRepository
156
+ from src.modules.user.exceptions import UserAlreadyExistsError
157
+
158
+ logger = structlog.get_logger()
159
+
160
+ class UserService:
161
+ def __init__(self, repo: UserRepository) -> None:
162
+ self._repo = repo
163
+
164
+ async def create(self, request: CreateUserRequest) -> UserResponse:
165
+ if await self._repo.exists_by_email(request.email):
166
+ raise UserAlreadyExistsError(request.email)
167
+
168
+ user = await self._repo.insert(request)
169
+ logger.info("user_created", user_id=str(user.id), email=user.email)
170
+ return UserResponse.model_validate(user)
171
+ ```
172
+
173
+ ### shared/errors.py — Error Foundation
174
+ ```python
175
+ from fastapi import Request, HTTPException
176
+ from fastapi.responses import JSONResponse
177
+
178
+ class AppError(Exception):
179
+ def __init__(self, code: str, message: str, status_code: int = 400) -> None:
180
+ self.code = code
181
+ self.message = message
182
+ self.status_code = status_code
183
+
184
+ async def app_error_handler(request: Request, exc: AppError) -> JSONResponse:
185
+ return JSONResponse(
186
+ status_code=exc.status_code,
187
+ content={
188
+ "error": {
189
+ "code": exc.code,
190
+ "message": exc.message,
191
+ }
192
+ },
193
+ )
194
+ ```
195
+
196
+ ---
197
+
198
+ ## Scaffolding Checklist
199
+
200
+ - [ ] Create project with `pyproject.toml` (ruff, pyright, pytest config)
201
+ - [ ] Set up `src/config.py` with Pydantic Settings
202
+ - [ ] Set up `src/shared/database.py` with async SQLAlchemy engine
203
+ - [ ] Set up `src/shared/errors.py` with base error class + handler
204
+ - [ ] Set up `src/shared/logger.py` with structlog
205
+ - [ ] Create first module following the pattern above
206
+ - [ ] Set up Alembic with initial migration
207
+ - [ ] Create `Makefile` with dev, test, lint, format commands
208
+ - [ ] Create `.env.example`
209
+ - [ ] Create `Dockerfile` (multi-stage)
210
+ - [ ] Run `ruff check` and `pyright` — zero errors
@@ -0,0 +1,217 @@
1
+ # Blueprint: Go HTTP Service (chi / stdlib)
2
+
3
+ > Go is not Java. Don't bring Spring patterns.
4
+ > Embrace simplicity, explicit errors, and small interfaces.
5
+
6
+ ## Tech Stack
7
+
8
+ | Layer | Choice | Why |
9
+ |-------|--------|-----|
10
+ | Language | Go 1.23+ | Latest stable |
11
+ | Router | `net/http` (Go 1.22+ enhanced) + `chi` | stdlib-compatible, composable middleware |
12
+ | Validation | Custom + `go-playground/validator` | Type-safe, struct tags |
13
+ | Database | `database/sql` + `pgx` + `sqlc` | Type-safe queries from SQL |
14
+ | Migrations | `goose` or `golang-migrate` | Versioned, reversible |
15
+ | Logging | `log/slog` (stdlib) | Structured, zero-dependency |
16
+ | Config | `envconfig` or `koanf` | Env-first, typed config |
17
+ | Testing | stdlib `testing` + `testify` (assertions) | Community standard |
18
+ | OpenAPI | `swaggo/swag` or `ogen` | Generate spec from annotations |
19
+ | Telemetry | OpenTelemetry Go SDK | Vendor-neutral observability |
20
+
21
+ ## Project Structure
22
+
23
+ ```
24
+ project-root/
25
+ ├── cmd/
26
+ │ └── api/
27
+ │ └── main.go # Entry point: wire dependencies, start server
28
+
29
+ ├── internal/ # Private application code
30
+ │ ├── config/
31
+ │ │ └── config.go # Env-based configuration struct
32
+ │ │
33
+ │ ├── domain/ # Core business types (no external deps)
34
+ │ │ ├── user.go # Entity: User
35
+ │ │ ├── order.go # Entity: Order
36
+ │ │ └── errors.go # Domain-specific error types
37
+ │ │
38
+ │ ├── service/ # Business logic (depends on domain + ports)
39
+ │ │ ├── user_service.go
40
+ │ │ └── order_service.go
41
+ │ │
42
+ │ ├── repository/ # Data access (implements domain ports)
43
+ │ │ ├── postgres/
44
+ │ │ │ ├── user_repo.go
45
+ │ │ │ └── queries/ # sqlc generated or raw SQL
46
+ │ │ └── repository.go # Interface definitions
47
+ │ │
48
+ │ ├── handler/ # HTTP handlers (chi routes)
49
+ │ │ ├── user_handler.go
50
+ │ │ ├── order_handler.go
51
+ │ │ ├── middleware.go # Custom middleware
52
+ │ │ └── response.go # Standardized JSON response helpers
53
+ │ │
54
+ │ └── platform/ # Infrastructure adapters
55
+ │ ├── database.go # Database connection setup
56
+ │ ├── logger.go # slog configuration
57
+ │ └── telemetry.go # OpenTelemetry setup
58
+
59
+ ├── migrations/
60
+ │ ├── 001_create_users.sql
61
+ │ └── 002_create_orders.sql
62
+
63
+ ├── go.mod
64
+ ├── go.sum
65
+ ├── Makefile # Standard commands
66
+ ├── Dockerfile
67
+ └── .env.example
68
+ ```
69
+
70
+ ## Key Patterns
71
+
72
+ ### Dependency Injection via Constructors
73
+
74
+ ```go
75
+ // internal/service/user_service.go
76
+ type UserService struct {
77
+ repo UserRepository
78
+ logger *slog.Logger
79
+ }
80
+
81
+ func NewUserService(repo UserRepository, logger *slog.Logger) *UserService {
82
+ return &UserService{repo: repo, logger: logger}
83
+ }
84
+ ```
85
+
86
+ ### Interfaces in the Consumer Package
87
+
88
+ ```go
89
+ // internal/service/user_service.go
90
+ // Interface defined HERE (consumer), not in the repository package
91
+ type UserRepository interface {
92
+ FindByID(ctx context.Context, id string) (*domain.User, error)
93
+ Create(ctx context.Context, user *domain.User) error
94
+ }
95
+ ```
96
+
97
+ ### Error Handling: Explicit, Always
98
+
99
+ ```go
100
+ // ❌ BANNED in Go
101
+ result, _ := db.Query(ctx, sql) // Ignoring error
102
+
103
+ // ✅ REQUIRED
104
+ result, err := db.Query(ctx, sql)
105
+ if err != nil {
106
+ return fmt.Errorf("query users: %w", err)
107
+ }
108
+ ```
109
+
110
+ ### HTTP Handler Pattern
111
+
112
+ ```go
113
+ // internal/handler/user_handler.go
114
+ func (h *UserHandler) Create(w http.ResponseWriter, r *http.Request) {
115
+ var req CreateUserRequest
116
+ if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
117
+ respondError(w, http.StatusBadRequest, "invalid request body")
118
+ return
119
+ }
120
+
121
+ if err := req.Validate(); err != nil {
122
+ respondError(w, http.StatusBadRequest, err.Error())
123
+ return
124
+ }
125
+
126
+ user, err := h.service.Create(r.Context(), req.ToDomain())
127
+ if err != nil {
128
+ h.logger.ErrorContext(r.Context(), "create user failed",
129
+ slog.String("error", err.Error()),
130
+ )
131
+ respondError(w, http.StatusInternalServerError, "internal error")
132
+ return
133
+ }
134
+
135
+ respondJSON(w, http.StatusCreated, user)
136
+ }
137
+ ```
138
+
139
+ ### Router Setup with chi
140
+
141
+ ```go
142
+ // cmd/api/main.go
143
+ func setupRouter(h *handler.Handler) http.Handler {
144
+ r := chi.NewRouter()
145
+
146
+ // Global middleware
147
+ r.Use(middleware.RequestID)
148
+ r.Use(middleware.RealIP)
149
+ r.Use(middleware.Logger)
150
+ r.Use(middleware.Recoverer)
151
+ r.Use(middleware.Timeout(30 * time.Second))
152
+
153
+ // API routes
154
+ r.Route("/api/v1", func(r chi.Router) {
155
+ r.Route("/users", func(r chi.Router) {
156
+ r.Get("/", h.User.List)
157
+ r.Post("/", h.User.Create)
158
+ r.Route("/{userId}", func(r chi.Router) {
159
+ r.Get("/", h.User.GetByID)
160
+ r.Put("/", h.User.Update)
161
+ r.Delete("/", h.User.Delete)
162
+ })
163
+ })
164
+ })
165
+
166
+ // Health check
167
+ r.Get("/health", func(w http.ResponseWriter, r *http.Request) {
168
+ w.WriteHeader(http.StatusOK)
169
+ w.Write([]byte(`{"status":"ok"}`))
170
+ })
171
+
172
+ return r
173
+ }
174
+ ```
175
+
176
+ ## Makefile Commands
177
+
178
+ ```makefile
179
+ .PHONY: run build test lint migrate
180
+
181
+ run:
182
+ go run ./cmd/api
183
+
184
+ build:
185
+ CGO_ENABLED=0 go build -o bin/api ./cmd/api
186
+
187
+ test:
188
+ go test ./... -v -race -coverprofile=coverage.out
189
+
190
+ lint:
191
+ golangci-lint run ./...
192
+
193
+ migrate-up:
194
+ goose -dir migrations postgres "$(DATABASE_URL)" up
195
+
196
+ migrate-down:
197
+ goose -dir migrations postgres "$(DATABASE_URL)" down
198
+
199
+ sqlc:
200
+ sqlc generate
201
+ ```
202
+
203
+ ## Scaffolding Checklist
204
+
205
+ - [ ] `go mod init` with proper module path
206
+ - [ ] Create `cmd/api/main.go` entry point
207
+ - [ ] Set up `internal/` structure (config, domain, service, repository, handler)
208
+ - [ ] Configure `chi` router with standard middleware stack
209
+ - [ ] Set up `slog` structured logging
210
+ - [ ] Configure database connection with `pgx` + connection pool
211
+ - [ ] Set up `sqlc` for type-safe SQL queries
212
+ - [ ] Create migration files with `goose`
213
+ - [ ] Write standardized JSON response helpers
214
+ - [ ] Add `Makefile` with run, build, test, lint, migrate commands
215
+ - [ ] Create `Dockerfile` (multi-stage, scratch base)
216
+ - [ ] Create `.env.example` with all required env vars
217
+ - [ ] Run `golangci-lint` with strict config
@@ -0,0 +1,51 @@
1
+ # GraphQL & gRPC API Blueprint
2
+
3
+ > REST is for resources; GraphQL is for queries; gRPC is for actions. Choose the right tool, then design the contract before writing the code.
4
+
5
+ ## 1. Schema-First Design (Mandatory)
6
+ Never rely on code-first generation for your public contracts.
7
+ - **Rule:** The schema (`.graphql` or `.proto`) is the single source of truth.
8
+ - **Why:** Schema-first forces API design discussions to happen independently of the implementation language. It allows frontend and backend teams to work in parallel.
9
+ - **Validation:** Your CI/CD MUST include a schema linter (e.g., `graphql-schema-linter` or `buf lint`).
10
+
11
+ ## 2. GraphQL Standards
12
+
13
+ ### A. The N+1 Problem (Dataloaders)
14
+ - **Rule:** EVERY resolver that fetches a relation MUST use a DataLoader (or equivalent batching/caching mechanism).
15
+ - **BANNED:** Resolving `author` inside a `posts` query by doing `SELECT * FROM users WHERE id = X` in a loop.
16
+ - **REQUIRED:** Batching IDs to fetch them all in one query `SELECT * FROM users WHERE id IN (X, Y, Z)`.
17
+
18
+ ### B. Pagination
19
+ - **Rule:** Use cursor-based pagination (Relay Connection Specification) for all list endpoints.
20
+ - **BANNED:** Offset-based pagination (`limit`, `offset`) for large datasets (it becomes exponentially slower).
21
+ - **REQUIRED Structure:**
22
+ ```graphql
23
+ type UserConnection {
24
+ edges: [UserEdge!]!
25
+ pageInfo: PageInfo!
26
+ }
27
+ ```
28
+
29
+ ### C. Mutations
30
+ - **Rule:** Mutations MUST represent business actions, not CRUD database operations.
31
+ - **BANNED:** `updateUser`, `createPost`.
32
+ - **REQUIRED:** `suspendUserAccount`, `publishBlogPost`. Every mutation must take a single `input` object and return a `payload` object.
33
+
34
+ ## 3. gRPC & Protobuf Standards
35
+
36
+ ### A. Backwards Compatibility
37
+ - **Rule:** Never rename a field, never change a field's type, and never reuse a field tag number.
38
+ - **BANNED:** `int32 count = 1;` -> `int64 count = 1;`
39
+ - **REQUIRED:** If the type must change, deprecate the old field and create a new one:
40
+ ```protobuf
41
+ int32 old_count = 1 [deprecated = true];
42
+ int64 count = 2;
43
+ ```
44
+
45
+ ### B. Structure & Organization
46
+ - **Rule:** Group proto files by domain/package (`package ecommerce.orders.v1;`).
47
+ - **Required Files:** Separate messages (`messages.proto`) from services (`service.proto`) if the domain is large.
48
+
49
+ ### C. Error Handling
50
+ gRPC uses standard status codes.
51
+ - **Rule:** Map business errors to explicit `google.rpc.Status` codes. Do not return `UNKNOWN` or `INTERNAL` for business logic failures (like "Insufficient Funds"). Use `FAILED_PRECONDITION` or custom detail payloads.