@mars-stack/cli 0.2.0 → 0.2.2
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/package.json +2 -2
- package/template/.cursor/rules/composition-patterns.mdc +186 -0
- package/template/.cursor/rules/data-access.mdc +29 -0
- package/template/.cursor/rules/project-structure.mdc +34 -0
- package/template/.cursor/rules/security.mdc +25 -0
- package/template/.cursor/rules/testing.mdc +24 -0
- package/template/.cursor/rules/ui-conventions.mdc +29 -0
- package/template/.cursor/skills/add-api-route/SKILL.md +122 -0
- package/template/.cursor/skills/add-audit-log/SKILL.md +373 -0
- package/template/.cursor/skills/add-blog/SKILL.md +447 -0
- package/template/.cursor/skills/add-command-palette/SKILL.md +438 -0
- package/template/.cursor/skills/add-component/SKILL.md +158 -0
- package/template/.cursor/skills/add-crud-routes/SKILL.md +221 -0
- package/template/.cursor/skills/add-e2e-test/SKILL.md +227 -0
- package/template/.cursor/skills/add-error-boundary/SKILL.md +472 -0
- package/template/.cursor/skills/add-feature/SKILL.md +174 -0
- package/template/.cursor/skills/add-middleware/SKILL.md +135 -0
- package/template/.cursor/skills/add-page/SKILL.md +151 -0
- package/template/.cursor/skills/add-prisma-model/SKILL.md +148 -0
- package/template/.cursor/skills/add-protected-resource/SKILL.md +192 -0
- package/template/.cursor/skills/add-role/SKILL.md +156 -0
- package/template/.cursor/skills/add-server-action/SKILL.md +167 -0
- package/template/.cursor/skills/add-webhook/SKILL.md +192 -0
- package/template/.cursor/skills/build-complete-feature/SKILL.md +227 -0
- package/template/.cursor/skills/build-dashboard/SKILL.md +211 -0
- package/template/.cursor/skills/build-data-table/SKILL.md +283 -0
- package/template/.cursor/skills/build-form/SKILL.md +231 -0
- package/template/.cursor/skills/build-landing-page/SKILL.md +248 -0
- package/template/.cursor/skills/configure-ai/SKILL.md +617 -0
- package/template/.cursor/skills/configure-analytics/SKILL.md +413 -0
- package/template/.cursor/skills/configure-dark-mode/SKILL.md +309 -0
- package/template/.cursor/skills/configure-email/SKILL.md +170 -0
- package/template/.cursor/skills/configure-email-verification/SKILL.md +333 -0
- package/template/.cursor/skills/configure-feature-flags/SKILL.md +361 -0
- package/template/.cursor/skills/configure-i18n/SKILL.md +518 -0
- package/template/.cursor/skills/configure-jobs/SKILL.md +500 -0
- package/template/.cursor/skills/configure-magic-links/SKILL.md +385 -0
- package/template/.cursor/skills/configure-multi-tenancy/SKILL.md +611 -0
- package/template/.cursor/skills/configure-notifications/SKILL.md +569 -0
- package/template/.cursor/skills/configure-oauth/SKILL.md +217 -0
- package/template/.cursor/skills/configure-onboarding/SKILL.md +483 -0
- package/template/.cursor/skills/configure-payments/SKILL.md +243 -0
- package/template/.cursor/skills/configure-realtime/SKILL.md +733 -0
- package/template/.cursor/skills/configure-search/SKILL.md +581 -0
- package/template/.cursor/skills/configure-storage/SKILL.md +273 -0
- package/template/.cursor/skills/configure-two-factor/SKILL.md +518 -0
- package/template/.cursor/skills/create-execution-plan/SKILL.md +204 -0
- package/template/.cursor/skills/create-seed/SKILL.md +191 -0
- package/template/.cursor/skills/deploy-to-vercel/SKILL.md +300 -0
- package/template/.cursor/skills/design-tokens/SKILL.md +138 -0
- package/template/.cursor/skills/mars-capture-conversation-context/SKILL.md +119 -0
- package/template/.cursor/skills/setup-billing/SKILL.md +322 -0
- package/template/.cursor/skills/setup-project/SKILL.md +104 -0
- package/template/.cursor/skills/setup-teams/SKILL.md +682 -0
- package/template/.cursor/skills/test-api-route/SKILL.md +219 -0
- package/template/.cursor/skills/update-architecture-docs/SKILL.md +99 -0
- package/template/AGENTS.md +104 -0
- package/template/ARCHITECTURE.md +102 -0
- package/template/docs/QUALITY_SCORE.md +20 -0
- package/template/docs/design-docs/conversation-as-system-record.md +70 -0
- package/template/docs/design-docs/core-beliefs.md +43 -0
- package/template/docs/design-docs/index.md +8 -0
- package/template/docs/exec-plans/active/.gitkeep +0 -0
- package/template/docs/exec-plans/completed/.gitkeep +0 -0
- package/template/docs/exec-plans/tech-debt.md +7 -0
- package/template/docs/generated/.gitkeep +0 -0
- package/template/docs/product-specs/index.md +7 -0
- package/template/docs/references/index.md +18 -0
- package/template/e2e/api.spec.ts +20 -0
- package/template/e2e/auth.spec.ts +24 -0
- package/template/e2e/public.spec.ts +25 -0
- package/template/eslint.config.mjs +24 -0
- package/template/next-env.d.ts +6 -0
- package/template/next.config.ts +45 -0
- package/template/package.json +80 -0
- package/template/playwright.config.ts +31 -0
- package/template/postcss.config.mjs +8 -0
- package/template/prisma/generated/prisma/browser.ts +49 -0
- package/template/prisma/generated/prisma/client.ts +73 -0
- package/template/prisma/generated/prisma/commonInputTypes.ts +406 -0
- package/template/prisma/generated/prisma/enums.ts +15 -0
- package/template/prisma/generated/prisma/internal/class.ts +254 -0
- package/template/prisma/generated/prisma/internal/prismaNamespace.ts +1240 -0
- package/template/prisma/generated/prisma/internal/prismaNamespaceBrowser.ts +190 -0
- package/template/prisma/generated/prisma/models/Account.ts +1543 -0
- package/template/prisma/generated/prisma/models/File.ts +1529 -0
- package/template/prisma/generated/prisma/models/Session.ts +1415 -0
- package/template/prisma/generated/prisma/models/Subscription.ts +1455 -0
- package/template/prisma/generated/prisma/models/User.ts +2235 -0
- package/template/prisma/generated/prisma/models/VerificationToken.ts +1099 -0
- package/template/prisma/generated/prisma/models.ts +17 -0
- package/template/prisma/schema/auth.prisma +69 -0
- package/template/prisma/schema/base.prisma +8 -0
- package/template/prisma/schema/file.prisma +15 -0
- package/template/prisma/schema/subscription.prisma +17 -0
- package/template/prisma.config.ts +13 -0
- package/template/scripts/check-architecture.ts +221 -0
- package/template/scripts/check-doc-freshness.ts +242 -0
- package/template/scripts/ensure-db.mjs +291 -0
- package/template/scripts/generate-docs.ts +143 -0
- package/template/scripts/generate-env-example.ts +89 -0
- package/template/scripts/seed.ts +56 -0
- package/template/scripts/update-quality-score.ts +263 -0
- package/template/src/__tests__/architecture.test.ts +114 -0
- package/template/src/app/(auth)/forgotten-password/page.tsx +92 -0
- package/template/src/app/(auth)/layout.tsx +11 -0
- package/template/src/app/(auth)/register/page.tsx +162 -0
- package/template/src/app/(auth)/reset-password/page.tsx +109 -0
- package/template/src/app/(auth)/sign-in/page.tsx +122 -0
- package/template/src/app/(auth)/verify/[token]/page.tsx +87 -0
- package/template/src/app/(auth)/verify/page.tsx +56 -0
- package/template/src/app/(protected)/admin/page.tsx +108 -0
- package/template/src/app/(protected)/dashboard/loading.tsx +20 -0
- package/template/src/app/(protected)/dashboard/page.tsx +22 -0
- package/template/src/app/(protected)/layout.tsx +262 -0
- package/template/src/app/(protected)/settings/page.tsx +370 -0
- package/template/src/app/api/auth/forgot/route.ts +63 -0
- package/template/src/app/api/auth/login/route.ts +121 -0
- package/template/src/app/api/auth/logout/route.ts +19 -0
- package/template/src/app/api/auth/me/route.ts +30 -0
- package/template/src/app/api/auth/reset/route.ts +45 -0
- package/template/src/app/api/auth/signup/route.ts +85 -0
- package/template/src/app/api/auth/verify/route.ts +46 -0
- package/template/src/app/api/csrf/route.ts +12 -0
- package/template/src/app/api/health/route.ts +10 -0
- package/template/src/app/api/protected/admin/users/route.ts +24 -0
- package/template/src/app/api/protected/billing/checkout/route.ts +83 -0
- package/template/src/app/api/protected/billing/portal/route.ts +39 -0
- package/template/src/app/api/protected/files/[fileId]/route.ts +86 -0
- package/template/src/app/api/protected/files/upload/route.ts +64 -0
- package/template/src/app/api/protected/user/password/route.ts +63 -0
- package/template/src/app/api/protected/user/profile/route.ts +35 -0
- package/template/src/app/api/protected/user/sessions/[sessionId]/route.ts +33 -0
- package/template/src/app/api/protected/user/sessions/route.ts +22 -0
- package/template/src/app/api/readiness/route.ts +15 -0
- package/template/src/app/api/webhooks/stripe/route.ts +166 -0
- package/template/src/app/error.tsx +33 -0
- package/template/src/app/layout.tsx +29 -0
- package/template/src/app/not-found.tsx +20 -0
- package/template/src/app/page.tsx +136 -0
- package/template/src/app/privacy/page.tsx +178 -0
- package/template/src/app/providers.tsx +8 -0
- package/template/src/app/terms/page.tsx +139 -0
- package/template/src/config/app.config.ts +70 -0
- package/template/src/config/routes.ts +17 -0
- package/template/src/features/admin/index.ts +11 -0
- package/template/src/features/admin/permissions.ts +64 -0
- package/template/src/features/auth/context/AuthContext.tsx +96 -0
- package/template/src/features/auth/context/index.ts +2 -0
- package/template/src/features/auth/index.ts +3 -0
- package/template/src/features/auth/server/consent.ts +66 -0
- package/template/src/features/auth/server/session-revocation.ts +20 -0
- package/template/src/features/auth/server/sessions.ts +66 -0
- package/template/src/features/auth/server/user.ts +166 -0
- package/template/src/features/auth/types.ts +19 -0
- package/template/src/features/auth/validators.ts +29 -0
- package/template/src/features/billing/server/index.ts +66 -0
- package/template/src/features/billing/types.ts +43 -0
- package/template/src/features/uploads/server/index.ts +49 -0
- package/template/src/features/uploads/types.ts +26 -0
- package/template/src/lib/core/email/templates/base-layout.ts +122 -0
- package/template/src/lib/core/email/templates/index.ts +4 -0
- package/template/src/lib/core/email/templates/password-reset-email.ts +42 -0
- package/template/src/lib/core/email/templates/verification-email.ts +41 -0
- package/template/src/lib/core/email/templates/welcome-email.ts +40 -0
- package/template/src/lib/mars.ts +56 -0
- package/template/src/lib/prisma.ts +19 -0
- package/template/src/proxy.ts +92 -0
- package/template/src/styles/brand.css +17 -0
- package/template/src/styles/globals.css +6 -0
- package/template/tsconfig.json +59 -0
- package/template/vitest.config.ts +41 -0
- package/template/vitest.setup.ts +24 -0
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
# Skill: Test an API Route
|
|
2
|
+
|
|
3
|
+
Write unit tests for a MARS API route using Vitest, mock-auth, and Prisma mocking.
|
|
4
|
+
|
|
5
|
+
## When to Use
|
|
6
|
+
|
|
7
|
+
Use this skill when the user asks to test an API endpoint, write backend tests, or add test coverage for a route handler.
|
|
8
|
+
|
|
9
|
+
## Architecture
|
|
10
|
+
|
|
11
|
+
MARS API route tests:
|
|
12
|
+
- Import and call the exported handler function directly (no HTTP server needed)
|
|
13
|
+
- Mock Prisma, auth session, and external services
|
|
14
|
+
- Assert response status and body
|
|
15
|
+
- Test all paths: success, validation errors, auth errors, database errors
|
|
16
|
+
|
|
17
|
+
## Template: Full API Route Test
|
|
18
|
+
|
|
19
|
+
```typescript
|
|
20
|
+
// src/app/api/protected/projects/route.test.ts
|
|
21
|
+
import { mockAuth, createTestRequest, createTestRequestWithBody } from '@mars-stack/core/test-utils';
|
|
22
|
+
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
23
|
+
import { GET, POST } from './route';
|
|
24
|
+
|
|
25
|
+
// Mock database
|
|
26
|
+
const mockFindMany = vi.fn();
|
|
27
|
+
const mockCreate = vi.fn();
|
|
28
|
+
|
|
29
|
+
vi.mock('@/lib/prisma', () => ({
|
|
30
|
+
prisma: {
|
|
31
|
+
project: {
|
|
32
|
+
findMany: (...args: unknown[]) => mockFindMany(...args),
|
|
33
|
+
create: (...args: unknown[]) => mockCreate(...args),
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
isDatabaseError: vi.fn(() => false),
|
|
37
|
+
formatDatabaseError: vi.fn(),
|
|
38
|
+
}));
|
|
39
|
+
|
|
40
|
+
// Mock auth -- return a valid session
|
|
41
|
+
vi.mock('@/lib/mars', () => ({
|
|
42
|
+
verifySessionForAPI: vi.fn(() => Promise.resolve(mockAuth)),
|
|
43
|
+
}));
|
|
44
|
+
|
|
45
|
+
describe('GET /api/protected/projects', () => {
|
|
46
|
+
beforeEach(() => {
|
|
47
|
+
vi.clearAllMocks();
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it('returns projects for the authenticated user', async () => {
|
|
51
|
+
const projects = [
|
|
52
|
+
{ id: '1', name: 'Project A', userId: mockAuth.userId },
|
|
53
|
+
{ id: '2', name: 'Project B', userId: mockAuth.userId },
|
|
54
|
+
];
|
|
55
|
+
mockFindMany.mockResolvedValue(projects);
|
|
56
|
+
|
|
57
|
+
const request = createTestRequest('GET', '/api/protected/projects');
|
|
58
|
+
const response = await GET(request);
|
|
59
|
+
const body = await response.json();
|
|
60
|
+
|
|
61
|
+
expect(response.status).toBe(200);
|
|
62
|
+
expect(body).toHaveLength(2);
|
|
63
|
+
expect(mockFindMany).toHaveBeenCalledWith(
|
|
64
|
+
expect.objectContaining({
|
|
65
|
+
where: { userId: mockAuth.userId },
|
|
66
|
+
}),
|
|
67
|
+
);
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it('returns 401 when not authenticated', async () => {
|
|
71
|
+
const { verifySessionForAPI } = await import('@/lib/mars');
|
|
72
|
+
vi.mocked(verifySessionForAPI).mockResolvedValueOnce(null);
|
|
73
|
+
|
|
74
|
+
const request = createTestRequest('GET', '/api/protected/projects');
|
|
75
|
+
const response = await GET(request);
|
|
76
|
+
|
|
77
|
+
expect(response.status).toBe(401);
|
|
78
|
+
});
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
describe('POST /api/protected/projects', () => {
|
|
82
|
+
beforeEach(() => {
|
|
83
|
+
vi.clearAllMocks();
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
it('creates a project with valid data', async () => {
|
|
87
|
+
const newProject = { id: '3', name: 'New Project', userId: mockAuth.userId };
|
|
88
|
+
mockCreate.mockResolvedValue(newProject);
|
|
89
|
+
|
|
90
|
+
const request = createTestRequestWithBody('POST', '/api/protected/projects', {
|
|
91
|
+
name: 'New Project',
|
|
92
|
+
});
|
|
93
|
+
const response = await POST(request);
|
|
94
|
+
const body = await response.json();
|
|
95
|
+
|
|
96
|
+
expect(response.status).toBe(201);
|
|
97
|
+
expect(body.name).toBe('New Project');
|
|
98
|
+
expect(mockCreate).toHaveBeenCalledWith(
|
|
99
|
+
expect.objectContaining({
|
|
100
|
+
data: expect.objectContaining({
|
|
101
|
+
name: 'New Project',
|
|
102
|
+
userId: mockAuth.userId,
|
|
103
|
+
}),
|
|
104
|
+
}),
|
|
105
|
+
);
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
it('returns 400 for invalid input', async () => {
|
|
109
|
+
const request = createTestRequestWithBody('POST', '/api/protected/projects', {
|
|
110
|
+
name: '', // Empty name should fail validation
|
|
111
|
+
});
|
|
112
|
+
const response = await POST(request);
|
|
113
|
+
|
|
114
|
+
expect(response.status).toBe(400);
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
it('returns 503 when database is unavailable', async () => {
|
|
118
|
+
const dbError = new Error("Can't reach database server");
|
|
119
|
+
Object.defineProperty(dbError, 'name', { value: 'PrismaClientInitializationError' });
|
|
120
|
+
mockCreate.mockRejectedValue(dbError);
|
|
121
|
+
|
|
122
|
+
const { isDatabaseError } = await import('@/lib/prisma');
|
|
123
|
+
vi.mocked(isDatabaseError).mockReturnValueOnce(true);
|
|
124
|
+
|
|
125
|
+
const request = createTestRequestWithBody('POST', '/api/protected/projects', {
|
|
126
|
+
name: 'Test Project',
|
|
127
|
+
});
|
|
128
|
+
const response = await POST(request);
|
|
129
|
+
|
|
130
|
+
expect(response.status).toBe(503);
|
|
131
|
+
});
|
|
132
|
+
});
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
## Mock Auth Utilities
|
|
136
|
+
|
|
137
|
+
MARS provides mock auth utilities via `@mars-stack/core/test-utils`:
|
|
138
|
+
|
|
139
|
+
```typescript
|
|
140
|
+
import { mockAuth, mockAuthAsAdmin, mockAuthAsUser } from '@mars-stack/core/test-utils';
|
|
141
|
+
import { createTestRequest, createTestRequestWithBody } from '@mars-stack/core/test-utils';
|
|
142
|
+
|
|
143
|
+
// mockAuth — default mock session object
|
|
144
|
+
// mockAuthAsAdmin / mockAuthAsUser — role-specific variants
|
|
145
|
+
// createTestRequest(method, path) — build a NextRequest without a body
|
|
146
|
+
// createTestRequestWithBody(method, path, body) — build a NextRequest with JSON body
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
## Testing Patterns
|
|
150
|
+
|
|
151
|
+
### Test Auth Required
|
|
152
|
+
|
|
153
|
+
```typescript
|
|
154
|
+
it('returns 401 when not authenticated', async () => {
|
|
155
|
+
const { verifySessionForAPI } = await import('@/lib/mars');
|
|
156
|
+
vi.mocked(verifySessionForAPI).mockResolvedValueOnce(null);
|
|
157
|
+
// ...assert 401
|
|
158
|
+
});
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
### Test Role Requirement
|
|
162
|
+
|
|
163
|
+
```typescript
|
|
164
|
+
it('returns 403 for non-admin users', async () => {
|
|
165
|
+
const { verifySessionForAPI } = await import('@/lib/mars');
|
|
166
|
+
vi.mocked(verifySessionForAPI).mockResolvedValueOnce({ ...mockAuth, role: 'user' });
|
|
167
|
+
|
|
168
|
+
// For withRole, also mock the DB role check
|
|
169
|
+
mockFindUnique.mockResolvedValueOnce({ role: 'user' });
|
|
170
|
+
|
|
171
|
+
// ...assert 403
|
|
172
|
+
});
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
### Test Validation Errors
|
|
176
|
+
|
|
177
|
+
```typescript
|
|
178
|
+
it('returns 400 with validation errors', async () => {
|
|
179
|
+
const request = createTestRequestWithBody('POST', '/api/protected/resource', {
|
|
180
|
+
email: 'not-an-email',
|
|
181
|
+
});
|
|
182
|
+
const response = await handler(request);
|
|
183
|
+
const body = await response.json();
|
|
184
|
+
|
|
185
|
+
expect(response.status).toBe(400);
|
|
186
|
+
expect(body.error).toBeDefined();
|
|
187
|
+
});
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
### Test Database Errors
|
|
191
|
+
|
|
192
|
+
```typescript
|
|
193
|
+
it('returns 503 when database is down', async () => {
|
|
194
|
+
mockFindMany.mockRejectedValueOnce(new Error("Can't reach database"));
|
|
195
|
+
// ... assert 503
|
|
196
|
+
});
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
## Running Tests
|
|
200
|
+
|
|
201
|
+
```bash
|
|
202
|
+
yarn test # Run all tests
|
|
203
|
+
yarn test src/app/api/ # Run only API tests
|
|
204
|
+
yarn test:watch # Watch mode
|
|
205
|
+
yarn test:coverage # With coverage report
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
## Checklist
|
|
209
|
+
|
|
210
|
+
- [ ] Test file created next to route (`route.test.ts`)
|
|
211
|
+
- [ ] Prisma mocked with `vi.mock('@/lib/prisma')`
|
|
212
|
+
- [ ] Auth session mocked with `vi.mock('@/lib/mars')`
|
|
213
|
+
- [ ] `vi.clearAllMocks()` in `beforeEach`
|
|
214
|
+
- [ ] Success path tested
|
|
215
|
+
- [ ] 401 unauthorized tested
|
|
216
|
+
- [ ] 400 validation error tested
|
|
217
|
+
- [ ] 403 forbidden tested (if role-gated)
|
|
218
|
+
- [ ] 503 database error tested
|
|
219
|
+
- [ ] Response status AND body assertions
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
# Skill: Update Architecture Docs
|
|
2
|
+
|
|
3
|
+
Keep the project documentation in sync with code changes. Covers when and how to update AGENTS.md, QUALITY_SCORE.md, and related docs.
|
|
4
|
+
|
|
5
|
+
## When to Use
|
|
6
|
+
|
|
7
|
+
Use this skill when:
|
|
8
|
+
- You have just added a new feature, service, or module
|
|
9
|
+
- You have changed the database schema
|
|
10
|
+
- You have completed an execution plan
|
|
11
|
+
- The user asks to update docs, sync docs, or refresh documentation
|
|
12
|
+
- Another skill's checklist includes "update docs"
|
|
13
|
+
|
|
14
|
+
## Prerequisites
|
|
15
|
+
|
|
16
|
+
- Read `AGENTS.md` for the project-level agent guide.
|
|
17
|
+
- Read `docs/QUALITY_SCORE.md` for current quality grades.
|
|
18
|
+
|
|
19
|
+
## Document Index
|
|
20
|
+
|
|
21
|
+
| Document | Location | Purpose | Update frequency |
|
|
22
|
+
|----------|----------|---------|------------------|
|
|
23
|
+
| `AGENTS.md` | Repo root | First file any agent reads. Overview + pointers | On structure changes |
|
|
24
|
+
| `docs/QUALITY_SCORE.md` | Docs | Quality grades by domain | After every improvement |
|
|
25
|
+
| `docs/design-docs/` | Docs | Architecture decisions and design documents | On architectural changes |
|
|
26
|
+
|
|
27
|
+
## When to Update Each Document
|
|
28
|
+
|
|
29
|
+
### AGENTS.md
|
|
30
|
+
|
|
31
|
+
Update when:
|
|
32
|
+
- The directory structure changes (new route groups, new features)
|
|
33
|
+
- New feature modules are added
|
|
34
|
+
- The config structure changes
|
|
35
|
+
- New "How to Run" commands are added
|
|
36
|
+
- Key constraints change
|
|
37
|
+
|
|
38
|
+
Keep it concise — AGENTS.md should fit in a single context window.
|
|
39
|
+
|
|
40
|
+
### docs/QUALITY_SCORE.md
|
|
41
|
+
|
|
42
|
+
Update when:
|
|
43
|
+
- A quality grade changes (feature implemented, bug fixed, test added)
|
|
44
|
+
- A new domain is added to the grading table
|
|
45
|
+
- An execution plan is completed
|
|
46
|
+
|
|
47
|
+
How to update:
|
|
48
|
+
1. Find the relevant row in the appropriate table
|
|
49
|
+
2. Update the grade (A/B/C/D/F)
|
|
50
|
+
3. Update the notes to reflect current state
|
|
51
|
+
|
|
52
|
+
```markdown
|
|
53
|
+
| Dashboard | B | Basic layout with stat cards. Missing: chart widgets, date range filter |
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## Step-by-Step: After Adding a Feature
|
|
57
|
+
|
|
58
|
+
1. **QUALITY_SCORE.md** — Update the grade for the relevant domain:
|
|
59
|
+
|
|
60
|
+
```markdown
|
|
61
|
+
| Billing | B | Stripe checkout, portal, webhook. Missing: usage-based billing |
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
2. **AGENTS.md** — If the feature adds new directories or conventions:
|
|
65
|
+
|
|
66
|
+
```markdown
|
|
67
|
+
├── features/
|
|
68
|
+
│ └── billing/ # Subscription management
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
3. **Execution plan** — Mark tasks complete, update verification:
|
|
72
|
+
|
|
73
|
+
```markdown
|
|
74
|
+
- [x] **3.1 Billing page** — Done. `src/app/(protected)/billing/page.tsx`
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## Step-by-Step: After a Schema Change
|
|
78
|
+
|
|
79
|
+
1. **AGENTS.md** — Update the schema file listing if new schema files added
|
|
80
|
+
2. **QUALITY_SCORE.md** — Update database-related grades
|
|
81
|
+
3. **Design docs** — Add a design doc if the schema change represents a significant architectural decision
|
|
82
|
+
|
|
83
|
+
## Validation
|
|
84
|
+
|
|
85
|
+
After updating docs, verify:
|
|
86
|
+
|
|
87
|
+
1. **No broken links** — Check that referenced files exist
|
|
88
|
+
2. **Consistent terminology** — Use the same names as the code
|
|
89
|
+
3. **Up-to-date grades** — Quality scores reflect actual state
|
|
90
|
+
4. **AGENTS.md fits in context** — Keep it under ~200 lines
|
|
91
|
+
|
|
92
|
+
## Checklist
|
|
93
|
+
|
|
94
|
+
- [ ] Identified which documents need updating
|
|
95
|
+
- [ ] AGENTS.md updated (if structure or workflow change)
|
|
96
|
+
- [ ] QUALITY_SCORE.md grades updated
|
|
97
|
+
- [ ] Design docs updated (if architectural decision)
|
|
98
|
+
- [ ] No broken internal links
|
|
99
|
+
- [ ] Execution plan marked as complete (if applicable)
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
# Project Agent Guide
|
|
2
|
+
|
|
3
|
+
> First file any agent reads. Fits in a single context window by design.
|
|
4
|
+
> This file is generated by Mars and customized per project.
|
|
5
|
+
|
|
6
|
+
## Overview
|
|
7
|
+
|
|
8
|
+
This is a Next.js SaaS application generated by [Mars](https://github.com/mars-js/mars). It includes authentication, role-based access control, design tokens, structured logging, and rate limiting out of the box.
|
|
9
|
+
|
|
10
|
+
**Tech stack:** Next.js 16 (App Router), TypeScript, Prisma (PostgreSQL), Tailwind CSS v4, Zod, Vitest, Playwright.
|
|
11
|
+
|
|
12
|
+
## Architecture (Layer Model)
|
|
13
|
+
|
|
14
|
+
```
|
|
15
|
+
Presentation src/app/ Pages, layouts, API routes (may import from any layer)
|
|
16
|
+
↓
|
|
17
|
+
Features src/features/ Feature modules with components/, server/, hooks/
|
|
18
|
+
↓
|
|
19
|
+
Library src/lib/ Wiring layer: mars.ts (core re-exports), prisma.ts
|
|
20
|
+
↓
|
|
21
|
+
Packages @mars-stack/core Auth, email, rate limiting, env, logging, SEO
|
|
22
|
+
@mars-stack/ui Primitives, patterns, hooks, design tokens
|
|
23
|
+
↓
|
|
24
|
+
Database prisma/schema/ Prisma schema files (base.prisma, auth.prisma, ...)
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
**Dependency direction is strictly top-down.** Features never import from other features. Shared logic goes in `src/lib/`.
|
|
28
|
+
|
|
29
|
+
## Directory Structure
|
|
30
|
+
|
|
31
|
+
```
|
|
32
|
+
src/
|
|
33
|
+
├── app/ Next.js App Router
|
|
34
|
+
│ ├── (auth)/ Auth pages (sign-in, register, forgot/reset password, verify)
|
|
35
|
+
│ ├── (protected)/ Authenticated pages (dashboard, settings)
|
|
36
|
+
│ ├── api/ API routes
|
|
37
|
+
│ │ ├── auth/ Public auth endpoints (login, signup, forgot, reset, verify)
|
|
38
|
+
│ │ ├── protected/ Authenticated API routes (must use withAuth/withRole/withOwnership)
|
|
39
|
+
│ │ └── csrf/ CSRF token endpoint
|
|
40
|
+
│ └── layout.tsx Root layout with providers
|
|
41
|
+
├── config/
|
|
42
|
+
│ ├── app.config.ts Single source of truth: identity, features, services, theme
|
|
43
|
+
│ └── routes.ts Route constants
|
|
44
|
+
├── features/
|
|
45
|
+
│ └── auth/ Auth feature module
|
|
46
|
+
│ ├── context/ AuthContext provider
|
|
47
|
+
│ ├── server/ Server-only auth logic (user queries, session, consent)
|
|
48
|
+
│ └── validators.ts Zod validation schemas
|
|
49
|
+
├── lib/
|
|
50
|
+
│ ├── mars.ts Re-exports from @mars-stack/core (logger, auth middleware, email, etc.)
|
|
51
|
+
│ └── prisma.ts Prisma client singleton
|
|
52
|
+
├── styles/
|
|
53
|
+
│ ├── globals.css CSS entry point
|
|
54
|
+
│ └── brand.css Brand token overrides
|
|
55
|
+
└── proxy.ts Route protection, CSRF validation, auth redirects (Next.js 16 proxy)
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## Key Constraints
|
|
59
|
+
|
|
60
|
+
1. **Protected API routes must use auth middleware** — `withAuth`, `withRole`, or `withOwnership` from `@/lib/mars`.
|
|
61
|
+
2. **Never import across features** — `src/features/billing/` cannot import from `src/features/auth/`. Shared logic goes in `src/lib/`.
|
|
62
|
+
3. **Use design tokens, not raw colors** — No `bg-blue-500`. Use semantic tokens from `@mars-stack/ui/styles`.
|
|
63
|
+
4. **Server modules must import `"server-only"`** — Any file under `*/server/` needs `import 'server-only'` as its first import.
|
|
64
|
+
5. **Parse data at the boundary** — Zod validation on every API route input.
|
|
65
|
+
6. **User-scoped queries use session userId** — Never accept userId from request params for authorization.
|
|
66
|
+
7. **Constant-time comparison for secrets** — Use `constantTimeEqual` from `@mars-stack/core`, never `===`.
|
|
67
|
+
|
|
68
|
+
## Config
|
|
69
|
+
|
|
70
|
+
`src/config/app.config.ts` is the single source of truth for:
|
|
71
|
+
- App identity (name, URL, support email)
|
|
72
|
+
- Feature flags (auth, admin, billing, notifications, etc.)
|
|
73
|
+
- Service providers (email, storage, database, payments, etc.)
|
|
74
|
+
- Theme (primary/secondary color, font, design direction)
|
|
75
|
+
|
|
76
|
+
## How to Run
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
yarn dev # Start Next.js dev server
|
|
80
|
+
yarn build # Production build
|
|
81
|
+
yarn test # Run Vitest unit tests
|
|
82
|
+
yarn test:e2e # Run Playwright e2e tests
|
|
83
|
+
yarn lint # ESLint
|
|
84
|
+
yarn db:push # Push Prisma schema to database
|
|
85
|
+
yarn db:generate # Generate Prisma client
|
|
86
|
+
yarn db:seed # Seed database
|
|
87
|
+
yarn db:studio # Open Prisma Studio
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
## Working Discipline
|
|
91
|
+
|
|
92
|
+
1. **Commit after every step.** When executing a plan, commit after each completed task — not at the end. Version control is the progress log. Each commit message should reference the plan and step (e.g., `feat(billing): add Subscription model (billing-plan step 1.1)`).
|
|
93
|
+
2. **Document every executed plan.** All plans — whether they started in a chat, a Cursor plan file, or someone's head — must end up in `docs/exec-plans/completed/` with decisions, discoveries, and outcomes recorded. Use the `capture-conversation-context` skill.
|
|
94
|
+
3. **Feed conversations back.** Decisions, discoveries, and context that live only in chat threads are invisible to future agents. See [docs/design-docs/conversation-as-system-record.md](docs/design-docs/conversation-as-system-record.md).
|
|
95
|
+
|
|
96
|
+
## Pointers
|
|
97
|
+
|
|
98
|
+
- **Architecture details:** [docs/design-docs/](docs/design-docs/)
|
|
99
|
+
- **Core beliefs:** [docs/design-docs/core-beliefs.md](docs/design-docs/core-beliefs.md)
|
|
100
|
+
- **Conversation feedback:** [docs/design-docs/conversation-as-system-record.md](docs/design-docs/conversation-as-system-record.md)
|
|
101
|
+
- **Execution plans:** [docs/exec-plans/](docs/exec-plans/)
|
|
102
|
+
- **Quality scores:** [docs/QUALITY_SCORE.md](docs/QUALITY_SCORE.md)
|
|
103
|
+
- **Skills:** [.cursor/skills/](.cursor/skills/)
|
|
104
|
+
- **Rules:** [.cursor/rules/](.cursor/rules/)
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
# Application Architecture
|
|
2
|
+
|
|
3
|
+
## Layer Model
|
|
4
|
+
|
|
5
|
+
```mermaid
|
|
6
|
+
flowchart TD
|
|
7
|
+
Presentation["Presentation Layer<br/>src/app/ — Pages, layouts, API routes"]
|
|
8
|
+
Features["Feature Layer<br/>src/features/ — Domain modules"]
|
|
9
|
+
Library["Library Layer<br/>src/lib/ — Wiring (mars.ts, prisma.ts)"]
|
|
10
|
+
Packages["Package Layer<br/>@mars-stack/core, @mars-stack/ui"]
|
|
11
|
+
Database["Database Layer<br/>prisma/schema/ — Prisma models"]
|
|
12
|
+
|
|
13
|
+
Presentation --> Features
|
|
14
|
+
Presentation --> Library
|
|
15
|
+
Presentation --> Packages
|
|
16
|
+
Features --> Library
|
|
17
|
+
Features --> Packages
|
|
18
|
+
Library --> Packages
|
|
19
|
+
Packages --> Database
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
**Dependency direction is strictly top-down.** A lower layer never imports from a higher layer. Features never import from other features.
|
|
23
|
+
|
|
24
|
+
## Cross-Cutting Concerns
|
|
25
|
+
|
|
26
|
+
All cross-cutting infrastructure is wired through `configureMars()` in `src/lib/mars.ts`:
|
|
27
|
+
|
|
28
|
+
| Concern | Mechanism | Import |
|
|
29
|
+
|---------|-----------|--------|
|
|
30
|
+
| Authentication | JWE sessions (HKDF + A256GCM) | `@/lib/mars` → `withAuth`, `withRole`, `withOwnership` |
|
|
31
|
+
| CSRF Protection | HMAC tokens with session fingerprint binding | `@/lib/mars` → `generateCSRFToken`, `validateCSRFRequest` |
|
|
32
|
+
| Logging | Pino structured logging (domain-scoped loggers) | `@/lib/mars` → `logger`, `authLogger`, `apiLogger` |
|
|
33
|
+
| Error Handling | Typed API errors with consistent response format | `@/lib/mars` → `handleApiError` |
|
|
34
|
+
| Rate Limiting | Upstash Redis with in-memory fallback | `@mars-stack/core/rate-limit` |
|
|
35
|
+
| Email | Provider interface (SendGrid, Resend, Console) | `@/lib/mars` → `sendEmail` |
|
|
36
|
+
| SEO | JSON-LD structured data builders | `@/lib/mars` → `buildOrganizationJsonLd`, etc. |
|
|
37
|
+
| Env Validation | Zod-based lazy proxy with build-time relaxation | `@/lib/mars` → `env`, `validateJWTSecret` |
|
|
38
|
+
|
|
39
|
+
## Database Schema
|
|
40
|
+
|
|
41
|
+
Prisma schema files live in `prisma/schema/`:
|
|
42
|
+
|
|
43
|
+
- `base.prisma` — Datasource config, generator config
|
|
44
|
+
- `auth.prisma` — User, Session, VerificationToken models
|
|
45
|
+
|
|
46
|
+
**Naming conventions:** Models are PascalCase singular (`User`, not `Users`). Fields are camelCase. Relations use the model name as the field name.
|
|
47
|
+
|
|
48
|
+
**Entity relationships:**
|
|
49
|
+
```
|
|
50
|
+
User 1──* Session (active sessions per user)
|
|
51
|
+
User 1──* VerificationToken (email verification / password reset)
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## API Route Conventions
|
|
55
|
+
|
|
56
|
+
**Public auth routes** (`src/app/api/auth/`):
|
|
57
|
+
- Rate limited via `checkRateLimit`
|
|
58
|
+
- Validate input with Zod schemas
|
|
59
|
+
- Return consistent JSON responses via `handleApiError`
|
|
60
|
+
|
|
61
|
+
**Protected routes** (`src/app/api/protected/`):
|
|
62
|
+
- Wrapped with `withAuth`, `withRole`, or `withOwnership`
|
|
63
|
+
- UserId comes from the verified session, never from request params
|
|
64
|
+
- CSRF validation handled by middleware for state-changing methods
|
|
65
|
+
|
|
66
|
+
**Handler pattern:**
|
|
67
|
+
```typescript
|
|
68
|
+
import { withAuth } from '@/lib/mars';
|
|
69
|
+
|
|
70
|
+
export const GET = withAuth(async (request, context, session) => {
|
|
71
|
+
// session.userId is verified
|
|
72
|
+
// Use prisma with userId in where clause
|
|
73
|
+
});
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## Client-Side Patterns
|
|
77
|
+
|
|
78
|
+
- **AuthContext:** Provides `user`, `isLoading`, `logout` via React Context. Wraps the app in `src/app/providers.tsx`.
|
|
79
|
+
- **Forms:** `useZodForm` hook from `@mars-stack/ui/hooks` for type-safe form state with Zod validation.
|
|
80
|
+
- **Server vs Client Components:** Pages are server components by default. Interactive components use `"use client"` directive. Auth state comes from AuthContext (client) or session verification (server).
|
|
81
|
+
|
|
82
|
+
## Design Token System
|
|
83
|
+
|
|
84
|
+
Three-layer OKLCH system:
|
|
85
|
+
|
|
86
|
+
1. **Primitives** (`@mars-stack/ui/styles/primitives.css`): Raw OKLCH color scales — `--brand-50` through `--brand-950`, plus neutral, feedback colors
|
|
87
|
+
2. **Semantic Tokens** (`@mars-stack/ui/styles/tokens.css`): Intent-mapped tokens — `--surface-primary`, `--text-primary`, `--border-default`, etc. Includes `.dark` overrides.
|
|
88
|
+
3. **Theme** (`@mars-stack/ui/styles/theme.css`): Tailwind CSS v4 `@theme` block wiring semantic tokens to utility classes
|
|
89
|
+
|
|
90
|
+
**Override path:** Projects customize via `src/styles/brand.css` which overrides primitive values. The semantic layer automatically adapts.
|
|
91
|
+
|
|
92
|
+
## Proxy (Middleware)
|
|
93
|
+
|
|
94
|
+
`src/proxy.ts` runs on every matched request (Next.js 16 uses `proxy.ts` instead of `middleware.ts`):
|
|
95
|
+
|
|
96
|
+
1. Coming-soon mode redirect
|
|
97
|
+
2. JWT_SECRET validation
|
|
98
|
+
3. Session decryption (non-blocking — null session = unauthenticated)
|
|
99
|
+
4. CSRF validation for state-changing API requests
|
|
100
|
+
5. Auth route redirect (authenticated users → dashboard)
|
|
101
|
+
6. Protected route redirect (unauthenticated users → sign-in with callback URL)
|
|
102
|
+
7. Admin route access control (role check from session)
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# Quality Score
|
|
2
|
+
|
|
3
|
+
Grades by domain. Updated as features are built and tested.
|
|
4
|
+
|
|
5
|
+
**Grading scale:**
|
|
6
|
+
- **A** — Complete, tested, documented, meets all quality bars
|
|
7
|
+
- **B** — Functional with minor gaps
|
|
8
|
+
- **C** — Partially implemented, needs significant work
|
|
9
|
+
- **D** — Scaffolded but not implemented
|
|
10
|
+
- **F** — Missing entirely
|
|
11
|
+
|
|
12
|
+
| Domain | Grade | Notes |
|
|
13
|
+
|--------|-------|-------|
|
|
14
|
+
| Auth API | A | Rate limited, CSRF protected, tested |
|
|
15
|
+
| Auth pages | B+ | All flows present. Terms/privacy links need real pages |
|
|
16
|
+
| Middleware | B | Route protection, CSRF validation, auth redirects |
|
|
17
|
+
| Dashboard | D | Placeholder — needs real content |
|
|
18
|
+
| Settings | D | Read-only — needs forms for profile, password, sessions |
|
|
19
|
+
| Navigation | D | Needs responsive nav shell |
|
|
20
|
+
| Accessibility | N/A | Audit pending |
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# Design Doc: Conversation as System Record
|
|
2
|
+
|
|
3
|
+
**Status:** Accepted
|
|
4
|
+
**Created:** 2026-03-11
|
|
5
|
+
|
|
6
|
+
## Problem
|
|
7
|
+
|
|
8
|
+
Agent conversations produce valuable context — architectural decisions, trade-off analysis, debugging insights, root cause analysis — that evaporates when the chat session ends. Future agents (and humans) working on the same codebase have no access to why decisions were made, what alternatives were considered, or what pitfalls were discovered.
|
|
9
|
+
|
|
10
|
+
This is the same problem the Harness Engineering team identified: *"Knowledge that lives in Google Docs, chat threads, or people's heads is not accessible to the system."* The solution for conversations is the same as for design docs and plans — feed them back into the repository.
|
|
11
|
+
|
|
12
|
+
## Principle
|
|
13
|
+
|
|
14
|
+
**Every significant agent conversation must produce a persistent artifact in the repository.** The artifact type depends on the conversation:
|
|
15
|
+
|
|
16
|
+
| Conversation Type | Artifact Location | Format |
|
|
17
|
+
|---|---|---|
|
|
18
|
+
| Planning / roadmap | `docs/exec-plans/active/` → `completed/` | Execution plan markdown |
|
|
19
|
+
| Architecture decision | `docs/design-docs/` | Design doc markdown |
|
|
20
|
+
| Bug investigation | `docs/exec-plans/active/` | Bug fix plan with root cause |
|
|
21
|
+
| Feature implementation | `docs/exec-plans/completed/` | Completed plan with decisions |
|
|
22
|
+
| Refactoring rationale | `docs/design-docs/` | Design doc explaining why |
|
|
23
|
+
| Quality/debt discovery | `docs/QUALITY_SCORE.md` + `docs/exec-plans/tech-debt.md` | Grade updates + debt items |
|
|
24
|
+
|
|
25
|
+
## What to Capture
|
|
26
|
+
|
|
27
|
+
Not every message needs to be saved. Capture the **decisions and their context**, not the transcript:
|
|
28
|
+
|
|
29
|
+
1. **What was decided** — the actual choice made
|
|
30
|
+
2. **What alternatives were considered** — and why they were rejected
|
|
31
|
+
3. **What was discovered** — bugs found, constraints identified, assumptions invalidated
|
|
32
|
+
4. **What changed** — files modified, architecture updated, quality scores adjusted
|
|
33
|
+
5. **What remains** — open questions, known limitations, follow-up work
|
|
34
|
+
|
|
35
|
+
## What NOT to Capture
|
|
36
|
+
|
|
37
|
+
- The conversational back-and-forth (tone, corrections, false starts)
|
|
38
|
+
- Implementation details already visible in the code diff
|
|
39
|
+
- Obvious choices that don't need justification
|
|
40
|
+
|
|
41
|
+
## Process
|
|
42
|
+
|
|
43
|
+
### During the conversation
|
|
44
|
+
|
|
45
|
+
1. When a significant decision is made, note it. Don't wait until the end.
|
|
46
|
+
2. When an unexpected constraint is discovered (e.g., "Next.js 16 uses proxy.ts, not middleware.ts"), document it immediately in the relevant artifact.
|
|
47
|
+
3. When a plan is being executed, update the plan file as tasks complete.
|
|
48
|
+
|
|
49
|
+
### At the end of the conversation
|
|
50
|
+
|
|
51
|
+
1. If the conversation produced a plan, ensure it's in `docs/exec-plans/` with correct status.
|
|
52
|
+
2. If architectural decisions were made, ensure they're in `docs/design-docs/` or in `ARCHITECTURE.md`.
|
|
53
|
+
3. If quality changed, update `docs/QUALITY_SCORE.md`.
|
|
54
|
+
4. If tech debt was discovered, update `docs/exec-plans/tech-debt.md`.
|
|
55
|
+
5. If core beliefs were validated or updated, update `docs/design-docs/core-beliefs.md`.
|
|
56
|
+
|
|
57
|
+
### For future agents
|
|
58
|
+
|
|
59
|
+
The `AGENTS.md` file points to `docs/` as the knowledge base. Future agents should:
|
|
60
|
+
|
|
61
|
+
1. Read `docs/exec-plans/completed/` to understand how the system was built and why
|
|
62
|
+
2. Read `docs/design-docs/` to understand architectural decisions
|
|
63
|
+
3. Read `docs/QUALITY_SCORE.md` to understand current state and priorities
|
|
64
|
+
4. Read `docs/exec-plans/tech-debt.md` to understand known limitations
|
|
65
|
+
|
|
66
|
+
## Origin
|
|
67
|
+
|
|
68
|
+
This design doc was created after executing the Mars Product Roadmap — a plan that served as the complete blueprint for building the Mars system. The plan itself was originally stored in a Cursor-specific file (`.cursor/plans/`), invisible to all future agents. Moving it to `docs/exec-plans/completed/mars-product-roadmap.md` was the first application of this principle.
|
|
69
|
+
|
|
70
|
+
The Mars Product Roadmap is the most important completed execution plan in the repository. It documents every phase of the system's construction, every cross-cutting concern, every key decision made during execution, and the influencing principles from the Harness Engineering article on agent-first development.
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# Core Beliefs
|
|
2
|
+
|
|
3
|
+
Architectural opinions that govern this project. Enforced mechanically where possible via linters and structural tests.
|
|
4
|
+
|
|
5
|
+
## Data Validation
|
|
6
|
+
|
|
7
|
+
- Parse data shapes at the boundary — Zod validation on every API route input.
|
|
8
|
+
- Fail fast with clear, structured error messages.
|
|
9
|
+
|
|
10
|
+
## Security
|
|
11
|
+
|
|
12
|
+
- Server-only modules must import `"server-only"` to prevent client-side bundling of secrets.
|
|
13
|
+
- User-scoped queries must include userId from the session, never from request parameters.
|
|
14
|
+
- Constant-time comparison (`constantTimeEqual`) for all secret/token comparisons.
|
|
15
|
+
- Derive keys with domain separators, never reuse JWT_SECRET directly.
|
|
16
|
+
|
|
17
|
+
## Architecture
|
|
18
|
+
|
|
19
|
+
- No cross-feature imports. Shared logic goes in `src/lib/`.
|
|
20
|
+
- `app.config.ts` is the single source of truth for feature flags and service providers.
|
|
21
|
+
- Composition over inheritance — `configureMars()` wires services via function composition.
|
|
22
|
+
|
|
23
|
+
## Technology
|
|
24
|
+
|
|
25
|
+
- Prefer boring, well-documented technology: Next.js, Prisma, Tailwind, Zod.
|
|
26
|
+
- Official SDKs over wrapper libraries for third-party integrations.
|
|
27
|
+
|
|
28
|
+
## Testing
|
|
29
|
+
|
|
30
|
+
- Every server function needs a corresponding test.
|
|
31
|
+
- Structural tests validate architecture invariants (dependency direction, auth coverage, design tokens).
|
|
32
|
+
|
|
33
|
+
## Accessibility
|
|
34
|
+
|
|
35
|
+
- All UI components must meet WCAG 2.1 AA (keyboard navigation, focus management, ARIA, contrast).
|
|
36
|
+
|
|
37
|
+
## Agent-First Development
|
|
38
|
+
|
|
39
|
+
- The repository is the system of record. All knowledge lives in code, docs, skills, and rules.
|
|
40
|
+
- Every agent conversation must feed decisions and context back into the repository as persistent artifacts (execution plans, design docs, quality score updates). See `docs/design-docs/conversation-as-system-record.md`.
|
|
41
|
+
- All executed plans are documented in `docs/exec-plans/completed/`. Plans are references for future agents — they record what was built, what decisions were made, and what was discovered.
|
|
42
|
+
- Commit after every step in a plan. Version control is the progress log. Do not batch all changes into one commit at the end.
|
|
43
|
+
- When documentation falls short, promote the rule into a linter with an agent-legible error message.
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
# Design Documents
|
|
2
|
+
|
|
3
|
+
Catalog of architectural decisions and design rationale for this project.
|
|
4
|
+
|
|
5
|
+
| Document | Status | Summary |
|
|
6
|
+
|----------|--------|---------|
|
|
7
|
+
| [core-beliefs.md](core-beliefs.md) | Active | Architectural operating principles |
|
|
8
|
+
| [conversation-as-system-record.md](conversation-as-system-record.md) | Accepted | Every conversation feeds back into the repo |
|
|
File without changes
|
|
File without changes
|