@relipa/ai-flow-kit 0.0.6-beta.0 → 0.0.7-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/README.md +33 -13
  2. package/bin/aiflow.js +58 -2
  3. package/custom/mcp-presets/gitnexus.json +8 -0
  4. package/custom/rules/java/spring-boot-examples.md +329 -329
  5. package/custom/skills/generate-spec/SKILL.md +7 -0
  6. package/custom/skills/impact-analysis/SKILL.md +10 -0
  7. package/custom/skills/read-study-requirement/SKILL.md +11 -0
  8. package/custom/skills/review-plan/SKILL.md +15 -0
  9. package/custom/templates/spring-boot.md +224 -224
  10. package/docs/common/AIFLOW.md +12 -6
  11. package/docs/common/CHANGELOG.md +49 -5
  12. package/docs/common/QUICK_START.md +13 -11
  13. package/docs/common/cli-reference.md +23 -0
  14. package/package.json +2 -7
  15. package/scripts/checkpoint.js +46 -0
  16. package/scripts/doctor.js +192 -89
  17. package/scripts/gitnexus-worker.js +94 -0
  18. package/scripts/guide.js +42 -51
  19. package/scripts/hooks/session-start.js +274 -244
  20. package/scripts/hooks/session-stop.js +55 -0
  21. package/scripts/init.js +293 -18
  22. package/scripts/prompt.js +2 -2
  23. package/scripts/remove.js +54 -0
  24. package/scripts/task.js +446 -384
  25. package/scripts/update.js +14 -4
  26. package/scripts/use.js +41 -0
  27. package/upstream/.claude-plugin/marketplace.json +4 -4
  28. package/upstream/.claude-plugin/plugin.json +2 -4
  29. package/upstream/.cursor-plugin/plugin.json +2 -4
  30. package/upstream/docs/plans/2025-11-22-opencode-support-design.md +1 -1
  31. package/upstream/docs/plans/2025-11-28-skills-improvements-from-user-feedback.md +2 -2
  32. package/upstream/docs/testing.md +2 -2
  33. package/upstream/skills/subagent-driven-development/SKILL.md +5 -6
  34. package/upstream/skills/subagent-driven-development/implementer-prompt.md +2 -3
  35. package/upstream/skills/systematic-debugging/CREATION-LOG.md +1 -1
  36. package/upstream/skills/systematic-debugging/root-cause-tracing.md +1 -1
  37. package/upstream/skills/tdd-lean/SKILL.md +127 -0
  38. package/upstream/skills/using-git-worktrees/SKILL.md +4 -5
  39. package/upstream/skills/writing-plans/SKILL.md +3 -9
  40. package/upstream/tests/brainstorm-server/package-lock.json +36 -0
@@ -1,329 +1,329 @@
1
- # Spring Boot Code Examples
2
-
3
- Reference examples for each layer. Read the relevant section when generating code for that layer.
4
-
5
- ---
6
-
7
- ## Controller
8
-
9
- ```java
10
- // ✅ Good
11
- \@PostMapping
12
- public ResponseEntity<UserResponse> createUser(
13
- \@Valid \@RequestBody CreateUserRequest request) {
14
- UserResponse response = userService.create(request);
15
- return ResponseEntity.status(HttpStatus.CREATED).body(response);
16
- }
17
-
18
- // ❌ Bad — business logic in controller
19
- \@PostMapping
20
- public ResponseEntity<User> createUser(\@RequestBody User user) {
21
- if (userRepository.existsByEmail(user.getEmail())) {
22
- throw new RuntimeException("Email exists");
23
- }
24
- return ResponseEntity.ok(userRepository.save(user));
25
- }
26
- ```
27
-
28
- ---
29
-
30
- ## Service
31
-
32
- ```java
33
- // Interface
34
- public interface UserService {
35
- UserResponse create(CreateUserRequest request);
36
- UserResponse findById(Long id);
37
- }
38
-
39
- // Implementation
40
- \@Service
41
- \@RequiredArgsConstructor
42
- public class UserServiceImpl implements UserService {
43
-
44
- private final UserRepository userRepository;
45
- private final UserMapper userMapper;
46
-
47
- \@Override
48
- \@Transactional
49
- public UserResponse create(CreateUserRequest request) {
50
- if (userRepository.existsByEmail(request.getEmail())) {
51
- throw new DuplicateEmailException(request.getEmail());
52
- }
53
- User user = userMapper.toEntity(request);
54
- User saved = userRepository.save(user);
55
- return userMapper.toResponse(saved);
56
- }
57
-
58
- \@Override
59
- \@Transactional(readOnly = true)
60
- public UserResponse findById(Long id) {
61
- log.info("Finding user by id: {}", id);
62
- return userRepository.findById(id)
63
- .map(userMapper::toResponse)
64
- .orElseThrow(() -> {
65
- log.warn("User not found: id={}", id);
66
- return new ResourceNotFoundException("User", id);
67
- });
68
- }
69
-
70
- \@Transactional(readOnly = true)
71
- public Page<UserResponse> findAll(Pageable pageable) {
72
- return userRepository.findAll(pageable).map(userMapper::toResponse);
73
- }
74
- }
75
- ```
76
-
77
- ---
78
-
79
- ## Repository
80
-
81
- ```java
82
- // ✅ Good
83
- public interface UserRepository extends JpaRepository<User, Long> {
84
-
85
- boolean existsByEmail(String email);
86
-
87
- Optional<User> findByEmailAndDeletedFalse(String email);
88
-
89
- \@Query("SELECT u FROM User u JOIN FETCH u.roles WHERE u.id = :id")
90
- Optional<User> findByIdWithRoles(\@Param("id") Long id);
91
- }
92
-
93
- // ❌ Bad — N+1 problem
94
- List<Order> findAll(); // If Order has \@ManyToOne User, this causes N+1
95
- ```
96
-
97
- ---
98
-
99
- ## Entity
100
-
101
- ```java
102
- \@Entity
103
- \@Table(name = "users")
104
- \@Getter
105
- \@Setter
106
- \@NoArgsConstructor
107
- \@AllArgsConstructor
108
- \@Builder
109
- public class User extends BaseEntity {
110
-
111
- \@Id
112
- \@GeneratedValue(strategy = GenerationType.IDENTITY)
113
- private Long id;
114
-
115
- \@Column(name = "email", nullable = false, unique = true, length = 255)
116
- private String email;
117
-
118
- \@Column(name = "full_name", nullable = false, length = 100)
119
- private String fullName;
120
-
121
- \@Column(name = "deleted", nullable = false)
122
- \@Builder.Default
123
- private boolean deleted = false;
124
-
125
- \@OneToMany(mappedBy = "user", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
126
- private List<Order> orders = new ArrayList<>();
127
- }
128
- ```
129
-
130
- ---
131
-
132
- ## DTO
133
-
134
- ```java
135
- // Request DTO
136
- \@Getter
137
- \@NoArgsConstructor
138
- \@AllArgsConstructor
139
- \@Builder
140
- public class CreateUserRequest {
141
-
142
- \@NotBlank(message = "Email is required")
143
- \@Email(message = "Invalid email format")
144
- private String email;
145
-
146
- \@NotBlank(message = "Full name is required")
147
- \@Size(min = 2, max = 100, message = "Full name must be 2-100 characters")
148
- private String fullName;
149
-
150
- \@NotBlank(message = "Password is required")
151
- \@Size(min = 8, message = "Password must be at least 8 characters")
152
- private String password;
153
- }
154
-
155
- // Response DTO
156
- \@Getter
157
- \@NoArgsConstructor
158
- \@AllArgsConstructor
159
- \@Builder
160
- public class UserResponse {
161
- private Long id;
162
- private String email;
163
- private String fullName;
164
- private LocalDateTime createdAt;
165
- }
166
- ```
167
-
168
- ---
169
-
170
- ## MapStruct Mapper
171
-
172
- ```java
173
- \@Mapper(componentModel = "spring")
174
- public interface UserMapper {
175
-
176
- \@Mapping(target = "createdAt", source = "createdAt")
177
- UserResponse toResponse(User user);
178
-
179
- \@Mapping(target = "id", ignore = true)
180
- \@Mapping(target = "deleted", ignore = true)
181
- \@Mapping(target = "createdAt", ignore = true)
182
- \@Mapping(target = "updatedAt", ignore = true)
183
- User toEntity(CreateUserRequest request);
184
-
185
- List<UserResponse> toResponseList(List<User> users);
186
- }
187
- ```
188
-
189
- ---
190
-
191
- ## Exception Handling
192
-
193
- ```java
194
- // Custom exception
195
- public class ResourceNotFoundException extends RuntimeException {
196
- public ResourceNotFoundException(String resource, Long id) {
197
- super(String.format("%s with id %d not found", resource, id));
198
- }
199
- }
200
-
201
- // Global handler
202
- \@RestControllerAdvice
203
- \@Slf4j
204
- public class GlobalExceptionHandler {
205
-
206
- \@ExceptionHandler(ResourceNotFoundException.class)
207
- public ResponseEntity<ErrorResponse> handleNotFound(ResourceNotFoundException ex) {
208
- log.warn("Resource not found: {}", ex.getMessage());
209
- return ResponseEntity.status(HttpStatus.NOT_FOUND)
210
- .body(ErrorResponse.of("NOT_FOUND", ex.getMessage()));
211
- }
212
-
213
- \@ExceptionHandler(MethodArgumentNotValidException.class)
214
- public ResponseEntity<ErrorResponse> handleValidation(MethodArgumentNotValidException ex) {
215
- List<String> errors = ex.getBindingResult()
216
- .getFieldErrors()
217
- .stream()
218
- .map(e -> e.getField() + ": " + e.getDefaultMessage())
219
- .toList();
220
- return ResponseEntity.status(HttpStatus.BAD_REQUEST)
221
- .body(ErrorResponse.of("VALIDATION_FAILED", errors.toString()));
222
- }
223
-
224
- \@ExceptionHandler(Exception.class)
225
- public ResponseEntity<ErrorResponse> handleGeneral(Exception ex) {
226
- log.error("Unexpected error", ex);
227
- return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
228
- .body(ErrorResponse.of("INTERNAL_ERROR", "An unexpected error occurred"));
229
- }
230
- }
231
- ```
232
-
233
- ---
234
-
235
- ## Unit Tests (Service layer)
236
-
237
- ```java
238
- \@ExtendWith(MockitoExtension.class)
239
- class UserServiceImplTest {
240
-
241
- \@Mock
242
- private UserRepository userRepository;
243
-
244
- \@Mock
245
- private UserMapper userMapper;
246
-
247
- \@InjectMocks
248
- private UserServiceImpl userService;
249
-
250
- \@Test
251
- void create_shouldReturnUserResponse_whenEmailIsUnique() {
252
- CreateUserRequest request = CreateUserRequest.builder()
253
- .email("test\@example.com")
254
- .fullName("Test User")
255
- .build();
256
- User user = User.builder().id(1L).email("test\@example.com").build();
257
- UserResponse expected = UserResponse.builder().id(1L).build();
258
-
259
- given(userRepository.existsByEmail(request.getEmail())).willReturn(false);
260
- given(userMapper.toEntity(request)).willReturn(user);
261
- given(userRepository.save(user)).willReturn(user);
262
- given(userMapper.toResponse(user)).willReturn(expected);
263
-
264
- UserResponse result = userService.create(request);
265
-
266
- assertThat(result).isEqualTo(expected);
267
- verify(userRepository).save(user);
268
- }
269
-
270
- \@Test
271
- void create_shouldThrowDuplicateEmailException_whenEmailAlreadyExists() {
272
- CreateUserRequest request = CreateUserRequest.builder()
273
- .email("existing\@example.com").build();
274
- given(userRepository.existsByEmail(request.getEmail())).willReturn(true);
275
-
276
- assertThatThrownBy(() -> userService.create(request))
277
- .isInstanceOf(DuplicateEmailException.class);
278
- verify(userRepository, never()).save(any());
279
- }
280
- }
281
- ```
282
-
283
- ---
284
-
285
- ## Integration Tests (Controller layer)
286
-
287
- ```java
288
- \@SpringBootTest
289
- \@AutoConfigureMockMvc
290
- class UserControllerIntegrationTest {
291
-
292
- \@Autowired
293
- private MockMvc mockMvc;
294
-
295
- \@Autowired
296
- private ObjectMapper objectMapper;
297
-
298
- \@Test
299
- void createUser_shouldReturn201_whenRequestIsValid() throws Exception {
300
- CreateUserRequest request = CreateUserRequest.builder()
301
- .email("test\@example.com")
302
- .fullName("Test User")
303
- .password("password123")
304
- .build();
305
-
306
- mockMvc.perform(post("/api/v1/users")
307
- .contentType(MediaType.APPLICATION_JSON)
308
- .content(objectMapper.writeValueAsString(request)))
309
- .andExpect(status().isCreated())
310
- .andExpect(jsonPath("$.email").value("test\@example.com"))
311
- .andExpect(jsonPath("$.id").isNumber());
312
- }
313
-
314
- \@Test
315
- void createUser_shouldReturn400_whenEmailIsInvalid() throws Exception {
316
- CreateUserRequest request = CreateUserRequest.builder()
317
- .email("not-an-email")
318
- .fullName("Test")
319
- .password("password123")
320
- .build();
321
-
322
- mockMvc.perform(post("/api/v1/users")
323
- .contentType(MediaType.APPLICATION_JSON)
324
- .content(objectMapper.writeValueAsString(request)))
325
- .andExpect(status().isBadRequest())
326
- .andExpect(jsonPath("$.code").value("VALIDATION_FAILED"));
327
- }
328
- }
329
- ```
1
+ # Spring Boot Code Examples
2
+
3
+ Reference examples for each layer. Read the relevant section when generating code for that layer.
4
+
5
+ ---
6
+
7
+ ## Controller
8
+
9
+ ```java
10
+ // ✅ Good
11
+ \@PostMapping
12
+ public ResponseEntity<UserResponse> createUser(
13
+ \@Valid \@RequestBody CreateUserRequest request) {
14
+ UserResponse response = userService.create(request);
15
+ return ResponseEntity.status(HttpStatus.CREATED).body(response);
16
+ }
17
+
18
+ // ❌ Bad — business logic in controller
19
+ \@PostMapping
20
+ public ResponseEntity<User> createUser(\@RequestBody User user) {
21
+ if (userRepository.existsByEmail(user.getEmail())) {
22
+ throw new RuntimeException("Email exists");
23
+ }
24
+ return ResponseEntity.ok(userRepository.save(user));
25
+ }
26
+ ```
27
+
28
+ ---
29
+
30
+ ## Service
31
+
32
+ ```java
33
+ // Interface
34
+ public interface UserService {
35
+ UserResponse create(CreateUserRequest request);
36
+ UserResponse findById(Long id);
37
+ }
38
+
39
+ // Implementation
40
+ \@Service
41
+ \@RequiredArgsConstructor
42
+ public class UserServiceImpl implements UserService {
43
+
44
+ private final UserRepository userRepository;
45
+ private final UserMapper userMapper;
46
+
47
+ \@Override
48
+ \@Transactional
49
+ public UserResponse create(CreateUserRequest request) {
50
+ if (userRepository.existsByEmail(request.getEmail())) {
51
+ throw new DuplicateEmailException(request.getEmail());
52
+ }
53
+ User user = userMapper.toEntity(request);
54
+ User saved = userRepository.save(user);
55
+ return userMapper.toResponse(saved);
56
+ }
57
+
58
+ \@Override
59
+ \@Transactional(readOnly = true)
60
+ public UserResponse findById(Long id) {
61
+ log.info("Finding user by id: {}", id);
62
+ return userRepository.findById(id)
63
+ .map(userMapper::toResponse)
64
+ .orElseThrow(() -> {
65
+ log.warn("User not found: id={}", id);
66
+ return new ResourceNotFoundException("User", id);
67
+ });
68
+ }
69
+
70
+ \@Transactional(readOnly = true)
71
+ public Page<UserResponse> findAll(Pageable pageable) {
72
+ return userRepository.findAll(pageable).map(userMapper::toResponse);
73
+ }
74
+ }
75
+ ```
76
+
77
+ ---
78
+
79
+ ## Repository
80
+
81
+ ```java
82
+ // ✅ Good
83
+ public interface UserRepository extends JpaRepository<User, Long> {
84
+
85
+ boolean existsByEmail(String email);
86
+
87
+ Optional<User> findByEmailAndDeletedFalse(String email);
88
+
89
+ \@Query("SELECT u FROM User u JOIN FETCH u.roles WHERE u.id = :id")
90
+ Optional<User> findByIdWithRoles(\@Param("id") Long id);
91
+ }
92
+
93
+ // ❌ Bad — N+1 problem
94
+ List<Order> findAll(); // If Order has \@ManyToOne User, this causes N+1
95
+ ```
96
+
97
+ ---
98
+
99
+ ## Entity
100
+
101
+ ```java
102
+ \@Entity
103
+ \@Table(name = "users")
104
+ \@Getter
105
+ \@Setter
106
+ \@NoArgsConstructor
107
+ \@AllArgsConstructor
108
+ \@Builder
109
+ public class User extends BaseEntity {
110
+
111
+ \@Id
112
+ \@GeneratedValue(strategy = GenerationType.IDENTITY)
113
+ private Long id;
114
+
115
+ \@Column(name = "email", nullable = false, unique = true, length = 255)
116
+ private String email;
117
+
118
+ \@Column(name = "full_name", nullable = false, length = 100)
119
+ private String fullName;
120
+
121
+ \@Column(name = "deleted", nullable = false)
122
+ \@Builder.Default
123
+ private boolean deleted = false;
124
+
125
+ \@OneToMany(mappedBy = "user", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
126
+ private List<Order> orders = new ArrayList<>();
127
+ }
128
+ ```
129
+
130
+ ---
131
+
132
+ ## DTO
133
+
134
+ ```java
135
+ // Request DTO
136
+ \@Getter
137
+ \@NoArgsConstructor
138
+ \@AllArgsConstructor
139
+ \@Builder
140
+ public class CreateUserRequest {
141
+
142
+ \@NotBlank(message = "Email is required")
143
+ \@Email(message = "Invalid email format")
144
+ private String email;
145
+
146
+ \@NotBlank(message = "Full name is required")
147
+ \@Size(min = 2, max = 100, message = "Full name must be 2-100 characters")
148
+ private String fullName;
149
+
150
+ \@NotBlank(message = "Password is required")
151
+ \@Size(min = 8, message = "Password must be at least 8 characters")
152
+ private String password;
153
+ }
154
+
155
+ // Response DTO
156
+ \@Getter
157
+ \@NoArgsConstructor
158
+ \@AllArgsConstructor
159
+ \@Builder
160
+ public class UserResponse {
161
+ private Long id;
162
+ private String email;
163
+ private String fullName;
164
+ private LocalDateTime createdAt;
165
+ }
166
+ ```
167
+
168
+ ---
169
+
170
+ ## MapStruct Mapper
171
+
172
+ ```java
173
+ \@Mapper(componentModel = "spring")
174
+ public interface UserMapper {
175
+
176
+ \@Mapping(target = "createdAt", source = "createdAt")
177
+ UserResponse toResponse(User user);
178
+
179
+ \@Mapping(target = "id", ignore = true)
180
+ \@Mapping(target = "deleted", ignore = true)
181
+ \@Mapping(target = "createdAt", ignore = true)
182
+ \@Mapping(target = "updatedAt", ignore = true)
183
+ User toEntity(CreateUserRequest request);
184
+
185
+ List<UserResponse> toResponseList(List<User> users);
186
+ }
187
+ ```
188
+
189
+ ---
190
+
191
+ ## Exception Handling
192
+
193
+ ```java
194
+ // Custom exception
195
+ public class ResourceNotFoundException extends RuntimeException {
196
+ public ResourceNotFoundException(String resource, Long id) {
197
+ super(String.format("%s with id %d not found", resource, id));
198
+ }
199
+ }
200
+
201
+ // Global handler
202
+ \@RestControllerAdvice
203
+ \@Slf4j
204
+ public class GlobalExceptionHandler {
205
+
206
+ \@ExceptionHandler(ResourceNotFoundException.class)
207
+ public ResponseEntity<ErrorResponse> handleNotFound(ResourceNotFoundException ex) {
208
+ log.warn("Resource not found: {}", ex.getMessage());
209
+ return ResponseEntity.status(HttpStatus.NOT_FOUND)
210
+ .body(ErrorResponse.of("NOT_FOUND", ex.getMessage()));
211
+ }
212
+
213
+ \@ExceptionHandler(MethodArgumentNotValidException.class)
214
+ public ResponseEntity<ErrorResponse> handleValidation(MethodArgumentNotValidException ex) {
215
+ List<String> errors = ex.getBindingResult()
216
+ .getFieldErrors()
217
+ .stream()
218
+ .map(e -> e.getField() + ": " + e.getDefaultMessage())
219
+ .toList();
220
+ return ResponseEntity.status(HttpStatus.BAD_REQUEST)
221
+ .body(ErrorResponse.of("VALIDATION_FAILED", errors.toString()));
222
+ }
223
+
224
+ \@ExceptionHandler(Exception.class)
225
+ public ResponseEntity<ErrorResponse> handleGeneral(Exception ex) {
226
+ log.error("Unexpected error", ex);
227
+ return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
228
+ .body(ErrorResponse.of("INTERNAL_ERROR", "An unexpected error occurred"));
229
+ }
230
+ }
231
+ ```
232
+
233
+ ---
234
+
235
+ ## Unit Tests (Service layer)
236
+
237
+ ```java
238
+ \@ExtendWith(MockitoExtension.class)
239
+ class UserServiceImplTest {
240
+
241
+ \@Mock
242
+ private UserRepository userRepository;
243
+
244
+ \@Mock
245
+ private UserMapper userMapper;
246
+
247
+ \@InjectMocks
248
+ private UserServiceImpl userService;
249
+
250
+ \@Test
251
+ void create_shouldReturnUserResponse_whenEmailIsUnique() {
252
+ CreateUserRequest request = CreateUserRequest.builder()
253
+ .email("test\@example.com")
254
+ .fullName("Test User")
255
+ .build();
256
+ User user = User.builder().id(1L).email("test\@example.com").build();
257
+ UserResponse expected = UserResponse.builder().id(1L).build();
258
+
259
+ given(userRepository.existsByEmail(request.getEmail())).willReturn(false);
260
+ given(userMapper.toEntity(request)).willReturn(user);
261
+ given(userRepository.save(user)).willReturn(user);
262
+ given(userMapper.toResponse(user)).willReturn(expected);
263
+
264
+ UserResponse result = userService.create(request);
265
+
266
+ assertThat(result).isEqualTo(expected);
267
+ verify(userRepository).save(user);
268
+ }
269
+
270
+ \@Test
271
+ void create_shouldThrowDuplicateEmailException_whenEmailAlreadyExists() {
272
+ CreateUserRequest request = CreateUserRequest.builder()
273
+ .email("existing\@example.com").build();
274
+ given(userRepository.existsByEmail(request.getEmail())).willReturn(true);
275
+
276
+ assertThatThrownBy(() -> userService.create(request))
277
+ .isInstanceOf(DuplicateEmailException.class);
278
+ verify(userRepository, never()).save(any());
279
+ }
280
+ }
281
+ ```
282
+
283
+ ---
284
+
285
+ ## Integration Tests (Controller layer)
286
+
287
+ ```java
288
+ \@SpringBootTest
289
+ \@AutoConfigureMockMvc
290
+ class UserControllerIntegrationTest {
291
+
292
+ \@Autowired
293
+ private MockMvc mockMvc;
294
+
295
+ \@Autowired
296
+ private ObjectMapper objectMapper;
297
+
298
+ \@Test
299
+ void createUser_shouldReturn201_whenRequestIsValid() throws Exception {
300
+ CreateUserRequest request = CreateUserRequest.builder()
301
+ .email("test\@example.com")
302
+ .fullName("Test User")
303
+ .password("password123")
304
+ .build();
305
+
306
+ mockMvc.perform(post("/api/v1/users")
307
+ .contentType(MediaType.APPLICATION_JSON)
308
+ .content(objectMapper.writeValueAsString(request)))
309
+ .andExpect(status().isCreated())
310
+ .andExpect(jsonPath("$.email").value("test\@example.com"))
311
+ .andExpect(jsonPath("$.id").isNumber());
312
+ }
313
+
314
+ \@Test
315
+ void createUser_shouldReturn400_whenEmailIsInvalid() throws Exception {
316
+ CreateUserRequest request = CreateUserRequest.builder()
317
+ .email("not-an-email")
318
+ .fullName("Test")
319
+ .password("password123")
320
+ .build();
321
+
322
+ mockMvc.perform(post("/api/v1/users")
323
+ .contentType(MediaType.APPLICATION_JSON)
324
+ .content(objectMapper.writeValueAsString(request)))
325
+ .andExpect(status().isBadRequest())
326
+ .andExpect(jsonPath("$.code").value("VALIDATION_FAILED"));
327
+ }
328
+ }
329
+ ```
@@ -17,6 +17,13 @@ keywords: spec, plan, implementation, tdd, coding plan
17
17
  - ✅ Gate 1 APPROVED — `plan/[ticket-id]/requirement.md` reviewed and approved by DEV
18
18
  - ✅ Requirement document includes: requirements, solution, impact analysis, estimate
19
19
 
20
+ ## Fast Mode Output Rules (CRITICAL)
21
+
22
+ When updating `plan/[ticket-id]/summary.md` in **fast mode**:
23
+ - **Keep it extremely short.**
24
+ - Add a bullet point indicating Gate 2 plan is ready.
25
+ - **DO NOT copy the entire plan.md into summary.md.**
26
+
20
27
  ---
21
28
 
22
29
  ## Process