@quanticjs/create-app 0.1.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.
Files changed (136) hide show
  1. package/dist/deps.d.ts +4 -0
  2. package/dist/deps.js +97 -0
  3. package/dist/deps.js.map +1 -0
  4. package/dist/generators/backend.d.ts +2 -0
  5. package/dist/generators/backend.js +20 -0
  6. package/dist/generators/backend.js.map +1 -0
  7. package/dist/generators/bff.d.ts +2 -0
  8. package/dist/generators/bff.js +9 -0
  9. package/dist/generators/bff.js.map +1 -0
  10. package/dist/generators/claude.d.ts +2 -0
  11. package/dist/generators/claude.js +45 -0
  12. package/dist/generators/claude.js.map +1 -0
  13. package/dist/generators/docker.d.ts +2 -0
  14. package/dist/generators/docker.js +10 -0
  15. package/dist/generators/docker.js.map +1 -0
  16. package/dist/generators/e2e.d.ts +2 -0
  17. package/dist/generators/e2e.js +8 -0
  18. package/dist/generators/e2e.js.map +1 -0
  19. package/dist/generators/frontend.d.ts +2 -0
  20. package/dist/generators/frontend.js +28 -0
  21. package/dist/generators/frontend.js.map +1 -0
  22. package/dist/generators/module.d.ts +2 -0
  23. package/dist/generators/module.js +35 -0
  24. package/dist/generators/module.js.map +1 -0
  25. package/dist/generators/root.d.ts +2 -0
  26. package/dist/generators/root.js +7 -0
  27. package/dist/generators/root.js.map +1 -0
  28. package/dist/generators/scripts.d.ts +2 -0
  29. package/dist/generators/scripts.js +10 -0
  30. package/dist/generators/scripts.js.map +1 -0
  31. package/dist/index.d.ts +2 -0
  32. package/dist/index.js +40 -0
  33. package/dist/index.js.map +1 -0
  34. package/dist/prompts.d.ts +8 -0
  35. package/dist/prompts.js +53 -0
  36. package/dist/prompts.js.map +1 -0
  37. package/dist/scaffold.d.ts +2 -0
  38. package/dist/scaffold.js +79 -0
  39. package/dist/scaffold.js.map +1 -0
  40. package/dist/utils/exec.d.ts +2 -0
  41. package/dist/utils/exec.js +14 -0
  42. package/dist/utils/exec.js.map +1 -0
  43. package/dist/utils/template.d.ts +10 -0
  44. package/dist/utils/template.js +20 -0
  45. package/dist/utils/template.js.map +1 -0
  46. package/dist/utils/validate.d.ts +4 -0
  47. package/dist/utils/validate.js +27 -0
  48. package/dist/utils/validate.js.map +1 -0
  49. package/package.json +50 -0
  50. package/templates/backend/app.module.ts.ejs +61 -0
  51. package/templates/backend/bff.controller.ts.ejs +60 -0
  52. package/templates/backend/bff.module.ts.ejs +10 -0
  53. package/templates/backend/bff.service.ts.ejs +6 -0
  54. package/templates/backend/create-schema.migration.ts.ejs +15 -0
  55. package/templates/backend/data-source.ts.ejs +13 -0
  56. package/templates/backend/env.example.ejs +30 -0
  57. package/templates/backend/main.ts.ejs +43 -0
  58. package/templates/backend/module.ts.ejs +14 -0
  59. package/templates/backend/nest-cli.json.ejs +8 -0
  60. package/templates/backend/package.json.ejs +23 -0
  61. package/templates/backend/tsconfig.build.json.ejs +4 -0
  62. package/templates/backend/tsconfig.json.ejs +24 -0
  63. package/templates/claude/CLAUDE.md.ejs +86 -0
  64. package/templates/claude/hooks/auto-format.sh +22 -0
  65. package/templates/claude/hooks/check-secrets.sh +49 -0
  66. package/templates/claude/hooks/guard-destructive.sh +42 -0
  67. package/templates/claude/hooks/on-compaction.sh +29 -0
  68. package/templates/claude/mcp.json +10 -0
  69. package/templates/claude/rules/api-patterns.md +86 -0
  70. package/templates/claude/rules/auth-patterns.md +109 -0
  71. package/templates/claude/rules/backend-patterns.md +421 -0
  72. package/templates/claude/rules/database-patterns.md +96 -0
  73. package/templates/claude/rules/docker-patterns.md +86 -0
  74. package/templates/claude/rules/frontend-patterns.md +262 -0
  75. package/templates/claude/rules/observability-backend.md +132 -0
  76. package/templates/claude/rules/observability-frontend.md +49 -0
  77. package/templates/claude/rules/playwright-mcp.md +80 -0
  78. package/templates/claude/rules/resilience-ops.md +103 -0
  79. package/templates/claude/rules/testing-e2e-ui.md +190 -0
  80. package/templates/claude/rules/testing-patterns.md +94 -0
  81. package/templates/claude/rules/workflow-backend.md +64 -0
  82. package/templates/claude/rules/workflow-frontend.md +60 -0
  83. package/templates/claude/settings.json +68 -0
  84. package/templates/claude/skills/add-api-endpoint/SKILL.md +59 -0
  85. package/templates/claude/skills/add-auth-endpoint/SKILL.md +68 -0
  86. package/templates/claude/skills/add-entity/SKILL.md +56 -0
  87. package/templates/claude/skills/add-event/SKILL.md +127 -0
  88. package/templates/claude/skills/add-feature/SKILL.md +20 -0
  89. package/templates/claude/skills/add-frontend-page/SKILL.md +75 -0
  90. package/templates/claude/skills/add-handler/SKILL.md +105 -0
  91. package/templates/claude/skills/add-integration/SKILL.md +176 -0
  92. package/templates/claude/skills/add-migration/SKILL.md +20 -0
  93. package/templates/claude/skills/add-module/SKILL.md +89 -0
  94. package/templates/claude/skills/add-realtime/SKILL.md +119 -0
  95. package/templates/claude/skills/audit-rules/SKILL.md +120 -0
  96. package/templates/claude/skills/debugging/SKILL.md +105 -0
  97. package/templates/claude/skills/docker-dev/SKILL.md +86 -0
  98. package/templates/claude/skills/e2e-audit/SKILL.md +85 -0
  99. package/templates/claude/skills/e2e-full/SKILL.md +132 -0
  100. package/templates/claude/skills/e2e-scan/SKILL.md +171 -0
  101. package/templates/claude/skills/e2e-verify/SKILL.md +145 -0
  102. package/templates/claude/skills/fix-bug/SKILL.md +33 -0
  103. package/templates/claude/skills/implement-spec/SKILL.md +98 -0
  104. package/templates/claude/skills/review-code/SKILL.md +109 -0
  105. package/templates/claude/skills/review-spec/SKILL.md +216 -0
  106. package/templates/claude/skills/run-tests/SKILL.md +37 -0
  107. package/templates/claude/skills/specify/SKILL.md +87 -0
  108. package/templates/claude/skills/write-backend-tests/SKILL.md +182 -0
  109. package/templates/claude/skills/write-ui-tests/SKILL.md +118 -0
  110. package/templates/docker/Dockerfile.client.ejs +14 -0
  111. package/templates/docker/Dockerfile.ejs +28 -0
  112. package/templates/docker/docker-compose.test.yml.ejs +54 -0
  113. package/templates/docker/docker-compose.yml.ejs +76 -0
  114. package/templates/docker/nginx.conf.ejs +21 -0
  115. package/templates/frontend/App.tsx.ejs +64 -0
  116. package/templates/frontend/DashboardPage.tsx.ejs +37 -0
  117. package/templates/frontend/LoginPage.tsx.ejs +20 -0
  118. package/templates/frontend/NotFoundPage.tsx.ejs +15 -0
  119. package/templates/frontend/api-client.ts.ejs +15 -0
  120. package/templates/frontend/index.css.ejs +57 -0
  121. package/templates/frontend/index.html.ejs +13 -0
  122. package/templates/frontend/main.tsx.ejs +10 -0
  123. package/templates/frontend/package.json.ejs +16 -0
  124. package/templates/frontend/playwright.config.ts.ejs +20 -0
  125. package/templates/frontend/postcss.config.js.ejs +3 -0
  126. package/templates/frontend/smoke.spec.ts.ejs +37 -0
  127. package/templates/frontend/tailwind.config.ts.ejs +56 -0
  128. package/templates/frontend/tsconfig.json.ejs +25 -0
  129. package/templates/frontend/tsconfig.node.json.ejs +15 -0
  130. package/templates/frontend/utils.ts.ejs +6 -0
  131. package/templates/frontend/vite-env.d.ts.ejs +1 -0
  132. package/templates/frontend/vite.config.ts.ejs +20 -0
  133. package/templates/root/gitignore.ejs +9 -0
  134. package/templates/root/prettierrc.ejs +7 -0
  135. package/templates/scripts/init-db.sh.ejs +8 -0
  136. package/templates/scripts/save-auth-state.ts.ejs +24 -0
@@ -0,0 +1,176 @@
1
+ # Add External Integration
2
+
3
+ ## When to Use
4
+ When the project integrates with an external system (third-party API, AI provider, partner service).
5
+
6
+ ## Steps
7
+ 1. **Create adapter service** in `src/<module>/services/<SystemName>Adapter.ts`:
8
+ ```typescript
9
+ @Injectable()
10
+ export class AcmeAdapter {
11
+ private readonly logger = new Logger(AcmeAdapter.name);
12
+ private readonly baseUrl: string;
13
+ private readonly apiKey: string;
14
+
15
+ constructor() {
16
+ this.baseUrl = process.env.ACME_API_URL!;
17
+ this.apiKey = process.env.ACME_API_KEY!;
18
+ }
19
+
20
+ async createResource(payload: CreatePayload): Promise<AcmeResponse> {
21
+ return this.request('POST', '/api/resources', payload);
22
+ }
23
+
24
+ private async request<T>(method: string, path: string, body?: unknown): Promise<T> {
25
+ const url = `${this.baseUrl}${path}`;
26
+ const response = await fetch(url, {
27
+ method,
28
+ headers: {
29
+ 'Content-Type': 'application/json',
30
+ 'Authorization': `Bearer ${this.apiKey}`,
31
+ 'X-Idempotency-Key': crypto.randomUUID(),
32
+ },
33
+ body: body ? JSON.stringify(body) : undefined,
34
+ });
35
+
36
+ if (response.status === 429) {
37
+ const retryAfter = parseInt(response.headers.get('Retry-After') || '5');
38
+ await new Promise(r => setTimeout(r, retryAfter * 1000));
39
+ return this.request(method, path, body);
40
+ }
41
+
42
+ if (!response.ok) {
43
+ const errorBody = await response.text();
44
+ this.logger.error({ method, path, status: response.status }, 'External API failed');
45
+ throw new Error(`${method} ${path}: ${response.status}`);
46
+ }
47
+
48
+ return response.json() as T;
49
+ }
50
+ }
51
+ ```
52
+ 2. **Define types** for request/response payloads
53
+ 3. **Create command with @Validate** — every integration command needs validation:
54
+ ```typescript
55
+ @Validate(SyncResourceValidator)
56
+ @DistributedLock('sync-resource:{resourceId}')
57
+ export class SyncResourceCommand {
58
+ constructor(
59
+ public readonly resourceId: string,
60
+ public readonly data: Record<string, unknown>,
61
+ ) {}
62
+ }
63
+ ```
64
+ 4. **Create .validator.ts** — co-located Zod schema
65
+ 5. **Use in command handler** — inject adapter, use `getTransactionalRepo()`:
66
+ ```typescript
67
+ @CommandHandler(SyncResourceCommand)
68
+ export class SyncResourceHandler implements ICommandHandler<SyncResourceCommand> {
69
+ constructor(
70
+ private readonly acme: AcmeAdapter,
71
+ @InjectRepository(Resource) private readonly resourceRepo: Repository<Resource>,
72
+ ) {}
73
+
74
+ async execute(command: SyncResourceCommand): Promise<Result<ResourceDto>> {
75
+ const resourceRepo = getTransactionalRepo(this.resourceRepo);
76
+
77
+ const externalResult = await this.acme.createResource({
78
+ reference: command.resourceId,
79
+ ...command.data,
80
+ });
81
+
82
+ const resource = await resourceRepo.save(
83
+ resourceRepo.create({
84
+ ...command,
85
+ externalId: externalResult.id,
86
+ syncStatus: 'synced',
87
+ }),
88
+ );
89
+
90
+ return Result.success(toDto(resource));
91
+ }
92
+ }
93
+ ```
94
+ 6. **Add webhook controller** (if the external system sends callbacks):
95
+ ```typescript
96
+ @Public()
97
+ @Controller('webhooks/<system>')
98
+ export class AcmeWebhookController {
99
+ constructor(private readonly commandBus: CommandBus) {}
100
+
101
+ @Post()
102
+ async handleWebhook(
103
+ @Req() req: RawBodyRequest<Request>,
104
+ @Headers('x-signature') signature: string,
105
+ ) {
106
+ const expectedSig = crypto
107
+ .createHmac('sha256', process.env.ACME_WEBHOOK_SECRET!)
108
+ .update(req.rawBody!)
109
+ .digest('hex');
110
+
111
+ if (signature !== expectedSig) {
112
+ throw new UnauthorizedException('Invalid webhook signature');
113
+ }
114
+
115
+ const event = JSON.parse(req.rawBody!.toString());
116
+ return this.commandBus.execute(new HandleWebhookEventCommand(event));
117
+ }
118
+ }
119
+ ```
120
+ 7. **Add environment variables** to `.env` and `docker-compose.yml`
121
+ 8. **Add circuit breaker** (MANDATORY for all external integrations):
122
+ ```typescript
123
+ import { createCircuitBreaker } from '@quanticjs/core';
124
+
125
+ private readonly breaker = createCircuitBreaker({
126
+ maxRetries: 2, // 3 total attempts, exponential backoff
127
+ consecutiveFailures: 5, // open circuit after 5 consecutive failures
128
+ halfOpenAfterMs: 30_000, // test one request after 30s
129
+ });
130
+
131
+ async createResource(payload: CreatePayload): Promise<AcmeResponse> {
132
+ return this.breaker.execute(() => this.request('POST', '/api/resources', payload));
133
+ }
134
+ ```
135
+ - **States:** Closed (normal) → Open (fast-fail) → Half-open (test one) → Closed on success
136
+ - 4xx responses are **never retried** and do not count toward circuit-breaker failures
137
+ - Each integration gets its **own** circuit breaker instance — never share across integrations
138
+ 9. **Add tests** — run `/write-backend-tests` for handler, validator, and webhook controller
139
+
140
+ ## Integration Spec Template
141
+ Create `docs/integrations/<system>.md` if it doesn't exist:
142
+ ```markdown
143
+ # Integration: <System Name>
144
+
145
+ ## Connection
146
+ - Base URL: env `<SYSTEM>_API_URL`
147
+ - Auth: Bearer token (env `<SYSTEM>_API_KEY`)
148
+ - Rate limit: N req/min
149
+
150
+ ## Endpoints
151
+ | Method | Path | Purpose | Request | Response |
152
+ |--------|------|---------|---------|----------|
153
+ | POST | /api/resource | Create | `{ field: value }` | `{ id, status }` |
154
+
155
+ ## Webhooks (inbound)
156
+ - URL: /webhooks/<system>
157
+ - Signature: HMAC-SHA256 in X-Signature header
158
+ - Secret: env `<SYSTEM>_WEBHOOK_SECRET`
159
+ - Events: resource.created, resource.updated
160
+
161
+ ## Error Handling
162
+ - 429 → retry after Retry-After header
163
+ - 5xx → circuit breaker (5 consecutive failures → open 30s, 2 retries with exponential backoff)
164
+ ```
165
+
166
+ ## Rules
167
+ - NEVER hardcode API keys — always `process.env.*`
168
+ - NEVER trust inbound webhooks without signature verification
169
+ - Adapter is the ONLY class that knows the external API — handlers call adapter methods
170
+ - Store external IDs on your entities (`externalId` column) for reconciliation
171
+ - Use idempotency keys on all mutating external calls
172
+ - Log all external calls at debug level (structured JSON) for troubleshooting
173
+ - Circuit breaker on ALL external HTTP calls — prevent cascade failures
174
+ - Each integration gets its own circuit breaker — never share across integrations
175
+ - 4xx responses are never retried and don't count toward circuit-breaker failures
176
+ - All repo access via `getTransactionalRepo()` — never use `this.repo` directly in handlers
@@ -0,0 +1,20 @@
1
+ # Add Migration
2
+
3
+ ## Steps
4
+ 1. Modify TypeORM entity classes in `src/<module>/entities/`
5
+ 2. Generate migration: `npx typeorm migration:generate src/migrations/<MigrationName>`
6
+ 3. Review generated SQL — ensure it handles existing data
7
+ 4. Run migration: `npx typeorm migration:run`
8
+ 5. Verify with: `npx typeorm migration:revert` (test down migration)
9
+
10
+ ## Migration Naming Convention
11
+ Use descriptive names: `AddItemTable`, `AddStatusColumnToOrder`, `CreateIndexOnEmail`.
12
+
13
+ ## Rules
14
+ - NEVER modify a migration that has been run — create a new one
15
+ - Column names are `"camelCase"` (quoted in SQL) — NOT `snake_case`
16
+ - Use regular `CREATE INDEX` in transactional migrations — NEVER `CREATE INDEX CONCURRENTLY`
17
+ - For large tables needing non-blocking index creation, use a separate migration with `transaction = false`
18
+ - NEVER write manual `CREATE TABLE` for tables with TypeORM entities — use `migration:generate`
19
+ - Test migration both up and down
20
+ - Each module's tables live in its own schema (e.g., `identity.*`, `project.*`)
@@ -0,0 +1,89 @@
1
+ # Add Module (Bounded Context)
2
+
3
+ ## Steps
4
+ 1. **Create module directory** — `src/<module-name>/`
5
+ 2. **Create subdirectories:**
6
+ ```
7
+ src/<module-name>/
8
+ ├── commands/ # Command classes + handlers + validators
9
+ ├── queries/ # Query classes + handlers
10
+ ├── entities/ # TypeORM entities
11
+ ├── dtos/ # Request/response DTOs
12
+ ├── controllers/ # THIN controllers
13
+ ├── services/ # Adapters (external integrations)
14
+ └── <module-name>.module.ts
15
+ ```
16
+ 3. **Create module file:**
17
+ ```typescript
18
+ import { Module } from '@nestjs/common';
19
+ import { CqrsModule } from '@nestjs/cqrs';
20
+ import { TypeOrmModule } from '@nestjs/typeorm';
21
+
22
+ @Module({
23
+ imports: [
24
+ CqrsModule,
25
+ TypeOrmModule.forFeature([/* entities */]),
26
+ ],
27
+ controllers: [/* controllers */],
28
+ providers: [
29
+ ...CommandHandlers,
30
+ ...QueryHandlers,
31
+ ...Validators,
32
+ ],
33
+ })
34
+ export class XxxModule {}
35
+ ```
36
+ 4. **Register in AppModule** — add to imports array in `src/app.module.ts`
37
+ 5. **Create PostgreSQL schema** — migration: `CREATE SCHEMA IF NOT EXISTS <module_name>;`
38
+ 6. **Add initial entity** — run `/add-entity`
39
+
40
+ ## App Module Checklist
41
+
42
+ When registering a new module in `app.module.ts`, ensure these `.forRoot()` modules are present (import once, never in feature modules):
43
+
44
+ ```typescript
45
+ @Module({
46
+ imports: [
47
+ QuanticModule.forRoot({ redis: { url: process.env.REDIS_URL } }),
48
+ QuanticHealthModule.forRoot(), // health probes — auto-detects DB + Redis
49
+ ScheduleModule.forRoot(),
50
+ LoggerModule.forRoot(pinoConfig),
51
+ // ... feature modules
52
+ XxxModule, // your new module
53
+ ],
54
+ })
55
+ export class AppModule {}
56
+ ```
57
+
58
+ ## Graceful Shutdown
59
+
60
+ If the service has custom resources (queue workers, websockets, outbox publisher), extend `GracefulShutdownService`:
61
+
62
+ ```typescript
63
+ @Injectable()
64
+ export class AppShutdownService extends GracefulShutdownService {
65
+ constructor(
66
+ @Optional() dataSource: DataSource,
67
+ @Optional() @Inject('REDIS_CLIENT') redis: Redis,
68
+ private readonly queueWorker: Worker,
69
+ ) {
70
+ super(dataSource, redis);
71
+ }
72
+
73
+ protected async drainWork(): Promise<void> {
74
+ await this.queueWorker.close();
75
+ }
76
+ }
77
+ ```
78
+
79
+ Register `AppShutdownService` in `app.module.ts` providers. Base class handles DB and Redis cleanup automatically.
80
+
81
+ ## Rules
82
+ - Each module owns its own PostgreSQL schema — no cross-schema queries
83
+ - Modules communicate through `CommandBus`/`QueryBus` — never import another module's services or repositories
84
+ - Only commands, queries, and DTOs are exported from a module
85
+ - Import from `@quanticjs/core` — never import from sibling modules directly
86
+ - Inter-module async communication uses Redis Streams
87
+ - `.forRoot()` modules (ScheduleModule, LoggerModule, QuanticHealthModule, etc.) go ONLY in `app.module.ts`
88
+ - `QuanticHealthModule.forRoot()` is MANDATORY — every service needs health probes
89
+ - Services with queue workers or websockets MUST extend `GracefulShutdownService` and close resources in `drainWork()`
@@ -0,0 +1,119 @@
1
+ # Add Real-time Feature (Socket.IO)
2
+
3
+ ## When to Use
4
+ When you need live updates pushed to the browser — notifications, collaborative editing, live status, real-time feeds.
5
+
6
+ ## Steps
7
+ 1. **Create WebSocket gateway** in `src/<module>/gateways/<Feature>Gateway.ts`:
8
+ ```typescript
9
+ import { WebSocketGateway, WebSocketServer, SubscribeMessage, ConnectedSocket, MessageBody } from '@nestjs/websockets';
10
+ import { Server, Socket } from 'socket.io';
11
+ import { createAdapter } from '@socket.io/redis-adapter';
12
+ import { Injectable, Inject, Optional } from '@nestjs/common';
13
+ import { REDIS_CLIENT } from '@quanticjs/core';
14
+ import type { Redis } from 'ioredis';
15
+
16
+ @WebSocketGateway({
17
+ namespace: '/<feature>',
18
+ cors: {
19
+ origin: (process.env.CORS_ORIGINS || 'http://localhost:5173').split(','),
20
+ credentials: true,
21
+ },
22
+ })
23
+ export class FeatureGateway {
24
+ @WebSocketServer()
25
+ server!: Server;
26
+
27
+ constructor(@Optional() @Inject(REDIS_CLIENT) private readonly redis?: Redis) {}
28
+
29
+ afterInit(server: Server): void {
30
+ if (this.redis) {
31
+ const pubClient = this.redis.duplicate();
32
+ const subClient = this.redis.duplicate();
33
+ server.adapter(createAdapter(pubClient, subClient));
34
+ }
35
+ }
36
+
37
+ @SubscribeMessage('subscribe')
38
+ handleSubscribe(
39
+ @ConnectedSocket() client: Socket,
40
+ @MessageBody() data: { resourceId: string },
41
+ ): void {
42
+ client.join(`<feature>:${data.resourceId}`);
43
+ }
44
+
45
+ @SubscribeMessage('unsubscribe')
46
+ handleUnsubscribe(
47
+ @ConnectedSocket() client: Socket,
48
+ @MessageBody() data: { resourceId: string },
49
+ ): void {
50
+ client.leave(`<feature>:${data.resourceId}`);
51
+ }
52
+
53
+ emitUpdate(resourceId: string, payload: unknown): void {
54
+ this.server.to(`<feature>:${resourceId}`).emit('update', payload);
55
+ }
56
+ }
57
+ ```
58
+
59
+ 2. **Register in module** — add gateway to `providers` array
60
+
61
+ 3. **Emit from handler** — inject gateway, call `emitUpdate()` after mutation:
62
+ ```typescript
63
+ @CommandHandler(UpdateStatusCommand)
64
+ export class UpdateStatusHandler implements ICommandHandler<UpdateStatusCommand> {
65
+ constructor(
66
+ private readonly gateway: FeatureGateway,
67
+ @InjectRepository(Resource) private readonly repo: Repository<Resource>,
68
+ ) {}
69
+
70
+ async execute(command: UpdateStatusCommand): Promise<Result<void>> {
71
+ const repo = getTransactionalRepo(this.repo);
72
+ await repo.update(command.id, { status: command.status });
73
+ this.gateway.emitUpdate(command.id, { status: command.status });
74
+ return Result.success();
75
+ }
76
+ }
77
+ ```
78
+
79
+ 4. **Frontend hook** using `@quanticjs/react-query` for cache invalidation:
80
+ ```typescript
81
+ import { useEffect } from 'react';
82
+ import { io } from 'socket.io-client';
83
+ import { useQueryClient } from '@tanstack/react-query';
84
+
85
+ export function useRealtimeUpdates(resourceId: string, queryKey: string[]) {
86
+ const queryClient = useQueryClient();
87
+
88
+ useEffect(() => {
89
+ const socket = io('/<feature>', { withCredentials: true });
90
+ socket.emit('subscribe', { resourceId });
91
+
92
+ socket.on('update', () => {
93
+ queryClient.invalidateQueries({ queryKey });
94
+ });
95
+
96
+ return () => { socket.disconnect(); };
97
+ }, [resourceId, queryKey, queryClient]);
98
+ }
99
+ ```
100
+
101
+ ## Patterns
102
+ - **Room per resource** — `<feature>:<resourceId>`
103
+ - **Redis adapter** — required for multi-instance deployments (horizontal scaling)
104
+ - **Graceful fallback** — if Redis unavailable, in-memory adapter (single instance only)
105
+ - **Invalidate, don't push data** — emit event type + ID, let client refetch via React Query
106
+
107
+ ## Frontend Connection
108
+ ```typescript
109
+ // Socket.IO uses the BFF cookie automatically
110
+ const socket = io('/<feature>', { withCredentials: true });
111
+ ```
112
+
113
+ ## Rules
114
+ - ALWAYS use Redis adapter in production (horizontal scaling)
115
+ - ALWAYS use namespace per feature (avoids event collision)
116
+ - ALWAYS disconnect on component unmount (memory leak prevention)
117
+ - NEVER send large payloads via WebSocket — send event + ID, client refetches
118
+ - NEVER use Socket.IO for data that should go through the command bus
119
+ - Frontend connects with `withCredentials: true` — BFF cookie sent on handshake
@@ -0,0 +1,120 @@
1
+ # Audit Rules Against ADRs
2
+
3
+ ## Usage
4
+ `/audit-rules` — compare `.claude/rules/` against the engineering ADR repository and report gaps.
5
+
6
+ ## Context
7
+
8
+ Rules in `.claude/rules/` are derived from architectural decision records (ADRs) at:
9
+ `/Users/turkel/Desktop/personal/projecets/engineering-adrs`
10
+
11
+ Over time, ADRs get updated or new ones are added without updating rules, and vice versa. This skill detects drift between the two.
12
+
13
+ ## ADR-to-Rule Mapping
14
+
15
+ | ADR (Backend) | Rule File |
16
+ |---|---|
17
+ | 001-modular-monolith.md | backend-patterns.md |
18
+ | 002-cqrs-pipeline-behaviors.md | backend-patterns.md |
19
+ | 003-result-error-handling.md | backend-patterns.md, api-patterns.md |
20
+ | 004-bff-authentication.md | auth-patterns.md |
21
+ | 005-multi-tenancy-rls.md | database-patterns.md |
22
+ | 006-typeorm-code-first-migrations.md | database-patterns.md |
23
+ | 007-two-layer-validation.md | backend-patterns.md |
24
+ | 008-redis-streams-events.md | backend-patterns.md |
25
+ | 009-kubernetes-helm-argocd.md | docker-patterns.md |
26
+ | 010-api-documentation.md | api-patterns.md |
27
+ | 011-observability.md | observability-patterns.md |
28
+ | 012-testing-strategy.md | testing-patterns.md |
29
+ | 013-local-development-workflow.md | docker-patterns.md |
30
+
31
+ | ADR (Frontend) | Rule File |
32
+ |---|---|
33
+ | 001-monorepo-tooling.md | frontend-patterns.md |
34
+ | 002-design-tokens-and-component-libraries.md | frontend-patterns.md |
35
+ | 003-state-management.md | frontend-patterns.md |
36
+ | 004-authentication-and-authorization.md | auth-patterns.md |
37
+ | 005-typescript-strict-mode.md | frontend-patterns.md |
38
+ | 006-mobile-secure-storage.md | auth-patterns.md |
39
+ | 007-observability.md | observability-patterns.md |
40
+ | 008-testing-strategy.md | testing-patterns.md |
41
+ | 009-local-development-workflow.md | docker-patterns.md |
42
+
43
+ ## Process
44
+
45
+ ### Step 1: Pull latest ADRs
46
+
47
+ Run `git -C /Users/turkel/Desktop/personal/projecets/engineering-adrs pull` to ensure you're comparing against the latest ADRs.
48
+
49
+ ### Step 2: Read all files
50
+
51
+ Read every ADR file listed in the mapping table above and every rule file in `.claude/rules/`. Do this in parallel using multiple Read calls to be efficient.
52
+
53
+ ### Step 3: Compare each ADR-rule pair
54
+
55
+ For each mapping in the table, compare the ADR's decisions against the corresponding rule file. Check for:
56
+
57
+ **Missing rules** — decisions, bans ("NEVER"), or patterns defined in the ADR but absent from the rule file:
58
+ - Code patterns or conventions mandated in the ADR
59
+ - Explicit bans ("What is explicitly banned" sections)
60
+ - Technology choices or library mandates
61
+ - Configuration requirements
62
+ - Mapping tables (e.g., Result-to-HTTP mappings)
63
+
64
+ **Stale rules** — rules that contradict or no longer match the current ADR:
65
+ - Values that changed (e.g., a timeout was 30s in the rule but the ADR now says 60s)
66
+ - Patterns that were replaced by newer decisions
67
+ - Libraries or tools that were swapped
68
+
69
+ **Unmapped ADRs** — any ADR file in the repo that does not appear in the mapping table above. These may need a new rule file or an addition to an existing one.
70
+
71
+ ### Step 4: Report
72
+
73
+ Output a structured report with three sections:
74
+
75
+ ```
76
+ ## Audit: Rules vs ADRs
77
+
78
+ ### Gaps Found
79
+ For each gap:
80
+ - **ADR:** <filename> — <section or quote>
81
+ - **Rule:** <filename> — what's missing or wrong
82
+ - **Action:** what should be added/changed
83
+
84
+ ### Stale Rules
85
+ For each stale rule:
86
+ - **Rule:** <filename> — <the stale content>
87
+ - **ADR:** <filename> — <what it should say now>
88
+ - **Action:** what should change
89
+
90
+ ### New ADRs Without Rules
91
+ For each unmapped ADR:
92
+ - **ADR:** <filename> — <summary of decisions>
93
+ - **Suggested rule file:** <which rule file to add it to>
94
+
95
+ ### Summary
96
+ - X gaps found
97
+ - X stale rules
98
+ - X unmapped ADRs
99
+ ```
100
+
101
+ ### Step 5: Offer to fix
102
+
103
+ After presenting the report, ask the user:
104
+ "Want me to fix these gaps?" using AskUserQuestion with options:
105
+ - **Fix all** — update all rule files to match ADRs
106
+ - **Fix one by one** — go through each gap and ask before fixing
107
+ - **Report only** — no changes, just the audit
108
+
109
+ If the user chooses to fix, update only the rule files (`.claude/rules/`), never the ADRs. ADRs are the source of truth.
110
+
111
+ Also update the mapping table in this skill file if new ADRs were found that need mapping.
112
+
113
+ ## Rules
114
+
115
+ - ADRs are the source of truth. Rules derive from ADRs, not the other way around.
116
+ - NEVER modify ADR files — only modify rule files.
117
+ - When reporting gaps, quote the specific ADR section so the user can verify.
118
+ - Ignore stylistic differences (wording, formatting) — only flag semantic gaps.
119
+ - If an ADR section says "planned" or "future", do NOT flag it as missing from rules. Rules only cover current decisions.
120
+ - Check ADR status field — only compare "Accepted" ADRs. Skip "Proposed", "Deprecated", or "Superseded" ADRs.
@@ -0,0 +1,105 @@
1
+ # Debugging
2
+
3
+ ## Backend (NestJS)
4
+
5
+ ### Logs
6
+ ```bash
7
+ docker compose logs backend -f --tail=100 # Live structured logs (Pino)
8
+ docker compose logs backend 2>&1 | grep -i error # Filter errors only
9
+ ```
10
+
11
+ ### Health Check
12
+ ```bash
13
+ curl http://localhost:3000/health
14
+ ```
15
+
16
+ ### Database
17
+ ```bash
18
+ docker compose exec postgres psql -U postgres -d autoflux
19
+ npx typeorm migration:show # List migrations and status
20
+ npx typeorm migration:run # Apply pending migrations
21
+ npx typeorm migration:revert # Rollback last migration
22
+ ```
23
+
24
+ ### Redis
25
+ ```bash
26
+ docker compose exec redis redis-cli PING
27
+ docker compose exec redis redis-cli MONITOR # Watch all commands live
28
+ docker compose exec redis redis-cli KEYS '*' # List all keys
29
+ docker compose exec redis redis-cli XINFO GROUPS <stream> # Stream consumer info
30
+ ```
31
+
32
+ ### Running specific tests
33
+ ```bash
34
+ npx jest --testPathPattern=CreateItem --verbose
35
+ npx jest --watch # interactive mode
36
+ ```
37
+
38
+ ## Frontend (React)
39
+
40
+ ### Dev Server
41
+ ```bash
42
+ cd client && npm run dev # Check terminal for build errors
43
+ ```
44
+
45
+ ### Browser DevTools
46
+ 1. **Console** — check for React errors, unhandled rejections
47
+ 2. **Network** — verify API calls go to `/api/*` (not direct to :3000)
48
+ 3. **Application > Cookies** — verify httpOnly session cookie exists after login
49
+ 4. **React DevTools** — inspect component state and query cache
50
+ 5. **TanStack Query DevTools** — inspect cache state, stale queries, refetch triggers
51
+
52
+ ### Running specific tests
53
+ ```bash
54
+ cd client && npx vitest run src/pages/ProjectsPage.test.tsx
55
+ cd client && npx playwright test projects.spec.ts
56
+ ```
57
+
58
+ ## Common Issues
59
+
60
+ ### Auth / 401 Errors
61
+ | Symptom | Cause | Fix |
62
+ |---------|-------|-----|
63
+ | 401 on every request | Session cookie missing/expired | Re-login via `/auth/login` |
64
+ | 401 after refresh | Keycloak session expired | Restart Keycloak: `docker compose restart keycloak` |
65
+ | Login redirect loop | Callback URL mismatch | Check Keycloak client config: Valid Redirect URIs |
66
+ | No cookie set | Vite proxy not configured | Verify `vite.config.ts` has `/auth` proxy |
67
+
68
+ ### Database / Migrations
69
+ | Symptom | Cause | Fix |
70
+ |---------|-------|-----|
71
+ | `relation does not exist` | Migration not run | `npx typeorm migration:run` |
72
+ | `column does not exist` | Entity/migration mismatch | `npx typeorm migration:generate src/migrations/Fix` |
73
+ | Migration fails | Conflict with existing data | Check migration SQL, add IF NOT EXISTS |
74
+ | Duplicate key error | Missing unique constraint handling | Add `@DistributedLock` or check-before-insert |
75
+
76
+ ### Docker / Services
77
+ | Symptom | Cause | Fix |
78
+ |---------|-------|-----|
79
+ | Connection refused :3000 | Backend not started | `docker compose up backend` |
80
+ | Connection refused :5432 | Trying localhost for DB | Use Docker hostname `postgres` in backend config |
81
+ | Container restarting | OOM or crash loop | `docker compose logs <svc> --tail=20` |
82
+ | Slow hot reload | Volume mount lag | Restart: `docker compose restart backend` |
83
+
84
+ ### Redis / Streams
85
+ | Symptom | Cause | Fix |
86
+ |---------|-------|-----|
87
+ | Stream lag growing | Consumer crashed | Check consumer logs, restart service |
88
+ | DLQ filling up | Handler throwing repeatedly | Fix handler bug, then reprocess DLQ |
89
+ | Cache stale | Missing invalidation | Check mutation's cache invalidation logic |
90
+
91
+ ### Frontend / API
92
+ | Symptom | Cause | Fix |
93
+ |---------|-------|-----|
94
+ | CORS error | Bypassing Vite proxy | Use relative URLs (`/api/...`), not `http://localhost:3000` |
95
+ | Stale data | Missing `invalidates` | Add `invalidates: [['queryKey']]` to `useApiMutation` |
96
+ | Form errors not showing | Not using `@quanticjs/react-forms` | Use `useForm` — auto-maps server errors |
97
+ | White screen | Unhandled error | Check browser console, add ErrorBoundary |
98
+
99
+ ## Quick Diagnostic Script
100
+ ```bash
101
+ echo "=== Services ===" && docker compose ps
102
+ echo "=== Backend Health ===" && curl -s http://localhost:3000/health | jq .
103
+ echo "=== Pending Migrations ===" && npx typeorm migration:show 2>&1 | grep -v "✓"
104
+ echo "=== Redis ===" && docker compose exec redis redis-cli PING
105
+ ```