@bradtaylorsf/alpha-loop 1.1.2 → 1.1.3

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/dist/cli.js CHANGED
@@ -8,7 +8,7 @@ import { syncCommand } from './commands/sync.js';
8
8
  program
9
9
  .name('alpha-loop')
10
10
  .description('Agent-agnostic automated development loop')
11
- .version('1.1.2');
11
+ .version('1.1.3');
12
12
  program
13
13
  .command('init')
14
14
  .description('Full project onboarding: config, templates, vision, scan, sync')
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bradtaylorsf/alpha-loop",
3
- "version": "1.1.2",
3
+ "version": "1.1.3",
4
4
  "description": "Agent-agnostic automated development loop: Plan → Build → Test → Review → Ship",
5
5
  "type": "module",
6
6
  "packageManager": "pnpm@9.0.0",
@@ -3,7 +3,7 @@ name: implementer
3
3
  description: Implements GitHub issues by writing code, tests, and committing. The primary coding agent in the loop.
4
4
  tools: Read, Write, Edit, Glob, Grep, Bash
5
5
  model: opus
6
- skills: api-patterns, api-contracts, testing-patterns, jest-mock-patterns, implementation-planning, git-workflow, sqlite-patterns, security-analysis
6
+ skills: testing-patterns, test-robustness, implementation-planning, git-workflow, security-analysis
7
7
  ---
8
8
 
9
9
  # Implementer Agent
@@ -1,676 +0,0 @@
1
- ---
2
- name: api-contracts
3
- description: Shared type contracts between backend and frontend to prevent API response mismatches. Essential for all full-stack features.
4
- auto_load: backend-developer, frontend-developer, integration-specialist
5
- priority: critical
6
- ---
7
-
8
- # API Contracts Skill
9
-
10
- ## Quick Reference
11
-
12
- **Use when**: Implementing ANY feature that spans backend and frontend
13
-
14
- **Purpose**: Prevent API response mismatches by defining shared TypeScript interfaces
15
-
16
- **Key Pattern**: Single source of truth for API response types used by both backend and frontend
17
-
18
- **Critical**: This skill prevents the "Test-Reality Gap" where unit tests pass but production fails due to API contract mismatches.
19
-
20
- ---
21
-
22
- ## The Problem This Solves
23
-
24
- ### Before Shared Contracts (Causes Production Failures)
25
-
26
- **Backend** (`src/server/routes/dependencies.ts`):
27
- ```typescript
28
- // Backend developer's assumption
29
- router.get('/dependency-graph', (req, res) => {
30
- const nodes = {}; // Returns Object
31
- features.forEach(f => {
32
- nodes[f.id] = { id: f.id, dependsOn: f.depends_on_features };
33
- });
34
- res.json({ nodes });
35
- });
36
- ```
37
-
38
- **Frontend** (`src/client/components/DependencyGraph.tsx`):
39
- ```typescript
40
- // Frontend developer's assumption
41
- const graph = await response.json();
42
- graph.nodes.forEach(node => { // ❌ CRASH: nodes is Object, not Array
43
- renderNode(node.name); // ❌ CRASH: name field doesn't exist
44
- });
45
- ```
46
-
47
- **Result**: All unit tests pass (mocked data is "perfect"), but production fails with runtime errors.
48
-
49
- ### After Shared Contracts (Prevents Failures)
50
-
51
- **Shared Contract** (`src/shared/types/api-contracts.ts`):
52
- ```typescript
53
- export interface DependencyGraphResponse {
54
- nodes: DependencyNode[]; // ✅ Both backend and frontend agree: Array
55
- }
56
-
57
- export interface DependencyNode {
58
- id: number;
59
- name: string; // ✅ Frontend knows this field exists
60
- dependsOn: number[];
61
- }
62
- ```
63
-
64
- **Backend** (TypeScript enforces compliance):
65
- ```typescript
66
- import { DependencyGraphResponse } from '../shared/types/api-contracts';
67
-
68
- router.get('/dependency-graph', (req, res) => {
69
- const response: DependencyGraphResponse = {
70
- nodes: features.map(f => ({
71
- id: f.id,
72
- name: f.description, // ✅ Must include name field
73
- dependsOn: f.depends_on_features || []
74
- })) // ✅ Must be Array
75
- };
76
- res.json(response); // ✅ TypeScript validates structure
77
- });
78
- ```
79
-
80
- **Frontend** (TypeScript knows exact structure):
81
- ```typescript
82
- import { DependencyGraphResponse } from '../../shared/types/api-contracts';
83
-
84
- const graph: DependencyGraphResponse = await response.json();
85
- graph.nodes.forEach(node => { // ✅ TypeScript knows nodes is Array
86
- renderNode(node.name); // ✅ TypeScript knows name exists
87
- });
88
- ```
89
-
90
- ---
91
-
92
- ## Pattern: Creating Shared Contracts
93
-
94
- ### Step 1: Create Shared Types Directory
95
-
96
- ```bash
97
- # Create directory structure
98
- mkdir -p src/shared/types
99
-
100
- # Create api-contracts.ts
101
- touch src/shared/types/api-contracts.ts
102
- ```
103
-
104
- ### Step 2: Define Complete API Response Interface
105
-
106
- ```typescript
107
- // src/shared/types/api-contracts.ts
108
-
109
- /**
110
- * GET /api/projects/:id/features/dependency-graph
111
- *
112
- * Returns dependency graph data for visualization.
113
- *
114
- * Used by:
115
- * - Backend: src/server/routes/dependencies.ts
116
- * - Frontend: src/client/components/DependencyGraph.tsx
117
- *
118
- * @example
119
- * {
120
- * nodes: [
121
- * { id: 1, name: "Authentication", status: "completed", dependsOn: [], blocks: [2] },
122
- * { id: 2, name: "User Profile", status: "pending", dependsOn: [1], blocks: [] }
123
- * ],
124
- * edges: [
125
- * { from: 2, to: 1, type: "dependency" }
126
- * ],
127
- * hasCircularDependencies: false,
128
- * circularDependencies: []
129
- * }
130
- */
131
- export interface DependencyGraphResponse {
132
- /** List of all features as graph nodes */
133
- nodes: DependencyNode[];
134
-
135
- /** List of edges connecting nodes */
136
- edges: DependencyEdge[];
137
-
138
- /** Map of feature ID to count of unmet dependencies */
139
- unmetDependencies: Record<number, number>;
140
-
141
- /** Whether circular dependencies exist */
142
- hasCircularDependencies: boolean;
143
-
144
- /** List of detected circular dependency cycles */
145
- circularDependencies: CircularDependency[];
146
- }
147
-
148
- /**
149
- * Single feature node in dependency graph
150
- */
151
- export interface DependencyNode {
152
- /** Feature ID */
153
- id: number;
154
-
155
- /** Feature name (displayed in graph node) */
156
- name: string;
157
-
158
- /** Full feature description (shown in tooltip) */
159
- description: string;
160
-
161
- /** Current implementation status */
162
- status: 'pending' | 'in_progress' | 'completed' | 'blocked';
163
-
164
- /** Category for color coding */
165
- category: string;
166
-
167
- /** List of feature IDs this feature depends on */
168
- dependsOn: number[];
169
-
170
- /** List of feature IDs this feature blocks */
171
- blocks: number[];
172
- }
173
-
174
- /**
175
- * Edge connecting two nodes in the graph
176
- */
177
- export interface DependencyEdge {
178
- /** Source node ID */
179
- from: number;
180
-
181
- /** Target node ID */
182
- to: number;
183
-
184
- /** Edge type for styling */
185
- type: 'dependency' | 'blocks';
186
- }
187
-
188
- /**
189
- * Circular dependency detection result
190
- */
191
- export interface CircularDependency {
192
- /** List of feature IDs forming the cycle */
193
- cycle: number[];
194
-
195
- /** Human-readable description */
196
- description: string;
197
- }
198
- ```
199
-
200
- ### Step 3: Document Example Responses
201
-
202
- Always include `@example` JSDoc tag with realistic response data:
203
-
204
- ```typescript
205
- /**
206
- * User authentication response
207
- *
208
- * @example
209
- * {
210
- * user: {
211
- * id: 123,
212
- * email: "user@example.com",
213
- * name: "John Doe",
214
- * role: "admin"
215
- * },
216
- * token: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
217
- * expiresAt: 1640995200000
218
- * }
219
- */
220
- export interface AuthResponse {
221
- user: User;
222
- token: string;
223
- expiresAt: number;
224
- }
225
- ```
226
-
227
- ### Step 4: Mark Required vs Optional Fields
228
-
229
- ```typescript
230
- export interface UpdateUserRequest {
231
- /** User's email (REQUIRED) */
232
- email: string;
233
-
234
- /** User's display name (REQUIRED) */
235
- name: string;
236
-
237
- /** User's age (OPTIONAL) */
238
- age?: number;
239
-
240
- /** User's bio (OPTIONAL - can be empty string) */
241
- bio?: string;
242
- }
243
- ```
244
-
245
- ---
246
-
247
- ## Pattern: Backend Usage
248
-
249
- ### Import and Use Shared Contract
250
-
251
- ```typescript
252
- // src/server/routes/dependencies.ts
253
-
254
- import { Router } from 'express';
255
- import {
256
- DependencyGraphResponse,
257
- DependencyNode,
258
- DependencyEdge
259
- } from '../shared/types/api-contracts';
260
- import { getDependencyGraph } from '../dal/dependency-resolver';
261
-
262
- const router = Router();
263
-
264
- router.get('/api/projects/:id/features/dependency-graph', (req, res) => {
265
- const projectId = parseInt(req.params.id, 10);
266
-
267
- // Get data from DAL
268
- const { nodes, edges, cycles } = getDependencyGraph(projectId);
269
-
270
- // Build response matching shared contract
271
- const response: DependencyGraphResponse = {
272
- nodes: nodes.map((node): DependencyNode => ({
273
- id: node.id,
274
- name: node.name || `Feature ${node.id}`,
275
- description: node.description || '',
276
- status: node.status,
277
- category: node.category || 'general',
278
- dependsOn: node.dependsOn || [],
279
- blocks: node.blocks || []
280
- })),
281
- edges: edges.map((edge): DependencyEdge => ({
282
- from: edge.from,
283
- to: edge.to,
284
- type: edge.type
285
- })),
286
- unmetDependencies: {},
287
- hasCircularDependencies: cycles.length > 0,
288
- circularDependencies: cycles
289
- };
290
-
291
- // TypeScript validates response matches contract
292
- res.json(response);
293
- });
294
-
295
- export default router;
296
- ```
297
-
298
- ### Ensure ALL Required Fields Present
299
-
300
- ```typescript
301
- // ❌ WRONG - Missing required fields
302
- const response: DependencyGraphResponse = {
303
- nodes: nodes.map(n => ({ id: n.id })) // TypeScript error: missing name, description, status, etc.
304
- };
305
-
306
- // ✅ CORRECT - All required fields provided
307
- const response: DependencyGraphResponse = {
308
- nodes: nodes.map(n => ({
309
- id: n.id,
310
- name: n.description, // Map from database field
311
- description: n.full_description || '', // Provide default if missing
312
- status: n.passes ? 'completed' : 'pending',
313
- category: n.category || 'general',
314
- dependsOn: n.depends_on_features || [], // Provide empty array if null
315
- blocks: n.blocks_features || []
316
- }))
317
- };
318
- ```
319
-
320
- ---
321
-
322
- ## Pattern: Frontend Usage
323
-
324
- ### Import and Use Shared Contract
325
-
326
- ```typescript
327
- // src/client/components/DependencyGraph.tsx
328
-
329
- import React, { useState, useEffect } from 'react';
330
- import {
331
- DependencyGraphResponse,
332
- DependencyNode
333
- } from '../../shared/types/api-contracts';
334
-
335
- export function DependencyGraph({ projectId }: { projectId: number }) {
336
- const [graph, setGraph] = useState<DependencyGraphResponse | null>(null);
337
- const [error, setError] = useState<string | null>(null);
338
-
339
- useEffect(() => {
340
- async function fetchGraph() {
341
- try {
342
- const response = await fetch(`/api/projects/${projectId}/features/dependency-graph`);
343
-
344
- if (!response.ok) {
345
- throw new Error(`HTTP ${response.status}`);
346
- }
347
-
348
- // TypeScript knows exact shape of response
349
- const data: DependencyGraphResponse = await response.json();
350
- setGraph(data);
351
- } catch (err) {
352
- setError(err instanceof Error ? err.message : 'Unknown error');
353
- }
354
- }
355
-
356
- fetchGraph();
357
- }, [projectId]);
358
-
359
- if (error) {
360
- return <div className="error">Error loading graph: {error}</div>;
361
- }
362
-
363
- if (!graph) {
364
- return <div>Loading...</div>;
365
- }
366
-
367
- return (
368
- <div className="dependency-graph">
369
- {/* TypeScript knows nodes is Array with specific fields */}
370
- {graph.nodes.map((node: DependencyNode) => (
371
- <div key={node.id} className="graph-node">
372
- <h3>{node.name}</h3>
373
- <p>{node.description}</p>
374
- <span className={`status-${node.status}`}>{node.status}</span>
375
- </div>
376
- ))}
377
- </div>
378
- );
379
- }
380
- ```
381
-
382
- ### TypeScript Catches Errors at Compile Time
383
-
384
- ```typescript
385
- // ❌ TypeScript error: nodes might be undefined
386
- graph.nodes.map(node => ...);
387
-
388
- // ✅ CORRECT: Check for null
389
- {graph?.nodes.map(node => ...)}
390
-
391
- // ❌ TypeScript error: field doesn't exist on DependencyNode
392
- node.title
393
-
394
- // ✅ CORRECT: Use correct field from contract
395
- node.name
396
-
397
- // ❌ TypeScript error: wrong status value
398
- node.status === 'done'
399
-
400
- // ✅ CORRECT: Use value from contract's union type
401
- node.status === 'completed'
402
- ```
403
-
404
- ---
405
-
406
- ## Pattern: Updating Contracts
407
-
408
- When API changes, update contract FIRST, then let TypeScript guide you:
409
-
410
- ### 1. Update Shared Contract
411
-
412
- ```typescript
413
- // src/shared/types/api-contracts.ts
414
-
415
- export interface DependencyNode {
416
- id: number;
417
- name: string;
418
- description: string;
419
- status: 'pending' | 'in_progress' | 'completed' | 'blocked';
420
- category: string;
421
- dependsOn: number[];
422
- blocks: number[];
423
- priority: 'low' | 'medium' | 'high'; // ✅ NEW FIELD ADDED
424
- }
425
- ```
426
-
427
- ### 2. TypeScript Shows Compilation Errors
428
-
429
- ```bash
430
- $ pnpm build
431
-
432
- src/server/routes/dependencies.ts:45:7 - error TS2322:
433
- Type '{ id: number; name: string; ... }' is not assignable to type 'DependencyNode'.
434
- Property 'priority' is missing in type '...' but required in type 'DependencyNode'.
435
-
436
- src/client/components/DependencyGraph.tsx:67:23 - error TS2339:
437
- Property 'priority' does not exist on type '{ id: number; name: string; ... }'.
438
- ```
439
-
440
- ### 3. Fix Backend First
441
-
442
- ```typescript
443
- // src/server/routes/dependencies.ts
444
-
445
- nodes: nodes.map(n => ({
446
- // ... existing fields
447
- priority: n.priority || 'medium' // ✅ Add new field
448
- }))
449
- ```
450
-
451
- ### 4. Fix Frontend Second
452
-
453
- ```typescript
454
- // src/client/components/DependencyGraph.tsx
455
-
456
- <div className="graph-node">
457
- <h3>{node.name}</h3>
458
- <span className={`priority-${node.priority}`}>{node.priority}</span> {/* ✅ Use new field */}
459
- </div>
460
- ```
461
-
462
- ### 5. Compilation Succeeds
463
-
464
- All TypeScript errors resolved. Contract updated everywhere.
465
-
466
- ---
467
-
468
- ## Common Contracts to Create
469
-
470
- ### 1. List/Collection Responses
471
-
472
- ```typescript
473
- /**
474
- * GET /api/projects/:id/features
475
- */
476
- export interface FeaturesListResponse {
477
- features: Feature[];
478
- total: number;
479
- page: number;
480
- pageSize: number;
481
- }
482
- ```
483
-
484
- ### 2. Single Resource Responses
485
-
486
- ```typescript
487
- /**
488
- * GET /api/projects/:id
489
- */
490
- export interface ProjectResponse {
491
- project: Project;
492
- }
493
- ```
494
-
495
- ### 3. Create/Update Requests
496
-
497
- ```typescript
498
- /**
499
- * POST /api/projects
500
- */
501
- export interface CreateProjectRequest {
502
- name: string;
503
- description?: string;
504
- techStack: {
505
- frontend: string;
506
- backend: string;
507
- database: string;
508
- };
509
- }
510
-
511
- export interface CreateProjectResponse {
512
- project: Project;
513
- message: string;
514
- }
515
- ```
516
-
517
- ### 4. Error Responses
518
-
519
- ```typescript
520
- /**
521
- * Standard error response (4xx, 5xx)
522
- */
523
- export interface ErrorResponse {
524
- error: string;
525
- message: string;
526
- statusCode: number;
527
- details?: Record<string, string[]>; // Validation errors
528
- }
529
- ```
530
-
531
- ---
532
-
533
- ## Validation Pattern
534
-
535
- Always validate API responses match contracts:
536
-
537
- ```typescript
538
- // tests/api/contracts/dependency-graph.contract.test.ts
539
-
540
- import { DependencyGraphResponse, DependencyNode } from '../../../src/shared/types/api-contracts';
541
-
542
- describe('Dependency Graph API Contract', () => {
543
- it('returns response matching DependencyGraphResponse interface', async () => {
544
- const response = await fetch('http://localhost:4243/api/projects/1/features/dependency-graph');
545
- expect(response.ok).toBe(true);
546
-
547
- const data = await response.json();
548
-
549
- // Validate top-level structure
550
- expect(data).toHaveProperty('nodes');
551
- expect(data).toHaveProperty('edges');
552
- expect(data).toHaveProperty('hasCircularDependencies');
553
-
554
- // Validate types
555
- expect(Array.isArray(data.nodes)).toBe(true);
556
- expect(Array.isArray(data.edges)).toBe(true);
557
- expect(typeof data.hasCircularDependencies).toBe('boolean');
558
-
559
- // Validate node structure (if any nodes exist)
560
- if (data.nodes.length > 0) {
561
- const node = data.nodes[0];
562
- expect(typeof node.id).toBe('number');
563
- expect(typeof node.name).toBe('string');
564
- expect(typeof node.description).toBe('string');
565
- expect(['pending', 'in_progress', 'completed', 'blocked']).toContain(node.status);
566
- expect(Array.isArray(node.dependsOn)).toBe(true);
567
- expect(Array.isArray(node.blocks)).toBe(true);
568
- }
569
- });
570
- });
571
- ```
572
-
573
- ---
574
-
575
- ## Checklist for Every Full-Stack Feature
576
-
577
- Before implementation starts:
578
- - [ ] Shared contract created in `src/shared/types/api-contracts.ts`
579
- - [ ] All request/response interfaces defined
580
- - [ ] Example responses documented with `@example` JSDoc
581
- - [ ] Required vs optional fields clearly marked
582
-
583
- During backend implementation:
584
- - [ ] Backend imports shared contract types
585
- - [ ] Backend response typed as contract interface
586
- - [ ] All required fields provided (no TypeScript errors)
587
- - [ ] Contract validation test written
588
-
589
- During frontend implementation:
590
- - [ ] Frontend imports shared contract types
591
- - [ ] API response typed as contract interface
592
- - [ ] TypeScript shows autocomplete for all fields
593
- - [ ] No `any` types used
594
-
595
- Before marking complete:
596
- - [ ] TypeScript compiles without errors
597
- - [ ] Contract tests pass
598
- - [ ] Integration tests pass
599
- - [ ] Manual browser verification shows no errors
600
-
601
- ---
602
-
603
- ## Common Mistakes to Avoid
604
-
605
- ### Mistake 1: Forgetting to Create Contract
606
-
607
- ```typescript
608
- // ❌ WRONG - No shared contract
609
- // backend.ts
610
- res.json({ nodes: Object.values(graph) });
611
-
612
- // frontend.ts
613
- const data: any = await response.json(); // No type safety
614
- ```
615
-
616
- **Fix**: Create shared contract first, before any implementation.
617
-
618
- ### Mistake 2: Backend and Frontend Use Different Interfaces
619
-
620
- ```typescript
621
- // ❌ WRONG - Duplicated interfaces
622
- // backend/types.ts
623
- interface GraphResponse { nodes: Node[] }
624
-
625
- // frontend/types.ts
626
- interface GraphData { items: Node[] } // Different name!
627
- ```
628
-
629
- **Fix**: Use SINGLE shared interface imported by both.
630
-
631
- ### Mistake 3: Using `any` Instead of Contract
632
-
633
- ```typescript
634
- // ❌ WRONG - Loses type safety
635
- const data: any = await response.json();
636
-
637
- // ✅ CORRECT - Use contract type
638
- const data: DependencyGraphResponse = await response.json();
639
- ```
640
-
641
- ### Mistake 4: Contract Doesn't Match Reality
642
-
643
- ```typescript
644
- // ❌ WRONG - Contract says Array, backend returns Object
645
- export interface Response {
646
- nodes: Node[]; // Contract says Array
647
- }
648
-
649
- // backend.ts
650
- res.json({ nodes: { "1": node1, "2": node2 } }); // Returns Object!
651
- ```
652
-
653
- **Fix**: Contract tests will catch this mismatch.
654
-
655
- ---
656
-
657
- ## Summary
658
-
659
- **Core Principle**: API contracts are the single source of truth for communication between backend and frontend.
660
-
661
- **When to Create**: Before starting ANY full-stack feature implementation.
662
-
663
- **Who Uses**:
664
- - Backend developers: Type their responses
665
- - Frontend developers: Type their API data
666
- - Integration specialists: Validate responses match
667
- - QA specialists: Write contract tests
668
-
669
- **Benefits**:
670
- - TypeScript catches mismatches at compile time
671
- - Eliminates runtime errors from unexpected API responses
672
- - Provides autocomplete in both backend and frontend
673
- - Self-documenting with JSDoc examples
674
- - Prevents the Test-Reality Gap
675
-
676
- **Remember**: Unit tests passing is NOT enough. Shared contracts + contract tests + integration tests = Production confidence.